From 592aa43089ed4451ad103cfe570887fcbbf91310 Mon Sep 17 00:00:00 2001 From: ggurdin <46800240+ggurdin@users.noreply.github.com> Date: Tue, 25 Mar 2025 14:40:18 -0400 Subject: [PATCH] 2202 positioning to dos in reading assistance (#2214) * chore: move toolbar buttons above reading assistance input bar * chore: positioning on message relative to header and footer / positioning of tooltip between message and header * chore: update inline tooltip color * chore: animate reading assistance input bar height --- assets/l10n/intl_en.arb | 2 + lib/config/app_config.dart | 1 + lib/pages/chat/chat_input_row.dart | 3 + lib/pages/chat/events/message_content.dart | 2 +- .../instructions_inline_tooltip.dart | 5 +- .../reset_instructions_list_tile.dart | 45 +-- .../pages/settings_learning.dart | 5 +- .../dotted_border_painter.dart | 3 +- .../overlay_footer.dart | 13 + .../reading_assistance_input_bar.dart | 17 +- .../toolbar/widgets/measure_render_box.dart | 10 +- .../widgets/message_selection_overlay.dart | 26 +- .../widgets/message_selection_positioner.dart | 350 ++++++++++-------- .../widgets/overlay_center_content.dart | 121 +++--- .../toolbar/widgets/overlay_message.dart | 35 +- .../widgets/toolbar_button_column.dart | 132 +++---- 16 files changed, 407 insertions(+), 363 deletions(-) diff --git a/assets/l10n/intl_en.arb b/assets/l10n/intl_en.arb index eebdfea0a..82ac9ca96 100644 --- a/assets/l10n/intl_en.arb +++ b/assets/l10n/intl_en.arb @@ -4831,5 +4831,7 @@ "referFriendDialogTitle": "Invite a friend to your conversation", "referFriendDialogDesc": "Do you have a friend who is excited to learn a new language with you? Then copy and send this invitation link to join and start chatting with you today.", "youUnlocked": "You've unlocked", + "resetInstructionTooltipsTitle": "Reset instruction tooltips", + "resetInstructionTooltipsDesc": "Click to show instruction tooltips like for a brand new user.", "selectForGrammar": "Select a grammar icon for activities and details." } \ No newline at end of file diff --git a/lib/config/app_config.dart b/lib/config/app_config.dart index 36209f5b0..5e098c190 100644 --- a/lib/config/app_config.dart +++ b/lib/config/app_config.dart @@ -28,6 +28,7 @@ abstract class AppConfig { static const double toolbarMinWidth = 350.0; static const double defaultHeaderHeight = 56.0; static const double readingAssistanceInputBarHeight = 170; + static const double toolbarButtonsHeight = 100.0; static const double toolbarSpacing = 8.0; static const double toolbarIconSize = 24.0; diff --git a/lib/pages/chat/chat_input_row.dart b/lib/pages/chat/chat_input_row.dart index f90c37810..62bb9ecc3 100644 --- a/lib/pages/chat/chat_input_row.dart +++ b/lib/pages/chat/chat_input_row.dart @@ -75,6 +75,9 @@ class ChatInputRow extends StatelessWidget { } return Column( + // #Pangea + mainAxisSize: MainAxisSize.min, + // Pangea# children: [ // if (!controller.selectMode) WritingAssistanceInputRow(controller), CompositedTransformTarget( diff --git a/lib/pages/chat/events/message_content.dart b/lib/pages/chat/events/message_content.dart index 24d597b00..19cc32e74 100644 --- a/lib/pages/chat/events/message_content.dart +++ b/lib/pages/chat/events/message_content.dart @@ -382,7 +382,7 @@ class MessageContent extends StatelessWidget { isSelected: overlayController != null ? isSelected : null, messageMode: overlayController?.toolbarMode, isHighlighted: (PangeaToken token) => - overlayController!.toolbarMode.associatedActivityType != + overlayController?.toolbarMode.associatedActivityType != null && overlayController?.messageAnalyticsEntry?.hasActivity( overlayController! diff --git a/lib/pangea/instructions/instructions_inline_tooltip.dart b/lib/pangea/instructions/instructions_inline_tooltip.dart index 584736ad8..0f664a225 100644 --- a/lib/pangea/instructions/instructions_inline_tooltip.dart +++ b/lib/pangea/instructions/instructions_inline_tooltip.dart @@ -86,7 +86,10 @@ class InstructionsInlineTooltipState extends State child: DecoratedBox( decoration: BoxDecoration( borderRadius: BorderRadius.circular(AppConfig.borderRadius), - color: AppConfig.gold.withAlpha(widget.bold ? 80 : 10), + color: Color.alphaBlend( + Colors.black.withAlpha(70), + AppConfig.gold, + ), ), child: Padding( padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 8), diff --git a/lib/pangea/instructions/reset_instructions_list_tile.dart b/lib/pangea/instructions/reset_instructions_list_tile.dart index a6cdf0b11..3e20f68e2 100644 --- a/lib/pangea/instructions/reset_instructions_list_tile.dart +++ b/lib/pangea/instructions/reset_instructions_list_tile.dart @@ -1,6 +1,9 @@ import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/l10n.dart'; + import 'package:fluffychat/pangea/learning_settings/pages/settings_learning.dart'; +import 'package:fluffychat/widgets/adaptive_dialogs/show_ok_cancel_alert_dialog.dart'; class ResetInstructionsListTile extends StatelessWidget { const ResetInstructionsListTile({ @@ -15,37 +18,21 @@ class ResetInstructionsListTile extends StatelessWidget { //TODO: add to L10n return ListTile( leading: const Icon(Icons.lightbulb), - title: const Text( - "Reset instruction tooltips", + title: Text( + L10n.of(context).resetInstructionTooltipsTitle, ), - subtitle: const Text( - "Click to show instruction tooltips like for a brand new user.", - ), - onTap: () => showAdaptiveDialog( - context: context, - builder: (context) { - return AlertDialog( - title: const Text( - "Reset instruction tooltips?", - ), - actions: [ - TextButton( - onPressed: () { - controller.resetInstructionTooltips(); - Navigator.of(context).pop(); - }, - child: const Text("Reset"), - ), - TextButton( - onPressed: () { - Navigator.of(context).pop(); - }, - child: const Text("Cancel"), - ), - ], - ); - }, + subtitle: Text( + L10n.of(context).resetInstructionTooltipsDesc, ), + onTap: () async { + final resp = await showOkCancelAlertDialog( + context: context, + title: L10n.of(context).areYouSure, + ); + if (resp == OkCancelResult.ok) { + controller.resetInstructionTooltips(); + } + }, ); } } diff --git a/lib/pangea/learning_settings/pages/settings_learning.dart b/lib/pangea/learning_settings/pages/settings_learning.dart index 4aa83ac89..8bd97840f 100644 --- a/lib/pangea/learning_settings/pages/settings_learning.dart +++ b/lib/pangea/learning_settings/pages/settings_learning.dart @@ -137,7 +137,10 @@ class SettingsLearningController extends State { debugPrint("Error resetting instruction tooltips: $e"); debugger(when: kDebugMode); ErrorHandler.logError( - e: e, s: s, data: {"resetInstructionTooltips": true}); + e: e, + s: s, + data: {"resetInstructionTooltips": true}, + ); return null; }, ); diff --git a/lib/pangea/message_token_text/dotted_border_painter.dart b/lib/pangea/message_token_text/dotted_border_painter.dart index a52879ab0..7259ecce2 100644 --- a/lib/pangea/message_token_text/dotted_border_painter.dart +++ b/lib/pangea/message_token_text/dotted_border_painter.dart @@ -24,7 +24,8 @@ class DottedBorderPainter extends CustomPainter { final path = Path() ..addRRect( - borderRadius.toRRect(Rect.fromLTWH(0, 0, size.width, size.height))); + borderRadius.toRRect(Rect.fromLTWH(0, 0, size.width, size.height)), + ); final dashPath = Path(); final pathMetrics = path.computeMetrics(); 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 636ac2417..b49961615 100644 --- a/lib/pangea/toolbar/reading_assistance_input_row/overlay_footer.dart +++ b/lib/pangea/toolbar/reading_assistance_input_row/overlay_footer.dart @@ -1,10 +1,13 @@ import 'package:flutter/material.dart'; +import 'package:matrix/matrix_api_lite/model/message_types.dart'; + import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/config/themes.dart'; import 'package:fluffychat/pages/chat/chat.dart'; import 'package:fluffychat/pages/chat/chat_input_row.dart'; import 'package:fluffychat/pangea/toolbar/widgets/message_selection_overlay.dart'; +import 'package:fluffychat/pangea/toolbar/widgets/toolbar_button_column.dart'; class OverlayFooter extends StatelessWidget { final ChatController controller; @@ -16,6 +19,12 @@ class OverlayFooter extends StatelessWidget { super.key, }); + bool get showToolbarButtons => + overlayController.pangeaMessageEvent != null && + overlayController.pangeaMessageEvent!.shouldShowToolbar && + overlayController.pangeaMessageEvent!.event.messageType == + MessageTypes.Text; + @override Widget build(BuildContext context) { //@ggurdin can we change this mobile padding to 0? seems a some extrea space on mobile @@ -33,6 +42,10 @@ class OverlayFooter extends StatelessWidget { alignment: Alignment.center, child: Column( children: [ + ToolbarButtonRow( + overlayController: overlayController, + shouldShowToolbarButtons: showToolbarButtons, + ), Material( clipBehavior: Clip.hardEdge, color: Theme.of(context).colorScheme.surfaceContainerHighest, diff --git a/lib/pangea/toolbar/reading_assistance_input_row/reading_assistance_input_bar.dart b/lib/pangea/toolbar/reading_assistance_input_row/reading_assistance_input_bar.dart index 0236a712b..0f0190c64 100644 --- a/lib/pangea/toolbar/reading_assistance_input_row/reading_assistance_input_bar.dart +++ b/lib/pangea/toolbar/reading_assistance_input_row/reading_assistance_input_bar.dart @@ -110,17 +110,24 @@ class ReadingAssistanceInputBar extends StatelessWidget { return Expanded( child: Container( - height: AppConfig.readingAssistanceInputBarHeight, - padding: const EdgeInsets.all(8.0), + constraints: BoxConstraints( + maxHeight: (MediaQuery.of(context).size.height / 2) - + AppConfig.toolbarButtonsHeight, + ), + padding: const EdgeInsets.all(16.0), decoration: BoxDecoration( color: Theme.of(context).cardColor, borderRadius: const BorderRadius.all( Radius.circular(8.0), ), ), - alignment: Alignment.center, - child: SingleChildScrollView( - child: barContent(context), + child: AnimatedSize( + duration: const Duration( + milliseconds: AppConfig.overlayAnimationDuration, + ), + child: SingleChildScrollView( + child: barContent(context), + ), ), ), ); diff --git a/lib/pangea/toolbar/widgets/measure_render_box.dart b/lib/pangea/toolbar/widgets/measure_render_box.dart index 4deea10e9..85bbed893 100644 --- a/lib/pangea/toolbar/widgets/measure_render_box.dart +++ b/lib/pangea/toolbar/widgets/measure_render_box.dart @@ -38,6 +38,14 @@ class MeasureRenderBoxState extends State { @override Widget build(BuildContext context) { WidgetsBinding.instance.addPostFrameCallback((_) => _updateOffset()); - return widget.child; + return NotificationListener( + onNotification: (notification) { + WidgetsBinding.instance.addPostFrameCallback((_) => _updateOffset()); + return true; + }, + child: SizeChangedLayoutNotifier( + child: widget.child, + ), + ); } } diff --git a/lib/pangea/toolbar/widgets/message_selection_overlay.dart b/lib/pangea/toolbar/widgets/message_selection_overlay.dart index 6cce65962..058cbdc15 100644 --- a/lib/pangea/toolbar/widgets/message_selection_overlay.dart +++ b/lib/pangea/toolbar/widgets/message_selection_overlay.dart @@ -290,20 +290,22 @@ class MessageOverlayController extends State pangeaMessageEvent: pangeaMessageEvent!, overlayController: this, ); - OverlayUtil.showPositionedCard( - context: context, - cardToShow: entry, - transformTargetId: selectedToken!.text.uniqueKey, - closePrevOverlay: false, - backDropToDismiss: false, - addBorder: false, - overlayKey: selectedToken!.text.uniqueKey, - maxHeight: AppConfig.toolbarMaxHeight, - maxWidth: AppConfig.toolbarMinWidth, - ); + if (context.mounted) { + OverlayUtil.showPositionedCard( + context: context, + cardToShow: entry, + transformTargetId: selectedToken!.text.uniqueKey, + closePrevOverlay: false, + backDropToDismiss: false, + addBorder: false, + overlayKey: selectedToken!.text.uniqueKey, + maxHeight: AppConfig.toolbarMaxHeight, + maxWidth: AppConfig.toolbarMinWidth, + ); + } } - setState(() {}); + if (mounted) setState(() {}); } void updateToolbarMode(MessageMode mode) => setState(() { diff --git a/lib/pangea/toolbar/widgets/message_selection_positioner.dart b/lib/pangea/toolbar/widgets/message_selection_positioner.dart index 8506914c4..0d0181b2f 100644 --- a/lib/pangea/toolbar/widgets/message_selection_positioner.dart +++ b/lib/pangea/toolbar/widgets/message_selection_positioner.dart @@ -13,13 +13,14 @@ 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/extensions/pangea_event_extension.dart'; import 'package:fluffychat/pangea/events/models/pangea_token_model.dart'; +import 'package:fluffychat/pangea/instructions/instructions_enum.dart'; import 'package:fluffychat/pangea/instructions/instructions_inline_tooltip.dart'; import 'package:fluffychat/pangea/toolbar/enums/message_mode_enum.dart'; import 'package:fluffychat/pangea/toolbar/reading_assistance_input_row/overlay_footer.dart'; +import 'package:fluffychat/pangea/toolbar/widgets/measure_render_box.dart'; import 'package:fluffychat/pangea/toolbar/widgets/message_selection_overlay.dart'; import 'package:fluffychat/pangea/toolbar/widgets/overlay_center_content.dart'; import 'package:fluffychat/pangea/toolbar/widgets/overlay_header.dart'; -import 'package:fluffychat/pangea/toolbar/widgets/toolbar_button_column.dart'; import 'package:fluffychat/widgets/avatar.dart'; import 'package:fluffychat/widgets/matrix.dart'; @@ -54,21 +55,18 @@ class MessageSelectionPositionerState extends State with TickerProviderStateMixin { late AnimationController _animationController; Animation? _overlayOffsetAnimation; - Animation? _buttonsOffsetAnimation; Animation? _messageSizeAnimation; StreamSubscription? _reactionSubscription; - /// if the message height is too tall to fit with the tools, adjust the message height - double? _adjustedMessageHeight; - Offset? _centeredMessageOffset; Size? _centeredMessageSize; - final Completer _centeredMessageCompleter = Completer(); - Offset? _centeredButtonsOffset; - Size? _centeredButtonsSize; - final Completer _centeredButtonsCompleter = Completer(); + Size? _tooltipSize; + Size? _inputBarSize; + + final Completer _centeredMessageCompleter = Completer(); + final Completer _tooltipCompleter = Completer(); bool _finishedAnimation = false; @@ -105,7 +103,7 @@ class MessageSelectionPositionerState extends State Future.wait([ _centeredMessageCompleter.future, - _centeredButtonsCompleter.future, + _tooltipCompleter.future, ]).then((_) => _startAnimation()); } @@ -131,7 +129,6 @@ class MessageSelectionPositionerState extends State } void _setCenteredMessageSize(RenderBox renderBox) { - if (_finishedAnimation) return; _centeredMessageSize = renderBox.size; final offset = renderBox.localToGlobal(Offset.zero); _centeredMessageOffset = Offset( @@ -148,58 +145,32 @@ class MessageSelectionPositionerState extends State } } - void _setCenteredButtonsSize(RenderBox renderBox) { - if (_finishedAnimation) return; - _centeredButtonsSize = renderBox.size; - final offset = renderBox.localToGlobal(Offset.zero); - _centeredButtonsOffset = Offset( - offset.dx - _columnWidth - _horizontalPadding - 2.0, - offset.dy, - ); - setState(() {}); + void _setTooltipSize(RenderBox renderBox) { + setState(() { + _tooltipSize = renderBox.size; + }); - if (!_centeredButtonsCompleter.isCompleted) { - _centeredButtonsCompleter.complete(); + if (!_tooltipCompleter.isCompleted) { + _tooltipCompleter.complete(); } } + void _setInputBarSize(RenderBox renderBox) { + setState(() => _inputBarSize = renderBox.size); + } + void _startAnimation() { if (_mediaQuery == null) { return; } - // if the message height is too tall to fit, adjust the message height - if (_totalVerticalSpace! < _maxTotalToolbarHeight) { - _adjustedMessageHeight = _totalVerticalSpace! - - // one for within the toolbar itself, one for the top, and one for the bottom - ((AppConfig.toolbarSpacing * 3) + - _reactionsHeight + - AppConfig.toolbarMaxHeight); - _adjustedMessageHeight = max(_adjustedMessageHeight!, 0); - } - _overlayOffsetAnimation = Tween( begin: Offset( _ownMessage ? _messageRightOffset : _messageLeftOffset, - _totalToolbarBottomOffset, + _messageBottomOffset - _reactionsHeight, ), // For own messages, dx is the right offset. For other's messages, dx is the left offset. - end: _centeredMessageOffset, - ).animate( - CurvedAnimation( - parent: _animationController, - curve: FluffyThemes.animationCurve, - ), - ); - - _buttonsOffsetAnimation = Tween( - begin: Offset( - _ownMessage - ? (_centeredButtonsSize!.width * -1) - : _centeredButtonsSize!.width + _mediaQuery!.size.width, - _totalToolbarBottomOffset, - ), - end: _centeredButtonsOffset, + end: _adjustedCenteredMessageOffset, ).animate( CurvedAnimation( parent: _animationController, @@ -210,9 +181,9 @@ class MessageSelectionPositionerState extends State _messageSizeAnimation = Tween( begin: Size( _messageSize.width, - _messageHeight, + _originalMessageHeight, ), - end: _centeredMessageSize, + end: _adjustedCenteredMessageSize, ).animate( CurvedAnimation( parent: _animationController, @@ -220,16 +191,6 @@ class MessageSelectionPositionerState extends State ), ); - // _contentSizeAnimation = Tween( - // begin: 0, - // end: 1, - // ).animate( - // CurvedAnimation( - // parent: _animationController, - // curve: FluffyThemes.animationCurve, - // ), - // ); - _animationController.forward().then((_) { _finishedAnimation = true; if (mounted) setState(() {}); @@ -296,7 +257,43 @@ class MessageSelectionPositionerState extends State ); } - double get _messageHeight => _adjustedMessageHeight ?? _messageSize.height; + double get _originalMessageHeight => _messageSize.height; + + double? get _centerSpace { + if (_mediaQuery == null) return null; + return _mediaQuery!.size.height - _headerHeight - _footerHeight; + } + + bool get _centeredMessageHasOverflow { + if (_centerSpace == null || + _centeredMessageSize == null || + _centeredMessageOffset == null) { + return false; + } + + final finalMessageHeight = _centeredMessageSize!.height + _reactionsHeight; + return finalMessageHeight > _centerSpace!; + } + + Size? get _adjustedCenteredMessageSize { + if (_centeredMessageHasOverflow) { + return Size( + _centeredMessageSize!.width, + _centerSpace! - (AppConfig.toolbarSpacing * 2), + ); + } + return _centeredMessageSize; + } + + Offset? get _adjustedCenteredMessageOffset { + if (_centeredMessageHasOverflow) { + return Offset( + _centeredMessageOffset!.dx, + _footerHeight + AppConfig.toolbarSpacing, + ); + } + return _centeredMessageOffset; + } //TODO: figure out where the 16 and 8 come from and use references instead of hard-coded values static const _messageDefaultLeftMargin = Avatar.defaultSize + 16 + 8; @@ -338,7 +335,19 @@ class MessageSelectionPositionerState extends State } double get _messageBottomOffset => - _mediaQuery!.size.height - _messageOffset.dy - _messageHeight; + _mediaQuery!.size.height - _messageOffset.dy - _originalMessageHeight; + + double? get _centeredMessageTopOffset { + if (_mediaQuery == null || + _adjustedCenteredMessageOffset == null || + _adjustedCenteredMessageSize == null) { + return null; + } + return _mediaQuery!.size.height - + _adjustedCenteredMessageOffset!.dy - + _adjustedCenteredMessageSize!.height - + _reactionsHeight; + } double get _messageLeftOffset => max( _messageOffset.dx - _columnWidth - _horizontalPadding, @@ -367,18 +376,11 @@ class MessageSelectionPositionerState extends State } double get _footerHeight { - return AppConfig.readingAssistanceInputBarHeight + - (FluffyThemes.isColumnMode(context) ? 16.0 : 8.0) + + return (_inputBarSize?.height ?? + (showToolbarButtons ? AppConfig.toolbarButtonsHeight : 0)) + (_mediaQuery?.padding.bottom ?? 0); } - double? get _totalVerticalSpace { - if (_mediaQuery == null) { - return null; - } - return _mediaQuery!.size.height - _headerHeight - _footerHeight; - } - // measurement for items in the toolbar bool get showToolbarButtons => @@ -396,17 +398,6 @@ class MessageSelectionPositionerState extends State double get _reactionsHeight => _hasReactions ? 28 : 0; - double get _maxTotalCenterHeight => - _reactionsHeight + - _messageHeight + - AppConfig.toolbarSpacing + - AppConfig.toolbarMaxHeight; - - double get _maxTotalToolbarHeight => _maxTotalCenterHeight; - - double get _totalToolbarBottomOffset => - _messageBottomOffset - _reactionsHeight; - bool get _ownMessage => widget.event.senderId == widget.event.room.client.userID; @@ -424,24 +415,75 @@ class MessageSelectionPositionerState extends State child: Stack( alignment: Alignment.center, children: [ - Opacity( - opacity: _finishedAnimation ? 1.0 : 0.0, - child: OverlayCenterContent( - event: widget.event, - messageHeight: _messageHeight, - messageWidth: _messageSize.width, - toolbarMaxWidth: _toolbarMaxWidth, - overlayController: widget.overlayController, - chatController: widget.chatController, - pangeaMessageEvent: widget.pangeaMessageEvent, - nextEvent: widget.nextEvent, - prevEvent: widget.prevEvent, - showToolbarButtons: showToolbarButtons, - hasReactions: _hasReactions, - onChangeMessageSize: _setCenteredMessageSize, - onChangeButtonsSize: _setCenteredButtonsSize, - isTransitionAnimation: false, - ), + 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: _finishedAnimation ? 1.0 : 0.0, + child: OverlayCenterContent( + event: widget.event, + messageHeight: null, + messageWidth: null, + // messageHeight: _adjustedCenteredMessageSize?.height, + // messageWidth: _adjustedCenteredMessageSize?.width, + maxWidth: _toolbarMaxWidth, + overlayController: widget.overlayController, + chatController: widget.chatController, + pangeaMessageEvent: widget.pangeaMessageEvent, + nextEvent: widget.nextEvent, + prevEvent: widget.prevEvent, + showToolbarButtons: showToolbarButtons, + hasReactions: _hasReactions, + onChangeMessageSize: _setCenteredMessageSize, + isTransitionAnimation: false, + transitionAnimationFinished: _finishedAnimation, + maxHeight: _mediaQuery!.size.height - + _headerHeight - + _footerHeight - + AppConfig.toolbarSpacing * 2, + ), + ), + const Expanded( + flex: 1, + child: SizedBox.shrink(), + ), + Row( + mainAxisSize: MainAxisSize.min, + children: [ + Expanded( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + MeasureRenderBox( + onChange: _setInputBarSize, + child: OverlayFooter( + controller: widget.chatController, + overlayController: widget.overlayController, + ), + ), + SizedBox(height: _mediaQuery?.padding.bottom ?? 0), + ], + ), + ), + if (_showDetails) + const SizedBox( + width: FluffyThemes.columnWidth, + ), + ], + ), + ], ), if (!_finishedAnimation) AnimatedBuilder( @@ -457,12 +499,12 @@ class MessageSelectionPositionerState extends State _messageRightOffset : null, bottom: (_overlayOffsetAnimation?.value)?.dy ?? - _totalToolbarBottomOffset, + _messageBottomOffset - _reactionsHeight, child: OverlayCenterContent( event: widget.event, - messageHeight: _messageHeight, + messageHeight: _originalMessageHeight, messageWidth: _messageSize.width, - toolbarMaxWidth: _toolbarMaxWidth, + maxWidth: _toolbarMaxWidth, overlayController: widget.overlayController, chatController: widget.chatController, pangeaMessageEvent: widget.pangeaMessageEvent, @@ -472,75 +514,57 @@ class MessageSelectionPositionerState extends State hasReactions: _hasReactions, sizeAnimation: _messageSizeAnimation, isTransitionAnimation: true, + transitionAnimationFinished: _finishedAnimation, + maxHeight: _mediaQuery!.size.height - + _headerHeight - + _footerHeight - + AppConfig.toolbarSpacing * 2, ), ); }, ), - if (!_finishedAnimation && _buttonsOffsetAnimation != null) - AnimatedBuilder( - animation: _buttonsOffsetAnimation!, - builder: (context, child) { - return Positioned( - left: (_buttonsOffsetAnimation?.value)!.dx, - top: _centeredButtonsOffset?.dy, - child: ToolbarButtonRow( - event: widget.event, - overlayController: widget.overlayController, - shouldShowToolbarButtons: showToolbarButtons, + MeasureRenderBox( + onChange: _setTooltipSize, + child: Positioned( + top: 0, + left: 0, + child: Opacity( + opacity: 0.0, + child: Container( + constraints: const BoxConstraints( + minWidth: 200.0, + maxWidth: 400.0, ), - ); - }, - ), - Align( - alignment: Alignment.bottomCenter, - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - Expanded( - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - // ToolbarButtonRow( - // event: widget.overlayController.pangeaMessageEvent!.event, - // overlayController: widget.overlayController, - // shouldShowToolbarButtons: showToolbarButtons, - // ), - OverlayFooter( - controller: widget.chatController, - overlayController: widget.overlayController, - ), - SizedBox(height: _mediaQuery?.padding.bottom ?? 0), - ], + child: InstructionsInlineTooltip( + instructionsEnum: + widget.overlayController.toolbarMode.instructionsEnum ?? + InstructionsEnum.readingAssistanceOverview, + bold: true, ), ), - if (_showDetails) - const SizedBox( - width: FluffyThemes.columnWidth, - ), - ], + ), ), ), - Material( - type: MaterialType.transparency, - child: Column( - children: [ - SizedBox(height: _mediaQuery?.padding.top ?? 0), - OverlayHeader(controller: widget.chatController), - widget.overlayController.toolbarMode.instructionsEnum != null - ? Container( - constraints: const BoxConstraints( - maxWidth: 400, - ), - child: InstructionsInlineTooltip( - instructionsEnum: widget - .overlayController.toolbarMode.instructionsEnum!, - bold: true, - ), - ) - : const SizedBox.shrink(), - ], + if (_centeredMessageTopOffset != null && _finishedAnimation) + Positioned( + top: max( + ((_headerHeight + _centeredMessageTopOffset!) / 2) - + (_tooltipSize!.height / 2), + _headerHeight, + ), + child: Container( + constraints: const BoxConstraints( + minWidth: 200.0, + maxWidth: 400.0, + ), + child: InstructionsInlineTooltip( + instructionsEnum: + widget.overlayController.toolbarMode.instructionsEnum ?? + InstructionsEnum.readingAssistanceOverview, + bold: true, + ), + ), ), - ), ], ), ); diff --git a/lib/pangea/toolbar/widgets/overlay_center_content.dart b/lib/pangea/toolbar/widgets/overlay_center_content.dart index cd7b936dd..4b85e9e80 100644 --- a/lib/pangea/toolbar/widgets/overlay_center_content.dart +++ b/lib/pangea/toolbar/widgets/overlay_center_content.dart @@ -8,7 +8,6 @@ import 'package:fluffychat/pangea/events/event_wrappers/pangea_message_event.dar import 'package:fluffychat/pangea/toolbar/widgets/measure_render_box.dart'; import 'package:fluffychat/pangea/toolbar/widgets/message_selection_overlay.dart'; import 'package:fluffychat/pangea/toolbar/widgets/overlay_message.dart'; -import 'package:fluffychat/pangea/toolbar/widgets/toolbar_button_column.dart'; class OverlayCenterContent extends StatelessWidget { final Event event; @@ -21,22 +20,24 @@ class OverlayCenterContent extends StatelessWidget { final Animation? sizeAnimation; final void Function(RenderBox)? onChangeMessageSize; - final void Function(RenderBox)? onChangeContentSize; - final void Function(RenderBox)? onChangeButtonsSize; - final double messageHeight; - final double messageWidth; - final double toolbarMaxWidth; + final double? messageHeight; + final double? messageWidth; + final double maxWidth; + final double maxHeight; final bool showToolbarButtons; final bool hasReactions; + final bool isTransitionAnimation; + final bool transitionAnimationFinished; const OverlayCenterContent({ required this.event, required this.messageHeight, required this.messageWidth, - required this.toolbarMaxWidth, + required this.maxWidth, + required this.maxHeight, required this.overlayController, required this.chatController, required this.pangeaMessageEvent, @@ -45,78 +46,62 @@ class OverlayCenterContent extends StatelessWidget { required this.showToolbarButtons, required this.hasReactions, this.onChangeMessageSize, - this.onChangeContentSize, - this.onChangeButtonsSize, this.sizeAnimation, this.isTransitionAnimation = false, + this.transitionAnimationFinished = false, super.key, }); @override Widget build(BuildContext context) { - return Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Container( - constraints: BoxConstraints(maxWidth: toolbarMaxWidth), - child: Material( - type: MaterialType.transparency, - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: event.senderId == event.room.client.userID - ? CrossAxisAlignment.end - : CrossAxisAlignment.start, - children: [ - MeasureRenderBox( - onChange: onChangeMessageSize, - child: OverlayMessage( + return Container( + constraints: BoxConstraints(maxWidth: maxWidth), + child: Material( + type: MaterialType.transparency, + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: event.senderId == event.room.client.userID + ? CrossAxisAlignment.end + : CrossAxisAlignment.start, + children: [ + MeasureRenderBox( + onChange: onChangeMessageSize, + child: OverlayMessage( + event, + pangeaMessageEvent: pangeaMessageEvent, + immersionMode: chatController.choreographer.immersionMode, + controller: chatController, + overlayController: overlayController, + nextEvent: nextEvent, + prevEvent: prevEvent, + timeline: chatController.timeline!, + sizeAnimation: sizeAnimation, + // there's a split seconds between when the transition animation starts and + // when the sizeAnimation is set when the original dimensions need to be enforced + messageWidth: (sizeAnimation == null && isTransitionAnimation) + ? messageWidth + : null, + messageHeight: (sizeAnimation == null && isTransitionAnimation) + ? messageHeight + : null, + maxHeight: maxHeight, + isTransitionAnimation: isTransitionAnimation, + ), + ), + if (hasReactions) + Padding( + padding: const EdgeInsets.all(4), + child: SizedBox( + height: 20, + child: MessageReactions( event, - pangeaMessageEvent: pangeaMessageEvent, - immersionMode: chatController.choreographer.immersionMode, - controller: chatController, - overlayController: overlayController, - nextEvent: nextEvent, - prevEvent: prevEvent, - timeline: chatController.timeline!, - sizeAnimation: sizeAnimation, - // there's a split seconds between when the transition animation starts and - // when the sizeAnimation is set when the original dimensions need to be enforced - messageWidth: sizeAnimation == null && isTransitionAnimation - ? messageWidth - : null, - messageHeight: - sizeAnimation == null && isTransitionAnimation - ? messageHeight - : null, - isTransitionAnimation: isTransitionAnimation, + chatController.timeline!, ), ), - if (hasReactions) - Padding( - padding: const EdgeInsets.all(4), - child: SizedBox( - height: 20, - child: MessageReactions( - event, - chatController.timeline!, - ), - ), - ), - ], - ), - ), + ), + ], ), - if (!isTransitionAnimation) - MeasureRenderBox( - onChange: onChangeButtonsSize, - child: ToolbarButtonRow( - event: event, - overlayController: overlayController, - shouldShowToolbarButtons: showToolbarButtons, - ), - ), - ], + ), ); } } diff --git a/lib/pangea/toolbar/widgets/overlay_message.dart b/lib/pangea/toolbar/widgets/overlay_message.dart index b0da3382b..34375105c 100644 --- a/lib/pangea/toolbar/widgets/overlay_message.dart +++ b/lib/pangea/toolbar/widgets/overlay_message.dart @@ -27,6 +27,7 @@ class OverlayMessage extends StatelessWidget { final Animation? sizeAnimation; final double? messageWidth; final double? messageHeight; + final double maxHeight; final bool isTransitionAnimation; @@ -38,6 +39,7 @@ class OverlayMessage extends StatelessWidget { required this.timeline, required this.messageWidth, required this.messageHeight, + required this.maxHeight, this.pangeaMessageEvent, this.nextEvent, this.prevEvent, @@ -119,22 +121,25 @@ class OverlayMessage extends StatelessWidget { ? ThemeData.dark().colorScheme.onPrimary : theme.colorScheme.onSurface; - final content = SingleChildScrollView( - dragStartBehavior: DragStartBehavior.down, - child: Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular( - AppConfig.borderRadius, - ), + final content = Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular( + AppConfig.borderRadius, ), - padding: noBubble || noPadding - ? EdgeInsets.zero - : const EdgeInsets.symmetric( - horizontal: 16, - vertical: 8, - ), - width: messageWidth, - height: messageHeight, + ), + padding: noBubble || noPadding + ? EdgeInsets.zero + : const EdgeInsets.symmetric( + horizontal: 16, + vertical: 8, + ), + width: messageWidth, + height: messageHeight, + constraints: BoxConstraints( + maxHeight: maxHeight, + ), + child: SingleChildScrollView( + dragStartBehavior: DragStartBehavior.down, child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, diff --git a/lib/pangea/toolbar/widgets/toolbar_button_column.dart b/lib/pangea/toolbar/widgets/toolbar_button_column.dart index 12ee53636..1f26ac88e 100644 --- a/lib/pangea/toolbar/widgets/toolbar_button_column.dart +++ b/lib/pangea/toolbar/widgets/toolbar_button_column.dart @@ -2,17 +2,16 @@ import 'package:flutter/material.dart'; import 'package:matrix/matrix.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'; class ToolbarButtonRow extends StatelessWidget { - final Event event; final MessageOverlayController overlayController; final bool shouldShowToolbarButtons; const ToolbarButtonRow({ - required this.event, required this.overlayController, required this.shouldShowToolbarButtons, super.key, @@ -25,84 +24,85 @@ class ToolbarButtonRow extends StatelessWidget { @override Widget build(BuildContext context) { - if (event.messageType == MessageTypes.Audio || + if (overlayController.event.messageType == MessageTypes.Audio || !shouldShowToolbarButtons || !(overlayController.pangeaMessageEvent?.messageDisplayLangIsL2 ?? false)) { return const SizedBox( - height: 50.0, + height: AppConfig.toolbarButtonsHeight, ); } - return Column( - mainAxisAlignment: MainAxisAlignment.center, - mainAxisSize: MainAxisSize.min, - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.center, - mainAxisSize: MainAxisSize.min, - children: [ - ToolbarButton( - mode: MessageMode.messageTranslation, - overlayController: overlayController, - onPressed: overlayController.updateToolbarMode, - buttonSize: buttonSize, - ), - ], - ), - Row( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisSize: MainAxisSize.min, - children: [ - // wrapping these with a container to prevent the buttons from - // moving around when they press and depress - Container( - width: buttonSize + 4, - height: buttonSize + 4, - alignment: Alignment.center, - child: ToolbarButton( - mode: MessageMode.wordMorph, + return SizedBox( + height: AppConfig.toolbarButtonsHeight, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.center, + mainAxisSize: MainAxisSize.min, + children: [ + 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, + ], + ), + 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.wordMorph, + overlayController: overlayController, + onPressed: overlayController.updateToolbarMode, + buttonSize: buttonSize, + ), ), - ), - 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.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.listening, + 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, + ), + ), + ], + ), + ], + ), ); } }