Offline-Fallbacks für diary, poison, map + SW-Erweiterung
- sw.js: /api/places, /api/breeder/map-markers, /api/gassi-zeiten in _CACHEABLE_GET; /api/lost/report und /api/walks in _QUEUEABLE - diary.js: localStorage-Cache pro Hund, Fallback bei Offline mit Toast - poison.js: localStorage-Cache, Fallback bei Offline mit Toast (sicherheitsrelevant) - map.js: POI-Cache (places/poison/breeders) in localStorage, Offline-Toast + Fallback auf gecachte Daten
This commit is contained in:
parent
0c0daaad6b
commit
53fcb61933
6 changed files with 222 additions and 14 deletions
|
|
@ -5,6 +5,45 @@
|
|||
|
||||
window.Page_walks = (() => {
|
||||
|
||||
// ----------------------------------------------------------
|
||||
// OFFLINE-CACHE
|
||||
// ----------------------------------------------------------
|
||||
const _CACHE_KEY = 'by_walks_cache';
|
||||
const _PENDING_KEY = 'by_walks_pending';
|
||||
|
||||
function _getPending() {
|
||||
try { return JSON.parse(localStorage.getItem(_PENDING_KEY) || '[]'); } catch { return []; }
|
||||
}
|
||||
function _setPending(list) {
|
||||
try { localStorage.setItem(_PENDING_KEY, JSON.stringify(list)); } catch {}
|
||||
}
|
||||
function _addPending(data) {
|
||||
const list = _getPending();
|
||||
const entry = { ...data, id: `pending_${Date.now()}`, _isPending: true,
|
||||
created_at: new Date().toISOString(),
|
||||
teilnehmer_count: 1, max_teilnehmer: data.max_teilnehmer || 10,
|
||||
status: 'open' };
|
||||
list.push(entry);
|
||||
_setPending(list);
|
||||
return entry;
|
||||
}
|
||||
async function _syncPending() {
|
||||
if (!navigator.onLine) return;
|
||||
const list = _getPending();
|
||||
if (!list.length) return;
|
||||
let ok = 0;
|
||||
for (const item of [...list]) {
|
||||
try {
|
||||
const { id: _pid, _isPending, created_at: _ca, teilnehmer_count: _tc, status: _st, ...payload } = item;
|
||||
await API.walks.create(payload);
|
||||
_setPending(_getPending().filter(x => x.id !== item.id));
|
||||
ok++;
|
||||
} catch {}
|
||||
}
|
||||
if (ok > 0) { UI.toast.success(`${ok} Treffen synchronisiert.`); _loadData(); }
|
||||
}
|
||||
window.addEventListener('online', _syncPending);
|
||||
|
||||
let _container = null;
|
||||
let _appState = null;
|
||||
let _data = [];
|
||||
|
|
@ -195,14 +234,16 @@ window.Page_walks = (() => {
|
|||
// Daten laden
|
||||
// ----------------------------------------------------------
|
||||
async function _loadData() {
|
||||
const pending = _getPending();
|
||||
try {
|
||||
_data = await API.walks.list(
|
||||
const fetched = await API.walks.list(
|
||||
_userPos?.lat ?? null,
|
||||
_userPos?.lon ?? null
|
||||
);
|
||||
try { localStorage.setItem(_CACHE_KEY, JSON.stringify({ ts: Date.now(), data: fetched })); } catch {}
|
||||
_data = [...pending, ...fetched];
|
||||
_renderList();
|
||||
_renderMarkers();
|
||||
// Desktop: Karte direkt initialisieren (beide Panels sichtbar)
|
||||
if (window.innerWidth >= 1024) {
|
||||
UI.loadLeaflet().then(() => {
|
||||
_initMap();
|
||||
|
|
@ -210,8 +251,20 @@ window.Page_walks = (() => {
|
|||
setTimeout(() => _map?.invalidateSize(), 400);
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
UI.toast.error(err.message || 'Fehler beim Laden.');
|
||||
} catch {
|
||||
try {
|
||||
const raw = localStorage.getItem(_CACHE_KEY);
|
||||
if (raw) {
|
||||
_data = [...pending, ...(JSON.parse(raw).data || [])];
|
||||
_renderList();
|
||||
_renderMarkers();
|
||||
UI.toast.info('Offline — zeige zuletzt geladene Treffen.');
|
||||
return;
|
||||
}
|
||||
} catch {}
|
||||
_data = pending;
|
||||
if (pending.length) { _renderList(); _renderMarkers(); return; }
|
||||
UI.toast.error('Treffen konnten nicht geladen werden.');
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -291,6 +344,7 @@ window.Page_walks = (() => {
|
|||
</span>
|
||||
<span class="walks-badge">${UI.icon('paw-print')} ${w.teilnehmer_count}/${w.max_teilnehmer}</span>
|
||||
${isOwn ? '<span class="walks-badge walks-badge--own">Mein Treffen</span>' : ''}
|
||||
${w._isPending ? `<span style="font-size:10px;color:var(--c-warning,#d97706);font-weight:600">⏳ Sync ausstehend</span>` : ''}
|
||||
</div>
|
||||
</div>
|
||||
<div style="display:flex;flex-direction:column;align-items:flex-end;gap:var(--space-1)">
|
||||
|
|
@ -1128,15 +1182,24 @@ window.Page_walks = (() => {
|
|||
const idx = _data.findIndex(w => w.id === walk.id);
|
||||
if (idx !== -1) _data[idx] = { ..._data[idx], ...updated };
|
||||
UI.toast.success('Treffen aktualisiert.');
|
||||
UI.modal.close();
|
||||
_renderList();
|
||||
_renderMarkers();
|
||||
} else {
|
||||
if (!navigator.onLine) {
|
||||
_addPending(payload);
|
||||
UI.modal.close();
|
||||
UI.toast.success('Offline gespeichert — wird synchronisiert sobald Verbindung besteht.');
|
||||
_loadData();
|
||||
return;
|
||||
}
|
||||
const created = await API.walks.create(payload);
|
||||
_data.unshift({ ...created, teilnehmer_count: 0 });
|
||||
UI.toast.success('Treffen geplant! 🎉');
|
||||
UI.modal.close();
|
||||
_renderList();
|
||||
_renderMarkers();
|
||||
}
|
||||
|
||||
UI.modal.close();
|
||||
_renderList();
|
||||
_renderMarkers();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue