App web familiar de rutinas visuales para niños con TDAH: muestra cada día el material del cole y las rutinas de tarde, con gamificación por monedas y tienda de recompensas. Multi-niño y bilingüe ES/CA. Uso doméstico/homelab. Backend (Spring Boot 3.5 / Java 21 / Gradle): - Dominio por capas, PostgreSQL + Liquibase, datos semilla. - API REST con DTOs: /today, toggle con monedas y bonos de bloque/día, monedero, tienda/canje, ajustes y CRUD del panel de padres. - Seguridad ligera por PIN (BCrypt + sesion en memoria), sin Keycloak. - Tests JUnit: generacion del dia, monedas/bonos con reversion, canje, seguridad. Frontend (Angular 19, standalone + signals): - Perfiles, Home (Tablero y Foco), Tienda y panel de padres (5 pestañas). - Tipografia OpenDyslexic conmutable (accesibilidad), i18n ES/CA, TTS y sonido. - Tokens de diseño fieles al handoff (paleta, animaciones, monedas voladoras). Empaquetado: - Docker multi-stage + docker-compose (PostgreSQL + backend + Nginx). - Decisiones de arquitectura documentadas en docs/adr.
81 lines
2.7 KiB
TypeScript
81 lines
2.7 KiB
TypeScript
import { DOCUMENT } from '@angular/common';
|
|
import { Injectable, inject, signal } from '@angular/core';
|
|
|
|
/**
|
|
* Gestiona la preferencia de tipografía OpenDyslexic.
|
|
*
|
|
* Es la "costura" de accesibilidad: aplica (o quita) el atributo
|
|
* `data-dyslexia-font` en el elemento <html>, que es el interruptor que el
|
|
* fichero de tokens (_theme.scss) usa para alternar entre OpenDyslexic y las
|
|
* tipografías de marca del handoff (Fredoka/Nunito).
|
|
*
|
|
* Decisión de producto (Fase 1): OpenDyslexic activada POR DEFECTO y aplicada a
|
|
* TODO el texto. Es una preferencia por niño; de momento se persiste en
|
|
* localStorage. En la Fase 5 esta preferencia pasará a leerse/escribirse contra
|
|
* el backend (ajustes por niño), sustituyendo el almacenamiento local.
|
|
*/
|
|
@Injectable({ providedIn: 'root' })
|
|
export class FontPreferenceService {
|
|
/** Clave de persistencia temporal hasta el cableado con el backend. */
|
|
private static readonly STORAGE_KEY = 'recordalexia.dyslexiaFont';
|
|
|
|
private readonly document = inject(DOCUMENT);
|
|
|
|
/** Estado reactivo: ¿está activada OpenDyslexic? Por defecto, sí. */
|
|
private readonly enabledSignal = signal<boolean>(this.readInitialState());
|
|
|
|
/** Señal de solo lectura para que la consuma la UI. */
|
|
readonly enabled = this.enabledSignal.asReadonly();
|
|
|
|
constructor() {
|
|
// Sincroniza el DOM con el estado inicial al arrancar la app.
|
|
this.applyToDom(this.enabledSignal());
|
|
}
|
|
|
|
/** Activa o desactiva OpenDyslexic y propaga el cambio al DOM y a la persistencia. */
|
|
setEnabled(enabled: boolean): void {
|
|
this.enabledSignal.set(enabled);
|
|
this.applyToDom(enabled);
|
|
this.persist(enabled);
|
|
}
|
|
|
|
/** Alterna el estado actual. */
|
|
toggle(): void {
|
|
this.setEnabled(!this.enabledSignal());
|
|
}
|
|
|
|
/** Lee el estado inicial de localStorage; si no hay nada guardado, ACTIVA por defecto. */
|
|
private readInitialState(): boolean {
|
|
const stored = this.safeGetItem(FontPreferenceService.STORAGE_KEY);
|
|
return stored === null ? true : stored === 'true';
|
|
}
|
|
|
|
/** Refleja la preferencia en <html data-dyslexia-font="on|off">. */
|
|
private applyToDom(enabled: boolean): void {
|
|
this.document.documentElement.setAttribute(
|
|
'data-dyslexia-font',
|
|
enabled ? 'on' : 'off',
|
|
);
|
|
}
|
|
|
|
/** Guarda la preferencia, tolerando entornos sin localStorage. */
|
|
private persist(enabled: boolean): void {
|
|
try {
|
|
this.document.defaultView?.localStorage.setItem(
|
|
FontPreferenceService.STORAGE_KEY,
|
|
String(enabled),
|
|
);
|
|
} catch {
|
|
// localStorage no disponible (modo kiosko restringido): se ignora.
|
|
}
|
|
}
|
|
|
|
private safeGetItem(key: string): string | null {
|
|
try {
|
|
return this.document.defaultView?.localStorage.getItem(key) ?? null;
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|
|
}
|