From e1062b3443211f4d4bc4a4f1c6df0d581eaf76b6 Mon Sep 17 00:00:00 2001 From: ggurdin Date: Tue, 22 Oct 2024 10:54:46 -0400 Subject: [PATCH] if not add bot, show disabled, low opacity form. Moved state handling to top level widget --- .../conversation_bot_mode_dynamic_zone.dart | 27 +- .../conversation_bot_mode_select.dart | 8 +- .../conversation_bot_settings.dart | 332 +++++++++--------- .../conversation_bot_settings_form.dart | 68 ++-- .../space/language_level_dropdown.dart | 4 +- 5 files changed, 215 insertions(+), 224 deletions(-) diff --git a/lib/pangea/widgets/conversation_bot/conversation_bot_mode_dynamic_zone.dart b/lib/pangea/widgets/conversation_bot/conversation_bot_mode_dynamic_zone.dart index dc1997a20..f001fe8f7 100644 --- a/lib/pangea/widgets/conversation_bot/conversation_bot_mode_dynamic_zone.dart +++ b/lib/pangea/widgets/conversation_bot/conversation_bot_mode_dynamic_zone.dart @@ -4,20 +4,20 @@ import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; class ConversationBotModeDynamicZone extends StatelessWidget { - final BotOptionsModel initialBotOptions; - final GlobalKey formKey; - + final BotOptionsModel botOptions; final TextEditingController discussionTopicController; final TextEditingController discussionKeywordsController; final TextEditingController customSystemPromptController; + final bool enabled; + const ConversationBotModeDynamicZone({ super.key, - required this.initialBotOptions, - required this.formKey, + required this.botOptions, required this.discussionTopicController, required this.discussionKeywordsController, required this.customSystemPromptController, + this.enabled = true, }); @override @@ -29,9 +29,12 @@ class ConversationBotModeDynamicZone extends StatelessWidget { .conversationBotDiscussionZone_discussionTopicPlaceholder, ), controller: discussionTopicController, - validator: (value) => value == null || value.isEmpty + validator: (value) => enabled && + botOptions.mode == BotMode.discussion && + (value == null || value.isEmpty) ? L10n.of(context)!.enterDiscussionTopic : null, + enabled: enabled, ), const SizedBox(height: 12), TextFormField( @@ -40,6 +43,7 @@ class ConversationBotModeDynamicZone extends StatelessWidget { .conversationBotDiscussionZone_discussionKeywordsPlaceholder, ), controller: discussionKeywordsController, + enabled: enabled, ), ]; @@ -49,17 +53,20 @@ class ConversationBotModeDynamicZone extends StatelessWidget { hintText: L10n.of(context)! .conversationBotCustomZone_customSystemPromptPlaceholder, ), - validator: (value) => value == null || value.isEmpty + validator: (value) => enabled && + botOptions.mode == BotMode.custom && + (value == null || value.isEmpty) ? L10n.of(context)!.enterPrompt : null, controller: customSystemPromptController, + enabled: enabled, ), ]; return Column( children: [ - if (initialBotOptions.mode == BotMode.discussion) ...discussionChildren, - if (initialBotOptions.mode == BotMode.custom) ...customChildren, + if (botOptions.mode == BotMode.discussion) ...discussionChildren, + if (botOptions.mode == BotMode.custom) ...customChildren, const SizedBox(height: 12), CheckboxListTile( title: Text( @@ -67,7 +74,7 @@ class ConversationBotModeDynamicZone extends StatelessWidget { .conversationBotCustomZone_customTriggerReactionEnabledLabel, ), enabled: false, - value: initialBotOptions.customTriggerReactionEnabled ?? true, + value: botOptions.customTriggerReactionEnabled ?? true, onChanged: null, ), const SizedBox(height: 12), diff --git a/lib/pangea/widgets/conversation_bot/conversation_bot_mode_select.dart b/lib/pangea/widgets/conversation_bot/conversation_bot_mode_select.dart index c22801d25..3d893f078 100644 --- a/lib/pangea/widgets/conversation_bot/conversation_bot_mode_select.dart +++ b/lib/pangea/widgets/conversation_bot/conversation_bot_mode_select.dart @@ -4,12 +4,14 @@ import 'package:flutter_gen/gen_l10n/l10n.dart'; class ConversationBotModeSelect extends StatelessWidget { final String? initialMode; - final void Function(String?)? onChanged; + final void Function(String?) onChanged; + final bool enabled; const ConversationBotModeSelect({ super.key, this.initialMode, - this.onChanged, + required this.onChanged, + this.enabled = true, }); @override @@ -52,7 +54,7 @@ class ConversationBotModeSelect extends StatelessWidget { ), ), ], - onChanged: onChanged, + onChanged: enabled ? onChanged : null, ); } } diff --git a/lib/pangea/widgets/conversation_bot/conversation_bot_settings.dart b/lib/pangea/widgets/conversation_bot/conversation_bot_settings.dart index 7d94b6624..4ff52642f 100644 --- a/lib/pangea/widgets/conversation_bot/conversation_bot_settings.dart +++ b/lib/pangea/widgets/conversation_bot/conversation_bot_settings.dart @@ -1,5 +1,7 @@ import 'dart:developer'; +import 'package:fluffychat/config/themes.dart'; +import 'package:fluffychat/pangea/constants/bot_mode.dart'; import 'package:fluffychat/pangea/constants/pangea_event_types.dart'; import 'package:fluffychat/pangea/extensions/pangea_room_extension/pangea_room_extension.dart'; import 'package:fluffychat/pangea/models/bot_options_model.dart'; @@ -11,17 +13,14 @@ import 'package:fluffychat/widgets/matrix.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; -import 'package:future_loading_dialog/future_loading_dialog.dart'; import 'package:matrix/matrix.dart'; class ConversationBotSettings extends StatefulWidget { final Room room; - final String? activeSpaceId; const ConversationBotSettings({ super.key, required this.room, - this.activeSpaceId, }); @override @@ -29,35 +28,7 @@ class ConversationBotSettings extends StatefulWidget { } class ConversationBotSettingsState extends State { - late BotOptionsModel botOptions; - bool addBot = false; - - ConversationBotSettingsState({Key? key}); - - final TextEditingController discussionTopicController = - TextEditingController(); - final TextEditingController discussionKeywordsController = - TextEditingController(); - final TextEditingController customSystemPromptController = - TextEditingController(); - - @override - void initState() { - super.initState(); - botOptions = widget.room.botOptions != null - ? BotOptionsModel.fromJson(widget.room.botOptions?.toJson()) - : BotOptionsModel(); - - widget.room.botIsInRoom.then((bool isBotRoom) { - setState(() => addBot = isBotRoom); - }); - - discussionKeywordsController.text = botOptions.discussionKeywords ?? ""; - discussionTopicController.text = botOptions.discussionTopic ?? ""; - customSystemPromptController.text = botOptions.customSystemPrompt ?? ""; - } - - Future setBotOption() async { + Future setBotOptions(BotOptionsModel botOptions) async { try { await Matrix.of(context).client.setRoomStateWithKey( widget.room.id, @@ -71,77 +42,18 @@ class ConversationBotSettingsState extends State { } } - Future updateBotOption(void Function() makeLocalChange) async { - makeLocalChange(); - await showFutureLoadingDialog( - context: context, - future: () async { - try { - await setBotOption(); - } catch (err, stack) { - debugger(when: kDebugMode); - ErrorHandler.logError(e: err, s: stack); - } - setState(() {}); - }, - ); - } - - void updateFromTextControllers() { - botOptions.discussionTopic = discussionTopicController.text; - botOptions.discussionKeywords = discussionKeywordsController.text; - botOptions.customSystemPrompt = customSystemPromptController.text; - } - Future showBotOptionsDialog() async { - final bool? confirm = await showDialog( + final BotOptionsModel? newBotOptions = await showDialog( context: context, - builder: (BuildContext context) { - return StatefulBuilder( - builder: (context, setState) => Dialog( - child: Form( - key: formKey, - child: Container( - padding: const EdgeInsets.all(16), - constraints: const BoxConstraints( - maxWidth: 450, - maxHeight: 725, - ), - child: ClipRRect( - borderRadius: BorderRadius.circular(20.0), - child: ConversationBotSettingsDialog( - addBot: addBot, - botOptions: botOptions, - formKey: formKey, - updateAddBot: (bool value) => - setState(() => addBot = value), - discussionKeywordsController: discussionKeywordsController, - discussionTopicController: discussionTopicController, - customSystemPromptController: customSystemPromptController, - ), - ), - ), - ), - ), - ); - }, + builder: (BuildContext context) => + ConversationBotSettingsDialog(room: widget.room), ); - if (confirm == true) { - updateFromTextControllers(); - updateBotOption(() => botOptions = botOptions); - - final bool isBotRoomMember = await widget.room.botIsInRoom; - if (addBot && !isBotRoomMember) { - await widget.room.invite(BotName.byEnvironment); - } else if (!addBot && isBotRoomMember) { - await widget.room.kick(BotName.byEnvironment); - } + if (newBotOptions != null) { + setBotOptions(newBotOptions); } } - final GlobalKey formKey = GlobalKey(); - @override Widget build(BuildContext context) { return AnimatedContainer( @@ -175,91 +87,175 @@ class ConversationBotSettingsState extends State { } } -class ConversationBotSettingsDialog extends StatelessWidget { - final bool addBot; - final BotOptionsModel botOptions; - final GlobalKey formKey; - - final void Function(bool) updateAddBot; - - final TextEditingController discussionTopicController; - final TextEditingController discussionKeywordsController; - final TextEditingController customSystemPromptController; +class ConversationBotSettingsDialog extends StatefulWidget { + final Room room; const ConversationBotSettingsDialog({ super.key, - required this.addBot, - required this.botOptions, - required this.formKey, - required this.updateAddBot, - required this.discussionTopicController, - required this.discussionKeywordsController, - required this.customSystemPromptController, + required this.room, }); + @override + ConversationBotSettingsDialogState createState() => + ConversationBotSettingsDialogState(); +} + +class ConversationBotSettingsDialogState + extends State { + late BotOptionsModel botOptions; + bool addBot = false; + + final TextEditingController discussionTopicController = + TextEditingController(); + final TextEditingController discussionKeywordsController = + TextEditingController(); + final TextEditingController customSystemPromptController = + TextEditingController(); + + @override + void initState() { + super.initState(); + botOptions = widget.room.botOptions != null + ? BotOptionsModel.fromJson(widget.room.botOptions?.toJson()) + : BotOptionsModel(); + + widget.room.botIsInRoom.then((bool isBotRoom) { + setState(() => addBot = isBotRoom); + }); + + discussionKeywordsController.text = botOptions.discussionKeywords ?? ""; + discussionTopicController.text = botOptions.discussionTopic ?? ""; + customSystemPromptController.text = botOptions.customSystemPrompt ?? ""; + } + + final GlobalKey formKey = GlobalKey(); + + void updateFromTextControllers() { + botOptions.discussionTopic = discussionTopicController.text; + botOptions.discussionKeywords = discussionKeywordsController.text; + botOptions.customSystemPrompt = customSystemPromptController.text; + } + + void onUpdateChatMode(String? mode) { + setState(() => botOptions.mode = mode ?? BotMode.discussion); + } + + void onUpdateBotLanguage(String? language) { + setState(() => botOptions.targetLanguage = language); + } + + void onUpdateBotVoice(String? voice) { + setState(() => botOptions.targetVoice = voice); + } + + void onUpdateBotLanguageLevel(int? level) { + setState(() => botOptions.languageLevel = level); + } + @override Widget build(BuildContext context) { - return Column( - mainAxisSize: MainAxisSize.min, - children: [ - Align( - alignment: Alignment.centerLeft, - child: Padding( - padding: const EdgeInsets.symmetric( - vertical: 12, - ), - child: Text( - L10n.of(context)!.botConfig, - style: Theme.of(context).textTheme.titleLarge, - ), - ), - ), - SwitchListTile( - title: Text( - L10n.of(context)!.conversationBotStatus, - ), - value: addBot, - onChanged: updateAddBot, - contentPadding: const EdgeInsets.all(4), - ), - if (addBot) - Expanded( - child: SingleChildScrollView( - child: Column( + final dialogContent = Form( + key: formKey, + child: Container( + padding: const EdgeInsets.all(16), + constraints: kIsWeb + ? const BoxConstraints( + maxWidth: 450, + maxHeight: 725, + ) + : null, + child: ClipRRect( + borderRadius: BorderRadius.circular(20.0), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Align( + alignment: Alignment.centerLeft, + child: Padding( + padding: const EdgeInsets.symmetric( + vertical: 12, + ), + child: Text( + L10n.of(context)!.botConfig, + style: Theme.of(context).textTheme.titleLarge, + ), + ), + ), + SwitchListTile( + title: Text( + L10n.of(context)!.conversationBotStatus, + ), + value: addBot, + onChanged: (bool value) { + setState(() => addBot = value); + }, + contentPadding: const EdgeInsets.all(4), + ), + Expanded( + child: SingleChildScrollView( + child: Column( + children: [ + const SizedBox(height: 20), + AnimatedOpacity( + duration: FluffyThemes.animationDuration, + opacity: addBot ? 1.0 : 0.5, + child: ConversationBotSettingsForm( + botOptions: botOptions, + discussionKeywordsController: + discussionKeywordsController, + discussionTopicController: discussionTopicController, + customSystemPromptController: + customSystemPromptController, + enabled: addBot, + onUpdateBotMode: onUpdateChatMode, + onUpdateBotLanguage: onUpdateBotLanguage, + onUpdateBotVoice: onUpdateBotVoice, + onUpdateBotLanguageLevel: onUpdateBotLanguageLevel, + ), + ), + ], + ), + ), + ), + Row( + mainAxisAlignment: MainAxisAlignment.end, children: [ - const SizedBox(height: 20), - ConversationBotSettingsForm( - botOptions: botOptions, - formKey: formKey, - discussionKeywordsController: discussionKeywordsController, - discussionTopicController: discussionTopicController, - customSystemPromptController: customSystemPromptController, + TextButton( + onPressed: () { + Navigator.of(context).pop(null); + }, + child: Text(L10n.of(context)!.cancel), + ), + const SizedBox(width: 20), + TextButton( + onPressed: () async { + final isValid = formKey.currentState!.validate(); + if (!isValid) return; + + updateFromTextControllers(); + + final bool isBotRoomMember = + await widget.room.botIsInRoom; + if (addBot && !isBotRoomMember) { + await widget.room.invite(BotName.byEnvironment); + } else if (!addBot && isBotRoomMember) { + await widget.room.kick(BotName.byEnvironment); + } + + Navigator.of(context).pop(botOptions); + }, + child: Text(L10n.of(context)!.confirm), ), ], ), - ), + ], ), - Row( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - TextButton( - onPressed: () { - Navigator.of(context).pop(false); - }, - child: Text(L10n.of(context)!.cancel), - ), - const SizedBox(width: 20), - TextButton( - onPressed: () { - final isValid = formKey.currentState!.validate(); - if (!isValid) return; - Navigator.of(context).pop(true); - }, - child: Text(L10n.of(context)!.confirm), - ), - ], ), - ], + ), ); + + return kIsWeb + ? Dialog(child: dialogContent) + : Dialog.fullscreen(child: dialogContent); } } diff --git a/lib/pangea/widgets/conversation_bot/conversation_bot_settings_form.dart b/lib/pangea/widgets/conversation_bot/conversation_bot_settings_form.dart index 5447b67bb..195d35801 100644 --- a/lib/pangea/widgets/conversation_bot/conversation_bot_settings_form.dart +++ b/lib/pangea/widgets/conversation_bot/conversation_bot_settings_form.dart @@ -1,4 +1,3 @@ -import 'package:fluffychat/pangea/constants/bot_mode.dart'; import 'package:fluffychat/pangea/models/bot_options_model.dart'; import 'package:fluffychat/pangea/widgets/conversation_bot/conversation_bot_mode_dynamic_zone.dart'; import 'package:fluffychat/pangea/widgets/conversation_bot/conversation_bot_mode_select.dart'; @@ -7,38 +6,32 @@ import 'package:fluffychat/widgets/matrix.dart'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; -class ConversationBotSettingsForm extends StatefulWidget { +class ConversationBotSettingsForm extends StatelessWidget { final BotOptionsModel botOptions; - final GlobalKey formKey; final TextEditingController discussionTopicController; final TextEditingController discussionKeywordsController; final TextEditingController customSystemPromptController; + final bool enabled; + final void Function(String?) onUpdateBotMode; + final void Function(String?) onUpdateBotLanguage; + final void Function(String?) onUpdateBotVoice; + final void Function(int?) onUpdateBotLanguageLevel; + const ConversationBotSettingsForm({ super.key, required this.botOptions, - required this.formKey, required this.discussionTopicController, required this.discussionKeywordsController, required this.customSystemPromptController, + required this.onUpdateBotMode, + required this.onUpdateBotLanguage, + required this.onUpdateBotVoice, + required this.onUpdateBotLanguageLevel, + this.enabled = true, }); - @override - ConversationBotSettingsFormState createState() => - ConversationBotSettingsFormState(); -} - -class ConversationBotSettingsFormState - extends State { - late BotOptionsModel botOptions; - - @override - void initState() { - super.initState(); - botOptions = widget.botOptions; - } - @override Widget build(BuildContext context) { return Column( @@ -64,9 +57,7 @@ class ConversationBotSettingsFormState ), ); }).toList(), - onChanged: (String? newValue) => { - setState(() => botOptions.targetLanguage = newValue!), - }, + onChanged: enabled ? onUpdateBotLanguage : null, ), const SizedBox(height: 12), DropdownButtonFormField( @@ -80,20 +71,16 @@ class ConversationBotSettingsFormState isExpanded: true, icon: const Icon(Icons.keyboard_arrow_down), items: const [], - onChanged: (String? newValue) => { - setState(() => botOptions.targetVoice = newValue!), - }, + onChanged: enabled ? onUpdateBotVoice : null, ), const SizedBox(height: 12), LanguageLevelDropdown( initialLevel: botOptions.languageLevel, - onChanged: (int? newValue) => { - setState(() { - botOptions.languageLevel = newValue!; - }), - }, - validator: (value) => - value == null ? L10n.of(context)!.enterLanguageLevel : null, + onChanged: onUpdateBotLanguageLevel, + validator: (value) => enabled && value == null + ? L10n.of(context)!.enterLanguageLevel + : null, + enabled: enabled, ), const SizedBox(height: 12), Align( @@ -108,19 +95,16 @@ class ConversationBotSettingsFormState ), ConversationBotModeSelect( initialMode: botOptions.mode, - onChanged: (String? mode) => { - setState(() { - botOptions.mode = mode ?? BotMode.discussion; - }), - }, + onChanged: onUpdateBotMode, + enabled: enabled, ), const SizedBox(height: 12), ConversationBotModeDynamicZone( - initialBotOptions: botOptions, - discussionTopicController: widget.discussionTopicController, - discussionKeywordsController: widget.discussionKeywordsController, - customSystemPromptController: widget.customSystemPromptController, - formKey: widget.formKey, + botOptions: botOptions, + discussionTopicController: discussionTopicController, + discussionKeywordsController: discussionKeywordsController, + customSystemPromptController: customSystemPromptController, + enabled: enabled, ), ], ); diff --git a/lib/pangea/widgets/space/language_level_dropdown.dart b/lib/pangea/widgets/space/language_level_dropdown.dart index 0964a49ab..0b238485a 100644 --- a/lib/pangea/widgets/space/language_level_dropdown.dart +++ b/lib/pangea/widgets/space/language_level_dropdown.dart @@ -7,12 +7,14 @@ class LanguageLevelDropdown extends StatelessWidget { final int? initialLevel; final void Function(int?)? onChanged; final String? Function(int?)? validator; + final bool enabled; const LanguageLevelDropdown({ super.key, this.initialLevel, this.onChanged, this.validator, + this.enabled = true, }); @override @@ -42,7 +44,7 @@ class LanguageLevelDropdown extends StatelessWidget { ), ); }).toList(), - onChanged: onChanged, + onChanged: enabled ? onChanged : null, validator: validator, ); }