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 readSecure(SecureStorageKey key) { return _secureStorage.read(key: key.name); } /// Writes a value to secure storage. /// /// Pass `null` to delete the key. Future writeSecure(SecureStorageKey key, String? value) { return _secureStorage.write(key: key.name, value: value); } /// Clears the JWT token from secure storage. Future clearToken() { return writeSecure(SecureStorageKey.jwt, null); } // --------------------------------------------------------------------------- // Config // --------------------------------------------------------------------------- /// Reads the app configuration from storage. Future 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 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> 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 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 writeChange(Change change) async { final box = await Hive.openBox(_changeQueueBox); await box.add(change.toMap()); } /// Reads all pending changes from the queue. Future readChangeQueue() async { final box = await Hive.openBox(_changeQueueBox); final queue = ChangeQueue(); for (final map in box.values) { queue.addChange(Change.fromMap(map as Map)); } return queue; } /// Removes the oldest change from the queue. /// /// Call this after successfully syncing a change to the server. Future deleteOldestChange() async { final box = await Hive.openBox(_changeQueueBox); if (box.isNotEmpty) { await box.deleteAt(0); } } }