feat: autoscroll and scroll to bottom

This commit is contained in:
Infi 2023-01-07 02:44:58 +01:00
parent efdc035406
commit 5d5e396a26
5 changed files with 87 additions and 38 deletions

View File

@ -9,7 +9,6 @@ import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import chat.revolt.BuildConfig
import com.bumptech.glide.integration.compose.ExperimentalGlideComposeApi
import com.bumptech.glide.integration.compose.GlideImage
@ -38,8 +37,4 @@ fun RemoteImage(
.width(pxAsDp(width))
.height(pxAsDp(height)),
)
}
fun drawableResource(id: Int): String {
return "android.resource://" + BuildConfig.APPLICATION_ID + "/" + id
}

View File

@ -4,6 +4,7 @@ import android.annotation.SuppressLint
import android.content.Context
import android.net.ConnectivityManager
import android.net.NetworkCapabilities
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
@ -12,6 +13,7 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.ViewModel
@ -19,8 +21,6 @@ import androidx.lifecycle.viewModelScope
import androidx.navigation.NavController
import chat.revolt.R
import chat.revolt.api.RevoltAPI
import chat.revolt.components.generic.RemoteImage
import chat.revolt.components.generic.drawableResource
import chat.revolt.components.screens.splash.DisconnectedScreen
import chat.revolt.persistence.KVStorage
import dagger.hilt.android.lifecycle.HiltViewModel
@ -110,9 +110,9 @@ fun SplashScreen(navController: NavController, viewModel: SplashScreenViewModel
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
RemoteImage(
url = drawableResource(R.drawable.revolt_logo_wide),
description = "Revolt Logo",
Image(
painter = painterResource(id = R.drawable.revolt_logo_wide),
contentDescription = "Revolt Logo",
contentScale = ContentScale.Fit,
modifier = Modifier
.fillMaxWidth()

View File

@ -10,7 +10,9 @@ import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.KeyboardArrowDown
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
@ -71,6 +73,11 @@ class ChannelScreenViewModel : ViewModel() {
val renderableMessages: List<MessageSchema>
get() = _renderableMessages
fun setRenderableMessages(messages: List<MessageSchema>) {
_renderableMessages.clear()
_renderableMessages.addAll(messages)
}
private var _typingUsers = mutableStateListOf<String>()
val typingUsers: List<String>
get() = _typingUsers
@ -165,15 +172,17 @@ class ChannelScreenViewModel : ViewModel() {
_renderableMessages.clear()
viewModelScope.launch {
val messages = arrayListOf<MessageSchema>()
fetchMessagesFromChannel(channel!!.id!!, limit = 50, false).let {
it.messages!!.reversed().forEach { message ->
addUserIfUnknown(message.author!!)
addUserIfUnknown(message.author ?: return@forEach)
if (!RevoltAPI.messageCache.containsKey(message.id)) {
RevoltAPI.messageCache[message.id!!] = message
}
_renderableMessages.add(message)
messages.add(message)
}
}
setRenderableMessages(renderableMessages + messages)
}
}
@ -183,6 +192,7 @@ class ChannelScreenViewModel : ViewModel() {
}
viewModelScope.launch {
val messages = arrayListOf<MessageSchema>()
fetchMessagesFromChannel(
channel!!.id!!,
limit = 20,
@ -190,13 +200,14 @@ class ChannelScreenViewModel : ViewModel() {
before = renderableMessages.first().id
).let {
it.messages!!.forEach { message ->
addUserIfUnknown(message.author!!)
addUserIfUnknown(message.author ?: return@forEach)
if (!RevoltAPI.messageCache.containsKey(message.id)) {
RevoltAPI.messageCache[message.id!!] = message
}
_renderableMessages.add(0, message)
messages.add(message)
}
}
setRenderableMessages(messages + renderableMessages)
}
}
@ -348,7 +359,7 @@ fun ChannelScreen(
val channel = viewModel.channel
val context = LocalContext.current
val scrollState = rememberScrollState()
val lazyListState = rememberLazyListState()
val coroutineScope = rememberCoroutineScope()
val channelInfoOpen = remember { mutableStateOf(false) }
@ -375,7 +386,6 @@ fun ChannelScreen(
)
}
}
}
}
@ -436,23 +446,65 @@ fun ChannelScreen(
)
}
LazyColumn(Modifier.weight(1f)) {
item {
Button(
onClick = {
coroutineScope.launch {
viewModel.fetchOlderMessages()
}
},
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 16.dp, horizontal = 8.dp)
) {
Text("Load older")
val isScrolledToBottom = remember(lazyListState) {
derivedStateOf {
(lazyListState.layoutInfo.visibleItemsInfo.lastOrNull()?.index
?: 0) >= viewModel.renderableMessages.size - 5
}
}
LaunchedEffect(viewModel.renderableMessages.size) {
if (isScrolledToBottom.value) {
coroutineScope.launch {
lazyListState.scrollToItem(viewModel.renderableMessages.size)
}
}
items(viewModel.renderableMessages) { message ->
Message(message)
}
Box(modifier = Modifier.weight(1f)) {
LazyColumn(state = lazyListState) {
item {
Button(
onClick = {
coroutineScope.launch {
viewModel.fetchOlderMessages()
}
},
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 16.dp, horizontal = 8.dp)
) {
Text("Load older")
}
}
items(viewModel.renderableMessages) { message ->
Message(message)
}
}
androidx.compose.animation.AnimatedVisibility(!isScrolledToBottom.value) {
ExtendedFloatingActionButton(
modifier = Modifier
.align(Alignment.BottomEnd)
.padding(16.dp),
text = {
Text(stringResource(R.string.scroll_to_bottom))
},
icon = {
Icon(
imageVector = Icons.Default.KeyboardArrowDown,
contentDescription = stringResource(R.string.scroll_to_bottom)
)
},
onClick = {
coroutineScope.launch {
lazyListState.animateScrollToItem(viewModel.renderableMessages.size)
}
},
contentColor = MaterialTheme.colorScheme.onSecondary,
containerColor = MaterialTheme.colorScheme.secondary
)
}
}
@ -504,7 +556,7 @@ fun ChannelScreen(
channelType = channel.channelType!!,
channelName = channel.name ?: channel.id!!,
forceSendButton = viewModel.attachments.isNotEmpty(),
disabled = viewModel.sendingMessage
disabled = viewModel.attachments.isNotEmpty() && viewModel.sendingMessage
)
}
}

View File

@ -1,5 +1,6 @@
package chat.revolt.screens.login
import androidx.compose.foundation.Image
import chat.revolt.R
import androidx.compose.foundation.layout.*
import androidx.compose.material3.Button
@ -11,14 +12,13 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.navigation.NavController
import chat.revolt.components.generic.RemoteImage
import chat.revolt.components.generic.drawableResource
@Composable
fun GreeterScreen(navController: NavController) {
@ -37,9 +37,9 @@ fun GreeterScreen(navController: NavController) {
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
RemoteImage(
url = drawableResource(R.drawable.revolt_logo_wide),
description = "Revolt Logo",
Image(
painter = painterResource(id = R.drawable.revolt_logo_wide),
contentDescription = "Revolt Logo",
contentScale = ContentScale.Fit,
modifier = Modifier
.fillMaxWidth()

View File

@ -104,4 +104,6 @@
<string name="no_connection">No connection</string>
<string name="no_connection_message">You are not connected to the internet. Please check your connection and try again.</string>
<string name="tap_to_retry">Tap to retry</string>
<string name="scroll_to_bottom">Scroll to bottom</string>
</resources>