perf(launcher): minimize session switch latency with non-blocking SWR and stable observables
This commit is contained in:
@@ -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<ae_EventSession | null> {
|
||||
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<ae_EventSession[]> {
|
||||
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 });
|
||||
|
||||
@@ -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<boolean | undefined>;
|
||||
|
||||
$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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user