From 3d4d2ca9bf30b029e8b97f96eda4c594ba8f8ec7 Mon Sep 17 00:00:00 2001 From: 4gray <4gray@users.noreply.github.com> Date: Tue, 26 May 2026 18:33:44 +0200 Subject: [PATCH] fix(angular): remove template diagnostics warnings (#1011) * fix(angular): remove template diagnostics warnings * fix(angular): preserve template fallback behavior --- .../account-info/account-info.component.html | 10 ++-- .../serial-details.component.html | 4 +- .../serial-details.component.spec.ts | 28 ++++++++--- .../serial-details.component.ts | 5 ++ .../vod-details-route.component.html | 2 +- .../vod-details-route.component.spec.ts | 45 ++++++++++++++--- .../vod-details-route.component.ts | 5 ++ .../channel-details-dialog.component.html | 4 +- .../recent-view/recent-view.component.html | 6 +-- .../season-container.component.html | 4 +- .../epg-list-item.component.html | 2 +- .../toolbar/toolbar.component.html | 2 +- .../web-player-view.component.html | 2 +- .../web-player-view.component.spec.ts | 27 +++++++++- .../web-player-view.component.ts | 9 ++-- .../src/lib/epg-view/epg-view.component.html | 9 ++-- .../lib/epg-view/epg-view.component.spec.ts | 50 +++++++++++++++++++ .../src/lib/epg-view/epg-view.component.ts | 8 ++- ...space-context-category-view.component.html | 2 +- 19 files changed, 184 insertions(+), 40 deletions(-) create mode 100644 libs/ui/shared-portals/src/lib/epg-view/epg-view.component.spec.ts diff --git a/libs/portal/xtream/feature/src/lib/account-info/account-info.component.html b/libs/portal/xtream/feature/src/lib/account-info/account-info.component.html index a5ae8ee7a..4cbafd473 100644 --- a/libs/portal/xtream/feature/src/lib/account-info/account-info.component.html +++ b/libs/portal/xtream/feature/src/lib/account-info/account-info.component.html @@ -3,10 +3,10 @@
@if (accountInfo(); as info) { - {{ info.server_info?.server_protocol || 'Xtream' }} - @if (info.server_info?.timezone) { + {{ info.server_info.server_protocol || 'Xtream' }} + @if (info.server_info.timezone) { - {{ info.server_info?.timezone }} + {{ info.server_info.timezone }} } } @else { Xtream @@ -29,7 +29,7 @@ class="account-dialog__status-pill" [class.account-dialog__status-pill--active]="isActive()" > - {{ info.user_info?.status || '-' }} + {{ info.user_info.status || '-' }} @if (isTrial()) { @@ -122,7 +122,7 @@

{{ 'XTREAM.ACCOUNT_INFO.USER_INFO' | translate }}

-

{{ info.user_info?.username || '-' }}

+

{{ info.user_info.username || '-' }}

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 ad0ab5e9b..64787faff 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 @@ -5,7 +5,7 @@ [title]="info.name" [description]="info.plot" [posterUrl]="info.cover" - [backdropUrl]="info.backdrop_path?.[0]" + [backdropUrl]="getBackdropUrl(info)" (backClicked)="goBack()" > @@ -101,7 +101,7 @@ [seasons]="item.episodes" [seriesId]="item.series_id" [playlistId]="currentPlaylistId()" - [seriesTitle]="info.name ?? ''" + [seriesTitle]="info.name" [playbackPositions]="episodePlaybackPositions()" [xtreamDownloadContext]="xtreamDownloadContext()" [openingEpisodeId]="openingEpisodeId()" 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 456db8613..ccfff63bb 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 @@ -278,6 +278,26 @@ describe('SerialDetailsComponent', () => { }); }); + it('renders series metadata when backdrop_path is absent at runtime', () => { + selectedItem.set({ + series_id: 103, + info: { + name: 'Series Without Backdrop', + plot: 'Series plot', + cover: 'cover.jpg', + genre: 'Drama', + }, + episodes: {}, + }); + + expect(() => fixture.detectChanges()).not.toThrow(); + + const hero = fixture.debugElement.query( + By.directive(StubContentHeroComponent) + ).componentInstance as StubContentHeroComponent; + expect(hero.backdropUrl()).toBeUndefined(); + }); + it('renders quick start as the first episode action and opens that episode', async () => { fixture.detectChanges(); await fixture.whenStable(); @@ -292,9 +312,7 @@ describe('SerialDetailsComponent', () => { expect(quickStartButton?.textContent).toContain( 'XTREAM.PLAY_FIRST_EPISODE' ); - expect(quickStartButton?.textContent).toContain( - 'S01E01 · Episode 1' - ); + expect(quickStartButton?.textContent).toContain('S01E01 · Episode 1'); quickStartButton?.click(); @@ -349,9 +367,7 @@ describe('SerialDetailsComponent', () => { expect(quickStartButton?.textContent).toContain( 'XTREAM.RESUME_EPISODE' ); - expect(quickStartButton?.textContent).toContain( - 'S01E01 · Episode 1' - ); + expect(quickStartButton?.textContent).toContain('S01E01 · Episode 1'); quickStartButton?.click(); 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 f518ef24b..893f73fa8 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 @@ -34,6 +34,7 @@ import { PlaybackPositionData, PlayerContentInfo, ResolvedPortalPlayback, + XtreamSerieInfo, XtreamSerieEpisode, XtreamSerieDetails, } from '@iptvnator/shared/interfaces'; @@ -309,6 +310,10 @@ export class SerialDetailsComponent implements OnInit, OnDestroy { ); } + getBackdropUrl(info: XtreamSerieInfo): string | undefined { + return info.backdrop_path?.[0]; + } + goBack(): void { this.closeInlinePlayer(); this.location.back(); 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 b13bbd318..6d9708189 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 @@ -4,7 +4,7 @@ [title]="info.name || item?.movie_data?.name" [description]="info.description || info.plot" [posterUrl]="info.movie_image || info.cover_big" - [backdropUrl]="info.backdrop_path?.[0]" + [backdropUrl]="getBackdropUrl(info)" (backClicked)="goBack()" > diff --git a/libs/portal/xtream/feature/src/lib/vod-details/vod-details-route.component.spec.ts b/libs/portal/xtream/feature/src/lib/vod-details/vod-details-route.component.spec.ts index 10e9b47cd..4970c8466 100644 --- a/libs/portal/xtream/feature/src/lib/vod-details/vod-details-route.component.spec.ts +++ b/libs/portal/xtream/feature/src/lib/vod-details/vod-details-route.component.spec.ts @@ -1,9 +1,11 @@ import { signal } from '@angular/core'; import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { By } from '@angular/platform-browser'; import { ActivatedRoute } from '@angular/router'; import { TranslateService } from '@ngx-translate/core'; import { of } from 'rxjs'; import { Location } from '@angular/common'; +import { ContentHeroComponent } from '@iptvnator/ui/components'; import { PORTAL_EXTERNAL_PLAYBACK, PORTAL_PLAYBACK_POSITIONS, @@ -37,9 +39,9 @@ describe('VodDetailsRouteComponent', () => { const checkFavoriteStatus = jest.fn(); const setSelectedItem = jest.fn(); const toggleFavorite = jest.fn(); - const constructVodStreamUrl = jest.fn().mockReturnValue( - 'http://example.com/movie/650020.mp4' - ); + const constructVodStreamUrl = jest + .fn() + .mockReturnValue('http://example.com/movie/650020.mp4'); const addRecentItem = jest.fn(); const downloads = signal([]); const getPlaybackPosition = jest.fn().mockResolvedValue(null); @@ -134,7 +136,9 @@ describe('VodDetailsRouteComponent', () => { provide: PORTAL_PLAYBACK_POSITIONS, useValue: { getPlaybackPosition, - savePlaybackPosition: jest.fn().mockResolvedValue(undefined), + savePlaybackPosition: jest + .fn() + .mockResolvedValue(undefined), }, }, { @@ -190,7 +194,8 @@ describe('VodDetailsRouteComponent', () => { const host = fixture.nativeElement as HTMLElement; expect(host.textContent).toContain('Die Kühe sind Los! (2004) DE'); expect( - host.querySelector('[data-testid="xtream-vod-fallback"]')?.textContent + host.querySelector('[data-testid="xtream-vod-fallback"]') + ?.textContent ).toContain('XTREAM.DETAIL_FALLBACK.NOTE'); expect( host.querySelector('[data-testid="xtream-vod-fallback-status"]') @@ -248,7 +253,35 @@ describe('VodDetailsRouteComponent', () => { const host = fixture.nativeElement as HTMLElement; expect(host.textContent).toContain('City of McFarland (2015)'); - expect(host.querySelector('[data-testid="xtream-vod-fallback"]')).toBeNull(); + expect( + host.querySelector('[data-testid="xtream-vod-fallback"]') + ).toBeNull(); expect(host.querySelector('button.play-btn')).not.toBeNull(); }); + + it('renders usable metadata when backdrop_path is absent at runtime', () => { + selectedItem.set({ + info: { + name: 'Metadata Without Backdrop', + description: 'A populated description', + movie_image: 'https://example.com/poster.jpg', + }, + movie_data: { + stream_id: 678140, + name: 'Metadata Without Backdrop', + added: '1750671180', + category_id: '235', + container_extension: 'mkv', + custom_sid: null, + direct_source: '', + }, + } as unknown as XtreamVodDetails); + + expect(() => fixture.detectChanges()).not.toThrow(); + + const hero = fixture.debugElement.query( + By.directive(ContentHeroComponent) + ).componentInstance as ContentHeroComponent; + expect(hero.backdropUrl()).toBeUndefined(); + }); }); 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 5a436853d..58f82b02e 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 @@ -38,6 +38,7 @@ import { ResolvedPortalPlayback, XtreamCategory, XtreamVodDetails, + XtreamVodInfo, XtreamVodStream, getXtreamVodInfo, } from '@iptvnator/shared/interfaces'; @@ -470,6 +471,10 @@ export class VodDetailsRouteComponent implements OnInit, OnDestroy { ); } + getBackdropUrl(info: XtreamVodInfo): string | undefined { + return info.backdrop_path?.[0]; + } + goBack(): void { this.closeInlinePlayer(); this.location.back(); diff --git a/libs/ui/components/src/lib/channel-list-container/channel-details-dialog/channel-details-dialog.component.html b/libs/ui/components/src/lib/channel-list-container/channel-details-dialog/channel-details-dialog.component.html index 427674563..2ad0bbc02 100644 --- a/libs/ui/components/src/lib/channel-list-container/channel-details-dialog/channel-details-dialog.component.html +++ b/libs/ui/components/src/lib/channel-list-container/channel-details-dialog/channel-details-dialog.component.html @@ -2,12 +2,12 @@
- {{ channel.group?.title || 'M3U' }} + {{ channel.group.title || 'M3U' }}
- @if (channel.tvg?.logo) { + @if (channel.tvg.logo) {
{{ season.key }}
Season
- @if (season.value?.length) { + @if (season.value.length) {
{{ season.value.length }} {{ diff --git a/libs/ui/epg/src/lib/epg-list/epg-list-item/epg-list-item.component.html b/libs/ui/epg/src/lib/epg-list/epg-list-item/epg-list-item.component.html index 3c2505781..b6bbc3955 100644 --- a/libs/ui/epg/src/lib/epg-list/epg-list-item/epg-list-item.component.html +++ b/libs/ui/epg/src/lib/epg-list/epg-list-item/epg-list-item.component.html @@ -21,7 +21,7 @@ } -@if (item?.desc) { +@if (item.desc) { info - {{ activeChannel?.name }} + {{ activeChannel.name }}