diff --git a/lib/pages/chat/events/message_content.dart b/lib/pages/chat/events/message_content.dart index 2ce81888b..c04c5c6d5 100644 --- a/lib/pages/chat/events/message_content.dart +++ b/lib/pages/chat/events/message_content.dart @@ -1,11 +1,5 @@ import 'dart:math'; -import 'package:flutter/material.dart'; - -import 'package:flutter_gen/gen_l10n/l10n.dart'; -import 'package:flutter_linkify/flutter_linkify.dart'; -import 'package:matrix/matrix.dart'; - import 'package:fluffychat/pages/chat/chat.dart'; import 'package:fluffychat/pages/chat/events/video_player.dart'; import 'package:fluffychat/pangea/choreographer/widgets/igc/pangea_rich_text.dart'; @@ -17,6 +11,11 @@ import 'package:fluffychat/pangea/toolbar/widgets/message_selection_overlay.dart import 'package:fluffychat/pangea/toolbar/widgets/message_token_text.dart'; import 'package:fluffychat/pangea/toolbar/widgets/message_toolbar_selection_area.dart'; import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_locals.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/l10n.dart'; +import 'package:flutter_linkify/flutter_linkify.dart'; +import 'package:matrix/matrix.dart'; + import '../../../config/app_config.dart'; import '../../../utils/platform_infos.dart'; import '../../../utils/url_launcher.dart'; @@ -385,7 +384,8 @@ class MessageContent extends StatelessWidget { isHighlighted: (PangeaToken token) => overlayController?.toolbarMode.associatedActivityType != null && - overlayController?.practiceSelection?.hasActivity( + overlayController?.practiceSelection + ?.hasActiveActivityByToken( overlayController! .toolbarMode.associatedActivityType!, token, diff --git a/lib/pangea/practice_activities/practice_activity_model.dart b/lib/pangea/practice_activities/practice_activity_model.dart index 41637db28..4e314ab2a 100644 --- a/lib/pangea/practice_activities/practice_activity_model.dart +++ b/lib/pangea/practice_activities/practice_activity_model.dart @@ -74,6 +74,10 @@ class PracticeActivityModel { return; } + // final ConstructIdentifier? cId = activityType == ActivityTypeEnum.morphId + // ? morphFeature ?= null ? token.getMorphTag(morphFeature) : null + // : choice.form.cId; + if (practiceTarget.record.hasTextResponse(choice.choiceContent) || isComplete) { // the user has already selected this choice @@ -221,31 +225,6 @@ class PracticeActivityModel { callback(); } - bool? wasCorrectChoice(String choice) { - for (final response in practiceTarget.record.responses) { - if (response.text == choice) { - return multipleChoiceContent?.answers - .any((answer) => answer.toLowerCase() == choice.toLowerCase()); - } - } - return null; - } - - /// if null, it means the user has not yet responded with that choice - bool? wasCorrectMatch(PracticeChoice choice) { - for (final response in practiceTarget.record.responses) { - if (response.text == choice.choiceContent && response.isCorrect) { - return true; - } - } - for (final response in practiceTarget.record.responses) { - if (response.text == choice.choiceContent) { - return false; - } - } - return null; - } - PracticeRecord get record => practiceTarget.record; PracticeTarget get practiceTarget => PracticeTarget( diff --git a/lib/pangea/practice_activities/practice_selection.dart b/lib/pangea/practice_activities/practice_selection.dart index 249608cf7..5a1eeaed8 100644 --- a/lib/pangea/practice_activities/practice_selection.dart +++ b/lib/pangea/practice_activities/practice_selection.dart @@ -225,12 +225,12 @@ class PracticeSelection { ); } - bool hasActivity( - ActivityTypeEnum a, [ - PangeaToken? t, + bool hasActiveActivityByToken( + ActivityTypeEnum a, + PangeaToken t, [ MorphFeaturesEnum? morph, ]) => - getSelection(a, t, morph) != null; + getSelection(a, t, morph)?.isCompleteByToken(t, morph) == false; /// Add a message meaning activity to the front of the queue /// And limits to _maxQueueLength activities diff --git a/lib/pangea/practice_activities/practice_target.dart b/lib/pangea/practice_activities/practice_target.dart index 9d743e947..d29a58d79 100644 --- a/lib/pangea/practice_activities/practice_target.dart +++ b/lib/pangea/practice_activities/practice_target.dart @@ -1,10 +1,14 @@ -import 'package:flutter/foundation.dart'; +import 'dart:developer'; +import 'package:fluffychat/pangea/common/utils/error_handler.dart'; +import 'package:fluffychat/pangea/constructs/construct_identifier.dart'; import 'package:fluffychat/pangea/events/models/pangea_token_model.dart'; import 'package:fluffychat/pangea/morphs/morph_features_enum.dart'; import 'package:fluffychat/pangea/practice_activities/activity_type_enum.dart'; +import 'package:fluffychat/pangea/practice_activities/practice_choice.dart'; import 'package:fluffychat/pangea/practice_activities/practice_record.dart'; import 'package:fluffychat/pangea/practice_activities/practice_record_repo.dart'; +import 'package:flutter/foundation.dart'; /// Picks which tokens to do activities on and what types of activities to do /// Caches result so that we don't have to recompute it @@ -94,4 +98,49 @@ class PracticeTarget { .any((res) => res.cId == t.vocabConstructID && res.isCorrect), ); } + + bool isCompleteByToken(PangeaToken token, [MorphFeaturesEnum? morph]) { + final ConstructIdentifier? cId = + morph == null ? token.vocabConstructID : token.morphIdByFeature(morph); + if (cId == null) { + debugger(when: kDebugMode); + ErrorHandler.logError( + m: "isCompleteByToken: cId is null for token ${token.text.content}", + data: { + "t": token.toJson(), + "morph": morph?.name, + }, + ); + return false; + } + return record.responses.any( + (res) => res.cId == token.vocabConstructID && res.isCorrect, + ); + } + + bool? wasCorrectChoice(String choice) { + for (final response in record.responses) { + if (response.text == choice) { + return response.isCorrect; + } + } + return null; + } + + /// if any of the choices were correct, return true + /// if all of the choices were incorrect, return false + /// if null, it means the user has not yet responded with that choice + bool? wasCorrectMatch(PracticeChoice choice) { + for (final response in record.responses) { + if (response.text == choice.choiceContent && response.isCorrect) { + return true; + } + } + for (final response in record.responses) { + if (response.text == choice.choiceContent) { + return false; + } + } + return null; + } } diff --git a/lib/pangea/toolbar/reading_assistance_input_row/message_morph_choice.dart b/lib/pangea/toolbar/reading_assistance_input_row/message_morph_choice.dart index 3561a7397..bad728095 100644 --- a/lib/pangea/toolbar/reading_assistance_input_row/message_morph_choice.dart +++ b/lib/pangea/toolbar/reading_assistance_input_row/message_morph_choice.dart @@ -1,8 +1,4 @@ -import 'package:flutter/material.dart'; - import 'package:collection/collection.dart'; -import 'package:flutter_gen/gen_l10n/l10n.dart'; - import 'package:fluffychat/pangea/analytics_misc/construct_type_enum.dart'; import 'package:fluffychat/pangea/choreographer/widgets/choice_animation.dart'; import 'package:fluffychat/pangea/constructs/construct_form.dart'; @@ -15,6 +11,8 @@ import 'package:fluffychat/pangea/practice_activities/practice_activity_model.da import 'package:fluffychat/pangea/practice_activities/practice_choice.dart'; import 'package:fluffychat/pangea/toolbar/reading_assistance_input_row/message_morph_choice_item.dart'; import 'package:fluffychat/pangea/toolbar/widgets/message_selection_overlay.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/l10n.dart'; // this widget will handle the content of the input bar when mode == MessageMode.wordMorph @@ -111,42 +109,45 @@ class MessageMorphInputBarContentState runAlignment: WrapAlignment.center, spacing: spacing, runSpacing: spacing, - children: widget.activity.multipleChoiceContent!.choices - .mapIndexed( - (index, choice) => ChoiceAnimationWidget( - isSelected: selectedTag == choice, - isCorrect: widget.activity.wasCorrectChoice(choice) ?? false, - child: MessageMorphChoiceItem( - cId: ConstructIdentifier( - lemma: choice, - type: ConstructTypeEnum.morph, - category: morph.name, - ), - onTap: () { - setState(() => selectedTag = choice); + children: widget.activity.multipleChoiceContent!.choices.mapIndexed( + (index, choice) { + final wasCorrect = + widget.activity.practiceTarget.wasCorrectChoice(choice); - widget.activity.onMultipleChoiceSelect( - token, - PracticeChoice( - choiceContent: choice, - form: ConstructForm( - cId: widget.activity.targetTokens.first - .morphIdByFeature( - widget.activity.morphFeature!, - )!, - form: token.text.content, - ), - ), - widget.pangeaMessageEvent, - () => overlay.setState(() {}), - ); - }, - isSelected: selectedTag == choice, - isGold: widget.activity.wasCorrectChoice(choice), + return ChoiceAnimationWidget( + isSelected: selectedTag == choice, + isCorrect: wasCorrect, + child: MessageMorphChoiceItem( + cId: ConstructIdentifier( + lemma: choice, + type: ConstructTypeEnum.morph, + category: morph.name, ), + onTap: () { + setState(() => selectedTag = choice); + + widget.activity.onMultipleChoiceSelect( + token, + PracticeChoice( + choiceContent: choice, + form: ConstructForm( + cId: widget.activity.targetTokens.first + .morphIdByFeature( + widget.activity.morphFeature!, + )!, + form: token.text.content, + ), + ), + widget.pangeaMessageEvent, + () => overlay.setState(() {}), + ); + }, + isSelected: selectedTag == choice, + isGold: wasCorrect, ), - ) - .toList(), + ); + }, + ).toList(), ), // SizedBox( // height: 50, diff --git a/lib/pangea/toolbar/reading_assistance_input_row/practice_match_card.dart b/lib/pangea/toolbar/reading_assistance_input_row/practice_match_card.dart index 4c950ea93..b8f69b053 100644 --- a/lib/pangea/toolbar/reading_assistance_input_row/practice_match_card.dart +++ b/lib/pangea/toolbar/reading_assistance_input_row/practice_match_card.dart @@ -83,12 +83,14 @@ class MatchActivityCard extends StatelessWidget { runSpacing: 4.0, children: activity.matchContent!.choices.map( (PracticeChoice cf) { + final bool? wasCorrect = + currentActivity.practiceTarget.wasCorrectMatch(cf); return ChoiceAnimationWidget( isSelected: overlayController.selectedChoice == cf, - isCorrect: currentActivity.wasCorrectMatch(cf), + isCorrect: wasCorrect, child: PracticeMatchItem( isSelected: overlayController.selectedChoice == cf, - isCorrect: currentActivity.wasCorrectMatch(cf), + isCorrect: wasCorrect, constructForm: cf, content: choiceDisplayContent(cf.choiceContent, fontSize), audioContent: diff --git a/lib/pangea/toolbar/widgets/message_selection_overlay.dart b/lib/pangea/toolbar/widgets/message_selection_overlay.dart index 0b9d048e8..b185f0083 100644 --- a/lib/pangea/toolbar/widgets/message_selection_overlay.dart +++ b/lib/pangea/toolbar/widgets/message_selection_overlay.dart @@ -1,13 +1,7 @@ import 'dart:async'; import 'dart:developer'; -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter/scheduler.dart'; - import 'package:collection/collection.dart'; -import 'package:matrix/matrix.dart'; - import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/pages/chat/chat.dart'; import 'package:fluffychat/pangea/common/utils/error_handler.dart'; @@ -31,6 +25,10 @@ import 'package:fluffychat/pangea/toolbar/reading_assistance_input_row/morph_sel import 'package:fluffychat/pangea/toolbar/widgets/message_selection_positioner.dart'; import 'package:fluffychat/pangea/toolbar/widgets/reading_assistance_content.dart'; import 'package:fluffychat/widgets/matrix.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/scheduler.dart'; +import 'package:matrix/matrix.dart'; /// Controls data at the top level of the toolbar (mainly token / toolbar mode selection) class MessageSelectionOverlay extends StatefulWidget { @@ -503,7 +501,7 @@ class MessageOverlayController extends State } /// we don't want to associate the audio with the text in this mode - if (practiceSelection?.hasActivity( + if (practiceSelection?.hasActiveActivityByToken( ActivityTypeEnum.wordFocusListening, token, ) == diff --git a/lib/pangea/toolbar/widgets/word_zoom/lemma_meaning_widget.dart b/lib/pangea/toolbar/widgets/word_zoom/lemma_meaning_widget.dart index 13d650ba1..8224d0f01 100644 --- a/lib/pangea/toolbar/widgets/word_zoom/lemma_meaning_widget.dart +++ b/lib/pangea/toolbar/widgets/word_zoom/lemma_meaning_widget.dart @@ -1,11 +1,5 @@ import 'dart:developer'; -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; - -import 'package:flutter_gen/gen_l10n/l10n.dart'; -import 'package:material_symbols_icons/symbols.dart'; - import 'package:fluffychat/pangea/analytics_misc/construct_use_model.dart'; import 'package:fluffychat/pangea/analytics_misc/text_loading_shimmer.dart'; import 'package:fluffychat/pangea/events/models/pangea_token_model.dart'; @@ -18,6 +12,10 @@ import 'package:fluffychat/pangea/toolbar/enums/message_mode_enum.dart'; import 'package:fluffychat/pangea/toolbar/widgets/message_selection_overlay.dart'; import 'package:fluffychat/pangea/toolbar/widgets/practice_activity/word_zoom_activity_button.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:material_symbols_icons/symbols.dart'; class LemmaMeaningWidget extends StatefulWidget { final ConstructUses constructUse; @@ -121,8 +119,10 @@ class LemmaMeaningWidgetState extends State { Widget build(BuildContext context) { if (widget.token != null && widget.controller?.practiceSelection != null && - widget.controller!.practiceSelection! - .hasActivity(ActivityTypeEnum.wordMeaning, widget.token!)) { + widget.controller!.practiceSelection!.hasActiveActivityByToken( + ActivityTypeEnum.wordMeaning, + widget.token!, + )) { return WordZoomActivityButton( icon: const Icon(Symbols.dictionary), isSelected: widget.controller?.toolbarMode == MessageMode.wordMeaning, diff --git a/lib/pangea/toolbar/widgets/word_zoom/lemma_widget.dart b/lib/pangea/toolbar/widgets/word_zoom/lemma_widget.dart index 706a8bf8e..a9cc0303d 100644 --- a/lib/pangea/toolbar/widgets/word_zoom/lemma_widget.dart +++ b/lib/pangea/toolbar/widgets/word_zoom/lemma_widget.dart @@ -191,15 +191,15 @@ class LemmaWidgetState extends State { isSelected: MessageMode.listening == widget.overlayController?.toolbarMode, baseOpacity: 0.4, - callbackOverride: - widget.overlayController?.practiceSelection?.hasActivity( - MessageMode.listening.associatedActivityType!, - widget.token, - ) == - true - ? () => widget.overlayController - ?.updateToolbarMode(MessageMode.listening) - : null, + callbackOverride: widget.overlayController?.practiceSelection + ?.hasActiveActivityByToken( + MessageMode.listening.associatedActivityType!, + widget.token, + ) == + true + ? () => widget.overlayController + ?.updateToolbarMode(MessageMode.listening) + : null, uniqueID: "lemma-content-${widget.token.text.content}", ), ], diff --git a/lib/pangea/toolbar/widgets/word_zoom/morphological_list_item.dart b/lib/pangea/toolbar/widgets/word_zoom/morphological_list_item.dart index 845e14bde..ca3ed8a90 100644 --- a/lib/pangea/toolbar/widgets/word_zoom/morphological_list_item.dart +++ b/lib/pangea/toolbar/widgets/word_zoom/morphological_list_item.dart @@ -1,7 +1,3 @@ -import 'package:flutter/material.dart'; - -import 'package:material_symbols_icons/symbols.dart'; - import 'package:fluffychat/pangea/events/models/pangea_token_model.dart'; import 'package:fluffychat/pangea/morphs/get_grammar_copy.dart'; import 'package:fluffychat/pangea/morphs/morph_features_enum.dart'; @@ -11,6 +7,8 @@ import 'package:fluffychat/pangea/toolbar/enums/message_mode_enum.dart'; import 'package:fluffychat/pangea/toolbar/reading_assistance_input_row/morph_selection.dart'; import 'package:fluffychat/pangea/toolbar/widgets/message_selection_overlay.dart'; import 'package:fluffychat/pangea/toolbar/widgets/practice_activity/word_zoom_activity_button.dart'; +import 'package:flutter/material.dart'; +import 'package:material_symbols_icons/symbols.dart'; class MorphologicalListItem extends StatelessWidget { final MorphFeaturesEnum morphFeature; @@ -25,7 +23,7 @@ class MorphologicalListItem extends StatelessWidget { }); bool get shouldDoActivity => - overlayController.practiceSelection?.hasActivity( + overlayController.practiceSelection?.hasActiveActivityByToken( ActivityTypeEnum.morphId, token, morphFeature, diff --git a/lib/pangea/toolbar/widgets/word_zoom/word_zoom_widget.dart b/lib/pangea/toolbar/widgets/word_zoom/word_zoom_widget.dart index f167b146d..ee58118f7 100644 --- a/lib/pangea/toolbar/widgets/word_zoom/word_zoom_widget.dart +++ b/lib/pangea/toolbar/widgets/word_zoom/word_zoom_widget.dart @@ -1,5 +1,3 @@ -import 'package:flutter/material.dart'; - import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/pangea/analytics_details_popup/analytics_details_popup.dart'; import 'package:fluffychat/pangea/analytics_misc/construct_type_enum.dart'; @@ -18,6 +16,7 @@ import 'package:fluffychat/pangea/toolbar/widgets/word_zoom/lemma_meaning_widget import 'package:fluffychat/pangea/toolbar/widgets/word_zoom/lemma_widget.dart'; import 'package:fluffychat/pangea/toolbar/widgets/word_zoom/morphological_list_item.dart'; import 'package:fluffychat/widgets/matrix.dart'; +import 'package:flutter/material.dart'; class WordZoomWidget extends StatelessWidget { final PangeaToken token; @@ -38,7 +37,7 @@ class WordZoomWidget extends StatelessWidget { void onEditDone() => overlayController.initializeTokensAndMode(); bool get hasEmojiActivity => - overlayController.practiceSelection?.hasActivity( + overlayController.practiceSelection?.hasActiveActivityByToken( ActivityTypeEnum.emoji, _selectedToken, ) == @@ -170,15 +169,15 @@ class WordZoomWidget extends StatelessWidget { isSelected: MessageMode.listening == overlayController.toolbarMode, baseOpacity: 0.4, - callbackOverride: - overlayController.practiceSelection?.hasActivity( - MessageMode.listening.associatedActivityType!, - _selectedToken, - ) == - true - ? () => overlayController - .updateToolbarMode(MessageMode.listening) - : null, + callbackOverride: overlayController.practiceSelection + ?.hasActiveActivityByToken( + MessageMode.listening.associatedActivityType!, + _selectedToken, + ) == + true + ? () => overlayController + .updateToolbarMode(MessageMode.listening) + : null, uniqueID: "word-zoom-audio-${_selectedToken.text.content}", ), ],