Echtzeit & Chat
Der Chat aktualisiert sich in Echtzeit und fühlt sich dabei sofort und flackerfrei an. Dahinter steht ein bewusst gewähltes Muster.
Hybrid-Broadcast
Abschnitt betitelt „Hybrid-Broadcast“Neue Ereignisse (Senden, Bearbeiten, Löschen, Reaktion) werden auf zwei Wegen verteilt:
- Client → Client direkt per Broadcast (
broadcastFamily) — minimale Latenz. - DB-Trigger-Broadcast als Fallback, falls der direkte Weg jemanden nicht erreicht (z. B. gerade offline).
Optimistic Overlay
Abschnitt betitelt „Optimistic Overlay“Mutationen werden sofort lokal angezeigt und in getrennten Overlays gehalten
(pending, pendingEdits, pendingDels, pendingReactions). Diese Overlays
werden zuletzt über die Serverliste gelegt — so überlebt eine optimistische
Änderung auch einen zwischenzeitlichen, noch veralteten Server-Reload. Die
Merge-Logik (mergeThread, mergeReactions, applyReactionOverlay,
receiptState) ist als reine, getestete Funktionen ausgelagert.
Private Kanäle
Abschnitt betitelt „Private Kanäle“Realtime läuft über private Kanäle (Migration 0082):
const ch = sb.channel(topic, { config: { private: true } });await sb.realtime.setAuth(token); // vor subscribeRLS auf realtime.messages regelt Empfangen (SELECT) und Senden (INSERT).
Wichtig: Der Abo-Umfang muss exakt dem RLS-Umfang entsprechen
(memberFamilyIds). Abonniert ein Client Kanäle fremder Familien, führt das zu
einer „Unauthorized“-Rejoin-Schleife und damit zu einseitiger Verzögerung.
Robustheit beim Senden
Abschnitt betitelt „Robustheit beim Senden“- Idempotenz: Die Nachrichten-ID wird clientseitig erzeugt
(
crypto.randomUUID()); ein23505(Unique-Verletzung) gilt als Erfolg. - Hintergrund-Retry mit Timeout (
Promise.race) — kurzer Timeout für Text, längerer für Anhänge.
Lesebestätigungen
Abschnitt betitelt „Lesebestätigungen“markRead / markDelivered schreiben nur vorwärts (per
last_x.is.null or last_x.lt.<iso>); receiptState leitet daraus den Status
ab (gelesen > zugestellt > gesendet).