mirror of
				https://github.com/immich-app/immich.git
				synced 2025-11-03 19:17:11 -05:00 
			
		
		
		
	refactor: bottom sheet action button (#20964)
* fix: incorrect archive action shown in asset viewer' * Refactor * use enums syntax and add tests
This commit is contained in:
		
							parent
							
								
									cd6d8fcdfe
								
							
						
					
					
						commit
						ec01db5c8b
					
				@ -44,6 +44,7 @@ class LikeActivityActionButton extends ConsumerWidget {
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        return BaseActionButton(
 | 
			
		||||
          maxWidth: 60,
 | 
			
		||||
          iconData: liked != null ? Icons.favorite : Icons.favorite_border,
 | 
			
		||||
          label: "like".t(context: context),
 | 
			
		||||
          onPressed: () => onTap(liked),
 | 
			
		||||
 | 
			
		||||
@ -8,6 +8,7 @@ import 'package:immich_mobile/presentation/widgets/action_buttons/delete_action_
 | 
			
		||||
import 'package:immich_mobile/presentation/widgets/action_buttons/delete_local_action_button.widget.dart';
 | 
			
		||||
import 'package:immich_mobile/presentation/widgets/action_buttons/edit_image_action_button.widget.dart';
 | 
			
		||||
import 'package:immich_mobile/presentation/widgets/action_buttons/share_action_button.widget.dart';
 | 
			
		||||
import 'package:immich_mobile/presentation/widgets/action_buttons/unarchive_action_button.widget.dart';
 | 
			
		||||
import 'package:immich_mobile/presentation/widgets/action_buttons/upload_action_button.widget.dart';
 | 
			
		||||
import 'package:immich_mobile/presentation/widgets/asset_viewer/asset_viewer.state.dart';
 | 
			
		||||
import 'package:immich_mobile/providers/infrastructure/asset_viewer/current_asset.provider.dart';
 | 
			
		||||
@ -31,6 +32,7 @@ class ViewerBottomBar extends ConsumerWidget {
 | 
			
		||||
    int opacity = ref.watch(assetViewerProvider.select((state) => state.backgroundOpacity));
 | 
			
		||||
    final showControls = ref.watch(assetViewerProvider.select((s) => s.showingControls));
 | 
			
		||||
    final isInLockedView = ref.watch(inLockedViewProvider);
 | 
			
		||||
    final isArchived = asset is RemoteAsset && asset.visibility == AssetVisibility.archive;
 | 
			
		||||
 | 
			
		||||
    if (!showControls) {
 | 
			
		||||
      opacity = 0;
 | 
			
		||||
@ -40,10 +42,15 @@ class ViewerBottomBar extends ConsumerWidget {
 | 
			
		||||
      const ShareActionButton(source: ActionSource.viewer),
 | 
			
		||||
      if (asset.isLocalOnly) const UploadActionButton(source: ActionSource.viewer),
 | 
			
		||||
      if (asset.type == AssetType.image) const EditImageActionButton(),
 | 
			
		||||
      if (asset.hasRemote && isOwner) const ArchiveActionButton(source: ActionSource.viewer),
 | 
			
		||||
      if (isOwner) ...[
 | 
			
		||||
        if (asset.hasRemote && isOwner && isArchived)
 | 
			
		||||
          const UnArchiveActionButton(source: ActionSource.viewer)
 | 
			
		||||
        else
 | 
			
		||||
          const ArchiveActionButton(source: ActionSource.viewer),
 | 
			
		||||
        asset.isLocalOnly
 | 
			
		||||
            ? const DeleteLocalActionButton(source: ActionSource.viewer)
 | 
			
		||||
            : const DeleteActionButton(source: ActionSource.viewer, showConfirmation: true),
 | 
			
		||||
      ],
 | 
			
		||||
    ];
 | 
			
		||||
 | 
			
		||||
    return IgnorePointer(
 | 
			
		||||
 | 
			
		||||
@ -6,18 +6,6 @@ import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
 | 
			
		||||
import 'package:immich_mobile/domain/models/exif.model.dart';
 | 
			
		||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
 | 
			
		||||
import 'package:immich_mobile/extensions/translate_extensions.dart';
 | 
			
		||||
import 'package:immich_mobile/presentation/widgets/action_buttons/archive_action_button.widget.dart';
 | 
			
		||||
import 'package:immich_mobile/presentation/widgets/action_buttons/delete_action_button.widget.dart';
 | 
			
		||||
import 'package:immich_mobile/presentation/widgets/action_buttons/delete_local_action_button.widget.dart';
 | 
			
		||||
import 'package:immich_mobile/presentation/widgets/action_buttons/delete_permanent_action_button.widget.dart';
 | 
			
		||||
import 'package:immich_mobile/presentation/widgets/action_buttons/download_action_button.widget.dart';
 | 
			
		||||
import 'package:immich_mobile/presentation/widgets/action_buttons/like_activity_action_button.widget.dart';
 | 
			
		||||
import 'package:immich_mobile/presentation/widgets/action_buttons/move_to_lock_folder_action_button.widget.dart';
 | 
			
		||||
import 'package:immich_mobile/presentation/widgets/action_buttons/remove_from_album_action_button.widget.dart';
 | 
			
		||||
import 'package:immich_mobile/presentation/widgets/action_buttons/share_action_button.widget.dart';
 | 
			
		||||
import 'package:immich_mobile/presentation/widgets/action_buttons/share_link_action_button.widget.dart';
 | 
			
		||||
import 'package:immich_mobile/presentation/widgets/action_buttons/trash_action_button.widget.dart';
 | 
			
		||||
import 'package:immich_mobile/presentation/widgets/action_buttons/upload_action_button.widget.dart';
 | 
			
		||||
import 'package:immich_mobile/presentation/widgets/asset_viewer/bottom_sheet/sheet_location_details.widget.dart';
 | 
			
		||||
import 'package:immich_mobile/presentation/widgets/asset_viewer/bottom_sheet/sheet_people_details.widget.dart';
 | 
			
		||||
import 'package:immich_mobile/presentation/widgets/bottom_sheet/base_bottom_sheet.widget.dart';
 | 
			
		||||
@ -26,6 +14,8 @@ import 'package:immich_mobile/providers/infrastructure/asset_viewer/current_asse
 | 
			
		||||
import 'package:immich_mobile/providers/infrastructure/current_album.provider.dart';
 | 
			
		||||
import 'package:immich_mobile/providers/routes.provider.dart';
 | 
			
		||||
import 'package:immich_mobile/providers/server_info.provider.dart';
 | 
			
		||||
import 'package:immich_mobile/providers/user.provider.dart';
 | 
			
		||||
import 'package:immich_mobile/utils/action_button.utils.dart';
 | 
			
		||||
import 'package:immich_mobile/utils/bytes_units.dart';
 | 
			
		||||
import 'package:immich_mobile/widgets/common/immich_toast.dart';
 | 
			
		||||
 | 
			
		||||
@ -45,34 +35,25 @@ class AssetDetailBottomSheet extends ConsumerWidget {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    final isTrashEnable = ref.watch(serverInfoProvider.select((state) => state.serverFeatures.trash));
 | 
			
		||||
    final isOwner = asset is RemoteAsset && asset.ownerId == ref.watch(currentUserProvider)?.id;
 | 
			
		||||
    final isInLockedView = ref.watch(inLockedViewProvider);
 | 
			
		||||
    final currentAlbum = ref.watch(currentRemoteAlbumProvider);
 | 
			
		||||
    final isArchived = asset is RemoteAsset && asset.visibility == AssetVisibility.archive;
 | 
			
		||||
 | 
			
		||||
    final actions = <Widget>[
 | 
			
		||||
      const ShareActionButton(source: ActionSource.viewer),
 | 
			
		||||
      if (currentAlbum != null && currentAlbum.isActivityEnabled && currentAlbum.isShared)
 | 
			
		||||
        const LikeActivityActionButton(),
 | 
			
		||||
      if (asset.hasRemote) ...[
 | 
			
		||||
        const ShareLinkActionButton(source: ActionSource.viewer),
 | 
			
		||||
        const ArchiveActionButton(source: ActionSource.viewer),
 | 
			
		||||
        if (!asset.hasLocal) const DownloadActionButton(source: ActionSource.viewer),
 | 
			
		||||
        isTrashEnable
 | 
			
		||||
            ? const TrashActionButton(source: ActionSource.viewer)
 | 
			
		||||
            : const DeletePermanentActionButton(source: ActionSource.viewer),
 | 
			
		||||
        const DeleteActionButton(source: ActionSource.viewer),
 | 
			
		||||
        const MoveToLockFolderActionButton(source: ActionSource.viewer),
 | 
			
		||||
      ],
 | 
			
		||||
      if (asset.storage == AssetState.local) ...[
 | 
			
		||||
        const DeleteLocalActionButton(source: ActionSource.viewer),
 | 
			
		||||
        const UploadActionButton(source: ActionSource.timeline),
 | 
			
		||||
      ],
 | 
			
		||||
      if (currentAlbum != null) RemoveFromAlbumActionButton(albumId: currentAlbum.id, source: ActionSource.viewer),
 | 
			
		||||
    ];
 | 
			
		||||
    final buttonContext = ActionButtonContext(
 | 
			
		||||
      asset: asset,
 | 
			
		||||
      isOwner: isOwner,
 | 
			
		||||
      isArchived: isArchived,
 | 
			
		||||
      isTrashEnabled: isTrashEnable,
 | 
			
		||||
      isInLockedView: isInLockedView,
 | 
			
		||||
      currentAlbum: currentAlbum,
 | 
			
		||||
      source: ActionSource.viewer,
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    final lockedViewActions = <Widget>[];
 | 
			
		||||
    final actions = ActionButtonBuilder.build(buttonContext);
 | 
			
		||||
 | 
			
		||||
    return BaseBottomSheet(
 | 
			
		||||
      actions: isInLockedView ? lockedViewActions : actions,
 | 
			
		||||
      actions: actions,
 | 
			
		||||
      slivers: const [_AssetDetailBottomSheet()],
 | 
			
		||||
      controller: controller,
 | 
			
		||||
      initialChildSize: initialChildSize,
 | 
			
		||||
 | 
			
		||||
@ -69,10 +69,8 @@ class _BaseDraggableScrollableSheetState extends ConsumerState<BaseBottomSheet>
 | 
			
		||||
      shouldCloseOnMinExtent: widget.shouldCloseOnMinExtent,
 | 
			
		||||
      builder: (BuildContext context, ScrollController scrollController) {
 | 
			
		||||
        return Card(
 | 
			
		||||
          color: widget.backgroundColor ?? context.colorScheme.surface,
 | 
			
		||||
          borderOnForeground: false,
 | 
			
		||||
          clipBehavior: Clip.antiAlias,
 | 
			
		||||
          elevation: 6.0,
 | 
			
		||||
          color: widget.backgroundColor ?? context.colorScheme.surfaceContainer,
 | 
			
		||||
          elevation: 3.0,
 | 
			
		||||
          shape: const RoundedRectangleBorder(borderRadius: BorderRadius.vertical(top: Radius.circular(18))),
 | 
			
		||||
          margin: const EdgeInsets.symmetric(horizontal: 0),
 | 
			
		||||
          child: CustomScrollView(
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										160
									
								
								mobile/lib/utils/action_button.utils.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										160
									
								
								mobile/lib/utils/action_button.utils.dart
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,160 @@
 | 
			
		||||
import 'package:flutter/widgets.dart';
 | 
			
		||||
import 'package:immich_mobile/constants/enums.dart';
 | 
			
		||||
import 'package:immich_mobile/domain/models/album/album.model.dart';
 | 
			
		||||
import 'package:immich_mobile/presentation/widgets/action_buttons/archive_action_button.widget.dart';
 | 
			
		||||
import 'package:immich_mobile/presentation/widgets/action_buttons/delete_action_button.widget.dart';
 | 
			
		||||
import 'package:immich_mobile/presentation/widgets/action_buttons/delete_local_action_button.widget.dart';
 | 
			
		||||
import 'package:immich_mobile/presentation/widgets/action_buttons/delete_permanent_action_button.widget.dart';
 | 
			
		||||
import 'package:immich_mobile/presentation/widgets/action_buttons/download_action_button.widget.dart';
 | 
			
		||||
import 'package:immich_mobile/presentation/widgets/action_buttons/like_activity_action_button.widget.dart';
 | 
			
		||||
import 'package:immich_mobile/presentation/widgets/action_buttons/move_to_lock_folder_action_button.widget.dart';
 | 
			
		||||
import 'package:immich_mobile/presentation/widgets/action_buttons/remove_from_album_action_button.widget.dart';
 | 
			
		||||
import 'package:immich_mobile/presentation/widgets/action_buttons/remove_from_lock_folder_action_button.widget.dart';
 | 
			
		||||
import 'package:immich_mobile/presentation/widgets/action_buttons/share_action_button.widget.dart';
 | 
			
		||||
import 'package:immich_mobile/presentation/widgets/action_buttons/share_link_action_button.widget.dart';
 | 
			
		||||
import 'package:immich_mobile/presentation/widgets/action_buttons/trash_action_button.widget.dart';
 | 
			
		||||
import 'package:immich_mobile/presentation/widgets/action_buttons/unarchive_action_button.widget.dart';
 | 
			
		||||
import 'package:immich_mobile/presentation/widgets/action_buttons/upload_action_button.widget.dart';
 | 
			
		||||
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
 | 
			
		||||
 | 
			
		||||
class ActionButtonContext {
 | 
			
		||||
  final BaseAsset asset;
 | 
			
		||||
  final bool isOwner;
 | 
			
		||||
  final bool isArchived;
 | 
			
		||||
  final bool isTrashEnabled;
 | 
			
		||||
  final bool isInLockedView;
 | 
			
		||||
  final RemoteAlbum? currentAlbum;
 | 
			
		||||
  final ActionSource source;
 | 
			
		||||
 | 
			
		||||
  const ActionButtonContext({
 | 
			
		||||
    required this.asset,
 | 
			
		||||
    required this.isOwner,
 | 
			
		||||
    required this.isArchived,
 | 
			
		||||
    required this.isTrashEnabled,
 | 
			
		||||
    required this.isInLockedView,
 | 
			
		||||
    required this.currentAlbum,
 | 
			
		||||
    required this.source,
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
enum ActionButtonType {
 | 
			
		||||
  share,
 | 
			
		||||
  shareLink,
 | 
			
		||||
  archive,
 | 
			
		||||
  unarchive,
 | 
			
		||||
  download,
 | 
			
		||||
  trash,
 | 
			
		||||
  deletePermanent,
 | 
			
		||||
  delete,
 | 
			
		||||
  moveToLockFolder,
 | 
			
		||||
  removeFromLockFolder,
 | 
			
		||||
  deleteLocal,
 | 
			
		||||
  upload,
 | 
			
		||||
  removeFromAlbum,
 | 
			
		||||
  likeActivity;
 | 
			
		||||
 | 
			
		||||
  bool shouldShow(ActionButtonContext context) {
 | 
			
		||||
    return switch (this) {
 | 
			
		||||
      ActionButtonType.share => true,
 | 
			
		||||
      ActionButtonType.shareLink =>
 | 
			
		||||
        !context.isInLockedView && //
 | 
			
		||||
            context.asset.hasRemote,
 | 
			
		||||
      ActionButtonType.archive =>
 | 
			
		||||
        context.isOwner && //
 | 
			
		||||
            !context.isInLockedView && //
 | 
			
		||||
            context.asset.hasRemote && //
 | 
			
		||||
            !context.isArchived,
 | 
			
		||||
      ActionButtonType.unarchive =>
 | 
			
		||||
        context.isOwner && //
 | 
			
		||||
            !context.isInLockedView && //
 | 
			
		||||
            context.asset.hasRemote && //
 | 
			
		||||
            context.isArchived,
 | 
			
		||||
      ActionButtonType.download =>
 | 
			
		||||
        !context.isInLockedView && //
 | 
			
		||||
            context.asset.hasRemote && //
 | 
			
		||||
            !context.asset.hasLocal,
 | 
			
		||||
      ActionButtonType.trash =>
 | 
			
		||||
        context.isOwner && //
 | 
			
		||||
            !context.isInLockedView && //
 | 
			
		||||
            context.asset.hasRemote && //
 | 
			
		||||
            context.isTrashEnabled,
 | 
			
		||||
      ActionButtonType.deletePermanent =>
 | 
			
		||||
        context.isOwner && //
 | 
			
		||||
                context.asset.hasRemote && //
 | 
			
		||||
                !context.isTrashEnabled ||
 | 
			
		||||
            context.isInLockedView,
 | 
			
		||||
      ActionButtonType.delete =>
 | 
			
		||||
        context.isOwner && //
 | 
			
		||||
            !context.isInLockedView && //
 | 
			
		||||
            context.asset.hasRemote,
 | 
			
		||||
      ActionButtonType.moveToLockFolder =>
 | 
			
		||||
        context.isOwner && //
 | 
			
		||||
            !context.isInLockedView && //
 | 
			
		||||
            context.asset.hasRemote,
 | 
			
		||||
      ActionButtonType.removeFromLockFolder =>
 | 
			
		||||
        context.isOwner && //
 | 
			
		||||
            context.isInLockedView && //
 | 
			
		||||
            context.asset.hasRemote,
 | 
			
		||||
      ActionButtonType.deleteLocal =>
 | 
			
		||||
        !context.isInLockedView && //
 | 
			
		||||
            context.asset.storage == AssetState.local,
 | 
			
		||||
      ActionButtonType.upload =>
 | 
			
		||||
        !context.isInLockedView && //
 | 
			
		||||
            context.asset.storage == AssetState.local,
 | 
			
		||||
      ActionButtonType.removeFromAlbum =>
 | 
			
		||||
        context.isOwner && //
 | 
			
		||||
            !context.isInLockedView && //
 | 
			
		||||
            context.currentAlbum != null,
 | 
			
		||||
      ActionButtonType.likeActivity =>
 | 
			
		||||
        !context.isInLockedView &&
 | 
			
		||||
            context.currentAlbum != null &&
 | 
			
		||||
            context.currentAlbum!.isActivityEnabled &&
 | 
			
		||||
            context.currentAlbum!.isShared,
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  Widget buildButton(ActionButtonContext context) {
 | 
			
		||||
    return switch (this) {
 | 
			
		||||
      ActionButtonType.share => ShareActionButton(source: context.source),
 | 
			
		||||
      ActionButtonType.shareLink => ShareLinkActionButton(source: context.source),
 | 
			
		||||
      ActionButtonType.archive => ArchiveActionButton(source: context.source),
 | 
			
		||||
      ActionButtonType.unarchive => UnArchiveActionButton(source: context.source),
 | 
			
		||||
      ActionButtonType.download => DownloadActionButton(source: context.source),
 | 
			
		||||
      ActionButtonType.trash => TrashActionButton(source: context.source),
 | 
			
		||||
      ActionButtonType.deletePermanent => DeletePermanentActionButton(source: context.source),
 | 
			
		||||
      ActionButtonType.delete => DeleteActionButton(source: context.source),
 | 
			
		||||
      ActionButtonType.moveToLockFolder => MoveToLockFolderActionButton(source: context.source),
 | 
			
		||||
      ActionButtonType.removeFromLockFolder => RemoveFromLockFolderActionButton(source: context.source),
 | 
			
		||||
      ActionButtonType.deleteLocal => DeleteLocalActionButton(source: context.source),
 | 
			
		||||
      ActionButtonType.upload => UploadActionButton(source: context.source),
 | 
			
		||||
      ActionButtonType.removeFromAlbum => RemoveFromAlbumActionButton(
 | 
			
		||||
        albumId: context.currentAlbum!.id,
 | 
			
		||||
        source: context.source,
 | 
			
		||||
      ),
 | 
			
		||||
      ActionButtonType.likeActivity => const LikeActivityActionButton(),
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class ActionButtonBuilder {
 | 
			
		||||
  static const List<ActionButtonType> _actionTypes = [
 | 
			
		||||
    ActionButtonType.share,
 | 
			
		||||
    ActionButtonType.shareLink,
 | 
			
		||||
    ActionButtonType.likeActivity,
 | 
			
		||||
    ActionButtonType.archive,
 | 
			
		||||
    ActionButtonType.unarchive,
 | 
			
		||||
    ActionButtonType.download,
 | 
			
		||||
    ActionButtonType.trash,
 | 
			
		||||
    ActionButtonType.deletePermanent,
 | 
			
		||||
    ActionButtonType.delete,
 | 
			
		||||
    ActionButtonType.moveToLockFolder,
 | 
			
		||||
    ActionButtonType.removeFromLockFolder,
 | 
			
		||||
    ActionButtonType.deleteLocal,
 | 
			
		||||
    ActionButtonType.upload,
 | 
			
		||||
    ActionButtonType.removeFromAlbum,
 | 
			
		||||
  ];
 | 
			
		||||
 | 
			
		||||
  static List<Widget> build(ActionButtonContext context) {
 | 
			
		||||
    return _actionTypes.where((type) => type.shouldShow(context)).map((type) => type.buildButton(context)).toList();
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										717
									
								
								mobile/test/utils/action_button_utils_test.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										717
									
								
								mobile/test/utils/action_button_utils_test.dart
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,717 @@
 | 
			
		||||
import 'package:flutter/widgets.dart';
 | 
			
		||||
import 'package:flutter_test/flutter_test.dart';
 | 
			
		||||
import 'package:immich_mobile/constants/enums.dart';
 | 
			
		||||
import 'package:immich_mobile/domain/models/album/album.model.dart';
 | 
			
		||||
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
 | 
			
		||||
import 'package:immich_mobile/utils/action_button.utils.dart';
 | 
			
		||||
 | 
			
		||||
LocalAsset createLocalAsset({
 | 
			
		||||
  String? remoteId,
 | 
			
		||||
  String name = 'test.jpg',
 | 
			
		||||
  String? checksum = 'test-checksum',
 | 
			
		||||
  AssetType type = AssetType.image,
 | 
			
		||||
  DateTime? createdAt,
 | 
			
		||||
  DateTime? updatedAt,
 | 
			
		||||
  bool isFavorite = false,
 | 
			
		||||
}) {
 | 
			
		||||
  return LocalAsset(
 | 
			
		||||
    id: 'local-id',
 | 
			
		||||
    remoteId: remoteId,
 | 
			
		||||
    name: name,
 | 
			
		||||
    checksum: checksum,
 | 
			
		||||
    type: type,
 | 
			
		||||
    createdAt: createdAt ?? DateTime.now(),
 | 
			
		||||
    updatedAt: updatedAt ?? DateTime.now(),
 | 
			
		||||
    isFavorite: isFavorite,
 | 
			
		||||
  );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
RemoteAsset createRemoteAsset({
 | 
			
		||||
  String? localId,
 | 
			
		||||
  String name = 'test.jpg',
 | 
			
		||||
  String checksum = 'test-checksum',
 | 
			
		||||
  AssetType type = AssetType.image,
 | 
			
		||||
  DateTime? createdAt,
 | 
			
		||||
  DateTime? updatedAt,
 | 
			
		||||
  bool isFavorite = false,
 | 
			
		||||
}) {
 | 
			
		||||
  return RemoteAsset(
 | 
			
		||||
    id: 'remote-id',
 | 
			
		||||
    localId: localId,
 | 
			
		||||
    name: name,
 | 
			
		||||
    checksum: checksum,
 | 
			
		||||
    type: type,
 | 
			
		||||
    ownerId: 'owner-id',
 | 
			
		||||
    createdAt: createdAt ?? DateTime.now(),
 | 
			
		||||
    updatedAt: updatedAt ?? DateTime.now(),
 | 
			
		||||
    isFavorite: isFavorite,
 | 
			
		||||
  );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
RemoteAlbum createRemoteAlbum({
 | 
			
		||||
  String id = 'test-album-id',
 | 
			
		||||
  String name = 'Test Album',
 | 
			
		||||
  bool isActivityEnabled = false,
 | 
			
		||||
  bool isShared = false,
 | 
			
		||||
}) {
 | 
			
		||||
  return RemoteAlbum(
 | 
			
		||||
    id: id,
 | 
			
		||||
    name: name,
 | 
			
		||||
    ownerId: 'owner-id',
 | 
			
		||||
    description: 'Test Description',
 | 
			
		||||
    createdAt: DateTime.now(),
 | 
			
		||||
    updatedAt: DateTime.now(),
 | 
			
		||||
    isActivityEnabled: isActivityEnabled,
 | 
			
		||||
    isShared: isShared,
 | 
			
		||||
    order: AlbumAssetOrder.asc,
 | 
			
		||||
    assetCount: 0,
 | 
			
		||||
    ownerName: 'Test Owner',
 | 
			
		||||
  );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void main() {
 | 
			
		||||
  group('ActionButtonContext', () {
 | 
			
		||||
    test('should create context with all required parameters', () {
 | 
			
		||||
      final asset = createLocalAsset();
 | 
			
		||||
 | 
			
		||||
      final context = ActionButtonContext(
 | 
			
		||||
        asset: asset,
 | 
			
		||||
        isOwner: true,
 | 
			
		||||
        isArchived: false,
 | 
			
		||||
        isTrashEnabled: true,
 | 
			
		||||
        isInLockedView: false,
 | 
			
		||||
        currentAlbum: null,
 | 
			
		||||
        source: ActionSource.timeline,
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      expect(context.asset, isA<BaseAsset>());
 | 
			
		||||
      expect(context.isOwner, isTrue);
 | 
			
		||||
      expect(context.isArchived, isFalse);
 | 
			
		||||
      expect(context.isTrashEnabled, isTrue);
 | 
			
		||||
      expect(context.isInLockedView, isFalse);
 | 
			
		||||
      expect(context.currentAlbum, isNull);
 | 
			
		||||
      expect(context.source, ActionSource.timeline);
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  group('ActionButtonType.shouldShow', () {
 | 
			
		||||
    late BaseAsset mergedAsset;
 | 
			
		||||
 | 
			
		||||
    setUp(() {
 | 
			
		||||
      mergedAsset = createLocalAsset(remoteId: 'remote-id');
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    group('share button', () {
 | 
			
		||||
      test('should show when not in locked view', () {
 | 
			
		||||
        final context = ActionButtonContext(
 | 
			
		||||
          asset: mergedAsset,
 | 
			
		||||
          isOwner: true,
 | 
			
		||||
          isArchived: false,
 | 
			
		||||
          isTrashEnabled: true,
 | 
			
		||||
          isInLockedView: false,
 | 
			
		||||
          currentAlbum: null,
 | 
			
		||||
          source: ActionSource.timeline,
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        expect(ActionButtonType.share.shouldShow(context), isTrue);
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      test('should show when in locked view', () {
 | 
			
		||||
        final context = ActionButtonContext(
 | 
			
		||||
          asset: mergedAsset,
 | 
			
		||||
          isOwner: true,
 | 
			
		||||
          isArchived: false,
 | 
			
		||||
          isTrashEnabled: true,
 | 
			
		||||
          isInLockedView: true,
 | 
			
		||||
          currentAlbum: null,
 | 
			
		||||
          source: ActionSource.timeline,
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        expect(ActionButtonType.share.shouldShow(context), isTrue);
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    group('shareLink button', () {
 | 
			
		||||
      test('should show when not in locked view and asset has remote', () {
 | 
			
		||||
        final remoteAsset = createRemoteAsset();
 | 
			
		||||
        final context = ActionButtonContext(
 | 
			
		||||
          asset: remoteAsset,
 | 
			
		||||
          isOwner: true,
 | 
			
		||||
          isArchived: false,
 | 
			
		||||
          isTrashEnabled: true,
 | 
			
		||||
          isInLockedView: false,
 | 
			
		||||
          currentAlbum: null,
 | 
			
		||||
          source: ActionSource.timeline,
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        expect(ActionButtonType.shareLink.shouldShow(context), isTrue);
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      test('should not show when in locked view', () {
 | 
			
		||||
        final remoteAsset = createRemoteAsset();
 | 
			
		||||
        final context = ActionButtonContext(
 | 
			
		||||
          asset: remoteAsset,
 | 
			
		||||
          isOwner: true,
 | 
			
		||||
          isArchived: false,
 | 
			
		||||
          isTrashEnabled: true,
 | 
			
		||||
          isInLockedView: true,
 | 
			
		||||
          currentAlbum: null,
 | 
			
		||||
          source: ActionSource.timeline,
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        expect(ActionButtonType.shareLink.shouldShow(context), isFalse);
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      test('should not show when asset has no remote', () {
 | 
			
		||||
        final localAsset = createLocalAsset();
 | 
			
		||||
        final context = ActionButtonContext(
 | 
			
		||||
          asset: localAsset,
 | 
			
		||||
          isOwner: true,
 | 
			
		||||
          isArchived: false,
 | 
			
		||||
          isTrashEnabled: true,
 | 
			
		||||
          isInLockedView: false,
 | 
			
		||||
          currentAlbum: null,
 | 
			
		||||
          source: ActionSource.timeline,
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        expect(ActionButtonType.shareLink.shouldShow(context), isFalse);
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    group('archive button', () {
 | 
			
		||||
      test('should show when owner, not locked, has remote, and not archived', () {
 | 
			
		||||
        final remoteAsset = createRemoteAsset();
 | 
			
		||||
        final context = ActionButtonContext(
 | 
			
		||||
          asset: remoteAsset,
 | 
			
		||||
          isOwner: true,
 | 
			
		||||
          isArchived: false,
 | 
			
		||||
          isTrashEnabled: true,
 | 
			
		||||
          isInLockedView: false,
 | 
			
		||||
          currentAlbum: null,
 | 
			
		||||
          source: ActionSource.timeline,
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        expect(ActionButtonType.archive.shouldShow(context), isTrue);
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      test('should not show when not owner', () {
 | 
			
		||||
        final remoteAsset = createRemoteAsset();
 | 
			
		||||
        final context = ActionButtonContext(
 | 
			
		||||
          asset: remoteAsset,
 | 
			
		||||
          isOwner: false,
 | 
			
		||||
          isArchived: false,
 | 
			
		||||
          isTrashEnabled: true,
 | 
			
		||||
          isInLockedView: false,
 | 
			
		||||
          currentAlbum: null,
 | 
			
		||||
          source: ActionSource.timeline,
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        expect(ActionButtonType.archive.shouldShow(context), isFalse);
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      test('should not show when in locked view', () {
 | 
			
		||||
        final remoteAsset = createRemoteAsset();
 | 
			
		||||
        final context = ActionButtonContext(
 | 
			
		||||
          asset: remoteAsset,
 | 
			
		||||
          isOwner: true,
 | 
			
		||||
          isArchived: false,
 | 
			
		||||
          isTrashEnabled: true,
 | 
			
		||||
          isInLockedView: true,
 | 
			
		||||
          currentAlbum: null,
 | 
			
		||||
          source: ActionSource.timeline,
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        expect(ActionButtonType.archive.shouldShow(context), isFalse);
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      test('should not show when asset has no remote', () {
 | 
			
		||||
        final localAsset = createLocalAsset();
 | 
			
		||||
        final context = ActionButtonContext(
 | 
			
		||||
          asset: localAsset,
 | 
			
		||||
          isOwner: true,
 | 
			
		||||
          isArchived: false,
 | 
			
		||||
          isTrashEnabled: true,
 | 
			
		||||
          isInLockedView: false,
 | 
			
		||||
          currentAlbum: null,
 | 
			
		||||
          source: ActionSource.timeline,
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        expect(ActionButtonType.archive.shouldShow(context), isFalse);
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      test('should not show when already archived', () {
 | 
			
		||||
        final remoteAsset = createRemoteAsset();
 | 
			
		||||
        final context = ActionButtonContext(
 | 
			
		||||
          asset: remoteAsset,
 | 
			
		||||
          isOwner: true,
 | 
			
		||||
          isArchived: true,
 | 
			
		||||
          isTrashEnabled: true,
 | 
			
		||||
          isInLockedView: false,
 | 
			
		||||
          currentAlbum: null,
 | 
			
		||||
          source: ActionSource.timeline,
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        expect(ActionButtonType.archive.shouldShow(context), isFalse);
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    group('unarchive button', () {
 | 
			
		||||
      test('should show when owner, not locked, has remote, and is archived', () {
 | 
			
		||||
        final remoteAsset = createRemoteAsset();
 | 
			
		||||
        final context = ActionButtonContext(
 | 
			
		||||
          asset: remoteAsset,
 | 
			
		||||
          isOwner: true,
 | 
			
		||||
          isArchived: true,
 | 
			
		||||
          isTrashEnabled: true,
 | 
			
		||||
          isInLockedView: false,
 | 
			
		||||
          currentAlbum: null,
 | 
			
		||||
          source: ActionSource.timeline,
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        expect(ActionButtonType.unarchive.shouldShow(context), isTrue);
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      test('should not show when not archived', () {
 | 
			
		||||
        final remoteAsset = createRemoteAsset();
 | 
			
		||||
        final context = ActionButtonContext(
 | 
			
		||||
          asset: remoteAsset,
 | 
			
		||||
          isOwner: true,
 | 
			
		||||
          isArchived: false,
 | 
			
		||||
          isTrashEnabled: true,
 | 
			
		||||
          isInLockedView: false,
 | 
			
		||||
          currentAlbum: null,
 | 
			
		||||
          source: ActionSource.timeline,
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        expect(ActionButtonType.unarchive.shouldShow(context), isFalse);
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      test('should not show when not owner', () {
 | 
			
		||||
        final remoteAsset = createRemoteAsset();
 | 
			
		||||
        final context = ActionButtonContext(
 | 
			
		||||
          asset: remoteAsset,
 | 
			
		||||
          isOwner: false,
 | 
			
		||||
          isArchived: true,
 | 
			
		||||
          isTrashEnabled: true,
 | 
			
		||||
          isInLockedView: false,
 | 
			
		||||
          currentAlbum: null,
 | 
			
		||||
          source: ActionSource.timeline,
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        expect(ActionButtonType.unarchive.shouldShow(context), isFalse);
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    group('download button', () {
 | 
			
		||||
      test('should show when not locked, has remote, and no local copy', () {
 | 
			
		||||
        final remoteAsset = createRemoteAsset();
 | 
			
		||||
        final context = ActionButtonContext(
 | 
			
		||||
          asset: remoteAsset,
 | 
			
		||||
          isOwner: true,
 | 
			
		||||
          isArchived: false,
 | 
			
		||||
          isTrashEnabled: true,
 | 
			
		||||
          isInLockedView: false,
 | 
			
		||||
          currentAlbum: null,
 | 
			
		||||
          source: ActionSource.timeline,
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        expect(ActionButtonType.download.shouldShow(context), isTrue);
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      test('should not show when has local copy', () {
 | 
			
		||||
        final mergedAsset = createLocalAsset(remoteId: 'remote-id');
 | 
			
		||||
        final context = ActionButtonContext(
 | 
			
		||||
          asset: mergedAsset,
 | 
			
		||||
          isOwner: true,
 | 
			
		||||
          isArchived: false,
 | 
			
		||||
          isTrashEnabled: true,
 | 
			
		||||
          isInLockedView: false,
 | 
			
		||||
          currentAlbum: null,
 | 
			
		||||
          source: ActionSource.timeline,
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        expect(ActionButtonType.download.shouldShow(context), isFalse);
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      test('should not show when in locked view', () {
 | 
			
		||||
        final remoteAsset = createRemoteAsset();
 | 
			
		||||
        final context = ActionButtonContext(
 | 
			
		||||
          asset: remoteAsset,
 | 
			
		||||
          isOwner: true,
 | 
			
		||||
          isArchived: false,
 | 
			
		||||
          isTrashEnabled: true,
 | 
			
		||||
          isInLockedView: true,
 | 
			
		||||
          currentAlbum: null,
 | 
			
		||||
          source: ActionSource.timeline,
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        expect(ActionButtonType.download.shouldShow(context), isFalse);
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    group('trash button', () {
 | 
			
		||||
      test('should show when owner, not locked, has remote, and trash enabled', () {
 | 
			
		||||
        final remoteAsset = createRemoteAsset();
 | 
			
		||||
        final context = ActionButtonContext(
 | 
			
		||||
          asset: remoteAsset,
 | 
			
		||||
          isOwner: true,
 | 
			
		||||
          isArchived: false,
 | 
			
		||||
          isTrashEnabled: true,
 | 
			
		||||
          isInLockedView: false,
 | 
			
		||||
          currentAlbum: null,
 | 
			
		||||
          source: ActionSource.timeline,
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        expect(ActionButtonType.trash.shouldShow(context), isTrue);
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      test('should not show when trash disabled', () {
 | 
			
		||||
        final remoteAsset = createRemoteAsset();
 | 
			
		||||
        final context = ActionButtonContext(
 | 
			
		||||
          asset: remoteAsset,
 | 
			
		||||
          isOwner: true,
 | 
			
		||||
          isArchived: false,
 | 
			
		||||
          isTrashEnabled: false,
 | 
			
		||||
          isInLockedView: false,
 | 
			
		||||
          currentAlbum: null,
 | 
			
		||||
          source: ActionSource.timeline,
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        expect(ActionButtonType.trash.shouldShow(context), isFalse);
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    group('deletePermanent button', () {
 | 
			
		||||
      test('should show when owner, not locked, has remote, and trash disabled', () {
 | 
			
		||||
        final remoteAsset = createRemoteAsset();
 | 
			
		||||
        final context = ActionButtonContext(
 | 
			
		||||
          asset: remoteAsset,
 | 
			
		||||
          isOwner: true,
 | 
			
		||||
          isArchived: false,
 | 
			
		||||
          isTrashEnabled: false,
 | 
			
		||||
          isInLockedView: false,
 | 
			
		||||
          currentAlbum: null,
 | 
			
		||||
          source: ActionSource.timeline,
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        expect(ActionButtonType.deletePermanent.shouldShow(context), isTrue);
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      test('should not show when trash enabled', () {
 | 
			
		||||
        final remoteAsset = createRemoteAsset();
 | 
			
		||||
        final context = ActionButtonContext(
 | 
			
		||||
          asset: remoteAsset,
 | 
			
		||||
          isOwner: true,
 | 
			
		||||
          isArchived: false,
 | 
			
		||||
          isTrashEnabled: true,
 | 
			
		||||
          isInLockedView: false,
 | 
			
		||||
          currentAlbum: null,
 | 
			
		||||
          source: ActionSource.timeline,
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        expect(ActionButtonType.deletePermanent.shouldShow(context), isFalse);
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    group('delete button', () {
 | 
			
		||||
      test('should show when owner, not locked, and has remote', () {
 | 
			
		||||
        final remoteAsset = createRemoteAsset();
 | 
			
		||||
        final context = ActionButtonContext(
 | 
			
		||||
          asset: remoteAsset,
 | 
			
		||||
          isOwner: true,
 | 
			
		||||
          isArchived: false,
 | 
			
		||||
          isTrashEnabled: true,
 | 
			
		||||
          isInLockedView: false,
 | 
			
		||||
          currentAlbum: null,
 | 
			
		||||
          source: ActionSource.timeline,
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        expect(ActionButtonType.delete.shouldShow(context), isTrue);
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    group('moveToLockFolder button', () {
 | 
			
		||||
      test('should show when owner, not locked, and has remote', () {
 | 
			
		||||
        final remoteAsset = createRemoteAsset();
 | 
			
		||||
        final context = ActionButtonContext(
 | 
			
		||||
          asset: remoteAsset,
 | 
			
		||||
          isOwner: true,
 | 
			
		||||
          isArchived: false,
 | 
			
		||||
          isTrashEnabled: true,
 | 
			
		||||
          isInLockedView: false,
 | 
			
		||||
          currentAlbum: null,
 | 
			
		||||
          source: ActionSource.timeline,
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        expect(ActionButtonType.moveToLockFolder.shouldShow(context), isTrue);
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    group('deleteLocal button', () {
 | 
			
		||||
      test('should show when not locked and asset is local only', () {
 | 
			
		||||
        final localAsset = createLocalAsset();
 | 
			
		||||
        final context = ActionButtonContext(
 | 
			
		||||
          asset: localAsset,
 | 
			
		||||
          isOwner: true,
 | 
			
		||||
          isArchived: false,
 | 
			
		||||
          isTrashEnabled: true,
 | 
			
		||||
          isInLockedView: false,
 | 
			
		||||
          currentAlbum: null,
 | 
			
		||||
          source: ActionSource.timeline,
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        expect(ActionButtonType.deleteLocal.shouldShow(context), isTrue);
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      test('should not show when asset is not local only', () {
 | 
			
		||||
        final remoteAsset = createRemoteAsset();
 | 
			
		||||
        final context = ActionButtonContext(
 | 
			
		||||
          asset: remoteAsset,
 | 
			
		||||
          isOwner: true,
 | 
			
		||||
          isArchived: false,
 | 
			
		||||
          isTrashEnabled: true,
 | 
			
		||||
          isInLockedView: false,
 | 
			
		||||
          currentAlbum: null,
 | 
			
		||||
          source: ActionSource.timeline,
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        expect(ActionButtonType.deleteLocal.shouldShow(context), isFalse);
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    group('upload button', () {
 | 
			
		||||
      test('should show when not locked and asset is local only', () {
 | 
			
		||||
        final localAsset = createLocalAsset();
 | 
			
		||||
        final context = ActionButtonContext(
 | 
			
		||||
          asset: localAsset,
 | 
			
		||||
          isOwner: true,
 | 
			
		||||
          isArchived: false,
 | 
			
		||||
          isTrashEnabled: true,
 | 
			
		||||
          isInLockedView: false,
 | 
			
		||||
          currentAlbum: null,
 | 
			
		||||
          source: ActionSource.timeline,
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        expect(ActionButtonType.upload.shouldShow(context), isTrue);
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    group('removeFromAlbum button', () {
 | 
			
		||||
      test('should show when owner, not locked, and has current album', () {
 | 
			
		||||
        final album = createRemoteAlbum();
 | 
			
		||||
        final context = ActionButtonContext(
 | 
			
		||||
          asset: mergedAsset,
 | 
			
		||||
          isOwner: true,
 | 
			
		||||
          isArchived: false,
 | 
			
		||||
          isTrashEnabled: true,
 | 
			
		||||
          isInLockedView: false,
 | 
			
		||||
          currentAlbum: album,
 | 
			
		||||
          source: ActionSource.timeline,
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        expect(ActionButtonType.removeFromAlbum.shouldShow(context), isTrue);
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      test('should not show when no current album', () {
 | 
			
		||||
        final context = ActionButtonContext(
 | 
			
		||||
          asset: mergedAsset,
 | 
			
		||||
          isOwner: true,
 | 
			
		||||
          isArchived: false,
 | 
			
		||||
          isTrashEnabled: true,
 | 
			
		||||
          isInLockedView: false,
 | 
			
		||||
          currentAlbum: null,
 | 
			
		||||
          source: ActionSource.timeline,
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        expect(ActionButtonType.removeFromAlbum.shouldShow(context), isFalse);
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    group('likeActivity button', () {
 | 
			
		||||
      test('should show when not locked, has album, activity enabled, and shared', () {
 | 
			
		||||
        final album = createRemoteAlbum(isActivityEnabled: true, isShared: true);
 | 
			
		||||
        final context = ActionButtonContext(
 | 
			
		||||
          asset: mergedAsset,
 | 
			
		||||
          isOwner: true,
 | 
			
		||||
          isArchived: false,
 | 
			
		||||
          isTrashEnabled: true,
 | 
			
		||||
          isInLockedView: false,
 | 
			
		||||
          currentAlbum: album,
 | 
			
		||||
          source: ActionSource.timeline,
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        expect(ActionButtonType.likeActivity.shouldShow(context), isTrue);
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      test('should not show when activity not enabled', () {
 | 
			
		||||
        final album = createRemoteAlbum(isActivityEnabled: false, isShared: true);
 | 
			
		||||
        final context = ActionButtonContext(
 | 
			
		||||
          asset: mergedAsset,
 | 
			
		||||
          isOwner: true,
 | 
			
		||||
          isArchived: false,
 | 
			
		||||
          isTrashEnabled: true,
 | 
			
		||||
          isInLockedView: false,
 | 
			
		||||
          currentAlbum: album,
 | 
			
		||||
          source: ActionSource.timeline,
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        expect(ActionButtonType.likeActivity.shouldShow(context), isFalse);
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      test('should not show when album not shared', () {
 | 
			
		||||
        final album = createRemoteAlbum(isActivityEnabled: true, isShared: false);
 | 
			
		||||
        final context = ActionButtonContext(
 | 
			
		||||
          asset: mergedAsset,
 | 
			
		||||
          isOwner: true,
 | 
			
		||||
          isArchived: false,
 | 
			
		||||
          isTrashEnabled: true,
 | 
			
		||||
          isInLockedView: false,
 | 
			
		||||
          currentAlbum: album,
 | 
			
		||||
          source: ActionSource.timeline,
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        expect(ActionButtonType.likeActivity.shouldShow(context), isFalse);
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      test('should not show when no album', () {
 | 
			
		||||
        final context = ActionButtonContext(
 | 
			
		||||
          asset: mergedAsset,
 | 
			
		||||
          isOwner: true,
 | 
			
		||||
          isArchived: false,
 | 
			
		||||
          isTrashEnabled: true,
 | 
			
		||||
          isInLockedView: false,
 | 
			
		||||
          currentAlbum: null,
 | 
			
		||||
          source: ActionSource.timeline,
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        expect(ActionButtonType.likeActivity.shouldShow(context), isFalse);
 | 
			
		||||
      });
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  group('ActionButtonType.buildButton', () {
 | 
			
		||||
    late BaseAsset asset;
 | 
			
		||||
    late ActionButtonContext context;
 | 
			
		||||
 | 
			
		||||
    setUp(() {
 | 
			
		||||
      asset = createLocalAsset(remoteId: 'remote-id');
 | 
			
		||||
      context = ActionButtonContext(
 | 
			
		||||
        asset: asset,
 | 
			
		||||
        isOwner: true,
 | 
			
		||||
        isArchived: false,
 | 
			
		||||
        isTrashEnabled: true,
 | 
			
		||||
        isInLockedView: false,
 | 
			
		||||
        currentAlbum: null,
 | 
			
		||||
        source: ActionSource.timeline,
 | 
			
		||||
      );
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    test('should build correct widget for each button type', () {
 | 
			
		||||
      for (final buttonType in ActionButtonType.values) {
 | 
			
		||||
        if (buttonType == ActionButtonType.removeFromAlbum) {
 | 
			
		||||
          final album = createRemoteAlbum();
 | 
			
		||||
          final contextWithAlbum = ActionButtonContext(
 | 
			
		||||
            asset: asset,
 | 
			
		||||
            isOwner: true,
 | 
			
		||||
            isArchived: false,
 | 
			
		||||
            isTrashEnabled: true,
 | 
			
		||||
            isInLockedView: false,
 | 
			
		||||
            currentAlbum: album,
 | 
			
		||||
            source: ActionSource.timeline,
 | 
			
		||||
          );
 | 
			
		||||
          final widget = buttonType.buildButton(contextWithAlbum);
 | 
			
		||||
          expect(widget, isA<Widget>());
 | 
			
		||||
        } else {
 | 
			
		||||
          final widget = buttonType.buildButton(context);
 | 
			
		||||
          expect(widget, isA<Widget>());
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  group('ActionButtonBuilder', () {
 | 
			
		||||
    test('should return buttons that should show', () {
 | 
			
		||||
      final remoteAsset = createRemoteAsset();
 | 
			
		||||
      final context = ActionButtonContext(
 | 
			
		||||
        asset: remoteAsset,
 | 
			
		||||
        isOwner: true,
 | 
			
		||||
        isArchived: false,
 | 
			
		||||
        isTrashEnabled: true,
 | 
			
		||||
        isInLockedView: false,
 | 
			
		||||
        currentAlbum: null,
 | 
			
		||||
        source: ActionSource.timeline,
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      final widgets = ActionButtonBuilder.build(context);
 | 
			
		||||
 | 
			
		||||
      expect(widgets, isNotEmpty);
 | 
			
		||||
      expect(widgets.length, greaterThan(0));
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    test('should include album-specific buttons when album is present', () {
 | 
			
		||||
      final remoteAsset = createRemoteAsset();
 | 
			
		||||
      final album = createRemoteAlbum(isActivityEnabled: true, isShared: true);
 | 
			
		||||
      final context = ActionButtonContext(
 | 
			
		||||
        asset: remoteAsset,
 | 
			
		||||
        isOwner: true,
 | 
			
		||||
        isArchived: false,
 | 
			
		||||
        isTrashEnabled: true,
 | 
			
		||||
        isInLockedView: false,
 | 
			
		||||
        currentAlbum: album,
 | 
			
		||||
        source: ActionSource.timeline,
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      final widgets = ActionButtonBuilder.build(context);
 | 
			
		||||
 | 
			
		||||
      expect(widgets, isNotEmpty);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    test('should only include local buttons for local assets', () {
 | 
			
		||||
      final localAsset = createLocalAsset();
 | 
			
		||||
      final context = ActionButtonContext(
 | 
			
		||||
        asset: localAsset,
 | 
			
		||||
        isOwner: true,
 | 
			
		||||
        isArchived: false,
 | 
			
		||||
        isTrashEnabled: true,
 | 
			
		||||
        isInLockedView: false,
 | 
			
		||||
        currentAlbum: null,
 | 
			
		||||
        source: ActionSource.timeline,
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      final widgets = ActionButtonBuilder.build(context);
 | 
			
		||||
 | 
			
		||||
      expect(widgets, isNotEmpty);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    test('should respect archived state', () {
 | 
			
		||||
      final remoteAsset = createRemoteAsset();
 | 
			
		||||
 | 
			
		||||
      final archivedContext = ActionButtonContext(
 | 
			
		||||
        asset: remoteAsset,
 | 
			
		||||
        isOwner: true,
 | 
			
		||||
        isArchived: true,
 | 
			
		||||
        isTrashEnabled: true,
 | 
			
		||||
        isInLockedView: false,
 | 
			
		||||
        currentAlbum: null,
 | 
			
		||||
        source: ActionSource.timeline,
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      final archivedWidgets = ActionButtonBuilder.build(archivedContext);
 | 
			
		||||
 | 
			
		||||
      final nonArchivedContext = ActionButtonContext(
 | 
			
		||||
        asset: remoteAsset,
 | 
			
		||||
        isOwner: true,
 | 
			
		||||
        isArchived: false,
 | 
			
		||||
        isTrashEnabled: true,
 | 
			
		||||
        isInLockedView: false,
 | 
			
		||||
        currentAlbum: null,
 | 
			
		||||
        source: ActionSource.timeline,
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      final nonArchivedWidgets = ActionButtonBuilder.build(nonArchivedContext);
 | 
			
		||||
 | 
			
		||||
      expect(archivedWidgets, isNotEmpty);
 | 
			
		||||
      expect(nonArchivedWidgets, isNotEmpty);
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user