2506 feedback on the new word card and message activities (#2525)

* fix(reading_assistance): fixed playing of sound and edited some copy

* chore: show token emojis for non-activity tokens, show morph defintion below choices once correct choice has been made

* chore(reading_assistance): added morph meaning for incorrect choices and adjusted some spacing

* chore(reading_assistance): adjusting spacing of morph choices to accomodate meaning text

---------

Co-authored-by: ggurdin <ggurdin@gmail.com>
This commit is contained in:
wcjord 2025-04-21 18:17:36 -04:00 committed by GitHub
parent 6d8d03910f
commit 9b547d702b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 97 additions and 95 deletions

View file

@ -4507,7 +4507,7 @@
},
"botModeValidation": "Please select a chat mode",
"clickBestOption": "Choose the best options to translate your message!",
"completeActivitiesToUnlock": "Complete one of the activities (emoji, meaning, listening OR grammar) to unlock the translation!",
"completeActivitiesToUnlock": "Complete at least one activity to unlock the translation!",
"botSettingsSubtitle": "Invite bot to moderate chat activity",
"invitePeople": "Invite users",
"noCapacityLimit": "No capacity limit",
@ -4866,4 +4866,4 @@
"shareSpaceLink": "Share link to space",
"byUsingPangeaChat": "By using Pangea Chat, I agree to the ",
"details": "Details"
}
}

View file

@ -316,7 +316,7 @@ class HtmlMessage extends StatelessWidget {
),
width: tokenWidth,
animateIn: isTransitionAnimation,
practiceTarget:
practiceTargetForToken:
overlayController?.toolbarMode.associatedActivityType !=
null
? overlayController?.practiceSelection

View file

@ -1,28 +1,24 @@
import 'dart:developer';
import 'dart:math';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:fluffychat/pangea/analytics_misc/text_loading_shimmer.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_meaning/morph_info_repo.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
class MorphMeaningWidget extends StatefulWidget {
final MorphFeaturesEnum feature;
final String tag;
final TextStyle? style;
final InlineSpan? leading;
const MorphMeaningWidget({
super.key,
required this.feature,
required this.tag,
this.style,
this.leading,
});
@override
@ -136,36 +132,18 @@ class MorphMeaningWidgetState extends State<MorphMeaningWidget> {
);
}
return Row(
mainAxisAlignment: widget.leading != null
? MainAxisAlignment.start
: MainAxisAlignment.center,
children: [
Flexible(
child: Tooltip(
triggerMode: TooltipTriggerMode.tap,
message: L10n.of(context).doubleClickToEdit,
child: GestureDetector(
onLongPress: () => _toggleEditMode(true),
onDoubleTap: () => _toggleEditMode(true),
child: RichText(
textAlign:
widget.leading == null ? TextAlign.center : TextAlign.start,
text: TextSpan(
style: widget.style,
children: [
if (widget.leading != null) widget.leading!,
if (widget.leading != null) const TextSpan(text: ' '),
TextSpan(
text: _cachedResponse ?? L10n.of(context).meaningNotFound,
),
],
),
),
),
),
return Tooltip(
triggerMode: TooltipTriggerMode.tap,
message: L10n.of(context).doubleClickToEdit,
child: GestureDetector(
onLongPress: () => _toggleEditMode(true),
onDoubleTap: () => _toggleEditMode(true),
child: Text(
textAlign: TextAlign.center,
_cachedResponse ?? L10n.of(context).meaningNotFound,
style: widget.style,
),
],
),
);
}
}

View file

