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 3f223135c1..e03b40e840 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,9 +94,9 @@ class HomePresenter( } } - LaunchedEffect(homeSpacesState.spaceRooms.isEmpty()) { - // If the last space is left, ensure that the Chat view is rendered. - if (homeSpacesState.spaceRooms.isEmpty()) { + 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 } } 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 474fb6d5ba..1850d2d4cc 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 @@ -33,5 +33,5 @@ data class HomeState( ) { val displayActions = currentHomeNavigationBarItem == HomeNavigationBarItem.Chats val displayRoomListFilters = currentHomeNavigationBarItem == HomeNavigationBarItem.Chats && roomListState.displayFilters - val showNavigationBar = homeSpacesState.spaceRooms.isNotEmpty() + 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 f55183feb0..52745108f6 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 @@ -268,7 +268,10 @@ private fun HomeScaffold( lazyListState = spacesLazyListState, onSpaceClick = { spaceId -> onRoomClick(spaceId) - } + }, + onCreateSpaceClick = onCreateSpaceClick, + // TODO use actual callbacks for this + onExploreClick = {}, ) } } 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 00129235fe..d9e6aaa4d3 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 @@ -53,6 +53,8 @@ class HomeSpacesPresenter( 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 9bcf7131c8..84b2dc7f52 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 @@ -19,6 +19,7 @@ data class HomeSpacesState( 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 c1a32a1f34..a65f29cc2f 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 @@ -37,6 +37,11 @@ open class HomeSpacesStateProvider : PreviewParameterProvider { spaceRooms = aListOfSpaceRooms(), canCreateSpaces = false, ), + aHomeSpacesState( + space = CurrentSpace.Root, + spaceRooms = emptyList(), + canCreateSpaces = true, + ), ) } @@ -46,6 +51,7 @@ internal fun aHomeSpacesState( seenSpaceInvites: Set = emptySet(), hideInvitesAvatar: Boolean = false, canCreateSpaces: Boolean = true, + canExploreSpaces: Boolean = true, eventSink: (HomeSpacesEvents) -> Unit = {}, ) = HomeSpacesState( space = space, @@ -53,6 +59,7 @@ internal fun aHomeSpacesState( 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 2505cf831d..7d7deab688 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 @@ -8,23 +8,40 @@ package io.element.android.features.home.impl.spaces +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyListState import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.foundation.lazy.rememberLazyListState +import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.PreviewParameter +import androidx.compose.ui.unit.dp +import io.element.android.compound.theme.ElementTheme +import io.element.android.compound.tokens.generated.CompoundIcons +import io.element.android.libraries.designsystem.atomic.molecules.ButtonColumnMolecule +import io.element.android.libraries.designsystem.atomic.pages.HeaderFooterPage +import io.element.android.libraries.designsystem.components.BigIcon import io.element.android.libraries.designsystem.components.avatar.AvatarSize import io.element.android.libraries.designsystem.preview.ElementPreview import io.element.android.libraries.designsystem.preview.PreviewsDayNight +import io.element.android.libraries.designsystem.theme.components.Button import io.element.android.libraries.designsystem.theme.components.HorizontalDivider +import io.element.android.libraries.designsystem.theme.components.TextButton import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.room.CurrentUserMembership import io.element.android.libraries.matrix.ui.components.SpaceHeaderRootView import io.element.android.libraries.matrix.ui.components.SpaceHeaderView import io.element.android.libraries.matrix.ui.components.SpaceRoomItemView import io.element.android.libraries.matrix.ui.model.getAvatarData +import io.element.android.libraries.ui.strings.CommonStrings import kotlinx.collections.immutable.toImmutableList @Composable @@ -32,56 +49,119 @@ fun HomeSpacesView( state: HomeSpacesState, lazyListState: LazyListState, onSpaceClick: (RoomId) -> Unit, + onCreateSpaceClick: () -> Unit, + onExploreClick: () -> Unit, modifier: Modifier = Modifier, ) { - LazyColumn( - modifier = modifier, - state = lazyListState - ) { - val space = state.space - when (space) { - CurrentSpace.Root -> { - item { - SpaceHeaderRootView(numberOfSpaces = state.spaceRooms.size) + if (state.canCreateSpaces && state.spaceRooms.isEmpty()) { + EmptySpaceHomeView( + modifier = modifier, + onCreateSpaceClick = onCreateSpaceClick, + onExploreClick = onExploreClick, + canExploreSpaces = state.canExploreSpaces, + ) + } else { + LazyColumn( + modifier = modifier, + state = lazyListState + ) { + val space = state.space + when (space) { + CurrentSpace.Root -> { + item { + SpaceHeaderRootView(numberOfSpaces = state.spaceRooms.size) + } + } + is CurrentSpace.Space -> { + item { + SpaceHeaderView( + avatarData = space.spaceRoom.getAvatarData(AvatarSize.SpaceHeader), + name = space.spaceRoom.displayName, + topic = space.spaceRoom.topic, + visibility = space.spaceRoom.visibility, + heroes = space.spaceRoom.heroes.toImmutableList(), + numberOfMembers = space.spaceRoom.numJoinedMembers, + ) + } } } - is CurrentSpace.Space -> item { - SpaceHeaderView( - avatarData = space.spaceRoom.getAvatarData(AvatarSize.SpaceHeader), - name = space.spaceRoom.displayName, - topic = space.spaceRoom.topic, - visibility = space.spaceRoom.visibility, - heroes = space.spaceRoom.heroes.toImmutableList(), - numberOfMembers = space.spaceRoom.numJoinedMembers, - ) - } - } - item { - HorizontalDivider() - } - itemsIndexed( - items = state.spaceRooms, - key = { _, spaceRoom -> spaceRoom.roomId } - ) { index, spaceRoom -> - val isInvitation = spaceRoom.state == CurrentUserMembership.INVITED - SpaceRoomItemView( - spaceRoom = spaceRoom, - showUnreadIndicator = isInvitation && spaceRoom.roomId !in state.seenSpaceInvites, - hideAvatars = isInvitation && state.hideInvitesAvatar, - onClick = { - onSpaceClick(spaceRoom.roomId) - }, - onLongClick = { - // TODO - }, - ) - if (index != state.spaceRooms.lastIndex) { + + item { HorizontalDivider() } + + itemsIndexed( + items = state.spaceRooms, + key = { _, spaceRoom -> spaceRoom.roomId } + ) { index, spaceRoom -> + val isInvitation = spaceRoom.state == CurrentUserMembership.INVITED + SpaceRoomItemView( + spaceRoom = spaceRoom, + showUnreadIndicator = isInvitation && spaceRoom.roomId !in state.seenSpaceInvites, + hideAvatars = isInvitation && state.hideInvitesAvatar, + onClick = { + onSpaceClick(spaceRoom.roomId) + }, + onLongClick = { + // TODO + }, + ) + if (index != state.spaceRooms.lastIndex) { + HorizontalDivider() + } + } } } } +@Composable +private fun EmptySpaceHomeView( + onCreateSpaceClick: () -> Unit, + onExploreClick: () -> Unit, + canExploreSpaces: Boolean, + modifier: Modifier = Modifier, +) { + HeaderFooterPage( + modifier = modifier, + topBar = { + Column( + modifier = Modifier + .fillMaxWidth() + .padding(top = 32.dp, bottom = 16.dp, start = 40.dp, end = 40.dp), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.spacedBy(16.dp), + ) { + BigIcon( + style = BigIcon.Style.Default(CompoundIcons.SpaceSolid()) + ) + Text( + text = stringResource(CommonStrings.screen_space_list_empty_state_title), + style = ElementTheme.typography.fontHeadingLgBold, + color = ElementTheme.colors.textPrimary, + textAlign = TextAlign.Center, + ) + } + }, + footer = { + ButtonColumnMolecule { + Button( + modifier = Modifier.fillMaxWidth(), + text = stringResource(CommonStrings.action_create_space), + onClick = onCreateSpaceClick, + ) + if (canExploreSpaces) { + TextButton( + modifier = Modifier.fillMaxWidth(), + text = stringResource(CommonStrings.action_explore_public_spaces), + onClick = onExploreClick, + ) + } + } + } + ) { + } +} + @PreviewsDayNight @Composable internal fun HomeSpacesViewPreview( @@ -91,6 +171,7 @@ internal fun HomeSpacesViewPreview( state = state, lazyListState = rememberLazyListState(), onSpaceClick = {}, - modifier = Modifier, + onCreateSpaceClick = {}, + onExploreClick = {}, ) } 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 266c33015d..37ed6e6909 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 @@ -173,7 +173,7 @@ class HomePresenterTest { } @Test - fun `present - NavigationBar is hidden when the last space is left`() = runTest { + 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( @@ -193,7 +193,7 @@ class HomePresenterTest { val spaceState = awaitItem() assertThat(spaceState.currentHomeNavigationBarItem).isEqualTo(HomeNavigationBarItem.Spaces) // The last space is left - homeSpacesPresenter.updateState(aHomeSpacesState(spaceRooms = emptyList())) + homeSpacesPresenter.updateState(aHomeSpacesState(spaceRooms = emptyList(), canCreateSpaces = false)) skipItems(1) val finalState = awaitItem() // We are back to Chats diff --git a/libraries/ui-strings/src/main/res/values/localazy.xml b/libraries/ui-strings/src/main/res/values/localazy.xml index 7940aa3855..332151e5f8 100644 --- a/libraries/ui-strings/src/main/res/values/localazy.xml +++ b/libraries/ui-strings/src/main/res/values/localazy.xml @@ -93,6 +93,7 @@ "Enable" "End poll" "Enter PIN" + "Explore public spaces" "Finish" "Forgot password?" "Forward" 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 new file mode 100644 index 0000000000..c8cfca0ff6 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.home.impl.spaces_HomeSpacesView_Day_3_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:119b7216f0be644eb1153439eebdaf0ebb9acae3b68161c66fa0fa8c21db8703 +size 24868 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 new file mode 100644 index 0000000000..f0095cc4b9 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/features.home.impl.spaces_HomeSpacesView_Night_3_en.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ec42ba9eb13978cadb3d556482413eda959178d9a32743c2469ac3702541e8b5 +size 24247