Skip to content

LiveList: preloadedColumns #458

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
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
81 changes: 49 additions & 32 deletions packages/dart/lib/src/utils/parse_live_list.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,34 +2,47 @@ part of flutter_parse_sdk;

// ignore_for_file: invalid_use_of_protected_member
class ParseLiveList<T extends ParseObject> {
ParseLiveList._(this._query, this._listeningIncludes, this._lazyLoading) {
ParseLiveList._(this._query, this._listeningIncludes, this._lazyLoading,
{List<String> preloadedColumns = const <String>[]})
: _preloadedColumns = preloadedColumns {
_debug = isDebugEnabled();
}

static Future<ParseLiveList<T>> create<T extends ParseObject>(
QueryBuilder<T> _query,
{bool listenOnAllSubItems,
List<String> listeningIncludes,
bool lazyLoading = true}) {
QueryBuilder<T> _query, {
bool listenOnAllSubItems,
List<String> listeningIncludes,
bool lazyLoading = true,
List<String> preloadedColumns,
}) {
final ParseLiveList<T> parseLiveList = ParseLiveList<T>._(
_query,
listenOnAllSubItems == true
? _toIncludeMap(
_query.limiters['include']?.toString()?.split(',') ??
<String>[])
: _toIncludeMap(listeningIncludes ?? <String>[]),
lazyLoading);
_query,
listenOnAllSubItems == true
? _toIncludeMap(
_query.limiters['include']?.toString()?.split(',') ?? <String>[])
: _toIncludeMap(listeningIncludes ?? <String>[]),
lazyLoading,
preloadedColumns: preloadedColumns,
);

return parseLiveList._init().then((_) {
return parseLiveList;
});
}

final QueryBuilder<T> _query;
//The included Items, where LiveList should look for updates.
final Map<String, dynamic> _listeningIncludes;
final bool _lazyLoading;
final List<String> _preloadedColumns;

List<ParseLiveListElement<T>> _list = List<ParseLiveListElement<T>>();
StreamController<ParseLiveListEvent<T>> _eventStreamController;
int _nextID = 0;
bool _debug;

int get nextID => _nextID++;

/// is object1 listed after object2?
/// can return null
bool after(T object1, T object2) {
Expand Down Expand Up @@ -84,14 +97,6 @@ class ParseLiveList<T extends ParseObject> {
return null;
}

int get nextID => _nextID++;

final QueryBuilder<T> _query;
//The included Items, where LiveList should look for updates.
final Map<String, dynamic> _listeningIncludes;

final bool _lazyLoading;

int get size {
return _list.length;
}
Expand Down Expand Up @@ -128,17 +133,17 @@ class ParseLiveList<T extends ParseObject> {
if (_debug)
print('ParseLiveList: lazyLoading is ${_lazyLoading ? 'on' : 'off'}');
if (_lazyLoading) {
if (query.limiters.containsKey('order')) {
query.keysToReturn(
query.limiters['order'].toString().split(',').map((String string) {
if (string.startsWith('-')) {
return string.substring(1);
}
return string;
}).toList());
} else {
query.keysToReturn(List<String>());
}
final List<String> keys = _preloadedColumns?.toList() ?? <String>[];
if (_lazyLoading && query.limiters.containsKey('order'))
keys.addAll(
query.limiters['order'].toString().split(',').map((String string) {
if (string.startsWith('-')) {
return string.substring(1);
}
return string;
}),
);
query.keysToReturn(keys);
}
return await query.query<T>();
}
Expand Down Expand Up @@ -449,6 +454,13 @@ class ParseLiveList<T extends ParseObject> {
return null;
}

T getPreLoadedAt(int index) {
if (index < _list.length) {
return _list[index].object;
}
return null;
}

void dispose() {
if (_liveQuerySubscription != null) {
LiveQuery().client.unSubscribe(_liveQuerySubscription);
Expand Down Expand Up @@ -739,12 +751,17 @@ typedef StreamGetter<T extends ParseObject> = Stream<T> Function();
typedef DataGetter<T extends ParseObject> = T Function();

class ParseLiveListElementSnapshot<T extends ParseObject> {
ParseLiveListElementSnapshot({this.loadedData, this.error});
ParseLiveListElementSnapshot(
{this.loadedData, this.error, this.preLoadedData});

final T loadedData;
final T preLoadedData;

final ParseError error;

bool get hasData => loadedData != null;

bool get hasPreLoadedData => preLoadedData != null;

bool get failed => error != null;
}
32 changes: 32 additions & 0 deletions packages/flutter/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -597,6 +597,38 @@ To activate listening for updates on all included objects, add `listenOnAllSubIt
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.

### Lazy loading
By default, ParseLiveList lazy loads the content.
You can avoid that by setting `lazyLoading: false`.
In case you want to use lazyLoading but you need some columns to be preloaded, you can provide a list of `preloadedColumns`.
Preloading fields of a pointer is supported by using the dot-notation.
You can access the preloaded data is stored in the `preLoadedData` field of the `ParseLiveListElementSnapshot`.
```dart
ParseLiveListWidget<ParseObject>(
query: query,
lazyLoading: true,
preloadedColumns: ["test1", "sender.username"],
childBuilder:
(BuildContext context, ParseLiveListElementSnapshot<ParseObject> snapshot) {
if (snapshot.failed) {
return const Text('something went wrong!');
} else if (snapshot.hasData) {
return ListTile(
title: Text(
snapshot.loadedData.get<String>("text"),
),
);
} else {
return ListTile(
title: Text(
"loading comment from: ${snapshot.preLoadedData?.get<ParseObject>("sender")?.get<String>("username")}",
),
);
}
},
);
```

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

## Users
Expand Down
28 changes: 20 additions & 8 deletions packages/flutter/lib/src/utils/parse_live_list.dart
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ class ParseLiveListWidget<T extends sdk.ParseObject> extends StatefulWidget {
this.listenOnAllSubItems,
this.listeningIncludes,
this.lazyLoading = true,
this.preloadedColumns,
}) : super(key: key);

final sdk.QueryBuilder<T> query;
Expand All @@ -42,6 +43,7 @@ class ParseLiveListWidget<T extends sdk.ParseObject> extends StatefulWidget {
final List<String> listeningIncludes;

final bool lazyLoading;
final List<String> preloadedColumns;

@override
_ParseLiveListWidgetState<T> createState() => _ParseLiveListWidgetState<T>(
Expand All @@ -50,6 +52,7 @@ class ParseLiveListWidget<T extends sdk.ParseObject> extends StatefulWidget {
listenOnAllSubItems: listenOnAllSubItems,
listeningIncludes: listeningIncludes,
lazyLoading: lazyLoading,
preloadedColumns: preloadedColumns,
);

static Widget defaultChildBuilder<T extends sdk.ParseObject>(
Expand Down Expand Up @@ -79,12 +82,14 @@ class _ParseLiveListWidgetState<T extends sdk.ParseObject>
@required this.removedItemBuilder,
bool listenOnAllSubItems,
List<String> listeningIncludes,
bool lazyLoading = true}) {
bool lazyLoading = true,
List<String> preloadedColumns}) {
sdk.ParseLiveList.create(
query,
listenOnAllSubItems: listenOnAllSubItems,
listeningIncludes: listeningIncludes,
lazyLoading: lazyLoading,
preloadedColumns: preloadedColumns,
).then((sdk.ParseLiveList<T> value) {
setState(() {
_liveList = value;
Expand All @@ -107,6 +112,7 @@ class _ParseLiveListWidgetState<T extends sdk.ParseObject>
sizeFactor: animation,
duration: widget.duration,
loadedData: () => event.object,
preLoadedData: () => event.object,
),
duration: widget.duration);
}
Expand Down Expand Up @@ -146,6 +152,7 @@ class _ParseLiveListWidgetState<T extends sdk.ParseObject>
_liveList?.getIdentifier(index) ?? '_NotFound'),
stream: () => _liveList?.getAt(index),
loadedData: () => _liveList?.getLoadedAt(index),
preLoadedData: () => _liveList?.getPreLoadedAt(index),
sizeFactor: animation,
duration: widget.duration,
childBuilder:
Expand All @@ -168,39 +175,44 @@ class ParseLiveListElementWidget<T extends sdk.ParseObject>
{Key key,
this.stream,
this.loadedData,
this.preLoadedData,
@required this.sizeFactor,
@required this.duration,
@required this.childBuilder})
: super(key: key);

final sdk.StreamGetter<T> stream;
final sdk.DataGetter<T> loadedData;
final sdk.DataGetter<T> preLoadedData;
final Animation<double> sizeFactor;
final Duration duration;
final ChildBuilder<T> childBuilder;

@override
_ParseLiveListElementWidgetState<T> createState() {
return _ParseLiveListElementWidgetState<T>(loadedData, stream);
return _ParseLiveListElementWidgetState<T>(
loadedData, preLoadedData, stream);
}
}

class _ParseLiveListElementWidgetState<T extends sdk.ParseObject>
extends State<ParseLiveListElementWidget<T>>
with SingleTickerProviderStateMixin {
_ParseLiveListElementWidgetState(
sdk.DataGetter<T> loadedDataGetter, sdk.StreamGetter<T> stream) {
_snapshot =
sdk.ParseLiveListElementSnapshot<T>(loadedData: loadedDataGetter());
_ParseLiveListElementWidgetState(sdk.DataGetter<T> loadedDataGetter,
sdk.DataGetter<T> preLoadedDataGetter, sdk.StreamGetter<T> stream) {
_snapshot = sdk.ParseLiveListElementSnapshot<T>(
loadedData: loadedDataGetter(), preLoadedData: preLoadedDataGetter());
if (stream != null) {
_streamSubscription = stream().listen(
(T data) {
if (widget != null) {
setState(() {
_snapshot = sdk.ParseLiveListElementSnapshot<T>(loadedData: data);
_snapshot = sdk.ParseLiveListElementSnapshot<T>(
loadedData: data, preLoadedData: data);
});
} else {
_snapshot = sdk.ParseLiveListElementSnapshot<T>(loadedData: data);
_snapshot = sdk.ParseLiveListElementSnapshot<T>(
loadedData: data, preLoadedData: data);
}
},
onError: (Object error) {
Expand Down