design(home) : allow scrolling to top when clicking on already selected tab
This commit is contained in:
parent
eb2b527236
commit
a5b4f50334
3 changed files with 81 additions and 33 deletions
|
|
@ -18,6 +18,7 @@ import androidx.compose.foundation.layout.calculateStartPadding
|
||||||
import androidx.compose.foundation.layout.consumeWindowInsets
|
import androidx.compose.foundation.layout.consumeWindowInsets
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.TopAppBarDefaults
|
import androidx.compose.material3.TopAppBarDefaults
|
||||||
import androidx.compose.material3.rememberTopAppBarState
|
import androidx.compose.material3.rememberTopAppBarState
|
||||||
|
|
@ -25,7 +26,6 @@ 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
|
||||||
|
|
@ -40,9 +40,9 @@ import dev.chrisbanes.haze.materials.HazeMaterials
|
||||||
import dev.chrisbanes.haze.rememberHazeState
|
import dev.chrisbanes.haze.rememberHazeState
|
||||||
import io.element.android.compound.theme.ElementTheme
|
import io.element.android.compound.theme.ElementTheme
|
||||||
import io.element.android.compound.tokens.generated.CompoundIcons
|
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.RoomListContentView
|
||||||
import io.element.android.features.home.impl.components.RoomListMenuAction
|
import io.element.android.features.home.impl.components.RoomListMenuAction
|
||||||
import io.element.android.features.home.impl.components.HomeTopBar
|
|
||||||
import io.element.android.features.home.impl.model.RoomListRoomSummary
|
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.RoomListContextMenu
|
||||||
import io.element.android.features.home.impl.roomlist.RoomListDeclineInviteMenu
|
import io.element.android.features.home.impl.roomlist.RoomListDeclineInviteMenu
|
||||||
|
|
@ -63,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.SnackbarHost
|
||||||
import io.element.android.libraries.designsystem.utils.snackbar.rememberSnackbarHostState
|
import io.element.android.libraries.designsystem.utils.snackbar.rememberSnackbarHostState
|
||||||
import io.element.android.libraries.matrix.api.core.RoomId
|
import io.element.android.libraries.matrix.api.core.RoomId
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun HomeView(
|
fun HomeView(
|
||||||
|
|
@ -155,6 +156,8 @@ private fun HomeScaffold(
|
||||||
}
|
}
|
||||||
|
|
||||||
val hazeState = rememberHazeState()
|
val hazeState = rememberHazeState()
|
||||||
|
val roomsLazyListState = rememberLazyListState()
|
||||||
|
val spacesLazyListState = rememberLazyListState()
|
||||||
|
|
||||||
Scaffold(
|
Scaffold(
|
||||||
modifier = modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
|
modifier = modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
|
||||||
|
|
@ -181,41 +184,39 @@ private fun HomeScaffold(
|
||||||
style = HazeMaterials.thick(),
|
style = HazeMaterials.thick(),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
Modifier
|
Modifier.background(ElementTheme.colors.bgCanvasDefault)
|
||||||
.background(ElementTheme.colors.bgCanvasDefault)
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
bottomBar = {
|
bottomBar = {
|
||||||
if (state.showNavigationBar) {
|
if (state.showNavigationBar) {
|
||||||
NavigationBar(
|
val coroutineScope = rememberCoroutineScope()
|
||||||
containerColor = Color.Transparent,
|
HomeBottomBar(
|
||||||
modifier = Modifier
|
currentHomeNavigationBarItem = state.currentHomeNavigationBarItem,
|
||||||
.hazeEffect(
|
onItemClick = { item ->
|
||||||
state = hazeState,
|
// scroll to top if selecting the same item
|
||||||
style = HazeMaterials.thick(),
|
if (item == state.currentHomeNavigationBarItem) {
|
||||||
)
|
val lazyListStateTarget = when (item) {
|
||||||
) {
|
HomeNavigationBarItem.Chats -> roomsLazyListState
|
||||||
HomeNavigationBarItem.entries.forEach { item ->
|
HomeNavigationBarItem.Spaces -> spacesLazyListState
|
||||||
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),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
)
|
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 ->
|
content = { padding ->
|
||||||
|
|
@ -224,6 +225,7 @@ private fun HomeScaffold(
|
||||||
RoomListContentView(
|
RoomListContentView(
|
||||||
contentState = roomListState.contentState,
|
contentState = roomListState.contentState,
|
||||||
filtersState = roomListState.filtersState,
|
filtersState = roomListState.filtersState,
|
||||||
|
lazyListState = roomsLazyListState,
|
||||||
hideInvitesAvatars = roomListState.hideInvitesAvatars,
|
hideInvitesAvatars = roomListState.hideInvitesAvatars,
|
||||||
eventSink = roomListState.eventSink,
|
eventSink = roomListState.eventSink,
|
||||||
onSetUpRecoveryClick = onSetUpRecoveryClick,
|
onSetUpRecoveryClick = onSetUpRecoveryClick,
|
||||||
|
|
@ -261,6 +263,7 @@ private fun HomeScaffold(
|
||||||
.consumeWindowInsets(padding)
|
.consumeWindowInsets(padding)
|
||||||
.hazeSource(state = hazeState),
|
.hazeSource(state = hazeState),
|
||||||
state = state.homeSpacesState,
|
state = state.homeSpacesState,
|
||||||
|
lazyListState = spacesLazyListState,
|
||||||
onSpaceClick = { spaceId ->
|
onSpaceClick = { spaceId ->
|
||||||
onRoomClick(spaceId)
|
onRoomClick(spaceId)
|
||||||
}
|
}
|
||||||
|
|
@ -284,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
|
internal fun RoomListRoomSummary.contentType() = displayType.ordinal
|
||||||
|
|
||||||
@PreviewsDayNight
|
@PreviewsDayNight
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
|
import androidx.compose.foundation.lazy.LazyListState
|
||||||
import androidx.compose.foundation.lazy.itemsIndexed
|
import androidx.compose.foundation.lazy.itemsIndexed
|
||||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
|
@ -60,6 +61,7 @@ import kotlinx.collections.immutable.ImmutableList
|
||||||
fun RoomListContentView(
|
fun RoomListContentView(
|
||||||
contentState: RoomListContentState,
|
contentState: RoomListContentState,
|
||||||
filtersState: RoomListFiltersState,
|
filtersState: RoomListFiltersState,
|
||||||
|
lazyListState: LazyListState,
|
||||||
hideInvitesAvatars: Boolean,
|
hideInvitesAvatars: Boolean,
|
||||||
eventSink: (RoomListEvents) -> Unit,
|
eventSink: (RoomListEvents) -> Unit,
|
||||||
onSetUpRecoveryClick: () -> Unit,
|
onSetUpRecoveryClick: () -> Unit,
|
||||||
|
|
@ -97,6 +99,7 @@ fun RoomListContentView(
|
||||||
onSetUpRecoveryClick = onSetUpRecoveryClick,
|
onSetUpRecoveryClick = onSetUpRecoveryClick,
|
||||||
onConfirmRecoveryKeyClick = onConfirmRecoveryKeyClick,
|
onConfirmRecoveryKeyClick = onConfirmRecoveryKeyClick,
|
||||||
onRoomClick = onRoomClick,
|
onRoomClick = onRoomClick,
|
||||||
|
lazyListState = lazyListState,
|
||||||
contentPadding = contentPadding,
|
contentPadding = contentPadding,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -176,6 +179,7 @@ private fun RoomsView(
|
||||||
onConfirmRecoveryKeyClick: () -> Unit,
|
onConfirmRecoveryKeyClick: () -> Unit,
|
||||||
onRoomClick: (RoomListRoomSummary) -> Unit,
|
onRoomClick: (RoomListRoomSummary) -> Unit,
|
||||||
contentPadding: PaddingValues,
|
contentPadding: PaddingValues,
|
||||||
|
lazyListState: LazyListState,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
if (state.summaries.isEmpty() && filtersState.hasAnyFilterSelected) {
|
if (state.summaries.isEmpty() && filtersState.hasAnyFilterSelected) {
|
||||||
|
|
@ -192,6 +196,7 @@ private fun RoomsView(
|
||||||
onConfirmRecoveryKeyClick = onConfirmRecoveryKeyClick,
|
onConfirmRecoveryKeyClick = onConfirmRecoveryKeyClick,
|
||||||
onRoomClick = onRoomClick,
|
onRoomClick = onRoomClick,
|
||||||
contentPadding = contentPadding,
|
contentPadding = contentPadding,
|
||||||
|
lazyListState = lazyListState,
|
||||||
modifier = modifier.fillMaxSize(),
|
modifier = modifier.fillMaxSize(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -206,9 +211,9 @@ private fun RoomsViewList(
|
||||||
onConfirmRecoveryKeyClick: () -> Unit,
|
onConfirmRecoveryKeyClick: () -> Unit,
|
||||||
onRoomClick: (RoomListRoomSummary) -> Unit,
|
onRoomClick: (RoomListRoomSummary) -> Unit,
|
||||||
contentPadding: PaddingValues,
|
contentPadding: PaddingValues,
|
||||||
|
lazyListState: LazyListState,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
val lazyListState = rememberLazyListState()
|
|
||||||
val visibleRange by remember {
|
val visibleRange by remember {
|
||||||
derivedStateOf {
|
derivedStateOf {
|
||||||
val layoutInfo = lazyListState.layoutInfo
|
val layoutInfo = lazyListState.layoutInfo
|
||||||
|
|
@ -343,6 +348,7 @@ internal fun RoomListContentViewPreview(@PreviewParameter(RoomListContentStatePr
|
||||||
onConfirmRecoveryKeyClick = {},
|
onConfirmRecoveryKeyClick = {},
|
||||||
onRoomClick = {},
|
onRoomClick = {},
|
||||||
onCreateRoomClick = {},
|
onCreateRoomClick = {},
|
||||||
|
lazyListState = rememberLazyListState(),
|
||||||
contentPadding = PaddingValues(0.dp),
|
contentPadding = PaddingValues(0.dp),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,8 @@
|
||||||
package io.element.android.features.home.impl.spaces
|
package io.element.android.features.home.impl.spaces
|
||||||
|
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
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.runtime.Composable
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||||
|
|
@ -26,10 +28,14 @@ import kotlinx.collections.immutable.toImmutableList
|
||||||
@Composable
|
@Composable
|
||||||
fun HomeSpacesView(
|
fun HomeSpacesView(
|
||||||
state: HomeSpacesState,
|
state: HomeSpacesState,
|
||||||
|
lazyListState: LazyListState,
|
||||||
onSpaceClick: (RoomId) -> Unit,
|
onSpaceClick: (RoomId) -> Unit,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
LazyColumn(modifier) {
|
LazyColumn(
|
||||||
|
modifier = modifier,
|
||||||
|
state = lazyListState
|
||||||
|
) {
|
||||||
val space = state.space
|
val space = state.space
|
||||||
when (space) {
|
when (space) {
|
||||||
CurrentSpace.Root -> {
|
CurrentSpace.Root -> {
|
||||||
|
|
@ -77,6 +83,7 @@ internal fun HomeSpacesViewPreview(
|
||||||
) = ElementPreview {
|
) = ElementPreview {
|
||||||
HomeSpacesView(
|
HomeSpacesView(
|
||||||
state = state,
|
state = state,
|
||||||
|
lazyListState = rememberLazyListState(),
|
||||||
onSpaceClick = {},
|
onSpaceClick = {},
|
||||||
modifier = Modifier,
|
modifier = Modifier,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue