close tabs by host

This commit is contained in:
Fabian Freund
2025-06-02 22:22:07 +02:00
parent f6e0f665ce
commit 8fccd767af
9 changed files with 91 additions and 16 deletions
@@ -7,7 +7,7 @@ part of 'providers.dart';
// **************************************************************************
String _$selectionActionServiceHash() =>
r'9393acfbc2cc0864d9e9b116e65e4c2713da2ab1';
r'c45737a0a403bff1bdb1931b89701300f8dc3726';
/// See also [selectionActionService].
@ProviderFor(selectionActionService)
@@ -76,11 +76,11 @@ class _TabSheetHeader extends HookConsumerWidget {
final container = ref.read(selectedContainerProvider);
await ref
.read(tabDataRepositoryProvider.notifier)
.closeAllTabs(container);
.closeAllTabsByContainer(container);
onClose();
},
icon: const Icon(Icons.delete),
icon: const Icon(MdiIcons.closeBoxMultiple),
label: const Text('Close All'),
),
],
@@ -1,18 +1,23 @@
import 'package:flutter/material.dart';
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:nullability/nullability.dart';
import 'package:weblibre/features/geckoview/domain/entities/states/tab.dart';
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/presentation/widgets/tab_icon.dart';
import 'package:weblibre/features/geckoview/features/tabs/domain/repositories/tab.dart';
import 'package:weblibre/presentation/hooks/menu_controller.dart';
class TabPreview extends StatelessWidget {
class TabPreview extends HookWidget {
final TabState tab;
final bool isActive;
final VoidCallback? onTap;
final VoidCallback? onDoubleTap;
final VoidCallback? onDelete;
final void Function(String host)? onDeleteAll;
const TabPreview({
required this.tab,
@@ -20,12 +25,15 @@ class TabPreview extends StatelessWidget {
this.onTap,
this.onDoubleTap,
this.onDelete,
this.onDeleteAll,
super.key,
});
@override
Widget build(BuildContext context) {
final colorScheme = Theme.of(context).colorScheme;
final extendedDeleteMenuController = useMenuController();
return Container(
decoration: BoxDecoration(
border: Border.all(
@@ -62,13 +70,37 @@ class TabPreview extends StatelessWidget {
),
),
),
IconButton(
visualDensity: const VisualDensity(
horizontal: -4.0,
vertical: -4.0,
MenuAnchor(
controller: extendedDeleteMenuController,
builder: (context, controller, child) {
return child!;
},
menuChildren: [
MenuItemButton(
onPressed: onDeleteAll.mapNotNull(
(p0) => () {
p0(tab.url.host);
},
),
leadingIcon: const Icon(MdiIcons.closeBoxMultiple),
child: Text('Close all from ${tab.url.host}'),
),
],
child: IconButton(
visualDensity: const VisualDensity(
horizontal: -4.0,
vertical: -4.0,
),
onPressed: onDelete,
onLongPress: () {
if (extendedDeleteMenuController.isOpen) {
extendedDeleteMenuController.close();
} else {
extendedDeleteMenuController.open();
}
},
icon: const Icon(Icons.close),
),
onPressed: onDelete,
icon: const Icon(Icons.close),
),
],
),
@@ -166,6 +198,15 @@ class TabPreviewDraggable extends HookConsumerWidget {
onClose();
}
},
onDeleteAll: (host) async {
final containerId = await ref
.read(tabDataRepositoryProvider.notifier)
.containerTabId(tab.id);
await ref
.read(tabDataRepositoryProvider.notifier)
.closeAllTabsByHost(containerId, host);
},
// onDoubleTap: () {
// ref.read(overlayDialogControllerProvider.notifier).show(
// TabActionDialog(
@@ -66,6 +66,20 @@ class ContainerDao extends DatabaseAccessor<TabDatabase>
return query.map((row) => row.read(db.tab.id)!);
}
Selectable<TabData> getContainerTabsData(String? containerId) {
final query =
db.tab.select()
..where(
(t) =>
(containerId != null)
? t.containerId.equals(containerId)
: t.containerId.isNull(),
)
..orderBy([(t) => OrderingTerm.asc(t.orderKey)]);
return query;
}
SingleSelectable<String> generateLeadingOrderKey(
String? containerId, {
int bucket = 0,
@@ -43,7 +43,9 @@ class ContainerRepository extends _$ContainerRepository {
}
Future<void> deleteContainer(String id) async {
await ref.read(tabDataRepositoryProvider.notifier).closeAllTabs(id);
await ref
.read(tabDataRepositoryProvider.notifier)
.closeAllTabsByContainer(id);
return ref.read(tabDatabaseProvider).containerDao.deleteContainer(id);
}
@@ -7,7 +7,7 @@ part of 'container.dart';
// **************************************************************************
String _$containerRepositoryHash() =>
r'9fff10b7896ea8865282bc315df21abde18b9d69';
r'5344ecde33c1c80c1fa2bc8383faca181da91593';
/// See also [ContainerRepository].
@ProviderFor(ContainerRepository)
@@ -20,7 +20,7 @@ class TabDataRepository extends _$TabDataRepository {
.assignOrderKey(tabId, orderKey: orderKey);
}
Future<void> closeAllTabs(String? containerId) async {
Future<void> closeAllTabsByContainer(String? containerId) async {
final tabIds =
await ref
.read(tabDatabaseProvider)
@@ -33,6 +33,25 @@ class TabDataRepository extends _$TabDataRepository {
}
}
Future<void> closeAllTabsByHost(String? containerId, String host) async {
final tabs =
await ref
.read(tabDatabaseProvider)
.containerDao
.getContainerTabsData(containerId)
.get();
final filtered =
tabs
.where((tab) => tab.url?.host == host)
.map((tab) => tab.id)
.toList();
if (filtered.isNotEmpty) {
await ref.read(tabRepositoryProvider.notifier).closeTabs(filtered);
}
}
Future<String?> containerTabId(String tabId) {
return ref
.read(tabDatabaseProvider)
@@ -6,7 +6,7 @@ part of 'tab.dart';
// RiverpodGenerator
// **************************************************************************
String _$tabDataRepositoryHash() => r'b5193e58aa331c7d0dc374e01032fe3a44955c99';
String _$tabDataRepositoryHash() => r'0de1de9f948e38cc0a6d2fc18a06b1327d41c7ca';
/// See also [TabDataRepository].
@ProviderFor(TabDataRepository)
@@ -1,5 +1,3 @@
import 'dart:io';
import 'package:drift/drift.dart';
import 'package:drift/native.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
@@ -8,6 +6,7 @@ import 'package:path_provider/path_provider.dart' as path_provider;
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:sqlite3/sqlite3.dart';
import 'package:sqlite3_flutter_libs/sqlite3_flutter_libs.dart';
import 'package:universal_io/io.dart';
import 'package:weblibre/features/web_feed/data/database/database.dart';
part 'providers.g.dart';