feat(xtream): catch-up badge for live channels with archive (#1341)

* feat(xtream): show a catch-up badge on live channels that have archive

Live channels whose provider declares playable catch-up (tv_archive=1
with a positive tv_archive_duration) now show a small history badge in
the channel sidebar next to the name and on the all-channels grid cards,
with the archive window (days) in the tooltip.

Closes #1128

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

* fix(xtream): expose the catch-up badge status to assistive technology

The mat-icon is aria-hidden and the tooltip is pointer-only, so the
badge status was invisible to keyboard and screen-reader users. Both
badge surfaces now also render the translated status as visually-hidden
text (Codex review, P2).

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

* feat(xtream): show the catch-up badge in favorites and recent lists

Carries tvArchive/tvArchiveDuration through UnifiedFavoriteChannel so
the shared favorites list (portal favorites/recent tabs and global
favorites) renders the same catch-up badge as the live sidebar.
Requested in PR feedback by the issue author.

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

* feat(ui): show the programme-info button in portal sidebars, stacked vertically

Adds the (i) programme-info button to the Xtream and Stalker live
sidebars and reworks the row action column: buttons stack vertically
(favorite on top, info below), so the second button costs no horizontal
space — the column is actually narrower than the previous single-button
row. The info slot is reserved (inert, visibility:hidden) while the row
has no programme, so the star never shifts when EPG data arrives.
Requested by the issue author in PR feedback.

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

* test(ui): move the archive passthrough spec out of the budget-capped file

CI lints the merge with master, where unified-live-tab.component.spec.ts
grew (#1374) to one line under the 1200 max-lines test budget — the
archive passthrough test added here tipped the merged result over. The
test moves to a focused template-less spec (plus a null-normalisation
case), leaving the main spec at master's size.

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

* fix(ui): constrain Material touch targets to the stacked button bounds

mat-icon-button keeps a 48px touch target; stacked 28px buttons
overlapped by 20px and the later sibling (programme info) stole clicks
from the lower third of the favorite star. Verified via
document.elementFromPoint before/after: the star's visual bounds now hit
the star, and clicks left of the column reach the row again instead of
the button's oversized target (Codex review).

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

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
4grayandClaude Fable 5 authored and GitHub committed 2026-08-09 10:47:44 +02:00
1 parent 87dc45957b
commit d5f84fb130
42 files changed
+689 -22

No files matched your search

@@ -38,6 +38,8 @@ interface XtreamGlobalFavoriteRow {
readonly position?: number | null;
readonly poster_url?: string | null;
readonly title: string;
readonly tv_archive?: number | null;
readonly tv_archive_duration?: number | null;
readonly xtream_id: number;
}
@@ -220,6 +222,8 @@ export class GlobalFavoritesService {
playlistId: item.playlist_id,
playlistName: item.playlist_name,
xtreamId: item.xtream_id,
tvArchive: item.tv_archive ?? null,
tvArchiveDuration: item.tv_archive_duration ?? null,
tvgId: String(item.xtream_id),
addedAt: item.added_at ?? new Date(0).toISOString(),
position: item.position ?? 0,