diff --git a/src/lib/ae_events/ae_events__event.ts b/src/lib/ae_events/ae_events__event.ts index 12bb4026..0a060996 100644 --- a/src/lib/ae_events/ae_events__event.ts +++ b/src/lib/ae_events/ae_events__event.ts @@ -797,7 +797,7 @@ export async function qry_ae_obj_li__event_v2({ return filtered_obj_li.slice(0, limit); } -// Updated 2025-05-09 +// Updated 2026-03-10 export const properties_to_save = [ 'id', 'event_id', @@ -858,7 +858,23 @@ export const properties_to_save = [ 'file_count', 'file_count_all', 'internal_use_count', - 'event_file_id_li_json' + 'event_file_id_li_json', + // Legacy flat address fields — preserved so background SWR re-fires do not wipe + // address data for events that predate location_address_json. The edit form reads + // these as a fallback: location_address_json?.name ?? address_name ?? ''. + // New saves always write structured data into location_address_json, but the API + // may still return the flat fields for older records and they must survive an IDB + // round-trip without being stripped by _process_generic_props. + 'address_location_id', + 'address_name', + 'address_line_1', + 'address_line_2', + 'address_city', + 'address_state', + 'address_postal_code', + 'address_country', + 'address_country_alpha_2_code', + 'address_country_subdivision_code' ]; async function _process_generic_props>({ diff --git a/src/routes/events/[event_id]/(launcher)/launcher_background_sync.svelte b/src/routes/events/[event_id]/(launcher)/launcher_background_sync.svelte index 01d2bae9..522b5e00 100644 --- a/src/routes/events/[event_id]/(launcher)/launcher_background_sync.svelte +++ b/src/routes/events/[event_id]/(launcher)/launcher_background_sync.svelte @@ -21,16 +21,18 @@ let last_heartbeat: string | null = $state(null); // Loop Timings (Visible in UI) - // WHY: Session was 15s which combined with the SWR background-refresh pattern - // caused a continuous API call every 15s even on cache hits. 60s is still + // WHY: Session was originally 15s which combined with the SWR background-refresh + // pattern caused a continuous API call every 15s even on cache hits. 60s is still // responsive for live conference use but dramatically reduces server load. + // Defaults here must match the onMount fallbacks below — keep in sync. let loop_info = $state({ event: 90000, device: 60000, location: 60000, session: 60000, presentation: 120000, - presenter: 120000 + presenter: 120000, + file_sync: 30000 }); // Timer Handles @@ -72,27 +74,28 @@ const dev = $ae_loc.native_device || {}; const cfg = $events_loc.launcher.sync_intervals || {}; - // Load timings from persistent config, with fallbacks to device config or defaults - loop_info.event = cfg.event || dev.check_event_loop_period || 90000; - loop_info.device = cfg.device || dev.check_event_device_loop_period || 60000; - loop_info.location = cfg.location || dev.check_event_location_loop_period || 30000; - loop_info.session = cfg.session || dev.check_event_session_loop_period || 15000; - loop_info.presentation = cfg.presentation || dev.check_event_presentation_loop_period || 45000; - loop_info.presenter = cfg.presenter || dev.check_event_presenter_loop_period || 60000; + // Load timings from persistent config, with fallbacks to device config or defaults. + // Fallback values here must match the loop_info $state defaults above — keep in sync. + loop_info.event = cfg.event || dev.check_event_loop_period || 90000; + loop_info.device = cfg.device || dev.check_event_device_loop_period || 60000; + loop_info.location = cfg.location || dev.check_event_location_loop_period || 60000; + loop_info.session = cfg.session || dev.check_event_session_loop_period || 60000; + loop_info.presentation = cfg.presentation || dev.check_event_presentation_loop_period || 120000; + loop_info.presenter = cfg.presenter || dev.check_event_presenter_loop_period || 120000; + loop_info.file_sync = cfg.file_sync || dev.check_file_sync_loop_period || 30000; // 1. Structural/Metadata Loops - timer__event = setInterval(() => refresh_event_data(), loop_info.event); - timer__device = setInterval(() => run_device_heartbeat(), loop_info.device); + timer__event = setInterval(() => refresh_event_data(), loop_info.event); + timer__device = setInterval(() => run_device_heartbeat(), loop_info.device); timer__location = setInterval(() => refresh_location_config(), loop_info.location); // 2. Room Content Refresh Loops (API -> Dexie) - timer__session = setInterval(() => refresh_session_data(), loop_info.session); + timer__session = setInterval(() => refresh_session_data(), loop_info.session); timer__presentation = setInterval(() => refresh_presentation_data(), loop_info.presentation); - timer__presenter = setInterval(() => refresh_presenter_data(), loop_info.presenter); + timer__presenter = setInterval(() => refresh_presenter_data(), loop_info.presenter); // 3. Native File Sync Loop (Dexie -> Disk) - const sync_period = cfg.file_sync || dev.check_file_sync_loop_period || 10000; - timer__file_sync = setInterval(() => run_sync_cycle(), sync_period); + timer__file_sync = setInterval(() => run_sync_cycle(), loop_info.file_sync); // Immediate first run for metadata refresh_event_data(); @@ -241,6 +244,11 @@ let missing_count = 0; for (const file_obj of files) { + // Re-check pause flag each iteration — a sync cycle can run for many + // seconds if there are missing files, so we must honour a pause request + // mid-loop rather than waiting for the entire batch to finish. + if ($events_loc.launcher.sync_paused) break; + if (!file_obj.hash_sha256) continue; if (sync_results[file_obj.event_file_id] === 'success') { cached_count++;