feat: autoscroll and scroll to bottom
This commit is contained in:
parent
efdc035406
commit
5d5e396a26
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
Loading…
Reference in New Issue