diff --git a/lib/config/app_config.dart b/lib/config/app_config.dart index d9902f763..d897abb6a 100644 --- a/lib/config/app_config.dart +++ b/lib/config/app_config.dart @@ -31,6 +31,17 @@ abstract class AppConfig { static const double toolbarSpacing = 8.0; static const double toolbarIconSize = 24.0; static const double readingAssistanceInputBarHeight = 140.0; + static const double reactionsPickerHeight = 48.0; + static const double chatInputRowOverlayPadding = 8.0; + static const double tokenModeInputBarHeight = reactionsPickerHeight + + toolbarButtonsHeight + + (chatInputRowOverlayPadding * 2) + + toolbarSpacing; + static const double messageModeInputBarHeight = + readingAssistanceInputBarHeight + + toolbarButtonsHeight + + (chatInputRowOverlayPadding * 2) + + toolbarSpacing; static TextStyle messageTextStyle( Event? event, diff --git a/lib/pages/chat/chat.dart b/lib/pages/chat/chat.dart index fbfb55f1b..a23e37049 100644 --- a/lib/pages/chat/chat.dart +++ b/lib/pages/chat/chat.dart @@ -1881,7 +1881,6 @@ class ChatController extends State context: context, child: overlayEntry!, transformTargetId: "", - backgroundColor: Colors.black, position: OverlayPositionEnum.centered, onDismiss: clearSelectedEvents, blurBackground: true, diff --git a/lib/pages/chat/reactions_picker.dart b/lib/pages/chat/reactions_picker.dart index d2598abfc..10344d873 100644 --- a/lib/pages/chat/reactions_picker.dart +++ b/lib/pages/chat/reactions_picker.dart @@ -24,7 +24,10 @@ class ReactionsPicker extends StatelessWidget { return AnimatedContainer( duration: FluffyThemes.animationDuration, curve: FluffyThemes.animationCurve, - height: (display) ? 56 : 0, + // #Pangea + // height: (display) ? 56 : 0, + height: (display) ? AppConfig.reactionsPickerHeight : 0, + // Pangea# child: Material( color: Colors.transparent, child: Builder( @@ -69,12 +72,19 @@ class ReactionsPicker extends StatelessWidget { borderRadius: BorderRadius.circular(8), onTap: () => controller.sendEmojiAction(emojis[i]), child: Container( - width: 56, - height: 56, + // #Pangea + // width: 56, + // height: 56, + width: AppConfig.reactionsPickerHeight, + height: AppConfig.reactionsPickerHeight, + // Pangea# alignment: Alignment.center, child: Text( emojis[i], - style: const TextStyle(fontSize: 30), + // #Pangea + // style: const TextStyle(fontSize: 30), + style: const TextStyle(fontSize: 20), + // Pangea# ), ), ), @@ -86,7 +96,10 @@ class ReactionsPicker extends StatelessWidget { child: Container( margin: const EdgeInsets.symmetric(horizontal: 8), width: 36, - height: 56, + // #Pangea + // height: 56, + height: AppConfig.reactionsPickerHeight, + // Pangea# decoration: BoxDecoration( color: theme.colorScheme.onInverseSurface, shape: BoxShape.circle, diff --git a/lib/pangea/chat/widgets/pangea_chat_input_row.dart b/lib/pangea/chat/widgets/pangea_chat_input_row.dart index 8e0bf3c5f..93b314598 100644 --- a/lib/pangea/chat/widgets/pangea_chat_input_row.dart +++ b/lib/pangea/chat/widgets/pangea_chat_input_row.dart @@ -136,8 +136,11 @@ class PangeaChatInputRowState extends State { CompositedTransformTarget( link: _controller.choreographer.inputLayerLinkAndKey.link, child: Container( - padding: - EdgeInsets.all(widget.overlayController != null ? 8.0 : 0.0), + padding: EdgeInsets.all( + widget.overlayController != null + ? AppConfig.chatInputRowOverlayPadding + : 0.0, + ), decoration: BoxDecoration( color: widget.overlayController != null ? Theme.of(context).cardColor diff --git a/lib/pangea/toolbar/reading_assistance_input_row/overlay_footer.dart b/lib/pangea/toolbar/reading_assistance_input_row/overlay_footer.dart index 1ea455a6c..4dc09068e 100644 --- a/lib/pangea/toolbar/reading_assistance_input_row/overlay_footer.dart +++ b/lib/pangea/toolbar/reading_assistance_input_row/overlay_footer.dart @@ -4,6 +4,7 @@ import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/config/themes.dart'; import 'package:fluffychat/pages/chat/chat.dart'; import 'package:fluffychat/pangea/chat/widgets/pangea_chat_input_row.dart'; +import 'package:fluffychat/pangea/toolbar/enums/reading_assistance_mode_enum.dart'; import 'package:fluffychat/pangea/toolbar/widgets/message_selection_overlay.dart'; import 'package:fluffychat/pangea/toolbar/widgets/toolbar_button_column.dart'; @@ -11,11 +12,13 @@ class OverlayFooter extends StatelessWidget { final ChatController controller; final MessageOverlayController overlayController; final bool showToolbarButtons; + final ReadingAssistanceMode? readingAssistanceMode; const OverlayFooter({ required this.controller, required this.overlayController, required this.showToolbarButtons, + required this.readingAssistanceMode, super.key, }); @@ -30,12 +33,10 @@ class OverlayFooter extends StatelessWidget { left: bottomSheetPadding, right: bottomSheetPadding, ), - // constraints: const BoxConstraints( - // maxWidth: FluffyThemes.columnWidth * 2.5, - // ), - height: AppConfig.readingAssistanceInputBarHeight + - AppConfig.toolbarButtonsHeight + - 20.0, + height: readingAssistanceMode == ReadingAssistanceMode.messageMode || + readingAssistanceMode == ReadingAssistanceMode.transitionMode + ? AppConfig.messageModeInputBarHeight + : AppConfig.tokenModeInputBarHeight, alignment: Alignment.center, child: Column( mainAxisAlignment: MainAxisAlignment.end, diff --git a/lib/pangea/toolbar/widgets/message_selection_overlay.dart b/lib/pangea/toolbar/widgets/message_selection_overlay.dart index 7c45ce29f..59e180021 100644 --- a/lib/pangea/toolbar/widgets/message_selection_overlay.dart +++ b/lib/pangea/toolbar/widgets/message_selection_overlay.dart @@ -205,7 +205,7 @@ class MessageOverlayController extends State /// Decides whether an _initialSelectedToken should be used /// for a first practice activity on the word meaning - void _initializeSelectedToken() { + Future _initializeSelectedToken() async { // if there is no initial selected token, then we don't need to do anything if (widget._initialSelectedToken == null || practiceSelection == null) { return; @@ -223,6 +223,17 @@ class MessageOverlayController extends State } _updateSelectedSpan(widget._initialSelectedToken!.text); + + int retries = 0; + while (retries < 5 && + selectedToken != null && + !MatrixState.pAnyState.isOverlayOpen( + selectedToken!.text.uniqueKey, + )) { + await Future.delayed(const Duration(milliseconds: 100)); + _showReadingAssistanceContent(); + retries++; + } } ///////////////////////////////////// diff --git a/lib/pangea/toolbar/widgets/message_selection_positioner.dart b/lib/pangea/toolbar/widgets/message_selection_positioner.dart index 9daf90d8e..657e5a5e4 100644 --- a/lib/pangea/toolbar/widgets/message_selection_positioner.dart +++ b/lib/pangea/toolbar/widgets/message_selection_positioner.dart @@ -1,5 +1,6 @@ import 'dart:async'; import 'dart:math'; +import 'dart:ui'; import 'package:flutter/material.dart'; @@ -68,6 +69,7 @@ class MessageSelectionPositionerState extends State Animation? _overlayOffsetAnimation; Animation? _messageSizeAnimation; + Animation? _blurAnimation; Offset? _currentOffset; StreamSubscription? _reactionSubscription; @@ -106,10 +108,19 @@ class MessageSelectionPositionerState extends State }, ).listen((_) => setState(() {})); + _blurAnimation = Tween(begin: 0.0, end: 2.5).animate( + CurvedAnimation( + parent: _animationController, + curve: FluffyThemes.animationCurve, + ), + ); + WidgetsBinding.instance.addPostFrameCallback((_) async { await _centeredMessageCompleter.future; if (!mounted) return; + _animationController.forward(from: 0); + setState(() { _currentOffset = Offset( _ownMessage ? _messageRightOffset : _messageLeftOffset, @@ -152,7 +163,10 @@ class MessageSelectionPositionerState extends State _mediaQuery!.size.height - offset.dy - renderBox.size.height - - _reactionsHeight, + _reactionsHeight + + ((AppConfig.messageModeInputBarHeight - + AppConfig.tokenModeInputBarHeight) * + 0.75), ); setState(() {}); @@ -254,9 +268,11 @@ class MessageSelectionPositionerState extends State } } - final double _inputBarSize = AppConfig.readingAssistanceInputBarHeight + - AppConfig.toolbarButtonsHeight + - 20.0; + double get _inputBarSize => + _readingAssistanceMode == ReadingAssistanceMode.messageMode || + _readingAssistanceMode == ReadingAssistanceMode.transitionMode + ? AppConfig.messageModeInputBarHeight + : AppConfig.tokenModeInputBarHeight; bool get _showDetails => (Matrix.of(context).store.getBool(SettingKeys.displayChatDetailsColumn) ?? @@ -487,6 +503,17 @@ class MessageSelectionPositionerState extends State bool get _ownMessage => widget.event.senderId == widget.event.room.client.userID; + double get _readingAssistanceModeOpacity { + switch (_readingAssistanceMode) { + case ReadingAssistanceMode.messageMode: + case ReadingAssistanceMode.transitionMode: + return 0.8; + case ReadingAssistanceMode.tokenMode: + case null: + return 0.4; + } + } + @override Widget build(BuildContext context) { if (_messageRenderBox == null || _mediaQuery == null) { @@ -495,169 +522,204 @@ class MessageSelectionPositionerState extends State widget.overlayController.maxWidth = _toolbarMaxWidth; - return Padding( - padding: EdgeInsets.only( - left: _horizontalPadding, - right: _horizontalPadding, - ), - child: Stack( - alignment: Alignment.center, - children: [ - Column( - children: [ - Material( - type: MaterialType.transparency, - child: Column( - children: [ - SizedBox(height: _mediaQuery?.padding.top ?? 0), - OverlayHeader(controller: widget.chatController), - ], - ), - ), - const Expanded( - flex: 3, - child: SizedBox.shrink(), - ), - Opacity( - opacity: - _readingAssistanceMode == ReadingAssistanceMode.messageMode - ? 1.0 - : 0.0, - child: OverlayCenterContent( - event: widget.event, - messageHeight: null, - messageWidth: null, - maxWidth: widget.overlayController.maxWidth, - overlayController: widget.overlayController, - chatController: widget.chatController, - pangeaMessageEvent: widget.pangeaMessageEvent, - nextEvent: widget.nextEvent, - prevEvent: widget.prevEvent, - hasReactions: _hasReactions, - onChangeMessageSize: _setCenteredMessageSize, - isTransitionAnimation: false, - maxHeight: _mediaQuery!.size.height - - _headerHeight - - _footerHeight - - AppConfig.toolbarSpacing * 2, - readingAssistanceMode: _readingAssistanceMode, - ), - ), - const Expanded( - flex: 1, - child: SizedBox.shrink(), - ), - Row( - mainAxisSize: MainAxisSize.min, - children: [ - Expanded( - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - OverlayFooter( - controller: widget.chatController, - overlayController: widget.overlayController, - showToolbarButtons: showToolbarButtons, + return Stack( + children: [ + if (_blurAnimation != null) + IgnorePointer( + child: AnimatedOpacity( + duration: _animationDuration, + opacity: _readingAssistanceModeOpacity, + child: Positioned.fill( + child: DecoratedBox( + decoration: const BoxDecoration( + color: Colors.black, + ), + child: AnimatedBuilder( + animation: _blurAnimation!, + builder: (context, _) { + return BackdropFilter( + filter: ImageFilter.blur( + sigmaX: _blurAnimation!.value, + sigmaY: _blurAnimation!.value, ), - SizedBox(height: _mediaQuery?.padding.bottom ?? 0), + child: Container( + height: double.infinity, + width: double.infinity, + color: Colors.transparent, + ), + ); + }, + ), + ), + ), + ), + ), + Padding( + padding: EdgeInsets.only( + left: _horizontalPadding, + right: _horizontalPadding, + ), + child: Stack( + alignment: Alignment.center, + children: [ + Column( + children: [ + Material( + type: MaterialType.transparency, + child: Column( + children: [ + SizedBox(height: _mediaQuery?.padding.top ?? 0), + OverlayHeader(controller: widget.chatController), ], ), ), - if (_showDetails) - const SizedBox( - width: FluffyThemes.columnWidth, + const Expanded( + flex: 3, + child: SizedBox.shrink(), + ), + Opacity( + opacity: _readingAssistanceMode == + ReadingAssistanceMode.messageMode + ? 1.0 + : 0.0, + child: OverlayCenterContent( + event: widget.event, + messageHeight: null, + messageWidth: null, + maxWidth: widget.overlayController.maxWidth, + overlayController: widget.overlayController, + chatController: widget.chatController, + pangeaMessageEvent: widget.pangeaMessageEvent, + nextEvent: widget.nextEvent, + prevEvent: widget.prevEvent, + hasReactions: _hasReactions, + onChangeMessageSize: _setCenteredMessageSize, + isTransitionAnimation: false, + maxHeight: _mediaQuery!.size.height - + _headerHeight - + _footerHeight - + AppConfig.toolbarSpacing * 2, + readingAssistanceMode: _readingAssistanceMode, ), + ), + const Expanded( + flex: 1, + child: SizedBox.shrink(), + ), + Row( + mainAxisSize: MainAxisSize.min, + children: [ + Expanded( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + OverlayFooter( + controller: widget.chatController, + overlayController: widget.overlayController, + showToolbarButtons: showToolbarButtons, + readingAssistanceMode: _readingAssistanceMode, + ), + SizedBox(height: _mediaQuery?.padding.bottom ?? 0), + ], + ), + ), + if (_showDetails) + const SizedBox( + width: FluffyThemes.columnWidth, + ), + ], + ), ], ), - ], - ), - if (_readingAssistanceMode != ReadingAssistanceMode.messageMode) - AnimatedBuilder( - animation: _overlayOffsetAnimation ?? _animationController, - builder: (context, child) { - return Positioned( - left: _ownMessage - ? null - : (_overlayOffsetAnimation?.value)?.dx ?? - _messageLeftOffset, - right: _ownMessage - ? (_overlayOffsetAnimation?.value)?.dx ?? - _messageRightOffset - : null, - bottom: (_overlayOffsetAnimation?.value)?.dy ?? - _originalMessageBottomOffset - _reactionsHeight, - child: OverlayCenterContent( - event: widget.event, - messageHeight: _originalMessageSize.height, - messageWidth: _originalMessageSize.width, - maxWidth: widget.overlayController.maxWidth, - overlayController: widget.overlayController, - chatController: widget.chatController, - pangeaMessageEvent: widget.pangeaMessageEvent, - nextEvent: widget.nextEvent, - prevEvent: widget.prevEvent, - hasReactions: _hasReactions, - sizeAnimation: _messageSizeAnimation, - isTransitionAnimation: true, - maxHeight: _mediaQuery!.size.height - - _headerHeight - - _footerHeight - - AppConfig.toolbarSpacing * 2, - readingAssistanceMode: _readingAssistanceMode, - ), - ); - }, - ), - if (showToolbarButtons) - Positioned( - top: 0, - child: IgnorePointer( - child: MeasureRenderBox( - onChange: _setTooltipSize, - child: Opacity( - opacity: 0.0, - child: Container( - constraints: BoxConstraints( - minWidth: 200.0, - maxWidth: _toolbarMaxWidth, + if (_readingAssistanceMode != ReadingAssistanceMode.messageMode) + AnimatedBuilder( + animation: _overlayOffsetAnimation ?? _animationController, + builder: (context, child) { + return Positioned( + left: _ownMessage + ? null + : (_overlayOffsetAnimation?.value)?.dx ?? + _messageLeftOffset, + right: _ownMessage + ? (_overlayOffsetAnimation?.value)?.dx ?? + _messageRightOffset + : null, + bottom: (_overlayOffsetAnimation?.value)?.dy ?? + _originalMessageBottomOffset - _reactionsHeight, + child: OverlayCenterContent( + event: widget.event, + messageHeight: _originalMessageSize.height, + messageWidth: _originalMessageSize.width, + maxWidth: widget.overlayController.maxWidth, + overlayController: widget.overlayController, + chatController: widget.chatController, + pangeaMessageEvent: widget.pangeaMessageEvent, + nextEvent: widget.nextEvent, + prevEvent: widget.prevEvent, + hasReactions: _hasReactions, + sizeAnimation: _messageSizeAnimation, + isTransitionAnimation: true, + maxHeight: _mediaQuery!.size.height - + _headerHeight - + _footerHeight - + AppConfig.toolbarSpacing * 2, + readingAssistanceMode: _readingAssistanceMode, ), - child: InstructionsInlineTooltip( - instructionsEnum: widget.overlayController.toolbarMode - .instructionsEnum ?? - InstructionsEnum.readingAssistanceOverview, - bold: true, + ); + }, + ), + if (showToolbarButtons) + Positioned( + top: 0, + child: IgnorePointer( + child: MeasureRenderBox( + onChange: _setTooltipSize, + child: Opacity( + opacity: 0.0, + child: Container( + constraints: BoxConstraints( + minWidth: 200.0, + maxWidth: _toolbarMaxWidth, + ), + child: InstructionsInlineTooltip( + instructionsEnum: widget.overlayController + .toolbarMode.instructionsEnum ?? + InstructionsEnum.readingAssistanceOverview, + bold: true, + ), + ), ), ), ), ), - ), - ), - if (_centeredMessageTopOffset != null && - _tooltipSize != null && - widget.overlayController.toolbarMode != - MessageMode.noneSelected && - widget.overlayController.selectedToken == null) - Positioned( - top: max( - ((_headerHeight + _centeredMessageTopOffset!) / 2) - - (_tooltipSize!.height / 2), - _headerHeight, - ), - child: Container( - constraints: BoxConstraints( - minWidth: 200.0, - maxWidth: widget.overlayController.maxWidth, - ), - child: InstructionsInlineTooltip( - instructionsEnum: - widget.overlayController.toolbarMode.instructionsEnum ?? + if (_centeredMessageTopOffset != null && + _tooltipSize != null && + widget.overlayController.toolbarMode != + MessageMode.noneSelected && + widget.overlayController.selectedToken == null) + Positioned( + top: max( + ((_headerHeight + _centeredMessageTopOffset!) / 2) - + (_tooltipSize!.height / 2), + _headerHeight, + ), + child: Container( + constraints: BoxConstraints( + minWidth: 200.0, + maxWidth: widget.overlayController.maxWidth, + ), + child: InstructionsInlineTooltip( + instructionsEnum: widget + .overlayController.toolbarMode.instructionsEnum ?? InstructionsEnum.readingAssistanceOverview, - bold: true, + bold: true, + ), + ), ), - ), - ), - ], - ), + ], + ), + ), + ], ); } } diff --git a/lib/pangea/toolbar/widgets/toolbar_button_column.dart b/lib/pangea/toolbar/widgets/toolbar_button_column.dart index 903aeb9da..b1cf8907d 100644 --- a/lib/pangea/toolbar/widgets/toolbar_button_column.dart +++ b/lib/pangea/toolbar/widgets/toolbar_button_column.dart @@ -1,6 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:fluffychat/config/app_config.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'; @@ -20,70 +19,73 @@ class ToolbarButtonRow extends StatelessWidget { @override Widget build(BuildContext context) { - return SizedBox( - height: AppConfig.toolbarButtonsHeight, - child: Row( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisSize: MainAxisSize.min, - spacing: 4.0, - children: [ - Container( - width: buttonSize + 4, - height: buttonSize + 4, - alignment: Alignment.center, - child: ToolbarButton( - mode: MessageMode.listening, - overlayController: overlayController, - onPressed: overlayController.updateToolbarMode, - buttonSize: buttonSize, + return Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Row( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisSize: MainAxisSize.min, + spacing: 4.0, + children: [ + Container( + width: buttonSize + 4, + height: buttonSize + 4, + alignment: Alignment.center, + child: ToolbarButton( + mode: MessageMode.listening, + overlayController: overlayController, + onPressed: overlayController.updateToolbarMode, + buttonSize: buttonSize, + ), ), - ), - Container( - width: buttonSize + 4, - height: buttonSize + 4, - alignment: Alignment.center, - child: ToolbarButton( - mode: MessageMode.wordMorph, - overlayController: overlayController, - onPressed: overlayController.updateToolbarMode, - buttonSize: buttonSize, + Container( + width: buttonSize + 4, + height: buttonSize + 4, + alignment: Alignment.center, + child: ToolbarButton( + mode: MessageMode.wordMorph, + overlayController: overlayController, + onPressed: overlayController.updateToolbarMode, + buttonSize: buttonSize, + ), ), - ), - Container( - width: buttonSize + 4, - height: buttonSize + 4, - alignment: Alignment.center, - child: ToolbarButton( - mode: MessageMode.messageTranslation, - overlayController: overlayController, - onPressed: overlayController.updateToolbarMode, - buttonSize: buttonSize, + Container( + width: buttonSize + 4, + height: buttonSize + 4, + alignment: Alignment.center, + child: ToolbarButton( + mode: MessageMode.messageTranslation, + overlayController: overlayController, + onPressed: overlayController.updateToolbarMode, + buttonSize: buttonSize, + ), ), - ), - Container( - width: buttonSize + 4, - height: buttonSize + 4, - alignment: Alignment.center, - child: ToolbarButton( - mode: MessageMode.wordMeaning, - overlayController: overlayController, - onPressed: overlayController.updateToolbarMode, - buttonSize: buttonSize, + Container( + width: buttonSize + 4, + height: buttonSize + 4, + alignment: Alignment.center, + child: ToolbarButton( + mode: MessageMode.wordMeaning, + overlayController: overlayController, + onPressed: overlayController.updateToolbarMode, + buttonSize: buttonSize, + ), ), - ), - Container( - width: buttonSize + 4, - height: buttonSize + 4, - alignment: Alignment.center, - child: ToolbarButton( - mode: MessageMode.wordEmoji, - overlayController: overlayController, - onPressed: overlayController.updateToolbarMode, - buttonSize: buttonSize, + Container( + width: buttonSize + 4, + height: buttonSize + 4, + alignment: Alignment.center, + child: ToolbarButton( + mode: MessageMode.wordEmoji, + overlayController: overlayController, + onPressed: overlayController.updateToolbarMode, + buttonSize: buttonSize, + ), ), - ), - ], - ), + ], + ), + ], ); } }