diff --git a/app/src/main/java/chat/revolt/api/unreads/Unreads.kt b/app/src/main/java/chat/revolt/api/unreads/Unreads.kt index f5317d2e..36584444 100644 --- a/app/src/main/java/chat/revolt/api/unreads/Unreads.kt +++ b/app/src/main/java/chat/revolt/api/unreads/Unreads.kt @@ -7,6 +7,7 @@ import chat.revolt.api.internals.ULID import chat.revolt.api.routes.channel.ackChannel import chat.revolt.api.routes.server.ackServer import chat.revolt.api.routes.sync.syncUnreads +import chat.revolt.api.schemas.ChannelType import chat.revolt.api.schemas.ChannelUnread class Unreads { @@ -35,6 +36,17 @@ class Unreads { 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) { if (!hasLoaded.value) return channels[channelId]?.let { diff --git a/app/src/main/java/chat/revolt/components/screens/chat/drawer/server/DrawerChannel.kt b/app/src/main/java/chat/revolt/components/screens/chat/drawer/server/DrawerChannel.kt index 05af671e..ed8bc0b0 100644 --- a/app/src/main/java/chat/revolt/components/screens/chat/drawer/server/DrawerChannel.kt +++ b/app/src/main/java/chat/revolt/components/screens/chat/drawer/server/DrawerChannel.kt @@ -44,7 +44,7 @@ fun DrawerChannel( animationSpec = spring() ) val channelAlpha = animateFloatAsState( - if (hasUnread) 1f else 0.8f, + if (hasUnread || selected) 1f else 0.8f, animationSpec = spring() ) diff --git a/app/src/main/java/chat/revolt/components/screens/chat/drawer/server/DrawerServer.kt b/app/src/main/java/chat/revolt/components/screens/chat/drawer/server/DrawerServer.kt index 1d157f3a..7f06187a 100644 --- a/app/src/main/java/chat/revolt/components/screens/chat/drawer/server/DrawerServer.kt +++ b/app/src/main/java/chat/revolt/components/screens/chat/drawer/server/DrawerServer.kt @@ -1,17 +1,22 @@ 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.clickable import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.offset import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material3.LocalContentColor import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.material3.surfaceColorAtElevation import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.alpha import androidx.compose.ui.draw.clip import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp @@ -23,34 +28,55 @@ import chat.revolt.components.generic.RemoteImage fun DrawerServer( iconId: String?, serverName: String, + hasUnreads: Boolean, onClick: () -> Unit ) { - if (iconId != null) { - RemoteImage( - url = "$REVOLT_FILES/icons/${iconId}/server.png?max_side=256", - modifier = Modifier - .padding(8.dp) - .size(48.dp) - .clip(CircleShape) - .clickable(onClick = onClick), - description = serverName - ) - } 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 + val unreadIndicatorAlpha = animateFloatAsState( + if (hasUnreads) 1f else 0f, + animationSpec = spring() + ) + + Box( + contentAlignment = Alignment.CenterStart + ) { + if (iconId != null) { + RemoteImage( + url = "$REVOLT_FILES/icons/${iconId}/server.png?max_side=256", + modifier = Modifier + .padding(8.dp) + .size(48.dp) + .clip(CircleShape) + .clickable(onClick = onClick), + description = serverName ) + } 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) + ) } } \ No newline at end of file diff --git a/app/src/main/java/chat/revolt/screens/chat/ChatRouterScreen.kt b/app/src/main/java/chat/revolt/screens/chat/ChatRouterScreen.kt index 1e8f7b2b..0efdead4 100644 --- a/app/src/main/java/chat/revolt/screens/chat/ChatRouterScreen.kt +++ b/app/src/main/java/chat/revolt/screens/chat/ChatRouterScreen.kt @@ -113,7 +113,8 @@ fun ChatRouterScreen(topNav: NavController, viewModel: ChatRouterViewModel = vie Column( modifier = Modifier .fillMaxHeight() - .verticalScroll(rememberScrollState()) + .verticalScroll(rememberScrollState()), + horizontalAlignment = Alignment.CenterHorizontally ) { UserAvatar( username = RevoltAPI.userCache[RevoltAPI.selfId]?.username @@ -142,14 +143,15 @@ fun ChatRouterScreen(topNav: NavController, viewModel: ChatRouterViewModel = vie RevoltAPI.serverCache.values .sortedBy { it.id } .forEach { server -> - if (server.name == null) return@forEach + if (server.id == null || server.name == null) return@forEach DrawerServer( iconId = server.icon?.id, - serverName = server.name + serverName = server.name, + hasUnreads = RevoltAPI.unreads.serverHasUnread(server.id), ) { viewModel.navigateToServer( - server.id!!, + server.id, navController ) }