Browse Source

Merge branch 'rewrite_device_sync' into rewrite_device_sync_remote

Neeraj Gupta 2 years ago
parent
commit
591e8ba1d9

+ 20 - 4
lib/db/device_files_db.dart

@@ -5,6 +5,7 @@ import 'package:photos/db/files_db.dart';
 import 'package:photos/models/device_collection.dart';
 import 'package:photos/models/file.dart';
 import 'package:photos/models/file_load_result.dart';
+import 'package:photos/models/upload_strategy.dart';
 import 'package:photos/services/local/local_sync_util.dart';
 import 'package:sqflite/sqlite_api.dart';
 import 'package:tuple/tuple.dart';
@@ -178,11 +179,23 @@ extension DeviceFiles on FilesDB {
         final String localID = tup.item2;
         final bool shouldUpdate = existingPathIds.contains(pathEntity.id);
         if (shouldUpdate) {
-          await db.rawUpdate(
+          final rowUpdated = await db.rawUpdate(
             "UPDATE device_collections SET name = ?, cover_id = ?, count"
-            " = ? where id = ?",
-            [pathEntity.name, localID, pathEntity.assetCount, pathEntity.id],
+            " = ? where id = ? AND (name != ? OR cover_id != ? OR count != ?)",
+            [
+              pathEntity.name,
+              localID,
+              pathEntity.assetCount,
+              pathEntity.id,
+              pathEntity.name,
+              localID,
+              pathEntity.assetCount,
+            ],
           );
+          if (rowUpdated > 0) {
+            _logger.fine("Updated $rowUpdated rows for ${pathEntity.name}");
+            hasUpdated = true;
+          }
         } else {
           hasUpdated = true;
           await db.insert(
@@ -290,7 +303,9 @@ extension DeviceFiles on FilesDB {
     bool includeCoverThumbnail = false,
   }) async {
     debugPrint(
-        "Fetching DeviceCollections From DB with thumnail = $includeCoverThumbnail");
+      "Fetching DeviceCollections From DB with thumbnail = "
+      "$includeCoverThumbnail",
+    );
     try {
       final db = await database;
       final coverFiles = <File>[];
@@ -314,6 +329,7 @@ extension DeviceFiles on FilesDB {
           collectionID: row["collection_id"],
           coverId: row["cover_id"],
           shouldBackup: (row["should_backup"] ?? _sqlBoolFalse) == _sqlBoolTrue,
+          uploadStrategy: getUploadType(row["upload_strategy"] ?? 0),
         );
         if (includeCoverThumbnail) {
           deviceCollection.thumbnail = coverFiles.firstWhere(

+ 19 - 0
lib/db/files_db.dart

@@ -317,6 +317,7 @@ class FilesDB {
           should_backup INTEGER NOT NULL DEFAULT 0,
           count INTEGER NOT NULL DEFAULT 0,
           collection_id INTEGER DEFAULT -1,
+          upload_strategy INTEGER DEFAULT 0,
           cover_id TEXT
       );
       ''',
@@ -1357,6 +1358,24 @@ class FilesDB {
     return result;
   }
 
+  Future<Set<int>> getAllCollectionIDsOfFile(
+    int uploadedFileID,
+  ) async {
+    final db = await instance.database;
+    final results = await db.query(
+      filesTable,
+      where: '$columnUploadedFileID = ? AND $columnCollectionID != -1',
+      columns: [columnCollectionID],
+      whereArgs: [uploadedFileID],
+      distinct: true,
+    );
+    final collectionIDsOfFile = <int>{};
+    for (var result in results) {
+      collectionIDsOfFile.add(result['collection_id']);
+    }
+    return collectionIDsOfFile;
+  }
+
   List<File> convertToFiles(List<Map<String, dynamic>> results) {
     final List<File> files = [];
     for (final result in results) {

+ 3 - 2
lib/models/device_collection.dart

@@ -7,7 +7,8 @@ class DeviceCollection {
   final String coverId;
   final int count;
   final bool shouldBackup;
-  final UploadStrategy uploadStrategy;
+  UploadStrategy uploadStrategy;
+
   int collectionID;
   File thumbnail;
 
@@ -18,7 +19,7 @@ class DeviceCollection {
     this.count,
     this.collectionID,
     this.thumbnail,
-    this.shouldBackup = false,
     this.uploadStrategy = UploadStrategy.ifMissing,
+    this.shouldBackup = false,
   });
 }

+ 4 - 0
lib/services/local_sync_service.dart

@@ -31,6 +31,7 @@ class LocalSyncService {
 
   static const kDbUpdationTimeKey = "db_updation_time";
   static const kHasCompletedFirstImportKey = "has_completed_firstImport";
+  static const hasImportedDeviceCollections = "has_imported_device_collections";
   static const kHasGrantedPermissionsKey = "has_granted_permissions";
   static const kPermissionStateKey = "permission_state";
   static const kEditedFileIDsKey = "edited_file_ids";
@@ -122,7 +123,9 @@ class LocalSyncService {
     if (!_prefs.containsKey(kHasCompletedFirstImportKey) ||
         !_prefs.getBool(kHasCompletedFirstImportKey)) {
       await _prefs.setBool(kHasCompletedFirstImportKey, true);
+      // mark device collection has imported on first import
       await _refreshDeviceFolderCountAndCover();
+      await _prefs.setBool(hasImportedDeviceCollections, true);
       _logger.fine("first gallery import finished");
       Bus.instance
           .fire(SyncStatusUpdate(SyncStatus.completedFirstGalleryImport));
@@ -273,6 +276,7 @@ class LocalSyncService {
     await IgnoredFilesDB.instance.clearTable();
     for (var element in [
       kHasCompletedFirstImportKey,
+      hasImportedDeviceCollections,
       kDbUpdationTimeKey,
       kDownloadedFileIDsKey,
       kEditedFileIDsKey

+ 54 - 0
lib/ui/viewer/file/collections_list_of_file_widget.dart

@@ -0,0 +1,54 @@
+import 'package:flutter/material.dart';
+import 'package:logging/logging.dart';
+import 'package:photos/models/collection_items.dart';
+import 'package:photos/services/collections_service.dart';
+import 'package:photos/ui/common/loading_widget.dart';
+import 'package:photos/ui/viewer/file/file_info_collection_widget.dart';
+import 'package:photos/ui/viewer/gallery/collection_page.dart';
+import 'package:photos/utils/navigation_util.dart';
+
+class CollectionsListOfFileWidget extends StatelessWidget {
+  final Future<Set<int>> allCollectionIDsOfFile;
+  const CollectionsListOfFileWidget(this.allCollectionIDsOfFile, {Key key})
+      : super(key: key);
+
+  @override
+  Widget build(BuildContext context) {
+    return FutureBuilder(
+      future: allCollectionIDsOfFile,
+      builder: (context, snapshot) {
+        if (snapshot.hasData) {
+          final Set<int> collectionIDs = snapshot.data;
+          final collections = [];
+          for (var collectionID in collectionIDs) {
+            collections.add(
+              CollectionsService.instance.getCollectionByID(collectionID),
+            );
+          }
+          return ListView.builder(
+            itemCount: collections.length,
+            scrollDirection: Axis.horizontal,
+            itemBuilder: (context, index) {
+              return FileInfoCollectionWidget(
+                name: collections[index].name,
+                onTap: () {
+                  routeToPage(
+                    context,
+                    CollectionPage(
+                      CollectionWithThumbnail(collections[index], null),
+                    ),
+                  );
+                },
+              );
+            },
+          );
+        } else if (snapshot.hasError) {
+          Logger("CollectionsListOfFile").info(snapshot.error);
+          return const SizedBox.shrink();
+        } else {
+          return const EnteLoadingWidget();
+        }
+      },
+    );
+  }
+}

+ 37 - 0
lib/ui/viewer/file/device_folders_list_of_file_widget.dart

@@ -0,0 +1,37 @@
+import 'package:flutter/material.dart';
+import 'package:logging/logging.dart';
+import 'package:photos/ui/common/loading_widget.dart';
+import 'package:photos/ui/viewer/file/file_info_collection_widget.dart';
+
+class DeviceFoldersListOfFileWidget extends StatelessWidget {
+  final Future<Set<String>> allDeviceFoldersOfFile;
+  const DeviceFoldersListOfFileWidget(this.allDeviceFoldersOfFile, {Key key})
+      : super(key: key);
+
+  @override
+  Widget build(BuildContext context) {
+    return FutureBuilder(
+      future: allDeviceFoldersOfFile,
+      builder: (context, snapshot) {
+        if (snapshot.hasData) {
+          final List<String> deviceFolders = snapshot.data.toList();
+          return ListView.builder(
+            itemCount: deviceFolders.length,
+            scrollDirection: Axis.horizontal,
+            itemBuilder: (context, index) {
+              return FileInfoCollectionWidget(
+                name: deviceFolders[index],
+                onTap: () {},
+              );
+            },
+          );
+        } else if (snapshot.hasError) {
+          Logger("DeviceFoldersListOfFile").info(snapshot.error);
+          return const SizedBox.shrink();
+        } else {
+          return const EnteLoadingWidget();
+        }
+      },
+    );
+  }
+}

+ 1 - 1
lib/ui/viewer/file/fading_bottom_bar.dart

@@ -10,7 +10,7 @@ import 'package:photos/models/magic_metadata.dart';
 import 'package:photos/models/selected_files.dart';
 import 'package:photos/models/trash_file.dart';
 import 'package:photos/ui/create_collection_page.dart';
-import 'package:photos/ui/viewer/file/file_info_dialog.dart';
+import 'package:photos/ui/viewer/file/file_info_widget.dart';
 import 'package:photos/utils/delete_file_util.dart';
 import 'package:photos/utils/magic_util.dart';
 import 'package:photos/utils/share_util.dart';

+ 42 - 0
lib/ui/viewer/file/file_info_collection_widget.dart

@@ -0,0 +1,42 @@
+import 'package:flutter/material.dart';
+import 'package:photos/ente_theme_data.dart';
+
+class FileInfoCollectionWidget extends StatelessWidget {
+  final String name;
+  final Function onTap;
+  const FileInfoCollectionWidget({this.name, this.onTap, Key key})
+      : super(key: key);
+
+  @override
+  Widget build(BuildContext context) {
+    return GestureDetector(
+      onTap: onTap,
+      child: Container(
+        margin: const EdgeInsets.only(
+          top: 10,
+          bottom: 18,
+          right: 8,
+        ),
+        decoration: BoxDecoration(
+          color: Theme.of(context)
+              .colorScheme
+              .inverseBackgroundColor
+              .withOpacity(0.025),
+          borderRadius: const BorderRadius.all(
+            Radius.circular(8),
+          ),
+        ),
+        child: Center(
+          child: Padding(
+            padding: const EdgeInsets.symmetric(horizontal: 8),
+            child: Text(
+              name,
+              style: Theme.of(context).textTheme.subtitle2,
+              overflow: TextOverflow.ellipsis,
+            ),
+          ),
+        ),
+      ),
+    );
+  }
+}

+ 24 - 29
lib/ui/viewer/file/file_info_dialog.dart → lib/ui/viewer/file/file_info_widget.dart

@@ -3,20 +3,18 @@ import "package:flutter/cupertino.dart";
 import "package:flutter/material.dart";
 import 'package:flutter_datetime_picker/flutter_datetime_picker.dart';
 import "package:photos/core/configuration.dart";
+import 'package:photos/db/files_db.dart';
 import "package:photos/ente_theme_data.dart";
-import 'package:photos/models/collection.dart';
-import 'package:photos/models/collection_items.dart';
 import "package:photos/models/file.dart";
 import "package:photos/models/file_type.dart";
-import "package:photos/services/collections_service.dart";
 import 'package:photos/ui/common/DividerWithPadding.dart';
-import 'package:photos/ui/viewer/file/RawExifButton.dart';
-import 'package:photos/ui/viewer/gallery/collection_page.dart';
+import 'package:photos/ui/viewer/file/collections_list_of_file_widget.dart';
+import 'package:photos/ui/viewer/file/device_folders_list_of_file_widget.dart';
+import 'package:photos/ui/viewer/file/raw_exif_button.dart';
 import "package:photos/utils/date_time_util.dart";
 import "package:photos/utils/exif_util.dart";
 import "package:photos/utils/file_util.dart";
 import "package:photos/utils/magic_util.dart";
-import 'package:photos/utils/navigation_util.dart';
 
 class FileInfoWidget extends StatefulWidget {
   final File file;
@@ -62,6 +60,17 @@ class _FileInfoWidgetState extends State<FileInfoWidget> {
   @override
   Widget build(BuildContext context) {
     final file = widget.file;
+    final fileIsBackedup = file.uploadedFileID == null ? false : true;
+    Future<Set<int>> allCollectionIDsOfFile;
+    Future<Set<String>>
+        allDeviceFoldersOfFile; //Typing this as Future<Set<T>> as it would be easier to implement showing multiple device folders for a file in the future
+    if (fileIsBackedup) {
+      allCollectionIDsOfFile = FilesDB.instance.getAllCollectionIDsOfFile(
+        file.uploadedFileID,
+      );
+    } else {
+      allDeviceFoldersOfFile = Future.sync(() => {file.deviceFolder});
+    }
     final dateTime = DateTime.fromMicrosecondsSinceEpoch(file.creationTime);
     final dateTimeForUpdationTime =
         DateTime.fromMicrosecondsSinceEpoch(file.updationTime);
@@ -198,30 +207,16 @@ class _FileInfoWidgetState extends State<FileInfoWidget> {
       showExifListTile
           ? const DividerWithPadding(left: 70, right: 20)
           : const SizedBox.shrink(),
-      ListTile(
-        leading: const Padding(
-          padding: EdgeInsets.only(left: 6),
-          child: Icon(Icons.folder_outlined),
-        ),
-        title: GestureDetector(
-          onTap: () {
-            if (file.collectionID != null) {
-              Navigator.pop(context); // info dialog
-              final Collection c = CollectionsService.instance
-                  .getCollectionByID(file.collectionID);
-              routeToPage(
-                context,
-                CollectionPage(CollectionWithThumbnail(c, null)),
-              );
-            }
-          },
-          child: Text(
-            file.collectionID != null
-                ? CollectionsService.instance
-                    .getCollectionByID(file.collectionID)
-                    .name
-                : file.deviceFolder,
+      SizedBox(
+        height: 62,
+        child: ListTile(
+          leading: const Padding(
+            padding: EdgeInsets.only(left: 6),
+            child: Icon(Icons.folder_outlined),
           ),
+          title: fileIsBackedup
+              ? CollectionsListOfFileWidget(allCollectionIDsOfFile)
+              : DeviceFoldersListOfFileWidget(allDeviceFoldersOfFile),
         ),
       ),
       const DividerWithPadding(left: 70, right: 20),

+ 0 - 0
lib/ui/viewer/file/RawExifButton.dart → lib/ui/viewer/file/raw_exif_button.dart