Fjern unpkg CDN-avhengigheit og legg til slett-buffer-knapp
Leaflet vendora: - Fjerna CDN <link> for Leaflet CSS (allereie bundla via npm-import) - Kartmarkør-ikon brukar bundla bilete i staden for unpkg URL-ar - CSP stramma: unpkg ikkje lenger naudsynt i style-src/img-src Slett buffer: - «Slett lagra data»-knapp i om-dialogen - Slettar localStorage, IndexedDB og tenestararbeidar-bufferar - Lokalisert til en/nb/nn Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
97225d1e77
commit
4a95e0e23f
7 changed files with 62 additions and 8 deletions
|
|
@ -6,14 +6,11 @@
|
||||||
<meta name="theme-color" content="#1A1A2E" />
|
<meta name="theme-color" content="#1A1A2E" />
|
||||||
<meta name="description" content="Find the nearest public shelter in Norway" />
|
<meta name="description" content="Find the nearest public shelter in Norway" />
|
||||||
<meta http-equiv="Content-Security-Policy"
|
<meta http-equiv="Content-Security-Policy"
|
||||||
content="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline' https://unpkg.com; img-src 'self' data: https://*.tile.openstreetmap.org https://unpkg.com; connect-src 'self' https://*.tile.openstreetmap.org; font-src 'self'; worker-src 'self'" />
|
content="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https://*.tile.openstreetmap.org; connect-src 'self' https://*.tile.openstreetmap.org; font-src 'self'; worker-src 'self'" />
|
||||||
<title>Tilfluktsrom</title>
|
<title>Tilfluktsrom</title>
|
||||||
<link rel="manifest" href="/manifest.webmanifest" />
|
<link rel="manifest" href="/manifest.webmanifest" />
|
||||||
<link rel="icon" type="image/png" sizes="192x192" href="/icons/icon-192.png" />
|
<link rel="icon" type="image/png" sizes="192x192" href="/icons/icon-192.png" />
|
||||||
<link rel="apple-touch-icon" href="/icons/icon-192.png" />
|
<link rel="apple-touch-icon" href="/icons/icon-192.png" />
|
||||||
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css"
|
|
||||||
integrity="sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY="
|
|
||||||
crossorigin="anonymous" />
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="app">
|
<div id="app">
|
||||||
|
|
|
||||||
|
|
@ -71,4 +71,6 @@ export const en: Record<string, string> = {
|
||||||
about_open_source: 'Open source — kode.naiv.no/olemd/tilfluktsrom',
|
about_open_source: 'Open source — kode.naiv.no/olemd/tilfluktsrom',
|
||||||
action_about: 'About',
|
action_about: 'About',
|
||||||
action_close: 'Close',
|
action_close: 'Close',
|
||||||
|
action_clear_cache: 'Clear cached data',
|
||||||
|
cache_cleared: 'All cached data cleared',
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -66,4 +66,6 @@ export const nb: Record<string, string> = {
|
||||||
about_open_source: 'Åpen kildekode — kode.naiv.no/olemd/tilfluktsrom',
|
about_open_source: 'Åpen kildekode — kode.naiv.no/olemd/tilfluktsrom',
|
||||||
action_about: 'Om',
|
action_about: 'Om',
|
||||||
action_close: 'Lukk',
|
action_close: 'Lukk',
|
||||||
|
action_clear_cache: 'Slett lagrede data',
|
||||||
|
cache_cleared: 'Alle lagrede data slettet',
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -66,4 +66,6 @@ export const nn: Record<string, string> = {
|
||||||
about_open_source: 'Open kjeldekode — kode.naiv.no/olemd/tilfluktsrom',
|
about_open_source: 'Open kjeldekode — kode.naiv.no/olemd/tilfluktsrom',
|
||||||
action_about: 'Om',
|
action_about: 'Om',
|
||||||
action_close: 'Lukk',
|
action_close: 'Lukk',
|
||||||
|
action_clear_cache: 'Slett lagra data',
|
||||||
|
cache_cleared: 'Alle lagra data sletta',
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -458,6 +458,24 @@ html, body {
|
||||||
margin-bottom: 2px;
|
margin-bottom: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.about-clear-btn {
|
||||||
|
display: block;
|
||||||
|
margin: 12px 0 0;
|
||||||
|
padding: 8px 16px;
|
||||||
|
background: transparent;
|
||||||
|
border: 1px solid #90A4AE;
|
||||||
|
border-radius: 6px;
|
||||||
|
color: #90A4AE;
|
||||||
|
font-size: 13px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.about-clear-btn:disabled {
|
||||||
|
border-color: #4a6a5a;
|
||||||
|
color: #4a6a5a;
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
|
||||||
.about-close-btn {
|
.about-close-btn {
|
||||||
display: block;
|
display: block;
|
||||||
margin: 16px auto 0;
|
margin: 16px auto 0;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
/**
|
/**
|
||||||
* About dialog: app info, privacy statement, data sources, copyright.
|
* About dialog: app info, privacy statement, data sources, copyright.
|
||||||
|
* Includes a "Clear cached data" button that removes all local storage.
|
||||||
* Opens as a modal overlay, same pattern as loading-overlay.
|
* Opens as a modal overlay, same pattern as loading-overlay.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
@ -35,6 +36,13 @@ export function showAbout(): void {
|
||||||
content.appendChild(subheading(t('about_stored_title')));
|
content.appendChild(subheading(t('about_stored_title')));
|
||||||
content.appendChild(para(t('about_stored_body')));
|
content.appendChild(para(t('about_stored_body')));
|
||||||
|
|
||||||
|
// Clear cache button
|
||||||
|
const clearBtn = document.createElement('button');
|
||||||
|
clearBtn.className = 'about-clear-btn';
|
||||||
|
clearBtn.textContent = t('action_clear_cache');
|
||||||
|
clearBtn.addEventListener('click', () => clearAllData(clearBtn));
|
||||||
|
content.appendChild(clearBtn);
|
||||||
|
|
||||||
const footer = document.createElement('div');
|
const footer = document.createElement('div');
|
||||||
footer.className = 'about-footer';
|
footer.className = 'about-footer';
|
||||||
footer.appendChild(small(t('about_copyright')));
|
footer.appendChild(small(t('about_copyright')));
|
||||||
|
|
@ -63,6 +71,28 @@ export function hideAbout(): void {
|
||||||
previousFocus = null;
|
previousFocus = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Clear all cached data: IndexedDB, localStorage, and service worker caches. */
|
||||||
|
async function clearAllData(btn: HTMLButtonElement): Promise<void> {
|
||||||
|
btn.disabled = true;
|
||||||
|
|
||||||
|
// Clear localStorage (map cache metadata)
|
||||||
|
localStorage.clear();
|
||||||
|
|
||||||
|
// Clear IndexedDB (shelter database)
|
||||||
|
const dbs = await indexedDB.databases?.() ?? [];
|
||||||
|
for (const db of dbs) {
|
||||||
|
if (db.name) indexedDB.deleteDatabase(db.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear service worker caches (map tiles, precache)
|
||||||
|
const cacheNames = await caches.keys();
|
||||||
|
for (const name of cacheNames) {
|
||||||
|
await caches.delete(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
btn.textContent = t('cache_cleared');
|
||||||
|
}
|
||||||
|
|
||||||
function heading(text: string): HTMLElement {
|
function heading(text: string): HTMLElement {
|
||||||
const el = document.createElement('h2');
|
const el = document.createElement('h2');
|
||||||
el.textContent = text;
|
el.textContent = text;
|
||||||
|
|
|
||||||
|
|
@ -6,16 +6,19 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import L from 'leaflet';
|
import L from 'leaflet';
|
||||||
|
import markerIcon from 'leaflet/dist/images/marker-icon.png';
|
||||||
|
import markerIcon2x from 'leaflet/dist/images/marker-icon-2x.png';
|
||||||
|
import markerShadow from 'leaflet/dist/images/marker-shadow.png';
|
||||||
import type { Shelter, ShelterWithDistance, LatLon } from '../types';
|
import type { Shelter, ShelterWithDistance, LatLon } from '../types';
|
||||||
import { t } from '../i18n/i18n';
|
import { t } from '../i18n/i18n';
|
||||||
|
|
||||||
// Fix Leaflet default icon paths (broken by bundlers)
|
// Fix Leaflet default icon paths (broken by bundlers) — use bundled assets
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
delete (L.Icon.Default.prototype as any)._getIconUrl;
|
delete (L.Icon.Default.prototype as any)._getIconUrl;
|
||||||
L.Icon.Default.mergeOptions({
|
L.Icon.Default.mergeOptions({
|
||||||
iconUrl: 'https://unpkg.com/leaflet@1.9.4/dist/images/marker-icon.png',
|
iconUrl: markerIcon,
|
||||||
iconRetinaUrl: 'https://unpkg.com/leaflet@1.9.4/dist/images/marker-icon-2x.png',
|
iconRetinaUrl: markerIcon2x,
|
||||||
shadowUrl: 'https://unpkg.com/leaflet@1.9.4/dist/images/marker-shadow.png',
|
shadowUrl: markerShadow,
|
||||||
});
|
});
|
||||||
|
|
||||||
const DEFAULT_ZOOM = 14;
|
const DEFAULT_ZOOM = 14;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue