stream-chat 8.38.0 → 8.40.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.
Files changed (42) hide show
  1. package/README.md +15 -2
  2. package/dist/browser.es.js +1667 -372
  3. package/dist/browser.es.js.map +1 -1
  4. package/dist/browser.full-bundle.min.js +1 -1
  5. package/dist/browser.full-bundle.min.js.map +1 -1
  6. package/dist/browser.js +1668 -371
  7. package/dist/browser.js.map +1 -1
  8. package/dist/index.es.js +1667 -372
  9. package/dist/index.es.js.map +1 -1
  10. package/dist/index.js +1668 -371
  11. package/dist/index.js.map +1 -1
  12. package/dist/types/channel.d.ts +6 -8
  13. package/dist/types/channel.d.ts.map +1 -1
  14. package/dist/types/channel_state.d.ts +14 -22
  15. package/dist/types/channel_state.d.ts.map +1 -1
  16. package/dist/types/client.d.ts +3 -1
  17. package/dist/types/client.d.ts.map +1 -1
  18. package/dist/types/constants.d.ts +7 -0
  19. package/dist/types/constants.d.ts.map +1 -0
  20. package/dist/types/index.d.ts +2 -0
  21. package/dist/types/index.d.ts.map +1 -1
  22. package/dist/types/store.d.ts +14 -0
  23. package/dist/types/store.d.ts.map +1 -0
  24. package/dist/types/thread.d.ts +93 -29
  25. package/dist/types/thread.d.ts.map +1 -1
  26. package/dist/types/thread_manager.d.ts +51 -0
  27. package/dist/types/thread_manager.d.ts.map +1 -0
  28. package/dist/types/types.d.ts +35 -18
  29. package/dist/types/types.d.ts.map +1 -1
  30. package/dist/types/utils.d.ts +48 -7
  31. package/dist/types/utils.d.ts.map +1 -1
  32. package/package.json +7 -6
  33. package/src/channel.ts +28 -13
  34. package/src/channel_state.ts +30 -27
  35. package/src/client.ts +182 -104
  36. package/src/constants.ts +4 -0
  37. package/src/index.ts +2 -0
  38. package/src/store.ts +57 -0
  39. package/src/thread.ts +470 -107
  40. package/src/thread_manager.ts +297 -0
  41. package/src/types.ts +34 -19
  42. package/src/utils.ts +362 -43
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "stream-chat",
3
- "version": "8.38.0",
3
+ "version": "8.40.0",
4
4
  "description": "JS SDK for the Stream Chat API",
5
5
  "author": "GetStream",
6
6
  "homepage": "https://getstream.io/chat/",
@@ -43,7 +43,7 @@
43
43
  "form-data": "^4.0.0",
44
44
  "isomorphic-ws": "^4.0.1",
45
45
  "jsonwebtoken": "~9.0.0",
46
- "ws": "^7.4.4"
46
+ "ws": "^7.5.10"
47
47
  },
48
48
  "devDependencies": {
49
49
  "@babel/cli": "^7.16.0",
@@ -95,7 +95,7 @@
95
95
  "eslint-plugin-typescript-sort-keys": "1.5.0",
96
96
  "husky": "^4.3.8",
97
97
  "lint-staged": "^15.2.2",
98
- "mocha": "^9.1.3",
98
+ "mocha": "^10.7.0",
99
99
  "nyc": "^15.1.0",
100
100
  "prettier": "^2.2.1",
101
101
  "rollup": "^2.41.0",
@@ -103,7 +103,7 @@
103
103
  "rollup-plugin-terser": "^7.0.2",
104
104
  "sinon": "^12.0.1",
105
105
  "standard-version": "^9.3.2",
106
- "typescript": "^4.2.3",
106
+ "typescript": "4.2.3",
107
107
  "uuid": "^8.3.2"
108
108
  },
