From cb46eec4405a10f0f9af20d06380886e4e34aedf Mon Sep 17 00:00:00 2001 From: Scott Idem Date: Fri, 30 Jan 2026 16:26:38 -0500 Subject: [PATCH] perf(launcher): minimize session switch latency with non-blocking SWR and stable observables --- src/lib/ae_events/ae_events__event_session.ts | 37 +- .../(launcher)/launcher/+layout.svelte | 417 +++++------------- .../(launcher)/menu_session_list.svelte | 17 +- 3 files changed, 141 insertions(+), 330 deletions(-) diff --git a/src/lib/ae_events/ae_events__event_session.ts b/src/lib/ae_events/ae_events__event_session.ts index 2b5ecbcb..46f117c7 100644 --- a/src/lib/ae_events/ae_events__event_session.ts +++ b/src/lib/ae_events/ae_events__event_session.ts @@ -10,7 +10,7 @@ import { load_ae_obj_li__event_presentation } from '$lib/ae_events/ae_events__ev const ae_promises: key_val = {}; -// Updated 2026-01-30: Trace-Ready SWR Pattern +// Updated 2026-01-30: Trace-Ready SWR Pattern with Performance Timing export async function load_ae_obj_id__event_session({ api_cfg, event_session_id, @@ -40,6 +40,7 @@ export async function load_ae_obj_id__event_session({ try_cache?: boolean; log_lvl?: number; }): Promise { + const start_time = performance.now(); if (log_lvl) { console.log(`🔎 [Trace] load_ae_obj_id__event_session: START (id=${event_session_id}, try_cache=${try_cache})`); } @@ -49,18 +50,22 @@ export async function load_ae_obj_id__event_session({ try { const cached = await db_events.session.get(event_session_id); if (cached) { - if (log_lvl) console.log(`✅ [Trace] load_ae_obj_id: CACHE HIT. Returning stale data for id=${event_session_id}`); + const elapsed = (performance.now() - start_time).toFixed(2); + if (log_lvl) console.log(`✅ [Trace] load_ae_obj_id: CACHE HIT at ${elapsed}ms. Returning stale shell for id=${event_session_id}`); - // Background refresh & nested loads (non-blocking) + // Background tasks: refresh parent and warm child caches (non-blocking) _refresh_session_id_background({ api_cfg, event_session_id, view, try_cache, inc_file_li, inc_all_file_li, inc_presentation_li, inc_presenter_li, enabled, hidden, limit, offset, log_lvl: log_lvl > 1 ? log_lvl : 0 }); - return await _handle_nested_loads(cached, { api_cfg, inc_file_li, inc_all_file_li, inc_presentation_li, inc_presenter_li, enabled, hidden, limit, offset, try_cache, log_lvl: 0 }); + // In SWR mode, we fire child loads in background to warm IDB for the view's LiveQueries + _handle_nested_loads(cached, { api_cfg, inc_file_li, inc_all_file_li, inc_presentation_li, inc_presenter_li, enabled, hidden, limit, offset, try_cache, log_lvl: 0 }); + + return cached; // Return immediately without awaiting nested loads } else if (log_lvl) { - console.log(`⏳ [Trace] load_ae_obj_id: CACHE MISS for id=${event_session_id}`); + console.log(`⏳ [Trace] load_ae_obj_id: CACHE MISS at ${(performance.now() - start_time).toFixed(2)}ms for id=${event_session_id}`); } } catch (e) { if (log_lvl) console.error(`❌ [Trace] load_ae_obj_id: Cache access error:`, e); @@ -76,6 +81,7 @@ export async function load_ae_obj_id__event_session({ * Internal background refresh for a single session */ async function _refresh_session_id_background({ api_cfg, event_session_id, view, try_cache, inc_file_li, inc_all_file_li, inc_presentation_li, inc_presenter_li, enabled, hidden, limit, offset, log_lvl }: any) { + const start_time = performance.now(); if (typeof navigator !== 'undefined' && !navigator.onLine) return null; try { if (log_lvl) console.log(`📡 [Trace] _refresh_session_id: API Fetching id=${event_session_id}`); @@ -84,8 +90,9 @@ async function _refresh_session_id_background({ api_cfg, event_session_id, view, if (result) { const processed = await process_ae_obj__event_session_props({ obj_li: [result], log_lvl }); const processed_obj = processed[0]; + const elapsed = (performance.now() - start_time).toFixed(2); - if (log_lvl) console.log(`📦 [Trace] _refresh_session_id: Received from API (id=${processed_obj.id})`); + if (log_lvl) console.log(`📦 [Trace] _refresh_session_id: Received from API at ${elapsed}ms (id=${processed_obj.id})`); if (try_cache) { await db_save_ae_obj_li__ae_obj({ db_instance: db_events, table_name: 'session', obj_li: [processed_obj], properties_to_save, log_lvl }); @@ -103,6 +110,7 @@ async function _refresh_session_id_background({ api_cfg, event_session_id, view, * Helper to handle nested collection loads for a session */ async function _handle_nested_loads(session_obj: any, { api_cfg, inc_file_li, inc_all_file_li, inc_presentation_li, inc_presenter_li, enabled, hidden, limit, offset, try_cache, log_lvl }: any) { + const start_time = performance.now(); const current_session_id = session_obj.id || session_obj.event_session_id; if (!current_session_id) return session_obj; @@ -121,11 +129,14 @@ async function _handle_nested_loads(session_obj: any, { api_cfg, inc_file_li, in }).then(res => session_obj.event_presentation_li = res)); } - if (tasks.length > 0) await Promise.all(tasks); + if (tasks.length > 0) { + await Promise.all(tasks); + if (log_lvl) console.log(`🔗 [Trace] _handle_nested_loads: Finished child collections in ${(performance.now() - start_time).toFixed(2)}ms`); + } return session_obj; } -// Updated 2026-01-30: Robust Cache Lookups with Tracing +// Updated 2026-01-30: Robust Cache Lookups with Performance Tracing export async function load_ae_obj_li__event_session({ api_cfg, for_obj_type = 'event', @@ -165,6 +176,7 @@ export async function load_ae_obj_li__event_session({ try_cache?: boolean; log_lvl?: number; }): Promise { + const start_time = performance.now(); if (log_lvl) { console.log(`🔎 [Trace] load_ae_obj_li__event_session: START (for=${for_obj_type}:${for_obj_id}, try_cache=${try_cache})`); } @@ -180,14 +192,15 @@ export async function load_ae_obj_li__event_session({ const cached_li = await query.toArray(); if (cached_li && cached_li.length > 0) { - if (log_lvl) console.log(`✅ [Trace] load_ae_obj_li: CACHE HIT (${cached_li.length} items).`); + const elapsed = (performance.now() - start_time).toFixed(2); + if (log_lvl) console.log(`✅ [Trace] load_ae_obj_li: CACHE HIT at ${elapsed}ms (${cached_li.length} items).`); // Background refresh (non-blocking) _refresh_session_li_background({ api_cfg, for_obj_type, for_obj_id, inc_file_li, inc_all_file_li, inc_presentation_li, inc_presenter_li, enabled, hidden, limit, offset, order_by_li, try_cache, log_lvl: log_lvl > 1 ? log_lvl : 0 }); return cached_li; } else if (log_lvl) { - console.log(`⏳ [Trace] load_ae_obj_li: CACHE MISS for type=${for_obj_type} id=${for_obj_id}`); + console.log(`⏳ [Trace] load_ae_obj_li: CACHE MISS at ${(performance.now() - start_time).toFixed(2)}ms for type=${for_obj_type} id=${for_obj_id}`); } } catch (e) { if (log_lvl) console.error(`❌ [Trace] load_ae_obj_li: Cache access error:`, e); @@ -198,6 +211,7 @@ export async function load_ae_obj_li__event_session({ } async function _refresh_session_li_background({ api_cfg, for_obj_type, for_obj_id, inc_file_li, inc_all_file_li, inc_presentation_li, inc_presenter_li, enabled, hidden, limit, offset, order_by_li, try_cache, log_lvl }: any) { + const start_time = performance.now(); if (typeof navigator !== 'undefined' && !navigator.onLine) return []; try { if (log_lvl) console.log(`📡 [Trace] _refresh_session_li: API Fetching for=${for_obj_type}:${for_obj_id}`); @@ -205,7 +219,8 @@ async function _refresh_session_li_background({ api_cfg, for_obj_type, for_obj_i if (result_li) { const processed = await process_ae_obj__event_session_props({ obj_li: result_li, log_lvl }); - if (log_lvl) console.log(`📦 [Trace] _refresh_session_li: Received ${processed.length} items from API.`); + const elapsed = (performance.now() - start_time).toFixed(2); + if (log_lvl) console.log(`📦 [Trace] _refresh_session_li: Received ${processed.length} items from API at ${elapsed}ms.`); if (try_cache) { await db_save_ae_obj_li__ae_obj({ db_instance: db_events, table_name: 'session', obj_li: processed, properties_to_save, log_lvl }); diff --git a/src/routes/events/[event_id]/(launcher)/launcher/+layout.svelte b/src/routes/events/[event_id]/(launcher)/launcher/+layout.svelte index da4e75c4..78527e8c 100644 --- a/src/routes/events/[event_id]/(launcher)/launcher/+layout.svelte +++ b/src/routes/events/[event_id]/(launcher)/launcher/+layout.svelte @@ -56,50 +56,27 @@ import Element_websocket_v2 from '$lib/elements/element_websocket_v2.svelte'; // *** Set initial variables - // 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); let ae_acct = data[$slct.account_id]; - // console.log(`ae_acct = `, ae_acct); import { - // devicePixelRatio, - // innerHeight, - // innerWidth, online - // outerHeight, - // outerWidth, - // screenLeft, - // screenTop, - // scrollX, - // scrollY } from 'svelte/reactivity/window'; - // const online: ReactiveValue; - $ae_sess.disable_sys_nav = true; $ae_sess.disable_sys_header = true; $ae_sess.disable_sys_footer = true; - // This is a just in case check... if (!$events_loc?.launcher) { $events_loc.launcher = { app_mode: 'default', - controller: 'local', controller_group_code: 'launcher-00', ws_connect: false, - hide_drawer__cfg: true, hide_drawer__debug: true }; } - // svelte-ignore state_referenced_locally - if (log_lvl > 1) { - console.log(`$events_loc.launcher:`, $events_loc.launcher); - } - // svelte-ignore state_referenced_locally if (log_lvl) { console.log(`event_id: ${data.params.event_id}`); console.log(`event_location_id: ${data.params.event_location_id}`); @@ -115,164 +92,93 @@ $events_slct.event_device_id = native_dev.event_device_id || native_dev.id || native_dev.event_device_id_random || native_dev.id_random; } - // console.log(`ae_acct.slct.event_id:`, ae_acct.slct.event_id); - // $events_slct.event_id = ae_acct.slct.event_id; - // $events_slct.event_obj = ae_acct.slct.event_obj; $events_slct.event_location_obj_li = ae_acct.slct.event_location_obj_li ?? ['']; $events_slct.id_li__event_location = ae_acct.slct.id_li__event_location ?? ['']; - // svelte-ignore state_referenced_locally - if (log_lvl > 1) { - console.log(`$events_slct.event_location_obj_li:`, $events_slct.event_location_obj_li); - console.log(`$events_slct.id_li__event_location:`, $events_slct.id_li__event_location); - } // *** Functions and Logic // Event - let lq__event_obj = $derived( - liveQuery(async () => { - if (log_lvl > 1) { - console.log(`lq__event_obj: event_id = ${$events_slct?.event_id}`); + let lq__event_obj = liveQuery(async () => { + const id = $events_slct?.event_id; + if (!id) return null; + if (log_lvl > 1) console.log(`lq__event_obj: event_id = ${id}`); + let results = await db_events.event.get(id); + + if ($events_slct.event_obj && results) { + if (JSON.stringify($events_slct.event_obj) !== JSON.stringify(results)) { + $events_slct.event_obj = { ...results }; } - - let results = await db_events.event.get($events_slct?.event_id ?? ''); // null or undefined does not reset things like '' does - - // Check if results are different than the current session version stored under $events_slct - if ($events_slct.event_obj && results) { - if (JSON.stringify($events_slct.event_obj) !== JSON.stringify(results)) { - $events_slct.event_obj = { ...results }; - if (log_lvl) { - console.log( - `Session slct stored version has changed for ID = ${$events_slct.event_id}`, - $events_slct.event_obj - ); - } - } - } - - return results; - }) - ); + } + return results; + }); // Event Device - let lq__event_device_obj = $derived( - liveQuery(async () => { - let results = await db_events.device.get($events_slct.event_device_id); - - // Check if results are different than the current session version stored under $events_slct - if ($events_slct.event_device_obj && results) { - if (JSON.stringify($events_slct.event_device_obj) !== JSON.stringify(results)) { - $events_slct.event_device_obj = { ...results }; - } + let lq__event_device_obj = liveQuery(async () => { + const id = $events_slct.event_device_id; + if (!id) return null; + let results = await db_events.device.get(id); + if ($events_slct.event_device_obj && results) { + if (JSON.stringify($events_slct.event_device_obj) !== JSON.stringify(results)) { + $events_slct.event_device_obj = { ...results }; } - - return results; - }) - ); + } + return results; + }); // Event File - For Event - let lq__event_event_file_obj_li = $derived( - liveQuery(async () => { - let results = await db_events.file - .where('for_id') - .equals($events_slct.event_id ?? '') - .sortBy('filename'); - - return results; - }) - ); + let lq__event_event_file_obj_li = liveQuery(async () => { + const id = $events_slct.event_id; + if (!id) return []; + return await db_events.file.where('for_id').equals(id).sortBy('filename'); + }); // Event File - For Location - let lq__location_event_file_obj_li = $derived( - liveQuery(async () => { - let results = await db_events.file - // .where('event_location_id') - .where('for_id') - .equals($events_slct.event_location_id ?? '') - .sortBy('filename'); - - return results; - }) - ); + let lq__location_event_file_obj_li = liveQuery(async () => { + const id = $events_slct.event_location_id; + if (!id) return []; + return await db_events.file.where('for_id').equals(id).sortBy('filename'); + }); // Event Location - let lq__event_location_obj = $derived( - liveQuery(async () => { - let results = await db_events.location.get($events_slct.event_location_id); + let lq__event_location_obj = liveQuery(async () => { + const id = $events_slct.event_location_id; + if (!id) return null; + return await db_events.location.get(id); + }); - return results; - }) - ); + let lq__event_location_obj_li = liveQuery(async () => { + const id = $events_slct.event_id; + if (!id) return []; + return await db_events.location.where('event_id').equals(id).sortBy('name'); + }); - let lq__event_location_obj_li = $derived( - liveQuery(async () => { - let results = await db_events.location - .where('event_id') - .equals($events_slct.event_id) - .sortBy('name'); + // Event Session (Main View Trigger) + // Removed $derived wrapper to ensure stable observable subscription in Svelte 5 + let lq__event_session_obj = liveQuery(async () => { + const id = $events_slct.event_session_id; + if (!id) return null; + if (log_lvl) console.log(`🔍 [Trace] Launcher Layout LQ: Fetching session_id=${id}`); + const start = performance.now(); + let results = await db_events.session.get(id); + if (log_lvl) console.log(`📦 [Trace] Launcher Layout LQ: Result obtained in ${(performance.now() - start).toFixed(2)}ms (Result=${results?.name || 'NOT FOUND'})`); + return results; + }); - return results; - }) - ); + let lq__event_session_obj_li = liveQuery(async () => { + 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'); - // Event Session - let lq__event_session_obj = $derived( - liveQuery(async () => { - if (log_lvl) console.log(`🔍 [Trace] Launcher Layout LQ: Fetching session_id=${$events_slct.event_session_id}`); - let results = await db_events.session.get($events_slct.event_session_id); - if (log_lvl) console.log(`📦 [Trace] Launcher Layout LQ: Result=`, results?.name || 'NOT FOUND'); - return results; - }) - ); - - let lq__event_session_obj_li = $derived( - liveQuery(async () => { - if ($events_sess.session_li_trigger && !$events_sess?.session_li) { - $events_sess.session_li = null; - $events_sess.session_li_trigger = false; - } - - let results; - - if ($events_sess?.session_li && $events_sess?.session_li?.length) { - // Pre-filtered list in session storage - } else if ($events_slct.event_location_id) { - if (log_lvl > 1) { - console.log( - `LQ - Using default sort for Event Session list location_id: ${$events_slct?.event_location_id}` - ); - } - results = await db_events.session - .where('event_location_id') - .equals($events_slct.event_location_id) - .reverse() - .sortBy('name'); - } - - // Check if results are different than the current session version stored under $events_slct - 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_sess.launcher.modal__open) { - // if ($events_loc.launcher.controller == 'local_push' && $events_sess.launcher.ws_connect_status == 'connected') { - // console.log(`Local Push Controller Command: ae_close:event_file_modal`); - // $events_sess.launcher.controller_cmd = `ae_close:event_file_modal`; - // $events_sess.launcher.controller_trigger_send = true; - // } - // } - - let ae_promises: key_val = { - slct_event_session_id: null, - slct_event_presentation_li: null - }; + 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; + }); let trigger_handle_ws_conn = $state(false); let trigger_handle_ws_recv = $state(false); @@ -280,240 +186,133 @@ /* *** BEGIN *** Handle WebSocket events */ function handle_ws_conn(ws_conn_status: any) { - if (log_lvl) { - console.log('*** handle_ws_conn() ***', ws_conn_status); - } - + if (log_lvl) console.log('*** handle_ws_conn() ***', ws_conn_status); if (ws_conn_status.status == 'connected') { - $events_sess.launcher.ws = {}; // Reset WS related values on a new connection. - $events_sess.launcher.ws.status = 'connected'; + $events_sess.launcher.ws = { status: 'connected' }; } else { - $events_sess.launcher.ws = {}; // Reset WS related values when disconnected connection. - $events_sess.launcher.ws.status = 'disconnected'; + $events_sess.launcher.ws = { status: 'disconnected' }; } - $events_sess.launcher = $events_sess.launcher; } - // This client received a WebSocket message. - // When this is called something seems to go wrong. It creates a loop when connected. function handle_ws_recv(ws_recv_status: any) { - if (log_lvl) { - console.log('*** handle_ws_recv() ***', ws_recv_status); - } - + if (log_lvl) console.log('*** handle_ws_recv() ***', ws_recv_status); if (ws_recv_status.type == 'cmd' && ws_recv_status.cmd) { let cmd = ws_recv_status.cmd; - // console.log(cmd); + if ($events_loc.launcher.controller != 'remote') return; - if ($events_loc.launcher.controller != 'remote') { - if (log_lvl) { - console.log( - 'Ignoring WS command. Launcher is not set to be remotely controlled.', - cmd - ); - } - return; - } - - // AE Load (event session ID) if (cmd.startsWith('ae_load:')) { let cmd_parts = cmd.split(':'); let obj_parts = cmd_parts[1].split('='); let obj_type = obj_parts[0]; let obj_id = obj_parts[1]; - if (log_lvl) { - console.log(`ae_load: ${obj_type} ${obj_id}`); - } if (obj_type == 'event_session') { $events_slct.event_session_id = obj_id; - - if (log_lvl) console.log(`[WS Trace] Current URL`, data.url); let new_url = new URL(data.url); new_url.pathname = `/events/${$lq__event_session_obj?.event_id}/launcher/${$lq__event_session_obj?.event_location_id}`; new_url.searchParams.set('session_id', $events_slct.event_session_id); - if (log_lvl) console.log(`[WS Trace] New URL: ${new_url}`); - let new_url_str = new_url.toString(); - goto(new_url_str, { replaceState: false }); + goto(new_url.toString(), { replaceState: false }); } - // AE Download (event file ID) - // This does not yet work with the native app caching. } else if (cmd.startsWith('ae_download:')) { let cmd_parts = cmd.split(':'); let obj_parts = cmd_parts[1].split('='); let obj_type = obj_parts[0]; let obj_id = obj_parts[1]; let obj_filename = cmd_parts[2]; - let obj_extension = cmd_parts[3]; - if (log_lvl) { - console.log( - `ae_download: ${obj_type} ${obj_id} ${obj_filename} ${obj_extension}` - ); - } - - ae_promises[obj_id] = api.download_hosted_file({ + api.download_hosted_file({ api_cfg: $ae_api, - hosted_file_id: obj_id, // +'x' + hosted_file_id: obj_id, return_file: true, - filename: `${obj_filename}`, + filename: obj_filename, auto_download: true, log_lvl: 1 }); - - // AE Open (event file ID) } else if (cmd.startsWith('ae_open:')) { let cmd_parts = cmd.split(':'); let obj_parts = cmd_parts[1].split('='); - let obj_type = obj_parts[0]; let obj_id = obj_parts[1]; - if (log_lvl) { - console.log(`ae_open: ${obj_type} ${obj_id}`); - } - - // NOTE: This is not finished yet. - if (obj_type == 'event_file') { - // We are assuming this is for digital posters and needs to be opened in the modal. - + if (obj_parts[0] == 'event_file') { $events_sess.launcher.modal__open_event_file_id = null; - clearInterval(idle_timer_interval); saver_looping = false; restartCountdown(); - $events_slct.event_file_id = obj_id; - - $events_sess.launcher.modal__title = $events_sess.launcher.modal__title ?? '*'; - - $events_sess.launcher.modal__open_event_file_id = $events_slct.event_file_id; + $events_sess.launcher.modal__open_event_file_id = obj_id; } - // NOTE: This is not finished yet. - - // AE Close (event file modal) } else if (cmd.startsWith('ae_close:')) { - let cmd_parts = cmd.split(':'); - let what = cmd_parts[1]; - - if (what == 'event_file_modal') { - $events_sess.launcher.modal__title = ''; + if (cmd.split(':')[1] == 'event_file_modal') { $events_sess.launcher.modal__open_event_file_id = null; - $events_sess.launcher.modal__event_file_obj = null; } - clearInterval(idle_timer_interval); saver_looping = false; restartCountdown(); - - // WORKING! AE Refresh (now) } else if (cmd.startsWith('ae_refresh:')) { - let cmd_parts = cmd.split(':'); - let what = cmd_parts[1]; - - if (what == 'now') { - location.reload(); - } + if (cmd.split(':')[1] == 'now') location.reload(); } } } - // This client sent a WebSocket message. function handle_ws_sent(ws_sent_status: any) { - if (log_lvl) console.log('*** handle_ws_sent() ***', ws_sent_status); - $events_sess.launcher.controller_cmd = null; $events_sess.launcher.controller_trigger_send = null; } - /* *** END *** Handle WebSocket events */ $effect(() => { if (trigger_handle_ws_conn) { - let ws_conn_status = trigger_handle_ws_conn; // This a string status + handle_ws_conn(trigger_handle_ws_conn); trigger_handle_ws_conn = false; - handle_ws_conn(ws_conn_status); } }); $effect(() => { if (trigger_handle_ws_recv) { - let ws_recv_status = trigger_handle_ws_recv; // This should be a key value object. + handle_ws_recv(trigger_handle_ws_recv); trigger_handle_ws_recv = false; - handle_ws_recv(ws_recv_status); } }); $effect(() => { if (trigger_handle_ws_sent) { - let ws_sent_status = trigger_handle_ws_sent; // This should be a key value object. + handle_ws_sent(trigger_handle_ws_sent); trigger_handle_ws_sent = false; - handle_ws_sent(ws_sent_status); } }); - if (!$events_loc.launcher.idle_timer) { - $events_loc.launcher.idle_timer = 5 * 60 * 1000; - } - if (!$events_loc.launcher.idle_cycle) { - $events_loc.launcher.idle_cycle = 5 * 1000; - } - if (!$events_loc.launcher.idle_loop_period) { - $events_loc.launcher.idle_loop_period = 3 * 60 * 1000; - } + if (!$events_loc.launcher.idle_timer) $events_loc.launcher.idle_timer = 5 * 60 * 1000; + if (!$events_loc.launcher.idle_cycle) $events_loc.launcher.idle_cycle = 5 * 1000; + if (!$events_loc.launcher.idle_loop_period) $events_loc.launcher.idle_loop_period = 3 * 60 * 1000; listen({ timer: $events_loc.launcher.idle_timer, - cycle: $events_loc.launcher.idle_cycle ?? 5 * 1000 + cycle: $events_loc.launcher.idle_cycle }); let idle_timer_interval: any = $state(); let saver_looping: boolean = $state(false); function handle_idle_client() { - // Start the digital poster screen saver after additional idle time. if ($lq__event_session_obj && $lq__event_session_obj?.type_code == 'poster') { - if (log_lvl) { - console.log( - `Turned idle! Open poster file for poster session in ${($events_loc.launcher.idle_loop_period / 60000).toPrecision(4)} minutes.` - ); - } if (saver_looping) return false; - saver_looping = true; - idle_timer_interval = setInterval( - () => { - if (log_lvl) { - console.log( - `Screen saver tick every ${($events_loc.launcher.idle_loop_period / 60000).toPrecision(4)} minutes after initially being idle for ${($events_loc.launcher.idle_timer / 60000).toPrecision(4)} minutes.` - ); - } + idle_timer_interval = setInterval(() => { + if ($events_loc.launcher.screen_saver_img_kv) { + const keys = Object.keys($events_loc.launcher.screen_saver_img_kv); + const rand_index = Math.floor(Math.random() * keys.length); + let event_file_obj = $events_loc.launcher.screen_saver_img_kv[keys[rand_index]]; - if ($events_loc.launcher.screen_saver_img_kv) { - const rand_index = Math.floor( - Math.random() * - Object.keys($events_loc.launcher.screen_saver_img_kv).length - ); - - let event_file_obj = - $events_loc.launcher.screen_saver_img_kv[ - Object.keys($events_loc.launcher.screen_saver_img_kv)[rand_index] - ]; - - $events_slct.event_file_id = event_file_obj.event_file_id; - $events_slct.event_file_obj = event_file_obj; - - $events_sess.launcher.modal__open_event_file_id = null; - $events_sess.launcher.modal__title = event_file_obj.filename ?? '*'; - $events_sess.launcher.modal__open_event_file_id = - $events_slct.event_file_id; - $events_sess.launcher.modal__event_file_obj = event_file_obj; - - return true; - } - return false; - }, - $events_loc.launcher.idle_loop_period ?? 2 * 60 * 1000 - ); + $events_slct.event_file_id = event_file_obj.event_file_id; + $events_slct.event_file_obj = event_file_obj; + $events_sess.launcher.modal__open_event_file_id = null; + $events_sess.launcher.modal__title = event_file_obj.filename ?? '*'; + $events_sess.launcher.modal__open_event_file_id = $events_slct.event_file_id; + $events_sess.launcher.modal__event_file_obj = event_file_obj; + return true; + } + return false; + }, $events_loc.launcher.idle_loop_period ?? 2 * 60 * 1000); } else { saver_looping = false; return false; @@ -521,24 +320,12 @@ } onIdle(() => { - if (log_lvl) { - console.log( - `User has gone idle after ${($events_loc.launcher?.idle_timer / 60000).toPrecision(4)} minutes.`, - $idle - ); - } clearInterval(idle_timer_interval); handle_idle_client(); }); $effect(() => { if (!$idle) { - if (log_lvl > 1) { - console.log( - `User is active again after being idle. End screen saver if active.`, - $idle - ); - } clearInterval(idle_timer_interval); saver_looping = false; } diff --git a/src/routes/events/[event_id]/(launcher)/menu_session_list.svelte b/src/routes/events/[event_id]/(launcher)/menu_session_list.svelte index 72b9d83e..d0200801 100644 --- a/src/routes/events/[event_id]/(launcher)/menu_session_list.svelte +++ b/src/routes/events/[event_id]/(launcher)/menu_session_list.svelte @@ -65,6 +65,7 @@ $effect(() => { if (trigger_reload__event_session_obj_id) { + const start = performance.now(); if (log_lvl) { console.log( `[UI Trace] trigger_reload__event_session_obj_id changed: ${trigger_reload__event_session_obj_id}` @@ -72,9 +73,12 @@ } let event_session_id = String(trigger_reload__event_session_obj_id); trigger_reload__event_session_obj_id = false; + + // OPTIMIZATION: Update slct__event_session_id prop (which is bound to the store) + // BEFORE calling goto. This triggers the LiveQuery in the layout instantly. slct__event_session_id = event_session_id; - if (log_lvl) console.log(`[UI Trace] UI setting slct__event_session_id = ${slct__event_session_id}`); + if (log_lvl) console.log(`[UI Trace] UI setting slct__event_session_id = ${slct__event_session_id} (+${(performance.now() - start).toFixed(2)}ms)`); handle_load_ae_obj_id__event_session(event_session_id); @@ -87,12 +91,17 @@ data_url.searchParams.set('session_id', event_session_id); let new_url = data_url.toString(); - if (log_lvl) console.log(`[UI Trace] Updating URL to: ${new_url}`); - goto(new_url, { replaceState: false }); + if (log_lvl) console.log(`[UI Trace] Initiating SvelteKit goto... (+${(performance.now() - start).toFixed(2)}ms)`); + + // Use noscroll and keepfocus to speed up the transition + goto(new_url, { replaceState: false, noscroll: true, keepfocus: true }).then(() => { + if (log_lvl) console.log(`🏁 [Trace] Total Navigation Roundtrip: ${(performance.now() - start).toFixed(2)}ms`); + }); } }); function handle_load_ae_obj_id__event_session(event_session_id: any) { + const start = performance.now(); if (log_lvl) { console.log( `[UI Trace] handle_load_ae_obj_id__event_session: Calling library for id=${event_session_id}` @@ -111,7 +120,7 @@ log_lvl: log_lvl }) .then(async (load_results) => { - if (log_lvl) console.log(`[UI Trace] handle_load_ae_obj_id: Library returned results.`, load_results); + if (log_lvl) console.log(`[UI Trace] handle_load_ae_obj_id: Library returned results in ${(performance.now() - start).toFixed(2)}ms.`); if (load_results) { $events_slct.event_session_obj = load_results;