From 99b386db541bae6740efd5bbac58544592b4f34d Mon Sep 17 00:00:00 2001 From: thePR0M3TH3AN <53631862+PR0M3TH3AN@users.noreply.github.com> Date: Thu, 28 May 2026 21:53:31 -0400 Subject: [PATCH] Premium unlock: refresh preview pane in place with a reveal flash MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- src/index.html | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/src/index.html b/src/index.html index b264cf0..3df7035 100644 --- a/src/index.html +++ b/src/index.html @@ -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.';