Private
Public Access
1
0

MessageListScreen: Annotate sender in group chats

This commit is contained in:
2024-03-19 00:48:15 -07:00
parent 3d3abc1813
commit 09294e7da9
4 changed files with 69 additions and 27 deletions

View File

@@ -66,6 +66,7 @@ private val OutgoingChatBubbleShape = RoundedCornerShape(20.dp, 4.dp, 20.dp, 20.
data class MessageListViewItem( data class MessageListViewItem(
val text: String, val text: String,
val fromAddress: String,
val fromMe: Boolean, val fromMe: Boolean,
val date: Date, val date: Date,
val delivered: Boolean = true, val delivered: Boolean = true,
@@ -92,6 +93,7 @@ fun MessageListScreen(
text = it.text, text = it.text,
fromMe = it.sender == null, fromMe = it.sender == null,
date = it.date, date = it.date,
fromAddress = it.sender ?: "<me>",
delivered = !viewModel.isPendingMessage(it) delivered = !viewModel.isPendingMessage(it)
) )
} }
@@ -100,6 +102,7 @@ fun MessageListScreen(
MessageTranscript( MessageTranscript(
messages = messageItems, messages = messageItems,
paddingValues = padding, paddingValues = padding,
showSenders = viewModel.isGroupChat,
onSendMessage = { text -> onSendMessage = { text ->
viewModel.enqueueOutgoingMessage(text) viewModel.enqueueOutgoingMessage(text)
} }
@@ -111,6 +114,7 @@ fun MessageListScreen(
fun MessageTranscript( fun MessageTranscript(
messages: List<MessageListViewItem>, messages: List<MessageListViewItem>,
paddingValues: PaddingValues, paddingValues: PaddingValues,
showSenders: Boolean,
onSendMessage: (text: String) -> Unit, onSendMessage: (text: String) -> Unit,
) { ) {
val scrollState = rememberLazyListState() val scrollState = rememberLazyListState()
@@ -123,7 +127,12 @@ fun MessageTranscript(
.fillMaxSize() .fillMaxSize()
.padding(paddingValues)) { .padding(paddingValues)) {
Messages(messages = messages, modifier = Modifier.weight(1f), scrollState = scrollState) Messages(
messages = messages,
modifier = Modifier.weight(1f),
showSenders = showSenders,
scrollState = scrollState
)
MessageEntry( MessageEntry(
onTextChanged = { textState = it }, onTextChanged = { textState = it },
@@ -141,6 +150,7 @@ fun MessageTranscript(
@Composable @Composable
fun Messages( fun Messages(
messages: List<MessageListViewItem>, messages: List<MessageListViewItem>,
showSenders: Boolean,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
scrollState: LazyListState scrollState: LazyListState
) { ) {
@@ -153,19 +163,33 @@ fun Messages(
.fillMaxSize() .fillMaxSize()
.padding(horizontal = 16.dp) .padding(horizontal = 16.dp)
) { ) {
var lastDate: Date? = null var lastDate: Date = Date()
val dateFormatter = SimpleDateFormat.getDateTimeInstance() val dateFormatter = SimpleDateFormat.getDateTimeInstance()
for (index in messages.indices) { for (index in messages.indices) {
val content = messages[index] 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()) val duration: Duration? = if (previousMessage != null) Duration.between(
lastDate = content.date 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. // Remember: This is upside down.
item { item {
@@ -176,24 +200,37 @@ fun Messages(
.alpha(if (!content.delivered) 0.5F else 1.0f) .alpha(if (!content.delivered) 0.5F else 1.0f)
) )
// Greater than 30 minutes: show date: // Sender
if (duration.toMinutes() > 30) { if (!content.fromMe && showSenders && !repeatMessage) {
val formattedDate = dateFormatter.format(content.date)
Text( Text(
text = formattedDate, text = content.fromAddress,
textAlign = TextAlign.Center, style = MaterialTheme.typography.subtitle2.copy(
style = MaterialTheme.typography.caption.copy( color = MaterialTheme.colors.onSurface.copy(alpha = 0.7f)
color = MaterialTheme.colors.onSurface.copy(alpha = 0.4f)
), ),
modifier = Modifier modifier = Modifier.padding(vertical = 8.dp)
.fillMaxWidth()
.padding(18.dp),
) )
} }
// Greater than five minutes: add a bit of space. // Greater than 30 minutes: show date:
else if (duration.toMinutes() > 5) { if (duration != null) {
Spacer(modifier = Modifier.height(12.dp)) 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 = text, text = text,
style = MaterialTheme.typography.body1, style = MaterialTheme.typography.body2,
modifier = Modifier modifier = Modifier
.padding(16.dp) .padding(12.dp)
) )
} }
} }
@@ -243,12 +280,12 @@ fun MessageBubble(
@Composable @Composable
private fun MessageListScreenPreview() { private fun MessageListScreenPreview() {
val messages = listOf<MessageListViewItem>( val messages = listOf<MessageListViewItem>(
MessageListViewItem(text = "Hello", fromMe = 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()), MessageListViewItem(text = "Hey there, this is a longer text message that might wrap to another line", fromMe = true, date = Date(), fromAddress = "<me>"),
MessageListViewItem(text = "How's it going", fromMe = true, delivered = false, date = Date()) MessageListViewItem(text = "How's it going", fromMe = true, delivered = false, date = Date(), fromAddress = "<me>")
).reversed() ).reversed()
Scaffold() { Scaffold() {
MessageTranscript(messages = messages, paddingValues = it, onSendMessage = {}) MessageTranscript(messages = messages, paddingValues = it, showSenders = true, onSendMessage = {})
} }
} }

View File

@@ -68,6 +68,8 @@ class MessageListViewModel @Inject constructor(
val title: String get() = conversation!!.formattedDisplayName() val title: String get() = conversation!!.formattedDisplayName()
val isGroupChat: Boolean get() = conversation!!.isGroupChat
fun enqueueOutgoingMessage(text: String) { fun enqueueOutgoingMessage(text: String) {
val outgoingMessage = Message( val outgoingMessage = Message(
guid = UUID.randomUUID().toString(), guid = UUID.randomUUID().toString(),

View File

@@ -42,6 +42,9 @@ data class Conversation(
} }
} }
val isGroupChat: Boolean
get() = participants.count() > 1
fun formattedDisplayName(): String { fun formattedDisplayName(): String {
return displayName ?: participants.joinToString(", ") return displayName ?: participants.joinToString(", ")
} }

View File

@@ -42,7 +42,7 @@ class UpdateMonitor(private val client: APIClient) : WebSocketListener() {
fun beginMonitoringUpdates() { fun beginMonitoringUpdates() {
Log.d(UPMON_LOG, "Opening websocket connection") Log.d(UPMON_LOG, "Opening websocket connection")
this.webSocket = client.getWebSocketClient( this.webSocket = client.getWebSocketClient(
serverPath = "/updates", serverPath = "updates",
queryParams = mapOf("seq" to messageSeq.toString()), queryParams = mapOf("seq" to messageSeq.toString()),
listener = this listener = this
) )