Przeglądaj źródła

Merge pull request #124 from ente-io/hide_ignored_files

Hide ignored files from home page + refactoring
Neeraj Gupta 3 lat temu
rodzic
commit
6fce44a28a

+ 3 - 38
lib/db/ignored_files_db.dart

@@ -91,47 +91,12 @@ class IgnoredFilesDB {
         "took ${duration.inMilliseconds} ms.");
   }
 
-  Future<int> insert(IgnoredFile ignoredFile) async {
-    final db = await instance.database;
-    return db.insert(
-      tableName,
-      _getRowForIgnoredFile(ignoredFile),
-      conflictAlgorithm: ConflictAlgorithm.replace,
-    );
-  }
-
-  // returns a  map of device folder to set of title/filenames which exist
-  // in the particular device folder.
-  Future<Map<String, Set<String>>> getFilenamesForDeviceFolders(
-      Set<String> folders) async {
-    final result = <String, Set<String>>{};
-    final db = await instance.database;
-
-    if (folders.isEmpty) {
-      return result;
-    }
-    String inParam = "";
-    for (final folder in folders) {
-      inParam += "'" + folder.replaceAll("'", "''") + "',";
-    }
-    inParam = inParam.substring(0, inParam.length - 1);
-    final rows =
-        await db.query(tableName, where: '$columnDeviceFolder IN ($inParam)');
-    for (final row in rows) {
-      final ignoredFile = _getIgnoredFileFromRow(row);
-      result
-          .putIfAbsent(ignoredFile.deviceFolder, () => <String>{})
-          .add(ignoredFile.title);
-    }
-    return result;
-  }
-
-  Future<Set<String>> getAllLocalIDs() async {
+  Future<List<IgnoredFile>> getAll() async {
     final db = await instance.database;
     final rows = await db.query(tableName);
-    final result = <String>{};
+    final result = <IgnoredFile>[];
     for (final row in rows) {
-      result.add(row[columnLocalID]);
+      result.add(_getIgnoredFileFromRow(row));
     }
     return result;
   }

+ 82 - 0
lib/services/ignored_files_service.dart

@@ -0,0 +1,82 @@
+import 'dart:async';
+import 'dart:io';
+
+import 'package:logging/logging.dart';
+import 'package:photos/db/ignored_files_db.dart';
+import 'package:photos/models/file.dart';
+import 'package:photos/models/ignored_file.dart';
+
+class IgnoredFilesService {
+  final _logger = Logger("IgnoredFilesService");
+  final _db = IgnoredFilesDB.instance;
+
+  IgnoredFilesService._privateConstructor();
+
+  static final IgnoredFilesService instance =
+      IgnoredFilesService._privateConstructor();
+
+  Future<Set<String>> _ignoredIDs;
+
+  Future<Set<String>> get ignoredIDs async {
+    // lazily instantiate the db the first time it is accessed
+    _ignoredIDs ??= _loadExistingIDs();
+    return _ignoredIDs;
+  }
+
+  Future<void> cacheAndInsert(List<IgnoredFile> ignoredFiles) async {
+    final existingIDs = await ignoredIDs;
+    existingIDs.addAll(ignoredFiles
+        .map((e) => _idForIgnoredFile(e))
+        .where((id) => id != null)
+        .toSet());
+    return _db.insertMultiple(ignoredFiles);
+  }
+
+  // shouldSkipUpload takes IDs to ignore and file for which it will return
+  // whether either true or false. This helper method takes ignoredIDs as input
+  // to avoid making it async in nature.
+  // This syntax is intentional as we want to ensure that ignoredIDs are loaded
+  // from the DB before calling this method.
+  bool shouldSkipUpload(Set<String> ignoredIDs, File file) {
+    final id = _getIgnoreID(file.localID, file.deviceFolder, file.title);
+    if (id != null && id.isNotEmpty) {
+      return ignoredIDs.contains(id);
+    }
+    return false;
+  }
+
+  Future<Set<String>> _loadExistingIDs() async {
+    _logger.fine('loading existing IDs');
+    final result = await _db.getAll();
+    return result
+        .map((e) => _idForIgnoredFile(e))
+        .where((id) => id != null)
+        .toSet();
+  }
+
+  String _idForIgnoredFile(IgnoredFile ignoredFile) {
+    return _getIgnoreID(
+        ignoredFile.localID, ignoredFile.deviceFolder, ignoredFile.title);
+  }
+
+  // _computeIgnoreID will return null if don't have sufficient information
+  // to ignore the file based on the platform. Uploads from web or files shared to
+  // end usually don't have local id.
+  // For Android: It returns deviceFolder-title as ID for Android.
+  // For iOS, it returns localID as localID is uuid and the title or deviceFolder (aka
+  // album name) can be missing due to various reasons.
+  String _getIgnoreID(String localID, String deviceFolder, String title) {
+    // file was not uploaded from mobile device
+    if (localID == null || localID.isEmpty) {
+      return null;
+    }
+    if (Platform.isAndroid) {
+      if (deviceFolder == null || title == null) {
+        return null;
+      }
+      return '$deviceFolder-$title';
+    } else {
+      return localID;
+    }
+  }
+}

+ 4 - 14
lib/services/remote_sync_service.dart

@@ -7,7 +7,6 @@ import 'package:photos/core/configuration.dart';
 import 'package:photos/core/errors.dart';
 import 'package:photos/core/event_bus.dart';
 import 'package:photos/db/files_db.dart';
-import 'package:photos/db/ignored_files_db.dart';
 import 'package:photos/events/collection_updated_event.dart';
 import 'package:photos/events/files_updated_event.dart';
 import 'package:photos/events/local_photos_updated_event.dart';
@@ -15,6 +14,7 @@ import 'package:photos/events/sync_status_update_event.dart';
 import 'package:photos/models/file.dart';
 import 'package:photos/models/file_type.dart';
 import 'package:photos/services/collections_service.dart';
+import 'package:photos/services/ignored_files_service.dart';
 import 'package:photos/services/local_sync_service.dart';
 import 'package:photos/services/trash_sync_service.dart';
 import 'package:photos/utils/diff_fetcher.dart';
@@ -132,9 +132,6 @@ class RemoteSyncService {
     }
   }
 
