launcher: fix liveQuery reactivity for location-dependent queries

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.
This commit is contained in:
Scott Idem
2026-03-06 21:51:31 -05:00
parent 589ab9b652
commit 3447c4d4a4

View File

@@ -181,20 +181,28 @@
}); });
// Event File - For Location // 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; const id = $events_slct.event_location_id;
if (!id) return []; return liveQuery(async () => {
return await db_events.file if (!id) return [];
.where('for_id') return await db_events.file
.equals(id) .where('for_id')
.sortBy('filename'); .equals(id)
.sortBy('filename');
});
}); });
// Event Location // Event Location — same reason as above
let lq__event_location_obj = liveQuery(async () => { let lq__event_location_obj = $derived.by(() => {
const id = $events_slct.event_location_id; const id = $events_slct.event_location_id;
if (!id) return null; return liveQuery(async () => {
return await db_events.location.get(id); if (!id) return null;
return await db_events.location.get(id);
});
}); });
let lq__event_location_obj_li = liveQuery(async () => { let lq__event_location_obj_li = liveQuery(async () => {
@@ -206,27 +214,30 @@
.sortBy('name'); .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; const id = $events_slct.event_location_id;
if (!id) return []; return liveQuery(async () => {
if (log_lvl > 1) if (!id) return [];
console.log( if (log_lvl > 1)
`LQ - Using default sort for Event Session list location_id: ${id}` console.log(
); `LQ - Event Session list location_id: ${id}`
let results = await db_events.session );
.where('event_location_id') // Note: .reverse() before .sortBy() is a no-op — sortBy always re-sorts.
.equals(id) let results = await db_events.session
.reverse() .where('event_location_id')
.sortBy('name'); .equals(id)
.sortBy('name');
if ( if (
$events_slct.event_session_obj_li && $events_slct.event_session_obj_li &&
JSON.stringify($events_slct.event_session_obj_li) !== JSON.stringify($events_slct.event_session_obj_li) !==
JSON.stringify(results) JSON.stringify(results)
) { ) {
$events_slct.event_session_obj_li = [...(results || [])]; $events_slct.event_session_obj_li = [...(results || [])];
} }
return results; return results;
});
}); });
// Event Session (Main View Trigger - Needed for Global Header/Idle) // Event Session (Main View Trigger - Needed for Global Header/Idle)