diff --git a/src/channel.ts b/src/channel.ts index 29fb88bceb..05afc4623f 100644 --- a/src/channel.ts +++ b/src/channel.ts @@ -51,6 +51,7 @@ import { UpdateChannelAPIResponse, UserFilters, UserResponse, + QueryChannelAPIResponse, } from './types'; import { Role } from './permissions'; @@ -64,9 +65,24 @@ export class Channel | ChannelResponse | undefined; _data: ChannelData | ChannelResponse; cid: string; + /** */ listeners: { [key: string]: (string | EventHandler)[] }; state: ChannelState; + /** + * This boolean is a vague indication of weather the channel exists on chat backend. + * + * If the value is true, then that means the channel has been initialized by either calling + * channel.create() or channel.query() or channel.watch(). + * + * If the value is false, then channel may or may not exist on the backend. The only way to ensure + * is by calling channel.create() or channel.query() or channel.watch(). + */ initialized: boolean; + /** + * Indicates weather channel has been initialized by manually populating the state with some messages, members etc. + * Static state indicates that channel exists on backend, but is not being watched yet. + */ + offlineMode: boolean; lastKeyStroke?: Date; lastTypingEvent: Date | null; isTyping: boolean; @@ -110,6 +126,7 @@ export class Channel(this); this.initialized = false; + this.offlineMode = false; this.lastTypingEvent = null; this.isTyping = false; this.disconnected = false; @@ -730,7 +747,7 @@ export class Channel} options additional options for the query endpoint * - * @return {Promise>} The server response + * @return {Promise>} The server response */ async watch(options?: ChannelQueryOptions) { const defaultOptions = { @@ -929,7 +946,7 @@ export class Channel>} The Server Response + * @return {Promise>} The Server Response */ create = async () => { const options = { @@ -946,7 +963,7 @@ export class Channel} options The query options * @param {MessageSetType} messageSetToAddToIfDoesNotExist It's possible to load disjunct sets of a channel's messages into state, use `current` to load the initial channel state or if you want to extend the currently displayed messages, use `latest` if you want to load/extend the latest messages, `new` is used for loading a specific message and it's surroundings * - * @return {Promise>} Returns a query response + * @return {Promise>} Returns a query response */ async query( options: ChannelQueryOptions, @@ -960,7 +977,7 @@ export class Channel>(queryURL + '/query', { + const state = await this.getClient().post>(queryURL + '/query', { data: this._data, state: true, ...options, @@ -992,10 +1009,18 @@ export class Channel { - if (truncatedAt > +createdAt) channelState.removePinnedMessage({ id }); + if (truncatedAt > +createdAt) + channelState.removePinnedMessage({ id } as MessageResponse); }); } else { channelState.clearMessages(); @@ -1368,7 +1394,7 @@ export class Channel) { diff --git a/src/channel_state.ts b/src/channel_state.ts index 9e399705e4..1a27a4076a 100644 --- a/src/channel_state.ts +++ b/src/channel_state.ts @@ -244,6 +244,10 @@ export class ChannelState; }; anonymous: boolean; + persistUserOnConnectionFailure?: boolean; axiosInstance: AxiosInstance; baseURL?: string; browser: boolean; @@ -279,6 +281,7 @@ export class StreamChat[] }>( - this.baseURL + '/channels', - payload, - ); + const data = await this.post>(this.baseURL + '/channels', payload); - const channels: Channel[] = []; + this.dispatchEvent({ + type: 'channels.queried', + queriedChannels: { + channels: data.channels, + isLatestMessageSet: true, + }, + }); + + return this.hydrateActiveChannels(data.channels, stateOptions); + } - // update our cache of the configs - for (const channelState of data.channels) { + hydrateActiveChannels( + channelsFromApi: ChannelAPIResponse[] = [], + stateOptions: ChannelStateOptions = {}, + ) { + const { skipInitialization, offlineMode = false } = stateOptions; + + for (const channelState of channelsFromApi) { this._addChannelConfig(channelState); } - for (const channelState of data.channels) { + const channels: Channel[] = []; + + for (const channelState of channelsFromApi) { const c = this.channel(channelState.channel.type, channelState.channel.id); c.data = channelState.channel; - c.initialized = true; + c.offlineMode = offlineMode; + c.initialized = !offlineMode; if (skipInitialization === undefined) { c._initializeState(channelState, 'latest'); @@ -1504,6 +1524,18 @@ export class StreamChat} Promise that completes once the first health check message is received */ async _connect() { - if (this.isConnecting || this.isDisconnected) return; // simply ignore _connect if it's currently trying to connect + if (this.isConnecting || (this.isDisconnected && this.client.options.enableWSFallback)) return; // simply ignore _connect if it's currently trying to connect this.isConnecting = true; this.requestID = randomId(); this.client.insightMetrics.connectionStartTimestamp = new Date().getTime(); + let isTokenReady = false; try { this._log(`_connect() - waiting for token`); await this.client.tokenManager.tokenReady(); + isTokenReady = true; + } catch (e) { + // token provider has failed before, so try again + } + + try { + if (!isTokenReady) { + this._log(`_connect() - tokenProvider failed before, so going to retry`); + await this.client.tokenManager.loadToken(); + } + this._setupConnectionPromise(); const wsURL = this._buildUrl(); this._log(`_connect() - Connecting to ${wsURL}`, { wsURL, requestID: this.requestID }); @@ -288,7 +300,7 @@ export class StableWSConnection = APIResponse & { +export type QueryChannelsAPIResponse = APIResponse & { + channels: Omit, keyof APIResponse>[]; +}; + +export type QueryChannelAPIResponse = APIResponse & + ChannelAPIResponse; + +export type ChannelAPIResponse = { channel: ChannelResponse; members: ChannelMemberResponse[]; messages: MessageResponse[]; @@ -500,6 +507,7 @@ export type MessageResponse< export type MessageResponseBase< StreamChatGenerics extends ExtendableGenerics = DefaultGenerics > = MessageBase & { + type: MessageLabel; args?: string; channel?: ChannelResponse; cid?: string; @@ -523,7 +531,6 @@ export type MessageResponseBase< silent?: boolean; status?: string; thread_participants?: UserResponse[]; - type?: MessageLabel; updated_at?: string; }; @@ -580,6 +587,7 @@ export type ReactionResponse< StreamChatGenerics extends ExtendableGenerics = DefaultGenerics > = Reaction & { created_at: string; + message_id: string; updated_at: string; }; @@ -733,6 +741,7 @@ export type ChannelQueryOptions; member?: ChannelMemberResponse; message?: MessageResponse; + mode?: string; online?: boolean; parent_id?: string; + queriedChannels?: { + channels: ChannelAPIResponse[]; + isLatestMessageSet?: boolean; + }; reaction?: ReactionResponse; received_at?: string | Date; team?: string;