diff --git a/lib/pangea/controllers/message_analytics_controller.dart b/lib/pangea/controllers/message_analytics_controller.dart index d89ef3ef8..46e656dc1 100644 --- a/lib/pangea/controllers/message_analytics_controller.dart +++ b/lib/pangea/controllers/message_analytics_controller.dart @@ -33,24 +33,13 @@ class MessageAnalyticsEntry { .map((token) => TokenWithXP(token: token)) .toList(); - computeTargetTypesForMessage(); + updateTargetTypesForMessage(); } List get tokensThatCanBeHeard => tokensWithXp.where((t) => t.token.canBeHeard).toList(); - // compute target tokens within async wrapper that adds a 250ms delay - // to avoid blocking the UI thread - Future computeTargetTypesForMessageAsync() async { - await Future.delayed(const Duration(milliseconds: 250)); - computeTargetTypesForMessage(); - } - - void computeTargetTypesForMessage() { - // reset - nextActivityToken = null; - nextActivityType = null; - + void updateTokenTargetTypes() { // compute target types for each token for (final token in tokensWithXp) { token.targetTypes = []; @@ -81,26 +70,39 @@ class MessageAnalyticsEntry { token.targetTypes.add(ActivityTypeEnum.hiddenWordListening); } } + } + + /// Updates the target types for each token in the message and the next + /// activity token and type. Called before requesting the next new activity. + void updateTargetTypesForMessage() { + // reset + nextActivityToken = null; + nextActivityType = null; + updateTokenTargetTypes(); - // from the tokens with hiddenWordListening in targetTypes, pick one at random - final List withListening = tokensWithXp + // From the tokens with hiddenWordListening in targetTypes, pick one at random. + // Create a list of token indicies with hiddenWordListening type available. + final List withHiddenWordIndices = tokensWithXp .asMap() .entries .where( - (entry) => entry.value.targetTypes - .contains(ActivityTypeEnum.hiddenWordListening), + (entry) => entry.value.targetTypes.contains( + ActivityTypeEnum.hiddenWordListening, + ), ) .map((entry) => entry.key) .toList(); - // randomly pick one entry in the list - if (withListening.isNotEmpty) { + + // randomly pick one index in the list and set the next activity + if (withHiddenWordIndices.isNotEmpty) { final int randomIndex = - withListening[Random().nextInt(withListening.length)]; + withHiddenWordIndices[Random().nextInt(withHiddenWordIndices.length)]; nextActivityToken = tokensWithXp[randomIndex]; nextActivityType = ActivityTypeEnum.hiddenWordListening; - // remove from all other tokens + // remove hiddenWord type from all other tokens + // there can only be one hidden word activity for a message for (int i = 0; i < tokensWithXp.length; i++) { if (i != randomIndex) { tokensWithXp[i] diff --git a/lib/pangea/widgets/practice_activity/practice_activity_card.dart b/lib/pangea/widgets/practice_activity/practice_activity_card.dart index 3b616577f..2d32e036c 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/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'; @@ -53,10 +54,11 @@ class PracticeActivityCardState extends State { List get practiceActivities => widget.pangeaMessageEvent.practiceActivities; - PracticeActivityEvent? get existingActivityMatchingNeeds { - final messageAnalyticsEntry = pangeaController.getAnalytics.perMessage - .get(widget.pangeaMessageEvent, false); + MessageAnalyticsEntry? get messageAnalyticsEntry => + MatrixState.pangeaController.getAnalytics.perMessage + .get(widget.pangeaMessageEvent, false); + PracticeActivityEvent? get existingActivityMatchingNeeds { if (messageAnalyticsEntry?.nextActivityToken == null) { debugger(when: kDebugMode); return null; @@ -67,7 +69,7 @@ class PracticeActivityCardState extends State { if (existingActivity.practiceActivity.tgtConstructs .any((tc) => tc == c.id) && existingActivity.practiceActivity.activityType == - messageAnalyticsEntry.nextActivityType) { + messageAnalyticsEntry!.nextActivityType) { debugPrint('found existing activity'); return existingActivity; } @@ -163,9 +165,6 @@ class PracticeActivityCardState extends State { return null; } - final messageAnalyticsEntry = pangeaController.getAnalytics.perMessage - .get(widget.pangeaMessageEvent, false); - // the client is going to be choosing the next activity now // if nothing is set then it must be done with practice if (messageAnalyticsEntry?.nextActivityToken == null || @@ -206,8 +205,8 @@ class PracticeActivityCardState extends State { : ActivityTypeEnum.values .where((type) => type != ActivityTypeEnum.wordFocusListening) .toList(), - clientTokenRequest: messageAnalyticsEntry.nextActivityToken!, - clientTypeRequest: messageAnalyticsEntry.nextActivityType!, + clientTokenRequest: messageAnalyticsEntry!.nextActivityToken!, + clientTypeRequest: messageAnalyticsEntry!.nextActivityType!, ), widget.pangeaMessageEvent, ); @@ -277,13 +276,7 @@ class PracticeActivityCardState extends State { // previously we would update the tokens with the constructs // now the tokens themselves calculate their own points using the analytics // we're going to see if this creates performance issues - final messageAnalytics = MatrixState - .pangeaController.getAnalytics.perMessage - .get(widget.pangeaMessageEvent, false); - // messageAnalytics will only be null if there are no tokens to update - - // set the target types for the next activity - messageAnalytics!.computeTargetTypesForMessageAsync(); + messageAnalyticsEntry?.updateTargetTypesForMessage(); widget.overlayController.onActivityFinish(); pangeaController.activityRecordController.completeActivity(