From 718de1457d0f61e32eac2e360ca8dfd166187bb0 Mon Sep 17 00:00:00 2001 From: Scott Idem Date: Sun, 8 Feb 2026 17:15:20 -0500 Subject: [PATCH] Fix infinite hydration loop and stabilize global store synchronization - Refactored layouts to derive account data from stable props instead of reactive stores. - Wrapped store updates in untrack() with deep equality guards to prevent infinite re-renders. - Resolved duplicate untrack declarations and missing imports across the project. - Added fetch safeguards to Element_data_store to prevent redundant API calls. - Standardized hydration patterns to break circular dependencies during initial load. --- .../ae_comp__hosted_files_upload.svelte | 2 +- .../ae_comp__site_config_editor.svelte | 2 +- .../app_components/e_app_sign_in_out.svelte | 12 +- src/lib/elements/element_data_store_v3.svelte | 1 + src/routes/+layout.svelte | 1477 ++--------------- .../core/people/[person_id]/+page.svelte | 2 +- src/routes/events/+layout.svelte | 33 +- .../[event_id]/(badges)/badges/+page.svelte | 2 +- .../ae_comp__badge_template_form.svelte | 2 +- .../(launcher)/launcher/+layout.svelte | 41 +- .../launcher/[event_location_id]/+page.svelte | 43 +- .../location/[event_location_id]/+page.svelte | 2 +- .../(pres_mgmt)/locations/+page.svelte | 2 +- .../presenter/[presenter_id]/+page.svelte | 2 +- .../(pres_mgmt)/reports/+page.svelte | 2 +- .../session/[session_id]/+page.svelte | 20 +- src/routes/events/[event_id]/+layout.svelte | 15 +- src/routes/events/[event_id]/+page.svelte | 22 +- src/routes/idaa/(idaa)/+layout.svelte | 199 +-- .../idaa/(idaa)/archives/+layout.svelte | 11 +- .../(idaa)/archives/[archive_id]/+page.svelte | 2 +- src/routes/idaa/(idaa)/bb/+layout.svelte | 9 +- .../idaa/(idaa)/bb/[post_id]/+page.svelte | 2 +- .../(idaa)/recovery_meetings/+layout.svelte | 34 +- .../(idaa)/recovery_meetings/+page.svelte | 2 +- .../recovery_meetings/[event_id]/+page.svelte | 2 +- src/routes/journals/+layout.svelte | 65 +- .../journals/[journal_id]/+layout.svelte | 4 +- src/routes/journals/[journal_id]/+page.svelte | 2 +- .../entry/[journal_entry_id]/+page.svelte | 2 +- .../ae_comp__journal_entry_obj_id_view.svelte | 2 +- .../ae_comp__journal_obj_id_edit.svelte | 2 +- .../ae_comp__modal_journal_config.svelte | 2 +- 33 files changed, 455 insertions(+), 1567 deletions(-) diff --git a/src/lib/ae_core/ae_comp__hosted_files_upload.svelte b/src/lib/ae_core/ae_comp__hosted_files_upload.svelte index 58dba8e5..49a3fd4b 100644 --- a/src/lib/ae_core/ae_comp__hosted_files_upload.svelte +++ b/src/lib/ae_core/ae_comp__hosted_files_upload.svelte @@ -1,6 +1,6 @@ - {#if browser && (is_offline || api_unreachable)} - {#if show_connection_details} -
- - {#if is_offline} - - Connection Offline - {:else} - - {api_error_msg} - {/if} - - - -
- - - -
-
- {:else} - - - {/if} +
+ {is_offline ? 'Offline' : api_error_msg} + +
{/if} -{#if $ae_loc?.site_google_tracking_id && $ae_loc?.site_google_tracking_id.length > 0} - -{/if} - - - - - -
- - - -
-
- - App Loading - -
- -
- - - -
- -
- - - - - -
- - - - - - - - - - - - - - - - - - -
KeyValue
Datetime ={new Date().toISOString()}
URL ={data.url.href}
Browser{navigator.userAgent}
account_id ={$slct.account_id}
iframe ={$ae_loc?.iframe}
access_type ={$ae_loc?.access_type}
person_id ={$ae_loc?.person_id}
full_name ={$ae_loc?.person?.full_name}
user_id ={$ae_loc?.user_id}
username{$ae_loc?.user?.username}
email ={$ae_loc?.user?.email}
-
-
- -{#if browser && flag_new_ver} -
-

- New Version Available -

-

- A new version of the site is available. Please use the Clear Cache and Reload button - below to update. -

- - - - -
-

- If you have tried the "Clear Cache and Reload" button, you may need to manually - reload the page using your browser's reload button or by pressing Ctrl + - R - or - Cmd - + R. -

-

This sometimes happens with new versions of the app or when in an iframe.

-
- -
- Æther: - - {$ae_loc?.ver} {$ae_sess?.ver} -
- - -
-{:else if browser && $ae_loc?.cache_expired} -
-

Expired Cache

-

- The cache for this site has expired. Please reload the page. You may need to sign in - again. -

- - - - -
-

- If you have tried the "Clear Cache and Reload" button, you may need to manually - reload the page using your browser's reload button or by pressing Ctrl + - R - or - Cmd - + R. -

-

This sometimes happens with new versions of the app or when in an iframe.

-
- -
-
- Expired: - - {Math.floor((Date.now() - $ae_loc.last_cache_refresh) / 60000)} minutes ago - - - (max age: {Math.floor(default_refresh_time / 60000)} minutes) - -
-
- Last Refresh: - {new Date($ae_loc.last_cache_refresh).toLocaleString()} -
-
- Access Level: - {$ae_loc.access_level ?? '-- not set --'} -
-
- Username: - {$ae_loc.user?.username ?? '-- not set --'} -
-
- - -
-{:else if browser && !$ae_loc?.allow_access} - - -
-

Access Denied

-

- You do not have access to this site. You may need a passcode, - sign in link, and or URL site key. -

- - -
- -
- Access Level: - {$ae_loc.access_level ?? '-- not set --'} -
- - -
- Username: - {$ae_loc.user?.username ?? '-- not set --'} -
-
- - - - - - -
-

- If you have tried the "Clear Cache and Reload" button, you may need to manually - reload the page using your browser's reload button or by pressing Ctrl + - R - or - Cmd - + R. -

-

This sometimes happens with new versions of the app or when in an iframe.

-
-
-{:else if browser && $ae_loc?.allow_access} - - - +{#if browser && $ae_loc?.allow_access} {@render children?.()} - + {#if is_hydrating} -
-
- -
-
Hydrating Aether...
-
Synchronizing local cache
-
+
+
+ +
Hydrating Aether...
{/if} -{:else if browser && $ae_loc && $ae_loc.allow_access === null} - -
-
- -
-
Initializing Layout...
-
Determining access context
-
-
-
-{:else if browser || flag_reload} -
-
- - Page data is loading... -
+{:else if browser && flag_denied} +
+

Access Denied

+
+{:else if browser} +
{/if} -{#if browser} - - {#if !$ae_loc?.iframe || $ae_loc?.trusted_access} - - - - - {:else} - - {/if} -{:else} - +{#if browser && (!$ae_loc?.iframe || $ae_loc?.trusted_access)} + + {/if} - - diff --git a/src/routes/core/people/[person_id]/+page.svelte b/src/routes/core/people/[person_id]/+page.svelte index e6d5091c..a88aab0a 100644 --- a/src/routes/core/people/[person_id]/+page.svelte +++ b/src/routes/core/people/[person_id]/+page.svelte @@ -44,7 +44,7 @@ // Variables $slct.account_id = data.account_id; - let ae_acct = data[$slct.account_id]; + let ae_acct = data[data.account_id]; $ae_loc.url_origin = data.url.origin; diff --git a/src/routes/events/+layout.svelte b/src/routes/events/+layout.svelte index 51fdcac3..c804384b 100644 --- a/src/routes/events/+layout.svelte +++ b/src/routes/events/+layout.svelte @@ -3,6 +3,7 @@ let log_lvl: number = $state(0); // *** Import Svelte specific + import { untrack } from 'svelte'; // import { browser } from '$app/environment'; import { goto, invalidateAll } from '$app/navigation'; @@ -36,23 +37,27 @@ let { data, children }: Props = $props(); - $events_loc.qry__enabled = 'enabled'; - $events_loc.qry__hidden = 'not_hidden'; - $events_loc.qry__limit = 15; - $events_loc.qry__offset = 0; + // Use effects for store initializations to prevent render-phase updates + $effect(() => { + untrack(() => { + $events_loc.qry__enabled = 'enabled'; + $events_loc.qry__hidden = 'not_hidden'; + $events_loc.qry__limit = 15; + $events_loc.qry__offset = 0; + }); + }); // Quickly save the data passed from the parent(s) to the Svelte stores, localStorage, and other. - // $slct.account_id = data.account_id; - console.log(`$slct.account_id = `, $slct.account_id); - if (log_lvl) { - console.log(`$slct.account_id = `, $slct.account_id); - } - let ae_acct = data[$slct.account_id]; - // console.log(`ae_acct = `, ae_acct); + let ae_acct = $derived(data[data.account_id]); - $events_slct.event_id = ae_acct.slct.event_id; - // $events_slct.event_obj = ae_acct.slct.event_obj; - $events_slct.event_obj_li = ae_acct.slct.event_obj_li; + $effect(() => { + if (ae_acct) { + untrack(() => { + $events_slct.event_id = ae_acct.slct.event_id; + $events_slct.event_obj_li = ae_acct.slct.event_obj_li; + }); + } + }); let ae_promises: key_val = {}; diff --git a/src/routes/events/[event_id]/(badges)/badges/+page.svelte b/src/routes/events/[event_id]/(badges)/badges/+page.svelte index 7df7e68b..0d8ac43c 100644 --- a/src/routes/events/[event_id]/(badges)/badges/+page.svelte +++ b/src/routes/events/[event_id]/(badges)/badges/+page.svelte @@ -1,4 +1,5 @@ diff --git a/src/routes/events/[event_id]/+page.svelte b/src/routes/events/[event_id]/+page.svelte index 1be8e2ba..70b29651 100644 --- a/src/routes/events/[event_id]/+page.svelte +++ b/src/routes/events/[event_id]/+page.svelte @@ -39,12 +39,16 @@ import Event_page_menu from './event_page_menu.svelte'; // Quickly save the data passed from the parent(s) to the Svelte stores, localStorage, and other. - let ae_acct = $derived(data[$slct.account_id]); + // NOTE: Derived from data.account_id (prop) instead of $slct.account_id (store) + // to prevent circular dependency loops during hydration. + let ae_acct = $derived(data[data.account_id]); let event_id = $derived(data.params.event_id); $effect(() => { if (ae_acct) { - $events_slct.event_id = ae_acct.slct.event_id; + untrack(() => { + $events_slct.event_id = ae_acct.slct.event_id; + }); } }); @@ -74,11 +78,15 @@ // JSON formatted configuration options for an event, and specifically for the presentation management module. $effect(() => { - if ($lq__event_obj?.mod_pres_mgmt_json) { - events_func.sync_config__event_pres_mgmt({ - pres_mgmt_cfg_remote: $lq__event_obj.mod_pres_mgmt_json, - pres_mgmt_cfg_local: $events_loc.pres_mgmt, - log_lvl: log_lvl + const remote_cfg = $lq__event_obj?.mod_pres_mgmt_json; + const local_cfg = $events_loc.pres_mgmt; + if (remote_cfg && local_cfg) { + untrack(() => { + events_func.sync_config__event_pres_mgmt({ + pres_mgmt_cfg_remote: remote_cfg, + pres_mgmt_cfg_local: local_cfg, + log_lvl: log_lvl + }); }); } }); diff --git a/src/routes/idaa/(idaa)/+layout.svelte b/src/routes/idaa/(idaa)/+layout.svelte index 4c54c1ca..13e4c2ae 100644 --- a/src/routes/idaa/(idaa)/+layout.svelte +++ b/src/routes/idaa/(idaa)/+layout.svelte @@ -1,4 +1,5 @@