From 2449dbe0cd0dc2d84b980a95b4d74c061fde2ee8 Mon Sep 17 00:00:00 2001 From: Aman Raj Singh Mourya Date: Tue, 22 Apr 2025 12:42:40 +0530 Subject: [PATCH] [mob][photos] feat: update selection logic to exclude favorite collections from actions --- .../lib/ui/collections/album/list_item.dart | 86 +++-- mobile/lib/ui/collections/album/row_item.dart | 337 +++++++++--------- .../album_selection_action_widget.dart | 10 +- 3 files changed, 225 insertions(+), 208 deletions(-) diff --git a/mobile/lib/ui/collections/album/list_item.dart b/mobile/lib/ui/collections/album/list_item.dart index 2a9d7a4ac8..97a31f0938 100644 --- a/mobile/lib/ui/collections/album/list_item.dart +++ b/mobile/lib/ui/collections/album/list_item.dart @@ -30,30 +30,31 @@ class AlbumListItemWidget extends StatelessWidget { final textTheme = getEnteTextTheme(context); final colorScheme = getEnteColorScheme(context); const sideOfThumbnail = 60.0; + final bool isFavCollection = collection.type == CollectionType.favorites; - return ListenableBuilder( - listenable: selectedAlbums!, - builder: (context, _) { - final isSelected = selectedAlbums?.isAlbumSelected(collection) ?? false; - - return GestureDetector( - onTap: () { - if (onTapCallback != null) { - onTapCallback!(collection); - } - }, - onLongPress: () { - if (onLongPressCallback != null) { - onLongPressCallback!(collection); - } - }, - behavior: HitTestBehavior.opaque, - child: AnimatedContainer( + return GestureDetector( + onTap: () { + if (onTapCallback != null) { + onTapCallback!(collection); + } + }, + onLongPress: () { + if (onLongPressCallback != null) { + onLongPressCallback!(collection); + } + }, + behavior: HitTestBehavior.opaque, + child: ListenableBuilder( + listenable: selectedAlbums!, + builder: (context, _) { + final isSelected = + selectedAlbums?.isAlbumSelected(collection) ?? false; + return AnimatedContainer( curve: Curves.easeOut, duration: const Duration(milliseconds: 200), decoration: BoxDecoration( border: Border.all( - color: isSelected + color: isSelected & !isFavCollection ? colorScheme.strokeMuted : colorScheme.strokeFainter, ), @@ -81,7 +82,6 @@ class AlbumListItemWidget extends StatelessWidget { builder: (context, snapshot) { if (snapshot.hasData) { final thumbnail = snapshot.data!; - return ThumbnailWidget( thumbnail, showFavForAlbumOnly: true, @@ -143,29 +143,37 @@ class AlbumListItemWidget extends StatelessWidget { ), Flexible( flex: 1, - child: AnimatedSwitcher( - duration: const Duration(milliseconds: 200), - switchInCurve: Curves.easeOut, - switchOutCurve: Curves.easeIn, - child: isSelected - ? IconButtonWidget( - key: const ValueKey("selected"), - icon: Icons.check_circle_rounded, - iconButtonType: IconButtonType.secondary, - iconColor: colorScheme.blurStrokeBase, - ) - : const IconButtonWidget( - key: ValueKey("unselected"), - icon: Icons.chevron_right_outlined, - iconButtonType: IconButtonType.secondary, - ), + child: ListenableBuilder( + listenable: selectedAlbums!, + builder: (context, _) { + final isSelected = + selectedAlbums?.isAlbumSelected(collection) ?? false; + + return AnimatedSwitcher( + duration: const Duration(milliseconds: 200), + switchInCurve: Curves.easeOut, + switchOutCurve: Curves.easeIn, + child: isSelected & !isFavCollection + ? IconButtonWidget( + key: const ValueKey("selected"), + icon: Icons.check_circle_rounded, + iconButtonType: IconButtonType.secondary, + iconColor: colorScheme.blurStrokeBase, + ) + : const IconButtonWidget( + key: ValueKey("unselected"), + icon: Icons.chevron_right_outlined, + iconButtonType: IconButtonType.secondary, + ), + ); + }, ), ), ], ), - ), - ); - }, + ); + }, + ), ); } } diff --git a/mobile/lib/ui/collections/album/row_item.dart b/mobile/lib/ui/collections/album/row_item.dart index 7c8e273a3a..481d4bda50 100644 --- a/mobile/lib/ui/collections/album/row_item.dart +++ b/mobile/lib/ui/collections/album/row_item.dart @@ -53,80 +53,82 @@ class AlbumRowItemWidget extends StatelessWidget { color: c.publicURLs.first.isExpired ? warning500 : strokeBaseDark, ) : null; - return ListenableBuilder( - listenable: selectedAlbums ?? ValueNotifier(false), - builder: (context, _) { - final bool isSelected = selectedAlbums?.isAlbumSelected(c) ?? false; - return GestureDetector( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Stack( - children: [ - ClipRRect( - borderRadius: BorderRadius.circular(1), - child: SizedBox( - height: sideOfThumbnail, - width: sideOfThumbnail, - child: Stack( - children: [ - FutureBuilder( - future: CollectionsService.instance.getCover(c), - builder: (context, snapshot) { - EnteFile? thumbnail; - if (snapshot.hasData) { - thumbnail = snapshot.data!; - } else { - //Need to use cached thumbnail so that the hero - //animation works as expected. - thumbnail = CollectionsService.instance - .getCoverCache(c); - } - if (thumbnail != null) { - final String heroTag = - tagPrefix + thumbnail.tag; - return Hero( - tag: heroTag, - transitionOnUserGestures: true, - child: ThumbnailWidget( - thumbnail, - shouldShowArchiveStatus: isOwner - ? c.isArchived() - : c.hasShareeArchived(), - showFavForAlbumOnly: true, - shouldShowSyncStatus: false, - shouldShowPinIcon: isOwner && c.isPinned, - key: Key(heroTag), - ), - ); - } else { - return const NoThumbnailWidget(); - } - }, - ), - if (isOwner && (c.hasSharees || c.hasLink)) - Hero( - tag: tagPrefix + "_sharees", + final bool isFavCollection = c.type == CollectionType.favorites; + + return GestureDetector( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Stack( + children: [ + ClipRRect( + borderRadius: BorderRadius.circular(1), + child: SizedBox( + height: sideOfThumbnail, + width: sideOfThumbnail, + child: Stack( + children: [ + FutureBuilder( + future: CollectionsService.instance.getCover(c), + builder: (context, snapshot) { + EnteFile? thumbnail; + if (snapshot.hasData) { + thumbnail = snapshot.data!; + } else { + //Need to use cached thumbnail so that the hero + //animation works as expected. + thumbnail = + CollectionsService.instance.getCoverCache(c); + } + if (thumbnail != null) { + final String heroTag = tagPrefix + thumbnail.tag; + return Hero( + tag: heroTag, transitionOnUserGestures: true, - child: Align( - alignment: Alignment.topLeft, - child: AlbumSharesIcons( - sharees: c.getSharees(), - type: AvatarType.mini, - trailingWidget: linkIcon, - ), + child: ThumbnailWidget( + thumbnail, + shouldShowArchiveStatus: isOwner + ? c.isArchived() + : c.hasShareeArchived(), + showFavForAlbumOnly: true, + shouldShowSyncStatus: false, + shouldShowPinIcon: isOwner && c.isPinned, + key: Key(heroTag), ), + ); + } else { + return const NoThumbnailWidget(); + } + }, + ), + if (isOwner && (c.hasSharees || c.hasLink)) + Hero( + tag: tagPrefix + "_sharees", + transitionOnUserGestures: true, + child: Align( + alignment: Alignment.topLeft, + child: AlbumSharesIcons( + sharees: c.getSharees(), + type: AvatarType.mini, + trailingWidget: linkIcon, ), - Align( - alignment: Alignment.topRight, - child: Hero( - tag: tagPrefix + "_album_selection", - transitionOnUserGestures: true, - child: AnimatedSwitcher( + ), + ), + Align( + alignment: Alignment.topRight, + child: Hero( + tag: tagPrefix + "_album_selection", + transitionOnUserGestures: true, + child: ListenableBuilder( + listenable: selectedAlbums ?? ValueNotifier(false), + builder: (context, _) { + final bool isSelected = + selectedAlbums?.isAlbumSelected(c) ?? false; + return AnimatedSwitcher( duration: const Duration(milliseconds: 200), switchInCurve: Curves.easeOut, switchOutCurve: Curves.easeIn, - child: isSelected + child: isSelected && !isFavCollection ? IconButtonWidget( key: const ValueKey("selected"), icon: Icons.check_circle_rounded, @@ -135,117 +137,116 @@ class AlbumRowItemWidget extends StatelessWidget { iconColor: colorScheme.blurStrokeBase, ) : null, + ); + }, + ), + ), + ), + if (!isOwner) + Align( + alignment: Alignment.bottomRight, + child: Hero( + tag: tagPrefix + "_owner_other", + transitionOnUserGestures: true, + child: Padding( + padding: const EdgeInsets.only( + right: 8.0, + bottom: 8.0, + ), + child: UserAvatarWidget( + c.owner, + thumbnailView: true, ), ), ), - if (!isOwner) - Align( - alignment: Alignment.bottomRight, - child: Hero( - tag: tagPrefix + "_owner_other", - transitionOnUserGestures: true, - child: Padding( - padding: const EdgeInsets.only( - right: 8.0, - bottom: 8.0, - ), - child: UserAvatarWidget( - c.owner, - thumbnailView: true, - ), - ), - ), - ), - ], - ), - ), - ), - ], - ), - const SizedBox(height: 4), - Hero( - tag: tagPrefix + "_title", - transitionOnUserGestures: true, - child: SizedBox( - width: sideOfThumbnail, - child: FutureBuilder( - future: showFileCount - ? CollectionsService.instance.getFileCount(c) - : Future.value(0), - builder: (context, snapshot) { - int? cachedCount; - if (showFileCount) { - if (snapshot.hasData) { - cachedCount = snapshot.data; - } else { - //Need to use cached count so that the hero - //animation works as expected without flickering. - cachedCount = - CollectionsService.instance.getCachedFileCount(c); - } - } - if (cachedCount != null && cachedCount > 0) { - final String textCount = - NumberFormat().format(cachedCount); - return Row( - children: [ - Container( - constraints: BoxConstraints( - maxWidth: sideOfThumbnail - - ((textCount.length + 3) * 10), - ), - child: Text( - c.displayName, - style: enteTextTheme.small, - overflow: TextOverflow.ellipsis, - ), - ), - RichText( - text: TextSpan( - style: enteTextTheme.smallMuted, - children: [ - TextSpan(text: ' \u2022 $textCount'), - ], - ), - ), - ], - ); - } else { - return Text( - c.displayName, - style: enteTextTheme.small, - overflow: TextOverflow.ellipsis, - ); - } - }, + ), + ], ), ), ), ], ), - onTap: () async { - if (onTapCallback != null) { - onTapCallback!(c); - return; - } - final thumbnail = await CollectionsService.instance.getCover(c); - // ignore: unawaited_futures - routeToPage( - context, - CollectionPage( - CollectionWithThumbnail(c, thumbnail), - tagPrefix: tagPrefix, - hasVerifiedLock: hasVerifiedLock, + const SizedBox(height: 4), + Hero( + tag: tagPrefix + "_title", + transitionOnUserGestures: true, + child: SizedBox( + width: sideOfThumbnail, + child: FutureBuilder( + future: showFileCount + ? CollectionsService.instance.getFileCount(c) + : Future.value(0), + builder: (context, snapshot) { + int? cachedCount; + if (showFileCount) { + if (snapshot.hasData) { + cachedCount = snapshot.data; + } else { + //Need to use cached count so that the hero + //animation works as expected without flickering. + cachedCount = + CollectionsService.instance.getCachedFileCount(c); + } + } + if (cachedCount != null && cachedCount > 0) { + final String textCount = NumberFormat().format(cachedCount); + return Row( + children: [ + Container( + constraints: BoxConstraints( + maxWidth: + sideOfThumbnail - ((textCount.length + 3) * 10), + ), + child: Text( + c.displayName, + style: enteTextTheme.small, + overflow: TextOverflow.ellipsis, + ), + ), + RichText( + text: TextSpan( + style: enteTextTheme.smallMuted, + children: [ + TextSpan(text: ' \u2022 $textCount'), + ], + ), + ), + ], + ); + } else { + return Text( + c.displayName, + style: enteTextTheme.small, + overflow: TextOverflow.ellipsis, + ); + } + }, ), - ); - }, - onLongPress: () { - if (onLongPressCallback != null) { - onLongPressCallback!(c); - } - }, + ), + ), + ], + ), + onTap: () async { + if (onTapCallback != null) { + onTapCallback!(c); + return; + } + final thumbnail = await CollectionsService.instance.getCover(c); + // ignore: unawaited_futures + routeToPage( + context, + CollectionPage( + CollectionWithThumbnail(c, thumbnail), + tagPrefix: tagPrefix, + hasVerifiedLock: hasVerifiedLock, + ), ); }, + onLongPress: () { + if (onLongPressCallback != null) { + onLongPressCallback!(c); + } + }, ); } } diff --git a/mobile/lib/ui/viewer/actions/album_selection_action_widget.dart b/mobile/lib/ui/viewer/actions/album_selection_action_widget.dart index a73781d192..7ad42c188a 100644 --- a/mobile/lib/ui/viewer/actions/album_selection_action_widget.dart +++ b/mobile/lib/ui/viewer/actions/album_selection_action_widget.dart @@ -122,6 +122,8 @@ class _AlbumSelectionActionWidgetState _logger.warning("failed to trash collection", e, s); await showGenericErrorDialog(context: context, error: e); } + } else if (collection.type == CollectionType.favorites) { + continue; } else { nonEmptyCollection.add(collection); } @@ -143,7 +145,10 @@ class _AlbumSelectionActionWidgetState Future _onPinClick() async { for (final collection in widget.selectedAlbums.albums) { - if (collection.isPinned) continue; + if (collection.type == CollectionType.favorites || collection.isPinned) { + continue; + } + await updateOrder( context, collection, @@ -155,6 +160,9 @@ class _AlbumSelectionActionWidgetState Future _onHideClick() async { for (final collection in widget.selectedAlbums.albums) { + if (collection.type == CollectionType.favorites) { + continue; + } final isHidden = collection.isHidden(); final int prevVisiblity = isHidden ? hiddenVisibility : visibleVisibility; final int newVisiblity = isHidden ? visibleVisibility : hiddenVisibility;