From 328ecfb7e8f9bbba5774691c56c04a7cd6eeb8a8 Mon Sep 17 00:00:00 2001 From: Ivan Sekovanikj Date: Tue, 3 Nov 2026 14:01:48 +0100 Subject: [PATCH 1/3] refactor: remove setupCommandUIMiddlewares --- ai-docs/ai-migration-v9-to-v10.md | 4 +- ai-docs/v10-breaking-changes.md | 7 ++- examples/SampleApp/App.tsx | 3 -- package/src/index.ts | 1 - .../setupCommandUIMiddlewares.test.ts | 49 ------------------- .../src/utils/setupCommandUIMiddlewares.ts | 34 ------------- 6 files changed, 5 insertions(+), 93 deletions(-) delete mode 100644 package/src/utils/__tests__/setupCommandUIMiddlewares.test.ts delete mode 100644 package/src/utils/setupCommandUIMiddlewares.ts diff --git a/ai-docs/ai-migration-v9-to-v10.md b/ai-docs/ai-migration-v9-to-v10.md index 49ff58898b..f21790334c 100644 --- a/ai-docs/ai-migration-v9-to-v10.md +++ b/ai-docs/ai-migration-v9-to-v10.md @@ -315,6 +315,7 @@ means changed. Details in the linked section. | `client.configs[cid]` | `client.channelServerConfigs[cid]` | §13.1 | | `channel.serverConfig?.typing_events` (and the other gated flags) | `channel.config.typingEvents.enabled` — resolved, server ANDed with yours | §13.1 | | `client.setMessageComposerSetupFunction(fn)` | `client.config.setSetupFunction('messageComposer', fn)` | §13.1 | +| `setupCommandUIMiddlewares(composer)` | delete the call; `stream-chat` installs the command middlewares by default | §13.1 | | re-setting `channel.messagePaginator.pageSize` after mount | `client.config.set({ channel: { messagePaginator: { pageSize } } })` | §13.1, §16.1 | | `useTargetedMessage()` / `setTargetedMessage(id)` | `useMessagePaginator()?.jumpToMessage(id, { focusReason: 'jump-to-message', focusSignalTtlMs: DEFAULT_HIGHLIGHT_DURATION })`; read with `useIsTargetedMessage(id)` | §6 | | `useChannelContext().channelUnreadStateStore` / `setChannelUnreadState` | `channel.messagePaginator.unreadStateSnapshot` | §7 | @@ -1222,7 +1223,6 @@ that no value can express — middleware, comparators: ```tsx client.config.setSetupFunction('messageComposer', ({ composer }) => { - setupCommandUIMiddlewares(composer); composer.textComposer.middlewareExecutor.insert({ /* … */ }); }); ``` @@ -3313,7 +3313,7 @@ Names that still exist in v10 with a different meaning or on a different object ```bash # stream-chat-react-native / -expo / -core names removed or renamed in v10 rg -n -t js -t ts -g '!**/node_modules/**' '\b(useTypingContext|TypingProvider|TypingContextValue|useCreateTypingContext|usePaginatedMessageListContext|PaginatedMessageListProvider|PaginatedMessageListContextValue|useCreatePaginatedMessageListContext|useTargetedMessage|setTargetedMessage|channelUnreadStateStore|setChannelUnreadState|ChannelUnreadState|loadChannelAroundMessage|loadChannelAtFirstUnreadMessage|reloadChannel|highlightedMessageId|loadMoreThread|loadMoreRecentThread|reloadThread|threadMessages|setThreadMessages|threadHasMore|threadLoadingMore|threadLoadingMoreRecent|threadInstance|closeThreadOnDismount|ThreadMessageComposer|ThreadType|additionalMessageListProps|additionalMessageFlashListProps|additionalMessageComposerProps|shouldUseFlashList|shouldSyncChannel|MessageActionsHookProps|uploadAbortControllerRef|deliveredToCount|connectionRecovering)\b' . -rg -n -t js -t ts -g '!**/node_modules/**' '\b(doMarkReadRequest|doSendMessageRequest|doUpdateMessageRequest|stateUpdateThrottleInterval|newMessageStateUpdateThrottleInterval|preSendMessageRequest|patchMessageTextCommand|maximumMessageLimit|allowSendBeforeAttachmentsUpload|useChannelUpdated|queryChannelsOverride|lockChannelOrder|onAddedToChannel|onRemovedFromChannel|onChannelDeleted|onChannelHidden|onChannelVisible|onChannelUpdated|onChannelTruncated|onChannelMemberUpdated|onNewMessage|onNewMessageNotification|useSelectedChannelState|reduceMessagesToString|findInMessagesById|findInMessagesByDate|getLastReceivedMessage|DebugContextProvider|useDebugContext|DebugContextValue|DebugDataType|useIsOnline|isChannelActive|scrollToFirstUnreadThreshold|deleteReactionsForMessage|useClientMutedUsers|useAppSettings)\b' . +rg -n -t js -t ts -g '!**/node_modules/**' '\b(doMarkReadRequest|doSendMessageRequest|doUpdateMessageRequest|stateUpdateThrottleInterval|newMessageStateUpdateThrottleInterval|preSendMessageRequest|patchMessageTextCommand|maximumMessageLimit|allowSendBeforeAttachmentsUpload|useChannelUpdated|queryChannelsOverride|lockChannelOrder|onAddedToChannel|onRemovedFromChannel|onChannelDeleted|onChannelHidden|onChannelVisible|onChannelUpdated|onChannelTruncated|onChannelMemberUpdated|onNewMessage|onNewMessageNotification|useSelectedChannelState|reduceMessagesToString|findInMessagesById|findInMessagesByDate|getLastReceivedMessage|DebugContextProvider|useDebugContext|DebugContextValue|DebugDataType|useIsOnline|isChannelActive|scrollToFirstUnreadThreshold|deleteReactionsForMessage|useClientMutedUsers|useAppSettings|setupCommandUIMiddlewares)\b' . rg -n -t js -t ts -g '!**/node_modules/**' '\b(geti18Instance|getAvailableLanguages|getTranslations|addOnTFunctionOverrideListener|addOnLanguageChangeListener|getTranslators|relativeCompactDateFormatter|getDateStringForA11y|(en|es|fr|he|hi|it|ja|ko|nl|ptBR|ru|tr)Translations)\b' . rg -n -t js -t ts -t json -g '!**/node_modules/**' "'a11y/|\"a11y/" . rg -n -t js -t ts -g '!**/node_modules/**' '\bthreadList\s*[=:,}]|\bhardDelete\s*:|\buse(MutedUsers|MutedChannels)\(\s*\w|\buse(MessageActions|MessageActionHandlers)\(\s*\{|\buseMessageList\(\{\s*threadList' . diff --git a/ai-docs/v10-breaking-changes.md b/ai-docs/v10-breaking-changes.md index 216829399c..73710d362b 100644 --- a/ai-docs/v10-breaking-changes.md +++ b/ai-docs/v10-breaking-changes.md @@ -88,6 +88,9 @@ Everything below was exported from the package root in v9 and is gone in v10. - `reduceMessagesToString`, `findInMessagesById`, `findInMessagesByDate` → `channel.messagePaginator.getItem(id)` / `jumpToMessage(id)`. — P.1 - `patchMessageTextCommand` → nothing (the composer submits through `stream-chat`). — P.17 +- `setupCommandUIMiddlewares` → nothing; `stream-chat` installs the four command middlewares by default, so delete + the call. An app that never called it now gets them too: typing `/giphy cats` makes `giphy` the active command and + leaves `cats` in the input. — §13.1 - `createAttachmentsCompositionMiddleware`, `createDraftAttachmentsCompositionMiddleware`, `localAttachmentToAttachment` → `stream-chat`'s own `createAttachmentsCompositionMiddleware` (installed by default). — §17.6 · #3821 @@ -641,10 +644,6 @@ on `V10` after the guide's last update are listed first. ### #3916 (V10 @ `74884ff55`) -- `setupCommandUIMiddlewares` inserts all four middleware with `unique: true`, so running it again (the composer - setup function re-runs on every composer subscribe and every `client.config` change) replaces them rather than - stacking duplicates. A draft saved with an active command no longer comes back as `/giphy /giphy cats`. No action; - an app that calls it from its own setup function gets the fix too. - `` treats a paginator that was never queried as unloaded, so a list that mounts after `ChannelManager.ingestChannel()` routed an event into its paginator (opened from a push notification, or a second list such as archived channels) runs its first query instead of showing only the routed channels. — P.21 diff --git a/examples/SampleApp/App.tsx b/examples/SampleApp/App.tsx index 96390b81d4..f2ee5ab60d 100644 --- a/examples/SampleApp/App.tsx +++ b/examples/SampleApp/App.tsx @@ -18,7 +18,6 @@ import { createTextComposerEmojiMiddleware, LiveLocationManagerProvider, OverlayProvider, - setupCommandUIMiddlewares, SqliteClient, Streami18n, ThemeProvider, @@ -247,8 +246,6 @@ const App = () => { // to the declarative `client.config.set()` call in `useChatClient`, next to where the client is // created. A setup function is re-run on every configuration cycle, which is what middleware needs. chatClient.config.setSetupFunction('messageComposer', ({ composer }) => { - setupCommandUIMiddlewares(composer); - composer.textComposer.middlewareExecutor.insert({ middleware: [ createTextComposerEmojiMiddleware({ diff --git a/package/src/index.ts b/package/src/index.ts index eacc920cb7..427b5b9788 100644 --- a/package/src/index.ts +++ b/package/src/index.ts @@ -23,7 +23,6 @@ export * from './types/types'; export * from './utils/attachmentUrls'; export * from './utils/i18n/Streami18n'; export * from './i18n'; -export * from './utils/setupCommandUIMiddlewares'; export * from './utils/createGenerateVideoThumbnails'; export * from './utils/utils'; export * from './nativeMultipartUpload'; diff --git a/package/src/utils/__tests__/setupCommandUIMiddlewares.test.ts b/package/src/utils/__tests__/setupCommandUIMiddlewares.test.ts deleted file mode 100644 index 073f83d8e2..0000000000 --- a/package/src/utils/__tests__/setupCommandUIMiddlewares.test.ts +++ /dev/null @@ -1,49 +0,0 @@ -import type { MessageComposer } from 'stream-chat'; - -import { initiateClientWithChannels } from '../../mock-builders/api/initiateClientWithChannels'; -import { setupCommandUIMiddlewares } from '../setupCommandUIMiddlewares'; - -const countMiddleware = (composer: MessageComposer) => { - const count = (middleware: ReadonlyArray<{ id: string }>, id: string) => - middleware.filter((md) => md.id === id).length; - - return { - composition: count( - composer.compositionMiddlewareExecutor.installedMiddleware, - 'stream-io/message-composer-middleware/command-string-injection', - ), - draftComposition: count( - composer.draftCompositionMiddlewareExecutor.installedMiddleware, - 'stream-io/message-composer-middleware/draft-command-string-injection', - ), - textActiveCommandGuard: count( - composer.textComposer.middlewareExecutor.installedMiddleware, - 'stream-io/text-composer/active-command-guard', - ), - textCommandStringExtraction: count( - composer.textComposer.middlewareExecutor.installedMiddleware, - 'stream-io/text-composer/command-string-extraction', - ), - }; -}; - -describe('setupCommandUIMiddlewares', () => { - it('inserts each command middleware once, however often it runs on the same composer', async () => { - const { - channels: [channel], - } = await initiateClientWithChannels(); - const composer = channel.messageComposer; - - // The `messageComposer` setup function re-runs on every config change and re-subscription. - setupCommandUIMiddlewares(composer); - setupCommandUIMiddlewares(composer); - setupCommandUIMiddlewares(composer); - - expect(countMiddleware(composer)).toEqual({ - composition: 1, - draftComposition: 1, - textActiveCommandGuard: 1, - textCommandStringExtraction: 1, - }); - }); -}); diff --git a/package/src/utils/setupCommandUIMiddlewares.ts b/package/src/utils/setupCommandUIMiddlewares.ts deleted file mode 100644 index 8bd0dce3d9..0000000000 --- a/package/src/utils/setupCommandUIMiddlewares.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { - createActiveCommandGuardMiddleware, - createCommandInjectionMiddleware, - createCommandStringExtractionMiddleware, - createDraftCommandInjectionMiddleware, - MessageComposer, - TextComposerMiddleware, -} from 'stream-chat'; - -export const setupCommandUIMiddlewares = (messageComposer: MessageComposer) => { - messageComposer.compositionMiddlewareExecutor.insert({ - middleware: [createCommandInjectionMiddleware(messageComposer)], - position: { after: 'stream-io/message-composer-middleware/attachments' }, - unique: true, - }); - - messageComposer.draftCompositionMiddlewareExecutor.insert({ - middleware: [createDraftCommandInjectionMiddleware(messageComposer)], - position: { after: 'stream-io/message-composer-middleware/draft-attachments' }, - unique: true, - }); - - messageComposer.textComposer.middlewareExecutor.insert({ - middleware: [createActiveCommandGuardMiddleware() as TextComposerMiddleware], - position: { before: 'stream-io/text-composer/commands-middleware' }, - unique: true, - }); - - messageComposer.textComposer.middlewareExecutor.insert({ - middleware: [createCommandStringExtractionMiddleware() as TextComposerMiddleware], - position: { after: 'stream-io/text-composer/commands-middleware' }, - unique: true, - }); -}; From 01671f45b3f4de552003ce11e2e32945c5fe858c Mon Sep 17 00:00:00 2001 From: Ivan Sekovanikj Date: Tue, 3 Nov 2026 14:10:07 +0100 Subject: [PATCH 2/3] refactor(ChannelDetails): use the default text search for pinned messages --- .../ChannelPinnedMessageListContext.tsx | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/package/src/contexts/channelPinnedMessageListContext/ChannelPinnedMessageListContext.tsx b/package/src/contexts/channelPinnedMessageListContext/ChannelPinnedMessageListContext.tsx index 6c21e7fce3..6e9e547147 100644 --- a/package/src/contexts/channelPinnedMessageListContext/ChannelPinnedMessageListContext.tsx +++ b/package/src/contexts/channelPinnedMessageListContext/ChannelPinnedMessageListContext.tsx @@ -21,20 +21,11 @@ export const ChannelPinnedMessageListProvider = ({ }: PropsWithChildren<{ channel: Channel; searchSource?: MessageSearchSource }>) => { const { client } = useChatContext(); const [defaultSearchSource] = useState(() => { - const source = new MessageSearchSource( - client, - { pageSize: 25, allowEmptySearchString: true, resetOnNewSearchQuery: false }, - { - messageSearch: { - initialFilterConfig: { - $or: { - enabled: true, - generate: ({ searchQuery }) => (searchQuery ? { text: { $q: searchQuery } } : {}), - }, - }, - }, - }, - ); + const source = new MessageSearchSource(client, { + pageSize: 25, + allowEmptySearchString: true, + resetOnNewSearchQuery: false, + }); source.messageSearchChannelFilters = { cid: channel.cid, members: undefined }; source.messageSearchFilters = { pinned: true, type: undefined }; source.activate(); From f34a0b01cd2452af50bc73e20586a4f38c2d8b4a Mon Sep 17 00:00:00 2001 From: Ivan Sekovanikj Date: Tue, 3 Nov 2026 14:17:19 +0100 Subject: [PATCH 3/3] chore: bump stream-chat-js --- examples/E2ETestApp/package.json | 2 +- examples/ExpoChatGPTSample/package.json | 2 +- examples/ExpoMessaging/package.json | 2 +- examples/SampleApp/package.json | 2 +- package/package.json | 2 +- yarn.lock | 18 +++++++++--------- 6 files changed, 14 insertions(+), 14 deletions(-) diff --git a/examples/E2ETestApp/package.json b/examples/E2ETestApp/package.json index 4141218c32..92cf3d1658 100644 --- a/examples/E2ETestApp/package.json +++ b/examples/E2ETestApp/package.json @@ -23,7 +23,7 @@ "react-native-svg": "^15.15.5", "react-native-teleport": "^1.2.2", "react-native-worklets": "^0.13.0", - "stream-chat": "^10.0.0-rc.30", + "stream-chat": "^10.0.0-rc.31", "stream-chat-react-native": "workspace:^" }, "devDependencies": { diff --git a/examples/ExpoChatGPTSample/package.json b/examples/ExpoChatGPTSample/package.json index ae780d2eab..8ab3293b1f 100644 --- a/examples/ExpoChatGPTSample/package.json +++ b/examples/ExpoChatGPTSample/package.json @@ -29,7 +29,7 @@ "react-native-svg": "15.15.5", "react-native-teleport": "^1.2.2", "react-native-worklets": "0.13.0", - "stream-chat": "^10.0.0-rc.30", + "stream-chat": "^10.0.0-rc.31", "stream-chat-expo": "workspace:^", "stream-chat-react-native-core": "workspace:^", "victory-native": "^42.0.1" diff --git a/examples/ExpoMessaging/package.json b/examples/ExpoMessaging/package.json index 83a850f1c8..30598978a2 100644 --- a/examples/ExpoMessaging/package.json +++ b/examples/ExpoMessaging/package.json @@ -51,7 +51,7 @@ "react-native-teleport": "^1.2.2", "react-native-web": "~0.21.4", "react-native-worklets": "0.13.0", - "stream-chat": "^10.0.0-rc.30", + "stream-chat": "^10.0.0-rc.31", "stream-chat-expo": "workspace:^", "stream-chat-react-native-core": "workspace:^" }, diff --git a/examples/SampleApp/package.json b/examples/SampleApp/package.json index 2afea71076..e787ade062 100644 --- a/examples/SampleApp/package.json +++ b/examples/SampleApp/package.json @@ -65,7 +65,7 @@ "react-native-teleport": "^1.2.2", "react-native-video": "^6.19.3", "react-native-worklets": "^0.13.0", - "stream-chat": "^10.0.0-rc.30", + "stream-chat": "^10.0.0-rc.31", "stream-chat-react-native": "workspace:^", "stream-chat-react-native-core": "workspace:^" }, diff --git a/package/package.json b/package/package.json index ac553e816b..9832285fbc 100644 --- a/package/package.json +++ b/package/package.json @@ -85,7 +85,7 @@ "react-native-markdown-package": "1.8.2", "react-native-url-polyfill": "^2.0.0", "react-syntax-highlighter": "^16.1.0", - "stream-chat": "^10.0.0-rc.30", + "stream-chat": "^10.0.0-rc.31", "use-sync-external-store": "^1.7.0", "zod": "^4.1.12" }, diff --git a/yarn.lock b/yarn.lock index df9f280f45..d0ffa5584b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6933,7 +6933,7 @@ __metadata: react-native-teleport: "npm:^1.2.2" react-native-web: "npm:~0.21.4" react-native-worklets: "npm:0.13.0" - stream-chat: "npm:^10.0.0-rc.30" + stream-chat: "npm:^10.0.0-rc.31" stream-chat-expo: "workspace:^" stream-chat-react-native-core: "workspace:^" typescript: "npm:6.0.3" @@ -9569,7 +9569,7 @@ __metadata: react-native-svg: "npm:^15.15.5" react-native-teleport: "npm:^1.2.2" react-native-worklets: "npm:^0.13.0" - stream-chat: "npm:^10.0.0-rc.30" + stream-chat: "npm:^10.0.0-rc.31" stream-chat-react-native: "workspace:^" typescript: "npm:6.0.3" languageName: unknown @@ -10952,7 +10952,7 @@ __metadata: react-native-svg: "npm:15.15.5" react-native-teleport: "npm:^1.2.2" react-native-worklets: "npm:0.13.0" - stream-chat: "npm:^10.0.0-rc.30" + stream-chat: "npm:^10.0.0-rc.31" stream-chat-expo: "workspace:^" stream-chat-react-native-core: "workspace:^" typescript: "npm:6.0.3" @@ -18558,7 +18558,7 @@ __metadata: react-native-teleport: "npm:^1.2.2" react-native-video: "npm:^6.19.3" react-native-worklets: "npm:^0.13.0" - stream-chat: "npm:^10.0.0-rc.30" + stream-chat: "npm:^10.0.0-rc.31" stream-chat-react-native: "workspace:^" stream-chat-react-native-core: "workspace:^" typescript: "npm:6.0.3" @@ -19371,7 +19371,7 @@ __metadata: react-syntax-highlighter: "npm:^16.1.0" react-test-renderer: "npm:19.3.0" rimraf: "npm:^6.0.1" - stream-chat: "npm:^10.0.0-rc.30" + stream-chat: "npm:^10.0.0-rc.31" typescript: "npm:6.0.3" use-sync-external-store: "npm:^1.7.0" uuid: "npm:^11.1.0" @@ -19459,9 +19459,9 @@ __metadata: languageName: unknown linkType: soft -"stream-chat@npm:^10.0.0-rc.30": - version: 10.0.0-rc.30 - resolution: "stream-chat@npm:10.0.0-rc.30" +"stream-chat@npm:^10.0.0-rc.31": + version: 10.0.0-rc.31 + resolution: "stream-chat@npm:10.0.0-rc.31" dependencies: "@stream-io/logger": "npm:^2.1.0" "@stream-io/state-store": "npm:^1.2.0" @@ -19472,7 +19472,7 @@ __metadata: built: true husky: built: true - checksum: 10c0/7b37807f87081900ea407dba5fd26f8ad345ab7c7e8c4caf3b40c6bafb20a1fc56a0c7249a760d12949c700de5e633186b5c49c380f08d2c908e583d999f174f + checksum: 10c0/aa6dfa9c4b9adb6d7a3c86afe2f819e0f1f27f4903f2e29596a8e4e19ffdef7f7bc8b7fd579a9e6a25b95a4984137ce7f96557ef8932a4c4a127624a34c13374 languageName: node linkType: hard