Parcourir la source

Merge branch 'collaboration_view' into action-sheet-add-on

ashilkn il y a 2 ans
Parent
commit
2ee6e26e48

+ 20 - 2
lib/models/gallery_type.dart

@@ -46,16 +46,34 @@ extension GalleyTypeExtension on GalleryType {
     }
   }
 
-  bool showDeleteOption() {
+  // showDeleteTopOption indicates whether we should show
+  // delete icon as iconButton
+  bool showDeleteIconOption() {
     switch (this) {
       case GalleryType.ownedCollection:
       case GalleryType.searchResults:
       case GalleryType.homepage:
       case GalleryType.favorite:
+      case GalleryType.localFolder:
         return true;
+      case GalleryType.trash:
+      case GalleryType.archive:
       case GalleryType.hidden:
+      case GalleryType.sharedCollection:
+        return false;
+    }
+  }
+
+  bool showDeleteOption() {
+    switch (this) {
+      case GalleryType.ownedCollection:
+      case GalleryType.searchResults:
+      case GalleryType.homepage:
+      case GalleryType.favorite:
       case GalleryType.archive:
+      case GalleryType.hidden:
       case GalleryType.localFolder:
+        return true;
       case GalleryType.trash:
       case GalleryType.sharedCollection:
         return false;
@@ -104,10 +122,10 @@ extension GalleyTypeExtension on GalleryType {
       case GalleryType.ownedCollection:
       case GalleryType.homepage:
       case GalleryType.searchResults:
+      case GalleryType.archive:
         return true;
 
       case GalleryType.hidden:
-      case GalleryType.archive:
       case GalleryType.localFolder:
       case GalleryType.trash:
       case GalleryType.favorite:

+ 9 - 1
lib/models/magic_metadata.dart

@@ -15,6 +15,7 @@ const subTypeKey = 'subType';
 const pubMagicKeyEditedTime = 'editedTime';
 const pubMagicKeyEditedName = 'editedName';
 const pubMagicKeyCaption = "caption";
+const pubMagicKeyUploaderName = "uploaderName";
 
 class MagicMetadata {
   // 0 -> visible
@@ -41,8 +42,14 @@ class PubMagicMetadata {
   int? editedTime;
   String? editedName;
   String? caption;
+  String? uploaderName;
 
-  PubMagicMetadata({this.editedTime, this.editedName, this.caption});
+  PubMagicMetadata({
+    this.editedTime,
+    this.editedName,
+    this.caption,
+    this.uploaderName,
+  });
 
   factory PubMagicMetadata.fromEncodedJson(String encodedJson) =>
       PubMagicMetadata.fromJson(jsonDecode(encodedJson));
@@ -56,6 +63,7 @@ class PubMagicMetadata {
       editedTime: map[pubMagicKeyEditedTime],
       editedName: map[pubMagicKeyEditedName],
       caption: map[pubMagicKeyCaption],
+      uploaderName: map[pubMagicKeyUploaderName],
     );
   }
 }

+ 1 - 1
lib/theme/colors.dart

@@ -191,7 +191,7 @@ const Color strokeMutedDark = Color.fromRGBO(255, 255, 255, 0.24);
 const Color strokeFaintDark = Color.fromRGBO(255, 255, 255, 0.16);
 const Color strokeFainterDark = Color.fromRGBO(255, 255, 255, 0.08);
 const Color blurStrokeBaseDark = Color.fromRGBO(255, 255, 255, 0.90);
-const Color blurStrokeFaintDark = Color.fromRGBO(255, 255, 255, 0.08);
+const Color blurStrokeFaintDark = Color.fromRGBO(255, 255, 255, 0.06);
 const Color blurStrokePressedDark = Color.fromRGBO(255, 255, 255, 0.50);
 
 // Other colors

+ 55 - 8
lib/ui/actions/collection/collection_file_actions.dart

@@ -1,9 +1,11 @@
 import 'package:flutter/cupertino.dart';
 import 'package:flutter/widgets.dart';
+import 'package:photos/db/files_db.dart';
 import 'package:photos/models/collection.dart';
 import 'package:photos/models/file.dart';
 import 'package:photos/models/selected_files.dart';
 import 'package:photos/services/favorites_service.dart';
+import 'package:photos/theme/ente_theme.dart';
 import 'package:photos/ui/actions/collection/collection_sharing_actions.dart';
 import 'package:photos/ui/common/progress_dialog.dart';
 import 'package:photos/utils/dialog_util.dart';
@@ -16,21 +18,41 @@ extension CollectionFileActions on CollectionActions {
     SelectedFiles selectedFiles,
   ) async {
     final count = selectedFiles.files.length;
+    final textTheme = getEnteTextTheme(context);
+    final showDeletePrompt = await _anyItemPresentOnlyInCurrentAlbum(
+        selectedFiles.files, collection.id);
+    final String title =
+        showDeletePrompt ? "Delete items?" : "Remove from album?";
+    final String message1 = showDeletePrompt
+        ? "Some of the selected items are present only in this album and will be deleted."
+        : "Selected items will be removed from this album.";
+
+    final String message2 = showDeletePrompt
+        ? "\n\nItems which are also "
+            "present in other albums will be removed from this album but will remain elsewhere."
+        : "";
+
     final action = CupertinoActionSheet(
       title: Text(
-        "Remove " +
-            count.toString() +
-            " file" +
-            (count == 1 ? "" : "s") +
-            " from " +
-            (collection.name ?? ""),
+        title,
+        style: textTheme.h3Bold,
+        textAlign: TextAlign.left,
+      ),
+      message: RichText(
+        text: TextSpan(
+          children: [
+            TextSpan(text: message1, style: textTheme.body),
+            TextSpan(text: message2, style: textTheme.body)
+          ],
+        ),
       ),
       actions: <Widget>[
         CupertinoActionSheetAction(
           isDestructiveAction: true,
           onPressed: () async {
             Navigator.of(context, rootNavigator: true).pop();
-            final dialog = createProgressDialog(context, "Removing files...");
+            final dialog = createProgressDialog(context,
+                showDeletePrompt ? "Deleting files..." : "Removing files...",);
             await dialog.show();
             try {
               await collectionsService.removeFromCollection(
@@ -45,7 +67,7 @@ extension CollectionFileActions on CollectionActions {
               showGenericErrorDialog(context);
             }
           },
-          child: const Text("Remove"),
+          child: Text(showDeletePrompt ? "Yes, delete" : "Yes, remove"),
         ),
       ],
       cancelButton: CupertinoActionSheetAction(
@@ -58,6 +80,31 @@ extension CollectionFileActions on CollectionActions {
     await showCupertinoModalPopup(context: context, builder: (_) => action);
   }
 
+  // check if any of the file only belongs in the given collection id.
+  // if true, then we need to warn the user that some of the items will be
+  // deleted
+  Future<bool> _anyItemPresentOnlyInCurrentAlbum(
+    Set<File> files,
+    int collectionID,
+  ) async {
+    final List<int> uploadedIDs = files
+        .where((e) => e.uploadedFileID != null)
+        .map((e) => e.uploadedFileID!)
+        .toList();
+
+    final Map<int, List<File>> collectionToFilesMap =
+        await FilesDB.instance.getAllFilesGroupByCollectionID(uploadedIDs);
+    final Set<int> ids = uploadedIDs.toSet();
+    for (MapEntry<int, List<File>> entry in collectionToFilesMap.entries) {
+      if (entry.key == collectionID) {
+        logger.finest('ignore the collection from which remove is happening');
+        continue;
+      }
+      ids.removeAll(entry.value.map((f) => f.uploadedFileID!).toSet());
+    }
+    return ids.isNotEmpty;
+  }
+
   Future<bool> updateFavorites(
     BuildContext context,
     List<File> files,

+ 0 - 1
lib/ui/collections/collection_item_widget.dart

@@ -43,7 +43,6 @@ class CollectionItem extends StatelessWidget {
                             c.thumbnail,
                             shouldShowArchiveStatus: c.collection.isArchived(),
                             showFavForAlbumOnly: true,
-                            shownAsAlbumCover: true,
                             key: Key(heroTag),
                           )
                         : const NoThumbnailWidget(),

+ 3 - 0
lib/ui/components/bottom_action_bar/action_bar_widget.dart

@@ -9,11 +9,14 @@ class ActionBarWidget extends StatefulWidget {
   final List<Widget> iconButtons;
   final SelectedFiles? selectedFiles;
   final GalleryType galleryType;
+  final bool isCollaborator;
+
   const ActionBarWidget({
     required this.iconButtons,
     required this.galleryType,
     this.text,
     this.selectedFiles,
+    this.isCollaborator = false,
     super.key,
   });
 

+ 4 - 1
lib/ui/components/bottom_action_bar/bottom_action_bar_widget.dart

@@ -43,7 +43,7 @@ class BottomActionBarWidget extends StatelessWidget {
         : 0;
     return ClipRRect(
       child: BackdropFilter(
-        filter: ImageFilter.blur(sigmaX: blurBase, sigmaY: blurBase),
+        filter: ImageFilter.blur(sigmaX: blurFaint, sigmaY: blurFaint),
         child: Container(
           color: colorScheme.backdropBase,
           padding: EdgeInsets.only(
@@ -148,6 +148,7 @@ class _ExpansionIconWidgetState extends State<ExpansionIconWidget> {
 
   @override
   Widget build(BuildContext context) {
+    final iconColor = getEnteColorScheme(context).blurStrokeBase;
     return AnimatedSwitcher(
       duration: const Duration(milliseconds: 200),
       switchInCurve: Curves.easeInOutExpo,
@@ -160,6 +161,7 @@ class _ExpansionIconWidgetState extends State<ExpansionIconWidget> {
               },
               icon: Icons.expand_more_outlined,
               iconButtonType: IconButtonType.primary,
+              iconColor: iconColor,
             )
           : IconButtonWidget(
               key: const ValueKey<bool>(true),
@@ -169,6 +171,7 @@ class _ExpansionIconWidgetState extends State<ExpansionIconWidget> {
               },
               icon: Icons.more_horiz_outlined,
               iconButtonType: IconButtonType.primary,
+              iconColor: iconColor,
             ),
     );
   }

+ 4 - 2
lib/ui/huge_listview/lazy_loading_gallery.dart

@@ -403,6 +403,7 @@ class _LazyLoadingGridViewState extends State<LazyLoadingGridView> {
   }
 
   Widget _buildFile(BuildContext context, File file) {
+    final isFileSelected = widget.selectedFiles.isFileSelected(file);
     return GestureDetector(
       onTap: () {
         if (widget.selectedFiles.files.isNotEmpty) {
@@ -424,7 +425,7 @@ class _LazyLoadingGridViewState extends State<LazyLoadingGridView> {
               child: ColorFiltered(
                 colorFilter: ColorFilter.mode(
                   Colors.black.withOpacity(
-                    widget.selectedFiles.isFileSelected(file) ? 0.4 : 0,
+                    isFileSelected ? 0.4 : 0,
                   ),
                   BlendMode.darken,
                 ),
@@ -437,11 +438,12 @@ class _LazyLoadingGridViewState extends State<LazyLoadingGridView> {
                   thumbnailSize: widget.photoGridSize < photoGridSizeDefault
                       ? thumbnailLargeSize
                       : thumbnailSmallSize,
+                  shouldShowOwnerAvatar: !isFileSelected,
                 ),
               ),
             ),
             Visibility(
-              visible: widget.selectedFiles.isFileSelected(file),
+              visible: isFileSelected,
               child: const Positioned(
                 right: 4,
                 top: 4,

+ 0 - 2
lib/ui/shared_collections_gallery.dart

@@ -296,7 +296,6 @@ class OutgoingCollectionItem extends StatelessWidget {
                   tag: "outgoing_collection" + c.thumbnail.tag,
                   child: ThumbnailWidget(
                     c.thumbnail,
-                    shownAsAlbumCover: true,
                     key: Key("outgoing_collection" + c.thumbnail.tag),
                   ),
                 ),
@@ -389,7 +388,6 @@ class IncomingCollectionItem extends StatelessWidget {
                     child: ThumbnailWidget(
                       c.thumbnail,
                       key: Key("shared_collection" + c.thumbnail.tag),
-                      shownAsAlbumCover: true,
                     ),
                   ),
                   Align(

+ 7 - 8
lib/ui/sharing/album_participants_page.dart

@@ -209,6 +209,7 @@ class _AlbumParticipantsPageState extends State<AlbumParticipantsPage> {
                     final currentUser = viewers[listIndex];
                     final isSameAsLoggedInUser =
                         currentUserID == currentUser.id;
+                    final isLastItem = !isOwner && index == viewers.length;
                     return Column(
                       children: [
                         MenuItemWidget(
@@ -234,17 +235,15 @@ class _AlbumParticipantsPageState extends State<AlbumParticipantsPage> {
                             }
                           },
                           isTopBorderRadiusRemoved: listIndex > 0,
-                          isBottomBorderRadiusRemoved:
-                              listIndex == viewers.length,
+                          isBottomBorderRadiusRemoved: !isLastItem,
                           borderRadius: 8,
                         ),
-                        listIndex == viewers.length
-                            ? DividerWidget(
+                        isLastItem
+                            ? const SizedBox.shrink()
+                            : DividerWidget(
                                 dividerType: DividerType.menu,
-                                bgColor:
-                                    getEnteColorScheme(context).blurStrokeFaint,
-                              )
-                            : const SizedBox.shrink(),
+                                bgColor: getEnteColorScheme(context).fillFaint,
+                              ),
                       ],
                     );
                   } else if (index == (1 + viewers.length) && isOwner) {

+ 4 - 4
lib/ui/sharing/share_collection_page.dart

@@ -139,7 +139,7 @@ class _ShareCollectionPageState extends State<ShareCollectionPage> {
             ),
             DividerWidget(
               dividerType: DividerType.menu,
-              bgColor: getEnteColorScheme(context).blurStrokeFaint,
+              bgColor: getEnteColorScheme(context).fillFaint,
             ),
             MenuItemWidget(
               captionedTextWidget: const CaptionedTextWidget(
@@ -163,7 +163,7 @@ class _ShareCollectionPageState extends State<ShareCollectionPage> {
         [
           DividerWidget(
             dividerType: DividerType.menu,
-            bgColor: getEnteColorScheme(context).blurStrokeFaint,
+            bgColor: getEnteColorScheme(context).fillFaint,
           ),
           MenuItemWidget(
             captionedTextWidget: const CaptionedTextWidget(
@@ -268,7 +268,7 @@ class EmailItemWidget extends StatelessWidget {
           ),
           DividerWidget(
             dividerType: DividerType.menu,
-            bgColor: getEnteColorScheme(context).blurStrokeFaint,
+            bgColor: getEnteColorScheme(context).fillFaint,
           ),
         ],
       );
@@ -294,7 +294,7 @@ class EmailItemWidget extends StatelessWidget {
           ),
           DividerWidget(
             dividerType: DividerType.menu,
-            bgColor: getEnteColorScheme(context).blurStrokeFaint,
+            bgColor: getEnteColorScheme(context).fillFaint,
           ),
         ],
       );

+ 33 - 17
lib/ui/viewer/actions/file_selection_actions_widget.dart

@@ -73,17 +73,32 @@ class _FileSelectionActionWidgetState extends State<FileSelectionActionWidget> {
         ? " (${split.ownedByCurrentUser.length})"
             ""
         : "";
+    final String suffixInPending = split.ownedByOtherUsers.isNotEmpty
+        ? " (${split.ownedByCurrentUser.length + split.pendingUploads.length})"
+            ""
+        : "";
+    final bool anyOwnedFiles =
+        split.pendingUploads.isNotEmpty || split.ownedByCurrentUser.isNotEmpty;
+    final bool anyUploadedFiles = split.ownedByCurrentUser.isNotEmpty;
+    bool showRemoveOption = widget.type.showRemoveFromAlbum();
+    if (showRemoveOption && widget.type == GalleryType.sharedCollection) {
+      showRemoveOption = split.ownedByCurrentUser.isNotEmpty;
+    }
     debugPrint('$runtimeType building  $mounted');
     final colorScheme = getEnteColorScheme(context);
     final List<List<BlurMenuItemWidget>> items = [];
     final List<BlurMenuItemWidget> firstList = [];
+    final showUploadIcon = widget.type == GalleryType.localFolder &&
+        split.ownedByCurrentUser.isEmpty;
     if (widget.type.showAddToAlbum()) {
       firstList.add(
         BlurMenuItemWidget(
-          leadingIcon: Icons.add_outlined,
-          labelText: "Add to album$suffix",
+          leadingIcon:
+              showUploadIcon ? Icons.cloud_upload_outlined : Icons.add_outlined,
+          labelText:
+              "Add to ${showUploadIcon ? 'ente' : 'album'}$suffixInPending",
           menuItemColor: colorScheme.fillFaint,
-          onTap: _addToAlbum,
+          onTap: anyOwnedFiles ? _addToAlbum : null,
         ),
       );
     }
@@ -93,20 +108,18 @@ class _FileSelectionActionWidgetState extends State<FileSelectionActionWidget> {
           leadingIcon: Icons.arrow_forward_outlined,
           labelText: "Move to album$suffix",
           menuItemColor: colorScheme.fillFaint,
-          onTap: _moveFiles,
+          onTap: anyUploadedFiles ? _moveFiles : null,
         ),
       );
     }
 
-    if (widget.type.showRemoveFromAlbum()) {
+    if (showRemoveOption) {
       firstList.add(
         BlurMenuItemWidget(
           leadingIcon: Icons.remove_outlined,
           labelText: "Remove from album$suffix",
           menuItemColor: colorScheme.fillFaint,
-          onTap: split.ownedByCurrentUser.isNotEmpty
-              ? _removeFilesFromAlbum
-              : null,
+          onTap: anyUploadedFiles ? _removeFilesFromAlbum : null,
         ),
       );
     }
@@ -115,9 +128,9 @@ class _FileSelectionActionWidgetState extends State<FileSelectionActionWidget> {
       firstList.add(
         BlurMenuItemWidget(
           leadingIcon: Icons.delete_outline,
-          labelText: "Delete$suffix",
+          labelText: "Delete$suffixInPending",
           menuItemColor: colorScheme.fillFaint,
-          onTap: _onDeleteClick,
+          onTap: anyOwnedFiles ? _onDeleteClick : null,
         ),
       );
     }
@@ -128,7 +141,7 @@ class _FileSelectionActionWidgetState extends State<FileSelectionActionWidget> {
           leadingIcon: Icons.visibility_off_outlined,
           labelText: "Hide$suffix",
           menuItemColor: colorScheme.fillFaint,
-          onTap: _onHideClick,
+          onTap: anyUploadedFiles ? _onHideClick : null,
         ),
       );
     } else if (widget.type.showUnHideOption()) {
@@ -147,13 +160,13 @@ class _FileSelectionActionWidgetState extends State<FileSelectionActionWidget> {
           leadingIcon: Icons.archive_outlined,
           labelText: "Archive$suffix",
           menuItemColor: colorScheme.fillFaint,
-          onTap: _onArchiveClick,
+          onTap: anyUploadedFiles ? _onArchiveClick : null,
         ),
       );
     } else if (widget.type.showUnArchiveOption()) {
       firstList.add(
         BlurMenuItemWidget(
-          leadingIcon: Icons.unarchive_outlined,
+          leadingIcon: Icons.unarchive,
           labelText: "Unarchive$suffix",
           menuItemColor: colorScheme.fillFaint,
           onTap: _onUnArchiveClick,
@@ -167,7 +180,7 @@ class _FileSelectionActionWidgetState extends State<FileSelectionActionWidget> {
           leadingIcon: Icons.favorite_border_rounded,
           labelText: "Favorite$suffix",
           menuItemColor: colorScheme.fillFaint,
-          onTap: _onFavoriteClick,
+          onTap: anyUploadedFiles ? _onFavoriteClick : null,
         ),
       );
     } else if (widget.type.showUnFavoriteOption()) {
@@ -183,10 +196,13 @@ class _FileSelectionActionWidgetState extends State<FileSelectionActionWidget> {
 
     if (firstList.isNotEmpty) {
       items.add(firstList);
+      return ExpandedMenuWidget(
+        items: items,
+      );
+    } else {
+      // TODO: Return "Select All" here
+      return const SizedBox.shrink();
     }
-    return ExpandedMenuWidget(
-      items: items,
-    );
   }
 
   Future<void> _moveFiles() async {

+ 79 - 21
lib/ui/viewer/actions/file_selection_overlay_bar.dart

@@ -1,13 +1,18 @@
 import 'package:flutter/cupertino.dart';
 import 'package:flutter/material.dart';
+import 'package:page_transition/page_transition.dart';
 import 'package:photos/models/collection.dart';
 import 'package:photos/models/device_collection.dart';
 import 'package:photos/models/gallery_type.dart';
+import 'package:photos/models/magic_metadata.dart';
 import 'package:photos/models/selected_files.dart';
+import 'package:photos/theme/ente_theme.dart';
 import 'package:photos/ui/components/bottom_action_bar/bottom_action_bar_widget.dart';
 import 'package:photos/ui/components/icon_button_widget.dart';
+import 'package:photos/ui/create_collection_page.dart';
 import 'package:photos/ui/viewer/actions/file_selection_actions_widget.dart';
 import 'package:photos/utils/delete_file_util.dart';
+import 'package:photos/utils/magic_util.dart';
 import 'package:photos/utils/share_util.dart';
 
 class FileSelectionOverlayBar extends StatefulWidget {
@@ -36,8 +41,7 @@ class _FileSelectionOverlayBarState extends State<FileSelectionOverlayBar> {
 
   @override
   void initState() {
-    showDeleteOption = (widget.galleryType == GalleryType.homepage ||
-        widget.galleryType == GalleryType.ownedCollection);
+    showDeleteOption = widget.galleryType.showDeleteIconOption();
     widget.selectedFiles.addListener(_selectedFilesListener);
     super.initState();
   }
@@ -52,6 +56,53 @@ class _FileSelectionOverlayBarState extends State<FileSelectionOverlayBar> {
   Widget build(BuildContext context) {
     debugPrint(
         '$runtimeType building with ${widget.selectedFiles.files.length}');
+    final List<IconButtonWidget> iconsButton = [];
+    final iconColor = getEnteColorScheme(context).blurStrokeBase;
+    if (showDeleteOption) {
+      iconsButton.add(
+        IconButtonWidget(
+          icon: Icons.delete_outlined,
+          iconButtonType: IconButtonType.primary,
+          iconColor: iconColor,
+          onTap: () => showDeleteSheet(context, widget.selectedFiles),
+        ),
+      );
+    }
+
+    if (widget.galleryType.showUnArchiveOption()) {
+      iconsButton.add(
+        IconButtonWidget(
+          icon: Icons.unarchive,
+          iconButtonType: IconButtonType.primary,
+          iconColor: iconColor,
+          onTap: () => _onUnArchiveClick(),
+        ),
+      );
+    }
+    if (widget.galleryType.showUnHideOption()) {
+      iconsButton.add(
+        IconButtonWidget(
+          icon: Icons.visibility_off_outlined,
+          iconButtonType: IconButtonType.primary,
+          iconColor: iconColor,
+          onTap: () => _selectionCollectionForAction(
+            CollectionActionType.unHide,
+          ),
+        ),
+      );
+    }
+    iconsButton.add(
+      IconButtonWidget(
+        icon: Icons.ios_share_outlined,
+        iconButtonType: IconButtonType.primary,
+        iconColor: getEnteColorScheme(context).blurStrokeBase,
+        onTap: () => shareSelected(
+          context,
+          shareButtonKey,
+          widget.selectedFiles.files,
+        ),
+      ),
+    );
     return ValueListenableBuilder(
       valueListenable: _bottomPosition,
       builder: (context, value, child) {
@@ -76,31 +127,38 @@ class _FileSelectionOverlayBarState extends State<FileSelectionOverlayBar> {
                 widget.selectedFiles.clearAll();
               }
             },
-            iconButtons: [
-              showDeleteOption
-                  ? IconButtonWidget(
-                      icon: Icons.delete_outlined,
-                      iconButtonType: IconButtonType.primary,
-                      onTap: () =>
-                          showDeleteSheet(context, widget.selectedFiles),
-                    )
-                  : const SizedBox.shrink(),
-              IconButtonWidget(
-                icon: Icons.ios_share_outlined,
-                iconButtonType: IconButtonType.primary,
-                onTap: () => shareSelected(
-                  context,
-                  shareButtonKey,
-                  widget.selectedFiles.files,
-                ),
-              ),
-            ],
+            iconButtons: iconsButton,
           ),
         );
       },
     );
   }
 
+  Future<void> _onUnArchiveClick() async {
+    await changeVisibility(
+      context,
+      widget.selectedFiles.files.toList(),
+      visibilityVisible,
+    );
+    widget.selectedFiles.clearAll();
+  }
+
+  Future<Object?> _selectionCollectionForAction(
+    CollectionActionType type,
+  ) async {
+    return Navigator.push(
+      context,
+      PageTransition(
+        type: PageTransitionType.bottomToTop,
+        child: CreateCollectionPage(
+          widget.selectedFiles,
+          null,
+          actionType: type,
+        ),
+      ),
+    );
+  }
+
   _selectedFilesListener() {
     widget.selectedFiles.files.isNotEmpty
         ? _bottomPosition.value = 0.0

+ 26 - 13
lib/ui/viewer/file/thumbnail_widget.dart

@@ -11,6 +11,7 @@ import 'package:photos/db/files_db.dart';
 import 'package:photos/db/trash_db.dart';
 import 'package:photos/events/files_updated_event.dart';
 import 'package:photos/events/local_photos_updated_event.dart';
+import 'package:photos/models/collection.dart';
 import 'package:photos/models/file.dart';
 import 'package:photos/models/file_type.dart';
 import 'package:photos/models/trash_file.dart';
@@ -30,7 +31,7 @@ class ThumbnailWidget extends StatefulWidget {
   final Duration diskLoadDeferDuration;
   final Duration serverLoadDeferDuration;
   final int thumbnailSize;
-  final bool shownAsAlbumCover;
+  final bool shouldShowOwnerAvatar;
 
   ThumbnailWidget(
     this.file, {
@@ -40,7 +41,7 @@ class ThumbnailWidget extends StatefulWidget {
     this.shouldShowLivePhotoOverlay = false,
     this.shouldShowArchiveStatus = false,
     this.showFavForAlbumOnly = false,
-    this.shownAsAlbumCover = false,
+    this.shouldShowOwnerAvatar = false,
     this.diskLoadDeferDuration,
     this.serverLoadDeferDuration,
     this.thumbnailSize = thumbnailSmallSize,
@@ -115,17 +116,29 @@ class _ThumbnailWidgetState extends State<ThumbnailWidget> {
           widget.shouldShowLivePhotoOverlay) {
         contentChildren.add(const LivePhotoOverlayIcon());
       }
-      if (widget.file.ownerID != null &&
-          widget.file.ownerID != Configuration.instance.getUserID() &&
-          widget.shownAsAlbumCover == false) {
-        // hide this icon if the current thumbnail is being showed as album
-        // cover
-        contentChildren.add(
-          OwnerAvatarOverlayIcon(
-            CollectionsService.instance
-                .getFileOwner(widget.file.ownerID, widget.file.collectionID),
-          ),
-        );
+      if (widget.shouldShowOwnerAvatar) {
+        final owner = CollectionsService.instance
+            .getFileOwner(widget.file.ownerID, widget.file.collectionID);
+        if (widget.file.ownerID != null &&
+            widget.file.ownerID != Configuration.instance.getUserID()) {
+          // hide this icon if the current thumbnail is being showed as album
+          // cover
+          contentChildren.add(
+            OwnerAvatarOverlayIcon(owner),
+          );
+        } else if (widget.file.pubMagicMetadata.uploaderName != null) {
+          contentChildren.add(
+            // Use uploadName hashCode as userID so that different uploader
+            // get avatar color
+            OwnerAvatarOverlayIcon(
+              User(
+                id: widget.file.pubMagicMetadata.uploaderName.hashCode,
+                email: owner.email,
+                name: widget.file.pubMagicMetadata.uploaderName,
+              ),
+            ),
+          );
+        }
       }
       content = contentChildren.length == 1
           ? contentChildren.first

+ 3 - 3
lib/ui/viewer/gallery/archive_page.dart

@@ -9,9 +9,9 @@ import 'package:photos/models/gallery_type.dart';
 import 'package:photos/models/magic_metadata.dart';
 import 'package:photos/models/selected_files.dart';
 import 'package:photos/services/collections_service.dart';
+import 'package:photos/ui/viewer/actions/file_selection_overlay_bar.dart';
 import 'package:photos/ui/viewer/gallery/gallery.dart';
 import 'package:photos/ui/viewer/gallery/gallery_app_bar_widget.dart';
-import 'package:photos/ui/viewer/gallery/gallery_overlay_widget.dart';
 
 class ArchivePage extends StatelessWidget {
   final String tagPrefix;
@@ -78,10 +78,10 @@ class ArchivePage extends StatelessWidget {
         alignment: Alignment.bottomCenter,
         children: [
           gallery,
-          GalleryOverlayWidget(
+          FileSelectionOverlayBar(
             overlayType,
             _selectedFiles,
-          )
+          ),
         ],
       ),
     );

+ 2 - 2
lib/ui/viewer/gallery/device_folder_page.dart

@@ -12,9 +12,9 @@ import 'package:photos/models/device_collection.dart';
 import 'package:photos/models/gallery_type.dart';
 import 'package:photos/models/selected_files.dart';
 import 'package:photos/services/remote_sync_service.dart';
+import 'package:photos/ui/viewer/actions/file_selection_overlay_bar.dart';
 import 'package:photos/ui/viewer/gallery/gallery.dart';
 import 'package:photos/ui/viewer/gallery/gallery_app_bar_widget.dart';
-import 'package:photos/ui/viewer/gallery/gallery_overlay_widget.dart';
 
 class DeviceFolderPage extends StatelessWidget {
   final DeviceCollection deviceCollection;
@@ -61,7 +61,7 @@ class DeviceFolderPage extends StatelessWidget {
         alignment: Alignment.bottomCenter,
         children: [
           gallery,
-          GalleryOverlayWidget(
+          FileSelectionOverlayBar(
             GalleryType.localFolder,
             _selectedFiles,
           )

+ 3 - 3
lib/ui/viewer/gallery/hidden_page.dart

@@ -8,10 +8,10 @@ import 'package:photos/events/files_updated_event.dart';
 import 'package:photos/models/gallery_type.dart';
 import 'package:photos/models/selected_files.dart';
 import 'package:photos/services/collections_service.dart';
+import 'package:photos/ui/viewer/actions/file_selection_overlay_bar.dart';
 import 'package:photos/ui/viewer/gallery/empty_hidden_widget.dart';
 import 'package:photos/ui/viewer/gallery/gallery.dart';
 import 'package:photos/ui/viewer/gallery/gallery_app_bar_widget.dart';
-import 'package:photos/ui/viewer/gallery/gallery_overlay_widget.dart';
 
 class HiddenPage extends StatelessWidget {
   final String tagPrefix;
@@ -80,10 +80,10 @@ class HiddenPage extends StatelessWidget {
         alignment: Alignment.bottomCenter,
         children: [
           gallery,
-          GalleryOverlayWidget(
+          FileSelectionOverlayBar(
             overlayType,
             _selectedFiles,
-          )
+          ),
         ],
       ),
     );

+ 2 - 1
lib/ui/viewer/search/result/search_result_page.dart

@@ -9,6 +9,7 @@ import 'package:photos/models/file_load_result.dart';
 import 'package:photos/models/gallery_type.dart';
 import 'package:photos/models/search/search_result.dart';
 import 'package:photos/models/selected_files.dart';
+import 'package:photos/ui/viewer/actions/file_selection_overlay_bar.dart';
 import 'package:photos/ui/viewer/gallery/gallery.dart';
 import 'package:photos/ui/viewer/gallery/gallery_app_bar_widget.dart';
 import 'package:photos/ui/viewer/gallery/gallery_overlay_widget.dart';
@@ -66,7 +67,7 @@ class SearchResultPage extends StatelessWidget {
         alignment: Alignment.bottomCenter,
         children: [
           gallery,
-          GalleryOverlayWidget(
+          FileSelectionOverlayBar(
             overlayType,
             _selectedFiles,
           )

+ 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.6.74+394
+version: 0.6.76+396
 
 environment:
   sdk: '>=2.17.0 <3.0.0'