Reaktionen: Vorschlaege, ganzer Katalog, eigene Favoriten
Filipe: "soll viel besser und geiler sein und man soll auch auswaehlen
koennen, also paar als vorschlaege mit der moeglichkeit andere
auszuwaehlen und als favoriten zu speichern."
Aus einem Streifen mit sechs Zeichen ist eine Karte geworden:
Vorschlaege oben, Suchfeld, darunter der Katalog in sechs Gruppen --
82 Zeichen, jedes mit deutschen Suchwoertern. Wer "feuer" tippt, muss
nicht scrollen.
DREI LISTEN, DREI FRAGEN, und sie werden gern verwechselt:
KATALOG Was gibt es? fest, workspace-reaktionen.js
VORSCHLAG Was steht vorn, solange fest -- bis jemand eigene
ich nichts gewaehlt habe? Favoriten hat
FAVORITEN Was hat DIESER Mensch in der Datenbank
sich gemerkt?
Nur der Katalog entscheidet, was ANGENOMMEN wird. Vorschlag und
Favoriten sind Reihenfolge, keine Erlaubnis -- sonst koennte man sich
ueber einen Favoriten etwas erlauben, das im Katalog nicht steht. Die
erlaubte Menge wird aus dem Katalog ABGELEITET; eine zweite Liste
daneben waere die, in der ein Zeichen fehlt, das die Oberflaeche
anbietet.
FAVORITEN HAENGEN AM MENSCHEN, NICHT AM GERAET. Im Browser abgelegt
waeren sie am Handy andere als am Rechner und beim naechsten Loeschen
der Seitendaten weg.
DER STERN SCHALTET EINEN MODUS, statt an jedem Zeichen einen zweiten
Knopf zu haben: Am Handy gibt es kein "daneben zeigen". Im Merk-Modus
faerbt sich die ganze Tafel, damit niemand reagiert, wenn er merken
wollte. Die Tafel bleibt dabei offen -- acht Favoriten waeren sonst
acht Mal Aufmachen -- und die Vorschlagszeile aendert sich sofort mit.
DIE GRENZE SAGT BESCHEID, statt still den aeltesten wegzuwerfen: Wer
einen neunten merken will, bekommt "nimm erst einen weg". Ein Favorit,
der ohne Ansage verschwindet, sieht wie ein Fehler aus.
Eine Selbstpruefung beim Laden meldet doppelte Zeichen im Katalog und
Vorschlaege, die nicht darin stehen -- beides waere in der Tafel still.
pruef-chat-aufloesen 85 -> 115. Gemessen wird, was PASSIERT, nicht dass
es Elemente gibt: dass die Suche wirklich eingrenzt (1 statt 82) und
das Richtige findet, dass ein Fehlgriff "Nichts zu ... gefunden" sagt
statt eine leere Flaeche zu zeigen, und vor allem, dass im Merk-Modus
KEINE Reaktion entsteht -- an der Zahl der Reaktionen gemessen, nicht
an der Absicht.
NEBENBEFUND AUS DER EIGENEN PRUEFUNG: pruef-css-klassen hat meine
Gruppenueberschrift mit 10,56 px abgelehnt (Grenze 11,5). Richtig so --
eine gesperrte Versalzeile ist ohnehin schwerer zu lesen, die darf
nicht auch noch die kleinste sein. Auf .72rem angehoben, statt die
Grundlinie zu verschieben.
Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
@@ -412,6 +412,96 @@ console.log("=== Reaktionen ===");
|
||||
const zaehle = () => d.prepare(
|
||||
"SELECT COUNT(*) AS n FROM chat_reaktionen WHERE nachricht_id = ?").get(nid).n;
|
||||
|
||||
/* ---- KATALOG UND FAVORITEN (14.09.2026) -------------------------
|
||||
Filipe: "paar als vorschlaege mit der moeglichkeit andere
|
||||
auszuwaehlen und als favoriten zu speichern."
|
||||
|
||||
DREI LISTEN, DREI FRAGEN -- und sie werden gern verwechselt:
|
||||
Katalog (was gibt es?), Vorschlag (was steht vorn?), Favoriten
|
||||
(was hat DIESER Mensch gewaehlt?). Geprueft wird, dass sie
|
||||
zusammenhaengen, ohne dasselbe zu sein. */
|
||||
const aus1 = await holen(eins.keks, "/workspace/api/chat/reaktionen/auswahl");
|
||||
ok(Array.isArray(aus1.katalog) && aus1.katalog.length >= 4,
|
||||
`der Katalog hat ${(aus1.katalog || []).length} Gruppen`);
|
||||
const alleZeichen = (aus1.katalog || []).flatMap((g) => g.zeichen.map((z) => z[0]));
|
||||
ok(alleZeichen.length >= 50, `und ${alleZeichen.length} Zeichen insgesamt`);
|
||||
ok(new Set(alleZeichen).size === alleZeichen.length,
|
||||
"keines davon doppelt -- ein doppeltes waere in der Tafel still");
|
||||
ok((aus1.katalog || []).every((g) => g.zeichen.every(([, w]) => typeof w === "string" && w.length > 2)),
|
||||
"jedes Zeichen hat deutsche Suchwoerter");
|
||||
ok(aus1.eigene === false && aus1.zeichen.length === 6,
|
||||
`ohne eigene Favoriten stehen die sechs Vorgaben vorn (${aus1.zeichen.length})`);
|
||||
ok(aus1.zeichen.every((z) => alleZeichen.includes(z)),
|
||||
"und jeder Vorschlag steht auch im Katalog");
|
||||
|
||||
/* ---- Einen Favoriten merken ------------------------------------- */
|
||||
const exot = alleZeichen.find((z) => !aus1.zeichen.includes(z));
|
||||
const f1 = await (await rufe(eins.keks, "/workspace/api/chat/reaktionen/favorit",
|
||||
{ zeichen: exot }, "PUT")).json();
|
||||
ok(f1.favoriten?.length === 1 && f1.favoriten[0] === exot,
|
||||
`ein Zeichen wird gemerkt (${f1.favoriten?.join(" ")})`);
|
||||
ok(f1.zeichen?.length === 1,
|
||||
"und steht ab jetzt ALLEIN vorn -- Favoriten ersetzen die Vorgabe");
|
||||
|
||||
/* ---- Sie bleiben, auch nach dem Neuladen ------------------------- */
|
||||
const aus2 = await holen(eins.keks, "/workspace/api/chat/reaktionen/auswahl");
|
||||
ok(aus2.eigene === true && aus2.favoriten[0] === exot,
|
||||
"beim naechsten Aufruf sind sie noch da");
|
||||
|
||||
/* ---- Und sie gehoeren EINEM Menschen ----------------------------- */
|
||||
const ausBen = await holen(zwei.keks, "/workspace/api/chat/reaktionen/auswahl");
|
||||
ok(ausBen.eigene === false,
|
||||
"Ben hat davon nichts -- Favoriten sind persoenlich");
|
||||
|
||||
/* ---- Zweimal dasselbe nimmt es weg ------------------------------- */
|
||||
const f2 = await (await rufe(eins.keks, "/workspace/api/chat/reaktionen/favorit",
|
||||
{ zeichen: exot }, "PUT")).json();
|
||||
ok(f2.favoriten?.length === 0 && f2.gesetzt === false,
|
||||
"noch einmal darauf nimmt den Favoriten wieder weg");
|
||||
const aus3 = await holen(eins.keks, "/workspace/api/chat/reaktionen/auswahl");
|
||||
ok(aus3.eigene === false && aus3.zeichen.length === 6,
|
||||
"und die Vorgabe steht wieder vorn");
|
||||
|
||||
/* ---- Die Grenze sagt Bescheid, statt still zu schlucken ---------- */
|
||||
const acht = alleZeichen.slice(0, aus1.favoriten_max);
|
||||
for (const z of acht) {
|
||||
await rufe(eins.keks, "/workspace/api/chat/reaktionen/favorit", { zeichen: z }, "PUT");
|
||||
}
|
||||
const voll = await rufe(eins.keks, "/workspace/api/chat/reaktionen/favorit",
|
||||
{ zeichen: alleZeichen[aus1.favoriten_max] }, "PUT");
|
||||
const vollD = await voll.json();
|
||||
ok(voll.status === 409 && /Favoriten/.test(vollD.fehler || ""),
|
||||
`der neunte wird abgelehnt und begruendet (HTTP ${voll.status})`);
|
||||
ok((vollD.favoriten || []).length === aus1.favoriten_max,
|
||||
`die acht bleiben unangetastet (${(vollD.favoriten || []).length})`);
|
||||
|
||||
/* GEGENPROBE: Nach dem Wegnehmen eines Favoriten geht wieder einer
|
||||
hinein. Ohne sie hiesse das oben nur, dass ab acht gar nichts mehr
|
||||
geht. */
|
||||
await rufe(eins.keks, "/workspace/api/chat/reaktionen/favorit",
|
||||
{ zeichen: acht[0] }, "PUT");
|
||||
const platzFrei = await rufe(eins.keks, "/workspace/api/chat/reaktionen/favorit",
|
||||
{ zeichen: alleZeichen[aus1.favoriten_max] }, "PUT");
|
||||
ok(platzFrei.status === 200,
|
||||
`nach dem Wegnehmen passt wieder einer (HTTP ${platzFrei.status})`);
|
||||
|
||||
/* ---- Ein Favorit ist keine Erlaubnis ----------------------------- */
|
||||
const erfunden = await rufe(eins.keks, "/workspace/api/chat/reaktionen/favorit",
|
||||
{ zeichen: "nicht-im-katalog" }, "PUT");
|
||||
ok(erfunden.status === 400,
|
||||
`ein Zeichen ausserhalb des Katalogs wird nicht gemerkt (HTTP ${erfunden.status})`);
|
||||
|
||||
/* ---- Der ganze Katalog ist auch zum Reagieren erlaubt ------------ */
|
||||
const weit = alleZeichen[alleZeichen.length - 1];
|
||||
const gr = await (await rufe(eins.keks, "/workspace/api/chat/gruppe",
|
||||
{ mit: [idZwei], name: "Katalogtest" }, "POST")).json();
|
||||
const nn = await (await rufe(eins.keks,
|
||||
`/workspace/api/chat/raeume/${gr.raum_id}/nachrichten`, { text: "x" }, "POST")).json();
|
||||
const mitExot = await rufe(eins.keks,
|
||||
`/workspace/api/chat/nachrichten/${nn.nachricht.id}/reaktion`, { zeichen: weit }, "PUT");
|
||||
ok(mitExot.status === 200,
|
||||
`auch das letzte Zeichen des Katalogs laesst sich geben (${weit})`);
|
||||
|
||||
/* ---- Setzen ---------------------------------------------------- */
|
||||
const a1 = await (await rufe(zwei.keks,
|
||||
`/workspace/api/chat/nachrichten/${nid}/reaktion`, { zeichen: daumen }, "PUT")).json();
|
||||
@@ -601,6 +691,101 @@ console.log("=== Das Neu-Fenster ===");
|
||||
const tafel = await seite.$$eval(".reagieren-tafel__zeichen",
|
||||
(b) => b.map((x) => x.textContent.trim()));
|
||||
ok(tafel.length >= 4, `die Auswahl klappt auf (${tafel.length} Zeichen)`);
|
||||
|
||||
/* ---- DIE TAFEL: Vorschlaege, Suche, Katalog, Favoriten ----------
|
||||
(14.09.2026) Filipe: "soll viel besser und geiler sein und man
|
||||
soll auch auswaehlen koennen."
|
||||
|
||||
Gemessen wird, was auf dem Bildschirm PASSIERT -- nicht, dass es
|
||||
Elemente gibt. Eine Tafel, die aufklappt und deren Suche nichts
|
||||
tut, haette alle Anwesenheitspruefungen bestanden. */
|
||||
const teile = await seite.evaluate(() => ({
|
||||
schnell: document.querySelectorAll(".reagieren-tafel__schnell .reagieren-tafel__zeichen").length,
|
||||
gruppen: document.querySelectorAll(".reagieren-tafel__gruppe").length,
|
||||
katalog: document.querySelectorAll(".reagieren-tafel__katalog .reagieren-tafel__zeichen").length,
|
||||
suche: !!document.querySelector(".reagieren-tafel__suche"),
|
||||
stern: !!document.querySelector(".reagieren-tafel__stern"),
|
||||
}));
|
||||
ok(teile.schnell === 6, `sechs Vorschlaege stehen oben (${teile.schnell})`);
|
||||
ok(teile.gruppen >= 4 && teile.katalog >= 50,
|
||||
`darunter der Katalog: ${teile.gruppen} Gruppen, ${teile.katalog} Zeichen`);
|
||||
ok(teile.suche && teile.stern, "Suchfeld und Favoriten-Schalter sind da");
|
||||
|
||||
if (process.env.SCHIRM) {
|
||||
await seite.locator(".chat-nachricht").last().screenshot(
|
||||
{ path: join(HIER_BILD, "pruef-chat-tafel.png") });
|
||||
}
|
||||
|
||||
/* ---- Die Suche muss SUCHEN --------------------------------------- */
|
||||
await seite.fill(".reagieren-tafel__suche", "feuer");
|
||||
await seite.waitForTimeout(400);
|
||||
const gefunden = await seite.evaluate(() => ({
|
||||
zahl: document.querySelectorAll(".reagieren-tafel__katalog .reagieren-tafel__zeichen").length,
|
||||
erstes: document.querySelector(".reagieren-tafel__katalog .reagieren-tafel__zeichen")?.textContent,
|
||||
}));
|
||||
ok(gefunden.zahl >= 1 && gefunden.zahl < teile.katalog,
|
||||
`"feuer" grenzt ein: ${gefunden.zahl} statt ${teile.katalog}`);
|
||||
ok(gefunden.erstes === "\u{1F525}", `und findet das Richtige (${gefunden.erstes})`);
|
||||
|
||||
/* Ein Wort, das es nicht gibt, sagt das -- statt eine leere Flaeche
|
||||
zu zeigen, die wie ein Fehler aussieht. */
|
||||
await seite.fill(".reagieren-tafel__suche", "zwiebelkuchen");
|
||||
await seite.waitForTimeout(400);
|
||||
const leer = await seite.evaluate(() =>
|
||||
document.querySelector(".reagieren-tafel__leer")?.textContent || "");
|
||||
ok(leer.includes("Nichts zu"), `ein Fehlgriff wird gesagt ("${leer.slice(0, 40)}")`);
|
||||
await seite.fill(".reagieren-tafel__suche", "");
|
||||
await seite.waitForTimeout(400);
|
||||
|
||||
/* ---- Der Favoriten-Modus ----------------------------------------- */
|
||||
await seite.click(".reagieren-tafel__stern");
|
||||
await seite.waitForTimeout(300);
|
||||
const anModus = await seite.evaluate(() => ({
|
||||
merken: document.querySelector(".reagieren-tafel")?.dataset.merken,
|
||||
hinweis: document.querySelector(".reagieren-tafel__hinweis")?.textContent || "",
|
||||
}));
|
||||
ok(anModus.merken === "ja", "der Stern schaltet den Merk-Modus ein");
|
||||
ok(anModus.hinweis.includes("merkt"),
|
||||
`und sagt, was ein Antippen jetzt tut ("${anModus.hinweis.slice(0, 44)}…")`);
|
||||
|
||||
/* Ein Zeichen im Merk-Modus antippen REAGIERT NICHT, es merkt. Das
|
||||
ist der Unterschied, der schiefgehen kann -- und er wird hier an
|
||||
der Zahl der Reaktionen gemessen, nicht an der Absicht. */
|
||||
const vorher = await seite.$$eval(".reaktionen__stueck", (b) => b.length);
|
||||
await seite.click(".reagieren-tafel__katalog .reagieren-tafel__zeichen");
|
||||
await seite.waitForTimeout(700);
|
||||
const nachher = await seite.evaluate(() => ({
|
||||
reaktionen: document.querySelectorAll(".reaktionen__stueck").length,
|
||||
gemerkt: document.querySelectorAll('.reagieren-tafel__zeichen[data-favorit="ja"]').length,
|
||||
offen: !!document.querySelector(".reagieren-tafel"),
|
||||
hinweis: document.querySelector(".reagieren-tafel__hinweis")?.textContent || "",
|
||||
}));
|
||||
ok(nachher.reaktionen === vorher,
|
||||
`im Merk-Modus entsteht KEINE Reaktion (${vorher} -> ${nachher.reaktionen})`);
|
||||
ok(nachher.gemerkt >= 1, `sondern ein Favorit (${nachher.gemerkt} markiert)`);
|
||||
ok(nachher.offen, "und die Tafel bleibt offen -- sonst achtmal aufmachen");
|
||||
ok(nachher.hinweis.includes("gemerkt"),
|
||||
`der Hinweis zaehlt mit ("${nachher.hinweis.slice(0, 34)}…")`);
|
||||
|
||||
if (process.env.SCHIRM) {
|
||||
await seite.locator(".chat-nachricht").last().screenshot(
|
||||
{ path: join(HIER_BILD, "pruef-chat-favoriten.png") });
|
||||
}
|
||||
|
||||
/* Und der Favorit steht danach VORN -- ohne die Tafel neu zu oeffnen. */
|
||||
const vornDrin = await seite.evaluate(() => {
|
||||
const merk = [...document.querySelectorAll('.reagieren-tafel__katalog .reagieren-tafel__zeichen[data-favorit="ja"]')]
|
||||
.map((b) => b.dataset.zeichen);
|
||||
const vorn = [...document.querySelectorAll(".reagieren-tafel__schnell .reagieren-tafel__zeichen")]
|
||||
.map((b) => b.dataset.zeichen);
|
||||
return merk.every((z) => vorn.includes(z)) && vorn.length > 0;
|
||||
});
|
||||
ok(vornDrin, "der frisch gemerkte steht sofort in der Vorschlagszeile");
|
||||
|
||||
/* Modus wieder aus, dann wirklich reagieren. */
|
||||
await seite.click(".reagieren-tafel__stern");
|
||||
await seite.waitForTimeout(250);
|
||||
|
||||
const tafelLage = await seite.evaluate(() => {
|
||||
const t = document.querySelector(".reagieren-tafel");
|
||||
const z = document.querySelector(".reagieren-tafel__zeichen");
|
||||
|
||||
Reference in New Issue
Block a user