mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
Compare commits
145
Commits
@@ -37,11 +37,6 @@ ADD scripts /app/scripts
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
RUN scripts/buck/buck_fetch.sh
|
||||
|
||||
RUN buck build packages/react-native/ReactAndroid/src/main/java/com/facebook/react
|
||||
RUN buck build packages/react-native/ReactAndroid/src/main/java/com/facebook/react/shell
|
||||
|
||||
ADD . /app
|
||||
|
||||
RUN yarn
|
||||
|
||||
+29
-65
@@ -46,7 +46,7 @@ references:
|
||||
# Dependency Anchors
|
||||
# -------------------------
|
||||
dependency_versions:
|
||||
xcode_version: &xcode_version "14.2.0"
|
||||
xcode_version: &xcode_version "14.3.0"
|
||||
nodelts_image: &nodelts_image "cimg/node:18.12.1"
|
||||
nodeprevlts_image: &nodeprevlts_image "cimg/node:16.18.1"
|
||||
|
||||
@@ -62,7 +62,8 @@ references:
|
||||
hermes_workspace_cache_key: &hermes_workspace_cache_key v4-hermes-{{ .Environment.CIRCLE_JOB }}-{{ checksum "/tmp/hermes/hermesversion" }}
|
||||
hermes_workspace_debug_cache_key: &hermes_workspace_debug_cache_key v2-hermes-{{ .Environment.CIRCLE_JOB }}-debug-{{ checksum "/tmp/hermes/hermesversion" }}-{{ checksum "/tmp/react-native-version" }}
|
||||
hermes_workspace_release_cache_key: &hermes_workspace_release_cache_key v2-hermes-{{ .Environment.CIRCLE_JOB }}-release-{{ checksum "/tmp/hermes/hermesversion" }}-{{ checksum "/tmp/react-native-version" }}
|
||||
hermes_windows_cache_key: &hermes_windows_cache_key v3-hermes-{{ .Environment.CIRCLE_JOB }}-{{ checksum "tmp/hermes/hermesversion" }}
|
||||
hermes_linux_cache_key: &hermes_linux_cache_key v1-hermes-{{ .Environment.CIRCLE_JOB }}-linux-{{ checksum "/tmp/hermes/hermesversion" }}-{{ checksum "/tmp/react-native-version" }}
|
||||
hermes_windows_cache_key: &hermes_windows_cache_key v2-hermes-{{ .Environment.CIRCLE_JOB }}-windows-{{ checksum "/Users/circleci/project/tmp/hermes/hermesversion" }}-{{ checksum "/tmp/react-native-version" }}
|
||||
hermes_tarball_debug_cache_key: &hermes_tarball_debug_cache_key v4-hermes-tarball-debug-{{ checksum "/tmp/hermes/hermesversion" }}-{{ checksum "/tmp/react-native-version" }}
|
||||
hermes_tarball_release_cache_key: &hermes_tarball_release_cache_key v3-hermes-tarball-release-{{ checksum "/tmp/hermes/hermesversion" }}-{{ checksum "/tmp/react-native-version" }}
|
||||
pods_cache_key: &pods_cache_key v8-pods-{{ .Environment.CIRCLE_JOB }}-{{ checksum "packages/rn-tester/Podfile.lock.bak" }}-{{ checksum "packages/rn-tester/Podfile" }}
|
||||
@@ -314,11 +315,24 @@ commands:
|
||||
- run:
|
||||
name: Get React Native version
|
||||
command: |
|
||||
VERSION=$( grep '"version"' packages/react-native/package.json | cut -d '"' -f 4 | head -1)
|
||||
VERSION=$(cat packages/react-native/package.json | jq -r '.version')
|
||||
# Save the react native version we are building in a file so we can use that file as part of the cache key.
|
||||
echo "$VERSION" > /tmp/react-native-version
|
||||
echo "React Native Version is $(cat /tmp/react-native-version)"
|
||||
echo "Hermes commit is $(cat /tmp/hermes/hermesversion)"
|
||||
HERMES_VERSION="$(cat /tmp/hermes/hermesversion)"
|
||||
echo "Hermes commit is $HERMES_VERSION"
|
||||
|
||||
get_react_native_version_windows:
|
||||
steps:
|
||||
- run:
|
||||
name: Get React Native version on Windows
|
||||
command: |
|
||||
$VERSION=cat packages/react-native/package.json | jq -r '.version'
|
||||
# Save the react native version we are building in a file so we can use that file as part of the cache key.
|
||||
echo "$VERSION" > /tmp/react-native-version
|
||||
echo "React Native Version is $(cat /tmp/react-native-version)"
|
||||
$HERMES_VERSION=cat C:\Users\circleci\project\tmp\hermes\hermesversion
|
||||
echo "Hermes commit is $HERMES_VERSION"
|
||||
|
||||
with_hermes_tarball_cache_span:
|
||||
parameters:
|
||||
@@ -620,7 +634,7 @@ jobs:
|
||||
- run:
|
||||
name: Run Ruby Tests
|
||||
command: |
|
||||
cd scripts
|
||||
cd packages/react-native/scripts
|
||||
sh run_ruby_tests.sh
|
||||
- run_yarn
|
||||
- *attach_hermes_workspace
|
||||
@@ -719,60 +733,6 @@ jobs:
|
||||
- store_test_results:
|
||||
path: ./reports/junit
|
||||
|
||||
# -------------------------
|
||||
# JOBS: Test Buck
|
||||
# -------------------------
|
||||
test_buck:
|
||||
executor: reactnativeandroid
|
||||
environment:
|
||||
KOTLIN_HOME=packages/react-native/third-party/kotlin
|
||||
steps:
|
||||
- checkout
|
||||
- setup_artifacts
|
||||
- run_yarn
|
||||
|
||||
- run:
|
||||
name: Download Dependencies Using Buck
|
||||
command: ./scripts/buck/buck_fetch.sh
|
||||
|
||||
- run:
|
||||
name: Build & Test React Native using Buck
|
||||
command: |
|
||||
buck build packages/react-native/ReactAndroid/src/main/java/com/facebook/react
|
||||
buck build packages/react-native/ReactAndroid/src/main/java/com/facebook/react/shell
|
||||
|
||||
- run:
|
||||
name: Run Tests - Android Unit Tests with Buck
|
||||
command: buck test packages/react-native/ReactAndroid/src/test/... --config build.threads=$BUILD_THREADS --xml ./reports/buck/all-results-raw.xml
|
||||
|
||||
- run:
|
||||
name: Build JavaScript Bundle for instrumentation tests
|
||||
working_directory: ~/react-native/packages/react-native
|
||||
command: node cli.js bundle --max-workers 2 --platform android --dev true --entry-file ReactAndroid/src/androidTest/js/TestBundle.js --bundle-output ReactAndroid/src/androidTest/assets/AndroidTestBundle.js
|
||||
|
||||
- run:
|
||||
name: Build Tests - Android Instrumentation Tests with Buck
|
||||
# Here, just build the instrumentation tests. There is a known issue with installing the APK to android-21+ emulator.
|
||||
command: |
|
||||
if [[ ! -e packages/react-native/ReactAndroid/src/androidTest/assets/AndroidTestBundle.js ]]; then
|
||||
echo "JavaScript bundle missing, cannot run instrumentation tests. Verify Build JavaScript Bundle step completed successfully."; exit 1;
|
||||
fi
|
||||
source scripts/android-setup.sh && NO_BUCKD=1 scripts/retry3 timeout 300 buck build packages/react-native/ReactAndroid/src/androidTest/buck-runner:instrumentation-tests --config build.threads=$BUILD_THREADS
|
||||
|
||||
- run:
|
||||
name: Collect Test Results
|
||||
command: |
|
||||
find . -type f -regex ".*/build/test-results/debug/.*xml" -exec cp {} ./reports/build/ \;
|
||||
find . -type f -regex ".*/outputs/androidTest-results/connected/.*xml" -exec cp {} ./reports/outputs/ \;
|
||||
find . -type f -regex ".*/buck-out/gen/packages/react-native/ReactAndroid/src/test/.*/.*xml" -exec cp {} ./reports/buck/ \;
|
||||
if [ -f ~/react-native/reports/buck/all-results-raw.xml ]; then
|
||||
~/react-native/scripts/circleci/buckToJunit/buckToJunit.sh ~/react-native/reports/buck/all-results-raw.xml ~/react-native/reports/junit/results.xml
|
||||
fi
|
||||
when: always
|
||||
|
||||
- store_test_results:
|
||||
path: ./reports/junit
|
||||
|
||||
# -------------------------
|
||||
# JOBS: Test Android
|
||||
# -------------------------
|
||||
@@ -1175,7 +1135,7 @@ jobs:
|
||||
- image: debian:11
|
||||
environment:
|
||||
- HERMES_WS_DIR: *hermes_workspace_root
|
||||
- HERMES_VERSION_FILE: "sdks/.hermesversion"
|
||||
- HERMES_VERSION_FILE: "packages/react-native/sdks/.hermesversion"
|
||||
- BUILD_FROM_SOURCE: true
|
||||
steps:
|
||||
- run:
|
||||
@@ -1194,8 +1154,10 @@ jobs:
|
||||
mkdir -p "/tmp/hermes" "/tmp/hermes/download" "/tmp/hermes/hermes"
|
||||
|
||||
if [ -f "$HERMES_VERSION_FILE" ]; then
|
||||
echo "Hermes version file found. Using the latest commit"
|
||||
cat $HERMES_VERSION_FILE > /tmp/hermes/hermesversion
|
||||
else
|
||||
echo "No hermes version file found. Using the latest commit"
|
||||
HERMES_TAG_SHA=$(git ls-remote https://github.com/facebook/hermes main | cut -f 1 | tr -d '[:space:]')
|
||||
echo $HERMES_TAG_SHA > /tmp/hermes/hermesversion
|
||||
fi
|
||||
@@ -1226,17 +1188,18 @@ jobs:
|
||||
docker:
|
||||
- image: debian:bullseye
|
||||
resource_class: "xlarge"
|
||||
working_directory: /root
|
||||
steps:
|
||||
- checkout_code_with_cache
|
||||
- run:
|
||||
name: Install dependencies
|
||||
command: |
|
||||
apt update
|
||||
apt install -y git openssh-client cmake build-essential \
|
||||
libreadline-dev libicu-dev zip python3
|
||||
libreadline-dev libicu-dev jq zip python3
|
||||
- *attach_hermes_workspace
|
||||
- get_react_native_version
|
||||
- restore_cache:
|
||||
key: *hermes_workspace_cache_key
|
||||
key: *hermes_linux_cache_key
|
||||
- run:
|
||||
name: Set up workspace
|
||||
command: |
|
||||
@@ -1255,7 +1218,7 @@ jobs:
|
||||
cp /tmp/hermes/build/bin/hermesc /tmp/hermes/linux64-bin/.
|
||||
fi
|
||||
- save_cache:
|
||||
key: *hermes_workspace_cache_key
|
||||
key: *hermes_linux_cache_key
|
||||
paths:
|
||||
- /tmp/hermes/linux64-bin/
|
||||
- /tmp/hermes/hermes/destroot/
|
||||
@@ -1376,7 +1339,9 @@ jobs:
|
||||
- MSBUILD_DIR: 'C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\MSBuild\Current\Bin'
|
||||
- CMAKE_DIR: 'C:\Program Files\CMake\bin'
|
||||
steps:
|
||||
- checkout_code_with_cache
|
||||
- *attach_hermes_workspace
|
||||
- get_react_native_version_windows
|
||||
- restore_cache:
|
||||
key: *hermes_windows_cache_key
|
||||
- run:
|
||||
@@ -1694,7 +1659,6 @@ workflows:
|
||||
architecture: ["NewArch", "OldArch"]
|
||||
jsengine: ["Hermes", "JSC"]
|
||||
flavor: ["Debug", "Release"]
|
||||
- test_buck
|
||||
- test_ios_template:
|
||||
requires:
|
||||
- build_npm_package
|
||||
|
||||
+9
-9
@@ -3,22 +3,22 @@ GEM
|
||||
specs:
|
||||
CFPropertyList (3.0.6)
|
||||
rexml
|
||||
activesupport (7.0.4.2)
|
||||
activesupport (7.0.5)
|
||||
concurrent-ruby (~> 1.0, >= 1.0.2)
|
||||
i18n (>= 1.6, < 2)
|
||||
minitest (>= 5.1)
|
||||
tzinfo (~> 2.0)
|
||||
addressable (2.8.1)
|
||||
addressable (2.8.4)
|
||||
public_suffix (>= 2.0.2, < 6.0)
|
||||
algoliasearch (1.27.5)
|
||||
httpclient (~> 2.8, >= 2.8.3)
|
||||
json (>= 1.5.1)
|
||||
atomos (0.1.3)
|
||||
claide (1.1.0)
|
||||
cocoapods (1.12.0)
|
||||
cocoapods (1.12.1)
|
||||
addressable (~> 2.8)
|
||||
claide (>= 1.0.2, < 2.0)
|
||||
cocoapods-core (= 1.12.0)
|
||||
cocoapods-core (= 1.12.1)
|
||||
cocoapods-deintegrate (>= 1.0.3, < 2.0)
|
||||
cocoapods-downloader (>= 1.6.0, < 2.0)
|
||||
cocoapods-plugins (>= 1.0.0, < 2.0)
|
||||
@@ -33,7 +33,7 @@ GEM
|
||||
nap (~> 1.0)
|
||||
ruby-macho (>= 2.3.0, < 3.0)
|
||||
xcodeproj (>= 1.21.0, < 2.0)
|
||||
cocoapods-core (1.12.0)
|
||||
cocoapods-core (1.12.1)
|
||||
activesupport (>= 5.0, < 8)
|
||||
addressable (~> 2.8)
|
||||
algoliasearch (~> 1.0)
|
||||
@@ -62,10 +62,10 @@ GEM
|
||||
fuzzy_match (2.0.4)
|
||||
gh_inspector (1.1.3)
|
||||
httpclient (2.8.3)
|
||||
i18n (1.12.0)
|
||||
i18n (1.14.1)
|
||||
concurrent-ruby (~> 1.0)
|
||||
json (2.6.3)
|
||||
minitest (5.18.0)
|
||||
minitest (5.18.1)
|
||||
molinillo (0.8.0)
|
||||
nanaimo (0.3.0)
|
||||
nap (1.1.0)
|
||||
@@ -93,7 +93,7 @@ DEPENDENCIES
|
||||
cocoapods (~> 1.12)
|
||||
|
||||
RUBY VERSION
|
||||
ruby 3.2.0p0
|
||||
ruby 3.0.6p216
|
||||
|
||||
BUNDLED WITH
|
||||
2.4.7
|
||||
2.4.13
|
||||
|
||||
+8
-8
@@ -40,8 +40,7 @@
|
||||
"test-ios": "./scripts/objc-test.sh test",
|
||||
"test-typescript": "dtslint packages/react-native/types",
|
||||
"test-typescript-offline": "dtslint --localTs node_modules/typescript/lib packages/react-native/types",
|
||||
"bump-all-updated-packages": "node ./scripts/monorepo/bump-all-updated-packages",
|
||||
"align-package-versions": "node ./scripts/monorepo/align-package-versions.js"
|
||||
"bump-all-updated-packages": "node ./scripts/monorepo/bump-all-updated-packages"
|
||||
},
|
||||
"workspaces": [
|
||||
"packages/*"
|
||||
@@ -51,16 +50,17 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.20.0",
|
||||
"@babel/eslint-parser": "^7.19.0",
|
||||
"@babel/eslint-parser": "^7.20.0",
|
||||
"@babel/generator": "^7.20.0",
|
||||
"@babel/plugin-transform-regenerator": "^7.0.0",
|
||||
"@babel/plugin-transform-regenerator": "^7.20.0",
|
||||
"@definitelytyped/dtslint": "^0.0.127",
|
||||
"@jest/create-cache-key-function": "^29.2.1",
|
||||
"@reactions/component": "^2.0.2",
|
||||
"@react-native/metro-config": "^0.72.6",
|
||||
"@types/react": "^18.0.18",
|
||||
"@typescript-eslint/parser": "^5.30.5",
|
||||
"async": "^3.2.2",
|
||||
"babel-plugin-transform-flow-enums":"^0.0.2",
|
||||
"babel-plugin-transform-flow-enums": "^0.0.2",
|
||||
"clang-format": "^1.8.0",
|
||||
"connect": "^3.6.5",
|
||||
"coveralls": "^3.1.1",
|
||||
@@ -84,9 +84,9 @@
|
||||
"jest": "^29.2.1",
|
||||
"jest-junit": "^10.0.0",
|
||||
"jscodeshift": "^0.14.0",
|
||||
"metro-babel-register": "0.75.1",
|
||||
"metro-memory-fs": "0.75.1",
|
||||
"metro-react-native-babel-transformer": "0.75.1",
|
||||
"metro-babel-register": "0.76.5",
|
||||
"metro-memory-fs": "0.76.5",
|
||||
"metro-react-native-babel-transformer": "0.76.5",
|
||||
"mkdirp": "^0.5.1",
|
||||
"mock-fs": "^5.1.4",
|
||||
"prettier": "^2.4.1",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@react-native/eslint-config",
|
||||
"version": "0.72.1",
|
||||
"version": "0.72.2",
|
||||
"description": "ESLint config for React Native",
|
||||
"main": "index.js",
|
||||
"license": "MIT",
|
||||
@@ -12,7 +12,7 @@
|
||||
"homepage": "https://github.com/facebook/react-native/tree/HEAD/packages/eslint-config-react-native-community#readme",
|
||||
"dependencies": {
|
||||
"@babel/core": "^7.20.0",
|
||||
"@babel/eslint-parser": "^7.19.0",
|
||||
"@babel/eslint-parser": "^7.20.0",
|
||||
"@react-native/eslint-plugin": "^0.72.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.30.5",
|
||||
"@typescript-eslint/parser": "^5.30.5",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@react-native/eslint-plugin-specs",
|
||||
"version": "0.72.2",
|
||||
"version": "0.72.4",
|
||||
"description": "ESLint rules to validate NativeModule and Component Specs",
|
||||
"main": "index.js",
|
||||
"repository": {
|
||||
@@ -14,11 +14,11 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/core": "^7.20.0",
|
||||
"@babel/eslint-parser": "^7.19.0",
|
||||
"@babel/plugin-transform-flow-strip-types": "^7.0.0",
|
||||
"@babel/preset-flow": "^7.18.0",
|
||||
"@babel/eslint-parser": "^7.20.0",
|
||||
"@babel/plugin-transform-flow-strip-types": "^7.20.0",
|
||||
"@babel/preset-flow": "^7.20.0",
|
||||
"@react-native/codegen": "*",
|
||||
"flow-parser": "^0.185.0",
|
||||
"flow-parser": "^0.206.0",
|
||||
"make-dir": "^2.1.0",
|
||||
"pirates": "^4.0.1",
|
||||
"source-map-support": "0.5.0"
|
||||
|
||||
@@ -17,10 +17,10 @@
|
||||
"yargs": "^17.6.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "^7.19.0",
|
||||
"@babel/cli": "^7.20.0",
|
||||
"@babel/core": "^7.20.0",
|
||||
"@babel/preset-env": "^7.20.0",
|
||||
"@babel/preset-flow": "^7.18.0",
|
||||
"@babel/preset-flow": "^7.20.0",
|
||||
"jest": "^29.2.1"
|
||||
},
|
||||
"jest": {
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow
|
||||
* @noformat
|
||||
*/
|
||||
|
||||
/*:: import type {MetroConfig} from 'metro-config'; */
|
||||
|
||||
const {mergeConfig} = require('metro-config');
|
||||
|
||||
const INTERNAL_CALLSITES_REGEX = new RegExp(
|
||||
[
|
||||
'/Libraries/Renderer/implementations/.+\\.js$',
|
||||
'/Libraries/BatchedBridge/MessageQueue\\.js$',
|
||||
'/Libraries/YellowBox/.+\\.js$',
|
||||
'/Libraries/LogBox/.+\\.js$',
|
||||
'/Libraries/Core/Timers/.+\\.js$',
|
||||
'/Libraries/WebSocket/.+\\.js$',
|
||||
'/Libraries/vendor/.+\\.js$',
|
||||
'/node_modules/react-devtools-core/.+\\.js$',
|
||||
'/node_modules/react-refresh/.+\\.js$',
|
||||
'/node_modules/scheduler/.+\\.js$',
|
||||
'/node_modules/event-target-shim/.+\\.js$',
|
||||
'/node_modules/invariant/.+\\.js$',
|
||||
'/node_modules/react-native/index.js$',
|
||||
'/metro-runtime/.+\\.js$',
|
||||
'^\\[native code\\]$',
|
||||
].join('|'),
|
||||
);
|
||||
|
||||
/**
|
||||
* Get the base Metro configuration for a React Native project.
|
||||
*/
|
||||
|
||||
function getDefaultConfig(
|
||||
projectRoot /*: string */
|
||||
) /*: MetroConfig */ {
|
||||
return {
|
||||
resolver: {
|
||||
resolverMainFields: ['react-native', 'browser', 'main'],
|
||||
platforms: ['android', 'ios'],
|
||||
unstable_conditionNames: ['require', 'import', 'react-native'],
|
||||
},
|
||||
serializer: {
|
||||
getPolyfills: () => require('@react-native/js-polyfills')(),
|
||||
},
|
||||
server: {
|
||||
port: Number(process.env.RCT_METRO_PORT) || 8081,
|
||||
},
|
||||
symbolicator: {
|
||||
customizeFrame: (frame /*: $ReadOnly<{file: ?string, ...}>*/) => {
|
||||
const collapse = Boolean(
|
||||
frame.file && INTERNAL_CALLSITES_REGEX.test(frame.file),
|
||||
);
|
||||
return {collapse};
|
||||
},
|
||||
},
|
||||
transformer: {
|
||||
allowOptionalDependencies: true,
|
||||
assetRegistryPath: 'react-native/Libraries/Image/AssetRegistry',
|
||||
asyncRequireModulePath: require.resolve(
|
||||
'metro-runtime/src/modules/asyncRequire',
|
||||
),
|
||||
babelTransformerPath: require.resolve(
|
||||
'metro-react-native-babel-transformer',
|
||||
),
|
||||
getTransformOptions: async () => ({
|
||||
transform: {
|
||||
experimentalImportSupport: false,
|
||||
inlineRequires: true,
|
||||
},
|
||||
}),
|
||||
},
|
||||
watchFolders: [],
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = {getDefaultConfig, mergeConfig};
|
||||
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"name": "@react-native/metro-config",
|
||||
"version": "0.72.6",
|
||||
"description": "Metro configuration for React Native.",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git@github.com:facebook/react-native.git",
|
||||
"directory": "packages/metro-config"
|
||||
},
|
||||
"license": "MIT",
|
||||
"exports": "./index.js",
|
||||
"dependencies": {
|
||||
"@react-native/js-polyfills": "^0.72.1",
|
||||
"metro-config": "0.76.5",
|
||||
"metro-react-native-babel-transformer": "0.76.5",
|
||||
"metro-runtime": "0.76.5"
|
||||
}
|
||||
}
|
||||
+16
-16
@@ -193,7 +193,7 @@ enum NativeEnumTurboModuleStatusRegularEnum { Active, Paused, Off };
|
||||
|
||||
template <>
|
||||
struct Bridging<NativeEnumTurboModuleStatusRegularEnum> {
|
||||
static NativeEnumTurboModuleStatusRegularEnum fromJs(jsi::Runtime &rt, const jsi::String &rawValue, const std::shared_ptr<CallInvoker> &jsInvoker) {
|
||||
static NativeEnumTurboModuleStatusRegularEnum fromJs(jsi::Runtime &rt, const jsi::String &rawValue) {
|
||||
std::string value = rawValue.utf8(rt);
|
||||
if (value == \\"Active\\") {
|
||||
return NativeEnumTurboModuleStatusRegularEnum::Active;
|
||||
@@ -206,7 +206,7 @@ struct Bridging<NativeEnumTurboModuleStatusRegularEnum> {
|
||||
}
|
||||
}
|
||||
|
||||
static jsi::String toJs(jsi::Runtime &rt, NativeEnumTurboModuleStatusRegularEnum value, const std::shared_ptr<CallInvoker> &jsInvoker) {
|
||||
static jsi::String toJs(jsi::Runtime &rt, NativeEnumTurboModuleStatusRegularEnum value) {
|
||||
if (value == NativeEnumTurboModuleStatusRegularEnum::Active) {
|
||||
return bridging::toJs(rt, \\"Active\\");
|
||||
} else if (value == NativeEnumTurboModuleStatusRegularEnum::Paused) {
|
||||
@@ -225,7 +225,7 @@ enum NativeEnumTurboModuleStatusStrEnum { Active, Paused, Off };
|
||||
|
||||
template <>
|
||||
struct Bridging<NativeEnumTurboModuleStatusStrEnum> {
|
||||
static NativeEnumTurboModuleStatusStrEnum fromJs(jsi::Runtime &rt, const jsi::String &rawValue, const std::shared_ptr<CallInvoker> &jsInvoker) {
|
||||
static NativeEnumTurboModuleStatusStrEnum fromJs(jsi::Runtime &rt, const jsi::String &rawValue) {
|
||||
std::string value = rawValue.utf8(rt);
|
||||
if (value == \\"active\\") {
|
||||
return NativeEnumTurboModuleStatusStrEnum::Active;
|
||||
@@ -238,7 +238,7 @@ struct Bridging<NativeEnumTurboModuleStatusStrEnum> {
|
||||
}
|
||||
}
|
||||
|
||||
static jsi::String toJs(jsi::Runtime &rt, NativeEnumTurboModuleStatusStrEnum value, const std::shared_ptr<CallInvoker> &jsInvoker) {
|
||||
static jsi::String toJs(jsi::Runtime &rt, NativeEnumTurboModuleStatusStrEnum value) {
|
||||
if (value == NativeEnumTurboModuleStatusStrEnum::Active) {
|
||||
return bridging::toJs(rt, \\"active\\");
|
||||
} else if (value == NativeEnumTurboModuleStatusStrEnum::Paused) {
|
||||
@@ -257,7 +257,7 @@ enum NativeEnumTurboModuleStatusNumEnum { Active, Paused, Off };
|
||||
|
||||
template <>
|
||||
struct Bridging<NativeEnumTurboModuleStatusNumEnum> {
|
||||
static NativeEnumTurboModuleStatusNumEnum fromJs(jsi::Runtime &rt, const jsi::Value &rawValue, const std::shared_ptr<CallInvoker> &jsInvoker) {
|
||||
static NativeEnumTurboModuleStatusNumEnum fromJs(jsi::Runtime &rt, const jsi::Value &rawValue) {
|
||||
double value = (double)rawValue.asNumber();
|
||||
if (value == 2) {
|
||||
return NativeEnumTurboModuleStatusNumEnum::Active;
|
||||
@@ -270,7 +270,7 @@ struct Bridging<NativeEnumTurboModuleStatusNumEnum> {
|
||||
}
|
||||
}
|
||||
|
||||
static jsi::Value toJs(jsi::Runtime &rt, NativeEnumTurboModuleStatusNumEnum value, const std::shared_ptr<CallInvoker> &jsInvoker) {
|
||||
static jsi::Value toJs(jsi::Runtime &rt, NativeEnumTurboModuleStatusNumEnum value) {
|
||||
if (value == NativeEnumTurboModuleStatusNumEnum::Active) {
|
||||
return bridging::toJs(rt, 2);
|
||||
} else if (value == NativeEnumTurboModuleStatusNumEnum::Paused) {
|
||||
@@ -289,7 +289,7 @@ enum NativeEnumTurboModuleStatusFractionEnum { Active, Paused, Off };
|
||||
|
||||
template <>
|
||||
struct Bridging<NativeEnumTurboModuleStatusFractionEnum> {
|
||||
static NativeEnumTurboModuleStatusFractionEnum fromJs(jsi::Runtime &rt, const jsi::Value &rawValue, const std::shared_ptr<CallInvoker> &jsInvoker) {
|
||||
static NativeEnumTurboModuleStatusFractionEnum fromJs(jsi::Runtime &rt, const jsi::Value &rawValue) {
|
||||
double value = (double)rawValue.asNumber();
|
||||
if (value == 0.2f) {
|
||||
return NativeEnumTurboModuleStatusFractionEnum::Active;
|
||||
@@ -302,7 +302,7 @@ struct Bridging<NativeEnumTurboModuleStatusFractionEnum> {
|
||||
}
|
||||
}
|
||||
|
||||
static jsi::Value toJs(jsi::Runtime &rt, NativeEnumTurboModuleStatusFractionEnum value, const std::shared_ptr<CallInvoker> &jsInvoker) {
|
||||
static jsi::Value toJs(jsi::Runtime &rt, NativeEnumTurboModuleStatusFractionEnum value) {
|
||||
if (value == NativeEnumTurboModuleStatusFractionEnum::Active) {
|
||||
return bridging::toJs(rt, 0.2f);
|
||||
} else if (value == NativeEnumTurboModuleStatusFractionEnum::Paused) {
|
||||
@@ -2136,7 +2136,7 @@ enum NativeEnumTurboModuleStatusRegularEnum { Active, Paused, Off };
|
||||
|
||||
template <>
|
||||
struct Bridging<NativeEnumTurboModuleStatusRegularEnum> {
|
||||
static NativeEnumTurboModuleStatusRegularEnum fromJs(jsi::Runtime &rt, const jsi::String &rawValue, const std::shared_ptr<CallInvoker> &jsInvoker) {
|
||||
static NativeEnumTurboModuleStatusRegularEnum fromJs(jsi::Runtime &rt, const jsi::String &rawValue) {
|
||||
std::string value = rawValue.utf8(rt);
|
||||
if (value == \\"Active\\") {
|
||||
return NativeEnumTurboModuleStatusRegularEnum::Active;
|
||||
@@ -2149,7 +2149,7 @@ struct Bridging<NativeEnumTurboModuleStatusRegularEnum> {
|
||||
}
|
||||
}
|
||||
|
||||
static jsi::String toJs(jsi::Runtime &rt, NativeEnumTurboModuleStatusRegularEnum value, const std::shared_ptr<CallInvoker> &jsInvoker) {
|
||||
static jsi::String toJs(jsi::Runtime &rt, NativeEnumTurboModuleStatusRegularEnum value) {
|
||||
if (value == NativeEnumTurboModuleStatusRegularEnum::Active) {
|
||||
return bridging::toJs(rt, \\"Active\\");
|
||||
} else if (value == NativeEnumTurboModuleStatusRegularEnum::Paused) {
|
||||
@@ -2168,7 +2168,7 @@ enum NativeEnumTurboModuleStatusStrEnum { Active, Paused, Off };
|
||||
|
||||
template <>
|
||||
struct Bridging<NativeEnumTurboModuleStatusStrEnum> {
|
||||
static NativeEnumTurboModuleStatusStrEnum fromJs(jsi::Runtime &rt, const jsi::String &rawValue, const std::shared_ptr<CallInvoker> &jsInvoker) {
|
||||
static NativeEnumTurboModuleStatusStrEnum fromJs(jsi::Runtime &rt, const jsi::String &rawValue) {
|
||||
std::string value = rawValue.utf8(rt);
|
||||
if (value == \\"active\\") {
|
||||
return NativeEnumTurboModuleStatusStrEnum::Active;
|
||||
@@ -2181,7 +2181,7 @@ struct Bridging<NativeEnumTurboModuleStatusStrEnum> {
|
||||
}
|
||||
}
|
||||
|
||||
static jsi::String toJs(jsi::Runtime &rt, NativeEnumTurboModuleStatusStrEnum value, const std::shared_ptr<CallInvoker> &jsInvoker) {
|
||||
static jsi::String toJs(jsi::Runtime &rt, NativeEnumTurboModuleStatusStrEnum value) {
|
||||
if (value == NativeEnumTurboModuleStatusStrEnum::Active) {
|
||||
return bridging::toJs(rt, \\"active\\");
|
||||
} else if (value == NativeEnumTurboModuleStatusStrEnum::Paused) {
|
||||
@@ -2200,7 +2200,7 @@ enum NativeEnumTurboModuleStatusNumEnum { Active, Paused, Off };
|
||||
|
||||
template <>
|
||||
struct Bridging<NativeEnumTurboModuleStatusNumEnum> {
|
||||
static NativeEnumTurboModuleStatusNumEnum fromJs(jsi::Runtime &rt, const jsi::Value &rawValue, const std::shared_ptr<CallInvoker> &jsInvoker) {
|
||||
static NativeEnumTurboModuleStatusNumEnum fromJs(jsi::Runtime &rt, const jsi::Value &rawValue) {
|
||||
double value = (double)rawValue.asNumber();
|
||||
if (value == 2) {
|
||||
return NativeEnumTurboModuleStatusNumEnum::Active;
|
||||
@@ -2213,7 +2213,7 @@ struct Bridging<NativeEnumTurboModuleStatusNumEnum> {
|
||||
}
|
||||
}
|
||||
|
||||
static jsi::Value toJs(jsi::Runtime &rt, NativeEnumTurboModuleStatusNumEnum value, const std::shared_ptr<CallInvoker> &jsInvoker) {
|
||||
static jsi::Value toJs(jsi::Runtime &rt, NativeEnumTurboModuleStatusNumEnum value) {
|
||||
if (value == NativeEnumTurboModuleStatusNumEnum::Active) {
|
||||
return bridging::toJs(rt, 2);
|
||||
} else if (value == NativeEnumTurboModuleStatusNumEnum::Paused) {
|
||||
@@ -2232,7 +2232,7 @@ enum NativeEnumTurboModuleStatusFractionEnum { Active, Paused, Off };
|
||||
|
||||
template <>
|
||||
struct Bridging<NativeEnumTurboModuleStatusFractionEnum> {
|
||||
static NativeEnumTurboModuleStatusFractionEnum fromJs(jsi::Runtime &rt, const jsi::Value &rawValue, const std::shared_ptr<CallInvoker> &jsInvoker) {
|
||||
static NativeEnumTurboModuleStatusFractionEnum fromJs(jsi::Runtime &rt, const jsi::Value &rawValue) {
|
||||
double value = (double)rawValue.asNumber();
|
||||
if (value == 0.2f) {
|
||||
return NativeEnumTurboModuleStatusFractionEnum::Active;
|
||||
@@ -2245,7 +2245,7 @@ struct Bridging<NativeEnumTurboModuleStatusFractionEnum> {
|
||||
}
|
||||
}
|
||||
|
||||
static jsi::Value toJs(jsi::Runtime &rt, NativeEnumTurboModuleStatusFractionEnum value, const std::shared_ptr<CallInvoker> &jsInvoker) {
|
||||
static jsi::Value toJs(jsi::Runtime &rt, NativeEnumTurboModuleStatusFractionEnum value) {
|
||||
if (value == NativeEnumTurboModuleStatusFractionEnum::Active) {
|
||||
return bridging::toJs(rt, 0.2f);
|
||||
} else if (value == NativeEnumTurboModuleStatusFractionEnum::Paused) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@react-native/codegen",
|
||||
"version": "0.72.3",
|
||||
"version": "0.72.6",
|
||||
"description": "⚛️ Code generation tools for React Native",
|
||||
"homepage": "https://github.com/facebook/react-native/tree/HEAD/packages/react-native-codegen",
|
||||
"repository": {
|
||||
@@ -19,21 +19,21 @@
|
||||
],
|
||||
"dependencies": {
|
||||
"@babel/parser": "^7.20.0",
|
||||
"flow-parser": "^0.185.0",
|
||||
"flow-parser": "^0.206.0",
|
||||
"jscodeshift": "^0.14.0",
|
||||
"nullthrows": "^1.1.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.20.0",
|
||||
"@babel/plugin-proposal-class-properties": "^7.0.0",
|
||||
"@babel/plugin-proposal-nullish-coalescing-operator": "^7.0.0",
|
||||
"@babel/plugin-proposal-object-rest-spread": "^7.0.0",
|
||||
"@babel/plugin-proposal-optional-chaining": "^7.0.0",
|
||||
"@babel/plugin-syntax-dynamic-import": "^7.0.0",
|
||||
"@babel/plugin-transform-async-to-generator": "^7.0.0",
|
||||
"@babel/plugin-transform-destructuring": "^7.0.0",
|
||||
"@babel/plugin-transform-flow-strip-types": "^7.0.0",
|
||||
"@babel/preset-env": "^7.14.0",
|
||||
"@babel/plugin-proposal-class-properties": "^7.18.0",
|
||||
"@babel/plugin-proposal-nullish-coalescing-operator": "^7.18.0",
|
||||
"@babel/plugin-proposal-object-rest-spread": "^7.20.0",
|
||||
"@babel/plugin-proposal-optional-chaining": "^7.20.0",
|
||||
"@babel/plugin-syntax-dynamic-import": "^7.8.0",
|
||||
"@babel/plugin-transform-async-to-generator": "^7.20.0",
|
||||
"@babel/plugin-transform-destructuring": "^7.20.0",
|
||||
"@babel/plugin-transform-flow-strip-types": "^7.20.0",
|
||||
"@babel/preset-env": "^7.20.0",
|
||||
"chalk": "^4.0.0",
|
||||
"glob": "^7.1.1",
|
||||
"invariant": "^2.2.4",
|
||||
|
||||
@@ -338,12 +338,12 @@ enum ${enumName} { ${values} };
|
||||
|
||||
template <>
|
||||
struct Bridging<${enumName}> {
|
||||
static ${enumName} fromJs(jsi::Runtime &rt, ${fromValue}, const std::shared_ptr<CallInvoker> &jsInvoker) {
|
||||
static ${enumName} fromJs(jsi::Runtime &rt, ${fromValue}) {
|
||||
${fromValueConversion}
|
||||
${fromCases}
|
||||
}
|
||||
|
||||
static ${toValue} toJs(jsi::Runtime &rt, ${enumName} value, const std::shared_ptr<CallInvoker> &jsInvoker) {
|
||||
static ${toValue} toJs(jsi::Runtime &rt, ${enumName} value) {
|
||||
${toCases}
|
||||
}
|
||||
};`;
|
||||
|
||||
+12
-12
@@ -211,7 +211,7 @@ enum SampleTurboModuleCxxNumEnum { ONE, TWO };
|
||||
|
||||
template <>
|
||||
struct Bridging<SampleTurboModuleCxxNumEnum> {
|
||||
static SampleTurboModuleCxxNumEnum fromJs(jsi::Runtime &rt, const jsi::Value &rawValue, const std::shared_ptr<CallInvoker> &jsInvoker) {
|
||||
static SampleTurboModuleCxxNumEnum fromJs(jsi::Runtime &rt, const jsi::Value &rawValue) {
|
||||
double value = (double)rawValue.asNumber();
|
||||
if (value == 1) {
|
||||
return SampleTurboModuleCxxNumEnum::ONE;
|
||||
@@ -222,7 +222,7 @@ struct Bridging<SampleTurboModuleCxxNumEnum> {
|
||||
}
|
||||
}
|
||||
|
||||
static jsi::Value toJs(jsi::Runtime &rt, SampleTurboModuleCxxNumEnum value, const std::shared_ptr<CallInvoker> &jsInvoker) {
|
||||
static jsi::Value toJs(jsi::Runtime &rt, SampleTurboModuleCxxNumEnum value) {
|
||||
if (value == SampleTurboModuleCxxNumEnum::ONE) {
|
||||
return bridging::toJs(rt, 1);
|
||||
} else if (value == SampleTurboModuleCxxNumEnum::TWO) {
|
||||
@@ -239,7 +239,7 @@ enum SampleTurboModuleCxxFloatEnum { POINT_ZERO, POINT_ONE, POINT_TWO };
|
||||
|
||||
template <>
|
||||
struct Bridging<SampleTurboModuleCxxFloatEnum> {
|
||||
static SampleTurboModuleCxxFloatEnum fromJs(jsi::Runtime &rt, const jsi::Value &rawValue, const std::shared_ptr<CallInvoker> &jsInvoker) {
|
||||
static SampleTurboModuleCxxFloatEnum fromJs(jsi::Runtime &rt, const jsi::Value &rawValue) {
|
||||
double value = (double)rawValue.asNumber();
|
||||
if (value == 0.0f) {
|
||||
return SampleTurboModuleCxxFloatEnum::POINT_ZERO;
|
||||
@@ -252,7 +252,7 @@ struct Bridging<SampleTurboModuleCxxFloatEnum> {
|
||||
}
|
||||
}
|
||||
|
||||
static jsi::Value toJs(jsi::Runtime &rt, SampleTurboModuleCxxFloatEnum value, const std::shared_ptr<CallInvoker> &jsInvoker) {
|
||||
static jsi::Value toJs(jsi::Runtime &rt, SampleTurboModuleCxxFloatEnum value) {
|
||||
if (value == SampleTurboModuleCxxFloatEnum::POINT_ZERO) {
|
||||
return bridging::toJs(rt, 0.0f);
|
||||
} else if (value == SampleTurboModuleCxxFloatEnum::POINT_ONE) {
|
||||
@@ -271,7 +271,7 @@ enum SampleTurboModuleCxxStringEnum { HELLO, GoodBye };
|
||||
|
||||
template <>
|
||||
struct Bridging<SampleTurboModuleCxxStringEnum> {
|
||||
static SampleTurboModuleCxxStringEnum fromJs(jsi::Runtime &rt, const jsi::String &rawValue, const std::shared_ptr<CallInvoker> &jsInvoker) {
|
||||
static SampleTurboModuleCxxStringEnum fromJs(jsi::Runtime &rt, const jsi::String &rawValue) {
|
||||
std::string value = rawValue.utf8(rt);
|
||||
if (value == \\"hello\\") {
|
||||
return SampleTurboModuleCxxStringEnum::HELLO;
|
||||
@@ -282,7 +282,7 @@ struct Bridging<SampleTurboModuleCxxStringEnum> {
|
||||
}
|
||||
}
|
||||
|
||||
static jsi::String toJs(jsi::Runtime &rt, SampleTurboModuleCxxStringEnum value, const std::shared_ptr<CallInvoker> &jsInvoker) {
|
||||
static jsi::String toJs(jsi::Runtime &rt, SampleTurboModuleCxxStringEnum value) {
|
||||
if (value == SampleTurboModuleCxxStringEnum::HELLO) {
|
||||
return bridging::toJs(rt, \\"hello\\");
|
||||
} else if (value == SampleTurboModuleCxxStringEnum::GoodBye) {
|
||||
@@ -1209,7 +1209,7 @@ enum SampleTurboModuleNumEnum { ONE, TWO };
|
||||
|
||||
template <>
|
||||
struct Bridging<SampleTurboModuleNumEnum> {
|
||||
static SampleTurboModuleNumEnum fromJs(jsi::Runtime &rt, const jsi::Value &rawValue, const std::shared_ptr<CallInvoker> &jsInvoker) {
|
||||
static SampleTurboModuleNumEnum fromJs(jsi::Runtime &rt, const jsi::Value &rawValue) {
|
||||
double value = (double)rawValue.asNumber();
|
||||
if (value == 1) {
|
||||
return SampleTurboModuleNumEnum::ONE;
|
||||
@@ -1220,7 +1220,7 @@ struct Bridging<SampleTurboModuleNumEnum> {
|
||||
}
|
||||
}
|
||||
|
||||
static jsi::Value toJs(jsi::Runtime &rt, SampleTurboModuleNumEnum value, const std::shared_ptr<CallInvoker> &jsInvoker) {
|
||||
static jsi::Value toJs(jsi::Runtime &rt, SampleTurboModuleNumEnum value) {
|
||||
if (value == SampleTurboModuleNumEnum::ONE) {
|
||||
return bridging::toJs(rt, 1);
|
||||
} else if (value == SampleTurboModuleNumEnum::TWO) {
|
||||
@@ -1237,7 +1237,7 @@ enum SampleTurboModuleFloatEnum { POINT_ZERO, POINT_ONE, POINT_TWO };
|
||||
|
||||
template <>
|
||||
struct Bridging<SampleTurboModuleFloatEnum> {
|
||||
static SampleTurboModuleFloatEnum fromJs(jsi::Runtime &rt, const jsi::Value &rawValue, const std::shared_ptr<CallInvoker> &jsInvoker) {
|
||||
static SampleTurboModuleFloatEnum fromJs(jsi::Runtime &rt, const jsi::Value &rawValue) {
|
||||
double value = (double)rawValue.asNumber();
|
||||
if (value == 0.0f) {
|
||||
return SampleTurboModuleFloatEnum::POINT_ZERO;
|
||||
@@ -1250,7 +1250,7 @@ struct Bridging<SampleTurboModuleFloatEnum> {
|
||||
}
|
||||
}
|
||||
|
||||
static jsi::Value toJs(jsi::Runtime &rt, SampleTurboModuleFloatEnum value, const std::shared_ptr<CallInvoker> &jsInvoker) {
|
||||
static jsi::Value toJs(jsi::Runtime &rt, SampleTurboModuleFloatEnum value) {
|
||||
if (value == SampleTurboModuleFloatEnum::POINT_ZERO) {
|
||||
return bridging::toJs(rt, 0.0f);
|
||||
} else if (value == SampleTurboModuleFloatEnum::POINT_ONE) {
|
||||
@@ -1269,7 +1269,7 @@ enum SampleTurboModuleStringEnum { HELLO, GoodBye };
|
||||
|
||||
template <>
|
||||
struct Bridging<SampleTurboModuleStringEnum> {
|
||||
static SampleTurboModuleStringEnum fromJs(jsi::Runtime &rt, const jsi::String &rawValue, const std::shared_ptr<CallInvoker> &jsInvoker) {
|
||||
static SampleTurboModuleStringEnum fromJs(jsi::Runtime &rt, const jsi::String &rawValue) {
|
||||
std::string value = rawValue.utf8(rt);
|
||||
if (value == \\"hello\\") {
|
||||
return SampleTurboModuleStringEnum::HELLO;
|
||||
@@ -1280,7 +1280,7 @@ struct Bridging<SampleTurboModuleStringEnum> {
|
||||
}
|
||||
}
|
||||
|
||||
static jsi::String toJs(jsi::Runtime &rt, SampleTurboModuleStringEnum value, const std::shared_ptr<CallInvoker> &jsInvoker) {
|
||||
static jsi::String toJs(jsi::Runtime &rt, SampleTurboModuleStringEnum value) {
|
||||
if (value == SampleTurboModuleStringEnum::HELLO) {
|
||||
return bridging::toJs(rt, \\"hello\\");
|
||||
} else if (value == SampleTurboModuleStringEnum::GoodBye) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@react-native/gradle-plugin",
|
||||
"version": "0.72.5",
|
||||
"version": "0.72.10",
|
||||
"description": "⚛️ Gradle Plugin for React Native",
|
||||
"homepage": "https://github.com/facebook/react-native/tree/HEAD/packages/react-native-gradle-plugin",
|
||||
"repository": {
|
||||
|
||||
+9
-3
@@ -18,7 +18,8 @@ import com.facebook.react.utils.AgpConfiguratorUtils.configureDevPorts
|
||||
import com.facebook.react.utils.BackwardCompatUtils.configureBackwardCompatibilityReactMap
|
||||
import com.facebook.react.utils.DependencyUtils.configureDependencies
|
||||
import com.facebook.react.utils.DependencyUtils.configureRepositories
|
||||
import com.facebook.react.utils.DependencyUtils.readVersionString
|
||||
import com.facebook.react.utils.DependencyUtils.readVersionAndGroupStrings
|
||||
import com.facebook.react.utils.JdkConfiguratorUtils.configureJavaToolChains
|
||||
import com.facebook.react.utils.JsonUtils
|
||||
import com.facebook.react.utils.NdkConfiguratorUtils.configureReactNativeNdk
|
||||
import com.facebook.react.utils.ProjectUtils.needsCodegenFromPackageJson
|
||||
@@ -54,8 +55,10 @@ class ReactPlugin : Plugin<Project> {
|
||||
project.afterEvaluate {
|
||||
val reactNativeDir = extension.reactNativeDir.get().asFile
|
||||
val propertiesFile = File(reactNativeDir, "ReactAndroid/gradle.properties")
|
||||
val versionString = readVersionString(propertiesFile)
|
||||
configureDependencies(project, versionString)
|
||||
val versionAndGroupStrings = readVersionAndGroupStrings(propertiesFile)
|
||||
val versionString = versionAndGroupStrings.first
|
||||
val groupString = versionAndGroupStrings.second
|
||||
configureDependencies(project, versionString, groupString)
|
||||
configureRepositories(project, reactNativeDir)
|
||||
}
|
||||
|
||||
@@ -76,6 +79,9 @@ class ReactPlugin : Plugin<Project> {
|
||||
project.pluginManager.withPlugin("com.android.library") {
|
||||
configureCodegen(project, extension, rootExtension, isLibrary = true)
|
||||
}
|
||||
|
||||
// App and Library Configurations
|
||||
configureJavaToolChains(project)
|
||||
}
|
||||
|
||||
private fun checkJvmVersion(project: Project) {
|
||||
|
||||
+1
@@ -174,6 +174,7 @@ abstract class BundleHermesCTask : DefaultTask() {
|
||||
return windowsAwareCommandLine(
|
||||
hermesCommand,
|
||||
"-emit-binary",
|
||||
"-max-diagnostic-width=80",
|
||||
"-out",
|
||||
bytecodeFile.cliPath(rootFile),
|
||||
bundleFile.cliPath(rootFile),
|
||||
|
||||
+32
-12
@@ -13,6 +13,8 @@ import java.util.*
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.artifacts.repositories.MavenArtifactRepository
|
||||
|
||||
internal const val DEFAULT_GROUP_STRING = "com.facebook.react"
|
||||
|
||||
internal object DependencyUtils {
|
||||
|
||||
/**
|
||||
@@ -46,7 +48,11 @@ internal object DependencyUtils {
|
||||
* - Forcing the react-android/hermes-android version to the one specified in the package.json
|
||||
* - Substituting `react-native` with `react-android` and `hermes-engine` with `hermes-android`.
|
||||
*/
|
||||
fun configureDependencies(project: Project, versionString: String) {
|
||||
fun configureDependencies(
|
||||
project: Project,
|
||||
versionString: String,
|
||||
groupString: String = DEFAULT_GROUP_STRING
|
||||
) {
|
||||
if (versionString.isBlank()) return
|
||||
project.rootProject.allprojects { eachProject ->
|
||||
eachProject.configurations.all { configuration ->
|
||||
@@ -56,32 +62,46 @@ internal object DependencyUtils {
|
||||
// implementation("com.facebook.react:react-native:+") and resolve the right dependency.
|
||||
configuration.resolutionStrategy.dependencySubstitution {
|
||||
it.substitute(it.module("com.facebook.react:react-native"))
|
||||
.using(it.module("com.facebook.react:react-android:${versionString}"))
|
||||
.using(it.module("${groupString}:react-android:${versionString}"))
|
||||
.because(
|
||||
"The react-native artifact was deprecated in favor of react-android due to https://github.com/facebook/react-native/issues/35210.")
|
||||
it.substitute(it.module("com.facebook.react:hermes-engine"))
|
||||
.using(it.module("com.facebook.react:hermes-android:${versionString}"))
|
||||
.using(it.module("${groupString}:hermes-android:${versionString}"))
|
||||
.because(
|
||||
"The hermes-engine artifact was deprecated in favor of hermes-android due to https://github.com/facebook/react-native/issues/35210.")
|
||||
if (groupString != DEFAULT_GROUP_STRING) {
|
||||
it.substitute(it.module("com.facebook.react:react-android"))
|
||||
.using(it.module("${groupString}:react-android:${versionString}"))
|
||||
.because(
|
||||
"The react-android dependency was modified to use the correct Maven group.")
|
||||
it.substitute(it.module("com.facebook.react:hermes-android"))
|
||||
.using(it.module("${groupString}:hermes-android:${versionString}"))
|
||||
.because(
|
||||
"The hermes-android dependency was modified to use the correct Maven group.")
|
||||
}
|
||||
}
|
||||
configuration.resolutionStrategy.force(
|
||||
"com.facebook.react:react-android:${versionString}",
|
||||
"com.facebook.react:hermes-android:${versionString}",
|
||||
"${groupString}:react-android:${versionString}",
|
||||
"${groupString}:hermes-android:${versionString}",
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun readVersionString(propertiesFile: File): String {
|
||||
fun readVersionAndGroupStrings(propertiesFile: File): Pair<String, String> {
|
||||
val reactAndroidProperties = Properties()
|
||||
propertiesFile.inputStream().use { reactAndroidProperties.load(it) }
|
||||
val versionString = reactAndroidProperties["VERSION_NAME"] as? String ?: ""
|
||||
val versionStringFromFile = reactAndroidProperties["VERSION_NAME"] as? String ?: ""
|
||||
// If on a nightly, we need to fetch the -SNAPSHOT artifact from Sonatype.
|
||||
return if (versionString.startsWith("0.0.0")) {
|
||||
"$versionString-SNAPSHOT"
|
||||
} else {
|
||||
versionString
|
||||
}
|
||||
val versionString =
|
||||
if (versionStringFromFile.startsWith("0.0.0")) {
|
||||
"$versionStringFromFile-SNAPSHOT"
|
||||
} else {
|
||||
versionStringFromFile
|
||||
}
|
||||
// Returns Maven group for repos using different group for Maven artifacts
|
||||
val groupString = reactAndroidProperties["GROUP"] as? String ?: DEFAULT_GROUP_STRING
|
||||
return Pair(versionString, groupString)
|
||||
}
|
||||
|
||||
fun Project.mavenRepoFromUrl(url: String): MavenArtifactRepository =
|
||||
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package com.facebook.react.utils
|
||||
|
||||
import com.android.build.api.variant.AndroidComponentsExtension
|
||||
import com.facebook.react.utils.PropertyUtils.INTERNAL_DISABLE_JAVA_VERSION_ALIGNMENT
|
||||
import org.gradle.api.Action
|
||||
import org.gradle.api.JavaVersion
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.plugins.AppliedPlugin
|
||||
|
||||
internal object JdkConfiguratorUtils {
|
||||
/**
|
||||
* Function that takes care of configuring the JDK toolchain for all the projects projects. As we
|
||||
* do decide the JDK version based on the AGP version that RNGP brings over, here we can safely
|
||||
* configure the toolchain to 11.
|
||||
*/
|
||||
fun configureJavaToolChains(input: Project) {
|
||||
if (input.hasProperty(INTERNAL_DISABLE_JAVA_VERSION_ALIGNMENT)) {
|
||||
return
|
||||
}
|
||||
input.rootProject.allprojects { project ->
|
||||
val action =
|
||||
Action<AppliedPlugin> {
|
||||
project.extensions.getByType(AndroidComponentsExtension::class.java).finalizeDsl {
|
||||
ext ->
|
||||
ext.compileOptions.sourceCompatibility = JavaVersion.VERSION_11
|
||||
ext.compileOptions.targetCompatibility = JavaVersion.VERSION_11
|
||||
}
|
||||
}
|
||||
project.pluginManager.withPlugin("com.android.application", action)
|
||||
project.pluginManager.withPlugin("com.android.library", action)
|
||||
}
|
||||
}
|
||||
}
|
||||
+9
-3
@@ -40,16 +40,22 @@ internal object NdkConfiguratorUtils {
|
||||
// Parameters should be provided in an additive manner (do not override what
|
||||
// the user provided, but allow for sensible defaults).
|
||||
val cmakeArgs = ext.defaultConfig.externalNativeBuild.cmake.arguments
|
||||
if ("-DPROJECT_BUILD_DIR" !in cmakeArgs) {
|
||||
if (cmakeArgs.none { it.startsWith("-DPROJECT_BUILD_DIR") }) {
|
||||
cmakeArgs.add("-DPROJECT_BUILD_DIR=${project.buildDir}")
|
||||
}
|
||||
if ("-DREACT_ANDROID_DIR" !in cmakeArgs) {
|
||||
if (cmakeArgs.none { it.startsWith("-DREACT_ANDROID_DIR") }) {
|
||||
cmakeArgs.add(
|
||||
"-DREACT_ANDROID_DIR=${extension.reactNativeDir.file("ReactAndroid").get().asFile}")
|
||||
}
|
||||
if ("-DANDROID_STL" !in cmakeArgs) {
|
||||
if (cmakeArgs.none { it.startsWith("-DANDROID_STL") }) {
|
||||
cmakeArgs.add("-DANDROID_STL=c++_shared")
|
||||
}
|
||||
// Due to the new NDK toolchain file, the C++ flags gets overridden between compilation
|
||||
// units. This is causing some libraries to don't be compiled with -DANDROID and other
|
||||
// crucial flags. This can be revisited once we bump to NDK 25/26
|
||||
if (cmakeArgs.none { it.startsWith("-DANDROID_USE_LEGACY_TOOLCHAIN_FILE") }) {
|
||||
cmakeArgs.add("-DANDROID_USE_LEGACY_TOOLCHAIN_FILE=ON")
|
||||
}
|
||||
|
||||
val architectures = project.getReactNativeArchitectures()
|
||||
// abiFilters are split ABI are not compatible each other, so we set the abiFilters
|
||||
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package com.facebook.react.utils
|
||||
|
||||
/** Collection of all the Gradle Propreties that are accepted by React Native Gradle Plugin. */
|
||||
object PropertyUtils {
|
||||
|
||||
/**
|
||||
* Internal Property that acts as a killswitch to configure the JDK version and align it for app
|
||||
* and all the libraries.
|
||||
*/
|
||||
const val INTERNAL_DISABLE_JAVA_VERSION_ALIGNMENT = "react.internal.disableJavaVersionAlignment"
|
||||
}
|
||||
+12
-10
@@ -341,11 +341,12 @@ class BundleHermesCTaskTest {
|
||||
|
||||
assertEquals(customHermesc, hermesCommand[0])
|
||||
assertEquals("-emit-binary", hermesCommand[1])
|
||||
assertEquals("-out", hermesCommand[2])
|
||||
assertEquals(bytecodeFile.absolutePath, hermesCommand[3])
|
||||
assertEquals(bundleFile.absolutePath, hermesCommand[4])
|
||||
assertEquals("my-custom-hermes-flag", hermesCommand[5])
|
||||
assertEquals(6, hermesCommand.size)
|
||||
assertEquals("-max-diagnostic-width=80", hermesCommand[2])
|
||||
assertEquals("-out", hermesCommand[3])
|
||||
assertEquals(bytecodeFile.absolutePath, hermesCommand[4])
|
||||
assertEquals(bundleFile.absolutePath, hermesCommand[5])
|
||||
assertEquals("my-custom-hermes-flag", hermesCommand[6])
|
||||
assertEquals(7, hermesCommand.size)
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -366,11 +367,12 @@ class BundleHermesCTaskTest {
|
||||
assertEquals("/c", hermesCommand[1])
|
||||
assertEquals(customHermesc, hermesCommand[2])
|
||||
assertEquals("-emit-binary", hermesCommand[3])
|
||||
assertEquals("-out", hermesCommand[4])
|
||||
assertEquals(bytecodeFile.relativeTo(tempFolder.root).path, hermesCommand[5])
|
||||
assertEquals(bundleFile.relativeTo(tempFolder.root).path, hermesCommand[6])
|
||||
assertEquals("my-custom-hermes-flag", hermesCommand[7])
|
||||
assertEquals(8, hermesCommand.size)
|
||||
assertEquals("-max-diagnostic-width=80", hermesCommand[4])
|
||||
assertEquals("-out", hermesCommand[5])
|
||||
assertEquals(bytecodeFile.relativeTo(tempFolder.root).path, hermesCommand[6])
|
||||
assertEquals(bundleFile.relativeTo(tempFolder.root).path, hermesCommand[7])
|
||||
assertEquals("my-custom-hermes-flag", hermesCommand[8])
|
||||
assertEquals(9, hermesCommand.size)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+56
-5
@@ -12,7 +12,7 @@ import com.facebook.react.utils.DependencyUtils.configureDependencies
|
||||
import com.facebook.react.utils.DependencyUtils.configureRepositories
|
||||
import com.facebook.react.utils.DependencyUtils.mavenRepoFromURI
|
||||
import com.facebook.react.utils.DependencyUtils.mavenRepoFromUrl
|
||||
import com.facebook.react.utils.DependencyUtils.readVersionString
|
||||
import com.facebook.react.utils.DependencyUtils.readVersionAndGroupStrings
|
||||
import java.net.URI
|
||||
import org.gradle.api.artifacts.repositories.MavenArtifactRepository
|
||||
import org.gradle.testfixtures.ProjectBuilder
|
||||
@@ -226,6 +226,24 @@ class DependencyUtilsTest {
|
||||
assertTrue(libForcedModules.any { it.toString() == "com.facebook.react:hermes-android:1.2.3" })
|
||||
}
|
||||
|
||||
@Test
|
||||
fun configureDependencies_withVersionStringAndGroupString_appliesOnAllProjects() {
|
||||
val rootProject = ProjectBuilder.builder().build()
|
||||
val appProject = ProjectBuilder.builder().withName("app").withParent(rootProject).build()
|
||||
val libProject = ProjectBuilder.builder().withName("lib").withParent(rootProject).build()
|
||||
appProject.plugins.apply("com.android.application")
|
||||
libProject.plugins.apply("com.android.library")
|
||||
|
||||
configureDependencies(appProject, "1.2.3", "io.github.test")
|
||||
|
||||
val appForcedModules = appProject.configurations.first().resolutionStrategy.forcedModules
|
||||
val libForcedModules = libProject.configurations.first().resolutionStrategy.forcedModules
|
||||
assertTrue(appForcedModules.any { it.toString() == "io.github.test:react-android:1.2.3" })
|
||||
assertTrue(appForcedModules.any { it.toString() == "io.github.test:hermes-android:1.2.3" })
|
||||
assertTrue(libForcedModules.any { it.toString() == "io.github.test:react-android:1.2.3" })
|
||||
assertTrue(libForcedModules.any { it.toString() == "io.github.test:hermes-android:1.2.3" })
|
||||
}
|
||||
|
||||
@Test
|
||||
fun readVersionString_withCorrectVersionString_returnsIt() {
|
||||
val propertiesFile =
|
||||
@@ -238,7 +256,7 @@ class DependencyUtilsTest {
|
||||
.trimIndent())
|
||||
}
|
||||
|
||||
val versionString = readVersionString(propertiesFile)
|
||||
val versionString = readVersionAndGroupStrings(propertiesFile).first
|
||||
|
||||
assertEquals("1000.0.0", versionString)
|
||||
}
|
||||
@@ -255,7 +273,7 @@ class DependencyUtilsTest {
|
||||
.trimIndent())
|
||||
}
|
||||
|
||||
val versionString = readVersionString(propertiesFile)
|
||||
val versionString = readVersionAndGroupStrings(propertiesFile).first
|
||||
|
||||
assertEquals("0.0.0-20221101-2019-cfe811ab1-SNAPSHOT", versionString)
|
||||
}
|
||||
@@ -271,7 +289,7 @@ class DependencyUtilsTest {
|
||||
.trimIndent())
|
||||
}
|
||||
|
||||
val versionString = readVersionString(propertiesFile)
|
||||
val versionString = readVersionAndGroupStrings(propertiesFile).first
|
||||
assertEquals("", versionString)
|
||||
}
|
||||
|
||||
@@ -287,10 +305,43 @@ class DependencyUtilsTest {
|
||||
.trimIndent())
|
||||
}
|
||||
|
||||
val versionString = readVersionString(propertiesFile)
|
||||
val versionString = readVersionAndGroupStrings(propertiesFile).first
|
||||
assertEquals("", versionString)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun readGroupString_withCorrectGroupString_returnsIt() {
|
||||
val propertiesFile =
|
||||
tempFolder.newFile("gradle.properties").apply {
|
||||
writeText(
|
||||
"""
|
||||
GROUP=io.github.test
|
||||
ANOTHER_PROPERTY=true
|
||||
"""
|
||||
.trimIndent())
|
||||
}
|
||||
|
||||
val groupString = readVersionAndGroupStrings(propertiesFile).second
|
||||
|
||||
assertEquals("io.github.test", groupString)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun readGroupString_withEmptyGroupString_returnsDefault() {
|
||||
val propertiesFile =
|
||||
tempFolder.newFile("gradle.properties").apply {
|
||||
writeText(
|
||||
"""
|
||||
ANOTHER_PROPERTY=true
|
||||
"""
|
||||
.trimIndent())
|
||||
}
|
||||
|
||||
val groupString = readVersionAndGroupStrings(propertiesFile).second
|
||||
|
||||
assertEquals("com.facebook.react", groupString)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun mavenRepoFromUrl_worksCorrectly() {
|
||||
val process = createProject()
|
||||
|
||||
@@ -94,6 +94,11 @@
|
||||
*/
|
||||
- (UIViewController *)createRootViewController;
|
||||
|
||||
/// This method controls whether the App will use RuntimeScheduler. Only applicable in the legacy architecture.
|
||||
///
|
||||
/// @return: `YES` to use RuntimeScheduler, `NO` to use JavaScript scheduler. The default value is `YES`.
|
||||
- (BOOL)runtimeSchedulerEnabled;
|
||||
|
||||
#if RCT_NEW_ARCH_ENABLED
|
||||
|
||||
/// The TurboModule manager
|
||||
|
||||
@@ -6,33 +6,41 @@
|
||||
*/
|
||||
|
||||
#import "RCTAppDelegate.h"
|
||||
#import <React/RCTCxxBridgeDelegate.h>
|
||||
#import <React/RCTRootView.h>
|
||||
#import <React/RCTRuntimeExecutorFromBridge.h>
|
||||
#import <react/renderer/runtimescheduler/RuntimeScheduler.h>
|
||||
|
||||
#import "RCTAppSetupUtils.h"
|
||||
|
||||
#if RCT_NEW_ARCH_ENABLED
|
||||
#import <React/CoreModulesPlugins.h>
|
||||
#import <React/RCTCxxBridgeDelegate.h>
|
||||
#import <React/RCTComponentViewFactory.h>
|
||||
#import <React/RCTComponentViewProtocol.h>
|
||||
#import <React/RCTFabricSurfaceHostingProxyRootView.h>
|
||||
#import <React/RCTLegacyViewManagerInteropComponentView.h>
|
||||
#import <React/RCTSurfacePresenter.h>
|
||||
#import <React/RCTSurfacePresenterBridgeAdapter.h>
|
||||
#import <ReactCommon/RCTTurboModuleManager.h>
|
||||
#import <react/config/ReactNativeConfig.h>
|
||||
#import <react/renderer/runtimescheduler/RuntimeScheduler.h>
|
||||
#import <react/renderer/runtimescheduler/RuntimeSchedulerCallInvoker.h>
|
||||
#import "RCTLegacyInteropComponents.h"
|
||||
|
||||
static NSString *const kRNConcurrentRoot = @"concurrentRoot";
|
||||
|
||||
@interface RCTAppDelegate () <RCTTurboModuleManagerDelegate, RCTCxxBridgeDelegate> {
|
||||
@interface RCTAppDelegate () <RCTTurboModuleManagerDelegate> {
|
||||
std::shared_ptr<const facebook::react::ReactNativeConfig> _reactNativeConfig;
|
||||
facebook::react::ContextContainer::Shared _contextContainer;
|
||||
std::shared_ptr<facebook::react::RuntimeScheduler> _runtimeScheduler;
|
||||
}
|
||||
@end
|
||||
|
||||
#endif
|
||||
|
||||
@interface RCTAppDelegate () <RCTCxxBridgeDelegate> {
|
||||
std::shared_ptr<facebook::react::RuntimeScheduler> _runtimeScheduler;
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation RCTAppDelegate
|
||||
|
||||
#if RCT_NEW_ARCH_ENABLED
|
||||
@@ -126,21 +134,34 @@ static NSString *const kRNConcurrentRoot = @"concurrentRoot";
|
||||
return [UIViewController new];
|
||||
}
|
||||
|
||||
#if RCT_NEW_ARCH_ENABLED
|
||||
- (BOOL)runtimeSchedulerEnabled
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
|
||||
#pragma mark - RCTCxxBridgeDelegate
|
||||
- (std::unique_ptr<facebook::react::JSExecutorFactory>)jsExecutorFactoryForBridge:(RCTBridge *)bridge
|
||||
{
|
||||
_runtimeScheduler = _runtimeScheduler =
|
||||
std::make_shared<facebook::react::RuntimeScheduler>(RCTRuntimeExecutorFromBridge(bridge));
|
||||
#if RCT_NEW_ARCH_ENABLED
|
||||
_runtimeScheduler = std::make_shared<facebook::react::RuntimeScheduler>(RCTRuntimeExecutorFromBridge(bridge));
|
||||
std::shared_ptr<facebook::react::CallInvoker> callInvoker =
|
||||
std::make_shared<facebook::react::RuntimeSchedulerCallInvoker>(_runtimeScheduler);
|
||||
self.turboModuleManager = [[RCTTurboModuleManager alloc] initWithBridge:bridge delegate:self jsInvoker:callInvoker];
|
||||
_contextContainer->erase("RuntimeScheduler");
|
||||
_contextContainer->insert("RuntimeScheduler", _runtimeScheduler);
|
||||
return RCTAppSetupDefaultJsExecutorFactory(bridge, _turboModuleManager, _runtimeScheduler);
|
||||
return RCTAppSetupDefaultJsExecutorFactory(bridge, self.turboModuleManager, _runtimeScheduler);
|
||||
#else
|
||||
if (self.runtimeSchedulerEnabled) {
|
||||
_runtimeScheduler = std::make_shared<facebook::react::RuntimeScheduler>(RCTRuntimeExecutorFromBridge(bridge));
|
||||
}
|
||||
return RCTAppSetupJsExecutorFactoryForOldArch(bridge, _runtimeScheduler);
|
||||
#endif
|
||||
}
|
||||
|
||||
#pragma mark RCTTurboModuleManagerDelegate
|
||||
|
||||
#if RCT_NEW_ARCH_ENABLED
|
||||
|
||||
#pragma mark - RCTTurboModuleManagerDelegate
|
||||
|
||||
- (Class)getModuleClassFromName:(const char *)name
|
||||
{
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
#if RCT_NEW_ARCH_ENABLED
|
||||
#import <memory>
|
||||
|
||||
#ifndef RCT_USE_HERMES
|
||||
#if __has_include(<reacthermes/HermesExecutorFactory.h>)
|
||||
@@ -27,21 +27,27 @@
|
||||
#import <React/JSCExecutorFactory.h>
|
||||
#endif
|
||||
|
||||
#if RCT_NEW_ARCH_ENABLED
|
||||
#import <ReactCommon/RCTTurboModuleManager.h>
|
||||
#endif
|
||||
|
||||
#if RCT_NEW_ARCH_ENABLED
|
||||
// Forward declaration to decrease compilation coupling
|
||||
namespace facebook::react {
|
||||
class RuntimeScheduler;
|
||||
}
|
||||
|
||||
#if RCT_NEW_ARCH_ENABLED
|
||||
RCT_EXTERN id<RCTTurboModule> RCTAppSetupDefaultModuleFromClass(Class moduleClass);
|
||||
|
||||
std::unique_ptr<facebook::react::JSExecutorFactory> RCTAppSetupDefaultJsExecutorFactory(
|
||||
RCTBridge *bridge,
|
||||
RCTTurboModuleManager *turboModuleManager,
|
||||
std::shared_ptr<facebook::react::RuntimeScheduler> const &runtimeScheduler);
|
||||
|
||||
#else
|
||||
std::unique_ptr<facebook::react::JSExecutorFactory> RCTAppSetupJsExecutorFactoryForOldArch(
|
||||
RCTBridge *bridge,
|
||||
std::shared_ptr<facebook::react::RuntimeScheduler> const &runtimeScheduler);
|
||||
#endif
|
||||
|
||||
#endif // __cplusplus
|
||||
|
||||
@@ -7,6 +7,10 @@
|
||||
|
||||
#import "RCTAppSetupUtils.h"
|
||||
|
||||
#import <React/RCTJSIExecutorRuntimeInstaller.h>
|
||||
#import <react/renderer/runtimescheduler/RuntimeScheduler.h>
|
||||
#import <react/renderer/runtimescheduler/RuntimeSchedulerBinding.h>
|
||||
|
||||
#if RCT_NEW_ARCH_ENABLED
|
||||
// Turbo Module
|
||||
#import <React/CoreModulesPlugins.h>
|
||||
@@ -15,7 +19,6 @@
|
||||
#import <React/RCTGIFImageDecoder.h>
|
||||
#import <React/RCTHTTPRequestHandler.h>
|
||||
#import <React/RCTImageLoader.h>
|
||||
#import <React/RCTJSIExecutorRuntimeInstaller.h>
|
||||
#import <React/RCTLocalAssetImageLoader.h>
|
||||
#import <React/RCTNetworking.h>
|
||||
|
||||
@@ -135,4 +138,25 @@ std::unique_ptr<facebook::react::JSExecutorFactory> RCTAppSetupDefaultJsExecutor
|
||||
}));
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
std::unique_ptr<facebook::react::JSExecutorFactory> RCTAppSetupJsExecutorFactoryForOldArch(
|
||||
RCTBridge *bridge,
|
||||
std::shared_ptr<facebook::react::RuntimeScheduler> const &runtimeScheduler)
|
||||
{
|
||||
#if RCT_USE_HERMES
|
||||
return std::make_unique<facebook::react::HermesExecutorFactory>(
|
||||
#else
|
||||
return std::make_unique<facebook::react::JSCExecutorFactory>(
|
||||
#endif
|
||||
facebook::react::RCTJSIExecutorRuntimeInstaller([bridge, runtimeScheduler](facebook::jsi::Runtime &runtime) {
|
||||
if (!bridge) {
|
||||
return;
|
||||
}
|
||||
if (runtimeScheduler) {
|
||||
facebook::react::RuntimeSchedulerBinding::createAndInstallIfNeeded(runtime, runtimeScheduler);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -47,6 +47,9 @@ header_search_paths = [
|
||||
"$(PODS_CONFIGURATION_BUILD_DIR)/ReactCommon/ReactCommon.framework/Headers/react/nativemodule/core",
|
||||
"$(PODS_CONFIGURATION_BUILD_DIR)/React-NativeModulesApple/React_NativeModulesApple.framework/Headers",
|
||||
"$(PODS_CONFIGURATION_BUILD_DIR)/React-RCTFabric/RCTFabric.framework/Headers/",
|
||||
"$(PODS_CONFIGURATION_BUILD_DIR)/React-utils/React_utils.framework/Headers/",
|
||||
"$(PODS_CONFIGURATION_BUILD_DIR)/React-debug/React_debug.framework/Headers/",
|
||||
"$(PODS_CONFIGURATION_BUILD_DIR)/React-runtimescheduler/React_runtimescheduler.framework/Headers/",
|
||||
] : []).map{|p| "\"#{p}\""}.join(" ")
|
||||
|
||||
Pod::Spec.new do |s|
|
||||
@@ -75,15 +78,29 @@ Pod::Spec.new do |s|
|
||||
s.dependency "RCTRequired"
|
||||
s.dependency "RCTTypeSafety"
|
||||
s.dependency "ReactCommon/turbomodule/core"
|
||||
s.dependency "React-RCTNetwork"
|
||||
s.dependency "React-RCTImage"
|
||||
s.dependency "React-NativeModulesApple"
|
||||
s.dependency "React-CoreModules"
|
||||
s.dependency "React-runtimescheduler"
|
||||
|
||||
if ENV['USE_HERMES'] == nil || ENV['USE_HERMES'] == "1"
|
||||
s.dependency "React-hermes"
|
||||
else
|
||||
s.dependency "React-jsc"
|
||||
end
|
||||
|
||||
if is_new_arch_enabled
|
||||
s.dependency "React-RCTFabric"
|
||||
s.dependency "React-graphics"
|
||||
s.dependency "React-utils"
|
||||
s.dependency "React-debug"
|
||||
|
||||
s.script_phases = {
|
||||
:name => "Generate Legacy Components Interop",
|
||||
:script => "
|
||||
. ${PODS_ROOT}/../.xcode.env
|
||||
WITH_ENVIRONMENT=\"$REACT_NATIVE_PATH/scripts/xcode/with-environment.sh\"
|
||||
source $WITH_ENVIRONMENT
|
||||
${NODE_BINARY} ${REACT_NATIVE_PATH}/scripts/codegen/generate-legacy-interop-components.js -p #{ENV['APP_PATH']} -o ${REACT_NATIVE_PATH}/Libraries/AppDelegate
|
||||
",
|
||||
:execution_position => :before_compile,
|
||||
|
||||
-1
@@ -692,7 +692,6 @@ export const __INTERNAL_VIEW_CONFIG: PartialViewConfig = {
|
||||
fontStyle: true,
|
||||
textShadowOffset: true,
|
||||
selectionColor: {process: require('../../StyleSheet/processColor').default},
|
||||
selection: true,
|
||||
placeholderTextColor: {
|
||||
process: require('../../StyleSheet/processColor').default,
|
||||
},
|
||||
|
||||
+121
-86
@@ -289,92 +289,6 @@ export interface TextInputIOSProps {
|
||||
* @see https://reactnative.dev/docs/textinput#props
|
||||
*/
|
||||
export interface TextInputAndroidProps {
|
||||
/**
|
||||
* Specifies autocomplete hints for the system, so it can provide autofill. On Android, the system will always attempt to offer autofill by using heuristics to identify the type of content.
|
||||
* To disable autocomplete, set `autoComplete` to `off`.
|
||||
*
|
||||
* *Android Only*
|
||||
*
|
||||
* Possible values for `autoComplete` are:
|
||||
*
|
||||
* - `birthdate-day`
|
||||
* - `birthdate-full`
|
||||
* - `birthdate-month`
|
||||
* - `birthdate-year`
|
||||
* - `cc-csc`
|
||||
* - `cc-exp`
|
||||
* - `cc-exp-day`
|
||||
* - `cc-exp-month`
|
||||
* - `cc-exp-year`
|
||||
* - `cc-number`
|
||||
* - `email`
|
||||
* - `gender`
|
||||
* - `name`
|
||||
* - `name-family`
|
||||
* - `name-given`
|
||||
* - `name-middle`
|
||||
* - `name-middle-initial`
|
||||
* - `name-prefix`
|
||||
* - `name-suffix`
|
||||
* - `password`
|
||||
* - `password-new`
|
||||
* - `postal-address`
|
||||
* - `postal-address-country`
|
||||
* - `postal-address-extended`
|
||||
* - `postal-address-extended-postal-code`
|
||||
* - `postal-address-locality`
|
||||
* - `postal-address-region`
|
||||
* - `postal-code`
|
||||
* - `street-address`
|
||||
* - `sms-otp`
|
||||
* - `tel`
|
||||
* - `tel-country-code`
|
||||
* - `tel-national`
|
||||
* - `tel-device`
|
||||
* - `username`
|
||||
* - `username-new`
|
||||
* - `off`
|
||||
*/
|
||||
autoComplete?:
|
||||
| 'birthdate-day'
|
||||
| 'birthdate-full'
|
||||
| 'birthdate-month'
|
||||
| 'birthdate-year'
|
||||
| 'cc-csc'
|
||||
| 'cc-exp'
|
||||
| 'cc-exp-day'
|
||||
| 'cc-exp-month'
|
||||
| 'cc-exp-year'
|
||||
| 'cc-number'
|
||||
| 'email'
|
||||
| 'gender'
|
||||
| 'name'
|
||||
| 'name-family'
|
||||
| 'name-given'
|
||||
| 'name-middle'
|
||||
| 'name-middle-initial'
|
||||
| 'name-prefix'
|
||||
| 'name-suffix'
|
||||
| 'password'
|
||||
| 'password-new'
|
||||
| 'postal-address'
|
||||
| 'postal-address-country'
|
||||
| 'postal-address-extended'
|
||||
| 'postal-address-extended-postal-code'
|
||||
| 'postal-address-locality'
|
||||
| 'postal-address-region'
|
||||
| 'postal-code'
|
||||
| 'street-address'
|
||||
| 'sms-otp'
|
||||
| 'tel'
|
||||
| 'tel-country-code'
|
||||
| 'tel-national'
|
||||
| 'tel-device'
|
||||
| 'username'
|
||||
| 'username-new'
|
||||
| 'off'
|
||||
| undefined;
|
||||
|
||||
/**
|
||||
* When provided it will set the color of the cursor (or "caret") in the component.
|
||||
* Unlike the behavior of `selectionColor` the cursor color will be set independently
|
||||
@@ -558,6 +472,127 @@ export interface TextInputProps
|
||||
*/
|
||||
autoCapitalize?: 'none' | 'sentences' | 'words' | 'characters' | undefined;
|
||||
|
||||
/**
|
||||
* Specifies autocomplete hints for the system, so it can provide autofill.
|
||||
* On Android, the system will always attempt to offer autofill by using heuristics to identify the type of content.
|
||||
* To disable autocomplete, set autoComplete to off.
|
||||
*
|
||||
* The following values work across platforms:
|
||||
*
|
||||
* - `additional-name`
|
||||
* - `address-line1`
|
||||
* - `address-line2`
|
||||
* - `cc-number`
|
||||
* - `country`
|
||||
* - `current-password`
|
||||
* - `email`
|
||||
* - `family-name`
|
||||
* - `given-name`
|
||||
* - `honorific-prefix`
|
||||
* - `honorific-suffix`
|
||||
* - `name`
|
||||
* - `new-password`
|
||||
* - `off`
|
||||
* - `one-time-code`
|
||||
* - `postal-code`
|
||||
* - `street-address`
|
||||
* - `tel`
|
||||
* - `username`
|
||||
*
|
||||
* The following values work on iOS only:
|
||||
*
|
||||
* - `nickname`
|
||||
* - `organization`
|
||||
* - `organization-title`
|
||||
* - `url`
|
||||
*
|
||||
* The following values work on Android only:
|
||||
*
|
||||
* - `birthdate-day`
|
||||
* - `birthdate-full`
|
||||
* - `birthdate-month`
|
||||
* - `birthdate-year`
|
||||
* - `cc-csc`
|
||||
* - `cc-exp`
|
||||
* - `cc-exp-day`
|
||||
* - `cc-exp-month`
|
||||
* - `cc-exp-year`
|
||||
* - `gender`
|
||||
* - `name-family`
|
||||
* - `name-given`
|
||||
* - `name-middle`
|
||||
* - `name-middle-initial`
|
||||
* - `name-prefix`
|
||||
* - `name-suffix`
|
||||
* - `password`
|
||||
* - `password-new`
|
||||
* - `postal-address`
|
||||
* - `postal-address-country`
|
||||
* - `postal-address-extended`
|
||||
* - `postal-address-extended-postal-code`
|
||||
* - `postal-address-locality`
|
||||
* - `postal-address-region`
|
||||
* - `sms-otp`
|
||||
* - `tel-country-code`
|
||||
* - `tel-national`
|
||||
* - `tel-device`
|
||||
* - `username-new`
|
||||
*/
|
||||
autoComplete?:
|
||||
| 'additional-name'
|
||||
| 'address-line1'
|
||||
| 'address-line2'
|
||||
| 'birthdate-day'
|
||||
| 'birthdate-full'
|
||||
| 'birthdate-month'
|
||||
| 'birthdate-year'
|
||||
| 'cc-csc'
|
||||
| 'cc-exp'
|
||||
| 'cc-exp-day'
|
||||
| 'cc-exp-month'
|
||||
| 'cc-exp-year'
|
||||
| 'cc-number'
|
||||
| 'country'
|
||||
| 'current-password'
|
||||
| 'email'
|
||||
| 'family-name'
|
||||
| 'gender'
|
||||
| 'given-name'
|
||||
| 'honorific-prefix'
|
||||
| 'honorific-suffix'
|
||||
| 'name'
|
||||
| 'name-family'
|
||||
| 'name-given'
|
||||
| 'name-middle'
|
||||
| 'name-middle-initial'
|
||||
| 'name-prefix'
|
||||
| 'name-suffix'
|
||||
| 'new-password'
|
||||
| 'nickname'
|
||||
| 'one-time-code'
|
||||
| 'organization'
|
||||
| 'organization-title'
|
||||
| 'password'
|
||||
| 'password-new'
|
||||
| 'postal-address'
|
||||
| 'postal-address-country'
|
||||
| 'postal-address-extended'
|
||||
| 'postal-address-extended-postal-code'
|
||||
| 'postal-address-locality'
|
||||
| 'postal-address-region'
|
||||
| 'postal-code'
|
||||
| 'street-address'
|
||||
| 'sms-otp'
|
||||
| 'tel'
|
||||
| 'tel-country-code'
|
||||
| 'tel-national'
|
||||
| 'tel-device'
|
||||
| 'url'
|
||||
| 'username'
|
||||
| 'username-new'
|
||||
| 'off'
|
||||
| undefined;
|
||||
|
||||
/**
|
||||
* If false, disables auto-correct.
|
||||
* The default value is true.
|
||||
|
||||
@@ -196,36 +196,6 @@ export type enterKeyHintType =
|
||||
type PasswordRules = string;
|
||||
|
||||
type IOSProps = $ReadOnly<{|
|
||||
/**
|
||||
* Give the keyboard and the system information about the
|
||||
* expected semantic meaning for the content that users enter.
|
||||
* @platform ios
|
||||
*/
|
||||
autoComplete?: ?(
|
||||
| 'address-line1'
|
||||
| 'address-line2'
|
||||
| 'cc-number'
|
||||
| 'current-password'
|
||||
| 'country'
|
||||
| 'email'
|
||||
| 'name'
|
||||
| 'additional-name'
|
||||
| 'family-name'
|
||||
| 'given-name'
|
||||
| 'nickname'
|
||||
| 'honorific-prefix'
|
||||
| 'honorific-suffix'
|
||||
| 'new-password'
|
||||
| 'off'
|
||||
| 'one-time-code'
|
||||
| 'organization'
|
||||
| 'organization-title'
|
||||
| 'postal-code'
|
||||
| 'street-address'
|
||||
| 'tel'
|
||||
| 'url'
|
||||
| 'username'
|
||||
),
|
||||
/**
|
||||
* When the clear button should appear on the right side of the text view.
|
||||
* This property is supported only for single-line TextInput component.
|
||||
@@ -328,111 +298,6 @@ type IOSProps = $ReadOnly<{|
|
||||
|}>;
|
||||
|
||||
type AndroidProps = $ReadOnly<{|
|
||||
/**
|
||||
* Specifies autocomplete hints for the system, so it can provide autofill. On Android, the system will always attempt to offer autofill by using heuristics to identify the type of content.
|
||||
* To disable autocomplete, set `autoComplete` to `off`.
|
||||
*
|
||||
* *Android Only*
|
||||
*
|
||||
* Possible values for `autoComplete` are:
|
||||
*
|
||||
* - `birthdate-day`
|
||||
* - `birthdate-full`
|
||||
* - `birthdate-month`
|
||||
* - `birthdate-year`
|
||||
* - `cc-csc`
|
||||
* - `cc-exp`
|
||||
* - `cc-exp-day`
|
||||
* - `cc-exp-month`
|
||||
* - `cc-exp-year`
|
||||
* - `cc-number`
|
||||
* - `email`
|
||||
* - `gender`
|
||||
* - `name`
|
||||
* - `name-family`
|
||||
* - `name-given`
|
||||
* - `name-middle`
|
||||
* - `name-middle-initial`
|
||||
* - `name-prefix`
|
||||
* - `name-suffix`
|
||||
* - `password`
|
||||
* - `password-new`
|
||||
* - `postal-address`
|
||||
* - `postal-address-country`
|
||||
* - `postal-address-extended`
|
||||
* - `postal-address-extended-postal-code`
|
||||
* - `postal-address-locality`
|
||||
* - `postal-address-region`
|
||||
* - `postal-code`
|
||||
* - `street-address`
|
||||
* - `sms-otp`
|
||||
* - `tel`
|
||||
* - `tel-country-code`
|
||||
* - `tel-national`
|
||||
* - `tel-device`
|
||||
* - `username`
|
||||
* - `username-new`
|
||||
* - `off`
|
||||
*
|
||||
* @platform android
|
||||
*/
|
||||
autoComplete?: ?(
|
||||
| 'birthdate-day'
|
||||
| 'birthdate-full'
|
||||
| 'birthdate-month'
|
||||
| 'birthdate-year'
|
||||
| 'cc-csc'
|
||||
| 'cc-exp'
|
||||
| 'cc-exp-day'
|
||||
| 'cc-exp-month'
|
||||
| 'cc-exp-year'
|
||||
| 'cc-number'
|
||||
| 'email'
|
||||
| 'gender'
|
||||
| 'name'
|
||||
| 'name-family'
|
||||
| 'name-given'
|
||||
| 'name-middle'
|
||||
| 'name-middle-initial'
|
||||
| 'name-prefix'
|
||||
| 'name-suffix'
|
||||
| 'password'
|
||||
| 'password-new'
|
||||
| 'postal-address'
|
||||
| 'postal-address-country'
|
||||
| 'postal-address-extended'
|
||||
| 'postal-address-extended-postal-code'
|
||||
| 'postal-address-locality'
|
||||
| 'postal-address-region'
|
||||
| 'postal-code'
|
||||
| 'street-address'
|
||||
| 'sms-otp'
|
||||
| 'tel'
|
||||
| 'tel-country-code'
|
||||
| 'tel-national'
|
||||
| 'tel-device'
|
||||
| 'username'
|
||||
| 'username-new'
|
||||
| 'off'
|
||||
// additional HTML autocomplete values
|
||||
| 'address-line1'
|
||||
| 'address-line2'
|
||||
| 'bday'
|
||||
| 'bday-day'
|
||||
| 'bday-month'
|
||||
| 'bday-year'
|
||||
| 'country'
|
||||
| 'current-password'
|
||||
| 'honorific-prefix'
|
||||
| 'honorific-suffix'
|
||||
| 'additional-name'
|
||||
| 'family-name'
|
||||
| 'given-name'
|
||||
| 'new-password'
|
||||
| 'one-time-code'
|
||||
| 'sex'
|
||||
),
|
||||
|
||||
/**
|
||||
* When provided it will set the color of the cursor (or "caret") in the component.
|
||||
* Unlike the behavior of `selectionColor` the cursor color will be set independently
|
||||
@@ -533,6 +398,127 @@ export type Props = $ReadOnly<{|
|
||||
*/
|
||||
autoCapitalize?: ?AutoCapitalize,
|
||||
|
||||
/**
|
||||
* Specifies autocomplete hints for the system, so it can provide autofill.
|
||||
* On Android, the system will always attempt to offer autofill by using heuristics to identify the type of content.
|
||||
* To disable autocomplete, set autoComplete to off.
|
||||
*
|
||||
* The following values work across platforms:
|
||||
*
|
||||
* - `additional-name`
|
||||
* - `address-line1`
|
||||
* - `address-line2`
|
||||
* - `cc-number`
|
||||
* - `country`
|
||||
* - `current-password`
|
||||
* - `email`
|
||||
* - `family-name`
|
||||
* - `given-name`
|
||||
* - `honorific-prefix`
|
||||
* - `honorific-suffix`
|
||||
* - `name`
|
||||
* - `new-password`
|
||||
* - `off`
|
||||
* - `one-time-code`
|
||||
* - `postal-code`
|
||||
* - `street-address`
|
||||
* - `tel`
|
||||
* - `username`
|
||||
*
|
||||
* The following values work on iOS only:
|
||||
*
|
||||
* - `nickname`
|
||||
* - `organization`
|
||||
* - `organization-title`
|
||||
* - `url`
|
||||
*
|
||||
* The following values work on Android only:
|
||||
*
|
||||
* - `birthdate-day`
|
||||
* - `birthdate-full`
|
||||
* - `birthdate-month`
|
||||
* - `birthdate-year`
|
||||
* - `cc-csc`
|
||||
* - `cc-exp`
|
||||
* - `cc-exp-day`
|
||||
* - `cc-exp-month`
|
||||
* - `cc-exp-year`
|
||||
* - `gender`
|
||||
* - `name-family`
|
||||
* - `name-given`
|
||||
* - `name-middle`
|
||||
* - `name-middle-initial`
|
||||
* - `name-prefix`
|
||||
* - `name-suffix`
|
||||
* - `password`
|
||||
* - `password-new`
|
||||
* - `postal-address`
|
||||
* - `postal-address-country`
|
||||
* - `postal-address-extended`
|
||||
* - `postal-address-extended-postal-code`
|
||||
* - `postal-address-locality`
|
||||
* - `postal-address-region`
|
||||
* - `sms-otp`
|
||||
* - `tel-country-code`
|
||||
* - `tel-national`
|
||||
* - `tel-device`
|
||||
* - `username-new`
|
||||
*/
|
||||
autoComplete?: ?(
|
||||
| 'additional-name'
|
||||
| 'address-line1'
|
||||
| 'address-line2'
|
||||
| 'birthdate-day'
|
||||
| 'birthdate-full'
|
||||
| 'birthdate-month'
|
||||
| 'birthdate-year'
|
||||
| 'cc-csc'
|
||||
| 'cc-exp'
|
||||
| 'cc-exp-day'
|
||||
| 'cc-exp-month'
|
||||
| 'cc-exp-year'
|
||||
| 'cc-number'
|
||||
| 'country'
|
||||
| 'current-password'
|
||||
| 'email'
|
||||
| 'family-name'
|
||||
| 'gender'
|
||||
| 'given-name'
|
||||
| 'honorific-prefix'
|
||||
| 'honorific-suffix'
|
||||
| 'name'
|
||||
| 'name-family'
|
||||
| 'name-given'
|
||||
| 'name-middle'
|
||||
| 'name-middle-initial'
|
||||
| 'name-prefix'
|
||||
| 'name-suffix'
|
||||
| 'new-password'
|
||||
| 'nickname'
|
||||
| 'one-time-code'
|
||||
| 'organization'
|
||||
| 'organization-title'
|
||||
| 'password'
|
||||
| 'password-new'
|
||||
| 'postal-address'
|
||||
| 'postal-address-country'
|
||||
| 'postal-address-extended'
|
||||
| 'postal-address-extended-postal-code'
|
||||
| 'postal-address-locality'
|
||||
| 'postal-address-region'
|
||||
| 'postal-code'
|
||||
| 'street-address'
|
||||
| 'sms-otp'
|
||||
| 'tel'
|
||||
| 'tel-country-code'
|
||||
| 'tel-national'
|
||||
| 'tel-device'
|
||||
| 'url'
|
||||
| 'username'
|
||||
| 'username-new'
|
||||
| 'off'
|
||||
),
|
||||
|
||||
/**
|
||||
* If `false`, disables auto-correct. The default value is `true`.
|
||||
*/
|
||||
|
||||
+126
-155
@@ -234,36 +234,6 @@ export type enterKeyHintType =
|
||||
type PasswordRules = string;
|
||||
|
||||
type IOSProps = $ReadOnly<{|
|
||||
/**
|
||||
* Give the keyboard and the system information about the
|
||||
* expected semantic meaning for the content that users enter.
|
||||
* @platform ios
|
||||
*/
|
||||
autoComplete?: ?(
|
||||
| 'address-line1'
|
||||
| 'address-line2'
|
||||
| 'cc-number'
|
||||
| 'current-password'
|
||||
| 'country'
|
||||
| 'email'
|
||||
| 'name'
|
||||
| 'additional-name'
|
||||
| 'family-name'
|
||||
| 'given-name'
|
||||
| 'nickname'
|
||||
| 'honorific-prefix'
|
||||
| 'honorific-suffix'
|
||||
| 'new-password'
|
||||
| 'off'
|
||||
| 'one-time-code'
|
||||
| 'organization'
|
||||
| 'organization-title'
|
||||
| 'postal-code'
|
||||
| 'street-address'
|
||||
| 'tel'
|
||||
| 'url'
|
||||
| 'username'
|
||||
),
|
||||
/**
|
||||
* When the clear button should appear on the right side of the text view.
|
||||
* This property is supported only for single-line TextInput component.
|
||||
@@ -369,111 +339,6 @@ type IOSProps = $ReadOnly<{|
|
||||
|}>;
|
||||
|
||||
type AndroidProps = $ReadOnly<{|
|
||||
/**
|
||||
* Specifies autocomplete hints for the system, so it can provide autofill. On Android, the system will always attempt to offer autofill by using heuristics to identify the type of content.
|
||||
* To disable autocomplete, set `autoComplete` to `off`.
|
||||
*
|
||||
* *Android Only*
|
||||
*
|
||||
* Possible values for `autoComplete` are:
|
||||
*
|
||||
* - `birthdate-day`
|
||||
* - `birthdate-full`
|
||||
* - `birthdate-month`
|
||||
* - `birthdate-year`
|
||||
* - `cc-csc`
|
||||
* - `cc-exp`
|
||||
* - `cc-exp-day`
|
||||
* - `cc-exp-month`
|
||||
* - `cc-exp-year`
|
||||
* - `cc-number`
|
||||
* - `email`
|
||||
* - `gender`
|
||||
* - `name`
|
||||
* - `name-family`
|
||||
* - `name-given`
|
||||
* - `name-middle`
|
||||
* - `name-middle-initial`
|
||||
* - `name-prefix`
|
||||
* - `name-suffix`
|
||||
* - `password`
|
||||
* - `password-new`
|
||||
* - `postal-address`
|
||||
* - `postal-address-country`
|
||||
* - `postal-address-extended`
|
||||
* - `postal-address-extended-postal-code`
|
||||
* - `postal-address-locality`
|
||||
* - `postal-address-region`
|
||||
* - `postal-code`
|
||||
* - `street-address`
|
||||
* - `sms-otp`
|
||||
* - `tel`
|
||||
* - `tel-country-code`
|
||||
* - `tel-national`
|
||||
* - `tel-device`
|
||||
* - `username`
|
||||
* - `username-new`
|
||||
* - `off`
|
||||
*
|
||||
* @platform android
|
||||
*/
|
||||
autoComplete?: ?(
|
||||
| 'birthdate-day'
|
||||
| 'birthdate-full'
|
||||
| 'birthdate-month'
|
||||
| 'birthdate-year'
|
||||
| 'cc-csc'
|
||||
| 'cc-exp'
|
||||
| 'cc-exp-day'
|
||||
| 'cc-exp-month'
|
||||
| 'cc-exp-year'
|
||||
| 'cc-number'
|
||||
| 'email'
|
||||
| 'gender'
|
||||
| 'name'
|
||||
| 'name-family'
|
||||
| 'name-given'
|
||||
| 'name-middle'
|
||||
| 'name-middle-initial'
|
||||
| 'name-prefix'
|
||||
| 'name-suffix'
|
||||
| 'password'
|
||||
| 'password-new'
|
||||
| 'postal-address'
|
||||
| 'postal-address-country'
|
||||
| 'postal-address-extended'
|
||||
| 'postal-address-extended-postal-code'
|
||||
| 'postal-address-locality'
|
||||
| 'postal-address-region'
|
||||
| 'postal-code'
|
||||
| 'street-address'
|
||||
| 'sms-otp'
|
||||
| 'tel'
|
||||
| 'tel-country-code'
|
||||
| 'tel-national'
|
||||
| 'tel-device'
|
||||
| 'username'
|
||||
| 'username-new'
|
||||
| 'off'
|
||||
// additional HTML autocomplete values
|
||||
| 'address-line1'
|
||||
| 'address-line2'
|
||||
| 'bday'
|
||||
| 'bday-day'
|
||||
| 'bday-month'
|
||||
| 'bday-year'
|
||||
| 'country'
|
||||
| 'current-password'
|
||||
| 'honorific-prefix'
|
||||
| 'honorific-suffix'
|
||||
| 'additional-name'
|
||||
| 'family-name'
|
||||
| 'given-name'
|
||||
| 'new-password'
|
||||
| 'one-time-code'
|
||||
| 'sex'
|
||||
),
|
||||
|
||||
/**
|
||||
* When provided it will set the color of the cursor (or "caret") in the component.
|
||||
* Unlike the behavior of `selectionColor` the cursor color will be set independently
|
||||
@@ -574,6 +439,127 @@ export type Props = $ReadOnly<{|
|
||||
*/
|
||||
autoCapitalize?: ?AutoCapitalize,
|
||||
|
||||
/**
|
||||
* Specifies autocomplete hints for the system, so it can provide autofill.
|
||||
* On Android, the system will always attempt to offer autofill by using heuristics to identify the type of content.
|
||||
* To disable autocomplete, set autoComplete to off.
|
||||
*
|
||||
* The following values work across platforms:
|
||||
*
|
||||
* - `additional-name`
|
||||
* - `address-line1`
|
||||
* - `address-line2`
|
||||
* - `cc-number`
|
||||
* - `country`
|
||||
* - `current-password`
|
||||
* - `email`
|
||||
* - `family-name`
|
||||
* - `given-name`
|
||||
* - `honorific-prefix`
|
||||
* - `honorific-suffix`
|
||||
* - `name`
|
||||
* - `new-password`
|
||||
* - `off`
|
||||
* - `one-time-code`
|
||||
* - `postal-code`
|
||||
* - `street-address`
|
||||
* - `tel`
|
||||
* - `username`
|
||||
*
|
||||
* The following values work on iOS only:
|
||||
*
|
||||
* - `nickname`
|
||||
* - `organization`
|
||||
* - `organization-title`
|
||||
* - `url`
|
||||
*
|
||||
* The following values work on Android only:
|
||||
*
|
||||
* - `birthdate-day`
|
||||
* - `birthdate-full`
|
||||
* - `birthdate-month`
|
||||
* - `birthdate-year`
|
||||
* - `cc-csc`
|
||||
* - `cc-exp`
|
||||
* - `cc-exp-day`
|
||||
* - `cc-exp-month`
|
||||
* - `cc-exp-year`
|
||||
* - `gender`
|
||||
* - `name-family`
|
||||
* - `name-given`
|
||||
* - `name-middle`
|
||||
* - `name-middle-initial`
|
||||
* - `name-prefix`
|
||||
* - `name-suffix`
|
||||
* - `password`
|
||||
* - `password-new`
|
||||
* - `postal-address`
|
||||
* - `postal-address-country`
|
||||
* - `postal-address-extended`
|
||||
* - `postal-address-extended-postal-code`
|
||||
* - `postal-address-locality`
|
||||
* - `postal-address-region`
|
||||
* - `sms-otp`
|
||||
* - `tel-country-code`
|
||||
* - `tel-national`
|
||||
* - `tel-device`
|
||||
* - `username-new`
|
||||
*/
|
||||
autoComplete?: ?(
|
||||
| 'additional-name'
|
||||
| 'address-line1'
|
||||
| 'address-line2'
|
||||
| 'birthdate-day'
|
||||
| 'birthdate-full'
|
||||
| 'birthdate-month'
|
||||
| 'birthdate-year'
|
||||
| 'cc-csc'
|
||||
| 'cc-exp'
|
||||
| 'cc-exp-day'
|
||||
| 'cc-exp-month'
|
||||
| 'cc-exp-year'
|
||||
| 'cc-number'
|
||||
| 'country'
|
||||
| 'current-password'
|
||||
| 'email'
|
||||
| 'family-name'
|
||||
| 'gender'
|
||||
| 'given-name'
|
||||
| 'honorific-prefix'
|
||||
| 'honorific-suffix'
|
||||
| 'name'
|
||||
| 'name-family'
|
||||
| 'name-given'
|
||||
| 'name-middle'
|
||||
| 'name-middle-initial'
|
||||
| 'name-prefix'
|
||||
| 'name-suffix'
|
||||
| 'new-password'
|
||||
| 'nickname'
|
||||
| 'one-time-code'
|
||||
| 'organization'
|
||||
| 'organization-title'
|
||||
| 'password'
|
||||
| 'password-new'
|
||||
| 'postal-address'
|
||||
| 'postal-address-country'
|
||||
| 'postal-address-extended'
|
||||
| 'postal-address-extended-postal-code'
|
||||
| 'postal-address-locality'
|
||||
| 'postal-address-region'
|
||||
| 'postal-code'
|
||||
| 'street-address'
|
||||
| 'sms-otp'
|
||||
| 'tel'
|
||||
| 'tel-country-code'
|
||||
| 'tel-national'
|
||||
| 'tel-device'
|
||||
| 'url'
|
||||
| 'username'
|
||||
| 'username-new'
|
||||
| 'off'
|
||||
),
|
||||
|
||||
/**
|
||||
* If `false`, disables auto-correct. The default value is `true`.
|
||||
*/
|
||||
@@ -1080,27 +1066,19 @@ function InternalTextInput(props: Props): React.Node {
|
||||
accessibilityState,
|
||||
id,
|
||||
tabIndex,
|
||||
selection: propsSelection,
|
||||
...otherProps
|
||||
} = props;
|
||||
|
||||
const inputRef = useRef<null | React.ElementRef<HostComponent<mixed>>>(null);
|
||||
|
||||
// Android sends a "onTextChanged" event followed by a "onSelectionChanged" event, for
|
||||
// the same "most recent event count".
|
||||
// For controlled selection, that means that immediately after text is updated,
|
||||
// a controlled component will pass in the *previous* selection, even if the controlled
|
||||
// component didn't mean to modify the selection at all.
|
||||
// Therefore, we ignore selections and pass them through until the selection event has
|
||||
// been sent.
|
||||
// Note that this mitigation is NOT needed for Fabric.
|
||||
// discovered when upgrading react-hooks
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
let selection: ?Selection =
|
||||
props.selection == null
|
||||
const selection: ?Selection =
|
||||
propsSelection == null
|
||||
? null
|
||||
: {
|
||||
start: props.selection.start,
|
||||
end: props.selection.end ?? props.selection.start,
|
||||
start: propsSelection.start,
|
||||
end: propsSelection.end ?? propsSelection.start,
|
||||
};
|
||||
|
||||
const [mostRecentEventCount, setMostRecentEventCount] = useState<number>(0);
|
||||
@@ -1112,12 +1090,6 @@ function InternalTextInput(props: Props): React.Node {
|
||||
|}>({selection, mostRecentEventCount});
|
||||
|
||||
const lastNativeSelection = lastNativeSelectionState.selection;
|
||||
const lastNativeSelectionEventCount =
|
||||
lastNativeSelectionState.mostRecentEventCount;
|
||||
|
||||
if (lastNativeSelectionEventCount < mostRecentEventCount) {
|
||||
selection = null;
|
||||
}
|
||||
|
||||
let viewCommands;
|
||||
if (AndroidTextInputCommands) {
|
||||
@@ -1517,7 +1489,6 @@ function InternalTextInput(props: Props): React.Node {
|
||||
onScroll={_onScroll}
|
||||
onSelectionChange={_onSelectionChange}
|
||||
placeholder={placeholder}
|
||||
selection={selection}
|
||||
style={style}
|
||||
text={text}
|
||||
textBreakStrategy={props.textBreakStrategy}
|
||||
|
||||
@@ -46,13 +46,6 @@ const ReactNativeStyleAttributes: {[string]: AnyAttributeType, ...} = {
|
||||
flexWrap: true,
|
||||
gap: true,
|
||||
height: true,
|
||||
inset: true,
|
||||
insetBlock: true,
|
||||
insetBlockEnd: true,
|
||||
insetBlockStart: true,
|
||||
insetInline: true,
|
||||
insetInlineEnd: true,
|
||||
insetInlineStart: true,
|
||||
justifyContent: true,
|
||||
left: true,
|
||||
margin: true,
|
||||
|
||||
@@ -10,8 +10,8 @@
|
||||
*/
|
||||
|
||||
exports.version = {
|
||||
major: 1000,
|
||||
minor: 0,
|
||||
major: 0,
|
||||
minor: 72,
|
||||
patch: 0,
|
||||
prerelease: null,
|
||||
};
|
||||
|
||||
@@ -39,8 +39,12 @@ exports.checkVersions = function checkVersions(): void {
|
||||
}
|
||||
};
|
||||
|
||||
// Note: in OSS, the prerelease version is usually 0.Y.0-rc.W, so it is a string and not a number
|
||||
// Then we need to keep supporting that object shape.
|
||||
function _formatVersion(
|
||||
version: (typeof Platform)['constants']['reactNativeVersion'],
|
||||
version:
|
||||
| (typeof Platform)['constants']['reactNativeVersion']
|
||||
| {major: number, minor: number, patch: number, prerelease: ?string},
|
||||
): string {
|
||||
return (
|
||||
`${version.major}.${version.minor}.${version.patch}` +
|
||||
|
||||
@@ -111,6 +111,7 @@ exports[`FlatList renders all the bells and whistles 1`] = `
|
||||
<RCTRefreshControl />
|
||||
<View>
|
||||
<View
|
||||
collapsable={false}
|
||||
onLayout={[Function]}
|
||||
>
|
||||
<header />
|
||||
|
||||
@@ -243,6 +243,7 @@ exports[`SectionList renders all the bells and whistles 1`] = `
|
||||
<RCTRefreshControl />
|
||||
<View>
|
||||
<View
|
||||
collapsable={false}
|
||||
onLayout={[Function]}
|
||||
>
|
||||
<header
|
||||
|
||||
@@ -258,14 +258,6 @@ const validAttributesForNonEventProps = {
|
||||
top: true,
|
||||
bottom: true,
|
||||
|
||||
inset: true,
|
||||
insetBlock: true,
|
||||
insetBlockEnd: true,
|
||||
insetBlockStart: true,
|
||||
insetInline: true,
|
||||
insetInlineEnd: true,
|
||||
insetInlineStart: true,
|
||||
|
||||
position: true,
|
||||
|
||||
style: ReactNativeStyleAttributes,
|
||||
|
||||
@@ -236,14 +236,6 @@ const validAttributesForNonEventProps = {
|
||||
bottom: true,
|
||||
left: true,
|
||||
|
||||
inset: true,
|
||||
insetBlock: true,
|
||||
insetBlockEnd: true,
|
||||
insetBlockStart: true,
|
||||
insetInline: true,
|
||||
insetInlineEnd: true,
|
||||
insetInlineStart: true,
|
||||
|
||||
width: true,
|
||||
height: true,
|
||||
|
||||
|
||||
@@ -75,7 +75,6 @@ const PERMISSIONS = Object.freeze({
|
||||
ANSWER_PHONE_CALLS: 'android.permission.ANSWER_PHONE_CALLS',
|
||||
READ_PHONE_NUMBERS: 'android.permission.READ_PHONE_NUMBERS',
|
||||
UWB_RANGING: 'android.permission.UWB_RANGING',
|
||||
POST_NOTIFICATION: 'android.permission.POST_NOTIFICATIONS', // Remove in 0.72
|
||||
POST_NOTIFICATIONS: 'android.permission.POST_NOTIFICATIONS',
|
||||
NEARBY_WIFI_DEVICES: 'android.permission.NEARBY_WIFI_DEVICES',
|
||||
});
|
||||
@@ -107,7 +106,6 @@ class PermissionsAndroid {
|
||||
CAMERA: string,
|
||||
GET_ACCOUNTS: string,
|
||||
NEARBY_WIFI_DEVICES: string,
|
||||
POST_NOTIFICATION: string, // Remove in 0.72
|
||||
POST_NOTIFICATIONS: string,
|
||||
PROCESS_OUTGOING_CALLS: string,
|
||||
READ_CALENDAR: string,
|
||||
|
||||
+27
-1
@@ -9,7 +9,6 @@
|
||||
*/
|
||||
|
||||
import type {RootTag} from '../Types/RootTagTypes';
|
||||
import type {Spec as FabricUIManagerSpec} from './FabricUIManager';
|
||||
import type {Spec} from './NativeUIManager';
|
||||
|
||||
import {getFabricUIManager} from './FabricUIManager';
|
||||
@@ -175,6 +174,33 @@ const UIManager = {
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
dispatchViewManagerCommand(
|
||||
reactTag: number,
|
||||
commandName: number | string,
|
||||
commandArgs: any[],
|
||||
) {
|
||||
if (isFabricReactTag(reactTag)) {
|
||||
const FabricUIManager = nullthrows(getFabricUIManager());
|
||||
const shadowNode =
|
||||
FabricUIManager.findShadowNodeByTag_DEPRECATED(reactTag);
|
||||
if (shadowNode) {
|
||||
// Transform the accidental CommandID into a CommandName which is the stringified number.
|
||||
// The interop layer knows how to convert this number into the right method name.
|
||||
// Stringify a string is a no-op, so it's safe.
|
||||
commandName = `${commandName}`;
|
||||
FabricUIManager.dispatchCommand(shadowNode, commandName, commandArgs);
|
||||
}
|
||||
} else {
|
||||
UIManagerImpl.dispatchViewManagerCommand(
|
||||
reactTag,
|
||||
// We have some legacy components that are actually already using strings. ¯\_(ツ)_/¯
|
||||
// $FlowFixMe[incompatible-call]
|
||||
commandName,
|
||||
commandArgs,
|
||||
);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = UIManager;
|
||||
|
||||
+26
-3
@@ -3419,6 +3419,23 @@ function mountSafeCallback_NOT_REALLY_SAFE(context, callback) {
|
||||
return callback.apply(context, arguments);
|
||||
};
|
||||
}
|
||||
function warnForStyleProps(props, validAttributes) {
|
||||
{
|
||||
for (var key in validAttributes.style) {
|
||||
if (!(validAttributes[key] || props[key] === undefined)) {
|
||||
error(
|
||||
"You are setting the style `{ %s" +
|
||||
": ... }` as a prop. You " +
|
||||
"should nest it in a style object. " +
|
||||
"E.g. `{ style: { %s" +
|
||||
": ... } }`",
|
||||
key,
|
||||
key
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Modules provided by RN:
|
||||
var emptyObject = {};
|
||||
@@ -5100,7 +5117,8 @@ var _nativeFabricUIManage = nativeFabricUIManager,
|
||||
FabricDefaultPriority = _nativeFabricUIManage.unstable_DefaultEventPriority,
|
||||
FabricDiscretePriority = _nativeFabricUIManage.unstable_DiscreteEventPriority,
|
||||
fabricGetCurrentEventPriority =
|
||||
_nativeFabricUIManage.unstable_getCurrentEventPriority;
|
||||
_nativeFabricUIManage.unstable_getCurrentEventPriority,
|
||||
_setNativeProps = _nativeFabricUIManage.setNativeProps;
|
||||
var getViewConfigForType =
|
||||
ReactNativePrivateInterface.ReactNativeViewConfigRegistry.get; // Counter for uniquely identifying views.
|
||||
// % 10 === 1 means it is a rootTag.
|
||||
@@ -5199,10 +5217,15 @@ var ReactFabricHostComponent = /*#__PURE__*/ (function() {
|
||||
|
||||
_proto.setNativeProps = function setNativeProps(nativeProps) {
|
||||
{
|
||||
error("Warning: setNativeProps is not currently supported in Fabric");
|
||||
warnForStyleProps(nativeProps, this.viewConfig.validAttributes);
|
||||
}
|
||||
|
||||
return;
|
||||
var updatePayload = create(nativeProps, this.viewConfig.validAttributes);
|
||||
var stateNode = this._internalInstanceHandle.stateNode;
|
||||
|
||||
if (stateNode != null && updatePayload != null) {
|
||||
_setNativeProps(stateNode.node, updatePayload);
|
||||
}
|
||||
}; // This API (addEventListener, removeEventListener) attempts to adhere to the
|
||||
// w3 Level2 Events spec as much as possible, treating HostComponent as a DOM node.
|
||||
//
|
||||
|
||||
+13
-1
@@ -1922,6 +1922,7 @@ var _nativeFabricUIManage = nativeFabricUIManager,
|
||||
FabricDiscretePriority = _nativeFabricUIManage.unstable_DiscreteEventPriority,
|
||||
fabricGetCurrentEventPriority =
|
||||
_nativeFabricUIManage.unstable_getCurrentEventPriority,
|
||||
_setNativeProps = _nativeFabricUIManage.setNativeProps,
|
||||
getViewConfigForType =
|
||||
ReactNativePrivateInterface.ReactNativeViewConfigRegistry.get,
|
||||
nextReactTag = 2;
|
||||
@@ -1979,7 +1980,18 @@ var ReactFabricHostComponent = (function() {
|
||||
);
|
||||
}
|
||||
};
|
||||
_proto.setNativeProps = function() {};
|
||||
_proto.setNativeProps = function(nativeProps) {
|
||||
nativeProps = diffProperties(
|
||||
null,
|
||||
emptyObject,
|
||||
nativeProps,
|
||||
this.viewConfig.validAttributes
|
||||
);
|
||||
var stateNode = this._internalInstanceHandle.stateNode;
|
||||
null != stateNode &&
|
||||
null != nativeProps &&
|
||||
_setNativeProps(stateNode.node, nativeProps);
|
||||
};
|
||||
_proto.addEventListener_unstable = function(eventType, listener, options) {
|
||||
if ("string" !== typeof eventType)
|
||||
throw Error("addEventListener_unstable eventType must be a string");
|
||||
|
||||
+13
-1
@@ -1981,6 +1981,7 @@ var _nativeFabricUIManage = nativeFabricUIManager,
|
||||
FabricDiscretePriority = _nativeFabricUIManage.unstable_DiscreteEventPriority,
|
||||
fabricGetCurrentEventPriority =
|
||||
_nativeFabricUIManage.unstable_getCurrentEventPriority,
|
||||
_setNativeProps = _nativeFabricUIManage.setNativeProps,
|
||||
getViewConfigForType =
|
||||
ReactNativePrivateInterface.ReactNativeViewConfigRegistry.get,
|
||||
nextReactTag = 2;
|
||||
@@ -2038,7 +2039,18 @@ var ReactFabricHostComponent = (function() {
|
||||
);
|
||||
}
|
||||
};
|
||||
_proto.setNativeProps = function() {};
|
||||
_proto.setNativeProps = function(nativeProps) {
|
||||
nativeProps = diffProperties(
|
||||
null,
|
||||
emptyObject,
|
||||
nativeProps,
|
||||
this.viewConfig.validAttributes
|
||||
);
|
||||
var stateNode = this._internalInstanceHandle.stateNode;
|
||||
null != stateNode &&
|
||||
null != nativeProps &&
|
||||
_setNativeProps(stateNode.node, nativeProps);
|
||||
};
|
||||
_proto.addEventListener_unstable = function(eventType, listener, options) {
|
||||
if ("string" !== typeof eventType)
|
||||
throw Error("addEventListener_unstable eventType must be a string");
|
||||
|
||||
@@ -18,7 +18,7 @@ type FlexAlignType =
|
||||
| 'stretch'
|
||||
| 'baseline';
|
||||
|
||||
type DimensionValue =
|
||||
export type DimensionValue =
|
||||
| number
|
||||
| 'auto'
|
||||
| `${number}%`
|
||||
@@ -69,13 +69,6 @@ export interface FlexStyle {
|
||||
flexShrink?: number | undefined;
|
||||
flexWrap?: 'wrap' | 'nowrap' | 'wrap-reverse' | undefined;
|
||||
height?: DimensionValue | undefined;
|
||||
inset?: DimensionValue | undefined;
|
||||
insetBlock?: DimensionValue | undefined;
|
||||
insetBlockEnd?: DimensionValue | undefined;
|
||||
insetBlockStart?: DimensionValue | undefined;
|
||||
insetInline?: DimensionValue | undefined;
|
||||
insetInlineEnd?: DimensionValue | undefined;
|
||||
insetInlineStart?: DimensionValue | undefined;
|
||||
justifyContent?:
|
||||
| 'flex-start'
|
||||
| 'flex-end'
|
||||
@@ -86,15 +79,9 @@ export interface FlexStyle {
|
||||
| undefined;
|
||||
left?: DimensionValue | undefined;
|
||||
margin?: DimensionValue | undefined;
|
||||
marginBlock?: DimensionValue | undefined;
|
||||
marginBlockEnd?: DimensionValue | undefined;
|
||||
marginBlockStart?: DimensionValue | undefined;
|
||||
marginBottom?: DimensionValue | undefined;
|
||||
marginEnd?: DimensionValue | undefined;
|
||||
marginHorizontal?: DimensionValue | undefined;
|
||||
marginInline?: DimensionValue | undefined;
|
||||
marginInlineEnd?: DimensionValue | undefined;
|
||||
marginInlineStart?: DimensionValue | undefined;
|
||||
marginLeft?: DimensionValue | undefined;
|
||||
marginRight?: DimensionValue | undefined;
|
||||
marginStart?: DimensionValue | undefined;
|
||||
@@ -107,14 +94,8 @@ export interface FlexStyle {
|
||||
overflow?: 'visible' | 'hidden' | 'scroll' | undefined;
|
||||
padding?: DimensionValue | undefined;
|
||||
paddingBottom?: DimensionValue | undefined;
|
||||
paddingBlock?: DimensionValue | undefined;
|
||||
paddingBlockEnd?: DimensionValue | undefined;
|
||||
paddingBlockStart?: DimensionValue | undefined;
|
||||
paddingEnd?: DimensionValue | undefined;
|
||||
paddingHorizontal?: DimensionValue | undefined;
|
||||
paddingInline?: DimensionValue | undefined;
|
||||
paddingInlineEnd?: DimensionValue | undefined;
|
||||
paddingInlineStart?: DimensionValue | undefined;
|
||||
paddingLeft?: DimensionValue | undefined;
|
||||
paddingRight?: DimensionValue | undefined;
|
||||
paddingStart?: DimensionValue | undefined;
|
||||
|
||||
@@ -134,80 +134,6 @@ type ____LayoutStyle_Internal = $ReadOnly<{
|
||||
*/
|
||||
top?: DimensionValue,
|
||||
|
||||
/** `inset` is a shorthand that corresponds to the top, right, bottom, and/or left properties.
|
||||
*
|
||||
* It works similarly to `inset` in CSS, but in React Native you
|
||||
* must use points or percentages. Ems and other units are not supported.
|
||||
*
|
||||
* See https://developer.mozilla.org/en-US/docs/Web/CSS/inset
|
||||
* for more details of how `inset` affects layout.
|
||||
*/
|
||||
inset?: DimensionValue,
|
||||
|
||||
/** `insetBlock` is a shorthand that corresponds to the `insetBlockStart` and `insetBlockEnd` properties.
|
||||
*
|
||||
* It works similarly to `inset-block` in CSS, but in React Native you
|
||||
* must use points or percentages. Ems and other units are not supported.
|
||||
*
|
||||
* See https://developer.mozilla.org/en-US/docs/Web/CSS/inset-block
|
||||
* for more details of how `inset-block` affects layout.
|
||||
*/
|
||||
insetBlock?: DimensionValue,
|
||||
|
||||
/** `insetBlockEnd` is a logical property that sets the length that an
|
||||
* element is offset in the block direction from its ending edge.
|
||||
*
|
||||
* It works similarly to `inset-block-end` in CSS, but in React Native you
|
||||
* must use points or percentages. Ems and other units are not supported.
|
||||
*
|
||||
* See https://developer.mozilla.org/en-US/docs/Web/CSS/inset-block-end
|
||||
* for more details of how `inset-block-end` affects layout.
|
||||
*/
|
||||
insetBlockEnd?: DimensionValue,
|
||||
|
||||
/** `insetBlockStart` is a logical property that sets the length that an
|
||||
* element is offset in the block direction from its starting edge.
|
||||
*
|
||||
* It works similarly to `inset-block-start` in CSS, but in React Native you
|
||||
* must use points or percentages. Ems and other units are not supported.
|
||||
*
|
||||
* See https://developer.mozilla.org/en-US/docs/Web/CSS/inset-block-start
|
||||
* for more details of how `inset-block-start` affects layout.
|
||||
*/
|
||||
insetBlockStart?: DimensionValue,
|
||||
|
||||
/** `insetInline` is a shorthand that corresponds to the `insetInlineStart` and `insetInlineEnd` properties.
|
||||
*
|
||||
* It works similarly to `inset-inline` in CSS, but in React Native you
|
||||
* must use points or percentages. Ems and other units are not supported.
|
||||
*
|
||||
* See https://developer.mozilla.org/en-US/docs/Web/CSS/inset-inline
|
||||
* for more details of how `inset-inline` affects layout.
|
||||
*/
|
||||
insetInline?: DimensionValue,
|
||||
|
||||
/** `insetInlineEnd` is a logical property that sets the length that an
|
||||
* element is offset in the starting inline direction.
|
||||
*
|
||||
* It works similarly to `inset-inline-end` in CSS, but in React Native you
|
||||
* must use points or percentages. Ems and other units are not supported.
|
||||
*
|
||||
* See https://developer.mozilla.org/en-US/docs/Web/CSS/inset-inline-end
|
||||
* for more details of how `inset-inline-end` affects layout.
|
||||
*/
|
||||
insetInlineEnd?: DimensionValue,
|
||||
|
||||
/** `insetInlineStart` is a logical property that sets the length that an
|
||||
* element is offset in the starting inline direction.
|
||||
*
|
||||
* It works similarly to `inset-inline-start` in CSS, but in React Native you
|
||||
* must use points or percentages. Ems and other units are not supported.
|
||||
*
|
||||
* See https://developer.mozilla.org/en-US/docs/Web/CSS/inset-inline-start
|
||||
* for more details of how `inset-inline-start` affects layout.
|
||||
*/
|
||||
insetInlineStart?: DimensionValue,
|
||||
|
||||
/** `minWidth` is the minimum width for this component, in logical pixels.
|
||||
*
|
||||
* It works similarly to `min-width` in CSS, but in React Native you
|
||||
|
||||
@@ -256,21 +256,21 @@ static void *TextFieldSelectionObservingContext = &TextFieldSelectionObservingCo
|
||||
|
||||
- (void)textViewDidChange:(__unused UITextView *)textView
|
||||
{
|
||||
if (_ignoreNextTextInputCall && [_lastStringStateWasUpdatedWith isEqual:_backedTextInputView.attributedText]) {
|
||||
if (_ignoreNextTextInputCall) {
|
||||
_ignoreNextTextInputCall = NO;
|
||||
return;
|
||||
}
|
||||
_lastStringStateWasUpdatedWith = _backedTextInputView.attributedText;
|
||||
_textDidChangeIsComing = NO;
|
||||
[_backedTextInputView.textInputDelegate textInputDidChange];
|
||||
}
|
||||
|
||||
- (void)textViewDidChangeSelection:(__unused UITextView *)textView
|
||||
{
|
||||
if (![_lastStringStateWasUpdatedWith isEqual:_backedTextInputView.attributedText]) {
|
||||
if (_lastStringStateWasUpdatedWith && ![_lastStringStateWasUpdatedWith isEqual:_backedTextInputView.attributedText]) {
|
||||
[self textViewDidChange:_backedTextInputView];
|
||||
_ignoreNextTextInputCall = YES;
|
||||
}
|
||||
_lastStringStateWasUpdatedWith = _backedTextInputView.attributedText;
|
||||
[self textViewProbablyDidChangeSelection];
|
||||
}
|
||||
|
||||
|
||||
@@ -126,11 +126,13 @@ Pod::Spec.new do |s|
|
||||
end
|
||||
|
||||
s.dependency "RCT-Folly", folly_version
|
||||
s.dependency "React-cxxreact", version
|
||||
s.dependency "React-perflogger", version
|
||||
s.dependency "React-jsi", version
|
||||
s.dependency "React-jsiexecutor", version
|
||||
s.dependency "React-cxxreact"
|
||||
s.dependency "React-perflogger"
|
||||
s.dependency "React-jsi"
|
||||
s.dependency "React-jsiexecutor"
|
||||
s.dependency "React-utils"
|
||||
s.dependency "SocketRocket", socket_rocket_version
|
||||
s.dependency "React-runtimeexecutor"
|
||||
s.dependency "Yoga"
|
||||
s.dependency "glog"
|
||||
|
||||
|
||||
@@ -312,7 +312,17 @@ static void attemptAsynchronousLoadOfBundleAtURL(
|
||||
return;
|
||||
}
|
||||
|
||||
RCTSource *source = RCTSourceCreate(scriptURL, data, data.length);
|
||||
// Prefer `Content-Location` as the canonical source URL, if given, or fall back to scriptURL.
|
||||
NSURL *sourceURL = scriptURL;
|
||||
NSString *contentLocationHeader = headers[@"Content-Location"];
|
||||
if (contentLocationHeader) {
|
||||
NSURL *contentLocationURL = [NSURL URLWithString:contentLocationHeader relativeToURL:scriptURL];
|
||||
if (contentLocationURL) {
|
||||
sourceURL = contentLocationURL;
|
||||
}
|
||||
}
|
||||
|
||||
RCTSource *source = RCTSourceCreate(sourceURL, data, data.length);
|
||||
parseHeaders(headers, source);
|
||||
onComplete(nil, source);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#ifdef __cplusplus
|
||||
#import <ReactCommon/RuntimeExecutor.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class RCTBridge;
|
||||
|
||||
facebook::react::RuntimeExecutor RCTRuntimeExecutorFromBridge(RCTBridge *bridge);
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
#endif
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
#import "RCTRuntimeExecutorFromBridge.h"
|
||||
#import <React/RCTAssert.h>
|
||||
#import <React/RCTBridge+Private.h>
|
||||
#import <cxxreact/MessageQueueThread.h>
|
||||
#import <react/utils/ManagedObjectWrapper.h>
|
||||
|
||||
using namespace facebook::react;
|
||||
|
||||
@interface RCTBridge ()
|
||||
- (std::shared_ptr<MessageQueueThread>)jsMessageThread;
|
||||
- (void)invokeAsync:(std::function<void()> &&)func;
|
||||
@end
|
||||
|
||||
RuntimeExecutor RCTRuntimeExecutorFromBridge(RCTBridge *bridge)
|
||||
{
|
||||
RCTAssert(bridge, @"RCTRuntimeExecutorFromBridge: Bridge must not be nil.");
|
||||
|
||||
auto bridgeWeakWrapper = wrapManagedObjectWeakly([bridge batchedBridge] ?: bridge);
|
||||
|
||||
RuntimeExecutor runtimeExecutor = [bridgeWeakWrapper](
|
||||
std::function<void(facebook::jsi::Runtime & runtime)> &&callback) {
|
||||
RCTBridge *bridge = unwrapManagedObjectWeakly(bridgeWeakWrapper);
|
||||
|
||||
RCTAssert(bridge, @"RCTRuntimeExecutorFromBridge: Bridge must not be nil at the moment of scheduling a call.");
|
||||
|
||||
[bridge invokeAsync:[bridgeWeakWrapper, callback = std::move(callback)]() {
|
||||
RCTCxxBridge *batchedBridge = (RCTCxxBridge *)unwrapManagedObjectWeakly(bridgeWeakWrapper);
|
||||
|
||||
RCTAssert(batchedBridge, @"RCTRuntimeExecutorFromBridge: Bridge must not be nil at the moment of invocation.");
|
||||
|
||||
if (!batchedBridge) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto runtime = (facebook::jsi::Runtime *)(batchedBridge.runtime);
|
||||
|
||||
RCTAssert(
|
||||
runtime, @"RCTRuntimeExecutorFromBridge: Bridge must have a valid jsi::Runtime at the moment of invocation.");
|
||||
|
||||
if (!runtime) {
|
||||
return;
|
||||
}
|
||||
|
||||
callback(*runtime);
|
||||
}];
|
||||
};
|
||||
|
||||
return runtimeExecutor;
|
||||
}
|
||||
@@ -21,8 +21,8 @@ NSDictionary* RCTGetReactNativeVersion(void)
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^(void){
|
||||
__rnVersion = @{
|
||||
RCTVersionMajor: @(1000),
|
||||
RCTVersionMinor: @(0),
|
||||
RCTVersionMajor: @(0),
|
||||
RCTVersionMinor: @(72),
|
||||
RCTVersionPatch: @(0),
|
||||
RCTVersionPrerelease: [NSNull null],
|
||||
};
|
||||
|
||||
@@ -428,10 +428,10 @@ RCT_EXPORT_METHOD(show)
|
||||
? UIAlertControllerStyleActionSheet
|
||||
: UIAlertControllerStyleAlert;
|
||||
|
||||
NSString *debugMenuType = self.bridge ? @"Bridge" : @"Bridgeless";
|
||||
NSString *debugMenuTitle = [NSString stringWithFormat:@"React Native Debug Menu (%@)", debugMenuType];
|
||||
NSString *devMenuType = self.bridge ? @"Bridge" : @"Bridgeless";
|
||||
NSString *devMenuTitle = [NSString stringWithFormat:@"React Native Dev Menu (%@)", devMenuType];
|
||||
|
||||
_actionSheet = [UIAlertController alertControllerWithTitle:debugMenuTitle message:description preferredStyle:style];
|
||||
_actionSheet = [UIAlertController alertControllerWithTitle:devMenuTitle message:description preferredStyle:style];
|
||||
|
||||
NSArray<RCTDevMenuItem *> *items = [self _menuItemsToPresent];
|
||||
for (RCTDevMenuItem *item in items) {
|
||||
|
||||
@@ -475,6 +475,7 @@ struct RCTInstanceCallback : public InstanceCallback {
|
||||
// Load the source asynchronously, then store it for later execution.
|
||||
dispatch_group_enter(prepareBridge);
|
||||
__block NSData *sourceCode;
|
||||
__block NSURL *sourceURL = self.bundleURL;
|
||||
|
||||
#if (RCT_DEV | RCT_ENABLE_LOADING_VIEW) && __has_include(<React/RCTDevLoadingViewProtocol.h>)
|
||||
{
|
||||
@@ -490,6 +491,9 @@ struct RCTInstanceCallback : public InstanceCallback {
|
||||
}
|
||||
|
||||
sourceCode = source.data;
|
||||
if (source.url) {
|
||||
sourceURL = source.url;
|
||||
}
|
||||
dispatch_group_leave(prepareBridge);
|
||||
}
|
||||
onProgress:^(RCTLoadingProgress *progressData) {
|
||||
@@ -504,7 +508,7 @@ struct RCTInstanceCallback : public InstanceCallback {
|
||||
dispatch_group_notify(prepareBridge, dispatch_get_global_queue(QOS_CLASS_USER_INTERACTIVE, 0), ^{
|
||||
RCTCxxBridge *strongSelf = weakSelf;
|
||||
if (sourceCode && strongSelf.loading) {
|
||||
[strongSelf executeSourceCode:sourceCode sync:NO];
|
||||
[strongSelf executeSourceCode:sourceCode withSourceURL:sourceURL sync:NO];
|
||||
}
|
||||
});
|
||||
RCT_PROFILE_END_EVENT(RCTProfileTagAlways, @"");
|
||||
@@ -1050,7 +1054,7 @@ struct RCTInstanceCallback : public InstanceCallback {
|
||||
[_displayLink registerModuleForFrameUpdates:module withModuleData:moduleData];
|
||||
}
|
||||
|
||||
- (void)executeSourceCode:(NSData *)sourceCode sync:(BOOL)sync
|
||||
- (void)executeSourceCode:(NSData *)sourceCode withSourceURL:(NSURL *)url sync:(BOOL)sync
|
||||
{
|
||||
// This will get called from whatever thread was actually executing JS.
|
||||
dispatch_block_t completion = ^{
|
||||
@@ -1075,12 +1079,13 @@ struct RCTInstanceCallback : public InstanceCallback {
|
||||
};
|
||||
|
||||
if (sync) {
|
||||
[self executeApplicationScriptSync:sourceCode url:self.bundleURL];
|
||||
[self executeApplicationScriptSync:sourceCode url:url];
|
||||
completion();
|
||||
} else {
|
||||
[self enqueueApplicationScript:sourceCode url:self.bundleURL onComplete:completion];
|
||||
[self enqueueApplicationScript:sourceCode url:url onComplete:completion];
|
||||
}
|
||||
|
||||
// Use the original request URL here - HMRClient uses this to derive the /hot URL and entry point.
|
||||
[self.devSettings setupHMRClientWithBundleURL:self.bundleURL];
|
||||
}
|
||||
|
||||
|
||||
+12
-1
@@ -14,6 +14,7 @@
|
||||
#import <react/renderer/components/image/ImageComponentDescriptor.h>
|
||||
#import <react/renderer/components/image/ImageEventEmitter.h>
|
||||
#import <react/renderer/components/image/ImageProps.h>
|
||||
#import <react/renderer/core/CoreFeatures.h>
|
||||
#import <react/renderer/imagemanager/ImageRequest.h>
|
||||
#import <react/renderer/imagemanager/RCTImagePrimitivesConversions.h>
|
||||
|
||||
@@ -97,8 +98,18 @@ using namespace facebook::react;
|
||||
- (void)_setStateAndResubscribeImageResponseObserver:(ImageShadowNode::ConcreteState::Shared const &)state
|
||||
{
|
||||
if (_state) {
|
||||
auto &observerCoordinator = _state->getData().getImageRequest().getObserverCoordinator();
|
||||
auto const &imageRequest = _state->getData().getImageRequest();
|
||||
auto &observerCoordinator = imageRequest.getObserverCoordinator();
|
||||
observerCoordinator.removeObserver(_imageResponseObserverProxy);
|
||||
if (CoreFeatures::cancelImageDownloadsOnRecycle) {
|
||||
// Cancelling image request because we are no longer observing it.
|
||||
// This is not 100% correct place to do this because we may want to
|
||||
// re-create RCTImageComponentView with the same image and if it
|
||||
// was cancelled before downloaded, download is not resumed.
|
||||
// This will only become issue if we decouple life cycle of a
|
||||
// ShadowNode from ComponentView, which is not something we do now.
|
||||
imageRequest.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
_state = state;
|
||||
|
||||
+1
-1
@@ -50,7 +50,7 @@
|
||||
|
||||
- (void)handleCommand:(NSString *)commandName args:(NSArray *)args
|
||||
{
|
||||
[_coordinator handleCommand:commandName args:args reactTag:_tag];
|
||||
[_coordinator handleCommand:commandName args:args reactTag:_tag paperView:self.paperView];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -285,6 +285,10 @@ static BackgroundExecutor RCTGetBackgroundExecutor()
|
||||
CoreFeatures::cacheNSTextStorage = true;
|
||||
}
|
||||
|
||||
if (reactNativeConfig && reactNativeConfig->getBool("react_fabric:cancel_image_downloads_on_recycle")) {
|
||||
CoreFeatures::cancelImageDownloadsOnRecycle = true;
|
||||
}
|
||||
|
||||
auto componentRegistryFactory =
|
||||
[factory = wrapManagedObject(_mountingManager.componentViewRegistry.componentViewFactory)](
|
||||
EventDispatcher::Weak const &eventDispatcher, ContextContainer::Shared const &contextContainer) {
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
*/
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <ReactCommon/RuntimeExecutor.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <react/utils/ContextContainer.h>
|
||||
|
||||
@@ -15,8 +14,6 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
@class RCTSurfacePresenter;
|
||||
@class RCTBridge;
|
||||
|
||||
facebook::react::RuntimeExecutor RCTRuntimeExecutorFromBridge(RCTBridge *bridge);
|
||||
|
||||
/*
|
||||
* Controls a life-cycle of a Surface Presenter based on Bridge's life-cycle.
|
||||
* We are moving away from using Bridge.
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#import <React/RCTConstants.h>
|
||||
#import <React/RCTImageLoader.h>
|
||||
#import <React/RCTImageLoaderWithAttributionProtocol.h>
|
||||
#import <React/RCTRuntimeExecutorFromBridge.h>
|
||||
#import <React/RCTSurfacePresenter.h>
|
||||
#import <React/RCTSurfacePresenterStub.h>
|
||||
|
||||
@@ -42,43 +43,6 @@ static ContextContainer::Shared RCTContextContainerFromBridge(RCTBridge *bridge)
|
||||
return contextContainer;
|
||||
}
|
||||
|
||||
RuntimeExecutor RCTRuntimeExecutorFromBridge(RCTBridge *bridge)
|
||||
{
|
||||
RCTAssert(bridge, @"RCTRuntimeExecutorFromBridge: Bridge must not be nil.");
|
||||
|
||||
auto bridgeWeakWrapper = wrapManagedObjectWeakly([bridge batchedBridge] ?: bridge);
|
||||
|
||||
RuntimeExecutor runtimeExecutor = [bridgeWeakWrapper](
|
||||
std::function<void(facebook::jsi::Runtime & runtime)> &&callback) {
|
||||
RCTBridge *bridge = unwrapManagedObjectWeakly(bridgeWeakWrapper);
|
||||
|
||||
RCTAssert(bridge, @"RCTRuntimeExecutorFromBridge: Bridge must not be nil at the moment of scheduling a call.");
|
||||
|
||||
[bridge invokeAsync:[bridgeWeakWrapper, callback = std::move(callback)]() {
|
||||
RCTCxxBridge *batchedBridge = (RCTCxxBridge *)unwrapManagedObjectWeakly(bridgeWeakWrapper);
|
||||
|
||||
RCTAssert(batchedBridge, @"RCTRuntimeExecutorFromBridge: Bridge must not be nil at the moment of invocation.");
|
||||
|
||||
if (!batchedBridge) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto runtime = (facebook::jsi::Runtime *)(batchedBridge.runtime);
|
||||
|
||||
RCTAssert(
|
||||
runtime, @"RCTRuntimeExecutorFromBridge: Bridge must have a valid jsi::Runtime at the moment of invocation.");
|
||||
|
||||
if (!runtime) {
|
||||
return;
|
||||
}
|
||||
|
||||
callback(*runtime);
|
||||
}];
|
||||
};
|
||||
|
||||
return runtimeExecutor;
|
||||
}
|
||||
|
||||
@implementation RCTSurfacePresenterBridgeAdapter {
|
||||
RCTSurfacePresenter *_Nullable _surfacePresenter;
|
||||
__weak RCTBridge *_bridge;
|
||||
|
||||
@@ -29,7 +29,6 @@ header_search_paths = [
|
||||
"\"$(PODS_ROOT)/Headers/Private/React-Core\"",
|
||||
"\"$(PODS_ROOT)/Headers/Public/React-Codegen\"",
|
||||
"\"${PODS_CONFIGURATION_BUILD_DIR}/React-Codegen/React_Codegen.framework/Headers\"",
|
||||
|
||||
]
|
||||
|
||||
if ENV['USE_FRAMEWORKS']
|
||||
@@ -41,6 +40,9 @@ if ENV['USE_FRAMEWORKS']
|
||||
header_search_paths << "\"${PODS_CONFIGURATION_BUILD_DIR}/React-graphics/React_graphics.framework/Headers/react/renderer/graphics/platform/ios\""
|
||||
header_search_paths << "\"${PODS_CONFIGURATION_BUILD_DIR}/React-ImageManager/React_ImageManager.framework/Headers\""
|
||||
header_search_paths << "\"${PODS_CONFIGURATION_BUILD_DIR}/React-RCTFabric/RCTFabric.framework/Headers\""
|
||||
header_search_paths << "\"${PODS_CONFIGURATION_BUILD_DIR}/React-debug/React_debug.framework/Headers\""
|
||||
header_search_paths << "\"${PODS_CONFIGURATION_BUILD_DIR}/React-utils/React_utils.framework/Headers\""
|
||||
header_search_paths << "\"${PODS_CONFIGURATION_BUILD_DIR}/React-runtimescheduler/React_runtimescheduler.framework/Headers\""
|
||||
end
|
||||
|
||||
Pod::Spec.new do |s|
|
||||
@@ -72,6 +74,17 @@ Pod::Spec.new do |s|
|
||||
s.dependency "React-RCTImage", version
|
||||
s.dependency "React-ImageManager"
|
||||
s.dependency "RCT-Folly/Fabric", folly_version
|
||||
s.dependency "glog"
|
||||
s.dependency "Yoga"
|
||||
s.dependency "React-RCTText"
|
||||
s.dependency "React-utils"
|
||||
s.dependency "React-runtimescheduler"
|
||||
|
||||
if ENV["USE_HERMES"] == nil || ENV["USE_HERMES"] == "1"
|
||||
s.dependency "hermes-engine"
|
||||
else
|
||||
s.dependency "React-jsi"
|
||||
end
|
||||
|
||||
s.test_spec 'Tests' do |test_spec|
|
||||
test_spec.source_files = "Tests/**/*.{mm}"
|
||||
|
||||
@@ -469,7 +469,10 @@ android {
|
||||
"-DREACT_BUILD_DIR=$buildDir",
|
||||
"-DANDROID_STL=c++_shared",
|
||||
"-DANDROID_TOOLCHAIN=clang",
|
||||
"-DANDROID_PLATFORM=android-21"
|
||||
"-DANDROID_PLATFORM=android-21",
|
||||
// Due to https://github.com/android/ndk/issues/1693 we're losing Android
|
||||
// specific compilation flags. This can be removed once we moved to NDK 25/26
|
||||
"-DANDROID_USE_LEGACY_TOOLCHAIN_FILE=ON"
|
||||
|
||||
targets "jsijniprofiler",
|
||||
"reactnativeblob",
|
||||
|
||||
@@ -46,7 +46,20 @@ target_include_directories(${CMAKE_PROJECT_NAME}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
${PROJECT_BUILD_DIR}/generated/rncli/src/main/jni)
|
||||
|
||||
target_compile_options(${CMAKE_PROJECT_NAME} PRIVATE -Wall -Werror -fexceptions -frtti -std=c++17 -DWITH_INSPECTOR=1 -DLOG_TAG=\"ReactNative\")
|
||||
target_compile_options(${CMAKE_PROJECT_NAME}
|
||||
PRIVATE
|
||||
-Wall
|
||||
-Werror
|
||||
# We suppress cpp #error and #warning to don't fail the build
|
||||
# due to use migrating away from
|
||||
# #include <react/renderer/graphics/conversions.h>
|
||||
# This can be removed for React Native 0.73
|
||||
-Wno-error=cpp
|
||||
-fexceptions
|
||||
-frtti
|
||||
-std=c++17
|
||||
-DWITH_INSPECTOR=1
|
||||
-DLOG_TAG=\"ReactNative\")
|
||||
|
||||
# Prefab packages from React Native
|
||||
find_package(ReactAndroid REQUIRED CONFIG)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
VERSION_NAME=1000.0.0
|
||||
VERSION_NAME=0.72.0
|
||||
GROUP=com.facebook.react
|
||||
|
||||
# JVM Versions
|
||||
|
||||
@@ -154,6 +154,9 @@ android {
|
||||
// We intentionally build Hermes with Intl support only. This is to simplify
|
||||
// the build setup and to avoid overcomplicating the build-type matrix.
|
||||
arguments "-DHERMES_ENABLE_INTL=True"
|
||||
// Due to https://github.com/android/ndk/issues/1693 we're losing Android
|
||||
// specific compilation flags. This can be removed once we moved to NDK 25/26
|
||||
arguments "-DANDROID_USE_LEGACY_TOOLCHAIN_FILE=ON"
|
||||
targets "libhermes"
|
||||
}
|
||||
}
|
||||
@@ -178,6 +181,8 @@ android {
|
||||
// This has the (unlucky) side effect of letting AGP call the build
|
||||
// tasks `configureCMakeRelease` while is actually building the debug flavor.
|
||||
arguments "-DCMAKE_BUILD_TYPE=Release"
|
||||
// Adding -O3 to patch the issue here: https://github.com/android/ndk/issues/1740#issuecomment-1198438260
|
||||
cppFlags "-O3"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+40
-1
@@ -11,6 +11,7 @@ import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.view.KeyEvent;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import com.facebook.infer.annotation.Assertions;
|
||||
import com.facebook.react.devsupport.DoubleTapReloadRecognizer;
|
||||
@@ -33,6 +34,8 @@ public class ReactDelegate {
|
||||
|
||||
private ReactNativeHost mReactNativeHost;
|
||||
|
||||
private boolean mFabricEnabled = false;
|
||||
|
||||
public ReactDelegate(
|
||||
Activity activity,
|
||||
ReactNativeHost reactNativeHost,
|
||||
@@ -45,6 +48,20 @@ public class ReactDelegate {
|
||||
mReactNativeHost = reactNativeHost;
|
||||
}
|
||||
|
||||
public ReactDelegate(
|
||||
Activity activity,
|
||||
ReactNativeHost reactNativeHost,
|
||||
@Nullable String appKey,
|
||||
@Nullable Bundle launchOptions,
|
||||
boolean fabricEnabled) {
|
||||
mActivity = activity;
|
||||
mMainComponentName = appKey;
|
||||
mLaunchOptions = composeLaunchOptions(launchOptions);
|
||||
mDoubleTapReloadRecognizer = new DoubleTapReloadRecognizer();
|
||||
mReactNativeHost = reactNativeHost;
|
||||
mFabricEnabled = fabricEnabled;
|
||||
}
|
||||
|
||||
public void onHostResume() {
|
||||
if (getReactNativeHost().hasInstance()) {
|
||||
if (mActivity instanceof DefaultHardwareBackBtnHandler) {
|
||||
@@ -109,7 +126,9 @@ public class ReactDelegate {
|
||||
}
|
||||
|
||||
protected ReactRootView createRootView() {
|
||||
return new ReactRootView(mActivity);
|
||||
ReactRootView reactRootView = new ReactRootView(mActivity);
|
||||
reactRootView.setIsFabric(isFabricEnabled());
|
||||
return reactRootView;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -144,4 +163,24 @@ public class ReactDelegate {
|
||||
public ReactInstanceManager getReactInstanceManager() {
|
||||
return getReactNativeHost().getReactInstanceManager();
|
||||
}
|
||||
|
||||
/**
|
||||
* Override this method if you wish to selectively toggle Fabric for a specific surface. This will
|
||||
* also control if Concurrent Root (React 18) should be enabled or not.
|
||||
*
|
||||
* @return true if Fabric is enabled for this Activity, false otherwise.
|
||||
*/
|
||||
protected boolean isFabricEnabled() {
|
||||
return mFabricEnabled;
|
||||
}
|
||||
|
||||
private @NonNull Bundle composeLaunchOptions(Bundle composedLaunchOptions) {
|
||||
if (isFabricEnabled()) {
|
||||
if (composedLaunchOptions == null) {
|
||||
composedLaunchOptions = new Bundle();
|
||||
}
|
||||
composedLaunchOptions.putBoolean("concurrentRoot", true);
|
||||
}
|
||||
return composedLaunchOptions;
|
||||
}
|
||||
}
|
||||
|
||||
+22
-7
@@ -16,6 +16,7 @@ import android.view.KeyEvent;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import com.facebook.react.modules.core.PermissionAwareActivity;
|
||||
@@ -29,6 +30,7 @@ public class ReactFragment extends Fragment implements PermissionAwareActivity {
|
||||
|
||||
protected static final String ARG_COMPONENT_NAME = "arg_component_name";
|
||||
protected static final String ARG_LAUNCH_OPTIONS = "arg_launch_options";
|
||||
protected static final String ARG_FABRIC_ENABLED = "arg_fabric_enabled";
|
||||
|
||||
private ReactDelegate mReactDelegate;
|
||||
|
||||
@@ -40,13 +42,16 @@ public class ReactFragment extends Fragment implements PermissionAwareActivity {
|
||||
|
||||
/**
|
||||
* @param componentName The name of the react native component
|
||||
* @param fabricEnabled Flag to enable Fabric for ReactFragment
|
||||
* @return A new instance of fragment ReactFragment.
|
||||
*/
|
||||
private static ReactFragment newInstance(String componentName, Bundle launchOptions) {
|
||||
private static ReactFragment newInstance(
|
||||
String componentName, Bundle launchOptions, Boolean fabricEnabled) {
|
||||
ReactFragment fragment = new ReactFragment();
|
||||
Bundle args = new Bundle();
|
||||
args.putString(ARG_COMPONENT_NAME, componentName);
|
||||
args.putBundle(ARG_LAUNCH_OPTIONS, launchOptions);
|
||||
args.putBoolean(ARG_FABRIC_ENABLED, fabricEnabled);
|
||||
fragment.setArguments(args);
|
||||
return fragment;
|
||||
}
|
||||
@@ -57,15 +62,18 @@ public class ReactFragment extends Fragment implements PermissionAwareActivity {
|
||||
super.onCreate(savedInstanceState);
|
||||
String mainComponentName = null;
|
||||
Bundle launchOptions = null;
|
||||
Boolean fabricEnabled = null;
|
||||
if (getArguments() != null) {
|
||||
mainComponentName = getArguments().getString(ARG_COMPONENT_NAME);
|
||||
launchOptions = getArguments().getBundle(ARG_LAUNCH_OPTIONS);
|
||||
fabricEnabled = getArguments().getBoolean(ARG_FABRIC_ENABLED);
|
||||
}
|
||||
if (mainComponentName == null) {
|
||||
throw new IllegalStateException("Cannot loadApp if component name is null");
|
||||
}
|
||||
mReactDelegate =
|
||||
new ReactDelegate(getActivity(), getReactNativeHost(), mainComponentName, launchOptions);
|
||||
new ReactDelegate(
|
||||
getActivity(), getReactNativeHost(), mainComponentName, launchOptions, fabricEnabled);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -85,7 +93,7 @@ public class ReactFragment extends Fragment implements PermissionAwareActivity {
|
||||
|
||||
@Override
|
||||
public View onCreateView(
|
||||
LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
mReactDelegate.loadApp();
|
||||
return mReactDelegate.getReactRootView();
|
||||
}
|
||||
@@ -140,7 +148,7 @@ public class ReactFragment extends Fragment implements PermissionAwareActivity {
|
||||
|
||||
@Override
|
||||
public void onRequestPermissionsResult(
|
||||
int requestCode, String[] permissions, int[] grantResults) {
|
||||
int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
|
||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||
if (mPermissionListener != null
|
||||
&& mPermissionListener.onRequestPermissionsResult(requestCode, permissions, grantResults)) {
|
||||
@@ -170,12 +178,14 @@ public class ReactFragment extends Fragment implements PermissionAwareActivity {
|
||||
/** Builder class to help instantiate a ReactFragment */
|
||||
public static class Builder {
|
||||
|
||||
String mComponentName;
|
||||
Bundle mLaunchOptions;
|
||||
@Nullable String mComponentName;
|
||||
@Nullable Bundle mLaunchOptions;
|
||||
@Nullable Boolean mFabricEnabled;
|
||||
|
||||
public Builder() {
|
||||
mComponentName = null;
|
||||
mLaunchOptions = null;
|
||||
mFabricEnabled = null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -201,7 +211,12 @@ public class ReactFragment extends Fragment implements PermissionAwareActivity {
|
||||
}
|
||||
|
||||
public ReactFragment build() {
|
||||
return ReactFragment.newInstance(mComponentName, mLaunchOptions);
|
||||
return ReactFragment.newInstance(mComponentName, mLaunchOptions, mFabricEnabled);
|
||||
}
|
||||
|
||||
public Builder setFabricEnabled(boolean fabricEnabled) {
|
||||
mFabricEnabled = fabricEnabled;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+9
@@ -1360,6 +1360,15 @@ public class ReactInstanceManager {
|
||||
|
||||
reactContext.initializeWithInstance(catalystInstance);
|
||||
|
||||
if (ReactFeatureFlags.unstable_useRuntimeSchedulerAlways) {
|
||||
// On Old Architecture, we need to initialize the Native Runtime Scheduler so that
|
||||
// the `nativeRuntimeScheduler` object is registered on JS.
|
||||
// On New Architecture, this is normally triggered by instantiate a TurboModuleManager.
|
||||
// Here we invoke getRuntimeScheduler() to trigger the creation of it regardless of the
|
||||
// architecture so it will always be there.
|
||||
catalystInstance.getRuntimeScheduler();
|
||||
}
|
||||
|
||||
if (ReactFeatureFlags.useTurboModules && mTMMDelegateBuilder != null) {
|
||||
TurboModuleManagerDelegate tmmDelegate =
|
||||
mTMMDelegateBuilder
|
||||
|
||||
+10
@@ -295,6 +295,16 @@ public class NativeAnimatedModule extends NativeAnimatedModuleSpec
|
||||
mCurrentFrameNumber++;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void willMountItems(UIManager uiManager) {
|
||||
// noop
|
||||
}
|
||||
|
||||
@Override
|
||||
public void didMountItems(UIManager uiManager) {
|
||||
// noop
|
||||
}
|
||||
|
||||
// For FabricUIManager only
|
||||
@Override
|
||||
@UiThread
|
||||
|
||||
+29
@@ -21,6 +21,7 @@ import com.facebook.common.logging.FLog;
|
||||
import com.facebook.infer.annotation.Assertions;
|
||||
import com.facebook.infer.annotation.ThreadConfined;
|
||||
import com.facebook.proguard.annotations.DoNotStrip;
|
||||
import com.facebook.react.bridge.interop.InteropModuleRegistry;
|
||||
import com.facebook.react.bridge.queue.MessageQueueThread;
|
||||
import com.facebook.react.bridge.queue.ReactQueueConfiguration;
|
||||
import com.facebook.react.common.LifecycleState;
|
||||
@@ -69,6 +70,8 @@ public class ReactContext extends ContextWrapper {
|
||||
private @Nullable JSExceptionHandler mJSExceptionHandler;
|
||||
private @Nullable JSExceptionHandler mExceptionHandlerWrapper;
|
||||
private @Nullable WeakReference<Activity> mCurrentActivity;
|
||||
|
||||
private @Nullable InteropModuleRegistry mInteropModuleRegistry;
|
||||
private boolean mIsInitialized = false;
|
||||
|
||||
public ReactContext(Context base) {
|
||||
@@ -93,6 +96,7 @@ public class ReactContext extends ContextWrapper {
|
||||
|
||||
ReactQueueConfiguration queueConfig = catalystInstance.getReactQueueConfiguration();
|
||||
initializeMessageQueueThreads(queueConfig);
|
||||
initializeInteropModules();
|
||||
}
|
||||
|
||||
/** Initialize message queue threads using a ReactQueueConfiguration. */
|
||||
@@ -120,6 +124,14 @@ public class ReactContext extends ContextWrapper {
|
||||
mIsInitialized = true;
|
||||
}
|
||||
|
||||
protected void initializeInteropModules() {
|
||||
mInteropModuleRegistry = new InteropModuleRegistry();
|
||||
}
|
||||
|
||||
protected void initializeInteropModules(ReactContext reactContext) {
|
||||
mInteropModuleRegistry = reactContext.mInteropModuleRegistry;
|
||||
}
|
||||
|
||||
public void resetPerfStats() {
|
||||
if (mNativeModulesMessageQueueThread != null) {
|
||||
mNativeModulesMessageQueueThread.resetPerfStats();
|
||||
@@ -163,6 +175,10 @@ public class ReactContext extends ContextWrapper {
|
||||
}
|
||||
throw new IllegalStateException(EARLY_JS_ACCESS_EXCEPTION_MESSAGE);
|
||||
}
|
||||
if (mInteropModuleRegistry != null
|
||||
&& mInteropModuleRegistry.shouldReturnInteropModule(jsInterface)) {
|
||||
return mInteropModuleRegistry.getInteropModule(jsInterface);
|
||||
}
|
||||
return mCatalystInstance.getJSModule(jsInterface);
|
||||
}
|
||||
|
||||
@@ -546,4 +562,17 @@ public class ReactContext extends ContextWrapper {
|
||||
Assertions.assertNotNull(mCatalystInstance).registerSegment(segmentId, path);
|
||||
Assertions.assertNotNull(callback).invoke();
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a {@link JavaScriptModule} within the Interop Layer so that can be consumed whenever
|
||||
* getJSModule is invoked.
|
||||
*
|
||||
* <p>This method is internal to React Native and should not be used externally.
|
||||
*/
|
||||
public <T extends JavaScriptModule> void internal_registerInteropModule(
|
||||
Class<T> interopModuleInterface, Object interopModule) {
|
||||
if (mInteropModuleRegistry != null) {
|
||||
mInteropModuleRegistry.registerInteropModule(interopModuleInterface, interopModule);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+25
-2
@@ -12,10 +12,33 @@ public interface UIManagerListener {
|
||||
/**
|
||||
* Called right before view updates are dispatched at the end of a batch. This is useful if a
|
||||
* module needs to add UIBlocks to the queue before it is flushed.
|
||||
*
|
||||
* <p>This is called by Paper only.
|
||||
*/
|
||||
void willDispatchViewUpdates(UIManager uiManager);
|
||||
/* Called right after view updates are dispatched for a frame. */
|
||||
/**
|
||||
* Called on UIThread right before view updates are executed.
|
||||
*
|
||||
* <p>This is called by Fabric only.
|
||||
*/
|
||||
void willMountItems(UIManager uiManager);
|
||||
/**
|
||||
* Called on UIThread right after view updates are executed.
|
||||
*
|
||||
* <p>This is called by Fabric only.
|
||||
*/
|
||||
void didMountItems(UIManager uiManager);
|
||||
/**
|
||||
* Called on UIThread right after view updates are dispatched for a frame. Note that this will be
|
||||
* called for every frame even if there are no updates.
|
||||
*
|
||||
* <p>This is called by Fabric only.
|
||||
*/
|
||||
void didDispatchMountItems(UIManager uiManager);
|
||||
/* Called right after scheduleMountItems is called in Fabric, after a new tree is committed. */
|
||||
/**
|
||||
* Called right after scheduleMountItems is called in Fabric, after a new tree is committed.
|
||||
*
|
||||
* <p>This is called by Fabric only.
|
||||
*/
|
||||
void didScheduleMountItems(UIManager uiManager);
|
||||
}
|
||||
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package com.facebook.react.bridge.interop;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import com.facebook.react.bridge.JavaScriptModule;
|
||||
import com.facebook.react.config.ReactFeatureFlags;
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
* A utility class that takes care of returning {@link JavaScriptModule} which are used for the
|
||||
* Fabric Interop Layer. This allows us to override the returned classes once the user is invoking
|
||||
* `ReactContext.getJsModule()`.
|
||||
*
|
||||
* <p>Currently we only support a `RCTEventEmitter` re-implementation, being `InteropEventEmitter`
|
||||
* but this class can support other re-implementation in the future.
|
||||
*/
|
||||
public class InteropModuleRegistry {
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
private final HashMap<Class, Object> supportedModules;
|
||||
|
||||
public InteropModuleRegistry() {
|
||||
this.supportedModules = new HashMap<>();
|
||||
}
|
||||
|
||||
public <T extends JavaScriptModule> boolean shouldReturnInteropModule(Class<T> requestedModule) {
|
||||
return checkReactFeatureFlagsConditions() && supportedModules.containsKey(requestedModule);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public <T extends JavaScriptModule> T getInteropModule(Class<T> requestedModule) {
|
||||
if (checkReactFeatureFlagsConditions()) {
|
||||
//noinspection unchecked
|
||||
return (T) supportedModules.get(requestedModule);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public <T extends JavaScriptModule> void registerInteropModule(
|
||||
Class<T> interopModuleInterface, Object interopModule) {
|
||||
if (checkReactFeatureFlagsConditions()) {
|
||||
supportedModules.put(interopModuleInterface, interopModule);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean checkReactFeatureFlagsConditions() {
|
||||
return ReactFeatureFlags.enableFabricRenderer && ReactFeatureFlags.unstable_useFabricInterop;
|
||||
}
|
||||
}
|
||||
+18
@@ -34,6 +34,21 @@ public class ReactFeatureFlags {
|
||||
*/
|
||||
public static volatile boolean enableFabricRenderer = false;
|
||||
|
||||
/**
|
||||
* Should this application enable the Fabric Interop Layer for Android? If yes, the application
|
||||
* will behave so that it can accept non-Fabric components and render them on Fabric. This toggle
|
||||
* is controlling extra logic such as custom event dispatching that are needed for the Fabric
|
||||
* Interop Layer to work correctly.
|
||||
*/
|
||||
public static volatile boolean unstable_useFabricInterop = false;
|
||||
|
||||
/**
|
||||
* Should this application always use the Native RuntimeScheduler? If yes, we'll be instantiating
|
||||
* it over all the architectures (both Old and New). This is intentionally set to true as we want
|
||||
* to use it more as a kill-switch to turn off this feature to potentially debug issues.
|
||||
*/
|
||||
public static volatile boolean unstable_useRuntimeSchedulerAlways = true;
|
||||
|
||||
/**
|
||||
* Feature flag to enable the new bridgeless architecture. Note: Enabling this will force enable
|
||||
* the following flags: `useTurboModules` & `enableFabricRenderer`.
|
||||
@@ -75,6 +90,9 @@ public class ReactFeatureFlags {
|
||||
/** Feature Flag to enable the pending event queue in fabric before mounting views */
|
||||
public static boolean enableFabricPendingEventQueue = false;
|
||||
|
||||
/** Feature Flag to enable caching mechanism of text measurement at shadow node level */
|
||||
public static boolean enableTextMeasureCachePerShadowNode = false;
|
||||
|
||||
/**
|
||||
* Feature flag that controls how turbo modules are exposed to JS
|
||||
*
|
||||
|
||||
+1
@@ -31,6 +31,7 @@ object DefaultNewArchitectureEntryPoint {
|
||||
) {
|
||||
ReactFeatureFlags.useTurboModules = turboModulesEnabled
|
||||
ReactFeatureFlags.enableFabricRenderer = fabricEnabled
|
||||
ReactFeatureFlags.unstable_useFabricInterop = fabricEnabled
|
||||
|
||||
this.privateFabricEnabled = fabricEnabled
|
||||
this.privateTurboModulesEnabled = turboModulesEnabled
|
||||
|
||||
+4
@@ -7,6 +7,7 @@
|
||||
|
||||
package com.facebook.react.defaults
|
||||
|
||||
import android.os.Bundle
|
||||
import com.facebook.react.ReactActivity
|
||||
import com.facebook.react.ReactActivityDelegate
|
||||
import com.facebook.react.ReactRootView
|
||||
@@ -43,4 +44,7 @@ open class DefaultReactActivityDelegate(
|
||||
|
||||
override fun createRootView(): ReactRootView =
|
||||
ReactRootView(context).apply { setIsFabric(fabricEnabled) }
|
||||
|
||||
override fun createRootView(bundle: Bundle?): ReactRootView =
|
||||
ReactRootView(context).apply { setIsFabric(fabricEnabled) }
|
||||
}
|
||||
|
||||
+26
-8
@@ -24,6 +24,7 @@ import android.util.Pair;
|
||||
import android.view.Gravity;
|
||||
import android.view.View;
|
||||
import android.widget.EditText;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
import androidx.annotation.Nullable;
|
||||
@@ -553,17 +554,30 @@ public abstract class DevSupportManagerBase implements DevSupportManager {
|
||||
return;
|
||||
}
|
||||
|
||||
final TextView textView = new TextView(getApplicationContext());
|
||||
textView.setText("React Native DevMenu (" + getUniqueTag() + ")");
|
||||
textView.setPadding(0, 50, 0, 0);
|
||||
textView.setGravity(Gravity.CENTER);
|
||||
textView.setTextColor(Color.BLACK);
|
||||
textView.setTextSize(17);
|
||||
textView.setTypeface(textView.getTypeface(), Typeface.BOLD);
|
||||
final LinearLayout header = new LinearLayout(getApplicationContext());
|
||||
header.setOrientation(LinearLayout.VERTICAL);
|
||||
|
||||
final TextView title = new TextView(getApplicationContext());
|
||||
title.setText("React Native Dev Menu (" + getUniqueTag() + ")");
|
||||
title.setPadding(0, 50, 0, 0);
|
||||
title.setGravity(Gravity.CENTER);
|
||||
title.setTextColor(Color.DKGRAY);
|
||||
title.setTextSize(16);
|
||||
title.setTypeface(title.getTypeface(), Typeface.BOLD);
|
||||
|
||||
final TextView jsExecutorLabel = new TextView(getApplicationContext());
|
||||
jsExecutorLabel.setText(getJSExecutorDescription());
|
||||
jsExecutorLabel.setPadding(0, 20, 0, 0);
|
||||
jsExecutorLabel.setGravity(Gravity.CENTER);
|
||||
jsExecutorLabel.setTextColor(Color.GRAY);
|
||||
jsExecutorLabel.setTextSize(14);
|
||||
|
||||
header.addView(title);
|
||||
header.addView(jsExecutorLabel);
|
||||
|
||||
mDevOptionsDialog =
|
||||
new AlertDialog.Builder(context)
|
||||
.setCustomTitle(textView)
|
||||
.setCustomTitle(header)
|
||||
.setItems(
|
||||
options.keySet().toArray(new String[0]),
|
||||
new DialogInterface.OnClickListener() {
|
||||
@@ -587,6 +601,10 @@ public abstract class DevSupportManagerBase implements DevSupportManager {
|
||||
}
|
||||
}
|
||||
|
||||
private String getJSExecutorDescription() {
|
||||
return "Running " + getReactInstanceDevHelper().getJavaScriptExecutorFactory().toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link ReactInstanceDevCommandsHandler} is responsible for enabling/disabling dev support when
|
||||
* a React view is attached/detached or when application state changes (e.g. the application is
|
||||
|
||||
+21
@@ -56,6 +56,7 @@ import com.facebook.react.config.ReactFeatureFlags;
|
||||
import com.facebook.react.fabric.events.EventBeatManager;
|
||||
import com.facebook.react.fabric.events.EventEmitterWrapper;
|
||||
import com.facebook.react.fabric.events.FabricEventEmitter;
|
||||
import com.facebook.react.fabric.interop.InteropEventEmitter;
|
||||
import com.facebook.react.fabric.mounting.MountItemDispatcher;
|
||||
import com.facebook.react.fabric.mounting.MountingManager;
|
||||
import com.facebook.react.fabric.mounting.SurfaceMountingManager;
|
||||
@@ -81,6 +82,7 @@ import com.facebook.react.uimanager.ViewManagerRegistry;
|
||||
import com.facebook.react.uimanager.events.EventCategoryDef;
|
||||
import com.facebook.react.uimanager.events.EventDispatcher;
|
||||
import com.facebook.react.uimanager.events.EventDispatcherImpl;
|
||||
import com.facebook.react.uimanager.events.RCTEventEmitter;
|
||||
import com.facebook.react.views.text.TextLayoutManager;
|
||||
import com.facebook.react.views.text.TextLayoutManagerMapBuffer;
|
||||
import java.util.HashMap;
|
||||
@@ -390,6 +392,11 @@ public class FabricUIManager implements UIManager, LifecycleEventListener {
|
||||
|
||||
ReactMarker.addFabricListener(mDevToolsReactPerfLogger);
|
||||
}
|
||||
if (ReactFeatureFlags.unstable_useFabricInterop) {
|
||||
InteropEventEmitter interopEventEmitter = new InteropEventEmitter(mReactApplicationContext);
|
||||
mReactApplicationContext.internal_registerInteropModule(
|
||||
RCTEventEmitter.class, interopEventEmitter);
|
||||
}
|
||||
}
|
||||
|
||||
// This is called on the JS thread (see CatalystInstanceImpl).
|
||||
@@ -1171,6 +1178,20 @@ public class FabricUIManager implements UIManager, LifecycleEventListener {
|
||||
}
|
||||
|
||||
private class MountItemDispatchListener implements MountItemDispatcher.ItemDispatchListener {
|
||||
@Override
|
||||
public void willMountItems() {
|
||||
for (UIManagerListener listener : mListeners) {
|
||||
listener.willMountItems(FabricUIManager.this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void didMountItems() {
|
||||
for (UIManagerListener listener : mListeners) {
|
||||
listener.didMountItems(FabricUIManager.this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void didDispatchMountItems() {
|
||||
for (UIManagerListener listener : mListeners) {
|
||||
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package com.facebook.react.fabric.interop;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import com.facebook.react.bridge.WritableMap;
|
||||
import com.facebook.react.common.annotations.VisibleForTesting;
|
||||
import com.facebook.react.uimanager.events.Event;
|
||||
|
||||
/**
|
||||
* An {@link Event} class used by the {@link InteropEventEmitter}. This class is just holding the
|
||||
* event name and the data which is received by the `receiveEvent` method and will be passed over
|
||||
* the the {@link com.facebook.react.uimanager.events.EventDispatcher}
|
||||
*/
|
||||
class InteropEvent extends Event<InteropEvent> {
|
||||
|
||||
private final String mName;
|
||||
private final WritableMap mEventData;
|
||||
|
||||
InteropEvent(String name, @Nullable WritableMap eventData, int surfaceId, int viewTag) {
|
||||
super(surfaceId, viewTag);
|
||||
mName = name;
|
||||
mEventData = eventData;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getEventName() {
|
||||
return mName;
|
||||
}
|
||||
|
||||
@Override
|
||||
@VisibleForTesting
|
||||
public WritableMap getEventData() {
|
||||
return mEventData;
|
||||
}
|
||||
}
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package com.facebook.react.fabric.interop;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import com.facebook.react.bridge.ReactContext;
|
||||
import com.facebook.react.bridge.WritableArray;
|
||||
import com.facebook.react.bridge.WritableMap;
|
||||
import com.facebook.react.common.annotations.VisibleForTesting;
|
||||
import com.facebook.react.uimanager.UIManagerHelper;
|
||||
import com.facebook.react.uimanager.events.EventDispatcher;
|
||||
import com.facebook.react.uimanager.events.RCTEventEmitter;
|
||||
|
||||
/**
|
||||
* A reimplementation of {@link RCTEventEmitter} which is using a {@link EventDispatcher} under the
|
||||
* hood.
|
||||
*
|
||||
* <p>On Fabric, you're supposed to use {@link EventDispatcher} to dispatch events. However, we
|
||||
* provide an interop layer for non-Fabric migrated components.
|
||||
*
|
||||
* <p>This instance will be returned if the user is invoking `context.getJsModule(RCTEventEmitter)
|
||||
* and is providing support for the `receiveEvent` method, so that non-Fabric ViewManagers can
|
||||
* continue to deliver events also when Fabric is turned on.
|
||||
*/
|
||||
public class InteropEventEmitter implements RCTEventEmitter {
|
||||
|
||||
private final ReactContext mReactContext;
|
||||
|
||||
private @Nullable EventDispatcher mEventDispatcherOverride;
|
||||
|
||||
public InteropEventEmitter(ReactContext reactContext) {
|
||||
mReactContext = reactContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receiveEvent(int targetReactTag, String eventName, @Nullable WritableMap eventData) {
|
||||
EventDispatcher dispatcher;
|
||||
if (mEventDispatcherOverride != null) {
|
||||
dispatcher = mEventDispatcherOverride;
|
||||
} else {
|
||||
dispatcher = UIManagerHelper.getEventDispatcherForReactTag(mReactContext, targetReactTag);
|
||||
}
|
||||
int surfaceId = UIManagerHelper.getSurfaceId(mReactContext);
|
||||
if (dispatcher != null) {
|
||||
dispatcher.dispatchEvent(new InteropEvent(eventName, eventData, surfaceId, targetReactTag));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receiveTouches(
|
||||
String eventName, WritableArray touches, WritableArray changedIndices) {
|
||||
throw new UnsupportedOperationException(
|
||||
"EventEmitter#receiveTouches is not supported by the Fabric Interop Layer");
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void overrideEventDispatcher(EventDispatcher eventDispatcherOverride) {
|
||||
mEventDispatcherOverride = eventDispatcherOverride;
|
||||
}
|
||||
}
|
||||
+9
@@ -193,6 +193,8 @@ public class MountItemDispatcher {
|
||||
return false;
|
||||
}
|
||||
|
||||
mItemDispatchListener.willMountItems();
|
||||
|
||||
// As an optimization, execute all ViewCommands first
|
||||
// This should be:
|
||||
// 1) Performant: ViewCommands are often a replacement for SetNativeProps, which we've always
|
||||
@@ -299,6 +301,9 @@ public class MountItemDispatcher {
|
||||
}
|
||||
mBatchedExecutionTime += SystemClock.uptimeMillis() - batchedExecutionStartTime;
|
||||
}
|
||||
|
||||
mItemDispatchListener.didMountItems();
|
||||
|
||||
Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
|
||||
|
||||
return true;
|
||||
@@ -415,6 +420,10 @@ public class MountItemDispatcher {
|
||||
}
|
||||
|
||||
public interface ItemDispatchListener {
|
||||
void willMountItems();
|
||||
|
||||
void didMountItems();
|
||||
|
||||
void didDispatchMountItems();
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -15,8 +15,8 @@ import java.util.Map;
|
||||
|
||||
public class ReactNativeVersion {
|
||||
public static final Map<String, Object> VERSION = MapBuilder.<String, Object>of(
|
||||
"major", 1000,
|
||||
"minor", 0,
|
||||
"major", 0,
|
||||
"minor", 72,
|
||||
"patch", 0,
|
||||
"prerelease", null);
|
||||
}
|
||||
|
||||
+1
@@ -50,6 +50,7 @@ public class ThemedReactContext extends ReactContext {
|
||||
if (reactApplicationContext.hasCatalystInstance()) {
|
||||
initializeWithInstance(reactApplicationContext.getCatalystInstance());
|
||||
}
|
||||
initializeInteropModules(reactApplicationContext);
|
||||
mReactApplicationContext = reactApplicationContext;
|
||||
mModuleName = moduleName;
|
||||
mSurfaceId = surfaceId;
|
||||
|
||||
+32
@@ -10,11 +10,15 @@ package com.facebook.react.uimanager;
|
||||
import static com.facebook.systrace.Systrace.TRACE_TAG_REACT_JAVA_BRIDGE;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
import com.facebook.react.common.MapBuilder;
|
||||
import com.facebook.react.config.ReactFeatureFlags;
|
||||
import com.facebook.systrace.SystraceMessage;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Helps generate constants map for {@link UIManagerModule} by collecting and merging constants from
|
||||
@@ -113,6 +117,13 @@ import java.util.Map;
|
||||
|
||||
Map viewManagerBubblingEvents = viewManager.getExportedCustomBubblingEventTypeConstants();
|
||||
if (viewManagerBubblingEvents != null) {
|
||||
|
||||
if (ReactFeatureFlags.enableFabricRenderer && ReactFeatureFlags.unstable_useFabricInterop) {
|
||||
// For Fabric, events needs to be fired with a "top" prefix.
|
||||
// For the sake of Fabric Interop, here we normalize events adding "top" in their
|
||||
// name if the user hasn't provided it.
|
||||
normalizeEventTypes(viewManagerBubblingEvents);
|
||||
}
|
||||
recursiveMerge(cumulativeBubblingEventTypes, viewManagerBubblingEvents);
|
||||
recursiveMerge(viewManagerBubblingEvents, defaultBubblingEvents);
|
||||
viewManagerConstants.put(BUBBLING_EVENTS_KEY, viewManagerBubblingEvents);
|
||||
@@ -145,6 +156,27 @@ import java.util.Map;
|
||||
return viewManagerConstants;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
/* package */ static void normalizeEventTypes(Map events) {
|
||||
if (events == null) {
|
||||
return;
|
||||
}
|
||||
Set<String> keysToNormalize = new HashSet<>();
|
||||
for (Object key : events.keySet()) {
|
||||
if (key instanceof String) {
|
||||
String keyString = (String) key;
|
||||
if (!keyString.startsWith("top")) {
|
||||
keysToNormalize.add(keyString);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (String oldKey : keysToNormalize) {
|
||||
Object value = events.get(oldKey);
|
||||
String newKey = "top" + oldKey.substring(0, 1).toUpperCase() + oldKey.substring(1);
|
||||
events.put(newKey, value);
|
||||
}
|
||||
}
|
||||
|
||||
/** Merges {@param source} map into {@param dest} map recursively */
|
||||
private static void recursiveMerge(@Nullable Map dest, @Nullable Map source) {
|
||||
if (dest == null || source == null || source.isEmpty()) {
|
||||
|
||||
+19
@@ -18,6 +18,7 @@ import com.facebook.react.bridge.UIManager;
|
||||
import com.facebook.react.bridge.UIManagerListener;
|
||||
import com.facebook.react.bridge.UiThreadUtil;
|
||||
import com.facebook.react.uimanager.UIManagerHelper;
|
||||
import com.facebook.react.uimanager.common.UIManagerType;
|
||||
import com.facebook.react.uimanager.common.ViewUtil;
|
||||
import com.facebook.react.views.scroll.ReactScrollViewHelper.HasSmoothScroll;
|
||||
import com.facebook.react.views.view.ReactViewGroup;
|
||||
@@ -89,6 +90,14 @@ public class MaintainVisibleScrollPositionHelper<ScrollViewT extends ViewGroup &
|
||||
* been updated.
|
||||
*/
|
||||
public void updateScrollPosition() {
|
||||
// On Fabric this will be called internally in `didMountItems`.
|
||||
if (ViewUtil.getUIManagerType(mScrollView.getId()) == UIManagerType.FABRIC) {
|
||||
return;
|
||||
}
|
||||
updateScrollPositionInternal();
|
||||
}
|
||||
|
||||
private void updateScrollPositionInternal() {
|
||||
if (mConfig == null || mFirstVisibleView == null || mPrevFirstVisibleFrame == null) {
|
||||
return;
|
||||
}
|
||||
@@ -169,6 +178,16 @@ public class MaintainVisibleScrollPositionHelper<ScrollViewT extends ViewGroup &
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void willMountItems(UIManager uiManager) {
|
||||
computeTargetView();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void didMountItems(UIManager uiManager) {
|
||||
updateScrollPositionInternal();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void didDispatchMountItems(UIManager uiManager) {
|
||||
// noop
|
||||
|
||||
+4
@@ -37,6 +37,10 @@ public class CustomLetterSpacingSpan extends MetricAffectingSpan implements Reac
|
||||
apply(paint);
|
||||
}
|
||||
|
||||
public float getSpacing() {
|
||||
return mLetterSpacing;
|
||||
}
|
||||
|
||||
private void apply(TextPaint paint) {
|
||||
if (!Float.isNaN(mLetterSpacing)) {
|
||||
paint.setLetterSpacing(mLetterSpacing);
|
||||
|
||||
+4
@@ -71,6 +71,10 @@ public class CustomStyleSpan extends MetricAffectingSpan implements ReactSpan {
|
||||
return mFontFamily;
|
||||
}
|
||||
|
||||
public @Nullable String getFontFeatureSettings() {
|
||||
return mFeatureSettings;
|
||||
}
|
||||
|
||||
private static void apply(
|
||||
Paint paint,
|
||||
int style,
|
||||
|
||||
+5
-30
@@ -69,31 +69,6 @@ public abstract class ReactBaseTextShadowNode extends LayoutShadowNode {
|
||||
|
||||
protected @Nullable ReactTextViewManagerCallback mReactTextViewManagerCallback;
|
||||
|
||||
private static class SetSpanOperation {
|
||||
protected int start, end;
|
||||
protected ReactSpan what;
|
||||
|
||||
SetSpanOperation(int start, int end, ReactSpan what) {
|
||||
this.start = start;
|
||||
this.end = end;
|
||||
this.what = what;
|
||||
}
|
||||
|
||||
public void execute(SpannableStringBuilder sb, int priority) {
|
||||
// All spans will automatically extend to the right of the text, but not the left - except
|
||||
// for spans that start at the beginning of the text.
|
||||
int spanFlags = Spannable.SPAN_EXCLUSIVE_INCLUSIVE;
|
||||
if (start == 0) {
|
||||
spanFlags = Spannable.SPAN_INCLUSIVE_INCLUSIVE;
|
||||
}
|
||||
|
||||
spanFlags &= ~Spannable.SPAN_PRIORITY;
|
||||
spanFlags |= (priority << Spannable.SPAN_PRIORITY_SHIFT) & Spannable.SPAN_PRIORITY;
|
||||
|
||||
sb.setSpan(what, start, end, spanFlags);
|
||||
}
|
||||
}
|
||||
|
||||
private static void buildSpannedFromShadowNode(
|
||||
ReactBaseTextShadowNode textShadowNode,
|
||||
SpannableStringBuilder sb,
|
||||
@@ -276,8 +251,9 @@ public abstract class ReactBaseTextShadowNode extends LayoutShadowNode {
|
||||
|
||||
// While setting the Spans on the final text, we also check whether any of them are inline views
|
||||
// or images.
|
||||
int priority = 0;
|
||||
for (SetSpanOperation op : ops) {
|
||||
for (int priorityIndex = 0; priorityIndex < ops.size(); priorityIndex++) {
|
||||
final SetSpanOperation op = ops.get(ops.size() - priorityIndex - 1);
|
||||
|
||||
boolean isInlineImage = op.what instanceof TextInlineImageSpan;
|
||||
if (isInlineImage || op.what instanceof TextInlineViewPlaceholderSpan) {
|
||||
int height;
|
||||
@@ -304,9 +280,8 @@ public abstract class ReactBaseTextShadowNode extends LayoutShadowNode {
|
||||
}
|
||||
|
||||
// Actual order of calling {@code execute} does NOT matter,
|
||||
// but the {@code priority} DOES matter.
|
||||
op.execute(sb, priority);
|
||||
priority++;
|
||||
// but the {@code priorityIndex} DOES matter.
|
||||
op.execute(sb, priorityIndex);
|
||||
}
|
||||
|
||||
textShadowNode.mTextAttributes.setHeightOfTallestInlineViewOrImage(
|
||||
|
||||
+4
-52
@@ -27,12 +27,8 @@ public class ReactTextUpdate {
|
||||
private final float mPaddingBottom;
|
||||
private final int mTextAlign;
|
||||
private final int mTextBreakStrategy;
|
||||
private final int mSelectionStart;
|
||||
private final int mSelectionEnd;
|
||||
private final int mJustificationMode;
|
||||
|
||||
public boolean mContainsMultipleFragments;
|
||||
|
||||
/**
|
||||
* @deprecated Use a non-deprecated constructor for ReactTextUpdate instead. This one remains
|
||||
* because it's being used by a unit test that isn't currently open source.
|
||||
@@ -57,35 +53,7 @@ public class ReactTextUpdate {
|
||||
paddingBottom,
|
||||
textAlign,
|
||||
Layout.BREAK_STRATEGY_HIGH_QUALITY,
|
||||
Layout.JUSTIFICATION_MODE_NONE,
|
||||
-1,
|
||||
-1);
|
||||
}
|
||||
|
||||
public ReactTextUpdate(
|
||||
Spannable text,
|
||||
int jsEventCounter,
|
||||
boolean containsImages,
|
||||
float paddingStart,
|
||||
float paddingTop,
|
||||
float paddingEnd,
|
||||
float paddingBottom,
|
||||
int textAlign,
|
||||
int textBreakStrategy,
|
||||
int justificationMode) {
|
||||
this(
|
||||
text,
|
||||
jsEventCounter,
|
||||
containsImages,
|
||||
paddingStart,
|
||||
paddingTop,
|
||||
paddingEnd,
|
||||
paddingBottom,
|
||||
textAlign,
|
||||
textBreakStrategy,
|
||||
justificationMode,
|
||||
-1,
|
||||
-1);
|
||||
Layout.JUSTIFICATION_MODE_NONE);
|
||||
}
|
||||
|
||||
public ReactTextUpdate(
|
||||
@@ -105,9 +73,7 @@ public class ReactTextUpdate {
|
||||
UNSET,
|
||||
textAlign,
|
||||
textBreakStrategy,
|
||||
justificationMode,
|
||||
-1,
|
||||
-1);
|
||||
justificationMode);
|
||||
}
|
||||
|
||||
public ReactTextUpdate(
|
||||
@@ -120,9 +86,7 @@ public class ReactTextUpdate {
|
||||
float paddingBottom,
|
||||
int textAlign,
|
||||
int textBreakStrategy,
|
||||
int justificationMode,
|
||||
int selectionStart,
|
||||
int selectionEnd) {
|
||||
int justificationMode) {
|
||||
mText = text;
|
||||
mJsEventCounter = jsEventCounter;
|
||||
mContainsImages = containsImages;
|
||||
@@ -132,8 +96,6 @@ public class ReactTextUpdate {
|
||||
mPaddingBottom = paddingBottom;
|
||||
mTextAlign = textAlign;
|
||||
mTextBreakStrategy = textBreakStrategy;
|
||||
mSelectionStart = selectionStart;
|
||||
mSelectionEnd = selectionEnd;
|
||||
mJustificationMode = justificationMode;
|
||||
}
|
||||
|
||||
@@ -142,13 +104,11 @@ public class ReactTextUpdate {
|
||||
int jsEventCounter,
|
||||
int textAlign,
|
||||
int textBreakStrategy,
|
||||
int justificationMode,
|
||||
boolean containsMultipleFragments) {
|
||||
int justificationMode) {
|
||||
|
||||
ReactTextUpdate reactTextUpdate =
|
||||
new ReactTextUpdate(
|
||||
text, jsEventCounter, false, textAlign, textBreakStrategy, justificationMode);
|
||||
reactTextUpdate.mContainsMultipleFragments = containsMultipleFragments;
|
||||
return reactTextUpdate;
|
||||
}
|
||||
|
||||
@@ -191,12 +151,4 @@ public class ReactTextUpdate {
|
||||
public int getJustificationMode() {
|
||||
return mJustificationMode;
|
||||
}
|
||||
|
||||
public int getSelectionStart() {
|
||||
return mSelectionStart;
|
||||
}
|
||||
|
||||
public int getSelectionEnd() {
|
||||
return mSelectionEnd;
|
||||
}
|
||||
}
|
||||
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
package com.facebook.react.views.text;
|
||||
|
||||
import android.text.Spannable;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.text.Spanned;
|
||||
import com.facebook.common.logging.FLog;
|
||||
|
||||
class SetSpanOperation {
|
||||
private static final String TAG = "SetSpanOperation";
|
||||
static final int SPAN_MAX_PRIORITY = Spanned.SPAN_PRIORITY >> Spanned.SPAN_PRIORITY_SHIFT;
|
||||
|
||||
protected int start, end;
|
||||
protected ReactSpan what;
|
||||
|
||||
SetSpanOperation(int start, int end, ReactSpan what) {
|
||||
this.start = start;
|
||||
this.end = end;
|
||||
this.what = what;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param sb builder
|
||||
* @param priorityIndex index of this operation in the topological sorting which puts operations
|
||||
* with higher priority before operations with lower priority.
|
||||
*/
|
||||
public void execute(SpannableStringBuilder sb, int priorityIndex) {
|
||||
assert priorityIndex >= 0;
|
||||
|
||||
// All spans will automatically extend to the right of the text, but not the left - except
|
||||
// for spans that start at the beginning of the text.
|
||||
int spanFlags = Spannable.SPAN_EXCLUSIVE_INCLUSIVE;
|
||||
if (start == 0) {
|
||||
spanFlags = Spannable.SPAN_INCLUSIVE_INCLUSIVE;
|
||||
}
|
||||
|
||||
// Calculate priority, assigning the highest values to operations with the highest priority
|
||||
final int priority = SPAN_MAX_PRIORITY - priorityIndex;
|
||||
|
||||
if (priority < 0) {
|
||||
FLog.w(TAG, "Text tree size exceeded the limit, styling may become unpredictable");
|
||||
}
|
||||
|
||||
// If the computed priority doesn't fit in the flags, clamp it. The effect might not be correct
|
||||
// in 100% of cases, but doing nothing (as we did in the past) leads to totally random results.
|
||||
final int effectivePriority = Math.max(priority, 0);
|
||||
|
||||
spanFlags &= ~Spannable.SPAN_PRIORITY;
|
||||
spanFlags |= (effectivePriority << Spannable.SPAN_PRIORITY_SHIFT) & Spannable.SPAN_PRIORITY;
|
||||
|
||||
sb.setSpan(what, start, end, spanFlags);
|
||||
}
|
||||
}
|
||||
+5
-31
@@ -213,12 +213,12 @@ public class TextLayoutManager {
|
||||
|
||||
// TODO T31905686: add support for inline Images
|
||||
// While setting the Spans on the final text, we also check whether any of them are images.
|
||||
int priority = 0;
|
||||
for (SetSpanOperation op : ops) {
|
||||
for (int priorityIndex = 0; priorityIndex < ops.size(); ++priorityIndex) {
|
||||
final SetSpanOperation op = ops.get(ops.size() - priorityIndex - 1);
|
||||
|
||||
// Actual order of calling {@code execute} does NOT matter,
|
||||
// but the {@code priority} DOES matter.
|
||||
op.execute(sb, priority);
|
||||
priority++;
|
||||
// but the {@code priorityIndex} DOES matter.
|
||||
op.execute(sb, priorityIndex);
|
||||
}
|
||||
|
||||
if (reactTextViewManagerCallback != null) {
|
||||
@@ -551,30 +551,4 @@ public class TextLayoutManager {
|
||||
hyphenationFrequency);
|
||||
return FontMetricsUtil.getFontMetrics(text, layout, sTextPaintInstance, context);
|
||||
}
|
||||
|
||||
// TODO T31905686: This class should be private
|
||||
public static class SetSpanOperation {
|
||||
protected int start, end;
|
||||
protected ReactSpan what;
|
||||
|
||||
public SetSpanOperation(int start, int end, ReactSpan what) {
|
||||
this.start = start;
|
||||
this.end = end;
|
||||
this.what = what;
|
||||
}
|
||||
|
||||
public void execute(Spannable sb, int priority) {
|
||||
// All spans will automatically extend to the right of the text, but not the left - except
|
||||
// for spans that start at the beginning of the text.
|
||||
int spanFlags = Spannable.SPAN_EXCLUSIVE_INCLUSIVE;
|
||||
if (start == 0) {
|
||||
spanFlags = Spannable.SPAN_INCLUSIVE_INCLUSIVE;
|
||||
}
|
||||
|
||||
spanFlags &= ~Spannable.SPAN_PRIORITY;
|
||||
spanFlags |= (priority << Spannable.SPAN_PRIORITY_SHIFT) & Spannable.SPAN_PRIORITY;
|
||||
|
||||
sb.setSpan(what, start, end, spanFlags);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+5
-31
@@ -227,12 +227,12 @@ public class TextLayoutManagerMapBuffer {
|
||||
|
||||
// TODO T31905686: add support for inline Images
|
||||
// While setting the Spans on the final text, we also check whether any of them are images.
|
||||
int priority = 0;
|
||||
for (SetSpanOperation op : ops) {
|
||||
for (int priorityIndex = 0; priorityIndex < ops.size(); ++priorityIndex) {
|
||||
final SetSpanOperation op = ops.get(ops.size() - priorityIndex - 1);
|
||||
|
||||
// Actual order of calling {@code execute} does NOT matter,
|
||||
// but the {@code priority} DOES matter.
|
||||
op.execute(sb, priority);
|
||||
priority++;
|
||||
// but the {@code priorityIndex} DOES matter.
|
||||
op.execute(sb, priorityIndex);
|
||||
}
|
||||
|
||||
if (reactTextViewManagerCallback != null) {
|
||||
@@ -570,30 +570,4 @@ public class TextLayoutManagerMapBuffer {
|
||||
hyphenationFrequency);
|
||||
return FontMetricsUtil.getFontMetrics(text, layout, sTextPaintInstance, context);
|
||||
}
|
||||
|
||||
// TODO T31905686: This class should be private
|
||||
public static class SetSpanOperation {
|
||||
protected int start, end;
|
||||
protected ReactSpan what;
|
||||
|
||||
public SetSpanOperation(int start, int end, ReactSpan what) {
|
||||
this.start = start;
|
||||
this.end = end;
|
||||
this.what = what;
|
||||
}
|
||||
|
||||
public void execute(Spannable sb, int priority) {
|
||||
// All spans will automatically extend to the right of the text, but not the left - except
|
||||
// for spans that start at the beginning of the text.
|
||||
int spanFlags = Spannable.SPAN_EXCLUSIVE_INCLUSIVE;
|
||||
if (start == 0) {
|
||||
spanFlags = Spannable.SPAN_INCLUSIVE_INCLUSIVE;
|
||||
}
|
||||
|
||||
spanFlags &= ~Spannable.SPAN_PRIORITY;
|
||||
spanFlags |= (priority << Spannable.SPAN_PRIORITY_SHIFT) & Spannable.SPAN_PRIORITY;
|
||||
|
||||
sb.setSpan(what, start, end, spanFlags);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+200
-136
@@ -11,6 +11,8 @@ import static com.facebook.react.uimanager.UIManagerHelper.getReactContext;
|
||||
import static com.facebook.react.views.text.TextAttributeProps.UNSET;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.Typeface;
|
||||
import android.graphics.drawable.Drawable;
|
||||
@@ -50,15 +52,19 @@ import com.facebook.react.views.text.CustomLetterSpacingSpan;
|
||||
import com.facebook.react.views.text.CustomLineHeightSpan;
|
||||
import com.facebook.react.views.text.CustomStyleSpan;
|
||||
import com.facebook.react.views.text.ReactAbsoluteSizeSpan;
|
||||
import com.facebook.react.views.text.ReactBackgroundColorSpan;
|
||||
import com.facebook.react.views.text.ReactForegroundColorSpan;
|
||||
import com.facebook.react.views.text.ReactSpan;
|
||||
import com.facebook.react.views.text.ReactStrikethroughSpan;
|
||||
import com.facebook.react.views.text.ReactTextUpdate;
|
||||
import com.facebook.react.views.text.ReactTypefaceUtils;
|
||||
import com.facebook.react.views.text.ReactUnderlineSpan;
|
||||
import com.facebook.react.views.text.TextAttributes;
|
||||
import com.facebook.react.views.text.TextInlineImageSpan;
|
||||
import com.facebook.react.views.text.TextLayoutManager;
|
||||
import com.facebook.react.views.view.ReactViewBackgroundManager;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* A wrapper around the EditText that lets us better control what happens when an EditText gets
|
||||
@@ -82,7 +88,6 @@ public class ReactEditText extends AppCompatEditText
|
||||
// *TextChanged events should be triggered. This is less expensive than removing the text
|
||||
// listeners and adding them back again after the text change is completed.
|
||||
protected boolean mIsSettingTextFromJS;
|
||||
protected boolean mIsSettingTextFromCacheUpdate = false;
|
||||
private int mDefaultGravityHorizontal;
|
||||
private int mDefaultGravityVertical;
|
||||
|
||||
@@ -111,6 +116,7 @@ public class ReactEditText extends AppCompatEditText
|
||||
private int mFontStyle = UNSET;
|
||||
private boolean mAutoFocus = false;
|
||||
private boolean mDidAttachToWindow = false;
|
||||
private @Nullable String mPlaceholder = null;
|
||||
|
||||
private ReactViewBackgroundManager mReactBackgroundManager;
|
||||
|
||||
@@ -362,7 +368,7 @@ public class ReactEditText extends AppCompatEditText
|
||||
}
|
||||
|
||||
super.onSelectionChanged(selStart, selEnd);
|
||||
if (!mIsSettingTextFromCacheUpdate && mSelectionWatcher != null && hasFocus()) {
|
||||
if (mSelectionWatcher != null && hasFocus()) {
|
||||
mSelectionWatcher.onSelectionChanged(selStart, selEnd);
|
||||
}
|
||||
}
|
||||
@@ -491,6 +497,13 @@ public class ReactEditText extends AppCompatEditText
|
||||
setKeyListener(mKeyListener);
|
||||
}
|
||||
|
||||
public void setPlaceholder(@Nullable String placeholder) {
|
||||
if (!Objects.equals(placeholder, mPlaceholder)) {
|
||||
mPlaceholder = placeholder;
|
||||
setHint(placeholder);
|
||||
}
|
||||
}
|
||||
|
||||
public void setFontFamily(String fontFamily) {
|
||||
mFontFamily = fontFamily;
|
||||
mTypefaceDirty = true;
|
||||
@@ -512,6 +525,14 @@ public class ReactEditText extends AppCompatEditText
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFontFeatureSettings(String fontFeatureSettings) {
|
||||
if (!Objects.equals(fontFeatureSettings, getFontFeatureSettings())) {
|
||||
super.setFontFeatureSettings(fontFeatureSettings);
|
||||
mTypefaceDirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void maybeUpdateTypeface() {
|
||||
if (!mTypefaceDirty) {
|
||||
return;
|
||||
@@ -523,6 +544,17 @@ public class ReactEditText extends AppCompatEditText
|
||||
ReactTypefaceUtils.applyStyles(
|
||||
getTypeface(), mFontStyle, mFontWeight, mFontFamily, getContext().getAssets());
|
||||
setTypeface(newTypeface);
|
||||
|
||||
// Match behavior of CustomStyleSpan and enable SUBPIXEL_TEXT_FLAG when setting anything
|
||||
// nonstandard
|
||||
if (mFontStyle != UNSET
|
||||
|| mFontWeight != UNSET
|
||||
|| mFontFamily != null
|
||||
|| getFontFeatureSettings() != null) {
|
||||
setPaintFlags(getPaintFlags() | Paint.SUBPIXEL_TEXT_FLAG);
|
||||
} else {
|
||||
setPaintFlags(getPaintFlags() & (~Paint.SUBPIXEL_TEXT_FLAG));
|
||||
}
|
||||
}
|
||||
|
||||
// VisibleForTesting from {@link TextInputEventsTestCase}.
|
||||
@@ -584,10 +616,8 @@ public class ReactEditText extends AppCompatEditText
|
||||
SpannableStringBuilder spannableStringBuilder =
|
||||
new SpannableStringBuilder(reactTextUpdate.getText());
|
||||
|
||||
manageSpans(spannableStringBuilder, reactTextUpdate.mContainsMultipleFragments);
|
||||
|
||||
// Mitigation for https://github.com/facebook/react-native/issues/35936 (S318090)
|
||||
stripAttributeEquivalentSpans(spannableStringBuilder);
|
||||
manageSpans(spannableStringBuilder);
|
||||
stripStyleEquivalentSpans(spannableStringBuilder);
|
||||
|
||||
mContainsImages = reactTextUpdate.containsImages();
|
||||
|
||||
@@ -615,7 +645,7 @@ public class ReactEditText extends AppCompatEditText
|
||||
}
|
||||
|
||||
// Update cached spans (in Fabric only).
|
||||
updateCachedSpannable(false);
|
||||
updateCachedSpannable();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -624,8 +654,7 @@ public class ReactEditText extends AppCompatEditText
|
||||
* will adapt to the new text, hence why {@link SpannableStringBuilder#replace} never removes
|
||||
* them.
|
||||
*/
|
||||
private void manageSpans(
|
||||
SpannableStringBuilder spannableStringBuilder, boolean skipAddSpansForMeasurements) {
|
||||
private void manageSpans(SpannableStringBuilder spannableStringBuilder) {
|
||||
Object[] spans = getText().getSpans(0, length(), Object.class);
|
||||
for (int spanIdx = 0; spanIdx < spans.length; spanIdx++) {
|
||||
Object span = spans[spanIdx];
|
||||
@@ -653,58 +682,170 @@ public class ReactEditText extends AppCompatEditText
|
||||
spannableStringBuilder.setSpan(span, spanStart, spanEnd, spanFlags);
|
||||
}
|
||||
}
|
||||
|
||||
// In Fabric only, apply necessary styles to entire span
|
||||
// If the Spannable was constructed from multiple fragments, we don't apply any spans that could
|
||||
// impact the whole Spannable, because that would override "local" styles per-fragment
|
||||
if (!skipAddSpansForMeasurements) {
|
||||
addSpansForMeasurement(getText());
|
||||
}
|
||||
}
|
||||
|
||||
private void stripAttributeEquivalentSpans(SpannableStringBuilder sb) {
|
||||
// We have already set a font size on the EditText itself. We can safely remove sizing spans
|
||||
// which are the same as the set font size, and not otherwise overlapped.
|
||||
final int effectiveFontSize = mTextAttributes.getEffectiveFontSize();
|
||||
ReactAbsoluteSizeSpan[] spans = sb.getSpans(0, sb.length(), ReactAbsoluteSizeSpan.class);
|
||||
// TODO: Replace with Predicate<T> and lambdas once Java 8 builds in OSS
|
||||
interface SpanPredicate<T> {
|
||||
boolean test(T span);
|
||||
}
|
||||
|
||||
outerLoop:
|
||||
for (ReactAbsoluteSizeSpan span : spans) {
|
||||
ReactAbsoluteSizeSpan[] overlappingSpans =
|
||||
sb.getSpans(sb.getSpanStart(span), sb.getSpanEnd(span), ReactAbsoluteSizeSpan.class);
|
||||
/**
|
||||
* Remove spans from the SpannableStringBuilder which can be represented by TextAppearance
|
||||
* attributes on the underlying EditText. This works around instability on Samsung devices with
|
||||
* the presence of spans https://github.com/facebook/react-native/issues/35936 (S318090)
|
||||
*/
|
||||
private void stripStyleEquivalentSpans(SpannableStringBuilder sb) {
|
||||
stripSpansOfKind(
|
||||
sb,
|
||||
ReactAbsoluteSizeSpan.class,
|
||||
new SpanPredicate<ReactAbsoluteSizeSpan>() {
|
||||
@Override
|
||||
public boolean test(ReactAbsoluteSizeSpan span) {
|
||||
return span.getSize() == mTextAttributes.getEffectiveFontSize();
|
||||
}
|
||||
});
|
||||
|
||||
for (ReactAbsoluteSizeSpan overlappingSpan : overlappingSpans) {
|
||||
if (span.getSize() != effectiveFontSize) {
|
||||
continue outerLoop;
|
||||
}
|
||||
stripSpansOfKind(
|
||||
sb,
|
||||
ReactBackgroundColorSpan.class,
|
||||
new SpanPredicate<ReactBackgroundColorSpan>() {
|
||||
@Override
|
||||
public boolean test(ReactBackgroundColorSpan span) {
|
||||
return span.getBackgroundColor() == mReactBackgroundManager.getBackgroundColor();
|
||||
}
|
||||
});
|
||||
|
||||
stripSpansOfKind(
|
||||
sb,
|
||||
ReactForegroundColorSpan.class,
|
||||
new SpanPredicate<ReactForegroundColorSpan>() {
|
||||
@Override
|
||||
public boolean test(ReactForegroundColorSpan span) {
|
||||
return span.getForegroundColor() == getCurrentTextColor();
|
||||
}
|
||||
});
|
||||
|
||||
stripSpansOfKind(
|
||||
sb,
|
||||
ReactStrikethroughSpan.class,
|
||||
new SpanPredicate<ReactStrikethroughSpan>() {
|
||||
@Override
|
||||
public boolean test(ReactStrikethroughSpan span) {
|
||||
return (getPaintFlags() & Paint.STRIKE_THRU_TEXT_FLAG) != 0;
|
||||
}
|
||||
});
|
||||
|
||||
stripSpansOfKind(
|
||||
sb,
|
||||
ReactUnderlineSpan.class,
|
||||
new SpanPredicate<ReactUnderlineSpan>() {
|
||||
@Override
|
||||
public boolean test(ReactUnderlineSpan span) {
|
||||
return (getPaintFlags() & Paint.UNDERLINE_TEXT_FLAG) != 0;
|
||||
}
|
||||
});
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
stripSpansOfKind(
|
||||
sb,
|
||||
CustomLetterSpacingSpan.class,
|
||||
new SpanPredicate<CustomLetterSpacingSpan>() {
|
||||
@Override
|
||||
public boolean test(CustomLetterSpacingSpan span) {
|
||||
return span.getSpacing() == mTextAttributes.getEffectiveLetterSpacing();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
stripSpansOfKind(
|
||||
sb,
|
||||
CustomStyleSpan.class,
|
||||
new SpanPredicate<CustomStyleSpan>() {
|
||||
@Override
|
||||
public boolean test(CustomStyleSpan span) {
|
||||
return span.getStyle() == mFontStyle
|
||||
&& Objects.equals(span.getFontFamily(), mFontFamily)
|
||||
&& span.getWeight() == mFontWeight
|
||||
&& Objects.equals(span.getFontFeatureSettings(), getFontFeatureSettings());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private <T> void stripSpansOfKind(
|
||||
SpannableStringBuilder sb, Class<T> clazz, SpanPredicate<T> shouldStrip) {
|
||||
T[] spans = sb.getSpans(0, sb.length(), clazz);
|
||||
|
||||
for (T span : spans) {
|
||||
if (shouldStrip.test(span)) {
|
||||
sb.removeSpan(span);
|
||||
}
|
||||
|
||||
sb.removeSpan(span);
|
||||
}
|
||||
}
|
||||
|
||||
private void unstripAttributeEquivalentSpans(
|
||||
SpannableStringBuilder workingText, Spannable originalText) {
|
||||
// We must add spans back for Fabric to be able to measure, at lower precedence than any
|
||||
// existing spans. Remove all spans, add the attributes, then re-add the spans over
|
||||
workingText.append(originalText);
|
||||
/**
|
||||
* Copy styles represented as attributes to the underlying span, for later measurement or other
|
||||
* usage outside the ReactEditText.
|
||||
*/
|
||||
private void addSpansFromStyleAttributes(SpannableStringBuilder workingText) {
|
||||
int spanFlags = Spannable.SPAN_INCLUSIVE_INCLUSIVE;
|
||||
|
||||
for (Object span : workingText.getSpans(0, workingText.length(), Object.class)) {
|
||||
workingText.removeSpan(span);
|
||||
}
|
||||
// Set all bits for SPAN_PRIORITY so that this span has the highest possible priority
|
||||
// (least precedence). This ensures the span is behind any overlapping spans.
|
||||
spanFlags |= Spannable.SPAN_PRIORITY;
|
||||
|
||||
workingText.setSpan(
|
||||
new ReactAbsoluteSizeSpan(mTextAttributes.getEffectiveFontSize()),
|
||||
0,
|
||||
workingText.length(),
|
||||
Spanned.SPAN_INCLUSIVE_INCLUSIVE);
|
||||
spanFlags);
|
||||
|
||||
for (Object span : originalText.getSpans(0, originalText.length(), Object.class)) {
|
||||
workingText.setSpan(
|
||||
new ReactForegroundColorSpan(getCurrentTextColor()), 0, workingText.length(), spanFlags);
|
||||
|
||||
int backgroundColor = mReactBackgroundManager.getBackgroundColor();
|
||||
if (backgroundColor != Color.TRANSPARENT) {
|
||||
workingText.setSpan(
|
||||
span,
|
||||
originalText.getSpanStart(span),
|
||||
originalText.getSpanEnd(span),
|
||||
originalText.getSpanFlags(span));
|
||||
new ReactBackgroundColorSpan(backgroundColor), 0, workingText.length(), spanFlags);
|
||||
}
|
||||
|
||||
if ((getPaintFlags() & Paint.STRIKE_THRU_TEXT_FLAG) != 0) {
|
||||
workingText.setSpan(new ReactStrikethroughSpan(), 0, workingText.length(), spanFlags);
|
||||
}
|
||||
|
||||
if ((getPaintFlags() & Paint.UNDERLINE_TEXT_FLAG) != 0) {
|
||||
workingText.setSpan(new ReactUnderlineSpan(), 0, workingText.length(), spanFlags);
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
float effectiveLetterSpacing = mTextAttributes.getEffectiveLetterSpacing();
|
||||
if (!Float.isNaN(effectiveLetterSpacing)) {
|
||||
workingText.setSpan(
|
||||
new CustomLetterSpacingSpan(effectiveLetterSpacing),
|
||||
0,
|
||||
workingText.length(),
|
||||
spanFlags);
|
||||
}
|
||||
}
|
||||
|
||||
if (mFontStyle != UNSET
|
||||
|| mFontWeight != UNSET
|
||||
|| mFontFamily != null
|
||||
|| getFontFeatureSettings() != null) {
|
||||
workingText.setSpan(
|
||||
new CustomStyleSpan(
|
||||
mFontStyle,
|
||||
mFontWeight,
|
||||
getFontFeatureSettings(),
|
||||
mFontFamily,
|
||||
getContext().getAssets()),
|
||||
0,
|
||||
workingText.length(),
|
||||
spanFlags);
|
||||
}
|
||||
|
||||
float lineHeight = mTextAttributes.getEffectiveLineHeight();
|
||||
if (!Float.isNaN(lineHeight)) {
|
||||
workingText.setSpan(new CustomLineHeightSpan(lineHeight), 0, workingText.length(), spanFlags);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -724,73 +865,6 @@ public class ReactEditText extends AppCompatEditText
|
||||
return true;
|
||||
}
|
||||
|
||||
// This is hacked in for Fabric. When we delete non-Fabric code, we might be able to simplify or
|
||||
// clean this up a bit.
|
||||
private void addSpansForMeasurement(Spannable spannable) {
|
||||
if (!mFabricViewStateManager.hasStateWrapper()) {
|
||||
return;
|
||||
}
|
||||
|
||||
boolean originalDisableTextDiffing = mDisableTextDiffing;
|
||||
mDisableTextDiffing = true;
|
||||
|
||||
int start = 0;
|
||||
int end = spannable.length();
|
||||
|
||||
// Remove duplicate spans we might add here
|
||||
Object[] spans = spannable.getSpans(0, length(), Object.class);
|
||||
for (Object span : spans) {
|
||||
int spanFlags = spannable.getSpanFlags(span);
|
||||
boolean isInclusive =
|
||||
(spanFlags & Spanned.SPAN_INCLUSIVE_INCLUSIVE) == Spanned.SPAN_INCLUSIVE_INCLUSIVE
|
||||
|| (spanFlags & Spanned.SPAN_INCLUSIVE_EXCLUSIVE) == Spanned.SPAN_INCLUSIVE_EXCLUSIVE;
|
||||
if (isInclusive
|
||||
&& span instanceof ReactSpan
|
||||
&& spannable.getSpanStart(span) == start
|
||||
&& spannable.getSpanEnd(span) == end) {
|
||||
spannable.removeSpan(span);
|
||||
}
|
||||
}
|
||||
|
||||
List<TextLayoutManager.SetSpanOperation> ops = new ArrayList<>();
|
||||
|
||||
if (!Float.isNaN(mTextAttributes.getLetterSpacing())) {
|
||||
ops.add(
|
||||
new TextLayoutManager.SetSpanOperation(
|
||||
start, end, new CustomLetterSpacingSpan(mTextAttributes.getLetterSpacing())));
|
||||
}
|
||||
ops.add(
|
||||
new TextLayoutManager.SetSpanOperation(
|
||||
start, end, new ReactAbsoluteSizeSpan((int) mTextAttributes.getEffectiveFontSize())));
|
||||
if (mFontStyle != UNSET || mFontWeight != UNSET || mFontFamily != null) {
|
||||
ops.add(
|
||||
new TextLayoutManager.SetSpanOperation(
|
||||
start,
|
||||
end,
|
||||
new CustomStyleSpan(
|
||||
mFontStyle,
|
||||
mFontWeight,
|
||||
null, // TODO: do we need to support FontFeatureSettings / fontVariant?
|
||||
mFontFamily,
|
||||
getReactContext(ReactEditText.this).getAssets())));
|
||||
}
|
||||
if (!Float.isNaN(mTextAttributes.getEffectiveLineHeight())) {
|
||||
ops.add(
|
||||
new TextLayoutManager.SetSpanOperation(
|
||||
start, end, new CustomLineHeightSpan(mTextAttributes.getEffectiveLineHeight())));
|
||||
}
|
||||
|
||||
int priority = 0;
|
||||
for (TextLayoutManager.SetSpanOperation op : ops) {
|
||||
// Actual order of calling {@code execute} does NOT matter,
|
||||
// but the {@code priority} DOES matter.
|
||||
op.execute(spannable, priority);
|
||||
priority++;
|
||||
}
|
||||
|
||||
mDisableTextDiffing = originalDisableTextDiffing;
|
||||
}
|
||||
|
||||
protected boolean showSoftKeyboard() {
|
||||
return mInputMethodManager.showSoftInput(this, 0);
|
||||
}
|
||||
@@ -1060,7 +1134,9 @@ public class ReactEditText extends AppCompatEditText
|
||||
|
||||
float effectiveLetterSpacing = mTextAttributes.getEffectiveLetterSpacing();
|
||||
if (!Float.isNaN(effectiveLetterSpacing)) {
|
||||
setLetterSpacing(effectiveLetterSpacing);
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
setLetterSpacing(effectiveLetterSpacing);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1075,7 +1151,7 @@ public class ReactEditText extends AppCompatEditText
|
||||
* TextLayoutManager.java with some very minor modifications. There's some duplication between
|
||||
* here and TextLayoutManager, so there might be an opportunity for refactor.
|
||||
*/
|
||||
private void updateCachedSpannable(boolean resetStyles) {
|
||||
private void updateCachedSpannable() {
|
||||
// Noops in non-Fabric
|
||||
if (mFabricViewStateManager == null || !mFabricViewStateManager.hasStateWrapper()) {
|
||||
return;
|
||||
@@ -1085,12 +1161,6 @@ public class ReactEditText extends AppCompatEditText
|
||||
return;
|
||||
}
|
||||
|
||||
if (resetStyles) {
|
||||
mIsSettingTextFromCacheUpdate = true;
|
||||
addSpansForMeasurement(getText());
|
||||
mIsSettingTextFromCacheUpdate = false;
|
||||
}
|
||||
|
||||
Editable currentText = getText();
|
||||
boolean haveText = currentText != null && currentText.length() > 0;
|
||||
|
||||
@@ -1132,8 +1202,7 @@ public class ReactEditText extends AppCompatEditText
|
||||
// ...
|
||||
// - android.app.Activity.dispatchKeyEvent (Activity.java:3447)
|
||||
try {
|
||||
Spannable text = (Spannable) currentText.subSequence(0, currentText.length());
|
||||
unstripAttributeEquivalentSpans(sb, text);
|
||||
sb.append(currentText.subSequence(0, currentText.length()));
|
||||
} catch (IndexOutOfBoundsException e) {
|
||||
ReactSoftExceptionLogger.logSoftException(TAG, e);
|
||||
}
|
||||
@@ -1149,11 +1218,9 @@ public class ReactEditText extends AppCompatEditText
|
||||
// Measure something so we have correct height, even if there's no string.
|
||||
sb.append("I");
|
||||
}
|
||||
|
||||
// Make sure that all text styles are applied when we're measurable the hint or "blank" text
|
||||
addSpansForMeasurement(sb);
|
||||
}
|
||||
|
||||
addSpansFromStyleAttributes(sb);
|
||||
TextLayoutManager.setCachedSpannabledForTag(getId(), sb);
|
||||
}
|
||||
|
||||
@@ -1168,7 +1235,7 @@ public class ReactEditText extends AppCompatEditText
|
||||
private class TextWatcherDelegator implements TextWatcher {
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
|
||||
if (!mIsSettingTextFromCacheUpdate && !mIsSettingTextFromJS && mListeners != null) {
|
||||
if (!mIsSettingTextFromJS && mListeners != null) {
|
||||
for (TextWatcher listener : mListeners) {
|
||||
listener.beforeTextChanged(s, start, count, after);
|
||||
}
|
||||
@@ -1182,23 +1249,20 @@ public class ReactEditText extends AppCompatEditText
|
||||
TAG, "onTextChanged[" + getId() + "]: " + s + " " + start + " " + before + " " + count);
|
||||
}
|
||||
|
||||
if (!mIsSettingTextFromCacheUpdate) {
|
||||
if (!mIsSettingTextFromJS && mListeners != null) {
|
||||
for (TextWatcher listener : mListeners) {
|
||||
listener.onTextChanged(s, start, before, count);
|
||||
}
|
||||
if (!mIsSettingTextFromJS && mListeners != null) {
|
||||
for (TextWatcher listener : mListeners) {
|
||||
listener.onTextChanged(s, start, before, count);
|
||||
}
|
||||
|
||||
updateCachedSpannable(
|
||||
!mIsSettingTextFromJS && !mIsSettingTextFromState && start == 0 && before == 0);
|
||||
}
|
||||
|
||||
updateCachedSpannable();
|
||||
|
||||
onContentSizeChange();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable s) {
|
||||
if (!mIsSettingTextFromCacheUpdate && !mIsSettingTextFromJS && mListeners != null) {
|
||||
if (!mIsSettingTextFromJS && mListeners != null) {
|
||||
for (TextWatcher listener : mListeners) {
|
||||
listener.afterTextChanged(s);
|
||||
}
|
||||
|
||||
+30
-19
@@ -13,6 +13,7 @@ import android.content.Context;
|
||||
import android.content.res.ColorStateList;
|
||||
import android.graphics.BlendMode;
|
||||
import android.graphics.BlendModeColorFilter;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Build;
|
||||
@@ -68,6 +69,7 @@ import com.facebook.react.views.text.DefaultStyleValuesUtil;
|
||||
import com.facebook.react.views.text.ReactBaseTextShadowNode;
|
||||
import com.facebook.react.views.text.ReactTextUpdate;
|
||||
import com.facebook.react.views.text.ReactTextViewManagerCallback;
|
||||
import com.facebook.react.views.text.ReactTypefaceUtils;
|
||||
import com.facebook.react.views.text.TextAttributeProps;
|
||||
import com.facebook.react.views.text.TextInlineImageSpan;
|
||||
import com.facebook.react.views.text.TextLayoutManager;
|
||||
@@ -326,21 +328,19 @@ public class ReactTextInputManager extends BaseViewManager<ReactEditText, Layout
|
||||
|
||||
if (!args.isNull(1)) {
|
||||
String text = args.getString(1);
|
||||
reactEditText.maybeSetTextFromJS(
|
||||
getReactTextUpdate(text, mostRecentEventCount, start, end));
|
||||
reactEditText.maybeSetTextFromJS(getReactTextUpdate(text, mostRecentEventCount));
|
||||
}
|
||||
reactEditText.maybeSetSelection(mostRecentEventCount, start, end);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private ReactTextUpdate getReactTextUpdate(
|
||||
String text, int mostRecentEventCount, int start, int end) {
|
||||
private ReactTextUpdate getReactTextUpdate(String text, int mostRecentEventCount) {
|
||||
SpannableStringBuilder sb = new SpannableStringBuilder();
|
||||
sb.append(TextTransform.apply(text, TextTransform.UNSET));
|
||||
|
||||
return new ReactTextUpdate(
|
||||
sb, mostRecentEventCount, false, 0, 0, 0, 0, Gravity.NO_GRAVITY, 0, 0, start, end);
|
||||
sb, mostRecentEventCount, false, 0, 0, 0, 0, Gravity.NO_GRAVITY, 0, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -371,9 +371,9 @@ public class ReactTextInputManager extends BaseViewManager<ReactEditText, Layout
|
||||
|
||||
// Ensure that selection is handled correctly on text update
|
||||
boolean isCurrentSelectionEmpty = view.getSelectionStart() == view.getSelectionEnd();
|
||||
int selectionStart = update.getSelectionStart();
|
||||
int selectionEnd = update.getSelectionEnd();
|
||||
if ((selectionStart == UNSET || selectionEnd == UNSET) && isCurrentSelectionEmpty) {
|
||||
int selectionStart = UNSET;
|
||||
int selectionEnd = UNSET;
|
||||
if (isCurrentSelectionEmpty) {
|
||||
// if selection is not set by state, shift current selection to ensure constant gap to
|
||||
// text end
|
||||
int textLength = view.getText() == null ? 0 : view.getText().length();
|
||||
@@ -412,6 +412,11 @@ public class ReactTextInputManager extends BaseViewManager<ReactEditText, Layout
|
||||
view.setFontStyle(fontStyle);
|
||||
}
|
||||
|
||||
@ReactProp(name = ViewProps.FONT_VARIANT)
|
||||
public void setFontVariant(ReactEditText view, @Nullable ReadableArray fontVariant) {
|
||||
view.setFontFeatureSettings(ReactTypefaceUtils.parseFontVariant(fontVariant));
|
||||
}
|
||||
|
||||
@ReactProp(name = ViewProps.INCLUDE_FONT_PADDING, defaultBoolean = true)
|
||||
public void setIncludeFontPadding(ReactEditText view, boolean includepad) {
|
||||
view.setIncludeFontPadding(includepad);
|
||||
@@ -500,7 +505,7 @@ public class ReactTextInputManager extends BaseViewManager<ReactEditText, Layout
|
||||
|
||||
@ReactProp(name = "placeholder")
|
||||
public void setPlaceholder(ReactEditText view, String placeholder) {
|
||||
view.setHint(placeholder);
|
||||
view.setPlaceholder(placeholder);
|
||||
}
|
||||
|
||||
@ReactProp(name = "placeholderTextColor", customType = "Color")
|
||||
@@ -935,6 +940,20 @@ public class ReactTextInputManager extends BaseViewManager<ReactEditText, Layout
|
||||
view.setAutoFocus(autoFocus);
|
||||
}
|
||||
|
||||
@ReactProp(name = ViewProps.TEXT_DECORATION_LINE)
|
||||
public void setTextDecorationLine(ReactEditText view, @Nullable String textDecorationLineString) {
|
||||
view.setPaintFlags(
|
||||
view.getPaintFlags() & ~(Paint.STRIKE_THRU_TEXT_FLAG | Paint.UNDERLINE_TEXT_FLAG));
|
||||
|
||||
for (String token : textDecorationLineString.split(" ")) {
|
||||
if (token.equals("underline")) {
|
||||
view.setPaintFlags(view.getPaintFlags() | Paint.UNDERLINE_TEXT_FLAG);
|
||||
} else if (token.equals("line-through")) {
|
||||
view.setPaintFlags(view.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ReactPropGroup(
|
||||
names = {
|
||||
ViewProps.BORDER_WIDTH,
|
||||
@@ -1329,9 +1348,6 @@ public class ReactTextInputManager extends BaseViewManager<ReactEditText, Layout
|
||||
TextLayoutManager.getOrCreateSpannableForText(
|
||||
view.getContext(), attributedString, mReactTextViewManagerCallback);
|
||||
|
||||
boolean containsMultipleFragments =
|
||||
attributedString.getArray("fragments").toArrayList().size() > 1;
|
||||
|
||||
int textBreakStrategy =
|
||||
TextAttributeProps.getTextBreakStrategy(
|
||||
paragraphAttributes.getString(ViewProps.TEXT_BREAK_STRATEGY));
|
||||
@@ -1344,8 +1360,7 @@ public class ReactTextInputManager extends BaseViewManager<ReactEditText, Layout
|
||||
TextAttributeProps.getTextAlignment(
|
||||
props, TextLayoutManager.isRTL(attributedString), view.getGravityHorizontal()),
|
||||
textBreakStrategy,
|
||||
TextAttributeProps.getJustificationMode(props, currentJustificationMode),
|
||||
containsMultipleFragments);
|
||||
TextAttributeProps.getJustificationMode(props, currentJustificationMode));
|
||||
}
|
||||
|
||||
public Object getReactTextUpdate(ReactEditText view, ReactStylesDiffMap props, MapBuffer state) {
|
||||
@@ -1366,9 +1381,6 @@ public class ReactTextInputManager extends BaseViewManager<ReactEditText, Layout
|
||||
TextLayoutManagerMapBuffer.getOrCreateSpannableForText(
|
||||
view.getContext(), attributedString, mReactTextViewManagerCallback);
|
||||
|
||||
boolean containsMultipleFragments =
|
||||
attributedString.getMapBuffer(TextLayoutManagerMapBuffer.AS_KEY_FRAGMENTS).getCount() > 1;
|
||||
|
||||
int textBreakStrategy =
|
||||
TextAttributeProps.getTextBreakStrategy(
|
||||
paragraphAttributes.getString(TextLayoutManagerMapBuffer.PA_KEY_TEXT_BREAK_STRATEGY));
|
||||
@@ -1381,7 +1393,6 @@ public class ReactTextInputManager extends BaseViewManager<ReactEditText, Layout
|
||||
TextAttributeProps.getTextAlignment(
|
||||
props, TextLayoutManagerMapBuffer.isRTL(attributedString), view.getGravityHorizontal()),
|
||||
textBreakStrategy,
|
||||
TextAttributeProps.getJustificationMode(props, currentJustificationMode),
|
||||
containsMultipleFragments);
|
||||
TextAttributeProps.getJustificationMode(props, currentJustificationMode));
|
||||
}
|
||||
}
|
||||
|
||||
+1
-31
@@ -17,7 +17,6 @@ import androidx.annotation.Nullable;
|
||||
import androidx.core.view.ViewCompat;
|
||||
import com.facebook.common.logging.FLog;
|
||||
import com.facebook.infer.annotation.Assertions;
|
||||
import com.facebook.react.bridge.ReadableMap;
|
||||
import com.facebook.react.common.ReactConstants;
|
||||
import com.facebook.react.common.annotations.VisibleForTesting;
|
||||
import com.facebook.react.uimanager.Spacing;
|
||||
@@ -44,13 +43,10 @@ public class ReactTextInputShadowNode extends ReactBaseTextShadowNode
|
||||
|
||||
@VisibleForTesting public static final String PROP_TEXT = "text";
|
||||
@VisibleForTesting public static final String PROP_PLACEHOLDER = "placeholder";
|
||||
@VisibleForTesting public static final String PROP_SELECTION = "selection";
|
||||
|
||||
// Represents the {@code text} property only, not possible nested content.
|
||||
private @Nullable String mText = null;
|
||||
private @Nullable String mPlaceholder = null;
|
||||
private int mSelectionStart = UNSET;
|
||||
private int mSelectionEnd = UNSET;
|
||||
|
||||
public ReactTextInputShadowNode(
|
||||
@Nullable ReactTextViewManagerCallback reactTextViewManagerCallback) {
|
||||
@@ -165,18 +161,6 @@ public class ReactTextInputShadowNode extends ReactBaseTextShadowNode
|
||||
@ReactProp(name = PROP_TEXT)
|
||||
public void setText(@Nullable String text) {
|
||||
mText = text;
|
||||
if (text != null) {
|
||||
// The selection shouldn't be bigger than the length of the text
|
||||
if (mSelectionStart > text.length()) {
|
||||
mSelectionStart = text.length();
|
||||
}
|
||||
if (mSelectionEnd > text.length()) {
|
||||
mSelectionEnd = text.length();
|
||||
}
|
||||
} else {
|
||||
mSelectionStart = UNSET;
|
||||
mSelectionEnd = UNSET;
|
||||
}
|
||||
markUpdated();
|
||||
}
|
||||
|
||||
@@ -194,18 +178,6 @@ public class ReactTextInputShadowNode extends ReactBaseTextShadowNode
|
||||
return mPlaceholder;
|
||||
}
|
||||
|
||||
@ReactProp(name = PROP_SELECTION)
|
||||
public void setSelection(@Nullable ReadableMap selection) {
|
||||
mSelectionStart = mSelectionEnd = UNSET;
|
||||
if (selection == null) return;
|
||||
|
||||
if (selection.hasKey("start") && selection.hasKey("end")) {
|
||||
mSelectionStart = selection.getInt("start");
|
||||
mSelectionEnd = selection.getInt("end");
|
||||
markUpdated();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTextBreakStrategy(@Nullable String textBreakStrategy) {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
|
||||
@@ -245,9 +217,7 @@ public class ReactTextInputShadowNode extends ReactBaseTextShadowNode
|
||||
getPadding(Spacing.BOTTOM),
|
||||
mTextAlign,
|
||||
mTextBreakStrategy,
|
||||
mJustificationMode,
|
||||
mSelectionStart,
|
||||
mSelectionEnd);
|
||||
mJustificationMode);
|
||||
uiViewOperationQueue.enqueueUpdateExtraData(getReactTag(), reactTextUpdate);
|
||||
}
|
||||
}
|
||||
|
||||
+17
-14
@@ -567,19 +567,28 @@ public class ReactViewBackgroundDrawable extends Drawable {
|
||||
int colorRight = getBorderColor(Spacing.RIGHT);
|
||||
int colorBottom = getBorderColor(Spacing.BOTTOM);
|
||||
int borderColor = getBorderColor(Spacing.ALL);
|
||||
|
||||
int colorBlock = getBorderColor(Spacing.BLOCK);
|
||||
int colorBlockStart = getBorderColor(Spacing.BLOCK_START);
|
||||
int colorBlockEnd = getBorderColor(Spacing.BLOCK_END);
|
||||
|
||||
if (isBorderColorDefined(Spacing.BLOCK)) {
|
||||
colorBottom = colorBlock;
|
||||
colorTop = colorBlock;
|
||||
}
|
||||
if (isBorderColorDefined(Spacing.BLOCK_END)) {
|
||||
colorBottom = colorBlockEnd;
|
||||
}
|
||||
if (isBorderColorDefined(Spacing.BLOCK_START)) {
|
||||
colorTop = colorBlockStart;
|
||||
}
|
||||
|
||||
// Clip border ONLY if its color is non transparent
|
||||
if (Color.alpha(colorLeft) != 0
|
||||
&& Color.alpha(colorTop) != 0
|
||||
&& Color.alpha(colorRight) != 0
|
||||
&& Color.alpha(colorBottom) != 0
|
||||
&& Color.alpha(borderColor) != 0
|
||||
&& Color.alpha(colorBlock) != 0
|
||||
&& Color.alpha(colorBlockStart) != 0
|
||||
&& Color.alpha(colorBlockEnd) != 0) {
|
||||
&& Color.alpha(borderColor) != 0) {
|
||||
|
||||
mInnerClipTempRectForBorderRadius.top += borderWidth.top;
|
||||
mInnerClipTempRectForBorderRadius.bottom -= borderWidth.bottom;
|
||||
@@ -885,7 +894,7 @@ public class ReactViewBackgroundDrawable extends Drawable {
|
||||
|
||||
/** Compute mInnerTopLeftCorner */
|
||||
mInnerTopLeftCorner.x = mInnerClipTempRectForBorderRadius.left;
|
||||
mInnerTopLeftCorner.y = mInnerClipTempRectForBorderRadius.top * 2;
|
||||
mInnerTopLeftCorner.y = mInnerClipTempRectForBorderRadius.top;
|
||||
|
||||
getEllipseIntersectionWithLine(
|
||||
// Ellipse Bounds
|
||||
@@ -911,10 +920,7 @@ public class ReactViewBackgroundDrawable extends Drawable {
|
||||
}
|
||||
|
||||
mInnerBottomLeftCorner.x = mInnerClipTempRectForBorderRadius.left;
|
||||
mInnerBottomLeftCorner.y =
|
||||
borderWidth.bottom != 0
|
||||
? mInnerClipTempRectForBorderRadius.bottom * -2
|
||||
: mInnerClipTempRectForBorderRadius.bottom;
|
||||
mInnerBottomLeftCorner.y = mInnerClipTempRectForBorderRadius.bottom;
|
||||
|
||||
getEllipseIntersectionWithLine(
|
||||
// Ellipse Bounds
|
||||
@@ -940,7 +946,7 @@ public class ReactViewBackgroundDrawable extends Drawable {
|
||||
}
|
||||
|
||||
mInnerTopRightCorner.x = mInnerClipTempRectForBorderRadius.right;
|
||||
mInnerTopRightCorner.y = mInnerClipTempRectForBorderRadius.top * 2;
|
||||
mInnerTopRightCorner.y = mInnerClipTempRectForBorderRadius.top;
|
||||
|
||||
getEllipseIntersectionWithLine(
|
||||
// Ellipse Bounds
|
||||
@@ -966,10 +972,7 @@ public class ReactViewBackgroundDrawable extends Drawable {
|
||||
}
|
||||
|
||||
mInnerBottomRightCorner.x = mInnerClipTempRectForBorderRadius.right;
|
||||
mInnerBottomRightCorner.y =
|
||||
borderWidth.bottom != 0
|
||||
? mInnerClipTempRectForBorderRadius.bottom * -2
|
||||
: mInnerClipTempRectForBorderRadius.bottom;
|
||||
mInnerBottomRightCorner.y = mInnerClipTempRectForBorderRadius.bottom;
|
||||
|
||||
getEllipseIntersectionWithLine(
|
||||
// Ellipse Bounds
|
||||
|
||||
+5
@@ -19,6 +19,7 @@ public class ReactViewBackgroundManager {
|
||||
|
||||
private @Nullable ReactViewBackgroundDrawable mReactBackgroundDrawable;
|
||||
private View mView;
|
||||
private int mColor = Color.TRANSPARENT;
|
||||
|
||||
public ReactViewBackgroundManager(View view) {
|
||||
this.mView = view;
|
||||
@@ -56,6 +57,10 @@ public class ReactViewBackgroundManager {
|
||||
}
|
||||
}
|
||||
|
||||
public int getBackgroundColor() {
|
||||
return mColor;
|
||||
}
|
||||
|
||||
public void setBorderWidth(int position, float width) {
|
||||
getOrCreateReactViewBackground().setBorderWidth(position, width);
|
||||
}
|
||||
|
||||
@@ -190,7 +190,6 @@ add_executable(reactnative_unittest
|
||||
-fexceptions
|
||||
-frtti
|
||||
-std=c++17
|
||||
-DANDROID
|
||||
-DHERMES_ENABLE_DEBUGGER)
|
||||
|
||||
target_link_libraries(reactnative_unittest
|
||||
|
||||
@@ -429,6 +429,9 @@ void Binding::installFabricUIManager(
|
||||
"CalculateTransformedFramesEnabled",
|
||||
getFeatureFlagValue("calculateTransformedFramesEnabled"));
|
||||
|
||||
CoreFeatures::cacheLastTextMeasurement =
|
||||
getFeatureFlagValue("enableTextMeasureCachePerShadowNode");
|
||||
|
||||
// Props setter pattern feature
|
||||
CoreFeatures::enablePropIteratorSetter =
|
||||
getFeatureFlagValue("enableCppPropsIteratorSetter");
|
||||
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package com.facebook.react.bridge.interop;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import com.facebook.react.bridge.WritableArray;
|
||||
import com.facebook.react.bridge.WritableMap;
|
||||
import com.facebook.react.uimanager.events.RCTEventEmitter;
|
||||
|
||||
public class FakeRCTEventEmitter implements RCTEventEmitter {
|
||||
|
||||
@Override
|
||||
public void receiveEvent(int targetReactTag, String eventName, @Nullable WritableMap event) {}
|
||||
|
||||
@Override
|
||||
public void receiveTouches(
|
||||
String eventName, WritableArray touches, WritableArray changedIndices) {}
|
||||
}
|
||||
+92
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package com.facebook.react.bridge.interop;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import com.facebook.react.config.ReactFeatureFlags;
|
||||
import com.facebook.react.modules.core.JSTimers;
|
||||
import com.facebook.react.uimanager.events.RCTEventEmitter;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
public class InteropModuleRegistryTest {
|
||||
|
||||
InteropModuleRegistry underTest;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
underTest = new InteropModuleRegistry();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldReturnInteropModule_withFabricDisabled_returnsFalse() {
|
||||
ReactFeatureFlags.enableFabricRenderer = false;
|
||||
|
||||
assertFalse(underTest.shouldReturnInteropModule(RCTEventEmitter.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldReturnInteropModule_withFabricInteropDisabled_returnsFalse() {
|
||||
ReactFeatureFlags.enableFabricRenderer = true;
|
||||
ReactFeatureFlags.unstable_useFabricInterop = false;
|
||||
|
||||
assertFalse(underTest.shouldReturnInteropModule(RCTEventEmitter.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldReturnInteropModule_withUnregisteredClass_returnsFalse() {
|
||||
ReactFeatureFlags.enableFabricRenderer = true;
|
||||
ReactFeatureFlags.unstable_useFabricInterop = true;
|
||||
|
||||
assertFalse(underTest.shouldReturnInteropModule(JSTimers.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldReturnInteropModule_withRegisteredClass_returnsTrue() {
|
||||
ReactFeatureFlags.enableFabricRenderer = true;
|
||||
ReactFeatureFlags.unstable_useFabricInterop = true;
|
||||
|
||||
underTest.registerInteropModule(RCTEventEmitter.class, new FakeRCTEventEmitter());
|
||||
|
||||
assertTrue(underTest.shouldReturnInteropModule(RCTEventEmitter.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getInteropModule_withRegisteredClassAndInvalidFlags_returnsNull() {
|
||||
ReactFeatureFlags.enableFabricRenderer = false;
|
||||
ReactFeatureFlags.unstable_useFabricInterop = false;
|
||||
underTest.registerInteropModule(RCTEventEmitter.class, new FakeRCTEventEmitter());
|
||||
|
||||
RCTEventEmitter interopModule = underTest.getInteropModule(RCTEventEmitter.class);
|
||||
|
||||
assertNull(interopModule);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getInteropModule_withRegisteredClassAndValidFlags_returnsInteropModule() {
|
||||
ReactFeatureFlags.enableFabricRenderer = true;
|
||||
ReactFeatureFlags.unstable_useFabricInterop = true;
|
||||
underTest.registerInteropModule(RCTEventEmitter.class, new FakeRCTEventEmitter());
|
||||
|
||||
RCTEventEmitter interopModule = underTest.getInteropModule(RCTEventEmitter.class);
|
||||
|
||||
assertTrue(interopModule instanceof FakeRCTEventEmitter);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getInteropModule_withUnregisteredClass_returnsNull() {
|
||||
ReactFeatureFlags.enableFabricRenderer = true;
|
||||
ReactFeatureFlags.unstable_useFabricInterop = true;
|
||||
JSTimers missingModule = underTest.getInteropModule(JSTimers.class);
|
||||
|
||||
assertNull(missingModule);
|
||||
}
|
||||
}
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package com.facebook.react.fabric.interop;
|
||||
|
||||
import com.facebook.react.uimanager.events.BatchEventDispatchedListener;
|
||||
import com.facebook.react.uimanager.events.Event;
|
||||
import com.facebook.react.uimanager.events.EventDispatcher;
|
||||
import com.facebook.react.uimanager.events.EventDispatcherListener;
|
||||
import com.facebook.react.uimanager.events.RCTEventEmitter;
|
||||
import com.facebook.react.uimanager.events.RCTModernEventEmitter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
public class FakeEventDispatcher implements EventDispatcher {
|
||||
|
||||
List<Event> recordedDispatchedEvents = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
public void dispatchEvent(Event event) {
|
||||
recordedDispatchedEvents.add(event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispatchAllEvents() {}
|
||||
|
||||
@Override
|
||||
public void addListener(EventDispatcherListener listener) {}
|
||||
|
||||
@Override
|
||||
public void removeListener(EventDispatcherListener listener) {}
|
||||
|
||||
@Override
|
||||
public void addBatchEventDispatchedListener(BatchEventDispatchedListener listener) {}
|
||||
|
||||
@Override
|
||||
public void removeBatchEventDispatchedListener(BatchEventDispatchedListener listener) {}
|
||||
|
||||
@Override
|
||||
public void registerEventEmitter(int uiManagerType, RCTEventEmitter eventEmitter) {}
|
||||
|
||||
@Override
|
||||
public void registerEventEmitter(int uiManagerType, RCTModernEventEmitter eventEmitter) {}
|
||||
|
||||
@Override
|
||||
public void unregisterEventEmitter(int uiManagerType) {}
|
||||
|
||||
@Override
|
||||
public void onCatalystInstanceDestroyed() {}
|
||||
}
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
package com.facebook.react.fabric.interop;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
|
||||
import com.facebook.react.bridge.JavaOnlyMap;
|
||||
import com.facebook.react.bridge.ReactApplicationContext;
|
||||
import com.facebook.react.bridge.ReactContext;
|
||||
import com.facebook.react.bridge.WritableMap;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.RuntimeEnvironment;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
public class InteropEventEmitterTest {
|
||||
|
||||
ReactContext mReactContext;
|
||||
FakeEventDispatcher mEventDispatcher;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
mReactContext = new ReactApplicationContext(RuntimeEnvironment.application);
|
||||
mEventDispatcher = new FakeEventDispatcher();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void receiveEvent_dispatchesCorrectly() {
|
||||
InteropEventEmitter eventEmitter = new InteropEventEmitter(mReactContext);
|
||||
eventEmitter.overrideEventDispatcher(mEventDispatcher);
|
||||
|
||||
eventEmitter.receiveEvent(42, "onTest", null);
|
||||
|
||||
assertEquals(1, mEventDispatcher.recordedDispatchedEvents.size());
|
||||
assertEquals("onTest", mEventDispatcher.recordedDispatchedEvents.get(0).getEventName());
|
||||
assertEquals(InteropEvent.class, mEventDispatcher.recordedDispatchedEvents.get(0).getClass());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void receiveEvent_dataIsPreserved() {
|
||||
InteropEventEmitter eventEmitter = new InteropEventEmitter(mReactContext);
|
||||
eventEmitter.overrideEventDispatcher(mEventDispatcher);
|
||||
WritableMap eventData = JavaOnlyMap.of("color", "indigo");
|
||||
|
||||
eventEmitter.receiveEvent(42, "onTest", eventData);
|
||||
|
||||
InteropEvent event = (InteropEvent) mEventDispatcher.recordedDispatchedEvents.get(0);
|
||||
WritableMap dispatchedEventData = event.getEventData();
|
||||
assertNotNull(dispatchedEventData);
|
||||
assertEquals("indigo", dispatchedEventData.getString("color"));
|
||||
}
|
||||
|
||||
@Test(expected = UnsupportedOperationException.class)
|
||||
public void receiveTouches_isNotSupported() {
|
||||
InteropEventEmitter eventEmitter = new InteropEventEmitter(mReactContext);
|
||||
eventEmitter.receiveTouches("a touch", null, null);
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user