From 55305c3dd14f12bbe40ce9dcf96f9851941a1ced Mon Sep 17 00:00:00 2001 From: Fabian Freund Date: Wed, 5 Nov 2025 05:37:45 +0100 Subject: [PATCH] option to open tab view in fullscreen --- app/lib/core/routing/routes.browser.dart | 8 ++ app/lib/core/routing/routes.dart | 1 + app/lib/core/routing/routes.g.dart | 25 ++++ .../domain/services/generic_website.g.dart | 2 +- .../presentation/dialogs/tab_tree.dart | 17 ++- .../browser/presentation/screens/browser.dart | 24 +++- .../presentation/screens/tab_view.dart | 57 ++++++++ .../browser_modules/bottom_app_bar.dart | 24 ++-- .../widgets/{sheets => }/view_tabs.dart | 134 ++++++++++-------- .../search/presentation/screens/search.dart | 5 +- .../search_modules/history_suggestions.dart | 4 +- .../widgets/search_modules/tab_search.dart | 4 +- .../screens/container_draft_suggestions.dart | 2 +- .../screens/general_settings.dart | 16 +++ .../user/data/models/general_settings.dart | 7 +- .../user/data/models/general_settings.g.dart | 16 +++ .../domain/repositories/general_settings.dart | 4 + .../repositories/general_settings.g.dart | 2 +- .../controllers/website_title.g.dart | 2 +- 19 files changed, 263 insertions(+), 91 deletions(-) create mode 100644 app/lib/features/geckoview/features/browser/presentation/screens/tab_view.dart rename app/lib/features/geckoview/features/browser/presentation/widgets/{sheets => }/view_tabs.dart (90%) diff --git a/app/lib/core/routing/routes.browser.dart b/app/lib/core/routing/routes.browser.dart index 3b533ec6..5efd4735 100644 --- a/app/lib/core/routing/routes.browser.dart +++ b/app/lib/core/routing/routes.browser.dart @@ -29,6 +29,7 @@ part of 'routes.dart'; ), TypedGoRoute(name: 'TorProxyRoute', path: 'tor_proxy'), TypedGoRoute(name: 'HistoryRoute', path: 'history'), + TypedGoRoute(name: 'TabViewRoute', path: 'tab_view'), TypedGoRoute( name: 'ContextMenuRoute', path: 'context_menu', @@ -201,3 +202,10 @@ class HistoryRoute extends GoRouteData with $HistoryRoute { return const HistoryScreen(); } } + +class TabViewRoute extends GoRouteData with $TabViewRoute { + @override + Page buildPage(BuildContext context, GoRouterState state) { + return DialogPage(builder: (_) => const TabViewScreen()); + } +} diff --git a/app/lib/core/routing/routes.dart b/app/lib/core/routing/routes.dart index c36d15c7..0d415edd 100644 --- a/app/lib/core/routing/routes.dart +++ b/app/lib/core/routing/routes.dart @@ -29,6 +29,7 @@ import 'package:weblibre/features/bangs/presentation/screens/search.dart'; import 'package:weblibre/features/geckoview/features/browser/presentation/dialogs/open_shared_content.dart'; import 'package:weblibre/features/geckoview/features/browser/presentation/dialogs/tab_tree.dart'; import 'package:weblibre/features/geckoview/features/browser/presentation/screens/browser.dart'; +import 'package:weblibre/features/geckoview/features/browser/presentation/screens/tab_view.dart'; import 'package:weblibre/features/geckoview/features/contextmenu/extensions/hit_result.dart'; import 'package:weblibre/features/geckoview/features/contextmenu/presentation/context_menu_dialog.dart'; import 'package:weblibre/features/geckoview/features/history/presentation/screens/history.dart'; diff --git a/app/lib/core/routing/routes.g.dart b/app/lib/core/routing/routes.g.dart index 443078dc..b2d63e83 100644 --- a/app/lib/core/routing/routes.g.dart +++ b/app/lib/core/routing/routes.g.dart @@ -395,6 +395,11 @@ RouteBase get $browserRoute => GoRouteData.$route( name: 'HistoryRoute', factory: $HistoryRoute._fromState, ), + GoRouteData.$route( + path: 'tab_view', + name: 'TabViewRoute', + factory: $TabViewRoute._fromState, + ), GoRouteData.$route( path: 'context_menu', name: 'ContextMenuRoute', @@ -545,6 +550,26 @@ mixin $HistoryRoute on GoRouteData { void replace(BuildContext context) => context.replace(location); } +mixin $TabViewRoute on GoRouteData { + static TabViewRoute _fromState(GoRouterState state) => TabViewRoute(); + + @override + String get location => GoRouteData.$location('/tab_view'); + + @override + void go(BuildContext context) => context.go(location); + + @override + Future push(BuildContext context) => context.push(location); + + @override + void pushReplacement(BuildContext context) => + context.pushReplacement(location); + + @override + void replace(BuildContext context) => context.replace(location); +} + mixin $ContextMenuRoute on GoRouteData { static ContextMenuRoute _fromState(GoRouterState state) => ContextMenuRoute(state.extra as String); diff --git a/app/lib/domain/services/generic_website.g.dart b/app/lib/domain/services/generic_website.g.dart index 2b556015..be0256d4 100644 --- a/app/lib/domain/services/generic_website.g.dart +++ b/app/lib/domain/services/generic_website.g.dart @@ -42,7 +42,7 @@ final class GenericWebsiteServiceProvider } String _$genericWebsiteServiceHash() => - r'9bc32261abd76a79efb57f6d27832e949cb495a6'; + r'44d6e4cc161b1200070d99e29c1db04cd18ca091'; abstract class _$GenericWebsiteService extends $Notifier { void build(); diff --git a/app/lib/features/geckoview/features/browser/presentation/dialogs/tab_tree.dart b/app/lib/features/geckoview/features/browser/presentation/dialogs/tab_tree.dart index 8f02fd14..a7c6f470 100644 --- a/app/lib/features/geckoview/features/browser/presentation/dialogs/tab_tree.dart +++ b/app/lib/features/geckoview/features/browser/presentation/dialogs/tab_tree.dart @@ -27,10 +27,12 @@ import 'package:graphview/GraphView.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:nullability/nullability.dart'; import 'package:skeletonizer/skeletonizer.dart'; +import 'package:weblibre/core/routing/routes.dart'; import 'package:weblibre/features/geckoview/domain/controllers/bottom_sheet.dart'; import 'package:weblibre/features/geckoview/domain/providers/selected_tab.dart'; import 'package:weblibre/features/geckoview/features/browser/presentation/widgets/tab_preview.dart'; import 'package:weblibre/features/geckoview/features/tabs/domain/providers.dart'; +import 'package:weblibre/features/user/domain/repositories/general_settings.dart'; import 'package:weblibre/utils/ui_helper.dart'; class TabTreeDialog extends HookConsumerWidget { @@ -131,10 +133,17 @@ class TabTreeDialog extends HookConsumerWidget { tabId: id, activeTabId: selectedTabId, onClose: () { - context.pop(); - ref - .read(bottomSheetControllerProvider.notifier) - .requestDismiss(); + final tabViewBottomSheet = ref + .read(generalSettingsWithDefaultsProvider) + .tabViewBottomSheet; + + if (tabViewBottomSheet) { + ref + .read(bottomSheetControllerProvider.notifier) + .requestDismiss(); + } + + BrowserRoute().go(context); }, sourceSearchQuery: null, ), diff --git a/app/lib/features/geckoview/features/browser/presentation/screens/browser.dart b/app/lib/features/geckoview/features/browser/presentation/screens/browser.dart index 85c908a7..06576a6f 100644 --- a/app/lib/features/geckoview/features/browser/presentation/screens/browser.dart +++ b/app/lib/features/geckoview/features/browser/presentation/screens/browser.dart @@ -43,7 +43,7 @@ import 'package:weblibre/features/geckoview/features/browser/presentation/contro import 'package:weblibre/features/geckoview/features/browser/presentation/widgets/browser_modules/bottom_app_bar.dart'; import 'package:weblibre/features/geckoview/features/browser/presentation/widgets/browser_modules/browser_view.dart'; import 'package:weblibre/features/geckoview/features/browser/presentation/widgets/sheets/view_tab.dart'; -import 'package:weblibre/features/geckoview/features/browser/presentation/widgets/sheets/view_tabs.dart'; +import 'package:weblibre/features/geckoview/features/browser/presentation/widgets/view_tabs.dart'; import 'package:weblibre/features/geckoview/features/contextmenu/extensions/hit_result.dart'; import 'package:weblibre/features/geckoview/features/find_in_page/presentation/widgets/find_in_page.dart'; import 'package:weblibre/features/geckoview/features/readerview/presentation/controllers/readerable.dart'; @@ -572,7 +572,17 @@ class _ViewUrlSheet extends HookConsumerWidget { sheetScrollController: scrollController, draggableScrollableController: draggableScrollableController, onClose: () { - ref.read(bottomSheetControllerProvider.notifier).requestDismiss(); + final tabViewBottomSheet = ref + .read(generalSettingsWithDefaultsProvider) + .tabViewBottomSheet; + + if (tabViewBottomSheet) { + ref + .read(bottomSheetControllerProvider.notifier) + .requestDismiss(); + } else { + BrowserRoute().go(context); + } }, initialHeight: initialHeight, ), @@ -605,16 +615,18 @@ class _ViewTabsSheet extends HookConsumerWidget { topRight: Radius.circular(28), ), child: treeModeEnabled - ? ViewTabTreesSheetWidget( - sheetScrollController: scrollController, + ? ViewTabTreesWidget( + scrollController: scrollController, + showNewTabFab: true, onClose: () { ref .read(bottomSheetControllerProvider.notifier) .requestDismiss(); }, ) - : ViewTabsSheetWidget( - sheetScrollController: scrollController, + : ViewTabsWidget( + scrollController: scrollController, + showNewTabFab: true, draggableScrollableController: draggableScrollableController, onClose: () { ref diff --git a/app/lib/features/geckoview/features/browser/presentation/screens/tab_view.dart b/app/lib/features/geckoview/features/browser/presentation/screens/tab_view.dart new file mode 100644 index 00000000..2d6f120a --- /dev/null +++ b/app/lib/features/geckoview/features/browser/presentation/screens/tab_view.dart @@ -0,0 +1,57 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:weblibre/core/routing/routes.dart'; +import 'package:weblibre/features/geckoview/domain/providers/tab_state.dart'; +import 'package:weblibre/features/geckoview/features/browser/presentation/controllers/tree_view.dart'; +import 'package:weblibre/features/geckoview/features/browser/presentation/widgets/view_tabs.dart'; +import 'package:weblibre/features/user/domain/repositories/general_settings.dart'; + +class TabViewScreen extends HookConsumerWidget { + const TabViewScreen(); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final treeModeEnabled = ref.watch(treeViewControllerProvider); + + final scrollController = useScrollController(); + + return Dialog.fullscreen( + child: Scaffold( + body: SafeArea( + child: treeModeEnabled + ? ViewTabTreesWidget( + scrollController: scrollController, + showNewTabFab: false, + onClose: () { + BrowserRoute().go(context); + }, + ) + : ViewTabsWidget( + scrollController: scrollController, + showNewTabFab: false, + onClose: () { + BrowserRoute().go(context); + }, + ), + ), + floatingActionButton: FloatingActionButton( + onPressed: () async { + final settings = ref.read(generalSettingsWithDefaultsProvider); + + await SearchRoute( + tabType: + ref.read(selectedTabTypeProvider) ?? + settings.defaultCreateTabType, + ).push(context); + + if (context.mounted) { + BrowserRoute().go(context); + } + }, + child: const Icon(Icons.add), + ), + ), + ); + } +} diff --git a/app/lib/features/geckoview/features/browser/presentation/widgets/browser_modules/bottom_app_bar.dart b/app/lib/features/geckoview/features/browser/presentation/widgets/browser_modules/bottom_app_bar.dart index ee4d9dbd..135db378 100644 --- a/app/lib/features/geckoview/features/browser/presentation/widgets/browser_modules/bottom_app_bar.dart +++ b/app/lib/features/geckoview/features/browser/presentation/widgets/browser_modules/bottom_app_bar.dart @@ -158,15 +158,23 @@ class BrowserBottomAppBar extends HookConsumerWidget { selectedTabId: selectedTabId, child: TabsActionButton( isActive: displayedSheet is ViewTabsSheet, - onTap: () { - if (displayedSheet case ViewTabsSheet()) { - ref - .read(bottomSheetControllerProvider.notifier) - .requestDismiss(); + onTap: () async { + final tabViewBottomSheet = ref + .read(generalSettingsWithDefaultsProvider) + .tabViewBottomSheet; + + if (tabViewBottomSheet) { + if (displayedSheet case ViewTabsSheet()) { + ref + .read(bottomSheetControllerProvider.notifier) + .requestDismiss(); + } else { + ref + .read(bottomSheetControllerProvider.notifier) + .show(ViewTabsSheet()); + } } else { - ref - .read(bottomSheetControllerProvider.notifier) - .show(ViewTabsSheet()); + await TabViewRoute().push(context); } }, onLongPress: () { diff --git a/app/lib/features/geckoview/features/browser/presentation/widgets/sheets/view_tabs.dart b/app/lib/features/geckoview/features/browser/presentation/widgets/view_tabs.dart similarity index 90% rename from app/lib/features/geckoview/features/browser/presentation/widgets/sheets/view_tabs.dart rename to app/lib/features/geckoview/features/browser/presentation/widgets/view_tabs.dart index 27893e34..8151d95d 100644 --- a/app/lib/features/geckoview/features/browser/presentation/widgets/sheets/view_tabs.dart +++ b/app/lib/features/geckoview/features/browser/presentation/widgets/view_tabs.dart @@ -27,6 +27,7 @@ import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_material_design_icons/flutter_material_design_icons.dart'; import 'package:flutter_reorderable_grid_view/widgets/widgets.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:nullability/nullability.dart'; import 'package:weblibre/core/providers/global_drop.dart'; import 'package:weblibre/core/routing/routes.dart'; import 'package:weblibre/data/models/drag_data.dart'; @@ -160,13 +161,13 @@ class _TabDraggable extends HookConsumerWidget { } } -class _TabSheetHeader extends HookConsumerWidget { +class _TabViewHeader extends HookConsumerWidget { static const headerSize = 124.0; final bool treeViewEnabled; final VoidCallback onClose; - const _TabSheetHeader({required this.onClose, required this.treeViewEnabled}); + const _TabViewHeader({required this.onClose, required this.treeViewEnabled}); @override Widget build(BuildContext context, WidgetRef ref) { @@ -366,15 +367,17 @@ class _TabSheetHeader extends HookConsumerWidget { } } -class ViewTabsSheetWidget extends HookConsumerWidget { - final ScrollController sheetScrollController; - final DraggableScrollableController draggableScrollableController; +class ViewTabsWidget extends HookConsumerWidget { + final ScrollController scrollController; + final DraggableScrollableController? draggableScrollableController; final VoidCallback onClose; + final bool showNewTabFab; - const ViewTabsSheetWidget({ + const ViewTabsWidget({ required this.onClose, - required this.sheetScrollController, - required this.draggableScrollableController, + required this.scrollController, + this.draggableScrollableController, + required this.showNewTabFab, super.key, }); @@ -387,13 +390,18 @@ class ViewTabsSheetWidget extends HookConsumerWidget { physics: const NeverScrollableScrollPhysics(), headerSliverBuilder: (context, innerBoxIsScrolled) => [ SliverToBoxAdapter( - child: DraggableScrollableHeader( - controller: draggableScrollableController, - child: _TabSheetHeader( - onClose: onClose, - treeViewEnabled: false, - ), - ), + child: + draggableScrollableController.mapNotNull( + (draggableScrollableController) => + DraggableScrollableHeader( + controller: draggableScrollableController, + child: _TabViewHeader( + onClose: onClose, + treeViewEnabled: false, + ), + ), + ) ?? + _TabViewHeader(onClose: onClose, treeViewEnabled: false), ), ], body: HookConsumer( @@ -455,7 +463,7 @@ class ViewTabsSheetWidget extends HookConsumerWidget { useEffect(() { WidgetsBinding.instance.addPostFrameCallback((_) { - if (sheetScrollController.hasClients) { + if (scrollController.hasClients) { if (lastScroll.value != activeTab) { final index = filteredTabEntities.value.indexWhere( (entity) => entity.tabId == activeTab, @@ -464,11 +472,11 @@ class ViewTabsSheetWidget extends HookConsumerWidget { if (index > -1) { final offset = (index ~/ 2) * itemHeight; - if (offset != sheetScrollController.offset) { + if (offset != scrollController.offset) { lastScroll.value = activeTab; unawaited( - sheetScrollController.animateTo( + scrollController.animateTo( offset, duration: const Duration(milliseconds: 200), curve: Curves.easeInOut, @@ -487,7 +495,7 @@ class ViewTabsSheetWidget extends HookConsumerWidget { padding: const EdgeInsets.symmetric(horizontal: 4.0), child: FadingScroll( fadingSize: 5, - controller: sheetScrollController, + controller: scrollController, builder: (context, controller) { return ReorderableBuilder.builder( //Rebuild when cross axis count changes @@ -601,38 +609,41 @@ class ViewTabsSheetWidget extends HookConsumerWidget { }, ), ), - Padding( - padding: const EdgeInsets.only( - top: _TabSheetHeader.headerSize + 4, - right: 4, - ), - child: FloatingActionButton.small( - onPressed: () async { - final settings = ref.read(generalSettingsWithDefaultsProvider); + if (showNewTabFab) + Padding( + padding: const EdgeInsets.only( + top: _TabViewHeader.headerSize + 4, + right: 4, + ), + child: FloatingActionButton.small( + onPressed: () async { + final settings = ref.read(generalSettingsWithDefaultsProvider); - await SearchRoute( - tabType: - ref.read(selectedTabTypeProvider) ?? - settings.defaultCreateTabType, - ).push(context); + await SearchRoute( + tabType: + ref.read(selectedTabTypeProvider) ?? + settings.defaultCreateTabType, + ).push(context); - onClose(); - }, - child: const Icon(Icons.add), + onClose(); + }, + child: const Icon(Icons.add), + ), ), - ), ], ); } } -class ViewTabTreesSheetWidget extends HookConsumerWidget { - final ScrollController sheetScrollController; +class ViewTabTreesWidget extends HookConsumerWidget { + final ScrollController scrollController; final VoidCallback onClose; + final bool showNewTabFab; - const ViewTabTreesSheetWidget({ + const ViewTabTreesWidget({ required this.onClose, - required this.sheetScrollController, + required this.scrollController, + required this.showNewTabFab, super.key, }); @@ -643,7 +654,7 @@ class ViewTabTreesSheetWidget extends HookConsumerWidget { children: [ Column( children: [ - _TabSheetHeader(onClose: onClose, treeViewEnabled: true), + _TabViewHeader(onClose: onClose, treeViewEnabled: true), Expanded( child: HookConsumer( builder: (context, ref, child) { @@ -697,9 +708,9 @@ class ViewTabTreesSheetWidget extends HookConsumerWidget { if (index > -1) { final offset = (index ~/ 2) * itemHeight; - if (offset != sheetScrollController.offset) { + if (offset != scrollController.offset) { unawaited( - sheetScrollController.animateTo( + scrollController.animateTo( offset, duration: const Duration(milliseconds: 200), curve: Curves.easeInOut, @@ -728,7 +739,7 @@ class ViewTabTreesSheetWidget extends HookConsumerWidget { return Padding( padding: const EdgeInsets.symmetric(horizontal: 4.0), child: GridView.builder( - controller: sheetScrollController, + controller: scrollController, gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( //Sync values for itemHeight calculation _calculateItemHeight childAspectRatio: 0.75, @@ -745,26 +756,27 @@ class ViewTabTreesSheetWidget extends HookConsumerWidget { ), ], ), - Padding( - padding: const EdgeInsets.only( - top: _TabSheetHeader.headerSize + 4, - right: 4, - ), - child: FloatingActionButton.small( - onPressed: () async { - final settings = ref.read(generalSettingsWithDefaultsProvider); + if (showNewTabFab) + Padding( + padding: const EdgeInsets.only( + top: _TabViewHeader.headerSize + 4, + right: 4, + ), + child: FloatingActionButton.small( + onPressed: () async { + final settings = ref.read(generalSettingsWithDefaultsProvider); - await SearchRoute( - tabType: - ref.read(selectedTabTypeProvider) ?? - settings.defaultCreateTabType, - ).push(context); + await SearchRoute( + tabType: + ref.read(selectedTabTypeProvider) ?? + settings.defaultCreateTabType, + ).push(context); - onClose(); - }, - child: const Icon(Icons.add), + onClose(); + }, + child: const Icon(Icons.add), + ), ), - ), ], ); } diff --git a/app/lib/features/geckoview/features/search/presentation/screens/search.dart b/app/lib/features/geckoview/features/search/presentation/screens/search.dart index 29d9b6ab..6bc6c2ec 100644 --- a/app/lib/features/geckoview/features/search/presentation/screens/search.dart +++ b/app/lib/features/geckoview/features/search/presentation/screens/search.dart @@ -20,7 +20,6 @@ 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:go_router/go_router.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:weblibre/core/routing/routes.dart'; import 'package:weblibre/features/bangs/data/models/bang_data.dart'; @@ -120,7 +119,7 @@ class SearchScreen extends HookConsumerWidget { if (context.mounted) { ref.read(bottomSheetControllerProvider.notifier).requestDismiss(); - context.pop(); + BrowserRoute().go(context); } } } @@ -226,7 +225,7 @@ class SearchScreen extends HookConsumerWidget { .read(bottomSheetControllerProvider.notifier) .requestDismiss(); - context.pop(); + BrowserRoute().go(context); } } } diff --git a/app/lib/features/geckoview/features/search/presentation/widgets/search_modules/history_suggestions.dart b/app/lib/features/geckoview/features/search/presentation/widgets/search_modules/history_suggestions.dart index 6eaff808..f98556b5 100644 --- a/app/lib/features/geckoview/features/search/presentation/widgets/search_modules/history_suggestions.dart +++ b/app/lib/features/geckoview/features/search/presentation/widgets/search_modules/history_suggestions.dart @@ -21,11 +21,11 @@ import 'package:flutter/foundation.dart'; 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:go_router/go_router.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:nullability/nullability.dart'; import 'package:skeletonizer/skeletonizer.dart'; import 'package:sliver_tools/sliver_tools.dart'; +import 'package:weblibre/core/routing/routes.dart'; import 'package:weblibre/features/geckoview/domain/controllers/bottom_sheet.dart'; import 'package:weblibre/features/geckoview/domain/repositories/tab.dart'; import 'package:weblibre/features/geckoview/features/search/domain/providers/engine_suggestions.dart'; @@ -129,7 +129,7 @@ class HistorySuggestions extends HookConsumerWidget { ) .requestDismiss(); - context.pop(); + BrowserRoute().go(context); } } } diff --git a/app/lib/features/geckoview/features/search/presentation/widgets/search_modules/tab_search.dart b/app/lib/features/geckoview/features/search/presentation/widgets/search_modules/tab_search.dart index 11270b44..36333097 100644 --- a/app/lib/features/geckoview/features/search/presentation/widgets/search_modules/tab_search.dart +++ b/app/lib/features/geckoview/features/search/presentation/widgets/search_modules/tab_search.dart @@ -21,10 +21,10 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_markdown/flutter_markdown.dart'; -import 'package:go_router/go_router.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:nullability/nullability.dart'; import 'package:sliver_tools/sliver_tools.dart'; +import 'package:weblibre/core/routing/routes.dart'; import 'package:weblibre/features/geckoview/domain/controllers/bottom_sheet.dart'; import 'package:weblibre/features/geckoview/domain/repositories/tab.dart'; import 'package:weblibre/features/geckoview/features/browser/domain/providers.dart'; @@ -175,7 +175,7 @@ class TabSearch extends HookConsumerWidget { .read(bottomSheetControllerProvider.notifier) .requestDismiss(); - context.pop(); + BrowserRoute().go(context); } }, ); diff --git a/app/lib/features/geckoview/features/tabs/presentation/screens/container_draft_suggestions.dart b/app/lib/features/geckoview/features/tabs/presentation/screens/container_draft_suggestions.dart index 835f677c..2eb7b1e5 100644 --- a/app/lib/features/geckoview/features/tabs/presentation/screens/container_draft_suggestions.dart +++ b/app/lib/features/geckoview/features/tabs/presentation/screens/container_draft_suggestions.dart @@ -10,7 +10,7 @@ import 'package:skeletonizer/skeletonizer.dart'; import 'package:weblibre/core/logger.dart'; import 'package:weblibre/core/routing/routes.dart'; import 'package:weblibre/core/uuid.dart'; -import 'package:weblibre/features/geckoview/features/browser/presentation/widgets/sheets/view_tabs.dart'; +import 'package:weblibre/features/geckoview/features/browser/presentation/widgets/view_tabs.dart'; import 'package:weblibre/features/geckoview/features/browser/presentation/widgets/tab_preview.dart'; import 'package:weblibre/features/geckoview/features/tabs/data/models/container_data.dart'; import 'package:weblibre/features/geckoview/features/tabs/domain/repositories/container.dart'; diff --git a/app/lib/features/settings/presentation/screens/general_settings.dart b/app/lib/features/settings/presentation/screens/general_settings.dart index b4713617..81703f35 100644 --- a/app/lib/features/settings/presentation/screens/general_settings.dart +++ b/app/lib/features/settings/presentation/screens/general_settings.dart @@ -392,6 +392,22 @@ class GeneralSettingsScreen extends HookConsumerWidget { ); }, ), + SwitchListTile.adaptive( + title: const Text('Bottom Sheet Tab View'), + subtitle: const Text( + 'Display tabs in a bottom sheet instead of fullscreen', + ), + secondary: const Icon(MdiIcons.dockBottom), + value: generalSettings.tabViewBottomSheet, + onChanged: (value) async { + await ref + .read(saveGeneralSettingsControllerProvider.notifier) + .save( + (currentSettings) => + currentSettings.copyWith.tabViewBottomSheet(value), + ); + }, + ), Padding( padding: const EdgeInsets.symmetric( horizontal: 16.0, diff --git a/app/lib/features/user/data/models/general_settings.dart b/app/lib/features/user/data/models/general_settings.dart index 7e4ae9b3..cf1e7fe0 100644 --- a/app/lib/features/user/data/models/general_settings.dart +++ b/app/lib/features/user/data/models/general_settings.dart @@ -70,6 +70,7 @@ class GeneralSettings with FastEquatable { final bool autoHideTabBar; final TabBarSwipeAction tabBarSwipeAction; final Duration historyAutoCleanInterval; + final bool tabViewBottomSheet; GeneralSettings({ required this.themeMode, @@ -86,6 +87,7 @@ class GeneralSettings with FastEquatable { required this.autoHideTabBar, required this.tabBarSwipeAction, required this.historyAutoCleanInterval, + required this.tabViewBottomSheet, }); GeneralSettings.withDefaults({ @@ -103,6 +105,7 @@ class GeneralSettings with FastEquatable { bool? autoHideTabBar, TabBarSwipeAction? tabBarSwipeAction, Duration? historyAutoCleanInterval, + bool? tabViewBottomSheet, }) : themeMode = themeMode ?? ThemeMode.dark, enableReadability = enableReadability ?? true, enforceReadability = enforceReadability ?? false, @@ -118,7 +121,8 @@ class GeneralSettings with FastEquatable { tabBarSwipeAction = tabBarSwipeAction ?? TabBarSwipeAction.switchLastOpened, historyAutoCleanInterval = - historyAutoCleanInterval ?? const Duration(days: 90); + historyAutoCleanInterval ?? const Duration(days: 90), + tabViewBottomSheet = tabViewBottomSheet ?? false; factory GeneralSettings.fromJson(Map json) => _$GeneralSettingsFromJson(json); @@ -141,5 +145,6 @@ class GeneralSettings with FastEquatable { autoHideTabBar, tabBarSwipeAction, historyAutoCleanInterval, + tabViewBottomSheet, ]; } diff --git a/app/lib/features/user/data/models/general_settings.g.dart b/app/lib/features/user/data/models/general_settings.g.dart index e86f295b..cd5559a7 100644 --- a/app/lib/features/user/data/models/general_settings.g.dart +++ b/app/lib/features/user/data/models/general_settings.g.dart @@ -41,6 +41,8 @@ abstract class _$GeneralSettingsCWProxy { GeneralSettings historyAutoCleanInterval(Duration historyAutoCleanInterval); + GeneralSettings tabViewBottomSheet(bool tabViewBottomSheet); + /// Creates a new instance with the provided field values. /// Passing `null` to a nullable field nullifies it, while `null` for a non-nullable field is ignored. To update a single field use `GeneralSettings(...).copyWith.fieldName(value)`. /// @@ -63,6 +65,7 @@ abstract class _$GeneralSettingsCWProxy { bool autoHideTabBar, TabBarSwipeAction tabBarSwipeAction, Duration historyAutoCleanInterval, + bool tabViewBottomSheet, }); } @@ -131,6 +134,10 @@ class _$GeneralSettingsCWProxyImpl implements _$GeneralSettingsCWProxy { GeneralSettings historyAutoCleanInterval(Duration historyAutoCleanInterval) => call(historyAutoCleanInterval: historyAutoCleanInterval); + @override + GeneralSettings tabViewBottomSheet(bool tabViewBottomSheet) => + call(tabViewBottomSheet: tabViewBottomSheet); + @override /// Creates a new instance with the provided field values. /// Passing `null` to a nullable field nullifies it, while `null` for a non-nullable field is ignored. To update a single field use `GeneralSettings(...).copyWith.fieldName(value)`. @@ -154,6 +161,7 @@ class _$GeneralSettingsCWProxyImpl implements _$GeneralSettingsCWProxy { Object? autoHideTabBar = const $CopyWithPlaceholder(), Object? tabBarSwipeAction = const $CopyWithPlaceholder(), Object? historyAutoCleanInterval = const $CopyWithPlaceholder(), + Object? tabViewBottomSheet = const $CopyWithPlaceholder(), }) { return GeneralSettings( themeMode: themeMode == const $CopyWithPlaceholder() || themeMode == null @@ -236,6 +244,12 @@ class _$GeneralSettingsCWProxyImpl implements _$GeneralSettingsCWProxy { ? _value.historyAutoCleanInterval // ignore: cast_nullable_to_non_nullable : historyAutoCleanInterval as Duration, + tabViewBottomSheet: + tabViewBottomSheet == const $CopyWithPlaceholder() || + tabViewBottomSheet == null + ? _value.tabViewBottomSheet + // ignore: cast_nullable_to_non_nullable + : tabViewBottomSheet as bool, ); } } @@ -288,6 +302,7 @@ GeneralSettings _$GeneralSettingsFromJson(Map json) => : Duration( microseconds: (json['historyAutoCleanInterval'] as num).toInt(), ), + tabViewBottomSheet: json['tabViewBottomSheet'] as bool?, ); Map _$GeneralSettingsToJson( @@ -314,6 +329,7 @@ Map _$GeneralSettingsToJson( 'autoHideTabBar': instance.autoHideTabBar, 'tabBarSwipeAction': _$TabBarSwipeActionEnumMap[instance.tabBarSwipeAction]!, 'historyAutoCleanInterval': instance.historyAutoCleanInterval.inMicroseconds, + 'tabViewBottomSheet': instance.tabViewBottomSheet, }; const _$ThemeModeEnumMap = { diff --git a/app/lib/features/user/domain/repositories/general_settings.dart b/app/lib/features/user/domain/repositories/general_settings.dart index f3028f77..eb9ba6b6 100644 --- a/app/lib/features/user/domain/repositories/general_settings.dart +++ b/app/lib/features/user/domain/repositories/general_settings.dart @@ -100,6 +100,10 @@ class GeneralSettingsRepository extends _$GeneralSettingsRepository { DriftSqlType.int, db.typeMapping, ), + 'tabViewBottomSheet': settings['tabViewBottomSheet']?.readAs( + DriftSqlType.bool, + db.typeMapping, + ), }); } diff --git a/app/lib/features/user/domain/repositories/general_settings.g.dart b/app/lib/features/user/domain/repositories/general_settings.g.dart index d29a2fef..3915cf39 100644 --- a/app/lib/features/user/domain/repositories/general_settings.g.dart +++ b/app/lib/features/user/domain/repositories/general_settings.g.dart @@ -35,7 +35,7 @@ final class GeneralSettingsRepositoryProvider } String _$generalSettingsRepositoryHash() => - r'a9b21f9c5ef50bb8a8761995ed2fea723375b80e'; + r'7ab8eb72c43ed9486bf9fa8b13a6cce83e2b3d66'; abstract class _$GeneralSettingsRepository extends $StreamNotifier { diff --git a/app/lib/presentation/controllers/website_title.g.dart b/app/lib/presentation/controllers/website_title.g.dart index 33fb2a86..aefa4609 100644 --- a/app/lib/presentation/controllers/website_title.g.dart +++ b/app/lib/presentation/controllers/website_title.g.dart @@ -58,7 +58,7 @@ final class CompletePageInfoProvider } } -String _$completePageInfoHash() => r'e033a1439ee47af5aef0fd6bf3e8a41d5e88c6fe'; +String _$completePageInfoHash() => r'edf5538b913d1ebb285c15ec83644818fe90707d'; final class CompletePageInfoFamily extends $Family with