UX: Step 4 wieder strikt + Long-Press auf FAB = Status-Modal, SW by-v1093

- Step 4 wieder strikt: alle 3 (expenses + routes + notes) müssen
  im Cache sein, sonst weiß. 5/5 Braun = wirklich alles bereit.
- Long-Press (600ms) auf #worlds-fab öffnet das Status-Modal mit
  detaillierter 5-Punkte-Checkliste + 'Fehlende nachladen'-Button.
  Normaler Click bleibt unverändert (FAB-Schnellaktion).
  Wenn Long-Press feuert, wird nachfolgender Click unterdrückt.
- _bindLongPress wird mehrfach gebunden falls FAB neu gerendert
  wird (data-lpBound verhindert Doppel-Binding).
This commit is contained in:
rene 2026-05-26 19:13:12 +02:00
parent 66d2d96a2f
commit 61af803d99
5 changed files with 43 additions and 18 deletions

View file

@ -410,7 +410,7 @@ async def serve_media(path: str, request: _Request):
raise _HE(404, "Nicht gefunden.")
return _media_response(filepath)
APP_VER = "1092" # muss mit APP_VER in app.js übereinstimmen
APP_VER = "1093" # muss mit APP_VER in app.js übereinstimmen
@app.get("/.well-known/assetlinks.json")
async def assetlinks():

View file

@ -101,9 +101,9 @@
</script>
<!-- CSS: Reihenfolge ist wichtig — ?v= zwingt Browser zur Neuladung -->
<link rel="stylesheet" href="/css/design-system.css?v=1092">
<link rel="stylesheet" href="/css/layout.css?v=1092">
<link rel="stylesheet" href="/css/components.css?v=1092">
<link rel="stylesheet" href="/css/design-system.css?v=1093">
<link rel="stylesheet" href="/css/layout.css?v=1093">
<link rel="stylesheet" href="/css/components.css?v=1093">
</head>
<body>
@ -625,11 +625,11 @@
<div id="modal-container"></div>
<!-- JS: Reihenfolge ist wichtig — erst Basis, dann Features -->
<script src="/js/api.js?v=1092"></script>
<script src="/js/ui.js?v=1092"></script>
<script src="/js/app.js?v=1092"></script>
<script src="/js/worlds.js?v=1092"></script>
<script src="/js/offline-indicator.js?v=1092"></script>
<script src="/js/api.js?v=1093"></script>
<script src="/js/ui.js?v=1093"></script>
<script src="/js/app.js?v=1093"></script>
<script src="/js/worlds.js?v=1093"></script>
<script src="/js/offline-indicator.js?v=1093"></script>
<!-- Feature-Seiten werden lazy geladen -->

View file

@ -3,7 +3,7 @@
Router, State-Management, Navigation, Initialisierung.
============================================================ */
const APP_VER = '1092'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen
const APP_VER = '1093'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen
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_VERSION = APP_VERSION;

View file

@ -63,12 +63,9 @@ window.OfflineIndicator = (() => {
const c = await caches.open(CACHE_API).catch(() => null);
if (!c) return false;
const urls = (await c.keys()).map(r => r.url);
const found = [
urls.some(u => u.includes('/api/expenses')),
urls.some(u => u.includes('/api/routes')),
urls.some(u => u.includes('/api/notes')),
].filter(Boolean).length;
return found >= 2; // 2 von 3 — toleriert wenn z.B. notes 401 lieferte
return urls.some(u => u.includes('/api/expenses'))
&& urls.some(u => u.includes('/api/routes'))
&& urls.some(u => u.includes('/api/notes'));
} },
{ step: 5, title: 'Karten-Kacheln',
@ -257,16 +254,44 @@ window.OfflineIndicator = (() => {
}
}
// Long-Press auf #worlds-fab → Status-Modal (normaler Click bleibt = FAB-Aktion)
function _bindLongPress() {
const fab = document.getElementById('worlds-fab');
if (!fab || fab.dataset.lpBound) return;
fab.dataset.lpBound = '1';
let timer = null;
let fired = false;
const start = () => {
fired = false;
clearTimeout(timer);
timer = setTimeout(() => { fired = true; openStatus(); }, 600);
};
const cancel = () => clearTimeout(timer);
fab.addEventListener('touchstart', start, { passive: true });
fab.addEventListener('touchend', cancel);
fab.addEventListener('touchmove', cancel);
fab.addEventListener('touchcancel',cancel);
fab.addEventListener('mousedown', start);
fab.addEventListener('mouseup', cancel);
fab.addEventListener('mouseleave', cancel);
// Wenn Long-Press gefeuert hat, normalen Click verhindern
fab.addEventListener('click', e => {
if (fired) { e.stopImmediatePropagation(); e.preventDefault(); fired = false; }
}, true);
}
function init() {
refresh();
_prefetchPages();
_prefetchTiles();
_prefetchData();
_bindLongPress();
// Mehrere Retries für hund-spezifische Daten — _appState.activeDog wird oft
// erst nach Login/Hunde-Load gesetzt, manchmal mehrere Sekunden nach Init
[2_000, 5_000, 10_000, 20_000].forEach(delay => {
setTimeout(() => { _prefetchData(); refresh(); }, delay);
setTimeout(() => { _prefetchData(); refresh(); _bindLongPress(); }, delay);
});
if (navigator.serviceWorker) {

View file

@ -4,7 +4,7 @@
============================================================ */
// ← EINZIGE Stelle für die Version — STATIC_ASSETS und CACHE_VERSION leiten sich ab
const VER = '1092';
const VER = '1093';
const CACHE_VERSION = `by-v${VER}`;
const CACHE_STATIC = `${CACHE_VERSION}-static`;
const CACHE_TILES = 'ban-yaro-tiles-v1'; // bleibt über SW-Updates erhalten