109
109
  "scripts": {
@@ -118,7 +118,7 @@
118
118
  "test-types": "node test/typescript/index.js && tsc --esModuleInterop true --noEmit true --strictNullChecks true --noImplicitAny true --strict true test/typescript/*.ts",
119
119
  "eslint": "eslint '**/*.{js,md,ts}' --max-warnings 0 --ignore-path ./.eslintignore",
120
120
  "eslint-fix": "npx eslint --fix '**/*.{js,md,ts}' --max-warnings 0 --ignore-path ./.eslintignore",
121
- "test-unit": "NODE_ENV=test mocha --exit --bail --timeout 20000 --require ./babel-register test/unit/*.js",
121
+ "test-unit": "NODE_ENV=test mocha --exit --bail --timeout 20000 --require ./babel-register test/unit/*.{js,test.ts}",
122
122
  "test-coverage": "nyc yarn test-unit",
123
123
  "test": "yarn test-unit",
124
124
  "testwatch": "NODE_ENV=test nodemon ./node_modules/.bin/mocha --timeout 20000 --require test-entry.js test/test.js",
@@ -132,5 +132,6 @@
132
132
  },
133
133
  "engines": {
134
134
  "node": ">=16"
135
- }
135
+ },
136
+ "packageManager": "yarn@1.22.21+sha1.1959a18351b811cdeedbd484a8f86c3cc3bbaf72"
136
137
  }
package/src/channel.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { ChannelState } from './channel_state';
2
- import { logChatPromiseExecution, normalizeQuerySort } from './utils';
2
+ import { logChatPromiseExecution, messageSetPagination, normalizeQuerySort } from './utils';
3
3
  import { StreamChat } from './client';
