2455 reading assistance feedback from linh (#2456)

* chore: show correct morph once matched

* fix: account for morph activities in function to determine is token activity is complete

* chore: on click already correctly matched item, open the toolbar for the corresponding token
This commit is contained in:
ggurdin 2025-04-16 12:44:23 -04:00 committed by GitHub
parent 43d6b992f7
commit 739e271cb4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 66 additions and 26 deletions

View file

@ -406,7 +406,7 @@ class ChatController extends State<ChatPageWithRoom>
pangeaController.getAnalytics.analyticsStream.stream.listen((update) {
if (update.targetID == null) return;
OverlayUtil.showOverlay(
overlayKey: update.targetID,
overlayKey: "${update.targetID ?? ""}_points",
followerAnchor: Alignment.bottomCenter,
targetAnchor: Alignment.bottomCenter,
context: context,

View file

@ -119,7 +119,7 @@ class PointsGainedAnimationState extends State<PointsGainedAnimation>
_controller?.forward().then(
(_) {
if (!mounted) return;
MatrixState.pAnyState.closeOverlay(widget.targetID);
MatrixState.pAnyState.closeOverlay("${widget.targetID}_points");
},
);
}
@ -132,7 +132,7 @@ class PointsGainedAnimationState extends State<PointsGainedAnimation>
_offsetAnimation == null) {
WidgetsBinding.instance.addPostFrameCallback((_) {
if (mounted) {
MatrixState.pAnyState.closeOverlay(widget.targetID);
MatrixState.pAnyState.closeOverlay("${widget.targetID}_points");
}
});
return const SizedBox();

View file

@ -11,7 +11,9 @@ import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/pangea/common/utils/error_handler.dart';
import 'package:fluffychat/pangea/events/models/pangea_token_model.dart';
import 'package:fluffychat/pangea/message_token_text/dotted_border_painter.dart';
import 'package:fluffychat/pangea/practice_activities/activity_type_enum.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_icon.dart';
import 'package:fluffychat/pangea/practice_activities/practice_choice.dart';
import 'package:fluffychat/pangea/practice_activities/practice_target.dart';
import 'package:fluffychat/pangea/toolbar/enums/message_mode_enum.dart';
@ -181,17 +183,12 @@ class MessageTokenButtonState extends State<MessageTokenButton>
// );
}
bool get isActivityCompleteForToken {
if (activity?.activityType == ActivityTypeEnum.morphId) {
return (activity?.record.completeResponses ?? 0) > 0;
}
for (final response in activity!.record.responses) {
if (response.cId == widget.token.vocabConstructID && response.isCorrect) {
return true;
}
}
return false;
}
bool get isActivityCompleteForToken =>
activity?.isCompleteByToken(
widget.token,
activity!.morphFeature,
) ==
true;
Color get color {
if (activity == null) {
@ -209,6 +206,31 @@ class MessageTokenButtonState extends State<MessageTokenButton>
if (MessageMode.wordEmoji == widget.overlayController?.toolbarMode) {
return SizedBox(height: height, child: emojiView);
}
if (MessageMode.wordMorph == widget.overlayController?.toolbarMode &&
activity?.morphFeature != null) {
final morphFeature = activity!.morphFeature!;
final morphTag = widget.token.morphIdByFeature(morphFeature);
if (morphTag != null) {
return Tooltip(
message: getGrammarCopy(
category: morphFeature.toShortString(),
lemma: morphTag.lemma,
context: context,
),
child: SizedBox(
width: widget.width,
height: height,
child: Center(
child: MorphIcon(
morphFeature: morphFeature,
morphTag: morphTag.lemma,
size: const Size(24.0, 24.0),
),
),
),
);
}
}
return SizedBox(height: height);
}
@ -216,6 +238,7 @@ class MessageTokenButtonState extends State<MessageTokenButton>
if (activity?.morphFeature == null) {
return SizedBox(height: height);
}
final bool isSelected =
(widget.overlayController?.selectedMorph?.token == widget.token &&
widget.overlayController?.selectedMorph?.morph ==

View file

@ -74,8 +74,8 @@ class PracticeTarget {
Map<String, dynamic> toJson() {
return {
'tokens': tokens.map((e) => e.toJson()).toList(),
'activityType': activityType.index,
'morphFeature': morphFeature?.index,
'activityType': activityType.name,
'morphFeature': morphFeature?.name,
'userL2': userL2,
};
}
@ -114,6 +114,13 @@ class PracticeTarget {
);
return false;
}
if (activityType == ActivityTypeEnum.morphId) {
return record.responses.any(
(res) => res.cId == token.morphIdByFeature(morph!) && res.isCorrect,
);
}
return record.responses.any(
(res) => res.cId == token.vocabConstructID && res.isCorrect,
);

View file

@ -61,7 +61,7 @@ class MessageMorphChoiceItemState extends State<MessageMorphChoiceItem> {
@override
Widget build(BuildContext context) {
final color = _color;
final iconSize = FluffyThemes.isColumnMode(context) ? 40.0 : 24.0;
final iconSize = FluffyThemes.isColumnMode(context) ? 32.0 : 24.0;
final style = FluffyThemes.isColumnMode(context)
? Theme.of(context).textTheme.bodyLarge
: Theme.of(context).textTheme.bodySmall;

View file

@ -3,6 +3,8 @@ import 'dart:developer';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:collection/collection.dart';
import 'package:fluffychat/config/themes.dart';
import 'package:fluffychat/pangea/choreographer/widgets/choice_animation.dart';
import 'package:fluffychat/pangea/practice_activities/activity_type_enum.dart';
@ -89,6 +91,9 @@ class MatchActivityCard extends StatelessWidget {
isSelected: overlayController.selectedChoice == cf,
isCorrect: wasCorrect,
child: PracticeMatchItem(
token: currentActivity.practiceTarget.tokens.firstWhereOrNull(
(t) => t.vocabConstructID == cf.form.cId,
),
isSelected: overlayController.selectedChoice == cf,
isCorrect: wasCorrect,
constructForm: cf,

View file

@ -5,6 +5,7 @@ import 'package:flutter/material.dart';
import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/pangea/common/utils/error_handler.dart';
import 'package:fluffychat/pangea/events/models/pangea_token_model.dart';
import 'package:fluffychat/pangea/practice_activities/practice_choice.dart';
import 'package:fluffychat/pangea/toolbar/controllers/tts_controller.dart';
import 'package:fluffychat/pangea/toolbar/widgets/message_selection_overlay.dart';
@ -13,6 +14,7 @@ class PracticeMatchItem extends StatefulWidget {
const PracticeMatchItem({
super.key,
required this.content,
required this.token,
required this.constructForm,
required this.isCorrect,
required this.isSelected,
@ -22,6 +24,7 @@ class PracticeMatchItem extends StatefulWidget {
});
final Widget content;
final PangeaToken? token;
final PracticeChoice constructForm;
final String? audioContent;
final MessageOverlayController overlayController;
@ -131,7 +134,9 @@ class PracticeMatchItemState extends State<PracticeMatchItem> {
void onTap() {
play();
widget.overlayController.onChoiceSelect(widget.constructForm);
isCorrect == null || !isCorrect! || widget.token == null
? widget.overlayController.onChoiceSelect(widget.constructForm)
: widget.overlayController.updateSelectedSpan(widget.token!.text);
}
@override

View file

@ -222,7 +222,7 @@ class MessageOverlayController extends State<MessageSelectionOverlay>
return;
}
_updateSelectedSpan(widget._initialSelectedToken!.text);
updateSelectedSpan(widget._initialSelectedToken!.text);
int retries = 0;
while (retries < 5 &&
@ -285,14 +285,14 @@ class MessageOverlayController extends State<MessageSelectionOverlay>
}
/// Update [selectedSpan]
void _updateSelectedSpan(PangeaTokenText selectedSpan, [bool force = false]) {
void updateSelectedSpan(PangeaTokenText selectedSpan, [bool force = false]) {
if (selectedMorph != null) {
selectedMorph = null;
}
// close overlay of previous token
if (selectedToken != null) {
MatrixState.pAnyState.closeOverlay(
selectedToken!.text.uniqueKey,
"${selectedToken!.text.uniqueKey}_toolbar",
);
}
@ -329,7 +329,7 @@ class MessageOverlayController extends State<MessageSelectionOverlay>
closePrevOverlay: false,
backDropToDismiss: false,
addBorder: false,
overlayKey: selectedToken!.text.uniqueKey,
overlayKey: "${selectedToken!.text.uniqueKey}_toolbar",
maxHeight: AppConfig.toolbarMaxHeight,
maxWidth: AppConfig.toolbarMinWidth,
);
@ -341,7 +341,7 @@ class MessageOverlayController extends State<MessageSelectionOverlay>
// close overlay of any selected token
if (_selectedSpan != null) {
_updateSelectedSpan(_selectedSpan!);
updateSelectedSpan(_selectedSpan!);
}
toolbarMode = mode;
@ -383,7 +383,7 @@ class MessageOverlayController extends State<MessageSelectionOverlay>
toolbarMode = MessageMode.wordMorph;
// // close overlay of previous token
if (_selectedSpan != null && _selectedSpan != newMorph.token.text) {
_updateSelectedSpan(_selectedSpan!);
updateSelectedSpan(_selectedSpan!);
}
selectedMorph = newMorph;
setState(() {});
@ -543,7 +543,7 @@ class MessageOverlayController extends State<MessageSelectionOverlay>
);
}
_updateSelectedSpan(token.text);
updateSelectedSpan(token.text);
}
/// Whether the given token is currently selected or highlighted