From c82ce3412486ee1ac94194305a5f50a7d2757da0 Mon Sep 17 00:00:00 2001 From: William Jordan-Cooley Date: Fri, 15 Nov 2024 16:09:36 -0500 Subject: [PATCH] using queue and simplifying selected token for activity flow --- .../message_analytics_controller.dart | 25 +++++++++--- .../chat/message_selection_overlay.dart | 39 +++++++++---------- .../practice_activity_card.dart | 32 ++------------- 3 files changed, 41 insertions(+), 55 deletions(-) diff --git a/lib/pangea/controllers/message_analytics_controller.dart b/lib/pangea/controllers/message_analytics_controller.dart index 33459e6c5..450d5d49a 100644 --- a/lib/pangea/controllers/message_analytics_controller.dart +++ b/lib/pangea/controllers/message_analytics_controller.dart @@ -127,15 +127,28 @@ class MessageAnalyticsEntry { return queue.take(3).toList(); } - /// Removes the last activity from the queue - /// This should only used when there is a startingToken in practice flow - /// and we want to go down to 2 activities + the activity with the startingToken - void goDownTo2Activities() { - if (_activityQueue.isNotEmpty && _activityQueue.length > 2) { - _activityQueue.removeLast(); + /// Adds a word focus listening activity to the front of the queue + /// And limits to 3 activities + void addForWordMeaning(PangeaToken selectedToken) { + _activityQueue.insert( + 0, + TargetTokensAndActivityType( + tokens: [selectedToken], + activityType: ActivityTypeEnum.wordMeaning, + ), + ); + // remove down to three activities + if (_activityQueue.length > 3) { + _activityQueue.removeRange(3, _activityQueue.length); } } + int get numActivities => _activityQueue.length; + + void clearActivityQueue() { + _activityQueue.clear(); + } + /// Returns a hidden word activity if there is a sequence of tokens that have hiddenWordListening in their eligibleActivityTypes TargetTokensAndActivityType? getHiddenWordActivity(int numOtherActivities) { // don't do hidden word listening on own messages diff --git a/lib/pangea/widgets/chat/message_selection_overlay.dart b/lib/pangea/widgets/chat/message_selection_overlay.dart index 6254db276..412edc045 100644 --- a/lib/pangea/widgets/chat/message_selection_overlay.dart +++ b/lib/pangea/widgets/chat/message_selection_overlay.dart @@ -65,7 +65,6 @@ class MessageOverlayController extends State /// The number of activities that need to be completed before the toolbar is unlocked /// If we don't have any good activities for them, we'll decrease this number static const int neededActivities = 3; - int activitiesLeftToComplete = neededActivities; bool get messageInUserL2 => pangeaMessageEvent.messageDisplayLangCode == @@ -121,9 +120,6 @@ class MessageOverlayController extends State _getTokens(); - activitiesLeftToComplete = activitiesLeftToComplete - - widget._pangeaMessageEvent.numberOfActivitiesCompleted; - _reactionSubscription = widget.chatController.room.client.onSync.stream.where( (update) { @@ -146,7 +142,12 @@ class MessageOverlayController extends State tts.setupTTS(); - _setInitialToolbarModeAndSelectedSpan(); + if (selectedTargetTokenForWordMeaning != null) { + messageAnalyticsEntry + ?.addForWordMeaning(selectedTargetTokenForWordMeaning!); + } + + _setInitialToolbarMode(); } MessageAnalyticsEntry? get messageAnalyticsEntry => tokens != null @@ -171,7 +172,7 @@ class MessageOverlayController extends State .then((tokens) { // this isn't currently working because originalSent's _event is null this.tokens = tokens; - _setInitialToolbarModeAndSelectedSpan(); + _setInitialToolbarMode(); }); } } @@ -209,13 +210,13 @@ class MessageOverlayController extends State } } + int get activitiesLeftToComplete => messageAnalyticsEntry?.numActivities ?? 0; bool get isPracticeComplete => activitiesLeftToComplete <= 0; /// When an activity is completed, we need to update the state /// and check if the toolbar should be unlocked void onActivityFinish() { if (!mounted) return; - activitiesLeftToComplete -= 1; clearSelection(); setState(() {}); } @@ -223,29 +224,25 @@ class MessageOverlayController extends State /// In some cases, we need to exit the practice flow and let the user /// interact with the toolbar without completing activities void exitPracticeFlow() { + messageAnalyticsEntry?.clearActivityQueue(); clearSelection(); - activitiesLeftToComplete = 0; setState(() {}); } - Future _setInitialToolbarModeAndSelectedSpan() async { + Future _setInitialToolbarMode() async { if (widget._pangeaMessageEvent.isAudioMessage) { toolbarMode = MessageMode.speechToText; return setState(() => toolbarMode = MessageMode.practiceActivity); } - // we're only going to do activities if we have tokens for the message - if (tokens != null) { - // if the user selects a span on initialization, then we want to give - // them a practice activity on that word - if (selectedTargetTokenForWordMeaning != null) { - _selectedSpan = selectedTargetTokenForWordMeaning?.text; - return setState(() => toolbarMode = MessageMode.practiceActivity); - } - - if (activitiesLeftToComplete > 0) { - return setState(() => toolbarMode = MessageMode.practiceActivity); - } + // 1) we're only going to do activities if we have tokens for the message + // 2) if the user selects a span on initialization, then we want to give + // them a practice activity on that word + // 3) if the user has activities left to complete, then we want to give them + if (tokens != null && + (selectedTargetTokenForWordMeaning != null || + activitiesLeftToComplete > 0)) { + return setState(() => toolbarMode = MessageMode.practiceActivity); } // Note: this setting is now hidden so this will always be false diff --git a/lib/pangea/widgets/practice_activity/practice_activity_card.dart b/lib/pangea/widgets/practice_activity/practice_activity_card.dart index e84f57566..38a910c0c 100644 --- a/lib/pangea/widgets/practice_activity/practice_activity_card.dart +++ b/lib/pangea/widgets/practice_activity/practice_activity_card.dart @@ -2,7 +2,6 @@ import 'dart:async'; import 'dart:developer'; import 'package:collection/collection.dart'; -import 'package:fluffychat/pangea/controllers/message_analytics_controller.dart'; import 'package:fluffychat/pangea/controllers/pangea_controller.dart'; import 'package:fluffychat/pangea/controllers/practice_activity_generation_controller.dart'; import 'package:fluffychat/pangea/controllers/put_analytics_controller.dart'; @@ -10,7 +9,6 @@ import 'package:fluffychat/pangea/enum/activity_type_enum.dart'; import 'package:fluffychat/pangea/matrix_event_wrappers/pangea_message_event.dart'; import 'package:fluffychat/pangea/matrix_event_wrappers/practice_activity_event.dart'; import 'package:fluffychat/pangea/models/analytics/constructs_model.dart'; -import 'package:fluffychat/pangea/models/pangea_token_model.dart'; import 'package:fluffychat/pangea/models/practice_activities.dart/message_activity_request.dart'; import 'package:fluffychat/pangea/models/practice_activities.dart/practice_activity_model.dart'; import 'package:fluffychat/pangea/models/practice_activities.dart/practice_activity_record_model.dart'; @@ -55,9 +53,6 @@ class PracticeActivityCardState extends State { List get practiceActivities => widget.pangeaMessageEvent.practiceActivities; - // if the user has selected a token, we're going to give them an activity on that token first - late PangeaToken? startingToken; - // Used to show an animation when the user completes an activity // while simultaneously fetching a new activity and not showing the loading spinner // until the appropriate time has passed to 'savor the joy' @@ -97,7 +92,6 @@ class PracticeActivityCardState extends State { /// Get an existing activity if there is one. /// If not, get a new activity from the server. Future initialize() async { - startingToken = widget.overlayController.selectedTargetTokenForWordMeaning; _setPracticeActivity( await _fetchActivity(), ); @@ -120,26 +114,8 @@ class PracticeActivityCardState extends State { return null; } - // if the user selected a token which is not already in a hidden word activity, - // we're going to give them an activity on that token first - // otherwise, we're going to give them an activity on the next token in the queue - TargetTokensAndActivityType? nextActivitySpecs; - if (startingToken != null) { - // if the user selected a token, we're going to give them an activity on that token first - nextActivitySpecs = TargetTokensAndActivityType( - tokens: [startingToken!], - activityType: ActivityTypeEnum.wordMeaning, - ); - // clear the starting token so that the next activity is not based on it - startingToken = null; - // we want to go down to 2 activities + the activity with the startingToken - // so we remove the last activity from the queue if there's more than 2 - widget.overlayController.messageAnalyticsEntry?.goDownTo2Activities(); - } else { - nextActivitySpecs = - widget.overlayController.messageAnalyticsEntry?.nextActivity; - } - + final nextActivitySpecs = + widget.overlayController.messageAnalyticsEntry?.nextActivity; // the client is going to be choosing the next activity now // if nothing is set then it must be done with practice if (nextActivitySpecs == null) { @@ -151,7 +127,7 @@ class PracticeActivityCardState extends State { // check if we already have an activity matching the specs final existingActivity = practiceActivities.firstWhereOrNull( (activity) => - nextActivitySpecs!.matchesActivity(activity.practiceActivity), + nextActivitySpecs.matchesActivity(activity.practiceActivity), ); if (existingActivity != null) { debugPrint('found existing activity'); @@ -160,7 +136,7 @@ class PracticeActivityCardState extends State { } debugPrint( - "client requesting ${nextActivitySpecs.activityType.string} for ${nextActivitySpecs.tokens.map((t) => t.text).join(' ')}", + "client requesting ${nextActivitySpecs.activityType.string} for: ${nextActivitySpecs.tokens.map((t) => t.text.content).join(' ')}", ); final PracticeActivityModelResponse? activityResponse =