Files
recordalexia/frontend/src/app/core/font-preference.service.ts
Jaume Garriga Maestre 52e559a159 feat: app completa recordaLexia (fases 1-5)
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.
2026-06-21 10:48:57 +02:00

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;
}
}
}