stream-chat 8.14.5 → 8.16.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/src/thread.ts ADDED
@@ -0,0 +1,116 @@
1
+ import { StreamChat } from './client';
2
+ import {
3
+ DefaultGenerics,
4
+ ExtendableGenerics,
5
+ MessageResponse,
6
+ ThreadResponse,
7
+ ChannelResponse,
8
+ FormatMessageResponse,
9
+ ReactionResponse,
10
+ UserResponse,
11
+ } from './types';
12
+ import { addToMessageList, formatMessage } from './utils';
13
+
14
+ type ThreadReadStatus<StreamChatGenerics extends ExtendableGenerics = DefaultGenerics> = Record<
15
+ string,
16
+ {
17
+ last_read: Date;
18
+ last_read_message_id: string;
19
+ unread_messages: number;
20
+ user: UserResponse<StreamChatGenerics>;
21
+ }
22
+ >;
23
+
24
+ export class Thread<StreamChatGenerics extends ExtendableGenerics = DefaultGenerics> {
25
+ id: string;
26
+ latestReplies: FormatMessageResponse<StreamChatGenerics>[] = [];
27
+ participants: ThreadResponse['thread_participants'] = [];
28
+ message: FormatMessageResponse<StreamChatGenerics>;
29
+ channel: ChannelResponse<StreamChatGenerics>;
30
+ _channel: ReturnType<StreamChat<StreamChatGenerics>['channel']>;
31
+ replyCount = 0;
32
+ _client: StreamChat<StreamChatGenerics>;
33
+ read: ThreadReadStatus<StreamChatGenerics> = {};
34
+
35
+ constructor(client: StreamChat<StreamChatGenerics>, t: ThreadResponse<StreamChatGenerics>) {
36
+ this.id = t.parent_message.id;
37
+ this.message = formatMessage(t.parent_message);
38
+ this.latestReplies = t.latest_replies.map(formatMessage);
39
+ this.participants = t.thread_participants;
40
+ this.replyCount = t.reply_count;
41
+ this.channel = t.channel;
42
+ this._channel = client.channel(t.channel.type, t.channel.id);
43
+ this._client = client;
44
+ for (const r of t.read) {
45
+ this.read[r.user.id] = {
46
+ ...r,
47
+ last_read: new Date(r.last_read),
48
+ };
49
+ }
50
+ }
51
+
52
+ getClient(): StreamChat<StreamChatGenerics> {
53
+ return this._client;
54
+ }
55
+
56
+ addReply(message: MessageResponse<StreamChatGenerics>) {
57
+ this.latestReplies = addToMessageList(this.latestReplies, formatMessage(message));
58
+ }
59
+
60
+ updateReply(message: MessageResponse<StreamChatGenerics>) {
61
+ this.latestReplies = this.latestReplies.map((m) => {
62
+ if (m.id === message.id) {
63
+ return formatMessage(message);
64
+ }
65
+ return m;
66
+ });
67
+ }
68
+
69
+ updateMessageOrReplyIfExists(message: MessageResponse<StreamChatGenerics>) {
70
+ if (!message.parent_id && message.id !== this.message.id) {
71
+ return;
72
+ }
73
+
74
+ if (message.parent_id && message.parent_id !== this.message.id) {
75
+ return;
76
+ }
77
+
78
+ if (message.parent_id && message.parent_id === this.message.id) {
79
+ this.updateReply(message);
80
+ }
81
+
82
+ if (!message.parent_id && message.id === this.message.id) {
83
+ this.message = formatMessage(message);
84
+ }
85
+ }
86
+
87
+ addReaction(
88
+ reaction: ReactionResponse<StreamChatGenerics>,
89
+ message?: MessageResponse<StreamChatGenerics>,
90
+ enforce_unique?: boolean,
91
+ ) {
92
+ if (!message) return;
93
+
94
+ this.latestReplies = this.latestReplies.map((m) => {
95
+ if (m.id === message.id) {
96
+ return formatMessage(
97
+ this._channel.state.addReaction(reaction, message, enforce_unique) as MessageResponse<StreamChatGenerics>,
98
+ );
99
+ }
100
+ return m;
101
+ });
102
+ }
103
+
104
+ removeReaction(reaction: ReactionResponse<StreamChatGenerics>, message?: MessageResponse<StreamChatGenerics>) {
105
+ if (!message) return;
106
+
107
+ this.latestReplies = this.latestReplies.map((m) => {
108
+ if (m.id === message.id) {
109
+ return formatMessage(
110
+ this._channel.state.removeReaction(reaction, message) as MessageResponse<StreamChatGenerics>,
111
+ );
112
+ }
113
+ return m;
114
+ });
115
+ }
116
+ }
package/src/types.ts CHANGED
@@ -114,6 +114,10 @@ export type AppSettingsAPIResponse<StreamChatGenerics extends ExtendableGenerics
114
114
  campaign_enabled?: boolean;
