Files
pluriwave/lib/servicios/servicio_export_import.dart
T
FreeTLab 0416b301b2 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
2026-06-11 21:16:30 +02:00

81 lines
3.2 KiB
Dart

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