move app bar to bottom screen

This commit is contained in:
Fabian Freund
2024-06-06 05:30:32 +02:00
parent bb23870bf8
commit 063a118f90
2 changed files with 201 additions and 184 deletions
@@ -40,6 +40,8 @@ class KagiScreen extends HookConsumerWidget {
), ),
); );
final menuController = useMemoized(() => MenuController());
final lastBackButtonPress = useRef<DateTime?>(null); final lastBackButtonPress = useRef<DateTime?>(null);
final webViewController = useRef<InAppWebViewController?>(null); final webViewController = useRef<InAppWebViewController?>(null);
@@ -67,172 +69,180 @@ class KagiScreen extends HookConsumerWidget {
); );
return Scaffold( return Scaffold(
appBar: AppBar( bottomNavigationBar: BottomAppBar(
automaticallyImplyLeading: false, height: AppBar().preferredSize.height,
title: AppBarTitle( padding: EdgeInsets.zero,
onTap: () { child: AppBar(
final page = ref.read(webViewTabControllerProvider)?.page.value; automaticallyImplyLeading: false,
title: AppBarTitle(
if (page != null) {
ref.watch(overlayDialogProvider.notifier).show(
WebPageDialog(
page: page,
webViewController: webViewController.value,
onDismiss:
ref.watch(overlayDialogProvider.notifier).dismiss,
),
);
}
},
),
actions: [
TabsActionButton(
onTap: () { onTap: () {
if (displayedSheet case ViewTabs()) { final page = ref.read(webViewTabControllerProvider)?.page.value;
ref.read(bottomSheetProvider.notifier).dismiss();
} else { if (page != null) {
ref.read(bottomSheetProvider.notifier).show(ViewTabs()); ref.watch(overlayDialogProvider.notifier).show(
WebPageDialog(
page: page,
webViewController: webViewController.value,
onDismiss:
ref.watch(overlayDialogProvider.notifier).dismiss,
),
);
} }
}, },
), ),
MenuAnchor( actions: [
builder: (context, controller, child) { TabsActionButton(
return IconButton( onTap: () {
onPressed: () { if (displayedSheet case ViewTabs()) {
if (controller.isOpen) { ref.read(bottomSheetProvider.notifier).dismiss();
controller.close(); } else {
} else { ref.read(bottomSheetProvider.notifier).show(ViewTabs());
controller.open(); }
} },
}, ),
icon: const Icon(Icons.more_vert), MenuAnchor(
); controller: menuController,
}, builder: (context, controller, child) {
menuChildren: [ return IconButton(
HookConsumer( onPressed: () {
builder: (context, ref, child) { if (controller.isOpen) {
final activeWebView = ref.watch(webViewTabControllerProvider); controller.close();
final history = useListenableSelector( } else {
activeWebView?.page, controller.open();
() => }
activeWebView?.page.value.pageHistory ?? },
(canGoBack: false, canGoForward: false), icon: const Icon(Icons.more_vert),
); );
},
return Row( menuChildren: [
children: [ MenuItemButton(
Expanded( onPressed: () async {
child: IconButton( await context.push(AboutRoute().location);
onPressed: (history.canGoBack) },
? () async { leadingIcon: const Icon(Icons.info),
await webViewController.value?.goBack(); child: const Text('About'),
} ),
: null, const Divider(),
icon: const Icon(Icons.arrow_back), MenuItemButton(
), onPressed: () async {
), await context.push(SettingsRoute().location);
const SizedBox(height: 48, child: VerticalDivider()), },
Expanded( leadingIcon: const Icon(Icons.settings),
child: IconButton( child: const Text('Settings'),
onPressed: (history.canGoForward) ),
? () async { if (showEarlyAccessFeatures) const Divider(),
await webViewController.value?.goForward(); if (showEarlyAccessFeatures)
} MenuItemButton(
: null, onPressed: () async {
icon: const Icon(Icons.arrow_forward), await context.push(ChatArchiveListRoute().location);
), },
), leadingIcon: const Icon(MdiIcons.archive),
], child: const Text('Chat Archive'),
); ),
}, const Divider(),
), if (showEarlyAccessFeatures)
const Divider(), MenuItemButton(
MenuItemButton( onPressed: () {
onPressed: () async { ref.read(createTabStreamProvider.notifier).createTab(
await webViewController.value?.reload(); CreateTab(preferredTool: KagiTool.assistant),
}, );
leadingIcon: const Icon(Icons.refresh), },
child: const Text('Reload'), leadingIcon: const Icon(MdiIcons.brain),
), child: const Text('Assistant'),
const Divider(), ),
MenuItemButton(
onPressed: () async {
await context.push(SettingsRoute().location);
},
leadingIcon: const Icon(Icons.settings),
child: const Text('Settings'),
),
const Divider(),
MenuItemButton(
onPressed: () async {
final url = await webViewController.value?.getUrl();
if (url != null) {
await Share.shareUri(url);
}
},
leadingIcon: const Icon(Icons.share),
child: const Text('Share'),
),
MenuItemButton(
onPressed: () async {
final url = await webViewController.value?.getUrl();
if (url != null) {
// ignore: use_build_context_synchronously
await ui_helper.launchUrlFeedback(context, url);
}
},
leadingIcon: const Icon(Icons.open_in_browser),
child: const Text('Launch External'),
),
const Divider(),
MenuItemButton(
onPressed: () {
ref.read(createTabStreamProvider.notifier).createTab(
CreateTab(preferredTool: KagiTool.search),
);
},
leadingIcon: const Icon(MdiIcons.searchWeb),
child: const Text('Search'),
),
MenuItemButton(
onPressed: () {
ref.read(createTabStreamProvider.notifier).createTab(
CreateTab(preferredTool: KagiTool.summarizer),
);
},
leadingIcon: const Icon(MdiIcons.text),
child: const Text('Summarizer'),
),
if (showEarlyAccessFeatures)
MenuItemButton( MenuItemButton(
onPressed: () { onPressed: () {
ref.read(createTabStreamProvider.notifier).createTab( ref.read(createTabStreamProvider.notifier).createTab(
CreateTab(preferredTool: KagiTool.assistant), CreateTab(preferredTool: KagiTool.summarizer),
); );
}, },
leadingIcon: const Icon(MdiIcons.brain), leadingIcon: const Icon(MdiIcons.text),
child: const Text('Assistant'), child: const Text('Summarizer'),
), ),
const Divider(), MenuItemButton(
if (showEarlyAccessFeatures) onPressed: () {
ref.read(createTabStreamProvider.notifier).createTab(
CreateTab(preferredTool: KagiTool.search),
);
},
leadingIcon: const Icon(MdiIcons.searchWeb),
child: const Text('Search'),
),
const Divider(),
MenuItemButton( MenuItemButton(
onPressed: () async { onPressed: () async {
await context.push(ChatArchiveListRoute().location); final url = await webViewController.value?.getUrl();
if (url != null) {
// ignore: use_build_context_synchronously
await ui_helper.launchUrlFeedback(context, url);
}
}, },
leadingIcon: const Icon(MdiIcons.archive), leadingIcon: const Icon(Icons.open_in_browser),
child: const Text('Chat Archive'), child: const Text('Launch External'),
), ),
if (showEarlyAccessFeatures) const Divider(), MenuItemButton(
MenuItemButton( onPressed: () async {
onPressed: () async { final url = await webViewController.value?.getUrl();
await context.push(AboutRoute().location); if (url != null) {
}, await Share.shareUri(url);
leadingIcon: const Icon(Icons.info), }
child: const Text('About'), },
), leadingIcon: const Icon(Icons.share),
], child: const Text('Share'),
), ),
], const Divider(),
MenuItemButton(
onPressed: () async {
await webViewController.value?.reload();
},
leadingIcon: const Icon(Icons.refresh),
child: const Text('Reload'),
),
const Divider(),
HookConsumer(
builder: (context, ref, child) {
final activeWebView =
ref.watch(webViewTabControllerProvider);
final history = useListenableSelector(
activeWebView?.page,
() =>
activeWebView?.page.value.pageHistory ??
(canGoBack: false, canGoForward: false),
);
return Row(
children: [
Expanded(
child: IconButton(
onPressed: (history.canGoBack)
? () async {
await webViewController.value?.goBack();
menuController.close();
}
: null,
icon: const Icon(Icons.arrow_back),
),
),
const SizedBox(height: 48, child: VerticalDivider()),
Expanded(
child: IconButton(
onPressed: (history.canGoForward)
? () async {
await webViewController.value?.goForward();
menuController.close();
}
: null,
icon: const Icon(Icons.arrow_forward),
),
),
],
);
},
),
],
),
],
),
), ),
body: OverlayPortal( body: OverlayPortal(
controller: overlayController, controller: overlayController,
@@ -305,29 +315,31 @@ class KagiScreen extends HookConsumerWidget {
return true; return true;
} }
}, },
child: AnimatedIndexedStack( child: SafeArea(
duration: const Duration(milliseconds: 250), child: AnimatedIndexedStack(
transitionBuilder: (child, animation, secondaryAnimation) => duration: const Duration(milliseconds: 250),
SharedAxisTransition( transitionBuilder: (child, animation, secondaryAnimation) =>
animation: animation, SharedAxisTransition(
secondaryAnimation: secondaryAnimation, animation: animation,
transitionType: SharedAxisTransitionType.horizontal, secondaryAnimation: secondaryAnimation,
child: child, transitionType: SharedAxisTransitionType.horizontal,
), child: child,
key: ValueKey( ),
(activeWebView != null) key: ValueKey(
? webViews.keys (activeWebView != null)
.toList() ? webViews.keys
.indexOf(activeWebView.page.value.key)
: null,
),
index: (activeWebView != null)
? webViews.keys
.toList() .toList()
.indexOf(activeWebView.page.value.key) + .indexOf(activeWebView.page.value.key)
1 : null,
: 0, ),
children: [const LandingContent(), ...webViews.values], index: (activeWebView != null)
? webViews.keys
.toList()
.indexOf(activeWebView.page.value.key) +
1
: 0,
children: [const LandingContent(), ...webViews.values],
),
), ),
); );
}, },
@@ -373,17 +373,22 @@ class _WebViewState extends ConsumerState<WebView> {
} }
}, },
), ),
HookBuilder( Positioned(
builder: (context) { bottom: 0,
final value = useValueListenable(webViewProgress); left: 0,
right: 0,
child: HookBuilder(
builder: (context) {
final value = useValueListenable(webViewProgress);
return Visibility( return Visibility(
visible: value < 100, visible: value < 100,
child: LinearProgressIndicator( child: LinearProgressIndicator(
value: value / 100, value: value / 100,
), ),
); );
}, },
),
), ),
], ],
); );