Film: „Gesehen" lässt sich nicht mehr zurücknehmen #5
Notifications
Total Time Spent: 9 minutes
florian.wilde
9 minutes
No due date set.
Dependencies
No dependencies set.
Reference: florian.wilde/flimmer#5
Reference in New Issue
Block a user
Was passiert
Ist ein Film einmal als gesehen markiert, gibt es keinen Weg zurück. Der Statuswechsel auf Watchlist, Pausiert oder Abgebrochen ändert nichts — die grüne Plakette „✓ Gesehen" bleibt stehen, und der Fortschrittsbalken zeigt weiter 100 %.
Bei Serien geht es: einzelne Episoden und ganze Staffeln lassen sich abwählen. Nur beim Film ist es eine Einbahnstraße.
Warum
Drei Dinge greifen ineinander:
1. Der Knopf ist gar keiner mehr. In
WatchMovieButton(src/components/library/NextEpisode.tsx:96) wird im Zustand „gesehen" ein<p>gerendert statt eines<button>— ein Etikett ohne Klickziel. Es gibt schlicht nichts anzuklicken.2. Der Status kann das nicht heilen. „Gesehen" wird nicht aus dem Status abgeleitet, sondern aus den Sichtungen:
movieWatched = (progress?.watched ?? 0) > 0(src/app/titel/[id]/page.tsx:103).setStatus()rührt die Sichtungen nicht an (src/lib/library.ts:96) — zu Recht: eine Serie auf „Pausiert" zu setzen darf ja auch nicht die abgehakten Episoden löschen. Beim Film wirkt das aber wie ein kaputter Knopf, weil man genau das versucht.3. Es fehlt die Gegenfunktion. Es gibt
unmarkEpisodes()undunmarkSeason()(src/lib/library.ts:230und:252), aber kein Gegenstück für Filme.undoWatches()gibt es zwar, das braucht aber die konkretenwatchIds— und die hält nur die Einblendung direkt nach dem Klick. Ist sie weg oder die Seite neu geladen, sind sie unerreichbar.Kurz: Serien haben einen Umschalter bekommen, Filme nur einen Hinweg.
Der Fallstrick beim Beheben
markMovieWatched()legt die Sichtung mitrun: entry.currentRunan (src/lib/library.ts:318). Das Zurücknehmen muss genauso auf den aktuellen Durchlauf eingegrenzt sein. EindeleteMany({ userId, entryId, episodeId: null })ohnerunwürde bei einem Film, den man dreimal gesehen hat, alle drei Sichtungen löschen — und damit rückwirkend die Statistik verfälschen. Genau dafür istrunja da.Umzusetzen
unmarkMovie(userId, entryId)insrc/lib/library.ts, eingegrenzt aufrun: entry.currentRunundepisodeId: null, danachreconcileStatus()unmarkMovieActioninsrc/app/actions/library.ts, analog zutoggleEpisodeActionWatchMovieButtonzu einem echten Umschalter machen: der Zustand „gesehen" bleibt sichtbar grün, wird aber klickbar („Markierung entfernen") — so wie die Episodenhäkchen sich auch wieder abwählen lassenDELETE /api/v1/bibliothek/{entryId}/sichtungenerreichbar machen, ohne dass manwatchIdskennen muss (etwa{"action":"movie"}als Gegenstück zum POST) — samtdocs/API.mdundopenapi.jsonNebenbei aufgefallen
Klickt man „Als gesehen markieren" bei einem schon markierten Film, antwortet
markMovieWatched()mitwatchIds: []und der Meldung „War schon als gesehen markiert" (src/lib/library.ts:322). Die Einblendung bietet dann ein Zurücknehmen an, das nichts zurücknimmt. Sollte mit erledigt werden.Abnahme
watchIdsmöglichAufwand
Klein. Eine Funktion, eine Aktion, ein Knopf, ein Endpunkt — die Bausteine für Serien stehen schon und geben das Muster vor.
Angelegt auf ausdrücklichen Wunsch direkt in Gitea, ohne Verknüpfung zu einem Nebula-Task.
🤖 Claude Code hat mit der Umsetzung begonnen.
fix/5-gesehen-zuruecknehmenunmarkMovie()eingegrenzt aufrun: entry.currentRunundepisodeId: null, danachreconcileStatus()WatchMovieButtonzu einem echten Umschalter machen; den irreführenden „Zurücknehmen"-Hinweis bei einem schon markierten Film gleich mit erledigenDELETE /api/v1/bibliothek/{entryId}/sichtungenmit{"action":"movie"}erreichbar machen, samtdocs/API.mdundopenapi.jsonZeiterfassung läuft.
✅ Umsetzung abgeschlossen — bereit zum Testen.
Was geändert wurde
unmarkMovie()als fehlendes Gegenstück zuunmarkEpisodes(), eingegrenzt aufrun: entry.currentRunundepisodeId: null, danachreconcileStatus()unmarkMovieActionanalog zutoggleEpisodeActionDELETE /api/v1/bibliothek/{entryId}/sichtungennimmt jetzt auch{"action":"movie"}— ohne Kenntnis derwatchIds;docs/API.mdundopenapi.jsonnachgezogenGeänderte Dateien
src/lib/library.tssrc/app/actions/library.tssrc/components/library/NextEpisode.tsxsrc/app/api/v1/bibliothek/[entryId]/sichtungen/route.tssrc/app/api/v1/openapi.json/route.tsdocs/API.mdtests/library.test.tsDer Nebenpunkt aus der Beschreibung trifft nicht zu
Ich hatte vermutet, die Einblendung biete bei einem schon markierten Film ein wirkungsloses Zurücknehmen an. Nachgesehen:
report()(src/components/Toasts.tsx:59) blendet den Knopf nur beiids.length > 0ein. Das Angebot gab es also nie — hier war nichts zu reparieren.Geprüft
59 Tests grün (drei neue), Typecheck sauber. Klickweg gegen einen Produktionsbau:
Über die Schnittstelle:
POST {"action":"movie"}→ 1 Sichtung,DELETE {"action":"movie"}→ 200, 0 Sichtungen, 0 %.So testest du es
git switch fix/5-gesehen-zuruecknehmennpm run dev(Port 3011), einen Film öffnen und „Als gesehen markieren" klickenAufgewendete Zeit: 00:10
Pull Request: #9
Nach Freigabe:
/issue-freigabe 5📝 Nebenbei aufgefallen — bewusst nicht in diesem PR erledigt:
Der Status bleibt beim Zurücknehmen stehen. Beim Prüfen fiel auf: nimmt man die Sichtung eines Films zurück, der dadurch auf „Abgeschlossen" gesprungen war, bleibt dieser Status stehen. Der Eintrag steht dann auf „Abgeschlossen" bei 0 %, während der Knopf wieder „Als gesehen markieren" anbietet.
reconcileStatus()(src/lib/library.ts:372) behandelt den Fallwatched === 0nicht — es gibt nur Zweige für „alles gesehen" und „mehr als null gesehen".Das ist kein neuer Fehler: bei Serien war der Zustand schon immer erreichbar, indem man alle Episoden abwählt. Durch den neuen Knopf wird er bei Filmen allerdings leicht erreichbar.
Ich habe es aus zwei Gründen nicht mitgeändert:
setStatus()rührt die Sichtungen nicht an — zu Recht"). Den Status beim Zurücknehmen automatisch umzustellen, würde diese Trennung wieder aufweichen.reconcileStatus()gilt für Serien und Filme gleichermaßen. Eine Änderung dort wäre ein Verhaltenseingriff weit über dieses Issue hinaus, und es ist nicht offensichtlich, worauf zurückgefallen werden sollte — aufWATCHLIST, oder auf den Stand davor?Der Datenbestand ist in jedem Fall unversehrt, und der Status lässt sich direkt darunter mit einem Klick setzen. Wäre aber eine eigene, gut umrissene Aufgabe fürs Dashboard.
🚀 Freigegeben und nach
mainübernommen.fd1ab29Vor dem Merge noch einmal geprüft: Typecheck sauber, 59 Tests grün.
Issue wird geschlossen.