Ensure that uploaded files are not reuploaded on reinstall

This commit is contained in:
Vishnu Mohandas 2020-05-18 00:12:03 +05:30
parent 69e746b9c2
commit 3a2ea5014f
3 changed files with 68 additions and 35 deletions

View file

@ -108,10 +108,27 @@ class DatabaseHelper {
return _convertToPhotos(results);
}
Future<int> updatePhoto(
int generatedId, String remotePath, int syncTimestamp) async {
Future<Photo> getMatchingPhoto(String localId, String title,
String deviceFolder, int createTimestamp) async {
final db = await instance.database;
final rows = await db.query(
table,
where:
'$columnLocalId=? AND $columnTitle=? AND $columnDeviceFolder=? AND $columnCreateTimestamp=?',
whereArgs: [localId, title, deviceFolder, createTimestamp],
);
if (rows.isNotEmpty) {
return _getPhotoFromRow(rows[0]);
} else {
throw ("No matching photo found");
}
}
Future<int> updatePhoto(int generatedId, int uploadedId, String remotePath,
int syncTimestamp) async {
final db = await instance.database;
final values = new Map<String, dynamic>();
values[columnUploadedFileId] = uploadedId;
values[columnRemotePath] = remotePath;
values[columnSyncTimestamp] = syncTimestamp;
return await db.update(
@ -130,12 +147,13 @@ class DatabaseHelper {
whereArgs: [path],
);
if (rows.isNotEmpty) {
return _getPhotofromRow(rows[0]);
return _getPhotoFromRow(rows[0]);
} else {
throw ("No cached photo");
}
}
// TODO: Remove deleted photos on remote
Future<int> markPhotoForDeletion(Photo photo) async {
final db = await instance.database;
var values = new Map<String, dynamic>();
@ -181,7 +199,7 @@ class DatabaseHelper {
limit: 1,
);
if (rows.isNotEmpty) {
return _getPhotofromRow(rows[0]);
return _getPhotoFromRow(rows[0]);
} else {
throw ("No photo found in path");
}
@ -197,7 +215,7 @@ class DatabaseHelper {
limit: 1,
);
if (rows.isNotEmpty) {
return _getPhotofromRow(rows[0]);
return _getPhotoFromRow(rows[0]);
} else {
throw ("No photo found with ids " + generatedIds.join(", ").toString());
}
@ -206,7 +224,7 @@ class DatabaseHelper {
List<Photo> _convertToPhotos(List<Map<String, dynamic>> results) {
var photos = List<Photo>();
for (var result in results) {
photos.add(_getPhotofromRow(result));
photos.add(_getPhotoFromRow(result));
}
return photos;
}
@ -224,7 +242,7 @@ class DatabaseHelper {
return row;
}
Photo _getPhotofromRow(Map<String, dynamic> row) {
Photo _getPhotoFromRow(Map<String, dynamic> row) {
Photo photo = Photo();
photo.generatedId = row[columnGeneratedId];
photo.localId = row[columnLocalId];

View file

@ -17,9 +17,10 @@ class Photo {
Photo();
Photo.fromJson(Map<String, dynamic> json)
: uploadedFileId = json["fileId"],
title = json["title"],
: uploadedFileId = json["fileID"],
localId = json["deviceFileID"],
deviceFolder = json["deviceFolder"],
title = json["title"],
remotePath = json["path"],
createTimestamp = json["createTimestamp"],
syncTimestamp = json["syncTimestamp"];

View file

@ -20,6 +20,7 @@ import 'package:photos/events/remote_sync_event.dart';
class PhotoSyncManager {
final _logger = Logger("PhotoSyncManager");
final _dio = Dio();
final _db = DatabaseHelper.instance;
bool _isSyncInProgress = false;
static final _lastSyncTimestampKey = "last_sync_timestamp_0";
@ -72,16 +73,16 @@ class PhotoSyncManager {
}
if (photos.isEmpty) {
_isSyncInProgress = false;
_syncPhotos().then((_) {
_deletePhotos();
_syncPhotosWithServer().then((_) {
_deletePhotosOnServer();
});
} else {
photos.sort((first, second) =>
first.createTimestamp.compareTo(second.createTimestamp));
_updateDatabase(photos, prefs, lastDBUpdateTimestamp).then((_) {
_isSyncInProgress = false;
_syncPhotos().then((_) {
_deletePhotos();
_syncPhotosWithServer().then((_) {
_deletePhotosOnServer();
});
});
}
@ -99,37 +100,44 @@ class PhotoSyncManager {
photosToBeAdded, prefs, DateTime.now().microsecondsSinceEpoch);
}
_syncPhotos() async {
_syncPhotosWithServer() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
var lastSyncTimestamp = prefs.getInt(_lastSyncTimestampKey);
if (lastSyncTimestamp == null) {
lastSyncTimestamp = 0;
}
_logger.info("Last sync timestamp: " + lastSyncTimestamp.toString());
await _getDiff(lastSyncTimestamp, _diffLimit).then((diff) async {
if (diff != null) {
await _storeDiff(diff, prefs).then((_) {
// TODO: Recursively store diff
_uploadDiff(prefs);
});
var shouldFetchDiff = true;
while (shouldFetchDiff) {
int lastSyncTimestamp = _getLastSyncTimestamp(prefs);
var diff = await _getDiff(lastSyncTimestamp, _diffLimit);
if (diff != null && diff.isNotEmpty) {
await _storeDiff(diff, prefs);
PhotoRepository.instance.reloadPhotos();
}
});
if (diff.length < _diffLimit) {
shouldFetchDiff = false;
}
}
_uploadDiff(prefs);
// TODO: Fix race conditions triggered due to concurrent syncs.
// Add device_id/last_sync_timestamp to the upload request?
}
int _getLastSyncTimestamp(SharedPreferences prefs) {
var lastSyncTimestamp = prefs.getInt(_lastSyncTimestampKey);
if (lastSyncTimestamp == null) {
lastSyncTimestamp = 0;
}
return lastSyncTimestamp;
}
Future _uploadDiff(SharedPreferences prefs) async {
List<Photo> photosToBeUploaded =
await DatabaseHelper.instance.getPhotosToBeUploaded();
List<Photo> photosToBeUploaded = await _db.getPhotosToBeUploaded();
for (Photo photo in photosToBeUploaded) {
try {
var uploadedPhoto = await _uploadFile(photo);
if (uploadedPhoto == null) {
return;
}
await DatabaseHelper.instance.updatePhoto(photo.generatedId,
await _db.updatePhoto(photo.generatedId, uploadedPhoto.uploadedFileId,
uploadedPhoto.remotePath, uploadedPhoto.syncTimestamp);
prefs.setInt(_lastSyncTimestampKey, uploadedPhoto.syncTimestamp);
} catch (e) {
@ -140,10 +148,16 @@ class PhotoSyncManager {
Future _storeDiff(List<Photo> diff, SharedPreferences prefs) async {
for (Photo photo in diff) {
await DatabaseHelper.instance.insertPhoto(photo);
try {
var existingPhoto = await _db.getMatchingPhoto(photo.localId,
photo.title, photo.deviceFolder, photo.createTimestamp);
await _db.updatePhoto(existingPhoto.generatedId, photo.uploadedFileId,
photo.remotePath, photo.syncTimestamp);
} catch (e) {
await _db.insertPhoto(photo);
}
await prefs.setInt(_lastSyncTimestampKey, photo.syncTimestamp);
}
PhotoRepository.instance.reloadPhotos();
}
Future<List<Photo>> _getDiff(int lastSyncTimestamp, int limit) async {
@ -190,11 +204,11 @@ class PhotoSyncManager {
});
}
Future<void> _deletePhotos() async {
DatabaseHelper.instance.getAllDeletedPhotos().then((deletedPhotos) {
Future<void> _deletePhotosOnServer() async {
_db.getAllDeletedPhotos().then((deletedPhotos) {
for (Photo deletedPhoto in deletedPhotos) {
_deletePhotoOnServer(deletedPhoto)
.then((value) => DatabaseHelper.instance.deletePhoto(deletedPhoto));
.then((value) => _db.deletePhoto(deletedPhoto));
}
});
}
@ -219,7 +233,7 @@ class PhotoSyncManager {
Future<bool> _insertPhotosToDB(
List<Photo> photos, SharedPreferences prefs, int timestamp) async {
await DatabaseHelper.instance.insertPhotos(photos);
await _db.insertPhotos(photos);
_logger.info("Inserted " + photos.length.toString() + " photos.");
PhotoRepository.instance.reloadPhotos();
return await prefs.setInt(_lastDBUpdateTimestampKey, timestamp);