diff --git a/client/src/components/SettingsPage.tsx b/client/src/components/SettingsPage.tsx index 21cf2bd..ee3efc9 100644 --- a/client/src/components/SettingsPage.tsx +++ b/client/src/components/SettingsPage.tsx @@ -47,23 +47,18 @@ export function SettingsPage() { const [storefrontEnabled, setStorefrontEnabled] = useState(false); const [savingStorefront, setSavingStorefront] = useState(false); const [saving, setSaving] = useState(false); - const [loadingSettings, setLoadingSettings] = useState(true); + const [loadingSettings, setLoadingSettings] = useState(() => hasIdentity()); const [settingsChanged, setSettingsChanged] = useState(false); // Blossom server state const [blossomServer, setBlossomServerState] = useState(getBlossomServer()); - const [customBlossomUrl, setCustomBlossomUrl] = useState(''); + // Kept in sync after mount by the preset/submit handlers below — no effect needed. + const [customBlossomUrl, setCustomBlossomUrl] = useState(() => + PRESET_BLOSSOM_SERVERS.some((s) => s.url === blossomServer) ? '' : blossomServer + ); const [blossomUrlError, setBlossomUrlError] = useState(''); const [mirroringEnabled, setMirroringEnabledState] = useState(getMirroringEnabled()); - const isCustomServer = !PRESET_BLOSSOM_SERVERS.some((s) => s.url === blossomServer); - - useEffect(() => { - if (isCustomServer) { - setCustomBlossomUrl(blossomServer); - } - }, [blossomServer, isCustomServer]); - useEffect(() => { if (hasIdentity()) { const pubkey = getPublicKeyHex(); @@ -77,8 +72,6 @@ export function SettingsPage() { }) .catch(() => {}) .finally(() => setLoadingSettings(false)); - } else { - setLoadingSettings(false); } }, []); @@ -166,6 +159,7 @@ export function SettingsPage() { } setBlossomServer(normalized); setBlossomServerState(normalized); + setCustomBlossomUrl(normalized); setBlossomUrlError(''); toast.showToast('Blossom server updated', 'success'); }; diff --git a/client/src/components/UnlockPage.tsx b/client/src/components/UnlockPage.tsx index 675b7f1..3e8e279 100644 --- a/client/src/components/UnlockPage.tsx +++ b/client/src/components/UnlockPage.tsx @@ -59,15 +59,16 @@ export function UnlockPage() { () => verifyGeneratedPreviewBundle(unlock.stash?.generatedPreview, unlock.stash?.previewProof), [unlock.stash?.generatedPreview, unlock.stash?.previewProof] ); + const generatedPreview = unlock.stash?.generatedPreview; const previewStats = useMemo(() => { - if (!unlock.stash?.generatedPreview || unlock.stash.generatedPreview.kind !== 'text-peek') { + if (!generatedPreview || generatedPreview.kind !== 'text-peek') { return null; } - const metadata = unlock.stash.generatedPreview.metadata as TextPreviewMetadata; + const metadata = generatedPreview.metadata as TextPreviewMetadata; const percent = - unlock.stash.generatedPreview.fileSize > 0 - ? Math.round((metadata.previewBytes / unlock.stash.generatedPreview.fileSize) * 1000) / 10 + generatedPreview.fileSize > 0 + ? Math.round((metadata.previewBytes / generatedPreview.fileSize) * 1000) / 10 : 0; return { @@ -75,7 +76,7 @@ export function UnlockPage() { lines: metadata.linesIncluded, percent, }; - }, [unlock.stash?.generatedPreview]); + }, [generatedPreview]); const previewProofInvalid = previewVerification.state === 'invalid'; const legacyTextPeek = unlock.stash?.generatedPreview?.kind === 'text-peek' && @@ -212,21 +213,7 @@ export function UnlockPage() { } }, [id, previewProofInvalid, startTimerAndPolling]); - // Auto-create or resume invoice when Lightning tab is active and stash is loaded - useEffect(() => { - if ( - tab !== 'lightning' || - !unlock.stash || - invoice || - lnLoading || - unlock.status !== 'ready' || - !claimAttempted.current || - previewProofInvalid - ) { - return; - } - - // Try to resume a persisted invoice from sessionStorage + const startOrResumeInvoice = useCallback(() => { if (id) { try { const saved = sessionStorage.getItem(`stashu-invoice-${id}`); @@ -237,10 +224,9 @@ export function UnlockPage() { // Resume polling the existing invoice startTimerAndPolling(parsed.invoice, parsed.quoteId, parsed.expiresAt); return; - } else { - // Expired — clean up - sessionStorage.removeItem(`stashu-invoice-${id}`); } + // Expired — clean up + sessionStorage.removeItem(`stashu-invoice-${id}`); } } catch { // Corrupted storage — ignore @@ -248,15 +234,31 @@ export function UnlockPage() { } createInvoice(); + }, [id, createInvoice, startTimerAndPolling]); + + // Auto-create or resume invoice when Lightning tab is active and stash is loaded + useEffect(() => { + if ( + tab !== 'lightning' || + !unlock.stash || + invoice || + lnLoading || + unlock.status !== 'ready' || + !claimAttempted.current || + previewProofInvalid + ) { + return; + } + + // Records the invoice the mint hands back, so the extra render here is intentional. + startOrResumeInvoice(); }, [ tab, unlock.stash, invoice, lnLoading, unlock.status, - id, - createInvoice, - startTimerAndPolling, + startOrResumeInvoice, previewProofInvalid, ]);