From b6ce6a95c0fe3c96bd1c37f82ae27ff5622edb19 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 9 Apr 2026 10:54:54 +0200 Subject: [PATCH 1/4] Remove FF `SpaceSettings` --- .../impl/root/SecurityAndPrivacyPresenter.kt | 4 ---- .../impl/root/SecurityAndPrivacyState.kt | 3 +-- .../root/SecurityAndPrivacyStateProvider.kt | 2 -- .../root/SecurityAndPrivacyPresenterTest.kt | 19 ------------------ .../space/impl/root/SpacePresenter.kt | 10 ++-------- .../space/impl/root/SpacePresenterTest.kt | 20 +------------------ .../libraries/featureflag/api/FeatureFlags.kt | 7 ------- 7 files changed, 4 insertions(+), 61 deletions(-) diff --git a/features/securityandprivacy/impl/src/main/kotlin/io/element/android/features/securityandprivacy/impl/root/SecurityAndPrivacyPresenter.kt b/features/securityandprivacy/impl/src/main/kotlin/io/element/android/features/securityandprivacy/impl/root/SecurityAndPrivacyPresenter.kt index 9230c1183a..48ab41a820 100644 --- a/features/securityandprivacy/impl/src/main/kotlin/io/element/android/features/securityandprivacy/impl/root/SecurityAndPrivacyPresenter.kt +++ b/features/securityandprivacy/impl/src/main/kotlin/io/element/android/features/securityandprivacy/impl/root/SecurityAndPrivacyPresenter.kt @@ -78,9 +78,6 @@ class SecurityAndPrivacyPresenter( val isKnockEnabled by remember { featureFlagService.isFeatureEnabledFlow(FeatureFlags.Knock) }.collectAsState(false) - val isSpaceSettingsEnabled by remember { - featureFlagService.isFeatureEnabledFlow(FeatureFlags.SpaceSettings) - }.collectAsState(false) val saveAction = remember { mutableStateOf>(AsyncAction.Uninitialized) } val homeserverName = remember { matrixClient.userIdServerName() } @@ -248,7 +245,6 @@ class SecurityAndPrivacyPresenter( saveAction = saveAction.value, permissions = permissions, isSpace = roomInfo.isSpace, - isSpaceSettingsEnabled = isSpaceSettingsEnabled, selectableJoinedSpaces = selectableJoinedSpaces, spaceSelectionMode = spaceSelectionMode, eventSink = ::handleEvent, diff --git a/features/securityandprivacy/impl/src/main/kotlin/io/element/android/features/securityandprivacy/impl/root/SecurityAndPrivacyState.kt b/features/securityandprivacy/impl/src/main/kotlin/io/element/android/features/securityandprivacy/impl/root/SecurityAndPrivacyState.kt index 6ec47ba183..26e77b3c70 100644 --- a/features/securityandprivacy/impl/src/main/kotlin/io/element/android/features/securityandprivacy/impl/root/SecurityAndPrivacyState.kt +++ b/features/securityandprivacy/impl/src/main/kotlin/io/element/android/features/securityandprivacy/impl/root/SecurityAndPrivacyState.kt @@ -29,7 +29,6 @@ data class SecurityAndPrivacyState( val homeserverName: String, val showEnableEncryptionConfirmation: Boolean, private val isKnockEnabled: Boolean, - private val isSpaceSettingsEnabled: Boolean, val saveAction: AsyncAction, val isSpace: Boolean, private val permissions: SecurityAndPrivacyPermissions, @@ -37,7 +36,7 @@ data class SecurityAndPrivacyState( private val spaceSelectionMode: SpaceSelectionMode, val eventSink: (SecurityAndPrivacyEvent) -> Unit ) { - val isSpaceMemberSelectable = isSpaceSettingsEnabled && spaceSelectionMode != SpaceSelectionMode.None + val isSpaceMemberSelectable = spaceSelectionMode != SpaceSelectionMode.None // Show SpaceMember option in two cases: // - SpaceMember is the current saved value diff --git a/features/securityandprivacy/impl/src/main/kotlin/io/element/android/features/securityandprivacy/impl/root/SecurityAndPrivacyStateProvider.kt b/features/securityandprivacy/impl/src/main/kotlin/io/element/android/features/securityandprivacy/impl/root/SecurityAndPrivacyStateProvider.kt index 95cb45d641..19124302e3 100644 --- a/features/securityandprivacy/impl/src/main/kotlin/io/element/android/features/securityandprivacy/impl/root/SecurityAndPrivacyStateProvider.kt +++ b/features/securityandprivacy/impl/src/main/kotlin/io/element/android/features/securityandprivacy/impl/root/SecurityAndPrivacyStateProvider.kt @@ -138,7 +138,6 @@ fun aSecurityAndPrivacyState( isSpace: Boolean = false, selectableJoinedSpaces: Set = emptySet(), spaceSelectionMode: SpaceSelectionMode = SpaceSelectionMode.None, - isSpaceSettingsEnabled: Boolean = true, eventSink: (SecurityAndPrivacyEvent) -> Unit = {} ) = SecurityAndPrivacyState( editedSettings = editedSettings, @@ -151,6 +150,5 @@ fun aSecurityAndPrivacyState( isSpace = isSpace, selectableJoinedSpaces = selectableJoinedSpaces.toImmutableSet(), spaceSelectionMode = spaceSelectionMode, - isSpaceSettingsEnabled = isSpaceSettingsEnabled, eventSink = eventSink, ) diff --git a/features/securityandprivacy/impl/src/test/kotlin/io/element/android/features/securityandprivacy/impl/root/SecurityAndPrivacyPresenterTest.kt b/features/securityandprivacy/impl/src/test/kotlin/io/element/android/features/securityandprivacy/impl/root/SecurityAndPrivacyPresenterTest.kt index d2844c79f0..34b4222053 100644 --- a/features/securityandprivacy/impl/src/test/kotlin/io/element/android/features/securityandprivacy/impl/root/SecurityAndPrivacyPresenterTest.kt +++ b/features/securityandprivacy/impl/src/test/kotlin/io/element/android/features/securityandprivacy/impl/root/SecurityAndPrivacyPresenterTest.kt @@ -416,11 +416,6 @@ class SecurityAndPrivacyPresenterTest { val presenter = createSecurityAndPrivacyPresenter( room = room, matrixClient = client, - featureFlagService = FakeFeatureFlagService( - initialState = mapOf( - FeatureFlags.SpaceSettings.key to true, - ) - ) ) presenter.test { skipItems(1) @@ -461,11 +456,6 @@ class SecurityAndPrivacyPresenterTest { room = room, navigator = navigator, matrixClient = client, - featureFlagService = FakeFeatureFlagService( - initialState = mapOf( - FeatureFlags.SpaceSettings.key to true, - ) - ) ) presenter.test { skipItems(1) @@ -587,7 +577,6 @@ class SecurityAndPrivacyPresenterTest { featureFlagService = FakeFeatureFlagService( initialState = mapOf( FeatureFlags.Knock.key to true, - FeatureFlags.SpaceSettings.key to true, ) ) ) @@ -633,7 +622,6 @@ class SecurityAndPrivacyPresenterTest { featureFlagService = FakeFeatureFlagService( initialState = mapOf( FeatureFlags.Knock.key to true, - FeatureFlags.SpaceSettings.key to true, ) ) ) @@ -859,9 +847,6 @@ class SecurityAndPrivacyPresenterTest { val presenter = createSecurityAndPrivacyPresenter( room = room, matrixClient = client, - featureFlagService = FakeFeatureFlagService( - initialState = mapOf(FeatureFlags.SpaceSettings.key to true) - ) ) presenter.test { skipItems(1) @@ -901,9 +886,6 @@ class SecurityAndPrivacyPresenterTest { val presenter = createSecurityAndPrivacyPresenter( room = room, matrixClient = client, - featureFlagService = FakeFeatureFlagService( - initialState = mapOf(FeatureFlags.SpaceSettings.key to true) - ) ) presenter.test { skipItems(1) @@ -975,7 +957,6 @@ class SecurityAndPrivacyPresenterTest { featureFlagService = FakeFeatureFlagService( initialState = mapOf( FeatureFlags.Knock.key to true, - FeatureFlags.SpaceSettings.key to true, ) ) ) diff --git a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/root/SpacePresenter.kt b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/root/SpacePresenter.kt index 807d139e6a..5ce6575493 100644 --- a/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/root/SpacePresenter.kt +++ b/features/space/impl/src/main/kotlin/io/element/android/features/space/impl/root/SpacePresenter.kt @@ -29,8 +29,6 @@ import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.core.coroutine.mapState import io.element.android.libraries.di.annotations.SessionCoroutineScope -import io.element.android.libraries.featureflag.api.FeatureFlagService -import io.element.android.libraries.featureflag.api.FeatureFlags import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.toRoomIdOrAlias @@ -66,7 +64,6 @@ class SpacePresenter( private val joinRoom: JoinRoom, private val acceptDeclineInvitePresenter: Presenter, @SessionCoroutineScope private val sessionCoroutineScope: CoroutineScope, - private val featureFlagService: FeatureFlagService, private val spaceService: SpaceService, ) : Presenter { private var children by mutableStateOf>(persistentListOf()) @@ -99,16 +96,13 @@ class SpacePresenter( val permissions by room.permissionsAsState(SpacePermissions.DEFAULT) { perms -> perms.spacePermissions() } - val isSpaceSettingsEnabled by remember { - featureFlagService.isFeatureEnabledFlow(FeatureFlags.SpaceSettings) - }.collectAsState(false) val roomInfo by room.roomInfoFlow.collectAsState() val canAccessSpaceSettings by remember { - derivedStateOf { isSpaceSettingsEnabled && permissions.settingsPermissions.hasAny(roomInfo.joinRule) } + derivedStateOf { permissions.settingsPermissions.hasAny(roomInfo.joinRule) } } val canEditSpaceGraph by remember { - derivedStateOf { isSpaceSettingsEnabled && permissions.canEditSpaceGraph } + derivedStateOf { permissions.canEditSpaceGraph } } val (joinActions, setJoinActions) = remember { mutableStateOf(emptyMap>()) } diff --git a/features/space/impl/src/test/kotlin/io/element/android/features/space/impl/root/SpacePresenterTest.kt b/features/space/impl/src/test/kotlin/io/element/android/features/space/impl/root/SpacePresenterTest.kt index 1d38e2e0f7..e183de6f27 100644 --- a/features/space/impl/src/test/kotlin/io/element/android/features/space/impl/root/SpacePresenterTest.kt +++ b/features/space/impl/src/test/kotlin/io/element/android/features/space/impl/root/SpacePresenterTest.kt @@ -19,8 +19,6 @@ import io.element.android.features.invite.api.toInviteData import io.element.android.features.invite.test.InMemorySeenInvitesStore import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.architecture.Presenter -import io.element.android.libraries.featureflag.api.FeatureFlags -import io.element.android.libraries.featureflag.test.FakeFeatureFlagService import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.RoomIdOrAlias @@ -75,16 +73,7 @@ class SpacePresenterTest { } @Test - fun `present - canAccessSpaceSettings false when space settings ff is enabled but no permissions`() = runTest { - val presenter = createSpacePresenter(spaceSettingsEnabled = true) - presenter.test { - val state = awaitItem() - assertThat(state.canAccessSpaceSettings).isFalse() - } - } - - @Test - fun `present - canAccessSpaceSettings true when space settings ff is enabled and has permissions`() = runTest { + fun `present - canAccessSpaceSettings true when has permissions`() = runTest { val room = FakeBaseRoom( roomPermissions = FakeRoomPermissions( canSendState = { true } @@ -92,7 +81,6 @@ class SpacePresenterTest { ) val presenter = createSpacePresenter( room = room, - spaceSettingsEnabled = true, ) presenter.test { skipItems(1) @@ -627,7 +615,6 @@ class SpacePresenterTest { lambda = { _, _, _ -> Result.success(Unit) }, ), acceptDeclineInvitePresenter: Presenter = Presenter { anAcceptDeclineInviteState() }, - spaceSettingsEnabled: Boolean = false, spaceService: FakeSpaceService = FakeSpaceService(), ): SpacePresenter { return SpacePresenter( @@ -638,11 +625,6 @@ class SpacePresenterTest { joinRoom = joinRoom, acceptDeclineInvitePresenter = acceptDeclineInvitePresenter, sessionCoroutineScope = this, - featureFlagService = FakeFeatureFlagService( - initialState = mapOf( - FeatureFlags.SpaceSettings.key to spaceSettingsEnabled, - ) - ), spaceService = spaceService, ) } diff --git a/libraries/featureflag/api/src/main/kotlin/io/element/android/libraries/featureflag/api/FeatureFlags.kt b/libraries/featureflag/api/src/main/kotlin/io/element/android/libraries/featureflag/api/FeatureFlags.kt index a8e59e5c6d..5a80354b07 100644 --- a/libraries/featureflag/api/src/main/kotlin/io/element/android/libraries/featureflag/api/FeatureFlags.kt +++ b/libraries/featureflag/api/src/main/kotlin/io/element/android/libraries/featureflag/api/FeatureFlags.kt @@ -77,13 +77,6 @@ enum class FeatureFlags( defaultValue = { true }, isFinished = false, ), - SpaceSettings( - key = "feature.spaceSettings", - title = "Space settings", - description = "Allow managing space settings such as details, permissions and privacy.", - defaultValue = { true }, - isFinished = false, - ), RoomListSpaceFilters( key = "feature.roomListSpaceFilters", title = "Room list space filters", From 64b0a7eef39548a8a8c53ebadfb284fb17e7cec6 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 9 Apr 2026 11:00:55 +0200 Subject: [PATCH 2/4] Remove FF `CreateSpaces` --- .../configureroom/ConfigureRoomPresenter.kt | 9 +-- .../features/home/impl/HomePresenter.kt | 6 -- .../android/features/home/impl/HomeState.kt | 1 - .../android/features/home/impl/HomeView.kt | 69 ++++++++----------- .../home/impl/spaces/HomeSpacesPresenter.kt | 5 -- .../home/impl/spaces/HomeSpacesState.kt | 1 - .../impl/spaces/HomeSpacesStateProvider.kt | 10 --- .../home/impl/spaces/HomeSpacesView.kt | 2 +- .../features/home/impl/HomePresenterTest.kt | 32 --------- .../impl/spaces/HomeSpacesPresenterTest.kt | 10 --- .../libraries/featureflag/api/FeatureFlags.kt | 7 -- 11 files changed, 33 insertions(+), 119 deletions(-) diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenter.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenter.kt index 38d6132e14..0d74ca05bf 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenter.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenter.kt @@ -84,7 +84,6 @@ class ConfigureRoomPresenter( @Composable override fun present(): ConfigureRoomState { - val canAddRoomToSpace by featureFlagService.isFeatureEnabledFlow(FeatureFlags.CreateSpaces).collectAsState(false) val cameraPermissionState = cameraPermissionPresenter.present() val createRoomConfig by dataStore.getCreateRoomConfigFlow().collectAsState() val homeserverName = remember { matrixClient.userIdServerName() } @@ -113,12 +112,8 @@ class ConfigureRoomPresenter( } var spaces by remember { mutableStateOf>(persistentListOf()) } - LaunchedEffect(canAddRoomToSpace) { - spaces = if (canAddRoomToSpace) { - matrixClient.spaceService.editableSpaces().getOrElse { emptyList() }.toImmutableList() - } else { - persistentListOf() - } + LaunchedEffect(Unit) { + spaces = matrixClient.spaceService.editableSpaces().getOrElse { emptyList() }.toImmutableList() val parentSpace = spaces.find { it.roomId == initialParentSpaceId } parentSpace?.let { dataStore.setParentSpace(parentSpace = parentSpace, updateVisibility = true) diff --git a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/HomePresenter.kt b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/HomePresenter.kt index 6878f4d53c..5985e33127 100644 --- a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/HomePresenter.kt +++ b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/HomePresenter.kt @@ -94,12 +94,6 @@ class HomePresenter( } } - LaunchedEffect(homeSpacesState.canCreateSpaces, homeSpacesState.spaceRooms.isEmpty()) { - // If the flag to create spaces is disabled and the last space is left, ensure that the Chat view is rendered. - if (!homeSpacesState.canCreateSpaces && homeSpacesState.spaceRooms.isEmpty()) { - currentHomeNavigationBarItemOrdinal = HomeNavigationBarItem.Chats.ordinal - } - } val snackbarMessage by snackbarDispatcher.collectSnackbarMessageAsState() return HomeState( currentUserAndNeighbors = currentUserAndNeighbors, diff --git a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/HomeState.kt b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/HomeState.kt index 934dac831e..ae59ef8eb9 100644 --- a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/HomeState.kt +++ b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/HomeState.kt @@ -34,5 +34,4 @@ data class HomeState( ) { val isBackHandlerEnabled = currentHomeNavigationBarItem != HomeNavigationBarItem.Chats || roomListState.spaceFiltersState is SpaceFiltersState.Selected val displayRoomListFilters = currentHomeNavigationBarItem == HomeNavigationBarItem.Chats && roomListState.displayFilters - val showNavigationBar = homeSpacesState.canCreateSpaces || homeSpacesState.spaceRooms.isNotEmpty() } diff --git a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/HomeView.kt b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/HomeView.kt index ddf8b1c499..6956ba6ba5 100644 --- a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/HomeView.kt +++ b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/HomeView.kt @@ -199,50 +199,41 @@ private fun HomeScaffold( ) }, floatingActionButton = { - if (state.showNavigationBar) { - val coroutineScope = rememberCoroutineScope() - HomeBottomBar( - currentHomeNavigationBarItem = state.currentHomeNavigationBarItem, - onItemClick = { item -> - // scroll to top if selecting the same item - if (item == state.currentHomeNavigationBarItem) { - val lazyListStateTarget = when (item) { - HomeNavigationBarItem.Chats -> roomsLazyListState - HomeNavigationBarItem.Spaces -> spacesLazyListState - } - coroutineScope.launch { - if (lazyListStateTarget.firstVisibleItemIndex > 10) { - lazyListStateTarget.scrollToItem(10) - } - // Also reset the scrollBehavior height offset as it's not triggered by programmatic scrolls - scrollBehavior.state.heightOffset = 0f - lazyListStateTarget.animateScrollToItem(0) - } - } else { - state.eventSink(HomeEvent.SelectHomeNavigationBarItem(item)) + val coroutineScope = rememberCoroutineScope() + HomeBottomBar( + currentHomeNavigationBarItem = state.currentHomeNavigationBarItem, + onItemClick = { item -> + // scroll to top if selecting the same item + if (item == state.currentHomeNavigationBarItem) { + val lazyListStateTarget = when (item) { + HomeNavigationBarItem.Chats -> roomsLazyListState + HomeNavigationBarItem.Spaces -> spacesLazyListState } - }, - floatingActionButton = when (state.currentHomeNavigationBarItem) { + coroutineScope.launch { + if (lazyListStateTarget.firstVisibleItemIndex > 10) { + lazyListStateTarget.scrollToItem(10) + } + // Also reset the scrollBehavior height offset as it's not triggered by programmatic scrolls + scrollBehavior.state.heightOffset = 0f + lazyListStateTarget.animateScrollToItem(0) + } + } else { + state.eventSink(HomeEvent.SelectHomeNavigationBarItem(item)) + } + }, + floatingActionButton = { + when (state.currentHomeNavigationBarItem) { HomeNavigationBarItem.Chats -> { - { - HomeFloatingActionButton(onStartChatClick, CommonStrings.action_create_room) - } + HomeFloatingActionButton(onStartChatClick, CommonStrings.action_create_room) } - HomeNavigationBarItem.Spaces -> if (state.homeSpacesState.canCreateSpaces) { - { - HomeFloatingActionButton(onCreateSpaceClick, CommonStrings.action_create_space) - } - } else { - // No FAB for spaces if we cannot create spaces - null + HomeNavigationBarItem.Spaces -> { + HomeFloatingActionButton(onCreateSpaceClick, CommonStrings.action_create_space) } - }, - ) - } else { - HomeFloatingActionButton(onStartChatClick, CommonStrings.action_create_room) - } + } + }, + ) }, - floatingActionButtonPosition = if (state.showNavigationBar) FabPosition.Center else FabPosition.End, + floatingActionButtonPosition = FabPosition.Center, content = { padding -> val contentPadding = PaddingValues( bottom = 96.dp, diff --git a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/spaces/HomeSpacesPresenter.kt b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/spaces/HomeSpacesPresenter.kt index 707ac73261..b758a1e593 100644 --- a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/spaces/HomeSpacesPresenter.kt +++ b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/spaces/HomeSpacesPresenter.kt @@ -15,8 +15,6 @@ import androidx.compose.runtime.remember import dev.zacsweers.metro.Inject import io.element.android.features.invite.api.SeenInvitesStore import io.element.android.libraries.architecture.Presenter -import io.element.android.libraries.featureflag.api.FeatureFlagService -import io.element.android.libraries.featureflag.api.FeatureFlags import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.ui.safety.rememberHideInvitesAvatar import kotlinx.collections.immutable.persistentListOf @@ -29,11 +27,9 @@ import kotlinx.coroutines.flow.map class HomeSpacesPresenter( private val client: MatrixClient, private val seenInvitesStore: SeenInvitesStore, - private val featureFlagsService: FeatureFlagService, ) : Presenter { @Composable override fun present(): HomeSpacesState { - val canCreateSpaces by featureFlagsService.isFeatureEnabledFlow(FeatureFlags.CreateSpaces).collectAsState(false) val hideInvitesAvatar by client.rememberHideInvitesAvatar() val spaceRooms by remember { client.spaceService.topLevelSpacesFlow.map { it.toImmutableList() } @@ -52,7 +48,6 @@ class HomeSpacesPresenter( spaceRooms = spaceRooms, seenSpaceInvites = seenSpaceInvites, hideInvitesAvatar = hideInvitesAvatar, - canCreateSpaces = canCreateSpaces, // TODO enable once we can link to the screen to explore public spaces canExploreSpaces = false, eventSink = ::handleEvent, diff --git a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/spaces/HomeSpacesState.kt b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/spaces/HomeSpacesState.kt index 84b2dc7f52..e93f04291e 100644 --- a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/spaces/HomeSpacesState.kt +++ b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/spaces/HomeSpacesState.kt @@ -18,7 +18,6 @@ data class HomeSpacesState( val spaceRooms: ImmutableList, val seenSpaceInvites: ImmutableSet, val hideInvitesAvatar: Boolean, - val canCreateSpaces: Boolean, val canExploreSpaces: Boolean, val eventSink: (HomeSpacesEvents) -> Unit, ) diff --git a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/spaces/HomeSpacesStateProvider.kt b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/spaces/HomeSpacesStateProvider.kt index a65f29cc2f..17f2cbad31 100644 --- a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/spaces/HomeSpacesStateProvider.kt +++ b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/spaces/HomeSpacesStateProvider.kt @@ -30,17 +30,9 @@ open class HomeSpacesStateProvider : PreviewParameterProvider { ), spaceRooms = aListOfSpaceRooms(), ), - aHomeSpacesState( - space = CurrentSpace.Space( - spaceRoom = aSpaceRoom(roomId = RoomId("!mySpace:example.com")) - ), - spaceRooms = aListOfSpaceRooms(), - canCreateSpaces = false, - ), aHomeSpacesState( space = CurrentSpace.Root, spaceRooms = emptyList(), - canCreateSpaces = true, ), ) } @@ -50,7 +42,6 @@ internal fun aHomeSpacesState( spaceRooms: List = aListOfSpaceRooms(), seenSpaceInvites: Set = emptySet(), hideInvitesAvatar: Boolean = false, - canCreateSpaces: Boolean = true, canExploreSpaces: Boolean = true, eventSink: (HomeSpacesEvents) -> Unit = {}, ) = HomeSpacesState( @@ -58,7 +49,6 @@ internal fun aHomeSpacesState( spaceRooms = spaceRooms.toImmutableList(), seenSpaceInvites = seenSpaceInvites.toImmutableSet(), hideInvitesAvatar = hideInvitesAvatar, - canCreateSpaces = canCreateSpaces, canExploreSpaces = canExploreSpaces, eventSink = eventSink, ) diff --git a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/spaces/HomeSpacesView.kt b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/spaces/HomeSpacesView.kt index c563e6eb26..9125912bf0 100644 --- a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/spaces/HomeSpacesView.kt +++ b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/spaces/HomeSpacesView.kt @@ -55,7 +55,7 @@ fun HomeSpacesView( onExploreClick: () -> Unit, modifier: Modifier = Modifier, ) { - if (state.canCreateSpaces && state.spaceRooms.isEmpty()) { + if (state.spaceRooms.isEmpty()) { EmptySpaceHomeView( modifier = modifier.padding(contentPadding), onCreateSpaceClick = onCreateSpaceClick, diff --git a/features/home/impl/src/test/kotlin/io/element/android/features/home/impl/HomePresenterTest.kt b/features/home/impl/src/test/kotlin/io/element/android/features/home/impl/HomePresenterTest.kt index 4002844947..90cf160cc6 100644 --- a/features/home/impl/src/test/kotlin/io/element/android/features/home/impl/HomePresenterTest.kt +++ b/features/home/impl/src/test/kotlin/io/element/android/features/home/impl/HomePresenterTest.kt @@ -33,7 +33,6 @@ import io.element.android.libraries.matrix.test.sync.FakeSyncService import io.element.android.libraries.sessionstorage.api.SessionStore import io.element.android.libraries.sessionstorage.test.InMemorySessionStore import io.element.android.libraries.sessionstorage.test.aSessionData -import io.element.android.tests.testutils.MutablePresenter import io.element.android.tests.testutils.WarmUpRule import io.element.android.tests.testutils.lambda.lambdaRecorder import io.element.android.tests.testutils.lambda.value @@ -79,7 +78,6 @@ class HomePresenterTest { MatrixUser(A_USER_ID, A_USER_NAME, AN_AVATAR_URL) ) assertThat(withUserState.showAvatarIndicator).isFalse() - assertThat(withUserState.showNavigationBar).isTrue() } } @@ -158,36 +156,6 @@ class HomePresenterTest { .with(value(Announcement.Space)) } } - - @Test - fun `present - NavigationBar is hidden when the last space is left when the user can't create new spaces`() = runTest { - val homeSpacesPresenter = MutablePresenter(aHomeSpacesState()) - val presenter = createHomePresenter( - sessionStore = InMemorySessionStore( - updateUserProfileResult = { _, _, _ -> }, - ), - homeSpacesPresenter = homeSpacesPresenter, - announcementService = FakeAnnouncementService( - showAnnouncementResult = {}, - ) - ) - presenter.test { - val initialState = awaitItem() - assertThat(initialState.currentHomeNavigationBarItem).isEqualTo(HomeNavigationBarItem.Chats) - assertThat(initialState.showNavigationBar).isTrue() - // User navigate to Spaces - initialState.eventSink(HomeEvent.SelectHomeNavigationBarItem(HomeNavigationBarItem.Spaces)) - val spaceState = awaitItem() - assertThat(spaceState.currentHomeNavigationBarItem).isEqualTo(HomeNavigationBarItem.Spaces) - // The last space is left - homeSpacesPresenter.updateState(aHomeSpacesState(spaceRooms = emptyList(), canCreateSpaces = false)) - skipItems(1) - val finalState = awaitItem() - // We are back to Chats - assertThat(finalState.currentHomeNavigationBarItem).isEqualTo(HomeNavigationBarItem.Chats) - assertThat(finalState.showNavigationBar).isFalse() - } - } } internal fun createHomePresenter( diff --git a/features/home/impl/src/test/kotlin/io/element/android/features/home/impl/spaces/HomeSpacesPresenterTest.kt b/features/home/impl/src/test/kotlin/io/element/android/features/home/impl/spaces/HomeSpacesPresenterTest.kt index 43d3a8896d..c7608833ac 100644 --- a/features/home/impl/src/test/kotlin/io/element/android/features/home/impl/spaces/HomeSpacesPresenterTest.kt +++ b/features/home/impl/src/test/kotlin/io/element/android/features/home/impl/spaces/HomeSpacesPresenterTest.kt @@ -11,9 +11,6 @@ package io.element.android.features.home.impl.spaces import com.google.common.truth.Truth.assertThat import io.element.android.features.invite.api.SeenInvitesStore import io.element.android.features.invite.test.InMemorySeenInvitesStore -import io.element.android.libraries.featureflag.api.FeatureFlagService -import io.element.android.libraries.featureflag.api.FeatureFlags -import io.element.android.libraries.featureflag.test.FakeFeatureFlagService import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.test.FakeMatrixClient import io.element.android.tests.testutils.test @@ -26,25 +23,18 @@ class HomeSpacesPresenterTest { val presenter = createPresenter() presenter.test { val state = awaitItem() - // canCreateSpaces is initially false - assertThat(state.canCreateSpaces).isFalse() assertThat(state.space).isEqualTo(CurrentSpace.Root) assertThat(state.spaceRooms).isEmpty() assertThat(state.hideInvitesAvatar).isFalse() assertThat(state.seenSpaceInvites).isEmpty() - - // It'll eventually be true - assertThat(awaitItem().canCreateSpaces).isTrue() } } private fun createPresenter( client: MatrixClient = FakeMatrixClient(), seenInvitesStore: SeenInvitesStore = InMemorySeenInvitesStore(), - featureFlagsService: FeatureFlagService = FakeFeatureFlagService(initialState = mapOf(FeatureFlags.CreateSpaces.key to true)), ) = HomeSpacesPresenter( client = client, seenInvitesStore = seenInvitesStore, - featureFlagsService = featureFlagsService, ) } diff --git a/libraries/featureflag/api/src/main/kotlin/io/element/android/libraries/featureflag/api/FeatureFlags.kt b/libraries/featureflag/api/src/main/kotlin/io/element/android/libraries/featureflag/api/FeatureFlags.kt index 5a80354b07..d0683fe53e 100644 --- a/libraries/featureflag/api/src/main/kotlin/io/element/android/libraries/featureflag/api/FeatureFlags.kt +++ b/libraries/featureflag/api/src/main/kotlin/io/element/android/libraries/featureflag/api/FeatureFlags.kt @@ -70,13 +70,6 @@ enum class FeatureFlags( defaultValue = { false }, isFinished = false, ), - CreateSpaces( - key = "feature.createSpaces", - title = "Create spaces", - description = "Allow creating spaces.", - defaultValue = { true }, - isFinished = false, - ), RoomListSpaceFilters( key = "feature.roomListSpaceFilters", title = "Room list space filters", From 5eb9bed386595e204db08626af3c0f3ec21c889b Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 9 Apr 2026 11:33:08 +0200 Subject: [PATCH 3/4] Remove FF `RoomListSpaceFilters` --- .../spacefilters/SpaceFiltersPresenter.kt | 9 +--- .../spacefilters/SpaceFiltersPresenterTest.kt | 49 +------------------ .../libraries/featureflag/api/FeatureFlags.kt | 7 --- 3 files changed, 3 insertions(+), 62 deletions(-) diff --git a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/spacefilters/SpaceFiltersPresenter.kt b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/spacefilters/SpaceFiltersPresenter.kt index 9813732bdb..76fce9bfec 100644 --- a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/spacefilters/SpaceFiltersPresenter.kt +++ b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/spacefilters/SpaceFiltersPresenter.kt @@ -17,8 +17,6 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import dev.zacsweers.metro.Inject import io.element.android.libraries.architecture.Presenter -import io.element.android.libraries.featureflag.api.FeatureFlagService -import io.element.android.libraries.featureflag.api.FeatureFlags import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.spaces.SpaceServiceFilter import kotlinx.collections.immutable.persistentListOf @@ -27,20 +25,15 @@ import kotlinx.coroutines.flow.map @Inject class SpaceFiltersPresenter( - private val featureFlagService: FeatureFlagService, private val matrixClient: MatrixClient, ) : Presenter { @Composable override fun present(): SpaceFiltersState { - val isFeatureEnabled by featureFlagService - .isFeatureEnabledFlow(FeatureFlags.RoomListSpaceFilters) - .collectAsState(initial = false) - val availableFilters by remember { matrixClient.spaceService.spaceFiltersFlow.map { it.toImmutableList() } }.collectAsState(initial = persistentListOf()) - if (!isFeatureEnabled || availableFilters.isEmpty()) { + if (availableFilters.isEmpty()) { return SpaceFiltersState.Disabled } diff --git a/features/home/impl/src/test/kotlin/io/element/android/features/home/impl/spacefilters/SpaceFiltersPresenterTest.kt b/features/home/impl/src/test/kotlin/io/element/android/features/home/impl/spacefilters/SpaceFiltersPresenterTest.kt index 278a268864..31a47d830f 100644 --- a/features/home/impl/src/test/kotlin/io/element/android/features/home/impl/spacefilters/SpaceFiltersPresenterTest.kt +++ b/features/home/impl/src/test/kotlin/io/element/android/features/home/impl/spacefilters/SpaceFiltersPresenterTest.kt @@ -8,8 +8,6 @@ package io.element.android.features.home.impl.spacefilters import com.google.common.truth.Truth.assertThat -import io.element.android.libraries.featureflag.api.FeatureFlags -import io.element.android.libraries.featureflag.test.FakeFeatureFlagService import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.test.FakeMatrixClient import io.element.android.libraries.matrix.test.spaces.FakeSpaceService @@ -21,26 +19,9 @@ import org.junit.Test @OptIn(ExperimentalCoroutinesApi::class) class SpaceFiltersPresenterTest { - @Test - fun `present - when feature flag is disabled returns Disabled state`() = runTest { - val presenter = createSpaceFiltersPresenter( - featureFlagService = FakeFeatureFlagService( - initialState = mapOf(FeatureFlags.RoomListSpaceFilters.key to false) - ) - ) - presenter.test { - val state = awaitItem() - assertThat(state).isEqualTo(SpaceFiltersState.Disabled) - } - } - @Test fun `present - when available filters is empty returns Disabled state`() = runTest { - val presenter = createSpaceFiltersPresenter( - featureFlagService = FakeFeatureFlagService( - initialState = mapOf(FeatureFlags.RoomListSpaceFilters.key to true) - ) - ) + val presenter = createSpaceFiltersPresenter() presenter.test { val state = awaitLastSequentialItem() assertThat(state).isEqualTo(SpaceFiltersState.Disabled) @@ -48,15 +29,12 @@ class SpaceFiltersPresenterTest { } @Test - fun `present - when feature flag is enabled and filters exist returns Unselected state`() = runTest { + fun `present - when filters exist returns Unselected state`() = runTest { val spaceFilter = aSpaceServiceFilter(displayName = "Test Space") val spaceService = FakeSpaceService() val matrixClient = FakeMatrixClient(spaceService = spaceService) val presenter = createSpaceFiltersPresenter( - featureFlagService = FakeFeatureFlagService( - initialState = mapOf(FeatureFlags.RoomListSpaceFilters.key to true) - ), matrixClient = matrixClient, ) presenter.test { @@ -75,9 +53,6 @@ class SpaceFiltersPresenterTest { val matrixClient = FakeMatrixClient(spaceService = spaceService) val presenter = createSpaceFiltersPresenter( - featureFlagService = FakeFeatureFlagService( - initialState = mapOf(FeatureFlags.RoomListSpaceFilters.key to true) - ), matrixClient = matrixClient, ) presenter.test { @@ -99,9 +74,6 @@ class SpaceFiltersPresenterTest { val matrixClient = FakeMatrixClient(spaceService = spaceService) val presenter = createSpaceFiltersPresenter( - featureFlagService = FakeFeatureFlagService( - initialState = mapOf(FeatureFlags.RoomListSpaceFilters.key to true) - ), matrixClient = matrixClient, ) presenter.test { @@ -129,9 +101,6 @@ class SpaceFiltersPresenterTest { val matrixClient = FakeMatrixClient(spaceService = spaceService) val presenter = createSpaceFiltersPresenter( - featureFlagService = FakeFeatureFlagService( - initialState = mapOf(FeatureFlags.RoomListSpaceFilters.key to true) - ), matrixClient = matrixClient, ) presenter.test { @@ -159,9 +128,6 @@ class SpaceFiltersPresenterTest { val matrixClient = FakeMatrixClient(spaceService = spaceService) val presenter = createSpaceFiltersPresenter( - featureFlagService = FakeFeatureFlagService( - initialState = mapOf(FeatureFlags.RoomListSpaceFilters.key to true) - ), matrixClient = matrixClient, ) presenter.test { @@ -196,9 +162,6 @@ class SpaceFiltersPresenterTest { val matrixClient = FakeMatrixClient(spaceService = spaceService) val presenter = createSpaceFiltersPresenter( - featureFlagService = FakeFeatureFlagService( - initialState = mapOf(FeatureFlags.RoomListSpaceFilters.key to true) - ), matrixClient = matrixClient, ) presenter.test { @@ -224,9 +187,6 @@ class SpaceFiltersPresenterTest { val matrixClient = FakeMatrixClient(spaceService = spaceService) val presenter = createSpaceFiltersPresenter( - featureFlagService = FakeFeatureFlagService( - initialState = mapOf(FeatureFlags.RoomListSpaceFilters.key to true) - ), matrixClient = matrixClient, ) presenter.test { @@ -271,9 +231,6 @@ class SpaceFiltersPresenterTest { val matrixClient = FakeMatrixClient(spaceService = spaceService) val presenter = createSpaceFiltersPresenter( - featureFlagService = FakeFeatureFlagService( - initialState = mapOf(FeatureFlags.RoomListSpaceFilters.key to true) - ), matrixClient = matrixClient, ) presenter.test { @@ -302,11 +259,9 @@ class SpaceFiltersPresenterTest { } private fun createSpaceFiltersPresenter( - featureFlagService: FakeFeatureFlagService = FakeFeatureFlagService(), matrixClient: FakeMatrixClient = FakeMatrixClient(), ): SpaceFiltersPresenter { return SpaceFiltersPresenter( - featureFlagService = featureFlagService, matrixClient = matrixClient, ) } diff --git a/libraries/featureflag/api/src/main/kotlin/io/element/android/libraries/featureflag/api/FeatureFlags.kt b/libraries/featureflag/api/src/main/kotlin/io/element/android/libraries/featureflag/api/FeatureFlags.kt index d0683fe53e..5b65a32f61 100644 --- a/libraries/featureflag/api/src/main/kotlin/io/element/android/libraries/featureflag/api/FeatureFlags.kt +++ b/libraries/featureflag/api/src/main/kotlin/io/element/android/libraries/featureflag/api/FeatureFlags.kt @@ -70,13 +70,6 @@ enum class FeatureFlags( defaultValue = { false }, isFinished = false, ), - RoomListSpaceFilters( - key = "feature.roomListSpaceFilters", - title = "Room list space filters", - description = "Allow filtering the room list by space.", - defaultValue = { true }, - isFinished = false, - ), PrintLogsToLogcat( key = "feature.print_logs_to_logcat", title = "Print logs to logcat", From 3ba0689cf847ea0383bac5055311030a344fb2b5 Mon Sep 17 00:00:00 2001 From: ElementBot Date: Thu, 9 Apr 2026 10:18:06 +0000 Subject: [PATCH 4/4] Update screenshots --- .../features.home.impl.spaces_HomeSpacesView_Day_2_en.png | 4 ++-- .../features.home.impl.spaces_HomeSpacesView_Day_3_en.png | 3 --- .../features.home.impl.spaces_HomeSpacesView_Night_2_en.png | 4 ++-- .../features.home.impl.spaces_HomeSpacesView_Night_3_en.png | 3 --- 4 files changed, 4 insertions(+), 10 deletions(-) delete mode 100644 tests/uitests/src/test/snapshots/images/features.home.impl.spaces_HomeSpacesView_Day_3_en.png delete mode 100644 tests/uitests/src/test/snapshots/images/features.home.impl.spaces_HomeSpacesView_Night_3_en.png diff --git a/tests/uitests/src/test/snapshots/images/features.home.impl.spaces_HomeSpacesView_Day_2_en.png b/tests/uitests/src/test/snapshots/images/features.home.impl.spaces_HomeSpacesView_Day_2_en.png index de86c4285d..03d7d97186 100644 --- a/tests/uitests/src/test/snapshots/images/features.home.impl.spaces_HomeSpacesView_Day_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.home.impl.spaces_HomeSpacesView_Day_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2d69d687090971ab7e9b87f959521109fbed48413ef217e1be3da8d7d985038b -size 38751 +oid sha256:ecd79d3f8c4efbb54efa6a5b8dee79274ae9957b01146025dab42a45fc328fc0 +size 24740 diff --git a/tests/uitests/src/test/snapshots/images/features.home.impl.spaces_HomeSpacesView_Day_3_en.png b/tests/uitests/src/test/snapshots/images/features.home.impl.spaces_HomeSpacesView_Day_3_en.png deleted file mode 100644 index 03d7d97186..0000000000 --- a/tests/uitests/src/test/snapshots/images/features.home.impl.spaces_HomeSpacesView_Day_3_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:ecd79d3f8c4efbb54efa6a5b8dee79274ae9957b01146025dab42a45fc328fc0 -size 24740 diff --git a/tests/uitests/src/test/snapshots/images/features.home.impl.spaces_HomeSpacesView_Night_2_en.png b/tests/uitests/src/test/snapshots/images/features.home.impl.spaces_HomeSpacesView_Night_2_en.png index 07e9ef1e28..049aa93a8a 100644 --- a/tests/uitests/src/test/snapshots/images/features.home.impl.spaces_HomeSpacesView_Night_2_en.png +++ b/tests/uitests/src/test/snapshots/images/features.home.impl.spaces_HomeSpacesView_Night_2_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d2a6a7f3188eb9635ade7c0bbc2239257cfbe3166f4a6fb7917a1e5578f51d9e -size 37436 +oid sha256:0fd37517b8913e2bac3275f717aa15bed18055a7525e0404b25b43cb8bd8422d +size 23840 diff --git a/tests/uitests/src/test/snapshots/images/features.home.impl.spaces_HomeSpacesView_Night_3_en.png b/tests/uitests/src/test/snapshots/images/features.home.impl.spaces_HomeSpacesView_Night_3_en.png deleted file mode 100644 index 049aa93a8a..0000000000 --- a/tests/uitests/src/test/snapshots/images/features.home.impl.spaces_HomeSpacesView_Night_3_en.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:0fd37517b8913e2bac3275f717aa15bed18055a7525e0404b25b43cb8bd8422d -size 23840