diff --git a/README.md b/README.md index 7118c17cd..bf4041208 100644 --- a/README.md +++ b/README.md @@ -119,6 +119,24 @@ class DietPlan extends ParseObject implements ParseCloneable { ``` +When receiving an `ParseObject` from the SDK, you can often provide an instance of your custom object as an copy object. +To always use your custom object class, you can register your subclass at the initialization of the SDK. +```dart +Parse().initialize( + ..., + registeredSubClassMap: { + 'Diet_Plans': () => DietPlan(), + }, + parseUserConstructor: (username, password, emailAddress, {client, debug, sessionToken}) => CustomParseUser(username, password, emailAddress), +); +``` +Additionally you can register `SubClasses` after the initialization of the SDK. +```dart +ParseCoreData().registerSubClass('Diet_Plans', () => DietPlan()); +ParseCoreData().registerUserSubClass((username, password, emailAddress, {client, debug, sessionToken}) => CustomParseUser(username, password, emailAddress)); +``` +Providing a `ParseObject` as described above should still work, even if you have registered a different `SubClass`. + ## Add new values to objects To add a variable to an object call and retrieve it, call diff --git a/lib/parse_server_sdk.dart b/lib/parse_server_sdk.dart index c8950d096..8e8045a3f 100644 --- a/lib/parse_server_sdk.dart +++ b/lib/parse_server_sdk.dart @@ -23,6 +23,7 @@ export 'src/network/parse_live_query.dart' if (dart.library.js) 'src/network/parse_live_query_web.dart'; export 'src/utils/parse_live_list.dart'; +part 'package:parse_server_sdk/src/data/parse_subclass_handler.dart'; part 'package:parse_server_sdk/src/objects/response/parse_error_response.dart'; part 'package:parse_server_sdk/src/objects/response/parse_exception_response.dart'; @@ -111,28 +112,38 @@ class Parse { // debug: true, // liveQuery: true); // ``` - Future initialize(String appId, String serverUrl, - {bool debug = false, - String appName = '', - String liveQueryUrl, - String clientKey, - String masterKey, - String sessionId, - bool autoSendSessionId, - SecurityContext securityContext, - CoreStore coreStore}) async { + Future initialize( + String appId, + String serverUrl, { + bool debug = false, + String appName = '', + String liveQueryUrl, + String clientKey, + String masterKey, + String sessionId, + bool autoSendSessionId, + SecurityContext securityContext, + CoreStore coreStore, + Map registeredSubClassMap, + ParseUserConstructor parseUserConstructor, + }) async { final String url = removeTrailingSlash(serverUrl); - await ParseCoreData.init(appId, url, - debug: debug, - appName: appName, - liveQueryUrl: liveQueryUrl, - masterKey: masterKey, - clientKey: clientKey, - sessionId: sessionId, - autoSendSessionId: autoSendSessionId, - securityContext: securityContext, - store: coreStore); + await ParseCoreData.init( + appId, + url, + debug: debug, + appName: appName, + liveQueryUrl: liveQueryUrl, + masterKey: masterKey, + clientKey: clientKey, + sessionId: sessionId, + autoSendSessionId: autoSendSessionId, + securityContext: securityContext, + store: coreStore, + registeredSubClassMap: registeredSubClassMap, + parseUserConstructor: parseUserConstructor, + ); _hasBeenInitialized = true; diff --git a/lib/src/data/parse_core_data.dart b/lib/src/data/parse_core_data.dart index 7ffdaa7ce..22bc19a47 100644 --- a/lib/src/data/parse_core_data.dart +++ b/lib/src/data/parse_core_data.dart @@ -22,8 +22,10 @@ class ParseCoreData { String clientKey, String sessionId, bool autoSendSessionId, - SecurityContext securityContext, - CoreStore store}) async { + SecurityContext securityContext, + CoreStore store, + Map registeredSubClassMap, + ParseUserConstructor parseUserConstructor}) async { _instance = ParseCoreData._init(appId, serverUrl); _instance.storage ??= @@ -53,6 +55,11 @@ class ParseCoreData { if (securityContext != null) { _instance.securityContext = securityContext; } + + _instance._subClassHandler = ParseSubClassHandler( + registeredSubClassMap: registeredSubClassMap, + parseUserConstructor: parseUserConstructor, + ); } String appName; @@ -66,6 +73,27 @@ class ParseCoreData { SecurityContext securityContext; bool debug; CoreStore storage; + ParseSubClassHandler _subClassHandler; + + void registerSubClass( + String className, ParseObjectConstructor objectConstructor) { + _subClassHandler.registerSubClass(className, objectConstructor); + } + + void registerUserSubClass(ParseUserConstructor parseUserConstructor) { + _subClassHandler.registerUserSubClass(parseUserConstructor); + } + + ParseObject createObject(String classname) { + return _subClassHandler.createObject(classname); + } + + ParseUser createParseUser( + String username, String password, String emailAddress, + {String sessionToken, bool debug, ParseHTTPClient client}) { + return _subClassHandler.createParseUser(username, password, emailAddress, + sessionToken: sessionToken, debug: debug, client: client); + } /// Sets the current sessionId. /// diff --git a/lib/src/data/parse_subclass_handler.dart b/lib/src/data/parse_subclass_handler.dart new file mode 100644 index 000000000..6f2a8db49 --- /dev/null +++ b/lib/src/data/parse_subclass_handler.dart @@ -0,0 +1,46 @@ +part of flutter_parse_sdk; + +typedef ParseObjectConstructor = ParseObject Function(); +typedef ParseUserConstructor = ParseUser Function( + String username, String password, String emailAddress, + {String sessionToken, bool debug, ParseHTTPClient client}); + +class ParseSubClassHandler { + + ParseSubClassHandler({Map registeredSubClassMap, + ParseUserConstructor parseUserConstructor}){ + _subClassMap = registeredSubClassMap ?? Map(); + _parseUserConstructor = parseUserConstructor; + } + + Map _subClassMap; + ParseUserConstructor _parseUserConstructor; + + void registerSubClass( + String className, ParseObjectConstructor objectConstructor) { + if (className != keyClassUser && + className != keyClassInstallation && + className != keyClassSession) + _subClassMap.putIfAbsent(className, () => objectConstructor); + } + + void registerUserSubClass(ParseUserConstructor parseUserConstructor) { + _parseUserConstructor ??= parseUserConstructor; + } + + ParseObject createObject(String classname) { + if (classname == keyClassUser) return createParseUser(null, null, null); + if (_subClassMap.containsKey(classname)) return _subClassMap[classname](); + return ParseObject(classname); + } + + ParseUser createParseUser( + String username, String password, String emailAddress, + {String sessionToken, bool debug, ParseHTTPClient client}) { + return _parseUserConstructor != null + ? _parseUserConstructor(username, password, emailAddress, + sessionToken: sessionToken, debug: debug, client: client) + : ParseUser(username, password, emailAddress, + sessionToken: sessionToken, debug: debug, client: client); + } +} diff --git a/lib/src/network/parse_live_query.dart b/lib/src/network/parse_live_query.dart index 6a34af0c0..88aa868df 100644 --- a/lib/src/network/parse_live_query.dart +++ b/lib/src/network/parse_live_query.dart @@ -408,13 +408,15 @@ class Client { if (actionData.containsKey('object')) { final Map map = actionData['object']; final String className = map['className']; - if (className == '_User') { + if (className == keyClassUser) { subscription.eventCallbacks[actionData['op']]( - (subscription.copyObject ?? ParseUser(null, null, null)) + (subscription.copyObject ?? + ParseCoreData.instance.createParseUser(null, null, null)) .fromJson(map)); } else { subscription.eventCallbacks[actionData['op']]( - (subscription.copyObject ?? ParseObject(className)) + (subscription.copyObject ?? + ParseCoreData.instance.createObject(className)) .fromJson(map)); } } else { diff --git a/lib/src/network/parse_live_query_web.dart b/lib/src/network/parse_live_query_web.dart index 7ece31398..d9a6f7602 100644 --- a/lib/src/network/parse_live_query_web.dart +++ b/lib/src/network/parse_live_query_web.dart @@ -406,13 +406,15 @@ class Client { if (actionData.containsKey('object')) { final Map map = actionData['object']; final String className = map['className']; - if (className == '_User') { + if (className == keyClassUser) { subscription.eventCallbacks[actionData['op']]( - (subscription.copyObject ?? ParseUser(null, null, null)) + (subscription.copyObject ?? + ParseCoreData.instance.createParseUser(null, null, null)) .fromJson(map)); } else { subscription.eventCallbacks[actionData['op']]( - (subscription.copyObject ?? ParseObject(className)) + (subscription.copyObject ?? + ParseCoreData.instance.createObject(className)) .fromJson(map)); } } else { diff --git a/lib/src/network/parse_query.dart b/lib/src/network/parse_query.dart index 83cb866ad..90af6f513 100644 --- a/lib/src/network/parse_query.dart +++ b/lib/src/network/parse_query.dart @@ -5,6 +5,9 @@ class QueryBuilder { /// Class to create complex queries QueryBuilder(this.object) : super(); + QueryBuilder.name(String classname) + : this(ParseCoreData.instance.createObject(classname)); + QueryBuilder.or(this.object, List> list) { if (list != null) { String query = '"\$or":['; diff --git a/lib/src/objects/parse_user.dart b/lib/src/objects/parse_user.dart index fc46d570a..35e8b3924 100644 --- a/lib/src/objects/parse_user.dart +++ b/lib/src/objects/parse_user.dart @@ -76,7 +76,8 @@ class ParseUser extends ParseObject implements ParseCloneable { static ParseUser createUser( [String username, String password, String emailAddress]) { - return ParseUser(username, password, emailAddress); + return ParseCoreData.instance + .createParseUser(username, password, emailAddress); } /// Gets the current user from the server @@ -327,6 +328,19 @@ class ParseUser extends ParseObject implements ParseCloneable { } } + @override + Future update() async { + if (objectId == null) { + return await signUp(); + } else { + final ParseResponse response = await super.update(); + if (response.success) { + await _onResponseSuccess(); + } + return response; + } + } + Future _onResponseSuccess() async { await saveInStorage(keyParseStoreUser); } @@ -412,7 +426,8 @@ class ParseUser extends ParseObject implements ParseCloneable { } } - static ParseUser _getEmptyUser() => ParseUser(null, null, null); + static ParseUser _getEmptyUser() => + ParseCoreData.instance.createParseUser(null, null, null); static Future _getInstallationId() async { final ParseInstallation parseInstallation = diff --git a/lib/src/storage/core_store_sem_impl.dart b/lib/src/storage/core_store_sem_impl.dart index b2c67d35f..e63ee6b0d 100644 --- a/lib/src/storage/core_store_sem_impl.dart +++ b/lib/src/storage/core_store_sem_impl.dart @@ -12,7 +12,7 @@ class CoreStoreSembastImp implements CoreStore { factory ??= databaseFactoryIo; final SembastCodec codec = getXXTeaSembastCodec(password: password); String dbDirectory = ''; - if (Platform.isIOS || Platform.isAndroid) + if (Platform.isIOS || Platform.isAndroid || Platform.isMacOS) dbDirectory = (await getApplicationDocumentsDirectory()).path; final String dbPath = path.join('$dbDirectory/parse', 'parse.db'); final Database db = await factory.openDatabase(dbPath, codec: codec); diff --git a/lib/src/utils/parse_decoder.dart b/lib/src/utils/parse_decoder.dart index 73a01d557..f6628dc88 100644 --- a/lib/src/utils/parse_decoder.dart +++ b/lib/src/utils/parse_decoder.dart @@ -58,17 +58,9 @@ dynamic parseDecode(dynamic value) { final String val = map['base64']; return base64.decode(val); case 'Pointer': - final String className = map['className']; - if (className == '_User') { - return ParseUser._getEmptyUser().fromJson(map); - } - return ParseObject(className).fromJson(map); case 'Object': final String className = map['className']; - if (className == '_User') { - return ParseUser._getEmptyUser().fromJson(map); - } - return ParseObject(className).fromJson(map); + return ParseCoreData.instance.createObject(className).fromJson(map); case 'File': return ParseFile(null, url: map['url'], name: map['name']) .fromJson(map); @@ -86,15 +78,15 @@ dynamic parseDecode(dynamic value) { /// Decoding from locally cached JSON if (map.containsKey('className')) { switch (map['className']) { - case '_User': - return ParseUser._getEmptyUser().fromJson(map); case 'GeoPoint': final num latitude = map['latitude'] ?? 0.0; final num longitude = map['longitude'] ?? 0.0; return ParseGeoPoint( latitude: latitude.toDouble(), longitude: longitude.toDouble()); default: - return ParseObject(map['className']).fromJson(map); + return ParseCoreData.instance + .createObject(map['className']) + .fromJson(map); } }