refactor(state): extract export/import service and equalizer state from EstadoRadio
- New ServicioExportImport owns the v2 backup envelope, pretty JSON encode and graceful decode; byte-compatible with existing exports, locked by a round-trip test - pantalla_ajustes delegates backup serialization to the service (inline jsonDecode/jsonEncode removed) - New EstadoEcualizador ChangeNotifier owns all EQ state and persistence (principal/current/per-station presets, active flag), exposed via its own provider so EQ changes no longer rebuild EstadoRadio consumers - EstadoRadio slims down ~210 lines and keeps 15 delegating compat members marked TODO(S4b) for the next slice to remove - Player EQ toggle rewired to the new provider to avoid going stale - 4 new tests (103 total green), flutter analyze clean
This commit is contained in:
@@ -0,0 +1,80 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import '../modelos/emisora.dart';
|
||||
import '../modelos/grupo_favoritos.dart';
|
||||
import '../modelos/preset_ecualizador.dart';
|
||||
|
||||
/// Owns the backup (export/import) JSON serialization (S4-R4).
|
||||
///
|
||||
/// The v2 envelope produced here is byte-compatible with the legacy format
|
||||
/// previously assembled inline by `EstadoRadio.exportarConfig` and
|
||||
/// pretty-printed by `pantalla_ajustes.dart`, so existing exports keep
|
||||
/// round-tripping. State APPLICATION (writing favorites, EQ, alarms back
|
||||
/// into the app) stays in `EstadoRadio.importarConfig` — this service only
|
||||
/// owns serialization, parsing and the envelope shape.
|
||||
class ServicioExportImport {
|
||||
const ServicioExportImport();
|
||||
|
||||
/// Current backup schema version (v2 — full portability).
|
||||
static const int versionActual = 2;
|
||||
|
||||
/// Builds the v2 export envelope. Key set and semantics must stay exactly
|
||||
/// as the legacy inline export so old backups remain importable:
|
||||
/// the `alarmas` block is the RAW JSON map persisted by ServicioAlarmas
|
||||
/// and passes through untouched (no re-parsing here).
|
||||
Map<String, dynamic> construirExportacion({
|
||||
required List<GrupoFavoritos> gruposFavoritos,
|
||||
required List<Emisora> favoritos,
|
||||
required List<Emisora> emisorasCustom,
|
||||
required PresetEcualizador presetPrincipal,
|
||||
required Map<String, PresetEcualizador> presetsPorEmisora,
|
||||
required Map<String, dynamic>? alarmas,
|
||||
required String? emisoraPreferidaUuid,
|
||||
required String ordenListas,
|
||||
required List<int> timerSuenoPresetsSegundos,
|
||||
DateTime? exportadoEn,
|
||||
}) {
|
||||
return {
|
||||
'version': versionActual,
|
||||
'exportedAt': (exportadoEn ?? DateTime.now()).toIso8601String(),
|
||||
// Favorites + groups (preserves grupo_id assignments per station).
|
||||
// The protected "sin asignar" group is implicit and never exported.
|
||||
'gruposFavoritos':
|
||||
gruposFavoritos
|
||||
.where((g) => !g.esSinAsignar)
|
||||
.map((g) => g.toMap())
|
||||
.toList(),
|
||||
'favoritos': favoritos.map((e) => e.toMap()).toList(),
|
||||
// Custom stations.
|
||||
'emisorasCustom': emisorasCustom.map((e) => e.toMap()).toList(),
|
||||
// Equalizer.
|
||||
'presetPrincipalEcualizador': presetPrincipal.toJson(),
|
||||
'presetsEcualizador': presetsPorEmisora.map(
|
||||
(uuid, preset) => MapEntry(uuid, preset.toJson()),
|
||||
),
|
||||
// Full alarm block (alarms + vacations + exceptions) — raw passthrough.
|
||||
'alarmas': alarmas,
|
||||
// User preferences.
|
||||
'emisoraPreferidaUuid': emisoraPreferidaUuid,
|
||||
'ordenListas': ordenListas,
|
||||
'timerSuenoPresetsSegundos': timerSuenoPresetsSegundos,
|
||||
};
|
||||
}
|
||||
|
||||
/// Serializes an export envelope to pretty-printed JSON (same formatting
|
||||
/// the legacy export shared as a file).
|
||||
String exportar(Map<String, dynamic> config) =>
|
||||
const JsonEncoder.withIndent(' ').convert(config);
|
||||
|
||||
/// Parses a backup JSON string. Returns `null` on malformed input or when
|
||||
/// the document is not a JSON object — graceful, never throws (S4-R4).
|
||||
Map<String, dynamic>? importar(String raw) {
|
||||
try {
|
||||
final decoded = jsonDecode(raw);
|
||||
if (decoded is! Map) return null;
|
||||
return Map<String, dynamic>.from(decoded);
|
||||
} on FormatException {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user