From e7b88a97ea8f7db7b5fa2a151b2448f9fb7fcf3d Mon Sep 17 00:00:00 2001 From: Minsu Lee Date: Tue, 8 Mar 2022 15:47:00 +0900 Subject: [PATCH 1/2] feat: allow a custom equals parameter for ObservableStream Signed-off-by: Minsu Lee --- docs/docs/api/observable.mdx | 5 ++- flutter_mobx/lib/version.dart | 2 +- mobx/lib/src/api/async/observable_future.dart | 2 +- mobx/lib/src/api/async/observable_stream.dart | 38 ++++++++++++++----- mobx/pubspec.yaml | 2 +- mobx/test/observable_stream_test.dart | 18 +++++++++ mobx_codegen/lib/version.dart | 2 +- 7 files changed, 54 insertions(+), 15 deletions(-) diff --git a/docs/docs/api/observable.mdx b/docs/docs/api/observable.mdx index 721d3fa6a..cfd16dece 100644 --- a/docs/docs/api/observable.mdx +++ b/docs/docs/api/observable.mdx @@ -370,7 +370,8 @@ class LoadingIndicator extends StatelessWidget { ## ObservableStream -#### `ObservableStream(Stream stream, {T initialValue, bool cancelOnError, ReactiveContext context})` +#### `ObservableStream(Stream stream, {T initialValue, bool cancelOnError, ReactiveContext context, String? name, + EqualityComparer? equals})` - **`Stream stream`**: The stream that is tracked for `status` and `value` changes. @@ -380,6 +381,8 @@ class LoadingIndicator extends StatelessWidget { - **`ReactiveContext context`**: the context to which this observable-stream is bound. By default, all `ObservableStream`s are bound to the singleton `mainContext` of the application. +- **`String? name`**: This string is used as a debug name. +- **`EqualityComparer? equals`**: It acts as a comparison function for comparing the previous value with the next value. If this function considers the values to be equal, then the observers will not be re-evaluated. Similar to `ObservableFuture`, an **`ObservableStream`** provides a reactive wrapper around a `Stream`. This gives an easy way to observe and re-render diff --git a/flutter_mobx/lib/version.dart b/flutter_mobx/lib/version.dart index a76ba7192..792061d96 100644 --- a/flutter_mobx/lib/version.dart +++ b/flutter_mobx/lib/version.dart @@ -1,4 +1,4 @@ // Generated via set_version.dart. !!!DO NOT MODIFY BY HAND!!! /// The current version as per `pubspec.yaml`. -const version = '2.0.6+2'; +const version = '2.0.6+3'; diff --git a/mobx/lib/src/api/async/observable_future.dart b/mobx/lib/src/api/async/observable_future.dart index 8b80143ac..f3b5fe628 100644 --- a/mobx/lib/src/api/async/observable_future.dart +++ b/mobx/lib/src/api/async/observable_future.dart @@ -124,7 +124,7 @@ class ObservableFuture implements Future, ObservableValue { @override ObservableStream asStream() => ObservableStream._( - _context, _future.asStream(), value, false, '${name}_asStream'); + _context, _future.asStream(), value, false, '${name}_asStream', null); @override ObservableFuture catchError(Function onError, diff --git a/mobx/lib/src/api/async/observable_stream.dart b/mobx/lib/src/api/async/observable_stream.dart index 302b3e838..56d8c735c 100644 --- a/mobx/lib/src/api/async/observable_stream.dart +++ b/mobx/lib/src/api/async/observable_stream.dart @@ -36,17 +36,21 @@ class ObservableStream implements Stream, ObservableValue { /// /// If `cancelOnError` is `true`, the stream will be cancelled when an error /// event is emitted by the source stream. The default value is `false`. + /// + /// It is possible to override equality comparison of new values with [equals]. ObservableStream(Stream stream, {T? initialValue, bool cancelOnError = false, ReactiveContext? context, - String? name}) - : this._( - context ?? mainContext, stream, initialValue, cancelOnError, name); + String? name, + EqualityComparer? equals}) + : this._(context ?? mainContext, stream, initialValue, cancelOnError, + name, equals); ObservableStream._(ReactiveContext context, this._stream, this._initialValue, - this._cancelOnError, String? name) - : _context = context { + this._cancelOnError, String? name, EqualityComparer? equals) + : _context = context, + _equals = equals { _name = name ?? _context.nameFor('ObservableStream<$T>'); } @@ -56,14 +60,20 @@ class ObservableStream implements Stream, ObservableValue { final Stream _stream; late String _name; + String get name => _name; + final EqualityComparer? _equals; + _ObservableStreamController? _controllerField; + _ObservableStreamController get _controller { if (_controllerField == null) { _controllerField = _ObservableStreamController( _context, _stream, _initialValue, - cancelOnError: _cancelOnError, name: '$name.StreamController'); + cancelOnError: _cancelOnError, + name: '$name.StreamController', + equals: _equals); _initialValue = null; } return _controllerField!; @@ -123,10 +133,12 @@ class ObservableStream implements Stream, ObservableValue { /// Create a new stream with the provided initialValue and cancelOnError. ObservableStream configure( {T? initialValue, bool cancelOnError = false}) => - ObservableStream._(_context, _stream, initialValue, cancelOnError, name); + ObservableStream._( + _context, _stream, initialValue, cancelOnError, name, _equals); - ObservableStream _wrap(Stream stream) => - ObservableStream._(_context, stream, null, _cancelOnError, name); + ObservableStream _wrap(Stream stream, + {EqualityComparer? equals}) => + ObservableStream._(_context, stream, null, _cancelOnError, name, _equals ?? equals); ObservableFuture _wrapFuture(Future future) => ObservableFuture._(_context, future, FutureStatus.pending, null, name); @@ -323,6 +335,7 @@ class _ObservableStreamController { T? initialValue, { required this.cancelOnError, required this.name, + EqualityComparer? equals, }) : _initialStreamValue = origStream.isBroadcast ? null : initialValue, _actions = ActionController(context: context, name: '$name.ActionController'), @@ -332,7 +345,8 @@ class _ObservableStreamController { name: '$name.status'), _valueType = Observable(_ValueType.value, context: context, name: '$name.valueType'), - _data = Observable(initialValue, context: context, name: '$name.data') { + _data = Observable(initialValue, + context: context, name: '$name.data', equals: equals) { _status ..onBecomeObserved(_listen) ..onBecomeUnobserved(_unsubscribe); @@ -366,12 +380,15 @@ class _ObservableStreamController { final ActionController _actions; final Observable<_ValueType> _valueType; + _ValueType get valueType => _valueType.value; final Observable _data; + dynamic get data => _data.value; final Observable _status; + StreamStatus get status => _status.value; late final Stream stream = _controller.stream; @@ -379,6 +396,7 @@ class _ObservableStreamController { int _listenCount = 0; bool _isCancelled = false; + bool get isCancelled => _isCancelled; Future _onCancel() async { diff --git a/mobx/pubspec.yaml b/mobx/pubspec.yaml index 2db5344b7..3b85993d9 100644 --- a/mobx/pubspec.yaml +++ b/mobx/pubspec.yaml @@ -1,5 +1,5 @@ name: mobx -version: 2.1.0 +version: 2.1.1 description: "MobX is a library for reactively managing the state of your applications. Use the power of observables, actions, and reactions to supercharge your Dart and Flutter apps." homepage: https://github.com/mobxjs/mobx.dart diff --git a/mobx/test/observable_stream_test.dart b/mobx/test/observable_stream_test.dart index 5b825f936..7b72f574f 100644 --- a/mobx/test/observable_stream_test.dart +++ b/mobx/test/observable_stream_test.dart @@ -600,6 +600,24 @@ void main() { reason: 'sub on non-broadcast stream already cancelled'); }); + test('can observe value with custom equals', () async { + final ctrl = StreamController(); + final stream = ObservableStream(ctrl.stream, + initialValue: 3, equals: (_, __) => false); + + final subValues = []; + final sub = stream.listen(subValues.add); + + ctrl.add(3); + ctrl.add(3); + ctrl.add(3); + await pumpEventQueue(); + expect(stream.value, equals(3), reason: 'with subs, with updates again'); + expect(subValues, equals([3, 3, 3, 3])); + + await sub.cancel(); + }); + { 'asBroadcastStream': (s) { final stream = s.asBroadcastStream(); diff --git a/mobx_codegen/lib/version.dart b/mobx_codegen/lib/version.dart index c86b66b2a..9524758d7 100644 --- a/mobx_codegen/lib/version.dart +++ b/mobx_codegen/lib/version.dart @@ -1,4 +1,4 @@ // Generated via set_version.dart. !!!DO NOT MODIFY BY HAND!!! /// The current version as per `pubspec.yaml`. -const version = '2.0.7+1'; +const version = '2.0.7+2'; From 28d90cf5885da93aeb4aee32a546bdb58bf87070 Mon Sep 17 00:00:00 2001 From: Minsu Lee Date: Mon, 19 Sep 2022 10:30:16 +0900 Subject: [PATCH 2/2] feat: allow a custom equals parameter for ObservableStream Signed-off-by: Minsu Lee --- mobx/CHANGELOG.md | 4 ++++ mobx/lib/version.dart | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/mobx/CHANGELOG.md b/mobx/CHANGELOG.md index 9bb93e167..e195b9802 100644 --- a/mobx/CHANGELOG.md +++ b/mobx/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.1.1 + +- Allow a custom equals parameter for ObservableStream - [@amondnet](https://github.com/amondnet) + ## 2.1.0 - `ObservableSet` now uses `Set` to maintain order of iteration diff --git a/mobx/lib/version.dart b/mobx/lib/version.dart index 53a13d259..416e1234e 100644 --- a/mobx/lib/version.dart +++ b/mobx/lib/version.dart @@ -1,4 +1,4 @@ // Generated via set_version.dart. !!!DO NOT MODIFY BY HAND!!! /// The current version as per `pubspec.yaml`. -const version = '2.1.0'; +const version = '2.1.1';