Files
orca/mobile/patches/react-native@0.88.1.patch
T
NeilandKelvin Amoaba 5d61e00c6d build(mobile): upgrade to Expo SDK 58 and React Native 0.88 (#37935)
* build(mobile): upgrade to Expo SDK 58 and React Native 0.88

Expo 58 drops Zod from @expo/cli, which clears the last Zod 3 advisory
(GHSA-mrhx-c7xc-m3w5) in mobile along with its local patch and override.

- Expo 55 -> 58, React Native 0.83 -> 0.88, React 19.3, gesture-handler 3,
  webview 14, reanimated 4.7, worklets 0.13 (SDK 58 bundled versions,
  pinned below today's batch to respect minimumReleaseAge).
- Port the react-native and react-native-webview patches unchanged; drop
  expo-constants, expo-notifications, @expo/cli, expo-router, zod and
  query-string patches plus the zod/query-string/xcode>uuid overrides,
  which upstream fixed or no longer need.
- iOS minimum rises to 16.4 (Expo 58 / RN 0.88 requirement).
- Migrate to RN 0.88 strict types: instance ref types, NamedStyles,
  AppState.currentState, list header/footer nulls.
- Replace removed runtime APIs: InteractionManager -> idle callback with a
  macrotask fallback; StyleSheet.absoluteFillObject -> absoluteFill;
  FileHandle.readBytes -> readBytesSync (it became async).
- Resolve react-native/asset-registry for shared assets in Metro and alias
  the old registry path for react-native-svg in the desktop mobile-web bundle.
- Reduce the iOS scene plugin to the reconnect override; Expo 58's template
  now ships the scene delegate itself.

* fix(mobile): bridge react-native-svg web imports and libsodium runtime for RN 0.88

- Share react-native-svg web shims between the mobile-web build and its render
  checks: map the old asset-registry path to react-native-web's registry and stub
  the Android-only touch-sound TurboModule.
- Patch react-native-libsodium 1.7.0 to read the JSI runtime from the bridge proxy;
  RN 0.88 removed RCTCxxBridge.runtime.

* fix(mobile): use Expo 58's legacy promise resolver for audio permissions

* fix(mobile): skip null handles in the RPC recording timer shims for RN 0.88 types

* style(mobile): format MobilePRSidebar after rebase

* fix(mobile): name RN 0.88 text input refs for declarations; allow queued back in stack render check

- Type text input refs with an exported interface so declaration emit can name hook results
  (RN 0.88 aliases TextInputInstance to an unexported class).
- Expo Router 58 drains back() after a commit; check the interrupted push's exit from the
  frame it starts, bounded to three frames of lag.

* chore(mobile): justify the text input handle interface for React Doctor

* chore(mobile): use a nameable intersection alias for text input refs

* fix(mobile): use sync expo-file-system writes, copies and moves on Expo 58

Expo 58 made File.write/copy/move and FileHandle.writeBytes return promises.
Our callers never awaited them, so failures escaped their try/catch and the
store's tmp-then-move and PDF writes could race their close or rename.

* build(mobile): raise the page bundle budget to 10 MiB for Expo 58

Expo 58 adds about 310 KB to the page: expo-router (+258 KB, now carrying
React Navigation, net about +110 KB) and Reanimated 4.7 (+152 KB). Main had
85 KB of headroom left. 10 MiB still stays within the 10 MiB per-asset contract.

* fix(mobile): drop the window-metrics seed RN 0.88 removed from Android

RN 0.88 no longer holds window metrics: DeviceInfoModule reads the activity's
WindowMetrics on demand and setWindowDisplayMetrics is gone, so MainActivity
no longer compiled. Layout scale still comes from the screen metrics we reseed.

---------

Co-authored-by: Kelvin Amoaba <97001695+AmoabaKelvin@users.noreply.github.com>
2026-11-11 23:30:02 +00:00

155 lines
7.1 KiB
Diff

diff --git a/Libraries/Text/TextInput/Multiline/RCTUITextView.mm b/Libraries/Text/TextInput/Multiline/RCTUITextView.mm
index a803387b98bfb68e0c9bbb783e25bc649566e6a3..53adfdd413797871d29e13770f50e9be6377c3e1 100644
--- a/Libraries/Text/TextInput/Multiline/RCTUITextView.mm
+++ b/Libraries/Text/TextInput/Multiline/RCTUITextView.mm
@@ -326,6 +326,11 @@ static UIColor *defaultPlaceholderColor(void)
#pragma mark - Dictation
+- (void)resetDictationRecognition
+{
+ _dictationRecognizing = NO;
+}
+
- (void)dictationRecordingDidEnd
{
_dictationRecognizing = YES;
diff --git a/Libraries/Text/TextInput/RCTBackedTextInputViewProtocol.h b/Libraries/Text/TextInput/RCTBackedTextInputViewProtocol.h
index 2f32b96d2a17b1fd512c2d985dd536ffd4c011c8..4089c3a940a7c389c17500667b6c026bc741034d 100644
--- a/Libraries/Text/TextInput/RCTBackedTextInputViewProtocol.h
+++ b/Libraries/Text/TextInput/RCTBackedTextInputViewProtocol.h
@@ -47,6 +47,7 @@ NS_ASSUME_NONNULL_BEGIN
- (void)setSelectedTextRange:(nullable UITextRange *)selectedTextRange NS_UNAVAILABLE;
- (void)setSelectedTextRange:(nullable UITextRange *)selectedTextRange notifyDelegate:(BOOL)notifyDelegate;
- (void)scrollRangeToVisible:(NSRange)selectedTextRange;
+- (void)resetDictationRecognition;
// This protocol disallows direct access to `text` property because
// unwise usage of it can break the `attributeText` behavior.
diff --git a/Libraries/Text/TextInput/Singleline/RCTUITextField.mm b/Libraries/Text/TextInput/Singleline/RCTUITextField.mm
index 064b48bb372074df29f961ff8d79e7caba8520c5..b0b73ecba1f42a7789ede064aa83f4ae9f62c1fb 100644
--- a/Libraries/Text/TextInput/Singleline/RCTUITextField.mm
+++ b/Libraries/Text/TextInput/Singleline/RCTUITextField.mm
@@ -197,6 +197,11 @@
#pragma mark - Dictation
+- (void)resetDictationRecognition
+{
+ _dictationRecognizing = NO;
+}
+
- (void)dictationRecordingDidEnd
{
_dictationRecognizing = YES;
diff --git a/React/Fabric/Mounting/ComponentViews/Text/RCTParagraphComponentView.mm b/React/Fabric/Mounting/ComponentViews/Text/RCTParagraphComponentView.mm
index 228ec0d2a28feeee886e1105f4b1e6cd18649b42..c50f79b48e0d4ae0fcdcfb49d0b9db949f55f5d7 100644
--- a/React/Fabric/Mounting/ComponentViews/Text/RCTParagraphComponentView.mm
+++ b/React/Fabric/Mounting/ComponentViews/Text/RCTParagraphComponentView.mm
@@ -167,6 +167,7 @@ using namespace facebook::react;
{
[super prepareForRecycle];
_textView.state = nullptr;
+ _textView.layoutMetrics = EmptyLayoutMetrics;
_accessibilityProvider = nil;
}
diff --git a/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputComponentView.mm b/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputComponentView.mm
index 8f55121cd23d27577bab45716d91d6cdad878e41..05529db1a074652d2ca45abd70e421484b38e87b 100644
--- a/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputComponentView.mm
+++ b/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputComponentView.mm
@@ -397,6 +397,7 @@ static NSSet<NSNumber *> *returnKeyTypesSet;
_backedTextInputView.inputAccessoryView = nil;
_hasInputAccessoryView = false;
[_backedTextInputView resignFirstResponder];
+ [_backedTextInputView resetDictationRecognition];
}
#pragma mark - RCTBackedTextInputDelegate
@@ -733,6 +734,7 @@ static NSSet<NSNumber *> *returnKeyTypesSet;
{
return {
.text = RCTStringFromNSString(_backedTextInputView.attributedText.string),
+ .isComposing = _backedTextInputView.markedTextRange != nil,
.selectionRange = [self _selectionRange],
.contentSize = RCTSizeFromCGSize(_backedTextInputView.contentSize),
.contentOffset = RCTPointFromCGPoint(_backedTextInputView.contentOffset),
@@ -892,8 +894,10 @@ static NSSet<NSNumber *> *returnKeyTypesSet;
}
}];
+ // Preserve the native recognition placeholder while iOS finalizes dictation.
BOOL shouldFallbackToBareTextComparison =
[_backedTextInputView.textInputMode.primaryLanguage isEqualToString:@"dictation"] ||
+ _backedTextInputView.dictationRecognizing ||
[_backedTextInputView.textInputMode.primaryLanguage isEqualToString:@"ko-KR"] ||
_backedTextInputView.markedTextRange || _backedTextInputView.isSecureTextEntry || fontHasBeenUpdatedBySystem;
diff --git a/ReactCommon/react/renderer/components/textinput/TextInputEventEmitter.cpp b/ReactCommon/react/renderer/components/textinput/TextInputEventEmitter.cpp
index 7da2622926dcb85968d0d8f17a2ec57098117bdd..42d814740d9cfd46bfd40f1cc5b9e851d3765fbd 100644
--- a/ReactCommon/react/renderer/components/textinput/TextInputEventEmitter.cpp
+++ b/ReactCommon/react/renderer/components/textinput/TextInputEventEmitter.cpp
@@ -24,6 +24,10 @@ static jsi::Value textInputMetricsPayload(
payload.setProperty(runtime, "eventCount", textInputMetrics.eventCount);
+ if (textInputMetrics.isComposing.has_value()) {
+ payload.setProperty(runtime, "isComposing", *textInputMetrics.isComposing);
+ }
+
if (includeSelectionState) {
auto selection = jsi::Object(runtime);
selection.setProperty(
diff --git a/ReactCommon/react/renderer/components/textinput/TextInputEventEmitter.h b/ReactCommon/react/renderer/components/textinput/TextInputEventEmitter.h
index 8c1a443f9e5f769ea0a1e50e818ab7aae63c72b2..cf6aa26c9a1b6469f110fb0d39e2456bb464bb4f 100644
--- a/ReactCommon/react/renderer/components/textinput/TextInputEventEmitter.h
+++ b/ReactCommon/react/renderer/components/textinput/TextInputEventEmitter.h
@@ -9,6 +9,8 @@
#include <react/cxxstableapi/FrameworksGuard.h>
+#include <optional>
+
#include <react/renderer/attributedstring/AttributedString.h>
#include <react/renderer/components/view/ViewEventEmitter.h>
@@ -20,6 +22,7 @@ class TextInputEventEmitter : public ViewEventEmitter {
struct Metrics {
std::string text;
+ std::optional<bool> isComposing;
AttributedString::Range selectionRange;
// ScrollView-like metrics
Size contentSize;
diff --git a/Libraries/WebSocket/WebSocket.js b/Libraries/WebSocket/WebSocket.js
index 2eccf43..b5dfad2 100644
--- a/Libraries/WebSocket/WebSocket.js
+++ b/Libraries/WebSocket/WebSocket.js
@@ -90,6 +90,8 @@ class WebSocket extends EventTarget {
_eventEmitter: NativeEventEmitter<WebSocketEventDefinitions>;
_subscriptions: Array<EventSubscription>;
_binaryType: ?BinaryType;
+ // Orca: opt-in per socket; binary messages arrive as {base64} so the app decodes them natively.
+ orcaBinaryAsBase64: boolean = false;
bufferedAmount: number;
extension: ?string;
@@ -241,11 +243,14 @@ class WebSocket extends EventTarget {
if (ev.id !== this._socketId) {
return;
}
- let data: Blob | BlobData | ArrayBuffer | string = ev.data;
+ let data: Blob | BlobData | ArrayBuffer | string | {base64: string} =
+ ev.data;
switch (ev.type) {
case 'binary':
- // $FlowFixMe[incompatible-type]
- data = base64.toByteArray(ev.data).buffer;
+ data = this.orcaBinaryAsBase64
+ ? {base64: ev.data}
+ : // $FlowFixMe[incompatible-type]
+ base64.toByteArray(ev.data).buffer;
break;
case 'blob':
data = BlobManager.createFromOptions(ev.data);