Skip to content

notifications

Actionbot edited this page Oct 6, 2026 · 9 revisions

Modul: Notifications

Zweck

Zeigt die Liste der Benachrichtigungen (Likes, Follows, etc.) mit automatischer Aktualisierung.

Kontext

Notifications werden im Tab "Meldungen" angezeigt und in der BottomNavigation mit einem Badge versehen, wenn ungelesene vorhanden sind.

Wichtige Dateien

  • app/src/main/kotlin/de/traewelling/app/ui/screens/NotificationScreen.kt
  • app/src/main/kotlin/de/traewelling/app/viewmodel/NotificationViewModel.kt
  • app/src/main/kotlin/de/traewelling/app/viewmodel/NotificationController.kt

Verhalten

Lade-Prozess

  1. loadNotifications(refresh) lädt eine Seite
  2. loadMore() lädt weitere Seiten (Pagination via links.next)
  3. refresh() lädt die erste Seite mit Pull-to-Refresh

Refresh beendet den alten Ladeauftrag und erhöht die Generation; eine ältere Seite darf die neue Liste nicht überschreiben. Pagination wartet auf laufenden Refresh und dedupliziert nach Meldungs-ID. Einträge ohne brauchbare ID werden nicht als ausführbare Meldung übernommen. Sessionwechsel beendet das gesamte Feature-ViewModel über den Auth-Store.

NotificationViewModel bleibt der Android-Lifecycle-Adapter mit unverändertem Anwendungskonstruktor und öffentlicher Aktions-API. NotificationController hält die accountbezogenen Zustände und erhält einen CoroutineScope sowie ein API-Gateway. Dadurch lassen sich echte verzögerte Listen-, Lese- und Zählerantworten ohne Android-Instanz überprüfen.

Unread-Count Polling

  • Im init wird ein Coroutine gestartet, der alle 60 Sekunden refreshUnreadCount() aufruft
  • Der Badge in der NavigationBar zeigt die Anzahl

Ein Mutex serialisiert Zähleranfragen. Zusätzlich tragen sie eine eigene Requestgeneration und den Stand der Leseänderungsrevision. Beginnt inzwischen ein neuer Zählerauftrag oder eine Leseaktion, darf die alte Antwort den Badge nicht überschreiben. Das gilt auch für parallele normale Anfragen aus Polling und Listenladen ohne zwischenzeitliches Lesen. HTTP-Fehler erzeugen keine erfolgreiche Nullantwort.

Mark-Read Logik

  • markAsRead(notificationId): Optimistic Update + API-Aufruf
  • markAllAsRead(): Setzt alle auf gelesen
  • Tippen auf eine Meldung ruft nur für ungelesene Einträge markAsRead() auf. Es gibt dabei keine Zielnavigation zum Profil oder zur Fahrt.

Unbekannte oder bereits gelesene IDs verringern den Badge nicht. Pro Meldung ist höchstens ein Leseauftrag offen; markAllAsRead und einzelne Leseaufträge überschneiden sich nicht. Neue Listenantworten erhalten noch offene optimistische Lesemarkierungen. Nach erfolgreichem Leseauftrag bleibt die konkrete ID zusätzlich bestätigt überlagert, auch nachdem pendingReads entfernt wurde. Erst ein danach gestarteter GET mit serverseitigem readAt beendet diese zusätzliche Überlagerung. Ein früher begonnener GET kann eine erfolgreich gelesene Meldung damit nicht erneut als ungelesen anzeigen.

markAllAsRead überlagert und bestätigt ausschließlich die beim Start bekannten IDs. Ein während des offenen PUT geladener neuer Eintrag kann nach dem Servercommit entstanden sein und bleibt entsprechend der Serverantwort ungelesen. Der Startzeitpunkt eines GET belegt nicht den Zeitpunkt seines Serversnapshots. Nach erfolgreichem PUT startet genau eine aktuelle erste Seite und beendet beziehungsweise sperrt ältere Listenaufträge; zuvor nicht geladene Einträge werden aus diesem neuen Serverstand übernommen, ohne globale Gelesen-Markierung.

Bei API-Fehler werden nur die eigenen vorläufigen Markierungen zurückgenommen. Der Badge erhält den bisherigen Zähler und mindestens die jetzt sichtbaren ungelesenen Einträge, einschließlich neuer Meldungen während des PUT, auch wenn der anschließende Count-GET fehlschlägt.

NotificationControllerTest prüft mit virtueller Coroutine-Zeit und gezielt zurückgehaltenen API-Antworten verspätete GETs nach erfolgreichem Lesen, neue Meldungen nach markAllAsRead, Rollback neben einem anderen laufenden Leseauftrag, Zählerserialisierung und abgebrochene Refreshs. NotificationReadStateTest ergänzt die lokalen Mapping-/Rollbackregeln. Es werden keine Zeit-Sleeps verwendet; siehe Tests.

Notification-Typen

  • "Liked": Herz-Icon (Error-Farbe)
  • "Follow": PersonAdd-Icon (Primary-Farbe)
  • "Connection": Zug-Icon (Secondary-Farbe)
  • "Mention": AlternateEmail-Icon
  • Sonst: Notifications-Icon

UI-Darstellung

  • Lade-, Fehler- und Empty-States via StateMessage
  • Benachrichtigungen werden als abgerundete Cards dargestellt
  • Ungelesene Einträge erhalten einen farbigen linken Akzent, stärkere Elevation und ein Neu-Badge

UI-Zustand (NotificationUiState)

Feld Typ Beschreibung
notifications List Liste aller Benachrichtigungen
unreadCount Int Anzahl ungelesener
hasMore Boolean Weitere Seiten verfügbar
currentPage Int Aktuelle Seiten-Nummer

Abhängigkeiten

  • TraewellingRepository: getNotifications, markNotificationRead, markAllNotificationsRead, getUnreadNotificationCount
  • StateMessage: Einheitliche UI für Lade-, Fehler- und Empty-States

Offene Fragen

  • TODO: Meldungen mit auflösbaren Zielinformationen zum zugehörigen Profil oder Status navigieren lassen.

Verwandte Seiten

Clone this wiki locally