feat: render a lone zap notification as a full note, not a bare card

The single-zap/single-nutzap branch of MultiSetCompose dropped the bare
RenderLnZap/RenderNutzap activity card straight into the feed, with no
author column on the left, so it didn't read like the surrounding notes.

Reuse NoteCompose on the zap-receipt note instead: it wraps the same zap
card as the note body and gives it the standard note chrome — author
picture on the left, username header, reactions row. Padding is handed to
NoteCompose for this branch so it isn't doubled against the card column.

NoteCompose's author column previously resolved baseNote.author, which for
a kind-9735 zap receipt is the recipient's lightning provider (the signer),
not the zapper. Resolve the sender from the embedded zap request the same
way the thread's master note already does, so the picture on the left is
the actual zapper.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Cg8mHZDCfGmGAVDaeJxPFz
This commit is contained in:
Claude
2026-06-26 00:02:37 +00:00
parent 82e1b99036
commit 1614e01f0e
2 changed files with 30 additions and 12 deletions
@@ -87,8 +87,6 @@ import com.vitorpamplona.amethyst.ui.navigation.routes.Route
import com.vitorpamplona.amethyst.ui.navigation.routes.authorRouteFor
import com.vitorpamplona.amethyst.ui.navigation.routes.routeFor
import com.vitorpamplona.amethyst.ui.note.elements.NoteDropDownMenu
import com.vitorpamplona.amethyst.ui.note.types.RenderLnZap
import com.vitorpamplona.amethyst.ui.note.types.RenderNutzap
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.notifications.CombinedZap
import com.vitorpamplona.amethyst.ui.screen.loggedIn.notifications.MultiSetCard
@@ -185,29 +183,39 @@ fun MultiSetCompose(
},
onLongClick = { popupExpanded.value = true },
).padding(
start = 12.dp,
end = 12.dp,
top = 10.dp,
bottom = if (isLargeCard) 10.dp else 0.dp,
// The large-card branch renders through NoteCompose, which applies
// its own 12dp/10dp note padding; let it own the inset there so we
// don't double it up. The gallery branch keeps the card's padding.
start = if (isLargeCard) 0.dp else 12.dp,
end = if (isLargeCard) 0.dp else 12.dp,
top = if (isLargeCard) 0.dp else 10.dp,
bottom = 0.dp,
)
}
Column(modifier = columnModifier) {
when {
// Render the lone zap/nutzap as a full note (author picture on the left,
// the zap card as its body) by reusing NoteCompose on the receipt note,
// instead of dropping the bare activity card into the feed.
singleZap != null ->
RenderLnZap(
note = singleZap.response,
NoteCompose(
baseNote = singleZap.response,
routeForLastRead = null,
isHiddenFeed = showHidden,
quotesLeft = 1,
backgroundColor = backgroundColor,
parentBackgroundColor = backgroundColor,
accountViewModel = accountViewModel,
nav = nav,
)
singleNutzap != null ->
RenderNutzap(
note = singleNutzap,
NoteCompose(
baseNote = singleNutzap,
routeForLastRead = null,
isHiddenFeed = showHidden,
quotesLeft = 1,
backgroundColor = backgroundColor,
parentBackgroundColor = backgroundColor,
accountViewModel = accountViewModel,
nav = nav,
)
@@ -2010,6 +2010,16 @@ fun RenderAuthorImages(
} else {
NoteAuthorPicture(baseNote, Size55dp, accountViewModel = accountViewModel, nav = nav)
}
} else if (noteEvent is LnZapEvent) {
// Zap receipts are signed by the recipient's lightning provider; show the
// sender from the embedded zap request instead of the service key, matching
// how the thread's master note resolves the author.
val zapSender = observeZapSender(baseNote, accountViewModel).value
if (zapSender != null) {
UserPicture(zapSender, Size55dp, accountViewModel = accountViewModel, nav = nav)
} else {
NoteAuthorPicture(baseNote, Size55dp, accountViewModel = accountViewModel, nav = nav)
}
} else {
NoteAuthorPicture(baseNote, Size55dp, accountViewModel = accountViewModel, nav = nav)
}