Merge pull request #1578 from GetStream/fix/broken-message-list-view
This commit is contained in:
@@ -20,7 +20,10 @@
|
|||||||
for `WebOrDesktopAttachmentPickerOption` in `StreamMessageInput`.
|
for `WebOrDesktopAttachmentPickerOption` in `StreamMessageInput`.
|
||||||
- [[#1250]](https://github.com/GetStream/stream-chat-flutter/issues/1250) Fixed bottomRow widgetSpans getting resized
|
- [[#1250]](https://github.com/GetStream/stream-chat-flutter/issues/1250) Fixed bottomRow widgetSpans getting resized
|
||||||
twice when `textScaling` is enabled.
|
twice when `textScaling` is enabled.
|
||||||
- [[#1498]](https://github.com/GetStream/stream-chat-flutter/issues/1498) Fixed `MessageInput` autocomplete not working on non-mobile platforms.
|
- [[#1498]](https://github.com/GetStream/stream-chat-flutter/issues/1498) Fixed `MessageInput` autocomplete not working
|
||||||
|
on non-mobile platforms.
|
||||||
|
- [[#1576]](https://github.com/GetStream/stream-chat-flutter/issues/1576) Temporary fix for `StreamMessageListView`
|
||||||
|
getting broken when loaded at a particular message and a new message is added.
|
||||||
|
|
||||||
✅ Added
|
✅ Added
|
||||||
|
|
||||||
|
|||||||
@@ -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,33 @@ 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:
|
// Issues faced:
|
||||||
// * Add 2 to the index retrieved to account for the footer and the bottom loader.
|
// * https://github.com/GetStream/stream-chat-flutter/issues/1576
|
||||||
// * Multiply the result by 2 to account for the separators between each pair of items.
|
// * https://github.com/GetStream/stream-chat-flutter/issues/1414
|
||||||
// * Subtract 1 to adjust for the 0-based indexing of the list view.
|
//
|
||||||
return ((index + 2) * 2) - 1;
|
// Related issues: https://github.com/flutter/flutter/issues/107123
|
||||||
}
|
//
|
||||||
}
|
// findChildIndexCallback: (Key key) {
|
||||||
return null;
|
// 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)
|
// 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 +911,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 +995,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 +1099,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) {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
- Fixed `StreamMessageInputController.textPatternStyle` not matching case-insensitive patterns.
|
- Fixed `StreamMessageInputController.textPatternStyle` not matching case-insensitive patterns.
|
||||||
- Updated `connectivity_plus` dependency to `^4.0.0`
|
- Updated `connectivity_plus` dependency to `^4.0.0`
|
||||||
|
- Fixed `StreamChannel` shows black screen while loading in some cases.
|
||||||
|
|
||||||
## 6.1.0
|
## 6.1.0
|
||||||
|
|
||||||
|
|||||||
@@ -251,14 +251,14 @@ class StreamChannelState extends State<StreamChannel> {
|
|||||||
channel.state!.truncate();
|
channel.state!.truncate();
|
||||||
|
|
||||||
if (messageId == null) {
|
if (messageId == null) {
|
||||||
await channel.query(
|
final state = await channel.query(
|
||||||
messagesPagination: PaginationParams(
|
messagesPagination: PaginationParams(
|
||||||
limit: limit,
|
limit: limit,
|
||||||
),
|
),
|
||||||
preferOffline: preferOffline,
|
preferOffline: preferOffline,
|
||||||
);
|
);
|
||||||
channel.state!.isUpToDate = true;
|
channel.state!.isUpToDate = true;
|
||||||
return null;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
return channel.query(
|
return channel.query(
|
||||||
@@ -451,7 +451,7 @@ class StreamChannelState extends State<StreamChannel> {
|
|||||||
return widget.child;
|
return widget.child;
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
if (initialMessageId != null) {
|
if (_futures.length > 1) {
|
||||||
child = Material(child: child);
|
child = Material(child: child);
|
||||||
}
|
}
|
||||||
return child;
|
return child;
|
||||||
|
|||||||
Reference in New Issue
Block a user