Merge branch 'main' of https://github.com/pangeachat/client into toolbar-selection
This commit is contained in:
commit
ed57c340d1
45 changed files with 646 additions and 382 deletions
|
|
@ -3879,7 +3879,7 @@
|
|||
"define": "Define",
|
||||
"listen": "Listen",
|
||||
"addConversationBot": "Enable Conversation Bot",
|
||||
"addConversationBotDesc": "Add a bot to this group chat that will ask questions on a specific topic",
|
||||
"addConversationBotDesc": "Add a bot to this group chat",
|
||||
"convoBotSettingsTitle": "Conversation Bot Settings",
|
||||
"convoBotSettingsDescription": "Edit conversation topic and difficulty",
|
||||
"enterAConversationTopic": "Enter a conversation topic",
|
||||
|
|
@ -4003,13 +4003,21 @@
|
|||
"conversationBotCustomZone_title": "Custom Settings",
|
||||
"conversationBotCustomZone_customSystemPromptLabel": "System prompt",
|
||||
"conversationBotCustomZone_customSystemPromptPlaceholder": "Set custom system prompt",
|
||||
"conversationBotCustomZone_customSystemPromptEmptyError": "Missing custom system prompt",
|
||||
"conversationBotCustomZone_customTriggerReactionEnabledLabel": "Responds on ⏩ reaction",
|
||||
"botConfig": "Conversation Bot Settings",
|
||||
"addConversationBotDialogTitleInvite": "Confirm inviting conversation bot",
|
||||
"addConversationBotButtonInvite": "Invite",
|
||||
"addConversationBotDialogInviteConfirmation": "Invite",
|
||||
"addConversationBotButtonTitleRemove": "Confirm removing conversation bot",
|
||||
"addConversationBotButtonRemove": "Remove",
|
||||
"addConversationBotDialogRemoveConfirmation": "Remove",
|
||||
"conversationBotConfigConfirmChange": "Confirm",
|
||||
"conversationBotStatus": "Bot Status",
|
||||
"conversationBotTextAdventureZone_title": "Text Adventure",
|
||||
"conversationBotTextAdventureZone_instructionLabel": "Game Master Instructions",
|
||||
"conversationBotTextAdventureZone_instructionPlaceholder": "Set game master instructions",
|
||||
"conversationBotCustomZone_instructionSystemPromptEmptyError": "Missing game master instructions",
|
||||
"studentAnalyticsNotAvailable": "Student data not currently available",
|
||||
"roomDataMissing": "Some data may be missing from rooms in which you are not a member.",
|
||||
"updatePhoneOS": "You may need to update your device's OS version.",
|
||||
|
|
|
|||
|
|
@ -331,7 +331,6 @@ class ChatController extends State<ChatPageWithRoom>
|
|||
);
|
||||
}
|
||||
await Matrix.of(context).client.roomsLoading;
|
||||
choreographer.setRoomId(roomId);
|
||||
});
|
||||
// Pangea#
|
||||
_tryLoadTimeline();
|
||||
|
|
|
|||
|
|
@ -463,7 +463,7 @@ class InputBar extends StatelessWidget {
|
|||
debounceDuration: const Duration(milliseconds: 50),
|
||||
// show suggestions after 50ms idle time (default is 300)
|
||||
// #Pangea
|
||||
key: controller!.choreographer.inputLayerLinkAndKey.key,
|
||||
key: controller?.choreographer.inputLayerLinkAndKey.key,
|
||||
// builder: (context, controller, focusNode) => TextField(
|
||||
builder: (context, _, focusNode) => TextField(
|
||||
// Pangea#
|
||||
|
|
@ -504,11 +504,11 @@ class InputBar extends StatelessWidget {
|
|||
onSubmitted!(text);
|
||||
},
|
||||
// #Pangea
|
||||
style: controller?.isMaxLength ?? false
|
||||
style: controller?.exceededMaxLength ?? false
|
||||
? const TextStyle(color: Colors.red)
|
||||
: null,
|
||||
onTap: () {
|
||||
controller!.onInputTap(
|
||||
controller?.onInputTap(
|
||||
context,
|
||||
fNode: focusNode,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ import 'package:fluffychat/pages/settings/settings.dart';
|
|||
import 'package:fluffychat/pangea/pages/class_settings/p_class_widgets/class_description_button.dart';
|
||||
import 'package:fluffychat/pangea/utils/set_class_name.dart';
|
||||
import 'package:fluffychat/pangea/widgets/class/add_space_toggles.dart';
|
||||
import 'package:fluffychat/pangea/widgets/conversation_bot/conversation_bot_settings.dart';
|
||||
import 'package:fluffychat/utils/platform_infos.dart';
|
||||
import 'package:fluffychat/widgets/app_lock.dart';
|
||||
import 'package:fluffychat/widgets/matrix.dart';
|
||||
|
|
@ -43,8 +42,9 @@ class ChatDetailsController extends State<ChatDetails> {
|
|||
|
||||
// #Pangea
|
||||
final GlobalKey<AddToSpaceState> addToSpaceKey = GlobalKey<AddToSpaceState>();
|
||||
final GlobalKey<ConversationBotSettingsState> addConversationBotKey =
|
||||
GlobalKey<ConversationBotSettingsState>();
|
||||
final GlobalKey<ChatDetailsController>
|
||||
addConversationBotKey =
|
||||
GlobalKey<ChatDetailsController>();
|
||||
|
||||
bool displayAddStudentOptions = false;
|
||||
void toggleAddStudentOptions() =>
|
||||
|
|
|
|||
|
|
@ -105,6 +105,34 @@ class NewGroupController extends State<NewGroup> {
|
|||
|
||||
if (!mounted) return;
|
||||
|
||||
// #Pangea
|
||||
// validate init bot options
|
||||
final addBot = addConversationBotKey.currentState?.addBot ?? false;
|
||||
if (addBot) {
|
||||
final botOptions = addConversationBotKey.currentState!.botOptions;
|
||||
if (botOptions.mode == "custom") {
|
||||
if (botOptions.customSystemPrompt == null ||
|
||||
botOptions.customSystemPrompt!.isEmpty) {
|
||||
setState(() {
|
||||
error = L10n.of(context)!
|
||||
.conversationBotCustomZone_customSystemPromptEmptyError;
|
||||
loading = false;
|
||||
});
|
||||
return;
|
||||
}
|
||||
} else if (botOptions.mode == "text_adventure") {
|
||||
if (botOptions.textAdventureGameMasterInstructions == null ||
|
||||
botOptions.textAdventureGameMasterInstructions!.isEmpty) {
|
||||
setState(() {
|
||||
error = L10n.of(context)!
|
||||
.conversationBotCustomZone_instructionSystemPromptEmptyError;
|
||||
loading = false;
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final roomId = await client.createGroupChat(
|
||||
// #Pangea
|
||||
// visibility:
|
||||
|
|
|
|||
|
|
@ -91,7 +91,7 @@ class AlternativeTranslator {
|
|||
|
||||
final FullTextTranslationResponseModel results =
|
||||
await FullTextTranslationRepo.translate(
|
||||
accessToken: await choreographer.accessToken,
|
||||
accessToken: choreographer.accessToken,
|
||||
request: FullTextTranslationRequestModel(
|
||||
text: choreographer.itController.sourceText!,
|
||||
tgtLang: choreographer.l2LangCode!,
|
||||
|
|
@ -117,7 +117,7 @@ class AlternativeTranslator {
|
|||
}
|
||||
|
||||
similarityResponse = await SimilarityRepo.get(
|
||||
accessToken: await choreographer.accessToken,
|
||||
accessToken: choreographer.accessToken,
|
||||
request: SimilarityRequestModel(
|
||||
benchmark: results.bestTranslation,
|
||||
toCompare: [userTranslation!],
|
||||
|
|
|
|||
|
|
@ -42,7 +42,6 @@ class Choreographer {
|
|||
|
||||
bool isFetching = false;
|
||||
Timer? debounceTimer;
|
||||
String? _roomId;
|
||||
ChoreoRecord choreoRecord = ChoreoRecord.newRecord;
|
||||
// last checked by IGC or translation
|
||||
String? _lastChecked;
|
||||
|
|
@ -403,7 +402,7 @@ class Choreographer {
|
|||
|
||||
PangeaTextController get textController => _textController;
|
||||
|
||||
Future<String> get accessToken => pangeaController.userController.accessToken;
|
||||
String get accessToken => pangeaController.userController.accessToken;
|
||||
|
||||
clear() {
|
||||
choreoMode = ChoreoMode.igc;
|
||||
|
|
@ -464,10 +463,7 @@ class Choreographer {
|
|||
setState();
|
||||
}
|
||||
|
||||
get roomId => _roomId;
|
||||
void setRoomId(String? roomId) {
|
||||
_roomId = roomId ?? '';
|
||||
}
|
||||
get roomId => chatController.roomId;
|
||||
|
||||
bool get _useCustomInput => [
|
||||
EditType.keyboard,
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ class IgcController {
|
|||
);
|
||||
|
||||
final IGCTextData igcTextDataResponse = await IgcRepo.getIGC(
|
||||
await choreographer.accessToken,
|
||||
choreographer.accessToken,
|
||||
igcRequest: reqBody,
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ class SpanDataController {
|
|||
response = _cache[cacheKey]!.data;
|
||||
} else {
|
||||
response = SpanDataRepo.getSpanDetails(
|
||||
await choreographer.accessToken,
|
||||
choreographer.accessToken,
|
||||
request: SpanDetailsRepoReqAndRes(
|
||||
userL1: choreographer.l1LangCode!,
|
||||
userL2: choreographer.l2LangCode!,
|
||||
|
|
|
|||
|
|
@ -73,7 +73,7 @@ class ITFeedbackCardController extends State<ITFeedbackCard> {
|
|||
isTranslating = true;
|
||||
});
|
||||
FullTextTranslationRepo.translate(
|
||||
accessToken: await controller.userController.accessToken,
|
||||
accessToken: controller.userController.accessToken,
|
||||
request: FullTextTranslationRequestModel(
|
||||
text: res!.text,
|
||||
tgtLang: controller.languageController.userL1?.langCode ??
|
||||
|
|
|
|||
|
|
@ -114,6 +114,9 @@ class ModelKey {
|
|||
"custom_trigger_reaction_enabled";
|
||||
static const String customTriggerReactionKey = "custom_trigger_reaction_key";
|
||||
|
||||
static const String textAdventureGameMasterInstructions =
|
||||
"text_adventure_game_master_instructions";
|
||||
|
||||
static const String prevEventId = "prev_event_id";
|
||||
static const String prevLastUpdated = "prev_last_updated";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ class ContextualDefinitionController {
|
|||
try {
|
||||
final ContextualDefinitionResponseModel res =
|
||||
await _ContextualDefinitionRepo.define(
|
||||
await _pangeaController.userController.accessToken,
|
||||
_pangeaController.userController.accessToken,
|
||||
request,
|
||||
);
|
||||
return res;
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ class ITFeedbackController {
|
|||
) async {
|
||||
try {
|
||||
final ITFeedbackResponseModel res = await _ITFeedbackRepo.get(
|
||||
await _pangeaController.userController.accessToken,
|
||||
_pangeaController.userController.accessToken,
|
||||
request,
|
||||
);
|
||||
return res;
|
||||
|
|
|
|||
|
|
@ -145,7 +145,7 @@ class LanguageDetectionController {
|
|||
return _cache[params]!.data;
|
||||
} else {
|
||||
final Future<LanguageDetectionResponse> response = _fetchResponse(
|
||||
await _pangeaController.userController.accessToken,
|
||||
_pangeaController.userController.accessToken,
|
||||
params,
|
||||
);
|
||||
_cache[params] = _LanguageDetectionCacheItem(data: response);
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ class MessageDataController extends BaseController {
|
|||
Future<PangeaMessageTokens?> _getTokens(
|
||||
TokensRequestModel req,
|
||||
) async {
|
||||
final accessToken = await _pangeaController.userController.accessToken;
|
||||
final accessToken = _pangeaController.userController.accessToken;
|
||||
|
||||
final TokensResponseModel igcTextData =
|
||||
await TokensRepo.tokenize(accessToken, req);
|
||||
|
|
@ -195,7 +195,7 @@ class MessageDataController extends BaseController {
|
|||
try {
|
||||
final FullTextTranslationResponseModel res =
|
||||
await FullTextTranslationRepo.translate(
|
||||
accessToken: await _pangeaController.userController.accessToken,
|
||||
accessToken: _pangeaController.userController.accessToken,
|
||||
request: req,
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -277,7 +277,11 @@ class MyAnalyticsController {
|
|||
// get the timelines for each chat
|
||||
final List<Future<Timeline>> timelineFutures = [];
|
||||
for (final chat in chats) {
|
||||
timelineFutures.add(chat.getTimeline());
|
||||
timelineFutures.add(
|
||||
chat.timeline == null
|
||||
? chat.getTimeline()
|
||||
: Future.value(chat.timeline),
|
||||
);
|
||||
}
|
||||
final List<Timeline> timelines = await Future.wait(timelineFutures);
|
||||
final Map<String, Timeline> timelineMap =
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ class SpeechToTextController {
|
|||
return _cache[cacheKey]!.data;
|
||||
} else {
|
||||
final Future<SpeechToTextModel> response = _fetchResponse(
|
||||
accessToken: await _pangeaController.userController.accessToken,
|
||||
accessToken: _pangeaController.userController.accessToken,
|
||||
requestModel: requestModel,
|
||||
);
|
||||
_cache[cacheKey] = _SpeechToTextCacheItem(data: response);
|
||||
|
|
|
|||
|
|
@ -100,7 +100,7 @@ class TextToSpeechController {
|
|||
return _cache[params]!.data;
|
||||
} else {
|
||||
final Future<TextToSpeechResponse> response = _fetchResponse(
|
||||
await _pangeaController.userController.accessToken,
|
||||
_pangeaController.userController.accessToken,
|
||||
params,
|
||||
);
|
||||
_cache[params] = _TextToSpeechCacheItem(data: response);
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ import 'package:fluffychat/pangea/utils/error_handler.dart';
|
|||
import 'package:jwt_decode/jwt_decode.dart';
|
||||
import 'package:matrix/matrix.dart' as matrix;
|
||||
|
||||
import '../constants/local.key.dart';
|
||||
import '../models/user_model.dart';
|
||||
import '../repo/user_repo.dart';
|
||||
|
||||
|
|
@ -83,15 +82,6 @@ class UserController extends BaseController {
|
|||
createdAt: DateTime.now(),
|
||||
);
|
||||
final newProfile = Profile(userSettings: userSettings);
|
||||
|
||||
// we don't use the pangea profile anymore, but we still need
|
||||
// it to get access token for the choreographer, so create one
|
||||
await PUserRepo.repoCreatePangeaUser(
|
||||
userID: userId!,
|
||||
dob: dob.toIso8601String(),
|
||||
fullName: fullname!,
|
||||
matrixAccessToken: _matrixAccessToken!,
|
||||
);
|
||||
await newProfile.saveProfileData(waitForDataInSync: true);
|
||||
}
|
||||
|
||||
|
|
@ -157,41 +147,13 @@ class UserController extends BaseController {
|
|||
/// Returns a boolean value indicating whether a new JWT (JSON Web Token) is needed.
|
||||
bool needNewJWT(String token) => Jwt.isExpired(token);
|
||||
|
||||
/// Retrieves the access token for the user. Looks for it locally,
|
||||
/// and if it's not found or expired, fetches it from the server.
|
||||
Future<String> get accessToken async {
|
||||
final localAccessToken =
|
||||
_pangeaController.pStoreService.read(PLocalKey.access);
|
||||
|
||||
if (localAccessToken == null || needNewJWT(localAccessToken)) {
|
||||
PangeaProfileResponse? userModel = await PUserRepo.fetchPangeaUserInfo(
|
||||
userID: userId!,
|
||||
matrixAccessToken: _matrixAccessToken!,
|
||||
);
|
||||
// Oops, some accounts were made without creating pangea profiles, so they
|
||||
// don't have access to an access token yet. In that case, create a pangea profile.
|
||||
if (userModel?.access == null) {
|
||||
final dob = profile.userSettings.dateOfBirth;
|
||||
if (dob != null) {
|
||||
userModel = await PUserRepo.repoCreatePangeaUser(
|
||||
userID: userId!,
|
||||
dob: dob.toIso8601String(),
|
||||
fullName: fullname!,
|
||||
matrixAccessToken: _matrixAccessToken!,
|
||||
);
|
||||
if (userModel?.access == null) {
|
||||
throw ("Trying to get accessToken with null userModel");
|
||||
}
|
||||
}
|
||||
}
|
||||
_pangeaController.pStoreService.save(
|
||||
PLocalKey.access,
|
||||
userModel!.access,
|
||||
);
|
||||
return userModel.access;
|
||||
/// Retrieves matrix access token.
|
||||
String get accessToken {
|
||||
final token = _pangeaController.matrixState.client.accessToken;
|
||||
if (token == null) {
|
||||
throw ("Trying to get accessToken with null token. User is not logged in.");
|
||||
}
|
||||
|
||||
return localAccessToken;
|
||||
return token;
|
||||
}
|
||||
|
||||
/// Returns the full name of the user.
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ class WordController extends BaseController {
|
|||
if (local != null) return local;
|
||||
|
||||
final WordData remote = await WordRepo.getWordNetData(
|
||||
accessToken: await _pangeaController.userController.accessToken,
|
||||
accessToken: _pangeaController.userController.accessToken,
|
||||
fullText: fullText,
|
||||
word: word,
|
||||
userL1: userL1,
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ part of "client_extension.dart";
|
|||
extension GeneralInfoClientExtension on Client {
|
||||
Future<List<String>> get _teacherRoomIds async {
|
||||
final List<String> adminRoomIds = [];
|
||||
for (final Room adminSpace in (await _spacesImTeaching)) {
|
||||
for (final Room adminSpace in (_spacesImTeaching)) {
|
||||
adminRoomIds.add(adminSpace.id);
|
||||
final List<String> adminSpaceRooms = adminSpace.allSpaceChildRoomIds;
|
||||
adminRoomIds.addAll(adminSpaceRooms);
|
||||
|
|
@ -59,7 +59,7 @@ extension GeneralInfoClientExtension on Client {
|
|||
final Event? originalEvent = await room!.getEventById(edittedEventId);
|
||||
if (originalEvent == null) return [];
|
||||
|
||||
final Timeline timeline = await room.getTimeline();
|
||||
final Timeline timeline = room.timeline ?? await room.getTimeline();
|
||||
final List<Event> editEvents = originalEvent
|
||||
.aggregatedEvents(
|
||||
timeline,
|
||||
|
|
|
|||
|
|
@ -320,7 +320,7 @@ extension EventsRoomExtension on Room {
|
|||
"In messageListForChat with room that is not a chat",
|
||||
);
|
||||
}
|
||||
final Timeline timeline = await getTimeline();
|
||||
final Timeline timeline = this.timeline ?? await getTimeline();
|
||||
|
||||
while (timeline.canRequestHistory && numberOfSearches < 50) {
|
||||
await timeline.requestHistory(historyCount: 100);
|
||||
|
|
@ -433,7 +433,7 @@ extension EventsRoomExtension on Room {
|
|||
}) async {
|
||||
try {
|
||||
int numberOfSearches = 0;
|
||||
final Timeline timeline = await getTimeline();
|
||||
final Timeline timeline = this.timeline ?? await getTimeline();
|
||||
|
||||
List<Event> relevantEvents() => timeline.events
|
||||
.where((event) => event.senderId == sender && event.type == type)
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ class BotOptionsModel {
|
|||
String? customSystemPrompt;
|
||||
bool? customTriggerReactionEnabled;
|
||||
String? customTriggerReactionKey;
|
||||
String? textAdventureGameMasterInstructions;
|
||||
|
||||
BotOptionsModel({
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
|
@ -45,6 +46,11 @@ class BotOptionsModel {
|
|||
this.customSystemPrompt,
|
||||
this.customTriggerReactionEnabled = true,
|
||||
this.customTriggerReactionKey = "⏩",
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Text Adventure Mode Options
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
this.textAdventureGameMasterInstructions,
|
||||
});
|
||||
|
||||
factory BotOptionsModel.fromJson(json) {
|
||||
|
|
@ -73,6 +79,12 @@ class BotOptionsModel {
|
|||
customTriggerReactionEnabled:
|
||||
json[ModelKey.customTriggerReactionEnabled] ?? true,
|
||||
customTriggerReactionKey: json[ModelKey.customTriggerReactionKey] ?? "⏩",
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Text Adventure Mode Options
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
textAdventureGameMasterInstructions:
|
||||
json[ModelKey.textAdventureGameMasterInstructions],
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -93,6 +105,8 @@ class BotOptionsModel {
|
|||
data[ModelKey.customTriggerReactionEnabled] =
|
||||
customTriggerReactionEnabled ?? true;
|
||||
data[ModelKey.customTriggerReactionKey] = customTriggerReactionKey ?? "⏩";
|
||||
data[ModelKey.textAdventureGameMasterInstructions] =
|
||||
textAdventureGameMasterInstructions;
|
||||
return data;
|
||||
} catch (e, s) {
|
||||
debugger(when: kDebugMode);
|
||||
|
|
@ -134,6 +148,9 @@ class BotOptionsModel {
|
|||
case ModelKey.customTriggerReactionKey:
|
||||
customTriggerReactionKey = value;
|
||||
break;
|
||||
case ModelKey.textAdventureGameMasterInstructions:
|
||||
textAdventureGameMasterInstructions = value;
|
||||
break;
|
||||
default:
|
||||
throw Exception('Invalid key for bot options - $key');
|
||||
}
|
||||
|
|
|
|||
|
|
@ -166,7 +166,7 @@ class ConstructListViewState extends State<ConstructListView> {
|
|||
if (_timelinesCache.containsKey(use.chatId)) {
|
||||
timeline = _timelinesCache[use.chatId];
|
||||
} else {
|
||||
timeline = await msgRoom.getTimeline();
|
||||
timeline = msgRoom.timeline ?? await msgRoom.getTimeline();
|
||||
_timelinesCache[use.chatId] = timeline;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -107,8 +107,7 @@ class FindPartnerController extends State<FindPartner> {
|
|||
|
||||
UserProfileSearchResponse response;
|
||||
try {
|
||||
final String accessToken =
|
||||
await pangeaController.userController.accessToken;
|
||||
final String accessToken = pangeaController.userController.accessToken;
|
||||
response = await PUserRepo.searchUserProfiles(
|
||||
accessToken: accessToken,
|
||||
targetLanguage: targetLanguageSearch.langCode,
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
import 'package:fluffychat/config/app_config.dart';
|
||||
import 'package:fluffychat/pangea/models/space_model.dart';
|
||||
import 'package:fluffychat/pangea/pages/settings_learning/settings_learning.dart';
|
||||
import 'package:fluffychat/pangea/widgets/user_settings/country_picker_tile.dart';
|
||||
|
|
@ -32,15 +31,15 @@ class SettingsLearningView extends StatelessWidget {
|
|||
const SizedBox(height: 8),
|
||||
const Divider(height: 1),
|
||||
const SizedBox(height: 8),
|
||||
if (controller.pangeaController.permissionsController.isUser18())
|
||||
SwitchListTile.adaptive(
|
||||
activeColor: AppConfig.activeToggleColor,
|
||||
title: Text(L10n.of(context)!.publicProfileTitle),
|
||||
subtitle: Text(L10n.of(context)!.publicProfileDesc),
|
||||
value: controller.pangeaController.userController.isPublic,
|
||||
onChanged: (bool isPublicProfile) =>
|
||||
controller.setPublicProfile(isPublicProfile),
|
||||
),
|
||||
// if (controller.pangeaController.permissionsController.isUser18())
|
||||
// SwitchListTile.adaptive(
|
||||
// activeColor: AppConfig.activeToggleColor,
|
||||
// title: Text(L10n.of(context)!.publicProfileTitle),
|
||||
// subtitle: Text(L10n.of(context)!.publicProfileDesc),
|
||||
// value: controller.pangeaController.userController.isPublic,
|
||||
// onChanged: (bool isPublicProfile) =>
|
||||
// controller.setPublicProfile(isPublicProfile),
|
||||
// ),
|
||||
ListTile(
|
||||
subtitle: Text(L10n.of(context)!.toggleToolSettingsDescription),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ import 'dart:convert';
|
|||
import 'dart:developer';
|
||||
|
||||
import 'package:fluffychat/pangea/constants/model_keys.dart';
|
||||
import 'package:fluffychat/pangea/utils/error_handler.dart';
|
||||
import 'package:http/http.dart';
|
||||
|
||||
import '../models/user_model.dart';
|
||||
|
|
@ -11,33 +10,33 @@ import '../network/requests.dart';
|
|||
import '../network/urls.dart';
|
||||
|
||||
class PUserRepo {
|
||||
static Future<PangeaProfileResponse?> repoCreatePangeaUser({
|
||||
required String userID,
|
||||
required String dob,
|
||||
required fullName,
|
||||
required String matrixAccessToken,
|
||||
}) async {
|
||||
try {
|
||||
final Requests req = Requests(
|
||||
baseUrl: PApiUrls.baseAPI,
|
||||
matrixAccessToken: matrixAccessToken,
|
||||
);
|
||||
// static Future<PangeaProfileResponse?> repoCreatePangeaUser({
|
||||
// required String userID,
|
||||
// required String dob,
|
||||
// required fullName,
|
||||
// required String matrixAccessToken,
|
||||
// }) async {
|
||||
// try {
|
||||
// final Requests req = Requests(
|
||||
// baseUrl: PApiUrls.baseAPI,
|
||||
// matrixAccessToken: matrixAccessToken,
|
||||
// );
|
||||
|
||||
final Map<String, dynamic> body = {
|
||||
ModelKey.userFullName: fullName,
|
||||
ModelKey.userPangeaUserId: userID,
|
||||
ModelKey.userDateOfBirth: dob,
|
||||
};
|
||||
final resp = await req.post(
|
||||
url: PApiUrls.createUser,
|
||||
body: body,
|
||||
);
|
||||
return PangeaProfileResponse.fromJson(jsonDecode(resp.body));
|
||||
} catch (err, s) {
|
||||
ErrorHandler.logError(e: err, s: s);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
// final Map<String, dynamic> body = {
|
||||
// ModelKey.userFullName: fullName,
|
||||
// ModelKey.userPangeaUserId: userID,
|
||||
// ModelKey.userDateOfBirth: dob,
|
||||
// };
|
||||
// final resp = await req.post(
|
||||
// url: PApiUrls.createUser,
|
||||
// body: body,
|
||||
// );
|
||||
// return PangeaProfileResponse.fromJson(jsonDecode(resp.body));
|
||||
// } catch (err, s) {
|
||||
// ErrorHandler.logError(e: err, s: s);
|
||||
// return null;
|
||||
// }
|
||||
// }
|
||||
|
||||
static Future<PangeaProfileResponse?> fetchPangeaUserInfo({
|
||||
required String userID,
|
||||
|
|
|
|||
|
|
@ -36,9 +36,10 @@ class GetChatListItemSubtitle {
|
|||
eventContextId = null;
|
||||
}
|
||||
|
||||
final Timeline timeline = await event.room.getTimeline(
|
||||
eventContextId: eventContextId,
|
||||
);
|
||||
final Timeline timeline = event.room.timeline != null &&
|
||||
event.room.timeline!.chunk.eventsMap.containsKey(eventContextId)
|
||||
? event.room.timeline!
|
||||
: await event.room.getTimeline(eventContextId: eventContextId);
|
||||
|
||||
if (moveBackInTimeline(event)) {
|
||||
event = timeline.events.firstWhereOrNull((e) => !moveBackInTimeline(e));
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ class MessageTranslationCardState extends State<MessageTranslationCard> {
|
|||
|
||||
oldSelectedText = widget.selection.selectedText;
|
||||
final String accessToken =
|
||||
await MatrixState.pangeaController.userController.accessToken;
|
||||
MatrixState.pangeaController.userController.accessToken;
|
||||
|
||||
final resp = await FullTextTranslationRepo.translate(
|
||||
accessToken: accessToken,
|
||||
|
|
|
|||
|
|
@ -20,6 +20,9 @@ class ConversationBotCustomSystemPromptInput extends StatelessWidget {
|
|||
final TextEditingController textFieldController =
|
||||
TextEditingController(text: customSystemPrompt);
|
||||
|
||||
final GlobalKey<FormState> customSystemPromptFormKey =
|
||||
GlobalKey<FormState>();
|
||||
|
||||
void setBotCustomSystemPromptAction() async {
|
||||
showDialog(
|
||||
context: context,
|
||||
|
|
@ -28,14 +31,25 @@ class ConversationBotCustomSystemPromptInput extends StatelessWidget {
|
|||
title: Text(
|
||||
L10n.of(context)!.conversationBotCustomZone_customSystemPromptLabel,
|
||||
),
|
||||
content: TextField(
|
||||
minLines: 1,
|
||||
maxLines: 10,
|
||||
maxLength: 1000,
|
||||
controller: textFieldController,
|
||||
onChanged: (value) {
|
||||
customSystemPrompt = value;
|
||||
},
|
||||
content: Form(
|
||||
key: customSystemPromptFormKey,
|
||||
child: TextFormField(
|
||||
minLines: 1,
|
||||
maxLines: 10,
|
||||
maxLength: 1000,
|
||||
controller: textFieldController,
|
||||
onChanged: (value) {
|
||||
if (value.isNotEmpty) {
|
||||
customSystemPrompt = value;
|
||||
}
|
||||
},
|
||||
validator: (value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
return 'This field cannot be empty';
|
||||
}
|
||||
return null;
|
||||
},
|
||||
),
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
|
|
@ -47,11 +61,12 @@ class ConversationBotCustomSystemPromptInput extends StatelessWidget {
|
|||
TextButton(
|
||||
child: Text(L10n.of(context)!.ok),
|
||||
onPressed: () {
|
||||
if (customSystemPrompt == "") return;
|
||||
if (customSystemPrompt !=
|
||||
initialBotOptions.customSystemPrompt) {
|
||||
initialBotOptions.customSystemPrompt = customSystemPrompt;
|
||||
onChanged.call(initialBotOptions);
|
||||
if (customSystemPromptFormKey.currentState!.validate()) {
|
||||
if (customSystemPrompt !=
|
||||
initialBotOptions.customSystemPrompt) {
|
||||
initialBotOptions.customSystemPrompt = customSystemPrompt;
|
||||
onChanged.call(initialBotOptions);
|
||||
}
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
},
|
||||
|
|
@ -68,6 +83,13 @@ class ConversationBotCustomSystemPromptInput extends StatelessWidget {
|
|||
L10n.of(context)!
|
||||
.conversationBotCustomZone_customSystemPromptPlaceholder,
|
||||
),
|
||||
subtitle: customSystemPrompt.isEmpty
|
||||
? Text(
|
||||
L10n.of(context)!
|
||||
.conversationBotCustomZone_customSystemPromptEmptyError,
|
||||
style: const TextStyle(color: Colors.red),
|
||||
)
|
||||
: null,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
import 'package:fluffychat/pangea/models/bot_options_model.dart';
|
||||
import 'package:fluffychat/pangea/widgets/conversation_bot/conversation_bot_custom_system_prompt_input.dart';
|
||||
import 'package:fluffychat/pangea/widgets/conversation_bot/conversation_bot_dynamic_zone_label.dart';
|
||||
import 'package:fluffychat/pangea/widgets/conversation_bot/conversation_bot_dynamic_zone_title.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||
|
||||
|
|
@ -16,35 +18,14 @@ class ConversationBotCustomZone extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
print(initialBotOptions.toJson());
|
||||
return Column(
|
||||
children: [
|
||||
const SizedBox(height: 12),
|
||||
Text(
|
||||
L10n.of(context)!.conversationBotCustomZone_title,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.secondary,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
ConversationBotDynamicZoneTitle(
|
||||
title: L10n.of(context)!.conversationBotCustomZone_title,
|
||||
),
|
||||
const Divider(
|
||||
color: Colors.grey,
|
||||
thickness: 1,
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.fromLTRB(12, 0, 0, 0),
|
||||
child: Text(
|
||||
L10n.of(context)!
|
||||
.conversationBotCustomZone_customSystemPromptLabel,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.secondary,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
ConversationBotDynamicZoneLabel(
|
||||
label: L10n.of(context)!
|
||||
.conversationBotCustomZone_customSystemPromptLabel,
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8),
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
import 'package:fluffychat/pangea/models/bot_options_model.dart';
|
||||
import 'package:fluffychat/pangea/widgets/conversation_bot/conversation_bot_discussion_keywords_input.dart';
|
||||
import 'package:fluffychat/pangea/widgets/conversation_bot/conversation_bot_discussion_topic_input.dart';
|
||||
import 'package:fluffychat/pangea/widgets/conversation_bot/conversation_bot_dynamic_zone_label.dart';
|
||||
import 'package:fluffychat/pangea/widgets/conversation_bot/conversation_bot_dynamic_zone_title.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||
|
||||
|
|
@ -19,32 +21,12 @@ class ConversationBotDiscussionZone extends StatelessWidget {
|
|||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
children: [
|
||||
const SizedBox(height: 12),
|
||||
Text(
|
||||
L10n.of(context)!.conversationBotDiscussionZone_title,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.secondary,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
ConversationBotDynamicZoneTitle(
|
||||
title: L10n.of(context)!.conversationBotDiscussionZone_title,
|
||||
),
|
||||
const Divider(
|
||||
color: Colors.grey,
|
||||
thickness: 1,
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.fromLTRB(12, 0, 0, 0),
|
||||
child: Text(
|
||||
L10n.of(context)!
|
||||
.conversationBotDiscussionZone_discussionTopicLabel,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.secondary,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
ConversationBotDynamicZoneLabel(
|
||||
label: L10n.of(context)!
|
||||
.conversationBotDiscussionZone_discussionTopicLabel,
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8),
|
||||
|
|
@ -54,19 +36,9 @@ class ConversationBotDiscussionZone extends StatelessWidget {
|
|||
),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.fromLTRB(12, 0, 0, 0),
|
||||
child: Text(
|
||||
L10n.of(context)!
|
||||
.conversationBotDiscussionZone_discussionKeywordsLabel,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.secondary,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
ConversationBotDynamicZoneLabel(
|
||||
label: L10n.of(context)!
|
||||
.conversationBotDiscussionZone_discussionKeywordsLabel,
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8),
|
||||
|
|
|
|||
|
|
@ -0,0 +1,27 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
class ConversationBotDynamicZoneLabel extends StatelessWidget {
|
||||
final String label;
|
||||
|
||||
const ConversationBotDynamicZoneLabel({
|
||||
super.key,
|
||||
required this.label,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.fromLTRB(12, 0, 0, 0),
|
||||
child: Text(
|
||||
label,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.secondary,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
class ConversationBotDynamicZoneTitle extends StatelessWidget {
|
||||
final String title;
|
||||
|
||||
const ConversationBotDynamicZoneTitle({
|
||||
super.key,
|
||||
required this.title,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
children: [
|
||||
const SizedBox(height: 12),
|
||||
Text(
|
||||
title,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.secondary,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
const Divider(
|
||||
color: Colors.grey,
|
||||
thickness: 1,
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
import 'package:fluffychat/pangea/models/bot_options_model.dart';
|
||||
import 'package:fluffychat/pangea/widgets/conversation_bot/conversation_bot_custom_zone.dart';
|
||||
import 'package:fluffychat/pangea/widgets/conversation_bot/conversation_bot_text_adventure_zone.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'conversation_bot_discussion_zone.dart';
|
||||
|
|
@ -26,7 +27,10 @@ class ConversationBotModeDynamicZone extends StatelessWidget {
|
|||
onChanged: onChanged,
|
||||
),
|
||||
// "conversation": const ConversationBotConversationZone(),
|
||||
// "text_adventure": const ConversationBotTextAdventureZone(),
|
||||
"text_adventure": ConversationBotTextAdventureZone(
|
||||
initialBotOptions: initialBotOptions,
|
||||
onChanged: onChanged,
|
||||
),
|
||||
};
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
|
|
|
|||
|
|
@ -19,8 +19,8 @@ class ConversationBotModeSelect extends StatelessWidget {
|
|||
"custom": L10n.of(context)!.conversationBotModeSelectOption_custom,
|
||||
// "conversation":
|
||||
// L10n.of(context)!.conversationBotModeSelectOption_conversation,
|
||||
// "text_adventure":
|
||||
// L10n.of(context)!.conversationBotModeSelectOption_textAdventure,
|
||||
"text_adventure":
|
||||
L10n.of(context)!.conversationBotModeSelectOption_textAdventure,
|
||||
};
|
||||
|
||||
return Padding(
|
||||
|
|
|
|||
|
|
@ -1,22 +1,19 @@
|
|||
import 'dart:developer';
|
||||
|
||||
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';
|
||||
import 'package:fluffychat/pangea/utils/bot_name.dart';
|
||||
import 'package:fluffychat/pangea/utils/error_handler.dart';
|
||||
import 'package:fluffychat/pangea/widgets/common/bot_face_svg.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';
|
||||
import 'package:fluffychat/pangea/widgets/space/language_level_dropdown.dart';
|
||||
import 'package:fluffychat/pangea/widgets/conversation_bot/conversation_bot_settings_form.dart';
|
||||
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';
|
||||
|
||||
import '../../../widgets/matrix.dart';
|
||||
import '../../constants/pangea_event_types.dart';
|
||||
import '../../extensions/pangea_room_extension/pangea_room_extension.dart';
|
||||
import '../../utils/error_handler.dart';
|
||||
|
||||
class ConversationBotSettings extends StatefulWidget {
|
||||
final Room? room;
|
||||
final bool startOpen;
|
||||
|
|
@ -36,6 +33,7 @@ class ConversationBotSettings extends StatefulWidget {
|
|||
class ConversationBotSettingsState extends State<ConversationBotSettings> {
|
||||
late BotOptionsModel botOptions;
|
||||
late bool isOpen;
|
||||
late bool isCreating;
|
||||
bool addBot = false;
|
||||
Room? parentSpace;
|
||||
|
||||
|
|
@ -56,6 +54,22 @@ class ConversationBotSettingsState extends State<ConversationBotSettings> {
|
|||
parentSpace = widget.activeSpaceId != null
|
||||
? Matrix.of(context).client.getRoomById(widget.activeSpaceId!)
|
||||
: null;
|
||||
isCreating = widget.room == null;
|
||||
}
|
||||
|
||||
Future<void> setBotOption() async {
|
||||
if (widget.room == null) return;
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> updateBotOption(void Function() makeLocalChange) async {
|
||||
|
|
@ -74,196 +88,191 @@ class ConversationBotSettingsState extends State<ConversationBotSettings> {
|
|||
);
|
||||
}
|
||||
|
||||
Future<void> setBotOption() async {
|
||||
if (widget.room == null) return;
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => Column(
|
||||
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)!.convoBotSettingsTitle,
|
||||
isCreating
|
||||
? L10n.of(context)!.addConversationBot
|
||||
: L10n.of(context)!.botConfig,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.secondary,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
subtitle: Text(L10n.of(context)!.convoBotSettingsDescription),
|
||||
subtitle: isCreating
|
||||
? Text(L10n.of(context)!.addConversationBotDesc)
|
||||
: null,
|
||||
leading: CircleAvatar(
|
||||
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
|
||||
foregroundColor: Theme.of(context).textTheme.bodyLarge!.color,
|
||||
child: const Icon(Icons.psychology_outlined),
|
||||
child: const BotFace(
|
||||
width: 30.0,
|
||||
expression: BotExpression.idle,
|
||||
),
|
||||
),
|
||||
trailing: Icon(
|
||||
isOpen
|
||||
? Icons.keyboard_arrow_down_outlined
|
||||
: Icons.keyboard_arrow_right_outlined,
|
||||
),
|
||||
onTap: () => setState(() => isOpen = !isOpen),
|
||||
),
|
||||
if (isOpen)
|
||||
AnimatedContainer(
|
||||
duration: const Duration(milliseconds: 300),
|
||||
height: isOpen ? null : 0,
|
||||
width: double.infinity,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 16),
|
||||
child: ListTile(
|
||||
title: Text(
|
||||
L10n.of(context)!.addConversationBot,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.secondary,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
subtitle: Text(L10n.of(context)!.addConversationBotDesc),
|
||||
leading: CircleAvatar(
|
||||
backgroundColor:
|
||||
Theme.of(context).scaffoldBackgroundColor,
|
||||
foregroundColor:
|
||||
Theme.of(context).textTheme.bodyLarge!.color,
|
||||
child: const BotFace(
|
||||
width: 30.0,
|
||||
expression: BotExpression.idle,
|
||||
),
|
||||
),
|
||||
trailing: ElevatedButton(
|
||||
onPressed: () async {
|
||||
final bool? confirm = await showDialog<bool>(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AlertDialog(
|
||||
title: addBot
|
||||
trailing: isCreating
|
||||
? ElevatedButton(
|
||||
onPressed: () async {
|
||||
final bool? confirm = await showDialog<bool>(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AlertDialog(
|
||||
title: addBot
|
||||
? Text(
|
||||
L10n.of(context)!
|
||||
.addConversationBotButtonTitleRemove,
|
||||
)
|
||||
: Text(
|
||||
L10n.of(context)!
|
||||
.addConversationBotDialogTitleInvite,
|
||||
),
|
||||
actions: <Widget>[
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop(false);
|
||||
},
|
||||
child: Text(L10n.of(context)!.cancel),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop(!addBot);
|
||||
},
|
||||
child: addBot
|
||||
? Text(
|
||||
L10n.of(context)!
|
||||
.addConversationBotButtonTitleRemove,
|
||||
.addConversationBotDialogRemoveConfirmation,
|
||||
)
|
||||
: Text(
|
||||
L10n.of(context)!
|
||||
.addConversationBotDialogTitleInvite,
|
||||
.addConversationBotDialogInviteConfirmation,
|
||||
),
|
||||
actions: <Widget>[
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop(false);
|
||||
},
|
||||
child: Text(L10n.of(context)!.cancel),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop(!addBot);
|
||||
},
|
||||
child: addBot
|
||||
? Text(
|
||||
L10n.of(context)!
|
||||
.addConversationBotDialogRemoveConfirmation,
|
||||
)
|
||||
: Text(
|
||||
L10n.of(context)!
|
||||
.addConversationBotDialogInviteConfirmation,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
if (confirm == true) {
|
||||
setState(() => addBot = true);
|
||||
widget.room?.invite(BotName.byEnvironment);
|
||||
} else {
|
||||
setState(() => addBot = false);
|
||||
widget.room?.kick(BotName.byEnvironment);
|
||||
}
|
||||
},
|
||||
child: addBot
|
||||
? Text(
|
||||
L10n.of(context)!
|
||||
.addConversationBotButtonRemove,
|
||||
)
|
||||
: Text(
|
||||
L10n.of(context)!
|
||||
.addConversationBotButtonInvite,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
if (addBot) ...[
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(32, 16, 0, 0),
|
||||
child: Text(
|
||||
L10n.of(context)!.conversationLanguageLevel,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.secondary,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 16,
|
||||
),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 16),
|
||||
child: LanguageLevelDropdown(
|
||||
initialLevel: botOptions.languageLevel,
|
||||
onChanged: (int? newValue) => updateBotOption(() {
|
||||
botOptions.languageLevel = newValue!;
|
||||
}),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(32, 16, 0, 0),
|
||||
child: Text(
|
||||
L10n.of(context)!.conversationBotModeSelectDescription,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.secondary,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 16,
|
||||
),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 16),
|
||||
child: ConversationBotModeSelect(
|
||||
initialMode: botOptions.mode,
|
||||
onChanged: (String? mode) => updateBotOption(
|
||||
() {
|
||||
botOptions.mode = mode ?? "discussion";
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(28, 0, 12, 0),
|
||||
child: ConversationBotModeDynamicZone(
|
||||
initialBotOptions: botOptions,
|
||||
onChanged: (BotOptionsModel? newOptions) {
|
||||
updateBotOption(() {
|
||||
if (newOptions != null) {
|
||||
botOptions = newOptions;
|
||||
}
|
||||
});
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
],
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
if (confirm == true) {
|
||||
setState(() => addBot = true);
|
||||
widget.room?.invite(BotName.byEnvironment);
|
||||
} else {
|
||||
setState(() => addBot = false);
|
||||
widget.room?.kick(BotName.byEnvironment);
|
||||
}
|
||||
},
|
||||
child: addBot
|
||||
? Text(
|
||||
L10n.of(context)!.addConversationBotButtonRemove,
|
||||
)
|
||||
: Text(
|
||||
L10n.of(context)!.addConversationBotButtonInvite,
|
||||
),
|
||||
)
|
||||
: const Icon(Icons.settings),
|
||||
onTap: isCreating
|
||||
? null
|
||||
: () async {
|
||||
final bool? confirm = await showDialog<bool>(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return StatefulBuilder(
|
||||
builder: (context, setState) => AlertDialog(
|
||||
title: Text(
|
||||
L10n.of(context)!.botConfig,
|
||||
),
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Padding(
|
||||
padding:
|
||||
const EdgeInsets.fromLTRB(0, 0, 0, 12),
|
||||
child: Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
L10n.of(context)!.conversationBotStatus,
|
||||
),
|
||||
Switch(
|
||||
value: addBot,
|
||||
onChanged: (value) {
|
||||
setState(
|
||||
() => addBot = value,
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
if (addBot)
|
||||
Flexible(
|
||||
child: SingleChildScrollView(
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.secondary,
|
||||
width: 0.5,
|
||||
),
|
||||
borderRadius: const BorderRadius.all(
|
||||
Radius.circular(10),
|
||||
),
|
||||
),
|
||||
child: ConversationBotSettingsForm(
|
||||
botOptions: botOptions,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
actions: <Widget>[
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop(false);
|
||||
},
|
||||
child: Text(L10n.of(context)!.cancel),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop(true);
|
||||
},
|
||||
child: Text(
|
||||
L10n.of(context)!
|
||||
.conversationBotConfigConfirmChange,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
if (confirm == true) {
|
||||
if (addBot) {
|
||||
await widget.room?.invite(BotName.byEnvironment);
|
||||
} else {
|
||||
await widget.room?.kick(BotName.byEnvironment);
|
||||
}
|
||||
updateBotOption(() {
|
||||
botOptions = botOptions;
|
||||
});
|
||||
}
|
||||
},
|
||||
),
|
||||
if (isCreating && addBot)
|
||||
ConversationBotSettingsForm(
|
||||
botOptions: botOptions,
|
||||
),
|
||||
],
|
||||
);
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,88 @@
|
|||
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';
|
||||
import 'package:fluffychat/pangea/widgets/space/language_level_dropdown.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||
|
||||
class ConversationBotSettingsForm extends StatefulWidget {
|
||||
final BotOptionsModel botOptions;
|
||||
|
||||
const ConversationBotSettingsForm({
|
||||
super.key,
|
||||
required this.botOptions,
|
||||
});
|
||||
|
||||
@override
|
||||
ConversationBotSettingsFormState createState() =>
|
||||
ConversationBotSettingsFormState();
|
||||
}
|
||||
|
||||
class ConversationBotSettingsFormState
|
||||
extends State<ConversationBotSettingsForm> {
|
||||
final formKey = GlobalKey<FormState>();
|
||||
|
||||
late BotOptionsModel botOptions;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
botOptions = widget.botOptions;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(12.0),
|
||||
child: Text(
|
||||
L10n.of(context)!.conversationLanguageLevel,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.secondary,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 16,
|
||||
),
|
||||
),
|
||||
),
|
||||
LanguageLevelDropdown(
|
||||
initialLevel: botOptions.languageLevel,
|
||||
onChanged: (int? newValue) => {
|
||||
setState(() {
|
||||
botOptions.languageLevel = newValue!;
|
||||
}),
|
||||
},
|
||||
),
|
||||
Text(
|
||||
L10n.of(context)!.conversationBotModeSelectDescription,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.secondary,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 16,
|
||||
),
|
||||
),
|
||||
ConversationBotModeSelect(
|
||||
initialMode: botOptions.mode,
|
||||
onChanged: (String? mode) => {
|
||||
setState(() {
|
||||
botOptions.mode = mode ?? "discussion";
|
||||
}),
|
||||
},
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(12),
|
||||
child: ConversationBotModeDynamicZone(
|
||||
initialBotOptions: botOptions,
|
||||
onChanged: (BotOptionsModel? newOptions) {
|
||||
if (newOptions != null) {
|
||||
setState(() {
|
||||
botOptions = newOptions;
|
||||
});
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,91 @@
|
|||
import 'package:fluffychat/pangea/models/bot_options_model.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_gen/gen_l10n/l10n.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,15 +1,39 @@
|
|||
import 'package:fluffychat/pangea/models/bot_options_model.dart';
|
||||
import 'package:fluffychat/pangea/widgets/conversation_bot/conversation_bot_dynamic_zone_label.dart';
|
||||
import 'package:fluffychat/pangea/widgets/conversation_bot/conversation_bot_dynamic_zone_title.dart';
|
||||
import 'package:fluffychat/pangea/widgets/conversation_bot/conversation_bot_text_adventure_game_master_instruction_input.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_gen/gen_l10n/l10n.dart';
|
||||
|
||||
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 const Column(
|
||||
return Column(
|
||||
children: [
|
||||
Text('Text Adventure Zone'),
|
||||
ConversationBotDynamicZoneTitle(
|
||||
title: L10n.of(context)!.conversationBotTextAdventureZone_title,
|
||||
),
|
||||
ConversationBotDynamicZoneLabel(
|
||||
label: L10n.of(context)!
|
||||
.conversationBotTextAdventureZone_instructionLabel,
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8),
|
||||
child: ConversationBotGameMasterInstructionsInput(
|
||||
initialBotOptions: initialBotOptions,
|
||||
onChanged: onChanged,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ class PangeaTextController extends TextEditingController {
|
|||
}
|
||||
|
||||
static const int maxLength = 1000;
|
||||
bool get isMaxLength => text.length == 1000;
|
||||
bool get exceededMaxLength => text.length >= maxLength;
|
||||
|
||||
bool forceKeepOpen = false;
|
||||
|
||||
|
|
|
|||
|
|
@ -312,7 +312,7 @@ class GenerateVocabButtonState extends State<GenerateVocabButton> {
|
|||
|
||||
Future<List<Lemma>> _getWords() async {
|
||||
final ChatTopic topic = await TopicDataRepo.generate(
|
||||
await _pangeaController.userController.accessToken,
|
||||
_pangeaController.userController.accessToken,
|
||||
request: TopicDataRequest(
|
||||
topicInfo: widget.topic,
|
||||
numWords: 10,
|
||||
|
|
@ -514,7 +514,7 @@ class PromptsFieldState extends State<PromptsField> {
|
|||
|
||||
Future<List<DiscussionPrompt>> _getPrompts() async {
|
||||
final ChatTopic res = await TopicDataRepo.generate(
|
||||
await _pangeaController.userController.accessToken,
|
||||
_pangeaController.userController.accessToken,
|
||||
request: TopicDataRequest(
|
||||
topicInfo: widget.topic,
|
||||
numPrompts: 10,
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading…
Add table
Reference in a new issue