Skip to content

Next Release#3639

Merged
isekovanic merged 13 commits into
mainfrom
develop
Jun 10, 2026
Merged

Next Release#3639
isekovanic merged 13 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

oliverlaz and others added 13 commits May 27, 2026 16:23
## 🎯 Goal

Move us onto Yarn 4 with native workspaces and drop Lerna. The
`.yarnrc.yml` was already half-migrated, but `yarnPath` still pointed at
the v1 binary β€” this finishes that off.

## πŸ›  Implementation details

Tooling only; nothing in `package/src` changes. Roughly:

- `.yarn/releases/` swapped to 4.14.1, and all seven `yarn.lock` files
migrated v1 β†’ v8 in place β€” resolutions preserved, no drift.
- Single root `workspaces` array now covers `package/`, both native
wrapper packages, and all three example apps. `link:../../package/*`
becomes `workspace:^`, nested lockfiles are gone, and the
install-and-build-sdk composite action collapses to one `yarn install
--immutable`.
- Shared-native sync + husky setup run from the core SDK workspace's
`postinstall` (Yarn 4 doesn't run root-workspace lifecycle scripts on
install).
- Lerna removed: `release/release.config.js` no longer reads
`lerna.json`, and `release` / `release-next` / `extract-changelog` use
`yarn workspaces foreach`.
- Husky 6 β†’ 9 (the v6 hook boilerplate is on the deprecation path).
- `.yarnrc.yml` picks up the conservative hardening tier:
`enableHardenedMode`, `npmMinimalAgeGate: 3d`, `enableScripts: false`
with a small `dependenciesMeta` allowlist for the packages that
genuinely need to build (`@swc/core`, `better-sqlite3`,
`react-native-nitro-modules`, `unrs-resolver`).
- CI workflows cache `.yarn/` via setup-node; drive-by fix for the
deprecated `::set-output` calls in `changelog-preview.yml`.

## 🎨 UI Changes

N/A.

## πŸ§ͺ Testing

Locally: `yarn install --immutable` is clean, `yarn lint` + `yarn build`
pass, husky hooks fired on every commit in this PR.

Can't verify locally: example apps on real devices, and the release flow
itself. The Lerna β†’ `yarn workspaces foreach` rewrite is the riskiest
single change β€” worth a dry-run on a throwaway branch before merging.

## β˜‘οΈ Checklist

