intermediate

This commit is contained in:
Fabian Freund
2024-09-13 09:11:06 +02:00
parent 3b047b5d8a
commit 37d2c84d7f
155 changed files with 5666 additions and 1816 deletions
@@ -0,0 +1,45 @@
import 'dart:async';
import 'package:lensai/features/search_browser/domain/entities/sheet.dart';
import 'package:lensai/features/search_browser/domain/services/create_tab.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:rxdart/rxdart.dart';
part 'bottom_sheet.g.dart';
@Riverpod()
class BottomSheetController extends _$BottomSheetController {
@override
Sheet? build() {
//Don't use select here because it will only fire on new != previous
return ref.watch(createTabStreamProvider).valueOrNull;
}
void show(Sheet sheet) {
state = sheet;
}
void dismiss() {
state = null;
}
}
@Riverpod(keepAlive: true)
class BottomSheetExtend extends _$BottomSheetExtend {
late StreamController<double> _extentStreamController;
void add(double extent) {
_extentStreamController.add(extent);
}
@override
Stream<double> build() {
_extentStreamController = StreamController();
ref.onDispose(() async {
await _extentStreamController.close();
});
return _extentStreamController.stream
.sampleTime(const Duration(milliseconds: 50));
}
}
@@ -0,0 +1,43 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'bottom_sheet.dart';
// **************************************************************************
// RiverpodGenerator
// **************************************************************************
String _$bottomSheetControllerHash() =>
r'ae91f911fc35fdc0dfb14149ac2295e58ad13a86';
/// See also [BottomSheetController].
@ProviderFor(BottomSheetController)
final bottomSheetControllerProvider =
AutoDisposeNotifierProvider<BottomSheetController, Sheet?>.internal(
BottomSheetController.new,
name: r'bottomSheetControllerProvider',
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
? null
: _$bottomSheetControllerHash,
dependencies: null,
allTransitiveDependencies: null,
);
typedef _$BottomSheetController = AutoDisposeNotifier<Sheet?>;
String _$bottomSheetExtendHash() => r'81ae254499d1d7f57309274b0f18747b94d6dda3';
/// See also [BottomSheetExtend].
@ProviderFor(BottomSheetExtend)
final bottomSheetExtendProvider =
StreamNotifierProvider<BottomSheetExtend, double>.internal(
BottomSheetExtend.new,
name: r'bottomSheetExtendProvider',
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
? null
: _$bottomSheetExtendHash,
dependencies: null,
allTransitiveDependencies: null,
);
typedef _$BottomSheetExtend = StreamNotifier<double>;
// ignore_for_file: type=lint
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member
@@ -0,0 +1,20 @@
import 'package:flutter/widgets.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'overlay_dialog.g.dart';
@Riverpod()
class OverlayDialogController extends _$OverlayDialogController {
@override
Widget? build() {
return null;
}
void show(Widget dialog) {
state = dialog;
}
void dismiss() {
state = null;
}
}
@@ -0,0 +1,27 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'overlay_dialog.dart';
// **************************************************************************
// RiverpodGenerator
// **************************************************************************
String _$overlayDialogControllerHash() =>
r'53b44d9567bcb5bd9faecbfb8bb1af5aafb369c9';
/// See also [OverlayDialogController].
@ProviderFor(OverlayDialogController)
final overlayDialogControllerProvider =
AutoDisposeNotifierProvider<OverlayDialogController, Widget?>.internal(
OverlayDialogController.new,
name: r'overlayDialogControllerProvider',
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
? null
: _$overlayDialogControllerHash,
dependencies: null,
allTransitiveDependencies: null,
);
typedef _$OverlayDialogController = AutoDisposeNotifier<Widget?>;
// ignore_for_file: type=lint
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member
@@ -2,25 +2,25 @@ import 'package:collection/collection.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:lensai/features/topics/domain/repositories/topic.dart';
import 'package:lensai/features/web_view/domain/entities/web_view_page.dart';
import 'package:lensai/features/web_view/domain/repositories/web_view.dart';
import 'package:lensai/features/web_view/presentation/widgets/favicon.dart';
import 'package:lensai/features/geckoview/domain/entities/tab_state.dart';
import 'package:lensai/features/geckoview/domain/providers.dart';
import 'package:lensai/features/geckoview/features/topics/domain/repositories/topic.dart';
class TabActionDialog extends HookConsumerWidget {
final WebViewPage initialTab;
final String tabId;
final void Function()? onDismiss;
const TabActionDialog({
required this.initialTab,
required this.tabId,
this.onDismiss,
super.key,
});
@override
Widget build(BuildContext context, WidgetRef ref) {
final tabState = ref.watch(tabStateProvider(initialTab.id)) ?? initialTab;
final tabState =
ref.watch(tabStateProvider(tabId)) ?? TabState.$default(tabId);
final topics =
ref.watch(topicRepositoryProvider.select((value) => value.valueOrNull));
final selectedTopic =
@@ -42,13 +42,13 @@ class TabActionDialog extends HookConsumerWidget {
vertical: 24.0,
),
title: ListTile(
leading: FaviconImage(
favicon: tabState.favicon,
url: tabState.url,
size: 24,
leading: RawImage(
image: tabState.icon?.value,
width: 24,
height: 24,
),
contentPadding: EdgeInsets.zero,
title: Text(tabState.title ?? 'Unknown Title'),
title: Text(tabState.title),
subtitle: Text(tabState.url.authority),
),
children: [
@@ -7,11 +7,12 @@ 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:lensai/core/routing/routes.dart';
import 'package:lensai/features/search_browser/domain/entities/modes.dart';
import 'package:lensai/features/kagi/data/entities/modes.dart';
import 'package:lensai/features/search_browser/domain/entities/sheet.dart';
import 'package:lensai/features/search_browser/domain/providers.dart';
import 'package:lensai/features/search_browser/domain/services/create_tab.dart';
import 'package:lensai/features/search_browser/domain/services/session.dart';
import 'package:lensai/features/search_browser/presentation/controllers/bottom_sheet.dart';
import 'package:lensai/features/search_browser/presentation/controllers/overlay_dialog.dart';
import 'package:lensai/features/search_browser/presentation/widgets/app_bar_title.dart';
import 'package:lensai/features/search_browser/presentation/widgets/landing/content.dart';
import 'package:lensai/features/search_browser/presentation/widgets/sheets/shared_content_sheet.dart';
@@ -19,11 +20,6 @@ import 'package:lensai/features/search_browser/presentation/widgets/sheets/view_
import 'package:lensai/features/search_browser/presentation/widgets/tabs_action_button.dart';
import 'package:lensai/features/settings/data/models/settings.dart';
import 'package:lensai/features/settings/data/repositories/settings_repository.dart';
import 'package:lensai/features/topics/domain/repositories/tab.dart';
import 'package:lensai/features/web_view/domain/repositories/web_view.dart';
import 'package:lensai/features/web_view/presentation/controllers/readerability.dart';
import 'package:lensai/features/web_view/presentation/controllers/switch_new_tab.dart';
import 'package:lensai/features/web_view/presentation/widgets/web_page_dialog.dart';
import 'package:lensai/presentation/hooks/overlay_portal_controller.dart';
import 'package:lensai/presentation/widgets/animate_gradient_shader.dart';
import 'package:lensai/presentation/widgets/animated_indexed_stack.dart';
@@ -41,8 +37,8 @@ class KagiScreen extends HookConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final displayedSheet = ref.watch(bottomSheetProvider);
final displayedOverlayDialog = ref.watch(overlayDialogProvider);
final displayedSheet = ref.watch(bottomSheetControllerProvider);
final displayedOverlayDialog = ref.watch(overlayDialogControllerProvider);
final showEarlyAccessFeatures = ref.watch(
settingsRepositoryProvider.select(
@@ -75,7 +71,7 @@ class KagiScreen extends HookConsumerWidget {
final activeKagiTool = useValueNotifier<KagiTool?>(null, [displayedSheet]);
ref.listen(
overlayDialogProvider,
overlayDialogControllerProvider,
(previous, next) {
if (next != null) {
overlayController.show();
@@ -109,7 +105,7 @@ class KagiScreen extends HookConsumerWidget {
return (page != null)
? AppBarTitle(
page: page,
tab: page,
onTap: () {
ref.read(overlayDialogProvider.notifier).show(
WebPageDialog(
@@ -149,7 +145,8 @@ class KagiScreen extends HookConsumerWidget {
ref
.read(createTabStreamProvider.notifier)
.createTab(
CreateTab(preferredTool: KagiTool.search),
CreateTabSheet(
preferredTool: KagiTool.search),
);
},
icon: Icon(KagiTool.search.icon),
@@ -162,7 +159,8 @@ class KagiScreen extends HookConsumerWidget {
ref
.read(createTabStreamProvider.notifier)
.createTab(
CreateTab(preferredTool: KagiTool.summarizer),
CreateTabSheet(
preferredTool: KagiTool.summarizer),
);
},
icon: Icon(KagiTool.summarizer.icon),
@@ -176,7 +174,7 @@ class KagiScreen extends HookConsumerWidget {
ref
.read(createTabStreamProvider.notifier)
.createTab(
CreateTab(
CreateTabSheet(
preferredTool: KagiTool.assistant,
),
);
@@ -278,7 +276,7 @@ class KagiScreen extends HookConsumerWidget {
if (quickAction != null)
InkWell(
onTap: () async {
var tab = CreateTab(
var tab = CreateTabSheet(
preferredTool: quickAction,
);
@@ -303,7 +301,7 @@ class KagiScreen extends HookConsumerWidget {
}
}
tab = CreateTab(
tab = CreateTabSheet(
preferredTool: quickAction,
content: await completer.future,
);
@@ -320,12 +318,12 @@ class KagiScreen extends HookConsumerWidget {
),
),
TabsActionButton(
isActive: displayedSheet is ViewTabs,
isActive: displayedSheet is ViewTabsSheet,
onTap: () {
if (displayedSheet case ViewTabs()) {
if (displayedSheet case ViewTabsSheet()) {
ref.read(bottomSheetProvider.notifier).dismiss();
} else {
ref.read(bottomSheetProvider.notifier).show(ViewTabs());
ref.read(bottomSheetProvider.notifier).show(ViewTabsSheet());
}
},
),
@@ -387,7 +385,7 @@ class KagiScreen extends HookConsumerWidget {
MenuItemButton(
onPressed: () {
ref.read(createTabStreamProvider.notifier).createTab(
CreateTab(preferredTool: KagiTool.assistant),
CreateTabSheet(preferredTool: KagiTool.assistant),
);
},
leadingIcon: Icon(KagiTool.assistant.icon),
@@ -396,7 +394,7 @@ class KagiScreen extends HookConsumerWidget {
MenuItemButton(
onPressed: () {
ref.read(createTabStreamProvider.notifier).createTab(
CreateTab(preferredTool: KagiTool.summarizer),
CreateTabSheet(preferredTool: KagiTool.summarizer),
);
},
leadingIcon: Icon(KagiTool.summarizer.icon),
@@ -405,7 +403,7 @@ class KagiScreen extends HookConsumerWidget {
MenuItemButton(
onPressed: () {
ref.read(createTabStreamProvider.notifier).createTab(
CreateTab(preferredTool: KagiTool.search),
CreateTabSheet(preferredTool: KagiTool.search),
);
},
leadingIcon: Icon(KagiTool.search.icon),
@@ -649,7 +647,7 @@ class KagiScreen extends HookConsumerWidget {
return false;
},
child: switch (displayedSheet) {
ViewTabs() => DraggableScrollableSheet(
ViewTabsSheet() => DraggableScrollableSheet(
key: ValueKey(displayedSheet),
expand: false,
minChildSize: 0.1,
@@ -669,7 +667,7 @@ class KagiScreen extends HookConsumerWidget {
);
},
),
final CreateTab parameter => DraggableScrollableSheet(
final CreateTabSheet parameter => DraggableScrollableSheet(
key: ValueKey(displayedSheet),
expand: false,
initialChildSize: 0.8,
@@ -1,24 +1,23 @@
import 'package:flutter/material.dart';
import 'package:flutter_material_design_icons/flutter_material_design_icons.dart';
import 'package:lensai/features/web_view/domain/entities/web_view_page.dart';
import 'package:lensai/features/web_view/presentation/widgets/favicon.dart';
import 'package:lensai/features/geckoview/domain/entities/tab_state.dart';
import 'package:text_scroll/text_scroll.dart';
class AppBarTitle extends StatelessWidget {
final WebViewPage page;
final TabState tab;
final void Function()? onTap;
const AppBarTitle({required this.page, this.onTap, super.key});
const AppBarTitle({required this.tab, this.onTap, super.key});
Icon _securityStatusIcon(BuildContext context, WebViewPage page) {
if (page.url.isScheme('http')) {
Icon _securityStatusIcon(BuildContext context) {
if (tab.url.isScheme('http')) {
return Icon(
MdiIcons.lockOff,
color: Theme.of(context).colorScheme.error,
size: 14,
);
} else if (page.sslError != null) {
} else if (!tab.securityInfoState.secure) {
return Icon(
MdiIcons.lockAlert,
color: Theme.of(context).colorScheme.errorContainer,
@@ -39,9 +38,10 @@ class AppBarTitle extends StatelessWidget {
onTap: onTap,
child: Row(
children: [
FaviconImage(
favicon: page.favicon,
url: page.url,
RawImage(
image: tab.icon?.value,
height: 16,
width: 16,
),
const SizedBox(
width: 8,
@@ -52,8 +52,8 @@ class AppBarTitle extends StatelessWidget {
mainAxisSize: MainAxisSize.min,
children: [
TextScroll(
key: ValueKey(page.title),
page.title ?? 'New Tab',
key: ValueKey(tab.title),
tab.title,
style: theme.textTheme.bodyLarge
?.copyWith(color: theme.colorScheme.onSurface),
// mode: TextScrollMode.bouncing,
@@ -68,13 +68,13 @@ class AppBarTitle extends StatelessWidget {
),
Row(
children: [
_securityStatusIcon(context, page),
_securityStatusIcon(context),
const SizedBox(
width: 4,
),
Expanded(
child: Text(
page.url.authority,
tab.url.authority,
style: theme.textTheme.bodyMedium
?.copyWith(color: theme.colorScheme.onSurface),
),
@@ -6,7 +6,6 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:lensai/domain/services/app_initialization.dart';
import 'package:lensai/features/search_browser/presentation/widgets/error_container.dart';
import 'package:lensai/features/search_browser/presentation/widgets/landing/action.dart';
import 'package:lensai/features/web_view/presentation/controllers/switch_new_tab.dart';
import 'package:lensai/presentation/hooks/cached_future.dart';
class LandingContent extends HookConsumerWidget {
@@ -2,7 +2,7 @@ import 'package:expandable_page_view/expandable_page_view.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:lensai/features/search_browser/domain/entities/modes.dart';
import 'package:lensai/features/kagi/data/entities/modes.dart';
import 'package:lensai/features/search_browser/domain/entities/sheet.dart';
import 'package:lensai/features/search_browser/presentation/widgets/tabs/assistant_tab.dart';
import 'package:lensai/features/search_browser/presentation/widgets/tabs/search_tab.dart';
@@ -15,7 +15,7 @@ import 'package:lensai/presentation/hooks/sync_page_tab.dart';
typedef OnSubmitUri = void Function(Uri url);
class SharedContentSheet extends HookConsumerWidget {
final CreateTab parameter;
final CreateTabSheet parameter;
final OnSubmitUri onSubmit;
final void Function(KagiTool tool)? onActiveToolChanged;
@@ -4,14 +4,10 @@ import 'package:fading_scroll/fading_scroll.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:lensai/features/search_browser/domain/providers.dart';
import 'package:lensai/features/geckoview/features/topics/domain/providers.dart';
import 'package:lensai/features/geckoview/features/topics/domain/repositories/tab_link.dart';
import 'package:lensai/features/geckoview/features/topics/presentation/widgets/topic_chips.dart';
import 'package:lensai/features/search_browser/presentation/dialogs/tab_action.dart';
import 'package:lensai/features/topics/domain/providers.dart';
import 'package:lensai/features/topics/domain/repositories/tab.dart';
import 'package:lensai/features/topics/presentation/widgets/topic_chips.dart';
import 'package:lensai/features/web_view/domain/repositories/web_view.dart';
import 'package:lensai/features/web_view/presentation/controllers/switch_new_tab.dart';
import 'package:lensai/features/web_view/presentation/widgets/web_view_tab.dart';
class _SliverHeaderDelagate extends SliverPersistentHeaderDelegate {
static const _headerSize = 104.0;
@@ -3,7 +3,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:flutter_material_design_icons/flutter_material_design_icons.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:lensai/features/search_browser/domain/entities/modes.dart';
import 'package:lensai/features/kagi/data/entities/modes.dart';
import 'package:lensai/features/search_browser/domain/providers.dart';
import 'package:lensai/features/search_browser/presentation/widgets/sheets/shared_content_sheet.dart';
import 'package:lensai/features/search_browser/presentation/widgets/speech_to_text_button.dart';
@@ -9,6 +9,7 @@ import 'package:lensai/features/bangs/domain/repositories/data.dart';
import 'package:lensai/features/bangs/presentation/widgets/bang_icon.dart';
import 'package:lensai/features/bangs/presentation/widgets/search_field.dart';
import 'package:lensai/features/search_browser/domain/providers.dart';
import 'package:lensai/features/search_browser/presentation/controllers/bottom_sheet.dart';
import 'package:lensai/features/search_browser/presentation/widgets/sheets/shared_content_sheet.dart';
import 'package:lensai/features/share_intent/domain/entities/shared_content.dart';
import 'package:lensai/presentation/widgets/selectable_chips.dart';
@@ -6,7 +6,7 @@ import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:flutter_material_design_icons/flutter_material_design_icons.dart';
import 'package:flutter_pdf_text/flutter_pdf_text.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:lensai/features/search_browser/domain/entities/modes.dart';
import 'package:lensai/features/kagi/data/entities/modes.dart';
import 'package:lensai/features/search_browser/presentation/widgets/sheets/shared_content_sheet.dart';
import 'package:lensai/features/search_browser/utils/url_builder.dart'
as uri_builder;
@@ -1,6 +1,6 @@
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:lensai/features/web_view/domain/providers.dart';
import 'package:lensai/features/geckoview/domain/repositories/tab.dart';
class TabsActionButton extends HookConsumerWidget {
final bool isActive;
@@ -17,7 +17,7 @@ class TabsActionButton extends HookConsumerWidget {
final theme = Theme.of(context);
final tabCount =
ref.watch(activeTabsProvider.select((tabs) => tabs.length));
ref.watch(tabRepositoryProvider.select((tabs) => tabs.length));
return InkWell(
onTap: onTap,