Skip to content

Next Release#3607

Merged
isekovanic merged 6 commits into
mainfrom
develop
May 19, 2026
Merged

Next Release#3607
isekovanic merged 6 commits into
mainfrom
develop

Conversation

@isekovanic
Copy link
Copy Markdown
Contributor

🎯 Goal

🛠 Implementation details

🎨 UI Changes

iOS
Before After
Android
Before After

🧪 Testing

☑️ 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

Stream-SDK-Bot and others added 6 commits May 15, 2026 17:25
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
@isekovanic isekovanic requested review from oliverlaz and szuperaz May 19, 2026 14:58
@Stream-SDK-Bot
Copy link
Copy Markdown
Contributor

SDK Size

title develop branch diff status
js_bundle_size 368 KB 368 KB 0 B 🟢

@github-actions
Copy link
Copy Markdown

Next releases

v9.2.2

9.2.2 (2026-05-19)

@isekovanic isekovanic merged commit eeb5b5d into main May 19, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants