Files
ignis_app/lib/services/settings_service.dart
2026-04-22 23:25:48 +07:00

142 lines
4.5 KiB
Dart

import 'dart:convert';
import 'package:shared_preferences/shared_preferences.dart';
import '../models/home_config.dart';
import 'credentials_storage.dart';
/// Сервис для хранения списка "домов" и текущего выбранного.
/// Несекретные данные лежат в SharedPreferences, API-ключи -- отдельно.
class SettingsService {
static const String _homesKey = 'ignis_homes';
static const String _currentHomeKey = 'ignis_current_home_id';
final CredentialsStorage _credentialsStorage;
SettingsService({CredentialsStorage? credentialsStorage})
: _credentialsStorage = credentialsStorage ?? SecureCredentialsStorage();
/// Загрузить все дома
Future<List<HomeConfig>> getHomes() async {
final prefs = await SharedPreferences.getInstance();
final raw = prefs.getString(_homesKey);
if (raw == null || raw.isEmpty) return [];
final list = jsonDecode(raw) as List<dynamic>;
final migrated = await _migrateApiKeysIfNeeded(prefs, list);
return migrated.map(HomeConfig.fromJson).toList();
}
/// Сохранить весь список домов
Future<void> saveHomes(List<HomeConfig> homes) async {
final prefs = await SharedPreferences.getInstance();
await prefs.setString(
_homesKey,
jsonEncode(homes.map((h) => h.toJson()).toList()),
);
}
/// Добавить или обновить дом
Future<void> upsertHome(HomeConfig home, {String? apiKey}) async {
final homes = await getHomes();
final idx = homes.indexWhere((h) => h.id == home.id);
if (idx >= 0) {
homes[idx] = home;
} else {
homes.add(home);
}
if (apiKey != null) {
await setHomeApiKey(home.id, apiKey);
}
await saveHomes(homes);
}
/// Удалить дом по id
Future<void> deleteHome(String id) async {
final homes = await getHomes();
homes.removeWhere((h) => h.id == id);
await saveHomes(homes);
await deleteHomeApiKey(id);
// Если удалили текущий -- сбрасываем выбор
final currentId = await getCurrentHomeId();
if (currentId == id) {
await setCurrentHomeId(homes.isNotEmpty ? homes.first.id : null);
}
}
/// Получить id текущего активного дома
Future<String?> getCurrentHomeId() async {
final prefs = await SharedPreferences.getInstance();
return prefs.getString(_currentHomeKey);
}
/// Установить текущий дом
Future<void> setCurrentHomeId(String? id) async {
final prefs = await SharedPreferences.getInstance();
if (id == null) {
await prefs.remove(_currentHomeKey);
} else {
await prefs.setString(_currentHomeKey, id);
}
}
/// Удобный метод: получить текущий HomeConfig или null
Future<HomeConfig?> getCurrentHome() async {
final homes = await getHomes();
final id = await getCurrentHomeId();
if (id == null && homes.isNotEmpty) return homes.first;
try {
return homes.firstWhere((h) => h.id == id);
} catch (_) {
return homes.isNotEmpty ? homes.first : null;
}
}
Future<String?> getHomeApiKey(String homeId) =>
_credentialsStorage.getApiKey(homeId);
Future<String> requireHomeApiKey(String homeId) async {
final key = await getHomeApiKey(homeId);
if (key == null || key.isEmpty) {
throw StateError('API key is missing for home $homeId');
}
return key;
}
Future<void> setHomeApiKey(String homeId, String apiKey) =>
_credentialsStorage.setApiKey(homeId, apiKey);
Future<void> deleteHomeApiKey(String homeId) =>
_credentialsStorage.deleteApiKey(homeId);
Future<List<Map<String, dynamic>>> _migrateApiKeysIfNeeded(
SharedPreferences prefs,
List<dynamic> rawList,
) async {
var changed = false;
final result = <Map<String, dynamic>>[];
for (final item in rawList) {
final map = Map<String, dynamic>.from(item as Map);
final id = map['id']?.toString();
final legacyApiKey = map['apiKey']?.toString();
if (id != null && legacyApiKey != null && legacyApiKey.isNotEmpty) {
final existingKey = await getHomeApiKey(id);
if (existingKey == null || existingKey.isEmpty) {
await setHomeApiKey(id, legacyApiKey);
}
}
if (map.remove('apiKey') != null) {
changed = true;
}
result.add(map);
}
if (changed) {
await prefs.setString(_homesKey, jsonEncode(result));
}
return result;
}
}