From 8245d13cc4e50fe3d622f47a8b8b3115172805a2 Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Wed, 13 Jan 2021 11:15:31 +0530 Subject: [PATCH 01/18] [QuotedMessageWidget] Move padding inside widget Signed-off-by: Sahil Kumar --- lib/src/message_input.dart | 12 +++++------- lib/src/message_widget.dart | 22 ++++++++-------------- lib/src/quoted_message_widget.dart | 26 ++++++++++++++++---------- 3 files changed, 29 insertions(+), 31 deletions(-) diff --git a/lib/src/message_input.dart b/lib/src/message_input.dart index cc12fbd9..db21c206 100644 --- a/lib/src/message_input.dart +++ b/lib/src/message_input.dart @@ -1527,14 +1527,12 @@ class MessageInputState extends State { return Transform( transform: Matrix4.rotationY(pi), alignment: Alignment.center, - child: Padding( + child: QuotedMessageWidget( + reverse: true, + showBorder: !containsUrl, + message: widget.quotedMessage, + messageTheme: StreamChatTheme.of(context).otherMessageTheme, padding: const EdgeInsets.fromLTRB(8, 8, 8, 0), - child: QuotedMessageWidget( - reverse: true, - showBorder: !containsUrl, - message: widget.quotedMessage, - messageTheme: StreamChatTheme.of(context).otherMessageTheme, - ), ), ); } diff --git a/lib/src/message_widget.dart b/lib/src/message_widget.dart index ccefce6e..f4f8944f 100644 --- a/lib/src/message_widget.dart +++ b/lib/src/message_widget.dart @@ -494,21 +494,15 @@ class _MessageWidgetState extends State { widget.onQuotedMessageTap != null ? () => widget.onQuotedMessageTap(widget.message.quotedMessageId) : null; - return Padding( + return QuotedMessageWidget( + onTap: onTap, + message: widget.message.quotedMessage, + messageTheme: isMyMessage + ? StreamChatTheme.of(context).otherMessageTheme + : StreamChatTheme.of(context).ownMessageTheme, + reverse: widget.reverse, padding: EdgeInsets.only( - right: 8, - left: 8, - top: 8, - bottom: hasNonUrlAttachments ? 8 : 0, - ), - child: QuotedMessageWidget( - onTap: onTap, - message: widget.message.quotedMessage, - messageTheme: isMyMessage - ? StreamChatTheme.of(context).otherMessageTheme - : StreamChatTheme.of(context).ownMessageTheme, - reverse: widget.reverse, - ), + right: 8, left: 8, top: 8, bottom: hasNonUrlAttachments ? 8 : 0), ); } diff --git a/lib/src/quoted_message_widget.dart b/lib/src/quoted_message_widget.dart index 309df703..e4e10e17 100644 --- a/lib/src/quoted_message_widget.dart +++ b/lib/src/quoted_message_widget.dart @@ -83,6 +83,8 @@ class QuotedMessageWidget extends StatelessWidget { final Map attachmentThumbnailBuilders; + final EdgeInsetsGeometry padding; + final GestureTapCallback onTap; /// @@ -94,6 +96,7 @@ class QuotedMessageWidget extends StatelessWidget { this.showBorder = false, this.textLimit = 170, this.attachmentThumbnailBuilders, + this.padding = const EdgeInsets.all(8), this.onTap, }) : super(key: key); @@ -107,16 +110,19 @@ class QuotedMessageWidget extends StatelessWidget { @override Widget build(BuildContext context) { - return InkWell( - onTap: onTap, - child: Row( - crossAxisAlignment: CrossAxisAlignment.end, - mainAxisSize: MainAxisSize.min, - children: [ - Flexible(child: _buildMessage(context)), - SizedBox(width: 8), - _buildUserAvatar(), - ], + return Padding( + padding: padding, + child: InkWell( + onTap: onTap, + child: Row( + crossAxisAlignment: CrossAxisAlignment.end, + mainAxisSize: MainAxisSize.min, + children: [ + Flexible(child: _buildMessage(context)), + SizedBox(width: 8), + _buildUserAvatar(), + ], + ), ), ); } From 18c31a666b7b55a174f8ba3745c1c8f23ad7a27d Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Wed, 13 Jan 2021 11:22:03 +0530 Subject: [PATCH 02/18] [MessageInput] Fix attachments padding Signed-off-by: Sahil Kumar --- lib/src/message_input.dart | 61 ++++++++++++++++++-------------------- 1 file changed, 29 insertions(+), 32 deletions(-) diff --git a/lib/src/message_input.dart b/lib/src/message_input.dart index db21c206..22a36882 100644 --- a/lib/src/message_input.dart +++ b/lib/src/message_input.dart @@ -1542,17 +1542,17 @@ class MessageInputState extends State { return Column( children: [ if (_attachments.any((e) => e.attachment?.type == 'file')) - LimitedBox( - maxHeight: 136.0, - child: ListView( - reverse: true, - shrinkWrap: true, - children: _attachments.reversed - .where((e) => e.attachment?.type == 'file') - .map( - (e) => Padding( - padding: const EdgeInsets.symmetric(horizontal: 8.0), - child: ClipRRect( + Padding( + padding: const EdgeInsets.fromLTRB(8, 8, 8, 0), + child: LimitedBox( + maxHeight: 136.0, + child: ListView( + reverse: true, + shrinkWrap: true, + children: _attachments.reversed + .where((e) => e.attachment?.type == 'file') + .map( + (e) => ClipRRect( borderRadius: BorderRadius.circular(10), clipBehavior: Clip.antiAlias, child: FileAttachment( @@ -1578,31 +1578,28 @@ class MessageInputState extends State { .white, ), ), - onTap: () { - setState(() { - _attachments.remove(e); - }); - }, + onTap: () => + setState(() => _attachments.remove(e)), ), ), ), ), - ), - ) - .toList(), + ) + .insertBetween(const SizedBox(width: 8)), + ), ), ), if (_attachments.any((e) => e.attachment?.type != 'file')) - LimitedBox( - maxHeight: 104.0, - child: ListView( - scrollDirection: Axis.horizontal, - children: _attachments - .where((e) => e.attachment?.type != 'file') - .map( - (attachment) => Padding( - padding: const EdgeInsets.all(8.0), - child: ClipRRect( + Padding( + padding: const EdgeInsets.fromLTRB(8, 8, 8, 0), + child: LimitedBox( + maxHeight: 104.0, + child: ListView( + scrollDirection: Axis.horizontal, + children: _attachments + .where((e) => e.attachment?.type != 'file') + .map( + (attachment) => ClipRRect( borderRadius: BorderRadius.circular(10), clipBehavior: Clip.antiAlias, child: Stack( @@ -1629,9 +1626,9 @@ class MessageInputState extends State { ], ), ), - ), - ) - .toList(), + ) + .insertBetween(const SizedBox(width: 8)), + ), ), ), ], From c22c6d29f5cfa0dc8c73048fc52c7c6d2dce051f Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Wed, 13 Jan 2021 11:33:59 +0530 Subject: [PATCH 03/18] [MessageListView] Fix message borders Signed-off-by: Sahil Kumar --- lib/src/message_list_view.dart | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/lib/src/message_list_view.dart b/lib/src/message_list_view.dart index 741ba6ef..35956729 100644 --- a/lib/src/message_list_view.dart +++ b/lib/src/message_list_view.dart @@ -811,15 +811,11 @@ class _MessageListViewState extends State { final showThreadReplyIndicator = !_isThreadConversation && hasReplies; final isOnlyEmoji = message.text.isOnlyEmoji; - final showMessageBorder = - showThreadReplyIndicator || showInChannelIndicator; - final borderSide = isMyMessage - ? !showMessageBorder - ? BorderSide.none - : null - : isOnlyEmoji && !showMessageBorder - ? BorderSide.none - : null; + final hasUrlAttachment = + message.attachments?.any((it) => it.ogScrapeUrl != null) == true; + + final borderSide = + isOnlyEmoji || hasUrlAttachment || isMyMessage ? BorderSide.none : null; Widget child = MessageWidget( key: ValueKey('MESSAGE-${message.id}'), From a9c52c66549e58790bfc8e16448c4112e86e0996 Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Wed, 13 Jan 2021 11:40:46 +0530 Subject: [PATCH 04/18] [MessageWidget] Fix thread tail Signed-off-by: Sahil Kumar --- lib/src/message_widget.dart | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/src/message_widget.dart b/lib/src/message_widget.dart index f4f8944f..b6fc1bb6 100644 --- a/lib/src/message_widget.dart +++ b/lib/src/message_widget.dart @@ -278,6 +278,9 @@ class _MessageWidgetState extends State { ?.isNotEmpty == true; + bool get hasUrlAttachments => + widget.message.attachments?.any((it) => it.ogScrapeUrl != null) == true; + bool get showBottomRow => showThreadReplyIndicator || showUsername || @@ -586,12 +589,15 @@ class _MessageWidgetState extends State { if (widget.reverse) children = children.reversed.toList(); + final showThreadTail = !(hasUrlAttachments || isGiphy || isOnlyEmoji) && + (showThreadReplyIndicator || showInChannel); + return Flex( direction: Axis.horizontal, clipBehavior: Clip.none, crossAxisAlignment: CrossAxisAlignment.end, children: [ - if (showThreadReplyIndicator || showInChannel) + if (showThreadTail) Container( margin: EdgeInsets.only( bottom: widget.messageTheme.replies.fontSize / 2, @@ -938,11 +944,7 @@ class _MessageWidgetState extends State { : widget.messageTheme, ), ), - if (widget.message.attachments - ?.any((element) => element.ogScrapeUrl != null) == - true && - !hasQuotedMessage) - _buildUrlAttachment(), + if (hasUrlAttachments && !hasQuotedMessage) _buildUrlAttachment(), ], ), ); @@ -955,9 +957,7 @@ class _MessageWidgetState extends State { return widget.messageTheme.messageBackgroundColor; } - if (widget.message.attachments - ?.any((element) => element.ogScrapeUrl != null) == - true) { + if (hasUrlAttachments) { return StreamChatTheme.of(context).colorTheme.blueAlice; } From f353e02c0321d102c5b433bfce5aae00a6824b74 Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Wed, 13 Jan 2021 11:57:45 +0530 Subject: [PATCH 05/18] [MessageWidget] Fix threadParticipants indicator Signed-off-by: Sahil Kumar --- lib/src/message_widget.dart | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/lib/src/message_widget.dart b/lib/src/message_widget.dart index b6fc1bb6..989de8c8 100644 --- a/lib/src/message_widget.dart +++ b/lib/src/message_widget.dart @@ -565,7 +565,7 @@ class _MessageWidgetState extends State { if (showInChannel || showThreadReplyIndicator) ...[ if (showThreadParticipants) SizedBox.fromSize( - size: Size((threadParticipants.length * 8.0) + 10, 16), + size: Size((threadParticipants.length * 8.0) + 8, 16), child: _buildThreadParticipantsIndicator(), ), InkWell( @@ -587,7 +587,7 @@ class _MessageWidgetState extends State { ), ]); - if (widget.reverse) children = children.reversed.toList(); + // if (widget.reverse) children = children.reversed.toList(); final showThreadTail = !(hasUrlAttachments || isGiphy || isOnlyEmoji) && (showThreadReplyIndicator || showInChannel); @@ -648,18 +648,20 @@ class _MessageWidgetState extends State { var padding = 0.0; return Stack( children: widget.message.threadParticipants.map((user) { - padding += 10.0; + padding += 8.0; return Positioned( - left: padding - 10, + right: padding - 8, bottom: 0, top: 0, - child: Material( - color: Colors.white, - clipBehavior: Clip.antiAlias, - shape: CircleBorder(), + child: Container( + decoration: BoxDecoration( + shape: BoxShape.circle, + color: StreamChatTheme.of(context).colorTheme.white, + ), + padding: const EdgeInsets.all(1), child: UserAvatar( user: user, - constraints: BoxConstraints.loose(Size.fromRadius(8)), + constraints: BoxConstraints.loose(Size.fromRadius(7)), showOnlineStatus: false, ), ), From b27f04bf362b1efa1143ff643e0f36345ec3dd92 Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Wed, 13 Jan 2021 12:09:36 +0530 Subject: [PATCH 06/18] [MessageWidget] Fix bottomRow children position Signed-off-by: Sahil Kumar --- lib/src/message_widget.dart | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/src/message_widget.dart b/lib/src/message_widget.dart index 989de8c8..a74477b2 100644 --- a/lib/src/message_widget.dart +++ b/lib/src/message_widget.dart @@ -561,7 +561,6 @@ class _MessageWidgetState extends State { }; children.addAll([ - if (showSendingIndicator) _buildSendingIndicator(), if (showInChannel || showThreadReplyIndicator) ...[ if (showThreadParticipants) SizedBox.fromSize( @@ -585,10 +584,9 @@ class _MessageWidgetState extends State { Jiffy(widget.message.createdAt.toLocal()).jm, style: widget.messageTheme.createdAt, ), + if (showSendingIndicator) _buildSendingIndicator(), ]); - // if (widget.reverse) children = children.reversed.toList(); - final showThreadTail = !(hasUrlAttachments || isGiphy || isOnlyEmoji) && (showThreadReplyIndicator || showInChannel); From 7c8bd11fd2a41d9810b7dd7a01e92fee7e661d7d Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Wed, 13 Jan 2021 13:25:38 +0530 Subject: [PATCH 07/18] [ImageActionsModal] Remove cross icon and fix dividers Signed-off-by: Sahil Kumar --- lib/src/image_actions_modal.dart | 206 +++++++++++++++---------------- 1 file changed, 102 insertions(+), 104 deletions(-) diff --git a/lib/src/image_actions_modal.dart b/lib/src/image_actions_modal.dart index 332117d4..97729ca1 100644 --- a/lib/src/image_actions_modal.dart +++ b/lib/src/image_actions_modal.dart @@ -8,6 +8,7 @@ import 'package:image_gallery_saver/image_gallery_saver.dart'; import 'package:path_provider/path_provider.dart'; import '../stream_chat_flutter.dart'; +import 'extension.dart'; class ImageActionsModal extends StatelessWidget { final Message message; @@ -35,127 +36,124 @@ class ImageActionsModal extends StatelessWidget { } Widget _buildPage(context) { - return Material( - color: Colors.transparent, - child: Column( - crossAxisAlignment: CrossAxisAlignment.end, - children: [ - SizedBox( - height: kToolbarHeight, - child: IconButton( - icon: StreamSvgIcon.close(), - onPressed: () => Navigator.maybePop(context), + return Column( + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + SizedBox(height: kToolbarHeight), + Padding( + padding: const EdgeInsets.only(right: 8.0), + child: Container( + width: MediaQuery.of(context).size.width * 0.5, + clipBehavior: Clip.hardEdge, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(16.0), ), - ), - Align( - alignment: Alignment.centerRight, child: Container( - width: MediaQuery.of(context).size.width / 1.8, - child: Padding( - padding: const EdgeInsets.all(8.0), - child: Material( - clipBehavior: Clip.hardEdge, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.end, + mainAxisSize: MainAxisSize.min, + children: [ + _buildButton( + context, + 'Reply', + StreamSvgIcon.Icon_curve_line_left_up( + size: 24.0, + color: StreamChatTheme.of(context).colorTheme.grey, + ), + () {}, ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: ListTile.divideTiles( - color: StreamChatTheme.of(context).colorTheme.greyWhisper, - context: context, - tiles: [ - _buildButton( - context, - 'Reply', - StreamSvgIcon.Icon_curve_line_left_up( - size: 24.0, - color: - StreamChatTheme.of(context).colorTheme.grey, - ), - () {}), - _buildButton( - context, - 'Show in Chat', - StreamSvgIcon.eye( - size: 24.0, - color: - StreamChatTheme.of(context).colorTheme.black, - ), - onShowMessage), - _buildButton( - context, - 'Save ${urls[currentIndex].type == 'video' ? 'Video' : 'Image'}', - StreamSvgIcon.Icon_save( - size: 24.0, - color: - StreamChatTheme.of(context).colorTheme.grey, - ), () async { - var url = urls[currentIndex].imageUrl ?? - urls[currentIndex].assetUrl ?? - urls[currentIndex].thumbUrl; + _buildButton( + context, + 'Show in Chat', + StreamSvgIcon.eye( + size: 24.0, + color: StreamChatTheme.of(context).colorTheme.black, + ), + onShowMessage, + ), + _buildButton( + context, + 'Save ${urls[currentIndex].type == 'video' ? 'Video' : 'Image'}', + StreamSvgIcon.Icon_save( + size: 24.0, + color: StreamChatTheme.of(context).colorTheme.grey, + ), + () async { + var url = urls[currentIndex].imageUrl ?? + urls[currentIndex].assetUrl ?? + urls[currentIndex].thumbUrl; - if (urls[currentIndex].type == 'video') { - await _saveVideo(url); - Navigator.pop(context); - } else { - await _saveImage(url); - Navigator.pop(context); - } - }), - if (StreamChat.of(context).user.id == message.user.id) - _buildButton( - context, - 'Delete', - StreamSvgIcon.delete( - size: 24.0, - color: StreamChatTheme.of(context) - .colorTheme - .accentRed, - ), - () { - Navigator.pop(context); - Navigator.pop(context); - StreamChat.of(context).client.deleteMessage( - message, - StreamChannel.of(context).channel.cid, - ); - }, - color: StreamChatTheme.of(context) - .colorTheme - .accentRed, - ), - ], - ).toList(), + if (urls[currentIndex].type == 'video') { + await _saveVideo(url); + Navigator.pop(context); + } else { + await _saveImage(url); + Navigator.pop(context); + } + }, ), - ), + if (StreamChat.of(context).user.id == message.user.id) + _buildButton( + context, + 'Delete', + StreamSvgIcon.delete( + size: 24.0, + color: StreamChatTheme.of(context).colorTheme.accentRed, + ), + () { + Navigator.pop(context); + Navigator.pop(context); + StreamChat.of(context).client.deleteMessage( + message, + StreamChannel.of(context).channel.cid, + ); + }, + color: StreamChatTheme.of(context).colorTheme.accentRed, + ), + ] + .map((e) => + Align(alignment: Alignment.centerRight, child: e)) + .insertBetween( + Container( + height: 1, + color: + StreamChatTheme.of(context).colorTheme.greyWhisper, + ), + ), ), ), ), - ], - ), + ) + ], ); } Widget _buildButton( - context, String title, StreamSvgIcon icon, VoidCallback onTap, - {Color color}) { - var titleStyle = TextStyle( - fontSize: 14.5, - color: StreamChatTheme.of(context).colorTheme.black, - ); - + context, + String title, + StreamSvgIcon icon, + VoidCallback onTap, { + Color color, + }) { return Material( color: StreamChatTheme.of(context).colorTheme.white, child: InkWell( onTap: onTap, - child: ListTile( - dense: true, - title: Text( - title, - style: - color == null ? titleStyle : titleStyle.copyWith(color: color), + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 8.0, horizontal: 16.0), + child: Row( + children: [ + icon, + SizedBox(width: 16), + Text( + title, + style: StreamChatTheme.of(context) + .textTheme + .body + .copyWith(color: color), + ), + ], ), - leading: icon, ), ), ); From 987f6991fe082f12763e69af24f7cb1364b5e298 Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Wed, 13 Jan 2021 13:39:27 +0530 Subject: [PATCH 08/18] [FileAttachment] Fix trailing icon padding and minor fixes Signed-off-by: Sahil Kumar --- lib/src/file_attachment.dart | 47 +++++++++++++++++++----------------- 1 file changed, 25 insertions(+), 22 deletions(-) diff --git a/lib/src/file_attachment.dart b/lib/src/file_attachment.dart index ca51bf76..303b2b25 100644 --- a/lib/src/file_attachment.dart +++ b/lib/src/file_attachment.dart @@ -72,6 +72,7 @@ class _FileAttachmentState extends State { : null, ), child: Row( + crossAxisAlignment: CrossAxisAlignment.start, children: [ Container( child: _getFileTypeImage(), @@ -79,9 +80,7 @@ class _FileAttachmentState extends State { width: 33.33, margin: EdgeInsets.all(8.0), ), - SizedBox( - width: 6.0, - ), + SizedBox(width: 8.0), Expanded( child: Column( mainAxisAlignment: MainAxisAlignment.center, @@ -93,32 +92,36 @@ class _FileAttachmentState extends State { maxLines: 1, overflow: TextOverflow.ellipsis, ), - SizedBox( - height: 3.0, - ), + SizedBox(height: 3.0), Text( '${getSizeText(widget.attachment.extraData['file_size'])}', - style: StreamChatTheme.of(context).textTheme.body.copyWith( - color: StreamChatTheme.of(context) - .colorTheme - .black - .withOpacity(0.5)), + style: StreamChatTheme.of(context) + .textTheme + .footnote + .copyWith( + color: StreamChatTheme.of(context) + .colorTheme + .black + .withOpacity(0.5)), ), ], ), ), - Column( - children: [ - widget.trailing ?? - IconButton( - icon: StreamSvgIcon.cloud_download( - color: StreamChatTheme.of(context).colorTheme.black, - ), - onPressed: () { - launchURL(context, widget.attachment.assetUrl); - }, + SizedBox(width: 8.0), + Material( + type: MaterialType.transparency, + child: widget.trailing ?? + IconButton( + icon: StreamSvgIcon.cloud_download( + color: StreamChatTheme.of(context).colorTheme.black, ), - ], + padding: const EdgeInsets.all(8), + visualDensity: VisualDensity.compact, + splashRadius: 16, + onPressed: () { + launchURL(context, widget.attachment.assetUrl); + }, + ), ), ], ), From 3432b7c5eef27337fd7eb8be16db860fe5fe819b Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Wed, 13 Jan 2021 15:09:28 +0530 Subject: [PATCH 09/18] [MessageWidget] Fix attachment padding and border sides Signed-off-by: Sahil Kumar --- lib/src/file_attachment.dart | 10 +---- lib/src/message_list_view.dart | 9 ++-- lib/src/message_widget.dart | 77 +++++++++++++++++----------------- 3 files changed, 45 insertions(+), 51 deletions(-) diff --git a/lib/src/file_attachment.dart b/lib/src/file_attachment.dart index 303b2b25..96061cab 100644 --- a/lib/src/file_attachment.dart +++ b/lib/src/file_attachment.dart @@ -62,15 +62,7 @@ class _FileAttachmentState extends State { child: Container( width: widget.size?.width ?? 100, height: 56.0, - decoration: BoxDecoration( - color: StreamChatTheme.of(context).colorTheme.white, - borderRadius: - widget.trailing != null ? BorderRadius.circular(16.0) : null, - border: widget.trailing != null - ? Border.fromBorderSide(BorderSide( - color: StreamChatTheme.of(context).colorTheme.greyWhisper)) - : null, - ), + color: StreamChatTheme.of(context).colorTheme.white, child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ diff --git a/lib/src/message_list_view.dart b/lib/src/message_list_view.dart index 35956729..021b2a15 100644 --- a/lib/src/message_list_view.dart +++ b/lib/src/message_list_view.dart @@ -815,7 +815,9 @@ class _MessageListViewState extends State { message.attachments?.any((it) => it.ogScrapeUrl != null) == true; final borderSide = - isOnlyEmoji || hasUrlAttachment || isMyMessage ? BorderSide.none : null; + isOnlyEmoji || hasUrlAttachment || (isMyMessage && !hasFileAttachment) + ? BorderSide.none + : null; Widget child = MessageWidget( key: ValueKey('MESSAGE-${message.id}'), @@ -859,14 +861,15 @@ class _MessageListViewState extends State { attachmentBorderRadiusGeometry: BorderRadius.only( topLeft: Radius.circular(attachmentBorderRadius), bottomLeft: Radius.circular( - (timeDiff >= 1 || !isNextUserSame) && !(hasReplies || isThreadMessage) + (timeDiff >= 1 || !isNextUserSame) && + !(hasReplies || isThreadMessage || hasFileAttachment) ? 0 : attachmentBorderRadius, ), topRight: Radius.circular(attachmentBorderRadius), bottomRight: Radius.circular(attachmentBorderRadius), ), - attachmentPadding: const EdgeInsets.all(2), + attachmentPadding: EdgeInsets.all(hasFileAttachment ? 4 : 2), borderRadiusGeometry: BorderRadius.only( topLeft: Radius.circular(16), bottomLeft: Radius.circular( diff --git a/lib/src/message_widget.dart b/lib/src/message_widget.dart index a74477b2..2157aa32 100644 --- a/lib/src/message_widget.dart +++ b/lib/src/message_widget.dart @@ -419,7 +419,7 @@ class _MessageWidgetState extends State { if (hasQuotedMessage) _buildQuotedMessage(), if (hasNonUrlAttachments) - ..._parseAttachments( + _parseAttachments( context), if (widget.message.text .trim() @@ -777,7 +777,7 @@ class _MessageWidgetState extends State { side: widget.attachmentBorderSide ?? widget.borderSide ?? BorderSide( - color: StreamChatTheme.of(context).colorTheme.greyGainsboro, + color: StreamChatTheme.of(context).colorTheme.greyWhisper, ), borderRadius: widget.attachmentBorderRadiusGeometry ?? widget.borderRadiusGeometry ?? @@ -785,7 +785,7 @@ class _MessageWidgetState extends State { ); } - List _parseAttachments(BuildContext context) { + Widget _parseAttachments(BuildContext context) { final images = widget.message.attachments ?.where((element) => element.type == 'image' && element.ogScrapeUrl == null) @@ -793,8 +793,9 @@ class _MessageWidgetState extends State { []; if (images.length > 1) { - return [ - wrapAttachmentWidget( + return Padding( + padding: widget.attachmentPadding, + child: wrapAttachmentWidget( context, Material( color: widget.messageTheme.messageBackgroundColor, @@ -809,30 +810,36 @@ class _MessageWidgetState extends State { ), ), ), - ]; + ); } - return widget.message.attachments - ?.where((element) => element.ogScrapeUrl == null) - ?.map((attachment) { - final attachmentBuilder = widget.attachmentBuilders[attachment.type]; + return Padding( + padding: widget.attachmentPadding, + child: Column( + mainAxisSize: MainAxisSize.min, + children: widget.message.attachments + ?.where((element) => element.ogScrapeUrl == null) + ?.map((attachment) { + final attachmentBuilder = + widget.attachmentBuilders[attachment.type]; - if (attachmentBuilder == null) { - return SizedBox(); - } - - final attachmentWidget = attachmentBuilder( - context, - widget.message, - attachment, - ); - return wrapAttachmentWidget( - context, - attachmentWidget, - attachment: attachment, - ); - })?.toList() ?? - []; + if (attachmentBuilder == null) return SizedBox(); + final attachmentWidget = attachmentBuilder( + context, + widget.message, + attachment, + ); + return wrapAttachmentWidget( + context, + attachmentWidget, + attachment: attachment, + ); + })?.insertBetween(SizedBox( + height: widget.attachmentPadding.vertical / 2, + )) ?? + [], + ), + ); } Widget wrapAttachmentWidget( @@ -843,21 +850,13 @@ class _MessageWidgetState extends State { final attachmentShape = widget.attachmentShape ?? widget.shape ?? _getDefaultShape(context); return Material( - color: _getBackgroundColor(), clipBehavior: Clip.antiAlias, shape: attachmentShape, - child: Padding( - padding: widget.attachmentPadding, - child: Material( - clipBehavior: Clip.hardEdge, - shape: attachmentShape, - type: MaterialType.transparency, - child: Transform( - transform: Matrix4.rotationY(widget.reverse ? pi : 0), - alignment: Alignment.center, - child: attachmentWidget, - ), - ), + type: MaterialType.transparency, + child: Transform( + transform: Matrix4.rotationY(widget.reverse ? pi : 0), + alignment: Alignment.center, + child: attachmentWidget, ), ); } From e07a07d91025a68fc3e463d09b9576d2aad470b2 Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Wed, 13 Jan 2021 15:21:58 +0530 Subject: [PATCH 10/18] Remove message text horizontal padding if it's only emoji Signed-off-by: Sahil Kumar --- lib/src/message_actions_modal.dart | 6 ++++++ lib/src/message_list_view.dart | 8 ++++++++ lib/src/message_reactions_modal.dart | 5 +++++ 3 files changed, 19 insertions(+) diff --git a/lib/src/message_actions_modal.dart b/lib/src/message_actions_modal.dart index 2d125bab..9d9670cf 100644 --- a/lib/src/message_actions_modal.dart +++ b/lib/src/message_actions_modal.dart @@ -14,6 +14,7 @@ import 'message_input.dart'; import 'message_widget.dart'; import 'stream_chat.dart'; import 'stream_chat_theme.dart'; +import 'extension.dart'; class MessageActionsModal extends StatefulWidget { final Widget Function(BuildContext, Message) editMessageInputBuilder; @@ -152,6 +153,11 @@ class _MessageActionsModalState extends State { showUserAvatar: widget.showUserAvatar, showTimestamp: false, translateUserAvatar: false, + textPadding: EdgeInsets.symmetric( + vertical: 8.0, + horizontal: + widget.message.text.isOnlyEmoji ? 0 : 16.0, + ), showReactionPickerIndicator: widget.showReactions && (widget.message.status == diff --git a/lib/src/message_list_view.dart b/lib/src/message_list_view.dart index 021b2a15..b659845e 100644 --- a/lib/src/message_list_view.dart +++ b/lib/src/message_list_view.dart @@ -732,6 +732,10 @@ class _MessageListViewState extends State { topRight: Radius.circular(16), bottomRight: Radius.circular(16), ), + textPadding: EdgeInsets.symmetric( + vertical: 8.0, + horizontal: isOnlyEmoji ? 0 : 16.0, + ), borderSide: isMyMessage || isOnlyEmoji ? BorderSide.none : null, showUserAvatar: isMyMessage ? DisplayWidget.gone : DisplayWidget.show, messageTheme: isMyMessage @@ -880,6 +884,10 @@ class _MessageListViewState extends State { topRight: Radius.circular(16), bottomRight: Radius.circular(16), ), + textPadding: EdgeInsets.symmetric( + vertical: 8.0, + horizontal: isOnlyEmoji ? 0 : 16.0, + ), messageTheme: isMyMessage ? StreamChatTheme.of(context).ownMessageTheme : StreamChatTheme.of(context).otherMessageTheme, diff --git a/lib/src/message_reactions_modal.dart b/lib/src/message_reactions_modal.dart index 15a301cc..0f056dde 100644 --- a/lib/src/message_reactions_modal.dart +++ b/lib/src/message_reactions_modal.dart @@ -9,6 +9,7 @@ import 'package:stream_chat_flutter/src/user_avatar.dart'; import 'message_widget.dart'; import 'stream_chat_theme.dart'; +import 'extension.dart'; class MessageReactionsModal extends StatelessWidget { final Widget Function(BuildContext, Message) editMessageInputBuilder; @@ -124,6 +125,10 @@ class MessageReactionsModal extends StatelessWidget { showSendingIndicator: false, shape: messageShape, showInChannelIndicator: false, + textPadding: EdgeInsets.symmetric( + vertical: 8.0, + horizontal: message.text.isOnlyEmoji ? 0 : 16.0, + ), showReactionPickerIndicator: showReactions && (message.status == MessageSendingStatus.SENT || From 829dddb5e095be9c7b234d677facacd79168846a Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Wed, 13 Jan 2021 16:14:45 +0530 Subject: [PATCH 11/18] [ReactionBubble] Add masked spacing Signed-off-by: Sahil Kumar --- lib/src/message_reactions_modal.dart | 1 + lib/src/message_widget.dart | 4 + lib/src/reaction_bubble.dart | 128 +++++++++++++++++++-------- lib/src/stream_chat_theme.dart | 7 ++ 4 files changed, 102 insertions(+), 38 deletions(-) diff --git a/lib/src/message_reactions_modal.dart b/lib/src/message_reactions_modal.dart index 0f056dde..8520e8ec 100644 --- a/lib/src/message_reactions_modal.dart +++ b/lib/src/message_reactions_modal.dart @@ -232,6 +232,7 @@ class MessageReactionsModal extends StatelessWidget { flipTail: !reverse, borderColor: messageTheme.reactionsBorderColor, backgroundColor: messageTheme.reactionsBackgroundColor, + maskColor: messageTheme.reactionsMaskColor, highlightOwnReactions: false, ), ), diff --git a/lib/src/message_widget.dart b/lib/src/message_widget.dart index 2157aa32..3a718471 100644 --- a/lib/src/message_widget.dart +++ b/lib/src/message_widget.dart @@ -447,6 +447,9 @@ class _MessageWidgetState extends State { StreamChatTheme.of(context) .colorTheme .white, + StreamChatTheme.of(context) + .colorTheme + .white, ), ), ), @@ -694,6 +697,7 @@ class _MessageWidgetState extends State { flipTail: widget.reverse, backgroundColor: widget.messageTheme.reactionsBackgroundColor, borderColor: widget.messageTheme.reactionsBorderColor, + maskColor: widget.messageTheme.reactionsMaskColor, reactions: reactionsList, ), ) diff --git a/lib/src/reaction_bubble.dart b/lib/src/reaction_bubble.dart index 9eee9997..61cca62e 100644 --- a/lib/src/reaction_bubble.dart +++ b/lib/src/reaction_bubble.dart @@ -12,6 +12,7 @@ class ReactionBubble extends StatelessWidget { @required this.reactions, @required this.borderColor, @required this.backgroundColor, + @required this.maskColor, this.reverse = false, this.flipTail = false, this.highlightOwnReactions = true, @@ -20,6 +21,7 @@ class ReactionBubble extends StatelessWidget { final List reactions; final Color borderColor; final Color backgroundColor; + final Color maskColor; final bool reverse; final bool flipTail; final bool highlightOwnReactions; @@ -38,51 +40,58 @@ class ReactionBubble extends StatelessWidget { Transform.translate( offset: Offset(reverse ? offset : -offset, 0), child: Container( - padding: EdgeInsets.symmetric( - vertical: 4, - horizontal: totalReactions > 1 ? 4 : 0, - ), + padding: const EdgeInsets.all(2), decoration: BoxDecoration( - border: Border.all( - color: borderColor, - ), - color: backgroundColor, + color: maskColor, borderRadius: BorderRadius.all(Radius.circular(14)), ), - child: LayoutBuilder( - builder: (context, constraints) { - return Flex( - direction: Axis.horizontal, - mainAxisSize: MainAxisSize.min, - children: [ - if (constraints.maxWidth < double.infinity) - ...reactions - .take((constraints.maxWidth) ~/ 22) - .map((reaction) { - return _buildReaction( - reactionIcons, - reaction, - context, - ); - }).toList(), - if (constraints.maxWidth == double.infinity) - ...reactions.map((reaction) { - return _buildReaction( - reactionIcons, - reaction, - context, - ); - }).toList(), - ], - ); - }, + child: Container( + padding: EdgeInsets.symmetric( + vertical: 4, + horizontal: totalReactions > 1 ? 4 : 0, + ), + decoration: BoxDecoration( + border: Border.all( + color: borderColor, + ), + color: backgroundColor, + borderRadius: BorderRadius.all(Radius.circular(14)), + ), + child: LayoutBuilder( + builder: (context, constraints) { + return Flex( + direction: Axis.horizontal, + mainAxisSize: MainAxisSize.min, + children: [ + if (constraints.maxWidth < double.infinity) + ...reactions + .take((constraints.maxWidth) ~/ 22) + .map((reaction) { + return _buildReaction( + reactionIcons, + reaction, + context, + ); + }).toList(), + if (constraints.maxWidth == double.infinity) + ...reactions.map((reaction) { + return _buildReaction( + reactionIcons, + reaction, + context, + ); + }).toList(), + ], + ); + }, + ), ), ), ), Positioned( - bottom: 0, - left: reverse ? null : 11, - right: !reverse ? null : 11, + bottom: 2, + left: reverse ? null : 13, + right: !reverse ? null : 13, child: _buildReactionsTail(context), ), ], @@ -136,6 +145,7 @@ class ReactionBubble extends StatelessWidget { painter: ReactionBubblePainter( backgroundColor, borderColor, + maskColor, ), ); return Transform( @@ -149,14 +159,20 @@ class ReactionBubble extends StatelessWidget { class ReactionBubblePainter extends CustomPainter { final Color color; final Color borderColor; + final Color maskColor; ReactionBubblePainter( this.color, this.borderColor, + this.maskColor, ); @override void paint(Canvas canvas, Size size) { + _drawOvalMask(size, canvas); + + _drawMask(size, canvas); + _drawOval(size, canvas); _drawOvalBorder(size, canvas); @@ -166,6 +182,21 @@ class ReactionBubblePainter extends CustomPainter { _drawBorder(size, canvas); } + void _drawOvalMask(Size size, Canvas canvas) { + final paint = Paint() + ..color = maskColor + ..style = PaintingStyle.fill; + + final path = Path(); + path.addOval( + Rect.fromCircle( + center: Offset(4, 3), + radius: 4, + ), + ); + canvas.drawPath(path, paint); + } + void _drawOvalBorder(Size size, Canvas canvas) { final paint = Paint() ..color = borderColor @@ -236,6 +267,27 @@ class ReactionBubblePainter extends CustomPainter { canvas.drawPath(path, paint); } + void _drawMask(Size size, Canvas canvas) { + final paint = Paint() + ..color = maskColor + ..strokeWidth = 1 + ..style = PaintingStyle.fill; + + final dy = -2.2; + final startAngle = 1.1; + final sweepAngle = 1.2; + final path = Path(); + path.addArc( + Rect.fromCircle( + center: Offset(1, dy), + radius: 6, + ), + -pi * startAngle, + -pi / sweepAngle, + ); + canvas.drawPath(path, paint); + } + @override bool shouldRepaint(CustomPainter oldDelegate) { return true; diff --git a/lib/src/stream_chat_theme.dart b/lib/src/stream_chat_theme.dart index c6d2d72f..70845ba9 100644 --- a/lib/src/stream_chat_theme.dart +++ b/lib/src/stream_chat_theme.dart @@ -234,6 +234,7 @@ class StreamChatThemeData { messageBackgroundColor: colorTheme.greyGainsboro, reactionsBackgroundColor: colorTheme.white, reactionsBorderColor: colorTheme.greyWhisper, + reactionsMaskColor: colorTheme.whiteSnow, messageBorderColor: colorTheme.greyGainsboro, avatarTheme: AvatarTheme( borderRadius: BorderRadius.circular(20), @@ -249,6 +250,7 @@ class StreamChatThemeData { otherMessageTheme: MessageTheme( reactionsBackgroundColor: colorTheme.greyGainsboro, reactionsBorderColor: colorTheme.white, + reactionsMaskColor: colorTheme.whiteSnow, messageText: textTheme.body, createdAt: textTheme.footnote.copyWith(color: colorTheme.grey), replies: textTheme.footnoteBold.copyWith(color: accentColor), @@ -710,6 +712,7 @@ class MessageTheme { final Color messageBorderColor; final Color reactionsBackgroundColor; final Color reactionsBorderColor; + final Color reactionsMaskColor; final AvatarTheme avatarTheme; const MessageTheme({ @@ -721,6 +724,7 @@ class MessageTheme { this.messageBorderColor, this.reactionsBackgroundColor, this.reactionsBorderColor, + this.reactionsMaskColor, this.avatarTheme, this.createdAt, }); @@ -736,6 +740,7 @@ class MessageTheme { AvatarTheme avatarTheme, Color reactionsBackgroundColor, Color reactionsBorderColor, + Color reactionsMaskColor, }) => MessageTheme( messageText: messageText ?? this.messageText, @@ -750,6 +755,7 @@ class MessageTheme { reactionsBackgroundColor: reactionsBackgroundColor ?? this.reactionsBackgroundColor, reactionsBorderColor: reactionsBorderColor ?? this.reactionsBorderColor, + reactionsMaskColor: reactionsMaskColor ?? this.reactionsMaskColor, ); MessageTheme merge(MessageTheme other) { @@ -767,6 +773,7 @@ class MessageTheme { avatarTheme: avatarTheme?.merge(other.avatarTheme) ?? other.avatarTheme, reactionsBackgroundColor: other.reactionsBackgroundColor, reactionsBorderColor: other.reactionsBorderColor, + reactionsMaskColor: other.reactionsMaskColor, ); } } From 854aea1fa2a5a226c82e3f8569d02191f3c02c8a Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Wed, 13 Jan 2021 16:24:55 +0530 Subject: [PATCH 12/18] [MessageActionModal] Fix action tile padding and divider Signed-off-by: Sahil Kumar --- lib/src/message_actions_modal.dart | 281 +++++++++++++++-------------- 1 file changed, 147 insertions(+), 134 deletions(-) diff --git a/lib/src/message_actions_modal.dart b/lib/src/message_actions_modal.dart index 9d9670cf..f0d378cf 100644 --- a/lib/src/message_actions_modal.dart +++ b/lib/src/message_actions_modal.dart @@ -186,36 +186,37 @@ class _MessageActionsModalState extends State { child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, - children: ListTile.divideTiles( - color: StreamChatTheme.of(context) - .colorTheme - .greyWhisper, - context: context, - tiles: [ - if (widget.showReplyMessage && - (widget.message.status == - MessageSendingStatus.SENT || - widget.message.status == null) && - widget.message.parentId == null) - _buildReplyButton(context), - if (widget.showThreadReplyMessage && - (widget.message.status == - MessageSendingStatus.SENT || - widget.message.status == null) && - widget.message.parentId == null) - _buildThreadReplyButton(context), - if (widget.showResendMessage) - _buildResendMessage(context), - if (widget.showEditMessage) - _buildEditMessage(context), - if (widget.showCopyMessage) - _buildCopyButton(context), - if (widget.showFlagButton) - _buildFlagButton(context), - if (widget.showDeleteMessage) - _buildDeleteButton(context), - ], - ).toList(), + children: [ + if (widget.showReplyMessage && + (widget.message.status == + MessageSendingStatus.SENT || + widget.message.status == null) && + widget.message.parentId == null) + _buildReplyButton(context), + if (widget.showThreadReplyMessage && + (widget.message.status == + MessageSendingStatus.SENT || + widget.message.status == null) && + widget.message.parentId == null) + _buildThreadReplyButton(context), + if (widget.showResendMessage) + _buildResendMessage(context), + if (widget.showEditMessage) + _buildEditMessage(context), + if (widget.showCopyMessage) + _buildCopyButton(context), + if (widget.showFlagButton) + _buildFlagButton(context), + if (widget.showDeleteMessage) + _buildDeleteButton(context), + ].insertBetween( + Container( + height: 1, + color: StreamChatTheme.of(context) + .colorTheme + .greyWhisper, + ), + ), ), ), ), @@ -416,136 +417,131 @@ class _MessageActionsModalState extends State { } Widget _buildReplyButton(BuildContext context) { - return ListTile( - dense: true, - title: Row( - children: [ - StreamSvgIcon.reply( - color: StreamChatTheme.of(context).primaryIconTheme.color, - ), - const SizedBox(width: 16), - Text( - 'Reply', - style: StreamChatTheme.of(context).textTheme.headline, - ), - ], - ), + return InkWell( onTap: () { Navigator.pop(context); if (widget.onReplyTap != null) { widget.onReplyTap(widget.message); } }, + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 8.0, horizontal: 16.0), + child: Row( + children: [ + StreamSvgIcon.reply( + color: StreamChatTheme.of(context).primaryIconTheme.color, + ), + const SizedBox(width: 16), + Text( + 'Reply', + style: StreamChatTheme.of(context).textTheme.body, + ), + ], + ), + ), ); } Widget _buildFlagButton(BuildContext context) { - return ListTile( - dense: true, - title: Row( - children: [ - StreamSvgIcon.icon_flag( - color: StreamChatTheme.of(context).primaryIconTheme.color, - ), - const SizedBox(width: 16), - Text( - 'Flag', - style: StreamChatTheme.of(context).textTheme.headline, - ), - ], - ), + return InkWell( onTap: () => _showFlagDialog(), + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 8.0, horizontal: 16.0), + child: Row( + children: [ + StreamSvgIcon.icon_flag( + color: StreamChatTheme.of(context).primaryIconTheme.color, + ), + const SizedBox(width: 16), + Text( + 'Flag', + style: StreamChatTheme.of(context).textTheme.body, + ), + ], + ), + ), ); } Widget _buildDeleteButton(BuildContext context) { final isDeleteFailed = widget.message.status == MessageSendingStatus.FAILED_DELETE; - return ListTile( - dense: true, - title: Row( - children: [ - StreamSvgIcon.delete( - color: Colors.red, - ), - const SizedBox(width: 16), - Text( - isDeleteFailed ? 'Retry Deleting Message' : 'Delete Message', - style: StreamChatTheme.of(context) - .textTheme - .headline - .copyWith(color: Colors.red), - ), - ], + return InkWell( + onTap: () => _showDeleteDialog(), + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 8.0, horizontal: 16.0), + child: Row( + children: [ + StreamSvgIcon.delete( + color: Colors.red, + ), + const SizedBox(width: 16), + Text( + isDeleteFailed ? 'Retry Deleting Message' : 'Delete Message', + style: StreamChatTheme.of(context) + .textTheme + .body + .copyWith(color: Colors.red), + ), + ], + ), ), - onTap: () { - _showDeleteDialog(); - }, ); } Widget _buildCopyButton(BuildContext context) { - return ListTile( - dense: true, - title: Row( - children: [ - StreamSvgIcon.copy( - size: 24, - color: StreamChatTheme.of(context).primaryIconTheme.color, - ), - const SizedBox(width: 16), - Text( - 'Copy Message', - style: StreamChatTheme.of(context).textTheme.headline, - ), - ], - ), + return InkWell( onTap: () async { await Clipboard.setData(ClipboardData(text: widget.message.text)); Navigator.pop(context); }, + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 8.0, horizontal: 16.0), + child: Row( + children: [ + StreamSvgIcon.copy( + size: 24, + color: StreamChatTheme.of(context).primaryIconTheme.color, + ), + const SizedBox(width: 16), + Text( + 'Copy Message', + style: StreamChatTheme.of(context).textTheme.body, + ), + ], + ), + ), ); } Widget _buildEditMessage(BuildContext context) { - return ListTile( - dense: true, - title: Row( - children: [ - StreamSvgIcon.edit( - color: StreamChatTheme.of(context).primaryIconTheme.color, - ), - const SizedBox(width: 16), - Text( - 'Edit Message', - style: StreamChatTheme.of(context).textTheme.headline, - ), - ], - ), + return InkWell( onTap: () async { Navigator.pop(context); _showEditBottomSheet(context); }, + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 8.0, horizontal: 16.0), + child: Row( + children: [ + StreamSvgIcon.edit( + color: StreamChatTheme.of(context).primaryIconTheme.color, + ), + const SizedBox(width: 16), + Text( + 'Edit Message', + style: StreamChatTheme.of(context).textTheme.body, + ), + ], + ), + ), ); } Widget _buildResendMessage(BuildContext context) { final isUpdateFailed = widget.message.status == MessageSendingStatus.FAILED_UPDATE; - return ListTile( - dense: true, - title: Row( - children: [ - StreamSvgIcon.circle_up( - color: StreamChatTheme.of(context).colorTheme.accentBlue, - ), - const SizedBox(width: 16), - Text( - isUpdateFailed ? 'Resend Edited Message' : 'Resend', - style: StreamChatTheme.of(context).textTheme.headline, - ), - ], - ), + return InkWell( onTap: () { Navigator.pop(context); final client = StreamChat.of(context).client; @@ -556,6 +552,21 @@ class _MessageActionsModalState extends State { channel.sendMessage(widget.message); } }, + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 8.0, horizontal: 16.0), + child: Row( + children: [ + StreamSvgIcon.circle_up( + color: StreamChatTheme.of(context).colorTheme.accentBlue, + ), + const SizedBox(width: 16), + Text( + isUpdateFailed ? 'Resend Edited Message' : 'Resend', + style: StreamChatTheme.of(context).textTheme.body, + ), + ], + ), + ), ); } @@ -629,26 +640,28 @@ class _MessageActionsModalState extends State { } Widget _buildThreadReplyButton(BuildContext context) { - return ListTile( - dense: true, - title: Row( - children: [ - StreamSvgIcon.thread( - color: StreamChatTheme.of(context).primaryIconTheme.color, - ), - const SizedBox(width: 16), - Text( - 'Thread Reply', - style: StreamChatTheme.of(context).textTheme.headline, - ), - ], - ), + return InkWell( onTap: () { Navigator.pop(context); if (widget.onThreadReplyTap != null) { widget.onThreadReplyTap(widget.message); } }, + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 8.0, horizontal: 16.0), + child: Row( + children: [ + StreamSvgIcon.thread( + color: StreamChatTheme.of(context).primaryIconTheme.color, + ), + const SizedBox(width: 16), + Text( + 'Thread Reply', + style: StreamChatTheme.of(context).textTheme.body, + ), + ], + ), + ), ); } } From 3b0c2a9a73f671a033473f82ca5143cb7a58c837 Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Wed, 13 Jan 2021 16:40:28 +0530 Subject: [PATCH 13/18] [ReactionBubble] Added Customization for the spacing between the bottom circles Signed-off-by: Sahil Kumar --- lib/src/message_widget.dart | 9 +++------ lib/src/reaction_bubble.dart | 15 ++++++++++----- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/lib/src/message_widget.dart b/lib/src/message_widget.dart index 3a718471..95753d09 100644 --- a/lib/src/message_widget.dart +++ b/lib/src/message_widget.dart @@ -444,12 +444,9 @@ class _MessageWidgetState extends State { StreamChatTheme.of(context) .colorTheme .white, - StreamChatTheme.of(context) - .colorTheme - .white, - StreamChatTheme.of(context) - .colorTheme - .white, + Colors.transparent, + Colors.transparent, + tailCirclesSpace: 1, ), ), ), diff --git a/lib/src/reaction_bubble.dart b/lib/src/reaction_bubble.dart index 61cca62e..3811bace 100644 --- a/lib/src/reaction_bubble.dart +++ b/lib/src/reaction_bubble.dart @@ -16,6 +16,7 @@ class ReactionBubble extends StatelessWidget { this.reverse = false, this.flipTail = false, this.highlightOwnReactions = true, + this.tailCirclesSpace = 0, }) : super(key: key); final List reactions; @@ -25,6 +26,7 @@ class ReactionBubble extends StatelessWidget { final bool reverse; final bool flipTail; final bool highlightOwnReactions; + final double tailCirclesSpace; @override Widget build(BuildContext context) { @@ -146,6 +148,7 @@ class ReactionBubble extends StatelessWidget { backgroundColor, borderColor, maskColor, + tailCirclesSpace: tailCirclesSpace, ), ); return Transform( @@ -160,12 +163,14 @@ class ReactionBubblePainter extends CustomPainter { final Color color; final Color borderColor; final Color maskColor; + final double tailCirclesSpace; ReactionBubblePainter( this.color, this.borderColor, - this.maskColor, - ); + this.maskColor, { + this.tailCirclesSpace = 0, + }); @override void paint(Canvas canvas, Size size) { @@ -190,7 +195,7 @@ class ReactionBubblePainter extends CustomPainter { final path = Path(); path.addOval( Rect.fromCircle( - center: Offset(4, 3), + center: Offset(4, 3) + Offset(tailCirclesSpace, tailCirclesSpace), radius: 4, ), ); @@ -206,7 +211,7 @@ class ReactionBubblePainter extends CustomPainter { final path = Path(); path.addOval( Rect.fromCircle( - center: Offset(4, 3), + center: Offset(4, 3) + Offset(tailCirclesSpace, tailCirclesSpace), radius: 2, ), ); @@ -220,7 +225,7 @@ class ReactionBubblePainter extends CustomPainter { final path = Path(); path.addOval(Rect.fromCircle( - center: Offset(4, 3), + center: Offset(4, 3) + Offset(tailCirclesSpace, tailCirclesSpace), radius: 2, )); canvas.drawPath(path, paint); From ab29edd89e1ba4708d019cd56a2b63ef5b0ca4f3 Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Wed, 13 Jan 2021 18:21:02 +0530 Subject: [PATCH 14/18] Minor ui fixes Signed-off-by: Sahil Kumar --- lib/src/message_actions_modal.dart | 13 ++++- lib/src/message_list_view.dart | 2 +- lib/src/message_reactions_modal.dart | 82 +++++++++++++++------------- lib/src/message_widget.dart | 4 +- 4 files changed, 56 insertions(+), 45 deletions(-) diff --git a/lib/src/message_actions_modal.dart b/lib/src/message_actions_modal.dart index f0d378cf..9b93695e 100644 --- a/lib/src/message_actions_modal.dart +++ b/lib/src/message_actions_modal.dart @@ -90,6 +90,8 @@ class _MessageActionsModalState extends State { duration: Duration(milliseconds: 300), curve: Curves.easeInOutBack, builder: (context, val, snapshot) { + final hasFileAttachment = + widget.message.attachments?.any((it) => it.type == 'file') == true; return GestureDetector( behavior: HitTestBehavior.translucent, onTap: () => Navigator.maybePop(context), @@ -111,7 +113,7 @@ class _MessageActionsModalState extends State { child: Center( child: SingleChildScrollView( child: Padding( - padding: const EdgeInsets.symmetric(vertical: 8.0), + padding: const EdgeInsets.all(8.0), child: Column( crossAxisAlignment: widget.reverse ? CrossAxisAlignment.end @@ -136,6 +138,7 @@ class _MessageActionsModalState extends State { messageTheme: widget.messageTheme, ), ), + SizedBox(height: 8), IgnorePointer( child: MessageWidget( key: Key('MessageWidget'), @@ -151,8 +154,12 @@ class _MessageActionsModalState extends State { showThreadReplyIndicator: false, showReplyMessage: false, showUserAvatar: widget.showUserAvatar, + attachmentPadding: EdgeInsets.all( + hasFileAttachment ? 4 : 2, + ), showTimestamp: false, translateUserAvatar: false, + padding: const EdgeInsets.all(0), textPadding: EdgeInsets.symmetric( vertical: 8.0, horizontal: @@ -168,10 +175,10 @@ class _MessageActionsModalState extends State { shape: widget.messageShape, ), ), + SizedBox(height: 8), Padding( padding: EdgeInsets.only( - right: widget.reverse ? 8 : 0, - left: widget.reverse ? 0 : 48, + left: widget.reverse ? 0 : 40, ), child: SizedBox( width: MediaQuery.of(context).size.width * 0.75, diff --git a/lib/src/message_list_view.dart b/lib/src/message_list_view.dart index b659845e..0e764bca 100644 --- a/lib/src/message_list_view.dart +++ b/lib/src/message_list_view.dart @@ -783,7 +783,7 @@ class _MessageListViewState extends State { final allRead = readList.length >= (channel.memberCount ?? 0) - 1; final hasFileAttachment = - message.attachments.any((it) => it.type == 'file'); + message.attachments?.any((it) => it.type == 'file') == true; final isThreadMessage = message?.parentId != null && message?.showInChannel == true; diff --git a/lib/src/message_reactions_modal.dart b/lib/src/message_reactions_modal.dart index 8520e8ec..bf72daf8 100644 --- a/lib/src/message_reactions_modal.dart +++ b/lib/src/message_reactions_modal.dart @@ -62,6 +62,8 @@ class MessageReactionsModal extends StatelessWidget { duration: Duration(milliseconds: 300), curve: Curves.easeInOutBack, builder: (context, val, snapshot) { + final hasFileAttachment = + message.attachments?.any((it) => it.type == 'file') == true; return GestureDetector( behavior: HitTestBehavior.translucent, onTap: () => Navigator.maybePop(context), @@ -83,7 +85,7 @@ class MessageReactionsModal extends StatelessWidget { child: Center( child: SingleChildScrollView( child: Padding( - padding: const EdgeInsets.symmetric(vertical: 8.0), + padding: const EdgeInsets.all(8.0), child: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.stretch, @@ -106,6 +108,7 @@ class MessageReactionsModal extends StatelessWidget { messageTheme: messageTheme, ), ), + const SizedBox(height: 8), IgnorePointer( child: MessageWidget( key: Key('MessageWidget'), @@ -124,6 +127,10 @@ class MessageReactionsModal extends StatelessWidget { translateUserAvatar: false, showSendingIndicator: false, shape: messageShape, + padding: const EdgeInsets.all(0), + attachmentPadding: EdgeInsets.all( + hasFileAttachment ? 4 : 2, + ), showInChannelIndicator: false, textPadding: EdgeInsets.symmetric( vertical: 8.0, @@ -135,8 +142,10 @@ class MessageReactionsModal extends StatelessWidget { message.status == null), ), ), - if (message.latestReactions?.isNotEmpty == true) + if (message.latestReactions?.isNotEmpty == true) ...[ + const SizedBox(height: 8), _buildReactionCard(context), + ] ], ), ), @@ -150,47 +159,42 @@ class MessageReactionsModal extends StatelessWidget { ); } - Padding _buildReactionCard(BuildContext context) { + Widget _buildReactionCard(BuildContext context) { final currentUser = StreamChat.of(context).user; - return Padding( - padding: const EdgeInsets.symmetric( - horizontal: 8.0, + return Card( + color: StreamChatTheme.of(context).colorTheme.white, + clipBehavior: Clip.hardEdge, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(16), ), - child: Card( - color: StreamChatTheme.of(context).colorTheme.white, - clipBehavior: Clip.hardEdge, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(16), - ), - margin: EdgeInsets.zero, - child: Padding( - padding: const EdgeInsets.all(16), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Text( - 'Message Reactions', - style: StreamChatTheme.of(context).textTheme.headlineBold, - ), - const SizedBox(height: 16), - Flexible( - child: SingleChildScrollView( - child: Wrap( - spacing: 16, - runSpacing: 16, - alignment: WrapAlignment.start, - children: message.latestReactions - .map((e) => _buildReaction( - e, - currentUser, - context, - )) - .toList(), - ), + margin: EdgeInsets.zero, + child: Padding( + padding: const EdgeInsets.all(16), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + 'Message Reactions', + style: StreamChatTheme.of(context).textTheme.headlineBold, + ), + const SizedBox(height: 16), + Flexible( + child: SingleChildScrollView( + child: Wrap( + spacing: 16, + runSpacing: 16, + alignment: WrapAlignment.start, + children: message.latestReactions + .map((e) => _buildReaction( + e, + currentUser, + context, + )) + .toList(), ), ), - ], - ), + ), + ], ), ), ); diff --git a/lib/src/message_widget.dart b/lib/src/message_widget.dart index 95753d09..29749c14 100644 --- a/lib/src/message_widget.dart +++ b/lib/src/message_widget.dart @@ -434,8 +434,8 @@ class _MessageWidgetState extends State { ), if (widget.showReactionPickerIndicator) Positioned( - right: 0, - top: -8, + right: widget.reverse ? -6 : 6, + top: -6, child: Transform( transform: Matrix4.rotationY( widget.reverse ? pi : 0), From 150e51ac62ccfb260b37786a1dcdd9d0c019b5cf Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Wed, 13 Jan 2021 18:37:54 +0530 Subject: [PATCH 15/18] [UserAvatar] Fix online status indicator size Signed-off-by: Sahil Kumar --- lib/src/channel_bottom_sheet.dart | 4 +++- lib/src/user_avatar.dart | 8 ++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/lib/src/channel_bottom_sheet.dart b/lib/src/channel_bottom_sheet.dart index d03f233c..6b3e4658 100644 --- a/lib/src/channel_bottom_sheet.dart +++ b/lib/src/channel_bottom_sheet.dart @@ -74,7 +74,7 @@ class _ChannelBottomSheetState extends State { ), borderRadius: BorderRadius.circular(32.0), onlineIndicatorConstraints: - BoxConstraints.tight(Size(16.0, 16.0)), + BoxConstraints.tight(Size(12.0, 12.0)), ), SizedBox( height: 6.0, @@ -110,6 +110,8 @@ class _ChannelBottomSheetState extends State { maxWidth: 64.0, ), borderRadius: BorderRadius.circular(32.0), + onlineIndicatorConstraints: + BoxConstraints.tight(Size(12.0, 12.0)), ), SizedBox( height: 6.0, diff --git a/lib/src/user_avatar.dart b/lib/src/user_avatar.dart index 688eff77..706c4d7e 100644 --- a/lib/src/user_avatar.dart +++ b/lib/src/user_avatar.dart @@ -92,18 +92,18 @@ class UserAvatar extends StatelessWidget { child: Material( type: MaterialType.circle, child: Container( - padding: const EdgeInsets.all(2.0), + margin: const EdgeInsets.all(2.0), constraints: onlineIndicatorConstraints ?? BoxConstraints.tightFor( - width: 12, - height: 12, + width: 8, + height: 8, ), child: Material( shape: CircleBorder(), color: streamChatTheme.colorTheme.accentGreen, ), ), - color: StreamChatTheme.of(context).colorTheme.white, + color: streamChatTheme.colorTheme.white, ), ), ), From ed767ec65b6198a36ac061dc6b8c7f9e2573b676 Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Wed, 13 Jan 2021 18:41:26 +0530 Subject: [PATCH 16/18] Fix tests Signed-off-by: Sahil Kumar --- test/src/reaction_bubble_test.dart | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/src/reaction_bubble_test.dart b/test/src/reaction_bubble_test.dart index c0bc8cd1..0b6c90ef 100644 --- a/test/src/reaction_bubble_test.dart +++ b/test/src/reaction_bubble_test.dart @@ -19,6 +19,7 @@ void main() { reactions: [], borderColor: Colors.black, backgroundColor: Colors.white, + maskColor: Colors.white, ), ), ), @@ -56,6 +57,7 @@ void main() { ], borderColor: Colors.black, backgroundColor: Colors.white, + maskColor: Colors.white, ), ), ), @@ -96,6 +98,7 @@ void main() { ], borderColor: Colors.black, backgroundColor: Colors.white, + maskColor: Colors.white, ), ), ), From 60c3118cd88bcaf2be5462b423004cf506f531b2 Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Wed, 13 Jan 2021 19:02:59 +0530 Subject: [PATCH 17/18] [MessageReactionsModal] Fix user online indicator size, reaction bubble alignment Signed-off-by: Sahil Kumar --- lib/src/message_reactions_modal.dart | 12 +++++++++--- lib/src/reaction_bubble.dart | 8 ++++---- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/lib/src/message_reactions_modal.dart b/lib/src/message_reactions_modal.dart index bf72daf8..c96b3afa 100644 --- a/lib/src/message_reactions_modal.dart +++ b/lib/src/message_reactions_modal.dart @@ -217,6 +217,7 @@ class MessageReactionsModal extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.center, children: [ Stack( + overflow: Overflow.visible, children: [ UserAvatar( onTap: onUserAvatarTap, @@ -225,6 +226,10 @@ class MessageReactionsModal extends StatelessWidget { height: 64, width: 64, ), + onlineIndicatorConstraints: BoxConstraints.tightFor( + height: 12, + width: 12, + ), borderRadius: BorderRadius.circular(32), ), Positioned( @@ -236,13 +241,14 @@ class MessageReactionsModal extends StatelessWidget { flipTail: !reverse, borderColor: messageTheme.reactionsBorderColor, backgroundColor: messageTheme.reactionsBackgroundColor, - maskColor: messageTheme.reactionsMaskColor, + maskColor: StreamChatTheme.of(context).colorTheme.white, + tailCirclesSpacing: 1, highlightOwnReactions: false, ), ), bottom: 6, - left: isCurrentUser ? 0 : null, - right: isCurrentUser ? 0 : null, + left: isCurrentUser ? -3 : null, + right: isCurrentUser ? -3 : null, ), ], ), diff --git a/lib/src/reaction_bubble.dart b/lib/src/reaction_bubble.dart index 3811bace..74a2ae99 100644 --- a/lib/src/reaction_bubble.dart +++ b/lib/src/reaction_bubble.dart @@ -16,7 +16,7 @@ class ReactionBubble extends StatelessWidget { this.reverse = false, this.flipTail = false, this.highlightOwnReactions = true, - this.tailCirclesSpace = 0, + this.tailCirclesSpacing = 0, }) : super(key: key); final List reactions; @@ -26,7 +26,7 @@ class ReactionBubble extends StatelessWidget { final bool reverse; final bool flipTail; final bool highlightOwnReactions; - final double tailCirclesSpace; + final double tailCirclesSpacing; @override Widget build(BuildContext context) { @@ -45,7 +45,7 @@ class ReactionBubble extends StatelessWidget { padding: const EdgeInsets.all(2), decoration: BoxDecoration( color: maskColor, - borderRadius: BorderRadius.all(Radius.circular(14)), + borderRadius: BorderRadius.all(Radius.circular(16)), ), child: Container( padding: EdgeInsets.symmetric( @@ -148,7 +148,7 @@ class ReactionBubble extends StatelessWidget { backgroundColor, borderColor, maskColor, - tailCirclesSpace: tailCirclesSpace, + tailCirclesSpace: tailCirclesSpacing, ), ); return Transform( From 6535e44313ea53d15ddb72604cfb9b0f777cb3b5 Mon Sep 17 00:00:00 2001 From: Salvatore Giordano Date: Wed, 13 Jan 2021 16:22:46 +0100 Subject: [PATCH 18/18] fix reaction picker --- lib/src/message_widget.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/src/message_widget.dart b/lib/src/message_widget.dart index 29749c14..070bdad5 100644 --- a/lib/src/message_widget.dart +++ b/lib/src/message_widget.dart @@ -434,8 +434,8 @@ class _MessageWidgetState extends State { ), if (widget.showReactionPickerIndicator) Positioned( - right: widget.reverse ? -6 : 6, - top: -6, + right: 4, + top: -8, child: Transform( transform: Matrix4.rotationY( widget.reverse ? pi : 0),