Compare commits

..
Author SHA1 Message Date
Christian Falch a97e38029d Added missing define to target .reactRuntime
The target needs the HERMES_ENABLE_DEBUGGER flag in debug just like .reactHermes does.

This commit fixes this by adding the define.
2025-06-17 14:28:37 +02:00
209 changed files with 5596 additions and 15033 deletions
-1
View File
@@ -48,7 +48,6 @@ packages/react-native/flow/
[options]
enums=true
casting_syntax=both
component_syntax=true
emoji=true
+3
View File
@@ -26,6 +26,9 @@ runs:
- name: Lint code
shell: bash
run: ./.github/workflow-scripts/exec_swallow_error.sh yarn lint --format junit -o ./reports/junit/eslint/results.xml
- name: Lint java
shell: bash
run: ./.github/workflow-scripts/exec_swallow_error.sh yarn lint-java --check
- name: Lint file structure
shell: bash
run: ./.github/workflow-scripts/lint_files.sh
+3
View File
@@ -13,6 +13,9 @@ export GITHUB_REPO=-react-native
echo flow
npm run flow-check --silent --json
echo google-java-format
node scripts/lint-java.js --diff
} | node private/react-native-bots/code-analysis-bot.js
STATUS=$?
+22
View File
@@ -140,6 +140,27 @@ jobs:
hermes-version: ${{ needs.prepare_hermes_workspace.outputs.hermes-version }}
react-native-version: ${{ needs.prepare_hermes_workspace.outputs.react-native-version }}
build_android:
runs-on: 8-core-ubuntu
needs: [set_release_type]
container:
image: reactnativecommunity/react-native-android:latest
env:
TERM: "dumb"
GRADLE_OPTS: "-Dorg.gradle.daemon=false"
ORG_GRADLE_PROJECT_SIGNING_PWD: ${{ secrets.ORG_GRADLE_PROJECT_SIGNING_PWD }}
ORG_GRADLE_PROJECT_SIGNING_KEY: ${{ secrets.ORG_GRADLE_PROJECT_SIGNING_KEY }}
ORG_GRADLE_PROJECT_SONATYPE_USERNAME: ${{ secrets.ORG_GRADLE_PROJECT_SONATYPE_USERNAME }}
ORG_GRADLE_PROJECT_SONATYPE_PASSWORD: ${{ secrets.ORG_GRADLE_PROJECT_SONATYPE_PASSWORD }}
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Build Android
uses: ./.github/actions/build-android
with:
release-type: ${{ needs.set_release_type.outputs.RELEASE_TYPE }}
gradle-cache-encryption-key: ${{ secrets.GRADLE_CACHE_ENCRYPTION_KEY }}
build_npm_package:
runs-on: 8-core-ubuntu
needs:
@@ -149,6 +170,7 @@ jobs:
build_hermes_macos,
build_hermesc_linux,
build_hermesc_windows,
build_android,
prebuild_apple_dependencies,
prebuild_react_native_core,
]
+1
View File
@@ -139,6 +139,7 @@ vendor/
/packages/react-native/React/FBReactNativeSpec/
/packages/react-native-codegen/lib
/packages/react-native-codegen/tmp/
/packages/react-native/ReactCommon/react/renderer/components/rncore/
/packages/rn-tester/NativeModuleExample/ScreenshotManagerSpec*
/**/RCTThirdPartyFabricComponentsProvider.*
-16
View File
@@ -321,22 +321,6 @@
- Fix bug: unstable_hasComponent(*) = true for unregistered components for n > 1th call. ([f4d99d6a23](https://github.com/facebook/react-native/commit/f4d99d6a23a08c2ef2c2fff78dc966961608683b) by [@RSNara](https://github.com/RSNara))
- RCTDeviceInfo: fix crash due to failure to get AccessibilityManager ([ac23323da1](https://github.com/facebook/react-native/commit/ac23323da1bda2ce271797aa58dd74ffb0a5992f) by Adam Ernst)
## v0.79.4
### Fixed
- **Codegen:** Backports fix: exclusion of selectively disabled libraries from codegen generation ([3acfe01382](https://github.com/facebook/react-native/commit/3acfe01382e67b16386bb04f7730abc479fec1cc) by [@cipolleschi](https://github.com/cipolleschi))
- **Flow:** Publish top-level Flow types for `react-native` ([50667eceb1](https://github.com/facebook/react-native/commit/50667eceb1be4771375d6a3cc2f4e42d4d8aad3a) by [@aswinandro](https://github.com/aswinandro))
- **Runtime:** Disabling `updateRuntimeShadowNodeReferencesOnCommit` by default. ([2cd074d77a](https://github.com/facebook/react-native/commit/2cd074d77a706f4fe4396ffdf87408f781e935a4) by [@lenaic](https://github.com/lenaic))
#### Android specific
- **TextInput:** Fix broken focus behavior for TextInput in older Android versions (< 9) ([0ebaaed281](https://github.com/facebook/react-native/commit/0ebaaed281fcfa01b20a17a461c1d0b489a7f0a2) by [@joevilches](https://github.com/joevilches))
#### iOS specific
- **Dimensions:** Fix Dimensions not changing when orientation changes ([235ac2cca0](https://github.com/facebook/react-native/commit/235ac2cca0b173934d329f3a797f136a219d315f) by [@riteshshukla04](https://github.com/riteshshukla04))
## v0.79.3
### Fixed
+2 -1
View File
@@ -17,6 +17,7 @@
"format": "npm run prettier && npm run clang-format",
"featureflags": "yarn --cwd packages/react-native featureflags",
"lint-ci": "./.github/workflow-scripts/analyze_code.sh && yarn shellcheck",
"lint-java": "node ./scripts/lint-java.js",
"lint-markdown": "markdownlint-cli2 2>&1",
"lint": "eslint --max-warnings 0 .",
"prettier": "prettier --write \"./**/*.{js,md,yml,ts,tsx}\"",
@@ -54,7 +55,7 @@
"@react-native/metro-babel-transformer": "0.80.0-main",
"@react-native/metro-config": "0.80.0-main",
"@tsconfig/node22": "22.0.2",
"@types/react": "^19.1.0",
"@types/react": "^19.0.0",
"@typescript-eslint/parser": "^7.1.1",
"ansi-styles": "^4.2.1",
"babel-plugin-minify-dead-code-elimination": "^0.5.2",
@@ -1,116 +0,0 @@
/*
* 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.tasks.internal
import javax.inject.Inject
import org.gradle.api.DefaultTask
import org.gradle.api.file.ConfigurableFileCollection
import org.gradle.api.file.CopySpec
import org.gradle.api.file.DirectoryProperty
import org.gradle.api.file.DuplicatesStrategy
import org.gradle.api.file.FileSystemOperations
import org.gradle.api.provider.Property
import org.gradle.api.tasks.*
/**
* A task that takes care of extracting gflags from a source folder/zip and preparing it to be
* consumed by the NDK. This task will also take care of applying the mapping for gflags parameters.
*/
abstract class PrepareGflagsTask : DefaultTask() {
@get:InputFiles abstract val gflagsPath: ConfigurableFileCollection
@get:InputDirectory abstract val gflagsThirdPartyPath: DirectoryProperty
@get:Input abstract val gflagsVersion: Property<String>
@get:OutputDirectory abstract val outputDir: DirectoryProperty
@get:Inject abstract val fs: FileSystemOperations
@TaskAction
fun taskAction() {
val commonCopyConfig: (action: CopySpec) -> Unit = { action ->
action.from(gflagsPath)
action.from(gflagsThirdPartyPath)
action.duplicatesStrategy = DuplicatesStrategy.INCLUDE
action.includeEmptyDirs = false
action.into(outputDir)
}
fs.copy { action ->
commonCopyConfig(action)
action.include(
"gflags-${gflagsVersion.get()}/src/*.h",
"gflags-${gflagsVersion.get()}/src/*.cc",
"CMakeLists.txt")
action.filesMatching("*/src/*") { matchedFile ->
matchedFile.path = "gflags/${matchedFile.name}"
}
}
fs.copy { action ->
commonCopyConfig(action)
action.include("gflags-${gflagsVersion.get()}/src/gflags_declare.h.in")
action.filesMatching("*/src/*") { matchedFile ->
matchedFile.filter { line ->
// Replace all placeholders with appropriate values
// see https://github.com/gflags/gflags/blob/v2.2.0/src/gflags_declare.h.in
line
.replace(Regex("@GFLAGS_NAMESPACE@"), "gflags")
.replace(
Regex(
"@(HAVE_STDINT_H|HAVE_SYS_TYPES_H|HAVE_INTTYPES_H|GFLAGS_INTTYPES_FORMAT_C99)@"),
"1")
.replace(Regex("@([A-Z0-9_]+)@"), "1")
}
matchedFile.path = "gflags/${matchedFile.name.removeSuffix(".in")}"
}
}
fs.copy { action ->
commonCopyConfig(action)
action.include("gflags-${gflagsVersion.get()}/src/config.h.in")
action.filesMatching("*/src/*") { matchedFile ->
matchedFile.filter { line -> line.replace(Regex("^#cmakedefine"), "//cmakedefine") }
matchedFile.path = "gflags/${matchedFile.name.removeSuffix(".in")}"
}
}
fs.copy { action ->
commonCopyConfig(action)
action.include("gflags-${gflagsVersion.get()}/src/gflags_ns.h.in")
action.filesMatching("*/src/*") { matchedFile ->
matchedFile.filter { line ->
line.replace(Regex("@ns@"), "google").replace(Regex("@NS@"), "google".uppercase())
}
matchedFile.path = "gflags/gflags_google.h"
}
}
fs.copy { action ->
commonCopyConfig(action)
action.include("gflags-${gflagsVersion.get()}/src/gflags.h.in")
action.filesMatching("*/src/*") { matchedFile ->
matchedFile.filter { line ->
line
.replace(Regex("@GFLAGS_ATTRIBUTE_UNUSED@"), "")
.replace(Regex("@INCLUDE_GFLAGS_NS_H@"), "#include \"gflags/gflags_google.h\"")
}
matchedFile.path = "gflags/${matchedFile.name.removeSuffix(".in")}"
}
}
fs.copy { action ->
commonCopyConfig(action)
action.include("gflags-${gflagsVersion.get()}/src/gflags_completions.h.in")
action.filesMatching("*/src/*") { matchedFile ->
matchedFile.filter { line -> line.replace(Regex("@GFLAGS_NAMESPACE@"), "gflags") }
matchedFile.path = "gflags/${matchedFile.name.removeSuffix(".in")}"
}
}
}
}
@@ -126,8 +126,6 @@ internal object NdkConfiguratorUtils {
): Pair<List<String>, List<String>> {
val excludes = mutableListOf<String>()
val includes = mutableListOf<String>()
// note: libjsctooling.so is kept here for backward compatibility.
when {
hermesEnabled -> {
excludes.add("**/libjsc.so")
@@ -1,209 +0,0 @@
/*
* 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.tasks.internal
import com.facebook.react.tests.createProject
import com.facebook.react.tests.createTestTask
import java.io.*
import org.assertj.core.api.Assertions.assertThat
import org.junit.Assert.assertEquals
import org.junit.Rule
import org.junit.Test
import org.junit.rules.TemporaryFolder
class PrepareGflagsTaskTest {
@get:Rule val tempFolder = TemporaryFolder()
@Test(expected = IllegalStateException::class)
fun prepareGflagsTask_withMissingConfiguration_fails() {
val task = createTestTask<PrepareGflagsTask>()
task.taskAction()
}
@Test
fun prepareGflagsTask_copiesCMakefile() {
val gflagspath = tempFolder.newFolder("gflagspath")
val output = tempFolder.newFolder("output")
val project = createProject()
val gflagsThirdPartyPath = File(project.projectDir, "src/main/jni/third-party/gflags/")
val task =
createTestTask<PrepareGflagsTask>(project = project) {
it.gflagsPath.setFrom(gflagspath)
it.gflagsThirdPartyPath.set(gflagsThirdPartyPath)
it.gflagsVersion.set("1.0.0")
it.outputDir.set(output)
}
File(gflagsThirdPartyPath, "CMakeLists.txt").apply {
parentFile.mkdirs()
createNewFile()
}
task.taskAction()
assertThat(output.listFiles()!!.any { it.name == "CMakeLists.txt" }).isTrue()
}
@Test
fun prepareGflagsTask_copiesSourceCodeAndHeaders() {
val gflagspath = tempFolder.newFolder("gflagspath")
val gflagsThirdPartyPath = tempFolder.newFolder("gflagspath/jni")
val output = tempFolder.newFolder("output")
val task =
createTestTask<PrepareGflagsTask> {
it.gflagsPath.setFrom(gflagspath)
it.gflagsThirdPartyPath.set(gflagsThirdPartyPath)
it.gflagsVersion.set("1.0.0")
it.outputDir.set(output)
}
File(gflagspath, "gflags-1.0.0/src/gflags.cc").apply {
parentFile.mkdirs()
createNewFile()
}
File(gflagspath, "gflags-1.0.0/src/util.h").apply {
parentFile.mkdirs()
createNewFile()
}
task.taskAction()
assertThat(File(output, "gflags/gflags.cc").exists()).isTrue()
assertThat(File(output, "gflags/util.h").exists()).isTrue()
}
@Test
fun prepareGflagsTask_replacesTokenCorrectly() {
val gflagspath = tempFolder.newFolder("gflagspath")
val gflagsThirdPartyPath = tempFolder.newFolder("gflagspath/jni")
val output = tempFolder.newFolder("output")
val task =
createTestTask<PrepareGflagsTask> {
it.gflagsPath.setFrom(gflagspath)
it.gflagsThirdPartyPath.set(gflagsThirdPartyPath)
it.gflagsVersion.set("1.0.0")
it.outputDir.set(output)
}
File(gflagspath, "gflags-1.0.0/src/gflags_declare.h.in").apply {
parentFile.mkdirs()
writeText(
"""
#define GFLAGS_NAMESPACE @GFLAGS_NAMESPACE@
#include <string>
#if @HAVE_STDINT_H@
# include <stdint.h>
#elif @HAVE_SYS_TYPES_H@
# include <sys/types.h>
#elif @HAVE_INTTYPES_H@
# include <inttypes.h>
#endif
namespace GFLAGS_NAMESPACE {
#if @GFLAGS_INTTYPES_FORMAT_C99@ // C99
typedef int32_t int32;
typedef uint32_t uint32;
typedef int64_t int64;
typedef uint64_t uint64;
#elif @GFLAGS_INTTYPES_FORMAT_BSD@ // BSD
typedef int32_t int32;
typedef u_int32_t uint32;
typedef int64_t int64;
typedef u_int64_t uint64;
#elif @GFLAGS_INTTYPES_FORMAT_VC7@ // Windows
typedef __int32 int32;
typedef unsigned __int32 uint32;
typedef __int64 int64;
typedef unsigned __int64 uint64;
#else
# error Do not know how to define a 32-bit integer quantity on your system
#endif
} // namespace GFLAGS_NAMESPACE
""")
}
File(gflagspath, "gflags-1.0.0/src/config.h.in").apply {
parentFile.mkdirs()
createNewFile()
writeText("#cmakedefine")
}
File(gflagspath, "gflags-1.0.0/src/gflags_ns.h.in").apply {
parentFile.mkdirs()
createNewFile()
writeText("@ns@ @NS@")
}
File(gflagspath, "gflags-1.0.0/src/gflags.h.in").apply {
parentFile.mkdirs()
createNewFile()
writeText("@GFLAGS_ATTRIBUTE_UNUSED@\n@INCLUDE_GFLAGS_NS_H@")
}
File(gflagspath, "gflags-1.0.0/src/gflags_completions.h.in").apply {
parentFile.mkdirs()
createNewFile()
writeText("@GFLAGS_NAMESPACE@")
}
task.taskAction()
val declareFile = File(output, "gflags/gflags_declare.h")
assertThat(declareFile.exists()).isTrue()
assertEquals(
declareFile.readText(),
"""
#define GFLAGS_NAMESPACE gflags
#include <string>
#if 1
# include <stdint.h>
#elif 1
# include <sys/types.h>
#elif 1
# include <inttypes.h>
#endif
namespace GFLAGS_NAMESPACE {
#if 1 // C99
typedef int32_t int32;
typedef uint32_t uint32;
typedef int64_t int64;
typedef uint64_t uint64;
#elif 1 // BSD
typedef int32_t int32;
typedef u_int32_t uint32;
typedef int64_t int64;
typedef u_int64_t uint64;
#elif 1 // Windows
typedef __int32 int32;
typedef unsigned __int32 uint32;
typedef __int64 int64;
typedef unsigned __int64 uint64;
#else
# error Do not know how to define a 32-bit integer quantity on your system
#endif
} // namespace GFLAGS_NAMESPACE
""")
val configFile = File(output, "gflags/config.h")
assertThat(configFile.exists()).isTrue()
assertEquals(configFile.readText(), "//cmakedefine")
val nsFile = File(output, "gflags/gflags_google.h")
assertThat(nsFile.exists()).isTrue()
assertEquals(nsFile.readText(), "google GOOGLE")
val gflagsFile = File(output, "gflags/gflags.h")
assertThat(gflagsFile.exists()).isTrue()
assertEquals(gflagsFile.readText(), "\n#include \"gflags/gflags_google.h\"")
val completionsFile = File(output, "gflags/gflags_completions.h")
assertThat(completionsFile.exists()).isTrue()
assertEquals(completionsFile.readText(), "gflags")
}
}
+1 -1
View File
@@ -20,7 +20,7 @@
],
"dependencies": {},
"peerDependencies": {
"@types/react": "^19.1.0",
"@types/react": "^19.0.0",
"react": "*",
"react-native": "*"
},
+1 -4
View File
@@ -10,16 +10,13 @@
'use strict';
// This is a test change. @nocommit
const passthroughSyntaxPlugins = require('../passthrough-syntax-plugins');
const lazyImports = require('./lazy-imports');
const EXCLUDED_FIRST_PARTY_PATHS = [
/[/\\]node_modules[/\\]/,
/[/\\]packages[/\\]react-native[/\\]/,
/[/\\]packages[/\\]react-native(?:-fantom)?[/\\]/,
/[/\\]packages[/\\]virtualized-lists[/\\]/,
/[/\\]private[/\\]react-native-fantom[/\\]/,
];
function isTypeScriptSource(fileName) {
+9 -9
View File
@@ -73,9 +73,9 @@ const ALL_GENERATORS = {
generateViewConfigJs: generateViewConfigJs.generate,
};
export type FilesOutput = Map<string, string>;
type FilesOutput = Map<string, string>;
export type GenerateFunction = (
type GenerateFunction = (
libraryName: string,
schema: SchemaType,
packageName?: string,
@@ -83,11 +83,11 @@ export type GenerateFunction = (
headerPrefix?: string,
) => FilesOutput;
export type LibraryGeneratorsFunctions = $ReadOnly<{
type LibraryGeneratorsFunctions = $ReadOnly<{
[string]: Array<GenerateFunction>,
}>;
export type LibraryOptions = $ReadOnly<{
type LibraryOptions = $ReadOnly<{
libraryName: string,
schema: SchemaType,
outputDirectory: string,
@@ -97,13 +97,13 @@ export type LibraryOptions = $ReadOnly<{
libraryGenerators?: LibraryGeneratorsFunctions,
}>;
export type SchemasOptions = $ReadOnly<{
type SchemasOptions = $ReadOnly<{
schemas: {[string]: SchemaType},
outputDirectory: string,
supportedApplePlatforms?: {[string]: {[string]: boolean}},
}>;
export type LibraryGenerators =
type LibraryGenerators =
| 'componentsAndroid'
| 'componentsIOS'
| 'descriptors'
@@ -116,14 +116,14 @@ export type LibraryGenerators =
| 'modulesCxx'
| 'modulesIOS';
export type SchemasGenerators = 'providerIOS';
type SchemasGenerators = 'providerIOS';
export type LibraryConfig = $ReadOnly<{
type LibraryConfig = $ReadOnly<{
generators: Array<LibraryGenerators>,
test?: boolean,
}>;
export type SchemasConfig = $ReadOnly<{
type SchemasConfig = $ReadOnly<{
generators: Array<SchemasGenerators>,
test?: boolean,
}>;
@@ -18,10 +18,7 @@ const flowFixtures = require('../../flow/modules/__test_fixtures__/fixtures.js')
const tsFixtures = require('../../typescript/modules/__test_fixtures__/fixtures.js');
const {compareSnaps, compareTsArraySnaps} = require('../compareSnaps.js');
const flowExtraCases = [
'NATIVE_MODULE_WITH_OPAQUE_TYPES',
'PROMISE_WITH_COMMONLY_USED_TYPES',
];
const flowExtraCases = ['PROMISE_WITH_COMMONLY_USED_TYPES'];
const tsExtraCases = [
'NATIVE_MODULE_WITH_ARRAY2_WITH_ALIAS',
'NATIVE_MODULE_WITH_ARRAY2_WITH_UNION_AND_TOUPLE',
@@ -691,37 +691,6 @@ export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const NATIVE_MODULE_WITH_OPAQUE_TYPES = `
/**
* 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 strict-local
* @format
*/
'use strict';
import type {TurboModule} from '../RCTExport';
import * as TurboModuleRegistry from '../TurboModuleRegistry';
export opaque type Task = mixed;
export opaque type TimeoutID = number;
export interface Spec extends TurboModule {
+createTask: (callback: () => void) => Task;
+cancelTask: (task: Task) => void;
+setTimeout: (callback: () => void) => TimeoutID;
+clearTimeout: (timeoutID: TimeoutID) => void;
}
export default TurboModuleRegistry.getEnforcing<Spec>('SampleTurboModule');
`;
const ANDROID_ONLY_NATIVE_MODULE = `
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
@@ -1007,7 +976,6 @@ module.exports = {
NATIVE_MODULE_WITH_UNION,
NATIVE_MODULE_WITH_UNION_RETURN_TYPES,
NATIVE_MODULE_WITH_EVENT_EMITTERS,
NATIVE_MODULE_WITH_OPAQUE_TYPES,
EMPTY_NATIVE_MODULE,
ANDROID_ONLY_NATIVE_MODULE,
IOS_ONLY_NATIVE_MODULE,
@@ -2187,108 +2187,6 @@ exports[`RN Codegen Flow Parser can generate fixture NATIVE_MODULE_WITH_OBJECT_W
}"
`;
exports[`RN Codegen Flow Parser can generate fixture NATIVE_MODULE_WITH_OPAQUE_TYPES 1`] = `
"{
'modules': {
'NativeSampleTurboModule': {
'type': 'NativeModule',
'aliasMap': {},
'enumMap': {},
'spec': {
'eventEmitters': [],
'methods': [
{
'name': 'createTask',
'optional': false,
'typeAnnotation': {
'type': 'FunctionTypeAnnotation',
'returnTypeAnnotation': {
'type': 'GenericObjectTypeAnnotation'
},
'params': [
{
'name': 'callback',
'optional': false,
'typeAnnotation': {
'type': 'FunctionTypeAnnotation',
'returnTypeAnnotation': {
'type': 'VoidTypeAnnotation'
},
'params': []
}
}
]
}
},
{
'name': 'cancelTask',
'optional': false,
'typeAnnotation': {
'type': 'FunctionTypeAnnotation',
'returnTypeAnnotation': {
'type': 'VoidTypeAnnotation'
},
'params': [
{
'name': 'task',
'optional': false,
'typeAnnotation': {
'type': 'GenericObjectTypeAnnotation'
}
}
]
}
},
{
'name': 'setTimeout',
'optional': false,
'typeAnnotation': {
'type': 'FunctionTypeAnnotation',
'returnTypeAnnotation': {
'type': 'NumberTypeAnnotation'
},
'params': [
{
'name': 'callback',
'optional': false,
'typeAnnotation': {
'type': 'FunctionTypeAnnotation',
'returnTypeAnnotation': {
'type': 'VoidTypeAnnotation'
},
'params': []
}
}
]
}
},
{
'name': 'clearTimeout',
'optional': false,
'typeAnnotation': {
'type': 'FunctionTypeAnnotation',
'returnTypeAnnotation': {
'type': 'VoidTypeAnnotation'
},
'params': [
{
'name': 'timeoutID',
'optional': false,
'typeAnnotation': {
'type': 'NumberTypeAnnotation'
}
}
]
}
}
]
},
'moduleName': 'SampleTurboModule'
}
}
}"
`;
exports[`RN Codegen Flow Parser can generate fixture NATIVE_MODULE_WITH_PARTIALS 1`] = `
"{
'modules': {
@@ -296,7 +296,6 @@ class FlowParser implements Parser {
if (
node.declaration != null &&
(node.declaration.type === 'TypeAlias' ||
node.declaration.type === 'OpaqueType' ||
node.declaration.type === 'InterfaceDeclaration')
) {
types[node.declaration.id.name] = node.declaration;
@@ -310,7 +309,6 @@ class FlowParser implements Parser {
types[node.declaration.id.name] = node.declaration;
} else if (
node.type === 'TypeAlias' ||
node.type === 'OpaqueType' ||
node.type === 'InterfaceDeclaration' ||
node.type === 'EnumDeclaration'
) {
@@ -545,10 +543,6 @@ class FlowParser implements Parser {
}
nextNodeForTypeAlias(typeAnnotation: $FlowFixMe): $FlowFixMe {
if (typeAnnotation.type === 'OpaqueType') {
return typeAnnotation.impltype;
}
return typeAnnotation.right;
}
@@ -1233,8 +1233,7 @@ function handleGenericTypeAnnotation(
let node;
switch (resolvedTypeAnnotation.type) {
case parser.typeAlias:
case 'OpaqueType': {
case parser.typeAlias: {
typeResolutionStatus = getTypeResolutionStatus(
'alias',
typeAnnotation,
@@ -24,7 +24,7 @@
"@react-native/codegen": "0.80.0-main"
},
"peerDependencies": {
"@types/react": "^19.1.0",
"@types/react": "^19.0.0",
"react": "*",
"react-native": "*"
},
@@ -4,7 +4,6 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @fantom_flags cxxNativeAnimatedRemoveJsSync:*
* @flow strict-local
* @format
*/
@@ -17,7 +16,6 @@ import ensureInstance from '../../../src/private/__tests__/utilities/ensureInsta
import * as Fantom from '@react-native/fantom';
import {createRef} from 'react';
import {Animated, View, useAnimatedValue} from 'react-native';
import * as ReactNativeFeatureFlags from 'react-native/src/private/featureflags/ReactNativeFeatureFlags';
import ReactNativeElement from 'react-native/src/private/webapis/dom/nodes/ReactNativeElement';
test('moving box by 100 points', () => {
@@ -34,9 +32,11 @@ test('moving box by 100 points', () => {
{
width: 100,
height: 100,
backgroundColor: 'red',
},
{transform: [{translateX}]},
]}
testID="box"
/>
);
}
@@ -75,15 +75,11 @@ test('moving box by 100 points', () => {
Fantom.unstable_produceFramesForDuration(500);
// Animation is completed now. C++ Animated will commit the final position to the shadow tree.
if (ReactNativeFeatureFlags.cxxNativeAnimatedRemoveJsSync()) {
expect(viewElement.getBoundingClientRect().x).toBe(100);
// TODO(T223344928): this shouldn't be neccessary
Fantom.runWorkLoop();
} else {
expect(viewElement.getBoundingClientRect().x).toBe(0);
Fantom.runWorkLoop(); // Animated still schedules a React state update for synchronisation to shadow tree
}
expect(viewElement.getBoundingClientRect().x).toBe(100);
// TODO: this shouldn't be needed but C++ Animated still schedules a React state update
// for synchronisation, even though it doesn't need to.
Fantom.runWorkLoop();
expect(viewElement.getBoundingClientRect().x).toBe(100);
});
@@ -147,331 +143,6 @@ test('animation driven by onScroll event', () => {
expect(transform.translateY).toBeCloseTo(100, 0.001);
expect(viewElement.getBoundingClientRect().y).toBe(100);
});
test('animated opacity', () => {
let _opacity;
const viewRef = createRef<HostInstance>();
function MyApp() {
const opacity = useAnimatedValue(1);
_opacity = opacity;
return (
<Animated.View
ref={viewRef}
style={[
{
width: 100,
height: 100,
opacity: opacity,
},
]}
/>
);
}
const root = Fantom.createRoot();
Fantom.runTask(() => {
root.render(<MyApp />);
});
const viewElement = ensureInstance(viewRef.current, ReactNativeElement);
expect(viewElement.getBoundingClientRect().x).toBe(0);
Fantom.runTask(() => {
Animated.timing(_opacity, {
toValue: 0,
duration: 30,
useNativeDriver: true,
}).start();
});
Fantom.unstable_produceFramesForDuration(30);
expect(Fantom.unstable_getDirectManipulationProps(viewElement).opacity).toBe(
0,
);
// TODO(T223344928): this shouldn't be neccessary with cxxNativeAnimatedRemoveJsSync:true
Fantom.runWorkLoop();
expect(root.getRenderedOutput({props: ['opacity']}).toJSX()).toEqual(
<rn-view opacity="0" />,
);
});
test('moving box by 50 points with offset 10', () => {
let _translateX;
const viewRef = createRef<HostInstance>();
function MyApp() {
const translateX = useAnimatedValue(0);
_translateX = translateX;
return (
<Animated.View
ref={viewRef}
style={[
{
width: 100,
height: 100,
},
{transform: [{translateX}]},
]}
/>
);
}
const root = Fantom.createRoot();
Fantom.runTask(() => {
root.render(<MyApp />);
});
const viewElement = ensureInstance(viewRef.current, ReactNativeElement);
expect(viewElement.getBoundingClientRect().x).toBe(0);
let finishValue = null;
Fantom.runTask(() => {
Animated.timing(_translateX, {
toValue: 50,
duration: 1000, // 1 second
useNativeDriver: true,
}).start(result => {
finishValue = result;
});
});
Fantom.runTask(() => {
_translateX.setOffset(10);
});
Fantom.unstable_produceFramesForDuration(500);
// shadow tree is not synchronised yet, position X is still 0.
expect(viewElement.getBoundingClientRect().x).toBe(0);
expect(
// $FlowFixMe[incompatible-use]
Fantom.unstable_getDirectManipulationProps(viewElement).transform[0]
.translateX,
).toBeCloseTo(35, 0.001);
Fantom.unstable_produceFramesForDuration(500);
expect(
// $FlowFixMe[incompatible-use]
Fantom.unstable_getDirectManipulationProps(viewElement).transform[0]
.translateX,
).toBeCloseTo(60, 0.001);
if (ReactNativeFeatureFlags.cxxNativeAnimatedRemoveJsSync()) {
expect(root.getRenderedOutput({props: ['transform']}).toJSX()).toEqual(
<rn-view transform='[{"translateX": 60.000000}]' />,
);
// TODO(T223344928): this shouldn't be neccessary
Fantom.runWorkLoop();
} else {
expect(root.getRenderedOutput({props: ['transform']}).toJSX()).toEqual(
<rn-view transform="[]" />,
);
Fantom.runWorkLoop(); // Animated still schedules a React state update for synchronisation to shadow tree
}
expect(root.getRenderedOutput({props: ['transform']}).toJSX()).toEqual(
<rn-view transform='[{"translateX": 60.000000}]' />, // // must include offset.
);
expect(finishValue?.finished).toBe(true);
expect(finishValue?.value).toBe(50); // must not include offset.
expect(finishValue?.offset).toBe(10);
});
describe('Value.flattenOffset', () => {
it('accumulates offset with onScroll value', () => {
const scrollViewRef = createRef<HostInstance>();
const viewRef = createRef<HostInstance>();
let _onScroll;
function PressableWithNativeDriver() {
_onScroll = useAnimatedValue(0);
return (
<View style={{flex: 1}}>
<Animated.View
ref={viewRef}
style={{
position: 'absolute',
width: 10,
height: 10,
transform: [{translateY: _onScroll}],
}}
/>
<Animated.ScrollView
ref={scrollViewRef}
onScroll={Animated.event(
[
{
nativeEvent: {
contentOffset: {
y: _onScroll,
},
},
},
],
{useNativeDriver: true},
)}>
<View style={{height: 1000, width: 100}} />
</Animated.ScrollView>
</View>
);
}
const root = Fantom.createRoot();
Fantom.runTask(() => {
root.render(<PressableWithNativeDriver />);
});
const fn = jest.fn();
Fantom.runTask(() => {
_onScroll.addListener(fn);
});
const scrollViewelement = ensureInstance(
scrollViewRef.current,
ReactNativeElement,
);
const viewElement = ensureInstance(viewRef.current, ReactNativeElement);
Fantom.scrollTo(scrollViewelement, {
x: 0,
y: 10,
});
expect(fn).toBeCalledWith({value: 10});
Fantom.runTask(() => {
_onScroll.setOffset(15);
_onScroll.flattenOffset();
});
expect(fn).toHaveBeenCalledTimes(1);
Fantom.runTask(() => {
_onScroll.setOffset(15);
});
expect(fn).toHaveBeenCalledTimes(1);
let transform =
// $FlowFixMe[incompatible-use]
Fantom.unstable_getDirectManipulationProps(viewElement).transform[0];
expect(transform.translateY).toBeCloseTo(40, 0.001);
// TODO(T223344928): this shouldn't be neccessary with cxxNativeAnimatedRemoveJsSync:true
Fantom.runWorkLoop();
});
});
describe('Value.extractOffset', () => {
it('sets the offset value to the base value and resets the base value to zero', () => {
const scrollViewRef = createRef<HostInstance>();
const viewRef = createRef<HostInstance>();
let _onScroll;
function PressableWithNativeDriver() {
_onScroll = useAnimatedValue(0);
return (
<View style={{flex: 1}}>
<Animated.View
ref={viewRef}
style={{
position: 'absolute',
width: 10,
height: 10,
transform: [{translateY: _onScroll}],
}}
/>
<Animated.ScrollView
ref={scrollViewRef}
onScroll={Animated.event(
[
{
nativeEvent: {
contentOffset: {
y: _onScroll,
},
},
},
],
{useNativeDriver: true},
)}>
<View style={{height: 1000, width: 100}} />
</Animated.ScrollView>
</View>
);
}
const root = Fantom.createRoot();
Fantom.runTask(() => {
root.render(<PressableWithNativeDriver />);
});
const fn = jest.fn();
Fantom.runTask(() => {
_onScroll.addListener(fn);
});
const scrollViewelement = ensureInstance(
scrollViewRef.current,
ReactNativeElement,
);
const viewElement = ensureInstance(viewRef.current, ReactNativeElement);
Fantom.scrollTo(scrollViewelement, {
x: 0,
y: 10,
});
expect(fn).toBeCalledWith({value: 10});
Fantom.runTask(() => {
_onScroll.setOffset(15);
// Sets offset to be 15 + 10 = 25 (this is not observable from JS).
_onScroll.extractOffset();
});
expect(fn).toHaveBeenCalledTimes(1);
let transform =
// $FlowFixMe[incompatible-use]
Fantom.unstable_getDirectManipulationProps(viewElement).transform[0];
// Animated value is now 0 but offset is 25. The final value is 25.
expect(transform.translateY).toBeCloseTo(25, 0.001);
Fantom.runTask(() => {
// Sets offset 35, overriding the previous `setOffset`.
// Due to `extractOffset`, base value was restarted to 0.
_onScroll.setOffset(35);
});
expect(fn).toHaveBeenCalledTimes(1);
transform =
// $FlowFixMe[incompatible-use]
Fantom.unstable_getDirectManipulationProps(viewElement).transform[0];
// `extractOffset` resets value back to 0.
// Previously we set offset to 35. The final value is 35.
expect(transform.translateY).toBeCloseTo(35, 0.001);
// TODO(T223344928): this shouldn't be neccessary with cxxNativeAnimatedRemoveJsSync:true
Fantom.runWorkLoop();
});
// TODO(T226364699): this should `toBe(100)` but we are not syncing shadow tree yet.
expect(viewElement.getBoundingClientRect().y).toBe(0);
});
@@ -13,7 +13,6 @@ import type AnimatedNode from '../nodes/AnimatedNode';
import type AnimatedValue from '../nodes/AnimatedValue';
import NativeAnimatedHelper from '../../../src/private/animated/NativeAnimatedHelper';
import * as ReactNativeFeatureFlags from '../../../src/private/featureflags/ReactNativeFeatureFlags';
import AnimatedProps from '../nodes/AnimatedProps';
export type EndResult = {
@@ -150,15 +149,8 @@ export default class Animation {
if (value != null) {
animatedValue.__onAnimatedValueUpdateReceived(value, offset);
if (
!(
ReactNativeFeatureFlags.cxxNativeAnimatedEnabled() &&
ReactNativeFeatureFlags.cxxNativeAnimatedRemoveJsSync()
)
) {
if (this.__isLooping === true) {
return;
}
if (this.__isLooping === true) {
return;
}
// Once the JS side node is synced with the updated values, trigger an
@@ -14,8 +14,12 @@
#import <memory>
#if USE_THIRD_PARTY_JSC != 1
#if __has_include(<jsireact/HermesExecutorFactory.h>)
#import <jsireact/HermesExecutorFactory.h>
#elif __has_include(<reacthermes/HermesExecutorFactory.h>)
#import <reacthermes/HermesExecutorFactory.h>
#endif
#endif
#import <ReactCommon/RCTTurboModuleManager.h>
#import <jsireact/JSIExecutor.h>
-1
View File
@@ -63,7 +63,6 @@ export type ButtonProps = $ReadOnly<{
@platform tv
@default false
@deprecated Use `focusable` instead
*/
hasTVPreferredFocus?: ?boolean,
@@ -13,21 +13,12 @@ import {NativeMethods} from '../../../types/public/ReactNativeTypes';
import {ViewProps} from '../View/ViewPropTypes';
/**
* @deprecated
* Use `react-native-safe-area-context` instead. This component is deprecated and will be removed in a future release.
*
* Renders nested content and automatically applies paddings reflect the portion of the view
* that is not covered by navigation bars, tab bars, toolbars, and other ancestor views.
* Moreover, and most importantly, Safe Area's paddings reflect physical limitation of the screen,
* such as rounded corners or camera notches (aka sensor housing area on iPhone X).
*/
declare class SafeAreaViewComponent extends React.Component<ViewProps> {}
declare const SafeAreaViewBase: Constructor<NativeMethods> &
typeof SafeAreaViewComponent;
/**
* @deprecated
* Use `react-native-safe-area-context` instead. This component is deprecated and will be removed in a future release.
*/
export class SafeAreaView extends SafeAreaViewBase {}
@@ -22,7 +22,6 @@ import * as React from 'react';
* Moreover, and most importantly, Safe Area's paddings reflect physical
* limitation of the screen, such as rounded corners or camera notches (aka
* sensor housing area on iPhone X).
* @deprecated Use `react-native-safe-area-context` instead. This component will be removed in a future release.
*/
const exported: component(
ref?: React.RefSetter<React.ElementRef<typeof View>>,
@@ -35,8 +35,6 @@ export interface StatusBarPropsAndroid {
/**
* The background color of the status bar.
*
* Please note that this prop has no effect on Android 15+
*
* @platform android
*/
backgroundColor?: ColorValue | undefined;
@@ -46,8 +44,6 @@ export interface StatusBarPropsAndroid {
* the app will draw under the status bar. This is useful when using a
* semi transparent status bar color.
*
* Please note that this prop has no effect on Android 15+
*
* @platform android
*/
translucent?: boolean | undefined;
@@ -58,9 +58,6 @@ export type StatusBarAnimation = $Keys<{
export type StatusBarPropsAndroid = $ReadOnly<{
/**
* The background color of the status bar.
*
* Please note that this prop has no effect on Android 15+
*
* @platform android
*/
backgroundColor?: ?ColorValue,
@@ -69,8 +66,6 @@ export type StatusBarPropsAndroid = $ReadOnly<{
* When translucent is set to true, the app will draw under the status bar.
* This is useful when using a semi transparent status bar color.
*
* Please note that this prop has no effect on Android 15+
*
* @platform android
*/
translucent?: ?boolean,
@@ -30,9 +30,6 @@ type AndroidProps = $ReadOnly<{
}>;
type IOSProps = $ReadOnly<{
/**
* @deprecated Use `focusable` instead
*/
hasTVPreferredFocus?: ?boolean,
}>;
@@ -29,7 +29,6 @@ type TVProps = {
* *(Apple TV only)* TV preferred focus (see documentation for the View component).
*
* @platform ios
* @deprecated Use `focusable` instead
*/
hasTVPreferredFocus?: ?boolean,
@@ -16,7 +16,6 @@ export interface TVProps {
* *(Apple TV only)* TV preferred focus (see documentation for the View component).
*
* @platform ios
* @deprecated Use `focusable` instead
*/
hasTVPreferredFocus?: boolean | undefined;
@@ -26,7 +26,6 @@ export type TVProps = $ReadOnly<{
* *(Apple TV only)* TV preferred focus (see documentation for the View component).
*
* @platform ios
* @deprecated Use `focusable` instead
*/
hasTVPreferredFocus?: ?boolean,
+13 -12
View File
@@ -16,6 +16,11 @@ import ViewNativeComponent from './ViewNativeComponent';
import * as React from 'react';
import {use} from 'react';
type PropsWithRef = $ReadOnly<{
ref?: React.RefSetter<React.ElementRef<typeof ViewNativeComponent>>,
...ViewProps,
}>;
/**
* The most fundamental component for building a UI, View is a container that
* supports layout with flexbox, style, some touch handling, and accessibility
@@ -23,10 +28,7 @@ import {use} from 'react';
*
* @see https://reactnative.dev/docs/view
*/
export default component View(
ref?: React.RefSetter<React.ElementRef<typeof ViewNativeComponent>>,
...props: ViewProps
) {
function View(props: PropsWithRef): React.Node {
const hasTextAncestor = use(TextAncestor);
let actualView;
@@ -53,7 +55,7 @@ export default component View(
} = props;
// Since we destructured props, we can now treat it as mutable
const processedProps = otherProps as {...ViewProps};
const processedProps = otherProps as {...PropsWithRef};
const parsedAriaLabelledBy = ariaLabelledBy?.split(/\s*,\s*/g);
if (parsedAriaLabelledBy !== undefined) {
@@ -116,12 +118,7 @@ export default component View(
};
}
actualView =
ref == null ? (
<ViewNativeComponent {...processedProps} />
) : (
<ViewNativeComponent {...processedProps} ref={ref} />
);
actualView = <ViewNativeComponent {...processedProps} />;
} else {
const {
accessibilityElementsHidden,
@@ -201,7 +198,6 @@ export default component View(
: importantForAccessibility
}
nativeID={id ?? nativeID}
ref={ref}
/>
);
}
@@ -211,3 +207,8 @@ export default component View(
}
return actualView;
}
export default View as component(
ref?: React.RefSetter<React.ElementRef<typeof ViewNativeComponent>>,
...props: ViewProps
);
@@ -374,6 +374,13 @@ export type AccessibilityProps = $ReadOnly<{
*/
'aria-label'?: ?Stringish,
/**
* Defines the order in which descendant elements receive accessibility focus.
* The elements in the array represent nativeID values for the respective
* descendant elements.
*/
experimental_accessibilityOrder?: ?Array<string>,
/**
* Indicates to accessibility services to treat UI component like a specific role.
*/
@@ -21,9 +21,6 @@ import {
import {Touchable} from '../Touchable/Touchable';
import {AccessibilityProps} from './ViewAccessibility';
/**
* @deprecated These properties are not implemented natively.
*/
export interface TVViewPropsIOS {
/**
* *(Apple TV only)* When set to true, this view will be focusable
@@ -37,7 +34,6 @@ export interface TVViewPropsIOS {
* *(Apple TV only)* May be set to true to force the Apple TV focus engine to move focus to this view.
*
* @platform ios
* @deprecated Use `focusable` instead
*/
hasTVPreferredFocus?: boolean | undefined;
@@ -280,7 +280,6 @@ export type ViewPropsAndroid = $ReadOnly<{
* Whether to force the Android TV focus engine to move focus to this view.
*
* @platform android
* @deprecated Use `focusable` instead
*/
hasTVPreferredFocus?: ?boolean,
@@ -490,13 +489,6 @@ type ViewBaseProps = $ReadOnly<{
* See https://reactnative.dev/docs/view#removeclippedsubviews
*/
removeClippedSubviews?: ?boolean,
/**
* Defines the order in which descendant elements receive accessibility focus.
* The elements in the array represent nativeID values for the respective
* descendant elements.
*/
experimental_accessibilityOrder?: ?Array<string>,
}>;
export type ViewProps = $ReadOnly<{
@@ -169,20 +169,6 @@ describe('<View>', () => {
});
});
describe('transform style', () => {
it('causes view to be unflattened', () => {
const root = Fantom.createRoot();
Fantom.runTask(() => {
root.render(<View style={{transform: [{translateX: 10}]}} />);
});
expect(root.getRenderedOutput({props: ['transform']}).toJSX()).toEqual(
<rn-view transform='[{"translateX": 10.000000}]' />,
);
});
});
describe('props', () => {
describe('pointerEvents', () => {
it('auto does not propagate to the mounting layer, it is the default', () => {
@@ -31,8 +31,8 @@ describe('<Button>', () => {
.toJSX(),
).toEqual(
// Upper case on Android (also used by Fantom)
<rn-view backgroundColor="rgba(33, 150, 243, 1)">
<rn-paragraph foregroundColor="rgba(255, 255, 255, 1)">
<rn-view backgroundColor="rgba(33, 150, 243, 255)">
<rn-paragraph foregroundColor="rgba(255, 255, 255, 255)">
HELLO
</rn-paragraph>
</rn-view>,
@@ -53,8 +53,8 @@ describe('<Button>', () => {
.getRenderedOutput({props: ['foregroundColor', 'backgroundColor']})
.toJSX(),
).toEqual(
<rn-view backgroundColor="rgba(0, 0, 255, 1)">
<rn-paragraph foregroundColor="rgba(255, 255, 255, 1)">
<rn-view backgroundColor="rgba(0, 0, 255, 255)">
<rn-paragraph foregroundColor="rgba(255, 255, 255, 255)">
HELLO
</rn-paragraph>
</rn-view>,
@@ -99,8 +99,8 @@ describe('<Button>', () => {
.getRenderedOutput({props: ['foregroundColor', 'backgroundColor']})
.toJSX(),
).toEqual(
<rn-view backgroundColor="rgba(223, 223, 223, 1)">
<rn-paragraph foregroundColor="rgba(161, 161, 161, 1)">
<rn-view backgroundColor="rgba(223, 223, 223, 255)">
<rn-paragraph foregroundColor="rgba(161, 161, 161, 255)">
HELLO
</rn-paragraph>
</rn-view>,
@@ -181,8 +181,8 @@ describe('<Button>', () => {
.getRenderedOutput({props: ['foregroundColor', 'backgroundColor']})
.toJSX(),
).toEqual(
<rn-view backgroundColor="rgba(33, 150, 243, 1)">
<rn-paragraph foregroundColor="rgba(255, 255, 255, 1)">
<rn-view backgroundColor="rgba(33, 150, 243, 255)">
<rn-paragraph foregroundColor="rgba(255, 255, 255, 255)">
HELLO
</rn-paragraph>
</rn-view>,
@@ -223,8 +223,8 @@ describe('<Button>', () => {
.getRenderedOutput({props: ['foregroundColor', 'backgroundColor']})
.toJSX(),
).toEqual(
<rn-view backgroundColor="rgba(223, 223, 223, 1)">
<rn-paragraph foregroundColor="rgba(161, 161, 161, 1)">
<rn-view backgroundColor="rgba(223, 223, 223, 255)">
<rn-paragraph foregroundColor="rgba(161, 161, 161, 255)">
HELLO
</rn-paragraph>
</rn-view>,
+2 -8
View File
@@ -35,9 +35,9 @@ export interface ModalBaseProps {
*/
visible?: boolean | undefined;
/**
* The `onRequestClose` callback is called when the user taps the hardware back button on Android, dismisses the sheet using a gesture on iOS (when `allowSwipeDismissal` is set to true) or the menu button on Apple TV.
* The `onRequestClose` callback is called when the user taps the hardware back button on Android or the menu button on Apple TV.
*
* This is required on iOS and Android.
* This is required on Apple TV and Android.
*/
onRequestClose?: ((event: NativeSyntheticEvent<any>) => void) | undefined;
/**
@@ -89,12 +89,6 @@ export interface ModalPropsIOS {
onOrientationChange?:
| ((event: NativeSyntheticEvent<any>) => void)
| undefined;
/**
* Controls whether the modal can be dismissed by swiping down on iOS.
* This requires you to implement the `onRequestClose` prop to handle the dismissal.
*/
allowSwipeDismissal?: boolean | undefined;
}
export interface ModalPropsAndroid {
+2 -19
View File
@@ -86,9 +86,9 @@ export type ModalBaseProps = {
*/
visible?: ?boolean,
/**
* The `onRequestClose` callback is called when the user taps the hardware back button on Android, dismisses the sheet using a gesture on iOS (when `allowSwipeDismissal` is set to true) or the menu button on Apple TV.
* The `onRequestClose` callback is called when the user taps the hardware back button on Android or the menu button on Apple TV.
*
* This is required on iOS and Android.
* This is required on Apple TV and Android.
*/
// onRequestClose?: (event: NativeSyntheticEvent<any>) => void;
onRequestClose?: ?DirectEventHandler<null>,
@@ -147,12 +147,6 @@ export type ModalPropsIOS = {
// | ((event: NativeSyntheticEvent<any>) => void)
// | undefined;
onOrientationChange?: ?DirectEventHandler<OrientationChangeEvent>,
/**
* Controls whether the modal can be dismissed by swiping down on iOS.
* This requires you to implement the `onRequestClose` prop to handle the dismissal.
*/
allowSwipeDismissal?: ?boolean,
};
export type ModalPropsAndroid = {
@@ -198,16 +192,6 @@ function confirmProps(props: ModalProps) {
'Modal with translucent navigation bar and without translucent status bar is not supported.',
);
}
if (
Platform.OS === 'ios' &&
props.allowSwipeDismissal === true &&
!props.onRequestClose
) {
console.warn(
'Modal requires the onRequestClose prop when used with `allowSwipeDismissal`. This is necessary to prevent state corruption.',
);
}
}
}
@@ -343,7 +327,6 @@ class Modal extends React.Component<ModalProps, ModalState> {
onStartShouldSetResponder={this._shouldSetResponder}
supportedOrientations={this.props.supportedOrientations}
onOrientationChange={this.props.onOrientationChange}
allowSwipeDismissal={this.props.allowSwipeDismissal}
testID={this.props.testID}>
<VirtualizedListContextResetter>
<ScrollView.Context.Provider value={null}>
@@ -8,7 +8,7 @@
* @format
*/
import type {PartialAttributeConfiguration as AttributeConfiguration} from '../../Renderer/shims/ReactNativeTypes';
import type {AttributeConfiguration} from '../../Renderer/shims/ReactNativeTypes';
import flattenStyle from '../../StyleSheet/flattenStyle';
import deepDiffer from '../../Utilities/differ/deepDiffer';
@@ -61,7 +61,7 @@ function restoreDeletedValuesInNestedArray(
} else if (node && removedKeyCount > 0) {
const obj = node;
for (const propKey in removedKeys) {
// $FlowFixMe[incompatible-use] removedKeys is always non-null
// $FlowFixMe[incompatible-use] found when upgrading Flow
if (!removedKeys[propKey]) {
continue;
}
@@ -133,12 +133,12 @@ function diffNestedArrayProperty(
);
}
for (; i < nextArray.length; i++) {
// Add all remaining properties
const nextProp = nextArray[i];
if (!nextProp) {
continue;
}
updatePayload = addNestedProperty(updatePayload, nextProp, validAttributes);
// Add all remaining properties.
updatePayload = addNestedProperty(
updatePayload,
nextArray[i],
validAttributes,
);
}
return updatePayload;
}
@@ -183,7 +183,9 @@ function diffNestedProperty(
if (Array.isArray(prevProp)) {
return diffProperties(
updatePayload,
// $FlowFixMe - We know that this is always an object when the input is.
flattenStyle(prevProp),
// $FlowFixMe - We know that this isn't an array because of above flow.
nextProp,
validAttributes,
);
@@ -192,11 +194,43 @@ function diffNestedProperty(
return diffProperties(
updatePayload,
prevProp,
// $FlowFixMe - We know that this is always an object when the input is.
flattenStyle(nextProp),
validAttributes,
);
}
/**
* addNestedProperty takes a single set of props and valid attribute
* attribute configurations. It processes each prop and adds it to the
* updatePayload.
*/
function addNestedProperty(
updatePayload: null | Object,
nextProp: NestedNode,
validAttributes: AttributeConfiguration,
): $FlowFixMe {
if (!nextProp) {
return updatePayload;
}
if (!Array.isArray(nextProp)) {
// Add each property of the leaf.
return addProperties(updatePayload, nextProp, validAttributes);
}
for (let i = 0; i < nextProp.length; i++) {
// Add all the properties of the array.
updatePayload = addNestedProperty(
updatePayload,
nextProp[i],
validAttributes,
);
}
return updatePayload;
}
/**
* clearNestedProperty takes a single set of props and valid attributes. It
* adds a null sentinel to the updatePayload, for each prop key.
@@ -251,19 +285,14 @@ function diffProperties(
prevProp = prevProps[propKey];
nextProp = nextProps[propKey];
// functions are converted to booleans as markers that the associated
// events should be sent from native.
if (typeof nextProp === 'function') {
const attributeConfigHasProcess =
typeof attributeConfig === 'object' &&
typeof attributeConfig.process === 'function';
if (!attributeConfigHasProcess) {
// functions are converted to booleans as markers that the associated
// events should be sent from native.
nextProp = (true: any);
// If nextProp is not a function, then don't bother changing prevProp
// since nextProp will win and go into the updatePayload regardless.
if (typeof prevProp === 'function') {
prevProp = (true: any);
}
nextProp = (true: any);
// If nextProp is not a function, then don't bother changing prevProp
// since nextProp will win and go into the updatePayload regardless.
if (typeof prevProp === 'function') {
prevProp = (true: any);
}
}
@@ -413,69 +442,16 @@ function diffProperties(
return updatePayload;
}
function addNestedProperty(
payload: null | Object,
/**
* addProperties adds all the valid props to the payload after being processed.
*/
function addProperties(
updatePayload: null | Object,
props: Object,
validAttributes: AttributeConfiguration,
): null | Object {
// Flatten nested style props.
if (Array.isArray(props)) {
for (let i = 0; i < props.length; i++) {
payload = addNestedProperty(payload, props[i], validAttributes);
}
return payload;
}
for (const propKey in props) {
const prop = props[propKey];
const attributeConfig = ((validAttributes[
propKey
]: any): AttributeConfiguration);
if (attributeConfig == null) {
continue;
}
let newValue;
if (prop === undefined) {
// Discard the prop if it was previously defined.
if (payload && payload[propKey] !== undefined) {
newValue = null;
} else {
continue;
}
} else if (typeof attributeConfig === 'object') {
if (typeof attributeConfig.process === 'function') {
// An atomic prop with custom processing.
newValue = attributeConfig.process(prop);
} else if (typeof attributeConfig.diff === 'function') {
// An atomic prop with custom diffing. We don't need to do diffing when adding props.
newValue = prop;
}
} else {
if (typeof prop === 'function') {
// A function prop. It represents an event handler. Pass it to native as 'true'.
newValue = true;
} else {
// An atomic prop. Doesn't need to be flattened.
newValue = prop;
}
}
if (newValue !== undefined) {
if (!payload) {
payload = ({}: {[string]: $FlowFixMe});
}
payload[propKey] = newValue;
continue;
}
payload = addNestedProperty(payload, prop, attributeConfig);
}
return payload;
// TODO: Fast path
return diffProperties(updatePayload, emptyObject, props, validAttributes);
}
/**
@@ -487,6 +463,7 @@ function clearProperties(
prevProps: Object,
validAttributes: AttributeConfiguration,
): null | Object {
// TODO: Fast path
return diffProperties(updatePayload, prevProps, emptyObject, validAttributes);
}
@@ -494,7 +471,11 @@ export function create(
props: Object,
validAttributes: AttributeConfiguration,
): null | Object {
return addNestedProperty(null, props, validAttributes);
return addProperties(
null, // updatePayload
props,
validAttributes,
);
}
export function diff(
@@ -1,495 +0,0 @@
/**
* 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
* @format
*/
'use strict';
import type {PartialAttributeConfiguration as AttributeConfiguration} from '../../../Renderer/shims/ReactNativeTypes';
const {create, diff} = require('../ReactNativeAttributePayload');
describe('ReactNativeAttributePayload.create', () => {
it('should work with simple example', () => {
expect(create({b: 2, c: 3}, {a: true, b: true})).toEqual({
b: 2,
});
});
it('should work with complex example', () => {
const validAttributes: AttributeConfiguration = {
style: {
position: true,
zIndex: true,
flexGrow: true,
flexShrink: true,
flexDirection: true,
overflow: true,
backgroundColor: true,
},
};
expect(
create(
{
style: [
{
flexGrow: 1,
flexShrink: 1,
flexDirection: 'row',
overflow: 'scroll',
},
[
{position: 'relative', zIndex: 2},
{flexGrow: 0},
{backgroundColor: 'red'},
],
],
},
validAttributes,
),
).toEqual({
flexGrow: 0,
flexShrink: 1,
flexDirection: 'row',
overflow: 'scroll',
position: 'relative',
zIndex: 2,
backgroundColor: 'red',
});
});
it('should nullify previously defined style prop that is subsequently set to null or undefined', () => {
expect(
create({style: [{a: 0}, {a: undefined}]}, {style: {a: true}}),
).toEqual({a: null});
expect(create({style: [{a: 0}, {a: null}]}, {style: {a: true}})).toEqual({
a: null,
});
});
it('should ignore non-style fields that are set to undefined', () => {
expect(create({}, {a: true})).toEqual(null);
expect(create({a: undefined}, {a: true})).toEqual(null);
expect(create({a: undefined, b: undefined}, {a: true, b: true})).toEqual(
null,
);
expect(
create({a: undefined, b: undefined, c: 1}, {a: true, b: true}),
).toEqual(null);
expect(
create({a: undefined, b: undefined, c: 1}, {a: true, b: true, c: true}),
).toEqual({c: 1});
expect(
create({a: 1, b: undefined, c: 2}, {a: true, b: true, c: true}),
).toEqual({a: 1, c: 2});
});
it('should ignore invalid fields', () => {
expect(create({b: 2}, {})).toEqual(null);
});
it('should not use the diff attribute', () => {
const diffA = jest.fn();
expect(create({a: [2]}, {a: {diff: diffA}})).toEqual({a: [2]});
expect(diffA).not.toBeCalled();
});
it('should use the process attribute', () => {
const processA = jest.fn(a => a + 1);
expect(create({a: 2}, {a: {process: processA}})).toEqual({a: 3});
expect(processA).toBeCalledWith(2);
});
it('should use the process attribute for functions as well', () => {
const process = (x: Object) => x;
const nextFunction = () => {};
expect(create({a: nextFunction}, {a: {process}})).toEqual({
a: nextFunction,
});
});
it('should work with undefined styles', () => {
expect(create({style: undefined}, {style: {b: true}})).toEqual(null);
expect(create({style: {a: '#ffffff', b: 1}}, {style: {b: true}})).toEqual({
b: 1,
});
});
it('should flatten nested styles and predefined styles', () => {
const validStyleAttribute: AttributeConfiguration = {
style: {foo: true, bar: true},
};
expect(create({style: [{foo: 1}, {bar: 2}]}, validStyleAttribute)).toEqual({
foo: 1,
bar: 2,
});
expect(create({}, validStyleAttribute)).toEqual(null);
const barStyle = {
bar: 3,
};
expect(
create({style: [[{foo: 1}, {foo: 2}], barStyle]}, validStyleAttribute),
).toEqual({foo: 2, bar: 3});
});
it('should not flatten nested props if attribute config is a primitive or only has diff/process', () => {
expect(create({a: {foo: 1, bar: 2}}, {a: true})).toEqual({
a: {foo: 1, bar: 2},
});
expect(create({a: [{foo: 1}, {bar: 2}]}, {a: true})).toEqual({
a: [{foo: 1}, {bar: 2}],
});
expect(create({a: {foo: 1, bar: 2}}, {a: {diff: a => a}})).toEqual({
a: {foo: 1, bar: 2},
});
expect(
create({a: [{foo: 1}, {bar: 2}]}, {a: {diff: a => a, process: a => a}}),
).toEqual({a: [{foo: 1}, {bar: 2}]});
});
it('handles attributes defined multiple times', () => {
const validAttributes: AttributeConfiguration = {
foo: true,
style: {foo: true},
};
expect(create({foo: 4, style: {foo: 2}}, validAttributes)).toEqual({
foo: 2,
});
expect(create({style: {foo: 2}}, validAttributes)).toEqual({
foo: 2,
});
expect(create({style: {foo: 2}, foo: 4}, validAttributes)).toEqual({
foo: 4,
});
expect(create({foo: 4, style: {foo: null}}, validAttributes)).toEqual({
foo: null, // this should ideally be null.
});
expect(
create({foo: 4, style: [{foo: null}, {foo: 5}]}, validAttributes),
).toEqual({
foo: 5,
});
});
// Function properties are just markers to native that events should be sent.
it('should convert functions to booleans', () => {
expect(
create(
{
a: function () {
return 9;
},
b: function () {
return 3;
},
},
{a: true, b: true},
),
).toEqual({a: true, b: true});
});
});
describe('ReactNativeAttributePayload.diff', () => {
it('should work with simple example', () => {
expect(diff({a: 1, c: 3}, {b: 2, c: 3}, {a: true, b: true})).toEqual({
a: null,
b: 2,
});
});
it('should skip fields that are equal', () => {
expect(
diff(
{a: 1, b: 'two', c: true, d: false, e: undefined, f: 0},
{a: 1, b: 'two', c: true, d: false, e: undefined, f: 0},
{a: true, b: true, c: true, d: true, e: true, f: true},
),
).toEqual(null);
});
it('should remove fields', () => {
expect(diff({a: 1}, {}, {a: true})).toEqual({a: null});
});
it('should remove fields that are set to undefined', () => {
expect(diff({a: 1}, {a: undefined}, {a: true})).toEqual({a: null});
});
it('should ignore invalid fields', () => {
expect(diff({a: 1}, {b: 2}, {})).toEqual(null);
});
it('should use the diff attribute', () => {
const diffA = jest.fn((a, b) => true);
const diffB = jest.fn((a, b) => false);
expect(
diff(
{a: [1], b: [3]},
{a: [2], b: [4]},
{a: {diff: diffA}, b: {diff: diffB}},
),
).toEqual({a: [2]});
expect(diffA).toBeCalledWith([1], [2]);
expect(diffB).toBeCalledWith([3], [4]);
});
it('should not use the diff attribute on addition/removal', () => {
const diffA = jest.fn();
const diffB = jest.fn();
expect(
diff({a: [1]}, {b: [2]}, {a: {diff: diffA}, b: {diff: diffB}}),
).toEqual({a: null, b: [2]});
expect(diffA).not.toBeCalled();
expect(diffB).not.toBeCalled();
});
it('should do deep diffs of Objects by default', () => {
expect(
diff(
{a: [1], b: {k: [3, 4]}, c: {k: [4, 4]}},
{a: [2], b: {k: [3, 4]}, c: {k: [4, 5]}},
{a: true, b: true, c: true},
),
).toEqual({a: [2], c: {k: [4, 5]}});
});
it('should work with undefined styles', () => {
expect(
diff(
{style: {a: '#ffffff', b: 1}},
{style: undefined},
{style: {b: true}},
),
).toEqual({b: null});
expect(
diff(
{style: undefined},
{style: {a: '#ffffff', b: 1}},
{style: {b: true}},
),
).toEqual({b: 1});
expect(
diff({style: undefined}, {style: undefined}, {style: {b: true}}),
).toEqual(null);
});
it('should work with empty styles', () => {
const validAttributes: AttributeConfiguration = {a: true, b: true};
expect(diff({a: 1, c: 3}, {}, validAttributes)).toEqual({a: null});
expect(diff({}, {a: 1, c: 3}, validAttributes)).toEqual({a: 1});
expect(diff({}, {}, validAttributes)).toEqual(null);
});
it('should flatten nested styles and predefined styles', () => {
const validStyleAttribute: AttributeConfiguration = {
style: {foo: true, bar: true},
};
expect(
diff({}, {style: [{foo: 1}, {bar: 2}]}, validStyleAttribute),
).toEqual({foo: 1, bar: 2});
expect(
diff({style: [{foo: 1}, {bar: 2}]}, {}, validStyleAttribute),
).toEqual({foo: null, bar: null});
const barStyle = {
bar: 3,
};
expect(
diff({}, {style: [[{foo: 1}, {foo: 2}], barStyle]}, validStyleAttribute),
).toEqual({foo: 2, bar: 3});
});
it('should reset a value to a previous if it is removed', () => {
const validStyleAttribute: AttributeConfiguration = {
style: {foo: true, bar: true},
};
expect(
diff(
{style: [{foo: 1}, {foo: 3}]},
{style: [{foo: 1}, {bar: 2}]},
validStyleAttribute,
),
).toEqual({foo: 1, bar: 2});
});
it('should not clear removed props if they are still in another slot', () => {
const validStyleAttribute: AttributeConfiguration = {
style: {foo: true, bar: true},
};
expect(
diff(
{style: [{}, {foo: 3, bar: 2}]},
{style: [{foo: 3}, {bar: 2}]},
validStyleAttribute,
),
).toEqual({foo: 3}); // this should ideally be null. heuristic tradeoff.
expect(
diff(
{style: [{}, {foo: 3, bar: 2}]},
{style: [{foo: 1, bar: 1}, {bar: 2}]},
validStyleAttribute,
),
).toEqual({bar: 2, foo: 1});
});
it('should clear a prop if a later style is explicit null/undefined', () => {
const validStyleAttribute: AttributeConfiguration = {
style: {foo: true, bar: true},
};
expect(
diff(
{style: [{}, {foo: 3, bar: 2}]},
{style: [{foo: 1}, {bar: 2, foo: null}]},
validStyleAttribute,
),
).toEqual({foo: null});
expect(
diff(
{style: [{foo: 3}, {foo: null, bar: 2}]},
{style: [{foo: null}, {bar: 2}]},
validStyleAttribute,
),
).toEqual({foo: null});
expect(
diff(
{style: [{foo: 1}, {foo: null}]},
{style: [{foo: 2}, {foo: null}]},
validStyleAttribute,
),
).toEqual({foo: null}); // this should ideally be null. heuristic.
// Test the same case with object equality because an early bailout doesn't
// work in this case.
const fooObj = {foo: 3};
expect(
diff(
{style: [{foo: 1}, fooObj]},
{style: [{foo: 2}, fooObj]},
validStyleAttribute,
),
).toEqual({foo: 3}); // this should ideally be null. heuristic.
expect(
diff(
{style: [{foo: 1}, {foo: 3}]},
{style: [{foo: 2}, {foo: undefined}]},
validStyleAttribute,
),
).toEqual({foo: null}); // this should ideally be null. heuristic.
});
it('handles attributes defined multiple times', () => {
const validAttributes: AttributeConfiguration = {
foo: true,
style: {foo: true},
};
expect(diff({}, {foo: 4, style: {foo: 2}}, validAttributes)).toEqual({
foo: 2,
});
expect(diff({foo: 4}, {style: {foo: 2}}, validAttributes)).toEqual({
foo: 2,
});
expect(diff({style: {foo: 2}}, {foo: 4}, validAttributes)).toEqual({
foo: 4,
});
});
// Function properties are just markers to native that events should be sent.
it('should convert functions to booleans', () => {
// Note that if the property changes from one function to another, we don't
// need to send an update.
expect(
diff(
{
a: function () {
return 1;
},
b: function () {
return 2;
},
c: 3,
},
{
b: function () {
return 9;
},
c: function () {
return 3;
},
},
{a: true, b: true, c: true},
),
).toEqual({a: null, c: true});
});
it('should skip changed functions', () => {
expect(
diff(
{
a: function () {
return 1;
},
},
{
a: function () {
return 9;
},
},
{a: true},
),
).toEqual(null);
});
it('should skip deeply-nested changed functions', () => {
expect(
diff(
{
wrapper: {
a: function () {
return 1;
},
},
},
{
wrapper: {
a: function () {
return 9;
},
},
},
{wrapper: true},
),
).toEqual(null);
});
it('should use the process function config when prop is a function', () => {
const process = jest.fn(a => a);
const nextFunction = function () {};
expect(
diff(
{
a: function () {},
},
{
a: nextFunction,
},
{a: {process}},
),
).toEqual({a: nextFunction});
expect(process).toBeCalled();
});
});
@@ -3515,7 +3515,7 @@ declare export default typeof ReactNativeViewAttributes;
`;
exports[`public API should not change unintentionally Libraries/Components/View/View.js 1`] = `
"declare export default component View(
"declare export default component(
ref?: React.RefSetter<React.ElementRef<typeof ViewNativeComponent>>,
...props: ViewProps
);
@@ -3685,6 +3685,7 @@ export type AccessibilityProps = $ReadOnly<{
accessibilityLabel?: ?Stringish,
accessibilityHint?: ?Stringish,
\\"aria-label\\"?: ?Stringish,
experimental_accessibilityOrder?: ?Array<string>,
accessibilityRole?: ?AccessibilityRole,
role?: ?Role,
accessibilityState?: ?AccessibilityState,
@@ -3832,7 +3833,6 @@ type ViewBaseProps = $ReadOnly<{
hitSlop?: ?EdgeInsetsOrSizeProp,
pointerEvents?: ?(\\"auto\\" | \\"box-none\\" | \\"box-only\\" | \\"none\\"),
removeClippedSubviews?: ?boolean,
experimental_accessibilityOrder?: ?Array<string>,
}>;
export type ViewProps = $ReadOnly<{
...DirectEventProps,
@@ -5689,7 +5689,6 @@ export type ModalPropsIOS = {
>,
onDismiss?: ?() => void,
onOrientationChange?: ?DirectEventHandler<OrientationChangeEvent>,
allowSwipeDismissal?: ?boolean,
};
export type ModalPropsAndroid = {
hardwareAccelerated?: ?boolean,
@@ -0,0 +1,31 @@
/*
* 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.
*/
#pragma once
#ifndef RCT_FIT_RM_OLD_RUNTIME
#include <jsireact/JSIExecutor.h>
namespace facebook::react {
class JSCExecutorFactory : public JSExecutorFactory {
public:
explicit JSCExecutorFactory(JSIExecutor::RuntimeInstaller runtimeInstaller)
: runtimeInstaller_(std::move(runtimeInstaller)) {}
std::unique_ptr<JSExecutor> createJSExecutor(
std::shared_ptr<ExecutorDelegate> delegate,
std::shared_ptr<MessageQueueThread> jsQueue) override;
private:
JSIExecutor::RuntimeInstaller runtimeInstaller_;
};
} // namespace facebook::react
#endif // RCT_FIT_RM_OLD_RUNTIME
@@ -0,0 +1,28 @@
/*
* 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.
*/
#include "JSCExecutorFactory.h"
#ifndef RCT_FIT_RM_OLD_RUNTIME
#import <jsc/JSCRuntime.h>
#import <memory>
namespace facebook::react {
std::unique_ptr<JSExecutor> JSCExecutorFactory::createJSExecutor(
std::shared_ptr<ExecutorDelegate> delegate,
std::shared_ptr<MessageQueueThread> __unused jsQueue)
{
return std::make_unique<JSIExecutor>(
facebook::jsc::makeJSCRuntime(), delegate, JSIExecutor::defaultTimeoutInvoker, runtimeInstaller_);
}
} // namespace facebook::react
#endif // RCT_FIT_RM_OLD_RUNTIME
@@ -22,6 +22,8 @@
}
_touchHandler = [RCTSurfaceTouchHandler new];
self.modalInPresentation = YES;
return self;
}
@@ -124,7 +124,6 @@ static ModalHostViewEventEmitter::OnOrientationChange onOrientationChangeStruct(
_viewController = [RCTFabricModalHostViewController new];
_viewController.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
_viewController.delegate = self;
_viewController.modalInPresentation = YES;
}
return _viewController;
}
@@ -240,7 +239,6 @@ static ModalHostViewEventEmitter::OnOrientationChange onOrientationChangeStruct(
- (void)updateProps:(const Props::Shared &)props oldProps:(const Props::Shared &)oldProps
{
const auto &oldViewProps = static_cast<const ModalHostViewProps &>(*_props);
const auto &newProps = static_cast<const ModalHostViewProps &>(*props);
#if !TARGET_OS_TV
@@ -253,10 +251,6 @@ static ModalHostViewEventEmitter::OnOrientationChange onOrientationChangeStruct(
self.viewController.modalPresentationStyle = presentationConfiguration(newProps);
if (oldViewProps.allowSwipeDismissal != newProps.allowSwipeDismissal) {
self.viewController.modalInPresentation = !newProps.allowSwipeDismissal;
}
_shouldPresent = newProps.visible;
[self ensurePresentedOnlyIfNeeded];
@@ -289,16 +283,6 @@ static ModalHostViewEventEmitter::OnOrientationChange onOrientationChangeStruct(
}
}
- (void)presentationControllerDidDismiss:(UIPresentationController *)presentationController
{
auto eventEmitter = [self modalEventEmitter];
const auto &props = static_cast<const ModalHostViewProps &>(*_props);
if (eventEmitter && props.allowSwipeDismissal) {
eventEmitter->onRequestClose({});
}
}
@end
#ifdef __cplusplus
@@ -1230,10 +1230,7 @@ static NSString *RCTRecursiveAccessibilityLabel(UIView *view)
return label;
}
if (self.isAccessibilityElement) {
return RCTRecursiveAccessibilityLabel(self.currentContainerView);
}
return nil;
return RCTRecursiveAccessibilityLabel(self.currentContainerView);
}
- (NSString *)accessibilityLabelForCoopting
@@ -0,0 +1,20 @@
/*
* 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 <react/runtime/JSRuntimeFactoryCAPI.h>
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
JSRuntimeFactoryRef jsrt_create_jsc_factory(void);
#ifdef __cplusplus
}
#endif
@@ -0,0 +1,20 @@
/*
* 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 "RCTJscInstanceFactory.h"
#import <ReactCommon/RCTJscInstance.h>
using namespace facebook::react;
extern "C" {
JSRuntimeFactoryRef jsrt_create_jsc_factory(void)
{
return reinterpret_cast<JSRuntimeFactoryRef>(new RCTJscInstance());
}
} // extern "C"
@@ -24,7 +24,6 @@
@property (nonatomic, copy) RCTDirectEventBlock onShow;
@property (nonatomic, assign) BOOL visible;
@property (nonatomic, assign) BOOL allowSwipeDismissal;
// Android only
@property (nonatomic, assign) BOOL statusBarTranslucent;
@@ -35,7 +35,6 @@ RCT_NOT_IMPLEMENTED(-(instancetype)initWithCoder : coder)
if ((self = [super initWithFrame:CGRectZero])) {
_bridge = bridge;
_modalViewController = [RCTModalHostViewController new];
_modalViewController.modalInPresentation = YES;
UIView *containerView = [UIView new];
containerView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
_modalViewController.view = containerView;
@@ -51,14 +50,6 @@ RCT_NOT_IMPLEMENTED(-(instancetype)initWithCoder : coder)
return self;
}
- (void)setAllowSwipeDismissal:(BOOL)allowSwipeDismissal
{
if (_allowSwipeDismissal != allowSwipeDismissal) {
_allowSwipeDismissal = allowSwipeDismissal;
_modalViewController.modalInPresentation = !allowSwipeDismissal;
}
}
- (void)notifyForBoundsChange:(CGRect)newBounds
{
if (_reactSubview && _isPresented) {
@@ -79,13 +70,6 @@ RCT_NOT_IMPLEMENTED(-(instancetype)initWithCoder : coder)
}
}
- (void)presentationControllerDidDismiss:(UIPresentationController *)presentationController
{
if (_onRequestClose != nil && _allowSwipeDismissal) {
_onRequestClose(nil);
}
}
- (void)notifyForOrientationChange
{
if (!_onOrientationChange) {
@@ -22,6 +22,8 @@
return nil;
}
self.modalInPresentation = YES;
_preferredStatusBarStyle = [RCTUIStatusBarManager() statusBarStyle];
_preferredStatusBarHidden = [RCTUIStatusBarManager() isStatusBarHidden];
@@ -119,7 +119,6 @@ RCT_EXPORT_VIEW_PROPERTY(supportedOrientations, NSArray)
RCT_EXPORT_VIEW_PROPERTY(onOrientationChange, RCTDirectEventBlock)
RCT_EXPORT_VIEW_PROPERTY(visible, BOOL)
RCT_EXPORT_VIEW_PROPERTY(onRequestClose, RCTDirectEventBlock)
RCT_EXPORT_VIEW_PROPERTY(allowSwipeDismissal, BOOL)
// Fabric only
RCT_EXPORT_VIEW_PROPERTY(onDismiss, RCTDirectEventBlock)
+1 -4
View File
@@ -240,10 +240,7 @@ RCT_NOT_IMPLEMENTED(-(instancetype)initWithCoder : unused)
if (label) {
return label;
}
if (self.isAccessibilityElement) {
return RCTRecursiveAccessibilityLabel(self);
}
return nil;
return RCTRecursiveAccessibilityLabel(self);
}
- (NSArray<UIAccessibilityCustomAction *> *)accessibilityCustomActions
@@ -2448,6 +2448,19 @@ public abstract interface class com/facebook/react/interfaces/fabric/ReactSurfac
public abstract fun stop ()Lcom/facebook/react/interfaces/TaskInterface;
}
public final class com/facebook/react/jscexecutor/JSCExecutor : com/facebook/react/bridge/JavaScriptExecutor {
public fun getName ()Ljava/lang/String;
public static final fun loadLibrary ()V
}
public final class com/facebook/react/jscexecutor/JSCExecutorFactory : com/facebook/react/bridge/JavaScriptExecutorFactory {
public fun <init> (Ljava/lang/String;Ljava/lang/String;)V
public fun create ()Lcom/facebook/react/bridge/JavaScriptExecutor;
public fun startSamplingProfiler ()V
public fun stopSamplingProfiler (Ljava/lang/String;)V
public fun toString ()Ljava/lang/String;
}
public final class com/facebook/react/jstasks/HeadlessJsTaskConfig {
public fun <init> (Lcom/facebook/react/jstasks/HeadlessJsTaskConfig;)V
public fun <init> (Ljava/lang/String;Lcom/facebook/react/bridge/WritableMap;)V
@@ -3122,6 +3135,10 @@ public class com/facebook/react/runtime/CoreReactPackage$$ReactModuleInfoProvide
public fun getReactModuleInfos ()Ljava/util/Map;
}
public final class com/facebook/react/runtime/JSCInstance : com/facebook/react/runtime/JSRuntimeFactory {
public fun <init> ()V
}
public abstract class com/facebook/react/runtime/JSRuntimeFactory {
public fun <init> (Lcom/facebook/jni/HybridData;)V
}
@@ -3236,6 +3253,10 @@ public final class com/facebook/react/soloader/OpenSourceMergedSoMapping : com/f
public final fun libhermes_executor_so ()I
public final fun libhermesinstancejni_so ()I
public final fun libhermestooling_so ()I
public final fun libjscexecutor_so ()I
public final fun libjscinstance_so ()I
public final fun libjscruntime_so ()I
public final fun libjsctooling_so ()I
public final fun libjsijniprofiler_so ()I
public final fun libjsinspector_so ()I
public final fun libmapbufferjni_so ()I
@@ -3315,7 +3336,6 @@ public abstract class com/facebook/react/uimanager/BaseViewManager : com/faceboo
public fun getExportedCustomBubblingEventTypeConstants ()Ljava/util/Map;
public fun getExportedCustomDirectEventTypeConstants ()Ljava/util/Map;
protected fun onAfterUpdateTransaction (Landroid/view/View;)V
public fun onDropViewInstance (Landroid/view/View;)V
public fun onLayoutChange (Landroid/view/View;IIIIIIII)V
protected fun prepareToRecycleView (Lcom/facebook/react/uimanager/ThemedReactContext;Landroid/view/View;)Landroid/view/View;
public fun setAccessibilityActions (Landroid/view/View;Lcom/facebook/react/bridge/ReadableArray;)V
@@ -5307,7 +5327,6 @@ public class com/facebook/react/viewmanagers/ModalHostViewManagerDelegate : com/
}
public abstract interface class com/facebook/react/viewmanagers/ModalHostViewManagerInterface : com/facebook/react/uimanager/ViewManagerWithGeneratedInterface {
public abstract fun setAllowSwipeDismissal (Landroid/view/View;Z)V
public abstract fun setAnimated (Landroid/view/View;Z)V
public abstract fun setAnimationType (Landroid/view/View;Ljava/lang/String;)V
public abstract fun setHardwareAccelerated (Landroid/view/View;Z)V
@@ -87,6 +87,8 @@ val preparePrefab by
// hermes_executor
// This prefab targets is used by Expo & Reanimated
Pair("../ReactCommon/hermes/inspector-modern/", "hermes/inspector-modern/"),
// jscexecutor
Pair("../ReactCommon/jsc/", "jsc/"),
// fabricjni
Pair("src/main/jni/react/fabric", "react/fabric/"),
// glog
@@ -234,6 +236,10 @@ val preparePrefab by
"hermestooling",
// hermes_executor
Pair("../ReactCommon/hermes/inspector-modern/", "hermes/inspector-modern/")),
PrefabPreprocessingEntry(
"jsctooling",
// jsc
Pair("../ReactCommon/jsc/", "jsc/")),
))
outputDir.set(prefabHeadersDir)
}
@@ -528,6 +534,7 @@ android {
"reactnative",
"jsi",
"hermestooling",
"jsctooling",
)
}
}
@@ -576,9 +583,10 @@ android {
resources.excludes.add("META-INF/LICENSE")
// We intentionally don't want to bundle any JS Runtime inside the Android AAR
// we produce. The reason behind this is that we want to allow users to pick the
// JS engine by specifying a dependency on either `hermes-engine` or other engines
// JS engine by specifying a dependency on either `hermes-engine` or `android-jsc`
// that will include the necessary .so files to load.
jniLibs.excludes.add("**/libhermes.so")
jniLibs.excludes.add("**/libjsc.so")
}
buildFeatures {
@@ -591,6 +599,7 @@ android {
create("jsi") { headers = File(prefabHeadersDir, "jsi").absolutePath }
create("reactnative") { headers = File(prefabHeadersDir, "reactnative").absolutePath }
create("hermestooling") { headers = File(prefabHeadersDir, "hermestooling").absolutePath }
create("jsctooling") { headers = File(prefabHeadersDir, "jsctooling").absolutePath }
}
publishing {
@@ -635,9 +644,10 @@ dependencies {
compileOnly(libs.javax.annotation.api)
api(libs.javax.inject)
// It's up to the consumer to decide if hermes or other engines should be included or not.
// Therefore hermes-engine is a compileOnly dependencies.
// It's up to the consumer to decide if hermes/jsc should be included or not.
// Therefore hermes-engine and jsc are compileOnly dependencies.
compileOnly(project(":packages:react-native:ReactAndroid:hermes-engine"))
compileOnly(libs.jsc.android)
testImplementation(libs.junit)
testImplementation(libs.assertj)
@@ -186,24 +186,6 @@ val prepareHeadersForPrefab by
into(prefabHeadersDir)
}
val buildHermesLib by
tasks.registering(CustomExecTask::class) {
dependsOn(buildHermesC)
workingDir(hermesDir)
inputs.files(hermesBuildOutputFileTree)
commandLine(
cmakeBinaryPath,
"--build",
hermesBuildDir.toString(),
"--target",
"libhermes",
"-j",
ndkBuildJobs,
)
standardOutputFile.set(project.file("$buildDir/build-hermes-lib.log"))
errorOutputFile.set(project.file("$buildDir/build-hermes-lib.error.log"))
}
fun windowsAwareCommandLine(vararg commands: String): List<String> {
val result =
if (Os.isFamily(Os.FAMILY_WINDOWS)) {
@@ -33,4 +33,13 @@ internal object ReactDebugOverlayTags {
"UI Manager",
"UI Manager View Operations (requires restart\nwarning: this is spammy)",
Color.CYAN)
@JvmField
val FABRIC_UI_MANAGER: DebugOverlayTag =
DebugOverlayTag("FabricUIManager", "Fabric UI Manager View Operations", Color.CYAN)
@JvmField
val FABRIC_RECONCILER: DebugOverlayTag =
DebugOverlayTag("FabricReconciler", "Reconciler for Fabric", Color.CYAN)
@JvmField
val RELAY: DebugOverlayTag =
DebugOverlayTag("Relay", "including prefetching", Color.rgb(0xFF, 0x99, 0x00))
}
@@ -1079,10 +1079,6 @@ public class ReactInstanceManager {
if (reactPackage instanceof ViewManagerOnDemandReactPackage) {
Collection<String> names =
((ViewManagerOnDemandReactPackage) reactPackage).getViewManagerNames(context);
// When converting this class to Kotlin, you need to retain this null check
// or wrap around a try/catch otherwise this will cause a crash for OSS libraries
// that are not migrated to Kotlin yet and are returning null for
// `getViewManagerNames`
if (names != null) {
uniqueNames.addAll(names);
}
@@ -84,7 +84,7 @@ public class NativeAnimatedModule(reactContext: ReactApplicationContext?) :
NativeAnimatedModuleSpec(reactContext), LifecycleEventListener, UIManagerListener {
// For `queueAndExecuteBatchedOperations`
private enum class BatchExecutionOpCodes(value: Int) {
private enum class BatchExecutionOpCodes(val value: Int) {
OP_CODE_CREATE_ANIMATED_NODE(1),
OP_CODE_UPDATE_ANIMATED_NODE_CONFIG(2),
OP_CODE_GET_VALUE(3),
@@ -55,6 +55,11 @@ internal object ReactSoftExceptionLogger {
listeners.remove(listener)
}
@JvmStatic
fun clearListeners(): Unit {
listeners.clear()
}
@JvmStatic
fun logSoftExceptionVerbose(@CategoryMode category: String, cause: Throwable): Unit {
logSoftException("${category}|${cause.javaClass.simpleName}:${cause.message}", cause)
@@ -17,6 +17,7 @@ import com.facebook.react.common.annotations.UnstableReactNativeAPI
import com.facebook.react.common.build.ReactBuildConfig
import com.facebook.react.fabric.ComponentFactory
import com.facebook.react.runtime.BindingsInstaller
import com.facebook.react.runtime.JSCInstance
import com.facebook.react.runtime.JSRuntimeFactory
import com.facebook.react.runtime.ReactHostImpl
import com.facebook.react.runtime.cxxreactpackage.CxxReactPackage
@@ -207,7 +208,7 @@ public object DefaultReactHost {
jsMainModulePath,
jsBundleAssetPath,
jsBundleFilePath,
HermesInstance(),
if (isHermesEnabled) HermesInstance() else JSCInstance(),
useDevSupport,
cxxReactPackageProviders,
exceptionHandler,
@@ -263,7 +264,7 @@ public object DefaultReactHost {
jsMainModulePath,
jsBundleAssetPath,
jsBundleFilePath,
HermesInstance(),
if (isHermesEnabled) HermesInstance() else JSCInstance(),
useDevSupport,
cxxReactPackageProviders,
)
@@ -17,6 +17,7 @@ import com.facebook.react.bridge.UIManagerProvider
import com.facebook.react.common.annotations.UnstableReactNativeAPI
import com.facebook.react.fabric.ComponentFactory
import com.facebook.react.fabric.FabricUIManagerProviderImpl
import com.facebook.react.runtime.JSCInstance
import com.facebook.react.runtime.JSRuntimeFactory
import com.facebook.react.runtime.hermes.HermesInstance
import com.facebook.react.uimanager.ViewManagerRegistry
@@ -92,9 +93,6 @@ protected constructor(
* If true, the app will load the Hermes engine, and fail if not found. If false, the app will
* load the JSC engine, and fail if not found.
*/
@Deprecated(
"Setting isHermesEnabled inside `ReactNativeHost` is deprecated and this field will be ignored. If this field is set to true, you can safely remove it. If this field is set to false, please follow the setup on https://github.com/react-native-community/javascriptcore to continue using JSC",
ReplaceWith(""))
protected open val isHermesEnabled: Boolean
get() = true
@@ -108,7 +106,8 @@ protected constructor(
context: Context,
jsRuntimeFactory: JSRuntimeFactory? = null
): ReactHost {
val concreteJSRuntimeFactory = jsRuntimeFactory ?: HermesInstance()
val concreteJSRuntimeFactory =
jsRuntimeFactory ?: if (isHermesEnabled) HermesInstance() else JSCInstance()
return DefaultReactHost.getDefaultReactHost(
context,
packages,
@@ -107,6 +107,9 @@ public class DefaultDevLoadingViewImplementation(
}
}
private val context: Context?
get() = reactInstanceDevHelper.currentActivity
public companion object {
private var isEnabled = true
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<d8013c4b4ec45f241908e7f8f23f7dd1>>
* @generated SignedSource<<4c40e490481c50d2f19830366f50aad5>>
*/
/**
@@ -48,12 +48,6 @@ public object ReactNativeFeatureFlags {
@JvmStatic
public fun cxxNativeAnimatedEnabled(): Boolean = accessor.cxxNativeAnimatedEnabled()
/**
* Removes JS sync at end of native animation
*/
@JvmStatic
public fun cxxNativeAnimatedRemoveJsSync(): Boolean = accessor.cxxNativeAnimatedRemoveJsSync()
/**
* Disable sync dispatch on the main queue on iOS
*/
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<15b001b69d9ea8bedbce2ce0f70bfa8e>>
* @generated SignedSource<<8cb9eb082005be480c39272ffc7dde03>>
*/
/**
@@ -23,7 +23,6 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces
private var commonTestFlagCache: Boolean? = null
private var animatedShouldSignalBatchCache: Boolean? = null
private var cxxNativeAnimatedEnabledCache: Boolean? = null
private var cxxNativeAnimatedRemoveJsSyncCache: Boolean? = null
private var disableMainQueueSyncDispatchIOSCache: Boolean? = null
private var disableMountItemReorderingAndroidCache: Boolean? = null
private var disableTextLayoutManagerCacheAndroidCache: Boolean? = null
@@ -101,15 +100,6 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces
return cached
}
override fun cxxNativeAnimatedRemoveJsSync(): Boolean {
var cached = cxxNativeAnimatedRemoveJsSyncCache
if (cached == null) {
cached = ReactNativeFeatureFlagsCxxInterop.cxxNativeAnimatedRemoveJsSync()
cxxNativeAnimatedRemoveJsSyncCache = cached
}
return cached
}
override fun disableMainQueueSyncDispatchIOS(): Boolean {
var cached = disableMainQueueSyncDispatchIOSCache
if (cached == null) {
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<dc8ac9241675deb1324d8ff0572f2fad>>
* @generated SignedSource<<b6ddd7c2e829c427e7d6b07bc588cd8c>>
*/
/**
@@ -34,8 +34,6 @@ public object ReactNativeFeatureFlagsCxxInterop {
@DoNotStrip @JvmStatic public external fun cxxNativeAnimatedEnabled(): Boolean
@DoNotStrip @JvmStatic public external fun cxxNativeAnimatedRemoveJsSync(): Boolean
@DoNotStrip @JvmStatic public external fun disableMainQueueSyncDispatchIOS(): Boolean
@DoNotStrip @JvmStatic public external fun disableMountItemReorderingAndroid(): Boolean
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<56835f26710665a11a05879a0c90e577>>
* @generated SignedSource<<76edde93369be682e837189f82871dfe>>
*/
/**
@@ -29,8 +29,6 @@ public open class ReactNativeFeatureFlagsDefaults : ReactNativeFeatureFlagsProvi
override fun cxxNativeAnimatedEnabled(): Boolean = false
override fun cxxNativeAnimatedRemoveJsSync(): Boolean = false
override fun disableMainQueueSyncDispatchIOS(): Boolean = false
override fun disableMountItemReorderingAndroid(): Boolean = false
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<a6444cccfff813fe0e96c20855496560>>
* @generated SignedSource<<e718763435de15f788b1f510e4799a67>>
*/
/**
@@ -27,7 +27,6 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc
private var commonTestFlagCache: Boolean? = null
private var animatedShouldSignalBatchCache: Boolean? = null
private var cxxNativeAnimatedEnabledCache: Boolean? = null
private var cxxNativeAnimatedRemoveJsSyncCache: Boolean? = null
private var disableMainQueueSyncDispatchIOSCache: Boolean? = null
private var disableMountItemReorderingAndroidCache: Boolean? = null
private var disableTextLayoutManagerCacheAndroidCache: Boolean? = null
@@ -108,16 +107,6 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc
return cached
}
override fun cxxNativeAnimatedRemoveJsSync(): Boolean {
var cached = cxxNativeAnimatedRemoveJsSyncCache
if (cached == null) {
cached = currentProvider.cxxNativeAnimatedRemoveJsSync()
accessedFeatureFlags.add("cxxNativeAnimatedRemoveJsSync")
cxxNativeAnimatedRemoveJsSyncCache = cached
}
return cached
}
override fun disableMainQueueSyncDispatchIOS(): Boolean {
var cached = disableMainQueueSyncDispatchIOSCache
if (cached == null) {
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<e671a1bb72c4b5afcd6f364f05666e89>>
* @generated SignedSource<<892db232156970e8241d1f1ddde3a745>>
*/
/**
@@ -29,8 +29,6 @@ public interface ReactNativeFeatureFlagsProvider {
@DoNotStrip public fun cxxNativeAnimatedEnabled(): Boolean
@DoNotStrip public fun cxxNativeAnimatedRemoveJsSync(): Boolean
@DoNotStrip public fun disableMainQueueSyncDispatchIOS(): Boolean
@DoNotStrip public fun disableMountItemReorderingAndroid(): Boolean
@@ -233,8 +233,8 @@ internal object TurboModuleInteropUtils {
i += 1
}
if (returnClass == Boolean::class.javaPrimitiveType ||
returnClass == Boolean::class.javaObjectType) {
if (returnClass == java.lang.Boolean::class.javaPrimitiveType ||
returnClass == java.lang.Boolean::class.java) {
return "BooleanKind"
}
@@ -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.jscexecutor
import com.facebook.jni.HybridData
import com.facebook.proguard.annotations.DoNotStrip
import com.facebook.react.bridge.JavaScriptExecutor
import com.facebook.react.bridge.ReadableNativeMap
import com.facebook.react.common.annotations.internal.LegacyArchitecture
import com.facebook.react.common.annotations.internal.LegacyArchitectureLogger
import com.facebook.soloader.SoLoader
@DoNotStrip
@LegacyArchitecture
public class JSCExecutor internal constructor(jscConfig: ReadableNativeMap) :
JavaScriptExecutor(initHybrid(jscConfig)) {
override fun getName(): String {
return "JSCExecutor"
}
internal companion object {
init {
loadLibrary()
LegacyArchitectureLogger.assertLegacyArchitecture("JSCExecutor")
}
@JvmStatic
@Throws(UnsatisfiedLinkError::class)
fun loadLibrary() {
SoLoader.loadLibrary("jscexecutor")
}
@JvmStatic private external fun initHybrid(jscConfig: ReadableNativeMap): HybridData
}
}
@@ -0,0 +1,46 @@
/*
* 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.jscexecutor
import com.facebook.react.bridge.JavaScriptExecutor
import com.facebook.react.bridge.JavaScriptExecutorFactory
import com.facebook.react.bridge.WritableNativeMap
import com.facebook.react.common.annotations.internal.LegacyArchitecture
import com.facebook.react.common.annotations.internal.LegacyArchitectureLogLevel
import com.facebook.react.common.annotations.internal.LegacyArchitectureLogger
@LegacyArchitecture(logLevel = LegacyArchitectureLogLevel.ERROR)
public class JSCExecutorFactory(private val appName: String, private val deviceName: String) :
JavaScriptExecutorFactory {
init {
LegacyArchitectureLogger.assertLegacyArchitecture(
"JSCExecutorFactory", LegacyArchitectureLogLevel.ERROR)
}
@Throws(Exception::class)
override fun create(): JavaScriptExecutor {
val jscConfig =
WritableNativeMap().apply {
putString("OwnerIdentity", "ReactNative")
putString("AppIdentity", appName)
putString("DeviceIdentity", deviceName)
}
return JSCExecutor(jscConfig)
}
override fun startSamplingProfiler() {
throw UnsupportedOperationException("Starting sampling profiler not supported on ${toString()}")
}
override fun stopSamplingProfiler(filename: String) {
throw UnsupportedOperationException("Stopping sampling profiler not supported on ${toString()}")
}
override fun toString(): String = "JSIExecutor+JSCRuntime"
}
@@ -8,13 +8,13 @@
package com.facebook.react.modules.appearance
import android.content.Context
import android.content.res.Configuration
import androidx.appcompat.app.AppCompatDelegate
import com.facebook.fbreact.specs.NativeAppearanceSpec
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.bridge.UiThreadUtil
import com.facebook.react.bridge.buildReadableMap
import com.facebook.react.module.annotations.ReactModule
import com.facebook.react.views.common.UiModeUtils
/** Module that exposes the user's preferred color scheme. */
@ReactModule(name = NativeAppearanceSpec.NAME)
@@ -41,7 +41,13 @@ constructor(
return overrideColorScheme.getScheme()
}
return if (UiModeUtils.isDarkMode(context)) "dark" else "light"
val currentNightMode =
context.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK
return when (currentNightMode) {
Configuration.UI_MODE_NIGHT_NO -> "light"
Configuration.UI_MODE_NIGHT_YES -> "dark"
else -> "light"
}
}
public override fun getColorScheme(): String {
@@ -0,0 +1,24 @@
/*
* 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.runtime
import com.facebook.jni.HybridData
import com.facebook.jni.annotations.DoNotStrip
import com.facebook.jni.annotations.DoNotStripAny
import com.facebook.soloader.SoLoader
@DoNotStripAny
public class JSCInstance : JSRuntimeFactory(initHybrid()) {
private companion object {
init {
SoLoader.loadLibrary("jscinstance")
}
@DoNotStrip @JvmStatic private external fun initHybrid(): HybridData
}
}
@@ -8,14 +8,9 @@
package com.facebook.react.runtime
import com.facebook.jni.HybridData
import com.facebook.proguard.annotations.DoNotStrip
import com.facebook.soloader.SoLoader
public abstract class JSRuntimeFactory(
@Suppress("unused", "NoHungarianNotation", "NotAccessedPrivateField")
@DoNotStrip
private val mHybridData: HybridData
) {
public abstract class JSRuntimeFactory(private val mHybridData: HybridData) {
private companion object {
init {
SoLoader.loadLibrary("rninstance")
@@ -67,7 +67,6 @@ import com.facebook.react.uimanager.ViewManager
import com.facebook.react.uimanager.ViewManagerRegistry
import com.facebook.react.uimanager.ViewManagerResolver
import com.facebook.react.uimanager.events.EventDispatcher
import com.facebook.react.util.RNLog
import com.facebook.soloader.SoLoader
import com.facebook.systrace.Systrace
import com.facebook.systrace.SystraceMessage
@@ -543,18 +542,7 @@ internal class ReactInstance(
for (reactPackage in reactPackages) {
if (reactPackage is ViewManagerOnDemandReactPackage) {
val names = reactPackage.getViewManagerNames(context)
// We need to null check here because some Java implementation of the
// `ViewManagerOnDemandReactPackage` interface could still return null even
// if the method is marked as returning a non-nullable collection in Kotlin.
// See https://github.com/facebook/react-native/issues/52014
@Suppress("SENSELESS_COMPARISON")
if (names == null) {
RNLog.w(
context,
"The ReactPackage called: `${reactPackage.javaClass.simpleName}` is returning null for getViewManagerNames(). This is violating the signature of the method. That method should be updated to return an empty collection.")
} else {
uniqueNames.addAll(names)
}
uniqueNames.addAll(names)
}
}
return uniqueNames
@@ -42,6 +42,11 @@ public object OpenSourceMergedSoMapping : ExternalSoMapping {
"jsijniprofiler" -> {
"hermestooling"
}
"jscexecutor",
"jscruntime",
"jscinstance" -> {
"jsctooling"
}
else -> input
}
@@ -51,6 +56,10 @@ public object OpenSourceMergedSoMapping : ExternalSoMapping {
"hermes_executor" -> libhermes_executor_so()
"hermesinstancejni" -> libhermesinstancejni_so()
"hermestooling" -> libhermestooling_so()
"jscexecutor" -> libjscexecutor_so()
"jscinstance" -> libjscinstance_so()
"jscruntime" -> libjscruntime_so()
"jsctooling" -> libjsctooling_so()
"jsijniprofiler" -> libjsijniprofiler_so()
"jsinspector" -> libjsinspector_so()
"mapbufferjni" -> libmapbufferjni_so()
@@ -76,6 +85,14 @@ public object OpenSourceMergedSoMapping : ExternalSoMapping {
public external fun libhermestooling_so(): Int
public external fun libjscexecutor_so(): Int
public external fun libjscinstance_so(): Int
public external fun libjscruntime_so(): Int
public external fun libjsctooling_so(): Int
public external fun libjsijniprofiler_so(): Int
public external fun libjsinspector_so(): Int
@@ -173,19 +173,30 @@ public abstract class BaseViewManager<T extends View, C extends LayoutShadowNode
protected void addEventEmitters(@NonNull ThemedReactContext reactContext, @NonNull T view) {
super.addEventEmitters(reactContext, view);
BaseVMFocusChangeListener focusChangeListener =
new BaseVMFocusChangeListener(view.getOnFocusChangeListener());
focusChangeListener.attach(view);
}
@Override
public void onDropViewInstance(@NonNull T view) {
super.onDropViewInstance(view);
@Nullable OnFocusChangeListener focusChangeListener = view.getOnFocusChangeListener();
if (focusChangeListener instanceof BaseVMFocusChangeListener) {
((BaseVMFocusChangeListener) focusChangeListener).detach(view);
}
@Nullable OnFocusChangeListener originalFocusChangeListener = view.getOnFocusChangeListener();
view.setOnFocusChangeListener(
(v, hasFocus) -> {
if (originalFocusChangeListener != null) {
originalFocusChangeListener.onFocusChange(v, hasFocus);
}
int surfaceId = UIManagerHelper.getSurfaceId(v.getContext());
if (surfaceId == View.NO_ID) {
return;
}
if (view.getContext() instanceof ThemedReactContext) {
ThemedReactContext themedReactContext = (ThemedReactContext) v.getContext();
@Nullable
EventDispatcher eventDispatcher =
UIManagerHelper.getEventDispatcherForReactTag(themedReactContext, view.getId());
if (eventDispatcher != null) {
if (hasFocus) {
eventDispatcher.dispatchEvent(new FocusEvent(surfaceId, view.getId()));
} else {
eventDispatcher.dispatchEvent(new BlurEvent(surfaceId, view.getId()));
}
}
}
});
}
// Currently, layout listener is only attached when transform or transformOrigin is set.
@@ -1041,49 +1052,4 @@ public abstract class BaseViewManager<T extends View, C extends LayoutShadowNode
}
// Please add new props to BaseViewManagerDelegate as well!
/**
* A helper class to keep track of the original focus change listener if one is set. This is
* especially helpful for views that are recycled so we can retain and restore the original
* listener upon recycling (onDropViewInstance).
*/
private class BaseVMFocusChangeListener<V extends View> implements OnFocusChangeListener {
private @Nullable OnFocusChangeListener mOriginalFocusChangeListener;
public BaseVMFocusChangeListener(@Nullable OnFocusChangeListener originalFocusChangeListener) {
mOriginalFocusChangeListener = originalFocusChangeListener;
}
public void attach(T view) {
view.setOnFocusChangeListener(this);
}
public void detach(T view) {
view.setOnFocusChangeListener(mOriginalFocusChangeListener);
}
@Override
public void onFocusChange(View view, boolean hasFocus) {
if (mOriginalFocusChangeListener != null) {
mOriginalFocusChangeListener.onFocusChange(view, hasFocus);
}
int surfaceId = UIManagerHelper.getSurfaceId(view.getContext());
if (surfaceId == View.NO_ID) {
return;
}
if (view.getContext() instanceof ThemedReactContext) {
ThemedReactContext themedReactContext = (ThemedReactContext) view.getContext();
@Nullable
EventDispatcher eventDispatcher =
UIManagerHelper.getEventDispatcherForReactTag(themedReactContext, view.getId());
if (eventDispatcher != null) {
if (hasFocus) {
eventDispatcher.dispatchEvent(new FocusEvent(surfaceId, view.getId()));
} else {
eventDispatcher.dispatchEvent(new BlurEvent(surfaceId, view.getId()));
}
}
}
}
}
}
@@ -27,4 +27,12 @@ internal object LayoutDirectionUtil {
YogaDirection.RTL -> View.LAYOUT_DIRECTION_RTL
else -> View.LAYOUT_DIRECTION_INHERIT
}
@JvmStatic
fun toYogaFromAndroid(direction: Int): YogaDirection =
when (direction) {
View.LAYOUT_DIRECTION_LTR -> YogaDirection.LTR
View.LAYOUT_DIRECTION_RTL -> YogaDirection.RTL
else -> YogaDirection.INHERIT
}
}
@@ -17,6 +17,7 @@ import android.view.View;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent;
import android.widget.EditText;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.view.ViewCompat;
@@ -500,6 +501,10 @@ public class ReactAccessibilityDelegate extends ExploreByTouchHelper {
node.setContentDescription(mView.getContentDescription());
}
if (mView instanceof TextView && ((TextView) mView).getText() != null) {
node.setText(((TextView) mView).getText());
}
populateAccessibilityNodeInfo(mView, node);
node.setBoundsInParent(new Rect(0, 0, mView.getWidth(), mView.getHeight()));
} else {
@@ -13,6 +13,9 @@ import com.facebook.react.uimanager.events.TouchEventType
/** Constants exposed to JS from [UIManagerModule]. */
internal object UIManagerModuleConstants {
const val ACTION_DISMISSED: String = "dismissed"
const val ACTION_ITEM_SELECTED: String = "itemSelected"
@JvmField
val bubblingEventTypeConstants: Map<String, Any> =
mapOf(
@@ -17,23 +17,22 @@ import com.facebook.react.bridge.UiThreadUtil
*/
public class ViewManagerRegistry : ComponentCallbacks2 {
private val viewManagersMap: MutableMap<String, ViewManager<*, *>>
private val viewManagers: MutableMap<String, ViewManager<*, *>>
private val viewManagerResolver: ViewManagerResolver?
public constructor(viewManagerResolver: ViewManagerResolver) {
this.viewManagersMap = mutableMapOf<String, ViewManager<*, *>>()
this.viewManagers = mutableMapOf<String, ViewManager<*, *>>()
this.viewManagerResolver = viewManagerResolver
}
public constructor(viewManagerList: List<ViewManager<in Nothing, in Nothing>>) {
this.viewManagersMap = viewManagerList.associateBy { it.name }.toMutableMap()
this.viewManagerResolver = null
viewManagers = viewManagerList.associateBy { it.name }.toMutableMap()
viewManagerResolver = null
}
public constructor(viewManagerMap: Map<String, ViewManager<*, *>>?) {
this.viewManagersMap =
viewManagerMap?.toMutableMap() ?: mutableMapOf<String, ViewManager<*, *>>()
this.viewManagerResolver = null
viewManagers = viewManagerMap?.toMutableMap() ?: mutableMapOf<String, ViewManager<*, *>>()
viewManagerResolver = null
}
/**
@@ -45,13 +44,13 @@ public class ViewManagerRegistry : ComponentCallbacks2 {
@Synchronized
public fun get(className: String): ViewManager<*, *> {
// 1. Try to get the manager without the prefix.
viewManagersMap[className]?.let {
viewManagers[className]?.let {
return it
}
// 2. Try to get the manager with the RCT prefix.
val rctViewManagerName = "RCT$className"
viewManagersMap[rctViewManagerName]?.let {
viewManagers[rctViewManagerName]?.let {
return it
}
@@ -80,7 +79,7 @@ public class ViewManagerRegistry : ComponentCallbacks2 {
private fun getViewManagerFromResolver(className: String): ViewManager<*, *>? {
val viewManager = viewManagerResolver?.getViewManager(className)
if (viewManager != null) {
viewManagersMap[className] = viewManager
viewManagers[className] = viewManager
}
return viewManager
}
@@ -92,7 +91,7 @@ public class ViewManagerRegistry : ComponentCallbacks2 {
@JvmName("getViewManagerIfExists")
@Synchronized
internal fun getViewManagerIfExists(className: String): ViewManager<*, *>? {
viewManagersMap[className]?.let {
viewManagers[className]?.let {
return it
}
return viewManagerResolver?.let { getViewManagerFromResolver(className) }
@@ -101,7 +100,7 @@ public class ViewManagerRegistry : ComponentCallbacks2 {
/** Send lifecycle signal to all ViewManagers that StopSurface has been called. */
public fun onSurfaceStopped(surfaceId: Int) {
val viewManagers: List<ViewManager<*, *>> =
synchronized(this) { ArrayList(viewManagersMap.values) }
synchronized(this) { ArrayList(viewManagers.values) }
val runnable = {
for (viewManager in viewManagers) {
@@ -119,7 +118,7 @@ public class ViewManagerRegistry : ComponentCallbacks2 {
/** Called on instance destroy */
public fun invalidate() {
val viewManagers: List<ViewManager<*, *>> =
synchronized(this) { ArrayList(viewManagersMap.values) }
synchronized(this) { ArrayList(viewManagers.values) }
val runnable = {
for (viewManager in viewManagers) {
@@ -137,7 +136,7 @@ public class ViewManagerRegistry : ComponentCallbacks2 {
/** ComponentCallbacks2 method. */
public override fun onTrimMemory(level: Int) {
val viewManagers: List<ViewManager<*, *>> =
synchronized(this) { ArrayList(viewManagersMap.values) }
synchronized(this) { ArrayList(viewManagers.values) }
val runnable = {
for (viewManager in viewManagers) {
@@ -227,6 +227,11 @@ internal class BorderDrawable(
return borderColors?.edgeColors?.get(position.ordinal) ?: Color.BLACK
}
fun invalidateSelfAndUpdatePath() {
needUpdatePath = true
invalidateSelf()
}
private fun drawRectangularBorders(canvas: Canvas) {
val borderWidth = computeBorderInsets()
val borderLeft = borderWidth.left.roundToInt()
@@ -1,26 +0,0 @@
/*
* 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.views.common
import android.content.Context
import android.content.res.Configuration
/** Utility object providing static methods for working with UI mode properties from Context. */
internal object UiModeUtils {
/**
* Determines whether the current UI mode is dark mode
*
* @param context The context to check the UI mode from
* @return true if the current UI mode is dark mode, false otherwise
*/
@JvmStatic
fun isDarkMode(context: Context): Boolean =
context.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK ==
Configuration.UI_MODE_NIGHT_YES
}
@@ -85,9 +85,6 @@ internal class ReactModalHostManager :
@ReactProp(name = "identifier")
override fun setIdentifier(view: ReactModalHostView, value: Int): Unit = Unit
@ReactProp(name = "allowSwipeDismissal")
override fun setAllowSwipeDismissal(view: ReactModalHostView, value: Boolean): Unit = Unit
override fun setTestId(view: ReactModalHostView, value: String?) {
super.setTestId(view, value)
view.setDialogRootViewGroupTestId(value)
@@ -37,6 +37,7 @@ import com.facebook.react.bridge.ReactContext
import com.facebook.react.bridge.UiThreadUtil
import com.facebook.react.bridge.WritableMap
import com.facebook.react.bridge.WritableNativeMap
import com.facebook.react.common.ReactConstants
import com.facebook.react.common.annotations.VisibleForTesting
import com.facebook.react.common.build.ReactBuildConfig
import com.facebook.react.config.ReactFeatureFlags
@@ -390,7 +391,8 @@ public class ReactModalHostView(context: ThemedReactContext) :
// This is to prevent a crash from the following error, without a clear repro steps:
// java.lang.IllegalArgumentException: View=DecorView@c94931b[XxxActivity] not attached to
// window manager
FLog.e(TAG, "ReactModalHostView: error while setting window flags: ", e.message)
FLog.e(
ReactConstants.TAG, "ReactModalHostView: error while setting window flags: ", e.message)
}
}
@@ -23,7 +23,6 @@ import androidx.annotation.ColorInt
import androidx.annotation.DoNotInline
import androidx.annotation.RequiresApi
import androidx.core.view.ViewCompat
import com.facebook.proguard.annotations.DoNotStrip
import com.facebook.react.uimanager.BackgroundStyleApplicator
import com.facebook.react.uimanager.ReactCompoundView
import com.facebook.react.uimanager.style.Overflow
@@ -37,7 +36,6 @@ import kotlin.math.roundToInt
* existing layout, previously generated for measurement by Fabric, to ensure consistency of
* measurements, and avoid duplicate work.
*/
@DoNotStrip
internal class PreparedLayoutTextView(context: Context) : ViewGroup(context), ReactCompoundView {
private var clickableSpans: List<ClickableSpan> = emptyList()
@@ -76,9 +74,7 @@ internal class PreparedLayoutTextView(context: Context) : ViewGroup(context), Re
@ColorInt var selectionColor: Int? = null
val text: CharSequence?
// Avoid mangling the getter name, to allow black box E2E tests to read text content via
// reflection
@DoNotStrip get() = preparedLayout?.layout?.text
get() = preparedLayout?.layout?.text
init {
initView()
@@ -166,7 +162,7 @@ internal class PreparedLayoutTextView(context: Context) : ViewGroup(context), Re
val x = event.x.toInt()
val y = event.y.toInt()
val clickableSpan = getSpanInCoords(x, y, ClickableSpan::class.java)
val clickableSpan = getClickableSpanInCoords(x, y)
if (clickableSpan == null) {
clearSelection()
@@ -186,7 +182,15 @@ internal class PreparedLayoutTextView(context: Context) : ViewGroup(context), Re
return true
}
private fun <T> getSpanInCoords(x: Int, y: Int, clazz: Class<T>): T? {
/**
* Get the clickable span that is at the exact coordinates
*
* @param x x-position of the click
* @param y y-position of the click
* @return a clickable span that's located where the click occurred, or: `null` if no clickable
* span was located there
*/
private fun getClickableSpanInCoords(x: Int, y: Int): ClickableSpan? {
val offset = getTextOffsetAt(x, y)
if (offset < 0) {
return null
@@ -194,45 +198,17 @@ internal class PreparedLayoutTextView(context: Context) : ViewGroup(context), Re
val spanned = text as? Spanned ?: return null
val spans = spanned.getSpans(offset, offset, clazz)
if (spans.isEmpty()) {
return null
}
// When we have multiple spans marked with SPAN_EXCLUSIVE_INCLUSIVE next to each other, both
// spans are returned by getSpans
check(spans.size <= 2)
for (span in spans) {
val spanFlags = spanned.getSpanFlags(span)
val inclusiveStart =
if ((spanFlags and Spanned.SPAN_INCLUSIVE_INCLUSIVE) != 0 ||
(spanFlags and Spanned.SPAN_INCLUSIVE_EXCLUSIVE) != 0) {
spanned.getSpanStart(span)
} else {
spanned.getSpanStart(span) + 1
}
val inclusiveEnd =
if ((spanFlags and Spanned.SPAN_INCLUSIVE_INCLUSIVE) != 0 ||
(spanFlags and Spanned.SPAN_EXCLUSIVE_INCLUSIVE) != 0) {
spanned.getSpanEnd(span)
} else {
spanned.getSpanEnd(span) - 1
}
if (offset >= inclusiveStart && offset <= inclusiveEnd) {
return span
}
val clickableSpans = spanned.getSpans(offset, offset, ClickableSpan::class.java)
if (clickableSpans.isNotEmpty()) {
return clickableSpans[0]
}
return null
}
private fun getTextOffsetAt(x: Int, y: Int): Int {
val layoutX = x - paddingLeft
val layoutY = y - (paddingTop + (preparedLayout?.verticalOffset?.roundToInt() ?: 0))
val layout = preparedLayout?.layout ?: return -1
val line = layout.getLineForVertical(layoutY)
val line = layout.getLineForVertical(y)
val left: Float
val right: Float
@@ -262,12 +238,12 @@ internal class PreparedLayoutTextView(context: Context) : ViewGroup(context), Re
right = if (rtl) layout.getParagraphRight(line).toFloat() else layout.getLineMax(line)
}
if (layoutX < left || layoutX > right) {
if (x < left || x > right) {
return -1
}
return try {
layout.getOffsetForHorizontal(line, layoutX.toFloat())
layout.getOffsetForHorizontal(line, x.toFloat())
} catch (e: ArrayIndexOutOfBoundsException) {
// This happens for bidi text on Android 7-8.
// See
@@ -309,10 +285,6 @@ internal class PreparedLayoutTextView(context: Context) : ViewGroup(context), Re
// TODO T225199534: Add support for "needsOffscreenAlphaCompositing" to Text
override fun hasOverlappingRendering(): Boolean = false
override fun reactTagForTouch(touchX: Float, touchY: Float): Int =
getSpanInCoords(touchX.roundToInt(), touchY.roundToInt(), ReactTagSpan::class.java)?.reactTag
?: id
@RequiresApi(api = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
private object Api34Utils {
private var highlightPaths: List<Path>? = null
@@ -359,4 +331,21 @@ internal class PreparedLayoutTextView(context: Context) : ViewGroup(context), Re
return spans
}
}
override fun reactTagForTouch(touchX: Float, touchY: Float): Int {
val offset = getTextOffsetAt(touchX.roundToInt(), touchY.roundToInt())
if (offset < 0) {
return id
}
val spanned = text as? Spanned ?: return id
val reactSpans = spanned.getSpans(offset, offset, ReactTagSpan::class.java)
check(reactSpans.size <= 1)
return if (reactSpans.isNotEmpty()) {
reactSpans[0].reactTag
} else {
id
}
}
}
@@ -469,7 +469,7 @@ internal object TextLayoutManager {
baseTextAttributes: TextAttributeProps,
context: Context
): TextPaint {
val paint = TextPaint(TextPaint.ANTI_ALIAS_FLAG)
val paint = TextPaint()
updateTextPaint(paint, baseTextAttributes, context)
return paint
}
@@ -7,6 +7,7 @@
package com.facebook.react.views.view
import android.content.res.Configuration
import android.graphics.Color
import android.os.Build
import android.view.Window
@@ -14,7 +15,6 @@ import android.view.WindowManager
import androidx.core.view.ViewCompat
import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsControllerCompat
import com.facebook.react.views.common.UiModeUtils
@Suppress("DEPRECATION")
internal fun Window.setStatusBarTranslucency(isTranslucent: Boolean) {
@@ -71,7 +71,9 @@ internal fun Window.setSystemBarsTranslucency(isTranslucent: Boolean) {
WindowCompat.setDecorFitsSystemWindows(this, !isTranslucent)
if (isTranslucent) {
val isDarkMode = UiModeUtils.isDarkMode(context)
val isDarkMode =
context.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK ==
Configuration.UI_MODE_NIGHT_YES
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
isStatusBarContrastEnforced = false
@@ -51,7 +51,9 @@ endfunction()
# Third-party prefabs
find_package(hermes-engine REQUIRED CONFIG)
find_package(fbjni REQUIRED CONFIG)
find_package(jsc-android REQUIRED CONFIG)
add_library(fbjni ALIAS fbjni::fbjni)
add_library(jsc ALIAS jsc-android::jsc)
# Third-party downloaded targets
add_react_third_party_ndk_subdir(glog)
@@ -69,6 +71,7 @@ add_react_common_subdir(logger)
add_react_common_subdir(jsiexecutor)
add_react_common_subdir(jsitooling)
add_react_common_subdir(cxxreact)
add_react_common_subdir(jsc)
add_react_common_subdir(jsi)
add_react_common_subdir(callinvoker)
add_react_common_subdir(oscompat)
@@ -137,6 +140,8 @@ add_react_android_subdir(src/main/jni/first-party/yogajni)
add_react_android_subdir(src/main/jni/first-party/jni-lib-merge)
add_react_android_subdir(src/main/jni/react/jni)
add_react_android_subdir(src/main/jni/react/reactperflogger)
add_react_android_subdir(src/main/jni/react/jscexecutor)
add_react_android_subdir(src/main/jni/react/jsctooling)
add_react_android_subdir(src/main/jni/react/turbomodule)
add_react_android_subdir(src/main/jni/react/uimanager)
add_react_android_subdir(src/main/jni/react/mapbuffer)
@@ -150,6 +155,7 @@ add_react_android_subdir(src/main/jni/react/hermes/instrumentation/)
add_react_android_subdir(src/main/jni/react/runtime/cxxreactpackage)
add_react_android_subdir(src/main/jni/react/runtime/jni)
add_react_android_subdir(src/main/jni/react/runtime/hermes/jni)
add_react_android_subdir(src/main/jni/react/runtime/jsc/jni)
add_react_android_subdir(src/main/jni/react/devsupport)
# SoMerging Utils
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<b9aef65ab7a1dffc1646519619dedc58>>
* @generated SignedSource<<bb0b241f561c9c443d6453fa259d0a5d>>
*/
/**
@@ -57,12 +57,6 @@ class ReactNativeFeatureFlagsJavaProvider
return method(javaProvider_);
}
bool cxxNativeAnimatedRemoveJsSync() override {
static const auto method =
getReactNativeFeatureFlagsProviderJavaClass()->getMethod<jboolean()>("cxxNativeAnimatedRemoveJsSync");
return method(javaProvider_);
}
bool disableMainQueueSyncDispatchIOS() override {
static const auto method =
getReactNativeFeatureFlagsProviderJavaClass()->getMethod<jboolean()>("disableMainQueueSyncDispatchIOS");
@@ -376,11 +370,6 @@ bool JReactNativeFeatureFlagsCxxInterop::cxxNativeAnimatedEnabled(
return ReactNativeFeatureFlags::cxxNativeAnimatedEnabled();
}
bool JReactNativeFeatureFlagsCxxInterop::cxxNativeAnimatedRemoveJsSync(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop> /*unused*/) {
return ReactNativeFeatureFlags::cxxNativeAnimatedRemoveJsSync();
}
bool JReactNativeFeatureFlagsCxxInterop::disableMainQueueSyncDispatchIOS(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop> /*unused*/) {
return ReactNativeFeatureFlags::disableMainQueueSyncDispatchIOS();
@@ -666,9 +655,6 @@ void JReactNativeFeatureFlagsCxxInterop::registerNatives() {
makeNativeMethod(
"cxxNativeAnimatedEnabled",
JReactNativeFeatureFlagsCxxInterop::cxxNativeAnimatedEnabled),
makeNativeMethod(
"cxxNativeAnimatedRemoveJsSync",
JReactNativeFeatureFlagsCxxInterop::cxxNativeAnimatedRemoveJsSync),
makeNativeMethod(
"disableMainQueueSyncDispatchIOS",
JReactNativeFeatureFlagsCxxInterop::disableMainQueueSyncDispatchIOS),
@@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<f2c3bfb76a9efd7d5bc6c01ab1cccb2c>>
* @generated SignedSource<<04986fa84ec60aa0816ee79ab68e56aa>>
*/
/**
@@ -39,9 +39,6 @@ class JReactNativeFeatureFlagsCxxInterop
static bool cxxNativeAnimatedEnabled(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop>);
static bool cxxNativeAnimatedRemoveJsSync(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop>);
static bool disableMainQueueSyncDispatchIOS(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop>);
@@ -12,6 +12,7 @@ include(${REACT_COMMON_DIR}/cmake-utils/react-native-flags.cmake)
# hermestooling is a shared library where we merge all the hermes* related libraries.
#
# It acts as an 'umbrella' library and gets removed by RNGP (see `configureJsEnginePackagingOptions`)
# Please note that this library gets removed for users that opt to use JSC as their JS engine.
add_library(hermestooling
SHARED
@@ -36,8 +36,6 @@
#include "JniJSModulesUnbundle.h"
#include "NativeArray.h"
#ifndef RCT_FIT_RM_OLD_RUNTIME
using namespace facebook::jni;
namespace facebook::react {
@@ -421,5 +419,3 @@ void CatalystInstanceImpl::unregisterFromInspector() {
}
} // namespace facebook::react
#endif
@@ -21,8 +21,6 @@
#include "ModuleRegistryBuilder.h"
#include "ReactInstanceManagerInspectorTarget.h"
#ifndef RCT_FIT_RM_OLD_RUNTIME
namespace facebook::react {
class Instance;
@@ -127,5 +125,3 @@ class CatalystInstanceImpl : public jni::HybridClass<CatalystInstanceImpl> {
};
} // namespace facebook::react
#endif
@@ -23,8 +23,6 @@
#include "CatalystInstanceImpl.h"
#include "ReadableNativeArray.h"
#ifndef RCT_FIT_RM_OLD_RUNTIME
using facebook::xplat::module::CxxModule;
namespace facebook::react {
@@ -154,5 +152,3 @@ jni::local_ref<JReflectMethod::javaobject> JMethodDescriptor::getMethod()
}
} // namespace facebook::react
#endif
@@ -13,8 +13,6 @@
#include "MethodInvoker.h"
#ifndef RCT_FIT_RM_OLD_RUNTIME
namespace facebook::react {
class Instance;
@@ -87,5 +85,3 @@ class JavaNativeModule : public NativeModule {
};
} // namespace facebook::react
#endif
@@ -10,8 +10,6 @@
#include <cxxreact/JSExecutor.h>
#include <fbjni/fbjni.h>
#ifndef RCT_FIT_RM_OLD_RUNTIME
namespace facebook::react {
class JavaScriptExecutorHolder
@@ -33,5 +31,3 @@ class JavaScriptExecutorHolder
};
} // namespace facebook::react
#endif
@@ -17,8 +17,6 @@
#include <sstream>
#include <utility>
#ifndef RCT_FIT_RM_OLD_RUNTIME
using magic_number_t = uint32_t;
const magic_number_t MAGIC_FILE_HEADER = 0xFB0BD1E5;
const char* MAGIC_FILE_NAME = "UNBUNDLE";
@@ -96,5 +94,3 @@ JSModulesUnbundle::Module JniJSModulesUnbundle::getModule(
}
} // namespace facebook::react
#endif

Some files were not shown because too many files have changed in this diff Show More