6 Wochen uncommittete Arbeit (Voice-Channels, LiveKit-Manager, Settings-Modal u.v.m.) als ein WIP-Commit gesichert, damit nichts verloren geht und der Pi den aktuellen Stand klonen kann. Thematische Aufarbeitung: siehe ROADMAP M0. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CPrAGBxBT6GfPXzeWQ4AXb
18 KiB
Matrix / Element Feature Checklist für Pyramid
Stand: April 2026 — Pyramid v0.1.x (Early Development) Letzte Tiefenanalyse: 2026-04-29 (Vergleich mit FluffyChat / Element Web)
Legende
- ✅
[x]— Implementiert und funktioniert korrekt - ⚠️
[~]— Teilweise / fehlerhaft implementiert (Details in Technische Analyse) - ❌
[ ]— Fehlt komplett - 🔴 P0 — Kritisch (Kernfunktionalität kaputt)
- 🟠 P1 — Hoch (wichtige Funktion defekt oder nicht vorhanden)
- 🟡 P2 — Mittel
- 🔵 P3 — Niedrig / Nice-to-have
Nachrichten (Messaging)
Kern-Funktionen
- Textnachrichten senden
- Nachrichten empfangen & anzeigen
- Zeitstempel (formatiert, relativ)
- Nachrichtengruppierung (gleicher Sender, <5 Min.)
- Datumstrennlinien zwischen Gruppen
- Verschlüsselte Nachrichten anzeigen (Entschlüsselungsstatus)
- Nachrichtenverlauf paginieren (ältere Nachrichten beim Scrollen laden)
- Ungelesene-Nachrichten-Tracking
- [~] Nachrichtenbearbeitung (Edit-Event erkannt,
getDisplayEventwird nicht gerendert) 🟠 P1 - [~] Nachrichtenvorschau bei Reply (Zitat-Preview vorhanden, kein Jump-to-original) 🟡 P2
Nachrichtenaktionen
- Nachrichtentext kopieren
- Eigene Nachrichten löschen (redact)
- Nachrichten anpinnen / Pinboard anzeigen
- Auf Nachrichten antworten (in-reply-to)
- Nachrichten weiterleiten
- Zitat-Styling (visuelle Blockquote-Anzeige)
Textformatierung (formatted_body)
- [~] Markdown-Rendering —
formatted_body/org.matrix.custom.htmlwird nicht gesendet und nicht gerendert 🔵 P3 - Fett (text)
- Kursiv (text)
- Durchgestrichen (
text) - Inline-Code (
code) - Code-Block (
code) - Aufzählungslisten
- Klickbare Links
- [~] @Erwähnungen — Button vorhanden,
onTap: null🟡 P2 - #Kanal-Erwähnungen
- Spoiler-Tags (||text||)
Medien & Anhänge
- Dateianhänge senden (mit MIME-Erkennung)
- Bilder senden & inline anzeigen
- Videos senden & inline abspielen
- Audio senden & inline abspielen
- Sticker anzeigen (m.sticker)
- Dateianhang mit Untertitel
- Dateigröße berechnen & anzeigen
- Verschlüsselte Medien entschlüsseln & herunterladen
- Thumbnail-Generierung für Bilder/Videos
- [~] Sprachnachrichten (Struktur vorhanden, Wiedergabe-UI fehlt)
- Animierte GIFs
- Link-Vorschau (URL-Unfurling)
- Dokument-Vorschau (PDF, Office)
Reaktionen
- Emoji-Reaktionen senden
- Reaktionszähler anzeigen
- 6 häufigste Reaktionen als Schnellauswahl
- Vollständiger Emoji-Picker mit Suche
- Eigene Reaktion entfernen
- Anzeigen wer reagiert hat
- Mehrere Reaktionen pro Nachricht
- Kategoriebasierte Emoji-Organisation
Tipp-Indikatoren (Typing)
- [~]
room.setTyping()wird nirgendwo aufgerufen — Einstellung existiert, aber Composer sendet nie "tippt…" 🟡 P2 - "X tippt gerade"-Anzeige empfangen (kein Widget)
- Timeout nach Tipp-Ende
Threads
- [~] Thread-Button vorhanden (
onPressed: () {}— leer) 🔵 P3 - Thread aufklappen / eigene Ansicht
- Thread-Benachrichtigungen
- Thread als gelöst markieren
Lesebestätigungen (Read Receipts)
- Senden von
m.read-Receipts viaroom.setReadMarker() - Privacy-Toggle (kein Senden wenn deaktiviert)
- ✓-Icon (Nachricht gesendet/zugestellt) auf eigenen Nachrichten
- ✓✓-Icon (gelesen) in DMs
- Sende-Status (Uhr/Fehler/gesendet/gelesen)
- [~] ✓✓-Logik nur für DMs — in Gruppenräumen wird
receiptState.global.otherUsersnicht ausgewertet 🟡 P2 - [~] Off-by-one: Divider erscheint über der Gruppe die das
fullyRead-Event enthält, nicht darunter 🟡 P2 - Gruppen-Read-Receipt-Display (Seen-by-Avatare wie in FluffyChat) 🟡 P2
- Hover-Tooltip "Gelesen von: ..." auf ✓✓
- Notification-Cancellation:
cancelNotification(roomId)beim Setzen des Read-Markers 🟡 P2
„Neue Nachrichten" Trennlinie
_UnreadDivider-Widget korrekt gestylt_sessionFullyReadIdeinmalig beim Öffnen erfasst (stabil für die Session)- Keine Anzeige wenn Raum bereits gelesen
- [~] Position falsch — Divider über der Gruppe mit
fullyRead-Event statt darunter 🟡 P2 - Auto-Scroll zum Divider beim Öffnen eines Raums mit ungelesenen Nachrichten 🟡 P2
Presence / Zuletzt Online
_PresenceLineim DM-Header mit Live-Updates- 4s-Debounce gegen Sync-Spike-Fehlmeldungen
- Relative Zeitanzeige (gerade eben / vor Xmin / vor Xh)
- Guard: eigene Presence wird nie angezeigt
- Presence-Dot im Rooms-Panel und Members-Panel
setPresence(online/offline)in den Settings- [~] Kein Auto-Heartbeat —
setPresencenur in Settings, nie periodisch während Nutzung 🔵 P3 - [~] Kein
WidgetsBindingObserver— App-Lifecycle (Hintergrund/Vordergrund) löst kein Presence-Event aus 🔵 P3 - Presence-Dot in DM-Listen-Items (
room_list_item.dart) 🔵 P3
Benachrichtigungen
Local Notifications (App im Vordergrund)
- Android Local Notifications (
flutter_local_notifications) - Windows Toast-Notifications (
local_notifier) - Windows Taskbar-Badge (Progress-Error-Mode)
- Tap-Handler navigiert zum richtigen Raum (Android)
- Guards: nur fremde Nachrichten, nur aktive Rooms, nur
Membership.join notificationsBlockedProvider+ Banner inapp_shell.dart- [~] Alle 4 Notification-Prefs werden ignoriert —
notifDesktopProvider,notifSoundProvider,notifPreviewProvider,notifMentionOnlyDmProviderhaben keinen Effekt 🟠 P1 - [~] Android-Grouping ohne Summary-Notification 🔵 P3
Remote Push (App im Hintergrund / geschlossen)
- 🔴 Keine Remote-Push-Integration — FCM/UnifiedPush fehlt komplett, App muss laufen
- iOS APNs-Setup
- macOS-Notifications
- Android Icon-Badge (Zahlenbadge)
E2EE Verschlüsselungs-Pipeline
Implementiert
- Vodozemac (Rust-Olm) via Flutter-Isolate — modern, performant
- Bootstrap: SSSS, Cross-Signing, Online-Key-Backup
_decryptLegacyEvents— entschlüsselt ältere Events in-place- Device-Key-Preload vor erstem Senden
- Key-Verification UI (SAS Emoji, QR-Code, alle States)
- Megolm Session Export/Import (Element-kompatibles Format)
- Encrypted-Attachment-Handling
m.fully_read-Marker beim Timeline-Opentimeline.requestKeys(onlineKeyBackupOnly: false)
Fehlerhaft / Fehlend
- [~] "Entschlüsselt…"-Placeholder bleibt dauerhaft wenn Key fehlt — kein Recovery-Button 🟠 P1
- [~]
loadAllKeysFromRoomFehler werden silent geschluckt (catch (_) {}) 🟡 P2 - [~] Kein Incoming-Verification-Request-Handler —
client.onKeyVerificationRequestwird nicht abonniert 🔴 P0 - [~] Doppelte Bootstrap-Implementierung (page vs. dialog) mit leicht inkonsistenter Logik 🔵 P3
- Device-Trust-Indicator in der Timeline 🔵 P3
- Unverified-Device-Warning beim Senden 🔵 P3
- "Schlüssel anfordern"-Button bei dauerhaft verschlüsselten Events 🟠 P1
Suche
- Suchdialog öffnen (UI vorhanden)
- Server-seitige Volltextsuche via Matrix
/searchAPI - Filter: Alle / Medien / Personen
- Ergebnisse: Raum + Absender + Body + Zeitstempel
- Keyboard-Navigation in Ergebnissen
- 400ms Debounce
- Jump-to-Message bei Klick auf Suchergebnis
- Suche nach Absender filtern
- Filter nach Datum
- Suchergebnisse im Chat hervorheben
- Suchverlauf
Raumverwaltung
Grundfunktionen
- Raumliste anzeigen (sortiert nach letzter Aktivität)
- Räume beitreten
- Räume verlassen
- Raumname & Avatar anzeigen
- Verschlüsselungsstatus (Schloss-Icon)
- Einladungen annehmen / ablehnen
- Neuen Raum erstellen (UI-Einstiegspunkt)
- Raumname bearbeiten
- Raumthema/Beschreibung bearbeiten
- Raum-Avatar ändern
- Raumvisibilität (öffentlich / privat)
- Verlaufsvisibilität konfigurieren
Direktnachrichten (DMs)
- 1:1 DM-Räume anzeigen & öffnen
- Presence-Status des Gegenübers anzeigen
- Echtzeit-Presence-Updates
- Sprachanruf aus DM starten
- Videoanruf aus DM starten
- DM starten (neuen Chat beginnen)
Mitglieder & Rollen
- Mitgliederliste anzeigen
- Mitgliederanzahl anzeigen
- Avatar & Anzeigename der Mitglieder
- Nutzer in Raum einladen
- Nutzer aus Raum entfernen (Kick)
- Nutzer sperren (Ban)
- Power Levels bearbeiten
Spaces & Gemeinschaften
- Spaces in der Seitenleiste anzeigen
- Zwischen Spaces navigieren
- Space-Avatar & Banner anzeigen
- Räume nach Space filtern
- Unterkategorien (Sub-Spaces)
- Drag-and-drop Kanalreihenfolge für Admins
- Space-Verwaltungsdialog
- Neuen Space erstellen
- Räume zum Space hinzufügen / entfernen
- Space-Entdeckung (öffentliche Spaces)
Sprach- & Videoanrufe (LiveKit)
- Sprachkanal betreten / verlassen
- Mikrofon stummschalten / Deafen
- Teilnehmerliste im Sprachkanal
- Anruf-UI (Vollansicht + Mini-Widget)
- Videoanruf mit Kamera-Toggle
- Klingelton bei eingehenden Anrufen
- Anruf annehmen / ablehnen (UI)
- Anrufdauer-Timer
- Bildschirmfreigabe
Nutzereinstellungen & Profil
- Hell / Dunkel-Theme, Akzentfarbe, Eckradius, Dichte
- Eigene User-ID, Avatar (Initialen-Fallback), Anzeigename anzeigen
- Anzeigenamen & Avatar bearbeiten
- Passwort ändern
- Sitzungsverwaltung
- Konto deaktivieren
Auto-Update
- GitHub-Releases als Update-Quelle
- Versions-Vergleich (SemVer), Rate-Limiting, Update-Banner
- "Diese Version überspringen"-Option
- Automatischer Download & Installation
Plattform-spezifisch
Desktop
- Native Desktop-App, Tastenkürzel, Drag-and-Drop
- System-Tray-Integration
- Fenster-State speichern (Größe, Position)
Mobile
- Mobile App (Flutter), Drawer-Navigation
- Touch-optimiertes UI
- Mobile Push-Benachrichtigungen
Technische Tiefenanalyse (2026-04-29)
Vergleichsbasis: FluffyChat (krille-chan/fluffychat) und Element Web Format: Befund → betroffene Datei:Zeile → Referenz-Implementierung
IMPLEMENTIERUNGS-BACKLOG (nach Priorität)
🔴 P0 — Kritisch
[P0-1] Incoming Key-Verification-Request-Handler fehlt
Problem: Wenn ein anderes Gerät eine Verifikations-Anfrage (m.key.verification.request) an Pyramid schickt, gibt es keinen Listener. Die Anfrage läuft ab, ohne dass der Nutzer sie sieht. Das bedeutet: andere Geräte können Pyramid de facto nicht verifizieren — nur Pyramid kann aktiv verifizieren.
Betroffene Datei: lib/widgets/settings_modal.dart (kein onKeyVerificationRequest-Listener)
FluffyChat-Referenz: lib/pages/key_verification/key_verification_dialog.dart + client.onKeyVerificationRequest.stream.listen(...) in lib/pages/matrix.dart
Fix: Stream auf client.onKeyVerificationRequest in matrix_client.dart oder app_shell.dart subscriben und KeyVerificationDialog öffnen.
🟠 P1 — Hoch
[P1-1] Notification-Prefs werden ignoriert
Problem: notifDesktopProvider, notifSoundProvider, notifPreviewProvider, notifMentionOnlyDmProvider sind in settings_prefs.dart gespeichert, werden in notification_service.dart/_watchEvents() aber nie ausgelesen. Notifications erscheinen immer, unabhängig von Nutzereinstellungen.
Betroffene Datei: lib/core/notification_service.dart, Zeile 145–168
Fix: In _watchEvents() die Prefs vor showMessageNotification() prüfen.
[P1-2] Edits werden nicht gerendert
Problem: _isVisible() filtert Edit-Events korrekt heraus, aber event.getDisplayEvent(timeline) wird nirgendwo aufgerufen. Nachrichten zeigen immer die Originalversion, auch wenn sie via m.replace bearbeitet wurden. Ein (bearbeitet)-Label fehlt ebenfalls.
Betroffene Datei: lib/features/chat/message_group.dart — Event-Rendering-Bereich
FluffyChat-Referenz: event.getDisplayEvent(timeline) vor dem Rendern jedes Events
Fix: Beim Rendern von Events event.getDisplayEvent(timeline) statt event verwenden; (bearbeitet)-Label wenn event != displayEvent.
[P1-3] Encrypted-Placeholder bleibt dauerhaft — kein Recovery-Button
Problem: EventTypes.Encrypted zeigt "Nachricht wird entschlüsselt…" dauerhaft wenn die Entschlüsselung scheitert. Es gibt keinen Button zum Anfordern fehlender Keys.
Betroffene Datei: lib/features/chat/message_group.dart, Zeilen ~408–421
FluffyChat-Referenz: _requestMissingKey() mit room.client.sendToDeviceEncrypted(...) / SDK-requestKey()
Fix: Wenn event.status == EventStatus.error oder nach Timeout immer noch EventTypes.Encrypted: "Schlüssel anfordern"-Button anzeigen, der timeline.requestKeys() aufruft.
🟡 P2 — Mittel
[P2-1] Typing-Indikator: room.setTyping() nie aufgerufen
Problem: privacyTypingProvider existiert, chat_composer.dart ruft aber nie room.setTyping() auf. Andere Nutzer sehen nie "X tippt gerade…".
Betroffene Datei: lib/features/chat/chat_input.dart (oder chat_composer.dart)
FluffyChat-Referenz: onInputBarChanged → 2s Debounce → room.setTyping(true, timeout: 30000); bei Senden/Leerfeld: room.setTyping(false)
Fix: Im Composer-Input onChanged-Handler mit Debounce und room.setTyping() ergänzen.
[P2-2] Typing-Indikator: Kein Display-Widget
Problem: Kein Widget das room.typingUsers anzeigt.
Fix: _TypingIndicator-Widget in chat_view.dart unter der Nachrichtenliste einfügen, das room.onUpdate-Stream abonniert und room.typingUsers anzeigt.
[P2-3] Unread-Divider: Position off-by-one
Problem: Divider erscheint über der Gruppe die das fullyRead-Event enthält, nicht darunter. Das bereits-gelesene Event liegt unter dem "Neue Nachrichten"-Strich.
Betroffene Datei: lib/features/chat/chat_view.dart, Zeile ~715
Fix: Divider nach der Gruppe einsetzen, die das fullyReadId-Event enthält, nicht davor.
[P2-4] Unread-Divider: Kein Auto-Scroll beim Öffnen
Problem: Beim Öffnen eines Raums mit ungelesenen Nachrichten landet die View unten, nicht beim Divider.
FluffyChat-Referenz: Initialer scrollController.jumpTo() zum Index des fullyRead-Events nach dem ersten Frame.
Fix: In initState/WidgetsBinding.addPostFrameCallback zum Divider-Index springen wenn _sessionHasUnread.
[P2-5] Gruppen-Read-Receipts fehlen
Problem: ✓✓-Logik greift nur bei room.isDirectChat. In Gruppenräumen sehen eigene Nachrichten nie ✓✓.
FluffyChat-Referenz: SeenByRow zeigt Avatare aller Leser unter der letzten Nachricht.
Fix: Receipt-Prüfung auch in Gruppenräumen aktivieren; ggf. Avatar-Reihe unter letzter Nachricht.
[P2-6] Notification-Cancellation beim Lesen fehlt
Problem: Android-Notifications bleiben in der Benachrichtigungsleiste stehen, auch wenn der Chat geöffnet und die Nachrichten gelesen wurden.
FluffyChat-Referenz: backgroundPush?.cancelNotification(roomId) in _markRead()
Fix: In _markRead() in chat_view.dart die Android-Notification für roomId canceln.
🔵 P3 — Niedrig
[P3-1] Presence: Kein Auto-Heartbeat
Problem: setPresence(online) nur in Settings — Server setzt Client nach Timeout offline.
Fix: Periodischer Timer (alle 60s) der setPresence(online) sendet solange App im Vordergrund.
[P3-2] Presence: Kein App-Lifecycle-Hook
Problem: Kein WidgetsBindingObserver — kein unavailable bei App-Hintergrund.
Fix: WidgetsBindingObserver.didChangeAppLifecycleState in matrix_client.dart oder app_shell.dart.
[P3-3] Markdown/formatted_body nicht gerendert
Problem: sendTextEvent sendet nur plain text. Empfangene formatted_body-Felder werden nicht gerendert.
Fix: Empfang: flutter_html oder flutter_markdown für formatted_body; Senden: simple Markdown-Erkennung im Composer.
[P3-4] requestFuture() fehlt
Problem: Kein Forward-Pagination-Mechanismus. Nach Jump-to-Event in der Vergangenheit kein Weg zurück.
Fix: timeline.requestFuture() im Scroll-Listener für die Vorwärts-Richtung.
[P3-5] message_bubble.dart — ungenutzter Code
Problem: Veraltete WhatsApp-Style-Implementation koexistiert neben aktiver Discord-Style-Implementierung. Fix: Datei löschen oder auskommentieren.
Statistik
| Bereich | Implementiert | Fehlerhaft | Fehlend |
|---|---|---|---|
| Chat-Mechanik | Solide Basis | Edits, Typing, requestFuture |
Jump-to-quote, Threads, Edited-Badge |
| Read Receipts | DM-Logik ✓ | Gruppen, Off-by-one, Notif-Cancel | Seen-by-Avatare, Tooltip |
| Presence | Anzeige ✓ | Broadcasting, Lifecycle | Heartbeat, Dot in Raumliste |
| Benachrichtigungen | Local ✓ | Prefs ignoriert, Grouping | Remote Push, iOS, macOS |
| Unread-Divider | Konzept ✓ | Position, Auto-Scroll | — |
| E2EE | Stärkster Bereich | Incoming-Verif., Placeholder | Device-Trust-Indicator, Warn beim Senden |
| Suche | API-Basis ✓ | — | Jump-to-message, Filter |
Implementierungsgrad gesamt: ~42 % (inkl. Teilimplementierungen)
Implementierungs-Fortschritt
| ID | Beschreibung | Status |
|---|---|---|
| P0-1 | Incoming Key-Verification-Request-Handler | ✅ War bereits implementiert (app_shell.dart) |
| P1-1 | Notification-Prefs auswerten | ✅ Implementiert (notification_service.dart) |
| P1-2 | Edits rendern (getDisplayEvent) |
✅ Implementiert (message_group.dart) |
| P1-3 | Encrypted-Placeholder + Recovery-Button | ✅ Implementiert (message_group.dart) |
| P2-1 | Typing: room.setTyping() senden |
✅ Implementiert (chat_composer.dart) |
| P2-2 | Typing: Display-Widget | ✅ Implementiert (chat_view.dart — _TypingIndicator) |
| P2-3 | Unread-Divider: Position + leere fullyRead-Guard | ✅ Neu implementiert (chat_view.dart) |
| P2-4 | Unread-Divider: Auto-Scroll (Schätzung) | ✅ Implementiert (_scrollToUnread) |
| P2-5 | Gruppen-Read-Receipts (DM-Guard entfernt) | ✅ Implementiert (chat_view.dart) |
| P2-6 | Notification-Cancellation beim Lesen | ✅ Implementiert (chat_view.dart/_markRead) |
| P3-1 | Presence: Auto-Heartbeat (10s) | ✅ Implementiert (app_shell.dart) |
| P3-2 | Presence: App-Lifecycle-Hook | ✅ Implementiert (app_shell.dart — WidgetsBindingObserver) |
| P3-3 | HTML/Markdown (_MatrixHtmlText, _PlainLinkText) |
✅ Implementiert (message_group.dart) |
| P3-4 | requestFuture() |
✅ Implementiert (chat_view.dart/_onScroll) |
| P3-5 | message_bubble.dart entfernen |
⏳ Offen |