Premium unlock: refresh preview pane in place with a reveal flash

Previously, after clicking Unlock from the preview pane, the pane
kept showing the watermarked locked version until the user closed
and re-opened it. That made the "I just bought this" moment feel
inert, and could read as "did the payment work?" friction.

Now, on a successful unlock from openUnlockConfirm:
  - Check if #nostrPreviewPanel is currently showing the row we
    just unlocked (it stashes the row reference on the panel).
  - If so, rebuild the row from _feedCache.events so it reflects
    the freshly-decrypted payload (passes skipVersionFilter so the
    cache stays fresh even for legacy version fields), and call
    openDesignPreview(freshRow). The pane re-paints: the action
    rail's "⚡ Unlock for N sats" swaps to "Use/Fork", the
    watermarked JPEG body swaps to the real renderTemplate /
    renderCustomArt etc. preview DOM, and Save JSON re-enables.
  - Apply a new .premiumRevealFlash class to the panel right
    after the swap. Defined as a 0.55 s premiumRevealFade
    keyframe that fades from opacity 0 + 0.985 scale + slight
    brightness boost to fully visible — reads as "the content
    just appeared" without being garish.

Failure cases stay non-blocking: a try/catch around the preview
refresh logs to console without affecting the rest of the unlock
toast or the feed rerender.

Tested via CDP: synthetic premium event in the cache, open
preview pane (locked: watermarked image + Unlock CTA),
stub payZapSplit + upsertPurchaseVaultItem + native confirm so
openUnlockConfirm runs through. After the call returns the
preview pane has Use/Fork button, no JPEG image in the body, and
the premiumRevealFlash class set.

Co-Authored-By: Claude Opus 4.7 <[email protected]>
This commit is contained in:
thePR0M3TH3ANandClaude Opus 4.7 committed 2026-05-28 21:53:31 -04:00
1 parent 191d216084
commit 99b386db54
1 file changed
+33
+33
View File
@@ -2113,6 +2113,15 @@
background-image:linear-gradient(var(--panel),var(--panel)),linear-gradient(135deg,#ffd166 0%,#ef476f 50%,#06d6a0 100%);
}
.feedCard.feedCardPremium:hover{box-shadow:0 0 0 1px rgba(239,71,111,.20), 0 6px 22px rgba(239,71,111,.18)}
/* Reveal animation when a premium design switches from locked to
unlocked inside the preview pane — fades the new content in over the
watermark with a subtle scale. */
@keyframes premiumRevealFade {
0% { opacity: 0; transform: scale(.985); filter: brightness(1.04); }
60% { opacity: 1; transform: none; filter: brightness(1.04); }
100% { opacity: 1; transform: none; filter: none; }
}
.premiumRevealFlash { animation: premiumRevealFade .55s ease-out; }
.feedCardPremiumRibbon{
position:absolute;top:6px;left:6px;z-index:7;
font-size:.62rem;font-weight:800;letter-spacing:.04em;
@@ -5709,6 +5718,30 @@
if (typeof _currentFeedRerender === 'function') {
try { _currentFeedRerender(); } catch {}
}
// If the user opened the preview pane for THIS design before clicking
// unlock, refresh the pane to show the decrypted preview (instead of
// the watermarked locked version) and flash the reveal animation.
try {
const previewedRow = $('nostrPreviewPanel')?._row;
if (previewedRow?.e?.id === eventId) {
const freshEvent = (_feedCache.events || []).find((ev) => ev.id === eventId) || row.e;
const freshRows = buildRowsFromEvents([freshEvent], 'any', '', true);
const freshRow = freshRows[0] || row;
// openDesignPreview wipes #nostrPreviewPanel; refresh from the
// updated cache state.
openDesignPreview(freshRow);
const panel = $('nostrPreviewPanel');
if (panel) {
panel.classList.remove('premiumRevealFlash');
// Force reflow so the animation re-triggers if it had been
// applied previously.
void panel.offsetWidth;
panel.classList.add('premiumRevealFlash');
}
}
} catch (refreshErr) {
console.warn('[unlock] preview refresh failed', refreshErr);
}
} catch (err) {
if (statusToast?.dismiss) statusToast.dismiss();
const msg = err?.message || 'Unlock failed.';