120 lines
4.0 KiB
Dart
120 lines
4.0 KiB
Dart
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
|
import 'package:hive/hive.dart';
|
|
import 'package:sheetless/core/models/change.dart';
|
|
import 'package:sheetless/core/models/config.dart';
|
|
|
|
/// Keys for secure storage (credentials and tokens).
|
|
enum SecureStorageKey { url, jwt, email }
|
|
|
|
/// Service for managing local storage operations.
|
|
///
|
|
/// Uses [FlutterSecureStorage] for sensitive data (credentials, tokens)
|
|
/// and [Hive] for general app data (config, sheet access times, change queue).
|
|
class StorageService {
|
|
// Hive box names
|
|
static const String _sheetAccessTimesBox = 'sheetAccessTimes';
|
|
static const String _configBox = 'config';
|
|
static const String _changeQueueBox = 'changeQueue';
|
|
|
|
late final FlutterSecureStorage _secureStorage;
|
|
|
|
StorageService() {
|
|
_secureStorage = FlutterSecureStorage(
|
|
aOptions: const AndroidOptions(encryptedSharedPreferences: true),
|
|
);
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Secure Storage (Credentials & Tokens)
|
|
// ---------------------------------------------------------------------------
|
|
|
|
/// Reads a value from secure storage.
|
|
Future<String?> readSecure(SecureStorageKey key) {
|
|
return _secureStorage.read(key: key.name);
|
|
}
|
|
|
|
/// Writes a value to secure storage.
|
|
///
|
|
/// Pass `null` to delete the key.
|
|
Future<void> writeSecure(SecureStorageKey key, String? value) {
|
|
return _secureStorage.write(key: key.name, value: value);
|
|
}
|
|
|
|
/// Clears the JWT token from secure storage.
|
|
Future<void> clearToken() {
|
|
return writeSecure(SecureStorageKey.jwt, null);
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Config
|
|
// ---------------------------------------------------------------------------
|
|
|
|
/// Reads the app configuration from storage.
|
|
Future<Config> readConfig() async {
|
|
final box = await Hive.openBox(_configBox);
|
|
return Config(
|
|
twoPageMode: box.get(Config.keyTwoPageMode) ?? false,
|
|
fullscreen: box.get(Config.keyFullscreen) ?? false,
|
|
);
|
|
}
|
|
|
|
/// Writes the app configuration to storage.
|
|
Future<void> writeConfig(Config config) async {
|
|
final box = await Hive.openBox(_configBox);
|
|
await box.put(Config.keyTwoPageMode, config.twoPageMode);
|
|
await box.put(Config.keyFullscreen, config.fullscreen);
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Sheet Access Times (for sorting by recency)
|
|
// ---------------------------------------------------------------------------
|
|
|
|
/// Reads all sheet access times.
|
|
///
|
|
/// Returns a map of sheet UUID to last access time.
|
|
Future<Map<String, DateTime>> readSheetAccessTimes() async {
|
|
final box = await Hive.openBox(_sheetAccessTimesBox);
|
|
return box.toMap().map(
|
|
(key, value) => MapEntry(key as String, DateTime.parse(value as String)),
|
|
);
|
|
}
|
|
|
|
/// Records when a sheet was last accessed.
|
|
Future<void> writeSheetAccessTime(String uuid, DateTime datetime) async {
|
|
final box = await Hive.openBox(_sheetAccessTimesBox);
|
|
await box.put(uuid, datetime.toIso8601String());
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Change Queue (Offline Sync)
|
|
// ---------------------------------------------------------------------------
|
|
|
|
/// Adds a change to the offline queue.
|
|
Future<void> writeChange(Change change) async {
|
|
final box = await Hive.openBox(_changeQueueBox);
|
|
await box.add(change.toMap());
|
|
}
|
|
|
|
/// Reads all pending changes from the queue.
|
|
Future<ChangeQueue> readChangeQueue() async {
|
|
final box = await Hive.openBox(_changeQueueBox);
|
|
final queue = ChangeQueue();
|
|
|
|
for (final map in box.values) {
|
|
queue.addChange(Change.fromMap(map as Map<dynamic, dynamic>));
|
|
}
|
|
|
|
return queue;
|
|
}
|
|
|
|
/// Removes the oldest change from the queue.
|
|
///
|
|
/// Call this after successfully syncing a change to the server.
|
|
Future<void> deleteOldestChange() async {
|
|
final box = await Hive.openBox(_changeQueueBox);
|
|
if (box.isNotEmpty) {
|
|
await box.deleteAt(0);
|
|
}
|
|
}
|
|
}
|