mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-15 05:36:17 -08:00
Merge remote-tracking branch 'origin/master' into fix/workspace-route-swap
# Conflicts: # apps/web/src/app/app.config.ts
This commit is contained in:
commit
3a68793df2
78 files changed
+3051
-218
No files matched your search
@@ -9,6 +9,7 @@ export * from './lib/confirm-dialog/confirm-dialog.component';
|
||||
export * from './lib/confirm-dialog/dialog.service';
|
||||
export * from './lib/content-hero/content-hero.component';
|
||||
export * from './lib/expandable-text/expandable-text.component';
|
||||
export * from './lib/form-field-defaults/form-field-defaults';
|
||||
export * from './lib/external-playback-dock/external-playback-dock.component';
|
||||
export * from './lib/portal-detail-shell/content-about.component';
|
||||
export * from './lib/portal-detail-shell/detail-template.directives';
|
||||
|
||||
+2
@@ -22,6 +22,7 @@ import { MatSnackBar } from '@angular/material/snack-bar';
|
||||
import { EpgRuntimeBridgeService, EpgService } from '@iptvnator/epg/data-access';
|
||||
import { TranslatePipe, TranslateService } from '@ngx-translate/core';
|
||||
import { debounceTime, from, Subject, switchMap } from 'rxjs';
|
||||
import { provideFormFieldDefaults } from '../../form-field-defaults/form-field-defaults';
|
||||
|
||||
export interface EpgMappingDialogData {
|
||||
channelKey: string;
|
||||
@@ -39,6 +40,7 @@ const SEARCH_MIN_CHARS = 2;
|
||||
|
||||
@Component({
|
||||
selector: 'app-epg-mapping-dialog',
|
||||
providers: [provideFormFieldDefaults()],
|
||||
templateUrl: './epg-mapping-dialog.component.html',
|
||||
styleUrl: './epg-mapping-dialog.component.scss',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
import { Provider } from '@angular/core';
|
||||
import {
|
||||
MAT_FORM_FIELD_DEFAULT_OPTIONS,
|
||||
MatFormFieldDefaultOptions,
|
||||
} from '@angular/material/form-field';
|
||||
|
||||
/** The app's outlined, dynamically sized form fields (see m3-theme.scss). */
|
||||
export const APP_FORM_FIELD_DEFAULTS: MatFormFieldDefaultOptions = {
|
||||
appearance: 'outline',
|
||||
subscriptSizing: 'dynamic',
|
||||
};
|
||||
|
||||
/**
|
||||
* Provide the app's form-field defaults on a component that renders
|
||||
* `<mat-form-field>`. They are not provided in the root injector: importing
|
||||
* the token there loads `@angular/material/form-field` (and, since Material
|
||||
* 22.2, `@angular/forms`) on startup, while every form field lives in a lazy
|
||||
* chunk. `tools/nx/check-form-field-defaults.mjs` enforces this.
|
||||
*/
|
||||
export function provideFormFieldDefaults(): Provider {
|
||||
return {
|
||||
provide: MAT_FORM_FIELD_DEFAULT_OPTIONS,
|
||||
useValue: APP_FORM_FIELD_DEFAULTS,
|
||||
};
|
||||
}
|
||||
+2
@@ -27,6 +27,7 @@ import {
|
||||
PARENTAL_LOCK_PIN_MAX_LENGTH,
|
||||
PARENTAL_LOCK_PIN_MIN_LENGTH,
|
||||
} from '@iptvnator/shared/interfaces';
|
||||
import { provideFormFieldDefaults } from '../form-field-defaults/form-field-defaults';
|
||||
|
||||
export type ParentalLockPinDialogMode = 'unlock' | 'set';
|
||||
|
||||
@@ -59,6 +60,7 @@ export interface ParentalLockPinDialogData {
|
||||
*/
|
||||
@Component({
|
||||
selector: 'app-parental-lock-pin-dialog',
|
||||
providers: [provideFormFieldDefaults()],
|
||||
templateUrl: './parental-lock-pin-dialog.component.html',
|
||||
styleUrl: './parental-lock-pin-dialog.component.scss',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
|
||||
@@ -166,18 +166,20 @@ describe('VodDetailsComponent offline playback', () => {
|
||||
};
|
||||
|
||||
const render = async ({
|
||||
item = STALKER_VOD,
|
||||
playbackPosition = null,
|
||||
externalPlayback = null,
|
||||
providerOnly = false,
|
||||
isWatched = false,
|
||||
}: {
|
||||
item?: VodDetailsItem;
|
||||
playbackPosition?: number | null;
|
||||
externalPlayback?: ExternalPlayerSession | null;
|
||||
providerOnly?: boolean;
|
||||
isWatched?: boolean;
|
||||
} = {}) => {
|
||||
fixture = TestBed.createComponent(VodDetailsComponent);
|
||||
fixture.componentRef.setInput('item', STALKER_VOD);
|
||||
fixture.componentRef.setInput('item', item);
|
||||
fixture.componentRef.setInput(
|
||||
'playbackSessionKey',
|
||||
'stalker-host-owned-key'
|
||||
@@ -378,6 +380,46 @@ describe('VodDetailsComponent offline playback', () => {
|
||||
fixture?.destroy();
|
||||
});
|
||||
|
||||
it('renders no provider stand-ins in the hero or the About block', async () => {
|
||||
await render({
|
||||
item: createStalkerVodItem(
|
||||
{
|
||||
id: '43',
|
||||
cmd: '/media/file_43.mpg',
|
||||
info: {
|
||||
name: 'Placeholder Movie',
|
||||
o_name: 'N/A',
|
||||
movie_image: 'N/A',
|
||||
description: 'N/A',
|
||||
actors: 'N/A',
|
||||
director: 'N/A',
|
||||
releasedate: 'N/A',
|
||||
genre: '-',
|
||||
rating_imdb: 'N/A',
|
||||
rating_kinopoisk: '0',
|
||||
},
|
||||
},
|
||||
'stalker-1'
|
||||
),
|
||||
});
|
||||
// Watch state stamps the same templates into the About block
|
||||
fixture.componentRef.setInput('inlinePlayback', {
|
||||
streamUrl: 'https://portal.example/movie.mp4',
|
||||
title: 'Placeholder Movie',
|
||||
});
|
||||
fixture.detectChanges();
|
||||
|
||||
const text = fixture.nativeElement.textContent as string;
|
||||
expect(text).toContain('Placeholder Movie');
|
||||
expect(text).not.toMatch(/N\/A/i);
|
||||
expect(text).not.toContain('⭐');
|
||||
expect(
|
||||
fixture.nativeElement.querySelectorAll(
|
||||
'.details__tag:not(.details__tag--clickable), .details__meta-item'
|
||||
).length
|
||||
).toBe(0);
|
||||
});
|
||||
|
||||
it('renders Offline and plays a completed download from the primary action', async () => {
|
||||
completeDownload();
|
||||
await render();
|
||||
|
||||
Reference in new issue
Block a user