115
115
  cdn_expiration_seconds?: number;
116
116
  custom_action_handler_url?: string;
117
+ datadog_info?: {
118
+ api_key: string;
119
+ site: string;
120
+ };
117
121
  disable_auth_checks?: boolean;
118
122
  disable_permissions_checks?: boolean;
119
123
  enforce_unique_usernames?: 'no' | 'app' | 'team';
@@ -247,6 +251,7 @@ export type BannedUsersResponse<StreamChatGenerics extends ExtendableGenerics =
247
251
 
248
252
  export type BlockListResponse = BlockList & {
249
253
  created_at?: string;
254
+ type?: string;
250
255
  updated_at?: string;
251
256
  };
252
257
 
@@ -473,6 +478,58 @@ export type GetMessageAPIResponse<
473
478
  StreamChatGenerics extends ExtendableGenerics = DefaultGenerics
474
479
  > = SendMessageAPIResponse<StreamChatGenerics>;
475
480
 
481
+ export type ThreadResponse<StreamChatGenerics extends ExtendableGenerics = DefaultGenerics> = {
482
+ channel: ChannelResponse<StreamChatGenerics>;
483
+ channel_cid: string;
484
+ created_at: string;
485
+ deleted_at: string;
486
+ latest_replies: MessageResponse<StreamChatGenerics>[];
487
+ parent_message: MessageResponse<StreamChatGenerics>;
488
+ parent_message_id: string;
489
+ read: {
490
+ last_read: string;
491
+ last_read_message_id: string;
492
+ unread_messages: number;
493
+ user: UserResponse<StreamChatGenerics>;
494
+ }[];
495
+ reply_count: number;
496
+ thread_participants: {
497
+ created_at: string;
498
+ user: UserResponse<StreamChatGenerics>;
499
+ }[];
500
+ title: string;
501
+ updated_at: string;
502
+ };
503
+
504
+ // TODO: Figure out a way to strongly type set and unset.
505
+ export type PartialThreadUpdate = {
506
+ set?: Partial<Record<string, unknown>>;
507
+ unset?: Partial<Record<string, unknown>>;
508
+ };
509
+
510
+ export type QueryThreadsOptions = {
511
+ limit?: number;
512
+ next?: string;
513
+ participant_limit?: number;
514
+ reply_limit?: number;
515
+ watch?: boolean;
516
+ };
517
+
518
+ export type QueryThreadsAPIResponse<StreamChatGenerics extends ExtendableGenerics = DefaultGenerics> = APIResponse & {
519
+ threads: ThreadResponse<StreamChatGenerics>[];
520
+ next?: string;
521
+ };
522
+
523
+ export type GetThreadOptions = {
524
+ participant_limit?: number;
525
+ reply_limit?: number;
526
+ watch?: boolean;
527
+ };
528
+
529
+ export type GetThreadAPIResponse<StreamChatGenerics extends ExtendableGenerics = DefaultGenerics> = APIResponse & {
530
+ thread: ThreadResponse<StreamChatGenerics>;
531
+ };
532
+
476
533
  export type GetMultipleMessagesAPIResponse<
477
534
  StreamChatGenerics extends ExtendableGenerics = DefaultGenerics
