design(home) : use enterAlwaysScrollBehavior for the RoomListFiltersView
This commit is contained in:
parent
0498c3e4ed
commit
eb2b527236
4 changed files with 90 additions and 64 deletions
|
|
@ -32,5 +32,6 @@ data class HomeState(
|
||||||
val eventSink: (HomeEvents) -> Unit,
|
val eventSink: (HomeEvents) -> Unit,
|
||||||
) {
|
) {
|
||||||
val displayActions = currentHomeNavigationBarItem == HomeNavigationBarItem.Chats
|
val displayActions = currentHomeNavigationBarItem == HomeNavigationBarItem.Chats
|
||||||
|
val displayRoomListFilters = currentHomeNavigationBarItem == HomeNavigationBarItem.Chats && roomListState.displayFilters
|
||||||
val showNavigationBar = isSpaceFeatureEnabled && homeSpacesState.spaceRooms.isNotEmpty()
|
val showNavigationBar = isSpaceFeatureEnabled && homeSpacesState.spaceRooms.isNotEmpty()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.rememberCoroutineScope
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.draw.shadow
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||||
import androidx.compose.ui.platform.LocalLayoutDirection
|
import androidx.compose.ui.platform.LocalLayoutDirection
|
||||||
|
|
@ -143,7 +144,7 @@ private fun HomeScaffold(
|
||||||
}
|
}
|
||||||
|
|
||||||
val appBarState = rememberTopAppBarState()
|
val appBarState = rememberTopAppBarState()
|
||||||
val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior(appBarState)
|
val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior(appBarState)
|
||||||
val snackbarHostState = rememberSnackbarHostState(snackbarMessage = state.snackbarMessage)
|
val snackbarHostState = rememberSnackbarHostState(snackbarMessage = state.snackbarMessage)
|
||||||
val roomListState: RoomListState = state.roomListState
|
val roomListState: RoomListState = state.roomListState
|
||||||
|
|
||||||
|
|
@ -171,7 +172,7 @@ private fun HomeScaffold(
|
||||||
},
|
},
|
||||||
scrollBehavior = scrollBehavior,
|
scrollBehavior = scrollBehavior,
|
||||||
displayMenuItems = state.displayActions,
|
displayMenuItems = state.displayActions,
|
||||||
displayFilters = roomListState.displayFilters && state.currentHomeNavigationBarItem == HomeNavigationBarItem.Chats,
|
displayFilters = state.displayRoomListFilters,
|
||||||
filtersState = roomListState.filtersState,
|
filtersState = roomListState.filtersState,
|
||||||
canReportBug = state.canReportBug,
|
canReportBug = state.canReportBug,
|
||||||
modifier = if (state.isSpaceFeatureEnabled) {
|
modifier = if (state.isSpaceFeatureEnabled) {
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,6 @@ import androidx.compose.foundation.layout.statusBarsPadding
|
||||||
import androidx.compose.foundation.pager.VerticalPager
|
import androidx.compose.foundation.pager.VerticalPager
|
||||||
import androidx.compose.foundation.pager.rememberPagerState
|
import androidx.compose.foundation.pager.rememberPagerState
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.TopAppBar
|
|
||||||
import androidx.compose.material3.TopAppBarDefaults
|
import androidx.compose.material3.TopAppBarDefaults
|
||||||
import androidx.compose.material3.TopAppBarScrollBehavior
|
import androidx.compose.material3.TopAppBarScrollBehavior
|
||||||
import androidx.compose.material3.rememberTopAppBarState
|
import androidx.compose.material3.rememberTopAppBarState
|
||||||
|
|
@ -44,18 +43,20 @@ 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.RoomListFiltersView
|
||||||
import io.element.android.features.home.impl.filters.aRoomListFiltersState
|
import io.element.android.features.home.impl.filters.aRoomListFiltersState
|
||||||
import io.element.android.libraries.designsystem.atomic.atoms.RedIndicatorAtom
|
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
|
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.AvatarSize
|
||||||
import io.element.android.libraries.designsystem.components.avatar.AvatarType
|
import io.element.android.libraries.designsystem.components.avatar.AvatarType
|
||||||
import io.element.android.libraries.designsystem.modifiers.backgroundVerticalGradient
|
import io.element.android.libraries.designsystem.modifiers.backgroundVerticalGradient
|
||||||
import io.element.android.libraries.designsystem.preview.ElementPreview
|
import io.element.android.libraries.designsystem.preview.ElementPreview
|
||||||
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
||||||
|
import io.element.android.libraries.designsystem.theme.aliasScreenTitle
|
||||||
import io.element.android.libraries.designsystem.theme.components.DropdownMenu
|
import io.element.android.libraries.designsystem.theme.components.DropdownMenu
|
||||||
import io.element.android.libraries.designsystem.theme.components.DropdownMenuItem
|
import io.element.android.libraries.designsystem.theme.components.DropdownMenuItem
|
||||||
import io.element.android.libraries.designsystem.theme.components.Icon
|
import io.element.android.libraries.designsystem.theme.components.Icon
|
||||||
import io.element.android.libraries.designsystem.theme.components.IconButton
|
import io.element.android.libraries.designsystem.theme.components.IconButton
|
||||||
import io.element.android.libraries.designsystem.theme.components.MediumTopAppBar
|
|
||||||
import io.element.android.libraries.designsystem.theme.components.Text
|
import io.element.android.libraries.designsystem.theme.components.Text
|
||||||
|
import io.element.android.libraries.designsystem.theme.components.TopAppBar
|
||||||
import io.element.android.libraries.matrix.api.core.SessionId
|
import io.element.android.libraries.matrix.api.core.SessionId
|
||||||
import io.element.android.libraries.matrix.api.core.UserId
|
import io.element.android.libraries.matrix.api.core.UserId
|
||||||
import io.element.android.libraries.matrix.api.user.MatrixUser
|
import io.element.android.libraries.matrix.api.user.MatrixUser
|
||||||
|
|
@ -81,45 +82,9 @@ fun HomeTopBar(
|
||||||
onAccountSwitch: (SessionId) -> Unit,
|
onAccountSwitch: (SessionId) -> Unit,
|
||||||
scrollBehavior: TopAppBarScrollBehavior,
|
scrollBehavior: TopAppBarScrollBehavior,
|
||||||
displayMenuItems: Boolean,
|
displayMenuItems: Boolean,
|
||||||
|
canReportBug: Boolean,
|
||||||
displayFilters: Boolean,
|
displayFilters: Boolean,
|
||||||
filtersState: RoomListFiltersState,
|
filtersState: RoomListFiltersState,
|
||||||
canReportBug: Boolean,
|
|
||||||
modifier: Modifier = Modifier,
|
|
||||||
) {
|
|
||||||
DefaultHomeTopBar(
|
|
||||||
title = title,
|
|
||||||
currentUserAndNeighbors = currentUserAndNeighbors,
|
|
||||||
showAvatarIndicator = showAvatarIndicator,
|
|
||||||
areSearchResultsDisplayed = areSearchResultsDisplayed,
|
|
||||||
onOpenSettings = onOpenSettings,
|
|
||||||
onAccountSwitch = onAccountSwitch,
|
|
||||||
onSearchClick = onToggleSearch,
|
|
||||||
onMenuActionClick = onMenuActionClick,
|
|
||||||
scrollBehavior = scrollBehavior,
|
|
||||||
displayMenuItems = displayMenuItems,
|
|
||||||
displayFilters = displayFilters,
|
|
||||||
filtersState = filtersState,
|
|
||||||
canReportBug = canReportBug,
|
|
||||||
modifier = modifier,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
|
||||||
@Composable
|
|
||||||
private fun DefaultHomeTopBar(
|
|
||||||
title: String,
|
|
||||||
currentUserAndNeighbors: ImmutableList<MatrixUser>,
|
|
||||||
showAvatarIndicator: Boolean,
|
|
||||||
areSearchResultsDisplayed: Boolean,
|
|
||||||
scrollBehavior: TopAppBarScrollBehavior,
|
|
||||||
onOpenSettings: () -> Unit,
|
|
||||||
onAccountSwitch: (SessionId) -> Unit,
|
|
||||||
onSearchClick: () -> Unit,
|
|
||||||
onMenuActionClick: (RoomListMenuAction) -> Unit,
|
|
||||||
displayMenuItems: Boolean,
|
|
||||||
displayFilters: Boolean,
|
|
||||||
filtersState: RoomListFiltersState,
|
|
||||||
canReportBug: Boolean,
|
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
Column(modifier) {
|
Column(modifier) {
|
||||||
|
|
@ -138,6 +103,7 @@ private fun DefaultHomeTopBar(
|
||||||
modifier = Modifier.semantics {
|
modifier = Modifier.semantics {
|
||||||
heading()
|
heading()
|
||||||
},
|
},
|
||||||
|
style = ElementTheme.typography.aliasScreenTitle,
|
||||||
text = title,
|
text = title,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
|
@ -152,7 +118,7 @@ private fun DefaultHomeTopBar(
|
||||||
actions = {
|
actions = {
|
||||||
if (displayMenuItems) {
|
if (displayMenuItems) {
|
||||||
IconButton(
|
IconButton(
|
||||||
onClick = onSearchClick,
|
onClick = onToggleSearch,
|
||||||
) {
|
) {
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = CompoundIcons.Search(),
|
imageVector = CompoundIcons.Search(),
|
||||||
|
|
@ -209,15 +175,17 @@ private fun DefaultHomeTopBar(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
scrollBehavior = scrollBehavior,
|
//scrollBehavior = scrollBehavior,
|
||||||
// We need a 16dp left padding : 4dp default padding + 8dp IconButton padding + 4dp extra padding
|
// We need a 16dp left padding : 4dp default padding + 8dp IconButton padding + 4dp extra padding
|
||||||
windowInsets = WindowInsets(4.dp),
|
windowInsets = WindowInsets(left = 4.dp),
|
||||||
)
|
)
|
||||||
if (displayFilters) {
|
if (displayFilters) {
|
||||||
RoomListFiltersView(
|
TopAppBarScrollBehaviorLayout(scrollBehavior = scrollBehavior) {
|
||||||
state = filtersState,
|
RoomListFiltersView(
|
||||||
modifier = Modifier.padding(bottom = 16.dp)
|
state = filtersState,
|
||||||
)
|
modifier = Modifier.padding(bottom = 16.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -270,9 +238,11 @@ private fun AccountIcon(
|
||||||
isCurrentAccount: Boolean,
|
isCurrentAccount: Boolean,
|
||||||
showAvatarIndicator: Boolean,
|
showAvatarIndicator: Boolean,
|
||||||
onClick: () -> Unit,
|
onClick: () -> Unit,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
|
val testTag = if (isCurrentAccount) Modifier.testTag(TestTags.homeScreenSettings) else Modifier
|
||||||
IconButton(
|
IconButton(
|
||||||
modifier = if (isCurrentAccount) Modifier.testTag(TestTags.homeScreenSettings) else Modifier,
|
modifier = modifier.then(testTag),
|
||||||
onClick = onClick,
|
onClick = onClick,
|
||||||
) {
|
) {
|
||||||
Box {
|
Box {
|
||||||
|
|
@ -298,20 +268,20 @@ private fun AccountIcon(
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@PreviewsDayNight
|
@PreviewsDayNight
|
||||||
@Composable
|
@Composable
|
||||||
internal fun DefaultHomeTopBarPreview() = ElementPreview {
|
internal fun HomeTopBarPreview() = ElementPreview {
|
||||||
DefaultHomeTopBar(
|
HomeTopBar(
|
||||||
title = stringResource(R.string.screen_roomlist_main_space_title),
|
title = stringResource(R.string.screen_roomlist_main_space_title),
|
||||||
currentUserAndNeighbors = persistentListOf(MatrixUser(UserId("@id:domain"), "Alice")),
|
currentUserAndNeighbors = persistentListOf(MatrixUser(UserId("@id:domain"), "Alice")),
|
||||||
showAvatarIndicator = false,
|
showAvatarIndicator = false,
|
||||||
areSearchResultsDisplayed = false,
|
areSearchResultsDisplayed = false,
|
||||||
scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior(rememberTopAppBarState()),
|
scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState()),
|
||||||
onOpenSettings = {},
|
onOpenSettings = {},
|
||||||
onAccountSwitch = {},
|
onAccountSwitch = {},
|
||||||
onSearchClick = {},
|
onToggleSearch = {},
|
||||||
displayMenuItems = true,
|
displayMenuItems = true,
|
||||||
|
canReportBug = true,
|
||||||
displayFilters = true,
|
displayFilters = true,
|
||||||
filtersState = aRoomListFiltersState(),
|
filtersState = aRoomListFiltersState(),
|
||||||
canReportBug = true,
|
|
||||||
onMenuActionClick = {},
|
onMenuActionClick = {},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -319,20 +289,20 @@ internal fun DefaultHomeTopBarPreview() = ElementPreview {
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@PreviewsDayNight
|
@PreviewsDayNight
|
||||||
@Composable
|
@Composable
|
||||||
internal fun DefaultHomeTopBarWithIndicatorPreview() = ElementPreview {
|
internal fun HomeTopBarWithIndicatorPreview() = ElementPreview {
|
||||||
DefaultHomeTopBar(
|
HomeTopBar(
|
||||||
title = stringResource(R.string.screen_roomlist_main_space_title),
|
title = stringResource(R.string.screen_roomlist_main_space_title),
|
||||||
currentUserAndNeighbors = persistentListOf(MatrixUser(UserId("@id:domain"), "Alice")),
|
currentUserAndNeighbors = persistentListOf(MatrixUser(UserId("@id:domain"), "Alice")),
|
||||||
showAvatarIndicator = true,
|
showAvatarIndicator = true,
|
||||||
areSearchResultsDisplayed = false,
|
areSearchResultsDisplayed = false,
|
||||||
scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior(rememberTopAppBarState()),
|
scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState()),
|
||||||
onOpenSettings = {},
|
onOpenSettings = {},
|
||||||
onAccountSwitch = {},
|
onAccountSwitch = {},
|
||||||
onSearchClick = {},
|
onToggleSearch = {},
|
||||||
displayMenuItems = true,
|
displayMenuItems = true,
|
||||||
|
canReportBug = true,
|
||||||
displayFilters = true,
|
displayFilters = true,
|
||||||
filtersState = aRoomListFiltersState(),
|
filtersState = aRoomListFiltersState(),
|
||||||
canReportBug = true,
|
|
||||||
onMenuActionClick = {},
|
onMenuActionClick = {},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -340,20 +310,20 @@ internal fun DefaultHomeTopBarWithIndicatorPreview() = ElementPreview {
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@PreviewsDayNight
|
@PreviewsDayNight
|
||||||
@Composable
|
@Composable
|
||||||
internal fun DefaultHomeTopBarMultiAccountPreview() = ElementPreview {
|
internal fun HomeTopBarMultiAccountPreview() = ElementPreview {
|
||||||
DefaultHomeTopBar(
|
HomeTopBar(
|
||||||
title = stringResource(R.string.screen_roomlist_main_space_title),
|
title = stringResource(R.string.screen_roomlist_main_space_title),
|
||||||
currentUserAndNeighbors = aMatrixUserList().take(3).toImmutableList(),
|
currentUserAndNeighbors = aMatrixUserList().take(3).toImmutableList(),
|
||||||
showAvatarIndicator = false,
|
showAvatarIndicator = false,
|
||||||
areSearchResultsDisplayed = false,
|
areSearchResultsDisplayed = false,
|
||||||
scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior(rememberTopAppBarState()),
|
scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState()),
|
||||||
onOpenSettings = {},
|
onOpenSettings = {},
|
||||||
onAccountSwitch = {},
|
onAccountSwitch = {},
|
||||||
onSearchClick = {},
|
onToggleSearch = {},
|
||||||
displayMenuItems = true,
|
displayMenuItems = true,
|
||||||
|
canReportBug = true,
|
||||||
displayFilters = true,
|
displayFilters = true,
|
||||||
filtersState = aRoomListFiltersState(),
|
filtersState = aRoomListFiltersState(),
|
||||||
canReportBug = true,
|
|
||||||
onMenuActionClick = {},
|
onMenuActionClick = {},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,54 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2025 New Vector Ltd.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
|
* Please see LICENSE files in the repository root for full details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.element.android.libraries.designsystem.components
|
||||||
|
|
||||||
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
|
import androidx.compose.material3.Surface
|
||||||
|
import androidx.compose.material3.TopAppBarScrollBehavior
|
||||||
|
import androidx.compose.material3.contentColorFor
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.UiComposable
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.layout.Layout
|
||||||
|
import io.element.android.compound.theme.ElementTheme
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A layout that measures its content to set the height offset limit of a [TopAppBarScrollBehavior].
|
||||||
|
* It places the content according to the current height offset of the scroll behavior.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@ExperimentalMaterial3Api
|
||||||
|
@Composable
|
||||||
|
fun TopAppBarScrollBehaviorLayout(
|
||||||
|
scrollBehavior: TopAppBarScrollBehavior,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
backgroundColor: Color = ElementTheme.colors.bgCanvasDefault,
|
||||||
|
contentColor: Color = contentColorFor(backgroundColor),
|
||||||
|
content: @Composable @UiComposable () -> Unit,
|
||||||
|
) {
|
||||||
|
Surface(
|
||||||
|
modifier = modifier,
|
||||||
|
color = backgroundColor,
|
||||||
|
contentColor = contentColor
|
||||||
|
) {
|
||||||
|
Layout(
|
||||||
|
content = content,
|
||||||
|
measurePolicy = { measurables, constraints ->
|
||||||
|
val placeable = measurables.first().measure(constraints)
|
||||||
|
val contentHeight = placeable.height.toFloat()
|
||||||
|
scrollBehavior.state.heightOffsetLimit = -contentHeight
|
||||||
|
val heightOffset = scrollBehavior.state.heightOffset
|
||||||
|
val layoutHeight = (contentHeight + heightOffset).toInt()
|
||||||
|
layout(placeable.width, layoutHeight) {
|
||||||
|
placeable.place(0, heightOffset.toInt())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue