LCOV - code coverage report
Current view: top level - src/repository - remote_adapter_watch.dart (source / functions) Hit Total Coverage
Test: lcov.info Lines: 82 85 96.5 %
Date: 2020-07-30 22:52:57 Functions: 0 0 -

          Line data    Source code
       1             : part of flutter_data;
       2             : 
       3             : mixin _RemoteAdapterWatch<T extends DataModel<T>> on _RemoteAdapter<T> {
       4           0 :   @protected
       5             :   @visibleForTesting
       6             :   Duration get throttleDuration =>
       7             :       const Duration(milliseconds: 16); // 1 frame at 60fps
       8             : 
       9           1 :   @protected
      10             :   @visibleForTesting
      11             :   StateNotifier<List<DataGraphEvent>> get throttledGraph =>
      12           3 :       graph.throttle(throttleDuration);
      13             : 
      14           1 :   DataStateNotifier<List<T>> watchAll(
      15             :       {final bool remote,
      16             :       final Map<String, dynamic> params,
      17             :       final Map<String, String> headers}) {
      18           1 :     _assertInit();
      19           1 :     final _notifier = DataStateNotifier<List<T>>(
      20           2 :       DataState(localAdapter
      21           1 :           .findAll()
      22           3 :           .map((m) => initializeModel(m, save: true))
      23           1 :           .toList()),
      24           1 :       reload: (notifier) async {
      25             :         try {
      26           1 :           final _future = findAll(
      27             :               params: params, headers: headers, remote: remote, init: true);
      28           4 :           notifier.data = notifier.data.copyWith(isLoading: true);
      29           1 :           await _future;
      30             :         } catch (error, stackTrace) {
      31             :           // we're only interested in notifying errors
      32             :           // as models will pop up via the graph notifier
      33           4 :           notifier.data = notifier.data.copyWith(
      34             :             isLoading: false,
      35             :             exception: error,
      36             :             stackTrace: stackTrace,
      37             :           );
      38             :         }
      39             :       },
      40             :     );
      41             : 
      42             :     // kick off
      43           1 :     _notifier.reload();
      44             : 
      45           3 :     final _graphNotifier = throttledGraph.forEach((events) {
      46           1 :       if (!_notifier.mounted) {
      47             :         return;
      48             :       }
      49             : 
      50             :       // filter by keys (that are not IDs)
      51           2 :       final filteredEvents = events.where((event) =>
      52           2 :           event.type.isNode &&
      53           4 :           event.keys.first.startsWith(type) &&
      54           4 :           !event.graph._hasEdge(event.keys.first, metadata: 'key'));
      55             : 
      56           1 :       if (filteredEvents.isEmpty) {
      57             :         return;
      58             :       }
      59             : 
      60           3 :       final list = _notifier.data.model.toList();
      61             : 
      62           2 :       for (final event in filteredEvents) {
      63           2 :         final key = event.keys.first;
      64           0 :         assert(key != null);
      65           1 :         switch (event.type) {
      66           1 :           case DataGraphEventType.addNode:
      67           3 :             list.add(localAdapter.findOne(key));
      68             :             break;
      69           1 :           case DataGraphEventType.updateNode:
      70           4 :             final idx = list.indexWhere((model) => model?._key == key);
      71           3 :             list[idx] = localAdapter.findOne(key);
      72             :             break;
      73           1 :           case DataGraphEventType.removeNode:
      74           4 :             list..removeWhere((model) => model?._key == key);
      75             :             break;
      76             :           default:
      77             :         }
      78             :       }
      79             : 
      80           4 :       _notifier.data = _notifier.data.copyWith(model: list, isLoading: false);
      81             :     });
      82             : 
      83           2 :     _notifier.onDispose = _graphNotifier.dispose;
      84             :     return _notifier;
      85             :   }
      86             : 
      87           1 :   DataStateNotifier<T> watchOne(final dynamic model,
      88             :       {final bool remote,
      89             :       final Map<String, dynamic> params,
      90             :       final Map<String, String> headers,
      91             :       final AlsoWatch<T> alsoWatch}) {
      92           1 :     _assertInit();
      93           0 :     assert(model != null);
      94             : 
      95           2 :     final id = model is T ? model.id : model;
      96             : 
      97             :     // lazy key access
      98             :     String _key;
      99           1 :     String key() => _key ??=
     100           5 :         graph.getKeyForId(type, id) ?? (model is T ? model._key : null);
     101             : 
     102             :     final _alsoWatchFilters = <String>{};
     103             :     var _relatedKeys = <String>{};
     104             : 
     105           1 :     final _notifier = DataStateNotifier<T>(
     106           5 :       DataState(initializeModel(localAdapter.findOne(key()), save: true)),
     107           1 :       reload: (notifier) async {
     108             :         if (id == null) return;
     109             :         try {
     110           1 :           final _future = findOne(id,
     111             :               params: params, headers: headers, remote: remote, init: true);
     112           4 :           notifier.data = notifier.data.copyWith(isLoading: true);
     113           1 :           await _future;
     114             :         } catch (error, stackTrace) {
     115             :           // we're only interested in notifying errors
     116             :           // as models will pop up via the graph notifier
     117           4 :           notifier.data = notifier.data.copyWith(
     118             :             isLoading: false,
     119             :             exception: error,
     120             :             stackTrace: stackTrace,
     121             :           );
     122             :         }
     123             :       },
     124             :     );
     125             : 
     126           1 :     void _initializeRelationshipsToWatch(T model) {
     127             :       if (alsoWatch != null &&
     128           1 :           _alsoWatchFilters.isEmpty &&
     129             :           model != null &&
     130           1 :           model._isInitialized) {
     131           4 :         _alsoWatchFilters.addAll(alsoWatch(model).map((rel) {
     132           1 :           return rel?._name;
     133             :         }));
     134             :       }
     135             :     }
     136             : 
     137             :     // kick off
     138             : 
     139             :     // try to find relationships to watch
     140           3 :     _initializeRelationshipsToWatch(_notifier.data.model);
     141             : 
     142             :     // trigger local + async loading
     143           1 :     _notifier.reload();
     144             : 
     145             :     // start listening to graph for further changes
     146           3 :     final _graphNotifier = throttledGraph.forEach((events) {
     147           1 :       if (!_notifier.mounted) {
     148             :         return;
     149             :       }
     150             : 
     151             :       // buffers
     152           2 :       var modelBuffer = _notifier.data.model;
     153             :       var refresh = false;
     154             : 
     155           2 :       for (final event in events) {
     156           3 :         if (event.keys.containsFirst(key())) {
     157             :           // add/update
     158           2 :           if (event.type == DataGraphEventType.addNode ||
     159           2 :               event.type == DataGraphEventType.updateNode) {
     160           3 :             final model = localAdapter.findOne(key());
     161             :             if (model != null) {
     162           1 :               initializeModel(model, save: true);
     163           1 :               _initializeRelationshipsToWatch(model);
     164             :               modelBuffer = model;
     165             :             }
     166             :           }
     167             : 
     168             :           // remove
     169           2 :           if (event.type == DataGraphEventType.removeNode &&
     170           2 :               _notifier.data.model != null) {
     171             :             modelBuffer = null;
     172             :           }
     173             : 
     174             :           // changes on specific relationships of this model
     175           2 :           if (_notifier.data.model != null &&
     176           2 :               event.type.isEdge &&
     177           2 :               _alsoWatchFilters.contains(event.metadata)) {
     178             :             // calculate currently related models
     179           1 :             _relatedKeys = localAdapter
     180           3 :                 .relationshipsFor(_notifier.data.model)
     181           1 :                 .values
     182           4 :                 .map((meta) => (meta['instance'] as Relationship)?.keys)
     183           2 :                 .where((keys) => keys != null)
     184           2 :                 .expand((key) => key)
     185           1 :                 .toSet();
     186             : 
     187             :             refresh = true;
     188             :           }
     189             :         }
     190             : 
     191             :         // updates on all models of specific relationships of this model
     192           2 :         if (event.type == DataGraphEventType.updateNode &&
     193           3 :             _relatedKeys.any(event.keys.contains)) {
     194             :           refresh = true;
     195             :         }
     196             :       }
     197             : 
     198             :       // NOTE: because of this comparison, use field equality
     199             :       // rather than key equality (which wouldn't update)
     200           3 :       if (modelBuffer != _notifier.data.model || refresh) {
     201           2 :         _notifier.data = _notifier.data
     202           2 :             .copyWith(model: modelBuffer, isLoading: false, exception: null);
     203             :       }
     204             :     });
     205             : 
     206           2 :     _notifier.onDispose = _graphNotifier.dispose;
     207             :     return _notifier;
     208             :   }
     209             : }
     210             : 
     211             : typedef AlsoWatch<T> = List<Relationship> Function(T);

Generated by: LCOV version 1.14