MessageListScreen: Annotate sender in group chats
This commit is contained in:
@@ -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 = {})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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(),
|
||||||
|
|||||||
@@ -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(", ")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user