From 473228bfabeea7e54ff4d9f32bc19573938c256a Mon Sep 17 00:00:00 2001 From: Kgothatso Ngako Date: Sun, 6 Sep 2026 18:21:48 +0200 Subject: [PATCH] feat: show the last message and its time on the home chat list The row was a centred column with the room's name in it and nothing else. It is now a row: the name and a one-line preview of what was last said in a weighted column on the left, the clock on the right. Both text lines are clipped to one with an ellipsis, the name included, and that is what keeps the clock on the row. A long subject, or a five-member group whose title is five names joined with commas, would otherwise push the timestamp off the edge; a multi-line message would push the next room down the screen. The clipping is on the title's own composable rather than the row, because that is where the three ways of building a title live -- a subject, "Note to Self", or the participant list -- and only one of them being clipped is the shape this would rot into. The clock is absent rather than blank for a room with nothing in it. There is no message time to show, and the room's own creation -- which is what it sorts on in that case -- is not something the user has any reason to read here. The preview line carries "No messages yet" in its place, which is the honest state for a room that exists because it was just made, or because a member joined a working group and is still waiting on the chronicle to fill it in. Everything the row renders was decided in the model, so this commit is the card body and the title clipping and nothing else. Verified: `:composeApp:compileDebugKotlinAndroid` builds, and the full `:composeApp:jvmTest` suite passes at 526 tests, 0 failures -- the 503 that were there before this branch plus its 23. The layout itself has not been run on a device; it is checked by compilation and by the model tests behind it. Co-Authored-By: Claude Opus 5 --- .../model/intermdiate/LocalChatRoom.kt | 10 ++++-- .../ui/view/model/ChatRoomListViewModel.kt | 36 ++++++++++++++++--- 2 files changed, 40 insertions(+), 6 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/press/mantra/compose/database/model/intermdiate/LocalChatRoom.kt b/composeApp/src/commonMain/kotlin/press/mantra/compose/database/model/intermdiate/LocalChatRoom.kt index 021f0f0a..2529c8ee 100644 --- a/composeApp/src/commonMain/kotlin/press/mantra/compose/database/model/intermdiate/LocalChatRoom.kt +++ b/composeApp/src/commonMain/kotlin/press/mantra/compose/database/model/intermdiate/LocalChatRoom.kt @@ -42,12 +42,16 @@ data class LocalChatRoom( if (chatRoom.subject != null) { Text( text = chatRoom.subject, - color = ProfileColor.fromPublicKey(chatRoom.id) + color = ProfileColor.fromPublicKey(chatRoom.id), + maxLines = 1, + overflow = TextOverflow.Ellipsis ) } else { if (localParticipants.size == 1) { Text( - text = "Note to Self (${localParticipants.first().profile?.humanReadableNameOrPubkey()})" + text = "Note to Self (${localParticipants.first().profile?.humanReadableNameOrPubkey()})", + maxLines = 1, + overflow = TextOverflow.Ellipsis ) } else { // Remove the active user publicKey from participants... @@ -66,6 +70,8 @@ data class LocalChatRoom( append(participantName) } }, + maxLines = 1, + overflow = TextOverflow.Ellipsis ) } } diff --git a/composeApp/src/commonMain/kotlin/press/mantra/compose/ui/view/model/ChatRoomListViewModel.kt b/composeApp/src/commonMain/kotlin/press/mantra/compose/ui/view/model/ChatRoomListViewModel.kt index 6ef606ae..bf36213c 100755 --- a/composeApp/src/commonMain/kotlin/press/mantra/compose/ui/view/model/ChatRoomListViewModel.kt +++ b/composeApp/src/commonMain/kotlin/press/mantra/compose/ui/view/model/ChatRoomListViewModel.kt @@ -2,6 +2,7 @@ package press.mantra.compose.ui.view.model import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height @@ -11,6 +12,7 @@ import androidx.compose.foundation.lazy.items import androidx.compose.material3.Card import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi import androidx.compose.material3.LoadingIndicator +import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue @@ -27,6 +29,7 @@ import androidx.lifecycle.viewmodel.initializer import androidx.lifecycle.viewmodel.viewModelFactory import press.mantra.compose.database.model.NegentropySynchronizeRequest import press.mantra.compose.database.model.types.SynchronizationFilter +import press.mantra.compose.extensions.toChatListTimestampString import press.mantra.compose.nostr.Nip17Filters import press.mantra.compose.nostr.Relays import press.mantra.compose.repository.ChatRepository @@ -186,11 +189,36 @@ class ChatRoomListViewModel( ) } ) { - Column( - modifier = Modifier.padding(20.dp), - horizontalAlignment = Alignment.CenterHorizontally, + Row( + modifier = Modifier.fillMaxWidth().padding(20.dp), + horizontalArrangement = Arrangement.spacedBy(10.dp), + verticalAlignment = Alignment.CenterVertically ) { - localChatRoom.RenderChatRoomTitleText() + // Both lines are clipped to one, so the name + // and the preview keep their width whatever + // was said -- a long message must not push + // the clock off the row. + Column( + modifier = Modifier.weight(1f), + verticalArrangement = Arrangement.spacedBy(4.dp) + ) { + localChatRoom.RenderChatRoomTitleText() + localChatRoom.RenderChatRoomLastMessageText() + } + + // Absent rather than blank for a room with + // nothing in it: there is no time to show, + // and the room's own creation -- which is + // what it sorts on -- is not one the user + // has any reason to read here. + localChatRoom.lastChatMessage?.let { lastChatMessage -> + Text( + text = lastChatMessage.createdAt.toChatListTimestampString(), + style = MaterialTheme.typography.labelSmall, + color = MaterialTheme.colorScheme.onSurfaceVariant, + maxLines = 1 + ) + } } } }