mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-09 17:36:15 -08:00
Add an EPG list view — a vertical, single-day programme list — as an alternative rendering of the live EPG panel, selectable via a new Settings → EPG → "Guide view" toggle (epgViewMode: 'timeline' | 'list', default 'timeline' so existing users see no change). - New EpgListViewComponent (app-epg-list-view) mirrors EpgTimelineComponent's input/output contract 1:1, so all four live hosts (M3U player, unified live tab, Xtream, Stalker) swap the panel with a plain @if and identical bindings. - Reuses the shared view-agnostic EPG modules (classifyTimelineWhen, hasProgramsForDateKey, epg-archive.util catch-up gating, epg-summary.util collapsed-summary maths, epg-date helpers, EpgProgrammeDialogService, app-epg-timeline-empty-state) — no duplicated logic. - Rows show time range, title, optional description, live progress on the on-air row, catch-up "Watch" on past rows when archive playback is available, and a details dialog; keyboard activation guards nested buttons (target === currentTarget). - Auto-focuses the on-air row on channel select, restores it across collapse/expand remounts, and shows a sticky in-flow "On now" strip (never overlaying rows) when the current programme is scrolled away; all scroll maths is rect-based relative to the scroller. - List mode raises only the inline panel height via an epg--list modifier (--epg-inline-height clamp); timeline and collapsed heights are unchanged. - Setting flows end-to-end (Settings interface → DEFAULT_SETTINGS → SettingsStore/StorageMap → segmented control in the EPG section); Electron-only UI, PWA stays on the timeline default. i18n keys added to all 18 locales. - Tests: new component/row/utils/scroll-controller specs, settings persistence spec, swap tests in all four host specs, and Electron E2E for the settings round-trip and the rendered list view. Docs updated (m3u-playlist-module.md). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
47 lines
1.9 KiB
TypeScript
47 lines
1.9 KiB
TypeScript
import { Component, input, output, ViewEncapsulation } from '@angular/core';
|
|
import { FormArray, FormGroup, ReactiveFormsModule } from '@angular/forms';
|
|
import { MatButtonModule } from '@angular/material/button';
|
|
import { MatCheckboxModule } from '@angular/material/checkbox';
|
|
import { MatFormFieldModule } from '@angular/material/form-field';
|
|
import { MatIconModule } from '@angular/material/icon';
|
|
import { MatInputModule } from '@angular/material/input';
|
|
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
|
|
import { MatTooltipModule } from '@angular/material/tooltip';
|
|
import { EpgViewMode } from '@iptvnator/shared/interfaces';
|
|
import { EpgSourceStatusComponent } from '@iptvnator/ui/epg';
|
|
import { TranslateModule } from '@ngx-translate/core';
|
|
import { EpgViewModeOption } from './settings.models';
|
|
|
|
@Component({
|
|
selector: 'app-settings-epg-section',
|
|
imports: [
|
|
EpgSourceStatusComponent,
|
|
MatButtonModule,
|
|
MatCheckboxModule,
|
|
MatFormFieldModule,
|
|
MatIconModule,
|
|
MatInputModule,
|
|
MatProgressSpinnerModule,
|
|
MatTooltipModule,
|
|
ReactiveFormsModule,
|
|
TranslateModule,
|
|
],
|
|
templateUrl: './settings-epg-section.component.html',
|
|
encapsulation: ViewEncapsulation.None,
|
|
styles: [':host { display: contents; }'],
|
|
})
|
|
export class SettingsEpgSectionComponent {
|
|
readonly form = input.required<FormGroup>();
|
|
readonly activeSection = input.required<string>();
|
|
readonly epgUrl = input.required<FormArray>();
|
|
readonly isClearingEpgData = input(false);
|
|
readonly epgViewModeOptions = input.required<EpgViewModeOption[]>();
|
|
|
|
readonly refreshEpg = output<string>();
|
|
readonly removeEpgSource = output<number>();
|
|
readonly addEpgSource = output<void>();
|
|
readonly refreshAllEpg = output<void>();
|
|
readonly clearEpgData = output<void>();
|
|
readonly selectEpgViewMode = output<EpgViewMode>();
|
|
}
|