-
-
Notifications
You must be signed in to change notification settings - Fork 311
/
observable.dart
128 lines (105 loc) · 3.47 KB
/
observable.dart
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
part of '../core.dart';
class Observable<T> extends Atom
implements
Interceptable<T>,
Listenable<ChangeNotification<T>>,
ObservableValue<T> {
/// Create an observable value with an [initialValue] and an optional [name]
///
/// Observable values are tracked inside MobX. When a reaction uses them
/// they are implicitly added as a dependency of the reaction. When its value changes
/// the linked reaction is re-triggered.
///
/// An Observable's value is read with the `value` property.
///
/// It is possible to override equality comparison of new values with [equals].
///
/// ```
/// var x = Observable(10);
/// var message = Observable('hello');
///
/// print('x = ${x.value}'); // read an Observable's value
/// ```
factory Observable(T initialValue,
{String? name,
ReactiveContext? context,
EqualityComparer<T>? equals}) =>
Observable._(context ?? mainContext, initialValue,
name: name, equals: equals);
Observable._(ReactiveContext context, this._value,
{String? name, this.equals})
: _interceptors = Interceptors(context),
_listeners = Listeners(context),
super._(context, name: name ?? context.nameFor('Observable')) {
if (_context.isSpyEnabled) {
_context.spyReport(ObservableValueSpyEvent(this,
newValue: _value, name: this.name, isEnd: true));
}
}
final Interceptors<T> _interceptors;
final Listeners<ChangeNotification<T>> _listeners;
final EqualityComparer<T>? equals;
T _value;
@override
T get value {
_context.enforceReadPolicy(this);
reportObserved();
return _value;
}
set value(T value) {
_context.enforceWritePolicy(this);
final oldValue = _value;
final newValueDynamic = _prepareNewValue(value);
if (newValueDynamic == WillChangeNotification.unchanged) {
return;
}
final newValue = newValueDynamic as T;
final notifySpy = _context.isSpyEnabled;
if (notifySpy) {
_context.spyReport(ObservableValueSpyEvent(this,
newValue: newValue, oldValue: oldValue, name: name));
}
_value = newValue;
reportChanged();
if (_listeners.hasHandlers) {
final change = ChangeNotification<T>(
newValue: value,
oldValue: oldValue,
type: OperationType.update,
object: this);
_listeners.notifyListeners(change);
}
if (notifySpy) {
_context.spyReport(EndedSpyEvent(type: 'observable', name: name));
}
}
dynamic _prepareNewValue(T newValue) {
T? prepared = newValue;
if (_interceptors.hasHandlers) {
final change = _interceptors.interceptChange(WillChangeNotification(
newValue: prepared, type: OperationType.update, object: this));
if (change == null) {
return WillChangeNotification.unchanged;
}
prepared = change.newValue;
}
final areEqual =
equals == null ? prepared == value : equals!(prepared, _value);
return (!areEqual) ? prepared : WillChangeNotification.unchanged;
}
@override
Dispose observe(Listener<ChangeNotification<T>> listener,
{bool fireImmediately = false}) {
if (fireImmediately == true) {
listener(ChangeNotification<T>(
type: OperationType.update,
newValue: _value,
oldValue: null,
object: this));
}
return _listeners.add(listener);
}
@override
Dispose intercept(Interceptor<T> interceptor) =>
_interceptors.add(interceptor);
}