mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-10 18:16:43 -08:00
fix(ui): app-prefix detail template directive selectors, build fixes
detailTags/detailMeta/detailActions → appDetailTags/appDetailMeta/ appDetailActions per @angular-eslint/directive-selector; type the Escape host listener as Event; drop a redundant ?? in season tabs. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
81605d3ded
commit
6e2cc56e2c
11 files changed
+26
-26
No files matched your search
@@ -622,7 +622,7 @@ This project uses modern Angular signal-based APIs and patterns. **ALWAYS** use
|
||||
|
||||
- Xtream and Stalker detail pages use the shared `PortalDetailShellComponent` (`libs/ui/components/src/lib/portal-detail-shell/`) with two states: **Browse** (hero with poster/metadata/actions, episodes below) and **Watch** (hero collapses with a ~300ms morph, the inline player takes the full content width, metadata moves to an About block below the episodes)
|
||||
- Watch state derives from `inlinePlayback() !== null` only; external MPV/VLC playback keeps the browse layout. Esc and the now-playing back button close the inline player back to browse without route navigation
|
||||
- Hosts pass hero chips/meta/actions as `*detailTags`/`*detailMeta`/`*detailActions` templates; the shell stamps them into both the hero and the About block
|
||||
- Hosts pass hero chips/meta/actions as `*appDetailTags`/`*appDetailMeta`/`*appDetailActions` templates; the shell stamps them into both the hero and the About block
|
||||
- Seasons are tabs (`SeasonTabsComponent`, dropdown beyond 6 seasons) with auto-selection (playing episode's season → resume season → first) that fires the same `seasonSelected` lazy-load/enrichment hooks as manual clicks; grid/list episode view toggle persists to localStorage; season descriptions come from `get_series_info` (Xtream) or TMDB (Stalker)
|
||||
- See `docs/architecture/embedded-inline-playback.md` ("Two-State Detail Layout")
|
||||
|
||||
|
||||
@@ -101,8 +101,8 @@ which owns the page scroll container and two layout states:
|
||||
|
||||
Contracts:
|
||||
|
||||
- Hosts provide hero chips/meta/actions as `*detailTags` / `*detailMeta` /
|
||||
`*detailActions` templates; the shell stamps them into the hero and again
|
||||
- Hosts provide hero chips/meta/actions as `*appDetailTags` / `*appDetailMeta` /
|
||||
`*appDetailActions` templates; the shell stamps them into the hero and again
|
||||
into the About block. Degradation stays "missing → not rendered".
|
||||
- The shell never wraps the `[detail-player]` slot in a conditional; the
|
||||
host's `@if (inlinePlayback())` is the only creator/destroyer of the
|
||||
|
||||
+3
-3
@@ -9,7 +9,7 @@
|
||||
(closePlayerRequested)="closeInlinePlayer()"
|
||||
>
|
||||
<!-- Tags: Year, Genre, Rating -->
|
||||
<ng-template detailTags>
|
||||
<ng-template appDetailTags>
|
||||
@if (serial.info.releasedate) {
|
||||
<span class="details__tag">{{ serial.info.releasedate }}</span>
|
||||
}
|
||||
@@ -29,7 +29,7 @@
|
||||
</ng-template>
|
||||
|
||||
<!-- Metadata Grid -->
|
||||
<ng-template detailMeta>
|
||||
<ng-template appDetailMeta>
|
||||
@if (serial.info.actors || serial.info.tmdb_cast?.length) {
|
||||
<div class="details__meta-item">
|
||||
<span class="label">{{ 'XTREAM.ACTORS' | translate }}</span>
|
||||
@@ -89,7 +89,7 @@
|
||||
</ng-template>
|
||||
|
||||
<!-- Action Buttons -->
|
||||
<ng-template detailActions>
|
||||
<ng-template appDetailActions>
|
||||
@if (quickStartAction(); as action) {
|
||||
<button
|
||||
class="play-btn"
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
(backClicked)="goBack()"
|
||||
(closePlayerRequested)="closeInlinePlayer()"
|
||||
>
|
||||
<ng-template detailTags>
|
||||
<ng-template appDetailTags>
|
||||
@if (info.releaseDate) {
|
||||
<span class="details__tag">{{
|
||||
info.releaseDate | slice: 0 : 4
|
||||
@@ -31,7 +31,7 @@
|
||||
}
|
||||
</ng-template>
|
||||
|
||||
<ng-template detailMeta>
|
||||
<ng-template appDetailMeta>
|
||||
@if (info.cast || info.tmdb_cast?.length) {
|
||||
<div class="details__meta-item">
|
||||
<span class="label">{{ 'XTREAM.CAST' | translate }}</span>
|
||||
@@ -87,7 +87,7 @@
|
||||
}
|
||||
</ng-template>
|
||||
|
||||
<ng-template detailActions>
|
||||
<ng-template appDetailActions>
|
||||
@if (quickStartAction(); as action) {
|
||||
<button
|
||||
class="play-btn"
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
(backClicked)="goBack()"
|
||||
(closePlayerRequested)="closeInlinePlayer()"
|
||||
>
|
||||
<ng-template detailTags>
|
||||
<ng-template appDetailTags>
|
||||
@if (info.releasedate) {
|
||||
<span class="details__tag">{{
|
||||
info.releasedate | slice: 0 : 4
|
||||
@@ -31,7 +31,7 @@
|
||||
}
|
||||
</ng-template>
|
||||
|
||||
<ng-template detailMeta>
|
||||
<ng-template appDetailMeta>
|
||||
@if (info.actors || info.cast || info.tmdb_cast?.length) {
|
||||
<div class="details__meta-item">
|
||||
<span class="label">{{ 'XTREAM.ACTORS' | translate }}</span>
|
||||
@@ -97,7 +97,7 @@
|
||||
}
|
||||
</ng-template>
|
||||
|
||||
<ng-template detailActions>
|
||||
<ng-template appDetailActions>
|
||||
@if (hasPlaybackPosition()) {
|
||||
<button
|
||||
class="play-btn play-btn--resume"
|
||||
@@ -277,7 +277,7 @@
|
||||
[posterUrl]="fallback.posterUrl"
|
||||
(backClicked)="goBack()"
|
||||
>
|
||||
<ng-template detailTags>
|
||||
<ng-template appDetailTags>
|
||||
<span
|
||||
class="details__tag details__tag--status"
|
||||
data-testid="xtream-vod-fallback-status"
|
||||
@@ -300,7 +300,7 @@
|
||||
}
|
||||
</ng-template>
|
||||
|
||||
<ng-template detailMeta>
|
||||
<ng-template appDetailMeta>
|
||||
<div
|
||||
class="details__fallback-note"
|
||||
data-testid="xtream-vod-fallback"
|
||||
|
||||
@@ -9,7 +9,7 @@ import { TranslateModule } from '@ngx-translate/core';
|
||||
*
|
||||
* Degradation rule: anything missing simply is not rendered — no "N/A"
|
||||
* placeholders. Chips and credits are stamped from the host-provided
|
||||
* *detailTags / *detailMeta templates, so the host's own @if guards apply.
|
||||
* *appDetailTags / *appDetailMeta templates, so the host's own @if guards apply.
|
||||
*/
|
||||
@Component({
|
||||
selector: 'app-content-about',
|
||||
|
||||
@@ -9,7 +9,7 @@ import { Directive, TemplateRef, inject } from '@angular/core';
|
||||
* appear once, a TemplateRef can be stamped any number of times.
|
||||
*/
|
||||
@Directive({
|
||||
selector: '[detailTags]',
|
||||
selector: '[appDetailTags]',
|
||||
standalone: true,
|
||||
})
|
||||
export class DetailTagsTemplateDirective {
|
||||
@@ -17,7 +17,7 @@ export class DetailTagsTemplateDirective {
|
||||
}
|
||||
|
||||
@Directive({
|
||||
selector: '[detailMeta]',
|
||||
selector: '[appDetailMeta]',
|
||||
standalone: true,
|
||||
})
|
||||
export class DetailMetaTemplateDirective {
|
||||
@@ -25,7 +25,7 @@ export class DetailMetaTemplateDirective {
|
||||
}
|
||||
|
||||
@Directive({
|
||||
selector: '[detailActions]',
|
||||
selector: '[appDetailActions]',
|
||||
standalone: true,
|
||||
})
|
||||
export class DetailActionsTemplateDirective {
|
||||
|
||||
+3
-3
@@ -24,13 +24,13 @@ import { PortalDetailShellComponent } from './portal-detail-shell.component';
|
||||
[playbackActive]="playbackActive()"
|
||||
(closePlayerRequested)="closeRequests = closeRequests + 1"
|
||||
>
|
||||
<ng-template detailTags>
|
||||
<ng-template appDetailTags>
|
||||
<span class="details__tag">2026</span>
|
||||
</ng-template>
|
||||
<ng-template detailMeta>
|
||||
<ng-template appDetailMeta>
|
||||
<div class="details__meta-item">Cast entry</div>
|
||||
</ng-template>
|
||||
<ng-template detailActions>
|
||||
<ng-template appDetailActions>
|
||||
<button class="play-btn">Play</button>
|
||||
</ng-template>
|
||||
@if (playbackActive()) {
|
||||
|
||||
@@ -76,7 +76,7 @@ export class PortalDetailShellComponent {
|
||||
});
|
||||
}
|
||||
|
||||
onEscape(event: KeyboardEvent): void {
|
||||
onEscape(event: Event): void {
|
||||
if (!this.playbackActive()) return;
|
||||
if (event.defaultPrevented) return;
|
||||
// Browser fullscreen owns Escape (exits fullscreen first).
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
}}
|
||||
</span>
|
||||
<span class="season-tabs__menu-count">
|
||||
{{ episodeCounts()[seasonKey] ?? 0 }}
|
||||
{{ episodeCounts()[seasonKey] }}
|
||||
</span>
|
||||
@if (isSeasonCompleted(seasonKey)) {
|
||||
<mat-icon class="season-tabs__done">check</mat-icon>
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
(closePlayerRequested)="closeInlinePlayback()"
|
||||
>
|
||||
<!-- Tags: Year, Genre, Duration, Rating -->
|
||||
<ng-template detailTags>
|
||||
<ng-template appDetailTags>
|
||||
@if (meta.year) {
|
||||
<span class="details__tag">{{ meta.year }}</span>
|
||||
}
|
||||
@@ -31,7 +31,7 @@
|
||||
</ng-template>
|
||||
|
||||
<!-- Metadata Grid -->
|
||||
<ng-template detailMeta>
|
||||
<ng-template appDetailMeta>
|
||||
@if (meta.actors || meta.tmdbCast?.length) {
|
||||
<div class="details__meta-item">
|
||||
<span class="label">{{ 'XTREAM.ACTORS' | translate }}</span>
|
||||
@@ -90,7 +90,7 @@
|
||||
</ng-template>
|
||||
|
||||
<!-- Action Buttons -->
|
||||
<ng-template detailActions>
|
||||
<ng-template appDetailActions>
|
||||
<!-- Resume/Play buttons -->
|
||||
@if (hasPlaybackPosition()) {
|
||||
<button
|
||||
|
||||
Reference in new issue
Block a user