Badge-System: personal/general Split, Punkte ohne Zahl, SW by-v328

This commit is contained in:
rene 2026-04-24 08:39:41 +02:00
parent 0a3ad2207e
commit fc7d743153
4 changed files with 35 additions and 13 deletions

View file

@ -40,6 +40,35 @@ async def unread_count(user=Depends(get_current_user)):
return {"count": row[0]}
# ------------------------------------------------------------------
# GET /api/notifications/badge — Dot-Indikatoren für Header
# personal: Chat, Freunde, Trainer, Sitting, Gesundheit, Meilensteine
# general: Gassi-Treffen, Giftköder, Verlorene Hunde, Wetter
# ------------------------------------------------------------------
_PERSONAL = {
'chat_message', 'friend_request', 'weekly_praise',
'health_reminder', 'milestone',
'sitting_request', 'sitting_confirmed', 'sitting_cancelled',
}
_GENERAL = {
'walk_invite', 'lost_dog_alert', 'poison_alert',
'weather_heat', 'weather_thunder',
}
@router.get("/badge")
async def badge_status(user=Depends(get_current_user)):
with db() as conn:
rows = conn.execute(
"SELECT DISTINCT type FROM notifications WHERE user_id=? AND read_at IS NULL",
(user["id"],),
).fetchall()
types = {r["type"] for r in rows}
return {
"personal": bool(types & _PERSONAL),
"general": bool(types & _GENERAL),
}
# ------------------------------------------------------------------
# PATCH /api/notifications/read-all — alle als gelesen markieren
# ------------------------------------------------------------------

View file

@ -515,6 +515,7 @@ const API = (() => {
const notifications = {
list() { return get('/notifications'); },
unreadCount() { return get('/notifications/unread-count'); },
badge() { return get('/notifications/badge'); },
readAll() { return patch('/notifications/read-all', {}); },
read(id) { return patch(`/notifications/${id}/read`, {}); },
delete(id) { return del(`/notifications/${id}`); },

View file

@ -3,7 +3,7 @@
Router, State-Management, Navigation, Initialisierung.
============================================================ */
const APP_VER = '315'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen
const APP_VER = '316'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen
const App = (() => {
@ -470,13 +470,9 @@ const App = (() => {
async function _updateNotifBadge() {
if (!state.user) return;
try {
const { count } = await API.notifications.unreadCount();
// Sidebar-Badge (Zahl) — entfernt aus Sidebar, aber ID bleibt für Kompatibilität
const badge = document.getElementById('notif-badge');
if (badge) { badge.textContent = count; badge.style.display = count > 0 ? '' : 'none'; }
// Burger-Punkt
const navBadge = document.getElementById('notif-nav-badge');
if (navBadge) navBadge.classList.toggle('hidden', count === 0);
const b = await API.notifications.badge();
document.getElementById('notif-nav-badge')?.classList.toggle('hidden', !b.general);
document.getElementById('chat-nav-badge')?.classList.toggle('hidden', !b.personal);
} catch { /* ignorieren */ }
}
@ -485,12 +481,8 @@ const App = (() => {
try {
const convs = await API.chat.conversations();
const total = convs.reduce((s, c) => s + (c.unread_count || 0), 0);
// Sidebar-Badge (Zahl)
const badge = document.getElementById('chat-badge');
if (badge) { badge.textContent = total; badge.style.display = total > 0 ? '' : 'none'; }
// Avatar-Punkt
const navBadge = document.getElementById('chat-nav-badge');
if (navBadge) navBadge.classList.toggle('hidden', total === 0);
} catch { /* ignorieren */ }
}

View file

@ -3,7 +3,7 @@
Offline-Cache + Push Notifications + Tile-Cache
============================================================ */
const CACHE_VERSION = 'by-v327';
const CACHE_VERSION = 'by-v328';
const CACHE_STATIC = `${CACHE_VERSION}-static`;
const CACHE_TILES = 'ban-yaro-tiles-v1'; // bleibt über SW-Updates erhalten