diff --git a/.github/workflows/dart_code_metrics.yaml b/.github/workflows/dart_code_metrics.yaml index 0ae85b88..667ac2dd 100644 --- a/.github/workflows/dart_code_metrics.yaml +++ b/.github/workflows/dart_code_metrics.yaml @@ -33,7 +33,7 @@ jobs: flutter-version: ${{ env.flutter_version }} - name: "Install Tools" - run: flutter pub global activate melos 1.0.0-dev.6 + run: flutter pub global activate melos 1.0.0-dev.10 - name: "Bootstrap Workspace" run: melos bootstrap diff --git a/.github/workflows/stream_flutter_workflow.yml b/.github/workflows/stream_flutter_workflow.yml index 65913a18..b91dcb85 100644 --- a/.github/workflows/stream_flutter_workflow.yml +++ b/.github/workflows/stream_flutter_workflow.yml @@ -31,7 +31,7 @@ jobs: flutter-version: ${{ env.flutter_version }} - name: "Install Tools" run: | - flutter pub global activate melos 1.0.0-dev.6 + flutter pub global activate melos 1.0.0-dev.10 - name: "Bootstrap Workspace" run: melos bootstrap - name: "Dart Analyze" diff --git a/packages/stream_chat/CHANGELOG.md b/packages/stream_chat/CHANGELOG.md index 4b409536..2996db07 100644 --- a/packages/stream_chat/CHANGELOG.md +++ b/packages/stream_chat/CHANGELOG.md @@ -1,3 +1,13 @@ +## Upcoming + +✅ Added + +- Extra properties added to `PaginationParams` to aid in fetching messages. + +🐞 Fixed + +- `closeConnection()` now uses `normalClosure` status when closing websocket. + ## 3.2.0 🐞 Fixed @@ -720,4 +730,4 @@ ## 0.0.2 -- first beta version +- first beta version \ No newline at end of file diff --git a/packages/stream_chat/lib/src/core/api/requests.dart b/packages/stream_chat/lib/src/core/api/requests.dart index 14995b10..6f00b374 100644 --- a/packages/stream_chat/lib/src/core/api/requests.dart +++ b/packages/stream_chat/lib/src/core/api/requests.dart @@ -60,8 +60,11 @@ class PaginationParams extends Equatable { /// ``` const PaginationParams({ this.limit = 10, + this.before = 10, + this.after = 10, this.offset, this.next, + this.idAround, this.greaterThan, this.greaterThanOrEqual, this.lessThan, @@ -78,12 +81,22 @@ class PaginationParams extends Equatable { /// The amount of items requested from the APIs. final int limit; + /// The amount of items requested before message ID from the APIs. + final int before; + + /// The amount of items requested after message ID from the APIs. + final int after; + /// The offset of requesting items. final int? offset; /// A key used to paginate. final String? next; + /// Message ID to fetch messages around + @JsonKey(name: 'id_around') + final String? idAround; + /// Filter on ids greater than the given value. @JsonKey(name: 'id_gt') final String? greaterThan; @@ -106,7 +119,10 @@ class PaginationParams extends Equatable { /// Creates a copy of [PaginationParams] with specified attributes overridden. PaginationParams copyWith({ int? limit, + int? before, + int? after, int? offset, + String? idAround, String? next, String? greaterThan, String? greaterThanOrEqual, @@ -115,7 +131,10 @@ class PaginationParams extends Equatable { }) => PaginationParams( limit: limit ?? this.limit, + before: before ?? this.before, + after: limit ?? this.after, offset: offset ?? this.offset, + idAround: idAround ?? this.idAround, next: next ?? this.next, greaterThan: greaterThan ?? this.greaterThan, greaterThanOrEqual: greaterThanOrEqual ?? this.greaterThanOrEqual, @@ -126,8 +145,11 @@ class PaginationParams extends Equatable { @override List get props => [ limit, + before, + after, offset, next, + idAround, greaterThan, greaterThanOrEqual, lessThan, diff --git a/packages/stream_chat/lib/src/core/api/requests.g.dart b/packages/stream_chat/lib/src/core/api/requests.g.dart index ef995997..7d45ee86 100644 --- a/packages/stream_chat/lib/src/core/api/requests.g.dart +++ b/packages/stream_chat/lib/src/core/api/requests.g.dart @@ -21,8 +21,11 @@ Map _$SortOptionToJson(SortOption instance) => PaginationParams _$PaginationParamsFromJson(Map json) => PaginationParams( limit: json['limit'] as int? ?? 10, + before: json['before'] as int? ?? 10, + after: json['after'] as int? ?? 10, offset: json['offset'] as int?, next: json['next'] as String?, + idAround: json['id_around'] as String?, greaterThan: json['id_gt'] as String?, greaterThanOrEqual: json['id_gte'] as String?, lessThan: json['id_lt'] as String?, @@ -32,6 +35,8 @@ PaginationParams _$PaginationParamsFromJson(Map json) => Map _$PaginationParamsToJson(PaginationParams instance) { final val = { 'limit': instance.limit, + 'before': instance.before, + 'after': instance.after, }; void writeNotNull(String key, dynamic value) { @@ -42,6 +47,7 @@ Map _$PaginationParamsToJson(PaginationParams instance) { writeNotNull('offset', instance.offset); writeNotNull('next', instance.next); + writeNotNull('id_around', instance.idAround); writeNotNull('id_gt', instance.greaterThan); writeNotNull('id_gte', instance.greaterThanOrEqual); writeNotNull('id_lt', instance.lessThan); diff --git a/packages/stream_chat/lib/src/ws/websocket.dart b/packages/stream_chat/lib/src/ws/websocket.dart index 05382fb7..a95eae70 100644 --- a/packages/stream_chat/lib/src/ws/websocket.dart +++ b/packages/stream_chat/lib/src/ws/websocket.dart @@ -121,7 +121,8 @@ class WebSocket with TimerHelper { _logger?.info('Closing connection with $baseUrl'); if (_webSocketChannel != null) { _unsubscribeFromWebSocketChannel(); - _webSocketChannel?.sink.close(status.goingAway); + _webSocketChannel?.sink + .close(_manuallyClosed ? status.normalClosure : status.goingAway); _webSocketChannel = null; } } diff --git a/packages/stream_chat_flutter/CHANGELOG.md b/packages/stream_chat_flutter/CHANGELOG.md index 00f8412b..0088abfa 100644 --- a/packages/stream_chat_flutter/CHANGELOG.md +++ b/packages/stream_chat_flutter/CHANGELOG.md @@ -1,3 +1,16 @@ +## Upcoming + +✅ Added + +- `MessageListView` now allows more better control over spacing after messages using `spacingWidgetBuilder`. +- `StreamChannel` can now fetch messages around a message ID with the `queryAroundMessage` call. + +🐞 Fixed + +- [[#766]]`AttachmentActionsModal` now has customisation options for actions. +- Fixed `MessageWidget` null errors associated with `channel.memberCount`. +- Fixed adding attachments on web. + ## 3.2.0 - Updated Dart SDK constraints to `>=2.14.0 <3.0.0` @@ -860,4 +873,4 @@ The property showVideoFullScreen was added mainly because of this issue brianega ## 0.0.1 -- First release +- First release \ No newline at end of file diff --git a/packages/stream_chat_flutter/lib/src/attachment_actions_modal.dart b/packages/stream_chat_flutter/lib/src/attachment_actions_modal.dart index f49561aa..0c10ae46 100644 --- a/packages/stream_chat_flutter/lib/src/attachment_actions_modal.dart +++ b/packages/stream_chat_flutter/lib/src/attachment_actions_modal.dart @@ -24,6 +24,11 @@ class AttachmentActionsModal extends StatelessWidget { this.onShowMessage, this.imageDownloader, this.fileDownloader, + this.showReply = true, + this.showShowInChat = true, + this.showSave = true, + this.showDelete = true, + this.customActions = const [], }) : super(key: key); /// The message containing the attachments @@ -41,6 +46,49 @@ class AttachmentActionsModal extends StatelessWidget { /// Callback to provide download files final AttachmentDownloader? fileDownloader; + /// Show reply option + final bool showReply; + + /// Show show in chat option + final bool showShowInChat; + + /// Show save option + final bool showSave; + + /// Show delete option + final bool showDelete; + + /// List of custom actions + final List customActions; + + /// Creates a copy of [MessageWidget] with specified attributes overridden. + AttachmentActionsModal copyWith({ + Key? key, + int? currentIndex, + Message? message, + VoidCallback? onShowMessage, + AttachmentDownloader? imageDownloader, + AttachmentDownloader? fileDownloader, + bool? showReply, + bool? showShowInChat, + bool? showSave, + bool? showDelete, + List? customActions, + }) => + AttachmentActionsModal( + key: key ?? this.key, + currentIndex: currentIndex ?? this.currentIndex, + message: message ?? this.message, + onShowMessage: onShowMessage ?? this.onShowMessage, + imageDownloader: imageDownloader ?? this.imageDownloader, + fileDownloader: fileDownloader ?? this.fileDownloader, + showReply: showReply ?? this.showReply, + showShowInChat: showShowInChat ?? this.showShowInChat, + showSave: showSave ?? this.showSave, + showDelete: showDelete ?? this.showDelete, + customActions: customActions ?? this.customActions, + ); + @override Widget build(BuildContext context) => GestureDetector( behavior: HitTestBehavior.translucent, @@ -67,82 +115,86 @@ class AttachmentActionsModal extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.end, mainAxisSize: MainAxisSize.min, children: [ - _buildButton( - context, - context.translations.replyLabel, - StreamSvgIcon.iconCurveLineLeftUp( - size: 24, - color: theme.colorTheme.textLowEmphasis, + if (showReply) + _buildButton( + context, + context.translations.replyLabel, + StreamSvgIcon.iconCurveLineLeftUp( + size: 24, + color: theme.colorTheme.textLowEmphasis, + ), + () { + Navigator.pop(context, ReturnActionType.reply); + }, ), - () { - Navigator.pop(context, ReturnActionType.reply); - }, - ), - _buildButton( - context, - context.translations.showInChatLabel, - StreamSvgIcon.eye( - size: 24, - color: theme.colorTheme.textHighEmphasis, + if (showShowInChat) + _buildButton( + context, + context.translations.showInChatLabel, + StreamSvgIcon.eye( + size: 24, + color: theme.colorTheme.textHighEmphasis, + ), + onShowMessage, ), - onShowMessage, - ), - _buildButton( - context, - message.attachments[currentIndex].type == 'video' - ? context.translations.saveVideoLabel - : context.translations.saveImageLabel, - StreamSvgIcon.iconSave( - size: 24, - color: theme.colorTheme.textLowEmphasis, + if (showSave) + _buildButton( + context, + message.attachments[currentIndex].type == 'video' + ? context.translations.saveVideoLabel + : context.translations.saveImageLabel, + StreamSvgIcon.iconSave( + size: 24, + color: theme.colorTheme.textLowEmphasis, + ), + () { + final attachment = message.attachments[currentIndex]; + final isImage = attachment.type == 'image'; + final Future Function( + Attachment, { + void Function(int, int) progressCallback, + }) saveFile = fileDownloader ?? _downloadAttachment; + final Future Function( + Attachment, { + void Function(int, int) progressCallback, + }) saveImage = imageDownloader ?? _downloadAttachment; + final downloader = isImage ? saveImage : saveFile; + + final progressNotifier = + ValueNotifier<_DownloadProgress?>( + _DownloadProgress.initial(), + ); + + downloader( + attachment, + progressCallback: (received, total) { + progressNotifier.value = _DownloadProgress( + total, + received, + ); + }, + ).catchError((e, stk) { + progressNotifier.value = null; + }); + + // Closing attachment actions modal before opening + // attachment download dialog + Navigator.pop(context); + + showDialog( + barrierDismissible: false, + context: context, + barrierColor: theme.colorTheme.overlay, + builder: (context) => _buildDownloadProgressDialog( + context, + progressNotifier, + ), + ); + }, ), - () { - final attachment = message.attachments[currentIndex]; - final isImage = attachment.type == 'image'; - final Future Function( - Attachment, { - void Function(int, int) progressCallback, - }) saveFile = fileDownloader ?? _downloadAttachment; - final Future Function( - Attachment, { - void Function(int, int) progressCallback, - }) saveImage = imageDownloader ?? _downloadAttachment; - final downloader = isImage ? saveImage : saveFile; - - final progressNotifier = - ValueNotifier<_DownloadProgress?>( - _DownloadProgress.initial(), - ); - - downloader( - attachment, - progressCallback: (received, total) { - progressNotifier.value = _DownloadProgress( - total, - received, - ); - }, - ).catchError((e, stk) { - progressNotifier.value = null; - }); - - // Closing attachment actions modal before opening - // attachment download dialog - Navigator.pop(context); - - showDialog( - barrierDismissible: false, - context: context, - barrierColor: theme.colorTheme.overlay, - builder: (context) => _buildDownloadProgressDialog( - context, - progressNotifier, - ), - ); - }, - ), if (StreamChat.of(context).currentUser?.id == - message.user?.id) + message.user?.id && + showDelete) _buildButton( context, context.translations.deleteLabel.capitalize(), @@ -171,6 +223,16 @@ class AttachmentActionsModal extends StatelessWidget { }, color: theme.colorTheme.accentError, ), + ...customActions + .map( + (e) => _buildButton( + context, + e.actionTitle, + e.icon, + e.onTap, + ), + ) + .toList(), ] .map((e) => Align( alignment: Alignment.centerRight, @@ -193,7 +255,7 @@ class AttachmentActionsModal extends StatelessWidget { Widget _buildButton( context, String title, - StreamSvgIcon icon, + Widget icon, VoidCallback? onTap, { Color? color, Key? key, @@ -331,3 +393,22 @@ class _DownloadProgress { int get toPercentage => (received * 100) ~/ total; } + +/// Class for custom attachment action +class AttachmentAction { + /// Constructor for custom attachment action + AttachmentAction({ + required this.actionTitle, + required this.icon, + required this.onTap, + }); + + /// Title for the attachment action + String actionTitle; + + /// Icon for the attachment action + Widget icon; + + /// Callback for when the action is tapped + VoidCallback onTap; +} diff --git a/packages/stream_chat_flutter/lib/src/extension.dart b/packages/stream_chat_flutter/lib/src/extension.dart index 92590de1..73956da6 100644 --- a/packages/stream_chat_flutter/lib/src/extension.dart +++ b/packages/stream_chat_flutter/lib/src/extension.dart @@ -1,6 +1,7 @@ import 'package:characters/characters.dart'; import 'package:diacritic/diacritic.dart'; import 'package:file_picker/file_picker.dart'; +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:stream_chat_flutter/src/emoji/emoji.dart'; import 'package:stream_chat_flutter/src/localization/translations.dart'; @@ -46,7 +47,7 @@ extension IterableX on Iterable { extension PlatformFileX on PlatformFile { /// Converts the [PlatformFile] into [AttachmentFile] AttachmentFile get toAttachmentFile => AttachmentFile( - path: path, + path: kIsWeb ? null : path, name: name, bytes: bytes, size: size, diff --git a/packages/stream_chat_flutter/lib/src/full_screen_media.dart b/packages/stream_chat_flutter/lib/src/full_screen_media.dart index 0c47383c..6547a93d 100644 --- a/packages/stream_chat_flutter/lib/src/full_screen_media.dart +++ b/packages/stream_chat_flutter/lib/src/full_screen_media.dart @@ -33,6 +33,7 @@ class FullScreenMedia extends StatefulWidget { this.startIndex = 0, String? userName, this.onShowMessage, + this.attachmentActionsModalBuilder, }) : userName = userName ?? '', super(key: key); @@ -51,6 +52,11 @@ class FullScreenMedia extends StatefulWidget { /// Callback for when show message is tapped final ShowMessageCallback? onShowMessage; + /// Widget builder for attachment actions modal + /// [defaultActionsModal] is the default [AttachmentActionsModal] config + /// Use [defaultActionsModal.copyWith] to easily customize it + final AttachmentActionsBuilder? attachmentActionsModalBuilder; + @override _FullScreenMediaState createState() => _FullScreenMediaState(); } @@ -196,6 +202,8 @@ class _FullScreenMediaState extends State StreamChannel.of(context).channel, ); }, + attachmentActionsModalBuilder: + widget.attachmentActionsModalBuilder, ), if (!widget.message.isEphemeral) GalleryFooter( diff --git a/packages/stream_chat_flutter/lib/src/gallery_footer.dart b/packages/stream_chat_flutter/lib/src/gallery_footer.dart index dcfa0e29..0acd9b02 100644 --- a/packages/stream_chat_flutter/lib/src/gallery_footer.dart +++ b/packages/stream_chat_flutter/lib/src/gallery_footer.dart @@ -1,7 +1,6 @@ import 'dart:io'; import 'package:cached_network_image/cached_network_image.dart'; -import 'package:dio/dio.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:path_provider/path_provider.dart'; diff --git a/packages/stream_chat_flutter/lib/src/gallery_header.dart b/packages/stream_chat_flutter/lib/src/gallery_header.dart index fc5f67b4..2325af69 100644 --- a/packages/stream_chat_flutter/lib/src/gallery_header.dart +++ b/packages/stream_chat_flutter/lib/src/gallery_header.dart @@ -6,6 +6,15 @@ import 'package:stream_chat_flutter/src/stream_svg_icon.dart'; import 'package:stream_chat_flutter/src/theme/themes.dart'; import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; +/// Widget builder for attachment actions modal +/// [defaultActionsModal] is the default [AttachmentActionsModal] config +/// Use [defaultActionsModal.copyWith] to easily customize it +typedef AttachmentActionsBuilder = Widget Function( + BuildContext context, + Attachment attachment, + AttachmentActionsModal defaultActionsModal, +); + /// Header/AppBar widget for media display screen class GalleryHeader extends StatelessWidget implements PreferredSizeWidget { /// Creates a channel header @@ -21,6 +30,7 @@ class GalleryHeader extends StatelessWidget implements PreferredSizeWidget { this.userName = '', this.sentAt = '', this.backgroundColor, + this.attachmentActionsModalBuilder, }) : preferredSize = const Size.fromHeight(kToolbarHeight), super(key: key); @@ -55,6 +65,11 @@ class GalleryHeader extends StatelessWidget implements PreferredSizeWidget { /// The background color of this [GalleryHeader]. final Color? backgroundColor; + /// Widget builder for attachment actions modal + /// [defaultActionsModal] is the default [AttachmentActionsModal] config + /// Use [defaultActionsModal.copyWith] to easily customize it + final AttachmentActionsBuilder? attachmentActionsModalBuilder; + @override Widget build(BuildContext context) { final galleryHeaderThemeData = GalleryHeaderTheme.of(context); @@ -123,17 +138,26 @@ class GalleryHeader extends StatelessWidget implements PreferredSizeWidget { final galleryHeaderThemeData = StreamChatTheme.of(context).galleryHeaderTheme; + final defaultModal = AttachmentActionsModal( + message: message, + currentIndex: currentIndex, + onShowMessage: onShowMessage, + ); + + final effectiveModal = attachmentActionsModalBuilder?.call( + context, + message.attachments[currentIndex], + defaultModal, + ) ?? + defaultModal; + final result = await showDialog( useRootNavigator: false, context: context, barrierColor: galleryHeaderThemeData.bottomSheetBarrierColor, builder: (context) => StreamChannel( channel: channel, - child: AttachmentActionsModal( - message: message, - currentIndex: currentIndex, - onShowMessage: onShowMessage, - ), + child: effectiveModal, ), ); diff --git a/packages/stream_chat_flutter/lib/src/message_list_view.dart b/packages/stream_chat_flutter/lib/src/message_list_view.dart index b2ea57f6..c61ccfed 100644 --- a/packages/stream_chat_flutter/lib/src/message_list_view.dart +++ b/packages/stream_chat_flutter/lib/src/message_list_view.dart @@ -58,6 +58,46 @@ typedef OnMessageTap = void Function(Message); /// Callback on reply tapped typedef ReplyTapCallback = void Function(Message); +/// Spacing Types (These are properties of a message to help inform the decision +/// of how much space / which widget to build after it) +enum SpacingType { + /// Message is a thread + thread, + + /// There is a >1s time diff between current and last message + timeDiff, + + /// Next message is by a different user + otherUser, + + /// Message is deleted + deleted, + + /// No other conditions are valid, default spacing (This will likely be the + /// only rule in the list provided) + defaultSpacing, +} + +/// Builder for building certain spacing after widgets. +/// This spacing can be in form of any widgets you like. +/// A List of [SpacingType] is provided to help inform the decision of +/// what to build after the message. +/// +/// As an example: +/// MessageListView( +/// spacingWidgetBuilder: (context, list) { +/// if(list.contains(SpacingType.defaultSpacing)) { +/// return SizedBox(height: 2.0,); +/// } else { +/// return SizedBox(height: 8.0,); +/// } +/// }, +/// ), +typedef SpacingWidgetBuilder = Widget Function( + BuildContext context, + List spacingTypes, +); + /// Class for message details // ignore: prefer-match-file-name class MessageDetails { @@ -172,6 +212,7 @@ class MessageListView extends StatefulWidget { this.paginationLimit = 20, this.paginationLoadingIndicatorBuilder, this.keyboardDismissBehavior = ScrollViewKeyboardDismissBehavior.onDrag, + this.spacingWidgetBuilder, }) : super(key: key); /// [ScrollViewKeyboardDismissBehavior] the defines how this [PositionedList] will @@ -294,6 +335,12 @@ class MessageListView extends StatefulWidget { /// Builder used to build the loading indicator shown while paginating. final WidgetBuilder? paginationLoadingIndicatorBuilder; + /// This allows a user to customise the space after a message + /// A List of [SpacingType] is provided to provide more data about the + /// type of message (thread, difference in time between current and last + /// message, default spacing, etc) + final SpacingWidgetBuilder? spacingWidgetBuilder; + @override _MessageListViewState createState() => _MessageListViewState(); } @@ -567,17 +614,38 @@ class _MessageListViewState extends State { Units.MINUTE, ); + final spacingRules = []; + final isNextUserSame = message.user!.id == nextMessage.user?.id; final isThread = message.replyCount! > 0; final isDeleted = message.isDeleted; - if (timeDiff >= 1 || - !isNextUserSame || - isThread || - isDeleted) { - return const SizedBox(height: 8); + final hasTimeDiff = timeDiff >= 1; + + if (hasTimeDiff) { + spacingRules.add(SpacingType.timeDiff); } - return const SizedBox(height: 2); + + if (!isNextUserSame) { + spacingRules.add(SpacingType.otherUser); + } + + if (isThread) { + spacingRules.add(SpacingType.thread); + } + + if (isDeleted) { + spacingRules.add(SpacingType.deleted); + } + + if (spacingRules.isNotEmpty) { + return widget.spacingWidgetBuilder + ?.call(context, spacingRules) ?? + const SizedBox(height: 8); + } + return widget.spacingWidgetBuilder + ?.call(context, [SpacingType.defaultSpacing]) ?? + const SizedBox(height: 2); }, itemBuilder: (context, i) { if (i == itemCount - 1) { diff --git a/packages/stream_chat_flutter/lib/src/message_widget.dart b/packages/stream_chat_flutter/lib/src/message_widget.dart index f6ae52c6..cd2488c3 100644 --- a/packages/stream_chat_flutter/lib/src/message_widget.dart +++ b/packages/stream_chat_flutter/lib/src/message_widget.dart @@ -1230,6 +1230,7 @@ class _MessageWidgetState extends State Widget _buildSendingIndicator() { final style = widget.messageTheme.createdAtStyle; final message = widget.message; + final memberCount = StreamChannel.of(context).channel.memberCount ?? 0; if (hasNonUrlAttachments && (message.status == MessageSendingStatus.sending || @@ -1260,7 +1261,7 @@ class _MessageWidgetState extends State if (isMessageRead) { child = Row( children: [ - if (StreamChannel.of(context).channel.memberCount! > 2) + if (memberCount > 2) Text( widget.readList!.length.toString(), style: style.copyWith( diff --git a/packages/stream_chat_flutter/lib/stream_chat_flutter.dart b/packages/stream_chat_flutter/lib/stream_chat_flutter.dart index 0447fc68..cd0d93a0 100644 --- a/packages/stream_chat_flutter/lib/stream_chat_flutter.dart +++ b/packages/stream_chat_flutter/lib/stream_chat_flutter.dart @@ -2,6 +2,7 @@ export 'package:jiffy/jiffy.dart'; export 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; export 'src/attachment/attachment.dart'; +export 'src/attachment_actions_modal.dart'; export 'src/back_button.dart'; export 'src/channel_avatar.dart'; export 'src/channel_header.dart'; diff --git a/packages/stream_chat_flutter_core/lib/src/stream_channel.dart b/packages/stream_chat_flutter_core/lib/src/stream_channel.dart index 8117a26e..633c3137 100644 --- a/packages/stream_chat_flutter_core/lib/src/stream_channel.dart +++ b/packages/stream_chat_flutter_core/lib/src/stream_channel.dart @@ -227,13 +227,13 @@ class StreamChannelState extends State { preferOffline: preferOffline, ); - Future> _queryAtMessage({ + Future _queryAtMessage({ String? messageId, int before = 20, int after = 20, bool preferOffline = false, }) async { - if (channel.state == null) return []; + if (channel.state == null) return null; channel.state!.isUpToDate = false; channel.state!.truncate(); @@ -245,23 +245,33 @@ class StreamChannelState extends State { preferOffline: preferOffline, ); channel.state!.isUpToDate = true; - return []; + return null; } - return Future.wait([ - queryBeforeMessage( - messageId, - limit: before, - preferOffline: preferOffline, - ), - queryAfterMessage( - messageId, - limit: after, - preferOffline: preferOffline, - ), - ]); + return queryAroundMessage( + messageId, + before: before, + after: after, + preferOffline: preferOffline, + ); } + /// + Future queryAroundMessage( + String messageId, { + int before = 20, + int after = 20, + bool preferOffline = false, + }) => + channel.query( + messagesPagination: PaginationParams( + idAround: messageId, + before: before, + after: after, + ), + preferOffline: preferOffline, + ); + /// Future queryBeforeMessage( String messageId, {