From a14731feea681d573f04afe7e4513360fa2df500 Mon Sep 17 00:00:00 2001 From: Sahil Kumar Date: Wed, 31 May 2023 16:46:57 +0530 Subject: [PATCH] fix(ui): fix various message_list_view issues. Signed-off-by: xsahil03x --- .../lib/src/attachment/giphy_attachment.dart | 6 +- .../message_input/stream_message_input.dart | 39 ++++++++--- .../message_list_view/message_list_view.dart | 68 +++++++++---------- .../src/message_widget/message_widget.dart | 35 +++++----- 4 files changed, 82 insertions(+), 66 deletions(-) diff --git a/packages/stream_chat_flutter/lib/src/attachment/giphy_attachment.dart b/packages/stream_chat_flutter/lib/src/attachment/giphy_attachment.dart index 4d5f9d99..85997b64 100644 --- a/packages/stream_chat_flutter/lib/src/attachment/giphy_attachment.dart +++ b/packages/stream_chat_flutter/lib/src/attachment/giphy_attachment.dart @@ -59,6 +59,7 @@ class StreamGiphyAttachment extends StreamAttachmentWidget { color: StreamChatTheme.of(context).colorTheme.barsBg, elevation: 2, clipBehavior: Clip.hardEdge, + margin: EdgeInsets.zero, shape: const RoundedRectangleBorder( borderRadius: BorderRadius.only( topRight: Radius.circular(16), @@ -241,10 +242,7 @@ class StreamGiphyAttachment extends StreamAttachmentWidget { const SizedBox(height: 4), const Align( alignment: Alignment.centerRight, - child: Padding( - padding: EdgeInsets.symmetric(horizontal: 8, vertical: 4), - child: StreamVisibleFootnote(), - ), + child: StreamVisibleFootnote(), ), ], ), diff --git a/packages/stream_chat_flutter/lib/src/message_input/stream_message_input.dart b/packages/stream_chat_flutter/lib/src/message_input/stream_message_input.dart index 3a96e008..8baf5913 100644 --- a/packages/stream_chat_flutter/lib/src/message_input/stream_message_input.dart +++ b/packages/stream_chat_flutter/lib/src/message_input/stream_message_input.dart @@ -1372,10 +1372,10 @@ class StreamMessageInputState extends State } final streamChannel = StreamChannel.of(context); + final channel = streamChannel.channel; var message = _effectiveController.value; - if (!streamChannel.channel.ownCapabilities - .contains(PermissionType.sendLinks) && + if (!channel.ownCapabilities.contains(PermissionType.sendLinks) && _urlRegex.allMatches(message.text ?? '').any((element) => element.group(0)?.split('.').last.isValidTLD() == true)) { showInfoBottomSheet( @@ -1401,8 +1401,8 @@ class StreamMessageInputState extends State final skipEnrichUrl = _effectiveController.ogAttachment == null; var shouldKeepFocus = widget.shouldKeepFocusAfterMessage; - shouldKeepFocus ??= !_commandEnabled; + widget.onQuotedMessageCleared?.call(); _effectiveController.reset(); @@ -1411,12 +1411,35 @@ class StreamMessageInputState extends State message = await widget.preMessageSending!(message); } - final channel = streamChannel.channel; + message = message.replaceMentionsWithId(); + + // If the channel is not up to date, we should reload it before sending + // the message. if (!channel.state!.isUpToDate) { await streamChannel.reloadChannel(); + + // We need to wait for the frame to be rendered with the updated channel + // state before sending the message. + await WidgetsBinding.instance.endOfFrame; } - message = message.replaceMentionsWithId(); + await sendOrUpdateMessage( + message: message, + skipEnrichUrl: skipEnrichUrl, + ); + + if (shouldKeepFocus) { + FocusScope.of(context).requestFocus(_effectiveFocusNode); + } else { + FocusScope.of(context).unfocus(); + } + } + + Future sendOrUpdateMessage({ + required Message message, + bool skipEnrichUrl = false, + }) async { + final channel = StreamChannel.of(context).channel; try { Future sendingFuture; @@ -1432,12 +1455,6 @@ class StreamMessageInputState extends State ); } - if (shouldKeepFocus) { - FocusScope.of(context).requestFocus(_effectiveFocusNode); - } else { - FocusScope.of(context).unfocus(); - } - final resp = await sendingFuture; if (resp.message?.type == 'error') { _effectiveController.message = message; diff --git a/packages/stream_chat_flutter/lib/src/message_list_view/message_list_view.dart b/packages/stream_chat_flutter/lib/src/message_list_view/message_list_view.dart index 9060e96f..67ef1488 100644 --- a/packages/stream_chat_flutter/lib/src/message_list_view/message_list_view.dart +++ b/packages/stream_chat_flutter/lib/src/message_list_view/message_list_view.dart @@ -566,21 +566,30 @@ class _StreamMessageListViewState extends State { reverse: widget.reverse, shrinkWrap: widget.shrinkWrap, itemCount: itemCount, - findChildIndexCallback: (Key key) { - final indexedKey = key as IndexedKey; - final valueKey = indexedKey.key as ValueKey?; - if (valueKey != null) { - final index = messagesIndex[valueKey.value]; - if (index != null) { - // The calculation is as follows: - // * Add 2 to the index retrieved to account for the footer and the bottom loader. - // * Multiply the result by 2 to account for the separators between each pair of items. - // * Subtract 1 to adjust for the 0-based indexing of the list view. - return ((index + 2) * 2) - 1; - } - } - return null; - }, + + // Commented out as it is not working as expected. + // The list view gets broken in the following case: + // * The list view is loaded at a particular message (eg: Last Read, or a quoted message) + // and a new message is added to the list view. + // + // Github faced: https://github.com/GetStream/stream-chat-flutter/issues/1576 + // Related issues: https://github.com/flutter/flutter/issues/107123 + // + // findChildIndexCallback: (Key key) { + // final indexedKey = key as IndexedKey; + // final valueKey = indexedKey.key as ValueKey?; + // if (valueKey != null) { + // final index = messagesIndex[valueKey.value]; + // if (index != null) { + // // The calculation is as follows: + // // * Add 2 to the index retrieved to account for the footer and the bottom loader. + // // * Multiply the result by 2 to account for the separators between each pair of items. + // // * Subtract 1 to adjust for the 0-based indexing of the list view. + // return ((index + 2) * 2) - 1; + // } + // } + // return null; + // }, // Item Count -> 8 (1 parent, 2 header+footer, 2 top+bottom, 3 messages) // eg: |Type| rev(|Index(item)|) rev(|Index(separator)|) |Index(item)| |Index(separator)| @@ -899,7 +908,10 @@ class _StreamMessageListViewState extends State { final hasUrlAttachment = message.attachments.any((it) => it.ogScrapeUrl != null); - final borderSide = isOnlyEmoji || hasUrlAttachment ? BorderSide.none : null; + final isEphemeral = message.isEphemeral; + + final borderSide = + isOnlyEmoji || hasUrlAttachment || isEphemeral ? BorderSide.none : null; final defaultMessageWidget = StreamMessageWidget( showReplyMessage: false, @@ -980,24 +992,7 @@ class _StreamMessageListViewState extends State { FloatingActionButton( backgroundColor: _streamTheme.colorTheme.barsBg, onPressed: () async { - if (unreadCount > 0) { - streamChannel!.channel.markRead(); - } - if (!_upToDate) { - _bottomPaginationActive = false; - initialAlignment = 0; - initialIndex = 0; - await streamChannel!.reloadChannel(); - - WidgetsBinding.instance.addPostFrameCallback((_) { - _scrollController!.jumpTo(index: 0); - }); - } else { - _showScrollToBottom.value = false; - _scrollController!.jumpTo( - index: 0, - ); - } + return scrollToBottomDefaultTapAction(unreadCount); }, child: widget.reverse ? StreamSvgIcon.down( @@ -1101,10 +1096,13 @@ class _StreamMessageListViewState extends State { final showThreadReplyIndicator = !_isThreadConversation && hasReplies; final isOnlyEmoji = message.text?.isOnlyEmoji ?? false; + final isEphemeral = message.isEphemeral; + final hasUrlAttachment = message.attachments.any((it) => it.ogScrapeUrl != null); - final borderSide = isOnlyEmoji || hasUrlAttachment ? BorderSide.none : null; + final borderSide = + isOnlyEmoji || hasUrlAttachment || isEphemeral ? BorderSide.none : null; final currentUser = StreamChat.of(context).currentUser; final members = StreamChannel.of(context).channel.state?.members ?? []; diff --git a/packages/stream_chat_flutter/lib/src/message_widget/message_widget.dart b/packages/stream_chat_flutter/lib/src/message_widget/message_widget.dart index 0eb30cfb..b002d2b5 100644 --- a/packages/stream_chat_flutter/lib/src/message_widget/message_widget.dart +++ b/packages/stream_chat_flutter/lib/src/message_widget/message_widget.dart @@ -217,17 +217,9 @@ class StreamMessageWidget extends StatefulWidget { ); }, 'giphy': (context, message, attachments) { - final border = RoundedRectangleBorder( - side: attachmentBorderSide ?? - BorderSide( - color: StreamChatTheme.of(context).colorTheme.borders, - ), - borderRadius: attachmentBorderRadiusGeometry ?? BorderRadius.zero, - ); - - return WrapAttachmentWidget( - attachmentWidget: Column( - children: attachments.map((attachment) { + final attachmentWidget = Column( + children: [ + ...attachments.map((attachment) { final mediaQueryData = MediaQuery.of(context); return StreamGiphyAttachment( attachment: attachment, @@ -240,14 +232,25 @@ class StreamMessageWidget extends StatefulWidget { onShowMessage: onShowMessage, onReplyMessage: onReplyTap, onAttachmentTap: onAttachmentTap != null - ? () { - onAttachmentTap(message, attachment); - } + ? () => onAttachmentTap(message, attachment) : null, ); - }).toList(), - ), + }), + ], + ); + + // If the message is ephemeral, we don't want to show the border. + if (message.isEphemeral) return attachmentWidget; + + final color = StreamChatTheme.of(context).colorTheme.borders; + final border = RoundedRectangleBorder( + side: attachmentBorderSide ?? BorderSide(color: color), + borderRadius: attachmentBorderRadiusGeometry ?? BorderRadius.zero, + ); + + return WrapAttachmentWidget( attachmentShape: border, + attachmentWidget: attachmentWidget, ); }, 'file': (context, message, attachments) {