From 9af29d1d0c854908b316f69902d4bdbc2b754c52 Mon Sep 17 00:00:00 2001 From: ggurdin Date: Wed, 30 Oct 2024 15:06:37 -0400 Subject: [PATCH 01/16] start language assistance on enter --- lib/pages/chat/chat_input_row.dart | 10 +++++----- .../choreographer/controllers/choreographer.dart | 7 ++++++- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/lib/pages/chat/chat_input_row.dart b/lib/pages/chat/chat_input_row.dart index 57360d25d..b135f9b58 100644 --- a/lib/pages/chat/chat_input_row.dart +++ b/lib/pages/chat/chat_input_row.dart @@ -1,5 +1,4 @@ import 'package:animations/animations.dart'; -import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/pangea/choreographer/widgets/send_button.dart'; import 'package:fluffychat/pangea/constants/language_constants.dart'; import 'package:fluffychat/utils/platform_infos.dart'; @@ -299,11 +298,12 @@ class ChatInputRow extends StatelessWidget { maxLines: 8, autofocus: !PlatformInfos.isMobile, keyboardType: TextInputType.multiline, - textInputAction: AppConfig.sendOnEnter == true && - PlatformInfos.isMobile - ? TextInputAction.send - : null, // #Pangea + // textInputAction: AppConfig.sendOnEnter == true && + // PlatformInfos.isMobile + // ? TextInputAction.send + // : null, + textInputAction: TextInputAction.send, // onSubmitted: controller.onInputBarSubmitted, onSubmitted: (String value) => controller.onInputBarSubmitted(value, context), diff --git a/lib/pangea/choreographer/controllers/choreographer.dart b/lib/pangea/choreographer/controllers/choreographer.dart index 6f65ea836..578ffc127 100644 --- a/lib/pangea/choreographer/controllers/choreographer.dart +++ b/lib/pangea/choreographer/controllers/choreographer.dart @@ -68,7 +68,12 @@ class Choreographer { } void send(BuildContext context) { - if (!canSendMessage) return; + if (!canSendMessage) { + if (igc.igcTextData != null) { + igc.showFirstMatch(context); + } + return; + } if (pangeaController.subscriptionController.subscriptionStatus == SubscriptionStatus.showPaywall) { From 61aed51d15033e1a4642efdfff48602d956ac72f Mon Sep 17 00:00:00 2001 From: ggurdin Date: Wed, 30 Oct 2024 15:11:26 -0400 Subject: [PATCH 02/16] always show the choreographer send button --- lib/pages/chat/chat_input_row.dart | 56 +++++++++---------- .../choreographer/widgets/send_button.dart | 6 +- 2 files changed, 32 insertions(+), 30 deletions(-) diff --git a/lib/pages/chat/chat_input_row.dart b/lib/pages/chat/chat_input_row.dart index b135f9b58..340784455 100644 --- a/lib/pages/chat/chat_input_row.dart +++ b/lib/pages/chat/chat_input_row.dart @@ -336,36 +336,36 @@ class ChatInputRow extends StatelessWidget { height: height, width: height, alignment: Alignment.center, - child: PlatformInfos.platformCanRecord && - controller.sendController.text.isEmpty - ? FloatingActionButton.small( - tooltip: L10n.of(context)!.voiceMessage, - onPressed: controller.voiceMessageAction, - elevation: 0, - heroTag: null, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(height), - ), - backgroundColor: theme.colorScheme.primary, - foregroundColor: theme.colorScheme.onPrimary, - child: const Icon(Icons.mic_none_outlined), - ) - : + child: // #Pangea + // PlatformInfos.platformCanRecord && + // controller.sendController.text.isEmpty + // ? FloatingActionButton.small( + // tooltip: L10n.of(context)!.voiceMessage, + // onPressed: controller.voiceMessageAction, + // elevation: 0, + // heroTag: null, + // shape: RoundedRectangleBorder( + // borderRadius: BorderRadius.circular(height), + // ), + // backgroundColor: theme.colorScheme.primary, + // foregroundColor: theme.colorScheme.onPrimary, + // child: const Icon(Icons.mic_none_outlined), + // ) + // : FloatingActionButton.small( + // tooltip: L10n.of(context)!.send, + // onPressed: controller.send, + // elevation: 0, + // heroTag: null, + // shape: RoundedRectangleBorder( + // borderRadius: BorderRadius.circular(height), + // ), + // backgroundColor: + // theme.colorScheme.onPrimaryContainer, + // foregroundColor: theme.colorScheme.onPrimary, + // child: const Icon(Icons.send_outlined), + // ), ChoreographerSendButton(controller: controller), - // FloatingActionButton.small( - // tooltip: L10n.of(context)!.send, - // onPressed: controller.send, - // elevation: 0, - // heroTag: null, - // shape: RoundedRectangleBorder( - // borderRadius: BorderRadius.circular(height), - // ), - // backgroundColor: - // theme.colorScheme.onPrimaryContainer, - // foregroundColor: theme.colorScheme.onPrimary, - // child: const Icon(Icons.send_outlined), - // ), // Pangea# ), ], diff --git a/lib/pangea/choreographer/widgets/send_button.dart b/lib/pangea/choreographer/widgets/send_button.dart index 6fba75395..159a70ea1 100644 --- a/lib/pangea/choreographer/widgets/send_button.dart +++ b/lib/pangea/choreographer/widgets/send_button.dart @@ -58,8 +58,10 @@ class ChoreographerSendButtonState extends State { onPressed: () { widget.controller.choreographer.canSendMessage ? widget.controller.choreographer.send(context) - : widget.controller.choreographer.igc - .showFirstMatch(context); + : !widget.controller.choreographer.isRunningIT + ? widget.controller.choreographer.igc + .showFirstMatch(context) + : null; }, tooltip: L10n.of(context)!.send, ), From 2c75090a06cd8eee65d462536a7365b611f24299 Mon Sep 17 00:00:00 2001 From: ggurdin Date: Wed, 30 Oct 2024 15:12:40 -0400 Subject: [PATCH 03/16] removed freeze-analytics comments --- lib/pangea/widgets/chat/missing_voice_button.dart | 2 -- .../widgets/practice_activity/multiple_choice_activity.dart | 2 -- lib/pangea/widgets/practice_activity/word_audio_button.dart | 1 - 3 files changed, 5 deletions(-) diff --git a/lib/pangea/widgets/chat/missing_voice_button.dart b/lib/pangea/widgets/chat/missing_voice_button.dart index b1f12c626..e1f8b74fb 100644 --- a/lib/pangea/widgets/chat/missing_voice_button.dart +++ b/lib/pangea/widgets/chat/missing_voice_button.dart @@ -49,8 +49,6 @@ class MissingVoiceButton extends StatelessWidget { ), TextButton( onPressed: () => launchTTSSettings, - // commenting out as suspecting this is causing an issue - // #freeze-activity style: const ButtonStyle( tapTargetSize: MaterialTapTargetSize.shrinkWrap, ), diff --git a/lib/pangea/widgets/practice_activity/multiple_choice_activity.dart b/lib/pangea/widgets/practice_activity/multiple_choice_activity.dart index e76021000..e48a7bb5b 100644 --- a/lib/pangea/widgets/practice_activity/multiple_choice_activity.dart +++ b/lib/pangea/widgets/practice_activity/multiple_choice_activity.dart @@ -70,7 +70,6 @@ class MultipleChoiceActivityState extends State { return; } - // #freeze-activity MatrixState.pangeaController.myAnalytics.setState( AnalyticsStream( // note - this maybe should be the activity event id @@ -112,7 +111,6 @@ class MultipleChoiceActivityState extends State { ), ), const SizedBox(height: 8), - // #freeze-activity if (practiceActivity.activityType == ActivityTypeEnum.wordFocusListening) WordAudioButton( diff --git a/lib/pangea/widgets/practice_activity/word_audio_button.dart b/lib/pangea/widgets/practice_activity/word_audio_button.dart index 2f56299c8..591228e18 100644 --- a/lib/pangea/widgets/practice_activity/word_audio_button.dart +++ b/lib/pangea/widgets/practice_activity/word_audio_button.dart @@ -55,7 +55,6 @@ class WordAudioButtonState extends State { } }, // Disable button if language isn't supported ), - // #freeze-activity widget.ttsController.missingVoiceButton, ], ); From 66e7eb79fa65f342e3e580d4fd8e62019240da31 Mon Sep 17 00:00:00 2001 From: ggurdin Date: Wed, 30 Oct 2024 16:56:50 -0400 Subject: [PATCH 04/16] don't run languageAssistance after dismissing IT --- lib/pangea/choreographer/controllers/choreographer.dart | 9 ++++++--- lib/pangea/choreographer/controllers/it_controller.dart | 6 ++++++ lib/pangea/choreographer/widgets/send_button.dart | 7 +------ lib/pangea/enum/edit_type.dart | 1 + 4 files changed, 14 insertions(+), 9 deletions(-) diff --git a/lib/pangea/choreographer/controllers/choreographer.dart b/lib/pangea/choreographer/controllers/choreographer.dart index 578ffc127..04e10ae0d 100644 --- a/lib/pangea/choreographer/controllers/choreographer.dart +++ b/lib/pangea/choreographer/controllers/choreographer.dart @@ -89,7 +89,7 @@ class Choreographer { return; } - if (!igc.hasRelevantIGCTextData) { + if (!igc.hasRelevantIGCTextData && !itController.dismissed) { getLanguageHelp().then((value) => _sendWithIGC(context)); } else { _sendWithIGC(context); @@ -206,7 +206,8 @@ class Choreographer { return; } - if (_textController.editType == EditType.igc) { + if (_textController.editType == EditType.igc || + _textController.editType == EditType.itDismissed) { _lastChecked = _textController.text; _textController.editType = EditType.keyboard; return; @@ -608,7 +609,9 @@ class Choreographer { if (isFetching) return false; // they're supposed to run IGC but haven't yet, don't let them send - if (isAutoIGCEnabled && igc.igcTextData == null) return false; + if (igc.igcTextData == null) { + return itController.dismissed; + } // if they have relevant matches, don't let them send final hasITMatches = diff --git a/lib/pangea/choreographer/controllers/it_controller.dart b/lib/pangea/choreographer/controllers/it_controller.dart index e30f30b3d..08450605a 100644 --- a/lib/pangea/choreographer/controllers/it_controller.dart +++ b/lib/pangea/choreographer/controllers/it_controller.dart @@ -4,6 +4,7 @@ import 'dart:developer'; import 'package:fluffychat/pangea/choreographer/controllers/error_service.dart'; import 'package:fluffychat/pangea/constants/choreo_constants.dart'; import 'package:fluffychat/pangea/enum/construct_use_type_enum.dart'; +import 'package:fluffychat/pangea/enum/edit_type.dart'; import 'package:fluffychat/pangea/models/pangea_token_model.dart'; import 'package:fluffychat/pangea/utils/error_handler.dart'; import 'package:flutter/foundation.dart'; @@ -25,6 +26,7 @@ class ITController { bool _willOpen = false; bool _isEditingSourceText = false; bool showChoiceFeedback = false; + bool dismissed = false; ITStartData? _itStartData; String? sourceText; @@ -41,6 +43,7 @@ class ITController { _willOpen = false; showChoiceFeedback = false; _isEditingSourceText = false; + dismissed = false; _itStartData = null; sourceText = null; @@ -70,9 +73,11 @@ class ITController { void closeIT() { // if the user hasn't gone through any IT steps, reset the text if (completedITSteps.isEmpty && sourceText != null) { + choreographer.textController.editType = EditType.itDismissed; choreographer.textController.text = sourceText!; } clear(); + dismissed = true; } /// if IGC isn't positive that text is full L1 then translate to L1 @@ -200,6 +205,7 @@ class ITController { final ITResponseModel res = await _customInputTranslation(currentText + nextText); + if (sourceText == null) return; nextITStep = CurrentITStep( sourceText: sourceText!, diff --git a/lib/pangea/choreographer/widgets/send_button.dart b/lib/pangea/choreographer/widgets/send_button.dart index 159a70ea1..f5e358a31 100644 --- a/lib/pangea/choreographer/widgets/send_button.dart +++ b/lib/pangea/choreographer/widgets/send_button.dart @@ -56,12 +56,7 @@ class ChoreographerSendButtonState extends State { color: widget.controller.choreographer.assistanceState .stateColor(context), onPressed: () { - widget.controller.choreographer.canSendMessage - ? widget.controller.choreographer.send(context) - : !widget.controller.choreographer.isRunningIT - ? widget.controller.choreographer.igc - .showFirstMatch(context) - : null; + widget.controller.choreographer.send(context); }, tooltip: L10n.of(context)!.send, ), diff --git a/lib/pangea/enum/edit_type.dart b/lib/pangea/enum/edit_type.dart index 5d0a43932..775c376f8 100644 --- a/lib/pangea/enum/edit_type.dart +++ b/lib/pangea/enum/edit_type.dart @@ -5,4 +5,5 @@ enum EditType { alternativeTranslation, itGold, itStart, + itDismissed, } From b5fd9bed4bfc36135ccd07bd56755fe8bb9ba537 Mon Sep 17 00:00:00 2001 From: WilsonLe Date: Thu, 31 Oct 2024 17:18:05 +0700 Subject: [PATCH 05/16] fix bot not responding when new user creates an account and start dm with bot --- lib/pangea/controllers/pangea_controller.dart | 83 ++++++++++++++++++- 1 file changed, 79 insertions(+), 4 deletions(-) diff --git a/lib/pangea/controllers/pangea_controller.dart b/lib/pangea/controllers/pangea_controller.dart index 3f0938052..c24e8b3d2 100644 --- a/lib/pangea/controllers/pangea_controller.dart +++ b/lib/pangea/controllers/pangea_controller.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'dart:developer'; import 'dart:math'; +import 'package:fluffychat/pangea/constants/bot_mode.dart'; import 'package:fluffychat/pangea/constants/class_default_values.dart'; import 'package:fluffychat/pangea/constants/pangea_event_types.dart'; import 'package:fluffychat/pangea/controllers/class_controller.dart'; @@ -22,6 +23,7 @@ import 'package:fluffychat/pangea/controllers/user_controller.dart'; import 'package:fluffychat/pangea/controllers/word_net_controller.dart'; import 'package:fluffychat/pangea/extensions/pangea_room_extension/pangea_room_extension.dart'; import 'package:fluffychat/pangea/guard/p_vguard.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/utils/instructions.dart'; @@ -205,15 +207,88 @@ class PangeaController { if (botDMs.isEmpty) { try { - await matrixState.client.startDirectChat( - BotName.byEnvironment, - enableEncryption: false, + // Copied from client.dart.startDirectChat + final directChatRoomId = + matrixState.client.getDirectChatFromUserId(BotName.byEnvironment); + if (directChatRoomId != null) { + final room = matrixState.client.getRoomById(directChatRoomId); + if (room != null) { + if (room.membership == Membership.join) { + return null; + } else if (room.membership == Membership.invite) { + // we might already have an invite into a DM room. If that is the case, we should try to join. If the room is + // unjoinable, that will automatically leave the room, so in that case we need to continue creating a new + // room. (This implicitly also prevents the room from being returned as a DM room by getDirectChatFromUserId, + // because it only returns joined or invited rooms atm.) + await room.join(); + if (room.membership != Membership.leave) { + if (room.membership != Membership.join) { + // Wait for room actually appears in sync with the right membership + await matrixState.client + .waitForRoomInSync(directChatRoomId, join: true); + } + return null; + } + } + } + } + // enableEncryption ??= + // encryptionEnabled && await userOwnsEncryptionKeys(mxid); + // if (enableEncryption) { + // initialState ??= []; + // if (!initialState.any((s) => s.type == EventTypes.Encryption)) { + // initialState.add( + // StateEvent( + // content: { + // 'algorithm': supportedGroupEncryptionAlgorithms.first, + // }, + // type: EventTypes.Encryption, + // ), + // ); + // } + // } + + // Start a new direct chat + final roomId = await matrixState.client.createRoom( + invite: [], // intentionally not invite bot yet + isDirect: true, + preset: CreateRoomPreset.trustedPrivateChat, + initialState: [ + BotOptionsModel(mode: BotMode.directChat).toStateEvent, + ], ); + + final room = matrixState.client.getRoomById(roomId); + if (room == null || room.membership != Membership.join) { + // Wait for room actually appears in sync + await matrixState.client.waitForRoomInSync(roomId, join: true); + } + + final botOptions = room!.getState(PangeaEventTypes.botOptions); + if (botOptions == null) { + await matrixState.client.setRoomStateWithKey( + roomId, + PangeaEventTypes.botOptions, + "", + BotOptionsModel(mode: BotMode.directChat).toJson(), + ); + await matrixState.client + .getRoomStateWithKey(roomId, PangeaEventTypes.botOptions, ""); + } + + // invite bot to direct chat + await matrixState.client.setRoomStateWithKey( + roomId, EventTypes.RoomMember, BotName.byEnvironment, { + "membership": Membership.invite.name, + "is_direct": true, + }); + await room.addToDirectChat(BotName.byEnvironment); + + return null; } catch (err, stack) { debugger(when: kDebugMode); ErrorHandler.logError(e: err, s: stack); } - return; } final Room botDMWithLatestActivity = botDMs.reduce((a, b) { From 7f844ff1a22cd39c7eaf1b8697eb090ccc02019d Mon Sep 17 00:00:00 2001 From: ggurdin Date: Thu, 31 Oct 2024 11:42:44 -0400 Subject: [PATCH 06/16] decide whether to show points gain animation based on the origin of the point update --- lib/pages/chat/chat.dart | 1 + lib/pages/chat/chat_view.dart | 3 ++ .../controllers/it_controller.dart | 2 ++ lib/pangea/choreographer/widgets/it_bar.dart | 6 +++- .../controllers/get_analytics_controller.dart | 34 ++++++++++++++----- .../controllers/my_analytics_controller.dart | 24 ++++++++++--- .../widgets/animations/gain_points.dart | 9 +++-- .../learning_progress_indicators.dart | 7 ++-- lib/pangea/widgets/igc/span_card.dart | 7 +++- .../multiple_choice_activity.dart | 1 + .../practice_activity_card.dart | 5 ++- .../target_tokens_controller.dart | 12 +++---- .../word_focus_listening_activity.dart | 1 + 13 files changed, 84 insertions(+), 28 deletions(-) diff --git a/lib/pages/chat/chat.dart b/lib/pages/chat/chat.dart index db5041623..203186116 100644 --- a/lib/pages/chat/chat.dart +++ b/lib/pages/chat/chat.dart @@ -691,6 +691,7 @@ class ChatController extends State metadata: metadata, )), ], + origin: AnalyticsUpdateOrigin.sendMessage, ), ); } diff --git a/lib/pages/chat/chat_view.dart b/lib/pages/chat/chat_view.dart index 52d5ed351..e3df6a0e2 100644 --- a/lib/pages/chat/chat_view.dart +++ b/lib/pages/chat/chat_view.dart @@ -9,6 +9,7 @@ import 'package:fluffychat/pages/chat/pinned_events.dart'; import 'package:fluffychat/pages/chat/reply_display.dart'; import 'package:fluffychat/pangea/choreographer/widgets/it_bar.dart'; import 'package:fluffychat/pangea/choreographer/widgets/start_igc_button.dart'; +import 'package:fluffychat/pangea/controllers/my_analytics_controller.dart'; import 'package:fluffychat/pangea/extensions/pangea_room_extension/pangea_room_extension.dart'; import 'package:fluffychat/pangea/widgets/animations/gain_points.dart'; import 'package:fluffychat/pangea/widgets/chat/chat_floating_action_button.dart'; @@ -454,6 +455,8 @@ class ChatView extends StatelessWidget { gainColor: Theme.of(context) .colorScheme .onPrimary, + origin: AnalyticsUpdateOrigin + .sendMessage, ), const SizedBox(width: 100), ChatFloatingActionButton( diff --git a/lib/pangea/choreographer/controllers/it_controller.dart b/lib/pangea/choreographer/controllers/it_controller.dart index e30f30b3d..f04689ba0 100644 --- a/lib/pangea/choreographer/controllers/it_controller.dart +++ b/lib/pangea/choreographer/controllers/it_controller.dart @@ -3,6 +3,7 @@ import 'dart:developer'; import 'package:fluffychat/pangea/choreographer/controllers/error_service.dart'; import 'package:fluffychat/pangea/constants/choreo_constants.dart'; +import 'package:fluffychat/pangea/controllers/my_analytics_controller.dart'; import 'package:fluffychat/pangea/enum/construct_use_type_enum.dart'; import 'package:fluffychat/pangea/models/pangea_token_model.dart'; import 'package:fluffychat/pangea/utils/error_handler.dart'; @@ -315,6 +316,7 @@ class ITController { ignoredTokens ?? [], choreographer.roomId, ConstructUseTypeEnum.ignIt, + AnalyticsUpdateOrigin.it, ); Future.delayed( diff --git a/lib/pangea/choreographer/widgets/it_bar.dart b/lib/pangea/choreographer/widgets/it_bar.dart index 9b81eb960..89f9c72ac 100644 --- a/lib/pangea/choreographer/widgets/it_bar.dart +++ b/lib/pangea/choreographer/widgets/it_bar.dart @@ -7,6 +7,7 @@ import 'package:fluffychat/pangea/choreographer/widgets/it_bar_buttons.dart'; import 'package:fluffychat/pangea/choreographer/widgets/it_feedback_card.dart'; import 'package:fluffychat/pangea/choreographer/widgets/translation_finished_flow.dart'; import 'package:fluffychat/pangea/constants/choreo_constants.dart'; +import 'package:fluffychat/pangea/controllers/my_analytics_controller.dart'; import 'package:fluffychat/pangea/enum/construct_use_type_enum.dart'; import 'package:fluffychat/pangea/enum/instructions_enum.dart'; import 'package:fluffychat/pangea/utils/error_handler.dart'; @@ -80,7 +81,9 @@ class ITBarState extends State { children: [ const Positioned( top: 60, - child: PointsGainedAnimation(), + child: PointsGainedAnimation( + origin: AnalyticsUpdateOrigin.it, + ), ), SingleChildScrollView( child: Column( @@ -372,6 +375,7 @@ class ITChoices extends StatelessWidget { continuance.level > 1 ? ConstructUseTypeEnum.incIt : ConstructUseTypeEnum.corIt, + AnalyticsUpdateOrigin.it, ); } controller.currentITStep!.continuances[index].wasClicked = true; diff --git a/lib/pangea/controllers/get_analytics_controller.dart b/lib/pangea/controllers/get_analytics_controller.dart index 26aa7234b..1f891ccd9 100644 --- a/lib/pangea/controllers/get_analytics_controller.dart +++ b/lib/pangea/controllers/get_analytics_controller.dart @@ -23,8 +23,8 @@ class GetAnalyticsController { late PangeaController _pangeaController; final List _cache = []; StreamSubscription? _analyticsUpdateSubscription; - CachedStreamController> analyticsStream = - CachedStreamController>(); + CachedStreamController analyticsStream = + CachedStreamController(); /// The previous XP points of the user, before the last update. /// Used for animating analytics updates. @@ -83,7 +83,7 @@ class GetAnalyticsController { _analyticsUpdateSubscription?.cancel(); _analyticsUpdateSubscription = null; _cache.clear(); - analyticsStream.add([]); + analyticsStream.add(AnalyticsStreamUpdate(constructs: [])); prevXP = null; } @@ -92,24 +92,30 @@ class GetAnalyticsController { if (analyticsUpdate.type == AnalyticsUpdateType.server) { await getConstructs(forceUpdate: true); } - updateAnalyticsStream(); + updateAnalyticsStream(origin: analyticsUpdate.origin); } - void updateAnalyticsStream() { + void updateAnalyticsStream({AnalyticsUpdateOrigin? origin}) { // if there are no construct uses, or if the last update in this // stream has the same length as this update, don't update the stream if (allConstructUses.isEmpty || - allConstructUses.length == analyticsStream.value?.length) { + allConstructUses.length == analyticsStream.value?.constructs.length) { return; } // set the previous XP to the currentXP - if (analyticsStream.value != null && analyticsStream.value!.isNotEmpty) { - prevXP = calcXP(analyticsStream.value!); + if (analyticsStream.value != null && + analyticsStream.value!.constructs.isNotEmpty) { + prevXP = calcXP(analyticsStream.value!.constructs); } // finally, add to the stream - analyticsStream.add(allConstructUses); + analyticsStream.add( + AnalyticsStreamUpdate( + constructs: allConstructUses, + origin: origin, + ), + ); } /// Calculates the user's xpPoints for their current L2, @@ -347,3 +353,13 @@ class AnalyticsCacheEntry { return _createdAt.isBefore(lastEventUpdated); } } + +class AnalyticsStreamUpdate { + final List constructs; + final AnalyticsUpdateOrigin? origin; + + AnalyticsStreamUpdate({ + required this.constructs, + this.origin, + }); +} diff --git a/lib/pangea/controllers/my_analytics_controller.dart b/lib/pangea/controllers/my_analytics_controller.dart index 48dc67573..9c7523991 100644 --- a/lib/pangea/controllers/my_analytics_controller.dart +++ b/lib/pangea/controllers/my_analytics_controller.dart @@ -125,7 +125,7 @@ class MyAnalyticsController extends BaseController { _addLocalMessage(eventID, filtered).then( (_) { _clearDraftUses(roomID); - _decideWhetherToUpdateAnalyticsRoom(level); + _decideWhetherToUpdateAnalyticsRoom(level, data.origin); }, ); }); @@ -135,6 +135,7 @@ class MyAnalyticsController extends BaseController { List tokens, String roomID, ConstructUseTypeEnum useType, + AnalyticsUpdateOrigin origin, ) { final metadata = ConstructUseMetaData( roomId: roomID, @@ -178,7 +179,7 @@ class MyAnalyticsController extends BaseController { final level = _pangeaController.analytics.level; _addLocalMessage('draft$roomID', uses).then( - (_) => _decideWhetherToUpdateAnalyticsRoom(level), + (_) => _decideWhetherToUpdateAnalyticsRoom(level, origin), ); } @@ -218,7 +219,10 @@ class MyAnalyticsController extends BaseController { /// If the addition brought the total number of messages in the cache /// to the max, or if the addition triggered a level-up, update the analytics. /// Otherwise, add a local update to the alert stream. - void _decideWhetherToUpdateAnalyticsRoom(int prevLevel) { + void _decideWhetherToUpdateAnalyticsRoom( + int prevLevel, + AnalyticsUpdateOrigin? origin, + ) { // cancel the last timer that was set on message event and // reset it to fire after _minutesBeforeUpdate minutes _updateTimer?.cancel(); @@ -238,7 +242,7 @@ class MyAnalyticsController extends BaseController { newLevel > prevLevel ? sendLocalAnalyticsToAnalyticsRoom() : analyticsUpdateStream.add( - AnalyticsUpdate(AnalyticsUpdateType.local), + AnalyticsUpdate(AnalyticsUpdateType.local, origin: origin), ); } @@ -345,6 +349,7 @@ class MyAnalyticsController extends BaseController { class AnalyticsStream { final String eventId; final String roomId; + final AnalyticsUpdateOrigin? origin; final List constructs; @@ -352,12 +357,21 @@ class AnalyticsStream { required this.eventId, required this.roomId, required this.constructs, + this.origin, }); } +enum AnalyticsUpdateOrigin { + it, + igc, + sendMessage, + practiceActivity, +} + class AnalyticsUpdate { final AnalyticsUpdateType type; + final AnalyticsUpdateOrigin? origin; final bool isLogout; - AnalyticsUpdate(this.type, {this.isLogout = false}); + AnalyticsUpdate(this.type, {this.isLogout = false, this.origin}); } diff --git a/lib/pangea/widgets/animations/gain_points.dart b/lib/pangea/widgets/animations/gain_points.dart index 13ae324e7..d9d9a0111 100644 --- a/lib/pangea/widgets/animations/gain_points.dart +++ b/lib/pangea/widgets/animations/gain_points.dart @@ -1,6 +1,7 @@ import 'dart:async'; -import 'package:fluffychat/pangea/models/analytics/constructs_model.dart'; +import 'package:fluffychat/pangea/controllers/get_analytics_controller.dart'; +import 'package:fluffychat/pangea/controllers/my_analytics_controller.dart'; import 'package:fluffychat/pangea/utils/bot_style.dart'; import 'package:fluffychat/widgets/matrix.dart'; import 'package:flutter/material.dart'; @@ -8,8 +9,11 @@ import 'package:flutter/material.dart'; class PointsGainedAnimation extends StatefulWidget { final Color? gainColor; final Color? loseColor; + final AnalyticsUpdateOrigin origin; + const PointsGainedAnimation({ super.key, + required this.origin, this.gainColor, this.loseColor = Colors.red, }); @@ -69,7 +73,8 @@ class PointsGainedAnimationState extends State super.dispose(); } - void _showPointsGained(List constructs) { + void _showPointsGained(AnalyticsStreamUpdate update) { + if (update.origin != widget.origin) return; setState(() => _addedPoints = (_currentXP ?? 0) - (_prevXP ?? 0)); if (_prevXP != _currentXP) { _controller.reset(); diff --git a/lib/pangea/widgets/chat_list/analytics_summary/learning_progress_indicators.dart b/lib/pangea/widgets/chat_list/analytics_summary/learning_progress_indicators.dart index 6695d2673..07a5aedb7 100644 --- a/lib/pangea/widgets/chat_list/analytics_summary/learning_progress_indicators.dart +++ b/lib/pangea/widgets/chat_list/analytics_summary/learning_progress_indicators.dart @@ -1,6 +1,7 @@ import 'dart:async'; import 'package:fluffychat/config/themes.dart'; +import 'package:fluffychat/pangea/controllers/get_analytics_controller.dart'; import 'package:fluffychat/pangea/controllers/pangea_controller.dart'; import 'package:fluffychat/pangea/enum/construct_type_enum.dart'; import 'package:fluffychat/pangea/enum/progress_indicators_enum.dart'; @@ -37,7 +38,7 @@ class LearningProgressIndicatorsState /// A stream subscription to listen for updates to /// the analytics data, either locally or from events - StreamSubscription>? _analyticsUpdateSubscription; + StreamSubscription? _analyticsUpdateSubscription; /// Vocabulary constructs model ConstructListModel? words; @@ -65,11 +66,11 @@ class LearningProgressIndicatorsState void initState() { super.initState(); updateAnalyticsData( - _pangeaController.analytics.analyticsStream.value ?? [], + _pangeaController.analytics.analyticsStream.value?.constructs ?? [], ); _analyticsUpdateSubscription = _pangeaController .analytics.analyticsStream.stream - .listen(updateAnalyticsData); + .listen((update) => updateAnalyticsData(update.constructs)); } @override diff --git a/lib/pangea/widgets/igc/span_card.dart b/lib/pangea/widgets/igc/span_card.dart index ddfa43ba4..53bd4050d 100644 --- a/lib/pangea/widgets/igc/span_card.dart +++ b/lib/pangea/widgets/igc/span_card.dart @@ -1,6 +1,7 @@ import 'dart:developer'; import 'package:fluffychat/config/app_config.dart'; +import 'package:fluffychat/pangea/controllers/my_analytics_controller.dart'; import 'package:fluffychat/pangea/enum/construct_use_type_enum.dart'; import 'package:fluffychat/pangea/enum/span_data_type.dart'; import 'package:fluffychat/pangea/models/pangea_token_model.dart'; @@ -130,6 +131,7 @@ class SpanCardState extends State { selectedChoice!.isBestCorrection ? ConstructUseTypeEnum.corIGC : ConstructUseTypeEnum.incIGC, + AnalyticsUpdateOrigin.igc, ); } @@ -160,6 +162,7 @@ class SpanCardState extends State { ignoredTokens ?? [], widget.roomId, ConstructUseTypeEnum.ignIGC, + AnalyticsUpdateOrigin.igc, ); } @@ -226,7 +229,9 @@ class WordMatchContent extends StatelessWidget { children: [ const Positioned( top: 40, - child: PointsGainedAnimation(), + child: PointsGainedAnimation( + origin: AnalyticsUpdateOrigin.igc, + ), ), Column( children: [ diff --git a/lib/pangea/widgets/practice_activity/multiple_choice_activity.dart b/lib/pangea/widgets/practice_activity/multiple_choice_activity.dart index e76021000..b427793a7 100644 --- a/lib/pangea/widgets/practice_activity/multiple_choice_activity.dart +++ b/lib/pangea/widgets/practice_activity/multiple_choice_activity.dart @@ -81,6 +81,7 @@ class MultipleChoiceActivityState extends State { widget.practiceCardController.currentActivity!, widget.practiceCardController.metadata, ), + origin: AnalyticsUpdateOrigin.practiceActivity, ), ); diff --git a/lib/pangea/widgets/practice_activity/practice_activity_card.dart b/lib/pangea/widgets/practice_activity/practice_activity_card.dart index dbb98de73..53644494d 100644 --- a/lib/pangea/widgets/practice_activity/practice_activity_card.dart +++ b/lib/pangea/widgets/practice_activity/practice_activity_card.dart @@ -1,6 +1,7 @@ import 'dart:async'; import 'dart:developer'; +import 'package:fluffychat/pangea/controllers/my_analytics_controller.dart'; import 'package:fluffychat/pangea/controllers/pangea_controller.dart'; import 'package:fluffychat/pangea/enum/activity_type_enum.dart'; import 'package:fluffychat/pangea/matrix_event_wrappers/pangea_message_event.dart'; @@ -335,7 +336,9 @@ class PracticeActivityCardState extends State { children: [ // Main content const Positioned( - child: PointsGainedAnimation(), + child: PointsGainedAnimation( + origin: AnalyticsUpdateOrigin.practiceActivity, + ), ), if (activityWidget != null) Padding( diff --git a/lib/pangea/widgets/practice_activity/target_tokens_controller.dart b/lib/pangea/widgets/practice_activity/target_tokens_controller.dart index e358614f3..874a90a62 100644 --- a/lib/pangea/widgets/practice_activity/target_tokens_controller.dart +++ b/lib/pangea/widgets/practice_activity/target_tokens_controller.dart @@ -29,7 +29,9 @@ class TargetTokensController { _targetTokens = await _initialize(context, pangeaMessageEvent); await updateTokensWithConstructs( - MatrixState.pangeaController.analytics.analyticsStream.value ?? [], + MatrixState + .pangeaController.analytics.analyticsStream.value?.constructs ?? + [], context, pangeaMessageEvent, ); @@ -58,9 +60,8 @@ class TargetTokensController { return _targetTokens = []; } - return _targetTokens = tokens - .map((token) => token.emptyTokenWithXP) - .toList(); + return _targetTokens = + tokens.map((token) => token.emptyTokenWithXP).toList(); } Future updateTokensWithConstructs( @@ -76,9 +77,8 @@ class TargetTokensController { _targetTokens ??= await _initialize(context, pangeaMessageEvent); for (final token in _targetTokens!) { - // we don't need to do this for tokens that don't have saveVocab set to true - if (!token.token.lemma.saveVocab){ + if (!token.token.lemma.saveVocab) { continue; } diff --git a/lib/pangea/widgets/practice_activity/word_focus_listening_activity.dart b/lib/pangea/widgets/practice_activity/word_focus_listening_activity.dart index 8e22aced8..810074a76 100644 --- a/lib/pangea/widgets/practice_activity/word_focus_listening_activity.dart +++ b/lib/pangea/widgets/practice_activity/word_focus_listening_activity.dart @@ -79,6 +79,7 @@ class WordFocusListeningActivityState widget.practiceCardController.currentActivity!, widget.practiceCardController.metadata, ), + origin: AnalyticsUpdateOrigin.practiceActivity, ), ); setState(() { From 8e4bf109ed409e76e1c9a6e7f2926c5a2b874bae Mon Sep 17 00:00:00 2001 From: WilsonLe Date: Thu, 31 Oct 2024 22:58:59 +0700 Subject: [PATCH 07/16] fix brand new account not using new function --- lib/pangea/pages/p_user_age/p_user_age.dart | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/lib/pangea/pages/p_user_age/p_user_age.dart b/lib/pangea/pages/p_user_age/p_user_age.dart index fd937485c..d7e36f200 100644 --- a/lib/pangea/pages/p_user_age/p_user_age.dart +++ b/lib/pangea/pages/p_user_age/p_user_age.dart @@ -2,7 +2,6 @@ import 'dart:developer'; import 'package:fluffychat/pangea/constants/age_limits.dart'; import 'package:fluffychat/pangea/controllers/pangea_controller.dart'; -import 'package:fluffychat/pangea/extensions/client_extension/client_extension.dart'; import 'package:fluffychat/pangea/pages/p_user_age/p_user_age_view.dart'; import 'package:fluffychat/pangea/utils/p_extension.dart'; import 'package:fluffychat/widgets/fluffy_chat_app.dart'; @@ -11,7 +10,6 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; -import '../../utils/bot_name.dart'; import '../../utils/error_handler.dart'; class PUserAge extends StatefulWidget { @@ -34,20 +32,7 @@ class PUserAgeController extends State { @override void initState() { super.initState(); - Future.delayed(Duration.zero, () async { - if (!(await Matrix.of(context).client.hasBotDM)) { - Matrix.of(context) - .client - .startDirectChat( - BotName.byEnvironment, - enableEncryption: false, - ) - .onError( - (error, stackTrace) => - ErrorHandler.logError(e: error, s: stackTrace), - ); - } - }); + pangeaController.startChatWithBotIfNotPresent(); } String? dobValidator() { From 79a6c300d639ffd82a6208f411f36adacb54a002 Mon Sep 17 00:00:00 2001 From: William Jordan-Cooley Date: Thu, 31 Oct 2024 12:52:05 -0400 Subject: [PATCH 08/16] copy for Loc --- assets/l10n/intl_en.arb | 1 + assets/l10n/intl_es.arb | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/assets/l10n/intl_en.arb b/assets/l10n/intl_en.arb index 1cd522a5c..b5310c8e3 100644 --- a/assets/l10n/intl_en.arb +++ b/assets/l10n/intl_en.arb @@ -4303,6 +4303,7 @@ "grammarCopyAccDat": "Accusative, Dative", "grammarCopyInf": "Infinitive", "grammarCopyLong": "Long", + "grammarCopyLoc": "Locative", "grammarCopyInd": "Indicative", "grammarCopyCmp": "Comparative", "grammarCopyRelative_case": "Relative Case", diff --git a/assets/l10n/intl_es.arb b/assets/l10n/intl_es.arb index a833f813c..fd478e870 100644 --- a/assets/l10n/intl_es.arb +++ b/assets/l10n/intl_es.arb @@ -4962,6 +4962,7 @@ "grammarCopyAccDat": "Acusativo, Dativo", "grammarCopyInf": "Infinitivo", "grammarCopyLong": "Largo", + "grammarCopyLoc": "Locativa", "grammarCopyInd": "Indicativo", "grammarCopyCmp": "Comparativa", "grammarCopyRelative_case": "Caso relativo", @@ -5008,4 +5009,4 @@ "spaceCapacityNotSet": "Este espacio no tiene lĂ­mite de capacidad.", "chatExceedsCapacity": "Este chat supera su capacidad.", "spaceExceedsCapacity": "Este espacio supera su capacidad." -} +} \ No newline at end of file From 307eb92227b55372e27112758ed54ca4416be6ee Mon Sep 17 00:00:00 2001 From: ggurdin Date: Thu, 31 Oct 2024 13:30:51 -0400 Subject: [PATCH 09/16] allow text to wrap in inline tooltips --- lib/pangea/utils/inline_tooltip.dart | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/lib/pangea/utils/inline_tooltip.dart b/lib/pangea/utils/inline_tooltip.dart index 4c81e1bbd..f96cc05bb 100644 --- a/lib/pangea/utils/inline_tooltip.dart +++ b/lib/pangea/utils/inline_tooltip.dart @@ -41,14 +41,16 @@ class InlineTooltip extends StatelessWidget { ), const SizedBox(width: 8), // Text in the middle - Center( - child: Text( - instructionsEnum.body(L10n.of(context)!), - style: TextStyle( - color: Theme.of(context).colorScheme.onSurface, - height: 1.5, + Flexible( + child: Center( + child: Text( + instructionsEnum.body(L10n.of(context)!), + style: TextStyle( + color: Theme.of(context).colorScheme.onSurface, + height: 1.5, + ), + textAlign: TextAlign.left, ), - textAlign: TextAlign.left, ), ), // Close button on the right From 646272eae0cb491505fdc1bed516b89f16544cb3 Mon Sep 17 00:00:00 2001 From: ggurdin Date: Thu, 31 Oct 2024 14:10:38 -0400 Subject: [PATCH 10/16] in conversation bot settings dialog, on tap outside textfield close mobile keyboard --- .../conversation_bot/conversation_bot_mode_dynamic_zone.dart | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/pangea/widgets/conversation_bot/conversation_bot_mode_dynamic_zone.dart b/lib/pangea/widgets/conversation_bot/conversation_bot_mode_dynamic_zone.dart index 5a3082610..e691cbcb5 100644 --- a/lib/pangea/widgets/conversation_bot/conversation_bot_mode_dynamic_zone.dart +++ b/lib/pangea/widgets/conversation_bot/conversation_bot_mode_dynamic_zone.dart @@ -24,6 +24,7 @@ class ConversationBotModeDynamicZone extends StatelessWidget { Widget build(BuildContext context) { final discussionChildren = [ TextFormField( + onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(), decoration: InputDecoration( hintText: L10n.of(context)! .conversationBotDiscussionZone_discussionTopicPlaceholder, @@ -43,6 +44,7 @@ class ConversationBotModeDynamicZone extends StatelessWidget { ), const SizedBox(height: 12), TextFormField( + onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(), decoration: InputDecoration( hintText: L10n.of(context)! .conversationBotDiscussionZone_discussionKeywordsPlaceholder, @@ -58,6 +60,7 @@ class ConversationBotModeDynamicZone extends StatelessWidget { final customChildren = [ TextFormField( + onTapOutside: (_) => FocusManager.instance.primaryFocus?.unfocus(), decoration: InputDecoration( hintText: L10n.of(context)! .conversationBotCustomZone_customSystemPromptPlaceholder, From aab41e260025291f778e337e1118138a8d8153a9 Mon Sep 17 00:00:00 2001 From: ggurdin Date: Thu, 31 Oct 2024 14:15:53 -0400 Subject: [PATCH 11/16] remove room details button because it's duplicate of button next to avatar --- lib/pages/chat_details/chat_details_view.dart | 6 ------ 1 file changed, 6 deletions(-) diff --git a/lib/pages/chat_details/chat_details_view.dart b/lib/pages/chat_details/chat_details_view.dart index 90f2a8927..72441e450 100644 --- a/lib/pages/chat_details/chat_details_view.dart +++ b/lib/pages/chat_details/chat_details_view.dart @@ -7,7 +7,6 @@ import 'package:fluffychat/pangea/pages/class_settings/class_name_header.dart'; import 'package:fluffychat/pangea/pages/class_settings/p_class_widgets/class_description_button.dart'; import 'package:fluffychat/pangea/pages/class_settings/p_class_widgets/class_details_toggle_add_students_tile.dart'; import 'package:fluffychat/pangea/pages/class_settings/p_class_widgets/class_invitation_buttons.dart'; -import 'package:fluffychat/pangea/pages/class_settings/p_class_widgets/class_name_button.dart'; import 'package:fluffychat/pangea/pages/class_settings/p_class_widgets/room_capacity_button.dart'; import 'package:fluffychat/pangea/utils/lock_room.dart'; import 'package:fluffychat/pangea/widgets/conversation_bot/conversation_bot_settings.dart'; @@ -213,11 +212,6 @@ class ChatDetailsView extends StatelessWidget { ), Divider(color: theme.dividerColor), // #Pangea - if (room.isRoomAdmin) - ClassNameButton( - room: room, - controller: controller, - ), if (room.canSendEvent('m.room.topic')) ClassDescriptionButton( room: room, From 662098ee4c7fda6691a02e20f35e90ba86b7627b Mon Sep 17 00:00:00 2001 From: ggurdin Date: Thu, 31 Oct 2024 14:46:48 -0400 Subject: [PATCH 12/16] use filtered construct list and number of lemmas in learning analytics view --- .../analytics/construct_list_model.dart | 7 +++++++ .../analytics_summary/analytics_popup.dart | 19 ++++++++----------- .../learning_progress_indicators.dart | 4 ++-- 3 files changed, 17 insertions(+), 13 deletions(-) diff --git a/lib/pangea/models/analytics/construct_list_model.dart b/lib/pangea/models/analytics/construct_list_model.dart index d73b5060a..23763824c 100644 --- a/lib/pangea/models/analytics/construct_list_model.dart +++ b/lib/pangea/models/analytics/construct_list_model.dart @@ -26,6 +26,10 @@ class ConstructListModel { /// All unique lemmas used in the construct events List get lemmas => constructList.map((e) => e.lemma).toSet().toList(); + /// All unique lemmas used in the construct events with non-zero points + List get lemmasWithPoints => + constructListWithPoints.map((e) => e.lemma).toSet().toList(); + /// A map of lemmas to ConstructUses, each of which contains a lemma /// key = lemmma + constructType.string, value = ConstructUses void _buildConstructMap() { @@ -72,6 +76,9 @@ class ConstructListModel { return _constructList!; } + List get constructListWithPoints => + constructList.where((constructUse) => constructUse.points > 0).toList(); + get maxXPPerLemma { return type != null ? type!.maxXPPerLemma diff --git a/lib/pangea/widgets/chat_list/analytics_summary/analytics_popup.dart b/lib/pangea/widgets/chat_list/analytics_summary/analytics_popup.dart index 7f609ce11..d7978af73 100644 --- a/lib/pangea/widgets/chat_list/analytics_summary/analytics_popup.dart +++ b/lib/pangea/widgets/chat_list/analytics_summary/analytics_popup.dart @@ -16,11 +16,6 @@ class AnalyticsPopup extends StatelessWidget { super.key, }); - // we just want to show the constructs that have points - List get constructs => constructsModel.constructList - .where((constructUse) => constructUse.points > 0) - .toList(); - @override Widget build(BuildContext context) { return Dialog( @@ -41,29 +36,31 @@ class AnalyticsPopup extends StatelessWidget { ), body: Padding( padding: const EdgeInsets.symmetric(vertical: 20), - child: constructs.isEmpty + child: constructsModel.constructListWithPoints.isEmpty ? Center( child: Text(L10n.of(context)!.noDataFound), ) : ListView.builder( - itemCount: constructs.length, + itemCount: constructsModel.constructListWithPoints.length, itemBuilder: (context, index) { return Tooltip( message: - "${constructs[index].points} / ${constructsModel.maxXPPerLemma}", + "${constructsModel.constructListWithPoints[index].points} / ${constructsModel.maxXPPerLemma}", child: ListTile( onTap: () {}, title: Text( constructsModel.type == ConstructTypeEnum.morph ? getGrammarCopy( constructsModel - .constructList[index].lemma, + .constructListWithPoints[index].lemma, context, ) - : constructs[index].lemma, + : constructsModel + .constructListWithPoints[index].lemma, ), subtitle: LinearProgressIndicator( - value: constructs[index].points / + value: constructsModel + .constructListWithPoints[index].points / constructsModel.maxXPPerLemma, minHeight: 20, borderRadius: const BorderRadius.all( diff --git a/lib/pangea/widgets/chat_list/analytics_summary/learning_progress_indicators.dart b/lib/pangea/widgets/chat_list/analytics_summary/learning_progress_indicators.dart index 6695d2673..0a616c6bd 100644 --- a/lib/pangea/widgets/chat_list/analytics_summary/learning_progress_indicators.dart +++ b/lib/pangea/widgets/chat_list/analytics_summary/learning_progress_indicators.dart @@ -111,9 +111,9 @@ class LearningProgressIndicatorsState int? getProgressPoints(ProgressIndicatorEnum indicator) { switch (indicator) { case ProgressIndicatorEnum.wordsUsed: - return words?.lemmas.length; + return words?.lemmasWithPoints.length; case ProgressIndicatorEnum.morphsUsed: - return morphs?.lemmas.length; + return morphs?.lemmasWithPoints.length; case ProgressIndicatorEnum.level: return level; } From a0faa997306aba4043cfca0a03ab27d4cf3edcea Mon Sep 17 00:00:00 2001 From: ggurdin Date: Thu, 31 Oct 2024 15:01:56 -0400 Subject: [PATCH 13/16] restrict width of missing voice button --- .../widgets/chat/missing_voice_button.dart | 36 ++++++++++--------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/lib/pangea/widgets/chat/missing_voice_button.dart b/lib/pangea/widgets/chat/missing_voice_button.dart index b1f12c626..1765a9d20 100644 --- a/lib/pangea/widgets/chat/missing_voice_button.dart +++ b/lib/pangea/widgets/chat/missing_voice_button.dart @@ -40,23 +40,27 @@ class MissingVoiceButton extends StatelessWidget { ), padding: const EdgeInsets.all(8), margin: const EdgeInsets.only(top: 8), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text( - L10n.of(context)!.voiceNotAvailable, - textAlign: TextAlign.center, - ), - TextButton( - onPressed: () => launchTTSSettings, - // commenting out as suspecting this is causing an issue - // #freeze-activity - style: const ButtonStyle( - tapTargetSize: MaterialTapTargetSize.shrinkWrap, + child: SizedBox( + width: AppConfig.toolbarMinWidth, + child: Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + L10n.of(context)!.voiceNotAvailable, + textAlign: TextAlign.center, ), - child: Text(L10n.of(context)!.openVoiceSettings), - ), - ], + TextButton( + onPressed: () => launchTTSSettings, + // commenting out as suspecting this is causing an issue + // #freeze-activity + style: const ButtonStyle( + tapTargetSize: MaterialTapTargetSize.shrinkWrap, + ), + child: Text(L10n.of(context)!.openVoiceSettings), + ), + ], + ), ), ); } From 80d9276f6e393e42281619a838c70e994e5b034c Mon Sep 17 00:00:00 2001 From: ggurdin Date: Thu, 31 Oct 2024 15:44:41 -0400 Subject: [PATCH 14/16] don't allow user to chose the incorrect choices in IT --- lib/pangea/choreographer/widgets/it_bar.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pangea/choreographer/widgets/it_bar.dart b/lib/pangea/choreographer/widgets/it_bar.dart index 9b81eb960..529100d94 100644 --- a/lib/pangea/choreographer/widgets/it_bar.dart +++ b/lib/pangea/choreographer/widgets/it_bar.dart @@ -352,7 +352,7 @@ class ITChoices extends StatelessWidget { void selectContinuance(int index, BuildContext context) { final Continuance continuance = controller.currentITStep!.continuances[index]; - if (continuance.level == 1 || continuance.wasClicked) { + if (continuance.level == 1) { Future.delayed( const Duration(milliseconds: 500), () => controller.selectTranslation(index), From bda5cd4728336e705cc5545ba08bd81ce396d073 Mon Sep 17 00:00:00 2001 From: ggurdin Date: Thu, 31 Oct 2024 16:42:33 -0400 Subject: [PATCH 15/16] updated chat description copy and added padding to move scrollbar out of bot settings box --- .../class_description_button.dart | 2 +- .../conversation_bot_settings.dart | 44 ++++++++++--------- pubspec.yaml | 2 +- 3 files changed, 26 insertions(+), 22 deletions(-) diff --git a/lib/pangea/pages/class_settings/p_class_widgets/class_description_button.dart b/lib/pangea/pages/class_settings/p_class_widgets/class_description_button.dart index cea57995f..35d787fce 100644 --- a/lib/pangea/pages/class_settings/p_class_widgets/class_description_button.dart +++ b/lib/pangea/pages/class_settings/p_class_widgets/class_description_button.dart @@ -42,7 +42,7 @@ class ClassDescriptionButton extends StatelessWidget { ? (room.isRoomAdmin ? (room.isSpace ? L10n.of(context)!.classDescriptionDesc - : L10n.of(context)!.chatTopicDesc) + : L10n.of(context)!.setChatDescription) : L10n.of(context)!.topicNotSet) : room.topic, ), diff --git a/lib/pangea/widgets/conversation_bot/conversation_bot_settings.dart b/lib/pangea/widgets/conversation_bot/conversation_bot_settings.dart index 044fcc214..e30429214 100644 --- a/lib/pangea/widgets/conversation_bot/conversation_bot_settings.dart +++ b/lib/pangea/widgets/conversation_bot/conversation_bot_settings.dart @@ -200,27 +200,31 @@ class ConversationBotSettingsDialogState ), Expanded( child: SingleChildScrollView( - child: Column( - children: [ - const SizedBox(height: 20), - AnimatedOpacity( - duration: FluffyThemes.animationDuration, - opacity: addBot ? 1.0 : 0.5, - child: ConversationBotSettingsForm( - botOptions: botOptions, - discussionKeywordsController: - discussionKeywordsController, - discussionTopicController: discussionTopicController, - customSystemPromptController: - customSystemPromptController, - enabled: addBot, - onUpdateBotMode: onUpdateChatMode, - onUpdateBotLanguage: onUpdateBotLanguage, - onUpdateBotVoice: onUpdateBotVoice, - onUpdateBotLanguageLevel: onUpdateBotLanguageLevel, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 16), + child: Column( + children: [ + const SizedBox(height: 20), + AnimatedOpacity( + duration: FluffyThemes.animationDuration, + opacity: addBot ? 1.0 : 0.5, + child: ConversationBotSettingsForm( + botOptions: botOptions, + discussionKeywordsController: + discussionKeywordsController, + discussionTopicController: + discussionTopicController, + customSystemPromptController: + customSystemPromptController, + enabled: addBot, + onUpdateBotMode: onUpdateChatMode, + onUpdateBotLanguage: onUpdateBotLanguage, + onUpdateBotVoice: onUpdateBotVoice, + onUpdateBotLanguageLevel: onUpdateBotLanguageLevel, + ), ), - ), - ], + ], + ), ), ), ), diff --git a/pubspec.yaml b/pubspec.yaml index 788d4d1ca..8e7242893 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -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.23.2+3561 +version: 1.23.3+3562 environment: sdk: ">=3.0.0 <4.0.0" From cbec09fec72bd59b0db54e536bc4de96bb4e9eed Mon Sep 17 00:00:00 2001 From: WilsonLe Date: Fri, 1 Nov 2024 11:21:01 +0700 Subject: [PATCH 16/16] =?UTF-8?q?fix=20word=20sticking=20together,=20final?= =?UTF-8?q?ly=20=F0=9F=A4=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../widgets/chat/overlay_message_text.dart | 57 +++++++++++++++---- pubspec.lock | 2 +- pubspec.yaml | 1 + 3 files changed, 48 insertions(+), 12 deletions(-) diff --git a/lib/pangea/widgets/chat/overlay_message_text.dart b/lib/pangea/widgets/chat/overlay_message_text.dart index 4d91ed359..1ca2f8473 100644 --- a/lib/pangea/widgets/chat/overlay_message_text.dart +++ b/lib/pangea/widgets/chat/overlay_message_text.dart @@ -65,33 +65,71 @@ class OverlayMessageTextState extends State { ); } - int lastEnd = 0; +// Convert the entire message into a list of characters + final Characters messageCharacters = + widget.pangeaMessageEvent.event.body.characters; + + // When building token positions, use grapheme cluster indices final List tokenPositions = []; + int globalIndex = 0; for (int i = 0; i < tokens!.length; i++) { final token = tokens![i]; final start = token.start; final end = token.end; - if (lastEnd < start) { - tokenPositions.add(TokenPosition(start: lastEnd, end: start)); + // Calculate the number of grapheme clusters up to the start and end positions + final int startIndex = messageCharacters.take(start).length; + final int endIndex = messageCharacters.take(end).length; + + if (globalIndex < startIndex) { + tokenPositions.add(TokenPosition(start: globalIndex, end: startIndex)); } tokenPositions.add( TokenPosition( - start: start, - end: end, + start: startIndex, + end: endIndex, tokenIndex: i, token: token, ), ); - lastEnd = end; + globalIndex = endIndex; } + // debug prints for fixing words sticking together + // void printEscapedString(String input) { + // // Escaped string using Unicode escape sequences + // final String escapedString = input.replaceAllMapped( + // RegExp(r'[^\w\s]', unicode: true), + // (match) { + // final codeUnits = match.group(0)!.runes; + // String unicodeEscapes = ''; + // for (final rune in codeUnits) { + // unicodeEscapes += '\\u{${rune.toRadixString(16)}}'; + // } + // return unicodeEscapes; + // }, + // ); + // print("Escaped String: $escapedString"); + + // // Printing each character with its index + // int index = 0; + // for (final char in input.characters) { + // print("Index $index: $char"); + // index++; + // } + // } + //TODO - take out of build function of every message return RichText( text: TextSpan( children: tokenPositions.map((tokenPosition) { + final substring = messageCharacters + .skip(tokenPosition.start) + .take(tokenPosition.end - tokenPosition.start) + .toString(); + if (tokenPosition.token != null) { final isSelected = widget.overlayController.isTokenSelected(tokenPosition.token!); @@ -106,7 +144,7 @@ class OverlayMessageTextState extends State { ); setState(() {}); }, - text: tokenPosition.token!.text.content, + text: substring, style: style.merge( TextStyle( backgroundColor: isSelected @@ -119,10 +157,7 @@ class OverlayMessageTextState extends State { ); } else { return TextSpan( - text: widget.pangeaMessageEvent.event.body.substring( - tokenPosition.start, - tokenPosition.end, - ), + text: substring, style: style, ); } diff --git a/pubspec.lock b/pubspec.lock index c8a53d2c7..1beb361d5 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -162,7 +162,7 @@ packages: source: hosted version: "1.1.2" characters: - dependency: transitive + dependency: "direct main" description: name: characters sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" diff --git a/pubspec.yaml b/pubspec.yaml index 69f0f1a63..be22ad146 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -19,6 +19,7 @@ dependencies: badges: ^3.1.2 blurhash_dart: ^1.2.1 callkeep: ^0.3.2 + characters: ^1.2.0 chewie: ^1.8.1 collection: ^1.18.0 cupertino_icons: any