From 53fd5e7de44f794221f87252378fbd69710c7581 Mon Sep 17 00:00:00 2001 From: Scott Idem Date: Tue, 19 May 2026 16:04:28 -0400 Subject: [PATCH] fix(idaa): increase spinner timeout to 35s, guard sessionStorage with try-catch MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit VERIFY_TIMEOUT_MS 8s → 35s: worst-case auto-retry cycle is 27s (12s abort + 3s wait + 12s abort). At 8s the "Reset & Retry" banner fired while the second retry was still in flight; members who clicked it cleared their stores and reloaded mid-attempt, landing on Access Denied. At 35s the escape hatch only appears if verification is genuinely stuck (slow Novi server or missing api_key). sessionStorage try-catch: iOS Safari Private Browsing and certain iframe sandbox configs throw on sessionStorage access. Wrap setItem (onMount) and getItem (reload_with_uuid) in try-catch so the component degrades gracefully to location.reload() rather than crashing silently. Co-Authored-By: Claude Sonnet 4.6 --- src/routes/idaa/(idaa)/+layout.svelte | 37 +++++++++++++++++++-------- 1 file changed, 27 insertions(+), 10 deletions(-) diff --git a/src/routes/idaa/(idaa)/+layout.svelte b/src/routes/idaa/(idaa)/+layout.svelte index 198c92e3..84048597 100644 --- a/src/routes/idaa/(idaa)/+layout.svelte +++ b/src/routes/idaa/(idaa)/+layout.svelte @@ -83,18 +83,29 @@ const IDAA_IFRAME_RELOAD_URL_KEY = 'idaa_iframe_reload_url'; onMount(() => { const uuid_in_url = new URLSearchParams(window.location.search).get('uuid'); - if (uuid_in_url && !sessionStorage.getItem(IDAA_IFRAME_RELOAD_URL_KEY)) { - sessionStorage.setItem(IDAA_IFRAME_RELOAD_URL_KEY, window.location.href); + if (uuid_in_url) { + // Guard: iOS Safari Private Browsing and some iframe sandbox configs throw on + // sessionStorage access. Graceful fallback: skip the save; reload_with_uuid() + // will fall back to location.reload() (loses UUID-preservation but doesn't crash). + try { + if (!sessionStorage.getItem(IDAA_IFRAME_RELOAD_URL_KEY)) { + sessionStorage.setItem(IDAA_IFRAME_RELOAD_URL_KEY, window.location.href); + } + } catch { + console.warn('IDAA Layout: sessionStorage unavailable — reload buttons will use location.reload() fallback.'); + } } }); function reload_with_uuid() { - const initial_url = sessionStorage.getItem(IDAA_IFRAME_RELOAD_URL_KEY); - if (initial_url && initial_url !== location.href) { - location.href = initial_url; - } else { - location.reload(); - } + try { + const initial_url = sessionStorage.getItem(IDAA_IFRAME_RELOAD_URL_KEY); + if (initial_url && initial_url !== location.href) { + location.href = initial_url; + return; + } + } catch { /* sessionStorage unavailable — fall through to location.reload() */ } + location.reload(); } // Clear stale db_events.event IDB data on IDAA session start. @@ -116,7 +127,13 @@ if (browser) { // Show a manual reset button if the spinner is still visible after this many ms. // Handles the case where site_cfg_json loads without novi_idaa_api_key (stale cache) // or the Novi API call hangs — the user would otherwise be stuck with no escape. -const VERIFY_TIMEOUT_MS = 8000; +// +// WHY 35s: worst-case auto-retry cycle is 27s (12s first timeout + 3s wait + 12s retry). +// If the auto-retries succeed or fail within that window, the spinner is already gone +// (content shown or error panel shown) before this fires. The escape hatch only appears +// for the rare case where the Novi API is slow-but-not-timing-out, or site_cfg_json +// never loads. Previously 8s — fired mid-flight and caused premature Reset & Retry clicks. +const VERIFY_TIMEOUT_MS = 35_000; let verifying_timed_out: boolean = $state(false); @@ -131,7 +148,7 @@ $effect(() => { } }); -const VERIFIED_TTL_MS_DEFAULT = 45 * 60 * 1000; // 25 minutes +const VERIFIED_TTL_MS_DEFAULT = 45 * 60 * 1000; // 45 minutes // Effect 1: Set URL origin and params $effect(() => {