* ui: Remove dead code from stores - persisted() helper was exported but never used - messageUpdateCallback / registerMessageUpdateCallback were never wired up - conversationsStore.initialize() alias, single caller moved to init() * ui: Merge device, theme and viewport into a single deviceStore All three are reactive browser-environment signals, now exposed as one class store: deviceStore.isMobile, deviceStore.isIOSDevice / isIOSSafari / isWKWebView / isStandalone and deviceStore.systemTheme.isDark. The systemTheme name disambiguates the OS preference from the user theme preference in settingsStore. Drops the unused viewport export (only isMobile was consumed). * ui: Merge build info into version store One VersionStore class with build (llama.cpp build number from build.json) and frontend (PWA version from _app/version.json), matching the class pattern of the other stores. * ui: Colocate context gauge popup state with its components The gauge popup state is local UI state shared only by the ChatFormContextGauge subtree, so it lives next to its consumers instead of the app-scope stores barrel.
87 lines
2.6 KiB
TypeScript
87 lines
2.6 KiB
TypeScript
import { browser } from '$app/environment';
|
|
import { BUILD_VERSION_LOCALSTORAGE_KEY, SW_CONFIG } from '$lib/constants';
|
|
import { versionStore } from '$lib/stores';
|
|
import { useRegisterSW } from 'virtual:pwa-register/svelte';
|
|
|
|
/**
|
|
* Hook for PWA service worker registration, update polling, and build version mismatch detection.
|
|
*
|
|
* Combines two concerns that always belong together:
|
|
* 1. SW registration with periodic polling for updates
|
|
* 2. localStorage-based version tracking for non-PWA users
|
|
*/
|
|
export function usePwa() {
|
|
let swCheckInterval: ReturnType<typeof setInterval> | null = null;
|
|
let needRefreshByStorage = $state(false);
|
|
|
|
const {
|
|
// offlineReady, // to do - add installation banners for iOS
|
|
needRefresh: pwaNeedRefresh,
|
|
updateServiceWorker
|
|
} = useRegisterSW({
|
|
onRegisteredSW(swUrl: string, r: ServiceWorkerRegistration | undefined) {
|
|
if (swCheckInterval) {
|
|
clearInterval(swCheckInterval);
|
|
}
|
|
|
|
swCheckInterval = setInterval(async () => {
|
|
if (!r || r.installing || !navigator?.onLine) return;
|
|
|
|
try {
|
|
const resp = await fetch(swUrl, {
|
|
cache: SW_CONFIG.UPDATE_FETCH_OPTIONS.CACHE,
|
|
headers: {
|
|
cache: SW_CONFIG.UPDATE_FETCH_OPTIONS.HEADERS.CACHE,
|
|
'cache-control': SW_CONFIG.UPDATE_FETCH_OPTIONS.HEADERS.CACHE_CONTROL
|
|
}
|
|
});
|
|
|
|
if (resp?.status === 200) {
|
|
await r.update();
|
|
}
|
|
} catch (e) {
|
|
console.error(e);
|
|
}
|
|
}, SW_CONFIG.CHECK_INTERVAL_MS);
|
|
},
|
|
onRegisterError(error: unknown) {
|
|
console.error('[PWA] SW registration error:', error);
|
|
}
|
|
});
|
|
|
|
// Detect version mismatch via localStorage.
|
|
// _app/version.json is SvelteKit's native version file for PWA cache invalidation.
|
|
// This comparison detects server upgrades for non-PWA users.
|
|
$effect(() => {
|
|
if (!browser) return;
|
|
|
|
// PWA pages update via the service worker path; the storage check is the non-PWA fallback only
|
|
if (navigator.serviceWorker?.controller) return;
|
|
|
|
const currentVersion = versionStore.frontend;
|
|
|
|
if (!currentVersion) return;
|
|
|
|
try {
|
|
const storedVersion = localStorage.getItem(BUILD_VERSION_LOCALSTORAGE_KEY);
|
|
|
|
needRefreshByStorage = !!storedVersion && storedVersion !== currentVersion;
|
|
localStorage.setItem(BUILD_VERSION_LOCALSTORAGE_KEY, currentVersion);
|
|
} catch {
|
|
needRefreshByStorage = false;
|
|
}
|
|
});
|
|
|
|
return {
|
|
/** Writable that is true when a PWA service worker update is available */
|
|
get needRefresh() {
|
|
return pwaNeedRefresh;
|
|
},
|
|
/** Version mismatch detected via localStorage (non-PWA users) */
|
|
get needRefreshByStorage() {
|
|
return needRefreshByStorage;
|
|
},
|
|
updateServiceWorker
|
|
};
|
|
}
|