* fix(settings): hide cached title matches the parental lock withholds
Cross-playlist title matches are cached by their consumers (Actor and
Discover routes, the dashboard trending and recommendation rails and
the four "similar in your portals" rails), so matches found while
unlocked kept advertising locked titles and their playlist names after
a relock. Each consumer now filters on read through a reactive
predicate on the match's provider category, so a relock hides them at
once and an unlock shows them again, without a re-query.
Closes#1723
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(settings): fall back to unlocked copies of a withheld title match
The dashboard trending and recommendation rails and the "similar in your
portals" rails picked one match per title at load, so hiding a withheld
match lost a copy of the same title in an unlocked portal. They now keep
every row the lookup returned and pick the match on read from the rows
the parental lock does not withhold. Adds the release note.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* fix(settings): derive the recommendations seed heading from the visible cards
The rail kept only the seeds that contributed a card to the original
selection, so a seed whose recommendations filled in after a relock (or
an offline prune) was missing from the "Because you watched" heading.
Every seed of the load is kept in order and the heading lists those that
contribute a card now.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: 4gray <fourgray@proton.me>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* feat(dashboard): warn on source cards when a portal subscription expires soon
Dashboard source cards now carry a passive expiry chip: amber "Expires in
N d" within 7 days of the subscription lapsing, error-toned "Expired" once
it has. Account details stay behind the card's ⋮ → Account info.
Xtream expirations ride on the playlist switcher's cached PortalStatusService
check — checkPortalStatusDetails() now surfaces the parsed exp_date from the
same round-trip, so the dashboard adds no extra portal calls. Stalker
expirations come from the stalkerAccountInfo snapshot persisted at import;
it lives in the playlist payload (meta rows carry payload: null), so each
Stalker source costs one full-playlist read memoized on the playlist's
update timestamp.
New i18n keys added to all 19 locales via the i18n-fill merger.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(dashboard): address review feedback on expiry badges
- Recompute expiry badges on a minute tick so a dashboard left open
crosses day-countdown and expiration boundaries (Greptile P1 / Codex P2)
- Gate the expiry refresh on the recent-sources rail setting so hidden
rails cost no portal checks or playlist reads (Codex P2)
- Move chip colors to theme-aware tokens in m3-theme.scss; both themes
now hold >= 4.5:1 small-text contrast (light warn 5.3:1, light expired
5.4:1, dark warn 7.4:1, dark expired 6.0:1) (Codex P2)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(dashboard): make expiry-badge labels depend on the language signal
sourceCards previously relied on getPlaylistProvider's indirect language
read; the translate.instant() labels now read languageTick explicitly
(mirroring trendingCards). Also shift the minute tick by one so the
interval's first 0 differs from initialValue — the signal equality check
was swallowing the first heartbeat, delaying it to two minutes.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>