feat(zap): default message + zap comment in history + completion polish#406
Conversation
There was a problem hiding this comment.
Pull request overview
Adds a user-configurable default zap message that is pre-filled into both the ZapModal and one-tap zap flows, then plumbs the comment end-to-end so it appears in wallet history, the ZapModal success state, and the ZappersListModal. Also includes several zap-flow reliability and polish fixes: deferring the optimistic count until LNURL succeeds (with a new revertOptimisticZap), a friendlier ZapModal error state, a 30-minute pending-tx TTL, instant bolt feedback on tap, and a one-shot sparkle burst on payment completion driven by a new markSelfZapCompleted / lastSelfZapAt channel.
Changes:
- New
defaultZapMessagestore + NIP-78 persistence inautoZapSettings.ts, surfaced via a Settings textarea and consumed byZapModal,oneTapZap, and wallet metadata. engagementCache.tsgainsZapper.comment,lastSelfZapAt,markSelfZapCompleted, andrevertOptimisticZap;processZapextracts the kind-9734 comment from the receipt description.- Polish: friendlier ZapModal error mapping, pending-tx TTL bumped to 30 min (matched in
WalletPanel), instant gold bolt + reduced-motion-aware sparkle burst inNoteTotalZaps.
Reviewed changes
Copilot reviewed 11 out of 11 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| src/lib/autoZapSettings.ts | New defaultZapMessage store, MAX_ZAP_MESSAGE_LENGTH, setters, localStorage + NIP-78 sync. |
| src/routes/settings/+page.svelte | Adds the "Default zap message" textarea outside the one-tap wallet-gated block. |
| src/lib/engagementCache.ts | Zapper.comment, lastSelfZapAt, comment parsed from zap request, markSelfZapCompleted, revertOptimisticZap. |
| src/lib/oneTapZap.ts | Defers optimistic update until after createZap, passes message to zap request + wallet metadata, reverts on failure, marks completion. |
| src/lib/wallet/walletManager.ts | Forwards metadata.comment into pending tx; pending TTL bumped to 30 min. |
| src/components/wallet/WalletPanel.svelte | Migration window aligned with new 30-min TTL via shared constant. |
| src/components/ZapModal.svelte | Pre-fills message from default store, friendlier error UI with retry gating, dispatches comment and renders it on success. |
| src/components/ZappersListModal.svelte | Renders per-zapper comment in italics under the display name. |
| src/components/NoteTotalZaps.svelte | Instant gold bolt on isZapping, one-shot sparkle burst keyed off lastSelfZapAt, reduced-motion fallback. |
| src/components/NoteActionBar.svelte | Modal zap-complete handler passes comment and calls markSelfZapCompleted. |
| package.json | Version bump 4.2.448 → 4.2.449. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
…polish
Adds a user-configurable default zap message that pre-fills the ZapModal
textarea on every open and ships with one-tap zaps. The message is then
threaded end-to-end so it surfaces wherever the zap is visible — wallet
history row, zappers-list modal, ZapModal success state. Plus a handful
of related polish to the zap flow that fell out of testing.
Settings + persistence
- New `defaultZapMessage` writable in autoZapSettings, persisted to
localStorage (`zapcooking_default_zap_message`) and synced to the
existing NIP-78 settings event alongside the one-tap toggle/amount.
- New Settings → Zap Settings → "Default zap message" textarea (live
bind via `bind:value={$defaultZapMessage}`; relay publish on blur
via on:change so we don't spam during typing).
- 280-char cap (MAX_ZAP_MESSAGE_LENGTH), counter shown under the field.
Comment plumbed everywhere
- `Zapper.comment` added to the engagement-cache type. `processZap`
parses it out of the kind-9734 zap request inside the receipt's
description tag, so zaps from anyone now carry their message
through to UI.
- `ZappersListModal` renders the comment in italics under each zapper.
- `optimisticZapUpdate` accepts a `comment` and stores it on the
optimistic zapper entry so the just-sent zap shows the user's own
message before the real receipt lands.
- `walletManager.sendPayment` forwards `metadata.comment` into the
pending transaction; WalletPanel already rendered `tx.comment` in
italics for outgoing rows.
- ZapModal success state shows the message in italics under the amount.
- ZapModal's `zap-complete` dispatch carries `comment` so
NoteActionBar's handler can pass it into the optimistic update.
One-tap zap uses the default
- `sendOneTapZap` reads `defaultZapMessage` and uses it for both the
zap request content and the wallet-history description/comment.
Reliability fixes
- `revertOptimisticZap` added. Optimistic update is now applied AFTER
`createZap()` succeeds (so a recipient with no lud16 / a failing
LNURL endpoint never produces a phantom zap count), and reverted
if `sendPayment` errors. No more ghost counts that never reconcile.
- Friendlier ZapModal error state: amber LightningSlash icon instead
of the giant red X, mapped messages for no-lightning-address /
timeout / insufficient-funds / cancelled / no-wallet, and a
contextual Try Again button that hides itself for unrecoverable
errors.
- Pending tx TTL bumped to 30 min (was 5) and WalletPanel's matching
window bumped to match. The pending tx is the bridge that carries
recipient pubkey + comment onto the SDK history row — dropping it
too early left rows labelled "Sent" with no recipient name.
Completion polish on the bolt
- Bolt turns yellow + filled the moment `isZapping` flips (synchronous
on tap, before any await) for instant tap confirmation — no waiting
for LNURL to resolve before the visual responds.
- One-shot sparkle burst on the bolt when the zap is fully paid. Six
amber particles fan out with a brief "pop" overshoot then fade.
Fires via a dedicated `markSelfZapCompleted(eventId)` called from
the one-tap path AFTER `sendPayment` returns success, and from
NoteActionBar's `handleZapComplete` (modal path). Separating the
completion signal from `optimisticZapUpdate` ensures the burst
fires exactly once, at the end of the flow — not at the LNURL step
and again at payment.
- Sparkle respects prefers-reduced-motion.
b1237f3 to
34aa577
Compare
Summary
Adds a user-configurable default zap message that pre-fills the ZapModal textarea on every open and ships with one-tap zaps. The message is then plumbed end-to-end so it surfaces wherever the zap is visible — wallet history rows, ZappersListModal entries, and the ZapModal success state. Plus a handful of related polish around the zap flow that fell out of testing on a Vercel preview:
bind:value, NIP-78 relay publish on blur). 280-char cap with counter.Zapper.commentplumbed through the engagement-cache.processZapparses the kind-9734 zap request from the receipt'sdescriptiontag, so the message shows for everyone's zaps, not just your own optimistic ones.walletManager.sendPaymentforwardsmetadata.commentinto the pending transaction. WalletPanel already renderedtx.commentin italics on outgoing rows.Reliability fixes
createZap()succeeds (so a recipient with nolud16/ a failing LNURL endpoint never produces a count that won't reconcile). NewrevertOptimisticZap()undoes the update ifsendPaymenterrors.LightningSlashin a tinted circle, mapped raw errors to human messages ("This user hasn't set up a Lightning address yet, so zaps can't be sent to them."instead of"Profile lud16: undefined"), and made Try Again context-aware — hidden for unrecoverable cases.WalletPanel.loadTransactionHistorybumped to match.Completion polish on the bolt
isZappingflips (synchronous on tap, before any await). Instant tap confirmation — no waiting on LNURL.markSelfZapCompleted(eventId)called only aftersendPaymentreturns success (one-tap path) or after the modal'szap-completeevent (modal path). Decoupled fromoptimisticZapUpdateso it fires exactly once at the end of the flow, not at the LNURL step and again at payment.prefers-reduced-motion.Test plan
lud16→ friendly "No Lightning address" error, no phantom zap count gets added (refresh and confirm)🤖 Generated with Claude Code