improve: performance
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
<div class="app-content" [attr.aria-hidden]="isLanguageSelectorOpen() ? 'true' : null" [attr.inert]="isLanguageSelectorOpen() ? '' : null">
|
||||
<router-outlet (activate)="scrollToTopAfterOutletActivation()"></router-outlet>
|
||||
<router-outlet></router-outlet>
|
||||
</div>
|
||||
|
||||
<button
|
||||
|
||||
+10
-55
@@ -1,22 +1,18 @@
|
||||
import { DOCUMENT, isPlatformBrowser } from '@angular/common';
|
||||
import { DOCUMENT } from '@angular/common';
|
||||
import {
|
||||
ChangeDetectionStrategy,
|
||||
Component,
|
||||
DestroyRef,
|
||||
computed,
|
||||
effect,
|
||||
inject,
|
||||
PLATFORM_ID,
|
||||
signal,
|
||||
} from '@angular/core';
|
||||
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
||||
import type { OnDestroy, OnInit } from '@angular/core';
|
||||
import { NavigationEnd, Router, RouterOutlet } from '@angular/router';
|
||||
import { filter } from 'rxjs';
|
||||
import { RouterOutlet } from '@angular/router';
|
||||
|
||||
import type { LanguageCode } from '../languages/language.types';
|
||||
import type { LanguageOption } from './app.types';
|
||||
import { LanguageService } from './services/language.service';
|
||||
import { ShellLanguageService } from './services/shell-language.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
@@ -27,14 +23,11 @@ import { LanguageService } from './services/language.service';
|
||||
})
|
||||
export class AppComponent implements OnInit, OnDestroy {
|
||||
private readonly document = inject(DOCUMENT);
|
||||
private readonly destroyRef = inject(DestroyRef);
|
||||
private readonly languageService = inject(LanguageService);
|
||||
private readonly platformId = inject(PLATFORM_ID);
|
||||
private readonly router = inject(Router);
|
||||
private readonly shellLanguageService = inject(ShellLanguageService);
|
||||
|
||||
protected readonly content = this.languageService.shellContent;
|
||||
protected readonly currentLanguageCode = this.languageService.languageCode;
|
||||
protected readonly isLanguageConfirmed = this.languageService.isLanguageConfirmed;
|
||||
protected readonly content = this.shellLanguageService.content;
|
||||
protected readonly currentLanguageCode = this.shellLanguageService.languageCode;
|
||||
protected readonly isLanguageConfirmed = this.shellLanguageService.isLanguageConfirmed;
|
||||
protected readonly isLanguageSelectorOpen = signal(false);
|
||||
protected readonly selectedLanguage = signal<LanguageCode>('en');
|
||||
protected readonly languageOptions: readonly LanguageOption[] = [
|
||||
@@ -42,7 +35,7 @@ export class AppComponent implements OnInit, OnDestroy {
|
||||
{ code: 'de', accent: 'DE' },
|
||||
];
|
||||
protected readonly dialogContent = computed(() =>
|
||||
this.languageService.getShellPack(this.selectedLanguage()),
|
||||
this.shellLanguageService.getPack(this.selectedLanguage()),
|
||||
);
|
||||
protected readonly languageSwitcherLabel = computed(() =>
|
||||
this.getLanguageLabel(this.currentLanguageCode(), this.content()),
|
||||
@@ -54,17 +47,10 @@ export class AppComponent implements OnInit, OnDestroy {
|
||||
this.document.body.style.overflow = overflowValue;
|
||||
this.document.documentElement.style.overflow = overflowValue;
|
||||
});
|
||||
|
||||
this.router.events
|
||||
.pipe(
|
||||
filter((event): event is NavigationEnd => event instanceof NavigationEnd),
|
||||
takeUntilDestroyed(this.destroyRef),
|
||||
)
|
||||
.subscribe((event: NavigationEnd): void => this.scrollToTopAfterRouteChange(event));
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
const initialLanguage = this.languageService.initializeFromStorage();
|
||||
const initialLanguage = this.shellLanguageService.initializeFromStorage();
|
||||
this.selectedLanguage.set(initialLanguage);
|
||||
}
|
||||
|
||||
@@ -74,7 +60,7 @@ export class AppComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
protected confirmLanguage(): void {
|
||||
this.languageService.confirmLanguage(this.selectedLanguage());
|
||||
this.shellLanguageService.confirmLanguage(this.selectedLanguage());
|
||||
this.isLanguageSelectorOpen.set(false);
|
||||
}
|
||||
|
||||
@@ -98,37 +84,6 @@ export class AppComponent implements OnInit, OnDestroy {
|
||||
return language === 'de' ? content.app.languageGerman : content.app.languageEnglish;
|
||||
}
|
||||
|
||||
protected scrollToTopAfterOutletActivation(): void {
|
||||
if (!isPlatformBrowser(this.platformId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.scrollDocumentToTop();
|
||||
this.document.defaultView?.requestAnimationFrame((): void => this.scrollDocumentToTop());
|
||||
this.document.defaultView?.setTimeout((): void => this.scrollDocumentToTop(), 0);
|
||||
this.document.defaultView?.setTimeout((): void => this.scrollDocumentToTop(), 100);
|
||||
}
|
||||
|
||||
private scrollToTopAfterRouteChange(event: NavigationEnd): void {
|
||||
if (!isPlatformBrowser(this.platformId) || event.urlAfterRedirects.includes('#')) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.scrollToTopAfterOutletActivation();
|
||||
}
|
||||
|
||||
private scrollDocumentToTop(): void {
|
||||
const scrollingElement = this.document.scrollingElement ?? this.document.documentElement;
|
||||
|
||||
scrollingElement.scrollTop = 0;
|
||||
scrollingElement.scrollLeft = 0;
|
||||
this.document.documentElement.scrollTop = 0;
|
||||
this.document.documentElement.scrollLeft = 0;
|
||||
this.document.body.scrollTop = 0;
|
||||
this.document.body.scrollLeft = 0;
|
||||
this.document.defaultView?.scrollTo({ top: 0, left: 0, behavior: 'auto' });
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.document.body.style.overflow = '';
|
||||
this.document.documentElement.style.overflow = '';
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { ChangeDetectionStrategy, Component, inject } from '@angular/core';
|
||||
import { RouterLink } from '@angular/router';
|
||||
import { global } from '../../global';
|
||||
import { LanguageService } from '../services/language.service';
|
||||
import { ShellLanguageService } from '../services/shell-language.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-footer',
|
||||
@@ -11,9 +11,9 @@ import { LanguageService } from '../services/language.service';
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class FooterComponent {
|
||||
private readonly languageService = inject(LanguageService);
|
||||
private readonly shellLanguageService = inject(ShellLanguageService);
|
||||
|
||||
protected readonly content = this.languageService.shellContent;
|
||||
protected readonly content = this.shellLanguageService.content;
|
||||
protected readonly global = global;
|
||||
protected readonly currentYear = new Date().getFullYear();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
import { Injectable, signal } from '@angular/core';
|
||||
|
||||
import type { LanguageCode } from '../../languages/language.types';
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class LanguagePreferenceService {
|
||||
private static readonly storageKey = 'portfolio-language';
|
||||
private readonly currentLanguageCode = signal<LanguageCode>('en');
|
||||
private readonly languageConfirmed = signal(true);
|
||||
|
||||
readonly languageCode = this.currentLanguageCode.asReadonly();
|
||||
readonly isLanguageConfirmed = this.languageConfirmed.asReadonly();
|
||||
|
||||
initializeFromStorage(): LanguageCode {
|
||||
if (typeof window === 'undefined') {
|
||||
return this.currentLanguageCode();
|
||||
}
|
||||
|
||||
const storedLanguage = window.localStorage.getItem(LanguagePreferenceService.storageKey);
|
||||
if (storedLanguage === 'de' || storedLanguage === 'en') {
|
||||
this.currentLanguageCode.set(storedLanguage);
|
||||
}
|
||||
|
||||
return this.currentLanguageCode();
|
||||
}
|
||||
|
||||
confirmLanguage(code: LanguageCode): void {
|
||||
this.currentLanguageCode.set(code);
|
||||
this.languageConfirmed.set(true);
|
||||
|
||||
if (typeof window !== 'undefined') {
|
||||
window.localStorage.setItem(LanguagePreferenceService.storageKey, code);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,54 +1,27 @@
|
||||
import { Injectable, computed, signal } from '@angular/core';
|
||||
import { Injectable, computed, inject } from '@angular/core';
|
||||
|
||||
import { deLanguage } from '../../languages/de';
|
||||
import { enLanguage } from '../../languages/en';
|
||||
import type { LanguageCode, LanguagePack, LanguageShellPack } from '../../languages/language.types';
|
||||
import type { LanguageCode, LanguagePack } from '../../languages/language.types';
|
||||
import { LanguagePreferenceService } from './language-preference.service';
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class LanguageService {
|
||||
private static readonly storageKey = 'portfolio-language';
|
||||
private readonly currentLanguageCode = signal<LanguageCode>('en');
|
||||
private readonly languageConfirmed = signal(true);
|
||||
private readonly languagePreferenceService = inject(LanguagePreferenceService);
|
||||
|
||||
readonly languageCode = this.currentLanguageCode.asReadonly();
|
||||
readonly isLanguageConfirmed = this.languageConfirmed.asReadonly();
|
||||
readonly content = computed<LanguagePack>(() => this.getPack(this.currentLanguageCode()));
|
||||
readonly shellContent = computed<LanguageShellPack>(() => this.toShellPack(this.content()));
|
||||
readonly languageCode = this.languagePreferenceService.languageCode;
|
||||
readonly isLanguageConfirmed = this.languagePreferenceService.isLanguageConfirmed;
|
||||
readonly content = computed<LanguagePack>(() => this.getPack(this.languageCode()));
|
||||
|
||||
initializeFromStorage(): LanguageCode {
|
||||
if (typeof window === 'undefined') {
|
||||
return this.currentLanguageCode();
|
||||
}
|
||||
|
||||
const storedLanguage = window.localStorage.getItem(LanguageService.storageKey);
|
||||
if (storedLanguage === 'de' || storedLanguage === 'en') {
|
||||
this.currentLanguageCode.set(storedLanguage);
|
||||
}
|
||||
|
||||
return this.currentLanguageCode();
|
||||
return this.languagePreferenceService.initializeFromStorage();
|
||||
}
|
||||
|
||||
confirmLanguage(code: LanguageCode): void {
|
||||
this.currentLanguageCode.set(code);
|
||||
this.languageConfirmed.set(true);
|
||||
|
||||
if (typeof window !== 'undefined') {
|
||||
window.localStorage.setItem(LanguageService.storageKey, code);
|
||||
}
|
||||
}
|
||||
|
||||
getShellPack(code: LanguageCode): LanguageShellPack {
|
||||
return this.toShellPack(this.getPack(code));
|
||||
this.languagePreferenceService.confirmLanguage(code);
|
||||
}
|
||||
|
||||
getPack(code: LanguageCode): LanguagePack {
|
||||
return code === 'de' ? deLanguage : enLanguage;
|
||||
}
|
||||
|
||||
private toShellPack(pack: LanguagePack): LanguageShellPack {
|
||||
return {
|
||||
app: pack.app,
|
||||
footer: pack.footer,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
import { Injectable, computed, inject } from '@angular/core';
|
||||
|
||||
import { deShellLanguage, enShellLanguage } from '../../languages/shell';
|
||||
import type { LanguageCode, LanguageShellPack } from '../../languages/language.types';
|
||||
import { LanguagePreferenceService } from './language-preference.service';
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class ShellLanguageService {
|
||||
private readonly languagePreferenceService = inject(LanguagePreferenceService);
|
||||
|
||||
readonly languageCode = this.languagePreferenceService.languageCode;
|
||||
readonly isLanguageConfirmed = this.languagePreferenceService.isLanguageConfirmed;
|
||||
readonly content = computed<LanguageShellPack>(() => this.getPack(this.languageCode()));
|
||||
|
||||
initializeFromStorage(): LanguageCode {
|
||||
return this.languagePreferenceService.initializeFromStorage();
|
||||
}
|
||||
|
||||
confirmLanguage(code: LanguageCode): void {
|
||||
this.languagePreferenceService.confirmLanguage(code);
|
||||
}
|
||||
|
||||
getPack(code: LanguageCode): LanguageShellPack {
|
||||
return code === 'de' ? deShellLanguage : enShellLanguage;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
import type { LanguageShellPack } from './language.types';
|
||||
|
||||
export const enShellLanguage: LanguageShellPack = {
|
||||
app: {
|
||||
selectorTitle: 'Select language',
|
||||
changeLanguage: 'Language',
|
||||
changeLanguageAriaLabel: 'Change language',
|
||||
closeSelector: 'Close language selection',
|
||||
languageEnglish: 'English',
|
||||
languageGerman: 'German',
|
||||
},
|
||||
footer: {
|
||||
noscriptMessage: 'Please enable JavaScript for the full experience.',
|
||||
imprintLink: 'Imprint',
|
||||
},
|
||||
};
|
||||
|
||||
export const deShellLanguage: LanguageShellPack = {
|
||||
app: {
|
||||
selectorTitle: 'Sprache auswählen',
|
||||
changeLanguage: 'Sprache',
|
||||
changeLanguageAriaLabel: 'Sprache ändern',
|
||||
closeSelector: 'Sprachauswahl schließen',
|
||||
languageEnglish: 'Englisch',
|
||||
languageGerman: 'Deutsch',
|
||||
},
|
||||
footer: {
|
||||
noscriptMessage: 'Bitte aktiviere JavaScript um alles zu sehen.',
|
||||
imprintLink: 'Impressum',
|
||||
},
|
||||
};
|
||||
Reference in New Issue
Block a user