import { Component, OnInit, signal, ApplicationRef, inject, DestroyRef } from '@angular/core'; import { Router, RouterOutlet, NavigationEnd } from '@angular/router'; import { Title } from '@angular/platform-browser'; import { HeaderComponent } from './components/header/header.component'; import { FooterComponent } from './components/footer/footer.component'; import { BackButtonComponent } from './components/back-button/back-button.component'; import { interval, concat } from 'rxjs'; import { filter, first } from 'rxjs/operators'; import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; 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'; import { UiRuntimeFacade } from './facades/runtime/ui-runtime.facade'; import { ApiHealthService } from './services/api-health.service'; import { FloatingNotificationsComponent } from './features/website/user-experience/components/floating-notifications/floating-notifications.component'; import { AdminAuthService } from './core/admin-auth/admin-auth.service'; import { AuthService } from './services/auth.service'; import { TelegramLoginComponent } from './components/telegram-login/telegram-login.component'; @Component({ selector: 'app-root', imports: [RouterOutlet, HeaderComponent, FooterComponent, BackButtonComponent, TranslatePipe, FloatingNotificationsComponent, TelegramLoginComponent], templateUrl: './app.html', styleUrl: './app.scss' }) export class App implements OnInit { protected title = ''; isHomePage = signal(true); isAdminRoute = signal(false); checkingServer = signal(true); serverAvailable = signal(false); private destroyRef = inject(DestroyRef); private titleService = inject(Title); private swUpdate = inject(SwUpdate); private appRef = inject(ApplicationRef); private router = inject(Router); private i18n = inject(TranslateService); private platformRuntime = inject(PlatformRuntimeService); private uiRuntime = inject(UiRuntimeFacade); private apiHealth = inject(ApiHealthService); private authService = inject(AuthService); private adminAuthService = inject(AdminAuthService); ngOnInit(): void { this.platformRuntime.initialize(); this.title = this.uiRuntime.marketplaceName(); this.titleService.setTitle(`${this.uiRuntime.marketplaceDisplayName()} - ${this.i18n.t('app.pageTitle')}`); this.checkServerHealth(); this.setupAutoUpdates(); this.openLoginDialogsFromTestModeQueryParams(); // Track route changes to show/hide back button this.router.events .pipe( filter(event => event instanceof NavigationEnd), takeUntilDestroyed(this.destroyRef) ) .subscribe((event) => { const navEnd = event as NavigationEnd; const url = navEnd.urlAfterRedirects || navEnd.url; // Home pages: /ru, /en, /hy (with or without trailing slash) this.isHomePage.set(/^\/[a-z]{2}\/?$/.test(url) || url === '/' || url === ''); // Admin backoffice and the Marketplace Builder (/edit) own their own // shells (AdminLayoutComponent / ProjectEditorPageComponent's sidebar) - // the storefront header/back-button/footer never render on either. this.isAdminRoute.set(/^\/[a-z]{2}\/(backoffice|edit)(\/|$|\?)/.test(url)); }); } private checkServerHealth(): void { this.checkingServer.set(true); this.apiHealth.ping() .pipe(takeUntilDestroyed(this.destroyRef)) .subscribe({ next: () => { this.serverAvailable.set(true); this.checkingServer.set(false); }, error: () => { this.serverAvailable.set(false); this.checkingServer.set(false); } }); } retryConnection(): void { this.checkServerHealth(); } /** * ?login=true / ?adminLogin=true open the respective login dialog for * manual testing. ?devBypassAdmin=true skips the QR flow entirely and * activates a fake local admin session - dev builds only, no effect (and * no-ops server-side too, see AdminAuthService.devBypassLogin) in * production. No effect when the params are absent. */ private openLoginDialogsFromTestModeQueryParams(): void { if (typeof window === 'undefined') { return; } const params = new URLSearchParams(window.location.search); if (params.get('login') === 'true') { this.authService.requestLogin(); } if (params.get('adminLogin') === 'true') { this.adminAuthService.requestLogin(); } if (params.get('devBypassAdmin') === 'true') { this.adminAuthService.devBypassLogin(); } } private setupAutoUpdates(): void { if (!this.swUpdate.isEnabled) { return; } const appIsStable$ = this.appRef.isStable.pipe(first(isStable => isStable === true)); const every6Hours$ = interval(6 * 60 * 60 * 1000); const checkInterval$ = concat(appIsStable$, every6Hours$); checkInterval$ .pipe(takeUntilDestroyed(this.destroyRef)) .subscribe(async () => { try { await this.swUpdate.checkForUpdate(); } catch (err) { console.error('Update check failed:', err); } }); } }