From 5d5e396a2631af63664985f39f22048061170119 Mon Sep 17 00:00:00 2001 From: Infi Date: Sat, 7 Jan 2023 02:44:58 +0100 Subject: [PATCH] feat: autoscroll and scroll to bottom --- .../revolt/components/generic/RemoteImage.kt | 5 - .../java/chat/revolt/screens/SplashScreen.kt | 10 +- .../screens/chat/views/ChannelScreen.kt | 98 ++++++++++++++----- .../revolt/screens/login/GreeterScreen.kt | 10 +- app/src/main/res/values/strings.xml | 2 + 5 files changed, 87 insertions(+), 38 deletions(-) diff --git a/app/src/main/java/chat/revolt/components/generic/RemoteImage.kt b/app/src/main/java/chat/revolt/components/generic/RemoteImage.kt index 1417f30d..f624c275 100644 --- a/app/src/main/java/chat/revolt/components/generic/RemoteImage.kt +++ b/app/src/main/java/chat/revolt/components/generic/RemoteImage.kt @@ -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 } \ No newline at end of file diff --git a/app/src/main/java/chat/revolt/screens/SplashScreen.kt b/app/src/main/java/chat/revolt/screens/SplashScreen.kt index f78e9280..a5ff3b89 100644 --- a/app/src/main/java/chat/revolt/screens/SplashScreen.kt +++ b/app/src/main/java/chat/revolt/screens/SplashScreen.kt @@ -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() diff --git a/app/src/main/java/chat/revolt/screens/chat/views/ChannelScreen.kt b/app/src/main/java/chat/revolt/screens/chat/views/ChannelScreen.kt index 858d4ed5..8ac26bb5 100644 --- a/app/src/main/java/chat/revolt/screens/chat/views/ChannelScreen.kt +++ b/app/src/main/java/chat/revolt/screens/chat/views/ChannelScreen.kt @@ -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 get() = _renderableMessages + fun setRenderableMessages(messages: List) { + _renderableMessages.clear() + _renderableMessages.addAll(messages) + } + private var _typingUsers = mutableStateListOf() val typingUsers: List get() = _typingUsers @@ -165,15 +172,17 @@ class ChannelScreenViewModel : ViewModel() { _renderableMessages.clear() viewModelScope.launch { + val messages = arrayListOf() 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() 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 ) } } diff --git a/app/src/main/java/chat/revolt/screens/login/GreeterScreen.kt b/app/src/main/java/chat/revolt/screens/login/GreeterScreen.kt index 02668265..403888a0 100644 --- a/app/src/main/java/chat/revolt/screens/login/GreeterScreen.kt +++ b/app/src/main/java/chat/revolt/screens/login/GreeterScreen.kt @@ -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() diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 69762573..65b1290a 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -104,4 +104,6 @@ No connection You are not connected to the internet. Please check your connection and try again. Tap to retry + + Scroll to bottom \ No newline at end of file