Files
marketplaces/src/app/pages/static-page/static-page.component.ts
sdarbinyan 6231128288
Some checks failed
Architecture Governance / architecture (push) Has been cancelled
fix: static-page loadByKey/loadByPath had no error handler, infinite spinner on failure
resolveByKey/resolveByRoute subscribed with only a next callback -
a resolver failure left loading=true forever with no error branch to
recover from. Added an error signal, error subscribe handler, and a
distinct error state UI (separate from the existing 404 not-found
state) with a way back home.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-13 08:56:25 +04:00

125 lines
4.4 KiB
TypeScript

import { ChangeDetectionStrategy, Component, DestroyRef, SecurityContext, effect, inject, signal } from '@angular/core';
import { CommonModule } from '@angular/common';
import { ActivatedRoute, RouterLink } from '@angular/router';
import { DomSanitizer, Meta, SafeHtml, Title } from '@angular/platform-browser';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import { StaticPageResolverService } from '../../core/config/static-page-resolver.service';
import { LanguageService } from '../../services/language.service';
import { TranslatePipe } from '../../i18n/translate.pipe';
import { EmptyStateComponent } from '../../shared/ui/empty-state/empty-state.component';
import { SkeletonComponent } from '../../shared/ui/skeleton/skeleton.component';
import { ButtonComponent } from '../../shared/ui/button/button.component';
@Component({
selector: 'app-static-page',
standalone: true,
imports: [CommonModule, RouterLink, TranslatePipe, EmptyStateComponent, SkeletonComponent, ButtonComponent],
templateUrl: './static-page.component.html',
styleUrls: ['./static-page.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class StaticPageComponent {
private readonly route = inject(ActivatedRoute);
private readonly destroyRef = inject(DestroyRef);
private readonly sanitizer = inject(DomSanitizer);
private readonly staticPageResolver = inject(StaticPageResolverService);
private readonly languageService = inject(LanguageService);
private readonly titleService = inject(Title);
private readonly meta = inject(Meta);
readonly loading = signal(true);
readonly notFound = signal(false);
readonly error = signal(false);
readonly title = signal('');
readonly homeRoute = signal('');
readonly dir = signal<'ltr' | 'rtl'>('ltr');
readonly safeHtml = signal<SafeHtml>(this.sanitizer.bypassSecurityTrustHtml(''));
private lastKey: string | null = null;
private lastPath: string | null = null;
constructor() {
effect(() => {
const lang = this.languageService.currentLanguage();
this.homeRoute.set(`/${lang}`);
this.dir.set(['ar', 'fa', 'he', 'ur'].includes(lang) ? 'rtl' : 'ltr');
if (this.lastKey) {
this.loadByKey(this.lastKey);
} else if (this.lastPath) {
this.loadByPath(this.lastPath);
}
});
this.route.paramMap.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(params => {
const keyFromParam = params.get('key');
const staticPath = params.get('staticPath');
if (keyFromParam) {
this.lastKey = keyFromParam;
this.lastPath = null;
this.loadByKey(keyFromParam);
return;
}
if (staticPath) {
this.lastKey = null;
this.lastPath = `/${staticPath}`;
this.loadByPath(`/${staticPath}`);
return;
}
this.notFound.set(true);
this.loading.set(false);
});
}
private loadByKey(key: string): void {
this.loading.set(true);
this.notFound.set(false);
this.error.set(false);
this.staticPageResolver.resolveByKey(key, this.languageService.currentLanguage()).subscribe({
next: page => this.applyPage(page?.title ?? '', page?.html ?? '', !page),
error: () => this.applyError()
});
}
private loadByPath(path: string): void {
this.loading.set(true);
this.notFound.set(false);
this.error.set(false);
this.staticPageResolver.resolveByRoute(path, this.languageService.currentLanguage()).subscribe({
next: page => this.applyPage(page?.title ?? '', page?.html ?? '', !page),
error: () => this.applyError()
});
}
private applyError(): void {
this.title.set('');
this.safeHtml.set(this.sanitizer.bypassSecurityTrustHtml(''));
this.error.set(true);
this.loading.set(false);
}
private applyPage(title: string, html: string, notFound: boolean): void {
if (notFound) {
this.title.set('');
this.safeHtml.set(this.sanitizer.bypassSecurityTrustHtml(''));
this.notFound.set(true);
this.loading.set(false);
this.titleService.setTitle('404');
return;
}
const sanitized = this.sanitizer.sanitize(SecurityContext.HTML, html) ?? '';
this.title.set(title);
this.safeHtml.set(this.sanitizer.bypassSecurityTrustHtml(sanitized));
this.notFound.set(false);
this.loading.set(false);
this.titleService.setTitle(title);
this.meta.updateTag({ name: 'description', content: title });
}
}