mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-08 17:06:15 -08:00
* perf(web): keep backup/restore and portal helpers off the initial path #1601 (parental lock) put about 35 KB onto the renderer's initial path by design (the lock service, lock store and enforcement gate the workspace resolver and the catalog data sources) and was merged with the ratchet red: renderer.initialBytes 1,655,428 against the 1,619,993 baseline. Offset it without touching the lock gate. Code splitting puts a module in the chunk shared by every entry that reaches it, so helpers only lazy routes use landed in initial chunks because eager files reach them through barrels: - PlaylistBackupService (only the lazy settings page) moves to @iptvnator/services/playlist-backup and out of the services barrel. - The eager Xtream data layer and root shell import the portal logger and DI tokens through @iptvnator/portal/shared/util/logger and /tokens instead of the barrel, whose navigation, keyboard-shortcut and download helpers (about 45 KB) belong to the lazy portal routes. renderer.initialBytes 1,655,428 -> 1,598,232 bytes (-57,196). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(performance): lower the initial-bytes baseline to 1,598,232 bytes Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: 4gray <fourgray@proton.me> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
176 lines
6.3 KiB
TypeScript
176 lines
6.3 KiB
TypeScript
import {
|
|
HttpClient,
|
|
provideHttpClient,
|
|
withInterceptorsFromDi,
|
|
withXhr,
|
|
} from '@angular/common/http';
|
|
import {
|
|
FullscreenOverlayContainer,
|
|
OverlayContainer,
|
|
} from '@angular/cdk/overlay';
|
|
import {
|
|
ApplicationConfig,
|
|
inject,
|
|
importProvidersFrom,
|
|
provideZoneChangeDetection,
|
|
} from '@angular/core';
|
|
import { MAT_FORM_FIELD_DEFAULT_OPTIONS } from '@angular/material/form-field';
|
|
import { provideAnimations } from '@angular/platform-browser/animations';
|
|
import { provideRouter, withComponentInputBinding } from '@angular/router';
|
|
import { provideServiceWorker } from '@angular/service-worker';
|
|
import { provideEffects } from '@ngrx/effects';
|
|
import { provideRouterStore, routerReducer } from '@ngrx/router-store';
|
|
import { provideStore } from '@ngrx/store';
|
|
import { provideStoreDevtools } from '@ngrx/store-devtools';
|
|
import { TranslateLoader, TranslateModule } from '@ngx-translate/core';
|
|
import { TranslateHttpLoader } from '@ngx-translate/http-loader';
|
|
import { PlaylistEffects, playlistReducer } from '@iptvnator/m3u-state';
|
|
import { NgxIndexedDBModule } from 'ngx-indexed-db';
|
|
import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader';
|
|
import {
|
|
PORTAL_EXTERNAL_PLAYBACK,
|
|
PORTAL_PLAYER,
|
|
} from '@iptvnator/portal/shared/util/tokens';
|
|
import { STALKER_PLAYLIST_CONNECTION_EDITOR } from '@iptvnator/playlist/shared/ui/stalker-connection-editor';
|
|
import { provideXtreamDataSource } from '@iptvnator/portal/xtream/data-access';
|
|
import {
|
|
provideParentalLockPlaylistCleanup,
|
|
DataService,
|
|
} from '@iptvnator/services';
|
|
import { dbConfig } from '@iptvnator/shared/interfaces';
|
|
import { AppConfig } from '../environments/environment';
|
|
import { routes } from './app.routes';
|
|
import { ElectronService } from './services/electron.service';
|
|
import { ExternalPlaybackService } from './services/external-playback.service';
|
|
import { PlayerService } from './services/player.service';
|
|
import { provideParentalLockPrompt } from './services/parental-lock-prompt.service';
|
|
import { providePortalPlaybackPositions } from './services/portal-playback-positions.service';
|
|
import { PwaService } from './services/pwa.service';
|
|
import { shouldEnableServiceWorker } from './services/runtime-config';
|
|
import { LazyStalkerPlaylistConnectionEditor } from './services/lazy-stalker-playlist-connection-editor';
|
|
import { provideWorkspaceShellActions } from './services/workspace-shell-actions.service';
|
|
|
|
// AoT requires an exported function for factories
|
|
export function HttpLoaderFactory(http: HttpClient): TranslateHttpLoader {
|
|
return new TranslateHttpLoader(http, './assets/i18n/', '.json');
|
|
}
|
|
|
|
/**
|
|
* Synchronously read the user's preferred language from localStorage so the
|
|
* very first render uses the right locale instead of flashing English first
|
|
* and re-rendering once the IDB-backed settings finish loading.
|
|
*
|
|
* The hint is written by AppComponent.initSettings() whenever the saved
|
|
* settings resolve, so first-ever boots fall back to English (no hint
|
|
* present yet) and every subsequent boot uses the saved language.
|
|
*/
|
|
const SUPPORTED_LANGS = new Set([
|
|
'ar',
|
|
'ary',
|
|
'by',
|
|
'de',
|
|
'el',
|
|
'en',
|
|
'es',
|
|
'fr',
|
|
'hu',
|
|
'it',
|
|
'ja',
|
|
'ko',
|
|
'nl',
|
|
'pl',
|
|
'pt',
|
|
'ru',
|
|
'tr',
|
|
'zh',
|
|
'zhtw',
|
|
]);
|
|
export const PREFERRED_LANGUAGE_STORAGE_KEY = 'iptvnator:preferred-language';
|
|
export function getInitialLanguage(): string {
|
|
try {
|
|
const saved = localStorage.getItem(PREFERRED_LANGUAGE_STORAGE_KEY);
|
|
if (saved && SUPPORTED_LANGS.has(saved)) {
|
|
return saved;
|
|
}
|
|
} catch {
|
|
// localStorage may throw in privacy modes; fall through to default.
|
|
}
|
|
return 'en';
|
|
}
|
|
|
|
/**
|
|
* Conditionally provides the necessary service based on the current environment
|
|
*/
|
|
export function DataFactory() {
|
|
if (window.electron) {
|
|
return inject(ElectronService);
|
|
}
|
|
return inject(PwaService);
|
|
}
|
|
|
|
export const appConfig: ApplicationConfig = {
|
|
providers: [
|
|
provideZoneChangeDetection({ eventCoalescing: true }),
|
|
provideRouter(routes, withComponentInputBinding()),
|
|
provideAnimations(),
|
|
// CDK overlays (menus, tooltips, dialogs) live in a container under
|
|
// <body>, which the browser hides behind an element in DOM fullscreen.
|
|
// This container follows the fullscreen element instead, so the
|
|
// fullscreen channel panel's sort/context menus and the controls'
|
|
// tooltips render while the player is fullscreen.
|
|
{ provide: OverlayContainer, useClass: FullscreenOverlayContainer },
|
|
provideHttpClient(withXhr(), withInterceptorsFromDi()),
|
|
provideStore({
|
|
router: routerReducer,
|
|
playlistState: playlistReducer,
|
|
}),
|
|
provideEffects([PlaylistEffects]),
|
|
provideRouterStore(),
|
|
...(AppConfig.production ? [] : [provideStoreDevtools({ maxAge: 25 })]),
|
|
provideServiceWorker('ngsw-worker.js', {
|
|
enabled: shouldEnableServiceWorker(),
|
|
registrationStrategy: 'registerWhenStable:30000',
|
|
}),
|
|
importProvidersFrom(
|
|
NgxIndexedDBModule.forRoot(dbConfig),
|
|
NgxSkeletonLoaderModule.forRoot({
|
|
animation: 'pulse',
|
|
loadingText: 'This item is actually loading...',
|
|
}),
|
|
TranslateModule.forRoot({
|
|
defaultLanguage: getInitialLanguage(),
|
|
loader: {
|
|
provide: TranslateLoader,
|
|
useFactory: HttpLoaderFactory,
|
|
deps: [HttpClient],
|
|
},
|
|
})
|
|
),
|
|
{
|
|
provide: DataService,
|
|
useFactory: DataFactory,
|
|
},
|
|
{
|
|
provide: PORTAL_PLAYER,
|
|
useExisting: PlayerService,
|
|
},
|
|
{
|
|
provide: PORTAL_EXTERNAL_PLAYBACK,
|
|
useExisting: ExternalPlaybackService,
|
|
},
|
|
...providePortalPlaybackPositions(),
|
|
{
|
|
provide: STALKER_PLAYLIST_CONNECTION_EDITOR,
|
|
useExisting: LazyStalkerPlaylistConnectionEditor,
|
|
},
|
|
...provideWorkspaceShellActions(),
|
|
...provideParentalLockPrompt(),
|
|
provideParentalLockPlaylistCleanup(),
|
|
...provideXtreamDataSource(),
|
|
{
|
|
provide: MAT_FORM_FIELD_DEFAULT_OPTIONS,
|
|
useValue: { appearance: 'outline', subscriptSizing: 'dynamic' },
|
|
},
|
|
],
|
|
};
|