fix(ui): fix various message_list_view issues.

Signed-off-by: xsahil03x <xdsahil@gmail.com>
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,
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(),
),
],
),
@@ -1372,10 +1372,10 @@ class StreamMessageInputState extends State<StreamMessageInput>
}
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<StreamMessageInput>
final skipEnrichUrl = _effectiveController.ogAttachment == null;
var shouldKeepFocus = widget.shouldKeepFocusAfterMessage;
shouldKeepFocus ??= !_commandEnabled;
widget.onQuotedMessageCleared?.call();
_effectiveController.reset();
@@ -1411,12 +1411,35 @@ class StreamMessageInputState extends State<StreamMessageInput>
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<void> 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<StreamMessageInput>
);
}
if (shouldKeepFocus) {
FocusScope.of(context).requestFocus(_effectiveFocusNode);
} else {
FocusScope.of(context).unfocus();
}
final resp = await sendingFuture;
if (resp.message?.type == 'error') {
_effectiveController.message = message;
@@ -566,21 +566,30 @@ class _StreamMessageListViewState extends State<StreamMessageListView> {
reverse: widget.reverse,
shrinkWrap: widget.shrinkWrap,
itemCount: itemCount,
findChildIndexCallback: (Key key) {
final indexedKey = key as IndexedKey;
final valueKey = indexedKey.key as ValueKey<String>?;
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<String>?;
// 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<StreamMessageListView> {
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<StreamMessageListView> {
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<StreamMessageListView> {
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 ?? [];
@@ -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) {