arch(sprint1): centralize startup in platform runtime

This commit is contained in:
sdarbinyan
2026-07-03 02:05:25 +04:00
parent d6a1d5e3f5
commit 8132c1a535
5 changed files with 75 additions and 14 deletions

View File

@@ -13,6 +13,7 @@ import { environment } from '../environments/environment';
import { SwUpdate } from '@angular/service-worker';
import { TranslatePipe } from './i18n/translate.pipe';
import { TranslateService } from './i18n/translate.service';
import { PlatformRuntimeService } from './core/runtime/platform-runtime.service';
@Component({
selector: 'app-root',
@@ -33,8 +34,10 @@ export class App implements OnInit {
private appRef = inject(ApplicationRef);
private router = inject(Router);
private i18n = inject(TranslateService);
private platformRuntime = inject(PlatformRuntimeService);
ngOnInit(): void {
this.platformRuntime.initialize();
this.titleService.setTitle(`${environment.brandFullName} - ${this.i18n.t('app.pageTitle')}`);
this.checkServerHealth();
this.setupAutoUpdates();

View File

@@ -0,0 +1,26 @@
import { Injectable, signal } from '@angular/core';
import { FeatureFlagsConfig, LocalizationConfig, PermissionsConfig } from '../../shared/models/config';
@Injectable({ providedIn: 'root' })
export class PlatformRuntimeStateService {
private readonly initializedState = signal(false);
private readonly localizationState = signal<LocalizationConfig | null>(null);
private readonly featureFlagsState = signal<FeatureFlagsConfig | null>(null);
private readonly permissionsState = signal<PermissionsConfig | null>(null);
readonly initialized = this.initializedState.asReadonly();
readonly localization = this.localizationState.asReadonly();
readonly featureFlags = this.featureFlagsState.asReadonly();
readonly permissions = this.permissionsState.asReadonly();
markInitialized(next: {
localization: LocalizationConfig;
featureFlags: FeatureFlagsConfig;
permissions: PermissionsConfig;
}): void {
this.localizationState.set(next.localization);
this.featureFlagsState.set(next.featureFlags);
this.permissionsState.set(next.permissions);
this.initializedState.set(true);
}
}

View File

@@ -0,0 +1,45 @@
import { Injectable } from '@angular/core';
import { take } from 'rxjs/operators';
import { ConfigService } from '../config/config.service';
import { BrandingEngineService } from '../../theme/runtime/branding-engine.service';
import { ThemeEngineService } from '../../theme/runtime/theme-engine.service';
import { WidgetRegistryBootstrapService } from '../../widgets/registry/widget-registry.bootstrap.service';
import { PlatformRuntimeStateService } from './platform-runtime-state.service';
@Injectable({ providedIn: 'root' })
export class PlatformRuntimeService {
private initializing = false;
constructor(
private readonly configService: ConfigService,
private readonly widgetRegistryBootstrap: WidgetRegistryBootstrapService,
private readonly themeEngine: ThemeEngineService,
private readonly brandingEngine: BrandingEngineService,
private readonly runtimeState: PlatformRuntimeStateService
) {}
initialize(): void {
if (this.initializing || this.runtimeState.initialized()) {
return;
}
this.initializing = true;
this.configService.loadBootstrap().pipe(take(1)).subscribe({
next: (bootstrap) => {
this.widgetRegistryBootstrap.registerDefaults();
this.themeEngine.applyTheme(bootstrap.theme);
this.brandingEngine.applyBranding(bootstrap.branding);
this.runtimeState.markInitialized({
localization: bootstrap.localization,
featureFlags: bootstrap.featureFlags,
permissions: bootstrap.permissions
});
this.initializing = false;
},
error: () => {
this.initializing = false;
}
});
}
}

View File

@@ -1,28 +1,16 @@
import { Injectable } from '@angular/core';
import { Observable, map } from 'rxjs';
import { BrandingEngineService } from '../../theme/runtime/branding-engine.service';
import { ThemeEngineService } from '../../theme/runtime/theme-engine.service';
import { PageRendererService } from '../../dynamic-renderer/page-renderer/page-renderer.service';
import { PageRenderModel } from '../../dynamic-renderer/page-renderer/page-renderer.model';
import { WidgetRegistryBootstrapService } from '../../widgets/registry/widget-registry.bootstrap.service';
import { PageResolverService } from '../../dynamic-renderer/page-resolver/page-resolver.service';
@Injectable({ providedIn: 'root' })
export class WebsiteRuntimeFacade {
constructor(
private readonly pageResolver: PageResolverService,
private readonly pageRenderer: PageRendererService,
private readonly registryBootstrap: WidgetRegistryBootstrapService,
private readonly themeEngine: ThemeEngineService,
private readonly brandingEngine: BrandingEngineService
private readonly pageRenderer: PageRendererService
) {}
initializeRuntime(): void {
this.registryBootstrap.registerDefaults();
this.themeEngine.initialize();
this.brandingEngine.initialize();
}
getPageRenderModelForUrl(url: string): Observable<PageRenderModel | null> {
return this.pageResolver.resolveByUrl(url).pipe(
map(page => {

View File

@@ -30,7 +30,6 @@ export class PlatformHomeComponent {
private readonly router = inject(Router);
constructor(private readonly websiteRuntime: WebsiteRuntimeFacade) {
this.websiteRuntime.initializeRuntime();
this.websiteRuntime.getPageRenderModelForUrl(this.router.url).subscribe({
next: (model) => {
this.model.set(model);