diff --git a/README.md b/README.md index c558a67b3..c21cddf2e 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ You can sign up for a Stream account at our [Get Started](https://getstream.io/chat/get_started/) page. -This library can be used by both frontend and backend applications. For frontend, we have frameworks that are based on this library such as the [Flutter](https://github.com/GetStream/stream-chat-flutter), [React](https://github.com/GetStream/stream-chat-react) and [Angular](https://github.com/GetStream/stream-chat-angular) SDKs. For more information, check out our [docs](https://getstream.io/chat/docs/). +This library can be used by both frontend and backend applications. For frontend, we have frameworks that are based on this library such as the [Flutter](https://github.com/GetStream/stream-chat-flutter), [React](https://github.com/GetStream/stream-chat-react) and [Angular](https://github.com/GetStream/stream-chat-angular) SDKs. For more information, check out our [documentation](https://getstream.io/chat/docs/). ## ⚙️ Installation @@ -36,95 +36,95 @@ npm install stream-chat yarn add stream-chat ``` -### JS deliver +### jsDelivr ```html ``` -## ✨ Getting started +## ✨ Getting Started -The StreamChat client is setup to allow extension of the base types through use of generics when instantiated. The default instantiation has all generics set to `Record`. - -```typescript +```ts import { StreamChat } from 'stream-chat'; -// Or if you are on commonjs -const StreamChat = require('stream-chat').StreamChat; +// or if you are using CommonJS +const { StreamChat } = require('stream-chat'); -const client = StreamChat.getInstance('YOUR_API_KEY', 'API_KEY_SECRET'); +const client = new StreamChat('API_KEY', 'API_SECRET', { + disableCache: true, // recommended option for server-side use + // ...other options like `baseURL`... +}); -const channel = client.channel('messaging', 'TestChannel'); +// create a user +await client.upsertUser({ + id: 'vishal-1', + name: 'Vishal', +}); + +// create a channel +const channel = client.channel('messaging', 'test-channel', { created_by_id: 'vishal-1' }); await channel.create(); + +// send message +const { message } = await channel.sendMessage({ text: 'This is a test message' }); + +// send reaction +await channel.sendReaction(message.id, { type: 'love', user: { id: 'vishal-1' } }); ``` -Or you can customize the generics: - -```typescript -type ChatChannel = { image: string; category?: string }; -type ChatUser1 = { nickname: string; age: number; admin?: boolean }; -type ChatUser2 = { nickname: string; avatar?: string }; -type UserMessage = { country?: string }; -type AdminMessage = { priorityLevel: number }; -type ChatAttachment = { originalURL?: string }; -type CustomReaction = { size?: number }; -type ChatEvent = { quitChannel?: boolean }; -type CustomCommands = 'giphy'; - -type StreamType = { - attachmentType: ChatAttachment; - channelType: ChatChannel; - commandType: CustomCommands; - eventType: ChatEvent; - messageType: UserMessage | AdminMessage; - reactionType: CustomReaction; - userType: ChatUser1 | ChatUser2; -}; +The `StreamChat` client is set up to allow extension of the base types through use of module augmentation, custom types will carry through to all client returns and provide code-completion to queries (if supported). To extend Stream's entities with custom data you'll have to create a declaration file and make sure it's loaded by TypeScript, [see the list of extendable interfaces](https://github.com/GetStream/stream-chat-js/blob/master/src/custom_types.ts) and the example bellow using two of the most common ones: -const client = StreamChat.getInstance('YOUR_API_KEY', 'API_KEY_SECRET'); +```ts +// stream-custom-data.d.ts -// Create channel -const channel = client.channel('messaging', 'TestChannel'); -await channel.create(); +import 'stream-chat'; -// Create user -await client.upsertUser({ - id: 'vishal-1', - name: 'Vishal', -}); +declare module 'stream-chat' { + interface CustomMessageData { + custom_property?: number; + } + interface CustomUserData { + profile_picture?: string; + } +} -// Send message -const { message } = await channel.sendMessage({ text: `Test message` }); +// index.ts -// Send reaction -await channel.sendReaction(message.id, { type: 'love', user: { id: 'vishal-1' } }); +// property `profile_picture` is code-completed and expects type `string | undefined` +await client.partialUpdateUser({ id: 'vishal-1', set: { profile_picture: 'https://random.picture/1.jpg' } }); + +// property `custom_property` is code-completed and expects type `number | undefined` +const { message } = await channel.sendMessage({ text: 'This is another test message', custom_property: 255 }); + +message.custom_property; // in the response object as well ``` -Custom types provided when initializing the client will carry through to all client returns and provide intellisense to queries. +> [!WARNING] +> Generics mechanism has been removed in version `9.0.0` in favour of the module augmentation, please see [the release guide](https://getstream.io/chat/docs/node/upgrade-stream-chat-to-v9) on how to migrate. -## 🔗 (Optional) Development Setup in Combination with our SDKs +## 🔗 (Optional) Development Setup in Combination With Our SDKs ### Connect to [Stream Chat React Native SDK](https://github.com/GetStream/stream-chat-react-native) -Run in the root of this repo +Run in the root of this repository: -```shell +```sh yarn link ``` -Run in the root of one of the example apps (SampleApp/TypeScriptMessaging) in the `stream-chat-react-native` repo +Run in the root of one of the example applications (SampleApp/TypeScriptMessaging) in the `stream-chat-react-native` repository: -```shell +```sh yarn link stream-chat yarn start ``` -Open `metro.config.js` file and set value for watchFolders as +Open `metro.config.js` file and set value for `watchFolders` as: -```javascript -const streamChatRoot = '{{CHANGE_TO_THE_PATH_TO_YOUR_PROJECT}}/stream-chat-js' +```js +const streamChatRoot = '/stream-chat-js' module.exports = { - // the rest of the metro config goes here + // the rest of the metro configuration goes here ... watchFolders: [projectRoot].concat(alternateRoots).concat([streamChatRoot]), resolver: { @@ -139,17 +139,17 @@ module.exports = { }; ``` -Make sure to replace `{{CHANGE_TO_THE_PATH_TO_YOUR_PROJECT}}` with the correct path for the `stream-chat-js` folder as per your directory structure. +Make sure to replace `` with the correct path for the `stream-chat-js` folder as per your directory structure. -Run in the root of this repo +Run in the root of this repository: -```shell +```sh yarn start ``` -## 📚 More code examples +## 📚 More Code Examples -Head over to [docs/typescript.md](./docs/typescript.md) for more examples. +Read up more on [Logging](./docs/logging.md) and [User Token](./docs/userToken.md) or visit our [documentation](https://getstream.io/chat/docs/) for more examples. ## ✍️ Contributing @@ -157,7 +157,7 @@ We welcome code changes that improve this library or fix a problem, please make Head over to [CONTRIBUTING.md](./CONTRIBUTING.md) for some development tips. -## 🧑‍💻 We are hiring! +## 🧑‍💻 We Are Hiring! We've recently closed a [$38 million Series B funding round](https://techcrunch.com/2021/03/04/stream-raises-38m-as-its-chat-and-activity-feed-apis-power-communications-for-1b-users/) and we keep actively growing. Our APIs are used by more than a billion end-users, and you'll have a chance to make a huge impact on the product within a team of the strongest engineers all over the world. diff --git a/assets/logo.svg b/assets/logo.svg index 1c68c5ccc..ac2e18775 100644 --- a/assets/logo.svg +++ b/assets/logo.svg @@ -1,16 +1,31 @@ - - - - STREAM MARK - Created with Sketch. - - - - - - - - - - - \ No newline at end of file + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/typescript.md b/docs/typescript.md deleted file mode 100644 index 118ba59b7..000000000 --- a/docs/typescript.md +++ /dev/null @@ -1,122 +0,0 @@ -# Typescript (v2.x.x) - -The StreamChat client is setup to allow extension of the base types through use of generics when instantiated. The default instantiation has all generics set to `Record`. - -```typescript -StreamChat<{ - attachmentType: AttachmentType; - channelType: ChannelType; - commandType: CommandType; - eventType: EventType; - messageType: MessageType; - reactionType: ReactionType; - userType: UserType; -}> -``` - -Custom types provided when initializing the client will carry through to all client returns and provide intellisense to queries. - -**NOTE:** If you utilize the `setAnonymousUser` function you must account for this in your user types. - -```typescript -import { StreamChat } from 'stream-chat'; -// or if you are on commonjs -const StreamChat = require('stream-chat').StreamChat; - -type ChatChannel = { image: string; category?: string }; -type ChatUser1 = { nickname: string; age: number; admin?: boolean }; -type ChatUser2 = { nickname: string; avatar?: string }; -type UserMessage = { country?: string }; -type AdminMessage = { priorityLevel: number }; -type ChatAttachment = { originalURL?: string }; -type CustomReaction = { size?: number }; -type ChatEvent = { quitChannel?: boolean }; -type CustomCommands = 'giphy'; - -type StreamType = { - attachmentType: ChatAttachment; - channelType: ChatChannel; - commandType: CustomCommands; - eventType: ChatEvent; - messageType: UserMessage | AdminMessage; - reactionType: CustomReaction; - userType: ChatUser1 | ChatUser2; -}; - -// Instantiate a new client (server side) -// you can also use `new StreamChat()` -const client = StreamChat.getInstance('YOUR_API_KEY', 'API_KEY_SECRET'); - -/** - * Instantiate a new client (client side) - * Unused generics default to Record - * with the exception of Command which defaults to string & {} - */ -const client = StreamChat.getInstance('YOUR_API_KEY'); -``` - -Query operations will return results that utilize the custom types added via generics. In addition the query filters are type checked and provide intellisense using both the key and type of the parameter to ensure accurate use. - -```typescript -// Valid queries -// users: { duration: string; users: UserResponse[]; } -const users = await client.queryUsers({ id: '1080' }); -const users = await client.queryUsers({ nickname: 'streamUser' }); -const users = await client.queryUsers({ nickname: { $eq: 'streamUser' } }); - -// Invalid queries -const users = await client.queryUsers({ nickname: { $contains: ['stream'] } }); // $contains is only an operator on arrays -const users = await client.queryUsers({ nickname: 1080 }); // nickname must be a string -const users = await client.queryUsers({ name: { $eq: 1080 } }); // name must be a string -``` - -**Note:** If you have differing union types like `ChatUser1 | ChatUser2` or `UserMessage | AdminMessage` you can use [type guards](https://www.typescriptlang.org/docs/handbook/advanced-types.html#type-guards-and-differentiating-types) to maintain type safety when dealing with the results of queries. - -```typescript -function isChatUser1(user: ChatUser1 | ChatUser2): user is ChatUser1 { - return (user as ChatUser1).age !== undefined; -} - -function isAdminMessage(msg: UserMessage | AdminMessage): msg is AdminMessage { - return (msg as AdminMessage).priorityLevel !== undefined; -} -``` - -Intellisense, type checking, and return types are provided for all queries. - -```typescript -const channel = client.channel('messaging', 'TestChannel'); -await channel.create(); - -// Valid queries -// messages: SearchAPIResponse -const messages = await channel.search({ country: 'NL' }); -const messages = await channel.search({ priorityLevel: { $gt: 5 } }); -const messages = await channel.search({ - $and: [{ priorityLevel: { $gt: 5 } }, { deleted_at: { $exists: false } }], -}); - -// Invalid queries -const messages = await channel.search({ country: { $eq: 5 } }); // country must be a string -const messages = await channel.search({ - $or: [{ id: '2' }, { reaction_counts: { $eq: 'hello' } }], -}); // reaction_counts must be a number -``` - -Custom types are carried into all creation functions as well. - -```typescript -// Valid -client.connectUser({ id: 'testId', nickname: 'testUser', age: 3 }, 'TestToken'); -client.connectUser({ id: 'testId', nickname: 'testUser', avatar: 'testAvatar' }, 'TestToken'); - -// Invalid -client.connectUser({ id: 'testId' }, 'TestToken'); // Type ChatUser1 | ChatUser2 requires nickname for both types -client.connectUser({ id: 'testId', nickname: true }, 'TestToken'); // nickname must be a string -client.connectUser({ id: 'testId', nickname: 'testUser', country: 'NL' }, 'TestToken'); // country does not exist on type ChatUser1 | ChatUser2 -``` - -## More - -- [Logging](./logging.md) -- [User Token](./userToken.md) diff --git a/src/campaign.ts b/src/campaign.ts index f4201cd12..ed0e23962 100644 --- a/src/campaign.ts +++ b/src/campaign.ts @@ -1,12 +1,12 @@ import { StreamChat } from './client'; -import { CampaignData, DefaultGenerics, ExtendableGenerics } from './types'; +import { CampaignData } from './types'; -export class Campaign { +export class Campaign { id: string | null; data?: CampaignData; - client: StreamChat; + client: StreamChat; - constructor(client: StreamChat, id: string | null, data?: CampaignData) { + constructor(client: StreamChat, id: string | null, data?: CampaignData) { this.client = client; this.id = id; this.data = data; diff --git a/src/channel.ts b/src/channel.ts index 94962b2eb..4f0d8e4de 100644 --- a/src/channel.ts +++ b/src/channel.ts @@ -1,7 +1,8 @@ import { ChannelState } from './channel_state'; import { generateChannelTempCid, logChatPromiseExecution, messageSetPagination, normalizeQuerySort } from './utils'; import { StreamChat } from './client'; -import { +import { DEFAULT_QUERY_CHANNEL_MESSAGE_LIST_PAGE_SIZE } from './constants'; +import type { APIResponse, BanUserOptions, ChannelAPIResponse, @@ -14,18 +15,15 @@ import { ChannelUpdateOptions, CreateCallOptions, CreateCallResponse, - DefaultGenerics, DeleteChannelAPIResponse, Event, EventAPIResponse, EventHandler, EventTypes, - ExtendableGenerics, FormatMessageResponse, GetMultipleMessagesAPIResponse, GetReactionsAPIResponse, GetRepliesAPIResponse, - InviteOptions, MarkReadOptions, MarkUnreadOptions, MemberFilters, @@ -62,23 +60,24 @@ import { AIState, MessageOptions, PushPreference, + UpdateChannelOptions, } from './types'; -import { Role } from './permissions'; -import { DEFAULT_QUERY_CHANNEL_MESSAGE_LIST_PAGE_SIZE } from './constants'; +import type { Role } from './permissions'; +import type { CustomChannelData } from './custom_types'; /** * Channel - The Channel class manages it's own state. */ -export class Channel { - _client: StreamChat; +export class Channel { + _client: StreamChat; type: string; id: string | undefined; - data: ChannelData | ChannelResponse | undefined; - _data: ChannelData | ChannelResponse; + data: Partial | undefined; + _data: Partial; cid: string; /** */ - listeners: { [key: string]: (string | EventHandler)[] }; - state: ChannelState; + listeners: { [key: string]: (string | EventHandler)[] }; + state: ChannelState; /** * This boolean is a vague indication of weather the channel exists on chat backend. * @@ -103,19 +102,14 @@ export class Channel} client the chat client + * @param {StreamChat} client the chat client * @param {string} type the type of channel * @param {string} [id] the id of the chat - * @param {ChannelData} data any additional custom params + * @param {ChannelData} data any additional custom params * - * @return {Channel} Returns a new uninitialized channel + * @return {Channel} Returns a new uninitialized channel */ - constructor( - client: StreamChat, - type: string, - id: string | undefined, - data: ChannelData, - ) { + constructor(client: StreamChat, type: string, id: string | undefined, data: ChannelData) { const validTypeRe = /^[\w_-]+$/; const validIDRe = /^[\w!_-]+$/; @@ -136,7 +130,7 @@ export class Channel(this); + this.state = new ChannelState(this); this.initialized = false; this.offlineMode = false; this.lastTypingEvent = null; @@ -147,9 +141,9 @@ export class Channel} + * @return {StreamChat} */ - getClient(): StreamChat { + getClient(): StreamChat { if (this.disconnected === true) { throw Error(`You can't use a channel after client.disconnect() was called`); } @@ -169,7 +163,7 @@ export class Channel} message The Message object + * @param {Message} message The Message object * @param {boolean} [options.skip_enrich_url] Do not try to enrich the URLs within message * @param {boolean} [options.skip_push] Skip sending push notifications * @param {boolean} [options.is_pending_message] DEPRECATED, please use `pending` instead. @@ -177,10 +171,10 @@ export class Channel} [options.pending_message_metadata] Metadata for the pending message * @param {boolean} [options.force_moderation] Apply force moderation for server-side requests * - * @return {Promise>} The Server Response + * @return {Promise} The Server Response */ - async sendMessage(message: Message, options?: SendMessageOptions) { - return await this.getClient().post>(this._channelURL() + '/message', { + async sendMessage(message: Message, options?: SendMessageOptions) { + return await this.getClient().post(this._channelURL() + '/message', { message, ...options, }); @@ -190,17 +184,12 @@ export class Channel, + user?: UserResponse, ) { return this.getClient().sendFile(`${this._channelURL()}/file`, uri, name, contentType, user); } - sendImage( - uri: string | NodeJS.ReadableStream | File, - name?: string, - contentType?: string, - user?: UserResponse, - ) { + sendImage(uri: string | NodeJS.ReadableStream | File, name?: string, contentType?: string, user?: UserResponse) { return this.getClient().sendFile(`${this._channelURL()}/image`, uri, name, contentType, user); } @@ -215,13 +204,13 @@ export class Channel} event for example {type: 'message.read'} + * @param {Event} event for example {type: 'message.read'} * - * @return {Promise>} The Server Response + * @return {Promise} The Server Response */ - async sendEvent(event: Event) { + async sendEvent(event: Event) { this._checkInitialized(); - return await this.getClient().post>(this._channelURL() + '/event', { + return await this.getClient().post(this._channelURL() + '/event', { event, }); } @@ -229,17 +218,17 @@ export class Channel | string} query search query or object MongoDB style filters - * @param {{client_id?: string; connection_id?: string; query?: string; message_filter_conditions?: MessageFilters}} options Option object, {user_id: 'tommaso'} + * @param {MessageFilters | string} query search query or object MongoDB style filters + * @param {{client_id?: string; connection_id?: string; query?: string; message_filter_conditions?: MessageFilters}} options Option object, {user_id: 'tommaso'} * - * @return {Promise>} search messages response + * @return {Promise} search messages response */ async search( - query: MessageFilters | string, - options: SearchOptions & { + query: MessageFilters | string, + options: SearchOptions & { client_id?: string; connection_id?: string; - message_filter_conditions?: MessageFilters; + message_filter_conditions?: MessageFilters; message_options?: MessageOptions; query?: string; } = {}, @@ -248,10 +237,10 @@ export class Channel = { - filter_conditions: { cid: this.cid } as ChannelFilters, + const payload: SearchPayload = { + filter_conditions: { cid: this.cid } as ChannelFilters, ...options, - sort: options.sort ? normalizeQuerySort>(options.sort) : undefined, + sort: options.sort ? normalizeQuerySort(options.sort) : undefined, }; if (typeof query === 'string') { payload.query = query; @@ -263,7 +252,7 @@ export class Channel>(this.getClient().baseURL + '/search', { + return await this.getClient().get(this.getClient().baseURL + '/search', { payload, }); } @@ -271,56 +260,49 @@ export class Channel} filterConditions object MongoDB style filters - * @param {MemberSort} [sort] Sort options, for instance [{created_at: -1}]. + * @param {MemberFilters} filterConditions object MongoDB style filters + * @param {MemberSort} [sort] Sort options, for instance [{created_at: -1}]. * When using multiple fields, make sure you use array of objects to guarantee field order, for instance [{name: -1}, {created_at: 1}] * @param {{ limit?: number; offset?: number }} [options] Option object, {limit: 10, offset:10} * - * @return {Promise>} Query Members response + * @return {Promise} Query Members response */ - async queryMembers( - filterConditions: MemberFilters, - sort: MemberSort = [], - options: QueryMembersOptions = {}, - ) { + async queryMembers(filterConditions: MemberFilters, sort: MemberSort = [], options: QueryMembersOptions = {}) { let id: string | undefined; const type = this.type; - let members: string[] | ChannelMemberResponse[] | undefined; + let members: string[] | ChannelMemberResponse[] | undefined; if (this.id) { id = this.id; } else if (this.data?.members && Array.isArray(this.data.members)) { members = this.data.members; } // Return a list of members - return await this.getClient().get>( - this.getClient().baseURL + '/members', - { - payload: { - type, - id, - members, - sort: normalizeQuerySort(sort), - filter_conditions: filterConditions, - ...options, - }, + return await this.getClient().get(this.getClient().baseURL + '/members', { + payload: { + type, + id, + members, + sort: normalizeQuerySort(sort), + filter_conditions: filterConditions, + ...options, }, - ); + }); } /** * partialUpdateMember - Partial update a member * * @param {string} user_id member user id - * @param {PartialUpdateMember} updates + * @param {PartialUpdateMember} updates * - * @return {Promise>} Updated member + * @return {Promise} Updated member */ - async partialUpdateMember(user_id: string, updates: PartialUpdateMember) { + async partialUpdateMember(user_id: string, updates: PartialUpdateMember) { if (!user_id) { throw Error('Please specify the user id'); } - return await this.getClient().patch>( + return await this.getClient().patch( this._channelURL() + `/member/${encodeURIComponent(user_id)}`, updates, ); @@ -330,14 +312,14 @@ export class Channel} reaction the reaction object for instance {type: 'love'} + * @param {Reaction} reaction the reaction object for instance {type: 'love'} * @param {{ enforce_unique?: boolean, skip_push?: boolean }} [options] Option object, {enforce_unique: true, skip_push: true} to override any existing reaction or skip sending push notifications * - * @return {Promise>} The Server Response + * @return {Promise} The Server Response */ async sendReaction( messageID: string, - reaction: Reaction, + reaction: Reaction, options?: { enforce_unique?: boolean; skip_push?: boolean }, ) { if (!messageID) { @@ -346,7 +328,7 @@ export class Channel>( + return await this.getClient().post( this.getClient().baseURL + `/messages/${encodeURIComponent(messageID)}/reaction`, { reaction, @@ -362,7 +344,7 @@ export class Channel>} The Server Response + * @return {Promise} The Server Response */ deleteReaction(messageID: string, reactionType: string, user_id?: string) { this._checkInitialized(); @@ -375,27 +357,28 @@ export class Channel>(url, { user_id }); + return this.getClient().delete(url, { user_id }); } - return this.getClient().delete>(url, {}); + return this.getClient().delete(url, {}); } /** * update - Edit the channel's custom properties * - * @param {ChannelData} channelData The object to update the custom properties of this channel with - * @param {Message} [updateMessage] Optional message object for channel members notification + * @param {ChannelData} channelData The object to update the custom properties of this channel with + * @param {Message} [updateMessage] Optional message object for channel members notification * @param {ChannelUpdateOptions} [options] Option object, configuration to control the behavior while updating - * @return {Promise>} The server response + * @return {Promise} The server response */ async update( - channelData: Partial> | Partial> = {}, - updateMessage?: Message, + channelData: Partial = {}, + updateMessage?: Message, options?: ChannelUpdateOptions, ) { // Strip out reserved names that will result in API errors. - const reserved = [ + // TODO: this needs to be typed better + const reserved: Exclude[] = [ 'config', 'cid', 'created_by', @@ -407,6 +390,7 @@ export class Channel { delete channelData[key]; }); @@ -421,15 +405,12 @@ export class Channel} partial update request + * @param {PartialUpdateChannel} partial update request * - * @return {Promise>} + * @return {Promise} */ - async updatePartial(update: PartialUpdateChannel) { - const data = await this.getClient().patch>( - this._channelURL(), - update, - ); + async updatePartial(update: PartialUpdateChannel) { + const data = await this.getClient().patch(this._channelURL(), update); const areCapabilitiesChanged = [...(data.channel.own_capabilities || [])].sort().join() !== @@ -450,10 +431,10 @@ export class Channel>} The server response + * @return {Promise} The server response */ async enableSlowMode(coolDownInterval: number) { - const data = await this.getClient().post>(this._channelURL(), { + const data = await this.getClient().post(this._channelURL(), { cooldown: coolDownInterval, }); this.data = data.channel; @@ -463,10 +444,10 @@ export class Channel>} The server response + * @return {Promise} The server response */ async disableSlowMode() { - const data = await this.getClient().post>(this._channelURL(), { + const data = await this.getClient().post(this._channelURL(), { cooldown: 0, }); this.data = data.channel; @@ -478,61 +459,54 @@ export class Channel>} The server response + * @return {Promise} The server response */ async delete(options: { hard_delete?: boolean } = {}) { - return await this.getClient().delete>(this._channelURL(), { + return await this.getClient().delete(this._channelURL(), { ...options, }); } /** * truncate - Removes all messages from the channel - * @param {TruncateOptions} [options] Defines truncation options - * @return {Promise>} The server response + * @param {TruncateOptions} [options] Defines truncation options + * @return {Promise} The server response */ - async truncate(options: TruncateOptions = {}) { - return await this.getClient().post>( - this._channelURL() + '/truncate', - options, - ); + async truncate(options: TruncateOptions = {}) { + return await this.getClient().post(this._channelURL() + '/truncate', options); } /** * acceptInvite - accept invitation to the channel * - * @param {InviteOptions} [options] The object to update the custom properties of this channel with + * @param {UpdateChannelOptions} [options] The object to update the custom properties of this channel with * - * @return {Promise>} The server response + * @return {Promise} The server response */ - async acceptInvite(options: InviteOptions = {}) { + async acceptInvite(options: UpdateChannelOptions = {}) { return await this._update({ accept_invite: true, ...options }); } /** * rejectInvite - reject invitation to the channel * - * @param {InviteOptions} [options] The object to update the custom properties of this channel with + * @param {UpdateChannelOptions} [options] The object to update the custom properties of this channel with * - * @return {Promise>} The server response + * @return {Promise} The server response */ - async rejectInvite(options: InviteOptions = {}) { + async rejectInvite(options: UpdateChannelOptions = {}) { return await this._update({ reject_invite: true, ...options }); } /** * addMembers - add members to the channel * - * @param {string[] | Array>} members An array of members to add to the channel - * @param {Message} [message] Optional message object for channel members notification + * @param {string[] | Array} members An array of members to add to the channel + * @param {Message} [message] Optional message object for channel members notification * @param {ChannelUpdateOptions} [options] Option object, configuration to control the behavior while updating - * @return {Promise>} The server response + * @return {Promise} The server response */ - async addMembers( - members: string[] | Array>, - message?: Message, - options: ChannelUpdateOptions = {}, - ) { + async addMembers(members: string[] | Array, message?: Message, options: ChannelUpdateOptions = {}) { return await this._update({ add_members: members, message, ...options }); } @@ -540,11 +514,11 @@ export class Channel} [message] Optional message object for channel members notification + * @param {Message} [message] Optional message object for channel members notification * @param {ChannelUpdateOptions} [options] Option object, configuration to control the behavior while updating - * @return {Promise>} The server response + * @return {Promise} The server response */ - async addModerators(members: string[], message?: Message, options: ChannelUpdateOptions = {}) { + async addModerators(members: string[], message?: Message, options: ChannelUpdateOptions = {}) { return await this._update({ add_moderators: members, message, ...options }); } @@ -552,13 +526,13 @@ export class Channel} [message] Optional message object for channel members notification + * @param {Message} [message] Optional message object for channel members notification * @param {ChannelUpdateOptions} [options] Option object, configuration to control the behavior while updating - * @return {Promise>} The server response + * @return {Promise} The server response */ async assignRoles( roles: { channel_role: Role; user_id: string }[], - message?: Message, + message?: Message, options: ChannelUpdateOptions = {}, ) { return await this._update({ assign_roles: roles, message, ...options }); @@ -567,14 +541,14 @@ export class Channel>} members An array of members to invite to the channel - * @param {Message} [message] Optional message object for channel members notification + * @param {string[] | Array} members An array of members to invite to the channel + * @param {Message} [message] Optional message object for channel members notification * @param {ChannelUpdateOptions} [options] Option object, configuration to control the behavior while updating - * @return {Promise>} The server response + * @return {Promise} The server response */ async inviteMembers( - members: string[] | Array>, - message?: Message, + members: string[] | Required>[], + message?: Message, options: ChannelUpdateOptions = {}, ) { return await this._update({ invites: members, message, ...options }); @@ -584,11 +558,11 @@ export class Channel} [message] Optional message object for channel members notification + * @param {Message} [message] Optional message object for channel members notification * @param {ChannelUpdateOptions} [options] Option object, configuration to control the behavior while updating - * @return {Promise>} The server response + * @return {Promise} The server response */ - async removeMembers(members: string[], message?: Message, options: ChannelUpdateOptions = {}) { + async removeMembers(members: string[], message?: Message, options: ChannelUpdateOptions = {}) { return await this._update({ remove_members: members, message, ...options }); } @@ -596,22 +570,22 @@ export class Channel} [message] Optional message object for channel members notification + * @param {Message} [message] Optional message object for channel members notification * @param {ChannelUpdateOptions} [options] Option object, configuration to control the behavior while updating - * @return {Promise>} The server response + * @return {Promise} The server response */ - async demoteModerators(members: string[], message?: Message, options: ChannelUpdateOptions = {}) { + async demoteModerators(members: string[], message?: Message, options: ChannelUpdateOptions = {}) { return await this._update({ demote_moderators: members, message, ...options }); } /** * _update - executes channel update request * @param payload Object Update Channel payload - * @return {Promise>} The server response + * @return {Promise} The server response * TODO: introduce new type instead of Object in the next major update */ async _update(payload: Object) { - const data = await this.getClient().post>(this._channelURL(), payload); + const data = await this.getClient().post(this._channelURL(), payload); this.data = data.channel; return data; } @@ -619,7 +593,7 @@ export class Channel>} The server response + * @return {Promise} The server response * * example with expiration: * await channel.mute({expiration: moment.duration(2, 'weeks')}); @@ -629,10 +603,10 @@ export class Channel>( - this.getClient().baseURL + '/moderation/mute/channel', - { channel_cid: this.cid, ...opts }, - ); + return await this.getClient().post(this.getClient().baseURL + '/moderation/mute/channel', { + channel_cid: this.cid, + ...opts, + }); } /** @@ -653,7 +627,7 @@ export class Channel>} The server response + * @return {Promise} The server response * * example: * await channel.archives(); @@ -675,7 +649,7 @@ export class Channel>} The server response + * @return {Promise} The server response * * example: * await channel.unarchive(); @@ -697,7 +671,7 @@ export class Channel>} The server response + * @return {Promise} The server response * * example: * await channel.pin(); @@ -719,7 +693,7 @@ export class Channel>} The server response + * @return {Promise} The server response * * example: * await channel.unpin(); @@ -756,7 +730,7 @@ export class Channel>( + return this.getClient().post( this.getClient().baseURL + `/messages/${encodeURIComponent(messageID)}/action`, { message_id: messageID, @@ -788,7 +762,7 @@ export class Channel); + } as Event); } } @@ -806,7 +780,7 @@ export class Channel); + } as Event); } /** @@ -816,7 +790,7 @@ export class Channel); + } as Event); } /** @@ -826,7 +800,7 @@ export class Channel); + } as Event); } /** @@ -844,7 +818,7 @@ export class Channel); + } as Event); } _isTypingIndicatorsEnabled(): boolean { @@ -857,9 +831,9 @@ export class Channel['formatMessage']> | undefined} Description + * @return {ReturnType | undefined} Description */ - lastMessage(): FormatMessageResponse | undefined { + lastMessage(): FormatMessageResponse | undefined { // get last 5 messages, sort, return the latest // get a slice of the last 5 let min = this.state.latestMessages.length - 5; @@ -878,17 +852,17 @@ export class Channel} data - * @return {Promise | null>} Description + * @param {MarkReadOptions} data + * @return {Promise} Description */ - async markRead(data: MarkReadOptions = {}) { + async markRead(data: MarkReadOptions = {}) { this._checkInitialized(); if (!this.getConfig()?.read_events && !this.getClient()._isUsingServerAuth()) { return Promise.resolve(null); } - return await this.getClient().post>(this._channelURL() + '/read', { + return await this.getClient().post(this._channelURL() + '/read', { ...data, }); } @@ -896,10 +870,10 @@ export class Channel} data + * @param {MarkUnreadOptions} data * @return {APIResponse} An API response */ - async markUnread(data: MarkUnreadOptions) { + async markUnread(data: MarkUnreadOptions) { this._checkInitialized(); if (!this.getConfig()?.read_events && !this.getClient()._isUsingServerAuth()) { @@ -929,11 +903,11 @@ export class Channel} options additional options for the query endpoint + * @param {ChannelQueryOptions} options additional options for the query endpoint * - * @return {Promise>} The server response + * @return {Promise} The server response */ - async watch(options?: ChannelQueryOptions) { + async watch(options?: ChannelQueryOptions) { const defaultOptions = { state: true, watch: true, @@ -981,17 +955,17 @@ export class Channel; user_id?: string }} options Pagination params, ie {limit:10, id_lte: 10} + * @param {MessagePaginationOptions & { user?: UserResponse; user_id?: string }} options Pagination params, ie {limit:10, id_lte: 10} * - * @return {Promise>} A response with a list of messages + * @return {Promise} A response with a list of messages */ async getReplies( parent_id: string, - options: MessagePaginationOptions & { user?: UserResponse; user_id?: string }, + options: MessagePaginationOptions & { user?: UserResponse; user_id?: string }, sort?: { created_at: AscDesc }[], ) { const normalizedSort = sort ? normalizeQuerySort(sort) : undefined; - const data = await this.getClient().get>( + const data = await this.getClient().get( this.getClient().baseURL + `/messages/${encodeURIComponent(parent_id)}/replies`, { sort: normalizedSort, @@ -1010,24 +984,21 @@ export class Channel; user_id?: string }} options Pagination params, ie {limit:10, id_lte: 10} + * @param {PinnedMessagePaginationOptions & { user?: UserResponse; user_id?: string }} options Pagination params, ie {limit:10, id_lte: 10} * @param {PinnedMessagesSort} sort defines sorting direction of pinned messages * - * @return {Promise>} A response with a list of messages + * @return {Promise} A response with a list of messages */ async getPinnedMessages( - options: PinnedMessagePaginationOptions & { user?: UserResponse; user_id?: string }, + options: PinnedMessagePaginationOptions & { user?: UserResponse; user_id?: string }, sort: PinnedMessagesSort = [], ) { - return await this.getClient().get>( - this._channelURL() + '/pinned_messages', - { - payload: { - ...options, - sort: normalizeQuerySort(sort), - }, + return await this.getClient().get(this._channelURL() + '/pinned_messages', { + payload: { + ...options, + sort: normalizeQuerySort(sort), }, - ); + }); } /** @@ -1036,10 +1007,10 @@ export class Channel>} Server response + * @return {Promise} Server response */ getReactions(message_id: string, options: { limit?: number; offset?: number }) { - return this.getClient().get>( + return this.getClient().get( this.getClient().baseURL + `/messages/${encodeURIComponent(message_id)}/reactions`, { ...options, @@ -1052,10 +1023,10 @@ export class Channel>} Server response + * @return {Promise} Server response */ getMessagesById(messageIds: string[]) { - return this.getClient().get>(this._channelURL() + '/messages', { + return this.getClient().get(this._channelURL() + '/messages', { ids: messageIds.join(','), }); } @@ -1071,7 +1042,7 @@ export class Channel | MessageResponse) { + _countMessageAsUnread(message: FormatMessageResponse | MessageResponse) { if (message.shadowed) return false; if (message.silent) return false; if (message.parent_id && !message.show_in_channel) return false; @@ -1079,8 +1050,9 @@ export class Channel>} The Server Response + * @return {Promise} The Server Response * */ - create = async (options?: ChannelQueryOptions) => { + create = async (options?: ChannelQueryOptions) => { const defaultOptions = { ...options, watch: false, @@ -1150,15 +1122,12 @@ export class Channel} options The query options + * @param {ChannelQueryOptions} options The query options * @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 * - * @return {Promise>} Returns a query response + * @return {Promise} Returns a query response */ - async query( - options?: ChannelQueryOptions, - messageSetToAddToIfDoesNotExist: MessageSetType = 'current', - ) { + async query(options?: ChannelQueryOptions, messageSetToAddToIfDoesNotExist: MessageSetType = 'current') { // Make sure we wait for the connect promise if there is a pending one await this.getClient().wsPromise; @@ -1167,7 +1136,7 @@ export class Channel>(queryURL + '/query', { + const state = await this.getClient().post(queryURL + '/query', { data: this._data, state: true, ...options, @@ -1214,7 +1183,7 @@ export class Channel} options + * @param {BanUserOptions} options * @returns {Promise} */ - async banUser(targetUserID: string, options: BanUserOptions) { + async banUser(targetUserID: string, options: BanUserOptions) { this._checkInitialized(); return await this.getClient().banUser(targetUserID, { ...options, @@ -1301,10 +1270,10 @@ export class Channel} options + * @param {BanUserOptions} options * @returns {Promise} */ - async shadowBan(targetUserID: string, options: BanUserOptions) { + async shadowBan(targetUserID: string, options: BanUserOptions) { this._checkInitialized(); return await this.getClient().shadowBan(targetUserID, { ...options, @@ -1358,15 +1327,12 @@ export class Channel {console.log(event.type)}) * - * @param {EventHandler | EventTypes} callbackOrString The event type to listen for (optional) - * @param {EventHandler} [callbackOrNothing] The callback to call + * @param {EventHandler | EventTypes} callbackOrString The event type to listen for (optional) + * @param {EventHandler} [callbackOrNothing] The callback to call */ - on(eventType: EventTypes, callback: EventHandler): { unsubscribe: () => void }; - on(callback: EventHandler): { unsubscribe: () => void }; - on( - callbackOrString: EventHandler | EventTypes, - callbackOrNothing?: EventHandler, - ): { unsubscribe: () => void } { + on(eventType: EventTypes, callback: EventHandler): { unsubscribe: () => void }; + on(callback: EventHandler): { unsubscribe: () => void }; + on(callbackOrString: EventHandler | EventTypes, callbackOrNothing?: EventHandler): { unsubscribe: () => void } { const key = callbackOrNothing ? (callbackOrString as string) : 'all'; const callback = callbackOrNothing ? callbackOrNothing : callbackOrString; if (!(key in this.listeners)) { @@ -1395,12 +1361,9 @@ export class Channel): void; - off(callback: EventHandler): void; - off( - callbackOrString: EventHandler | EventTypes, - callbackOrNothing?: EventHandler, - ): void { + off(eventType: EventTypes, callback: EventHandler): void; + off(callback: EventHandler): void; + off(callbackOrString: EventHandler | EventTypes, callbackOrNothing?: EventHandler): void { const key = callbackOrNothing ? (callbackOrString as string) : 'all'; const callback = callbackOrNothing ? callbackOrNothing : callbackOrString; if (!(key in this.listeners)) { @@ -1415,7 +1378,7 @@ export class Channel) { + _handleChannelEvent(event: Event) { const channel = this; this._client.logger( 'info', @@ -1538,8 +1501,7 @@ export class Channel { - if (truncatedAt > +createdAt) - channelState.removePinnedMessage({ id } as MessageResponse); + if (truncatedAt > +createdAt) channelState.removePinnedMessage({ id } as MessageResponse); }); } else { channelState.clearMessages(); @@ -1666,7 +1628,7 @@ export class Channel) => { + _callChannelListeners = (event: Event) => { const channel = this; // gather and call the listeners const listeners = []; @@ -1706,10 +1668,7 @@ export class Channel, - messageSetToAddToIfDoesNotExist: MessageSetType = 'latest', - ) { + _initializeState(state: ChannelAPIResponse, messageSetToAddToIfDoesNotExist: MessageSetType = 'latest') { const { state: clientState, user, userID } = this.getClient(); // add the members and users @@ -1786,7 +1745,7 @@ export class Channel) { + _extendEventWithOwnReactions(event: Event) { if (!event.message) { return; } @@ -1800,7 +1759,7 @@ export class Channel[]; + members: ChannelMemberResponse[]; /** * If set to `true` then `ChannelState.members` will be overriden with the newly * provided `members`, setting this property to `false` will merge current `ChannelState.members` @@ -1809,7 +1768,7 @@ export class Channel['members']>((membersById, member) => { + const newMembersById = members.reduce((membersById, member) => { if (member.user) { membersById[member.user.id] = member; } diff --git a/src/channel_manager.ts b/src/channel_manager.ts index 7a419a0af..0129b220b 100644 --- a/src/channel_manager.ts +++ b/src/channel_manager.ts @@ -1,13 +1,5 @@ import type { StreamChat } from './client'; -import type { - DefaultGenerics, - ExtendableGenerics, - Event, - ChannelOptions, - ChannelStateOptions, - ChannelFilters, - ChannelSort, -} from './types'; +import type { Event, ChannelOptions, ChannelStateOptions, ChannelFilters, ChannelSort } from './types'; import { StateStore, ValueOrPatch, isPatch } from './store'; import { Channel } from './channel'; import { @@ -22,17 +14,17 @@ import { uniqBy, } from './utils'; -export type ChannelManagerPagination = { - filters: ChannelFilters; +export type ChannelManagerPagination = { + filters: ChannelFilters; hasNext: boolean; isLoading: boolean; isLoadingNext: boolean; options: ChannelOptions; - sort: ChannelSort; + sort: ChannelSort; }; -export type ChannelManagerState = { - channels: Channel[]; +export type ChannelManagerState = { + channels: Channel[]; /** * This value will become true the first time queryChannels is successfully executed and * will remain false otherwise. It's used as a control property regarding whether the list @@ -40,23 +32,17 @@ export type ChannelManagerState; + pagination: ChannelManagerPagination; }; -export type ChannelSetterParameterType = ValueOrPatch< - ChannelManagerState['channels'] ->; -export type ChannelSetterType = ( - arg: ChannelSetterParameterType, -) => void; +export type ChannelSetterParameterType = ValueOrPatch; +export type ChannelSetterType = (arg: ChannelSetterParameterType) => void; export type GenericEventHandlerType = ( ...args: T ) => void | (() => void) | ((...args: T) => Promise) | Promise; -export type EventHandlerType = GenericEventHandlerType<[Event]>; -export type EventHandlerOverrideType = GenericEventHandlerType< - [ChannelSetterType, Event] ->; +export type EventHandlerType = GenericEventHandlerType<[Event]>; +export type EventHandlerOverrideType = GenericEventHandlerType<[ChannelSetterType, Event]>; export type ChannelManagerEventTypes = | 'notification.added_to_channel' @@ -80,8 +66,8 @@ export type ChannelManagerEventHandlerNames = | 'notificationNewMessageHandler' | 'notificationRemovedFromChannelHandler'; -export type ChannelManagerEventHandlerOverrides = Partial< - Record> +export type ChannelManagerEventHandlerOverrides = Partial< + Record >; export const channelManagerEventToHandlerMapping: { @@ -144,12 +130,12 @@ export const DEFAULT_CHANNEL_MANAGER_PAGINATION_OPTIONS = { * * @internal */ -export class ChannelManager { - public readonly state: StateStore>; - private client: StreamChat; +export class ChannelManager { + public readonly state: StateStore; + private client: StreamChat; private unsubscribeFunctions: Set<() => void> = new Set(); - private eventHandlers: Map> = new Map(); - private eventHandlerOverrides: Map> = new Map(); + private eventHandlers: Map = new Map(); + private eventHandlerOverrides: Map = new Map(); private options: ChannelManagerOptions = {}; private stateOptions: ChannelStateOptions = {}; @@ -158,12 +144,12 @@ export class ChannelManager { eventHandlerOverrides = {}, options = {}, }: { - client: StreamChat; - eventHandlerOverrides?: ChannelManagerEventHandlerOverrides; + client: StreamChat; + eventHandlerOverrides?: ChannelManagerEventHandlerOverrides; options?: ChannelManagerOptions; }) { this.client = client; - this.state = new StateStore>({ + this.state = new StateStore({ channels: [], pagination: { isLoading: false, @@ -178,7 +164,7 @@ export class ChannelManager { this.setEventHandlerOverrides(eventHandlerOverrides); this.setOptions(options); this.eventHandlers = new Map( - Object.entries>({ + Object.entries({ channelDeletedHandler: this.channelDeletedHandler, channelHiddenHandler: this.channelHiddenHandler, channelVisibleHandler: this.channelVisibleHandler, @@ -191,7 +177,7 @@ export class ChannelManager { ); } - public setChannels = (valueOrFactory: ChannelSetterParameterType) => { + public setChannels = (valueOrFactory: ChannelSetterParameterType) => { this.state.next((current) => { const { channels: currentChannels } = current; const newChannels = isPatch(valueOrFactory) ? valueOrFactory(currentChannels) : valueOrFactory; @@ -205,16 +191,16 @@ export class ChannelManager { }); }; - public setEventHandlerOverrides = (eventHandlerOverrides: ChannelManagerEventHandlerOverrides = {}) => { + public setEventHandlerOverrides = (eventHandlerOverrides: ChannelManagerEventHandlerOverrides = {}) => { const truthyEventHandlerOverrides = Object.entries(eventHandlerOverrides).reduce< - Partial> + Partial >((acc, [key, value]) => { if (value) { - acc[key as keyof ChannelManagerEventHandlerOverrides] = value; + acc[key as keyof ChannelManagerEventHandlerOverrides] = value; } return acc; }, {}); - this.eventHandlerOverrides = new Map(Object.entries>(truthyEventHandlerOverrides)); + this.eventHandlerOverrides = new Map(Object.entries(truthyEventHandlerOverrides)); }; public setOptions = (options: ChannelManagerOptions = {}) => { @@ -222,8 +208,8 @@ export class ChannelManager { }; public queryChannels = async ( - filters: ChannelFilters, - sort: ChannelSort = [], + filters: ChannelFilters, + sort: ChannelSort = [], options: ChannelOptions = {}, stateOptions: ChannelStateOptions = {}, ) => { @@ -294,7 +280,7 @@ export class ChannelManager { const newOptions = { ...options, offset: newOffset }; this.state.partialNext({ - channels: uniqBy>([...(channels || []), ...nextChannels], 'cid'), + channels: uniqBy([...(channels || []), ...nextChannels], 'cid'), pagination: { ...pagination, hasNext: (nextChannels?.length ?? 0) >= limit, @@ -313,7 +299,7 @@ export class ChannelManager { } }; - private notificationAddedToChannelHandler = async (event: Event) => { + private notificationAddedToChannelHandler = async (event: Event) => { const { id, type, members } = event?.channel ?? {}; if (!type || !this.options.allowNotLoadedChannelPromotionForEvent?.['notification.added_to_channel']) { @@ -349,7 +335,7 @@ export class ChannelManager { ); }; - private channelDeletedHandler = (event: Event) => { + private channelDeletedHandler = (event: Event) => { const { channels } = this.state.getLatestValue(); if (!channels) { return; @@ -368,7 +354,7 @@ export class ChannelManager { private channelHiddenHandler = this.channelDeletedHandler; - private newMessageHandler = (event: Event) => { + private newMessageHandler = (event: Event) => { const { pagination, channels } = this.state.getLatestValue(); if (!channels) { return; @@ -417,7 +403,7 @@ export class ChannelManager { ); }; - private notificationNewMessageHandler = async (event: Event) => { + private notificationNewMessageHandler = async (event: Event) => { const { id, type } = event?.channel ?? {}; if (!id || !type) { @@ -454,7 +440,7 @@ export class ChannelManager { ); }; - private channelVisibleHandler = async (event: Event) => { + private channelVisibleHandler = async (event: Event) => { const { channel_type: channelType, channel_id: channelId } = event; if (!channelType || !channelId) { @@ -493,7 +479,7 @@ export class ChannelManager { private notificationRemovedFromChannelHandler = this.channelDeletedHandler; - private memberUpdatedHandler = (event: Event) => { + private memberUpdatedHandler = (event: Event) => { const { pagination, channels } = this.state.getLatestValue(); const { filters, sort } = pagination; if ( @@ -557,7 +543,7 @@ export class ChannelManager { this.setChannels(newChannels); }; - private subscriptionOrOverride = (event: Event) => { + private subscriptionOrOverride = (event: Event) => { const handlerName = channelManagerEventToHandlerMapping[event.type as ChannelManagerEventTypes]; const defaultEventHandler = this.eventHandlers.get(handlerName); const eventHandlerOverride = this.eventHandlerOverrides.get(handlerName); diff --git a/src/channel_state.ts b/src/channel_state.ts index c29fcd71b..938254a5b 100644 --- a/src/channel_state.ts +++ b/src/channel_state.ts @@ -1,9 +1,7 @@ import { Channel } from './channel'; import { ChannelMemberResponse, - DefaultGenerics, Event, - ExtendableGenerics, FormatMessageResponse, MessageResponse, MessageSet, @@ -15,12 +13,12 @@ import { import { addToMessageList, formatMessage } from './utils'; import { DEFAULT_MESSAGE_SET_PAGINATION } from './constants'; -type ChannelReadStatus = Record< +type ChannelReadStatus = Record< string, { last_read: Date; unread_messages: number; - user: UserResponse; + user: UserResponse; first_unread_message_id?: string; last_read_message_id?: string; } @@ -29,19 +27,19 @@ type ChannelReadStatus { - _channel: Channel; +export class ChannelState { + _channel: Channel; watcher_count: number; - typing: Record>; - read: ChannelReadStatus; - pinnedMessages: Array['formatMessage']>>; - pending_messages: Array>; - threads: Record['formatMessage']>>>; - mutedUsers: Array>; - watchers: Record>; - members: Record>; + typing: Record; + read: ChannelReadStatus; + pinnedMessages: Array>; + pending_messages: Array; + threads: Record>>; + mutedUsers: Array; + watchers: Record; + members: Record; unreadCount: number; - membership: ChannelMemberResponse; + membership: ChannelMemberResponse; last_message_at: Date | null; /** * Flag which indicates if channel state contain latest/recent messages or no. @@ -58,7 +56,7 @@ export class ChannelState) { + constructor(channel: Channel) { this._channel = channel; this.watcher_count = 0; this.typing = {}; @@ -87,7 +85,7 @@ export class ChannelState s.isCurrent)?.messages || []; } - set messages(messages: Array['formatMessage']>>) { + set messages(messages: Array>) { const index = this.messageSets.findIndex((s) => s.isCurrent); this.messageSets[index].messages = messages; } @@ -100,7 +98,7 @@ export class ChannelState s.isLatest)?.messages || []; } - set latestMessages(messages: Array['formatMessage']>>) { + set latestMessages(messages: Array>) { const index = this.messageSets.findIndex((s) => s.isLatest); this.messageSets[index].messages = messages; } @@ -112,13 +110,13 @@ export class ChannelState} newMessage A new message + * @param {MessageResponse} newMessage A new message * @param {boolean} timestampChanged Whether updating a message with changed created_at value. * @param {boolean} addIfDoesNotExist Add message if it is not in the list, used to prevent out of order updated messages from being added. * @param {MessageSetType} messageSetToAddToIfDoesNotExist Which message set to add to if message is not in the list (only used if addIfDoesNotExist is true) */ addMessageSorted( - newMessage: MessageResponse, + newMessage: MessageResponse, timestampChanged = false, addIfDoesNotExist = true, messageSetToAddToIfDoesNotExist: MessageSetType = 'latest', @@ -136,14 +134,14 @@ export class ChannelState} message `MessageResponse` object + * @param {MessageResponse} message `MessageResponse` object */ - formatMessage = (message: MessageResponse) => formatMessage(message); + formatMessage = (message: MessageResponse) => formatMessage(message); /** * addMessagesSorted - Add the list of messages to state and resorts the messages * - * @param {Array>} newMessages A list of messages + * @param {Array} newMessages A list of messages * @param {boolean} timestampChanged Whether updating messages with changed created_at value. * @param {boolean} initializing Whether channel is being initialized. * @param {boolean} addIfDoesNotExist Add message if it is not in the list, used to prevent out of order updated messages from being added. @@ -151,7 +149,7 @@ export class ChannelState[], + newMessages: MessageResponse[], timestampChanged = false, initializing = false, addIfDoesNotExist = true, @@ -172,11 +170,11 @@ export class ChannelState this happens when we perform merging of message sets // This will be also true for message previews used by some SDKs const isMessageFormatted = messagesToAdd[i].created_at instanceof Date; - let message: ReturnType['formatMessage']>; + let message: ReturnType; if (isMessageFormatted) { - message = messagesToAdd[i] as ReturnType['formatMessage']>; + message = messagesToAdd[i] as ReturnType; } else { - message = this.formatMessage(messagesToAdd[i] as MessageResponse); + message = this.formatMessage(messagesToAdd[i] as MessageResponse); if (message.user && this._channel?.cid) { /** @@ -247,10 +245,10 @@ export class ChannelState>} pinnedMessages A list of pinned messages + * @param {Array} pinnedMessages A list of pinned messages * */ - addPinnedMessages(pinnedMessages: MessageResponse[]) { + addPinnedMessages(pinnedMessages: MessageResponse[]) { for (let i = 0; i < pinnedMessages.length; i += 1) { this.addPinnedMessage(pinnedMessages[i]); } @@ -259,10 +257,10 @@ export class ChannelState} pinnedMessage message to update + * @param {MessageResponse} pinnedMessage message to update * */ - addPinnedMessage(pinnedMessage: MessageResponse) { + addPinnedMessage(pinnedMessage: MessageResponse) { this.pinnedMessages = this._addToMessageList( this.pinnedMessages, this.formatMessage(pinnedMessage), @@ -274,19 +272,15 @@ export class ChannelState} message message to remove + * @param {MessageResponse} message message to remove * */ - removePinnedMessage(message: MessageResponse) { + removePinnedMessage(message: MessageResponse) { const { result } = this.removeMessageFromArray(this.pinnedMessages, message); this.pinnedMessages = result; } - addReaction( - reaction: ReactionResponse, - message?: MessageResponse, - enforce_unique?: boolean, - ) { + addReaction(reaction: ReactionResponse, message?: MessageResponse, enforce_unique?: boolean) { if (!message) return; const messageWithReaction = message; this._updateMessage(message, (msg) => { @@ -297,8 +291,8 @@ export class ChannelState[] | null | undefined, - reaction: ReactionResponse, + ownReactions: ReactionResponse[] | null | undefined, + reaction: ReactionResponse, enforce_unique?: boolean, ) { if (enforce_unique) { @@ -315,17 +309,14 @@ export class ChannelState[] | null | undefined, - reaction: ReactionResponse, - ) { + _removeOwnReactionFromMessage(ownReactions: ReactionResponse[] | null | undefined, reaction: ReactionResponse) { if (ownReactions) { return ownReactions.filter((item) => item.user_id !== reaction.user_id || item.type !== reaction.type); } return ownReactions; } - removeReaction(reaction: ReactionResponse, message?: MessageResponse) { + removeReaction(reaction: ReactionResponse, message?: MessageResponse) { if (!message) return; const messageWithReaction = message; this._updateMessage(message, (msg) => { @@ -335,23 +326,17 @@ export class ChannelState; - remove?: boolean; - }) { - const parseMessage = (m: ReturnType['formatMessage']>) => + _updateQuotedMessageReferences({ message, remove }: { message: MessageResponse; remove?: boolean }) { + const parseMessage = (m: ReturnType) => (({ ...m, created_at: m.created_at.toISOString(), pinned_at: m.pinned_at?.toISOString(), updated_at: m.updated_at?.toISOString(), - } as unknown) as MessageResponse); + } as unknown) as MessageResponse); - const update = (messages: FormatMessageResponse[]) => { - const updatedMessages = messages.reduce[]>((acc, msg) => { + const update = (messages: FormatMessageResponse[]) => { + const updatedMessages = messages.reduce((acc, msg) => { if (msg.quoted_message_id === message.id) { acc.push({ ...parseMessage(msg), quoted_message: remove ? { ...message, attachments: [] } : message }); } @@ -368,7 +353,7 @@ export class ChannelState) { + removeQuotedMessageReferences(message: MessageResponse) { this._updateQuotedMessageReferences({ message, remove: true }); } @@ -384,9 +369,7 @@ export class ChannelState['formatMessage']>, - ) => ReturnType['formatMessage']>, + updateFunc: (msg: ReturnType) => ReturnType, ) { const { parent_id, show_in_channel, pinned } = message; @@ -434,15 +417,15 @@ export class ChannelState['formatMessage']>>} messages A list of messages + * @param {Array>} messages A list of messages * @param message * @param {boolean} timestampChanged Whether updating a message with changed created_at value. * @param {string} sortBy field name to use to sort the messages by * @param {boolean} addIfDoesNotExist Add message if it is not in the list, used to prevent out of order updated messages from being added. */ _addToMessageList( - messages: Array['formatMessage']>>, - message: ReturnType['formatMessage']>, + messages: Array>, + message: ReturnType, timestampChanged = false, sortBy: 'pinned_at' | 'created_at' = 'created_at', addIfDoesNotExist = true, @@ -483,7 +466,7 @@ export class ChannelState['formatMessage']>>, + msgArray: Array>, msg: { id: string; parent_id?: string }, ) => { const result = msgArray.filter((message) => !(!!message.id && !!msg.id && message.id === msg.id)); @@ -494,13 +477,10 @@ export class ChannelState} user + * @param {UserResponse} user */ - updateUserMessages = (user: UserResponse) => { - const _updateUserMessages = ( - messages: Array['formatMessage']>>, - user: UserResponse, - ) => { + updateUserMessages = (user: UserResponse) => { + const _updateUserMessages = (messages: Array>, user: UserResponse) => { for (let i = 0; i < messages.length; i++) { const m = messages[i]; if (m.user?.id === user.id) { @@ -521,13 +501,13 @@ export class ChannelState} user + * @param {UserResponse} user * @param {boolean} hardDelete */ - deleteUserMessages = (user: UserResponse, hardDelete = false) => { + deleteUserMessages = (user: UserResponse, hardDelete = false) => { const _deleteUserMessages = ( - messages: Array['formatMessage']>>, - user: UserResponse, + messages: Array>, + user: UserResponse, hardDelete = false, ) => { for (let i = 0; i < messages.length; i++) { @@ -556,7 +536,7 @@ export class ChannelState['formatMessage']>; + } as unknown) as ReturnType; } else { messages[i] = { ...m, @@ -603,7 +583,7 @@ export class ChannelState); + } as Event); } } } @@ -663,7 +643,7 @@ export class ChannelState['formatMessage']>} Returns the message, or undefined if the message wasn't found + * @return {ReturnType} Returns the message, or undefined if the message wasn't found */ findMessage(messageId: string, parentMessageId?: string) { if (parentMessageId) { @@ -699,14 +679,11 @@ export class ChannelState[], + newMessages: MessageResponse[], addIfDoesNotExist = true, messageSetToAddToIfDoesNotExist: MessageSetType = 'current', ) { - let messagesToAdd: ( - | MessageResponse - | ReturnType['formatMessage']> - )[] = newMessages; + let messagesToAdd: (MessageResponse | ReturnType)[] = newMessages; let targetMessageSetIndex!: number; if (addIfDoesNotExist) { const overlappingMessageSetIndices = this.messageSets diff --git a/src/client.ts b/src/client.ts index e82ef9b16..d9effaf66 100644 --- a/src/client.ts +++ b/src/client.ts @@ -13,7 +13,7 @@ import { TokenManager } from './token_manager'; import { WSConnectionFallback } from './connection_fallback'; import { Campaign } from './campaign'; import { Segment } from './segment'; -import { isErrorResponse, isWSFailure } from './errors'; +import { APIError, isErrorResponse, isWSFailure } from './errors'; import { addFileToFormData, axiosParamsSerializer, @@ -74,7 +74,6 @@ import { CreatePollOptionAPIResponse, CustomPermissionOptions, DeactivateUsersOptions, - DefaultGenerics, DeleteChannelsResponse, DeleteCommandResponse, DeleteUserOptions, @@ -89,7 +88,6 @@ import { ExportChannelStatusResponse, ExportUsersRequest, ExportUsersResponse, - ExtendableGenerics, FlagMessageResponse, FlagReportsFilters, FlagReportsPaginationOptions, @@ -118,7 +116,6 @@ import { ListImportsResponse, Logger, MarkChannelsReadOptions, - Message, MessageFilters, MessageFlagsFilters, MessageFlagsPaginationOptions, @@ -224,15 +221,15 @@ function isString(x: unknown): x is string { return typeof x === 'string' || x instanceof String; } -export class StreamChat { +export class StreamChat { private static _instance?: unknown | StreamChat; // type is undefined|StreamChat, unknown is due to TS limitations with statics - _user?: OwnUserResponse | UserResponse; + _user?: OwnUserResponse | UserResponse; activeChannels: { - [key: string]: Channel; + [key: string]: Channel; }; - threads: ThreadManager; - polls: PollManager; + threads: ThreadManager; + polls: PollManager; anonymous: boolean; persistUserOnConnectionFailure?: boolean; axiosInstance: AxiosInstance; @@ -240,9 +237,9 @@ export class StreamChat; + configs: Configs; key: string; - listeners: Record) => void>>; + listeners: Record void>>; logger: Logger; /** * When network is recovered, we re-query the active channels on client. But in single query, you can recover @@ -254,22 +251,22 @@ export class StreamChat; - mutedChannels: ChannelMute[]; - mutedUsers: Mute[]; + moderation: Moderation; + mutedChannels: ChannelMute[]; + mutedUsers: Mute[]; node: boolean; options: StreamChatOptions; secret?: string; - setUserPromise: ConnectAPIResponse | null; - state: ClientState; - tokenManager: TokenManager; - user?: OwnUserResponse | UserResponse; + setUserPromise: ConnectAPIResponse | null; + state: ClientState; + tokenManager: TokenManager; + user?: OwnUserResponse | UserResponse; userAgent?: string; userID?: string; wsBaseURL?: string; - wsConnection: StableWSConnection | null; - wsFallback?: WSConnectionFallback; - wsPromise: ConnectAPIResponse | null; + wsConnection: StableWSConnection | null; + wsFallback?: WSConnectionFallback; + wsPromise: ConnectAPIResponse | null; consecutiveFailures: number; insightMetrics: InsightMetrics; defaultWSTimeoutWithFallback: number; @@ -301,7 +298,7 @@ export class StreamChat({ client: this }); + this.state = new ClientState({ client: this }); // a list of channels to hide ws events from this.mutedChannels = []; this.mutedUsers = []; @@ -446,29 +443,22 @@ export class StreamChatsecret is optional and only used in server side mode * StreamChat.getInstance('api_key', "secret", { httpsAgent: customAgent }) */ - public static getInstance( - key: string, - options?: StreamChatOptions, - ): StreamChat; - public static getInstance( - key: string, - secret?: string, - options?: StreamChatOptions, - ): StreamChat; - public static getInstance( + public static getInstance(key: string, options?: StreamChatOptions): StreamChat; + public static getInstance(key: string, secret?: string, options?: StreamChatOptions): StreamChat; + public static getInstance( key: string, secretOrOptions?: StreamChatOptions | string, options?: StreamChatOptions, - ): StreamChat { + ): StreamChat { if (!StreamChat._instance) { if (typeof secretOrOptions === 'string') { - StreamChat._instance = new StreamChat(key, secretOrOptions, options); + StreamChat._instance = new StreamChat(key, secretOrOptions, options); } else { - StreamChat._instance = new StreamChat(key, secretOrOptions); + StreamChat._instance = new StreamChat(key, secretOrOptions); } } - return StreamChat._instance as StreamChat; + return StreamChat._instance as StreamChat; } devToken(userID: string) { @@ -491,15 +481,12 @@ export class StreamChat | UserResponse} user Data about this user. IE {name: "john"} + * @param {OwnUserResponse | UserResponse} user Data about this user. IE {name: "john"} * @param {TokenOrProvider} userTokenOrProvider Token or provider * - * @return {ConnectAPIResponse} Returns a promise that resolves when the connection is setup + * @return {ConnectAPIResponse} Returns a promise that resolves when the connection is setup */ - connectUser = async ( - user: OwnUserResponse | UserResponse, - userTokenOrProvider: TokenOrProvider, - ) => { + connectUser = async (user: OwnUserResponse | UserResponse, userTokenOrProvider: TokenOrProvider) => { if (!user.id) { throw new Error('The "id" field on the user is missing'); } @@ -558,17 +545,17 @@ export class StreamChat | UserResponse} user Data about this user. IE {name: "john"} + * @param {OwnUserResponse | UserResponse} user Data about this user. IE {name: "john"} * @param {TokenOrProvider} userTokenOrProvider Token or provider * - * @return {ConnectAPIResponse} Returns a promise that resolves when the connection is setup + * @return {ConnectAPIResponse} Returns a promise that resolves when the connection is setup */ setUser = this.connectUser; - _setToken = (user: UserResponse, userTokenOrProvider: TokenOrProvider) => + _setToken = (user: UserResponse, userTokenOrProvider: TokenOrProvider) => this.tokenManager.setTokenOrProvider(userTokenOrProvider, user); - _setUser(user: OwnUserResponse | UserResponse) { + _setUser(user: OwnUserResponse | UserResponse) { /** * This one is used by the frontend. This is a copy of the current user object stored on backend. * It contains reserved properties and own user properties which are not present in `this._user`. @@ -614,7 +601,7 @@ export class StreamChat; + eventHandlerOverrides?: ChannelManagerEventHandlerOverrides; options?: ChannelManagerOptions; }) => { return new ChannelManager({ client: this, eventHandlerOverrides, options }); @@ -735,11 +722,11 @@ export class StreamChat[] = []; + const users: PartialUserUpdate[] = []; for (const userID of userIDs) { users.push({ id: userID, - set: >>{ + set: >{ revoke_tokens_issued_before: before, }, }); @@ -752,7 +739,7 @@ export class StreamChat>(this.baseURL + '/app'); + return await this.get(this.baseURL + '/app'); } /** @@ -870,7 +857,7 @@ export class StreamChat; + } as UserResponse; this._setToken(anonymousUser, ''); this._setUser(anonymousUser); @@ -886,18 +873,18 @@ export class StreamChat} user Data about this user. IE {name: "john"} + * @param {UserResponse} user Data about this user. IE {name: "john"} * - * @return {ConnectAPIResponse} Returns a promise that resolves when the connection is setup + * @return {ConnectAPIResponse} Returns a promise that resolves when the connection is setup */ - async setGuestUser(user: UserResponse) { - let response: { access_token: string; user: UserResponse } | undefined; + async setGuestUser(user: UserResponse) { + let response: { access_token: string; user: UserResponse } | undefined; this.anonymous = true; try { response = await this.post< APIResponse & { access_token: string; - user: UserResponse; + user: UserResponse; } >(this.baseURL + '/guest', { user }); } catch (e) { @@ -907,7 +894,7 @@ export class StreamChat, response.access_token); + return await this.connectUser(guestUser as UserResponse, response.access_token); } /** @@ -943,19 +930,16 @@ export class StreamChat {console.log(event.type)}) * - * @param {EventHandler | string} callbackOrString The event type to listen for (optional) - * @param {EventHandler} [callbackOrNothing] The callback to call + * @param {EventHandler | string} callbackOrString The event type to listen for (optional) + * @param {EventHandler} [callbackOrNothing] The callback to call * * @return {{ unsubscribe: () => void }} Description */ - on(callback: EventHandler): { unsubscribe: () => void }; - on(eventType: string, callback: EventHandler): { unsubscribe: () => void }; - on( - callbackOrString: EventHandler | string, - callbackOrNothing?: EventHandler, - ): { unsubscribe: () => void } { + on(callback: EventHandler): { unsubscribe: () => void }; + on(eventType: string, callback: EventHandler): { unsubscribe: () => void }; + on(callbackOrString: EventHandler | string, callbackOrNothing?: EventHandler): { unsubscribe: () => void } { const key = callbackOrNothing ? (callbackOrString as string) : 'all'; - const callback = callbackOrNothing ? callbackOrNothing : (callbackOrString as EventHandler); + const callback = callbackOrNothing ? callbackOrNothing : (callbackOrString as EventHandler); if (!(key in this.listeners)) { this.listeners[key] = []; } @@ -977,14 +961,11 @@ export class StreamChat): void; - off(eventType: string, callback: EventHandler): void; - off( - callbackOrString: EventHandler | string, - callbackOrNothing?: EventHandler, - ) { + off(callback: EventHandler): void; + off(eventType: string, callback: EventHandler): void; + off(callbackOrString: EventHandler | string, callbackOrNothing?: EventHandler) { const key = callbackOrNothing ? (callbackOrString as string) : 'all'; - const callback = callbackOrNothing ? callbackOrNothing : (callbackOrString as EventHandler); + const callback = callbackOrNothing ? callbackOrNothing : (callbackOrString as EventHandler); if (!(key in this.listeners)) { this.listeners[key] = []; } @@ -1114,7 +1095,7 @@ export class StreamChat, + user?: UserResponse, ) { const data = addFileToFormData(uri, name, contentType || 'multipart/form-data'); if (user != null) data.append('user', JSON.stringify(user)); @@ -1149,7 +1130,7 @@ export class StreamChat) => { + dispatchEvent = (event: Event) => { if (!event.received_at) event.received_at = new Date(); // client event handlers @@ -1174,16 +1155,16 @@ export class StreamChat { // dispatch the event to the channel listeners const jsonString = messageEvent.data as string; - const event = JSON.parse(jsonString) as Event; + const event = JSON.parse(jsonString) as Event; this.dispatchEvent(event); }; /** * Updates the members, watchers and read references of the currently active channels that contain this user * - * @param {UserResponse} user + * @param {UserResponse} user */ - _updateMemberWatcherReferences = (user: UserResponse) => { + _updateMemberWatcherReferences = (user: UserResponse) => { const refMap = this.state.userChannelReferences[user.id] || {}; for (const channelID in refMap) { const channel = this.activeChannels[channelID]; @@ -1213,9 +1194,9 @@ export class StreamChat} user + * @param {UserResponse} user */ - _updateUserMessageReferences = (user: UserResponse) => { + _updateUserMessageReferences = (user: UserResponse) => { const refMap = this.state.userChannelReferences[user.id] || {}; for (const channelID in refMap) { @@ -1238,10 +1219,10 @@ export class StreamChat} user + * @param {UserResponse} user * @param {boolean} hardDelete */ - _deleteUserMessageReference = (user: UserResponse, hardDelete = false) => { + _deleteUserMessageReference = (user: UserResponse, hardDelete = false) => { const refMap = this.state.userChannelReferences[user.id] || {}; for (const channelID in refMap) { @@ -1265,7 +1246,7 @@ export class StreamChat) => { + _handleUserEvent = (event: Event) => { if (!event.user) { return; } @@ -1273,8 +1254,8 @@ export class StreamChat; + const _user = { ...this._user } as NonNullable; // Remove deleted properties from user objects. for (const key in this.user) { @@ -1282,19 +1263,23 @@ export class StreamChat) { + _handleClientEvent(event: Event) { const client = this; const postListenerCallbacks = []; this.logger('info', `client:_handleClientEvent - Received event of type { ${event.type} }`, { @@ -1386,10 +1371,10 @@ export class StreamChat) => { + _callClientListeners = (event: Event) => { const client = this; // gather and call the listeners - const listeners: Array<(event: Event) => void> = []; + const listeners: Array<(event: Event) => void> = []; if (client.listeners.all) { listeners.push(...client.listeners.all); } @@ -1414,20 +1399,16 @@ export class StreamChat, - { last_message_at: -1 }, - { limit: 30 }, - ); + await this.queryChannels({ cid: { $in: cids } } as ChannelFilters, { last_message_at: -1 }, { limit: 30 }); this.logger('info', 'client:recoverState() - Querying channels finished', { tags: ['connection', 'client'] }); this.dispatchEvent({ type: 'connection.recovered', - } as Event); + } as Event); } else { this.dispatchEvent({ type: 'connection.recovered', - } as Event); + } as Event); } this.wsPromise = Promise.resolve(); @@ -1454,10 +1435,10 @@ export class StreamChat).setClient(this); - this.wsConnection = (this.options.wsConnection as unknown) as StableWSConnection; + ((this.options.wsConnection as unknown) as StableWSConnection).setClient(this); + this.wsConnection = (this.options.wsConnection as unknown) as StableWSConnection; } else { - this.wsConnection = new StableWSConnection({ + this.wsConnection = new StableWSConnection({ client: this, }); } @@ -1475,13 +1456,13 @@ export class StreamChat({ + this.wsFallback = new WSConnectionFallback({ client: this, }); return await this.wsFallback.connect(); @@ -1513,18 +1494,14 @@ export class StreamChat} filterConditions MongoDB style filter conditions - * @param {UserSort} sort Sort options, for instance [{last_active: -1}]. + * @param {UserFilters} filterConditions MongoDB style filter conditions + * @param {UserSort} sort Sort options, for instance [{last_active: -1}]. * When using multiple fields, make sure you use array of objects to guarantee field order, for instance [{last_active: -1}, {created_at: 1}] * @param {UserOptions} options Option object, {presence: true} * - * @return {Promise<{ users: Array> }>} User Query Response + * @return {Promise<{ users: Array }>} User Query Response */ - async queryUsers( - filterConditions: UserFilters, - sort: UserSort = [], - options: UserOptions = {}, - ) { + async queryUsers(filterConditions: UserFilters, sort: UserSort = [], options: UserOptions = {}) { const defaultOptions = { presence: false, }; @@ -1537,17 +1514,14 @@ export class StreamChat> }>( - this.baseURL + '/users', - { - payload: { - filter_conditions: filterConditions, - sort: normalizeQuerySort(sort), - ...defaultOptions, - ...options, - }, + const data = await this.get }>(this.baseURL + '/users', { + payload: { + filter_conditions: filterConditions, + sort: normalizeQuerySort(sort), + ...defaultOptions, + ...options, }, - ); + }); this.state.updateUsers(data.users); @@ -1561,7 +1535,7 @@ export class StreamChat>} Ban Query Response + * @return {Promise} Ban Query Response */ async queryBannedUsers( filterConditions: BannedUsersFilters = {}, @@ -1569,7 +1543,7 @@ export class StreamChat>(this.baseURL + '/query_banned_users', { + return await this.get(this.baseURL + '/query_banned_users', { payload: { filter_conditions: filterConditions, sort: normalizeQuerySort(sort), @@ -1584,11 +1558,11 @@ export class StreamChat>} Message Flags Response + * @return {Promise} Message Flags Response */ async queryMessageFlags(filterConditions: MessageFlagsFilters = {}, options: MessageFlagsPaginationOptions = {}) { // Return a list of message flags - return await this.get>(this.baseURL + '/moderation/flags/message', { + return await this.get(this.baseURL + '/moderation/flags/message', { payload: { filter_conditions: filterConditions, ...options }, }); } @@ -1596,18 +1570,18 @@ export class StreamChat} filterConditions object MongoDB style filters - * @param {ChannelSort} [sort] Sort options, for instance {created_at: -1}. + * @param {ChannelFilters} filterConditions object MongoDB style filters + * @param {ChannelSort} [sort] Sort options, for instance {created_at: -1}. * When using multiple fields, make sure you use array of objects to guarantee field order, for instance [{last_updated: -1}, {created_at: 1}] * @param {ChannelOptions} [options] Options object * @param {ChannelStateOptions} [stateOptions] State options object. These options will only be used for state management and won't be sent in the request. * - stateOptions.skipInitialization - Skips the initialization of the state for the channels matching the ids in the list. * - * @return {Promise<{ channels: Array>}> } search channels response + * @return {Promise<{ channels: Array}> } search channels response */ async queryChannels( - filterConditions: ChannelFilters, - sort: ChannelSort = [], + filterConditions: ChannelFilters, + sort: ChannelSort = [], options: ChannelOptions = {}, stateOptions: ChannelStateOptions = {}, ) { @@ -1631,7 +1605,7 @@ export class StreamChat>(this.baseURL + '/channels', payload); + const data = await this.post(this.baseURL + '/channels', payload); this.dispatchEvent({ type: 'channels.queried', @@ -1647,16 +1621,16 @@ export class StreamChat} filter object MongoDB style filters - * @param {ReactionSort} [sort] Sort options, for instance {created_at: -1}. + * @param {ReactionFilters} filter object MongoDB style filters + * @param {ReactionSort} [sort] Sort options, for instance {created_at: -1}. * @param {QueryReactionsOptions} [options] Pagination object * * @return {Promise<{ QueryReactionsAPIResponse } search channels response */ async queryReactions( messageID: string, - filter: ReactionFilters, - sort: ReactionSort = [], + filter: ReactionFilters, + sort: ReactionSort = [], options: QueryReactionsOptions = {}, ) { // Make sure we wait for the connect promise if there is a pending one @@ -1669,19 +1643,19 @@ export class StreamChat>( + return await this.post( this.baseURL + '/messages/' + encodeURIComponent(messageID) + '/reactions', payload, ); } hydrateActiveChannels( - channelsFromApi: ChannelAPIResponse[] = [], + channelsFromApi: ChannelAPIResponse[] = [], stateOptions: ChannelStateOptions = {}, queryChannelsOptions?: ChannelOptions, ) { const { skipInitialization, offlineMode = false } = stateOptions; - const channels: Channel[] = []; + const channels: Channel[] = []; for (const channelState of channelsFromApi) { this._addChannelConfig(channelState.channel); @@ -1723,24 +1697,20 @@ export class StreamChat} filterConditions MongoDB style filter conditions - * @param {MessageFilters | string} query search query or object MongoDB style filters - * @param {SearchOptions} [options] Option object, {user_id: 'tommaso'} + * @param {ChannelFilters} filterConditions MongoDB style filter conditions + * @param {MessageFilters | string} query search query or object MongoDB style filters + * @param {SearchOptions} [options] Option object, {user_id: 'tommaso'} * - * @return {Promise>} search messages response + * @return {Promise} search messages response */ - async search( - filterConditions: ChannelFilters, - query: string | MessageFilters, - options: SearchOptions = {}, - ) { + async search(filterConditions: ChannelFilters, query: string | MessageFilters, options: SearchOptions = {}) { if (options.offset && options.next) { throw Error(`Cannot specify offset with next`); } - const payload: SearchPayload = { + const payload: SearchPayload = { filter_conditions: filterConditions, ...options, - sort: options.sort ? normalizeQuerySort>(options.sort) : undefined, + sort: options.sort ? normalizeQuerySort(options.sort) : undefined, }; if (typeof query === 'string') { payload.query = query; @@ -1753,7 +1723,7 @@ export class StreamChat>(this.baseURL + '/search', { payload }); + return await this.get(this.baseURL + '/search', { payload }); } /** @@ -1798,10 +1768,10 @@ export class StreamChat[]} Array of devices + * @return {Device[]} Array of devices */ async getDevices(userID?: string) { - return await this.get[] }>( + return await this.get( this.baseURL + '/devices', userID ? { user_id: userID } : {}, ); @@ -1878,7 +1848,7 @@ export class StreamChat) { + _addChannelConfig({ cid, config }: ChannelResponse) { if (this._cacheEnabled()) { this.configs[cid] = config; } @@ -1893,28 +1863,28 @@ export class StreamChat | null} [channelIDOrCustom] The channel ID, you can leave this out if you want to create a conversation channel + * @param {string | ChannelData | null} [channelIDOrCustom] The channel ID, you can leave this out if you want to create a conversation channel * @param {object} [custom] Custom data to attach to the channel * * @return {channel} The channel object, initialize it using channel.watch() */ - channel( - channelType: string, - channelID?: string | null, - custom?: ChannelData, - ): Channel; - channel(channelType: string, custom?: ChannelData): Channel; - channel( - channelType: string, - channelIDOrCustom?: string | ChannelData | null, - custom: ChannelData = {} as ChannelData, - ) { + channel(channelType: string, channelID?: string | null, custom?: ChannelData): Channel; + channel(channelType: string, custom?: ChannelData): Channel; + channel(channelType: string, channelIDOrCustom?: string | ChannelData | null, custom: ChannelData = {}) { if (!this.userID && !this._isUsingServerAuth()) { throw Error('Call connectUser or connectAnonymousUser before creating a channel'); } + if ( + this._isUsingServerAuth() && + typeof custom?.created_by_id !== 'string' && + typeof custom?.created_by?.id !== 'string' + ) { + throw new Error('Either `created_by` (with `id` property) or `created_by_id` are required'); + } + if (~channelType.indexOf(':')) { - throw Error(`Invalid channel group ${channelType}, can't contain the : character`); + throw new Error(`Invalid channel group ${channelType}, can't contain the : character`); } // support channel("messaging", {options}) @@ -1922,7 +1892,7 @@ export class StreamChat(this, channelType, undefined, custom); + return new Channel(this, channelType, undefined, custom); } return this.getChannelById(channelType, channelIDOrCustom, custom); @@ -1953,10 +1923,10 @@ export class StreamChat) => { + getChannelByMembers = (channelType: string, custom: ChannelData) => { // Check if the channel already exists. // Only allow 1 channel object per cid - const memberIds = (custom.members ?? []).map((member: string | NewMemberPayload) => + const memberIds = (custom.members ?? []).map((member: string | NewMemberPayload) => typeof member === 'string' ? member : member.user_id ?? '', ); const membersStr = memberIds.sort().join(','); @@ -1989,7 +1959,7 @@ export class StreamChat(this, channelType, undefined, custom); + const channel = new Channel(this, channelType, undefined, custom); // For the time being set the key as membersStr, since we don't know the cid yet. // In channel.query, we will replace it with 'cid'. @@ -2016,7 +1986,7 @@ export class StreamChat) => { + getChannelById = (channelType: string, channelID: string, custom: ChannelData) => { if (typeof channelID === 'string' && ~channelID.indexOf(':')) { throw Error(`Invalid channel id ${channelID}, can't contain the : character`); } @@ -2031,7 +2001,7 @@ export class StreamChat(this, channelType, channelID, custom); + const channel = new Channel(this, channelType, channelID, custom); if (this._cacheEnabled()) { this.activeChannels[channel.cid] = channel; } @@ -2042,24 +2012,24 @@ export class StreamChat} partialUserObject which should contain id and any of "set" or "unset" params; + * @param {PartialUserUpdate} partialUserObject which should contain id and any of "set" or "unset" params; * example: {id: "user1", set:{field: value}, unset:["field2"]} * - * @return {Promise<{ users: { [key: string]: UserResponse } }>} list of updated users + * @return {Promise<{ users: { [key: string]: UserResponse } }>} list of updated users */ - async partialUpdateUser(partialUserObject: PartialUserUpdate) { + async partialUpdateUser(partialUserObject: PartialUserUpdate) { return await this.partialUpdateUsers([partialUserObject]); } /** * upsertUsers - Batch upsert the list of users * - * @param {UserResponse[]} users list of users + * @param {UserResponse[]} users list of users * - * @return {Promise<{ users: { [key: string]: UserResponse } }>} + * @return {Promise<{ users: { [key: string]: UserResponse } }>} */ - async upsertUsers(users: UserResponse[]) { - const userMap: { [key: string]: UserResponse } = {}; + async upsertUsers(users: UserResponse[]) { + const userMap: { [key: string]: UserResponse } = {}; for (const userObject of users) { if (!userObject.id) { throw Error('User ID is required when updating a user'); @@ -2069,7 +2039,7 @@ export class StreamChat }; + users: { [key: string]: UserResponse }; } >(this.baseURL + '/users', { users: userMap }); } @@ -2079,19 +2049,19 @@ export class StreamChat[]} users list of users - * @return {Promise<{ users: { [key: string]: UserResponse } }>} + * @param {UserResponse[]} users list of users + * @return {Promise<{ users: { [key: string]: UserResponse } }>} */ updateUsers = this.upsertUsers; /** * upsertUser - Update or Create the given user object * - * @param {UserResponse} userObject user object, the only required field is the user id. IE {id: "myuser"} is valid + * @param {UserResponse} userObject user object, the only required field is the user id. IE {id: "myuser"} is valid * - * @return {Promise<{ users: { [key: string]: UserResponse } }>} + * @return {Promise<{ users: { [key: string]: UserResponse } }>} */ - upsertUser(userObject: UserResponse) { + upsertUser(userObject: UserResponse) { return this.upsertUsers([userObject]); } @@ -2100,19 +2070,19 @@ export class StreamChat} userObject user object, the only required field is the user id. IE {id: "myuser"} is valid - * @return {Promise<{ users: { [key: string]: UserResponse } }>} + * @param {UserResponse} userObject user object, the only required field is the user id. IE {id: "myuser"} is valid + * @return {Promise<{ users: { [key: string]: UserResponse } }>} */ updateUser = this.upsertUser; /** * partialUpdateUsers - Batch partial update of users * - * @param {PartialUserUpdate[]} users list of partial update requests + * @param {PartialUserUpdate[]} users list of partial update requests * - * @return {Promise<{ users: { [key: string]: UserResponse } }>} + * @return {Promise<{ users: { [key: string]: UserResponse } }>} */ - async partialUpdateUsers(users: PartialUserUpdate[]) { + async partialUpdateUsers(users: PartialUserUpdate[]) { for (const userObject of users) { if (!userObject.id) { throw Error('User ID is required when updating a user'); @@ -2121,7 +2091,7 @@ export class StreamChat }; + users: { [key: string]: UserResponse }; } >(this.baseURL + '/users', { users }); } @@ -2135,7 +2105,7 @@ export class StreamChat } & { + APIResponse & { user: UserResponse } & { task_id?: string; } >(this.baseURL + `/users/${encodeURIComponent(userID)}`, params); @@ -2163,7 +2133,7 @@ export class StreamChat }>( + return await this.post( this.baseURL + `/users/${encodeURIComponent(userID)}/reactivate`, { ...options }, ); @@ -2190,7 +2160,7 @@ export class StreamChat }>( + return await this.post( this.baseURL + `/users/${encodeURIComponent(userID)}/deactivate`, { ...options }, ); @@ -2211,9 +2181,9 @@ export class StreamChat) { return await this.get< APIResponse & { - messages: MessageResponse[]; - reactions: ReactionResponse[]; - user: UserResponse; + messages: MessageResponse[]; + reactions: ReactionResponse[]; + user: UserResponse; } >(this.baseURL + `/users/${encodeURIComponent(userID)}/export`, { ...options }); } @@ -2221,10 +2191,10 @@ export class StreamChat} [options] + * @param {BanUserOptions} [options] * @returns {Promise} */ - async banUser(targetUserID: string, options?: BanUserOptions) { + async banUser(targetUserID: string, options?: BanUserOptions) { return await this.post(this.baseURL + '/moderation/ban', { target_user_id: targetUserID, ...options, @@ -2247,10 +2217,10 @@ export class StreamChat} [options] + * @param {BanUserOptions} [options] * @returns {Promise} */ - async shadowBan(targetUserID: string, options?: BanUserOptions) { + async shadowBan(targetUserID: string, options?: BanUserOptions) { return await this.banUser(targetUserID, { shadow: true, ...options, @@ -2291,11 +2261,11 @@ export class StreamChat} [options] - * @returns {Promise>} + * @param {MuteUserOptions} [options] + * @returns {Promise} */ - async muteUser(targetID: string, userID?: string, options: MuteUserOptions = {}) { - return await this.post>(this.baseURL + '/moderation/mute', { + async muteUser(targetID: string, userID?: string, options: MuteUserOptions = {}) { + return await this.post(this.baseURL + '/moderation/mute', { target_id: targetID, ...(userID ? { user_id: userID } : {}), ...options, @@ -2338,7 +2308,7 @@ export class StreamChat} */ async flagMessage(targetMessageID: string, options: { reason?: string; user_id?: string } = {}) { - return await this.post>(this.baseURL + '/moderation/flag', { + return await this.post(this.baseURL + '/moderation/flag', { target_message_id: targetMessageID, ...options, }); @@ -2351,7 +2321,7 @@ export class StreamChat} */ async flagUser(targetID: string, options: { reason?: string; user_id?: string } = {}) { - return await this.post>(this.baseURL + '/moderation/flag', { + return await this.post(this.baseURL + '/moderation/flag', { target_user_id: targetID, ...options, }); @@ -2364,7 +2334,7 @@ export class StreamChat} */ async unflagMessage(targetMessageID: string, options: { user_id?: string } = {}) { - return await this.post>(this.baseURL + '/moderation/unflag', { + return await this.post(this.baseURL + '/moderation/unflag', { target_message_id: targetMessageID, ...options, }); @@ -2377,7 +2347,7 @@ export class StreamChat} */ async unflagUser(targetID: string, options: { user_id?: string } = {}) { - return await this.post>(this.baseURL + '/moderation/unflag', { + return await this.post(this.baseURL + '/moderation/unflag', { target_user_id: targetID, ...options, }); @@ -2405,11 +2375,11 @@ export class StreamChat>} Flags Response + * @return {Promise} Flags Response */ async _queryFlags(filterConditions: FlagsFilters = {}, options: FlagsPaginationOptions = {}) { // Return a list of flags - return await this.post>(this.baseURL + '/moderation/flags', { + return await this.post(this.baseURL + '/moderation/flags', { filter_conditions: filterConditions, ...options, }); @@ -2426,11 +2396,11 @@ export class StreamChat>} Flag Reports Response + * @return {Promise} Flag Reports Response */ async _queryFlagReports(filterConditions: FlagReportsFilters = {}, options: FlagReportsPaginationOptions = {}) { // Return a list of message flags - return await this.post>(this.baseURL + '/moderation/reports', { + return await this.post(this.baseURL + '/moderation/reports', { filter_conditions: filterConditions, ...options, }); @@ -2451,13 +2421,10 @@ export class StreamChat>} */ async _reviewFlagReport(id: string, reviewResult: string, options: ReviewFlagReportOptions = {}) { - return await this.patch>( - this.baseURL + `/moderation/reports/${encodeURIComponent(id)}`, - { - review_result: reviewResult, - ...options, - }, - ); + return await this.patch(this.baseURL + `/moderation/reports/${encodeURIComponent(id)}`, { + review_result: reviewResult, + ...options, + }); } /** @@ -2481,7 +2448,7 @@ export class StreamChat} [data] + * @param {MarkAllReadOptions} [data] * * @return {Promise} */ @@ -2491,55 +2458,45 @@ export class StreamChat} [data] + * @param {MarkChannelsReadOptions } [data] * * @return {Promise} */ - async markChannelsRead(data: MarkChannelsReadOptions = {}) { + async markChannelsRead(data: MarkChannelsReadOptions = {}) { await this.post(this.baseURL + '/channels/read', { ...data }); } - createCommand(data: CreateCommandOptions) { - return this.post>(this.baseURL + '/commands', data); + createCommand(data: CreateCommandOptions) { + return this.post(this.baseURL + '/commands', data); } getCommand(name: string) { - return this.get>(this.baseURL + `/commands/${encodeURIComponent(name)}`); + return this.get(this.baseURL + `/commands/${encodeURIComponent(name)}`); } - updateCommand(name: string, data: UpdateCommandOptions) { - return this.put>( - this.baseURL + `/commands/${encodeURIComponent(name)}`, - data, - ); + updateCommand(name: string, data: UpdateCommandOptions) { + return this.put(this.baseURL + `/commands/${encodeURIComponent(name)}`, data); } deleteCommand(name: string) { - return this.delete>( - this.baseURL + `/commands/${encodeURIComponent(name)}`, - ); + return this.delete(this.baseURL + `/commands/${encodeURIComponent(name)}`); } listCommands() { - return this.get>(this.baseURL + `/commands`); + return this.get(this.baseURL + `/commands`); } - createChannelType(data: CreateChannelOptions) { + createChannelType(data: CreateChannelOptions) { const channelData = Object.assign({}, { commands: ['all'] }, data); - return this.post>(this.baseURL + '/channeltypes', channelData); + return this.post(this.baseURL + '/channeltypes', channelData); } getChannelType(channelType: string) { - return this.get>( - this.baseURL + `/channeltypes/${encodeURIComponent(channelType)}`, - ); + return this.get(this.baseURL + `/channeltypes/${encodeURIComponent(channelType)}`); } - updateChannelType(channelType: string, data: UpdateChannelOptions) { - return this.put>( - this.baseURL + `/channeltypes/${encodeURIComponent(channelType)}`, - data, - ); + updateChannelType(channelType: string, data: UpdateChannelOptions) { + return this.put(this.baseURL + `/channeltypes/${encodeURIComponent(channelType)}`, data); } deleteChannelType(channelType: string) { @@ -2547,7 +2504,7 @@ export class StreamChat>(this.baseURL + `/channeltypes`); + return this.get(this.baseURL + `/channeltypes`); } /** @@ -2556,10 +2513,10 @@ export class StreamChat} Response that includes the message + * @return {MessageResponse} Response that includes the message */ async translateMessage(messageId: string, language: string) { - return await this.post>( + return await this.post( this.baseURL + `/messages/${encodeURIComponent(messageId)}/translate`, { language }, ); @@ -2643,7 +2600,7 @@ export class StreamChat, + } as unknown) as PartialMessageUpdate, pinnedBy, ); } @@ -2662,7 +2619,7 @@ export class StreamChat, + } as unknown) as PartialMessageUpdate, userId, ); } @@ -2670,22 +2627,18 @@ export class StreamChat, 'mentioned_users'> & { mentioned_users?: string[] }} message object, id needs to be specified + * @param {Omit & { mentioned_users?: string[] }} message object, id needs to be specified * @param {string | { id: string }} [userId] * @param {boolean} [options.skip_enrich_url] Do not try to enrich the URLs within message * - * @return {{ message: MessageResponse }} Response that includes the message + * @return {{ message: MessageResponse }} Response that includes the message */ - async updateMessage( - message: UpdatedMessage, - userId?: string | { id: string }, - options?: UpdateMessageOptions, - ) { + async updateMessage(message: UpdatedMessage, userId?: string | { id: string }, options?: UpdateMessageOptions) { if (!message.id) { throw Error('Please specify the message id when calling updateMessage'); } - const clonedMessage: Message = Object.assign({}, message); + const clonedMessage: Partial = { ...message }; delete clonedMessage.id; const reservedMessageFields: Array = [ @@ -2700,11 +2653,10 @@ export class StreamChat; + } as UserResponse; } } @@ -2727,7 +2679,7 @@ export class StreamChat ((mu as unknown) as UserResponse).id); } - return await this.post>( + return await this.post( this.baseURL + `/messages/${encodeURIComponent(message.id as string)}`, { message: clonedMessage, @@ -2741,17 +2693,17 @@ export class StreamChat} partialMessageObject which should contain id and any of "set" or "unset" params; + * @param {PartialUpdateMessage} partialMessageObject which should contain id and any of "set" or "unset" params; * example: {id: "user1", set:{text: "hi"}, unset:["color"]} * @param {string | { id: string }} [userId] * * @param {boolean} [options.skip_enrich_url] Do not try to enrich the URLs within message * - * @return {{ message: MessageResponse }} Response that includes the updated message + * @return {{ message: MessageResponse }} Response that includes the updated message */ async partialUpdateMessage( id: string, - partialMessageObject: PartialMessageUpdate, + partialMessageObject: PartialMessageUpdate, userId?: string | { id: string }, options?: UpdateMessageOptions, ) { @@ -2762,14 +2714,11 @@ export class StreamChat>( - this.baseURL + `/messages/${encodeURIComponent(id)}`, - { - ...partialMessageObject, - ...options, - user, - }, - ); + return await this.put(this.baseURL + `/messages/${encodeURIComponent(id)}`, { + ...partialMessageObject, + ...options, + user, + }); } async deleteMessage(messageID: string, hardDelete?: boolean) { @@ -2777,7 +2726,7 @@ export class StreamChat }>( + return await this.delete( this.baseURL + `/messages/${encodeURIComponent(messageID)}`, params, ); @@ -2793,20 +2742,19 @@ export class StreamChat }} Response that includes the message + * @return {{ message: MessageResponse }} Response that includes the message */ async undeleteMessage(messageID: string, userID: string) { - return await this.post }>( + return await this.post( this.baseURL + `/messages/${encodeURIComponent(messageID)}/undelete`, { undeleted_by: userID }, ); } async getMessage(messageID: string, options?: GetMessageOptions) { - return await this.get>( - this.baseURL + `/messages/${encodeURIComponent(messageID)}`, - { ...options }, - ); + return await this.get(this.baseURL + `/messages/${encodeURIComponent(messageID)}`, { + ...options, + }); } /** @@ -2818,7 +2766,7 @@ export class StreamChat[], next: string }} Returns the list of threads and the next cursor. + * @returns {{ threads: Thread[], next: string }} Returns the list of threads and the next cursor. */ async queryThreads(options: QueryThreadsOptions = {}) { const optionsWithDefaults = { @@ -2829,15 +2777,10 @@ export class StreamChat>( - `${this.baseURL}/threads`, - optionsWithDefaults, - ); + const response = await this.post(`${this.baseURL}/threads`, optionsWithDefaults); return { - threads: response.threads.map( - (thread) => new Thread({ client: this, threadData: thread }), - ), + threads: response.threads.map((thread) => new Thread({ client: this, threadData: thread })), next: response.next, }; } @@ -2851,7 +2794,7 @@ export class StreamChat} Returns the thread. + * @returns {Thread} Returns the thread. */ async getThread(messageId: string, options: GetThreadOptions = {}) { if (!messageId) { @@ -2865,12 +2808,12 @@ export class StreamChat>( + const response = await this.get( `${this.baseURL}/threads/${encodeURIComponent(messageId)}`, optionsWithDefaults, ); - return new Thread({ client: this, threadData: response.thread }); + return new Thread({ client: this, threadData: response.thread }); } /** @@ -2879,7 +2822,7 @@ export class StreamChat} Returns the updated thread. + * @returns {GetThreadAPIResponse} Returns the updated thread. */ async partialUpdateThread(messageId: string, partialThreadObject: PartialThreadUpdate) { if (!messageId) { @@ -2909,7 +2852,7 @@ export class StreamChat>( + return await this.patch( `${this.baseURL}/threads/${encodeURIComponent(messageId)}`, partialThreadObject, ); @@ -3677,8 +3620,8 @@ export class StreamChat, userId?: string) { - return await this.post>(this.baseURL + `/polls`, { + async createPoll(poll: CreatePollData, userId?: string) { + return await this.post(this.baseURL + `/polls`, { ...poll, ...(userId ? { user_id: userId } : {}), }); @@ -3690,8 +3633,8 @@ export class StreamChat> { - return await this.get>( + async getPoll(id: string, userId?: string): Promise { + return await this.get( this.baseURL + `/polls/${encodeURIComponent(id)}`, userId ? { user_id: userId } : {}, ); @@ -3703,8 +3646,8 @@ export class StreamChat, userId?: string) { - return await this.put>(this.baseURL + `/polls`, { + async updatePoll(poll: PollData, userId?: string) { + return await this.put(this.baseURL + `/polls`, { ...poll, ...(userId ? { user_id: userId } : {}), }); @@ -3713,23 +3656,20 @@ export class StreamChat} partialPollObject which should contain id and any of "set" or "unset" params; + * @param {PartialPollUpdate} partialPollObject which should contain id and any of "set" or "unset" params; * @param userId string The user id (only serverside) * example: {id: "44f26af5-f2be-4fa7-9dac-71cf893781de", set:{field: value}, unset:["field2"]} * @returns {APIResponse & UpdatePollAPIResponse} The poll */ async partialUpdatePoll( id: string, - partialPollObject: PartialPollUpdate, + partialPollObject: PartialPollUpdate, userId?: string, - ): Promise> { - return await this.patch>( - this.baseURL + `/polls/${encodeURIComponent(id)}`, - { - ...partialPollObject, - ...(userId ? { user_id: userId } : {}), - }, - ); + ): Promise { + return await this.patch(this.baseURL + `/polls/${encodeURIComponent(id)}`, { + ...partialPollObject, + ...(userId ? { user_id: userId } : {}), + }); } /** @@ -3750,13 +3690,13 @@ export class StreamChat> { + async closePoll(id: string, userId?: string): Promise { return this.partialUpdatePoll( id, { set: { is_closed: true, - } as PartialPollUpdate['set'], + } as PartialPollUpdate['set'], }, userId, ); @@ -3769,8 +3709,8 @@ export class StreamChat, userId?: string) { - return await this.post>( + async createPollOption(pollId: string, option: PollOptionData, userId?: string) { + return await this.post( this.baseURL + `/polls/${encodeURIComponent(pollId)}/options`, { ...option, @@ -3787,7 +3727,7 @@ export class StreamChat>( + return await this.get( this.baseURL + `/polls/${encodeURIComponent(pollId)}/options/${encodeURIComponent(optionId)}`, userId ? { user_id: userId } : {}, ); @@ -3800,8 +3740,8 @@ export class StreamChat, userId?: string) { - return await this.put>( + async updatePollOption(pollId: string, option: PollOptionData, userId?: string) { + return await this.put( this.baseURL + `/polls/${encodeURIComponent(pollId)}/options`, { ...option, @@ -3833,7 +3773,7 @@ export class StreamChat>( + return await this.post( this.baseURL + `/messages/${encodeURIComponent(messageId)}/polls/${encodeURIComponent(pollId)}/vote`, { vote, @@ -3885,9 +3825,9 @@ export class StreamChat> { + ): Promise { const q = userId ? `?user_id=${userId}` : ''; - return await this.post>(this.baseURL + `/polls/query${q}`, { + return await this.post(this.baseURL + `/polls/query${q}`, { filter, sort: normalizeQuerySort(sort), ...options, @@ -3909,9 +3849,9 @@ export class StreamChat> { + ): Promise { const q = userId ? `?user_id=${userId}` : ''; - return await this.post>( + return await this.post( this.baseURL + `/polls/${encodeURIComponent(pollId)}/votes${q}`, { filter, @@ -3936,9 +3876,9 @@ export class StreamChat> { + ): Promise { const q = userId ? `?user_id=${userId}` : ''; - return await this.post>( + return await this.post( this.baseURL + `/polls/${encodeURIComponent(pollId)}/votes${q}`, { filter: { ...filter, is_answer: true }, @@ -3959,15 +3899,12 @@ export class StreamChat> { - return await this.post>( - this.baseURL + '/messages/history', - { - filter, - sort: normalizeQuerySort(sort), - ...options, - }, - ); + ): Promise { + return await this.post(this.baseURL + '/messages/history', { + filter, + sort: normalizeQuerySort(sort), + ...options, + }); } /** diff --git a/src/client_state.ts b/src/client_state.ts index 82acd9d8b..71f0080e9 100644 --- a/src/client_state.ts +++ b/src/client_state.ts @@ -1,16 +1,16 @@ -import { UserResponse, ExtendableGenerics, DefaultGenerics } from './types'; +import { UserResponse } from './types'; import { StreamChat } from './client'; /** * ClientState - A container class for the client state. */ -export class ClientState { - private client: StreamChat; +export class ClientState { + private client: StreamChat; users: { - [key: string]: UserResponse; + [key: string]: UserResponse; }; userChannelReferences: { [key: string]: { [key: string]: boolean } }; - constructor({ client }: { client: StreamChat }) { + constructor({ client }: { client: StreamChat }) { // show the status for a certain user... // ie online, offline etc this.client = client; @@ -19,19 +19,19 @@ export class ClientState[]) { + updateUsers(users: UserResponse[]) { for (const user of users) { this.updateUser(user); } } - updateUser(user?: UserResponse) { + updateUser(user?: UserResponse) { if (user != null && this.client._cacheEnabled()) { this.users[user.id] = user; } } - updateUserReference(user: UserResponse, channelID: string) { + updateUserReference(user: UserResponse, channelID: string) { if (user == null || !this.client._cacheEnabled()) { return; } diff --git a/src/connection.ts b/src/connection.ts index d567bdb56..f271c11e7 100644 --- a/src/connection.ts +++ b/src/connection.ts @@ -9,8 +9,9 @@ import { addConnectionEventListeners, } from './utils'; import { buildWsFatalInsight, buildWsSuccessAfterFailureInsight, postInsights } from './insights'; -import { ConnectAPIResponse, ConnectionOpen, ExtendableGenerics, DefaultGenerics, UR, LogLevel } from './types'; +import { ConnectAPIResponse, ConnectionOpen, UR, LogLevel } from './types'; import { StreamChat } from './client'; +import { APIError } from './errors'; // Type guards to check WebSocket error type const isCloseEvent = (res: WebSocket.CloseEvent | WebSocket.Data | WebSocket.ErrorEvent): res is WebSocket.CloseEvent => @@ -36,13 +37,13 @@ const isErrorEvent = (res: WebSocket.CloseEvent | WebSocket.Data | WebSocket.Err * - state can be recovered by querying the channel again * - if the servers fails to publish a message to the client, the WS connection is destroyed */ -export class StableWSConnection { +export class StableWSConnection { // global from constructor - client: StreamChat; + client: StreamChat; // local vars connectionID?: string; - connectionOpen?: ConnectAPIResponse; + connectionOpen?: ConnectAPIResponse; consecutiveFailures: number; pingInterval: number; healthCheckTimeoutRef?: NodeJS.Timeout; @@ -57,12 +58,12 @@ export class StableWSConnection void; requestID: string | undefined; - resolvePromise?: (value: ConnectionOpen) => void; + resolvePromise?: (value: ConnectionOpen) => void; totalFailures: number; ws?: WebSocket; wsID: number; - constructor({ client }: { client: StreamChat }) { + constructor({ client }: { client: StreamChat }) { /** StreamChat client */ this.client = client; /** consecutive failures influence the duration of the timeout */ @@ -92,7 +93,7 @@ export class StableWSConnection) { + setClient(client: StreamChat) { this.client = client; } @@ -117,17 +118,19 @@ export class StableWSConnection { this.isResolved = false; /** a promise that is resolved once ws.open is called */ - this.connectionOpen = new Promise>((resolve, reject) => { + this.connectionOpen = new Promise((resolve, reject) => { this.resolvePromise = resolve; this.rejectPromise = reject; }); diff --git a/src/connection_fallback.ts b/src/connection_fallback.ts index ea0565574..51071ed22 100644 --- a/src/connection_fallback.ts +++ b/src/connection_fallback.ts @@ -1,8 +1,8 @@ import axios, { AxiosRequestConfig, CancelTokenSource } from 'axios'; import { StreamChat } from './client'; import { addConnectionEventListeners, removeConnectionEventListeners, retryInterval, sleep } from './utils'; -import { isAPIError, isConnectionIDError, isErrorRetryable } from './errors'; -import { ConnectionOpen, Event, UR, ExtendableGenerics, DefaultGenerics, LogLevel } from './types'; +import { APIError, isAPIError, isConnectionIDError, isErrorRetryable } from './errors'; +import { ConnectionOpen, Event, UR, LogLevel } from './types'; export enum ConnectionState { Closed = 'CLOSED', @@ -12,14 +12,14 @@ export enum ConnectionState { Init = 'INIT', } -export class WSConnectionFallback { - client: StreamChat; +export class WSConnectionFallback { + client: StreamChat; state: ConnectionState; consecutiveFailures: number; connectionID?: string; cancelToken?: CancelTokenSource; - constructor({ client }: { client: StreamChat }) { + constructor({ client }: { client: StreamChat }) { this.client = client; this.state = ConnectionState.Init; this.consecutiveFailures = 0; @@ -84,7 +84,7 @@ export class WSConnectionFallback(params, config, retry); @@ -99,7 +99,7 @@ export class WSConnectionFallback[]; + events: Event[]; }>({}, { timeout: 30000 }, true); // 30s => API responds in 20s if there is no event if (data.events?.length) { @@ -115,14 +115,14 @@ export class WSConnectionFallback }>( + const { event } = await this._req<{ event: ConnectionOpen }>( { json: this.client._buildWSPayload() }, { timeout: 8000 }, // 8s reconnect, diff --git a/src/custom_types.ts b/src/custom_types.ts new file mode 100644 index 000000000..62eb9a384 --- /dev/null +++ b/src/custom_types.ts @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-empty-interface */ + +export interface CustomAttachmentData {} +export interface CustomChannelData {} +export interface CustomCommandData {} +export interface CustomEventData {} +export interface CustomMemberData {} +export interface CustomMessageData {} +export interface CustomPollOptionData {} +export interface CustomPollData {} +export interface CustomReactionData {} +export interface CustomUserData {} +export interface CustomThreadData {} diff --git a/src/errors.ts b/src/errors.ts index 7eb1c4f28..cdaa2c9d6 100644 --- a/src/errors.ts +++ b/src/errors.ts @@ -31,7 +31,7 @@ export const APIErrorCodes: Record '99': { name: 'AppSuspendedError', retryable: false }, }; -type APIError = Error & { code: number; isWSFailure?: boolean }; +export type APIError = Error & { code: number; isWSFailure?: boolean; StatusCode?: number }; export function isAPIError(error: Error): error is APIError { return (error as APIError).code !== undefined; diff --git a/src/index.ts b/src/index.ts index 742b4c281..6ce9fb626 100644 --- a/src/index.ts +++ b/src/index.ts @@ -21,4 +21,5 @@ export * from './thread_manager'; export * from './token_manager'; export * from './types'; export * from './channel_manager'; +export * from './custom_types'; export { isOwnUser, chatCodes, logChatPromiseExecution, formatMessage } from './utils'; diff --git a/src/moderation.ts b/src/moderation.ts index d9be41da4..40b0f9fd8 100644 --- a/src/moderation.ts +++ b/src/moderation.ts @@ -1,8 +1,6 @@ import { APIResponse, ModerationConfig, - DefaultGenerics, - ExtendableGenerics, GetConfigResponse, GetUserModerationReportResponse, MuteUserResponse, @@ -31,10 +29,10 @@ export const MODERATION_ENTITY_TYPES = { }; // Moderation class provides all the endpoints related to moderation v2. -export class Moderation { - client: StreamChat; +export class Moderation { + client: StreamChat; - constructor(client: StreamChat) { + constructor(client: StreamChat) { this.client = client; } @@ -104,13 +102,10 @@ export class Moderation & APIResponse>( - this.client.baseURL + '/api/v2/moderation/mute', - { - target_ids: [targetID], - ...options, - }, - ); + return await this.client.post(this.client.baseURL + '/api/v2/moderation/mute', { + target_ids: [targetID], + ...options, + }); } /** @@ -144,7 +139,7 @@ export class Moderation>( + return await this.client.get( this.client.baseURL + `/api/v2/moderation/user_report`, { user_id: userID, diff --git a/src/permissions.ts b/src/permissions.ts index f6b96bf17..4bc530160 100644 --- a/src/permissions.ts +++ b/src/permissions.ts @@ -42,7 +42,7 @@ export const AllowAll = new Permission('Allow all', MaxPriority, AnyResource, An // deprecated export const DenyAll = new Permission('Deny all', MinPriority, AnyResource, AnyRole, false, Deny); -export type Role = 'admin' | 'user' | 'guest' | 'anonymous' | 'channel_member' | 'channel_moderator' | string; +export type Role = 'admin' | 'user' | 'guest' | 'anonymous' | 'channel_member' | 'channel_moderator' | (string & {}); export const BuiltinRoles = { Admin: 'admin', diff --git a/src/poll.ts b/src/poll.ts index aad89abde..b75ff0565 100644 --- a/src/poll.ts +++ b/src/poll.ts @@ -1,9 +1,7 @@ import { StateStore } from './store'; import type { StreamChat } from './client'; import type { - DefaultGenerics, Event, - ExtendableGenerics, PartialPollUpdate, PollAnswer, PollData, @@ -16,58 +14,46 @@ import type { VoteSort, } from './types'; -type PollEvent = { +type PollEvent = { cid: string; created_at: string; - poll: PollResponse; + poll: PollResponse; }; -type PollUpdatedEvent = PollEvent & { +type PollUpdatedEvent = PollEvent & { type: 'poll.updated'; }; -type PollClosedEvent = PollEvent & { +type PollClosedEvent = PollEvent & { type: 'poll.closed'; }; -type PollVoteEvent = { +type PollVoteEvent = { cid: string; created_at: string; - poll: PollResponse; - poll_vote: PollVote | PollAnswer; + poll: PollResponse; + poll_vote: PollVote | PollAnswer; }; -type PollVoteCastedEvent = PollVoteEvent & { +type PollVoteCastedEvent = PollVoteEvent & { type: 'poll.vote_casted'; }; -type PollVoteCastedChanged = PollVoteEvent & { +type PollVoteCastedChanged = PollVoteEvent & { type: 'poll.vote_removed'; }; -type PollVoteCastedRemoved = PollVoteEvent & { +type PollVoteCastedRemoved = PollVoteEvent & { type: 'poll.vote_removed'; }; -const isPollUpdatedEvent = ( - e: Event, -): e is PollUpdatedEvent => e.type === 'poll.updated'; -const isPollClosedEventEvent = ( - e: Event, -): e is PollClosedEvent => e.type === 'poll.closed'; -const isPollVoteCastedEvent = ( - e: Event, -): e is PollVoteCastedEvent => e.type === 'poll.vote_casted'; -const isPollVoteChangedEvent = ( - e: Event, -): e is PollVoteCastedChanged => e.type === 'poll.vote_changed'; -const isPollVoteRemovedEvent = ( - e: Event, -): e is PollVoteCastedRemoved => e.type === 'poll.vote_removed'; - -export const isVoteAnswer = ( - vote: PollVote | PollAnswer, -): vote is PollAnswer => !!(vote as PollAnswer)?.answer_text; +const isPollUpdatedEvent = (e: Event): e is PollUpdatedEvent => e.type === 'poll.updated'; +const isPollClosedEventEvent = (e: Event): e is PollClosedEvent => e.type === 'poll.closed'; +const isPollVoteCastedEvent = (e: Event): e is PollVoteCastedEvent => e.type === 'poll.vote_casted'; +const isPollVoteChangedEvent = (e: Event): e is PollVoteCastedChanged => e.type === 'poll.vote_changed'; +const isPollVoteRemovedEvent = (e: Event): e is PollVoteCastedRemoved => e.type === 'poll.vote_removed'; + +export const isVoteAnswer = (vote: PollVote | PollAnswer): vote is PollAnswer => !!(vote as PollAnswer)?.answer_text; export type PollAnswersQueryParams = { filter?: QueryVotesFilters; @@ -83,36 +69,35 @@ export type PollOptionVotesQueryParams = { type OptionId = string; -export type PollState = SCG['pollType'] & - Omit, 'own_votes' | 'id'> & { - lastActivityAt: Date; // todo: would be ideal to get this from the BE - maxVotedOptionIds: OptionId[]; - ownVotesByOptionId: Record>; - ownAnswer?: PollAnswer; // each user can have only one answer - }; +export type PollState = Omit & { + lastActivityAt: Date; // todo: would be ideal to get this from the BE + maxVotedOptionIds: OptionId[]; + ownVotesByOptionId: Record; + ownAnswer?: PollAnswer; // each user can have only one answer +}; -type PollInitOptions = { - client: StreamChat; - poll: PollResponse; +type PollInitOptions = { + client: StreamChat; + poll: PollResponse; }; -export class Poll { - public readonly state: StateStore>; +export class Poll { + public readonly state: StateStore; public id: string; - private client: StreamChat; + private client: StreamChat; private unsubscribeFunctions: Set<() => void> = new Set(); - constructor({ client, poll }: PollInitOptions) { + constructor({ client, poll }: PollInitOptions) { this.client = client; this.id = poll.id; - this.state = new StateStore>(this.getInitialStateFromPollResponse(poll)); + this.state = new StateStore(this.getInitialStateFromPollResponse(poll)); } - private getInitialStateFromPollResponse = (poll: PollInitOptions['poll']) => { + private getInitialStateFromPollResponse = (poll: PollInitOptions['poll']) => { // eslint-disable-next-line @typescript-eslint/no-unused-vars const { own_votes, id, ...pollResponseForState } = poll; - const { ownAnswer, ownVotes } = own_votes?.reduce<{ ownVotes: PollVote[]; ownAnswer?: PollAnswer }>( + const { ownAnswer, ownVotes } = own_votes?.reduce<{ ownVotes: PollVote[]; ownAnswer?: PollAnswer }>( (acc, voteOrAnswer) => { if (isVoteAnswer(voteOrAnswer)) { acc.ownAnswer = voteOrAnswer; @@ -128,22 +113,22 @@ export class Poll { ...pollResponseForState, lastActivityAt: new Date(), maxVotedOptionIds: getMaxVotedOptionIds( - pollResponseForState.vote_counts_by_option as PollResponse['vote_counts_by_option'], + pollResponseForState.vote_counts_by_option as PollResponse['vote_counts_by_option'], ), ownAnswer, ownVotesByOptionId: getOwnVotesByOptionId(ownVotes), }; }; - public reinitializeState = (poll: PollInitOptions['poll']) => { + public reinitializeState = (poll: PollInitOptions['poll']) => { this.state.partialNext(this.getInitialStateFromPollResponse(poll)); }; - get data(): PollState { + get data(): PollState { return this.state.getLatestValue(); } - public handlePollUpdated = (event: Event) => { + public handlePollUpdated = (event: Event) => { if (event.poll?.id && event.poll.id !== this.id) return; if (!isPollUpdatedEvent(event)) return; // eslint-disable-next-line @typescript-eslint/no-unused-vars @@ -152,14 +137,14 @@ export class Poll { this.state.partialNext({ ...pollData, lastActivityAt: new Date(event.created_at) }); }; - public handlePollClosed = (event: Event) => { + public handlePollClosed = (event: Event) => { if (event.poll?.id && event.poll.id !== this.id) return; if (!isPollClosedEventEvent(event)) return; // @ts-ignore this.state.partialNext({ is_closed: true, lastActivityAt: new Date(event.created_at) }); }; - public handleVoteCasted = (event: Event) => { + public handleVoteCasted = (event: Event) => { if (event.poll?.id && event.poll.id !== this.id) return; if (!isPollVoteCastedEvent(event)) return; const currentState = this.data; @@ -195,7 +180,7 @@ export class Poll { }); }; - public handleVoteChanged = (event: Event) => { + public handleVoteChanged = (event: Event) => { // this event is triggered only when event.poll.enforce_unique_vote === true if (event.poll?.id && event.poll.id !== this.id) return; if (!isPollVoteChangedEvent(event)) return; @@ -214,7 +199,7 @@ export class Poll { if (event.poll.enforce_unique_votes) { ownVotesByOptionId = { [event.poll_vote.option_id]: event.poll_vote }; } else { - ownVotesByOptionId = Object.entries(ownVotesByOptionId).reduce>>( + ownVotesByOptionId = Object.entries(ownVotesByOptionId).reduce>( (acc, [optionId, vote]) => { if (optionId !== event.poll_vote.option_id && vote.id === event.poll_vote.id) { return acc; @@ -250,7 +235,7 @@ export class Poll { }); }; - public handleVoteRemoved = (event: Event) => { + public handleVoteRemoved = (event: Event) => { if (event.poll?.id && event.poll.id !== this.id) return; if (!isPollVoteRemovedEvent(event)) return; const currentState = this.data; @@ -291,11 +276,11 @@ export class Poll { return poll; }; - update = async (data: Exclude, 'id'>) => { + update = async (data: Exclude) => { return await this.client.updatePoll({ ...data, id: this.id }); }; - partialUpdate = async (partialPollObject: PartialPollUpdate) => { + partialUpdate = async (partialPollObject: PartialPollUpdate) => { return await this.client.partialUpdatePoll(this.id as string, partialPollObject); }; @@ -375,19 +360,17 @@ function getMaxVotedOptionIds(voteCountsByOption: PollResponse['vote_counts_by_o return winningOptions; } -function getOwnVotesByOptionId(ownVotes: PollVote[]) { +function getOwnVotesByOptionId(ownVotes: PollVote[]) { return !ownVotes - ? ({} as Record>) - : ownVotes.reduce>>((acc, vote) => { + ? ({} as Record) + : ownVotes.reduce>((acc, vote) => { if (isVoteAnswer(vote) || !vote.option_id) return acc; acc[vote.option_id] = vote; return acc; }, {}); } -export function extractPollData( - pollResponse: PollResponse, -): PollData { +export function extractPollData(pollResponse: PollResponse): PollData { return { allow_answers: pollResponse.allow_answers, allow_user_suggested_options: pollResponse.allow_user_suggested_options, @@ -402,9 +385,9 @@ export function extractPollData( - pollResponse: PollResponse, -): Omit, 'own_votes' | 'latest_answers'> { +export function extractPollEnrichedData( + pollResponse: PollResponse, +): Omit { return { answers_count: pollResponse.answers_count, latest_votes_by_option: pollResponse.latest_votes_by_option, diff --git a/src/poll_manager.ts b/src/poll_manager.ts index 58f08b55d..d81f8ee21 100644 --- a/src/poll_manager.ts +++ b/src/poll_manager.ts @@ -1,8 +1,6 @@ import type { StreamChat } from './client'; import type { CreatePollData, - DefaultGenerics, - ExtendableGenerics, MessageResponse, PollResponse, PollSort, @@ -13,21 +11,21 @@ import { Poll } from './poll'; import { FormatMessageResponse } from './types'; import { formatMessage } from './utils'; -export class PollManager { - private client: StreamChat; +export class PollManager { + private client: StreamChat; // The pollCache contains only polls that have been created and sent as messages // (i.e only polls that are coupled with a message, can be voted on and require a // reactive state). It shall work as a basic look-up table for our SDK to be able // to quickly consume poll state that will be reactive even without the polls being // rendered within the UI. - private pollCache = new Map>(); + private pollCache = new Map(); private unsubscribeFunctions: Set<() => void> = new Set(); - constructor({ client }: { client: StreamChat }) { + constructor({ client }: { client: StreamChat }) { this.client = client; } - get data(): Map> { + get data(): Map { return this.pollCache; } @@ -54,7 +52,7 @@ export class PollManager { this.unsubscribeFunctions.clear(); }; - public createPoll = async (poll: CreatePollData) => { + public createPoll = async (poll: CreatePollData) => { const { poll: createdPoll } = await this.client.createPoll(poll); return new Poll({ client: this.client, poll: createdPoll }); @@ -91,26 +89,23 @@ export class PollManager { }; }; - public hydratePollCache = ( - messages: FormatMessageResponse[] | MessageResponse[], - overwriteState?: boolean, - ) => { + public hydratePollCache = (messages: FormatMessageResponse[] | MessageResponse[], overwriteState?: boolean) => { for (const message of messages) { if (!message.poll) { continue; } - const pollResponse = message.poll as PollResponse; + const pollResponse = message.poll as PollResponse; this.setOrOverwriteInCache(pollResponse, overwriteState); } }; - private setOrOverwriteInCache = (pollResponse: PollResponse, overwriteState?: boolean) => { + private setOrOverwriteInCache = (pollResponse: PollResponse, overwriteState?: boolean) => { if (!this.client._cacheEnabled()) { return; } const pollFromCache = this.fromState(pollResponse.id); if (!pollFromCache) { - const poll = new Poll({ client: this.client, poll: pollResponse }); + const poll = new Poll({ client: this.client, poll: pollResponse }); this.pollCache.set(poll.id, poll); } else if (overwriteState) { pollFromCache.reinitializeState(pollResponse); diff --git a/src/search_controller.ts b/src/search_controller.ts index 584ab13ee..1f14e469c 100644 --- a/src/search_controller.ts +++ b/src/search_controller.ts @@ -6,8 +6,6 @@ import type { ChannelFilters, ChannelOptions, ChannelSort, - DefaultGenerics, - ExtendableGenerics, MessageFilters, MessageResponse, SearchMessageSort, @@ -203,16 +201,14 @@ export abstract class BaseSearchSource implements SearchSource { } } -export class UserSearchSource extends BaseSearchSource< - UserResponse -> { +export class UserSearchSource extends BaseSearchSource { readonly type = 'users'; - private client: StreamChat; - filters: UserFilters | undefined; - sort: UserSort | undefined; + private client: StreamChat; + filters: UserFilters | undefined; + sort: UserSort | undefined; searchOptions: Omit | undefined; - constructor(client: StreamChat, options?: SearchSourceOptions) { + constructor(client: StreamChat, options?: SearchSourceOptions) { super(options); this.client = client; } @@ -221,28 +217,26 @@ export class UserSearchSource; - const sort = { id: 1, ...this.sort } as UserSort; + } as UserFilters; + const sort = { id: 1, ...this.sort } as UserSort; const options = { ...this.searchOptions, limit: this.pageSize, offset: this.offset }; const { users } = await this.client.queryUsers(filters, sort, options); return { items: users }; } - protected filterQueryResults(items: UserResponse[]) { + protected filterQueryResults(items: UserResponse[]) { return items.filter((u) => u.id !== this.client.user?.id); } } -export class ChannelSearchSource< - StreamChatGenerics extends ExtendableGenerics = DefaultGenerics -> extends BaseSearchSource> { +export class ChannelSearchSource extends BaseSearchSource { readonly type = 'channels'; - private client: StreamChat; - filters: ChannelFilters | undefined; - sort: ChannelSort | undefined; + private client: StreamChat; + filters: ChannelFilters | undefined; + sort: ChannelSort | undefined; searchOptions: Omit | undefined; - constructor(client: StreamChat, options?: SearchSourceOptions) { + constructor(client: StreamChat, options?: SearchSourceOptions) { super(options); this.client = client; } @@ -252,31 +246,29 @@ export class ChannelSearchSource< members: { $in: [this.client.userID] }, name: { $autocomplete: searchQuery }, ...this.filters, - } as ChannelFilters; + } as ChannelFilters; const sort = this.sort ?? {}; const options = { ...this.searchOptions, limit: this.pageSize, offset: this.offset }; const items = await this.client.queryChannels(filters, sort, options); return { items }; } - protected filterQueryResults(items: Channel[]) { + protected filterQueryResults(items: Channel[]) { return items; } } -export class MessageSearchSource< - StreamChatGenerics extends ExtendableGenerics = DefaultGenerics -> extends BaseSearchSource> { +export class MessageSearchSource extends BaseSearchSource { readonly type = 'messages'; - private client: StreamChat; - messageSearchChannelFilters: ChannelFilters | undefined; - messageSearchFilters: MessageFilters | undefined; - messageSearchSort: SearchMessageSort | undefined; - channelQueryFilters: ChannelFilters | undefined; - channelQuerySort: ChannelSort | undefined; + private client: StreamChat; + messageSearchChannelFilters: ChannelFilters | undefined; + messageSearchFilters: MessageFilters | undefined; + messageSearchSort: SearchMessageSort | undefined; + channelQueryFilters: ChannelFilters | undefined; + channelQuerySort: ChannelSort | undefined; channelQueryOptions: Omit | undefined; - constructor(client: StreamChat, options?: SearchSourceOptions) { + constructor(client: StreamChat, options?: SearchSourceOptions) { super(options); this.client = client; } @@ -284,18 +276,18 @@ export class MessageSearchSource< protected async query(searchQuery: string) { if (!this.client.userID) return { items: [] }; - const channelFilters: ChannelFilters = { + const channelFilters: ChannelFilters = { members: { $in: [this.client.userID] }, ...this.messageSearchChannelFilters, - } as ChannelFilters; + } as ChannelFilters; - const messageFilters: MessageFilters = { + const messageFilters: MessageFilters = { text: searchQuery, type: 'regular', // FIXME: type: 'reply' resp. do not filter by type and allow to jump to a message in a thread - missing support ...this.messageSearchFilters, - } as MessageFilters; + } as MessageFilters; - const sort: SearchMessageSort = { + const sort: SearchMessageSort = { created_at: -1, ...this.messageSearchSort, }; @@ -304,7 +296,7 @@ export class MessageSearchSource< limit: this.pageSize, next: this.next, sort, - } as SearchOptions; + } as SearchOptions; const { next, results } = await this.client.search(channelFilters, messageFilters, options); const items = results.map(({ message }) => message); @@ -321,7 +313,7 @@ export class MessageSearchSource< { cid: { $in: cids }, ...this.channelQueryFilters, - } as ChannelFilters, + } as ChannelFilters, { last_message_at: -1, ...this.channelQuerySort, @@ -333,16 +325,12 @@ export class MessageSearchSource< return { items, next }; } - protected filterQueryResults(items: MessageResponse[]) { + protected filterQueryResults(items: MessageResponse[]) { return items; } } -export type DefaultSearchSources = [ - UserSearchSource, - ChannelSearchSource, - MessageSearchSource, -]; +export type DefaultSearchSources = [UserSearchSource, ChannelSearchSource, MessageSearchSource]; export type SearchControllerState = { isActive: boolean; @@ -350,10 +338,10 @@ export type SearchControllerState = { sources: SearchSource[]; }; -export type InternalSearchControllerState = { +export type InternalSearchControllerState = { // FIXME: focusedMessage should live in a MessageListController class that does not exist yet. // This state prop should be then removed - focusedMessage?: MessageResponse; + focusedMessage?: MessageResponse; }; export type SearchControllerConfig = { @@ -366,12 +354,12 @@ export type SearchControllerOptions = { sources?: SearchSource[]; }; -export class SearchController { +export class SearchController { /** * Not intended for direct use by integrators, might be removed without notice resulting in * broken integrations. */ - _internalState: StateStore>; + _internalState: StateStore; state: StateStore; config: SearchControllerConfig; @@ -381,7 +369,7 @@ export class SearchController>({}); + this._internalState = new StateStore({}); this.config = { keepSingleActiveSource: true, ...config }; } get hasNext() { diff --git a/src/segment.ts b/src/segment.ts index 748d32f4e..2f60985c8 100644 --- a/src/segment.ts +++ b/src/segment.ts @@ -1,12 +1,5 @@ import { StreamChat } from './client'; -import { - DefaultGenerics, - ExtendableGenerics, - QuerySegmentTargetsFilter, - SegmentData, - SegmentResponse, - SortParam, -} from './types'; +import { QuerySegmentTargetsFilter, SegmentData, SegmentResponse, SortParam } from './types'; type SegmentType = 'user' | 'channel'; @@ -16,13 +9,13 @@ type SegmentUpdatableFields = { name?: string; }; -export class Segment { +export class Segment { type: SegmentType; id: string | null; - client: StreamChat; + client: StreamChat; data?: SegmentData | SegmentResponse; - constructor(client: StreamChat, type: SegmentType, id: string | null, data?: SegmentData) { + constructor(client: StreamChat, type: SegmentType, id: string | null, data?: SegmentData) { this.client = client; this.type = type; this.id = id; diff --git a/src/thread.ts b/src/thread.ts index 0a88ac42d..5ed74ddd3 100644 --- a/src/thread.ts +++ b/src/thread.ts @@ -1,34 +1,32 @@ -import type { Channel } from './channel'; -import type { StreamChat } from './client'; import { StateStore } from './store'; +import { addToMessageList, findIndexInSortedArray, formatMessage, throttle } from './utils'; import type { AscDesc, - DefaultGenerics, EventTypes, - ExtendableGenerics, FormatMessageResponse, MessagePaginationOptions, MessageResponse, ReadResponse, ThreadResponse, - ThreadResponseCustomData, UserResponse, } from './types'; -import { addToMessageList, findIndexInSortedArray, formatMessage, throttle } from './utils'; +import type { Channel } from './channel'; +import type { StreamChat } from './client'; +import type { CustomThreadData } from './custom_types'; -type QueryRepliesOptions = { +type QueryRepliesOptions = { sort?: { created_at: AscDesc }[]; -} & MessagePaginationOptions & { user?: UserResponse; user_id?: string }; +} & MessagePaginationOptions & { user?: UserResponse; user_id?: string }; -export type ThreadState = { +export type ThreadState = { /** * Determines if the thread is currently opened and on-screen. When the thread is active, * all new messages are immediately marked as read. */ active: boolean; - channel: Channel; + channel: Channel; createdAt: Date; - custom: ThreadResponseCustomData; + custom: CustomThreadData; deletedAt: Date | null; isLoading: boolean; isStateStale: boolean; @@ -37,10 +35,10 @@ export type ThreadState = { * Thread is identified by and has a one-to-one relation with its parent message. * We use parent message id as a thread id. */ - parentMessage: FormatMessageResponse; - participants: ThreadResponse['thread_participants']; + parentMessage: FormatMessageResponse; + participants: ThreadResponse['thread_participants']; read: ThreadReadState; - replies: Array>; + replies: Array; replyCount: number; title: string; updatedAt: Date | null; @@ -53,17 +51,14 @@ export type ThreadRepliesPagination = { prevCursor: string | null; }; -export type ThreadUserReadState = { +export type ThreadUserReadState = { lastReadAt: Date; unreadMessageCount: number; - user: UserResponse; + user: UserResponse; lastReadMessageId?: string; }; -export type ThreadReadState = Record< - string, - ThreadUserReadState | undefined ->; +export type ThreadReadState = Record; const DEFAULT_PAGE_LIMIT = 50; const DEFAULT_SORT: { created_at: AscDesc }[] = [{ created_at: -1 }]; @@ -91,14 +86,14 @@ export const THREAD_RESPONSE_RESERVED_KEYS: Record = // TODO: remove this once we move to API v2 const constructCustomDataObject = (threadData: T) => { - const custom: ThreadResponseCustomData = {}; + const custom: CustomThreadData = {}; for (const key in threadData) { if (THREAD_RESPONSE_RESERVED_KEYS[key as keyof ThreadResponse]) { continue; } - const customKey = key as keyof ThreadResponseCustomData; + const customKey = key as keyof CustomThreadData; custom[customKey] = threadData[customKey]; } @@ -106,15 +101,15 @@ const constructCustomDataObject = (threadData: T) => { return custom; }; -export class Thread { - public readonly state: StateStore>; +export class Thread { + public readonly state: StateStore; public readonly id: string; - private client: StreamChat; + private client: StreamChat; private unsubscribeFunctions: Set<() => void> = new Set(); - private failedRepliesMap: Map> = new Map(); + private failedRepliesMap: Map = new Map(); - constructor({ client, threadData }: { client: StreamChat; threadData: ThreadResponse }) { + constructor({ client, threadData }: { client: StreamChat; threadData: ThreadResponse }) { const channel = client.channel(threadData.channel.type, threadData.channel.id, { name: threadData.channel.name, }); @@ -126,7 +121,7 @@ export class Thread { ? [{ user: { id: client.userID }, unread_messages: 0, last_read: new Date().toISOString() }] : []; - this.state = new StateStore>({ + this.state = new StateStore({ // local only active: false, isLoading: false, @@ -188,7 +183,7 @@ export class Thread { } }; - public hydrateState = (thread: Thread) => { + public hydrateState = (thread: Thread) => { if (thread === this) { // skip if the instances are the same return; @@ -410,7 +405,7 @@ export class Thread { this.unsubscribeFunctions.clear(); }; - public deleteReplyLocally = ({ message }: { message: MessageResponse }) => { + public deleteReplyLocally = ({ message }: { message: MessageResponse }) => { const { replies } = this.state.getLatestValue(); const index = findIndexInSortedArray({ @@ -437,7 +432,7 @@ export class Thread { message, timestampChanged = false, }: { - message: MessageResponse; + message: MessageResponse; timestampChanged?: boolean; }) => { if (message.parent_id !== this.id) { @@ -459,7 +454,7 @@ export class Thread { })); }; - public updateParentMessageLocally = ({ message }: { message: MessageResponse }) => { + public updateParentMessageLocally = ({ message }: { message: MessageResponse }) => { if (message.id !== this.id) { throw new Error('Message does not belong to this thread'); } @@ -476,7 +471,7 @@ export class Thread { }); }; - public updateParentMessageOrReplyLocally = (message: MessageResponse) => { + public updateParentMessageOrReplyLocally = (message: MessageResponse) => { if (message.parent_id === this.id) { this.upsertReplyLocally({ message }); } @@ -500,7 +495,7 @@ export class Thread { limit = DEFAULT_PAGE_LIMIT, sort = DEFAULT_SORT, ...otherOptions - }: QueryRepliesOptions = {}) => { + }: QueryRepliesOptions = {}) => { return this.channel.getReplies(this.id, { limit, ...otherOptions }, sort); }; @@ -585,8 +580,5 @@ const repliesPaginationFromInitialThread = (thread: ThreadResponse): ThreadRepli }; }; -const ownUnreadCountSelector = (currentUserId: string | undefined) => < - SCG extends ExtendableGenerics = DefaultGenerics ->( - state: ThreadState, -) => (currentUserId && state.read[currentUserId]?.unreadMessageCount) || 0; +const ownUnreadCountSelector = (currentUserId: string | undefined) => (state: ThreadState) => + (currentUserId && state.read[currentUserId]?.unreadMessageCount) || 0; diff --git a/src/thread_manager.ts b/src/thread_manager.ts index 878d2b63a..4a4568ba0 100644 --- a/src/thread_manager.ts +++ b/src/thread_manager.ts @@ -3,7 +3,7 @@ import { throttle } from './utils'; import type { StreamChat } from './client'; import type { Thread } from './thread'; -import type { DefaultGenerics, Event, ExtendableGenerics, OwnUserResponse, QueryThreadsOptions } from './types'; +import type { Event, OwnUserResponse, QueryThreadsOptions } from './types'; const DEFAULT_CONNECTION_RECOVERY_THROTTLE_DURATION = 1000; const MAX_QUERY_THREADS_LIMIT = 25; @@ -22,13 +22,13 @@ export const THREAD_MANAGER_INITIAL_STATE = { ready: false, }; -export type ThreadManagerState = { +export type ThreadManagerState = { active: boolean; isThreadOrderStale: boolean; lastConnectionDropAt: Date | null; pagination: ThreadManagerPagination; ready: boolean; - threads: Thread[]; + threads: Thread[]; unreadThreadCount: number; /** * List of threads that haven't been loaded in the list, but have received new messages @@ -43,18 +43,18 @@ export type ThreadManagerPagination = { nextCursor: string | null; }; -export class ThreadManager { - public readonly state: StateStore>; - private client: StreamChat; +export class ThreadManager { + public readonly state: StateStore; + private client: StreamChat; private unsubscribeFunctions: Set<() => void> = new Set(); private threadsByIdGetterCache: { - threads: ThreadManagerState['threads']; - threadsById: Record | undefined>; + threads: ThreadManagerState['threads']; + threadsById: Record; }; - constructor({ client }: { client: StreamChat }) { + constructor({ client }: { client: StreamChat }) { this.client = client; - this.state = new StateStore>(THREAD_MANAGER_INITIAL_STATE); + this.state = new StateStore(THREAD_MANAGER_INITIAL_STATE); this.threadsByIdGetterCache = { threads: [], threadsById: {} }; } @@ -66,7 +66,7 @@ export class ThreadManager { return this.threadsByIdGetterCache.threadsById; } - const threadsById = threads.reduce>>((newThreadsById, thread) => { + const threadsById = threads.reduce>((newThreadsById, thread) => { newThreadsById[thread.id] = thread; return newThreadsById; }, {}); @@ -102,7 +102,7 @@ export class ThreadManager { private subscribeUnreadThreadsCountChange = () => { // initiate - const { unread_threads: unreadThreadCount = 0 } = (this.client.user as OwnUserResponse) ?? {}; + const { unread_threads: unreadThreadCount = 0 } = (this.client.user as OwnUserResponse) ?? {}; this.state.partialNext({ unreadThreadCount }); const unsubscribeFunctions = [ @@ -155,7 +155,7 @@ export class ThreadManager { ); private subscribeNewReplies = () => - this.client.on('notification.thread_message_new', (event: Event) => { + this.client.on('notification.thread_message_new', (event: Event) => { const parentId = event.message?.parent_id; if (!parentId) return; @@ -228,7 +228,7 @@ export class ThreadManager { }); const currentThreads = this.threadsById; - const nextThreads: Thread[] = []; + const nextThreads: Thread[] = []; for (const incomingThread of response.threads) { const existingThread = currentThreads[incomingThread.id]; diff --git a/src/token_manager.ts b/src/token_manager.ts index 32ec8fee9..e569b95d9 100644 --- a/src/token_manager.ts +++ b/src/token_manager.ts @@ -1,20 +1,20 @@ import { Secret } from 'jsonwebtoken'; import { UserFromToken, JWTServerToken, JWTUserToken } from './signing'; import { isFunction } from './utils'; -import { TokenOrProvider, ExtendableGenerics, DefaultGenerics, UserResponse } from './types'; +import { TokenOrProvider, UserResponse } from './types'; /** * TokenManager * * Handles all the operations around user token. */ -export class TokenManager { +export class TokenManager { loadTokenPromise: Promise | null; type: 'static' | 'provider'; secret?: Secret; token?: string; tokenProvider?: TokenOrProvider; - user?: UserResponse; + user?: UserResponse; /** * Constructor * @@ -38,9 +38,9 @@ export class TokenManager} user + * @param {UserResponse} user */ - setTokenOrProvider = async (tokenOrProvider: TokenOrProvider, user: UserResponse) => { + setTokenOrProvider = async (tokenOrProvider: TokenOrProvider, user: UserResponse) => { this.validateToken(tokenOrProvider, user); this.user = user; @@ -75,7 +75,7 @@ export class TokenManager) => { + validateToken = (tokenOrProvider: TokenOrProvider, user: UserResponse) => { // allow empty token for anon user if (user && user.anon && !tokenOrProvider) return; diff --git a/src/types.ts b/src/types.ts index f73def52d..edaeeea1d 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,13 +1,30 @@ -import { AxiosRequestConfig, AxiosResponse } from 'axios'; -import { StableWSConnection } from './connection'; import { EVENT_MAP } from './events'; -import { Role } from './permissions'; import type { Channel } from './channel'; +import type { AxiosRequestConfig, AxiosResponse } from 'axios'; +import type { StableWSConnection } from './connection'; +import type { Role } from './permissions'; +import type { + CustomChannelData, + CustomMemberData, + CustomThreadData, + CustomEventData, + CustomMessageData, + CustomUserData, + CustomReactionData, + CustomAttachmentData, + CustomCommandData, + CustomPollData, + CustomPollOptionData, +} from './custom_types'; /** * Utility Types */ +export type Readable = { + [key in keyof T]: T[key]; +} & {}; + export type ArrayOneOrMore = { 0: T; } & Array; @@ -27,40 +44,14 @@ export type RequireAtLeastOne = { [K in keyof T]-?: Required> & Partial>>; }[keyof T]; -export type RequireOnlyOne = Pick> & +export type RequireOnlyOne = Omit & { - [K in Keys]-?: Required> & Partial, undefined>>; + [K in Keys]-?: Required> & Partial>; }[Keys]; /* Unknown Record */ export type UR = Record; -export type UnknownType = UR; //alias to avoid breaking change - -export type DefaultGenerics = { - attachmentType: UR; - channelType: UR; - commandType: LiteralStringForUnion; - eventType: UR; - memberType: UR; - messageType: UR; - pollOptionType: UR; - pollType: UR; - reactionType: UR; - userType: UR; -}; - -export type ExtendableGenerics = { - attachmentType: UR; - channelType: UR; - commandType: string; - eventType: UR; - memberType: UR; - messageType: UR; - pollOptionType: UR; - pollType: UR; - reactionType: UR; - userType: UR; -}; +export type UnknownType = UR; // alias to avoid breaking change export type Unpacked = T extends (infer U)[] ? U // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -83,7 +74,7 @@ export type TranslateResponse = { translated_text: string; }; -export type AppSettingsAPIResponse = APIResponse & { +export type AppSettingsAPIResponse = APIResponse & { app?: { // TODO // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -96,7 +87,7 @@ export type AppSettingsAPIResponse[]; + commands?: CommandVariants[]; connect_events?: boolean; created_at?: string; custom_events?: boolean; @@ -201,62 +192,62 @@ export type FlagDetails = { automod?: AutomodDetails; }; -export type Flag = { +export type Flag = { created_at: string; created_by_automod: boolean; updated_at: string; details?: FlagDetails; - target_message?: MessageResponse; - target_user?: UserResponse; - user?: UserResponse; + target_message?: MessageResponse; + target_user?: UserResponse; + user?: UserResponse; }; -export type FlagsResponse = APIResponse & { - flags?: Array>; +export type FlagsResponse = APIResponse & { + flags?: Array; }; -export type MessageFlagsResponse = APIResponse & { +export type MessageFlagsResponse = APIResponse & { flags?: Array<{ - message: MessageResponse; - user: UserResponse; + message: MessageResponse; + user: UserResponse; approved_at?: string; created_at?: string; created_by_automod?: boolean; moderation_result?: ModerationResult; rejected_at?: string; reviewed_at?: string; - reviewed_by?: UserResponse; + reviewed_by?: UserResponse; updated_at?: string; }>; }; -export type FlagReport = { +export type FlagReport = { flags_count: number; id: string; - message: MessageResponse; - user: UserResponse; + message: MessageResponse; + user: UserResponse; created_at?: string; details?: FlagDetails; - first_reporter?: UserResponse; + first_reporter?: UserResponse; review_result?: string; reviewed_at?: string; - reviewed_by?: UserResponse; + reviewed_by?: UserResponse; updated_at?: string; }; -export type FlagReportsResponse = APIResponse & { - flag_reports: Array>; +export type FlagReportsResponse = APIResponse & { + flag_reports: Array; }; -export type ReviewFlagReportResponse = APIResponse & { - flag_report: FlagReport; +export type ReviewFlagReportResponse = APIResponse & { + flag_report: FlagReport; }; -export type BannedUsersResponse = APIResponse & { +export type BannedUsersResponse = APIResponse & { bans?: Array<{ - user: UserResponse; - banned_by?: UserResponse; - channel?: ChannelResponse; + user: UserResponse; + banned_by?: UserResponse; + channel?: ChannelResponse; expires?: string; ip_ban?: boolean; reason?: string; @@ -270,9 +261,7 @@ export type BlockListResponse = BlockList & { updated_at?: string; }; -export type ChannelResponse< - StreamChatGenerics extends ExtendableGenerics = DefaultGenerics -> = StreamChatGenerics['channelType'] & { +export type ChannelResponse = CustomChannelData & { cid: string; disabled: boolean; frozen: boolean; @@ -280,10 +269,10 @@ export type ChannelResponse< type: string; auto_translation_enabled?: boolean; auto_translation_language?: TranslationLanguages | ''; - config?: ChannelConfigWithInfo; + config?: ChannelConfigWithInfo; cooldown?: number; created_at?: string; - created_by?: UserResponse | null; + created_by?: UserResponse | null; created_by_id?: string; deleted_at?: string; hidden?: boolean; @@ -291,44 +280,43 @@ export type ChannelResponse< joined?: boolean; last_message_at?: string; member_count?: number; - members?: ChannelMemberResponse[]; + members?: ChannelMemberResponse[]; muted?: boolean; - name?: string; + name?: string; // FIXME: I believe this property should live in CustomChannelData own_capabilities?: string[]; team?: string; truncated_at?: string; - truncated_by?: UserResponse; + truncated_by?: UserResponse; truncated_by_id?: string; updated_at?: string; }; export type QueryReactionsOptions = Pager; -export type QueryReactionsAPIResponse = APIResponse & { - reactions: ReactionResponse[]; +export type QueryReactionsAPIResponse = APIResponse & { + reactions: ReactionResponse[]; next?: string; }; -export type QueryChannelsAPIResponse = APIResponse & { - channels: Omit, keyof APIResponse>[]; +export type QueryChannelsAPIResponse = APIResponse & { + channels: Omit[]; }; -export type QueryChannelAPIResponse = APIResponse & - ChannelAPIResponse; +export type QueryChannelAPIResponse = APIResponse & ChannelAPIResponse; -export type ChannelAPIResponse = { - channel: ChannelResponse; - members: ChannelMemberResponse[]; - messages: MessageResponse[]; - pinned_messages: MessageResponse[]; +export type ChannelAPIResponse = { + channel: ChannelResponse; + members: ChannelMemberResponse[]; + messages: MessageResponse[]; + pinned_messages: MessageResponse[]; hidden?: boolean; - membership?: ChannelMemberResponse | null; - pending_messages?: PendingMessageResponse[]; + membership?: ChannelMemberResponse | null; + pending_messages?: PendingMessageResponse[]; push_preferences?: PushPreference; - read?: ReadResponse[]; + read?: ReadResponse[]; threads?: ThreadResponse[]; watcher_count?: number; - watchers?: UserResponse[]; + watchers?: UserResponse[]; }; export type ChannelUpdateOptions = { @@ -336,21 +324,17 @@ export type ChannelUpdateOptions = { skip_push?: boolean; }; -export type ChannelMemberAPIResponse = APIResponse & { - members: ChannelMemberResponse[]; +export type ChannelMemberAPIResponse = APIResponse & { + members: ChannelMemberResponse[]; }; -export type ChannelMemberUpdates< - StreamChatGenerics extends ExtendableGenerics = DefaultGenerics -> = StreamChatGenerics['memberType'] & { +export type ChannelMemberUpdates = CustomMemberData & { archived?: boolean; channel_role?: Role; pinned?: boolean; }; -export type ChannelMemberResponse< - StreamChatGenerics extends ExtendableGenerics = DefaultGenerics -> = StreamChatGenerics['memberType'] & { +export type ChannelMemberResponse = CustomMemberData & { archived_at?: string; ban_expires?: string; banned?: boolean; @@ -366,14 +350,12 @@ export type ChannelMemberResponse< shadow_banned?: boolean; status?: InviteStatus; updated_at?: string; - user?: UserResponse; + user?: UserResponse; user_id?: string; }; -export type PartialUpdateMemberAPIResponse< - StreamChatGenerics extends ExtendableGenerics = DefaultGenerics -> = APIResponse & { - channel_member: ChannelMemberResponse; +export type PartialUpdateMemberAPIResponse = APIResponse & { + channel_member: ChannelMemberResponse; }; export type CheckPushResponse = APIResponse & { @@ -403,40 +385,36 @@ export type CheckSNSResponse = APIResponse & { error?: string; }; -export type CommandResponse< - StreamChatGenerics extends ExtendableGenerics = DefaultGenerics -> = Partial & { +export type CommandResponse = Partial & { args?: string; description?: string; - name?: CommandVariants; - set?: CommandVariants; + name?: CommandVariants; + set?: CommandVariants; }; -export type ConnectAPIResponse< - StreamChatGenerics extends ExtendableGenerics = DefaultGenerics -> = Promise>; +export type ConnectAPIResponse = Promise; -export type CreateChannelResponse = APIResponse & - Omit, 'client_id' | 'connection_id'> & { +export type CreateChannelResponse = APIResponse & + Omit & { created_at: string; updated_at: string; grants?: Record; }; -export type CreateCommandResponse = APIResponse & { - command: CreateCommandOptions & CreatedAtUpdatedAt; +export type CreateCommandResponse = APIResponse & { + command: CreateCommandOptions & CreatedAtUpdatedAt; }; -export type DeleteChannelAPIResponse = APIResponse & { - channel: ChannelResponse; +export type DeleteChannelAPIResponse = APIResponse & { + channel: ChannelResponse; }; -export type DeleteCommandResponse = APIResponse & { - name?: CommandVariants; +export type DeleteCommandResponse = APIResponse & { + name?: CommandVariants; }; -export type EventAPIResponse = APIResponse & { - event: Event; +export type EventAPIResponse = APIResponse & { + event: Event; }; export type ExportChannelResponse = { @@ -454,13 +432,13 @@ export type ExportChannelStatusResponse = { updated_at?: string; }; -export type FlagMessageResponse = APIResponse & { +export type FlagMessageResponse = APIResponse & { flag: { created_at: string; created_by_automod: boolean; target_message_id: string; updated_at: string; - user: UserResponse; + user: UserResponse; approved_at?: string; channel_cid?: string; details?: Object; // Any JSON @@ -472,13 +450,13 @@ export type FlagMessageResponse = APIResponse & { +export type FlagUserResponse = APIResponse & { flag: { created_at: string; created_by_automod: boolean; - target_user: UserResponse; + target_user: UserResponse; updated_at: string; - user: UserResponse; + user: UserResponse; approved_at?: string; details?: Object; // Any JSON rejected_at?: string; @@ -488,65 +466,46 @@ export type FlagUserResponse = Omit< - MessageResponse<{ - attachmentType: StreamChatGenerics['attachmentType']; - channelType: StreamChatGenerics['channelType']; - commandType: StreamChatGenerics['commandType']; - eventType: StreamChatGenerics['eventType']; - memberType: StreamChatGenerics['memberType']; - messageType: {}; - pollOptionType: StreamChatGenerics['pollOptionType']; - pollType: StreamChatGenerics['pollType']; - reactionType: StreamChatGenerics['reactionType']; - userType: StreamChatGenerics['userType']; - }>, +export type FormatMessageResponse = Omit< + MessageResponse, 'created_at' | 'pinned_at' | 'updated_at' | 'deleted_at' | 'status' -> & - StreamChatGenerics['messageType'] & { - created_at: Date; - deleted_at: Date | null; - pinned_at: Date | null; - status: string; - updated_at: Date; - }; +> & { + created_at: Date; + deleted_at: Date | null; + pinned_at: Date | null; + status: string; + updated_at: Date; +}; -export type GetChannelTypeResponse = APIResponse & - Omit, 'client_id' | 'connection_id' | 'commands'> & { +export type GetChannelTypeResponse = APIResponse & + Omit & { created_at: string; updated_at: string; - commands?: CommandResponse[]; + commands?: CommandResponse[]; grants?: Record; }; -export type GetCommandResponse = APIResponse & - CreateCommandOptions & - CreatedAtUpdatedAt; - -export type GetMessageAPIResponse< - StreamChatGenerics extends ExtendableGenerics = DefaultGenerics -> = SendMessageAPIResponse; +export type GetCommandResponse = APIResponse & CreateCommandOptions & CreatedAtUpdatedAt; -// eslint-disable-next-line @typescript-eslint/no-empty-interface -export interface ThreadResponseCustomData {} +export type GetMessageAPIResponse = SendMessageAPIResponse; -export interface ThreadResponse extends ThreadResponseCustomData { +export interface ThreadResponse extends CustomThreadData { // FIXME: according to OpenAPI, `channel` could be undefined but since cid is provided I'll asume that it's wrong - channel: ChannelResponse; + channel: ChannelResponse; channel_cid: string; created_at: string; created_by_user_id: string; - latest_replies: Array>; - parent_message: MessageResponse; + latest_replies: Array; + parent_message: MessageResponse; parent_message_id: string; title: string; updated_at: string; active_participant_count?: number; - created_by?: UserResponse; + created_by?: UserResponse; deleted_at?: string; last_message_at?: string; participant_count?: number; - read?: Array>; + read?: Array; reply_count?: number; thread_participants?: Array<{ channel_cid: string; @@ -555,11 +514,11 @@ export interface ThreadResponse; + user?: UserResponse; user_id?: string; }>; // TODO: when moving to API v2 we should do this instead - // custom: ThreadResponseCustomData; + // custom: CustomThreadType; } // TODO: Figure out a way to strongly type set and unset. @@ -577,8 +536,8 @@ export type QueryThreadsOptions = { watch?: boolean; }; -export type QueryThreadsAPIResponse = APIResponse & { - threads: ThreadResponse[]; +export type QueryThreadsAPIResponse = APIResponse & { + threads: ThreadResponse[]; next?: string; }; @@ -589,14 +548,12 @@ export type GetThreadOptions = { watch?: boolean; }; -export type GetThreadAPIResponse = APIResponse & { - thread: ThreadResponse; +export type GetThreadAPIResponse = APIResponse & { + thread: ThreadResponse; }; -export type GetMultipleMessagesAPIResponse< - StreamChatGenerics extends ExtendableGenerics = DefaultGenerics -> = APIResponse & { - messages: MessageResponse[]; +export type GetMultipleMessagesAPIResponse = APIResponse & { + messages: MessageResponse[]; }; export type GetRateLimitsResponse = APIResponse & { @@ -606,12 +563,12 @@ export type GetRateLimitsResponse = APIResponse & { web?: RateLimitsMap; }; -export type GetReactionsAPIResponse = APIResponse & { - reactions: ReactionResponse[]; +export type GetReactionsAPIResponse = APIResponse & { + reactions: ReactionResponse[]; }; -export type GetRepliesAPIResponse = APIResponse & { - messages: MessageResponse[]; +export type GetRepliesAPIResponse = APIResponse & { + messages: MessageResponse[]; }; export type GetUnreadCountAPIResponse = APIResponse & { @@ -660,11 +617,11 @@ export type GetUnreadCountBatchAPIResponse = APIResponse & { counts_by_user: { [userId: string]: GetUnreadCountAPIResponse }; }; -export type ListChannelResponse = APIResponse & { +export type ListChannelResponse = APIResponse & { channel_types: Record< string, - Omit, 'client_id' | 'connection_id' | 'commands'> & { - commands: CommandResponse[]; + Omit & { + commands: CommandResponse[]; created_at: string; updated_at: string; grants?: Record; @@ -672,34 +629,28 @@ export type ListChannelResponse; }; -export type ListChannelTypesAPIResponse< - StreamChatGenerics extends ExtendableGenerics = DefaultGenerics -> = ListChannelResponse; +export type ListChannelTypesAPIResponse = ListChannelResponse; -export type ListCommandsResponse = APIResponse & { - commands: Array & Partial>; +export type ListCommandsResponse = APIResponse & { + commands: Array>; }; -export type MuteChannelAPIResponse = APIResponse & { - channel_mute: ChannelMute; - own_user: OwnUserResponse; - channel_mutes?: ChannelMute[]; - mute?: MuteResponse; +export type MuteChannelAPIResponse = APIResponse & { + channel_mute: ChannelMute; + own_user: OwnUserResponse; + channel_mutes?: ChannelMute[]; + mute?: MuteResponse; }; -export type MessageResponse< - StreamChatGenerics extends ExtendableGenerics = DefaultGenerics -> = MessageResponseBase & { - quoted_message?: MessageResponseBase; +export type MessageResponse = MessageResponseBase & { + quoted_message?: MessageResponseBase; }; -export type MessageResponseBase< - StreamChatGenerics extends ExtendableGenerics = DefaultGenerics -> = MessageBase & { +export type MessageResponseBase = MessageBase & { type: MessageLabel; args?: string; before_message_send_failed?: boolean; - channel?: ChannelResponse; + channel?: ChannelResponse; cid?: string; command?: string; command_info?: { name?: string }; @@ -709,23 +660,23 @@ export type MessageResponseBase< i18n?: RequireAtLeastOne> & { language: TranslationLanguages; }; - latest_reactions?: ReactionResponse[]; - mentioned_users?: UserResponse[]; + latest_reactions?: ReactionResponse[]; + mentioned_users?: UserResponse[]; message_text_updated_at?: string; moderation?: ModerationResponse; // present only with Moderation v2 moderation_details?: ModerationDetailsResponse; // present only with Moderation v1 - own_reactions?: ReactionResponse[] | null; + own_reactions?: ReactionResponse[] | null; pin_expires?: string | null; pinned_at?: string | null; - pinned_by?: UserResponse | null; - poll?: PollResponse; + pinned_by?: UserResponse | null; + poll?: PollResponse; reaction_counts?: { [key: string]: number } | null; reaction_groups?: { [key: string]: ReactionGroupResponse } | null; reaction_scores?: { [key: string]: number } | null; reply_count?: number; shadowed?: boolean; status?: string; - thread_participants?: UserResponse[]; + thread_participants?: UserResponse[]; updated_at?: string; }; @@ -755,18 +706,18 @@ export type ModerationResponse = { original_text: string; }; -export type MuteResponse = { - user: UserResponse; +export type MuteResponse = { + user: UserResponse; created_at?: string; expires?: string; - target?: UserResponse; + target?: UserResponse; updated_at?: string; }; -export type MuteUserResponse = APIResponse & { - mute?: MuteResponse; - mutes?: Array>; - own_user?: OwnUserResponse; +export type MuteUserResponse = APIResponse & { + mute?: MuteResponse; + mutes?: Array; + own_user?: OwnUserResponse; }; export type BlockUserAPIResponse = APIResponse & { @@ -786,10 +737,10 @@ export type BlockedUserDetails = APIResponse & { user_id: string; }; -export type OwnUserBase = { - channel_mutes: ChannelMute[]; - devices: Device[]; - mutes: Mute[]; +export type OwnUserBase = { + channel_mutes: ChannelMute[]; + devices: Device[]; + mutes: Mute[]; total_unread_count: number; unread_channels: number; unread_count: number; @@ -800,15 +751,11 @@ export type OwnUserBase = UserResponse & OwnUserBase; +export type OwnUserResponse = UserResponse & OwnUserBase; -export type PartialUpdateChannelAPIResponse< - StreamChatGenerics extends ExtendableGenerics = DefaultGenerics -> = APIResponse & { - channel: ChannelResponse; - members: ChannelMemberResponse[]; +export type PartialUpdateChannelAPIResponse = APIResponse & { + channel: ChannelResponse; + members: ChannelMemberResponse[]; }; export type PermissionAPIResponse = APIResponse & { @@ -819,29 +766,27 @@ export type PermissionsAPIResponse = APIResponse & { permissions?: PermissionAPIObject[]; }; -export type ReactionAPIResponse = APIResponse & { - message: MessageResponse; - reaction: ReactionResponse; +export type ReactionAPIResponse = APIResponse & { + message: MessageResponse; + reaction: ReactionResponse; }; -export type ReactionResponse< - StreamChatGenerics extends ExtendableGenerics = DefaultGenerics -> = Reaction & { +export type ReactionResponse = Reaction & { created_at: string; message_id: string; updated_at: string; }; -export type ReadResponse = { +export type ReadResponse = { last_read: string; - user: UserResponse; + user: UserResponse; last_read_message_id?: string; unread_messages?: number; }; -export type SearchAPIResponse = APIResponse & { +export type SearchAPIResponse = APIResponse & { results: { - message: MessageResponse; + message: MessageResponse; }[]; next?: string; previous?: string; @@ -858,55 +803,53 @@ export type SearchWarning = { // Thumb URL(thumb_url) is added considering video attachments as the backend will return the thumbnail in the response. export type SendFileAPIResponse = APIResponse & { file: string; thumb_url?: string }; -export type SendMessageAPIResponse = APIResponse & { - message: MessageResponse; +export type SendMessageAPIResponse = APIResponse & { + message: MessageResponse; pending_message_metadata?: Record | null; }; -export type SyncResponse = APIResponse & { - events: Event[]; +export type SyncResponse = APIResponse & { + events: Event[]; inaccessible_cids?: string[]; }; -export type TruncateChannelAPIResponse< - StreamChatGenerics extends ExtendableGenerics = DefaultGenerics -> = APIResponse & { - channel: ChannelResponse; - message?: MessageResponse; +export type TruncateChannelAPIResponse = APIResponse & { + channel: ChannelResponse; + message?: MessageResponse; }; -export type UpdateChannelAPIResponse = APIResponse & { - channel: ChannelResponse; - members: ChannelMemberResponse[]; - message?: MessageResponse; +export type UpdateChannelAPIResponse = APIResponse & { + channel: ChannelResponse; + members: ChannelMemberResponse[]; + message?: MessageResponse; }; -export type UpdateChannelResponse = APIResponse & - Omit, 'client_id' | 'connection_id'> & { +export type UpdateChannelResponse = APIResponse & + Omit & { created_at: string; updated_at: string; }; -export type UpdateCommandResponse = APIResponse & { - command: UpdateCommandOptions & +export type UpdateCommandResponse = APIResponse & { + command: UpdateCommandOptions & CreatedAtUpdatedAt & { - name: CommandVariants; + name: CommandVariants; }; }; -export type UpdateMessageAPIResponse = APIResponse & { - message: MessageResponse; +export type UpdateMessageAPIResponse = APIResponse & { + message: MessageResponse; }; -export type UsersAPIResponse = APIResponse & { - users: Array>; +export type UsersAPIResponse = APIResponse & { + users: Array; }; -export type UpdateUsersAPIResponse = APIResponse & { - users: { [key: string]: UserResponse }; +export type UpdateUsersAPIResponse = APIResponse & { + users: { [key: string]: UserResponse }; }; -export type UserResponse = User & { +export type UserResponse = User & { banned?: boolean; blocked_user_ids?: string[]; created_at?: string; @@ -914,6 +857,8 @@ export type UserResponse = UnBanUserOptions & { - banned_by?: UserResponse; +export type BanUserOptions = UnBanUserOptions & { + banned_by?: UserResponse; banned_by_id?: string; ip_ban?: boolean; reason?: string; @@ -983,10 +928,10 @@ export type ChannelOptions = { watch?: boolean; }; -export type ChannelQueryOptions = { +export type ChannelQueryOptions = { client_id?: string; connection_id?: string; - data?: ChannelResponse; + data?: ChannelResponse; hide_for_creator?: boolean; members?: PaginationOptions; messages?: MessagePaginationOptions; @@ -1001,14 +946,14 @@ export type ChannelStateOptions = { skipInitialization?: string[]; }; -export type CreateChannelOptions = { +export type CreateChannelOptions = { automod?: ChannelConfigAutomod; automod_behavior?: ChannelConfigAutomodBehavior; automod_thresholds?: ChannelConfigAutomodThresholds; blocklist?: string; blocklist_behavior?: ChannelConfigAutomodBehavior; client_id?: string; - commands?: CommandVariants[]; + commands?: CommandVariants[]; connect_events?: boolean; connection_id?: string; custom_events?: boolean; @@ -1033,11 +978,11 @@ export type CreateChannelOptions = { +export type CreateCommandOptions = { description: string; - name: CommandVariants; + name: CommandVariants; args?: string; - set?: CommandVariants; + set?: CommandVariants; }; export type CustomPermissionOptions = { @@ -1055,58 +1000,50 @@ export type DeactivateUsersOptions = { mark_messages_deleted?: boolean; }; -export type NewMemberPayload< - StreamChatGenerics extends ExtendableGenerics = DefaultGenerics -> = StreamChatGenerics['memberType'] & Pick, 'user_id' | 'channel_role'>; +export type NewMemberPayload = CustomMemberData & Pick; -// TODO: rename to UpdateChannelOptions in the next major update and use it in channel._update and/or channel.update -export type InviteOptions = { +export type UpdateChannelOptions = { accept_invite?: boolean; add_members?: string[]; add_moderators?: string[]; client_id?: string; connection_id?: string; - data?: Omit, 'id' | 'cid'>; + data?: Omit; demote_moderators?: string[]; invites?: string[]; - message?: MessageResponse; + message?: MessageResponse; reject_invite?: boolean; remove_members?: string[]; - user?: UserResponse; + user?: UserResponse; user_id?: string; }; -/** @deprecated use MarkChannelsReadOptions instead */ -export type MarkAllReadOptions< - StreamChatGenerics extends ExtendableGenerics = DefaultGenerics -> = MarkChannelsReadOptions; - -export type MarkChannelsReadOptions = { +export type MarkChannelsReadOptions = { client_id?: string; connection_id?: string; read_by_channel?: Record; - user?: UserResponse; + user?: UserResponse; user_id?: string; }; -export type MarkReadOptions = { +export type MarkReadOptions = { client_id?: string; connection_id?: string; thread_id?: string; - user?: UserResponse; + user?: UserResponse; user_id?: string; }; -export type MarkUnreadOptions = { +export type MarkUnreadOptions = { client_id?: string; connection_id?: string; message_id?: string; thread_id?: string; - user?: UserResponse; + user?: UserResponse; user_id?: string; }; -export type MuteUserOptions = { +export type MuteUserOptions = { client_id?: string; connection_id?: string; id?: string; @@ -1114,7 +1051,7 @@ export type MuteUserOptions; + user?: UserResponse; user_id?: string; }; @@ -1185,11 +1122,11 @@ export type ReactivateUsersOptions = { restore_messages?: boolean; }; -export type SearchOptions = { +export type SearchOptions = { limit?: number; next?: string; offset?: number; - sort?: SearchMessageSort; + sort?: SearchMessageSort; }; export type StreamChatOptions = AxiosRequestConfig & { @@ -1265,19 +1202,15 @@ export type UnBanUserOptions = { type?: string; }; -// TODO: rename to UpdateChannelTypeOptions in the next major update -export type UpdateChannelOptions = Omit< - CreateChannelOptions, - 'name' -> & { +export type UpdateChannelTypeOptions = Omit & { created_at?: string; updated_at?: string; }; -export type UpdateCommandOptions = { +export type UpdateCommandOptions = { description: string; args?: string; - set?: CommandVariants; + set?: CommandVariants; }; export type UserOptions = { @@ -1296,11 +1229,11 @@ export type ConnectionChangeEvent = { online?: boolean; }; -export type Event = StreamChatGenerics['eventType'] & { +export type Event = CustomEventData & { type: EventTypes; ai_message?: string; ai_state?: AIState; - channel?: ChannelResponse; + channel?: ChannelResponse; channel_id?: string; channel_type?: string; cid?: string; @@ -1315,23 +1248,25 @@ export type Event; - member?: ChannelMemberResponse; - message?: MessageResponse; + me?: OwnUserResponse; + member?: ChannelMemberResponse; + message?: MessageResponse; message_id?: string; mode?: string; online?: boolean; + own_capabilities?: string[]; parent_id?: string; - poll?: PollResponse; - poll_vote?: PollVote | PollAnswer; + poll?: PollResponse; + poll_vote?: PollVote | PollAnswer; queriedChannels?: { - channels: ChannelAPIResponse[]; + channels: ChannelAPIResponse[]; isLatestMessageSet?: boolean; }; - reaction?: ReactionResponse; + reaction?: ReactionResponse; received_at?: string | Date; + shadow?: boolean; team?: string; - thread?: ThreadResponse; + thread?: ThreadResponse; // @deprecated number of all unread messages across all current user's unread channels, equals unread_count total_unread_count?: number; // number of all current user's channels with at least one unread message including the channel in this event @@ -1342,20 +1277,16 @@ export type Event; + user?: UserResponse; user_id?: string; watcher_count?: number; }; -export type UserCustomEvent< - StreamChatGenerics extends ExtendableGenerics = DefaultGenerics -> = StreamChatGenerics['eventType'] & { +export type UserCustomEvent = CustomEventData & { type: string; }; -export type EventHandler = ( - event: Event, -) => void; +export type EventHandler = (event: Event) => void; export type EventTypes = 'all' | keyof typeof EVENT_MAP; @@ -1510,15 +1441,15 @@ export type BannedUsersFilters = QueryFilters< } >; -export type ReactionFilters = QueryFilters< +export type ReactionFilters = QueryFilters< { user_id?: - | RequireOnlyOne['user_id']>, '$eq' | '$in'>> - | PrimitiveFilter['user_id']>; + | RequireOnlyOne, '$eq' | '$in'>> + | PrimitiveFilter; } & { type?: - | RequireOnlyOne['type']>, '$eq'>> - | PrimitiveFilter['type']>; + | RequireOnlyOne, '$eq'>> + | PrimitiveFilter; } & { created_at?: | RequireOnlyOne, '$eq' | '$gt' | '$lt' | '$gte' | '$lte'>> @@ -1526,66 +1457,24 @@ export type ReactionFilters; -export type ChannelFilters = QueryFilters< - ContainsOperator & { +export type ChannelFilters = QueryFilters< + ContainsOperator & { members?: - | RequireOnlyOne, '$in' | '$nin'>> + | RequireOnlyOne, '$in'>> | RequireOnlyOne, '$eq'>> | PrimitiveFilter; } & { name?: | RequireOnlyOne< { - $autocomplete?: ChannelResponse['name']; - } & QueryFilter['name']> + $autocomplete?: ChannelResponse['name']; + } & QueryFilter > - | PrimitiveFilter['name']>; + | PrimitiveFilter; } & { - [Key in keyof Omit< - ChannelResponse<{ - attachmentType: StreamChatGenerics['attachmentType']; - channelType: {}; - commandType: StreamChatGenerics['commandType']; - eventType: StreamChatGenerics['eventType']; - memberType: StreamChatGenerics['memberType']; - messageType: StreamChatGenerics['messageType']; - pollOptionType: StreamChatGenerics['pollOptionType']; - pollType: StreamChatGenerics['pollType']; - reactionType: StreamChatGenerics['reactionType']; - userType: StreamChatGenerics['userType']; - }>, - 'name' | 'members' - >]: - | RequireOnlyOne< - QueryFilter< - ChannelResponse<{ - attachmentType: StreamChatGenerics['attachmentType']; - channelType: {}; - commandType: StreamChatGenerics['commandType']; - eventType: StreamChatGenerics['eventType']; - memberType: StreamChatGenerics['memberType']; - messageType: StreamChatGenerics['messageType']; - pollOptionType: StreamChatGenerics['pollOptionType']; - pollType: StreamChatGenerics['pollType']; - reactionType: StreamChatGenerics['reactionType']; - userType: StreamChatGenerics['userType']; - }>[Key] - > - > - | PrimitiveFilter< - ChannelResponse<{ - attachmentType: StreamChatGenerics['attachmentType']; - channelType: {}; - commandType: StreamChatGenerics['commandType']; - eventType: StreamChatGenerics['eventType']; - memberType: StreamChatGenerics['memberType']; - messageType: StreamChatGenerics['messageType']; - pollOptionType: StreamChatGenerics['pollOptionType']; - pollType: StreamChatGenerics['pollType']; - reactionType: StreamChatGenerics['reactionType']; - userType: StreamChatGenerics['userType']; - }>[Key] - >; + [Key in keyof Omit]: + | RequireOnlyOne> + | PrimitiveFilter; } & { archived?: boolean; pinned?: boolean; @@ -1621,9 +1510,7 @@ export type QueryPollsFilters = QueryFilters< | PrimitiveFilter; } & { max_votes_allowed?: - | RequireOnlyOne< - Pick, '$eq' | '$ne' | '$gt' | '$lt' | '$gte' | '$lte'> - > + | RequireOnlyOne, '$eq' | '$gt' | '$lt' | '$gte' | '$lte'>> | PrimitiveFilter; } & { allow_answers?: @@ -1700,62 +1587,20 @@ export type ContainsOperator = { : RequireOnlyOne> | PrimitiveFilter; }; -export type MessageFilters = QueryFilters< - ContainsOperator & { +export type MessageFilters = QueryFilters< + ContainsOperator & { text?: | RequireOnlyOne< { - $autocomplete?: MessageResponse['text']; - $q?: MessageResponse['text']; - } & QueryFilter['text']> + $autocomplete?: MessageResponse['text']; + $q?: MessageResponse['text']; + } & QueryFilter > - | PrimitiveFilter['text']>; + | PrimitiveFilter; } & { - [Key in keyof Omit< - MessageResponse<{ - attachmentType: StreamChatGenerics['attachmentType']; - channelType: StreamChatGenerics['channelType']; - commandType: StreamChatGenerics['commandType']; - eventType: StreamChatGenerics['eventType']; - memberType: StreamChatGenerics['memberType']; - messageType: {}; - pollOptionType: StreamChatGenerics['pollOptionType']; - pollType: StreamChatGenerics['pollType']; - reactionType: StreamChatGenerics['reactionType']; - userType: StreamChatGenerics['userType']; - }>, - 'text' - >]?: - | RequireOnlyOne< - QueryFilter< - MessageResponse<{ - attachmentType: StreamChatGenerics['attachmentType']; - channelType: StreamChatGenerics['channelType']; - commandType: StreamChatGenerics['commandType']; - eventType: StreamChatGenerics['eventType']; - memberType: StreamChatGenerics['memberType']; - messageType: {}; - pollOptionType: StreamChatGenerics['pollOptionType']; - pollType: StreamChatGenerics['pollType']; - reactionType: StreamChatGenerics['reactionType']; - userType: StreamChatGenerics['userType']; - }>[Key] - > - > - | PrimitiveFilter< - MessageResponse<{ - attachmentType: StreamChatGenerics['attachmentType']; - channelType: StreamChatGenerics['channelType']; - commandType: StreamChatGenerics['commandType']; - eventType: StreamChatGenerics['eventType']; - memberType: StreamChatGenerics['memberType']; - messageType: {}; - pollOptionType: StreamChatGenerics['pollOptionType']; - pollType: StreamChatGenerics['pollType']; - reactionType: StreamChatGenerics['reactionType']; - userType: StreamChatGenerics['userType']; - }>[Key] - >; + [Key in keyof Omit]?: + | RequireOnlyOne> + | PrimitiveFilter; } >; @@ -1774,27 +1619,11 @@ export type QueryFilter = NonNullable extends s $in?: PrimitiveFilter[]; $lt?: PrimitiveFilter; $lte?: PrimitiveFilter; - /** - * @deprecated and will be removed in a future release. Filtering shall be applied client-side. - */ - $ne?: PrimitiveFilter; - /** - * @deprecated and will be removed in a future release. Filtering shall be applied client-side. - */ - $nin?: PrimitiveFilter[]; } : { $eq?: PrimitiveFilter; $exists?: boolean; $in?: PrimitiveFilter[]; - /** - * @deprecated and will be removed in a future release. Filtering shall be applied client-side. - */ - $ne?: PrimitiveFilter; - /** - * @deprecated and will be removed in a future release. Filtering shall be applied client-side. - */ - $nin?: PrimitiveFilter[]; }; export type QueryFilters = { @@ -1808,147 +1637,87 @@ export type QueryLogicalOperators = { $or?: ArrayTwoOrMore>; }; -export type UserFilters = QueryFilters< - ContainsOperator & { +export type UserFilters = QueryFilters< + ContainsOperator & { id?: - | RequireOnlyOne< - { $autocomplete?: UserResponse['id'] } & QueryFilter< - UserResponse['id'] - > - > - | PrimitiveFilter['id']>; + | RequireOnlyOne<{ $autocomplete?: UserResponse['id'] } & QueryFilter> + | PrimitiveFilter; name?: - | RequireOnlyOne< - { $autocomplete?: UserResponse['name'] } & QueryFilter< - UserResponse['name'] - > - > - | PrimitiveFilter['name']>; + | RequireOnlyOne<{ $autocomplete?: UserResponse['name'] } & QueryFilter> + | PrimitiveFilter; notifications_muted?: | RequireOnlyOne<{ - $eq?: PrimitiveFilter['notifications_muted']>; + $eq?: PrimitiveFilter; }> | boolean; teams?: | RequireOnlyOne<{ $contains?: PrimitiveFilter; - $eq?: PrimitiveFilter['teams']>; - $in?: PrimitiveFilter['teams']>; + $eq?: PrimitiveFilter; + $in?: PrimitiveFilter; }> - | PrimitiveFilter['teams']>; + | PrimitiveFilter; username?: - | RequireOnlyOne< - { $autocomplete?: UserResponse['username'] } & QueryFilter< - UserResponse['username'] - > - > - | PrimitiveFilter['username']>; + | RequireOnlyOne<{ $autocomplete?: UserResponse['username'] } & QueryFilter> + | PrimitiveFilter; } & { - [Key in keyof Omit< - UserResponse<{ - attachmentType: StreamChatGenerics['attachmentType']; - channelType: StreamChatGenerics['channelType']; - commandType: StreamChatGenerics['commandType']; - eventType: StreamChatGenerics['eventType']; - memberType: StreamChatGenerics['memberType']; - messageType: StreamChatGenerics['messageType']; - pollOptionType: StreamChatGenerics['pollOptionType']; - pollType: StreamChatGenerics['pollType']; - reactionType: StreamChatGenerics['reactionType']; - userType: {}; - }>, - 'id' | 'name' | 'teams' | 'username' - >]?: - | RequireOnlyOne< - QueryFilter< - UserResponse<{ - attachmentType: StreamChatGenerics['attachmentType']; - channelType: StreamChatGenerics['channelType']; - commandType: StreamChatGenerics['commandType']; - eventType: StreamChatGenerics['eventType']; - memberType: StreamChatGenerics['memberType']; - messageType: StreamChatGenerics['messageType']; - pollOptionType: StreamChatGenerics['pollOptionType']; - pollType: StreamChatGenerics['pollType']; - reactionType: StreamChatGenerics['reactionType']; - userType: {}; - }>[Key] - > - > - | PrimitiveFilter< - UserResponse<{ - attachmentType: StreamChatGenerics['attachmentType']; - channelType: StreamChatGenerics['channelType']; - commandType: StreamChatGenerics['commandType']; - eventType: StreamChatGenerics['eventType']; - memberType: StreamChatGenerics['memberType']; - messageType: StreamChatGenerics['messageType']; - pollOptionType: StreamChatGenerics['pollOptionType']; - pollType: StreamChatGenerics['pollType']; - reactionType: StreamChatGenerics['reactionType']; - userType: {}; - }>[Key] - >; + [Key in keyof Omit]?: + | RequireOnlyOne> + | PrimitiveFilter; } >; export type InviteStatus = 'pending' | 'accepted' | 'rejected'; // https://getstream.io/chat/docs/react/channel_member/#update-channel-members -export type MemberFilters = QueryFilters< +export type MemberFilters = QueryFilters< { - banned?: - | { $eq?: ChannelMemberResponse['banned'] } - | ChannelMemberResponse['banned']; - channel_role?: - | { $eq?: ChannelMemberResponse['channel_role'] } - | ChannelMemberResponse['channel_role']; - cid?: { $eq?: ChannelResponse['cid'] } | ChannelResponse['cid']; + banned?: { $eq?: ChannelMemberResponse['banned'] } | ChannelMemberResponse['banned']; + channel_role?: { $eq?: ChannelMemberResponse['channel_role'] } | ChannelMemberResponse['channel_role']; + cid?: { $eq?: ChannelResponse['cid'] } | ChannelResponse['cid']; created_at?: | { - $eq?: ChannelMemberResponse['created_at']; - $gt?: ChannelMemberResponse['created_at']; - $gte?: ChannelMemberResponse['created_at']; - $lt?: ChannelMemberResponse['created_at']; - $lte?: ChannelMemberResponse['created_at']; + $eq?: ChannelMemberResponse['created_at']; + $gt?: ChannelMemberResponse['created_at']; + $gte?: ChannelMemberResponse['created_at']; + $lt?: ChannelMemberResponse['created_at']; + $lte?: ChannelMemberResponse['created_at']; } - | ChannelMemberResponse['created_at']; + | ChannelMemberResponse['created_at']; id?: | RequireOnlyOne<{ - $eq?: UserResponse['id']; - $in?: UserResponse['id'][]; + $eq?: UserResponse['id']; + $in?: UserResponse['id'][]; }> - | UserResponse['id']; - invite?: - | { $eq?: ChannelMemberResponse['status'] } - | ChannelMemberResponse['status']; + | UserResponse['id']; + invite?: { $eq?: ChannelMemberResponse['status'] } | ChannelMemberResponse['status']; joined?: { $eq?: boolean } | boolean; last_active?: | { - $eq?: UserResponse['last_active']; - $gt?: UserResponse['last_active']; - $gte?: UserResponse['last_active']; - $lt?: UserResponse['last_active']; - $lte?: UserResponse['last_active']; + $eq?: UserResponse['last_active']; + $gt?: UserResponse['last_active']; + $gte?: UserResponse['last_active']; + $lt?: UserResponse['last_active']; + $lte?: UserResponse['last_active']; } - | UserResponse['last_active']; + | UserResponse['last_active']; name?: | RequireOnlyOne<{ - $autocomplete?: ChannelMemberResponse['name']; - $eq?: ChannelMemberResponse['name']; - $in?: ChannelMemberResponse['name'][]; - $q?: ChannelMemberResponse['name']; + $autocomplete?: NonNullable['name']; + $eq?: NonNullable['name']; + $in?: NonNullable['name'][]; + $q?: NonNullable['name']; }> - | PrimitiveFilter['name']>; + | PrimitiveFilter['name']>; updated_at?: | { - $eq?: ChannelMemberResponse['updated_at']; - $gt?: ChannelMemberResponse['updated_at']; - $gte?: ChannelMemberResponse['updated_at']; - $lt?: ChannelMemberResponse['updated_at']; - $lte?: ChannelMemberResponse['updated_at']; + $eq?: ChannelMemberResponse['updated_at']; + $gt?: ChannelMemberResponse['updated_at']; + $gte?: ChannelMemberResponse['updated_at']; + $lt?: ChannelMemberResponse['updated_at']; + $lte?: ChannelMemberResponse['updated_at']; } - | ChannelMemberResponse['updated_at']; + | ChannelMemberResponse['updated_at']; 'user.email'?: | RequireOnlyOne<{ $autocomplete?: string; @@ -1958,14 +1727,14 @@ export type MemberFilters['user_id']; - $in?: ChannelMemberResponse['user_id'][]; + $eq?: ChannelMemberResponse['user_id']; + $in?: ChannelMemberResponse['user_id'][]; }> - | PrimitiveFilter['id']>; + | PrimitiveFilter['id'][]>; } & { - [Key in keyof ContainsOperator]?: - | RequireOnlyOne[Key]>> - | PrimitiveFilter[Key]>; + [Key in keyof ContainsOperator]?: + | RequireOnlyOne[Key]>> + | PrimitiveFilter[Key]>; } >; @@ -1977,23 +1746,15 @@ export type BannedUsersSort = BannedUsersSortBase | Array; export type BannedUsersSortBase = { created_at?: AscDesc }; -export type ReactionSort = - | ReactionSortBase - | Array>; +export type ReactionSort = ReactionSortBase | Array; -export type ReactionSortBase = Sort< - StreamChatGenerics['reactionType'] -> & { +export type ReactionSortBase = Sort & { created_at?: AscDesc; }; -export type ChannelSort = - | ChannelSortBase - | Array>; +export type ChannelSort = ChannelSortBase | Array; -export type ChannelSortBase = Sort< - StreamChatGenerics['channelType'] -> & { +export type ChannelSortBase = Sort & { created_at?: AscDesc; has_unread?: AscDesc; last_message_at?: AscDesc; @@ -2011,17 +1772,13 @@ export type Sort = { [P in keyof T]?: AscDesc; }; -export type UserSort = - | Sort> - | Array>>; +export type UserSort = Sort | Array>; -export type MemberSort = - | Sort, 'id' | 'created_at' | 'last_active' | 'name' | 'updated_at'>> - | Array, 'id' | 'created_at' | 'last_active' | 'name' | 'updated_at'>>>; +export type MemberSort = + | Sort> + | Array>>; -export type SearchMessageSortBase = Sort< - StreamChatGenerics['messageType'] -> & { +export type SearchMessageSortBase = Sort & { attachments?: AscDesc; 'attachments.type'?: AscDesc; created_at?: AscDesc; @@ -2037,15 +1794,9 @@ export type SearchMessageSortBase = - | SearchMessageSortBase - | Array>; +export type SearchMessageSort = SearchMessageSortBase | Array; -export type QuerySort = - | BannedUsersSort - | ChannelSort - | SearchMessageSort - | UserSort; +export type QuerySort = BannedUsersSort | ChannelSort | SearchMessageSort | UserSort; export type PollSort = PollSortBase | Array; @@ -2181,9 +1932,7 @@ export type AppSettings = { }; }; -export type Attachment< - StreamChatGenerics extends ExtendableGenerics = DefaultGenerics -> = StreamChatGenerics['attachmentType'] & { +export type Attachment = CustomAttachmentData & { actions?: Action[]; asset_url?: string; author_icon?: string; @@ -2234,9 +1983,9 @@ export type BlockList = { validate?: boolean; }; -export type ChannelConfig = ChannelConfigFields & +export type ChannelConfig = ChannelConfigFields & CreatedAtUpdatedAt & { - commands?: CommandVariants[]; + commands?: CommandVariants[]; }; export type ChannelConfigAutomod = '' | 'AI' | 'disabled' | 'simple'; @@ -2274,31 +2023,22 @@ export type ChannelConfigFields = { url_enrichment?: boolean; }; -export type ChannelConfigWithInfo< - StreamChatGenerics extends ExtendableGenerics = DefaultGenerics -> = ChannelConfigFields & +export type ChannelConfigWithInfo = ChannelConfigFields & CreatedAtUpdatedAt & { - commands?: CommandResponse[]; + commands?: CommandResponse[]; }; -export type ChannelData< - StreamChatGenerics extends ExtendableGenerics = DefaultGenerics -> = StreamChatGenerics['channelType'] & { +export type ChannelData = CustomChannelData & { blocked?: boolean; - members?: string[] | Array>; + created_by?: UserResponse | null; + created_by_id?: string; + members?: string[] | Array; name?: string; }; -/** - * @deprecated Use ChannelMemberResponse instead - */ -export type ChannelMembership< - StreamChatGenerics extends ExtendableGenerics = DefaultGenerics -> = ChannelMemberResponse; - -export type ChannelMute = { - user: UserResponse; - channel?: ChannelResponse; +export type ChannelMute = { + user: UserResponse; + channel?: ChannelResponse; created_at?: string; expires?: string; updated_at?: string; @@ -2312,14 +2052,14 @@ export type ChannelRole = { same_team?: boolean; }; -export type CheckPushInput = { +export type CheckPushInput = { apn_template?: string; client_id?: string; connection_id?: string; firebase_data_template?: string; firebase_template?: string; message_id?: string; - user?: UserResponse; + user?: UserResponse; user_id?: string; }; @@ -2375,7 +2115,7 @@ export type PushProviderXiaomi = { xiaomi_secret?: string; }; -export type CommandVariants = +export type CommandVariants = | 'all' | 'ban' | 'fun_set' @@ -2384,18 +2124,15 @@ export type CommandVariants = Record< - string, - ChannelConfigWithInfo | undefined ->; +export type Configs = Record; -export type ConnectionOpen = { +export type ConnectionOpen = { connection_id: string; cid?: string; created_at?: string; - me?: OwnUserResponse; + me?: OwnUserResponse; type?: string; }; @@ -2404,9 +2141,9 @@ export type CreatedAtUpdatedAt = { updated_at: string; }; -export type Device = DeviceFields & { +export type Device = DeviceFields & { provider?: string; - user?: UserResponse; + user?: UserResponse; user_id?: string; }; @@ -2626,17 +2363,13 @@ export type LogLevel = 'info' | 'error' | 'warn'; export type Logger = (logLevel: LogLevel, message: string, extraData?: Record) => void; -export type Message = Partial< - MessageBase -> & { +export type Message = Partial & { mentioned_users?: string[]; }; -export type MessageBase< - StreamChatGenerics extends ExtendableGenerics = DefaultGenerics -> = StreamChatGenerics['messageType'] & { +export type MessageBase = CustomMessageData & { id: string; - attachments?: Attachment[]; + attachments?: Attachment[]; html?: string; mml?: string; parent_id?: string; @@ -2649,7 +2382,7 @@ export type MessageBase< show_in_channel?: boolean; silent?: boolean; text?: string; - user?: UserResponse | null; + user?: UserResponse | null; user_id?: string; }; @@ -2673,41 +2406,41 @@ export type GetMessageOptions = { show_deleted_message?: boolean; }; -export type Mute = { +export type Mute = { created_at: string; - target: UserResponse; + target: UserResponse; updated_at: string; - user: UserResponse; + user: UserResponse; }; -export type PartialUpdateChannel = { - set?: Partial>; - unset?: Array>; +export type PartialUpdateChannel = { + set?: Partial; + unset?: Array; }; -export type PartialUpdateMember = { - set?: ChannelMemberUpdates; - unset?: Array>; +export type PartialUpdateMember = { + set?: ChannelMemberUpdates; + unset?: Array; }; -export type PartialUserUpdate = { +export type PartialUserUpdate = { id: string; - set?: Partial>; - unset?: Array>; + set?: Partial; + unset?: Array; }; -export type MessageUpdatableFields = Omit< - MessageResponse, +export type MessageUpdatableFields = Omit< + MessageResponse, 'cid' | 'created_at' | 'updated_at' | 'deleted_at' | 'user' | 'user_id' >; -export type PartialMessageUpdate = { - set?: Partial>; - unset?: Array>; +export type PartialMessageUpdate = { + set?: Partial; + unset?: Array; }; -export type PendingMessageResponse = { - message: MessageResponse; +export type PendingMessageResponse = { + message: MessageResponse; pending_message_metadata?: Record; }; @@ -2752,13 +2485,11 @@ export type RateLimitsInfo = { export type RateLimitsMap = Record; -export type Reaction< - StreamChatGenerics extends ExtendableGenerics = DefaultGenerics -> = StreamChatGenerics['reactionType'] & { +export type Reaction = CustomReactionData & { type: string; message_id?: string; score?: number; - user?: UserResponse | null; + user?: UserResponse | null; user_id?: string; }; @@ -2782,19 +2513,16 @@ export type Resource = | 'UpdateUser' | 'UploadAttachment'; -export type SearchPayload = Omit< - SearchOptions, - 'sort' -> & { +export type SearchPayload = Omit & { client_id?: string; connection_id?: string; - filter_conditions?: ChannelFilters; - message_filter_conditions?: MessageFilters; + filter_conditions?: ChannelFilters; + message_filter_conditions?: MessageFilters; message_options?: MessageOptions; query?: string; sort?: Array<{ direction: AscDesc; - field: keyof SearchMessageSortBase; + field: keyof SearchMessageSortBase; }>; }; @@ -2896,15 +2624,11 @@ export type ReservedMessageFields = | 'reply_count' | 'type' | 'updated_at' - | 'user' - | '__html'; + | 'user'; -export type UpdatedMessage = Omit< - MessageResponse, - 'mentioned_users' -> & { mentioned_users?: string[] }; +export type UpdatedMessage = Omit & { mentioned_users?: string[] }; -export type User = StreamChatGenerics['userType'] & { +export type User = CustomUserData & { id: string; anon?: boolean; name?: string; @@ -3077,12 +2801,12 @@ export type TaskStatus = { result?: UR; }; -export type TruncateOptions = { +export type TruncateOptions = { hard_delete?: boolean; - message?: Message; + message?: Message; skip_push?: boolean; truncated_at?: Date; - user?: UserResponse; + user?: UserResponse; user_id?: string; }; @@ -3130,10 +2854,10 @@ export type ImportTask = { }; export type MessageSetType = 'latest' | 'current' | 'new'; -export type MessageSet = { +export type MessageSet = { isCurrent: boolean; isLatest: boolean; - messages: FormatMessageResponse[]; + messages: FormatMessageResponse[]; pagination: { hasNext: boolean; hasPrev: boolean }; }; @@ -3145,11 +2869,11 @@ export type PushProviderListResponse = { push_providers: PushProvider[]; }; -export type CreateCallOptions = { +export type CreateCallOptions = { id: string; type: string; options?: UR; - user?: UserResponse | null; + user?: UserResponse | null; user_id?: string; }; @@ -3201,51 +2925,50 @@ export class ErrorFromResponse extends Error { status?: number; } -export type QueryPollsResponse = { - polls: PollResponse[]; +export type QueryPollsResponse = { + polls: PollResponse[]; next?: string; }; -export type CreatePollAPIResponse = { - poll: PollResponse; +export type CreatePollAPIResponse = { + poll: PollResponse; }; -export type GetPollAPIResponse = { - poll: PollResponse; +export type GetPollAPIResponse = { + poll: PollResponse; }; -export type UpdatePollAPIResponse = { - poll: PollResponse; +export type UpdatePollAPIResponse = { + poll: PollResponse; }; -export type PollResponse< - StreamChatGenerics extends ExtendableGenerics = DefaultGenerics -> = StreamChatGenerics['pollType'] & - PollEnrichData & { +export type PollResponse = CustomPollData & + PollEnrichData & { created_at: string; - created_by: UserResponse | null; + created_by: UserResponse | null; created_by_id: string; enforce_unique_vote: boolean; id: string; max_votes_allowed: number; name: string; - options: PollOption[]; + options: PollOption[]; updated_at: string; allow_answers?: boolean; allow_user_suggested_options?: boolean; description?: string; + enforce_unique_votes?: boolean; is_closed?: boolean; voting_visibility?: VotingVisibility; }; -export type PollOption = { +export type PollOption = { created_at: string; id: string; poll_id: string; text: string; updated_at: string; vote_count: number; - votes?: PollVote[]; + votes?: PollVote[]; }; export enum VotingVisibility { @@ -3253,18 +2976,16 @@ export enum VotingVisibility { public = 'public', } -export type PollEnrichData = { +export type PollEnrichData = { answers_count: number; - latest_answers: PollAnswer[]; // not updated with WS events, ordered DESC by created_at, seems like updated_at cannot be different from created_at - latest_votes_by_option: Record[]>; // not updated with WS events; always null in anonymous polls + latest_answers: PollAnswer[]; // not updated with WS events, ordered DESC by created_at, seems like updated_at cannot be different from created_at + latest_votes_by_option: Record; // not updated with WS events; always null in anonymous polls vote_count: number; vote_counts_by_option: Record; - own_votes?: (PollVote | PollAnswer)[]; // not updated with WS events + own_votes?: (PollVote | PollAnswer)[]; // not updated with WS events }; -export type PollData< - StreamChatGenerics extends ExtendableGenerics = DefaultGenerics -> = StreamChatGenerics['pollType'] & { +export type PollData = CustomPollData & { id: string; name: string; allow_answers?: boolean; @@ -3273,32 +2994,27 @@ export type PollData< enforce_unique_vote?: boolean; is_closed?: boolean; max_votes_allowed?: number; - options?: PollOptionData[]; + options?: PollOptionData[]; user_id?: string; voting_visibility?: VotingVisibility; }; -export type CreatePollData = Partial< - PollData -> & - Pick, 'name'>; +export type CreatePollData = Partial & Pick; -export type PartialPollUpdate = { - set?: Partial>; - unset?: Array>; +export type PartialPollUpdate = { + set?: Partial; + unset?: Array; }; -export type PollOptionData< - StreamChatGenerics extends ExtendableGenerics = DefaultGenerics -> = StreamChatGenerics['pollOptionType'] & { +export type PollOptionData = CustomPollOptionData & { text: string; id?: string; position?: number; }; -export type PartialPollOptionUpdate = { - set?: Partial>; - unset?: Array>; +export type PartialPollOptionUpdate = { + set?: Partial; + unset?: Array; }; export type PollVoteData = { @@ -3312,20 +3028,14 @@ export type PollPaginationOptions = { next?: string; }; -export type CreatePollOptionAPIResponse = { - poll_option: PollOptionResponse; +export type CreatePollOptionAPIResponse = { + poll_option: PollOptionResponse; }; -export type GetPollOptionAPIResponse< - StreamChatGenerics extends ExtendableGenerics = DefaultGenerics -> = CreatePollOptionAPIResponse; -export type UpdatePollOptionAPIResponse< - StreamChatGenerics extends ExtendableGenerics = DefaultGenerics -> = CreatePollOptionAPIResponse; +export type GetPollOptionAPIResponse = CreatePollOptionAPIResponse; +export type UpdatePollOptionAPIResponse = CreatePollOptionAPIResponse; -export type PollOptionResponse< - StreamChatGenerics extends ExtendableGenerics = DefaultGenerics -> = StreamChatGenerics['pollType'] & { +export type PollOptionResponse = CustomPollData & { created_at: string; id: string; poll_id: string; @@ -3333,39 +3043,36 @@ export type PollOptionResponse< text: string; updated_at: string; vote_count: number; - votes?: PollVote[]; + votes?: PollVote[]; }; -export type PollVote = { +export type PollVote = { created_at: string; id: string; poll_id: string; updated_at: string; option_id?: string; - user?: UserResponse; + user?: UserResponse; user_id?: string; }; -export type PollAnswer = Exclude< - PollVote, - 'option_id' -> & { +export type PollAnswer = Exclude & { answer_text: string; is_answer: boolean; // this is absolutely redundant prop as answer_text indicates that a vote is an answer }; -export type PollVotesAPIResponse = { - votes: (PollVote | PollAnswer)[]; +export type PollVotesAPIResponse = { + votes: (PollVote | PollAnswer)[]; next?: string; }; -export type PollAnswersAPIResponse = { - votes: PollAnswer[]; // todo: should be changes to answers? +export type PollAnswersAPIResponse = { + votes: PollAnswer[]; // todo: should be changes to answers? next?: string; }; -export type CastVoteAPIResponse = { - vote: PollVote | PollAnswer; +export type CastVoteAPIResponse = { + vote: PollVote | PollAnswer; }; export type QueryMessageHistoryFilters = QueryFilters< @@ -3395,16 +3102,16 @@ export type QueryMessageHistorySortBase = { export type QueryMessageHistoryOptions = Pager; -export type MessageHistoryEntry = { +export type MessageHistoryEntry = { message_id: string; message_updated_at: string; - attachments?: Attachment[]; + attachments?: Attachment[]; message_updated_by_id?: string; text?: string; }; -export type QueryMessageHistoryResponse = { - message_history: MessageHistoryEntry[]; +export type QueryMessageHistoryResponse = { + message_history: MessageHistoryEntry[]; next?: string; prev?: string; }; @@ -3498,14 +3205,14 @@ export type SubmitActionOptions = { user_id?: string; }; -export type GetUserModerationReportResponse = { - user: UserResponse; +export type GetUserModerationReportResponse = { + user: UserResponse; user_blocks?: Array<{ blocked_at: string; blocked_by_user_id: string; blocked_user_id: string; }>; - user_mutes?: Mute[]; + user_mutes?: Mute[]; }; export type QueryModerationConfigsFilters = QueryFilters< @@ -3798,10 +3505,10 @@ export type VelocityFilterConfig = { async?: boolean; }; -export type PromoteChannelParams = { - channels: Array>; - channelToMove: Channel; - sort: ChannelSort; +export type PromoteChannelParams = { + channels: Array; + channelToMove: Channel; + sort: ChannelSort; /** * If the index of the channel within `channels` list which is being moved upwards * (`channelToMove`) is known, you can supply it to skip extra calculation. diff --git a/src/utils.ts b/src/utils.ts index 5691c2512..fdbcb196b 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,8 +1,6 @@ import FormData from 'form-data'; import { AscDesc, - ExtendableGenerics, - DefaultGenerics, Logger, OwnUserBase, OwnUserResponse, @@ -76,10 +74,8 @@ function isFileWebAPI(uri: unknown): uri is File { return typeof window !== 'undefined' && 'File' in window && uri instanceof File; } -export function isOwnUser( - user?: OwnUserResponse | UserResponse, -): user is OwnUserResponse { - return (user as OwnUserResponse)?.total_unread_count !== undefined; +export function isOwnUser(user?: OwnUserResponse | UserResponse): user is OwnUserResponse { + return (user as OwnUserResponse)?.total_unread_count !== undefined; } function isBlobWebAPI(uri: unknown): uri is Blob { @@ -292,17 +288,11 @@ export const axiosParamsSerializer: AxiosRequestConfig['paramsSerializer'] = (pa * Takes the message object, parses the dates, sets `__html` * and sets the status to `received` if missing; returns a new message object. * - * @param {MessageResponse} message `MessageResponse` object + * @param {MessageResponse} message `MessageResponse` object */ -export function formatMessage( - message: MessageResponse | FormatMessageResponse, -): FormatMessageResponse { +export function formatMessage(message: MessageResponse | FormatMessageResponse): FormatMessageResponse { return { ...message, - /** - * @deprecated please use `html` - */ - __html: message.html, // parse the dates pinned_at: message.pinned_at ? new Date(message.pinned_at) : null, created_at: message.created_at ? new Date(message.created_at) : new Date(), @@ -623,10 +613,10 @@ export const uniqBy = (array: T[] | unknown, iteratee: ((item: T) => unknown) }); }; -type MessagePaginationUpdatedParams = { +type MessagePaginationUpdatedParams = { parentSet: MessageSet; requestedPageSize: number; - returnedPage: MessageResponse[]; + returnedPage: MessageResponse[]; logger?: Logger; messagePaginationOptions?: MessagePaginationOptions; }; @@ -661,12 +651,12 @@ export function binarySearchByDateEqualOrNearestGreater( return left; } -const messagePaginationCreatedAtAround = ({ +const messagePaginationCreatedAtAround = ({ parentSet, requestedPageSize, returnedPage, messagePaginationOptions, -}: MessagePaginationUpdatedParams) => { +}: MessagePaginationUpdatedParams) => { const newPagination = { ...parentSet.pagination }; if (!messagePaginationOptions?.created_at_around) return newPagination; let hasPrev; @@ -726,12 +716,12 @@ const messagePaginationCreatedAtAround = ({ +const messagePaginationIdAround = ({ parentSet, requestedPageSize, returnedPage, messagePaginationOptions, -}: MessagePaginationUpdatedParams) => { +}: MessagePaginationUpdatedParams) => { const newPagination = { ...parentSet.pagination }; const { id_around } = messagePaginationOptions || {}; if (!id_around) return newPagination; @@ -779,12 +769,12 @@ const messagePaginationIdAround = ({ +const messagePaginationLinear = ({ parentSet, requestedPageSize, returnedPage, messagePaginationOptions, -}: MessagePaginationUpdatedParams) => { +}: MessagePaginationUpdatedParams) => { const newPagination = { ...parentSet.pagination }; let hasPrev; @@ -836,9 +826,7 @@ const messagePaginationLinear = ( - params: MessagePaginationUpdatedParams, -) => { +export const messageSetPagination = (params: MessagePaginationUpdatedParams) => { if (params.parentSet.messages.length < params.returnedPage.length) { params.logger?.('error', 'Corrupted message set state: parent set size < returned page size'); return params.parentSet.pagination; @@ -859,12 +847,12 @@ export const messageSetPagination = | undefined> = {}; -type GetChannelParams = { - client: StreamChat; - channel?: Channel; +type GetChannelParams = { + client: StreamChat; + channel?: Channel; id?: string; members?: string[]; - options?: ChannelQueryOptions; + options?: ChannelQueryOptions; type?: string; }; /** @@ -877,14 +865,7 @@ type GetChannelParams({ - channel, - client, - id, - members, - options, - type, -}: GetChannelParams) => { +export const getAndWatchChannel = async ({ channel, client, id, members, options, type }: GetChannelParams) => { if (!channel && !type) { throw new Error('Channel or channel type have to be provided to query a channel.'); } @@ -938,9 +919,7 @@ export const generateChannelTempCid = (channelType: string, members: string[]) = * Checks if a channel is pinned or not. Will return true only if channel.state.membership.pinned_at exists. * @param channel */ -export const isChannelPinned = ( - channel: Channel, -) => { +export const isChannelPinned = (channel: Channel) => { if (!channel) return false; const member = channel.state.membership; @@ -952,9 +931,7 @@ export const isChannelPinned = ( - channel: Channel, -) => { +export const isChannelArchived = (channel: Channel) => { if (!channel) return false; const member = channel.state.membership; @@ -967,9 +944,7 @@ export const isChannelArchived = ( - filters: ChannelFilters, -) => { +export const shouldConsiderArchivedChannels = (filters: ChannelFilters) => { if (!filters) return false; return typeof filters.archived === 'boolean'; @@ -983,17 +958,17 @@ export const shouldConsiderArchivedChannels = ({ +export const extractSortValue = ({ atIndex, sort, targetKey, }: { atIndex: number; - targetKey: keyof ChannelSortBase; - sort?: ChannelSort; + targetKey: keyof ChannelSortBase; + sort?: ChannelSort; }) => { if (!sort) return null; - let option: null | ChannelSortBase = null; + let option: null | ChannelSortBase = null; if (Array.isArray(sort)) { option = sort[atIndex] ?? null; @@ -1021,9 +996,7 @@ export const extractSortValue = ( - sort: ChannelSort, -) => { +export const shouldConsiderPinnedChannels = (sort: ChannelSort) => { const value = findPinnedAtSortOrder({ sort }); if (typeof value !== 'number') return false; @@ -1036,11 +1009,7 @@ export const shouldConsiderPinnedChannels = ({ - sort, -}: { - sort: ChannelSort; -}) => +export const findPinnedAtSortOrder = ({ sort }: { sort: ChannelSort }) => extractSortValue({ atIndex: 0, sort, @@ -1053,11 +1022,7 @@ export const findPinnedAtSortOrder = ({ - channels, -}: { - channels: Channel[]; -}) => { +export const findLastPinnedChannelIndex = ({ channels }: { channels: Channel[] }) => { let lastPinnedChannelIndex: number | null = null; for (const channel of channels) { @@ -1082,12 +1047,12 @@ export const findLastPinnedChannelIndex = ({ +export const promoteChannel = ({ channels, channelToMove, channelToMoveIndexWithinChannels, sort, -}: PromoteChannelParams) => { +}: PromoteChannelParams) => { // get index of channel to move up const targetChannelIndex = channelToMoveIndexWithinChannels ?? channels.findIndex((channel) => channel.cid === channelToMove.cid); @@ -1099,7 +1064,7 @@ export const promoteChannel = (channelToMove); + const isTargetChannelPinned = isChannelPinned(channelToMove); if (targetChannelAlreadyAtTheTop || (considerPinnedChannels && isTargetChannelPinned)) { return channels; diff --git a/tsconfig.json b/tsconfig.json index 26dea59f5..862454f59 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,8 +1,6 @@ { "compilerOptions": { "allowSyntheticDefaultImports": true, - "rootDir": "./src", - "baseUrl": "./src", "esModuleInterop": true, "moduleResolution": "node", "lib": ["DOM", "ES6"],