From a72b7e5a6a8b186276f9a4943a10acf48fe7e51f Mon Sep 17 00:00:00 2001 From: Yaros Date: Fri, 20 Feb 2026 13:31:53 +0100 Subject: [PATCH 1/4] feat(mobile): prompt when deleting from trash --- i18n/en.json | 2 + .../delete_trash_action_button.widget.dart | 8 ++++ .../asset_grid/trash_delete_dialog.dart | 44 +++++++++++++++++++ 3 files changed, 54 insertions(+) create mode 100644 mobile/lib/widgets/asset_grid/trash_delete_dialog.dart diff --git a/i18n/en.json b/i18n/en.json index 95e958403283a..dbcd0d05a9172 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -1705,6 +1705,8 @@ "permanently_delete": "Permanently delete", "permanently_delete_assets_count": "Permanently delete {count, plural, one {asset} other {assets}}", "permanently_delete_assets_prompt": "Are you sure you want to permanently delete {count, plural, one {this asset?} other {these # assets?}} This will also remove {count, plural, one {it from its} other {them from their}} album(s).", + "permanently_delete_from_trash": "Permanently delete from trash", + "permanently_delete_from_trash_prompt": "Are you sure you want to permanently delete these assets from the trash? This action cannot be undone.", "permanently_deleted_asset": "Permanently deleted asset", "permanently_deleted_assets_count": "Permanently deleted {count, plural, one {# asset} other {# assets}}", "permission": "Permission", diff --git a/mobile/lib/presentation/widgets/action_buttons/delete_trash_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/delete_trash_action_button.widget.dart index cb0e7091c8855..ac3c941c1fc32 100644 --- a/mobile/lib/presentation/widgets/action_buttons/delete_trash_action_button.widget.dart +++ b/mobile/lib/presentation/widgets/action_buttons/delete_trash_action_button.widget.dart @@ -5,6 +5,7 @@ import 'package:immich_mobile/constants/enums.dart'; import 'package:immich_mobile/extensions/translate_extensions.dart'; import 'package:immich_mobile/providers/infrastructure/action.provider.dart'; import 'package:immich_mobile/providers/timeline/multiselect.provider.dart'; +import 'package:immich_mobile/widgets/asset_grid/trash_delete_dialog.dart'; import 'package:immich_mobile/widgets/common/immich_toast.dart'; /// This delete action has the following behavior: @@ -22,6 +23,13 @@ class DeleteTrashActionButton extends ConsumerWidget { return; } + final confirmDelete = + await showDialog(context: context, builder: (context) => const TrashDeleteDialog()) ?? false; + + if (!confirmDelete) { + return; + } + final result = await ref.read(actionProvider.notifier).deleteRemoteAndLocal(source); ref.read(multiSelectProvider.notifier).reset(); diff --git a/mobile/lib/widgets/asset_grid/trash_delete_dialog.dart b/mobile/lib/widgets/asset_grid/trash_delete_dialog.dart new file mode 100644 index 0000000000000..2309b5b25cec3 --- /dev/null +++ b/mobile/lib/widgets/asset_grid/trash_delete_dialog.dart @@ -0,0 +1,44 @@ +import 'package:flutter/material.dart'; +import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/extensions/translate_extensions.dart'; + +class TrashDeleteDialog extends StatelessWidget { + const TrashDeleteDialog({super.key}); + + @override + Widget build(BuildContext context) { + return AlertDialog( + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(10))), + title: const Text("permanently_delete_from_trash").t(context: context), + content: const Text("permanently_delete_from_trash_prompt").t(context: context), + actions: [ + SizedBox( + width: double.infinity, + height: 48, + child: FilledButton( + onPressed: () => context.pop(false), + style: FilledButton.styleFrom( + backgroundColor: context.colorScheme.surfaceDim, + foregroundColor: context.primaryColor, + ), + child: const Text("cancel", style: TextStyle(fontWeight: FontWeight.bold)).t(context: context), + ), + ), + const SizedBox(height: 8), + SizedBox( + width: double.infinity, + height: 48, + + child: FilledButton( + onPressed: () => context.pop(true), + style: FilledButton.styleFrom( + backgroundColor: context.colorScheme.errorContainer, + foregroundColor: context.colorScheme.onErrorContainer, + ), + child: const Text("delete", style: TextStyle(fontWeight: FontWeight.bold)).t(context: context), + ), + ), + ], + ); + } +} From b2bfcb834af66f52c555c8ad8e010cef6c383bad Mon Sep 17 00:00:00 2001 From: Yaros Date: Fri, 20 Feb 2026 17:44:09 +0100 Subject: [PATCH 2/4] refactor: use existing strings --- i18n/en.json | 2 -- .../delete_trash_action_button.widget.dart | 9 +++++++-- .../lib/widgets/asset_grid/trash_delete_dialog.dart | 11 ++++++++--- 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/i18n/en.json b/i18n/en.json index dbcd0d05a9172..95e958403283a 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -1705,8 +1705,6 @@ "permanently_delete": "Permanently delete", "permanently_delete_assets_count": "Permanently delete {count, plural, one {asset} other {assets}}", "permanently_delete_assets_prompt": "Are you sure you want to permanently delete {count, plural, one {this asset?} other {these # assets?}} This will also remove {count, plural, one {it from its} other {them from their}} album(s).", - "permanently_delete_from_trash": "Permanently delete from trash", - "permanently_delete_from_trash_prompt": "Are you sure you want to permanently delete these assets from the trash? This action cannot be undone.", "permanently_deleted_asset": "Permanently deleted asset", "permanently_deleted_assets_count": "Permanently deleted {count, plural, one {# asset} other {# assets}}", "permission": "Permission", diff --git a/mobile/lib/presentation/widgets/action_buttons/delete_trash_action_button.widget.dart b/mobile/lib/presentation/widgets/action_buttons/delete_trash_action_button.widget.dart index ac3c941c1fc32..0d9bc41734386 100644 --- a/mobile/lib/presentation/widgets/action_buttons/delete_trash_action_button.widget.dart +++ b/mobile/lib/presentation/widgets/action_buttons/delete_trash_action_button.widget.dart @@ -23,9 +23,14 @@ class DeleteTrashActionButton extends ConsumerWidget { return; } - final confirmDelete = - await showDialog(context: context, builder: (context) => const TrashDeleteDialog()) ?? false; + final selectCount = ref.watch(multiSelectProvider.select((s) => s.selectedAssets.length)); + final confirmDelete = + await showDialog( + context: context, + builder: (context) => TrashDeleteDialog(count: selectCount), + ) ?? + false; if (!confirmDelete) { return; } diff --git a/mobile/lib/widgets/asset_grid/trash_delete_dialog.dart b/mobile/lib/widgets/asset_grid/trash_delete_dialog.dart index 2309b5b25cec3..50db758c9e845 100644 --- a/mobile/lib/widgets/asset_grid/trash_delete_dialog.dart +++ b/mobile/lib/widgets/asset_grid/trash_delete_dialog.dart @@ -1,16 +1,21 @@ import 'package:flutter/material.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/extensions/translate_extensions.dart'; +import 'package:immich_ui/immich_ui.dart'; class TrashDeleteDialog extends StatelessWidget { - const TrashDeleteDialog({super.key}); + const TrashDeleteDialog({super.key, required this.count}); + + final int count; @override Widget build(BuildContext context) { return AlertDialog( shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(10))), - title: const Text("permanently_delete_from_trash").t(context: context), - content: const Text("permanently_delete_from_trash_prompt").t(context: context), + title: const Text("permanently_delete").t(context: context), + content: ImmichHtmlText( + "permanently_delete_assets_prompt".t(context: context, args: {'count': count.toString()}), + ), actions: [ SizedBox( width: double.infinity, From b13cb4168f7a4e6a046bfe9c5bc3eff912fd5f2c Mon Sep 17 00:00:00 2001 From: Yaros Date: Mon, 23 Feb 2026 14:15:04 +0100 Subject: [PATCH 3/4] chore: use type-safe translations --- .../lib/widgets/asset_grid/trash_delete_dialog.dart | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/mobile/lib/widgets/asset_grid/trash_delete_dialog.dart b/mobile/lib/widgets/asset_grid/trash_delete_dialog.dart index 50db758c9e845..d954711abd283 100644 --- a/mobile/lib/widgets/asset_grid/trash_delete_dialog.dart +++ b/mobile/lib/widgets/asset_grid/trash_delete_dialog.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/extensions/translate_extensions.dart'; +import 'package:immich_mobile/generated/translations.g.dart'; import 'package:immich_ui/immich_ui.dart'; class TrashDeleteDialog extends StatelessWidget { @@ -12,10 +13,8 @@ class TrashDeleteDialog extends StatelessWidget { Widget build(BuildContext context) { return AlertDialog( shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(10))), - title: const Text("permanently_delete").t(context: context), - content: ImmichHtmlText( - "permanently_delete_assets_prompt".t(context: context, args: {'count': count.toString()}), - ), + title: Text(context.t.permanently_delete), + content: ImmichHtmlText(context.t.permanently_delete_assets_prompt(count: count)), actions: [ SizedBox( width: double.infinity, @@ -26,7 +25,7 @@ class TrashDeleteDialog extends StatelessWidget { backgroundColor: context.colorScheme.surfaceDim, foregroundColor: context.primaryColor, ), - child: const Text("cancel", style: TextStyle(fontWeight: FontWeight.bold)).t(context: context), + child: Text(context.t.cancel, style: const TextStyle(fontWeight: FontWeight.bold)).t(context: context), ), ), const SizedBox(height: 8), @@ -40,7 +39,7 @@ class TrashDeleteDialog extends StatelessWidget { backgroundColor: context.colorScheme.errorContainer, foregroundColor: context.colorScheme.onErrorContainer, ), - child: const Text("delete", style: TextStyle(fontWeight: FontWeight.bold)).t(context: context), + child: Text(context.t.delete, style: const TextStyle(fontWeight: FontWeight.bold)).t(context: context), ), ), ], From b99c03c19bdb889e0788d8618f5ab8b32a01e146 Mon Sep 17 00:00:00 2001 From: Yaros Date: Mon, 23 Feb 2026 15:39:57 +0100 Subject: [PATCH 4/4] chore: remove old translation function --- mobile/lib/widgets/asset_grid/trash_delete_dialog.dart | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/mobile/lib/widgets/asset_grid/trash_delete_dialog.dart b/mobile/lib/widgets/asset_grid/trash_delete_dialog.dart index d954711abd283..2e0fae76a38d4 100644 --- a/mobile/lib/widgets/asset_grid/trash_delete_dialog.dart +++ b/mobile/lib/widgets/asset_grid/trash_delete_dialog.dart @@ -1,6 +1,5 @@ import 'package:flutter/material.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; -import 'package:immich_mobile/extensions/translate_extensions.dart'; import 'package:immich_mobile/generated/translations.g.dart'; import 'package:immich_ui/immich_ui.dart'; @@ -25,7 +24,7 @@ class TrashDeleteDialog extends StatelessWidget { backgroundColor: context.colorScheme.surfaceDim, foregroundColor: context.primaryColor, ), - child: Text(context.t.cancel, style: const TextStyle(fontWeight: FontWeight.bold)).t(context: context), + child: Text(context.t.cancel, style: const TextStyle(fontWeight: FontWeight.bold)), ), ), const SizedBox(height: 8), @@ -39,7 +38,7 @@ class TrashDeleteDialog extends StatelessWidget { backgroundColor: context.colorScheme.errorContainer, foregroundColor: context.colorScheme.onErrorContainer, ), - child: Text(context.t.delete, style: const TextStyle(fontWeight: FontWeight.bold)).t(context: context), + child: Text(context.t.delete, style: const TextStyle(fontWeight: FontWeight.bold)), ), ), ],