Add pagination to the face search API

This commit is contained in:
Vishnu Mohandas 2020-07-14 17:45:55 +05:30
parent 529e7f82d0
commit f77f14eb79
5 changed files with 43 additions and 34 deletions

View file

@ -114,12 +114,13 @@ class FileDB {
return _convertToFiles(results);
}
Future<List<File>> getAllInFolder(int folderId) async {
Future<List<File>> getAllInFolder(int folderId, int offset, int limit) async {
final db = await instance.database;
final results = await db.query(
table,
where: '$columnRemoteFolderId = ? AND $columnIsDeleted = 0',
whereArgs: [folderId],
where:
'$columnRemoteFolderId = ? AND $columnIsDeleted = 0 OFFSET ? LIMIT ?',
whereArgs: [folderId, offset, limit],
orderBy: '$columnCreationTime DESC',
);
return _convertToFiles(results);

View file

@ -28,14 +28,16 @@ class FaceSearchManager {
.catchError(_onError);
}
Future<List<File>> getFaceSearchResults(Face face) async {
Future<List<File>> getFaceSearchResults(
Face face, int offset, int limit) async {
final result = await _dio
.get(
Configuration.instance.getHttpEndpoint() +
"/photos/search/face/" +
face.faceID.toString(),
queryParameters: {
"limit": 100,
"limit": limit,
"offset": offset,
},
options:
Options(headers: {"X-Auth-Token": Configuration.instance.getToken()}),

View file

@ -21,7 +21,8 @@ class FaceSearchResultsPage extends StatelessWidget {
),
body: Container(
child: Gallery(
asyncLoader: () => _faceSearchManager.getFaceSearchResults(face),
asyncLoader: (offset, limit) =>
_faceSearchManager.getFaceSearchResults(face, offset, limit),
tagPrefix: "face_search_results",
),
),

View file

@ -16,7 +16,7 @@ import 'package:pull_to_refresh/pull_to_refresh.dart';
class Gallery extends StatefulWidget {
final List<File> Function() syncLoader;
final Future<List<File>> Function() asyncLoader;
final Future<List<File>> Function(int offset, int limit) asyncLoader;
// TODO: Verify why the event is necessary when calling loader post onRefresh
// should have done the job.
final Stream<Event> reloadEvent;
@ -43,11 +43,12 @@ class Gallery extends StatefulWidget {
class _GalleryState extends State<Gallery> {
final Logger _logger = Logger("Gallery");
static final int kLoadLimit = 100;
final ScrollController _scrollController = ScrollController();
final List<List<File>> _collatedFiles = List<List<File>>();
bool _requiresLoad = false;
AsyncSnapshot<List<File>> _lastSnapshot;
bool _hasLoadedAll = false;
Set<File> _selectedFiles = HashSet<File>();
List<File> _files;
RefreshController _refreshController = RefreshController();
@ -68,40 +69,29 @@ class _GalleryState extends State<Gallery> {
@override
Widget build(BuildContext context) {
if (!_requiresLoad) {
if (widget.syncLoader != null) {
return _onDataLoaded();
}
return _onSnapshotAvailable();
return _onDataLoaded();
}
if (widget.syncLoader != null) {
_files = widget.syncLoader();
return _onDataLoaded();
}
return FutureBuilder<List<File>>(
future: widget.asyncLoader(),
future: widget.asyncLoader(0, kLoadLimit),
builder: (context, snapshot) {
if (snapshot.hasData) {
_logger.info(snapshot.data.length.toString());
_requiresLoad = false;
_files = snapshot.data;
return _onDataLoaded();
} else if (snapshot.hasError) {
_requiresLoad = false;
return Center(child: Text(snapshot.error.toString()));
} else {
return Center(child: loadWidget);
}
_lastSnapshot = snapshot;
return _onSnapshotAvailable();
},
);
}
Widget _onSnapshotAvailable() {
if (_lastSnapshot.hasData) {
_requiresLoad = false;
_files = _lastSnapshot.data;
return _onDataLoaded();
} else if (_lastSnapshot.hasError) {
_requiresLoad = false;
return Center(child: Text(_lastSnapshot.error.toString()));
} else {
return Center(child: loadWidget);
}
}
Widget _onDataLoaded() {
_logger.info("Loaded " + _files.length.toString());
if (_files.isEmpty) {
@ -110,7 +100,7 @@ class _GalleryState extends State<Gallery> {
_selectedFiles = widget.selectedFiles ?? Set<File>();
_collateFiles();
final list = ListView.builder(
itemCount: _collatedFiles.length,
itemCount: _collatedFiles.length + 1, // h4ck to load the next set
itemBuilder: _buildListItem,
controller: _scrollController,
cacheExtent: 1000,
@ -123,9 +113,9 @@ class _GalleryState extends State<Gallery> {
onRefresh: () {
widget.onRefresh().then((_) {
_refreshController.refreshCompleted();
widget.asyncLoader().then((_) => setState(() {
_requiresLoad = true;
}));
setState(() {
_requiresLoad = true;
});
}).catchError((e) {
_refreshController.refreshFailed();
setState(() {});
@ -138,6 +128,20 @@ class _GalleryState extends State<Gallery> {
}
Widget _buildListItem(BuildContext context, int index) {
if (index == _collatedFiles.length) {
if (_hasLoadedAll || widget.asyncLoader == null) {
return Container();
}
widget.asyncLoader(_files.length, 100).then((files) {
setState(() {
if (files.length == 0) {
_hasLoadedAll = true;
}
_files.addAll(files);
});
});
return loadWidget;
}
var files = _collatedFiles[index];
return Column(
children: <Widget>[_getDay(files[0].creationTime), _getGallery(files)],

View file

@ -33,7 +33,8 @@ class _RemoteFolderPageState extends State<RemoteFolderPage> {
},
),
body: Gallery(
asyncLoader: () => FileDB.instance.getAllInFolder(widget.folder.id),
asyncLoader: (offset, limit) =>
FileDB.instance.getAllInFolder(widget.folder.id, offset, limit),
onRefresh: () => FolderSharingService.instance.syncDiff(widget.folder),
selectedFiles: _selectedPhotos,
onFileSelectionChange: (Set<File> selectedPhotos) {