Merge branch 'develop' into fix/message-search-pagination
This commit is contained in:
@@ -1,8 +1,20 @@
|
||||
## Upcoming
|
||||
|
||||
🛑️ Breaking Changes from `2.2.1`
|
||||
|
||||
- Added 6 new methods in `ChatPersistenceClient`.
|
||||
- `bulkUpdateMessages`
|
||||
- `bulkUpdatePinnedMessages`
|
||||
- `bulkUpdateMembers`
|
||||
- `bulkUpdateReads`
|
||||
- `updatePinnedMessageReactions`
|
||||
- `deletePinnedMessageReactionsByMessageId`
|
||||
|
||||
✅ Added
|
||||
|
||||
- Add support for `next`, `previous` value pagination in `client.search`, [read more.](https://getstream.io/chat/docs/other-rest/search/#pagination)
|
||||
- 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
|
||||
|
||||
|
||||
@@ -51,6 +51,9 @@ enum FilterOperator {
|
||||
|
||||
/// Matches none of the values specified in an array.
|
||||
nor,
|
||||
|
||||
/// Matches any list that contains the specified value
|
||||
contains,
|
||||
}
|
||||
|
||||
/// Helper extension for [FilterOperator]
|
||||
@@ -71,6 +74,7 @@ extension FilterOperatorX on FilterOperator {
|
||||
FilterOperator.and: '\$and',
|
||||
FilterOperator.or: '\$or',
|
||||
FilterOperator.nor: '\$nor',
|
||||
FilterOperator.contains: '\$contains',
|
||||
}[this]!;
|
||||
}
|
||||
|
||||
@@ -157,6 +161,10 @@ class Filter extends Equatable {
|
||||
factory Filter.exists(String key, {bool exists = true}) =>
|
||||
Filter._(operator: FilterOperator.exists, key: key, value: exists);
|
||||
|
||||
/// Matches any list that contains the specified values
|
||||
factory Filter.contains(String key, Object value) =>
|
||||
Filter._(operator: FilterOperator.contains, key: key, value: value);
|
||||
|
||||
/// Creates a custom [Filter] if there isn't one already available.
|
||||
const factory Filter.custom({
|
||||
required Object value,
|
||||
@@ -164,6 +172,9 @@ class Filter extends Equatable {
|
||||
String? key,
|
||||
}) = Filter.__;
|
||||
|
||||
/// An empty filter
|
||||
factory Filter.empty() => const Filter.raw(value: {});
|
||||
|
||||
/// Creates a custom [Filter] from a raw map value
|
||||
///
|
||||
/// ```dart
|
||||
|
||||
@@ -143,11 +143,19 @@ abstract class ChatPersistenceClient {
|
||||
|
||||
/// Updates the message data of a particular channel [cid] with
|
||||
/// the new [messages] data
|
||||
Future<void> updateMessages(String cid, List<Message> messages);
|
||||
Future<void> updateMessages(String cid, List<Message> messages) =>
|
||||
bulkUpdateMessages({cid: messages});
|
||||
|
||||
/// Bulk updates the message data of multiple channels.
|
||||
Future<void> bulkUpdateMessages(Map<String, List<Message>> messages);
|
||||
|
||||
/// Updates the pinned message data of a particular channel [cid] with
|
||||
/// the new [messages] data
|
||||
Future<void> updatePinnedMessages(String cid, List<Message> messages);
|
||||
Future<void> updatePinnedMessages(String cid, List<Message> messages) =>
|
||||
bulkUpdatePinnedMessages({cid: messages});
|
||||
|
||||
/// Bulk updates the message data of multiple channels.
|
||||
Future<void> bulkUpdatePinnedMessages(Map<String, List<Message>> messages);
|
||||
|
||||
/// Returns all the threads by parent message of a particular channel by
|
||||
/// providing channel [cid]
|
||||
@@ -158,11 +166,19 @@ abstract class ChatPersistenceClient {
|
||||
|
||||
/// Updates all the members of a particular channle [cid]
|
||||
/// with the new [members] data
|
||||
Future<void> updateMembers(String cid, List<Member> members);
|
||||
Future<void> updateMembers(String cid, List<Member> members) =>
|
||||
bulkUpdateMembers({cid: members});
|
||||
|
||||
/// Bulk updates the members data of multiple channels.
|
||||
Future<void> bulkUpdateMembers(Map<String, List<Member>> members);
|
||||
|
||||
/// Updates the read data of a particular channel [cid] with
|
||||
/// the new [reads] data
|
||||
Future<void> updateReads(String cid, List<Read> reads);
|
||||
Future<void> updateReads(String cid, List<Read> reads) =>
|
||||
bulkUpdateReads({cid: reads});
|
||||
|
||||
/// Bulk updates the read data of multiple channels.
|
||||
Future<void> bulkUpdateReads(Map<String, List<Read>> reads);
|
||||
|
||||
/// Updates the users data with the new [users] data
|
||||
Future<void> updateUsers(List<User> users);
|
||||
@@ -170,9 +186,15 @@ abstract class ChatPersistenceClient {
|
||||
/// Updates the reactions data with the new [reactions] data
|
||||
Future<void> updateReactions(List<Reaction> reactions);
|
||||
|
||||
/// Updates the pinned message reactions data with the new [reactions] data
|
||||
Future<void> updatePinnedMessageReactions(List<Reaction> reactions);
|
||||
|
||||
/// Deletes all the reactions by [messageIds]
|
||||
Future<void> deleteReactionsByMessageId(List<String> messageIds);
|
||||
|
||||
/// Deletes all the pinned messages reactions by [messageIds]
|
||||
Future<void> deletePinnedMessageReactionsByMessageId(List<String> messageIds);
|
||||
|
||||
/// Deletes all the members by channel [cids]
|
||||
Future<void> deleteMembersByCids(List<String> cids);
|
||||
|
||||
@@ -182,85 +204,92 @@ abstract class ChatPersistenceClient {
|
||||
|
||||
/// Update list of channel states
|
||||
Future<void> updateChannelStates(List<ChannelState> channelStates) async {
|
||||
final deleteReactions = deleteReactionsByMessageId(channelStates
|
||||
.expand((it) => it.messages)
|
||||
.map((m) => m.id)
|
||||
.toList(growable: false));
|
||||
final reactionsToDelete = <String>[];
|
||||
final pinnedReactionsToDelete = <String>[];
|
||||
final membersToDelete = <String>[];
|
||||
|
||||
final cleanedChannelStates =
|
||||
channelStates.where((it) => it.channel != null);
|
||||
final channels = <ChannelModel>[];
|
||||
final channelWithMessages = <String, List<Message>>{};
|
||||
final channelWithPinnedMessages = <String, List<Message>>{};
|
||||
final channelWithReads = <String, List<Read>>{};
|
||||
final channelWithMembers = <String, List<Member>>{};
|
||||
|
||||
final deleteMembers = deleteMembersByCids(
|
||||
cleanedChannelStates.map((it) => it.channel!.cid).toList(growable: false),
|
||||
);
|
||||
final users = <User>[];
|
||||
final reactions = <Reaction>[];
|
||||
final pinnedReactions = <Reaction>[];
|
||||
|
||||
for (final state in channelStates) {
|
||||
final channel = state.channel;
|
||||
if (channel != null) {
|
||||
channels.add(channel);
|
||||
|
||||
final cid = channel.cid;
|
||||
final reads = state.read;
|
||||
final members = state.members;
|
||||
final messages = state.messages;
|
||||
final pinnedMessages = state.pinnedMessages;
|
||||
|
||||
// Preparing deletion data
|
||||
membersToDelete.add(cid);
|
||||
reactionsToDelete.addAll(state.messages.map((it) => it.id));
|
||||
pinnedReactionsToDelete.addAll(state.pinnedMessages.map((it) => it.id));
|
||||
|
||||
// preparing addition data
|
||||
channelWithReads[cid] = reads;
|
||||
channelWithMembers[cid] = members;
|
||||
channelWithMessages[cid] = messages;
|
||||
channelWithPinnedMessages[cid] = pinnedMessages;
|
||||
|
||||
List<Reaction> expandReactions(Message message) {
|
||||
final own = message.ownReactions;
|
||||
final latest = message.latestReactions;
|
||||
return [
|
||||
if (own != null) ...own.where((r) => r.userId != null),
|
||||
if (latest != null) ...latest.where((r) => r.userId != null),
|
||||
];
|
||||
}
|
||||
|
||||
reactions.addAll(messages.expand(expandReactions));
|
||||
pinnedReactions.addAll(pinnedMessages.expand(expandReactions));
|
||||
|
||||
users.addAll([
|
||||
channel.createdBy,
|
||||
...reads.map((it) => it.user),
|
||||
...members.map((it) => it.user),
|
||||
...reactions.map((it) => it.user),
|
||||
...pinnedReactions.map((it) => it.user),
|
||||
].withNullifyer);
|
||||
}
|
||||
}
|
||||
|
||||
// Removing old members and reactions data as they may have
|
||||
// changes over the time.
|
||||
await Future.wait([
|
||||
deleteReactions,
|
||||
deleteMembers,
|
||||
deleteMembersByCids(membersToDelete),
|
||||
deleteReactionsByMessageId(reactionsToDelete),
|
||||
deletePinnedMessageReactionsByMessageId(pinnedReactionsToDelete),
|
||||
]);
|
||||
|
||||
final channels = cleanedChannelStates.map((it) => it.channel).withNullifyer;
|
||||
|
||||
final reactions = cleanedChannelStates
|
||||
.expand((it) => it.messages)
|
||||
.expand((it) => [
|
||||
if (it.ownReactions != null)
|
||||
...it.ownReactions!.where((r) => r.userId != null),
|
||||
if (it.latestReactions != null)
|
||||
...it.latestReactions!.where((r) => r.userId != null),
|
||||
])
|
||||
.withNullifyer;
|
||||
|
||||
final users = cleanedChannelStates
|
||||
.map((cs) => [
|
||||
cs.channel?.createdBy,
|
||||
...cs.messages
|
||||
.map((m) => [
|
||||
m.user,
|
||||
if (m.latestReactions != null)
|
||||
...m.latestReactions!.map((r) => r.user),
|
||||
if (m.ownReactions != null)
|
||||
...m.ownReactions!.map((r) => r.user),
|
||||
])
|
||||
.expand((v) => v),
|
||||
...cs.read.map((r) => r.user),
|
||||
...cs.members.map((m) => m.user),
|
||||
])
|
||||
.expand((it) => it)
|
||||
.withNullifyer;
|
||||
|
||||
final updateMessagesFuture = cleanedChannelStates.map((it) {
|
||||
final cid = it.channel!.cid;
|
||||
final messages = it.messages;
|
||||
return updateMessages(cid, messages.toList(growable: false));
|
||||
}).toList(growable: false);
|
||||
|
||||
final updatePinnedMessagesFuture = cleanedChannelStates.map((it) {
|
||||
final cid = it.channel!.cid;
|
||||
final messages = it.pinnedMessages;
|
||||
return updatePinnedMessages(cid, messages.toList(growable: false));
|
||||
}).toList(growable: false);
|
||||
|
||||
final updateReadsFuture = cleanedChannelStates.map((it) {
|
||||
final cid = it.channel!.cid;
|
||||
final reads = it.read;
|
||||
return updateReads(cid, reads.toList(growable: false));
|
||||
}).toList(growable: false);
|
||||
|
||||
final updateMembersFuture = cleanedChannelStates.map((it) {
|
||||
final cid = it.channel!.cid;
|
||||
final members = it.members;
|
||||
return updateMembers(cid, members.toList(growable: false));
|
||||
}).toList(growable: false);
|
||||
|
||||
// Updating first as does not depend on any other table.
|
||||
await Future.wait([
|
||||
...updateMessagesFuture,
|
||||
...updatePinnedMessagesFuture,
|
||||
...updateReadsFuture,
|
||||
...updateMembersFuture,
|
||||
updateUsers(users.toList(growable: false)),
|
||||
updateChannels(channels.toList(growable: false)),
|
||||
]);
|
||||
|
||||
// All has a foreign key relation with channels table.
|
||||
await Future.wait([
|
||||
bulkUpdateReads(channelWithReads),
|
||||
bulkUpdateMembers(channelWithMembers),
|
||||
bulkUpdateMessages(channelWithMessages),
|
||||
bulkUpdatePinnedMessages(channelWithPinnedMessages),
|
||||
]);
|
||||
|
||||
// Both has a foreign key relation with messages, pinnedMessages table.
|
||||
await Future.wait([
|
||||
updateReactions(reactions.toList(growable: false)),
|
||||
updatePinnedMessageReactions(
|
||||
pinnedReactions.toList(growable: false),
|
||||
),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -138,6 +138,20 @@ void main() {
|
||||
expect(filter.value, value);
|
||||
});
|
||||
|
||||
test('empty', () {
|
||||
final filter = Filter.empty();
|
||||
expect(filter.value, {});
|
||||
});
|
||||
|
||||
test('contains', () {
|
||||
const key = 'testKey';
|
||||
const values = 'testValue';
|
||||
final filter = Filter.contains(key, values);
|
||||
expect(filter.key, key);
|
||||
expect(filter.value, values);
|
||||
expect(filter.operator, FilterOperator.contains.rawValue);
|
||||
});
|
||||
|
||||
group('groupedOperator', () {
|
||||
final filter1 = Filter.equal('testKey', 'testValue');
|
||||
final filter2 = Filter.in_('testKey', const ['testValue']);
|
||||
|
||||
@@ -38,6 +38,11 @@ class TestPersistenceClient extends ChatPersistenceClient {
|
||||
Future<void> deleteReactionsByMessageId(List<String> messageIds) =>
|
||||
Future.value();
|
||||
|
||||
@override
|
||||
Future<void> deletePinnedMessageReactionsByMessageId(
|
||||
List<String> messageIds) =>
|
||||
Future.value();
|
||||
|
||||
@override
|
||||
Future<void> disconnect({bool flush = false}) => throw UnimplementedError();
|
||||
|
||||
@@ -101,26 +106,30 @@ class TestPersistenceClient extends ChatPersistenceClient {
|
||||
Future<void> updateLastSyncAt(DateTime lastSyncAt) =>
|
||||
throw UnimplementedError();
|
||||
|
||||
@override
|
||||
Future<void> updateMembers(String cid, List<Member> members) =>
|
||||
Future.value();
|
||||
|
||||
@override
|
||||
Future<void> updateMessages(String cid, List<Message> messages) =>
|
||||
Future.value();
|
||||
|
||||
@override
|
||||
Future<void> updatePinnedMessages(String cid, List<Message> messages) =>
|
||||
Future.value();
|
||||
|
||||
@override
|
||||
Future<void> updateReactions(List<Reaction> reactions) => Future.value();
|
||||
|
||||
@override
|
||||
Future<void> updateReads(String cid, List<Read> reads) => Future.value();
|
||||
Future<void> updatePinnedMessageReactions(List<Reaction> reactions) =>
|
||||
Future.value();
|
||||
|
||||
@override
|
||||
Future<void> updateUsers(List<User> users) => Future.value();
|
||||
|
||||
@override
|
||||
Future<void> bulkUpdateMembers(Map<String, List<Member>> members) =>
|
||||
Future.value();
|
||||
|
||||
@override
|
||||
Future<void> bulkUpdateMessages(Map<String, List<Message>> messages) =>
|
||||
Future.value();
|
||||
|
||||
@override
|
||||
Future<void> bulkUpdatePinnedMessages(Map<String, List<Message>> messages) =>
|
||||
Future.value();
|
||||
|
||||
@override
|
||||
Future<void> bulkUpdateReads(Map<String, List<Read>> reads) => Future.value();
|
||||
}
|
||||
|
||||
void main() {
|
||||
|
||||
Reference in New Issue
Block a user