Merge branch 'main' into 512-bump-matrix-sdk
This commit is contained in:
commit
500e54bed9
24 changed files with 654 additions and 469 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",
|
||||
|
|
@ -4004,12 +4004,15 @@
|
|||
"conversationBotCustomZone_customSystemPromptLabel": "System prompt",
|
||||
"conversationBotCustomZone_customSystemPromptPlaceholder": "Set 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",
|
||||
"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.",
|
||||
|
|
|
|||
|
|
@ -328,7 +328,6 @@ class ChatController extends State<ChatPageWithRoom>
|
|||
);
|
||||
}
|
||||
await Matrix.of(context).client.roomsLoading;
|
||||
choreographer.setRoomId(roomId);
|
||||
});
|
||||
// Pangea#
|
||||
_tryLoadTimeline();
|
||||
|
|
@ -486,12 +485,6 @@ class ChatController extends State<ChatPageWithRoom>
|
|||
|
||||
final timeline = this.timeline;
|
||||
if (timeline == null || timeline.events.isEmpty) {
|
||||
// #Pangea
|
||||
ErrorHandler.logError(
|
||||
e: PangeaWarningError("Timeline is null or empty"),
|
||||
s: StackTrace.current,
|
||||
);
|
||||
// Pangea#
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -1114,8 +1107,14 @@ class ChatController extends State<ChatPageWithRoom>
|
|||
inputFocus.requestFocus();
|
||||
}
|
||||
|
||||
void scrollToEventId(String eventId) async {
|
||||
final eventIndex = timeline!.events.indexWhere((e) => e.eventId == eventId);
|
||||
void scrollToEventId(
|
||||
String eventId, {
|
||||
bool highlightEvent = true,
|
||||
}) async {
|
||||
final eventIndex = timeline!.events
|
||||
.where((event) => event.isVisibleInGui)
|
||||
.toList()
|
||||
.indexWhere((e) => e.eventId == eventId);
|
||||
if (eventIndex == -1) {
|
||||
setState(() {
|
||||
timeline = null;
|
||||
|
|
@ -1131,11 +1130,14 @@ class ChatController extends State<ChatPageWithRoom>
|
|||
});
|
||||
return;
|
||||
}
|
||||
setState(() {
|
||||
scrollToEventIdMarker = eventId;
|
||||
});
|
||||
if (highlightEvent) {
|
||||
setState(() {
|
||||
scrollToEventIdMarker = eventId;
|
||||
});
|
||||
}
|
||||
await scrollController.scrollToIndex(
|
||||
eventIndex,
|
||||
eventIndex + 1,
|
||||
duration: FluffyThemes.animationDuration,
|
||||
preferPosition: AutoScrollPosition.middle,
|
||||
);
|
||||
_updateScrollController();
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@ import 'package:fluffychat/pangea/choreographer/widgets/start_igc_button.dart';
|
|||
import 'package:fluffychat/pangea/extensions/pangea_room_extension/pangea_room_extension.dart';
|
||||
import 'package:fluffychat/pangea/widgets/chat/chat_floating_action_button.dart';
|
||||
import 'package:fluffychat/utils/account_config.dart';
|
||||
import 'package:fluffychat/utils/platform_infos.dart';
|
||||
import 'package:fluffychat/widgets/chat_settings_popup_menu.dart';
|
||||
import 'package:fluffychat/widgets/connection_status_header.dart';
|
||||
import 'package:fluffychat/widgets/matrix.dart';
|
||||
|
|
@ -299,216 +298,232 @@ class ChatView extends StatelessWidget {
|
|||
),
|
||||
),
|
||||
SafeArea(
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: GestureDetector(
|
||||
onTap: controller.clearSingleSelectedEvent,
|
||||
child: Builder(
|
||||
builder: (context) {
|
||||
if (controller.timeline == null) {
|
||||
return const Center(
|
||||
child: CircularProgressIndicator.adaptive(
|
||||
strokeWidth: 2,
|
||||
),
|
||||
);
|
||||
}
|
||||
return ChatEventList(
|
||||
controller: controller,
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
if (controller.room.canSendDefaultMessages &&
|
||||
controller.room.membership == Membership.join)
|
||||
Container(
|
||||
margin: EdgeInsets.only(
|
||||
bottom: bottomSheetPadding,
|
||||
left: bottomSheetPadding,
|
||||
right: bottomSheetPadding,
|
||||
),
|
||||
constraints: const BoxConstraints(
|
||||
maxWidth: FluffyThemes.columnWidth * 2.5,
|
||||
),
|
||||
alignment: Alignment.center,
|
||||
child: Material(
|
||||
clipBehavior: Clip.hardEdge,
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
// ignore: deprecated_member_use
|
||||
.surfaceVariant,
|
||||
borderRadius: const BorderRadius.all(
|
||||
Radius.circular(24),
|
||||
),
|
||||
child: controller.room.isAbandonedDMRoom == true
|
||||
? Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceEvenly,
|
||||
children: [
|
||||
// #Pangea
|
||||
if (controller.room.isRoomAdmin)
|
||||
TextButton.icon(
|
||||
style: TextButton.styleFrom(
|
||||
padding: const EdgeInsets.all(
|
||||
16,
|
||||
),
|
||||
foregroundColor: Theme.of(context)
|
||||
.colorScheme
|
||||
.error,
|
||||
),
|
||||
icon: const Icon(
|
||||
Icons.archive_outlined,
|
||||
),
|
||||
onPressed: controller.archiveChat,
|
||||
label: Text(
|
||||
L10n.of(context)!.archive,
|
||||
),
|
||||
),
|
||||
// Pangea#
|
||||
TextButton.icon(
|
||||
style: TextButton.styleFrom(
|
||||
padding: const EdgeInsets.all(
|
||||
16,
|
||||
),
|
||||
foregroundColor: Theme.of(context)
|
||||
.colorScheme
|
||||
.error,
|
||||
),
|
||||
icon: const Icon(
|
||||
// #Pangea
|
||||
// Icons.archive_outlined,
|
||||
Icons.arrow_forward,
|
||||
// Pangea#
|
||||
),
|
||||
onPressed: controller.leaveChat,
|
||||
label: Text(
|
||||
L10n.of(context)!.leave,
|
||||
),
|
||||
),
|
||||
TextButton.icon(
|
||||
style: TextButton.styleFrom(
|
||||
padding: const EdgeInsets.all(
|
||||
16,
|
||||
),
|
||||
),
|
||||
icon: const Icon(
|
||||
Icons.forum_outlined,
|
||||
),
|
||||
onPressed: controller.recreateChat,
|
||||
label: Text(
|
||||
L10n.of(context)!.reopenChat,
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
:
|
||||
// #Pangea
|
||||
null,
|
||||
// Column(
|
||||
// mainAxisSize: MainAxisSize.min,
|
||||
// children: [
|
||||
// const ConnectionStatusHeader(),
|
||||
// ITBar(
|
||||
// choreographer:
|
||||
// controller.choreographer,
|
||||
// ),
|
||||
// ReactionsPicker(controller),
|
||||
// ReplyDisplay(controller),
|
||||
// ChatInputRow(controller),
|
||||
// ChatEmojiPicker(controller),
|
||||
// ],
|
||||
// ),
|
||||
// Pangea#
|
||||
),
|
||||
),
|
||||
child:
|
||||
// #Pangea
|
||||
// Keep messages above minimum input bar height
|
||||
SizedBox(
|
||||
height: (PlatformInfos.isMobile ? 30 : 60),
|
||||
Stack(
|
||||
children: [
|
||||
// Pangea#
|
||||
Column(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: GestureDetector(
|
||||
onTap: controller.clearSingleSelectedEvent,
|
||||
child: Builder(
|
||||
builder: (context) {
|
||||
if (controller.timeline == null) {
|
||||
return const Center(
|
||||
child:
|
||||
CircularProgressIndicator.adaptive(
|
||||
strokeWidth: 2,
|
||||
),
|
||||
);
|
||||
}
|
||||
return ChatEventList(
|
||||
controller: controller,
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
if (controller.room.canSendDefaultMessages &&
|
||||
controller.room.membership == Membership.join)
|
||||
Container(
|
||||
margin: EdgeInsets.only(
|
||||
bottom: bottomSheetPadding,
|
||||
left: bottomSheetPadding,
|
||||
right: bottomSheetPadding,
|
||||
),
|
||||
constraints: const BoxConstraints(
|
||||
maxWidth: FluffyThemes.columnWidth * 2.5,
|
||||
),
|
||||
alignment: Alignment.center,
|
||||
child: Material(
|
||||
clipBehavior: Clip.hardEdge,
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
// ignore: deprecated_member_use
|
||||
.surfaceVariant,
|
||||
borderRadius: const BorderRadius.all(
|
||||
Radius.circular(24),
|
||||
),
|
||||
child: controller.room.isAbandonedDMRoom ==
|
||||
true
|
||||
? Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceEvenly,
|
||||
children: [
|
||||
// #Pangea
|
||||
if (controller.room.isRoomAdmin)
|
||||
TextButton.icon(
|
||||
style: TextButton.styleFrom(
|
||||
padding: const EdgeInsets.all(
|
||||
16,
|
||||
),
|
||||
foregroundColor:
|
||||
Theme.of(context)
|
||||
.colorScheme
|
||||
.error,
|
||||
),
|
||||
icon: const Icon(
|
||||
Icons.archive_outlined,
|
||||
),
|
||||
onPressed:
|
||||
controller.archiveChat,
|
||||
label: Text(
|
||||
L10n.of(context)!.archive,
|
||||
),
|
||||
),
|
||||
// Pangea#
|
||||
TextButton.icon(
|
||||
style: TextButton.styleFrom(
|
||||
padding: const EdgeInsets.all(
|
||||
16,
|
||||
),
|
||||
foregroundColor:
|
||||
Theme.of(context)
|
||||
.colorScheme
|
||||
.error,
|
||||
),
|
||||
icon: const Icon(
|
||||
// #Pangea
|
||||
// Icons.archive_outlined,
|
||||
Icons.arrow_forward,
|
||||
// Pangea#
|
||||
),
|
||||
onPressed: controller.leaveChat,
|
||||
label: Text(
|
||||
L10n.of(context)!.leave,
|
||||
),
|
||||
),
|
||||
TextButton.icon(
|
||||
style: TextButton.styleFrom(
|
||||
padding: const EdgeInsets.all(
|
||||
16,
|
||||
),
|
||||
),
|
||||
icon: const Icon(
|
||||
Icons.forum_outlined,
|
||||
),
|
||||
onPressed:
|
||||
controller.recreateChat,
|
||||
label: Text(
|
||||
L10n.of(context)!.reopenChat,
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
:
|
||||
// #Pangea
|
||||
null,
|
||||
// Column(
|
||||
// mainAxisSize: MainAxisSize.min,
|
||||
// children: [
|
||||
// const ConnectionStatusHeader(),
|
||||
// ITBar(
|
||||
// choreographer:
|
||||
// controller.choreographer,
|
||||
// ),
|
||||
// ReactionsPicker(controller),
|
||||
// ReplyDisplay(controller),
|
||||
// ChatInputRow(controller),
|
||||
// ChatEmojiPicker(controller),
|
||||
// ],
|
||||
// ),
|
||||
// Pangea#
|
||||
),
|
||||
),
|
||||
// #Pangea
|
||||
// Keep messages above minimum input bar height
|
||||
const SizedBox(
|
||||
height: 60,
|
||||
),
|
||||
// Pangea#
|
||||
],
|
||||
),
|
||||
// #Pangea
|
||||
Positioned(
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: 16,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
if (!controller.selectMode)
|
||||
Container(
|
||||
margin: EdgeInsets.only(
|
||||
bottom: 10,
|
||||
left: bottomSheetPadding,
|
||||
right: bottomSheetPadding,
|
||||
),
|
||||
constraints: const BoxConstraints(
|
||||
maxWidth: FluffyThemes.columnWidth * 2.4,
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
StartIGCButton(
|
||||
controller: controller,
|
||||
),
|
||||
ChatFloatingActionButton(
|
||||
controller: controller,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Container(
|
||||
margin: EdgeInsets.only(
|
||||
bottom: bottomSheetPadding,
|
||||
left: bottomSheetPadding,
|
||||
right: bottomSheetPadding,
|
||||
),
|
||||
constraints: const BoxConstraints(
|
||||
maxWidth: FluffyThemes.columnWidth * 2.5,
|
||||
),
|
||||
alignment: Alignment.center,
|
||||
child: Material(
|
||||
clipBehavior: Clip.hardEdge,
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.surfaceContainerHighest,
|
||||
borderRadius: const BorderRadius.all(
|
||||
Radius.circular(24),
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
const ConnectionStatusHeader(),
|
||||
ITBar(
|
||||
choreographer: controller.choreographer,
|
||||
),
|
||||
ReactionsPicker(controller),
|
||||
ReplyDisplay(controller),
|
||||
ChatInputRow(controller),
|
||||
ChatEmojiPicker(controller),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
// Pangea#
|
||||
],
|
||||
),
|
||||
// #Pangea
|
||||
// if (controller.dragging)
|
||||
// Container(
|
||||
// color: Theme.of(context)
|
||||
// .scaffoldBackgroundColor
|
||||
// .withOpacity(0.9),
|
||||
// alignment: Alignment.center,
|
||||
// child: const Icon(
|
||||
// Icons.upload_outlined,
|
||||
// size: 100,
|
||||
// ),
|
||||
// ),
|
||||
// Pangea#
|
||||
),
|
||||
// #Pangea
|
||||
// if (controller.dragging)
|
||||
// Container(
|
||||
// color: Theme.of(context)
|
||||
// .scaffoldBackgroundColor
|
||||
// .withOpacity(0.9),
|
||||
// alignment: Alignment.center,
|
||||
// child: const Icon(
|
||||
// Icons.upload_outlined,
|
||||
// size: 100,
|
||||
// ),
|
||||
// ),
|
||||
Positioned(
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: 16,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
if (!controller.selectMode)
|
||||
Container(
|
||||
margin: EdgeInsets.only(
|
||||
bottom: 10,
|
||||
left: bottomSheetPadding,
|
||||
right: bottomSheetPadding,
|
||||
),
|
||||
constraints: const BoxConstraints(
|
||||
maxWidth: FluffyThemes.columnWidth * 2.4,
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
StartIGCButton(
|
||||
controller: controller,
|
||||
),
|
||||
ChatFloatingActionButton(
|
||||
controller: controller,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Container(
|
||||
margin: EdgeInsets.only(
|
||||
bottom: bottomSheetPadding,
|
||||
left: bottomSheetPadding,
|
||||
right: bottomSheetPadding,
|
||||
),
|
||||
constraints: const BoxConstraints(
|
||||
maxWidth: FluffyThemes.columnWidth * 2.5,
|
||||
),
|
||||
alignment: Alignment.center,
|
||||
child: Material(
|
||||
clipBehavior: Clip.hardEdge,
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.surfaceContainerHighest,
|
||||
borderRadius: const BorderRadius.all(
|
||||
Radius.circular(24),
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
const ConnectionStatusHeader(),
|
||||
ITBar(
|
||||
choreographer: controller.choreographer,
|
||||
),
|
||||
ReactionsPicker(controller),
|
||||
ReplyDisplay(controller),
|
||||
ChatInputRow(controller),
|
||||
ChatEmojiPicker(controller),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
// Pangea#
|
||||
],
|
||||
),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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() =>
|
||||
|
|
|
|||
|
|
@ -138,17 +138,20 @@ class ChatDetailsView extends StatelessWidget {
|
|||
Avatar.defaultSize * 2.5,
|
||||
),
|
||||
),
|
||||
child: Hero(
|
||||
tag: controller.widget
|
||||
.embeddedCloseButton !=
|
||||
null
|
||||
? 'embedded_content_banner'
|
||||
: 'content_banner',
|
||||
child: Avatar(
|
||||
mxContent: room.avatar,
|
||||
name: displayname,
|
||||
size: Avatar.defaultSize * 2.5,
|
||||
),
|
||||
// #Pangea
|
||||
// Hero animation is causing weird visual glitch
|
||||
// Probably not worth keeping
|
||||
// child: Hero(
|
||||
// tag: controller.widget
|
||||
// .embeddedCloseButton !=
|
||||
// null
|
||||
// ? 'embedded_content_banner'
|
||||
// : 'content_banner',
|
||||
// Pangea#
|
||||
child: Avatar(
|
||||
mxContent: room.avatar,
|
||||
name: displayname,
|
||||
size: Avatar.defaultSize * 2.5,
|
||||
),
|
||||
),
|
||||
if (!room.isDirectChat &&
|
||||
|
|
|
|||
|
|
@ -863,7 +863,7 @@ class ChatListController extends State<ChatList>
|
|||
|
||||
if (space.canSendDefaultStates) {
|
||||
for (final roomId in selectedRoomIds) {
|
||||
await space.pangeaSetSpaceChild(roomId);
|
||||
await space.pangeaSetSpaceChild(roomId, suggested: true);
|
||||
}
|
||||
}
|
||||
// Pangea#
|
||||
|
|
@ -911,11 +911,12 @@ class ChatListController extends State<ChatList>
|
|||
await client.roomsLoading;
|
||||
await client.accountDataLoading;
|
||||
await client.userDeviceKeysLoading;
|
||||
if (client.prevBatch == null) {
|
||||
// #Pangea
|
||||
// See here for explanation of this change: https://github.com/pangeachat/client/pull/539
|
||||
// if (client.prevBatch == null) {
|
||||
if (client.onSync.value?.nextBatch == null) {
|
||||
// Pangea#
|
||||
await client.onSync.stream.first;
|
||||
// #Pangea
|
||||
pangeaController.startChatWithBotIfNotPresent();
|
||||
//Pangea#
|
||||
|
||||
// Display first login bootstrap if enabled
|
||||
// #Pangea
|
||||
|
|
@ -930,9 +931,19 @@ class ChatListController extends State<ChatList>
|
|||
}
|
||||
|
||||
// #Pangea
|
||||
await _initPangeaControllers(client);
|
||||
// Pangea#
|
||||
if (!mounted) return;
|
||||
setState(() {
|
||||
waitForFirstSync = true;
|
||||
});
|
||||
}
|
||||
|
||||
// #Pangea
|
||||
Future<void> _initPangeaControllers(Client client) async {
|
||||
if (mounted) {
|
||||
// TODO try not to await so much
|
||||
GoogleAnalytics.analyticsUserUpdate(client.userID);
|
||||
pangeaController.startChatWithBotIfNotPresent();
|
||||
await pangeaController.subscriptionController.initialize();
|
||||
await pangeaController.myAnalytics.initialize();
|
||||
pangeaController.afterSyncAndFirstLoginInitialization(context);
|
||||
|
|
@ -943,14 +954,9 @@ class ChatListController extends State<ChatList>
|
|||
ErrorHandler.logError(
|
||||
m: "didn't run afterSyncAndFirstLoginInitialization because not mounted",
|
||||
);
|
||||
// debugger(when: kDebugMode);
|
||||
}
|
||||
// Pangea#
|
||||
if (!mounted) return;
|
||||
setState(() {
|
||||
waitForFirstSync = true;
|
||||
});
|
||||
}
|
||||
// Pangea#
|
||||
|
||||
void cancelAction() {
|
||||
if (selectMode == SelectMode.share) {
|
||||
|
|
|
|||
|
|
@ -579,7 +579,12 @@ class _SpaceViewState extends State<SpaceView> {
|
|||
: null,
|
||||
);
|
||||
}
|
||||
await activeSpace.setSpaceChild(roomId);
|
||||
await activeSpace.setSpaceChild(
|
||||
roomId,
|
||||
// #Pangea
|
||||
suggested: true,
|
||||
// Pangea#
|
||||
);
|
||||
},
|
||||
);
|
||||
if (result.error != null) return;
|
||||
|
|
|
|||
|
|
@ -370,15 +370,17 @@ class UserBottomSheetView extends StatelessWidget {
|
|||
onTap: () => controller
|
||||
.participantAction(UserBottomSheetAction.unban),
|
||||
),
|
||||
if (user != null && user.id != client.userID)
|
||||
ListTile(
|
||||
textColor: Theme.of(context).colorScheme.onErrorContainer,
|
||||
iconColor: Theme.of(context).colorScheme.onErrorContainer,
|
||||
title: Text(L10n.of(context)!.reportUser),
|
||||
leading: const Icon(Icons.report_outlined),
|
||||
onTap: () => controller
|
||||
.participantAction(UserBottomSheetAction.report),
|
||||
),
|
||||
// #Pangea
|
||||
// if (user != null && user.id != client.userID)
|
||||
// ListTile(
|
||||
// textColor: Theme.of(context).colorScheme.onErrorContainer,
|
||||
// iconColor: Theme.of(context).colorScheme.onErrorContainer,
|
||||
// title: Text(L10n.of(context)!.reportUser),
|
||||
// leading: const Icon(Icons.report_outlined),
|
||||
// onTap: () => controller
|
||||
// .participantAction(UserBottomSheetAction.report),
|
||||
// ),
|
||||
// Pangea#
|
||||
if (profileSearchError != null)
|
||||
ListTile(
|
||||
leading: const Icon(
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -233,11 +233,11 @@ class MyAnalyticsController {
|
|||
if (userL2 == null || _client.userID == null) return;
|
||||
|
||||
// analytics room for the user and current target language
|
||||
final Room analyticsRoom = await _client.getMyAnalyticsRoom(userL2!);
|
||||
final Room? analyticsRoom = await _client.getMyAnalyticsRoom(userL2!);
|
||||
|
||||
// get the last time analytics were updated for this room
|
||||
final DateTime? l2AnalyticsLastUpdated =
|
||||
await analyticsRoom.analyticsLastUpdated(
|
||||
await analyticsRoom?.analyticsLastUpdated(
|
||||
PangeaEventTypes.summaryAnalytics,
|
||||
_client.userID!,
|
||||
);
|
||||
|
|
@ -311,7 +311,7 @@ class MyAnalyticsController {
|
|||
// if there's new content to be sent, or if lastUpdated hasn't been
|
||||
// set yet for this room, send the analytics events
|
||||
if (summaryContent.isNotEmpty || l2AnalyticsLastUpdated == null) {
|
||||
await analyticsRoom.sendSummaryAnalyticsEvent(
|
||||
await analyticsRoom?.sendSummaryAnalyticsEvent(
|
||||
summaryContent,
|
||||
);
|
||||
}
|
||||
|
|
@ -351,7 +351,7 @@ class MyAnalyticsController {
|
|||
// );
|
||||
|
||||
if (recentConstructUses.isNotEmpty) {
|
||||
await analyticsRoom.sendConstructsEvent(
|
||||
await analyticsRoom?.sendConstructsEvent(
|
||||
recentConstructUses,
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -83,6 +83,15 @@ 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);
|
||||
}
|
||||
|
||||
|
|
@ -155,13 +164,25 @@ class UserController extends BaseController {
|
|||
_pangeaController.pStoreService.read(PLocalKey.access);
|
||||
|
||||
if (localAccessToken == null || needNewJWT(localAccessToken)) {
|
||||
final PangeaProfileResponse? userModel =
|
||||
await PUserRepo.fetchPangeaUserInfo(
|
||||
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) {
|
||||
throw ("Trying to get accessToken with null userModel");
|
||||
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,
|
||||
|
|
|
|||
|
|
@ -71,12 +71,11 @@ extension MessageModeExtension on MessageMode {
|
|||
switch (this) {
|
||||
case MessageMode.translation:
|
||||
case MessageMode.textToSpeech:
|
||||
case MessageMode.practiceActivity:
|
||||
case MessageMode.definition:
|
||||
return event.messageType == MessageTypes.Text;
|
||||
case MessageMode.speechToText:
|
||||
return event.messageType == MessageTypes.Audio;
|
||||
default:
|
||||
case MessageMode.practiceActivity:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ part of "client_extension.dart";
|
|||
extension AnalyticsClientExtension on Client {
|
||||
/// Get the logged in user's analytics room matching
|
||||
/// a given langCode. If not present, create it.
|
||||
Future<Room> _getMyAnalyticsRoom(String langCode) async {
|
||||
Future<Room?> _getMyAnalyticsRoom(String langCode) async {
|
||||
final Room? analyticsRoom = _analyticsRoomLocal(langCode);
|
||||
if (analyticsRoom != null) return analyticsRoom;
|
||||
return _makeAnalyticsRoom(langCode);
|
||||
|
|
@ -35,7 +35,11 @@ extension AnalyticsClientExtension on Client {
|
|||
///
|
||||
/// If the room does not appear immediately after creation, this method waits for it to appear in sync.
|
||||
/// Returns the created [Room] object.
|
||||
Future<Room> _makeAnalyticsRoom(String langCode) async {
|
||||
Future<Room?> _makeAnalyticsRoom(String langCode) async {
|
||||
if (userID == null || userID == BotName.byEnvironment) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final String roomID = await createRoom(
|
||||
creationContent: {
|
||||
'type': PangeaRoomTypes.analytics,
|
||||
|
|
@ -74,6 +78,7 @@ extension AnalyticsClientExtension on Client {
|
|||
// migration function to change analytics rooms' vsibility to public
|
||||
// so they will appear in the space hierarchy
|
||||
Future<void> _updateAnalyticsRoomVisibility() async {
|
||||
if (userID == null || userID == BotName.byEnvironment) return;
|
||||
await Future.wait(
|
||||
allMyAnalyticsRooms.map((room) async {
|
||||
final visability = await getRoomVisibilityOnDirectory(room.id);
|
||||
|
|
@ -91,6 +96,7 @@ extension AnalyticsClientExtension on Client {
|
|||
/// so teachers can join them via space hierarchy.
|
||||
/// Allows teachers to join analytics rooms without being invited.
|
||||
void _addAnalyticsRoomsToAllSpaces() {
|
||||
if (userID == null || userID == BotName.byEnvironment) return;
|
||||
for (final Room room in allMyAnalyticsRooms) {
|
||||
room.addAnalyticsRoomToSpaces();
|
||||
}
|
||||
|
|
@ -100,6 +106,7 @@ extension AnalyticsClientExtension on Client {
|
|||
/// Handles case when students cannot add analytics room to space(s)
|
||||
/// so teacher is still able to get analytics data for this student
|
||||
void _inviteAllTeachersToAllAnalyticsRooms() {
|
||||
if (userID == null || userID == BotName.byEnvironment) return;
|
||||
for (final Room room in allMyAnalyticsRooms) {
|
||||
room.inviteTeachersToAnalyticsRoom();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ extension PangeaClient on Client {
|
|||
|
||||
/// Get the logged in user's analytics room matching
|
||||
/// a given langCode. If not present, create it.
|
||||
Future<Room> getMyAnalyticsRoom(String langCode) async =>
|
||||
Future<Room?> getMyAnalyticsRoom(String langCode) async =>
|
||||
await _getMyAnalyticsRoom(langCode);
|
||||
|
||||
/// Get local analytics room for a given langCode and
|
||||
|
|
|
|||
|
|
@ -140,8 +140,6 @@ class IGCTextData {
|
|||
matches.removeAt(matchIndex);
|
||||
|
||||
for (final match in matches) {
|
||||
final matchOffset = match.match.offset;
|
||||
final matchLength = match.match.length;
|
||||
match.match.fullText = originalInput;
|
||||
if (match.match.offset > pangeaMatch.match.offset) {
|
||||
match.match.offset += replacement.length - pangeaMatch.match.length;
|
||||
|
|
@ -305,7 +303,7 @@ class IGCTextData {
|
|||
// create a pointer to the current index in the original input
|
||||
// and iterate until the pointer has reached the end of the input
|
||||
int currentIndex = 0;
|
||||
while (currentIndex < originalInput.characters.length - 1) {
|
||||
while (currentIndex < originalInput.characters.length) {
|
||||
// check if the pointer is at a match, and if so, get the index of the match
|
||||
final int matchIndex = matchRanges.indexWhere(
|
||||
(range) => currentIndex >= range[0] && currentIndex < range[1],
|
||||
|
|
|
|||
|
|
@ -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,6 +2,7 @@ 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';
|
||||
|
|
@ -10,6 +11,34 @@ 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,
|
||||
);
|
||||
|
||||
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,
|
||||
required String matrixAccessToken,
|
||||
|
|
|
|||
|
|
@ -435,7 +435,11 @@ class MessageToolbarState extends State<MessageToolbar> {
|
|||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Flexible(
|
||||
Container(
|
||||
constraints: const BoxConstraints(
|
||||
minWidth: 300,
|
||||
maxHeight: 228,
|
||||
),
|
||||
child: SingleChildScrollView(
|
||||
child: AnimatedSize(
|
||||
duration: FluffyThemes.animationDuration,
|
||||
|
|
|
|||
|
|
@ -16,7 +16,6 @@ class ConversationBotCustomZone extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
print(initialBotOptions.toJson());
|
||||
return Column(
|
||||
children: [
|
||||
const SizedBox(height: 12),
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
});
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ description: Learn a language while texting your friends.
|
|||
# Pangea#
|
||||
publish_to: none
|
||||
# On version bump also increase the build number for F-Droid
|
||||
version: 1.21.1+3533
|
||||
version: 1.21.2+3534
|
||||
|
||||
environment:
|
||||
sdk: ">=3.0.0 <4.0.0"
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue