fix(launcher): use $derived.by for session liveQueries to fix stale presentation/presenter data
When switching sessions within the same location, presentations and presenters
were not updating. The root cause: plain $derived(liveQuery(...)) never recreates
the Observable when slct__event_session_id changes, because liveQuery's async
callback runs in Dexie's zone where Svelte tracking is off. Dexie's range-level
change detection then ignores new session data (it arrives under a different
event_session_id index value, outside the originally observed range).
Replaced all four liveQuery declarations with $derived.by(() => { const id = ...;
return liveQuery(...id...); }) — the same pattern already used in +layout.svelte
for location-dependent queries. Svelte tracks the id read in the outer closure
and recreates the Observable on every session change.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -50,10 +50,17 @@ import {
|
||||
Users
|
||||
} from '@lucide/svelte';
|
||||
// Event Session (Main View Trigger)
|
||||
// WHY: We use a simple derived observable. The template handles the $ prefix.
|
||||
let lq__event_session_obj = $derived(
|
||||
liveQuery(() => db_events.session.get(slct__event_session_id))
|
||||
);
|
||||
// WHY: $derived.by captures the id in the outer closure so Svelte tracks it as a
|
||||
// reactive dependency and recreates the Observable when slct__event_session_id changes.
|
||||
// Plain $derived(liveQuery(...)) does NOT work here: liveQuery's async callback runs in
|
||||
// Dexie's zone where Svelte tracking is off, so $derived never sees the id read and never
|
||||
// recreates the Observable when the session changes. Dexie's range-level change tracking
|
||||
// then keeps the stale Observable alive — it only re-fires when data in the originally
|
||||
// observed key range changes, not when a different session's data arrives.
|
||||
let lq__event_session_obj = $derived.by(() => {
|
||||
const id = slct__event_session_id;
|
||||
return liveQuery(() => db_events.session.get(id));
|
||||
});
|
||||
|
||||
// WHY: type_code drives poster vs. oral UI branching throughout this component.
|
||||
// It was previously a prop that was never passed by the parent, so all poster
|
||||
@@ -62,65 +69,63 @@ let lq__event_session_obj = $derived(
|
||||
let type_code = $derived($lq__event_session_obj?.type_code ?? '');
|
||||
|
||||
// Event File (for a Session)
|
||||
// WHY: Pure data retrieval. Side effects (updating global stores) are removed
|
||||
// to prevent circular reactivity loops during rapid navigation.
|
||||
let lq__event_file_obj_li = $derived(
|
||||
liveQuery(async () => {
|
||||
if (!slct__event_session_id) return [];
|
||||
// WHY: $derived.by — same reason as lq__event_session_obj above. Without recreating
|
||||
// the Observable when slct__event_session_id changes, Dexie never re-fires for the new
|
||||
// session's files (they land in a different for_id range than what was originally observed).
|
||||
let lq__event_file_obj_li = $derived.by(() => {
|
||||
const id = slct__event_session_id;
|
||||
return liveQuery(async () => {
|
||||
if (!id) return [];
|
||||
|
||||
if (log_lvl > 1) {
|
||||
console.log(
|
||||
`[LQ] Fetching files for session: ${slct__event_session_id}`
|
||||
);
|
||||
console.log(`[LQ] Fetching files for session: ${id}`);
|
||||
}
|
||||
|
||||
return await db_events.file
|
||||
.where('for_id')
|
||||
.equals(slct__event_session_id)
|
||||
.equals(id)
|
||||
.reverse()
|
||||
.sortBy('created_on');
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
// Event Presentation
|
||||
let lq__event_presentation_obj_li = $derived(
|
||||
liveQuery(async () => {
|
||||
if (!slct__event_session_id) return [];
|
||||
// WHY: $derived.by — same reason as above. Captures both id and sort_by in the outer
|
||||
// closure so a new Observable is created whenever the session or its type changes.
|
||||
let lq__event_presentation_obj_li = $derived.by(() => {
|
||||
const id = slct__event_session_id;
|
||||
const sort_by = type_code == 'poster' ? 'name' : 'start_datetime';
|
||||
return liveQuery(async () => {
|
||||
if (!id) return [];
|
||||
|
||||
if (log_lvl > 1) {
|
||||
console.log(
|
||||
`[LQ] Fetching presentations for session: ${slct__event_session_id}`
|
||||
);
|
||||
console.log(`[LQ] Fetching presentations for session: ${id}`);
|
||||
}
|
||||
|
||||
let sort_by = 'start_datetime';
|
||||
if (type_code == 'poster') {
|
||||
sort_by = 'name';
|
||||
}
|
||||
return await db_events.presentation
|
||||
.where('event_session_id')
|
||||
.equals(slct__event_session_id)
|
||||
.equals(id)
|
||||
.sortBy(sort_by);
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
// Event Presenter
|
||||
let lq__event_presenter_obj_li = $derived(
|
||||
liveQuery(async () => {
|
||||
if (!slct__event_session_id) return [];
|
||||
// WHY: $derived.by — same reason as above.
|
||||
let lq__event_presenter_obj_li = $derived.by(() => {
|
||||
const id = slct__event_session_id;
|
||||
return liveQuery(async () => {
|
||||
if (!id) return [];
|
||||
|
||||
if (log_lvl > 1) {
|
||||
console.log(
|
||||
`[LQ] Fetching presenters for session: ${slct__event_session_id}`
|
||||
);
|
||||
console.log(`[LQ] Fetching presenters for session: ${id}`);
|
||||
}
|
||||
|
||||
return await db_events.presenter
|
||||
.where('event_session_id')
|
||||
.equals(slct__event_session_id)
|
||||
.equals(id)
|
||||
.sortBy('full_name');
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
// let show_modal_upload_files: boolean = false;
|
||||
// let link_to_type: null|string = null;
|
||||
|
||||
Reference in New Issue
Block a user