stream-chat 6.2.0 → 6.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/browser.es.js +662 -196
- package/dist/browser.es.js.map +1 -1
- package/dist/browser.full-bundle.min.js +1 -1
- package/dist/browser.full-bundle.min.js.map +1 -1
- package/dist/browser.js +662 -196
- package/dist/browser.js.map +1 -1
- package/dist/index.es.js +662 -196
- package/dist/index.es.js.map +1 -1
- package/dist/index.js +662 -196
- package/dist/index.js.map +1 -1
- package/dist/types/channel.d.ts +6 -5
- package/dist/types/channel.d.ts.map +1 -1
- package/dist/types/channel_state.d.ts +36 -5
- package/dist/types/channel_state.d.ts.map +1 -1
- package/dist/types/client.d.ts +77 -46
- package/dist/types/client.d.ts.map +1 -1
- package/dist/types/token_manager.d.ts.map +1 -1
- package/dist/types/types.d.ts +73 -5
- package/dist/types/types.d.ts.map +1 -1
- package/package.json +3 -1
- package/src/channel.ts +24 -16
- package/src/channel_state.ts +240 -48
- package/src/client.ts +99 -48
- package/src/token_manager.ts +6 -2
- package/src/types.ts +128 -9
package/src/channel.ts
CHANGED
|
@@ -28,6 +28,7 @@ import {
|
|
|
28
28
|
Message,
|
|
29
29
|
MessageFilters,
|
|
30
30
|
MessageResponse,
|
|
31
|
+
MessageSetType,
|
|
31
32
|
MuteChannelAPIResponse,
|
|
32
33
|
PartialUpdateChannel,
|
|
33
34
|
PartialUpdateChannelAPIResponse,
|
|
@@ -668,12 +669,12 @@ export class Channel<StreamChatGenerics extends ExtendableGenerics = DefaultGene
|
|
|
668
669
|
lastMessage() {
|
|
669
670
|
// get last 5 messages, sort, return the latest
|
|
670
671
|
// get a slice of the last 5
|
|
671
|
-
let min = this.state.
|
|
672
|
+
let min = this.state.latestMessages.length - 5;
|
|
672
673
|
if (min < 0) {
|
|
673
674
|
min = 0;
|
|
674
675
|
}
|
|
675
|
-
const max = this.state.
|
|
676
|
-
const messageSlice = this.state.
|
|
676
|
+
const max = this.state.latestMessages.length + 1;
|
|
677
|
+
const messageSlice = this.state.latestMessages.slice(min, max);
|
|
677
678
|
|
|
678
679
|
// sort by pk desc
|
|
679
680
|
messageSlice.sort((a, b) => b.created_at.getTime() - a.created_at.getTime());
|
|
@@ -736,7 +737,7 @@ export class Channel<StreamChatGenerics extends ExtendableGenerics = DefaultGene
|
|
|
736
737
|
}
|
|
737
738
|
|
|
738
739
|
const combined = { ...defaultOptions, ...options };
|
|
739
|
-
const state = await this.query(combined);
|
|
740
|
+
const state = await this.query(combined, 'latest');
|
|
740
741
|
this.initialized = true;
|
|
741
742
|
this.data = state.channel;
|
|
742
743
|
|
|
@@ -767,7 +768,7 @@ export class Channel<StreamChatGenerics extends ExtendableGenerics = DefaultGene
|
|
|
767
768
|
* getReplies - List the message replies for a parent message
|
|
768
769
|
*
|
|
769
770
|
* @param {string} parent_id The message parent id, ie the top of the thread
|
|
770
|
-
* @param {
|
|
771
|
+
* @param {MessagePaginationOptions & { user?: UserResponse<StreamChatGenerics>; user_id?: string }} options Pagination params, ie {limit:10, id_lte: 10}
|
|
771
772
|
*
|
|
772
773
|
* @return {Promise<GetRepliesAPIResponse<StreamChatGenerics>>} A response with a list of messages
|
|
773
774
|
*/
|
|
@@ -883,8 +884,8 @@ export class Channel<StreamChatGenerics extends ExtendableGenerics = DefaultGene
|
|
|
883
884
|
if (!lastRead) return this.state.unreadCount;
|
|
884
885
|
|
|
885
886
|
let count = 0;
|
|
886
|
-
for (let i = 0; i < this.state.
|
|
887
|
-
const message = this.state.
|
|
887
|
+
for (let i = 0; i < this.state.latestMessages.length; i += 1) {
|
|
888
|
+
const message = this.state.latestMessages[i];
|
|
888
889
|
if (message.created_at > lastRead && this._countMessageAsUnread(message)) {
|
|
889
890
|
count++;
|
|
890
891
|
}
|
|
@@ -893,7 +894,7 @@ export class Channel<StreamChatGenerics extends ExtendableGenerics = DefaultGene
|
|
|
893
894
|
}
|
|
894
895
|
|
|
895
896
|
/**
|
|
896
|
-
*
|
|
897
|
+
* countUnreadMentions - Count the number of unread messages mentioning the current user
|
|
897
898
|
*
|
|
898
899
|
* @return {number} Unread mentions count
|
|
899
900
|
*/
|
|
@@ -902,8 +903,8 @@ export class Channel<StreamChatGenerics extends ExtendableGenerics = DefaultGene
|
|
|
902
903
|
const userID = this.getClient().userID;
|
|
903
904
|
|
|
904
905
|
let count = 0;
|
|
905
|
-
for (let i = 0; i < this.state.
|
|
906
|
-
const message = this.state.
|
|
906
|
+
for (let i = 0; i < this.state.latestMessages.length; i += 1) {
|
|
907
|
+
const message = this.state.latestMessages[i];
|
|
907
908
|
if (
|
|
908
909
|
this._countMessageAsUnread(message) &&
|
|
909
910
|
(!lastRead || message.created_at > lastRead) &&
|
|
@@ -926,17 +927,21 @@ export class Channel<StreamChatGenerics extends ExtendableGenerics = DefaultGene
|
|
|
926
927
|
state: false,
|
|
927
928
|
presence: false,
|
|
928
929
|
};
|
|
929
|
-
return await this.query(options);
|
|
930
|
+
return await this.query(options, 'latest');
|
|
930
931
|
};
|
|
931
932
|
|
|
932
933
|
/**
|
|
933
934
|
* query - Query the API, get messages, members or other channel fields
|
|
934
935
|
*
|
|
935
936
|
* @param {ChannelQueryOptions<StreamChatGenerics>} options The query options
|
|
937
|
+
* @param {MessageSetType} messageSetToAddToIfDoesNotExist It's possible to load disjunct sets of a channel's messages into state, use `current` to load the initial channel state or if you want to extend the currently displayed messages, use `latest` if you want to load/extend the latest messages, `new` is used for loading a specific message and it's surroundings
|
|
936
938
|
*
|
|
937
939
|
* @return {Promise<ChannelAPIResponse<StreamChatGenerics>>} Returns a query response
|
|
938
940
|
*/
|
|
939
|
-
async query(
|
|
941
|
+
async query(
|
|
942
|
+
options: ChannelQueryOptions<StreamChatGenerics>,
|
|
943
|
+
messageSetToAddToIfDoesNotExist: MessageSetType = 'current',
|
|
944
|
+
) {
|
|
940
945
|
// Make sure we wait for the connect promise if there is a pending one
|
|
941
946
|
await this.getClient().wsPromise;
|
|
942
947
|
|
|
@@ -977,7 +982,7 @@ export class Channel<StreamChatGenerics extends ExtendableGenerics = DefaultGene
|
|
|
977
982
|
this.getClient()._addChannelConfig(state);
|
|
978
983
|
|
|
979
984
|
// add any messages to our channel state
|
|
980
|
-
this._initializeState(state);
|
|
985
|
+
this._initializeState(state, messageSetToAddToIfDoesNotExist);
|
|
981
986
|
|
|
982
987
|
return state;
|
|
983
988
|
}
|
|
@@ -1340,7 +1345,10 @@ export class Channel<StreamChatGenerics extends ExtendableGenerics = DefaultGene
|
|
|
1340
1345
|
}
|
|
1341
1346
|
|
|
1342
1347
|
// eslint-disable-next-line sonarjs/cognitive-complexity
|
|
1343
|
-
_initializeState(
|
|
1348
|
+
_initializeState(
|
|
1349
|
+
state: ChannelAPIResponse<StreamChatGenerics>,
|
|
1350
|
+
messageSetToAddToIfDoesNotExist: MessageSetType = 'latest',
|
|
1351
|
+
) {
|
|
1344
1352
|
const { state: clientState, user, userID } = this.getClient();
|
|
1345
1353
|
|
|
1346
1354
|
// add the Users
|
|
@@ -1356,9 +1364,9 @@ export class Channel<StreamChatGenerics extends ExtendableGenerics = DefaultGene
|
|
|
1356
1364
|
|
|
1357
1365
|
const messages = state.messages || [];
|
|
1358
1366
|
if (!this.state.messages) {
|
|
1359
|
-
this.state.
|
|
1367
|
+
this.state.initMessages();
|
|
1360
1368
|
}
|
|
1361
|
-
this.state.addMessagesSorted(messages, false, true);
|
|
1369
|
+
this.state.addMessagesSorted(messages, false, true, true, messageSetToAddToIfDoesNotExist);
|
|
1362
1370
|
if (!this.state.pinnedMessages) {
|
|
1363
1371
|
this.state.pinnedMessages = [];
|
|
1364
1372
|
}
|
package/src/channel_state.ts
CHANGED
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
Event,
|
|
7
7
|
ExtendableGenerics,
|
|
8
8
|
DefaultGenerics,
|
|
9
|
+
MessageSetType,
|
|
9
10
|
MessageResponse,
|
|
10
11
|
ReactionResponse,
|
|
11
12
|
UserResponse,
|
|
@@ -24,7 +25,6 @@ export class ChannelState<StreamChatGenerics extends ExtendableGenerics = Defaul
|
|
|
24
25
|
watcher_count: number;
|
|
25
26
|
typing: Record<string, Event<StreamChatGenerics>>;
|
|
26
27
|
read: ChannelReadStatus<StreamChatGenerics>;
|
|
27
|
-
messages: Array<ReturnType<ChannelState<StreamChatGenerics>['formatMessage']>>;
|
|
28
28
|
pinnedMessages: Array<ReturnType<ChannelState<StreamChatGenerics>['formatMessage']>>;
|
|
29
29
|
threads: Record<string, Array<ReturnType<ChannelState<StreamChatGenerics>['formatMessage']>>>;
|
|
30
30
|
mutedUsers: Array<UserResponse<StreamChatGenerics>>;
|
|
@@ -40,12 +40,23 @@ export class ChannelState<StreamChatGenerics extends ExtendableGenerics = Defaul
|
|
|
40
40
|
* be pushed on to message list.
|
|
41
41
|
*/
|
|
42
42
|
isUpToDate: boolean;
|
|
43
|
+
/**
|
|
44
|
+
* Disjoint lists of messages
|
|
45
|
+
* Users can jump in the message list (with searching) and this can result in disjoint lists of messages
|
|
46
|
+
* The state manages these lists and merges them when lists overlap
|
|
47
|
+
* The messages array contains the currently active set
|
|
48
|
+
*/
|
|
49
|
+
messageSets: {
|
|
50
|
+
isCurrent: boolean;
|
|
51
|
+
isLatest: boolean;
|
|
52
|
+
messages: Array<ReturnType<ChannelState<StreamChatGenerics>['formatMessage']>>;
|
|
53
|
+
}[] = [];
|
|
43
54
|
constructor(channel: Channel<StreamChatGenerics>) {
|
|
44
55
|
this._channel = channel;
|
|
45
56
|
this.watcher_count = 0;
|
|
46
57
|
this.typing = {};
|
|
47
58
|
this.read = {};
|
|
48
|
-
this.
|
|
59
|
+
this.initMessages();
|
|
49
60
|
this.pinnedMessages = [];
|
|
50
61
|
this.threads = {};
|
|
51
62
|
// a list of users to hide messages from
|
|
@@ -64,20 +75,49 @@ export class ChannelState<StreamChatGenerics extends ExtendableGenerics = Defaul
|
|
|
64
75
|
this.last_message_at = channel?.state?.last_message_at != null ? new Date(channel.state.last_message_at) : null;
|
|
65
76
|
}
|
|
66
77
|
|
|
78
|
+
get messages() {
|
|
79
|
+
return this.messageSets.find((s) => s.isCurrent)?.messages || [];
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
set messages(messages: Array<ReturnType<ChannelState<StreamChatGenerics>['formatMessage']>>) {
|
|
83
|
+
const index = this.messageSets.findIndex((s) => s.isCurrent);
|
|
84
|
+
this.messageSets[index].messages = messages;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* The list of latest messages
|
|
89
|
+
* The messages array not always contains the latest messages (for example if a user searched for an earlier message, that is in a different message set)
|
|
90
|
+
*/
|
|
91
|
+
get latestMessages() {
|
|
92
|
+
return this.messageSets.find((s) => s.isLatest)?.messages || [];
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
set latestMessages(messages: Array<ReturnType<ChannelState<StreamChatGenerics>['formatMessage']>>) {
|
|
96
|
+
const index = this.messageSets.findIndex((s) => s.isLatest);
|
|
97
|
+
this.messageSets[index].messages = messages;
|
|
98
|
+
}
|
|
99
|
+
|
|
67
100
|
/**
|
|
68
101
|
* addMessageSorted - Add a message to the state
|
|
69
102
|
*
|
|
70
103
|
* @param {MessageResponse<StreamChatGenerics>} newMessage A new message
|
|
71
104
|
* @param {boolean} timestampChanged Whether updating a message with changed created_at value.
|
|
72
105
|
* @param {boolean} addIfDoesNotExist Add message if it is not in the list, used to prevent out of order updated messages from being added.
|
|
73
|
-
*
|
|
106
|
+
* @param {MessageSetType} messageSetToAddToIfDoesNotExist Which message set to add to if message is not in the list (only used if addIfDoesNotExist is true)
|
|
74
107
|
*/
|
|
75
108
|
addMessageSorted(
|
|
76
109
|
newMessage: MessageResponse<StreamChatGenerics>,
|
|
77
110
|
timestampChanged = false,
|
|
78
111
|
addIfDoesNotExist = true,
|
|
112
|
+
messageSetToAddToIfDoesNotExist: MessageSetType = 'latest',
|
|
79
113
|
) {
|
|
80
|
-
return this.addMessagesSorted(
|
|
114
|
+
return this.addMessagesSorted(
|
|
115
|
+
[newMessage],
|
|
116
|
+
timestampChanged,
|
|
117
|
+
false,
|
|
118
|
+
addIfDoesNotExist,
|
|
119
|
+
messageSetToAddToIfDoesNotExist,
|
|
120
|
+
);
|
|
81
121
|
}
|
|
82
122
|
|
|
83
123
|
/**
|
|
@@ -109,6 +149,7 @@ export class ChannelState<StreamChatGenerics extends ExtendableGenerics = Defaul
|
|
|
109
149
|
* @param {boolean} timestampChanged Whether updating messages with changed created_at value.
|
|
110
150
|
* @param {boolean} initializing Whether channel is being initialized.
|
|
111
151
|
* @param {boolean} addIfDoesNotExist Add message if it is not in the list, used to prevent out of order updated messages from being added.
|
|
152
|
+
* @param {MessageSetType} messageSetToAddToIfDoesNotExist Which message set to add to if messages are not in the list (only used if addIfDoesNotExist is true)
|
|
112
153
|
*
|
|
113
154
|
*/
|
|
114
155
|
addMessagesSorted(
|
|
@@ -116,46 +157,62 @@ export class ChannelState<StreamChatGenerics extends ExtendableGenerics = Defaul
|
|
|
116
157
|
timestampChanged = false,
|
|
117
158
|
initializing = false,
|
|
118
159
|
addIfDoesNotExist = true,
|
|
160
|
+
messageSetToAddToIfDoesNotExist: MessageSetType = 'current',
|
|
119
161
|
) {
|
|
120
|
-
|
|
121
|
-
|
|
162
|
+
const { messagesToAdd, targetMessageSetIndex } = this.findTargetMessageSet(
|
|
163
|
+
newMessages,
|
|
164
|
+
addIfDoesNotExist,
|
|
165
|
+
messageSetToAddToIfDoesNotExist,
|
|
166
|
+
);
|
|
167
|
+
|
|
168
|
+
for (let i = 0; i < messagesToAdd.length; i += 1) {
|
|
169
|
+
const isFromShadowBannedUser = messagesToAdd[i].shadowed;
|
|
122
170
|
if (isFromShadowBannedUser) {
|
|
123
171
|
continue;
|
|
124
172
|
}
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
this.
|
|
134
|
-
|
|
173
|
+
// If message is already formatted we can skip the tasks below
|
|
174
|
+
// This will be true for messages that are already present at the state -> this happens when we perform merging of message sets
|
|
175
|
+
// This will be also true for message previews used by some SDKs
|
|
176
|
+
const isMessageFormatted = messagesToAdd[i].created_at instanceof Date;
|
|
177
|
+
let message: ReturnType<ChannelState<StreamChatGenerics>['formatMessage']>;
|
|
178
|
+
if (isMessageFormatted) {
|
|
179
|
+
message = messagesToAdd[i] as ReturnType<ChannelState<StreamChatGenerics>['formatMessage']>;
|
|
180
|
+
} else {
|
|
181
|
+
message = this.formatMessage(messagesToAdd[i] as MessageResponse<StreamChatGenerics>);
|
|
182
|
+
|
|
183
|
+
if (message.user && this._channel?.cid) {
|
|
184
|
+
/**
|
|
185
|
+
* Store the reference to user for this channel, so that when we have to
|
|
186
|
+
* handle updates to user, we can use the reference map, to determine which
|
|
187
|
+
* channels need to be updated with updated user object.
|
|
188
|
+
*/
|
|
189
|
+
this._channel.getClient().state.updateUserReference(message.user, this._channel.cid);
|
|
190
|
+
}
|
|
135
191
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
192
|
+
if (initializing && message.id && this.threads[message.id]) {
|
|
193
|
+
// If we are initializing the state of channel (e.g., in case of connection recovery),
|
|
194
|
+
// then in that case we remove thread related to this message from threads object.
|
|
195
|
+
// This way we can ensure that we don't have any stale data in thread object
|
|
196
|
+
// and consumer can refetch the replies.
|
|
197
|
+
delete this.threads[message.id];
|
|
198
|
+
}
|
|
143
199
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
200
|
+
if (!this.last_message_at) {
|
|
201
|
+
this.last_message_at = new Date(message.created_at.getTime());
|
|
202
|
+
}
|
|
147
203
|
|
|
148
|
-
|
|
149
|
-
|
|
204
|
+
if (message.created_at.getTime() > this.last_message_at.getTime()) {
|
|
205
|
+
this.last_message_at = new Date(message.created_at.getTime());
|
|
206
|
+
}
|
|
150
207
|
}
|
|
151
208
|
|
|
152
209
|
// update or append the messages...
|
|
153
210
|
const parentID = message.parent_id;
|
|
154
211
|
|
|
155
|
-
// add to the
|
|
156
|
-
if (!parentID || message.show_in_channel) {
|
|
157
|
-
this.messages = this._addToMessageList(
|
|
158
|
-
this.messages,
|
|
212
|
+
// add to the given message set
|
|
213
|
+
if ((!parentID || message.show_in_channel) && targetMessageSetIndex !== -1) {
|
|
214
|
+
this.messageSets[targetMessageSetIndex].messages = this._addToMessageList(
|
|
215
|
+
this.messageSets[targetMessageSetIndex].messages,
|
|
159
216
|
message,
|
|
160
217
|
timestampChanged,
|
|
161
218
|
'created_at',
|
|
@@ -286,12 +343,14 @@ export class ChannelState<StreamChatGenerics extends ExtendableGenerics = Defaul
|
|
|
286
343
|
updated_at: m.updated_at?.toString(),
|
|
287
344
|
} as unknown) as MessageResponse<StreamChatGenerics>);
|
|
288
345
|
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
346
|
+
this.messageSets.forEach((set) => {
|
|
347
|
+
const updatedMessages = set.messages
|
|
348
|
+
.filter((msg) => msg.quoted_message_id === message.id)
|
|
349
|
+
.map(parseMessage)
|
|
350
|
+
.map((msg) => ({ ...msg, quoted_message: { ...message, attachments: [] } }));
|
|
293
351
|
|
|
294
|
-
|
|
352
|
+
this.addMessagesSorted(updatedMessages, true);
|
|
353
|
+
});
|
|
295
354
|
}
|
|
296
355
|
|
|
297
356
|
/**
|
|
@@ -322,9 +381,14 @@ export class ChannelState<StreamChatGenerics extends ExtendableGenerics = Defaul
|
|
|
322
381
|
}
|
|
323
382
|
|
|
324
383
|
if ((!show_in_channel && !parent_id) || show_in_channel) {
|
|
325
|
-
const
|
|
326
|
-
if (
|
|
327
|
-
|
|
384
|
+
const messageSetIndex = this.findMessageSetIndex(message);
|
|
385
|
+
if (messageSetIndex !== -1) {
|
|
386
|
+
const msgIndex = this.messageSets[messageSetIndex].messages.findIndex((msg) => msg.id === message.id);
|
|
387
|
+
if (msgIndex !== -1) {
|
|
388
|
+
this.messageSets[messageSetIndex].messages[msgIndex] = updateFunc(
|
|
389
|
+
this.messageSets[messageSetIndex].messages[msgIndex],
|
|
390
|
+
);
|
|
391
|
+
}
|
|
328
392
|
}
|
|
329
393
|
}
|
|
330
394
|
|
|
@@ -442,9 +506,15 @@ export class ChannelState<StreamChatGenerics extends ExtendableGenerics = Defaul
|
|
|
442
506
|
this.threads[messageToRemove.parent_id] = threadMessages;
|
|
443
507
|
isRemoved = removed;
|
|
444
508
|
} else {
|
|
445
|
-
const
|
|
446
|
-
|
|
447
|
-
|
|
509
|
+
const messageSetIndex = this.findMessageSetIndex(messageToRemove);
|
|
510
|
+
if (messageSetIndex !== -1) {
|
|
511
|
+
const { removed, result: messages } = this.removeMessageFromArray(
|
|
512
|
+
this.messageSets[messageSetIndex].messages,
|
|
513
|
+
messageToRemove,
|
|
514
|
+
);
|
|
515
|
+
this.messageSets[messageSetIndex].messages = messages;
|
|
516
|
+
isRemoved = removed;
|
|
517
|
+
}
|
|
448
518
|
}
|
|
449
519
|
|
|
450
520
|
return isRemoved;
|
|
@@ -477,7 +547,7 @@ export class ChannelState<StreamChatGenerics extends ExtendableGenerics = Defaul
|
|
|
477
547
|
}
|
|
478
548
|
};
|
|
479
549
|
|
|
480
|
-
_updateUserMessages(
|
|
550
|
+
this.messageSets.forEach((set) => _updateUserMessages(set.messages, user));
|
|
481
551
|
|
|
482
552
|
for (const parentId in this.threads) {
|
|
483
553
|
_updateUserMessages(this.threads[parentId], user);
|
|
@@ -535,7 +605,7 @@ export class ChannelState<StreamChatGenerics extends ExtendableGenerics = Defaul
|
|
|
535
605
|
}
|
|
536
606
|
};
|
|
537
607
|
|
|
538
|
-
_deleteUserMessages(
|
|
608
|
+
this.messageSets.forEach((set) => _deleteUserMessages(set.messages, user, hardDelete));
|
|
539
609
|
|
|
540
610
|
for (const parentId in this.threads) {
|
|
541
611
|
_deleteUserMessages(this.threads[parentId], user, hardDelete);
|
|
@@ -549,9 +619,9 @@ export class ChannelState<StreamChatGenerics extends ExtendableGenerics = Defaul
|
|
|
549
619
|
*
|
|
550
620
|
*/
|
|
551
621
|
filterErrorMessages() {
|
|
552
|
-
const filteredMessages = this.
|
|
622
|
+
const filteredMessages = this.latestMessages.filter((message) => message.type !== 'error');
|
|
553
623
|
|
|
554
|
-
this.
|
|
624
|
+
this.latestMessages = filteredMessages;
|
|
555
625
|
}
|
|
556
626
|
|
|
557
627
|
/**
|
|
@@ -577,7 +647,129 @@ export class ChannelState<StreamChatGenerics extends ExtendableGenerics = Defaul
|
|
|
577
647
|
}
|
|
578
648
|
|
|
579
649
|
clearMessages() {
|
|
580
|
-
this.
|
|
650
|
+
this.initMessages();
|
|
581
651
|
this.pinnedMessages = [];
|
|
582
652
|
}
|
|
653
|
+
|
|
654
|
+
initMessages() {
|
|
655
|
+
this.messageSets = [{ messages: [], isLatest: true, isCurrent: true }];
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
/**
|
|
659
|
+
* loadMessageIntoState - Loads a given message (and messages around it) into the state
|
|
660
|
+
*
|
|
661
|
+
* @param {string} messageId The id of the message, or 'latest' to indicate switching to the latest messages
|
|
662
|
+
* @param {string} parentMessageId The id of the parent message, if we want load a thread reply
|
|
663
|
+
*/
|
|
664
|
+
async loadMessageIntoState(messageId: string | 'latest', parentMessageId?: string) {
|
|
665
|
+
let messageSetIndex: number;
|
|
666
|
+
let switchedToMessageSet = false;
|
|
667
|
+
let loadedMessageThread = false;
|
|
668
|
+
const messageIdToFind = parentMessageId || messageId;
|
|
669
|
+
if (messageId === 'latest') {
|
|
670
|
+
if (this.messages === this.latestMessages) {
|
|
671
|
+
return;
|
|
672
|
+
}
|
|
673
|
+
messageSetIndex = this.messageSets.findIndex((s) => s.isLatest);
|
|
674
|
+
} else {
|
|
675
|
+
messageSetIndex = this.findMessageSetIndex({ id: messageIdToFind });
|
|
676
|
+
}
|
|
677
|
+
if (messageSetIndex !== -1) {
|
|
678
|
+
this.switchToMessageSet(messageSetIndex);
|
|
679
|
+
switchedToMessageSet = true;
|
|
680
|
+
}
|
|
681
|
+
loadedMessageThread = !parentMessageId || !!this.threads[parentMessageId]?.find((m) => m.id === messageId);
|
|
682
|
+
if (switchedToMessageSet && loadedMessageThread) {
|
|
683
|
+
return;
|
|
684
|
+
}
|
|
685
|
+
if (!switchedToMessageSet) {
|
|
686
|
+
await this._channel.query({ messages: { id_around: messageIdToFind, limit: 25 } }, 'new');
|
|
687
|
+
}
|
|
688
|
+
if (!loadedMessageThread && parentMessageId) {
|
|
689
|
+
await this._channel.getReplies(parentMessageId, { id_around: messageId, limit: 25 });
|
|
690
|
+
}
|
|
691
|
+
messageSetIndex = this.findMessageSetIndex({ id: messageIdToFind });
|
|
692
|
+
if (messageSetIndex !== -1) {
|
|
693
|
+
this.switchToMessageSet(messageSetIndex);
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
private switchToMessageSet(index: number) {
|
|
698
|
+
const currentMessages = this.messageSets.find((s) => s.isCurrent);
|
|
699
|
+
if (!currentMessages) {
|
|
700
|
+
return;
|
|
701
|
+
}
|
|
702
|
+
currentMessages.isCurrent = false;
|
|
703
|
+
this.messageSets[index].isCurrent = true;
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
private areMessageSetsOverlap(messages1: Array<{ id: string }>, messages2: Array<{ id: string }>) {
|
|
707
|
+
return messages1.some((m1) => messages2.find((m2) => m1.id === m2.id));
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
private findMessageSetIndex(message: { id?: string }) {
|
|
711
|
+
return this.messageSets.findIndex((set) => !!set.messages.find((m) => m.id === message.id));
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
private findTargetMessageSet(
|
|
715
|
+
newMessages: MessageResponse<StreamChatGenerics>[],
|
|
716
|
+
addIfDoesNotExist = true,
|
|
717
|
+
messageSetToAddToIfDoesNotExist: MessageSetType = 'current',
|
|
718
|
+
) {
|
|
719
|
+
let messagesToAdd: (
|
|
720
|
+
| MessageResponse<StreamChatGenerics>
|
|
721
|
+
| ReturnType<ChannelState<StreamChatGenerics>['formatMessage']>
|
|
722
|
+
)[] = newMessages;
|
|
723
|
+
let targetMessageSetIndex!: number;
|
|
724
|
+
if (addIfDoesNotExist) {
|
|
725
|
+
const overlappingMessageSetIndices = this.messageSets
|
|
726
|
+
.map((_, i) => i)
|
|
727
|
+
.filter((i) => this.areMessageSetsOverlap(this.messageSets[i].messages, newMessages));
|
|
728
|
+
switch (messageSetToAddToIfDoesNotExist) {
|
|
729
|
+
case 'new':
|
|
730
|
+
if (overlappingMessageSetIndices.length > 0) {
|
|
731
|
+
targetMessageSetIndex = overlappingMessageSetIndices[0];
|
|
732
|
+
// No new message set is created if newMessages only contains thread replies
|
|
733
|
+
} else if (newMessages.some((m) => !m.parent_id)) {
|
|
734
|
+
this.messageSets.push({ messages: [], isCurrent: false, isLatest: false });
|
|
735
|
+
targetMessageSetIndex = this.messageSets.length - 1;
|
|
736
|
+
}
|
|
737
|
+
break;
|
|
738
|
+
case 'current':
|
|
739
|
+
targetMessageSetIndex = this.messageSets.findIndex((s) => s.isCurrent);
|
|
740
|
+
break;
|
|
741
|
+
case 'latest':
|
|
742
|
+
targetMessageSetIndex = this.messageSets.findIndex((s) => s.isLatest);
|
|
743
|
+
break;
|
|
744
|
+
default:
|
|
745
|
+
targetMessageSetIndex = -1;
|
|
746
|
+
}
|
|
747
|
+
// when merging the target set will be the first one from the overlapping message sets
|
|
748
|
+
const mergeTargetMessageSetIndex = overlappingMessageSetIndices.splice(0, 1)[0];
|
|
749
|
+
const mergeSourceMessageSetIndices = [...overlappingMessageSetIndices];
|
|
750
|
+
if (mergeTargetMessageSetIndex !== undefined && mergeTargetMessageSetIndex !== targetMessageSetIndex) {
|
|
751
|
+
mergeSourceMessageSetIndices.push(targetMessageSetIndex);
|
|
752
|
+
}
|
|
753
|
+
// merge message sets
|
|
754
|
+
if (mergeSourceMessageSetIndices.length > 0) {
|
|
755
|
+
const target = this.messageSets[mergeTargetMessageSetIndex];
|
|
756
|
+
const sources = this.messageSets.filter((_, i) => mergeSourceMessageSetIndices.indexOf(i) !== -1);
|
|
757
|
+
sources.forEach((messageSet) => {
|
|
758
|
+
target.isLatest = target.isLatest || messageSet.isLatest;
|
|
759
|
+
target.isCurrent = target.isCurrent || messageSet.isCurrent;
|
|
760
|
+
messagesToAdd = [...messagesToAdd, ...messageSet.messages];
|
|
761
|
+
});
|
|
762
|
+
sources.forEach((s) => this.messageSets.splice(this.messageSets.indexOf(s), 1));
|
|
763
|
+
const overlappingMessageSetIndex = this.messageSets.findIndex((s) =>
|
|
764
|
+
this.areMessageSetsOverlap(s.messages, newMessages),
|
|
765
|
+
);
|
|
766
|
+
targetMessageSetIndex = overlappingMessageSetIndex;
|
|
767
|
+
}
|
|
768
|
+
} else {
|
|
769
|
+
// assumes that all new messages belong to the same set
|
|
770
|
+
targetMessageSetIndex = this.findMessageSetIndex(newMessages[0]);
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
return { targetMessageSetIndex, messagesToAdd };
|
|
774
|
+
}
|
|
583
775
|
}
|