diff --git a/README.md b/README.md index 22ab586cc..c3b89bdc2 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,15 @@ Documentation for this JavaScript client are available at the [Stream Website](h 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 +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. @@ -57,26 +65,26 @@ 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< - ChatAttachment, - ChatChannel, - CustomCommands, - ChatEvent, - UserMessage | AdminMessage, - CustomReaction, - ChatUser1 | ChatUser2 ->('YOUR_API_KEY', 'API_KEY_SECRET'); +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<{}, ChatChannel, {}, {}, UserMessage | AdminMessage, {}, ChatUser1 | ChatUser2>( - 'YOUR_API_KEY', -); +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. diff --git a/src/channel.ts b/src/channel.ts index 3974dd6bd..1b33991d0 100644 --- a/src/channel.ts +++ b/src/channel.ts @@ -23,7 +23,6 @@ import { GetReactionsAPIResponse, GetRepliesAPIResponse, InviteOptions, - LiteralStringForUnion, MarkReadOptions, MemberSort, Message, @@ -43,10 +42,11 @@ import { SendMessageAPIResponse, TruncateChannelAPIResponse, TruncateOptions, - UR, UpdateChannelAPIResponse, UserFilters, UserResponse, + ExtendableGenerics, + DefaultGenerics, PinnedMessagePaginationOptions, PinnedMessagesSort, } from './types'; @@ -55,28 +55,15 @@ import { Role } from './permissions'; /** * Channel - The Channel class manages it's own state. */ -export class Channel< - AttachmentType extends UR = UR, - ChannelType extends UR = UR, - CommandType extends string = LiteralStringForUnion, - EventType extends UR = UR, - MessageType extends UR = UR, - ReactionType extends UR = UR, - UserType extends UR = UR -> { - _client: StreamChat; +export class Channel { + _client: StreamChat; type: string; id: string | undefined; - data: ChannelData | ChannelResponse | undefined; - _data: ChannelData | ChannelResponse; + data: ChannelData | ChannelResponse | undefined; + _data: ChannelData | ChannelResponse; cid: string; - listeners: { - [key: string]: ( - | string - | EventHandler - )[]; - }; - state: ChannelState; + listeners: { [key: string]: (string | EventHandler)[] }; + state: ChannelState; initialized: boolean; lastKeyStroke?: Date; lastTypingEvent: Date | null; @@ -86,18 +73,18 @@ export class Channel< /** * constructor - Create a channel * - * @param {StreamChat} 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, + client: StreamChat, type: string, id: string | undefined, - data: ChannelData, + data: ChannelData, ) { const validTypeRe = /^[\w_-]+$/; const validIDRe = /^[\w!_-]+$/; @@ -119,15 +106,7 @@ export class Channel< this.cid = `${type}:${id}`; this.listeners = {}; // perhaps the state variable should be private - this.state = new ChannelState< - AttachmentType, - ChannelType, - CommandType, - EventType, - MessageType, - ReactionType, - UserType - >(this); + this.state = new ChannelState(this); this.initialized = false; this.lastTypingEvent = null; this.isTyping = false; @@ -137,9 +116,9 @@ export class Channel< /** * getClient - Get the chat client for this channel. If client.disconnect() was called, this function will error * - * @return {StreamChat} + * @return {StreamChat} */ - getClient(): StreamChat { + getClient(): StreamChat { if (this.disconnected === true) { throw Error(`You can't use a channel after client.disconnect() was called`); } @@ -159,22 +138,23 @@ export class Channel< /** * sendMessage - Send a message to this channel * - * @param {Message} 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 * - * @return {Promise>} The Server Response + * @return {Promise>} The Server Response */ async sendMessage( - message: Message, + message: Message, options?: { skip_enrich_url?: boolean; skip_push?: boolean }, ) { - const sendMessageResponse = await this.getClient().post< - SendMessageAPIResponse - >(this._channelURL() + '/message', { - message, - ...options, - }); + const sendMessageResponse = await this.getClient().post>( + this._channelURL() + '/message', + { + message, + ...options, + }, + ); // Reset unreadCount to 0. this.state.unreadCount = 0; @@ -186,7 +166,7 @@ export class Channel< uri: string | NodeJS.ReadableStream | Buffer | File, name?: string, contentType?: string, - user?: UserResponse, + user?: UserResponse, ) { return this.getClient().sendFile(`${this._channelURL()}/file`, uri, name, contentType, user); } @@ -195,7 +175,7 @@ export class Channel< uri: string | NodeJS.ReadableStream | File, name?: string, contentType?: string, - user?: UserResponse, + user?: UserResponse, ) { return this.getClient().sendFile(`${this._channelURL()}/image`, uri, name, contentType, user); } @@ -211,17 +191,13 @@ export class Channel< /** * sendEvent - Send an event on this channel * - * @param {Event} 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< - EventAPIResponse - >(this._channelURL() + '/event', { + return await this.getClient().post>(this._channelURL() + '/event', { event, }); } @@ -229,24 +205,17 @@ export class Channel< /** * search - Query messages * - * @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'} + * @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< - AttachmentType, - ChannelType, - CommandType, - MessageType, - ReactionType, - UserType - >; + message_filter_conditions?: MessageFilters; query?: string; } = {}, ) { @@ -254,10 +223,10 @@ export class Channel< throw Error(`Cannot specify offset with sort or next parameters`); } // Return a list of channels - const payload: SearchPayload = { - 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; @@ -269,9 +238,7 @@ export class Channel< // Make sure we wait for the connect promise if there is a pending one await this.getClient().wsPromise; - return await this.getClient().get< - SearchAPIResponse - >(this.getClient().baseURL + '/search', { + return await this.getClient().get>(this.getClient().baseURL + '/search', { payload, }); } @@ -279,51 +246,54 @@ export class Channel< /** * queryMembers - Query Members * - * @param {UserFilters} filterConditions object MongoDB style filters - * @param {MemberSort} [sort] Sort options, for instance [{created_at: -1}]. + * @param {UserFilters} 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: UserFilters, - sort: MemberSort = [], + filterConditions: UserFilters, + 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, + }, }, - }); + ); } /** * sendReaction - Send a reaction about a message * * @param {string} messageID the message id - * @param {Reaction} 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) { @@ -332,12 +302,13 @@ export class Channel< if (!reaction || Object.keys(reaction).length === 0) { throw Error(`Reaction object is missing`); } - return await this.getClient().post< - ReactionAPIResponse - >(this.getClient().baseURL + `/messages/${messageID}/reaction`, { - reaction, - ...options, - }); + return await this.getClient().post>( + this.getClient().baseURL + `/messages/${messageID}/reaction`, + { + reaction, + ...options, + }, + ); } /** @@ -347,7 +318,7 @@ export class Channel< * @param {string} reactionType the type of reaction that should be removed * @param {string} [user_id] the id of the user (used only for server side request) default null * - * @return {Promise>} The Server Response + * @return {Promise>} The Server Response */ deleteReaction(messageID: string, reactionType: string, user_id?: string) { this._checkInitialized(); @@ -358,27 +329,23 @@ export class Channel< const url = this.getClient().baseURL + `/messages/${messageID}/reaction/${reactionType}`; //provided when server side request if (user_id) { - return this.getClient().delete< - ReactionAPIResponse - >(url, { user_id }); + return this.getClient().delete>(url, { user_id }); } - return this.getClient().delete< - ReactionAPIResponse - >(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> | Partial> = {}, + updateMessage?: Message, options?: ChannelUpdateOptions, ) { // Strip out reserved names that will result in API errors. @@ -408,12 +375,12 @@ export class Channel< /** * updatePartial - partial update channel properties * - * @param {PartialUpdateChannel} partial update request + * @param {PartialUpdateChannel} partial update request * - * @return {Promise>} + * @return {Promise>} */ - async updatePartial(update: PartialUpdateChannel) { - return await this.getClient().patch>( + async updatePartial(update: PartialUpdateChannel) { + return await this.getClient().patch>( this._channelURL(), update, ); @@ -423,12 +390,10 @@ export class Channel< * enableSlowMode - enable slow mode * * @param {number} coolDownInterval the cooldown interval in seconds - * @return {Promise>} The server response + * @return {Promise>} The server response */ async enableSlowMode(coolDownInterval: number) { - const data = await this.getClient().post< - UpdateChannelAPIResponse - >(this._channelURL(), { + const data = await this.getClient().post>(this._channelURL(), { cooldown: coolDownInterval, }); this.data = data.channel; @@ -438,12 +403,10 @@ export class Channel< /** * disableSlowMode - disable slow mode * - * @return {Promise>} The server response + * @return {Promise>} The server response */ async disableSlowMode() { - const data = await this.getClient().post< - UpdateChannelAPIResponse - >(this._channelURL(), { + const data = await this.getClient().post>(this._channelURL(), { cooldown: 0, }); this.data = data.channel; @@ -455,49 +418,45 @@ export class Channel< * * @param {boolean} [options.hard_delete] Defines if the channel is hard deleted or not * - * @return {Promise>} The server response + * @return {Promise>} The server response */ async delete(options: { hard_delete?: boolean } = {}) { - return await this.getClient().delete>( - this._channelURL(), - { ...options }, - ); + 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< - TruncateChannelAPIResponse - >(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 {InviteOptions} [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: InviteOptions = {}) { 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 {InviteOptions} [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: InviteOptions = {}) { return await this._update({ reject_invite: true, ...options }); } @@ -505,13 +464,13 @@ export class Channel< * addMembers - add members to the channel * * @param {{user_id: string, channel_role?: Role}[]} members An array of members to add to the channel - * @param {Message} [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 addMembers( members: string[] | { user_id: string; channel_role?: Role }[], - message?: Message, + message?: Message, options: ChannelUpdateOptions = {}, ) { return await this._update({ add_members: members, message, ...options }); @@ -521,15 +480,11 @@ export class Channel< * addModerators - add moderators to the channel * * @param {string[]} members An array of member identifiers - * @param {Message} [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 }); } @@ -537,13 +492,13 @@ export class Channel< * assignRoles - sets member roles in a channel * * @param {{channel_role: Role, user_id: string}[]} roles List of role assignments - * @param {Message} [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 }); @@ -553,13 +508,13 @@ export class Channel< * inviteMembers - invite members to the channel * * @param {{user_id: string, channel_role?: Role}[]} members An array of members to invite to the channel - * @param {Message} [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 inviteMembers( members: { user_id: string; channel_role?: Role }[] | string[], - message?: Message, + message?: Message, options: ChannelUpdateOptions = {}, ) { return await this._update({ invites: members, message, ...options }); @@ -569,15 +524,11 @@ export class Channel< * removeMembers - remove members from channel * * @param {string[]} members An array of member identifiers - * @param {Message} [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 }); } @@ -585,28 +536,22 @@ export class Channel< * demoteModerators - remove moderator role from channel members * * @param {string[]} members An array of member identifiers - * @param {Message} [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< - UpdateChannelAPIResponse - >(this._channelURL(), payload); + const data = await this.getClient().post>(this._channelURL(), payload); this.data = data.channel; return data; } @@ -614,7 +559,7 @@ export class Channel< /** * mute - mutes the current channel * @param {{ user_id?: string, expiration?: string }} opts expiration in minutes or user_id - * @return {Promise>} The server response + * @return {Promise>} The server response * * example with expiration: * await channel.mute({expiration: moment.duration(2, 'weeks')}); @@ -624,7 +569,7 @@ export class Channel< * */ async mute(opts: { expiration?: number; user_id?: string } = {}) { - return await this.getClient().post>( + return await this.getClient().post>( this.getClient().baseURL + '/moderation/mute/channel', { channel_cid: this.cid, ...opts }, ); @@ -663,14 +608,15 @@ export class Channel< if (!messageID) { throw Error(`Message id is missing`); } - return this.getClient().post< - SendMessageAPIResponse - >(this.getClient().baseURL + `/messages/${messageID}/action`, { - message_id: messageID, - form_data: formData, - id: this.id, - type: this.type, - }); + return this.getClient().post>( + this.getClient().baseURL + `/messages/${messageID}/action`, + { + message_id: messageID, + form_data: formData, + id: this.id, + type: this.type, + }, + ); } /** @@ -693,7 +639,7 @@ export class Channel< await this.sendEvent({ type: 'typing.start', parent_id, - } as Event); + } as Event); } } @@ -711,13 +657,13 @@ export class Channel< await this.sendEvent({ type: 'typing.stop', parent_id, - } as Event); + } as Event); } /** * lastMessage - return the last message, takes into account that last few messages might not be perfectly sorted * - * @return {ReturnType['formatMessage']> | undefined} Description + * @return {ReturnType['formatMessage']> | undefined} Description */ lastMessage() { // get last 5 messages, sort, return the latest @@ -738,19 +684,17 @@ export class Channel< /** * markRead - Send the mark read event for this user, only works if the `read_events` setting is enabled * - * @param {MarkReadOptions} data - * @return {Promise | null>} Description + * @param {MarkReadOptions} data + * @return {Promise | null>} Description */ - async markRead(data: MarkReadOptions = {}) { + async markRead(data: MarkReadOptions = {}) { this._checkInitialized(); if (!this.getConfig()?.read_events) { return Promise.resolve(null); } - return await this.getClient().post< - EventAPIResponse - >(this._channelURL() + '/read', { + return await this.getClient().post>(this._channelURL() + '/read', { ...data, }); } @@ -773,11 +717,11 @@ export class Channel< /** * watch - Loads the initial channel state and watches for changes * - * @param {ChannelQueryOptions} 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, @@ -823,19 +767,20 @@ export class Channel< * getReplies - List the message replies for a parent message * * @param {string} parent_id The message parent id, ie the top of the thread - * @param {PaginationOptions & { user?: UserResponse; user_id?: string }} options Pagination params, ie {limit:10, id_lte: 10} + * @param {PaginationOptions & { 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: PaginationOptions & { user?: UserResponse; user_id?: string }, + options: PaginationOptions & { user?: UserResponse; user_id?: string }, ) { - const data = await this.getClient().get< - GetRepliesAPIResponse - >(this.getClient().baseURL + `/messages/${parent_id}/replies`, { - ...options, - }); + const data = await this.getClient().get>( + this.getClient().baseURL + `/messages/${parent_id}/replies`, + { + ...options, + }, + ); // add any messages to our thread state if (data.messages) { @@ -848,23 +793,24 @@ export class Channel< /** * getPinnedMessages - List list pinned messages of the channel * - * @param {PinnedMessagePaginationOptions & { user?: UserResponse; 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< - GetRepliesAPIResponse - >(this.getClient().baseURL + `/channels/${this.type}/${this.id}/pinned_messages`, { - payload: { - ...options, - sort: normalizeQuerySort(sort), + return await this.getClient().get>( + this.getClient().baseURL + `/channels/${this.type}/${this.id}/pinned_messages`, + { + payload: { + ...options, + sort: normalizeQuerySort(sort), + }, }, - }); + ); } /** @@ -873,10 +819,10 @@ export class Channel< * @param {string} message_id The message id * @param {{ limit?: number; offset?: number }} options The pagination options * - * @return {Promise>} 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/${message_id}/reactions`, { ...options, @@ -889,12 +835,10 @@ export class Channel< * * @param {string[]} messageIds The ids of the messages to retrieve from this channel * - * @return {Promise>} Server response + * @return {Promise>} Server response */ getMessagesById(messageIds: string[]) { - return this.getClient().get< - GetMultipleMessagesAPIResponse - >(this._channelURL() + '/messages', { + return this.getClient().get>(this._channelURL() + '/messages', { ids: messageIds.join(','), }); } @@ -911,11 +855,7 @@ export class Channel< } } - _countMessageAsUnread( - message: - | FormatMessageResponse - | 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; @@ -974,7 +914,7 @@ export class Channel< /** * create - Creates a new channel * - * @return {Promise>} The Server Response + * @return {Promise>} The Server Response */ create = async () => { const options = { @@ -988,11 +928,11 @@ export class Channel< /** * query - Query the API, get messages, members or other channel fields * - * @param {ChannelQueryOptions} options The query options + * @param {ChannelQueryOptions} options The query options * - * @return {Promise>} Returns a query response + * @return {Promise>} Returns a query response */ - async query(options: ChannelQueryOptions) { + async query(options: ChannelQueryOptions) { // Make sure we wait for the connect promise if there is a pending one await this.getClient().wsPromise; @@ -1001,9 +941,7 @@ export class Channel< queryURL += `/${this.id}`; } - const state = await this.getClient().post< - ChannelAPIResponse - >(queryURL + '/query', { + const state = await this.getClient().post>(queryURL + '/query', { data: this._data, state: true, ...options, @@ -1044,10 +982,10 @@ export class Channel< * banUser - Bans a user from a channel * * @param {string} targetUserID - * @param {BanUserOptions} 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, @@ -1104,10 +1042,10 @@ export class Channel< * shadowBan - Shadow bans a user from a channel * * @param {string} targetUserID - * @param {BanUserOptions} 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, @@ -1137,29 +1075,14 @@ export class Channel< * or * channel.on(event => {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( - eventType: EventTypes, - callback: EventHandler, - ): { unsubscribe: () => void }; - on( - callback: EventHandler, - ): { unsubscribe: () => void }; - on( - callbackOrString: - | EventHandler - | EventTypes, - callbackOrNothing?: EventHandler< - AttachmentType, - ChannelType, - CommandType, - EventType, - MessageType, - ReactionType, - UserType - >, + callbackOrString: EventHandler | EventTypes, + callbackOrNothing?: EventHandler, ): { unsubscribe: () => void } { const key = callbackOrNothing ? (callbackOrString as string) : 'all'; const valid = isValidEventType(key); @@ -1193,26 +1116,11 @@ export class Channel< * off - Remove the event handler * */ + off(eventType: EventTypes, callback: EventHandler): void; + off(callback: EventHandler): void; off( - eventType: EventTypes, - callback: EventHandler, - ): void; - off( - callback: EventHandler, - ): void; - off( - callbackOrString: - | EventHandler - | EventTypes, - callbackOrNothing?: EventHandler< - AttachmentType, - ChannelType, - CommandType, - EventType, - MessageType, - ReactionType, - UserType - >, + callbackOrString: EventHandler | EventTypes, + callbackOrNothing?: EventHandler, ): void { const key = callbackOrNothing ? (callbackOrString as string) : 'all'; const valid = isValidEventType(key); @@ -1232,9 +1140,7 @@ export class Channel< } // eslint-disable-next-line sonarjs/cognitive-complexity - _handleChannelEvent( - event: Event, - ) { + _handleChannelEvent(event: Event) { const channel = this; this._client.logger( 'info', @@ -1390,9 +1296,7 @@ export class Channel< } } - _callChannelListeners = ( - event: Event, - ) => { + _callChannelListeners = (event: Event) => { const channel = this; // gather and call the listeners const listeners = []; @@ -1432,9 +1336,7 @@ export class Channel< } // eslint-disable-next-line sonarjs/cognitive-complexity - _initializeState( - state: ChannelAPIResponse, - ) { + _initializeState(state: ChannelAPIResponse) { const { state: clientState, user, userID } = this.getClient(); // add the Users diff --git a/src/channel_state.ts b/src/channel_state.ts index f23111463..bf98c504e 100644 --- a/src/channel_state.ts +++ b/src/channel_state.ts @@ -4,84 +4,34 @@ import { ChannelMembership, FormatMessageResponse, Event, - LiteralStringForUnion, + ExtendableGenerics, + DefaultGenerics, MessageResponse, ReactionResponse, - UR, UserResponse, } from './types'; -type ChannelReadStatus = Record< +type ChannelReadStatus = Record< string, - { last_read: Date; unread_messages: number; user: UserResponse } + { last_read: Date; unread_messages: number; user: UserResponse } >; /** * ChannelState - A container class for the channel state. */ -export class ChannelState< - AttachmentType extends UR = UR, - ChannelType extends UR = UR, - CommandType extends string = LiteralStringForUnion, - EventType extends UR = UR, - MessageType extends UR = UR, - ReactionType extends UR = UR, - UserType extends UR = UR -> { - _channel: Channel; +export class ChannelState { + _channel: Channel; watcher_count: number; - typing: Record< - string, - Event - >; - read: ChannelReadStatus; - messages: Array< - ReturnType< - ChannelState< - AttachmentType, - ChannelType, - CommandType, - EventType, - MessageType, - ReactionType, - UserType - >['formatMessage'] - > - >; - pinnedMessages: Array< - ReturnType< - ChannelState< - AttachmentType, - ChannelType, - CommandType, - EventType, - MessageType, - ReactionType, - UserType - >['formatMessage'] - > - >; - threads: Record< - string, - Array< - ReturnType< - ChannelState< - AttachmentType, - ChannelType, - CommandType, - EventType, - MessageType, - ReactionType, - UserType - >['formatMessage'] - > - > - >; - mutedUsers: Array>; - watchers: Record>; - members: Record>; + typing: Record>; + read: ChannelReadStatus; + messages: Array['formatMessage']>>; + pinnedMessages: Array['formatMessage']>>; + threads: Record['formatMessage']>>>; + mutedUsers: Array>; + watchers: Record>; + members: Record>; unreadCount: number; - membership: ChannelMembership; + membership: ChannelMembership; last_message_at: Date | null; /** * Flag which indicates if channel state contain latest/recent messages or no. @@ -90,9 +40,7 @@ export class ChannelState< * be pushed on to message list. */ isUpToDate: boolean; - constructor( - channel: Channel, - ) { + constructor(channel: Channel) { this._channel = channel; this.watcher_count = 0; this.typing = {}; @@ -119,13 +67,13 @@ export class ChannelState< /** * addMessageSorted - Add a message to the state * - * @param {MessageResponse} 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. * */ addMessageSorted( - newMessage: MessageResponse, + newMessage: MessageResponse, timestampChanged = false, addIfDoesNotExist = true, ) { @@ -136,12 +84,10 @@ export class ChannelState< * formatMessage - Takes the message object. Parses the dates, sets __html * and sets the status to received if missing. Returns a message object * - * @param {MessageResponse} message a message object + * @param {MessageResponse} message a message object * */ - formatMessage( - message: MessageResponse, - ): FormatMessageResponse { + formatMessage(message: MessageResponse): FormatMessageResponse { return { ...message, /** @@ -159,14 +105,14 @@ export class ChannelState< /** * 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. * */ addMessagesSorted( - newMessages: MessageResponse[], + newMessages: MessageResponse[], timestampChanged = false, initializing = false, addIfDoesNotExist = true, @@ -243,12 +189,10 @@ export class ChannelState< /** * addPinnedMessages - adds messages in pinnedMessages property * - * @param {Array>} 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]); } @@ -257,12 +201,10 @@ export class ChannelState< /** * addPinnedMessage - adds message in pinnedMessages * - * @param {MessageResponse} 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 +216,17 @@ export class ChannelState< /** * removePinnedMessage - removes pinned message from pinnedMessages * - * @param {MessageResponse} 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, + reaction: ReactionResponse, + message?: MessageResponse, enforce_unique?: boolean, ) { if (!message) return; @@ -299,8 +239,8 @@ export class ChannelState< } _addOwnReactionToMessage( - ownReactions: ReactionResponse[] | null | undefined, - reaction: ReactionResponse, + ownReactions: ReactionResponse[] | null | undefined, + reaction: ReactionResponse, enforce_unique?: boolean, ) { if (enforce_unique) { @@ -318,8 +258,8 @@ export class ChannelState< } _removeOwnReactionFromMessage( - ownReactions: ReactionResponse[] | null | undefined, - reaction: ReactionResponse, + ownReactions: ReactionResponse[] | null | undefined, + reaction: ReactionResponse, ) { if (ownReactions) { return ownReactions.filter((item) => item.user_id !== reaction.user_id || item.type !== reaction.type); @@ -327,10 +267,7 @@ export class ChannelState< return ownReactions; } - removeReaction( - reaction: ReactionResponse, - message?: MessageResponse, - ) { + removeReaction(reaction: ReactionResponse, message?: MessageResponse) { if (!message) return; const messageWithReaction = message; this._updateMessage(message, (msg) => { @@ -340,28 +277,14 @@ export class ChannelState< return messageWithReaction; } - removeQuotedMessageReferences( - message: MessageResponse, - ) { - const parseMessage = ( - m: ReturnType< - ChannelState< - AttachmentType, - ChannelType, - CommandType, - EventType, - MessageType, - ReactionType, - UserType - >['formatMessage'] - >, - ) => + removeQuotedMessageReferences(message: MessageResponse) { + const parseMessage = (m: ReturnType['formatMessage']>) => (({ ...m, created_at: m.created_at.toString(), pinned_at: m.pinned_at?.toString(), updated_at: m.updated_at?.toString(), - } as unknown) as MessageResponse); + } as unknown) as MessageResponse); const updatedMessages = this.messages .filter((msg) => msg.quoted_message_id === message.id) @@ -384,28 +307,8 @@ export class ChannelState< show_in_channel?: boolean; }, updateFunc: ( - msg: ReturnType< - ChannelState< - AttachmentType, - ChannelType, - CommandType, - EventType, - MessageType, - ReactionType, - UserType - >['formatMessage'] - >, - ) => ReturnType< - ChannelState< - AttachmentType, - ChannelType, - CommandType, - EventType, - MessageType, - ReactionType, - UserType - >['formatMessage'] - >, + msg: ReturnType['formatMessage']>, + ) => ReturnType['formatMessage']>, ) { const { parent_id, show_in_channel, pinned } = message; @@ -448,37 +351,15 @@ export class ChannelState< /** * _addToMessageList - Adds a message to a list of messages, tries to update first, appends if message isn't found * - * @param {Array['formatMessage']>>} messages A list of messages + * @param {Array['formatMessage']>>} 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< - ReturnType< - ChannelState< - AttachmentType, - ChannelType, - CommandType, - EventType, - MessageType, - ReactionType, - UserType - >['formatMessage'] - > - >, - message: ReturnType< - ChannelState< - AttachmentType, - ChannelType, - CommandType, - EventType, - MessageType, - ReactionType, - UserType - >['formatMessage'] - >, + messages: Array['formatMessage']>>, + message: ReturnType['formatMessage']>, timestampChanged = false, sortBy: 'pinned_at' | 'created_at' = 'created_at', addIfDoesNotExist = true, @@ -570,19 +451,7 @@ export class ChannelState< } removeMessageFromArray = ( - msgArray: Array< - ReturnType< - ChannelState< - AttachmentType, - ChannelType, - CommandType, - EventType, - MessageType, - ReactionType, - UserType - >['formatMessage'] - > - >, + msgArray: Array['formatMessage']>>, msg: { id: string; parent_id?: string }, ) => { const result = msgArray.filter((message) => !(!!message.id && !!msg.id && message.id === msg.id)); @@ -593,24 +462,12 @@ export class ChannelState< /** * Updates the message.user property with updated user object, for messages. * - * @param {UserResponse} user + * @param {UserResponse} user */ - updateUserMessages = (user: UserResponse) => { + updateUserMessages = (user: UserResponse) => { const _updateUserMessages = ( - messages: Array< - ReturnType< - ChannelState< - AttachmentType, - ChannelType, - CommandType, - EventType, - MessageType, - ReactionType, - UserType - >['formatMessage'] - > - >, - user: UserResponse, + messages: Array['formatMessage']>>, + user: UserResponse, ) => { for (let i = 0; i < messages.length; i++) { const m = messages[i]; @@ -632,25 +489,13 @@ export class ChannelState< /** * Marks the messages as deleted, from deleted user. * - * @param {UserResponse} user + * @param {UserResponse} user * @param {boolean} hardDelete */ - deleteUserMessages = (user: UserResponse, hardDelete = false) => { + deleteUserMessages = (user: UserResponse, hardDelete = false) => { const _deleteUserMessages = ( - messages: Array< - ReturnType< - ChannelState< - AttachmentType, - ChannelType, - CommandType, - EventType, - MessageType, - ReactionType, - UserType - >['formatMessage'] - > - >, - user: UserResponse, + messages: Array['formatMessage']>>, + user: UserResponse, hardDelete = false, ) => { for (let i = 0; i < messages.length; i++) { @@ -679,17 +524,7 @@ export class ChannelState< type: 'deleted', updated_at: m.updated_at, user: m.user, - } as unknown) as ReturnType< - ChannelState< - AttachmentType, - ChannelType, - CommandType, - EventType, - MessageType, - ReactionType, - UserType - >['formatMessage'] - >; + } as unknown) as ReturnType['formatMessage']>; } else { messages[i] = { ...m, @@ -736,7 +571,7 @@ export class ChannelState< cid: this._channel.cid, type: 'typing.stop', user: { id: userID }, - } as Event); + } as Event); } } } diff --git a/src/client.ts b/src/client.ts index cb9567add..0f85cb1ad 100644 --- a/src/client.ts +++ b/src/client.ts @@ -72,7 +72,6 @@ import { GetRateLimitsResponse, ListChannelResponse, ListCommandsResponse, - LiteralStringForUnion, Logger, MarkChannelsReadOptions, Message, @@ -97,7 +96,6 @@ import { TestSQSDataInput, TokenOrProvider, UnBanUserOptions, - UR, UpdateChannelOptions, UpdateChannelResponse, UpdateCommandOptions, @@ -120,6 +118,8 @@ import { DeleteChannelsResponse, TaskResponse, ReservedMessageFields, + ExtendableGenerics, + DefaultGenerics, ReviewFlagReportResponse, FlagReportsFilters, FlagReportsResponse, @@ -138,20 +138,12 @@ function isString(x: unknown): x is string { return typeof x === 'string' || x instanceof String; } -export class StreamChat< - AttachmentType extends UR = UR, - ChannelType extends UR = UR, - CommandType extends string = LiteralStringForUnion, - EventType extends UR = UR, - MessageType extends UR = UR, - ReactionType extends UR = UR, - UserType extends UR = UR -> { +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; }; anonymous: boolean; axiosInstance: AxiosInstance; @@ -159,12 +151,10 @@ export class StreamChat< browser: boolean; cleaningIntervalRef?: NodeJS.Timeout; clientID?: string; - configs: Configs; + configs: Configs; key: string; listeners: { - [key: string]: Array< - (event: Event) => void - >; + [key: string]: Array<(event: Event) => void>; }; logger: Logger; /** @@ -177,37 +167,21 @@ export class StreamChat< * manually calling queryChannels endpoint. */ recoverStateOnReconnect?: boolean; - mutedChannels: ChannelMute[]; - mutedUsers: Mute[]; + 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< - ChannelType, - CommandType, - UserType, - AttachmentType, - EventType, - MessageType, - ReactionType - > | null; - wsFallback?: WSConnectionFallback< - AttachmentType, - ChannelType, - CommandType, - EventType, - MessageType, - ReactionType, - UserType - >; - wsPromise: ConnectAPIResponse | null; + wsConnection: StableWSConnection | null; + wsFallback?: WSConnectionFallback; + wsPromise: ConnectAPIResponse | null; consecutiveFailures: number; insightMetrics: InsightMetrics; defaultWSTimeoutWithFallback: number; @@ -238,7 +212,7 @@ export class StreamChat< // set the key this.key = key; this.listeners = {}; - this.state = new ClientState(); + this.state = new ClientState(); // a list of channels to hide ws events from this.mutedChannels = []; this.mutedUsers = []; @@ -374,77 +348,29 @@ export class StreamChat< * @example secret is optional and only used in server side mode * StreamChat.getInstance('api_key', "secret", { httpsAgent: customAgent }) */ - public static getInstance< - AttachmentType extends UR = UR, - ChannelType extends UR = UR, - CommandType extends string = LiteralStringForUnion, - EventType extends UR = UR, - MessageType extends UR = UR, - ReactionType extends UR = UR, - UserType extends UR = UR - >( + public static getInstance( key: string, options?: StreamChatOptions, - ): StreamChat; - public static getInstance< - AttachmentType extends UR = UR, - ChannelType extends UR = UR, - CommandType extends string = LiteralStringForUnion, - EventType extends UR = UR, - MessageType extends UR = UR, - ReactionType extends UR = UR, - UserType extends UR = UR - >( + ): StreamChat; + public static getInstance( key: string, secret?: string, options?: StreamChatOptions, - ): StreamChat; - public static getInstance< - AttachmentType extends UR = UR, - ChannelType extends UR = UR, - CommandType extends string = LiteralStringForUnion, - EventType extends UR = UR, - MessageType extends UR = UR, - ReactionType extends UR = UR, - UserType extends UR = UR - >( + ): StreamChat; + public static getInstance( key: string, secretOrOptions?: StreamChatOptions | string, options?: StreamChatOptions, - ): StreamChat { + ): StreamChat { if (!StreamChat._instance) { if (typeof secretOrOptions === 'string') { - StreamChat._instance = new StreamChat< - AttachmentType, - ChannelType, - CommandType, - EventType, - MessageType, - ReactionType, - UserType - >(key, secretOrOptions, options); + StreamChat._instance = new StreamChat(key, secretOrOptions, options); } else { - StreamChat._instance = new StreamChat< - AttachmentType, - ChannelType, - CommandType, - EventType, - MessageType, - ReactionType, - UserType - >(key, secretOrOptions); + StreamChat._instance = new StreamChat(key, secretOrOptions); } } - return StreamChat._instance as StreamChat< - AttachmentType, - ChannelType, - CommandType, - EventType, - MessageType, - ReactionType, - UserType - >; + return StreamChat._instance as StreamChat; } devToken(userID: string) { @@ -467,13 +393,13 @@ export class StreamChat< /** * connectUser - Set the current user and open a WebSocket connection * - * @param {OwnUserResponse | 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, + user: OwnUserResponse | UserResponse, userTokenOrProvider: TokenOrProvider, ) => { if (!user.id) { @@ -530,17 +456,17 @@ export class StreamChat< * * setUser - Set the current user and open a WebSocket connection * - * @param {OwnUserResponse | 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`. @@ -651,9 +577,7 @@ export class StreamChat< * Revokes all tokens on application level issued before given time */ async revokeTokens(before: Date | string | null) { - return await this.updateAppSettings({ - revoke_tokens_issued_before: this._normalizeDate(before), - }); + return await this.updateAppSettings({ revoke_tokens_issued_before: this._normalizeDate(before) }); } /** @@ -673,13 +597,11 @@ export class StreamChat< before = this._normalizeDate(before); } - const users: PartialUserUpdate[] = []; + const users: PartialUserUpdate[] = []; for (const userID of userIDs) { users.push({ id: userID, - set: >>{ - revoke_tokens_issued_before: before, - }, + set: >>{ revoke_tokens_issued_before: before }, }); } @@ -690,7 +612,7 @@ export class StreamChat< * getAppSettings - retrieves application settings */ async getAppSettings() { - return await this.get>(this.baseURL + '/app'); + return await this.get>(this.baseURL + '/app'); } /** @@ -786,10 +708,7 @@ export class StreamChat< this.anonymous = true; this.userID = randomId(); - const anonymousUser = { - id: this.userID, - anon: true, - } as UserResponse; + const anonymousUser = { id: this.userID, anon: true } as UserResponse; this._setToken(anonymousUser, ''); this._setUser(anonymousUser); @@ -805,15 +724,15 @@ export class StreamChat< /** * setGuestUser - Setup a temporary guest user * - * @param {UserResponse} 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 }>( + response = await this.post }>( this.baseURL + '/guest', { user }, ); @@ -824,7 +743,7 @@ export class StreamChat< this.anonymous = false; // eslint-disable-next-line @typescript-eslint/no-unused-vars const { created_at, updated_at, last_active, online, ...guestUser } = response.user; - return await this.connectUser(guestUser as UserResponse, response.access_token); + return await this.connectUser(guestUser as UserResponse, response.access_token); } /** @@ -860,61 +779,31 @@ export class StreamChat< * or * client.on(event => {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( - callback: EventHandler, - ): { unsubscribe: () => void }; - on( - eventType: string, - callback: EventHandler, - ): { unsubscribe: () => void }; - on( - callbackOrString: - | EventHandler - | string, - callbackOrNothing?: EventHandler< - AttachmentType, - ChannelType, - CommandType, - EventType, - MessageType, - ReactionType, - UserType - >, + callbackOrString: EventHandler | string, + callbackOrNothing?: EventHandler, ): { unsubscribe: () => void } { const key = callbackOrNothing ? (callbackOrString as string) : 'all'; const valid = isValidEventType(key); if (!valid) { throw Error(`Invalid event type ${key}`); } - const callback = callbackOrNothing - ? callbackOrNothing - : (callbackOrString as EventHandler< - AttachmentType, - ChannelType, - CommandType, - EventType, - MessageType, - ReactionType, - UserType - >); + const callback = callbackOrNothing ? callbackOrNothing : (callbackOrString as EventHandler); if (!(key in this.listeners)) { this.listeners[key] = []; } - this.logger('info', `Attaching listener for ${key} event`, { - tags: ['event', 'client'], - }); + this.logger('info', `Attaching listener for ${key} event`, { tags: ['event', 'client'] }); this.listeners[key].push(callback); return { unsubscribe: () => { - this.logger('info', `Removing listener for ${key} event`, { - tags: ['event', 'client'], - }); - + this.logger('info', `Removing listener for ${key} event`, { tags: ['event', 'client'] }); this.listeners[key] = this.listeners[key].filter((el) => el !== callback); }, }; @@ -924,50 +813,23 @@ export class StreamChat< * off - Remove the event handler * */ + off(callback: EventHandler): void; + off(eventType: string, callback: EventHandler): void; off( - callback: EventHandler, - ): void; - off( - eventType: string, - callback: EventHandler, - ): void; - off( - callbackOrString: - | EventHandler - | string, - callbackOrNothing?: EventHandler< - AttachmentType, - ChannelType, - CommandType, - EventType, - MessageType, - ReactionType, - UserType - >, + callbackOrString: EventHandler | string, + callbackOrNothing?: EventHandler, ) { const key = callbackOrNothing ? (callbackOrString as string) : 'all'; const valid = isValidEventType(key); if (!valid) { throw Error(`Invalid event type ${key}`); } - const callback = callbackOrNothing - ? callbackOrNothing - : (callbackOrString as EventHandler< - AttachmentType, - ChannelType, - CommandType, - EventType, - MessageType, - ReactionType, - UserType - >); + const callback = callbackOrNothing ? callbackOrNothing : (callbackOrString as EventHandler); if (!(key in this.listeners)) { this.listeners[key] = []; } - this.logger('info', `Removing listener for ${key} event`, { - tags: ['event', 'client'], - }); + this.logger('info', `Removing listener for ${key} event`, { tags: ['event', 'client'] }); this.listeners[key] = this.listeners[key].filter((value) => value !== callback); } @@ -1007,9 +869,7 @@ export class StreamChat< type: string, url: string, data?: unknown, - options: AxiosRequestConfig & { - config?: AxiosRequestConfig & { maxBodyLength?: number }; - } = {}, + options: AxiosRequestConfig & { config?: AxiosRequestConfig & { maxBodyLength?: number } } = {}, ): Promise => { await this.tokenManager.tokenReady(); const requestConfig = this._enrichAxiosOptions(options); @@ -1087,7 +947,7 @@ export class StreamChat< uri: string | NodeJS.ReadableStream | Buffer | File, name?: string, contentType?: string, - user?: UserResponse, + user?: UserResponse, ) { const data = addFileToFormData(uri, name, contentType); if (user != null) data.append('user', JSON.stringify(user)); @@ -1122,9 +982,7 @@ export class StreamChat< return data; } - dispatchEvent = ( - event: Event, - ) => { + dispatchEvent = (event: Event) => { if (!event.received_at) event.received_at = new Date(); // client event handlers @@ -1149,24 +1007,16 @@ export class StreamChat< handleEvent = (messageEvent: WebSocket.MessageEvent) => { // dispatch the event to the channel listeners const jsonString = messageEvent.data as string; - const event = JSON.parse(jsonString) as Event< - AttachmentType, - ChannelType, - CommandType, - EventType, - MessageType, - ReactionType, - UserType - >; + const event = JSON.parse(jsonString) as Event; this.dispatchEvent(event); }; /** * Updates the members and watchers 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]; @@ -1194,9 +1044,9 @@ export class StreamChat< * Updates the messages from the currently active channels that contain this user, * with updated user object. * - * @param {UserResponse} user + * @param {UserResponse} user */ - _updateUserMessageReferences = (user: UserResponse) => { + _updateUserMessageReferences = (user: UserResponse) => { const refMap = this.state.userChannelReferences[user.id] || {}; for (const channelID in refMap) { @@ -1216,10 +1066,10 @@ export class StreamChat< * If hardDelete is true, all the content of message will be stripped down. * Otherwise, only 'message.type' will be set as 'deleted'. * - * @param {UserResponse} 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) { @@ -1241,9 +1091,7 @@ export class StreamChat< * * @param {Event} event */ - _handleUserEvent = ( - event: Event, - ) => { + _handleUserEvent = (event: Event) => { if (!event.user) { return; } @@ -1289,9 +1137,7 @@ export class StreamChat< } }; - _handleClientEvent( - event: Event, - ) { + _handleClientEvent(event: Event) { const client = this; const postListenerCallbacks = []; this.logger('info', `client:_handleClientEvent - Received event of type { ${event.type} }`, { @@ -1374,14 +1220,10 @@ export class StreamChat< }; } - _callClientListeners = ( - event: Event, - ) => { + _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); } @@ -1407,22 +1249,15 @@ export class StreamChat< }); await this.queryChannels( - { cid: { $in: cids } } as ChannelFilters, + { 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); + this.logger('info', 'client:recoverState() - Querying channels finished', { tags: ['connection', 'client'] }); + this.dispatchEvent({ type: 'connection.recovered' } as Event); } else { - this.dispatchEvent({ - type: 'connection.recovered', - } as Event); + this.dispatchEvent({ type: 'connection.recovered' } as Event); } this.wsPromise = Promise.resolve(); @@ -1448,15 +1283,7 @@ export class StreamChat< } // The StableWSConnection handles all the reconnection logic. - this.wsConnection = new StableWSConnection< - ChannelType, - CommandType, - UserType, - AttachmentType, - EventType, - MessageType, - ReactionType - >({ client: this }); + this.wsConnection = new StableWSConnection({ client: this }); try { // if fallback is used before, continue using it instead of waiting for WS to fail @@ -1473,20 +1300,11 @@ export class StreamChat< // make sure browser is online before even trying the longpoll if (this.options.enableWSFallback && isWSFailure(err) && isOnline()) { this.logger('info', 'client:connect() - WS failed, fallback to longpoll', { tags: ['connection', 'client'] }); - // @ts-expect-error this.dispatchEvent({ type: 'transport.changed', mode: 'longpoll' }); this.wsConnection._destroyCurrentWSConnection(); this.wsConnection.disconnect().then(); // close WS so no retry - this.wsFallback = new WSConnectionFallback< - AttachmentType, - ChannelType, - CommandType, - EventType, - MessageType, - ReactionType, - UserType - >({ client: this }); + this.wsFallback = new WSConnectionFallback({ client: this }); return await this.wsFallback.connect(); } @@ -1504,11 +1322,7 @@ export class StreamChat< const opts = { headers: { 'x-client-request-id': client_request_id } }; this.doAxiosRequest('get', this.baseURL + '/hi', null, opts).catch((e) => { if (this.options.enableInsights) { - postInsights('http_hi_failed', { - api_key: this.key, - err: e, - client_request_id, - }); + postInsights('http_hi_failed', { api_key: this.key, err: e, client_request_id }); } }); } @@ -1516,14 +1330,18 @@ export class StreamChat< /** * queryUsers - Query users and watch user presence * - * @param {UserFilters} 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> }>} 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, }; @@ -1536,18 +1354,17 @@ export class StreamChat< } // Return a list of users - const data = await this.get< - APIResponse & { - users: Array>; - } - >(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 +1378,7 @@ export class StreamChat< * @param {BannedUsersSort} sort Sort options [{created_at: 1}]. * @param {BannedUsersPaginationOptions} options Option object, {limit: 10, offset:0} * - * @return {Promise>} Ban Query Response + * @return {Promise>} Ban Query Response */ async queryBannedUsers( filterConditions: BannedUsersFilters = {}, @@ -1569,16 +1386,13 @@ export class StreamChat< options: BannedUsersPaginationOptions = {}, ) { // Return a list of user bans - return await this.get>( - this.baseURL + '/query_banned_users', - { - payload: { - filter_conditions: filterConditions, - sort: normalizeQuerySort(sort), - ...options, - }, + return await this.get>(this.baseURL + '/query_banned_users', { + payload: { + filter_conditions: filterConditions, + sort: normalizeQuerySort(sort), + ...options, }, - ); + }); } /** @@ -1587,45 +1401,35 @@ export class StreamChat< * @param {MessageFlagsFilters} filterConditions MongoDB style filter conditions * @param {MessageFlagsPaginationOptions} options Option object, {limit: 10, offset:0} * - * @return {Promise>} 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', - { - payload: { - filter_conditions: filterConditions, - ...options, - }, - }, - ); + return await this.get>(this.baseURL + '/moderation/flags/message', { + payload: { filter_conditions: filterConditions, ...options }, + }); } /** * queryChannels - Query channels * - * @param {ChannelFilters} 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>}> } search channels response + * @return {Promise<{ channels: Array>}> } search channels response */ async queryChannels( - filterConditions: ChannelFilters, - sort: ChannelSort = [], + filterConditions: ChannelFilters, + sort: ChannelSort = [], options: ChannelOptions = {}, stateOptions: ChannelStateOptions = {}, ) { const { skipInitialization } = stateOptions; - const defaultOptions: ChannelOptions = { - state: true, - watch: true, - presence: false, - }; + const defaultOptions: ChannelOptions = { state: true, watch: true, presence: false }; // Make sure we wait for the connect promise if there is a pending one await this.setUserPromise; @@ -1642,19 +1446,12 @@ export class StreamChat< ...options, }; - const data = await this.post<{ - channels: ChannelAPIResponse[]; - }>(this.baseURL + '/channels', payload); + const data = await this.post<{ channels: ChannelAPIResponse[] }>( + this.baseURL + '/channels', + payload, + ); - const channels: Channel< - AttachmentType, - ChannelType, - CommandType, - EventType, - MessageType, - ReactionType, - UserType - >[] = []; + const channels: Channel[] = []; // update our cache of the configs for (const channelState of data.channels) { @@ -1681,24 +1478,24 @@ export class StreamChat< /** * search - Query messages * - * @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'} + * @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 = {}, + filterConditions: ChannelFilters, + query: string | MessageFilters, + options: SearchOptions = {}, ) { if (options.offset && (options.sort || options.next)) { throw Error(`Cannot specify offset with sort or next parameters`); } - 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; @@ -1711,11 +1508,7 @@ export class StreamChat< // Make sure we wait for the connect promise if there is a pending one await this.setUserPromise; - return await this.get< - SearchAPIResponse - >(this.baseURL + '/search', { - payload, - }); + return await this.get>(this.baseURL + '/search', { payload }); } /** @@ -1755,10 +1548,10 @@ export class StreamChat< * * @param {string} [userID] User ID. Only works on serverside * - * @return {APIResponse & Device[]} 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 } : {}, ); @@ -1802,9 +1595,7 @@ export class StreamChat< }); } - _addChannelConfig( - channelState: ChannelAPIResponse, - ) { + _addChannelConfig(channelState: ChannelAPIResponse) { this.configs[channelState.channel.type] = channelState.channel.config; } @@ -1817,7 +1608,7 @@ export class StreamChat< * await channel.create() to assign an ID to channel * * @param {string} channelType The channel type - * @param {string | ChannelData | 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() @@ -1825,16 +1616,13 @@ export class StreamChat< channel( channelType: string, channelID?: string | null, - custom?: ChannelData, - ): Channel; + custom?: ChannelData, + ): Channel; + channel(channelType: string, custom?: ChannelData): Channel; channel( channelType: string, - custom?: ChannelData, - ): Channel; - channel( - channelType: string, - channelIDOrCustom?: string | ChannelData | null, - custom: ChannelData = {} as ChannelData, + channelIDOrCustom?: string | ChannelData | null, + custom: ChannelData = {} as ChannelData, ) { if (!this.userID && !this._isUsingServerAuth()) { throw Error('Call connectUser or connectAnonymousUser before creating a channel'); @@ -1848,12 +1636,7 @@ export class StreamChat< // support channel("messaging", undefined, {options}) // support channel("messaging", "", {options}) if (channelIDOrCustom == null || channelIDOrCustom === '') { - return new Channel( - this, - channelType, - undefined, - custom, - ); + return new Channel(this, channelType, undefined, custom); } // support channel("messaging", {options}) @@ -1880,7 +1663,7 @@ export class StreamChat< * * @return {channel} The channel object, initialize it using channel.watch() */ - getChannelByMembers = (channelType: string, custom: ChannelData) => { + getChannelByMembers = (channelType: string, custom: ChannelData) => { // Check if the channel already exists. // Only allow 1 channel object per cid const membersStr = [...(custom.members || [])].sort().join(','); @@ -1913,15 +1696,7 @@ export class StreamChat< } } - const channel = new Channel< - AttachmentType, - ChannelType, - CommandType, - EventType, - MessageType, - ReactionType, - UserType - >(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'. @@ -1945,7 +1720,7 @@ export class StreamChat< * * @return {channel} The channel object, initialize it using channel.watch() */ - getChannelById = (channelType: string, channelID: string, custom: ChannelData) => { + getChannelById = (channelType: string, channelID: string, custom: ChannelData) => { if (typeof channelID === 'string' && ~channelID.indexOf(':')) { throw Error(`Invalid channel id ${channelID}, can't contain the : character`); } @@ -1960,15 +1735,7 @@ export class StreamChat< } return channel; } - const channel = new Channel< - AttachmentType, - ChannelType, - CommandType, - EventType, - MessageType, - ReactionType, - UserType - >(this, channelType, channelID, custom); + const channel = new Channel(this, channelType, channelID, custom); this.activeChannels[channel.cid] = channel; return channel; @@ -1977,24 +1744,24 @@ export class StreamChat< /** * partialUpdateUser - Update the given user object * - * @param {PartialUserUpdate} 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 } }>} 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 } }>} + * @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'); @@ -2002,13 +1769,10 @@ export class StreamChat< userMap[userObject.id] = userObject; } - return await this.post< - APIResponse & { - users: { [key: string]: UserResponse }; - } - >(this.baseURL + '/users', { - users: userMap, - }); + return await this.post } }>( + this.baseURL + '/users', + { users: userMap }, + ); } /** @@ -2016,19 +1780,19 @@ export class StreamChat< * * updateUsers - Batch update the list of users * - * @param {UserResponse[]} users list of users - * @return {Promise } }>} + * @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 } }>} + * @return {Promise<{ users: { [key: string]: UserResponse } }>} */ - upsertUser(userObject: UserResponse) { + upsertUser(userObject: UserResponse) { return this.upsertUsers([userObject]); } @@ -2037,32 +1801,29 @@ export class StreamChat< * * updateUser - 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 - * @return {Promise } }>} + * @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 } }>} + * @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'); } } - return await this.patch< - APIResponse & { - users: { [key: string]: UserResponse }; - } - >(this.baseURL + '/users', { - users, - }); + return await this.patch } }>( + this.baseURL + '/users', + { users }, + ); } async deleteUser( @@ -2073,54 +1834,46 @@ export class StreamChat< mark_messages_deleted?: boolean; }, ) { - return await this.delete< - APIResponse & { - user: UserResponse; - } - >(this.baseURL + `/users/${userID}`, params); + return await this.delete }>( + this.baseURL + `/users/${userID}`, + params, + ); } async reactivateUser( userID: string, options?: { created_by_id?: string; name?: string; restore_messages?: boolean }, ) { - return await this.post< - APIResponse & { - user: UserResponse; - } - >(this.baseURL + `/users/${userID}/reactivate`, { - ...options, - }); + return await this.post }>( + this.baseURL + `/users/${userID}/reactivate`, + { ...options }, + ); } async deactivateUser(userID: string, options?: { created_by_id?: string; mark_messages_deleted?: boolean }) { - return await this.post }>( + return await this.post }>( this.baseURL + `/users/${userID}/deactivate`, - { - ...options, - }, + { ...options }, ); } async exportUser(userID: string, options?: Record) { return await this.get< APIResponse & { - messages: MessageResponse[]; - reactions: ReactionResponse[]; - user: UserResponse; + messages: MessageResponse[]; + reactions: ReactionResponse[]; + user: UserResponse; } - >(this.baseURL + `/users/${userID}/export`, { - ...options, - }); + >(this.baseURL + `/users/${userID}/export`, { ...options }); } /** banUser - bans a user from all channels * * @param {string} targetUserID - * @param {BanUserOptions} [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, @@ -2143,10 +1896,10 @@ export class StreamChat< /** shadowBan - shadow bans a user from all channels * * @param {string} targetUserID - * @param {BanUserOptions} [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, @@ -2170,11 +1923,11 @@ export class StreamChat< * * @param {string} targetID * @param {string} [userID] Only used with serverside auth - * @param {MuteUserOptions} [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, @@ -2217,7 +1970,7 @@ export class StreamChat< * @returns {Promise} */ async flagMessage(targetMessageID: string, options: { user_id?: string } = {}) { - return await this.post>(this.baseURL + '/moderation/flag', { + return await this.post>(this.baseURL + '/moderation/flag', { target_message_id: targetMessageID, ...options, }); @@ -2230,7 +1983,7 @@ export class StreamChat< * @returns {Promise} */ async flagUser(targetID: string, options: { user_id?: string } = {}) { - return await this.post>(this.baseURL + '/moderation/flag', { + return await this.post>(this.baseURL + '/moderation/flag', { target_user_id: targetID, ...options, }); @@ -2243,7 +1996,7 @@ export class StreamChat< * @returns {Promise} */ 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, }); @@ -2256,7 +2009,7 @@ export class StreamChat< * @returns {Promise} */ 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, }); @@ -2273,17 +2026,14 @@ export class StreamChat< * @param {FlagReportsFilters} filterConditions MongoDB style filter conditions * @param {FlagReportsPaginationOptions} options Option object, {limit: 10, offset:0} * - * @return {Promise>} 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', - { - filter_conditions: filterConditions, - ...options, - }, - ); + return await this.post>(this.baseURL + '/moderation/reports', { + filter_conditions: filterConditions, + ...options, + }); } /** @@ -2301,7 +2051,7 @@ export class StreamChat< * @returns {Promise>} */ async _reviewFlagReport(id: string, reviewResult: string, options: ReviewFlagReportOptions = {}) { - return await this.patch>(this.baseURL + `/moderation/reports/${id}`, { + return await this.patch>(this.baseURL + `/moderation/reports/${id}`, { review_result: reviewResult, ...options, }); @@ -2330,7 +2080,7 @@ export class StreamChat< * @deprecated use markChannelsRead instead * * markAllRead - marks all channels for this user as read - * @param {MarkAllReadOptions} [data] + * @param {MarkAllReadOptions} [data] * * @return {Promise} */ @@ -2340,47 +2090,45 @@ export class StreamChat< * markChannelsRead - marks channels read - * it accepts a map of cid:messageid pairs, if messageid is empty, the whole channel will be marked as read * - * @param {MarkChannelsReadOptions } [data] + * @param {MarkChannelsReadOptions } [data] * * @return {Promise} */ - async markChannelsRead(data: MarkChannelsReadOptions = {}) { - await this.post(this.baseURL + '/channels/read', { - ...data, - }); + 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/${name}`); + return this.get>(this.baseURL + `/commands/${name}`); } - updateCommand(name: string, data: UpdateCommandOptions) { - return this.put>(this.baseURL + `/commands/${name}`, data); + updateCommand(name: string, data: UpdateCommandOptions) { + return this.put>(this.baseURL + `/commands/${name}`, data); } deleteCommand(name: string) { - return this.delete>(this.baseURL + `/commands/${name}`); + return this.delete>(this.baseURL + `/commands/${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/${channelType}`); + return this.get>(this.baseURL + `/channeltypes/${channelType}`); } - updateChannelType(channelType: string, data: UpdateChannelOptions) { - return this.put>(this.baseURL + `/channeltypes/${channelType}`, data); + updateChannelType(channelType: string, data: UpdateChannelOptions) { + return this.put>(this.baseURL + `/channeltypes/${channelType}`, data); } deleteChannelType(channelType: string) { @@ -2388,7 +2136,7 @@ export class StreamChat< } listChannelTypes() { - return this.get>(this.baseURL + `/channeltypes`); + return this.get>(this.baseURL + `/channeltypes`); } /** @@ -2397,14 +2145,13 @@ export class StreamChat< * @param {string} messageId * @param {string} language * - * @return {APIResponse & MessageResponse} Response that includes the message + * @return {MessageResponse} Response that includes the message */ async translateMessage(messageId: string, language: string) { - return await this.post< - APIResponse & MessageResponse - >(this.baseURL + `/messages/${messageId}/translate`, { - language, - }); + return await this.post>( + this.baseURL + `/messages/${messageId}/translate`, + { language }, + ); } /** @@ -2462,13 +2209,13 @@ export class StreamChat< ); return this.partialUpdateMessage( messageId, - { + ({ set: { pinned: true, pin_expires: this._normalizeExpiration(timeoutOrExpirationDate), pinned_at: this._normalizeExpiration(pinnedAt), }, - }, + } as unknown) as PartialMessageUpdate, pinnedBy, ); } @@ -2485,11 +2232,7 @@ export class StreamChat< ); return this.partialUpdateMessage( messageId, - { - set: { - pinned: false, - }, - }, + ({ set: { pinned: false } } as unknown) as PartialMessageUpdate, userId, ); } @@ -2497,14 +2240,14 @@ export class StreamChat< /** * updateMessage - Update the given message * - * @param {Omit, 'mentioned_users'> & { mentioned_users?: string[] }} message object, id needs to be specified + * @param {Omit, 'mentioned_users'> & { 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 {APIResponse & { message: MessageResponse }} Response that includes the message + * @return {{ message: MessageResponse }} Response that includes the message */ async updateMessage( - message: UpdatedMessage, + message: UpdatedMessage, userId?: string | { id: string }, options?: { skip_enrich_url?: boolean }, ) { @@ -2540,7 +2283,7 @@ export class StreamChat< if (isString(userId)) { clonedMessage.user_id = userId; } else { - clonedMessage.user = { id: userId.id } as UserResponse; + clonedMessage.user = { id: userId.id } as UserResponse; } } @@ -2552,9 +2295,7 @@ export class StreamChat< clonedMessage.mentioned_users = clonedMessage.mentioned_users.map((mu) => ((mu as unknown) as UserResponse).id); } - return await this.post< - UpdateMessageAPIResponse - >(this.baseURL + `/messages/${message.id}`, { + return await this.post>(this.baseURL + `/messages/${message.id}`, { message: clonedMessage, ...options, }); @@ -2565,17 +2306,17 @@ export class StreamChat< * * @param {string} id the message id * - * @param {PartialUpdateMessage} 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 {APIResponse & { 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?: { skip_enrich_url?: boolean }, ) { @@ -2586,9 +2327,7 @@ export class StreamChat< if (userId != null && isString(userId)) { user = { id: userId }; } - return await this.put< - UpdateMessageAPIResponse - >(this.baseURL + `/messages/${id}`, { + return await this.put>(this.baseURL + `/messages/${id}`, { ...partialMessageObject, ...options, user, @@ -2600,19 +2339,16 @@ export class StreamChat< if (hardDelete) { params = { hard: true }; } - return await this.delete< - APIResponse & { - message: MessageResponse; - } - >(this.baseURL + `/messages/${messageID}`, params); + return await this.delete }>( + this.baseURL + `/messages/${messageID}`, + params, + ); } async getMessage(messageID: string) { - return await this.get< - APIResponse & { - message: MessageResponse; - } - >(this.baseURL + `/messages/${messageID}`); + return await this.get }>( + this.baseURL + `/messages/${messageID}`, + ); } getUserAgent() { @@ -2716,9 +2452,7 @@ export class StreamChat< * @returns {Promise} */ createPermission(permissionData: CustomPermissionOptions) { - return this.post(`${this.baseURL}/permissions`, { - ...permissionData, - }); + return this.post(`${this.baseURL}/permissions`, { ...permissionData }); } /** updatePermission - updates an existing custom permission @@ -2728,9 +2462,7 @@ export class StreamChat< * @returns {Promise} */ updatePermission(id: string, permissionData: Omit) { - return this.put(`${this.baseURL}/permissions/${id}`, { - ...permissionData, - }); + return this.put(`${this.baseURL}/permissions/${id}`, { ...permissionData }); } /** deletePermission - deletes a custom permission @@ -2781,11 +2513,7 @@ export class StreamChat< * @param {string} last_sync_at last time the user was online and in sync. RFC3339 ie. "2020-05-06T15:05:01.207Z" */ sync(channel_cids: string[], last_sync_at: string) { - return this.post< - APIResponse & { - events: Event[]; - } - >(`${this.baseURL}/sync`, { + return this.post[] }>(`${this.baseURL}/sync`, { channel_cids, last_sync_at, }); @@ -2826,10 +2554,7 @@ export class StreamChat< } exportChannels(request: Array, options: ExportChannelOptions = {}) { - const payload = { - channels: request, - ...options, - }; + const payload = { channels: request, ...options }; return this.post(`${this.baseURL}/export_channels`, payload); } diff --git a/src/client_state.ts b/src/client_state.ts index b9e63ee99..3c03da882 100644 --- a/src/client_state.ts +++ b/src/client_state.ts @@ -1,11 +1,11 @@ -import { UR, UserResponse } from './types'; +import { UserResponse, ExtendableGenerics, DefaultGenerics } from './types'; /** * ClientState - A container class for the client state. */ -export class ClientState { +export class ClientState { users: { - [key: string]: UserResponse; + [key: string]: UserResponse; }; userChannelReferences: { [key: string]: { [key: string]: boolean } }; constructor() { @@ -16,19 +16,19 @@ export class ClientState { this.userChannelReferences = {}; } - updateUsers(users: UserResponse[]) { + updateUsers(users: UserResponse[]) { for (const user of users) { this.updateUser(user); } } - updateUser(user?: UserResponse) { + updateUser(user?: UserResponse) { if (user != null) { this.users[user.id] = user; } } - updateUserReference(user: UserResponse, channelID: string) { + updateUserReference(user: UserResponse, channelID: string) { if (user == null) { return; } diff --git a/src/connection.ts b/src/connection.ts index 8ad9ddabf..2ff585bb3 100644 --- a/src/connection.ts +++ b/src/connection.ts @@ -9,7 +9,7 @@ import { addConnectionEventListeners, } from './utils'; import { buildWsFatalInsight, buildWsSuccessAfterFailureInsight, postInsights } from './insights'; -import { ConnectAPIResponse, ConnectionOpen, LiteralStringForUnion, UR, LogLevel } from './types'; +import { ConnectAPIResponse, ConnectionOpen, ExtendableGenerics, DefaultGenerics, UR, LogLevel } from './types'; import { StreamChat } from './client'; // Type guards to check WebSocket error type @@ -36,22 +36,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< - // CAUTION: generics are out of usual order here - ChannelType extends UR = UR, - CommandType extends string = LiteralStringForUnion, - UserType extends UR = UR, - AttachmentType extends UR = UR, - EventType extends UR = UR, - MessageType extends UR = UR, - ReactionType extends UR = UR -> { +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; @@ -66,16 +57,12 @@ export class StableWSConnection< reason?: Error & { code?: string | number; isWSFailure?: boolean; StatusCode?: string | number }, ) => 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 */ @@ -501,7 +488,6 @@ export class StableWSConnection< this.isHealthy = healthy; if (this.isHealthy) { - //@ts-expect-error this.client.dispatchEvent({ type: 'connection.changed', online: this.isHealthy }); return; } @@ -509,7 +495,6 @@ export class StableWSConnection< // we're offline, wait few seconds and fire and event if still offline setTimeout(() => { if (this.isHealthy) return; - //@ts-expect-error this.client.dispatchEvent({ type: 'connection.changed', online: this.isHealthy }); }, 5000); }; @@ -575,7 +560,7 @@ export class StableWSConnection< _setupConnectionPromise = () => { 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 d6e12c4a5..ea0565574 100644 --- a/src/connection_fallback.ts +++ b/src/connection_fallback.ts @@ -2,7 +2,7 @@ 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, UnknownType, UR, LiteralStringForUnion, LogLevel } from './types'; +import { ConnectionOpen, Event, UR, ExtendableGenerics, DefaultGenerics, LogLevel } from './types'; export enum ConnectionState { Closed = 'CLOSED', @@ -12,26 +12,14 @@ export enum ConnectionState { Init = 'INIT', } -export class WSConnectionFallback< - AttachmentType extends UR = UR, - ChannelType extends UR = UR, - CommandType extends string = LiteralStringForUnion, - EventType extends UR = UR, - MessageType extends UR = UR, - ReactionType extends UR = UR, - UserType extends UR = UR -> { - 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; @@ -48,12 +36,10 @@ export class WSConnectionFallback< // transition from connecting => connected if (this.state === ConnectionState.Connecting && state === ConnectionState.Connected) { - //@ts-expect-error this.client.dispatchEvent({ type: 'connection.changed', online: true }); } if (state === ConnectionState.Closed || state === ConnectionState.Disconnected) { - //@ts-expect-error this.client.dispatchEvent({ type: 'connection.changed', online: false }); } @@ -77,7 +63,7 @@ export class WSConnectionFallback< }; /** @private */ - _req = async (params: UnknownType, config: AxiosRequestConfig, retry: boolean): Promise => { + _req = async (params: UR, config: AxiosRequestConfig, retry: boolean): Promise => { if (!this.cancelToken && !params.close) { this.cancelToken = axios.CancelToken.source(); } @@ -113,7 +99,7 @@ export class WSConnectionFallback< while (this.state === ConnectionState.Connected) { try { const data = await this._req<{ - events: Event[]; + events: Event[]; }>({}, { timeout: 30000 }, true); // 30s => API responds in 20s if there is no event if (data.events?.length) { @@ -163,7 +149,7 @@ export class WSConnectionFallback< this._setState(ConnectionState.Connecting); this.connectionID = undefined; // connect should be sent with empty connection_id so API creates one try { - const { event } = await this._req<{ event: ConnectionOpen }>( + const { event } = await this._req<{ event: ConnectionOpen }>( { json: this.client._buildWSPayload() }, { timeout: 8000 }, // 8s reconnect, diff --git a/src/token_manager.ts b/src/token_manager.ts index c088fda3c..c62993466 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, UR, UserResponse } from './types'; +import { TokenOrProvider, ExtendableGenerics, DefaultGenerics, 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 { * Token provider should return a token string or a promise which resolves to string token. * * @param {TokenOrProvider} tokenOrProvider - * @param {UserResponse} user + * @param {UserResponse} user */ - setTokenOrProvider = async (tokenOrProvider: TokenOrProvider, user: UserResponse) => { + setTokenOrProvider = async (tokenOrProvider: TokenOrProvider, user: UserResponse) => { this.validateToken(tokenOrProvider, user); this.user = user; @@ -73,7 +73,7 @@ export class TokenManager { }; // Validates the user token. - validateToken = (tokenOrProvider: TokenOrProvider, user: UserResponse) => { + 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 b19cf1fc2..51b970beb 100644 --- a/src/types.ts +++ b/src/types.ts @@ -34,6 +34,26 @@ export type RequireOnlyOne = Pick; export type UnknownType = UR; //alias to avoid breaking change +export type DefaultGenerics = { + attachmentType: UR; + channelType: UR; + commandType: LiteralStringForUnion; + eventType: UR; + messageType: UR; + reactionType: UR; + userType: UR; +}; + +export type ExtendableGenerics = { + attachmentType: UR; + channelType: UR; + commandType: string; + eventType: UR; + messageType: UR; + reactionType: UR; + userType: UR; +}; + export type Unpacked = T extends (infer U)[] ? U // eslint-disable-next-line @typescript-eslint/no-explicit-any : T extends (...args: any[]) => infer U @@ -50,7 +70,7 @@ export type APIResponse = { duration: string; }; -export type AppSettingsAPIResponse = APIResponse & { +export type AppSettingsAPIResponse = APIResponse & { app?: { channel_configs: Record< string, @@ -58,7 +78,7 @@ export type AppSettingsAPIResponse[]; + commands?: CommandVariants[]; connect_events?: boolean; created_at?: string; custom_events?: boolean; @@ -127,79 +147,47 @@ export type ModerationResult = { moderated_by?: string; }; -export type MessageFlagsResponse< - ChannelType extends UR = UR, - CommandType extends string = LiteralStringForUnion, - UserType extends UR = UR, - AttachmentType = UR, - MessageType = UR, - ReactionType = UR -> = 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< - ChannelType extends UR = UR, - CommandType extends string = LiteralStringForUnion, - UserType extends UR = UR, - AttachmentType = UR, - MessageType = UR, - ReactionType = UR -> = { +export type FlagReport = { flags_count: number; id: string; - message: MessageResponse; - user: UserResponse; + message: MessageResponse; + user: UserResponse; created_at?: string; details?: Object; review_result?: string; reviewed_at?: string; - reviewed_by?: UserResponse; + reviewed_by?: UserResponse; updated_at?: string; }; -export type FlagReportsResponse< - ChannelType extends UR = UR, - CommandType extends string = LiteralStringForUnion, - UserType extends UR = UR, - AttachmentType = UR, - MessageType = UR, - ReactionType = UR -> = APIResponse & { - flag_reports: Array>; +export type FlagReportsResponse = APIResponse & { + flag_reports: Array>; }; -export type ReviewFlagReportResponse< - ChannelType extends UR = UR, - CommandType extends string = LiteralStringForUnion, - UserType extends UR = UR, - AttachmentType = UR, - MessageType = UR, - ReactionType = UR -> = APIResponse & { - flag_report: FlagReport; +export type ReviewFlagReportResponse = APIResponse & { + flag_report: FlagReport; }; -export type BannedUsersResponse< - ChannelType extends UR = UR, - CommandType extends string = LiteralStringForUnion, - UserType extends UR = UR -> = 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; @@ -213,10 +201,8 @@ export type BlockListResponse = BlockList & { }; export type ChannelResponse< - ChannelType = UR, - CommandType extends string = LiteralStringForUnion, - UserType = UR -> = ChannelType & { + StreamChatGenerics extends ExtendableGenerics = DefaultGenerics +> = StreamChatGenerics['channelType'] & { cid: string; disabled: boolean; frozen: boolean; @@ -224,17 +210,17 @@ 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; invites?: string[]; last_message_at?: string; member_count?: number; - members?: ChannelMemberResponse[]; + members?: ChannelMemberResponse[]; muted?: boolean; name?: string; own_capabilities?: string[]; @@ -243,23 +229,16 @@ export type ChannelResponse< updated_at?: string; }; -export type ChannelAPIResponse< - AttachmentType = UR, - ChannelType = UR, - CommandType extends string = LiteralStringForUnion, - MessageType = UR, - ReactionType = UR, - UserType = UR -> = APIResponse & { - channel: ChannelResponse; - members: ChannelMemberResponse[]; - messages: MessageResponse[]; - pinned_messages: MessageResponse[]; +export type ChannelAPIResponse = APIResponse & { + channel: ChannelResponse; + members: ChannelMemberResponse[]; + messages: MessageResponse[]; + pinned_messages: MessageResponse[]; hidden?: boolean; - membership?: ChannelMembership | null; - read?: ReadResponse[]; + membership?: ChannelMembership | null; + read?: ReadResponse[]; watcher_count?: number; - watchers?: UserResponse[]; + watchers?: UserResponse[]; }; export type ChannelUpdateOptions = { @@ -267,11 +246,11 @@ export type ChannelUpdateOptions = { skip_push?: boolean; }; -export type ChannelMemberAPIResponse = APIResponse & { - members: ChannelMemberResponse[]; +export type ChannelMemberAPIResponse = APIResponse & { + members: ChannelMemberResponse[]; }; -export type ChannelMemberResponse = { +export type ChannelMemberResponse = { banned?: boolean; channel_role?: Role; created_at?: string; @@ -282,7 +261,7 @@ export type ChannelMemberResponse = { role?: string; shadow_banned?: boolean; updated_at?: string; - user?: UserResponse; + user?: UserResponse; user_id?: string; }; @@ -306,52 +285,40 @@ export type CheckSQSResponse = APIResponse & { error?: string; }; -export type CommandResponse = Partial & { +export type CommandResponse< + StreamChatGenerics extends ExtendableGenerics = DefaultGenerics +> = Partial & { args?: string; description?: string; - name?: CommandVariants; - set?: CommandVariants; + name?: CommandVariants; + set?: CommandVariants; }; export type ConnectAPIResponse< - ChannelType extends UR = UR, - CommandType extends string = LiteralStringForUnion, - UserType extends UR = UR -> = Promise>; + StreamChatGenerics extends ExtendableGenerics = DefaultGenerics +> = Promise>; -export type CreateChannelResponse = APIResponse & - Omit, 'client_id' | 'connection_id'> & { +export type CreateChannelResponse = APIResponse & + Omit, 'client_id' | 'connection_id'> & { 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< - ChannelType = UR, - CommandType extends string = LiteralStringForUnion, - UserType = UR -> = APIResponse & { - channel: ChannelResponse; +export type DeleteChannelAPIResponse = APIResponse & { + channel: ChannelResponse; }; -export type DeleteCommandResponse = APIResponse & { - name?: CommandVariants; +export type DeleteCommandResponse = APIResponse & { + name?: CommandVariants; }; -export type EventAPIResponse< - AttachmentType extends UR = UR, - ChannelType extends UR = UR, - CommandType extends string = LiteralStringForUnion, - EventType extends UR = UR, - MessageType extends UR = UR, - ReactionType extends UR = UR, - UserType extends UR = UR -> = APIResponse & { - event: Event; +export type EventAPIResponse = APIResponse & { + event: Event; }; export type ExportChannelResponse = { @@ -369,13 +336,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 @@ -386,13 +353,13 @@ export type FlagMessageResponse = APIResponse & { }; }; -export type FlagUserResponse = 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; @@ -401,45 +368,41 @@ export type FlagUserResponse = APIResponse & { }; }; -export type FormatMessageResponse< - AttachmentType = UR, - ChannelType = UR, - CommandType extends string = LiteralStringForUnion, - MessageType = UR, - ReactionType = UR, - UserType = UR -> = Omit< - MessageResponse, +export type FormatMessageResponse = Omit< + MessageResponse<{ + attachmentType: StreamChatGenerics['attachmentType']; + channelType: StreamChatGenerics['channelType']; + commandType: StreamChatGenerics['commandType']; + eventType: StreamChatGenerics['eventType']; + messageType: {}; + reactionType: StreamChatGenerics['reactionType']; + userType: StreamChatGenerics['userType']; + }>, 'created_at' | 'pinned_at' | 'updated_at' | 'status' > & - MessageType & { + StreamChatGenerics['messageType'] & { created_at: Date; pinned_at: Date | null; status: string; updated_at: Date; }; -export type GetChannelTypeResponse = APIResponse & - Omit, 'client_id' | 'connection_id' | 'commands'> & { +export type GetChannelTypeResponse = APIResponse & + Omit, 'client_id' | 'connection_id' | 'commands'> & { created_at: string; updated_at: string; - commands?: CommandResponse[]; + commands?: CommandResponse[]; grants?: Record; }; -export type GetCommandResponse = APIResponse & - CreateCommandOptions & +export type GetCommandResponse = APIResponse & + CreateCommandOptions & CreatedAtUpdatedAt; export type GetMultipleMessagesAPIResponse< - AttachmentType = UR, - ChannelType = UR, - CommandType extends string = LiteralStringForUnion, - MessageType = UR, - ReactionType = UR, - UserType = UR + StreamChatGenerics extends ExtendableGenerics = DefaultGenerics > = APIResponse & { - messages: MessageResponse[]; + messages: MessageResponse[]; }; export type GetRateLimitsResponse = APIResponse & { @@ -449,26 +412,19 @@ export type GetRateLimitsResponse = APIResponse & { web?: RateLimitsMap; }; -export type GetReactionsAPIResponse = APIResponse & { - reactions: ReactionResponse[]; +export type GetReactionsAPIResponse = APIResponse & { + reactions: ReactionResponse[]; }; -export type GetRepliesAPIResponse< - AttachmentType = UR, - ChannelType = UR, - CommandType extends string = LiteralStringForUnion, - MessageType = UR, - ReactionType = UR, - UserType = UR -> = APIResponse & { - messages: MessageResponse[]; +export type GetRepliesAPIResponse = APIResponse & { + messages: MessageResponse[]; }; -export type ListChannelResponse = APIResponse & { +export type ListChannelResponse = APIResponse & { channel_types: Record< string, - Omit, 'client_id' | 'connection_id' | 'commands'> & { - commands: CommandResponse[]; + Omit, 'client_id' | 'connection_id' | 'commands'> & { + commands: CommandResponse[]; created_at: string; updated_at: string; grants?: Record; @@ -477,45 +433,31 @@ export type ListChannelResponse = ListChannelResponse; + StreamChatGenerics extends ExtendableGenerics = DefaultGenerics +> = ListChannelResponse; -export type ListCommandsResponse = APIResponse & { - commands: Array & Partial>; +export type ListCommandsResponse = APIResponse & { + commands: Array & Partial>; }; -export type MuteChannelAPIResponse< - ChannelType extends UR = UR, - CommandType extends string = LiteralStringForUnion, - UserType extends UR = UR -> = 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< - AttachmentType = UR, - ChannelType = UR, - CommandType extends string = LiteralStringForUnion, - MessageType = UR, - ReactionType = UR, - UserType = UR -> = MessageResponseBase & { - quoted_message?: MessageResponseBase; + StreamChatGenerics extends ExtendableGenerics = DefaultGenerics +> = MessageResponseBase & { + quoted_message?: MessageResponseBase; }; export type MessageResponseBase< - AttachmentType = UR, - ChannelType = UR, - CommandType extends string = LiteralStringForUnion, - MessageType = UR, - ReactionType = UR, - UserType = UR -> = MessageBase & { + StreamChatGenerics extends ExtendableGenerics = DefaultGenerics +> = MessageBase & { args?: string; - channel?: ChannelResponse; + channel?: ChannelResponse; cid?: string; command?: string; command_info?: { name?: string }; @@ -524,49 +466,41 @@ export type MessageResponseBase< i18n?: RequireAtLeastOne> & { language: TranslationLanguages; }; - latest_reactions?: ReactionResponse[]; - mentioned_users?: UserResponse[]; - own_reactions?: ReactionResponse[] | null; + latest_reactions?: ReactionResponse[]; + mentioned_users?: UserResponse[]; + own_reactions?: ReactionResponse[] | null; pin_expires?: string | null; pinned_at?: string | null; - pinned_by?: UserResponse | null; + pinned_by?: UserResponse | null; reaction_counts?: { [key: string]: number } | null; reaction_scores?: { [key: string]: number } | null; reply_count?: number; shadowed?: boolean; silent?: boolean; status?: string; - thread_participants?: UserResponse[]; + thread_participants?: UserResponse[]; type?: MessageLabel; updated_at?: 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< - ChannelType extends UR = UR, - CommandType extends string = LiteralStringForUnion, - UserType extends UR = UR -> = APIResponse & { - mute?: MuteResponse; - mutes?: Array>; - own_user?: OwnUserResponse; -}; - -export type OwnUserBase< - ChannelType extends UR = UR, - CommandType extends string = LiteralStringForUnion, - UserType extends UR = UR -> = { - channel_mutes: ChannelMute[]; - devices: Device[]; - mutes: Mute[]; +export type MuteUserResponse = APIResponse & { + mute?: MuteResponse; + mutes?: Array>; + own_user?: OwnUserResponse; +}; + +export type OwnUserBase = { + channel_mutes: ChannelMute[]; + devices: Device[]; + mutes: Mute[]; total_unread_count: number; unread_channels: number; unread_count: number; @@ -575,18 +509,14 @@ export type OwnUserBase< }; export type OwnUserResponse< - ChannelType extends UR = UR, - CommandType extends string = LiteralStringForUnion, - UserType extends UR = UR -> = UserResponse & OwnUserBase; + StreamChatGenerics extends ExtendableGenerics = DefaultGenerics +> = UserResponse & OwnUserBase; export type PartialUpdateChannelAPIResponse< - ChannelType = UR, - CommandType extends string = LiteralStringForUnion, - UserType = UR + StreamChatGenerics extends ExtendableGenerics = DefaultGenerics > = APIResponse & { - channel: ChannelResponse; - members: ChannelMemberResponse[]; + channel: ChannelResponse; + members: ChannelMemberResponse[]; }; export type PermissionAPIResponse = APIResponse & { @@ -597,39 +527,27 @@ export type PermissionsAPIResponse = APIResponse & { permissions?: PermissionAPIObject[]; }; -export type ReactionAPIResponse< - AttachmentType = UR, - ChannelType = UR, - CommandType extends string = LiteralStringForUnion, - MessageType = UR, - ReactionType = UR, - UserType = UR -> = APIResponse & { - message: MessageResponse; - reaction: ReactionResponse; +export type ReactionAPIResponse = APIResponse & { + message: MessageResponse; + reaction: ReactionResponse; }; -export type ReactionResponse = Reaction & { +export type ReactionResponse< + StreamChatGenerics extends ExtendableGenerics = DefaultGenerics +> = Reaction & { created_at: string; updated_at: string; }; -export type ReadResponse = { +export type ReadResponse = { last_read: string; - user: UserResponse; + user: UserResponse; unread_messages?: number; }; -export type SearchAPIResponse< - AttachmentType = UR, - ChannelType = UR, - CommandType extends string = LiteralStringForUnion, - MessageType = UR, - ReactionType = UR, - UserType = UR -> = APIResponse & { +export type SearchAPIResponse = APIResponse & { results: { - message: MessageResponse; + message: MessageResponse; }[]; next?: string; previous?: string; @@ -644,75 +562,49 @@ export type SearchWarning = { }; export type SendFileAPIResponse = APIResponse & { file: string }; -export type SendMessageAPIResponse< - AttachmentType = UR, - ChannelType = UR, - CommandType extends string = LiteralStringForUnion, - MessageType = UR, - ReactionType = UR, - UserType = UR -> = APIResponse & { - message: MessageResponse; +export type SendMessageAPIResponse = APIResponse & { + message: MessageResponse; }; export type TruncateChannelAPIResponse< - ChannelType = UR, - CommandType extends string = LiteralStringForUnion, - UserType = UR, - AttachmentType = UR, - MessageType = UR, - ReactionType = UR + StreamChatGenerics extends ExtendableGenerics = DefaultGenerics > = APIResponse & { - channel: ChannelResponse; - message?: MessageResponse; + channel: ChannelResponse; + message?: MessageResponse; }; -export type UpdateChannelAPIResponse< - AttachmentType = UR, - ChannelType = UR, - CommandType extends string = LiteralStringForUnion, - MessageType = UR, - ReactionType = UR, - UserType = UR -> = 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, 'client_id' | 'connection_id'> & { 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< - AttachmentType = UR, - ChannelType = UR, - CommandType extends string = LiteralStringForUnion, - MessageType = UR, - ReactionType = UR, - UserType = UR -> = 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; created_at?: string; deactivated_at?: string; @@ -752,8 +644,8 @@ export type ReviewFlagReportOptions = { export type BannedUsersPaginationOptions = Omit; -export type BanUserOptions = UnBanUserOptions & { - banned_by?: UserResponse; +export type BanUserOptions = UnBanUserOptions & { + banned_by?: UserResponse; banned_by_id?: string; ip_ban?: boolean; reason?: string; @@ -771,10 +663,10 @@ export type ChannelOptions = { watch?: boolean; }; -export type ChannelQueryOptions = { +export type ChannelQueryOptions = { client_id?: string; connection_id?: string; - data?: ChannelResponse; + data?: ChannelResponse; members?: PaginationOptions; messages?: MessagePaginationOptions; presence?: boolean; @@ -787,14 +679,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; @@ -815,11 +707,11 @@ export type CreateChannelOptions = { +export type CreateCommandOptions = { description: string; - name: CommandVariants; + name: CommandVariants; args?: string; - set?: CommandVariants; + set?: CommandVariants; }; export type CustomPermissionOptions = { @@ -833,49 +725,44 @@ export type CustomPermissionOptions = { }; // TODO: rename to UpdateChannelOptions in the next major update and use it in channel._update and/or channel.update -export type InviteOptions< - AttachmentType = UR, - ChannelType = UR, - CommandType extends string = LiteralStringForUnion, - MessageType = UR, - ReactionType = UR, - UserType = UR -> = { +export type InviteOptions = { accept_invite?: boolean; add_members?: string[]; add_moderators?: string[]; client_id?: string; connection_id?: string; - data?: Omit, 'id' | 'cid'>; + data?: Omit, 'id' | 'cid'>; 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 = MarkChannelsReadOptions; +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; message_id?: string; - user?: UserResponse; + user?: UserResponse; user_id?: string; }; -export type MuteUserOptions = { +export type MuteUserOptions = { client_id?: string; connection_id?: string; id?: string; @@ -883,7 +770,7 @@ export type MuteUserOptions = { target_user_id?: string; timeout?: number; type?: string; - user?: UserResponse; + user?: UserResponse; user_id?: string; }; @@ -929,11 +816,11 @@ export type QueryMembersOptions = { user_id_lte?: string; }; -export type SearchOptions = { +export type SearchOptions = { limit?: number; next?: string; offset?: number; - sort?: SearchMessageSort; + sort?: SearchMessageSort; }; export type StreamChatOptions = AxiosRequestConfig & { @@ -975,18 +862,18 @@ export type UnBanUserOptions = { }; // TODO: rename to UpdateChannelTypeOptions in the next major update -export type UpdateChannelOptions = Omit< - CreateChannelOptions, +export type UpdateChannelOptions = Omit< + CreateChannelOptions, 'name' > & { created_at?: string; updated_at?: string; }; -export type UpdateCommandOptions = { +export type UpdateCommandOptions = { description: string; args?: string; - set?: CommandVariants; + set?: CommandVariants; }; export type UserOptions = { @@ -1004,17 +891,9 @@ export type ConnectionChangeEvent = { online?: boolean; }; -export type Event< - AttachmentType extends UR = UR, - ChannelType extends UR = UR, - CommandType extends string = LiteralStringForUnion, - EventType extends UR = UR, - MessageType extends UR = UR, - ReactionType extends UR = UR, - UserType extends UR = UR -> = EventType & { +export type Event = StreamChatGenerics['eventType'] & { type: EventTypes; - channel?: ChannelResponse; + channel?: ChannelResponse; channel_id?: string; channel_type?: string; cid?: string; @@ -1023,35 +902,31 @@ export type Event< created_at?: string; hard_delete?: boolean; mark_messages_deleted?: boolean; - me?: OwnUserResponse; - member?: ChannelMemberResponse; - message?: MessageResponse; + me?: OwnUserResponse; + member?: ChannelMemberResponse; + message?: MessageResponse; online?: boolean; parent_id?: string; - reaction?: ReactionResponse; + reaction?: ReactionResponse; received_at?: string | Date; team?: string; total_unread_count?: number; unread_channels?: number; unread_count?: number; - user?: UserResponse; + user?: UserResponse; user_id?: string; watcher_count?: number; }; -export type UserCustomEvent = EventType & { +export type UserCustomEvent< + StreamChatGenerics extends ExtendableGenerics = DefaultGenerics +> = StreamChatGenerics['eventType'] & { type: string; }; -export type EventHandler< - AttachmentType extends UR = UR, - ChannelType extends UR = UR, - CommandType extends string = LiteralStringForUnion, - EventType extends UR = UR, - MessageType extends UR = UR, - ReactionType extends UR = UR, - UserType extends UR = UR -> = (event: Event) => void; +export type EventHandler = ( + event: Event, +) => void; export type EventTypes = 'all' | keyof typeof EVENT_MAP; @@ -1163,12 +1038,8 @@ export type BannedUsersFilters = QueryFilters< } >; -export type ChannelFilters< - ChannelType = UR, - CommandType extends string = LiteralStringForUnion, - UserType = UR -> = QueryFilters< - ContainsOperator & { +export type ChannelFilters = QueryFilters< + ContainsOperator & { members?: | RequireOnlyOne, '$in' | '$nin'>> | RequireOnlyOne, '$eq'>> @@ -1177,14 +1048,47 @@ export type ChannelFilters< name?: | RequireOnlyOne< { - $autocomplete?: ChannelResponse['name']; - } & QueryFilter['name']> + $autocomplete?: ChannelResponse['name']; + } & QueryFilter['name']> > - | PrimitiveFilter['name']>; + | PrimitiveFilter['name']>; } & { - [Key in keyof Omit, 'name' | 'members'>]: - | RequireOnlyOne[Key]>> - | PrimitiveFilter[Key]>; + [Key in keyof Omit< + ChannelResponse<{ + attachmentType: StreamChatGenerics['attachmentType']; + channelType: {}; + commandType: StreamChatGenerics['commandType']; + eventType: StreamChatGenerics['eventType']; + messageType: StreamChatGenerics['messageType']; + reactionType: StreamChatGenerics['reactionType']; + userType: StreamChatGenerics['userType']; + }>, + 'name' | 'members' + >]: + | RequireOnlyOne< + QueryFilter< + ChannelResponse<{ + attachmentType: StreamChatGenerics['attachmentType']; + channelType: {}; + commandType: StreamChatGenerics['commandType']; + eventType: StreamChatGenerics['eventType']; + messageType: StreamChatGenerics['messageType']; + reactionType: StreamChatGenerics['reactionType']; + userType: StreamChatGenerics['userType']; + }>[Key] + > + > + | PrimitiveFilter< + ChannelResponse<{ + attachmentType: StreamChatGenerics['attachmentType']; + channelType: {}; + commandType: StreamChatGenerics['commandType']; + eventType: StreamChatGenerics['eventType']; + messageType: StreamChatGenerics['messageType']; + reactionType: StreamChatGenerics['reactionType']; + userType: StreamChatGenerics['userType']; + }>[Key] + >; } >; @@ -1202,43 +1106,53 @@ export type ContainsOperator = { : RequireOnlyOne> | PrimitiveFilter; }; -export type MessageFilters< - AttachmentType = UR, - ChannelType = UR, - CommandType extends string = LiteralStringForUnion, - MessageType = UR, - ReactionType = UR, - UserType = UR -> = QueryFilters< - ContainsOperator & { +export type MessageFilters = QueryFilters< + ContainsOperator & { text?: | RequireOnlyOne< { - $autocomplete?: MessageResponse< - AttachmentType, - ChannelType, - CommandType, - MessageType, - ReactionType, - UserType - >['text']; - $q?: MessageResponse['text']; - } & QueryFilter< - MessageResponse['text'] - > + $autocomplete?: MessageResponse['text']; + $q?: MessageResponse['text']; + } & QueryFilter['text']> > - | PrimitiveFilter< - MessageResponse['text'] - >; + | PrimitiveFilter['text']>; } & { [Key in keyof Omit< - MessageResponse, + MessageResponse<{ + attachmentType: StreamChatGenerics['attachmentType']; + channelType: StreamChatGenerics['channelType']; + commandType: StreamChatGenerics['commandType']; + eventType: StreamChatGenerics['eventType']; + messageType: {}; + reactionType: StreamChatGenerics['reactionType']; + userType: StreamChatGenerics['userType']; + }>, 'text' >]?: | RequireOnlyOne< - QueryFilter[Key]> + QueryFilter< + MessageResponse<{ + attachmentType: StreamChatGenerics['attachmentType']; + channelType: StreamChatGenerics['channelType']; + commandType: StreamChatGenerics['commandType']; + eventType: StreamChatGenerics['eventType']; + messageType: {}; + reactionType: StreamChatGenerics['reactionType']; + userType: StreamChatGenerics['userType']; + }>[Key] + > > - | PrimitiveFilter[Key]>; + | PrimitiveFilter< + MessageResponse<{ + attachmentType: StreamChatGenerics['attachmentType']; + channelType: StreamChatGenerics['channelType']; + commandType: StreamChatGenerics['commandType']; + eventType: StreamChatGenerics['eventType']; + messageType: {}; + reactionType: StreamChatGenerics['reactionType']; + userType: StreamChatGenerics['userType']; + }>[Key] + >; } >; @@ -1275,29 +1189,72 @@ export type QueryLogicalOperators = { $or?: ArrayTwoOrMore>; }; -export type UserFilters = QueryFilters< - ContainsOperator & { +export type UserFilters = QueryFilters< + ContainsOperator & { id?: - | RequireOnlyOne<{ $autocomplete?: UserResponse['id'] } & QueryFilter['id']>> - | PrimitiveFilter['id']>; + | RequireOnlyOne< + { $autocomplete?: UserResponse['id'] } & QueryFilter< + UserResponse['id'] + > + > + | PrimitiveFilter['id']>; name?: - | RequireOnlyOne<{ $autocomplete?: UserResponse['name'] } & QueryFilter['name']>> - | PrimitiveFilter['name']>; + | RequireOnlyOne< + { $autocomplete?: UserResponse['name'] } & QueryFilter< + UserResponse['name'] + > + > + | PrimitiveFilter['name']>; teams?: | RequireOnlyOne<{ $contains?: PrimitiveFilter; - $eq?: PrimitiveFilter['teams']>; + $eq?: PrimitiveFilter['teams']>; }> - | PrimitiveFilter['teams']>; + | PrimitiveFilter['teams']>; username?: | RequireOnlyOne< - { $autocomplete?: UserResponse['username'] } & QueryFilter['username']> + { $autocomplete?: UserResponse['username'] } & QueryFilter< + UserResponse['username'] + > > - | PrimitiveFilter['username']>; + | PrimitiveFilter['username']>; } & { - [Key in keyof Omit, 'id' | 'name' | 'teams' | 'username'>]?: - | RequireOnlyOne[Key]>> - | PrimitiveFilter[Key]>; + [Key in keyof Omit< + UserResponse<{ + attachmentType: StreamChatGenerics['attachmentType']; + channelType: StreamChatGenerics['channelType']; + commandType: StreamChatGenerics['commandType']; + eventType: StreamChatGenerics['eventType']; + messageType: StreamChatGenerics['messageType']; + reactionType: StreamChatGenerics['reactionType']; + userType: {}; + }>, + 'id' | 'name' | 'teams' | 'username' + >]?: + | RequireOnlyOne< + QueryFilter< + UserResponse<{ + attachmentType: StreamChatGenerics['attachmentType']; + channelType: StreamChatGenerics['channelType']; + commandType: StreamChatGenerics['commandType']; + eventType: StreamChatGenerics['eventType']; + messageType: StreamChatGenerics['messageType']; + reactionType: StreamChatGenerics['reactionType']; + userType: {}; + }>[Key] + > + > + | PrimitiveFilter< + UserResponse<{ + attachmentType: StreamChatGenerics['attachmentType']; + channelType: StreamChatGenerics['channelType']; + commandType: StreamChatGenerics['commandType']; + eventType: StreamChatGenerics['eventType']; + messageType: StreamChatGenerics['messageType']; + reactionType: StreamChatGenerics['reactionType']; + userType: {}; + }>[Key] + >; } >; @@ -1309,9 +1266,13 @@ export type BannedUsersSort = BannedUsersSortBase | Array; export type BannedUsersSortBase = { created_at?: AscDesc }; -export type ChannelSort = ChannelSortBase | Array>; +export type ChannelSort = + | ChannelSortBase + | Array>; -export type ChannelSortBase = Sort & { +export type ChannelSortBase< + StreamChatGenerics extends ExtendableGenerics = DefaultGenerics +> = Sort & { created_at?: AscDesc; has_unread?: AscDesc; last_message_at?: AscDesc; @@ -1328,13 +1289,17 @@ 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' | 'name'>> - | Array, 'id' | 'created_at' | 'name'>>>; +export type MemberSort = + | Sort, 'id' | 'created_at' | 'name'>> + | Array, 'id' | 'created_at' | 'name'>>>; -export type SearchMessageSortBase = Sort & { +export type SearchMessageSortBase = Sort< + StreamChatGenerics['messageType'] +> & { attachments?: AscDesc; 'attachments.type'?: AscDesc; created_at?: AscDesc; @@ -1350,15 +1315,15 @@ export type SearchMessageSortBase = Sort & { 'user.id'?: AscDesc; }; -export type SearchMessageSort = - | SearchMessageSortBase - | Array>; +export type SearchMessageSort = + | SearchMessageSortBase + | Array>; -export type QuerySort = +export type QuerySort = | BannedUsersSort - | ChannelSort - | SearchMessageSort - | UserSort; + | ChannelSort + | SearchMessageSort + | UserSort; /** * Base Types @@ -1434,7 +1399,9 @@ export type AppSettings = { }; }; -export type Attachment = T & { +export type Attachment< + StreamChatGenerics extends ExtendableGenerics = DefaultGenerics +> = StreamChatGenerics['attachmentType'] & { actions?: Action[]; asset_url?: string; author_icon?: string; @@ -1477,9 +1444,9 @@ export type BlockList = { words: string[]; }; -export type ChannelConfig = ChannelConfigFields & +export type ChannelConfig = ChannelConfigFields & CreatedAtUpdatedAt & { - commands?: CommandVariants[]; + commands?: CommandVariants[]; }; export type ChannelConfigAutomod = '' | 'AI' | 'disabled' | 'simple'; @@ -1513,17 +1480,21 @@ export type ChannelConfigFields = { url_enrichment?: boolean; }; -export type ChannelConfigWithInfo = ChannelConfigFields & +export type ChannelConfigWithInfo< + StreamChatGenerics extends ExtendableGenerics = DefaultGenerics +> = ChannelConfigFields & CreatedAtUpdatedAt & { - commands?: CommandResponse[]; + commands?: CommandResponse[]; }; -export type ChannelData = ChannelType & { +export type ChannelData< + StreamChatGenerics extends ExtendableGenerics = DefaultGenerics +> = StreamChatGenerics['channelType'] & { members?: string[]; name?: string; }; -export type ChannelMembership = { +export type ChannelMembership = { banned?: boolean; channel_role?: Role; created_at?: string; @@ -1531,16 +1502,12 @@ export type ChannelMembership = { role?: string; shadow_banned?: boolean; updated_at?: string; - user?: UserResponse; + user?: UserResponse; }; -export type ChannelMute< - ChannelType extends UR = UR, - CommandType extends string = LiteralStringForUnion, - UserType extends UR = UR -> = { - user: UserResponse; - channel?: ChannelResponse; +export type ChannelMute = { + user: UserResponse; + channel?: ChannelResponse; created_at?: string; expires?: string; updated_at?: string; @@ -1554,20 +1521,20 @@ 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; }; export type PushProvider = 'apn' | 'firebase' | 'huawei' | 'xiaomi'; -export type CommandVariants = +export type CommandVariants = | 'all' | 'ban' | 'fun_set' @@ -1576,21 +1543,17 @@ export type CommandVariants | 'mute' | 'unban' | 'unmute' - | CommandType; + | StreamChatGenerics['commandType']; -export type Configs = { - [channel_type: string]: ChannelConfigWithInfo | undefined; +export type Configs = { + [channel_type: string]: ChannelConfigWithInfo | undefined; }; -export type ConnectionOpen< - ChannelType extends UR = UR, - CommandType extends string = LiteralStringForUnion, - UserType extends UR = UR -> = { +export type ConnectionOpen = { connection_id: string; cid?: string; created_at?: string; - me?: OwnUserResponse; + me?: OwnUserResponse; type?: string; }; @@ -1599,9 +1562,9 @@ export type CreatedAtUpdatedAt = { updated_at: string; }; -export type Device = DeviceFields & { +export type Device = DeviceFields & { provider?: string; - user?: UserResponse; + user?: UserResponse; user_id?: string; }; @@ -1755,15 +1718,17 @@ 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< + MessageBase > & { mentioned_users?: string[]; }; -export type MessageBase = MessageType & { +export type MessageBase< + StreamChatGenerics extends ExtendableGenerics = DefaultGenerics +> = StreamChatGenerics['messageType'] & { id: string; - attachments?: Attachment[]; + attachments?: Attachment[]; html?: string; mml?: string; parent_id?: string; @@ -1773,38 +1738,38 @@ export type MessageBase = quoted_message_id?: string; show_in_channel?: boolean; text?: string; - user?: UserResponse | null; + user?: UserResponse | null; user_id?: string; }; export type MessageLabel = 'deleted' | 'ephemeral' | 'error' | 'regular' | 'reply' | 'system'; -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 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 PermissionAPIObject = { @@ -1848,11 +1813,13 @@ export type RateLimitsInfo = { export type RateLimitsMap = Record; -export type Reaction = ReactionType & { +export type Reaction< + StreamChatGenerics extends ExtendableGenerics = DefaultGenerics +> = StreamChatGenerics['reactionType'] & { type: string; message_id?: string; score?: number; - user?: UserResponse | null; + user?: UserResponse | null; user_id?: string; }; @@ -1876,29 +1843,18 @@ export type Resource = | 'UpdateUser' | 'UploadAttachment'; -export type SearchPayload< - AttachmentType = UR, - ChannelType = UR, - CommandType extends string = LiteralStringForUnion, - MessageType = UR, - ReactionType = UR, - UserType = UR -> = Omit, 'sort'> & { +export type SearchPayload = Omit< + SearchOptions, + 'sort' +> & { client_id?: string; connection_id?: string; - filter_conditions?: ChannelFilters; - message_filter_conditions?: MessageFilters< - AttachmentType, - ChannelType, - CommandType, - MessageType, - ReactionType, - UserType - >; + filter_conditions?: ChannelFilters; + message_filter_conditions?: MessageFilters; query?: string; sort?: Array<{ direction: AscDesc; - field: keyof SearchMessageSortBase; + field: keyof SearchMessageSortBase; }>; }; @@ -1994,19 +1950,12 @@ export type ReservedMessageFields = | 'user' | '__html'; -export type UpdatedMessage< - AttachmentType = UR, - ChannelType = UR, - CommandType extends string = LiteralStringForUnion, - MessageType = UR, - ReactionType = UR, - UserType = UR -> = Omit< - MessageResponse, +export type UpdatedMessage = Omit< + MessageResponse, 'mentioned_users' > & { mentioned_users?: string[] }; -export type User = UserType & { +export type User = StreamChatGenerics['userType'] & { id: string; anon?: boolean; name?: string; @@ -2101,9 +2050,9 @@ export type TaskStatus = { result?: UR; }; -export type TruncateOptions = { +export type TruncateOptions = { hard_delete?: boolean; - message?: Message; + message?: Message; skip_push?: boolean; truncated_at?: Date; }; diff --git a/src/utils.ts b/src/utils.ts index dec738440..632ba4a3e 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,5 +1,5 @@ import FormData from 'form-data'; -import { AscDesc, LiteralStringForUnion, OwnUserBase, OwnUserResponse, UR, UserResponse } from './types'; +import { AscDesc, ExtendableGenerics, DefaultGenerics, OwnUserBase, OwnUserResponse, UserResponse } from './types'; /** * logChatPromiseExecution - utility function for logging the execution of a promise.. @@ -54,18 +54,14 @@ function isFileWebAPI(uri: unknown): uri is File { return typeof window !== 'undefined' && 'File' in window && uri instanceof File; } -function isBlobWebAPI(uri: unknown): uri is Blob { - return typeof window !== 'undefined' && 'Blob' in window && uri instanceof Blob; +export function isOwnUser( + user?: OwnUserResponse | UserResponse, +): user is OwnUserResponse { + return (user as OwnUserResponse)?.total_unread_count !== undefined; } -export function isOwnUser< - ChannelType extends UR = UR, - CommandType extends string = LiteralStringForUnion, - UserType extends UR = UR ->( - user?: OwnUserResponse | UserResponse, -): user is OwnUserResponse { - return (user as OwnUserResponse)?.total_unread_count !== undefined; +function isBlobWebAPI(uri: unknown): uri is Blob { + return typeof window !== 'undefined' && 'Blob' in window && uri instanceof Blob; } export function isOwnUserBaseProperty(property: string) { diff --git a/test/typescript/index.js b/test/typescript/index.js index 49670396b..7c656fb67 100644 --- a/test/typescript/index.js +++ b/test/typescript/index.js @@ -11,92 +11,95 @@ const executables = [ f: rg.acceptInvite, imports: ['Channel', 'Unpacked'], type: - "Unpacked['acceptInvite']>>", + "Unpacked['acceptInvite']>>", }, { f: rg.addDevice, - imports: ['StreamChat', 'Unpacked'], - type: "Unpacked['addDevice']>>", + imports: ['StreamChat', 'DefaultGenerics', 'Unpacked'], + type: "Unpacked['addDevice']>>", }, { f: rg.addMembers, imports: ['Channel', 'Unpacked'], - type: "Unpacked['addMembers']>>", + type: + "Unpacked['addMembers']>>", }, { f: rg.addModerators, imports: ['Channel', 'Unpacked'], type: - "Unpacked['addModerators']>>", + "Unpacked['addModerators']>>", }, { f: rg.banUsers, - imports: ['StreamChat', 'Unpacked'], - type: "Unpacked['banUser']>>", + imports: ['StreamChat', 'DefaultGenerics', 'Unpacked'], + type: "Unpacked['banUser']>>", }, { f: rg.channelSearch, - imports: ['StreamChat', 'Unpacked'], - type: "Unpacked['search']>>", + imports: ['StreamChat', 'DefaultGenerics', 'Unpacked'], + type: "Unpacked['search']>>", }, { f: rg.connect, - imports: ['StreamChat', 'Unpacked'], - type: "Unpacked['connect']>>", + imports: ['StreamChat', 'DefaultGenerics', 'Unpacked'], + type: "Unpacked['connect']>>", }, { f: rg.connectAnonymousUser, - imports: ['StreamChat', 'Unpacked'], - type: "Unpacked['connectAnonymousUser']>>", + imports: ['StreamChat', 'DefaultGenerics', 'Unpacked'], + type: "Unpacked['connectAnonymousUser']>>", }, { f: rg.connectUser, - imports: ['StreamChat', 'Unpacked'], - type: "Unpacked['connectUser']>>", + imports: ['StreamChat', 'DefaultGenerics', 'Unpacked'], + type: "Unpacked['connectUser']>>", }, { f: rg.create, imports: ['Channel', 'Unpacked'], - type: "Unpacked['create']>>", + type: + "Unpacked['create']>>", }, { f: rg.createBlockList, imports: ['StreamChat', 'Unpacked'], type: - "Unpacked['createBlockList']>>", + "Unpacked['createBlockList']>>", }, // createChannelType has a limit. So only run this when needed. // { // f: rg.createChannelType, // imports: ['StreamChat', 'Unpacked'], - // type: "Unpacked>", + // type: "Unpacked['createChannelType']>>", // }, { f: rg.createCommand, imports: ['StreamChat', 'Unpacked'], type: - "Unpacked['createCommand']>>", + "Unpacked['createCommand']>>", }, { f: rg.createPermission, - imports: ['StreamChat', 'Unpacked'], - type: "Unpacked['createPermission']>>", + imports: ['StreamChat', 'DefaultGenerics', 'Unpacked'], + type: "Unpacked['createPermission']>>", }, { f: rg.deactivateUser, - imports: ['StreamChat', 'Unpacked'], - type: "Unpacked['deactivateUser']>>", + imports: ['StreamChat', 'DefaultGenerics', 'Unpacked'], + type: "Unpacked['deactivateUser']>>", }, { f: rg.deleteBlockList, imports: ['StreamChat', 'Unpacked'], type: - "Unpacked['deleteBlockList']>>", + "Unpacked['deleteBlockList']>>", }, { f: rg.deleteChannel, imports: ['Channel', 'Unpacked'], - type: "Unpacked['delete']>>", + type: + "Unpacked['delete']>>", }, // TODO: Fix the error which results from deleteChannelType api call: // `deleteChannelType failed with error: { Error: StreamChat error code 16: DeleteChannelType failed with error: "bc0b09df-2cfd-4e80-93e7-1f0091e6a435 is not a defined channel type"` @@ -104,434 +107,457 @@ const executables = [ // { // f: rg.deleteChannelType, // imports: ['StreamChat', 'Unpacked'], - // type: "Unpacked>", + // type: "Unpacked['deleteChannelType']>>", // }, { f: rg.deleteCommand, imports: ['StreamChat', 'Unpacked'], type: - "Unpacked['deleteCommand']>>", + "Unpacked['deleteCommand']>>", }, { f: rg.deleteFile, imports: ['Channel', 'Unpacked'], - type: "Unpacked['deleteFile']>>", + type: + "Unpacked['deleteFile']>>", }, { f: rg.deleteImage, imports: ['Channel', 'Unpacked'], - type: "Unpacked['deleteImage']>>", + type: + "Unpacked['deleteImage']>>", }, { f: rg.deleteMessage, - imports: ['StreamChat', 'Unpacked'], - type: "Unpacked['deleteMessage']>>", + imports: ['StreamChat', 'DefaultGenerics', 'Unpacked'], + type: "Unpacked['deleteMessage']>>", }, { f: rg.deletePermission, - imports: ['StreamChat', 'Unpacked'], - type: "Unpacked['deletePermission']>>", + imports: ['StreamChat', 'DefaultGenerics', 'Unpacked'], + type: "Unpacked['deletePermission']>>", }, { f: rg.deleteReaction, imports: ['Channel', 'Unpacked'], type: - "Unpacked['deleteReaction']>>", + "Unpacked['deleteReaction']>>", }, { f: rg.deleteUser, - imports: ['StreamChat', 'Unpacked'], - type: "Unpacked['deleteUser']>>", + imports: ['StreamChat', 'DefaultGenerics', 'Unpacked'], + type: "Unpacked['deleteUser']>>", }, { f: rg.demoteModerators, imports: ['Channel', 'Unpacked'], type: - "Unpacked['demoteModerators']>>", + "Unpacked['demoteModerators']>>", }, { f: rg.disconnect, - imports: ['StreamChat', 'Unpacked'], - type: "Unpacked['disconnect']>>", + imports: ['StreamChat', 'DefaultGenerics', 'Unpacked'], + type: "Unpacked['disconnect']>>", }, { f: rg.exportUser, - imports: ['StreamChat', 'Unpacked'], - type: "Unpacked['exportUser']>>", + imports: ['StreamChat', 'DefaultGenerics', 'Unpacked'], + type: "Unpacked['exportUser']>>", }, { f: rg.flagMessage, imports: ['StreamChat', 'Unpacked'], type: - "Unpacked['flagMessage']>>", + "Unpacked['flagMessage']>>", }, { f: rg.flagUser, imports: ['StreamChat', 'Unpacked'], type: - "Unpacked['flagUser']>>", + "Unpacked['flagUser']>>", }, { f: rg.getAppSettings, - imports: ['StreamChat', 'Unpacked'], - type: "Unpacked['getAppSettings']>>", + imports: ['StreamChat', 'DefaultGenerics', 'Unpacked'], + type: "Unpacked['getAppSettings']>>", }, { f: rg.getBlockList, - imports: ['StreamChat', 'Unpacked'], - type: "Unpacked['getBlockList']>>", + imports: ['StreamChat', 'DefaultGenerics', 'Unpacked'], + type: "Unpacked['getBlockList']>>", }, { f: rg.getChannelType, - imports: ['StreamChat', 'Unpacked'], - type: "Unpacked['getChannelType']>>", + imports: ['StreamChat', 'DefaultGenerics', 'Unpacked'], + type: "Unpacked['getChannelType']>>", }, { f: rg.getCommand, imports: ['StreamChat', 'Unpacked'], type: - "Unpacked['getCommand']>>", + "Unpacked['getCommand']>>", }, { f: rg.getConfig, imports: ['Channel', 'Unpacked'], - type: "Unpacked['getConfig']>>", + type: + "Unpacked['getConfig']>>", }, { f: rg.getDevices, - imports: ['StreamChat', 'Unpacked'], - type: "Unpacked['getDevices']>>", + imports: ['StreamChat', 'DefaultGenerics', 'Unpacked'], + type: "Unpacked['getDevices']>>", }, { f: rg.getMessage, - imports: ['StreamChat', 'Unpacked'], - type: "Unpacked['getMessage']>>", + imports: ['StreamChat', 'DefaultGenerics', 'Unpacked'], + type: "Unpacked['getMessage']>>", }, { f: rg.getMessagesById, imports: ['Channel', 'Unpacked'], type: - "Unpacked['getMessagesById']>>", + "Unpacked['getMessagesById']>>", }, { f: rg.getMessageWithReply, imports: ['StreamChat', 'Unpacked'], type: - "Unpacked['getMessage']>>", + "Unpacked['getMessage']>>", }, { f: rg.getPermission, - imports: ['StreamChat', 'Unpacked'], - type: "Unpacked['getPermission']>>", + imports: ['StreamChat', 'DefaultGenerics', 'Unpacked'], + type: "Unpacked['getPermission']>>", }, { f: rg.getReactions, imports: ['Channel', 'Unpacked'], type: - "Unpacked['getReactions']>>", + "Unpacked['getReactions']>>", }, { f: rg.getReplies, imports: ['Channel', 'Unpacked'], type: - "Unpacked['getReplies']>>", + "Unpacked['getReplies']>>", }, { f: rg.hide, imports: ['Channel', 'Unpacked'], - type: "Unpacked['hide']>>", + type: + "Unpacked['hide']>>", }, { f: rg.inviteMembers, imports: ['Channel', 'Unpacked'], type: - "Unpacked['inviteMembers']>>", + "Unpacked['inviteMembers']>>", }, { f: rg.keystroke, imports: ['Channel', 'Unpacked'], - type: "Unpacked['keystroke']>>", + type: + "Unpacked['keystroke']>>", }, { f: rg.lastMessage, imports: ['Channel', 'FormatMessageResponse', 'Unpacked'], type: - "Omit, 'created_at' | 'updated_at'> & { created_at?: string; updated_at?: string } | undefined", + "Omit, 'created_at' | 'updated_at'> & { created_at?: string; updated_at?: string } | undefined", }, { f: rg.listBlockLists, - imports: ['StreamChat', 'Unpacked'], - type: "Unpacked['listBlockLists']>>", + imports: ['StreamChat', 'DefaultGenerics', 'Unpacked'], + type: "Unpacked['listBlockLists']>>", }, { f: rg.listChannelTypes, - imports: ['StreamChat', 'Unpacked'], - type: "Unpacked['listChannelTypes']>>", + imports: ['StreamChat', 'DefaultGenerics', 'Unpacked'], + type: "Unpacked['listChannelTypes']>>", }, { f: rg.listCommands, imports: ['StreamChat', 'Unpacked'], type: - "Unpacked['listCommands']>>", + "Unpacked['listCommands']>>", }, { f: rg.listPermissions, - imports: ['StreamChat', 'Unpacked'], - type: "Unpacked['listPermissions']>>", + imports: ['StreamChat', 'DefaultGenerics', 'Unpacked'], + type: "Unpacked['listPermissions']>>", }, { f: rg.markAllRead, - imports: ['StreamChat', 'Unpacked'], - type: "Unpacked['markAllRead']>>", + imports: ['StreamChat', 'DefaultGenerics', 'Unpacked'], + type: "Unpacked['markAllRead']>>", }, { f: rg.markRead, imports: ['Channel', 'Unpacked'], - type: "Unpacked['markRead']>>", + type: + "Unpacked['markRead']>>", }, { f: rg.mute, imports: ['Channel', 'Unpacked'], - type: "Unpacked['mute']>>", + type: + "Unpacked['mute']>>", }, { f: rg.muteStatus, imports: ['Channel', 'Unpacked'], - type: "Unpacked['muteStatus']>>", + type: + "Unpacked['muteStatus']>>", }, { f: rg.muteUser, - imports: ['StreamChat', 'Unpacked'], - type: "Unpacked['muteUser']>>", + imports: ['StreamChat', 'DefaultGenerics', 'Unpacked'], + type: "Unpacked['muteUser']>>", }, { f: rg.partialUpdateUser, imports: ['StreamChat', 'Unpacked'], type: - "Unpacked['partialUpdateUser']>>", + "Unpacked['partialUpdateUser']>>", }, { f: rg.partialUpdateUsers, imports: ['StreamChat', 'Unpacked'], type: - "Unpacked['partialUpdateUsers']>>", + "Unpacked['partialUpdateUsers']>>", }, { f: rg.query, imports: ['Channel', 'Unpacked'], - type: "Unpacked['query']>>", + type: + "Unpacked['query']>>", }, // TODO: Add this back in when queryBannedUsers is deployed to all shards for testing // { // f: rg.queryBannedUsers, - // imports: ['StreamChat', 'Unpacked'], + // imports: ['StreamChat', 'DefaultGenerics', 'Unpacked'], // type: - // "Unpacked['queryBannedUsers']>>", + // "Unpacked['queryBannedUsers']>>", // }, { f: rg.queryMembers, imports: ['Channel', 'Unpacked'], type: - "Unpacked['queryMembers']>>", + "Unpacked['queryMembers']>>", }, { f: rg.queryUsers, imports: ['StreamChat', 'Unpacked'], - type: "Unpacked['queryUsers']>>", + type: + "Unpacked['queryUsers']>>", }, { f: rg.reactivateUser, - imports: ['StreamChat', 'Unpacked'], - type: "Unpacked['reactivateUser']>>", + imports: ['StreamChat', 'DefaultGenerics', 'Unpacked'], + type: "Unpacked['reactivateUser']>>", }, { f: rg.rejectInvite, imports: ['Channel', 'Unpacked'], type: - "Unpacked['rejectInvite']>>", + "Unpacked['rejectInvite']>>", }, { f: rg.removeMembers, imports: ['Channel', 'Unpacked'], type: - "Unpacked['removeMembers']>>", + "Unpacked['removeMembers']>>", }, { f: rg.removeShadowBan, - imports: ['StreamChat', 'Unpacked'], - type: "Unpacked['removeShadowBan']>>", + imports: ['StreamChat', 'DefaultGenerics', 'Unpacked'], + type: "Unpacked['removeShadowBan']>>", }, { f: rg.sendAction, imports: ['Channel', 'Unpacked'], type: - "Unpacked['sendAction']>>", + "Unpacked['sendAction']>>", }, { f: rg.sendFile, imports: ['Channel', 'Unpacked'], - type: "Unpacked['sendFile']>>", + type: + "Unpacked['sendFile']>>", }, { f: rg.sendImage, imports: ['Channel', 'Unpacked'], - type: "Unpacked['sendImage']>>", + type: + "Unpacked['sendImage']>>", }, { f: rg.sendMessage, imports: ['Channel', 'Unpacked'], type: - "Unpacked['sendMessage']>>", + "Unpacked['sendMessage']>>", }, { f: rg.sendMessageReadEvent, imports: ['Channel', 'Unpacked'], - type: "Unpacked['sendEvent']>>", + type: + "Unpacked['sendEvent']>>", }, { f: rg.sendReaction, imports: ['Channel', 'Unpacked'], type: - "Unpacked['sendReaction']>>", + "Unpacked['sendReaction']>>", }, { f: rg.setGuestUser, - imports: ['StreamChat', 'Unpacked'], - type: "Unpacked['setGuestUser']>>", + imports: ['StreamChat', 'DefaultGenerics', 'Unpacked'], + type: "Unpacked['setGuestUser']>>", }, { f: rg.shadowBan, - imports: ['StreamChat', 'Unpacked'], - type: "Unpacked['shadowBan']>>", + imports: ['StreamChat', 'DefaultGenerics', 'Unpacked'], + type: "Unpacked['shadowBan']>>", }, { f: rg.show, imports: ['Channel', 'Unpacked'], - type: "Unpacked['show']>>", + type: + "Unpacked['show']>>", }, { f: rg.stopTyping, imports: ['Channel', 'Unpacked'], - type: "Unpacked['stopTyping']>>", + type: + "Unpacked['stopTyping']>>", }, { f: rg.stopWatching, imports: ['Channel', 'Unpacked'], type: - "Unpacked['stopWatching']>>", + "Unpacked['stopWatching']>>", }, { f: rg.sync, imports: ['StreamChat', 'Unpacked'], - type: "Unpacked['sync']>>", + type: + "Unpacked['sync']>>", }, { f: rg.syncTeam, imports: ['StreamChat', 'Unpacked'], - type: "Unpacked['sync']>>", + type: + "Unpacked['sync']>>", }, // Need translation on the account to run this test // { // f: rg.translateMessage, - // imports: ['StreamChat', 'Unpacked'], + // imports: ['StreamChat', 'DefaultGenerics', 'Unpacked'], // type: - // "Unpacked['translateMessage']>>", + // "Unpacked['translateMessage']>>", // }, { f: rg.truncateChannel, imports: ['Channel', 'Unpacked'], - type: "Unpacked['truncate']>>", + type: + "Unpacked['truncate']>>", }, { f: rg.unbanUsers, - imports: ['StreamChat', 'Unpacked'], - type: "Unpacked['unbanUser']>>", + imports: ['StreamChat', 'DefaultGenerics', 'Unpacked'], + type: "Unpacked['unbanUser']>>", }, { f: rg.unmute, imports: ['Channel', 'Unpacked'], - type: "Unpacked['unmute']>>", + type: + "Unpacked['unmute']>>", }, { f: rg.unmuteUser, - imports: ['StreamChat', 'Unpacked'], - type: "Unpacked['unmuteUser']>>", + imports: ['StreamChat', 'DefaultGenerics', 'Unpacked'], + type: "Unpacked['unmuteUser']>>", }, { f: rg.updateAppSettings, - imports: ['StreamChat', 'Unpacked'], - type: "Unpacked['updateAppSettings']>>", + imports: ['StreamChat', 'DefaultGenerics', 'Unpacked'], + type: "Unpacked['updateAppSettings']>>", }, { f: rg.updateBlockList, imports: ['StreamChat', 'Unpacked'], type: - "Unpacked['updateBlockList']>>", + "Unpacked['updateBlockList']>>", }, { f: rg.updateChannel, imports: ['Channel', 'Unpacked'], - type: "Unpacked['update']>>", + type: + "Unpacked['update']>>", }, { f: rg.updateChannelFromOriginal, imports: ['Channel', 'Unpacked'], type: - "Unpacked['update']>>", + "Unpacked['update']>>", }, { f: rg.updateChannelType, - imports: ['StreamChat', 'Unpacked'], - type: "Unpacked['updateChannelType']>>", + imports: ['StreamChat', 'DefaultGenerics', 'Unpacked'], + type: "Unpacked['updateChannelType']>>", }, { f: rg.updateCommand, imports: ['StreamChat', 'Unpacked'], type: - "Unpacked['updateCommand']>>", + "Unpacked['updateCommand']>>", }, { f: rg.updateMessage, imports: ['StreamChat', 'Unpacked'], type: - "Unpacked['updateMessage']>>", + "Unpacked['updateMessage']>>", }, { f: rg.updatePermission, - imports: ['StreamChat', 'Unpacked'], - type: "Unpacked['updatePermission']>>", + imports: ['StreamChat', 'DefaultGenerics', 'Unpacked'], + type: "Unpacked['updatePermission']>>", }, { f: rg.upsertUsers, imports: ['StreamChat', 'Unpacked'], - type: "Unpacked['upsertUsers']>>", + type: + "Unpacked['upsertUsers']>>", }, { f: rg.upsertUser, imports: ['StreamChat', 'Unpacked'], - type: "Unpacked['upsertUser']>>", + type: + "Unpacked['upsertUser']>>", }, { f: rg.watch, imports: ['Channel', 'Unpacked'], - type: "Unpacked['watch']>>", + type: + "Unpacked['watch']>>", }, // Currently roles do not return // { // f: rg.createRole, - // imports: ['StreamChat', 'Unpacked'], - // type: "Unpacked['createRole']>>", + // imports: ['StreamChat', 'DefaultGenerics', 'Unpacked'], + // type: "Unpacked['createRole']>>", // }, // { // f: rg.deleteRole, - // imports: ['StreamChat', 'Unpacked'], - // type: "Unpacked['deleteRole']>>", + // imports: ['StreamChat', 'DefaultGenerics', 'Unpacked'], + // type: "Unpacked['deleteRole']>>", // }, // { // f: rg.listRoles, - // imports: ['StreamChat', 'Unpacked'], - // type: "Unpacked['listRoles']>>", + // imports: ['StreamChat', 'DefaultGenerics', 'Unpacked'], + // type: "Unpacked['listRoles']>>", // }, ]; @@ -571,7 +597,7 @@ const executeAndWrite = async (func, name, type) => { const response = await func(); fs.appendFile( tsFileName, - `export const ${func.name}Response: ${type} = ${JSON.stringify(response, null, `\t`)}; \n`, + `export const ${func.name}Response: ${type} = ${JSON.stringify(response)}; \n`, function (err) { if (err) { return console.log(err); diff --git a/test/typescript/unit-test.ts b/test/typescript/unit-test.ts index 67f667a98..bd2d52f27 100644 --- a/test/typescript/unit-test.ts +++ b/test/typescript/unit-test.ts @@ -41,60 +41,82 @@ type MessageType = UR; type ReactionType = UR; type CommandType = string & {}; +type StreamTypes = { + attachmentType: AttachmentType; + channelType: ChannelType; + commandType: CommandType; + eventType: EventType; + messageType: MessageType; + reactionType: ReactionType; + userType: UserType; +}; + let voidReturn: void | { unsubscribe: () => void }; let voidPromise: Promise; -const client: StreamChat< - AttachmentType, - ChannelType, - CommandType, - EventType, - MessageType, - ReactionType, - UserType -> = new StreamChat( - apiKey, - undefined, - { - timeout: 3000, - logger: (logLevel: string, msg: string, extraData?: Record) => {}, - }, -); - -const clientWithoutSecret: StreamChat<{}, ChannelType, string & {}, {}, {}, {}, UserType> = new StreamChat< - {}, - ChannelType, - string & {}, - {}, - {}, - {}, - UserType ->(apiKey, { +const client: StreamChat = new StreamChat(apiKey, undefined, { timeout: 3000, logger: (logLevel: string, msg: string, extraData?: Record) => {}, }); -const singletonClient = StreamChat.getInstance< - AttachmentType, - ChannelType, - CommandType, - EventType, - MessageType, - ReactionType, - UserType ->(apiKey); - -const singletonClient1: StreamChat<{}, ChannelType, string & {}, {}, {}, {}, UserType> = StreamChat.getInstance< - {}, - ChannelType, - string & {}, - {}, - {}, - {}, - UserType ->(apiKey); - -const singletonClient2: StreamChat<{}, ChannelType> = StreamChat.getInstance<{}, ChannelType>(apiKey, '', {}); +const clientWithoutSecret: StreamChat<{ + attachmentType: {}; + channelType: ChannelType; + commandType: string & {}; + eventType: {}; + messageType: {}; + reactionType: {}; + userType: UserType; +}> = new StreamChat<{ + attachmentType: {}; + channelType: ChannelType; + commandType: string & {}; + eventType: {}; + messageType: {}; + reactionType: {}; + userType: UserType; +}>(apiKey, { + timeout: 3000, + logger: (logLevel: string, msg: string, extraData?: Record) => {}, +}); + +const singletonClient = StreamChat.getInstance(apiKey); + +const singletonClient1: StreamChat<{ + attachmentType: {}; + channelType: ChannelType; + commandType: string & {}; + eventType: {}; + messageType: {}; + reactionType: {}; + userType: UserType; +}> = StreamChat.getInstance<{ + attachmentType: {}; + channelType: ChannelType; + commandType: string & {}; + eventType: {}; + messageType: {}; + reactionType: {}; + userType: UserType; +}>(apiKey); + +const singletonClient2: StreamChat<{ + attachmentType: {}; + channelType: ChannelType; + commandType: string & {}; + eventType: {}; + messageType: {}; + reactionType: {}; + userType: UserType; +}> = StreamChat.getInstance<{ + attachmentType: {}; + channelType: ChannelType; + commandType: string & {}; + eventType: {}; + messageType: {}; + reactionType: {}; + userType: UserType; +}>(apiKey, '', {}); const devToken: string = client.devToken('joshua'); const token: string = client.createToken('james', 3600); @@ -105,7 +127,7 @@ const settingsPromise: Promise = client.updateAppSettings({}); const appPromise: Promise = client.getAppSettings(); voidPromise = client.disconnectUser(); -const updateRequest: PartialUserUpdate = { +const updateRequest: PartialUserUpdate = { id: 'vishal', set: { name: 'Awesome', @@ -114,14 +136,14 @@ const updateRequest: PartialUserUpdate = { }; const updateUser: Promise<{ - users: { [key: string]: UserResponse }; + users: { [key: string]: UserResponse }; }> = client.partialUpdateUser(updateRequest); const updateUsers: Promise<{ - users: { [key: string]: UserResponse }; + users: { [key: string]: UserResponse }; }> = client.partialUpdateUsers([updateRequest]); const updateUsersWithSingletonClient: Promise<{ - users: { [key: string]: UserResponse }; + users: { [key: string]: UserResponse }; }> = singletonClient.partialUpdateUsers([updateRequest]); const eventHandler = (event: Event) => {}; @@ -130,7 +152,7 @@ voidReturn = client.off(eventHandler); voidReturn = client.on('message.new', eventHandler); voidReturn = client.off('message.new', eventHandler); -let userReturn: ConnectAPIResponse; +let userReturn: ConnectAPIResponse; userReturn = client.connectUser({ id: 'john', phone: 2 }, devToken); userReturn = client.connectUser({ id: 'john', phone: 2 }, async () => 'token'); userReturn = client.setUser({ id: 'john', phone: 2 }, devToken); @@ -151,7 +173,7 @@ clientRes = client.delete('https://chat.stream-io-api.com/', { id: 2 }); const file: Promise = client.sendFile('aa', 'bb', 'text.jpg', 'image/jpg', { id: 'james' }); const type: EventTypes = 'user.updated'; -const event: Event = { +const event: Event = { type, cid: 'channelid', message: { @@ -180,42 +202,21 @@ const event: Event[] -> = client.queryChannels({}, {}, {}); +const channels: Promise[]> = client.queryChannels({}, {}, {}); channels.then((response) => { const type: string = response[0].type; const cid: string = response[0].cid; }); -const channel: Channel< - AttachmentType, - ChannelType, - CommandType, - EventType, - MessageType, - ReactionType, - UserType -> = client.channel('messaging', 'channelName', { color: 'green' }); -const channelState: ChannelState< - AttachmentType, - ChannelType, - CommandType, - EventType, - MessageType, - ReactionType, - UserType -> = channel.state; -const chUser1: ChannelMemberResponse = channelState.members.someUser12433222; -const chUser2: ChannelMemberResponse = channelState.members.someUser124332221; - -const chUser3: UserResponse = channelState.read.someUserId.user; -const typing: Event = - channelState.typing['someUserId']; - -const acceptInvite: Promise< - UpdateChannelAPIResponse -> = channel.acceptInvite({}); +const channel: Channel = client.channel('messaging', 'channelName', { color: 'green' }); +const channelState: ChannelState = channel.state; +const chUser1: ChannelMemberResponse = channelState.members.someUser12433222; +const chUser2: ChannelMemberResponse = channelState.members.someUser124332221; + +const chUser3: UserResponse = channelState.read.someUserId.user; +const typing: Event = channelState.typing['someUserId']; + +const acceptInvite: Promise> = channel.acceptInvite({}); voidReturn = channel.on(eventHandler); voidReturn = channel.off(eventHandler);