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 <noreply@anthropic.com>
This commit is contained in:
Kgothatso Ngako
2026-09-06 18:21:48 +02:00
parent 81965ba2d3
commit 473228bfab
2 changed files with 40 additions and 6 deletions

View File

@@ -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
)
}
}

View File

@@ -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
)
}
}
}
}