@@ -186,48 +186,59 @@ class ParseLiveList<T extends ParseObject> {
186186 .getClientEventStream
187187 .listen ((LiveQueryClientEvent event) async {
188188 if (event == LiveQueryClientEvent .CONNECTED ) {
189- final ParseResponse parseResponse = await _runQuery ();
190- if (parseResponse.success) {
191- final List <T > newList = parseResponse.results ?? List <T >();
192-
193- //update List
194- for (int i = 0 ; i < _list.length; i++ ) {
195- final ParseObject currentObject = _list[i].object;
196- final String currentObjectId =
197- currentObject.get <String >(keyVarObjectId);
198-
199- bool stillInList = false ;
200-
201- for (int j = 0 ; j < newList.length; j++ ) {
202- if (newList[j].get <String >(keyVarObjectId) == currentObjectId) {
203- stillInList = true ;
204- if (newList[j]
205- .get <DateTime >(keyVarUpdatedAt)
206- .isAfter (currentObject.get <DateTime >(keyVarUpdatedAt))) {
207- final QueryBuilder <T > queryBuilder =
208- QueryBuilder <T >.copy (_query)
209- ..whereEqualTo (keyVarObjectId, currentObjectId);
210- queryBuilder.query <T >().then ((ParseResponse result) {
211- if (result.success && result.results != null ) {
212- _objectUpdated (result.results.first);
213- }
214- });
189+ _updateQueue.whenComplete (() async {
190+ List <Future <void >> tasks = < Future <void >> [];
191+ final ParseResponse parseResponse = await _runQuery ();
192+ if (parseResponse.success) {
193+ final List <T > newList = parseResponse.results ?? List <T >();
194+
195+ //update List
196+ for (int i = 0 ; i < _list.length; i++ ) {
197+ final ParseObject currentObject = _list[i].object;
198+ final String currentObjectId =
199+ currentObject.get <String >(keyVarObjectId);
200+
201+ bool stillInList = false ;
202+
203+ for (int j = 0 ; j < newList.length; j++ ) {
204+ if (newList[j].get <String >(keyVarObjectId) == currentObjectId) {
205+ stillInList = true ;
206+ if (newList[j]
207+ .get <DateTime >(keyVarUpdatedAt)
208+ .isAfter (currentObject.get <DateTime >(keyVarUpdatedAt))) {
209+ final QueryBuilder <T > queryBuilder =
210+ QueryBuilder <T >.copy (_query)
211+ ..whereEqualTo (keyVarObjectId, currentObjectId);
212+ tasks.add (queryBuilder
213+ .query <T >()
214+ .then ((ParseResponse result) async {
215+ if (result.success && result.results != null ) {
216+ await _objectUpdated (result.results.first);
217+ }
218+ }));
219+ }
220+ newList.removeAt (j);
221+ j-- ;
222+ break ;
215223 }
216- newList.removeAt (j);
217- j-- ;
218- break ;
224+ }
225+ if (! stillInList) {
226+ _objectDeleted (currentObject);
227+ i-- ;
219228 }
220229 }
221- if ( ! stillInList) {
222- _objectDeleted (currentObject);
223- i -- ;
230+
231+ for ( int i = 0 ; i < newList.length; i ++ ) {
232+ tasks. add ( _objectAdded (newList[i], loaded : false )) ;
224233 }
225234 }
226-
227- for (int i = 0 ; i < newList.length; i++ ) {
228- _objectAdded (newList[i], loaded: false );
235+ await Future .wait (tasks);
236+ tasks = < Future <void >> [];
237+ for (ParseLiveListElement <T > element in _list) {
238+ tasks.add (element.reconnected ());
229239 }
230- }
240+ await Future .wait (tasks);
241+ });
231242 }
232243 });
233244 }
@@ -351,7 +362,7 @@ class ParseLiveList<T extends ParseObject> {
351362 _list.removeAt (i).dispose ();
352363 _eventStreamController.sink.add (ParseLiveListDeleteEvent <T >(
353364 i, object? .clone (object? .toJson (full: true ))));
354- _objectAdded (object? .clone (object? .toJson (full: true )),
365+ await _objectAdded (object? .clone (object? .toJson (full: true )),
355366 fetchedIncludes: true );
356367 }
357368 break ;
@@ -450,7 +461,7 @@ class ParseLiveListElement<T extends ParseObject> {
450461 final StreamController <T > _streamController = StreamController <T >.broadcast ();
451462 T _object;
452463 bool _loaded = false ;
453- Map <MapEntry < String , Subscription < ParseObject >> , dynamic > _updatedSubItems;
464+ Map <PathKey , dynamic > _updatedSubItems;
454465 LiveQuery _liveQuery;
455466 final Future <void > _subscriptionQueue = Future <void >.value ();
456467
@@ -459,21 +470,17 @@ class ParseLiveListElement<T extends ParseObject> {
459470 // ignore: invalid_use_of_protected_member
460471 T get object => _object? .clone (_object? .toJson (full: true ));
461472
462- Map <MapEntry <String , Subscription <ParseObject >>, dynamic > _toSubscriptionMap (
463- Map <String , dynamic > map) {
464- Map <MapEntry <String , Subscription <ParseObject >>, dynamic > result =
465- Map <MapEntry <String , Subscription <ParseObject >>, dynamic >();
473+ Map <PathKey , dynamic > _toSubscriptionMap (Map <String , dynamic > map) {
474+ final Map <PathKey , dynamic > result = Map <PathKey , dynamic >();
466475 for (String key in map.keys) {
467- result.putIfAbsent (MapEntry <String , Subscription <ParseObject >>(key, null ),
468- () => _toSubscriptionMap (map[key]));
476+ result.putIfAbsent (PathKey (key), () => _toSubscriptionMap (map[key]));
469477 }
470478 return result;
471479 }
472480
473- Map <String , dynamic > _toKeyMap (
474- Map <MapEntry <String , Subscription <ParseObject >>, dynamic > map) {
481+ Map <String , dynamic > _toKeyMap (Map <PathKey , dynamic > map) {
475482 final Map <String , dynamic > result = Map <String , dynamic >();
476- for (MapEntry < String , Subscription < ParseObject >> key in map.keys) {
483+ for (PathKey key in map.keys) {
477484 result.putIfAbsent (key.key, () => _toKeyMap (map[key]));
478485 }
479486 return result;
@@ -483,8 +490,7 @@ class ParseLiveListElement<T extends ParseObject> {
483490 _subscriptionQueue.whenComplete (() async {
484491 if (_updatedSubItems.isNotEmpty && _object != null ) {
485492 final List <Future <void >> tasks = < Future <void >> [];
486- for (MapEntry <String , Subscription <ParseObject >> key
487- in _updatedSubItems.keys) {
493+ for (PathKey key in _updatedSubItems.keys) {
488494 tasks.add (_subscribeSubItem (_object, key,
489495 _object.get <ParseObject >(key.key), _updatedSubItems[key]));
490496 }
@@ -493,24 +499,21 @@ class ParseLiveListElement<T extends ParseObject> {
493499 });
494500 }
495501
496- void _unsubscribe (
497- Map < MapEntry < String , Subscription < ParseObject >>, dynamic > subscriptions) {
498- for ( MapEntry < String , Subscription < ParseObject >> key
499- in subscriptions.keys) {
500- if (_liveQuery != null && key.value ! = null )
501- _liveQuery.client. unSubscribe (key.value);
502+ void _unsubscribe (Map < PathKey , dynamic > subscriptions) {
503+ for ( PathKey key in subscriptions.keys ) {
504+ if (_liveQuery != null && key.subscription != null ) {
505+ _liveQuery.client. unSubscribe (key.subscription);
506+ key.subscription = null ;
507+ }
502508 _unsubscribe (subscriptions[key]);
503509 }
504510 }
505511
506- Future <void > _subscribeSubItem (
507- ParseObject parentObject,
508- MapEntry <String , Subscription <ParseObject >> currentKey,
509- ParseObject subObject,
510- Map <MapEntry <String , Subscription <ParseObject >>, dynamic > path) async {
512+ Future <void > _subscribeSubItem (ParseObject parentObject, PathKey currentKey,
513+ ParseObject subObject, Map <PathKey , dynamic > path) async {
511514 if (_liveQuery != null && subObject != null ) {
512515 final List <Future <void >> tasks = < Future <void >> [];
513- for (MapEntry < String , Subscription < ParseObject >> key in path.keys) {
516+ for (PathKey key in path.keys) {
514517 tasks.add (_subscribeSubItem (
515518 subObject, key, subObject.get <ParseObject >(key.key), path[key]));
516519 }
@@ -521,6 +524,7 @@ class ParseLiveListElement<T extends ParseObject> {
521524 tasks.add (_liveQuery.client
522525 .subscribe (queryBuilder)
523526 .then ((Subscription <ParseObject > subscription) {
527+ currentKey.subscription = subscription;
524528 subscription.on (LiveQueryEvent .update, (ParseObject newObject) async {
525529 _subscriptionQueue.whenComplete (() async {
526530 await ParseLiveList ._loadIncludes (newObject,
@@ -530,11 +534,11 @@ class ParseLiveListElement<T extends ParseObject> {
530534 _streamController
531535 ? .add (_object? .clone (_object? .toJson (full: true )));
532536 //Resubscribe subitems
537+ // TODO(any): only resubscribe on changed pointers
533538 _unsubscribe (path);
534- for (MapEntry <String , Subscription <ParseObject >> key
535- in path.keys) {
536- tasks.add (_subscribeSubItem (subObject, key,
537- subObject.get <ParseObject >(key.key), path[key]));
539+ for (PathKey key in path.keys) {
540+ tasks.add (_subscribeSubItem (newObject, key,
541+ newObject.get <ParseObject >(key.key), path[key]));
538542 }
539543 }
540544 await Future .wait (tasks);
@@ -560,6 +564,70 @@ class ParseLiveListElement<T extends ParseObject> {
560564 _unsubscribe (_updatedSubItems);
561565 _streamController.close ();
562566 }
567+
568+ Future <void > reconnected () async {
569+ if (loaded) {
570+ _subscriptionQueue.whenComplete (() async {
571+ await _updateSubItems (_object, _updatedSubItems);
572+ // _streamController?.add(_object?.clone(_object?.toJson(full: true)));
573+ });
574+ }
575+ }
576+
577+ List <String > _getIncludeList (Map <PathKey , dynamic > path) {
578+ final List <String > includes = < String > [];
579+ for (PathKey key in path.keys) {
580+ includes.add (key.key);
581+ includes.addAll (
582+ _getIncludeList (path[key]).map ((String e) => '${key .key }.$e ' ));
583+ }
584+ return includes;
585+ }
586+
587+ Future <void > _updateSubItems (
588+ ParseObject root, Map <PathKey , dynamic > path) async {
589+ final List <Future <void >> tasks = < Future <void >> [];
590+ for (PathKey key in path.keys) {
591+ ParseObject subObject = root.get <ParseObject >(key.key);
592+ if (subObject? .containsKey (keyVarUpdatedAt) == true ) {
593+ final QueryBuilder <ParseObject > queryBuilder =
594+ QueryBuilder <ParseObject >(subObject)
595+ ..keysToReturn ([keyVarUpdatedAt])
596+ ..whereEqualTo (keyVarObjectId, subObject.objectId);
597+ ParseResponse parseResponse = await queryBuilder.query ();
598+ if (parseResponse.success &&
599+ (parseResponse.results.first as ParseObject ).updatedAt !=
600+ subObject.updatedAt) {
601+ queryBuilder.limiters.remove ("keys" );
602+ queryBuilder.includeObject (_getIncludeList (path[key]));
603+ ParseResponse parseResponse = await queryBuilder.query ();
604+ if (parseResponse.success) {
605+ subObject = parseResponse.result.first;
606+ // root.getObjectData()[key.key] = subObject;
607+ if (key.subscription? .eventCallbacks? .containsKey ("update" ) ==
608+ true ) {
609+ key.subscription.eventCallbacks["update" ](subObject);
610+ }
611+ // key.subscription.eventCallbacks["update"](subObject);
612+ break ;
613+ }
614+ }
615+ }
616+ tasks.add (_updateSubItems (subObject, path[key]));
617+ }
618+ await Future .wait (tasks);
619+ }
620+ }
621+
622+ class PathKey {
623+ PathKey (this .key, {this .subscription});
624+
625+ final String key;
626+ Subscription <ParseObject > subscription;
627+ @override
628+ String toString () {
629+ return 'PathKey(key: $key , subscription: ${subscription ?.requestId })' ;
630+ }
563631}
564632
565633abstract class ParseLiveListEvent <T extends ParseObject > {
0 commit comments