fix: abort dangling fetches and handle localStorage exceptions

- useNostr.ts: wrap auto-restore login fetch with AbortController,
  abort on logout to cancel in-flight request
- useWallet.ts: wrap localStorage.setItem/removeItem calls in try/catch
  for Safari private browsing quota exceptions

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Dorian
2026-03-08 20:43:34 +00:00
co-authored by Claude Opus 4.6
parent d98b264935
commit edd08fde59
2 changed files with 9 additions and 4 deletions
+3
View File
@@ -90,12 +90,14 @@ export function useNostr() {
const hasExtension = computed(() => !!window.nostr) const hasExtension = computed(() => !!window.nostr)
// Restore session on first load — re-verify with server (once only) // Restore session on first load — re-verify with server (once only)
const autoRestoreController = new AbortController()
if (!autoRestoreRan && pubkey.value && !bot.value) { if (!autoRestoreRan && pubkey.value && !bot.value) {
autoRestoreRan = true autoRestoreRan = true
fetch('/api/auth/login', { fetch('/api/auth/login', {
method: 'POST', method: 'POST',
headers: { 'Content-Type': 'application/json' }, headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ pubkey: pubkey.value }), body: JSON.stringify({ pubkey: pubkey.value }),
signal: autoRestoreController.signal,
}).then(r => r.json()).then(data => { }).then(r => r.json()).then(data => {
if (data.exists) { if (data.exists) {
bot.value = normalizeBotData(data.bot) bot.value = normalizeBotData(data.bot)
@@ -368,6 +370,7 @@ export function useNostr() {
} }
function logout() { function logout() {
autoRestoreController.abort()
clearAllState() clearAllState()
// Don't clear bf_nsec on logout — user may want to log back in // Don't clear bf_nsec on logout — user may want to log back in
} }
+6 -4
View File
@@ -16,8 +16,10 @@ function loadStored<T>(key: string): T | null {
} catch { return null } } catch { return null }
} }
function store(key: string, value: unknown) { function store(key: string, value: unknown) {
if (value == null) localStorage.removeItem(key) try {
else localStorage.setItem(key, JSON.stringify(value)) if (value == null) localStorage.removeItem(key)
else localStorage.setItem(key, JSON.stringify(value))
} catch { /* Safari private browsing quota exception */ }
} }
const isWalletConnected = ref(!!loadStored<string>('bf_wallet_method')) const isWalletConnected = ref(!!loadStored<string>('bf_wallet_method'))
@@ -73,7 +75,7 @@ export function useWallet() {
} }
// Store NWC string locally for client-side payment sending // Store NWC string locally for client-side payment sending
localStorage.setItem('bf_nwc_url', connectionString) try { localStorage.setItem('bf_nwc_url', connectionString) } catch { /* quota */ }
walletMethod.value = 'nwc' walletMethod.value = 'nwc'
isWalletConnected.value = true isWalletConnected.value = true
store('bf_wallet_method', 'nwc') store('bf_wallet_method', 'nwc')
@@ -120,7 +122,7 @@ export function useWallet() {
paymentStatus.value = 'idle' paymentStatus.value = 'idle'
pendingPayment.value = null pendingPayment.value = null
store('bf_wallet_method', null) store('bf_wallet_method', null)
localStorage.removeItem('bf_nwc_url') try { localStorage.removeItem('bf_nwc_url') } catch { /* quota */ }
} }
async function checkWalletStatus(): Promise<void> { async function checkWalletStatus(): Promise<void> {