diff --git a/lib/config/app_config.dart b/lib/config/app_config.dart index 71e6b225d..69f1a481f 100644 --- a/lib/config/app_config.dart +++ b/lib/config/app_config.dart @@ -35,7 +35,7 @@ abstract class AppConfig { static const double toolbarButtonsHeight = 50.0; static const double toolbarSpacing = 8.0; static const double toolbarIconSize = 24.0; - static const double readingAssistanceInputBarHeight = 140.0; + static const double readingAssistanceInputBarHeight = 175.0; static const double reactionsPickerHeight = 48.0; static const double chatInputRowOverlayPadding = 8.0; static const double selectModeInputBarHeight = 0; diff --git a/lib/pangea/toolbar/layout/message_selection_positioner.dart b/lib/pangea/toolbar/layout/message_selection_positioner.dart index 7b8e2c68d..9af6d7c94 100644 --- a/lib/pangea/toolbar/layout/message_selection_positioner.dart +++ b/lib/pangea/toolbar/layout/message_selection_positioner.dart @@ -14,6 +14,7 @@ import 'package:fluffychat/pages/chat/chat.dart'; import 'package:fluffychat/pangea/common/utils/error_handler.dart'; import 'package:fluffychat/pangea/events/event_wrappers/pangea_message_event.dart'; import 'package:fluffychat/pangea/events/models/pangea_token_model.dart'; +import 'package:fluffychat/pangea/instructions/instructions_inline_tooltip.dart'; import 'package:fluffychat/pangea/toolbar/layout/over_message_overlay.dart'; import 'package:fluffychat/pangea/toolbar/layout/practice_mode_transition_animation.dart'; import 'package:fluffychat/pangea/toolbar/layout/reading_assistance_mode_enum.dart'; @@ -394,6 +395,28 @@ class MessageSelectionPositionerState extends State alignment: ownMessage ? Alignment.centerRight : Alignment.centerLeft, children: [ + Positioned( + top: 0, + left: 0, + right: 0, + child: ListenableBuilder( + listenable: widget.overlayController.practiceController, + builder: (context, _) { + final instruction = widget.overlayController + .practiceController.practiceMode.instruction; + if (instruction != null) { + return InstructionsInlineTooltip( + instructionsEnum: widget.overlayController + .practiceController.practiceMode.instruction!, + padding: const EdgeInsets.all(16.0), + animate: false, + ); + } + + return const SizedBox(); + }, + ), + ), ValueListenableBuilder( valueListenable: _startedTransition, builder: (context, started, __) { diff --git a/lib/pangea/toolbar/message_practice/message_audio_card.dart b/lib/pangea/toolbar/message_practice/message_audio_card.dart index 82d2624c8..7bc02242c 100644 --- a/lib/pangea/toolbar/message_practice/message_audio_card.dart +++ b/lib/pangea/toolbar/message_practice/message_audio_card.dart @@ -64,22 +64,27 @@ class MessageAudioCardState extends State { @override Widget build(BuildContext context) { - return _isLoading - ? const TextLoadingShimmer(width: 200) - : audioFile != null - ? AudioPlayerWidget( - null, - eventId: "${widget.messageEvent.eventId}_practice", - roomId: widget.messageEvent.room.id, - senderId: widget.messageEvent.senderId, - matrixFile: audioFile, - color: Theme.of(context).colorScheme.onPrimaryContainer, - fontSize: AppConfig.messageFontSize * AppConfig.fontSizeFactor, - linkColor: Theme.of(context).brightness == Brightness.light - ? Theme.of(context).colorScheme.primary - : Theme.of(context).colorScheme.onPrimary, - ) - : const SizedBox(); + return Container( + height: 60.0, + alignment: Alignment.center, + child: _isLoading + ? const TextLoadingShimmer(width: 200) + : audioFile != null + ? AudioPlayerWidget( + null, + eventId: "${widget.messageEvent.eventId}_practice", + roomId: widget.messageEvent.room.id, + senderId: widget.messageEvent.senderId, + matrixFile: audioFile, + color: Theme.of(context).colorScheme.onPrimaryContainer, + fontSize: + AppConfig.messageFontSize * AppConfig.fontSizeFactor, + linkColor: Theme.of(context).brightness == Brightness.light + ? Theme.of(context).colorScheme.primary + : Theme.of(context).colorScheme.onPrimary, + ) + : const SizedBox(), + ); } } diff --git a/lib/pangea/toolbar/message_practice/practice_match_card.dart b/lib/pangea/toolbar/message_practice/practice_match_card.dart index 952f5fefa..b71d25589 100644 --- a/lib/pangea/toolbar/message_practice/practice_match_card.dart +++ b/lib/pangea/toolbar/message_practice/practice_match_card.dart @@ -7,7 +7,6 @@ import 'package:collection/collection.dart'; import 'package:fluffychat/config/themes.dart'; import 'package:fluffychat/pangea/common/widgets/choice_animation.dart'; -import 'package:fluffychat/pangea/common/widgets/shimmer_background.dart'; import 'package:fluffychat/pangea/practice_activities/activity_type_enum.dart'; import 'package:fluffychat/pangea/practice_activities/practice_activity_model.dart'; import 'package:fluffychat/pangea/practice_activities/practice_choice.dart'; @@ -38,28 +37,20 @@ class MatchActivityCard extends StatelessWidget { switch (activityType) { case ActivityTypeEnum.emoji: case ActivityTypeEnum.wordMeaning: - return ShimmerBackground( - enabled: controller.selectedChoice == null && - !currentActivity.practiceTarget.hasAnyCorrectChoices, - child: Padding( - padding: const EdgeInsets.all(8), - child: Text( - choice, - style: TextStyle(fontSize: fontSize), - textAlign: TextAlign.center, - ), + return Padding( + padding: const EdgeInsets.all(8), + child: Text( + choice, + style: TextStyle(fontSize: fontSize), + textAlign: TextAlign.center, ), ); case ActivityTypeEnum.wordFocusListening: - return ShimmerBackground( - enabled: controller.selectedChoice == null && - !currentActivity.practiceTarget.hasAnyCorrectChoices, - child: Padding( - padding: const EdgeInsets.all(8), - child: Icon( - Icons.volume_up, - size: fontSize, - ), + return Padding( + padding: const EdgeInsets.all(8), + child: Icon( + Icons.volume_up, + size: fontSize, ), ); default: @@ -113,6 +104,8 @@ class MatchActivityCard extends StatelessWidget { ? cf.choiceContent : null, controller: controller, + shimmer: controller.selectedChoice == null && + !currentActivity.practiceTarget.hasAnyCorrectChoices, ), ); }, diff --git a/lib/pangea/toolbar/message_practice/practice_match_item.dart b/lib/pangea/toolbar/message_practice/practice_match_item.dart index 46f031512..055250ed9 100644 --- a/lib/pangea/toolbar/message_practice/practice_match_item.dart +++ b/lib/pangea/toolbar/message_practice/practice_match_item.dart @@ -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/common/widgets/shimmer_background.dart'; import 'package:fluffychat/pangea/events/models/pangea_token_model.dart'; import 'package:fluffychat/pangea/practice_activities/practice_choice.dart'; import 'package:fluffychat/pangea/text_to_speech/tts_controller.dart'; @@ -19,6 +20,7 @@ class PracticeMatchItem extends StatefulWidget { final PracticeController controller; final bool? isCorrect; final bool isSelected; + final bool shimmer; const PracticeMatchItem({ super.key, @@ -29,6 +31,7 @@ class PracticeMatchItem extends StatefulWidget { required this.isSelected, this.audioContent, required this.controller, + this.shimmer = false, }); @override @@ -121,7 +124,6 @@ class PracticeMatchItemState extends State { children: [ Flexible( child: Container( - padding: const EdgeInsets.all(8), decoration: BoxDecoration( color: color(context).withAlpha((0.4 * 255).toInt()), borderRadius: BorderRadius.circular(AppConfig.borderRadius), @@ -152,7 +154,10 @@ class PracticeMatchItemState extends State { onHover: (isHovered) => setState(() => _isHovered = isHovered), borderRadius: BorderRadius.circular(AppConfig.borderRadius), onTap: onTap, - child: content, + child: ShimmerBackground( + enabled: widget.shimmer, + child: content, + ), ), ); } diff --git a/lib/pangea/toolbar/message_practice/reading_assistance_input_bar.dart b/lib/pangea/toolbar/message_practice/reading_assistance_input_bar.dart index 782769ff3..4d0b367b7 100644 --- a/lib/pangea/toolbar/message_practice/reading_assistance_input_bar.dart +++ b/lib/pangea/toolbar/message_practice/reading_assistance_input_bar.dart @@ -1,9 +1,10 @@ import 'package:flutter/material.dart'; +import 'package:material_symbols_icons/symbols.dart'; + import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/pangea/events/models/pangea_token_model.dart'; -import 'package:fluffychat/pangea/instructions/instructions_inline_tooltip.dart'; import 'package:fluffychat/pangea/practice_activities/practice_target.dart'; import 'package:fluffychat/pangea/toolbar/message_practice/message_practice_mode_enum.dart'; import 'package:fluffychat/pangea/toolbar/message_practice/practice_activity_card.dart'; @@ -59,19 +60,12 @@ class ReadingAssistanceInputBarState extends State { m.associatedActivityType!, ), isSelected: widget.controller.practiceMode == m, + shimmer: widget.controller.practiceMode == + MessagePracticeMode.noneSelected, ), ), ], ), - if (widget.controller.practiceMode.instruction != null) - InstructionsInlineTooltip( - instructionsEnum: widget.controller.practiceMode.instruction!, - padding: const EdgeInsets.symmetric( - horizontal: 16.0, - vertical: 4.0, - ), - animate: false, - ), Padding( padding: const EdgeInsets.symmetric(horizontal: 16.0), child: Material( @@ -133,13 +127,9 @@ class _ReadingAssistanceBarContent extends StatelessWidget { case MessagePracticeMode.noneSelected: return controller.isTotallyDone ? const _AllDoneWidget() - : Text( - L10n.of(context).choosePracticeMode, - style: Theme.of(context) - .textTheme - .bodyLarge - ?.copyWith(fontStyle: FontStyle.italic), - textAlign: TextAlign.center, + : const Icon( + Symbols.fitness_center, + size: 60.0, ); case MessagePracticeMode.wordEmoji: @@ -151,10 +141,10 @@ class _ReadingAssistanceBarContent extends StatelessWidget { final target = controller.practiceSelection?.getTarget(activityType!); if (target == null || activityCompleted) { - return Text( - L10n.of(context).practiceActivityCompleted, - style: Theme.of(context).textTheme.bodyLarge, - textAlign: TextAlign.center, + return const Icon( + Symbols.fitness_center, + size: 60.0, + color: AppConfig.goldLight, ); } @@ -169,10 +159,10 @@ class _ReadingAssistanceBarContent extends StatelessWidget { return const _AllDoneWidget(); } if (activityCompleted) { - return Text( - L10n.of(context).practiceActivityCompleted, - style: Theme.of(context).textTheme.bodyLarge, - textAlign: TextAlign.center, + return const Icon( + Symbols.fitness_center, + size: 60.0, + color: AppConfig.goldLight, ); } diff --git a/lib/pangea/toolbar/message_practice/token_practice_button.dart b/lib/pangea/toolbar/message_practice/token_practice_button.dart index 107b57bd8..b3341fe51 100644 --- a/lib/pangea/toolbar/message_practice/token_practice_button.dart +++ b/lib/pangea/toolbar/message_practice/token_practice_button.dart @@ -8,6 +8,7 @@ import 'package:shimmer/shimmer.dart'; import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/config/themes.dart'; +import 'package:fluffychat/pangea/common/widgets/shimmer_background.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'; @@ -103,6 +104,8 @@ class TokenPracticeButton extends StatelessWidget { _activity!.morphFeature!, ), ), + shimmer: controller.selectedMorph == null && + _activity?.hasAnyCorrectChoices == false, ); } else { child = _StandardMatchButton( @@ -194,10 +197,12 @@ class _MorphMatchButton extends StatelessWidget { final Function()? onTap; final bool active; final Color textColor; + final bool shimmer; const _MorphMatchButton({ required this.active, required this.textColor, + this.shimmer = false, this.onTap, }); @@ -210,16 +215,24 @@ class _MorphMatchButton extends StatelessWidget { return InkWell( onTap: onTap, borderRadius: BorderRadius.circular(AppConfig.borderRadius - 4), - child: Opacity( - opacity: active ? 1.0 : 0.6, - child: AnimatedScale( - scale: hovered || active ? 1.25 : 1.0, - duration: FluffyThemes.animationDuration, - curve: FluffyThemes.animationCurve, - child: Icon( - Symbols.toys_and_games, - color: textColor, - size: 24.0, + child: ShimmerBackground( + enabled: shimmer, + child: SizedBox( + width: 24.0, + child: Center( + child: Opacity( + opacity: active ? 1.0 : 0.6, + child: AnimatedScale( + scale: hovered || active ? 1.25 : 1.0, + duration: FluffyThemes.animationDuration, + curve: FluffyThemes.animationCurve, + child: Icon( + Symbols.toys_and_games, + color: textColor, + size: 24.0, + ), + ), + ), ), ), ), diff --git a/lib/pangea/toolbar/message_practice/toolbar_button.dart b/lib/pangea/toolbar/message_practice/toolbar_button.dart index 388d0a7ee..c4981c87e 100644 --- a/lib/pangea/toolbar/message_practice/toolbar_button.dart +++ b/lib/pangea/toolbar/message_practice/toolbar_button.dart @@ -1,5 +1,7 @@ import 'package:flutter/material.dart'; +import 'package:shimmer/shimmer.dart'; + import 'package:fluffychat/pangea/common/widgets/pressable_button.dart'; import 'package:fluffychat/pangea/toolbar/message_practice/message_practice_mode_enum.dart'; @@ -9,12 +11,14 @@ class ToolbarButton extends StatelessWidget { final bool isComplete; final bool isSelected; + final bool shimmer; const ToolbarButton({ required this.mode, required this.setMode, required this.isComplete, required this.isSelected, + this.shimmer = false, super.key, }); @@ -35,17 +39,38 @@ class ToolbarButton extends StatelessWidget { playSound: true, colorFactor: Theme.of(context).brightness == Brightness.light ? 0.55 : 0.3, - builder: (context, depressed, shadowColor) => Container( - height: 40.0, - width: 40.0, - decoration: BoxDecoration( - color: depressed ? shadowColor : color, - shape: BoxShape.circle, - ), - child: Icon( - mode.icon, - size: 20, - ), + builder: (context, depressed, shadowColor) => Stack( + alignment: Alignment.center, + children: [ + Container( + height: 40.0, + width: 40.0, + decoration: BoxDecoration( + color: depressed ? shadowColor : color, + shape: BoxShape.circle, + ), + ), + if (shimmer) + Shimmer.fromColors( + baseColor: Colors.transparent, + highlightColor: Theme.of(context) + .colorScheme + .primaryContainer + .withAlpha(0xAA), + child: Container( + height: 40.0, + width: 40.0, + decoration: BoxDecoration( + color: depressed ? shadowColor : color, + shape: BoxShape.circle, + ), + ), + ), + Icon( + mode.icon, + size: 20, + ), + ], ), ), ),