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.
7.3 KiB
7.3 KiB
Tasks: EQ Device Auto-Switch UX
Review Workload Forecast
| Field | Value |
|---|---|
| Estimated changed lines | 380–520 |
| 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.dartforcargarNombresDispositivos()returning empty map when SP key absent - 1.2 RED — Add failing test:
guardarNombresDispositivos({id: name})writes JSON toeq_nombres_dispositivos_v1SP key - 1.3 RED — Add failing test: round-trip save → load returns same map
- 1.4 GREEN — Add
_keyNombresDispositivos = 'eq_nombres_dispositivos_v1'tolib/servicios/servicio_ecualizador.dart - 1.5 GREEN — Implement
cargarNombresDispositivos()andguardarNombresDispositivos()inServicioEcualizador(reuse existing_leerMapa/_guardarMapaJSON pattern adapted forMap<String, String>) - 1.6 GREEN — Add
nombresDispositivosfield toConfiguracionEcualizadordata class; include incargar()and wire intoServicioEcualizador.cargar()call - 1.7 REFACTOR — Ensure no duplicate SP read calls; confirm
cargar()returnsnombresDispositivosin a single pass; update anycopyWithif 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: aftercargarPersistido()witheqMultiDeviceEnabled=trueand fake device service returning'bt_a2dp:AA:BB', assertdispositivoActualId == '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 →dispositivoActualIdfalls 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, insidecargarPersistido(), after_configurarSuscripcionDispositivo(), addtry/catchawait obtenerDispositivoActual()call that seeds_dispositivoActualIdthen calls_onDispositivoCambiado()to trigger bootstrap + preset resolution - 2.6 RED — Add failing tests for rename API:
renombrarDispositivo(id, nombre)writes to_nombresDispositivos, callsnotifyListeners(); 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
_nombresDispositivosmap field; implementrenombrarDispositivo(),obtenerNombreDispositivo(),nombreVisible(), andget nombresDispositivosgetter inlib/estado/estado_ecualizador.dart; wire to service persist on rename - 2.10 REFACTOR — Extract fallback chain to a single helper, ensure
_nombresDispositivosis loaded fromConfiguracionEcualizador.nombresDispositivosincargarPersistido()
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
TextFieldpre-filled with resolved display name and anEcualizadorWidget(spec scenario: modal opens on device row tap) - 3.3 RED — Add failing widget test:
EcualizadorWidgetin modal is reachable by scrolling on small viewport (spec scenario: modal is scrollable) - 3.4 GREEN — Rewrite
_SeccionEcualizadorAvanzadodevice rows inlib/pantallas/pantalla_ajustes.dart: add green dotIcongated onentry.key == eq.dispositivoActualId; display name viaeq.nombreVisible(id, device.nombre); addonTaphandler - 3.5 GREEN — Implement
_DialogoEdicionDispositivoStatefulWidgetinlib/pantallas/pantalla_ajustes.dart:showModalBottomSheetwithshowDragHandle: true,isScrollControlled: true,TextFieldpre-filled with display name, embeddedEcualizadorWidget, confirm/dismiss logic callingeq.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
renombrarDispositivocall on drag-dismiss) - 3.9 REFACTOR — Extract modal into its own file if
pantalla_ajustes.dartexceeds 400 lines; ensure fallback chain is called fromEstadoEcualizador, 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-l10nand verify no ARB parse errors; confirm generated.g.dartincludes all new keys - 4.4 Replace any hardcoded strings in
_SeccionEcualizadorAvanzadoand_DialogoEdicionDispositivowith l10n references
Phase 5: Cleanup + Verification (PR 3 slice)
- 5.1 Run full test suite; confirm all RED tasks from Phases 1–3 are GREEN
- 5.2 Verify spec scenario coverage: cross-check each scenario in
openspec/changes/eq-device-autoswitch-ux/spec.mdagainst 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/FIXMEcomments introduced during GREEN phase - 5.5 Confirm
nombresDispositivosomission from export schema v4 is documented in design open questions (no code change needed — already deferred per proposal)