cake_wallet/lib/utils/mobx.dart

164 lines
4.2 KiB
Dart

import 'dart:async';
import 'package:hive/hive.dart';
import 'package:mobx/mobx.dart';
import 'package:cw_core/keyable.dart';
void connectMapToListWithTransform<T extends Keyable, Y extends Keyable>(
ObservableMap<dynamic, T> source,
ObservableList<Y> dest,
Y Function(T) transform,
{bool Function(T) filter}) {
source.observe((MapChange<dynamic, T> 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<T> = bool Function(T);
typedef Transform<T, Y> = Y Function(T);
enum ChangeType { update, delete, add }
class EntityChange<T extends Keyable> {
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<T extends Keyable> on Box<T> {
StreamSubscription<BoxEvent> bindToList(
ObservableList<T> dest, {
bool initialFire = false,
Filter<T> filter,
}) {
if (initialFire) {
final res = filter != null ? values.where(filter) : values;
dest.addAll(res);
}
return watch().listen((event) {
if (filter != null && event.value != null && !filter(event.value as T)) {
return;
}
dest.acceptBoxChange(event);
});
}
StreamSubscription<BoxEvent> bindToListWithTransform<Y extends Keyable>(
ObservableList<Y> dest,
Transform<T, Y> transform, {
bool initialFire = false,
Filter<T> filter,
}) {
if (initialFire) {
dest.addAll(values.map((value) => transform(value)));
}
return watch().listen((event) {
if (filter != null && event.value != null && !filter(event.value as T)) {
return;
}
dest.acceptBoxChange(event,
transformed: event.deleted ? null : transform(event.value as T));
});
}
}
extension HiveBindable<T extends Keyable> on ObservableList<T> {
Stream<EntityChange<T>> listen() {
// ignore: close_sinks
final controller = StreamController<EntityChange<T>>();
observe((ListChange<T> 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<EntityChange<T>> bindToList(ObservableList<T> dest) =>
listen().listen((event) => dest.acceptEntityChange(event));
void acceptBoxChange(BoxEvent event, {T transformed}) {
if (event.deleted) {
removeWhere((el) {
return el.keyIndex == event.key;
});
return;
}
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<T> event) {
if (event.type == ChangeType.delete) {
removeWhere((el) => el.keyIndex == event.key);
return;
}
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);
}
}
}
}