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/domain/models/asset/base_asset.model.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/advanced_info_action_button.widget.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/edit_image_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/similar_photos_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/unstack_action_button.widget.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/upload_action_button.widget.dart'; class ActionButtonContext { final BaseAsset asset; final bool isOwner; final bool isArchived; final bool isTrashEnabled; final bool isInLockedView; final bool isStacked; final RemoteAlbum? currentAlbum; final bool advancedTroubleshooting; final ActionSource source; const ActionButtonContext({ required this.asset, required this.isOwner, required this.isArchived, required this.isTrashEnabled, required this.isStacked, required this.isInLockedView, required this.currentAlbum, required this.advancedTroubleshooting, required this.source, }); } enum ActionButtonType { advancedInfo, share, edit, shareLink, similarPhotos, archive, unarchive, download, trash, deletePermanent, delete, moveToLockFolder, removeFromLockFolder, deleteLocal, upload, removeFromAlbum, unstack, likeActivity; bool shouldShow(ActionButtonContext context) { return switch (this) { ActionButtonType.advancedInfo => context.advancedTroubleshooting, ActionButtonType.share => true, ActionButtonType.edit => !context.isInLockedView && // context.asset.isImage, 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.hasLocal, ActionButtonType.upload => !context.isInLockedView && // context.asset.storage == AssetState.local, ActionButtonType.removeFromAlbum => context.isOwner && // !context.isInLockedView && // context.currentAlbum != null, ActionButtonType.unstack => context.isOwner && // !context.isInLockedView && // context.isStacked, ActionButtonType.likeActivity => !context.isInLockedView && context.currentAlbum != null && context.currentAlbum!.isActivityEnabled && context.currentAlbum!.isShared, ActionButtonType.similarPhotos => !context.isInLockedView && // context.asset is RemoteAsset, }; } Widget buildButton(ActionButtonContext context) { return switch (this) { ActionButtonType.advancedInfo => AdvancedInfoActionButton(source: context.source), ActionButtonType.share => ShareActionButton(source: context.source), ActionButtonType.edit => const EditImageActionButton(), 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(), ActionButtonType.unstack => UnStackActionButton(source: context.source), ActionButtonType.similarPhotos => SimilarPhotosActionButton(assetId: (context.asset as RemoteAsset).id), }; } } class ActionButtonBuilder { static const List _actionTypes = ActionButtonType.values; static const int defaultQuickActionLimit = 4; static const String quickActionStorageDelimiter = ','; static const List _defaultQuickActionSeed = [ ActionButtonType.share, ActionButtonType.upload, ActionButtonType.edit, ActionButtonType.archive, ActionButtonType.delete, ActionButtonType.removeFromAlbum, ActionButtonType.likeActivity, ]; static final Set _quickActionSet = Set.unmodifiable(_defaultQuickActionSeed); static final List defaultQuickActionOrder = List.unmodifiable( _defaultQuickActionSeed, ); static final String defaultQuickActionOrderStorageValue = defaultQuickActionOrder .map((type) => type.name) .join(quickActionStorageDelimiter); static List get quickActionOptions => defaultQuickActionOrder; static List parseQuickActionOrder(String? stored) { final parsed = []; if (stored != null && stored.trim().isNotEmpty) { for (final name in stored.split(quickActionStorageDelimiter)) { final type = _typeByName(name.trim()); if (type != null) { parsed.add(type); } } } return normalizeQuickActionOrder(parsed); } static String encodeQuickActionOrder(List order) { final unique = {}; final buffer = []; for (final type in order) { if (unique.add(type)) { buffer.add(type.name); } } final result = buffer.join(quickActionStorageDelimiter); return result; } static List buildQuickActionTypes( ActionButtonContext context, { List? quickActionOrder, int limit = defaultQuickActionLimit, }) { final normalized = normalizeQuickActionOrder( quickActionOrder == null || quickActionOrder.isEmpty ? defaultQuickActionOrder : quickActionOrder, ); final seen = {}; final result = []; for (final type in normalized) { if (!_quickActionSet.contains(type)) { continue; } final resolved = _resolveQuickActionType(type, context); if (!seen.add(resolved) || !resolved.shouldShow(context)) { continue; } result.add(resolved); if (result.length >= limit) { break; } } return result; } static List buildQuickActions( ActionButtonContext context, { List? quickActionOrder, int limit = defaultQuickActionLimit, }) { final types = buildQuickActionTypes(context, quickActionOrder: quickActionOrder, limit: limit); return types.map((type) => type.buildButton(context)).toList(); } static ActionButtonType? _typeByName(String name) { if (name.isEmpty) { return null; } for (final type in ActionButtonType.values) { if (type.name == name) { return type; } } return null; } static List build(ActionButtonContext context) { return _actionTypes.where((type) => type.shouldShow(context)).map((type) => type.buildButton(context)).toList(); } static List normalizeQuickActionOrder(List order) { final ordered = {}; for (final type in order) { if (_quickActionSet.contains(type)) { ordered.add(type); } } ordered.addAll(_defaultQuickActionSeed); return ordered.toList(growable: false); } static ActionButtonType _resolveQuickActionType(ActionButtonType type, ActionButtonContext context) { if (type == ActionButtonType.archive && context.isArchived) { return ActionButtonType.unarchive; } if (type == ActionButtonType.delete && context.asset.isLocalOnly) { return ActionButtonType.deleteLocal; } return type; } static bool isSupportedQuickAction(ActionButtonType type) => _quickActionSet.contains(type); }