You've already forked immich
							
							
				mirror of
				https://github.com/immich-app/immich.git
				synced 2025-10-31 00:18:28 +02:00 
			
		
		
		
	feat(mobile): delete local action
This commit is contained in:
		| @@ -749,6 +749,7 @@ | ||||
|   "delete_key": "Delete key", | ||||
|   "delete_library": "Delete Library", | ||||
|   "delete_link": "Delete link", | ||||
|   "delete_local_action_prompt": "{count} deleted from device", | ||||
|   "delete_local_dialog_ok_backed_up_only": "Delete Backed Up Only", | ||||
|   "delete_local_dialog_ok_force": "Delete Anyway", | ||||
|   "delete_others": "Delete others", | ||||
|   | ||||
| @@ -43,4 +43,8 @@ class DriftLocalAssetRepository extends DriftDatabaseRepository { | ||||
|       } | ||||
|     }); | ||||
|   } | ||||
|  | ||||
|   Future<void> delete(List<String> ids) { | ||||
|     return _db.localAssetEntity.deleteWhere((row) => row.id.isIn(ids)); | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -1,10 +1,42 @@ | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:fluttertoast/fluttertoast.dart'; | ||||
| import 'package:hooks_riverpod/hooks_riverpod.dart'; | ||||
| import 'package:immich_mobile/constants/enums.dart'; | ||||
| import 'package:immich_mobile/extensions/translate_extensions.dart'; | ||||
| import 'package:immich_mobile/presentation/widgets/action_buttons/base_action_button.widget.dart'; | ||||
| import 'package:immich_mobile/providers/infrastructure/action.provider.dart'; | ||||
| import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; | ||||
| import 'package:immich_mobile/widgets/common/immich_toast.dart'; | ||||
|  | ||||
| class DeleteLocalActionButton extends ConsumerWidget { | ||||
|   const DeleteLocalActionButton({super.key}); | ||||
|   final ActionSource source; | ||||
|  | ||||
|   const DeleteLocalActionButton({super.key, required this.source}); | ||||
|  | ||||
|   void _onTap(BuildContext context, WidgetRef ref) async { | ||||
|     if (!context.mounted) { | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|     final result = await ref.read(actionProvider.notifier).deleteLocal(source); | ||||
|     ref.read(multiSelectProvider.notifier).reset(); | ||||
|  | ||||
|     final successMessage = 'delete_local_action_prompt'.t( | ||||
|       context: context, | ||||
|       args: {'count': result.count.toString()}, | ||||
|     ); | ||||
|  | ||||
|     if (context.mounted) { | ||||
|       ImmichToast.show( | ||||
|         context: context, | ||||
|         msg: result.success | ||||
|             ? successMessage | ||||
|             : 'scaffold_body_error_occurred'.t(context: context), | ||||
|         gravity: ToastGravity.BOTTOM, | ||||
|         toastType: result.success ? ToastType.success : ToastType.error, | ||||
|       ); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context, WidgetRef ref) { | ||||
| @@ -12,6 +44,7 @@ class DeleteLocalActionButton extends ConsumerWidget { | ||||
|       maxWidth: 95.0, | ||||
|       iconData: Icons.no_cell_outlined, | ||||
|       label: "control_bottom_app_bar_delete_from_local".t(context: context), | ||||
|       onPressed: () => _onTap(context, ref), | ||||
|     ); | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -58,7 +58,7 @@ class AssetDetailBottomSheet extends ConsumerWidget { | ||||
|         ), | ||||
|       ], | ||||
|       if (asset.storage == AssetState.local) ...[ | ||||
|         const DeleteLocalActionButton(), | ||||
|         const DeleteLocalActionButton(source: ActionSource.viewer), | ||||
|         const UploadActionButton(), | ||||
|       ], | ||||
|     ]; | ||||
|   | ||||
| @@ -52,7 +52,7 @@ class ArchiveBottomSheet extends ConsumerWidget { | ||||
|           const StackActionButton(), | ||||
|         ], | ||||
|         if (multiselect.hasLocal) ...[ | ||||
|           const DeleteLocalActionButton(), | ||||
|           const DeleteLocalActionButton(source: ActionSource.timeline), | ||||
|           const UploadActionButton(), | ||||
|         ], | ||||
|       ], | ||||
|   | ||||
| @@ -52,7 +52,7 @@ class FavoriteBottomSheet extends ConsumerWidget { | ||||
|           const StackActionButton(), | ||||
|         ], | ||||
|         if (multiselect.hasLocal) ...[ | ||||
|           const DeleteLocalActionButton(), | ||||
|           const DeleteLocalActionButton(source: ActionSource.timeline), | ||||
|           const UploadActionButton(), | ||||
|         ], | ||||
|       ], | ||||
|   | ||||
| @@ -52,7 +52,7 @@ class GeneralBottomSheet extends ConsumerWidget { | ||||
|           const StackActionButton(), | ||||
|         ], | ||||
|         if (multiselect.hasLocal) ...[ | ||||
|           const DeleteLocalActionButton(), | ||||
|           const DeleteLocalActionButton(source: ActionSource.timeline), | ||||
|           const UploadActionButton(), | ||||
|         ], | ||||
|       ], | ||||
|   | ||||
| @@ -1,5 +1,6 @@ | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:hooks_riverpod/hooks_riverpod.dart'; | ||||
| import 'package:immich_mobile/constants/enums.dart'; | ||||
| import 'package:immich_mobile/presentation/widgets/action_buttons/delete_local_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/upload_action_button.widget.dart'; | ||||
| @@ -16,7 +17,7 @@ class LocalAlbumBottomSheet extends ConsumerWidget { | ||||
|       shouldCloseOnMinExtent: false, | ||||
|       actions: [ | ||||
|         ShareActionButton(), | ||||
|         DeleteLocalActionButton(), | ||||
|         DeleteLocalActionButton(source: ActionSource.timeline), | ||||
|         UploadActionButton(), | ||||
|       ], | ||||
|     ); | ||||
|   | ||||
| @@ -55,7 +55,7 @@ class RemoteAlbumBottomSheet extends ConsumerWidget { | ||||
|           const StackActionButton(), | ||||
|         ], | ||||
|         if (multiselect.hasLocal) ...[ | ||||
|           const DeleteLocalActionButton(), | ||||
|           const DeleteLocalActionButton(source: ActionSource.timeline), | ||||
|           const UploadActionButton(), | ||||
|         ], | ||||
|         RemoveFromAlbumActionButton( | ||||
|   | ||||
| @@ -207,6 +207,21 @@ class ActionNotifier extends Notifier<void> { | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   Future<ActionResult> deleteLocal(ActionSource source) async { | ||||
|     final ids = _getOwnedRemoteForSource(source); | ||||
|     try { | ||||
|       await _service.deleteLocal(ids); | ||||
|       return ActionResult(count: ids.length, success: true); | ||||
|     } catch (error, stack) { | ||||
|       _logger.severe('Failed to delete local assets', error, stack); | ||||
|       return ActionResult( | ||||
|         count: ids.length, | ||||
|         success: false, | ||||
|         error: error.toString(), | ||||
|       ); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   Future<ActionResult?> editLocation( | ||||
|     ActionSource source, | ||||
|     BuildContext context, | ||||
|   | ||||
| @@ -2,6 +2,7 @@ import 'package:auto_route/auto_route.dart'; | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:immich_mobile/constants/enums.dart'; | ||||
| import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; | ||||
| import 'package:immich_mobile/infrastructure/repositories/local_asset.repository.dart'; | ||||
| import 'package:immich_mobile/infrastructure/repositories/remote_album.repository.dart'; | ||||
| import 'package:immich_mobile/infrastructure/repositories/remote_asset.repository.dart'; | ||||
| import 'package:immich_mobile/providers/infrastructure/album.provider.dart'; | ||||
| @@ -11,10 +12,12 @@ import 'package:immich_mobile/repositories/drift_album_api_repository.dart'; | ||||
| import 'package:immich_mobile/routing/router.dart'; | ||||
| import 'package:immich_mobile/widgets/common/location_picker.dart'; | ||||
| import 'package:maplibre_gl/maplibre_gl.dart'; | ||||
| import 'package:photo_manager/photo_manager.dart' hide LatLng; | ||||
| import 'package:riverpod_annotation/riverpod_annotation.dart'; | ||||
|  | ||||
| final actionServiceProvider = Provider<ActionService>( | ||||
|   (ref) => ActionService( | ||||
|     ref.watch(localAssetRepository), | ||||
|     ref.watch(assetApiRepositoryProvider), | ||||
|     ref.watch(remoteAssetRepositoryProvider), | ||||
|     ref.watch(driftAlbumApiRepositoryProvider), | ||||
| @@ -23,12 +26,14 @@ final actionServiceProvider = Provider<ActionService>( | ||||
| ); | ||||
|  | ||||
| class ActionService { | ||||
|   final DriftLocalAssetRepository _localAssetRepository; | ||||
|   final AssetApiRepository _assetApiRepository; | ||||
|   final RemoteAssetRepository _remoteAssetRepository; | ||||
|   final DriftAlbumApiRepository _albumApiRepository; | ||||
|   final DriftRemoteAlbumRepository _remoteAlbumRepository; | ||||
|  | ||||
|   const ActionService( | ||||
|     this._localAssetRepository, | ||||
|     this._assetApiRepository, | ||||
|     this._remoteAssetRepository, | ||||
|     this._albumApiRepository, | ||||
| @@ -107,6 +112,11 @@ class ActionService { | ||||
|     await _remoteAssetRepository.delete(remoteIds); | ||||
|   } | ||||
|  | ||||
|   Future<void> deleteLocal(List<String> localIds) async { | ||||
|     await PhotoManager.editor.deleteWithIds(localIds); | ||||
|     await _localAssetRepository.delete(localIds); | ||||
|   } | ||||
|  | ||||
|   Future<bool> editLocation( | ||||
|     List<String> remoteIds, | ||||
|     BuildContext context, | ||||
|   | ||||
		Reference in New Issue
	
	Block a user