Add space filter button to HomeTopBar and integrate SpaceFiltersView

This commit is contained in:
ganfra
2026-02-02 21:02:43 +01:00
parent c993a6b387
commit eb94015996
5 changed files with 112 additions and 18 deletions

View File

@@ -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(

View File

@@ -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<MatrixUser>,
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<MatrixUser>,
@@ -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 = {},
)
}

View File

@@ -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,

View File

@@ -18,10 +18,8 @@ import kotlinx.collections.immutable.persistentListOf
class SpaceFiltersStateProvider : PreviewParameterProvider<SpaceFiltersState> {
override val values: Sequence<SpaceFiltersState>
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,
)

View File

@@ -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,
)
}
}