mirror of
https://github.com/immich-app/immich
synced 2025-11-07 17:27:20 +00:00
feat(mobile): separate delete buttons (#4505)
* feat(mobile): delete assets from device only * mobile: add backed up only toggle for delete device only * remove toggle inside alert and show different content * mobile: change content color for local only * mobile: delete local only button to dialog * style: display bottom action in two lines * feat: separate delete buttons * fix: incorrect error message for ownedRemoteSelection * fix: handle remoteOnly from delete everywhere * request confirmation for local only only when non-backed assets are in selection --------- Co-authored-by: shalong-tanwen <139912620+shalong-tanwen@users.noreply.github.com> Co-authored-by: Alex Tran <alex.tran1502@gmail.com>
This commit is contained in:
parent
78de4f1312
commit
4c2befc68c
7 changed files with 328 additions and 54 deletions
|
|
@ -22,7 +22,6 @@ import 'package:immich_mobile/routing/router.dart';
|
|||
import 'package:immich_mobile/shared/models/album.dart';
|
||||
import 'package:immich_mobile/shared/models/asset.dart';
|
||||
import 'package:immich_mobile/shared/providers/asset.provider.dart';
|
||||
import 'package:immich_mobile/shared/providers/server_info.provider.dart';
|
||||
import 'package:immich_mobile/shared/providers/user.provider.dart';
|
||||
import 'package:immich_mobile/shared/ui/immich_loading_indicator.dart';
|
||||
import 'package:immich_mobile/shared/ui/immich_toast.dart';
|
||||
|
|
@ -115,10 +114,10 @@ class MultiselectGrid extends HookConsumerWidget {
|
|||
}) {
|
||||
final assets = selection.value;
|
||||
return assets
|
||||
.remoteOnly(errorCallback: errorBuilder(ownerErrorMessage))
|
||||
.remoteOnly(errorCallback: errorBuilder(localErrorMessage))
|
||||
.ownedOnly(
|
||||
currentUser,
|
||||
errorCallback: errorBuilder(localErrorMessage),
|
||||
errorCallback: errorBuilder(ownerErrorMessage),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -176,11 +175,9 @@ class MultiselectGrid extends HookConsumerWidget {
|
|||
}
|
||||
}
|
||||
|
||||
void onDelete() async {
|
||||
void onDelete([bool force = false]) async {
|
||||
processing.value = true;
|
||||
try {
|
||||
final trashEnabled =
|
||||
ref.read(serverInfoProvider.select((v) => v.serverFeatures.trash));
|
||||
final toDelete = selection.value
|
||||
.ownedOnly(
|
||||
currentUser,
|
||||
|
|
@ -192,23 +189,77 @@ class MultiselectGrid extends HookConsumerWidget {
|
|||
errorBuilder('asset_action_delete_err_read_only'.tr()),
|
||||
)
|
||||
.toList();
|
||||
await ref
|
||||
final isDeleted = await ref
|
||||
.read(assetProvider.notifier)
|
||||
.deleteAssets(toDelete, force: !trashEnabled);
|
||||
.deleteAssets(toDelete, force: force);
|
||||
|
||||
final hasRemote = toDelete.any((a) => a.isRemote);
|
||||
final assetOrAssets = toDelete.length > 1 ? 'assets' : 'asset';
|
||||
final trashOrRemoved =
|
||||
!trashEnabled ? 'deleted permanently' : 'trashed';
|
||||
if (hasRemote) {
|
||||
if (isDeleted) {
|
||||
final assetOrAssets = toDelete.length > 1 ? 'assets' : 'asset';
|
||||
final trashOrRemoved = force ? 'deleted permanently' : 'trashed';
|
||||
ImmichToast.show(
|
||||
context: context,
|
||||
msg: '${selection.value.length} $assetOrAssets $trashOrRemoved',
|
||||
gravity: ToastGravity.BOTTOM,
|
||||
);
|
||||
}
|
||||
selectionEnabledHook.value = false;
|
||||
} finally {
|
||||
selectionEnabledHook.value = false;
|
||||
processing.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
void onDeleteLocal(bool onlyBackedUp) async {
|
||||
processing.value = true;
|
||||
try {
|
||||
final localIds = selection.value.where((a) => a.isLocal).toList();
|
||||
|
||||
final isDeleted = await ref
|
||||
.read(assetProvider.notifier)
|
||||
.deleteLocalOnlyAssets(localIds, onlyBackedUp: onlyBackedUp);
|
||||
if (isDeleted) {
|
||||
final assetOrAssets = localIds.length > 1 ? 'assets' : 'asset';
|
||||
ImmichToast.show(
|
||||
context: context,
|
||||
msg:
|
||||
'${localIds.length} $assetOrAssets removed permanently from your device',
|
||||
gravity: ToastGravity.BOTTOM,
|
||||
);
|
||||
}
|
||||
} finally {
|
||||
selectionEnabledHook.value = false;
|
||||
processing.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
void onDeleteRemote([bool force = false]) async {
|
||||
processing.value = true;
|
||||
try {
|
||||
final toDelete = ownedRemoteSelection(
|
||||
localErrorMessage: 'home_page_delete_remote_err_local'.tr(),
|
||||
ownerErrorMessage: 'home_page_delete_err_partner'.tr(),
|
||||
)
|
||||
// Cannot delete readOnly / external assets. They are handled through library offline jobs
|
||||
.writableOnly(
|
||||
errorCallback:
|
||||
errorBuilder('asset_action_delete_err_read_only'.tr()),
|
||||
)
|
||||
.toList();
|
||||
|
||||
final isDeleted = await ref
|
||||
.read(assetProvider.notifier)
|
||||
.deleteRemoteOnlyAssets(toDelete, force: force);
|
||||
if (isDeleted) {
|
||||
final assetOrAssets = toDelete.length > 1 ? 'assets' : 'asset';
|
||||
final trashOrRemoved = force ? 'deleted permanently' : 'trashed';
|
||||
ImmichToast.show(
|
||||
context: context,
|
||||
msg:
|
||||
'${toDelete.length} $assetOrAssets $trashOrRemoved from the Immich server',
|
||||
gravity: ToastGravity.BOTTOM,
|
||||
);
|
||||
}
|
||||
} finally {
|
||||
selectionEnabledHook.value = false;
|
||||
processing.value = false;
|
||||
}
|
||||
}
|
||||
|
|
@ -401,6 +452,11 @@ class MultiselectGrid extends HookConsumerWidget {
|
|||
onFavorite: favoriteEnabled ? onFavoriteAssets : null,
|
||||
onArchive: archiveEnabled ? onArchiveAsset : null,
|
||||
onDelete: deleteEnabled ? onDelete : null,
|
||||
onDeleteServer: deleteEnabled ? onDeleteRemote : null,
|
||||
|
||||
/// local file deletion is allowed irrespective of [deleteEnabled] since it has
|
||||
/// nothing to do with the state of the asset in the Immich server
|
||||
onDeleteLocal: onDeleteLocal,
|
||||
onAddToAlbum: onAddToAlbum,
|
||||
onCreateNewAlbum: onCreateNewAlbum,
|
||||
onUpload: onUpload,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue