From 85249be62c756316dd97131daa71e08388ba433d Mon Sep 17 00:00:00 2001 From: stefan Date: Fri, 26 Sep 2025 14:57:09 +0200 Subject: [PATCH] fixing clients new frontend --- .../kotlin/at/mocode/clients/app/App.kt | 4 +-- clients/app/src/jsMain/resources/index.html | 15 ++++++++-- .../src/jsMain/resources/manifest.webmanifest | 4 +-- clients/app/src/jsMain/resources/sw.js | 30 +++++++++++++++++-- 4 files changed, 43 insertions(+), 10 deletions(-) diff --git a/clients/app/src/commonMain/kotlin/at/mocode/clients/app/App.kt b/clients/app/src/commonMain/kotlin/at/mocode/clients/app/App.kt index c17e7a9c..88c1ea3c 100644 --- a/clients/app/src/commonMain/kotlin/at/mocode/clients/app/App.kt +++ b/clients/app/src/commonMain/kotlin/at/mocode/clients/app/App.kt @@ -4,7 +4,6 @@ import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.padding import androidx.compose.runtime.* import androidx.compose.ui.Modifier -import androidx.lifecycle.viewmodel.compose.viewModel import at.mocode.clients.shared.commonui.components.AppHeader import at.mocode.clients.shared.commonui.components.AppScaffold import at.mocode.clients.shared.commonui.theme.AppTheme @@ -15,6 +14,8 @@ import at.mocode.clients.pingfeature.PingViewModel @Composable fun App() { var currentScreen: AppScreen by remember { mutableStateOf(AppScreen.Home) } + // Create a single PingViewModel instance for the lifetime of the App composition. + val pingViewModel: PingViewModel = remember { PingViewModel() } AppTheme { AppScaffold( @@ -31,7 +32,6 @@ fun App() { LandingScreen() } is AppScreen.Ping -> { - val pingViewModel: PingViewModel = viewModel() PingScreen(viewModel = pingViewModel) } } diff --git a/clients/app/src/jsMain/resources/index.html b/clients/app/src/jsMain/resources/index.html index 637b1320..ba790e85 100644 --- a/clients/app/src/jsMain/resources/index.html +++ b/clients/app/src/jsMain/resources/index.html @@ -7,14 +7,23 @@ +
diff --git a/clients/app/src/jsMain/resources/manifest.webmanifest b/clients/app/src/jsMain/resources/manifest.webmanifest index eecab271..3e89d363 100644 --- a/clients/app/src/jsMain/resources/manifest.webmanifest +++ b/clients/app/src/jsMain/resources/manifest.webmanifest @@ -7,7 +7,7 @@ "background_color": "#ffffff", "theme_color": "#0f172a", "icons": [ - { "src": "/icons/icon-192.png", "sizes": "192x192", "type": "image/png" }, - { "src": "/icons/icon-512.png", "sizes": "512x512", "type": "image/png" } + { "src": "icons/icon-192.png", "sizes": "192x192", "type": "image/png" }, + { "src": "icons/icon-512.png", "sizes": "512x512", "type": "image/png" } ] } diff --git a/clients/app/src/jsMain/resources/sw.js b/clients/app/src/jsMain/resources/sw.js index 33f7a113..b2a7400c 100644 --- a/clients/app/src/jsMain/resources/sw.js +++ b/clients/app/src/jsMain/resources/sw.js @@ -1,4 +1,6 @@ -const CACHE_NAME = 'meldestelle-cache-v1'; +const IS_DEV = self.location.hostname === 'localhost' || self.location.hostname === '127.0.0.1' || self.location.hostname === '::1'; + +const CACHE_NAME = 'meldestelle-cache-v2'; const PRECACHE_URLS = [ '/', '/index.html', @@ -6,6 +8,11 @@ const PRECACHE_URLS = [ ]; self.addEventListener('install', (event) => { + if (IS_DEV) { + // In dev, don't precache. Just activate the SW immediately. + self.skipWaiting(); + return; + } event.waitUntil( caches.open(CACHE_NAME) .then((cache) => cache.addAll(PRECACHE_URLS)) @@ -14,6 +21,10 @@ self.addEventListener('install', (event) => { }); self.addEventListener('activate', (event) => { + if (IS_DEV) { + event.waitUntil(self.clients.claim()); + return; + } event.waitUntil( caches.keys().then((keys) => Promise.all( keys.filter((k) => k !== CACHE_NAME).map((k) => caches.delete(k)) @@ -22,6 +33,10 @@ self.addEventListener('activate', (event) => { }); self.addEventListener('fetch', (event) => { + if (IS_DEV) { + return; // don't interfere with dev server/HMR + } + const req = event.request; const url = new URL(req.url); @@ -29,9 +44,10 @@ self.addEventListener('fetch', (event) => { const sameOrigin = url.origin === self.location.origin; const isExtension = url.protocol === 'chrome-extension:'; const isHotUpdate = url.pathname.includes('hot-update'); + const isWebSocketUpgrade = req.headers.get('upgrade') === 'websocket'; - // Ignore non-GET, cross-origin, browser extensions, and HMR/hot-update requests - if (req.method !== 'GET' || !isHttp || !sameOrigin || isExtension || isHotUpdate) { + // Ignore non-GET, cross-origin, browser extensions, HMR, and WebSocket upgrade requests + if (req.method !== 'GET' || !isHttp || !sameOrigin || isExtension || isHotUpdate || isWebSocketUpgrade) { return; // Let the browser handle it } @@ -51,6 +67,14 @@ self.addEventListener('fetch', (event) => { return; } + // Avoid noisy errors for favicon during dev/prod when missing + if (url.pathname === '/favicon.ico') { + event.respondWith( + fetch(req).catch(() => caches.match(req)) + ); + return; + } + // Cache-first for static assets event.respondWith( caches.match(req).then((cached) => {