From 8673be494df5c8a609a962d8f585b60a8e43a137 Mon Sep 17 00:00:00 2001 From: Jamie Hu Date: Thu, 30 Nov 2023 15:45:24 +0800 Subject: [PATCH] feat: The model selection interface supports grouping and disabling functions --- .../settings/geekerchat_active.dart | 2 +- .../grouped_bottom_sheet_switcher.dart | 123 ++++++++++++++++++ lib/controller/settings.dart | 25 ++++ lib/models/bottom_switcher_model.dart | 20 +++ lib/models/model.dart | 11 ++ lib/pages/chat_edit.dart | 36 ++++- macos/Podfile.lock | 2 +- 7 files changed, 213 insertions(+), 6 deletions(-) create mode 100644 lib/components/settings/grouped_bottom_sheet_switcher.dart create mode 100644 lib/models/bottom_switcher_model.dart diff --git a/lib/components/settings/geekerchat_active.dart b/lib/components/settings/geekerchat_active.dart index 22d1a99..2dea32d 100644 --- a/lib/components/settings/geekerchat_active.dart +++ b/lib/components/settings/geekerchat_active.dart @@ -121,7 +121,7 @@ class GeekerChatSettingsComponent extends StatelessWidget { submitActive(SettingsServerController controller, BuildContext context) { if (controller.defaultServer.license.isNotEmpty) { - if (controller.needReactive) { + if (controller.needReactive || !controller.defaultServer.isActived) { controller .activeLicense(controller.defaultServer.license, settingsController.settings.uuid, settingsController.lang) diff --git a/lib/components/settings/grouped_bottom_sheet_switcher.dart b/lib/components/settings/grouped_bottom_sheet_switcher.dart new file mode 100644 index 0000000..dd37d05 --- /dev/null +++ b/lib/components/settings/grouped_bottom_sheet_switcher.dart @@ -0,0 +1,123 @@ +import 'package:flutter/material.dart'; +import 'package:geek_chat/controller/settings.dart'; +import 'package:geek_chat/models/bottom_switcher_model.dart'; +import 'package:get/get.dart'; +import 'package:logger/logger.dart'; + +// ignore: must_be_immutable +class GroupedBottomSheetSwitcherComponent extends StatelessWidget { + GroupedBottomSheetSwitcherComponent( + {super.key, + required this.title, + required this.subTitle, + required this.selectedValue, + required this.options, + required this.leadingIcon, + required this.onTapCallback}); + + String title; + String subTitle; + String selectedValue; + Logger logger = Get.find(); + // List> options; + List options; + Function onTapCallback; + IconData leadingIcon; + + List buildOptionsList(BuildContext context) { + List widgets = []; + widgets.add(ListTile( + dense: true, + title: Text( + title.tr, + style: const TextStyle(fontWeight: FontWeight.bold), + ), + tileColor: Theme.of(context).scaffoldBackgroundColor, + )); + for (GroupedBottomSwitcherOption group in options) { + widgets.add(ListTile( + dense: true, + title: Text(group.groupName), + )); + for (BottomSwitcherOption option in group.options) { + if (option.disabled) { + widgets.add(ListTile( + title: Row( + children: [ + Radio( + value: option.id, + groupValue: selectedValue, + onChanged: null, + ), + Text( + option.name.tr, + ), + ], + ), + )); + } else { + widgets.add(ListTile( + title: Row( + children: [ + Radio( + value: option.id, + groupValue: selectedValue, + onChanged: (value) { + onTapCallback(value); + Navigator.pop(context); + }, + ), + Text( + option.name.tr, + ), + ], + ), + onTap: () { + /// + onTapCallback(option.id); + Navigator.pop(context); + }, + )); + } + } + } + + widgets.add(Container( + padding: const EdgeInsets.only(top: 20), + )); + return widgets; + } + + @override + Widget build(BuildContext context) { + logger.d("options: $options"); + return GetBuilder(builder: ((controller) { + return ListTile( + title: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(title.tr), + Text( + subTitle.tr, + style: const TextStyle(fontSize: 12), + ) + ], + ), + leading: Icon(leadingIcon), + trailing: const Icon(Icons.chevron_right_outlined), + onTap: () { + showModalBottomSheet( + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(5.0)), + context: context, + builder: (context) { + return SafeArea( + child: Wrap( + children: buildOptionsList(context), + )); + }); + }, + ); + })); + } +} diff --git a/lib/controller/settings.dart b/lib/controller/settings.dart index d95df23..12f8789 100644 --- a/lib/controller/settings.dart +++ b/lib/controller/settings.dart @@ -46,6 +46,31 @@ class SettingsController extends GetxController { DeviceType deviceType = DeviceType.wide; + final List _aiGroups = [ + AiGroup( + aitype: AiType.chatgpt, + groupName: "ChatGPT", + groupDesc: "OpenAI ChatGPT"), + AiGroup( + aitype: AiType.bard, + groupName: "Google Vertex AI", + groupDesc: "Google Vertex AI") + ]; + + List get aiGroups { + return _aiGroups; + } + + List getModelsByType(AiType aiType) { + List models = []; + for (AiModel aiModel in _aiModels) { + if (aiModel.aiType == aiType) { + models.add(aiModel); + } + } + return models; + } + final List _aiModels = [ AiModel( modelName: 'gpt-3.5-turbo', diff --git a/lib/models/bottom_switcher_model.dart b/lib/models/bottom_switcher_model.dart new file mode 100644 index 0000000..c5024ff --- /dev/null +++ b/lib/models/bottom_switcher_model.dart @@ -0,0 +1,20 @@ +class BottomSwitcherOption { + BottomSwitcherOption({ + required this.id, + required this.name, + required this.additionalText, + required this.disabled, + }); + String id; + String name; + String additionalText; + bool disabled; +} + +class GroupedBottomSwitcherOption { + GroupedBottomSwitcherOption( + {required this.groupName, required this.groupDesc}); + String groupName; + String groupDesc; + List options = []; +} diff --git a/lib/models/model.dart b/lib/models/model.dart index 699790e..07185c2 100644 --- a/lib/models/model.dart +++ b/lib/models/model.dart @@ -5,6 +5,17 @@ enum AiType { enum ModelType { chat, text, image } +class AiGroup { + AiGroup({ + required this.aitype, + required this.groupName, + required this.groupDesc, + }); + String groupName; + AiType aitype; + String groupDesc; +} + class AiModel { String modelName; String alias; diff --git a/lib/pages/chat_edit.dart b/lib/pages/chat_edit.dart index 1a77918..d3e8a96 100644 --- a/lib/pages/chat_edit.dart +++ b/lib/pages/chat_edit.dart @@ -1,10 +1,13 @@ import 'package:flutter/material.dart'; -import 'package:geek_chat/components/settings/bottom_sheet_switcher.dart'; +// import 'package:geek_chat/components/settings/bottom_sheet_switcher.dart'; +import 'package:geek_chat/components/settings/grouped_bottom_sheet_switcher.dart'; import 'package:geek_chat/controller/chat_list_controller.dart'; import 'package:geek_chat/controller/settings.dart'; import 'package:geek_chat/controller/settings_server_controller.dart'; +import 'package:geek_chat/models/bottom_switcher_model.dart'; import 'package:geek_chat/models/model.dart'; import 'package:get/get.dart'; +import 'package:logger/logger.dart'; // ignore: must_be_immutable class ChatEditPage extends StatelessWidget { @@ -12,6 +15,7 @@ class ChatEditPage extends StatelessWidget { SettingsController settingsController = Get.find(); SettingsServerController settingsServerController = Get.find(); + Logger logger = Get.find(); List> getModelOptions() { List> options = []; @@ -24,6 +28,29 @@ class ChatEditPage extends StatelessWidget { return options; } + List getGroupedModelOptions() { + List options = []; + for (AiGroup group in settingsController.aiGroups) { + // options.add(value) + GroupedBottomSwitcherOption gOptions = GroupedBottomSwitcherOption( + groupName: group.groupName, groupDesc: group.groupDesc); + for (AiModel model in settingsController.getModelsByType(group.aitype)) { + bool disabled = false; + if (settingsServerController.defaultServer.provider != "geekerchat" && + model.aiType == AiType.bard) { + disabled = true; + } + gOptions.options.add(BottomSwitcherOption( + id: model.modelName, + name: model.modelName, + additionalText: "", + disabled: disabled)); + } + options.add(gOptions); + } + return options; + } + String getTitle(String? opt) { String title = "New Chat"; if (opt == 'edit') { @@ -37,7 +64,8 @@ class ChatEditPage extends StatelessWidget { @override Widget build(BuildContext context) { var data = Get.parameters; - var options = getModelOptions(); + // var options = getModelOptions(); + if (data['opt'] == 'new') { chatListController.createNewSession(); } else { @@ -159,11 +187,11 @@ class ChatEditPage extends StatelessWidget { style: const TextStyle(fontWeight: FontWeight.bold), ), ), - BottomSheetSwitcherComponent( + GroupedBottomSheetSwitcherComponent( title: 'Model', subTitle: controller.currentSession.model, selectedValue: controller.currentSession.model, - options: options, + options: getGroupedModelOptions(), leadingIcon: Icons.smart_toy_outlined, onTapCallback: (value) { controller.currentSession.model = value; diff --git a/macos/Podfile.lock b/macos/Podfile.lock index 38ea8b6..fff328c 100644 --- a/macos/Podfile.lock +++ b/macos/Podfile.lock @@ -50,4 +50,4 @@ SPEC CHECKSUMS: PODFILE CHECKSUM: 236401fc2c932af29a9fcf0e97baeeb2d750d367 -COCOAPODS: 1.14.1 +COCOAPODS: 1.13.0