- [x] I have signed the [Stream
CLA](https://docs.google.com/forms/d/e/1FAIpQLScFKsKkAJI7mhCr7K9rEIOpqIDThrWxuvxnwUq2XkHyG154vQ/viewform)
(required)
- [x] PR targets the `develop` branch
- [x] Documentation is updated
- [ ] New code is tested in main example apps, including all possible
scenarios
  - [ ] SampleApp iOS and Android
  - [ ] Expo iOS and Android

---------

Co-authored-by: Ivan Sekovanikj <ivan.sekovanikj@getstream.io>
## 🎯 Goal

This PR should address the release process issues we introduced with the
Yarn Berry implementation.

## πŸ›  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 redundant views for a large portion of `Message`
components which will simply not be hit very often (but still result in
a `yoga` measurement/`ShadowNode` since they will not be collapsed).

## πŸ›  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 a long standing bug in the `AutoCompleteInput` where
typing `@` to open the mention picker silently fails in several
scenarios, most reproducibly when:

- the `@` is typed between text and a newline block (e.g.
`"asdf\n\n@\n\n dsfa"`),
- the user deletes an existing `@` and retypes it on the same line
(single line case is `iOS` reproducible, multiline case is `Android`
reproducible).

Should close [this ticket](https://app.zendesk.com/agent/tickets/81034).

## πŸ›  Implementation details

The LLC's mention/command/emoji detection operates on `text.slice(0,
selection.end)`. The RN composer was always passing `selection.end =
newText.length` to `textComposer.handleChange(...)`, regardless of where
the caret actually was. The LLC's mention trigger regex
`(/(?!^|\W)?[@][^\s@]*\s?[^\s@]*$/)` only tolerates a single whitespace
between `@` (as it should, this is correct) and end of string, so any
extra trailing whitespace (newlines, `"asdf @ dsfa more"`, etc.) caused
the trigger to be missed.

To fix this, we stop trying to invent a caret position.
`AutoCompleteInput` now holds two refs that mirror whatever native most
recently reported - `latestTextRef` from `onChangeText` and
`latestSelectionRef` from `onSelectionChange` and forwards both into the
LLC together via a coalesced flush:

- Each event updates its `ref` and calls `scheduleChange()`
- `scheduleChange()` does a `setTimeout(flushChange, 0)`, and is
idempotent - if a flush is already pending, the second event piggybacks
on it
- Because `onChangeText` and `onSelectionChange` fire as separate events
in different orders on `iOS` vs `Android`, the `setTimeout(0)` defers
the LLC call to the next task so both refs are populated with the
keystroke's final values regardless of which event landed first
- Sadly, on RN the order of these events is so unpredictable we need to
do this and potentially wait for (almost) a single frame; as sometimes
both of these callbacks arrive in different order, `Android` sometimes
fires it twice and sometimes `onSelectionChange` simply doesn't even
fire on `iOS`
- `flushChange()` calls `textComposer.handleChange({ text, selection })`
with whatever the refs hold at that moment

Because both events for the same keystroke land in the current event
loop task batch, in whichever order iOS or Android happens to dispatch
them, the deferred flush always sees the freshest text and the freshest
selection together.

## 🎨 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 should fix some outstanding `a11y` issues for screen readers.
The prior foundation work (`a11y` context, `useA11yLabel`,
`useAccessibilityAnnouncer`, `useResolvedModalAccessibilityProps`,
translation namespace, etc.) shipped the primitives. This PR uses those
primitives to fix the surfaces flagged by an audit - polls, message
context menus, and bottom sheets.

## πŸ›  Implementation details

Five focused changes, each scoped to one surface:

Poll - actionable rotor (`Poll.tsx`, new
`usePollAccessibilityActions`/`usePollAccessibilityLabel`/`PollUIStateContext`)
- Voting, ending the vote, and "show all options" are now exposed as
standard accessibility actions. Screen reader users reach each
interaction via the rotor/actions menu instead of having to hunt for the
right subelement
- `PollUIStateContext` carries state across the poll's modals (vote /
see results / end vote confirm) so the available action set stays
correct as the user navigates (we do this so that we have one source of
truth for firing these actions)

Poll & Message - context menu hint
- `accessibilityHint='a11y/Double tap and hold to activate contextual
menu'` on both `MessageContent` and `Poll`, so screen reader users know
a long-press menu is available before triggering it

Message overlay - open announcement (`MessageOverlayHostLayer.tsx`)
- One shot `announceForAccessibility('Swipe right to go through
different actions')` when the overlay activates. `useRef` dedupes so it
speaks once per open and resets on close

BottomSheetModal - modal trait + accessible dismiss
(`BottomSheetModal.tsx`)
- Drag handle is now a focusable element labeled "Close" with
`accessibilityRole='button'`
- iOS `VoiceOver` double tap goes into `onAccessibilityTap` which goes
into `onClose`
- Android `TalkBack` double tap goes into
`accessibilityActions=[{name:'activate'}]` + `onAccessibilityAction`
which then goes into `onClose` as well
- iOS two-finger Z scrub triggers `onAccessibilityEscape={onClose}`.
Android hardware back was already wired through `Modal.onRequestClose`.
- One shot open announcement, deferred `800ms` so `VoiceOver`'s auto
focus shift speech ("Close, button") doesn't preempt the announce mid
sentence.
- Lifts to every consumer automatically: Reactions detail, "More
reactions" emoji picker, Attachment type picker, Image gallery grid
view.

**Translations**
- Five new `a11y/*` keys (Bottom sheet opened…, Close, Double tap and
hold to activate contextual menu, Swipe right to go through different
actions, plus the poll action labels) added to all 13 locales.

## 🎨 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 bumps our `SampleApp` to React Native 0.85 as well adding
support for some major dependencies, such as:

- `react-native-gesture-handler` - `3.0.0`
- `react-native-reanimated` `4.4`

## πŸ›  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 reverts the `react-native-reanimated` version of our `SampleApp`
as it would appear version `4.4.0` has some severe performance issues of
layout animations on the latest version, likely related to the new
animation backend refactoring.

## πŸ›  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

Cross platform a11y fixes across the message surface (message row,
footer, reactions, replies) and the channel preview list. VoiceOver /
TalkBack now read each surface with proper labels, no misleading
affordances, and correct focus behavior.
## πŸ›  Implementation details
- Poll message rotor actions - iOS announced custom rotor actions that
couldn't actually be activated. Removed the composite rotor; poll
bubbles now drill in so each option is a focus stop with proper
radio/checkbox role.
- Reply composer focus - initiating a reply landed `VoiceOver` focus on
the navigation back button. Reply now focuses the composer's text input
when a screen reader is active, and announces "Replying to {user}" +
"Editing message" via `useAnnounceOnShow` so the user knows the composer
entered reply/edit mode.
- Channel preview verbosity - list rows previously announced "Avatar of
X" per member, bare timestamps ("12:14 AM"), bare unread counts ("3").
Each leaf now carries a curated label: avatar reads "Direct chat with
{name}" / "Channel with N members", date reads "Last message {date}",
unread badge reads "{count} unread messages", muted indicator reads
"Muted", delivery-status icon reads "Read, sent by you" / "Delivered,
sent by you" / etc.
- Channel preview delivery status not read - the status View had
accessibilityLabel but no accessible={true}, so VO walked past it. Fixed
by adding accessible so the composed delivery-status label is
announced.
- Message footer delivery state silent - the checkmark icon was a silent
SVG; VO read only "11:05 AM". Footer is now `accessible` +
`accessibilityRole='text'` (cross-platform compose), with
`MessageStatus`
rendering a HiddenA11yText for the localized status string. Footer
announces as one stop: "Read, 11:05 AM".
- Reaction list "actions available" misleading affordance β€” VO promised
rotor actions that didn't exist. Clustered reactions are now one labeled
button via the `Pressable`'s auto compose (emoji names + "you reacted"
tag for own + "and N more reactions" suffix), with hint "Double tap to
view reactions". Segmented reactions stay as per-item `Pressable`s with
the same "you reacted" tagging.
- Message rich content can't be activated - message rows with images,
files, quoted replies, or shared location couldn't drill into the inner
content. Generalized the poll only `accessible={false}` to cover any
interactive children (`attachments`, `quoted_message`, `poll`,
`shared_location`). Plain text messages keep their single focus stop
behavior; rich messages drill in.
New shared components
- `CompositeAccessibilityProbe`
(`package/src/components/Accessibility/`) - wraps non `Text` visual
content (avatars, icons, badges) so it announces as one focus stop with
a curated label. Internally renders a hidden `Text` sibling + a
decorative marked `View` around children. Used by `ChannelAvatar`,
`ChannelPreviewMutedStatus`, `ChannelMessagePreviewDeliveryStatus`.
Solves the iOS vs Android composition asymmetry where Android's
`importantForAccessibility='no-hide-descendants'` leaks through nested
`accessible={true}` descendants.
- `HiddenA11yText` (`package/src/components/Accessibility/`) - visually
invisible `Text` whose only job is to splice extra info into a parent's
compose loop (e.g. "you reacted", "and N more reactions", localized
delivery status). Different concern from the probe - this is for adding
text to a composition chain, not for collapsing a subtree into one stop.
Both are exported from the SDK so consumers building their own custom
message / preview UIs can reuse the patterns.

