Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
ed4d390
Release 1.0.27 - Create base branch
phillwiggins Mar 28, 2020
f095e6d
Fix for #347 (include in LiveList) (#348)
fischerscode Apr 4, 2020
059088e
LiveList autoupdate included sub-objects (#349)
fischerscode Apr 7, 2020
d6948d7
LiveList: sync subObjects after reconnect (#352)
fischerscode Apr 9, 2020
b76bef8
Update README.md (#353)
fischerscode Apr 15, 2020
057f2b3
ISSUE-351: Replaced isTypeOfException with Exception (#355)
PawlikMichal25 Apr 15, 2020
f107ff5
RegisterSubClass and smaler changes (#368)
fischerscode Apr 29, 2020
f5ce146
Update README.md (#367)
phanirithvij May 10, 2020
17a4a78
Save installationId when new installation created. Send installationI…
Danaru87 May 18, 2020
023d98c
ParseLiveElement (#376)
fischerscode May 18, 2020
58cd228
News methods for ParseQuery - whereMatchesKeyInQuery and whereDoesNo…
RodrigoSMarques May 19, 2020
e8ff0a0
Update README.md
phillwiggins May 26, 2020
78bb537
Update README.md
phillwiggins May 26, 2020
80e486c
Add google login helper (#387)
vreamer Jun 7, 2020
1e47689
Update parse_response_builder.dart (#390)
ZAYEC77 Jun 11, 2020
0672d49
[web] Check web before Platform.isX (#392)
nstrelow Jun 11, 2020
db10be3
Make web compatible calls for locale and skip PackageInfo (#395)
nstrelow Jun 11, 2020
16014e1
Added ParseFile check to _canBeSerialized() (#396)
hyperrecursive Jun 26, 2020
fd49451
Update README.md (#397)
RodrigoSMarques Jun 26, 2020
87cbba8
ParseLiveList: first implementation of lazyLoading (#407)
fischerscode Aug 5, 2020
fedd569
Update parse_live_list.dart (#406)
fischerscode Aug 9, 2020
7449b31
File Support for web (#409)
fischerscode Aug 9, 2020
92a26dc
add an other file example to README (#410)
fischerscode Aug 9, 2020
6b6374b
Web file upload: set content type to "text/plain" incase neighter nam…
fischerscode Aug 9, 2020
23f911e
LiveQuery reconnecting intervals (#414)
fischerscode Aug 11, 2020
c95cc4b
replace kIsWeb with its definition (#415)
fischerscode Aug 12, 2020
7d3c08b
release/1.0.27 update dependencies
phillwiggins Aug 20, 2020
42a1fcc
Release/1.0.27 (#427)
Danaru87 Aug 28, 2020
039cf29
Release 1.0.27 - Pre-release process
phillwiggins Aug 28, 2020
b695f37
Release 1.0.27 - Pre-release process
phillwiggins Aug 28, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
## 1.0.27
User login / signUp / loginAnonymous delete SessionId stored in device before calling server

## 1.0.26
LiveList
Bug fixes
Sembast update

## 1.0.25
Update dependencies
Expand Down
200 changes: 189 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
## Parse For Flutter!
Hi, this is a Flutter plugin that allows communication with a Parse Server, (https://parseplatform.org) either hosted on your own server or another, like (http://Back4App.com).

This is a work in project and we are consistently updating it. Please let us know if you think anything needs changing/adding, and more than ever, please do join in on this project (Even if it is just to improve our documentation.
This is a work in progress and we are consistently updating it. Please let us know if you think anything needs changing/adding, and more than ever, please do join in on this project. (Even if it is just to improve our documentation)

## Join in!
Want to get involved? Join our Slack channel and help out! (http://flutter-parse-sdk.slack.com)
Expand All @@ -13,7 +13,7 @@ Want to get involved? Join our Slack channel and help out! (http://flutter-parse
To install, either add to your pubspec.yaml
```yml
dependencies:
parse_server_sdk: ^1.0.26
parse_server_sdk: ^1.0.27
```
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.

Expand Down Expand Up @@ -49,6 +49,15 @@ It's possible to add other parameters to work with your instance of Parse Server
coreStore: await CoreStoreSharedPrefsImp.getInstance()); // Local data storage method. Will use SharedPreferences instead of Sembast as an internal DB
```


#### Early Web support
Currently this requires adding `X-Parse-Installation-Id` as an allowed header to parse-server.
When running directly via docker, set the env var `PARSE_SERVER_ALLOW_HEADERS=X-Parse-Installation-Id`.
When running via express, set [ParseServerOptions](https://parseplatform.org/parse-server/api/master/ParseServerOptions.html) `allowHeaders: ['X-Parse-Installation-Id']`.

Be aware that for web ParseInstallation does include app name, version or package identifier.


## Objects
You can create custom objects by calling:
```dart
Expand Down Expand Up @@ -98,7 +107,7 @@ The features available are:-
* Array Operators

## Custom Objects
You can create your own ParseObjects or convert your existing objects into Parse Objects by doing the following:
You can create your own `ParseObjects` or convert your existing objects into Parse Objects by doing the following:

```dart
class DietPlan extends ParseObject implements ParseCloneable {
Expand All @@ -119,6 +128,26 @@ 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: <String, ParseObjectConstructor>{
'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`.

For custom file classes have a lock at [here](#File).

## Add new values to objects
To add a variable to an object call and retrieve it, call

Expand All @@ -128,7 +157,7 @@ var randomInt = dietPlan.get<int>('RandomInt');
```

## Save objects using pins
You can now save an object by calling .pin() on an instance of an object
You can now save an object by calling `.pin()` on an instance of an object

```dart
dietPlan.pin();
Expand Down Expand Up @@ -229,6 +258,26 @@ if (response.success) {
}
```

if you want to find objects that match one of several queries, you can use __QueryBuilder.or__ method to construct a query that is an OR of the queries passed in. For instance if you want to find players who either have a lot of wins or a few wins, you can do:
```dart
ParseObject playerObject = ParseObject("Player");

QueryBuilder<ParseObject> lotsOfWins =
QueryBuilder<ParseObject>(playerObject))
..whereGreaterThan('wins', 50);

QueryBuilder<ParseObject> fewWins =
QueryBuilder<ParseObject>(playerObject)
..whereLessThan('wins', 5);

QueryBuilder<ParseObject> mainQuery = QueryBuilder.or(
playerObject,
[lotsOfWins, fewWins],
);

var apiResponse = await mainQuery.query();
```

The features available are:-
* Equals
* Contains
Expand All @@ -245,6 +294,10 @@ The features available are:-
* WithinKilometers
* WithinRadians
* WithinGeoBox
* MatchesQuery
* DoesNotMatchQuery
* MatchesKeyInQuery
* DoesNotMatchKeyInQuery
* Regex
* Order
* Limit
Expand Down Expand Up @@ -288,6 +341,47 @@ QueryBuilder<ParseObject> queryComment =
var apiResponse = await queryComment.query();
```

You can use the __whereMatchesKeyInQuery__ method to get objects where a key matches the value of a key in a set of objects resulting from another query. For example, if you have a class containing sports teams and you store a user’s hometown in the user class, you can issue one query to find the list of users whose hometown teams have winning records. The query would look like::

```dart
QueryBuilder<ParseObject> teamQuery =
QueryBuilder<ParseObject>(ParseObject('Team'))
..whereGreaterThan('winPct', 0.5);

QueryBuilder<ParseUser> userQuery =
QueryBuilder<ParseUser>ParseUser.forQuery())
..whereMatchesKeyInQuery('hometown', 'city', teamQuery);

var apiResponse = await userQuery.query();
```

Conversely, to get objects where a key does not match the value of a key in a set of objects resulting from another query, use __whereDoesNotMatchKeyInQuery__. For example, to find users whose hometown teams have losing records:

```dart
QueryBuilder<ParseObject> teamQuery =
QueryBuilder<ParseObject>(ParseObject('Team'))
..whereGreaterThan('winPct', 0.5);

QueryBuilder<ParseUser> losingUserQuery =
QueryBuilder<ParseUser>ParseUser.forQuery())
..whereDoesNotMatchKeyInQuery('hometown', 'city', teamQuery);

var apiResponse = await losingUserQuery.query();
```

To filter rows based on objectId’s from pointers in a second table, you can use dot notation:
```dart
QueryBuilder<ParseObject> rolesOfTypeX =
QueryBuilder<ParseObject>(ParseObject('Role'))
..whereEqualTo('type', 'x');

QueryBuilder<ParseObject> groupsWithRoleX =
QueryBuilder<ParseObject>(ParseObject('Group')))
..whereMatchesKeyInQuery('objectId', 'belongsTo.objectId', rolesOfTypeX);

var apiResponse = await groupsWithRoleX.query();
```

## Counting Objects
If you only care about the number of games played by a particular player:

Expand Down Expand Up @@ -430,10 +524,17 @@ LiveQuery server.
liveQuery.client.unSubscribe(subscription);
```

__Disconnection__
In case the client's connection to the server breaks,
LiveQuery will automatically try to reconnect.
LiveQuery will wait at increasing intervals between reconnection attempts.
By default, these intervals are set to `[0, 500, 1000, 2000, 5000, 10000]` for mobile and `[0, 500, 1000, 2000, 5000]` for web.
You can change these by providing a custom list using the `liveListRetryIntervals` parameter at `Parse.initialize()` ("-1" means "do not try to reconnect").

## ParseLiveList
ParseLiveList makes implementing a dynamic List as simple as possible.


#### General Use
It ships with the ParseLiveList class itself, this class manages all elements of the list, sorts them,
keeps itself up to date and Notifies you on changes.

Expand All @@ -451,13 +552,13 @@ ParseLiveListWidget<ParseObject>(
query: query,
reverse: false,
childBuilder:
(BuildContext context, bool failed, ParseObject loadedData) {
if (failed) {
(BuildContext context, ParseLiveListElementSnapshot<ParseObject> snapshot) {
if (snapshot.failed) {
return const Text('something went wrong!');
} else if (loadedData != null) {
} else if (snapshot.hasData) {
return ListTile(
title: Text(
loadedData.get("text"),
snapshot.loadedData.get("text"),
),
);
} else {
Expand Down Expand Up @@ -487,8 +588,17 @@ ParseLiveListWidget<ParseObject>(
duration: Duration(seconds: 1),
);
```
### included Sub-Objects
By default, ParseLiveQuery will provide you with all the objects you included in your Query like this:
```dart
queryBuilder.includeObject(/*List of all the included sub-objects*/);
```
ParseLiveList will not listen for updates on this objects by default.
To activate listening for updates on all included objects, add `listenOnAllSubItems: true` to your ParseLiveListWidgets constructor.
If you want ParseLiveList to listen for updates on only some sub-objects, use `listeningIncludes: const <String>[/*all the included sub-objects*/]` instead.
Just as QueryBuilder, ParseLiveList supports nested sub-objects too.

Note: To use this features you have to enable [Live Queries](#live-queries) first.
**NOTE:** To use this features you have to enable [Live Queries](#live-queries) first.

## Users
You can create and control users just as normal using this SDK.
Expand Down Expand Up @@ -566,6 +676,24 @@ Other user features are:-
}
```

For Google and the example below, we used the library provided at https://pub.dev/packages/google_sign_in

```
class OAuthLogin {
final GoogleSignIn _googleSignIn = GoogleSignIn( scopes: ['email', 'https://www.googleapis.com/auth/contacts.readonly'] );

sigInGoogle() async {
GoogleSignInAccount account = await _googleSignIn.signIn();
GoogleSignInAuthentication authentication = await account.authentication;
await ParseUser.loginWith(
'google',
google(_googleSignIn.currentUser.id,
authentication.accessToken,
authentication.idToken));
}
}
```

## Security for Objects - ParseACL
For any object, you can specify which users are allowed to read the object, and which users are allowed to modify an object.
To support this type of security, each object has an access control list, implemented by the __ParseACL__ class.
Expand Down Expand Up @@ -690,11 +818,61 @@ QueryBuilder<ParseObject> query =
..whereRelatedTo('fruits', 'DietPlan', DietPlan.objectId);
```

## File
There are three different file classes in this SDK:
- `ParseFileBase` is and abstract class and is the foundation of every file class that can be handled by this SDK.
- `ParseFile` (former the only file class in the SDK) extends ParseFileBase and is by default used as the file class on every platform but web.
This class uses a `File` from `dart:io` for storing the raw file.
- `ParseWebFile` is the equivalent to ParseFile used at Flutter Web.
This class uses an `Uint8List` for storing the raw file.

These classes are used by default to represent files, but you can also build your own class extending ParseFileBase and provide a custom `ParseFileConstructor` similar to the `SubClasses`.

Have a look at the example application for a small (non web) example.


```dart
//A short example for showing an image from a ParseFileBase
Widget buildImage(ParseFileBase image){
return FutureBuilder<ParseFileBase>(
future: image.download(),
builder: (BuildContext context,
AsyncSnapshot<ParseFileBase> snapshot) {
if (snapshot.hasData) {
if (kIsWeb) {
return Image.memory((snapshot.data as ParseWebFile).file);
} else {
return Image.file((snapshot.data as ParseFile).file);
}
} else {
return CircularProgressIndicator();
}
},
);
}
```
```dart
//A short example for storing a selected picture
//libraries: image_picker (https://pub.dev/packages/image_picker), image_picker_for_web (https://pub.dev/packages/image_picker_for_web)
PickedFile pickedFile = await ImagePicker().getImage(source: ImageSource.gallery);
ParseFileBase parseFile;
if (kIsWeb) {
//Seems weird, but this lets you get the data from the selected file as an Uint8List very easily.
ParseWebFile file = ParseWebFile(null, name: null, url: pickedFile.path);
await file.download();
parseFile = ParseWebFile(file.file, name: file.name);
} else {
parseFile = ParseFile(File(pickedFile.path));
}
someParseObject.set("image", parseFile);
//This saves the ParseObject as well as all of its children, and the ParseFileBase is such a child.
await someParseObject.save();
```

## Other Features of this library
Main:
* Installation (View the example application)
* GeoPoints (View the example application)
* Files (View the example application)
* Persistent storage
* Debug Mode - Logging API calls
* Manage Session ID's tokens
Expand Down
4 changes: 2 additions & 2 deletions example/lib/data/base/api_error.dart
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
class ApiError {
ApiError(this.code, this.message, this.isTypeOfException, this.type);
ApiError(this.code, this.message, this.exception, this.type);

final int code;
final String message;
final bool isTypeOfException;
final Exception exception;
final String type;
}
2 changes: 1 addition & 1 deletion example/lib/data/base/api_response.dart
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,6 @@ ApiError getApiError(ParseError response) {
return null;
}

return ApiError(response.code, response.message, response.isTypeOfException,
return ApiError(response.code, response.message, response.exception,
response.type);
}
Original file line number Diff line number Diff line change
Expand Up @@ -135,15 +135,13 @@ class DietPlanProviderDB implements DietPlanProviderContract {
final Map<String, dynamic> values = convertItemToStorageMap(item);
final Finder finder =
Finder(filter: Filter.equals('objectId', item.objectId));
final int returnedCount =
await _store.update(_db, values, finder: finder);
final int returnedCount = await _store.update(_db, values, finder: finder);

if (returnedCount == 0) {
return add(item);
}

return ApiResponse(
true, 200, <dynamic>[item], null);
return ApiResponse(true, 200, <dynamic>[item], null);
}

Map<String, dynamic> convertItemToStorageMap(DietPlan item) {
Expand Down Expand Up @@ -171,6 +169,6 @@ class DietPlanProviderDB implements DietPlanProviderContract {
}
}

static ApiError error = ApiError(1, 'No records found', false, '');
static ApiError error = ApiError(1, 'No records found', null, '');
ApiResponse errorResponse = ApiResponse(false, 1, null, error);
}
2 changes: 1 addition & 1 deletion example/lib/data/repositories/user/provider_db_user.dart
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,6 @@ class UserProviderDB implements UserProviderContract {
}
}

static ApiError error = ApiError(1, 'No records found', false, '');
static ApiError error = ApiError(1, 'No records found', null, '');
ApiResponse errorResponse = ApiResponse(false, 1, null, error);
}
2 changes: 1 addition & 1 deletion example/lib/pages/home_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ class _HomePageState extends State<HomePage> {
title: const Text('Parse Server demo'),
actions: <Widget>[
FlatButton(
child: Text('Logout',
child: const Text('Logout',
style: TextStyle(fontSize: 17.0, color: Colors.white)),
onPressed: () async {
final ParseUser user = await ParseUser.currentUser();
Expand Down
Loading