Merge pull request #660 from GetStream/fix/message-search-pagination
fix(llc, ui, core): list pagination
This commit is contained in:
@@ -13,6 +13,8 @@
|
|||||||
✅ Added
|
✅ Added
|
||||||
|
|
||||||
- Added `Filter.contains` and `Filter.empty`
|
- Added `Filter.contains` and `Filter.empty`
|
||||||
|
- Added support for `next`, `previous` value pagination in `client.search`
|
||||||
|
, [read more.](https://getstream.io/chat/docs/other-rest/search/#pagination)
|
||||||
|
|
||||||
## 2.2.1
|
## 2.2.1
|
||||||
|
|
||||||
|
|||||||
@@ -36,6 +36,10 @@ class GeneralApi {
|
|||||||
PaginationParams? pagination,
|
PaginationParams? pagination,
|
||||||
Filter? messageFilters,
|
Filter? messageFilters,
|
||||||
}) async {
|
}) async {
|
||||||
|
assert(
|
||||||
|
pagination?.offset == null || pagination?.offset == 0 || sort == null,
|
||||||
|
'Cannot specify `offset` with `sort` parameter',
|
||||||
|
);
|
||||||
assert(() {
|
assert(() {
|
||||||
if (query == null && messageFilters == null) {
|
if (query == null && messageFilters == null) {
|
||||||
throw ArgumentError('Provide at least `query` or `messageFilters`');
|
throw ArgumentError('Provide at least `query` or `messageFilters`');
|
||||||
|
|||||||
@@ -60,12 +60,16 @@ class PaginationParams extends Equatable {
|
|||||||
/// ```
|
/// ```
|
||||||
const PaginationParams({
|
const PaginationParams({
|
||||||
this.limit = 10,
|
this.limit = 10,
|
||||||
this.offset = 0,
|
this.offset,
|
||||||
|
this.next,
|
||||||
this.greaterThan,
|
this.greaterThan,
|
||||||
this.greaterThanOrEqual,
|
this.greaterThanOrEqual,
|
||||||
this.lessThan,
|
this.lessThan,
|
||||||
this.lessThanOrEqual,
|
this.lessThanOrEqual,
|
||||||
});
|
}) : assert(
|
||||||
|
offset == null || offset == 0 || next == null,
|
||||||
|
'Cannot specify non-zero `offset` with `next` parameter',
|
||||||
|
);
|
||||||
|
|
||||||
/// Create a new instance from a json
|
/// Create a new instance from a json
|
||||||
factory PaginationParams.fromJson(Map<String, dynamic> json) =>
|
factory PaginationParams.fromJson(Map<String, dynamic> json) =>
|
||||||
@@ -75,7 +79,10 @@ class PaginationParams extends Equatable {
|
|||||||
final int limit;
|
final int limit;
|
||||||
|
|
||||||
/// The offset of requesting items.
|
/// The offset of requesting items.
|
||||||
final int offset;
|
final int? offset;
|
||||||
|
|
||||||
|
/// A key used to paginate.
|
||||||
|
final String? next;
|
||||||
|
|
||||||
/// Filter on ids greater than the given value.
|
/// Filter on ids greater than the given value.
|
||||||
@JsonKey(name: 'id_gt')
|
@JsonKey(name: 'id_gt')
|
||||||
@@ -100,6 +107,7 @@ class PaginationParams extends Equatable {
|
|||||||
PaginationParams copyWith({
|
PaginationParams copyWith({
|
||||||
int? limit,
|
int? limit,
|
||||||
int? offset,
|
int? offset,
|
||||||
|
String? next,
|
||||||
String? greaterThan,
|
String? greaterThan,
|
||||||
String? greaterThanOrEqual,
|
String? greaterThanOrEqual,
|
||||||
String? lessThan,
|
String? lessThan,
|
||||||
@@ -108,6 +116,7 @@ class PaginationParams extends Equatable {
|
|||||||
PaginationParams(
|
PaginationParams(
|
||||||
limit: limit ?? this.limit,
|
limit: limit ?? this.limit,
|
||||||
offset: offset ?? this.offset,
|
offset: offset ?? this.offset,
|
||||||
|
next: next ?? this.next,
|
||||||
greaterThan: greaterThan ?? this.greaterThan,
|
greaterThan: greaterThan ?? this.greaterThan,
|
||||||
greaterThanOrEqual: greaterThanOrEqual ?? this.greaterThanOrEqual,
|
greaterThanOrEqual: greaterThanOrEqual ?? this.greaterThanOrEqual,
|
||||||
lessThan: lessThan ?? this.lessThan,
|
lessThan: lessThan ?? this.lessThan,
|
||||||
@@ -118,6 +127,7 @@ class PaginationParams extends Equatable {
|
|||||||
List<Object?> get props => [
|
List<Object?> get props => [
|
||||||
limit,
|
limit,
|
||||||
offset,
|
offset,
|
||||||
|
next,
|
||||||
greaterThan,
|
greaterThan,
|
||||||
greaterThanOrEqual,
|
greaterThanOrEqual,
|
||||||
lessThan,
|
lessThan,
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ PaginationParams _$PaginationParamsFromJson(Map<String, dynamic> json) {
|
|||||||
return PaginationParams(
|
return PaginationParams(
|
||||||
limit: json['limit'] as int,
|
limit: json['limit'] as int,
|
||||||
offset: json['offset'] as int,
|
offset: json['offset'] as int,
|
||||||
|
next: json['next'] as String?,
|
||||||
greaterThan: json['id_gt'] as String?,
|
greaterThan: json['id_gt'] as String?,
|
||||||
greaterThanOrEqual: json['id_gte'] as String?,
|
greaterThanOrEqual: json['id_gte'] as String?,
|
||||||
lessThan: json['id_lt'] as String?,
|
lessThan: json['id_lt'] as String?,
|
||||||
@@ -42,6 +43,7 @@ Map<String, dynamic> _$PaginationParamsToJson(PaginationParams instance) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
writeNotNull('next', instance.next);
|
||||||
writeNotNull('id_gt', instance.greaterThan);
|
writeNotNull('id_gt', instance.greaterThan);
|
||||||
writeNotNull('id_gte', instance.greaterThanOrEqual);
|
writeNotNull('id_gte', instance.greaterThanOrEqual);
|
||||||
writeNotNull('id_lt', instance.lessThan);
|
writeNotNull('id_lt', instance.lessThan);
|
||||||
|
|||||||
@@ -253,6 +253,12 @@ class SearchMessagesResponse extends _BaseResponse {
|
|||||||
@JsonKey(defaultValue: [])
|
@JsonKey(defaultValue: [])
|
||||||
late List<GetMessageResponse> results;
|
late List<GetMessageResponse> results;
|
||||||
|
|
||||||
|
/// Message id of where to start searching from for next [results]
|
||||||
|
late String? next;
|
||||||
|
|
||||||
|
/// Message id of where to start searching from for previous [results]
|
||||||
|
late String? previous;
|
||||||
|
|
||||||
/// Create a new instance from a json
|
/// Create a new instance from a json
|
||||||
static SearchMessagesResponse fromJson(Map<String, dynamic> json) =>
|
static SearchMessagesResponse fromJson(Map<String, dynamic> json) =>
|
||||||
_$SearchMessagesResponseFromJson(json);
|
_$SearchMessagesResponseFromJson(json);
|
||||||
|
|||||||
@@ -161,7 +161,9 @@ SearchMessagesResponse _$SearchMessagesResponseFromJson(
|
|||||||
..results = (json['results'] as List<dynamic>?)
|
..results = (json['results'] as List<dynamic>?)
|
||||||
?.map((e) => GetMessageResponse.fromJson(e as Map<String, dynamic>))
|
?.map((e) => GetMessageResponse.fromJson(e as Map<String, dynamic>))
|
||||||
.toList() ??
|
.toList() ??
|
||||||
[];
|
[]
|
||||||
|
..next = json['next'] as String?
|
||||||
|
..previous = json['previous'] as String?;
|
||||||
}
|
}
|
||||||
|
|
||||||
GetMessagesByIdResponse _$GetMessagesByIdResponseFromJson(
|
GetMessagesByIdResponse _$GetMessagesByIdResponseFromJson(
|
||||||
|
|||||||
@@ -86,6 +86,24 @@ void main() {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
test(
|
||||||
|
'should throw if `pagination.offset` and `sort` both are provided',
|
||||||
|
() async {
|
||||||
|
final filter = Filter.in_('cid', const ['test-cid-1', 'test-cid-2']);
|
||||||
|
const sort = [SortOption<ChannelModel>('test-field')];
|
||||||
|
const pagination = PaginationParams(offset: 10);
|
||||||
|
try {
|
||||||
|
await generalApi.searchMessages(
|
||||||
|
filter,
|
||||||
|
sort: sort,
|
||||||
|
pagination: pagination,
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
expect(e, isA<AssertionError>());
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
test('should run successfully with `query`', () async {
|
test('should run successfully with `query`', () async {
|
||||||
final filter = Filter.in_('cid', const ['test-cid-1', 'test-cid-2']);
|
final filter = Filter.in_('cid', const ['test-cid-1', 'test-cid-2']);
|
||||||
const query = 'test-query';
|
const query = 'test-query';
|
||||||
|
|||||||
@@ -9,11 +9,23 @@ void main() {
|
|||||||
expect(j, {'field': 'name', 'direction': -1});
|
expect(j, {'field': 'name', 'direction': -1});
|
||||||
});
|
});
|
||||||
|
|
||||||
test('PaginationParams', () {
|
group('PaginationParams', () {
|
||||||
const option = PaginationParams();
|
test('default', () {
|
||||||
final j = option.toJson();
|
const option = PaginationParams();
|
||||||
expect(j, containsPair('limit', 10));
|
final j = option.toJson();
|
||||||
expect(j, containsPair('offset', 0));
|
expect(j, containsPair('limit', 10));
|
||||||
|
});
|
||||||
|
|
||||||
|
test(
|
||||||
|
'should throw if non-zero `offset` and `next` both are provided',
|
||||||
|
() {
|
||||||
|
try {
|
||||||
|
PaginationParams(offset: 10, next: 'next-message-id');
|
||||||
|
} catch (e) {
|
||||||
|
expect(e, isA<AssertionError>());
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,22 @@
|
|||||||
## 2.2.1
|
## 2.2.1
|
||||||
|
|
||||||
|
🛑️ Breaking Changes from `2.2.1`
|
||||||
|
|
||||||
|
- `MessageSearchListView` paginationParams property is now non-nullable with a default value.
|
||||||
|
```dart
|
||||||
|
paginationParams = const PaginationParams(limit: 30)
|
||||||
|
```
|
||||||
|
- `UserListView` pagination property is now non-nullable with a default value.
|
||||||
|
```dart
|
||||||
|
pagination = const PaginationParams(limit: 30)
|
||||||
|
```
|
||||||
|
|
||||||
|
🐞 Fixed
|
||||||
|
|
||||||
|
- Fixed `MessageSearchListView` pagination.
|
||||||
|
|
||||||
|
## 2.2.1
|
||||||
|
|
||||||
- Updated `stream_chat_flutter_core` dependency to 2.2.1
|
- Updated `stream_chat_flutter_core` dependency to 2.2.1
|
||||||
|
|
||||||
## 2.2.0
|
## 2.2.0
|
||||||
@@ -7,14 +24,13 @@
|
|||||||
✅ Added
|
✅ Added
|
||||||
|
|
||||||
- [#516](https://github.com/GetStream/stream-chat-flutter/issues/516):
|
- [#516](https://github.com/GetStream/stream-chat-flutter/issues/516):
|
||||||
Added `StreamChatThemeData.placeholderUserImage` for building a widget when the `UserAvatar` image
|
Added `StreamChatThemeData.placeholderUserImage` for building a widget when the `UserAvatar` image is loading
|
||||||
is loading
|
|
||||||
- Added a `backgroundColor` property to the following widgets:
|
- Added a `backgroundColor` property to the following widgets:
|
||||||
- `ChannelHeader`
|
- `ChannelHeader`
|
||||||
- `ChannelListHeader`
|
- `ChannelListHeader`
|
||||||
- `GalleryHeader`
|
- `GalleryHeader`
|
||||||
- `GalleryFooter`
|
- `GalleryFooter`
|
||||||
- `ThreadHeader`
|
- `ThreadHeader`
|
||||||
- Added `MessageInput.attachmentLimit` in order to limit the no. of attachments that can be sent with a single message.
|
- Added `MessageInput.attachmentLimit` in order to limit the no. of attachments that can be sent with a single message.
|
||||||
- Added `MessageInput.onAttachmentLimitExceed` callback which will be called when the `attachmentLimit` is exceeded.
|
- Added `MessageInput.onAttachmentLimitExceed` callback which will be called when the `attachmentLimit` is exceeded.
|
||||||
This will override the default error alert behaviour.
|
This will override the default error alert behaviour.
|
||||||
@@ -34,9 +50,8 @@ You can call `.copyWith` to customize just a subset of properties.
|
|||||||
|
|
||||||
🔄 Changed
|
🔄 Changed
|
||||||
|
|
||||||
Theming has been upgraded! Most theme classes now have `InheritedTheme` classes associated with
|
Theming has been upgraded! Most theme classes now have `InheritedTheme` classes associated with them, and have been
|
||||||
them, and have been upgraded with some goodies like `lerp` functions. Here's the full naming
|
upgraded with some goodies like `lerp` functions. Here's the full naming breakdown:
|
||||||
breakdown:
|
|
||||||
|
|
||||||
* `AvatarTheme` is now `AvatarThemeData`
|
* `AvatarTheme` is now `AvatarThemeData`
|
||||||
* `ChannelHeaderTheme` is now `ChannelHeaderThemeData`
|
* `ChannelHeaderTheme` is now `ChannelHeaderThemeData`
|
||||||
@@ -53,18 +68,18 @@ breakdown:
|
|||||||
|
|
||||||
🐞 Fixed
|
🐞 Fixed
|
||||||
|
|
||||||
- Fixed `MessageInput` textField case where `input` is not enabled if the file picked from the
|
- Fixed `MessageInput` textField case where `input` is not enabled if the file picked from the camera is null.
|
||||||
camera is null.
|
|
||||||
- Fixed date dividers position/alignment in non reversed `MessageListView`.
|
- Fixed date dividers position/alignment in non reversed `MessageListView`.
|
||||||
- Fixed `MessageListView` not opening to the right initialMessage if `StreamChannel.initialMessageId` is set.
|
- Fixed `MessageListView` not opening to the right initialMessage if `StreamChannel.initialMessageId` is set.
|
||||||
- Fixed null check errors when accessing `message.text` in `MessageWidget` and `MessageListView`; this occurred when sending a message with no text.
|
- Fixed null check errors when accessing `message.text` in `MessageWidget` and `MessageListView`; this occurred when
|
||||||
|
sending a message with no text.
|
||||||
|
|
||||||
## 2.1.2
|
## 2.1.2
|
||||||
|
|
||||||
🐞 Fixed
|
🐞 Fixed
|
||||||
|
|
||||||
- [#590](https://github.com/GetStream/stream-chat-flutter/issues/590): livestream use case, no
|
- [#590](https://github.com/GetStream/stream-chat-flutter/issues/590): livestream use case, no members when sending
|
||||||
members when sending message
|
message
|
||||||
|
|
||||||
## 2.1.1
|
## 2.1.1
|
||||||
|
|
||||||
@@ -82,8 +97,7 @@ breakdown:
|
|||||||
🔄 Changed
|
🔄 Changed
|
||||||
|
|
||||||
- `StreamChat.of(context).user` is now deprecated in favor of `StreamChat.of(context).currentUser`.
|
- `StreamChat.of(context).user` is now deprecated in favor of `StreamChat.of(context).currentUser`.
|
||||||
- `StreamChat.of(context).userStream` is now deprecated in favor
|
- `StreamChat.of(context).userStream` is now deprecated in favor of `StreamChat.of(context).currentUserStream`.
|
||||||
of `StreamChat.of(context).currentUserStream`.
|
|
||||||
|
|
||||||
🐞 Fixed
|
🐞 Fixed
|
||||||
|
|
||||||
@@ -136,8 +150,7 @@ You can call `.copyWith` to customize just a subset of properties
|
|||||||
|
|
||||||
- Added video compress options (frame and quality) to `MessageInput`
|
- Added video compress options (frame and quality) to `MessageInput`
|
||||||
- TypingIndicator now has a property called `parentId` to show typing indicator specific to threads
|
- TypingIndicator now has a property called `parentId` to show typing indicator specific to threads
|
||||||
- [#493](https://github.com/GetStream/stream-chat-flutter/pull/493): add support for messageListView
|
- [#493](https://github.com/GetStream/stream-chat-flutter/pull/493): add support for messageListView header/footer
|
||||||
header/footer
|
|
||||||
- `MessageWidget` accepts a `userAvatarBuilder`
|
- `MessageWidget` accepts a `userAvatarBuilder`
|
||||||
- Added pinMessage ui support
|
- Added pinMessage ui support
|
||||||
- Added `MessageListView.threadSeparatorBuilder` property
|
- Added `MessageListView.threadSeparatorBuilder` property
|
||||||
@@ -146,12 +159,10 @@ You can call `.copyWith` to customize just a subset of properties
|
|||||||
|
|
||||||
🐞 Fixed
|
🐞 Fixed
|
||||||
|
|
||||||
- [#483](https://github.com/GetStream/stream-chat-flutter/issues/483): Keyboard covers input text
|
- [#483](https://github.com/GetStream/stream-chat-flutter/issues/483): Keyboard covers input text box when editing
|
||||||
box when editing message
|
message
|
||||||
- Modals are shown using the nearest `Navigator` to make using the SDK easier in a nested navigator
|
- Modals are shown using the nearest `Navigator` to make using the SDK easier in a nested navigator use case
|
||||||
use case
|
- [#484](https://github.com/GetStream/stream-chat-flutter/issues/484): messages don't update without a reload
|
||||||
- [#484](https://github.com/GetStream/stream-chat-flutter/issues/484): messages don't update without
|
|
||||||
a reload
|
|
||||||
- `MessageListView` not rendering if the user is not a member of the channel
|
- `MessageListView` not rendering if the user is not a member of the channel
|
||||||
- Fix `MessageInput` overflow when there are no actions
|
- Fix `MessageInput` overflow when there are no actions
|
||||||
- Minor fixes and improvements
|
- Minor fixes and improvements
|
||||||
@@ -204,18 +215,15 @@ You can call `.copyWith` to customize just a subset of properties.
|
|||||||
✅ Added
|
✅ Added
|
||||||
|
|
||||||
- TypingIndicator now has a property called `parentId` to show typing indicator specific to threads
|
- TypingIndicator now has a property called `parentId` to show typing indicator specific to threads
|
||||||
- [#493](https://github.com/GetStream/stream-chat-flutter/pull/493): add support for messageListView
|
- [#493](https://github.com/GetStream/stream-chat-flutter/pull/493): add support for messageListView header/footer
|
||||||
header/footer
|
|
||||||
- `MessageWidget` accepts a `userAvatarBuilder`
|
- `MessageWidget` accepts a `userAvatarBuilder`
|
||||||
|
|
||||||
🐞 Fixed
|
🐞 Fixed
|
||||||
|
|
||||||
- [#483](https://github.com/GetStream/stream-chat-flutter/issues/483): Keyboard covers input text
|
- [#483](https://github.com/GetStream/stream-chat-flutter/issues/483): Keyboard covers input text box when editing
|
||||||
box when editing message
|
message
|
||||||
- Modals are shown using the nearest `Navigator` to make using the SDK easier in a nested navigator
|
- Modals are shown using the nearest `Navigator` to make using the SDK easier in a nested navigator use case
|
||||||
use case
|
- [#484](https://github.com/GetStream/stream-chat-flutter/issues/484): messages don't update without a reload
|
||||||
- [#484](https://github.com/GetStream/stream-chat-flutter/issues/484): messages don't update without
|
|
||||||
a reload
|
|
||||||
- `MessageListView` not rendering if the user is not a member of the channel
|
- `MessageListView` not rendering if the user is not a member of the channel
|
||||||
|
|
||||||
## 2.0.0-nullsafety.7
|
## 2.0.0-nullsafety.7
|
||||||
@@ -285,8 +293,7 @@ You can call `.copyWith` to customize just a subset of properties.
|
|||||||
- Show error messages as system and keep them in the message input
|
- Show error messages as system and keep them in the message input
|
||||||
- Remove notification badge logic
|
- Remove notification badge logic
|
||||||
- Use shimmer while loading images
|
- Use shimmer while loading images
|
||||||
- Polished `StreamChatTheme` adding more options and a new `MessageInputTheme` dedicated
|
- Polished `StreamChatTheme` adding more options and a new `MessageInputTheme` dedicated to `MessageInput`
|
||||||
to `MessageInput`
|
|
||||||
- Add possibility to specify custom message actions using `MessageWidget.customActions`
|
- Add possibility to specify custom message actions using `MessageWidget.customActions`
|
||||||
- Added `MessageListView.onAttachmentTap` callback
|
- Added `MessageListView.onAttachmentTap` callback
|
||||||
- Fixed message newline issue
|
- Fixed message newline issue
|
||||||
@@ -343,8 +350,7 @@ You can call `.copyWith` to customize just a subset of properties.
|
|||||||
- Improved api documentation
|
- Improved api documentation
|
||||||
- Updated `stream_chat` dependency to `^1.0.0-beta`
|
- Updated `stream_chat` dependency to `^1.0.0-beta`
|
||||||
- Extracted sample app into dedicated [repo](https://github.com/GetStream/flutter-samples)
|
- Extracted sample app into dedicated [repo](https://github.com/GetStream/flutter-samples)
|
||||||
- Reimplemented existing widgets
|
- Reimplemented existing widgets using [stream_chat_flutter_core](https://pub.dev/packages/stream_chat_flutter_core)
|
||||||
using [stream_chat_flutter_core](https://pub.dev/packages/stream_chat_flutter_core)
|
|
||||||
|
|
||||||
## 0.2.21
|
## 0.2.21
|
||||||
|
|
||||||
@@ -361,8 +367,8 @@ You can call `.copyWith` to customize just a subset of properties.
|
|||||||
|
|
||||||
## 0.2.20+2
|
## 0.2.20+2
|
||||||
|
|
||||||
- Added `shouldAddChannel` to ChannelsBloc in order to check if a channel has to be added to the
|
- Added `shouldAddChannel` to ChannelsBloc in order to check if a channel has to be added to the list when a new message
|
||||||
list when a new message arrives
|
arrives
|
||||||
|
|
||||||
## 0.2.20+1
|
## 0.2.20+1
|
||||||
|
|
||||||
@@ -396,8 +402,7 @@ You can call `.copyWith` to customize just a subset of properties.
|
|||||||
|
|
||||||
## 0.2.16
|
## 0.2.16
|
||||||
|
|
||||||
- Do not wrap channel preview builder. Users will have to implement they're custom onTap/onLongPress
|
- Do not wrap channel preview builder. Users will have to implement they're custom onTap/onLongPress implementation
|
||||||
implementation
|
|
||||||
- Make public autofocus field of the TextField of message_input
|
- Make public autofocus field of the TextField of message_input
|
||||||
|
|
||||||
## 0.2.15
|
## 0.2.15
|
||||||
@@ -582,11 +587,10 @@ You can call `.copyWith` to customize just a subset of properties.
|
|||||||
|
|
||||||
## 0.2.1-alpha+1
|
## 0.2.1-alpha+1
|
||||||
|
|
||||||
- Removed the additional `Navigator` in `StreamChat` widget. It was added to make the app have
|
- Removed the additional `Navigator` in `StreamChat` widget. It was added to make the app have the `StreamChat` widget
|
||||||
the `StreamChat` widget as ancestor in every route. Now the recommended way to add `StreamChat` to
|
as ancestor in every route. Now the recommended way to add `StreamChat` to your app is using the `builder` property of
|
||||||
your app is using the `builder` property of your `MaterialApp` widget. Otherwise you can use it in
|
your `MaterialApp` widget. Otherwise you can use it in the usual way, but you need to add a `StreamChat` widget to
|
||||||
the usual way, but you need to add a `StreamChat` widget to every route of your app.
|
every route of your app. Read [this issue](https://github.com/GetStream/stream-chat-flutter/issues/47) for more
|
||||||
Read [this issue](https://github.com/GetStream/stream-chat-flutter/issues/47) for more
|
|
||||||
information.
|
information.
|
||||||
|
|
||||||
```dart
|
```dart
|
||||||
@@ -688,8 +692,8 @@ Widget build(BuildContext context) {
|
|||||||
|
|
||||||
- Add gesture (vertical drag down) to close the keyboard
|
- Add gesture (vertical drag down) to close the keyboard
|
||||||
|
|
||||||
- Add keyboard type parameters (set it to TextInputType.text to show the submit button that will
|
- Add keyboard type parameters (set it to TextInputType.text to show the submit button that will even close the
|
||||||
even close the keyboard)
|
keyboard)
|
||||||
|
|
||||||
The property showVideoFullScreen was added mainly because of this issue brianegan/chewie#261
|
The property showVideoFullScreen was added mainly because of this issue brianegan/chewie#261
|
||||||
|
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ class MessageSearchListView extends StatefulWidget {
|
|||||||
required this.filters,
|
required this.filters,
|
||||||
this.messageQuery,
|
this.messageQuery,
|
||||||
this.sortOptions,
|
this.sortOptions,
|
||||||
this.paginationParams,
|
this.paginationParams = const PaginationParams(limit: 30),
|
||||||
this.messageFilters,
|
this.messageFilters,
|
||||||
this.separatorBuilder,
|
this.separatorBuilder,
|
||||||
this.itemBuilder,
|
this.itemBuilder,
|
||||||
@@ -93,7 +93,7 @@ class MessageSearchListView extends StatefulWidget {
|
|||||||
/// limit: the number of users to return (max is 30)
|
/// limit: the number of users to return (max is 30)
|
||||||
/// offset: the offset (max is 1000)
|
/// offset: the offset (max is 1000)
|
||||||
/// message_limit: how many messages should be included to each channel
|
/// message_limit: how many messages should be included to each channel
|
||||||
final PaginationParams? paginationParams;
|
final PaginationParams paginationParams;
|
||||||
|
|
||||||
/// The message query filters to use.
|
/// The message query filters to use.
|
||||||
/// You can query on any of the custom fields you've defined on the [Channel].
|
/// You can query on any of the custom fields you've defined on the [Channel].
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ class UserListView extends StatefulWidget {
|
|||||||
this.filter,
|
this.filter,
|
||||||
this.sort,
|
this.sort,
|
||||||
this.presence,
|
this.presence,
|
||||||
this.pagination,
|
this.pagination = const PaginationParams(limit: 30),
|
||||||
this.onUserTap,
|
this.onUserTap,
|
||||||
this.onUserLongPress,
|
this.onUserLongPress,
|
||||||
this.userWidget,
|
this.userWidget,
|
||||||
@@ -93,7 +93,7 @@ class UserListView extends StatefulWidget {
|
|||||||
/// limit: the number of users to return (max is 30)
|
/// limit: the number of users to return (max is 30)
|
||||||
/// offset: the offset (max is 1000)
|
/// offset: the offset (max is 1000)
|
||||||
/// message_limit: how many messages should be included to each channel
|
/// message_limit: how many messages should be included to each channel
|
||||||
final PaginationParams? pagination;
|
final PaginationParams pagination;
|
||||||
|
|
||||||
/// Function called when tapping on a channel
|
/// Function called when tapping on a channel
|
||||||
/// By default it calls [Navigator.push] building a [MaterialPageRoute]
|
/// By default it calls [Navigator.push] building a [MaterialPageRoute]
|
||||||
|
|||||||
@@ -69,6 +69,7 @@ void main() {
|
|||||||
body: MessageSearchBloc(
|
body: MessageSearchBloc(
|
||||||
child: MessageSearchListView(
|
child: MessageSearchListView(
|
||||||
filters: Filter.in_('members', const ['test_id']),
|
filters: Filter.in_('members', const ['test_id']),
|
||||||
|
messageQuery: 'test query',
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -100,6 +101,7 @@ void main() {
|
|||||||
body: MessageSearchBloc(
|
body: MessageSearchBloc(
|
||||||
child: MessageSearchListView(
|
child: MessageSearchListView(
|
||||||
filters: Filter.in_('members', const ['test_id']),
|
filters: Filter.in_('members', const ['test_id']),
|
||||||
|
messageQuery: 'test query',
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,3 +1,20 @@
|
|||||||
|
## Upcoming
|
||||||
|
|
||||||
|
🛑️ Breaking Changes from `2.2.1`
|
||||||
|
|
||||||
|
- `MessageSearchListViewCore` paginationParams property is now non-nullable with a default value.
|
||||||
|
```dart
|
||||||
|
paginationParams = const PaginationParams(limit: 30)
|
||||||
|
```
|
||||||
|
- `UserListViewCore` pagination property is now non-nullable with a default value.
|
||||||
|
```dart
|
||||||
|
pagination = const PaginationParams(limit: 30)
|
||||||
|
```
|
||||||
|
|
||||||
|
🐞 Fixed
|
||||||
|
|
||||||
|
- Fixed `MessageSearchBloc` pagination.
|
||||||
|
|
||||||
## 2.2.1
|
## 2.2.1
|
||||||
|
|
||||||
- Updated `stream_chat` dependency to 2.2.1
|
- Updated `stream_chat` dependency to 2.2.1
|
||||||
@@ -5,13 +22,17 @@
|
|||||||
## 2.2.0
|
## 2.2.0
|
||||||
|
|
||||||
🛑️ Breaking Changes from `2.1.1`
|
🛑️ Breaking Changes from `2.1.1`
|
||||||
|
|
||||||
- Renamed `BetterStreamBuilder.loadingBuilder` to `.noDataBuilder`
|
- Renamed `BetterStreamBuilder.loadingBuilder` to `.noDataBuilder`
|
||||||
|
|
||||||
🔄 Changed
|
🔄 Changed
|
||||||
|
|
||||||
- `BetterStreamBuilder.initialData` is now nullable/not-required.
|
- `BetterStreamBuilder.initialData` is now nullable/not-required.
|
||||||
|
|
||||||
🐞 Fixed
|
🐞 Fixed
|
||||||
- [#612](https://github.com/GetStream/stream-chat-flutter/issues/612) `ChannelListView` pagination doesn't work after refresh
|
|
||||||
|
- [#612](https://github.com/GetStream/stream-chat-flutter/issues/612) `ChannelListView` pagination doesn't work after
|
||||||
|
refresh
|
||||||
|
|
||||||
## 2.1.1
|
## 2.1.1
|
||||||
|
|
||||||
@@ -20,12 +41,15 @@
|
|||||||
## 2.1.0
|
## 2.1.0
|
||||||
|
|
||||||
🛑️ Breaking Changes from `2.0.0`
|
🛑️ Breaking Changes from `2.0.0`
|
||||||
|
|
||||||
- Changed default message filter of `MessageListCore`
|
- Changed default message filter of `MessageListCore`
|
||||||
|
|
||||||
✅ Added
|
✅ Added
|
||||||
|
|
||||||
- Added `MessageListCore.paginationLimit`
|
- Added `MessageListCore.paginationLimit`
|
||||||
|
|
||||||
🔄 Changed
|
🔄 Changed
|
||||||
|
|
||||||
- `StreamChatCore.of(context).user` is now deprecated in favor of `StreamChatCore.of(context).currentUser`.
|
- `StreamChatCore.of(context).user` is now deprecated in favor of `StreamChatCore.of(context).currentUser`.
|
||||||
- `StreamChatCore.of(context).userStream` is now deprecated in favor of `StreamChatCore.of(context).currentUserStream`.
|
- `StreamChatCore.of(context).userStream` is now deprecated in favor of `StreamChatCore.of(context).currentUserStream`.
|
||||||
|
|
||||||
@@ -34,7 +58,8 @@
|
|||||||
🛑️ Breaking Changes from `1.5.3`
|
🛑️ Breaking Changes from `1.5.3`
|
||||||
|
|
||||||
- migrate this package to null safety
|
- migrate this package to null safety
|
||||||
- `channelsBloc.queryChannels()`, `ChannelListCore` options param/property is removed in favor of individual params/properties
|
- `channelsBloc.queryChannels()`, `ChannelListCore` options param/property is removed in favor of individual
|
||||||
|
params/properties
|
||||||
- `options.state` -> bool state
|
- `options.state` -> bool state
|
||||||
- `options.watch` -> bool watch
|
- `options.watch` -> bool watch
|
||||||
- `options.presence` -> bool presence
|
- `options.presence` -> bool presence
|
||||||
@@ -51,6 +76,7 @@
|
|||||||
- Performance improvements
|
- Performance improvements
|
||||||
|
|
||||||
## 2.0.0-nullsafety.9
|
## 2.0.0-nullsafety.9
|
||||||
|
|
||||||
- Update llc dependency
|
- Update llc dependency
|
||||||
|
|
||||||
## 2.0.0-nullsafety.8
|
## 2.0.0-nullsafety.8
|
||||||
|
|||||||
@@ -105,7 +105,8 @@ class ChannelsBlocState extends State<ChannelsBloc>
|
|||||||
}) async {
|
}) async {
|
||||||
final client = _streamChatCoreState!.client;
|
final client = _streamChatCoreState!.client;
|
||||||
|
|
||||||
final clear = paginationParams.offset == 0;
|
final offset = paginationParams.offset;
|
||||||
|
final clear = offset == null || offset == 0;
|
||||||
if (clear && _paginationEnded) {
|
if (clear && _paginationEnded) {
|
||||||
_paginationEnded = false;
|
_paginationEnded = false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,6 +43,12 @@ class MessageSearchBlocState extends State<MessageSearchBloc>
|
|||||||
with AutomaticKeepAliveClientMixin {
|
with AutomaticKeepAliveClientMixin {
|
||||||
late StreamChatCoreState _streamChatCoreState;
|
late StreamChatCoreState _streamChatCoreState;
|
||||||
|
|
||||||
|
/// The key used to paginate next items.
|
||||||
|
String? nextId;
|
||||||
|
|
||||||
|
/// The key used to paginate previous items.
|
||||||
|
String? previousId;
|
||||||
|
|
||||||
/// The current messages list
|
/// The current messages list
|
||||||
List<GetMessageResponse>? get messageResponses =>
|
List<GetMessageResponse>? get messageResponses =>
|
||||||
_messageResponses.valueOrNull;
|
_messageResponses.valueOrNull;
|
||||||
@@ -59,6 +65,8 @@ class MessageSearchBlocState extends State<MessageSearchBloc>
|
|||||||
Stream<bool> get queryMessagesLoading =>
|
Stream<bool> get queryMessagesLoading =>
|
||||||
_queryMessagesLoadingController.stream;
|
_queryMessagesLoadingController.stream;
|
||||||
|
|
||||||
|
bool _paginationEnded = false;
|
||||||
|
|
||||||
/// Calls [StreamChatClient.search] updating
|
/// Calls [StreamChatClient.search] updating
|
||||||
/// [messagesStream] and [queryMessagesLoading] stream
|
/// [messagesStream] and [queryMessagesLoading] stream
|
||||||
Future<void> search({
|
Future<void> search({
|
||||||
@@ -66,21 +74,34 @@ class MessageSearchBlocState extends State<MessageSearchBloc>
|
|||||||
Filter? messageFilter,
|
Filter? messageFilter,
|
||||||
List<SortOption>? sort,
|
List<SortOption>? sort,
|
||||||
String? query,
|
String? query,
|
||||||
PaginationParams? pagination,
|
PaginationParams pagination = const PaginationParams(limit: 30),
|
||||||
}) async {
|
}) async {
|
||||||
final client = _streamChatCoreState.client;
|
final client = _streamChatCoreState.client;
|
||||||
|
|
||||||
if (_queryMessagesLoadingController.value == true) return;
|
var clear = false;
|
||||||
|
if (sort != null) {
|
||||||
|
clear |= pagination.next == null;
|
||||||
|
} else {
|
||||||
|
final offset = pagination.offset;
|
||||||
|
clear |= offset == null || offset == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (clear && _paginationEnded) {
|
||||||
|
_paginationEnded = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((!clear && _paginationEnded) ||
|
||||||
|
_queryMessagesLoadingController.value == true) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (_messageResponses.hasValue) {
|
if (_messageResponses.hasValue) {
|
||||||
_queryMessagesLoadingController.add(true);
|
_queryMessagesLoadingController.add(true);
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
final clear = pagination == null || pagination.offset == 0;
|
|
||||||
|
|
||||||
final oldMessages = List<GetMessageResponse>.from(messageResponses ?? []);
|
final oldMessages = List<GetMessageResponse>.from(messageResponses ?? []);
|
||||||
|
|
||||||
final messages = await client.search(
|
final response = await client.search(
|
||||||
filter,
|
filter,
|
||||||
sort: sort,
|
sort: sort,
|
||||||
query: query,
|
query: query,
|
||||||
@@ -88,15 +109,29 @@ class MessageSearchBlocState extends State<MessageSearchBloc>
|
|||||||
messageFilters: messageFilter,
|
messageFilters: messageFilter,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
final next = response.next;
|
||||||
|
final previous = response.previous;
|
||||||
|
|
||||||
|
nextId = next != null && next.isNotEmpty
|
||||||
|
? next
|
||||||
|
: /*reset nextId if we get nothing*/ null;
|
||||||
|
previousId = previous != null && previous.isNotEmpty
|
||||||
|
? previous
|
||||||
|
: /*reset previousId if we get nothing*/ null;
|
||||||
|
|
||||||
|
final newMessages = response.results;
|
||||||
if (clear) {
|
if (clear) {
|
||||||
_messageResponses.add(messages.results);
|
_messageResponses.add(newMessages);
|
||||||
} else {
|
} else {
|
||||||
final temp = oldMessages + messages.results;
|
final temp = oldMessages + newMessages;
|
||||||
_messageResponses.add(temp);
|
_messageResponses.add(temp);
|
||||||
}
|
}
|
||||||
if (_messageResponses.hasValue && _queryMessagesLoadingController.value) {
|
if (_messageResponses.hasValue && _queryMessagesLoadingController.value) {
|
||||||
_queryMessagesLoadingController.add(false);
|
_queryMessagesLoadingController.add(false);
|
||||||
}
|
}
|
||||||
|
if (newMessages.isEmpty || newMessages.length < pagination.limit) {
|
||||||
|
_paginationEnded = true;
|
||||||
|
}
|
||||||
} catch (e, stk) {
|
} catch (e, stk) {
|
||||||
// reset loading controller
|
// reset loading controller
|
||||||
_queryMessagesLoadingController.add(false);
|
_queryMessagesLoadingController.add(false);
|
||||||
|
|||||||
@@ -47,10 +47,18 @@ class MessageSearchListCore extends StatefulWidget {
|
|||||||
required this.filters,
|
required this.filters,
|
||||||
this.messageQuery,
|
this.messageQuery,
|
||||||
this.sortOptions,
|
this.sortOptions,
|
||||||
this.paginationParams,
|
this.paginationParams = const PaginationParams(limit: 30),
|
||||||
this.messageFilters,
|
this.messageFilters,
|
||||||
this.messageSearchListController,
|
this.messageSearchListController,
|
||||||
}) : super(key: key);
|
}) : assert(
|
||||||
|
messageQuery != null || messageFilters != null,
|
||||||
|
'Provide at least `query` or `messageFilters`',
|
||||||
|
),
|
||||||
|
assert(
|
||||||
|
messageQuery == null || messageFilters == null,
|
||||||
|
"Can't provide both `query` and `messageFilters` at the same time",
|
||||||
|
),
|
||||||
|
super(key: key);
|
||||||
|
|
||||||
/// A [MessageSearchListController] allows reloading and pagination.
|
/// A [MessageSearchListController] allows reloading and pagination.
|
||||||
/// Use [MessageSearchListController.loadData] and
|
/// Use [MessageSearchListController.loadData] and
|
||||||
@@ -74,10 +82,9 @@ class MessageSearchListCore extends StatefulWidget {
|
|||||||
final List<SortOption>? sortOptions;
|
final List<SortOption>? sortOptions;
|
||||||
|
|
||||||
/// Pagination parameters
|
/// Pagination parameters
|
||||||
/// limit: the number of users to return (max is 30)
|
/// limit: the number of messages to return (max is 30)
|
||||||
/// offset: the offset (max is 1000)
|
/// offset: the offset (max is 1000)
|
||||||
/// message_limit: how many messages should be included to each channel
|
final PaginationParams paginationParams;
|
||||||
final PaginationParams? paginationParams;
|
|
||||||
|
|
||||||
/// The message query filters to use.
|
/// The message query filters to use.
|
||||||
/// You can query on any of the custom fields you've defined on the [Channel].
|
/// You can query on any of the custom fields you've defined on the [Channel].
|
||||||
@@ -155,15 +162,25 @@ class MessageSearchListCoreState extends State<MessageSearchListCore> {
|
|||||||
);
|
);
|
||||||
|
|
||||||
/// Fetches more messages with updated pagination and updates the widget
|
/// Fetches more messages with updated pagination and updates the widget
|
||||||
Future<void> paginateData() => _messageSearchBloc!.search(
|
Future<void> paginateData() {
|
||||||
filter: widget.filters,
|
PaginationParams pagination;
|
||||||
sort: widget.sortOptions,
|
if (widget.sortOptions != null) {
|
||||||
pagination: widget.paginationParams!.copyWith(
|
pagination = widget.paginationParams.copyWith(
|
||||||
offset: _messageSearchBloc!.messageResponses?.length ?? 0,
|
next: _messageSearchBloc?.nextId,
|
||||||
),
|
|
||||||
query: widget.messageQuery,
|
|
||||||
messageFilter: widget.messageFilters,
|
|
||||||
);
|
);
|
||||||
|
} else {
|
||||||
|
pagination = widget.paginationParams.copyWith(
|
||||||
|
offset: _messageSearchBloc?.messageResponses?.length,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return _messageSearchBloc!.search(
|
||||||
|
filter: widget.filters,
|
||||||
|
sort: widget.sortOptions,
|
||||||
|
pagination: pagination,
|
||||||
|
query: widget.messageQuery,
|
||||||
|
messageFilter: widget.messageFilters,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void didUpdateWidget(MessageSearchListCore oldWidget) {
|
void didUpdateWidget(MessageSearchListCore oldWidget) {
|
||||||
@@ -173,8 +190,8 @@ class MessageSearchListCoreState extends State<MessageSearchListCore> {
|
|||||||
widget.messageQuery?.toString() != oldWidget.messageQuery?.toString() ||
|
widget.messageQuery?.toString() != oldWidget.messageQuery?.toString() ||
|
||||||
widget.messageFilters?.toString() !=
|
widget.messageFilters?.toString() !=
|
||||||
oldWidget.messageFilters?.toString() ||
|
oldWidget.messageFilters?.toString() ||
|
||||||
widget.paginationParams?.toJson().toString() !=
|
widget.paginationParams.toJson().toString() !=
|
||||||
oldWidget.paginationParams?.toJson().toString()) {
|
oldWidget.paginationParams.toJson().toString()) {
|
||||||
loadData();
|
loadData();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ class UserListCore extends StatefulWidget {
|
|||||||
this.filter,
|
this.filter,
|
||||||
this.sort,
|
this.sort,
|
||||||
this.presence,
|
this.presence,
|
||||||
this.pagination,
|
this.pagination = const PaginationParams(limit: 30),
|
||||||
this.groupAlphabetically = false,
|
this.groupAlphabetically = false,
|
||||||
this.userListController,
|
this.userListController,
|
||||||
}) : super(key: key);
|
}) : super(key: key);
|
||||||
@@ -106,7 +106,7 @@ class UserListCore extends StatefulWidget {
|
|||||||
/// limit: the number of users to return (max is 30)
|
/// limit: the number of users to return (max is 30)
|
||||||
/// offset: the offset (max is 1000)
|
/// offset: the offset (max is 1000)
|
||||||
/// message_limit: how many messages should be included to each channel
|
/// message_limit: how many messages should be included to each channel
|
||||||
final PaginationParams? pagination;
|
final PaginationParams pagination;
|
||||||
|
|
||||||
/// Set it to true to group users by their first character
|
/// Set it to true to group users by their first character
|
||||||
///
|
///
|
||||||
@@ -201,7 +201,7 @@ class UserListCoreState extends State<UserListCore>
|
|||||||
filter: widget.filter,
|
filter: widget.filter,
|
||||||
sort: widget.sort,
|
sort: widget.sort,
|
||||||
presence: widget.presence,
|
presence: widget.presence,
|
||||||
pagination: widget.pagination!.copyWith(
|
pagination: widget.pagination.copyWith(
|
||||||
offset: _usersBloc!.users?.length ?? 0,
|
offset: _usersBloc!.users?.length ?? 0,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -212,8 +212,8 @@ class UserListCoreState extends State<UserListCore>
|
|||||||
if (widget.filter?.toString() != oldWidget.filter?.toString() ||
|
if (widget.filter?.toString() != oldWidget.filter?.toString() ||
|
||||||
jsonEncode(widget.sort) != jsonEncode(oldWidget.sort) ||
|
jsonEncode(widget.sort) != jsonEncode(oldWidget.sort) ||
|
||||||
widget.presence != oldWidget.presence ||
|
widget.presence != oldWidget.presence ||
|
||||||
widget.pagination?.toJson().toString() !=
|
widget.pagination.toJson().toString() !=
|
||||||
oldWidget.pagination?.toJson().toString()) {
|
oldWidget.pagination.toJson().toString()) {
|
||||||
loadData();
|
loadData();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -57,6 +57,8 @@ class UsersBlocState extends State<UsersBloc>
|
|||||||
|
|
||||||
late StreamChatCoreState _streamChatCore;
|
late StreamChatCoreState _streamChatCore;
|
||||||
|
|
||||||
|
bool _paginationEnded = false;
|
||||||
|
|
||||||
/// The Query Users method allows you to search for users and see if they are
|
/// The Query Users method allows you to search for users and see if they are
|
||||||
/// online/offline.
|
/// online/offline.
|
||||||
/// [API Reference](https://getstream.io/chat/docs/flutter-dart/query_users/?language=dart)
|
/// [API Reference](https://getstream.io/chat/docs/flutter-dart/query_users/?language=dart)
|
||||||
@@ -64,19 +66,27 @@ class UsersBlocState extends State<UsersBloc>
|
|||||||
Filter? filter,
|
Filter? filter,
|
||||||
List<SortOption>? sort,
|
List<SortOption>? sort,
|
||||||
bool? presence,
|
bool? presence,
|
||||||
PaginationParams? pagination,
|
PaginationParams pagination = const PaginationParams(limit: 30),
|
||||||
}) async {
|
}) async {
|
||||||
final client = _streamChatCore.client;
|
final client = _streamChatCore.client;
|
||||||
|
|
||||||
if (_queryUsersLoadingController.value == true) return;
|
final offset = pagination.offset;
|
||||||
|
final clear = offset == null || offset == 0;
|
||||||
|
|
||||||
|
if (clear && _paginationEnded) {
|
||||||
|
_paginationEnded = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((!clear && _paginationEnded) ||
|
||||||
|
_queryUsersLoadingController.value == true) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (_usersController.hasValue) {
|
if (_usersController.hasValue) {
|
||||||
_queryUsersLoadingController.add(true);
|
_queryUsersLoadingController.add(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final clear = pagination == null || pagination.offset == 0;
|
|
||||||
|
|
||||||
final oldUsers = List<User>.from(users ?? []);
|
final oldUsers = List<User>.from(users ?? []);
|
||||||
|
|
||||||
final usersResponse = await client.queryUsers(
|
final usersResponse = await client.queryUsers(
|
||||||
@@ -86,6 +96,7 @@ class UsersBlocState extends State<UsersBloc>
|
|||||||
pagination: pagination,
|
pagination: pagination,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
final newUsers = usersResponse.users;
|
||||||
if (clear) {
|
if (clear) {
|
||||||
_usersController.add(usersResponse.users);
|
_usersController.add(usersResponse.users);
|
||||||
} else {
|
} else {
|
||||||
@@ -95,6 +106,9 @@ class UsersBlocState extends State<UsersBloc>
|
|||||||
if (_usersController.hasValue && _queryUsersLoadingController.value) {
|
if (_usersController.hasValue && _queryUsersLoadingController.value) {
|
||||||
_queryUsersLoadingController.add(false);
|
_queryUsersLoadingController.add(false);
|
||||||
}
|
}
|
||||||
|
if (newUsers.isEmpty || newUsers.length < pagination.limit) {
|
||||||
|
_paginationEnded = true;
|
||||||
|
}
|
||||||
} catch (e, stk) {
|
} catch (e, stk) {
|
||||||
// reset loading controller
|
// reset loading controller
|
||||||
_queryUsersLoadingController.add(false);
|
_queryUsersLoadingController.add(false);
|
||||||
|
|||||||
@@ -31,8 +31,7 @@ void main() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
testWidgets(
|
testWidgets(
|
||||||
'messageSearchBlocState.search() should throw if used where '
|
'''messageSearchBlocState.search() should throw if used where StreamChat is not present in the widget tree''',
|
||||||
'StreamChat is not present in the widget tree',
|
|
||||||
(tester) async {
|
(tester) async {
|
||||||
const messageSearchBloc = MessageSearchBloc(
|
const messageSearchBloc = MessageSearchBloc(
|
||||||
child: Offstage(),
|
child: Offstage(),
|
||||||
@@ -74,7 +73,10 @@ void main() {
|
|||||||
messageFilters: any(named: 'messageFilters'),
|
messageFilters: any(named: 'messageFilters'),
|
||||||
paginationParams: any(named: 'paginationParams'),
|
paginationParams: any(named: 'paginationParams'),
|
||||||
)).thenAnswer(
|
)).thenAnswer(
|
||||||
(_) async => SearchMessagesResponse()..results = messageResponseList,
|
(_) async => SearchMessagesResponse()
|
||||||
|
..results = messageResponseList
|
||||||
|
..next = null
|
||||||
|
..previous = null,
|
||||||
);
|
);
|
||||||
|
|
||||||
messageSearchBlocState.search(filter: testFilter);
|
messageSearchBlocState.search(filter: testFilter);
|
||||||
@@ -95,8 +97,7 @@ void main() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
testWidgets(
|
testWidgets(
|
||||||
'messageSearchBlocState.messagesStream should emit error '
|
'''messageSearchBlocState.messagesStream should emit error if client.search() throws''',
|
||||||
'if client.search() throws',
|
|
||||||
(tester) async {
|
(tester) async {
|
||||||
const messageSearchBlocKey = Key('messageSearchBloc');
|
const messageSearchBlocKey = Key('messageSearchBloc');
|
||||||
const childKey = Key('child');
|
const childKey = Key('child');
|
||||||
@@ -144,9 +145,7 @@ void main() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
testWidgets(
|
testWidgets(
|
||||||
'calling messageSearchBlocState.search() again with an offset '
|
'''calling messageSearchBlocState.search() again with an offset should emit new data through messagesStream and also emit loading state through queryMessagesLoading''',
|
||||||
'should emit new data through messagesStream and also emit loading state '
|
|
||||||
'through queryMessagesLoading',
|
|
||||||
(tester) async {
|
(tester) async {
|
||||||
const messageSearchBlocKey = Key('messageSearchBloc');
|
const messageSearchBlocKey = Key('messageSearchBloc');
|
||||||
const childKey = Key('child');
|
const childKey = Key('child');
|
||||||
@@ -168,19 +167,23 @@ void main() {
|
|||||||
find.byKey(messageSearchBlocKey),
|
find.byKey(messageSearchBlocKey),
|
||||||
);
|
);
|
||||||
|
|
||||||
final messageResponseList = _generateMessages();
|
const pagination = PaginationParams(limit: 25);
|
||||||
|
final messageResponseList = _generateMessages(count: 25);
|
||||||
|
|
||||||
when(() => mockClient.search(
|
when(() => mockClient.search(
|
||||||
testFilter,
|
testFilter,
|
||||||
query: any(named: 'query'),
|
query: any(named: 'query'),
|
||||||
sort: any(named: 'sort'),
|
sort: any(named: 'sort'),
|
||||||
messageFilters: any(named: 'messageFilters'),
|
messageFilters: any(named: 'messageFilters'),
|
||||||
paginationParams: any(named: 'paginationParams'),
|
paginationParams: pagination,
|
||||||
)).thenAnswer(
|
)).thenAnswer(
|
||||||
(_) async => SearchMessagesResponse()..results = messageResponseList,
|
(_) async => SearchMessagesResponse()
|
||||||
|
..results = messageResponseList
|
||||||
|
..next = null
|
||||||
|
..previous = null,
|
||||||
);
|
);
|
||||||
|
|
||||||
messageSearchBlocState.search(filter: testFilter);
|
messageSearchBlocState.search(pagination: pagination, filter: testFilter);
|
||||||
|
|
||||||
await expectLater(
|
await expectLater(
|
||||||
messageSearchBlocState.messagesStream,
|
messageSearchBlocState.messagesStream,
|
||||||
@@ -192,22 +195,24 @@ void main() {
|
|||||||
query: any(named: 'query'),
|
query: any(named: 'query'),
|
||||||
sort: any(named: 'sort'),
|
sort: any(named: 'sort'),
|
||||||
messageFilters: any(named: 'messageFilters'),
|
messageFilters: any(named: 'messageFilters'),
|
||||||
paginationParams: any(named: 'paginationParams'),
|
paginationParams: pagination,
|
||||||
)).called(1);
|
)).called(1);
|
||||||
|
|
||||||
final offset = messageResponseList.length;
|
final offset = messageResponseList.length;
|
||||||
final paginatedMessageResponseList = _generateMessages(offset: offset);
|
final paginatedMessageResponseList = _generateMessages(offset: offset);
|
||||||
final pagination = PaginationParams(offset: offset);
|
final newPagination = pagination.copyWith(offset: offset);
|
||||||
|
|
||||||
when(() => mockClient.search(
|
when(() => mockClient.search(
|
||||||
testFilter,
|
testFilter,
|
||||||
query: any(named: 'query'),
|
query: any(named: 'query'),
|
||||||
sort: any(named: 'sort'),
|
sort: any(named: 'sort'),
|
||||||
messageFilters: any(named: 'messageFilters'),
|
messageFilters: any(named: 'messageFilters'),
|
||||||
paginationParams: pagination,
|
paginationParams: newPagination,
|
||||||
)).thenAnswer(
|
)).thenAnswer(
|
||||||
(_) async =>
|
(_) async => SearchMessagesResponse()
|
||||||
SearchMessagesResponse()..results = paginatedMessageResponseList,
|
..results = paginatedMessageResponseList
|
||||||
|
..next = null
|
||||||
|
..previous = null,
|
||||||
);
|
);
|
||||||
|
|
||||||
messageSearchBlocState.search(pagination: pagination, filter: testFilter);
|
messageSearchBlocState.search(pagination: pagination, filter: testFilter);
|
||||||
@@ -236,9 +241,7 @@ void main() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
testWidgets(
|
testWidgets(
|
||||||
'calling messageSearchBlocState.search() again with an offset '
|
'''calling messageSearchBlocState.search() again with an offset should emit error through queryUsersLoading if client.search() throws''',
|
||||||
'should emit error through queryUsersLoading if '
|
|
||||||
'client.search() throws',
|
|
||||||
(tester) async {
|
(tester) async {
|
||||||
const messageSearchBlocKey = Key('messageSearchBloc');
|
const messageSearchBlocKey = Key('messageSearchBloc');
|
||||||
const childKey = Key('child');
|
const childKey = Key('child');
|
||||||
@@ -260,19 +263,23 @@ void main() {
|
|||||||
find.byKey(messageSearchBlocKey),
|
find.byKey(messageSearchBlocKey),
|
||||||
);
|
);
|
||||||
|
|
||||||
final messageResponseList = _generateMessages();
|
const pagination = PaginationParams(limit: 25);
|
||||||
|
final messageResponseList = _generateMessages(count: 25);
|
||||||
|
|
||||||
when(() => mockClient.search(
|
when(() => mockClient.search(
|
||||||
testFilter,
|
testFilter,
|
||||||
query: any(named: 'query'),
|
query: any(named: 'query'),
|
||||||
sort: any(named: 'sort'),
|
sort: any(named: 'sort'),
|
||||||
messageFilters: any(named: 'messageFilters'),
|
messageFilters: any(named: 'messageFilters'),
|
||||||
paginationParams: any(named: 'paginationParams'),
|
paginationParams: pagination,
|
||||||
)).thenAnswer(
|
)).thenAnswer(
|
||||||
(_) async => SearchMessagesResponse()..results = messageResponseList,
|
(_) async => SearchMessagesResponse()
|
||||||
|
..results = messageResponseList
|
||||||
|
..next = null
|
||||||
|
..previous = null,
|
||||||
);
|
);
|
||||||
|
|
||||||
messageSearchBlocState.search(filter: testFilter);
|
messageSearchBlocState.search(pagination: pagination, filter: testFilter);
|
||||||
|
|
||||||
await expectLater(
|
await expectLater(
|
||||||
messageSearchBlocState.messagesStream,
|
messageSearchBlocState.messagesStream,
|
||||||
@@ -284,11 +291,11 @@ void main() {
|
|||||||
query: any(named: 'query'),
|
query: any(named: 'query'),
|
||||||
sort: any(named: 'sort'),
|
sort: any(named: 'sort'),
|
||||||
messageFilters: any(named: 'messageFilters'),
|
messageFilters: any(named: 'messageFilters'),
|
||||||
paginationParams: any(named: 'paginationParams'),
|
paginationParams: pagination,
|
||||||
)).called(1);
|
)).called(1);
|
||||||
|
|
||||||
final offset = messageResponseList.length;
|
final offset = messageResponseList.length;
|
||||||
final pagination = PaginationParams(offset: offset);
|
final newPagination = pagination.copyWith(offset: offset);
|
||||||
|
|
||||||
const error = 'Error! Error! Error!';
|
const error = 'Error! Error! Error!';
|
||||||
when(() => mockClient.search(
|
when(() => mockClient.search(
|
||||||
@@ -296,10 +303,13 @@ void main() {
|
|||||||
query: any(named: 'query'),
|
query: any(named: 'query'),
|
||||||
sort: any(named: 'sort'),
|
sort: any(named: 'sort'),
|
||||||
messageFilters: any(named: 'messageFilters'),
|
messageFilters: any(named: 'messageFilters'),
|
||||||
paginationParams: pagination,
|
paginationParams: newPagination,
|
||||||
)).thenThrow(error);
|
)).thenThrow(error);
|
||||||
|
|
||||||
messageSearchBlocState.search(pagination: pagination, filter: testFilter);
|
messageSearchBlocState.search(
|
||||||
|
pagination: newPagination,
|
||||||
|
filter: testFilter,
|
||||||
|
);
|
||||||
|
|
||||||
await expectLater(
|
await expectLater(
|
||||||
messageSearchBlocState.queryMessagesLoading,
|
messageSearchBlocState.queryMessagesLoading,
|
||||||
@@ -311,8 +321,80 @@ void main() {
|
|||||||
query: any(named: 'query'),
|
query: any(named: 'query'),
|
||||||
sort: any(named: 'sort'),
|
sort: any(named: 'sort'),
|
||||||
messageFilters: any(named: 'messageFilters'),
|
messageFilters: any(named: 'messageFilters'),
|
||||||
paginationParams: pagination,
|
paginationParams: newPagination,
|
||||||
)).called(1);
|
)).called(1);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
testWidgets(
|
||||||
|
'''calling messageSearchBlocState.search() again with an offset should do nothing and return if pagination is completed''',
|
||||||
|
(tester) async {
|
||||||
|
const messageSearchBlocKey = Key('messageSearchBloc');
|
||||||
|
const childKey = Key('child');
|
||||||
|
const messageSearchBloc = MessageSearchBloc(
|
||||||
|
key: messageSearchBlocKey,
|
||||||
|
child: Offstage(key: childKey),
|
||||||
|
);
|
||||||
|
|
||||||
|
final mockClient = MockClient();
|
||||||
|
|
||||||
|
await tester.pumpWidget(
|
||||||
|
StreamChatCore(
|
||||||
|
client: mockClient,
|
||||||
|
child: messageSearchBloc,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
final messageSearchBlocState = tester.state<MessageSearchBlocState>(
|
||||||
|
find.byKey(messageSearchBlocKey),
|
||||||
|
);
|
||||||
|
|
||||||
|
const pagination = PaginationParams(limit: 25);
|
||||||
|
|
||||||
|
final messageResponseList = _generateMessages(count: 20);
|
||||||
|
|
||||||
|
when(() => mockClient.search(
|
||||||
|
testFilter,
|
||||||
|
query: any(named: 'query'),
|
||||||
|
sort: any(named: 'sort'),
|
||||||
|
messageFilters: any(named: 'messageFilters'),
|
||||||
|
paginationParams: pagination,
|
||||||
|
)).thenAnswer(
|
||||||
|
(_) async => SearchMessagesResponse()
|
||||||
|
..results = messageResponseList
|
||||||
|
..next = null
|
||||||
|
..previous = null,
|
||||||
|
);
|
||||||
|
|
||||||
|
messageSearchBlocState.search(pagination: pagination, filter: testFilter);
|
||||||
|
|
||||||
|
await expectLater(
|
||||||
|
messageSearchBlocState.messagesStream,
|
||||||
|
emits(isSameMessageResponseListAs(messageResponseList)),
|
||||||
|
);
|
||||||
|
|
||||||
|
verify(() => mockClient.search(
|
||||||
|
testFilter,
|
||||||
|
query: any(named: 'query'),
|
||||||
|
sort: any(named: 'sort'),
|
||||||
|
messageFilters: any(named: 'messageFilters'),
|
||||||
|
paginationParams: pagination,
|
||||||
|
)).called(1);
|
||||||
|
|
||||||
|
final offset = messageResponseList.length;
|
||||||
|
final newPagination = pagination.copyWith(offset: offset);
|
||||||
|
|
||||||
|
messageSearchBlocState.search(
|
||||||
|
filter: testFilter,
|
||||||
|
pagination: newPagination,
|
||||||
|
);
|
||||||
|
|
||||||
|
// should emit nothing.
|
||||||
|
await expectLater(
|
||||||
|
// skipping the initial data (behaviorSubject).
|
||||||
|
messageSearchBlocState.messagesStream.skip(1),
|
||||||
|
emitsInOrder([]),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart';
|
|||||||
import 'mocks.dart';
|
import 'mocks.dart';
|
||||||
|
|
||||||
const testFilter = Filter.custom(operator: '\$test', value: 'testValue');
|
const testFilter = Filter.custom(operator: '\$test', value: 'testValue');
|
||||||
|
const testMessageFilter = Filter.custom(operator: '\$test', value: 'testValue');
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
List<GetMessageResponse> _generateMessages({
|
List<GetMessageResponse> _generateMessages({
|
||||||
@@ -28,6 +29,40 @@ void main() {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
testWidgets(
|
||||||
|
'should throw if both `messageQuery` and `messageFilters` are provided',
|
||||||
|
(tester) async {
|
||||||
|
expect(
|
||||||
|
() => MessageSearchListCore(
|
||||||
|
childBuilder: (_) => const Offstage(),
|
||||||
|
loadingBuilder: (_) => const Offstage(),
|
||||||
|
emptyBuilder: (_) => const Offstage(),
|
||||||
|
errorBuilder: (_, __) => const Offstage(),
|
||||||
|
filters: testFilter,
|
||||||
|
messageFilters: testMessageFilter,
|
||||||
|
messageQuery: 'test',
|
||||||
|
),
|
||||||
|
throwsAssertionError,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
testWidgets(
|
||||||
|
'should throw if both `messageQuery` and `messageFilters` are not provided',
|
||||||
|
(tester) async {
|
||||||
|
expect(
|
||||||
|
() => MessageSearchListCore(
|
||||||
|
childBuilder: (_) => const Offstage(),
|
||||||
|
loadingBuilder: (_) => const Offstage(),
|
||||||
|
emptyBuilder: (_) => const Offstage(),
|
||||||
|
errorBuilder: (_, __) => const Offstage(),
|
||||||
|
filters: testFilter,
|
||||||
|
),
|
||||||
|
throwsAssertionError,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
testWidgets(
|
testWidgets(
|
||||||
'should throw if MessageSearchListCore is used where MessageSearchBloc '
|
'should throw if MessageSearchListCore is used where MessageSearchBloc '
|
||||||
'is not present in the widget tree',
|
'is not present in the widget tree',
|
||||||
@@ -40,6 +75,7 @@ void main() {
|
|||||||
emptyBuilder: (BuildContext context) => const Offstage(),
|
emptyBuilder: (BuildContext context) => const Offstage(),
|
||||||
errorBuilder: (BuildContext context, Object? error) => const Offstage(),
|
errorBuilder: (BuildContext context, Object? error) => const Offstage(),
|
||||||
filters: testFilter,
|
filters: testFilter,
|
||||||
|
messageFilters: testMessageFilter,
|
||||||
);
|
);
|
||||||
|
|
||||||
await tester.pumpWidget(messageSearchListCore);
|
await tester.pumpWidget(messageSearchListCore);
|
||||||
@@ -61,6 +97,7 @@ void main() {
|
|||||||
emptyBuilder: (BuildContext context) => const Offstage(),
|
emptyBuilder: (BuildContext context) => const Offstage(),
|
||||||
errorBuilder: (BuildContext context, Object? error) => const Offstage(),
|
errorBuilder: (BuildContext context, Object? error) => const Offstage(),
|
||||||
filters: testFilter,
|
filters: testFilter,
|
||||||
|
messageFilters: testMessageFilter,
|
||||||
);
|
);
|
||||||
|
|
||||||
final mockClient = MockClient();
|
final mockClient = MockClient();
|
||||||
@@ -92,6 +129,7 @@ void main() {
|
|||||||
errorBuilder: (BuildContext context, Object error) => const Offstage(),
|
errorBuilder: (BuildContext context, Object error) => const Offstage(),
|
||||||
messageSearchListController: controller,
|
messageSearchListController: controller,
|
||||||
filters: testFilter,
|
filters: testFilter,
|
||||||
|
messageFilters: testMessageFilter,
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(controller.loadData, isNull);
|
expect(controller.loadData, isNull);
|
||||||
@@ -129,6 +167,7 @@ void main() {
|
|||||||
key: errorWidgetKey,
|
key: errorWidgetKey,
|
||||||
),
|
),
|
||||||
filters: testFilter,
|
filters: testFilter,
|
||||||
|
messageFilters: testMessageFilter,
|
||||||
);
|
);
|
||||||
|
|
||||||
final mockClient = MockClient();
|
final mockClient = MockClient();
|
||||||
@@ -138,7 +177,7 @@ void main() {
|
|||||||
testFilter,
|
testFilter,
|
||||||
query: any(named: 'query'),
|
query: any(named: 'query'),
|
||||||
sort: any(named: 'sort'),
|
sort: any(named: 'sort'),
|
||||||
messageFilters: any(named: 'messageFilters'),
|
messageFilters: testMessageFilter,
|
||||||
paginationParams: any(named: 'paginationParams'),
|
paginationParams: any(named: 'paginationParams'),
|
||||||
)).thenThrow(error);
|
)).thenThrow(error);
|
||||||
|
|
||||||
@@ -159,7 +198,7 @@ void main() {
|
|||||||
testFilter,
|
testFilter,
|
||||||
query: any(named: 'query'),
|
query: any(named: 'query'),
|
||||||
sort: any(named: 'sort'),
|
sort: any(named: 'sort'),
|
||||||
messageFilters: any(named: 'messageFilters'),
|
messageFilters: testMessageFilter,
|
||||||
paginationParams: any(named: 'paginationParams'),
|
paginationParams: any(named: 'paginationParams'),
|
||||||
)).called(1);
|
)).called(1);
|
||||||
},
|
},
|
||||||
@@ -179,6 +218,7 @@ void main() {
|
|||||||
const Offstage(key: emptyWidgetKey),
|
const Offstage(key: emptyWidgetKey),
|
||||||
errorBuilder: (BuildContext context, Object error) => const Offstage(),
|
errorBuilder: (BuildContext context, Object error) => const Offstage(),
|
||||||
filters: testFilter,
|
filters: testFilter,
|
||||||
|
messageFilters: testMessageFilter,
|
||||||
);
|
);
|
||||||
|
|
||||||
final mockClient = MockClient();
|
final mockClient = MockClient();
|
||||||
@@ -188,10 +228,13 @@ void main() {
|
|||||||
testFilter,
|
testFilter,
|
||||||
query: any(named: 'query'),
|
query: any(named: 'query'),
|
||||||
sort: any(named: 'sort'),
|
sort: any(named: 'sort'),
|
||||||
messageFilters: any(named: 'messageFilters'),
|
messageFilters: testMessageFilter,
|
||||||
paginationParams: any(named: 'paginationParams'),
|
paginationParams: any(named: 'paginationParams'),
|
||||||
)).thenAnswer(
|
)).thenAnswer(
|
||||||
(_) async => SearchMessagesResponse()..results = messageResponseList,
|
(_) async => SearchMessagesResponse()
|
||||||
|
..results = messageResponseList
|
||||||
|
..next = null
|
||||||
|
..previous = null,
|
||||||
);
|
);
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
@@ -211,7 +254,7 @@ void main() {
|
|||||||
testFilter,
|
testFilter,
|
||||||
query: any(named: 'query'),
|
query: any(named: 'query'),
|
||||||
sort: any(named: 'sort'),
|
sort: any(named: 'sort'),
|
||||||
messageFilters: any(named: 'messageFilters'),
|
messageFilters: testMessageFilter,
|
||||||
paginationParams: any(named: 'paginationParams'),
|
paginationParams: any(named: 'paginationParams'),
|
||||||
)).called(1);
|
)).called(1);
|
||||||
},
|
},
|
||||||
@@ -231,6 +274,7 @@ void main() {
|
|||||||
emptyBuilder: (BuildContext context) => const Offstage(),
|
emptyBuilder: (BuildContext context) => const Offstage(),
|
||||||
errorBuilder: (BuildContext context, Object error) => const Offstage(),
|
errorBuilder: (BuildContext context, Object error) => const Offstage(),
|
||||||
filters: testFilter,
|
filters: testFilter,
|
||||||
|
messageFilters: testMessageFilter,
|
||||||
);
|
);
|
||||||
|
|
||||||
final mockClient = MockClient();
|
final mockClient = MockClient();
|
||||||
@@ -240,10 +284,13 @@ void main() {
|
|||||||
testFilter,
|
testFilter,
|
||||||
query: any(named: 'query'),
|
query: any(named: 'query'),
|
||||||
sort: any(named: 'sort'),
|
sort: any(named: 'sort'),
|
||||||
messageFilters: any(named: 'messageFilters'),
|
messageFilters: testMessageFilter,
|
||||||
paginationParams: any(named: 'paginationParams'),
|
paginationParams: any(named: 'paginationParams'),
|
||||||
)).thenAnswer(
|
)).thenAnswer(
|
||||||
(_) async => SearchMessagesResponse()..results = messageResponseList,
|
(_) async => SearchMessagesResponse()
|
||||||
|
..results = messageResponseList
|
||||||
|
..next = null
|
||||||
|
..previous = null,
|
||||||
);
|
);
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
@@ -263,7 +310,7 @@ void main() {
|
|||||||
testFilter,
|
testFilter,
|
||||||
query: any(named: 'query'),
|
query: any(named: 'query'),
|
||||||
sort: any(named: 'sort'),
|
sort: any(named: 'sort'),
|
||||||
messageFilters: any(named: 'messageFilters'),
|
messageFilters: testMessageFilter,
|
||||||
paginationParams: any(named: 'paginationParams'),
|
paginationParams: any(named: 'paginationParams'),
|
||||||
)).called(1);
|
)).called(1);
|
||||||
},
|
},
|
||||||
@@ -275,7 +322,7 @@ void main() {
|
|||||||
(tester) async {
|
(tester) async {
|
||||||
const messageSearchListCoreKey = Key('messageSearchListCore');
|
const messageSearchListCoreKey = Key('messageSearchListCore');
|
||||||
const childWidgetKey = Key('childWidget');
|
const childWidgetKey = Key('childWidget');
|
||||||
const pagination = PaginationParams();
|
const pagination = PaginationParams(limit: 25);
|
||||||
final messageSearchListCore = MessageSearchListCore(
|
final messageSearchListCore = MessageSearchListCore(
|
||||||
key: messageSearchListCoreKey,
|
key: messageSearchListCoreKey,
|
||||||
childBuilder: (List<GetMessageResponse> messages) => Container(
|
childBuilder: (List<GetMessageResponse> messages) => Container(
|
||||||
@@ -289,19 +336,23 @@ void main() {
|
|||||||
errorBuilder: (BuildContext context, Object error) => const Offstage(),
|
errorBuilder: (BuildContext context, Object error) => const Offstage(),
|
||||||
paginationParams: pagination,
|
paginationParams: pagination,
|
||||||
filters: testFilter,
|
filters: testFilter,
|
||||||
|
messageFilters: testMessageFilter,
|
||||||
);
|
);
|
||||||
|
|
||||||
final mockClient = MockClient();
|
final mockClient = MockClient();
|
||||||
|
|
||||||
final messageResponseList = _generateMessages();
|
final messageResponseList = _generateMessages(count: 25);
|
||||||
when(() => mockClient.search(
|
when(() => mockClient.search(
|
||||||
testFilter,
|
testFilter,
|
||||||
query: any(named: 'query'),
|
query: any(named: 'query'),
|
||||||
sort: any(named: 'sort'),
|
sort: any(named: 'sort'),
|
||||||
messageFilters: any(named: 'messageFilters'),
|
messageFilters: testMessageFilter,
|
||||||
paginationParams: pagination,
|
paginationParams: pagination,
|
||||||
)).thenAnswer(
|
)).thenAnswer(
|
||||||
(_) async => SearchMessagesResponse()..results = messageResponseList,
|
(_) async => SearchMessagesResponse()
|
||||||
|
..results = messageResponseList
|
||||||
|
..next = null
|
||||||
|
..previous = null,
|
||||||
);
|
);
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
@@ -332,7 +383,7 @@ void main() {
|
|||||||
testFilter,
|
testFilter,
|
||||||
query: any(named: 'query'),
|
query: any(named: 'query'),
|
||||||
sort: any(named: 'sort'),
|
sort: any(named: 'sort'),
|
||||||
messageFilters: any(named: 'messageFilters'),
|
messageFilters: testMessageFilter,
|
||||||
paginationParams: pagination,
|
paginationParams: pagination,
|
||||||
)).called(1);
|
)).called(1);
|
||||||
|
|
||||||
@@ -348,11 +399,13 @@ void main() {
|
|||||||
testFilter,
|
testFilter,
|
||||||
query: any(named: 'query'),
|
query: any(named: 'query'),
|
||||||
sort: any(named: 'sort'),
|
sort: any(named: 'sort'),
|
||||||
messageFilters: any(named: 'messageFilters'),
|
messageFilters: testMessageFilter,
|
||||||
paginationParams: updatedPagination,
|
paginationParams: updatedPagination,
|
||||||
)).thenAnswer(
|
)).thenAnswer(
|
||||||
(_) async =>
|
(_) async => SearchMessagesResponse()
|
||||||
SearchMessagesResponse()..results = paginatedMessageResponseList,
|
..results = paginatedMessageResponseList
|
||||||
|
..next = null
|
||||||
|
..previous = null,
|
||||||
);
|
);
|
||||||
|
|
||||||
await messageSearchListCoreState.paginateData();
|
await messageSearchListCoreState.paginateData();
|
||||||
@@ -372,7 +425,7 @@ void main() {
|
|||||||
testFilter,
|
testFilter,
|
||||||
query: any(named: 'query'),
|
query: any(named: 'query'),
|
||||||
sort: any(named: 'sort'),
|
sort: any(named: 'sort'),
|
||||||
messageFilters: any(named: 'messageFilters'),
|
messageFilters: testMessageFilter,
|
||||||
paginationParams: updatedPagination,
|
paginationParams: updatedPagination,
|
||||||
)).called(1);
|
)).called(1);
|
||||||
},
|
},
|
||||||
@@ -406,6 +459,7 @@ void main() {
|
|||||||
const Offstage(),
|
const Offstage(),
|
||||||
paginationParams: pagination.copyWith(limit: limit),
|
paginationParams: pagination.copyWith(limit: limit),
|
||||||
filters: testFilter,
|
filters: testFilter,
|
||||||
|
messageFilters: testMessageFilter,
|
||||||
);
|
);
|
||||||
|
|
||||||
final mockClient = MockClient();
|
final mockClient = MockClient();
|
||||||
@@ -415,10 +469,13 @@ void main() {
|
|||||||
testFilter,
|
testFilter,
|
||||||
query: any(named: 'query'),
|
query: any(named: 'query'),
|
||||||
sort: any(named: 'sort'),
|
sort: any(named: 'sort'),
|
||||||
messageFilters: any(named: 'messageFilters'),
|
messageFilters: testMessageFilter,
|
||||||
paginationParams: pagination,
|
paginationParams: pagination,
|
||||||
)).thenAnswer(
|
)).thenAnswer(
|
||||||
(_) async => SearchMessagesResponse()..results = messageResponseList,
|
(_) async => SearchMessagesResponse()
|
||||||
|
..results = messageResponseList
|
||||||
|
..next = null
|
||||||
|
..previous = null,
|
||||||
);
|
);
|
||||||
|
|
||||||
await tester.pumpWidget(
|
await tester.pumpWidget(
|
||||||
@@ -453,7 +510,7 @@ void main() {
|
|||||||
testFilter,
|
testFilter,
|
||||||
query: any(named: 'query'),
|
query: any(named: 'query'),
|
||||||
sort: any(named: 'sort'),
|
sort: any(named: 'sort'),
|
||||||
messageFilters: any(named: 'messageFilters'),
|
messageFilters: testMessageFilter,
|
||||||
paginationParams: pagination,
|
paginationParams: pagination,
|
||||||
)).called(1);
|
)).called(1);
|
||||||
|
|
||||||
@@ -466,11 +523,13 @@ void main() {
|
|||||||
testFilter,
|
testFilter,
|
||||||
query: any(named: 'query'),
|
query: any(named: 'query'),
|
||||||
sort: any(named: 'sort'),
|
sort: any(named: 'sort'),
|
||||||
messageFilters: any(named: 'messageFilters'),
|
messageFilters: testMessageFilter,
|
||||||
paginationParams: updatedPagination,
|
paginationParams: updatedPagination,
|
||||||
)).thenAnswer(
|
)).thenAnswer(
|
||||||
(_) async =>
|
(_) async => SearchMessagesResponse()
|
||||||
SearchMessagesResponse()..results = updatedMessageResponseList,
|
..results = updatedMessageResponseList
|
||||||
|
..next = null
|
||||||
|
..previous = null,
|
||||||
);
|
);
|
||||||
|
|
||||||
await tester.pumpAndSettle();
|
await tester.pumpAndSettle();
|
||||||
@@ -487,7 +546,7 @@ void main() {
|
|||||||
testFilter,
|
testFilter,
|
||||||
query: any(named: 'query'),
|
query: any(named: 'query'),
|
||||||
sort: any(named: 'sort'),
|
sort: any(named: 'sort'),
|
||||||
messageFilters: any(named: 'messageFilters'),
|
messageFilters: testMessageFilter,
|
||||||
paginationParams: updatedPagination,
|
paginationParams: updatedPagination,
|
||||||
)).called(1);
|
)).called(1);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -321,7 +321,7 @@ void main() {
|
|||||||
(tester) async {
|
(tester) async {
|
||||||
const userListCoreKey = Key('userListCore');
|
const userListCoreKey = Key('userListCore');
|
||||||
const listWidgetKey = Key('listWidget');
|
const listWidgetKey = Key('listWidget');
|
||||||
const pagination = PaginationParams();
|
const pagination = PaginationParams(limit: 15);
|
||||||
final userListCore = UserListCore(
|
final userListCore = UserListCore(
|
||||||
key: userListCoreKey,
|
key: userListCoreKey,
|
||||||
listBuilder: (_, items) => Container(
|
listBuilder: (_, items) => Container(
|
||||||
@@ -347,7 +347,7 @@ void main() {
|
|||||||
|
|
||||||
final mockClient = MockClient();
|
final mockClient = MockClient();
|
||||||
|
|
||||||
final users = _generateUsers();
|
final users = _generateUsers(count: 15);
|
||||||
when(() => mockClient.queryUsers(
|
when(() => mockClient.queryUsers(
|
||||||
filter: any(named: 'filter'),
|
filter: any(named: 'filter'),
|
||||||
sort: any(named: 'sort'),
|
sort: any(named: 'sort'),
|
||||||
|
|||||||
@@ -164,16 +164,17 @@ void main() {
|
|||||||
find.byKey(usersBlocKey),
|
find.byKey(usersBlocKey),
|
||||||
);
|
);
|
||||||
|
|
||||||
final users = _generateUsers();
|
const pagination = PaginationParams(limit: 25);
|
||||||
|
final users = _generateUsers(count: 25);
|
||||||
|
|
||||||
when(() => mockClient.queryUsers(
|
when(() => mockClient.queryUsers(
|
||||||
filter: any(named: 'filter'),
|
filter: any(named: 'filter'),
|
||||||
sort: any(named: 'sort'),
|
sort: any(named: 'sort'),
|
||||||
presence: any(named: 'presence'),
|
presence: any(named: 'presence'),
|
||||||
pagination: any(named: 'pagination'),
|
pagination: pagination,
|
||||||
)).thenAnswer((_) async => QueryUsersResponse()..users = users);
|
)).thenAnswer((_) async => QueryUsersResponse()..users = users);
|
||||||
|
|
||||||
usersBlocState.queryUsers();
|
usersBlocState.queryUsers(pagination: pagination);
|
||||||
|
|
||||||
await expectLater(
|
await expectLater(
|
||||||
usersBlocState.usersStream,
|
usersBlocState.usersStream,
|
||||||
@@ -184,23 +185,23 @@ void main() {
|
|||||||
filter: any(named: 'filter'),
|
filter: any(named: 'filter'),
|
||||||
sort: any(named: 'sort'),
|
sort: any(named: 'sort'),
|
||||||
presence: any(named: 'presence'),
|
presence: any(named: 'presence'),
|
||||||
pagination: any(named: 'pagination'),
|
pagination: pagination,
|
||||||
)).called(1);
|
)).called(1);
|
||||||
|
|
||||||
final offset = users.length;
|
final offset = users.length;
|
||||||
final paginatedUsers = _generateUsers(offset: offset);
|
final paginatedUsers = _generateUsers(offset: offset);
|
||||||
final pagination = PaginationParams(offset: offset);
|
final newPagination = pagination.copyWith(offset: offset);
|
||||||
|
|
||||||
when(() => mockClient.queryUsers(
|
when(() => mockClient.queryUsers(
|
||||||
filter: any(named: 'filter'),
|
filter: any(named: 'filter'),
|
||||||
sort: any(named: 'sort'),
|
sort: any(named: 'sort'),
|
||||||
presence: any(named: 'presence'),
|
presence: any(named: 'presence'),
|
||||||
pagination: pagination,
|
pagination: newPagination,
|
||||||
))
|
)).thenAnswer(
|
||||||
.thenAnswer(
|
(_) async => QueryUsersResponse()..users = paginatedUsers,
|
||||||
(_) async => QueryUsersResponse()..users = paginatedUsers);
|
);
|
||||||
|
|
||||||
usersBlocState.queryUsers(pagination: pagination);
|
usersBlocState.queryUsers(pagination: newPagination);
|
||||||
|
|
||||||
await Future.wait([
|
await Future.wait([
|
||||||
expectLater(
|
expectLater(
|
||||||
@@ -217,7 +218,7 @@ void main() {
|
|||||||
filter: any(named: 'filter'),
|
filter: any(named: 'filter'),
|
||||||
sort: any(named: 'sort'),
|
sort: any(named: 'sort'),
|
||||||
presence: any(named: 'presence'),
|
presence: any(named: 'presence'),
|
||||||
pagination: pagination,
|
pagination: newPagination,
|
||||||
)).called(1);
|
)).called(1);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@@ -247,13 +248,89 @@ void main() {
|
|||||||
find.byKey(usersBlocKey),
|
find.byKey(usersBlocKey),
|
||||||
);
|
);
|
||||||
|
|
||||||
final users = _generateUsers();
|
const pagination = PaginationParams(limit: 25);
|
||||||
|
final users = _generateUsers(count: 25);
|
||||||
|
|
||||||
when(() => mockClient.queryUsers(
|
when(() => mockClient.queryUsers(
|
||||||
filter: any(named: 'filter'),
|
filter: any(named: 'filter'),
|
||||||
sort: any(named: 'sort'),
|
sort: any(named: 'sort'),
|
||||||
presence: any(named: 'presence'),
|
presence: any(named: 'presence'),
|
||||||
pagination: any(named: 'pagination'),
|
pagination: pagination,
|
||||||
|
)).thenAnswer((_) async => QueryUsersResponse()..users = users);
|
||||||
|
|
||||||
|
usersBlocState.queryUsers(pagination: pagination);
|
||||||
|
|
||||||
|
await expectLater(
|
||||||
|
usersBlocState.usersStream,
|
||||||
|
emits(isSameUserListAs(users)),
|
||||||
|
);
|
||||||
|
|
||||||
|
verify(() => mockClient.queryUsers(
|
||||||
|
filter: any(named: 'filter'),
|
||||||
|
sort: any(named: 'sort'),
|
||||||
|
presence: any(named: 'presence'),
|
||||||
|
pagination: pagination,
|
||||||
|
)).called(1);
|
||||||
|
|
||||||
|
final offset = users.length;
|
||||||
|
final newPagination = pagination.copyWith(offset: offset);
|
||||||
|
|
||||||
|
const error = 'Error! Error! Error!';
|
||||||
|
|
||||||
|
when(() => mockClient.queryUsers(
|
||||||
|
filter: any(named: 'filter'),
|
||||||
|
sort: any(named: 'sort'),
|
||||||
|
presence: any(named: 'presence'),
|
||||||
|
pagination: newPagination,
|
||||||
|
)).thenThrow(error);
|
||||||
|
|
||||||
|
usersBlocState.queryUsers(pagination: newPagination);
|
||||||
|
|
||||||
|
await expectLater(
|
||||||
|
usersBlocState.queryUsersLoading,
|
||||||
|
emitsError(error),
|
||||||
|
);
|
||||||
|
|
||||||
|
verify(() => mockClient.queryUsers(
|
||||||
|
filter: any(named: 'filter'),
|
||||||
|
sort: any(named: 'sort'),
|
||||||
|
presence: any(named: 'presence'),
|
||||||
|
pagination: newPagination,
|
||||||
|
)).called(1);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
testWidgets(
|
||||||
|
'''calling usersBlocState.queryUsers() again with an offset should do nothing and return if pagination is completed''',
|
||||||
|
(tester) async {
|
||||||
|
const usersBlocKey = Key('usersBloc');
|
||||||
|
const childKey = Key('child');
|
||||||
|
const usersBloc = UsersBloc(
|
||||||
|
key: usersBlocKey,
|
||||||
|
child: Offstage(key: childKey),
|
||||||
|
);
|
||||||
|
|
||||||
|
final mockClient = MockClient();
|
||||||
|
|
||||||
|
await tester.pumpWidget(
|
||||||
|
StreamChatCore(
|
||||||
|
client: mockClient,
|
||||||
|
child: usersBloc,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
final usersBlocState = tester.state<UsersBlocState>(
|
||||||
|
find.byKey(usersBlocKey),
|
||||||
|
);
|
||||||
|
|
||||||
|
const pagination = PaginationParams(limit: 30);
|
||||||
|
final users = _generateUsers(count: 25);
|
||||||
|
|
||||||
|
when(() => mockClient.queryUsers(
|
||||||
|
filter: any(named: 'filter'),
|
||||||
|
sort: any(named: 'sort'),
|
||||||
|
presence: any(named: 'presence'),
|
||||||
|
pagination: pagination,
|
||||||
)).thenAnswer((_) async => QueryUsersResponse()..users = users);
|
)).thenAnswer((_) async => QueryUsersResponse()..users = users);
|
||||||
|
|
||||||
usersBlocState.queryUsers();
|
usersBlocState.queryUsers();
|
||||||
@@ -271,30 +348,16 @@ void main() {
|
|||||||
)).called(1);
|
)).called(1);
|
||||||
|
|
||||||
final offset = users.length;
|
final offset = users.length;
|
||||||
final pagination = PaginationParams(offset: offset);
|
final newPagination = pagination.copyWith(offset: offset);
|
||||||
|
|
||||||
const error = 'Error! Error! Error!';
|
usersBlocState.queryUsers(pagination: newPagination);
|
||||||
|
|
||||||
when(() => mockClient.queryUsers(
|
|
||||||
filter: any(named: 'filter'),
|
|
||||||
sort: any(named: 'sort'),
|
|
||||||
presence: any(named: 'presence'),
|
|
||||||
pagination: pagination,
|
|
||||||
)).thenThrow(error);
|
|
||||||
|
|
||||||
usersBlocState.queryUsers(pagination: pagination);
|
|
||||||
|
|
||||||
|
// should emit nothing.
|
||||||
await expectLater(
|
await expectLater(
|
||||||
usersBlocState.queryUsersLoading,
|
// skipping the initial data (behaviorSubject).
|
||||||
emitsError(error),
|
usersBlocState.usersStream,
|
||||||
|
emitsInOrder([]),
|
||||||
);
|
);
|
||||||
|
|
||||||
verify(() => mockClient.queryUsers(
|
|
||||||
filter: any(named: 'filter'),
|
|
||||||
sort: any(named: 'sort'),
|
|
||||||
presence: any(named: 'presence'),
|
|
||||||
pagination: pagination,
|
|
||||||
)).called(1);
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -112,8 +112,9 @@ class ChannelQueryDao extends DatabaseAccessor<MoorChatDatabase>
|
|||||||
|
|
||||||
cachedChannels.sort(chainedComparator);
|
cachedChannels.sort(chainedComparator);
|
||||||
|
|
||||||
if (paginationParams?.offset != null && cachedChannels.isNotEmpty) {
|
final offset = paginationParams?.offset;
|
||||||
cachedChannels.removeRange(0, paginationParams!.offset);
|
if (offset != null && offset > 0 && cachedChannels.isNotEmpty) {
|
||||||
|
cachedChannels.removeRange(0, offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (paginationParams?.limit != null) {
|
if (paginationParams?.limit != null) {
|
||||||
|
|||||||
@@ -117,8 +117,9 @@ class MessageDao extends DatabaseAccessor<MoorChatDatabase>
|
|||||||
msgList.removeRange(0, greaterThanIndex);
|
msgList.removeRange(0, greaterThanIndex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (options?.limit != null) {
|
final limit = options?.limit;
|
||||||
return msgList.take(options!.limit).toList();
|
if (limit != null && limit > 0) {
|
||||||
|
return msgList.take(limit).toList();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return msgList;
|
return msgList;
|
||||||
|
|||||||
Reference in New Issue
Block a user