From 9af29d1d0c854908b316f69902d4bdbc2b754c52 Mon Sep 17 00:00:00 2001 From: ggurdin Date: Wed, 30 Oct 2024 15:06:37 -0400 Subject: [PATCH 01/14] 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/14] 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/14] 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/14] 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 7f844ff1a22cd39c7eaf1b8697eb090ccc02019d Mon Sep 17 00:00:00 2001 From: ggurdin Date: Thu, 31 Oct 2024 11:42:44 -0400 Subject: [PATCH 05/14] 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 04f6e7df1378d59adaada3099590d6c0fcef73a6 Mon Sep 17 00:00:00 2001 From: ggurdin Date: Thu, 31 Oct 2024 12:09:19 -0400 Subject: [PATCH 06/14] handle case of null voices list --- lib/pangea/widgets/chat/tts_controller.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/pangea/widgets/chat/tts_controller.dart b/lib/pangea/widgets/chat/tts_controller.dart index c98cb6220..baffb7b9b 100644 --- a/lib/pangea/widgets/chat/tts_controller.dart +++ b/lib/pangea/widgets/chat/tts_controller.dart @@ -45,8 +45,8 @@ class TtsController { await tts.awaitSpeakCompletion(true); - final voices = await tts.getVoices; - availableLangCodes = (voices as List) + final voices = (await tts.getVoices) as List?; + availableLangCodes = (voices ?? []) .map((v) { // on iOS / web, the codes are in 'locale', but on Android, they are in 'name' final nameCode = v['name']?.split("-").first; From 5dad3e5337669d3fc9bbac56b53f2b8a9e11fa9b Mon Sep 17 00:00:00 2001 From: ggurdin Date: Thu, 31 Oct 2024 13:08:22 -0400 Subject: [PATCH 07/14] check if overlay message text is still mounted before calling setState --- lib/pangea/widgets/chat/overlay_message_text.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pangea/widgets/chat/overlay_message_text.dart b/lib/pangea/widgets/chat/overlay_message_text.dart index 4d91ed359..16290e1a8 100644 --- a/lib/pangea/widgets/chat/overlay_message_text.dart +++ b/lib/pangea/widgets/chat/overlay_message_text.dart @@ -104,7 +104,7 @@ class OverlayMessageTextState extends State { widget.overlayController.onClickOverlayMessageToken( tokenPosition.token!, ); - setState(() {}); + if (mounted) setState(() {}); }, text: tokenPosition.token!.text.content, style: style.merge( From cbec09fec72bd59b0db54e536bc4de96bb4e9eed Mon Sep 17 00:00:00 2001 From: WilsonLe Date: Fri, 1 Nov 2024 11:21:01 +0700 Subject: [PATCH 08/14] =?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 From 0a627ef85675fac44edfc082d888b8f62ac79786 Mon Sep 17 00:00:00 2001 From: ggurdin Date: Fri, 1 Nov 2024 10:54:56 -0400 Subject: [PATCH 09/14] updates to main menu design --- lib/pages/chat_list/chat_list_body.dart | 7 +- lib/pages/chat_list/chat_list_header.dart | 16 ++- .../chat_list/client_chooser_button.dart | 57 +++++--- .../animations/progress_bar/level_bar.dart | 6 +- .../animations/progress_bar/progress_bar.dart | 56 +++++--- .../progress_bar/progress_bar_details.dart | 4 +- .../learning_progress_indicators.dart | 127 +++++++----------- .../analytics_summary/progress_indicator.dart | 60 ++++----- .../chat_list/pangea_chat_list_header.dart | 82 +++++++++++ 9 files changed, 266 insertions(+), 149 deletions(-) create mode 100644 lib/pangea/widgets/chat_list/pangea_chat_list_header.dart diff --git a/lib/pages/chat_list/chat_list_body.dart b/lib/pages/chat_list/chat_list_body.dart index 19e46b9ae..738a65126 100644 --- a/lib/pages/chat_list/chat_list_body.dart +++ b/lib/pages/chat_list/chat_list_body.dart @@ -1,11 +1,11 @@ import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/pages/chat_list/chat_list.dart'; -import 'package:fluffychat/pages/chat_list/chat_list_header.dart'; import 'package:fluffychat/pages/chat_list/chat_list_item.dart'; import 'package:fluffychat/pages/chat_list/dummy_chat_list_item.dart'; import 'package:fluffychat/pages/chat_list/search_title.dart'; import 'package:fluffychat/pages/chat_list/space_view.dart'; import 'package:fluffychat/pages/user_bottom_sheet/user_bottom_sheet.dart'; +import 'package:fluffychat/pangea/widgets/chat_list/pangea_chat_list_header.dart'; import 'package:fluffychat/utils/adaptive_bottom_sheet.dart'; import 'package:fluffychat/utils/stream_extension.dart'; import 'package:fluffychat/widgets/avatar.dart'; @@ -78,7 +78,10 @@ class ChatListViewBody extends StatelessWidget { child: CustomScrollView( controller: controller.scrollController, slivers: [ - ChatListHeader(controller: controller), + // #Pangea + // ChatListHeader(controller: controller), + PangeaChatListHeader(controller: controller), + // Pangea# SliverList( delegate: SliverChildListDelegate( [ diff --git a/lib/pages/chat_list/chat_list_header.dart b/lib/pages/chat_list/chat_list_header.dart index 1f7a1fbe9..58655259a 100644 --- a/lib/pages/chat_list/chat_list_header.dart +++ b/lib/pages/chat_list/chat_list_header.dart @@ -107,9 +107,13 @@ class ChatListHeader extends StatelessWidget implements PreferredSizeWidget { ), ) // #Pangea - : SizedBox( + : const SizedBox( width: 0, - child: ClientChooserButton(controller), + child: ClientChooserButton( + // #Pangea + // controller + // Pangea# + ), ) // : TextButton.icon( // onPressed: controller.setServer, @@ -127,9 +131,13 @@ class ChatListHeader extends StatelessWidget implements PreferredSizeWidget { // ), // ) // Pangea# - : SizedBox( + : const SizedBox( width: 0, - child: ClientChooserButton(controller), + child: ClientChooserButton( + // #Pangea + // controller + // Pangea# + ), ), ), ), diff --git a/lib/pages/chat_list/client_chooser_button.dart b/lib/pages/chat_list/client_chooser_button.dart index 0ac5ff0a8..46f5b944a 100644 --- a/lib/pages/chat_list/client_chooser_button.dart +++ b/lib/pages/chat_list/client_chooser_button.dart @@ -11,12 +11,18 @@ import 'package:go_router/go_router.dart'; // import 'package:keyboard_shortcuts/keyboard_shortcuts.dart'; import 'package:matrix/matrix.dart'; -import 'chat_list.dart'; - class ClientChooserButton extends StatelessWidget { - final ChatListController controller; + // #Pangea + // final ChatListController controller; + // Pangea# - const ClientChooserButton(this.controller, {super.key}); + const ClientChooserButton( + // #Pangea + // this.controller, + // Pangea# + { + super.key, + }); List> _bundleMenuItems(BuildContext context) { final matrix = Matrix.of(context); @@ -268,12 +274,27 @@ class ClientChooserButton extends StatelessWidget { child: Material( color: Colors.transparent, borderRadius: BorderRadius.circular(99), - child: Avatar( - mxContent: snapshot.data?.avatarUrl, - name: snapshot.data?.displayName ?? - matrix.client.userID!.localpart, - size: 32, + child: + // #Pangea + Stack( + alignment: Alignment.bottomRight, + children: [ + Padding( + padding: const EdgeInsets.all(4), + child: + // Pangea# + Avatar( + mxContent: snapshot.data?.avatarUrl, + name: snapshot.data?.displayName ?? + matrix.client.userID!.localpart, + size: 50, + ), + // #Pangea + ), + const Icon(Icons.settings_outlined, size: 20), + ], ), + // Pangea# ), ), ], @@ -296,11 +317,14 @@ class ClientChooserButton extends StatelessWidget { Object object, BuildContext context, ) async { - if (object is Client) { - controller.setActiveClient(object); - } else if (object is String) { - controller.setActiveBundle(object); - } else if (object is SettingsAction) { + // #Pangea + // if (object is Client) { + // controller.setActiveClient(object); + // } else if (object is String) { + // controller.setActiveBundle(object); + // } else + // Pangea# + if (object is SettingsAction) { switch (object) { case SettingsAction.addAccount: final consent = await showOkCancelAlertDialog( @@ -319,7 +343,10 @@ class ClientChooserButton extends StatelessWidget { // break; // Pangea# case SettingsAction.newSpace: - controller.createNewSpace(); + // #Pangea + // controller.createNewSpace(); + context.push('/rooms/newspace'); + // Pangea# break; // #Pangea // case SettingsAction.invite: diff --git a/lib/pangea/widgets/animations/progress_bar/level_bar.dart b/lib/pangea/widgets/animations/progress_bar/level_bar.dart index fb8461f43..78b4d4944 100644 --- a/lib/pangea/widgets/animations/progress_bar/level_bar.dart +++ b/lib/pangea/widgets/animations/progress_bar/level_bar.dart @@ -19,12 +19,14 @@ class LevelBar extends StatefulWidget { class LevelBarState extends State { double prevWidth = 0; + double get width => + widget.progressBarDetails.totalWidth * widget.details.widthMultiplier; @override void didUpdateWidget(covariant LevelBar oldWidget) { super.didUpdateWidget(oldWidget); if (oldWidget.details.currentPoints != widget.details.currentPoints) { - setState(() => prevWidth = widget.details.width); + setState(() => prevWidth = width); } } @@ -33,7 +35,7 @@ class LevelBarState extends State { return AnimatedLevelBar( height: widget.progressBarDetails.height, beginWidth: prevWidth, - endWidth: widget.details.width, + endWidth: width, decoration: BoxDecoration( borderRadius: const BorderRadius.all( Radius.circular(AppConfig.borderRadius), diff --git a/lib/pangea/widgets/animations/progress_bar/progress_bar.dart b/lib/pangea/widgets/animations/progress_bar/progress_bar.dart index ea0263a3c..a8383bae5 100644 --- a/lib/pangea/widgets/animations/progress_bar/progress_bar.dart +++ b/lib/pangea/widgets/animations/progress_bar/progress_bar.dart @@ -6,31 +6,55 @@ import 'package:flutter/material.dart'; // Provide an order list of level indicators, each with it's color // and stream. Also provide an overall width and pointsPerLevel. -class ProgressBar extends StatelessWidget { +class ProgressBar extends StatefulWidget { final List levelBars; - final ProgressBarDetails progressBarDetails; const ProgressBar({ super.key, required this.levelBars, - required this.progressBarDetails, }); + @override + ProgressBarState createState() => ProgressBarState(); +} + +class ProgressBarState extends State { + double width = 0; + void setWidth(double newWidth) { + if (width != newWidth) { + setState(() => width = newWidth); + } + } + + get progressBarDetails => ProgressBarDetails( + totalWidth: width, + borderColor: Theme.of(context).colorScheme.primary.withOpacity(0.5), + ); + @override Widget build(BuildContext context) { - return Stack( - alignment: Alignment.centerLeft, - children: [ - ProgressBarBackground(details: progressBarDetails), - for (final levelBar in levelBars) - Padding( - padding: const EdgeInsets.symmetric(horizontal: 2), - child: LevelBar( - details: levelBar, - progressBarDetails: progressBarDetails, - ), - ), - ], + return LayoutBuilder( + builder: (context, constraints) { + if (width != constraints.maxWidth) { + WidgetsBinding.instance.addPostFrameCallback( + (_) => setWidth(constraints.maxWidth), + ); + } + return Stack( + alignment: Alignment.centerLeft, + children: [ + ProgressBarBackground(details: progressBarDetails), + for (final levelBar in widget.levelBars) + Padding( + padding: const EdgeInsets.symmetric(horizontal: 2), + child: LevelBar( + details: levelBar, + progressBarDetails: progressBarDetails, + ), + ), + ], + ); + }, ); } } diff --git a/lib/pangea/widgets/animations/progress_bar/progress_bar_details.dart b/lib/pangea/widgets/animations/progress_bar/progress_bar_details.dart index 9ff4df142..347a98a49 100644 --- a/lib/pangea/widgets/animations/progress_bar/progress_bar_details.dart +++ b/lib/pangea/widgets/animations/progress_bar/progress_bar_details.dart @@ -3,12 +3,12 @@ import 'dart:ui'; class LevelBarDetails { final Color fillColor; final int currentPoints; - final double width; + final double widthMultiplier; const LevelBarDetails({ required this.fillColor, required this.currentPoints, - required this.width, + required this.widthMultiplier, }); } 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 1509386e1..980ee1488 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,22 +1,21 @@ import 'dart:async'; -import 'package:fluffychat/config/themes.dart'; +import 'package:fluffychat/pages/chat_list/client_chooser_button.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'; import 'package:fluffychat/pangea/models/analytics/construct_list_model.dart'; import 'package:fluffychat/pangea/models/analytics/constructs_model.dart'; +import 'package:fluffychat/pangea/pages/settings_learning/settings_learning.dart'; import 'package:fluffychat/pangea/widgets/animations/progress_bar/progress_bar.dart'; import 'package:fluffychat/pangea/widgets/animations/progress_bar/progress_bar_details.dart'; import 'package:fluffychat/pangea/widgets/chat_list/analytics_summary/analytics_popup.dart'; import 'package:fluffychat/pangea/widgets/chat_list/analytics_summary/progress_indicator.dart'; -import 'package:fluffychat/widgets/avatar.dart'; +import 'package:fluffychat/pangea/widgets/flag.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:matrix/matrix.dart'; /// A summary of "My Analytics" shown at the top of the chat list /// It shows a variety of progress indicators such as @@ -120,7 +119,7 @@ class LearningProgressIndicatorsState } } - double get levelBarWidth => FluffyThemes.columnWidth - (32 * 2) - 25; + // double get levelBarWidth => FluffyThemes.columnWidth - (32 * 2) - 25; Color levelColor(int level) { final colors = [ @@ -147,19 +146,14 @@ class LearningProgressIndicatorsState ? const Color.fromARGB(255, 0, 190, 83) : Theme.of(context).colorScheme.primary, currentPoints: currentXP, - width: levelBarWidth * _pangeaController.analytics.levelProgress, + widthMultiplier: _pangeaController.analytics.levelProgress, ), LevelBarDetails( fillColor: Theme.of(context).colorScheme.primary, currentPoints: serverXP, - width: - levelBarWidth * _pangeaController.analytics.serverLevelProgress, + widthMultiplier: _pangeaController.analytics.serverLevelProgress, ), ], - progressBarDetails: ProgressBarDetails( - totalWidth: levelBarWidth, - borderColor: Theme.of(context).colorScheme.primary.withOpacity(0.5), - ), ); final levelBadge = Container( @@ -184,84 +178,67 @@ class LearningProgressIndicatorsState ), ); - return Stack( - alignment: Alignment.topCenter, + return Row( children: [ - // const Positioned( - // child: PointsGainedAnimation(), - // ), - Column( - mainAxisSize: MainAxisSize.min, - children: [ - Padding( - padding: const EdgeInsets.fromLTRB(46, 0, 32, 4), - child: Row( + const ClientChooserButton(), + const SizedBox(width: 6), + Expanded( + child: Column( + children: [ + Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - FutureBuilder( - future: _pangeaController.matrixState.client - .getProfileFromUserId( - _pangeaController.matrixState.client.userID!, - ), - builder: (context, snapshot) { - final mxid = Matrix.of(context).client.userID ?? - L10n.of(context)!.user; - return Avatar( - name: snapshot.data?.displayName ?? - mxid.localpart ?? - mxid, - mxContent: snapshot.data?.avatarUrl, - size: 40, - ); - }, - ), Row( - mainAxisAlignment: MainAxisAlignment.center, children: ProgressIndicatorEnum.values - .where( - (indicator) => - indicator != ProgressIndicatorEnum.level, - ) + .where((i) => i != ProgressIndicatorEnum.level) .map( - (indicator) => ProgressIndicatorBadge( - points: getProgressPoints(indicator), - onTap: () { - final model = getConstructsModel(indicator); - if (model == null) return; - showDialog( - context: context, - builder: (c) => AnalyticsPopup( - indicator: indicator, - constructsModel: model, - ), - ); - }, - progressIndicator: indicator, - loading: loading, + (indicator) => Padding( + padding: const EdgeInsets.only(right: 10), + child: ProgressIndicatorBadge( + points: getProgressPoints(indicator), + onTap: () { + final model = getConstructsModel(indicator); + if (model == null) return; + showDialog( + context: context, + builder: (c) => AnalyticsPopup( + indicator: indicator, + constructsModel: model, + ), + ); + }, + progressIndicator: indicator, + loading: loading, + ), ), ) .toList(), ), + InkWell( + onTap: () => showDialog( + context: context, + builder: (c) => const SettingsLearning(), + ), + child: LanguageFlag( + language: _pangeaController.languageController.userL2, + size: 24, + ), + ), ], ), - ), - Center( - child: SizedBox( + const SizedBox(height: 6), + SizedBox( height: 36, - child: SizedBox( - width: levelBarWidth + 16, - child: Stack( - alignment: Alignment.center, - children: [ - Positioned(left: 16, right: 0, child: progressBar), - Positioned(left: 0, child: levelBadge), - ], - ), + child: Stack( + alignment: Alignment.center, + children: [ + Positioned(left: 16, right: 0, child: progressBar), + Positioned(left: 0, child: levelBadge), + ], ), ), - ), - const SizedBox(height: 16), - ], + ], + ), ), ], ); diff --git a/lib/pangea/widgets/chat_list/analytics_summary/progress_indicator.dart b/lib/pangea/widgets/chat_list/analytics_summary/progress_indicator.dart index bd24b206f..f05ac634c 100644 --- a/lib/pangea/widgets/chat_list/analytics_summary/progress_indicator.dart +++ b/lib/pangea/widgets/chat_list/analytics_summary/progress_indicator.dart @@ -18,40 +18,34 @@ class ProgressIndicatorBadge extends StatelessWidget { @override Widget build(BuildContext context) { - return Padding( - padding: const EdgeInsets.symmetric(horizontal: 5, vertical: 5), - child: Tooltip( - message: progressIndicator.tooltip(context), - child: InkWell( - onTap: onTap, - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 4), - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - Icon( - progressIndicator.icon, - color: progressIndicator.color(context), - ), - const SizedBox(width: 5), - !loading - ? Text( - points?.toString() ?? '0', - style: const TextStyle( - fontSize: 16, - fontWeight: FontWeight.bold, - ), - ) - : const SizedBox( - height: 8, - width: 8, - child: CircularProgressIndicator.adaptive( - strokeWidth: 2, - ), - ), - ], + return Tooltip( + message: progressIndicator.tooltip(context), + child: InkWell( + onTap: onTap, + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Icon( + progressIndicator.icon, + color: progressIndicator.color(context), ), - ), + const SizedBox(width: 5), + !loading + ? Text( + points?.toString() ?? '0', + style: const TextStyle( + fontSize: 16, + fontWeight: FontWeight.bold, + ), + ) + : const SizedBox( + height: 8, + width: 8, + child: CircularProgressIndicator.adaptive( + strokeWidth: 2, + ), + ), + ], ), ), ); diff --git a/lib/pangea/widgets/chat_list/pangea_chat_list_header.dart b/lib/pangea/widgets/chat_list/pangea_chat_list_header.dart new file mode 100644 index 000000000..5fdec6839 --- /dev/null +++ b/lib/pangea/widgets/chat_list/pangea_chat_list_header.dart @@ -0,0 +1,82 @@ +import 'package:fluffychat/pages/chat_list/chat_list.dart'; +import 'package:fluffychat/pangea/widgets/chat_list/analytics_summary/learning_progress_indicators.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/l10n.dart'; + +class PangeaChatListHeader extends StatelessWidget + implements PreferredSizeWidget { + final ChatListController controller; + final bool globalSearch; + + const PangeaChatListHeader({ + super.key, + required this.controller, + this.globalSearch = true, + }); + + @override + Widget build(BuildContext context) { + final theme = Theme.of(context); + + return SliverList( + delegate: SliverChildListDelegate( + [ + Padding( + padding: const EdgeInsets.only( + top: 16, + left: 16, + right: 16, + ), + child: Column( + children: [ + const LearningProgressIndicators(), + const SizedBox(height: 16), + TextField( + controller: controller.searchController, + focusNode: controller.searchFocusNode, + textInputAction: TextInputAction.search, + onChanged: (text) => controller.onSearchEnter( + text, + globalSearch: globalSearch, + ), + decoration: InputDecoration( + filled: true, + fillColor: theme.colorScheme.secondaryContainer, + border: OutlineInputBorder( + borderSide: BorderSide.none, + borderRadius: BorderRadius.circular(99), + ), + contentPadding: EdgeInsets.zero, + hintText: L10n.of(context)!.searchChatsRooms, + hintStyle: TextStyle( + color: theme.colorScheme.onPrimaryContainer, + fontWeight: FontWeight.normal, + ), + floatingLabelBehavior: FloatingLabelBehavior.never, + prefixIcon: controller.isSearchMode + ? IconButton( + tooltip: L10n.of(context)!.cancel, + icon: const Icon(Icons.close_outlined), + onPressed: controller.cancelSearch, + color: theme.colorScheme.onPrimaryContainer, + ) + : IconButton( + onPressed: controller.startSearch, + icon: Icon( + Icons.search_outlined, + color: theme.colorScheme.onPrimaryContainer, + ), + ), + ), + ), + ], + ), + ), + ], + ), + ); + } + + @override + Size get preferredSize => const Size.fromHeight(56); +} From d9484776a5f2e7494f7a9326127e009411e6b9a9 Mon Sep 17 00:00:00 2001 From: William Jordan-Cooley Date: Fri, 1 Nov 2024 10:59:55 -0400 Subject: [PATCH 10/14] grammar copy additions --- assets/l10n/intl_en.arb | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/assets/l10n/intl_en.arb b/assets/l10n/intl_en.arb index b5310c8e3..1731cadf2 100644 --- a/assets/l10n/intl_en.arb +++ b/assets/l10n/intl_en.arb @@ -4337,6 +4337,24 @@ "grammarCopyCase": "Case", "grammarCopyDefinite": "Definiteness", "grammarCopyNumForm": "Numeral Form", + "grammarCopyAdn": "Adnominal", + "grammarCopyVoc": "Vocative", + "grammarCopyCmpl": "Complementizer", + "grammarCopyAdv": "Adverbial", + "grammarCopyUnknown": "Unknown", + "grammarCopyJus": "Jussive", + "grammarCopyCom": "Common", + "grammarCopyCaus": "Causative", + "grammarCopyAux": "Auxiliary", + "grammarCopyRflx": "Reflexive", + "grammarCopyPar": "Partitive", + "grammarCopySpc": "Specific", + "grammarCopyPqp": "Pluperfect", + "grammarCopyRef": "Reflexive Case", + "grammarCopyShrt": "Short", + "grammarCopyDual": "Dual", + "grammarCopyLng": "Long", + "grammarCopyMid": "Middle Voice", "grammarCopyUnknown": "Unknown", "enterPrompt": "Please enter a system prompt", "selectBotLanguage": "Select bot language", From a2175d5206bcb37c70a519e8b5369a9c5581c59b Mon Sep 17 00:00:00 2001 From: ggurdin Date: Fri, 1 Nov 2024 11:54:00 -0400 Subject: [PATCH 11/14] removed unused context variable from tokensGlobal function --- .../pangea_message_event.dart | 4 +-- .../pangea_representation_event.dart | 3 +-- .../widgets/chat/message_audio_card.dart | 1 - .../widgets/chat/overlay_message_text.dart | 4 +-- .../practice_activity_card.dart | 2 -- .../target_tokens_controller.dart | 26 +++++-------------- 6 files changed, 9 insertions(+), 31 deletions(-) diff --git a/lib/pangea/matrix_event_wrappers/pangea_message_event.dart b/lib/pangea/matrix_event_wrappers/pangea_message_event.dart index 39d1bd314..88bcf7f19 100644 --- a/lib/pangea/matrix_event_wrappers/pangea_message_event.dart +++ b/lib/pangea/matrix_event_wrappers/pangea_message_event.dart @@ -17,7 +17,6 @@ import 'package:fluffychat/pangea/models/tokens_event_content_model.dart'; import 'package:fluffychat/pangea/repo/full_text_translation_repo.dart'; import 'package:fluffychat/pangea/widgets/chat/message_audio_card.dart'; import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; import 'package:matrix/matrix.dart'; import 'package:sentry_flutter/sentry_flutter.dart'; @@ -83,7 +82,6 @@ class PangeaMessageEvent { Future getMatrixAudioFile( String langCode, - BuildContext context, ) async { final RepresentationEvent? rep = representationByLanguage(langCode); @@ -91,7 +89,7 @@ class PangeaMessageEvent { final TextToSpeechRequest params = TextToSpeechRequest( text: rep.content.text, - tokens: (await rep.tokensGlobal(context)).map((t) => t.text).toList(), + tokens: (await rep.tokensGlobal()).map((t) => t.text).toList(), langCode: langCode, userL1: l1Code ?? LanguageKeys.unknownLanguage, userL2: l2Code ?? LanguageKeys.unknownLanguage, diff --git a/lib/pangea/matrix_event_wrappers/pangea_representation_event.dart b/lib/pangea/matrix_event_wrappers/pangea_representation_event.dart index 61d298807..7bd1381a8 100644 --- a/lib/pangea/matrix_event_wrappers/pangea_representation_event.dart +++ b/lib/pangea/matrix_event_wrappers/pangea_representation_event.dart @@ -6,7 +6,6 @@ import 'package:fluffychat/pangea/models/pangea_token_model.dart'; import 'package:fluffychat/pangea/models/token_api_models.dart'; import 'package:fluffychat/pangea/models/tokens_event_content_model.dart'; import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; import 'package:matrix/matrix.dart'; import 'package:matrix/src/utils/markdown.dart'; import 'package:sentry_flutter/sentry_flutter.dart'; @@ -118,7 +117,7 @@ class RepresentationEvent { return _tokens?.tokens; } - Future> tokensGlobal(BuildContext context) async { + Future> tokensGlobal() async { if (tokens != null) return tokens!; if (_event == null) { diff --git a/lib/pangea/widgets/chat/message_audio_card.dart b/lib/pangea/widgets/chat/message_audio_card.dart index 47cb41af8..302dcdef9 100644 --- a/lib/pangea/widgets/chat/message_audio_card.dart +++ b/lib/pangea/widgets/chat/message_audio_card.dart @@ -158,7 +158,6 @@ class MessageAudioCardState extends State { } else { audioFile = await widget.messageEvent.getMatrixAudioFile( langCode, - context, ); } debugPrint("audio file is now: $audioFile. setting starts and ends..."); diff --git a/lib/pangea/widgets/chat/overlay_message_text.dart b/lib/pangea/widgets/chat/overlay_message_text.dart index ababbf37c..40eb566d7 100644 --- a/lib/pangea/widgets/chat/overlay_message_text.dart +++ b/lib/pangea/widgets/chat/overlay_message_text.dart @@ -31,9 +31,7 @@ class OverlayMessageTextState extends State { void initState() { tokens = widget.pangeaMessageEvent.originalSent?.tokens; if (widget.pangeaMessageEvent.originalSent != null && tokens == null) { - widget.pangeaMessageEvent.originalSent! - .tokensGlobal(context) - .then((tokens) { + widget.pangeaMessageEvent.originalSent!.tokensGlobal().then((tokens) { // this isn't currently working because originalSent's _event is null setState(() => this.tokens = tokens); }); diff --git a/lib/pangea/widgets/practice_activity/practice_activity_card.dart b/lib/pangea/widgets/practice_activity/practice_activity_card.dart index 53644494d..ee451d1d9 100644 --- a/lib/pangea/widgets/practice_activity/practice_activity_card.dart +++ b/lib/pangea/widgets/practice_activity/practice_activity_card.dart @@ -141,7 +141,6 @@ class PracticeActivityCardState extends State { userL2: pangeaController.languageController.userL2!.langCode, messageText: widget.pangeaMessageEvent.originalSent!.text, tokensWithXP: await targetTokensController.targetTokens( - context, widget.pangeaMessageEvent, ), messageId: widget.pangeaMessageEvent.eventId, @@ -218,7 +217,6 @@ class PracticeActivityCardState extends State { currentActivity!, metadata, ), - context, widget.pangeaMessageEvent, ); diff --git a/lib/pangea/widgets/practice_activity/target_tokens_controller.dart b/lib/pangea/widgets/practice_activity/target_tokens_controller.dart index 874a90a62..41b3b0402 100644 --- a/lib/pangea/widgets/practice_activity/target_tokens_controller.dart +++ b/lib/pangea/widgets/practice_activity/target_tokens_controller.dart @@ -4,10 +4,8 @@ import 'package:fluffychat/pangea/matrix_event_wrappers/pangea_message_event.dar import 'package:fluffychat/pangea/models/analytics/construct_list_model.dart'; import 'package:fluffychat/pangea/models/analytics/constructs_model.dart'; import 'package:fluffychat/pangea/models/practice_activities.dart/message_activity_request.dart'; -import 'package:fluffychat/pangea/utils/error_handler.dart'; import 'package:fluffychat/widgets/matrix.dart'; import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; /// Seperated out the target tokens from the practice activity card /// in order to control the state of the target tokens @@ -19,20 +17,18 @@ class TargetTokensController { /// From the tokens in the message, do a preliminary filtering of which to target /// Then get the construct uses for those tokens Future> targetTokens( - BuildContext context, PangeaMessageEvent pangeaMessageEvent, ) async { if (_targetTokens != null) { return _targetTokens!; } - _targetTokens = await _initialize(context, pangeaMessageEvent); + _targetTokens = await _initialize(pangeaMessageEvent); + final allConstructs = MatrixState + .pangeaController.analytics.analyticsStream.value?.constructs; await updateTokensWithConstructs( - MatrixState - .pangeaController.analytics.analyticsStream.value?.constructs ?? - [], - context, + allConstructs ?? [], pangeaMessageEvent, ); @@ -40,20 +36,11 @@ class TargetTokensController { } Future> _initialize( - BuildContext context, PangeaMessageEvent pangeaMessageEvent, ) async { - if (!context.mounted) { - ErrorHandler.logError( - m: 'getTargetTokens called when not mounted', - s: StackTrace.current, - ); - return _targetTokens = []; - } - final tokens = await pangeaMessageEvent .representationByLanguage(pangeaMessageEvent.messageDisplayLangCode) - ?.tokensGlobal(context); + ?.tokensGlobal(); if (tokens == null || tokens.isEmpty) { debugger(when: kDebugMode); @@ -66,7 +53,6 @@ class TargetTokensController { Future updateTokensWithConstructs( List constructUses, - context, pangeaMessageEvent, ) async { final ConstructListModel constructList = ConstructListModel( @@ -74,7 +60,7 @@ class TargetTokensController { type: null, ); - _targetTokens ??= await _initialize(context, pangeaMessageEvent); + _targetTokens ??= await _initialize(pangeaMessageEvent); for (final token in _targetTokens!) { // we don't need to do this for tokens that don't have saveVocab set to true From aa2f949e390c7ce34a0a7f242d159e33b934500e Mon Sep 17 00:00:00 2001 From: ggurdin Date: Fri, 1 Nov 2024 11:59:35 -0400 Subject: [PATCH 12/14] don't log error for message sent on or before september 25th without original sent, log senderID and timestamp in error message --- .../matrix_event_wrappers/pangea_message_event.dart | 7 ++++++- .../pangea_representation_event.dart | 9 +++++++-- lib/pangea/widgets/chat/overlay_message_text.dart | 7 ++++++- .../practice_activity/target_tokens_controller.dart | 5 ++++- 4 files changed, 23 insertions(+), 5 deletions(-) diff --git a/lib/pangea/matrix_event_wrappers/pangea_message_event.dart b/lib/pangea/matrix_event_wrappers/pangea_message_event.dart index 88bcf7f19..74d4483dc 100644 --- a/lib/pangea/matrix_event_wrappers/pangea_message_event.dart +++ b/lib/pangea/matrix_event_wrappers/pangea_message_event.dart @@ -89,7 +89,12 @@ class PangeaMessageEvent { final TextToSpeechRequest params = TextToSpeechRequest( text: rep.content.text, - tokens: (await rep.tokensGlobal()).map((t) => t.text).toList(), + tokens: (await rep.tokensGlobal( + senderId, + originServerTs, + )) + .map((t) => t.text) + .toList(), langCode: langCode, userL1: l1Code ?? LanguageKeys.unknownLanguage, userL2: l2Code ?? LanguageKeys.unknownLanguage, diff --git a/lib/pangea/matrix_event_wrappers/pangea_representation_event.dart b/lib/pangea/matrix_event_wrappers/pangea_representation_event.dart index 7bd1381a8..5d4819de9 100644 --- a/lib/pangea/matrix_event_wrappers/pangea_representation_event.dart +++ b/lib/pangea/matrix_event_wrappers/pangea_representation_event.dart @@ -117,16 +117,21 @@ class RepresentationEvent { return _tokens?.tokens; } - Future> tokensGlobal() async { + Future> tokensGlobal( + String senderID, + DateTime timestamp, + ) async { if (tokens != null) return tokens!; - if (_event == null) { + if (_event == null && timestamp.isAfter(DateTime(2024, 9, 25))) { ErrorHandler.logError( m: 'representation with no _event and no tokens got tokens directly. This means an original_sent with no tokens. This should not happen in messages sent after September 25', s: StackTrace.current, data: { 'content': content.toJson(), 'event': _event?.toJson(), + 'timestamp': timestamp, + 'senderID': senderID, }, ); } diff --git a/lib/pangea/widgets/chat/overlay_message_text.dart b/lib/pangea/widgets/chat/overlay_message_text.dart index 40eb566d7..f23cbba5c 100644 --- a/lib/pangea/widgets/chat/overlay_message_text.dart +++ b/lib/pangea/widgets/chat/overlay_message_text.dart @@ -31,7 +31,12 @@ class OverlayMessageTextState extends State { void initState() { tokens = widget.pangeaMessageEvent.originalSent?.tokens; if (widget.pangeaMessageEvent.originalSent != null && tokens == null) { - widget.pangeaMessageEvent.originalSent!.tokensGlobal().then((tokens) { + widget.pangeaMessageEvent.originalSent! + .tokensGlobal( + widget.pangeaMessageEvent.senderId, + widget.pangeaMessageEvent.originServerTs, + ) + .then((tokens) { // this isn't currently working because originalSent's _event is null setState(() => this.tokens = tokens); }); diff --git a/lib/pangea/widgets/practice_activity/target_tokens_controller.dart b/lib/pangea/widgets/practice_activity/target_tokens_controller.dart index 41b3b0402..69be7f6c2 100644 --- a/lib/pangea/widgets/practice_activity/target_tokens_controller.dart +++ b/lib/pangea/widgets/practice_activity/target_tokens_controller.dart @@ -40,7 +40,10 @@ class TargetTokensController { ) async { final tokens = await pangeaMessageEvent .representationByLanguage(pangeaMessageEvent.messageDisplayLangCode) - ?.tokensGlobal(); + ?.tokensGlobal( + pangeaMessageEvent.senderId, + pangeaMessageEvent.originServerTs, + ); if (tokens == null || tokens.isEmpty) { debugger(when: kDebugMode); From 2a94d09968703352b2c4cb0c4952cc5043779508 Mon Sep 17 00:00:00 2001 From: ggurdin Date: Fri, 1 Nov 2024 16:04:53 -0400 Subject: [PATCH 13/14] backwards backwards compatibility for constructs category data --- lib/pangea/models/analytics/constructs_model.dart | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/lib/pangea/models/analytics/constructs_model.dart b/lib/pangea/models/analytics/constructs_model.dart index a9781f9ae..191683140 100644 --- a/lib/pangea/models/analytics/constructs_model.dart +++ b/lib/pangea/models/analytics/constructs_model.dart @@ -104,13 +104,21 @@ class OneConstructUse { : null; debugger(when: kDebugMode && constructType == null); + List categories = []; + final categoriesEntry = json['cat'] ?? json['categories']; + if (categoriesEntry != null) { + if (categoriesEntry is List) { + categories = List.from(categoriesEntry); + } else if (categoriesEntry is String) { + categories = [categoriesEntry]; + } + } + return OneConstructUse( useType: ConstructUseTypeUtil.fromString(json['useType']), lemma: json['lemma'], form: json['form'], - categories: json['categories'] != null - ? List.from(json['categories']) - : [], + categories: categories, constructType: constructType ?? ConstructTypeEnum.vocab, id: json['id'], metadata: ConstructUseMetaData( From dc68790c53242162bf153c9e631ddf641c36073d Mon Sep 17 00:00:00 2001 From: ggurdin Date: Fri, 1 Nov 2024 16:05:16 -0400 Subject: [PATCH 14/14] comment out unused typeahead widget --- lib/pages/chat/input_bar.dart | 180 +++++++++++++++++----------------- 1 file changed, 90 insertions(+), 90 deletions(-) diff --git a/lib/pages/chat/input_bar.dart b/lib/pages/chat/input_bar.dart index 347d68786..818861c2e 100644 --- a/lib/pages/chat/input_bar.dart +++ b/lib/pages/chat/input_bar.dart @@ -8,13 +8,10 @@ import 'package:fluffychat/widgets/mxc_image.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; -import 'package:flutter_typeahead/flutter_typeahead.dart'; import 'package:matrix/matrix.dart'; import 'package:pasteboard/pasteboard.dart'; import 'package:slugify/slugify.dart'; -import '../../widgets/matrix.dart'; - class InputBar extends StatelessWidget { final Room room; final int? minLines; @@ -23,7 +20,7 @@ class InputBar extends StatelessWidget { final TextInputAction? textInputAction; final ValueChanged? onSubmitted; final ValueChanged? onSubmitImage; - final FocusNode? focusNode; + final FocusNode focusNode; // #Pangea // final TextEditingController? controller; final PangeaTextController? controller; @@ -40,7 +37,7 @@ class InputBar extends StatelessWidget { this.keyboardType, this.onSubmitted, this.onSubmitImage, - this.focusNode, + required this.focusNode, this.controller, this.decoration, this.onChanged, @@ -463,100 +460,103 @@ class InputBar extends StatelessWidget { // #Pangea child: CompositedTransformTarget( link: controller!.choreographer.inputLayerLinkAndKey.link, - // Pangea# - child: TypeAheadField>( - direction: VerticalDirection.up, - hideOnEmpty: true, - hideOnLoading: true, + // child: TypeAheadField>( + // direction: VerticalDirection.up, + // hideOnEmpty: true, + // hideOnLoading: true, + // controller: controller, + // focusNode: focusNode, + // hideOnSelect: false, + // debounceDuration: const Duration(milliseconds: 50), + // // show suggestions after 50ms idle time (default is 300) + // // #Pangea + // // key: controller?.choreographer.inputLayerLinkAndKey.key, + // // builder: (context, controller, focusNode) => TextField( + // builder: (context, _, focusNode) => + child: TextField( + key: controller?.choreographer.inputLayerLinkAndKey.key, + enableSuggestions: false, + readOnly: + controller != null && controller!.choreographer.isRunningIT, + // Pangea# controller: controller, focusNode: focusNode, - hideOnSelect: false, - debounceDuration: const Duration(milliseconds: 50), - // show suggestions after 50ms idle time (default is 300) - // #Pangea - key: controller?.choreographer.inputLayerLinkAndKey.key, - // builder: (context, controller, focusNode) => TextField( - builder: (context, _, focusNode) => TextField( - enableSuggestions: false, - readOnly: - controller != null && controller!.choreographer.isRunningIT, - // Pangea# - controller: controller, - focusNode: focusNode, - contentInsertionConfiguration: ContentInsertionConfiguration( - onContentInserted: (KeyboardInsertedContent content) { - final data = content.data; - if (data == null) return; + contentInsertionConfiguration: ContentInsertionConfiguration( + onContentInserted: (KeyboardInsertedContent content) { + final data = content.data; + if (data == null) return; - final file = MatrixFile( - mimeType: content.mimeType, - bytes: data, - name: content.uri.split('/').last, - ); - room.sendFileEvent( - file, - shrinkImageMaxDimension: 1600, - ); - }, - ), - minLines: minLines, - maxLines: maxLines, - keyboardType: keyboardType!, - textInputAction: textInputAction, - autofocus: autofocus!, - inputFormatters: [ - //#Pangea - //LengthLimitingTextInputFormatter((maxPDUSize / 3).floor()), - //setting max character count to 1000 - //after max, nothing else can be typed - LengthLimitingTextInputFormatter(1000), - //Pangea# - ], - onSubmitted: (text) { - // fix for library for now - // it sets the types for the callback incorrectly - onSubmitted!(text); - }, - // #Pangea - style: controller?.exceededMaxLength ?? false - ? const TextStyle(color: Colors.red) - : null, - onTap: () { - controller?.onInputTap( - context, - fNode: focusNode, + final file = MatrixFile( + mimeType: content.mimeType, + bytes: data, + name: content.uri.split('/').last, + ); + room.sendFileEvent( + file, + shrinkImageMaxDimension: 1600, ); }, - // Pangea# - decoration: decoration!, - onChanged: (text) { - // fix for the library for now - // it sets the types for the callback incorrectly - onChanged!(text); - }, - textCapitalization: TextCapitalization.sentences, ), - suggestionsCallback: getSuggestions, - itemBuilder: (c, s) => - buildSuggestion(c, s, Matrix.of(context).client), - onSelected: (Map suggestion) => - insertSuggestion(context, suggestion), - errorBuilder: (BuildContext context, Object? error) => - const SizedBox.shrink(), - loadingBuilder: (BuildContext context) => const SizedBox.shrink(), - // fix loading briefly flickering a dark box - emptyBuilder: (BuildContext context) => const SizedBox - .shrink(), // fix loading briefly showing no suggestions + minLines: minLines, + maxLines: maxLines, + keyboardType: keyboardType!, + textInputAction: textInputAction, + autofocus: autofocus!, + inputFormatters: [ + //#Pangea + //LengthLimitingTextInputFormatter((maxPDUSize / 3).floor()), + //setting max character count to 1000 + //after max, nothing else can be typed + LengthLimitingTextInputFormatter(1000), + //Pangea# + ], + onSubmitted: (text) { + // fix for library for now + // it sets the types for the callback incorrectly + onSubmitted!(text); + }, // #Pangea - // If we ever want to change the suggestion background color - // here is the code for it - // decorationBuilder: (context, child) => Material( - // borderRadius: BorderRadius.circular(AppConfig.borderRadius), - // color: Theme.of(context).colorScheme.surfaceContainerHigh, - // child: child, - // ), + style: controller?.exceededMaxLength ?? false + ? const TextStyle(color: Colors.red) + : null, + onTap: () { + controller?.onInputTap( + context, + fNode: focusNode, + ); + }, // Pangea# + decoration: decoration!, + onChanged: (text) { + // fix for the library for now + // it sets the types for the callback incorrectly + onChanged!(text); + }, + textCapitalization: TextCapitalization.sentences, ), + // #Pangea + // suggestionsCallback: getSuggestions, + // itemBuilder: (c, s) => + // buildSuggestion(c, s, Matrix.of(context).client), + // onSelected: (Map suggestion) => + // insertSuggestion(context, suggestion), + // errorBuilder: (BuildContext context, Object? error) => + // const SizedBox.shrink(), + // loadingBuilder: (BuildContext context) => const SizedBox.shrink(), + // // fix loading briefly flickering a dark box + // emptyBuilder: (BuildContext context) => const SizedBox + // .shrink(), // fix loading briefly showing no suggestions + + // If we ever want to change the suggestion background color + // here is the code for it + // decorationBuilder: (context, child) => Material( + // borderRadius: BorderRadius.circular(AppConfig.borderRadius), + // color: Theme.of(context).colorScheme.surfaceContainerHigh, + // child: child, + // ), + + // ), + // Pangea# ), ), );