feat(dashboard): detail-first Continue Watching cards with quick actions (#1469)

* feat(dashboard): detail-first continue watching cards with quick actions (#1441)

Continue Watching cards now open the detail page on click like movie
cards; resuming the saved episode, marking it watched, and removing the
entry from history move into a per-card ⋮ menu. Series details land on
the earliest season with unwatched episodes (or the latest once all are
watched) instead of always season 1.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(dashboard): address review findings and season auto-select regressions

- A session's own watched toggles no longer re-resolve the selected
  season when the positions map first fills — marking season 1 watched
  used to jump the view to season 2 (CI regression in the web and
  Electron season-watched-toggle E2Es).
- The all-watched season fallback skips loaded-but-empty seasons and
  picks the latest season that has episodes (Greptile P1).
- Mark as Watched uses the strict failure-propagating save boundary
  (Codex P2), and both card mutations surface persistence failures via
  a snackbar with the new WORKSPACE.DASHBOARD.ACTION_FAILED key in all
  19 languages (Greptile P2).
- Season E2Es now assert the intended post-reload behavior: the fresh
  mount lands on the earliest unwatched season while season 1 keeps its
  watched state behind its tab.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: 4gray <fourgray@proton.me>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
authored and GitHub committed 2026-08-23 08:06:46 +02:00
1 parent 5b2eb515d1
commit 72727a5dfa
36 files changed
+722 -44

No files matched your search

+12 -3
View File
@@ -754,15 +754,24 @@ test('@xtream season watched toggle — marks a season, survives reload, and cle
);
// PWA persistence: positions live in localStorage, so a reload of the
// detail route must come back fully watched.
// detail route must come back fully watched. With season 1 completed the
// fresh mount auto-selects the earliest season with unwatched episodes
// (issue #1441), so season 2 opens while season 1's tab keeps the check.
await page.reload();
await expect(seasonToggle).toBeVisible({ timeout: 20_000 });
await expect(seasonToggle).toContainText('Mark season as unwatched');
await expect(watchedCards).toHaveCount(8, { timeout: 10_000 });
await expect(seasonToggle).toContainText('Mark season as watched (8)');
await expect(watchedCards).toHaveCount(0, { timeout: 10_000 });
await expect(
seasonTabs.first().locator('.season-tabs__done')
).toBeVisible();
// Season 1 itself still holds the 8 persisted rows.
await seasonTabs.first().click();
await expect(seasonToggle).toContainText('Mark season as unwatched', {
timeout: 15_000,
});
await expect(watchedCards).toHaveCount(8, { timeout: 10_000 });
// Second click clears every episode's position again.
await seasonToggle.click();
await expect(seasonToggle).toContainText('Mark season as watched (8)', {