Files
iptvnator/apps/web/src/app/app.config.ts
T

162 lines
5.6 KiB
TypeScript

import {
HttpClient,
provideHttpClient,
withInterceptorsFromDi,
} from '@angular/common/http';
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';
import { PLAYLIST_PLAYER_ACTIONS } from '@iptvnator/playlist/shared/util';
import { provideXtreamDataSource } from '@iptvnator/portal/xtream/data-access';
import { 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 {
AppPortalNavigationActionsService,
providePortalNavigationActions,
} from './services/portal-navigation-actions.service';
import { providePortalPlaybackPositions } from './services/portal-playback-positions.service';
import { PwaService } from './services/pwa.service';
import { shouldEnableServiceWorker } from './services/runtime-config';
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',
'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(),
provideHttpClient(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(),
...providePortalNavigationActions(),
{
provide: PLAYLIST_PLAYER_ACTIONS,
useExisting: AppPortalNavigationActionsService,
},
...provideWorkspaceShellActions(),
...provideXtreamDataSource(),
{
provide: MAT_FORM_FIELD_DEFAULT_OPTIONS,
useValue: { appearance: 'outline', subscriptSizing: 'dynamic' },
},
],
};