4
4
  import {
5
5
  APIResponse,
@@ -58,6 +58,7 @@ import {
58
58
  AscDesc,
59
59
  } from './types';
60
60
  import { Role } from './permissions';
61
+ import { DEFAULT_QUERY_CHANNEL_MESSAGE_LIST_PAGE_SIZE } from './constants';
61
62
 
62
63
  /**
63
64
  * Channel - The Channel class manages it's own state.
@@ -319,7 +320,7 @@ export class Channel<StreamChatGenerics extends ExtendableGenerics = DefaultGene
319
320
  throw Error(`Reaction object is missing`);
320
321
  }
321
322
  return await this.getClient().post<ReactionAPIResponse<StreamChatGenerics>>(
322
- this.getClient().baseURL + `/messages/${messageID}/reaction`,
323
+ this.getClient().baseURL + `/messages/${encodeURIComponent(messageID)}/reaction`,
323
324
  {
324
325
  reaction,
325
326
  ...options,
@@ -342,7 +343,9 @@ export class Channel<StreamChatGenerics extends ExtendableGenerics = DefaultGene
342
343
  throw Error('Deleting a reaction requires specifying both the message and reaction type');
343
344
  }
344
345
 
345
- const url = this.getClient().baseURL + `/messages/${messageID}/reaction/${reactionType}`;
346
+ const url =
347
+ this.getClient().baseURL +
348
+ `/messages/${encodeURIComponent(messageID)}/reaction/${encodeURIComponent(reactionType)}`;
346
349
  //provided when server side request
347
350
  if (user_id) {
348
351
  return this.getClient().delete<ReactionAPIResponse<StreamChatGenerics>>(url, { user_id });
@@ -639,7 +642,7 @@ export class Channel<StreamChatGenerics extends ExtendableGenerics = DefaultGene
639
642
  throw Error(`Message id is missing`);
640
643
  }
641
644
  return this.getClient().post<SendMessageAPIResponse<StreamChatGenerics>>(
642
- this.getClient().baseURL + `/messages/${messageID}/action`,
645
+ this.getClient().baseURL + `/messages/${encodeURIComponent(messageID)}/action`,
643
646
  {
644
647
  message_id: messageID,
645
648
  form_data: formData,
@@ -704,7 +707,7 @@ export class Channel<StreamChatGenerics extends ExtendableGenerics = DefaultGene
704
707
  *
705
708
  * @return {ReturnType<ChannelState<StreamChatGenerics>['formatMessage']> | undefined} Description
706
709
  */
707
- lastMessage() {
710
+ lastMessage(): FormatMessageResponse<StreamChatGenerics> | undefined {
708
711
  // get last 5 messages, sort, return the latest
709
712
  // get a slice of the last 5
710
713
  let min = this.state.latestMessages.length - 5;
@@ -821,7 +824,9 @@ export class Channel<StreamChatGenerics extends ExtendableGenerics = DefaultGene
821
824
  }
822
825
 
823
826
  /**
824
- * getReplies - List the message replies for a parent message
827
+ * getReplies - List the message replies for a parent message.
828
+ *
829
+ * The recommended way of working with threads is to use the Thread class.
825
830
  *
826
831
  * @param {string} parent_id The message parent id, ie the top of the thread
827
832
  * @param {MessagePaginationOptions & { user?: UserResponse<StreamChatGenerics>; user_id?: string }} options Pagination params, ie {limit:10, id_lte: 10}
@@ -835,7 +840,7 @@ export class Channel<StreamChatGenerics extends ExtendableGenerics = DefaultGene
835
840
  ) {
836
841
  const normalizedSort = sort ? normalizeQuerySort(sort) : undefined;
837
842
  const data = await this.getClient().get<GetRepliesAPIResponse<StreamChatGenerics>>(
838
- this.getClient().baseURL + `/messages/${parent_id}/replies`,
843
+ this.getClient().baseURL + `/messages/${encodeURIComponent(parent_id)}/replies`,
839
844
  {
840
845
  sort: normalizedSort,
841
846
  ...options,
@@ -863,7 +868,7 @@ export class Channel<StreamChatGenerics extends ExtendableGenerics = DefaultGene
863
868
  sort: PinnedMessagesSort = [],
864
869
  ) {
865
870
  return await this.getClient().get<GetRepliesAPIResponse<StreamChatGenerics>>(
866
- this.getClient().baseURL + `/channels/${this.type}/${this.id}/pinned_messages`,
871
+ this._channelURL() + '/pinned_messages',
867
872
  {
868
873
  payload: {
869
874
  ...options,
@@ -883,7 +888,7 @@ export class Channel<StreamChatGenerics extends ExtendableGenerics = DefaultGene
883
888
  */
884
889
  getReactions(message_id: string, options: { limit?: number; offset?: number }) {
885
890
  return this.getClient().get<GetReactionsAPIResponse<StreamChatGenerics>>(
886
- this.getClient().baseURL + `/messages/${message_id}/reactions`,
891
+ this.getClient().baseURL + `/messages/${encodeURIComponent(message_id)}/reactions`,
887
892
  {
888
893
  ...options,
889
894
  },
@@ -1000,15 +1005,15 @@ export class Channel<StreamChatGenerics extends ExtendableGenerics = DefaultGene
1000
1005
  * @return {Promise<QueryChannelAPIResponse<StreamChatGenerics>>} Returns a query response
1001
1006
  */
1002
1007
  async query(
1003
- options: ChannelQueryOptions<StreamChatGenerics>,
1008
+ options?: ChannelQueryOptions<StreamChatGenerics>,
1004
1009
  messageSetToAddToIfDoesNotExist: MessageSetType = 'current',
1005
1010
  ) {
1006
1011
  // Make sure we wait for the connect promise if there is a pending one
1007
1012
  await this.getClient().wsPromise;
1008
1013
 
1009
- let queryURL = `${this.getClient().baseURL}/channels/${this.type}`;
1014
+ let queryURL = `${this.getClient().baseURL}/channels/${encodeURIComponent(this.type)}`;
1010
1015
  if (this.id) {
1011
- queryURL += `/${this.id}`;
1016
+ queryURL += `/${encodeURIComponent(this.id)}`;
1012
1017
  }
1013
1018
 
1014
1019
  const state = await this.getClient().post<QueryChannelAPIResponse<StreamChatGenerics>>(queryURL + '/query', {
@@ -1044,6 +1049,16 @@ export class Channel<StreamChatGenerics extends ExtendableGenerics = DefaultGene
1044
1049
 
1045
1050
  // add any messages to our channel state
1046
1051
  const { messageSet } = this._initializeState(state, messageSetToAddToIfDoesNotExist);
1052
+ messageSet.pagination = {
1053
+ ...messageSet.pagination,
1054
+ ...messageSetPagination({
1055
+ parentSet: messageSet,
1056
+ messagePaginationOptions: options?.messages,
1057
+ requestedPageSize: options?.messages?.limit ?? DEFAULT_QUERY_CHANNEL_MESSAGE_LIST_PAGE_SIZE,
1058
+ returnedPage: state.messages,
1059
+ logger: this.getClient().logger,
1060
+ }),
1061
+ };
1047
1062
 
1048
1063
  const areCapabilitiesChanged =
1049
1064
  [...(state.channel.own_capabilities || [])].sort().join() !==
@@ -1533,7 +1548,7 @@ export class Channel<StreamChatGenerics extends ExtendableGenerics = DefaultGene
1533
1548
  if (!this.id) {
1534
1549
  throw new Error('channel id is not defined');
1535
1550
  }
1536
- return `${this.getClient().baseURL}/channels/${this.type}/${this.id}`;
1551
+ return `${this.getClient().baseURL}/channels/${encodeURIComponent(this.type)}/${encodeURIComponent(this.id)}`;
1537
1552
  };
1538
1553
 
1539
1554
  _checkInitialized() {
@@ -6,6 +6,7 @@ import {
6
6
  ExtendableGenerics,
7
7
  FormatMessageResponse,
8
8
  MessageResponse,
9
+ MessageSet,
9
10
  MessageSetType,
10
11
  PendingMessageResponse,
11
12
  PollResponse,
@@ -13,7 +14,8 @@ import {
13
14
  ReactionResponse,
14
15
  UserResponse,
15
16
  } from './types';
16
- import { addToMessageList } from './utils';
17
+ import { addToMessageList, formatMessage } from './utils';
18
+ import { DEFAULT_MESSAGE_SET_PAGINATION } from './constants';
17
19
 
18
20
  type ChannelReadStatus<StreamChatGenerics extends ExtendableGenerics = DefaultGenerics> = Record<
19
21
  string,
@@ -56,11 +58,8 @@ export class ChannelState<StreamChatGenerics extends ExtendableGenerics = Defaul
56
58
  * The state manages these lists and merges them when lists overlap
57
59
  * The messages array contains the currently active set
58
60
  */
59
- messageSets: {
60
- isCurrent: boolean;
61
- isLatest: boolean;
62
- messages: Array<ReturnType<ChannelState<StreamChatGenerics>['formatMessage']>>;
63
- }[] = [];
61
+ messageSets: MessageSet[] = [];
62
+
64
63
  constructor(channel: Channel<StreamChatGenerics>) {
65
64
  this._channel = channel;
66
65
  this.watcher_count = 0;
@@ -108,6 +107,10 @@ export class ChannelState<StreamChatGenerics extends ExtendableGenerics = Defaul
108
107
  this.messageSets[index].messages = messages;
109
108
  }
110
109
 
110
+ get messagePagination() {
111
+ return this.messageSets.find((s) => s.isCurrent)?.pagination || DEFAULT_MESSAGE_SET_PAGINATION;
112
+ }
113
+
111
114
  /**
112
115
  * addMessageSorted - Add a message to the state
113
116
  *
@@ -132,26 +135,12 @@ export class ChannelState<StreamChatGenerics extends ExtendableGenerics = Defaul
132
135
  }
133
136
 
134
137
  /**
135
- * formatMessage - Takes the message object. Parses the dates, sets __html
136
- * and sets the status to received if missing. Returns a message object
137
- *
138
- * @param {MessageResponse<StreamChatGenerics>} message a message object
138
+ * Takes the message object, parses the dates, sets `__html`
139
+ * and sets the status to `received` if missing; returns a new message object.
139
140
  *
141
+ * @param {MessageResponse<StreamChatGenerics>} message `MessageResponse` object
140
142
  */
141
- formatMessage(message: MessageResponse<StreamChatGenerics>): FormatMessageResponse<StreamChatGenerics> {
142
- return {
143
- ...message,
144
- /**
145
- * @deprecated please use `html`
146
- */
147
- __html: message.html,
148
- // parse the date..
149
- pinned_at: message.pinned_at ? new Date(message.pinned_at) : null,
150
- created_at: message.created_at ? new Date(message.created_at) : new Date(),
151
- updated_at: message.updated_at ? new Date(message.updated_at) : new Date(),
152
- status: message.status || 'received',
153
- };
154
- }
143
+ formatMessage = (message: MessageResponse<StreamChatGenerics>) => formatMessage<StreamChatGenerics>(message);
155
144
 
156
145
  /**
157
146
  * addMessagesSorted - Add the list of messages to state and resorts the messages
@@ -664,7 +653,7 @@ export class ChannelState<StreamChatGenerics extends ExtendableGenerics = Defaul
664
653
  messages[i] = {
665
654
  ...m,
666
655
  type: 'deleted',
667
- deleted_at: user.deleted_at,
656
+ deleted_at: user.deleted_at ? new Date(user.deleted_at) : null,
668
657
  };
669
658
  }
670
659
  }
@@ -717,7 +706,7 @@ export class ChannelState<StreamChatGenerics extends ExtendableGenerics = Defaul
717
706
  }
718
707
 
719
708
  initMessages() {
720
- this.messageSets = [{ messages: [], isLatest: true, isCurrent: true }];
709
+ this.messageSets = [{ messages: [], isLatest: true, isCurrent: true, pagination: DEFAULT_MESSAGE_SET_PAGINATION }];
721
710
  }
722
711
 
723
712
  /**
@@ -725,6 +714,7 @@ export class ChannelState<StreamChatGenerics extends ExtendableGenerics = Defaul
725
714
  *
726
715
  * @param {string} messageId The id of the message, or 'latest' to indicate switching to the latest messages
727
716
  * @param {string} parentMessageId The id of the parent message, if we want load a thread reply
717
+ * @param {number} limit The page size if the message has to be queried from the server
728
718
  */
729
719
  async loadMessageIntoState(messageId: string | 'latest', parentMessageId?: string, limit = 25) {
730
720
  let messageSetIndex: number;
@@ -820,7 +810,12 @@ export class ChannelState<StreamChatGenerics extends ExtendableGenerics = Defaul
820
810
  targetMessageSetIndex = overlappingMessageSetIndices[0];
821
811
  // No new message set is created if newMessages only contains thread replies
822
812
  } else if (newMessages.some((m) => !m.parent_id)) {
823
- this.messageSets.push({ messages: [], isCurrent: false, isLatest: false });
813
+ this.messageSets.push({
814
+ messages: [],
815
+ isCurrent: false,
816
+ isLatest: false,
817
+ pagination: DEFAULT_MESSAGE_SET_PAGINATION,
818
+ });
824
819
  targetMessageSetIndex = this.messageSets.length - 1;
825
820
  }
826
821
  break;
@@ -846,6 +841,14 @@ export class ChannelState<StreamChatGenerics extends ExtendableGenerics = Defaul
846
841
  sources.forEach((messageSet) => {
847
842
  target.isLatest = target.isLatest || messageSet.isLatest;
848
843
  target.isCurrent = target.isCurrent || messageSet.isCurrent;
844
+ target.pagination.hasPrev =
845
+ messageSet.messages[0].created_at < target.messages[0].created_at
846
+ ? messageSet.pagination.hasPrev
847
+ : target.pagination.hasPrev;
848
+ target.pagination.hasNext =
849
+ target.messages.slice(-1)[0].created_at < messageSet.messages.slice(-1)[0].created_at
850
+ ? messageSet.pagination.hasNext
851
+ : target.pagination.hasNext;
849
852
  messagesToAdd = [...messagesToAdd, ...messageSet.messages];
850
853
  });
851
854
  sources.forEach((s) => this.messageSets.splice(this.messageSets.indexOf(s), 1));