Jeder darf seine eigene Chat-Nachricht bearbeiten

Filipe: "die nachrichten die man in den chat reinschreibt. jeder soll
seine eigene nachricht bearbeiten koennen. diese option soll jeder
fuer seine eigene nachrichten haben die er selber verfasst hat."

NUR DIE EIGENE -- OHNE AUSNAHME, auch nicht fuer DogFather und die
rechte Hand. Beim LOESCHEN gibt es diese Ausnahme seit dem 22.09.
(fuer den Notfall), und es waere naheliegend gewesen, sie
mitzunehmen. Das waere falsch: Eine fremde Nachricht zu entfernen
heisst "das soll hier nicht stehen". Eine fremde zu AENDERN heisst,
jemandem Worte in den Mund zu legen, die unter seinem Namen und
seinem Bild stehen bleiben. Nicht dieselbe Befugnis in groesser,
sondern eine andere. Genau das ist die wichtigste Pruefzeile:
DogFather darf loeschen und bekommt beim Bearbeiten 403/404.

AN DER NACHRICHT STEHT "BEARBEITET". Ein Text, der sich still
aendert, nachdem andere darauf geantwortet haben, ist ein
Vertrauensproblem und kein Komfort. Der Vermerk traegt die Zeit im
Titel. Derselbe Text setzt ihn NICHT -- sonst stuende er irgendwann
ueberall und waere nichts mehr wert.

ERWAEHNUNGEN BLEIBEN, WIE SIE BEIM SENDEN WAREN. Wer beim Bearbeiten
"@Anna" ergaenzt, spricht Anna damit nicht an. Sonst gaebe es nur
schlechte Wege: nachtraeglich benachrichtigen laesst sich beliebig
oft wiederholen, und still eintragen setzt jemanden auf eine Liste,
von der er nie erfaehrt. Ansprechen tut man mit einer neuen
Nachricht. (Falls das anders gewuenscht ist, ist es eine eigene
Entscheidung -- nicht etwas, das hier nebenbei mitpassiert.)

DER RAUM RUECKT NICHT NACH OBEN und niemand bekommt die Nachricht
als ungelesen: Eine Tippfehlerkorrektur ist keine Wortmeldung.
`letzte_am` wird deshalb nicht angefasst.

Leer geht nicht -- dafuer steht "loeschen" daneben, mit Rueckfrage.
Grenzen (4000 Zeichen) sind dieselben wie beim Senden; eine zweite
Rechnung waere die, die auseinanderlaeuft.

Das Feld sitzt AN der Nachricht, nicht im Schreibfeld unten: Wer
seinen Text zum Bearbeiten unten wiederfindet, schickt ihn beim
naechsten Enter als NEUE Nachricht ab und hat ihn zweimal im Raum.
Enter speichert, Shift+Enter macht eine Zeile, Escape bricht ab --
dieselben Tasten wie beim Schreiben. 16 px Schrift, sonst zoomt iOS
beim Hineintippen die ganze Seite heran.

