fix(reading_assistance): register end of activity

pull/1817/head
wcjord 8 months ago
parent 0321d20f44
commit 32ab738477

@ -1,11 +1,5 @@
import 'dart:math'; 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/chat.dart';
import 'package:fluffychat/pages/chat/events/video_player.dart'; import 'package:fluffychat/pages/chat/events/video_player.dart';
import 'package:fluffychat/pangea/choreographer/widgets/igc/pangea_rich_text.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_token_text.dart';
import 'package:fluffychat/pangea/toolbar/widgets/message_toolbar_selection_area.dart'; import 'package:fluffychat/pangea/toolbar/widgets/message_toolbar_selection_area.dart';
import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_locals.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 '../../../config/app_config.dart';
import '../../../utils/platform_infos.dart'; import '../../../utils/platform_infos.dart';
import '../../../utils/url_launcher.dart'; import '../../../utils/url_launcher.dart';
@ -385,7 +384,8 @@ class MessageContent extends StatelessWidget {
isHighlighted: (PangeaToken token) => isHighlighted: (PangeaToken token) =>
overlayController?.toolbarMode.associatedActivityType != overlayController?.toolbarMode.associatedActivityType !=
null && null &&
overlayController?.practiceSelection?.hasActivity( overlayController?.practiceSelection
?.hasActiveActivityByToken(
overlayController! overlayController!
.toolbarMode.associatedActivityType!, .toolbarMode.associatedActivityType!,
token, token,

@ -74,6 +74,10 @@ class PracticeActivityModel {
return; return;
} }
// final ConstructIdentifier? cId = activityType == ActivityTypeEnum.morphId
// ? morphFeature ?= null ? token.getMorphTag(morphFeature) : null
// : choice.form.cId;
if (practiceTarget.record.hasTextResponse(choice.choiceContent) || if (practiceTarget.record.hasTextResponse(choice.choiceContent) ||
isComplete) { isComplete) {
// the user has already selected this choice // the user has already selected this choice
@ -221,31 +225,6 @@ class PracticeActivityModel {
callback(); 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; PracticeRecord get record => practiceTarget.record;
PracticeTarget get practiceTarget => PracticeTarget( PracticeTarget get practiceTarget => PracticeTarget(

@ -225,12 +225,12 @@ class PracticeSelection {
); );
} }
bool hasActivity( bool hasActiveActivityByToken(
ActivityTypeEnum a, [ ActivityTypeEnum a,
PangeaToken? t, PangeaToken t, [
MorphFeaturesEnum? morph, 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 /// Add a message meaning activity to the front of the queue
/// And limits to _maxQueueLength activities /// And limits to _maxQueueLength activities

@ -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/events/models/pangea_token_model.dart';
import 'package:fluffychat/pangea/morphs/morph_features_enum.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/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.dart';
import 'package:fluffychat/pangea/practice_activities/practice_record_repo.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 /// 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 /// 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), .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;
}
} }

@ -1,8 +1,4 @@
import 'package:flutter/material.dart';
import 'package:collection/collection.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/analytics_misc/construct_type_enum.dart';
import 'package:fluffychat/pangea/choreographer/widgets/choice_animation.dart'; import 'package:fluffychat/pangea/choreographer/widgets/choice_animation.dart';
import 'package:fluffychat/pangea/constructs/construct_form.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/practice_activities/practice_choice.dart';
import 'package:fluffychat/pangea/toolbar/reading_assistance_input_row/message_morph_choice_item.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: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 // this widget will handle the content of the input bar when mode == MessageMode.wordMorph
@ -111,42 +109,45 @@ class MessageMorphInputBarContentState
runAlignment: WrapAlignment.center, runAlignment: WrapAlignment.center,
spacing: spacing, spacing: spacing,
runSpacing: spacing, runSpacing: spacing,
children: widget.activity.multipleChoiceContent!.choices children: widget.activity.multipleChoiceContent!.choices.mapIndexed(
.mapIndexed( (index, choice) {
(index, choice) => ChoiceAnimationWidget( final wasCorrect =
isSelected: selectedTag == choice, widget.activity.practiceTarget.wasCorrectChoice(choice);
isCorrect: widget.activity.wasCorrectChoice(choice) ?? false,
child: MessageMorphChoiceItem( return ChoiceAnimationWidget(
cId: ConstructIdentifier( isSelected: selectedTag == choice,
lemma: choice, isCorrect: wasCorrect,
type: ConstructTypeEnum.morph, child: MessageMorphChoiceItem(
category: morph.name, cId: ConstructIdentifier(
), lemma: choice,
onTap: () { type: ConstructTypeEnum.morph,
setState(() => selectedTag = choice); category: morph.name,
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),
), ),
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( // SizedBox(
// height: 50, // height: 50,

@ -83,12 +83,14 @@ class MatchActivityCard extends StatelessWidget {
runSpacing: 4.0, runSpacing: 4.0,
children: activity.matchContent!.choices.map( children: activity.matchContent!.choices.map(
(PracticeChoice cf) { (PracticeChoice cf) {
final bool? wasCorrect =
currentActivity.practiceTarget.wasCorrectMatch(cf);
return ChoiceAnimationWidget( return ChoiceAnimationWidget(
isSelected: overlayController.selectedChoice == cf, isSelected: overlayController.selectedChoice == cf,
isCorrect: currentActivity.wasCorrectMatch(cf), isCorrect: wasCorrect,
child: PracticeMatchItem( child: PracticeMatchItem(
isSelected: overlayController.selectedChoice == cf, isSelected: overlayController.selectedChoice == cf,
isCorrect: currentActivity.wasCorrectMatch(cf), isCorrect: wasCorrect,
constructForm: cf, constructForm: cf,
content: choiceDisplayContent(cf.choiceContent, fontSize), content: choiceDisplayContent(cf.choiceContent, fontSize),
audioContent: audioContent:

@ -1,13 +1,7 @@
import 'dart:async'; import 'dart:async';
import 'dart:developer'; 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:collection/collection.dart';
import 'package:matrix/matrix.dart';
import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/pages/chat/chat.dart'; import 'package:fluffychat/pages/chat/chat.dart';
import 'package:fluffychat/pangea/common/utils/error_handler.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/message_selection_positioner.dart';
import 'package:fluffychat/pangea/toolbar/widgets/reading_assistance_content.dart'; import 'package:fluffychat/pangea/toolbar/widgets/reading_assistance_content.dart';
import 'package:fluffychat/widgets/matrix.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) /// Controls data at the top level of the toolbar (mainly token / toolbar mode selection)
class MessageSelectionOverlay extends StatefulWidget { class MessageSelectionOverlay extends StatefulWidget {
@ -503,7 +501,7 @@ class MessageOverlayController extends State<MessageSelectionOverlay>
} }
/// we don't want to associate the audio with the text in this mode /// we don't want to associate the audio with the text in this mode
if (practiceSelection?.hasActivity( if (practiceSelection?.hasActiveActivityByToken(
ActivityTypeEnum.wordFocusListening, ActivityTypeEnum.wordFocusListening,
token, token,
) == ) ==

@ -1,11 +1,5 @@
import 'dart:developer'; 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/construct_use_model.dart';
import 'package:fluffychat/pangea/analytics_misc/text_loading_shimmer.dart'; import 'package:fluffychat/pangea/analytics_misc/text_loading_shimmer.dart';
import 'package:fluffychat/pangea/events/models/pangea_token_model.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/message_selection_overlay.dart';
import 'package:fluffychat/pangea/toolbar/widgets/practice_activity/word_zoom_activity_button.dart'; import 'package:fluffychat/pangea/toolbar/widgets/practice_activity/word_zoom_activity_button.dart';
import 'package:fluffychat/widgets/matrix.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 { class LemmaMeaningWidget extends StatefulWidget {
final ConstructUses constructUse; final ConstructUses constructUse;
@ -121,8 +119,10 @@ class LemmaMeaningWidgetState extends State<LemmaMeaningWidget> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
if (widget.token != null && if (widget.token != null &&
widget.controller?.practiceSelection != null && widget.controller?.practiceSelection != null &&
widget.controller!.practiceSelection! widget.controller!.practiceSelection!.hasActiveActivityByToken(
.hasActivity(ActivityTypeEnum.wordMeaning, widget.token!)) { ActivityTypeEnum.wordMeaning,
widget.token!,
)) {
return WordZoomActivityButton( return WordZoomActivityButton(
icon: const Icon(Symbols.dictionary), icon: const Icon(Symbols.dictionary),
isSelected: widget.controller?.toolbarMode == MessageMode.wordMeaning, isSelected: widget.controller?.toolbarMode == MessageMode.wordMeaning,

@ -191,15 +191,15 @@ class LemmaWidgetState extends State<LemmaWidget> {
isSelected: isSelected:
MessageMode.listening == widget.overlayController?.toolbarMode, MessageMode.listening == widget.overlayController?.toolbarMode,
baseOpacity: 0.4, baseOpacity: 0.4,
callbackOverride: callbackOverride: widget.overlayController?.practiceSelection
widget.overlayController?.practiceSelection?.hasActivity( ?.hasActiveActivityByToken(
MessageMode.listening.associatedActivityType!, MessageMode.listening.associatedActivityType!,
widget.token, widget.token,
) == ) ==
true true
? () => widget.overlayController ? () => widget.overlayController
?.updateToolbarMode(MessageMode.listening) ?.updateToolbarMode(MessageMode.listening)
: null, : null,
uniqueID: "lemma-content-${widget.token.text.content}", uniqueID: "lemma-content-${widget.token.text.content}",
), ),
], ],

@ -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/events/models/pangea_token_model.dart';
import 'package:fluffychat/pangea/morphs/get_grammar_copy.dart'; import 'package:fluffychat/pangea/morphs/get_grammar_copy.dart';
import 'package:fluffychat/pangea/morphs/morph_features_enum.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/reading_assistance_input_row/morph_selection.dart';
import 'package:fluffychat/pangea/toolbar/widgets/message_selection_overlay.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/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 { class MorphologicalListItem extends StatelessWidget {
final MorphFeaturesEnum morphFeature; final MorphFeaturesEnum morphFeature;
@ -25,7 +23,7 @@ class MorphologicalListItem extends StatelessWidget {
}); });
bool get shouldDoActivity => bool get shouldDoActivity =>
overlayController.practiceSelection?.hasActivity( overlayController.practiceSelection?.hasActiveActivityByToken(
ActivityTypeEnum.morphId, ActivityTypeEnum.morphId,
token, token,
morphFeature, morphFeature,

@ -1,5 +1,3 @@
import 'package:flutter/material.dart';
import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/pangea/analytics_details_popup/analytics_details_popup.dart'; import 'package:fluffychat/pangea/analytics_details_popup/analytics_details_popup.dart';
import 'package:fluffychat/pangea/analytics_misc/construct_type_enum.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/lemma_widget.dart';
import 'package:fluffychat/pangea/toolbar/widgets/word_zoom/morphological_list_item.dart'; import 'package:fluffychat/pangea/toolbar/widgets/word_zoom/morphological_list_item.dart';
import 'package:fluffychat/widgets/matrix.dart'; import 'package:fluffychat/widgets/matrix.dart';
import 'package:flutter/material.dart';
class WordZoomWidget extends StatelessWidget { class WordZoomWidget extends StatelessWidget {
final PangeaToken token; final PangeaToken token;
@ -38,7 +37,7 @@ class WordZoomWidget extends StatelessWidget {
void onEditDone() => overlayController.initializeTokensAndMode(); void onEditDone() => overlayController.initializeTokensAndMode();
bool get hasEmojiActivity => bool get hasEmojiActivity =>
overlayController.practiceSelection?.hasActivity( overlayController.practiceSelection?.hasActiveActivityByToken(
ActivityTypeEnum.emoji, ActivityTypeEnum.emoji,
_selectedToken, _selectedToken,
) == ) ==
@ -170,15 +169,15 @@ class WordZoomWidget extends StatelessWidget {
isSelected: isSelected:
MessageMode.listening == overlayController.toolbarMode, MessageMode.listening == overlayController.toolbarMode,
baseOpacity: 0.4, baseOpacity: 0.4,
callbackOverride: callbackOverride: overlayController.practiceSelection
overlayController.practiceSelection?.hasActivity( ?.hasActiveActivityByToken(
MessageMode.listening.associatedActivityType!, MessageMode.listening.associatedActivityType!,
_selectedToken, _selectedToken,
) == ) ==
true true
? () => overlayController ? () => overlayController
.updateToolbarMode(MessageMode.listening) .updateToolbarMode(MessageMode.listening)
: null, : null,
uniqueID: "word-zoom-audio-${_selectedToken.text.content}", uniqueID: "word-zoom-audio-${_selectedToken.text.content}",
), ),
], ],

Loading…
Cancel
Save