@ -32,7 +32,7 @@ class MessageTokenButton extends StatefulWidget {
final TextStyle textStyle;
final double width;
final bool animateIn;
final PracticeTarget? practiceTarget;
final PracticeTarget? practiceTargetForToken;
const MessageTokenButton({
super.key,
@ -40,7 +40,7 @@ class MessageTokenButton extends StatefulWidget {
required this.token,
required this.textStyle,
required this.width,
required this.practiceTarget,
required this.practiceTargetForToken,
this.animateIn = false,
});
@ -127,9 +127,9 @@ class MessageTokenButtonState extends State<MessageTokenButton>
bool get _animate => widget.animateIn || _finishedInitialAnimation;
PracticeTarget? get _activity => widget.practiceTarget;
PracticeTarget? get _activity => widget.practiceTargetForToken;
bool get _isActivityCompleteForToken =>
bool get _isActivityCompleteOrNullForToken =>
_activity?.isCompleteByToken(
widget.token,
_activity!.morphFeature,
@ -189,8 +189,13 @@ class MessageTokenButtonState extends State<MessageTokenButton>
bool get _isEmpty {
final mode = widget.overlayController?.toolbarMode;
if (MessageMode.wordEmoji == mode &&
widget.token.vocabConstructID.userSetEmoji.firstOrNull != null) {
return false;
}
return _activity == null ||
(_isActivityCompleteForToken &&
(_isActivityCompleteOrNullForToken &&
![MessageMode.wordEmoji, MessageMode.wordMorph].contains(mode)) ||
(MessageMode.wordMorph == mode && _activity?.morphFeature == null);
}
@ -207,7 +212,7 @@ class MessageTokenButtonState extends State<MessageTokenButton>
messageMode: widget.overlayController!.toolbarMode,
token: widget.token,
selectedChoice: widget.overlayController?.selectedChoice,
isComplete: _isActivityCompleteForToken,
isActivityCompleteOrNullForToken: _isActivityCompleteOrNullForToken,
isSelected: _isSelected,
height: tokenButtonHeight,
width: widget.width,
@ -229,7 +234,7 @@ class MessageTokenButtonState extends State<MessageTokenButton>
messageMode: widget.overlayController!.toolbarMode,
token: widget.token,
selectedChoice: widget.overlayController?.selectedChoice,
isComplete: _isActivityCompleteForToken,
isActivityCompleteOrNullForToken: _isActivityCompleteOrNullForToken,
isSelected: _isSelected,
height: _heightAnimation.value,
width: widget.width,
@ -252,7 +257,7 @@ class MessageTokenButtonContent extends StatelessWidget {
final PangeaToken token;
final PracticeChoice? selectedChoice;
final bool isComplete;
final bool isActivityCompleteOrNullForToken;
final bool isSelected;
final double height;
final double width;
@ -269,7 +274,7 @@ class MessageTokenButtonContent extends StatelessWidget {
required this.messageMode,
required this.token,
required this.selectedChoice,
required this.isComplete,
required this.isActivityCompleteOrNullForToken,
required this.isSelected,
required this.height,
required this.width,
@ -291,7 +296,7 @@ class MessageTokenButtonContent extends StatelessWidget {
if (activity == null) {
return Theme.of(context).colorScheme.primary;
}
if (isComplete) {
if (isActivityCompleteOrNullForToken) {
return AppConfig.gold;
}
return Theme.of(context).colorScheme.primary;
@ -299,11 +304,7 @@ class MessageTokenButtonContent extends StatelessWidget {
@override
Widget build(BuildContext context) {
if (activity == null) {
return SizedBox(height: height);
}
if (isComplete) {
if (isActivityCompleteOrNullForToken || activity == null) {
if (MessageMode.wordEmoji == messageMode) {
return SizedBox(
height: height,
@ -314,7 +315,7 @@ class MessageTokenButtonContent extends StatelessWidget {
),
);
}
if (MessageMode.wordMorph == messageMode) {
if (MessageMode.wordMorph == messageMode && activity != null) {
final morphFeature = activity!.morphFeature!;
final morphTag = token.morphIdByFeature(morphFeature);
if (morphTag != null) {
@ -337,8 +338,9 @@ class MessageTokenButtonContent extends StatelessWidget {
),
);
}
} else {
return SizedBox(height: height);
}
return SizedBox(height: height);
}
if (MessageMode.wordMorph == messageMode) {

View file

@ -1,8 +1,5 @@
import 'package:flutter/material.dart';
import 'package:collection/collection.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:fluffychat/pangea/analytics_details_popup/morph_meaning_widget.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 +12,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
@ -62,19 +61,41 @@ class MessageMorphInputBarContentState
void didUpdateWidget(covariant MessageMorphInputBarContent oldWidget) {
if (morph != oldWidget.overlayController.selectedMorph?.morph ||
token != oldWidget.overlayController.selectedToken) {
selectedTag = null;
setState(() {});
}
super.didUpdateWidget(oldWidget);
}
String? get _correctChoice {
return widget.activity.multipleChoiceContent?.choices
.firstWhereOrNull((choice) {
return widget.activity.practiceTarget.wasCorrectChoice(choice) == true;
});
}
TextStyle? textStyle(BuildContext context) => overlay.maxWidth > 600
? Theme.of(context).textTheme.bodyLarge?.copyWith(
fontWeight: FontWeight.bold,
)
: Theme.of(context).textTheme.bodyMedium?.copyWith(
fontWeight: FontWeight.bold,
);
@override
Widget build(BuildContext context) {
final iconSize = overlay.maxWidth > 600 ? 30.0 : 24.0;
final spacing = overlay.maxWidth > 600 ? 16.0 : 8.0;
final iconSize = overlay.maxWidth > 600
? 28.0
: overlay.maxWidth > 600
? 24.0
: 16.0;
final spacing = overlay.maxWidth > 600
? 16.0
: overlay.maxWidth > 600
? 8.0
: 4.0;
return Column(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.start,
mainAxisSize: MainAxisSize.max,
spacing: spacing,
children: [
@ -94,13 +115,7 @@ class MessageMorphInputBarContentState
morph.getDisplayCopy(context),
token.text.content,
),
style: overlay.maxWidth > 600
? Theme.of(context).textTheme.bodyLarge?.copyWith(
fontWeight: FontWeight.bold,
)
: Theme.of(context).textTheme.bodyMedium?.copyWith(
fontWeight: FontWeight.bold,
),
style: textStyle(context),
textAlign: TextAlign.center,
),
),
@ -151,12 +166,21 @@ class MessageMorphInputBarContentState
},
).toList(),
),
// SizedBox(
// height: 50,
// child: selectedTag != null
// ? MorphMeaningWidget(feature: morph, tag: selectedTag!)
// : null,
// ),
Container(
constraints: BoxConstraints(
minHeight: overlay.maxWidth > 600 ? 20 : 34,
),
alignment: Alignment.center,
child: selectedTag != null
? MorphMeaningWidget(
feature: morph,
tag: selectedTag!,
style: overlay.maxWidth > 600
? Theme.of(context).textTheme.bodyLarge
: Theme.of(context).textTheme.bodySmall,
)
: const SizedBox.shrink(),
),
],
);
}

View file

@ -1,11 +1,10 @@
import 'package:flutter/material.dart';
import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/config/themes.dart';
import 'package:fluffychat/pangea/constructs/construct_identifier.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:flutter/material.dart';
class MessageMorphChoiceItem extends StatefulWidget {
const MessageMorphChoiceItem({
@ -61,7 +60,7 @@ class MessageMorphChoiceItemState extends State<MessageMorphChoiceItem> {
@override
Widget build(BuildContext context) {
final color = _color;
final iconSize = FluffyThemes.isColumnMode(context) ? 32.0 : 24.0;
final iconSize = FluffyThemes.isColumnMode(context) ? 24.0 : 16.0;
final style = FluffyThemes.isColumnMode(context)
? Theme.of(context).textTheme.bodyLarge
: Theme.of(context).textTheme.bodySmall;
@ -76,8 +75,9 @@ class MessageMorphChoiceItemState extends State<MessageMorphChoiceItem> {
color: color,
borderRadius: BorderRadius.circular(AppConfig.borderRadius),
border: Border.all(
color:
widget.isSelected || _isHovered ? color : Colors.transparent,
color: widget.isSelected || _isHovered
? color.withAlpha(255)
: Colors.transparent,
width: 2.0,
),
),

View file

@ -1,14 +1,13 @@
import 'dart:developer';
import 'package:flutter/foundation.dart';
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';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
class PracticeMatchItem extends StatefulWidget {
const PracticeMatchItem({
@ -129,7 +128,7 @@ class PracticeMatchItemState extends State<PracticeMatchItem> {
borderRadius: BorderRadius.circular(AppConfig.borderRadius),
border: isSelected
? Border.all(
color: color(context),
color: color(context).withAlpha(255),
width: 2,
)
: Border.all(

View file

@ -538,10 +538,11 @@ class MessageOverlayController extends State<MessageSelectionOverlay>
/// we don't want to associate the audio with the text in this mode
if (practiceSelection?.hasActiveActivityByToken(
ActivityTypeEnum.wordFocusListening,
token,
) ==
false) {
ActivityTypeEnum.wordFocusListening,
token,
) ==
false ||
!hideWordCardContent) {
widget.chatController.choreographer.tts.tryToSpeak(
token.text.content,
context,

View file

@ -226,7 +226,7 @@ class MessageTextWidget extends StatelessWidget {
textStyle: renderer.style(context),
width: tokenWidth,
animateIn: isTransitionAnimation,
practiceTarget: overlayController
practiceTargetForToken: overlayController
?.toolbarMode.associatedActivityType !=
null
? overlayController?.practiceSelection

View file

@ -1,8 +1,7 @@
import 'package:flutter/material.dart';
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/toolbar_button.dart';
import 'package:flutter/material.dart';
class ToolbarButtonRow extends StatelessWidget {
final MessageOverlayController overlayController;
@ -14,8 +13,6 @@ class ToolbarButtonRow extends StatelessWidget {
static const double iconWidth = 36.0;
static const double buttonSize = 40.0;
static const barMargin =
EdgeInsets.symmetric(horizontal: iconWidth / 2, vertical: buttonSize / 2);
@override
Widget build(BuildContext context) {
@ -85,6 +82,7 @@ class ToolbarButtonRow extends StatelessWidget {
),
],
),
const SizedBox(height: 4.0),
],
);
}