dev
dart 166 lines 4.25 KB
Raw
1 import 'dart:async';
2 import 'package:hive/hive.dart';
3 import 'package:mobx/mobx.dart';
4 import 'package:cw_core/keyable.dart';
5
6 void connectMapToListWithTransform<T extends Keyable, Y extends Keyable>(
7 ObservableMap<dynamic, T> source, ObservableList<Y> dest, Y Function(T?) transform,
8 {bool Function(T?)? filter}) {
9 source.observe((MapChange<dynamic, T> change) {
10 if (change.type == null) {
11 return;
12 }
13
14 switch (change.type) {
15 case OperationType.add:
16 if (filter?.call(change.newValue) ?? true) {
17 dest.add(transform(change.newValue));
18 }
19 break;
20 case OperationType.remove:
21 // Hive could has equal index and key
22 dest.removeWhere((elem) => elem.keyIndex == (change.key ?? change.newValue?.keyIndex));
23 break;
24 case OperationType.update:
25 for (var i = 0; i < dest.length; i++) {
26 final item = dest[i];
27
28 if (item.keyIndex == change.key) {
29 dest[i] = transform(change.newValue);
30 }
31 }
32 break;
33 default:
34 break;
35 }
36 });
37 }
38
39 typedef Filter<T> = bool Function(T);
40 typedef Transform<T, Y> = Y Function(T);
41
42 enum ChangeType { update, delete, add }
43
44 class EntityChange<T extends Keyable> {
45 EntityChange(this.value, this.type, {dynamic key}) : _key = key;
46
47 dynamic get key => _key ?? value.keyIndex;
48 final T value;
49 final ChangeType type;
50 final dynamic _key;
51 }
52
53 extension MobxBindable<T extends Keyable> on Box<T> {
54 StreamSubscription<BoxEvent> bindToList(
55 ObservableList<T> dest, {
56 bool initialFire = false,
57 Filter<T>? filter,
58 }) {
59 if (initialFire) {
60 final res = filter != null ? values.where(filter) : values;
61 dest.addAll(res);
62 }
63
64 return watch().listen((event) {
65 if (filter != null && event.value != null && !filter(event.value as T)) {
66 return;
67 }
68
69 dest.acceptBoxChange(event);
70 });
71 }
72
73 StreamSubscription<BoxEvent> bindToListWithTransform<Y extends Keyable>(
74 ObservableList<Y> dest,
75 Transform<T, Y> transform, {
76 bool initialFire = false,
77 Filter<T>? filter,
78 }) {
79 if (initialFire) {
80 dest.addAll(values.map((value) => transform(value)));
81 }
82
83 return watch().listen((event) {
84 if (filter != null && event.value != null && !filter(event.value as T)) {
85 return;
86 }
87
88 dest.acceptBoxChange(event, transformed: event.deleted ? null : transform(event.value as T));
89 });
90 }
91 }
92
93 extension HiveBindable<T extends Keyable> on ObservableList<T> {
94 Stream<EntityChange<T>> listen() {
95 // ignore: close_sinks
96 final controller = StreamController<EntityChange<T>>();
97
98 observe((ListChange<T> change) {
99 change.elementChanges?.forEach((change) {
100 ChangeType type;
101
102 switch (change.type) {
103 case OperationType.add:
104 type = ChangeType.add;
105 break;
106 case OperationType.remove:
107 type = ChangeType.delete;
108 break;
109 case OperationType.update:
110 type = ChangeType.update;
111 break;
112 }
113
114 final value = change.newValue as T;
115 controller.add(EntityChange(value, type,
116 key: type == ChangeType.delete ? change.index : value.keyIndex));
117 });
118 });
119
120 return controller.stream;
121 }
122
123 StreamSubscription<EntityChange<T>> bindToList(ObservableList<T> dest) =>
124 listen().listen((event) => dest.acceptEntityChange(event));
125
126 void acceptBoxChange(BoxEvent event, {T? transformed}) {
127 if (event.deleted) {
128 removeWhere((el) {
129 return el.keyIndex == event.key;
130 });
131
132 return;
133 }
134
135 final dynamic value = transformed ?? event.value;
136
137 if (value is T) {
138 final index = indexWhere((el) => el.keyIndex == value.keyIndex);
139
140 if (index > -1) {
141 this.setAll(index, [value]); // FIXME: fixme
142 } else {
143 add(value);
144 }
145 }
146 }
147
148 void acceptEntityChange(EntityChange<T> event) {
149 if (event.type == ChangeType.delete) {
150 removeWhere((el) => el.keyIndex == event.key);
151 return;
152 }
153
154 final dynamic value = event.value;
155
156 if (value is T) {
157 final index = indexWhere((el) => el.keyIndex == value.keyIndex);
158
159 if (index > -1) {
160 this.setAll(index, [value]); // FIXME: fixme
161 } else {
162 add(value);
163 }
164 }
165 }
166 }