From 3447c4d4a43267addb369cc56817a7440731f280 Mon Sep 17 00:00:00 2001 From: Scott Idem Date: Fri, 6 Mar 2026 21:51:31 -0500 Subject: [PATCH] launcher: fix liveQuery reactivity for location-dependent queries MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The three liveQueries that depend on $events_slct.event_location_id were plain liveQuery() calls, not $derived.by(() => liveQuery(...)). This meant Svelte store changes did NOT cause them to re-run. Root cause of the 'hung' bug: - On initial load at /launcher (no location in URL), id = null - Dexie watches the event_location_id = null index range - User selects a location (or navigates to /launcher/{id}): store updates - Sessions for the real location are in a DIFFERENT Dexie range - Dexie never fires because the null range was never touched - If sessions are already in IndexedDB cache (no new DB write), the list stays permanently frozen at [] Fix: convert lq__event_session_obj_li, lq__location_event_file_obj_li, and lq__event_location_obj to $derived.by(() => liveQuery(...)). When event_location_id changes, $derived.by creates a new Observable targeting the correct location, which immediately queries Dexie for existing cached data and then watches that range for further changes. Also: remove the .reverse() before .sortBy('name') on the session query — .sortBy() always re-sorts so .reverse() before it was a no-op. --- .../(launcher)/launcher/+layout.svelte | 69 +++++++++++-------- 1 file changed, 40 insertions(+), 29 deletions(-) diff --git a/src/routes/events/[event_id]/(launcher)/launcher/+layout.svelte b/src/routes/events/[event_id]/(launcher)/launcher/+layout.svelte index e6563b62..d1c6714f 100644 --- a/src/routes/events/[event_id]/(launcher)/launcher/+layout.svelte +++ b/src/routes/events/[event_id]/(launcher)/launcher/+layout.svelte @@ -181,20 +181,28 @@ }); // Event File - For Location - let lq__location_event_file_obj_li = liveQuery(async () => { + // $derived.by: must recreate the observable when event_location_id changes. + // Plain liveQuery() only re-fires when Dexie detects a change in the initially + // watched index range — if the id starts as null and changes to a real value, + // Dexie never fires because a different range was added (not the null range). + let lq__location_event_file_obj_li = $derived.by(() => { const id = $events_slct.event_location_id; - if (!id) return []; - return await db_events.file - .where('for_id') - .equals(id) - .sortBy('filename'); + return liveQuery(async () => { + if (!id) return []; + return await db_events.file + .where('for_id') + .equals(id) + .sortBy('filename'); + }); }); - // Event Location - let lq__event_location_obj = liveQuery(async () => { + // Event Location — same reason as above + let lq__event_location_obj = $derived.by(() => { const id = $events_slct.event_location_id; - if (!id) return null; - return await db_events.location.get(id); + return liveQuery(async () => { + if (!id) return null; + return await db_events.location.get(id); + }); }); let lq__event_location_obj_li = liveQuery(async () => { @@ -206,27 +214,30 @@ .sortBy('name'); }); - let lq__event_session_obj_li = liveQuery(async () => { + // $derived.by: must recreate when event_location_id changes (see comment above). + let lq__event_session_obj_li = $derived.by(() => { const id = $events_slct.event_location_id; - if (!id) return []; - if (log_lvl > 1) - console.log( - `LQ - Using default sort for Event Session list location_id: ${id}` - ); - let results = await db_events.session - .where('event_location_id') - .equals(id) - .reverse() - .sortBy('name'); + return liveQuery(async () => { + if (!id) return []; + if (log_lvl > 1) + console.log( + `LQ - Event Session list location_id: ${id}` + ); + // Note: .reverse() before .sortBy() is a no-op — sortBy always re-sorts. + let results = await db_events.session + .where('event_location_id') + .equals(id) + .sortBy('name'); - if ( - $events_slct.event_session_obj_li && - JSON.stringify($events_slct.event_session_obj_li) !== - JSON.stringify(results) - ) { - $events_slct.event_session_obj_li = [...(results || [])]; - } - return results; + if ( + $events_slct.event_session_obj_li && + JSON.stringify($events_slct.event_session_obj_li) !== + JSON.stringify(results) + ) { + $events_slct.event_session_obj_li = [...(results || [])]; + } + return results; + }); }); // Event Session (Main View Trigger - Needed for Global Header/Idle)