Skip to content

File Support for web #409

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
37 changes: 36 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,8 @@ ParseCoreData().registerUserSubClass((username, password, emailAddress, {client,
```
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 Down Expand Up @@ -809,11 +811,44 @@ 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
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();
}
},
);
}
```

## 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
47 changes: 7 additions & 40 deletions lib/parse_server_sdk.dart
Original file line number Diff line number Diff line change
Expand Up @@ -22,81 +22,48 @@ import 'package:uuid/uuid.dart';
import 'package:xxtea/xxtea.dart';

export 'src/network/parse_live_query.dart'
if (dart.library.js) 'src/network/parse_live_query_web.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/core_store.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';

part 'package:parse_server_sdk/src/objects/response/parse_response_builder.dart';

part 'package:parse_server_sdk/src/objects/response/parse_response_utils.dart';

part 'package:parse_server_sdk/src/objects/response/parse_success_no_results.dart';

part 'package:parse_server_sdk/src/data/core_store.dart';

part 'package:parse_server_sdk/src/storage/core_store_sem_impl.dart';

part 'package:parse_server_sdk/src/storage/core_store_sp_impl.dart';

part 'package:parse_server_sdk/src/storage/xxtea_codec.dart';

part 'src/base/parse_constants.dart';

part 'src/data/parse_core_data.dart';

part 'src/enums/parse_enum_api_rq.dart';

part 'src/network/parse_http_client.dart';

part 'src/network/parse_query.dart';

part 'src/objects/parse_acl.dart';

part 'src/objects/parse_base.dart';

part 'src/objects/parse_cloneable.dart';

part 'src/objects/parse_config.dart';

part 'src/objects/parse_error.dart';

part 'src/objects/parse_file.dart';

part 'src/objects/parse_file_base.dart';
part 'src/objects/parse_file_web.dart';
part 'src/objects/parse_function.dart';

part 'src/objects/parse_geo_point.dart';

part 'src/objects/parse_installation.dart';

part 'src/objects/parse_merge.dart';
part 'src/objects/parse_object.dart';

part 'src/objects/parse_relation.dart';

part 'src/objects/parse_response.dart';

part 'src/objects/parse_session.dart';

part 'src/objects/parse_user.dart';

part 'src/utils/parse_date_format.dart';

part 'src/utils/parse_decoder.dart';

part 'src/utils/parse_encoder.dart';

part 'src/utils/parse_file_extensions.dart';

part 'src/utils/parse_logger.dart';

part 'src/utils/parse_utils.dart';

part 'src/utils/parse_login_helpers.dart';

part 'src/objects/parse_merge.dart';
part 'src/utils/parse_utils.dart';

class Parse {
ParseCoreData data;
Expand Down Expand Up @@ -163,7 +130,7 @@ class Parse {
final ParseHTTPClient _client = client ??
ParseHTTPClient(
sendSessionId:
sendSessionIdByDefault ?? ParseCoreData().autoSendSessionId,
sendSessionIdByDefault ?? ParseCoreData().autoSendSessionId,
securityContext: ParseCoreData().securityContext);

const String className = 'parseBase';
Expand Down
1 change: 1 addition & 0 deletions lib/src/base/parse_constants.dart
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ const String keyClassInstallation = '_Installation';
const String keyGeoPoint = 'GeoPoint';
const String keyFile = 'File';
const String keyRelation = 'Relation';
const String keyFileClassname = 'ParseFile';

// Headers
const String keyHeaderSessionToken = 'X-Parse-Session-Token';
Expand Down
36 changes: 24 additions & 12 deletions lib/src/data/parse_core_data.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,22 @@ class ParseCoreData {
///
/// This class should not be user unless switching servers during the app,
/// which is odd. Should only be user by Parse.init
static Future<void> init(String appId, String serverUrl,
{bool debug,
String appName,
String liveQueryUrl,
String masterKey,
String clientKey,
String sessionId,
bool autoSendSessionId,
SecurityContext securityContext,
CoreStore store,
Map<String, ParseObjectConstructor> registeredSubClassMap,
ParseUserConstructor parseUserConstructor}) async {
static Future<void> init(
String appId,
String serverUrl, {
bool debug,
String appName,
String liveQueryUrl,
String masterKey,
String clientKey,
String sessionId,
bool autoSendSessionId,
SecurityContext securityContext,
CoreStore store,
Map<String, ParseObjectConstructor> registeredSubClassMap,
ParseUserConstructor parseUserConstructor,
ParseFileConstructor parseFileConstructor,
}) async {
_instance = ParseCoreData._init(appId, serverUrl);

_instance.storage ??=
Expand Down Expand Up @@ -59,6 +63,7 @@ class ParseCoreData {
_instance._subClassHandler = ParseSubClassHandler(
registeredSubClassMap: registeredSubClassMap,
parseUserConstructor: parseUserConstructor,
parseFileConstructor: parseFileConstructor,
);
}

Expand All @@ -84,6 +89,10 @@ class ParseCoreData {
_subClassHandler.registerUserSubClass(parseUserConstructor);
}

void registerFileSubClass(ParseFileConstructor parseFileConstructor) {
_subClassHandler.registerFileSubClass(parseFileConstructor);
}

ParseObject createObject(String classname) {
return _subClassHandler.createObject(classname);
}
Expand All @@ -95,6 +104,9 @@ class ParseCoreData {
sessionToken: sessionToken, debug: debug, client: client);
}

ParseFileBase createFile({String url, String name}) =>
_subClassHandler.createFile(name: name, url: url);

/// Sets the current sessionId.
///
/// This is generated when a users logs in, or calls currentUser to update
Expand Down
35 changes: 28 additions & 7 deletions lib/src/data/parse_subclass_handler.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,28 +4,46 @@ typedef ParseObjectConstructor = ParseObject Function();
typedef ParseUserConstructor = ParseUser Function(
String username, String password, String emailAddress,
{String sessionToken, bool debug, ParseHTTPClient client});
typedef ParseFileConstructor = ParseFileBase Function(
{String name, String url});

class ParseSubClassHandler {

ParseSubClassHandler({Map<String, ParseObjectConstructor> registeredSubClassMap,
ParseUserConstructor parseUserConstructor}){
_subClassMap = registeredSubClassMap ?? Map<String, ParseObjectConstructor>();
ParseSubClassHandler(
{Map<String, ParseObjectConstructor> registeredSubClassMap,
ParseUserConstructor parseUserConstructor,
ParseFileConstructor parseFileConstructor}) {
_subClassMap =
registeredSubClassMap ?? Map<String, ParseObjectConstructor>();
_parseUserConstructor = parseUserConstructor;
if (parseFileConstructor != null)
_parseFileConstructor = parseFileConstructor;
}

Map<String, ParseObjectConstructor> _subClassMap;
ParseUserConstructor _parseUserConstructor;
ParseFileConstructor _parseFileConstructor = ({String name, String url}) {
if (kIsWeb) {
return ParseWebFile(null, name: name, url: url);
} else {
return ParseFile(null, name: name, url: url);
}
};

void registerSubClass(
String className, ParseObjectConstructor objectConstructor) {
if (className != keyClassUser &&
className != keyClassInstallation &&
className != keyClassSession)
_subClassMap.putIfAbsent(className, () => objectConstructor);
className != keyClassSession &&
className != keyFileClassname)
_subClassMap[className] = objectConstructor;
}

void registerUserSubClass(ParseUserConstructor parseUserConstructor) {
_parseUserConstructor ??= parseUserConstructor;
_parseUserConstructor = parseUserConstructor;
}

void registerFileSubClass(ParseFileConstructor parseFileConstructor) {
_parseFileConstructor = parseFileConstructor;
}

ParseObject createObject(String classname) {
Expand All @@ -43,4 +61,7 @@ class ParseSubClassHandler {
: ParseUser(username, password, emailAddress,
sessionToken: sessionToken, debug: debug, client: client);
}

ParseFileBase createFile({String name, String url}) =>
_parseFileConstructor(name: name, url: url);
}
48 changes: 10 additions & 38 deletions lib/src/objects/parse_file.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
part of flutter_parse_sdk;

class ParseFile extends ParseObject {
class ParseFile extends ParseFileBase {
/// Creates a new file
///
/// {https://docs.parseplatform.org/rest/guide/#files/}
Expand All @@ -9,41 +9,17 @@ class ParseFile extends ParseObject {
String url,
bool debug,
ParseHTTPClient client,
bool autoSendSessionId})
: super('ParseFile', debug: debug, autoSendSessionId: autoSendSessionId) {
_debug = isDebugEnabled(objectLevelDebug: debug);
_client = client ??
ParseHTTPClient(
sendSessionId:
autoSendSessionId ?? ParseCoreData().autoSendSessionId,
securityContext: ParseCoreData().securityContext);

if (file != null) {
name = path.basename(file.path);
_path = '/files/$name';
} else {
name = name;
url = url;
}
}
bool autoSendSessionId})
: super(
name: file != null ? path.basename(file.path) : name,
url: url,
debug: debug,
client: client,
autoSendSessionId: autoSendSessionId,
);

File file;

String get name => super.get<String>(keyVarName);
set name(String name) => set<String>(keyVarName, name);

String get url => super.get<String>(keyVarURL);
set url(String url) => set<String>(keyVarURL, url);

bool get saved => url != null;

@override
Map<String, dynamic> toJson({bool full = false, bool forApiRQ = false}) =>
<String, String>{'__type': keyFile, 'name': name, 'url': url};

@override
String toString() => json.encode(toJson(full: true));

Future<ParseFile> loadStorage() async {
final Directory tempPath = await getTemporaryDirectory();

Expand All @@ -65,6 +41,7 @@ class ParseFile extends ParseObject {
return this;
}

@override
Future<ParseFile> download() async {
if (url == null) {
return this;
Expand All @@ -81,11 +58,6 @@ class ParseFile extends ParseObject {

/// Uploads a file to Parse Server
@override
Future<ParseResponse> save() async {
return upload();
}

/// Uploads a file to Parse Server
Future<ParseResponse> upload() async {
if (saved) {
//Creates a Fake Response to return the correct result
Expand Down
Loading