Merged
Conversation
This PR was created automatically by CI. Co-authored-by: Ivan Sekovanikj <31964049+isekovanic@users.noreply.github.com> Co-authored-by: Stream Bot <runner@runnervmh13bl.b2snqolw0euelb3mofnhwwxadc.bx.internal.cloudapp.net>
β¦change (#3319) This PR removes the deprecate API after the centralized audio change: - useAudioPlayerControl API changes to useAudioControl api - useAudioController is changed to useAudioRecorder hook as it just handles the audio recording part henceforth. - Removed the props with @deprecated label
β¦el unread state (#3318) Removed deprecated stuff from the message list improvement PR
Add optional support for the package react-native-keyboard-controller so as to make the keyboard interactions smoother in the SDK.
## π― Goal Resolves [this linear issue](https://linear.app/stream/issue/RN-328/message-context-menu). ## π 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
## π― Goal I unfortunately noticed today that there was a serious concurrency issue introduced on Android with [this PR](#3339), where the bottom sheet content would simply not load 90% of the time. The issue was that the animation got cancelled in-flight and so the animation callback of `withTiming` fired with `finished: false` every time, causing the content to never really be shown. These changes should address that now. I also introduced optionality for the lazy loading so that the sheet can properly be reused in the future. ## π 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
# π Changelog β Message Composer & Input Refactor ## β¨ Design & UX Improvements - Introduced a **new unified icon set** across the Message Composer. - Added **smoother and more contextual animations** throughout the component. - Message Composer now internally handles **bottom spacing**, removing the need for extra padding in the sample appβs `ChannelScreen`. - Added a new **Edit button** with a tick icon for clearer editing actions. - Reply UI received **theming updates** due to internal component refactor. --- ## β¬οΈ Scroll to Bottom Enhancements - Refactored **Scroll to Bottom button** with: - Dedicated wrapper - Configurable `chevronColor` - Touchable support - Button visibility logic improved: - Now appears when scroll distance **exceeds the composer height** - Replaces the previous hardcoded `150px` threshold --- ## π§± Architectural & State Improvements - Message Composer height is now stored in a **detached internal state**. - Introduced `messageInputFloating` **config prop at the Channel level**. - Introduced **OutputButtons** as a new dedicated component to manage: - Send - Cooldown - Edit - Audio recording buttons --- ## π¨ Theme Updates ### MessageList **New theme properties:** - `scrollToBottomButtonContainer` - `stickyHeaderContainer` - `unreadMessagesNotificationContainer` --- ### Removed / Deprecated Themes - `audioRecordingButton` theme is no longer relevant. - **SendButton** theme props removed: - `sendUpIcon` - `sendRightIcon` - `searchIcon` - `CooldownTimer.container` theme removed. - **InputButtons** - `MoreOptionsButton` - `CommandsButton` are no longer used. --- ## π Attachment & Preview Changes ### ImageAttachmentUploadPreview - `itemContainer` theme removed. - New unified `container` theme introduced. ### AttachmentUploadListPreview - Removed: - `imagesFlatList` - `filesFlatList` - `wrapper` - Now uses: - Single unified `flatList` - `itemSeparator` as the only theme prop. ### FileAttachmentUploadPreview - `wrapper` theme removed. - `flatListWidth` prop removed. - Title rendering logic simplified and made more consistent. --- ## β¨οΈ AutoComplete & Cooldown Updates - **AutoCompleteInput** - `coolDownActive` β `cooldownRemainingSeconds` - **CooldownTimer** - `container` theme removed. --- ## βοΈ Removed Components The following components are no longer part of the Message Input flow: - `InputEditingStateHeader` - `InputReplyStateHeader` - `CommandButton` - `MoreOptionsButton` --- ## π§© MessageInput β Breaking Changes ### β Removed Props - `InputEditingStateHeader` - `InputReplyStateHeader` - `StopMessageStreamingButton` - `SendButton` - `CooldownTimer` - `channel` ### β Added Props - `isKeyboardVisible` - `hasAttachments` --- ## π¨ MessageInput Theme Changes ### Removed Theme Keys - `editingBoxContainer` - `editingBoxHeader` - `editingBoxHeaderTitle` - `editingStateHeader.editingBoxHeader` - `editingStateHeader.editingBoxHeaderTitle` - `imageUploadPreview.flatList` - `moreOptionsButton` - `autoCompleteInputContainer` - `optionsContainer` - `composerContainer` - `inputBox` ### Added Theme Keys - `wrapper` - `contentContainer` - `inputBoxWrapper` - `inputButtonsContainer` - `inputContainer` - `inputFloatingContainer` - `floatingWrapper` - `editButton` - `cooldownButtonContainer` - `outputButtonsContainer` --- ##β οΈ Migration Notes - Custom themes targeting removed keys will need updates. - Remove manual bottom padding from `ChannelScreen`. - Update `AutoCompleteInput` usage to `cooldownRemainingSeconds`. - Consumers using removed MessageInput props must migrate to the new API. ---
This pull request refactors the image gallery to simplify state management and remove legacy props and logic related to image gallery behavior. The main improvement is the introduction of a centralized `imageGalleryStateStore`, which replaces the previous pattern of passing multiple state setters and props. Additionally, the video is now managed by a pool and a player class same as audio. This is for easy state management and ease of usage. Additionally, the dependency on `@gorhom/bottom-sheet` is updated, and related code is streamlined to use the new approach. **Refactoring and State Management Improvements:** * Replaced legacy props (`setMessages`, `setSelectedMessage`, `legacyImageViewerSwipeBehaviour`) in `Gallery`, `Giphy`, and related components with a single `imageGalleryStateStore` prop, centralizing image gallery state handling. [[1]](diffhunk://#diff-e7d0e211073121199347eedf392123d964951dbd5ebbda17d1f86c921ec39782L39-R39) [[2]](diffhunk://#diff-e7d0e211073121199347eedf392123d964951dbd5ebbda17d1f86c921ec39782R51-L58) [[3]](diffhunk://#diff-e7d0e211073121199347eedf392123d964951dbd5ebbda17d1f86c921ec39782L68-L80) [[4]](diffhunk://#diff-e7d0e211073121199347eedf392123d964951dbd5ebbda17d1f86c921ec39782R73-L101) [[5]](diffhunk://#diff-e7d0e211073121199347eedf392123d964951dbd5ebbda17d1f86c921ec39782R189-L213) [[6]](diffhunk://#diff-e7d0e211073121199347eedf392123d964951dbd5ebbda17d1f86c921ec39782L222-L224) [[7]](diffhunk://#diff-e7d0e211073121199347eedf392123d964951dbd5ebbda17d1f86c921ec39782L255-L274) [[8]](diffhunk://#diff-e7d0e211073121199347eedf392123d964951dbd5ebbda17d1f86c921ec39782L283-L285) [[9]](diffhunk://#diff-e7d0e211073121199347eedf392123d964951dbd5ebbda17d1f86c921ec39782L307-R288) [[10]](diffhunk://#diff-e7d0e211073121199347eedf392123d964951dbd5ebbda17d1f86c921ec39782L588-R564) [[11]](diffhunk://#diff-e7d0e211073121199347eedf392123d964951dbd5ebbda17d1f86c921ec39782L612) [[12]](diffhunk://#diff-e7d0e211073121199347eedf392123d964951dbd5ebbda17d1f86c921ec39782L634) [[13]](diffhunk://#diff-e7d0e211073121199347eedf392123d964951dbd5ebbda17d1f86c921ec39782R620-L665) [[14]](diffhunk://#diff-9175eb9fcc09f05267618eca638f8502019ed92e3751d38c2771d7083903ab5bL135-R135) [[15]](diffhunk://#diff-9175eb9fcc09f05267618eca638f8502019ed92e3751d38c2771d7083903ab5bR164) [[16]](diffhunk://#diff-9175eb9fcc09f05267618eca638f8502019ed92e3751d38c2771d7083903ab5bL175-L177) [[17]](diffhunk://#diff-9175eb9fcc09f05267618eca638f8502019ed92e3751d38c2771d7083903ab5bL212-R211) [[18]](diffhunk://#diff-9175eb9fcc09f05267618eca638f8502019ed92e3751d38c2771d7083903ab5bL455-R453) [[19]](diffhunk://#diff-9175eb9fcc09f05267618eca638f8502019ed92e3751d38c2771d7083903ab5bR471) [[20]](diffhunk://#diff-9175eb9fcc09f05267618eca638f8502019ed92e3751d38c2771d7083903ab5bL481-L483) * Updated the logic for opening the image viewer and Giphy attachments to use `imageGalleryStateStore.openImageGallery`, removing conditional logic for legacy behavior. [[1]](diffhunk://#diff-e7d0e211073121199347eedf392123d964951dbd5ebbda17d1f86c921ec39782L307-R288) [[2]](diffhunk://#diff-9175eb9fcc09f05267618eca638f8502019ed92e3751d38c2771d7083903ab5bL212-R211) * Removed all references to `legacyImageViewerSwipeBehaviour` from the codebase, including context, props, and hooks. [[1]](diffhunk://#diff-f7139f4cdb523365cfc277d72b827a3432325b9c6460cf14628f9df67d0e4d85L346) [[2]](diffhunk://#diff-f7139f4cdb523365cfc277d72b827a3432325b9c6460cf14628f9df67d0e4d85L664) [[3]](diffhunk://#diff-f7139f4cdb523365cfc277d72b827a3432325b9c6460cf14628f9df67d0e4d85L1945) [[4]](diffhunk://#diff-d3e4f4cdcef10807a38eab86f6ead6bbfe01980e7326f3cadfeb8186760d7af1L55) [[5]](diffhunk://#diff-d3e4f4cdcef10807a38eab86f6ead6bbfe01980e7326f3cadfeb8186760d7af1L173) **Dependency and Import Updates:** * Upgraded `@gorhom/bottom-sheet` dependency from version 5.1.8 to 5.2.8. * Updated `ImageGallery.tsx` to use the new `imageGalleryStateStore` and removed unused imports and legacy bottom sheet modal code. [[1]](diffhunk://#diff-b6da04367e722c0873941022e180cec3e33f89445cb2427ecaa78c2040037e5aL1-R16) [[2]](diffhunk://#diff-b6da04367e722c0873941022e180cec3e33f89445cb2427ecaa78c2040037e5aL39-L52) [[3]](diffhunk://#diff-b6da04367e722c0873941022e180cec3e33f89445cb2427ecaa78c2040037e5aR99-R128) These changes modernize and simplify the gallery and attachment components, making state management more robust and maintainable.
#3334) The following are the changes in the PR: - The android package name for sample app is changed to `io.getstream.reactnative.sampleapp` as it was `com.sampleapp` before. Doesn't make any sense. - Added lanes for android firebase build and upload. - Improved the ios lanes for tesflight build and upload. Our first firebase deployment build after a long time(lane was run manually locally): <img width="1008" height="891" alt="Screenshot 2025-12-18 at 1 48 03β―PM" src="https://github.com/user-attachments/assets/90710664-1ce3-4ddc-84e1-d9eb52b0d03b" />
## π― Goal
This PR fixes an unfortunate issue with state not being properly
synchronized in certain situations (typically on lower end devices),
where the updates of our `processedMessageList` would be ahead of the
updates of the previous and next `message` store.
Even thought there's a dependency on `processedMessageList`, since
`renderItem` is already memoized and the `message` references are stable
from the work done on the `MessageList` the past few months, this does
not in fact degrade performance while we focus on correctness.
## π 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
## π― 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
## π― Goal This PR is the RN SDK counterpart of [this change](GetStream/stream-chat-js#1716). In addition to the description there, it also: - Includes optimistic updates for updating a message - Makes sure the DB is up to date ## π 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
## π― Goal
This PR includes a batch of follow-up fixes on top of the redesign
branch, focused on sample app UX regressions, channel list actions, and
Android behaviour.
On the platform side, it improves Android behaviour by removing the old
IME hack from keyboard avoiding logic and makes our
`KeyboardCompatibleView` compatible with ETE on `Android` π . It also
rounds out the redesign polish by making filters toggleable and
reflecting the related behaviour in sample app screens.
Included changes
- enable audio recording in threads
- use the correct outlined video icon
- hide read indicators for deleted last messages
- fix back navigation in channel creation screens
- fix Android keyboard avoiding without the IME hack
- remove mark unread for own messages (this was never working well)
- make filters toggleable
- reflect filter behavior in sample app screens
- add muted icon/state to details bottom sheet
- make swipe actions always use mute
## π 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
## π― Goal
This should be the final batch of design token and icon changes before
we freeze them before the V9 release.
## π 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
## π― Goal
This PR addresses a very weird issue that could be noticed in some apps,
where `onLayout` was not being invoked by the child view wrapper at all,
making it impossible to remeasure translating views while the context
menu is open (for example the keyboard closing).
To combat this, we add one more measurement vector through invoking the
sync function when we begin closing the context menu as well. Since this
runs asynchronously and is controlled through shared values, the
overhead is almost non-existent and we make sure that we measure as late
as possible.
## π 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
## π― Goal
## Summary
This PR upgrades `examples/SampleApp` to React Native `0.81.6`, fixes
the Metro setup needed for that upgrade, and addresses the two main
animation regressions that showed up while validating the SDK against
newer React Native / Reanimated behavior:
- message overlay / context menu flicker during layout correction
- the context menu completely falling apart on bottom sheet open
- bottom sheet hard swipe close flicker
A big part of this work was understanding the impact of
`react-native-reanimated@4.3.0`.
`4.3.0` turned out to be a meaningful runtime change for this codebase,
not just a routine version bump. In practice it changes the behavior of
some animation paths that were previously stable on `4.2.3`, especially
around gesture driven transitions, overlay (modal specifically) post
layout settle corrections and UI that depends on tight synchronization
between layout and animated values.
During (yet another weekend) debugging, the `reanimated` static feature
flags were part of the culprit. Namely, `4.3.0` enables some of the
optional feature flags to be `true` by default. This unfortunately
changes things in many ways:
- `FORCE_REACT_RENDER_FOR_SETTLED_ANIMATIONS` was the strongest flag
level signal, as it directly messes with renders
- enabling it on the older good baseline (`4.2.3`) was enough to
reproduce the problematic behaviour
- `4.3.0` still had regressions even with
`FORCE_REACT_RENDER_FOR_SETTLED_ANIMATIONS=false`, so the upgrade issue
is broader than a single flag, but this flag was a major clue during the
investigation
PS: The thing is breaks is opening the bottom sheet to full screen,
while it has specifically a child `FlatList` that will change its
content container size when opened to the highest snap point. This is
noticeable immediately in the `emoji` picker (since it has a lot of
items), but it will be the case for any of the sheets with enough
content. Essentially, something in the commit flow of shared values
(when the feature flag is enabled) changes the way other components
behave when mounting new styles (i.e components with new animated
styles) and all of this happening while other animations are going on.
In the case of the `FlatList`, it's simply virtualization kicking in and
destroying other styles entirely while there is an animation going on
and items are being rendered. It boggles my mind that would affect the
content below it and I was not able to find the culprit (yet), but for
now we'll recommend to integrators that they do not use this feature
flag specifically until either I open an issue /PR in the `reanimated`
upstream or I find a good way to refactor our code so that this does not
happen anymore. Needless to say, these issues are ridiculously difficult
to debug and even more difficult to reproduce efficiently.
## Changes:
### SampleApp upgrade
- Upgraded `examples/SampleApp` to React Native `0.81.6`
- Updated the aligned native/runtime dependencies needed for the RN 0.81
baseline
- Fixed the SampleApp Metro config so the app can boot correctly on the
upgraded stack
### Message overlay / context menu
- Refactored `MessageOverlayHostLayer` so overlay pieces no longer split
their vertical position across absolute `top` and animated `translateY`
- Each overlay layer now animates a single corrected Y value and applies
it through absolute positioning
- This was the real kicker on `4.3.0`, as having multiple animated
styles and expecting them to play nice with each other is out of
question
This fixes the visible one frame message/context-menu flicker that could
happen during open time layout corrections, especially on newer
Reanimated versions. This was not an issue with `4.3.0` specifically
either.
### Bottom sheet
- Kept the sheet on the transform based motion path, but with a huge
overhaul (almost a rewrite), to facilitate animating through a logical
state machine that does not imply the need to `cancelAnimation`
- Fixed the hard swipe-to-close flicker by separating gesture
finalization from the close timing animation by one frame (even though
the shared values were kept intact, the animation would very briefly
restart on hard close and moving this orchestration to the JS stack
helps tremendously as we no longer depend layout commit topology on the
`reanimated` stack to make sure that we are doing stuff right)
- Kept keyboard offset composed into the final sheet transform instead
of letting keyboard handling fight the sheet motion path
- Fixed the bottom sheet opening for only 2-3 frames and then freezing,
often not accepting gestures either
- This was fixed by automation with the `cancelAnimation` fixes, but the
deal is that now the order of operations within `reanimated` has changed
and also having unsafe behaviour like we did really messes with the new
`SharedValue` architecture they've included in `4.3.0`
This fixes the brief top-snap flash that could happen when closing the
sheet with a fast downward gesture.
Most of the work here was not about changing product behaviour
intentionally, but about making the SDKβs animation model more robust
against newer React Native/`reanimated` timing and commit behaviour.
The two concrete fixes were:
- stop splitting overlay Y position across multiple visual channels
- stop handing bottom-sheet gesture motion directly into close timing in
the same frame
The changes have been extensively tested on both `4.3.0` and
pre-`4.3.0`. This is of course with
`FORCE_REACT_RENDER_FOR_SETTLED_ANIMATIONS` disabled, as I have no clue
why it breaks stuff so much. More thorough investigation into that will
definitely be done in the future.
## π 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
## π― Goal Add a `CLAUDE.md` file so that Claude Code (claude.ai/code) has the context it needs to work productively in this repo without lengthy exploration each session. ## π Implementation details Single new file at repo root covering: - **Common commands** β build, lint, test (including single-test), install, sample app - **Architecture** β package structure, component hierarchy, source directory map - **Key patterns** β component override props, context three-layer pattern, native module abstraction, state stores, memoization strategy - **Testing patterns** β provider wrapping, mock builders and generators - **Theme system** β token tiers, topological resolution, generated platform tokens, mergeThemes - **Native / Expo relationship** β thin wrapper pattern, runtime platform branching - **Chat root provider** β client setup, subscription lifecycle, provider stack - **Offline DB** β schema location, version-based reinit - **Translations** β validation rules, build-translations sync - **Conventions** β commits, linting, formatting, branching, shared native sync ## π¨ UI Changes N/A β documentation only. ## π§ͺ Testing No code changes; no tests needed. ## βοΈ Checklist - [x] 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 - [x] New code is tested in main example apps, including all possible scenarios - [x] SampleApp iOS and Android - [x] Expo iOS and Android
## π― Goal This PR is a port of [this change](#3534) to V9. ## π 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
## π― 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
## π― Goal
This PR fixes an Android timing bug in the message edit flow when edit
is triggered from the message overlay that caused the keyboard to be
immediately closed afterwards. It also fixes severe visual stuttering
when changing the state to editing within the `MessageComposer` on both
platforms.
The bug was caused by the edit action focusing the message input before
`PortalWhileClosingView` had fully returned the teleported composer back
into its normal tree. On Android, that caused the `TextInput` to lose
focus mid keyboard animation, which immediately closed the keyboard
again.
The stuttering part is caused by the fact that the keyboard opening and
the state changing to editing essentially animate the layout of the same
component, but through 2 different state values - often offset by a
single frame. This means that it is very realistic for the
`MessageComposer` to animate its layout height, bottom padding and other
layout props all offset from each other by a frame, making the
animations feel wonky.
The fix separates the problem into two explicit stages:
1. wait for portal close handoff to settle
2. wait for the keyboard to be open before running the edit callback
This preserves the original edit behavior while making the focus timing
safe.
When long pressing a message and tapping `Edit`, the expected behaviour
is:
1. close the overlay
2. focus the composer input
3. open the keyboard
4. enter edit mode
On Android, that flow was unstable when the overlay was still closing
through PortalWhileClosingView.
What was actually happening:
1. the overlay started closing
2. the composer was still teleported into the overlay host
3. the edit action focused the input too early
4. `react-native-teleport` moved the composer back into the normal tree
after focus had already been applied
5. the input lost focus during that handoff
6. the keyboard started opening and then immediately closed again
This showed up much more clearly in release builds than in debug builds,
because of the fact that no animation frames are actually skipped. In
other words, the invocation of the scheduled actions to execute after
the context menu closes happens way too fast in the React lifecycle.
So, the fix for both of these things looks something like this:
- Immediate focus after overlay close and only invoke edit after the
keyboard has been opened (if applicable)
- Waiting for two `requestAnimationFrames` worked reliably:
- one frame for the portal retarget / React commit
- one additional frame for the native view hierarchy and `TextInput`
host to settle
We also considered solving this directly in the overlay queue, but the
committed fix keeps the behavior local to the edit action instead.
In the future, if we figure that 2 RAFs are not enough (on `120mhz`
phones for example), there is an alternative solution that can introduce
a relatively complex registry on the `PortalWhileClosingView` layer that
waits for true layout settle before doing any teleport sensitive actions
(like focusing an input). I've yet to reproduce a case where they aren't
enough, though - so let's keep it simple for now.
## π 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
## π― Goal
This PR resolves a very long standing race condition that has evaded me
for quite a while.
Whenever we queue up send message requests while we're offline, they are
to be executed when we regain connection. However, the UI SDK takes care
of failed messages in the sense of re-adding them in case they weren't
resolved or saved in the LLC. This is done so that when a resync happens
of the channel, the state is preserved and the failed messages do not
disappear.
However, since our state layer is not LLC only fully, we have 2 vectors
of resyncing the SDK:
- One that comes from the LLC (i.e `channel.watch()` being called)
- One that comes from the SDK itself (re-adding those failed messages)
When `send-message` pending tasks are executed, the LLC takes care of
upserting the state immediately so that the UI SDK can simply consume
it. This is fine, except for the fact that for the UI SDK to also
resolve its own state, we rely on WS events (which might be a bit late,
especially for the last 1-2 messages, which is when this bug would
mostly happen).
So the flow would look something like this:
- We regain connection
- Pending tasks are executed, `channel.state.messages` are updated (from
the pending task execution)
- After this, the `onSyncStatusChange` callback is invoked
- The `channel` is actually resynced
- `channel.watch()` is called
- We also upsert all failed messages once again
- For any `message` for which we did not receive a WS event of the
successful pending task execution on time, both the failed `message` and
the actual (serverside) one appear in the list
To fix this, we deem messages eligible for recovery if they:
- are `FAILED`
- are not already present in `channel.state`
This makes sure that the race condition never happens when the state is
already up to date. If it's not, we anyway have to do it.
## π 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
## π― Goal
This PR stabilizes attachment picker and keyboard behavior on both
Android and iOS by simplifying the picker open/close flow and removing
timing based workarounds.
The main changes are:
- removed the delayed keyboard close/open coordination path
- removed `forceClose()` and the extra delayed `close()` fallback
- kept picker state changes aligned with bottom sheet state
- added a small handoff lock to prevent rapid picker/keyboard toggles
from desynchronizing the composer and picker
Net result: picker/keyboard transitions are more reliable, the Android
flicker/stuck states are resolved, and rapid toggling no longer leaves
the composer and attachment picker out of sync.
This is now finally stable and correct on Android.
## π 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
## π― Goal This PR fixes an Android crash in message rendering caused by the Unicode object replacement character (`U+FFFC`) reaching the markdown pipeline. The fix sanitizes message text before markdown generation by normalizing `U+FFFC` to whitespace and treating fully empty results as empty content. Should resolve [this issue](https://getstream.zendesk.com/agent/tickets/79676?brand_id=360003144254). ## π 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
## π― 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
## π― 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
## π― Goal
This PR refines dynamic bottom sheet sizing and cleans up a few related
UI issues in the SDK and `SampleApp`.
- Adds content driven initial sizing to `BottomSheetModal` while keeping
the top snap available (this is toggleable by a flag and will assume
`height` as the maximum height the sheet can grow to through dynamic
sizing)
- Moves dynamic sizing calculations to shared values
- Adds test coverage for the new bottom sheet snap point utilities
- Fixes channel preview spacing for very long channel names
- Hides quoted replies for attachment action previews such as ephemeral
`Giphys`
- Preserves the correct standalone message shell styling for ephemeral
`Giphy` previews
- Moves `hasAttachmentActions` into `MessageContext` so it is computed
once and reused downstream
On the `SampleApp` side, it fixes a few visual inconsistencies:
- network/loading and DM button backgrounds now match the header
- grouped `ChannelAvatar` now respects the requested size
- group channel details now actually request `2xl`
- `GoBack` and `GoForward` chevrons now mirror internally in `RTL` as an
SVG PoC
## π 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
## π― Goal This PR takes care of some general housekeeping for the incoming V9. Namely, we: - Bump back the `bottom-sheet` library's version to latest, as it addresses the issues we had before - It also cancels the need of a monkey patch any longer, so we can avoid doing that as well - Converts all `StyleSheet.absoluteFillObject` entries to `StyleSheet.absoluteFill` - We need to do this due to [this breaking change](https://reactnative.dev/blog/2026/04/07/react-native-0.85#stylesheetabsolutefillobject-removed) for RN `0.85` - Destructuring no longer works, so we flatten those styles - Direct usage is still allowed - Where not directly applicable, we create our own `absoluteFillObject`s and use those (not the cleanest but it'll do for now) With this, we should be RN `0.85` compatible as well. ## π 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
## π― 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
## π― Goal RTL mode fixes from [QA test](https://www.notion.so/stream-wiki/RN-QA-New-Design-30d6a5d7f9f680d29ba1f9eadfcc787e) ## π Implementation details ### 1. Mirror icons based on [Figma](https://www.figma.com/design/Us73erK1xFNcB5EH3hyq6Y/Chat-SDK-Design-System?node-id=13696-41010&t=9ujyMjzsjIngT6jR-0) All icons from this table should be mirrored after this PR is merged: | Old name | New name | src | already mirrored | |----------|----------|-----|------------------| | `IconArrowBoxLeft` | `leave` | `package/src/icons/leave.tsx` (`ArrowBoxLeft`) | no | | `IconArrowLeft` | `arrow-left` | `examples/SampleApp/src/icons/GoBack.tsx` (`GoBack`) β back chevron; in core, closest directional stroke icon is `package/src/icons/chevron-left.tsx` (`ChevronLeft`) | yes (SampleApp `GoBack` only) | | `IconArrowRight` | `arrow-right` | `examples/SampleApp/src/icons/RightArrow.tsx` (`RightArrow`) β arrow path flipped with a **fixed** `matrix(-1 β¦)`; optional chevron-style: `examples/SampleApp/src/icons/GoForward.tsx` (`GoForward`) | no for `RightArrow`; yes for `GoForward` | | `IconArrowUpRight` | `arrow-left` | `package/src/icons/arrow-up-right.tsx` (`ArrowUpRight`) | no | | `IconBubbles` | `message-bubbles` | No `message-bubbles` file. Closest in SampleApp: `examples/SampleApp/src/icons/ChatsTab.tsx` (overlapping chat bubbles). In core, only single-outline bubble: `package/src/icons/message-bubble.tsx` (`MessageBubbleEmpty`) | no | | `IconBubbleText6ChatMessage` | `thread` | `package/src/icons/thread.tsx` (`ThreadReply`) | no | | `IconBubbleText6Solid` | `thread-fill` | *(none β no filled thread / `thread-fill` icon in these folders)* | β | | `IconBubble2` | `message-bubble` | `package/src/icons/message-bubble.tsx` (`MessageBubbleEmpty`) | no | | `IconBubble2Solid` | `message-bubble-fill` | *(none β no filled `message-bubble` variant in these folders)* | β | | `IconChevronLeft` | `chevron-left` | `package/src/icons/chevron-left.tsx` (`ChevronLeft`) β not re-exported from `package/src/icons/index.ts` but used via direct import | no | | `IconChevronRight` | `chevron-right` | No `chevron-right` in core icons. `examples/SampleApp/src/icons/GoForward.tsx` (`GoForward`) matches chevron-right behavior | yes | | `IconArrowShareLeft` | `reply` | `package/src/icons/reply.tsx` (`CurveLineLeftUp` / alias `ArrowShareLeft`) | no | | `IconLayoutLeft` | `sidebar` | *(none β no sidebar / layout-left icon in these folders)* | β | | `IconMagnifyingGlassSearch` | `search` | `package/src/icons/search.tsx` (`Search`); SampleApp duplicate: `examples/SampleApp/src/icons/Search.tsx` | no | | `IconMinusSmall` | `mute` | `package/src/icons/mute.tsx` (`Mute`); SampleApp: `examples/SampleApp/src/icons/Mute.tsx` | no | | `IconPaperPlane` | `send` | `package/src/icons/send.tsx` (`SendRight`) | no | | `IconVideo` | `video` | `package/src/icons/video.tsx` (`VideoIcon`) | no | | `IconVideoSolid` | `video-fill` | `package/src/icons/video-fill.tsx` (`Recorder` β filled camera / video glyph; filename is `video-fill`) | no | | `IconVolumeFull` | `audio` | `package/src/icons/audio.tsx` (`Sound`) | no | Relevant issues from QA Notion: - Some issues with channel preview -> Muted icon wrong direction - Some issues with channel preview -> Video icon wrong direction - Some issues with Channel info screen -> Arrows (wrong direction) - Some issues with Channel info screen -> Muted icon (wrong direction) - [RTL] Swipe to reply icon has a wrong direction - [RTL] Some issues with composer -> Send button (wrong direction) ### 2. Channel preview fixes Relevant [block in Notion](https://www.notion.so/stream-wiki/RN-QA-New-Design-30d6a5d7f9f680d29ba1f9eadfcc787e?source=copy_link#3346a5d7f9f68041a8dce1c570fe45b3): - [RTL] Some issues with channel preview -> Voice recording (expected order: mic icon, text, duration) - [RTL] Some issues with channel preview -> Β Multiple attachments (expected order: attachments icon, count, text) `useMessagePreviewText` didn't translate message labels like "Voice Recording (0:05)" <img width="334" height="83" alt="Screenshot 2026-04-10 at 13 06 36" src="https://github.com/user-attachments/assets/c81afec3-2a55-4ccf-84db-5a2a6a99ad27" /> ### 3. Mirroring switch in RTL mode Relevant Notion block: [RTL] Some issues with Polls screen -> The pointer moves to the wrong direction when user is typing [RTL] Some issues with Channel info screen -> [iOS] Toggle (wrong direction) Added a mirroring to all switch elements (off state is on the right side, instead of left) on iOS. On Android the default behavior is correct, no need for mirroring. <img width="463" height="999" alt="Screenshot 2026-04-10 at 15 18 31" src="https://github.com/user-attachments/assets/ded00711-91b9-49bf-853a-645bb6aa5434" />  ### 4. Search bar not mirrored in RTL mode These are changes in the SampleApp: From Notion: [RTL] Search field is not mirrored, both channel search and user search Channel search: <img width="480" height="995" alt="Screenshot 2026-04-14 at 10 10 32" src="https://github.com/user-attachments/assets/fc5c4c1d-6ca3-46b7-8e61-ad8a63adecf7" /> User search - I assume this is the new DM screen, but this one seems fine to me without any change: <img width="427" height="910" alt="Screenshot 2026-04-10 at 16 29 28" src="https://github.com/user-attachments/assets/5d269bec-04af-45f8-be3f-9d1c5ab1824a" /> ### 5. Fix quoted message layout From Notion: "[RTL] Quoted reply has a wrong direction in both composer and message list" Fix: - Missing translation keys - `renderText` wrong text alignment <img width="407" height="502" alt="Screenshot 2026-04-13 at 14 00 37" src="https://github.com/user-attachments/assets/9493afe5-9ea1-4b0a-af52-cd3d8b2f57b8" /> <img width="398" height="467" alt="Screenshot 2026-04-13 at 14 03 47" src="https://github.com/user-attachments/assets/92cd8c1b-c423-4b25-9c1d-14c6e2e41a4a" /> ### 6. Fix channel details screen [RTL] Some issues with Channel info screen -> [iOS] Members title (wrong direction) [RTL] Some issues with Channel info screen -> [iOS] In general many things are shifted -> I think the icon fixes + title fixes all issues (see attached screenshot) <img width="456" height="908" alt="Screenshot 2026-04-14 at 8 32 03" src="https://github.com/user-attachments/assets/dd8c5f88-50de-46f7-8489-a9adef645b12" /> ## π¨ UI Changes ## π§ͺ Testing Tested on iOS and Android ## βοΈ 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
Replace the prop-drilling of 120+ component overrides with a single
`WithComponents` context provider. This eliminates massive boilerplate
across `Channel`, `ChannelList`, and all consumer components.
**Before:**
```tsx
<Channel Message={MyMessage} SendButton={MySendButton} Reply={MyReply} channel={channel}>
```
**After:**
```tsx
<WithComponents overrides={{ Message: MyMessage, SendButton: MySendButton, Reply: MyReply }}>
<Channel channel={channel}>
</WithComponents>
```
## π Implementation details
### Design principle
**All components are read from `useComponentsContext()`. All other
contexts only provide data + APIs β never components.**
### New `ComponentsContext`
- `WithComponents` provider β nestable, inner overrides win. Uses
`overrides` prop (aligned with `stream-chat-react`)
- `useComponentsContext()` hook β returns all components with defaults
filled in
- `ComponentOverrides` type β auto-derived from `DEFAULT_COMPONENTS` map
(`Partial<typeof DEFAULT_COMPONENTS>`)
- `defaultComponents.ts` β single source of truth for all ~120 default
component mappings (lazy-loaded to avoid circular deps)
- Adding a new overridable component only requires editing
`defaultComponents.ts` β the type is derived automatically
### What changed
- **Stripped component keys** from `MessagesContextValue`,
`InputMessageInputContextValue`, `ChannelContextValue`,
`ChannelsContextValue`, `AttachmentPickerContextValue`,
`ThreadsContextValue`, `ImageGalleryContextValue` β these now only carry
data + APIs
- **Simplified `useCreate*Context` hooks** β no longer receive or
forward component params
- **Simplified `Channel.tsx`** β removed ~90 component imports, prop
defaults, and forwarding lines
- **Simplified `ChannelList.tsx`** β removed ~19 component props
- **Updated ~80 consumer files** β switched from old context hooks to
`useComponentsContext()` for component reads
- **Removed component override props** from ALL individual components
(Chat, Thread, ThreadList, Poll, ChannelPreview, ImageGallery, etc.)
- **No component accepts component overrides as props anymore** β
everything goes through `WithComponents`
- **`ComponentsContext.tsx` reduced from ~350 lines to ~55 lines** β
type is derived from defaults, no manual type maintenance
- **Updated all 3 example apps** (SampleApp, ExpoMessaging,
TypeScriptMessaging) to use `WithComponents`
### Net result
- **90+ files changed**, net **-2500+ lines** removed
- Each component override name went from being listed 4 times to 0 in
the forwarding pipeline
- One place to override components: `<WithComponents overrides={{ ...
}}>`
- One place to read components: `useComponentsContext()`
- One place to add new overridable components: `defaultComponents.ts`
## BREAKING CHANGE
- **Component override props removed from all SDK components** β
`<Channel Message={X}>`, `<ChannelList Preview={X}>`, `<Thread
MessageComposer={X}>`, etc. no longer accept component overrides as
props. Use `<WithComponents overrides={{ Message: X }}>` instead.
- **Component keys removed from context value types** β
`MessagesContextValue`, `InputMessageInputContextValue`,
`ChannelContextValue`, `ChannelsContextValue`,
`AttachmentPickerContextValue`, `ThreadsContextValue`,
`ImageGalleryContextValue` no longer include component-type keys. Use
`useComponentsContext()` to read component overrides.
- **`List` prop removed from `ChannelList`** β use custom
`EmptyStateIndicator` override or wrap `ChannelListView` directly.
- **`LoadingIndicator` prop removed from `Chat`** β use `<WithComponents
overrides={{ ChatLoadingIndicator: X }}>`.
## π¨ UI Changes
No visual changes β this is a pure structural refactor.
## π§ͺ Testing
- `yarn build` β 0 type errors
- `yarn lint` β passes clean (0 warnings, 0 errors)
- `yarn test:unit` β 91/92 suites pass; 1 pre-existing timeout failure
in `offline-support` that also fails on `develop`
- New test: `defaultComponents.test.ts` verifies all default component
mappings are defined and optional keys are explicitly listed
- Updated test files to use `<WithComponents overrides={...}>` wrapper
instead of removed component override props
## βοΈ Checklist
- [x] 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
- [x] New code is tested in main example apps, including all possible
scenarios
- [x] SampleApp iOS and Android
- [x] Expo iOS and Android
---------
Co-authored-by: Ivan Sekovanikj <ivan.sekovanikj@getstream.io>
## π― Goal A follow-up to [this PR](#3542), containing fixes to some introduced issues. Additionally, it does some final touches to our API before the major release on Thursday. ## π 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
## π― Goal
This PR addresses 2 small UI issues regarding giphies and bumps the peer
dependency versions to what they'll be for V9. It should be the last
change before the V9 release.
## π 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
## π― 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
## π― Goal
Components with `.displayName` assignments (e.g.
`ChannelPreviewView.displayName = '...'`) caused `typeof` to include
`displayName: string` (required) in the inferred `DEFAULT_COMPONENTS`
type. This leaked into `ComponentOverrides`, forcing integrators to set
`displayName` on their custom component overrides.
## π Implementation details
- **`NormalizeComponents` mapped type** β normalizes each entry in
`DEFAULT_COMPONENTS` to `React.ComponentType<P>`, where `displayName` is
optional (`displayName?: string`). Applied via a typed cast on the
export so the raw object keeps its runtime shape.
- **`OptionalComponentOverrides` interface** β optional component slots
(no default implementation) are moved out of the runtime object into a
standalone interface. This eliminates `undefined as
React.ComponentType<any> | undefined` casts and `eslint-disable`
comments.
- **Proper props types** β `MessageLocation`, `MessageText`, and `Input`
now have real prop types instead of `React.ComponentType<any>`.
Components rendered with no props use `React.ComponentType` (defaults to
`{}`).
- **Removed stale `PLAN.md`** from the componentsContext directory.
## π¨ UI Changes
No UI changes β types only.
## π§ͺ Testing
- `npx tsc --noEmit` passes with zero errors
- Verified via TS compiler API that `ChannelPreview` on
`DEFAULT_COMPONENTS` resolves to `ComponentType<...>` with
`?displayName: string | undefined` (optional)
## βοΈ Checklist
- [x] 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
- [x] New code is tested in main example apps, including all possible
scenarios
- [ ] SampleApp iOS and Android
- [ ] Expo iOS and Android
Contributor
SDK Size
|
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