Compare commits
2 commits
8f13f4d38d
...
fd6be50762
| Author | SHA1 | Date | |
|---|---|---|---|
| fd6be50762 | |||
| 5337ddfa05 |
8 changed files with 89 additions and 19 deletions
2
VERSION
2
VERSION
|
|
@ -1 +1 @@
|
||||||
1211
|
1213
|
||||||
|
|
@ -86,14 +86,14 @@
|
||||||
<title>Ban Yaro</title>
|
<title>Ban Yaro</title>
|
||||||
|
|
||||||
<!-- Theme + theme-color Statusleiste vor CSS setzen -->
|
<!-- Theme + theme-color Statusleiste vor CSS setzen -->
|
||||||
<script src="/js/boot-early.js?v=1211"></script>
|
<script src="/js/boot-early.js?v=1213"></script>
|
||||||
|
|
||||||
<!-- CSS: Reihenfolge ist wichtig — ?v= zwingt Browser zur Neuladung -->
|
<!-- CSS: Reihenfolge ist wichtig — ?v= zwingt Browser zur Neuladung -->
|
||||||
<link rel="stylesheet" href="/css/design-system.css?v=1211">
|
<link rel="stylesheet" href="/css/design-system.css?v=1213">
|
||||||
<link rel="stylesheet" href="/css/layout.css?v=1211">
|
<link rel="stylesheet" href="/css/layout.css?v=1213">
|
||||||
<link rel="stylesheet" href="/css/components.css?v=1211">
|
<link rel="stylesheet" href="/css/components.css?v=1213">
|
||||||
<link rel="stylesheet" href="/css/utilities.css?v=1211">
|
<link rel="stylesheet" href="/css/utilities.css?v=1213">
|
||||||
<link rel="stylesheet" href="/css/lists.css?v=1211">
|
<link rel="stylesheet" href="/css/lists.css?v=1213">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
|
|
@ -617,11 +617,11 @@
|
||||||
<div id="modal-container"></div>
|
<div id="modal-container"></div>
|
||||||
|
|
||||||
<!-- JS: Reihenfolge ist wichtig — erst Basis, dann Features -->
|
<!-- JS: Reihenfolge ist wichtig — erst Basis, dann Features -->
|
||||||
<script src="/js/api.js?v=1211"></script>
|
<script src="/js/api.js?v=1213"></script>
|
||||||
<script src="/js/ui.js?v=1211"></script>
|
<script src="/js/ui.js?v=1213"></script>
|
||||||
<script src="/js/app.js?v=1211"></script>
|
<script src="/js/app.js?v=1213"></script>
|
||||||
<script src="/js/worlds.js?v=1211"></script>
|
<script src="/js/worlds.js?v=1213"></script>
|
||||||
<script src="/js/offline-indicator.js?v=1211"></script>
|
<script src="/js/offline-indicator.js?v=1213"></script>
|
||||||
|
|
||||||
<!-- Feature-Seiten werden lazy geladen -->
|
<!-- Feature-Seiten werden lazy geladen -->
|
||||||
|
|
||||||
|
|
@ -631,7 +631,7 @@
|
||||||
|
|
||||||
|
|
||||||
<!-- Boot: Offline-Banner + SW-Registration (extrahiert für CSP) -->
|
<!-- Boot: Offline-Banner + SW-Registration (extrahiert für CSP) -->
|
||||||
<script src="/js/boot.js?v=1211"></script>
|
<script src="/js/boot.js?v=1213"></script>
|
||||||
|
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
Router, State-Management, Navigation, Initialisierung.
|
Router, State-Management, Navigation, Initialisierung.
|
||||||
============================================================ */
|
============================================================ */
|
||||||
|
|
||||||
const APP_VER = '1211'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen
|
const APP_VER = '1213'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen
|
||||||
const APP_VERSION = '1.6.0'; // ← semantische Version, wird bei make release gesetzt
|
const APP_VERSION = '1.6.0'; // ← semantische Version, wird bei make release gesetzt
|
||||||
window.APP_VER = APP_VER; // global verfügbar für andere Module (z.B. offline-indicator)
|
window.APP_VER = APP_VER; // global verfügbar für andere Module (z.B. offline-indicator)
|
||||||
window.APP_VERSION = APP_VERSION;
|
window.APP_VERSION = APP_VERSION;
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,34 @@ window.OfflineIndicator = (() => {
|
||||||
return found ? await caches.open(found) : null;
|
return found ? await caches.open(found) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GL-Offline-Tiles-Modus (byt://-Vektorkacheln in IndexedDB) statt OSM-Raster.
|
||||||
|
function _offlineTilesMode() {
|
||||||
|
try { return localStorage.getItem('by_offline_tiles') === '1'; } catch (e) { return false; }
|
||||||
|
}
|
||||||
|
// Ist eine Offline-Region (Vektorkacheln) in IndexedDB gespeichert? (ohne MapOffline zu laden)
|
||||||
|
// WICHTIG: dasselbe Schema/Version wie map-offline.js anlegen — sonst legt ein versionsloses open()
|
||||||
|
// die DB leer an und MapOffline kann seine Stores nicht mehr erstellen.
|
||||||
|
function _offlineRegionStored() {
|
||||||
|
return new Promise(res => {
|
||||||
|
try {
|
||||||
|
const r = indexedDB.open('by-offline-tiles', 1);
|
||||||
|
r.onupgradeneeded = () => {
|
||||||
|
const d = r.result;
|
||||||
|
if (!d.objectStoreNames.contains('tiles')) d.createObjectStore('tiles');
|
||||||
|
if (!d.objectStoreNames.contains('meta')) d.createObjectStore('meta');
|
||||||
|
};
|
||||||
|
r.onsuccess = () => {
|
||||||
|
const db = r.result;
|
||||||
|
if (!db.objectStoreNames.contains('tiles')) { db.close(); return res(false); }
|
||||||
|
const cnt = db.transaction('tiles', 'readonly').objectStore('tiles').count();
|
||||||
|
cnt.onsuccess = () => { res(cnt.result > 0); db.close(); };
|
||||||
|
cnt.onerror = () => { res(false); db.close(); };
|
||||||
|
};
|
||||||
|
r.onerror = () => res(false);
|
||||||
|
} catch (e) { res(false); }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const CHECKS = [
|
const CHECKS = [
|
||||||
{ step: 1, title: 'App-Grundgerüst',
|
{ step: 1, title: 'App-Grundgerüst',
|
||||||
detail: 'CSS, Layout und Hauptmodule — die Basis',
|
detail: 'CSS, Layout und Hauptmodule — die Basis',
|
||||||
|
|
@ -69,8 +97,10 @@ window.OfflineIndicator = (() => {
|
||||||
} },
|
} },
|
||||||
|
|
||||||
{ step: 5, title: 'Karten-Kacheln',
|
{ step: 5, title: 'Karten-Kacheln',
|
||||||
detail: `Mindestens ${TILE_MIN} OSM-Tiles im Umkreis`,
|
detail: 'Karten für deine Gegend offline verfügbar',
|
||||||
probe: async () => {
|
probe: async () => {
|
||||||
|
// GL-Modus: gespeicherte Vektor-Region in IndexedDB (das alte OSM-Raster nutzt die GL-Karte nicht).
|
||||||
|
if (_offlineTilesMode()) return _offlineRegionStored();
|
||||||
const c = await caches.open(CACHE_TILES).catch(() => null);
|
const c = await caches.open(CACHE_TILES).catch(() => null);
|
||||||
if (!c) return false;
|
if (!c) return false;
|
||||||
return (await c.keys()).length >= TILE_MIN;
|
return (await c.keys()).length >= TILE_MIN;
|
||||||
|
|
@ -169,12 +199,30 @@ window.OfflineIndicator = (() => {
|
||||||
tasks.push(fetch('/api/routes').catch(() => {}));
|
tasks.push(fetch('/api/routes').catch(() => {}));
|
||||||
tasks.push(fetch('/api/notes').catch(() => {}));
|
tasks.push(fetch('/api/notes').catch(() => {}));
|
||||||
} else if (m.step === 5) {
|
} else if (m.step === 5) {
|
||||||
await _prefetchTiles();
|
if (_offlineTilesMode()) await _downloadOfflineRegion();
|
||||||
|
else await _prefetchTiles();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
await Promise.all(tasks);
|
await Promise.all(tasks);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GL-Offline: Vektor-Region (~5 km) um den aktuellen Standort in IndexedDB laden.
|
||||||
|
async function _downloadOfflineRegion() {
|
||||||
|
let pos = null;
|
||||||
|
try { pos = await API.getLocation(); } catch (e) {}
|
||||||
|
if (!pos) {
|
||||||
|
try {
|
||||||
|
const raw = localStorage.getItem(LS_LAST_POS);
|
||||||
|
if (raw) { const p = JSON.parse(raw); pos = { lat: p.lat, lon: p.lon }; }
|
||||||
|
} catch (e) {}
|
||||||
|
}
|
||||||
|
if (!pos) { UI.toast.warning('Standort nötig, um die Gegend offline zu speichern.'); return; }
|
||||||
|
try {
|
||||||
|
await UI.loadMapLibreUI();
|
||||||
|
if (window.MapOffline) await MapOffline.downloadAround(pos.lat, pos.lon, 5);
|
||||||
|
} catch (e) { console.warn('Offline-Region-Download fehlgeschlagen:', e); }
|
||||||
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
||||||
// Tile-URL-Berechnung (OSM, Subdomain 'a')
|
// Tile-URL-Berechnung (OSM, Subdomain 'a')
|
||||||
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
||||||
|
|
|
||||||
|
|
@ -1800,6 +1800,7 @@ const UI = (() => {
|
||||||
escape, escHtml, help, pageInfo,
|
escape, escHtml, help, pageInfo,
|
||||||
saveToAlbum,
|
saveToAlbum,
|
||||||
loadLeaflet,
|
loadLeaflet,
|
||||||
|
loadMapLibreUI,
|
||||||
leafletMarker,
|
leafletMarker,
|
||||||
locationPicker,
|
locationPicker,
|
||||||
map,
|
map,
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<meta name="color-scheme" content="light dark">
|
<meta name="color-scheme" content="light dark">
|
||||||
<script src="/js/landing-init.js?v=1211"></script>
|
<script src="/js/landing-init.js?v=1213"></script>
|
||||||
<title>Ban Yaro — Die Hunde-App für Deutschland, Österreich & Schweiz</title>
|
<title>Ban Yaro — Die Hunde-App für Deutschland, Österreich & Schweiz</title>
|
||||||
<meta name="description" content="Ban Yaro: Die kostenlose All-in-One Hunde-App für DACH. Tagebuch, Giftköder-Alarm, Training mit KI, Forum, Wurfbörse, Stammbaum, Inzucht-Check — DSGVO-konform, offline-fähig, ohne App Store.">
|
<meta name="description" content="Ban Yaro: Die kostenlose All-in-One Hunde-App für DACH. Tagebuch, Giftköder-Alarm, Training mit KI, Forum, Wurfbörse, Stammbaum, Inzucht-Check — DSGVO-konform, offline-fähig, ohne App Store.">
|
||||||
<meta name="keywords" content="Hunde App, Hunde Community, Wurfbörse, Züchter, Welpen kaufen, Stammbaum Hund, Inzuchtkoeffizient, Hundezucht, Impfpass Hund, Giftköder Alarm, Gassi Community, Hundetraining App, Hunde Forum, Hunde KI, Hundefilm Datenbank, Welpen Marktplatz">
|
<meta name="keywords" content="Hunde App, Hunde Community, Wurfbörse, Züchter, Welpen kaufen, Stammbaum Hund, Inzuchtkoeffizient, Hundezucht, Impfpass Hund, Giftköder Alarm, Gassi Community, Hundetraining App, Hunde Forum, Hunde KI, Hundefilm Datenbank, Welpen Marktplatz">
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
============================================================ */
|
============================================================ */
|
||||||
|
|
||||||
// ← EINZIGE Stelle für die Version — STATIC_ASSETS und CACHE_VERSION leiten sich ab
|
// ← EINZIGE Stelle für die Version — STATIC_ASSETS und CACHE_VERSION leiten sich ab
|
||||||
const VER = '1211';
|
const VER = '1213';
|
||||||
const CACHE_VERSION = `by-v${VER}`;
|
const CACHE_VERSION = `by-v${VER}`;
|
||||||
const CACHE_STATIC = `${CACHE_VERSION}-static`;
|
const CACHE_STATIC = `${CACHE_VERSION}-static`;
|
||||||
const CACHE_TILES = 'ban-yaro-tiles-v1'; // bleibt über SW-Updates erhalten
|
const CACHE_TILES = 'ban-yaro-tiles-v1'; // bleibt über SW-Updates erhalten
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,29 @@
|
||||||
# Offline-Karten (GL/Vektor) — Feature-Plan
|
# Offline-Karten (GL/Vektor) — Feature-Plan
|
||||||
|
|
||||||
**Status:** geplant (Umsetzung nach Tile-Build/Produktions-Rollout der GL-Karten).
|
**Status:** KERN UMGESETZT + headless verifiziert (2026-06-05, v1213), **flag-gated `by_offline_tiles` (Default AUS)** bis Gerätetest.
|
||||||
**Stand:** 2026-06-05. Autor: René + Claude (Design).
|
**Stand:** 2026-06-05. Autor: René + Claude (Design).
|
||||||
|
|
||||||
|
## Umsetzungsstand (2026-06-05)
|
||||||
|
**✅ Fertig + headless bewiesen:**
|
||||||
|
- `map-offline.js` (`window.MapOffline`): Region-Download (`downloadAround(lat,lon,radiusKm)`) → Vektorkacheln
|
||||||
|
z0–14 via `pmtiles.getZxy` (liefert bereits dekomprimierte MVT) + Glyphs in **IndexedDB** (`by-offline-tiles`).
|
||||||
|
`byt://`-MapLibre-Protokoll (IndexedDB-first, remote-Fallback). ~15 MB / 5 km (dekomprimiert).
|
||||||
|
- `map-gl-style.js` `build({offline})`: `byt`-Source statt `pmtiles://`. Flag `by_offline_tiles` (Default AUS).
|
||||||
|
- ui.js/map.js laden map-offline + registrieren `byt`. `UI.loadMapLibreUI` exportiert.
|
||||||
|
- Welten-FAB Segment 5: prüft im GL-Modus gespeicherte Region (nicht mehr OSM-Raster); „Fehlende nachladen"
|
||||||
|
stößt `MapOffline.downloadAround(GPS, 5km)` an.
|
||||||
|
- **Beweis:** Download 97 Tiles (5 km München) → Netz AUS → **1903 Features gerendert**, nicht geladene
|
||||||
|
Gegend (Paris) leer; Glyphs nötig (sonst lässt MapLibre offline die ganze Kachel fallen).
|
||||||
|
|
||||||
|
**🔲 Offen (Follow-ups):**
|
||||||
|
- **Gerätetest (iOS-PWA offline/IndexedDB)** → dann Flag-Default auf Staging-AN (analog `by_map_gl`).
|
||||||
|
- Download-Button auf der **Karte** (`map-offline-btn`) im GL-Modus auf `downloadAround(Karten-Center)` umbiegen
|
||||||
|
(bisher OSM-Raster-Prefetch).
|
||||||
|
- **Adaptives Lernen** (rollendes Vorausladen beim Aufzeichnen + Funkloch-Gedächtnis).
|
||||||
|
- **Bereichsauswahl / Routen-Korridor** (inkl. „Route offline speichern" aus routes.js `_openDetail`).
|
||||||
|
- **Glyph-Persistenz** über App-Updates (aktuell SW-Cache, wird bei Update gepurged) → in IndexedDB ablegen + via `byt://f/` servieren.
|
||||||
|
- Alten OSM-Raster-Prefetch (`offline-indicator.js _prefetchTiles`) entfernen, wenn Flag dauerhaft AN.
|
||||||
|
|
||||||
## Ziel
|
## Ziel
|
||||||
GL-Vektorkarten offline-tauglich machen — Kernszenario **Gassi/Wandern im Funkloch**.
|
GL-Vektorkarten offline-tauglich machen — Kernszenario **Gassi/Wandern im Funkloch**.
|
||||||
Selbst-zielend (cacht wo nötig, nicht überall), speichersparsam, ohne Nutzeraufwand.
|
Selbst-zielend (cacht wo nötig, nicht überall), speichersparsam, ohne Nutzeraufwand.
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue