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 <[email protected]>
This commit is contained in:
google-labs-jules[bot]andPR0M3TH3AN committed 2026-01-26 20:57:17 +00:00
1 parent 91e0be0ad4
commit a57947da0c
1 file changed
+23 -1
+23 -1
View File
@@ -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;
}