Files
pluriwave/openspec/changes/eq-device-autoswitch-ux/tasks.md
T
FreeTLab bccc5c48b8
Build & Deploy PluriWave / Análisis de código (push) Successful in 38s
Build & Deploy PluriWave / Build APK + AAB release (push) Successful in 2m30s
docs(openspec): add SDD artifact trail for recent alarm and EQ changes
Persist the exploration, proposal, spec, design, tasks, and
verify/archive reports produced during the multi-device EQ,
alarm-countdown, and notification-visual-polish SDD cycles.
2026-07-04 12:42:11 +02:00

88 lines
7.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Tasks: EQ Device Auto-Switch UX
## Review Workload Forecast
| Field | Value |
|-------|-------|
| Estimated changed lines | 380520 |
| 400-line budget risk | High |
| Chained PRs recommended | Yes |
| Suggested split | PR 1 (service + state) → PR 2 (UI + l10n) → PR 3 (widget tests) |
| Delivery strategy | ask-on-risk |
| Chain strategy | pending |
Decision needed before apply: Yes
Chained PRs recommended: Yes
Chain strategy: pending
400-line budget risk: High
### Suggested Work Units
| Unit | Goal | Likely PR | Notes |
|------|------|-----------|-------|
| 1 | Bug fix + rename persistence layer (service + state) | PR 1 | base = main; ~150 lines; no UI dependency |
| 2 | Settings UI rewrite + edit modal + l10n | PR 2 | base = PR 1; ~220 lines; depends on Unit 1 APIs |
| 3 | Widget tests for modal + integration tests | PR 3 | base = PR 2; ~120 lines; closes test coverage gap |
---
## Phase 1: Foundation — Service Layer (PR 1 slice)
> Strict TDD: RED first, then GREEN, then REFACTOR for each task group.
- [ ] 1.1 **RED** — Write failing unit tests in `test/servicios/servicio_ecualizador_test.dart` for `cargarNombresDispositivos()` returning empty map when SP key absent
- [ ] 1.2 **RED** — Add failing test: `guardarNombresDispositivos({id: name})` writes JSON to `eq_nombres_dispositivos_v1` SP key
- [ ] 1.3 **RED** — Add failing test: round-trip save → load returns same map
- [ ] 1.4 **GREEN** — Add `_keyNombresDispositivos = 'eq_nombres_dispositivos_v1'` to `lib/servicios/servicio_ecualizador.dart`
- [ ] 1.5 **GREEN** — Implement `cargarNombresDispositivos()` and `guardarNombresDispositivos()` in `ServicioEcualizador` (reuse existing `_leerMapa`/`_guardarMapa` JSON pattern adapted for `Map<String, String>`)
- [ ] 1.6 **GREEN** — Add `nombresDispositivos` field to `ConfiguracionEcualizador` data class; include in `cargar()` and wire into `ServicioEcualizador.cargar()` call
- [ ] 1.7 **REFACTOR** — Ensure no duplicate SP read calls; confirm `cargar()` returns `nombresDispositivos` in a single pass; update any `copyWith` if present
---
## Phase 2: Core — State Layer Bug Fix + Rename API (PR 1 slice)
- [ ] 2.1 **RED** — Write failing unit tests in `test/estado/estado_ecualizador_test.dart`: after `cargarPersistido()` with `eqMultiDeviceEnabled=true` and fake device service returning `'bt_a2dp:AA:BB'`, assert `dispositivoActualId == 'bt_a2dp:AA:BB'` (spec scenario: correct preset at startup)
- [ ] 2.2 **RED** — Add failing test: first-seen device is bootstrapped with copy of resolved preset after `cargarPersistido()` (spec scenario: first-seen device bootstrapped at startup)
- [ ] 2.3 **RED** — Add failing test: `obtenerDispositivoActual()` throws → `dispositivoActualId` falls back to `'builtin_speaker'` (spec scenario: graceful failure)
- [ ] 2.4 **RED** — Add failing test: `eqMultiDeviceEnabled=false``obtenerDispositivoActual()` never called (spec scenario: toggle off)
- [ ] 2.5 **GREEN** — In `lib/estado/estado_ecualizador.dart`, inside `cargarPersistido()`, after `_configurarSuscripcionDispositivo()`, add `try/catch` `await obtenerDispositivoActual()` call that seeds `_dispositivoActualId` then calls `_onDispositivoCambiado()` to trigger bootstrap + preset resolution
- [ ] 2.6 **RED** — Add failing tests for rename API: `renombrarDispositivo(id, nombre)` writes to `_nombresDispositivos`, calls `notifyListeners()`; empty string is no-op (spec scenarios: rename persists, empty name not persisted)
- [ ] 2.7 **RED** — Add failing test: `obtenerNombreDispositivo(id)` returns stored name or empty string
- [ ] 2.8 **RED** — Add failing tests for `nombreVisible(deviceId, platformName)` fallback chain: custom > platform > id (all three spec scenarios)
- [ ] 2.9 **GREEN** — Add `_nombresDispositivos` map field; implement `renombrarDispositivo()`, `obtenerNombreDispositivo()`, `nombreVisible()`, and `get nombresDispositivos` getter in `lib/estado/estado_ecualizador.dart`; wire to service persist on rename
- [ ] 2.10 **REFACTOR** — Extract fallback chain to a single helper, ensure `_nombresDispositivos` is loaded from `ConfiguracionEcualizador.nombresDispositivos` in `cargarPersistido()`
---
## Phase 3: Integration — Settings UI Rewrite (PR 2 slice)
- [ ] 3.1 **RED** — Write failing widget test in `test/pantallas/pantalla_ajustes_test.dart`: connection dot is present on active device row, absent on inactive rows (spec scenario: active device shows green indicator)
- [ ] 3.2 **RED** — Add failing widget test: tapping a device row opens a bottom sheet with a `TextField` pre-filled with resolved display name and an `EcualizadorWidget` (spec scenario: modal opens on device row tap)
- [ ] 3.3 **RED** — Add failing widget test: `EcualizadorWidget` in modal is reachable by scrolling on small viewport (spec scenario: modal is scrollable)
- [ ] 3.4 **GREEN** — Rewrite `_SeccionEcualizadorAvanzado` device rows in `lib/pantallas/pantalla_ajustes.dart`: add green dot `Icon` gated on `entry.key == eq.dispositivoActualId`; display name via `eq.nombreVisible(id, device.nombre)`; add `onTap` handler
- [ ] 3.5 **GREEN** — Implement `_DialogoEdicionDispositivo` `StatefulWidget` in `lib/pantallas/pantalla_ajustes.dart`: `showModalBottomSheet` with `showDragHandle: true`, `isScrollControlled: true`, `TextField` pre-filled with display name, embedded `EcualizadorWidget`, confirm/dismiss logic calling `eq.renombrarDispositivo(id, name)`
- [ ] 3.6 **RED** — Add failing widget test: renaming in modal and confirming calls `renombrarDispositivo`; device row reflects new name after close (spec scenario: rename confirmed in modal persists)
- [ ] 3.7 **RED** — Add failing widget test: dismissing modal without confirming leaves name unchanged (spec scenario: modal dismiss without save)
- [ ] 3.8 **GREEN** — Wire dismiss-without-save path (no `renombrarDispositivo` call on drag-dismiss)
- [ ] 3.9 **REFACTOR** — Extract modal into its own file if `pantalla_ajustes.dart` exceeds 400 lines; ensure fallback chain is called from `EstadoEcualizador`, not inlined in UI
---
## Phase 4: Localisation (PR 2 slice)
- [ ] 4.1 Add ~5 new ARB keys to `lib/l10n/app_en.arb`: `eqDeviceEditTitle`, `eqDeviceNameLabel`, `eqDeviceNameHint`, `eqDeviceNameConfirm`, `eqDeviceConnected`
- [ ] 4.2 Translate all 5 keys in the 12 remaining locale files (`app_es.arb`, `app_pt.arb`, `app_fr.arb`, `app_de.arb`, `app_it.arb`, `app_ja.arb`, `app_ko.arb`, `app_zh.arb`, `app_ru.arb`, `app_ar.arb`, `app_hi.arb`, `app_tr.arb`)
- [ ] 4.3 Run `flutter gen-l10n` and verify no ARB parse errors; confirm generated `.g.dart` includes all new keys
- [ ] 4.4 Replace any hardcoded strings in `_SeccionEcualizadorAvanzado` and `_DialogoEdicionDispositivo` with l10n references
---
## Phase 5: Cleanup + Verification (PR 3 slice)
- [ ] 5.1 Run full test suite; confirm all RED tasks from Phases 13 are GREEN
- [ ] 5.2 Verify spec scenario coverage: cross-check each scenario in `openspec/changes/eq-device-autoswitch-ux/spec.md` against a corresponding test (add any gap tests)
- [ ] 5.3 Run `flutter analyze`; resolve any new lint warnings introduced by this change
- [ ] 5.4 Remove any `TODO`/`FIXME` comments introduced during GREEN phase
- [ ] 5.5 Confirm `nombresDispositivos` omission from export schema v4 is documented in design open questions (no code change needed — already deferred per proposal)