diff --git a/lib/components/home/delete_note_button.dart b/lib/components/home/delete_note_button.dart new file mode 100644 index 000000000..eda024f27 --- /dev/null +++ b/lib/components/home/delete_note_button.dart @@ -0,0 +1,114 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:saber/components/theming/adaptive_alert_dialog.dart'; +import 'package:saber/data/file_manager/file_manager.dart'; +import 'package:saber/i18n/strings.g.dart'; +import 'package:saber/pages/editor/editor.dart'; + +class DeleteNoteButton extends StatelessWidget { + const DeleteNoteButton({ + super.key, + required this.filesToDelete, + required this.unselectNotes, + }); + + final List filesToDelete; + final void Function() unselectNotes; + + @override + Widget build(BuildContext context) { + return IconButton( + padding: EdgeInsets.zero, + tooltip: t.home.deleteNote, + onPressed: () async { + await showDialog( + context: context, + builder: (context) => _DeleteNoteDialog( + filesToDelete: filesToDelete, + unselectNotes: unselectNotes, + ), + ); + }, + icon: const Icon(Icons.delete_forever), + ); + } +} + +class _DeleteNoteDialog extends StatefulWidget { + const _DeleteNoteDialog({ + required this.filesToDelete, + required this.unselectNotes, + }); + + final List filesToDelete; + final void Function() unselectNotes; + + @override + State<_DeleteNoteDialog> createState() => _DeleteNoteDialogState(); +} + +class _DeleteNoteDialogState extends State<_DeleteNoteDialog> { + var deleteAllowed = false; + + @override + void initState() { + super.initState(); + } + + @override + Widget build(BuildContext context) { + return AdaptiveAlertDialog( + title: widget.filesToDelete.length < 5 + ? Text( + t.home.deleteNoteDialog.deleteName( + f: widget.filesToDelete.join(', '), + ), + ) + : Text( + t.home.deleteNoteDialog.deleteNotes( + n: widget.filesToDelete.length, + ), + ), + content: Row( + children: [ + Checkbox( + value: deleteAllowed, + onChanged: (value) => setState(() => deleteAllowed = value!), + ), + Expanded(child: Text(t.home.deleteNoteDialog.deleteAllowed)), + ], + ), + actions: [ + CupertinoDialogAction( + onPressed: () => Navigator.of(context).pop(), + child: Text(t.common.cancel), + ), + CupertinoDialogAction( + onPressed: deleteAllowed + ? () async { + await Future.wait([ + for (final String filePath in widget.filesToDelete) + Future.value( + FileManager.doesFileExist( + filePath + Editor.extensionOldJson, + ), + ).then( + (oldExtension) => FileManager.deleteFile( + filePath + + (oldExtension + ? Editor.extensionOldJson + : Editor.extension), + ), + ), + ]); + if (context.mounted) Navigator.of(context).pop(); + widget.unselectNotes(); + } + : null, + isDestructiveAction: true, + child: Text(t.home.deleteNoteDialog.delete), + ), + ], + ); + } +} diff --git a/lib/i18n/_missing_translations.yaml b/lib/i18n/_missing_translations.yaml index 982e10772..a7c6e4e70 100644 --- a/lib/i18n/_missing_translations.yaml +++ b/lib/i18n/_missing_translations.yaml @@ -4,6 +4,11 @@ ar: home: noPreviewAvailable(OUTDATED): No preview available + deleteNoteDialog: + deleteNotes: Delete $n notes + deleteName: Delete $f + deleteAllowed: Delete all selected notes (they cannot be restored) + delete: Delete sentry: consent: title(OUTDATED): Help improve Saber? @@ -45,8 +50,26 @@ ar: lineThickness(OUTDATED): Line thickness lineThicknessDescription(OUTDATED): Background line thickness cs: + home: + deleteNoteDialog: + deleteNotes: Delete $n notes + deleteName: Delete $f + deleteAllowed: Delete all selected notes (they cannot be restored) + delete: Delete de: + home: + deleteNoteDialog: + deleteNotes: Delete $n notes + deleteName: Delete $f + deleteAllowed: Delete all selected notes (they cannot be restored) + delete: Delete eo: + home: + deleteNoteDialog: + deleteNotes: Delete $n notes + deleteName: Delete $f + deleteAllowed: Delete all selected notes (they cannot be restored) + delete: Delete settings: prefLabels: autoDisableFingerDrawingWhenStylusDetected(OUTDATED): Auto-disable finger drawing @@ -58,6 +81,11 @@ es: continueBtn(OUTDATED): Continue home: noPreviewAvailable(OUTDATED): No preview available + deleteNoteDialog: + deleteNotes: Delete $n notes + deleteName: Delete $f + deleteAllowed: Delete all selected notes (they cannot be restored) + delete: Delete sentry: consent: title(OUTDATED): Help improve Saber? @@ -171,6 +199,11 @@ fa: continueBtn(OUTDATED): Continue home: noPreviewAvailable(OUTDATED): No preview available + deleteNoteDialog: + deleteNotes: Delete $n notes + deleteName: Delete $f + deleteAllowed: Delete all selected notes (they cannot be restored) + delete: Delete sentry: consent: title(OUTDATED): Help improve Saber? @@ -274,6 +307,11 @@ fr: continueBtn(OUTDATED): Continue home: noPreviewAvailable(OUTDATED): No preview available + deleteNoteDialog: + deleteNotes: Delete $n notes + deleteName: Delete $f + deleteAllowed: Delete all selected notes (they cannot be restored) + delete: Delete sentry: consent: title(OUTDATED): Help improve Saber? @@ -387,6 +425,11 @@ he: moveNotes(OUTDATED): Move $n notes multipleRenamedTo(OUTDATED): "The following notes will be renamed:" numberRenamedTo(OUTDATED): $n notes will be renamed to avoid conflicts + deleteNoteDialog: + deleteNotes: Delete $n notes + deleteName: Delete $f + deleteAllowed: Delete all selected notes (they cannot be restored) + delete: Delete sentry: consent: title(OUTDATED): Help improve Saber? @@ -542,6 +585,11 @@ hu: multipleRenamedTo(OUTDATED): "The following notes will be renamed:" numberRenamedTo(OUTDATED): $n notes will be renamed to avoid conflicts deleteNote(OUTDATED): Delete note + deleteNoteDialog: + deleteNotes: Delete $n notes + deleteName: Delete $f + deleteAllowed: Delete all selected notes (they cannot be restored) + delete: Delete renameFolder: renameFolder(OUTDATED): Rename folder folderName(OUTDATED): Folder name @@ -754,6 +802,12 @@ hu: lockAxisAlignedPan(OUTDATED): Lock panning to horizontal or vertical needsToSaveBeforeExiting(OUTDATED): Saving your changes... You can safely exit the editor when it's done it: + home: + deleteNoteDialog: + deleteNotes: Delete $n notes + deleteName: Delete $f + deleteAllowed: Delete all selected notes (they cannot be restored) + delete: Delete ja: common: done(OUTDATED): Done @@ -774,6 +828,11 @@ ja: multipleRenamedTo(OUTDATED): "The following notes will be renamed:" numberRenamedTo(OUTDATED): $n notes will be renamed to avoid conflicts deleteNote(OUTDATED): Delete note + deleteNoteDialog: + deleteNotes: Delete $n notes + deleteName: Delete $f + deleteAllowed: Delete all selected notes (they cannot be restored) + delete: Delete renameFolder: renameFolder(OUTDATED): Rename folder folderName(OUTDATED): Folder name @@ -925,6 +984,11 @@ pt-BR: moveNotes(OUTDATED): Move $n notes multipleRenamedTo(OUTDATED): "The following notes will be renamed:" numberRenamedTo(OUTDATED): $n notes will be renamed to avoid conflicts + deleteNoteDialog: + deleteNotes: Delete $n notes + deleteName: Delete $f + deleteAllowed: Delete all selected notes (they cannot be restored) + delete: Delete sentry: consent: title(OUTDATED): Help improve Saber? @@ -1041,6 +1105,12 @@ pt-BR: watchServer(OUTDATED): Watch for updates on the server watchServerReadOnly(OUTDATED): Editing is disabled while watching the server ru: + home: + deleteNoteDialog: + deleteNotes: Delete $n notes + deleteName: Delete $f + deleteAllowed: Delete all selected notes (they cannot be restored) + delete: Delete settings: prefLabels: autoDisableFingerDrawingWhenStylusDetected(OUTDATED): Auto-disable finger drawing @@ -1049,6 +1119,11 @@ ru: tr: home: noPreviewAvailable(OUTDATED): No preview available + deleteNoteDialog: + deleteNotes: Delete $n notes + deleteName: Delete $f + deleteAllowed: Delete all selected notes (they cannot be restored) + delete: Delete sentry: consent: title(OUTDATED): Help improve Saber? @@ -1090,7 +1165,19 @@ tr: lineThickness(OUTDATED): Line thickness lineThicknessDescription(OUTDATED): Background line thickness zh-Hans-CN: + home: + deleteNoteDialog: + deleteNotes: Delete $n notes + deleteName: Delete $f + deleteAllowed: Delete all selected notes (they cannot be restored) + delete: Delete zh-Hant-TW: + home: + deleteNoteDialog: + deleteNotes: Delete $n notes + deleteName: Delete $f + deleteAllowed: Delete all selected notes (they cannot be restored) + delete: Delete settings: prefLabels: autoDisableFingerDrawingWhenStylusDetected(OUTDATED): Auto-disable finger drawing diff --git a/lib/i18n/en.i18n.yaml b/lib/i18n/en.i18n.yaml index dedcb0b35..85463b230 100644 --- a/lib/i18n/en.i18n.yaml +++ b/lib/i18n/en.i18n.yaml @@ -49,6 +49,11 @@ home: multipleRenamedTo: "The following notes will be renamed:" numberRenamedTo: $n notes will be renamed to avoid conflicts deleteNote: Delete note + deleteNoteDialog: + deleteNotes: Delete $n notes + deleteName: Delete $f + deleteAllowed: Delete all selected notes (they cannot be restored) + delete: Delete renameFolder: renameFolder: Rename folder folderName: Folder name diff --git a/lib/i18n/strings_en.g.dart b/lib/i18n/strings_en.g.dart index 5bdb5c45f..9d1f2ec47 100644 --- a/lib/i18n/strings_en.g.dart +++ b/lib/i18n/strings_en.g.dart @@ -102,6 +102,7 @@ class TranslationsHomeEn { /// en: 'Delete note' String get deleteNote => 'Delete note'; + late final TranslationsHomeDeleteNoteDialogEn deleteNoteDialog = TranslationsHomeDeleteNoteDialogEn.internal(_root); late final TranslationsHomeRenameFolderEn renameFolder = TranslationsHomeRenameFolderEn.internal(_root); late final TranslationsHomeDeleteFolderEn deleteFolder = TranslationsHomeDeleteFolderEn.internal(_root); } @@ -477,6 +478,27 @@ class TranslationsHomeMoveNoteEn { String numberRenamedTo({required Object n}) => '${n} notes will be renamed to avoid conflicts'; } +// Path: home.deleteNoteDialog +class TranslationsHomeDeleteNoteDialogEn { + TranslationsHomeDeleteNoteDialogEn.internal(this._root); + + final Translations _root; // ignore: unused_field + + // Translations + + /// en: 'Delete $n notes' + String deleteNotes({required Object n}) => 'Delete ${n} notes'; + + /// en: 'Delete $f' + String deleteName({required Object f}) => 'Delete ${f}'; + + /// en: 'Delete all selected notes (they cannot be restored)' + String get deleteAllowed => 'Delete all selected notes (they cannot be restored)'; + + /// en: 'Delete' + String get delete => 'Delete'; +} + // Path: home.renameFolder class TranslationsHomeRenameFolderEn { TranslationsHomeRenameFolderEn.internal(this._root); diff --git a/lib/pages/home/browse.dart b/lib/pages/home/browse.dart index 03db7cabf..423544390 100644 --- a/lib/pages/home/browse.dart +++ b/lib/pages/home/browse.dart @@ -4,6 +4,7 @@ import 'package:collapsible/collapsible.dart'; import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; import 'package:path/path.dart' as p; +import 'package:saber/components/home/delete_note_button.dart'; import 'package:saber/components/home/export_note_button.dart'; import 'package:saber/components/home/grid_folders.dart'; import 'package:saber/components/home/masonry_files.dart'; @@ -17,7 +18,6 @@ import 'package:saber/components/theming/saber_theme.dart'; import 'package:saber/data/file_manager/file_manager.dart'; import 'package:saber/data/routes.dart'; import 'package:saber/i18n/strings.g.dart'; -import 'package:saber/pages/editor/editor.dart'; class BrowsePage extends StatefulWidget { const BrowsePage({super.key, String? path}) : initialPath = path; @@ -227,28 +227,9 @@ class _BrowsePageState extends State { filesToMove: selectedFiles.value, unselectNotes: () => selectedFiles.value = [], ), - IconButton( - padding: .zero, - tooltip: t.home.deleteNote, - onPressed: () async { - await Future.wait([ - for (final filePath in selectedFiles.value) - Future.value( - FileManager.doesFileExist( - filePath + Editor.extensionOldJson, - ), - ).then( - (oldExtension) => FileManager.deleteFile( - filePath + - (oldExtension - ? Editor.extensionOldJson - : Editor.extension), - ), - ), - ]); - selectedFiles.value = []; - }, - icon: const Icon(Icons.delete_forever), + DeleteNoteButton( + filesToDelete: selectedFiles.value, + unselectNotes: () => selectedFiles.value = [], ), ExportNoteButton(selectedFiles: selectedFiles.value), ], diff --git a/lib/pages/home/recent_notes.dart b/lib/pages/home/recent_notes.dart index b75fbc174..f29fee04e 100644 --- a/lib/pages/home/recent_notes.dart +++ b/lib/pages/home/recent_notes.dart @@ -4,6 +4,7 @@ import 'package:collapsible/collapsible.dart'; import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; import 'package:logging/logging.dart'; +import 'package:saber/components/home/delete_note_button.dart'; import 'package:saber/components/home/export_note_button.dart'; import 'package:saber/components/home/masonry_files.dart'; import 'package:saber/components/home/move_note_button.dart'; @@ -16,7 +17,6 @@ import 'package:saber/data/file_manager/file_manager.dart'; import 'package:saber/data/prefs.dart'; import 'package:saber/data/routes.dart'; import 'package:saber/i18n/strings.g.dart'; -import 'package:saber/pages/editor/editor.dart'; class RecentPage extends StatefulWidget { const RecentPage({super.key}); @@ -180,28 +180,9 @@ class _RecentPageState extends State { filesToMove: selectedFiles.value, unselectNotes: () => selectedFiles.value = [], ), - IconButton( - padding: .zero, - tooltip: t.home.deleteNote, - onPressed: () async { - await Future.wait([ - for (final filePath in selectedFiles.value) - Future.value( - FileManager.doesFileExist( - filePath + Editor.extensionOldJson, - ), - ).then( - (oldExtension) => FileManager.deleteFile( - filePath + - (oldExtension - ? Editor.extensionOldJson - : Editor.extension), - ), - ), - ]); - selectedFiles.value = []; - }, - icon: const Icon(Icons.delete_forever), + DeleteNoteButton( + filesToDelete: selectedFiles.value, + unselectNotes: () => selectedFiles.value = [], ), ExportNoteButton(selectedFiles: selectedFiles.value), ],