Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2ece38e52d | ||
|
|
e2356954ef |
No files matched your search
+2
-15
@@ -1,31 +1,18 @@
|
|||||||
import { APP_INITIALIZER, ApplicationConfig, provideBrowserGlobalErrorListeners } from '@angular/core';
|
import { ApplicationConfig, provideBrowserGlobalErrorListeners } from '@angular/core';
|
||||||
import { provideHttpClient, withInterceptors } from '@angular/common/http';
|
import { provideHttpClient, withInterceptors } from '@angular/common/http';
|
||||||
import { provideRouter } from '@angular/router';
|
import { provideRouter } from '@angular/router';
|
||||||
|
|
||||||
import { routes } from './app.routes';
|
import { routes } from './app.routes';
|
||||||
import { authInterceptor } from './interceptors/auth.interceptor';
|
import { authInterceptor } from './interceptors/auth.interceptor';
|
||||||
import {
|
import {
|
||||||
appConfigInitProvider,
|
|
||||||
appConfigProvider,
|
appConfigProvider,
|
||||||
} from './services/app-config.service';
|
} from './services/app-config.service';
|
||||||
import { StationConfigService } from './services/station-config.service';
|
|
||||||
|
|
||||||
export const stationConfigInitProvider = {
|
|
||||||
provide: APP_INITIALIZER,
|
|
||||||
useFactory: (stationConfigService: StationConfigService) => {
|
|
||||||
return () => stationConfigService.load();
|
|
||||||
},
|
|
||||||
deps: [StationConfigService],
|
|
||||||
multi: true,
|
|
||||||
};
|
|
||||||
|
|
||||||
export const appConfig: ApplicationConfig = {
|
export const appConfig: ApplicationConfig = {
|
||||||
providers: [
|
providers: [
|
||||||
provideBrowserGlobalErrorListeners(),
|
provideBrowserGlobalErrorListeners(),
|
||||||
provideRouter(routes),
|
|
||||||
provideHttpClient(withInterceptors([authInterceptor])),
|
provideHttpClient(withInterceptors([authInterceptor])),
|
||||||
|
provideRouter(routes),
|
||||||
appConfigProvider,
|
appConfigProvider,
|
||||||
appConfigInitProvider,
|
|
||||||
stationConfigInitProvider,
|
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
import { Component, computed, effect, inject, OnInit, OnDestroy, PLATFORM_ID } from '@angular/core';
|
import { Component, computed, effect, inject, OnInit, OnDestroy, PLATFORM_ID } from '@angular/core';
|
||||||
import { isPlatformBrowser } from '@angular/common';
|
import { isPlatformBrowser } from '@angular/common';
|
||||||
|
import { HttpClient } from '@angular/common/http';
|
||||||
import { RouterOutlet } from '@angular/router';
|
import { RouterOutlet } from '@angular/router';
|
||||||
import { NavbarComponent } from './navbar/navbar.component';
|
import { NavbarComponent } from './navbar/navbar.component';
|
||||||
import { MediaPlayerComponent } from './media-player/media-player.component';
|
import { MediaPlayerComponent } from './media-player/media-player.component';
|
||||||
@@ -7,6 +8,7 @@ import { FooterComponent } from './footer/footer.component';
|
|||||||
import { StructuredDataComponent } from './structured-data/structured-data.component';
|
import { StructuredDataComponent } from './structured-data/structured-data.component';
|
||||||
import { StationConfigService } from './services/station-config.service';
|
import { StationConfigService } from './services/station-config.service';
|
||||||
import { SeoService } from './services/seo.service';
|
import { SeoService } from './services/seo.service';
|
||||||
|
import { loadAppConfigAsync } from './services/app-config.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-root',
|
selector: 'app-root',
|
||||||
@@ -19,6 +21,7 @@ export class App implements OnInit, OnDestroy {
|
|||||||
private stationConfig = inject(StationConfigService);
|
private stationConfig = inject(StationConfigService);
|
||||||
private seoService = inject(SeoService);
|
private seoService = inject(SeoService);
|
||||||
private platformId = inject(PLATFORM_ID);
|
private platformId = inject(PLATFORM_ID);
|
||||||
|
private http = inject(HttpClient);
|
||||||
|
|
||||||
readonly hasStream = computed(() => {
|
readonly hasStream = computed(() => {
|
||||||
const cfg = this.stationConfig.config();
|
const cfg = this.stationConfig.config();
|
||||||
@@ -54,6 +57,12 @@ export class App implements OnInit, OnDestroy {
|
|||||||
// These DOM operations only run in the browser.
|
// These DOM operations only run in the browser.
|
||||||
if (!isPlatformBrowser(this.platformId)) return;
|
if (!isPlatformBrowser(this.platformId)) return;
|
||||||
|
|
||||||
|
// Load runtime config.json and station config asynchronously in the
|
||||||
|
// background — do not block rendering. The app already renders with
|
||||||
|
// defaults from the SSR/prerendered HTML.
|
||||||
|
loadAppConfigAsync(this.http).catch(() => {});
|
||||||
|
this.stationConfig.load().catch(() => {});
|
||||||
|
|
||||||
// Start watching router for automatic SEO metadata updates per route.
|
// Start watching router for automatic SEO metadata updates per route.
|
||||||
this.seoService.watchRouter();
|
this.seoService.watchRouter();
|
||||||
// Handle the initial route (NavigationEnd already fired before ngOnInit).
|
// Handle the initial route (NavigationEnd already fired before ngOnInit).
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { HttpClient } from '@angular/common/http';
|
import { HttpClient } from '@angular/common/http';
|
||||||
import { APP_INITIALIZER, InjectionToken } from '@angular/core';
|
import { InjectionToken } from '@angular/core';
|
||||||
import { lastValueFrom } from 'rxjs';
|
import { lastValueFrom } from 'rxjs';
|
||||||
|
|
||||||
import { environment } from '../../environments/environment';
|
import { environment } from '../../environments/environment';
|
||||||
@@ -16,15 +16,19 @@ const _appConfig: AppConfig = {
|
|||||||
apiBaseUrl: environment.apiBaseUrl,
|
apiBaseUrl: environment.apiBaseUrl,
|
||||||
};
|
};
|
||||||
|
|
||||||
export function appConfigFactory(http: HttpClient) {
|
/**
|
||||||
return (): Promise<void> => {
|
* Load runtime config.json asynchronously AFTER the app boots.
|
||||||
|
* Replaces the former APP_INITIALIZER that blocked the initial render.
|
||||||
|
* This way the page renders immediately with environment defaults, then
|
||||||
|
* patches in the runtime API URL once it arrives.
|
||||||
|
*/
|
||||||
|
export function loadAppConfigAsync(http: HttpClient): Promise<void> {
|
||||||
return lastValueFrom(http.get<AppConfig>('config.json')).then(
|
return lastValueFrom(http.get<AppConfig>('config.json')).then(
|
||||||
(config) => { Object.assign(_appConfig, config); },
|
(config) => { Object.assign(_appConfig, config); },
|
||||||
() => {
|
() => {
|
||||||
// Keep defaults if config.json is not available (dev mode).
|
// Keep defaults if config.json is not available (dev mode).
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const appConfigProvider = {
|
export const appConfigProvider = {
|
||||||
@@ -32,12 +36,12 @@ export const appConfigProvider = {
|
|||||||
useValue: _appConfig,
|
useValue: _appConfig,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const appConfigInitProvider = {
|
/**
|
||||||
provide: APP_INITIALIZER,
|
* @deprecated The APP_INITIALIZER is no longer registered by default.
|
||||||
useFactory: appConfigFactory,
|
* The `loadAppConfigAsync` function is called directly from `app.config.ts`
|
||||||
deps: [HttpClient],
|
* in a non-blocking way. This export is kept for backward compatibility only.
|
||||||
multi: true,
|
*/
|
||||||
};
|
export const appConfigInitProvider = null;
|
||||||
|
|
||||||
export function getAppConfig(): AppConfig {
|
export function getAppConfig(): AppConfig {
|
||||||
return _appConfig;
|
return _appConfig;
|
||||||
|
|||||||
@@ -6,7 +6,10 @@ import { StationConfig } from '../interfaces/station-config';
|
|||||||
import { getAppConfig } from './app-config.service';
|
import { getAppConfig } from './app-config.service';
|
||||||
import { ThemeService } from './theme.service';
|
import { ThemeService } from './theme.service';
|
||||||
|
|
||||||
/** Hardcoded defaults — used when the API is unavailable (dev fallback). */
|
/**
|
||||||
|
* Hardcoded defaults — used when the API is unavailable (dev fallback).
|
||||||
|
* Rendered immediately on the initial paint so the page is never blank.
|
||||||
|
*/
|
||||||
const DEFAULT_CONFIG: StationConfig = {
|
const DEFAULT_CONFIG: StationConfig = {
|
||||||
id: 0,
|
id: 0,
|
||||||
key: 'default',
|
key: 'default',
|
||||||
|
|||||||
@@ -6,6 +6,10 @@
|
|||||||
<base href="/" />
|
<base href="/" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
<link rel="icon" type="image/x-icon" href="favicon.ico" />
|
<link rel="icon" type="image/x-icon" href="favicon.ico" />
|
||||||
|
<!-- Preload hero background image (LCP element). The default SVG is inlined
|
||||||
|
here as a fallback; the runtime value from station config replaces it
|
||||||
|
dynamically after hydration. -->
|
||||||
|
<link rel="preload" as="image" href="svg/mountain-landscape.svg" />
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<app-root></app-root>
|
<app-root></app-root>
|
||||||
|
|||||||
Reference in new issue
Block a user