diff --git a/src/routes/idaa/(idaa)/+layout.svelte b/src/routes/idaa/(idaa)/+layout.svelte index 78a7b161..c83f47ff 100644 --- a/src/routes/idaa/(idaa)/+layout.svelte +++ b/src/routes/idaa/(idaa)/+layout.svelte @@ -63,6 +63,14 @@ let verify_in_flight = false; // UUID must be verified fresh. A plain boolean would wrongly block verification of the new UUID. // Storing the failed UUID means only that exact UUID is skipped; any other UUID is a clean slate. let verify_failed_for_uuid: string | null = null; +// Tracks the type of Novi API failure for the UI — 'rate_limited' or 'api_error'. +// A transient API error is NOT the same as a real membership denial; this lets us +// show a distinct "Verification Unavailable" state instead of "Access Denied". +let verify_error_type: 'rate_limited' | 'api_error' | null = $state(null); +// Shown inside the spinner — updated during rate-limit retry waits. +let verifying_status_msg: string = $state('Verifying identity...'); +// Incremented by handle_verify_retry() to re-run Effect 2 without a full page reload. +let retry_count: number = $state(0); // Clear stale db_events.event IDB data on IDAA session start. // @@ -140,6 +148,7 @@ $effect(() => { // Read url_uuid here (outside untrack) to create a reactive dependency. // Effect 2 re-runs whenever the UUID in the URL changes. const current_uuid = url_uuid; + void retry_count; // Reactive dep — re-run Effect 2 when user clicks "Try Again". untrack(() => { if (!current_uuid) { @@ -268,6 +277,8 @@ async function verify_novi_uuid( verify_in_flight = false; return; } + verifying_status_msg = 'Verifying identity...'; + verify_error_type = null; console.log(`IDAA Layout: Starting Novi UUID verification for ${uuid}...`); try { @@ -280,9 +291,11 @@ async function verify_novi_uuid( if (response.status === 429) { if (is_retry) { + verify_error_type = 'rate_limited'; throw new Error(`Novi API rate limited for UUID ${uuid} (retry also failed)`); } console.warn(`IDAA Layout: Novi API rate limited (429) for ${uuid}. Retrying in 10s...`); + verifying_status_msg = 'High traffic — retrying in 10 seconds...'; await new Promise((resolve) => setTimeout(resolve, 10_000)); await verify_novi_uuid(uuid, api_key, api_root_url, true); return; @@ -293,6 +306,10 @@ async function verify_novi_uuid( } const result = await response.json(); + log_lvl = 2; + if (log_lvl > 1) { + console.log(`IDAA Layout: Novi API response for ${uuid}:`, result); + } // Build display name: prefer "First L." format, fall back to full Name field. const first_name = result?.FirstName ?? null; @@ -359,6 +376,8 @@ async function verify_novi_uuid( error ); verify_failed_for_uuid = uuid; // Latch — stop retry loop for this UUID. See declaration comment. + // 'rate_limited' is set before throw for 429; everything else is an unexpected API error. + if (!verify_error_type) verify_error_type = 'api_error'; $idaa_loc.novi_uuid = null; $idaa_loc.novi_email = null; $idaa_loc.novi_full_name = null; @@ -375,6 +394,17 @@ async function verify_novi_uuid( novi_verifying = false; } } +/** + * Clears the verification failure latch and forces Effect 2 to re-run without a full page reload. + * Called by the "Try Again" button in the verification-error UI state. + */ +function handle_verify_retry() { + verify_error_type = null; + verify_failed_for_uuid = null; + verifying_timed_out = false; + novi_verifying = true; + retry_count++; +} {#if !browser} @@ -389,7 +419,7 @@ async function verify_novi_uuid( class="container m-8 flex w-full flex-col items-center justify-center gap-2 p-8">

- Verifying identity... + {verifying_status_msg}

{#if verifying_timed_out} +
+

+ + + Identity Verification Unavailable + +

+ {#if verify_error_type === 'rate_limited'} +

+ The membership directory is temporarily busy (rate limited). Please wait a moment and try again. +

+ {:else} +

+ We were unable to connect to the membership directory. This is likely a temporary issue — it may not be a problem with your membership or access. +

+ {/if} +

+ If this keeps happening, try "Clear Cache & Reload" or contact technical support. +

+
+ + + + + + +
+
{:else if $ae_loc.trusted_access || ($ae_loc.authenticated_access && $idaa_loc.novi_uuid && $idaa_loc.novi_verified)}