diff --git a/apps/flipcash/shared/chat-ui/src/main/kotlin/com/flipcash/shared/chat/ui/ChatSummaryMapping.kt b/apps/flipcash/shared/chat-ui/src/main/kotlin/com/flipcash/shared/chat/ui/ChatSummaryMapping.kt index ef28929149..1707d26c4d 100644 --- a/apps/flipcash/shared/chat-ui/src/main/kotlin/com/flipcash/shared/chat/ui/ChatSummaryMapping.kt +++ b/apps/flipcash/shared/chat-ui/src/main/kotlin/com/flipcash/shared/chat/ui/ChatSummaryMapping.kt @@ -52,17 +52,18 @@ private fun ChatSummary.formatPreview( resources: ResourceHelper, ): String? { val lastMsg = metadata.lastMessage ?: return null + val isGroup = metadata.type == ChatType.GROUP val sentBySelf = lastMsg.senderId != null && lastMsg.senderId == selfId // Only a group attributes, and only someone else's message: "You:" already covers the viewer's, // and a DM's counterparty is the row's own name. Null when the roster subset does not have the // sender — the list does not fetch profiles, so an unattributed body is the honest fallback. - val senderName = if (metadata.type == ChatType.GROUP && !sentBySelf) { + val senderName = if (isGroup && !sentBySelf) { metadata.members.firstOrNull { it.userId == lastMsg.senderId }?.userProfile?.displayName } else { null } return lastMsg.content.firstOrNull() - ?.previewText(sentBySelf, senderName, tokensByMint, resources) + ?.previewText(sentBySelf, senderName, isGroup, tokensByMint, resources) } /** @@ -74,6 +75,7 @@ private fun ChatSummary.formatPreview( private fun MessageContent.previewText( sentBySelf: Boolean, senderName: String?, + isGroup: Boolean, tokensByMint: Map, resources: ResourceHelper, depth: Int = 0, @@ -104,13 +106,17 @@ private fun MessageContent.previewText( formatted } // "You received" does not carry over: in a DM the cash came to the viewer, in a group it - // went to the group and the viewer may have got none of it. + // went to the group and the viewer may have got none of it. So a group message from anyone + // but the viewer previews as the amount alone — named when the roster subset has the + // sender, bare when it does not. if (senderName != null) { val previewRes = when (action) { MessageContent.Cash.Action.TIPPED -> R.string.label_chat_preview_tippedCash_bySender MessageContent.Cash.Action.SENT -> R.string.label_chat_preview_sentCash_bySender } resources.getString(previewRes, senderName, label) + } else if (isGroup && !sentBySelf) { + label } else { val previewRes = when (action) { MessageContent.Cash.Action.TIPPED -> @@ -129,7 +135,8 @@ private fun MessageContent.previewText( is MessageContent.Reply -> if (depth >= MAX_REPLY_UNWRAP_DEPTH) { null } else { - content.firstOrNull()?.previewText(sentBySelf, senderName, tokensByMint, resources, depth + 1) + content.firstOrNull() + ?.previewText(sentBySelf, senderName, isGroup, tokensByMint, resources, depth + 1) } // The feed carries the newest message that still has content, so a tombstone only diff --git a/apps/flipcash/shared/chat-ui/src/test/kotlin/com/flipcash/shared/chat/ui/ChatSummaryPreviewTest.kt b/apps/flipcash/shared/chat-ui/src/test/kotlin/com/flipcash/shared/chat/ui/ChatSummaryPreviewTest.kt index 370de9c58b..18856b4262 100644 --- a/apps/flipcash/shared/chat-ui/src/test/kotlin/com/flipcash/shared/chat/ui/ChatSummaryPreviewTest.kt +++ b/apps/flipcash/shared/chat-ui/src/test/kotlin/com/flipcash/shared/chat/ui/ChatSummaryPreviewTest.kt @@ -11,6 +11,8 @@ import com.flipcash.services.models.chat.ChatType import com.flipcash.services.models.chat.MessageContent import com.flipcash.shared.chat.ChatSummary import com.getcode.opencode.model.core.ID +import com.getcode.opencode.model.financial.toFiat +import com.getcode.solana.keys.Mint import com.getcode.util.resources.ResourceHelper import io.mockk.every import io.mockk.mockk @@ -209,4 +211,19 @@ class ChatSummaryPreviewTest { fun `a DM is not prefixed with the counterparty's name`() { assertEquals("gm", preview(listOf(MessageContent.Text("gm")))) } + + @Test + fun `group cash from someone the roster subset omits previews as the amount alone`() { + // "You received" would be a lie here: the cash went to the group, and the viewer may have + // got none of it. The reserve mint leaves the token name off, so this formats no string — + // which is why the mockk stub list stays empty. + val stranger: ID = listOf(7) + val cash = MessageContent.Cash( + intentId = listOf(3), + amount = 25.toFiat(), + mint = Mint.usdf, + ) + + assertEquals("$25.00", groupReference(listOf(cash), senderId = stranger).lastMessagePreview) + } }