Repository navigation
Release V10.0.0 - #3952
Merged
Merged
Release V10.0.0#3952
Conversation
## π― Goal Fix the code issues and wrong strings found while tidying the comments in #3884, which deliberately left them alone (comment-only change). **Stacked on #3884**: merge that first, then retarget this PR to `V10`. ## π Implementation details Every item was verified against the code first; the behavior fixes each come with a test that failed before the fix. **Bugs fixed** - **Markdown links** (`generateMarkdownText`): the conversion loop restarted from the untrimmed text on every link, so only the last link became a markdown link and the trim was lost. Links are now converted in one pass, longest first, so `getstream.io` inside `getstream.io/chat` is not wrapped twice. - **Offline drafts** (`upsertDraft`): the result of `queries.concat(...)` was discarded, so a draft's quoted and parent messages were never stored, although `getDraft` reads the quoted message back from `messages`. - **AI markdown `onLink`**: a rejected `onLink` was logged and rethrown inside a fire-and-forget `.catch`, causing an unhandled rejection. Now only logged. - **`parseJsonChart`**: invalid JSON threw a `SyntaxError` instead of the documented "Unknown type" error. - **`MessageActionList`**: destructive actions were given `length: standardActions.length` (visible to custom `MessageActionListItem` overrides). - **Emoji middleware**: an emoji with no `native` and empty `skins` threw a `TypeError`; such entries are now skipped (there is nothing to insert). - **`FileIcon`**: `.xlsb` used the `.xlam` MIME type and so showed the generic icon; removed duplicate `.odt/.ott/.fodt`, `application/gzip` and `video/webm` entries. - Removed an unreachable `null` branch in `getReactionsForFilterSort` and a dead `?? 0` in `hashStringToNumber`. **Strings** - **Permission alerts**: the shared `common.cameraPermission.text` / `common.galleryPermission.text` copy was ungrammatical ("Device gallery permissions is used to take photos or videos") and is fixed. The image-only channel image flow gets its own keys, `channelDetails.editImageSheet.cameraPermission.text` / `.galleryPermission.text`, instead of saying "photos or videos". - **`useMessagesContext` error**: named `MessageList` (the context comes from `<Channel>` / `<Thread>`) and linked a docs page that now returns 404. It now names `Channel` and links the live Channel page (the other context errors are fixed in #3884). - Clearer warning when the attachment picker cannot open (it reports the missing `snapToIndex`), an accurate label for the gallery's video placeholder, and fixed docs for `previewVoiceRecording`, the `useIsMountedRef` example and an `eslint-disable` description. - `keys.ts` regenerated; the SampleApp German and Italian dictionaries updated for the new and changed keys. **β οΈ Breaking: removed props that were never read** (v10 is a breaking release; listed in `ai-docs/v10-breaking-changes.md`) - `ProgressControl`: `width` (the control measures its own width). - `MessageActionList`: `dismissOverlay` (each item closes the overlay itself). A custom list that needs it can read `useMessageContext().dismissOverlay`. - `MessageFlashList`: `inverted` (the FlashList is never inverted). **Checked and left unchanged** - **Live locations in `upsertChannels`**: the `forEach(async)` really does drop the queries (they are never written today). A naive fix would make it worse: `locations.messageId` has a foreign key to `messages`, so a live location whose message is outside the stored window fails the whole channel batch. This needs a decision (drop the foreign key with a `dbVersion` bump, or write only locations whose message is stored). - **Gallery gestures**: the pan `finalXPosition` sign (velocity-gated paging, chosen in #3241) and the double-tap zoom-in `translateX` stay as they are. - Not bugs: `useRAFCoalescedValue` (a test now guards the re-enable case), the duration units (the gallery and media items both pass milliseconds), the pinch `scale < 1` branches (reachable on iOS) and the read of `translateY.value` right after starting an animation (Reanimated still returns the release position at that point). - `asyncMessagesMinimumPressDuration` is read by `AudioRecordingButton`, so it stays. ## π¨ UI Changes Only the permission alert copy. ## π§ͺ Testing - New or extended tests for every behavior fix: `upsertDraft` (real SQLite), `generateMarkdownText`, `Markdown` (`onLink` rejection), `charts`, `FileIcon`, `emojiControl`, `getTrimmedAttachmentTitle`, `useRAFCoalescedValue`, `ImageGallery`, `ImageGalleryVideoControl`, `MessageActionList`, `useEditChannelImage`, `filePickers`, `useAttachmentPickerBottomSheet` and the context error messages. - `yarn lint`, `yarn typecheck` (all workspaces), `yarn test:unit` and `yarn build` pass. ## βοΈ Checklist - [ ] I have signed the [Stream CLA](https://docs.google.com/forms/d/e/1FAIpQLScFKsKkAJI7mhCr7K9rEIOpqIDThrWxuvxnwUq2XkHyG154vQ/viewform) (required) - [ ] PR targets the `develop` branch (stacked on #3884, then `V10`) - [x] Documentation is updated - [ ] New code is tested in main example apps, including all possible scenarios - [ ] SampleApp iOS and Android - [ ] Expo iOS and Android --------- Co-authored-by: Ivan Sekovanikj <ivan.sekovanikj@getstream.io>
## π― Goal
Adapt to `stream-chat` 10.0.0-rc.24, which includes stream-chat-js#1926
(the message paginator now places every page itself, and
`messageSetToAddToIfDoesNotExist` is gone).
That PR renamed `messagePaginator.lastMessage` / `lastMessageAt` to
`latestMessage` / `latestMessageAt`. RN still read the old names, which
are now `undefined`. As a result, every channel preview showed "No
messages yet", and thread list rows lost the last reply's avatar and
time.
## π Implementation details
- Bump `stream-chat` to `^10.0.0-rc.24` in `package` and the example
apps.
- Read `latestMessage` / `latestMessageAt` in `useChannelPreviewData`,
`ThreadListItem`, `useMarkRead`, `MessageList` and `MessageFlashList`.
- Offline DB: `selectMessagesForChannels` returned 24 messages per
channel (`RowNum < 25`) instead of 25. The client reads a page shorter
than requested as the start of the channel, so older history could stop
loading past the cached messages. It now returns 25.
- Tests:
- `headItems` becomes `anchoredHeadItems`.
- The `Channel` test spies on `requestChannelState`, since the paginator
no longer goes through `channel.query`.
- The scroll-to-bottom test sets `hasMoreHead: true`, since a complete
short first page is now correctly the newest one.
- New real-SQLite test for the DB query.
- Docs: `ai-migration-v9-to-v10.md` and `v10-breaking-changes.md` now
use `latestMessage` and describe the `channel.query` behaviour
correctly. The link to the LLC's breaking-changes list is fixed.
No breaking changes in RN's own API.
## π¨ UI Changes
Channel list previews and thread list rows show the latest message /
reply again instead of "No messages yet".
## π§ͺ Testing
- `yarn lint`, `yarn build`, `yarn typecheck` and `yarn test:unit` (2243
passed).
- SampleApp on an Android emulator:
- Two cold starts with offline support on. Channel list previews show
the latest message all the way down the list, including soft-deleted
ones ("Message deleted").
- The thread list shows the last reply's avatar and time.
## βοΈ Checklist
- [x] I have signed the [Stream
CLA](https://docs.google.com/forms/d/e/1FAIpQLScFKsKkAJI7mhCr7K9rEIOpqIDThrWxuvxnwUq2XkHyG154vQ/viewform)
(required)
- [ ] PR targets the `develop` branch
- [x] Documentation is updated
- [ ] New code is tested in main example apps, including all possible
scenarios
- [ ] SampleApp iOS and Android
- [ ] Expo iOS and Android
## π― Goal Fix the SDK regressions that are new in v10, found while writing the v10 docs. In each case v9 behaved correctly, or the code is new in v10. ## π Implementation details - **Channels opened from search get live events again.** `Channel` now also calls `watch()` on mount when the channel's `watchStatus` is `notWatching`. Search sources load channels with `watch: false`, and those channels are initialized and at the head, so `Channel` used to skip the watch. A `wasWatching` channel is still left to the client's connection recovery. - **Channel and thread previews show "read" again.** `useMessageDeliveryStatus` now compares the last message's `created_at` with `lastReadRefByOthers` / `lastDeliveredRefByOthers`, the same check stream-chat-react uses. Before, it looked only for users whose cursor sits exactly on the message, so the mark stayed "sent" or "delivered" once the other member read a later message. - **Threads no longer show the channel's unread separator.** The LLC seeds a thread list's unread snapshot from the channel's read state. `MessageWrapper` now draws no separator when the list's paginator has a `parentMessageId`. The result matches stream-chat-react. - **A failed reply load shows its own error.** `Thread` used to show "Error loading messages for this channel...". It now passes a new `listType: 'thread'` to `LoadingErrorIndicator`, which shows "Error loading replies..." (key `indicators.loading.replies.error`). - **`Chat` keeps a network status reporter the app registered.** It installs `netInfoStatusReporter` only when no `statusReporter` is registered in `client.config`, which includes the tree given to the `StreamChat` constructor. Before, it replaced any reporter on every mount. - **New exports.** `useOwnUnreadCount`, `useChannelTypingUsers`, `useTypingUsers`, `getTranslatedMessage` and `useTranslatedMessage` are now exported. - **The channel row's accessibility hint has its own key.** It used the gallery's key, so translating one changed the other. It now uses `channelPreview.doubleTapToOpen.accessibilityLabel`. `ai-docs/i18n-v10-key-map.json` maps `a11y/Double tap to open` to both keys, through a new `alsoKeys` field. - **`userId` instead of the deprecated `userID`.** The SDK reads `client.userId` everywhere (35 call sites). Test client mocks are updated to match. - **JSDoc fix in `installNativeMultipartAdapter`.** With `useNativeMultipartUpload`, uploads go through the native uploader even when the app installed its own axios adapter, as in v9. The JSDoc said the adapter was left alone. No code change. - **`ThreadListUnreadBanner` reports only its own failure.** It shows "Couldn't load new threads" only when its own reload failed, not for a failed first load or load-more, as in v9. - **`ChannelListView` no longer passes or accepts `paginator`.** It never read it. - **The `MessageList` override key is removed** from `DEFAULT_COMPONENTS`. In v9 only `Thread` read it. v10's `Thread` renders its children, so overriding the key did nothing. This matches the earlier removal of `ThreadMessageComposer`. ### Breaking changes - The `MessageList` override key is removed. Render the list as a child of `<Thread>`. This is documented in `ai-migration-v9-to-v10.md` and `v10-breaking-changes.md`. - `ChannelListViewProps` no longer has `paginator`. That prop only existed in the v10 RCs. - `Thread` passes `listType='thread'` to `LoadingErrorIndicator`. A custom indicator that branches on `'message'` no longer hits that branch for threads. ## π¨ UI Changes - Channel list and thread list rows show the read mark once the other member has read past the last message. - Thread replies no longer show an "N unread messages" separator. - A failed reply load says "Error loading replies...". ## π§ͺ Testing - New or updated unit tests for each fix: `Channel`, `useMessageDeliveryStatus`, `MessageList` (thread separator), `Thread`, `Chat`, `ChannelPreviewView`, `ThreadList`, and the client mocks in about 25 test files. Each new regression test fails without its fix. - `yarn lint`, `yarn test:typecheck`, `yarn build` and `yarn test:unit` pass (2256 tests, 13 skipped). - Not checked on a device yet: - opening a search result outside the loaded list and receiving a message live - the read mark in the channel list after the other member replies - no separator in a thread of an unread channel ## βοΈ Checklist - [x] I have signed the [Stream CLA](https://docs.google.com/forms/d/e/1FAIpQLScFKsKkAJI7mhCr7K9rEIOpqIDThrWxuvxnwUq2XkHyG154vQ/viewform) (required) - [ ] PR targets the `develop` branch - [ ] Documentation is updated (the `ai-docs` migration guides are; the docs site follows separately) - [ ] New code is tested in main example apps, including all possible scenarios - [ ] SampleApp iOS and Android - [ ] Expo iOS and Android
β¦ations (V10) (#3896) Ticket: https://linear.app/stream/issue/RN-468 ## π― Goal Port of #3895 (the `develop` counterpart) to `V10`: the `VoiceWave` and `VoiceGlow` voice-agent visualizations in `stream-chat-react-native/ai-components` (and `stream-chat-expo/ai-components`), the `useDictationAudioLevel` hook, and the voice mode in `ExpoChatGPTSample`. Android, iOS and web already ship these visualizations in their AI components. ## π Implementation details Everything under `package/` is identical to #3895 (both of its commits, `0f07f0366` and `38a224a65`, squashed into one), and applied to V10 without changes. See #3895 for the full description. In short: - `VoiceWave` and `VoiceGlow`: one motion driver ported from Android, run as Reanimated worklets on the UI thread; the glow is Android's AGSL shader ported to Skia SkSL, the wave a Skia path. Props: `state`, `colors`, `expressivity`, `audioLevel` (`SharedValue<number | null>`) and `stateLabels`, plus `View` props. - `useDictationAudioLevel()`, backed by a new `AIDictationOnLevel` event in `package/shared-native/{android,ios}`; `useDictationState` and `useDictationTranscript` are now exported. - An optional `voiceVisualization` theme key (`colors`, `backdropColor`). - A `source` option on `useDictation`, so `ComposerView` only fills its input from its own mic button. - Fix: iOS dictation restores the `AVAudioSession` it found when dictation ends. **What differs from #3895 on V10** (all in `examples/ExpoChatGPTSample`): - `screens/ChatContent.tsx`: V10 already moved channel creation and `startAI`/`summarize` from `preSendMessageRequest` into the client's `sendMessageRequest` handler in `app/_layout.tsx`, so the port only adds `VoiceMode` and `useSendAIMessage` there and drops the imports it no longer needs. - `components/VoiceMode/useVoiceConversation.ts`: stream-chat v10 has no `channel.state.latestMessages`, so the voice mode reads `channel.messagePaginator.items`; and custom data lives under `custom`, so it checks `message.custom?.ai_generated`. - `hooks/useSendAIMessage.ts`: the doc comment names the `sendMessageRequest` handler instead of `preSendMessageRequest`. Docs: the v10-rc AI integration pages are updated in GetStream/getstream.io#810, together with v9. ## π¨ UI Changes Same UI as #3895; see its recordings. ## π§ͺ Testing - `yarn lint` (including `validate-entry-isolation`), `cd package && yarn test:typecheck`, `yarn typecheck`, `yarn build` and `yarn test:unit` (246 suites, 2302 tests) pass on this branch. `shared-native:sync` was run. - The unit tests are the same as on #3895 (driver, bounce, palette, geometry, rendering, accessibility, theme fallbacks, reduced motion, dictation level). - Not yet tested on a device on V10: the voice mode's switch to `messagePaginator.items` is type-checked only. Dictation needs a physical device (iOS 27 simulators can't initialize the speech recognizer). ## βοΈ Checklist - [ ] I have signed the [Stream CLA](https://docs.google.com/forms/d/e/1FAIpQLScFKsKkAJI7mhCr7K9rEIOpqIDThrWxuvxnwUq2XkHyG154vQ/viewform) (required) - [ ] PR targets the `develop` branch (targets `V10`; the `develop` counterpart is #3895) - [x] Documentation is updated (AI components README; v10-rc docs in GetStream/getstream.io#810) - [ ] New code is tested in main example apps, including all possible scenarios - [ ] SampleApp iOS and Android - [ ] Expo iOS and Android
## π― Goal
<!-- Describe why we are making this change -->
## π Implementation details
<!-- Provide a description of the implementation -->
## π¨ UI Changes
<!-- Add relevant screenshots -->
<details>
<summary>iOS</summary>
<table>
<thead>
<tr>
<td>Before</td>
<td>After</td>
</tr>
</thead>
<tbody>
<tr>
<td>
<!--<img src="" /> -->
</td>
<td>
<!--<img src="" /> -->
</td>
</tr>
</tbody>
</table>
</details>
<details>
<summary>Android</summary>
<table>
<thead>
<tr>
<td>Before</td>
<td>After</td>
</tr>
</thead>
<tbody>
<tr>
<td>
<!--<img src="" /> -->
</td>
<td>
<!--<img src="" /> -->
</td>
</tr>
</tbody>
</table>
</details>
## π§ͺ Testing
<!-- Explain how this change can be tested (or why it can't be tested)
-->
## βοΈ Checklist
- [ ] I have signed the [Stream
CLA](https://docs.google.com/forms/d/e/1FAIpQLScFKsKkAJI7mhCr7K9rEIOpqIDThrWxuvxnwUq2XkHyG154vQ/viewform)
(required)
- [ ] PR targets the `develop` branch
- [ ] Documentation is updated
- [ ] New code is tested in main example apps, including all possible
scenarios
- [ ] SampleApp iOS and Android
- [ ] Expo iOS and Android
---------
Co-authored-by: Oliver Lazoroski <oliver.lazoroski@gmail.com>
Co-authored-by: Alexey Alter-Pesotskiy <alex@testableapple.com>
Co-authored-by: semantic-release-bot <semantic-release-bot@martynus.net>
Co-authored-by: Artem Grintsevich <greenfrvr@gmail.com>
## π― Goal Fixes #3903 on V10. This is the V10 port of #3907. Linear ticket: https://linear.app/stream/issue/RN-471/offline-support-channels-hydrated-from-the-local-db-get-hasprev-false ## π Implementation details - Channels loaded from the offline DB can look fully loaded when the server still has older messages, so scrolling up never loads them. The offline DB returns at most 24 messages per channel and ignores the list's `message_limit`. Fixed in `selectMessagesForChannels` together with <LLC PR link>. The offline DB now returns the newest `message_limit` messages per channel (25 when not set), and the client reads that page against the same limit. - The offline DB can show messages with gaps between them. If messages 1-10 were cached in one session and 16-25 in a later one, they show up back to back with 11-15 missing. Fixed by the same change, since only the newest `message_limit` messages are read now. The other two fixes from #3907 aren't needed on V10. The lists call `loadMore()` directly, without per-length trackers that a failed request could leave stuck. `MessageFlashList` already loads older messages from its initial scroll event. ## π¨ UI Changes / ## π§ͺ Testing / ## βοΈ Checklist - [ ] I have signed the [Stream CLA](https://docs.google.com/forms/d/e/1FAIpQLScFKsKkAJI7mhCr7K9rEIOpqIDThrWxuvxnwUq2XkHyG154vQ/viewform) (required) - [ ] PR targets the `develop` branch - [ ] Documentation is updated - [ ] New code is tested in main example apps, including all possible scenarios - [ ] SampleApp iOS and Android - [ ] Expo iOS and Android
β¦tUser user keys (#3909) Docs-only fixes to `ai-docs/ai-migration-v9-to-v10.md`, found while migrating the stream-video-js RN dogfood app to `stream-chat-react-native@10.0.0-rc.2` (GetStream/stream-video-js#2559). - **Β§17.5:** pointed at `v9-to-v10-migration-guide-*.md` and the branch `claude/sdk-v10-breaking-changes-f64baf`. Neither exists any more; `release-v10`'s `ai-docs/` has only `v10-breaking-changes.md` and `v10-followups.md`. It now links that file on `release-v10`. - **`connectUser`:** the avatar field is `image`, which `UserAvatar` reads. An extra top-level key like `imageUrl` fails to compile only in an object literal, not in a user object built beforehand (checked with `stream-chat@10.0.0-rc.26`). Not in this PR: with Reanimated 4, a dev build logs `[Reanimated] Dependencies should only be used on the web` when a `<Channel>` mounts. It comes from `@gorhom/bottom-sheet@5.2.14`'s `useAnimatedLayout`, which passes deps to `useAnimatedReaction`, mounted by `AttachmentPicker`. I can open a separate issue for it. Ticket: https://linear.app/stream/issue/REACT-1474
Ticket: https://linear.app/stream/issue/REACT-1476/fix-odd-edge-cases-for-channel-list-recovery ## π― Goal A reconnect recovery re-hydrates every channel it gets back, and re-hydration replaces `channel.state.membership` with an equal copy. The channel preview selected the whole `membership` object only to read `pinned_at`, so every hydrated row re-rendered for it. ## π Implementation details `useChannelPreviewData` now selects `pinned` (`membership?.pinned_at != null`) instead of `membership`, so a row re-renders for pinning only when the pinned state changes. The hook returns the same values as before. Rows still re-render on a recovery, because `latestMessage` is also replaced with an equal copy on every re-hydration. That stays as it is, since our preview and integrators' custom previews can read any field of the message. On device, preview row renders per recovery stay at 86 with 120 channels loaded, so this change has no measurable effect on its own. ## π¨ UI Changes None. ## π§ͺ Testing - New `useChannelPreviewData` test: replacing `membership` with an equal copy does not re-render the hook, and pinning still updates it. The first test fails with the old selector. - `ChannelPreview` tests and `yarn test:typecheck` pass. - Checked on an Android emulator with a release build, together with the stream-chat-js recovery fixes. ## βοΈ Checklist - [x] I have signed the [Stream CLA](https://docs.google.com/forms/d/e/1FAIpQLScFKsKkAJI7mhCr7K9rEIOpqIDThrWxuvxnwUq2XkHyG154vQ/viewform) (required) - [ ] PR targets the `develop` branch - [ ] Documentation is updated - [ ] New code is tested in main example apps, including all possible scenarios - [ ] SampleApp iOS and Android - [ ] Expo iOS and Android
β¦ner-flicker fix: channel list footer flicker on pagination
β¦3915) Picks up the js-toolkit releases that ship explicit `.mjs`/`.cjs` builds targeting ES2022 (GetStream/js-toolkit#58). Their previous ESM build was `.js` in packages without `"type"`, which Node only loaded as ESM through syntax detection. - `package/`: `@stream-io/i18n` `^1.0.2` β `^1.1.0`, `@stream-io/state-store` `^1.1.6` β `^1.2.0`; SampleApp: state-store `^1.2.0` - Lockfile deduped to one copy each (`stream-chat`'s `^1.1.6` resolves to state-store 1.2.0; transitive `@stream-io/logger` 2.1.0) - `.yarnrc.yml`: preapprove the js-toolkit packages next to `stream-chat`, so `npmMinimalAgeGate` (3d) doesn't hold back first-party releases Metro (0.80+) indexes `.cjs`/`.mjs` by default (`watcher.additionalExts`), and the stream-video RN dogfood app bundles with these builds. Verified in a clean worktree: `yarn install --immutable`, `yarn build`, `yarn lint`, `yarn typecheck` pass. `yarn test:coverage`: 2333/2335; the two failures (`ChannelPreview` mark_read, `ownCapabilities` send-reply) pass when run on their own, with and without this change. π« Ticket: https://linear.app/stream/issue/REACT-1474
Tickets: - https://linear.app/stream/issue/REACT-1486 - https://linear.app/stream/issue/REACT-1487 - https://linear.app/stream/issue/REACT-1489 - https://linear.app/stream/issue/REACT-1490 ## π― Goal Fixes for the must-have V10 issues from the board triage. Each commit fixes one ticket. | Commit | Ticket | Fix | | --- | --- | --- | | [`a0354684c`](a035468) | [REACT-1486](https://linear.app/stream/issue/REACT-1486) | Regression test for an optimistic edit of an auto-translated message | | [`b5705f953`](b5705f9) | [REACT-1489](https://linear.app/stream/issue/REACT-1489) | `setupCommandUIMiddlewares` no longer inserts duplicate middleware | | [`b0d28db84`](b0d28db) | [REACT-1490](https://linear.app/stream/issue/REACT-1490) | A channel list loads its first page even when an event already added channels to it | | [`7a0376993`](7a03769) | [REACT-1487](https://linear.app/stream/issue/REACT-1487) | Reopening a channel or thread left on an older page shows the newest messages | ## π Implementation details ### REACT-1486 - optimistic edit of a translated message Commit: [`a0354684c`](a035468) The fix is in stream-chat-js (GetStream/stream-chat-js#1975). This commit adds the RN regression test, which edits an auto-translated message through the composer and checks the rendered row while the request is in flight. It fails until `stream-chat` is bumped to an rc that includes GetStream/stream-chat-js#1975. ### REACT-1489 - duplicate command middleware Commit: [`b5705f953`](b5705f9) `setupCommandUIMiddlewares` inserts four middleware but passed `unique: true` only on the first. The composer setup function runs again whenever the composer subscribes, which happens every time a channel opens, and on every `client.config` change. The draft command injection, the active command guard and the command string extraction piled up as a result. Each copy of the draft injection prepends the command, so a draft saved with an active command came out as `/giphy /giphy cats`. All four inserts now pass `unique: true`, so running the setup again replaces them. ### REACT-1490 - channel list stuck on channels an event added Commit: [`b0d28db84`](b0d28db) `ChannelManager.ingestChannel()` puts a channel into every matching paginator, including one that was never queried. `usePaginatedChannels` treated a list as unloaded only while `channels === undefined`, and `loadNextPage` refuses a paginator that was never queried. A list that mounted after an event reached its paginator showed only those channels until pull-to-refresh. That happens when the app opens a channel from a push notification and the user goes back to the list, or with a second list, such as archived channels, that mounts later. A paginator that was never queried now counts as unloaded too, so the list runs its first query. The offline DB seed and a failed first query both mark the paginator as queried, so neither causes an extra query. ### REACT-1487 - reopening a channel or thread on an older page Commit: [`7a0376993`](7a03769) The paginators of a channel and a thread outlive the screen. After a jump to a quoted, pinned or searched message, reopening the channel or thread showed that older page. `<Channel>` relied on `watch()` to go back to the newest messages, but `watch()` returns early for a channel that is already watched and loaded since GetStream/stream-chat-js#1943. v9 re-watched on open and landed on the newest messages. The new public `useJumpToLatestOnOpen()` hook calls `jumpToTheLatestMessage()` when a channel or thread opens on an older page. The newest page is normally still loaded, so this switches back locally without a request. It does nothing when a jump already landed or is in flight, so a deep link from the screen still lands on its target. It also does nothing when `<Channel>` is about to jump to the first unread message, or with `initializeOnMount={false}`. It runs in a layout effect so the old page isn't painted first, and its dependencies are stable, so it runs once per open. `ChannelShell` runs the hook from an empty `JumpToLatestOnOpen` component under its providers, the same way it renders `NotificationAnnouncer`. That covers `<Channel>`, `<Thread>` and any message list, including custom ones. `ChannelShell` takes `initializeOnMount` as an internal prop that `<Channel>` passes on. The hook is exported for apps that set `initializeOnMount={false}` and still want this behaviour. The `MessageList in a thread` test setup now seeds replies with `setItems()`, the way a thread seeds `latest_replies`. It used to write `items` straight into the paginator state, which leaves no intervals and looked like a list that isn't showing its newest page. ## π¨ UI Changes None. ## π§ͺ Testing - Running `setupCommandUIMiddlewares` three times on one composer leaves one of each middleware. Without the fix there are three. - A `ChannelList` whose paginator got a channel from `ingestChannel()` before it was queried runs its first query on mount and shows the queried page. Without the fix it never queries. - `Channel` tests cover reopening on an older page, a deep link that already landed, a jump in flight, `initializeOnMount={false}` and the first unread jump. `Thread` tests cover the first three. Without the fix, only the two reopening tests fail. - In SampleApp on iOS, reopening a channel after a search deep link and reopening a thread after jumping to a quoted reply both land on the newest messages, and the deep link still lands on its target. Opening threads normally triggers no jump and no request. - The full unit suite, `yarn test:typecheck`, `eslint`, `prettier` and `yarn build` pass, apart from the REACT-1486 test above. ## βοΈ Checklist - [x] I have signed the [Stream CLA](https://docs.google.com/forms/d/e/1FAIpQLScFKsKkAJI7mhCr7K9rEIOpqIDThrWxuvxnwUq2XkHyG154vQ/viewform) (required) - [ ] PR targets the `develop` branch - [ ] Documentation is updated - [ ] New code is tested in main example apps, including all possible scenarios - [ ] SampleApp iOS and Android - [ ] Expo iOS and Android
β¦ against V10 (#3918) Ticket: https://linear.app/stream/issue/REACT-1307 ## Goal Make the v9 β v10 guide the input of a prebuilt migration prompt: a coding agent gets one URL plus the user's repo and has to migrate accurately. The guide had grown by accretion, which hurt that: - "Part I" and "Β§19" each appeared twice; - Verify sat in the middle of the guide; - K.8 came before K.6; - detection greps hard-coded `src/`; - there was history from release candidates, and many bug-fix sections with nothing for an integrator to do. ## Changes **`ai-docs/ai-migration-v9-to-v10.md`: structure** - The order is now: 1. Title and verified line 2. Rules (old Β§0 folded in) 3. Companion documents 4. Workflow, definition of done and report template 5. Detection (`rg -t js -t ts -g '!**/node_modules/**'`, keyed on v9 names) 6. Quick reference (20 rows added) 7. A consolidated "Breaks that compile" table (~40 rows, each with a Detect command) 8. Parts AβS 9. Verification, with a leftover-identifier grep 10. An index mapping every `v10-breaking-changes.md` section to a guide section - IDs: - Verify is now an unnumbered `## Verification`, so every existing `Β§19` reference (all of them mean i18n) stays valid. - The duplicate Part I (i18n) is now Part S. - K.x sections are in order. - Every Β§ reference in `ai-docs/` resolves to a unique heading. - Sections with no integrator action are one-liners: L.8βL.10, M.1, M.3βM.5, M.7, P.10, P.14, P.16, P.18, P.19, P.21, P.22, Q.2. Their full text moved to section 17 of `v10-breaking-changes.md`. **Accuracy, re-verified at `74884ff55`** - #3916: a channel or thread reopened on an older page now jumps to the latest messages, and `useJumpToLatestOnOpen` is exported. Covered in Β§8, Β§R.5, the breaks table and the reference. - Wrong claim fixed: `<Chat>` does not replace an app's `statusReporter`; the app's reporter wins (Β§R.2). - Other fixes: - Β§16.3: capability gating; - `useMutedUsers()` returns `UserMuteResponse[]`; - the `typing` type; - `deleted_by` is removed; - `DEFAULT_BACKEND_SORT` is not exported; - a dead stream-chat-js link. - Names that only existed in release candidates are removed. Checked against the v9.11.1 / stream-chat 9.54.4 tags: - the `channel.state.*Store` handles and `mutedUsersStore`; - `WatcherState`, which was in Β§4's After snippet; - `MutedUsersState`, `normalizeTimestamp`, `getChannelUnreadState` and `*WithLocalUpdate`. - Remaining stale mentions fixed: `channel.deactivate()` and `client.activeChannels` in `channel-state-ui-migration.md`, and the `markRead` description in `v10-docs-impact.md`. - `ai-migration.md` (v8 β v9) has a banner pointing to this guide. ## How it was checked - Every identifier in the guide was checked against the SDK source, `stream-chat-js` `src/` and `@stream-io/i18n` / `state-store`. All 1,292 root exports were listed with the TS compiler, and every recommended hook is a root export. - About 40 v10 snippets typecheck against `package/src`. - The detection, breaks-table and leftover greps were run on the v9 and v10 SampleApps. ## Needs a decision - **Deep link vs reopen-at-latest.** If an app starts `jumpToMessage` in a screen-level `useLayoutEffect`, the SDK's jump to the latest messages may fire first. The SDK tests only cover a jump started before `<Channel>` mounts, so the guide recommends that order. Worth a test or a guard in the hook. - Phase 1 asks which pre-release to use when no stable 10.x exists. Check that this fits the prebuilt prompt during the RC period. ## Notes - The raw link to the stream-chat-js guide resolves once its PR is merged into `release-v10`. - The local `package/node_modules/stream-chat` is rc.26; `yarn install` brings it to rc.30. ## Related PRs The three guides link to each other; review together: - stream-chat-js: GetStream/stream-chat-js#1984 (merge first: the React and RN guides link to its raw URL) - stream-chat-react: GetStream/stream-chat-react#3377 - stream-chat-react-native: #3918
Tickets: - https://linear.app/stream/issue/REACT-1184 - https://linear.app/stream/issue/REACT-1400 ## π― Goal Bumps `stream-chat` to `10.0.0-rc.31` and removes the SDK code it makes redundant. rc.31 includes GetStream/stream-chat-js#1982, GetStream/stream-chat-js#1985, GetStream/stream-chat-js#1986 and GetStream/stream-chat-js#1987. | Commit | Ticket | Change | | --- | --- | --- | | [`328ecfb7e`](328ecfb) | [REACT-1184](https://linear.app/stream/issue/REACT-1184) | Remove `setupCommandUIMiddlewares` | | [`01671f45b`](01671f4) | [REACT-1400](https://linear.app/stream/issue/REACT-1400) | Pinned-message search uses the default text filter | | [`f34a0b01c`](f34a0b0) | | Bump `stream-chat` to `^10.0.0-rc.31` | ## π Implementation details ### REACT-1184 - remove `setupCommandUIMiddlewares` Commit: [`328ecfb7e`](328ecfb) GetStream/stream-chat-js#1985 installs the four command middlewares in every composer by default: the active command guard and the command string extraction around the commands middleware, and the command injection after attachments, for messages and for drafts. Those are the same middlewares, with the same ids and positions, that `setupCommandUIMiddlewares` inserted, so the helper has nothing left to do and is removed. SampleApp no longer calls it. This is a breaking change. Apps that call `setupCommandUIMiddlewares` delete the call. Apps that never called it now get the command behaviour too: typing `/giphy cats` makes `giphy` the active command and leaves `cats` in the input. `ai-docs/ai-migration-v9-to-v10.md` and `ai-docs/v10-breaking-changes.md` are updated, and the note about the REACT-1489 fix to this helper is dropped since the helper is gone. ### REACT-1400 - pinned-message search Commit: [`01671f45b`](01671f4) `MessageSearchSource` matched message text by exact equality, so `ChannelPinnedMessageListProvider` replaced that filter with its own `text: { $q }`. GetStream/stream-chat-js#1987 makes the default `text: { $autocomplete }`, so the override is removed. Pinned-message search now matches by word prefix: "lun" finds "Lunch tomorrow?". An empty query still lists every pinned message. ### Bump `stream-chat` to `10.0.0-rc.31` Commit: [`f34a0b01c`](f34a0b0) The SDK and the four example apps move to `^10.0.0-rc.31`. Besides the two changes above, it brings: - GetStream/stream-chat-js#1982 (REACT-1311): a send or retry the server already has is reported as sent instead of failed. - GetStream/stream-chat-js#1986 (REACT-1443): a channel list no longer empties when a later page only has channels another list owns. This only matters for apps that set an ownership resolver on `ChannelManager`. Neither needs a change here. ## π¨ UI Changes None. ## π§ͺ Testing - In SampleApp on iOS, typing `/giphy cats` shows the GIPHY chip with `cats` in the input, and sending it shows the Giphy preview. Picking Giphy from the commands menu and typing `dogs` does the same. - In SampleApp on iOS, pinned-message search finds "offline send check" for "offl" and "sen", and shows no results for "zzq". - The full unit suite, `yarn typecheck`, `yarn lint` and `yarn build` pass. ## βοΈ Checklist - [x] I have signed the [Stream CLA](https://docs.google.com/forms/d/e/1FAIpQLScFKsKkAJI7mhCr7K9rEIOpqIDThrWxuvxnwUq2XkHyG154vQ/viewform) (required) - [ ] PR targets the `develop` branch - [x] Documentation is updated - [ ] New code is tested in main example apps, including all possible scenarios - [ ] SampleApp iOS and Android - [ ] Expo iOS and Android
## π― Goal Fixes for issues QA found while porting the native e2e suites to `E2ETestApp`, and the RN side of the `stream-chat` changes going into rc.33. The js fixes from the same e2e run are in GetStream/stream-chat-js#1996. Tickets: [RN-493](https://linear.app/stream/issue/RN-493), [RN-488](https://linear.app/stream/issue/RN-488), [RN-492](https://linear.app/stream/issue/RN-492), [RN-494](https://linear.app/stream/issue/RN-494), [RN-502](https://linear.app/stream/issue/RN-502), [RN-490](https://linear.app/stream/issue/RN-490), [RN-487](https://linear.app/stream/issue/RN-487), [RN-481](https://linear.app/stream/issue/RN-481), [REACT-1380](https://linear.app/stream/issue/REACT-1380), [REACT-1360](https://linear.app/stream/issue/REACT-1360), [REACT-1326](https://linear.app/stream/issue/REACT-1326), [REACT-1515](https://linear.app/stream/issue/REACT-1515), [REACT-1363](https://linear.app/stream/issue/REACT-1363) ## π Implementation details One commit per change: - `426dab034` - [RN-493](https://linear.app/stream/issue/RN-493). The thread header shows the parent message's delivery status. - `ed4675e30`, `da2dd130f` - [RN-488](https://linear.app/stream/issue/RN-488). Sending a reply in a thread scrolls the thread to it, also when the thread is on an older page after a jump. A thread whose replies load after it opens scrolls to the newest reply. - `48167bee8` - [RN-492](https://linear.app/stream/issue/RN-492). Deleted messages show no delivery status. - `04b503acd` - [RN-494](https://linear.app/stream/issue/RN-494). The channel preview of a deleted message keeps the "You:" prefix or the sender's name, without the checkmarks. - `6b26091ff` - [RN-502](https://linear.app/stream/issue/RN-502). The unread indicator says "N unread messages", as on Android. - `7b8d0d97b` - [RN-490](https://linear.app/stream/issue/RN-490). The message menu offers "Reply" (quote) inside a thread. - `3c5e91f1f` - [RN-487](https://linear.app/stream/issue/RN-487). Pressing a quote of a reply that only lives in a thread opens that thread at the reply. The list's `goToMessage` decides this, so message rows get no new hooks. `goToMessage` takes the quoted message as an optional second argument, and the thread lookup that `SentToChannelHeader` already did moved into `useOpenThreadAtMessage`. - `a807673bc` - no ticket. Opening the media picker no longer crashes when the composer holds a video without a local file, such as a video from the message being edited or from a draft. `AudioAttachmentUploadPreview` gets the same guard. - `c35a597f2` - [RN-481](https://linear.app/stream/issue/RN-481). With read events off, the message list shows only the sending status, like the channel preview. Both read `channel.configState` `readEvents.enabled`. - `aafff47b5` - rc.33 compat: - GetStream/stream-chat-js#1991 ([REACT-1380](https://linear.app/stream/issue/REACT-1380)). Failed uploads are retried, and voice recordings uploaded, with `uploadFile`, and only while `isUploadEnabled` is true. - GetStream/stream-chat-js#1992 ([REACT-1360](https://linear.app/stream/issue/REACT-1360)). The tests no longer use `capabilities.changed`. - GetStream/stream-chat-js#1994 ([REACT-1326](https://linear.app/stream/issue/REACT-1326), [REACT-1515](https://linear.app/stream/issue/REACT-1515)). With connection recovery off, `<Channel>` doesn't mark the channel read on `connection.recovered`, because nothing was reloaded. - GetStream/stream-chat-js#1989 ([REACT-1363](https://linear.app/stream/issue/REACT-1363)). `DefaultCommandData` is removed. This is a breaking change and is written up in `ai-docs`. `stream-chat` is portaled to a local build of `release-v10` with GetStream/stream-chat-js#1996 in this branch, so CI fails until rc.33 is published. The rc.33 bump replaces the portal before this is merged. ## π¨ UI Changes No screenshots. ## π§ͺ Testing Each change has unit tests. RN-487 and RN-488 were also checked on an iOS simulator. The whole branch was checked against the local rc.33 build: unit tests, `test:typecheck` and `yarn lint` pass. ## βοΈ Checklist - [x] I have signed the [Stream CLA](https://docs.google.com/forms/d/e/1FAIpQLScFKsKkAJI7mhCr7K9rEIOpqIDThrWxuvxnwUq2XkHyG154vQ/viewform) (required) - [ ] PR targets the `develop` branch - [x] Documentation is updated - [ ] New code is tested in main example apps, including all possible scenarios - [ ] SampleApp iOS and Android - [ ] Expo iOS and Android
## π― Goal
<!-- Describe why we are making this change -->
## π Implementation details
<!-- Provide a description of the implementation -->
## π¨ UI Changes
<!-- Add relevant screenshots -->
<details>
<summary>iOS</summary>
<table>
<thead>
<tr>
<td>Before</td>
<td>After</td>
</tr>
</thead>
<tbody>
<tr>
<td>
<!--<img src="" /> -->
</td>
<td>
<!--<img src="" /> -->
</td>
</tr>
</tbody>
</table>
</details>
<details>
<summary>Android</summary>
<table>
<thead>
<tr>
<td>Before</td>
<td>After</td>
</tr>
</thead>
<tbody>
<tr>
<td>
<!--<img src="" /> -->
</td>
<td>
<!--<img src="" /> -->
</td>
</tr>
</tbody>
</table>
</details>
## π§ͺ Testing
<!-- Explain how this change can be tested (or why it can't be tested)
-->
## βοΈ Checklist
- [ ] I have signed the [Stream
CLA](https://docs.google.com/forms/d/e/1FAIpQLScFKsKkAJI7mhCr7K9rEIOpqIDThrWxuvxnwUq2XkHyG154vQ/viewform)
(required)
- [ ] PR targets the `develop` branch
- [ ] Documentation is updated
- [ ] New code is tested in main example apps, including all possible
scenarios
- [ ] SampleApp iOS and Android
- [ ] Expo iOS and Android
---------
Co-authored-by: semantic-release-bot <semantic-release-bot@martynus.net>
β¦merge Brings develop (9.12.1) into V10 ahead of the V10 -> develop merge. develop's changes since the last real sync (4.16.4, 2026-10-29) were already on V10 through the squashed consolidation in #3943, so every conflict resolves to V10's side: - #3927 (emoji avatar initials), #3937 (babel ternary fix): identical on V10 - #3917 (poll translations): ported to V10 with v10 i18n keys and types - #3945 (stream-chat ^9.55.0): not taken, V10 stays on ^10.0.0-rc.34 Taken from develop: the 9.12.1 / 4.16.5 version fields, src/version.json and both CHANGELOGs.
feat: stream-chat-react-native v10
Ticket: https://linear.app/stream/issue/RN-516 ## π― Goal `stream-chat` 10.0.0 is out on `latest`. Move the SDK and the example apps off the release candidate, so 10.0.0 of this SDK ships against the stable client. ## π Implementation details - `stream-chat`: `^10.0.0-rc.34` β `^10.0.0` in core (`package/package.json`) and in SampleApp, E2ETestApp, ExpoMessaging and ExpoChatGPTSample. The wrappers don't declare it. - `yarn.lock`: the one `stream-chat` entry now resolves to 10.0.0. Nothing else in the lockfile moved. - No code changes. ## π§ͺ Testing - `yarn build`, `yarn typecheck`, `yarn lint` and `yarn test:unit` (252 suites, 2385 tests) pass. ## βοΈ Checklist - [ ] I have signed the [Stream CLA](https://docs.google.com/forms/d/e/1FAIpQLScFKsKkAJI7mhCr7K9rEIOpqIDThrWxuvxnwUq2XkHyG154vQ/viewform) (required) - [x] PR targets the `develop` branch - [ ] Documentation is updated - [ ] New code is tested in main example apps, including all possible scenarios - [ ] SampleApp iOS and Android - [ ] Expo iOS and Android
oliverlaz
approved these changes
Nov 6, 2026
Contributor
SDK Size
|
Next releasesv10.0.010.0.0 (2026-11-06)β BREAKING CHANGES
It is also part 1/2 of a larger cleanup pass in SCRN, which will come in π Implementation detailsThe work is split into one commit per area. 1. Dead code (
|
Contributor
|
π This PR is included in version 10.0.0 π The release is available on:
Your semantic-release bot π¦π |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
π― Goal
π Implementation details
π¨ UI Changes
iOS
Android
π§ͺ Testing
βοΈ Checklist
developbranch