Line data Source code
1 : part of flutter_data; 2 : 3 : /// Hive implementation of [LocalAdapter] and Hive's [TypeAdapter]. 4 : // ignore: must_be_immutable 5 : abstract class HiveLocalAdapter<T extends DataModel<T>> extends LocalAdapter<T> 6 : with TypeAdapter<T> { 7 1 : HiveLocalAdapter(ProviderReference _ref) 8 2 : : _hiveLocalStorage = _ref.read(hiveLocalStorageProvider), 9 1 : super(_ref); 10 : 11 : final HiveLocalStorage _hiveLocalStorage; 12 : 13 : final _hiveAdapterNs = '_adapter_hive'; 14 3 : String get _hiveAdapterKey => StringUtils.namespace(_hiveAdapterNs, 'key'); 15 : 16 2 : String get _internalType => DataHelpers.getType<T>(); 17 : 18 : @protected 19 : @visibleForTesting 20 : Box<T>? box; 21 : 22 : @override 23 1 : Future<HiveLocalAdapter<T>> initialize() async { 24 1 : if (isInitialized) return this; 25 : 26 0 : if (!_hiveLocalStorage.hive.isBoxOpen(_internalType)) { 27 0 : if (!_hiveLocalStorage.hive.isAdapterRegistered(typeId)) { 28 0 : _hiveLocalStorage.hive.registerAdapter(this); 29 : } 30 0 : if (_hiveLocalStorage.clear) { 31 0 : await _hiveLocalStorage.deleteBox(_internalType); 32 : } 33 : } 34 : 35 : try { 36 0 : box = await _hiveLocalStorage.openBox<T>(_internalType); 37 : } catch (e) { 38 0 : await _hiveLocalStorage.deleteBox(_internalType); 39 0 : box = await _hiveLocalStorage.openBox<T>(_internalType); 40 : } 41 : 42 : return this; 43 : } 44 : 45 1 : @override 46 2 : bool get isInitialized => box?.isOpen ?? false; 47 : 48 1 : @override 49 : void dispose() { 50 2 : box?.close(); 51 : } 52 : 53 : // protected API 54 : 55 1 : @override 56 : List<T> findAll() { 57 3 : return box!.values.toImmutableList(); 58 : } 59 : 60 1 : @override 61 2 : T? findOne(String key) => box!.get(key); 62 : 63 : @override 64 1 : Future<T> save(String key, T model, {bool notify = true}) async { 65 2 : final keyExisted = box!.containsKey(key); 66 2 : final save = box!.put(key, model); 67 : if (notify) { 68 2 : graph._notify( 69 1 : [key], 70 : keyExisted ? DataGraphEventType.updateNode : DataGraphEventType.addNode, 71 : ); 72 : } 73 1 : await save; 74 : return model; 75 : } 76 : 77 : @override 78 1 : Future<void> delete(String key) async { 79 2 : final delete = box!.delete(key); // delete in bg 80 : // id will become orphan & purged 81 2 : graph.removeKey(key); 82 1 : await delete; 83 : } 84 : 85 : @override 86 1 : Future<void> clear() async { 87 3 : await box!.clear(); 88 : } 89 : 90 : // hive adapter 91 : 92 1 : @override 93 : int get typeId { 94 : // _adapter_hive:key: { 95 : // '_adapter_hive:posts': ['_adapter_hive:1'], 96 : // '_adapter_hive:comments': ['_adapter_hive:2'], 97 : // '_adapter_hive:houses': ['_adapter_hive:3'], 98 : // } 99 : 100 3 : if (!graph._hasNode(_hiveAdapterKey)) { 101 3 : graph._addNode(_hiveAdapterKey); 102 : } 103 : 104 3 : final _typesNode = graph._getNode(_hiveAdapterKey)!; 105 : 106 : final edge = 107 4 : _typesNode[StringUtils.namespace(_hiveAdapterNs, _internalType)]; 108 : 109 1 : if (edge != null && edge.isNotEmpty) { 110 : // first is of format: _adapter_hive:1 111 3 : return int.parse(edge.first.denamespace()); 112 : } 113 : 114 : // get namespaced indices 115 1 : final index = _typesNode.values 116 : // denamespace and parse single 117 5 : .map((e) => int.parse(e.first.denamespace())) 118 : // find max 119 2 : .fold(0, max) + 120 : 1; 121 : 122 3 : graph._addEdge(_hiveAdapterKey, 123 3 : StringUtils.namespace(_hiveAdapterNs, index.toString()), 124 3 : metadata: StringUtils.namespace(_hiveAdapterNs, _internalType)); 125 : return index; 126 : } 127 : 128 0 : @override 129 : T read(reader) { 130 : // read key first 131 0 : final key = reader.read().toString(); 132 : 133 : // read attributes (no relationships stored) 134 0 : final total = reader.readByte(); 135 0 : final map = <String, dynamic>{ 136 0 : for (var i = 0; i < total; i++) reader.read().toString(): reader.read(), 137 : }; 138 : 139 : // reconstruct relationship information from graph 140 0 : for (final entry in relationshipsFor().entries) { 141 : // entry keys are the name of relationships => metadata 142 0 : final name = entry.key; 143 0 : final relKeys = graph._getEdge(key, metadata: name); 144 0 : map[name] = 145 0 : entry.value['kind'] == 'BelongsTo' ? relKeys.safeFirst : relKeys; 146 : } 147 : 148 0 : return deserialize(map); 149 : } 150 : 151 0 : @override 152 : void write(writer, T obj) { 153 0 : final _map = serialize(obj); 154 : // write key first 155 0 : writer.write(obj._key); 156 : 157 : // exclude relationships 158 0 : final keys = _map.keys.where((k) => !relationshipsFor().containsKey(k)); 159 0 : writer.writeByte(keys.length); 160 0 : for (final k in keys) { 161 0 : writer.write(k); 162 0 : writer.write(_map[k]); 163 : } 164 : } 165 : }