fix(reading_assistance): register end of activity
This commit is contained in:
parent
0321d20f44
commit
32ab738477
11 changed files with 143 additions and 117 deletions
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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<MessageSelectionOverlay>
|
|||
}
|
||||
|
||||
/// we don't want to associate the audio with the text in this mode
|
||||
if (practiceSelection?.hasActivity(
|
||||
if (practiceSelection?.hasActiveActivityByToken(
|
||||
ActivityTypeEnum.wordFocusListening,
|
||||
token,
|
||||
) ==
|
||||
|
|
|
|||
|
|
@ -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<LemmaMeaningWidget> {
|
|||
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,
|
||||
|
|
|
|||
|
|
@ -191,15 +191,15 @@ class LemmaWidgetState extends State<LemmaWidget> {
|
|||
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}",
|
||||
),
|
||||
],
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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}",
|
||||
),
|
||||
],
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue