feat: prototype server unreads

This commit is contained in:
Infi 2023-02-28 00:50:26 +01:00
parent dd78679901
commit 74a1454f73
4 changed files with 70 additions and 30 deletions

View File

@ -7,6 +7,7 @@ import chat.revolt.api.internals.ULID
import chat.revolt.api.routes.channel.ackChannel import chat.revolt.api.routes.channel.ackChannel
import chat.revolt.api.routes.server.ackServer import chat.revolt.api.routes.server.ackServer
import chat.revolt.api.routes.sync.syncUnreads import chat.revolt.api.routes.sync.syncUnreads
import chat.revolt.api.schemas.ChannelType
import chat.revolt.api.schemas.ChannelUnread import chat.revolt.api.schemas.ChannelUnread
class Unreads { class Unreads {
@ -35,6 +36,17 @@ class Unreads {
return (channels[channelId]?.last_id?.compareTo(lastMessageId) ?: 0) < 0 return (channels[channelId]?.last_id?.compareTo(lastMessageId) ?: 0) < 0
} }
fun serverHasUnread(serverId: String): Boolean {
if (!hasLoaded.value) return false
return RevoltAPI.serverCache[serverId]?.channels?.any {
val channel = RevoltAPI.channelCache[it] ?: return@any false
if (channel.channelType == ChannelType.VoiceChannel) return@any false // TODO remove this when text in voice channels is implemented
hasUnread(it, channel.lastMessageID ?: "")
}
?: false
}
suspend fun markAsRead(channelId: String, messageId: String, sync: Boolean = true) { suspend fun markAsRead(channelId: String, messageId: String, sync: Boolean = true) {
if (!hasLoaded.value) return if (!hasLoaded.value) return
channels[channelId]?.let { channels[channelId]?.let {

View File

@ -44,7 +44,7 @@ fun DrawerChannel(
animationSpec = spring() animationSpec = spring()
) )
val channelAlpha = animateFloatAsState( val channelAlpha = animateFloatAsState(
if (hasUnread) 1f else 0.8f, if (hasUnread || selected) 1f else 0.8f,
animationSpec = spring() animationSpec = spring()
) )

View File

@ -1,17 +1,22 @@
package chat.revolt.components.screens.chat.drawer.server package chat.revolt.components.screens.chat.drawer.server
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.spring
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.surfaceColorAtElevation import androidx.compose.material3.surfaceColorAtElevation
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
@ -23,34 +28,55 @@ import chat.revolt.components.generic.RemoteImage
fun DrawerServer( fun DrawerServer(
iconId: String?, iconId: String?,
serverName: String, serverName: String,
hasUnreads: Boolean,
onClick: () -> Unit onClick: () -> Unit
) { ) {
if (iconId != null) { val unreadIndicatorAlpha = animateFloatAsState(
RemoteImage( if (hasUnreads) 1f else 0f,
url = "$REVOLT_FILES/icons/${iconId}/server.png?max_side=256", animationSpec = spring()
modifier = Modifier )
.padding(8.dp)
.size(48.dp) Box(
.clip(CircleShape) contentAlignment = Alignment.CenterStart
.clickable(onClick = onClick), ) {
description = serverName if (iconId != null) {
) RemoteImage(
} else { url = "$REVOLT_FILES/icons/${iconId}/server.png?max_side=256",
Box( modifier = Modifier
contentAlignment = Alignment.Center, .padding(8.dp)
modifier = Modifier .size(48.dp)
.padding(8.dp) .clip(CircleShape)
.size(48.dp) .clickable(onClick = onClick),
.clip(CircleShape) description = serverName
.background(MaterialTheme.colorScheme.surfaceColorAtElevation(3.dp))
.clickable(onClick = onClick)
) {
Text(
text = serverName.first().uppercase(),
fontSize = 20.sp,
fontWeight = FontWeight.SemiBold,
color = MaterialTheme.colorScheme.onSurface
) )
} else {
Box(
contentAlignment = Alignment.Center,
modifier = Modifier
.padding(8.dp)
.size(48.dp)
.clip(CircleShape)
.background(MaterialTheme.colorScheme.surfaceColorAtElevation(3.dp))
.clickable(onClick = onClick)
) {
Text(
text = serverName.first().uppercase(),
fontSize = 20.sp,
fontWeight = FontWeight.SemiBold,
color = MaterialTheme.colorScheme.onSurface
)
}
} }
// Unread indicator
Box(
modifier = Modifier
.padding(8.dp)
.size(8.dp)
.offset(x = (-12).dp)
.clip(CircleShape)
.alpha(unreadIndicatorAlpha.value)
.background(LocalContentColor.current)
)
} }
} }

View File

@ -113,7 +113,8 @@ fun ChatRouterScreen(topNav: NavController, viewModel: ChatRouterViewModel = vie
Column( Column(
modifier = Modifier modifier = Modifier
.fillMaxHeight() .fillMaxHeight()
.verticalScroll(rememberScrollState()) .verticalScroll(rememberScrollState()),
horizontalAlignment = Alignment.CenterHorizontally
) { ) {
UserAvatar( UserAvatar(
username = RevoltAPI.userCache[RevoltAPI.selfId]?.username username = RevoltAPI.userCache[RevoltAPI.selfId]?.username
@ -142,14 +143,15 @@ fun ChatRouterScreen(topNav: NavController, viewModel: ChatRouterViewModel = vie
RevoltAPI.serverCache.values RevoltAPI.serverCache.values
.sortedBy { it.id } .sortedBy { it.id }
.forEach { server -> .forEach { server ->
if (server.name == null) return@forEach if (server.id == null || server.name == null) return@forEach
DrawerServer( DrawerServer(
iconId = server.icon?.id, iconId = server.icon?.id,
serverName = server.name serverName = server.name,
hasUnreads = RevoltAPI.unreads.serverHasUnread(server.id),
) { ) {
viewModel.navigateToServer( viewModel.navigateToServer(
server.id!!, server.id,
navController navController
) )
} }