Procházet zdrojové kódy

Resolved merge conflicts

ashilkn před 2 roky
rodič
revize
1414c2f05b

+ 1 - 1
lib/db/file_updation_db.dart

@@ -105,7 +105,7 @@ class FileUpdationDB {
           endTime.microsecondsSinceEpoch - startTime.microsecondsSinceEpoch,
     );
     _logger.info(
-      "Batch insert of ${fileLocalIDs.length} "
+      "Batch insert of ${fileLocalIDs.length} updated files due to $reason "
       "took ${duration.inMilliseconds} ms.",
     );
   }

+ 39 - 61
lib/services/local_sync_service.dart

@@ -33,7 +33,6 @@ class LocalSyncService {
   static const hasImportedDeviceCollections = "has_imported_device_collections";
   static const kHasGrantedPermissionsKey = "has_granted_permissions";
   static const kPermissionStateKey = "permission_state";
-  static const kEditedFileIDsKey = "edited_file_ids";
 
   // Adding `_2` as a suffic to pull files that were earlier ignored due to permission errors
   // See https://github.com/CaiJingLong/flutter_photo_manager/issues/589
@@ -77,19 +76,16 @@ class LocalSyncService {
     _existingSync = Completer<void>();
     final int ownerID = Configuration.instance.getUserID()!;
     final existingLocalFileIDs = await _db.getExistingLocalFileIDs(ownerID);
-    _logger.info(
-      existingLocalFileIDs.length.toString() + " localIDs were discovered",
-    );
-    final editedFileIDs = _getEditedFileIDs().toSet();
+    _logger.info("${existingLocalFileIDs.length} localIDs were discovered");
+
     final syncStartTime = DateTime.now().microsecondsSinceEpoch;
     final lastDBUpdationTime = _prefs.getInt(kDbUpdationTimeKey) ?? 0;
     final startTime = DateTime.now().microsecondsSinceEpoch;
     if (lastDBUpdationTime != 0) {
-      await _loadAndStorePhotos(
-        lastDBUpdationTime,
-        syncStartTime,
+      await _loadAndStoreDiff(
         existingLocalFileIDs,
-        editedFileIDs,
+        fromTime: lastDBUpdationTime,
+        toTime: syncStartTime,
       );
     } else {
       // Load from 0 - 01.01.2010
@@ -98,25 +94,22 @@ class LocalSyncService {
       var toYear = 2010;
       var toTime = DateTime(toYear).microsecondsSinceEpoch;
       while (toTime < syncStartTime) {
-        await _loadAndStorePhotos(
-          startTime,
-          toTime,
+        await _loadAndStoreDiff(
           existingLocalFileIDs,
-          editedFileIDs,
+          fromTime: startTime,
+          toTime: toTime,
         );
         startTime = toTime;
         toYear++;
         toTime = DateTime(toYear).microsecondsSinceEpoch;
       }
-      await _loadAndStorePhotos(
-        startTime,
-        syncStartTime,
+      await _loadAndStoreDiff(
         existingLocalFileIDs,
-        editedFileIDs,
+        fromTime: startTime,
+        toTime: syncStartTime,
       );
     }
-    if (!_prefs.containsKey(kHasCompletedFirstImportKey) ||
-        !(_prefs.getBool(kHasCompletedFirstImportKey)!)) {
+    if (!hasCompletedFirstImport()) {
       await _prefs.setBool(kHasCompletedFirstImportKey, true);
       // mark device collection has imported on first import
       await _refreshDeviceFolderCountAndCover(isFirstSync: true);
@@ -239,15 +232,6 @@ class LocalSyncService {
     return hasUnsyncedFiles;
   }
 
-  List<String> _getEditedFileIDs() {
-    if (_prefs.containsKey(kEditedFileIDsKey)) {
-      return _prefs.getStringList(kEditedFileIDsKey)!;
-    } else {
-      final List<String> editedIDs = [];
-      return editedIDs;
-    }
-  }
-
   Future<void> trackInvalidFile(File file) async {
     if (file.localID == null) {
       debugPrint("Warning: Invalid file has no localID");
@@ -299,7 +283,6 @@ class LocalSyncService {
       kHasCompletedFirstImportKey,
       hasImportedDeviceCollections,
       kDbUpdationTimeKey,
-      kEditedFileIDsKey,
       "has_synced_edit_time",
       "has_selected_all_folders_for_backup",
     ]) {
@@ -307,39 +290,40 @@ class LocalSyncService {
     }
   }
 
-  Future<void> _loadAndStorePhotos(
-    int fromTime,
-    int toTime,
-    Set<String> existingLocalFileIDs,
-    Set<String> editedFileIDs,
-  ) async {
+  Future<void> _loadAndStoreDiff(
+    Set<String> existingLocalDs, {
+    required int fromTime,
+    required int toTime,
+  }) async {
     final Tuple2<List<LocalPathAsset>, List<File>> result =
         await getLocalPathAssetsAndFiles(fromTime, toTime, _computer);
+
+    // Update the mapping for device path_id to local file id. Also, keep track
+    // of newly discovered device paths
     await FilesDB.instance.insertLocalAssets(
       result.item1,
       shouldAutoBackup: Configuration.instance.hasSelectedAllFoldersForBackup(),
     );
+
     final List<File> files = result.item2;
-    _logger.info(
-      "Loaded ${files.length} photos from " +
-          DateTime.fromMicrosecondsSinceEpoch(fromTime).toString() +
-          " to " +
-          DateTime.fromMicrosecondsSinceEpoch(toTime).toString(),
-    );
     if (files.isNotEmpty) {
-      await _trackUpdatedFiles(
-        files,
-        existingLocalFileIDs,
-        editedFileIDs,
+      _logger.info(
+        "Loaded ${files.length} photos from " +
+            DateTime.fromMicrosecondsSinceEpoch(fromTime).toString() +
+            " to " +
+            DateTime.fromMicrosecondsSinceEpoch(toTime).toString(),
       );
+      await _trackUpdatedFiles(files, existingLocalDs);
+      // keep reference of all Files for firing LocalPhotosUpdatedEvent
       final List<File> allFiles = [];
       allFiles.addAll(files);
-      files.removeWhere((file) => existingLocalFileIDs.contains(file.localID));
+      // remove existing files and insert newly imported files in the table
+      files.removeWhere((file) => existingLocalDs.contains(file.localID));
       await _db.insertMultiple(
         files,
         conflictAlgorithm: ConflictAlgorithm.ignore,
       );
-      _logger.info("Inserted " + files.length.toString() + " files.");
+      _logger.info('Inserted ${files.length} files');
       Bus.instance.fire(
         LocalPhotosUpdatedEvent(allFiles, source: "loadedPhoto"),
       );
@@ -350,22 +334,16 @@ class LocalSyncService {
   Future<void> _trackUpdatedFiles(
     List<File> files,
     Set<String> existingLocalFileIDs,
-    Set<String> editedFileIDs,
   ) async {
-    final updatedFiles = files
-        .where((file) => existingLocalFileIDs.contains(file.localID))
+    final List<String> updatedLocalIDs = files
+        .where(
+          (file) =>
+              file.localID != null &&
+              existingLocalFileIDs.contains(file.localID),
+        )
+        .map((e) => e.localID!)
         .toList();
-    updatedFiles.removeWhere((file) => editedFileIDs.contains(file.localID));
-    if (updatedFiles.isNotEmpty) {
-      _logger.info(
-        updatedFiles.length.toString() + " local files were updated.",
-      );
-      final List<String> updatedLocalIDs = [];
-      for (final file in updatedFiles) {
-        if (file.localID != null) {
-          updatedLocalIDs.add(file.localID!);
-        }
-      }
+    if (updatedLocalIDs.isNotEmpty) {
       await FileUpdationDB.instance.insertMultiple(
         updatedLocalIDs,
         FileUpdationDB.modificationTimeUpdated,

+ 62 - 34
lib/ui/actions/collection/collection_sharing_actions.dart

@@ -173,18 +173,17 @@ class CollectionActions {
         showGenericErrorDialog(context: context);
       }
       return result == ButtonAction.first;
-    } else {
-      return false;
     }
+    return false;
   }
 
-  Future<bool?> addEmailToCollection(
+  // addEmailToCollection returns true if add operation was successful
+  Future<bool> addEmailToCollection(
     BuildContext context,
     Collection collection,
-    String email, {
-    CollectionParticipantRole role = CollectionParticipantRole.viewer,
+    String email,
+    CollectionParticipantRole role, {
     bool showProgress = false,
-    String? publicKey,
   }) async {
     if (!isValidEmail(email)) {
       await showErrorDialog(
@@ -192,31 +191,29 @@ class CollectionActions {
         "Invalid email address",
         "Please enter a valid email address.",
       );
-      return null;
-    } else if (email == Configuration.instance.getEmail()) {
+      return false;
+    } else if (email.trim() == Configuration.instance.getEmail()) {
       await showErrorDialog(context, "Oops", "You cannot share with yourself");
-      return null;
+      return false;
     }
 
     ProgressDialog? dialog;
-    if (publicKey == null) {
-      if (showProgress) {
-        dialog = createProgressDialog(context, "Searching for user...");
-        await dialog.show();
-      }
+    String? publicKey;
+    if (showProgress) {
+      dialog = createProgressDialog(context, "Sharing...", isDismissible: true);
+      await dialog.show();
+    }
 
-      try {
-        publicKey = await UserService.instance.getPublicKey(email);
-        await dialog?.hide();
-      } catch (e) {
-        await dialog?.hide();
-        logger.severe("Failed to get public key", e);
-        showGenericErrorDialog(context: context);
-        return false;
-      }
+    try {
+      publicKey = await UserService.instance.getPublicKey(email);
+    } catch (e) {
+      await dialog?.hide();
+      logger.severe("Failed to get public key", e);
+      showGenericErrorDialog(context: context);
+      return false;
     }
-    // getPublicKey can return null
-    // ignore: unnecessary_null_comparison
+    // getPublicKey can return null when no user is associated with given
+    // email id
     if (publicKey == null || publicKey == '') {
       // todo: neeraj replace this as per the design where a new screen
       // is used for error. Do this change along with handling of network errors
@@ -242,16 +239,8 @@ class CollectionActions {
           ),
         ],
       );
-      return null;
+      return false;
     } else {
-      if (showProgress) {
-        dialog = createProgressDialog(
-          context,
-          "Sharing...",
-          isDismissible: true,
-        );
-        await dialog.show();
-      }
       try {
         final newSharees = await CollectionsService.instance
             .share(collection.id, email, publicKey, role);
@@ -271,6 +260,7 @@ class CollectionActions {
     }
   }
 
+  // deleteCollectionSheet returns true if the album is successfully deleted
   Future<bool> deleteCollectionSheet(
     BuildContext bContext,
     Collection collection,
@@ -280,6 +270,13 @@ class CollectionActions {
     if (collection.owner!.id != currentUserID) {
       throw AssertionError("Can not delete album owned by others");
     }
+    if (collection.hasSharees) {
+      final bool confirmDelete =
+          await _confirmSharedAlbumDeletion(bContext, collection);
+      if (!confirmDelete) {
+        return false;
+      }
+    }
     final actionResult = await showActionSheet(
       context: bContext,
       buttons: [
@@ -360,6 +357,37 @@ class CollectionActions {
     return false;
   }
 
+  // _confirmSharedAlbumDeletion should be shown when user tries to delete an
+  // album shared with other ente users.
+  Future<bool> _confirmSharedAlbumDeletion(
+    BuildContext context,
+    Collection collection,
+  ) async {
+    final ButtonAction? result = await showActionSheet(
+      context: context,
+      buttons: [
+        const ButtonWidget(
+          buttonType: ButtonType.critical,
+          isInAlert: true,
+          shouldStickToDarkTheme: true,
+          buttonAction: ButtonAction.first,
+          labelText: "Delete album",
+        ),
+        const ButtonWidget(
+          buttonType: ButtonType.secondary,
+          buttonAction: ButtonAction.cancel,
+          isInAlert: true,
+          shouldStickToDarkTheme: true,
+          labelText: "Cancel",
+        )
+      ],
+      title: "Delete shared album?",
+      body: "The album will be deleted for everyone\n\nYou will lose access to "
+          "shared photos in this album that are owned by others",
+    );
+    return result != null && result == ButtonAction.first;
+  }
+
   /*
   _moveFilesFromCurrentCollection removes the file from the current
   collection. Based on the file and collection ownership, files will be

+ 2 - 2
lib/ui/sharing/add_partipant_page.dart

@@ -187,11 +187,11 @@ class _AddParticipantPage extends State<AddParticipantPage> {
                               context,
                               widget.collection,
                               emailToAdd,
-                              role: widget.isAddingViewer
+                              widget.isAddingViewer
                                   ? CollectionParticipantRole.viewer
                                   : CollectionParticipantRole.collaborator,
                             );
-                            if (result != null && result && mounted) {
+                            if (result && mounted) {
                               Navigator.of(context).pop(true);
                             }
                           },

+ 14 - 14
lib/ui/sharing/album_participants_page.dart

@@ -155,14 +155,15 @@ class _AlbumParticipantsPageState extends State<AlbumParticipantsPage> {
                             currentUserID: currentUserID,
                           ),
                           menuItemColor: getEnteColorScheme(context).fillFaint,
-                          pressedColor: getEnteColorScheme(context).fillFaint,
                           trailingIcon: isOwner ? Icons.chevron_right : null,
                           trailingIconIsMuted: true,
-                          onTap: () async {
-                            if (isOwner) {
-                              _navigateToManageUser(currentUser);
-                            }
-                          },
+                          onTap: isOwner
+                              ? () async {
+                                  if (isOwner) {
+                                    _navigateToManageUser(currentUser);
+                                  }
+                                }
+                              : null,
                           isTopBorderRadiusRemoved: listIndex > 0,
                           isBottomBorderRadiusRemoved: true,
                           borderRadius: 8,
@@ -183,7 +184,6 @@ class _AlbumParticipantsPageState extends State<AlbumParticipantsPage> {
                       ),
                       leadingIcon: Icons.add_outlined,
                       menuItemColor: getEnteColorScheme(context).fillFaint,
-                      pressedColor: getEnteColorScheme(context).fillFaint,
                       onTap: () async {
                         _navigateToAddUser(false);
                       },
@@ -229,14 +229,15 @@ class _AlbumParticipantsPageState extends State<AlbumParticipantsPage> {
                             currentUserID: currentUserID,
                           ),
                           menuItemColor: getEnteColorScheme(context).fillFaint,
-                          pressedColor: getEnteColorScheme(context).fillFaint,
                           trailingIcon: isOwner ? Icons.chevron_right : null,
                           trailingIconIsMuted: true,
-                          onTap: () async {
-                            if (isOwner) {
-                              await _navigateToManageUser(currentUser);
-                            }
-                          },
+                          onTap: isOwner
+                              ? () async {
+                                  if (isOwner) {
+                                    await _navigateToManageUser(currentUser);
+                                  }
+                                }
+                              : null,
                           isTopBorderRadiusRemoved: listIndex > 0,
                           isBottomBorderRadiusRemoved: !isLastItem,
                           borderRadius: 8,
@@ -257,7 +258,6 @@ class _AlbumParticipantsPageState extends State<AlbumParticipantsPage> {
                       ),
                       leadingIcon: Icons.add_outlined,
                       menuItemColor: getEnteColorScheme(context).fillFaint,
-                      pressedColor: getEnteColorScheme(context).fillFaint,
                       onTap: () async {
                         _navigateToAddUser(true);
                       },

+ 52 - 15
lib/ui/sharing/manage_album_participant.dart

@@ -4,12 +4,16 @@ import 'package:photos/services/collections_service.dart';
 import 'package:photos/theme/colors.dart';
 import 'package:photos/theme/ente_theme.dart';
 import 'package:photos/ui/actions/collection/collection_sharing_actions.dart';
+import 'package:photos/ui/components/action_sheet_widget.dart';
+import 'package:photos/ui/components/button_widget.dart';
 import 'package:photos/ui/components/captioned_text_widget.dart';
 import 'package:photos/ui/components/divider_widget.dart';
 import 'package:photos/ui/components/menu_item_widget/menu_item_widget.dart';
 import 'package:photos/ui/components/menu_section_description_widget.dart';
 import 'package:photos/ui/components/menu_section_title.dart';
+import 'package:photos/ui/components/models/button_type.dart';
 import 'package:photos/ui/components/title_bar_title_widget.dart';
+import 'package:photos/utils/dialog_util.dart';
 
 class ManageIndividualParticipant extends StatefulWidget {
   final Collection collection;
@@ -34,6 +38,7 @@ class _ManageIndividualParticipantState
   Widget build(BuildContext context) {
     final colorScheme = getEnteColorScheme(context);
     final textTheme = getEnteTextTheme(context);
+    bool isConvertToViewSuccess = false;
     return Scaffold(
       appBar: AppBar(),
       body: Padding(
@@ -69,7 +74,6 @@ class _ManageIndividualParticipantState
               ),
               leadingIcon: Icons.edit_outlined,
               menuItemColor: getEnteColorScheme(context).fillFaint,
-              pressedColor: getEnteColorScheme(context).fillFaint,
               trailingIcon: widget.user.isCollaborator ? Icons.check : null,
               onTap: widget.user.isCollaborator
                   ? null
@@ -79,9 +83,10 @@ class _ManageIndividualParticipantState
                         context,
                         widget.collection,
                         widget.user.email,
-                        role: CollectionParticipantRole.collaborator,
+                        CollectionParticipantRole.collaborator,
+                        showProgress: true,
                       );
-                      if ((result ?? false) && mounted) {
+                      if (result && mounted) {
                         widget.user.role = CollectionParticipantRole
                             .collaborator
                             .toStringVal();
@@ -101,22 +106,55 @@ class _ManageIndividualParticipantState
               leadingIcon: Icons.photo_outlined,
               leadingIconColor: getEnteColorScheme(context).strokeBase,
               menuItemColor: getEnteColorScheme(context).fillFaint,
-              pressedColor: getEnteColorScheme(context).fillFaint,
               trailingIcon: widget.user.isViewer ? Icons.check : null,
               onTap: widget.user.isViewer
                   ? null
                   : () async {
-                      final result =
-                          await collectionActions.addEmailToCollection(
-                        context,
-                        widget.collection,
-                        widget.user.email,
-                        role: CollectionParticipantRole.viewer,
+                      final ButtonAction? result = await showActionSheet(
+                        context: context,
+                        buttons: [
+                          ButtonWidget(
+                            buttonType: ButtonType.critical,
+                            isInAlert: true,
+                            shouldStickToDarkTheme: true,
+                            buttonAction: ButtonAction.first,
+                            shouldSurfaceExecutionStates: true,
+                            labelText: "Yes, convert to viewer",
+                            onTap: () async {
+                              isConvertToViewSuccess =
+                                  await collectionActions.addEmailToCollection(
+                                context,
+                                widget.collection,
+                                widget.user.email,
+                                CollectionParticipantRole.viewer,
+                              );
+                            },
+                          ),
+                          const ButtonWidget(
+                            buttonType: ButtonType.secondary,
+                            buttonAction: ButtonAction.cancel,
+                            isInAlert: true,
+                            shouldStickToDarkTheme: true,
+                            labelText: "Cancel",
+                          )
+                        ],
+                        title: "Change permissions?",
+                        body:
+                            '${widget.user.email} will not be able to add more photos to this album\n\nThey will still be able to remove existing photos added by them',
                       );
-                      if ((result ?? false) && mounted) {
-                        widget.user.role =
-                            CollectionParticipantRole.viewer.toStringVal();
-                        setState(() => {});
+                      if (result != null) {
+                        if (result == ButtonAction.error) {
+                          showGenericErrorDialog(context: context);
+                        }
+                        if (result == ButtonAction.first &&
+                            isConvertToViewSuccess &&
+                            mounted) {
+                          // reset value
+                          isConvertToViewSuccess = false;
+                          widget.user.role =
+                              CollectionParticipantRole.viewer.toStringVal();
+                          setState(() => {});
+                        }
                       }
                     },
               isTopBorderRadiusRemoved: true,
@@ -136,7 +174,6 @@ class _ManageIndividualParticipantState
               leadingIcon: Icons.not_interested_outlined,
               leadingIconColor: warning500,
               menuItemColor: getEnteColorScheme(context).fillFaint,
-              pressedColor: getEnteColorScheme(context).fillFaint,
               surfaceExecutionStates: false,
               onTap: () async {
                 final result = await collectionActions.removeParticipant(

+ 0 - 4
lib/ui/sharing/manage_links_widget.dart

@@ -81,7 +81,6 @@ class _ManageSharedLinkWidgetState extends State<ManageSharedLinkWidget> {
                     ),
                     alignCaptionedTextToLeft: true,
                     menuItemColor: getEnteColorScheme(context).fillFaint,
-                    pressedColor: getEnteColorScheme(context).fillFaint,
                     trailingWidget: Switch.adaptive(
                       value: widget.collection!.publicURLs?.firstOrNull
                               ?.enableCollect ??
@@ -155,7 +154,6 @@ class _ManageSharedLinkWidgetState extends State<ManageSharedLinkWidget> {
                     isBottomBorderRadiusRemoved: true,
                     isTopBorderRadiusRemoved: true,
                     menuItemColor: getEnteColorScheme(context).fillFaint,
-                    pressedColor: getEnteColorScheme(context).fillFaint,
                     trailingWidget: Switch.adaptive(
                       value: widget.collection!.publicURLs?.firstOrNull
                               ?.enableDownload ??
@@ -187,7 +185,6 @@ class _ManageSharedLinkWidgetState extends State<ManageSharedLinkWidget> {
                     alignCaptionedTextToLeft: true,
                     isTopBorderRadiusRemoved: true,
                     menuItemColor: getEnteColorScheme(context).fillFaint,
-                    pressedColor: getEnteColorScheme(context).fillFaint,
                     trailingWidget: Switch.adaptive(
                       value: widget.collection!.publicURLs?.firstOrNull
                               ?.passwordEnabled ??
@@ -226,7 +223,6 @@ class _ManageSharedLinkWidgetState extends State<ManageSharedLinkWidget> {
                     leadingIcon: Icons.remove_circle_outline,
                     leadingIconColor: warning500,
                     menuItemColor: getEnteColorScheme(context).fillFaint,
-                    pressedColor: getEnteColorScheme(context).fillFaint,
                     surfaceExecutionStates: false,
                     onTap: () async {
                       final bool result = await sharingActions.disableUrl(

+ 20 - 24
lib/ui/sharing/share_collection_page.dart

@@ -72,7 +72,6 @@ class _ShareCollectionPageState extends State<ShareCollectionPage> {
         ),
         leadingIcon: Icons.add,
         menuItemColor: getEnteColorScheme(context).fillFaint,
-        pressedColor: getEnteColorScheme(context).fillFaint,
         borderRadius: 4.0,
         isTopBorderRadiusRemoved: _sharees.isNotEmpty,
         isBottomBorderRadiusRemoved: true,
@@ -102,7 +101,6 @@ class _ShareCollectionPageState extends State<ShareCollectionPage> {
         ),
         leadingIcon: Icons.add,
         menuItemColor: getEnteColorScheme(context).fillFaint,
-        pressedColor: getEnteColorScheme(context).fillFaint,
         borderRadius: 4.0,
         isTopBorderRadiusRemoved: _sharees.isNotEmpty,
         onTap: () async {
@@ -132,9 +130,7 @@ class _ShareCollectionPageState extends State<ShareCollectionPage> {
         height: 24,
       ),
       MenuSectionTitle(
-        title: hasUrl
-            ? "Public link enabled"
-            : (_sharees.isEmpty ? "Or share a link" : "Share a link"),
+        title: hasUrl ? "Public link enabled" : "Share a link",
         iconData: Icons.public,
       ),
     ]);
@@ -149,7 +145,6 @@ class _ShareCollectionPageState extends State<ShareCollectionPage> {
             leadingIcon: Icons.error_outline,
             leadingIconColor: getEnteColorScheme(context).warning500,
             menuItemColor: getEnteColorScheme(context).fillFaint,
-            pressedColor: getEnteColorScheme(context).fillFaint,
             onTap: () async {},
             isBottomBorderRadiusRemoved: true,
           ),
@@ -169,7 +164,6 @@ class _ShareCollectionPageState extends State<ShareCollectionPage> {
               ),
               leadingIcon: Icons.copy,
               menuItemColor: getEnteColorScheme(context).fillFaint,
-              pressedColor: getEnteColorScheme(context).fillFaint,
               showOnlyLoadingState: true,
               onTap: () async {
                 await Clipboard.setData(ClipboardData(text: url));
@@ -188,7 +182,6 @@ class _ShareCollectionPageState extends State<ShareCollectionPage> {
               ),
               leadingIcon: Icons.adaptive.share,
               menuItemColor: getEnteColorScheme(context).fillFaint,
-              pressedColor: getEnteColorScheme(context).fillFaint,
               onTap: () async {
                 shareText(url);
               },
@@ -213,7 +206,6 @@ class _ShareCollectionPageState extends State<ShareCollectionPage> {
             leadingIcon: Icons.link,
             trailingIcon: Icons.navigate_next,
             menuItemColor: getEnteColorScheme(context).fillFaint,
-            pressedColor: getEnteColorScheme(context).fillFaint,
             trailingIconIsMuted: true,
             onTap: () async {
               routeToPage(
@@ -238,7 +230,6 @@ class _ShareCollectionPageState extends State<ShareCollectionPage> {
           ),
           leadingIcon: Icons.link,
           menuItemColor: getEnteColorScheme(context).fillFaint,
-          pressedColor: getEnteColorScheme(context).fillFaint,
           isBottomBorderRadiusRemoved: true,
           showOnlyLoadingState: true,
           onTap: () async {
@@ -249,9 +240,17 @@ class _ShareCollectionPageState extends State<ShareCollectionPage> {
             }
           },
         ),
-        DividerWidget(
-          dividerType: DividerType.menu,
-          bgColor: getEnteColorScheme(context).fillFaint,
+        _sharees.isEmpty
+            ? const MenuSectionDescriptionWidget(
+                content: "Share with non-ente users",
+              )
+            : const SizedBox.shrink(),
+        const SizedBox(
+          height: 24,
+        ),
+        const MenuSectionTitle(
+          title: "Collaborative link",
+          iconData: Icons.public,
         ),
         MenuItemWidget(
           captionedTextWidget: const CaptionedTextWidget(
@@ -260,7 +259,6 @@ class _ShareCollectionPageState extends State<ShareCollectionPage> {
           ),
           leadingIcon: Icons.link,
           menuItemColor: getEnteColorScheme(context).fillFaint,
-          pressedColor: getEnteColorScheme(context).fillFaint,
           showOnlyLoadingState: true,
           onTap: () async {
             final bool result = await collectionActions.enableUrl(
@@ -273,15 +271,14 @@ class _ShareCollectionPageState extends State<ShareCollectionPage> {
             }
           },
         ),
+        _sharees.isEmpty
+            ? const MenuSectionDescriptionWidget(
+                content:
+                    "Create a link to allow people to add and view photos in "
+                    "your shared album without needing an ente app or account. Great for collecting event photos.",
+              )
+            : const SizedBox.shrink(),
       ]);
-      if (_sharees.isEmpty && !hasUrl) {
-        children.add(
-          const MenuSectionDescriptionWidget(
-            content:
-                "Links allow people without an ente account to view and add photos to your shared albums.",
-          ),
-        );
-      }
     }
 
     return Scaffold(
@@ -300,6 +297,7 @@ class _ShareCollectionPageState extends State<ShareCollectionPage> {
               padding:
                   const EdgeInsets.symmetric(vertical: 4.0, horizontal: 16),
               child: Column(
+                crossAxisAlignment: CrossAxisAlignment.start,
                 children: children,
               ),
             ),
@@ -338,7 +336,6 @@ class EmailItemWidget extends StatelessWidget {
             ),
             leadingIconSize: 24,
             menuItemColor: getEnteColorScheme(context).fillFaint,
-            pressedColor: getEnteColorScheme(context).fillFaint,
             trailingIconIsMuted: true,
             trailingIcon: Icons.chevron_right,
             onTap: () async {
@@ -364,7 +361,6 @@ class EmailItemWidget extends StatelessWidget {
             ),
             leadingIcon: Icons.people_outline,
             menuItemColor: getEnteColorScheme(context).fillFaint,
-            pressedColor: getEnteColorScheme(context).fillFaint,
             trailingIconIsMuted: true,
             trailingIcon: Icons.chevron_right,
             onTap: () async {

+ 0 - 2
lib/ui/tools/debug/app_storage_viewer.dart

@@ -161,8 +161,6 @@ class _AppStorageViewerState extends State<AppStorageViewer> {
                             ),
                             menuItemColor:
                                 getEnteColorScheme(context).fillFaint,
-                            pressedColor:
-                                getEnteColorScheme(context).fillFaintPressed,
                             borderRadius: 8,
                             alwaysShowSuccessState: true,
                             onTap: () async {

+ 31 - 14
lib/ui/viewer/gallery/gallery_app_bar_widget.dart

@@ -18,6 +18,7 @@ import 'package:photos/services/sync_service.dart';
 import 'package:photos/services/update_service.dart';
 import 'package:photos/ui/actions/collection/collection_sharing_actions.dart';
 import 'package:photos/ui/common/rename_dialog.dart';
+import 'package:photos/ui/components/action_sheet_widget.dart';
 import 'package:photos/ui/components/button_widget.dart';
 import 'package:photos/ui/components/dialog_widget.dart';
 import 'package:photos/ui/components/models/button_type.dart';
@@ -138,21 +139,37 @@ class _GalleryAppBarWidgetState extends State<GalleryAppBarWidget> {
   }
 
   Future<dynamic> _leaveAlbum(BuildContext context) async {
-    final result = await showChoiceDialog(
-      context,
-      title: "Leave shared album",
-      body: "You will leave the album, and it will stop being visible to you",
-      firstButtonLabel: "Yes, leave",
-      isCritical: true,
-      firstButtonOnTap: () async {
-        await CollectionsService.instance.leaveAlbum(widget.collection!);
-        if (mounted) {
-          Navigator.of(context).pop();
-        }
-      },
+    final ButtonAction? result = await showActionSheet(
+      context: context,
+      buttons: [
+        ButtonWidget(
+          buttonType: ButtonType.critical,
+          isInAlert: true,
+          shouldStickToDarkTheme: true,
+          buttonAction: ButtonAction.first,
+          shouldSurfaceExecutionStates: true,
+          labelText: "Leave album",
+          onTap: () async {
+            await CollectionsService.instance.leaveAlbum(widget.collection!);
+          },
+        ),
+        const ButtonWidget(
+          buttonType: ButtonType.secondary,
+          buttonAction: ButtonAction.cancel,
+          isInAlert: true,
+          shouldStickToDarkTheme: true,
+          labelText: "Cancel",
+        )
+      ],
+      title: "Leave shared album?",
+      body: "Photos added by you will be removed from the album",
     );
-    if (result == ButtonAction.error) {
-      showGenericErrorDialog(context: context);
+    if (result != null && mounted) {
+      if (result == ButtonAction.error) {
+        showGenericErrorDialog(context: context);
+      } else if (result == ButtonAction.first) {
+        Navigator.of(context).pop();
+      }
     }
   }
 

+ 1 - 1
pubspec.yaml

@@ -12,7 +12,7 @@ description: ente photos application
 # Read more about iOS versioning at
 # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
 
-version: 0.7.16+416
+version: 0.7.18+418
 
 environment:
   sdk: '>=2.17.0 <3.0.0'