fix(ui): fix various message_list_view issues.

Signed-off-by: xsahil03x <[email protected]>
This commit is contained in:
Sahil Kumar
2023-05-31 16:46:57 +05:30
committed by xsahil03x
parent c013fee5cf
commit a14731feea
4 changed files with 82 additions and 66 deletions
@@ -59,6 +59,7 @@ class StreamGiphyAttachment extends StreamAttachmentWidget {
color: StreamChatTheme.of(context).colorTheme.barsBg, color: StreamChatTheme.of(context).colorTheme.barsBg,
elevation: 2, elevation: 2,
clipBehavior: Clip.hardEdge, clipBehavior: Clip.hardEdge,
margin: EdgeInsets.zero,
shape: const RoundedRectangleBorder( shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.only( borderRadius: BorderRadius.only(
topRight: Radius.circular(16), topRight: Radius.circular(16),
@@ -241,10 +242,7 @@ class StreamGiphyAttachment extends StreamAttachmentWidget {
const SizedBox(height: 4), const SizedBox(height: 4),
const Align( const Align(
alignment: Alignment.centerRight, alignment: Alignment.centerRight,
child: Padding( child: StreamVisibleFootnote(),
padding: EdgeInsets.symmetric(horizontal: 8, vertical: 4),
child: StreamVisibleFootnote(),
),
), ),
], ],
), ),
@@ -1372,10 +1372,10 @@ class StreamMessageInputState extends State<StreamMessageInput>
} }
final streamChannel = StreamChannel.of(context); final streamChannel = StreamChannel.of(context);
final channel = streamChannel.channel;
var message = _effectiveController.value; var message = _effectiveController.value;
if (!streamChannel.channel.ownCapabilities if (!channel.ownCapabilities.contains(PermissionType.sendLinks) &&
.contains(PermissionType.sendLinks) &&
_urlRegex.allMatches(message.text ?? '').any((element) => _urlRegex.allMatches(message.text ?? '').any((element) =>
element.group(0)?.split('.').last.isValidTLD() == true)) { element.group(0)?.split('.').last.isValidTLD() == true)) {
showInfoBottomSheet( showInfoBottomSheet(
@@ -1401,8 +1401,8 @@ class StreamMessageInputState extends State<StreamMessageInput>
final skipEnrichUrl = _effectiveController.ogAttachment == null; final skipEnrichUrl = _effectiveController.ogAttachment == null;
var shouldKeepFocus = widget.shouldKeepFocusAfterMessage; var shouldKeepFocus = widget.shouldKeepFocusAfterMessage;
shouldKeepFocus ??= !_commandEnabled; shouldKeepFocus ??= !_commandEnabled;
widget.onQuotedMessageCleared?.call(); widget.onQuotedMessageCleared?.call();
_effectiveController.reset(); _effectiveController.reset();
@@ -1411,12 +1411,35 @@ class StreamMessageInputState extends State<StreamMessageInput>
message = await widget.preMessageSending!(message); 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) { if (!channel.state!.isUpToDate) {
await streamChannel.reloadChannel(); 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<void> sendOrUpdateMessage({
required Message message,
bool skipEnrichUrl = false,
}) async {
final channel = StreamChannel.of(context).channel;
try { try {
Future sendingFuture; Future sendingFuture;
@@ -1432,12 +1455,6 @@ class StreamMessageInputState extends State<StreamMessageInput>
); );
} }
if (shouldKeepFocus) {
FocusScope.of(context).requestFocus(_effectiveFocusNode);
} else {
FocusScope.of(context).unfocus();
}
final resp = await sendingFuture; final resp = await sendingFuture;
if (resp.message?.type == 'error') { if (resp.message?.type == 'error') {
_effectiveController.message = message; _effectiveController.message = message;
@@ -566,21 +566,30 @@ class _StreamMessageListViewState extends State<StreamMessageListView> {
reverse: widget.reverse, reverse: widget.reverse,
shrinkWrap: widget.shrinkWrap, shrinkWrap: widget.shrinkWrap,
itemCount: itemCount, itemCount: itemCount,
findChildIndexCallback: (Key key) {
final indexedKey = key as IndexedKey; // Commented out as it is not working as expected.
final valueKey = indexedKey.key as ValueKey<String>?; // The list view gets broken in the following case:
if (valueKey != null) { // * The list view is loaded at a particular message (eg: Last Read, or a quoted message)
final index = messagesIndex[valueKey.value]; // and a new message is added to the list view.
if (index != null) { //
// The calculation is as follows: // Github faced: https://github.com/GetStream/stream-chat-flutter/issues/1576
// * Add 2 to the index retrieved to account for the footer and the bottom loader. // Related issues: https://github.com/flutter/flutter/issues/107123
// * 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. // findChildIndexCallback: (Key key) {
return ((index + 2) * 2) - 1; // final indexedKey = key as IndexedKey;
} // final valueKey = indexedKey.key as ValueKey<String>?;
} // if (valueKey != null) {
return 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) // 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)| // eg: |Type| rev(|Index(item)|) rev(|Index(separator)|) |Index(item)| |Index(separator)|
@@ -899,7 +908,10 @@ class _StreamMessageListViewState extends State<StreamMessageListView> {
final hasUrlAttachment = final hasUrlAttachment =
message.attachments.any((it) => it.ogScrapeUrl != null); 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( final defaultMessageWidget = StreamMessageWidget(
showReplyMessage: false, showReplyMessage: false,
@@ -980,24 +992,7 @@ class _StreamMessageListViewState extends State<StreamMessageListView> {
FloatingActionButton( FloatingActionButton(
backgroundColor: _streamTheme.colorTheme.barsBg, backgroundColor: _streamTheme.colorTheme.barsBg,
onPressed: () async { onPressed: () async {
if (unreadCount > 0) { return scrollToBottomDefaultTapAction(unreadCount);
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,
);
}
}, },
child: widget.reverse child: widget.reverse
? StreamSvgIcon.down( ? StreamSvgIcon.down(
@@ -1101,10 +1096,13 @@ class _StreamMessageListViewState extends State<StreamMessageListView> {
final showThreadReplyIndicator = !_isThreadConversation && hasReplies; final showThreadReplyIndicator = !_isThreadConversation && hasReplies;
final isOnlyEmoji = message.text?.isOnlyEmoji ?? false; final isOnlyEmoji = message.text?.isOnlyEmoji ?? false;
final isEphemeral = message.isEphemeral;
final hasUrlAttachment = final hasUrlAttachment =
message.attachments.any((it) => it.ogScrapeUrl != null); 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 currentUser = StreamChat.of(context).currentUser;
final members = StreamChannel.of(context).channel.state?.members ?? []; final members = StreamChannel.of(context).channel.state?.members ?? [];
@@ -217,17 +217,9 @@ class StreamMessageWidget extends StatefulWidget {
); );
}, },
'giphy': (context, message, attachments) { 'giphy': (context, message, attachments) {
final border = RoundedRectangleBorder( final attachmentWidget = Column(
side: attachmentBorderSide ?? children: [
BorderSide( ...attachments.map((attachment) {
color: StreamChatTheme.of(context).colorTheme.borders,
),
borderRadius: attachmentBorderRadiusGeometry ?? BorderRadius.zero,
);
return WrapAttachmentWidget(
attachmentWidget: Column(
children: attachments.map((attachment) {
final mediaQueryData = MediaQuery.of(context); final mediaQueryData = MediaQuery.of(context);
return StreamGiphyAttachment( return StreamGiphyAttachment(
attachment: attachment, attachment: attachment,
@@ -240,14 +232,25 @@ class StreamMessageWidget extends StatefulWidget {
onShowMessage: onShowMessage, onShowMessage: onShowMessage,
onReplyMessage: onReplyTap, onReplyMessage: onReplyTap,
onAttachmentTap: onAttachmentTap != null onAttachmentTap: onAttachmentTap != null
? () { ? () => onAttachmentTap(message, attachment)
onAttachmentTap(message, attachment);
}
: null, : 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, attachmentShape: border,
attachmentWidget: attachmentWidget,
); );
}, },
'file': (context, message, attachments) { 'file': (context, message, attachments) {