149 lines
9.7 KiB
Markdown
149 lines
9.7 KiB
Markdown
# Team-Chat: v1-Produkt- und UX-Vertrag
|
|
|
|
**Stand:** 2026-07-15
|
|
|
|
**Ticket:** `AK-P15b` / HIVEGRID-45
|
|
|
|
**Architektur:** Bespoke WebSocket-Chat nach AD-9, kein Hocuspocus
|
|
|
|
Dieses Dokument konkretisiert die bereits beauftragten Chat-Funktionen als belastbaren Produktvertrag. Es
|
|
trennt notwendige Ausführungsqualität von späteren Erweiterungen und verhindert, dass die Interim-UI einen
|
|
technisch vollständigen, aber im Alltag hakeligen Chat verdeckt.
|
|
|
|
## 1. Produktprinzipien
|
|
|
|
1. **Sofort reagieren, serverseitig entscheiden.** Senden, Reactions und Pins erscheinen optimistisch. Die
|
|
Datenbank bleibt autoritativ; Fehler bleiben am betroffenen Element sichtbar und können dort erneut versucht
|
|
werden.
|
|
2. **Kein Verlust und keine Duplikate.** Jeder Schreibvorgang besitzt vor dem ersten Versuch eine stabile
|
|
Client-ID. Wiederholungen mit derselben ID liefern dasselbe Resultat.
|
|
3. **Realtime ist ein Beschleuniger, nicht die Datenbank.** Redis verteilt Events; nach Reconnect werden Lücken
|
|
per Cursor aus PostgreSQL geschlossen.
|
|
4. **Ruhe beim Lesen.** Neue Nachrichten reißen Nutzer nicht ans Listenende. Wer hochgescrollt hat, sieht einen
|
|
Hinweis mit Anzahl und Sprungaktion.
|
|
5. **Mobile und Tastatur sind Primärpfade.** Alle Aktionen funktionieren per Touch, Tastatur und Screenreader.
|
|
|
|
## 2. Nachrichten- und Reconnect-Vertrag
|
|
|
|
### Optimistisches Senden
|
|
|
|
- UI-Zustände: `sending → sent` oder `sending → failed`. Einen erfundenen gerätebezogenen
|
|
`delivered`-Zustand gibt es in v1 nicht.
|
|
- `clientMessageId` wird vor dem ersten Versuch erzeugt und bei Retry wiederverwendet. Der Server hält
|
|
`(tenantId, authorUserId, clientMessageId)` eindeutig und antwortet bei Wiederholung mit derselben Nachricht.
|
|
- Die Local-Echo-Nachricht erscheint sofort mit dezentem Sendestatus. Server-Antwort und WebSocket-Echo werden
|
|
gegen die Client-ID abgeglichen und dürfen weder doppelte Zeilen noch doppelte Reaktionen erzeugen.
|
|
- Fehler entfernen den Text nicht. Direkt an der Nachricht stehen „Erneut versuchen" und „Verwerfen"; kein
|
|
Toast-Spam. Automatischer Retry gilt nur für Timeout, `429` mit `Retry-After` und `5xx`, mit Backoff und Jitter.
|
|
- Innerhalb eines Channels/Threads bleibt die Reihenfolge stabil; unabhängige Channels dürfen parallel senden.
|
|
|
|
### Verbindung und Gap-Fill
|
|
|
|
- Sichtbare Zustände: `connecting`, `online`, `reconnecting`, `offline`, `blocked`.
|
|
- Nach Reconnect sendet der Client den letzten bestätigten Cursor und erhält verpasste persistente Events in
|
|
stabiler Reihenfolge. Erst danach gilt die Verbindung als vollständig synchronisiert.
|
|
- Presence, Typing und Transportstatus sind flüchtig und werden nicht als Chat-Historie gespeichert.
|
|
- Queue-Grenzen und `WebSocket.bufferedAmount` werden überwacht, weil der klassische Browser-WebSocket keine
|
|
Backpressure bereitstellt.
|
|
- Vollständiges Offline-Senden über Reload oder Background Sync ist nicht Teil von v1. Entwürfe bleiben lokal
|
|
erhalten; ein offline ausgelöster Send bleibt sichtbar fehlgeschlagen und kann nach Reconnect wiederholt
|
|
werden.
|
|
|
|
## 3. Lesen, Scrollen und Entwürfe
|
|
|
|
- History wird cursorbasiert paginiert. Beim Nachladen älterer Nachrichten bleibt der visuelle Scroll-Anker
|
|
stabil.
|
|
- Automatisches Scrollen ans Ende erfolgt nur, wenn der Nutzer bereits am Ende ist oder selbst sendet. Sonst
|
|
erscheint „N neue Nachrichten".
|
|
- Read-Cursor sind monoton. Eine Nachricht wird nur als gelesen gewertet, wenn Channel und Dokument aktiv sind
|
|
und die Nachricht sichtbar war; Updates werden gebündelt. Eigene Nachrichten erzeugen kein Unread.
|
|
- Channel zeigen getrennt Gesamt-Unread und Mention-Marker. Mute unterdrückt Benachrichtigungen, aber niemals
|
|
Unread; direkte User-Mentions und DMs umgehen Mute gemäß aktivem Chat-Vertrag (`AK-P15b`).
|
|
- Der Composer speichert Textentwürfe debounced pro `Tenant + User + Channel` lokal und tenant-isoliert. Ein
|
|
Entwurf wird erst nach erfolgreichem Server-Ack gelöscht. Datei-Binaries landen nicht im Local Storage.
|
|
|
|
## 4. Composer und Nachrichten-QoL
|
|
|
|
- `Enter` sendet, `Shift+Enter` erzeugt eine neue Zeile. Während IME-Composition darf `Enter` nicht senden.
|
|
- Markdown bleibt auf den freigegebenen Umfang beschränkt und wird serverseitig sanitisiert.
|
|
- Eigene Nachrichten können innerhalb von 24 Stunden editiert werden; Änderungen tragen einen
|
|
„bearbeitet"-Marker. Löschen erzeugt einen Tombstone, damit Threads und Antworten verständlich bleiben.
|
|
- Hover-Aktionen sind identisch per Fokus und Mobile-Long-Press erreichbar: Reaktion, Thread, Bearbeiten,
|
|
Löschen, Pin und Als-Aufgabe-markieren gemäß Berechtigung.
|
|
- Mentions verwenden eine zugängliche Combobox mit Pfeiltasten, `Enter` und `Escape`. Autocomplete und
|
|
Benachrichtigung zeigen beziehungsweise adressieren nur Nutzer, die den Channel lesen dürfen.
|
|
- Reactions werden optimistisch gesetzt und bei Fehler zurückgerollt. Gleiche Reaktion eines Users auf dieselbe
|
|
Nachricht ist eindeutig.
|
|
- Voice Notes sind einfache Audio-Anhänge mit Aufnahme-/Upload-Fortschritt, Dauer, Abspielen, Abbrechen und
|
|
Retry. Es gibt kein Transkript und keine KI-Verarbeitung.
|
|
- Uploads nutzen die bestehende Files-Pipeline inklusive AV-Prüfung. Bilder unterstützen Vorschau und
|
|
verständlichen Alternativtext; Mobile bietet den direkten Kamera-Input.
|
|
|
|
## 5. Presence, Typing, Threads und Pins
|
|
|
|
- Typing startet erst nach echter Eingabe, wird gedrosselt, läuft nach kurzer TTL aus und endet bei Senden,
|
|
Leeren, Channelwechsel oder Disconnect. Die UI zeigt höchstens zwei Namen plus „N weitere".
|
|
- Presence wird aus aktiven Sessions abgeleitet und läuft über TTL aus; sie ist kein belastbarer
|
|
Arbeitszeitnachweis.
|
|
- Threads haben genau eine Ebene. Desktop nutzt ein Seitenpanel, Mobile eine eigene Detailansicht. Wer erstellt,
|
|
antwortet oder erwähnt wird, folgt dem Thread automatisch.
|
|
- Message-Pins sind in Group- und Projekt-Channels auf 50 begrenzt. Nach bestätigtem Produktentscheid gibt es
|
|
in DMs keine Message-Pins. Der persönliche Channel-Pin bleibt ein getrenntes Modell.
|
|
|
|
## 6. Benachrichtigungen und bestätigte v1-Grenzen
|
|
|
|
- Mentions, DMs und Thread-Antworten laufen über `notify()` und öffnen per Deep Link die exakte Nachricht.
|
|
- Der allgemeine Browser-Web-Push inklusive Permission-Prompt bleibt bei `VA-P27a`; Chat-v1 liefert In-App-
|
|
Notifications und Deep Links.
|
|
- Tenant-Admin-Audit liegt unter `/settings/chat/audit`. Eine kurzlebige Audit-Session verlangt einen Grund;
|
|
jeder fremde Lesezugriff wird auditiert und DMs zeigen den Audit-Banner.
|
|
- `FileEntityType` wird minimal um `chat_message` erweitert, damit Anhänge die bestehende `FileEntityLink`-
|
|
Pipeline nutzen und kein paralleles Datei-System entsteht.
|
|
|
|
## 7. Accessibility und Mobile
|
|
|
|
- Die Nachrichtenliste ist ein benanntes `role="log"` ohne pauschales Live-Announcement. Separate,
|
|
dosierte `aria-live="polite"`-Statusbereiche melden nur neue fremde Nachrichten; initial geladene Historie
|
|
und eigene Local Echoes werden nicht doppelt angesagt.
|
|
- Verbindungs- und Sendestatus verwenden einen separaten Statusbereich und nie nur Farbe.
|
|
- Fokus bleibt nach Send im Composer; Drawer/Dialog geben Fokus kontrolliert zurück. Channel-, Message- und
|
|
Picker-Listen sind vollständig per Tastatur bedienbar.
|
|
- Mobile verwendet Master/Detail-Navigation, mindestens 44 Pixel große Touch-Ziele, Safe-Area am sticky
|
|
Composer und behält Draft sowie Scrollposition beim Channelwechsel.
|
|
|
|
## 8. Wiederverwendung des HiveGrid-UI-Baukastens
|
|
|
|
| Fläche | Vorhandene Bausteine | Neuer chat-spezifischer Verbund |
|
|
|---|---|---|
|
|
| Shell | `SubframeAppShell`, `PageShell`, `SidebarInside`, `ResizablePanel` | `ChatWorkspace` |
|
|
| Channel-Liste | `SearchInput`, `Avatar`, `BadgeFeedback`, `DropdownMenu`, `ContextMenu`, `Tooltip` | `ChatChannelRow` |
|
|
| Verlauf | `Avatar`, `Button`, `IconButton`, `ContextMenu`, `ScrollArea`, `Skeleton` | `ChatMessageList`, `ChatMessageRow` |
|
|
| Composer | `TextArea`, `IconButton`, `Progress`, `BannerOffline` | `ChatComposer`, `ChatAttachmentTray` |
|
|
| Threads | `Drawer`, `Dialog`, `Accordion` | `ThreadDrawer` |
|
|
| Mentions/Suche | `SearchInput`, `Command`, `Avatar` | `MentionAutocomplete`, `ChatSearchResults` |
|
|
| States | `EmptyState`, `Alert`, `BannerOffline`, `Skeleton`, `LoadingIndicatorSkeleton` | nur Chat-Kompositionen |
|
|
| Medien | `uploadFileInChunks`, `AudioOrVideoPlayer`, `Progress` | Voice-Note- und Attachment-Steuerung |
|
|
|
|
Die bestehenden visuellen Primitives werden direkt genutzt. Nicht wiederverwendet werden die rein visuellen,
|
|
nicht interaktiven `FileUpload`-/`Combobox`-Attrappen und der für Chat zu schwere Legacy-Rich-Text-Editor.
|
|
|
|
## 9. Bewusst später
|
|
|
|
- persistente Offline-Outbox und Background Sync über Reload
|
|
- Scheduled Messages, Erinnerungen und „Für später speichern"
|
|
- gerätebezogener `delivered`-Status und Read-Receipt-Privacy-Schalter
|
|
- Slack-Power-Shortcuts, frei konfigurierbares Sendeverhalten und Pop-out-Threads
|
|
- server-/geräteübergreifend synchronisierte Drafts
|
|
- GIF-/Sticker-Apps, Huddles, Audio-/Videoanrufe und KI-Transkription
|
|
|
|
## Research-Quellen
|
|
|
|
- [React `useOptimistic`](https://react.dev/reference/react/useOptimistic)
|
|
- [Matrix Client-Server API: Transaction IDs, Local Echo, Receipts, Typing, Threads](https://spec.matrix.org/latest/client-server-api/)
|
|
- [MDN WebSocket API und fehlende Backpressure](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket)
|
|
- [Slack: Send, read and manage drafts](https://slack.com/help/articles/201457107-Send-and-read-messages)
|
|
- [Slack: Threads](https://slack.com/help/articles/115000769927-Use-threads-to-organize-discussions)
|
|
- [Slack: Keyboard navigation](https://slack.com/help/articles/115003340723-Navigate-Slack-with-your-keyboard)
|
|
- [W3C ARIA23: `role=log`](https://www.w3.org/WAI/WCAG21/Techniques/aria/ARIA23)
|
|
- [WAI-ARIA Combobox Pattern](https://www.w3.org/WAI/ARIA/apg/patterns/combobox/)
|