frontend: ensure ThemeService is initialized before app starts and apply the saved theme on initial load to prevent flickering

This commit is contained in:
juancarmore 2025-07-24 21:28:46 +02:00
parent 160cb3927d
commit aceedaddfa
5 changed files with 25 additions and 12 deletions

View File

@ -7,7 +7,7 @@ export type Theme = 'light' | 'dark';
providedIn: 'root'
})
export class ThemeService {
private readonly THEME_KEY = 'ov-theme-preference';
private readonly THEME_KEY = 'ov-meet-theme';
private readonly _currentTheme = signal<Theme>('light');
// Computed signals for reactivity
@ -15,9 +15,7 @@ export class ThemeService {
public readonly isDark = computed(() => this._currentTheme() === 'dark');
public readonly isLight = computed(() => this._currentTheme() === 'light');
constructor(@Inject(DOCUMENT) private document: Document) {
this.initializeTheme();
}
constructor(@Inject(DOCUMENT) private document: Document) {}
/**
* Initializes the theme based on:
@ -25,7 +23,7 @@ export class ThemeService {
* 2. System preference (prefers-color-scheme)
* 3. Light theme as default
*/
private initializeTheme(): void {
initializeTheme(): void {
const savedTheme = this.getSavedTheme();
const systemPreference = this.getSystemPreference();
const initialTheme = savedTheme || systemPreference || 'light';

View File

@ -1,6 +1,6 @@
import { Component, OnInit } from '@angular/core';
import { RouterOutlet } from '@angular/router';
import { AppDataService, ThemeService } from '@lib/services';
import { AppDataService } from '@lib/services';
import packageInfo from '../../package.json';
@Component({
@ -11,10 +11,7 @@ import packageInfo from '../../package.json';
imports: [RouterOutlet]
})
export class AppComponent implements OnInit {
constructor(
private appDataService: AppDataService,
private themeService: ThemeService
) {}
constructor(private appDataService: AppDataService) {}
ngOnInit() {
this.appDataService.setVersion(packageInfo.version);

View File

@ -1,11 +1,12 @@
import { STEPPER_GLOBAL_OPTIONS } from '@angular/cdk/stepper';
import { provideHttpClient, withInterceptors } from '@angular/common/http';
import { ApplicationConfig, importProvidersFrom, provideZoneChangeDetection } from '@angular/core';
import { APP_INITIALIZER, ApplicationConfig, importProvidersFrom, provideZoneChangeDetection } from '@angular/core';
import { provideAnimationsAsync } from '@angular/platform-browser/animations/async';
import { provideRouter } from '@angular/router';
import { routes } from '@app/app.routes';
import { environment } from '@environment/environment';
import { httpInterceptor } from '@lib/interceptors/index';
import { ThemeService } from '@lib/services/theme.service';
import { OpenViduComponentsConfig, OpenViduComponentsModule } from 'openvidu-components-angular';
const ovComponentsconfig: OpenViduComponentsConfig = {
@ -14,6 +15,13 @@ const ovComponentsconfig: OpenViduComponentsConfig = {
export const appConfig: ApplicationConfig = {
providers: [
{
provide: APP_INITIALIZER,
// Ensure the theme is initialized before the app starts
useFactory: (themeService: ThemeService) => () => themeService.initializeTheme(),
deps: [ThemeService],
multi: true
},
importProvidersFrom(OpenViduComponentsModule.forRoot(ovComponentsconfig)),
provideZoneChangeDetection({ eventCoalescing: true }),
provideRouter(routes),

View File

@ -10,6 +10,16 @@
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500&display=swap" rel="stylesheet" />
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet" />
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined" rel="stylesheet" />
<script>
// Apply the saved theme on initial load to prevent flickering
(function () {
const savedTheme = localStorage.getItem('ov-meet-theme') || 'light';
if (savedTheme === 'dark') {
document.documentElement.setAttribute('data-theme', 'dark');
}
})();
</script>
</head>
<body class="mat-typography" id="body">
<app-root></app-root>

View File

@ -1,5 +1,5 @@
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from '@app/app.component';
import { appConfig } from '@app/app.config';
import { bootstrapApplication } from '@angular/platform-browser';
bootstrapApplication(AppComponent, appConfig).catch((err) => console.error(err));