Implement scroll to top in friends and conversations screens
This commit is contained in:
@@ -36,7 +36,9 @@ import dev.chrisbanes.haze.HazeState
|
||||
import dev.chrisbanes.haze.hazeEffect
|
||||
import dev.chrisbanes.haze.materials.ExperimentalHazeMaterialsApi
|
||||
import dev.chrisbanes.haze.materials.HazeMaterials
|
||||
import dev.meloda.fast.conversations.navigation.Conversations
|
||||
import dev.meloda.fast.conversations.navigation.conversationsScreen
|
||||
import dev.meloda.fast.friends.navigation.Friends
|
||||
import dev.meloda.fast.friends.navigation.friendsScreen
|
||||
import dev.meloda.fast.model.BaseError
|
||||
import dev.meloda.fast.model.BottomNavigationItem
|
||||
@@ -44,6 +46,7 @@ import dev.meloda.fast.navigation.MainGraph
|
||||
import dev.meloda.fast.profile.navigation.profileScreen
|
||||
import dev.meloda.fast.ui.theme.LocalBottomPadding
|
||||
import dev.meloda.fast.ui.theme.LocalHazeState
|
||||
import dev.meloda.fast.ui.theme.LocalScrollToTop
|
||||
import dev.meloda.fast.ui.theme.LocalThemeConfig
|
||||
import dev.meloda.fast.ui.theme.LocalUser
|
||||
import dev.meloda.fast.ui.util.ImmutableList
|
||||
@@ -72,6 +75,14 @@ fun MainScreen(
|
||||
derivedStateOf { user?.photo100 }
|
||||
}
|
||||
|
||||
var scrollToTop by remember {
|
||||
mutableStateOf(
|
||||
navigationItems.associate {
|
||||
it.route to false
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
Scaffold(
|
||||
bottomBar = {
|
||||
NavigationBar(
|
||||
@@ -101,6 +112,10 @@ fun MainScreen(
|
||||
inclusive = true
|
||||
}
|
||||
}
|
||||
} else {
|
||||
scrollToTop = scrollToTop.toMutableMap().also {
|
||||
it[navigationItems[index].route] = true
|
||||
}
|
||||
}
|
||||
},
|
||||
icon = {
|
||||
@@ -148,7 +163,8 @@ fun MainScreen(
|
||||
) {
|
||||
CompositionLocalProvider(
|
||||
LocalHazeState provides hazeState,
|
||||
LocalBottomPadding provides padding.calculateBottomPadding()
|
||||
LocalBottomPadding provides padding.calculateBottomPadding(),
|
||||
LocalScrollToTop provides scrollToTop
|
||||
) {
|
||||
NavHost(
|
||||
navController = navController,
|
||||
@@ -164,13 +180,23 @@ fun MainScreen(
|
||||
friendsScreen(
|
||||
onError = onError,
|
||||
onPhotoClicked = onPhotoClicked,
|
||||
onMessageClicked = onMessageClicked
|
||||
onMessageClicked = onMessageClicked,
|
||||
onScrolledToTop = {
|
||||
scrollToTop = scrollToTop.toMutableMap().also {
|
||||
it[Friends] = false
|
||||
}
|
||||
},
|
||||
)
|
||||
conversationsScreen(
|
||||
onError = onError,
|
||||
onConversationItemClicked = onConversationItemClicked,
|
||||
onCreateChatClicked = onCreateChatClicked,
|
||||
navController = navController,
|
||||
onScrolledToTop = {
|
||||
scrollToTop = scrollToTop.toMutableMap().also {
|
||||
it[Conversations] = false
|
||||
}
|
||||
}
|
||||
)
|
||||
profileScreen(
|
||||
onError = onError,
|
||||
|
||||
@@ -129,6 +129,7 @@ val LocalSizeConfig = compositionLocalOf {
|
||||
val LocalHazeState = compositionLocalOf { HazeState() }
|
||||
val LocalBottomPadding = compositionLocalOf { 0.dp }
|
||||
val LocalUser = compositionLocalOf<VkUser?> { null }
|
||||
val LocalScrollToTop = compositionLocalOf { mapOf<Any, Boolean>() }
|
||||
|
||||
@Composable
|
||||
fun AppTheme(
|
||||
|
||||
+2
@@ -17,6 +17,7 @@ fun NavGraphBuilder.conversationsScreen(
|
||||
onError: (BaseError) -> Unit,
|
||||
onConversationItemClicked: (id: Int) -> Unit,
|
||||
onCreateChatClicked: () -> Unit,
|
||||
onScrolledToTop: () -> Unit,
|
||||
navController: NavController,
|
||||
) {
|
||||
composable<Conversations> {
|
||||
@@ -27,6 +28,7 @@ fun NavGraphBuilder.conversationsScreen(
|
||||
onError = onError,
|
||||
onConversationItemClicked = onConversationItemClicked,
|
||||
onCreateChatButtonClicked = onCreateChatClicked,
|
||||
onScrolledToTop = onScrolledToTop,
|
||||
viewModel = viewModel
|
||||
)
|
||||
}
|
||||
|
||||
+18
-2
@@ -63,6 +63,7 @@ import dev.chrisbanes.haze.materials.ExperimentalHazeMaterialsApi
|
||||
import dev.chrisbanes.haze.materials.HazeMaterials
|
||||
import dev.meloda.fast.conversations.ConversationsViewModel
|
||||
import dev.meloda.fast.conversations.model.ConversationsScreenState
|
||||
import dev.meloda.fast.conversations.navigation.Conversations
|
||||
import dev.meloda.fast.model.BaseError
|
||||
import dev.meloda.fast.ui.components.ErrorView
|
||||
import dev.meloda.fast.ui.components.FullScreenLoader
|
||||
@@ -72,6 +73,7 @@ import dev.meloda.fast.ui.model.api.ConversationOption
|
||||
import dev.meloda.fast.ui.model.api.UiConversation
|
||||
import dev.meloda.fast.ui.theme.LocalBottomPadding
|
||||
import dev.meloda.fast.ui.theme.LocalHazeState
|
||||
import dev.meloda.fast.ui.theme.LocalScrollToTop
|
||||
import dev.meloda.fast.ui.theme.LocalThemeConfig
|
||||
import dev.meloda.fast.ui.util.isScrollingUp
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
@@ -83,6 +85,7 @@ fun ConversationsRoute(
|
||||
onError: (BaseError) -> Unit,
|
||||
onConversationItemClicked: (conversationId: Int) -> Unit,
|
||||
onCreateChatButtonClicked: () -> Unit,
|
||||
onScrolledToTop: () -> Unit,
|
||||
viewModel: ConversationsViewModel
|
||||
) {
|
||||
val screenState by viewModel.screenState.collectAsStateWithLifecycle()
|
||||
@@ -105,7 +108,8 @@ fun ConversationsRoute(
|
||||
onRefresh = viewModel::onRefresh,
|
||||
onCreateChatButtonClicked = onCreateChatButtonClicked,
|
||||
setScrollIndex = viewModel::setScrollIndex,
|
||||
setScrollOffset = viewModel::setScrollOffset
|
||||
setScrollOffset = viewModel::setScrollOffset,
|
||||
onScrolledToTop = onScrolledToTop
|
||||
)
|
||||
|
||||
HandleDialogs(
|
||||
@@ -132,7 +136,8 @@ fun ConversationsScreen(
|
||||
onRefresh: () -> Unit = {},
|
||||
onCreateChatButtonClicked: () -> Unit = {},
|
||||
setScrollIndex: (Int) -> Unit = {},
|
||||
setScrollOffset: (Int) -> Unit = {}
|
||||
setScrollOffset: (Int) -> Unit = {},
|
||||
onScrolledToTop: () -> Unit = {}
|
||||
) {
|
||||
val currentTheme = LocalThemeConfig.current
|
||||
|
||||
@@ -145,6 +150,17 @@ fun ConversationsScreen(
|
||||
initialFirstVisibleItemScrollOffset = screenState.scrollOffset
|
||||
)
|
||||
|
||||
val scrollToTop = LocalScrollToTop.current[Conversations] ?: false
|
||||
LaunchedEffect(scrollToTop) {
|
||||
if (scrollToTop) {
|
||||
if (listState.firstVisibleItemIndex > 14) {
|
||||
listState.scrollToItem(14)
|
||||
}
|
||||
listState.animateScrollToItem(0)
|
||||
onScrolledToTop()
|
||||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(listState) {
|
||||
snapshotFlow { listState.firstVisibleItemIndex }
|
||||
.debounce(500L)
|
||||
|
||||
+4
-2
@@ -12,13 +12,15 @@ object Friends
|
||||
fun NavGraphBuilder.friendsScreen(
|
||||
onError: (BaseError) -> Unit,
|
||||
onPhotoClicked: (url: String) -> Unit,
|
||||
onMessageClicked: (userId: Int) -> Unit
|
||||
onMessageClicked: (userId: Int) -> Unit,
|
||||
onScrolledToTop: () -> Unit
|
||||
) {
|
||||
composable<Friends> {
|
||||
FriendsRoute(
|
||||
onError = onError,
|
||||
onPhotoClicked = onPhotoClicked,
|
||||
onMessageClicked = onMessageClicked
|
||||
onMessageClicked = onMessageClicked,
|
||||
onScrolledToTop = onScrolledToTop
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
+15
-1
@@ -29,12 +29,14 @@ import dev.chrisbanes.haze.hazeSource
|
||||
import dev.meloda.fast.friends.FriendsViewModel
|
||||
import dev.meloda.fast.friends.FriendsViewModelImpl
|
||||
import dev.meloda.fast.friends.OnlineFriendsViewModelImpl
|
||||
import dev.meloda.fast.friends.navigation.Friends
|
||||
import dev.meloda.fast.model.BaseError
|
||||
import dev.meloda.fast.ui.R
|
||||
import dev.meloda.fast.ui.components.ErrorView
|
||||
import dev.meloda.fast.ui.components.FullScreenLoader
|
||||
import dev.meloda.fast.ui.components.NoItemsView
|
||||
import dev.meloda.fast.ui.theme.LocalHazeState
|
||||
import dev.meloda.fast.ui.theme.LocalScrollToTop
|
||||
import dev.meloda.fast.ui.theme.LocalThemeConfig
|
||||
import dev.meloda.fast.ui.util.ImmutableList
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
@@ -51,7 +53,8 @@ fun FriendsScreen(
|
||||
onSessionExpiredLogOutButtonClicked: () -> Unit = {},
|
||||
onPhotoClicked: (url: String) -> Unit = {},
|
||||
onMessageClicked: (userId: Int) -> Unit = {},
|
||||
setCanScrollBackward: (Boolean) -> Unit = {}
|
||||
setCanScrollBackward: (Boolean) -> Unit = {},
|
||||
onScrolledToTop: () -> Unit = {}
|
||||
) {
|
||||
val context: Context = LocalContext.current
|
||||
val viewModel: FriendsViewModel =
|
||||
@@ -93,6 +96,17 @@ fun FriendsScreen(
|
||||
initialFirstVisibleItemScrollOffset = screenState.scrollOffset
|
||||
)
|
||||
|
||||
val scrollToTop = LocalScrollToTop.current[Friends] ?: false
|
||||
LaunchedEffect(scrollToTop) {
|
||||
if (scrollToTop) {
|
||||
if (listState.firstVisibleItemIndex > 14) {
|
||||
listState.scrollToItem(14)
|
||||
}
|
||||
listState.animateScrollToItem(0)
|
||||
onScrolledToTop()
|
||||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(listState) {
|
||||
snapshotFlow { listState.firstVisibleItemIndex }
|
||||
.debounce(250L)
|
||||
|
||||
+3
-1
@@ -58,6 +58,7 @@ fun FriendsRoute(
|
||||
onError: (BaseError) -> Unit,
|
||||
onPhotoClicked: (url: String) -> Unit,
|
||||
onMessageClicked: (userId: Int) -> Unit,
|
||||
onScrolledToTop: () -> Unit
|
||||
) {
|
||||
val scope = rememberCoroutineScope()
|
||||
val currentTheme = LocalThemeConfig.current
|
||||
@@ -236,7 +237,8 @@ fun FriendsRoute(
|
||||
onSessionExpiredLogOutButtonClicked = { onError(BaseError.SessionExpired) },
|
||||
onPhotoClicked = onPhotoClicked,
|
||||
onMessageClicked = onMessageClicked,
|
||||
setCanScrollBackward = { canScrollBackward = it }
|
||||
setCanScrollBackward = { canScrollBackward = it },
|
||||
onScrolledToTop = onScrolledToTop
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user