diff --git a/mobile/lib/domain/models/store.model.dart b/mobile/lib/domain/models/store.model.dart index c27b1f1ca8..ae0112acb3 100644 --- a/mobile/lib/domain/models/store.model.dart +++ b/mobile/lib/domain/models/store.model.dart @@ -1,5 +1,4 @@ import 'package:immich_mobile/domain/models/user.model.dart'; -import 'package:immich_mobile/utils/action_button.utils.dart'; /// Key for each possible value in the `Store`. /// Defines the data type for each value @@ -73,7 +72,7 @@ enum StoreKey { autoPlayVideo._(139), albumGridView._(140), - viewerQuickActionOrder>._(141), + viewerQuickActionOrder._(141), // Experimental stuff photoManagerCustomFilter._(1000), diff --git a/mobile/lib/domain/services/quick_action.service.dart b/mobile/lib/domain/services/quick_action.service.dart new file mode 100644 index 0000000000..a98e994b26 --- /dev/null +++ b/mobile/lib/domain/services/quick_action.service.dart @@ -0,0 +1,109 @@ +import 'package:immich_mobile/infrastructure/repositories/action_button_order.repository.dart'; +import 'package:immich_mobile/utils/action_button.utils.dart'; + +/// Service for managing quick action configurations. +/// Provides business logic for building quick action types based on context. +class QuickActionService { + final ActionButtonOrderRepository _repository; + + const QuickActionService(this._repository); + + // Constants + static const int defaultQuickActionLimit = 4; + + static const List defaultQuickActionSeed = [ + ActionButtonType.share, + ActionButtonType.upload, + ActionButtonType.edit, + ActionButtonType.add, + ActionButtonType.archive, + ActionButtonType.delete, + ActionButtonType.removeFromAlbum, + ActionButtonType.likeActivity, + ]; + + static final Set _quickActionSet = Set.unmodifiable(defaultQuickActionSeed); + + static final List defaultQuickActionOrder = List.unmodifiable( + defaultQuickActionSeed, + ); + + /// Get the list of available quick action options + // static List get quickActionOptions => defaultQuickActionOrder; + + /// Get the current quick action order + List get() { + return _repository.get(); + } + + /// Set the quick action order + Future set(List order) async { + final normalized = _normalizeQuickActionOrder(order); + await _repository.set(normalized); + } + + /// Watch for changes to quick action order + Stream> watch() { + return _repository.watch(); + } + + /// Normalize quick action order by filtering valid types and ensuring all defaults are included + 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); + } + + /// Build a list of quick action types based on context and custom order + 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; + } + + /// Resolve quick action type based on context (e.g., archive -> unarchive) + 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; + } +} diff --git a/mobile/lib/infrastructure/repositories/action_button_order.repository.dart b/mobile/lib/infrastructure/repositories/action_button_order.repository.dart new file mode 100644 index 0000000000..2efaeb0a75 --- /dev/null +++ b/mobile/lib/infrastructure/repositories/action_button_order.repository.dart @@ -0,0 +1,76 @@ +import 'dart:convert'; + +import 'package:immich_mobile/domain/models/store.model.dart'; +import 'package:immich_mobile/entities/store.entity.dart'; +import 'package:immich_mobile/utils/action_button.utils.dart'; + +/// Repository for managing quick action button order persistence. +/// Handles serialization, deserialization, and storage operations. +class ActionButtonOrderRepository { + const ActionButtonOrderRepository(); + + /// Default order for quick actions + static const List defaultOrder = [ + ActionButtonType.share, + ActionButtonType.upload, + ActionButtonType.edit, + ActionButtonType.add, + ActionButtonType.archive, + ActionButtonType.delete, + ActionButtonType.removeFromAlbum, + ActionButtonType.likeActivity, + ]; + + /// Get the current quick action order from storage + List get() { + final json = Store.tryGet(StoreKey.viewerQuickActionOrder); + if (json == null || json.isEmpty) { + return defaultOrder; + } + + final deserialized = _deserialize(json); + return deserialized.isEmpty ? defaultOrder : deserialized; + } + + /// Save quick action order to storage + Future set(List order) async { + final json = _serialize(order); + await Store.put(StoreKey.viewerQuickActionOrder, json); + } + + /// Watch for changes to quick action order + Stream> watch() { + return Store.watch(StoreKey.viewerQuickActionOrder).map((json) { + if (json == null || json.isEmpty) { + return defaultOrder; + } + final deserialized = _deserialize(json); + return deserialized.isEmpty ? defaultOrder : deserialized; + }); + } + + /// Serialize a list of ActionButtonType to JSON string + String _serialize(List order) { + return jsonEncode(order.map((type) => type.name).toList()); + } + + /// Deserialize a JSON string to a list of ActionButtonType + List _deserialize(String json) { + try { + final list = jsonDecode(json) as List; + return list + .whereType() + .map((name) { + try { + return ActionButtonType.values.byName(name); + } catch (e) { + return null; + } + }) + .whereType() + .toList(); + } catch (e) { + return []; + } + } +} diff --git a/mobile/lib/infrastructure/repositories/store.repository.dart b/mobile/lib/infrastructure/repositories/store.repository.dart index 920863ecf8..d4e34a02f5 100644 --- a/mobile/lib/infrastructure/repositories/store.repository.dart +++ b/mobile/lib/infrastructure/repositories/store.repository.dart @@ -1,4 +1,3 @@ -import 'dart:convert'; import 'package:drift/drift.dart'; import 'package:immich_mobile/domain/models/store.model.dart'; import 'package:immich_mobile/domain/models/user.model.dart'; @@ -6,7 +5,6 @@ import 'package:immich_mobile/infrastructure/entities/store.entity.dart'; import 'package:immich_mobile/infrastructure/entities/store.entity.drift.dart'; import 'package:immich_mobile/infrastructure/repositories/db.repository.dart'; import 'package:immich_mobile/infrastructure/repositories/user.repository.dart'; -import 'package:immich_mobile/utils/action_button.utils.dart'; import 'package:isar/isar.dart'; // Temporary interface until Isar is removed to make the service work with both Isar and Sqlite @@ -86,11 +84,6 @@ class IsarStoreRepository extends IsarDatabaseRepository implements IStoreReposi const (DateTime) => entity.intValue == null ? null : DateTime.fromMillisecondsSinceEpoch(entity.intValue!), const (UserDto) => entity.strValue == null ? null : await IsarUserRepository(_db).getByUserId(entity.strValue!), - const (List) => - (jsonDecode(entity.strValue ?? '[]') as List) - .map((d) => ActionButtonType.values.byName(d)) - .toList() - as T, _ => null, } as T?; @@ -102,7 +95,6 @@ class IsarStoreRepository extends IsarDatabaseRepository implements IStoreReposi const (bool) => ((value as bool) ? 1 : 0, null), const (DateTime) => ((value as DateTime).millisecondsSinceEpoch, null), const (UserDto) => (null, (await IsarUserRepository(_db).update(value as UserDto)).id), - const (List) => (null, jsonEncode(value)), _ => throw UnsupportedError("Unsupported primitive type: ${key.type} for key: ${key.name}"), }; return StoreValue(key.id, intValue: intValue, strValue: strValue); @@ -182,11 +174,6 @@ class DriftStoreRepository extends DriftDatabaseRepository implements IStoreRepo const (DateTime) => entity.intValue == null ? null : DateTime.fromMillisecondsSinceEpoch(entity.intValue!), const (UserDto) => entity.stringValue == null ? null : await DriftAuthUserRepository(_db).get(entity.stringValue!), - const (List) => - (jsonDecode(entity.stringValue ?? '[]') as List) - .map((d) => ActionButtonType.values.byName(d)) - .toList() - as T, _ => null, } as T?; @@ -198,7 +185,6 @@ class DriftStoreRepository extends DriftDatabaseRepository implements IStoreRepo const (bool) => ((value as bool) ? 1 : 0, null), const (DateTime) => ((value as DateTime).millisecondsSinceEpoch, null), const (UserDto) => (null, (await DriftAuthUserRepository(_db).upsert(value as UserDto)).id), - const (List) => (null, jsonEncode(value)), _ => throw UnsupportedError("Unsupported primitive type: ${key.type} for key: ${key.name}"), }; return StoreEntityCompanion(id: Value(key.id), intValue: Value(intValue), stringValue: Value(strValue)); diff --git a/mobile/lib/presentation/widgets/asset_viewer/bottom_bar.widget.dart b/mobile/lib/presentation/widgets/asset_viewer/bottom_bar.widget.dart index da897c6d57..832b8a0221 100644 --- a/mobile/lib/presentation/widgets/asset_viewer/bottom_bar.widget.dart +++ b/mobile/lib/presentation/widgets/asset_viewer/bottom_bar.widget.dart @@ -56,7 +56,8 @@ class ViewerBottomBar extends ConsumerWidget { source: ActionSource.viewer, ); - final quickActionTypes = ActionButtonBuilder.buildQuickActionTypes( + final quickActionService = ref.watch(quickActionServiceProvider); + final quickActionTypes = quickActionService.buildQuickActionTypes( buttonContext, quickActionOrder: quickActionOrder, ); @@ -76,9 +77,10 @@ class ViewerBottomBar extends ConsumerWidget { }); } - final actions = quickActionTypes - .map((type) => GestureDetector(onLongPress: openConfigurator, child: type.buildButton(buttonContext))) - .toList(growable: false); + final actions = ActionButtonBuilder.buildQuickActions( + buttonContext, + quickActionTypes: quickActionTypes, + ).map((widget) => GestureDetector(onLongPress: openConfigurator, child: widget)).toList(growable: false); return IgnorePointer( ignoring: opacity < 255, diff --git a/mobile/lib/presentation/widgets/asset_viewer/quick_action_configurator.widget.dart b/mobile/lib/presentation/widgets/asset_viewer/quick_action_configurator.widget.dart index 64fc915d11..9672edfc94 100644 --- a/mobile/lib/presentation/widgets/asset_viewer/quick_action_configurator.widget.dart +++ b/mobile/lib/presentation/widgets/asset_viewer/quick_action_configurator.widget.dart @@ -3,6 +3,7 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_reorderable_grid_view/widgets/reorderable_builder.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/domain/services/quick_action.service.dart'; import 'package:immich_mobile/providers/infrastructure/viewer_quick_action_order.provider.dart'; import 'package:immich_mobile/utils/action_button.utils.dart'; import 'package:immich_mobile/utils/action_button_visuals.dart'; @@ -41,7 +42,7 @@ class _QuickActionConfiguratorState extends ConsumerState.from(ActionButtonBuilder.defaultQuickActionOrder); + _order = List.from(QuickActionService.defaultQuickActionOrder); _hasLocalChanges = true; }); } @@ -49,9 +50,7 @@ class _QuickActionConfiguratorState extends ConsumerState Navigator.of(context).pop(); Future _save() async { - final normalized = ActionButtonBuilder.normalizeQuickActionOrder(_order); - - await ref.read(viewerQuickActionOrderProvider.notifier).setOrder(normalized); + await ref.read(viewerQuickActionOrderProvider.notifier).setOrder(_order); _hasLocalChanges = false; if (mounted) { Navigator.of(context).pop(); @@ -69,8 +68,8 @@ class _QuickActionConfiguratorState extends ConsumerState.from(currentOrder); } - final normalizedSelection = ActionButtonBuilder.normalizeQuickActionOrder(_order); - final hasChanges = !listEquals(currentOrder, normalizedSelection); + + final hasChanges = !listEquals(currentOrder, _order); return SafeArea( child: Padding( @@ -91,7 +90,7 @@ class _QuickActionConfiguratorState extends ConsumerState( + (ref) => const ActionButtonOrderRepository(), +); -@Riverpod(keepAlive: true) -class ViewerQuickActionOrder extends _$ViewerQuickActionOrder { +final quickActionServiceProvider = Provider( + (ref) => QuickActionService(ref.watch(actionButtonOrderRepositoryProvider)), +); + +final viewerQuickActionOrderProvider = StateNotifierProvider>( + (ref) => ViewerQuickActionOrderNotifier(ref.watch(quickActionServiceProvider)), +); + +class ViewerQuickActionOrderNotifier extends StateNotifier> { + final QuickActionService _service; StreamSubscription>? _subscription; + ViewerQuickActionOrderNotifier(this._service) : super(_service.get()) { + _subscription = _service.watch().listen((order) { + state = order; + }); + } + @override - List build() { - final service = ref.watch(appSettingsServiceProvider); - final initial = ActionButtonBuilder.normalizeQuickActionOrder( - service.getSetting(AppSettingsEnum.viewerQuickActionOrder), - ); - - _subscription ??= service.watchSetting(AppSettingsEnum.viewerQuickActionOrder).listen((order) { - state = ActionButtonBuilder.normalizeQuickActionOrder(order); - }); - - ref.onDispose(() { - _subscription?.cancel(); - _subscription = null; - }); - - return initial; + void dispose() { + _subscription?.cancel(); + super.dispose(); } Future setOrder(List order) async { - final normalized = ActionButtonBuilder.normalizeQuickActionOrder(order); - - if (listEquals(state, normalized)) { + if (listEquals(state, order)) { return; } final previous = state; - state = normalized; + state = order; try { - await ref.read(appSettingsServiceProvider).setSetting(AppSettingsEnum.viewerQuickActionOrder, normalized); + await _service.set(order); } catch (error) { state = previous; rethrow; } } } - -/// Mock class for testing -abstract class ViewerQuickActionOrderInternal extends _$ViewerQuickActionOrder {} diff --git a/mobile/lib/providers/infrastructure/viewer_quick_action_order.provider.g.dart b/mobile/lib/providers/infrastructure/viewer_quick_action_order.provider.g.dart deleted file mode 100644 index c54e80a452..0000000000 --- a/mobile/lib/providers/infrastructure/viewer_quick_action_order.provider.g.dart +++ /dev/null @@ -1,27 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of 'viewer_quick_action_order.provider.dart'; - -// ************************************************************************** -// RiverpodGenerator -// ************************************************************************** - -String _$viewerQuickActionOrderHash() => - r'd539bc6ba5fae4fa07a7c30c42d9f6aee1488f97'; - -/// See also [ViewerQuickActionOrder]. -@ProviderFor(ViewerQuickActionOrder) -final viewerQuickActionOrderProvider = - NotifierProvider>.internal( - ViewerQuickActionOrder.new, - name: r'viewerQuickActionOrderProvider', - debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') - ? null - : _$viewerQuickActionOrderHash, - dependencies: null, - allTransitiveDependencies: null, - ); - -typedef _$ViewerQuickActionOrder = Notifier>; -// ignore_for_file: type=lint -// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package diff --git a/mobile/lib/services/app_settings.service.dart b/mobile/lib/services/app_settings.service.dart index dc2c78331f..9216bc1254 100644 --- a/mobile/lib/services/app_settings.service.dart +++ b/mobile/lib/services/app_settings.service.dart @@ -1,7 +1,6 @@ import 'package:immich_mobile/constants/colors.dart'; import 'package:immich_mobile/domain/models/store.model.dart'; import 'package:immich_mobile/entities/store.entity.dart'; -import 'package:immich_mobile/utils/action_button.utils.dart'; enum AppSettingsEnum { loadPreview(StoreKey.loadPreview, "loadPreview", true), @@ -56,11 +55,7 @@ enum AppSettingsEnum { albumGridView(StoreKey.albumGridView, "albumGridView", false), backupRequireCharging(StoreKey.backupRequireCharging, null, false), backupTriggerDelay(StoreKey.backupTriggerDelay, null, 30), - viewerQuickActionOrder>( - StoreKey.viewerQuickActionOrder, - null, - ActionButtonBuilder.defaultQuickActionSeed, - ); + viewerQuickActionOrder(StoreKey.viewerQuickActionOrder, null, ''); const AppSettingsEnum(this.storeKey, this.hiveKey, this.defaultValue); diff --git a/mobile/lib/utils/action_button.utils.dart b/mobile/lib/utils/action_button.utils.dart index 40ae0e4157..a57484735e 100644 --- a/mobile/lib/utils/action_button.utils.dart +++ b/mobile/lib/utils/action_button.utils.dart @@ -169,99 +169,23 @@ enum ActionButtonType { } } +/// Builder class for creating action button widgets. +/// This class provides simple factory methods for building action button widgets +/// from ActionButtonContext. Business logic for quick actions is handled by QuickActionService. class ActionButtonBuilder { static const List _actionTypes = ActionButtonType.values; - static const int defaultQuickActionLimit = 4; - - static const List defaultQuickActionSeed = [ - ActionButtonType.share, - ActionButtonType.upload, - ActionButtonType.edit, - ActionButtonType.add, - ActionButtonType.archive, - ActionButtonType.delete, - ActionButtonType.removeFromAlbum, - ActionButtonType.likeActivity, - ]; - - static final Set _quickActionSet = Set.unmodifiable(defaultQuickActionSeed); - - static final List defaultQuickActionOrder = List.unmodifiable( - defaultQuickActionSeed, - ); - - static List get quickActionOptions => defaultQuickActionOrder; - - 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; - } - + /// Build a list of quick action widgets based on context and custom order. + /// Uses QuickActionService for business logic. static List buildQuickActions( ActionButtonContext context, { - List? quickActionOrder, - int limit = defaultQuickActionLimit, + required List quickActionTypes, }) { - final types = buildQuickActionTypes(context, quickActionOrder: quickActionOrder, limit: limit); - return types.map((type) => type.buildButton(context)).toList(); + return quickActionTypes.map((type) => type.buildButton(context)).toList(); } + /// Build all available action button widgets for the given context. 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); } diff --git a/mobile/test/domain/services/quick_action_service_test.dart b/mobile/test/domain/services/quick_action_service_test.dart new file mode 100644 index 0000000000..35d43c06a0 --- /dev/null +++ b/mobile/test/domain/services/quick_action_service_test.dart @@ -0,0 +1,150 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:immich_mobile/constants/enums.dart'; +import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; +import 'package:immich_mobile/domain/services/quick_action.service.dart'; +import 'package:immich_mobile/infrastructure/repositories/action_button_order.repository.dart'; +import 'package:immich_mobile/utils/action_button.utils.dart'; + +void main() { + group('QuickActionService', () { + late QuickActionService service; + + setUp(() { + // Use repository with default behavior for testing + service = const QuickActionService(ActionButtonOrderRepository()); + }); + + test('buildQuickActionTypes should respect custom order', () { + final remoteAsset = RemoteAsset( + id: 'test-id', + name: 'test.jpg', + checksum: 'checksum', + type: AssetType.image, + ownerId: 'owner-id', + createdAt: DateTime.now(), + updatedAt: DateTime.now(), + ); + + final context = ActionButtonContext( + asset: remoteAsset, + isOwner: true, + isArchived: false, + isTrashEnabled: true, + isInLockedView: false, + currentAlbum: null, + advancedTroubleshooting: false, + isStacked: false, + source: ActionSource.viewer, + ); + + final customOrder = [ + ActionButtonType.archive, + ActionButtonType.share, + ActionButtonType.edit, + ActionButtonType.delete, + ]; + + final types = service.buildQuickActionTypes(context, quickActionOrder: customOrder); + + expect(types.length, lessThanOrEqualTo(QuickActionService.defaultQuickActionLimit)); + expect(types.first, ActionButtonType.archive); + expect(types[1], ActionButtonType.share); + }); + + test('buildQuickActionTypes should resolve archive to unarchive when archived', () { + final remoteAsset = RemoteAsset( + id: 'test-id', + name: 'test.jpg', + checksum: 'checksum', + type: AssetType.image, + ownerId: 'owner-id', + createdAt: DateTime.now(), + updatedAt: DateTime.now(), + ); + + final context = ActionButtonContext( + asset: remoteAsset, + isOwner: true, + isArchived: true, // archived + isTrashEnabled: true, + isInLockedView: false, + currentAlbum: null, + advancedTroubleshooting: false, + isStacked: false, + source: ActionSource.viewer, + ); + + final customOrder = [ActionButtonType.archive]; + + final types = service.buildQuickActionTypes(context, quickActionOrder: customOrder); + + expect(types.contains(ActionButtonType.unarchive), isTrue); + expect(types.contains(ActionButtonType.archive), isFalse); + }); + + test('buildQuickActionTypes should filter types that shouldShow returns false', () { + final localAsset = LocalAsset( + id: 'local-id', + name: 'test.jpg', + checksum: 'checksum', + type: AssetType.image, + createdAt: DateTime.now(), + updatedAt: DateTime.now(), + ); + + final context = ActionButtonContext( + asset: localAsset, + isOwner: true, + isArchived: false, + isTrashEnabled: true, + isInLockedView: false, + currentAlbum: null, + advancedTroubleshooting: false, + isStacked: false, + source: ActionSource.viewer, + ); + + final customOrder = [ + ActionButtonType.archive, // should not show for local-only asset + ActionButtonType.share, + ]; + + final types = service.buildQuickActionTypes(context, quickActionOrder: customOrder); + + expect(types.contains(ActionButtonType.archive), isFalse); + expect(types.contains(ActionButtonType.share), isTrue); + }); + + test('buildQuickActionTypes should respect limit', () { + final remoteAsset = RemoteAsset( + id: 'test-id', + name: 'test.jpg', + checksum: 'checksum', + type: AssetType.image, + ownerId: 'owner-id', + createdAt: DateTime.now(), + updatedAt: DateTime.now(), + ); + + final context = ActionButtonContext( + asset: remoteAsset, + isOwner: true, + isArchived: false, + isTrashEnabled: true, + isInLockedView: false, + currentAlbum: null, + advancedTroubleshooting: false, + isStacked: false, + source: ActionSource.viewer, + ); + + final types = service.buildQuickActionTypes( + context, + quickActionOrder: QuickActionService.defaultQuickActionOrder, + limit: 2, + ); + + expect(types.length, 2); + }); + }); +} diff --git a/mobile/test/utils/action_button_utils_test.dart b/mobile/test/utils/action_button_utils_test.dart index bfc572624f..c03ca2910b 100644 --- a/mobile/test/utils/action_button_utils_test.dart +++ b/mobile/test/utils/action_button_utils_test.dart @@ -3,6 +3,8 @@ 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/domain/services/quick_action.service.dart'; +import 'package:immich_mobile/infrastructure/repositories/action_button_order.repository.dart'; import 'package:immich_mobile/presentation/widgets/action_buttons/archive_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'; @@ -1029,7 +1031,8 @@ void main() { source: ActionSource.viewer, ); - final quickActions = ActionButtonBuilder.buildQuickActions( + final quickActionService = const QuickActionService(ActionButtonOrderRepository()); + final quickActionTypes = quickActionService.buildQuickActionTypes( context, quickActionOrder: const [ ActionButtonType.archive, @@ -1039,7 +1042,9 @@ void main() { ], ); - expect(quickActions.length, ActionButtonBuilder.defaultQuickActionLimit); + final quickActions = ActionButtonBuilder.buildQuickActions(context, quickActionTypes: quickActionTypes); + + expect(quickActions.length, QuickActionService.defaultQuickActionLimit); expect(quickActions.first, isA()); expect(quickActions[1], isA()); expect(quickActions[2], isA());