From 607e5450e19e672c8ceeaae04f4e796a270d5a24 Mon Sep 17 00:00:00 2001 From: Florian Zumpe Date: Wed, 30 Sep 2026 23:34:26 +0200 Subject: [PATCH] fix sonarqube findings --- public/css/bootstrap.css | 10 +++++++++- public/index.html | 8 ++++---- public/js/offline-cache.js | 6 +++++- public/js/storage-usage.js | 20 ++++++++++++++------ public/offline-worker.js | 13 +++++++------ src/services/offline-manifest.js | 2 +- 6 files changed, 40 insertions(+), 19 deletions(-) diff --git a/public/css/bootstrap.css b/public/css/bootstrap.css index 83b7fad..131c8ca 100644 --- a/public/css/bootstrap.css +++ b/public/css/bootstrap.css @@ -264,7 +264,8 @@ background: #f9fbf6; } -#bootstrap-screen .privacy-summary > p { +#bootstrap-screen .privacy-summary > p, +#bootstrap-screen .privacy-summary > output { margin: 0; color: var(--bootstrap-muted); font-size: .92rem; @@ -431,3 +432,10 @@ .route-offline-controls { margin: .75rem 0; } .route-offline-controls > a { display: inline-block; padding: .75rem; } .route-offline-controls > a[hidden] { display: none; } + +.storage-usage output, +#route-offline-status { + display: block; + margin: .4rem 0; +} +.storage-usage output[hidden] { display: none; } diff --git a/public/index.html b/public/index.html index d36a61c..5676cfc 100644 --- a/public/index.html +++ b/public/index.html @@ -130,7 +130,7 @@

Warte vor dem Losgehen, bis die Downloads abgeschlossen sind. Aktualisieren braucht Internet. Beim Ausschalten dieses Schalters werden nur die App-Kopien entfernt; separat gespeicherte Strecken, lokale Entwürfe und Streckenfortschritt bleiben erhalten.

Die Speicheranzeige schätzt die Belegung dieser Website. Local Storage für Entwürfe hat zusätzlich eine eigene Grenze. Der Browser kann Offline-Dateien bei knappem Speicher entfernen; beim Löschen der Website-Daten gehen sie ebenfalls verloren. Externe Karten werden nicht automatisch gespeichert.

-

+
@@ -140,7 +140,7 @@

Website-Speicher: Browser-Schätzung für App-Dateien, Offline-Strecken und Medien. Local Storage: separater Speicher für Entwürfe und Einstellungen. Beide Anzeigen beziehen sich nur auf diese Website, nicht auf die gesamte Festplatte.

- +
@@ -233,7 +233,7 @@ -

+

Die Offline-Funktion speichert diese Strecke samt GPX, Bildern und Audio im Browser. Der GPX-Download speichert nur die GPX-Datei im Download-Ordner.

@@ -461,7 +461,7 @@

Website-Speicher: Browser-Schätzung für App-Dateien, Offline-Strecken und Medien. Local Storage: separater Speicher für Entwürfe und Einstellungen. Beide Anzeigen beziehen sich nur auf diese Website, nicht auf die gesamte Festplatte.

- +

Veröffentlichte Serverstrecken und ausschließlich in diesem Browser gespeicherte Entwürfe werden getrennt dargestellt.

