refactor tab close dialog
This commit is contained in:
+3
-6
@@ -36,6 +36,7 @@ import 'package:weblibre/features/geckoview/features/browser/domain/entities/fon
|
||||
import 'package:weblibre/features/geckoview/features/browser/features/contextual_toolbar/domain/entities/toolbar_button_spec.dart';
|
||||
import 'package:weblibre/features/geckoview/features/browser/features/contextual_toolbar/presentation/models/contextual_toolbar_scope.dart';
|
||||
import 'package:weblibre/features/geckoview/features/browser/features/contextual_toolbar/presentation/widgets/contextual_bar_buttons.dart';
|
||||
import 'package:weblibre/features/geckoview/features/browser/presentation/utils/tab_close_confirmation.dart';
|
||||
import 'package:weblibre/features/geckoview/features/browser/presentation/widgets/extension_shortcut_menu.dart';
|
||||
import 'package:weblibre/features/geckoview/features/browser/presentation/widgets/font_size_bottom_sheet.dart';
|
||||
import 'package:weblibre/features/geckoview/features/browser/presentation/widgets/navigation_buttons.dart';
|
||||
@@ -542,9 +543,7 @@ class _CloseTabToolbarButton extends HookConsumerWidget {
|
||||
.where((id) => id != scope.selectedTabId)
|
||||
.toList();
|
||||
if (otherIds.isNotEmpty) {
|
||||
await ref
|
||||
.read(tabRepositoryProvider.notifier)
|
||||
.closeTabs(otherIds);
|
||||
await closeTabsWithConfirmation(context, ref, otherIds);
|
||||
}
|
||||
},
|
||||
child: const Text('Close Others'),
|
||||
@@ -559,9 +558,7 @@ class _CloseTabToolbarButton extends HookConsumerWidget {
|
||||
.map((e) => e.key)
|
||||
.toList();
|
||||
if (sameHostIds.isNotEmpty) {
|
||||
await ref
|
||||
.read(tabRepositoryProvider.notifier)
|
||||
.closeTabs(sameHostIds);
|
||||
await closeTabsWithConfirmation(context, ref, sameHostIds);
|
||||
}
|
||||
},
|
||||
child: const Text('Close from Same Host'),
|
||||
|
||||
+86
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright (c) 2024-2026 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:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:weblibre/features/geckoview/domain/providers/tab_state.dart';
|
||||
import 'package:weblibre/features/geckoview/domain/repositories/tab.dart';
|
||||
import 'package:weblibre/utils/ui_helper.dart' as ui_helper;
|
||||
|
||||
int countIsolatedGroupsRemovedByClosingTabs(
|
||||
WidgetRef ref,
|
||||
Iterable<String> tabIds,
|
||||
) {
|
||||
final idsToClose = tabIds.toSet();
|
||||
if (idsToClose.isEmpty) return 0;
|
||||
|
||||
final allStates = ref.read(tabStatesProvider);
|
||||
final isolatedCloseCounts = <String, int>{};
|
||||
|
||||
for (final tabId in idsToClose) {
|
||||
final contextId = allStates[tabId]?.isolationContextId;
|
||||
if (contextId != null) {
|
||||
isolatedCloseCounts.update(
|
||||
contextId,
|
||||
(count) => count + 1,
|
||||
ifAbsent: () => 1,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return isolatedCloseCounts.entries.where((entry) {
|
||||
final totalInGroup = allStates.values
|
||||
.where((state) => state.isolationContextId == entry.key)
|
||||
.length;
|
||||
return totalInGroup == entry.value;
|
||||
}).length;
|
||||
}
|
||||
|
||||
Future<bool> confirmBulkTabCloseIfNeeded(
|
||||
BuildContext context,
|
||||
WidgetRef ref,
|
||||
Iterable<String> tabIds,
|
||||
) async {
|
||||
final groupsToDelete = countIsolatedGroupsRemovedByClosingTabs(ref, tabIds);
|
||||
if (groupsToDelete <= 0) return true;
|
||||
if (!context.mounted) return false;
|
||||
|
||||
return ui_helper.confirmIsolatedTabClose(context, groupCount: groupsToDelete);
|
||||
}
|
||||
|
||||
Future<bool> closeTabsWithConfirmation(
|
||||
BuildContext context,
|
||||
WidgetRef ref,
|
||||
Iterable<String> tabIds,
|
||||
) async {
|
||||
final idsToClose = tabIds.toSet().toList();
|
||||
if (idsToClose.isEmpty) return false;
|
||||
|
||||
final tabRepository = ref.read(tabRepositoryProvider.notifier);
|
||||
|
||||
if (!await confirmBulkTabCloseIfNeeded(context, ref, idsToClose)) {
|
||||
return false;
|
||||
}
|
||||
if (!context.mounted) {
|
||||
return false;
|
||||
}
|
||||
|
||||
await tabRepository.closeTabs(idsToClose);
|
||||
return true;
|
||||
}
|
||||
+3
-6
@@ -48,6 +48,7 @@ import 'package:weblibre/features/geckoview/domain/providers/web_extensions_stat
|
||||
import 'package:weblibre/features/geckoview/domain/repositories/tab.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/browser/presentation/utils/tab_close_confirmation.dart';
|
||||
import 'package:weblibre/features/geckoview/features/browser/presentation/widgets/extension_badge_icon.dart';
|
||||
import 'package:weblibre/features/geckoview/features/browser/presentation/widgets/history_menu.dart';
|
||||
import 'package:weblibre/features/geckoview/features/browser/presentation/widgets/translation_bottom_sheet.dart';
|
||||
@@ -319,9 +320,7 @@ class _NavigationRow extends HookConsumerWidget {
|
||||
.where((id) => id != selectedTabId)
|
||||
.toList();
|
||||
if (otherIds.isNotEmpty) {
|
||||
await ref
|
||||
.read(tabRepositoryProvider.notifier)
|
||||
.closeTabs(otherIds);
|
||||
await closeTabsWithConfirmation(context, ref, otherIds);
|
||||
}
|
||||
if (context.mounted) {
|
||||
Navigator.pop(context);
|
||||
@@ -339,9 +338,7 @@ class _NavigationRow extends HookConsumerWidget {
|
||||
.map((e) => e.key)
|
||||
.toList();
|
||||
if (sameHostIds.isNotEmpty) {
|
||||
await ref
|
||||
.read(tabRepositoryProvider.notifier)
|
||||
.closeTabs(sameHostIds);
|
||||
await closeTabsWithConfirmation(context, ref, sameHostIds);
|
||||
}
|
||||
if (context.mounted) {
|
||||
Navigator.pop(context);
|
||||
|
||||
+8
-25
@@ -29,6 +29,7 @@ import 'package:weblibre/features/geckoview/domain/providers/tab_state.dart';
|
||||
import 'package:weblibre/features/geckoview/domain/repositories/tab.dart';
|
||||
import 'package:weblibre/features/geckoview/features/browser/domain/providers.dart';
|
||||
import 'package:weblibre/features/geckoview/features/browser/presentation/controllers/tab_view_controllers.dart';
|
||||
import 'package:weblibre/features/geckoview/features/browser/presentation/utils/tab_close_confirmation.dart';
|
||||
import 'package:weblibre/features/geckoview/features/browser/presentation/widgets/tab_view/tab_preview.dart';
|
||||
import 'package:weblibre/features/geckoview/features/browser/presentation/widgets/tab_view/tab_view_header.dart';
|
||||
import 'package:weblibre/features/geckoview/features/browser/utils/grid_calculations.dart';
|
||||
@@ -139,32 +140,14 @@ class _TabTreePreview extends HookConsumerWidget {
|
||||
final tabs = await ref
|
||||
.read(tabDataRepositoryProvider.notifier)
|
||||
.getTabDescendants(entity.rootId);
|
||||
if (!context.mounted) return;
|
||||
|
||||
final allStates = ref.read(tabStatesProvider);
|
||||
final isolatedContextInCloseSet = <String, int>{};
|
||||
|
||||
for (final tabId in tabs.keys) {
|
||||
final contextId = allStates[tabId]?.isolationContextId;
|
||||
if (contextId == null) continue;
|
||||
isolatedContextInCloseSet[contextId] =
|
||||
(isolatedContextInCloseSet[contextId] ?? 0) + 1;
|
||||
}
|
||||
|
||||
final groupsToDelete = isolatedContextInCloseSet.entries.where((
|
||||
entry,
|
||||
) {
|
||||
final totalInGroup = allStates.values
|
||||
.where((state) => state.isolationContextId == entry.key)
|
||||
.length;
|
||||
return totalInGroup == entry.value;
|
||||
}).length;
|
||||
|
||||
if (groupsToDelete > 0 && context.mounted) {
|
||||
final confirmed = await ui_helper.confirmIsolatedTabClose(
|
||||
context,
|
||||
groupCount: groupsToDelete,
|
||||
);
|
||||
if (!confirmed) return;
|
||||
if (!await confirmBulkTabCloseIfNeeded(
|
||||
context,
|
||||
ref,
|
||||
tabs.keys,
|
||||
)) {
|
||||
return;
|
||||
}
|
||||
|
||||
await ref
|
||||
|
||||
+8
-24
@@ -41,6 +41,7 @@ import 'package:weblibre/features/geckoview/features/browser/domain/services/bro
|
||||
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/utils/tab_close_confirmation.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';
|
||||
@@ -846,34 +847,17 @@ class TabViewHeader extends HookConsumerWidget {
|
||||
.getFilteredTabIds(
|
||||
selectedContainerId,
|
||||
);
|
||||
if (!context.mounted) return;
|
||||
|
||||
if (filteredIds.isEmpty) return;
|
||||
|
||||
// Check for isolated tabs
|
||||
final allStates = ref.read(
|
||||
tabStatesProvider,
|
||||
);
|
||||
final isolatedContextIds = filteredIds
|
||||
.map((id) => allStates[id])
|
||||
.where(
|
||||
(s) =>
|
||||
s != null &&
|
||||
s.tabMode
|
||||
is IsolatedTabMode &&
|
||||
s.isolationContextId != null,
|
||||
)
|
||||
.map((s) => s!.isolationContextId!)
|
||||
.toSet();
|
||||
|
||||
if (isolatedContextIds.isNotEmpty &&
|
||||
context.mounted) {
|
||||
final confirmed = await ui_helper
|
||||
.confirmIsolatedTabClose(
|
||||
context,
|
||||
groupCount:
|
||||
isolatedContextIds.length,
|
||||
);
|
||||
if (!confirmed) return;
|
||||
if (!await confirmBulkTabCloseIfNeeded(
|
||||
context,
|
||||
ref,
|
||||
filteredIds,
|
||||
)) {
|
||||
return;
|
||||
}
|
||||
|
||||
await ref
|
||||
|
||||
Reference in New Issue
Block a user