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(Reader read) 8 2 : : _hiveLocalStorage = read(hiveLocalStorageProvider), 9 1 : super(read); 10 : 11 : final HiveLocalStorage _hiveLocalStorage; 12 : 13 : final _hiveAdapterNs = '_adapter_hive'; 14 3 : String get _hiveAdapterKey => 'key'.namespaceWith(_hiveAdapterNs); 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 4 : if (!_hiveLocalStorage.hive.isBoxOpen(_internalType)) { 27 4 : if (!_hiveLocalStorage.hive.isAdapterRegistered(typeId)) { 28 0 : _hiveLocalStorage.hive.registerAdapter(this); 29 : } 30 2 : if (_hiveLocalStorage.clear) { 31 0 : await _hiveLocalStorage.deleteBox(_internalType); 32 : } 33 : } 34 : 35 : try { 36 5 : box = await _hiveLocalStorage.openBox<T>(_internalType); 37 : } catch (e, stackTrace) { 38 0 : print('[flutter_data] Box failed to open:\n$stackTrace'); 39 : } 40 : 41 : return this; 42 : } 43 : 44 1 : @override 45 2 : bool get isInitialized => box?.isOpen ?? false; 46 : 47 1 : @override 48 : void dispose() { 49 2 : box?.close(); 50 : } 51 : 52 : // protected API 53 : 54 1 : @override 55 : List<T>? findAll() { 56 1 : if (_isLocalStorageTouched) { 57 3 : return box!.values.toImmutableList(); 58 : } 59 : return null; 60 : } 61 : 62 1 : @override 63 2 : T? findOne(String? key) => box!.get(key); 64 : 65 : @override 66 1 : Future<T> save(String key, T model, {bool notify = true}) async { 67 1 : _touchLocalStorage(); 68 : 69 2 : final keyExisted = box!.containsKey(key); 70 2 : final save = box!.put(key, model); 71 : if (notify) { 72 2 : graph._notify( 73 1 : [key], 74 : type: keyExisted 75 : ? DataGraphEventType.updateNode 76 : : DataGraphEventType.addNode, 77 : ); 78 : } 79 : 80 1 : await save; 81 : return model; 82 : } 83 : 84 : @override 85 1 : Future<void> delete(String key) async { 86 2 : final delete = box!.delete(key); // delete in bg 87 : // id will become orphan & purged 88 2 : graph.removeKey(key); 89 1 : await delete; 90 : } 91 : 92 : @override 93 1 : Future<void> clear() async { 94 3 : await box!.clear(); 95 : } 96 : 97 : // Touching local storage means the box has received data; 98 : // this is used to know whether `findAll` should return 99 : // null, or its models (possibly empty) 100 : 101 : // _boxMetadata: { 102 : // '_boxMetadata:touched': ['_'], 103 : // } 104 : 105 1 : @override 106 : bool get _isLocalStorageTouched { 107 2 : return graph._hasEdge('_boxMetadata', metadata: '_boxMetadata:touched'); 108 : } 109 : 110 1 : @override 111 : void _touchLocalStorage() { 112 2 : graph._addEdge('_boxMetadata', '_', 113 : metadata: '_boxMetadata:touched', addNode: true, notify: false); 114 : } 115 : 116 : // hive adapter 117 : 118 1 : @override 119 : int get typeId { 120 : // _adapter_hive:key: { 121 : // '_adapter_hive:posts': ['_adapter_hive:1'], 122 : // '_adapter_hive:comments': ['_adapter_hive:2'], 123 : // '_adapter_hive:houses': ['_adapter_hive:3'], 124 : // } 125 : 126 : final _typesNode = 127 3 : graph._getNode(_hiveAdapterKey, orAdd: true, notify: false)!; 128 : 129 4 : final edge = _typesNode[_internalType.namespaceWith(_hiveAdapterNs)]; 130 : 131 1 : if (edge != null && edge.isNotEmpty) { 132 : // first is of format: _adapter_hive:1 133 1 : return int.parse(edge.first.denamespace()); 134 : } 135 : 136 : // get namespaced indices 137 1 : final index = _typesNode.values 138 : // denamespace and parse single 139 5 : .map((e) => int.parse(e.first.denamespace())) 140 : // find max 141 2 : .fold(0, math.max) + 142 : 1; 143 : 144 2 : graph._addEdge( 145 4 : _hiveAdapterKey, index.toString().namespaceWith(_hiveAdapterNs), 146 3 : metadata: _internalType.namespaceWith(_hiveAdapterNs), notify: false); 147 : return index; 148 : } 149 : 150 0 : @override 151 : T read(reader) { 152 : // read attributes (no relationships stored) 153 0 : final total = reader.readByte(); 154 0 : final map = <String, dynamic>{ 155 0 : for (var i = 0; i < total; i++) reader.read().toString(): reader.read(), 156 : }; 157 : 158 0 : final model = deserialize(map); 159 : return model; 160 : } 161 : 162 0 : @override 163 : void write(writer, T obj) { 164 0 : final _map = serialize(obj, withRelationships: false); 165 : 166 0 : final keys = _map.keys; 167 0 : writer.writeByte(keys.length); 168 0 : for (final k in keys) { 169 0 : writer.write(k); 170 0 : writer.write(_map[k]); 171 : } 172 : } 173 : }