From 64b0a7eef39548a8a8c53ebadfb284fb17e7cec6 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 9 Apr 2026 11:00:55 +0200 Subject: [PATCH] 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",