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 7d210c3447..e6056e6636 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 @@ -50,6 +50,7 @@ import io.element.android.features.home.impl.roomlist.RoomListDeclineInviteMenu import io.element.android.features.home.impl.roomlist.RoomListEvent import io.element.android.features.home.impl.roomlist.RoomListState import io.element.android.features.home.impl.search.RoomListSearchView +import io.element.android.features.home.impl.spacefilters.SpaceFiltersView import io.element.android.features.home.impl.spaces.HomeSpacesView import io.element.android.libraries.androidutils.throttler.FirstThrottler import io.element.android.libraries.designsystem.preview.ElementPreview @@ -168,7 +169,6 @@ private fun HomeScaffold( topBar = { HomeTopBar( selectedNavigationItem = state.currentHomeNavigationBarItem, - title = stringResource(state.currentHomeNavigationBarItem.labelRes), currentUserAndNeighbors = state.currentUserAndNeighbors, showAvatarIndicator = state.showAvatarIndicator, areSearchResultsDisplayed = roomListState.searchState.isSearchActive, @@ -182,6 +182,7 @@ private fun HomeScaffold( scrollBehavior = scrollBehavior, displayFilters = state.displayRoomListFilters, filtersState = roomListState.filtersState, + spaceFiltersState = roomListState.spaceFiltersState, canCreateSpaces = state.homeSpacesState.canCreateSpaces, canReportBug = state.canReportBug, modifier = Modifier.hazeEffect( @@ -256,6 +257,7 @@ private fun HomeScaffold( .consumeWindowInsets(padding) .hazeSource(state = hazeState) ) + SpaceFiltersView(roomListState.spaceFiltersState) } HomeNavigationBarItem.Spaces -> { HomeSpacesView( diff --git a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/components/HomeTopBar.kt b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/components/HomeTopBar.kt index c92a5b9fb8..1f0e580d3e 100644 --- a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/components/HomeTopBar.kt +++ b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/components/HomeTopBar.kt @@ -17,6 +17,7 @@ import androidx.compose.foundation.layout.statusBarsPadding import androidx.compose.foundation.pager.VerticalPager import androidx.compose.foundation.pager.rememberPagerState import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.IconButtonDefaults import androidx.compose.material3.TopAppBarDefaults import androidx.compose.material3.TopAppBarScrollBehavior import androidx.compose.material3.rememberTopAppBarState @@ -40,10 +41,14 @@ import io.element.android.appconfig.RoomListConfig import io.element.android.compound.theme.ElementTheme import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.features.home.impl.HomeNavigationBarItem -import io.element.android.features.home.impl.R import io.element.android.features.home.impl.filters.RoomListFiltersState import io.element.android.features.home.impl.filters.RoomListFiltersView import io.element.android.features.home.impl.filters.aRoomListFiltersState +import io.element.android.features.home.impl.spacefilters.SpaceFiltersEvent +import io.element.android.features.home.impl.spacefilters.SpaceFiltersState +import io.element.android.features.home.impl.spacefilters.aDisabledSpaceFiltersState +import io.element.android.features.home.impl.spacefilters.aSelectedSpaceFiltersState +import io.element.android.features.home.impl.spacefilters.anUnselectedSpaceFiltersState import io.element.android.libraries.designsystem.atomic.atoms.RedIndicatorAtom import io.element.android.libraries.designsystem.components.TopAppBarScrollBehaviorLayout import io.element.android.libraries.designsystem.components.avatar.Avatar @@ -75,7 +80,6 @@ import kotlinx.collections.immutable.toImmutableList @Composable fun HomeTopBar( selectedNavigationItem: HomeNavigationBarItem, - title: String, currentUserAndNeighbors: ImmutableList, showAvatarIndicator: Boolean, areSearchResultsDisplayed: Boolean, @@ -89,6 +93,7 @@ fun HomeTopBar( canReportBug: Boolean, displayFilters: Boolean, filtersState: RoomListFiltersState, + spaceFiltersState: SpaceFiltersState, modifier: Modifier = Modifier, ) { Column(modifier) { @@ -103,12 +108,21 @@ fun HomeTopBar( scrolledContainerColor = Color.Transparent, ), title = { + val displayTitle = when (selectedNavigationItem) { + HomeNavigationBarItem.Chats -> { + when (spaceFiltersState) { + is SpaceFiltersState.Selected -> spaceFiltersState.selectedFilter.spaceRoom.displayName + else -> stringResource(selectedNavigationItem.labelRes) + } + } + HomeNavigationBarItem.Spaces -> stringResource(selectedNavigationItem.labelRes) + } Text( modifier = Modifier.semantics { heading() }, style = ElementTheme.typography.aliasScreenTitle, - text = title, + text = displayTitle, ) }, navigationIcon = { @@ -124,7 +138,8 @@ fun HomeTopBar( HomeNavigationBarItem.Chats -> RoomListMenuItems( onToggleSearch = onToggleSearch, onMenuActionClick = onMenuActionClick, - canReportBug = canReportBug + canReportBug = canReportBug, + spaceFiltersState = spaceFiltersState, ) HomeNavigationBarItem.Spaces -> SpacesMenuItems( canCreateSpaces = canCreateSpaces, @@ -154,6 +169,7 @@ private fun RoomListMenuItems( onToggleSearch: () -> Unit, onMenuActionClick: (RoomListMenuAction) -> Unit, canReportBug: Boolean, + spaceFiltersState: SpaceFiltersState, ) { IconButton( onClick = onToggleSearch, @@ -163,6 +179,7 @@ private fun RoomListMenuItems( contentDescription = stringResource(CommonStrings.action_search), ) } + SpaceFilterButton(spaceFiltersState = spaceFiltersState) if (RoomListConfig.HAS_DROP_DOWN_MENU) { var showMenu by remember { mutableStateOf(false) } IconButton( @@ -228,6 +245,47 @@ private fun SpacesMenuItems( } } +@Composable +private fun SpaceFilterButton( + spaceFiltersState: SpaceFiltersState, +) { + when (spaceFiltersState) { + SpaceFiltersState.Disabled -> Unit + is SpaceFiltersState.Unselected -> { + IconButton( + onClick = { spaceFiltersState.eventSink(SpaceFiltersEvent.Unselected.ShowFilters) } + ) { + Icon( + imageVector = CompoundIcons.Filter(), + contentDescription = null, + ) + } + } + is SpaceFiltersState.Selecting -> { + IconButton(onClick = {}) { + Icon( + imageVector = CompoundIcons.Filter(), + contentDescription = null, + ) + } + } + is SpaceFiltersState.Selected -> { + IconButton( + colors = IconButtonDefaults.iconButtonColors( + containerColor = ElementTheme.colors.bgAccentRest, + contentColor = ElementTheme.colors.iconOnSolidPrimary, + ), + onClick = { spaceFiltersState.eventSink(SpaceFiltersEvent.Selected.ClearSelection) }, + ) { + Icon( + imageVector = CompoundIcons.Filter(), + contentDescription = null, + ) + } + } + } +} + @Composable private fun NavigationIcon( currentUserAndNeighbors: ImmutableList, @@ -309,7 +367,6 @@ private fun AccountIcon( internal fun HomeTopBarPreview() = ElementPreview { HomeTopBar( selectedNavigationItem = HomeNavigationBarItem.Chats, - title = stringResource(R.string.screen_roomlist_main_space_title), currentUserAndNeighbors = persistentListOf(MatrixUser(UserId("@id:domain"), "Alice")), showAvatarIndicator = false, areSearchResultsDisplayed = false, @@ -322,6 +379,7 @@ internal fun HomeTopBarPreview() = ElementPreview { canReportBug = true, displayFilters = true, filtersState = aRoomListFiltersState(), + spaceFiltersState = anUnselectedSpaceFiltersState(), onMenuActionClick = {}, ) } @@ -332,7 +390,6 @@ internal fun HomeTopBarPreview() = ElementPreview { internal fun HomeTopBarSpacesPreview() = ElementPreview { HomeTopBar( selectedNavigationItem = HomeNavigationBarItem.Spaces, - title = stringResource(R.string.screen_home_tab_spaces), currentUserAndNeighbors = persistentListOf(MatrixUser(UserId("@id:domain"), "Alice")), showAvatarIndicator = false, areSearchResultsDisplayed = false, @@ -345,6 +402,7 @@ internal fun HomeTopBarSpacesPreview() = ElementPreview { canReportBug = true, displayFilters = false, filtersState = aRoomListFiltersState(), + spaceFiltersState = anUnselectedSpaceFiltersState(), onMenuActionClick = {}, ) } @@ -355,7 +413,6 @@ internal fun HomeTopBarSpacesPreview() = ElementPreview { internal fun HomeTopBarWithIndicatorPreview() = ElementPreview { HomeTopBar( selectedNavigationItem = HomeNavigationBarItem.Chats, - title = stringResource(R.string.screen_roomlist_main_space_title), currentUserAndNeighbors = persistentListOf(MatrixUser(UserId("@id:domain"), "Alice")), showAvatarIndicator = true, areSearchResultsDisplayed = false, @@ -368,6 +425,7 @@ internal fun HomeTopBarWithIndicatorPreview() = ElementPreview { canReportBug = true, displayFilters = true, filtersState = aRoomListFiltersState(), + spaceFiltersState = anUnselectedSpaceFiltersState(), onMenuActionClick = {}, ) } @@ -378,7 +436,6 @@ internal fun HomeTopBarWithIndicatorPreview() = ElementPreview { internal fun HomeTopBarMultiAccountPreview() = ElementPreview { HomeTopBar( selectedNavigationItem = HomeNavigationBarItem.Chats, - title = stringResource(R.string.screen_roomlist_main_space_title), currentUserAndNeighbors = aMatrixUserList().take(3).toImmutableList(), showAvatarIndicator = false, areSearchResultsDisplayed = false, @@ -391,6 +448,30 @@ internal fun HomeTopBarMultiAccountPreview() = ElementPreview { canReportBug = true, displayFilters = true, filtersState = aRoomListFiltersState(), + spaceFiltersState = anUnselectedSpaceFiltersState(), + onMenuActionClick = {}, + ) +} + +@OptIn(ExperimentalMaterial3Api::class) +@PreviewsDayNight +@Composable +internal fun HomeTopSpaceFiltersSelectedPreview() = ElementPreview { + HomeTopBar( + selectedNavigationItem = HomeNavigationBarItem.Chats, + currentUserAndNeighbors = persistentListOf(MatrixUser(UserId("@id:domain"), "Alice")), + showAvatarIndicator = false, + areSearchResultsDisplayed = false, + scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState()), + onOpenSettings = {}, + onAccountSwitch = {}, + onToggleSearch = {}, + onCreateSpace = {}, + canCreateSpaces = true, + canReportBug = true, + displayFilters = true, + filtersState = aRoomListFiltersState(), + spaceFiltersState = aSelectedSpaceFiltersState(), onMenuActionClick = {}, ) } 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 bcc6071723..3e3e789de6 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 @@ -14,8 +14,10 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue +import dev.zacsweers.metro.ContributesBinding import dev.zacsweers.metro.Inject import io.element.android.libraries.architecture.Presenter +import io.element.android.libraries.di.SessionScope import io.element.android.libraries.featureflag.api.FeatureFlagService import io.element.android.libraries.featureflag.api.FeatureFlags import io.element.android.libraries.matrix.api.MatrixClient @@ -24,7 +26,7 @@ import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.flow.map -@Inject +@ContributesBinding(SessionScope::class) class SpaceFiltersPresenter( private val featureFlagService: FeatureFlagService, private val matrixClient: MatrixClient, diff --git a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/spacefilters/SpaceFiltersStateProvider.kt b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/spacefilters/SpaceFiltersStateProvider.kt index 03159a9db4..931d39aef4 100644 --- a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/spacefilters/SpaceFiltersStateProvider.kt +++ b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/spacefilters/SpaceFiltersStateProvider.kt @@ -18,10 +18,8 @@ import kotlinx.collections.immutable.persistentListOf class SpaceFiltersStateProvider : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( - aDisabledSpaceFiltersState(), - anUnselectedSpaceFiltersState(), aSelectingSpaceFiltersState(), - aSelectedSpaceFiltersState(), + aSelectingSpaceFiltersState(searchQuery = "Pr") ) } @@ -54,11 +52,11 @@ fun aSelectingSpaceFiltersState( roomId = RoomId("!gaming:example.com"), ), ), - searchQuery: TextFieldState = TextFieldState(), + searchQuery: String = "", eventSink: (SpaceFiltersEvent.Selecting) -> Unit = {}, ) = SpaceFiltersState.Selecting( availableFilters = persistentListOf(*availableFilters.toTypedArray()), - searchQuery = searchQuery, + searchQuery = TextFieldState(searchQuery), eventSink = eventSink, ) diff --git a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/spacefilters/SpaceFiltersView.kt b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/spacefilters/SpaceFiltersView.kt index 29313cb924..5a8c583e57 100644 --- a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/spacefilters/SpaceFiltersView.kt +++ b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/spacefilters/SpaceFiltersView.kt @@ -12,6 +12,7 @@ import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.navigationBarsPadding @@ -32,9 +33,11 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import io.element.android.compound.theme.ElementTheme +import io.element.android.features.home.impl.R import io.element.android.libraries.designsystem.components.avatar.Avatar import io.element.android.libraries.designsystem.components.avatar.AvatarSize import io.element.android.libraries.designsystem.components.avatar.AvatarType @@ -97,16 +100,20 @@ private fun SpaceFiltersBottomSheetContent( Column( modifier = modifier .fillMaxWidth() - .padding(vertical = 16.dp, horizontal = 16.dp) + .fillMaxHeight(0.9f) + .padding(vertical = 16.dp) ) { Text( - text = "Your spaces", + text = stringResource(R.string.screen_roomlist_your_spaces), style = ElementTheme.typography.fontHeadingSmMedium, + modifier = Modifier.padding(horizontal = 16.dp), + maxLines = 1, + overflow = TextOverflow.Ellipsis, ) Spacer(modifier = Modifier.height(12.dp)) SearchField( state = searchQuery, - modifier = Modifier.fillMaxWidth(), + modifier = Modifier.fillMaxWidth().padding(horizontal = 16.dp), placeholder = stringResource(CommonStrings.action_search), ) Spacer(modifier = Modifier.height(16.dp)) @@ -149,12 +156,16 @@ private fun SpaceFilterItem( text = spaceRoom.displayName, style = ElementTheme.typography.fontBodyLgMedium, color = ElementTheme.colors.textPrimary, + maxLines = 1, + overflow = TextOverflow.Ellipsis, ) if (supportingText != null) { Text( text = supportingText, style = ElementTheme.typography.fontBodyMdRegular, color = ElementTheme.colors.textSecondary, + maxLines = 1, + overflow = TextOverflow.Ellipsis, ) } }