diff --git a/app/lib/features/geckoview/features/bookmarks/presentation/dialogs/import_bookmarks_dialog.dart b/app/lib/features/geckoview/features/bookmarks/presentation/dialogs/import_bookmarks_dialog.dart new file mode 100644 index 00000000..988b9953 --- /dev/null +++ b/app/lib/features/geckoview/features/bookmarks/presentation/dialogs/import_bookmarks_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 showImportBookmarksDialog(BuildContext context) { + return showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + title: const Text('Import Bookmarks'), + content: const Text( + 'Do you want to erase all existing bookmarks before importing?\n\n' + 'Choose "Replace" to delete existing bookmarks, or "Merge" to keep them.', + ), + actions: [ + TextButton( + onPressed: () { + Navigator.pop(context, false); + }, + child: const Text('Merge'), + ), + TextButton( + onPressed: () { + Navigator.pop(context, true); + }, + child: const Text('Replace'), + ), + ], + ); + }, + ); +} diff --git a/app/lib/features/geckoview/features/bookmarks/presentation/screens/bookmark_list.dart b/app/lib/features/geckoview/features/bookmarks/presentation/screens/bookmark_list.dart index 27f32d89..49c39cd3 100644 --- a/app/lib/features/geckoview/features/bookmarks/presentation/screens/bookmark_list.dart +++ b/app/lib/features/geckoview/features/bookmarks/presentation/screens/bookmark_list.dart @@ -33,6 +33,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/import_bookmarks_dialog.dart'; import 'package:weblibre/presentation/hooks/listenable_callback.dart'; import 'package:weblibre/presentation/hooks/menu_controller.dart'; import 'package:weblibre/presentation/widgets/failure_widget.dart'; @@ -359,12 +360,18 @@ class BookmarkListScreen extends HookConsumerWidget { return; } + if (!context.mounted) return; + + // Ask user if they want to erase existing bookmarks + final shouldReplace = await showImportBookmarksDialog(context); + if (shouldReplace == null) return; // User cancelled dialog + final content = await File(file.path!).readAsString(); final repository = ref.read(bookmarksRepositoryProvider.notifier); final count = format == 'json' - ? await repository.importFromJSON(content) - : await repository.importFromHTML(content); + ? await repository.importFromJSON(content, replace: shouldReplace) + : await repository.importFromHTML(content, replace: shouldReplace); if (context.mounted) { showInfoMessage(context, 'Imported $count bookmarks successfully'); diff --git a/app/lib/features/geckoview/features/bookmarks/utils/bookmark_html_utils.dart b/app/lib/features/geckoview/features/bookmarks/utils/bookmark_html_utils.dart index 346b4568..73f5418a 100644 --- a/app/lib/features/geckoview/features/bookmarks/utils/bookmark_html_utils.dart +++ b/app/lib/features/geckoview/features/bookmarks/utils/bookmark_html_utils.dart @@ -387,7 +387,12 @@ class _BookmarkImporter { Future _importBookmarks() async { if (_isImportDefaults) { - await _service.eraseEverything(BookmarkRoot.root); + // Delete bookmarks from each root folder (except root itself to avoid errors) + for (final root in BookmarkRoot.values) { + if (root != BookmarkRoot.root) { + await _service.eraseEverything(root); + } + } } final bookmarkTrees = _getBookmarkTrees(); diff --git a/app/lib/features/geckoview/features/bookmarks/utils/bookmark_json_utils.dart b/app/lib/features/geckoview/features/bookmarks/utils/bookmark_json_utils.dart index 73874212..9d4ba1cc 100644 --- a/app/lib/features/geckoview/features/bookmarks/utils/bookmark_json_utils.dart +++ b/app/lib/features/geckoview/features/bookmarks/utils/bookmark_json_utils.dart @@ -64,7 +64,12 @@ class BookmarkJSONUtils { // If replacing, erase existing bookmarks first if (replace) { - await _service.eraseEverything(BookmarkRoot.root); + // Delete bookmarks from each root folder (except root itself to avoid errors) + for (final root in BookmarkRoot.values) { + if (root != BookmarkRoot.root) { + await _service.eraseEverything(root); + } + } } final folderIdToGuidMap = {};