478
535
  > = APIResponse & {
@@ -505,7 +562,18 @@ export type GetUnreadCountAPIResponse = APIResponse & {
505
562
  last_read: string;
506
563
  unread_count: number;
507
564
  }[];
565
+ threads: {
566
+ last_read: string;
567
+ last_read_message_id: string;
568
+ parent_message_id: string;
569
+ unread_count: number;
570
+ }[];
508
571
  total_unread_count: number;
572
+ total_unread_threads_count: number;
573
+ };
574
+
575
+ export type GetUnreadCountBatchAPIResponse = APIResponse & {
576
+ counts_by_user: { [userId: string]: GetUnreadCountAPIResponse };
509
577
  };
510
578
 
511
579
  export type ListChannelResponse<StreamChatGenerics extends ExtendableGenerics = DefaultGenerics> = APIResponse & {
@@ -558,6 +626,7 @@ export type MessageResponseBase<
558
626
  };
559
627
  latest_reactions?: ReactionResponse<StreamChatGenerics>[];
560
628
  mentioned_users?: UserResponse<StreamChatGenerics>[];
629
+ moderation_details?: ModerationDetailsResponse;
561
630
  own_reactions?: ReactionResponse<StreamChatGenerics>[] | null;
562
631
  pin_expires?: string | null;
563
632
  pinned_at?: string | null;
@@ -571,6 +640,18 @@ export type MessageResponseBase<
571
640
  updated_at?: string;
572
641
  };
573
642
 
