Skip to content

feat: stream-chat-react-native v10 - #3948

Merged
isekovanic merged 146 commits into
developfrom
chore/v10-release-merge
Nov 6, 2026
Merged

isekovanic merged 146 commits into
developfrom
chore/v10-release-merge

Conversation

@isekovanic

Copy link
Copy Markdown
Contributor

Ticket: https://linear.app/stream/issue/RN-516

🎯 Goal

Bring v10 into develop, so that the next develop β†’ main release cuts 10.0.0 on latest. v9 continues on the v9-release maintenance branch (#3947).

πŸ›  Implementation details

The branch is V10 (10a59a562) plus one merge of develop (24dacb7de, 9.12.1), so this PR merges into develop without conflicts.

The 26 conflicts in that merge all resolve to V10's side. 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 git still saw them as unmerged:

Taken from develop: the 9.12.1 / 4.16.5 version fields, src/version.json and both CHANGELOGs. Apart from those 7 files, the tree is identical to V10.

⚠️ How to merge

πŸ§ͺ Testing

  • The merged tree was diffed against V10 (only the 7 metadata files differ) and against develop (no conflicts).
  • yarn.lock is V10's unchanged: workspace versions resolve to 0.0.0-use.local, so the version fields don't touch it.
  • The code is V10's, so this PR's CI is the build, lint and test check.

β˜‘οΈ Checklist

  • I have signed the Stream CLA (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

isekovanic and others added 23 commits October 19, 2026 13:44
## 🎯 Goal

Adapts the SDK to the signature changes in
[GetStream/stream-chat-js#<PR>](GetStream/stream-chat-js#1920),
the stream-chat v10 prerelease fixes. The other fixes in that PR need no
change here as RN already reads typing from `channel.state` and the rest
are LLC-internal.

## πŸ›  Implementation details

- `useCreateMessageActionHandlers` calls `client.pinMessage(message)`;
it passed `null` before, which was the default anyway. It also calls
`client.moderation.flagMessage({ message_id: message.id })`. (0d7953e)
- `useChannelActions` calls `channel.addMembers({ members })` and
`channel.removeMembers({ members })`. (0d7953e)
- The 12 `pollComposer.updateFields(...)` calls in `components/Poll`
pass `{ data: ... }`. (dcbb7f6)

This needs the stream-chat release that contains the stream-chat-js PR.
Until `stream-chat` is bumped, `yarn test:typecheck` fails on these call
sites, because the installed rc still has the old signatures.

## 🎨 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
## 🎯 Goal

Tidy the code and doc comments in `package/src` (SDK source and
`ai-components`; tests and mock-builders are out of scope) so they
describe what the code does today, keep only what is non-obvious, and
stop contradicting the code. This is a **comment-only change: no
behavior change.**

## πŸ›  Implementation details

- **Present tense only.** Removed history ("previously", "no longer",
"used to", "formerly", "as in vX", migration notes), review and issue
IDs, section banners, ASCII diagrams, em-dashes and "deliberately /
explicitly" filler. Generic code stays generic.
- **Terser.** Comment lines in scope went from 6,666 to 4,600 across 893
files (436 files changed). Doc blocks of 13 or more lines went from 59
to 29.
- **Wrong comments fixed (about 120).** Every claim was checked against
the code. Examples: the SQLCipher note on `getOfflineDbEncryptionKey`
said a missing flag is "silently ignored" while the code throws
`SQLCIPHER_BUILD_MISSING`; `buildThumbnailGrid`'s examples were swapped;
`getDurationLabelFromDuration` said seconds but takes milliseconds;
`@returns` on the sqlite query helpers said `string` but they return
`[sql, params]`; the `language.*` names were credited to
`@stream-io/i18n` instead of `stream-chat`.
- **Public API docs.** Any exported function with a doc block now has
`@param` (dot notation for object fields), `@returns` when it returns a
value and `@throws` when it throws. Existing tags were kept. Internals
(non-exported, `private`, `@internal`) are prose only.
- **Commented-out code removed** in 7 files (`VictoryChart`, `SendUp`,
the vega-lite schema, `flatten-nodes`, `MessageComposer`, `GiphyChip`,
`theme`).
- **Never touched:** code, string literals, `TODO`/`FIXME` lines,
`eslint-disable` and `@ts-*` directives, the lines carrying
`@deprecated` / `@internal` / `@example` / `@default` / `@overrideType`,
and generated code.
- **Two files outside `package/src`:**
`package/{native,expo}-package/src/native/NativeAIDictation.ts` got the
same comment fix as
`src/ai-components/native-specs/NativeAIDictation.ts`, because
`validate-entry-isolation` requires the three copies to be identical.
- **Base:** cut from `V10` (not stacked on another PR). The PR targets
`V10`, not `develop`.

## 🎨 UI Changes

None.

## πŸ§ͺ Testing

How "comment-only" was verified:
- A script compares the token stream of every file in scope (types, JSX
text and string literals included, comments excluded) against `V10`. It
is identical for all files except three, which are expected consequences
of removing commented-out code: `VictoryChart.tsx` and
`vega-lite/schema.ts` (prettier collapsed the `victory-native` import
and the `MarkName` enum onto one line, dropping a trailing comma) and
`SendUp.tsx` (the empty JSX expression `{}` that held a commented-out
`<Circle />`).
- The same script checks that non-comment lines, `TODO`/`FIXME` lines
and directive counts are identical. It was validated with negative
controls (changed string, type, operator, deleted line, stray
whitespace, JSX text, edited `TODO`, renamed `@deprecated`, reworded
`@ts-*`) that it flags.
- `yarn lint`, `yarn build`, `yarn typecheck` and `yarn test:unit`
(2,209 tests) pass.

What reviewers should look for:
- Comments that are still wrong. Claims that could not be verified
against the repo were kept trimmed rather than invented: the Expo audio
defaults in `types.ts`, the React Native 0.87 and TS2589 / TS2883 notes,
the iOS `UITextView` caching note, the FlashList
`onViewableItemsChanged` note and the Android pinch-to-pan handoff.
- A "why" that should have stayed (a timing, ordering or platform
constraint whose removal could cause a bug).
- Public docs that are now too terse, or still too long: `Streami18n`,
`i18n/runtimeDefaults.ts` and the `Chat` props are the largest remaining
blocks.

Not changed here (follow-ups): `@default` tags that disagree with the
code (`attachmentPickerBottomSheetHeight`,
`attachmentSelectionBarHeight`, `allowConcurrentAudioPlayback`),
`@example ./X.md` pointers to files that do not exist, and the code
issues noticed while reading.

## β˜‘οΈ Checklist

- [ ] I have signed the [Stream
CLA](https://docs.google.com/forms/d/e/1FAIpQLScFKsKkAJI7mhCr7K9rEIOpqIDThrWxuvxnwUq2XkHyG154vQ/viewform)
(required)
- [ ] PR targets the `develop` branch (targets `V10`, see above)
- [ ] Documentation is updated
- [ ] New code is tested in main example apps, including all possible
scenarios (comment-only change)
  - [ ] SampleApp iOS and Android
  - [ ] Expo iOS and Android
## 🎯 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.
Comment thread perf/gauntlet/actor.cjs Dismissed
@Stream-SDK-Bot

Copy link
Copy Markdown
Contributor

SDK Size

title develop branch diff status
js_bundle_size 2041 KB 2026 KB -15398 B πŸš€
ai_components_size 163 KB 163 KB -16 B πŸš€

@isekovanic
isekovanic merged commit 30529ad into develop Nov 6, 2026
10 of 12 checks passed
@isekovanic
isekovanic deleted the chore/v10-release-merge branch November 6, 2026 14:02
@stream-ci-bot

Copy link
Copy Markdown
Contributor

πŸŽ‰ This PR is included in version 10.0.0 πŸŽ‰

The release is available on:

Your semantic-release bot πŸ“¦πŸš€

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants