diff --git a/assets/pangea/bot_faces/pangea_bot.riv b/assets/pangea/bot_faces/pangea_bot.riv index 177b9b28f..94244a30f 100644 Binary files a/assets/pangea/bot_faces/pangea_bot.riv and b/assets/pangea/bot_faces/pangea_bot.riv differ diff --git a/lib/pages/chat/chat_input_row.dart b/lib/pages/chat/chat_input_row.dart index fd8d95b5b..a5340975a 100644 --- a/lib/pages/chat/chat_input_row.dart +++ b/lib/pages/chat/chat_input_row.dart @@ -1,6 +1,5 @@ import 'package:animations/animations.dart'; import 'package:fluffychat/config/app_config.dart'; -import 'package:fluffychat/pangea/choreographer/widgets/it_bar.dart'; import 'package:fluffychat/pangea/choreographer/widgets/send_button.dart'; import 'package:fluffychat/pangea/constants/language_keys.dart'; import 'package:fluffychat/utils/platform_infos.dart'; @@ -35,9 +34,6 @@ class ChatInputRow extends StatelessWidget { return Column( children: [ - ITBar( - choreographer: controller.choreographer, - ), Row( // crossAxisAlignment: CrossAxisAlignment.end, crossAxisAlignment: CrossAxisAlignment.center, diff --git a/lib/pages/chat/chat_view.dart b/lib/pages/chat/chat_view.dart index 21789f527..8f1b92ed5 100644 --- a/lib/pages/chat/chat_view.dart +++ b/lib/pages/chat/chat_view.dart @@ -22,6 +22,7 @@ import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:future_loading_dialog/future_loading_dialog.dart'; import 'package:matrix/matrix.dart'; +import '../../pangea/choreographer/widgets/it_bar.dart'; import '../../utils/stream_extension.dart'; import 'chat_emoji_picker.dart'; import 'chat_input_row.dart'; @@ -400,6 +401,9 @@ class ChatView extends StatelessWidget { mainAxisSize: MainAxisSize.min, children: [ const ConnectionStatusHeader(), + ITBar( + choreographer: controller.choreographer, + ), ReactionsPicker(controller), ReplyDisplay(controller), ChatInputRow(controller), diff --git a/lib/pangea/choreographer/widgets/choice_array.dart b/lib/pangea/choreographer/widgets/choice_array.dart index 54fd601b9..0ffa445c0 100644 --- a/lib/pangea/choreographer/widgets/choice_array.dart +++ b/lib/pangea/choreographer/widgets/choice_array.dart @@ -5,12 +5,11 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; -import 'package:matrix/matrix.dart'; import '../../utils/bot_style.dart'; import 'it_shimmer.dart'; -class ChoicesArray extends StatelessWidget { +class ChoicesArray extends StatefulWidget { final bool isLoading; final List? choices; final void Function(int) onPressed; @@ -18,6 +17,7 @@ class ChoicesArray extends StatelessWidget { final int? selectedChoiceIndex; final String originalSpan; final String Function(int) uniqueKeyForLayerLink; + const ChoicesArray({ super.key, required this.isLoading, @@ -29,23 +29,49 @@ class ChoicesArray extends StatelessWidget { this.onLongPress, }); + @override + ChoicesArrayState createState() => ChoicesArrayState(); +} + +class ChoicesArrayState extends State { + bool interactionDisabled = false; + + void disableInteraction() { + WidgetsBinding.instance.addPostFrameCallback((_) { + setState(() { + interactionDisabled = true; + }); + }); + } + + void enableInteractions() { + WidgetsBinding.instance.addPostFrameCallback((_) { + setState(() { + interactionDisabled = false; + }); + }); + } + @override Widget build(BuildContext context) { final ThemeData theme = Theme.of(context); - return isLoading && (choices == null || choices!.length <= 1) - ? ItShimmer(originalSpan: originalSpan) + return widget.isLoading && (widget.choices == null || widget.choices!.length <= 1) + ? ItShimmer(originalSpan: widget.originalSpan) : Wrap( alignment: WrapAlignment.center, - children: choices + children: widget.choices ?.asMap() .entries .map( (entry) => ChoiceItem( theme: theme, - onLongPress: onLongPress, - onPressed: onPressed, + onLongPress: widget.onLongPress, + onPressed: widget.onPressed, entry: entry, - isSelected: selectedChoiceIndex == entry.key, + interactionDisabled: interactionDisabled, + enableInteraction: enableInteractions, + disableInteraction: disableInteraction, + isSelected: widget.selectedChoiceIndex == entry.key, ), ) .toList() ?? @@ -74,6 +100,9 @@ class ChoiceItem extends StatelessWidget { required this.onPressed, required this.entry, required this.isSelected, + required this.interactionDisabled, + required this.enableInteraction, + required this.disableInteraction, }); final MapEntry entry; @@ -81,6 +110,9 @@ class ChoiceItem extends StatelessWidget { final void Function(int p1)? onLongPress; final void Function(int p1) onPressed; final bool isSelected; + final bool interactionDisabled; + final VoidCallback enableInteraction; + final VoidCallback disableInteraction; @override Widget build(BuildContext context) { @@ -94,6 +126,8 @@ class ChoiceItem extends StatelessWidget { key: ValueKey(entry.value.text), selected: entry.value.color != null, isGold: entry.value.isGold, + enableInteraction: enableInteraction, + disableInteraction: disableInteraction, child: Container( margin: const EdgeInsets.all(2), padding: EdgeInsets.zero, @@ -128,8 +162,9 @@ class ChoiceItem extends StatelessWidget { ), ), onLongPress: - onLongPress != null ? () => onLongPress!(entry.key) : null, - onPressed: () => onPressed(entry.key), + onLongPress != null && !interactionDisabled + ? () => onLongPress!(entry.key) : null, + onPressed: interactionDisabled ? null : () => onPressed(entry.key), child: Text( entry.value.text, style: BotStyle.text(context), @@ -149,11 +184,15 @@ class ChoiceAnimationWidget extends StatefulWidget { final Widget child; final bool selected; final bool isGold; + final VoidCallback enableInteraction; + final VoidCallback disableInteraction; const ChoiceAnimationWidget({ super.key, required this.child, required this.selected, + required this.enableInteraction, + required this.disableInteraction, this.isGold = false, }); @@ -161,11 +200,13 @@ class ChoiceAnimationWidget extends StatefulWidget { ChoiceAnimationWidgetState createState() => ChoiceAnimationWidgetState(); } +enum AnimationState { ready, forward, reverse, finished } + class ChoiceAnimationWidgetState extends State with SingleTickerProviderStateMixin { late final AnimationController _controller; late final Animation _animation; - bool animationPlayed = false; + AnimationState animationState = AnimationState.ready; @override void initState() { @@ -193,17 +234,29 @@ class ChoiceAnimationWidgetState extends State ), ]).animate(_controller); - if (widget.selected && !animationPlayed) { + widget.enableInteraction(); + + if (widget.selected && animationState == AnimationState.ready) { + widget.disableInteraction(); _controller.forward(); - animationPlayed = true; - setState(() {}); + setState(() { + animationState = AnimationState.forward; + }); } _controller.addStatusListener((status) { - if (status == AnimationStatus.completed) { + if (status == AnimationStatus.completed && + animationState == AnimationState.forward) { _controller.reverse(); - } else if (status == AnimationStatus.dismissed) { - _controller.stop(); - _controller.reset(); + setState(() { + animationState = AnimationState.reverse; + }); + } + if (status == AnimationStatus.dismissed && + animationState == AnimationState.reverse) { + widget.enableInteraction(); + setState(() { + animationState = AnimationState.finished; + }); } }); } @@ -211,10 +264,12 @@ class ChoiceAnimationWidgetState extends State @override void didUpdateWidget(ChoiceAnimationWidget oldWidget) { super.didUpdateWidget(oldWidget); - if (widget.selected && !animationPlayed) { + if (widget.selected && animationState == AnimationState.ready) { + widget.disableInteraction(); _controller.forward(); - animationPlayed = true; - setState(() {}); + setState(() { + animationState = AnimationState.forward; + }); } } diff --git a/lib/pangea/controllers/local_settings.dart b/lib/pangea/controllers/local_settings.dart index 5984a7bf5..d6ae119a5 100644 --- a/lib/pangea/controllers/local_settings.dart +++ b/lib/pangea/controllers/local_settings.dart @@ -9,7 +9,8 @@ class LocalSettings { } bool userLanguageToolSetting(ToolSetting setting) => - _pangeaController.pStoreService.read(setting.toString()) ?? true; + _pangeaController.pStoreService.read(setting.toString()) + ?? setting != ToolSetting.itAutoPlay; // bool get userEnableIT => // _pangeaController.pStoreService.read(ToolSetting.interactiveTranslator.toString()) ?? true; diff --git a/lib/pangea/widgets/igc/span_card.dart b/lib/pangea/widgets/igc/span_card.dart index 3d6df9928..75738a687 100644 --- a/lib/pangea/widgets/igc/span_card.dart +++ b/lib/pangea/widgets/igc/span_card.dart @@ -138,7 +138,7 @@ class WordMatchContent extends StatelessWidget { .choices![index] .isBestCorrection ? BotExpression.gold - : BotExpression.nonGold + : BotExpression.surprised ), ); // if (controller.widget.scm.pangeaMatch.match.choices![index].type ==