From 09294e7da9936b48cae923c232762dc61b498bec Mon Sep 17 00:00:00 2001 From: James Magahern Date: Tue, 19 Mar 2024 00:48:15 -0700 Subject: [PATCH] MessageListScreen: Annotate sender in group chats --- .../ui/messagelist/MessageListScreen.kt | 89 +++++++++++++------ .../ui/messagelist/MessageListViewModel.kt | 2 + .../kordophone/backend/model/Conversation.kt | 3 + .../backend/server/UpdateMonitor.kt | 2 +- 4 files changed, 69 insertions(+), 27 deletions(-) diff --git a/app/src/main/java/net/buzzert/kordophonedroid/ui/messagelist/MessageListScreen.kt b/app/src/main/java/net/buzzert/kordophonedroid/ui/messagelist/MessageListScreen.kt index e180f2a..f5800b6 100644 --- a/app/src/main/java/net/buzzert/kordophonedroid/ui/messagelist/MessageListScreen.kt +++ b/app/src/main/java/net/buzzert/kordophonedroid/ui/messagelist/MessageListScreen.kt @@ -66,6 +66,7 @@ private val OutgoingChatBubbleShape = RoundedCornerShape(20.dp, 4.dp, 20.dp, 20. data class MessageListViewItem( val text: String, + val fromAddress: String, val fromMe: Boolean, val date: Date, val delivered: Boolean = true, @@ -92,6 +93,7 @@ fun MessageListScreen( text = it.text, fromMe = it.sender == null, date = it.date, + fromAddress = it.sender ?: "", delivered = !viewModel.isPendingMessage(it) ) } @@ -100,6 +102,7 @@ fun MessageListScreen( MessageTranscript( messages = messageItems, paddingValues = padding, + showSenders = viewModel.isGroupChat, onSendMessage = { text -> viewModel.enqueueOutgoingMessage(text) } @@ -111,6 +114,7 @@ fun MessageListScreen( fun MessageTranscript( messages: List, paddingValues: PaddingValues, + showSenders: Boolean, onSendMessage: (text: String) -> Unit, ) { val scrollState = rememberLazyListState() @@ -123,7 +127,12 @@ fun MessageTranscript( .fillMaxSize() .padding(paddingValues)) { - Messages(messages = messages, modifier = Modifier.weight(1f), scrollState = scrollState) + Messages( + messages = messages, + modifier = Modifier.weight(1f), + showSenders = showSenders, + scrollState = scrollState + ) MessageEntry( onTextChanged = { textState = it }, @@ -141,6 +150,7 @@ fun MessageTranscript( @Composable fun Messages( messages: List, + showSenders: Boolean, modifier: Modifier = Modifier, scrollState: LazyListState ) { @@ -153,19 +163,33 @@ fun Messages( .fillMaxSize() .padding(horizontal = 16.dp) ) { - var lastDate: Date? = null + var lastDate: Date = Date() val dateFormatter = SimpleDateFormat.getDateTimeInstance() for (index in messages.indices) { val content = messages[index] - if (lastDate == null) { - lastDate = content.date + + var previousMessage: MessageListViewItem? = null + if ((index + 1) < messages.count()) { + previousMessage = messages[index + 1] } - val duration = Duration.between(content.date.toInstant(), lastDate.toInstant()) - lastDate = content.date + val duration: Duration? = if (previousMessage != null) Duration.between( + previousMessage.date.toInstant(), + content.date.toInstant() + ) else null - Log.d("MessageListScreen", "period: $duration") + val leapMessage = ( + duration == null || ( + duration.toMinutes() > 30 + ) + ) + + val repeatMessage = !leapMessage && ( + previousMessage == null || ( + (previousMessage.fromAddress == content.fromAddress) + ) + ) // Remember: This is upside down. item { @@ -176,24 +200,37 @@ fun Messages( .alpha(if (!content.delivered) 0.5F else 1.0f) ) - // Greater than 30 minutes: show date: - if (duration.toMinutes() > 30) { - val formattedDate = dateFormatter.format(content.date) + // Sender + if (!content.fromMe && showSenders && !repeatMessage) { Text( - text = formattedDate, - textAlign = TextAlign.Center, - style = MaterialTheme.typography.caption.copy( - color = MaterialTheme.colors.onSurface.copy(alpha = 0.4f) + text = content.fromAddress, + style = MaterialTheme.typography.subtitle2.copy( + color = MaterialTheme.colors.onSurface.copy(alpha = 0.7f) ), - modifier = Modifier - .fillMaxWidth() - .padding(18.dp), + modifier = Modifier.padding(vertical = 8.dp) ) } - // Greater than five minutes: add a bit of space. - else if (duration.toMinutes() > 5) { - Spacer(modifier = Modifier.height(12.dp)) + // Greater than 30 minutes: show date: + if (duration != null) { + if (duration.toMinutes() > 30) { + val formattedDate = dateFormatter.format(content.date) + Text( + text = formattedDate, + textAlign = TextAlign.Center, + style = MaterialTheme.typography.caption.copy( + color = MaterialTheme.colors.onSurface.copy(alpha = 0.4f) + ), + modifier = Modifier + .fillMaxWidth() + .padding(18.dp), + ) + } + + // Greater than five minutes: add a bit of space. + else if (duration.toMinutes() > 5) { + Spacer(modifier = Modifier.height(12.dp)) + } } } } @@ -223,9 +260,9 @@ fun MessageBubble( ) { Text( text = text, - style = MaterialTheme.typography.body1, + style = MaterialTheme.typography.body2, modifier = Modifier - .padding(16.dp) + .padding(12.dp) ) } } @@ -243,12 +280,12 @@ fun MessageBubble( @Composable private fun MessageListScreenPreview() { val messages = listOf( - MessageListViewItem(text = "Hello", fromMe = false, date = Date()), - MessageListViewItem(text = "Hey there, this is a longer text message that might wrap to another line", fromMe = true, date = Date()), - MessageListViewItem(text = "How's it going", fromMe = true, delivered = false, date = Date()) + MessageListViewItem(text = "Hello", fromMe = false, date = Date(), fromAddress = "cool@cool.com"), + MessageListViewItem(text = "Hey there, this is a longer text message that might wrap to another line", fromMe = true, date = Date(), fromAddress = ""), + MessageListViewItem(text = "How's it going", fromMe = true, delivered = false, date = Date(), fromAddress = "") ).reversed() Scaffold() { - MessageTranscript(messages = messages, paddingValues = it, onSendMessage = {}) + MessageTranscript(messages = messages, paddingValues = it, showSenders = true, onSendMessage = {}) } } \ No newline at end of file diff --git a/app/src/main/java/net/buzzert/kordophonedroid/ui/messagelist/MessageListViewModel.kt b/app/src/main/java/net/buzzert/kordophonedroid/ui/messagelist/MessageListViewModel.kt index 08a3a17..69d3de7 100644 --- a/app/src/main/java/net/buzzert/kordophonedroid/ui/messagelist/MessageListViewModel.kt +++ b/app/src/main/java/net/buzzert/kordophonedroid/ui/messagelist/MessageListViewModel.kt @@ -68,6 +68,8 @@ class MessageListViewModel @Inject constructor( val title: String get() = conversation!!.formattedDisplayName() + val isGroupChat: Boolean get() = conversation!!.isGroupChat + fun enqueueOutgoingMessage(text: String) { val outgoingMessage = Message( guid = UUID.randomUUID().toString(), diff --git a/backend/src/main/java/net/buzzert/kordophone/backend/model/Conversation.kt b/backend/src/main/java/net/buzzert/kordophone/backend/model/Conversation.kt index 6f4dfda..2ed7bfe 100644 --- a/backend/src/main/java/net/buzzert/kordophone/backend/model/Conversation.kt +++ b/backend/src/main/java/net/buzzert/kordophone/backend/model/Conversation.kt @@ -42,6 +42,9 @@ data class Conversation( } } + val isGroupChat: Boolean + get() = participants.count() > 1 + fun formattedDisplayName(): String { return displayName ?: participants.joinToString(", ") } diff --git a/backend/src/main/java/net/buzzert/kordophone/backend/server/UpdateMonitor.kt b/backend/src/main/java/net/buzzert/kordophone/backend/server/UpdateMonitor.kt index 14bcd0c..33af3a9 100644 --- a/backend/src/main/java/net/buzzert/kordophone/backend/server/UpdateMonitor.kt +++ b/backend/src/main/java/net/buzzert/kordophone/backend/server/UpdateMonitor.kt @@ -42,7 +42,7 @@ class UpdateMonitor(private val client: APIClient) : WebSocketListener() { fun beginMonitoringUpdates() { Log.d(UPMON_LOG, "Opening websocket connection") this.webSocket = client.getWebSocketClient( - serverPath = "/updates", + serverPath = "updates", queryParams = mapOf("seq" to messageSeq.toString()), listener = this )