Merge pull request #5599 from element-hq/feature/fga/home_topbar_2
Design : update Home TopBar and RoomList Filters
This commit is contained in:
@@ -32,5 +32,6 @@ data class HomeState(
|
||||
val eventSink: (HomeEvents) -> Unit,
|
||||
) {
|
||||
val displayActions = currentHomeNavigationBarItem == HomeNavigationBarItem.Chats
|
||||
val displayRoomListFilters = currentHomeNavigationBarItem == HomeNavigationBarItem.Chats && roomListState.displayFilters
|
||||
val showNavigationBar = isSpaceFeatureEnabled && homeSpacesState.spaceRooms.isNotEmpty()
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ import androidx.compose.foundation.layout.calculateStartPadding
|
||||
import androidx.compose.foundation.layout.consumeWindowInsets
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.material3.rememberTopAppBarState
|
||||
@@ -39,9 +40,9 @@ import dev.chrisbanes.haze.materials.HazeMaterials
|
||||
import dev.chrisbanes.haze.rememberHazeState
|
||||
import io.element.android.compound.theme.ElementTheme
|
||||
import io.element.android.compound.tokens.generated.CompoundIcons
|
||||
import io.element.android.features.home.impl.components.HomeTopBar
|
||||
import io.element.android.features.home.impl.components.RoomListContentView
|
||||
import io.element.android.features.home.impl.components.RoomListMenuAction
|
||||
import io.element.android.features.home.impl.components.RoomListTopBar
|
||||
import io.element.android.features.home.impl.model.RoomListRoomSummary
|
||||
import io.element.android.features.home.impl.roomlist.RoomListContextMenu
|
||||
import io.element.android.features.home.impl.roomlist.RoomListDeclineInviteMenu
|
||||
@@ -62,6 +63,7 @@ import io.element.android.libraries.designsystem.theme.components.Scaffold
|
||||
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarHost
|
||||
import io.element.android.libraries.designsystem.utils.snackbar.rememberSnackbarHostState
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@Composable
|
||||
fun HomeView(
|
||||
@@ -143,7 +145,7 @@ private fun HomeScaffold(
|
||||
}
|
||||
|
||||
val appBarState = rememberTopAppBarState()
|
||||
val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior(appBarState)
|
||||
val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior(appBarState)
|
||||
val snackbarHostState = rememberSnackbarHostState(snackbarMessage = state.snackbarMessage)
|
||||
val roomListState: RoomListState = state.roomListState
|
||||
|
||||
@@ -154,11 +156,13 @@ private fun HomeScaffold(
|
||||
}
|
||||
|
||||
val hazeState = rememberHazeState()
|
||||
val roomsLazyListState = rememberLazyListState()
|
||||
val spacesLazyListState = rememberLazyListState()
|
||||
|
||||
Scaffold(
|
||||
modifier = modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
|
||||
topBar = {
|
||||
RoomListTopBar(
|
||||
HomeTopBar(
|
||||
title = stringResource(state.currentHomeNavigationBarItem.labelRes),
|
||||
currentUserAndNeighbors = state.currentUserAndNeighbors,
|
||||
showAvatarIndicator = state.showAvatarIndicator,
|
||||
@@ -171,7 +175,7 @@ private fun HomeScaffold(
|
||||
},
|
||||
scrollBehavior = scrollBehavior,
|
||||
displayMenuItems = state.displayActions,
|
||||
displayFilters = roomListState.displayFilters && state.currentHomeNavigationBarItem == HomeNavigationBarItem.Chats,
|
||||
displayFilters = state.displayRoomListFilters,
|
||||
filtersState = roomListState.filtersState,
|
||||
canReportBug = state.canReportBug,
|
||||
modifier = if (state.isSpaceFeatureEnabled) {
|
||||
@@ -180,41 +184,39 @@ private fun HomeScaffold(
|
||||
style = HazeMaterials.thick(),
|
||||
)
|
||||
} else {
|
||||
Modifier
|
||||
.background(ElementTheme.colors.bgCanvasDefault)
|
||||
Modifier.background(ElementTheme.colors.bgCanvasDefault)
|
||||
}
|
||||
)
|
||||
},
|
||||
bottomBar = {
|
||||
if (state.showNavigationBar) {
|
||||
NavigationBar(
|
||||
containerColor = Color.Transparent,
|
||||
modifier = Modifier
|
||||
.hazeEffect(
|
||||
state = hazeState,
|
||||
style = HazeMaterials.thick(),
|
||||
)
|
||||
) {
|
||||
HomeNavigationBarItem.entries.forEach { item ->
|
||||
val isSelected = state.currentHomeNavigationBarItem == item
|
||||
NavigationBarItem(
|
||||
selected = isSelected,
|
||||
onClick = {
|
||||
state.eventSink(HomeEvents.SelectHomeNavigationBarItem(item))
|
||||
},
|
||||
icon = {
|
||||
NavigationBarIcon(
|
||||
imageVector = item.icon(isSelected),
|
||||
)
|
||||
},
|
||||
label = {
|
||||
NavigationBarText(
|
||||
text = stringResource(item.labelRes),
|
||||
)
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
HomeBottomBar(
|
||||
currentHomeNavigationBarItem = state.currentHomeNavigationBarItem,
|
||||
onItemClick = { item ->
|
||||
// scroll to top if selecting the same item
|
||||
if (item == state.currentHomeNavigationBarItem) {
|
||||
val lazyListStateTarget = when (item) {
|
||||
HomeNavigationBarItem.Chats -> roomsLazyListState
|
||||
HomeNavigationBarItem.Spaces -> spacesLazyListState
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
coroutineScope.launch {
|
||||
if (lazyListStateTarget.firstVisibleItemIndex > 10) {
|
||||
lazyListStateTarget.scrollToItem(10)
|
||||
}
|
||||
// Also reset the scrollBehavior height offset as it's not triggered by programmatic scrolls
|
||||
scrollBehavior.state.heightOffset = 0f
|
||||
lazyListStateTarget.animateScrollToItem(0)
|
||||
}
|
||||
} else {
|
||||
state.eventSink(HomeEvents.SelectHomeNavigationBarItem(item))
|
||||
}
|
||||
},
|
||||
modifier = Modifier.hazeEffect(
|
||||
state = hazeState,
|
||||
style = HazeMaterials.thick(),
|
||||
)
|
||||
)
|
||||
}
|
||||
},
|
||||
content = { padding ->
|
||||
@@ -223,6 +225,7 @@ private fun HomeScaffold(
|
||||
RoomListContentView(
|
||||
contentState = roomListState.contentState,
|
||||
filtersState = roomListState.filtersState,
|
||||
lazyListState = roomsLazyListState,
|
||||
hideInvitesAvatars = roomListState.hideInvitesAvatars,
|
||||
eventSink = roomListState.eventSink,
|
||||
onSetUpRecoveryClick = onSetUpRecoveryClick,
|
||||
@@ -260,6 +263,7 @@ private fun HomeScaffold(
|
||||
.consumeWindowInsets(padding)
|
||||
.hazeSource(state = hazeState),
|
||||
state = state.homeSpacesState,
|
||||
lazyListState = spacesLazyListState,
|
||||
onSpaceClick = { spaceId ->
|
||||
onRoomClick(spaceId)
|
||||
}
|
||||
@@ -283,6 +287,38 @@ private fun HomeScaffold(
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun HomeBottomBar(
|
||||
currentHomeNavigationBarItem: HomeNavigationBarItem,
|
||||
onItemClick: (HomeNavigationBarItem) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
NavigationBar(
|
||||
containerColor = Color.Transparent,
|
||||
modifier = modifier
|
||||
) {
|
||||
HomeNavigationBarItem.entries.forEach { item ->
|
||||
val isSelected = currentHomeNavigationBarItem == item
|
||||
NavigationBarItem(
|
||||
selected = isSelected,
|
||||
onClick = {
|
||||
onItemClick(item)
|
||||
},
|
||||
icon = {
|
||||
NavigationBarIcon(
|
||||
imageVector = item.icon(isSelected),
|
||||
)
|
||||
},
|
||||
label = {
|
||||
NavigationBarText(
|
||||
text = stringResource(item.labelRes),
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal fun RoomListRoomSummary.contentType() = displayType.ordinal
|
||||
|
||||
@PreviewsDayNight
|
||||
|
||||
@@ -10,14 +10,12 @@ package io.element.android.features.home.impl.components
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
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.MaterialTheme
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.material3.TopAppBarScrollBehavior
|
||||
import androidx.compose.material3.rememberTopAppBarState
|
||||
@@ -32,7 +30,6 @@ import androidx.compose.runtime.setValue
|
||||
import androidx.compose.runtime.snapshotFlow
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.alpha
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.semantics.heading
|
||||
@@ -46,22 +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.aRoomListFiltersState
|
||||
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.AvatarSize
|
||||
import io.element.android.libraries.designsystem.components.avatar.AvatarType
|
||||
import io.element.android.libraries.designsystem.modifiers.backgroundVerticalGradient
|
||||
import io.element.android.libraries.designsystem.preview.ElementPreview
|
||||
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
||||
import io.element.android.libraries.designsystem.text.applyScaleDown
|
||||
import io.element.android.libraries.designsystem.text.toSp
|
||||
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.DropdownMenuItem
|
||||
import io.element.android.libraries.designsystem.theme.components.HorizontalDivider
|
||||
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.MediumTopAppBar
|
||||
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.UserId
|
||||
import io.element.android.libraries.matrix.api.user.MatrixUser
|
||||
@@ -76,7 +71,7 @@ import kotlinx.collections.immutable.toImmutableList
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun RoomListTopBar(
|
||||
fun HomeTopBar(
|
||||
title: String,
|
||||
currentUserAndNeighbors: ImmutableList<MatrixUser>,
|
||||
showAvatarIndicator: Boolean,
|
||||
@@ -87,169 +82,113 @@ fun RoomListTopBar(
|
||||
onAccountSwitch: (SessionId) -> Unit,
|
||||
scrollBehavior: TopAppBarScrollBehavior,
|
||||
displayMenuItems: Boolean,
|
||||
canReportBug: Boolean,
|
||||
displayFilters: Boolean,
|
||||
filtersState: RoomListFiltersState,
|
||||
canReportBug: Boolean,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
DefaultRoomListTopBar(
|
||||
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 DefaultRoomListTopBar(
|
||||
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,
|
||||
) {
|
||||
val collapsedFraction = scrollBehavior.state.collapsedFraction
|
||||
Box(modifier = modifier) {
|
||||
val collapsedTitleTextStyle = ElementTheme.typography.aliasScreenTitle
|
||||
val expandedTitleTextStyle = ElementTheme.typography.fontHeadingLgBold.copy(
|
||||
// Due to a limitation of MediumTopAppBar, and to avoid the text to be truncated,
|
||||
// ensure that the font size will never be bigger than 28.dp.
|
||||
fontSize = 28.dp.applyScaleDown().toSp()
|
||||
)
|
||||
MaterialTheme(
|
||||
colorScheme = ElementTheme.materialColors,
|
||||
shapes = MaterialTheme.shapes,
|
||||
typography = ElementTheme.materialTypography.copy(
|
||||
headlineSmall = expandedTitleTextStyle,
|
||||
titleLarge = collapsedTitleTextStyle
|
||||
Column(modifier) {
|
||||
TopAppBar(
|
||||
modifier = Modifier
|
||||
.backgroundVerticalGradient(
|
||||
isVisible = !areSearchResultsDisplayed,
|
||||
)
|
||||
.statusBarsPadding(),
|
||||
colors = TopAppBarDefaults.topAppBarColors(
|
||||
containerColor = Color.Transparent,
|
||||
scrolledContainerColor = Color.Transparent,
|
||||
),
|
||||
) {
|
||||
Column {
|
||||
MediumTopAppBar(
|
||||
modifier = Modifier
|
||||
.backgroundVerticalGradient(
|
||||
isVisible = !areSearchResultsDisplayed,
|
||||
)
|
||||
.statusBarsPadding(),
|
||||
colors = TopAppBarDefaults.mediumTopAppBarColors(
|
||||
containerColor = Color.Transparent,
|
||||
scrolledContainerColor = Color.Transparent,
|
||||
),
|
||||
title = {
|
||||
Text(
|
||||
modifier = Modifier.semantics {
|
||||
heading()
|
||||
},
|
||||
text = title,
|
||||
)
|
||||
title = {
|
||||
Text(
|
||||
modifier = Modifier.semantics {
|
||||
heading()
|
||||
},
|
||||
navigationIcon = {
|
||||
NavigationIcon(
|
||||
currentUserAndNeighbors = currentUserAndNeighbors,
|
||||
showAvatarIndicator = showAvatarIndicator,
|
||||
onAccountSwitch = onAccountSwitch,
|
||||
onClick = onOpenSettings,
|
||||
style = ElementTheme.typography.aliasScreenTitle,
|
||||
text = title,
|
||||
)
|
||||
},
|
||||
navigationIcon = {
|
||||
NavigationIcon(
|
||||
currentUserAndNeighbors = currentUserAndNeighbors,
|
||||
showAvatarIndicator = showAvatarIndicator,
|
||||
onAccountSwitch = onAccountSwitch,
|
||||
onClick = onOpenSettings,
|
||||
)
|
||||
},
|
||||
actions = {
|
||||
if (displayMenuItems) {
|
||||
IconButton(
|
||||
onClick = onToggleSearch,
|
||||
) {
|
||||
Icon(
|
||||
imageVector = CompoundIcons.Search(),
|
||||
contentDescription = stringResource(CommonStrings.action_search),
|
||||
)
|
||||
},
|
||||
actions = {
|
||||
if (displayMenuItems) {
|
||||
IconButton(
|
||||
onClick = onSearchClick,
|
||||
) {
|
||||
Icon(
|
||||
imageVector = CompoundIcons.Search(),
|
||||
contentDescription = stringResource(CommonStrings.action_search),
|
||||
}
|
||||
if (RoomListConfig.HAS_DROP_DOWN_MENU) {
|
||||
var showMenu by remember { mutableStateOf(false) }
|
||||
IconButton(
|
||||
onClick = { showMenu = !showMenu }
|
||||
) {
|
||||
Icon(
|
||||
imageVector = CompoundIcons.OverflowVertical(),
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
DropdownMenu(
|
||||
expanded = showMenu,
|
||||
onDismissRequest = { showMenu = false }
|
||||
) {
|
||||
if (RoomListConfig.SHOW_INVITE_MENU_ITEM) {
|
||||
DropdownMenuItem(
|
||||
onClick = {
|
||||
showMenu = false
|
||||
onMenuActionClick(RoomListMenuAction.InviteFriends)
|
||||
},
|
||||
text = { Text(stringResource(id = CommonStrings.action_invite)) },
|
||||
leadingIcon = {
|
||||
Icon(
|
||||
imageVector = CompoundIcons.ShareAndroid(),
|
||||
tint = ElementTheme.colors.iconSecondary,
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
if (RoomListConfig.HAS_DROP_DOWN_MENU) {
|
||||
var showMenu by remember { mutableStateOf(false) }
|
||||
IconButton(
|
||||
onClick = { showMenu = !showMenu }
|
||||
) {
|
||||
Icon(
|
||||
imageVector = CompoundIcons.OverflowVertical(),
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
DropdownMenu(
|
||||
expanded = showMenu,
|
||||
onDismissRequest = { showMenu = false }
|
||||
) {
|
||||
if (RoomListConfig.SHOW_INVITE_MENU_ITEM) {
|
||||
DropdownMenuItem(
|
||||
onClick = {
|
||||
showMenu = false
|
||||
onMenuActionClick(RoomListMenuAction.InviteFriends)
|
||||
},
|
||||
text = { Text(stringResource(id = CommonStrings.action_invite)) },
|
||||
leadingIcon = {
|
||||
Icon(
|
||||
imageVector = CompoundIcons.ShareAndroid(),
|
||||
tint = ElementTheme.colors.iconSecondary,
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
if (RoomListConfig.SHOW_REPORT_PROBLEM_MENU_ITEM && canReportBug) {
|
||||
DropdownMenuItem(
|
||||
onClick = {
|
||||
showMenu = false
|
||||
onMenuActionClick(RoomListMenuAction.ReportBug)
|
||||
},
|
||||
text = { Text(stringResource(id = CommonStrings.common_report_a_problem)) },
|
||||
leadingIcon = {
|
||||
Icon(
|
||||
imageVector = CompoundIcons.ChatProblem(),
|
||||
tint = ElementTheme.colors.iconSecondary,
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
if (RoomListConfig.SHOW_REPORT_PROBLEM_MENU_ITEM && canReportBug) {
|
||||
DropdownMenuItem(
|
||||
onClick = {
|
||||
showMenu = false
|
||||
onMenuActionClick(RoomListMenuAction.ReportBug)
|
||||
},
|
||||
text = { Text(stringResource(id = CommonStrings.common_report_a_problem)) },
|
||||
leadingIcon = {
|
||||
Icon(
|
||||
imageVector = CompoundIcons.ChatProblem(),
|
||||
tint = ElementTheme.colors.iconSecondary,
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
scrollBehavior = scrollBehavior,
|
||||
windowInsets = WindowInsets(0.dp),
|
||||
)
|
||||
if (displayFilters) {
|
||||
RoomListFiltersView(
|
||||
state = filtersState,
|
||||
modifier = Modifier.padding(bottom = 16.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
// We want a 16dp left padding for the navigationIcon :
|
||||
// 4dp from default TopAppBarHorizontalPadding
|
||||
// 8dp from AccountIcon default padding (because of IconButton)
|
||||
// 4dp extra padding using left insets
|
||||
windowInsets = WindowInsets(left = 4.dp),
|
||||
)
|
||||
if (displayFilters) {
|
||||
TopAppBarScrollBehaviorLayout(scrollBehavior = scrollBehavior) {
|
||||
RoomListFiltersView(
|
||||
state = filtersState,
|
||||
modifier = Modifier.padding(bottom = 16.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
HorizontalDivider(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.alpha(collapsedFraction)
|
||||
.align(Alignment.BottomCenter),
|
||||
color = ElementTheme.materialColors.outlineVariant,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -301,9 +240,11 @@ private fun AccountIcon(
|
||||
isCurrentAccount: Boolean,
|
||||
showAvatarIndicator: Boolean,
|
||||
onClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val testTag = if (isCurrentAccount) Modifier.testTag(TestTags.homeScreenSettings) else Modifier
|
||||
IconButton(
|
||||
modifier = if (isCurrentAccount) Modifier.testTag(TestTags.homeScreenSettings) else Modifier,
|
||||
modifier = modifier.then(testTag),
|
||||
onClick = onClick,
|
||||
) {
|
||||
Box {
|
||||
@@ -329,20 +270,20 @@ private fun AccountIcon(
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@PreviewsDayNight
|
||||
@Composable
|
||||
internal fun DefaultRoomListTopBarPreview() = ElementPreview {
|
||||
DefaultRoomListTopBar(
|
||||
internal fun HomeTopBarPreview() = ElementPreview {
|
||||
HomeTopBar(
|
||||
title = stringResource(R.string.screen_roomlist_main_space_title),
|
||||
currentUserAndNeighbors = persistentListOf(MatrixUser(UserId("@id:domain"), "Alice")),
|
||||
showAvatarIndicator = false,
|
||||
areSearchResultsDisplayed = false,
|
||||
scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior(rememberTopAppBarState()),
|
||||
scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState()),
|
||||
onOpenSettings = {},
|
||||
onAccountSwitch = {},
|
||||
onSearchClick = {},
|
||||
onToggleSearch = {},
|
||||
displayMenuItems = true,
|
||||
canReportBug = true,
|
||||
displayFilters = true,
|
||||
filtersState = aRoomListFiltersState(),
|
||||
canReportBug = true,
|
||||
onMenuActionClick = {},
|
||||
)
|
||||
}
|
||||
@@ -350,20 +291,20 @@ internal fun DefaultRoomListTopBarPreview() = ElementPreview {
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@PreviewsDayNight
|
||||
@Composable
|
||||
internal fun DefaultRoomListTopBarWithIndicatorPreview() = ElementPreview {
|
||||
DefaultRoomListTopBar(
|
||||
internal fun HomeTopBarWithIndicatorPreview() = ElementPreview {
|
||||
HomeTopBar(
|
||||
title = stringResource(R.string.screen_roomlist_main_space_title),
|
||||
currentUserAndNeighbors = persistentListOf(MatrixUser(UserId("@id:domain"), "Alice")),
|
||||
showAvatarIndicator = true,
|
||||
areSearchResultsDisplayed = false,
|
||||
scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior(rememberTopAppBarState()),
|
||||
scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState()),
|
||||
onOpenSettings = {},
|
||||
onAccountSwitch = {},
|
||||
onSearchClick = {},
|
||||
onToggleSearch = {},
|
||||
displayMenuItems = true,
|
||||
canReportBug = true,
|
||||
displayFilters = true,
|
||||
filtersState = aRoomListFiltersState(),
|
||||
canReportBug = true,
|
||||
onMenuActionClick = {},
|
||||
)
|
||||
}
|
||||
@@ -371,20 +312,20 @@ internal fun DefaultRoomListTopBarWithIndicatorPreview() = ElementPreview {
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@PreviewsDayNight
|
||||
@Composable
|
||||
internal fun DefaultRoomListTopBarMultiAccountPreview() = ElementPreview {
|
||||
DefaultRoomListTopBar(
|
||||
internal fun HomeTopBarMultiAccountPreview() = ElementPreview {
|
||||
HomeTopBar(
|
||||
title = stringResource(R.string.screen_roomlist_main_space_title),
|
||||
currentUserAndNeighbors = aMatrixUserList().take(3).toImmutableList(),
|
||||
showAvatarIndicator = false,
|
||||
areSearchResultsDisplayed = false,
|
||||
scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior(rememberTopAppBarState()),
|
||||
scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState()),
|
||||
onOpenSettings = {},
|
||||
onAccountSwitch = {},
|
||||
onSearchClick = {},
|
||||
onToggleSearch = {},
|
||||
displayMenuItems = true,
|
||||
canReportBug = true,
|
||||
displayFilters = true,
|
||||
filtersState = aRoomListFiltersState(),
|
||||
canReportBug = true,
|
||||
onMenuActionClick = {},
|
||||
)
|
||||
}
|
||||
@@ -18,6 +18,7 @@ import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.height
|
||||
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.runtime.Composable
|
||||
@@ -60,6 +61,7 @@ import kotlinx.collections.immutable.ImmutableList
|
||||
fun RoomListContentView(
|
||||
contentState: RoomListContentState,
|
||||
filtersState: RoomListFiltersState,
|
||||
lazyListState: LazyListState,
|
||||
hideInvitesAvatars: Boolean,
|
||||
eventSink: (RoomListEvents) -> Unit,
|
||||
onSetUpRecoveryClick: () -> Unit,
|
||||
@@ -97,6 +99,7 @@ fun RoomListContentView(
|
||||
onSetUpRecoveryClick = onSetUpRecoveryClick,
|
||||
onConfirmRecoveryKeyClick = onConfirmRecoveryKeyClick,
|
||||
onRoomClick = onRoomClick,
|
||||
lazyListState = lazyListState,
|
||||
contentPadding = contentPadding,
|
||||
)
|
||||
}
|
||||
@@ -176,6 +179,7 @@ private fun RoomsView(
|
||||
onConfirmRecoveryKeyClick: () -> Unit,
|
||||
onRoomClick: (RoomListRoomSummary) -> Unit,
|
||||
contentPadding: PaddingValues,
|
||||
lazyListState: LazyListState,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
if (state.summaries.isEmpty() && filtersState.hasAnyFilterSelected) {
|
||||
@@ -192,6 +196,7 @@ private fun RoomsView(
|
||||
onConfirmRecoveryKeyClick = onConfirmRecoveryKeyClick,
|
||||
onRoomClick = onRoomClick,
|
||||
contentPadding = contentPadding,
|
||||
lazyListState = lazyListState,
|
||||
modifier = modifier.fillMaxSize(),
|
||||
)
|
||||
}
|
||||
@@ -206,9 +211,9 @@ private fun RoomsViewList(
|
||||
onConfirmRecoveryKeyClick: () -> Unit,
|
||||
onRoomClick: (RoomListRoomSummary) -> Unit,
|
||||
contentPadding: PaddingValues,
|
||||
lazyListState: LazyListState,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val lazyListState = rememberLazyListState()
|
||||
val visibleRange by remember {
|
||||
derivedStateOf {
|
||||
val layoutInfo = lazyListState.layoutInfo
|
||||
@@ -343,6 +348,7 @@ internal fun RoomListContentViewPreview(@PreviewParameter(RoomListContentStatePr
|
||||
onConfirmRecoveryKeyClick = {},
|
||||
onRoomClick = {},
|
||||
onCreateRoomClick = {},
|
||||
lazyListState = rememberLazyListState(),
|
||||
contentPadding = PaddingValues(0.dp),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
package io.element.android.features.home.impl.spaces
|
||||
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.LazyListState
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
@@ -26,10 +28,14 @@ import kotlinx.collections.immutable.toImmutableList
|
||||
@Composable
|
||||
fun HomeSpacesView(
|
||||
state: HomeSpacesState,
|
||||
lazyListState: LazyListState,
|
||||
onSpaceClick: (RoomId) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
LazyColumn(modifier) {
|
||||
LazyColumn(
|
||||
modifier = modifier,
|
||||
state = lazyListState
|
||||
) {
|
||||
val space = state.space
|
||||
when (space) {
|
||||
CurrentSpace.Root -> {
|
||||
@@ -77,6 +83,7 @@ internal fun HomeSpacesViewPreview(
|
||||
) = ElementPreview {
|
||||
HomeSpacesView(
|
||||
state = state,
|
||||
lazyListState = rememberLazyListState(),
|
||||
onSpaceClick = {},
|
||||
modifier = Modifier,
|
||||
)
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -81,11 +81,11 @@ class KonsistPreviewTest {
|
||||
"BackgroundVerticalGradientDisabledPreview",
|
||||
"BackgroundVerticalGradientPreview",
|
||||
"ColorAliasesPreview",
|
||||
"DefaultRoomListTopBarMultiAccountPreview",
|
||||
"DefaultRoomListTopBarWithIndicatorPreview",
|
||||
"FocusedEventPreview",
|
||||
"GradientFloatingActionButtonCircleShapePreview",
|
||||
"HeaderFooterPageScrollablePreview",
|
||||
"HomeTopBarMultiAccountPreview",
|
||||
"HomeTopBarWithIndicatorPreview",
|
||||
"IconsOtherPreview",
|
||||
"MarkdownTextComposerEditPreview",
|
||||
"MatrixBadgeAtomInfoPreview",
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user