feat: align live-chat zap cards with the new chat design

Zap messages in live streams (and other chat feeds) now render as a
content-hugging, centered pill with the 18dp rounding of the redesigned
bubbles and system messages, instead of an edge-to-edge 8dp card. The
bitcoin-orange accent, big-zap emphasis, and inline comment rendering
are unchanged.

StreamSystemCard gains opt-in fillWidth/shape parameters (defaults
preserve the raid and clip cards, which carry media previews and want
the full width).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0129yvP2hmVeDFfuKKy94tqX
This commit is contained in:
Claude
2026-07-11 14:34:54 +00:00
parent 8a6b658984
commit b68dd6ad16
2 changed files with 79 additions and 52 deletions
@@ -25,6 +25,7 @@ import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
@@ -51,11 +52,13 @@ import com.vitorpamplona.amethyst.ui.theme.BitcoinOrange
import com.vitorpamplona.amethyst.ui.theme.Size20Modifier
import com.vitorpamplona.amethyst.ui.theme.Size20dp
import com.vitorpamplona.amethyst.ui.theme.Size24Modifier
import com.vitorpamplona.amethyst.ui.theme.StdHorzSpacer
import com.vitorpamplona.quartz.nip57Zaps.LnZapEvent
private const val BIG_ZAP_THRESHOLD_SATS = 50_000L
// Matches the 18dp rounding of the redesigned chat bubbles / system pills.
private val ChatZapCardShape = RoundedCornerShape(18.dp)
@Composable
fun RenderChatZap(
baseNote: Note,
@@ -79,54 +82,56 @@ fun RenderChatZap(
val accentAlpha = if (isBigZap) 0.18f else 0.08f
val amountText = card.amount ?: showAmountInteger(zapEvent.amount)
StreamSystemCard(accent = BitcoinOrange, accentAlpha = accentAlpha) {
// Content-hugging centered pill, matching the system-message design the rest
// of the chat feed moved to; the bitcoin accent keeps zaps celebratory.
StreamSystemCard(
accent = BitcoinOrange,
accentAlpha = accentAlpha,
fillWidth = false,
shape = ChatZapCardShape,
) {
val backgroundColor = remember(accentAlpha) { mutableStateOf(BitcoinOrange.copy(alpha = accentAlpha)) }
Row(
modifier = Modifier,
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(6.dp),
) {
ZapIcon(
if (isBigZap) Size24Modifier else Size20Modifier,
BitcoinOrange,
)
Column(horizontalAlignment = Alignment.CenterHorizontally) {
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(6.dp),
) {
ZapIcon(
if (isBigZap) Size24Modifier else Size20Modifier,
BitcoinOrange,
)
Column(modifier = Modifier.weight(1f)) {
Row(verticalAlignment = Alignment.CenterVertically) {
val sender = card.user
if (sender != null) {
UserPicture(sender, Size20dp, Modifier, accountViewModel, nav)
Spacer(StdHorzSpacer)
UsernameDisplay(
baseUser = sender,
fontWeight = FontWeight.Bold,
accountViewModel = accountViewModel,
)
} else {
Text(
text = stringRes(R.string.chat_zap_anonymous),
fontWeight = FontWeight.Bold,
)
}
Spacer(StdHorzSpacer)
Text(
text = stringRes(R.string.chat_zap_amount_suffix, amountText),
color = BitcoinOrange,
fontWeight = if (isBigZap) FontWeight.Bold else FontWeight.Normal,
)
}
card.comment?.takeIf { it.isNotBlank() }?.let { comment ->
Spacer(Modifier.padding(top = 2.dp))
CrossfadeToDisplayComment(
comment = comment,
backgroundColor = backgroundColor,
nav = nav,
val sender = card.user
if (sender != null) {
UserPicture(sender, Size20dp, Modifier, accountViewModel, nav)
UsernameDisplay(
baseUser = sender,
fontWeight = FontWeight.Bold,
accountViewModel = accountViewModel,
)
} else {
Text(
text = stringRes(R.string.chat_zap_anonymous),
fontWeight = FontWeight.Bold,
)
}
Text(
text = stringRes(R.string.chat_zap_amount_suffix, amountText),
color = BitcoinOrange,
fontWeight = if (isBigZap) FontWeight.Bold else FontWeight.Normal,
)
}
card.comment?.takeIf { it.isNotBlank() }?.let { comment ->
Spacer(Modifier.padding(top = 2.dp))
CrossfadeToDisplayComment(
comment = comment,
backgroundColor = backgroundColor,
nav = nav,
accountViewModel = accountViewModel,
)
}
}
}
@@ -22,8 +22,10 @@ package com.vitorpamplona.amethyst.commons.nip53LiveActivities.ui
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxScope
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
@@ -32,11 +34,16 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.unit.dp
/**
* Shared rounded, accent-tinted container used by the centered "system-style"
* cards rendered inside the live stream chat feed (zaps, raids, clips).
* Shared rounded, accent-tinted container used by the "system-style" cards
* rendered inside the live stream chat feed (zaps, raids, clips).
*
* [fillWidth] = true (default) stretches the card edge to edge, for content that
* carries media previews. false hugs the content and centers the card in the
* feed, matching the centered system-message pill design.
*
* Kept platform-neutral so Desktop can consume it alongside Android.
*/
@@ -45,19 +52,34 @@ fun StreamSystemCard(
accent: Color = MaterialTheme.colorScheme.primary,
accentAlpha: Float = 0.12f,
onClick: (() -> Unit)? = null,
fillWidth: Boolean = true,
shape: Shape = RoundedCornerShape(8.dp),
content: @Composable BoxScope.() -> Unit,
) {
val base =
Modifier
.fillMaxWidth()
(if (fillWidth) Modifier.fillMaxWidth() else Modifier)
.padding(horizontal = 8.dp, vertical = 2.dp)
.clip(RoundedCornerShape(8.dp))
.clip(shape)
.background(accent.copy(alpha = accentAlpha))
val clickable = if (onClick != null) base.clickable(onClick = onClick) else base
Box(
modifier = clickable.padding(horizontal = 10.dp, vertical = 8.dp),
content = content,
)
val card =
@Composable {
Box(
modifier = clickable.padding(horizontal = 10.dp, vertical = 8.dp),
content = content,
)
}
if (fillWidth) {
card()
} else {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.Center,
) {
card()
}
}
}