Compare commits
2
Commits
c4e2dc5b9f
...
2ece38e52d
| 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 { provideRouter } from '@angular/router';
|
||||
|
||||
import { routes } from './app.routes';
|
||||
import { authInterceptor } from './interceptors/auth.interceptor';
|
||||
import {
|
||||
appConfigInitProvider,
|
||||
appConfigProvider,
|
||||
} 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 = {
|
||||
providers: [
|
||||
provideBrowserGlobalErrorListeners(),
|
||||
provideRouter(routes),
|
||||
provideHttpClient(withInterceptors([authInterceptor])),
|
||||
provideRouter(routes),
|
||||
appConfigProvider,
|
||||
appConfigInitProvider,
|
||||
stationConfigInitProvider,
|
||||
],
|
||||
};
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Component, computed, effect, inject, OnInit, OnDestroy, PLATFORM_ID } from '@angular/core';
|
||||
import { isPlatformBrowser } from '@angular/common';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { RouterOutlet } from '@angular/router';
|
||||
import { NavbarComponent } from './navbar/navbar.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 { StationConfigService } from './services/station-config.service';
|
||||
import { SeoService } from './services/seo.service';
|
||||
import { loadAppConfigAsync } from './services/app-config.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
@@ -19,6 +21,7 @@ export class App implements OnInit, OnDestroy {
|
||||
private stationConfig = inject(StationConfigService);
|
||||
private seoService = inject(SeoService);
|
||||
private platformId = inject(PLATFORM_ID);
|
||||
private http = inject(HttpClient);
|
||||
|
||||
readonly hasStream = computed(() => {
|
||||
const cfg = this.stationConfig.config();
|
||||
@@ -54,6 +57,12 @@ export class App implements OnInit, OnDestroy {
|
||||
// These DOM operations only run in the browser.
|
||||
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.
|
||||
this.seoService.watchRouter();
|
||||
// Handle the initial route (NavigationEnd already fired before ngOnInit).
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { APP_INITIALIZER, InjectionToken } from '@angular/core';
|
||||
import { InjectionToken } from '@angular/core';
|
||||
import { lastValueFrom } from 'rxjs';
|
||||
|
||||
import { environment } from '../../environments/environment';
|
||||
@@ -16,15 +16,19 @@ const _appConfig: AppConfig = {
|
||||
apiBaseUrl: environment.apiBaseUrl,
|
||||
};
|
||||
|
||||
export function appConfigFactory(http: HttpClient) {
|
||||
return (): Promise<void> => {
|
||||
return lastValueFrom(http.get<AppConfig>('config.json')).then(
|
||||
(config) => { Object.assign(_appConfig, config); },
|
||||
() => {
|
||||
// Keep defaults if config.json is not available (dev mode).
|
||||
},
|
||||
);
|
||||
};
|
||||
/**
|
||||
* 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(
|
||||
(config) => { Object.assign(_appConfig, config); },
|
||||
() => {
|
||||
// Keep defaults if config.json is not available (dev mode).
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
export const appConfigProvider = {
|
||||
@@ -32,12 +36,12 @@ export const appConfigProvider = {
|
||||
useValue: _appConfig,
|
||||
};
|
||||
|
||||
export const appConfigInitProvider = {
|
||||
provide: APP_INITIALIZER,
|
||||
useFactory: appConfigFactory,
|
||||
deps: [HttpClient],
|
||||
multi: true,
|
||||
};
|
||||
/**
|
||||
* @deprecated The APP_INITIALIZER is no longer registered by default.
|
||||
* The `loadAppConfigAsync` function is called directly from `app.config.ts`
|
||||
* in a non-blocking way. This export is kept for backward compatibility only.
|
||||
*/
|
||||
export const appConfigInitProvider = null;
|
||||
|
||||
export function getAppConfig(): AppConfig {
|
||||
return _appConfig;
|
||||
|
||||
@@ -6,7 +6,10 @@ import { StationConfig } from '../interfaces/station-config';
|
||||
import { getAppConfig } from './app-config.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 = {
|
||||
id: 0,
|
||||
key: 'default',
|
||||
|
||||
@@ -6,6 +6,10 @@
|
||||
<base href="/" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<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>
|
||||
<body>
|
||||
<app-root></app-root>
|
||||
|
||||
Reference in new issue
Block a user