From 8132c1a5351d5d1026590c98975d2a4988579825 Mon Sep 17 00:00:00 2001 From: sdarbinyan Date: Fri, 3 Jul 2026 02:05:25 +0400 Subject: [PATCH] arch(sprint1): centralize startup in platform runtime --- src/app/app.ts | 3 ++ .../runtime/platform-runtime-state.service.ts | 26 +++++++++++ .../core/runtime/platform-runtime.service.ts | 45 +++++++++++++++++++ .../facades/website/website-runtime.facade.ts | 14 +----- .../pages/public/platform-home.component.ts | 1 - 5 files changed, 75 insertions(+), 14 deletions(-) create mode 100644 src/app/core/runtime/platform-runtime-state.service.ts create mode 100644 src/app/core/runtime/platform-runtime.service.ts diff --git a/src/app/app.ts b/src/app/app.ts index 9a1bf06..d48587e 100644 --- a/src/app/app.ts +++ b/src/app/app.ts @@ -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(); diff --git a/src/app/core/runtime/platform-runtime-state.service.ts b/src/app/core/runtime/platform-runtime-state.service.ts new file mode 100644 index 0000000..1387da3 --- /dev/null +++ b/src/app/core/runtime/platform-runtime-state.service.ts @@ -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(null); + private readonly featureFlagsState = signal(null); + private readonly permissionsState = signal(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); + } +} diff --git a/src/app/core/runtime/platform-runtime.service.ts b/src/app/core/runtime/platform-runtime.service.ts new file mode 100644 index 0000000..cda9c30 --- /dev/null +++ b/src/app/core/runtime/platform-runtime.service.ts @@ -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; + } + }); + } +} diff --git a/src/app/facades/website/website-runtime.facade.ts b/src/app/facades/website/website-runtime.facade.ts index 893300a..471a476 100644 --- a/src/app/facades/website/website-runtime.facade.ts +++ b/src/app/facades/website/website-runtime.facade.ts @@ -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 { return this.pageResolver.resolveByUrl(url).pipe( map(page => { diff --git a/src/app/pages/public/platform-home.component.ts b/src/app/pages/public/platform-home.component.ts index b11a1a3..ddc729b 100644 --- a/src/app/pages/public/platform-home.component.ts +++ b/src/app/pages/public/platform-home.component.ts @@ -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);