Navigation, Karten-FABs, Nearby-Alerts
- Bottom-Nav neu: Karte | Routen | + | Tagebuch | Forum - + reduziert auf: Giftköder, Gassi-Treffen, Verlorener Hund - Notification-Badge auf User-Avatar (Header) - Nearby-Alerts: Nav-Leiste pulsiert rot/orange bei Giftköder/vermisstm Hund in 20km - SW postMessage bei poison/lost_alert → sofortiger Alert-Check - Karten-FABs: nur Marker setzen + Standort (Route aufzeichnen + Offline entfernt) - SW by-v272, APP_VER 262
This commit is contained in:
parent
5141ba9969
commit
65d1cf6c7f
7 changed files with 105 additions and 27 deletions
|
|
@ -176,6 +176,22 @@
|
|||
display: flex;
|
||||
align-items: stretch;
|
||||
box-shadow: 0 -2px 12px rgba(42, 31, 20, 0.08);
|
||||
transition: border-top-color 0.4s ease;
|
||||
}
|
||||
|
||||
@keyframes nav-alert-pulse {
|
||||
0%, 100% { box-shadow: 0 -2px 12px rgba(42,31,20,0.08); }
|
||||
50% { box-shadow: 0 -4px 20px var(--nav-alert-color, rgba(220,38,38,0.4)); }
|
||||
}
|
||||
#bottom-nav.alert-poison {
|
||||
border-top: 3px solid var(--c-danger);
|
||||
--nav-alert-color: rgba(220, 38, 38, 0.4);
|
||||
animation: nav-alert-pulse 2s ease-in-out infinite;
|
||||
}
|
||||
#bottom-nav.alert-lost {
|
||||
border-top: 3px solid #f59e0b;
|
||||
--nav-alert-color: rgba(245, 158, 11, 0.4);
|
||||
animation: nav-alert-pulse 2s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.nav-item {
|
||||
|
|
|
|||
|
|
@ -168,6 +168,11 @@
|
|||
style="width:18px;height:18px;color:var(--c-text-muted)">
|
||||
<use href="/icons/phosphor.svg#user"></use>
|
||||
</svg>
|
||||
<span id="notif-nav-badge" class="hidden"
|
||||
style="position:absolute;top:-3px;right:-3px;min-width:16px;height:16px;
|
||||
border-radius:8px;background:var(--c-danger);color:#fff;font-size:10px;
|
||||
font-weight:700;line-height:16px;text-align:center;padding:0 3px;
|
||||
pointer-events:none;z-index:1"></span>
|
||||
</button>
|
||||
<button class="header-menu-btn" id="header-menu-btn" aria-label="Menü"><svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#list"></use></svg></button>
|
||||
</header>
|
||||
|
|
@ -290,9 +295,9 @@
|
|||
|
||||
<!-- MOBILE BOTTOM NAVIGATION -->
|
||||
<nav id="bottom-nav" role="navigation" aria-label="Hauptnavigation">
|
||||
<div class="nav-item active" data-page="diary">
|
||||
<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#book-open"></use></svg>
|
||||
<span class="nav-item-label">Tagebuch</span>
|
||||
<div class="nav-item" data-page="map">
|
||||
<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#map-trifold"></use></svg>
|
||||
<span class="nav-item-label">Karte</span>
|
||||
</div>
|
||||
<div class="nav-item" data-page="routes">
|
||||
<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#path"></use></svg>
|
||||
|
|
@ -302,17 +307,14 @@
|
|||
<div class="nav-item nav-item-center" id="nav-add">
|
||||
<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#plus"></use></svg>
|
||||
</div>
|
||||
<div class="nav-item active" data-page="diary">
|
||||
<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#book-open"></use></svg>
|
||||
<span class="nav-item-label">Tagebuch</span>
|
||||
</div>
|
||||
<div class="nav-item" data-page="forum">
|
||||
<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#push-pin"></use></svg>
|
||||
<span class="nav-item-label">Forum</span>
|
||||
</div>
|
||||
<div class="nav-item" data-page="notifications">
|
||||
<span style="position:relative;display:inline-flex">
|
||||
<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#bell"></use></svg>
|
||||
<span class="nav-badge hidden" id="notif-nav-badge">0</span>
|
||||
</span>
|
||||
<span class="nav-item-label">Aktuelles</span>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
</div><!-- #app -->
|
||||
|
|
@ -376,6 +378,11 @@
|
|||
navigator.serviceWorker.addEventListener('controllerchange', () => {
|
||||
window.location.reload();
|
||||
});
|
||||
navigator.serviceWorker.addEventListener('message', e => {
|
||||
if (e.data?.type === 'CHECK_NEARBY_ALERTS') {
|
||||
window.App?._checkNearbyAlerts?.();
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
Router, State-Management, Navigation, Initialisierung.
|
||||
============================================================ */
|
||||
|
||||
const APP_VER = '261'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen
|
||||
const APP_VER = '262'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen
|
||||
|
||||
const App = (() => {
|
||||
|
||||
|
|
@ -361,17 +361,14 @@ const App = (() => {
|
|||
</button>`;
|
||||
|
||||
UI.modal.open({
|
||||
title: 'Was möchtest du hinzufügen?',
|
||||
title: 'Schnellmeldung',
|
||||
body: `
|
||||
<div class="flex flex-col gap-3">
|
||||
${authBtn('diary', 'btn-secondary', 'book-open', 'Tagebuch-Eintrag')}
|
||||
${authBtn('health', 'btn-secondary', 'syringe', 'Gesundheits-Eintrag')}
|
||||
${authBtn('chat', 'btn-secondary', 'chat-circle-dots','Neue Nachricht')}
|
||||
${authBtn('forum', 'btn-secondary', 'push-pin', 'Forenbeitrag erstellen')}
|
||||
<button class="btn btn-danger w-full" data-quick="poison">
|
||||
<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#warning-octagon"></use></svg> Giftköder melden
|
||||
</button>
|
||||
${authBtn('walk', 'btn-nature', 'paw-print', 'Gassi-Treffen erstellen')}
|
||||
${authBtn('walk', 'btn-secondary', 'paw-print', 'Gassi-Treffen erstellen')}
|
||||
${authBtn('lost', 'btn-secondary', 'magnifying-glass','Verlorener Hund melden')}
|
||||
</div>
|
||||
${!loggedIn ? `<p style="font-size:var(--text-xs);color:var(--c-text-muted);text-align:center;margin-top:var(--space-3)">
|
||||
<svg class="ph-icon" style="width:12px;height:12px" aria-hidden="true"><use href="/icons/phosphor.svg#info"></use></svg>
|
||||
|
|
@ -391,12 +388,9 @@ const App = (() => {
|
|||
// Ohne Delay trifft es das neu geöffnete Modal und schließt es sofort.
|
||||
setTimeout(() => {
|
||||
if (action.startsWith('auth-')) { navigate('settings'); return; }
|
||||
if (action === 'diary') { navigate('diary'); pages['diary'].module?.openNew?.(); }
|
||||
if (action === 'health') { navigate('health'); pages['health'].module?.openNew?.(); }
|
||||
if (action === 'poison') { navigate('poison'); pages['poison'].module?.openNew?.(); }
|
||||
if (action === 'walk') { navigate('walks'); pages['walks'].module?.openNew?.(); }
|
||||
if (action === 'chat') { navigate('chat'); setTimeout(() => pages['chat'].module?._showNewMessagePicker?.(), 400); }
|
||||
if (action === 'forum') { navigate('forum'); setTimeout(() => pages['forum'].module?.openNew?.(), 400); }
|
||||
if (action === 'lost') { navigate('lost'); setTimeout(() => pages['lost'].module?.openNew?.(), 400); }
|
||||
}, 350);
|
||||
}, { once: true });
|
||||
}
|
||||
|
|
@ -434,11 +428,14 @@ const App = (() => {
|
|||
|
||||
_updateNotifBadge();
|
||||
_updateChatBadge();
|
||||
_checkNearbyAlerts();
|
||||
setInterval(() => { _updateNotifBadge(); _updateChatBadge(); }, 30_000);
|
||||
setInterval(_checkNearbyAlerts, 5 * 60_000);
|
||||
document.addEventListener('visibilitychange', () => {
|
||||
if (document.visibilityState === 'visible') {
|
||||
_updateNotifBadge();
|
||||
_updateChatBadge();
|
||||
_checkNearbyAlerts();
|
||||
if (state.page === 'chat') {
|
||||
pages['chat']?.module?.refresh?.();
|
||||
}
|
||||
|
|
@ -452,6 +449,22 @@ const App = (() => {
|
|||
}
|
||||
}
|
||||
|
||||
async function _checkNearbyAlerts() {
|
||||
const nav = document.getElementById('bottom-nav');
|
||||
if (!nav) return;
|
||||
try {
|
||||
const pos = await new Promise((resolve, reject) =>
|
||||
navigator.geolocation.getCurrentPosition(resolve, reject, { timeout: 5000, maximumAge: 120_000 })
|
||||
);
|
||||
const { latitude: lat, longitude: lon } = pos.coords;
|
||||
const data = await API.get(`/alerts?lat=${lat}&lon=${lon}`);
|
||||
nav.classList.toggle('alert-poison', !!data.poison);
|
||||
nav.classList.toggle('alert-lost', !data.poison && !!data.lost);
|
||||
} catch {
|
||||
// Kein Standort verfügbar — kein Alert anzeigen
|
||||
}
|
||||
}
|
||||
|
||||
async function _updateNotifBadge() {
|
||||
if (!state.user) return;
|
||||
try {
|
||||
|
|
@ -820,7 +833,8 @@ const App = (() => {
|
|||
renderDogSwitcher: _renderDogSwitcher,
|
||||
getInstallPrompt: () => _installPrompt, requireAuth,
|
||||
showOnboarding: _showOnboardingModal,
|
||||
updateNotifBadge: _updateNotifBadge };
|
||||
updateNotifBadge: _updateNotifBadge,
|
||||
_checkNearbyAlerts };
|
||||
|
||||
})();
|
||||
|
||||
|
|
|
|||
|
|
@ -191,8 +191,6 @@ window.Page_map = (() => {
|
|||
</div>
|
||||
|
||||
<div class="map-fabs">
|
||||
<button class="map-fab map-fab--rec" id="map-rec-btn" title="Route aufzeichnen"><svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#path"></use></svg></button>
|
||||
<button class="map-fab map-fab--offline" id="map-offline-btn" title="Karte offline speichern"><svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#floppy-disk"></use></svg></button>
|
||||
<button class="map-fab map-fab--pin" id="map-pin-btn" title="Marker setzen"><svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#push-pin"></use></svg></button>
|
||||
<button class="map-fab" id="map-locate-btn" title="Meinen Standort"><svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#map-pin"></use></svg></button>
|
||||
</div>
|
||||
|
|
@ -270,8 +268,6 @@ window.Page_map = (() => {
|
|||
});
|
||||
|
||||
document.getElementById('map-pin-btn').addEventListener('click', _togglePlacementMode);
|
||||
document.getElementById('map-offline-btn').addEventListener('click', _cacheTiles);
|
||||
document.getElementById('map-rec-btn').addEventListener('click', _toggleRecording);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
Offline-Cache + Push Notifications + Tile-Cache
|
||||
============================================================ */
|
||||
|
||||
const CACHE_VERSION = 'by-v271';
|
||||
const CACHE_VERSION = 'by-v272';
|
||||
const CACHE_STATIC = `${CACHE_VERSION}-static`;
|
||||
const CACHE_TILES = 'ban-yaro-tiles-v1'; // bleibt über SW-Updates erhalten
|
||||
|
||||
|
|
@ -223,6 +223,13 @@ self.addEventListener('push', event => {
|
|||
event.waitUntil(
|
||||
self.registration.showNotification(data.title || 'Ban Yaro', options)
|
||||
);
|
||||
|
||||
// App informieren damit sie Nearby-Alerts neu prüft
|
||||
if (data.type === 'poison_alert' || data.type === 'lost_dog_alert') {
|
||||
self.clients.matchAll({ type: 'window' }).then(clients => {
|
||||
clients.forEach(c => c.postMessage({ type: 'CHECK_NEARBY_ALERTS' }));
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// ----------------------------------------------------------
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue