feat: smooth af scrolling in channel
- typing indicator doesnt go in front of messages - scrolling has been made so much smoother - loading older messages is literally seamless - FAB for scrolling back down is now in proper FAB position
This commit is contained in:
parent
95b6b17353
commit
0e001033e4
|
|
@ -138,7 +138,7 @@ class ChannelScreenViewModel : ViewModel() {
|
||||||
addUserIfUnknown(message.author!!)
|
addUserIfUnknown(message.author!!)
|
||||||
}
|
}
|
||||||
|
|
||||||
_renderableMessages.add(message)
|
_renderableMessages.add(0, message)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onStartTyping(typing: ChannelStartTypingFrame) {
|
override fun onStartTyping(typing: ChannelStartTypingFrame) {
|
||||||
|
|
@ -178,7 +178,7 @@ class ChannelScreenViewModel : ViewModel() {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
val messages = arrayListOf<MessageSchema>()
|
val messages = arrayListOf<MessageSchema>()
|
||||||
fetchMessagesFromChannel(channel!!.id!!, limit = 50, false).let {
|
fetchMessagesFromChannel(channel!!.id!!, limit = 50, false).let {
|
||||||
it.messages!!.reversed().forEach { message ->
|
it.messages!!.forEach { message ->
|
||||||
addUserIfUnknown(message.author ?: return@forEach)
|
addUserIfUnknown(message.author ?: return@forEach)
|
||||||
if (!RevoltAPI.messageCache.containsKey(message.id)) {
|
if (!RevoltAPI.messageCache.containsKey(message.id)) {
|
||||||
RevoltAPI.messageCache[message.id!!] = message
|
RevoltAPI.messageCache[message.id!!] = message
|
||||||
|
|
@ -201,7 +201,7 @@ class ChannelScreenViewModel : ViewModel() {
|
||||||
channel!!.id!!,
|
channel!!.id!!,
|
||||||
limit = 20,
|
limit = 20,
|
||||||
true,
|
true,
|
||||||
before = renderableMessages.first().id
|
before = renderableMessages.last().id
|
||||||
).let {
|
).let {
|
||||||
it.messages!!.forEach { message ->
|
it.messages!!.forEach { message ->
|
||||||
addUserIfUnknown(message.author ?: return@forEach)
|
addUserIfUnknown(message.author ?: return@forEach)
|
||||||
|
|
@ -211,7 +211,7 @@ class ChannelScreenViewModel : ViewModel() {
|
||||||
messages.add(message)
|
messages.add(message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
setRenderableMessages(messages + renderableMessages)
|
setRenderableMessages(renderableMessages + messages)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -453,21 +453,27 @@ fun ChannelScreen(
|
||||||
|
|
||||||
val isScrolledToBottom = remember(lazyListState) {
|
val isScrolledToBottom = remember(lazyListState) {
|
||||||
derivedStateOf {
|
derivedStateOf {
|
||||||
(lazyListState.layoutInfo.visibleItemsInfo.lastOrNull()?.index
|
lazyListState.firstVisibleItemIndex <= 5
|
||||||
?: 0) >= viewModel.renderableMessages.size - 5
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LaunchedEffect(viewModel.renderableMessages.size) {
|
LaunchedEffect(viewModel.renderableMessages.size) {
|
||||||
if (isScrolledToBottom.value) {
|
if (isScrolledToBottom.value) {
|
||||||
coroutineScope.launch {
|
coroutineScope.launch {
|
||||||
lazyListState.scrollToItem(viewModel.renderableMessages.size)
|
lazyListState.animateScrollToItem(0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Box(modifier = Modifier.weight(1f)) {
|
Box(
|
||||||
LazyColumn(state = lazyListState) {
|
modifier = Modifier.weight(1f),
|
||||||
|
contentAlignment = Alignment.BottomEnd
|
||||||
|
) {
|
||||||
|
LazyColumn(state = lazyListState, reverseLayout = true) {
|
||||||
|
items(viewModel.renderableMessages) { message ->
|
||||||
|
Message(message)
|
||||||
|
}
|
||||||
|
|
||||||
item {
|
item {
|
||||||
Button(
|
Button(
|
||||||
onClick = {
|
onClick = {
|
||||||
|
|
@ -482,21 +488,20 @@ fun ChannelScreen(
|
||||||
Text("Load older")
|
Text("Load older")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
items(viewModel.renderableMessages) { message ->
|
|
||||||
Message(message)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
androidx.compose.animation.AnimatedVisibility(
|
androidx.compose.animation.AnimatedVisibility(
|
||||||
!isScrolledToBottom.value,
|
!isScrolledToBottom.value,
|
||||||
enter = slideInHorizontally(
|
enter = slideInHorizontally(
|
||||||
animationSpec = RevoltTweenInt,
|
animationSpec = RevoltTweenInt,
|
||||||
initialOffsetX = { -it },
|
initialOffsetX = { it },
|
||||||
) + fadeIn(animationSpec = RevoltTweenFloat),
|
) + fadeIn(animationSpec = RevoltTweenFloat),
|
||||||
exit = slideOutHorizontally(
|
exit = slideOutHorizontally(
|
||||||
animationSpec = RevoltTweenInt,
|
animationSpec = RevoltTweenInt,
|
||||||
targetOffsetX = { -it },
|
targetOffsetX = { it },
|
||||||
) + fadeOut(animationSpec = RevoltTweenFloat)
|
) + fadeOut(animationSpec = RevoltTweenFloat),
|
||||||
|
modifier = Modifier
|
||||||
|
.align(Alignment.BottomEnd)
|
||||||
) {
|
) {
|
||||||
ExtendedFloatingActionButton(
|
ExtendedFloatingActionButton(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
|
|
@ -513,7 +518,7 @@ fun ChannelScreen(
|
||||||
},
|
},
|
||||||
onClick = {
|
onClick = {
|
||||||
coroutineScope.launch {
|
coroutineScope.launch {
|
||||||
lazyListState.animateScrollToItem(viewModel.renderableMessages.size)
|
lazyListState.animateScrollToItem(0)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
contentColor = MaterialTheme.colorScheme.onPrimary,
|
contentColor = MaterialTheme.colorScheme.onPrimary,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue