diff --git a/examples/ai-chatbot/package.json b/examples/ai-chatbot/package.json index f9f57c2d5e..520d6acede 100644 --- a/examples/ai-chatbot/package.json +++ b/examples/ai-chatbot/package.json @@ -18,7 +18,7 @@ "next": "^16.2.4", "react": "^19.2.6", "react-dom": "^19.2.6", - "stream-chat": "10.0.0-rc.20", + "stream-chat": "^10.0.0-rc.22", "stream-chat-react": "workspace:^" }, "devDependencies": { diff --git a/examples/tutorial/package.json b/examples/tutorial/package.json index 84ac206cd2..9ef2f9c87f 100644 --- a/examples/tutorial/package.json +++ b/examples/tutorial/package.json @@ -16,7 +16,7 @@ "emoji-mart": "^5.6.0", "react": "^19.2.6", "react-dom": "^19.2.6", - "stream-chat": "10.0.0-rc.20", + "stream-chat": "^10.0.0-rc.22", "stream-chat-react": "workspace:^" }, "devDependencies": { diff --git a/examples/vite/package.json b/examples/vite/package.json index ca24bd2f85..c8a4d5e2c9 100644 --- a/examples/vite/package.json +++ b/examples/vite/package.json @@ -18,7 +18,7 @@ "modern-normalize": "^3.0.1", "react": "^19.2.6", "react-dom": "^19.2.6", - "stream-chat": "10.0.0-rc.20", + "stream-chat": "^10.0.0-rc.22", "stream-chat-react": "workspace:^" }, "devDependencies": { diff --git a/examples/vite/src/AppSettings/ActionsMenu/ServerSideClientPromptDialog/serverSideClient.ts b/examples/vite/src/AppSettings/ActionsMenu/ServerSideClientPromptDialog/serverSideClient.ts index 60a0fa2e70..bab97b096d 100644 --- a/examples/vite/src/AppSettings/ActionsMenu/ServerSideClientPromptDialog/serverSideClient.ts +++ b/examples/vite/src/AppSettings/ActionsMenu/ServerSideClientPromptDialog/serverSideClient.ts @@ -77,7 +77,7 @@ export const createServerSideClient = async ({ const token = await createServerToken(secret); // Not `getInstance` — that would hand back (and mutate) the app's user-authenticated singleton. - const client = new StreamChat(apiKey, { allowServerSideConnect: true }); + const client = new StreamChat(apiKey, { client: { allowServerSideConnect: true } }); client.tokenManager.token = token; client.tokenManager.type = 'static'; diff --git a/package.json b/package.json index 6478cdfe16..25e0a8ca76 100644 --- a/package.json +++ b/package.json @@ -210,7 +210,7 @@ "react-dom": "^19.2.6", "sass": "^1.100.0", "semantic-release": "^25.0.3", - "stream-chat": "10.0.0-rc.20", + "stream-chat": "^10.0.0-rc.22", "typescript": "^6.0.3", "typescript-eslint": "^8.59.4", "vite": "^8.1.3", diff --git a/src/components/Chat/hooks/__tests__/useCreateChatClient.test.tsx b/src/components/Chat/hooks/__tests__/useCreateChatClient.test.tsx new file mode 100644 index 0000000000..a8b1737f19 --- /dev/null +++ b/src/components/Chat/hooks/__tests__/useCreateChatClient.test.tsx @@ -0,0 +1,31 @@ +import { renderHook, waitFor } from '@testing-library/react'; +import { describe, expect, it, vi } from 'vitest'; + +import { useCreateChatClient } from '../useCreateChatClient'; + +// `connectUser` and `disconnectUser` are instance properties, so they cannot be spied on through the +// prototype; the real client is subclassed to keep the network out of the test. +vi.mock('stream-chat', async (importOriginal) => { + const actual = await importOriginal(); + class OfflineStreamChat extends actual.StreamChat { + connectUser = vi.fn().mockResolvedValue(undefined); + disconnectUser = vi.fn().mockResolvedValue(undefined); + } + return { ...actual, StreamChat: OfflineStreamChat }; +}); + +describe('useCreateChatClient', () => { + it('constructs the client from the configuration tree it is given', async () => { + const { result } = renderHook(() => + useCreateChatClient({ + apiKey: 'key', + config: { client: { baseURL: 'http://example.com:3030' } }, + tokenOrProvider: 'token', + userData: { id: 'user' }, + }), + ); + + await waitFor(() => expect(result.current).not.toBeNull()); + expect(result.current?.baseURL).toBe('http://example.com:3030'); + }); +}); diff --git a/src/components/Chat/hooks/useCreateChatClient.ts b/src/components/Chat/hooks/useCreateChatClient.ts index 52f24aadc0..10cfbb4e08 100644 --- a/src/components/Chat/hooks/useCreateChatClient.ts +++ b/src/components/Chat/hooks/useCreateChatClient.ts @@ -2,21 +2,30 @@ import { useEffect, useState } from 'react'; import { StreamChat } from 'stream-chat'; -import type { ClientUser, StreamChatOptions, TokenOrProvider } from 'stream-chat'; +import type { + ClientUser, + DeepPartial, + InstanceConfigTree, + TokenOrProvider, +} from 'stream-chat'; /** * React hook to create, connect and return `StreamChat` client. + * + * `config` is the declarative configuration the client is constructed with — the same tree + * `client.config.set()` takes, e.g. `{ client: { baseURL, axiosRequestConfig } }`. It is read once, on + * the first render. */ export const useCreateChatClient = ({ apiKey, - options, + config, tokenOrProvider, userData, }: { apiKey: string; tokenOrProvider: TokenOrProvider; userData: ClientUser; - options?: StreamChatOptions; + config?: DeepPartial; }) => { const [chatClient, setChatClient] = useState(null); const [cachedUserData, setCachedUserData] = useState(userData); @@ -25,10 +34,10 @@ export const useCreateChatClient = ({ setCachedUserData(userData); } - const [cachedOptions] = useState(options); + const [cachedConfig] = useState(config); useEffect(() => { - const client = new StreamChat(apiKey, cachedOptions); + const client = new StreamChat(apiKey, cachedConfig); let didUserConnectInterrupt = false; const connectionPromise = client @@ -46,7 +55,7 @@ export const useCreateChatClient = ({ console.log(`Connection for user "${cachedUserData.id}" has been closed`); }); }; - }, [apiKey, cachedUserData, cachedOptions, tokenOrProvider]); + }, [apiKey, cachedUserData, cachedConfig, tokenOrProvider]); return chatClient; }; diff --git a/yarn.lock b/yarn.lock index bc4dfe58e7..f568b45935 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2186,7 +2186,7 @@ __metadata: react: "npm:^19.2.6" react-dom: "npm:^19.2.6" sass: "npm:^1.100.0" - stream-chat: "npm:10.0.0-rc.20" + stream-chat: "npm:^10.0.0-rc.22" stream-chat-react: "workspace:^" typescript: "npm:^6.0.3" languageName: unknown @@ -2203,7 +2203,7 @@ __metadata: emoji-mart: "npm:^5.6.0" react: "npm:^19.2.6" react-dom: "npm:^19.2.6" - stream-chat: "npm:10.0.0-rc.20" + stream-chat: "npm:^10.0.0-rc.22" stream-chat-react: "workspace:^" typescript: "npm:^6.0.3" vite: "npm:^8.1.3" @@ -2231,7 +2231,7 @@ __metadata: react: "npm:^19.2.6" react-dom: "npm:^19.2.6" sass: "npm:^1.100.0" - stream-chat: "npm:10.0.0-rc.20" + stream-chat: "npm:^10.0.0-rc.22" stream-chat-react: "workspace:^" typescript: "npm:^6.0.3" vite: "npm:^8.1.3" @@ -10257,7 +10257,7 @@ __metadata: remark-parse: "npm:^11.0.0" sass: "npm:^1.100.0" semantic-release: "npm:^25.0.3" - stream-chat: "npm:10.0.0-rc.20" + stream-chat: "npm:^10.0.0-rc.22" typescript: "npm:^6.0.3" typescript-eslint: "npm:^8.59.4" unified: "npm:^11.0.5" @@ -10299,9 +10299,9 @@ __metadata: languageName: unknown linkType: soft -"stream-chat@npm:10.0.0-rc.20": - version: 10.0.0-rc.20 - resolution: "stream-chat@npm:10.0.0-rc.20" +"stream-chat@npm:^10.0.0-rc.22": + version: 10.0.0-rc.22 + resolution: "stream-chat@npm:10.0.0-rc.22" dependencies: "@stream-io/logger": "npm:^2.0.0" "@stream-io/state-store": "npm:^1.1.6" @@ -10312,7 +10312,7 @@ __metadata: built: true husky: built: true - checksum: 10c0/6fa312d0a2fdb62c5493e5f97d188e15a142d351e5fcae77d9363fde5444d68a47f6206ec05fc4518aceb44a701a6b285b257cd1659e73391418f24d693188f9 + checksum: 10c0/8c1e1006c022d3433c7df5806c84d5f7dd4b487d3c973ab90d017cdaae7f57d7b58d45c0e6ce884861c0bfe631fe6a5efa2bdb28e727b0dbc272d80e3bea0ff2 languageName: node linkType: hard