app bar positioning
This commit is contained in:
@@ -300,13 +300,13 @@ AsyncValue<String?> selectedTabContainerId(Ref ref) {
|
|||||||
return const AsyncData(null);
|
return const AsyncData(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Riverpod()
|
// @Riverpod()
|
||||||
Stream<int> tabScrollY(Ref ref, String? tabId, Duration sampleTime) {
|
// Stream<int> tabScrollY(Ref ref, String? tabId, Duration sampleTime) {
|
||||||
final eventService = ref.watch(eventServiceProvider);
|
// final eventService = ref.watch(eventServiceProvider);
|
||||||
|
|
||||||
return eventService.scrollEvent
|
// return eventService.scrollEvent
|
||||||
.where((event) => event.tabId == tabId)
|
// .where((event) => event.tabId == tabId)
|
||||||
.sampleTime(sampleTime)
|
// .sampleTime(sampleTime)
|
||||||
.map((event) => event.scrollY)
|
// .map((event) => event.scrollY)
|
||||||
.asBroadcastStream();
|
// .asBroadcastStream();
|
||||||
}
|
// }
|
||||||
|
|||||||
@@ -337,72 +337,3 @@ final class SelectedTabContainerIdProvider
|
|||||||
|
|
||||||
String _$selectedTabContainerIdHash() =>
|
String _$selectedTabContainerIdHash() =>
|
||||||
r'07899d29f69654d3b314d0da945ad402b1003b41';
|
r'07899d29f69654d3b314d0da945ad402b1003b41';
|
||||||
|
|
||||||
@ProviderFor(tabScrollY)
|
|
||||||
const tabScrollYProvider = TabScrollYFamily._();
|
|
||||||
|
|
||||||
final class TabScrollYProvider
|
|
||||||
extends $FunctionalProvider<AsyncValue<int>, int, Stream<int>>
|
|
||||||
with $FutureModifier<int>, $StreamProvider<int> {
|
|
||||||
const TabScrollYProvider._({
|
|
||||||
required TabScrollYFamily super.from,
|
|
||||||
required (String?, Duration) super.argument,
|
|
||||||
}) : super(
|
|
||||||
retry: null,
|
|
||||||
name: r'tabScrollYProvider',
|
|
||||||
isAutoDispose: true,
|
|
||||||
dependencies: null,
|
|
||||||
$allTransitiveDependencies: null,
|
|
||||||
);
|
|
||||||
|
|
||||||
@override
|
|
||||||
String debugGetCreateSourceHash() => _$tabScrollYHash();
|
|
||||||
|
|
||||||
@override
|
|
||||||
String toString() {
|
|
||||||
return r'tabScrollYProvider'
|
|
||||||
''
|
|
||||||
'$argument';
|
|
||||||
}
|
|
||||||
|
|
||||||
@$internal
|
|
||||||
@override
|
|
||||||
$StreamProviderElement<int> $createElement($ProviderPointer pointer) =>
|
|
||||||
$StreamProviderElement(pointer);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Stream<int> create(Ref ref) {
|
|
||||||
final argument = this.argument as (String?, Duration);
|
|
||||||
return tabScrollY(ref, argument.$1, argument.$2);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool operator ==(Object other) {
|
|
||||||
return other is TabScrollYProvider && other.argument == argument;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
int get hashCode {
|
|
||||||
return argument.hashCode;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
String _$tabScrollYHash() => r'dcb1e82b42bab98c4fc2ee4c7a22f37a28b8ce9e';
|
|
||||||
|
|
||||||
final class TabScrollYFamily extends $Family
|
|
||||||
with $FunctionalFamilyOverride<Stream<int>, (String?, Duration)> {
|
|
||||||
const TabScrollYFamily._()
|
|
||||||
: super(
|
|
||||||
retry: null,
|
|
||||||
name: r'tabScrollYProvider',
|
|
||||||
dependencies: null,
|
|
||||||
$allTransitiveDependencies: null,
|
|
||||||
isAutoDispose: true,
|
|
||||||
);
|
|
||||||
|
|
||||||
TabScrollYProvider call(String? tabId, Duration sampleTime) =>
|
|
||||||
TabScrollYProvider._(argument: (tabId, sampleTime), from: this);
|
|
||||||
|
|
||||||
@override
|
|
||||||
String toString() => r'tabScrollYProvider';
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -50,20 +50,27 @@ import 'package:weblibre/features/geckoview/features/browser/presentation/widget
|
|||||||
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';
|
||||||
|
import 'package:weblibre/features/user/data/models/general_settings.dart';
|
||||||
import 'package:weblibre/features/user/domain/repositories/general_settings.dart';
|
import 'package:weblibre/features/user/domain/repositories/general_settings.dart';
|
||||||
import 'package:weblibre/utils/ui_helper.dart' as ui_helper;
|
import 'package:weblibre/utils/ui_helper.dart' as ui_helper;
|
||||||
|
|
||||||
class _TabBar extends HookConsumerWidget {
|
class _TabBar extends HookConsumerWidget {
|
||||||
|
final bool showMainToolbar;
|
||||||
final bool showContextualToolbar;
|
final bool showContextualToolbar;
|
||||||
final bool showQuickTabSwitcherBar;
|
final bool showQuickTabSwitcherBar;
|
||||||
final ValueNotifier<bool> showAppBar;
|
final ValueNotifier<bool> displayAppBar;
|
||||||
final ValueNotifier<PersistentBottomSheetController?> sheetController;
|
final ValueNotifier<PersistentBottomSheetController?> sheetController;
|
||||||
|
final Stream<Offset>? pointerMoveEvents;
|
||||||
|
final TabBarPosition tabBarPosition;
|
||||||
|
|
||||||
const _TabBar({
|
const _TabBar({
|
||||||
|
required this.showMainToolbar,
|
||||||
required this.showContextualToolbar,
|
required this.showContextualToolbar,
|
||||||
required this.showQuickTabSwitcherBar,
|
required this.showQuickTabSwitcherBar,
|
||||||
required this.showAppBar,
|
required this.displayAppBar,
|
||||||
required this.sheetController,
|
required this.sheetController,
|
||||||
|
required this.tabBarPosition,
|
||||||
|
required this.pointerMoveEvents,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -75,29 +82,43 @@ class _TabBar extends HookConsumerWidget {
|
|||||||
selectedTabStateProvider.select((value) => value?.isFullScreen ?? false),
|
selectedTabStateProvider.select((value) => value?.isFullScreen ?? false),
|
||||||
);
|
);
|
||||||
|
|
||||||
final autoHideTabBar = ref.watch(
|
final autoHideTabBar = switch (tabBarPosition) {
|
||||||
generalSettingsWithDefaultsProvider.select(
|
TabBarPosition.top => false,
|
||||||
(value) => value.autoHideTabBar,
|
TabBarPosition.bottom => ref.watch(
|
||||||
|
generalSettingsWithDefaultsProvider.select(
|
||||||
|
(value) => value.autoHideTabBar,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
};
|
||||||
|
|
||||||
|
final appBarVisible = tabBarPosition == TabBarPosition.top
|
||||||
|
? !ref.watch(tabBarDismissableControllerProvider)
|
||||||
|
: useValueListenable(displayAppBar);
|
||||||
|
|
||||||
if (!autoHideTabBar) {
|
if (!autoHideTabBar) {
|
||||||
return Visibility(
|
return Visibility(
|
||||||
visible: !tabInFullScreen,
|
visible: !tabInFullScreen && appBarVisible,
|
||||||
child: BrowserBottomAppBar(
|
child: switch (tabBarPosition) {
|
||||||
displayedSheet: displayedSheet,
|
TabBarPosition.top => BrowserTopAppBar(
|
||||||
showContextualToolbar: showContextualToolbar,
|
showMainToolbar: showMainToolbar,
|
||||||
showQuickTabSwitcherBar: showQuickTabSwitcherBar,
|
showContextualToolbar: showContextualToolbar,
|
||||||
),
|
showQuickTabSwitcherBar: showQuickTabSwitcherBar,
|
||||||
|
),
|
||||||
|
TabBarPosition.bottom => BrowserBottomAppBar(
|
||||||
|
displayedSheet: displayedSheet,
|
||||||
|
showMainToolbar: showMainToolbar,
|
||||||
|
showContextualToolbar: showContextualToolbar,
|
||||||
|
showQuickTabSwitcherBar: showQuickTabSwitcherBar,
|
||||||
|
),
|
||||||
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
final appBarVisible = useValueListenable(showAppBar);
|
|
||||||
final diffAcc = useRef(0.0);
|
final diffAcc = useRef(0.0);
|
||||||
|
|
||||||
void resetHiddenState() {
|
void resetHiddenState() {
|
||||||
if (!ref.read(tabBarDismissableControllerProvider)) {
|
if (!ref.read(tabBarDismissableControllerProvider)) {
|
||||||
showAppBar.value = true;
|
displayAppBar.value = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
diffAcc.value = 0.0;
|
diffAcc.value = 0.0;
|
||||||
@@ -137,12 +158,10 @@ class _TabBar extends HookConsumerWidget {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
ref.listen(tabScrollYProvider(tabId, const Duration(milliseconds: 50)), (
|
useOnStreamChange(
|
||||||
previous,
|
pointerMoveEvents,
|
||||||
next,
|
onData: (event) {
|
||||||
) {
|
final diff = event.dy;
|
||||||
if (previous?.value != null && next.value != null) {
|
|
||||||
final diff = previous!.value! - next.value!;
|
|
||||||
if (diff < 0) {
|
if (diff < 0) {
|
||||||
if (diffAcc.value > 0) {
|
if (diffAcc.value > 0) {
|
||||||
diffAcc.value = 0.0;
|
diffAcc.value = 0.0;
|
||||||
@@ -150,7 +169,7 @@ class _TabBar extends HookConsumerWidget {
|
|||||||
|
|
||||||
diffAcc.value += diff;
|
diffAcc.value += diff;
|
||||||
if (diffAcc.value.abs() > kToolbarHeight * 1.5) {
|
if (diffAcc.value.abs() > kToolbarHeight * 1.5) {
|
||||||
showAppBar.value = false;
|
displayAppBar.value = false;
|
||||||
}
|
}
|
||||||
} else if (diff > 0) {
|
} else if (diff > 0) {
|
||||||
if (diffAcc.value < 0) {
|
if (diffAcc.value < 0) {
|
||||||
@@ -162,18 +181,26 @@ class _TabBar extends HookConsumerWidget {
|
|||||||
resetHiddenState();
|
resetHiddenState();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
});
|
);
|
||||||
|
|
||||||
return Visibility(
|
return Visibility(
|
||||||
visible:
|
visible:
|
||||||
sheetController.value != null || (!tabInFullScreen && appBarVisible),
|
sheetController.value != null || (!tabInFullScreen && appBarVisible),
|
||||||
maintainState: true,
|
maintainState: true,
|
||||||
child: BrowserBottomAppBar(
|
child: switch (tabBarPosition) {
|
||||||
displayedSheet: displayedSheet,
|
TabBarPosition.top => BrowserTopAppBar(
|
||||||
showContextualToolbar: showContextualToolbar,
|
showMainToolbar: showMainToolbar,
|
||||||
showQuickTabSwitcherBar: showQuickTabSwitcherBar,
|
showContextualToolbar: showContextualToolbar,
|
||||||
),
|
showQuickTabSwitcherBar: showQuickTabSwitcherBar,
|
||||||
|
),
|
||||||
|
TabBarPosition.bottom => BrowserBottomAppBar(
|
||||||
|
showMainToolbar: showMainToolbar,
|
||||||
|
displayedSheet: displayedSheet,
|
||||||
|
showContextualToolbar: showContextualToolbar,
|
||||||
|
showQuickTabSwitcherBar: showQuickTabSwitcherBar,
|
||||||
|
),
|
||||||
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -191,7 +218,13 @@ class BrowserScreen extends HookConsumerWidget {
|
|||||||
|
|
||||||
final overlayController = useOverlayPortalController();
|
final overlayController = useOverlayPortalController();
|
||||||
|
|
||||||
final showContextualAppBar = ref.watch(
|
final tabBarPosition = ref.watch(
|
||||||
|
generalSettingsWithDefaultsProvider.select(
|
||||||
|
(value) => value.tabBarPosition,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
final showContextualToolbar = ref.watch(
|
||||||
generalSettingsWithDefaultsProvider.select(
|
generalSettingsWithDefaultsProvider.select(
|
||||||
(value) => value.tabBarShowContextualBar,
|
(value) => value.tabBarShowContextualBar,
|
||||||
),
|
),
|
||||||
@@ -203,10 +236,14 @@ class BrowserScreen extends HookConsumerWidget {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
final showAppBar = useValueNotifier(true);
|
final displayAppBar = useValueNotifier(true);
|
||||||
|
final removeTopAppBar = useState(false);
|
||||||
|
|
||||||
ref.listen(tabBarDismissableControllerProvider, (previous, next) {
|
ref.listen(tabBarDismissableControllerProvider, (previous, next) {
|
||||||
showAppBar.value = !next;
|
displayAppBar.value = !next;
|
||||||
|
if (tabBarPosition == TabBarPosition.bottom) {
|
||||||
|
removeTopAppBar.value = next;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
ref.listen(overlayControllerProvider, (previous, next) {
|
ref.listen(overlayControllerProvider, (previous, next) {
|
||||||
@@ -244,6 +281,8 @@ class BrowserScreen extends HookConsumerWidget {
|
|||||||
|
|
||||||
final sheetController = useState<PersistentBottomSheetController?>(null);
|
final sheetController = useState<PersistentBottomSheetController?>(null);
|
||||||
|
|
||||||
|
final pointerMoveEventsController = useStreamController<Offset>();
|
||||||
|
|
||||||
return PopScope(
|
return PopScope(
|
||||||
//We need this for BackButtonListener to work downstream
|
//We need this for BackButtonListener to work downstream
|
||||||
//No direct pop result will be handled here
|
//No direct pop result will be handled here
|
||||||
@@ -256,23 +295,50 @@ class BrowserScreen extends HookConsumerWidget {
|
|||||||
//This causes issues with a non dismissable barrier pushed, we ahve our own barrier and this does seem to have issues when dismissing, so disable it completely
|
//This causes issues with a non dismissable barrier pushed, we ahve our own barrier and this does seem to have issues when dismissing, so disable it completely
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
body: Column(
|
appBar: (tabBarPosition == TabBarPosition.top)
|
||||||
children: [
|
? PreferredSize(
|
||||||
_TabBar(
|
preferredSize: BrowserTopAppBar(
|
||||||
showAppBar: showAppBar,
|
showMainToolbar: true,
|
||||||
sheetController: sheetController,
|
showContextualToolbar: showContextualToolbar,
|
||||||
showContextualToolbar: showContextualAppBar,
|
showQuickTabSwitcherBar: showQuickTabSwitcherBar,
|
||||||
showQuickTabSwitcherBar: showQuickTabSwitcherBar,
|
).preferredSize,
|
||||||
),
|
child: _TabBar(
|
||||||
Expanded(
|
tabBarPosition: TabBarPosition.top,
|
||||||
child: _Browser(
|
showMainToolbar: true,
|
||||||
overlayController: overlayController,
|
displayAppBar: displayAppBar,
|
||||||
sheetController: sheetController,
|
sheetController: sheetController,
|
||||||
showAppBar: showAppBar,
|
showContextualToolbar: showContextualToolbar,
|
||||||
tabInFullScreen: tabInFullScreen,
|
showQuickTabSwitcherBar: showQuickTabSwitcherBar,
|
||||||
),
|
pointerMoveEvents: null,
|
||||||
),
|
),
|
||||||
],
|
)
|
||||||
|
: removeTopAppBar.value
|
||||||
|
? const PreferredSize(
|
||||||
|
preferredSize: Size.zero,
|
||||||
|
child: SizedBox.shrink(),
|
||||||
|
)
|
||||||
|
: null,
|
||||||
|
bottomNavigationBar: _TabBar(
|
||||||
|
tabBarPosition: TabBarPosition.bottom,
|
||||||
|
displayAppBar: displayAppBar,
|
||||||
|
sheetController: sheetController,
|
||||||
|
showMainToolbar: tabBarPosition == TabBarPosition.bottom,
|
||||||
|
showContextualToolbar: showContextualToolbar,
|
||||||
|
showQuickTabSwitcherBar: showQuickTabSwitcherBar,
|
||||||
|
pointerMoveEvents: pointerMoveEventsController.stream,
|
||||||
|
),
|
||||||
|
body: _Browser(
|
||||||
|
overlayController: overlayController,
|
||||||
|
sheetController: sheetController,
|
||||||
|
displayAppBar: displayAppBar,
|
||||||
|
tabInFullScreen: tabInFullScreen,
|
||||||
|
pointerMoveEventSink: pointerMoveEventsController.sink,
|
||||||
|
bottomAppBarSize: BrowserBottomAppBar(
|
||||||
|
showMainToolbar: false,
|
||||||
|
showContextualToolbar: showContextualToolbar,
|
||||||
|
showQuickTabSwitcherBar: showQuickTabSwitcherBar,
|
||||||
|
displayedSheet: null,
|
||||||
|
).preferredSize,
|
||||||
),
|
),
|
||||||
floatingActionButton: BrowserFab(),
|
floatingActionButton: BrowserFab(),
|
||||||
),
|
),
|
||||||
@@ -286,15 +352,19 @@ class _Browser extends HookConsumerWidget {
|
|||||||
|
|
||||||
final OverlayPortalController overlayController;
|
final OverlayPortalController overlayController;
|
||||||
final ValueNotifier<PersistentBottomSheetController?> sheetController;
|
final ValueNotifier<PersistentBottomSheetController?> sheetController;
|
||||||
final ValueNotifier<bool> showAppBar;
|
final ValueNotifier<bool> displayAppBar;
|
||||||
|
final StreamSink<Offset> pointerMoveEventSink;
|
||||||
|
final Size bottomAppBarSize;
|
||||||
|
|
||||||
final bool tabInFullScreen;
|
final bool tabInFullScreen;
|
||||||
|
|
||||||
const _Browser({
|
const _Browser({
|
||||||
required this.overlayController,
|
required this.overlayController,
|
||||||
required this.sheetController,
|
required this.sheetController,
|
||||||
required this.showAppBar,
|
required this.displayAppBar,
|
||||||
required this.tabInFullScreen,
|
required this.tabInFullScreen,
|
||||||
|
required this.pointerMoveEventSink,
|
||||||
|
required this.bottomAppBarSize,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -363,6 +433,7 @@ class _Browser extends HookConsumerWidget {
|
|||||||
child: _ViewUrlSheet(
|
child: _ViewUrlSheet(
|
||||||
initialTabState: parameter.tabState,
|
initialTabState: parameter.tabState,
|
||||||
maxChildSize: relativeSafeArea,
|
maxChildSize: relativeSafeArea,
|
||||||
|
bottomAppBarSize: bottomAppBarSize,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
@@ -453,7 +524,7 @@ class _Browser extends HookConsumerWidget {
|
|||||||
|
|
||||||
//Make sure app bar is visible
|
//Make sure app bar is visible
|
||||||
if (!ref.read(tabBarDismissableControllerProvider)) {
|
if (!ref.read(tabBarDismissableControllerProvider)) {
|
||||||
showAppBar.value = true;
|
displayAppBar.value = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tabState?.isLoading == true) {
|
if (tabState?.isLoading == true) {
|
||||||
@@ -530,6 +601,7 @@ class _Browser extends HookConsumerWidget {
|
|||||||
child: _BrowserView(
|
child: _BrowserView(
|
||||||
sheetDisplayed: sheetController.value != null,
|
sheetDisplayed: sheetController.value != null,
|
||||||
isFullscreen: tabInFullScreen,
|
isFullscreen: tabInFullScreen,
|
||||||
|
pointerMoveEventSink: pointerMoveEventSink,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -542,10 +614,12 @@ class _Browser extends HookConsumerWidget {
|
|||||||
class _BrowserView extends StatelessWidget {
|
class _BrowserView extends StatelessWidget {
|
||||||
final bool sheetDisplayed;
|
final bool sheetDisplayed;
|
||||||
final bool isFullscreen;
|
final bool isFullscreen;
|
||||||
|
final StreamSink<Offset>? pointerMoveEventSink;
|
||||||
|
|
||||||
const _BrowserView({
|
const _BrowserView({
|
||||||
required this.sheetDisplayed,
|
required this.sheetDisplayed,
|
||||||
required this.isFullscreen,
|
required this.isFullscreen,
|
||||||
|
this.pointerMoveEventSink,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -559,7 +633,7 @@ class _BrowserView extends StatelessWidget {
|
|||||||
left: !isFullscreen,
|
left: !isFullscreen,
|
||||||
child: Stack(
|
child: Stack(
|
||||||
children: [
|
children: [
|
||||||
const BrowserView(),
|
BrowserView(pointerMoveEventSink: pointerMoveEventSink),
|
||||||
Positioned(
|
Positioned(
|
||||||
bottom: 0,
|
bottom: 0,
|
||||||
left: 0,
|
left: 0,
|
||||||
@@ -627,8 +701,13 @@ class _BrowserView extends StatelessWidget {
|
|||||||
class _ViewUrlSheet extends HookConsumerWidget {
|
class _ViewUrlSheet extends HookConsumerWidget {
|
||||||
final double maxChildSize;
|
final double maxChildSize;
|
||||||
final TabState initialTabState;
|
final TabState initialTabState;
|
||||||
|
final Size bottomAppBarSize;
|
||||||
|
|
||||||
const _ViewUrlSheet({required this.initialTabState, this.maxChildSize = 1.0});
|
const _ViewUrlSheet({
|
||||||
|
required this.initialTabState,
|
||||||
|
required this.bottomAppBarSize,
|
||||||
|
this.maxChildSize = 1.0,
|
||||||
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
@@ -666,6 +745,7 @@ class _ViewUrlSheet extends HookConsumerWidget {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
initialHeight: initialHeight,
|
initialHeight: initialHeight,
|
||||||
|
bottomAppBarHeight: bottomAppBarSize.height,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|||||||
+224
-87
@@ -44,35 +44,114 @@ import 'package:weblibre/features/geckoview/features/browser/presentation/widget
|
|||||||
import 'package:weblibre/features/geckoview/features/browser/presentation/widgets/tab_creation_menu.dart';
|
import 'package:weblibre/features/geckoview/features/browser/presentation/widgets/tab_creation_menu.dart';
|
||||||
import 'package:weblibre/features/geckoview/features/browser/presentation/widgets/tab_menu.dart';
|
import 'package:weblibre/features/geckoview/features/browser/presentation/widgets/tab_menu.dart';
|
||||||
import 'package:weblibre/features/geckoview/features/browser/presentation/widgets/tabs_action_button.dart';
|
import 'package:weblibre/features/geckoview/features/browser/presentation/widgets/tabs_action_button.dart';
|
||||||
|
import 'package:weblibre/features/geckoview/features/history/domain/repositories/history.dart';
|
||||||
import 'package:weblibre/features/geckoview/features/readerview/presentation/controllers/readerable.dart';
|
import 'package:weblibre/features/geckoview/features/readerview/presentation/controllers/readerable.dart';
|
||||||
import 'package:weblibre/features/geckoview/features/readerview/presentation/widgets/reader_button.dart';
|
import 'package:weblibre/features/geckoview/features/readerview/presentation/widgets/reader_button.dart';
|
||||||
import 'package:weblibre/features/tor/domain/services/tor_proxy.dart';
|
import 'package:weblibre/features/tor/domain/services/tor_proxy.dart';
|
||||||
import 'package:weblibre/features/user/data/models/general_settings.dart';
|
import 'package:weblibre/features/user/data/models/general_settings.dart';
|
||||||
import 'package:weblibre/features/user/domain/providers.dart';
|
import 'package:weblibre/features/user/domain/providers.dart';
|
||||||
import 'package:weblibre/features/user/domain/repositories/general_settings.dart';
|
import 'package:weblibre/features/user/domain/repositories/general_settings.dart';
|
||||||
|
import 'package:weblibre/presentation/hooks/cached_future.dart';
|
||||||
import 'package:weblibre/presentation/hooks/menu_controller.dart';
|
import 'package:weblibre/presentation/hooks/menu_controller.dart';
|
||||||
import 'package:weblibre/presentation/icons/tor_icons.dart';
|
import 'package:weblibre/presentation/icons/tor_icons.dart';
|
||||||
import 'package:weblibre/presentation/widgets/selectable_chips.dart';
|
import 'package:weblibre/presentation/widgets/selectable_chips.dart';
|
||||||
import 'package:weblibre/presentation/widgets/url_icon.dart';
|
import 'package:weblibre/presentation/widgets/url_icon.dart';
|
||||||
import 'package:weblibre/utils/ui_helper.dart' as ui_helper;
|
import 'package:weblibre/utils/ui_helper.dart' as ui_helper;
|
||||||
|
|
||||||
class BrowserBottomAppBar extends HookConsumerWidget {
|
class BrowserTopAppBar extends HookConsumerWidget {
|
||||||
|
final bool showMainToolbar;
|
||||||
final bool showContextualToolbar;
|
final bool showContextualToolbar;
|
||||||
final bool showQuickTabSwitcherBar;
|
final bool showQuickTabSwitcherBar;
|
||||||
|
|
||||||
const BrowserBottomAppBar({
|
late final BrowserTabBar _tabBar;
|
||||||
|
late final _size = Size.fromHeight(_tabBar.getToolbarHeight());
|
||||||
|
|
||||||
|
BrowserTopAppBar({
|
||||||
|
required this.showMainToolbar,
|
||||||
|
required this.showContextualToolbar,
|
||||||
|
required this.showQuickTabSwitcherBar,
|
||||||
|
}) {
|
||||||
|
_tabBar = BrowserTabBar(
|
||||||
|
showMainToolbar: showMainToolbar,
|
||||||
|
displayedSheet: null,
|
||||||
|
showContextualToolbar: false,
|
||||||
|
showQuickTabSwitcherBar: false,
|
||||||
|
showMainToolbarNavigationButton: !showContextualToolbar,
|
||||||
|
showMainToolbarTabsCount: !showContextualToolbar,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
return SafeArea(
|
||||||
|
child: SizedBox(height: preferredSize.height, child: _tabBar),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Size get preferredSize => _size;
|
||||||
|
}
|
||||||
|
|
||||||
|
class BrowserBottomAppBar extends HookConsumerWidget {
|
||||||
|
final bool showMainToolbar;
|
||||||
|
final bool showContextualToolbar;
|
||||||
|
final bool showQuickTabSwitcherBar;
|
||||||
|
final Sheet? displayedSheet;
|
||||||
|
|
||||||
|
late final BrowserTabBar _tabBar;
|
||||||
|
late final _size = Size.fromHeight(_tabBar.getToolbarHeight());
|
||||||
|
|
||||||
|
BrowserBottomAppBar({
|
||||||
|
required this.showMainToolbar,
|
||||||
required this.displayedSheet,
|
required this.displayedSheet,
|
||||||
required this.showContextualToolbar,
|
required this.showContextualToolbar,
|
||||||
required this.showQuickTabSwitcherBar,
|
required this.showQuickTabSwitcherBar,
|
||||||
});
|
}) {
|
||||||
|
_tabBar = BrowserTabBar(
|
||||||
|
displayedSheet: displayedSheet,
|
||||||
|
showMainToolbar: showMainToolbar,
|
||||||
|
showContextualToolbar: showContextualToolbar,
|
||||||
|
showQuickTabSwitcherBar: showQuickTabSwitcherBar,
|
||||||
|
showMainToolbarNavigationButton: !showContextualToolbar,
|
||||||
|
showMainToolbarTabsCount: !showContextualToolbar,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
return BottomAppBar(
|
||||||
|
height: _size.height,
|
||||||
|
padding: EdgeInsets.zero,
|
||||||
|
child: _tabBar,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Size get preferredSize => _size;
|
||||||
|
}
|
||||||
|
|
||||||
|
class BrowserTabBar extends HookConsumerWidget {
|
||||||
|
final bool showMainToolbar;
|
||||||
|
final bool showContextualToolbar;
|
||||||
|
final bool showQuickTabSwitcherBar;
|
||||||
final Sheet? displayedSheet;
|
final Sheet? displayedSheet;
|
||||||
|
|
||||||
|
final bool showMainToolbarTabsCount;
|
||||||
|
final bool showMainToolbarNavigationButton;
|
||||||
|
|
||||||
|
const BrowserTabBar({
|
||||||
|
required this.showMainToolbar,
|
||||||
|
required this.displayedSheet,
|
||||||
|
required this.showContextualToolbar,
|
||||||
|
required this.showQuickTabSwitcherBar,
|
||||||
|
required this.showMainToolbarTabsCount,
|
||||||
|
required this.showMainToolbarNavigationButton,
|
||||||
|
});
|
||||||
|
|
||||||
static const contextualToolabarHeight = 54.0;
|
static const contextualToolabarHeight = 54.0;
|
||||||
static const quickTabSwitcherHeight = 48.0;
|
static const quickTabSwitcherHeight = 48.0;
|
||||||
|
|
||||||
bool get displayAppBar =>
|
bool get displayAppBar =>
|
||||||
!showContextualToolbar || displayedSheet is! ViewTabsSheet;
|
showMainToolbar &&
|
||||||
|
(!showContextualToolbar || displayedSheet is! ViewTabsSheet);
|
||||||
|
|
||||||
bool get displayQuickTabSwitcher =>
|
bool get displayQuickTabSwitcher =>
|
||||||
showQuickTabSwitcherBar && displayedSheet is! ViewTabsSheet;
|
showQuickTabSwitcherBar && displayedSheet is! ViewTabsSheet;
|
||||||
@@ -113,67 +192,68 @@ class BrowserBottomAppBar extends HookConsumerWidget {
|
|||||||
|
|
||||||
final dragStartPosition = useRef(Offset.zero);
|
final dragStartPosition = useRef(Offset.zero);
|
||||||
|
|
||||||
return BottomAppBar(
|
final toolbarHeight = useMemoized(() => getToolbarHeight());
|
||||||
height: getToolbarHeight(),
|
|
||||||
padding: EdgeInsets.zero,
|
|
||||||
child: GestureDetector(
|
|
||||||
onTap: () {
|
|
||||||
if (displayedSheet case EditUrlSheet()) {
|
|
||||||
ref.read(bottomSheetControllerProvider.notifier).requestDismiss();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
final tabState = ref.read(selectedTabStateProvider);
|
return GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
if (displayedSheet case EditUrlSheet()) {
|
||||||
|
ref.read(bottomSheetControllerProvider.notifier).requestDismiss();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (tabState != null) {
|
final tabState = ref.read(selectedTabStateProvider);
|
||||||
ref
|
|
||||||
.read(bottomSheetControllerProvider.notifier)
|
|
||||||
.show(EditUrlSheet(tabState: tabState));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onHorizontalDragStart: (details) {
|
|
||||||
dragStartPosition.value = details.globalPosition;
|
|
||||||
},
|
|
||||||
onHorizontalDragEnd: (details) async {
|
|
||||||
final distance = dragStartPosition.value - details.globalPosition;
|
|
||||||
|
|
||||||
if (distance.dx.abs() > 50 && distance.dy.abs() < 20) {
|
if (tabState != null) {
|
||||||
final selectedTab = ref.read(selectedTabProvider);
|
ref
|
||||||
final setting = await ref
|
.read(bottomSheetControllerProvider.notifier)
|
||||||
.read(generalSettingsRepositoryProvider.notifier)
|
.show(EditUrlSheet(tabState: tabState));
|
||||||
.fetchSettings();
|
}
|
||||||
|
},
|
||||||
|
onHorizontalDragStart: (details) {
|
||||||
|
dragStartPosition.value = details.globalPosition;
|
||||||
|
},
|
||||||
|
onHorizontalDragEnd: (details) async {
|
||||||
|
final distance = dragStartPosition.value - details.globalPosition;
|
||||||
|
|
||||||
if (selectedTab != null) {
|
if (distance.dx.abs() > 50 && distance.dy.abs() < 20) {
|
||||||
switch (setting.tabBarSwipeAction) {
|
final selectedTab = ref.read(selectedTabProvider);
|
||||||
case TabBarSwipeAction.switchLastOpened:
|
final setting = await ref
|
||||||
|
.read(generalSettingsRepositoryProvider.notifier)
|
||||||
|
.fetchSettings();
|
||||||
|
|
||||||
|
if (selectedTab != null) {
|
||||||
|
switch (setting.tabBarSwipeAction) {
|
||||||
|
case TabBarSwipeAction.switchLastOpened:
|
||||||
|
await ref
|
||||||
|
.read(tabRepositoryProvider.notifier)
|
||||||
|
.selectPreviouslyOpenedTab(selectedTab);
|
||||||
|
case TabBarSwipeAction.navigateOrderedTabs:
|
||||||
|
if (distance.dx < 0) {
|
||||||
await ref
|
await ref
|
||||||
.read(tabRepositoryProvider.notifier)
|
.read(tabRepositoryProvider.notifier)
|
||||||
.selectPreviouslyOpenedTab(selectedTab);
|
.selectPreviousTab(selectedTab);
|
||||||
case TabBarSwipeAction.navigateOrderedTabs:
|
} else {
|
||||||
if (distance.dx < 0) {
|
await ref
|
||||||
await ref
|
.read(tabRepositoryProvider.notifier)
|
||||||
.read(tabRepositoryProvider.notifier)
|
.selectNextTab(selectedTab);
|
||||||
.selectPreviousTab(selectedTab);
|
}
|
||||||
} else {
|
|
||||||
await ref
|
|
||||||
.read(tabRepositoryProvider.notifier)
|
|
||||||
.selectNextTab(selectedTab);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else if (distance.dy < 20 && distance.dx.abs() < 15) {
|
|
||||||
ref.read(tabBarDismissableControllerProvider.notifier).dismiss();
|
|
||||||
}
|
}
|
||||||
},
|
} else if (distance.dy < (toolbarHeight / 3) &&
|
||||||
child: Column(
|
distance.dx.abs() < 15) {
|
||||||
mainAxisSize: MainAxisSize.min,
|
ref.read(tabBarDismissableControllerProvider.notifier).dismiss();
|
||||||
children: [
|
}
|
||||||
if (showQuickTabSwitcherBar)
|
},
|
||||||
Visibility(
|
child: Column(
|
||||||
visible: displayQuickTabSwitcher,
|
mainAxisSize: MainAxisSize.min,
|
||||||
maintainState: true,
|
children: [
|
||||||
child: QuickTabSwitcher(),
|
if (showQuickTabSwitcherBar)
|
||||||
),
|
Visibility(
|
||||||
|
visible: displayQuickTabSwitcher,
|
||||||
|
maintainState: true,
|
||||||
|
child: QuickTabSwitcher(),
|
||||||
|
),
|
||||||
|
if (showMainToolbar)
|
||||||
Visibility(
|
Visibility(
|
||||||
visible: displayAppBar,
|
visible: displayAppBar,
|
||||||
maintainState: true,
|
maintainState: true,
|
||||||
@@ -269,29 +349,40 @@ class BrowserBottomAppBar extends HookConsumerWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (!showContextualToolbar)
|
if (showMainToolbarTabsCount)
|
||||||
TabsCountButton(
|
TabsCountButton(
|
||||||
selectedTabId: selectedTabId,
|
selectedTabId: selectedTabId,
|
||||||
displayedSheet: displayedSheet,
|
displayedSheet: displayedSheet,
|
||||||
showLongPressMenu: true,
|
showLongPressMenu: true,
|
||||||
),
|
),
|
||||||
if (!showContextualToolbar)
|
if (showMainToolbarNavigationButton)
|
||||||
NavigationMenuButton(selectedTabId: selectedTabId),
|
NavigationMenuButton(
|
||||||
|
selectedTabId: selectedTabId,
|
||||||
|
showNavigationButtons: true,
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (showContextualToolbar)
|
if (showContextualToolbar)
|
||||||
ContextualToolbar(
|
ContextualToolbar(
|
||||||
selectedTabId: selectedTabId,
|
selectedTabId: selectedTabId,
|
||||||
displayedSheet: displayedSheet,
|
displayedSheet: displayedSheet,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
typedef _QuickTabItem = ({
|
||||||
|
Color? color,
|
||||||
|
String id,
|
||||||
|
bool isPrivate,
|
||||||
|
bool isHistory,
|
||||||
|
String title,
|
||||||
|
Uri url,
|
||||||
|
});
|
||||||
|
|
||||||
class ContextualToolbar extends HookConsumerWidget {
|
class ContextualToolbar extends HookConsumerWidget {
|
||||||
const ContextualToolbar({
|
const ContextualToolbar({
|
||||||
super.key,
|
super.key,
|
||||||
@@ -333,7 +424,10 @@ class ContextualToolbar extends HookConsumerWidget {
|
|||||||
displayedSheet: displayedSheet,
|
displayedSheet: displayedSheet,
|
||||||
showLongPressMenu: false,
|
showLongPressMenu: false,
|
||||||
),
|
),
|
||||||
NavigationMenuButton(selectedTabId: selectedTabId),
|
NavigationMenuButton(
|
||||||
|
selectedTabId: selectedTabId,
|
||||||
|
showNavigationButtons: false,
|
||||||
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -342,7 +436,16 @@ class ContextualToolbar extends HookConsumerWidget {
|
|||||||
class QuickTabSwitcher extends HookConsumerWidget {
|
class QuickTabSwitcher extends HookConsumerWidget {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
final tabStates = ref.watch(fifoTabStatesProvider).value;
|
final tabStates = ref.watch(fifoTabStatesProvider);
|
||||||
|
final historyAsync = useCachedFuture(() {
|
||||||
|
if (tabStates.value.length <= 1) {
|
||||||
|
return ref
|
||||||
|
.read(historyRepositoryProvider.notifier)
|
||||||
|
.getVisitsPaginated(count: 25);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Future.value(<VisitInfo>[]);
|
||||||
|
}, [tabStates.value.length <= 1]);
|
||||||
|
|
||||||
final chipScrollController = useScrollController();
|
final chipScrollController = useScrollController();
|
||||||
|
|
||||||
@@ -351,19 +454,19 @@ class QuickTabSwitcher extends HookConsumerWidget {
|
|||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
height: 48,
|
height: 48,
|
||||||
width: double.maxFinite,
|
width: double.maxFinite,
|
||||||
child: SelectableChips<FifoTab, FifoTab, String>(
|
child: SelectableChips<_QuickTabItem, _QuickTabItem, String>(
|
||||||
enableDelete: false,
|
enableDelete: false,
|
||||||
scrollController: chipScrollController,
|
scrollController: chipScrollController,
|
||||||
itemId: (item) => item.$1.id,
|
itemId: (item) => item.id,
|
||||||
itemLabel: (item) {
|
itemLabel: (item) {
|
||||||
return Row(
|
return Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
ConstrainedBox(
|
ConstrainedBox(
|
||||||
constraints: const BoxConstraints(maxWidth: 64),
|
constraints: const BoxConstraints(maxWidth: 64),
|
||||||
child: Text(item.$1.titleOrAuthority),
|
child: Text(item.title),
|
||||||
),
|
),
|
||||||
if (item.$1.isPrivate)
|
if (item.isPrivate)
|
||||||
const Padding(
|
const Padding(
|
||||||
padding: EdgeInsets.only(left: 8.0),
|
padding: EdgeInsets.only(left: 8.0),
|
||||||
child: Icon(
|
child: Icon(
|
||||||
@@ -372,23 +475,57 @@ class QuickTabSwitcher extends HookConsumerWidget {
|
|||||||
size: 20,
|
size: 20,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
if (item.isHistory)
|
||||||
|
const Padding(
|
||||||
|
padding: EdgeInsets.only(left: 8.0),
|
||||||
|
child: Icon(MdiIcons.history, size: 20),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
itemAvatar: (item) => UrlIcon([item.$1.url], iconSize: 16),
|
itemAvatar: (item) => UrlIcon([item.url], iconSize: 16),
|
||||||
itemBackgroundColor: (item) => item.$2?.color.withValues(alpha: 0.5),
|
itemBackgroundColor: (item) => item.color?.withValues(alpha: 0.5),
|
||||||
onSelected: (item) async {
|
onSelected: (item) async {
|
||||||
final animation = chipScrollController.animateTo(
|
final animation = chipScrollController.animateTo(
|
||||||
0,
|
0,
|
||||||
duration: const Duration(milliseconds: 200),
|
duration: const Duration(milliseconds: 200),
|
||||||
curve: Curves.easeOutBack,
|
curve: Curves.easeOutBack,
|
||||||
);
|
);
|
||||||
await ref
|
if (item.isHistory) {
|
||||||
.read(tabRepositoryProvider.notifier)
|
await ref
|
||||||
.selectTab(item.$1.id);
|
.read(tabRepositoryProvider.notifier)
|
||||||
|
.addTab(url: item.url, private: false);
|
||||||
|
} else {
|
||||||
|
await ref.read(tabRepositoryProvider.notifier).selectTab(item.id);
|
||||||
|
}
|
||||||
await animation;
|
await animation;
|
||||||
},
|
},
|
||||||
availableItems: tabStates.skip(1),
|
availableItems: tabStates.value
|
||||||
|
.map(
|
||||||
|
(state) => (
|
||||||
|
id: state.$1.id,
|
||||||
|
title: state.$1.titleOrAuthority,
|
||||||
|
isPrivate: state.$1.isPrivate,
|
||||||
|
isHistory: false,
|
||||||
|
url: state.$1.url,
|
||||||
|
color: state.$2?.color,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.skip(1)
|
||||||
|
.followedBy(
|
||||||
|
(historyAsync.data ?? []).map((state) {
|
||||||
|
final url = Uri.parse(state.url);
|
||||||
|
|
||||||
|
return (
|
||||||
|
id: state.url,
|
||||||
|
title: state.title ?? url.authority,
|
||||||
|
isPrivate: false,
|
||||||
|
isHistory: true,
|
||||||
|
url: url,
|
||||||
|
color: null,
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -430,18 +567,18 @@ class ShareMenuButton extends HookConsumerWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class NavigationMenuButton extends HookConsumerWidget {
|
class NavigationMenuButton extends HookConsumerWidget {
|
||||||
const NavigationMenuButton({super.key, required this.selectedTabId});
|
|
||||||
|
|
||||||
final String? selectedTabId;
|
final String? selectedTabId;
|
||||||
|
final bool showNavigationButtons;
|
||||||
|
|
||||||
|
const NavigationMenuButton({
|
||||||
|
super.key,
|
||||||
|
required this.selectedTabId,
|
||||||
|
required this.showNavigationButtons,
|
||||||
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
final addonService = ref.watch(addonServiceProvider);
|
final addonService = ref.watch(addonServiceProvider);
|
||||||
final showContextualAppBar = ref.watch(
|
|
||||||
generalSettingsWithDefaultsProvider.select(
|
|
||||||
(value) => value.tabBarShowContextualBar,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
final hamburgerMenuController = useMenuController();
|
final hamburgerMenuController = useMenuController();
|
||||||
|
|
||||||
@@ -674,7 +811,7 @@ class NavigationMenuButton extends HookConsumerWidget {
|
|||||||
child: const Text('Reload'),
|
child: const Text('Reload'),
|
||||||
),
|
),
|
||||||
if (selectedTabId != null) const Divider(),
|
if (selectedTabId != null) const Divider(),
|
||||||
if (selectedTabId != null && !showContextualAppBar)
|
if (selectedTabId != null && showNavigationButtons)
|
||||||
Consumer(
|
Consumer(
|
||||||
builder: (context, ref, child) {
|
builder: (context, ref, child) {
|
||||||
final history = ref.watch(
|
final history = ref.watch(
|
||||||
|
|||||||
+85
-69
@@ -44,6 +44,7 @@ import 'package:weblibre/features/geckoview/features/browser/domain/providers/li
|
|||||||
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/domain/services/engine_settings_replication.dart';
|
import 'package:weblibre/features/geckoview/features/browser/domain/services/engine_settings_replication.dart';
|
||||||
import 'package:weblibre/features/geckoview/features/browser/domain/services/proxy_settings_replication.dart';
|
import 'package:weblibre/features/geckoview/features/browser/domain/services/proxy_settings_replication.dart';
|
||||||
|
import 'package:weblibre/features/geckoview/features/history/domain/repositories/history.dart';
|
||||||
import 'package:weblibre/features/geckoview/features/preferences/data/repositories/preference_observer.dart';
|
import 'package:weblibre/features/geckoview/features/preferences/data/repositories/preference_observer.dart';
|
||||||
import 'package:weblibre/features/share_intent/domain/entities/shared_content.dart';
|
import 'package:weblibre/features/share_intent/domain/entities/shared_content.dart';
|
||||||
import 'package:weblibre/features/user/data/models/general_settings.dart';
|
import 'package:weblibre/features/user/data/models/general_settings.dart';
|
||||||
@@ -59,11 +60,13 @@ class BrowserView extends StatefulHookConsumerWidget {
|
|||||||
final Duration screenshotPeriod;
|
final Duration screenshotPeriod;
|
||||||
final Duration suggestionTimeout;
|
final Duration suggestionTimeout;
|
||||||
final Future<void> Function()? postInitializationStep;
|
final Future<void> Function()? postInitializationStep;
|
||||||
|
final StreamSink<Offset>? pointerMoveEventSink;
|
||||||
|
|
||||||
const BrowserView({
|
const BrowserView({
|
||||||
this.screenshotPeriod = const Duration(seconds: 10),
|
this.screenshotPeriod = const Duration(seconds: 10),
|
||||||
this.suggestionTimeout = const Duration(seconds: 30),
|
this.suggestionTimeout = const Duration(seconds: 30),
|
||||||
this.postInitializationStep,
|
this.postInitializationStep,
|
||||||
|
this.pointerMoveEventSink,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -106,9 +109,9 @@ class _BrowserViewState extends ConsumerState<BrowserView>
|
|||||||
|
|
||||||
if (settings.historyAutoCleanInterval > Duration.zero) {
|
if (settings.historyAutoCleanInterval > Duration.zero) {
|
||||||
await GeckoHistoryService().deleteVisitsBetween(
|
await GeckoHistoryService().deleteVisitsBetween(
|
||||||
DateTime(0),
|
DateTime(0),
|
||||||
DateTime.now().subtract(settings.historyAutoCleanInterval),
|
DateTime.now().subtract(settings.historyAutoCleanInterval),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -216,74 +219,87 @@ class _BrowserViewState extends ConsumerState<BrowserView>
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
return Stack(
|
return Listener(
|
||||||
children: [
|
behavior: HitTestBehavior.translucent,
|
||||||
GeckoView(
|
onPointerMove: (widget.pointerMoveEventSink != null)
|
||||||
preInitializationStep: () async {
|
? (event) {
|
||||||
await ref
|
if (event.down) {
|
||||||
.read(eventServiceProvider)
|
widget.pointerMoveEventSink!.add(event.localDelta);
|
||||||
.viewReadyStateEvents
|
}
|
||||||
.firstWhere((state) => state == true)
|
|
||||||
.timeout(
|
|
||||||
const Duration(seconds: 3),
|
|
||||||
onTimeout: () {
|
|
||||||
logger.e(
|
|
||||||
'Browser fragement not reported ready, trying to intitialize anyways',
|
|
||||||
);
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
|
||||||
postInitializationStep: () async {
|
|
||||||
await widget.postInitializationStep?.call();
|
|
||||||
|
|
||||||
if (!initializationCompleter.isCompleted) {
|
|
||||||
const quickActions = QuickActions();
|
|
||||||
|
|
||||||
//Debounce: https://github.com/flutter/flutter/issues/131121
|
|
||||||
DateTime? lastAction;
|
|
||||||
await quickActions.initialize((type) async {
|
|
||||||
if (lastAction == null ||
|
|
||||||
DateTime.now().difference(lastAction!) >
|
|
||||||
const Duration(seconds: 5)) {
|
|
||||||
if (type == 'new_tab') {
|
|
||||||
lastAction = DateTime.now();
|
|
||||||
|
|
||||||
final router = await ref.read(routerProvider.future);
|
|
||||||
const route = SearchRoute(tabType: TabType.regular);
|
|
||||||
|
|
||||||
await router.push(route.location);
|
|
||||||
} else if (type == 'new_private_tab') {
|
|
||||||
lastAction = DateTime.now();
|
|
||||||
|
|
||||||
final router = await ref.read(routerProvider.future);
|
|
||||||
const route = SearchRoute(tabType: TabType.private);
|
|
||||||
|
|
||||||
await router.push(route.location);
|
|
||||||
} else {
|
|
||||||
throw UnimplementedError();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
await quickActions.setShortcutItems([
|
|
||||||
//TODO: add icons
|
|
||||||
const ShortcutItem(type: 'new_tab', localizedTitle: 'New Tab'),
|
|
||||||
const ShortcutItem(
|
|
||||||
type: 'new_private_tab',
|
|
||||||
localizedTitle: 'New Private Tab',
|
|
||||||
),
|
|
||||||
]);
|
|
||||||
|
|
||||||
initializationCompleter.complete();
|
|
||||||
}
|
}
|
||||||
},
|
: null,
|
||||||
),
|
child: Stack(
|
||||||
if (!hasTab)
|
children: [
|
||||||
Positioned.fill(
|
GeckoView(
|
||||||
child: SizedBox.expand(child: Container(color: Colors.grey[800])),
|
preInitializationStep: () async {
|
||||||
|
await ref
|
||||||
|
.read(eventServiceProvider)
|
||||||
|
.viewReadyStateEvents
|
||||||
|
.firstWhere((state) => state == true)
|
||||||
|
.timeout(
|
||||||
|
const Duration(seconds: 3),
|
||||||
|
onTimeout: () {
|
||||||
|
logger.e(
|
||||||
|
'Browser fragement not reported ready, trying to intitialize anyways',
|
||||||
|
);
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
postInitializationStep: () async {
|
||||||
|
await widget.postInitializationStep?.call();
|
||||||
|
|
||||||
|
if (!initializationCompleter.isCompleted) {
|
||||||
|
const quickActions = QuickActions();
|
||||||
|
|
||||||
|
//Debounce: https://github.com/flutter/flutter/issues/131121
|
||||||
|
DateTime? lastAction;
|
||||||
|
await quickActions.initialize((type) async {
|
||||||
|
if (lastAction == null ||
|
||||||
|
DateTime.now().difference(lastAction!) >
|
||||||
|
const Duration(seconds: 5)) {
|
||||||
|
if (type == 'new_tab') {
|
||||||
|
lastAction = DateTime.now();
|
||||||
|
|
||||||
|
final router = await ref.read(routerProvider.future);
|
||||||
|
const route = SearchRoute(tabType: TabType.regular);
|
||||||
|
|
||||||
|
await router.push(route.location);
|
||||||
|
} else if (type == 'new_private_tab') {
|
||||||
|
lastAction = DateTime.now();
|
||||||
|
|
||||||
|
final router = await ref.read(routerProvider.future);
|
||||||
|
const route = SearchRoute(tabType: TabType.private);
|
||||||
|
|
||||||
|
await router.push(route.location);
|
||||||
|
} else {
|
||||||
|
throw UnimplementedError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
await quickActions.setShortcutItems([
|
||||||
|
//TODO: add icons
|
||||||
|
const ShortcutItem(
|
||||||
|
type: 'new_tab',
|
||||||
|
localizedTitle: 'New Tab',
|
||||||
|
),
|
||||||
|
const ShortcutItem(
|
||||||
|
type: 'new_private_tab',
|
||||||
|
localizedTitle: 'New Private Tab',
|
||||||
|
),
|
||||||
|
]);
|
||||||
|
|
||||||
|
initializationCompleter.complete();
|
||||||
|
}
|
||||||
|
},
|
||||||
),
|
),
|
||||||
],
|
if (!hasTab)
|
||||||
|
Positioned.fill(
|
||||||
|
child: SizedBox.expand(child: Container(color: Colors.grey[800])),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+3
-1
@@ -48,6 +48,7 @@ class ViewTabSheetWidget extends HookConsumerWidget {
|
|||||||
final DraggableScrollableController draggableScrollableController;
|
final DraggableScrollableController draggableScrollableController;
|
||||||
final VoidCallback onClose;
|
final VoidCallback onClose;
|
||||||
final double initialHeight;
|
final double initialHeight;
|
||||||
|
final double bottomAppBarHeight;
|
||||||
|
|
||||||
const ViewTabSheetWidget({
|
const ViewTabSheetWidget({
|
||||||
required this.initialTabState,
|
required this.initialTabState,
|
||||||
@@ -55,6 +56,7 @@ class ViewTabSheetWidget extends HookConsumerWidget {
|
|||||||
required this.draggableScrollableController,
|
required this.draggableScrollableController,
|
||||||
required this.onClose,
|
required this.onClose,
|
||||||
required this.initialHeight,
|
required this.initialHeight,
|
||||||
|
required this.bottomAppBarHeight,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -79,7 +81,7 @@ class ViewTabSheetWidget extends HookConsumerWidget {
|
|||||||
final totalHeight =
|
final totalHeight =
|
||||||
headerBox.size.height +
|
headerBox.size.height +
|
||||||
textBox.size.height +
|
textBox.size.height +
|
||||||
kToolbarHeight +
|
bottomAppBarHeight +
|
||||||
MediaQuery.of(context).viewInsets.bottom;
|
MediaQuery.of(context).viewInsets.bottom;
|
||||||
|
|
||||||
final relative = (totalHeight / MediaQuery.of(context).size.height)
|
final relative = (totalHeight / MediaQuery.of(context).size.height)
|
||||||
|
|||||||
@@ -362,6 +362,55 @@ class GeneralSettingsScreen extends HookConsumerWidget {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(
|
||||||
|
horizontal: 16.0,
|
||||||
|
vertical: 8,
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
const ListTile(
|
||||||
|
title: Text('Tab Bar Position'),
|
||||||
|
leading: Icon(MdiIcons.dockWindow),
|
||||||
|
contentPadding: EdgeInsets.zero,
|
||||||
|
),
|
||||||
|
RadioGroup(
|
||||||
|
groupValue: generalSettings.tabBarPosition,
|
||||||
|
onChanged: (value) async {
|
||||||
|
if (value != null) {
|
||||||
|
await ref
|
||||||
|
.read(
|
||||||
|
saveGeneralSettingsControllerProvider.notifier,
|
||||||
|
)
|
||||||
|
.save(
|
||||||
|
(currentSettings) => currentSettings.copyWith
|
||||||
|
.tabBarPosition(value),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: const Column(
|
||||||
|
children: [
|
||||||
|
RadioListTile.adaptive(
|
||||||
|
value: TabBarPosition.top,
|
||||||
|
title: Text('Top'),
|
||||||
|
subtitle: Text(
|
||||||
|
'Persistent tab bar without auto-hide',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
RadioListTile.adaptive(
|
||||||
|
value: TabBarPosition.bottom,
|
||||||
|
title: Text('Bottom'),
|
||||||
|
subtitle: Text('Tab bar with auto-hide support'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
SwitchListTile.adaptive(
|
SwitchListTile.adaptive(
|
||||||
title: const Text('Show Contextual Tab Bar'),
|
title: const Text('Show Contextual Tab Bar'),
|
||||||
subtitle: const Text(
|
subtitle: const Text(
|
||||||
|
|||||||
@@ -38,6 +38,8 @@ enum TabBarSwipeAction { switchLastOpened, navigateOrderedTabs }
|
|||||||
|
|
||||||
enum TabIntentOpenSetting { regular, private, ask }
|
enum TabIntentOpenSetting { regular, private, ask }
|
||||||
|
|
||||||
|
enum TabBarPosition { top, bottom }
|
||||||
|
|
||||||
enum DeleteBrowsingDataType {
|
enum DeleteBrowsingDataType {
|
||||||
tabs('Open tabs'),
|
tabs('Open tabs'),
|
||||||
history('Browsing history'),
|
history('Browsing history'),
|
||||||
@@ -74,6 +76,7 @@ class GeneralSettings with FastEquatable {
|
|||||||
final bool tabBarReaderView;
|
final bool tabBarReaderView;
|
||||||
final bool tabBarShowContextualBar;
|
final bool tabBarShowContextualBar;
|
||||||
final bool tabBarShowQuickTabSwitcherBar;
|
final bool tabBarShowQuickTabSwitcherBar;
|
||||||
|
final TabBarPosition tabBarPosition;
|
||||||
|
|
||||||
GeneralSettings({
|
GeneralSettings({
|
||||||
required this.themeMode,
|
required this.themeMode,
|
||||||
@@ -94,6 +97,7 @@ class GeneralSettings with FastEquatable {
|
|||||||
required this.tabBarReaderView,
|
required this.tabBarReaderView,
|
||||||
required this.tabBarShowContextualBar,
|
required this.tabBarShowContextualBar,
|
||||||
required this.tabBarShowQuickTabSwitcherBar,
|
required this.tabBarShowQuickTabSwitcherBar,
|
||||||
|
required this.tabBarPosition,
|
||||||
});
|
});
|
||||||
|
|
||||||
GeneralSettings.withDefaults({
|
GeneralSettings.withDefaults({
|
||||||
@@ -115,6 +119,7 @@ class GeneralSettings with FastEquatable {
|
|||||||
bool? tabBarReaderView,
|
bool? tabBarReaderView,
|
||||||
bool? tabBarShowContextualBar,
|
bool? tabBarShowContextualBar,
|
||||||
bool? tabBarShowQuickTabSwitcherBar,
|
bool? tabBarShowQuickTabSwitcherBar,
|
||||||
|
TabBarPosition? tabBarPosition,
|
||||||
}) : themeMode = themeMode ?? ThemeMode.dark,
|
}) : themeMode = themeMode ?? ThemeMode.dark,
|
||||||
enableReadability = enableReadability ?? true,
|
enableReadability = enableReadability ?? true,
|
||||||
enforceReadability = enforceReadability ?? false,
|
enforceReadability = enforceReadability ?? false,
|
||||||
@@ -134,7 +139,8 @@ class GeneralSettings with FastEquatable {
|
|||||||
tabViewBottomSheet = tabViewBottomSheet ?? false,
|
tabViewBottomSheet = tabViewBottomSheet ?? false,
|
||||||
tabBarReaderView = tabBarReaderView ?? false,
|
tabBarReaderView = tabBarReaderView ?? false,
|
||||||
tabBarShowContextualBar = tabBarShowContextualBar ?? true,
|
tabBarShowContextualBar = tabBarShowContextualBar ?? true,
|
||||||
tabBarShowQuickTabSwitcherBar = tabBarShowQuickTabSwitcherBar ?? true;
|
tabBarShowQuickTabSwitcherBar = tabBarShowQuickTabSwitcherBar ?? true,
|
||||||
|
tabBarPosition = tabBarPosition ?? TabBarPosition.bottom;
|
||||||
|
|
||||||
factory GeneralSettings.fromJson(Map<String, dynamic> json) =>
|
factory GeneralSettings.fromJson(Map<String, dynamic> json) =>
|
||||||
_$GeneralSettingsFromJson(json);
|
_$GeneralSettingsFromJson(json);
|
||||||
@@ -161,5 +167,6 @@ class GeneralSettings with FastEquatable {
|
|||||||
tabBarReaderView,
|
tabBarReaderView,
|
||||||
tabBarShowContextualBar,
|
tabBarShowContextualBar,
|
||||||
tabBarShowQuickTabSwitcherBar,
|
tabBarShowQuickTabSwitcherBar,
|
||||||
|
tabBarPosition,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,6 +51,8 @@ abstract class _$GeneralSettingsCWProxy {
|
|||||||
bool tabBarShowQuickTabSwitcherBar,
|
bool tabBarShowQuickTabSwitcherBar,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
GeneralSettings tabBarPosition(TabBarPosition tabBarPosition);
|
||||||
|
|
||||||
/// 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)`.
|
||||||
///
|
///
|
||||||
@@ -77,6 +79,7 @@ abstract class _$GeneralSettingsCWProxy {
|
|||||||
bool tabBarReaderView,
|
bool tabBarReaderView,
|
||||||
bool tabBarShowContextualBar,
|
bool tabBarShowContextualBar,
|
||||||
bool tabBarShowQuickTabSwitcherBar,
|
bool tabBarShowQuickTabSwitcherBar,
|
||||||
|
TabBarPosition tabBarPosition,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -162,6 +165,10 @@ class _$GeneralSettingsCWProxyImpl implements _$GeneralSettingsCWProxy {
|
|||||||
bool tabBarShowQuickTabSwitcherBar,
|
bool tabBarShowQuickTabSwitcherBar,
|
||||||
) => call(tabBarShowQuickTabSwitcherBar: tabBarShowQuickTabSwitcherBar);
|
) => call(tabBarShowQuickTabSwitcherBar: tabBarShowQuickTabSwitcherBar);
|
||||||
|
|
||||||
|
@override
|
||||||
|
GeneralSettings tabBarPosition(TabBarPosition tabBarPosition) =>
|
||||||
|
call(tabBarPosition: tabBarPosition);
|
||||||
|
|
||||||
@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)`.
|
||||||
@@ -189,6 +196,7 @@ class _$GeneralSettingsCWProxyImpl implements _$GeneralSettingsCWProxy {
|
|||||||
Object? tabBarReaderView = const $CopyWithPlaceholder(),
|
Object? tabBarReaderView = const $CopyWithPlaceholder(),
|
||||||
Object? tabBarShowContextualBar = const $CopyWithPlaceholder(),
|
Object? tabBarShowContextualBar = const $CopyWithPlaceholder(),
|
||||||
Object? tabBarShowQuickTabSwitcherBar = const $CopyWithPlaceholder(),
|
Object? tabBarShowQuickTabSwitcherBar = const $CopyWithPlaceholder(),
|
||||||
|
Object? tabBarPosition = const $CopyWithPlaceholder(),
|
||||||
}) {
|
}) {
|
||||||
return GeneralSettings(
|
return GeneralSettings(
|
||||||
themeMode: themeMode == const $CopyWithPlaceholder() || themeMode == null
|
themeMode: themeMode == const $CopyWithPlaceholder() || themeMode == null
|
||||||
@@ -295,6 +303,12 @@ class _$GeneralSettingsCWProxyImpl implements _$GeneralSettingsCWProxy {
|
|||||||
? _value.tabBarShowQuickTabSwitcherBar
|
? _value.tabBarShowQuickTabSwitcherBar
|
||||||
// ignore: cast_nullable_to_non_nullable
|
// ignore: cast_nullable_to_non_nullable
|
||||||
: tabBarShowQuickTabSwitcherBar as bool,
|
: tabBarShowQuickTabSwitcherBar as bool,
|
||||||
|
tabBarPosition:
|
||||||
|
tabBarPosition == const $CopyWithPlaceholder() ||
|
||||||
|
tabBarPosition == null
|
||||||
|
? _value.tabBarPosition
|
||||||
|
// ignore: cast_nullable_to_non_nullable
|
||||||
|
: tabBarPosition as TabBarPosition,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -351,6 +365,10 @@ GeneralSettings _$GeneralSettingsFromJson(
|
|||||||
tabBarReaderView: json['tabBarReaderView'] as bool?,
|
tabBarReaderView: json['tabBarReaderView'] as bool?,
|
||||||
tabBarShowContextualBar: json['tabBarShowContextualBar'] as bool?,
|
tabBarShowContextualBar: json['tabBarShowContextualBar'] as bool?,
|
||||||
tabBarShowQuickTabSwitcherBar: json['tabBarShowQuickTabSwitcherBar'] as bool?,
|
tabBarShowQuickTabSwitcherBar: json['tabBarShowQuickTabSwitcherBar'] as bool?,
|
||||||
|
tabBarPosition: $enumDecodeNullable(
|
||||||
|
_$TabBarPositionEnumMap,
|
||||||
|
json['tabBarPosition'],
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
Map<String, dynamic> _$GeneralSettingsToJson(
|
Map<String, dynamic> _$GeneralSettingsToJson(
|
||||||
@@ -381,6 +399,7 @@ Map<String, dynamic> _$GeneralSettingsToJson(
|
|||||||
'tabBarReaderView': instance.tabBarReaderView,
|
'tabBarReaderView': instance.tabBarReaderView,
|
||||||
'tabBarShowContextualBar': instance.tabBarShowContextualBar,
|
'tabBarShowContextualBar': instance.tabBarShowContextualBar,
|
||||||
'tabBarShowQuickTabSwitcherBar': instance.tabBarShowQuickTabSwitcherBar,
|
'tabBarShowQuickTabSwitcherBar': instance.tabBarShowQuickTabSwitcherBar,
|
||||||
|
'tabBarPosition': _$TabBarPositionEnumMap[instance.tabBarPosition]!,
|
||||||
};
|
};
|
||||||
|
|
||||||
const _$ThemeModeEnumMap = {
|
const _$ThemeModeEnumMap = {
|
||||||
@@ -422,3 +441,8 @@ const _$TabBarSwipeActionEnumMap = {
|
|||||||
TabBarSwipeAction.switchLastOpened: 'switchLastOpened',
|
TabBarSwipeAction.switchLastOpened: 'switchLastOpened',
|
||||||
TabBarSwipeAction.navigateOrderedTabs: 'navigateOrderedTabs',
|
TabBarSwipeAction.navigateOrderedTabs: 'navigateOrderedTabs',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const _$TabBarPositionEnumMap = {
|
||||||
|
TabBarPosition.top: 'top',
|
||||||
|
TabBarPosition.bottom: 'bottom',
|
||||||
|
};
|
||||||
|
|||||||
@@ -114,6 +114,10 @@ class GeneralSettingsRepository extends _$GeneralSettingsRepository {
|
|||||||
),
|
),
|
||||||
'tabBarShowQuickTabSwitcherBar': settings['tabBarShowQuickTabSwitcherBar']
|
'tabBarShowQuickTabSwitcherBar': settings['tabBarShowQuickTabSwitcherBar']
|
||||||
?.readAs(DriftSqlType.bool, db.typeMapping),
|
?.readAs(DriftSqlType.bool, db.typeMapping),
|
||||||
|
'tabBarPosition': settings['tabBarPosition']?.readAs(
|
||||||
|
DriftSqlType.string,
|
||||||
|
db.typeMapping,
|
||||||
|
),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ final class GeneralSettingsRepositoryProvider
|
|||||||
}
|
}
|
||||||
|
|
||||||
String _$generalSettingsRepositoryHash() =>
|
String _$generalSettingsRepositoryHash() =>
|
||||||
r'4014986c9510d6d7210c5c16d814f6b018a1f93c';
|
r'09542387844db25334384af115c538348b0ad33f';
|
||||||
|
|
||||||
abstract class _$GeneralSettingsRepository
|
abstract class _$GeneralSettingsRepository
|
||||||
extends $StreamNotifier<GeneralSettings> {
|
extends $StreamNotifier<GeneralSettings> {
|
||||||
|
|||||||
Reference in New Issue
Block a user