LCOV - code coverage report
Current view: top level - graph - notifier_extension.dart (source / functions) Hit Total Coverage
Test: lcov.info Lines: 31 50 62.0 %
Date: 2022-05-06 22:54:19 Functions: 0 0 -

          Line data    Source code
       1             : library notifier_extension;
       2             : 
       3             : import 'dart:async';
       4             : 
       5             : import 'package:state_notifier/state_notifier.dart';
       6             : 
       7             : class DelayedStateNotifier<T> extends StateNotifier<T?> {
       8           2 :   DelayedStateNotifier() : super(null);
       9             : 
      10           1 :   @override
      11           1 :   set state(T? value) => super.state = value;
      12             : 
      13           1 :   @override
      14             :   RemoveListener addListener(void Function(T) listener,
      15             :       {bool fireImmediately = true}) {
      16           1 :     final _listener = (T? value) {
      17             :       // if `value` is `null` and `T` is actually a nullable
      18             :       // type, then the listener MUST be called with `null`
      19           1 :       if (_typesEqual<T, T?>() && value == null) {
      20           1 :         listener(null as T);
      21             :       } else {
      22             :         // if `value != null` and `T` is non-nullable, also
      23           1 :         listener(value!);
      24             :       }
      25             :     };
      26           1 :     return super.addListener(_listener, fireImmediately: false);
      27             :   }
      28             : 
      29             :   Function? onDispose;
      30             : 
      31           1 :   @override
      32             :   void dispose() {
      33           1 :     super.dispose();
      34           1 :     onDispose?.call();
      35             :   }
      36             : 
      37           2 :   bool _typesEqual<T1, T2>() => T1 == T2;
      38             : }
      39             : 
      40             : class _FunctionalStateNotifier<S, T> extends DelayedStateNotifier<T> {
      41             :   final DelayedStateNotifier<S> _source;
      42             :   final String? name;
      43             :   late RemoveListener _sourceDisposeFn;
      44             :   Timer? _timer;
      45             : 
      46           1 :   _FunctionalStateNotifier(this._source, {this.name});
      47             : 
      48           1 :   DelayedStateNotifier<T> where(bool Function(S) test) {
      49           4 :     _sourceDisposeFn = _source.addListener((_state) {
      50           1 :       if (test(_state)) {
      51           1 :         state = _state as T;
      52             :       }
      53             :     }, fireImmediately: false);
      54             :     return this;
      55             :   }
      56             : 
      57           1 :   DelayedStateNotifier<T> map(T Function(S) convert) {
      58           4 :     _sourceDisposeFn = _source.addListener((state) {
      59           2 :       super.state = convert(state);
      60             :     }, fireImmediately: false);
      61             :     return this;
      62             :   }
      63             : 
      64             :   final _bufferedState = <S>[];
      65             : 
      66           0 :   DelayedStateNotifier<T> throttle(Duration Function() durationFn) {
      67           0 :     _timer = _makeTimer(durationFn);
      68           0 :     _sourceDisposeFn = _source.addListener((model) {
      69           0 :       _bufferedState.add(model);
      70             :     }, fireImmediately: false);
      71             :     return this;
      72             :   }
      73             : 
      74           0 :   Timer _makeTimer(Duration Function() durationFn) {
      75           0 :     return Timer(durationFn(), () {
      76           0 :       if (mounted) {
      77           0 :         if (_bufferedState.isNotEmpty) {
      78             :           // Cloning the bufferedState list to force
      79             :           // calling listeners as workaround (need to figure out
      80             :           // where they are previously updated and why
      81             :           // super.state == _bufferedState -- and thus no update)
      82           0 :           super.state = [..._bufferedState] as T; // since T == List<S>;
      83           0 :           _bufferedState.clear(); // clear buffer
      84             :         }
      85           0 :         _timer = _makeTimer(durationFn); // reset timer
      86             :       }
      87             :     });
      88             :   }
      89             : 
      90           1 :   @override
      91             :   RemoveListener addListener(
      92             :     Listener<T> listener, {
      93             :     bool fireImmediately = true,
      94             :   }) {
      95             :     final dispose =
      96           1 :         super.addListener(listener, fireImmediately: fireImmediately);
      97           1 :     return () {
      98           1 :       dispose.call();
      99           1 :       _timer?.cancel();
     100           2 :       _sourceDisposeFn.call();
     101             :     };
     102             :   }
     103             : 
     104           0 :   @override
     105             :   void dispose() {
     106           0 :     if (mounted) {
     107           0 :       super.dispose();
     108             :     }
     109           0 :     _source.dispose();
     110           0 :     _timer?.cancel();
     111             :   }
     112             : }
     113             : 
     114             : /// Functional utilities for [StateNotifier]
     115             : extension StateNotifierX<T> on DelayedStateNotifier<T> {
     116             :   /// Filters incoming events by [test]
     117           1 :   DelayedStateNotifier<T> where(bool Function(T) test) {
     118           2 :     return _FunctionalStateNotifier<T, T>(this, name: 'where').where(test);
     119             :   }
     120             : 
     121             :   /// Maps events of type [T] onto events of type [R] via [convert]
     122           1 :   DelayedStateNotifier<R> map<R>(R Function(T) convert) {
     123           2 :     return _FunctionalStateNotifier<T, R>(this, name: 'map').map(convert);
     124             :   }
     125             : 
     126             :   /// Buffers all incoming [T] events for a duration obtained via
     127             :   /// [durationFn] and emits them as a [List<T>] (unless there were none)
     128           0 :   DelayedStateNotifier<List<T>> throttle(Duration Function() durationFn) {
     129           0 :     return _FunctionalStateNotifier<T, List<T>>(this, name: 'throttle')
     130           0 :         .throttle(durationFn);
     131             :   }
     132             : }

Generated by: LCOV version 1.15