Skip to content

Commit f20a393

Browse files
authored
feat(llc, core): add support for filtering and sorting threads (#2235)
1 parent 885786a commit f20a393

10 files changed

+160
-3
lines changed

packages/stream_chat/CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
✅ Added
44

55
- Added teams role support for users.
6+
- Added support for Filtering and Sorting in the `client.queryThreads` method.
67

78
## 9.8.0
89

packages/stream_chat/lib/src/client/client.dart

+7
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import 'package:stream_chat/src/core/models/own_user.dart';
3030
import 'package:stream_chat/src/core/models/poll.dart';
3131
import 'package:stream_chat/src/core/models/poll_option.dart';
3232
import 'package:stream_chat/src/core/models/poll_vote.dart';
33+
import 'package:stream_chat/src/core/models/thread.dart';
3334
import 'package:stream_chat/src/core/models/user.dart';
3435
import 'package:stream_chat/src/core/util/utils.dart';
3536
import 'package:stream_chat/src/db/chat_persistence_client.dart';
@@ -1841,11 +1842,17 @@ class StreamChatClient {
18411842
_chatApi.general.enrichUrl(url);
18421843

18431844
/// Queries threads with the given [options] and [pagination] params.
1845+
///
1846+
/// Optionally, pass [filter] and [sort] to filter and sort the threads.
18441847
Future<QueryThreadsResponse> queryThreads({
1848+
Filter? filter,
1849+
SortOrder<Thread>? sort,
18451850
ThreadOptions options = const ThreadOptions(),
18461851
PaginationParams pagination = const PaginationParams(),
18471852
}) =>
18481853
_chatApi.threads.queryThreads(
1854+
filter: filter,
1855+
sort: sort,
18491856
options: options,
18501857
pagination: pagination,
18511858
);

packages/stream_chat/lib/src/core/api/threads_api.dart

+7
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@ import 'dart:convert';
22

33
import 'package:stream_chat/src/core/api/requests.dart';
44
import 'package:stream_chat/src/core/api/responses.dart';
5+
import 'package:stream_chat/src/core/api/sort_order.dart';
56
import 'package:stream_chat/src/core/http/stream_http_client.dart';
7+
import 'package:stream_chat/src/core/models/filter.dart';
8+
import 'package:stream_chat/src/core/models/thread.dart';
69

710
/// Defines the api dedicated to threads operations
811
class ThreadsApi {
@@ -13,12 +16,16 @@ class ThreadsApi {
1316

1417
/// Queries threads with the given [options] and [pagination] params.
1518
Future<QueryThreadsResponse> queryThreads({
19+
Filter? filter,
20+
SortOrder<Thread>? sort,
1621
ThreadOptions options = const ThreadOptions(),
1722
PaginationParams pagination = const PaginationParams(),
1823
}) async {
1924
final response = await _client.post(
2025
'/threads',
2126
data: jsonEncode({
27+
if (filter != null) 'filter': filter.toJson(),
28+
if (sort != null) 'sort': sort.map((e) => e.toJson()).toList(),
2229
...options.toJson(),
2330
...pagination.toJson(),
2431
}),

packages/stream_chat/lib/src/core/models/thread.dart

+51-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import 'package:equatable/equatable.dart';
22
import 'package:json_annotation/json_annotation.dart';
33
import 'package:stream_chat/src/core/models/channel_model.dart';
4+
import 'package:stream_chat/src/core/models/comparable_field.dart';
45
import 'package:stream_chat/src/core/models/draft.dart';
56
import 'package:stream_chat/src/core/models/message.dart';
67
import 'package:stream_chat/src/core/models/read.dart';
@@ -21,7 +22,7 @@ const _nullConst = _NullConst();
2122
/// to a message in a channel.
2223
/// {@endtemplate}
2324
@JsonSerializable()
24-
class Thread extends Equatable {
25+
class Thread extends Equatable implements ComparableFieldProvider {
2526
/// {@macro streamThread}
2627
Thread({
2728
this.activeParticipantCount,
@@ -227,4 +228,53 @@ class Thread extends Equatable {
227228
read,
228229
draft,
229230
];
231+
232+
@override
233+
ComparableField? getComparableField(String sortKey) {
234+
final value = switch (sortKey) {
235+
ThreadSortKey.lastMessageAt => lastMessageAt,
236+
ThreadSortKey.createdAt => createdAt,
237+
ThreadSortKey.updatedAt => updatedAt,
238+
ThreadSortKey.replyCount => replyCount,
239+
ThreadSortKey.participantCount => participantCount,
240+
ThreadSortKey.activeParticipantCount => activeParticipantCount,
241+
ThreadSortKey.parentMessageId => parentMessageId,
242+
// TODO: Support providing default value for hasUnread
243+
ThreadSortKey.hasUnread => null,
244+
_ => null,
245+
};
246+
247+
return ComparableField.fromValue(value);
248+
}
249+
}
250+
251+
/// Extension type representing sortable fields for [Thread].
252+
///
253+
/// This type provides type-safe keys that can be used for sorting threads
254+
/// in queries. Each constant represents a field that can be sorted on.
255+
extension type const ThreadSortKey(String key) implements String {
256+
/// Sort threads by their last message date.
257+
static const lastMessageAt = ThreadSortKey('last_message_at');
258+
259+
/// Sort threads by their creation date.
260+
static const createdAt = ThreadSortKey('created_at');
261+
262+
/// Sort threads by their last update date.
263+
static const updatedAt = ThreadSortKey('updated_at');
264+
265+
/// Sort threads by their reply count.
266+
static const replyCount = ThreadSortKey('reply_count');
267+
268+
/// Sort threads by their participant count.
269+
static const participantCount = ThreadSortKey('participant_count');
270+
271+
/// Sort threads by their active participant count.
272+
static const activeParticipantCount =
273+
ThreadSortKey('active_participant_count');
274+
275+
/// Sort threads by their parent message id.
276+
static const parentMessageId = ThreadSortKey('parent_message_id');
277+
278+
/// Sort threads by their has unread.
279+
static const hasUnread = ThreadSortKey('has_unread');
230280
}

packages/stream_chat_flutter_core/CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
✅ Added
44

55
- Added `StreamDraftListController` to manage the list of draft messages.
6+
- Added support for Filtering and Sorting in the `StreamThreadListController`.
67

78
## 9.8.0
89

packages/stream_chat_flutter_core/lib/src/stream_member_list_controller.dart

+6
Original file line numberDiff line numberDiff line change
@@ -81,12 +81,18 @@ class StreamMemberListController extends PagedValueNotifier<int, Member> {
8181
///
8282
/// Use this if you need to support runtime filter changes,
8383
/// through custom filters UI.
84+
///
85+
/// Note: This will not trigger a new query. make sure to call
86+
/// [doInitialLoad] after setting a new filter.
8487
set filter(Filter? value) => _activeFilter = value;
8588

8689
/// Allows for the change of the query sort used for member queries.
8790
///
8891
/// Use this if you need to support runtime sort changes,
8992
/// through custom sort UI.
93+
///
94+
/// Note: This will not trigger a new query. make sure to call
95+
/// [doInitialLoad] after setting a new sort.
9096
set sort(SortOrder<Member>? value) => _activeSort = value;
9197

9298
@override

packages/stream_chat_flutter_core/lib/src/stream_message_search_list_controller.dart

+12
Original file line numberDiff line numberDiff line change
@@ -112,24 +112,36 @@ class StreamMessageSearchListController
112112
///
113113
/// Use this if you need to support runtime filter changes,
114114
/// through custom filters UI.
115+
///
116+
/// Note: This will not trigger a new query. make sure to call
117+
/// [doInitialLoad] after setting a new filter.
115118
set filter(Filter value) => _activeFilter = value;
116119

117120
/// Allows for the change of message filters used for user queries.
118121
///
119122
/// Use this if you need to support runtime filter changes,
120123
/// through custom filters UI.
124+
///
125+
/// Note: This will not trigger a new query. make sure to call
126+
/// [doInitialLoad] after setting a new filter.
121127
set messageFilter(Filter? value) => _activeMessageFilter = value;
122128

123129
/// Allows for the change of filters used for user queries.
124130
///
125131
/// Use this if you need to support runtime filter changes,
126132
/// through custom filters UI.
133+
///
134+
/// Note: This will not trigger a new query. make sure to call
135+
/// [doInitialLoad] after setting a new filter.
127136
set searchQuery(String? value) => _activeSearchQuery = value;
128137

129138
/// Allows for the change of the query sort used for user queries.
130139
///
131140
/// Use this if you need to support runtime sort changes,
132141
/// through custom sort UI.
142+
///
143+
/// Note: This will not trigger a new query. make sure to call
144+
/// [doInitialLoad] after setting a new sort.
133145
set sort(SortOrder? value) => _activeSort = value;
134146

135147
@override

packages/stream_chat_flutter_core/lib/src/stream_poll_vote_list_controller.dart

+6
Original file line numberDiff line numberDiff line change
@@ -90,12 +90,18 @@ class StreamPollVoteListController
9090
///
9191
/// Use this if you need to support runtime filter changes,
9292
/// through custom filters UI.
93+
///
94+
/// Note: This will not trigger a new query. make sure to call
95+
/// [doInitialLoad] after setting a new filter.
9396
set filter(Filter? value) => _activeFilter = value;
9497

9598
/// Allows for the change of the query sort used for poll vote queries.
9699
///
97100
/// Use this if you need to support runtime sort changes,
98101
/// through custom sort UI.
102+
///
103+
/// Note: This will not trigger a new query. make sure to call
104+
/// [doInitialLoad] after setting a new sort.
99105
set sort(SortOrder<PollVote>? value) => _activeSort = value;
100106

101107
@override

packages/stream_chat_flutter_core/lib/src/stream_thread_list_controller.dart

+63-2
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,13 @@ class StreamThreadListController extends PagedValueNotifier<String, Thread> {
2525
StreamThreadListController({
2626
required this.client,
2727
StreamThreadListEventHandler? eventHandler,
28+
this.filter,
29+
this.sort,
2830
this.options = const ThreadOptions(),
2931
this.limit = defaultThreadsPagedLimit,
30-
}) : _activeOptions = options,
32+
}) : _activeFilter = filter,
33+
_activeSort = sort,
34+
_activeOptions = options,
3135
_eventHandler = eventHandler ?? StreamThreadListEventHandler(),
3236
super(const PagedValue.loading());
3337

@@ -36,9 +40,13 @@ class StreamThreadListController extends PagedValueNotifier<String, Thread> {
3640
super.value, {
3741
required this.client,
3842
StreamThreadListEventHandler? eventHandler,
43+
this.filter,
44+
this.sort,
3945
this.options = const ThreadOptions(),
4046
this.limit = defaultThreadsPagedLimit,
41-
}) : _activeOptions = options,
47+
}) : _activeFilter = filter,
48+
_activeSort = sort,
49+
_activeOptions = options,
4250
_eventHandler = eventHandler ?? StreamThreadListEventHandler();
4351

4452
/// The Stream client used to perform the queries.
@@ -47,6 +55,21 @@ class StreamThreadListController extends PagedValueNotifier<String, Thread> {
4755
/// The thread event handlers to use for the thread list.
4856
final StreamThreadListEventHandler _eventHandler;
4957

58+
/// The query filters to use.
59+
///
60+
/// You can query on any of the custom fields you've defined on the [Thread].
61+
final Filter? filter;
62+
Filter? _activeFilter;
63+
64+
/// The sorting used for the threads matching the filters.
65+
///
66+
/// Sorting is based on field and direction, multiple sorting options
67+
/// can be provided.
68+
///
69+
/// Direction can be ascending or descending.
70+
final SortOrder<Thread>? sort;
71+
SortOrder<Thread>? _activeSort;
72+
5073
/// The limit to apply to the thread list.
5174
///
5275
/// The default is set to [defaultUserPagedLimit].
@@ -58,10 +81,31 @@ class StreamThreadListController extends PagedValueNotifier<String, Thread> {
5881
final ThreadOptions options;
5982
ThreadOptions _activeOptions;
6083

84+
/// Allows for the change of filters used for thread queries.
85+
///
86+
/// Use this if you need to support runtime filter changes,
87+
/// through custom filters UI.
88+
///
89+
/// Note: This will not trigger a new query. make sure to call
90+
/// [doInitialLoad] after setting a new filter.
91+
set filter(Filter? value) => _activeFilter = value;
92+
93+
/// Allows for the change of the query sort used for thread queries.
94+
///
95+
/// Use this if you need to support runtime sort changes,
96+
/// through custom sort UI.
97+
///
98+
/// Note: This will not trigger a new query. make sure to call
99+
/// [doInitialLoad] after setting a new sort.
100+
set sort(SortOrder<Thread>? value) => _activeSort = value;
101+
61102
/// Allows for the change of the [options] at runtime.
62103
///
63104
/// Use this if you need to support runtime option changes,
64105
/// through custom filters UI.
106+
///
107+
/// Note: This will not trigger a new query. make sure to call
108+
/// [doInitialLoad] after setting a new option.
65109
set options(ThreadOptions options) => _activeOptions = options;
66110

67111
/// The ids of the threads that have unseen messages.
@@ -80,6 +124,19 @@ class StreamThreadListController extends PagedValueNotifier<String, Thread> {
80124
/// Clears the set of unseen thread IDs.
81125
void clearUnseenThreadIds() => _unseenThreadIds.value = const {};
82126

127+
@override
128+
set value(PagedValue<String, Thread> newValue) {
129+
super.value = switch (_activeSort) {
130+
null => newValue,
131+
final threadSort => newValue.maybeMap(
132+
orElse: () => newValue,
133+
(success) => success.copyWith(
134+
items: success.items.sorted(threadSort.compare),
135+
),
136+
),
137+
};
138+
}
139+
83140
@override
84141
Future<void> doInitialLoad() async {
85142
final limit = min(
@@ -88,6 +145,8 @@ class StreamThreadListController extends PagedValueNotifier<String, Thread> {
88145
);
89146
try {
90147
final response = await client.queryThreads(
148+
filter: _activeFilter,
149+
sort: _activeSort,
91150
options: _activeOptions,
92151
pagination: PaginationParams(limit: limit),
93152
);
@@ -114,6 +173,8 @@ class StreamThreadListController extends PagedValueNotifier<String, Thread> {
114173

115174
try {
116175
final response = await client.queryThreads(
176+
filter: _activeFilter,
177+
sort: _activeSort,
117178
options: _activeOptions,
118179
pagination: PaginationParams(limit: limit, next: nextPageKey),
119180
);

packages/stream_chat_flutter_core/lib/src/stream_user_list_controller.dart

+6
Original file line numberDiff line numberDiff line change
@@ -86,12 +86,18 @@ class StreamUserListController extends PagedValueNotifier<int, User> {
8686
///
8787
/// Use this if you need to support runtime filter changes,
8888
/// through custom filters UI.
89+
///
90+
/// Note: This will not trigger a new query. make sure to call
91+
/// [doInitialLoad] after setting a new filter.
8992
set filter(Filter? value) => _activeFilter = value;
9093

9194
/// Allows for the change of the query sort used for user queries.
9295
///
9396
/// Use this if you need to support runtime sort changes,
9497
/// through custom sort UI.
98+
///
99+
/// Note: This will not trigger a new query. make sure to call
100+
/// [doInitialLoad] after setting a new sort.
95101
set sort(SortOrder<User>? value) => _activeSort = value;
96102

97103
@override

0 commit comments

Comments
 (0)