import 'dart:async'; import 'package:hive/hive.dart'; import 'package:mobx/mobx.dart'; mixin Keyable { dynamic keyIndex; } void connectMapToListWithTransform( ObservableMap source, ObservableList dest, Y Function(T) transform, {bool Function(T) filter}) { source.observe((MapChange change) { switch (change.type) { case OperationType.add: if (filter?.call(change.newValue) ?? true) { dest.add(transform(change.newValue)); } break; case OperationType.remove: // Hive could has equal index and key dest.removeWhere((elem) => elem.keyIndex == (change.key ?? change.newValue.keyIndex)); break; case OperationType.update: for (var i = 0; i < dest.length; i++) { final item = dest[i]; if (item.keyIndex == change.key) { dest[i] = transform(change.newValue); } } break; } }); } typedef Filter = bool Function(T); typedef Transform = Y Function(T); enum ChangeType { update, delete, add } class EntityChange { EntityChange(this.value, this.type, {dynamic key}) : _key = key; dynamic get key => _key ?? value.keyIndex; final T value; final ChangeType type; final dynamic _key; } extension MobxBindable on Box { StreamSubscription bindToList( ObservableList dest, { bool initialFire = false, Filter filter, }) { if (initialFire) { dest.addAll(values); } return watch().listen((event) { if (filter != null && !filter(event.value as T)) { return; } dest.acceptBoxChange(event); }); } StreamSubscription bindToListWithTransform( ObservableList dest, Transform transform, { bool initialFire = false, Filter filter, }) { if (initialFire) { dest.addAll(values.map((value) => transform(value))); } return watch().listen((event) { if (filter != null && !filter(event.value as T)) { return; } dest.acceptBoxChange(event, transformed: event.deleted ? null : transform(event.value as T)); }); } } extension HiveBindable on ObservableList { Stream> listen() { // ignore: close_sinks final controller = StreamController>(); observe((ListChange change) { change.elementChanges.forEach((change) { ChangeType type; switch (change.type) { case OperationType.add: type = ChangeType.add; break; case OperationType.remove: type = ChangeType.delete; break; case OperationType.update: type = ChangeType.update; break; } final value = change.newValue as T; controller.add(EntityChange(value, type, key: type == ChangeType.delete ? change.index : value.keyIndex)); }); }); return controller.stream; } StreamSubscription> bindToList(ObservableList dest) => listen().listen((event) => dest.acceptEntityChange(event)); void acceptBoxChange(BoxEvent event, {T transformed}) { print('---------------------'); print('event.key: ${event.key}; event.deleted: ${event.deleted};'); if (event.deleted) { removeWhere((el) { print('el.keyIndex ${el.keyIndex}'); return el.keyIndex == event.key; }); } print('---------------------'); final dynamic value = transformed ?? event.value; if (value is T) { final index = indexWhere((el) => el.keyIndex == value.keyIndex); if (index > -1) { this.setAll(index, [value]); // FIXME: fixme } else { add(value); } } } void acceptEntityChange(EntityChange event) { if (event.type == ChangeType.delete) { removeWhere((el) => el.keyIndex == event.key); } final dynamic value = event.value; if (value is T) { final index = indexWhere((el) => el.keyIndex == value.keyIndex); if (index > -1) { this.setAll(index, [value]); // FIXME: fixme } else { add(value); } } } }