feat: Implement pagination improvements in ChannelScreen
This commit enhances the pagination logic in the ChannelScreen to improve message loading behavior. - Introduced a new `isNearBottom` state to detect when the user is near the bottom of the message list, triggering loading of newer messages. - Refactored the existing pagination logic to handle both loading older messages when near the top and newer messages when near the bottom. - Added logic to preserve the scroll position when new messages are loaded, ensuring a smoother user experience. - Improved logging for better debugging and tracking of pagination events.
This commit is contained in:
parent
072fcbf5eb
commit
e121fc5774
|
|
@ -424,6 +424,13 @@ fun ChannelScreen(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Near bottom detection (reverseLayout = true): firstVisibleItemIndex small means near bottom/latest
|
||||||
|
val isNearBottom = remember(lazyListState) {
|
||||||
|
derivedStateOf {
|
||||||
|
lazyListState.firstVisibleItemIndex < 3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val scrollDownFABPadding by animateDpAsState(
|
val scrollDownFABPadding by animateDpAsState(
|
||||||
if (viewModel.typingUsers.isNotEmpty()) 25.dp else 0.dp,
|
if (viewModel.typingUsers.isNotEmpty()) 25.dp else 0.dp,
|
||||||
animationSpec = RevoltTweenDp,
|
animationSpec = RevoltTweenDp,
|
||||||
|
|
@ -467,25 +474,71 @@ fun ChannelScreen(
|
||||||
Log.w("ChannelScreen", "Target message not found after loading around window")
|
Log.w("ChannelScreen", "Target message not found after loading around window")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} ?: run {
|
}
|
||||||
LaunchedEffect(isNearTop) {
|
|
||||||
snapshotFlow { isNearTop.value }
|
// Always-on pagination collectors (work for both default and around modes)
|
||||||
.distinctUntilChanged()
|
LaunchedEffect(isNearTop) {
|
||||||
.collect { isNearTop ->
|
snapshotFlow { isNearTop.value }
|
||||||
if (isNearTop) {
|
.distinctUntilChanged()
|
||||||
Log.d("ChannelScreen", "Loading more messages")
|
.collect { nearTop ->
|
||||||
viewModel.loadMessages(before = viewModel.items.lastOrNull {
|
if (nearTop && !viewModel.endOfChannel) {
|
||||||
it is ChannelScreenItem.RegularMessage || it is ChannelScreenItem.SystemMessage
|
val oldestId = viewModel.items.lastOrNull {
|
||||||
}?.let {
|
it is ChannelScreenItem.RegularMessage || it is ChannelScreenItem.SystemMessage
|
||||||
when (it) {
|
}?.let {
|
||||||
is ChannelScreenItem.RegularMessage -> it.message.id
|
when (it) {
|
||||||
is ChannelScreenItem.SystemMessage -> it.message.id
|
is ChannelScreenItem.RegularMessage -> it.message.id
|
||||||
else -> null
|
is ChannelScreenItem.SystemMessage -> it.message.id
|
||||||
}
|
else -> null
|
||||||
}, amount = 50)
|
}
|
||||||
|
}
|
||||||
|
if (oldestId != null) {
|
||||||
|
Log.d("ChannelScreen", "Pagination: loading older before=$oldestId")
|
||||||
|
viewModel.loadMessages(amount = 50, before = oldestId, ignoreExisting = true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LaunchedEffect(isNearBottom) {
|
||||||
|
snapshotFlow { isNearBottom.value }
|
||||||
|
.distinctUntilChanged()
|
||||||
|
.collect { nearBottom ->
|
||||||
|
if (nearBottom) {
|
||||||
|
val newestId = viewModel.items.firstOrNull {
|
||||||
|
it is ChannelScreenItem.RegularMessage || it is ChannelScreenItem.SystemMessage
|
||||||
|
}?.let {
|
||||||
|
when (it) {
|
||||||
|
is ChannelScreenItem.RegularMessage -> it.message.id
|
||||||
|
is ChannelScreenItem.SystemMessage -> it.message.id
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (newestId != null) {
|
||||||
|
// Preserve anchor: remember current first visible item and offset
|
||||||
|
val beforeSize = viewModel.items.size
|
||||||
|
val anchorIndex = lazyListState.firstVisibleItemIndex
|
||||||
|
val anchorOffset = lazyListState.firstVisibleItemScrollOffset
|
||||||
|
Log.d("ChannelScreen", "Pagination: loading newer after=$newestId (anchor index=$anchorIndex, offset=$anchorOffset, size=$beforeSize)")
|
||||||
|
|
||||||
|
viewModel.loadMessages(amount = 50, after = newestId, ignoreExisting = true)
|
||||||
|
|
||||||
|
// Wait until items are appended to the front (after-insert path adds to front)
|
||||||
|
var attempts = 0
|
||||||
|
while (attempts < 30) { // ~1.5s max
|
||||||
|
kotlinx.coroutines.delay(50)
|
||||||
|
val added = viewModel.items.size - beforeSize
|
||||||
|
if (added > 0) {
|
||||||
|
// Shift anchor forward by number of inserted items to keep viewport stable
|
||||||
|
try {
|
||||||
|
lazyListState.scrollToItem(anchorIndex + added, anchorOffset)
|
||||||
|
} catch (_: Exception) {}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
attempts++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// </editor-fold>
|
// </editor-fold>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue