feat: add unified Settings dialog (#18)

This commit is contained in:
nessar committed 2026-10-04 22:01:00 +02:00
1 parent bdb1e3e230
commit 5a34160ab2
29 files changed
+420 -453

No files matched your search

@@ -33,7 +33,7 @@ export class CalendarSettingsService {
constructor() {
for (const [id, def] of this.definitions) {
const storageKey = `calendarSettings.${id}`;
this.signals.set(id, storedSignal(storageKey, def.defaultValue, this.injector));
this.signals.set(id, storedSignal(storageKey, def.defaultValue, this.injector, 0));
}
this.settingsArray = Array.from(this.definitions.entries()).map(([id, def]) => ({
+13 -12
View File
@@ -1,4 +1,5 @@
import { computed, Injectable, signal } from '@angular/core';
import { computed, inject, Injectable, Injector, signal } from '@angular/core';
import { storedSignal } from '../shared/utils/stored-signal';
export type ThemePreference = 'system' | 'light' | 'dark';
export type EffectiveTheme = 'light' | 'dark';
@@ -7,15 +8,23 @@ export type EffectiveTheme = 'light' | 'dark';
export class ThemeService {
private readonly lightModeMedia = window.matchMedia('(prefers-color-scheme: light)');
private readonly systemIsLight = signal(this.lightModeMedia.matches);
private readonly selectedPreference = signal<ThemePreference>('system');
private readonly selectedPreference = storedSignal<ThemePreference>(
'theme.preference',
'system',
inject(Injector),
0,
);
readonly themePreference = this.selectedPreference.asReadonly();
readonly effectiveTheme = computed<EffectiveTheme>(() => {
const preference = this.selectedPreference();
return preference === 'system' ? (this.systemIsLight() ? 'light' : 'dark') : preference;
});
readonly nextThemePreference = computed(() => this.nextPreference(this.selectedPreference()));
constructor() {
const preference = this.selectedPreference();
if (preference !== 'system' && preference !== 'light' && preference !== 'dark') {
this.selectedPreference.set('system');
}
this.applyEffectiveTheme();
this.lightModeMedia.addEventListener('change', (e) => {
this.systemIsLight.set(e.matches);
@@ -25,19 +34,11 @@ export class ThemeService {
});
}
toggleTheme(): void {
this.setThemePreference(this.nextThemePreference());
}
private setThemePreference(preference: ThemePreference): void {
setThemePreference(preference: ThemePreference): void {
this.selectedPreference.set(preference);
this.applyEffectiveTheme();
}
private nextPreference(preference: ThemePreference): ThemePreference {
return preference === 'system' ? 'light' : preference === 'light' ? 'dark' : 'system';
}
private applyEffectiveTheme(): void {
const isLight = this.effectiveTheme() === 'light';
document.body.classList.toggle('light-mode', isLight);
+8
View File
@@ -50,6 +50,13 @@ export interface AppTranslations {
showWeekNumbers: string;
close: string;
selectLanguage: string;
selectTheme: string;
themeOptions: {
system: string;
light: string;
dark: string;
};
systemAppearance: string;
};
rules: {
title: string;
@@ -307,6 +314,7 @@ export class TranslationService {
'app.language',
this.getBrowserLanguage(),
this.injector,
0,
);
public readonly tr = computed<AppTranslations>(() => {