diff --git a/public/js/offline-cache.js b/public/js/offline-cache.js index 3433e59..3d2a6c7 100644 --- a/public/js/offline-cache.js +++ b/public/js/offline-cache.js @@ -132,6 +132,10 @@ globalThis.Wegwichtel ??= {}; if (message && !routeTask) message.textContent = saved ? `${saved.count} Dateien dieser Strecke sind auf dem Gerät gespeichert.` : 'Speichert nur diese Strecke mit GPX, Bildern und Audio für die App.'; + updateGpxDownload(route); + } + + function updateGpxDownload(route) { const gpx = document.getElementById('route-gpx-download'); if (gpx) { gpx.hidden = !route.gpxUrl; @@ -199,7 +203,7 @@ globalThis.Wegwichtel ??= {}; const run = action => { const route = selectedRoute; if (!route) return; - void action(route).catch(error => { + return action(route).catch(error => { if (selectedRoute !== route) return; const message = document.getElementById('route-offline-status'); if (message) message.textContent = `${error.message} Ein bisheriger Offline-Stand bleibt erhalten.`; diff --git a/public/js/storage-usage.js b/public/js/storage-usage.js index cca7064..1852225 100644 --- a/public/js/storage-usage.js +++ b/public/js/storage-usage.js @@ -33,6 +33,18 @@ globalThis.Wegwichtel ??= {}; } } + function siteDescription(usage, quota, known) { + if (!known) return 'Der Browser meldet keine Speicherkapazität.'; + const warning = usage / quota >= .9 ? ' Speicher fast voll.' : ''; + return `Website-Speicher: ${format(usage)} von ${format(quota)} belegt (${Math.round(usage / quota * 100)} %). Etwa ${format(Math.max(0, quota - usage))} verfügbar.${warning}`; + } + + function localDescription(local) { + if (local == null) return 'Local Storage ist nicht verfügbar.'; + const warning = local >= LOCAL_REFERENCE * .9 ? ' Richtwert fast erreicht: Große Bilder können das Speichern verhindern.' : ''; + return `Local Storage: ungefähr ${format(local)} belegt. Balken bezogen auf einen Richtwert von 5 MiB; die tatsächliche Grenze hängt vom Browser ab.${warning}`; + } + async function refresh() { if (refreshTask) return refreshTask; refreshTask = (async () => { @@ -41,14 +53,10 @@ globalThis.Wegwichtel ??= {}; const usage = estimate?.usage; const quota = estimate?.quota; const known = Number.isFinite(usage) && Number.isFinite(quota) && quota > 0; - show('site', usage, known ? quota : null, known - ? `Website-Speicher: ${format(usage)} von ${format(quota)} belegt (${Math.round(usage / quota * 100)} %). Etwa ${format(Math.max(0, quota - usage))} verfügbar.${usage / quota >= .9 ? ' Speicher fast voll.' : ''}` - : 'Der Browser meldet keine Speicherkapazität.'); + show('site', usage, known ? quota : null, siteDescription(usage, quota, known)); let local = null; try { local = localBytes(); } catch { /* Storage can be blocked by browser settings. */ } - show('local', local, local == null ? null : LOCAL_REFERENCE, local == null - ? 'Local Storage ist nicht verfügbar.' - : `Local Storage: ungefähr ${format(local)} belegt. Balken bezogen auf einen Richtwert von 5 MiB; die tatsächliche Grenze hängt vom Browser ab.${local >= LOCAL_REFERENCE * .9 ? ' Richtwert fast erreicht: Große Bilder können das Speichern verhindern.' : ''}`); + show('local', local, local == null ? null : LOCAL_REFERENCE, localDescription(local)); for (const panel of document.querySelectorAll('[data-storage-usage]')) { const warning = panel.querySelector('[data-storage-warning]'); if (warning) { warning.hidden = !lastError; warning.textContent = lastError; } diff --git a/public/offline-worker.js b/public/offline-worker.js index 9b83f10..0a883fc 100644 --- a/public/offline-worker.js +++ b/public/offline-worker.js @@ -2,8 +2,8 @@ // Serve app files and individually selected route snapshots independently (v2). importScripts('/js/offline-storage.js'); -self.addEventListener('install', event => event.waitUntil(self.skipWaiting())); -self.addEventListener('activate', event => event.waitUntil(self.clients.claim())); +globalThis.addEventListener('install', event => event.waitUntil(globalThis.skipWaiting())); +globalThis.addEventListener('activate', event => event.waitUntil(globalThis.clients.claim())); /** Audio seeks need partial responses even when the entire file is stored locally. */ async function rangedResponse(response, range) { @@ -11,7 +11,8 @@ async function rangedResponse(response, range) { const match = /^bytes=(\d*)-(\d*)$/.exec(range); if (!match || (!match[1] && !match[2])) return new Response(null, { status: 416, headers: { 'Content-Range': `bytes */${bytes.byteLength}` } }); const start = match[1] ? Number(match[1]) : Math.max(0, bytes.byteLength - Number(match[2])); - const end = match[1] ? (match[2] ? Math.min(Number(match[2]), bytes.byteLength - 1) : bytes.byteLength - 1) : bytes.byteLength - 1; + let end = bytes.byteLength - 1; + if (match[1] && match[2]) end = Math.min(Number(match[2]), end); if (start > end || start >= bytes.byteLength) return new Response(null, { status: 416, headers: { 'Content-Range': `bytes */${bytes.byteLength}` } }); const headers = new Headers(response.headers); headers.set('Content-Range', `bytes ${start}-${end}/${bytes.byteLength}`); @@ -20,15 +21,15 @@ async function rangedResponse(response, range) { return new Response(bytes.slice(start, end + 1), { status: 206, headers }); } -self.addEventListener('fetch', event => { +globalThis.addEventListener('fetch', event => { const request = event.request; const url = new URL(request.url); - if (request.method !== 'GET' || url.origin !== self.location.origin + if (request.method !== 'GET' || url.origin !== globalThis.location.origin || request.headers.has('X-Wegwichtel-Offline-Download') || url.pathname === '/api/offline-manifest') return; event.respondWith((async () => { // A complete snapshot is cache-first, avoiding any wait for a failing mobile network. - const cached = await self.Wegwichtel.OfflineStore.match(request.url).catch(() => null); + const cached = await globalThis.Wegwichtel.OfflineStore.match(request.url).catch(() => null); if (cached) { const range = request.headers.get('Range'); return range ? rangedResponse(cached, range) : cached; diff --git a/src/services/offline-manifest.js b/src/services/offline-manifest.js index 9cb97cb..e149a92 100644 --- a/src/services/offline-manifest.js +++ b/src/services/offline-manifest.js @@ -16,5 +16,5 @@ export function offlineManifest(publicDirectory) { } } visit(publicDirectory); - return { resources: [...resources].sort() }; + return { resources: [...resources].sort((left, right) => left.localeCompare(right, 'en')) }; }