fix(stash): avoid nip44 getter entirely — check login type + window.nostr directly
The NUser extension signer's .nip44 getter calls .nostr which throws "Browser extension not available" when window.nostr is absent. A try/catch around ?.nip44 doesn't work because esbuild optimises it away. Fix: check login type + window.nostr directly (no getter, no throw) to determine nip44 availability, and wrap all actual signer accesses at call-time in their own try/catch. Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
This commit is contained in:
parent
4c4e7159ba
commit
73f51662af
1 changed files with 30 additions and 8 deletions
|
|
@ -1,6 +1,7 @@
|
|||
import { useCallback, useMemo } from 'react';
|
||||
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
import { useNostr } from '@nostrify/react';
|
||||
import { useNostrLogin } from '@nostrify/react/login';
|
||||
import { useCurrentUser } from './useCurrentUser';
|
||||
import { useNostrPublish } from './useNostrPublish';
|
||||
|
||||
|
|
@ -47,14 +48,26 @@ const STASH_D_TAG = 'skatespots-stash';
|
|||
export function useStash() {
|
||||
const { nostr } = useNostr();
|
||||
const { user } = useCurrentUser();
|
||||
const { logins } = useNostrLogin();
|
||||
const queryClient = useQueryClient();
|
||||
const { mutateAsync: createEvent } = useNostrPublish();
|
||||
|
||||
// Safely check if nip44 is available without throwing (some signers throw
|
||||
// "Browser extension not available" when the getter is accessed with no login)
|
||||
const hasNip44 = (() => {
|
||||
try { return !!user?.signer?.nip44; } catch { return false; }
|
||||
})();
|
||||
// Determine if nip44 is safely available WITHOUT touching any throwing getter.
|
||||
// Extension signers throw "Browser extension not available" from their .nostr
|
||||
// getter when window.nostr isn't present yet (page load race, or no extension).
|
||||
// We check the login type instead: nsec and bunker have nip44 built-in always;
|
||||
// extension logins only have it when window.nostr is present; amber never has it.
|
||||
const hasNip44 = useMemo(() => {
|
||||
if (!user?.pubkey) return false;
|
||||
const login = logins.find((l) => l.pubkey === user.pubkey);
|
||||
if (!login) return false;
|
||||
// nsec and bunker: nip44 is always available (pure JS, no external dep)
|
||||
if (login.type === 'nsec' || login.type === 'bunker') return true;
|
||||
// extension: safe to check window.nostr directly (no getter, no throw)
|
||||
if (login.type === 'extension') return !!(globalThis as { nostr?: { nip44?: unknown } }).nostr?.nip44;
|
||||
// amber / unknown: no nip44 support
|
||||
return false;
|
||||
}, [user?.pubkey, logins]);
|
||||
|
||||
// Fetch and decrypt stash
|
||||
const { data: stash, isLoading, error } = useQuery({
|
||||
|
|
@ -81,8 +94,12 @@ export function useStash() {
|
|||
const event = events[0];
|
||||
|
||||
try {
|
||||
// Decrypt the content (encrypted to self)
|
||||
const decrypted = await user.signer!.nip44!.decrypt(user.pubkey, event.content);
|
||||
// Decrypt the content (encrypted to self) — access nip44 inside try/catch
|
||||
// to guard against extension signers throwing when window.nostr is gone
|
||||
let nip44Dec: typeof user.signer.nip44;
|
||||
try { nip44Dec = user.signer?.nip44; } catch { return { items: [], updatedAt: 0 }; }
|
||||
if (!nip44Dec) return { items: [], updatedAt: 0 };
|
||||
const decrypted = await nip44Dec.decrypt(user.pubkey, event.content);
|
||||
const stashData = JSON.parse(decrypted) as Stash;
|
||||
return stashData;
|
||||
} catch (err) {
|
||||
|
|
@ -100,8 +117,13 @@ export function useStash() {
|
|||
throw new Error('Encryption not available');
|
||||
}
|
||||
|
||||
// Access nip44 inside a try/catch at call time (signer is guaranteed by hasNip44 above)
|
||||
let nip44Enc: typeof user.signer.nip44;
|
||||
try { nip44Enc = user.signer?.nip44; } catch { throw new Error('Encryption not available'); }
|
||||
if (!nip44Enc) throw new Error('Encryption not available');
|
||||
|
||||
// Encrypt to self
|
||||
const encrypted = await user.signer!.nip44!.encrypt(user.pubkey, JSON.stringify(newStash));
|
||||
const encrypted = await nip44Enc.encrypt(user.pubkey, JSON.stringify(newStash));
|
||||
|
||||
// Publish as replaceable event
|
||||
await createEvent({
|
||||
|
|
|
|||
Loading…
Reference in a new issue