From d5223d13c81639d2b09b39618288ec4ee4f91a94 Mon Sep 17 00:00:00 2001 From: Maximilian Fischer Date: Wed, 9 Jun 2021 12:25:15 +0200 Subject: [PATCH 01/18] make object in QueryBuilder not nullable We might have to discuss, wether we consider this to be a breaking change compared to 3.0.0. I have named the new version 3.0.1 for now. I think this should be fine. This change makes sense, as setting object to null (possible with the prior default constructor) would brake the whole QueryBuilder. --- .../lib/src/network/parse_live_query.dart | 2 +- .../dart/lib/src/network/parse_query.dart | 36 ++++++++++--------- .../dart/lib/src/utils/parse_live_list.dart | 4 +-- packages/dart/pubspec.yaml | 3 +- packages/flutter/pubspec.yaml | 4 +-- 5 files changed, 26 insertions(+), 23 deletions(-) diff --git a/packages/dart/lib/src/network/parse_live_query.dart b/packages/dart/lib/src/network/parse_live_query.dart index 16389586a..1c83c1bb5 100644 --- a/packages/dart/lib/src/network/parse_live_query.dart +++ b/packages/dart/lib/src/network/parse_live_query.dart @@ -347,7 +347,7 @@ class LiveQueryClient { 'op': 'subscribe', 'requestId': subscription.requestId, 'query': { - 'className': query.object!.parseClassName, + 'className': query.object.parseClassName, 'where': _whereMap, if (keysToReturn != null && keysToReturn.isNotEmpty) 'fields': keysToReturn diff --git a/packages/dart/lib/src/network/parse_query.dart b/packages/dart/lib/src/network/parse_query.dart index 627759a35..000bd64fd 100644 --- a/packages/dart/lib/src/network/parse_query.dart +++ b/packages/dart/lib/src/network/parse_query.dart @@ -5,8 +5,9 @@ class QueryBuilder { /// Class to create complex queries QueryBuilder(this.object) : super(); - QueryBuilder.name(String classname) - : this(ParseCoreData.instance.createObject(classname) as T?); + factory QueryBuilder.name(String classname) { + return QueryBuilder(ParseCoreData.instance.createObject(classname) as T); + } QueryBuilder.or(this.object, List> list) { String query = '"\$or":['; @@ -20,20 +21,21 @@ class QueryBuilder { queries.add(MapEntry(_NO_OPERATOR_NEEDED, query)); } - QueryBuilder.copy(QueryBuilder query) { - object = query.object; - queries = query.queries + factory QueryBuilder.copy(QueryBuilder query) { + QueryBuilder copy = QueryBuilder(query.object); + copy.queries = query.queries .map((MapEntry entry) => MapEntry(entry.key, entry.value.toString())) .toList(); query.limiters.forEach((String key, dynamic value) => - limiters.putIfAbsent(key, () => value.toString())); + copy.limiters.putIfAbsent(key, () => value.toString())); + return copy; } static const String _NO_OPERATOR_NEEDED = 'NO_OP'; static const String _SINGLE_QUERY = 'SINGLE_QUERY'; - T? object; + T object; List> queries = >[]; final Map limiters = Map(); @@ -284,18 +286,20 @@ class QueryBuilder { } // Add a constraint to the query that requires a particular key's value match another QueryBuilder - void whereMatchesQuery(String column, QueryBuilder query) { + void whereMatchesQuery( + String column, QueryBuilder query) { final String inQuery = - query._buildQueryRelational(query.object!.parseClassName); + query._buildQueryRelational(query.object.parseClassName); queries.add(MapEntry( _SINGLE_QUERY, '\"$column\":{\"\$inQuery\":$inQuery}')); } //Add a constraint to the query that requires a particular key's value does not match another QueryBuilder - void whereDoesNotMatchQuery(String column, QueryBuilder query) { + void whereDoesNotMatchQuery( + String column, QueryBuilder query) { final String inQuery = - query._buildQueryRelational(query.object!.parseClassName); + query._buildQueryRelational(query.object.parseClassName); queries.add(MapEntry( _SINGLE_QUERY, '\"$column\":{\"\$notInQuery\":$inQuery}')); @@ -315,7 +319,7 @@ class QueryBuilder { } final String inQuery = - query._buildQueryRelationalKey(query.object!.parseClassName, keyInQuery); + query._buildQueryRelationalKey(query.object.parseClassName, keyInQuery); queries.add(MapEntry( _SINGLE_QUERY, '\"$column\":{\"\$select\":$inQuery}')); @@ -335,7 +339,7 @@ class QueryBuilder { } final String inQuery = - query._buildQueryRelationalKey(query.object!.parseClassName, keyInQuery); + query._buildQueryRelationalKey(query.object.parseClassName, keyInQuery); queries.add(MapEntry( _SINGLE_QUERY, '\"$column\":{\"\$dontSelect\":$inQuery}')); @@ -346,7 +350,7 @@ class QueryBuilder { /// Make sure to call this after defining your queries Future query( {ProgressCallback? progressCallback}) async { - return object!.query( + return object.query( buildQuery(), progressCallback: progressCallback, ); @@ -355,12 +359,12 @@ class QueryBuilder { Future distinct( String className) async { final String queryString = 'distinct=$className'; - return object!.distinct(queryString); + return object.distinct(queryString); } ///Counts the number of objects that match this query Future count() async { - return object!.query(_buildQueryCount()); + return object.query(_buildQueryCount()); } /// Builds the query for Parse diff --git a/packages/dart/lib/src/utils/parse_live_list.dart b/packages/dart/lib/src/utils/parse_live_list.dart index 2fb58faad..b2ecbb51b 100644 --- a/packages/dart/lib/src/utils/parse_live_list.dart +++ b/packages/dart/lib/src/utils/parse_live_list.dart @@ -165,7 +165,7 @@ class ParseLiveList { LiveQuery() .client .subscribe(QueryBuilder.copy(_query), - copyObject: _query.object!.clone(_query.object!.toJson())) + copyObject: _query.object.clone(_query.object.toJson())) .then((Subscription subscription) { _liveQuerySubscription = subscription; @@ -266,7 +266,7 @@ class ParseLiveList { for (String key in paths.keys) { if (object.containsKey(key)) { ParseObject? includedObject = object.get(key); - if(includedObject != null){ + if (includedObject != null) { //If the object is not fetched if (!includedObject.containsKey(keyVarUpdatedAt)) { //See if oldObject contains key diff --git a/packages/dart/pubspec.yaml b/packages/dart/pubspec.yaml index a9baf22d2..efe5c5410 100644 --- a/packages/dart/pubspec.yaml +++ b/packages/dart/pubspec.yaml @@ -1,13 +1,12 @@ name: parse_server_sdk description: Dart plugin for Parse Server, (https://parseplatform.org), (https://back4app.com) -version: 3.0.0 +version: 3.0.1 homepage: https://github.com/phillwiggins/flutter_parse_sdk environment: sdk: '>=2.12.0 <3.0.0' dependencies: - # Networking dio: ^4.0.0 http: ^0.13.1 diff --git a/packages/flutter/pubspec.yaml b/packages/flutter/pubspec.yaml index 38387bcff..2c6d22b2c 100644 --- a/packages/flutter/pubspec.yaml +++ b/packages/flutter/pubspec.yaml @@ -1,6 +1,6 @@ name: parse_server_sdk_flutter description: Flutter plugin for Parse Server, (https://parseplatform.org), (https://back4app.com) -version: 3.0.0 +version: 3.0.1 homepage: https://github.com/phillwiggins/flutter_parse_sdk environment: @@ -11,7 +11,7 @@ dependencies: sdk: flutter # Uncomment for Release version - parse_server_sdk: ^3.0.0 + parse_server_sdk: ^3.0.1 # Uncomment for local testing #parse_server_sdk: From 1935c5b004bfbb5e80ffa747624379b7a8904e1a Mon Sep 17 00:00:00 2001 From: Maximilian Fischer Date: Wed, 9 Jun 2021 12:27:50 +0200 Subject: [PATCH 02/18] parse_live_list: fix unmodifiable list --- packages/dart/lib/src/utils/parse_live_list.dart | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/packages/dart/lib/src/utils/parse_live_list.dart b/packages/dart/lib/src/utils/parse_live_list.dart index b2ecbb51b..1a2304dbc 100644 --- a/packages/dart/lib/src/utils/parse_live_list.dart +++ b/packages/dart/lib/src/utils/parse_live_list.dart @@ -133,7 +133,7 @@ class ParseLiveList { if (_debug) print('ParseLiveList: lazyLoading is ${_lazyLoading ? 'on' : 'off'}'); if (_lazyLoading) { - final List keys = _preloadedColumns; + final List keys = _preloadedColumns.toList(); if (_lazyLoading && query.limiters.containsKey('order')) keys.addAll( query.limiters['order'].toString().split(',').map((String string) { @@ -480,12 +480,11 @@ class ParseLiveList { } class ParseLiveElement extends ParseLiveListElement { - ParseLiveElement(T object, - {bool loaded = false, List? includeObject}) + ParseLiveElement(T object, {bool loaded = false, List? includeObject}) : super(object, - loaded: loaded, - updatedSubItems: - ParseLiveList._toIncludeMap(includeObject ?? [])) { + loaded: loaded, + updatedSubItems: + ParseLiveList._toIncludeMap(includeObject ?? [])) { _includes = ParseLiveList._toIncludeMap(includeObject ?? []); queryBuilder = QueryBuilder(object.clone({})) ..whereEqualTo(keyVarObjectId, object.objectId); From b294d4b800b6958260e0a100936278ba0f7f124b Mon Sep 17 00:00:00 2001 From: Maximilian Fischer Date: Wed, 9 Jun 2021 12:27:58 +0200 Subject: [PATCH 03/18] auto format --- packages/dart/lib/src/utils/parse_live_list.dart | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/packages/dart/lib/src/utils/parse_live_list.dart b/packages/dart/lib/src/utils/parse_live_list.dart index 1a2304dbc..337c2a6c5 100644 --- a/packages/dart/lib/src/utils/parse_live_list.dart +++ b/packages/dart/lib/src/utils/parse_live_list.dart @@ -308,9 +308,11 @@ class ParseLiveList { ParseObject>(ParseObject(includedObject.parseClassName)) ..whereEqualTo(keyVarObjectId, includedObject.objectId) ..includeObject(_toIncludeStringList(paths[key])); - loadingNodes.add( - queryBuilder.query().then((ParseResponse parseResponse) { - if (parseResponse.success && parseResponse.results!.length == 1) { + loadingNodes.add(queryBuilder + .query() + .then((ParseResponse parseResponse) { + if (parseResponse.success && + parseResponse.results!.length == 1) { // ignore: deprecated_member_use_from_same_package object[key] = parseResponse.results![0]; } @@ -445,7 +447,7 @@ class ParseLiveList { String getIdentifier(int index) { if (index < _list.length) { return _list[index].object!.get(keyVarObjectId)! + - _list[index].object!.get(keyVarUpdatedAt).toString(); + _list[index].object!.get(keyVarUpdatedAt).toString(); } return 'NotFound'; } From d52cefa49c1a48d539dff4197d9e2525bc326571 Mon Sep 17 00:00:00 2001 From: Maximilian Fischer Date: Wed, 9 Jun 2021 12:32:59 +0200 Subject: [PATCH 04/18] fix forced not-null cast in LiveList this should break nothing and instead fix forced not-null casts on null objects --- packages/dart/lib/src/utils/parse_live_list.dart | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/packages/dart/lib/src/utils/parse_live_list.dart b/packages/dart/lib/src/utils/parse_live_list.dart index 337c2a6c5..0f4f9d792 100644 --- a/packages/dart/lib/src/utils/parse_live_list.dart +++ b/packages/dart/lib/src/utils/parse_live_list.dart @@ -590,11 +590,12 @@ class ParseLiveListElement { void _subscribe() { _subscriptionQueue.whenComplete(() async { - if (_updatedSubItems.isNotEmpty && _object != null) { + T? object = _object; + if (_updatedSubItems.isNotEmpty && object != null) { final List> tasks = >[]; for (PathKey key in _updatedSubItems.keys) { - tasks.add(_subscribeSubItem(_object!, key, - _object!.get(key.key)!, _updatedSubItems[key])); + tasks.add(_subscribeSubItem(object, key, + object.get(key.key), _updatedSubItems[key])); } await Future.wait(tasks); } @@ -612,12 +613,12 @@ class ParseLiveListElement { } Future _subscribeSubItem(ParseObject parentObject, PathKey currentKey, - ParseObject subObject, Map path) async { - if (_liveQuery != null) { + ParseObject? subObject, Map path) async { + if (_liveQuery != null && subObject != null) { final List> tasks = >[]; for (PathKey key in path.keys) { tasks.add(_subscribeSubItem( - subObject, key, subObject.get(key.key)!, path[key])); + subObject, key, subObject.get(key.key), path[key])); } final QueryBuilder queryBuilder = QueryBuilder(subObject) @@ -641,7 +642,7 @@ class ParseLiveListElement { _unsubscribe(path); for (PathKey key in path.keys) { tasks.add(_subscribeSubItem(newObject, key, - newObject.get(key.key)!, path[key])); + newObject.get(key.key), path[key])); } } await Future.wait(tasks); From ac1aaddd851a5a37402dc01a428d77c74256e51c Mon Sep 17 00:00:00 2001 From: Maximilian Fischer Date: Wed, 9 Jun 2021 12:34:42 +0200 Subject: [PATCH 05/18] remove null assertion operator this makes the code easier to maintain --- packages/dart/lib/src/utils/parse_live_list.dart | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/dart/lib/src/utils/parse_live_list.dart b/packages/dart/lib/src/utils/parse_live_list.dart index 0f4f9d792..11ed23e1f 100644 --- a/packages/dart/lib/src/utils/parse_live_list.dart +++ b/packages/dart/lib/src/utils/parse_live_list.dart @@ -614,7 +614,8 @@ class ParseLiveListElement { Future _subscribeSubItem(ParseObject parentObject, PathKey currentKey, ParseObject? subObject, Map path) async { - if (_liveQuery != null && subObject != null) { + LiveQuery? liveQuery = _liveQuery; + if (liveQuery != null && subObject != null) { final List> tasks = >[]; for (PathKey key in path.keys) { tasks.add(_subscribeSubItem( @@ -624,7 +625,7 @@ class ParseLiveListElement { QueryBuilder(subObject) ..whereEqualTo(keyVarObjectId, subObject.objectId); - tasks.add(_liveQuery!.client + tasks.add(liveQuery.client .subscribe(queryBuilder) .then((Subscription subscription) { currentKey.subscription = subscription; From 44b86113a96925c6584546e473b3d2503c4b85c3 Mon Sep 17 00:00:00 2001 From: Maximilian Fischer Date: Wed, 9 Jun 2021 12:37:43 +0200 Subject: [PATCH 06/18] remove null assertion operator this makes the code easier to maintain --- packages/dart/lib/src/utils/parse_live_list.dart | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/dart/lib/src/utils/parse_live_list.dart b/packages/dart/lib/src/utils/parse_live_list.dart index 11ed23e1f..93a28e04a 100644 --- a/packages/dart/lib/src/utils/parse_live_list.dart +++ b/packages/dart/lib/src/utils/parse_live_list.dart @@ -44,8 +44,7 @@ class ParseLiveList { int get nextID => _nextID++; /// is object1 listed after object2? - /// can return null - bool? after(T? object1, T? object2) { + bool? after(T object1, T object2) { List fields = []; if (_query.limiters.containsKey('order')) { @@ -58,8 +57,8 @@ class ParseLiveList { reverse = true; key = key.substring(1); } - final dynamic val1 = object1!.get(key); - final dynamic val2 = object2!.get(key); + final dynamic val1 = object1.get(key); + final dynamic val2 = object2.get(key); if (val1 == null && val2 == null) { break; @@ -357,7 +356,8 @@ class ParseLiveList { await _loadIncludes(object, paths: _includePaths); } for (int i = 0; i < _list.length; i++) { - if (after(object, _list[i].object) != true) { + T? other = _list[i].object; + if (other != null && after(object, other) != true) { _list.insert( i, ParseLiveListElement(object, From 6568bc208a42331c56bbcd5f8d4d3306f7ac743b Mon Sep 17 00:00:00 2001 From: Maximilian Fischer Date: Wed, 9 Jun 2021 12:45:17 +0200 Subject: [PATCH 07/18] update version --- packages/dart/lib/src/base/parse_constants.dart | 2 +- packages/flutter/README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/dart/lib/src/base/parse_constants.dart b/packages/dart/lib/src/base/parse_constants.dart index d1a5dc428..3794a40db 100644 --- a/packages/dart/lib/src/base/parse_constants.dart +++ b/packages/dart/lib/src/base/parse_constants.dart @@ -1,7 +1,7 @@ part of flutter_parse_sdk; // Library -const String keySdkVersion = '3.0.0'; +const String keySdkVersion = '3.0.1'; const String keyLibraryName = 'Flutter Parse SDK'; // End Points diff --git a/packages/flutter/README.md b/packages/flutter/README.md index bef34efec..8befebe22 100644 --- a/packages/flutter/README.md +++ b/packages/flutter/README.md @@ -14,7 +14,7 @@ This is a work in progress and we are consistently updating it. Please let us kn To install, either add to your pubspec.yaml ```yml dependencies: - parse_server_sdk_flutter: ^3.0.0 + parse_server_sdk_flutter: ^3.0.1 ``` or clone this repository and add to your project. As this is an early development with multiple contributors, it is probably best to download/clone and keep updating as an when a new feature is added. From 4b0c310d27c2e222ceb5f52b62e5894da6a2da34 Mon Sep 17 00:00:00 2001 From: Maximilian Fischer Date: Wed, 9 Jun 2021 12:53:34 +0200 Subject: [PATCH 08/18] LiveQuery: make _liveQueryURL not null and final --- .../lib/src/network/parse_live_query.dart | 35 ++++++++++--------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/packages/dart/lib/src/network/parse_live_query.dart b/packages/dart/lib/src/network/parse_live_query.dart index 1c83c1bb5..c0a20fa07 100644 --- a/packages/dart/lib/src/network/parse_live_query.dart +++ b/packages/dart/lib/src/network/parse_live_query.dart @@ -126,31 +126,34 @@ class LiveQueryReconnectingController { class LiveQueryClient { factory LiveQueryClient() => _getInstance(); - LiveQueryClient._internal({bool? debug, bool? autoSendSessionId}) { + LiveQueryClient._internal(this._liveQueryURL, + {bool? debug, bool? autoSendSessionId}) { _clientEventStreamController = StreamController(); _clientEventStream = _clientEventStreamController.stream.asBroadcastStream(); _debug = isDebugEnabled(objectLevelDebug: debug); - _sendSessionId = - autoSendSessionId ?? ParseCoreData().autoSendSessionId; - _liveQueryURL = ParseCoreData().liveQueryURL; - assert(_liveQueryURL != null, - 'liveQueryUrl is not set. For how to setup Live Queries, see https://github.com/parse-community/Parse-SDK-Flutter/tree/master/packages/flutter#live-queries.'); - if (_liveQueryURL!.contains('https')) { - _liveQueryURL = _liveQueryURL!.replaceAll('https', 'wss'); - } else if (_liveQueryURL!.contains('http')) { - _liveQueryURL = _liveQueryURL!.replaceAll('http', 'ws'); - } + _sendSessionId = autoSendSessionId ?? ParseCoreData().autoSendSessionId; reconnectingController = LiveQueryReconnectingController( () => reconnect(userInitialized: false), getClientEventStream, _debug); } static LiveQueryClient get instance => _getInstance(); static LiveQueryClient? _instance; - static LiveQueryClient _getInstance( - {bool? debug, bool? autoSendSessionId}) { - _instance ??= LiveQueryClient._internal( + static LiveQueryClient _getInstance({bool? debug, bool? autoSendSessionId}) { + String? liveQueryURL = ParseCoreData().liveQueryURL; + if (liveQueryURL == null) { + assert(false, + 'liveQueryUrl is not set. For how to setup Live Queries, see https://github.com/parse-community/Parse-SDK-Flutter/tree/master/packages/flutter#live-queries.'); + liveQueryURL = ""; + } else { + if (liveQueryURL.contains('https')) { + liveQueryURL = liveQueryURL.replaceAll('https', 'wss'); + } else if (liveQueryURL.contains('http')) { + liveQueryURL = liveQueryURL.replaceAll('http', 'ws'); + } + } + _instance ??= LiveQueryClient._internal(liveQueryURL, debug: debug, autoSendSessionId: autoSendSessionId); return _instance!; } @@ -163,7 +166,7 @@ class LiveQueryClient { late bool _debug; late bool _sendSessionId; WebSocketChannel? _channel; - String? _liveQueryURL; + final String _liveQueryURL; bool _connecting = false; late StreamController _clientEventStreamController; late Stream _clientEventStream; @@ -256,7 +259,7 @@ class LiveQueryClient { _connecting = true; try { - _webSocket = await parse_web_socket.WebSocket.connect(_liveQueryURL!); + _webSocket = await parse_web_socket.WebSocket.connect(_liveQueryURL); _connecting = false; if (_webSocket != null && _webSocket!.readyState == parse_web_socket.WebSocket.OPEN) { From 42f82a38054f50b277c7dd6b7b388b0b543f381d Mon Sep 17 00:00:00 2001 From: Maximilian Fischer Date: Wed, 9 Jun 2021 12:53:58 +0200 Subject: [PATCH 09/18] LiveQuery: _currentTimer null-safe --- packages/dart/lib/src/network/parse_live_query.dart | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/dart/lib/src/network/parse_live_query.dart b/packages/dart/lib/src/network/parse_live_query.dart index c0a20fa07..e0f697b6b 100644 --- a/packages/dart/lib/src/network/parse_live_query.dart +++ b/packages/dart/lib/src/network/parse_live_query.dart @@ -61,8 +61,9 @@ class LiveQueryReconnectingController { break; case LiveQueryClientEvent.USER_DISCONNECTED: _userDisconnected = true; - if (_currentTimer != null) { - _currentTimer!.cancel(); + Timer? currentTimer = _currentTimer; + if (currentTimer != null) { + currentTimer.cancel(); _currentTimer = null; } break; From aaeb5229f3f9daa133349200fed9e13a1666ebce Mon Sep 17 00:00:00 2001 From: Maximilian Fischer Date: Wed, 9 Jun 2021 12:55:46 +0200 Subject: [PATCH 10/18] LiveList: fix _objectUpdated --- packages/dart/lib/src/utils/parse_live_list.dart | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/dart/lib/src/utils/parse_live_list.dart b/packages/dart/lib/src/utils/parse_live_list.dart index 93a28e04a..f81f1ce68 100644 --- a/packages/dart/lib/src/utils/parse_live_list.dart +++ b/packages/dart/lib/src/utils/parse_live_list.dart @@ -375,11 +375,13 @@ class ParseLiveList { Future _objectUpdated(T object) async { for (int i = 0; i < _list.length; i++) { - if (_list[i].object!.get(keyVarObjectId) == - object.get(keyVarObjectId)) { + T? other = _list[i].object; + if (other != null && + other.get(keyVarObjectId) == + object.get(keyVarObjectId)) { await _loadIncludes(object, oldObject: _list[i].object, paths: _includePaths); - if (after(_list[i].object, object) == null) { + if (after(other, object) == null) { _list[i].object = object.clone(object.toJson(full: true)); _eventStreamController.sink.add(ParseLiveListUpdateEvent( i, object.clone(object.toJson(full: true)))); From 0619422c7fcf2a91306a17b70ad725a653cb035d Mon Sep 17 00:00:00 2001 From: Maximilian Fischer Date: Wed, 9 Jun 2021 12:58:34 +0200 Subject: [PATCH 11/18] LiveQuery: fix forced not-null cast this should break nothing and instead fix forced not-null casts on null objects --- packages/dart/lib/src/network/parse_live_query.dart | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/dart/lib/src/network/parse_live_query.dart b/packages/dart/lib/src/network/parse_live_query.dart index e0f697b6b..057c96263 100644 --- a/packages/dart/lib/src/network/parse_live_query.dart +++ b/packages/dart/lib/src/network/parse_live_query.dart @@ -154,9 +154,11 @@ class LiveQueryClient { liveQueryURL = liveQueryURL.replaceAll('http', 'ws'); } } - _instance ??= LiveQueryClient._internal(liveQueryURL, - debug: debug, autoSendSessionId: autoSendSessionId); - return _instance!; + LiveQueryClient instance = _instance ?? + LiveQueryClient._internal(liveQueryURL, + debug: debug, autoSendSessionId: autoSendSessionId); + _instance ??= instance; + return instance; } Stream get getClientEventStream { From 481f59e7984afa193e09f7d54230ec0c5a8ee255 Mon Sep 17 00:00:00 2001 From: Maximilian Fischer Date: Wed, 9 Jun 2021 13:14:02 +0200 Subject: [PATCH 12/18] LiveQuery: remove forced not-null casts this should break nothing and instead remove forced not-null casts on null objects --- .../lib/src/network/parse_live_query.dart | 84 +++++++++++-------- 1 file changed, 48 insertions(+), 36 deletions(-) diff --git a/packages/dart/lib/src/network/parse_live_query.dart b/packages/dart/lib/src/network/parse_live_query.dart index 057c96263..93dbba882 100644 --- a/packages/dart/lib/src/network/parse_live_query.dart +++ b/packages/dart/lib/src/network/parse_live_query.dart @@ -183,26 +183,29 @@ class LiveQueryClient { } int readyState() { - if (_webSocket != null) { - return _webSocket!.readyState; + parse_web_socket.WebSocket? webSocket = _webSocket; + if (webSocket != null) { + return webSocket.readyState; } return parse_web_socket.WebSocket.CONNECTING; } Future disconnect({bool userInitialized = false}) async { - if (_webSocket != null && - _webSocket!.readyState == parse_web_socket.WebSocket.OPEN) { + parse_web_socket.WebSocket? webSocket = _webSocket; + if (webSocket != null && + webSocket.readyState == parse_web_socket.WebSocket.OPEN) { if (_debug) { print('$_printConstLiveQuery: Socket closed'); } - await _webSocket!.close(); + await webSocket.close(); _webSocket = null; } - if (_channel != null && _channel!.sink != null) { + WebSocketChannel? channel = _channel; + if (channel != null) { if (_debug) { print('$_printConstLiveQuery: close'); } - await _channel!.sink.close(); + await channel.sink.close(); _channel = null; } _requestSubscription.values.toList().forEach((Subscription subscription) { @@ -237,11 +240,12 @@ class LiveQueryClient { 'op': 'unsubscribe', 'requestId': subscription.requestId, }; - if (_channel != null && _channel!.sink != null) { + WebSocketChannel? channel = _channel; + if (channel != null) { if (_debug) { print('$_printConstLiveQuery: UnsubscribeMessage: $unsubscribeMessage'); } - _channel!.sink.add(jsonEncode(unsubscribeMessage)); + channel.sink.add(jsonEncode(unsubscribeMessage)); subscription._enabled = false; _requestSubscription.remove(subscription.requestId); } @@ -262,10 +266,11 @@ class LiveQueryClient { _connecting = true; try { - _webSocket = await parse_web_socket.WebSocket.connect(_liveQueryURL); + parse_web_socket.WebSocket webSocket = + await parse_web_socket.WebSocket.connect(_liveQueryURL); + _webSocket = webSocket; _connecting = false; - if (_webSocket != null && - _webSocket!.readyState == parse_web_socket.WebSocket.OPEN) { + if (webSocket.readyState == parse_web_socket.WebSocket.OPEN) { if (_debug) { print('$_printConstLiveQuery: Socket opened'); } @@ -275,8 +280,9 @@ class LiveQueryClient { } return Future.value(null); } - _channel = _webSocket!.createWebSocketChannel(); - _channel!.stream.listen((dynamic message) { + WebSocketChannel channel = webSocket.createWebSocketChannel(); + _channel = channel; + channel.stream.listen((dynamic message) { _handleMessage(message); }, onDone: () { _clientEventStreamController.sink @@ -308,7 +314,8 @@ class LiveQueryClient { } void _connectLiveQuery() { - if (_channel == null || _channel!.sink == null) { + WebSocketChannel? channel = _channel; + if (channel == null) { return; } //The connect message is sent from a client to the LiveQuery server. @@ -318,19 +325,21 @@ class LiveQueryClient { 'applicationId': ParseCoreData().applicationId }; - if (_sendSessionId && ParseCoreData().sessionId != null) { - connectMessage['sessionToken'] = ParseCoreData().sessionId!; + if (_sendSessionId) { + String? sessionId = ParseCoreData().sessionId; + if (sessionId != null) { + connectMessage['sessionToken'] = sessionId; + } } - - if (ParseCoreData().clientKey != null) - connectMessage['clientKey'] = ParseCoreData().clientKey!; - if (ParseCoreData().masterKey != null) - connectMessage['masterKey'] = ParseCoreData().masterKey!; + String? clientKey = ParseCoreData().clientKey; + String? masterKey = ParseCoreData().masterKey; + if (clientKey != null) connectMessage['clientKey'] = clientKey; + if (masterKey != null) connectMessage['masterKey'] = masterKey; if (_debug) { print('$_printConstLiveQuery: ConnectMessage: $connectMessage'); } - _channel!.sink.add(jsonEncode(connectMessage)); + channel.sink.add(jsonEncode(connectMessage)); } void _subscribeLiveQuery(Subscription subscription) { @@ -367,7 +376,7 @@ class LiveQueryClient { print('$_printConstLiveQuery: SubscribeMessage: $subscribeMessage'); } - _channel!.sink.add(jsonEncode(subscribeMessage)); + _channel?.sink.add(jsonEncode(subscribeMessage)); } void _handleMessage(String message) { @@ -394,22 +403,25 @@ class LiveQueryClient { return; } if (subscription.eventCallbacks.containsKey(actionData['op'])) { - if (actionData.containsKey('object')) { - final Map map = actionData['object']; - final String? className = map['className']; - if (className == keyClassUser) { - subscription.eventCallbacks[actionData['op']]!( - (subscription.copyObject ?? + Function? eventCallback = subscription.eventCallbacks[actionData['op']]; + if (eventCallback != null) { + if (actionData.containsKey('object')) { + final Map map = actionData['object']; + final String? className = map['className']; + if (className != null) { + if (className == keyClassUser) { + eventCallback((subscription.copyObject ?? ParseCoreData.instance.createParseUser(null, null, null)) .fromJson(map)); - } else { - subscription.eventCallbacks[actionData['op']]!( - (subscription.copyObject ?? - ParseCoreData.instance.createObject(className!)) + } else { + eventCallback((subscription.copyObject ?? + ParseCoreData.instance.createObject(className)) .fromJson(map)); + } + } + } else { + eventCallback(actionData); } - } else { - subscription.eventCallbacks[actionData['op']]!(actionData); } } } From ad1d966bc85e90312092c4a452d93cfec0c74d82 Mon Sep 17 00:00:00 2001 From: Maximilian Fischer Date: Wed, 9 Jun 2021 14:36:25 +0200 Subject: [PATCH 13/18] LiveList: remove forced not-null casts this should break nothing and instead remove forced not-null casts on null objects --- .../dart/lib/src/utils/parse_live_list.dart | 211 ++++++++++-------- 1 file changed, 114 insertions(+), 97 deletions(-) diff --git a/packages/dart/lib/src/utils/parse_live_list.dart b/packages/dart/lib/src/utils/parse_live_list.dart index f81f1ce68..3e3ec8be6 100644 --- a/packages/dart/lib/src/utils/parse_live_list.dart +++ b/packages/dart/lib/src/utils/parse_live_list.dart @@ -205,9 +205,8 @@ class ParseLiveList { //update List for (int i = 0; i < _list.length; i++) { - final ParseObject currentObject = _list[i].object!; - final String currentObjectId = - currentObject.get(keyVarObjectId)!; + final ParseObject currentObject = _list[i].object; + final String? currentObjectId = currentObject.objectId; bool stillInList = false; @@ -223,8 +222,9 @@ class ParseLiveList { tasks.add(queryBuilder .query() .then((ParseResponse result) async { - if (result.success && result.results != null) { - await _objectUpdated(result.results!.first); + List? results = result.results; + if (result.success && results != null) { + await _objectUpdated(results.first); } })); } @@ -263,21 +263,51 @@ class ParseLiveList { final List> loadingNodes = >[]; for (String key in paths.keys) { - if (object.containsKey(key)) { + ParseObject? keyInObject = object.get(key); + if (keyInObject != null) { ParseObject? includedObject = object.get(key); if (includedObject != null) { //If the object is not fetched if (!includedObject.containsKey(keyVarUpdatedAt)) { //See if oldObject contains key - if (oldObject != null && oldObject.containsKey(key)) { - includedObject = oldObject.get(key)!; - //If the object is not fetched || the ids don't match / the pointer changed - if (!includedObject.containsKey(keyVarUpdatedAt) || - includedObject.objectId != - object.get(key)!.objectId) { - includedObject = object.get(key)!; + if (oldObject != null) { + ParseObject? keyInOld = oldObject.get(key); + if (keyInOld != null) { + includedObject = keyInOld; + + //If the object is not fetched || the ids don't match / the pointer changed + if (!includedObject.containsKey(keyVarUpdatedAt) || + includedObject.objectId != keyInObject.objectId) { + includedObject = keyInObject; + //fetch from web including sub objects + //same as down there + final QueryBuilder queryBuilder = QueryBuilder< + ParseObject>(ParseObject(includedObject.parseClassName)) + ..whereEqualTo(keyVarObjectId, includedObject.objectId) + ..includeObject(_toIncludeStringList(paths[key])); + loadingNodes.add(queryBuilder + .query() + .then((ParseResponse parseResponse) { + List? results = parseResponse.results; + if (parseResponse.success && + results != null && + results.length == 1) { + // ignore: deprecated_member_use_from_same_package + object[key] = results[0]; + } + })); + continue; + } else { + // ignore: deprecated_member_use_from_same_package + object[key] = includedObject; + //recursion + loadingNodes + .add(_loadIncludes(includedObject, paths: paths[key])); + continue; + } + } else { //fetch from web including sub objects - //same as down there + //same as up there final QueryBuilder queryBuilder = QueryBuilder< ParseObject>(ParseObject(includedObject.parseClassName)) ..whereEqualTo(keyVarObjectId, includedObject.objectId) @@ -285,38 +315,16 @@ class ParseLiveList { loadingNodes.add(queryBuilder .query() .then((ParseResponse parseResponse) { + List? results = parseResponse.results; if (parseResponse.success && - parseResponse.results!.length == 1) { + results != null && + results.length == 1) { // ignore: deprecated_member_use_from_same_package - object[key] = parseResponse.results![0]; + object[key] = results[0]; } })); continue; - } else { - // ignore: deprecated_member_use_from_same_package - object[key] = includedObject; - //recursion - loadingNodes - .add(_loadIncludes(includedObject, paths: paths[key])); - continue; } - } else { - //fetch from web including sub objects - //same as up there - final QueryBuilder queryBuilder = QueryBuilder< - ParseObject>(ParseObject(includedObject.parseClassName)) - ..whereEqualTo(keyVarObjectId, includedObject.objectId) - ..includeObject(_toIncludeStringList(paths[key])); - loadingNodes.add(queryBuilder - .query() - .then((ParseResponse parseResponse) { - if (parseResponse.success && - parseResponse.results!.length == 1) { - // ignore: deprecated_member_use_from_same_package - object[key] = parseResponse.results![0]; - } - })); - continue; } } } else { @@ -356,8 +364,7 @@ class ParseLiveList { await _loadIncludes(object, paths: _includePaths); } for (int i = 0; i < _list.length; i++) { - T? other = _list[i].object; - if (other != null && after(object, other) != true) { + if (after(object, _list[i].object) != true) { _list.insert( i, ParseLiveListElement(object, @@ -375,13 +382,11 @@ class ParseLiveList { Future _objectUpdated(T object) async { for (int i = 0; i < _list.length; i++) { - T? other = _list[i].object; - if (other != null && - other.get(keyVarObjectId) == - object.get(keyVarObjectId)) { + if (_list[i].object.get(keyVarObjectId) == + object.get(keyVarObjectId)) { await _loadIncludes(object, oldObject: _list[i].object, paths: _includePaths); - if (after(other, object) == null) { + if (after(_list[i].object, object) == null) { _list[i].object = object.clone(object.toJson(full: true)); _eventStreamController.sink.add(ParseLiveListUpdateEvent( i, object.clone(object.toJson(full: true)))); @@ -399,7 +404,7 @@ class ParseLiveList { Future _objectDeleted(T object) async { for (int i = 0; i < _list.length; i++) { - if (_list[i].object!.get(keyVarObjectId) == + if (_list[i].object.get(keyVarObjectId) == object.get(keyVarObjectId)) { await _loadIncludes(object, oldObject: _list[i].object, paths: _includePaths); @@ -416,7 +421,7 @@ class ParseLiveList { if (!_list[index].loaded) { final QueryBuilder queryBuilder = QueryBuilder.copy(_query) ..whereEqualTo( - keyVarObjectId, _list[index].object!.get(keyVarObjectId)) + keyVarObjectId, _list[index].object.get(keyVarObjectId)) ..setLimit(1); final ParseResponse response = await queryBuilder.query(); if (_list.isEmpty) { @@ -427,29 +432,29 @@ class ParseLiveList { if (response.success) { _list[index].object = response.results?.first; } else { - _list[index].object = null; - yield* _createStreamError(response.error!); + ParseError? error = response.error; + if (error != null) yield* _createStreamError(error); return; } } // just for testing // await Future.delayed(const Duration(seconds: 2)); - yield _list[index].object!; + yield _list[index].object; yield* _list[index].stream; } } String idOf(int index) { if (index < _list.length) { - return _list[index].object!.get(keyVarObjectId)!; + return _list[index].object.objectId ?? 'NotFound'; } return 'NotFound'; } String getIdentifier(int index) { if (index < _list.length) { - return _list[index].object!.get(keyVarObjectId)! + - _list[index].object!.get(keyVarUpdatedAt).toString(); + return idOf(index) + + _list[index].object.get(keyVarUpdatedAt).toString(); } return 'NotFound'; } @@ -469,12 +474,15 @@ class ParseLiveList { } void dispose() { - if (_liveQuerySubscription != null) { - LiveQuery().client.unSubscribe(_liveQuerySubscription!); + Subscription? liveQuerySubscription = _liveQuerySubscription; + if (liveQuerySubscription != null) { + LiveQuery().client.unSubscribe(liveQuerySubscription); _liveQuerySubscription = null; } - if (_liveQueryClientEventSubscription != null) { - _liveQueryClientEventSubscription!.cancel(); + StreamSubscription? liveQueryClientEventSubscription = + _liveQueryClientEventSubscription; + if (liveQueryClientEventSubscription != null) { + liveQueryClientEventSubscription.cancel(); _liveQueryClientEventSubscription = null; } while (_list.isNotEmpty) { @@ -511,11 +519,12 @@ class ParseLiveElement extends ParseLiveListElement { } } - _subscription = await LiveQuery().client.subscribe( + Subscription subscription = await LiveQuery().client.subscribe( QueryBuilder.copy(queryBuilder), copyObject: object.clone({})); + _subscription = subscription; - _subscription!.on(LiveQueryEvent.update, (T newObject) async { + subscription.on(LiveQueryEvent.update, (T newObject) async { await ParseLiveList._loadIncludes(newObject, oldObject: super.object, paths: _includes); super.object = newObject; @@ -541,8 +550,9 @@ class ParseLiveElement extends ParseLiveListElement { @override void dispose() { - if (_subscription != null) { - LiveQuery().client.unSubscribe(_subscription!); + final Subscription? subscription = _subscription; + if (subscription != null) { + LiveQuery().client.unSubscribe(subscription); _subscription = null; } super.dispose(); @@ -551,10 +561,8 @@ class ParseLiveElement extends ParseLiveListElement { class ParseLiveListElement { ParseLiveListElement(this._object, - {bool loaded = false, Map? updatedSubItems}) { - if (_object != null) { - _loaded = loaded; - } + {bool loaded = false, Map? updatedSubItems}) + : _loaded = loaded { _updatedSubItems = _toSubscriptionMap(updatedSubItems ?? Map()); if (_updatedSubItems.isNotEmpty) { @@ -564,7 +572,7 @@ class ParseLiveListElement { } final StreamController _streamController = StreamController.broadcast(); - T? _object; + T _object; bool _loaded = false; late Map _updatedSubItems; LiveQuery? _liveQuery; @@ -572,7 +580,7 @@ class ParseLiveListElement { Stream get stream => _streamController.stream; - T? get object => _object?.clone(_object!.toJson(full: true)); + T get object => _object.clone(_object.toJson(full: true)); Map _toSubscriptionMap(Map map) { final Map result = Map(); @@ -592,8 +600,8 @@ class ParseLiveListElement { void _subscribe() { _subscriptionQueue.whenComplete(() async { - T? object = _object; - if (_updatedSubItems.isNotEmpty && object != null) { + final T object = _object; + if (_updatedSubItems.isNotEmpty) { final List> tasks = >[]; for (PathKey key in _updatedSubItems.keys) { tasks.add(_subscribeSubItem(object, key, @@ -606,8 +614,10 @@ class ParseLiveListElement { void _unsubscribe(Map subscriptions) { for (PathKey key in subscriptions.keys) { - if (_liveQuery != null && key.subscription != null) { - _liveQuery!.client.unSubscribe(key.subscription!); + final Subscription? subscription = key.subscription; + LiveQuery? liveQuery = _liveQuery; + if (liveQuery != null && subscription != null) { + liveQuery.client.unSubscribe(subscription); key.subscription = null; } _unsubscribe(subscriptions[key]); @@ -638,8 +648,7 @@ class ParseLiveListElement { // ignore: deprecated_member_use_from_same_package parentObject[currentKey.key] = newObject; if (!_streamController.isClosed) { - _streamController - .add(_object?.clone(_object!.toJson(full: true))); + _streamController.add(object); //Resubscribe subitems // TODO(any): only resubscribe on changed pointers _unsubscribe(path); @@ -656,12 +665,12 @@ class ParseLiveListElement { } } - set object(T? value) { + set object(T value) { _loaded = true; _object = value; _unsubscribe(_updatedSubItems); _subscribe(); - _streamController.add(_object?.clone(_object!.toJson(full: true))); + _streamController.add(object); } bool get loaded => _loaded; @@ -674,7 +683,7 @@ class ParseLiveListElement { Future reconnected() async { if (loaded) { _subscriptionQueue.whenComplete(() async { - await _updateSubItems(_object!, _updatedSubItems); + await _updateSubItems(_object, _updatedSubItems); // _streamController.add(_object?.clone(_object.toJson(full: true))); }); } @@ -694,31 +703,39 @@ class ParseLiveListElement { ParseObject root, Map path) async { final List> tasks = >[]; for (PathKey key in path.keys) { - ParseObject subObject = root.get(key.key)!; - if (subObject.containsKey(keyVarUpdatedAt) == true) { - final QueryBuilder queryBuilder = - QueryBuilder(subObject) - ..keysToReturn([keyVarUpdatedAt]) - ..whereEqualTo(keyVarObjectId, subObject.objectId); - final ParseResponse parseResponse = await queryBuilder.query(); - if (parseResponse.success && - parseResponse.results!.first.updatedAt != subObject.updatedAt) { - queryBuilder.limiters.remove('keys'); - queryBuilder.includeObject(_getIncludeList(path[key])); + ParseObject? subObject = root.get(key.key); + if (subObject != null) { + if (subObject.containsKey(keyVarUpdatedAt) == true) { + final QueryBuilder queryBuilder = + QueryBuilder(subObject) + ..keysToReturn([keyVarUpdatedAt]) + ..whereEqualTo(keyVarObjectId, subObject.objectId); final ParseResponse parseResponse = await queryBuilder.query(); - if (parseResponse.success) { - subObject = parseResponse.result.first; + final List? results = parseResponse.results; + if (parseResponse.success && + results != null && + results.first.updatedAt != subObject.updatedAt) { + queryBuilder.limiters.remove('keys'); + queryBuilder.includeObject(_getIncludeList(path[key])); + final ParseResponse parseResponse = await queryBuilder.query(); + if (parseResponse.success) { + subObject = parseResponse.result.first; // root.getObjectData()[key.key] = subObject; - if (key.subscription?.eventCallbacks.containsKey('update') == - true) { - key.subscription!.eventCallbacks['update']!(subObject); - } + Subscription? subscription = key.subscription; + if (subscription != null && + subscription.eventCallbacks.containsKey('update') == true) { + Function? eventCallback = subscription.eventCallbacks['update']; + if (eventCallback != null) { + eventCallback(subObject); + } + } // key.subscription.eventCallbacks["update"](subObject); - break; + break; + } } } + tasks.add(_updateSubItems(subObject, path[key])); } - tasks.add(_updateSubItems(subObject, path[key])); } await Future.wait(tasks); } From 50fa8125edc75ed671cefb7390b41abfd1f6be7e Mon Sep 17 00:00:00 2001 From: Maximilian Fischer Date: Wed, 9 Jun 2021 14:40:47 +0200 Subject: [PATCH 14/18] LiveList: move typedefs to flutter package I think those where missed when splitting the package. --- packages/dart/lib/src/utils/parse_live_list.dart | 3 --- packages/flutter/lib/src/utils/parse_live_list.dart | 13 ++++++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/dart/lib/src/utils/parse_live_list.dart b/packages/dart/lib/src/utils/parse_live_list.dart index 3e3ec8be6..51d29b933 100644 --- a/packages/dart/lib/src/utils/parse_live_list.dart +++ b/packages/dart/lib/src/utils/parse_live_list.dart @@ -778,9 +778,6 @@ class ParseLiveListDeleteEvent ParseLiveListDeleteEvent(int index, T object) : super(index, object); } -typedef StreamGetter = Stream Function(); -typedef DataGetter = T Function(); - class ParseLiveListElementSnapshot { ParseLiveListElementSnapshot( {this.loadedData, this.error, this.preLoadedData}); diff --git a/packages/flutter/lib/src/utils/parse_live_list.dart b/packages/flutter/lib/src/utils/parse_live_list.dart index 0463c98bd..97c41ae33 100644 --- a/packages/flutter/lib/src/utils/parse_live_list.dart +++ b/packages/flutter/lib/src/utils/parse_live_list.dart @@ -3,6 +3,9 @@ part of flutter_parse_sdk_flutter; typedef ChildBuilder = Widget Function( BuildContext context, sdk.ParseLiveListElementSnapshot snapshot); +typedef StreamGetter = Stream Function(); +typedef DataGetter = T Function(); + class ParseLiveListWidget extends StatefulWidget { const ParseLiveListWidget({ Key? key, @@ -211,9 +214,9 @@ class ParseLiveListElementWidget required this.childBuilder}) : super(key: key); - final sdk.StreamGetter? stream; - final sdk.DataGetter? loadedData; - final sdk.DataGetter? preLoadedData; + final StreamGetter? stream; + final DataGetter? loadedData; + final DataGetter? preLoadedData; final Animation sizeFactor; final Duration duration; final ChildBuilder childBuilder; @@ -228,8 +231,8 @@ class ParseLiveListElementWidget class _ParseLiveListElementWidgetState extends State> with SingleTickerProviderStateMixin { - _ParseLiveListElementWidgetState(sdk.DataGetter? loadedDataGetter, - sdk.DataGetter? preLoadedDataGetter, sdk.StreamGetter? stream) { + _ParseLiveListElementWidgetState(DataGetter? loadedDataGetter, + DataGetter? preLoadedDataGetter, StreamGetter? stream) { _snapshot = sdk.ParseLiveListElementSnapshot( loadedData: loadedDataGetter != null ? loadedDataGetter() : null, preLoadedData: From 769fbf74e5651ae5d8784cbe062083679cc0a24a Mon Sep 17 00:00:00 2001 From: Maximilian Fischer Date: Wed, 9 Jun 2021 15:30:24 +0200 Subject: [PATCH 15/18] LiveListWidget: remove forced not-null casts This fixes a bug where a "Null check operator used on a null value"-error --- .../lib/src/utils/parse_live_list.dart | 73 ++++++++++--------- 1 file changed, 38 insertions(+), 35 deletions(-) diff --git a/packages/flutter/lib/src/utils/parse_live_list.dart b/packages/flutter/lib/src/utils/parse_live_list.dart index 97c41ae33..9cea74101 100644 --- a/packages/flutter/lib/src/utils/parse_live_list.dart +++ b/packages/flutter/lib/src/utils/parse_live_list.dart @@ -4,7 +4,7 @@ typedef ChildBuilder = Widget Function( BuildContext context, sdk.ParseLiveListElementSnapshot snapshot); typedef StreamGetter = Stream Function(); -typedef DataGetter = T Function(); +typedef DataGetter = T? Function(); class ParseLiveListWidget extends StatefulWidget { const ParseLiveListWidget({ @@ -68,7 +68,8 @@ class ParseLiveListWidget extends StatefulWidget { } else if (snapshot.hasData) { child = ListTile( title: Text( - snapshot.loadedData!.get(sdk.keyVarObjectId)!, + snapshot.loadedData?.get(sdk.keyVarObjectId) ?? + 'Missing Data!', ), ); } else { @@ -99,35 +100,37 @@ class _ParseLiveListWidgetState setState(() { _noData = livelist.size == 0; _liveList = livelist; - _liveList!.stream - .listen((sdk.ParseLiveListEvent event) { - if (event is sdk.ParseLiveListAddEvent) { - if (_animatedListKey.currentState != null) { - _animatedListKey.currentState! - .insertItem(event.index, duration: widget.duration); + livelist.stream.listen((sdk.ParseLiveListEvent event) { + final AnimatedListState? animatedListState = + _animatedListKey.currentState; + if (animatedListState != null) { + if (event is sdk.ParseLiveListAddEvent) { + animatedListState.insertItem(event.index, + duration: widget.duration); + + setState(() { + _noData = livelist.size == 0; + }); + } else if (event is sdk.ParseLiveListDeleteEvent) { + animatedListState.removeItem( + event.index, + (BuildContext context, Animation animation) => + ParseLiveListElementWidget( + key: ValueKey( + event.object.get(sdk.keyVarObjectId) ?? + 'removingItem'), + childBuilder: widget.childBuilder ?? + ParseLiveListWidget.defaultChildBuilder, + sizeFactor: animation, + duration: widget.duration, + loadedData: () => event.object as T, + preLoadedData: () => event.object as T, + ), + duration: widget.duration); + setState(() { + _noData = livelist.size == 0; + }); } - setState(() { - _noData = livelist.size == 0; - }); - } else if (event is sdk.ParseLiveListDeleteEvent) { - _animatedListKey.currentState!.removeItem( - event.index, - (BuildContext context, Animation animation) => - ParseLiveListElementWidget( - key: ValueKey(event.object.get( - sdk.keyVarObjectId, - defaultValue: 'removingItem')!), - childBuilder: widget.childBuilder ?? - ParseLiveListWidget.defaultChildBuilder, - sizeFactor: animation, - duration: widget.duration, - loadedData: () => event.object as T, - preLoadedData: () => event.object as T, - ), - duration: widget.duration); - setState(() { - _noData = livelist.size == 0; - }); } }); }); @@ -143,7 +146,8 @@ class _ParseLiveListWidgetState @override Widget build(BuildContext context) { - if (_liveList == null) { + final sdk.ParseLiveList? liveList = _liveList; + if (liveList == null) { return widget.listLoadingElement ?? Container(); } else { return Stack( @@ -154,8 +158,7 @@ class _ParseLiveListWidgetState duration: widget.duration, child: widget.queryEmptyElement, ), - //_liveList isn't (checked above) - buildAnimatedList(_liveList!), + buildAnimatedList(liveList), ], ); } @@ -184,8 +187,8 @@ class _ParseLiveListWidgetState return ParseLiveListElementWidget( key: ValueKey(liveList.getIdentifier(index)), stream: () => liveList.getAt(index), - loadedData: () => liveList.getLoadedAt(index)!, - preLoadedData: () => liveList.getPreLoadedAt(index)!, + loadedData: () => liveList.getLoadedAt(index), + preLoadedData: () => liveList.getPreLoadedAt(index), sizeFactor: animation, duration: widget.duration, childBuilder: From ea7edf47b132f507020c72fbb922f7bb71242789 Mon Sep 17 00:00:00 2001 From: Maximilian Fischer Date: Wed, 9 Jun 2021 15:46:20 +0200 Subject: [PATCH 16/18] LiveList: fix reusage of unchanged child objects I've broken this part while removing the not-null casts. My second attempt works :-) --- .../dart/lib/src/utils/parse_live_list.dart | 72 +++++++++---------- 1 file changed, 32 insertions(+), 40 deletions(-) diff --git a/packages/dart/lib/src/utils/parse_live_list.dart b/packages/dart/lib/src/utils/parse_live_list.dart index 51d29b933..fb6a74cd5 100644 --- a/packages/dart/lib/src/utils/parse_live_list.dart +++ b/packages/dart/lib/src/utils/parse_live_list.dart @@ -263,51 +263,19 @@ class ParseLiveList { final List> loadingNodes = >[]; for (String key in paths.keys) { - ParseObject? keyInObject = object.get(key); - if (keyInObject != null) { + if (object.containsKey(key)) { ParseObject? includedObject = object.get(key); if (includedObject != null) { //If the object is not fetched if (!includedObject.containsKey(keyVarUpdatedAt)) { //See if oldObject contains key - if (oldObject != null) { - ParseObject? keyInOld = oldObject.get(key); - if (keyInOld != null) { - includedObject = keyInOld; - - //If the object is not fetched || the ids don't match / the pointer changed - if (!includedObject.containsKey(keyVarUpdatedAt) || - includedObject.objectId != keyInObject.objectId) { - includedObject = keyInObject; - //fetch from web including sub objects - //same as down there - final QueryBuilder queryBuilder = QueryBuilder< - ParseObject>(ParseObject(includedObject.parseClassName)) - ..whereEqualTo(keyVarObjectId, includedObject.objectId) - ..includeObject(_toIncludeStringList(paths[key])); - loadingNodes.add(queryBuilder - .query() - .then((ParseResponse parseResponse) { - List? results = parseResponse.results; - if (parseResponse.success && - results != null && - results.length == 1) { - // ignore: deprecated_member_use_from_same_package - object[key] = results[0]; - } - })); - continue; - } else { - // ignore: deprecated_member_use_from_same_package - object[key] = includedObject; - //recursion - loadingNodes - .add(_loadIncludes(includedObject, paths: paths[key])); - continue; - } - } else { + ParseObject? keyInOld = oldObject?.get(key); + if (keyInOld != null) { + //If the object is not fetched || the ids don't match / the pointer changed + if (!keyInOld.containsKey(keyVarUpdatedAt) || + includedObject.objectId != keyInOld.objectId) { //fetch from web including sub objects - //same as up there + //same as down there final QueryBuilder queryBuilder = QueryBuilder< ParseObject>(ParseObject(includedObject.parseClassName)) ..whereEqualTo(keyVarObjectId, includedObject.objectId) @@ -319,12 +287,36 @@ class ParseLiveList { if (parseResponse.success && results != null && results.length == 1) { - // ignore: deprecated_member_use_from_same_package object[key] = results[0]; } })); continue; + } else { + includedObject = keyInOld; + object[key] = includedObject; + //recursion + loadingNodes + .add(_loadIncludes(includedObject, paths: paths[key])); + continue; } + } else { + //fetch from web including sub objects + //same as up there + final QueryBuilder queryBuilder = QueryBuilder< + ParseObject>(ParseObject(includedObject.parseClassName)) + ..whereEqualTo(keyVarObjectId, includedObject.objectId) + ..includeObject(_toIncludeStringList(paths[key])); + loadingNodes.add(queryBuilder + .query() + .then((ParseResponse parseResponse) { + List? results = parseResponse.results; + if (parseResponse.success && + results != null && + results.length == 1) { + object[key] = results[0]; + } + })); + continue; } } } else { From aa80140b719c384fec6838c3dc810261380041d2 Mon Sep 17 00:00:00 2001 From: Maximilian Fischer Date: Wed, 9 Jun 2021 16:27:45 +0200 Subject: [PATCH 17/18] update version to 3.1.0 --- packages/dart/lib/src/base/parse_constants.dart | 2 +- packages/dart/pubspec.yaml | 2 +- packages/flutter/README.md | 2 +- packages/flutter/pubspec.yaml | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/dart/lib/src/base/parse_constants.dart b/packages/dart/lib/src/base/parse_constants.dart index 3794a40db..a1c96cf18 100644 --- a/packages/dart/lib/src/base/parse_constants.dart +++ b/packages/dart/lib/src/base/parse_constants.dart @@ -1,7 +1,7 @@ part of flutter_parse_sdk; // Library -const String keySdkVersion = '3.0.1'; +const String keySdkVersion = '3.1.0'; const String keyLibraryName = 'Flutter Parse SDK'; // End Points diff --git a/packages/dart/pubspec.yaml b/packages/dart/pubspec.yaml index efe5c5410..d4cc690e5 100644 --- a/packages/dart/pubspec.yaml +++ b/packages/dart/pubspec.yaml @@ -1,6 +1,6 @@ name: parse_server_sdk description: Dart plugin for Parse Server, (https://parseplatform.org), (https://back4app.com) -version: 3.0.1 +version: 3.1.0 homepage: https://github.com/phillwiggins/flutter_parse_sdk environment: diff --git a/packages/flutter/README.md b/packages/flutter/README.md index 8befebe22..8559d98a4 100644 --- a/packages/flutter/README.md +++ b/packages/flutter/README.md @@ -14,7 +14,7 @@ This is a work in progress and we are consistently updating it. Please let us kn To install, either add to your pubspec.yaml ```yml dependencies: - parse_server_sdk_flutter: ^3.0.1 + parse_server_sdk_flutter: ^3.1.0 ``` or clone this repository and add to your project. As this is an early development with multiple contributors, it is probably best to download/clone and keep updating as an when a new feature is added. diff --git a/packages/flutter/pubspec.yaml b/packages/flutter/pubspec.yaml index 2c6d22b2c..cb366cfa5 100644 --- a/packages/flutter/pubspec.yaml +++ b/packages/flutter/pubspec.yaml @@ -1,6 +1,6 @@ name: parse_server_sdk_flutter description: Flutter plugin for Parse Server, (https://parseplatform.org), (https://back4app.com) -version: 3.0.1 +version: 3.1.0 homepage: https://github.com/phillwiggins/flutter_parse_sdk environment: @@ -11,7 +11,7 @@ dependencies: sdk: flutter # Uncomment for Release version - parse_server_sdk: ^3.0.1 + parse_server_sdk: ^3.1.0 # Uncomment for local testing #parse_server_sdk: From a1d95b379bab51d6f83beac498b1fcefb9747826 Mon Sep 17 00:00:00 2001 From: Maximilian Fischer Date: Wed, 9 Jun 2021 16:35:49 +0200 Subject: [PATCH 18/18] Update .travis.yml --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index 8819014fe..45d165edc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,6 +13,8 @@ script: - (cd packages/dart && pub get) - (cd packages/dart && dart run build_runner build --delete-conflicting-outputs) - (cd packages/dart && pub run test) + - (cd packages/flutter && flutter pub remove parse_server_sdk) + - (cd packages/flutter && flutter pub add parse_server_sdk --path ../dart) - (cd packages/flutter && flutter pub get) - (cd packages/flutter && flutter test --no-pub test/)