From 0aa33a3cdc18dd621603318cebd81f257e2ff5fe Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 8 Sep 2025 15:17:51 +0200 Subject: [PATCH] Hide the home navigation bar if the user is not a member of any Space. https://github.com/element-hq/element-meta/issues/2906: `The tab bar with the option to view joined spaces is only shown when the user has at least one space that they have joined (because otherwise they have no clue what to do in here).` --- .../features/home/impl/HomePresenter.kt | 6 +++ .../android/features/home/impl/HomeState.kt | 1 + .../features/home/impl/HomeStateProvider.kt | 2 + .../android/features/home/impl/HomeView.kt | 2 +- .../features/home/impl/HomePresenterTest.kt | 40 +++++++++++++++++-- 5 files changed, 46 insertions(+), 5 deletions(-) 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 f0debd88ab..90565de292 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 @@ -74,6 +74,12 @@ class HomePresenter( } } + LaunchedEffect(homeSpacesState.spaceRooms.isEmpty()) { + // If the last space is left, ensure that the Chat view is rendered. + if (homeSpacesState.spaceRooms.isEmpty()) { + currentHomeNavigationBarItemOrdinal = HomeNavigationBarItem.Chats.ordinal + } + } val snackbarMessage by snackbarDispatcher.collectSnackbarMessageAsState() return HomeState( matrixUser = matrixUser.value, 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 d8668e3200..c4fe0ce0fe 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 @@ -29,4 +29,5 @@ data class HomeState( val eventSink: (HomeEvents) -> Unit, ) { val displayActions = currentHomeNavigationBarItem == HomeNavigationBarItem.Chats + val showNavigationBar = isSpaceFeatureEnabled && homeSpacesState.spaceRooms.isNotEmpty() } diff --git a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/HomeStateProvider.kt b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/HomeStateProvider.kt index 1f78053c0a..59c8c3c500 100644 --- a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/HomeStateProvider.kt +++ b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/HomeStateProvider.kt @@ -36,6 +36,8 @@ open class HomeStateProvider : PreviewParameterProvider { summaries = generateRoomListRoomSummaryList(), ) ), + // For the bottom nav bar to be visible in the preview, the user must be member of at least one space + homeSpacesState = aHomeSpacesState(), ), aHomeState( isSpaceFeatureEnabled = true, 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 9217efcfe5..6d531504ab 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 @@ -194,7 +194,7 @@ private fun HomeScaffold( ) }, bottomBar = { - if (state.isSpaceFeatureEnabled) { + if (state.showNavigationBar) { NavigationBar( containerColor = Color.Transparent, modifier = Modifier 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 0d793f1097..57f270d3d3 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 @@ -12,9 +12,11 @@ import app.cash.molecule.moleculeFlow import app.cash.turbine.test import com.google.common.truth.Truth.assertThat import io.element.android.features.home.impl.roomlist.aRoomListState +import io.element.android.features.home.impl.spaces.HomeSpacesState import io.element.android.features.home.impl.spaces.aHomeSpacesState import io.element.android.features.logout.api.direct.aDirectLogoutState import io.element.android.features.rageshake.api.RageshakeFeatureAvailability +import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher import io.element.android.libraries.featureflag.api.FeatureFlagService import io.element.android.libraries.featureflag.api.FeatureFlags @@ -30,10 +32,10 @@ import io.element.android.libraries.matrix.test.A_USER_ID import io.element.android.libraries.matrix.test.A_USER_NAME import io.element.android.libraries.matrix.test.FakeMatrixClient import io.element.android.libraries.matrix.test.sync.FakeSyncService +import io.element.android.tests.testutils.MutablePresenter import io.element.android.tests.testutils.WarmUpRule import io.element.android.tests.testutils.test import kotlinx.coroutines.flow.flowOf -import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runTest import org.junit.Rule import org.junit.Test @@ -65,6 +67,7 @@ class HomePresenterTest { assertThat(withUserState.matrixUser.avatarUrl).isEqualTo(AN_AVATAR_URL) assertThat(withUserState.showAvatarIndicator).isFalse() assertThat(withUserState.isSpaceFeatureEnabled).isFalse() + assertThat(withUserState.showNavigationBar).isFalse() } } @@ -145,13 +148,42 @@ class HomePresenterTest { } } - private fun TestScope.createHomePresenter( + @Test + fun `present - NavigationBar is hidden when the last space is left`() = runTest { + val homeSpacesPresenter = MutablePresenter(aHomeSpacesState()) + val presenter = createHomePresenter( + featureFlagService = FakeFeatureFlagService( + initialState = mapOf(FeatureFlags.Space.key to true), + ), + homeSpacesPresenter = homeSpacesPresenter, + ) + presenter.test { + skipItems(1) + val initialState = awaitItem() + assertThat(initialState.currentHomeNavigationBarItem).isEqualTo(HomeNavigationBarItem.Chats) + assertThat(initialState.showNavigationBar).isTrue() + // User navigate to Spaces + initialState.eventSink(HomeEvents.SelectHomeNavigationBarItem(HomeNavigationBarItem.Spaces)) + val spaceState = awaitItem() + assertThat(spaceState.currentHomeNavigationBarItem).isEqualTo(HomeNavigationBarItem.Spaces) + // The last space is left + homeSpacesPresenter.updateState(aHomeSpacesState(spaceRooms = emptyList())) + skipItems(1) + val finalState = awaitItem() + // We are back to Chats + assertThat(finalState.currentHomeNavigationBarItem).isEqualTo(HomeNavigationBarItem.Chats) + assertThat(finalState.showNavigationBar).isFalse() + } + } + + private fun createHomePresenter( client: MatrixClient = FakeMatrixClient(), syncService: SyncService = FakeSyncService(), snackbarDispatcher: SnackbarDispatcher = SnackbarDispatcher(), rageshakeFeatureAvailability: RageshakeFeatureAvailability = RageshakeFeatureAvailability { flowOf(false) }, indicatorService: IndicatorService = FakeIndicatorService(), - featureFlagService: FeatureFlagService = FakeFeatureFlagService() + featureFlagService: FeatureFlagService = FakeFeatureFlagService(), + homeSpacesPresenter: Presenter = Presenter { aHomeSpacesState() }, ) = HomePresenter( client = client, syncService = syncService, @@ -159,7 +191,7 @@ class HomePresenterTest { indicatorService = indicatorService, logoutPresenter = { aDirectLogoutState() }, roomListPresenter = { aRoomListState() }, - homeSpacesPresenter = { aHomeSpacesState() }, + homeSpacesPresenter = homeSpacesPresenter, rageshakeFeatureAvailability = rageshakeFeatureAvailability, featureFlagService = featureFlagService, )