4307 bot in dms and activity chats only (#4319)
* remove bot settings files * exclude bot in invite page * listen to language / CERF level changes and update bot DM settings * Update lib/pangea/common/controllers/pangea_controller.dart Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
parent
71ea85876a
commit
dc8bb8fe8c
17 changed files with 99 additions and 797 deletions
|
|
@ -1,6 +1,3 @@
|
|||
import 'dart:developer';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:collection/collection.dart';
|
||||
|
|
@ -12,12 +9,9 @@ import 'package:matrix/matrix.dart';
|
|||
import 'package:fluffychat/l10n/l10n.dart';
|
||||
import 'package:fluffychat/pages/settings/settings.dart';
|
||||
import 'package:fluffychat/pangea/chat/constants/default_power_level.dart';
|
||||
import 'package:fluffychat/pangea/chat_settings/models/bot_options_model.dart';
|
||||
import 'package:fluffychat/pangea/chat_settings/pages/pangea_room_details.dart';
|
||||
import 'package:fluffychat/pangea/common/utils/error_handler.dart';
|
||||
import 'package:fluffychat/pangea/download/download_room_extension.dart';
|
||||
import 'package:fluffychat/pangea/download/download_type_enum.dart';
|
||||
import 'package:fluffychat/pangea/events/constants/pangea_event_types.dart';
|
||||
import 'package:fluffychat/pangea/extensions/join_rule_extension.dart';
|
||||
import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart';
|
||||
import 'package:fluffychat/utils/file_selector.dart';
|
||||
|
|
@ -269,31 +263,6 @@ class ChatDetailsController extends State<ChatDetails> {
|
|||
}
|
||||
}
|
||||
|
||||
Future<void> setBotOptions(BotOptionsModel botOptions) async {
|
||||
if (roomId == null) return;
|
||||
final Room? room = Matrix.of(context).client.getRoomById(roomId!);
|
||||
if (room == null) return;
|
||||
|
||||
try {
|
||||
await Matrix.of(context).client.setRoomStateWithKey(
|
||||
room.id,
|
||||
PangeaEventTypes.botOptions,
|
||||
'',
|
||||
botOptions.toJson(),
|
||||
);
|
||||
} catch (err, stack) {
|
||||
debugger(when: kDebugMode);
|
||||
ErrorHandler.logError(
|
||||
e: err,
|
||||
s: stack,
|
||||
data: {
|
||||
"botOptions": botOptions.toJson(),
|
||||
"roomID": room.id,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> setRoomCapacity() async {
|
||||
if (roomId == null) return;
|
||||
final Room? room = Matrix.of(context).client.getRoomById(roomId!);
|
||||
|
|
|
|||
21
lib/pangea/bot/utils/bot_room_extension.dart
Normal file
21
lib/pangea/bot/utils/bot_room_extension.dart
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
import 'package:matrix/matrix.dart';
|
||||
|
||||
import 'package:fluffychat/pangea/chat_settings/models/bot_options_model.dart';
|
||||
import 'package:fluffychat/pangea/events/constants/pangea_event_types.dart';
|
||||
|
||||
extension BotRoomExtension on Room {
|
||||
BotOptionsModel? get botOptions {
|
||||
if (isSpace) return null;
|
||||
final stateEvent = getState(PangeaEventTypes.botOptions);
|
||||
if (stateEvent == null) return null;
|
||||
return BotOptionsModel.fromJson(stateEvent.content);
|
||||
}
|
||||
|
||||
Future<void> setBotOptions(BotOptionsModel options) =>
|
||||
client.setRoomStateWithKey(
|
||||
id,
|
||||
PangeaEventTypes.botOptions,
|
||||
'',
|
||||
options.toJson(),
|
||||
);
|
||||
}
|
||||
|
|
@ -2,12 +2,9 @@ import 'dart:developer';
|
|||
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
import 'package:matrix/matrix.dart';
|
||||
|
||||
import 'package:fluffychat/pangea/chat_settings/constants/bot_mode.dart';
|
||||
import 'package:fluffychat/pangea/common/constants/model_keys.dart';
|
||||
import 'package:fluffychat/pangea/common/utils/error_handler.dart';
|
||||
import 'package:fluffychat/pangea/events/constants/pangea_event_types.dart';
|
||||
import 'package:fluffychat/pangea/learning_settings/enums/language_level_type_enum.dart';
|
||||
|
||||
class BotOptionsModel {
|
||||
|
|
@ -182,9 +179,4 @@ class BotOptionsModel {
|
|||
throw Exception('Invalid key for bot options - $key');
|
||||
}
|
||||
}
|
||||
|
||||
StateEvent get toStateEvent => StateEvent(
|
||||
content: toJson(),
|
||||
type: PangeaEventTypes.botOptions,
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,11 +10,8 @@ import 'package:fluffychat/config/themes.dart';
|
|||
import 'package:fluffychat/l10n/l10n.dart';
|
||||
import 'package:fluffychat/pages/chat_details/chat_details.dart';
|
||||
import 'package:fluffychat/pangea/activity_sessions/activity_room_extension.dart';
|
||||
import 'package:fluffychat/pangea/bot/widgets/bot_face_svg.dart';
|
||||
import 'package:fluffychat/pangea/chat_settings/models/bot_options_model.dart';
|
||||
import 'package:fluffychat/pangea/chat_settings/pages/room_details_buttons.dart';
|
||||
import 'package:fluffychat/pangea/chat_settings/utils/delete_room.dart';
|
||||
import 'package:fluffychat/pangea/chat_settings/widgets/conversation_bot/conversation_bot_settings.dart';
|
||||
import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart';
|
||||
import 'package:fluffychat/widgets/adaptive_dialogs/show_ok_cancel_alert_dialog.dart';
|
||||
import 'package:fluffychat/widgets/future_loading_dialog.dart';
|
||||
|
|
@ -116,23 +113,6 @@ class ChatDetailsButtonRowState extends State<ChatDetailsButtonRow> {
|
|||
enabled: room.ownPowerLevel >= 50,
|
||||
showInMainView: false,
|
||||
),
|
||||
ButtonDetails(
|
||||
title: l10n.botSettings,
|
||||
icon: const BotFace(
|
||||
width: 30.0,
|
||||
expression: BotExpression.idle,
|
||||
),
|
||||
onPressed: () => showDialog<BotOptionsModel?>(
|
||||
context: context,
|
||||
builder: (BuildContext context) => ConversationBotSettingsDialog(
|
||||
room: room,
|
||||
onSubmit: widget.controller.setBotOptions,
|
||||
),
|
||||
),
|
||||
visible: (!room.isDirectChat || room.botOptions != null) &&
|
||||
!room.showActivityChatUI,
|
||||
enabled: room.canInvite,
|
||||
),
|
||||
ButtonDetails(
|
||||
title: l10n.chatCapacity,
|
||||
icon: const Icon(Icons.reduce_capacity, size: 30.0),
|
||||
|
|
|
|||
|
|
@ -194,10 +194,6 @@ class PangeaInvitationSelectionController
|
|||
if (a.id == client.userID) return -1;
|
||||
if (b.id == client.userID) return 1;
|
||||
|
||||
// sort the bot to the bottom
|
||||
if (a.id == BotName.byEnvironment) return 1;
|
||||
if (b.id == BotName.byEnvironment) return -1;
|
||||
|
||||
if (participants != null) {
|
||||
final participantA = participants!.firstWhereOrNull((u) => u.id == a.id);
|
||||
final participantB = participants!.firstWhereOrNull((u) => u.id == b.id);
|
||||
|
|
@ -268,10 +264,8 @@ class PangeaInvitationSelectionController
|
|||
)
|
||||
.toList();
|
||||
|
||||
contacts.removeWhere((u) => u.id == BotName.byEnvironment);
|
||||
contacts.sort(_sortUsers);
|
||||
if (_room?.isSpace ?? false) {
|
||||
contacts.removeWhere((u) => u.id == BotName.byEnvironment);
|
||||
}
|
||||
return contacts;
|
||||
}
|
||||
|
||||
|
|
@ -347,9 +341,7 @@ class PangeaInvitationSelectionController
|
|||
}
|
||||
|
||||
final results = response.results;
|
||||
if (_room?.isSpace ?? false) {
|
||||
results.removeWhere((profile) => profile.userId == BotName.byEnvironment);
|
||||
}
|
||||
results.removeWhere((profile) => profile.userId == BotName.byEnvironment);
|
||||
|
||||
setState(() {
|
||||
foundProfiles = List<Profile>.from(results);
|
||||
|
|
@ -372,8 +364,8 @@ class PangeaInvitationSelectionController
|
|||
|
||||
foundProfiles.removeWhere(
|
||||
(profile) =>
|
||||
participants?.indexWhere((u) => u.id == profile.userId) != -1 &&
|
||||
BotName.byEnvironment != profile.userId,
|
||||
participants?.indexWhere((u) => u.id == profile.userId) != -1 ||
|
||||
BotName.byEnvironment == profile.userId,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,13 @@
|
|||
import 'package:collection/collection.dart';
|
||||
import 'package:matrix/matrix.dart';
|
||||
|
||||
import 'package:fluffychat/pangea/bot/utils/bot_name.dart';
|
||||
import 'package:fluffychat/pangea/bot/utils/bot_room_extension.dart';
|
||||
import 'package:fluffychat/pangea/chat/constants/default_power_level.dart';
|
||||
import 'package:fluffychat/pangea/chat_settings/constants/bot_mode.dart';
|
||||
import 'package:fluffychat/pangea/chat_settings/models/bot_options_model.dart';
|
||||
import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart';
|
||||
import 'package:fluffychat/pangea/events/constants/pangea_event_types.dart';
|
||||
import 'package:fluffychat/widgets/matrix.dart';
|
||||
|
||||
extension BotClientExtension on Client {
|
||||
bool get hasBotDM => rooms.any((room) {
|
||||
|
|
@ -18,11 +21,33 @@ extension BotClientExtension on Client {
|
|||
return false;
|
||||
});
|
||||
|
||||
Room? get botDM => rooms.firstWhereOrNull(
|
||||
(room) {
|
||||
if (room.isDirectChat &&
|
||||
room.directChatMatrixID == BotName.byEnvironment) {
|
||||
return true;
|
||||
}
|
||||
if (room.botOptions?.mode == BotMode.directChat) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
);
|
||||
|
||||
Future<String> startChatWithBot() => startDirectChat(
|
||||
BotName.byEnvironment,
|
||||
preset: CreateRoomPreset.trustedPrivateChat,
|
||||
initialState: [
|
||||
BotOptionsModel(mode: BotMode.directChat).toStateEvent,
|
||||
StateEvent(
|
||||
content: BotOptionsModel(
|
||||
mode: BotMode.directChat,
|
||||
targetLanguage: MatrixState
|
||||
.pangeaController.languageController.userL2?.langCode,
|
||||
languageLevel: MatrixState.pangeaController.userController.profile
|
||||
.userSettings.cefrLevel,
|
||||
).toJson(),
|
||||
type: PangeaEventTypes.botOptions,
|
||||
),
|
||||
RoomDefaults.defaultPowerLevels(
|
||||
userID!,
|
||||
),
|
||||
|
|
|
|||
|
|
@ -1,16 +0,0 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
class ConversationBotConversationZone extends StatelessWidget {
|
||||
const ConversationBotConversationZone({
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return const Column(
|
||||
children: [
|
||||
Text('Conversation Zone'),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,99 +0,0 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:fluffychat/l10n/l10n.dart';
|
||||
import 'package:fluffychat/pangea/chat_settings/constants/bot_mode.dart';
|
||||
import 'package:fluffychat/pangea/chat_settings/widgets/conversation_bot/conversation_bot_no_permission_dialog.dart';
|
||||
|
||||
class ConversationBotModeDynamicZone extends StatelessWidget {
|
||||
final String? mode;
|
||||
final TextEditingController discussionTopicController;
|
||||
final TextEditingController discussionKeywordsController;
|
||||
final TextEditingController customSystemPromptController;
|
||||
|
||||
final bool enabled;
|
||||
final bool hasPermission;
|
||||
|
||||
const ConversationBotModeDynamicZone({
|
||||
super.key,
|
||||
required this.discussionTopicController,
|
||||
required this.discussionKeywordsController,
|
||||
required this.customSystemPromptController,
|
||||
required this.hasPermission,
|
||||
this.enabled = true,
|
||||
this.mode,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final discussionChildren = [
|
||||
InkWell(
|
||||
onTap: hasPermission ? null : () => showNoPermissionDialog(context),
|
||||
child: TextFormField(
|
||||
onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(),
|
||||
decoration: InputDecoration(
|
||||
hintText: L10n.of(context)
|
||||
.conversationBotDiscussionZone_discussionTopicPlaceholder,
|
||||
),
|
||||
controller: discussionTopicController,
|
||||
validator: (value) => enabled &&
|
||||
mode == BotMode.discussion &&
|
||||
(value == null || value.isEmpty)
|
||||
? L10n.of(context).enterDiscussionTopic
|
||||
: null,
|
||||
enabled: hasPermission && enabled,
|
||||
minLines: 1, // Minimum number of lines
|
||||
maxLines: null, // Allow the field to expand based on content
|
||||
keyboardType: TextInputType.multiline,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
InkWell(
|
||||
onTap: hasPermission ? null : () => showNoPermissionDialog(context),
|
||||
child: TextFormField(
|
||||
onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(),
|
||||
decoration: InputDecoration(
|
||||
hintText: L10n.of(context)
|
||||
.conversationBotDiscussionZone_discussionKeywordsPlaceholder,
|
||||
),
|
||||
controller: discussionKeywordsController,
|
||||
enabled: hasPermission && enabled,
|
||||
minLines: 1, // Minimum number of lines
|
||||
maxLines: null, // Allow the field to expand based on content
|
||||
keyboardType: TextInputType.multiline,
|
||||
),
|
||||
),
|
||||
];
|
||||
|
||||
final customChildren = [
|
||||
InkWell(
|
||||
onTap: hasPermission ? null : () => showNoPermissionDialog(context),
|
||||
child: TextFormField(
|
||||
onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(),
|
||||
decoration: InputDecoration(
|
||||
hintText: L10n.of(context)
|
||||
.conversationBotCustomZone_customSystemPromptPlaceholder,
|
||||
contentPadding: const EdgeInsets.symmetric(horizontal: 28.0),
|
||||
),
|
||||
validator: (value) => enabled &&
|
||||
mode == BotMode.custom &&
|
||||
(value == null || value.isEmpty)
|
||||
? L10n.of(context).enterPrompt
|
||||
: null,
|
||||
controller: customSystemPromptController,
|
||||
enabled: hasPermission && enabled,
|
||||
minLines: 1, // Minimum number of lines
|
||||
maxLines: null, // Allow the field to expand based on content
|
||||
keyboardType: TextInputType.multiline,
|
||||
),
|
||||
),
|
||||
];
|
||||
|
||||
return Column(
|
||||
children: [
|
||||
if (mode == BotMode.discussion) ...discussionChildren,
|
||||
if (mode == BotMode.custom) ...customChildren,
|
||||
const SizedBox(height: 12),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,66 +0,0 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:dropdown_button2/dropdown_button2.dart';
|
||||
|
||||
import 'package:fluffychat/l10n/l10n.dart';
|
||||
import 'package:fluffychat/pangea/chat_settings/constants/bot_mode.dart';
|
||||
import 'package:fluffychat/pangea/common/widgets/dropdown_text_button.dart';
|
||||
|
||||
class ConversationBotModeSelect extends StatelessWidget {
|
||||
final String? initialMode;
|
||||
final void Function(String?)? onChanged;
|
||||
final bool enabled;
|
||||
final String? Function(String?)? validator;
|
||||
|
||||
const ConversationBotModeSelect({
|
||||
super.key,
|
||||
this.initialMode,
|
||||
required this.onChanged,
|
||||
this.enabled = true,
|
||||
this.validator,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final Map<String, String> options = {
|
||||
BotMode.discussion:
|
||||
L10n.of(context).conversationBotModeSelectOption_discussion,
|
||||
BotMode.custom: L10n.of(context).conversationBotModeSelectOption_custom,
|
||||
// BotMode.textAdventure:
|
||||
// L10n.of(context).conversationBotModeSelectOption_textAdventure,
|
||||
// BotMode.storyGame:
|
||||
// L10n.of(context).conversationBotModeSelectOption_storyGame,
|
||||
};
|
||||
|
||||
return DropdownButtonFormField2(
|
||||
customButton: initialMode != null && options.containsKey(initialMode)
|
||||
? CustomDropdownTextButton(
|
||||
text: options[initialMode]!,
|
||||
)
|
||||
: null,
|
||||
menuItemStyleData: const MenuItemStyleData(
|
||||
padding: EdgeInsets.zero, // Remove default padding
|
||||
),
|
||||
isExpanded: true,
|
||||
dropdownStyleData: DropdownStyleData(
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.surfaceContainerHigh,
|
||||
),
|
||||
),
|
||||
hint: Text(L10n.of(context).selectBotChatMode),
|
||||
items: [
|
||||
for (final entry in options.entries)
|
||||
DropdownMenuItem(
|
||||
value: entry.key,
|
||||
child: DropdownTextButton(
|
||||
text: entry.value,
|
||||
isSelected: initialMode == entry.key,
|
||||
),
|
||||
),
|
||||
],
|
||||
onChanged: enabled ? onChanged : null,
|
||||
validator: validator,
|
||||
value: initialMode,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:fluffychat/l10n/l10n.dart';
|
||||
|
||||
Future<void> showNoPermissionDialog(BuildContext context) {
|
||||
return showDialog<void>(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AlertDialog(
|
||||
title: Text(L10n.of(context).botConfigNoPermissionTitle),
|
||||
content: Text(L10n.of(context).botConfigNoPermissionMessage),
|
||||
actions: <Widget>[
|
||||
TextButton(
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
child: Text(L10n.of(context).ok),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
|
@ -1,306 +0,0 @@
|
|||
import 'dart:developer';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:matrix/matrix.dart';
|
||||
|
||||
import 'package:fluffychat/config/themes.dart';
|
||||
import 'package:fluffychat/l10n/l10n.dart';
|
||||
import 'package:fluffychat/pangea/bot/utils/bot_name.dart';
|
||||
import 'package:fluffychat/pangea/bot/widgets/bot_face_svg.dart';
|
||||
import 'package:fluffychat/pangea/chat_settings/constants/bot_mode.dart';
|
||||
import 'package:fluffychat/pangea/chat_settings/models/bot_options_model.dart';
|
||||
import 'package:fluffychat/pangea/chat_settings/widgets/conversation_bot/conversation_bot_no_permission_dialog.dart';
|
||||
import 'package:fluffychat/pangea/chat_settings/widgets/conversation_bot/conversation_bot_settings_form.dart';
|
||||
import 'package:fluffychat/pangea/common/utils/error_handler.dart';
|
||||
import 'package:fluffychat/pangea/common/widgets/full_width_dialog.dart';
|
||||
import 'package:fluffychat/pangea/events/constants/pangea_event_types.dart';
|
||||
import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart';
|
||||
import 'package:fluffychat/pangea/learning_settings/enums/language_level_type_enum.dart';
|
||||
import 'package:fluffychat/widgets/future_loading_dialog.dart';
|
||||
import 'package:fluffychat/widgets/matrix.dart';
|
||||
|
||||
class ConversationBotSettings extends StatefulWidget {
|
||||
final Room room;
|
||||
|
||||
const ConversationBotSettings({
|
||||
super.key,
|
||||
required this.room,
|
||||
});
|
||||
|
||||
@override
|
||||
ConversationBotSettingsState createState() => ConversationBotSettingsState();
|
||||
}
|
||||
|
||||
class ConversationBotSettingsState extends State<ConversationBotSettings> {
|
||||
Future<void> setBotOptions(BotOptionsModel botOptions) async {
|
||||
try {
|
||||
await Matrix.of(context).client.setRoomStateWithKey(
|
||||
widget.room.id,
|
||||
PangeaEventTypes.botOptions,
|
||||
'',
|
||||
botOptions.toJson(),
|
||||
);
|
||||
} catch (err, stack) {
|
||||
debugger(when: kDebugMode);
|
||||
ErrorHandler.logError(
|
||||
e: err,
|
||||
s: stack,
|
||||
data: {
|
||||
"botOptions": botOptions.toJson(),
|
||||
"roomID": widget.room.id,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AnimatedContainer(
|
||||
duration: const Duration(milliseconds: 300),
|
||||
width: double.infinity,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
ListTile(
|
||||
title: Text(
|
||||
L10n.of(context).botConfig,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.secondary,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
subtitle: Text(L10n.of(context).botSettingsSubtitle),
|
||||
leading: CircleAvatar(
|
||||
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
|
||||
foregroundColor: Theme.of(context).textTheme.bodyLarge!.color,
|
||||
child: const BotFace(
|
||||
width: 30.0,
|
||||
expression: BotExpression.idle,
|
||||
),
|
||||
),
|
||||
onTap: () => showDialog<BotOptionsModel?>(
|
||||
context: context,
|
||||
builder: (BuildContext context) => ConversationBotSettingsDialog(
|
||||
room: widget.room,
|
||||
onSubmit: setBotOptions,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ConversationBotSettingsDialog extends StatefulWidget {
|
||||
final Room room;
|
||||
final Function(BotOptionsModel) onSubmit;
|
||||
|
||||
const ConversationBotSettingsDialog({
|
||||
super.key,
|
||||
required this.room,
|
||||
required this.onSubmit,
|
||||
});
|
||||
|
||||
@override
|
||||
ConversationBotSettingsDialogState createState() =>
|
||||
ConversationBotSettingsDialogState();
|
||||
}
|
||||
|
||||
class ConversationBotSettingsDialogState
|
||||
extends State<ConversationBotSettingsDialog> {
|
||||
late BotOptionsModel botOptions;
|
||||
bool addBot = false;
|
||||
|
||||
final TextEditingController discussionTopicController =
|
||||
TextEditingController();
|
||||
final TextEditingController discussionKeywordsController =
|
||||
TextEditingController();
|
||||
final TextEditingController customSystemPromptController =
|
||||
TextEditingController();
|
||||
|
||||
bool hasUpdatedMode = false;
|
||||
bool hasPermission = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
botOptions = widget.room.botOptions != null
|
||||
? BotOptionsModel.fromJson(widget.room.botOptions?.toJson())
|
||||
: BotOptionsModel();
|
||||
|
||||
botOptions.targetLanguage ??=
|
||||
MatrixState.pangeaController.languageController.userL2?.langCode;
|
||||
|
||||
widget.room.botIsInRoom.then((bool isBotRoom) {
|
||||
setState(() => addBot = isBotRoom);
|
||||
});
|
||||
hasPermission =
|
||||
widget.room.powerForChangingStateEvent(PangeaEventTypes.botOptions) <=
|
||||
widget.room.ownPowerLevel;
|
||||
|
||||
discussionKeywordsController.text = botOptions.discussionKeywords ?? "";
|
||||
discussionTopicController.text = botOptions.discussionTopic ?? "";
|
||||
customSystemPromptController.text = botOptions.customSystemPrompt ?? "";
|
||||
|
||||
hasUpdatedMode = widget.room.botOptions != null;
|
||||
}
|
||||
|
||||
final GlobalKey<FormState> formKey = GlobalKey<FormState>();
|
||||
|
||||
void updateFromTextControllers() {
|
||||
botOptions.discussionTopic = discussionTopicController.text;
|
||||
botOptions.discussionKeywords = discussionKeywordsController.text;
|
||||
botOptions.customSystemPrompt = customSystemPromptController.text;
|
||||
}
|
||||
|
||||
void onUpdateChatMode(String? mode) {
|
||||
hasUpdatedMode = true;
|
||||
setState(() => botOptions.mode = mode ?? BotMode.discussion);
|
||||
}
|
||||
|
||||
void onUpdateBotLanguage(String? language) {
|
||||
setState(() => botOptions.targetLanguage = language);
|
||||
}
|
||||
|
||||
void onUpdateBotVoice(String? voice) {
|
||||
setState(() => botOptions.targetVoice = voice);
|
||||
}
|
||||
|
||||
void onUpdateBotLanguageLevel(LanguageLevelTypeEnum? level) {
|
||||
setState(() => botOptions.languageLevel = level!);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final dialogContent = Form(
|
||||
key: formKey,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
vertical: 12,
|
||||
),
|
||||
child: Text(
|
||||
L10n.of(context).botConfig,
|
||||
style: Theme.of(context).textTheme.titleLarge,
|
||||
),
|
||||
),
|
||||
IconButton(
|
||||
icon: const Icon(Icons.close),
|
||||
onPressed: () => Navigator.of(context).pop(null),
|
||||
),
|
||||
],
|
||||
),
|
||||
InkWell(
|
||||
onTap:
|
||||
hasPermission ? null : () => showNoPermissionDialog(context),
|
||||
child: SwitchListTile(
|
||||
title: Text(
|
||||
L10n.of(context).conversationBotStatus,
|
||||
),
|
||||
value: addBot,
|
||||
onChanged: hasPermission
|
||||
? (bool value) {
|
||||
setState(() => addBot = value);
|
||||
}
|
||||
: null, // Keeps the switch disabled
|
||||
contentPadding: const EdgeInsets.all(4),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: SingleChildScrollView(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
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,
|
||||
hasPermission: hasPermission,
|
||||
enabled: addBot,
|
||||
hasUpdatedMode: hasUpdatedMode,
|
||||
onUpdateBotMode: onUpdateChatMode,
|
||||
onUpdateBotLanguage: onUpdateBotLanguage,
|
||||
onUpdateBotVoice: onUpdateBotVoice,
|
||||
onUpdateBotLanguageLevel: onUpdateBotLanguageLevel,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
if (hasPermission)
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop(null);
|
||||
},
|
||||
child: Text(L10n.of(context).cancel),
|
||||
),
|
||||
const SizedBox(width: 20),
|
||||
TextButton(
|
||||
onPressed: () async {
|
||||
if (!hasPermission) {
|
||||
Navigator.of(context).pop(null);
|
||||
return;
|
||||
}
|
||||
final isValid = formKey.currentState!.validate();
|
||||
if (!isValid) return;
|
||||
|
||||
updateFromTextControllers();
|
||||
botOptions.targetLanguage ??= MatrixState
|
||||
.pangeaController.languageController.userL2?.langCode;
|
||||
|
||||
await showFutureLoadingDialog(
|
||||
context: context,
|
||||
future: () async => widget.onSubmit(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);
|
||||
}
|
||||
|
||||
Navigator.of(context).pop(botOptions);
|
||||
},
|
||||
child: hasPermission
|
||||
? Text(L10n.of(context).confirm)
|
||||
: Text(L10n.of(context).close),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
return FullWidthDialog(
|
||||
dialogContent: dialogContent,
|
||||
maxWidth: 450,
|
||||
maxHeight: 400,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,72 +0,0 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:fluffychat/l10n/l10n.dart';
|
||||
import 'package:fluffychat/pangea/chat_settings/models/bot_options_model.dart';
|
||||
import 'package:fluffychat/pangea/chat_settings/widgets/language_level_dropdown.dart';
|
||||
import 'package:fluffychat/pangea/learning_settings/enums/language_level_type_enum.dart';
|
||||
import 'package:fluffychat/pangea/learning_settings/utils/p_language_store.dart';
|
||||
import 'package:fluffychat/pangea/learning_settings/widgets/p_language_dropdown.dart';
|
||||
import 'package:fluffychat/widgets/matrix.dart';
|
||||
|
||||
class ConversationBotSettingsForm extends StatelessWidget {
|
||||
final BotOptionsModel botOptions;
|
||||
|
||||
final TextEditingController discussionTopicController;
|
||||
final TextEditingController discussionKeywordsController;
|
||||
final TextEditingController customSystemPromptController;
|
||||
|
||||
final bool enabled;
|
||||
final bool hasUpdatedMode;
|
||||
final bool hasPermission;
|
||||
|
||||
final void Function(String?) onUpdateBotMode;
|
||||
final void Function(String?) onUpdateBotLanguage;
|
||||
final void Function(String?) onUpdateBotVoice;
|
||||
final void Function(LanguageLevelTypeEnum?) onUpdateBotLanguageLevel;
|
||||
|
||||
const ConversationBotSettingsForm({
|
||||
super.key,
|
||||
required this.botOptions,
|
||||
required this.discussionTopicController,
|
||||
required this.discussionKeywordsController,
|
||||
required this.customSystemPromptController,
|
||||
required this.onUpdateBotMode,
|
||||
required this.onUpdateBotLanguage,
|
||||
required this.onUpdateBotVoice,
|
||||
required this.onUpdateBotLanguageLevel,
|
||||
required this.hasPermission,
|
||||
this.enabled = true,
|
||||
this.hasUpdatedMode = false,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
children: [
|
||||
PLanguageDropdown(
|
||||
decorationText: L10n.of(context).targetLanguage,
|
||||
languages: MatrixState.pangeaController.pLanguageStore.targetOptions,
|
||||
onChange: (lang) => hasPermission && enabled
|
||||
? onUpdateBotLanguage(lang.langCode)
|
||||
: null,
|
||||
initialLanguage: botOptions.targetLanguage != null
|
||||
? PLanguageStore.byLangCode(botOptions.targetLanguage!)
|
||||
: null,
|
||||
enabled: enabled && hasPermission,
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
LanguageLevelDropdown(
|
||||
initialLevel: botOptions.languageLevel,
|
||||
onChanged: hasPermission && enabled
|
||||
? (value) =>
|
||||
onUpdateBotLanguageLevel(value as LanguageLevelTypeEnum?)
|
||||
: null,
|
||||
validator: (value) => enabled && value == null
|
||||
? L10n.of(context).enterLanguageLevel
|
||||
: null,
|
||||
enabled: enabled && hasPermission,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,92 +0,0 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:fluffychat/l10n/l10n.dart';
|
||||
import 'package:fluffychat/pangea/chat_settings/models/bot_options_model.dart';
|
||||
|
||||
class ConversationBotGameMasterInstructionsInput extends StatelessWidget {
|
||||
final BotOptionsModel initialBotOptions;
|
||||
// call this to update propagate changes to parents
|
||||
final void Function(BotOptionsModel) onChanged;
|
||||
|
||||
const ConversationBotGameMasterInstructionsInput({
|
||||
super.key,
|
||||
required this.initialBotOptions,
|
||||
required this.onChanged,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
String gameMasterInstructions =
|
||||
initialBotOptions.textAdventureGameMasterInstructions ?? "";
|
||||
|
||||
final TextEditingController textFieldController =
|
||||
TextEditingController(text: gameMasterInstructions);
|
||||
|
||||
final GlobalKey<FormState> gameMasterInstructionsFormKey =
|
||||
GlobalKey<FormState>();
|
||||
|
||||
void setBotTextAdventureGameMasterInstructionsAction() async {
|
||||
showDialog(
|
||||
context: context,
|
||||
useRootNavigator: false,
|
||||
builder: (BuildContext context) => AlertDialog(
|
||||
title: Text(
|
||||
L10n.of(context)
|
||||
.conversationBotTextAdventureZone_instructionPlaceholder,
|
||||
),
|
||||
content: Form(
|
||||
key: gameMasterInstructionsFormKey,
|
||||
child: TextFormField(
|
||||
minLines: 1,
|
||||
maxLines: 10,
|
||||
maxLength: 1000,
|
||||
controller: textFieldController,
|
||||
onChanged: (value) {
|
||||
if (value.isNotEmpty) {
|
||||
gameMasterInstructions = value;
|
||||
}
|
||||
},
|
||||
validator: (value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
return 'This field cannot be empty';
|
||||
}
|
||||
return null;
|
||||
},
|
||||
),
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
child: Text(L10n.of(context).cancel),
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
),
|
||||
TextButton(
|
||||
child: Text(L10n.of(context).ok),
|
||||
onPressed: () {
|
||||
if (gameMasterInstructionsFormKey.currentState!.validate()) {
|
||||
if (gameMasterInstructions !=
|
||||
initialBotOptions.textAdventureGameMasterInstructions) {
|
||||
initialBotOptions.textAdventureGameMasterInstructions =
|
||||
gameMasterInstructions;
|
||||
onChanged.call(initialBotOptions);
|
||||
}
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return ListTile(
|
||||
onTap: setBotTextAdventureGameMasterInstructionsAction,
|
||||
title: Text(
|
||||
initialBotOptions.textAdventureGameMasterInstructions ??
|
||||
L10n.of(context)
|
||||
.conversationBotTextAdventureZone_instructionPlaceholder,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:fluffychat/pangea/chat_settings/models/bot_options_model.dart';
|
||||
import 'package:fluffychat/pangea/chat_settings/widgets/conversation_bot/conversation_bot_text_adventure_game_master_instruction_input.dart';
|
||||
|
||||
// TODO check how this looks
|
||||
class ConversationBotTextAdventureZone extends StatelessWidget {
|
||||
final BotOptionsModel initialBotOptions;
|
||||
// call this to update propagate changes to parents
|
||||
|
||||
final void Function(BotOptionsModel) onChanged;
|
||||
const ConversationBotTextAdventureZone({
|
||||
super.key,
|
||||
required this.initialBotOptions,
|
||||
required this.onChanged,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8),
|
||||
child: ConversationBotGameMasterInstructionsInput(
|
||||
initialBotOptions: initialBotOptions,
|
||||
onChanged: onChanged,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -11,6 +11,9 @@ import 'package:sentry_flutter/sentry_flutter.dart';
|
|||
import 'package:fluffychat/config/app_config.dart';
|
||||
import 'package:fluffychat/pangea/analytics_misc/get_analytics_controller.dart';
|
||||
import 'package:fluffychat/pangea/analytics_misc/put_analytics_controller.dart';
|
||||
import 'package:fluffychat/pangea/bot/utils/bot_room_extension.dart';
|
||||
import 'package:fluffychat/pangea/chat_settings/models/bot_options_model.dart';
|
||||
import 'package:fluffychat/pangea/chat_settings/utils/bot_client_extension.dart';
|
||||
import 'package:fluffychat/pangea/choreographer/controllers/contextual_definition_controller.dart';
|
||||
import 'package:fluffychat/pangea/events/constants/pangea_event_types.dart';
|
||||
import 'package:fluffychat/pangea/events/controllers/message_data_controller.dart';
|
||||
|
|
@ -48,7 +51,8 @@ class PangeaController {
|
|||
///store Services
|
||||
final pLanguageStore = PLanguageStore();
|
||||
|
||||
StreamSubscription? _languageStream;
|
||||
StreamSubscription? _languageSubscription;
|
||||
StreamSubscription? _settingsSubscription;
|
||||
|
||||
///Matrix Variables
|
||||
MatrixState matrixState;
|
||||
|
|
@ -57,7 +61,7 @@ class PangeaController {
|
|||
int? randomint;
|
||||
PangeaController({required this.matrix, required this.matrixState}) {
|
||||
_setup();
|
||||
_setLanguageSubscription();
|
||||
_setSettingsSubscriptions();
|
||||
randomint = Random().nextInt(2000);
|
||||
}
|
||||
|
||||
|
|
@ -175,14 +179,16 @@ class PangeaController {
|
|||
// Reset cached analytics data
|
||||
_disposeAnalyticsControllers();
|
||||
userController.clear();
|
||||
_languageStream?.cancel();
|
||||
_languageStream = null;
|
||||
_languageSubscription?.cancel();
|
||||
_settingsSubscription?.cancel();
|
||||
_languageSubscription = null;
|
||||
_settingsSubscription = null;
|
||||
_logOutfromPangea(context);
|
||||
break;
|
||||
case LoginState.loggedIn:
|
||||
// Initialize analytics data
|
||||
initControllers();
|
||||
_setLanguageSubscription();
|
||||
_setSettingsSubscriptions();
|
||||
|
||||
userController.reinitialize().then((_) {
|
||||
final l1 = userController.profile.userSettings.sourceLanguage;
|
||||
|
|
@ -218,11 +224,39 @@ class PangeaController {
|
|||
await _initAnalyticsControllers();
|
||||
}
|
||||
|
||||
void _setLanguageSubscription() {
|
||||
_languageStream?.cancel();
|
||||
_languageStream = userController.languageStream.stream.listen(
|
||||
(_) => clearCache(exclude: ["analytics_storage"]),
|
||||
);
|
||||
void _setSettingsSubscriptions() {
|
||||
_languageSubscription?.cancel();
|
||||
_languageSubscription =
|
||||
userController.languageStream.stream.listen(_onLanguageUpdate);
|
||||
_settingsSubscription?.cancel();
|
||||
_settingsSubscription = userController.settingsUpdateStream.stream
|
||||
.listen((_) => _updateBotOptions());
|
||||
}
|
||||
|
||||
Future<void> _onLanguageUpdate(LanguageUpdate update) async {
|
||||
clearCache(exclude: ["analytics_storage"]);
|
||||
_updateBotOptions();
|
||||
}
|
||||
|
||||
Future<void> _updateBotOptions() async {
|
||||
if (!matrixState.client.isLogged()) return;
|
||||
final botDM = matrixState.client.botDM;
|
||||
if (botDM == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final targetLanguage = languageController.userL2?.langCode;
|
||||
final cefrLevel = userController.profile.userSettings.cefrLevel;
|
||||
final updateBotOptions = botDM.botOptions ?? BotOptionsModel();
|
||||
|
||||
if (updateBotOptions.targetLanguage == targetLanguage &&
|
||||
updateBotOptions.languageLevel == cefrLevel) {
|
||||
return;
|
||||
}
|
||||
|
||||
updateBotOptions.targetLanguage = targetLanguage;
|
||||
updateBotOptions.languageLevel = cefrLevel;
|
||||
await botDM.setBotOptions(updateBotOptions);
|
||||
}
|
||||
|
||||
Future<void> setPangeaPushRules() async {
|
||||
|
|
|
|||
|
|
@ -18,10 +18,10 @@ import 'package:fluffychat/pangea/activity_sessions/activity_room_extension.dart
|
|||
import 'package:fluffychat/pangea/analytics_misc/constructs_event.dart';
|
||||
import 'package:fluffychat/pangea/analytics_misc/constructs_model.dart';
|
||||
import 'package:fluffychat/pangea/bot/utils/bot_name.dart';
|
||||
import 'package:fluffychat/pangea/bot/utils/bot_room_extension.dart';
|
||||
import 'package:fluffychat/pangea/chat/constants/default_power_level.dart';
|
||||
import 'package:fluffychat/pangea/chat_settings/constants/bot_mode.dart';
|
||||
import 'package:fluffychat/pangea/chat_settings/constants/pangea_room_types.dart';
|
||||
import 'package:fluffychat/pangea/chat_settings/models/bot_options_model.dart';
|
||||
import 'package:fluffychat/pangea/common/constants/model_keys.dart';
|
||||
import 'package:fluffychat/pangea/common/utils/error_handler.dart';
|
||||
import 'package:fluffychat/pangea/constructs/construct_identifier.dart';
|
||||
|
|
@ -38,10 +38,10 @@ import '../events/constants/pangea_event_types.dart';
|
|||
import '../events/models/representation_content_model.dart';
|
||||
|
||||
part "../analytics_misc/room_analytics_extension.dart";
|
||||
part "room_capacity_extension.dart";
|
||||
part "room_children_and_parents_extension.dart";
|
||||
part "room_events_extension.dart";
|
||||
part "room_information_extension.dart";
|
||||
part "room_settings_extension.dart";
|
||||
part "room_space_settings_extension.dart";
|
||||
part "room_user_permissions_extension.dart";
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
part of "pangea_room_extension.dart";
|
||||
|
||||
extension RoomSettingsRoomExtension on Room {
|
||||
extension RoomCapacityExtension on Room {
|
||||
Future<void> updateRoomCapacity(int newCapacity) =>
|
||||
client.setRoomStateWithKey(
|
||||
id,
|
||||
|
|
@ -13,11 +13,4 @@ extension RoomSettingsRoomExtension on Room {
|
|||
final t = getState(PangeaEventTypes.capacity)?.content['capacity'];
|
||||
return t is int ? t : null;
|
||||
}
|
||||
|
||||
BotOptionsModel? get botOptions {
|
||||
if (isSpace) return null;
|
||||
final stateEvent = getState(PangeaEventTypes.botOptions);
|
||||
if (stateEvent == null) return null;
|
||||
return BotOptionsModel.fromJson(stateEvent.content);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue