option to open tab view in fullscreen

This commit is contained in:
Fabian Freund
2025-11-05 05:37:45 +01:00
parent c173451b1d
commit 55305c3dd1
19 changed files with 263 additions and 91 deletions
+8
View File
@@ -29,6 +29,7 @@ part of 'routes.dart';
), ),
TypedGoRoute<TorProxyRoute>(name: 'TorProxyRoute', path: 'tor_proxy'), TypedGoRoute<TorProxyRoute>(name: 'TorProxyRoute', path: 'tor_proxy'),
TypedGoRoute<HistoryRoute>(name: 'HistoryRoute', path: 'history'), TypedGoRoute<HistoryRoute>(name: 'HistoryRoute', path: 'history'),
TypedGoRoute<TabViewRoute>(name: 'TabViewRoute', path: 'tab_view'),
TypedGoRoute<ContextMenuRoute>( TypedGoRoute<ContextMenuRoute>(
name: 'ContextMenuRoute', name: 'ContextMenuRoute',
path: 'context_menu', path: 'context_menu',
@@ -201,3 +202,10 @@ class HistoryRoute extends GoRouteData with $HistoryRoute {
return const HistoryScreen(); return const HistoryScreen();
} }
} }
class TabViewRoute extends GoRouteData with $TabViewRoute {
@override
Page<void> buildPage(BuildContext context, GoRouterState state) {
return DialogPage(builder: (_) => const TabViewScreen());
}
}
+1
View File
@@ -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/open_shared_content.dart';
import 'package:weblibre/features/geckoview/features/browser/presentation/dialogs/tab_tree.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/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/extensions/hit_result.dart';
import 'package:weblibre/features/geckoview/features/contextmenu/presentation/context_menu_dialog.dart'; import 'package:weblibre/features/geckoview/features/contextmenu/presentation/context_menu_dialog.dart';
import 'package:weblibre/features/geckoview/features/history/presentation/screens/history.dart'; import 'package:weblibre/features/geckoview/features/history/presentation/screens/history.dart';
+25
View File
@@ -395,6 +395,11 @@ RouteBase get $browserRoute => GoRouteData.$route(
name: 'HistoryRoute', name: 'HistoryRoute',
factory: $HistoryRoute._fromState, factory: $HistoryRoute._fromState,
), ),
GoRouteData.$route(
path: 'tab_view',
name: 'TabViewRoute',
factory: $TabViewRoute._fromState,
),
GoRouteData.$route( GoRouteData.$route(
path: 'context_menu', path: 'context_menu',
name: 'ContextMenuRoute', name: 'ContextMenuRoute',
@@ -545,6 +550,26 @@ mixin $HistoryRoute on GoRouteData {
void replace(BuildContext context) => context.replace(location); 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<T?> push<T>(BuildContext context) => context.push<T>(location);
@override
void pushReplacement(BuildContext context) =>
context.pushReplacement(location);
@override
void replace(BuildContext context) => context.replace(location);
}
mixin $ContextMenuRoute on GoRouteData { mixin $ContextMenuRoute on GoRouteData {
static ContextMenuRoute _fromState(GoRouterState state) => static ContextMenuRoute _fromState(GoRouterState state) =>
ContextMenuRoute(state.extra as String); ContextMenuRoute(state.extra as String);
@@ -42,7 +42,7 @@ final class GenericWebsiteServiceProvider
} }
String _$genericWebsiteServiceHash() => String _$genericWebsiteServiceHash() =>
r'9bc32261abd76a79efb57f6d27832e949cb495a6'; r'44d6e4cc161b1200070d99e29c1db04cd18ca091';
abstract class _$GenericWebsiteService extends $Notifier<void> { abstract class _$GenericWebsiteService extends $Notifier<void> {
void build(); void build();
@@ -27,10 +27,12 @@ import 'package:graphview/GraphView.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:nullability/nullability.dart'; import 'package:nullability/nullability.dart';
import 'package:skeletonizer/skeletonizer.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/controllers/bottom_sheet.dart';
import 'package:weblibre/features/geckoview/domain/providers/selected_tab.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/browser/presentation/widgets/tab_preview.dart';
import 'package:weblibre/features/geckoview/features/tabs/domain/providers.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'; import 'package:weblibre/utils/ui_helper.dart';
class TabTreeDialog extends HookConsumerWidget { class TabTreeDialog extends HookConsumerWidget {
@@ -131,10 +133,17 @@ class TabTreeDialog extends HookConsumerWidget {
tabId: id, tabId: id,
activeTabId: selectedTabId, activeTabId: selectedTabId,
onClose: () { onClose: () {
context.pop(); final tabViewBottomSheet = ref
ref .read(generalSettingsWithDefaultsProvider)
.read(bottomSheetControllerProvider.notifier) .tabViewBottomSheet;
.requestDismiss();
if (tabViewBottomSheet) {
ref
.read(bottomSheetControllerProvider.notifier)
.requestDismiss();
}
BrowserRoute().go(context);
}, },
sourceSearchQuery: null, sourceSearchQuery: null,
), ),
@@ -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/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/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_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/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/find_in_page/presentation/widgets/find_in_page.dart';
import 'package:weblibre/features/geckoview/features/readerview/presentation/controllers/readerable.dart'; import 'package:weblibre/features/geckoview/features/readerview/presentation/controllers/readerable.dart';
@@ -572,7 +572,17 @@ class _ViewUrlSheet extends HookConsumerWidget {
sheetScrollController: scrollController, sheetScrollController: scrollController,
draggableScrollableController: draggableScrollableController, draggableScrollableController: draggableScrollableController,
onClose: () { 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, initialHeight: initialHeight,
), ),
@@ -605,16 +615,18 @@ class _ViewTabsSheet extends HookConsumerWidget {
topRight: Radius.circular(28), topRight: Radius.circular(28),
), ),
child: treeModeEnabled child: treeModeEnabled
? ViewTabTreesSheetWidget( ? ViewTabTreesWidget(
sheetScrollController: scrollController, scrollController: scrollController,
showNewTabFab: true,
onClose: () { onClose: () {
ref ref
.read(bottomSheetControllerProvider.notifier) .read(bottomSheetControllerProvider.notifier)
.requestDismiss(); .requestDismiss();
}, },
) )
: ViewTabsSheetWidget( : ViewTabsWidget(
sheetScrollController: scrollController, scrollController: scrollController,
showNewTabFab: true,
draggableScrollableController: draggableScrollableController, draggableScrollableController: draggableScrollableController,
onClose: () { onClose: () {
ref ref
@@ -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),
),
),
);
}
}
@@ -158,15 +158,23 @@ class BrowserBottomAppBar extends HookConsumerWidget {
selectedTabId: selectedTabId, selectedTabId: selectedTabId,
child: TabsActionButton( child: TabsActionButton(
isActive: displayedSheet is ViewTabsSheet, isActive: displayedSheet is ViewTabsSheet,
onTap: () { onTap: () async {
if (displayedSheet case ViewTabsSheet()) { final tabViewBottomSheet = ref
ref .read(generalSettingsWithDefaultsProvider)
.read(bottomSheetControllerProvider.notifier) .tabViewBottomSheet;
.requestDismiss();
if (tabViewBottomSheet) {
if (displayedSheet case ViewTabsSheet()) {
ref
.read(bottomSheetControllerProvider.notifier)
.requestDismiss();
} else {
ref
.read(bottomSheetControllerProvider.notifier)
.show(ViewTabsSheet());
}
} else { } else {
ref await TabViewRoute().push(context);
.read(bottomSheetControllerProvider.notifier)
.show(ViewTabsSheet());
} }
}, },
onLongPress: () { onLongPress: () {
@@ -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_material_design_icons/flutter_material_design_icons.dart';
import 'package:flutter_reorderable_grid_view/widgets/widgets.dart'; import 'package:flutter_reorderable_grid_view/widgets/widgets.dart';
import 'package:hooks_riverpod/hooks_riverpod.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/providers/global_drop.dart';
import 'package:weblibre/core/routing/routes.dart'; import 'package:weblibre/core/routing/routes.dart';
import 'package:weblibre/data/models/drag_data.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; static const headerSize = 124.0;
final bool treeViewEnabled; final bool treeViewEnabled;
final VoidCallback onClose; final VoidCallback onClose;
const _TabSheetHeader({required this.onClose, required this.treeViewEnabled}); const _TabViewHeader({required this.onClose, required this.treeViewEnabled});
@override @override
Widget build(BuildContext context, WidgetRef ref) { Widget build(BuildContext context, WidgetRef ref) {
@@ -366,15 +367,17 @@ class _TabSheetHeader extends HookConsumerWidget {
} }
} }
class ViewTabsSheetWidget extends HookConsumerWidget { class ViewTabsWidget extends HookConsumerWidget {
final ScrollController sheetScrollController; final ScrollController scrollController;
final DraggableScrollableController draggableScrollableController; final DraggableScrollableController? draggableScrollableController;
final VoidCallback onClose; final VoidCallback onClose;
final bool showNewTabFab;
const ViewTabsSheetWidget({ const ViewTabsWidget({
required this.onClose, required this.onClose,
required this.sheetScrollController, required this.scrollController,
required this.draggableScrollableController, this.draggableScrollableController,
required this.showNewTabFab,
super.key, super.key,
}); });
@@ -387,13 +390,18 @@ class ViewTabsSheetWidget extends HookConsumerWidget {
physics: const NeverScrollableScrollPhysics(), physics: const NeverScrollableScrollPhysics(),
headerSliverBuilder: (context, innerBoxIsScrolled) => [ headerSliverBuilder: (context, innerBoxIsScrolled) => [
SliverToBoxAdapter( SliverToBoxAdapter(
child: DraggableScrollableHeader( child:
controller: draggableScrollableController, draggableScrollableController.mapNotNull(
child: _TabSheetHeader( (draggableScrollableController) =>
onClose: onClose, DraggableScrollableHeader(
treeViewEnabled: false, controller: draggableScrollableController,
), child: _TabViewHeader(
), onClose: onClose,
treeViewEnabled: false,
),
),
) ??
_TabViewHeader(onClose: onClose, treeViewEnabled: false),
), ),
], ],
body: HookConsumer( body: HookConsumer(
@@ -455,7 +463,7 @@ class ViewTabsSheetWidget extends HookConsumerWidget {
useEffect(() { useEffect(() {
WidgetsBinding.instance.addPostFrameCallback((_) { WidgetsBinding.instance.addPostFrameCallback((_) {
if (sheetScrollController.hasClients) { if (scrollController.hasClients) {
if (lastScroll.value != activeTab) { if (lastScroll.value != activeTab) {
final index = filteredTabEntities.value.indexWhere( final index = filteredTabEntities.value.indexWhere(
(entity) => entity.tabId == activeTab, (entity) => entity.tabId == activeTab,
@@ -464,11 +472,11 @@ class ViewTabsSheetWidget extends HookConsumerWidget {
if (index > -1) { if (index > -1) {
final offset = (index ~/ 2) * itemHeight; final offset = (index ~/ 2) * itemHeight;
if (offset != sheetScrollController.offset) { if (offset != scrollController.offset) {
lastScroll.value = activeTab; lastScroll.value = activeTab;
unawaited( unawaited(
sheetScrollController.animateTo( scrollController.animateTo(
offset, offset,
duration: const Duration(milliseconds: 200), duration: const Duration(milliseconds: 200),
curve: Curves.easeInOut, curve: Curves.easeInOut,
@@ -487,7 +495,7 @@ class ViewTabsSheetWidget extends HookConsumerWidget {
padding: const EdgeInsets.symmetric(horizontal: 4.0), padding: const EdgeInsets.symmetric(horizontal: 4.0),
child: FadingScroll( child: FadingScroll(
fadingSize: 5, fadingSize: 5,
controller: sheetScrollController, controller: scrollController,
builder: (context, controller) { builder: (context, controller) {
return ReorderableBuilder.builder( return ReorderableBuilder.builder(
//Rebuild when cross axis count changes //Rebuild when cross axis count changes
@@ -601,38 +609,41 @@ class ViewTabsSheetWidget extends HookConsumerWidget {
}, },
), ),
), ),
Padding( if (showNewTabFab)
padding: const EdgeInsets.only( Padding(
top: _TabSheetHeader.headerSize + 4, padding: const EdgeInsets.only(
right: 4, top: _TabViewHeader.headerSize + 4,
), right: 4,
child: FloatingActionButton.small( ),
onPressed: () async { child: FloatingActionButton.small(
final settings = ref.read(generalSettingsWithDefaultsProvider); onPressed: () async {
final settings = ref.read(generalSettingsWithDefaultsProvider);
await SearchRoute( await SearchRoute(
tabType: tabType:
ref.read(selectedTabTypeProvider) ?? ref.read(selectedTabTypeProvider) ??
settings.defaultCreateTabType, settings.defaultCreateTabType,
).push(context); ).push(context);
onClose(); onClose();
}, },
child: const Icon(Icons.add), child: const Icon(Icons.add),
),
), ),
),
], ],
); );
} }
} }
class ViewTabTreesSheetWidget extends HookConsumerWidget { class ViewTabTreesWidget extends HookConsumerWidget {
final ScrollController sheetScrollController; final ScrollController scrollController;
final VoidCallback onClose; final VoidCallback onClose;
final bool showNewTabFab;
const ViewTabTreesSheetWidget({ const ViewTabTreesWidget({
required this.onClose, required this.onClose,
required this.sheetScrollController, required this.scrollController,
required this.showNewTabFab,
super.key, super.key,
}); });
@@ -643,7 +654,7 @@ class ViewTabTreesSheetWidget extends HookConsumerWidget {
children: [ children: [
Column( Column(
children: [ children: [
_TabSheetHeader(onClose: onClose, treeViewEnabled: true), _TabViewHeader(onClose: onClose, treeViewEnabled: true),
Expanded( Expanded(
child: HookConsumer( child: HookConsumer(
builder: (context, ref, child) { builder: (context, ref, child) {
@@ -697,9 +708,9 @@ class ViewTabTreesSheetWidget extends HookConsumerWidget {
if (index > -1) { if (index > -1) {
final offset = (index ~/ 2) * itemHeight; final offset = (index ~/ 2) * itemHeight;
if (offset != sheetScrollController.offset) { if (offset != scrollController.offset) {
unawaited( unawaited(
sheetScrollController.animateTo( scrollController.animateTo(
offset, offset,
duration: const Duration(milliseconds: 200), duration: const Duration(milliseconds: 200),
curve: Curves.easeInOut, curve: Curves.easeInOut,
@@ -728,7 +739,7 @@ class ViewTabTreesSheetWidget extends HookConsumerWidget {
return Padding( return Padding(
padding: const EdgeInsets.symmetric(horizontal: 4.0), padding: const EdgeInsets.symmetric(horizontal: 4.0),
child: GridView.builder( child: GridView.builder(
controller: sheetScrollController, controller: scrollController,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
//Sync values for itemHeight calculation _calculateItemHeight //Sync values for itemHeight calculation _calculateItemHeight
childAspectRatio: 0.75, childAspectRatio: 0.75,
@@ -745,26 +756,27 @@ class ViewTabTreesSheetWidget extends HookConsumerWidget {
), ),
], ],
), ),
Padding( if (showNewTabFab)
padding: const EdgeInsets.only( Padding(
top: _TabSheetHeader.headerSize + 4, padding: const EdgeInsets.only(
right: 4, top: _TabViewHeader.headerSize + 4,
), right: 4,
child: FloatingActionButton.small( ),
onPressed: () async { child: FloatingActionButton.small(
final settings = ref.read(generalSettingsWithDefaultsProvider); onPressed: () async {
final settings = ref.read(generalSettingsWithDefaultsProvider);
await SearchRoute( await SearchRoute(
tabType: tabType:
ref.read(selectedTabTypeProvider) ?? ref.read(selectedTabTypeProvider) ??
settings.defaultCreateTabType, settings.defaultCreateTabType,
).push(context); ).push(context);
onClose(); onClose();
}, },
child: const Icon(Icons.add), child: const Icon(Icons.add),
),
), ),
),
], ],
); );
} }
@@ -20,7 +20,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:flutter_material_design_icons/flutter_material_design_icons.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:hooks_riverpod/hooks_riverpod.dart';
import 'package:weblibre/core/routing/routes.dart'; import 'package:weblibre/core/routing/routes.dart';
import 'package:weblibre/features/bangs/data/models/bang_data.dart'; import 'package:weblibre/features/bangs/data/models/bang_data.dart';
@@ -120,7 +119,7 @@ class SearchScreen extends HookConsumerWidget {
if (context.mounted) { if (context.mounted) {
ref.read(bottomSheetControllerProvider.notifier).requestDismiss(); ref.read(bottomSheetControllerProvider.notifier).requestDismiss();
context.pop(); BrowserRoute().go(context);
} }
} }
} }
@@ -226,7 +225,7 @@ class SearchScreen extends HookConsumerWidget {
.read(bottomSheetControllerProvider.notifier) .read(bottomSheetControllerProvider.notifier)
.requestDismiss(); .requestDismiss();
context.pop(); BrowserRoute().go(context);
} }
} }
} }
@@ -21,11 +21,11 @@ import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:flutter_material_design_icons/flutter_material_design_icons.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:hooks_riverpod/hooks_riverpod.dart';
import 'package:nullability/nullability.dart'; import 'package:nullability/nullability.dart';
import 'package:skeletonizer/skeletonizer.dart'; import 'package:skeletonizer/skeletonizer.dart';
import 'package:sliver_tools/sliver_tools.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/controllers/bottom_sheet.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/search/domain/providers/engine_suggestions.dart'; import 'package:weblibre/features/geckoview/features/search/domain/providers/engine_suggestions.dart';
@@ -129,7 +129,7 @@ class HistorySuggestions extends HookConsumerWidget {
) )
.requestDismiss(); .requestDismiss();
context.pop(); BrowserRoute().go(context);
} }
} }
} }
@@ -21,10 +21,10 @@ import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:flutter_markdown/flutter_markdown.dart'; import 'package:flutter_markdown/flutter_markdown.dart';
import 'package:go_router/go_router.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:nullability/nullability.dart'; import 'package:nullability/nullability.dart';
import 'package:sliver_tools/sliver_tools.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/controllers/bottom_sheet.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/browser/domain/providers.dart'; import 'package:weblibre/features/geckoview/features/browser/domain/providers.dart';
@@ -175,7 +175,7 @@ class TabSearch extends HookConsumerWidget {
.read(bottomSheetControllerProvider.notifier) .read(bottomSheetControllerProvider.notifier)
.requestDismiss(); .requestDismiss();
context.pop(); BrowserRoute().go(context);
} }
}, },
); );
@@ -10,7 +10,7 @@ import 'package:skeletonizer/skeletonizer.dart';
import 'package:weblibre/core/logger.dart'; import 'package:weblibre/core/logger.dart';
import 'package:weblibre/core/routing/routes.dart'; import 'package:weblibre/core/routing/routes.dart';
import 'package:weblibre/core/uuid.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/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/data/models/container_data.dart';
import 'package:weblibre/features/geckoview/features/tabs/domain/repositories/container.dart'; import 'package:weblibre/features/geckoview/features/tabs/domain/repositories/container.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(
padding: const EdgeInsets.symmetric( padding: const EdgeInsets.symmetric(
horizontal: 16.0, horizontal: 16.0,
@@ -70,6 +70,7 @@ class GeneralSettings with FastEquatable {
final bool autoHideTabBar; final bool autoHideTabBar;
final TabBarSwipeAction tabBarSwipeAction; final TabBarSwipeAction tabBarSwipeAction;
final Duration historyAutoCleanInterval; final Duration historyAutoCleanInterval;
final bool tabViewBottomSheet;
GeneralSettings({ GeneralSettings({
required this.themeMode, required this.themeMode,
@@ -86,6 +87,7 @@ class GeneralSettings with FastEquatable {
required this.autoHideTabBar, required this.autoHideTabBar,
required this.tabBarSwipeAction, required this.tabBarSwipeAction,
required this.historyAutoCleanInterval, required this.historyAutoCleanInterval,
required this.tabViewBottomSheet,
}); });
GeneralSettings.withDefaults({ GeneralSettings.withDefaults({
@@ -103,6 +105,7 @@ class GeneralSettings with FastEquatable {
bool? autoHideTabBar, bool? autoHideTabBar,
TabBarSwipeAction? tabBarSwipeAction, TabBarSwipeAction? tabBarSwipeAction,
Duration? historyAutoCleanInterval, Duration? historyAutoCleanInterval,
bool? tabViewBottomSheet,
}) : themeMode = themeMode ?? ThemeMode.dark, }) : themeMode = themeMode ?? ThemeMode.dark,
enableReadability = enableReadability ?? true, enableReadability = enableReadability ?? true,
enforceReadability = enforceReadability ?? false, enforceReadability = enforceReadability ?? false,
@@ -118,7 +121,8 @@ class GeneralSettings with FastEquatable {
tabBarSwipeAction = tabBarSwipeAction =
tabBarSwipeAction ?? TabBarSwipeAction.switchLastOpened, tabBarSwipeAction ?? TabBarSwipeAction.switchLastOpened,
historyAutoCleanInterval = historyAutoCleanInterval =
historyAutoCleanInterval ?? const Duration(days: 90); historyAutoCleanInterval ?? const Duration(days: 90),
tabViewBottomSheet = tabViewBottomSheet ?? false;
factory GeneralSettings.fromJson(Map<String, dynamic> json) => factory GeneralSettings.fromJson(Map<String, dynamic> json) =>
_$GeneralSettingsFromJson(json); _$GeneralSettingsFromJson(json);
@@ -141,5 +145,6 @@ class GeneralSettings with FastEquatable {
autoHideTabBar, autoHideTabBar,
tabBarSwipeAction, tabBarSwipeAction,
historyAutoCleanInterval, historyAutoCleanInterval,
tabViewBottomSheet,
]; ];
} }
@@ -41,6 +41,8 @@ abstract class _$GeneralSettingsCWProxy {
GeneralSettings historyAutoCleanInterval(Duration historyAutoCleanInterval); GeneralSettings historyAutoCleanInterval(Duration historyAutoCleanInterval);
GeneralSettings tabViewBottomSheet(bool tabViewBottomSheet);
/// Creates a new instance with the provided field values. /// 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)`. /// 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, bool autoHideTabBar,
TabBarSwipeAction tabBarSwipeAction, TabBarSwipeAction tabBarSwipeAction,
Duration historyAutoCleanInterval, Duration historyAutoCleanInterval,
bool tabViewBottomSheet,
}); });
} }
@@ -131,6 +134,10 @@ class _$GeneralSettingsCWProxyImpl implements _$GeneralSettingsCWProxy {
GeneralSettings historyAutoCleanInterval(Duration historyAutoCleanInterval) => GeneralSettings historyAutoCleanInterval(Duration historyAutoCleanInterval) =>
call(historyAutoCleanInterval: historyAutoCleanInterval); call(historyAutoCleanInterval: historyAutoCleanInterval);
@override
GeneralSettings tabViewBottomSheet(bool tabViewBottomSheet) =>
call(tabViewBottomSheet: tabViewBottomSheet);
@override @override
/// Creates a new instance with the provided field values. /// 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)`. /// 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? autoHideTabBar = const $CopyWithPlaceholder(),
Object? tabBarSwipeAction = const $CopyWithPlaceholder(), Object? tabBarSwipeAction = const $CopyWithPlaceholder(),
Object? historyAutoCleanInterval = const $CopyWithPlaceholder(), Object? historyAutoCleanInterval = const $CopyWithPlaceholder(),
Object? tabViewBottomSheet = const $CopyWithPlaceholder(),
}) { }) {
return GeneralSettings( return GeneralSettings(
themeMode: themeMode == const $CopyWithPlaceholder() || themeMode == null themeMode: themeMode == const $CopyWithPlaceholder() || themeMode == null
@@ -236,6 +244,12 @@ class _$GeneralSettingsCWProxyImpl implements _$GeneralSettingsCWProxy {
? _value.historyAutoCleanInterval ? _value.historyAutoCleanInterval
// ignore: cast_nullable_to_non_nullable // ignore: cast_nullable_to_non_nullable
: historyAutoCleanInterval as Duration, : 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<String, dynamic> json) =>
: Duration( : Duration(
microseconds: (json['historyAutoCleanInterval'] as num).toInt(), microseconds: (json['historyAutoCleanInterval'] as num).toInt(),
), ),
tabViewBottomSheet: json['tabViewBottomSheet'] as bool?,
); );
Map<String, dynamic> _$GeneralSettingsToJson( Map<String, dynamic> _$GeneralSettingsToJson(
@@ -314,6 +329,7 @@ Map<String, dynamic> _$GeneralSettingsToJson(
'autoHideTabBar': instance.autoHideTabBar, 'autoHideTabBar': instance.autoHideTabBar,
'tabBarSwipeAction': _$TabBarSwipeActionEnumMap[instance.tabBarSwipeAction]!, 'tabBarSwipeAction': _$TabBarSwipeActionEnumMap[instance.tabBarSwipeAction]!,
'historyAutoCleanInterval': instance.historyAutoCleanInterval.inMicroseconds, 'historyAutoCleanInterval': instance.historyAutoCleanInterval.inMicroseconds,
'tabViewBottomSheet': instance.tabViewBottomSheet,
}; };
const _$ThemeModeEnumMap = { const _$ThemeModeEnumMap = {
@@ -100,6 +100,10 @@ class GeneralSettingsRepository extends _$GeneralSettingsRepository {
DriftSqlType.int, DriftSqlType.int,
db.typeMapping, db.typeMapping,
), ),
'tabViewBottomSheet': settings['tabViewBottomSheet']?.readAs(
DriftSqlType.bool,
db.typeMapping,
),
}); });
} }
@@ -35,7 +35,7 @@ final class GeneralSettingsRepositoryProvider
} }
String _$generalSettingsRepositoryHash() => String _$generalSettingsRepositoryHash() =>
r'a9b21f9c5ef50bb8a8761995ed2fea723375b80e'; r'7ab8eb72c43ed9486bf9fa8b13a6cce83e2b3d66';
abstract class _$GeneralSettingsRepository abstract class _$GeneralSettingsRepository
extends $StreamNotifier<GeneralSettings> { extends $StreamNotifier<GeneralSettings> {
@@ -58,7 +58,7 @@ final class CompletePageInfoProvider
} }
} }
String _$completePageInfoHash() => r'e033a1439ee47af5aef0fd6bf3e8a41d5e88c6fe'; String _$completePageInfoHash() => r'edf5538b913d1ebb285c15ec83644818fe90707d';
final class CompletePageInfoFamily extends $Family final class CompletePageInfoFamily extends $Family
with with