643
+ export type ModerationDetailsResponse = {
644
+ action: 'MESSAGE_RESPONSE_ACTION_BOUNCE' | (string & {});
645
+ error_msg: string;
646
+ harms: ModerationHarmResponse[];
647
+ original_text: string;
648
+ };
649
+
650
+ export type ModerationHarmResponse = {
651
+ name: string;
652
+ phrase_list_ids: number[];
653
+ };
654
+
574
655
  export type MuteResponse<StreamChatGenerics extends ExtendableGenerics = DefaultGenerics> = {
575
656
  user: UserResponse<StreamChatGenerics>;
576
657
  created_at?: string;
@@ -871,6 +952,7 @@ export type MarkChannelsReadOptions<StreamChatGenerics extends ExtendableGeneric
871
952
  export type MarkReadOptions<StreamChatGenerics extends ExtendableGenerics = DefaultGenerics> = {
872
953
  client_id?: string;
873
954
  connection_id?: string;
955
+ thread_id?: string;
874
956
  user?: UserResponse<StreamChatGenerics>;
875
957
  user_id?: string;
876
958
  };
@@ -1055,8 +1137,13 @@ export type Event<StreamChatGenerics extends ExtendableGenerics = DefaultGeneric
1055
1137
  cid?: string;
1056
1138
  clear_history?: boolean;
1057
1139
  connection_id?: string;
1140
+ // event creation timestamp, format Date ISO string
1058
1141
  created_at?: string;
1142
+ // id of the message that was marked as unread - all the following messages are considered unread. (notification.mark_unread)
1143
+ first_unread_message_id?: string;
1059
1144
  hard_delete?: boolean;
1145
+ // creation date of a message with last_read_message_id, formatted as Date ISO string
1146
+ last_read_at?: string;
1060
1147
  last_read_message_id?: string;
1061
1148
  mark_messages_deleted?: boolean;
1062
1149
  me?: OwnUserResponse<StreamChatGenerics>;
@@ -1072,9 +1159,15 @@ export type Event<StreamChatGenerics extends ExtendableGenerics = DefaultGeneric
1072
1159
  reaction?: ReactionResponse<StreamChatGenerics>;
1073
1160
  received_at?: string | Date;
1074
1161
  team?: string;
1162
+ thread?: ThreadResponse<StreamChatGenerics>;
1163
+ // @deprecated number of all unread messages across all current user's unread channels, equals unread_count
1075
1164
  total_unread_count?: number;
1165
+ // number of all current user's channels with at least one unread message including the channel in this event
1076
1166
  unread_channels?: number;
1167
+ // number of all unread messages across all current user's unread channels
1077
1168
  unread_count?: number;
1169
+ // number of unread messages in the channel from this event (notification.mark_unread)
1170
+ unread_messages?: number;
1078
1171
  user?: UserResponse<StreamChatGenerics>;
1079
1172
  user_id?: string;
1080
1173
  watcher_count?: number;
@@ -2390,22 +2483,43 @@ export type DeleteUserOptions = {
2390
2483
  user?: DeleteType;
2391
2484
  };
2392
2485
 
2486
+ export type SegmentType = 'channel' | 'user';
2487
+
2393
2488
  export type SegmentData = {
2394
2489
  description: string;
2395
2490
  filter: {};
2396
- name: string;
2397
- type: 'channel' | 'user';
2398
2491
  };
2399
2492
 
2400
2493
  export type Segment = {
2401
2494
  created_at: string;
2495
+ deleted_at: string;
2402
2496
  id: string;
2403
- in_use: boolean;
2497
+ locked: boolean;
2498
+ name: string;
2404
2499
  size: number;
2405
- status: 'computing' | 'ready';
2500
+ type: SegmentType;
2406
2501
  updated_at: string;
2407
2502
  } & SegmentData;
2408
2503
 
2504
+ export type UpdateSegmentData = {
2505
+ name: string;
2506
+ } & SegmentData;
2507
+
2508
+ export type SortParam = {
2509
+ field: string;
2510
+ direction?: AscDesc;
2511
+ };
2512
+
2513
+ export type Pager = {
2514
+ limit?: number;
2515
+ next?: string;
2516
+ prev?: string;
2517
+ };
2518
+
2519
+ export type QuerySegmentsOptions = {
2520
+ sort?: SortParam[];
2521
+ } & Pager;
2522
+
2409
2523
  export type CampaignSortField = {
2410
2524
  field: string;
2411
2525
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -2423,12 +2537,9 @@ export type CampaignQueryOptions = {
2423
2537
  };
2424
2538
 
2425
2539
  export type SegmentQueryOptions = CampaignQueryOptions;
2426
- export type RecipientQueryOptions = CampaignQueryOptions;
2427
2540
 
2428
2541
  // TODO: add better typing
2429
- export type SegmentFilters = {};
2430
2542
  export type CampaignFilters = {};
2431
- export type RecipientFilters = {};
2432
2543
 
2433
2544
  export type CampaignData = {
2434
2545
  attachments: Attachment[];
@@ -2469,20 +2580,7 @@ export type TestCampaignResponse = {
2469
2580
  results?: Record<string, string>;
2470
2581
  };
2471
2582
 
2472
- export type DeleteCampaignOptions = {
2473
- recipients?: boolean;
2474
- };
2475
-
2476
- export type Recipient = {
2477
- campaign_id: string;
2478
- channel_cid: string;
2479
- created_at: string;
2480
- status: 'pending' | 'sent' | 'failed';
2481
- updated_at: string;
2482
- details?: string;
2483
- message_id?: string;
2484
- receiver_id?: string;
2485
- };
2583
+ export type DeleteCampaignOptions = {};
2486
2584
 
2487
2585
  export type TaskStatus = {
2488
2586
  created_at: string;
package/src/utils.ts CHANGED
@@ -1,5 +1,14 @@
1
1
  import FormData from 'form-data';
2
- import { AscDesc, ExtendableGenerics, DefaultGenerics, OwnUserBase, OwnUserResponse, UserResponse } from './types';
2
+ import {
3
+ AscDesc,
4
+ ExtendableGenerics,
5
+ DefaultGenerics,
6
+ OwnUserBase,
7
+ OwnUserResponse,
8
+ UserResponse,
9
+ MessageResponse,
10
+ FormatMessageResponse,
11
+ } from './types';
3
12
  import { AxiosRequestConfig } from 'axios';
4
13
 
5
14
  /**
@@ -263,3 +272,94 @@ export const axiosParamsSerializer: AxiosRequestConfig['paramsSerializer'] = (pa
263
272
 
264
273
  return newParams.join('&');
265
274
  };
275
+
276
+ /**
277
+ * formatMessage - Takes the message object. Parses the dates, sets __html
278
+ * and sets the status to received if missing. Returns a message object
279
+ *
280
+ * @param {MessageResponse<StreamChatGenerics>} message a message object
281
+ *
282
+ */
283
+ export function formatMessage<StreamChatGenerics extends ExtendableGenerics = DefaultGenerics>(
284
+ message: MessageResponse<StreamChatGenerics>,
285
+ ): FormatMessageResponse<StreamChatGenerics> {
286
+ return {
287
+ ...message,
288
+ /**
289
+ * @deprecated please use `html`
290
+ */
291
+ __html: message.html,
292
+ // parse the date..
293
+ pinned_at: message.pinned_at ? new Date(message.pinned_at) : null,
294
+ created_at: message.created_at ? new Date(message.created_at) : new Date(),
295
+ updated_at: message.updated_at ? new Date(message.updated_at) : new Date(),
296
+ status: message.status || 'received',
297
+ };
298
+ }
299
+
300
+ export function addToMessageList<StreamChatGenerics extends ExtendableGenerics = DefaultGenerics>(
301
+ messages: Array<FormatMessageResponse<StreamChatGenerics>>,
302
+ message: FormatMessageResponse<StreamChatGenerics>,
303
+ timestampChanged = false,
304
+ sortBy: 'pinned_at' | 'created_at' = 'created_at',
305
+ addIfDoesNotExist = true,
306
+ ) {
307
+ const addMessageToList = addIfDoesNotExist || timestampChanged;
308
+ let messageArr = messages;
309
+
310
+ // if created_at has changed, message should be filtered and re-inserted in correct order
311
+ // slow op but usually this only happens for a message inserted to state before actual response with correct timestamp
312
+ if (timestampChanged) {
313
+ messageArr = messageArr.filter((msg) => !(msg.id && message.id === msg.id));
314
+ }
315
+
316
+ // Get array length after filtering
317
+ const messageArrayLength = messageArr.length;
318
+
319
+ // for empty list just concat and return unless it's an update or deletion
320
+ if (messageArrayLength === 0 && addMessageToList) {
321
+ return messageArr.concat(message);
322
+ } else if (messageArrayLength === 0) {
323
+ return [...messageArr];
324
+ }
325
+
326
+ const messageTime = (message[sortBy] as Date).getTime();
327
+ const messageIsNewest = (messageArr[messageArrayLength - 1][sortBy] as Date).getTime() < messageTime;
328
+
329
+ // if message is newer than last item in the list concat and return unless it's an update or deletion
330
+ if (messageIsNewest && addMessageToList) {
331
+ return messageArr.concat(message);
332
+ } else if (messageIsNewest) {
333
+ return [...messageArr];
334
+ }
335
+
336
+ // find the closest index to push the new message
337
+ let left = 0;
338
+ let middle = 0;
339
+ let right = messageArrayLength - 1;
340
+ while (left <= right) {
341
+ middle = Math.floor((right + left) / 2);
342
+ if ((messageArr[middle][sortBy] as Date).getTime() <= messageTime) left = middle + 1;
343
+ else right = middle - 1;
344
+ }
345
+
346
+ // message already exists and not filtered due to timestampChanged, update and return
347
+ if (!timestampChanged && message.id) {
348
+ if (messageArr[left] && message.id === messageArr[left].id) {
349
+ messageArr[left] = message;
350
+ return [...messageArr];
351
+ }
352
+
353
+ if (messageArr[left - 1] && message.id === messageArr[left - 1].id) {
354
+ messageArr[left - 1] = message;
355
+ return [...messageArr];
356
+ }
357
+ }
358
+
359
+ // Do not add updated or deleted messages to the list if they do not already exist
360
+ // or have a timestamp change.
361
+ if (addMessageToList) {
362
+ messageArr.splice(left, 0, message);
363
+ }
364
+ return [...messageArr];
365
+ }