From e50316bbad329c5cd9f243acc37bf3e84fe56766 Mon Sep 17 00:00:00 2001 From: cyph3rasi Date: Tue, 14 Jul 2026 22:17:53 -0700 Subject: [PATCH] Prevent false Stuffbox failure when the successful popup closes first Hop-State: A_06FP8DE7KCCAG4HXZNBATBG Hop-Proposal: R_06FP8DDKY394CXK69M1Q78R Hop-Task: T_06FP8D3ECRPSTFC8CT8DZB0 Hop-Attempt: AT_06FP8D3ECRDMQ2HEYDAE7W0 --- src/components/StorageConfigurationPrompt.tsx | 23 ++++++++++++++++++- src/lib/stuffbox/popup-response.test.ts | 2 +- src/lib/stuffbox/popup-response.ts | 2 +- 3 files changed, 24 insertions(+), 3 deletions(-) diff --git a/src/components/StorageConfigurationPrompt.tsx b/src/components/StorageConfigurationPrompt.tsx index b7e99b1..cf6eeb3 100644 --- a/src/components/StorageConfigurationPrompt.tsx +++ b/src/components/StorageConfigurationPrompt.tsx @@ -2,6 +2,7 @@ import { useEffect, useState } from 'react'; import { Box, ChevronDown, ExternalLink } from 'lucide-react'; +import { getStorageProvider } from '@/lib/stuffbox/browser-upload'; interface StorageConfigurationPromptProps { open: boolean; @@ -61,14 +62,34 @@ export function StorageConfigurationPrompt({ open, onConfigured, onCancel, varia if (!response.ok || !data.authorizationUrl) throw new Error(data.error || 'Unable to connect Stuffbox'); const result = new Promise((resolve, reject) => { let settled = false; + let verifyingClosedPopup = false; let channel: BroadcastChannel | null = null; const timeout = window.setTimeout(() => { finish({ type: 'synapsis:stuffbox', success: false, message: 'Stuffbox connection timed out.' }); }, 10 * 60_000); const popupClosed = window.setInterval(() => { - if (popup.closed) finish({ type: 'synapsis:stuffbox', success: false, message: 'The Stuffbox window was closed before connecting.' }); + if (popup.closed) void verifyConnectionAfterPopupClosed(); }, 500); + async function verifyConnectionAfterPopupClosed() { + if (settled || verifyingClosedPopup) return; + verifyingClosedPopup = true; + window.clearInterval(popupClosed); + + for (let attempt = 0; attempt < 4 && !settled; attempt += 1) { + await new Promise(resolveDelay => window.setTimeout(resolveDelay, 250)); + if (settled) return; + try { + if (await getStorageProvider() === 'stuffbox') { + finish({ type: 'synapsis:stuffbox', success: true, message: 'Stuffbox connected.' }); + return; + } + } catch { /* Retry while the callback finishes saving the connection. */ } + } + + finish({ type: 'synapsis:stuffbox', success: false, message: 'The Stuffbox window was closed before connecting.' }); + } + function cleanup() { window.clearTimeout(timeout); window.clearInterval(popupClosed); diff --git a/src/lib/stuffbox/popup-response.test.ts b/src/lib/stuffbox/popup-response.test.ts index 7e83a97..1ad32a7 100644 --- a/src/lib/stuffbox/popup-response.test.ts +++ b/src/lib/stuffbox/popup-response.test.ts @@ -7,7 +7,7 @@ describe('renderStuffboxPopupResponse', () => { expect(html).toContain("new BroadcastChannel('synapsis:stuffbox')"); expect(html).toContain("localStorage.setItem('synapsis:stuffbox:result'"); - expect(html).toContain('window.close()'); + expect(html).toContain('window.setTimeout(() => window.close(), 250)'); expect(html).not.toContain('window.location.replace'); }); }); diff --git a/src/lib/stuffbox/popup-response.ts b/src/lib/stuffbox/popup-response.ts index a285409..495c37d 100644 --- a/src/lib/stuffbox/popup-response.ts +++ b/src/lib/stuffbox/popup-response.ts @@ -18,7 +18,7 @@ export function renderStuffboxPopupResponse(origin: string, success: boolean, me try { localStorage.setItem('synapsis:stuffbox:result', JSON.stringify({ ...payload, sentAt: Date.now() })); } catch {} - window.setTimeout(() => window.close(), 75); + window.setTimeout(() => window.close(), 250); `; }