diff --git a/app/lib/features/bangs/presentation/dialogs/delete_bang_dialog.dart b/app/lib/features/bangs/presentation/dialogs/delete_bang_dialog.dart new file mode 100644 index 00000000..eadc0f21 --- /dev/null +++ b/app/lib/features/bangs/presentation/dialogs/delete_bang_dialog.dart @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2024-2025 Fabian Freund. + * + * This file is part of WebLibre + * (see https://weblibre.eu). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +import 'package:flutter/material.dart'; + +/// Dialog to confirm bang deletion. +/// Returns true if user confirms deletion, false if cancelled, null if dismissed. +Future showDeleteBangDialog(BuildContext context) { + return showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + icon: const Icon(Icons.warning), + title: const Text('Delete Bang'), + content: const Text( + 'Are you sure you want to delete this Bang?', + ), + actions: [ + TextButton( + onPressed: () { + Navigator.pop(context, false); + }, + child: const Text('Cancel'), + ), + TextButton( + onPressed: () { + Navigator.pop(context, true); + }, + child: const Text('Delete'), + ), + ], + ); + }, + ); +} diff --git a/app/lib/features/bangs/presentation/screens/edit.dart b/app/lib/features/bangs/presentation/screens/edit.dart index 7ad54c55..b2b6e4d5 100644 --- a/app/lib/features/bangs/presentation/screens/edit.dart +++ b/app/lib/features/bangs/presentation/screens/edit.dart @@ -28,6 +28,7 @@ import 'package:weblibre/features/bangs/data/models/bang_group.dart'; import 'package:weblibre/features/bangs/data/models/bang_key.dart'; import 'package:weblibre/features/bangs/domain/providers/bangs.dart'; import 'package:weblibre/features/bangs/domain/repositories/data.dart'; +import 'package:weblibre/features/bangs/presentation/dialogs/delete_bang_dialog.dart'; import 'package:weblibre/utils/form_validators.dart'; import 'package:weblibre/utils/ui_helper.dart' as ui_helper; @@ -308,32 +309,7 @@ class EditBangScreen extends HookConsumerWidget { label: const Text('Delete'), icon: const Icon(Icons.delete), onPressed: () async { - final result = await showDialog( - context: context, - builder: (BuildContext context) { - return AlertDialog( - icon: const Icon(Icons.warning), - title: const Text('Delete Bang'), - content: const Text( - 'Are you sure you want to delete this Bang?', - ), - actions: [ - TextButton( - onPressed: () { - Navigator.pop(context, false); - }, - child: const Text('Cancel'), - ), - TextButton( - onPressed: () { - Navigator.pop(context, true); - }, - child: const Text('Delete'), - ), - ], - ); - }, - ); + final result = await showDeleteBangDialog(context); if (result == true) { await ref diff --git a/app/lib/features/geckoview/features/bookmarks/presentation/dialogs/delete_bookmark_dialog.dart b/app/lib/features/geckoview/features/bookmarks/presentation/dialogs/delete_bookmark_dialog.dart new file mode 100644 index 00000000..25fbc9c5 --- /dev/null +++ b/app/lib/features/geckoview/features/bookmarks/presentation/dialogs/delete_bookmark_dialog.dart @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2024-2025 Fabian Freund. + * + * This file is part of WebLibre + * (see https://weblibre.eu). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +import 'package:flutter/material.dart'; + +Future showDeleteBookmarkDialog(BuildContext context) { + return showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + icon: const Icon(Icons.warning), + title: const Text('Delete Bookmark'), + content: const Text( + 'Are you sure you want to delete this Bookmark?', + ), + actions: [ + TextButton( + onPressed: () { + Navigator.pop(context, false); + }, + child: const Text('Cancel'), + ), + TextButton( + onPressed: () { + Navigator.pop(context, true); + }, + child: const Text('Delete'), + ), + ], + ); + }, + ); +} diff --git a/app/lib/features/geckoview/features/bookmarks/presentation/dialogs/delete_folder_dialog.dart b/app/lib/features/geckoview/features/bookmarks/presentation/dialogs/delete_folder_dialog.dart new file mode 100644 index 00000000..071cd85d --- /dev/null +++ b/app/lib/features/geckoview/features/bookmarks/presentation/dialogs/delete_folder_dialog.dart @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2024-2025 Fabian Freund. + * + * This file is part of WebLibre + * (see https://weblibre.eu). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +import 'package:flutter/material.dart'; + +Future showDeleteFolderDialog(BuildContext context) { + return showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + icon: const Icon(Icons.warning), + title: const Text('Delete Folder'), + content: const Text( + 'Are you sure you want to delete this Folder including all bookmarks?', + ), + actions: [ + TextButton( + onPressed: () { + Navigator.pop(context, false); + }, + child: const Text('Cancel'), + ), + TextButton( + onPressed: () { + Navigator.pop(context, true); + }, + child: const Text('Delete'), + ), + ], + ); + }, + ); +} diff --git a/app/lib/features/geckoview/features/bookmarks/presentation/screens/bookmark_entry_edit.dart b/app/lib/features/geckoview/features/bookmarks/presentation/screens/bookmark_entry_edit.dart index 2fdde78c..2f042e65 100644 --- a/app/lib/features/geckoview/features/bookmarks/presentation/screens/bookmark_entry_edit.dart +++ b/app/lib/features/geckoview/features/bookmarks/presentation/screens/bookmark_entry_edit.dart @@ -28,6 +28,7 @@ import 'package:weblibre/core/routing/routes.dart'; import 'package:weblibre/features/geckoview/features/bookmarks/domain/entities/bookmark_item.dart'; import 'package:weblibre/features/geckoview/features/bookmarks/domain/providers/bookmarks.dart'; import 'package:weblibre/features/geckoview/features/bookmarks/domain/repositories/bookmarks.dart'; +import 'package:weblibre/features/geckoview/features/bookmarks/presentation/dialogs/delete_bookmark_dialog.dart'; import 'package:weblibre/presentation/widgets/failure_widget.dart'; import 'package:weblibre/utils/form_validators.dart'; import 'package:weblibre/utils/uri_parser.dart' as uri_parser; @@ -264,32 +265,7 @@ class BookmarkEntryEditScreen extends HookConsumerWidget { label: const Text('Delete'), icon: const Icon(MdiIcons.bookmarkRemove), onPressed: () async { - final result = await showDialog( - context: context, - builder: (BuildContext context) { - return AlertDialog( - icon: const Icon(Icons.warning), - title: const Text('Delete Bookmark'), - content: const Text( - 'Are you sure you want to delete this Bookmark?', - ), - actions: [ - TextButton( - onPressed: () { - Navigator.pop(context, false); - }, - child: const Text('Cancel'), - ), - TextButton( - onPressed: () { - Navigator.pop(context, true); - }, - child: const Text('Delete'), - ), - ], - ); - }, - ); + final result = await showDeleteBookmarkDialog(context); if (result == true) { await ref diff --git a/app/lib/features/geckoview/features/bookmarks/presentation/screens/bookmark_folder_edit.dart b/app/lib/features/geckoview/features/bookmarks/presentation/screens/bookmark_folder_edit.dart index 73f426c6..17ca538f 100644 --- a/app/lib/features/geckoview/features/bookmarks/presentation/screens/bookmark_folder_edit.dart +++ b/app/lib/features/geckoview/features/bookmarks/presentation/screens/bookmark_folder_edit.dart @@ -24,6 +24,7 @@ import 'package:go_router/go_router.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:weblibre/features/geckoview/features/bookmarks/domain/entities/bookmark_item.dart'; import 'package:weblibre/features/geckoview/features/bookmarks/domain/repositories/bookmarks.dart'; +import 'package:weblibre/features/geckoview/features/bookmarks/presentation/dialogs/delete_folder_dialog.dart'; import 'package:weblibre/utils/form_validators.dart'; class BookmarkFolderEditScreen extends HookConsumerWidget { @@ -104,32 +105,7 @@ class BookmarkFolderEditScreen extends HookConsumerWidget { label: const Text('Delete'), icon: const Icon(Icons.delete), onPressed: () async { - final result = await showDialog( - context: context, - builder: (BuildContext context) { - return AlertDialog( - icon: const Icon(Icons.warning), - title: const Text('Delete Folder'), - content: const Text( - 'Are you sure you want to delete this Folder including all bookmarks?', - ), - actions: [ - TextButton( - onPressed: () { - Navigator.pop(context, false); - }, - child: const Text('Cancel'), - ), - TextButton( - onPressed: () { - Navigator.pop(context, true); - }, - child: const Text('Delete'), - ), - ], - ); - }, - ); + final result = await showDeleteFolderDialog(context); if (result == true) { await ref diff --git a/app/lib/features/geckoview/features/browser/presentation/dialogs/content_selection_dialog.dart b/app/lib/features/geckoview/features/browser/presentation/dialogs/content_selection_dialog.dart new file mode 100644 index 00000000..10fd9c27 --- /dev/null +++ b/app/lib/features/geckoview/features/browser/presentation/dialogs/content_selection_dialog.dart @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2024-2025 Fabian Freund. + * + * This file is part of WebLibre + * (see https://weblibre.eu). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +import 'package:flutter/material.dart'; +import 'package:weblibre/features/geckoview/features/tabs/data/database/definitions.drift.dart'; + +/// Dialog to select between extracted or full content for sharing. +/// Shows options for extracted (reader-optimized) vs full (complete) content. +Future showContentSelectionDialog( + BuildContext context, { + required Widget title, + required TabData tabData, + required Future Function(String content, String? fileName) + shareMarkdownAction, +}) async { + await showDialog( + context: context, + builder: (context) => SimpleDialog( + title: title, + children: [ + ListTile( + title: const Text('Extracted Content'), + subtitle: const Text( + 'Reader-optimized content without navigation and ads', + ), + onTap: () async { + Navigator.of(context).pop(); + await shareMarkdownAction( + tabData.extractedContentMarkdown!, + tabData.title ?? tabData.url?.authority, + ); + }, + ), + ListTile( + title: const Text('Full Content'), + subtitle: const Text( + 'Complete page including all elements and structure', + ), + onTap: () async { + Navigator.of(context).pop(); + await shareMarkdownAction( + tabData.fullContentMarkdown!, + tabData.title ?? tabData.url?.authority, + ); + }, + ), + ], + ), + ); +} diff --git a/app/lib/features/geckoview/features/browser/presentation/widgets/menu_item_buttons.dart b/app/lib/features/geckoview/features/browser/presentation/widgets/menu_item_buttons.dart index 62c69de4..174f77d0 100644 --- a/app/lib/features/geckoview/features/browser/presentation/widgets/menu_item_buttons.dart +++ b/app/lib/features/geckoview/features/browser/presentation/widgets/menu_item_buttons.dart @@ -8,6 +8,7 @@ import 'package:nullability/nullability.dart'; import 'package:share_plus/share_plus.dart'; import 'package:weblibre/features/geckoview/domain/providers/tab_session.dart'; import 'package:weblibre/features/geckoview/domain/providers/tab_state.dart'; +import 'package:weblibre/features/geckoview/features/browser/presentation/dialogs/content_selection_dialog.dart'; import 'package:weblibre/features/geckoview/features/browser/presentation/dialogs/qr_code.dart'; import 'package:weblibre/features/geckoview/features/tabs/data/database/definitions.drift.dart'; import 'package:weblibre/features/geckoview/features/tabs/domain/repositories/tab.dart'; @@ -144,39 +145,11 @@ class ShareMarkdownActionMenuItemButton extends HookConsumerWidget { BuildContext context, TabData tabData, ) async { - await showDialog( - context: context, - builder: (context) => SimpleDialog( - title: title, - children: [ - ListTile( - title: const Text('Extracted Content'), - subtitle: const Text( - 'Reader-optimized content without navigation and ads', - ), - onTap: () async { - Navigator.of(context).pop(); - await shareMarkdownAction( - tabData.extractedContentMarkdown!, - tabData.title ?? tabData.url?.authority, - ); - }, - ), - ListTile( - title: const Text('Full Content'), - subtitle: const Text( - 'Complete page including all elements and structure', - ), - onTap: () async { - Navigator.of(context).pop(); - await shareMarkdownAction( - tabData.fullContentMarkdown!, - tabData.title ?? tabData.url?.authority, - ); - }, - ), - ], - ), + await showContentSelectionDialog( + context, + title: title, + tabData: tabData, + shareMarkdownAction: shareMarkdownAction, ); } } diff --git a/app/lib/features/geckoview/features/browser/presentation/widgets/tab_view/dialogs/clear_container_data_dialog.dart b/app/lib/features/geckoview/features/browser/presentation/widgets/tab_view/dialogs/clear_container_data_dialog.dart new file mode 100644 index 00000000..a13b57c1 --- /dev/null +++ b/app/lib/features/geckoview/features/browser/presentation/widgets/tab_view/dialogs/clear_container_data_dialog.dart @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2024-2025 Fabian Freund. + * + * This file is part of WebLibre + * (see https://weblibre.eu). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +import 'package:flutter/material.dart'; +import 'package:flutter_material_design_icons/flutter_material_design_icons.dart'; + +Future showClearContainerDataDialog( + BuildContext context, + int tabCount, +) { + return showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + icon: const Icon( + MdiIcons.databaseRemove, + ), + title: const Text( + 'Clear Container Data?', + ), + content: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text( + 'This will clear all data for this container:', + ), + const SizedBox(height: 8), + const Text('• Cookies'), + const Text('• Site data'), + const Text('• Cache'), + const Text('• Permissions'), + const SizedBox(height: 8), + Text( + '$tabCount tab(s) will be closed and reopened fresh.', + style: TextStyle( + fontWeight: FontWeight.bold, + color: Theme.of( + context, + ).colorScheme.tertiary, + ), + ), + ], + ), + actions: [ + TextButton( + onPressed: () { + Navigator.pop(context, false); + }, + child: const Text('Cancel'), + ), + TextButton( + onPressed: () { + Navigator.pop(context, true); + }, + child: const Text('Clear Data'), + ), + ], + ); + }, + ); +} diff --git a/app/lib/features/geckoview/features/browser/presentation/widgets/tab_view/dialogs/close_all_private_tabs_dialog.dart b/app/lib/features/geckoview/features/browser/presentation/widgets/tab_view/dialogs/close_all_private_tabs_dialog.dart new file mode 100644 index 00000000..0dc344bb --- /dev/null +++ b/app/lib/features/geckoview/features/browser/presentation/widgets/tab_view/dialogs/close_all_private_tabs_dialog.dart @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2024-2025 Fabian Freund. + * + * This file is part of WebLibre + * (see https://weblibre.eu). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +import 'package:flutter/material.dart'; + +Future showCloseAllPrivateTabsDialog(BuildContext context) { + return showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + icon: const Icon(Icons.warning), + title: const Text('Close All Private Tabs'), + content: const Text( + 'Are you sure you want to close all displayed private tabs?', + ), + actions: [ + TextButton( + onPressed: () { + Navigator.pop(context, false); + }, + child: const Text('Cancel'), + ), + TextButton( + onPressed: () { + Navigator.pop(context, true); + }, + child: const Text('Close'), + ), + ], + ); + }, + ); +} diff --git a/app/lib/features/geckoview/features/browser/presentation/widgets/tab_view/dialogs/close_all_tabs_dialog.dart b/app/lib/features/geckoview/features/browser/presentation/widgets/tab_view/dialogs/close_all_tabs_dialog.dart new file mode 100644 index 00000000..8cc5189d --- /dev/null +++ b/app/lib/features/geckoview/features/browser/presentation/widgets/tab_view/dialogs/close_all_tabs_dialog.dart @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2024-2025 Fabian Freund. + * + * This file is part of WebLibre + * (see https://weblibre.eu). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +import 'package:flutter/material.dart'; + +Future showCloseAllTabsDialog(BuildContext context) { + return showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + icon: const Icon(Icons.warning), + title: const Text('Close All Tabs'), + content: const Text( + 'Are you sure you want to close all displayed tabs?', + ), + actions: [ + TextButton( + onPressed: () { + Navigator.pop(context, false); + }, + child: const Text('Cancel'), + ), + TextButton( + onPressed: () { + Navigator.pop(context, true); + }, + child: const Text('Close'), + ), + ], + ); + }, + ); +} diff --git a/app/lib/features/geckoview/features/browser/presentation/widgets/tab_view/dialogs/enable_ai_tab_suggestions_dialog.dart b/app/lib/features/geckoview/features/browser/presentation/widgets/tab_view/dialogs/enable_ai_tab_suggestions_dialog.dart new file mode 100644 index 00000000..7a1592d4 --- /dev/null +++ b/app/lib/features/geckoview/features/browser/presentation/widgets/tab_view/dialogs/enable_ai_tab_suggestions_dialog.dart @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2024-2025 Fabian Freund. + * + * This file is part of WebLibre + * (see https://weblibre.eu). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +import 'package:flutter/material.dart'; +import 'package:flutter_material_design_icons/flutter_material_design_icons.dart'; + +Future showEnableAiTabSuggestionsDialog(BuildContext context) { + return showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + icon: const Icon(MdiIcons.download), + title: const Text( + 'Enable AI Tab Suggestions', + ), + content: const Text( + 'Enabling this feature may require downloading AI models. ' + 'The download size and progress cannot be determined in advance.\n\n' + 'Do you want to continue?', + ), + actions: [ + TextButton( + onPressed: () { + Navigator.pop(context, false); + }, + child: const Text('Cancel'), + ), + TextButton( + onPressed: () { + Navigator.pop(context, true); + }, + child: const Text('Enable'), + ), + ], + ); + }, + ); +} diff --git a/app/lib/features/geckoview/features/browser/presentation/widgets/tab_view/tab_view_header.dart b/app/lib/features/geckoview/features/browser/presentation/widgets/tab_view/tab_view_header.dart index 52587d57..b04a297d 100644 --- a/app/lib/features/geckoview/features/browser/presentation/widgets/tab_view/tab_view_header.dart +++ b/app/lib/features/geckoview/features/browser/presentation/widgets/tab_view/tab_view_header.dart @@ -11,6 +11,10 @@ import 'package:weblibre/features/geckoview/domain/providers.dart'; import 'package:weblibre/features/geckoview/domain/repositories/tab.dart'; import 'package:weblibre/features/geckoview/features/browser/domain/services/browser_data.dart'; import 'package:weblibre/features/geckoview/features/browser/presentation/controllers/tab_view_controllers.dart'; +import 'package:weblibre/features/geckoview/features/browser/presentation/widgets/tab_view/dialogs/clear_container_data_dialog.dart'; +import 'package:weblibre/features/geckoview/features/browser/presentation/widgets/tab_view/dialogs/close_all_private_tabs_dialog.dart'; +import 'package:weblibre/features/geckoview/features/browser/presentation/widgets/tab_view/dialogs/close_all_tabs_dialog.dart'; +import 'package:weblibre/features/geckoview/features/browser/presentation/widgets/tab_view/dialogs/enable_ai_tab_suggestions_dialog.dart'; import 'package:weblibre/features/geckoview/features/tabs/domain/providers/selected_container.dart'; import 'package:weblibre/features/geckoview/features/tabs/domain/repositories/tab.dart'; import 'package:weblibre/features/geckoview/features/tabs/domain/repositories/tab_search.dart'; @@ -172,36 +176,7 @@ class TabViewHeader extends HookConsumerWidget { : 'Enable AI tab suggestions', onPressed: () async { if (!tabSuggestionsEnabled) { - final result = await showDialog( - context: context, - builder: (BuildContext context) { - return AlertDialog( - icon: const Icon(MdiIcons.download), - title: const Text( - 'Enable AI Tab Suggestions', - ), - content: const Text( - 'Enabling this feature may require downloading AI models. ' - 'The download size and progress cannot be determined in advance.\n\n' - 'Do you want to continue?', - ), - actions: [ - TextButton( - onPressed: () { - Navigator.pop(context, false); - }, - child: const Text('Cancel'), - ), - TextButton( - onPressed: () { - Navigator.pop(context, true); - }, - child: const Text('Enable'), - ), - ], - ); - }, - ); + final result = await showEnableAiTabSuggestionsDialog(context); if (result == true) { ref @@ -300,32 +275,7 @@ class TabViewHeader extends HookConsumerWidget { leadingIcon: const Icon(MdiIcons.closeCircle), child: const Text('Close All Tabs'), onPressed: () async { - final result = await showDialog( - context: context, - builder: (BuildContext context) { - return AlertDialog( - icon: const Icon(Icons.warning), - title: const Text('Close All Tabs'), - content: const Text( - 'Are you sure you want to close all displayed tabs?', - ), - actions: [ - TextButton( - onPressed: () { - Navigator.pop(context, false); - }, - child: const Text('Cancel'), - ), - TextButton( - onPressed: () { - Navigator.pop(context, true); - }, - child: const Text('Close'), - ), - ], - ); - }, - ); + final result = await showCloseAllTabsDialog(context); if (result == true) { final container = ref.read( @@ -352,32 +302,7 @@ class TabViewHeader extends HookConsumerWidget { leadingIcon: const Icon(MdiIcons.incognitoCircleOff), child: const Text('Close Private Tabs'), onPressed: () async { - final result = await showDialog( - context: context, - builder: (BuildContext context) { - return AlertDialog( - icon: const Icon(Icons.warning), - title: const Text('Close All Private Tabs'), - content: const Text( - 'Are you sure you want to close all displayed private tabs?', - ), - actions: [ - TextButton( - onPressed: () { - Navigator.pop(context, false); - }, - child: const Text('Cancel'), - ), - TextButton( - onPressed: () { - Navigator.pop(context, true); - }, - child: const Text('Close'), - ), - ], - ); - }, - ); + final result = await showCloseAllPrivateTabsDialog(context); if (result == true) { final container = ref.read( @@ -439,57 +364,9 @@ class TabViewHeader extends HookConsumerWidget { if (!context.mounted) return; - final result = await showDialog( - context: context, - builder: (BuildContext context) { - return AlertDialog( - icon: const Icon( - MdiIcons.databaseRemove, - ), - title: const Text( - 'Clear Container Data?', - ), - content: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: - CrossAxisAlignment.start, - children: [ - const Text( - 'This will clear all data for this container:', - ), - const SizedBox(height: 8), - const Text('• Cookies'), - const Text('• Site data'), - const Text('• Cache'), - const Text('• Permissions'), - const SizedBox(height: 8), - Text( - '${tabs.length} tab(s) will be closed and reopened fresh.', - style: TextStyle( - fontWeight: FontWeight.bold, - color: Theme.of( - context, - ).colorScheme.tertiary, - ), - ), - ], - ), - actions: [ - TextButton( - onPressed: () { - Navigator.pop(context, false); - }, - child: const Text('Cancel'), - ), - TextButton( - onPressed: () { - Navigator.pop(context, true); - }, - child: const Text('Clear Data'), - ), - ], - ); - }, + final result = await showClearContainerDataDialog( + context, + tabs.length, ); if (result == true) { diff --git a/app/lib/features/geckoview/features/tabs/presentation/dialogs/delete_container_dialog.dart b/app/lib/features/geckoview/features/tabs/presentation/dialogs/delete_container_dialog.dart new file mode 100644 index 00000000..83197ab0 --- /dev/null +++ b/app/lib/features/geckoview/features/tabs/presentation/dialogs/delete_container_dialog.dart @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2024-2025 Fabian Freund. + * + * This file is part of WebLibre + * (see https://weblibre.eu). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +import 'package:flutter/material.dart'; + +/// Dialog to confirm container deletion. +/// Returns true if user confirms deletion, false if cancelled, null if dismissed. +Future showDeleteContainerDialog(BuildContext context) { + return showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + icon: const Icon(Icons.warning), + title: const Text('Delete Container'), + content: const Text( + 'Are you sure you want to delete this container and close all attached tabs?', + ), + actions: [ + TextButton( + onPressed: () { + Navigator.pop(context, false); + }, + child: const Text('Cancel'), + ), + TextButton( + onPressed: () { + Navigator.pop(context, true); + }, + child: const Text('Delete'), + ), + ], + ); + }, + ); +} diff --git a/app/lib/features/geckoview/features/tabs/presentation/screens/container_edit.dart b/app/lib/features/geckoview/features/tabs/presentation/screens/container_edit.dart index 3fb9aba4..5becf9f7 100644 --- a/app/lib/features/geckoview/features/tabs/presentation/screens/container_edit.dart +++ b/app/lib/features/geckoview/features/tabs/presentation/screens/container_edit.dart @@ -28,6 +28,7 @@ import 'package:weblibre/core/uuid.dart'; import 'package:weblibre/features/geckoview/features/tabs/data/models/container_data.dart'; import 'package:weblibre/features/geckoview/features/tabs/domain/repositories/container.dart'; import 'package:weblibre/features/geckoview/features/tabs/presentation/controllers/container_topic.dart'; +import 'package:weblibre/features/geckoview/features/tabs/presentation/dialogs/delete_container_dialog.dart'; import 'package:weblibre/features/geckoview/features/tabs/presentation/screens/container_sites.dart'; import 'package:weblibre/features/geckoview/features/tabs/presentation/widgets/color_picker_dialog.dart'; import 'package:weblibre/features/user/domain/services/local_authentication.dart'; @@ -358,32 +359,7 @@ class ContainerEditScreen extends HookConsumerWidget { label: const Text('Delete'), icon: const Icon(Icons.delete), onPressed: () async { - final result = await showDialog( - context: context, - builder: (BuildContext context) { - return AlertDialog( - icon: const Icon(Icons.warning), - title: const Text('Delete Container'), - content: const Text( - 'Are you sure you want to delete this container and close all attached tabs?', - ), - actions: [ - TextButton( - onPressed: () { - Navigator.pop(context, false); - }, - child: const Text('Cancel'), - ), - TextButton( - onPressed: () { - Navigator.pop(context, true); - }, - child: const Text('Delete'), - ), - ], - ); - }, - ); + final result = await showDeleteContainerDialog(context); if (result == true) { await ref diff --git a/app/lib/features/settings/presentation/dialogs/user_agent_restart_dialog.dart b/app/lib/features/settings/presentation/dialogs/user_agent_restart_dialog.dart new file mode 100644 index 00000000..ef53faf3 --- /dev/null +++ b/app/lib/features/settings/presentation/dialogs/user_agent_restart_dialog.dart @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2024-2025 Fabian Freund. + * + * This file is part of WebLibre + * (see https://weblibre.eu). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; + +Future showUserAgentRestartDialog(BuildContext context) { + return showDialog( + context: context, + builder: (context) => AlertDialog( + icon: const Icon(Icons.warning), + title: const Text('User Agent Changed'), + content: const Text( + 'The Browser needs to get restarted for the new user agent to take effect', + ), + actions: [ + TextButton( + onPressed: () { + context.pop(false); + }, + child: const Text('Later'), + ), + TextButton( + onPressed: () { + context.pop(true); + }, + child: const Text('Restart Now'), + ), + ], + ), + ); +} diff --git a/app/lib/features/settings/presentation/screens/developer_settings.dart b/app/lib/features/settings/presentation/screens/developer_settings.dart index 7f0db1ad..14d3ea55 100644 --- a/app/lib/features/settings/presentation/screens/developer_settings.dart +++ b/app/lib/features/settings/presentation/screens/developer_settings.dart @@ -25,13 +25,13 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_material_design_icons/flutter_material_design_icons.dart'; -import 'package:go_router/go_router.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:weblibre/core/logger.dart'; import 'package:weblibre/core/providers/app_state.dart'; import 'package:weblibre/core/routing/routes.dart'; import 'package:weblibre/features/settings/presentation/controllers/save_settings.dart'; +import 'package:weblibre/features/settings/presentation/dialogs/user_agent_restart_dialog.dart'; import 'package:weblibre/features/settings/presentation/widgets/custom_list_tile.dart'; import 'package:weblibre/features/user/data/models/engine_settings.dart'; import 'package:weblibre/features/user/domain/repositories/engine_settings.dart'; @@ -93,30 +93,7 @@ class DeveloperSettingsScreen extends HookConsumerWidget { ); if (context.mounted) { - final restart = await showDialog( - context: context, - builder: (context) => AlertDialog( - icon: const Icon(Icons.warning), - title: const Text('User Agent Changed'), - content: const Text( - 'The Browser needs to get restarted for the new user agent to take effect', - ), - actions: [ - TextButton( - onPressed: () { - context.pop(false); - }, - child: const Text('Later'), - ), - TextButton( - onPressed: () { - context.pop(true); - }, - child: const Text('Restart Now'), - ), - ], - ), - ); + final restart = await showUserAgentRestartDialog(context); if (restart == true) { await exitApp(ref.container); diff --git a/app/lib/features/user/domain/presentation/dialogs/delete_profile_dialog.dart b/app/lib/features/user/domain/presentation/dialogs/delete_profile_dialog.dart new file mode 100644 index 00000000..c157e1f6 --- /dev/null +++ b/app/lib/features/user/domain/presentation/dialogs/delete_profile_dialog.dart @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2024-2025 Fabian Freund. + * + * This file is part of WebLibre + * (see https://weblibre.eu). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +import 'package:flutter/material.dart'; + +/// Dialog to confirm profile/user deletion. +/// Returns true if user confirms deletion, false if cancelled, null if dismissed. +Future showDeleteProfileDialog(BuildContext context) { + return showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + icon: const Icon(Icons.warning), + title: const Text('Delete User'), + content: const Text( + 'Are you sure you want to delete this User including all data?', + ), + actions: [ + TextButton( + onPressed: () { + Navigator.pop(context, false); + }, + child: const Text('Cancel'), + ), + TextButton( + onPressed: () { + Navigator.pop(context, true); + }, + child: const Text('Delete'), + ), + ], + ); + }, + ); +} diff --git a/app/lib/features/user/domain/presentation/dialogs/override_profile_dialog.dart b/app/lib/features/user/domain/presentation/dialogs/override_profile_dialog.dart new file mode 100644 index 00000000..bad42f56 --- /dev/null +++ b/app/lib/features/user/domain/presentation/dialogs/override_profile_dialog.dart @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2024-2025 Fabian Freund. + * + * This file is part of WebLibre + * (see https://weblibre.eu). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +import 'package:flutter/material.dart'; + +/// Dialog to confirm overriding existing profile during restore. +/// Returns true if user confirms override, false if cancelled, null if dismissed. +Future showOverrideProfileDialog(BuildContext context) { + return showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + icon: const Icon(Icons.warning), + title: const Text('Override User'), + content: const Text( + 'Are you sure you want to override the exisiting User?', + ), + actions: [ + TextButton( + onPressed: () { + Navigator.pop(context, false); + }, + child: const Text('Cancel'), + ), + TextButton( + onPressed: () { + Navigator.pop(context, true); + }, + child: const Text('Override'), + ), + ], + ); + }, + ); +} diff --git a/app/lib/features/user/domain/presentation/dialogs/password_confirmation_dialog.dart b/app/lib/features/user/domain/presentation/dialogs/password_confirmation_dialog.dart new file mode 100644 index 00000000..a1f21000 --- /dev/null +++ b/app/lib/features/user/domain/presentation/dialogs/password_confirmation_dialog.dart @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2024-2025 Fabian Freund. + * + * This file is part of WebLibre + * (see https://weblibre.eu). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +import 'package:flutter/material.dart'; + +/// Dialog to confirm password during backup creation. +/// Returns the entered password string if confirmed, null if cancelled or dismissed. +Future showPasswordConfirmationDialog(BuildContext context) { + return showDialog( + context: context, + builder: (context) { + final controller = TextEditingController(); + + return AlertDialog( + title: const Text('Password Confirmation'), + content: TextField( + controller: controller, + enableSuggestions: false, + autocorrect: false, + enableIMEPersonalizedLearning: false, + keyboardType: TextInputType.visiblePassword, + obscureText: true, + decoration: const InputDecoration( + labelText: 'Password', + floatingLabelBehavior: FloatingLabelBehavior.always, + ), + ), + actions: [ + TextButton( + onPressed: () { + Navigator.of(context).pop(); + }, + child: const Text('Cancel'), + ), + TextButton( + onPressed: () { + Navigator.of(context).pop(controller.text); + }, + child: const Text('Confirm'), + ), + ], + ); + }, + ); +} diff --git a/app/lib/features/user/domain/presentation/screens/profile_backup.dart b/app/lib/features/user/domain/presentation/screens/profile_backup.dart index c01465bc..4640cc02 100644 --- a/app/lib/features/user/domain/presentation/screens/profile_backup.dart +++ b/app/lib/features/user/domain/presentation/screens/profile_backup.dart @@ -5,6 +5,7 @@ import 'package:flutter_material_design_icons/flutter_material_design_icons.dart import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:weblibre/core/routing/routes.dart'; import 'package:weblibre/domain/entities/profile.dart'; +import 'package:weblibre/features/user/domain/presentation/dialogs/password_confirmation_dialog.dart'; import 'package:weblibre/features/user/domain/services/user_backup.dart'; import 'package:weblibre/utils/ui_helper.dart'; @@ -120,43 +121,7 @@ class ProfileBackupScreen extends HookConsumerWidget { onPressed: () async { if (formKey.currentState?.validate() ?? false) { if (!skipPasswordConfirmation.value) { - final confirmation = await showDialog( - context: context, - builder: (context) { - final controller = TextEditingController(); - - return AlertDialog( - title: const Text('Password Confirmation'), - content: TextField( - controller: controller, - enableSuggestions: false, - autocorrect: false, - enableIMEPersonalizedLearning: false, - keyboardType: TextInputType.visiblePassword, - obscureText: true, - decoration: const InputDecoration( - labelText: 'Password', - floatingLabelBehavior: - FloatingLabelBehavior.always, - ), - ), - actions: [ - TextButton( - onPressed: () { - Navigator.of(context).pop(); - }, - child: const Text('Cancel'), - ), - TextButton( - onPressed: () { - Navigator.of(context).pop(controller.text); - }, - child: const Text('Confirm'), - ), - ], - ); - }, - ); + final confirmation = await showPasswordConfirmationDialog(context); if (confirmation != passwordTextController.text) { if (context.mounted) { diff --git a/app/lib/features/user/domain/presentation/screens/profile_edit.dart b/app/lib/features/user/domain/presentation/screens/profile_edit.dart index 4e75b179..a22024f2 100644 --- a/app/lib/features/user/domain/presentation/screens/profile_edit.dart +++ b/app/lib/features/user/domain/presentation/screens/profile_edit.dart @@ -27,6 +27,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:weblibre/core/filesystem.dart'; import 'package:weblibre/core/routing/routes.dart'; import 'package:weblibre/domain/entities/profile.dart'; +import 'package:weblibre/features/user/domain/presentation/dialogs/delete_profile_dialog.dart'; import 'package:weblibre/features/user/domain/presentation/utils/profile_switch_handler.dart'; import 'package:weblibre/features/user/domain/repositories/profile.dart'; import 'package:weblibre/utils/form_validators.dart'; @@ -129,32 +130,7 @@ class ProfileEditScreen extends HookConsumerWidget { label: const Text('Delete'), icon: const Icon(Icons.delete), onPressed: () async { - final result = await showDialog( - context: context, - builder: (BuildContext context) { - return AlertDialog( - icon: const Icon(Icons.warning), - title: const Text('Delete User'), - content: const Text( - 'Are you sure you want to delete this User including all data?', - ), - actions: [ - TextButton( - onPressed: () { - Navigator.pop(context, false); - }, - child: const Text('Cancel'), - ), - TextButton( - onPressed: () { - Navigator.pop(context, true); - }, - child: const Text('Delete'), - ), - ], - ); - }, - ); + final result = await showDeleteProfileDialog(context); if (result == true) { await ref diff --git a/app/lib/features/user/domain/presentation/screens/profile_restore.dart b/app/lib/features/user/domain/presentation/screens/profile_restore.dart index 2c90f223..a1dea0a3 100644 --- a/app/lib/features/user/domain/presentation/screens/profile_restore.dart +++ b/app/lib/features/user/domain/presentation/screens/profile_restore.dart @@ -5,6 +5,7 @@ import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_material_design_icons/flutter_material_design_icons.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:weblibre/core/routing/routes.dart'; +import 'package:weblibre/features/user/domain/presentation/dialogs/override_profile_dialog.dart'; import 'package:weblibre/features/user/domain/services/user_backup.dart'; import 'package:weblibre/utils/form_validators.dart'; import 'package:weblibre/utils/ui_helper.dart'; @@ -129,32 +130,7 @@ class ProfileRestoreScreen extends HookConsumerWidget { password: passwordTextController.text, confirmOverrideCallback: () { if (context.mounted) { - return showDialog( - context: context, - builder: (BuildContext context) { - return AlertDialog( - icon: const Icon(Icons.warning), - title: const Text('Override User'), - content: const Text( - 'Are you sure you want to override the exisiting User?', - ), - actions: [ - TextButton( - onPressed: () { - Navigator.pop(context, false); - }, - child: const Text('Cancel'), - ), - TextButton( - onPressed: () { - Navigator.pop(context, true); - }, - child: const Text('Override'), - ), - ], - ); - }, - ); + return showOverrideProfileDialog(context); } else { throw Exception('Override failed'); } diff --git a/app/lib/features/web_feed/presentation/dialogs/delete_feed_dialog.dart b/app/lib/features/web_feed/presentation/dialogs/delete_feed_dialog.dart new file mode 100644 index 00000000..e197be9e --- /dev/null +++ b/app/lib/features/web_feed/presentation/dialogs/delete_feed_dialog.dart @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2024-2025 Fabian Freund. + * + * This file is part of WebLibre + * (see https://weblibre.eu). + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +import 'package:flutter/material.dart'; + +Future showDeleteFeedDialog(BuildContext context) { + return showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + icon: const Icon(Icons.warning), + title: const Text('Delete Feed'), + content: const Text( + 'Are you sure you want to delete this feed and delete all related articles?', + ), + actions: [ + TextButton( + onPressed: () { + Navigator.pop(context, false); + }, + child: const Text('Cancel'), + ), + TextButton( + onPressed: () { + Navigator.pop(context, true); + }, + child: const Text('Delete'), + ), + ], + ); + }, + ); +} diff --git a/app/lib/features/web_feed/presentation/screens/feed_edit.dart b/app/lib/features/web_feed/presentation/screens/feed_edit.dart index a869a935..774a9114 100644 --- a/app/lib/features/web_feed/presentation/screens/feed_edit.dart +++ b/app/lib/features/web_feed/presentation/screens/feed_edit.dart @@ -29,6 +29,7 @@ import 'package:weblibre/features/web_feed/data/database/definitions.drift.dart' import 'package:weblibre/features/web_feed/data/models/feed_category.dart'; import 'package:weblibre/features/web_feed/domain/providers.dart'; import 'package:weblibre/features/web_feed/domain/repositories/feed_repository.dart'; +import 'package:weblibre/features/web_feed/presentation/dialogs/delete_feed_dialog.dart'; import 'package:weblibre/features/web_feed/presentation/widgets/tag_field.dart'; import 'package:weblibre/presentation/widgets/failure_widget.dart'; import 'package:weblibre/presentation/widgets/url_icon.dart'; @@ -302,32 +303,7 @@ class _FeedEditContent extends HookConsumerWidget { label: const Text('Delete'), icon: const Icon(Icons.delete), onPressed: () async { - final result = await showDialog( - context: context, - builder: (BuildContext context) { - return AlertDialog( - icon: const Icon(Icons.warning), - title: const Text('Delete Feed'), - content: const Text( - 'Are you sure you want to delete this feed and delete all related articles?', - ), - actions: [ - TextButton( - onPressed: () { - Navigator.pop(context, false); - }, - child: const Text('Cancel'), - ), - TextButton( - onPressed: () { - Navigator.pop(context, true); - }, - child: const Text('Delete'), - ), - ], - ); - }, - ); + final result = await showDeleteFeedDialog(context); if (result == true) { await ref