diff --git a/app/lib/features/search_browser/domain/providers.dart b/app/lib/features/search_browser/domain/providers.dart index cfcda982..49bbcc51 100644 --- a/app/lib/features/search_browser/domain/providers.dart +++ b/app/lib/features/search_browser/domain/providers.dart @@ -4,6 +4,7 @@ import 'dart:async'; import 'package:bang_navigator/features/bangs/data/models/bang_data.dart'; import 'package:bang_navigator/features/bangs/domain/repositories/data.dart'; +import 'package:bang_navigator/features/search_browser/domain/entities/modes.dart'; import 'package:bang_navigator/features/search_browser/domain/entities/sheet.dart'; import 'package:bang_navigator/features/search_browser/domain/services/create_tab.dart'; import 'package:flutter/widgets.dart'; @@ -91,3 +92,39 @@ Stream selectedBangData(SelectedBangDataRef ref, {String? domain}) { ref.watch(selectedBangTriggerProvider(domain: domain)); return repository.watchBang(selectedBangTrigger); } + +@Riverpod(keepAlive: true) +class LastUsedAssistantMode extends _$LastUsedAssistantMode { + void update(AssistantMode mode) { + state = mode; + } + + @override + AssistantMode build() { + return AssistantMode.research; + } +} + +@Riverpod(keepAlive: true) +class ActiveResearchVariant extends _$ActiveResearchVariant { + void update(ResearchVariant mode) { + state = mode; + } + + @override + ResearchVariant build() { + return ResearchVariant.expert; + } +} + +@Riverpod(keepAlive: true) +class ActiveChatModel extends _$ActiveChatModel { + void update(ChatModel model) { + state = model; + } + + @override + ChatModel build() { + return ChatModel.gpt4o; + } +} diff --git a/app/lib/features/search_browser/domain/providers.g.dart b/app/lib/features/search_browser/domain/providers.g.dart index 0ccb9939..f28a9c83 100644 --- a/app/lib/features/search_browser/domain/providers.g.dart +++ b/app/lib/features/search_browser/domain/providers.g.dart @@ -345,5 +345,56 @@ class _SelectedBangTriggerProviderElement @override String? get domain => (origin as SelectedBangTriggerProvider).domain; } + +String _$lastUsedAssistantModeHash() => + r'1255a2754a3ea5ea058d110fd9743897fe103a69'; + +/// See also [LastUsedAssistantMode]. +@ProviderFor(LastUsedAssistantMode) +final lastUsedAssistantModeProvider = + NotifierProvider.internal( + LastUsedAssistantMode.new, + name: r'lastUsedAssistantModeProvider', + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$lastUsedAssistantModeHash, + dependencies: null, + allTransitiveDependencies: null, +); + +typedef _$LastUsedAssistantMode = Notifier; +String _$activeResearchVariantHash() => + r'8c65a3f5ead6cc4f9e2af85b607b7a49b0764ee3'; + +/// See also [ActiveResearchVariant]. +@ProviderFor(ActiveResearchVariant) +final activeResearchVariantProvider = + NotifierProvider.internal( + ActiveResearchVariant.new, + name: r'activeResearchVariantProvider', + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$activeResearchVariantHash, + dependencies: null, + allTransitiveDependencies: null, +); + +typedef _$ActiveResearchVariant = Notifier; +String _$activeChatModelHash() => r'675f406af8b4aa92699d59e37b5805a81d03a799'; + +/// See also [ActiveChatModel]. +@ProviderFor(ActiveChatModel) +final activeChatModelProvider = + NotifierProvider.internal( + ActiveChatModel.new, + name: r'activeChatModelProvider', + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$activeChatModelHash, + dependencies: null, + allTransitiveDependencies: null, +); + +typedef _$ActiveChatModel = Notifier; // ignore_for_file: type=lint // ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member diff --git a/app/lib/features/search_browser/presentation/widgets/tabs/assistant_tab.dart b/app/lib/features/search_browser/presentation/widgets/tabs/assistant_tab.dart index 6038bd3b..70e2165a 100644 --- a/app/lib/features/search_browser/presentation/widgets/tabs/assistant_tab.dart +++ b/app/lib/features/search_browser/presentation/widgets/tabs/assistant_tab.dart @@ -1,4 +1,5 @@ import 'package:bang_navigator/features/search_browser/domain/entities/modes.dart'; +import 'package:bang_navigator/features/search_browser/domain/providers.dart'; import 'package:bang_navigator/features/search_browser/presentation/widgets/sheets/shared_content_sheet.dart'; import 'package:bang_navigator/features/search_browser/presentation/widgets/speech_to_text_button.dart'; import 'package:bang_navigator/features/search_browser/utils/url_builder.dart' @@ -29,20 +30,30 @@ class AssistantTab extends HookConsumerWidget { .select((value) => value.valueOrNull?.incognitoMode ?? false), ); + final researchVariant = ref.watch(activeResearchVariantProvider); + final chatModel = ref.watch(activeChatModelProvider); + useAutomaticKeepAlive(); final formKey = useMemoized(() => GlobalKey()); final textController = useTextEditingController(text: sharedContent?.toString()); - final tabController = - useTabController(initialLength: AssistantMode.values.length); + final tabController = useTabController( + initialLength: AssistantMode.values.length, + initialIndex: ref.read(lastUsedAssistantModeProvider).index, + ); final pageController = usePageController(initialPage: tabController.index); - useSyncPageWithTab(tabController, pageController); - - final researchVariant = useState(ResearchVariant.expert); - final chatModel = useState(ChatModel.gpt4o); + useSyncPageWithTab( + tabController, + pageController, + onIndexChanged: (index) { + ref + .read(lastUsedAssistantModeProvider.notifier) + .update(AssistantMode.values[index]); + }, + ); return Form( key: formKey, @@ -92,9 +103,11 @@ class AssistantTab extends HookConsumerWidget { label: Text('Fast'), ), ], - selected: {researchVariant.value}, + selected: {researchVariant}, onSelectionChanged: (value) { - researchVariant.value = value.first; + ref + .read(activeResearchVariantProvider.notifier) + .update(value.first); }, ), ], @@ -107,7 +120,7 @@ class AssistantTab extends HookConsumerWidget { height: 12, ), DropdownMenu( - initialSelection: chatModel.value, + initialSelection: chatModel, expandedInsets: EdgeInsets.zero, label: const Text('Model'), inputDecorationTheme: const InputDecorationTheme(), @@ -120,7 +133,7 @@ class AssistantTab extends HookConsumerWidget { ) .toList(), onSelected: (value) { - chatModel.value = value!; + ref.read(activeChatModelProvider.notifier).update(value!); }, ), ], @@ -165,8 +178,8 @@ class AssistantTab extends HookConsumerWidget { uri_builder.assistantUri( prompt: textController.text, assistantMode: AssistantMode.values[tabController.index], - researchVariant: researchVariant.value, - chatModel: chatModel.value, + researchVariant: researchVariant, + chatModel: chatModel, ), ); } diff --git a/app/lib/presentation/hooks/sync_page_tab.dart b/app/lib/presentation/hooks/sync_page_tab.dart index 1286e55b..4d0162d6 100644 --- a/app/lib/presentation/hooks/sync_page_tab.dart +++ b/app/lib/presentation/hooks/sync_page_tab.dart @@ -3,8 +3,9 @@ import 'package:flutter_hooks/flutter_hooks.dart'; void useSyncPageWithTab( TabController tabController, - PageController pageController, -) { + PageController pageController, { + void Function(int index)? onIndexChanged, +}) { useEffect( () { Future syncPage() async { @@ -13,11 +14,15 @@ void useSyncPageWithTab( curve: Curves.linear, duration: const Duration(milliseconds: 300), ); + onIndexChanged?.call(tabController.index); } void syncTab() { if (!tabController.indexIsChanging) { - tabController.animateTo(pageController.page!.round()); + final index = pageController.page!.round(); + + tabController.animateTo(index); + onIndexChanged?.call(index); } }