Skip to content

Commit 4a62c11

Browse files
committed
feat: create shared MessageList component; align Exchange messages based on sender
Signed-off-by: Brandon McAnsh <[email protected]>
1 parent 7967f85 commit 4a62c11

File tree

11 files changed

+165
-195
lines changed

11 files changed

+165
-195
lines changed

api/src/main/java/com/getcode/model/Chat.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,7 @@ sealed interface MessageContent {
227227
Content.TypeCase.LOCALIZED -> Localized(proto.localized.keyOrText)
228228
Content.TypeCase.EXCHANGE_DATA -> {
229229
val verb = Verb(proto.exchangeData.verb)
230+
val messageStatus = if (verb.increasesBalance) MessageStatus.Incoming else MessageStatus.Delivered
230231
when (proto.exchangeData.exchangeDataCase) {
231232
ChatService.ExchangeDataContent.ExchangeDataCase.EXACT -> {
232233
val exact = proto.exchangeData.exact
@@ -239,7 +240,7 @@ sealed interface MessageContent {
239240
)
240241
)
241242

242-
Exchange(GenericAmount.Exact(kinAmount), verb)
243+
Exchange(GenericAmount.Exact(kinAmount), verb, status = messageStatus)
243244
}
244245

245246
ChatService.ExchangeDataContent.ExchangeDataCase.PARTIAL -> {
@@ -251,7 +252,7 @@ sealed interface MessageContent {
251252
amount = partial.nativeAmount
252253
)
253254

254-
Exchange(GenericAmount.Partial(fiat), verb)
255+
Exchange(GenericAmount.Partial(fiat), verb, status = messageStatus)
255256
}
256257

257258
ChatService.ExchangeDataContent.ExchangeDataCase.EXCHANGEDATA_NOT_SET -> return null

app/src/main/java/com/getcode/ui/components/conversation/ChatInput.kt renamed to app/src/main/java/com/getcode/ui/components/chat/ChatInput.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package com.getcode.ui.components.conversation
1+
package com.getcode.ui.components.chat
22

33
import androidx.compose.animation.AnimatedContent
44
import androidx.compose.animation.AnimatedContentTransitionScope
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package com.getcode.ui.components.chat
2+
3+
import androidx.compose.foundation.layout.Box
4+
import androidx.compose.foundation.layout.fillMaxWidth
5+
import androidx.compose.runtime.Composable
6+
import androidx.compose.ui.Alignment
7+
import androidx.compose.ui.Modifier
8+
import com.getcode.theme.BrandDark
9+
import com.getcode.ui.components.Pill
10+
11+
@Composable
12+
internal fun DateBubble(
13+
modifier: Modifier = Modifier,
14+
date: String,
15+
) = Box(modifier = modifier.fillMaxWidth(), contentAlignment = Alignment.Center) {
16+
Pill(
17+
text = date,
18+
backgroundColor = BrandDark
19+
)
20+
}

app/src/main/java/com/getcode/ui/components/chat/DateWithStatus.kt

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import androidx.compose.ui.unit.Dp
2020
import com.getcode.R
2121
import com.getcode.model.MessageStatus
2222
import com.getcode.theme.BrandLight
23+
import com.getcode.theme.ChatOutgoing
2324
import com.getcode.theme.CodeTheme
2425
import com.getcode.util.formatTimeRelatively
2526
import kotlinx.datetime.Clock
@@ -83,7 +84,7 @@ private fun Preview_DateWithStatus() {
8384
modifier = Modifier
8485
.wrapContentWidth()
8586
.background(
86-
color = Color(0xFF443091),
87+
color = ChatOutgoing,
8788
shape = MessageNodeDefaults.DefaultShape
8889
)
8990
.padding(CodeTheme.dimens.grid.x2)
@@ -94,7 +95,7 @@ private fun Preview_DateWithStatus() {
9495
modifier = Modifier
9596
.wrapContentWidth()
9697
.background(
97-
color = Color(0xFF443091),
98+
color = ChatOutgoing,
9899
shape = MessageNodeDefaults.DefaultShape
99100
)
100101
.padding(CodeTheme.dimens.grid.x2)
@@ -105,7 +106,7 @@ private fun Preview_DateWithStatus() {
105106
modifier = Modifier
106107
.wrapContentWidth()
107108
.background(
108-
color = Color(0xFF443091),
109+
color = ChatOutgoing,
109110
shape = MessageNodeDefaults.DefaultShape
110111
)
111112
.padding(CodeTheme.dimens.grid.x2)
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
package com.getcode.ui.components.chat
2+
3+
import androidx.compose.foundation.layout.Arrangement
4+
import androidx.compose.foundation.layout.PaddingValues
5+
import androidx.compose.foundation.layout.fillMaxWidth
6+
import androidx.compose.foundation.layout.padding
7+
import androidx.compose.foundation.lazy.LazyColumn
8+
import androidx.compose.foundation.lazy.LazyListState
9+
import androidx.compose.foundation.lazy.rememberLazyListState
10+
import androidx.compose.runtime.Composable
11+
import androidx.compose.runtime.remember
12+
import androidx.compose.ui.Alignment
13+
import androidx.compose.ui.Modifier
14+
import androidx.paging.compose.LazyPagingItems
15+
import androidx.paging.compose.itemContentType
16+
import androidx.paging.compose.itemKey
17+
import com.getcode.model.ID
18+
import com.getcode.theme.CodeTheme
19+
import com.getcode.ui.components.chat.utils.ChatItem
20+
import com.getcode.util.formatDateRelatively
21+
22+
sealed interface MessageListEvent {
23+
data class ThankUser(val messageId: ID): MessageListEvent
24+
data class OpenMessageChat(val messageId: ID): MessageListEvent
25+
}
26+
@Composable
27+
fun MessageList(
28+
modifier: Modifier = Modifier,
29+
listState: LazyListState = rememberLazyListState(),
30+
verticalArrangement: Arrangement.Vertical = Arrangement.Top,
31+
messages: LazyPagingItems<ChatItem>,
32+
dispatch: (MessageListEvent) -> Unit = { },
33+
) {
34+
LazyColumn(
35+
modifier = modifier,
36+
state = listState,
37+
reverseLayout = true,
38+
39+
contentPadding = PaddingValues(
40+
horizontal = CodeTheme.dimens.inset,
41+
vertical = CodeTheme.dimens.inset,
42+
),
43+
verticalArrangement = verticalArrangement,
44+
) {
45+
items(
46+
count = messages.itemCount,
47+
key = messages.itemKey { item -> item.key },
48+
contentType = messages.itemContentType { item ->
49+
when (item) {
50+
is ChatItem.Date -> "separators"
51+
is ChatItem.Message -> "messages"
52+
}
53+
}
54+
) { index ->
55+
when (val item = messages[index]) {
56+
is ChatItem.Date -> DateBubble(
57+
modifier = Modifier.padding(vertical = CodeTheme.dimens.grid.x2),
58+
date = item.date
59+
)
60+
is ChatItem.Message -> {
61+
// reverse layout so +1 to get previous
62+
val prev = runCatching { messages[index + 1] }
63+
.map { it as? ChatItem.Message }
64+
.map { it?.chatMessageId }
65+
.getOrNull()
66+
// reverse layout so -1 to get next
67+
val next = runCatching { messages[index - 1] }
68+
.map { it as? ChatItem.Message }
69+
.map { it?.chatMessageId }
70+
.getOrNull()
71+
72+
MessageNode(
73+
modifier = Modifier.fillMaxWidth(),
74+
contents = item.message,
75+
date = item.date,
76+
isPreviousSameMessage = prev == item.chatMessageId,
77+
isNextSameMessage = next == item.chatMessageId,
78+
thankUser = { dispatch(MessageListEvent.ThankUser(item.chatMessageId)) },
79+
openMessageChat = { dispatch(MessageListEvent.OpenMessageChat(item.chatMessageId)) }
80+
)
81+
}
82+
83+
else -> Unit
84+
}
85+
}
86+
// add last separator
87+
// this isn't handled by paging separators due to no `beforeItem` to reference against
88+
// at end of list due to reverseLayout
89+
if (messages.itemCount > 0) {
90+
(messages[messages.itemCount - 1] as? ChatItem.Message)?.date?.let { date ->
91+
item {
92+
val dateString = remember(date) {
93+
date.formatDateRelatively()
94+
}
95+
DateBubble(
96+
modifier = Modifier.padding(bottom = CodeTheme.dimens.grid.x2),
97+
date = dateString
98+
)
99+
}
100+
}
101+
}
102+
}
103+
}

app/src/main/java/com/getcode/ui/components/chat/MessageNode.kt

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,11 @@ package com.getcode.ui.components.chat
33
import androidx.compose.foundation.background
44
import androidx.compose.foundation.layout.Arrangement
55
import androidx.compose.foundation.layout.Box
6+
import androidx.compose.foundation.layout.BoxWithConstraints
67
import androidx.compose.foundation.layout.Column
78
import androidx.compose.foundation.layout.fillMaxWidth
89
import androidx.compose.foundation.layout.padding
10+
import androidx.compose.foundation.layout.widthIn
911
import androidx.compose.foundation.layout.wrapContentWidth
1012
import androidx.compose.foundation.shape.CornerBasedShape
1113
import androidx.compose.foundation.shape.CornerSize
@@ -61,7 +63,7 @@ fun MessageNode(
6163
thankUser: () -> Unit = { },
6264
openMessageChat: () -> Unit = { },
6365
) {
64-
Box(
66+
BoxWithConstraints(
6567
modifier = modifier
6668
.padding(vertical = CodeTheme.dimens.grid.x1)
6769
) {
@@ -78,7 +80,8 @@ fun MessageNode(
7880
is MessageContent.Exchange -> {
7981
MessagePayment(
8082
modifier = Modifier
81-
.fillMaxWidth(0.895f)
83+
.align(if (contents.verb.increasesBalance) Alignment.CenterStart else Alignment.CenterEnd)
84+
.widthIn(max = maxWidth * 0.75f)
8285
.background(
8386
color = color,
8487
shape = when {
@@ -92,6 +95,7 @@ fun MessageNode(
9295
contents = contents,
9396
showTipActions = showTipActions,
9497
thankUser = thankUser,
98+
status = contents.status,
9599
date = date,
96100
openMessageChat = openMessageChat
97101
)
@@ -120,7 +124,8 @@ fun MessageNode(
120124
is MessageContent.SodiumBox -> {
121125
EncryptedContent(
122126
modifier = Modifier
123-
.fillMaxWidth(0.895f)
127+
.align(if (contents.status.isOutgoing()) Alignment.CenterEnd else Alignment.CenterStart)
128+
.widthIn(max = maxWidth * 0.75f)
124129
.background(
125130
color = color,
126131
shape = when {

app/src/main/java/com/getcode/ui/components/chat/MessageTextContent.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import androidx.compose.ui.text.font.FontWeight
2525
import androidx.compose.ui.text.rememberTextMeasurer
2626
import com.getcode.model.MessageStatus
2727
import com.getcode.theme.BrandDark
28+
import com.getcode.theme.ChatOutgoing
2829
import com.getcode.theme.CodeTheme
2930
import com.getcode.util.formatDateRelatively
3031
import kotlinx.datetime.Instant
@@ -38,12 +39,12 @@ fun MessageText(
3839
status: MessageStatus = MessageStatus.Unknown,
3940
) {
4041
val alignment = if (isFromSelf) Alignment.CenterEnd else Alignment.CenterStart
41-
val color = if (isFromSelf) Color(0xFF443091) else BrandDark
42+
val color = if (isFromSelf) ChatOutgoing else BrandDark
4243

4344
BoxWithConstraints(modifier = modifier.fillMaxWidth(), contentAlignment = alignment) {
4445
BoxWithConstraints(
4546
modifier = Modifier
46-
.widthIn(max = maxWidth * 0.895f)
47+
.widthIn(max = maxWidth * 0.75f)
4748
.background(
4849
color = color,
4950
shape = MessageNodeDefaults.DefaultShape

app/src/main/java/com/getcode/ui/components/chat/TipChatActions.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ internal fun TipChatActions(
3636
thankUser()
3737
}
3838

39+
if (showTipActions) {
3940
if (tipChatsEnabled && contents.verb is Verb.ReceivedTip) {
4041
Row(
4142
verticalAlignment = Alignment.CenterVertically,
@@ -60,4 +61,5 @@ internal fun TipChatActions(
6061
)
6162
}
6263
}
64+
}
6365
}

app/src/main/java/com/getcode/ui/components/conversation/utils/HandleMessageChanges.kt renamed to app/src/main/java/com/getcode/ui/components/chat/utils/HandleMessageChanges.kt

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package com.getcode.ui.components.conversation.utils
1+
package com.getcode.ui.components.chat.utils
22

33
import android.os.Build
44
import androidx.compose.foundation.lazy.LazyListState
@@ -10,10 +10,7 @@ import androidx.compose.runtime.saveable.rememberSaveable
1010
import androidx.compose.runtime.setValue
1111
import androidx.compose.runtime.snapshotFlow
1212
import androidx.paging.compose.LazyPagingItems
13-
import com.getcode.model.ConversationMessage
14-
import com.getcode.model.ConversationMessageContent
1513
import com.getcode.model.MessageContent
16-
import com.getcode.ui.components.chat.utils.ChatItem
1714
import com.getcode.ui.utils.isScrolledToTheBeginning
1815
import kotlinx.coroutines.delay
1916
import kotlinx.coroutines.flow.distinctUntilChangedBy

0 commit comments

Comments
 (0)