diff --git a/backend/routes/walks.py b/backend/routes/walks.py index 0c19987..85bea3d 100644 --- a/backend/routes/walks.py +++ b/backend/routes/walks.py @@ -252,6 +252,7 @@ async def invite_friend(walk_id: int, data: InviteRequest, user=Depends(get_curr "type": "walk_invite", "title": f"Einladung: {walk['titel']}", "body": f"{user['name']} lädt dich zu einem Gassi-Treffen ein ({walk['datum']} {walk['uhrzeit']})", + "page": "walks", "walk_id": walk_id, }) diff --git a/backend/static/js/app.js b/backend/static/js/app.js index 54d264f..69d7a07 100644 --- a/backend/static/js/app.js +++ b/backend/static/js/app.js @@ -3,7 +3,7 @@ Router, State-Management, Navigation, Initialisierung. ============================================================ */ -const APP_VER = '205'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen +const APP_VER = '206'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen const App = (() => { @@ -748,7 +748,13 @@ const App = (() => { // ÖFFENTLICHE API // (andere Module können App.state, App.navigate etc. nutzen) // ---------------------------------------------------------- - return { init, navigate, state, setActiveDog, renderDogSwitcher: _renderDogSwitcher, + function callModule(pageId, method, ...args) { + navigate(pageId); + setTimeout(() => pages[pageId]?.module?.[method]?.(...args), 500); + } + + return { init, navigate, callModule, state, setActiveDog, + renderDogSwitcher: _renderDogSwitcher, getInstallPrompt: () => _installPrompt, requireAuth, showOnboarding: _showOnboardingModal, updateNotifBadge: _updateNotifBadge }; diff --git a/backend/static/js/pages/notifications.js b/backend/static/js/pages/notifications.js index 6cf90bb..6737824 100644 --- a/backend/static/js/pages/notifications.js +++ b/backend/static/js/pages/notifications.js @@ -40,18 +40,60 @@ window.Page_notifications = (() => { try { return JSON.parse(raw); } catch (_) { return {}; } } - /** Ermittelt Ziel-Seite und optionale Label für den Toast */ + /** + * Gibt { page, label, go } zurück. + * go() führt die Navigation inklusive kontextspezifischer Aktion aus. + */ function _navTarget(n) { const d = _parseData(n.data); - // Explizit gesetzte Zielseite hat Vorrang - if (d.page) return { page: d.page, label: d.page }; - // Typ-basiertes Fallback + switch (n.type) { - case 'chat_message': return { page: d.conversation_id ? `chat?id=${d.conversation_id}` : 'chat', label: 'Chat' }; - case 'friend_request': return { page: 'friends', label: 'Freunde' }; - case 'milestone': return { page: 'diary', label: 'Tagebuch' }; - case 'poison_alert': return { page: 'map', label: 'Karte' }; - default: return { page: '', label: '' }; + case 'chat_message': + return { + page: 'chat', label: 'Chat', + go: d.conversation_id + ? () => App.callModule('chat', '_openThread', d.conversation_id) + : () => App.navigate('chat'), + }; + + case 'friend_request': + return { + page: 'friends', label: 'Freunde', + go: () => App.navigate('friends'), + }; + + case 'walk_invite': + return { + page: 'walks', label: 'Gassi-Treffen', + go: () => App.navigate('walks'), + }; + + case 'poison_alert': + return { + page: 'poison', label: 'Giftköder-Alarm', + go: () => App.navigate('poison'), + }; + + case 'health_reminder': + return { + page: 'health', label: 'Gesundheit', + go: () => App.navigate('health'), + }; + + case 'milestone': + return { + page: 'diary', label: 'Tagebuch', + go: () => App.navigate('diary'), + }; + + default: { + const page = d.page || ''; + return { + page, + label: page, + go: page ? () => App.navigate(page) : null, + }; + } } } @@ -61,9 +103,12 @@ window.Page_notifications = (() => { const iconName = unread ? _iconForType(n.type) : 'bell'; const cls = ['notif-item', unread ? 'notif-unread' : ''].filter(Boolean).join(' '); const nav = _navTarget(n); + // Nur serialisierbare Felder speichern (go ist eine Funktion, nicht serialisierbar) + const navData = JSON.stringify({ page: nav.page, label: nav.label, type: n.type, + data: _parseData(n.data) }); return ` -