From d28b716ae791f924d7dea901b9a9711e44bd7312 Mon Sep 17 00:00:00 2001 From: Adam Soltys Date: Wed, 2 Sep 2026 22:18:16 -0700 Subject: The Amethyst handshake survives its own launch: opening the signer app freezes the tab (relay sockets die as the approval publishes) and its callbackUrl then RELOADS the page, discarding the client key mid-handshake — so the pending connect now persists to sessionStorage, the wait races the live subscription against a stored-event poller kicked on every return to visibility, and a fresh boot resumes the handshake instead of shrugging. Proven both ways with a scripted signer: approval after reload, and approval stored while the page was gone MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01MrkLsNScXe9b6BviUSdmMr --- src/features/nostrlogin.js | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) (limited to 'src/features') diff --git a/src/features/nostrlogin.js b/src/features/nostrlogin.js index b891572..ec230e5 100644 --- a/src/features/nostrlogin.js +++ b/src/features/nostrlogin.js @@ -14,7 +14,7 @@ import { t } from '../i18n.js'; import { qrSvg } from '../qr.js'; import { extensionSigner, bunkerSigner, keySigner, parseNostrSecret, - walletForSigner, publishWalletBackup, fetchWalletBackup, nostrConnect, resumeBunker } from '../nostr-login.js'; + walletForSigner, publishWalletBackup, fetchWalletBackup, nostrConnect, nostrConnectPending, resumeBunker } from '../nostr-login.js'; // The nostr ostrich, in the brand's monochrome. const OSTRICH = ''; @@ -209,12 +209,12 @@ export function nostrLoginFeature(ctx) { let nc = null; let ncRun = null; let ncToken = null; // guards the async gap while the nip46 module loads - async function startNostrConnect(run) { + async function startNostrConnect(run, resume = null) { if (nc) nc.cancel(); ncRun = run; const token = (ncToken = {}); let mine; - try { mine = await nostrConnect(); } catch { + try { mine = await nostrConnect({ resume }); } catch { if (token === ncToken && ui.nostrConnectOpen) { ui.nostrConnectOpen = false; render(); } return; } @@ -236,6 +236,22 @@ export function nostrLoginFeature(ctx) { ncToken = null; } + // A reload mid-nostrconnect (the signer app's callbackUrl navigating the + // tab home, or Android discarding the frozen tab) used to strand the + // login: the approval lands on the relays and nobody is waiting. On boot, + // pick the persisted handshake back up — the poller finds the stored + // approval and the login finishes on its own. Deferred a beat so the boot + // restore has decided whether this tab is even logged out. + setTimeout(() => { + const pending = !nc && !wallet.mnemonic && ui.screen === 'unlock' && nostrConnectPending(); + if (!pending) return; + if (ui.onb && ['welcome', 'signin'].includes(ui.onb.step)) ui.onb = { step: 'signin' }; + ui.nostrLoginOpen = true; + ui.nostrConnectOpen = true; + startNostrConnect(loginWith, pending); + render(); + }, 400); + function nostrConnectSection(run) { if (!ui.nostrConnectOpen) { return h('button', { -- cgit v1.2.3