chore: integrate Hive for local storage management
- Added `hive_ce` and `hive_ce_flutter` dependencies for enhanced local storage capabilities. - Refactored the main application to initialize Hive and migrate existing data. - Updated storage service implementations to utilize Hive for managing application settings and task queues. - Removed the deprecated `StorageService` class to streamline the codebase and improve maintainability.
This commit is contained in:
210
lib/core/persistence/persistence_migrator.dart
Normal file
210
lib/core/persistence/persistence_migrator.dart
Normal file
@@ -0,0 +1,210 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
import '../utils/debug_logger.dart';
|
||||
import 'hive_boxes.dart';
|
||||
import 'persistence_keys.dart';
|
||||
|
||||
/// Handles one-time migration from SharedPreferences to Hive-backed storage.
|
||||
class PersistenceMigrator {
|
||||
PersistenceMigrator({required HiveBoxes hiveBoxes}) : _boxes = hiveBoxes;
|
||||
|
||||
static const int _targetVersion = 1;
|
||||
|
||||
final HiveBoxes _boxes;
|
||||
|
||||
Future<void> migrateIfNeeded() async {
|
||||
final currentVersion =
|
||||
_boxes.metadata.get(HiveStoreKeys.migrationVersion) as int?;
|
||||
if (currentVersion != null && currentVersion >= _targetVersion) {
|
||||
return;
|
||||
}
|
||||
|
||||
DebugLogger.log(
|
||||
'Starting SharedPreferences → Hive migration',
|
||||
scope: 'persistence/migration',
|
||||
);
|
||||
|
||||
try {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
await _migratePreferences(prefs);
|
||||
await _migrateCaches(prefs);
|
||||
await _migrateAttachmentQueue(prefs);
|
||||
await _migrateTaskQueue(prefs);
|
||||
|
||||
await _boxes.metadata.put(HiveStoreKeys.migrationVersion, _targetVersion);
|
||||
|
||||
await _cleanupLegacyKeys(prefs);
|
||||
DebugLogger.log('Migration completed', scope: 'persistence/migration');
|
||||
} catch (error, stack) {
|
||||
DebugLogger.error(
|
||||
'Migration failed',
|
||||
scope: 'persistence/migration',
|
||||
error: error,
|
||||
stackTrace: stack,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _migratePreferences(SharedPreferences prefs) async {
|
||||
final updates = <String, Object?>{};
|
||||
|
||||
void copyBool(String key) {
|
||||
final value = prefs.getBool(key);
|
||||
if (value != null) updates[key] = value;
|
||||
}
|
||||
|
||||
void copyDouble(String key) {
|
||||
final value = prefs.getDouble(key);
|
||||
if (value != null) updates[key] = value;
|
||||
}
|
||||
|
||||
void copyString(String key) {
|
||||
final value = prefs.getString(key);
|
||||
if (value != null && value.isNotEmpty) updates[key] = value;
|
||||
}
|
||||
|
||||
void copyStringList(String key) {
|
||||
final value = prefs.getStringList(key);
|
||||
if (value != null && value.isNotEmpty) {
|
||||
updates[key] = List<String>.from(value);
|
||||
}
|
||||
}
|
||||
|
||||
copyBool(PreferenceKeys.reduceMotion);
|
||||
copyDouble(PreferenceKeys.animationSpeed);
|
||||
copyBool(PreferenceKeys.hapticFeedback);
|
||||
copyBool(PreferenceKeys.highContrast);
|
||||
copyBool(PreferenceKeys.largeText);
|
||||
copyBool(PreferenceKeys.darkMode);
|
||||
copyString(PreferenceKeys.defaultModel);
|
||||
copyBool(PreferenceKeys.omitProviderInModelName);
|
||||
copyString(PreferenceKeys.voiceLocaleId);
|
||||
copyBool(PreferenceKeys.voiceHoldToTalk);
|
||||
copyBool(PreferenceKeys.voiceAutoSendFinal);
|
||||
copyString(PreferenceKeys.socketTransportMode);
|
||||
copyStringList(PreferenceKeys.quickPills);
|
||||
copyBool(PreferenceKeys.sendOnEnterKey);
|
||||
copyBool(PreferenceKeys.rememberCredentials);
|
||||
copyString(PreferenceKeys.activeServerId);
|
||||
copyString(PreferenceKeys.themeMode);
|
||||
copyString(PreferenceKeys.localeCode);
|
||||
copyBool(PreferenceKeys.onboardingSeen);
|
||||
copyBool(PreferenceKeys.reviewerMode);
|
||||
|
||||
if (updates.isNotEmpty) {
|
||||
await _boxes.preferences.putAll(updates);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _migrateCaches(SharedPreferences prefs) async {
|
||||
final jsonString = prefs.getString(HiveStoreKeys.localConversations);
|
||||
if (jsonString == null || jsonString.isEmpty) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
final decoded = jsonDecode(jsonString);
|
||||
if (decoded is List) {
|
||||
final list = decoded
|
||||
.map((entry) => Map<String, dynamic>.from(entry as Map))
|
||||
.toList(growable: false);
|
||||
await _boxes.caches.put(HiveStoreKeys.localConversations, list);
|
||||
}
|
||||
} catch (error, stack) {
|
||||
DebugLogger.error(
|
||||
'Failed to migrate local conversations',
|
||||
scope: 'persistence/migration',
|
||||
error: error,
|
||||
stackTrace: stack,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _migrateAttachmentQueue(SharedPreferences prefs) async {
|
||||
final jsonString = prefs.getString(
|
||||
LegacyPreferenceKeys.attachmentUploadQueue,
|
||||
);
|
||||
if (jsonString == null || jsonString.isEmpty) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
final decoded = jsonDecode(jsonString);
|
||||
if (decoded is List) {
|
||||
final list = decoded
|
||||
.map((entry) => Map<String, dynamic>.from(entry as Map))
|
||||
.toList(growable: false);
|
||||
await _boxes.attachmentQueue.put(
|
||||
HiveStoreKeys.attachmentQueueEntries,
|
||||
list,
|
||||
);
|
||||
}
|
||||
} catch (error, stack) {
|
||||
DebugLogger.error(
|
||||
'Failed to migrate attachment queue',
|
||||
scope: 'persistence/migration',
|
||||
error: error,
|
||||
stackTrace: stack,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _migrateTaskQueue(SharedPreferences prefs) async {
|
||||
final jsonString = prefs.getString(LegacyPreferenceKeys.taskQueue);
|
||||
if (jsonString == null || jsonString.isEmpty) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
final decoded = jsonDecode(jsonString);
|
||||
if (decoded is List) {
|
||||
final list = decoded
|
||||
.map((entry) => Map<String, dynamic>.from(entry as Map))
|
||||
.toList(growable: false);
|
||||
await _boxes.caches.put(HiveStoreKeys.taskQueue, list);
|
||||
}
|
||||
} catch (error, stack) {
|
||||
DebugLogger.error(
|
||||
'Failed to migrate outbound task queue',
|
||||
scope: 'persistence/migration',
|
||||
error: error,
|
||||
stackTrace: stack,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _cleanupLegacyKeys(SharedPreferences prefs) async {
|
||||
final keysToRemove = <String>[
|
||||
PreferenceKeys.reduceMotion,
|
||||
PreferenceKeys.animationSpeed,
|
||||
PreferenceKeys.hapticFeedback,
|
||||
PreferenceKeys.highContrast,
|
||||
PreferenceKeys.largeText,
|
||||
PreferenceKeys.darkMode,
|
||||
PreferenceKeys.defaultModel,
|
||||
PreferenceKeys.omitProviderInModelName,
|
||||
PreferenceKeys.voiceLocaleId,
|
||||
PreferenceKeys.voiceHoldToTalk,
|
||||
PreferenceKeys.voiceAutoSendFinal,
|
||||
PreferenceKeys.socketTransportMode,
|
||||
PreferenceKeys.quickPills,
|
||||
PreferenceKeys.sendOnEnterKey,
|
||||
PreferenceKeys.rememberCredentials,
|
||||
PreferenceKeys.activeServerId,
|
||||
PreferenceKeys.themeMode,
|
||||
PreferenceKeys.localeCode,
|
||||
PreferenceKeys.onboardingSeen,
|
||||
PreferenceKeys.reviewerMode,
|
||||
HiveStoreKeys.localConversations,
|
||||
HiveStoreKeys.attachmentQueueEntries,
|
||||
LegacyPreferenceKeys.attachmentUploadQueue,
|
||||
LegacyPreferenceKeys.taskQueue,
|
||||
];
|
||||
|
||||
for (final key in keysToRemove) {
|
||||
await prefs.remove(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user