app-init.svelte (3917B)
1 <script lang="ts"> 2 import { 3 app_init_has_completed, 4 app_init_state, 5 type AppInitStage, 6 } from "$lib/utils/app"; 7 import { LogoCircle } from "@radroots/apps-lib-pwa"; 8 import { onDestroy, onMount } from "svelte"; 9 10 const stage_messages: Record<AppInitStage, string[]> = { 11 idle: [`Preparing runtime`, `Starting services`, `Allocating memory`], 12 storage: [ 13 `Opening local storage`, 14 `Verifying data stores`, 15 `Syncing storage index`, 16 ], 17 download_sql: [ 18 `Fetching SQL runtime`, 19 `Downloading core engine`, 20 `Verifying module`, 21 ], 22 download_geo: [ 23 `Fetching map data`, 24 `Downloading geocoder DB`, 25 `Validating data file`, 26 ], 27 database: [ 28 `Preparing database`, 29 `Applying migrations`, 30 `Indexing records`, 31 ], 32 geocoder: [ 33 `Loading geocoder`, 34 `Preparing lookup tables`, 35 `Calibrating index`, 36 ], 37 ready: [`Final checks`, `Ready`, `Starting UI`], 38 error: [`Init error`, `Retrying setup`, `Check connection`], 39 }; 40 41 const bytes_to_mb = (bytes: number): string => 42 (bytes / (1024 * 1024)).toFixed(1); 43 44 let show_ui = $state(false); 45 let message = $state(``); 46 let message_index = $state(0); 47 let stage = $state<AppInitStage>(`idle`); 48 let message_timer_id = $state<number | null>(null); 49 50 let progress_ratio = $state(0); 51 let progress_text = $state(`0.0 MB`); 52 53 const set_message_for_stage = (next_stage: AppInitStage): void => { 54 const list = stage_messages[next_stage] ?? stage_messages.idle; 55 message_index = 0; 56 message = list[0] ?? ``; 57 }; 58 59 const step_message = (): void => { 60 const list = stage_messages[stage] ?? stage_messages.idle; 61 if (list.length <= 1) return; 62 message_index = (message_index + 1) % list.length; 63 message = list[message_index] ?? ``; 64 }; 65 66 $effect(() => { 67 stage = $app_init_state.stage; 68 set_message_for_stage(stage); 69 }); 70 71 $effect(() => { 72 const loaded = $app_init_state.loaded_bytes; 73 const total = $app_init_state.total_bytes; 74 progress_ratio = total ? Math.min(1, loaded / total) : 0; 75 const loaded_mb = bytes_to_mb(loaded); 76 progress_text = total 77 ? `${loaded_mb} / ${bytes_to_mb(total)} MB` 78 : `${loaded_mb} MB`; 79 }); 80 81 onMount(() => { 82 show_ui = !app_init_has_completed(); 83 if (!show_ui) return; 84 message_timer_id = window.setInterval(() => { 85 step_message(); 86 }, 4200); 87 }); 88 89 onDestroy(() => { 90 if (message_timer_id !== null) window.clearInterval(message_timer_id); 91 }); 92 </script> 93 94 {#if show_ui} 95 <div 96 class={`flex flex-col min-h-screen w-full justify-center items-center`} 97 > 98 <div class={`flex flex-col justify-center items-center gap-6`}> 99 <LogoCircle /> 100 <div class={`flex flex-col items-center gap-3`}> 101 <p class={`text-sm text-ly0-gl text-center`}>{message}</p> 102 <div 103 class={`flex flex-col items-center gap-2 w-[72vw] max-w-[22rem]`} 104 > 105 <div 106 class={`w-full h-2 bg-ly0-gl/15 rounded-full overflow-hidden`} 107 > 108 <div 109 class={`h-full bg-ly0-gl/55 rounded-full`} 110 style={`width: ${Math.max(progress_ratio * 100, 4)}%`} 111 ></div> 112 </div> 113 <p class={`text-xs text-ly0-gl-label`}> 114 {`Downloaded ${progress_text}`} 115 </p> 116 </div> 117 </div> 118 </div> 119 </div> 120 {/if}