From e9e885fc840eaa16fd0db14ff50f297b56f6cc97 Mon Sep 17 00:00:00 2001 From: 4gray Date: Sat, 4 Jul 2026 18:20:25 +0200 Subject: [PATCH] refactor(xtream): migrate VOD and serial detail templates to detail shell Hero chips/meta/actions become *detailTags/*detailMeta/*detailActions templates; the inline player moves to the full-width [detail-player] slot; trailer and similar rail move to [detail-extras]. Escape and the now-playing back button close the inline player back to browse. Co-Authored-By: Claude Fable 5 --- .../serial-details.component.html | 90 ++++++----- .../serial-details.component.spec.ts | 21 +-- .../serial-details.component.ts | 10 +- .../vod-details-route.component.html | 143 +++++++++--------- .../vod-details-route.component.ts | 12 +- .../lib/xtream-collection-detail.component.ts | 6 +- 6 files changed, 145 insertions(+), 137 deletions(-) diff --git a/libs/portal/xtream/feature/src/lib/serial-details/serial-details.component.html b/libs/portal/xtream/feature/src/lib/serial-details/serial-details.component.html index 9f56666bb..64b442115 100644 --- a/libs/portal/xtream/feature/src/lib/serial-details/serial-details.component.html +++ b/libs/portal/xtream/feature/src/lib/serial-details/serial-details.component.html @@ -1,14 +1,16 @@ @let item = selectedItem(); @if (item && item.info) { @let info = item.info; - - + @if (info.releaseDate) { {{ info.releaseDate | slice: 0 : 4 @@ -27,9 +29,9 @@ ⭐ {{ info.rating }} } - + - + @if (info.cast || info.tmdb_cast?.length) {
{{ 'XTREAM.CAST' | translate }} @@ -83,9 +85,9 @@ {{ info.director }}
} -
+ - + @if (quickStartAction(); as action) { - } - - - } -
+ + } + + + } + + } @else if (detailsError()) { - } @else { - + } diff --git a/libs/portal/xtream/feature/src/lib/serial-details/serial-details.component.spec.ts b/libs/portal/xtream/feature/src/lib/serial-details/serial-details.component.spec.ts index 000a22c51..d26751cfb 100644 --- a/libs/portal/xtream/feature/src/lib/serial-details/serial-details.component.spec.ts +++ b/libs/portal/xtream/feature/src/lib/serial-details/serial-details.component.spec.ts @@ -21,21 +21,6 @@ import { PortalInlinePlayerComponent } from '@iptvnator/ui/playback'; import { of } from 'rxjs'; import { SerialDetailsComponent } from './serial-details.component'; -@Component({ - selector: 'app-content-hero', - standalone: true, - template: '', -}) -class StubContentHeroComponent { - readonly title = input(undefined); - readonly description = input(undefined); - readonly posterUrl = input(undefined); - readonly backdropUrl = input(undefined); - readonly isLoading = input(false); - readonly errorMessage = input(null); - readonly backClicked = output(); -} - @Component({ selector: 'app-season-container', standalone: true, @@ -239,7 +224,6 @@ describe('SerialDetailsComponent', () => { .overrideComponent(SerialDetailsComponent, { remove: { imports: [ - ContentHeroComponent, MatIcon, PortalInlinePlayerComponent, SeasonContainerComponent, @@ -248,7 +232,6 @@ describe('SerialDetailsComponent', () => { }, add: { imports: [ - StubContentHeroComponent, StubMatIconComponent, StubPortalInlinePlayerComponent, StubSeasonContainerComponent, @@ -334,8 +317,8 @@ describe('SerialDetailsComponent', () => { expect(() => fixture.detectChanges()).not.toThrow(); const hero = fixture.debugElement.query( - By.directive(StubContentHeroComponent) - ).componentInstance as StubContentHeroComponent; + By.directive(ContentHeroComponent) + ).componentInstance as ContentHeroComponent; expect(hero.backdropUrl()).toBeUndefined(); }); diff --git a/libs/portal/xtream/feature/src/lib/serial-details/serial-details.component.ts b/libs/portal/xtream/feature/src/lib/serial-details/serial-details.component.ts index c637bde0a..c39931fea 100644 --- a/libs/portal/xtream/feature/src/lib/serial-details/serial-details.component.ts +++ b/libs/portal/xtream/feature/src/lib/serial-details/serial-details.component.ts @@ -14,7 +14,10 @@ import { MatSnackBar } from '@angular/material/snack-bar'; import { ActivatedRoute, Router } from '@angular/router'; import { TranslatePipe, TranslateService } from '@ngx-translate/core'; import { - ContentHeroComponent, + DetailActionsTemplateDirective, + DetailMetaTemplateDirective, + DetailTagsTemplateDirective, + PortalDetailShellComponent, SeasonContainerComponent, SeasonContainerPlaybackToggleRequest, SeasonContainerXtreamDownloadContext, @@ -57,8 +60,11 @@ import { ], providers: [SerialDetailsPlaybackService], imports: [ - ContentHeroComponent, + DetailActionsTemplateDirective, + DetailMetaTemplateDirective, + DetailTagsTemplateDirective, MatIcon, + PortalDetailShellComponent, PortalInlinePlayerComponent, SeasonContainerComponent, SlicePipe, diff --git a/libs/portal/xtream/feature/src/lib/vod-details/vod-details-route.component.html b/libs/portal/xtream/feature/src/lib/vod-details/vod-details-route.component.html index 1de3b1ae8..b12892727 100644 --- a/libs/portal/xtream/feature/src/lib/vod-details/vod-details-route.component.html +++ b/libs/portal/xtream/feature/src/lib/vod-details/vod-details-route.component.html @@ -1,13 +1,15 @@ @let item = selectedItem(); @if (selectedVodInfo(); as info) { - - + @if (info.releasedate) { {{ info.releasedate | slice: 0 : 4 @@ -27,9 +29,9 @@ @if (info.country) { {{ info.country }} } - + - + @if (info.actors || info.cast || info.tmdb_cast?.length) {
{{ 'XTREAM.ACTORS' | translate }} @@ -93,9 +95,9 @@ {{ info.rating_kinopoisk }}
} -
+ - + @if (hasPlaybackPosition()) { - } - - - } -
+ + } + + + } + +
} @else if (fallbackView(); as fallback) { - - + {{ fallback.format }} } - + - +
} -
-
+ + } @else if (detailsError()) { - } @else { - + } diff --git a/libs/portal/xtream/feature/src/lib/vod-details/vod-details-route.component.ts b/libs/portal/xtream/feature/src/lib/vod-details/vod-details-route.component.ts index 3b64ee0f2..463023e0c 100644 --- a/libs/portal/xtream/feature/src/lib/vod-details/vod-details-route.component.ts +++ b/libs/portal/xtream/feature/src/lib/vod-details/vod-details-route.component.ts @@ -14,7 +14,12 @@ import { MatIcon } from '@angular/material/icon'; import { MatSnackBar } from '@angular/material/snack-bar'; import { ActivatedRoute, Router } from '@angular/router'; import { TranslateModule, TranslateService } from '@ngx-translate/core'; -import { ContentHeroComponent } from '@iptvnator/ui/components'; +import { + DetailActionsTemplateDirective, + DetailMetaTemplateDirective, + DetailTagsTemplateDirective, + PortalDetailShellComponent, +} from '@iptvnator/ui/components'; import { SafePipe } from '@iptvnator/pipes'; import { createLogger } from '@iptvnator/portal/shared/util'; import { XtreamStore } from '@iptvnator/portal/xtream/data-access'; @@ -51,8 +56,11 @@ import { VodDetailsPlaybackService } from './vod-details-playback.service'; changeDetection: ChangeDetectionStrategy.OnPush, providers: [VodDetailsPlaybackService], imports: [ - ContentHeroComponent, + DetailActionsTemplateDirective, + DetailMetaTemplateDirective, + DetailTagsTemplateDirective, MatIcon, + PortalDetailShellComponent, SafePipe, SlicePipe, TranslateModule, diff --git a/libs/portal/xtream/feature/src/lib/xtream-collection-detail.component.ts b/libs/portal/xtream/feature/src/lib/xtream-collection-detail.component.ts index b6e56054e..e4020ffba 100644 --- a/libs/portal/xtream/feature/src/lib/xtream-collection-detail.component.ts +++ b/libs/portal/xtream/feature/src/lib/xtream-collection-detail.component.ts @@ -11,7 +11,7 @@ import { untracked, } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; -import { ContentHeroComponent } from '@iptvnator/ui/components'; +import { PortalDetailShellComponent } from '@iptvnator/ui/components'; import { UnifiedCollectionItem } from '@iptvnator/portal/shared/util'; import { XtreamPlaylistData, @@ -35,7 +35,7 @@ interface XtreamCollectionStateSnapshot { @Component({ selector: 'app-xtream-collection-detail', - imports: [ContentHeroComponent, NgComponentOutlet], + imports: [PortalDetailShellComponent, NgComponentOutlet], template: ` @if (detailComponent() && detailInjector()) { } @else { - + } `, changeDetection: ChangeDetectionStrategy.OnPush,