## 🎨 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
Follow up `a11y` pass on top of #3632. Covers the message row, the
attachment thumbnail gallery, the full screen image/video gallery
overlay, the reactions UI, the message cell annotations and date
separators, the channel preview date, and the poll option row. VoiceOver
and TalkBack now read each surface with proper labels, units and focus
behavior and the gallery traps focus while open.
## πŸ›  Implementation details
- Message row sender label - the bubble's `Pressable` now announces
"Message from {sender}" / "Message from you". Messages with interactive
children (poll, quoted message, attachments, shared location) opt out of
being a single focus stop so VO/TalkBack can drill into the children; an
iOS-only absolute-fill
`View` carries the sender label so the speaker is still announced when
focus lands inside the inner content.
- `hasInteractiveAccessibilityContent` override - new field on
`AccessibilityConfig` that receives the SDK's baseline boolean and lets
integrators extend the "is this message interactive?" decision without
losing the defaults. Resolved upstream in `useCreateMessageContext` and
exposed as a stable boolean on
`MessageContext`.
- Android compose gate on text bodies - `MessageTextContainer` and the
`Poll` header now set `accessible accessibilityRole='text'` so TalkBack
auto-composes the descendant `<Text>` content. Conditional on
`hasInteractiveAccessibilityContent` for the message text to avoid
double-announce on plain messages.
- Gallery thumbnails - the thumbnail `Pressable` now carries an
`a11y/Gallery Image` or `a11y/Gallery Video` label (added across all 13
locales), explicit `role='button'`, and the existing `a11y/Double tap to
open` hint. Announces as "Gallery video, button. Double tap to open."
- Full-screen gallery modal trap - `accessibilityViewIsModal` on the
`ImageGallery` root and the `MessageOverlayHostLayer` host on iOS.
Android gets a new `OverlayA11yShield` sibling wrapper that flips
`importantForAccessibility='no-hide-descendants'` when either the
gallery or the message context menu is active.
- Full-screen gallery adjustable cycling - the gallery backdrop is now
the focus target with `role='adjustable'`, `value={ text: 'N of M' }`,
increment/decrement actions, and an `accessibilityLabel` of "Image
Gallery". Swipe up/down cycles through assets.
- Gallery focus return on dismiss - the image gallery state store gained
a `requesterNode` field. `Gallery.tsx` captures the thumbnail's node
handle via `findNodeHandle` and passes it through `openImageGallery`; on
unmount the gallery restores focus via
`AccessibilityInfo.setAccessibilityFocus` inside an rAF.
- Reaction announcements - `ReactionListItem` gains `role='button'`
(trips the Android compose gate so TalkBack reads the emoji's CLDR name
instead of the reaction `type` string) and `state.selected`.
`ReactionButton` drops a broken `accessibilityLabelKey` that was
literally untranslated across every locale.
- Thread reply indicator - the `MessageReplies` `Pressable` now has
`role='button'`, an `accessibilityLabel` reading the reply count ("1
Reply" / "N Replies"), and an `accessibilityHint` of "Double tap to view
thread" (new i18n key across all 13 locales). Participant avatars in the
indicator are hidden from the
a11y tree so they no longer announce one-by-one.
- Decorative dot separators - the middle dots in `SentToChannelHeader`
("Replied to a thread Β· View") and `MessageReminderHeader` ("Reminder
set Β· 5 minutes left") are now marked decorative so focus moves directly
from the label to the action.
- Date announcements (iOS) - VoiceOver was reading numeric dates
literally ("zero four slash zero eight..."). Added a
`getDateStringForA11y` helper that substitutes `LL` for the calendar
`sameElse` slot, preserving locale relative words like "Today" /
"Yesterday". Wired into `InlineDateSeparator` (announces
"April 8, 2026") and `ChannelPreviewStatus` (announces "10:30 AM" for
same-day messages via a `sameDay: 'LT'` override). TalkBack honors the
override too, no platform branching needed.
- Poll option vote count - the bare count `Text` on each poll option row
now carries an `accessibilityLabel` using the existing `{{count}} votes`
plural key. Announces "0 votes" / "1 vote" / "N votes" instead of just
the bare number.
### New shared components and exports
- `OverlayA11yShield` (`package/src/components/Accessibility/`) -
Android-only focus-trap shield. Subscribes to the overlay context and
the message-overlay store to compute `isAnyOverlayActive`, then wraps
`{children}` in a `View` that flips
`importantForAccessibility='no-hide-descendants'` and
`accessibilityElementsHidden` while an overlay is active. On iOS it's a
pure passthrough selected at module load.
- `getDateStringForA11y` (`package/src/utils/i18n/getDateString.ts`) -
sibling helper to `getDateString`. Produces a TTS-friendly calendar
string by substituting `LL` for the `sameElse` slot in the locale's
calendar formats, with an optional `calendarFormatOverrides` parameter
for callers whose visible format
diverges from the locale defaults.
- `AccessibilityConfig.hasInteractiveAccessibilityContent` - new
override hook for integrators (see above).
- `MessageContext.hasInteractiveAccessibilityContent` - new stable
boolean field consumed by `MessageContent` and `MessageTextContainer`.
- `ImageGalleryState.requesterNode` and the matching `openImageGallery({
requesterNode? })` argument.

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

Fixes [this GH
issue](#3633).

All details are in the ticket.

## πŸ›  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 patches `react-native-gesture-handler` so that pressable items
work inside of it once again.

In `3.0.0`, pressable handlers are not invoked at all if they're within
`ReanimatedSwipable`. It's the same case for both RN and RNGH
`Pressable`s.

I'll open a separate upstream issue, as I'm pretty sure that the issue
goes deeper and the fix is just a bandaid masking the actual underlying
cause, but this'll have to do for now so that our `SampleApp` still
works properly (I don't want to revert the RNGH version so that I can
spot any other potential issues with it). More details will follow
there.

## πŸ›  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 a review from oliverlaz June 10, 2026 08:29
Comment thread perf/analyze-cpuprofile.js Dismissed
@github-actions

Copy link
Copy Markdown

Next releases

v9.3.1

9.3.1 (2026-06-10)

Bug Fixes

Performance Improvements

sampleapp@v4.12.3

4.12.3 (2026-06-10)

Bug Fixes

Performance Improvements

@Stream-SDK-Bot

Copy link
Copy Markdown
Contributor

SDK Size

title develop branch diff status
js_bundle_size 1734 KB 1734 KB 0 B 🟒

@isekovanic isekovanic merged commit 4599ed1 into main Jun 10, 2026
17 of 18 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.

4 participants