Drei Wege, die über ein Neuladen führten

**In „Fortsetzen" startet ein Tipp.** Dort steht ein Titel, *weil* man ihn
angefangen hat, und die Stelle steht daneben — der Umweg über die
Detailseite stellte eine Frage, die schon beantwortet war. Überall sonst
bleibt es beim Weg auf die Seite: wer nachsehen will, wie viele Staffeln
eine Serie hat, soll nicht den Film bekommen.

**Ein Haken nimmt die Kachel mit.** In „Fortsetzen" und „Als Nächstes"
steht, was offen ist. Eine abgehakte Folge ist nicht mehr offen — sie mit
einem Haken stehen zu lassen sah aus, als hätte das Abhaken nicht
gewirkt. Dasselbe für „Aus Fortsetzen entfernen": in der Reihe selbst
heisst entfernen genau das.

**Und ein getauschtes Bild erreicht den Aufmacher.** Wer auf der
Startseite ein Plakat wechselt, sieht denselben Titel gleich darüber in
voller Breite — dort stand bis zum nächsten Neuladen das alte. Die
Kachel frischt sich selbst auf; der Aufmacher steht ganz woanders und
erfuhr nichts davon. Jetzt meldet der Bilderdialog es der ganzen Seite,
und wer es angeht, hört zu.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Scarriffle
2026-10-12 21:54:50 +02:00
co-authored by Claude Opus 5
parent adca700c4a
commit 2c535e6f47
3 changed files with 67 additions and 11 deletions
+17
View File
@@ -16,6 +16,19 @@ type Bildart = 'poster' | 'backdrop' | 'logo';
* Fenster sähe man dasselbe zweimal im Monat. */ * Fenster sähe man dasselbe zweimal im Monat. */
const AUFMACHER_HOECHSTENS = 5; const AUFMACHER_HOECHSTENS = 5;
/** Sagt der ganzen Seite, dass ein Bild gewechselt hat.
*
* **Weil der Rückruf nur den erreicht, der den Dialog geöffnet hat.** Wer
* auf der Startseite das Plakat einer Kachel tauscht, ändert damit auch den
* Aufmacher — und der steht ganz woanders und weiss von nichts. Bisher
* musste man die Seite neu laden, um das neue Bild zu sehen.
*
* Ein Ereignis am Dokument statt eines durchgereichten Rückrufs: der
* Aufmacher hört zu, ohne dass die Kachel ihn kennen müsste. */
export function bildgeaendertMelden(art: 'movies' | 'series', id: string): void {
document.dispatchEvent(new CustomEvent('flaro:bildgeaendert', { detail: { art, id } }));
}
/** Was jede Bildart ist — und in welcher Form sie hochgeladen wird. /** Was jede Bildart ist — und in welcher Form sie hochgeladen wird.
* *
* **`zuschnitt: null` beim Schriftzug, und das ist kein Versehen.** Ein * **`zuschnitt: null` beim Schriftzug, und das ist kein Versehen.** Ein
@@ -212,6 +225,7 @@ export function bilderflaeche(
// Kachel zeigte danach das alte Bild weiter — bis man die ganze // Kachel zeigte danach das alte Bild weiter — bis man die ganze
// Oberfläche neu lud. // Oberfläche neu lud.
beiAenderung?.(); beiAenderung?.();
bildgeaendertMelden(art, id);
hinweisZeigen(`${name} gesetzt.`); hinweisZeigen(`${name} gesetzt.`);
await laden(); await laden();
} catch (fehler) { } catch (fehler) {
@@ -232,6 +246,7 @@ export function bilderflaeche(
try { try {
await bildFreigeben(art, id, bildart); await bildFreigeben(art, id, bildart);
beiAenderung?.(); beiAenderung?.();
bildgeaendertMelden(art, id);
hinweisZeigen('Kommt beim nächsten Auffrischen wieder aus der Quelle.'); hinweisZeigen('Kommt beim nächsten Auffrischen wieder aus der Quelle.');
await laden(); await laden();
} catch (fehler) { } catch (fehler) {
@@ -345,6 +360,7 @@ export function bilderflaeche(
async function speichern(neu: string[]): Promise<void> { async function speichern(neu: string[]): Promise<void> {
await aufmacherSetzen(art, id, neu); await aufmacherSetzen(art, id, neu);
beiAenderung?.(); beiAenderung?.();
bildgeaendertMelden(art, id);
await laden(); await laden();
} }
} }
@@ -412,6 +428,7 @@ export function bilderflaeche(
try { try {
await bildSetzen(art, id, bildart, kandidat!.url); await bildSetzen(art, id, bildart, kandidat!.url);
beiAenderung?.(); beiAenderung?.();
bildgeaendertMelden(art, id);
await laden(); await laden();
} }
catch (fehler) { catch (fehler) {
+18 -9
View File
@@ -22,7 +22,14 @@ export type Kachel = {
* *
* **Bisher nur für „Als Nächstes".** Dort heisst Weglegen etwas anderes * **Bisher nur für „Als Nächstes".** Dort heisst Weglegen etwas anderes
* als in „Fortsetzen": nicht diese Folge, sondern diese Serie. */ * als in „Fortsetzen": nicht diese Folge, sondern diese Serie. */
reihe?: 'naechste'; reihe?: 'naechste' | 'fortsetzen';
/** Was ein Tipp auslöst, wenn es nicht die Detailseite sein soll.
*
* **Nur in „Fortsetzen".** Dort ist die Absicht eindeutig: der Titel steht
* in der Reihe, *weil* man ihn angefangen hat, und die Stelle steht
* daneben. Überall sonst führt der Tipp auf die Seite — wer nur nachsehen
* will, wie viele Staffeln eine Serie hat, soll nicht den Film bekommen. */
starten?: () => void;
art: 'filme' | 'serien'; art: 'filme' | 'serien';
/** Die obere, fette Zeile. Bei einer Folge die Serie. */ /** Die obere, fette Zeile. Bei einer Folge die Serie. */
titel: string; titel: string;
@@ -107,7 +114,7 @@ export function kachel(k: Kachel, oeffnen: (art: 'filme' | 'serien', id: string)
// muss — sie liest sie vom angeklickten Element ab. // muss — sie liest sie vom angeklickten Element ab.
knopf.dataset.id = k.id; knopf.dataset.id = k.id;
knopf.dataset.art = k.art; knopf.dataset.art = k.art;
knopf.onclick = () => oeffnen(k.art, k.id); knopf.onclick = () => (k.starten ? k.starten() : oeffnen(k.art, k.id));
// **Das Kontextmenü an jeder Kachel, überall.** Gesehen, Favorit und // **Das Kontextmenü an jeder Kachel, überall.** Gesehen, Favorit und
// Weglegen gab es nur auf der Detailseite — in einer Übersicht mit zwanzig // Weglegen gab es nur auf der Detailseite — in einer Übersicht mit zwanzig
@@ -297,14 +304,14 @@ function kachelpunkte(
// weiss nur der Server. // weiss nur der Server.
k.stand = await gesehen(k.standId ?? k.id, !istGesehen); k.stand = await gesehen(k.standId ?? k.id, !istGesehen);
// **In „Als Nächstes" ist ein Haken ein Abschied.** // **In „Fortsetzen" und „Als Nächstes" ist ein Haken ein Abschied.**
// //
// Die Reihe zeigt, was dran ist. Eine abgehakte Folge ist nicht mehr // Beide Reihen zeigen, was offen ist. Eine abgehakte Folge ist nicht
// dran — sie mit einem Haken stehen zu lassen sah aus, als hätte das // mehr offen — sie mit einem Haken stehen zu lassen sah aus, als
// Abhaken nicht gewirkt. Gemessen: der Server lieferte beim nächsten // hätte das Abhaken nicht gewirkt. Der Server lieferte beim nächsten
// Laden längst die richtige Folge, nur sah man das nie, weil die // Laden längst das Richtige; nur sah man das nie, weil die Reihe
// Reihe nicht neu geladen wurde. // nicht neu geladen wurde.
if (k.reihe === 'naechste' && !istGesehen) { if (k.reihe && !istGesehen) {
knopf.remove(); knopf.remove();
return; return;
} }
@@ -335,6 +342,8 @@ function kachelpunkte(
text: 'Aus Fortsetzen entfernen', text: 'Aus Fortsetzen entfernen',
tun: async () => { tun: async () => {
await weglegen(k.standId ?? k.id); await weglegen(k.standId ?? k.id);
// In der Reihe selbst heisst „entfernen" genau das.
if (k.reihe === 'fortsetzen') { knopf.remove(); return; }
if (k.stand) k.stand.dismissed = true; if (k.stand) k.stand.dismissed = true;
knopf.querySelector('.fortschritt')?.remove(); knopf.querySelector('.fortschritt')?.remove();
}, },
+32 -2
View File
@@ -1,6 +1,6 @@
import { import {
inhalt, weiterschauen, naechsteFolgen, empfehlungen, saisonal, inhalt, weiterschauen, naechsteFolgen, empfehlungen, saisonal,
type Bibliothek, type Bibliothek, type Angefangen,
} from '../api'; } from '../api';
import { leer, fehlerzeile, meldung } from '../util'; import { leer, fehlerzeile, meldung } from '../util';
import { kachel, alsKachel, angefangenAlsKachel, reihe } from '../card'; import { kachel, alsKachel, angefangenAlsKachel, reihe } from '../card';
@@ -77,6 +77,21 @@ export function startseite(
}); });
vorschlag.items.forEach(e => dazu(alsHero(e, 'filme'))); vorschlag.items.forEach(e => dazu(alsHero(e, 'filme')));
/** Was ein Tipp in „Fortsetzen" tut. */
function fortsetzenStarten(e: Angefangen): void {
// Ohne Datei gibt es nichts zu starten — dann führt der Tipp
// dorthin, wo man wählen kann. Dieselbe Regel wie im Aufmacher.
if (!e.fileId) { oeffnen(e.seriesId ? 'serien' : 'filme', e.seriesId ?? e.id); return; }
spieler({
dateiId: e.fileId,
titelId: e.id,
name: e.subTitle ?? e.title,
ab: e.state.positionSeconds,
laenge: e.durationSeconds ?? null,
danach: () => void aufbauen(),
});
}
const aufmacher = hero(heroTitel, const aufmacher = hero(heroTitel,
(art, id) => oeffnen(art, id), (art, id) => oeffnen(art, id),
e => { e => {
@@ -95,9 +110,24 @@ export function startseite(
signal); signal);
if (aufmacher) wurzel.appendChild(aufmacher); if (aufmacher) wurzel.appendChild(aufmacher);
// **Ein getauschtes Bild gehört sofort in den Aufmacher.**
//
// Wer auf dieser Seite das Plakat oder den Hintergrund eines Titels
// wechselt, sieht ihn gleich darüber in voller Breite — und dort stand
// bis zum nächsten Neuladen das alte. Die Kachel frischt sich selbst
// auf; der Aufmacher hat davon nichts erfahren, weil er ganz woanders
// steht. Siehe `bildgeaendertMelden`.
document.addEventListener('flaro:bildgeaendert', () => void aufbauen(), { signal });
if (weiter.items.length) { if (weiter.items.length) {
// **Hier startet ein Tipp.** In „Fortsetzen" steht ein Titel, *weil*
// man ihn angefangen hat, und die Stelle steht daneben — den Umweg
// über die Detailseite zu nehmen hiesse, eine Frage zu stellen, die
// schon beantwortet ist.
wurzel.appendChild(reihe('Fortsetzen', wurzel.appendChild(reihe('Fortsetzen',
weiter.items.map(e => kachel(angefangenAlsKachel(e), oeffnen)))); weiter.items.map(e => kachel(
{ ...angefangenAlsKachel(e, 'fortsetzen'), starten: () => fortsetzenStarten(e) },
oeffnen))));
} }
if (naechste.items.length) { if (naechste.items.length) {
wurzel.appendChild(reihe('Nächste Folge', wurzel.appendChild(reihe('Nächste Folge',