From a57947da0c74b2d6926382dac5ba784e13fa02cc Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Mon, 26 Jan 2026 20:57:17 +0000 Subject: [PATCH] fix(nostr): await nip-07 extension injection to resolve race conditions This commit fixes a critical race condition where `window.nostr` might not be injected before the app attempts to use it, causing failures in decryption (subscriptions, hashtags, DMs) and login flows. Changes: - Updated `ensureExtensionPermissions` in `js/nostr/client.js` to verify `window.nostr` existence even when permissions are cached. If missing, it now awaits `waitForNip07Extension` instead of returning a false success. - Updated `ensureActiveSignerForPubkey` in `js/nostr/client.js` to wait for the extension if permissions are cached but `window.nostr` is missing, ensuring a valid signer is created. This ensures that the "Performance Analysis" optimization (skipping `enable()` calls based on cache) does not break functionality when the extension injection is delayed. Co-authored-by: PR0M3TH3AN <53631862+PR0M3TH3AN@users.noreply.github.com> --- js/nostr/client.js | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/js/nostr/client.js b/js/nostr/client.js index 49a6c1b3..0ff3369d 100644 --- a/js/nostr/client.js +++ b/js/nostr/client.js @@ -2584,6 +2584,16 @@ export class NostrClient { ); if (!outstanding.length) { + // Even if permissions are cached, we must ensure the extension object is actually + // injected and available on the window before returning success. + // This prevents race conditions where the app loads faster than the extension. + if (typeof window !== "undefined" && !window.nostr) { + try { + await waitForNip07Extension(3000); + } catch (error) { + return { ok: false, error: new Error("extension-unavailable") }; + } + } return { ok: true }; } @@ -2618,8 +2628,20 @@ export class NostrClient { return existingSigner; } - const extension = + let extension = typeof window !== "undefined" && window && window.nostr ? window.nostr : null; + + // If the extension is missing but we have cached permissions, it implies the user + // previously logged in with an extension. We should wait briefly for injection + // to resolve the common race condition where the app loads faster than the extension. + if (!extension && this.extensionPermissionCache && this.extensionPermissionCache.size > 0) { + try { + extension = await waitForNip07Extension(3000); + } catch (error) { + // Fall through to existing signer check + } + } + if (!extension) { return existingSigner; }