import 'dart:typed_data'; import 'package:core/presentation/extensions/map_extensions.dart'; import 'package:core/utils/app_logger.dart'; import 'package:hive_ce/hive.dart'; import 'package:tmail_ui_user/features/caching/config/hive_cache_config.dart'; import 'package:tmail_ui_user/features/caching/utils/cache_utils.dart'; abstract class HiveCacheClient { String get tableName; bool get encryption => false; Future _getEncryptionKey() => HiveCacheConfig.instance.getEncryptionKey(); Future> openIsolatedBox() async { if (IsolatedHive.isBoxOpen(tableName)) { return IsolatedHive.box(tableName); } else { if (encryption) { final encryptionKey = await _getEncryptionKey(); return IsolatedHive.openBox( tableName, encryptionCipher: encryptionKey != null ? HiveAesCipher(encryptionKey) : null, ); } else { return IsolatedHive.openBox(tableName); } } } Future> openBox() async { if (Hive.isBoxOpen(tableName)) { return Hive.box(tableName); } else { if (encryption) { final encryptionKey = await _getEncryptionKey(); return Hive.openBox( tableName, encryptionCipher: encryptionKey != null ? HiveAesCipher(encryptionKey) : null, ); } else { return Hive.openBox(tableName); } } } Future insertItem( String key, T newObject, { bool isolated = true, }) { log('$runtimeType::insertItem:encryption: $encryption - key = $key - isolated = $isolated'); return Future.sync(() async { if (isolated) { final boxItem = await openIsolatedBox(); return boxItem.put(key, newObject); } else { final boxItem = await openBox(); return boxItem.put(key, newObject); } }).catchError((error) { throw error; }); } Future insertMultipleItem( Map mapObject, { bool isolated = true, }) { return Future.sync(() async { if (isolated) { final boxItem = await openIsolatedBox(); return boxItem.putAll(mapObject); } else { final boxItem = await openBox(); return boxItem.putAll(mapObject); } }).catchError((error) { throw error; }); } Future getItem( String key, { bool isolated = true, }) { return Future.sync(() async { if (isolated) { final boxItem = await openIsolatedBox(); return boxItem.get(key); } else { final boxItem = await openBox(); return boxItem.get(key); } }).catchError((error) { throw error; }); } Future> getAll({bool isolated = true}) { return Future.sync(() async { Iterable items; if (isolated) { final boxItem = await openIsolatedBox(); items = await boxItem.values; } else { final boxItem = await openBox(); items = boxItem.values; } log('$runtimeType::getAll: Length of items is ${items.length}'); return items.toList(); }).catchError((error) { throw error; }); } Future> getMapItems({bool isolated = true}) { return Future.sync(() async { late Map mapItems; if (isolated) { final boxItem = await openIsolatedBox(); mapItems = await boxItem.toMap(); } else { final boxItem = await openBox(); mapItems = boxItem.toMap(); } log('$runtimeType::getMapItems: Length of mapItems is ${mapItems.length}'); return mapItems.map((key, value) => MapEntry(key.toString(), value)); }).catchError((error) { throw error; }); } Future> getListByNestedKey( String nestedKey, { bool isolated = true, }) { return Future.sync(() async { late Map mapItems; if (isolated) { final boxItem = await openIsolatedBox(); mapItems = await boxItem.toMap(); } else { final boxItem = await openBox(); mapItems = boxItem.toMap(); } final listItem = mapItems .where((key, value) => _matchedNestedKey(key, nestedKey)) .values .toList(); log('$runtimeType::getListByNestedKey: Length of listItem is ${listItem.length}'); return listItem; }).catchError((error) { throw error; }); } Future> getValuesByListKey( List listKeys, { bool isolated = true, }) { return Future.sync(() async { late Map mapItems; if (isolated) { final boxItem = await openIsolatedBox(); mapItems = await boxItem.toMap(); } else { final boxItem = await openBox(); mapItems = boxItem.toMap(); } return mapItems .where((key, value) => listKeys.contains(key)) .values .toList(); }).catchError((error) { throw error; }); } bool _matchedNestedKey(String key, String nestedKey) { final decodedKey = CacheUtils.decodeKey(key); final decodedNestedKey = CacheUtils.decodeKey(nestedKey); return decodedKey.contains(decodedNestedKey); } Future updateItem( String key, T newObject, { bool isolated = true, }) { return Future.sync(() async { if (isolated) { final boxItem = await openIsolatedBox(); return boxItem.put(key, newObject); } else { final boxItem = await openBox(); return boxItem.put(key, newObject); } }).catchError((error) { throw error; }); } Future updateMultipleItem( Map mapObject, { bool isolated = true, }) { return Future.sync(() async { if (isolated) { final boxItem = await openIsolatedBox(); return boxItem.putAll(mapObject); } else { final boxItem = await openBox(); return boxItem.putAll(mapObject); } }).catchError((error) { throw error; }); } Future deleteItem( String key, { bool isolated = true, }) { return Future.sync(() async { if (isolated) { final boxItem = await openIsolatedBox(); return boxItem.delete(key); } else { final boxItem = await openBox(); return boxItem.delete(key); } }).catchError((error) { throw error; }); } Future deleteMultipleItem( List listKey, { bool isolated = true, }) { return Future.sync(() async { if (isolated) { final boxItem = await openIsolatedBox(); return boxItem.deleteAll(listKey); } else { final boxItem = await openBox(); return boxItem.deleteAll(listKey); } }).catchError((error) { throw error; }); } Future isExistItem( String key, { bool isolated = true, }) { return Future.sync(() async { if (isolated) { final boxItem = await openIsolatedBox(); return boxItem.containsKey(key); } else { final boxItem = await openBox(); return boxItem.containsKey(key); } }).catchError((error) { throw error; }); } Future deleteBox({bool isolated = true}) { if (isolated) { return IsolatedHive.deleteBoxFromDisk(tableName); } else { return Hive.deleteBoxFromDisk(tableName); } } Future clearAllData({bool isolated = true}) { return Future.sync(() async { if (isolated) { final boxItem = await openIsolatedBox(); return boxItem.clear(); } else { final boxItem = await openBox(); return boxItem.clear(); } }).catchError((error) { throw error; }); } Future clearAllDataContainKey( String nestedKey, { bool isolated = true, }) { return Future.sync(() async { if (isolated) { final boxItem = await openIsolatedBox(); final mapItems = await boxItem.toMap(); final listKeys = mapItems .where((key, value) => _matchedNestedKey(key, nestedKey)) .keys; log('$runtimeType::clearAllDataContainKey: Length of keys is ${listKeys.length}'); return boxItem.deleteAll(listKeys); } else { final boxItem = await openBox(); final listKeys = boxItem.toMap() .where((key, value) => _matchedNestedKey(key, nestedKey)) .keys; log('$runtimeType::clearAllDataContainKey: Length of keys is ${listKeys.length}'); return boxItem.deleteAll(listKeys); } }).catchError((error) { throw error; }); } Future closeBox({bool isolated = true}) async { if (isolated) { if (IsolatedHive.isBoxOpen(tableName)) { await IsolatedHive.box(tableName).close(); } } else { if (Hive.isBoxOpen(tableName)) { await Hive.box(tableName).close(); } } } }