Der Stift traegt sich in die Familie der Handgriffe ein, wie es der
Hinweis in chat.css ausdruecklich verlangt ("wer einen sechsten
Handgriff baut, traegt ihn hier ein und bekommt sein Zeichen").

EIN FEHLER, DEN NUR DAS BILD GEZEIGT HAT. Ich hatte im Code
behauptet, die Gespraechsliste aendere sich beim Bearbeiten nicht,
und darum auf das Nachladen verzichtet. Auf dem Bildschirmfoto stand
rechts "Treffen um 15 Uhr" und links in der Liste weiter "Du:
Treffen um 15 Urh" -- derselbe Satz, zweimal verschieden, auf einem
Schirm. Richtig ist: Die REIHENFOLGE aendert sich nicht, die
VORSCHAU sehr wohl. Beide Haelften waren fuer sich gemessen und
gruen; keine Zahl hat es gemerkt.

pruef-chat 17 neue Pruefungen: eigene geht, fremde nicht, DogFather
nicht, Vermerk kommt mit nach draussen (auch im SELECT -- genau das
hat am 03.10. bei den Anhaengen einen halben Tag gekostet), Raum
rueckt nicht, Vorschau zieht nach, leer/zu lang abgelehnt,
unveraendert ohne Vermerk, geloeschte nicht bearbeitbar, wer nicht
im Raum ist bekommt 404 statt 403.

pruef-chat-optik 71 -> 84: im echten Browser, mit zwei Sitzungen.
Darunter die Zeile, auf die es ankommt -- der neue Text steht bei
Patrick, OHNE Neuladen. Ein Bearbeiten, das nur der Schreibende
sieht, waere schlimmer als keins.

Dabei zwei eigene Messfehler behoben: Die Sitzungen von oben waren
laengst geschlossen (Playwright meldet nur "Target page has been
closed"), und beide klickten "das oberste Gespraech" statt
denselben Raum -- wodurch die Pruefung "an ihr steht KEIN
bearbeiten" gruen war, weil die Nachricht gar nicht da war. Sie
haengt jetzt daran, dass er sie wirklich sieht.

Datenbank vorher gesichert und zurueckgelesen (integrity_check,
324 Nachrichten, 20 Personen).

Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
2026-10-04 00:06:29 +02:00
co-authored by Claude Opus 5
parent 5d9c8cd377
commit b625d901a5
52 changed files with 1314 additions and 686 deletions
+115
View File
@@ -313,6 +313,121 @@ console.log("\n=== Ungelesene Nachrichten ===");
ok(zurueck === 0, `ein alter "gelesen bis 1" dreht nichts zurück (${zurueck})`);
}
/* =======================================================================
6b. EIGENE NACHRICHT BEARBEITEN (03.10.2026)
Filipe: "die nachrichten die man in den chat reinschreibt. jeder
soll seine eigene nachricht bearbeiten koennen."
DIE WICHTIGSTE ZEILE IST DIE GEGENPROBE MIT DOGFATHER. Er darf
JEDE Nachricht loeschen -- fuer den Notfall, so steht es seit dem
22.09. im Haus. Beim Bearbeiten darf er das ausdruecklich NICHT:
Eine fremde Nachricht zu entfernen heisst "das soll hier nicht
stehen"; sie zu AENDERN heisst, jemandem Worte in den Mund zu
legen, die unter seinem Namen und seinem Bild stehen bleiben.
Waere der Loeschweg hier blind mituebernommen worden, haette es
niemandem auffallen muessen -- es sieht ja aus wie dieselbe
Befugnis.
======================================================================= */
console.log("\n=== Eine eigene Nachricht bearbeiten ===");
{
const a = await rufe(luna.keks, `/workspace/api/chat/raeume/${raumLunaPat}/nachrichten`,
{ text: "Ich kome moregn" }, "POST");
const { nachricht } = await a.json();
ok(!!nachricht?.id, `Luna schreibt etwas mit Tippfehlern (#${nachricht?.id})`);
/* Vorher merken: Rueckt der Raum beim Bearbeiten nach oben? Er
darf NICHT -- eine Tippfehlerkorrektur ist keine Wortmeldung. */
const vorher = await holen(luna.keks, "/workspace/api/chat/raeume");
const raumVorher = (vorher.raeume || []).find((r) => r.id === raumLunaPat);
ok(!!raumVorher?.letzte?.erstellt,
`der Raum steht in der Liste und hat einen Zeitpunkt (${raumVorher?.letzte?.erstellt || "keinen"})`);
const standVorher = raumVorher?.letzte?.erstellt;
/* ---- Die eigene: geht ------------------------------------------- */
const b = await rufe(luna.keks, `/workspace/api/chat/nachrichten/${nachricht.id}`,
{ text: "Ich komme morgen" }, "PATCH");
ok(b.ok, `die eigene bearbeiten: geht (${b.status})`);
const bd = await b.json().catch(() => ({}));
ok(!!bd.bearbeitet_am, `und der Zeitpunkt kommt zurueck (${bd.bearbeitet_am || "nichts"})`);
/* ---- Steht es auch im Verlauf? ---------------------------------- */
const verlauf = await holen(luna.keks,
`/workspace/api/chat/raeume/${raumLunaPat}/nachrichten`);
const drin = (verlauf.nachrichten || []).find((x) => x.id === nachricht.id);
ok(drin?.text === "Ich komme morgen",
`der neue Text steht im Verlauf ("${drin?.text}")`);
ok(!!drin?.bearbeitet_am,
`und der Vermerk "bearbeitet" geht mit nach draussen (${drin?.bearbeitet_am || "fehlt"})`);
/* DIE SPALTE MUSS AUCH IM SELECT STEHEN. Genau das hat am 03.10.
schon einmal einen halben Tag gekostet: `anhang_datei` fehlte dort,
die Abbildung lieferte brav `null`, und alles sah gebaut aus. */
/* ---- Der Raum ist NICHT nach oben gerueckt ---------------------- */
const nachher = await holen(luna.keks, "/workspace/api/chat/raeume");
const raumNachher = (nachher.raeume || []).find((r) => r.id === raumLunaPat);
ok(!!standVorher && standVorher === raumNachher?.letzte?.erstellt,
`der Raum rueckt nicht nach oben (${standVorher} -> ${raumNachher?.letzte?.erstellt})`);
/* Die Vorschau in der Liste zieht aber sehr wohl nach -- sie liest
dieselbe Nachricht. Stuende dort weiter der Tippfehler, waere die
Liste eine zweite, veraltete Wahrheit neben dem Verlauf. */
ok(raumNachher?.letzte?.text === "Ich komme morgen",
`aber die Vorschau zeigt den neuen Text ("${raumNachher?.letzte?.text}")`);
/* ---- GEGENPROBE: fremde Nachricht ------------------------------- */
const c = await rufe(pat.keks, `/workspace/api/chat/nachrichten/${nachricht.id}`,
{ text: "Von Patrick umgeschrieben" }, "PATCH");
ok(c.status === 403, `Gegenprobe: Patrick aendert sie NICHT (${c.status})`);
/* ---- GEGENPROBE, DIE ZAEHLT: auch DogFather nicht --------------- */
const d = await rufe(dogi.keks, `/workspace/api/chat/nachrichten/${nachricht.id}`,
{ text: "Von DogFather umgeschrieben" }, "PATCH");
ok(d.status === 403 || d.status === 404,
`auch DogFather nicht -- loeschen darf er, umschreiben nicht (${d.status})`);
const nochda = await holen(luna.keks,
`/workspace/api/chat/raeume/${raumLunaPat}/nachrichten`);
const unberuehrt = (nochda.nachrichten || []).find((x) => x.id === nachricht.id);
ok(unberuehrt?.text === "Ich komme morgen",
`und ihr Text ist unberuehrt ("${unberuehrt?.text}")`);
/* ---- Leer und zu lang ------------------------------------------- */
const leer = await rufe(luna.keks, `/workspace/api/chat/nachrichten/${nachricht.id}`,
{ text: " " }, "PATCH");
ok(leer.status === 400, `leer geht nicht -- dafuer gibt es loeschen (${leer.status})`);
const lang = await rufe(luna.keks, `/workspace/api/chat/nachrichten/${nachricht.id}`,
{ text: "x".repeat(4001) }, "PATCH");
ok(lang.status === 400, `zu lang geht nicht (${lang.status})`);
/* ---- Unveraendert setzt KEINEN Vermerk -------------------------- */
const zweite = await rufe(luna.keks, `/workspace/api/chat/raeume/${raumLunaPat}/nachrichten`,
{ text: "Steht schon richtig" }, "POST");
const n2 = (await zweite.json()).nachricht;
const gleich = await rufe(luna.keks, `/workspace/api/chat/nachrichten/${n2.id}`,
{ text: "Steht schon richtig" }, "PATCH");
const gd = await gleich.json().catch(() => ({}));
ok(gleich.ok && gd.unveraendert === true && !gd.bearbeitet_am,
`derselbe Text setzt keinen Vermerk (${JSON.stringify(gd)})`);
const pruefN2 = (await holen(luna.keks,
`/workspace/api/chat/raeume/${raumLunaPat}/nachrichten`)).nachrichten
.find((x) => x.id === n2.id);
ok(!pruefN2?.bearbeitet_am,
`und an ihr steht nichts -- sonst waere der Vermerk nichts mehr wert`);
/* ---- Eine geloeschte laesst sich nicht bearbeiten --------------- */
await rufe(luna.keks, `/workspace/api/chat/nachrichten/${n2.id}`, undefined, "DELETE");
const tot = await rufe(luna.keks, `/workspace/api/chat/nachrichten/${n2.id}`,
{ text: "Doch noch etwas" }, "PATCH");
ok(tot.status === 404,
`eine geloeschte Nachricht gibt es nicht mehr zu bearbeiten (${tot.status})`);
/* ---- Wer nicht im Raum ist, sieht sie gar nicht ----------------- */
const fremderRaum = await rufe(nora.keks, `/workspace/api/chat/nachrichten/${nachricht.id}`,
{ text: "Von Nora" }, "PATCH");
ok(fremderRaum.status === 404,
`wer nicht im Raum ist, bekommt 404 und nicht 403 (${fremderRaum.status})`);
}
/* =======================================================================
7. Zurücknehmen
======================================================================= */