Skip to content

feat: combine iOS native list, swipe, Liquid Glass and markdown PRs - #7723

Closed
diegolmello wants to merge 175 commits into
developfrom
diegolmello/native-1561-rn-upgrade-13
Closed

diegolmello wants to merge 175 commits into
developfrom
diegolmello/native-1561-rn-upgrade-13

Conversation

@diegolmello

@diegolmello diegolmello commented Sep 24, 2026 •

Copy link
Copy Markdown
Member

Proposed changes

Combines seven open UI PRs on top of the React Native 0.86 upgrade branch:

#7707, #7718, #7719 and #7727 are merged in. #7724, #7740 and #7739 are cherry-picked, since their branches also carry commits that are already here in another form (the unsquashed #7721 commits, and the previous version of this branch).

Conflicts resolved:

Also includes the iOS theme fix that resets the color scheme override before resolving the automatic theme.

Issue(s)

https://rocketchat.atlassian.net/browse/NATIVE-1576

How to test or reproduce

  1. Build the app for iOS 26 and for Android
  2. Follow the test steps in each included PR (feat: Liquid Glass support #7707, feat: render message markdown with react-native-enriched-markdown #7718, feat: allow only one swiped room item at a time #7719, perf: render single-style labels with react-native-plain-text #7727, feat(ios): native context menu with preview on RoomItem #7724, feat(room): migrate messages list to Legend List #7740, feat(rooms-list): render rooms list with Legend List #7739) on this combined build

Screenshots

Types of changes

  • Bugfix (non-breaking change which fixes an issue)
  • Improvement (non-breaking change which improves a current function)
  • New feature (non-breaking change which adds functionality)
  • Documentation update (if none of the other choices apply)

Checklist

  • I have read the CONTRIBUTING doc
  • I have signed the CLA
  • Lint and unit tests pass locally with my changes
  • I have added tests that prove my fix is effective or that my feature works (if applicable)
  • I have added necessary documentation (if applicable)
  • Any dependent changes have been merged and published in downstream modules

Further comments

Epic: https://rocketchat.atlassian.net/browse/NATIVE-1561

Summary by CodeRabbit

  • New Features

    • iOS now offers a more native experience with updated navigation headers, search bars, room details, and list layouts.
    • Markdown messages support richer formatting, math, emoji, links, and animated images.
    • Room-list swiping and row actions have been refined for smoother interactions.
  • Bug Fixes

    • Unread indicators now account for unread thread replies.
    • Improved room navigation and swipe behavior across iOS and Android.

diegolmello and others added 30 commits September 15, 2026 10:35
* chore: upgrade dependencies and patches to RN 0.86 / Expo 57

* chore: align upgrade toolchain floors and CI checks

* chore: update Jest toolchain for RN 0.86 and Expo 57
* fix: adapt app code to React Native 0.86 and Expo 57

* test: await scheduled gesture and floating date callbacks
* fix(ios): support React Native 0.86 and Expo 57

* test: fix jest failures under Node 22

Resolve Expo's lazy fetch global in jest setup so suites that fully mock
react-native no longer crash when jest enumerates globals at teardown.

Restore real timers only after Testing Library unmounts, so immediates
scheduled with fake timers are cancelled with the same clock.

* fix(android): adopt Expo 57 autolinking plugin and drop jcenter from patched libraries

Expo 57 removed scripts/autolinking.gradle in favour of the
expo-autolinking-settings and expo-root-project Gradle plugins.

Gradle 9 removed jcenter(), which @react-native-cookies/cookies and
react-native-math-view still declared.

Bump the iOS Pods cache key so runners stop restoring Local Podspecs
generated for the previous React Native version.

* fix(android): build with React Native 0.86 codegen and Expo 57 prebuilt modules

Bump @react-native/codegen, babel-preset and typescript-config to 0.86.3
so libraries stop resolving the stale 0.80 codegen.

Build the four patched Expo packages from source, since Expo 57 ships
prebuilt Android artifacts that ignore patch-package changes.

Patch camera-roll to drop string literal unions from its spec,
keyboard-controller for the non-null onConfigurationChanged signature,
math-view to drop the removed UIManagerModuleListener import, and
expo-font for the nullable loadedFonts field.

Declare androidx.localbroadcastmanager explicitly and resolve the
WRITE_EXTERNAL_STORAGE maxSdkVersion manifest conflict.

* fix: drop undefined request headers rejected by Expo fetch

Expo 57 installs its native fetch as the global and rejects header
entries whose value is undefined. getServerInfo sent X-Auth-Token and
X-User-Id as undefined before login, so every deep link login fell back
to the Add workspace screen and all E2E flows failed.

* test(e2e): wait for LoginView transition before keyboard navigation

* fix(login): keep focus on the password field when submitting with a hardware keyboard

* fix(ios): compile the release bundle with the hermesc matching the bundled Hermes runtime

* fix(ios): expose room header testIDs to accessibility tree

An accessible wrapper around the room header hides its children from
XCUITest on React Native 0.86, so Maestro could not find the header
title or the header button. Move the accessibility label and role to
the pressable header, which already exposes its children.

* fix(ios): expose rooms list header testIDs to accessibility tree

Same cause as the room header: an accessible wrapper inside the native
stack header hides its children from XCUITest, so Maestro could not find
the servers list button.

* fix(ios): bump react-native-linear-gradient to 2.8.3 to stop zero-size layer crash on mention autocomplete

* fix(ios): address upgrade review feedback

* fix(ios): exclude Android keyboard flow change
* fix: update Android build for React Native 0.86

* fix: restore Android Gradle 9 compatibility

* fix: align React Native codegen version

* fix: resolve Android storage permission merge

* fix: patch Android modules for React Native 0.86

* fix: restore Android local broadcast dependency

* fix: build patched Expo modules from source

* fix: raise iOS deployment target to 16.4 for Expo 57

* fix: materialize expo fetch global before jest mocks

* fix: keep setImmediate real under jest fake timers

* fix: key iOS pods cache by Podfile.lock hash

* fix: allow non-modular React includes in framework modules

* fix: drop undefined header values before fetch

expo/fetch, installed as the global fetch on native by Expo 57, forwards header entries to the native request verbatim, so an undefined value fails the Kotlin cast to Pair<String, String> and rejects the whole request.

* fix: build React-Core from source instead of prebuilt

* fix: match Expo import access level with generated provider

* fix: adopt Expo 57 AppDelegate contract

* fix: compile Hermes bytecode with the legacy compiler

* fix: expose header test ids on iOS

* fix: expose team channels header test id

* fix: remove iOS changes from Android upgrade
Removed the three Podfile opt-outs (RCT_USE_PREBUILT_RNCORE, RCT_USE_RN_DEP,
RCT_HERMES_V1_ENABLED) and the HERMES_CLI_PATH post_install override, letting
React Native 0.86 use its prebuilt React-Core/ReactNativeDependencies
tarball and Hermes V1 (250829098.0.17) instead of building from source.

Dropped the RCTViewManager accessibility-role patch: it only touched the
Paper renderer, the app runs on Fabric, and the file no longer compiles
under prebuilt Core. VoiceOver role-update behavior is covered by manual
smoke testing, not this patch.

RNFB pods forced to static libraries in the pre_install hook to avoid a
non-modular-include failure on RNFB 21.x; the fix ships in
react-native-firebase >= 26.3.0, so this can be dropped on that bump.

SocketRocket now ships vendored inside the ReactNativeDependencies
xcframework under a namespaced header path, so SSLPinning.mm's import
changed from a flat "SRWebSocket.h" to <SocketRocket/SRWebSocket.h>.

Copy Pods Resources phases regenerated by pod install, dropping the
boost/glog/RCT-Folly privacy bundles that no longer exist as separate pods.
* fix(e2e): reach login email field by focus instead of Dpad count

The keyboard navigation onboarding flow walked to the email field with
four blind Remote Dpad Down presses. Removing the decorative OR
separator from the accessibility focus chain shortened the chain by one,
so the email was typed into the password field and the password into the
email field, and login was never submitted.

Press Down until login-view-email reports focused, bounded by times: 10,
and assert focus before typing. Maestro returns normally when a repeat
bound is exhausted, so without the assertion an over-long chain would
fail later at the rooms-list-view wait with an unrelated error.

The separator only renders when totalServices > 0, so the chain length
depends on server config and no fixed count is correct.

* fix(e2e): reach password and submit by focus, hide keyboard first

Anchoring the email field exposed two more blind steps in the same flow.
The hop to the password field assumed Enter moves focus, and the submit
assumed Enter on the password field logs in. Neither had been exercised
before, because the flow failed earlier.

Press Down until each target reports focused, bounded and asserted like
the email field. The submit loop needs the soft keyboard hidden first:
with the keyboard up the Dpad presses go to the input method and never
reach the login screen, so the submit button is never focused.
* chore(deps): bump reanimated to 4.6.0 and worklets to 0.12.2

Reanimated 4.6.0 requires react-native-worklets 0.12.x, so both move
together. Worklets drops its caret to match reanimated's exact pin.

Reanimated 4.6.0 initializes CSS event handling at import time, which
the JS-only fallback used by Jest does not implement, so the mock entry
point throws on load. Stubbing setCSSEventHandler at its source keeps the
rest of the proxy module real.

* chore(ios): update Podfile.lock for reanimated and worklets

CocoaPods preserves versions already in Podfile.lock, so pod install
would not have picked these up on its own.

* chore(ios): bump ffi to 1.17.4 for Xcode 27 compatibility
iOS 27 SDK requires scene-based app lifecycle. The app now uses
Expo's ExpoAppSceneDelegate via a new SceneDelegate, with
startReactNative and boot splash initialization moved out of the
app delegate into the scene delegate.
Disable code-highlight and math native assets to avoid network-dependent
postinstall downloads and a known iOS static-linkage conflict with LaTeX.
Patch the package's jest.d.ts, which points at raw unbuilt TS source instead
of its compiled declarations, breaking typecheck for any project using the
official jest mock.
Serialize the message-parser AST into CommonMark/GFM per top-level block and
render each block with EnrichedMarkdownText instead of one React component per
AST node type. Mentions, channels, timestamps and custom emoji encode as
scheme links (user://, channel://, timestamp://) or inline images, resolved
through a single onLinkPress dispatcher. Block LaTeX keeps rendering through
the existing KaTeX component; inline LaTeX and the preview/plain-text path are
unchanged.
…pshots

Regenerate snapshots for stories that render through the new markdown
component tree.
…-pad

The RN 0.86 gesture-handler bump added an Android-only onKeyUp override
that intercepts DPAD_CENTER/ENTER/SPACE and activates whichever button
currently holds native view focus, bypassing the default performClick()
path. Every composer and header icon button is a BorderlessButton, so
this silently fired the wrong button (e.g. the header drawer button) on
hardware Enter/D-pad and swallowed the key event elsewhere. Removed the
hunk; the iOS press-handling hunk is untouched.
Derive the open side from the resting offset instead of a separate row
state, release and register the row inside the gesture worklet, fire the
threshold haptic from the pan update, and flip gesture input and output
once for right-to-left layouts. Drop the duplicate long-press gesture and
skip the snapshot rebuild on the record's initial emission.
…t' into combo-13-recreate

# Conflicts:
#	app/containers/BackgroundContainer/__snapshots__/index.test.tsx.snap
#	app/containers/BackgroundContainer/index.tsx
#	app/containers/Button/index.tsx
#	app/containers/DirectoryItem/__snapshots__/DirectoryItem.test.tsx.snap
#	app/containers/List/__snapshots__/List.test.tsx.snap
#	app/containers/MessageComposer/components/SendThreadToChannel.tsx
#	app/containers/RoomHeader/RoomHeader.tsx
#	app/containers/RoomHeader/__snapshots__/RoomHeader.test.tsx.snap
#	app/containers/RoomItem/__snapshots__/RoomItem.test.tsx.snap
#	app/containers/ServerItem/__snapshots__/ServerItem.test.tsx.snap
#	app/containers/markdown/__snapshots__/Markdown.test.tsx.snap
#	app/views/AccessibilityAndAppearanceView/components/ListPicker.tsx
#	app/views/MediaAutoDownloadView/ListPicker.tsx
#	app/views/NewMessageView/Item.test.tsx
#	app/views/NewMessageView/Item.tsx
#	app/views/NewMessageView/__snapshots__/Item.test.tsx.snap
#	app/views/NewServerView/components/ServersHistoryItem/__snapshots__/ServersHistoryItem.test.tsx.snap
#	app/views/RoomInfoView/components/BaseButton.tsx
#	app/views/RoomMembersView/index.tsx
#	app/views/RoomView/components/Banner.tsx
#	app/views/RoomsListView/components/Header.tsx
#	app/views/RoomsListView/components/SectionHeader.tsx
#	app/views/SelectedUsersView/Header.tsx
@diegolmello
diegolmello deployed to approve_e2e_testing September 29, 2026 20:58 — with GitHub Actions Active
@diegolmello
diegolmello deployed to android_build September 29, 2026 21:00 — with GitHub Actions Active
@diegolmello

Copy link
Copy Markdown
Member Author

Verified 96ec202 on an iOS 27 simulator (iPhone 17 Pro, debug build).

Steps shown in the video:

  1. Rooms list: swipe a room row left. Favorite and Hide appear and the row stays open (0:04–0:13).
  2. Tap the open row. It closes and the room does not open (0:14).
  3. Swipe another row right. The read toggle appears and the row stays open (0:20–0:26).
  4. With that row open, tap a different row. The open row closes and the tapped room does not open (0:27).
  5. Long-press a row. Nothing opens and no row moves (0:36).
  6. Tap the row. The room opens (0:44).
  7. Open the share flow with text. "Send to..." lists the rooms. Pull down to show the header search bar, then search "Legend" (0:49–1:13).
  8. Open the share flow with a file that doesn't exist. The toast "The shared file could not be found" appears and the app goes back to the rooms list (1:19–1:21).

Outcome: the swipe, open-row, tap and long-press behaviour works, and so does the missing-attachment guard.

One issue in the share list, unrelated to the merge (the merge changes only the attachment guard in that screen): the list begins under the native header and search bar. The "Chats" section header and the first two rooms sit behind the bar and can't be scrolled into view. When a search has results, they render behind the bar too: "Legend" matches one room, and the visible list stays empty (1:10–1:13).

96ec2024ff.mp4

@diegolmello
diegolmello force-pushed the diegolmello/native-1561-rn-upgrade-13 branch from 332e2ce to 14d846b Compare September 29, 2026 21:09
@diegolmello
diegolmello deployed to approve_e2e_testing September 29, 2026 21:36 — with GitHub Actions Active
@diegolmello
diegolmello deployed to android_build September 29, 2026 21:38 — with GitHub Actions Active
@github-actions

Copy link
Copy Markdown

iOS Build Available

Rocket.Chat 4.77.0.109816

@github-actions

Copy link
Copy Markdown

Base automatically changed from diegolmello/native-1561-rn-upgrade to develop September 30, 2026 13:18
@diegolmello diegolmello closed this Oct 1, 2026

This branch is waiting to be deployed

3 active and 1 waiting deployments
upload_android — 14d846bd Waiting Sep 29, 2026 by diegolmello via Build Android / Upload Hold #6871
android_build — 14d846bd Deployed Sep 29, 2026 by diegolmello via Build Android / Hold #6871
ios_build — 14d846bd Deployed Sep 29, 2026 by diegolmello via Build iOS / Hold #6871
approve_e2e_testing — 14d846bd Deployed Sep 29, 2026 by diegolmello via E2E Hold #6871
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.

2 participants