From 1424cc46382266a51123a8425424f3c8cb943e8e Mon Sep 17 00:00:00 2001 From: Adam Soltys Date: Sat, 14 Nov 2026 22:36:58 -0700 Subject: A reply, zap or reaction is about the moment of a clip only when you say so. "i zapped and commented on a post from helen yrmom and didn't intend to tie it to the video timestamp but i guess it did by default. maybe we can revisit the ui/flow there so it doesn't do that by default?" (Adam, 2026-11-14). Anything done to a post whose clip had played took the clip's current time: the reply opened as "Comment at 0:12" (an × to drop it), and a zap's or reaction's sheet had nothing showing it at all. Now the time is offered as a toggle, off until tapped — "⏱ About 0:12 in the clip", ✓ when on — in the reply box, the reaction sheet, and both zap sheets (Ark and Lightning); a one-tap zap, which has no sheet, is never tied. Ticked, it goes out as before (the media tag, "[0:12]" before a comment, its mark on the clip's bar). media-moments-feed-test ticks the toggle where it used to rely on the default and checks it starts off; media-timeline, features-smoke and the zap tests pass MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01VMan6UKGJyuGyZFxddYRxS --- src/features/zaps.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'src/features/zaps.js') diff --git a/src/features/zaps.js b/src/features/zaps.js index 8e29409..49e457f 100644 --- a/src/features/zaps.js +++ b/src/features/zaps.js @@ -12,6 +12,7 @@ // land here directly. import { t } from '../i18n.js'; +import { momentToggle, mediaClock } from '../media-timeline.js'; import { npubOf } from '../nostr.js'; import { parseZapTarget, zapTargetFromProfile, fetchPayParams, buildZapRequest, requestInvoice, parseLnurlAuth, lnurlAuthKey, lnurlAuthLogin, submitWithdraw } from '../lnurl.js'; import { HDKey } from '@scure/bip32'; @@ -213,7 +214,8 @@ export function zapsFeature(ctx) { const profile = await wallet.nostrProfile(target.pk); const lnTarget = zapTargetFromProfile(profile, target.pk); if (!lnTarget) throw new Error(t('lnZapNoAddress')); - const t2 = { ...lnTarget, eventId: target.eventId || null, moment: target.moment }; + // a one-tap zap has no sheet to tick: never tied to a moment + const t2 = { ...lnTarget, eventId: target.eventId || null, moment: null }; const p = await fetchPayParams(t2.url); const msat = sats * 1000; if (msat < p.minSendable || msat > p.maxSendable) { @@ -286,7 +288,7 @@ export function zapsFeature(ctx) { const sign = await zapSigner(); zapRequest = await buildZapRequest({ amountMsat: msat, relays: wallet.nostrRelays(), lnurlBech32: z.target.lnurlBech32, - recipientPk: z.target.pk, eventId: z.target.eventId || null, moment: z.target.moment, comment: z.comment, + recipientPk: z.target.pk, eventId: z.target.eventId || null, moment: z.momentOn ? z.target.moment : null, comment: z.comment, signFn: sign, }); } @@ -517,6 +519,8 @@ export function zapsFeature(ctx) { maxlength: p.allowsNostr ? '280' : String(p.commentAllowed || 280), onInput: (e) => { z.comment = e.target.value; } }) : null, + p.allowsNostr && z.target && z.target.moment ? h('div', { class: 'row' }, momentToggle(h, z.target.moment, !!z.momentOn, (on) => { z.momentOn = on; render(); }, + t('mediaTieTo', { time: mediaClock(z.target.moment.seconds) }))) : null, z.social ? h('div', { class: 'small faint' }, p.allowsNostr ? t('lnZapHintNostr') : t('lnZapHintPlain')) : null), z.error ? h('div', { class: 'notice err' }, z.error) : null, h('div', { class: 'row gap6' }, -- cgit v1.2.3