From 7f696236c446b4038d2e06eae08318bbc9af9ac0 Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Thu, 31 Dec 2020 13:05:32 +0530 Subject: [PATCH 1/7] [Message Widget] Add message error state Signed-off-by: Sahil Kumar --- lib/src/message_actions_modal.dart | 30 +++++- lib/src/message_widget.dart | 147 +++++++++++------------------ lib/src/sending_indicator.dart | 9 -- lib/src/stream_svg_icon.dart | 25 +++++ 4 files changed, 107 insertions(+), 104 deletions(-) diff --git a/lib/src/message_actions_modal.dart b/lib/src/message_actions_modal.dart index 8013a8b3..2d482c0e 100644 --- a/lib/src/message_actions_modal.dart +++ b/lib/src/message_actions_modal.dart @@ -22,6 +22,7 @@ class MessageActionsModal extends StatelessWidget { final bool showDeleteMessage; final bool showCopyMessage; final bool showEditMessage; + final bool showResendMessage; final bool showReply; final bool reverse; final ShapeBorder messageShape; @@ -37,6 +38,7 @@ class MessageActionsModal extends StatelessWidget { this.onThreadTap, this.showCopyMessage = true, this.showReply = true, + this.showResendMessage = true, this.showUserAvatar = DisplayWidget.show, this.editMessageInputBuilder, this.messageShape, @@ -159,6 +161,8 @@ class MessageActionsModal extends StatelessWidget { message.status == null) && message.parentId == null) _buildReplyButton(context), + if (showResendMessage) + _buildResendMessage(context), if (showEditMessage) _buildEditMessage(context), if (showDeleteMessage) @@ -183,9 +187,10 @@ class MessageActionsModal extends StatelessWidget { } Widget _buildDeleteButton(BuildContext context) { + final isDeleteFailed = message.status == MessageSendingStatus.FAILED_DELETE; return ListTile( title: Text( - 'Delete message', + isDeleteFailed ? 'Retry deleting message' : 'Delete message', style: Theme.of(context).textTheme.headline6.copyWith(color: Colors.red), ), @@ -234,6 +239,29 @@ class MessageActionsModal extends StatelessWidget { ); } + Widget _buildResendMessage(BuildContext context) { + final isUpdateFailed = message.status == MessageSendingStatus.FAILED_UPDATE; + return ListTile( + title: Text( + isUpdateFailed ? 'Resend edited message' : 'Resend', + style: Theme.of(context).textTheme.headline6, + ), + leading: StreamSvgIcon.circle_up( + color: StreamChatTheme.of(context).accentColor, + ), + onTap: () { + Navigator.pop(context); + final client = StreamChat.of(context).client; + final channel = StreamChannel.of(context).channel; + if (isUpdateFailed) { + client.updateMessage(message, channel.cid); + } else { + channel.sendMessage(message); + } + }, + ); + } + void _showEditBottomSheet(BuildContext context) { final channel = StreamChannel.of(context).channel; showModalBottomSheet( diff --git a/lib/src/message_widget.dart b/lib/src/message_widget.dart index 9d81d679..ee165057 100644 --- a/lib/src/message_widget.dart +++ b/lib/src/message_widget.dart @@ -114,6 +114,9 @@ class MessageWidget extends StatefulWidget { /// Used in [MessageReactionsModal] and [MessageActionsModal] final bool showReactionPickerIndicator; + /// If true the widget will show the resendMessage indicator + final bool showResendMessage; + final List readList; /// If true show the users username next to the timestamp of the message @@ -157,6 +160,7 @@ class MessageWidget extends StatefulWidget { this.editMessageInputBuilder, this.textBuilder, Map customAttachmentBuilders, + this.showResendMessage = true, this.readList, this.padding, this.textPadding = const EdgeInsets.symmetric( @@ -229,6 +233,16 @@ class _MessageWidgetState extends State { bool get showInChannel => widget.showInChannelIndicator && widget.message?.showInChannel == true; + bool get isSendFailed => widget.message.status == MessageSendingStatus.FAILED; + + bool get isUpdateFailed => + widget.message.status == MessageSendingStatus.FAILED_UPDATE; + + bool get isDeleteFailed => + widget.message.status == MessageSendingStatus.FAILED_DELETE; + + bool get isFailedState => isSendFailed || isUpdateFailed || isDeleteFailed; + @override Widget build(BuildContext context) { var leftPadding = widget.showUserAvatar != DisplayWidget.gone @@ -298,9 +312,7 @@ class _MessageWidgetState extends State { ) : EdgeInsets.zero, child: (widget.message.isDeleted && - widget.message.status != - MessageSendingStatus - .FAILED_DELETE) + !isFailedState) ? Transform( alignment: Alignment.center, transform: Matrix4.rotationY( @@ -377,6 +389,21 @@ class _MessageWidgetState extends State { ), ), ), + if (isFailedState) + Positioned( + left: widget.reverse ? -6 : null, + right: widget.reverse ? null : -6, + bottom: 0, + child: Container( + decoration: BoxDecoration( + color: Colors.white, + shape: BoxShape.circle, + ), + child: StreamSvgIcon.error( + size: 20, + ), + ), + ), ], ), ), @@ -619,14 +646,19 @@ class _MessageWidgetState extends State { message: widget.message, editMessageInputBuilder: widget.editMessageInputBuilder, onThreadTap: widget.onThreadTap, - showCopyMessage: widget.message.text?.trim()?.isNotEmpty == true, + showResendMessage: + widget.showResendMessage && (isSendFailed || isUpdateFailed), + showCopyMessage: !isFailedState && + widget.message.text?.trim()?.isNotEmpty == true, showEditMessage: widget.showEditMessage && + !isDeleteFailed && widget.message.attachments ?.any((element) => element.type == 'giphy') != true, showReactions: widget.showReactions, - showReply: - widget.showThreadReplyIndicator && widget.onThreadTap != null, + showReply: widget.showThreadReplyIndicator && + !isFailedState && + widget.onThreadTap != null, ), ); }); @@ -729,7 +761,6 @@ class _MessageWidgetState extends State { final attachmentShape = widget.attachmentShape ?? widget.shape ?? _getDefaultShape(context); return GestureDetector( - onTap: () => retryMessage(context), onLongPress: () => onLongPress(context), child: Material( color: _getBackgroundColor(), @@ -744,17 +775,7 @@ class _MessageWidgetState extends State { child: Transform( transform: Matrix4.rotationY(widget.reverse ? pi : 0), alignment: Alignment.center, - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.end, - children: [ - getFailedMessageWidget( - context, - padding: const EdgeInsets.all(8.0), - ), - attachmentWidget, - ], - ), + child: attachmentWidget, ), ), ), @@ -809,55 +830,6 @@ class _MessageWidgetState extends State { ), ); - Widget getFailedMessageWidget( - BuildContext context, { - EdgeInsetsGeometry padding, - }) { - Widget failedWidget; - if (widget.message.status == MessageSendingStatus.FAILED) { - failedWidget = Text( - 'MESSAGE FAILED · CLICK TO TRY AGAIN', - style: widget.messageTheme.messageText.copyWith( - color: Theme.of(context).brightness == Brightness.dark - ? Colors.white.withOpacity(.5) - : Colors.black.withOpacity(.5), - fontSize: 11, - ), - ); - } - if (widget.message.status == MessageSendingStatus.FAILED_UPDATE) { - failedWidget = Text( - 'MESSAGE UPDATE FAILED · CLICK TO TRY AGAIN', - style: widget.messageTheme.messageText.copyWith( - color: Theme.of(context).brightness == Brightness.dark - ? Colors.white.withOpacity(.5) - : Colors.black.withOpacity(.5), - fontSize: 11, - ), - ); - } - if (widget.message.status == MessageSendingStatus.FAILED_DELETE) { - failedWidget = Text( - 'MESSAGE DELETE FAILED · CLICK TO TRY AGAIN', - style: widget.messageTheme.messageText.copyWith( - color: Theme.of(context).brightness == Brightness.dark - ? Colors.white.withOpacity(.5) - : Colors.black.withOpacity(.5), - fontSize: 11, - ), - ); - } - - if (failedWidget != null) { - return Padding( - padding: padding ?? EdgeInsets.zero, - child: failedWidget, - ); - } - - return SizedBox(); - } - Widget _buildTextBubble(BuildContext context) { Widget child = Transform( transform: Matrix4.rotationY(widget.reverse ? pi : 0), @@ -867,26 +839,20 @@ class _MessageWidgetState extends State { children: [ Padding( padding: widget.textPadding, - child: Column( - crossAxisAlignment: CrossAxisAlignment.end, - children: [ - getFailedMessageWidget(context), - widget.textBuilder != null - ? widget.textBuilder(context, widget.message) - : MessageText( - onLinkTap: widget.onLinkTap, - message: widget.message, - onMentionTap: widget.onMentionTap, - messageTheme: isOnlyEmoji - ? widget.messageTheme.copyWith( - messageText: - widget.messageTheme.messageText.copyWith( - fontSize: 40, - )) - : widget.messageTheme, - ), - ], - ), + child: widget.textBuilder != null + ? widget.textBuilder(context, widget.message) + : MessageText( + onLinkTap: widget.onLinkTap, + message: widget.message, + onMentionTap: widget.onMentionTap, + messageTheme: isOnlyEmoji + ? widget.messageTheme.copyWith( + messageText: + widget.messageTheme.messageText.copyWith( + fontSize: 40, + )) + : widget.messageTheme, + ), ), if (widget.message.attachments ?.any((element) => element.ogScrapeUrl != null) == @@ -896,7 +862,6 @@ class _MessageWidgetState extends State { ), ); return GestureDetector( - onTap: () => retryMessage(context), onLongPress: () => onLongPress(context), child: child, ); @@ -907,12 +872,6 @@ class _MessageWidgetState extends State { widget.message.text.characters.every((c) => Emoji.byChar(c) != null); Color _getBackgroundColor() { - if ((widget.message.status == MessageSendingStatus.FAILED || - widget.message.status == MessageSendingStatus.FAILED_UPDATE || - widget.message.status == MessageSendingStatus.FAILED_DELETE)) { - return Color(0xffd0021B).withOpacity(.1); - } - if (widget.message.attachments ?.any((element) => element.ogScrapeUrl != null) == true) { diff --git a/lib/src/sending_indicator.dart b/lib/src/sending_indicator.dart index ce8915c3..6006f62d 100644 --- a/lib/src/sending_indicator.dart +++ b/lib/src/sending_indicator.dart @@ -35,15 +35,6 @@ class SendingIndicator extends StatelessWidget { size: 8, ); } - if (message.status == MessageSendingStatus.FAILED || - message.status == MessageSendingStatus.FAILED_UPDATE || - message.status == MessageSendingStatus.FAILED_DELETE) { - return Icon( - Icons.error_outline, - size: 8, - ); - } - return SizedBox(); } } diff --git a/lib/src/stream_svg_icon.dart b/lib/src/stream_svg_icon.dart index 98ce0f8e..c75e9327 100644 --- a/lib/src/stream_svg_icon.dart +++ b/lib/src/stream_svg_icon.dart @@ -757,4 +757,29 @@ class StreamSvgIcon extends StatelessWidget { height: size, ); } + + factory StreamSvgIcon.error({ + double size, + Color color, + }) { + return StreamSvgIcon( + assetName: 'Icon_error.svg', + color: color, + width: size, + height: size, + ); + } + + + factory StreamSvgIcon.circle_up({ + double size, + Color color, + }) { + return StreamSvgIcon( + assetName: 'Icon_circle_up.svg', + color: color, + width: size, + height: size, + ); + } } From 168630fcade71ecf7f457bb038e46a478eae5222 Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Thu, 31 Dec 2020 13:11:05 +0530 Subject: [PATCH 2/7] flutter format Signed-off-by: Sahil Kumar --- lib/src/stream_svg_icon.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/src/stream_svg_icon.dart b/lib/src/stream_svg_icon.dart index c75e9327..63eef658 100644 --- a/lib/src/stream_svg_icon.dart +++ b/lib/src/stream_svg_icon.dart @@ -770,7 +770,6 @@ class StreamSvgIcon extends StatelessWidget { ); } - factory StreamSvgIcon.circle_up({ double size, Color color, From ef0bad11929623a533efc4dff5ca29bd42d80506 Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Thu, 31 Dec 2020 14:18:12 +0530 Subject: [PATCH 3/7] [Message Widget] Modify read indicator as per new design v2 Signed-off-by: Sahil Kumar --- lib/src/channel_preview.dart | 6 ++++- lib/src/message_widget.dart | 45 ++++++++++++++++++++++------------ lib/src/sending_indicator.dart | 16 ++++++------ 3 files changed, 43 insertions(+), 24 deletions(-) diff --git a/lib/src/channel_preview.dart b/lib/src/channel_preview.dart index 5f58e254..d6623544 100644 --- a/lib/src/channel_preview.dart +++ b/lib/src/channel_preview.dart @@ -122,7 +122,11 @@ class ChannelPreview extends StatelessWidget { padding: const EdgeInsets.only(right: 4.0), child: SendingIndicator( message: channel.state.lastMessage, - allRead: channel.state.read + size: StreamChatTheme.of(context) + .channelPreviewTheme + .lastMessageAt + .fontSize, + isMessageRead: channel.state.read .where((element) => element.lastRead .isAfter(channel .state.lastMessage.createdAt)) diff --git a/lib/src/message_widget.dart b/lib/src/message_widget.dart index ee165057..226e98c0 100644 --- a/lib/src/message_widget.dart +++ b/lib/src/message_widget.dart @@ -228,7 +228,7 @@ class _MessageWidgetState extends State { bool get showTimeStamp => widget.message.createdAt != null && widget.showTimestamp; - bool get showReadList => widget.readList?.isNotEmpty == true; + bool get isMessageRead => widget.readList?.isNotEmpty == true; bool get showInChannel => widget.showInChannelIndicator && widget.message?.showInChannel == true; @@ -488,14 +488,14 @@ class _MessageWidgetState extends State { children.addAll([ if (showSendingIndicator) _buildSendingIndicator(), - if (showReadList) - SizedBox.fromSize( - size: Size((widget.readList.length * 10.0) + 10, 17), - child: Padding( - padding: const EdgeInsets.only(left: 4.0), - child: _buildReadIndicator(), - ), - ), + // if (showReadList) + // SizedBox.fromSize( + // size: Size((widget.readList.length * 10.0) + 10, 17), + // child: Padding( + // padding: const EdgeInsets.only(left: 4.0), + // child: _buildReadIndicator(), + // ), + // ), if (showThreadReplyIndicator) InkWell( onTap: widget.onThreadTap != null ? onThreadTap : null, @@ -798,14 +798,27 @@ class _MessageWidgetState extends State { } Widget _buildSendingIndicator() { - return Container( - height: widget.messageTheme.createdAt.fontSize + 2, - width: widget.messageTheme.createdAt.fontSize + 2, - child: SendingIndicator( - message: widget.message, - allRead: widget.allRead, - ), + final style = widget.messageTheme.createdAt; + Widget child = SendingIndicator( + message: widget.message, + isMessageRead: isMessageRead, + size: style.fontSize, ); + if (isMessageRead) { + child = Row( + children: [ + Text( + widget.readList.length.toString(), + style: style.copyWith( + color: StreamChatTheme.of(context).accentColor, + ), + ), + SizedBox(width: 2), + child, + ], + ); + } + return child; } Widget _buildUserAvatar() => Transform( diff --git a/lib/src/sending_indicator.dart b/lib/src/sending_indicator.dart index 6006f62d..34492167 100644 --- a/lib/src/sending_indicator.dart +++ b/lib/src/sending_indicator.dart @@ -4,27 +4,29 @@ import 'package:stream_chat_flutter/stream_chat_flutter.dart'; /// Used to show the sending status of the message class SendingIndicator extends StatelessWidget { final Message message; - final bool allRead; + final bool isMessageRead; + final double size; const SendingIndicator({ Key key, this.message, - this.allRead = false, + this.isMessageRead = false, + this.size = 12, }) : super(key: key); @override Widget build(BuildContext context) { - if (allRead) { + if (isMessageRead) { return Icon( - Icons.done_all, - size: 8, + Icons.done_all_rounded, + size: size, color: StreamChatTheme.of(context).accentColor, ); } if (message.status == MessageSendingStatus.SENT || message.status == null) { return Icon( Icons.done, - size: 8, + size: size, color: IconTheme.of(context).color.withOpacity(0.5), ); } @@ -32,7 +34,7 @@ class SendingIndicator extends StatelessWidget { message.status == MessageSendingStatus.UPDATING) { return Icon( Icons.access_time, - size: 8, + size: size, ); } return SizedBox(); From cf31f704d41d02381a9387b42240535712324ac8 Mon Sep 17 00:00:00 2001 From: Salvatore Giordano Date: Thu, 31 Dec 2020 10:53:56 +0100 Subject: [PATCH 4/7] hide bubbles --- lib/src/message_actions_modal.dart | 5 ++++- lib/src/message_reactions_modal.dart | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/lib/src/message_actions_modal.dart b/lib/src/message_actions_modal.dart index 2d482c0e..2cd66807 100644 --- a/lib/src/message_actions_modal.dart +++ b/lib/src/message_actions_modal.dart @@ -119,7 +119,10 @@ class MessageActionsModal extends StatelessWidget { showUserAvatar: showUserAvatar, showTimestamp: false, translateUserAvatar: false, - showReactionPickerIndicator: true, + showReactionPickerIndicator: showReactions && + (message.status == + MessageSendingStatus.SENT || + message.status == null), showInChannelIndicator: false, showSendingIndicator: DisplayWidget.gone, shape: messageShape, diff --git a/lib/src/message_reactions_modal.dart b/lib/src/message_reactions_modal.dart index e5043e62..ed3038fd 100644 --- a/lib/src/message_reactions_modal.dart +++ b/lib/src/message_reactions_modal.dart @@ -110,7 +110,10 @@ class MessageReactionsModal extends StatelessWidget { translateUserAvatar: false, showSendingIndicator: DisplayWidget.gone, shape: messageShape, - showReactionPickerIndicator: true, + showReactionPickerIndicator: showReactions && + (message.status == + MessageSendingStatus.SENT || + message.status == null), ), ), ); From dde32437c82d1339c5b1a085ec62fd0200aafbb9 Mon Sep 17 00:00:00 2001 From: Salvatore Giordano Date: Thu, 31 Dec 2020 11:10:03 +0100 Subject: [PATCH 5/7] fix messageinput send --- lib/src/message_actions_modal.dart | 3 ++- lib/src/message_input.dart | 4 ---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/lib/src/message_actions_modal.dart b/lib/src/message_actions_modal.dart index 2cd66807..d2d25333 100644 --- a/lib/src/message_actions_modal.dart +++ b/lib/src/message_actions_modal.dart @@ -332,9 +332,10 @@ class MessageActionsModal extends StatelessWidget { ? editMessageInputBuilder(context, message) : MessageInput( editMessage: message, - onMessageSent: (_) { + preMessageSending: (m) { FocusScope.of(context).unfocus(); Navigator.pop(context); + return m; }, ), ), diff --git a/lib/src/message_input.dart b/lib/src/message_input.dart index 8436d526..654aa068 100644 --- a/lib/src/message_input.dart +++ b/lib/src/message_input.dart @@ -1976,10 +1976,6 @@ class MessageInputState extends State { return sendingFuture.then((resp) { if (widget.onMessageSent != null) { widget.onMessageSent(resp.message); - } else { - if (widget.editMessage != null) { - Navigator.pop(context); - } } }); } From ab6a741183dfb6f14600019219f7b568a45f5cb1 Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Thu, 31 Dec 2020 15:59:51 +0530 Subject: [PATCH 6/7] [Message Widget] fix error icon stack position Signed-off-by: Sahil Kumar --- lib/src/message_widget.dart | 47 +++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 25 deletions(-) diff --git a/lib/src/message_widget.dart b/lib/src/message_widget.dart index 226e98c0..9834e097 100644 --- a/lib/src/message_widget.dart +++ b/lib/src/message_widget.dart @@ -243,6 +243,12 @@ class _MessageWidgetState extends State { bool get isFailedState => isSendFailed || isUpdateFailed || isDeleteFailed; + bool get showBottomRow => + showThreadReplyIndicator || + showUsername || + showTimeStamp || + showInChannel; + @override Widget build(BuildContext context) { var leftPadding = widget.showUserAvatar != DisplayWidget.gone @@ -267,6 +273,7 @@ class _MessageWidgetState extends State { mainAxisSize: MainAxisSize.min, children: [ Stack( + clipBehavior: Clip.none, alignment: AlignmentDirectional.bottomStart, children: [ Column( @@ -389,39 +396,29 @@ class _MessageWidgetState extends State { ), ), ), - if (isFailedState) - Positioned( - left: widget.reverse ? -6 : null, - right: widget.reverse ? null : -6, - bottom: 0, - child: Container( - decoration: BoxDecoration( - color: Colors.white, - shape: BoxShape.circle, - ), - child: StreamSvgIcon.error( - size: 20, - ), - ), - ), ], ), ), ), ], ), - if (showThreadReplyIndicator || - showUsername || - showTimeStamp || - showInChannel) - SizedBox(height: 20.0), + if (showBottomRow) SizedBox(height: 20.0), ], ), - if (showThreadReplyIndicator || - showUsername || - showTimeStamp || - showInChannel) - _buildBottomRow(leftPadding) + if (showBottomRow) _buildBottomRow(leftPadding), + if (isFailedState) + Positioned( + left: widget.reverse ? -3 : null, + right: widget.reverse ? null : -9, + bottom: showBottomRow ? 20 : 0, + child: Container( + decoration: BoxDecoration( + color: Colors.white, + shape: BoxShape.circle, + ), + child: StreamSvgIcon.error(size: 20), + ), + ), ], ), ], From f59db043c1c05c80b2ea6de493104d0c37e4150a Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Thu, 31 Dec 2020 16:15:16 +0530 Subject: [PATCH 7/7] minor ui fixes Signed-off-by: Sahil Kumar --- lib/src/message_actions_modal.dart | 4 +--- lib/src/message_reactions_modal.dart | 5 ++--- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/lib/src/message_actions_modal.dart b/lib/src/message_actions_modal.dart index d2d25333..fad73d93 100644 --- a/lib/src/message_actions_modal.dart +++ b/lib/src/message_actions_modal.dart @@ -130,9 +130,7 @@ class MessageActionsModal extends StatelessWidget { ), ); }), - SizedBox( - height: 8, - ), + SizedBox(height: 8), TweenAnimationBuilder( tween: Tween(begin: 0.0, end: 1.0), duration: Duration(milliseconds: 300), diff --git a/lib/src/message_reactions_modal.dart b/lib/src/message_reactions_modal.dart index ed3038fd..a8701f4d 100644 --- a/lib/src/message_reactions_modal.dart +++ b/lib/src/message_reactions_modal.dart @@ -110,6 +110,7 @@ class MessageReactionsModal extends StatelessWidget { translateUserAvatar: false, showSendingIndicator: DisplayWidget.gone, shape: messageShape, + showInChannelIndicator: false, showReactionPickerIndicator: showReactions && (message.status == MessageSendingStatus.SENT || @@ -118,9 +119,7 @@ class MessageReactionsModal extends StatelessWidget { ), ); }), - SizedBox( - height: 16, - ), + SizedBox(height: 8), if (message.latestReactions?.isNotEmpty == true) _buildReactionCard(context), ],