浏览代码

Ensure that uploaded files are not reuploaded on reinstall

Vishnu Mohandas 5 年之前
父节点
当前提交
3a2ea5014f
共有 3 个文件被更改,包括 68 次插入35 次删除
  1. 25 7
      lib/db/db_helper.dart
  2. 3 2
      lib/models/photo.dart
  3. 40 26
      lib/photo_sync_manager.dart

+ 25 - 7
lib/db/db_helper.dart

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

+ 3 - 2
lib/models/photo.dart

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

+ 40 - 26
lib/photo_sync_manager.dart

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