bookmark all option dialog fast/detailed
This commit is contained in:
+54
@@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_material_design_icons/flutter_material_design_icons.dart';
|
||||||
|
|
||||||
|
enum BookmarkAllChoice { fast, detailed }
|
||||||
|
|
||||||
|
/// Dialog to select between fast (automatic) or detailed (one-by-one) bookmark import.
|
||||||
|
Future<BookmarkAllChoice?> showBookmarkAllDialog(BuildContext context) {
|
||||||
|
return showDialog<BookmarkAllChoice>(
|
||||||
|
context: context,
|
||||||
|
builder: (context) => SimpleDialog(
|
||||||
|
title: const Text('Bookmark All Tabs'),
|
||||||
|
children: [
|
||||||
|
ListTile(
|
||||||
|
leading: const Icon(MdiIcons.fastForward),
|
||||||
|
title: const Text('Fast'),
|
||||||
|
subtitle: const Text(
|
||||||
|
'Automatically add all tabs to a selected folder',
|
||||||
|
),
|
||||||
|
onTap: () {
|
||||||
|
Navigator.of(context).pop(BookmarkAllChoice.fast);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
ListTile(
|
||||||
|
leading: const Icon(MdiIcons.stepForward),
|
||||||
|
title: const Text('Detailed'),
|
||||||
|
subtitle: const Text('Review and edit each bookmark individually'),
|
||||||
|
onTap: () {
|
||||||
|
Navigator.of(context).pop(BookmarkAllChoice.detailed);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
+66
@@ -0,0 +1,66 @@
|
|||||||
|
/*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
|
import 'package:flutter_mozilla_components/flutter_mozilla_components.dart';
|
||||||
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:weblibre/features/geckoview/features/bookmarks/presentation/widgets/folder_tree_picker.dart';
|
||||||
|
|
||||||
|
/// Dialog to select a bookmark folder.
|
||||||
|
/// Returns the selected folder GUID or null if cancelled.
|
||||||
|
Future<String?> showSelectFolderDialog(BuildContext context) {
|
||||||
|
return showDialog<String>(
|
||||||
|
context: context,
|
||||||
|
builder: (context) => const _SelectFolderDialog(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
class _SelectFolderDialog extends HookConsumerWidget {
|
||||||
|
const _SelectFolderDialog();
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
final selectedFolderGuid = useState(BookmarkRoot.mobile.id);
|
||||||
|
|
||||||
|
return AlertDialog(
|
||||||
|
title: const Text('Select folder'),
|
||||||
|
content: SizedBox(
|
||||||
|
width: double.maxFinite,
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
child: FolderTreePicker(
|
||||||
|
selectedFolderGuid: selectedFolderGuid,
|
||||||
|
entryGuid: BookmarkRoot.root.id,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
actions: [
|
||||||
|
TextButton(
|
||||||
|
onPressed: () => Navigator.of(context).pop(),
|
||||||
|
child: const Text('Cancel'),
|
||||||
|
),
|
||||||
|
FilledButton(
|
||||||
|
onPressed: () => Navigator.of(context).pop(selectedFolderGuid.value),
|
||||||
|
child: const Text('Select'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
+43
-9
@@ -10,8 +10,11 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
|
|||||||
import 'package:weblibre/core/routing/routes.dart';
|
import 'package:weblibre/core/routing/routes.dart';
|
||||||
import 'package:weblibre/features/geckoview/domain/providers.dart';
|
import 'package:weblibre/features/geckoview/domain/providers.dart';
|
||||||
import 'package:weblibre/features/geckoview/domain/repositories/tab.dart';
|
import 'package:weblibre/features/geckoview/domain/repositories/tab.dart';
|
||||||
|
import 'package:weblibre/features/geckoview/features/bookmarks/domain/repositories/bookmarks.dart';
|
||||||
import 'package:weblibre/features/geckoview/features/browser/domain/services/browser_data.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/controllers/tab_view_controllers.dart';
|
||||||
|
import 'package:weblibre/features/geckoview/features/browser/presentation/dialogs/bookmark_all_dialog.dart';
|
||||||
|
import 'package:weblibre/features/geckoview/features/browser/presentation/dialogs/select_folder_dialog.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/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_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/close_all_tabs_dialog.dart';
|
||||||
@@ -252,6 +255,9 @@ class TabViewHeader extends HookConsumerWidget {
|
|||||||
leadingIcon: const Icon(MdiIcons.bookmarkPlusOutline),
|
leadingIcon: const Icon(MdiIcons.bookmarkPlusOutline),
|
||||||
child: const Text('Bookmark all'),
|
child: const Text('Bookmark all'),
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
|
final choice = await showBookmarkAllDialog(context);
|
||||||
|
if (choice == null || !context.mounted) return;
|
||||||
|
|
||||||
final containerId = ref.read(
|
final containerId = ref.read(
|
||||||
selectedContainerProvider,
|
selectedContainerProvider,
|
||||||
);
|
);
|
||||||
@@ -260,16 +266,44 @@ class TabViewHeader extends HookConsumerWidget {
|
|||||||
.read(tabDataRepositoryProvider.notifier)
|
.read(tabDataRepositoryProvider.notifier)
|
||||||
.getContainerTabsData(containerId);
|
.getContainerTabsData(containerId);
|
||||||
|
|
||||||
for (final tab in tabData) {
|
if (choice == BookmarkAllChoice.fast) {
|
||||||
|
if (!context.mounted) return;
|
||||||
|
final folderGuid = await showSelectFolderDialog(
|
||||||
|
context,
|
||||||
|
);
|
||||||
|
if (folderGuid == null) return;
|
||||||
|
|
||||||
|
final repo = ref.read(
|
||||||
|
bookmarksRepositoryProvider.notifier,
|
||||||
|
);
|
||||||
|
for (final tab in tabData) {
|
||||||
|
if (tab.url != null) {
|
||||||
|
await repo.addBookmark(
|
||||||
|
parentGuid: folderGuid,
|
||||||
|
url: tab.url!,
|
||||||
|
title: tab.title ?? tab.url.toString(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (context.mounted) {
|
if (context.mounted) {
|
||||||
await BookmarkEntryAddRoute(
|
ui_helper.showInfoMessage(
|
||||||
bookmarkInfo: jsonEncode(
|
context,
|
||||||
BookmarkInfo(
|
'${tabData.length} bookmark(s) added',
|
||||||
title: tab.title,
|
);
|
||||||
url: tab.url.toString(),
|
}
|
||||||
).encode(),
|
} else {
|
||||||
),
|
for (final tab in tabData) {
|
||||||
).push(context);
|
if (context.mounted) {
|
||||||
|
await BookmarkEntryAddRoute(
|
||||||
|
bookmarkInfo: jsonEncode(
|
||||||
|
BookmarkInfo(
|
||||||
|
title: tab.title,
|
||||||
|
url: tab.url.toString(),
|
||||||
|
).encode(),
|
||||||
|
),
|
||||||
|
).push(context);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user