diff --git a/lib/pangea/widgets/chat/message_selection_overlay.dart b/lib/pangea/widgets/chat/message_selection_overlay.dart index 01c44955f..496c7ce66 100644 --- a/lib/pangea/widgets/chat/message_selection_overlay.dart +++ b/lib/pangea/widgets/chat/message_selection_overlay.dart @@ -4,14 +4,15 @@ import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/config/setting_keys.dart'; import 'package:fluffychat/config/themes.dart'; import 'package:fluffychat/pages/chat/chat.dart'; -import 'package:fluffychat/pages/chat/events/message.dart'; import 'package:fluffychat/pangea/enum/message_mode_enum.dart'; import 'package:fluffychat/pangea/matrix_event_wrappers/pangea_message_event.dart'; import 'package:fluffychat/pangea/models/pangea_token_model.dart'; import 'package:fluffychat/pangea/models/practice_activities.dart/practice_activity_model.dart'; import 'package:fluffychat/pangea/widgets/chat/message_toolbar.dart'; +import 'package:fluffychat/pangea/widgets/chat/message_toolbar_buttons.dart'; import 'package:fluffychat/pangea/widgets/chat/overlay_footer.dart'; import 'package:fluffychat/pangea/widgets/chat/overlay_header.dart'; +import 'package:fluffychat/pangea/widgets/chat/overlay_message.dart'; import 'package:fluffychat/widgets/avatar.dart'; import 'package:fluffychat/widgets/matrix.dart'; import 'package:flutter/foundation.dart'; @@ -214,6 +215,8 @@ class MessageOverlayController extends State PangeaTokenText? get selectedSpan => _selectedSpan; + final int toolbarButtonsHeight = 50; + @override void didChangeDependencies() { super.didChangeDependencies(); @@ -224,11 +227,13 @@ class MessageOverlayController extends State // position the overlay directly over the underlying message final headerBottomOffset = screenHeight - headerHeight; final footerBottomOffset = footerHeight; - final currentBottomOffset = - screenHeight - messageOffset!.dy - messageSize!.height; + final currentBottomOffset = screenHeight - + messageOffset!.dy - + messageSize!.height - + toolbarButtonsHeight; - final bool hasHeaderOverflow = - messageOffset!.dy < (AppConfig.toolbarMaxHeight + headerHeight); + final bool hasHeaderOverflow = (messageOffset!.dy - toolbarButtonsHeight) < + (AppConfig.toolbarMaxHeight + headerHeight); final bool hasFooterOverflow = footerHeight > currentBottomOffset; if (!hasHeaderOverflow && !hasFooterOverflow) return; @@ -241,7 +246,8 @@ class MessageOverlayController extends State // if the overlay would have a footer overflow for this message, // check if shifting the overlay up could cause a header overflow final bottomOffsetDifference = footerHeight - currentBottomOffset; - final newTopOffset = messageOffset!.dy - bottomOffsetDifference; + final newTopOffset = + messageOffset!.dy - bottomOffsetDifference - toolbarButtonsHeight; final bool upshiftCausesHeaderOverflow = hasFooterOverflow && newTopOffset < (headerHeight + AppConfig.toolbarMaxHeight); @@ -301,6 +307,8 @@ class MessageOverlayController extends State double get screenHeight => MediaQuery.of(context).size.height; + double get screenWidth => MediaQuery.of(context).size.width; + @override Widget build(BuildContext context) { final bool showDetails = (Matrix.of(context) @@ -310,7 +318,25 @@ class MessageOverlayController extends State FluffyThemes.isThreeColumnMode(context) && widget.chatController.room.membership == Membership.join; - final overlayMessage = ConstrainedBox( + // the default spacing between the side of the screen and the message bubble + final double messageMargin = + pangeaMessageEvent.ownMessage ? Avatar.defaultSize + 16 : 8; + + // the actual spacing between the side of the screen and + // the message bubble, accounts for wide screen + double extraChatSpace = FluffyThemes.isColumnMode(context) + ? ((screenWidth - + (FluffyThemes.columnWidth * 3.5) - + FluffyThemes.navRailWidth) / + 2) + + messageMargin + : messageMargin; + + if (extraChatSpace < messageMargin) { + extraChatSpace = messageMargin; + } + + final overlayMessage = Container( constraints: const BoxConstraints( maxWidth: FluffyThemes.columnWidth * 2.5, ), @@ -318,76 +344,75 @@ class MessageOverlayController extends State type: MaterialType.transparency, child: Column( mainAxisSize: MainAxisSize.min, + crossAxisAlignment: widget._pangeaMessageEvent.ownMessage + ? CrossAxisAlignment.end + : CrossAxisAlignment.start, children: [ - Row( - mainAxisAlignment: widget._pangeaMessageEvent.ownMessage - ? MainAxisAlignment.end - : MainAxisAlignment.start, - children: [ - MessagePadding( - pangeaMessageEvent: pangeaMessageEvent, - child: MessageToolbar( - pangeaMessageEvent: widget._pangeaMessageEvent, - overLayController: this, - ), - ), - ], + MessageToolbar( + pangeaMessageEvent: widget._pangeaMessageEvent, + overLayController: this, ), - Message( - widget._event, - onSwipe: () => {}, - onInfoTab: (_) => {}, - onAvatarTab: (_) => {}, - scrollToEventId: (_) => {}, - onSelect: (_) => {}, + OverlayMessage( + pangeaMessageEvent, immersionMode: widget.chatController.choreographer.immersionMode, controller: widget.chatController, - timeline: widget.chatController.timeline!, overlayController: this, - animateIn: false, nextEvent: widget._nextEvent, - previousEvent: widget._prevEvent, + prevEvent: widget._prevEvent, + timeline: widget.chatController.timeline!, + messageWidth: messageSize!.width, + ), + ToolbarButtons( + overlayController: this, + width: 250, ), - // MessageReactions(widget._event, widget.chatController.timeline!), - // const SizedBox(height: 6), - // MessagePadding( - // pangeaMessageEvent: pangeaMessageEvent, - // child: ToolbarButtons(overlayController: this, width: 250), - // ), ], ), ), ); + final horizontalPadding = FluffyThemes.isColumnMode(context) ? 8.0 : 0.0; + final columnOffset = FluffyThemes.isColumnMode(context) + ? FluffyThemes.columnWidth + FluffyThemes.navRailWidth + : 0; + + final double leftPadding = widget._pangeaMessageEvent.ownMessage + ? extraChatSpace + : messageOffset!.dx - horizontalPadding - columnOffset; + + final double rightPadding = widget._pangeaMessageEvent.ownMessage + ? screenWidth - + messageOffset!.dx - + messageSize!.width - + horizontalPadding + : extraChatSpace; + final positionedOverlayMessage = _overlayPositionAnimation == null ? Positioned( - left: 0, - right: showDetails ? FluffyThemes.columnWidth : 0, - bottom: screenHeight - messageOffset!.dy - messageSize!.height, - child: Align( - alignment: Alignment.center, - child: overlayMessage, - ), + left: leftPadding, + right: rightPadding, + bottom: screenHeight - + messageOffset!.dy - + messageSize!.height - + toolbarButtonsHeight, + child: overlayMessage, ) : AnimatedBuilder( animation: _overlayPositionAnimation!, builder: (context, child) { return Positioned( - left: 0, - right: showDetails ? FluffyThemes.columnWidth : 0, + left: leftPadding, + right: rightPadding, bottom: _overlayPositionAnimation!.value, - child: Align( - alignment: Alignment.center, - child: overlayMessage, - ), + child: overlayMessage, ); }, ); return Padding( padding: EdgeInsets.only( - left: FluffyThemes.isColumnMode(context) ? 8.0 : 0.0, - right: FluffyThemes.isColumnMode(context) ? 8.0 : 0.0, + left: horizontalPadding, + right: horizontalPadding, ), child: Stack( children: [ diff --git a/lib/pangea/widgets/chat/message_toolbar.dart b/lib/pangea/widgets/chat/message_toolbar.dart index f0c0dde80..0e5b40b7e 100644 --- a/lib/pangea/widgets/chat/message_toolbar.dart +++ b/lib/pangea/widgets/chat/message_toolbar.dart @@ -8,7 +8,6 @@ import 'package:fluffychat/pangea/utils/error_handler.dart'; import 'package:fluffychat/pangea/widgets/chat/message_audio_card.dart'; import 'package:fluffychat/pangea/widgets/chat/message_selection_overlay.dart'; import 'package:fluffychat/pangea/widgets/chat/message_speech_to_text_card.dart'; -import 'package:fluffychat/pangea/widgets/chat/message_toolbar_buttons.dart'; import 'package:fluffychat/pangea/widgets/chat/message_translation_card.dart'; import 'package:fluffychat/pangea/widgets/chat/message_unsubscribed_card.dart'; import 'package:fluffychat/pangea/widgets/igc/word_data_card.dart'; @@ -124,12 +123,6 @@ class MessageToolbarState extends State { child: Column( children: [ Container( - constraints: const BoxConstraints( - maxHeight: AppConfig.toolbarMaxHeight, - maxWidth: 350, - minWidth: 350, - ), - padding: const EdgeInsets.all(0), decoration: BoxDecoration( color: Theme.of(context).cardColor, border: Border.all( @@ -140,10 +133,9 @@ class MessageToolbarState extends State { Radius.circular(AppConfig.borderRadius), ), ), - child: Column( - mainAxisSize: MainAxisSize.min, + child: Row( children: [ - Flexible( + Expanded( child: SingleChildScrollView( child: AnimatedSize( duration: FluffyThemes.animationDuration, @@ -154,12 +146,6 @@ class MessageToolbarState extends State { ], ), ), - const SizedBox(height: 6), - ToolbarButtons( - overlayController: widget.overLayController, - width: 250, - ), - const SizedBox(height: 6), ], ), ); diff --git a/lib/pangea/widgets/chat/message_toolbar_buttons.dart b/lib/pangea/widgets/chat/message_toolbar_buttons.dart index c0d021caf..bd5b0802b 100644 --- a/lib/pangea/widgets/chat/message_toolbar_buttons.dart +++ b/lib/pangea/widgets/chat/message_toolbar_buttons.dart @@ -37,6 +37,7 @@ class ToolbarButtons extends StatelessWidget { return SizedBox( width: width, + height: 50, child: Stack( alignment: Alignment.center, children: [ diff --git a/lib/pangea/widgets/chat/overlay_message.dart b/lib/pangea/widgets/chat/overlay_message.dart new file mode 100644 index 000000000..07f83c8b5 --- /dev/null +++ b/lib/pangea/widgets/chat/overlay_message.dart @@ -0,0 +1,116 @@ +import 'package:fluffychat/config/app_config.dart'; +import 'package:fluffychat/pages/chat/chat.dart'; +import 'package:fluffychat/pages/chat/events/message_content.dart'; +import 'package:fluffychat/pangea/matrix_event_wrappers/pangea_message_event.dart'; +import 'package:fluffychat/pangea/widgets/chat/message_selection_overlay.dart'; +import 'package:fluffychat/utils/date_time_extension.dart'; +import 'package:fluffychat/widgets/matrix.dart'; +import 'package:flutter/material.dart'; +import 'package:matrix/matrix.dart'; + +class OverlayMessage extends StatelessWidget { + final PangeaMessageEvent pangeaMessageEvent; + final MessageOverlayController overlayController; + final ChatController controller; + final Event? nextEvent; + final Event? prevEvent; + final Timeline timeline; + final bool immersionMode; + final double messageWidth; + + const OverlayMessage( + this.pangeaMessageEvent, { + this.immersionMode = false, + required this.overlayController, + required this.controller, + required this.timeline, + required this.messageWidth, + this.nextEvent, + this.prevEvent, + super.key, + }); + + @override + Widget build(BuildContext context) { + final theme = Theme.of(context); + final bool ownMessage = + pangeaMessageEvent.event.senderId == Matrix.of(context).client.userID; + + final displayTime = + pangeaMessageEvent.event.type == EventTypes.RoomCreate || + nextEvent == null || + !pangeaMessageEvent.event.originServerTs + .sameEnvironment(nextEvent!.originServerTs); + + final nextEventSameSender = nextEvent != null && + { + EventTypes.Message, + EventTypes.Sticker, + EventTypes.Encrypted, + }.contains(nextEvent!.type) && + nextEvent!.senderId == pangeaMessageEvent.event.senderId && + !displayTime; + + final previousEventSameSender = prevEvent != null && + { + EventTypes.Message, + EventTypes.Sticker, + EventTypes.Encrypted, + }.contains(prevEvent!.type) && + prevEvent!.senderId == pangeaMessageEvent.event.senderId && + prevEvent!.originServerTs + .sameEnvironment(pangeaMessageEvent.event.originServerTs); + + const hardCorner = Radius.circular(4); + const roundedCorner = Radius.circular(AppConfig.borderRadius); + final borderRadius = BorderRadius.only( + topLeft: !ownMessage && nextEventSameSender ? hardCorner : roundedCorner, + topRight: ownMessage && nextEventSameSender ? hardCorner : roundedCorner, + bottomLeft: + !ownMessage && previousEventSameSender ? hardCorner : roundedCorner, + bottomRight: + ownMessage && previousEventSameSender ? hardCorner : roundedCorner, + ); + + final displayEvent = pangeaMessageEvent.event.getDisplayEvent(timeline); + var color = theme.colorScheme.surfaceContainerHighest; + if (ownMessage) { + color = displayEvent.status.isError + ? Colors.redAccent + : theme.colorScheme.primary; + } + + return Material( + color: color, + clipBehavior: Clip.antiAlias, + shape: RoundedRectangleBorder( + borderRadius: borderRadius, + ), + child: Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular( + AppConfig.borderRadius, + ), + ), + padding: const EdgeInsets.symmetric( + horizontal: 16, + vertical: 8, + ), + width: messageWidth, + child: MessageContent( + pangeaMessageEvent.event, + textColor: ownMessage + ? theme.colorScheme.onPrimary + : theme.colorScheme.onSurface, + pangeaMessageEvent: pangeaMessageEvent, + immersionMode: immersionMode, + overlayController: overlayController, + controller: controller, + nextEvent: nextEvent, + prevEvent: prevEvent, + borderRadius: borderRadius, + ), + ), + ); + } +}