diff --git a/lib/pangea/widgets/chat/message_selection_overlay.dart b/lib/pangea/widgets/chat/message_selection_overlay.dart index 01c44955f..dbde10f52 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'; @@ -225,10 +226,10 @@ class MessageOverlayController extends State final headerBottomOffset = screenHeight - headerHeight; final footerBottomOffset = footerHeight; final currentBottomOffset = - screenHeight - messageOffset!.dy - messageSize!.height; + screenHeight - messageOffset!.dy - messageSize!.height - 50; final bool hasHeaderOverflow = - messageOffset!.dy < (AppConfig.toolbarMaxHeight + headerHeight); + (messageOffset!.dy - 50) < (AppConfig.toolbarMaxHeight + headerHeight); final bool hasFooterOverflow = footerHeight > currentBottomOffset; if (!hasHeaderOverflow && !hasFooterOverflow) return; @@ -241,7 +242,7 @@ 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 - 50; final bool upshiftCausesHeaderOverflow = hasFooterOverflow && newTopOffset < (headerHeight + AppConfig.toolbarMaxHeight); @@ -301,6 +302,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 +313,22 @@ class MessageOverlayController extends State FluffyThemes.isThreeColumnMode(context) && widget.chatController.room.membership == Membership.join; - final overlayMessage = ConstrainedBox( + final messageMargin = + pangeaMessageEvent.ownMessage ? Avatar.defaultSize + 16 : 8; + + double extraChatSpace = FluffyThemes.isColumnMode(context) + ? ((screenWidth - + (FluffyThemes.columnWidth * 3.5) - + FluffyThemes.navRailWidth) / + 2) + + messageMargin + : 0.0; + + if (extraChatSpace < 0) { + extraChatSpace = 0; + } + + final overlayMessage = Container( constraints: const BoxConstraints( maxWidth: FluffyThemes.columnWidth * 2.5, ), @@ -318,76 +336,72 @@ 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 - 50, + 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..1b444af6c --- /dev/null +++ b/lib/pangea/widgets/chat/overlay_message.dart @@ -0,0 +1,117 @@ +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, + ), + // #Pangea + 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, + ), + ), + ); + } +}