-  // This method checks for deviceFolder + title for Android.
-  // For iOS, we rely on localIDs as they are uuid as title or deviceFolder (aka
-  // album name) can be missing due to various reasons.
   bool _shouldIgnoreFileUpload(
     File file, {
     Map<String, Set<String>> ignoredFilesMap,
@@ -168,16 +165,9 @@ class RemoteSyncService {
     }
     if (filesToBeUploaded.isNotEmpty) {
       final int prevCount = filesToBeUploaded.length;
-      if (Platform.isAndroid) {
-        final ignoredFilesMap = await IgnoredFilesDB.instance
-            .getFilenamesForDeviceFolders(foldersToBackUp);
-        filesToBeUploaded.removeWhere((file) =>
-            _shouldIgnoreFileUpload(file, ignoredFilesMap: ignoredFilesMap));
-      } else {
-        final ignoredLocalIDs = await IgnoredFilesDB.instance.getAllLocalIDs();
-        filesToBeUploaded.removeWhere((file) =>
-            _shouldIgnoreFileUpload(file, ignoredLocalIDs: ignoredLocalIDs));
-      }
+      final ignoredIDs = await IgnoredFilesService.instance.ignoredIDs;
+      filesToBeUploaded.removeWhere((file) =>
+          IgnoredFilesService.instance.shouldSkipUpload(ignoredIDs, file));
       if (prevCount != filesToBeUploaded.length) {
         _logger.info((prevCount - filesToBeUploaded.length).toString() +
             " files were ignored for upload");

+ 2 - 2
lib/services/trash_sync_service.dart

@@ -3,13 +3,13 @@ import 'package:logging/logging.dart';
 import 'package:photos/core/configuration.dart';
 import 'package:photos/core/event_bus.dart';
 import 'package:photos/core/network.dart';
-import 'package:photos/db/ignored_files_db.dart';
 import 'package:photos/db/trash_db.dart';
 import 'package:photos/events/force_reload_trash_page_event.dart';
 import 'package:photos/models/file.dart';
 import 'package:photos/models/ignored_file.dart';
 import 'package:photos/models/trash_file.dart';
 import 'package:photos/models/trash_item_request.dart';
+import 'package:photos/services/ignored_files_service.dart';
 import 'package:photos/utils/trash_diff_fetcher.dart';
 import 'package:shared_preferences/shared_preferences.dart';
 
@@ -75,7 +75,7 @@ class TrashSyncService {
     }
     if (ignoredFiles.isNotEmpty) {
       _logger.fine('updating ${ignoredFiles.length} ignored files ');
-      await IgnoredFilesDB.instance.insertMultiple(ignoredFiles);
+      await IgnoredFilesService.instance.cacheAndInsert(ignoredFiles);
     }
   }
 

+ 15 - 5
lib/ui/home_widget.dart

@@ -13,8 +13,8 @@ import 'package:photos/core/configuration.dart';
 import 'package:photos/core/event_bus.dart';
 import 'package:photos/db/files_db.dart';
 import 'package:photos/events/account_configured_event.dart';
-import 'package:photos/events/force_reload_home_gallery_event.dart';
 import 'package:photos/events/backup_folders_updated_event.dart';
+import 'package:photos/events/force_reload_home_gallery_event.dart';
 import 'package:photos/events/local_photos_updated_event.dart';
 import 'package:photos/events/permission_granted_event.dart';
 import 'package:photos/events/subscription_purchased_event.dart';
@@ -22,7 +22,9 @@ import 'package:photos/events/sync_status_update_event.dart';
 import 'package:photos/events/tab_changed_event.dart';
 import 'package:photos/events/trigger_logout_event.dart';
 import 'package:photos/events/user_logged_out_event.dart';
+import 'package:photos/models/file_load_result.dart';
 import 'package:photos/models/selected_files.dart';
+import 'package:photos/services/ignored_files_service.dart';
 import 'package:photos/services/local_sync_service.dart';
 import 'package:photos/services/update_service.dart';
 import 'package:photos/services/user_service.dart';
@@ -336,24 +338,32 @@ class _HomeWidgetState extends State<HomeWidget> {
       header = _headerWidget;
     }
     final gallery = Gallery(
-      asyncLoader: (creationStartTime, creationEndTime, {limit, asc}) {
+      asyncLoader: (creationStartTime, creationEndTime, {limit, asc}) async {
         final importantPaths = Configuration.instance.getPathsToBackUp();
         final ownerID = Configuration.instance.getUserID();
+        Future<FileLoadResult> result;
         if (importantPaths.isNotEmpty) {
-          return FilesDB.instance.getImportantFiles(creationStartTime,
+          result = FilesDB.instance.getImportantFiles(creationStartTime,
               creationEndTime, ownerID, importantPaths.toList(),
               limit: limit, asc: asc);
         } else {
           if (LocalSyncService.instance.hasGrantedLimitedPermissions()) {
-            return FilesDB.instance.getAllLocalAndUploadedFiles(
+            result = FilesDB.instance.getAllLocalAndUploadedFiles(
                 creationStartTime, creationEndTime, ownerID,
                 limit: limit, asc: asc);
           } else {
-            return FilesDB.instance.getAllUploadedFiles(
+            result = FilesDB.instance.getAllUploadedFiles(
                 creationStartTime, creationEndTime, ownerID,
                 limit: limit, asc: asc);
           }
         }
+        final fileLoadResult = await result;
+        // hide ignored files from home page UI
+        final ignoredIDs = await IgnoredFilesService.instance.ignoredIDs;
+        fileLoadResult.files.removeWhere((f) =>
+            f.uploadedFileID == null &&
+            IgnoredFilesService.instance.shouldSkipUpload(ignoredIDs, f));
+        return fileLoadResult;
       },
       reloadEvent: Bus.instance.on<LocalPhotosUpdatedEvent>(),
       forceReloadEvents: [

+ 1 - 1
lib/ui/settings/backup_section_widget.dart

@@ -254,7 +254,7 @@ class BackupSectionWidgetState extends State<BackupSectionWidget> {
             ),
           ),
           onPressed: () {
-            // TODO: After trash implementation - showToast("also empty your \"Trash\" to claim the freed up space");
+            showToast("also empty your \"Trash\" to claim the freed up space");
             Navigator.of(context, rootNavigator: true).pop('dialog');
           },
         ),

+ 1 - 1
pubspec.yaml

@@ -11,7 +11,7 @@ description: ente photos application
 # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
 # Read more about iOS versioning at
 # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
-version: 0.3.39+249
+version: 0.3.40+250
 
 environment:
   sdk: ">=2.10.0 <3.0.0"