Next Release#3607
Merged
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@runnervmrw5os.iok0aac14kyebbpycwio1tqtlb.gx.internal.cloudapp.net>
## 🎯 Goal This PR addresses an issue with `MessageFlashList` where items would disappear (but still keep their layout within the internal `ScrollView`). The reason behind it is quite technical and it was pretty difficult to find, even though it's just a oneliner. The PR that introduced this was [this one](b935bbe) specifically. Typically, when `overflow` is present - React Native takes a [completely different](https://github.com/facebook/react-native/blob/8636cadb8e7d0f62c213e98d264f11dfc5ea913e/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewGroup.kt#L885) draw path. If we look at the [concrete implementation](https://github.com/facebook/react-native/blob/8636cadb8e7d0f62c213e98d264f11dfc5ea913e/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/BackgroundStyleApplicator.kt#L537), we can see the radius distinction: - no rounded borders: `clipRect` - rounded borders: `clipPath` So `borderRadius` + `overflow: 'hidden'` forces the more complex rounded path clipping branch (in other words meaning, instead of drawing a rectangle with discrete dimensions; draw an oval shape instead). Now, `borderRadius` also [participates in](https://github.com/facebook/react-native/blob/8636cadb8e7d0f62c213e98d264f11dfc5ea913e/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/BackgroundStyleApplicator.kt#L747) `inset` math of the `View`. This essentially means that it creates proper bounds for the `Canvas` underneath, should something change. When a `border` is present (and it specifically needs to be a `border` with a known `width`), we essentially do bounded calculations differently. Namely, here's a breakdown of what's (likely) going on: 1. `overflow !== visible` makes `ReactViewGroup.dispatchDraw` call `clipToPaddingBox(...)` before drawing children 2. Because the view has rounded borders, `clipToPaddingBox(...)` uses `canvas.clipPath(...)`, not `canvas.clipRect(...)` 3. That rounded clipping path is computed from the view bounds, border radius and border insets 4. Removing `borderWidth` removes the border insets/border drawable layer that had previously participated in this rounded clipping geometry and invalidation path 5. Enter `MessageFlashList`, cells are recycled and absolutely repositioned while their content/layout changes and that makes the borderless rounded `clipPath` state fragile on Android: the row could remain mounted, measured and pressable, while the draw pass clipped some or all descendants out (this is why the bug represented either thorough an empty message bubble or a completely gone one, depending on which `item` type was being recycled, since single attachments are rendered fully as a bubble) 7. Removing `overflow: 'hidden'` avoids this native clipping branch entirely, so recycled `FlashList` cells are no longer dependent on a stale rounded clip path In other words, as the `CellRenderer` gets recycled from `FlashList`, its inner children do not know that they need to recalculate and so the new `props` are injected into a view which cannot display them and we either get the full bubble missing or the content not being there in text messages specifically. Very interesting nonetheless. Also probably something important to keep in mind for the future. If we are ever to use `overflow: 'hidden'` specifically in `FlashList` item descendants we need to make sure that the layout of the `View` is fully stable and measureable, so that recycling does not accidentally skip necessary recalculations. Also, ironically, this should also be a slight performance improvement as well (at least on Android) as now all `MessageContent` components are going to go down the easier drawing route. ## 🛠 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 issues with `MessageFlashList` which have made
`scrollTo` behaviour unreliable.
1. It should fix (or at least make a lot better) the issue of being
snapped back to the end of the list whenever we try to scroll to a
quoted message for the very first time
2. It should address not being able to scroll to the first unread
message
Even though these 2 issues are seemingly similar, their underlying
causes are completely different.
#### Quoted message scroll to
This particular issue happens specifically when we've loaded the
messages into memory that we want to scroll to (so loading a completely
different `messageSet` is working fine). The offender here is MVCP and
scrolling to bottom in particular. I'm pretty certain that there's an
upstream bug here, however I did not have a chance to debug it more
thoroughly and find the actual root cause. Roughly what goes on is the
following:
- The list mounts, MVCP gets armed
- We press on a quoted message and begin scrolling to it
- Mid scroll, MVCP decides something's changed (because of the fact that
recycling kicks in and layout gets revalidated) and triggers a pending
scroll
- The scroll is immediately consumed, snapping us back just after the
quoted message scroll finishes
I actually had a patch in `FlashList` which allows us to clear all
pending MVCP scrolls and also suspend MVCP from doing anything and it
worked like a charm (like an imperative API).
However for now, this will have to do. At the very least, even when the
issue happens it should reconcile shortly after and fix its positioning.
#### Initial scroll to first unread
This issue on the other hand is completely unrelated to MVCP. It's
actually related to our automatic scrolling mechanism, which happens on
mount and then also whenever `autoscrollToRecent` actually changes.
Namely, if we look into `FlashList`'s implementation we can see that
`scrollToEnd` is actually ultimately wrapped within a `setTimeout`,
likely to try to delay it natively on the JS runtime for timing
purposes. However, this also means that if a state update happens really
quickly (for example `targetedMessage` updating) we'll end up making the
2 scrolls race. `scrollToEnd` typically wins because it's invoked later
and also because it invokes the underlying scroll view's ref rather than
some abstraction.
We need this custom handling because `initialScrollIndex` for
`FlashList` is very often not correct at all (and off by some number of
offset). This is especially true whenever we scroll between 2 message
sets and virtually load new data. I've yet to find why this is but maybe
some day.
To prevent this, we expose a new API on the `Channel` level that allows
us to anticipate when we're about to scroll to a targeted message,
bypassing the `scrollToEnd` entirely in favor of having a pending
scroll.
These fixes will be ported back to V8 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
This PR removes a relatively archaic functionality we've had for voice
recordings where seeking to 0 would begin audio replay. This is neither
according to our new design system, nor does it make specific sense.
In fact, it would break the state completely as `replayAsync` does not
go through the `requestPlay` lifecycle of our `audio-player` pool,
meaning we would not be getting any state updates (and have audio
playing). Integrators anyway have access to the pool if they wish to
control this themselves.
## 🛠 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 removes remnants of `react-native-keyboard-controller` which we
initially wanted to add to the SDK in a first line support fashion.
However, since it was a bit rushed it was not up to our standards in
terms of quality and so we decided to remove it (hence why it isn't
mentioned in the docs). This was due to the complexity of the
integration within our SDK specifically, not the library itself (the
library's really really great and we actively recommend using it if your
project allows it).
However, some remnants of it spilled over in a way that it forces
`react-native-keyboard-controller` to be used if it's installed. This is
obviously not ideal as a lot of the issues did not get polished out and
so integrations that previously used `react-native-keyboard-controller`
elsewhere have a buggy handling of pretty much everything keyboard
related.
With this change this should be resolved.
## 🛠 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 regression with our `AttachmentPicker` component
which was done in order to fix accessibility issues. Namely, since we
calculate the `topInset` for our `BottomSheet` in order to make it
properly orderable by screen readers, the value for the global
`topInset` was omitted.
This is wrong, because then the calculations are very wrong particularly
in the cases of having native modals (i.e
`react-navigation/native-stack` or `expo-router`) whose height we need
to take into account, similarly to how we do it for
`keyboardVerticalOffset`.
## 🛠 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
szuperaz
approved these changes
May 19, 2026
Contributor
SDK Size
|
Next releasesv9.2.29.2.2 (2026-05-19)
|
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