diff --git a/.circleci/Dockerfiles/Dockerfile.android b/.circleci/Dockerfiles/Dockerfile.android index 8d0e28803a5f..a4bdb0cbd0d2 100644 --- a/.circleci/Dockerfiles/Dockerfile.android +++ b/.circleci/Dockerfiles/Dockerfile.android @@ -14,14 +14,13 @@ # and build a Android application that can be used to run the # tests specified in the scripts/ directory. # -FROM reactnativecommunity/react-native-android:5.4 +FROM reactnativecommunity/react-native-android:6.1 LABEL Description="React Native Android Test Image" LABEL maintainer="Héctor Ramos " # set default environment variables ENV GRADLE_OPTS="-Dorg.gradle.daemon=false -Dorg.gradle.jvmargs=\"-Xmx512m -XX:+HeapDumpOnOutOfMemoryError\"" -ENV JAVA_TOOL_OPTIONS="-Dfile.encoding=UTF8" ENV KOTLIN_HOME="third-party/kotlin" ADD .buckconfig /app/.buckconfig diff --git a/.circleci/Dockerfiles/scripts/run-android-docker-instrumentation-tests.sh b/.circleci/Dockerfiles/scripts/run-android-docker-instrumentation-tests.sh index 64f0ee465642..130ebbb01f0b 100644 --- a/.circleci/Dockerfiles/scripts/run-android-docker-instrumentation-tests.sh +++ b/.circleci/Dockerfiles/scripts/run-android-docker-instrumentation-tests.sh @@ -33,7 +33,7 @@ node cli.js bundle --platform android --dev true --entry-file ReactAndroid/src/a # build test APK # shellcheck disable=SC1091 -source ./scripts/android-setup.sh && NO_BUCKD=1 retry3 buck install ReactAndroid/src/androidTest/buck-runner:instrumentation-tests --config build.threads=1 +source ./scripts/android-setup.sh && NO_BUCKD=1 scripts/retry3 buck install ReactAndroid/src/androidTest/buck-runner:instrumentation-tests --config build.threads=1 # run installed apk with tests node ./.circleci/Dockerfiles/scripts/run-android-ci-instrumentation-tests.js "$*" diff --git a/.circleci/config.yml b/.circleci/config.yml index fee64be10324..8464e162fb2f 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -26,6 +26,10 @@ references: hermes_workspace_root: &hermes_workspace_root /tmp/hermes + hermes_tarball_artifacts_dir: &hermes_tarball_artifacts_dir + /tmp/hermes/hermes-runtime-darwin + hermes_osxbin_artifacts_dir: &hermes_osxbin_artifacts_dir + /tmp/hermes/osx-bin attach_hermes_workspace: &attach_hermes_workspace attach_workspace: at: *hermes_workspace_root @@ -34,10 +38,7 @@ references: # Dependency Anchors # ------------------------- dependency_versions: - # The Xcode version used on CircleCI OSS tests must be kept in sync with - # the Xcode version used on Sandcastle OSS tests. See _XCODE_VERSION in - # tools/utd/migrated_nbtd_jobs/react_native_oss.td - xcode_version: &xcode_version "13.3.1" + xcode_version: &xcode_version "14.0.1" nodelts_image: &nodelts_image "cimg/node:16.14" nodeprevlts_image: &nodeprevlts_image "cimg/node:14.19" @@ -47,17 +48,26 @@ references: # Anchors for the cache keys cache_keys: - buck_cache_key: &buck_cache_key v3-buck-v2019.01.10.01-{{ checksum "scripts/circleci/buck_fetch.sh" }}} - checkout_cache_key: &checkout_cache_key v1-checkout + checkout_cache_key: &checkout_cache_key v5-checkout gems_cache_key: &gems_cache_key v1-gems-{{ checksum "Gemfile.lock" }} gradle_cache_key: &gradle_cache_key v1-gradle-{{ checksum "gradle/wrapper/gradle-wrapper.properties" }}-{{ checksum "ReactAndroid/gradle.properties" }} - hermes_cache_key: &hermes_cache_key v1-hermes-{{ .Environment.CIRCLE_JOB }}-{{ checksum "/tmp/hermes/hermesversion" }} - hermes_sdk_cache_key: &hermes_sdk_cache_key v1-hermes-{{ .Environment.CIRCLE_JOB }}-{{ checksum "sdks/.hermes-cache-key-file" }} - hermes_windows_cache_key: &hermes_windows_cache_key v1-hermes-{{ .Environment.CIRCLE_JOB }}-{{ checksum "tmp/hermes/hermesversion" }} - pods_cache_key: &pods_cache_key v6-pods-{{ .Environment.CIRCLE_JOB }}-{{ checksum "packages/rn-tester/Podfile.lock.bak" }}-{{ checksum "packages/rn-tester/Podfile" }} + hermes_workspace_cache_key: &hermes_workspace_cache_key v4-hermes-{{ .Environment.CIRCLE_JOB }}-{{ checksum "/tmp/hermes/hermesversion" }} + hermes_workspace_debug_cache_key: &hermes_workspace_debug_cache_key v2-hermes-{{ .Environment.CIRCLE_JOB }}-debug-{{ checksum "/tmp/hermes/hermesversion" }}-{{ checksum "/tmp/react-native-version" }} + hermes_workspace_release_cache_key: &hermes_workspace_release_cache_key v2-hermes-{{ .Environment.CIRCLE_JOB }}-release-{{ checksum "/tmp/hermes/hermesversion" }}-{{ checksum "/tmp/react-native-version" }} + hermes_windows_cache_key: &hermes_windows_cache_key v3-hermes-{{ .Environment.CIRCLE_JOB }}-{{ checksum "tmp/hermes/hermesversion" }} + hermes_tarball_debug_cache_key: &hermes_tarball_debug_cache_key v4-hermes-tarball-debug-{{ checksum "/tmp/hermes/hermesversion" }}-{{ checksum "/tmp/react-native-version" }} + hermes_tarball_release_cache_key: &hermes_tarball_release_cache_key v3-hermes-tarball-release-{{ checksum "/tmp/hermes/hermesversion" }}-{{ checksum "/tmp/react-native-version" }} + pods_cache_key: &pods_cache_key v8-pods-{{ .Environment.CIRCLE_JOB }}-{{ checksum "packages/rn-tester/Podfile.lock.bak" }}-{{ checksum "packages/rn-tester/Podfile" }} windows_yarn_cache_key: &windows_yarn_cache_key v1-win-yarn-cache-{{ arch }}-{{ checksum "yarn.lock" }} yarn_cache_key: &yarn_cache_key v5-yarn-cache-{{ .Environment.CIRCLE_JOB }} + cache_paths: + hermes_workspace_macos_cache_paths: &hermes_workspace_macos_cache_paths + - ~/react-native/sdks/hermes/build_macosx + - ~/react-native/sdks/hermes/destroot + hermes_tarball_cache_paths: &hermes_tarball_cache_paths + - *hermes_tarball_artifacts_dir + # ------------------------- # Filters # ------------------------- @@ -92,7 +102,7 @@ executors: reactnativeandroid: <<: *defaults docker: - - image: reactnativecommunity/react-native-android:5.4 + - image: reactnativecommunity/react-native-android:6.1 resource_class: "xlarge" environment: - TERM: "dumb" @@ -109,6 +119,8 @@ executors: macos: xcode: *xcode_version resource_class: macos.x86.medium.gen2 + environment: + - REACT_NATIVE_CI: true # ------------------------- # COMMANDS @@ -122,13 +134,10 @@ commands: type: string steps: - restore_cache: - keys: - - << parameters.checkout_base_cache_key >>-{{ .Branch }}-{{ .Revision }} - - << parameters.checkout_base_cache_key >>-{{ .Branch }}- - - << parameters.checkout_base_cache_key >> + key: << parameters.checkout_base_cache_key >>-{{ arch }}-{{ .Branch }}-{{ .Revision }} - checkout - save_cache: - key: << parameters.checkout_base_cache_key >>-{{ .Branch }}-{{ .Revision }} + key: << parameters.checkout_base_cache_key >>-{{ arch }}-{{ .Branch }}-{{ .Revision }} paths: - ".git" @@ -177,31 +186,6 @@ commands: - ~/.cache/yarn key: << parameters.yarn_base_cache_key >>-{{ arch }}-{{ checksum "yarn.lock" }} - install_buck_tooling: - steps: - - restore_cache: - keys: - - *buck_cache_key - - run: - name: Install BUCK - command: | - buck --version - # Install related tooling - if [[ ! -e ~/okbuck ]]; then - git clone https://github.com/uber/okbuck.git ~/okbuck --depth=1 - fi - - save_cache: - paths: - - ~/buck - - ~/okbuck - key: *buck_cache_key - - install_github_bot_deps: - steps: - - run: - name: "Yarn: Install dependencies (GitHub bots)" - command: cd bots && yarn install --non-interactive --cache-folder ~/.cache/yarn - brew_install: parameters: package: @@ -247,12 +231,6 @@ commands: - ReactAndroid/build/third-party-ndk key: *gradle_cache_key - download_buck_dependencies: - steps: - - run: - name: Download Dependencies Using Buck - command: ./scripts/circleci/buck_fetch.sh - run_e2e: parameters: platform: @@ -276,32 +254,175 @@ commands: type: enum enum: ["android", "ios"] steps: - - install_github_bot_deps - run: name: Report size of RNTester.app (analysis-bot) command: GITHUB_TOKEN="$PUBLIC_ANALYSISBOT_GITHUB_TOKEN_A""$PUBLIC_ANALYSISBOT_GITHUB_TOKEN_B" scripts/circleci/report-bundle-size.sh << parameters.platform >> || true - with_hermes_sdk_cache_span: + get_react_native_version: + steps: + - run: + name: Get React Native version + command: | + VERSION=$( grep '"version"' package.json | cut -d '"' -f 4 | head -1) + # Save the react native version we are building in a file so we can use that file as part of the cache key. + echo "$VERSION" > /tmp/react-native-version + echo "React Native Version is $(cat /tmp/react-native-version)" + echo "Hermes commit is $(cat /tmp/hermes/hermesversion)" + + with_hermes_tarball_cache_span: parameters: steps: type: steps + set_tarball_path: + type: boolean + default: False + flavor: + default: "Debug" + description: The Hermes build type. Must be one of "Debug", "Release". + type: enum + enum: ["Debug", "Release"] + hermes_tarball_artifacts_dir: + type: string + default: *hermes_tarball_artifacts_dir steps: + - get_react_native_version + - when: + condition: + equal: [ << parameters.flavor >>, "Debug"] + steps: + - restore_cache: + keys: + - *hermes_tarball_debug_cache_key + - when: + condition: + equal: [ << parameters.flavor >>, "Release"] + steps: + - restore_cache: + keys: + - *hermes_tarball_release_cache_key + - when: + condition: << parameters.set_tarball_path >> + steps: + - run: + name: Set HERMES_ENGINE_TARBALL_PATH envvar if Hermes tarball is present + command: | + HERMES_TARBALL_ARTIFACTS_DIR=<< parameters.hermes_tarball_artifacts_dir >> + if [ ! -d $HERMES_TARBALL_ARTIFACTS_DIR ]; then + echo "Hermes tarball artifacts dir not present ($HERMES_TARBALL_ARTIFACTS_DIR). Build Hermes from source." + exit 0 + fi + + if [ ! -d ~/react-native ]; then + echo "No React Native checkout found. Run `checkout` first." + exit 0 + fi + + TARBALL_FILENAME=$(node ~/react-native/scripts/hermes/get-tarball-name.js --buildType "<< parameters.flavor >>") + TARBALL_PATH=$HERMES_TARBALL_ARTIFACTS_DIR/$TARBALL_FILENAME + + echo "Looking for $TARBALL_FILENAME in $HERMES_TARBALL_ARTIFACTS_DIR" + echo "$TARBALL_PATH" + + if [ ! -f $TARBALL_PATH ]; then + echo "Hermes tarball not present ($TARBALL_PATH). Build Hermes from source." + exit 0 + fi + + echo "Found Hermes tarball at $TARBALL_PATH" + echo "export HERMES_ENGINE_TARBALL_PATH=$TARBALL_PATH" >> $BASH_ENV - run: - name: Setup Hermes cache + name: Print Hermes version command: | - HERMES_CACHE_KEY_FILE="sdks/.hermes-cache-key-file" - if [ ! -f "$HERMES_CACHE_KEY_FILE" ]; then - git ls-remote https://github.com/facebook/hermes main | cut -f 1 > $HERMES_CACHE_KEY_FILE - fi - - restore_cache: - keys: - - *hermes_sdk_cache_key + HERMES_TARBALL_ARTIFACTS_DIR=<< parameters.hermes_tarball_artifacts_dir >> + TARBALL_FILENAME=$(node ~/react-native/scripts/hermes/get-tarball-name.js --buildType "<< parameters.flavor >>") + TARBALL_PATH=$HERMES_TARBALL_ARTIFACTS_DIR/$TARBALL_FILENAME + if [[ -e $TARBALL_PATH ]]; then + tar -xf $TARBALL_PATH + echo 'print(HermesInternal?.getRuntimeProperties?.()["OSS Release Version"])' > test.js + ./destroot/bin/hermes test.js + rm test.js + rm -rf destroot + else + echo 'No Hermes tarball found.' + fi - steps: << parameters.steps >> - - save_cache: - key: *hermes_sdk_cache_key - paths: - - sdks/hermesc - - sdks/hermes + - when: + condition: + equal: [ << parameters.flavor >>, "Debug"] + steps: + - save_cache: + key: *hermes_tarball_debug_cache_key + paths: *hermes_tarball_cache_paths + - when: + condition: + equal: [ << parameters.flavor >>, "Release"] + steps: + - save_cache: + key: *hermes_tarball_release_cache_key + paths: *hermes_tarball_cache_paths + + with_hermesc_span: + description: "Makes hermesc available to the provided steps, if hermesc is present." + parameters: + flavor: + default: "Debug" + description: The Hermes build type. Must be one of "Debug", "Release". + type: enum + enum: ["Debug", "Release"] + steps: + type: steps + steps: + - export_hermesc: + flavor: << parameters.flavor >> + - steps: << parameters.steps >> + - export_hermesc: + flavor: << parameters.flavor >> + + export_hermesc: + description: "Configures hermesc for use in Hermes builds when possible. The binary is built by either of the macOS or iOS builds, and may be cached by previous builds." + parameters: + flavor: + default: "Debug" + description: The Hermes build type. Must be one of "Debug", "Release". + type: enum + enum: ["Debug", "Release"] + artifacts_dir: + type: string + default: *hermes_osxbin_artifacts_dir + steps: + - run: + name: "Export path to HermesC if available" + command: | + # Although the hermesc binary built by debug and release jobs is + # identical, we need to store it in distinct paths as Circle CI + # cannot have two different jobs write to the same path in + # artifacts. + mkdir -p << parameters.artifacts_dir >>/Debug << parameters.artifacts_dir >>/Release + hermesc_artifacts_path=<< parameters.artifacts_dir >>/<< parameters.flavor >>/hermesc + + hermesc_bin_path=bin/hermesc + hermes_build_dir_macos=$(pwd)/sdks/hermes/build_macosx + hermes_build_dir_ios=$(pwd)/sdks/hermes/build_iphoneos + + function export_hermesc_cmake_path { + build_dir=$1 + hermesc_bin=$build_dir/$hermesc_bin_path + cmake_path=$build_dir/ImportHermesc.cmake + + if [[ -f $cmake_path ]]; then + echo "export HERMES_OVERRIDE_HERMESC_PATH=$cmake_path" >> $BASH_ENV + fi + + if [[ ! -f $hermesc_artifacts_path ]]; then + cp $hermesc_bin $hermesc_artifacts_path + fi + } + + if [[ -f $hermes_build_dir_macos/$hermesc_bin_path ]]; then + export_hermesc_cmake_path $hermes_build_dir_macos + elif [[ -f $hermes_build_dir_ios/$hermesc_bin_path ]]; then + export_hermesc_cmake_path $hermes_build_dir_ios + fi # ------------------------- # JOBS @@ -319,7 +440,6 @@ jobs: - checkout - run_yarn - - install_github_bot_deps # Note: The yarn gpg key needs to be refreshed to work around https://github.com/yarnpkg/yarn/issues/7866 - run: @@ -368,6 +488,11 @@ jobs: command: yarn flow-check-android when: always + - run: + name: Run TypeScript tests + command: yarn test-typescript + when: always + - run: name: Sanity checks command: | @@ -438,6 +563,11 @@ jobs: description: Specifies whether disabled tests should run. Set this to true to debug failing tests. type: boolean default: false + jsengine: + default: "Hermes" + description: Which JavaScript engine to use. Must be one of "Hermes", "JSC". + type: enum + enum: ["Hermes", "JSC"] environment: - REPORTS_DIR: "./reports/junit" steps: @@ -450,6 +580,7 @@ jobs: cd scripts sh run_ruby_tests.sh - run_yarn + - *attach_hermes_workspace - run: | cd packages/rn-tester bundle check || bundle install @@ -467,7 +598,7 @@ jobs: name: "Brew: Tap wix/brew" command: brew tap wix/brew >/dev/null - brew_install: - package: applesimutils watchman cmake + package: applesimutils watchman - run: name: Configure Watchman @@ -480,25 +611,23 @@ jobs: name: Set USE_FRAMEWORKS=1 command: echo "export USE_FRAMEWORKS=1" >> $BASH_ENV - - run: - name: Set USE_HERMES=1 - command: echo "export USE_HERMES=1" >> $BASH_ENV - - - run: - name: Set BUILD_HERMES_SOURCE=1 - command: echo "export BUILD_HERMES_SOURCE=1" >> $BASH_ENV - - run: name: Setup the CocoaPods environment command: bundle exec pod setup - - with_hermes_sdk_cache_span: + - with_hermes_tarball_cache_span: + set_tarball_path: True steps: - with_rntester_pods_cache_span: steps: - run: name: Generate RNTesterPods Workspace - command: cd packages/rn-tester && bundle exec pod install --verbose + command: | + if [[ << parameters.jsengine >> == "JSC" ]]; then + export USE_HERMES=0 + fi + + cd packages/rn-tester && bundle exec pod install --verbose # ------------------------- # Runs iOS unit tests @@ -508,6 +637,16 @@ jobs: - run: name: "Run Tests: iOS Unit and Integration Tests" command: yarn test-ios + - run: + name: Zip Derived data folder + when: always + command: | + echo "zipping tests results" + cd /Users/distiller/Library/Developer/Xcode + XCRESULT_PATH=$(find . -name '*.xcresult') + tar -zcvf xcresults.tar.gz $XCRESULT_PATH + - store_artifacts: + path: /Users/distiller/Library/Developer/Xcode/xcresults.tar.gz # Optionally, run disabled tests - when: @@ -534,6 +673,59 @@ jobs: - store_test_results: path: ./reports/junit + # ------------------------- + # JOBS: Test Buck + # ------------------------- + test_buck: + executor: reactnativeandroid + environment: + KOTLIN_HOME=third-party/kotlin + steps: + - checkout + - setup_artifacts + - run_yarn + + - run: + name: Download Dependencies Using Buck + command: ./scripts/circleci/buck_fetch.sh + + - run: + name: Build & Test React Native using Buck + command: | + buck build ReactAndroid/src/main/java/com/facebook/react + buck build ReactAndroid/src/main/java/com/facebook/react/shell + + - run: + name: Run Tests - Android Unit Tests with Buck + command: buck test ReactAndroid/src/test/... --config build.threads=$BUILD_THREADS --xml ./reports/buck/all-results-raw.xml + + - run: + name: Build JavaScript Bundle for instrumentation tests + command: node cli.js bundle --max-workers 2 --platform android --dev true --entry-file ReactAndroid/src/androidTest/js/TestBundle.js --bundle-output ReactAndroid/src/androidTest/assets/AndroidTestBundle.js + + - run: + name: Build Tests - Android Instrumentation Tests with Buck + # Here, just build the instrumentation tests. There is a known issue with installing the APK to android-21+ emulator. + command: | + if [[ ! -e ReactAndroid/src/androidTest/assets/AndroidTestBundle.js ]]; then + echo "JavaScript bundle missing, cannot run instrumentation tests. Verify Build JavaScript Bundle step completed successfully."; exit 1; + fi + source scripts/android-setup.sh && NO_BUCKD=1 scripts/retry3 timeout 300 buck build ReactAndroid/src/androidTest/buck-runner:instrumentation-tests --config build.threads=$BUILD_THREADS + + - run: + name: Collect Test Results + command: | + find . -type f -regex ".*/build/test-results/debug/.*xml" -exec cp {} ./reports/build/ \; + find . -type f -regex ".*/outputs/androidTest-results/connected/.*xml" -exec cp {} ./reports/outputs/ \; + find . -type f -regex ".*/buck-out/gen/ReactAndroid/src/test/.*/.*xml" -exec cp {} ./reports/buck/ \; + if [ -f ~/react-native/reports/buck/all-results-raw.xml ]; then + ~/react-native/scripts/circleci/buckToJunit/buckToJunit.sh ~/react-native/reports/buck/all-results-raw.xml ~/react-native/reports/junit/results.xml + fi + when: always + + - store_test_results: + path: ./reports/junit + # ------------------------- # JOBS: Test Android # ------------------------- @@ -543,18 +735,11 @@ jobs: run_disabled_tests: type: boolean default: false - environment: - KOTLIN_HOME=third-party/kotlin steps: - checkout - setup_artifacts - run_yarn - # Validate Android SDK installation and packages - - run: - name: Validate Android SDK Install - command: ./scripts/validate-android-sdk.sh - # Starting emulator in advance as it takes some time to boot. - run: name: Create Android Virtual Device @@ -564,60 +749,23 @@ jobs: command: source scripts/android-setup.sh && launchAVD background: true - # Install Buck - - install_buck_tooling - - # Validate Android test environment (including Buck) - - run: - name: Validate Android Test Environment - command: ./scripts/validate-android-test-env.sh - - - download_buck_dependencies - download_gradle_dependencies - # Build and compile - - run: - name: Build & Test React Native using Buck - command: | - buck build ReactAndroid/src/main/java/com/facebook/react - buck build ReactAndroid/src/main/java/com/facebook/react/shell - - run: name: Build & Test React Native using Gradle command: ./gradlew buildAll - - run: - name: Compile Native Libs for Unit and Integration Tests - command: ./gradlew :ReactAndroid:packageReactNdkLibsForBuck -Pjobs=$BUILD_THREADS - no_output_timeout: 30m - - run: name: Build RN Tester for Release using Gradle command: ./gradlew packages:rn-tester:android:app:assembleRelease - # Build JavaScript Bundle for instrumentation tests - - run: - name: Build JavaScript Bundle - command: node cli.js bundle --max-workers 2 --platform android --dev true --entry-file ReactAndroid/src/androidTest/js/TestBundle.js --bundle-output ReactAndroid/src/androidTest/assets/AndroidTestBundle.js - # Wait for AVD to finish booting before running tests - run: name: Wait for Android Virtual Device command: source scripts/android-setup.sh && waitForAVD - # ------------------------- - # Run Android tests - - run: - name: Run Tests - Android Unit Tests with Buck - command: buck test ReactAndroid/src/test/... --config build.threads=$BUILD_THREADS --xml ./reports/buck/all-results-raw.xml - - run: - name: Build Tests - Android Instrumentation Tests with Buck - # Here, just build the instrumentation tests. There is a known issue with installing the APK to android-21+ emulator. - command: | - if [[ ! -e ReactAndroid/src/androidTest/assets/AndroidTestBundle.js ]]; then - echo "JavaScript bundle missing, cannot run instrumentation tests. Verify Build JavaScript Bundle step completed successfully."; exit 1; - fi - source scripts/android-setup.sh && NO_BUCKD=1 retry3 timeout 300 buck build ReactAndroid/src/androidTest/buck-runner:instrumentation-tests --config build.threads=$BUILD_THREADS + - report_bundle_size: + platform: android # Optionally, run disabled tests - when: @@ -626,72 +774,139 @@ jobs: - run: echo "Failing tests may be moved here temporarily." - run_e2e: platform: android - # ------------------------- - - # Collect Results - - report_bundle_size: - platform: android - - run: - name: Collect Test Results - command: | - find . -type f -regex ".*/build/test-results/debug/.*xml" -exec cp {} ./reports/build/ \; - find . -type f -regex ".*/outputs/androidTest-results/connected/.*xml" -exec cp {} ./reports/outputs/ \; - find . -type f -regex ".*/buck-out/gen/ReactAndroid/src/test/.*/.*xml" -exec cp {} ./reports/buck/ \; - if [ -f ~/react-native/reports/buck/all-results-raw.xml ]; then - cd ~/okbuck - ./tooling/junit/buck_to_junit.sh ~/react-native/reports/buck/all-results-raw.xml ~/react-native/reports/junit/results.xml - fi - when: always - - store_test_results: - path: ./reports/junit # ------------------------- # JOBS: Test Android Template # ------------------------- test_android_template: executor: reactnativeandroid + parameters: + flavor: + default: "Debug" + description: The Android build type. Must be one of "Debug", "Release". + type: enum + enum: ["Debug", "Release"] + architecture: + default: "OldArch" + description: Which React Native architecture to use. Must be one of "NewArch", "OldArch". + type: enum + enum: [ "NewArch", "OldArch" ] + jsengine: + default: "Hermes" + description: Which JavaScript engine to use. Must be one of "Hermes", "JSC". + type: enum + enum: ["Hermes", "JSC"] + environment: + - PROJECT_NAME: "AndroidTemplateProject" steps: - - checkout + - checkout_code_with_cache - run_yarn - attach_workspace: at: . - - run: name: Create Android template project command: | REPO_ROOT=$(pwd) - PACKAGE=$(cat build/react-native-package-version) - PATH_TO_PACKAGE="$REPO_ROOT/build/$PACKAGE" - cd template - npm add $PATH_TO_PACKAGE - npm install + node ./scripts/set-rn-template-version.js "file:$REPO_ROOT/build/$(cat build/react-native-package-version)" + node cli.js init $PROJECT_NAME --directory "/tmp/$PROJECT_NAME" --template $REPO_ROOT --verbose --skip-install + cd /tmp/$PROJECT_NAME + yarn - run: - name: Build the template application - command: cd template/android/ && ./gradlew assembleDebug + name: Build the template application for << parameters.flavor >> with Architecture set to << parameters.architecture >>, and using the << parameters.jsengine>> JS engine. + command: | + cd /tmp/$PROJECT_NAME/android/ + if [[ << parameters.architecture >> == "NewArch" ]]; then + export ORG_GRADLE_PROJECT_newArchEnabled=true + else + export ORG_GRADLE_PROJECT_newArchEnabled=false + fi + if [[ << parameters.jsengine >> == "Hermes" ]]; then + export ORG_GRADLE_PROJECT_hermesEnabled=true + else + export ORG_GRADLE_PROJECT_hermesEnabled=false + fi + ./gradlew assemble<< parameters.flavor >> -PREACT_NATIVE_MAVEN_LOCAL_REPO=/root/react-native/maven-local # ------------------------- # JOBS: Test iOS Template # ------------------------- test_ios_template: executor: reactnativeios + parameters: + flavor: + default: "Debug" + description: The Xcode build type. Must be one of "Debug", "Release". + type: enum + enum: ["Debug", "Release"] + architecture: + default: "OldArch" + description: Which React Native architecture to use. Must be one of "NewArch", "OldArch". + type: enum + enum: ["NewArch", "OldArch"] + jsengine: + default: "Hermes" + description: Which JavaScript engine to use. Must be one of "Hermes", "JSC". + type: enum + enum: ["Hermes", "JSC"] + flipper: + default: "WithFlipper" + description: Whether Flipper is enabled. Must be one of "WithFlipper", "WithoutFlipper". + type: enum + enum: ["WithFlipper", "WithoutFlipper"] + use_frameworks: + default: "StaticLibraries" + description: Which kind of option we want to use for `use_frameworks!` + type: enum + enum: ["StaticLibraries", "StaticFrameworks"] #TODO: Add "DynamicFrameworks" environment: - PROJECT_NAME: "iOSTemplateProject" - HERMES_WS_DIR: *hermes_workspace_root - steps: + # Early exit in case of Release and WithFlipper. The two does not make sense together. + # Unfortunately, the `exclude` parameter of `matrix` does not work, so we have to do it manually. + - when: + condition: + and: + - equal: [ << parameters.flavor >>, "Release"] + - equal: [ << parameters.flipper >>, "WithFlipper" ] + steps: + - run: + command: circleci-agent step halt # this interrupts the job successfully. + # use_frameworks! does not works with Flipper enabled + - when: + condition: + and: + - equal: [ << parameters.use_frameworks >>, "StaticFrameworks"] + - equal: [ << parameters.flipper >>, "WithFlipper" ] + steps: + - run: + command: circleci-agent step halt # this interrupts the job successfully. + # use_frameworks! does not works with the New Architecture enabled + - when: + condition: + and: + - equal: [ << parameters.use_frameworks >>, "StaticFrameworks"] + - equal: [ << parameters.architecture >>, "NewArch" ] + steps: + - run: + command: circleci-agent step halt # this interrupts the job successfully. + # Valid configuration, we can continue - checkout_code_with_cache - run_yarn - attach_workspace: at: . - *attach_hermes_workspace - - run: - name: Set USE_HERMES=1 - command: echo "export USE_HERMES=1" >> $BASH_ENV - - run: - name: Set HERMES_ENGINE_TARBALL_PATH - command: | - echo "export HERMES_ENGINE_TARBALL_PATH=$(ls -AU $HERMES_WS_DIR/hermes-runtime-darwin/hermes-runtime-darwin-*.tar.gz | head -1)" >> $BASH_ENV + - when: + condition: + equal: ["Hermes", << parameters.jsengine >>] + steps: + - run: + name: Set HERMES_ENGINE_TARBALL_PATH + command: | + BUILD_TYPE="<< parameters.flavor >>" + TARBALL_FILENAME=$(node ./scripts/hermes/get-tarball-name.js --buildType "$BUILD_TYPE") + echo "export HERMES_ENGINE_TARBALL_PATH=$HERMES_WS_DIR/hermes-runtime-darwin/$TARBALL_FILENAME" >> $BASH_ENV - run: name: Create iOS template project command: | @@ -699,14 +914,43 @@ jobs: PACKAGE=$(cat build/react-native-package-version) PATH_TO_PACKAGE="$REPO_ROOT/build/$PACKAGE" node ./scripts/set-rn-template-version.js "file:$PATH_TO_PACKAGE" - mkdir -p ~/tmp - cd ~/tmp - node "$REPO_ROOT/cli.js" init "$PROJECT_NAME" --template "$REPO_ROOT" --verbose + node cli.js init $PROJECT_NAME --directory "/tmp/$PROJECT_NAME" --template $REPO_ROOT --verbose --skip-install + - run: + name: Install iOS dependencies - Configuration << parameters.flavor >>; New Architecture << parameters.architecture >>; JS Engine << parameters.jsengine>>; Flipper << parameters.flipper >> + command: | + cd /tmp/$PROJECT_NAME + yarn install + cd ios + + bundle install + + if [[ << parameters.flavor >> == "Release" ]]; then + export PRODUCTION=1 + fi + + if [[ << parameters.architecture >> == "NewArch" ]]; then + export RCT_NEW_ARCH_ENABLED=1 + fi + + if [[ << parameters.jsengine >> == "JSC" ]]; then + export USE_HERMES=0 + fi + + if [[ << parameters.flipper >> == "WithoutFlipper" ]]; then + export NO_FLIPPER=1 + fi + + if [[ << parameters.use_frameworks >> == "StaticFrameworks" ]]; then + export USE_FRAMEWORKS=static + fi + + bundle exec pod install - run: name: Build template project command: | xcodebuild build \ - -workspace ~/tmp/$PROJECT_NAME/ios/$PROJECT_NAME.xcworkspace \ + -configuration << parameters.flavor >> \ + -workspace /tmp/$PROJECT_NAME/ios/$PROJECT_NAME.xcworkspace \ -scheme $PROJECT_NAME \ -sdk iphonesimulator @@ -715,9 +959,21 @@ jobs: # ------------------------- test_ios_rntester: executor: reactnativeios + parameters: + jsengine: + default: "Hermes" + description: Which JavaScript engine to use. Must be one of "Hermes", "JSC". + type: enum + enum: ["Hermes", "JSC"] + architecture: + default: "OldArch" + description: Which React Native architecture to use. Must be one of "OldArch", "NewArch". + type: enum + enum: ["NewArch", "OldArch"] steps: - checkout_code_with_cache - run_yarn + - *attach_hermes_workspace # The macOS machine can run out of storage if Hermes is enabled and built from source. # Since this job does not use the iOS Simulator, deleting it provides a quick way to @@ -727,23 +983,22 @@ jobs: background: true command: sudo rm -rf /Library/Developer/CoreSimulator/Profiles/Runtimes/ - - run: - name: Set USE_HERMES=1 - command: echo "export USE_HERMES=1" >> $BASH_ENV - - - run: - name: Set BUILD_HERMES_SOURCE=1 - command: echo "export BUILD_HERMES_SOURCE=1" >> $BASH_ENV - - - brew_install: - package: cmake - - - with_hermes_sdk_cache_span: + - with_hermes_tarball_cache_span: + set_tarball_path: True steps: - run: - name: Install CocoaPods dependencies + name: Install CocoaPods dependencies - Architecture << parameters.architecture >> command: | rm -rf packages/rn-tester/Pods + + if [[ << parameters.architecture >> == "NewArch" ]]; then + export RCT_NEW_ARCH_ENABLED=1 + fi + + if [[ << parameters.jsengine >> == "JSC" ]]; then + export USE_HERMES=0 + fi + cd packages/rn-tester && bundle exec pod install - run: @@ -767,23 +1022,26 @@ jobs: environment: - ANDROID_HOME: "C:\\Android\\android-sdk" - ANDROID_NDK: "C:\\Android\\android-sdk\\ndk\\20.1.5948944" - - ANDROID_BUILD_VERSION: 31 - - ANDROID_TOOLS_VERSION: 31.0.0 + - ANDROID_BUILD_VERSION: 33 + - ANDROID_TOOLS_VERSION: 33.0.0 - GRADLE_OPTS: -Dorg.gradle.daemon=false steps: - checkout_code_with_cache - run: - name: Install Node + name: Disable NVM + # Use choco to manage node versions due to https://github.com/npm/cli/issues/4234 + command: nvm off + + - run: + name: Install Node JS # Note: Version set separately for non-Windows builds, see above. - command: | - nvm install 16 - nvm use 16 + command: choco install nodejs-lts # Setup Dependencies - run: - name: Install Yarn - command: choco install yarn + name: Enable Yarn with corepack + command: corepack enable - run: name: Display Environment info @@ -863,14 +1121,15 @@ jobs: path: ~/react-native/coverage/ # ------------------------- - # JOBS: Build hermesc + # JOBS: Build Hermes # ------------------------- prepare_hermes_workspace: docker: - - image: debian:bullseye + - image: debian:11 environment: - HERMES_WS_DIR: *hermes_workspace_root - HERMES_VERSION_FILE: "sdks/.hermesversion" + - REACT_NATIVE_CI: true steps: - run: name: Install dependencies @@ -893,16 +1152,19 @@ jobs: HERMES_TAG_SHA=$(git ls-remote https://github.com/facebook/hermes main | cut -f 1 | tr -d '[:space:]') echo $HERMES_TAG_SHA > /tmp/hermes/hermesversion fi + cat /tmp/hermes/hermesversion - restore_cache: - key: *hermes_cache_key + key: *hermes_workspace_cache_key - run: name: Download Hermes tarball command: | - node scripts/hermes/prepare-hermes-for-build + node scripts/hermes/prepare-hermes-for-build $CIRCLE_PULL_REQUEST cp sdks/download/* $HERMES_WS_DIR/download/. cp -r sdks/hermes/* $HERMES_WS_DIR/hermes/. + + cat /tmp/hermes/hermesversion - save_cache: - key: *hermes_cache_key + key: *hermes_workspace_cache_key paths: - /tmp/hermes/download/ - /tmp/hermes/hermes/ @@ -927,7 +1189,7 @@ jobs: libreadline-dev libicu-dev zip python3 - *attach_hermes_workspace - restore_cache: - key: *hermes_cache_key + key: *hermes_workspace_cache_key - run: name: Set up workspace command: | @@ -946,7 +1208,7 @@ jobs: cp /tmp/hermes/build/bin/hermesc /tmp/hermes/linux64-bin/. fi - save_cache: - key: *hermes_cache_key + key: *hermes_workspace_cache_key paths: - /tmp/hermes/linux64-bin/ - /tmp/hermes/hermes/destroot/ @@ -958,68 +1220,103 @@ jobs: - linux64-bin build_hermes_macos: + parameters: + flavor: + default: "Debug" + description: The Hermes build type. Must be one of "Debug", "Release". + type: enum + enum: ["Debug", "Release"] executor: reactnativeios environment: - HERMES_WS_DIR: *hermes_workspace_root + - HERMES_TARBALL_ARTIFACTS_DIR: *hermes_tarball_artifacts_dir + - HERMES_OSXBIN_ARTIFACTS_DIR: *hermes_osxbin_artifacts_dir steps: - checkout_code_with_cache + - run_yarn - *attach_hermes_workspace - - restore_cache: - key: *hermes_cache_key + - get_react_native_version + - when: + condition: + equal: [ << parameters.flavor >>, "Debug"] + steps: + - restore_cache: + keys: + - *hermes_workspace_debug_cache_key + - when: + condition: + equal: [ << parameters.flavor >>, "Release"] + steps: + - restore_cache: + keys: + - *hermes_workspace_release_cache_key - run: name: Set up workspace command: | - mkdir -p /tmp/hermes/osx-bin - mkdir -p ~/react-native/sdks/hermes - cp -r $HERMES_WS_DIR/hermes/* ~/react-native/sdks/hermes/. - - run: - name: Install dependencies - command: | - brew install cmake - - run: - name: Build the Hermes iOS frameworks - command: | - cd ~/react-native/sdks/hermes - ./utils/build-ios-framework.sh - - run: - name: Build the Hermes Mac frameworks - command: | - cd ~/react-native/sdks/hermes - ./utils/build-mac-framework.sh - cp build_macosx/bin/hermesc /tmp/hermes/osx-bin/. - - run: - name: Package the Hermes Apple frameworks - command: | - cd ~/react-native/sdks/hermes - . ./utils/build-apple-framework.sh + mkdir -p $HERMES_OSXBIN_ARTIFACTS_DIR ./sdks/hermes + cp -r $HERMES_WS_DIR/hermes/* ./sdks/hermes/. + - brew_install: + package: cmake + - with_hermes_tarball_cache_span: + flavor: << parameters.flavor >> + steps: + - with_hermesc_span: + flavor: << parameters.flavor >> + steps: + - run: + name: Build the Hermes Mac frameworks + command: | + cd ./sdks/hermes || exit 1 + BUILD_TYPE="<< parameters.flavor >>" ./utils/build-mac-framework.sh + - run: + name: Build the Hermes iOS frameworks + command: | + cd ./sdks/hermes || exit 1 + BUILD_TYPE="<< parameters.flavor >>" ./utils/build-ios-framework.sh + - run: + name: Package the Hermes Apple frameworks + command: | + BUILD_TYPE="<< parameters.flavor >>" + echo "Packaging Hermes Apple frameworks for $BUILD_TYPE build type" - mkdir -p /tmp/cocoapods-package-root/destroot - mkdir -p /tmp/hermes/output - cp -R ./destroot /tmp/cocoapods-package-root - cp LICENSE /tmp/cocoapods-package-root + TARBALL_OUTPUT_DIR=$(mktemp -d /tmp/hermes-tarball-output-XXXXXXXX) - tar -C /tmp/cocoapods-package-root/ -czvf /tmp/hermes/output/hermes-runtime-darwin-v$(get_release_version).tar.gz . + TARBALL_FILENAME=$(node ./scripts/hermes/get-tarball-name.js --buildType "$BUILD_TYPE") - mkdir -p /tmp/hermes/hermes-runtime-darwin - cp /tmp/hermes/output/hermes-runtime-darwin-v$(get_release_version).tar.gz /tmp/hermes/hermes-runtime-darwin/. - - save_cache: - key: *hermes_cache_key - paths: - - ~/react-native/hermes/build_host_hermesc - - ~/react-native/hermes/build_iphoneos - - ~/react-native/hermes/build_catalyst - - ~/react-native/hermes/build_iphonesimulator - - ~/react-native/hermes/build_macosx - - ~/react-native/hermes/destroot - - store_artifacts: - path: /tmp/hermes/hermes-runtime-darwin/ - - store_artifacts: - path: /tmp/hermes/osx-bin/ - - persist_to_workspace: - root: /tmp/hermes/ - paths: - - hermes-runtime-darwin - - osx-bin + echo "Packaging Hermes Apple frameworks for $BUILD_TYPE build type" + + TARBALL_OUTPUT_PATH=$(node ./scripts/hermes/create-tarball.js \ + --inputDir ./sdks/hermes \ + --buildType "$BUILD_TYPE" \ + --outputDir $TARBALL_OUTPUT_DIR) + + echo "Hermes tarball saved to $TARBALL_OUTPUT_PATH" + + mkdir -p $HERMES_TARBALL_ARTIFACTS_DIR + cp $TARBALL_OUTPUT_PATH $HERMES_TARBALL_ARTIFACTS_DIR/. + - when: + condition: + equal: [ << parameters.flavor >>, "Debug"] + steps: + - save_cache: + key: *hermes_workspace_debug_cache_key + paths: *hermes_workspace_macos_cache_paths + - when: + condition: + equal: [ << parameters.flavor >>, "Release"] + steps: + - save_cache: + key: *hermes_workspace_release_cache_key + paths: *hermes_workspace_macos_cache_paths + - store_artifacts: + path: *hermes_tarball_artifacts_dir + - store_artifacts: + path: *hermes_osxbin_artifacts_dir + - persist_to_workspace: + root: /tmp/hermes/ + paths: + - hermes-runtime-darwin + - osx-bin build_hermesc_windows: executor: @@ -1106,6 +1403,9 @@ jobs: latest: type: boolean default: false + dryrun: + type: boolean + default: false executor: reactnativeios steps: - checkout_code_with_cache @@ -1113,16 +1413,27 @@ jobs: - add_ssh_keys: fingerprints: - "1c:98:e0:3a:52:79:95:29:12:cd:b4:87:5b:41:e2:bb" + - brew_install: + package: cmake - run: name: "Set new react-native version and commit changes" command: | - node ./scripts/prepare-package-for-release.js -v << parameters.version >> -l << parameters.latest >> + VERSION=<< parameters.version >> + + if [[ -z "$VERSION" ]]; then + VERSION=$(grep '"version"' package.json | cut -d '"' -f 4 | head -1) + echo "Using the version from the package.json: $VERSION" + fi + + node ./scripts/prepare-package-for-release.js -v "$VERSION" -l << parameters.latest >> --dry-run << parameters.dryrun >> build_npm_package: parameters: - publish_npm_args: - type: string - default: --dry-run + release_type: + description: The type of release to build. Must be one of "nightly", "release", "dry-run". + type: enum + enum: ["nightly", "release", "dry-run"] + default: "dry-run" executor: reactnativeandroid environment: - HERMES_WS_DIR: *hermes_workspace_root @@ -1135,16 +1446,27 @@ jobs: - checkout - *attach_hermes_workspace - run: - name: Copy HermesC binaries + name: Copy Hermes binaries command: | mkdir -p ./sdks/hermesc ./sdks/hermesc/osx-bin ./sdks/hermesc/win64-bin ./sdks/hermesc/linux64-bin - cp -r $HERMES_WS_DIR/osx-bin/* ./sdks/hermesc/osx-bin/. + + # When build_hermes_macos runs as a matrix, it outputs + if [[ -d $HERMES_WS_DIR/osx-bin/Release ]]; then + cp -r $HERMES_WS_DIR/osx-bin/Release/* ./sdks/hermesc/osx-bin/. + elif [[ -d $HERMES_WS_DIR/osx-bin/Debug ]]; then + cp -r $HERMES_WS_DIR/osx-bin/Debug/* ./sdks/hermesc/osx-bin/. + else + ls $HERMES_WS_DIR/osx-bin || echo "hermesc macOS artifacts directory missing." + echo "Could not locate macOS hermesc binary."; exit 1; + fi + cp -r $HERMES_WS_DIR/win64-bin/* ./sdks/hermesc/win64-bin/. cp -r $HERMES_WS_DIR/linux64-bin/* ./sdks/hermesc/linux64-bin/. + mkdir -p ./ReactAndroid/external-artifacts/artifacts/ + cp $HERMES_WS_DIR/hermes-runtime-darwin/hermes-ios-debug.tar.gz ./ReactAndroid/external-artifacts/artifacts/hermes-ios-debug.tar.gz + cp $HERMES_WS_DIR/hermes-runtime-darwin/hermes-ios-release.tar.gz ./ReactAndroid/external-artifacts/artifacts/hermes-ios-release.tar.gz - run_yarn - - install_buck_tooling - - download_buck_dependencies - download_gradle_dependencies # START: Stables and nightlies @@ -1154,8 +1476,8 @@ jobs: - when: condition: or: - - equal: [ --release, << parameters.publish_npm_args >> ] - - equal: [ --nightly, << parameters.publish_npm_args >> ] + - equal: [ "release", << parameters.release_type >> ] + - equal: [ "nightly", << parameters.release_type >> ] steps: - run: echo "//registry.npmjs.org/:_authToken=${CIRCLE_NPM_TOKEN}" > ~/.npmrc - run: | @@ -1164,18 +1486,27 @@ jobs: echo "machine github.com login react-native-bot password $GITHUB_TOKEN" > ~/.netrc # END: Stables and nightlies - - run: node ./scripts/publish-npm.js << parameters.publish_npm_args >> + - run: node ./scripts/publish-npm.js --<< parameters.release_type >> - run: name: Zip Hermes Native Symbols command: zip -r /tmp/hermes-native-symbols.zip ~/react-native/ReactAndroid/hermes-engine/build/intermediates/cmake/ - store_artifacts: path: /tmp/hermes-native-symbols.zip + - run: + name: Zip Maven Artifacts from /tmp/maven-local + command: zip -r /tmp/maven-local.zip /tmp/maven-local + - store_artifacts: + path: /tmp/maven-local.zip + - persist_to_workspace: + root: /tmp + paths: + - maven-local # START: Commitlies # Provide a react-native package for this commit as a Circle CI release artifact. - when: condition: - equal: [ --dry-run, << parameters.publish_npm_args >> ] + equal: [ "dry-run", << parameters.release_type >> ] steps: - run: name: Build release package as a job artifact @@ -1199,7 +1530,6 @@ jobs: condition: matches: { pattern: '^pull\/.*$', value: << pipeline.git.branch >> } steps: - - install_github_bot_deps - run: name: Post link to PR build artifacts (pull-bot) command: GITHUB_TOKEN="$PUBLIC_PULLBOT_GITHUB_TOKEN_A""$PUBLIC_PULLBOT_GITHUB_TOKEN_B" scripts/circleci/post-artifacts-link.sh || true @@ -1208,7 +1538,7 @@ jobs: # START: Stable releases - when: condition: - equal: [ --release, << parameters.publish_npm_args >> ] + equal: [ "release", << parameters.release_type >> ] steps: - run: name: Update rn-diff-purge to generate upgrade-support diff @@ -1217,14 +1547,6 @@ jobs: -H "Accept: application/vnd.github.v3+json" \ -u "$PAT_USERNAME:$PAT_TOKEN" \ -d "{\"event_type\": \"publish\", \"client_payload\": { \"version\": \"${CIRCLE_TAG:1}\" }}" - - run: - name: Install dependencies - command: apt update && apt install -y jq jo - - run: - name: Create draft GitHub Release and upload Hermes binaries - command: | - ARTIFACTS=("$HERMES_WS_DIR/hermes-runtime-darwin/hermes-runtime-darwin-$CIRCLE_TAG.tar.gz") - ./scripts/circleci/create_github_release.sh $CIRCLE_TAG $CIRCLE_PROJECT_USERNAME $CIRCLE_PROJECT_REPONAME $GITHUB_TOKEN "${ARTIFACTS[@]}" # END: Stable releases # ------------------------- @@ -1276,12 +1598,15 @@ workflows: - build_hermes_macos: requires: - prepare_hermes_workspace + matrix: + parameters: + flavor: ["Debug", "Release"] - build_hermesc_windows: requires: - prepare_hermes_workspace - build_npm_package: # Build a release package on every untagged commit, but do not publish to npm. - publish_npm_args: --dry-run + release_type: "dry-run" requires: - build_hermesc_linux - build_hermes_macos @@ -1293,12 +1618,36 @@ workflows: - test_android_template: requires: - build_npm_package + matrix: + parameters: + architecture: ["NewArch", "OldArch"] + jsengine: ["Hermes", "JSC"] + flavor: ["Debug", "Release"] + - test_buck - test_ios_template: requires: - build_npm_package - - test_ios_rntester + matrix: + parameters: + architecture: ["NewArch", "OldArch"] + flavor: ["Debug", "Release"] + jsengine: ["Hermes", "JSC"] + flipper: ["WithFlipper", "WithoutFlipper"] + use_frameworks: [ "StaticLibraries", "StaticFrameworks" ] #TODO: make it works with DynamicFrameworks + - test_ios_rntester: + requires: + - build_hermes_macos + matrix: + parameters: + architecture: ["NewArch", "OldArch"] + jsengine: ["Hermes", "JSC"] - test_ios: run_unit_tests: true + requires: + - build_hermes_macos + matrix: + parameters: + jsengine: ["Hermes", "JSC"] # DISABLED: USE_FRAMEWORKS=1 not supported by Flipper # - test_ios: # name: test_ios_frameworks @@ -1335,6 +1684,9 @@ workflows: filters: *only_release_tags requires: - prepare_hermes_workspace + matrix: + parameters: + flavor: ["Debug", "Release"] - build_hermesc_windows: filters: *only_release_tags requires: @@ -1343,13 +1695,44 @@ workflows: - build_npm_package: name: build_and_publish_npm_package context: react-native-bot - publish_npm_args: --release + release_type: "release" filters: *only_release_tags requires: - build_hermesc_linux - build_hermes_macos - build_hermesc_windows + package_and_publish_release_dryrun: + jobs: + - prepare_package_for_release: + name: prepare_package_for_release + version: '' + latest : false + dryrun: true + - prepare_hermes_workspace: + requires: + - prepare_package_for_release + - build_hermesc_linux: + requires: + - prepare_hermes_workspace + - build_hermes_macos: + requires: + - prepare_hermes_workspace + matrix: + parameters: + flavor: ["Debug", "Release"] + - build_hermesc_windows: + requires: + - prepare_hermes_workspace + - build_npm_package: + name: build_and_publish_npm_package + context: react-native-bot + release_type: "dry-run" + requires: + - build_hermesc_linux + - build_hermes_macos + - build_hermesc_windows + analysis: unless: << pipeline.parameters.run_package_release_workflow_only >> jobs: @@ -1381,11 +1764,14 @@ workflows: - build_hermes_macos: requires: - prepare_hermes_workspace + matrix: + parameters: + flavor: ["Debug", "Release"] - build_hermesc_windows: requires: - prepare_hermes_workspace - build_npm_package: - publish_npm_args: --nightly + release_type: "nightly" requires: - build_hermesc_linux - build_hermes_macos diff --git a/.circleci/verdaccio.yml b/.circleci/verdaccio.yml new file mode 100644 index 000000000000..37e5d23b3802 --- /dev/null +++ b/.circleci/verdaccio.yml @@ -0,0 +1,44 @@ +storage: ./storage +auth: + htpasswd: + file: ./htpasswd +uplinks: + npmjs: + url: https://registry.npmjs.org/ + max_fails: 40 + maxage: 30m + timeout: 60s + fail_timeout: 10m + cache: false + agent_options: + keepAlive: true + maxSockets: 40 + maxFreeSockets: 10 +packages: + # Group and isolate all local packages, avoid being proxy from outside + '@react-native/*': + access: $all + publish: $all + # The below specific entries can be removed once they are renamed and have the @react-native prefix + '@react-native-community/eslint-config': + access: $all + publish: $all + '@react-native-community/eslint-plugin': + access: $all + publish: $all + 'react-native-codegen': + access: $all + publish: $all + 'react-native-gradle-plugin': + access: $all + publish: $all + '@*/*': + access: $all + publish: $authenticated + proxy: npmjs + '**': + access: $all + publish: $all + proxy: npmjs +logs: + - {type: file, path: verdaccio.log, format: json, level: warn} diff --git a/.eslintignore b/.eslintignore index f1beb735a483..49c1ac6eac73 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,6 +1,5 @@ **/main.js **/staticBundle.js -bots/node_modules docs/generatedComponentApiDocs.js flow/ Libraries/Renderer/* @@ -8,3 +7,4 @@ Libraries/vendor/**/* node_modules/ packages/*/node_modules packages/react-native-codegen/lib +tools/eslint/rules/sort-imports.js diff --git a/.eslintrc.js b/.eslintrc.js index 3d0dad11dfcd..1a9708475346 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -9,12 +9,16 @@ 'use strict'; +const path = require('node:path'); + +require('eslint-plugin-lint').load(path.join(__dirname, 'tools/eslint/rules')); + module.exports = { root: true, - extends: ['./packages/eslint-config-react-native-community/index.js'], + extends: ['@react-native-community'], - plugins: ['@react-native/eslint-plugin-specs'], + plugins: ['@react-native/eslint-plugin-specs', 'lint'], overrides: [ // overriding the JS config from eslint-config-react-native-community config to ensure @@ -26,8 +30,6 @@ module.exports = { // These rules are not required with hermes-eslint 'ft-flow/define-flow-type': 0, 'ft-flow/use-flow-type': 0, - 'flowtype/define-flow-type': 0, - 'flowtype/use-flow-type': 0, // flow handles this check for us, so it's not required 'no-undef': 0, }, @@ -36,15 +38,19 @@ module.exports = { { files: ['Libraries/**/*.js'], rules: { - '@react-native-community/no-haste-imports': 2, - '@react-native-community/error-subclass-name': 2, '@react-native-community/platform-colors': 2, '@react-native/specs/react-native-modules': 2, + 'lint/no-haste-imports': 2, + 'lint/no-react-native-imports': 2, + 'lint/require-extends-error': 2, + 'lint/sort-imports': 1, }, }, { files: ['flow-typed/**/*.js'], rules: { + 'lint/valid-flow-typed-signature': 2, + 'no-unused-vars': 0, quotes: 0, }, }, @@ -73,5 +79,17 @@ module.exports = { jest: true, }, }, + { + files: ['**/*.{ts,tsx}'], + parser: '@typescript-eslint/parser', + plugins: ['@typescript-eslint/eslint-plugin'], + rules: { + '@typescript-eslint/no-unused-vars': 'off', + 'react-native/no-inline-styles': 'off', + '@typescript-eslint/no-shadow': 'off', + 'no-self-compare': 'off', + 'react/self-closing-comp': 'off', + }, + }, ], }; diff --git a/.flowconfig b/.flowconfig index c2d114b4f676..9862b98d35db 100644 --- a/.flowconfig +++ b/.flowconfig @@ -6,7 +6,7 @@ /template/.* ; Ignore the Dangerfile -/bots/dangerfile.js +/packages/react-native-bots/dangerfile.js ; Ignore "BUCK" generated dirs /\.buckd/ @@ -32,7 +32,6 @@ flow/ emoji=true exact_by_default=true -exact_empty_objects=true format.bracket_spacing=false @@ -52,7 +51,7 @@ suppress_type=$FlowFixMeProps suppress_type=$FlowFixMeState suppress_type=$FlowFixMeEmpty -experimental.env_mode=ssa +inference_mode=constrain_writes [lints] sketchy-null-number=warn @@ -74,4 +73,4 @@ untyped-import untyped-type-import [version] -^0.182.0 +^0.191.0 diff --git a/.flowconfig.android b/.flowconfig.android index dc9d12aba7e9..ba1090d54053 100644 --- a/.flowconfig.android +++ b/.flowconfig.android @@ -6,7 +6,7 @@ /template/.* ; Ignore the Dangerfile -/bots/dangerfile.js +/packages/react-native-bots/dangerfile.js ; Ignore "BUCK" generated dirs /\.buckd/ @@ -32,7 +32,6 @@ flow/ emoji=true exact_by_default=true -exact_empty_objects=true format.bracket_spacing=false @@ -52,7 +51,7 @@ suppress_type=$FlowFixMeProps suppress_type=$FlowFixMeState suppress_type=$FlowFixMeEmpty -experimental.env_mode=ssa +inference_mode=constrain_writes [lints] sketchy-null-number=warn @@ -74,4 +73,4 @@ untyped-import untyped-type-import [version] -^0.182.0 +^0.191.0 diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 38dbd76f893b..113169229179 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -9,7 +9,7 @@ # GitHub Settings, Bots /.github/ @hramos -/bots @hramos +/packages/react-native-bots @hramos # Continuous Integration /.circleci/ @hramos diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 9c245257bf1e..5188c39c86ea 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -40,7 +40,7 @@ body: attributes: label: Snack, code example, screenshot, or link to a repository description: | - Please provide a Snack (https://snack.expo.io/), a link to a repository on GitHub, or provide a minimal code example that reproduces the problem. + Please provide a Snack (https://snack.expo.dev/), a link to a repository on GitHub, or provide a minimal code example that reproduces the problem. You may provide a screenshot of the application if you think it is relevant to your bug report. Here are some tips for providing a minimal example: https://stackoverflow.com/help/mcve Please note that a reproducer is mandatory. Issues without reproducer are more likely to stall and will be closed. diff --git a/.github/ISSUE_TEMPLATE/new_architecture_bug_report.yml b/.github/ISSUE_TEMPLATE/new_architecture_bug_report.yml index 8f060dd42de9..7b88c23b94e2 100644 --- a/.github/ISSUE_TEMPLATE/new_architecture_bug_report.yml +++ b/.github/ISSUE_TEMPLATE/new_architecture_bug_report.yml @@ -45,7 +45,7 @@ body: attributes: label: Snack, code example, screenshot, or link to a repository description: | - Please provide a Snack (https://snack.expo.io/), a link to a repository on GitHub, or provide a minimal code example that reproduces the problem. + Please provide a Snack (https://snack.expo.dev/), a link to a repository on GitHub, or provide a minimal code example that reproduces the problem. You may provide a screenshot of the application if you think it is relevant to your bug report. Here are some tips for providing a minimal example: https://stackoverflow.com/help/mcve Please note that a reproducer is mandatory. Issues without reproducer are more likely to stall and will be closed. diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 073b9af6d242..4b81ca2408c3 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -7,7 +7,7 @@ ## Changelog [CATEGORY] [TYPE] - Message diff --git a/.github/respond-to-issue-based-on-label.yml b/.github/respond-to-issue-based-on-label.yml index 57cc3eeb02a6..acb89311bc4f 100644 --- a/.github/respond-to-issue-based-on-label.yml +++ b/.github/respond-to-issue-based-on-label.yml @@ -46,7 +46,7 @@
:warning: Missing Reproducible Example
:information_source: - It looks like your issue is missing a reproducible example. Please provide a Snack or a repository that demonstrates the issue you are reporting in a minimal, complete, and reproducible manner. + It looks like your issue is missing a reproducible example. Please provide a Snack or a repository that demonstrates the issue you are reporting in a minimal, complete, and reproducible manner.
labels: - "Needs: Author Feedback" diff --git a/.github/workflows/autorebase.yml b/.github/workflows/autorebase.yml new file mode 100644 index 000000000000..78820f8fea4b --- /dev/null +++ b/.github/workflows/autorebase.yml @@ -0,0 +1,24 @@ +name: Automatic Rebase +on: + issue_comment: + types: [created] +permissions: + contents: read +jobs: + rebase: + permissions: + contents: write # for cirrus-actions/rebase to push code to rebase + pull-requests: read # for cirrus-actions/rebase to get info about PR + name: Rebase + if: github.event.issue.pull_request != '' && contains(github.event.comment.body, '/rebase') + runs-on: ubuntu-latest + steps: + - name: Checkout the latest code + uses: actions/checkout@v2 + with: + token: ${{ secrets.GITHUB_TOKEN }} + fetch-depth: 0 # otherwise, you will fail to push refs to dest repo + - name: Automatic Rebase + uses: cirrus-actions/rebase@1.7 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/danger_pr.yml b/.github/workflows/danger_pr.yml index d3e8072065a8..06f8a2e83020 100644 --- a/.github/workflows/danger_pr.yml +++ b/.github/workflows/danger_pr.yml @@ -1,22 +1,27 @@ name: Run Danger on PR on: - pull_request: + pull_request_target: types: [opened, edited, reopened, synchronize] permissions: - contents: read + actions: write + checks: write + contents: write + issues: write + pull-requests: write + statuses: write jobs: danger: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - - run: yarn install - working-directory: bots + - name: Run Yarn Install on Root + run: yarn install + working-directory: . - name: Danger - run: DANGER_GITHUB_API_TOKEN="$PUBLIC_PULLBOT_GITHUB_TOKEN_A""$PUBLIC_PULLBOT_GITHUB_TOKEN_B" yarn danger ci --use-github-checks --failOnErrors --id danger_pr - working-directory: bots + run: yarn danger ci --use-github-checks --failOnErrors + working-directory: packages/react-native-bots env: - PUBLIC_PULLBOT_GITHUB_TOKEN_A: a6edf8e8d40ce4e8b11a - PUBLIC_PULLBOT_GITHUB_TOKEN_B: 150e1341f4dd9c944d2a + DANGER_GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore index c954afb75a0f..01027b3802ae 100644 --- a/.gitignore +++ b/.gitignore @@ -36,6 +36,8 @@ project.xcworkspace /ReactAndroid/gradle/ /ReactAndroid/gradlew /ReactAndroid/gradlew.bat +/ReactAndroid/external-artifacts/build/ +/ReactAndroid/external-artifacts/artifacts/ /ReactAndroid/hermes-engine/build/ /ReactAndroid/hermes-engine/.cxx/ /template/android/app/build/ @@ -62,13 +64,13 @@ buck-out .gradle local.properties *.iml -/android/ +/android/* +!/android/README.md # Node node_modules *.log .nvm -/bots/node_modules/ package-lock.json # OS X @@ -106,18 +108,22 @@ package-lock.json # Ignore RNTester specific Pods, but keep the __offline_mirrors__ here. /packages/rn-tester/Pods/* -!/packages/rn-tester/Pods/__offline_mirrors__ +!/packages/rn-tester/Pods/__offline_mirrors_hermes__ +!/packages/rn-tester/Pods/__offline_mirrors_jsc__ # react-native-codegen /React/FBReactNativeSpec/FBReactNativeSpec /packages/react-native-codegen/lib +/packages/react-native-codegen/tmp/ /ReactCommon/react/renderer/components/rncore/ /packages/rn-tester/NativeModuleExample/ScreenshotManagerSpec* + # Additional SDKs /sdks/download /sdks/hermes /sdks/hermesc +/sdks/hermes-engine/build_host_hermesc # Visual studio .vscode @@ -125,3 +131,6 @@ package-lock.json # Android memory profiler files *.hprof + +# Temporary files created by Metro to check the health of the file watcher +.metro-health-check* diff --git a/.ruby-version b/.ruby-version index a603bb50a29e..49cdd668e1c8 100644 --- a/.ruby-version +++ b/.ruby-version @@ -1 +1 @@ -2.7.5 +2.7.6 diff --git a/BUCK b/BUCK index 69a9a077041b..4325eaa291ea 100644 --- a/BUCK +++ b/BUCK @@ -10,6 +10,9 @@ load( ) load( "//tools/build_defs/oss:rn_defs.bzl", + "ANDROID", + "APPLE", + "CXX", "HERMES_BYTECODE_VERSION", "IOS", "RCT_IMAGE_DATA_DECODER_SOCKET", @@ -57,9 +60,9 @@ fb_native.genrule( ) + [ react_native_root_target("packages/rn-tester:nativecomponent-srcs"), ], - labels = ["uses_hg"], - cmd = "$(exe {}) $OUT $SRCS".format(react_native_root_target("packages/react-native-codegen:write_to_json")), out = "schema-rncore.json", + cmd = "$(exe {}) $OUT $SRCS".format(react_native_root_target("packages/react-native-codegen:write_to_json")), + labels = ["uses_hg"], ) rn_codegen_components( @@ -98,7 +101,6 @@ rn_xplat_cxx_library2( labels = [ "depslint_never_remove", "pfh:ReactNative_CommonInfrastructurePlaceholder", - "supermodule:xplat/default/public.react_native.infra", ], preprocessor_flags = get_objc_arc_preprocessor_flags() + get_preprocessor_flags_for_build_mode() + [ "-DWITH_FBSYSTRACE=1", @@ -110,11 +112,10 @@ rn_xplat_cxx_library2( ":RCTCxxUtils", ":ReactInternal", "//fbobjc/Libraries/FBReactKit:RCTFBSystrace", - "//xplat/folly:molly", react_native_root_target("React/CoreModules:CoreModules"), react_native_xplat_target("cxxreact:bridge"), react_native_xplat_target("cxxreact:jsbigstring"), - react_native_xplat_target("jsi:JSCRuntime"), + react_native_xplat_target("jsc:JSCRuntime"), react_native_xplat_target("jsiexecutor:jsiexecutor"), react_native_xplat_target("reactperflogger:reactperflogger"), ], @@ -156,7 +157,6 @@ rn_xplat_cxx_library2( ], labels = [ "pfh:ReactNative_CommonInfrastructurePlaceholder", - "supermodule:xplat/default/public.react_native.infra", ], preprocessor_flags = get_objc_arc_preprocessor_flags() + get_preprocessor_flags_for_build_mode() + ["-DWITH_FBSYSTRACE=1"], visibility = ["PUBLIC"], @@ -164,7 +164,6 @@ rn_xplat_cxx_library2( ":RCTCxxUtils", ":ReactInternal", "//xplat/fbsystrace:fbsystrace", - "//xplat/folly:headers_only_do_not_use", react_native_xplat_target("cxxreact:module"), react_native_xplat_target("cxxreact:bridge"), react_native_xplat_target("reactperflogger:reactperflogger"), @@ -196,12 +195,11 @@ rn_xplat_cxx_library2( ], labels = [ "pfh:ReactNative_CommonInfrastructurePlaceholder", - "supermodule:xplat/default/public.react_native.infra", ], preprocessor_flags = get_objc_arc_preprocessor_flags() + get_preprocessor_flags_for_build_mode(), visibility = ["PUBLIC"], deps = [ - "//xplat/folly:molly", + "//xplat/folly:dynamic", ], ) @@ -224,7 +222,6 @@ rn_xplat_cxx_library2( fbobjc_enable_exceptions = True, labels = [ "pfh:ReactNative_CommonInfrastructurePlaceholder", - "supermodule:xplat/default/public.react_native.infra", ], preprocessor_flags = get_objc_arc_preprocessor_flags() + get_preprocessor_flags_for_build_mode(), visibility = ["PUBLIC"], @@ -248,14 +245,17 @@ REACT_PUBLIC_HEADERS = { "React/RCTAnimationType.h": RCTVIEWS_PATH + "RCTAnimationType.h", "React/RCTAssert.h": RCTBASE_PATH + "RCTAssert.h", "React/RCTAutoInsetsProtocol.h": RCTVIEWS_PATH + "RCTAutoInsetsProtocol.h", + "React/RCTBorderCurve.h": RCTVIEWS_PATH + "RCTBorderCurve.h", "React/RCTBorderDrawing.h": RCTVIEWS_PATH + "RCTBorderDrawing.h", "React/RCTBorderStyle.h": RCTVIEWS_PATH + "RCTBorderStyle.h", "React/RCTBridge+Private.h": RCTBASE_PATH + "RCTBridge+Private.h", "React/RCTBridge.h": RCTBASE_PATH + "RCTBridge.h", + "React/RCTBridgeConstants.h": RCTBASE_PATH + "RCTBridgeConstants.h", "React/RCTBridgeDelegate.h": RCTBASE_PATH + "RCTBridgeDelegate.h", "React/RCTBridgeMethod.h": RCTBASE_PATH + "RCTBridgeMethod.h", "React/RCTBridgeModule.h": RCTBASE_PATH + "RCTBridgeModule.h", "React/RCTBridgeModuleDecorator.h": RCTBASE_PATH + "RCTBridgeModuleDecorator.h", + "React/RCTBundleManager.h": RCTBASE_PATH + "RCTBundleManager.h", "React/RCTBundleURLProvider.h": RCTBASE_PATH + "RCTBundleURLProvider.h", "React/RCTComponent.h": RCTVIEWS_PATH + "RCTComponent.h", "React/RCTComponentData.h": RCTVIEWS_PATH + "RCTComponentData.h", @@ -267,6 +267,7 @@ REACT_PUBLIC_HEADERS = { "React/RCTDevLoadingViewProtocol.h": RCTDEVSUPPORT_PATH + "RCTDevLoadingViewProtocol.h", "React/RCTDevLoadingViewSetEnabled.h": RCTDEVSUPPORT_PATH + "RCTDevLoadingViewSetEnabled.h", "React/RCTDisplayLink.h": RCTBASE_PATH + "RCTDisplayLink.h", + "React/RCTDynamicTypeRamp.h": RCTLIB_PATH + "Text/Text/RCTDynamicTypeRamp.h", "React/RCTErrorCustomizer.h": RCTBASE_PATH + "RCTErrorCustomizer.h", "React/RCTErrorInfo.h": RCTBASE_PATH + "RCTErrorInfo.h", # NOTE: RCTEventDispatcher.h is exported from CoreModules:CoreModulesApple @@ -338,6 +339,7 @@ REACT_PUBLIC_HEADERS = { "React/RCTSurfaceView.h": RCTBASE_PATH + "Surface/RCTSurfaceView.h", "React/RCTTextDecorationLineType.h": RCTVIEWS_PATH + "RCTTextDecorationLineType.h", "React/RCTTouchHandler.h": RCTBASE_PATH + "RCTTouchHandler.h", + "React/RCTTurboModuleRegistry.h": RCTBASE_PATH + "RCTTurboModuleRegistry.h", "React/RCTUIManager.h": RCTMODULES_PATH + "RCTUIManager.h", "React/RCTUIManagerObserverCoordinator.h": RCTMODULES_PATH + "RCTUIManagerObserverCoordinator.h", "React/RCTUIManagerUtils.h": RCTMODULES_PATH + "RCTUIManagerUtils.h", @@ -429,7 +431,6 @@ rn_xplat_cxx_library2( "depslint_never_add", "depslint_never_remove", # Some old NativeModule still relies on +load unfortunately. "pfh:ReactNative_CommonInfrastructurePlaceholder", - "supermodule:xplat/default/public.react_native.infra", ], platform_preprocessor_flags = [( "linux", @@ -520,7 +521,6 @@ rn_xplat_cxx_library2( labels = [ "disable_plugins_only_validation", "pfh:ReactNative_CommonInfrastructurePlaceholder", - "supermodule:xplat/default/public.react_native.infra", ], plugins = [ react_fabric_component_plugin_provider("SafeAreaView", "RCTSafeAreaViewCls"), @@ -599,12 +599,13 @@ rn_apple_library( contacts = ["oncall+react_native@xmail.facebook.com"], extension_api_only = True, frameworks = [ - "$PLATFORM_DIR/Developer/Library/Frameworks/Foundation.framework", + "Foundation", ], inherited_buck_flags = get_static_library_ios_flags(), labels = [ + "fbios_link_group:xplat/default/public.react_native.infra", "pfh:ReactNative_CommonInfrastructurePlaceholder", - "supermodule:xplat/default/public.react_native.infra", + "talkios_link_group:xplat/default/public.react_native.infra", ], preprocessor_flags = get_objc_arc_preprocessor_flags() + get_preprocessor_flags_for_build_mode(), reexport_all_header_dependencies = True, @@ -676,7 +677,6 @@ rn_apple_library( labels = [ "disable_plugins_only_validation", "pfh:ReactNative_CommonInfrastructurePlaceholder", - "supermodule:xplat/default/public.react_native.infra", ], plugins = [react_fabric_component_plugin_provider("Image", "RCTImageCls")], visibility = ["PUBLIC"], @@ -708,7 +708,6 @@ rn_xplat_cxx_library2( contacts = ["oncall+react_native@xmail.facebook.com"], labels = [ "pfh:ReactNative_CommonInfrastructurePlaceholder", - "supermodule:xplat/default/public.react_native.infra", ], visibility = ["PUBLIC"], deps = [ @@ -741,13 +740,13 @@ rn_library( ), labels = [ "pfh:ReactNative_CommonInfrastructurePlaceholder", - "supermodule:xplat/default/public.react_native.core", ], visibility = ["PUBLIC"], deps = [ "//xplat/js:node_modules__abort_19controller", "//xplat/js:node_modules__anser", "//xplat/js:node_modules__base64_19js", + "//xplat/js:node_modules__deprecated_19react_19native_19prop_19types", "//xplat/js:node_modules__event_19target_19shim", "//xplat/js:node_modules__invariant", "//xplat/js:node_modules__memoize_19one", @@ -762,6 +761,7 @@ rn_library( "//xplat/js:node_modules__use_19sync_19external_19store", "//xplat/js:node_modules__whatwg_19fetch", "//xplat/js/RKJSModules/Libraries/Polyfills:Polyfills", + "//xplat/js/RKJSModules/Libraries/React:React", "//xplat/js/RKJSModules/vendor/react:react", "//xplat/js/RKJSModules/vendor/react-test-renderer:react-test-renderer", "//xplat/js/RKJSModules/vendor/scheduler:scheduler", @@ -779,7 +779,6 @@ rn_codegen( codegen_modules = True, ios_assume_nonnull = False, library_labels = [ - "supermodule:xplat/default/public.react_native.infra", "pfh:ReactNative_CommonInfrastructurePlaceholder", ], native_module_spec_name = "FBReactNativeSpec", @@ -792,7 +791,6 @@ rn_codegen( codegen_components = True, ios_assume_nonnull = False, library_labels = [ - "supermodule:xplat/default/public.react_native.infra", "pfh:ReactNative_CommonInfrastructurePlaceholder", ], src_prefix = "Libraries/", @@ -828,8 +826,9 @@ rn_apple_library( "depslint_never_remove", # Some old NativeModule still relies on +load unfortunately. "disable_plugins_only_validation", "extension_api_allow_unsafe_unavailable_usages", + "fbios_link_group:xplat/default/public.react_native.infra", "pfh:ReactNative_CommonInfrastructurePlaceholder", - "supermodule:xplat/default/public.react_native.infra", + "talkios_link_group:xplat/default/public.react_native.infra", ], plugins = react_module_plugin_providers( @@ -878,8 +877,8 @@ rn_apple_library( labels = [ "depslint_never_remove", # Some old NativeModule still relies on +load unfortunately. "disable_plugins_only_validation", + "fbios_link_group:xplat/default/public.react_native.infra", "pfh:ReactNative_CommonInfrastructurePlaceholder", - "supermodule:xplat/default/public.react_native.infra", ], plugins = react_module_plugin_providers( @@ -937,8 +936,9 @@ rn_apple_library( "depslint_never_remove", # Some old NativeModule still relies on +load unfortunately. "disable_plugins_only_validation", "extension_api_allow_unsafe_unavailable_usages", + "fbios_link_group:xplat/default/public.react_native.infra", "pfh:ReactNative_CommonInfrastructurePlaceholder", - "supermodule:xplat/default/public.react_native.infra", + "talkios_link_group:xplat/default/public.react_native.infra", ], plugins = react_module_plugin_providers( @@ -985,8 +985,9 @@ rn_apple_library( "depslint_never_remove", # Some old NativeModule still relies on +load unfortunately. "disable_plugins_only_validation", "extension_api_allow_unsafe_unavailable_usages", + "fbios_link_group:xplat/default/public.react_native.infra", "pfh:ReactNative_CommonInfrastructurePlaceholder", - "supermodule:xplat/default/public.react_native.infra", + "talkios_link_group:xplat/default/public.react_native.infra", ], plugins = react_module_plugin_providers( @@ -1037,8 +1038,9 @@ rn_apple_library( "depslint_never_remove", # Some old NativeModule still relies on +load unfortunately. "disable_plugins_only_validation", "extension_api_allow_unsafe_unavailable_usages", + "fbios_link_group:xplat/default/public.react_native.infra", "pfh:ReactNative_CommonInfrastructurePlaceholder", - "supermodule:xplat/default/public.react_native.infra", + "talkios_link_group:xplat/default/public.react_native.infra", ], plugins = react_module_plugin_providers( @@ -1118,8 +1120,9 @@ rn_apple_library( "depslint_never_remove", # Some old NativeModule still relies on +load unfortunately. "disable_plugins_only_validation", "extension_api_allow_unsafe_unavailable_usages", + "fbios_link_group:xplat/default/public.react_native.infra", "pfh:ReactNative_CommonInfrastructurePlaceholder", - "supermodule:xplat/default/public.react_native.infra", + "talkios_link_group:xplat/default/public.react_native.infra", ], plugins = react_module_plugin_providers( @@ -1253,7 +1256,6 @@ rn_xplat_cxx_library2( labels = [ "depslint_never_remove", # Some old NativeModule still relies on +load unfortunately. "pfh:ReactNative_CommonInfrastructurePlaceholder", - "supermodule:xplat/default/public.react_native.infra", ], preprocessor_flags = get_objc_arc_preprocessor_flags() + get_preprocessor_flags_for_build_mode(), visibility = ["PUBLIC"], @@ -1285,8 +1287,8 @@ rn_apple_library( labels = [ "depslint_never_remove", "disable_plugins_only_validation", + "fbios_link_group:xplat/default/public.react_native.infra", "pfh:ReactNative_CommonInfrastructurePlaceholder", - "supermodule:xplat/default/public.react_native.infra", ], plugins = react_module_plugin_providers( name = "Vibration", @@ -1327,7 +1329,6 @@ rn_xplat_cxx_library2( labels = [ "depslint_never_remove", # Some old NativeModule still relies on +load unfortunately. "pfh:ReactNative_CommonInfrastructurePlaceholder", - "supermodule:xplat/default/public.react_native.infra", ], preprocessor_flags = get_objc_arc_preprocessor_flags() + get_preprocessor_flags_for_build_mode(), visibility = ["PUBLIC"], @@ -1360,7 +1361,6 @@ rn_xplat_cxx_library2( labels = [ "depslint_never_remove", "pfh:ReactNative_CommonInfrastructurePlaceholder", - "supermodule:xplat/default/public.react_native.infra", ], preprocessor_flags = get_objc_arc_preprocessor_flags() + get_preprocessor_flags_for_build_mode(), visibility = ["PUBLIC"], @@ -1395,7 +1395,6 @@ rn_xplat_cxx_library2( labels = [ "depslint_never_remove", "pfh:ReactNative_CommonInfrastructurePlaceholder", - "supermodule:xplat/default/public.react_native.infra", ], preprocessor_flags = get_objc_arc_preprocessor_flags() + get_preprocessor_flags_for_build_mode(), visibility = ["PUBLIC"], @@ -1431,7 +1430,6 @@ rn_xplat_cxx_library2( labels = [ "depslint_never_remove", "pfh:ReactNative_CommonInfrastructurePlaceholder", - "supermodule:xplat/default/public.react_native.infra", ], preprocessor_flags = get_objc_arc_preprocessor_flags() + get_preprocessor_flags_for_build_mode(), visibility = ["PUBLIC"], @@ -1453,10 +1451,34 @@ rn_xplat_cxx_library2( }, labels = [ "pfh:ReactNative_CommonInfrastructurePlaceholder", - "supermodule:xplat/default/public.react_native.infra", ], visibility = [ "//fbobjc/Libraries/FBReactKit:RCTMapView", "//fbobjc/VendorLib/react-native-maps:react-native-maps", ], ) + +rn_xplat_cxx_library2( + name = "RCTWebPerformance", + srcs = glob([ + "Libraries/WebPerformance/**/*.cpp", + ]), + header_namespace = "", + exported_headers = subdir_glob( + [("Libraries/WebPerformance", "*.h")], + prefix = "RCTWebPerformance", + ), + fbandroid_compiler_flags = [ + "-fexceptions", + "-frtti", + ], + labels = [ + "depslint_never_remove", + "pfh:ReactNative_CommonInfrastructurePlaceholder", + ], + platforms = (ANDROID, APPLE, CXX), + visibility = ["PUBLIC"], + deps = [ + ":FBReactNativeSpecJSI", + ], +) diff --git a/CHANGELOG.md b/CHANGELOG.md index 255539250f8b..6b190ae10648 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,320 @@ # Changelog +## v0.70.3 + +### Fixed + +- Stop styles from being reset when detaching Animated.Values in old renderer ([2f58e52006](https://github.com/facebook/react-native/commit/2f58e520061a31ab90f7bbeef59e2bf723605106) by [@javache](https://github.com/javache)) +- Revert "Fix TextInput dropping text when used as uncontrolled component with `defaultValue`" to fix TextInputs not being settable to undefined programmatically ([e2645a5](https://github.com/facebook/react-native/commit/e2645a59f6211116d2069967443502910c167d6f)) by Garrett Forbes Monroe + +#### Android specific + +- Use NMake generator for Hermes build on Windows ([9d08d55bbe](https://github.com/facebook/react-native/commit/9d08d55bbef4e79a8843deef90bef828f7b9a6ef) by [@mganandraj](https://github.com/mganandraj)) +- Fixing failure building RN codegen CLI on Windows ([85c0c0f21f](https://github.com/facebook/react-native/commit/85c0c0f21fdb52543e603687a3c42dc40dff572b) by [@mganandraj](https://github.com/mganandraj)) + +#### iOS specific + +- Add xcode 14 workaround (turn off signing resource bundles) for `React-Core` ([967de03f30](https://github.com/facebook/react-native/commit/967de03f304404ac8817936da37ca39514a09e33) by [@kelset](https://github.com/kelset)) + +## v0.70.2 + +### Added + +#### iOS specific + +- Add support for "Prefer Cross-Fade Transitions" into AccessibilityInfo ([be7c50fefd](https://github.com/facebook/react-native/commit/be7c50fefd7f13201fb538ded93d91b374341173) by [@gabrieldonadel](https://github.com/gabrieldonadel)) + +### Changed + +- Bump CLI to 9.1.3 and Metro to 0.72.3 ([f164556037](https://github.com/facebook/react-native/commit/f1645560376b734a87f0eba1fef69f6cba312cc1) by [@kelset](https://github.com/kelset)) + +### Fixed + +- Inform ScrollView of Keyboard Events Before Mount ([26d148029c](https://github.com/facebook/react-native/commit/26d148029c7fde117f33b0d6c8b34286c45a0ef2) by [@NickGerleman](https://github.com/NickGerleman)) + +#### Android specific + +- Fix port as -1 if dev server without specifying port on Android ([3d7e1380b4](https://github.com/facebook/react-native/commit/3d7e1380b4e609f5340ee80c19d566b17e620427) by [@Kudo](https://github.com/Kudo)) + +## v0.70.1 + +### Added + +- Add more debugging settings for *HermesExecutorFactory* ([32d12e89f8](https://github.com/facebook/react-native/commit/32d12e89f864a106433c8e54c10691d7876333ee) by [@Kudo](https://github.com/Kudo)) +- Support TypeScript array types for turbo module (component only) ([33d1291e1a](https://github.com/facebook/react-native/commit/33d1291e1a96497a4f994e9d622248a745ee1ea6) by [@ZihanChen-MSFT](https://github.com/ZihanChen-MSFT)) + +### Changed + +- Accept TypeScript type `T | null | undefined` as a maybe type of T in turbo module. ([9ecd203eec](https://github.com/facebook/react-native/commit/9ecd203eec97e7d21d10311d950c9f8f30c7a4b1) by [@ZihanChen-MSFT](https://github.com/ZihanChen-MSFT)) +- Bump react-native-gradle-plugin to 0.70.3 ([e33633644c](https://github.com/facebook/react-native/commit/e33633644c70ea39af6e450fcf31d9458051fd5f) by [@dmytrorykun](https://github.com/dmytrorykun)) +- Bump react-native-codegen to 0.70.5 ([6a8c38eef2](https://github.com/facebook/react-native/commit/6a8c38eef272e79e52a35941afa9c3fe9e8fc191) by [@dmytrorykun](https://github.com/dmytrorykun)) +- Hermes version bump for 0.70.1 ([5132211228](https://github.com/facebook/react-native/commit/5132211228a5b9e36d58c1f7e2c99ccaabe1ba3d) by [@dmytrorykun](https://github.com/dmytrorykun)) + +### Fixed + +- Fix hermes profiler ([81564c1a3d](https://github.com/facebook/react-native/commit/81564c1a3dae4222858de2a9a34089097f665e82) by [@janicduplessis](https://github.com/janicduplessis)) + +#### Android specific + +- Support PlatformColor in borderColor ([2d5db284b0](https://github.com/facebook/react-native/commit/2d5db284b061aec33af671b25065632e20217f62) by [@danilobuerger](https://github.com/danilobuerger)) +- Avoid crash in ForwardingCookieHandler if webview is disabled ([5451cd48bd](https://github.com/facebook/react-native/commit/5451cd48bd0166ba70d516e3a11c6786bc22171a) by [@Pajn](https://github.com/Pajn)) +- Correctly resolve classes with FindClass(..) ([361b310bcc](https://github.com/facebook/react-native/commit/361b310bcc8dddbff42cf63495649291c894d661) by [@evancharlton](https://github.com/evancharlton)) + +#### iOS specific + +- Fix KeyboardAvoidingView height when "Prefer Cross-Fade Transitions" is enabled ([4b9382c250](https://github.com/facebook/react-native/commit/4b9382c250261aab89b271618f8b68083ba01785) by [@gabrieldonadel](https://github.com/gabrieldonadel)) +- Fix React module build error with swift integration on new architecture mode ([3afef3c167](https://github.com/facebook/react-native/commit/3afef3c16702cefa5115b059a08741fba255b2db) by [@Kudo](https://github.com/Kudo)) +- Fix ios pod install error ([0cae4959b7](https://github.com/facebook/react-native/commit/0cae4959b750ea051dcd04e4c9374e02b1de6e7a) by [@Romick2005](https://github.com/Romick2005)) + +## 0.70.0 + +### Breaking + +- Remove jest/preprocessor from the react-native package ([0301cb285b](https://github.com/facebook/react-native/commit/0301cb285b2e85b48a397fe58d565196654d9754) by [@motiz88](https://github.com/motiz88)) +- Remove nonstandard Promise.prototype.done ([018d5cf985](https://github.com/facebook/react-native/commit/018d5cf985497273dd700b56168cf1cf64f498d5) by [@motiz88](https://github.com/motiz88)) + +### Added + +- Support TypeScript array types for turbo module (module only) ([f0c4c291e1](https://github.com/facebook/react-native/commit/f0c4c291e12a8e76f91d3841d65291f0f1f16714) by [@ZihanChen-MSFT](https://github.com/ZihanChen-MSFT)) +- Added files for `avn`, `nodenv`, and other managers that set the node.js version in reactive native project including testing ([933fbb1b2b](https://github.com/facebook/react-native/commit/933fbb1b2b4d2b7c802bf1f2be4c47e5b442a850) by [@ramonmedel](https://github.com/ramonmedel)) +- Support BigInt in Hermes ([11bae63bb1](https://github.com/facebook/react-native/commit/11bae63bb1f833802ec6ce01342ebdd1d61e9252) by [@jpporto](https://github.com/jpporto)) +- The old Hermes instrumented stats migrated to the new one ([c37f719567](https://github.com/facebook/react-native/commit/c37f7195675df67d23c3c008ec5ab5fd7b8d0394) by [@jpporto](https://github.com/jpporto)) +- Modified **getDefaultJSExecutorFactory** method ([87cfd386cb](https://github.com/facebook/react-native/commit/87cfd386cb2e02bfa440c94706d9d0274f83070c) by [@KunalFarmah98](https://github.com/KunalFarmah98)) +- `EventEmitter#emit` now freezes the set of listeners before iterating over them, meaning listeners that are added or removed will not affect that iteration. ([e5c5dcd9e2](https://github.com/facebook/react-native/commit/e5c5dcd9e26e9443f59864d9763b049e0bda98e7) by [@yungsters](https://github.com/yungsters)) +- Added File and Blob globals to eslint community config ([d881c87231](https://github.com/facebook/react-native/commit/d881c872314e55e17b198a41c86528d79092d222) by [@shamilovtim](https://github.com/shamilovtim)) +- C++ TurboModule methods can now use mixed types ([3c569f546c](https://github.com/facebook/react-native/commit/3c569f546ca78b23fbcb9773a1273dd9710f8c60) by [@appden](https://github.com/appden)) +- Add useNativeDriver as a param for setValue for Animated ([73191edb72](https://github.com/facebook/react-native/commit/73191edb7255b1ba5e9a0955a25c14250186a676) by [@genkikondo](https://github.com/genkikondo)) +- Add `Animated.Numeric` Flow type ([9eb7629ac6](https://github.com/facebook/react-native/commit/9eb7629ac66abc23b91b81d420891d68bbd4f578) by [@motiz88](https://github.com/motiz88)) +- Add LTI annotations to function params ([c940eb0c49](https://github.com/facebook/react-native/commit/c940eb0c49518b82a3999dcac3027aa70018c763), [e7a4dbcefc](https://github.com/facebook/react-native/commit/e7a4dbcefc9e393c41f4a796d522211bc1e60b6f), [d96744e277](https://github.com/facebook/react-native/commit/d96744e27711c4fa4dfad1b5a796283a232e60af) by [@pieterv](https://github.com/pieterv)) + +#### Android specific + +- Accessibility announcement for list and grid in FlatList ([2d5882132f](https://github.com/facebook/react-native/commit/2d5882132fb2c533fe9bbba83576b8fac4aca727), [105a2397b6](https://github.com/facebook/react-native/commit/105a2397b6b187a9669ba1c028508a7bb9664009) by [@fabriziobertoglio1987](https://github.com/fabriziobertoglio1987)) +- Add READ_VOICEMAIL and WRITE_VOICEMAIL permissions to PermisionsAndroid library. ([8a2be3e143](https://github.com/facebook/react-native/commit/8a2be3e1438dd145ccb5374d6ef60811047d23aa) by [@zolbooo](https://github.com/zolbooo)) +- Add POST_NOTIFICATIONS, NEARBY_WIFI_DEVICES permission ([0a854c7c8b](https://github.com/facebook/react-native/commit/0a854c7c8b7ffc382c43fa460651a4b4de34c3c7) by [@vincent-paing](https://github.com/vincent-paing)) +- Extend the React Native Gradle plugin to accept a config from package.json ([5f3c5aa529](https://github.com/facebook/react-native/commit/5f3c5aa529ed75414eb339c3d8fd2c9628534621) by [@cortinico](https://github.com/cortinico)) +- Ability to pass a Typeface object to ReactFontManager in addition to a font resource ID ([e2dd2e2a6e](https://github.com/facebook/react-native/commit/e2dd2e2a6ed17b366a3e2ec0942ea1d82a404c5d) by [@thurn](https://github.com/thurn)) +- Option to enable lazyViewManager support with `ViewManagerOnDemandReactPackage` ([d4b59cd9d0](https://github.com/facebook/react-native/commit/d4b59cd9d02a8c4eda3ac4bf89cfe8161847adf0) by [@javache](https://github.com/javache)) +- Support for dataUri in form data ([c663c0ec9d](https://github.com/facebook/react-native/commit/c663c0ec9deee7281f819f222bb29ad79e99f3b8) by [@hetanthakkar1](https://github.com/hetanthakkar1)) +- Add android-only prop documentation at the TextInput js level. ([f2e23215ca](https://github.com/facebook/react-native/commit/f2e23215ca14c3c630aa931cdd114187589ac0fb)) +- Update template to gitignore `android/app/.cxx` ([542d43df9d](https://github.com/facebook/react-native/commit/542d43df9d84a88f742c273391f2596546b4c804) by [@leotm](https://github.com/leotm)) + +#### iOS specific + +- Add Mac Catalyst compatibility (can be enabled in Podfile) ([2fb6a3393d](https://github.com/facebook/react-native/commit/2fb6a3393d545a93518d1b2906bd9453458660a0) by [@Arkkeeper](https://github.com/Arkkeeper)) +- Enabled Hermes Intl ([3fa3aeba93](https://github.com/facebook/react-native/commit/3fa3aeba93f226b97e324f3643b98382947e5985) by [@neildhar](https://github.com/neildhar)) +- HTTP Response headers added to the error object passed to JS code. ([9eb2826f9b](https://github.com/facebook/react-native/commit/9eb2826f9beac5b7476f33e68803ca5a024867db)) +- Add userInterfaceStyle to Alert to override user interface style for iOS 13+ ([47bd78f64f](https://github.com/facebook/react-native/commit/47bd78f64f334b770edc7fabd4b9cceb07a7a503) by [@luoxuhai](https://github.com/luoxuhai)) +- Add function to cleanup codegen folders ([71692889b0](https://github.com/facebook/react-native/commit/71692889b0d89b033c07ef87ee3dbf6d62d79235) by [@cipolleschi](https://github.com/cipolleschi)) +- Cocoapods function to add the `CLANG_CXX_LANGUAGE_STANDARD` to all the targets if needed ([ca8174e15f](https://github.com/facebook/react-native/commit/ca8174e15f77cbeecb7ff7a5a583abb668817777) by [@f-meloni](https://github.com/f-meloni)) +- Support codegen from a single folder ([05aaba9514](https://github.com/facebook/react-native/commit/05aaba95145df0b7f541e391a9f64ba3402cac35) by [@cipolleschi](https://github.com/cipolleschi)) +- Run script phases tests in CI ([c171a6e157](https://github.com/facebook/react-native/commit/c171a6e1572f64b2ab9b26d431c07581d4ae832b) by [@cipolleschi](https://github.com/cipolleschi)) + +### Changed + +- Bump React Native Codegen to 0.70.0 ([a22ceecc84](https://github.com/facebook/react-native/commit/a22ceecc849fc62c926643f4d121cf1e4575c693) by [@dmytrorykun](https://github.com/dmytrorykun), [2a274c1a08](https://github.com/facebook/react-native/commit/2a274c1a082c3291d2df1a4b960bf654e217a4dd) by [@cortinico](https://github.com/cortinico), [ce4246a05c](https://github.com/facebook/react-native/commit/ce4246a05c96cd6fe805499960b105267ac044bb) by [@dmytrorykun](https://github.com/dmytrorykun)) +- Upgrade RN CLI to v9.0.0, Metro to 0.72.1 ([0c2fe96998](https://github.com/facebook/react-native/commit/0c2fe969984fff0676f99fe034b3e49d38ed7db6) by [@thymikee](https://github.com/thymikee), [7e580b97bf](https://github.com/facebook/react-native/commit/7e580b97bf63436978d053926e04adeb9ae6f75f) by [@kelset](https://github.com/kelset), [c504d038c4](https://github.com/facebook/react-native/commit/c504d038c470f7a13fb345f57261172c7c85248c) by [@thymikee](https://github.com/thymikee), [f1d624823f](https://github.com/facebook/react-native/commit/f1d624823fe23eb3d30de00cf78beb71dc1b8413) by [@kelset](https://github.com/kelset), [2b49ac6f8b](https://github.com/facebook/react-native/commit/2b49ac6f8b04953be4cd5bf0b1325986b117763c) by [@thymikee](https://github.com/thymikee)) +- Doc: fix minimum iOS version in requirements section ([ec3c8f4380](https://github.com/facebook/react-native/commit/ec3c8f43800a027a0a717367360421089e7293fd) by [@Simon-TechForm](https://github.com/Simon-TechForm)) +- Remove "Early" in Js error reporting pipeline ([0646551d76](https://github.com/facebook/react-native/commit/0646551d7690cd54847eb468f8e43d71ebebdda9) by [@sshic](https://github.com/sshic)) +- Update @react-native/eslint-plugin-specs to 0.70.0 ([d07fae9b23](https://github.com/facebook/react-native/commit/d07fae9b23c258a60045b666167efd5259b962ce), [afd76f69c7](https://github.com/facebook/react-native/commit/afd76f69c7d2408654ba67ac2ed4d612abfbe0ce) by [@dmytrorykun](https://github.com/dmytrorykun), [ea8d8e2f49](https://github.com/facebook/react-native/commit/ea8d8e2f49ea3ce15faeab500b661a1cacacf8a8) by [@cortinico](https://github.com/cortinico)) +- Do not depend on hermes-engine NPM package anymore ([78cd689f9a](https://github.com/facebook/react-native/commit/78cd689f9a634b152ea09ed6cb4fa858ee26e653) by [@cortinico](https://github.com/cortinico)) +- Add ability to pass `ItemSeparatorComponent` as React Element ([5854b11bf9](https://github.com/facebook/react-native/commit/5854b11bf9d42bab9dbe62b9152a3d3a94e42c13) by [@retyui](https://github.com/retyui)) +- Hermes version bump. ([0b4b7774e2](https://github.com/facebook/react-native/commit/0b4b7774e2d71259962ed36b7acb5c3989c3be9c) by [@dmytrorykun](https://github.com/dmytrorykun), [8c682ddd59](https://github.com/facebook/react-native/commit/8c682ddd599b75a547975104cb6f90eec8753daf) by [@dmytrorykun](https://github.com/dmytrorykun), [eb6767813a](https://github.com/facebook/react-native/commit/eb6767813a0efe04a9e79955b8f6ee909a4a76bf) by [@cortinico](https://github.com/cortinico)) +- Codemod `{...null}` to `{}` in xplat/js ([f392ba6725](https://github.com/facebook/react-native/commit/f392ba67254e95126974fafabf3e4ef0300e24e8) by [@gkz](https://github.com/gkz)) +- Fix TextInput dropping text when used as uncontrolled component with `defaultValue` ([51f49ca998](https://github.com/facebook/react-native/commit/51f49ca9982f24de08f5a5654a5210e547bb5b86)) +- Suppress errors ahead of launch ([67e12a19cb](https://github.com/facebook/react-native/commit/67e12a19cb236fbe0809fbbc9e516b37a5848a6a) by [@gkz](https://github.com/gkz)) +- Suppress missing 'this' annotations ([6c563a507f](https://github.com/facebook/react-native/commit/6c563a507fd8c41e04a1e62e2ba87993c6eb1e2f) by [@pieterv](https://github.com/pieterv)) +- Suppress missing annotations ([66c6a75650](https://github.com/facebook/react-native/commit/66c6a75650f91d61e7e87a8e661d87101e26cf9c) by [@pieterv](https://github.com/pieterv)) +- Use RuntimeConfig instead of VM Experiment Flag to set up the micro task queue. ([753038cf34](https://github.com/facebook/react-native/commit/753038cf345a45d95ab9b9343447f524e1b36840) by [@fbmal7](https://github.com/fbmal7)) +- Update direct Metro dependencies to 0.72.0 ([05dcebc211](https://github.com/facebook/react-native/commit/05dcebc21175a78c6533a8856aed644c45276169) by [@kelset](https://github.com/kelset), [64788cc9fe](https://github.com/facebook/react-native/commit/64788cc9fe42fbedc3e3b1c9c516a079cfa71cd1) by [@huntie](https://github.com/huntie), [bdeb4e0655](https://github.com/facebook/react-native/commit/bdeb4e065532dfb1bb4c9c1e87e8a869a737e48a) by [@jacdebug](https://github.com/jacdebug), [894f652639](https://github.com/facebook/react-native/commit/894f6526399098d825ef32c02eb201cd8ba41873) by [@robhogan](https://github.com/robhogan), [08ebc1cfd8](https://github.com/facebook/react-native/commit/08ebc1cfd88a629389c43abf23b40a2bdf1b1579) by [@arushikesarwani94](https://github.com/arushikesarwani94)) +- ECOSYSTEM.md: update Partner entries ([5471afeebf](https://github.com/facebook/react-native/commit/5471afeebf59853ce31df1ade6a4591414b6aa2f) by [@Simek](https://github.com/Simek)) +- Move ScrollView's contentOffset to common props ([7c581f3d30](https://github.com/facebook/react-native/commit/7c581f3d3007954413d68daf2e868ce93e120615) by [@genkikondo](https://github.com/genkikondo)) +- Upgrade react-native-gradle-plugin to 0.70.2 ([1518f838b7](https://github.com/facebook/react-native/commit/1518f838b70951882f7b414c90407d3eb584cab4), [2176173dcc](https://github.com/facebook/react-native/commit/2176173dcc029ab21bfcdfe5c9e150581db47409) by [@dmytrorykun](https://github.com/dmytrorykun)) +- Update a few metro deps as follow up from the commit from main ([7c7ba1babd](https://github.com/facebook/react-native/commit/7c7ba1babd41b6b60f0dc9f48c34d00235d2fef5) by [@kelset](https://github.com/kelset)) + +#### Android specific + +- Bump Android Gradle Plugin to 7.2.1 ([53c8fc9488](https://github.com/facebook/react-native/commit/53c8fc94882893dd8c337fd29543ae11fd467267) by [@leotm](https://github.com/leotm), [c274456e5b](https://github.com/facebook/react-native/commit/c274456e5b635825560852baa5787e96640800d8) by [@dulmandakh](https://github.com/dulmandakh)) +- Rename NativeModuleCallExceptionHandler to JSExceptionHandler for broader usage ([b6f7689d70](https://github.com/facebook/react-native/commit/b6f7689d701d0409c23ab364356aeb95710c20fa) by [@sshic](https://github.com/sshic)) +- Simplify the Android.mk file in the App Template ([7fb0bb40d2](https://github.com/facebook/react-native/commit/7fb0bb40d2206c734a1feb6b555c22d6d5f2436e) by [@cortinico](https://github.com/cortinico)) +- Make Hermes the default engine on Android ([a7db8df207](https://github.com/facebook/react-native/commit/a7db8df2076f68ae9451ce1c77d7eb09d8cfeb14) by [@cortinico](https://github.com/cortinico)) +- Revamp touch event dispatching methods ([089ff4555a](https://github.com/facebook/react-native/commit/089ff4555af27eec4561b1627298702b4ecee482) by [@sshic](https://github.com/sshic)) +- Demonstrating Dark Mode correctly in the `StatusBar` for the starter template App. ([763dc52387](https://github.com/facebook/react-native/commit/763dc5238721a21847b6d6670b5fa268e3bf2ed4) by [@mrbrentkelly](https://github.com/mrbrentkelly)) +- Bump Gradle to 7.5.0 ([5c8186623a](https://github.com/facebook/react-native/commit/5c8186623ae15388949cfc4143edae86863a447b) by [@leotm](https://github.com/leotm), [99e7373dd2](https://github.com/facebook/react-native/commit/99e7373dd2f20184153377109e0e8e48b5bf46f7) by [@dulmandakh](https://github.com/dulmandakh)) +- Generalized the return type of ViewManagerOnDemandReactPackage.getViewManagerNames ([51e029ec3c](https://github.com/facebook/react-native/commit/51e029ec3ce42ae8df3d367d8f553ec2148eeafc) by [@javache](https://github.com/javache)) +- Don't assert on current activity when call startActivityForResult ([bf6884dc90](https://github.com/facebook/react-native/commit/bf6884dc903154ae32daa50ce7983a9f014be781) by [@sshic](https://github.com/sshic)) +- Adapt template to new architecture autolinking on Android ([9ad7cbc3eb](https://github.com/facebook/react-native/commit/9ad7cbc3eb365190e0bfe290e1025553a807b298) by [@thymikee](https://github.com/thymikee)) +- Replaced reactnativeutilsjni with reactnativejni in the build process to reduce size ([54a4fcbfdc](https://github.com/facebook/react-native/commit/54a4fcbfdcc8111b3010b2c31ed3c1d48632ce4c) by [@SparshaSaha](https://github.com/SparshaSaha)) +- Bump Soloader to 0.10.4 ([b9adf2db20](https://github.com/facebook/react-native/commit/b9adf2db20bf9e1436fa58182d886fd9461df9af) by [@mikehardy](https://github.com/mikehardy)) +- Update the new app template to use CMake instead of Android.mk ([dfd7f70eff](https://github.com/facebook/react-native/commit/dfd7f70effeacfeb06d9c2d4762a279a079ee004) by [@cortinico](https://github.com/cortinico)) +- Refactored usage of kotlin plugin ([be35c6dafb](https://github.com/facebook/react-native/commit/be35c6dafbdb46d2ec165460d4bb12f34de6e878) by [@hurali97](https://github.com/hurali97)) +- Bump Gradle to 7.5.1 ([7a911e0730](https://github.com/facebook/react-native/commit/7a911e073094b533cb5a7ce76932c02f83f4fe5d) by [@AlexanderEggers](https://github.com/AlexanderEggers)) +- Fix error of release builds with Hermes enabled for Windows users ([7fcdb9d9d8](https://github.com/facebook/react-native/commit/7fcdb9d9d8f964d24a5ec3d423c67f49b7650ed8) by [@JoseLion](https://github.com/JoseLion)) + +#### iOS specific + +- Move and test Hermes setup from react_native_pods script into a dedicated file ([468b86bd37](https://github.com/facebook/react-native/commit/468b86bd3710b1d43a492c94fb314cc472f03b86) by [@cipolleschi](https://github.com/cipolleschi)) +- Use the correct operator to decide whether Hermes is enabled or not. ([61488449b9](https://github.com/facebook/react-native/commit/61488449b996da5881e4711e0813e9c90b6e57a1) by [@cipolleschi](https://github.com/cipolleschi)) +- Hermes is now the default engine on iOS. This setting is controlled via `flags[:hermes_enabled]` in the Podfile. ([1115bc77db](https://github.com/facebook/react-native/commit/1115bc77db1090042effc021837f70b28694fa09) by [@hramos](https://github.com/hramos)) +- Move LocalPodspecPatch to dedicated file ([8fe2b591c7](https://github.com/facebook/react-native/commit/8fe2b591c7e073d629e95cd7b67aa1dfa96ece38) by [@cipolleschi](https://github.com/cipolleschi)) +- Move the `modify_flags_for_new_architecture` method to separate ruby file ([71da21243c](https://github.com/facebook/react-native/commit/71da21243c85283445c6cefa64d1ace13823ab69) by [@cipolleschi](https://github.com/cipolleschi)) +- Refactoring part of the react_native_pods.rb script ([4f732ba9ee](https://github.com/facebook/react-native/commit/4f732ba9ee2a1e162729c97d5c12ea771b3a424a), [7a2704455f](https://github.com/facebook/react-native/commit/7a2704455f3edf203d2ecc8135fabf2667f718d8) by [@cipolleschi](https://github.com/cipolleschi)) +- When Hermes is enabled, it will use the same copy of JSI as React Native ([340612a200](https://github.com/facebook/react-native/commit/340612a200505ca829bae1f9bce800d3673dac04) by [@hramos](https://github.com/hramos)) +- Move `use_flipper` logic inside `use_react_native` and simplify the Flipper dependencies logic ([0bd5239553](https://github.com/facebook/react-native/commit/0bd523955385a3b1e622077b7ee4ea0df3c5c158) by [@f-meloni](https://github.com/f-meloni)) +- Export `flipper.rb` script file ([e07a7eb16b](https://github.com/facebook/react-native/commit/e07a7eb16b97e1222e23f935a3d4bb3dac848ef2) by [@cipolleschi](https://github.com/cipolleschi)) +- Use `outputDir` as base directory for the codegen and remove the possibility to customize the intermediate path. The generated code requires specific paths in the `#include` directive. ([e4d0153a67](https://github.com/facebook/react-native/commit/e4d0153a675fbdd8718f433b2e9f8bfdccec4b2f) by [@cipolleschi](https://github.com/cipolleschi)) +- Refactor part of the codegen scripts and add tests. ([0465c3fd10](https://github.com/facebook/react-native/commit/0465c3fd102525b005826f3c68923d7e9851d6b8), [305a054865](https://github.com/facebook/react-native/commit/305a0548652a405d9f638fb2c054781951dfc996) by [@cipolleschi](https://github.com/cipolleschi)) +- CodeGen now supports the `"all"` library type. ([6718500eaa](https://github.com/facebook/react-native/commit/6718500eaaeb92b8a74320dcee961ac96f6f12fa) by [@cipolleschi](https://github.com/cipolleschi)) +- Fix the test_ios_unit test ([fdbe4719e2](https://github.com/facebook/react-native/commit/fdbe4719e2e2b599e86d42c49d42c4da97ef431a) by [@cipolleschi](https://github.com/cipolleschi)) +- Update Podfile to allow `PRODUCTION=1 pod install` ([77752fc403](https://github.com/facebook/react-native/commit/77752fc4037e66d5b0a5851bae79c4d3285ed334) by [@leotm](https://github.com/leotm)) +- Destruct use_reactnative parameters and ad ddocumentation ([79a37e5a88](https://github.com/facebook/react-native/commit/79a37e5a88e179090ade7145a453a46719c87b3f) by [@cipolleschi](https://github.com/cipolleschi)) +- Move codegen in separate files ([7d069b2583](https://github.com/facebook/react-native/commit/7d069b25835ad20654a46ebb1e09631d826598e0) by [@cipolleschi](https://github.com/cipolleschi)) +- Silence warning due to react-native internal details. ([a4599225f5](https://github.com/facebook/react-native/commit/a4599225f5a6a2d6801dd80b7728c1bbe5b2ec3a) by [@cipolleschi](https://github.com/cipolleschi)) + +### Removed + +- Remove previously deprecated Transform style-attribute props ([7cfd77debd](https://github.com/facebook/react-native/commit/7cfd77debd36f867f5ddfdb9cc44fbe6137aaeba) by [@Zachinquarantine](https://github.com/Zachinquarantine)) +- Remove deprecated `isTVOS` constant. ([6075d64acf](https://github.com/facebook/react-native/commit/6075d64acf6f8d74e18ef6568c9438f73fe56d44) by [@Zachinquarantine](https://github.com/Zachinquarantine)) +- The diffs renames the required variable which was causing conflicts in names with Apple core SDK's ([086c13dd5f](https://github.com/facebook/react-native/commit/086c13dd5fba3f77acbc70c9bdedc9299118b526) by [@arinjay](https://github.com/arinjay)) +- Removed `EventEmitter.prototype.removeSubscription` method. ([870755fa7e](https://github.com/facebook/react-native/commit/870755fa7e7011ac46d269d5fb66d2a1d1543442) by [@yungsters](https://github.com/yungsters)) +- Remove deprecated removeListener methods ([2596b2f695](https://github.com/facebook/react-native/commit/2596b2f6954362d2cd34a1be870810ab90cbb916) by [@matinzd](https://github.com/matinzd)) +- Remove APPLETVOS variants from BUCk targets. ([cf2e27c388](https://github.com/facebook/react-native/commit/cf2e27c3888ded6f851ba267597ef13f1d0cfd8c) by [@d16r](https://github.com/d16r)) + +#### iOS specific + +- Remove `emulateUnlessSupported` ([c73e021a4b](https://github.com/facebook/react-native/commit/c73e021a4b11bbae3a7868670d140fe3d5dac6ae) by [@ken0nek](https://github.com/ken0nek)) +- Remove USE_CODEGEN_DISCOVERY flag ([2e720c3610](https://github.com/facebook/react-native/commit/2e720c361001d996ed35d8bfbf4dc67c31fb895d) by [@cipolleschi](https://github.com/cipolleschi)) + +### Fixed + +- Throw JSINativeException from asHostObject ([ef6ab3f5ca](https://github.com/facebook/react-native/commit/ef6ab3f5cad968d7b2c9127d834429b0f4e1b2cf) by [@neildhar](https://github.com/neildhar)) +- Use new Babel parser instead of deprecated one ([97291bfa31](https://github.com/facebook/react-native/commit/97291bfa3157ac171a2754e19a52d006040961fb) by [@Kerumen](https://github.com/Kerumen)) +- Fixed a crash on deserialization of props when using 'px'/'em' units. ([70788313fe](https://github.com/facebook/react-native/commit/70788313fedd40fe2e6d1cf15980ce3cca5adaac) by [@nlutsenko](https://github.com/nlutsenko)) +- Fix nullability lost on readonly types in TurboModule specs ([c006722e6c](https://github.com/facebook/react-native/commit/c006722e6cdbe02711cb50ea7a739e0d4d81c7e7) by [@appden](https://github.com/appden)) +- Make tests pass for windows ([9596bf045d](https://github.com/facebook/react-native/commit/9596bf045d527e27608ac4b7b2990a4e6846fdeb) by [@cipolleschi](https://github.com/cipolleschi)) +- Handle possible null exception on ReactEditText with AppCompat 1.4.0 ([24a1f5c66c](https://github.com/facebook/react-native/commit/24a1f5c66c8633f9b41eef45df3297ffc1d2b606) by [@mikemasam](https://github.com/mikemasam)) +- Fixed the disappearance of items when scrolling after zooming VirtualizedList. ([13a72e0ccc](https://github.com/facebook/react-native/commit/13a72e0ccceb2db6aeacd03b4f429d200392c17b) by [@islandryu](https://github.com/islandryu)) +- Improved Flow type inference in Animated `.interpolate()` ([7b86fa2b79](https://github.com/facebook/react-native/commit/7b86fa2b795647f5c89e04e4c3ee4b83bc27ef77) by [@motiz88](https://github.com/motiz88)) +- Add Jest mock for Vibration module ([79529a1c77](https://github.com/facebook/react-native/commit/79529a1c77e7e1b174fdbe8103a2199c9ac924ff) by [@hduprat](https://github.com/hduprat)) +- Allow ReactInstrumentationTest to use custom JSIModules ([eb2a83b0be](https://github.com/facebook/react-native/commit/eb2a83b0be4658654fc6ca6f4671e45f1898798d) by [@christophpurrer](https://github.com/christophpurrer)) +- Working around Long paths limitation on Windows ([883a93871c](https://github.com/facebook/react-native/commit/883a93871cb1fbca4434600a322f63afbba333da) by [@mganandraj](https://github.com/mganandraj)) +- Fix eslint-plugin-specs prepack npm lifecycle hook now that we use npm 8 ([8441c4a6f7](https://github.com/facebook/react-native/commit/8441c4a6f7bfeda73f89f076fe7d8d1132e4b9be) by [@kelset](https://github.com/kelset)) +- Codegen should ignore `.d.ts` files ([0f0d52067c](https://github.com/facebook/react-native/commit/0f0d52067cb89fdb39a99021f0745282ce087fc2) by [@tido64](https://github.com/tido64)) +- Avoid full copy of large folly::dynamic objects in JSIExecutor#defaultTimeoutInvoker ([521011d4cc](https://github.com/facebook/react-native/commit/521011d4cc713dfce97dc8872fd0f5171e587b5d) by [@christophpurrer](https://github.com/christophpurrer)) + +#### Android specific + +- Fixed HorizontalScrollView API scrollToEnd causing NPE in side-effects. ([e5ba6ab7b4](https://github.com/facebook/react-native/commit/e5ba6ab7b482c380e35765b898e522e9d4e1d3b0) by [@ryancat](https://github.com/ryancat)) +- Fix InputAccessoryView crash on Android ([afa5df1764](https://github.com/facebook/react-native/commit/afa5df1764324f2574d102abeae7199d4b02d183) by [@hduprat](https://github.com/hduprat)) +- Bring back non-rootview touch handling based on reactTag ([8b837268b4](https://github.com/facebook/react-native/commit/8b837268b49fd4e72a05f955c20702c457a68fab) by [@fkgozali](https://github.com/fkgozali)) +- Make Text not focusable by default ([8ced165e53](https://github.com/facebook/react-native/commit/8ced165e53135d9d33cfdc55a9d4660f8eb5b3c5) by [@kacieb](https://github.com/kacieb)) +- Revert [PR 33924](https://github.com/facebook/react-native/pull/33924) because of issues with TextInputs with numeric keyboard types not respecting the secureTextEntry prop ([edb27e3aa1](https://github.com/facebook/react-native/commit/edb27e3aa1210ef33d55c1840065457c31b19cb0) by [@charlesbdudley](https://github.com/charlesbdudley)) +- Fix edge case when we enqueue a pending event to views on stopped surface ([ea7c9f2ad9](https://github.com/facebook/react-native/commit/ea7c9f2ad9a78b16234306932edc1d78b783ac27) by [@ryancat](https://github.com/ryancat)) +- Fix a bug where the keyboard, once set as email, won't change back to default. ([ec307e0167](https://github.com/facebook/react-native/commit/ec307e0167deca7f17640cd3c5a60f6be5f47b62) by [@larkox](https://github.com/larkox)) +- NPE in `ReactEditText.setInputType` when React Native is used with some versions of a AppCompat 1.4.x. (and possibly others) ([92ebb298e2](https://github.com/facebook/react-native/commit/92ebb298e2e5ad640754e09ce3a37d3de1d28f58)) +- Fix NPE on `ReactEditText` due to null mFabricViewStateManager ([ba6bf5a3ce](https://github.com/facebook/react-native/commit/ba6bf5a3ce7039a7e407a6632ee41aa3d504f833) by [@cortinico](https://github.com/cortinico)) +- Scroll views would still receive scroll events when nested in a view with `pointer-events: "none"` ([fced96bf52](https://github.com/facebook/react-native/commit/fced96bf5202e8b89b804ccc1004abacc9f91660) by [@javache](https://github.com/javache)) +- Fixed an edge case that event dispatching is failed after pre-allocation of a view and before the view is mounted. ([a093fe5f2f](https://github.com/facebook/react-native/commit/a093fe5f2fae4e9996b0cbffdfccdce8e58e8cf1) by [@ryancat](https://github.com/ryancat)) +- Avoid crash by handling missing views in dispatchViewManagerCommand ([ee1a191cb1](https://github.com/facebook/react-native/commit/ee1a191cb1c10085722d57fc276734f83e86a4f3) by [@hsource](https://github.com/hsource)) +- Pass react build dir to cmake ([6ab7a99518](https://github.com/facebook/react-native/commit/6ab7a99518f8ba0d53e62e35d230ebec78e50217) by [@janicduplessis](https://github.com/janicduplessis)) +- Fix missing space in ReactPropertyException message ([24560b6718](https://github.com/facebook/react-native/commit/24560b67184da00e05491af38289865c4b934ee8) by [@markv](https://github.com/markv)) +- Fix import path breakage ([2e1e62f2bf](https://github.com/facebook/react-native/commit/2e1e62f2bf043ea0bf9926e1f5786ca54a22005e) by [@aniketmathur](https://github.com/aniketmathur)) +- When `onEndReachedThreshold` is set to 0 `onEndReached` function on `VirtualizedList` properly fires once the user scrolls to the bottom of the list. ([b869680c11](https://github.com/facebook/react-native/commit/b869680c1196a6549154a4b9cb7ffa10eab1989c)) +- Fix rendering of transparent borders in RN Android ([a9659ce86d](https://github.com/facebook/react-native/commit/a9659ce86d94dd34768b067763740a5c41917e42) by [@mdvacca](https://github.com/mdvacca)) +- Exception with `Cannot load WebView` message will initialising WebView (along with existing checks) ([9e0d8696cc](https://github.com/facebook/react-native/commit/9e0d8696cc68436a0d309cafde252c55fc337be4) by [@rachitmishra](https://github.com/rachitmishra)) +- Fix accessibilityState overwriting view's disabled state on Android ([f35d18caa3](https://github.com/facebook/react-native/commit/f35d18caa302351319840ec85337182f4f148e5e) by [@okwasniewski](https://github.com/okwasniewski)) +- Make sure *.ts files are considered for task avoidance in the Gradle Plugin ([1a9fb6cb68](https://github.com/facebook/react-native/commit/1a9fb6cb682aa5ff83462e1e2869eb99f3b873fd) by [@cortinico](https://github.com/cortinico)) +- Fix missing import on New Architecture build script in template ([a22f30d2ce](https://github.com/facebook/react-native/commit/a22f30d2ce866cb1488b26bb18eee0620a0ac259) by [@cortinico](https://github.com/cortinico)) + +#### iOS specific + +- Use `NODE_BINARY` from `.xcode.env` when running packager from Xcode ([ff785dbcf5](https://github.com/facebook/react-native/commit/ff785dbcf5c464a4d850fa738e977702efd8abd3) by [@elsurudo](https://github.com/elsurudo)) +- Bug with error message formatting when bundle is missing ([f501979f3d](https://github.com/facebook/react-native/commit/f501979f3d1e5c053eed16967a3d3385eab8e15f) by [@BenLorantfy](https://github.com/BenLorantfy)) +- Fix the race condition when calling readAsDataURL after new Blob(blobs) ([bd12e41188](https://github.com/facebook/react-native/commit/bd12e41188c8d85c0acbd713f10f0bd34ea0edca) by [@wood1986](https://github.com/wood1986)) +- Fix the way the orientation events are published, to avoid false publish on orientation change when app changes state to inactive ([7d42106d4c](https://github.com/facebook/react-native/commit/7d42106d4ce20c644bda4d928fb0abc163580cee) by [@lbaldy](https://github.com/lbaldy)) +- Fix sed error when installing `glog` ([4a7e4b9ca6](https://github.com/facebook/react-native/commit/4a7e4b9ca6ef4fb52611b6c3cb788f624d1f81a4) by [@alphashuro](https://github.com/alphashuro)) +- Don't validate ENTRY_FILE in react-native-xcode.sh ([780fe80fca](https://github.com/facebook/react-native/commit/780fe80fcaf213d84d9d087132af933bd02d1349) by [@janicduplessis](https://github.com/janicduplessis)) +- `_scrollViewComponentView` is set to `RCTPullToRefreshViewComponentView's` superview ([4e4b9e2111](https://github.com/facebook/react-native/commit/4e4b9e2111faaf5652ae1f5b885730b378f3de98) by [@dmytrorykun](https://github.com/dmytrorykun)) +- Use `GCC_PREPROCESSOR_DEFINITIONS` to set `FB_SONARKIT_ENABLED` ([77e6bff629](https://github.com/facebook/react-native/commit/77e6bff629312f20cdacb1e798cd2464ac50db9e) by [@janicduplessis](https://github.com/janicduplessis)) +- Fix Hermes not being properly downloaded during pod install ([d5e0659fcc](https://github.com/facebook/react-native/commit/d5e0659fccf2767beb7aae55461e9690ba335c81) by [@cortinico](https://github.com/cortinico)) +- Typo in the documation for the `automaticallyAdjustKeyboardInsets` prop ([927b43d47c](https://github.com/facebook/react-native/commit/927b43d47c2cd42538265cb06154b12cb0be6816) by [@jeremybarbet](https://github.com/jeremybarbet)) +- Deprecate iOS/tvOS SDK 11.0 support now that 12.4+ is required ([f56d701e56](https://github.com/facebook/react-native/commit/f56d701e567af0c252a2e297bf81cd4add59378a) by [@leotm](https://github.com/leotm)) +- Fix blank spaces that don't recover as you scroll when in an RTL locale and e.nativeEvent.zoomScale is -1. ([bc7b5c3011](https://github.com/facebook/react-native/commit/bc7b5c3011460935614a47a03cd077cd1059de72), [2f491bfa9f](https://github.com/facebook/react-native/commit/2f491bfa9f86c3db2e459e331f39bc3cf12e7239)) +- Fixed paddingTop not being applied when using padding and paddingVertical in multiline textinput ([2fb107c9a6](https://github.com/facebook/react-native/commit/2fb107c9a63aacd2c880ad6abedaad67ffb6022b) by [@hetanthakkar1](https://github.com/hetanthakkar1)) +- Fixed the ability to pass the port to use for Metro when running `react-native run-ios --port `. ([7dc0b5153e](https://github.com/facebook/react-native/commit/7dc0b5153e4eb91c333238a58fe8c75a47cb5f81) by [@lindboe](https://github.com/lindboe)) +- Fix missing imports ([c78babac39](https://github.com/facebook/react-native/commit/c78babac39b7c64e03e137d8fddd91e783303426)) +- Fix React-bridging headers import not found ([c4b51e8d76](https://github.com/facebook/react-native/commit/c4b51e8d7679c3c20b843072581acd23a931fc83) by [@Kudo](https://github.com/Kudo)) +- Fix Hermes executor not available when `use_frameworks` is enabled ([88b7b640a7](https://github.com/facebook/react-native/commit/88b7b640a74bafd918b8b1cd5d58e1f5ddfb730a) by [@Kudo](https://github.com/Kudo)) + +### Security + +- Add GitHub token permissions for workflows ([3da3d82320](https://github.com/facebook/react-native/commit/3da3d82320bd035c6bd361a82ea12a70dba4e851) by [@varunsh-coder](https://github.com/varunsh-coder)) +- Bump RCT-Folly to 2021-07-22 ([68f3a42fc7](https://github.com/facebook/react-native/commit/68f3a42fc7380051714253f43b42175de361f8bd) by [@luissantana](https://github.com/luissantana)) + +## v0.69.6 + +### Changed + +- Bump version of `promise` from 8.0.3 to 8.2.0, enabling `Promise.allSettled` ([951538c080](https://github.com/facebook/react-native/commit/951538c080ef745da304fb308fa91d597e0dd98a) by [@retyui](https://github.com/retyui)) + +### Fixed + +- Fix hermes profiler ([81564c1a3d](https://github.com/facebook/react-native/commit/81564c1a3dae4222858de2a9a34089097f665e82) by [@janicduplessis](https://github.com/janicduplessis)) + +#### Android specific + +- Correctly resolve classes with FindClass(..) ([361b310bcc](https://github.com/facebook/react-native/commit/361b310bcc8dddbff42cf63495649291c894d661) by [@evancharlton](https://github.com/evancharlton)) + +#### iOS specific + +- Fix the way the orientation events are published, to avoid false publish on orientation change when app changes state to inactive ([7d42106d4c](https://github.com/facebook/react-native/commit/7d42106d4ce20c644bda4d928fb0abc163580cee) by [@lbaldy](https://github.com/lbaldy)) +- Fix React module build error with swift integration on new architecture mode ([3afef3c167](https://github.com/facebook/react-native/commit/3afef3c16702cefa5115b059a08741fba255b2db) by [@Kudo](https://github.com/Kudo)) + +## v0.69.5 + +### Changed + +- Bump react-native-codegen to 0.69.2 ([df3d52bfbf](https://github.com/facebook/react-native/commit/df3d52bfbf4254cd16e1dc0ca0af2743cd7e11c1) by [@dmytrorykun](https://github.com/dmytrorykun)) + +#### Android specific + +- Replaced reactnativeutilsjni with reactnativejni in the build process to reduce size ([54a4fcbfdc](https://github.com/facebook/react-native/commit/54a4fcbfdcc8111b3010b2c31ed3c1d48632ce4c) by [@SparshaSaha](https://github.com/SparshaSaha)) + +### Fixed + +- Codegen should ignore `.d.ts` files ([0f0d52067c](https://github.com/facebook/react-native/commit/0f0d52067cb89fdb39a99021f0745282ce087fc2) by [@tido64](https://github.com/tido64)) + +## v0.69.4 + +### Changed + +- Upgrade RN CLI to v8.0.4 ([66c68c37ce](https://github.com/facebook/react-native/commit/66c68c37ce94f6c1160e7f260c0d1887539c6605) by [@thymikee](https://github.com/thymikee)) + +#### Android specific + +- Modified **getDefaultJSExecutorFactory** method ([87cfd386cb](https://github.com/facebook/react-native/commit/87cfd386cb2e02bfa440c94706d9d0274f83070c) by [@KunalFarmah98](https://github.com/KunalFarmah98)) + +## v0.69.3 + +### Fixed + +#### iOS specific + +- Fix React-bridging header not found for third party modules ([fa2acc32d1](https://github.com/facebook/react-native/commit/fa2acc32d1490f6e418689dec321f8bd4ef7bb28) by [@Kudo](https://github.com/Kudo)) + +## v0.69.2 + +### Changed + +- Set react-shallow-renderer v16.15.0 for react v18 compat ([a39a7c453d](https://github.com/facebook/react-native/commit/a39a7c453d87086935ff07d549ba8220cbcf30bd) by [@mikehardy](https://github.com/mikehardy)) +- Upgrade RN CLI to v8.0.3 ([28cbd21d21](https://github.com/facebook/react-native/commit/28cbd21d21f2ffb3f38b2449a4983f013947ce0a) by [@thymikee](https://github.com/thymikee)) + +#### iOS specific + +- Hermes pod: change logic to use the hermes tag to set the pod source correctly ([46a9edc854](https://github.com/facebook/react-native/commit/46a9edc8544ae070149a97ea3d919b88dd6e2942) by [@kelset](https://github.com/kelset)) +- Fix the race condition when calling readAsDataURL after new Blob(blobs) ([bd12e41188](https://github.com/facebook/react-native/commit/bd12e41188c8d85c0acbd713f10f0bd34ea0edca) by [@wood1986](https://github.com/wood1986)) +- Make sure that Flipper pods are not installed when creating a release build ([23accbf58d](https://github.com/facebook/react-native/commit/23accbf58d2fa03ad020e07f00012a32609c7218) by [@cipolleschi](https://github.com/cipolleschi)) + ## v0.69.1 ### Changed @@ -90,6 +405,7 @@ - Type the argument of Animated.interpolate as read-only ([6584304c10](https://github.com/facebook/react-native/commit/6584304c100ce4d51a5c4d606170a6ad0dc00875) by [@motiz88](https://github.com/motiz88)) - Update gradle-download-task to 5.0.1 to support concurrent downloads ([a86cae7aac](https://github.com/facebook/react-native/commit/a86cae7aacc9837536e7d679870a57dcd0f45475) by [@michel-kraemer](https://github.com/michel-kraemer)) - Logging a soft error when ReactRootView has an id other than -1 instead of crashing the app in hybrid apps ([1ca2c24930](https://github.com/facebook/react-native/commit/1ca2c2493027c1b027146cd41e17dd8a4fc33a41) by [@Kunal-Airtel2022](https://github.com/Kunal-Airtel2022)) +- Upgrade to React 18 ([41cbccd98d](https://github.com/facebook/react-native/commit/41cbccd98dd6c98d1f662674164cf455105a1359) by [@rickhanlonii](https://github.com/rickhanlonii)) #### Android specific @@ -229,6 +545,41 @@ - Encode URL params in URLSearchParams.toString() ([1042a8012f](https://github.com/facebook/react-native/commit/1042a8012fb472bd5c882b469fe507dd6279d562) by [@sshic](https://github.com/sshic)) +## v0.68.4 + +### Changed + +- Bump version of `promise` from 8.0.3 to 8.2.0, enabling `Promise.allSettled` ([951538c080](https://github.com/facebook/react-native/commit/951538c080ef745da304fb308fa91d597e0dd98a) by [@retyui](https://github.com/retyui)) +- Bump react-native-codegen to 0.0.18 ([40a3ae3613](https://github.com/facebook/react-native/commit/40a3ae3613394fe5f0d728bada538d2d5b78a8a4) by [@dmytrorykun](https://github.com/dmytrorykun)) + +#### Android specific + +- Correctly resolve classes with FindClass(..) ([361b310bcc](https://github.com/facebook/react-native/commit/361b310bcc8dddbff42cf63495649291c894d661) by [@evancharlton](https://github.com/evancharlton)) + +### Fixed + +- Codegen should ignore `.d.ts` files ([0f0d52067c](https://github.com/facebook/react-native/commit/0f0d52067cb89fdb39a99021f0745282ce087fc2) by [@tido64](https://github.com/tido64)) + +#### iOS specific + +- Fix the way the orientation events are published ([7d42106d4c](https://github.com/facebook/react-native/commit/7d42106d4ce20c644bda4d928fb0abc163580cee) by [lbaldy](https://github.com/lbaldy)) + +## v0.68.3 + +### Changed + +#### Android specific + +- Let's not build reactnativeutilsjni shared library ([af9225ec5f](https://github.com/facebook/react-native/commit/af9225ec5fd22da802e3da4d786fa7f6ec956b0f) by [@SparshaSaha](https://github.com/SparshaSaha)) +- Modified **getDefaultJSExecutorFactory** method ([87cfd386cb](https://github.com/facebook/react-native/commit/87cfd386cb2e02bfa440c94706d9d0274f83070c) by [@KunalFarmah98](https://github.com/KunalFarmah98)) + +### Fixed + +- Use monotonic clock for performance.now() ([114d31feee](https://github.com/facebook/react-native/commit/114d31feeeb47f5a57419e5088c3cbe9340f757a)) + +#### Android specific + +- Logging a soft error when ReactRootView has an id other than -1 instead of crashing the app in hybrid apps ([1ca2c24930](https://github.com/facebook/react-native/commit/1ca2c2493027c1b027146cd41e17dd8a4fc33a41) by [@Kunal-Airtel2022](https://github.com/Kunal-Airtel2022)) ## v0.68.2 diff --git a/ECOSYSTEM.md b/ECOSYSTEM.md index 2505baabf0cc..4adef0816a8a 100644 --- a/ECOSYSTEM.md +++ b/ECOSYSTEM.md @@ -1,27 +1,36 @@ # The React Native Ecosystem -We aim to build a vibrant and inclusive ecosystem of partners, core contributors, and community that goes beyond the main React Native GitHub repository. This document explains the roles and responsibilities of various stakeholders and provides guidelines for the community organization. The structure outlined in this document has been in place for a while but hadn't been written down before. +We aim to build a vibrant and inclusive ecosystem of partners, core contributors, and community that goes beyond the main React Native GitHub repository. This document explains the roles and responsibilities of various stakeholders and provides guidelines for the community organization. The structure outlined in this document has been in place for a while but had not been written down before. There are three types of stakeholders: -* **Partners:** Companies that are significantly invested in React Native and have been for years. +* **Partners:** Companies significantly invested in React Native and take responsibility for the React Native vision and community. * **Core Contributors:** Individual people who contribute to the React Native project. * **Community Contributors:** Individuals who support projects in the [react-native-community](https://github.com/react-native-community) organization. ## Partners -Partners are companies that are significantly invested in React Native and have been for years. Informed by their use of React Native, they push for improvements of the core and/or the ecosystem around it. Partners think of React Native as a product: they understand the trade offs that the project makes as well as future plans and goals. Together we shape the vision for React Native to make it the best way to build applications. +Partners are companies that are significantly invested in React Native and demonstrate ownership. Informed by their use of React Native, they push for improvements of the core and/or the ecosystem around it. Examples of this may include large scale contributions to `react-native` or owning essential tools or libraries. -React Native's current set of partners include Callstack, Expo, Infinite Red, Meta, Microsoft and Software Mansion. Many engineers from these companies are core contributors, and their partner responsibilities also include: +Partners think of React Native as a product; they understand the trade offs that the project makes as well as future plans and goals. Together we shape the vision for React Native to make it the best way to build applications. -* **[Callstack](https://callstack.com/):** Manages releases, maintains the [React Native CLI](https://github.com/react-native-community/react-native-cli) and organizes [React Native EU](https://react-native.eu/) -* **[Expo](https://expo.dev/):** Builds [expo](https://github.com/expo/expo) and [EAS](https://expo.dev/eas) on top of React Native to simplify app development -* **[Infinite Red](https://infinite.red/):** Maintains the [ignite cli/boilerplate](https://github.com/infinitered/ignite), organizes [Chain React Conf](https://cr.infinite.red/) +Baseline responsibilities of partners include: +* Attending monthly meeting +* Contributing to the release process. Examples include being a [community releaser](https://reactnative.dev/contributing/release-roles-responsibilities#release-role-2-community-releaser), testing new releases, technical support for release issues. +* Engage on the core contributor Discord + +Our current partners and other areas of ownership: +* **[Coinbase](https://www.coinbase.com/):** Publishes [posts](https://blog.coinbase.com/tagged/react-native) advocating React Native usage. Supports `@react-native-community/datetimepicker` and other community modules to migrate to the new architecture. Supports releases in testing and feedback. +* **[Callstack](https://callstack.com/):** Maintains the [React Native CLI](https://github.com/react-native-community/react-native-cli) and [other community libraries](https://github.com/callstack), organizes [React Native EU](https://react-native.eu/) and hosts [The React Native Show podcast](https://www.callstack.com/podcast-react-native-show) +* **[Expo](https://expo.dev/):** Builds [Expo Go and SDK](https://github.com/expo/expo), [Snack](https://snack.expo.dev/), and [Expo Application Services](https://expo.dev/eas). Maintains [React Native Directory](https://reactnative.directory/), stewards [React Navigation](https://reactnavigation.org/) along with other partners. +* **[Infinite Red](https://infinite.red/):** Maintains the [ignite cli/boilerplate](https://github.com/infinitered/ignite), organizes [Chain React Conf](https://cr.infinite.red/), hosts the [React Native Radio podcast](https://reactnativeradio.com), publishes the [React Native Newsletter](https://reactnativenewsletter.com) * **[Meta](https://opensource.fb.com/):** Oversees the React Native product and maintains the [React Native core repo](https://reactnative.dev/) -* **[Microsoft](http://aka.ms/reactnative):** Develops [React Native Windows](https://github.com/Microsoft/react-native-windows) and [React Native macOS](https://github.com/microsoft/react-native-macos) for building apps that target Windows and macOS +* **[Microsoft](https://twitter.com/ReactNativeMSFT):** Develops [React Native Windows](https://github.com/Microsoft/react-native-windows) and [React Native macOS](https://github.com/microsoft/react-native-macos) for building apps that target Windows and macOS; maintains [rnx-kit](https://github.com/microsoft/rnx-kit), [react-native-test-app](https://github.com/microsoft/react-native-test-app) and coordinates cross-companies efforts such as the [bundle working group](https://github.com/microsoft/rnx-kit/discussions/categories/bundle-working-group). +* **[Shopify](https://www.shopify.com/):** Maintains React Native open source libraries such as [flash-list](https://github.com/Shopify/flash-list) or [@shopify/react-native-skia](https://github.com/Shopify/react-native-skia) and sponsors Software Mansion. * **[Software Mansion](https://swmansion.com/):** Maintain core infrastructure including JSC, Animated, and other popular third-party plugins and organizes [App.js Conf](https://appjs.co/) +* **[Wix.com](https://wix.engineering/open-source):** Maintains a variety of React Native open source projects ([see all](https://github.com/orgs/wix/repositories?q=react-native)), including: [Detox](https://wix.github.io/Detox/) end-to-end testing library for React Native apps, [RN UILib](https://wix.github.io/react-native-ui-lib/), [RN Navigation](https://wix.github.io/react-native-navigation/), [RN Calendars](https://wix.github.io/react-native-calendars/) and [RN Notifications](https://github.com/wix/react-native-notifications). -In terms of open source work, pull requests from partners are commonly prioritized. When you are contributing to React Native, you'll most likely meet somebody who works at one of the partner companies and who is a core contributor: +This list may fluctuate in response to newcomers who meet our partner definition. In terms of open source work, pull requests from partners are commonly prioritized. When you are contributing to React Native, you'll most likely meet somebody who works at one of the partner companies and who is a core contributor: ## Core Contributors @@ -38,9 +47,15 @@ Core contributors are individuals who contribute to the React Native project. A These are behaviors we have observed in our existing core contributors. They aren't strict rules but rather outline their usual responsibilities. We do not expect every core contributor to do all of the above things all the time. Most importantly, we want to create a supportive and friendly environment that fosters collaboration. Above all else, **we are always polite and friendly.** -Core contributor status is attained after consistently contributing and taking on the responsibilities outlined above and granted by other core contributors. Similarly, after a long period of inactivity, a core contributor may be removed. +Core contributor status is attained after consistently contributing and taking on the responsibilities outlined above and granted by other core contributors. Similarly, after a long period of inactivity (~6 months or more), a core contributor may be contacted to understand if they’re still interested in being part of the program. + +You can use this [form](https://forms.gle/4jpA4QeNUvAUDnNe8) to either: +* Apply yourself to become a Core Contributor. Make sure to include a list of valuable contributions you did to the React Native repository and ecosystem. +* Nominate someone to become a Core Contributor. + +As a core contributor, you will have access to the core contributor Discord which is used for light-weight coordination and discussion. -We aim to make contributing to React Native as easy and transparent as possible. All important topics are handled through a [discussion or RFC process on GitHub](https://github.com/react-native-community/discussions-and-proposals). We are always looking for active, enthusiastic members of the React Native community to become core contributors. +**We aim to make contributing to React Native as easy and transparent as possible.** We have discussion groups dedicated to the [new architecture rollout](https://github.com/reactwg/react-native-new-architecture), [releases](https://github.com/reactwg/react-native-releases), and [general questions and proposals](https://github.com/react-native-community/discussions-and-proposals). We are always looking for active, enthusiastic members of the React Native community to become core contributors. ## Community Contributors diff --git a/Gemfile b/Gemfile index 5efda89f4525..567e59805c4a 100644 --- a/Gemfile +++ b/Gemfile @@ -1,6 +1,6 @@ source 'https://rubygems.org' # You may use http://rbenv.org/ or https://rvm.io/ to install and use this version -ruby '2.7.5' +ruby File.read(File.join(__dir__, '.ruby-version')).strip -gem 'cocoapods', '~> 1.11', '>= 1.11.2' +gem 'cocoapods', '~> 1.11', '>= 1.11.3' diff --git a/Gemfile.lock b/Gemfile.lock index fd7c2f3b8469..5e3ce4aca5c3 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -3,14 +3,14 @@ GEM specs: CFPropertyList (3.0.5) rexml - activesupport (6.1.5) + activesupport (6.1.7) concurrent-ruby (~> 1.0, >= 1.0.2) i18n (>= 1.6, < 2) minitest (>= 5.1) tzinfo (~> 2.0) zeitwerk (~> 2.3) - addressable (2.8.0) - public_suffix (>= 2.0.2, < 5.0) + addressable (2.8.1) + public_suffix (>= 2.0.2, < 6.0) algoliasearch (1.27.5) httpclient (~> 2.8, >= 2.8.3) json (>= 1.5.1) @@ -45,7 +45,7 @@ GEM public_suffix (~> 4.0) typhoeus (~> 1.0) cocoapods-deintegrate (1.0.5) - cocoapods-downloader (1.6.1) + cocoapods-downloader (1.6.3) cocoapods-plugins (1.0.0) nap cocoapods-search (1.0.1) @@ -56,45 +56,45 @@ GEM colored2 (3.1.2) concurrent-ruby (1.1.10) escape (0.0.4) - ethon (0.15.0) + ethon (0.16.0) ffi (>= 1.15.0) ffi (1.15.5) fourflusher (2.3.1) fuzzy_match (2.0.4) gh_inspector (1.1.3) httpclient (2.8.3) - i18n (1.10.0) + i18n (1.12.0) concurrent-ruby (~> 1.0) - json (2.6.1) - minitest (5.15.0) + json (2.6.3) + minitest (5.16.3) molinillo (0.8.0) nanaimo (0.3.0) nap (1.1.0) netrc (0.11.0) - public_suffix (4.0.6) + public_suffix (4.0.7) rexml (3.2.5) ruby-macho (2.5.1) typhoeus (1.4.0) ethon (>= 0.9.0) - tzinfo (2.0.4) + tzinfo (2.0.5) concurrent-ruby (~> 1.0) - xcodeproj (1.21.0) + xcodeproj (1.22.0) CFPropertyList (>= 2.3.3, < 4.0) atomos (~> 0.1.3) claide (>= 1.0.2, < 2.0) colored2 (~> 3.1) nanaimo (~> 0.3.0) rexml (~> 3.2.4) - zeitwerk (2.5.4) + zeitwerk (2.6.6) PLATFORMS ruby DEPENDENCIES - cocoapods (~> 1.11, >= 1.11.2) + cocoapods (~> 1.11, >= 1.11.3) RUBY VERSION - ruby 2.7.5p203 + ruby 2.7.6p219 BUNDLED WITH - 2.3.10 + 2.3.22 diff --git a/IntegrationTests/AccessibilityManagerTest.js b/IntegrationTests/AccessibilityManagerTest.js index f23972f25da1..e68cd5bb6826 100644 --- a/IntegrationTests/AccessibilityManagerTest.js +++ b/IntegrationTests/AccessibilityManagerTest.js @@ -16,7 +16,7 @@ import * as React from 'react'; const {TestModule} = NativeModules; class AccessibilityManagerTest extends React.Component<{...}> { - componentDidMount() { + componentDidMount(): void { invariant( NativeAccessibilityManager, "NativeAccessibilityManager doesn't exist", diff --git a/IntegrationTests/AppEventsTest.js b/IntegrationTests/AppEventsTest.js index 5ff829b20c5a..e1736962006f 100644 --- a/IntegrationTests/AppEventsTest.js +++ b/IntegrationTests/AppEventsTest.js @@ -38,7 +38,6 @@ class AppEventsTest extends React.Component<{...}, State> { NativeAppEventEmitter.addListener('testEvent', this.receiveEvent); const event = {data: TEST_PAYLOAD, ts: Date.now()}; TestModule.sendAppEvent('testEvent', event); - // eslint-disable-next-line react/no-did-mount-set-state this.setState({sent: event}); } diff --git a/IntegrationTests/AsyncStorageTest.js b/IntegrationTests/AsyncStorageTest.js deleted file mode 100644 index 42bdbd629654..000000000000 --- a/IntegrationTests/AsyncStorageTest.js +++ /dev/null @@ -1,261 +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. - * - * @format - * @flow - */ - -'use strict'; - -const React = require('react'); -const ReactNative = require('react-native'); -const {AsyncStorage, Text, View, StyleSheet} = ReactNative; -const {TestModule} = ReactNative.NativeModules; - -const deepDiffer = require('react-native/Libraries/Utilities/differ/deepDiffer'); -const nullthrows = require('nullthrows'); - -const DEBUG = false; - -const KEY_1 = 'key_1'; -const VAL_1 = 'val_1'; -const KEY_2 = 'key_2'; -const VAL_2 = 'val_2'; -const KEY_MERGE = 'key_merge'; -const VAL_MERGE_1 = {foo: 1, bar: {hoo: 1, boo: 1}, moo: {a: 3}}; -const VAL_MERGE_2 = {bar: {hoo: 2}, baz: 2, moo: {a: 3}}; -const VAL_MERGE_EXPECT = {foo: 1, bar: {hoo: 2, boo: 1}, baz: 2, moo: {a: 3}}; - -// setup in componentDidMount -let done = (result: ?boolean) => {}; -let updateMessage = (message: string) => {}; - -function runTestCase(description: string, fn: () => void) { - updateMessage(description); - fn(); -} - -function expectTrue(condition: boolean, message: string) { - if (!condition) { - throw new Error(message); - } -} - -// Type-safe wrapper around JSON.stringify -function stringify( - value: - | void - | null - | string - | number - | boolean - | {...} - | $ReadOnlyArray, -): string { - if (typeof value === 'undefined') { - return 'undefined'; - } - return JSON.stringify(value); -} - -function expectEqual( - lhs: ?(any | string | Array>), - rhs: - | null - | string - | { - bar: {boo: number, hoo: number}, - baz: number, - foo: number, - moo: {a: number}, - } - | Array>, - testname: string, -) { - expectTrue( - !deepDiffer(lhs, rhs), - 'Error in test ' + - testname + - ': expected\n' + - stringify(rhs) + - '\ngot\n' + - stringify(lhs), - ); -} - -function expectAsyncNoError( - place: string, - err: ?(Error | string | Array), -) { - if (err instanceof Error) { - err = err.message; - } - expectTrue( - err === null, - 'Unexpected error in ' + place + ': ' + stringify(err), - ); -} - -function testSetAndGet() { - AsyncStorage.setItem(KEY_1, VAL_1, err1 => { - expectAsyncNoError('testSetAndGet/setItem', err1); - AsyncStorage.getItem(KEY_1, (err2, result) => { - expectAsyncNoError('testSetAndGet/getItem', err2); - expectEqual(result, VAL_1, 'testSetAndGet setItem'); - updateMessage('get(key_1) correctly returned ' + String(result)); - runTestCase('should get null for missing key', testMissingGet); - }); - }); -} - -function testMissingGet() { - AsyncStorage.getItem(KEY_2, (err, result) => { - expectAsyncNoError('testMissingGet/setItem', err); - expectEqual(result, null, 'testMissingGet'); - updateMessage('missing get(key_2) correctly returned ' + String(result)); - runTestCase('check set twice results in a single key', testSetTwice); - }); -} - -function testSetTwice() { - AsyncStorage.setItem(KEY_1, VAL_1, () => { - AsyncStorage.setItem(KEY_1, VAL_1, () => { - AsyncStorage.getItem(KEY_1, (err, result) => { - expectAsyncNoError('testSetTwice/setItem', err); - expectEqual(result, VAL_1, 'testSetTwice'); - updateMessage('setTwice worked as expected'); - runTestCase('test removeItem', testRemoveItem); - }); - }); - }); -} - -function testRemoveItem() { - AsyncStorage.setItem(KEY_1, VAL_1, () => { - AsyncStorage.setItem(KEY_2, VAL_2, () => { - AsyncStorage.getAllKeys((err, result) => { - expectAsyncNoError('testRemoveItem/getAllKeys', err); - expectTrue( - nullthrows(result).indexOf(KEY_1) >= 0 && - nullthrows(result).indexOf(KEY_2) >= 0, - 'Missing KEY_1 or KEY_2 in ' + '(' + nullthrows(result).join() + ')', - ); - updateMessage('testRemoveItem - add two items'); - AsyncStorage.removeItem(KEY_1, err2 => { - expectAsyncNoError('testRemoveItem/removeItem', err2); - updateMessage('delete successful '); - AsyncStorage.getItem(KEY_1, (err3, result2) => { - expectAsyncNoError('testRemoveItem/getItem', err3); - expectEqual( - result2, - null, - 'testRemoveItem: key_1 present after delete', - ); - updateMessage('key properly removed '); - AsyncStorage.getAllKeys((err4, result3) => { - expectAsyncNoError('testRemoveItem/getAllKeys', err4); - expectTrue( - nullthrows(result3).indexOf(KEY_1) === -1, - 'Unexpected: KEY_1 present in ' + nullthrows(result3).join(), - ); - updateMessage('proper length returned.'); - runTestCase('should merge values', testMerge); - }); - }); - }); - }); - }); - }); -} - -function testMerge() { - AsyncStorage.setItem(KEY_MERGE, stringify(VAL_MERGE_1), err1 => { - expectAsyncNoError('testMerge/setItem', err1); - AsyncStorage.mergeItem(KEY_MERGE, stringify(VAL_MERGE_2), err2 => { - expectAsyncNoError('testMerge/mergeItem', err2); - AsyncStorage.getItem(KEY_MERGE, (err3, result) => { - expectAsyncNoError('testMerge/setItem', err3); - expectEqual( - JSON.parse(nullthrows(result)), - VAL_MERGE_EXPECT, - 'testMerge', - ); - updateMessage('objects deeply merged\nDone!'); - runTestCase('multi set and get', testOptimizedMultiGet); - }); - }); - }); -} - -function testOptimizedMultiGet() { - let batch = [ - [KEY_1, VAL_1], - [KEY_2, VAL_2], - ]; - let keys = batch.map(([key, value]) => key); - AsyncStorage.multiSet(batch, err1 => { - // yes, twice on purpose - [1, 2].forEach(i => { - expectAsyncNoError(`${i} testOptimizedMultiGet/multiSet`, err1); - AsyncStorage.multiGet(keys, (err2, result) => { - expectAsyncNoError(`${i} testOptimizedMultiGet/multiGet`, err2); - expectEqual(result, batch, `${i} testOptimizedMultiGet multiGet`); - updateMessage( - 'multiGet([key_1, key_2]) correctly returned ' + stringify(result), - ); - done(); - }); - }); - }); -} - -class AsyncStorageTest extends React.Component<{...}, $FlowFixMeState> { - state: any | {|done: boolean, messages: string|} = { - messages: 'Initializing...', - done: false, - }; - - componentDidMount() { - done = () => - this.setState({done: true}, () => { - TestModule.markTestCompleted(); - }); - updateMessage = (msg: string) => { - this.setState({messages: this.state.messages.concat('\n' + msg)}); - DEBUG && console.log(msg); - }; - AsyncStorage.clear(testSetAndGet); - } - - render(): React.Node { - return ( - - - { - /* $FlowFixMe[incompatible-type] (>=0.54.0 site=react_native_fb,react_ - * native_oss) This comment suppresses an error found when Flow v0.54 - * was deployed. To see the error delete this comment and run Flow. - */ - this.constructor.displayName + ': ' - } - {this.state.done ? 'Done' : 'Testing...'} - {'\n\n' + this.state.messages} - - - ); - } -} - -const styles = StyleSheet.create({ - container: { - backgroundColor: 'white', - padding: 40, - }, -}); - -AsyncStorageTest.displayName = 'AsyncStorageTest'; - -module.exports = AsyncStorageTest; diff --git a/IntegrationTests/BUCK b/IntegrationTests/BUCK index fa6fe51a3c53..0ac92a5c69e3 100644 --- a/IntegrationTests/BUCK +++ b/IntegrationTests/BUCK @@ -19,7 +19,6 @@ rn_library( ), labels = [ "pfh:ReactNative_CommonInfrastructurePlaceholder", - "supermodule:xplat/default/public.react_native.tests", ], skip_processors = True, visibility = ["PUBLIC"], diff --git a/IntegrationTests/GlobalEvalWithSourceUrlTest.js b/IntegrationTests/GlobalEvalWithSourceUrlTest.js index dd3206f3c9a3..6b1d83ed765a 100644 --- a/IntegrationTests/GlobalEvalWithSourceUrlTest.js +++ b/IntegrationTests/GlobalEvalWithSourceUrlTest.js @@ -20,7 +20,7 @@ const {View} = ReactNative; const {TestModule} = ReactNative.NativeModules; class GlobalEvalWithSourceUrlTest extends React.Component<{...}> { - componentDidMount() { + componentDidMount(): void { if (typeof global.globalEvalWithSourceUrl !== 'function') { throw new Error( 'Expected to find globalEvalWithSourceUrl function on global object but found ' + diff --git a/IntegrationTests/ImageSnapshotTest.js b/IntegrationTests/ImageSnapshotTest.js index 0d6aa3bb67f6..62895ae33e77 100644 --- a/IntegrationTests/ImageSnapshotTest.js +++ b/IntegrationTests/ImageSnapshotTest.js @@ -16,7 +16,7 @@ const {Image} = ReactNative; const {TestModule} = ReactNative.NativeModules; class ImageSnapshotTest extends React.Component<{...}> { - componentDidMount() { + componentDidMount(): void { if (!TestModule.verifySnapshot) { throw new Error('TestModule.verifySnapshot not defined.'); } diff --git a/IntegrationTests/IntegrationTestsApp.js b/IntegrationTests/IntegrationTestsApp.js index bb8efcbcd497..442e70327482 100644 --- a/IntegrationTests/IntegrationTestsApp.js +++ b/IntegrationTests/IntegrationTestsApp.js @@ -20,7 +20,6 @@ const {AppRegistry, ScrollView, StyleSheet, Text, TouchableOpacity, View} = const TESTS = [ require('./IntegrationTestHarnessTest'), require('./TimersTest'), - require('./AsyncStorageTest'), require('./LayoutEventsTest'), require('./AppEventsTest'), require('./SimpleSnapshotTest'), @@ -46,11 +45,11 @@ require('./LoggingTestModule'); type Test = any; class IntegrationTestsApp extends React.Component<{...}, $FlowFixMeState> { - state = { + state: {test: ?Test} = { test: (null: ?Test), }; - render() { + render(): React.Node { if (this.state.test) { return ( diff --git a/IntegrationTests/SimpleSnapshotTest.js b/IntegrationTests/SimpleSnapshotTest.js index 27e5d2c12d54..347de5c815d3 100644 --- a/IntegrationTests/SimpleSnapshotTest.js +++ b/IntegrationTests/SimpleSnapshotTest.js @@ -17,7 +17,7 @@ const {StyleSheet, View} = ReactNative; const {TestModule} = ReactNative.NativeModules; class SimpleSnapshotTest extends React.Component<{...}> { - componentDidMount() { + componentDidMount(): void { if (!TestModule.verifySnapshot) { throw new Error('TestModule.verifySnapshot not defined.'); } diff --git a/IntegrationTests/SyncMethodTest.js b/IntegrationTests/SyncMethodTest.js index febe02154375..ab5166c057e4 100644 --- a/IntegrationTests/SyncMethodTest.js +++ b/IntegrationTests/SyncMethodTest.js @@ -17,7 +17,7 @@ const {View} = ReactNative; const {TestModule, RNTesterTestModule} = ReactNative.NativeModules; class SyncMethodTest extends React.Component<{...}> { - componentDidMount() { + componentDidMount(): void { if ( RNTesterTestModule.echoString('test string value') !== 'test string value' ) { diff --git a/IntegrationTests/TimersTest.js b/IntegrationTests/TimersTest.js index 64b3f1233b6a..3a74f97edf31 100644 --- a/IntegrationTests/TimersTest.js +++ b/IntegrationTests/TimersTest.js @@ -39,7 +39,7 @@ class TimersTest extends React.Component { }; setTimeout(fn: () => void, time: number): TimeoutID { - const id = setTimeout(() => { + const id: TimeoutID = setTimeout(() => { this._timeoutIDs.delete(id); fn(); }, time); @@ -70,7 +70,7 @@ class TimersTest extends React.Component { } setImmediate(fn: () => void): ImmediateID { - const id = setImmediate(() => { + const id: any = setImmediate(() => { this._immediateIDs.delete(id); fn(); }); @@ -81,7 +81,7 @@ class TimersTest extends React.Component { } requestAnimationFrame(fn: () => void): AnimationFrameID { - const id = requestAnimationFrame(() => { + const id: AnimationFrameID = requestAnimationFrame(() => { this._animationFrameIDs.delete(id); fn(); }); @@ -253,7 +253,7 @@ class TimersTest extends React.Component { ); } - _incrementInterval() { + _incrementInterval(): void { if (this.state.count > 3) { throw new Error('interval incremented past end.'); } diff --git a/IntegrationTests/WebSocketTest.js b/IntegrationTests/WebSocketTest.js index 6fbcbfb6cd8f..648478bacf10 100644 --- a/IntegrationTests/WebSocketTest.js +++ b/IntegrationTests/WebSocketTest.js @@ -69,11 +69,11 @@ class WebSocketTest extends React.Component<{...}, State> { }); }; - _socketIsConnected = () => { + _socketIsConnected = (): boolean => { return this.state.socketState === 1; //'OPEN' }; - _socketIsDisconnected = () => { + _socketIsDisconnected = (): boolean => { return this.state.socketState === 3; //'CLOSED' }; @@ -106,7 +106,7 @@ class WebSocketTest extends React.Component<{...}, State> { this._sendText(this.state.testMessage); }; - _receivedTestExpectedResponse = () => { + _receivedTestExpectedResponse = (): boolean => { return this.state.lastMessage === this.state.testExpectedResponse; }; diff --git a/Libraries/ActionSheetIOS/ActionSheetIOS.d.ts b/Libraries/ActionSheetIOS/ActionSheetIOS.d.ts new file mode 100644 index 000000000000..89bdd3732399 --- /dev/null +++ b/Libraries/ActionSheetIOS/ActionSheetIOS.d.ts @@ -0,0 +1,80 @@ +/** + * 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. + * + * @format + */ + +import {ProcessedColorValue} from '../StyleSheet/processColor'; +import {ColorValue} from '../StyleSheet/StyleSheet'; + +/** + * @see: https://reactnative.dev/docs/actionsheetios#content + */ +export interface ActionSheetIOSOptions { + title?: string | undefined; + options: string[]; + cancelButtonIndex?: number | undefined; + destructiveButtonIndex?: number | number[] | undefined | null; + message?: string | undefined; + anchor?: number | undefined; + tintColor?: ColorValue | ProcessedColorValue | undefined; + cancelButtonTintColor?: ColorValue | ProcessedColorValue | undefined; + userInterfaceStyle?: 'light' | 'dark' | undefined; + disabledButtonIndices?: number[] | undefined; +} + +export interface ShareActionSheetIOSOptions { + message?: string | undefined; + url?: string | undefined; + subject?: string | undefined; + anchor?: number | undefined; + /** The activities to exclude from the ActionSheet. + * For example: ['com.apple.UIKit.activity.PostToTwitter'] + */ + excludedActivityTypes?: string[] | undefined; +} + +/** + * @see https://reactnative.dev/docs/actionsheetios#content + */ +export interface ActionSheetIOSStatic { + /** + * Display an iOS action sheet. The `options` object must contain one or more + * of: + * - `options` (array of strings) - a list of button titles (required) + * - `cancelButtonIndex` (int) - index of cancel button in `options` + * - `destructiveButtonIndex` (int) - index of destructive button in `options` + * - `title` (string) - a title to show above the action sheet + * - `message` (string) - a message to show below the title + */ + showActionSheetWithOptions: ( + options: ActionSheetIOSOptions, + callback: (buttonIndex: number) => void, + ) => void; + + /** + * Display the iOS share sheet. The `options` object should contain + * one or both of `message` and `url` and can additionally have + * a `subject` or `excludedActivityTypes`: + * + * - `url` (string) - a URL to share + * - `message` (string) - a message to share + * - `subject` (string) - a subject for the message + * - `excludedActivityTypes` (array) - the activities to exclude from the ActionSheet + * + * NOTE: if `url` points to a local file, or is a base64-encoded + * uri, the file it points to will be loaded and shared directly. + * In this way, you can share images, videos, PDF files, etc. + */ + showShareActionSheetWithOptions: ( + options: ShareActionSheetIOSOptions, + failureCallback: (error: Error) => void, + successCallback: (success: boolean, method: string) => void, + ) => void; +} + +export const ActionSheetIOS: ActionSheetIOSStatic; +export type ActionSheetIOS = ActionSheetIOSStatic; diff --git a/Libraries/ActionSheetIOS/ActionSheetIOS.js b/Libraries/ActionSheetIOS/ActionSheetIOS.js index 0904d14024f2..c9aaca1e3ad5 100644 --- a/Libraries/ActionSheetIOS/ActionSheetIOS.js +++ b/Libraries/ActionSheetIOS/ActionSheetIOS.js @@ -8,12 +8,13 @@ * @format */ +import type {ProcessedColorValue} from '../StyleSheet/processColor'; +import type {ColorValue} from '../StyleSheet/StyleSheet'; + import RCTActionSheetManager from './NativeActionSheetManager'; -const invariant = require('invariant'); const processColor = require('../StyleSheet/processColor'); -import type {ColorValue} from '../StyleSheet/StyleSheet'; -import type {ProcessedColorValue} from '../StyleSheet/processColor'; +const invariant = require('invariant'); /** * Display action sheets and share sheets on iOS. diff --git a/Libraries/ActionSheetIOS/NativeActionSheetManager.js b/Libraries/ActionSheetIOS/NativeActionSheetManager.js index 1f9ead0bdd8f..8247ac260aff 100644 --- a/Libraries/ActionSheetIOS/NativeActionSheetManager.js +++ b/Libraries/ActionSheetIOS/NativeActionSheetManager.js @@ -9,6 +9,7 @@ */ import type {TurboModule} from '../TurboModule/RCTExport'; + import * as TurboModuleRegistry from '../TurboModule/TurboModuleRegistry'; export interface Spec extends TurboModule { diff --git a/Libraries/Alert/Alert.d.ts b/Libraries/Alert/Alert.d.ts new file mode 100644 index 000000000000..a05fe55a877f --- /dev/null +++ b/Libraries/Alert/Alert.d.ts @@ -0,0 +1,90 @@ +/** + * 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. + * + * @format + */ + +/** + * @see https://reactnative.dev/docs/alert#content + */ +export interface AlertButton { + text?: string | undefined; + onPress?: ((value?: string) => void) | undefined; + isPreferred?: boolean; + style?: 'default' | 'cancel' | 'destructive' | undefined; +} + +interface AlertOptions { + /** @platform android */ + cancelable?: boolean | undefined; + userInterfaceStyle?: 'unspecified' | 'light' | 'dark'; + /** @platform android */ + onDismiss?: (() => void) | undefined; +} + +/** + * Launches an alert dialog with the specified title and message. + * + * Optionally provide a list of buttons. Tapping any button will fire the + * respective onPress callback and dismiss the alert. By default, the only + * button will be an 'OK' button. + * + * This is an API that works both on iOS and Android and can show static + * alerts. To show an alert that prompts the user to enter some information, + * see `AlertIOS`; entering text in an alert is common on iOS only. + * + * ## iOS + * + * On iOS you can specify any number of buttons. Each button can optionally + * specify a style, which is one of 'default', 'cancel' or 'destructive'. + * + * ## Android + * + * On Android at most three buttons can be specified. Android has a concept + * of a neutral, negative and a positive button: + * + * - If you specify one button, it will be the 'positive' one (such as 'OK') + * - Two buttons mean 'negative', 'positive' (such as 'Cancel', 'OK') + * - Three buttons mean 'neutral', 'negative', 'positive' (such as 'Later', 'Cancel', 'OK') + * + * ``` + * // Works on both iOS and Android + * Alert.alert( + * 'Alert Title', + * 'My Alert Msg', + * [ + * {text: 'Ask me later', onPress: () => console.log('Ask me later pressed')}, + * {text: 'Cancel', onPress: () => console.log('Cancel Pressed'), style: 'cancel'}, + * {text: 'OK', onPress: () => console.log('OK Pressed')}, + * ] + * ) + * ``` + */ +export interface AlertStatic { + alert: ( + title: string, + message?: string, + buttons?: AlertButton[], + options?: AlertOptions, + ) => void; + prompt: ( + title: string, + message?: string, + callbackOrButtons?: ((text: string) => void) | AlertButton[], + type?: AlertType, + defaultValue?: string, + keyboardType?: string, + ) => void; +} + +export type AlertType = + | 'default' + | 'plain-text' + | 'secure-text' + | 'login-password'; + +export const Alert: AlertStatic; +export type Alert = AlertStatic; diff --git a/Libraries/Alert/Alert.js b/Libraries/Alert/Alert.js index 1a943e1bbd7b..a6a0ccbfecc5 100644 --- a/Libraries/Alert/Alert.js +++ b/Libraries/Alert/Alert.js @@ -22,6 +22,7 @@ export type AlertButtonStyle = 'default' | 'cancel' | 'destructive'; export type Buttons = Array<{ text?: string, onPress?: ?Function, + isPreferred?: boolean, style?: AlertButtonStyle, ... }>; @@ -122,10 +123,11 @@ class Alert { options?: Options, ): void { if (Platform.OS === 'ios') { - let callbacks = []; + let callbacks: Array = []; const buttons = []; let cancelButtonKey; let destructiveButtonKey; + let preferredButtonKey; if (typeof callbackOrButtons === 'function') { callbacks = [callbackOrButtons]; } else if (Array.isArray(callbackOrButtons)) { @@ -136,6 +138,9 @@ class Alert { } else if (btn.style === 'destructive') { destructiveButtonKey = String(index); } + if (btn.isPreferred) { + preferredButtonKey = String(index); + } if (btn.text || index < (callbackOrButtons || []).length - 1) { const btnDef: {[number]: string} = {}; btnDef[index] = btn.text || ''; @@ -153,6 +158,7 @@ class Alert { defaultValue, cancelButtonKey, destructiveButtonKey, + preferredButtonKey, keyboardType, userInterfaceStyle: options?.userInterfaceStyle || undefined, }, diff --git a/Libraries/Alert/Alert.js.flow b/Libraries/Alert/Alert.js.flow new file mode 100644 index 000000000000..be6f948f557a --- /dev/null +++ b/Libraries/Alert/Alert.js.flow @@ -0,0 +1,49 @@ +/** + * 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. + * + * @format + * @flow + */ + +export type AlertType = + | 'default' + | 'plain-text' + | 'secure-text' + | 'login-password'; +export type AlertButtonStyle = 'default' | 'cancel' | 'destructive'; +export type Buttons = Array<{ + text?: string, + onPress?: ?Function, + isPreferred?: boolean, + style?: AlertButtonStyle, + ... +}>; +type Options = { + cancelable?: ?boolean, + userInterfaceStyle?: 'unspecified' | 'light' | 'dark', + onDismiss?: ?() => void, + ... +}; + +declare class Alert { + static alert( + title: ?string, + message?: ?string, + buttons?: Buttons, + options?: Options, + ): void; + static prompt( + title: ?string, + message?: ?string, + callbackOrButtons?: ?(((text: string) => void) | Buttons), + type?: ?AlertType, + defaultValue?: string, + keyboardType?: string, + options?: Options, + ): void; +} + +module.exports = Alert; diff --git a/Libraries/Alert/NativeAlertManager.js b/Libraries/Alert/NativeAlertManager.js index 9597ef155a52..7f7b1cf6a9b6 100644 --- a/Libraries/Alert/NativeAlertManager.js +++ b/Libraries/Alert/NativeAlertManager.js @@ -9,6 +9,7 @@ */ import type {TurboModule} from '../TurboModule/RCTExport'; + import * as TurboModuleRegistry from '../TurboModule/TurboModuleRegistry'; export type Args = {| @@ -19,6 +20,7 @@ export type Args = {| defaultValue?: string, cancelButtonKey?: string, destructiveButtonKey?: string, + preferredButtonKey?: string, keyboardType?: string, userInterfaceStyle?: string, |}; diff --git a/Libraries/Alert/RCTAlertManager.ios.js b/Libraries/Alert/RCTAlertManager.ios.js index 01f055da53d4..d4e9eab97ff6 100644 --- a/Libraries/Alert/RCTAlertManager.ios.js +++ b/Libraries/Alert/RCTAlertManager.ios.js @@ -8,9 +8,10 @@ * @flow strict-local */ -import NativeAlertManager from './NativeAlertManager'; import type {Args} from './NativeAlertManager'; +import NativeAlertManager from './NativeAlertManager'; + module.exports = { alertWithArgs( args: Args, diff --git a/Libraries/Animated/Animated.d.ts b/Libraries/Animated/Animated.d.ts new file mode 100644 index 000000000000..3301ff377246 --- /dev/null +++ b/Libraries/Animated/Animated.d.ts @@ -0,0 +1,615 @@ +/** + * 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. + * + * @format + */ + +import type * as React from 'react'; +import {ScrollView} from '../Components/ScrollView/ScrollView'; +import {View} from '../Components/View/View'; +import {Image} from '../Image/Image'; +import {FlatListProps} from '../Lists/FlatList'; +import {DefaultSectionT, SectionListProps} from '../Lists/SectionList'; +import {ColorValue} from '../StyleSheet/StyleSheet'; +import {Text} from '../Text/Text'; +import {NativeSyntheticEvent} from '../Types/CoreEventTypes'; + +export namespace Animated { + type AnimatedValue = Value; + type AnimatedValueXY = ValueXY; + + class Animated { + // Internal class, no public API. + } + + class AnimatedNode { + /** + * Adds an asynchronous listener to the value so you can observe updates from + * animations. This is useful because there is no way to + * synchronously read the value because it might be driven natively. + * + * See https://reactnative.dev/docs/animatedvalue.html#addlistener + */ + addListener(callback: (value: any) => any): string; + /** + * Unregister a listener. The `id` param shall match the identifier + * previously returned by `addListener()`. + * + * See https://reactnative.dev/docs/animatedvalue.html#removelistener + */ + removeListener(id: string): void; + /** + * Remove all registered listeners. + * + * See https://reactnative.dev/docs/animatedvalue.html#removealllisteners + */ + removeAllListeners(): void; + + hasListeners(): boolean; + } + + class AnimatedWithChildren extends AnimatedNode { + // Internal class, no public API. + } + + type RgbaValue = { + readonly r: number; + readonly g: number; + readonly b: number; + readonly a: number; + }; + + type RgbaAnimatedValue = { + readonly r: AnimatedValue; + readonly g: AnimatedValue; + readonly b: AnimatedValue; + readonly a: AnimatedValue; + }; + + type AnimatedConfig = { + readonly useNativeDriver: boolean; + }; + + class AnimatedColor extends AnimatedWithChildren { + r: AnimatedValue; + g: AnimatedValue; + b: AnimatedValue; + a: AnimatedValue; + + constructor( + valueIn?: RgbaValue | RgbaAnimatedValue | ColorValue | null, + config?: AnimatedConfig | null, + ); + nativeColor: unknown; // Unsure what to do here + setValue: (value: RgbaValue | ColorValue) => void; + setOffset: (offset: RgbaValue) => void; + flattenOffset: () => void; + extractOffset: () => void; + addListener: (callback: (value: ColorValue) => unknown) => string; + removeListener: (id: string) => void; + removeAllListeners: () => void; + stopAnimation: (callback: (value: ColorValue) => unknown) => void; + resetAnimation: (callback: (value: ColorValue) => unknown) => void; + } + + class AnimatedInterpolation< + OutputT extends number | string, + > extends AnimatedWithChildren { + interpolate( + config: InterpolationConfigType, + ): AnimatedInterpolation; + } + + type ExtrapolateType = 'extend' | 'identity' | 'clamp'; + + type InterpolationConfigType = { + inputRange: number[]; + outputRange: number[] | string[]; + easing?: ((input: number) => number) | undefined; + extrapolate?: ExtrapolateType | undefined; + extrapolateLeft?: ExtrapolateType | undefined; + extrapolateRight?: ExtrapolateType | undefined; + }; + + type ValueListenerCallback = (state: {value: number}) => void; + + type Animation = { + start( + fromValue: number, + onUpdate: (value: number) => void, + onEnd: EndCallback | null, + previousAnimation: Animation | null, + animatedValue: AnimatedValue, + ): void; + stop(): void; + }; + + /** + * Standard value for driving animations. One `Animated.Value` can drive + * multiple properties in a synchronized fashion, but can only be driven by one + * mechanism at a time. Using a new mechanism (e.g. starting a new animation, + * or calling `setValue`) will stop any previous ones. + */ + export class Value extends AnimatedWithChildren { + constructor(value: number, config?: AnimatedConfig | null); + + /** + * Directly set the value. This will stop any animations running on the value + * and update all the bound properties. + */ + setValue(value: number): void; + + /** + * Sets an offset that is applied on top of whatever value is set, whether via + * `setValue`, an animation, or `Animated.event`. Useful for compensating + * things like the start of a pan gesture. + */ + setOffset(offset: number): void; + + /** + * Merges the offset value into the base value and resets the offset to zero. + * The final output of the value is unchanged. + */ + flattenOffset(): void; + + /** + * Sets the offset value to the base value, and resets the base value to zero. + * The final output of the value is unchanged. + */ + extractOffset(): void; + + /** + * Adds an asynchronous listener to the value so you can observe updates from + * animations. This is useful because there is no way to + * synchronously read the value because it might be driven natively. + */ + addListener(callback: ValueListenerCallback): string; + + removeListener(id: string): void; + + removeAllListeners(): void; + + /** + * Stops any running animation or tracking. `callback` is invoked with the + * final value after stopping the animation, which is useful for updating + * state to match the animation position with layout. + */ + stopAnimation(callback?: (value: number) => void): void; + + /** + * Stops any animation and resets the value to its original. + * + * See https://reactnative.dev/docs/animatedvalue#resetanimation + */ + resetAnimation(callback?: (value: number) => void): void; + + /** + * Interpolates the value before updating the property, e.g. mapping 0-1 to + * 0-10. + */ + interpolate( + config: InterpolationConfigType, + ): AnimatedInterpolation; + + /** + * Typically only used internally, but could be used by a custom Animation + * class. + * + * See https://reactnative.dev/docs/animatedvalue#animate + */ + animate(animation: Animation, callback?: EndCallback | null): void; + } + + type ValueXYListenerCallback = (value: {x: number; y: number}) => void; + + /** + * 2D Value for driving 2D animations, such as pan gestures. Almost identical + * API to normal `Animated.Value`, but multiplexed. Contains two regular + * `Animated.Value`s under the hood. + */ + export class ValueXY extends AnimatedWithChildren { + x: AnimatedValue; + y: AnimatedValue; + + constructor( + valueIn?: {x: number | AnimatedValue; y: number | AnimatedValue}, + config?: AnimatedConfig | null, + ); + + setValue(value: {x: number; y: number}): void; + + setOffset(offset: {x: number; y: number}): void; + + flattenOffset(): void; + + extractOffset(): void; + + resetAnimation(callback?: (value: {x: number; y: number}) => void): void; + + stopAnimation(callback?: (value: {x: number; y: number}) => void): void; + + addListener(callback: ValueXYListenerCallback): string; + + removeListener(id: string): void; + + /** + * Converts `{x, y}` into `{left, top}` for use in style, e.g. + * + *```javascript + * style={this.state.anim.getLayout()} + *``` + */ + getLayout(): {[key: string]: AnimatedValue}; + + /** + * Converts `{x, y}` into a useable translation transform, e.g. + * + *```javascript + * style={{ + * transform: this.state.anim.getTranslateTransform() + * }} + *``` + */ + getTranslateTransform(): [ + {translateX: AnimatedValue}, + {translateY: AnimatedValue}, + ]; + } + + type EndResult = {finished: boolean}; + type EndCallback = (result: EndResult) => void; + + export interface CompositeAnimation { + /** + * Animations are started by calling start() on your animation. + * start() takes a completion callback that will be called when the + * animation is done or when the animation is done because stop() was + * called on it before it could finish. + * + * @param callback - Optional function that will be called + * after the animation finished running normally or when the animation + * is done because stop() was called on it before it could finish + * + * @example + * Animated.timing({}).start(({ finished }) => { + * // completion callback + * }); + */ + start: (callback?: EndCallback) => void; + /** + * Stops any running animation. + */ + stop: () => void; + /** + * Stops any running animation and resets the value to its original. + */ + reset: () => void; + } + + interface AnimationConfig { + isInteraction?: boolean | undefined; + useNativeDriver: boolean; + } + + /** + * Animates a value from an initial velocity to zero based on a decay + * coefficient. + */ + export function decay( + value: AnimatedValue | AnimatedValueXY, + config: DecayAnimationConfig, + ): CompositeAnimation; + + interface DecayAnimationConfig extends AnimationConfig { + velocity: number | {x: number; y: number}; + deceleration?: number | undefined; + } + + /** + * Animates a value along a timed easing curve. The `Easing` module has tons + * of pre-defined curves, or you can use your own function. + */ + export const timing: ( + value: AnimatedValue | AnimatedValueXY, + config: TimingAnimationConfig, + ) => CompositeAnimation; + + interface TimingAnimationConfig extends AnimationConfig { + toValue: + | number + | AnimatedValue + | {x: number; y: number} + | AnimatedValueXY + | AnimatedInterpolation; + easing?: ((value: number) => number) | undefined; + duration?: number | undefined; + delay?: number | undefined; + } + + interface SpringAnimationConfig extends AnimationConfig { + toValue: + | number + | AnimatedValue + | {x: number; y: number} + | AnimatedValueXY + | RgbaValue + | AnimatedColor + | AnimatedInterpolation; + overshootClamping?: boolean | undefined; + restDisplacementThreshold?: number | undefined; + restSpeedThreshold?: number | undefined; + velocity?: number | {x: number; y: number} | undefined; + bounciness?: number | undefined; + speed?: number | undefined; + tension?: number | undefined; + friction?: number | undefined; + stiffness?: number | undefined; + mass?: number | undefined; + damping?: number | undefined; + delay?: number | undefined; + } + + interface LoopAnimationConfig { + iterations?: number | undefined; // default -1 for infinite + /** + * Defaults to `true` + */ + resetBeforeIteration?: boolean | undefined; + } + + /** + * Creates a new Animated value composed from two Animated values added + * together. + */ + export function add( + a: Animated, + b: Animated, + ): AnimatedAddition; + + class AnimatedAddition< + OutputT extends number | string, + > extends AnimatedInterpolation {} + + /** + * Creates a new Animated value composed by subtracting the second Animated + * value from the first Animated value. + */ + export function subtract( + a: Animated, + b: Animated, + ): AnimatedSubtraction; + + class AnimatedSubtraction< + OutputT extends number | string, + > extends AnimatedInterpolation {} + + /** + * Creates a new Animated value composed by dividing the first Animated + * value by the second Animated value. + */ + export function divide( + a: Animated, + b: Animated, + ): AnimatedDivision; + + class AnimatedDivision< + OutputT extends number | string, + > extends AnimatedInterpolation {} + + /** + * Creates a new Animated value composed from two Animated values multiplied + * together. + */ + export function multiply( + a: Animated, + b: Animated, + ): AnimatedMultiplication; + + class AnimatedMultiplication< + OutputT extends number | string, + > extends AnimatedInterpolation {} + + /** + * Creates a new Animated value that is the (non-negative) modulo of the + * provided Animated value + */ + export function modulo( + a: Animated, + modulus: number, + ): AnimatedModulo; + + class AnimatedModulo< + OutputT extends number | string, + > extends AnimatedInterpolation {} + + /** + * Create a new Animated value that is limited between 2 values. It uses the + * difference between the last value so even if the value is far from the bounds + * it will start changing when the value starts getting closer again. + * (`value = clamp(value + diff, min, max)`). + * + * This is useful with scroll events, for example, to show the navbar when + * scrolling up and to hide it when scrolling down. + */ + export function diffClamp( + a: Animated, + min: number, + max: number, + ): AnimatedDiffClamp; + + class AnimatedDiffClamp< + OutputT extends number | string, + > extends AnimatedInterpolation {} + + /** + * Starts an animation after the given delay. + */ + export function delay(time: number): CompositeAnimation; + + /** + * Starts an array of animations in order, waiting for each to complete + * before starting the next. If the current running animation is stopped, no + * following animations will be started. + */ + export function sequence( + animations: Array, + ): CompositeAnimation; + + /** + * Array of animations may run in parallel (overlap), but are started in + * sequence with successive delays. Nice for doing trailing effects. + */ + + export function stagger( + time: number, + animations: Array, + ): CompositeAnimation; + + /** + * Loops a given animation continuously, so that each time it reaches the end, + * it resets and begins again from the start. Can specify number of times to + * loop using the key 'iterations' in the config. Will loop without blocking + * the UI thread if the child animation is set to 'useNativeDriver'. + */ + + export function loop( + animation: CompositeAnimation, + config?: LoopAnimationConfig, + ): CompositeAnimation; + + /** + * Spring animation based on Rebound and Origami. Tracks velocity state to + * create fluid motions as the `toValue` updates, and can be chained together. + */ + export function spring( + value: AnimatedValue | AnimatedValueXY, + config: SpringAnimationConfig, + ): CompositeAnimation; + + type ParallelConfig = { + stopTogether?: boolean | undefined; // If one is stopped, stop all. default: true + }; + + /** + * Starts an array of animations all at the same time. By default, if one + * of the animations is stopped, they will all be stopped. You can override + * this with the `stopTogether` flag. + */ + export function parallel( + animations: Array, + config?: ParallelConfig, + ): CompositeAnimation; + + type Mapping = {[key: string]: Mapping} | AnimatedValue; + interface EventConfig { + listener?: ((event: NativeSyntheticEvent) => void) | undefined; + useNativeDriver: boolean; + } + + /** + * Takes an array of mappings and extracts values from each arg accordingly, + * then calls `setValue` on the mapped outputs. e.g. + * + *```javascript + * onScroll={Animated.event( + * [{nativeEvent: {contentOffset: {x: this._scrollX}}}] + * {listener}, // Optional async listener + * ) + * ... + * onPanResponderMove: Animated.event([ + * null, // raw event arg ignored + * {dx: this._panX}, // gestureState arg + * ]), + *``` + */ + export function event( + argMapping: Array, + config?: EventConfig, + ): (...args: any[]) => void; + + export type ComponentProps = T extends + | React.ComponentType + | React.Component + ? P + : never; + + export type LegacyRef = {getNode(): C}; + + type Nullable = undefined | null; + type Primitive = string | number | boolean | symbol; + type Builtin = Function | Date | Error | RegExp; + + interface WithAnimatedArray

extends Array> {} + type WithAnimatedObject = { + [K in keyof T]: WithAnimatedValue; + }; + + export type WithAnimatedValue = T extends Builtin | Nullable + ? T + : T extends Primitive + ? T | Value | AnimatedInterpolation // add `Value` and `AnimatedInterpolation` but also preserve original T + : T extends Array + ? WithAnimatedArray

+ : T extends {} + ? WithAnimatedObject + : T; // in case it's something we don't yet know about (for .e.g bigint) + + type NonAnimatedProps = 'key' | 'ref'; + + type TAugmentRef = T extends React.Ref + ? React.Ref> + : never; + + export type AnimatedProps = { + [key in keyof T]: key extends NonAnimatedProps + ? key extends 'ref' + ? TAugmentRef + : T[key] + : WithAnimatedValue; + }; + + export interface AnimatedComponent> + extends React.FC>> {} + + export type AnimatedComponentOptions = { + collapsable?: boolean; + }; + + /** + * Make any React component Animatable. Used to create `Animated.View`, etc. + */ + export function createAnimatedComponent>( + component: T, + options?: AnimatedComponentOptions, + ): AnimatedComponent; + + /** + * Animated variants of the basic native views. Accepts Animated.Value for + * props and style. + */ + export const View: AnimatedComponent; + export const Image: AnimatedComponent; + export const Text: AnimatedComponent; + export const ScrollView: AnimatedComponent; + + /** + * FlatList and SectionList infer generic Type defined under their `data` and `section` props. + */ + export class FlatList extends React.Component< + AnimatedProps> + > {} + export class SectionList< + ItemT = any, + SectionT = DefaultSectionT, + > extends React.Component>> {} +} + +// We need to alias these views so we can reference them in the Animated +// namespace where their names are shadowed. +declare const _View: typeof View; +declare const _Image: typeof Image; +declare const _Text: typeof Text; +declare const _ScrollView: typeof ScrollView; diff --git a/Libraries/Animated/Animated.js b/Libraries/Animated/Animated.js index 88fd9dbb188d..ec17ef764543 100644 --- a/Libraries/Animated/Animated.js +++ b/Libraries/Animated/Animated.js @@ -8,7 +8,8 @@ * @format */ -import Platform from '../Utilities/Platform'; +export type {CompositeAnimation, Numeric} from './AnimatedImplementation'; + import typeof AnimatedFlatList from './components/AnimatedFlatList'; import typeof AnimatedImage from './components/AnimatedImage'; import typeof AnimatedScrollView from './components/AnimatedScrollView'; @@ -16,31 +17,32 @@ import typeof AnimatedSectionList from './components/AnimatedSectionList'; import typeof AnimatedText from './components/AnimatedText'; import typeof AnimatedView from './components/AnimatedView'; -import * as AnimatedMock from './AnimatedMock'; -import * as AnimatedImplementation from './AnimatedImplementation'; +import Platform from '../Utilities/Platform'; +import AnimatedImplementation from './AnimatedImplementation'; +import AnimatedMock from './AnimatedMock'; const Animated = ((Platform.isTesting ? AnimatedMock - : AnimatedImplementation): typeof AnimatedMock); + : AnimatedImplementation): typeof AnimatedImplementation); -module.exports = { +export default { get FlatList(): AnimatedFlatList { - return require('./components/AnimatedFlatList'); + return require('./components/AnimatedFlatList').default; }, get Image(): AnimatedImage { - return require('./components/AnimatedImage'); + return require('./components/AnimatedImage').default; }, get ScrollView(): AnimatedScrollView { - return require('./components/AnimatedScrollView'); + return require('./components/AnimatedScrollView').default; }, get SectionList(): AnimatedSectionList { - return require('./components/AnimatedSectionList'); + return require('./components/AnimatedSectionList').default; }, get Text(): AnimatedText { - return require('./components/AnimatedText'); + return require('./components/AnimatedText').default; }, get View(): AnimatedView { - return require('./components/AnimatedView'); + return require('./components/AnimatedView').default; }, ...Animated, }; diff --git a/Libraries/Animated/AnimatedEvent.js b/Libraries/Animated/AnimatedEvent.js index af1d660a8de7..7279c2af4dea 100644 --- a/Libraries/Animated/AnimatedEvent.js +++ b/Libraries/Animated/AnimatedEvent.js @@ -10,17 +10,14 @@ 'use strict'; -const AnimatedValue = require('./nodes/AnimatedValue'); -const AnimatedValueXY = require('./nodes/AnimatedValueXY'); -const NativeAnimatedHelper = require('./NativeAnimatedHelper'); -const ReactNative = require('../Renderer/shims/ReactNative'); - -const invariant = require('invariant'); - -const {shouldUseNativeDriver} = require('./NativeAnimatedHelper'); - import type {PlatformConfig} from './AnimatedPlatformConfig'; +import {findNodeHandle} from '../ReactNative/RendererProxy'; +import NativeAnimatedHelper from './NativeAnimatedHelper'; +import AnimatedValue from './nodes/AnimatedValue'; +import AnimatedValueXY from './nodes/AnimatedValueXY'; +import invariant from 'invariant'; + export type Mapping = | {[key: string]: Mapping, ...} | AnimatedValue @@ -31,7 +28,7 @@ export type EventConfig = { platformConfig?: PlatformConfig, }; -function attachNativeEvent( +export function attachNativeEvent( viewRef: any, eventName: string, argMapping: $ReadOnlyArray, @@ -67,7 +64,7 @@ function attachNativeEvent( // Assume that the event containing `nativeEvent` is always the first argument. traverse(argMapping[0].nativeEvent, []); - const viewTag = ReactNative.findNodeHandle(viewRef); + const viewTag = findNodeHandle(viewRef); if (viewTag != null) { eventMappings.forEach(mapping => { NativeAnimatedHelper.API.addAnimatedEventToView( @@ -146,7 +143,7 @@ function validateMapping(argMapping: $ReadOnlyArray, args: any) { }); } -class AnimatedEvent { +export class AnimatedEvent { _argMapping: $ReadOnlyArray; _listeners: Array = []; _attachedEvent: ?{detach: () => void, ...}; @@ -165,7 +162,7 @@ class AnimatedEvent { this.__addListener(config.listener); } this._attachedEvent = null; - this.__isNative = shouldUseNativeDriver(config); + this.__isNative = NativeAnimatedHelper.shouldUseNativeDriver(config); this.__platformConfig = config.platformConfig; } @@ -177,7 +174,7 @@ class AnimatedEvent { this._listeners = this._listeners.filter(listener => listener !== callback); } - __attach(viewRef: any, eventName: string) { + __attach(viewRef: any, eventName: string): void { invariant( this.__isNative, 'Only native driven events need to be attached.', @@ -191,7 +188,7 @@ class AnimatedEvent { ); } - __detach(viewTag: any, eventName: string) { + __detach(viewTag: any, eventName: string): void { invariant( this.__isNative, 'Only native driven events need to be detached.', @@ -257,5 +254,3 @@ class AnimatedEvent { this._listeners.forEach(listener => listener(...args)); }; } - -module.exports = {AnimatedEvent, attachNativeEvent}; diff --git a/Libraries/Animated/AnimatedImplementation.js b/Libraries/Animated/AnimatedImplementation.js index 5aaa29f33653..7e25c65f2a46 100644 --- a/Libraries/Animated/AnimatedImplementation.js +++ b/Libraries/Animated/AnimatedImplementation.js @@ -10,35 +10,33 @@ 'use strict'; -const {AnimatedEvent, attachNativeEvent} = require('./AnimatedEvent'); -const AnimatedAddition = require('./nodes/AnimatedAddition'); -const AnimatedDiffClamp = require('./nodes/AnimatedDiffClamp'); -const AnimatedDivision = require('./nodes/AnimatedDivision'); -const AnimatedInterpolation = require('./nodes/AnimatedInterpolation'); -const AnimatedModulo = require('./nodes/AnimatedModulo'); -const AnimatedMultiplication = require('./nodes/AnimatedMultiplication'); -const AnimatedNode = require('./nodes/AnimatedNode'); -const AnimatedSubtraction = require('./nodes/AnimatedSubtraction'); -const AnimatedTracking = require('./nodes/AnimatedTracking'); -const AnimatedValue = require('./nodes/AnimatedValue'); -const AnimatedValueXY = require('./nodes/AnimatedValueXY'); -const DecayAnimation = require('./animations/DecayAnimation'); -const SpringAnimation = require('./animations/SpringAnimation'); -const TimingAnimation = require('./animations/TimingAnimation'); - -const createAnimatedComponent = require('./createAnimatedComponent'); - +import type {EventConfig, Mapping} from './AnimatedEvent'; import type { AnimationConfig, EndCallback, EndResult, } from './animations/Animation'; -import type {TimingAnimationConfig} from './animations/TimingAnimation'; import type {DecayAnimationConfig} from './animations/DecayAnimation'; import type {SpringAnimationConfig} from './animations/SpringAnimation'; -import type {Mapping, EventConfig} from './AnimatedEvent'; +import type {TimingAnimationConfig} from './animations/TimingAnimation'; +import {AnimatedEvent, attachNativeEvent} from './AnimatedEvent'; +import DecayAnimation from './animations/DecayAnimation'; +import SpringAnimation from './animations/SpringAnimation'; +import TimingAnimation from './animations/TimingAnimation'; +import createAnimatedComponent from './createAnimatedComponent'; +import AnimatedAddition from './nodes/AnimatedAddition'; import AnimatedColor from './nodes/AnimatedColor'; +import AnimatedDiffClamp from './nodes/AnimatedDiffClamp'; +import AnimatedDivision from './nodes/AnimatedDivision'; +import AnimatedInterpolation from './nodes/AnimatedInterpolation'; +import AnimatedModulo from './nodes/AnimatedModulo'; +import AnimatedMultiplication from './nodes/AnimatedMultiplication'; +import AnimatedNode from './nodes/AnimatedNode'; +import AnimatedSubtraction from './nodes/AnimatedSubtraction'; +import AnimatedTracking from './nodes/AnimatedTracking'; +import AnimatedValue from './nodes/AnimatedValue'; +import AnimatedValueXY from './nodes/AnimatedValueXY'; export type CompositeAnimation = { start: (callback?: ?EndCallback) => void, @@ -369,7 +367,7 @@ const parallel = function ( ): CompositeAnimation { let doneCount = 0; // Make sure we only call stop() at most once for each animation - const hasEnded = {}; + const hasEnded: {[number]: boolean} = {}; const stopTogether = !(config && config.stopTogether === false); const result = { @@ -417,7 +415,7 @@ const parallel = function ( }); }, - _startNativeLoop: function () { + _startNativeLoop: function (): empty { throw new Error( 'Loops run using the native driver cannot contain Animated.parallel animations', ); @@ -575,7 +573,7 @@ export type {AnimatedNumeric as Numeric}; * * See https://reactnative.dev/docs/animated */ -module.exports = { +export default { /** * Standard value class for driving animations. Typically initialized with * `new Animated.Value(0);` diff --git a/Libraries/Animated/AnimatedMock.js b/Libraries/Animated/AnimatedMock.js index b7af8042fb90..c509c830f7d0 100644 --- a/Libraries/Animated/AnimatedMock.js +++ b/Libraries/Animated/AnimatedMock.js @@ -10,23 +10,21 @@ 'use strict'; +import type {Numeric as AnimatedNumeric} from './AnimatedImplementation'; import type {EndResult} from './animations/Animation'; - -const {AnimatedEvent, attachNativeEvent} = require('./AnimatedEvent'); -const AnimatedImplementation = require('./AnimatedImplementation'); -const AnimatedInterpolation = require('./nodes/AnimatedInterpolation'); -const AnimatedNode = require('./nodes/AnimatedNode'); -const AnimatedValue = require('./nodes/AnimatedValue'); -const AnimatedValueXY = require('./nodes/AnimatedValueXY'); - -const createAnimatedComponent = require('./createAnimatedComponent'); - import type {EndCallback} from './animations/Animation'; -import type {TimingAnimationConfig} from './animations/TimingAnimation'; import type {DecayAnimationConfig} from './animations/DecayAnimation'; import type {SpringAnimationConfig} from './animations/SpringAnimation'; -import type {Numeric as AnimatedNumeric} from './AnimatedImplementation'; +import type {TimingAnimationConfig} from './animations/TimingAnimation'; + +import {AnimatedEvent, attachNativeEvent} from './AnimatedEvent'; +import AnimatedImplementation from './AnimatedImplementation'; +import createAnimatedComponent from './createAnimatedComponent'; import AnimatedColor from './nodes/AnimatedColor'; +import AnimatedInterpolation from './nodes/AnimatedInterpolation'; +import AnimatedNode from './nodes/AnimatedNode'; +import AnimatedValue from './nodes/AnimatedValue'; +import AnimatedValueXY from './nodes/AnimatedValueXY'; /** * Animations are a source of flakiness in snapshot testing. This mock replaces @@ -168,7 +166,7 @@ const loop = function ( export type {AnimatedNumeric as Numeric}; -module.exports = { +export default { Value: AnimatedValue, ValueXY: AnimatedValueXY, Color: AnimatedColor, diff --git a/Libraries/Animated/AnimatedWeb.js b/Libraries/Animated/AnimatedWeb.js index e1c016b75324..e23a04587a70 100644 --- a/Libraries/Animated/AnimatedWeb.js +++ b/Libraries/Animated/AnimatedWeb.js @@ -10,9 +10,9 @@ 'use strict'; -const AnimatedImplementation = require('./AnimatedImplementation'); +import AnimatedImplementation from './AnimatedImplementation'; -module.exports = { +export default { ...AnimatedImplementation, /* $FlowFixMe[incompatible-call] createAnimatedComponent expects to receive * types. Plain intrinsic components can't be typed like this */ diff --git a/Libraries/Animated/Easing.d.ts b/Libraries/Animated/Easing.d.ts new file mode 100644 index 000000000000..bb496f0cd776 --- /dev/null +++ b/Libraries/Animated/Easing.d.ts @@ -0,0 +1,37 @@ +/** + * 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. + * + * @format + */ + +/** + * This class implements common easing functions. The math is pretty obscure, + * but this cool website has nice visual illustrations of what they represent: + * http://xaedes.de/dev/transitions/ + */ +export type EasingFunction = (value: number) => number; +export interface EasingStatic { + step0: EasingFunction; + step1: EasingFunction; + linear: EasingFunction; + ease: EasingFunction; + quad: EasingFunction; + cubic: EasingFunction; + poly(n: number): EasingFunction; + sin: EasingFunction; + circle: EasingFunction; + exp: EasingFunction; + elastic(bounciness: number): EasingFunction; + back(s: number): EasingFunction; + bounce: EasingFunction; + bezier(x1: number, y1: number, x2: number, y2: number): EasingFunction; + in(easing: EasingFunction): EasingFunction; + out(easing: EasingFunction): EasingFunction; + inOut(easing: EasingFunction): EasingFunction; +} + +export type Easing = EasingStatic; +export const Easing: EasingStatic; diff --git a/Libraries/Animated/Easing.js b/Libraries/Animated/Easing.js index f8b5bac2f304..32819708a11d 100644 --- a/Libraries/Animated/Easing.js +++ b/Libraries/Animated/Easing.js @@ -214,7 +214,7 @@ const Easing = { x2: number, y2: number, ): (t: number) => number { - const _bezier = require('./bezier'); + const _bezier = require('./bezier').default; return _bezier(x1, y1, x2, y2); }, @@ -247,4 +247,4 @@ const Easing = { }, }; -module.exports = Easing; +export default Easing; diff --git a/Libraries/Animated/NativeAnimatedHelper.js b/Libraries/Animated/NativeAnimatedHelper.js index 255f6a3ee9ff..e0c91788c4b8 100644 --- a/Libraries/Animated/NativeAnimatedHelper.js +++ b/Libraries/Animated/NativeAnimatedHelper.js @@ -8,22 +8,23 @@ * @format */ -import NativeAnimatedNonTurboModule from './NativeAnimatedModule'; -import NativeAnimatedTurboModule from './NativeAnimatedTurboModule'; -import NativeEventEmitter from '../EventEmitter/NativeEventEmitter'; -import Platform from '../Utilities/Platform'; +import type {EventSubscription} from '../vendor/emitter/EventEmitter'; import type {EventConfig} from './AnimatedEvent'; +import type {AnimationConfig, EndCallback} from './animations/Animation'; import type { - EventMapping, AnimatedNodeConfig, AnimatingNodeConfig, + EventMapping, } from './NativeAnimatedModule'; -import type {AnimationConfig, EndCallback} from './animations/Animation'; import type {InterpolationConfigType} from './nodes/AnimatedInterpolation'; + +import NativeEventEmitter from '../EventEmitter/NativeEventEmitter'; +import RCTDeviceEventEmitter from '../EventEmitter/RCTDeviceEventEmitter'; import ReactNativeFeatureFlags from '../ReactNative/ReactNativeFeatureFlags'; +import Platform from '../Utilities/Platform'; +import NativeAnimatedNonTurboModule from './NativeAnimatedModule'; +import NativeAnimatedTurboModule from './NativeAnimatedTurboModule'; import invariant from 'invariant'; -import RCTDeviceEventEmitter from '../EventEmitter/RCTDeviceEventEmitter'; -import type {EventSubscription} from '../vendor/emitter/EventEmitter'; // TODO T69437152 @petetheheat - Delete this fork when Fabric ships to 100%. const NativeAnimatedModule = @@ -36,7 +37,7 @@ let __nativeAnimationIdCount = 1; /* used for started animations */ let nativeEventEmitter; -let waitingForQueuedOperations = new Set(); +let waitingForQueuedOperations = new Set(); let queueOperations = false; let queue: Array<() => void> = []; // $FlowFixMe @@ -48,8 +49,12 @@ const useSingleOpBatching = ReactNativeFeatureFlags.animatedShouldUseSingleOp(); let flushQueueTimeout = null; -const eventListenerGetValueCallbacks = {}; -const eventListenerAnimationFinishedCallbacks = {}; +const eventListenerGetValueCallbacks: { + [$FlowFixMe | number]: ((value: number) => void) | void, +} = {}; +const eventListenerAnimationFinishedCallbacks: { + [$FlowFixMe | number]: EndCallback | void, +} = {}; let globalEventEmitterGetValueListener: ?EventSubscription = null; let globalEventEmitterAnimationFinishedListener: ?EventSubscription = null; @@ -78,7 +83,7 @@ const nativeOps: ?typeof NativeAnimatedModule = useSingleOpBatching 'addListener', // 20 'removeListener', // 21 ]; - return apis.reduce((acc, functionName, i) => { + return apis.reduce<{[string]: number}>((acc, functionName, i) => { // These indices need to be kept in sync with the indices in native (see NativeAnimatedModule in Java, or the equivalent for any other native platform). // $FlowFixMe[prop-missing] acc[functionName] = i + 1; @@ -420,14 +425,17 @@ const SUPPORTED_INTERPOLATION_PARAMS = { }; function addWhitelistedStyleProp(prop: string): void { + // $FlowFixMe[prop-missing] SUPPORTED_STYLES[prop] = true; } function addWhitelistedTransformProp(prop: string): void { + // $FlowFixMe[prop-missing] SUPPORTED_TRANSFORMS[prop] = true; } function addWhitelistedInterpolationParam(param: string): void { + // $FlowFixMe[prop-missing] SUPPORTED_INTERPOLATION_PARAMS[param] = true; } @@ -519,15 +527,17 @@ function shouldUseNativeDriver( } if (config.useNativeDriver === true && !NativeAnimatedModule) { - if (!_warnedMissingNativeAnimated) { - console.warn( - 'Animated: `useNativeDriver` is not supported because the native ' + - 'animated module is missing. Falling back to JS-based animation. To ' + - 'resolve this, add `RCTAnimation` module to this app, or remove ' + - '`useNativeDriver`. ' + - 'Make sure to run `bundle exec pod install` first. Read more about autolinking: https://github.com/react-native-community/cli/blob/master/docs/autolinking.md', - ); - _warnedMissingNativeAnimated = true; + if (process.env.NODE_ENV !== 'test') { + if (!_warnedMissingNativeAnimated) { + console.warn( + 'Animated: `useNativeDriver` is not supported because the native ' + + 'animated module is missing. Falling back to JS-based animation. To ' + + 'resolve this, add `RCTAnimation` module to this app, or remove ' + + '`useNativeDriver`. ' + + 'Make sure to run `bundle exec pod install` first. Read more about autolinking: https://github.com/react-native-community/cli/blob/master/docs/autolinking.md', + ); + _warnedMissingNativeAnimated = true; + } } return false; } @@ -550,7 +560,7 @@ function transformDataType(value: number | string): number | string { } } -module.exports = { +export default { API, isSupportedColorStyleProp, isSupportedStyleProp, diff --git a/Libraries/Animated/NativeAnimatedModule.js b/Libraries/Animated/NativeAnimatedModule.js index 73f3bb76f90c..9fc932e6b509 100644 --- a/Libraries/Animated/NativeAnimatedModule.js +++ b/Libraries/Animated/NativeAnimatedModule.js @@ -9,6 +9,7 @@ */ import type {TurboModule} from '../TurboModule/RCTExport'; + import * as TurboModuleRegistry from '../TurboModule/TurboModuleRegistry'; type EndResult = {finished: boolean, ...}; diff --git a/Libraries/Animated/NativeAnimatedTurboModule.js b/Libraries/Animated/NativeAnimatedTurboModule.js index 3adac4237dae..58664ca87421 100644 --- a/Libraries/Animated/NativeAnimatedTurboModule.js +++ b/Libraries/Animated/NativeAnimatedTurboModule.js @@ -9,6 +9,7 @@ */ import type {TurboModule} from '../TurboModule/RCTExport'; + import * as TurboModuleRegistry from '../TurboModule/TurboModuleRegistry'; type EndResult = {finished: boolean, ...}; diff --git a/Libraries/Animated/SpringConfig.js b/Libraries/Animated/SpringConfig.js index c687f6b97126..2ed616eb87e6 100644 --- a/Libraries/Animated/SpringConfig.js +++ b/Libraries/Animated/SpringConfig.js @@ -24,7 +24,7 @@ function dampingFromOrigamiValue(oValue: number) { return (oValue - 8) * 3 + 25; } -function fromOrigamiTensionAndFriction( +export function fromOrigamiTensionAndFriction( tension: number, friction: number, ): SpringConfigType { @@ -34,7 +34,7 @@ function fromOrigamiTensionAndFriction( }; } -function fromBouncinessAndSpeed( +export function fromBouncinessAndSpeed( bounciness: number, speed: number, ): SpringConfigType { @@ -96,8 +96,3 @@ function fromBouncinessAndSpeed( damping: dampingFromOrigamiValue(bouncyFriction), }; } - -module.exports = { - fromOrigamiTensionAndFriction, - fromBouncinessAndSpeed, -}; diff --git a/Libraries/Animated/__tests__/Animated-test.js b/Libraries/Animated/__tests__/Animated-test.js index 8f62804c73b7..698dee83e999 100644 --- a/Libraries/Animated/__tests__/Animated-test.js +++ b/Libraries/Animated/__tests__/Animated-test.js @@ -5,12 +5,14 @@ * LICENSE file in the root directory of this source tree. * * @format - * @emails oncall+react_native + * @oncall react_native */ -import AnimatedProps from '../nodes/AnimatedProps'; -import TestRenderer from 'react-test-renderer'; import * as React from 'react'; +import TestRenderer from 'react-test-renderer'; + +let Animated = require('../Animated').default; +let AnimatedProps = require('../nodes/AnimatedProps').default; jest.mock('../../BatchedBridge/NativeModules', () => ({ NativeAnimatedModule: {}, @@ -21,8 +23,6 @@ jest.mock('../../BatchedBridge/NativeModules', () => ({ }, })); -let Animated = require('../Animated'); - describe('Animated tests', () => { beforeEach(() => { jest.resetModules(); @@ -31,6 +31,10 @@ describe('Animated tests', () => { describe('Animated', () => { it('works end to end', () => { const anim = new Animated.Value(0); + const translateAnim = anim.interpolate({ + inputRange: [0, 1], + outputRange: [100, 200], + }); const callback = jest.fn(); @@ -41,10 +45,10 @@ describe('Animated tests', () => { opacity: anim, transform: [ { - translateX: anim.interpolate({ - inputRange: [0, 1], - outputRange: [100, 200], - }), + translate: [translateAnim, translateAnim], + }, + { + translateX: translateAnim, }, {scale: anim}, ], @@ -61,7 +65,7 @@ describe('Animated tests', () => { style: { backgroundColor: 'red', opacity: 0, - transform: [{translateX: 100}, {scale: 0}], + transform: [{translate: [100, 100]}, {translateX: 100}, {scale: 0}], shadowOffset: { width: 0, height: 0, @@ -83,7 +87,7 @@ describe('Animated tests', () => { style: { backgroundColor: 'red', opacity: 0.5, - transform: [{translateX: 150}, {scale: 0.5}], + transform: [{translate: [150, 150]}, {translateX: 150}, {scale: 0.5}], shadowOffset: { width: 0.5, height: 0.5, @@ -692,7 +696,7 @@ describe('Animated tests', () => { beforeEach(() => { jest.mock('../../Interaction/InteractionManager'); - Animated = require('../Animated'); + Animated = require('../Animated').default; InteractionManager = require('../../Interaction/InteractionManager'); }); diff --git a/Libraries/Animated/__tests__/AnimatedMock-test.js b/Libraries/Animated/__tests__/AnimatedMock-test.js index cc510feefefb..d08a5ecc38f7 100644 --- a/Libraries/Animated/__tests__/AnimatedMock-test.js +++ b/Libraries/Animated/__tests__/AnimatedMock-test.js @@ -5,13 +5,13 @@ * LICENSE file in the root directory of this source tree. * * @format - * @emails oncall+react_native + * @oncall react_native */ 'use strict'; -const AnimatedMock = require('../AnimatedMock'); -const AnimatedImplementation = require('../AnimatedImplementation'); +import AnimatedImplementation from '../AnimatedImplementation'; +import AnimatedMock from '../AnimatedMock'; describe('Animated Mock', () => { it('matches implementation keys', () => { diff --git a/Libraries/Animated/__tests__/AnimatedNative-test.js b/Libraries/Animated/__tests__/AnimatedNative-test.js index 677527e22c9d..2f7b10af0526 100644 --- a/Libraries/Animated/__tests__/AnimatedNative-test.js +++ b/Libraries/Animated/__tests__/AnimatedNative-test.js @@ -5,7 +5,7 @@ * LICENSE file in the root directory of this source tree. * * @format - * @emails oncall+react_native + * @oncall react_native */ jest @@ -20,14 +20,14 @@ jest })) .mock('../NativeAnimatedModule') .mock('../../EventEmitter/NativeEventEmitter') - // findNodeHandle is imported from ReactNative so mock that whole module. - .setMock('../../Renderer/shims/ReactNative', {findNodeHandle: () => 1}); + // findNodeHandle is imported from RendererProxy so mock that whole module. + .setMock('../../ReactNative/RendererProxy', {findNodeHandle: () => 1}); -import TestRenderer from 'react-test-renderer'; import * as React from 'react'; +import TestRenderer from 'react-test-renderer'; -const Animated = require('../Animated'); -const NativeAnimatedHelper = require('../NativeAnimatedHelper'); +const Animated = require('../Animated').default; +const NativeAnimatedHelper = require('../NativeAnimatedHelper').default; describe('Native Animated', () => { const NativeAnimatedModule = require('../NativeAnimatedModule').default; diff --git a/Libraries/Animated/__tests__/Easing-test.js b/Libraries/Animated/__tests__/Easing-test.js index 8efeed00037d..1447c0c60edd 100644 --- a/Libraries/Animated/__tests__/Easing-test.js +++ b/Libraries/Animated/__tests__/Easing-test.js @@ -5,12 +5,12 @@ * LICENSE file in the root directory of this source tree. * * @format - * @emails oncall+react_native + * @oncall react_native */ 'use strict'; -const Easing = require('../Easing'); +import Easing from '../Easing'; describe('Easing', () => { it('should work with linear', () => { const easing = Easing.linear; diff --git a/Libraries/Animated/__tests__/Interpolation-test.js b/Libraries/Animated/__tests__/Interpolation-test.js index 64585ac683bd..2893cdd39399 100644 --- a/Libraries/Animated/__tests__/Interpolation-test.js +++ b/Libraries/Animated/__tests__/Interpolation-test.js @@ -5,13 +5,13 @@ * LICENSE file in the root directory of this source tree. * * @format - * @emails oncall+react_native + * @oncall react_native */ 'use strict'; -const AnimatedInterpolation = require('../nodes/AnimatedInterpolation'); -const Easing = require('../Easing'); +import Easing from '../Easing'; +import AnimatedInterpolation from '../nodes/AnimatedInterpolation'; describe('Interpolation', () => { it('should work with defaults', () => { diff --git a/Libraries/Animated/__tests__/TimingAnimation-test.js b/Libraries/Animated/__tests__/TimingAnimation-test.js index e82407876a2d..b05a2ff3c76e 100644 --- a/Libraries/Animated/__tests__/TimingAnimation-test.js +++ b/Libraries/Animated/__tests__/TimingAnimation-test.js @@ -5,12 +5,12 @@ * LICENSE file in the root directory of this source tree. * * @format - * @emails oncall+react_native + * @oncall react_native */ 'use strict'; -const TimingAnimation = require('../animations/TimingAnimation'); +import TimingAnimation from '../animations/TimingAnimation'; describe('Timing Animation', () => { it('should return array of 61 items', () => { diff --git a/Libraries/Animated/__tests__/bezier-test.js b/Libraries/Animated/__tests__/bezier-test.js index 48bfdef0f2d6..da5303f9c5e8 100644 --- a/Libraries/Animated/__tests__/bezier-test.js +++ b/Libraries/Animated/__tests__/bezier-test.js @@ -4,9 +4,9 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @emails oncall+react_native * @flow * @format + * @oncall react_native */ /** @@ -17,7 +17,7 @@ 'use strict'; -const bezier = require('../bezier'); +import bezier from '../bezier'; const identity = function (x: number) { return x; diff --git a/Libraries/Animated/__tests__/createAnimatedComponentInjection-test.js b/Libraries/Animated/__tests__/createAnimatedComponentInjection-test.js index 9d8fa9b1869d..15936f7b675a 100644 --- a/Libraries/Animated/__tests__/createAnimatedComponentInjection-test.js +++ b/Libraries/Animated/__tests__/createAnimatedComponentInjection-test.js @@ -4,16 +4,17 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @emails oncall+react_native * @flow strict-local * @format + * @oncall react_native */ 'use strict'; -const createAnimatedComponent = require('../createAnimatedComponent'); +import * as React from 'react'; + +const createAnimatedComponent = require('../createAnimatedComponent').default; const createAnimatedComponentInjection = require('../createAnimatedComponentInjection'); -const React = require('react'); function injected( Component: React.AbstractComponent, diff --git a/Libraries/Animated/animations/Animation.js b/Libraries/Animated/animations/Animation.js index f22c5e9e4ada..f3d2ac6d3ac2 100644 --- a/Libraries/Animated/animations/Animation.js +++ b/Libraries/Animated/animations/Animation.js @@ -10,10 +10,11 @@ 'use strict'; -const NativeAnimatedHelper = require('../NativeAnimatedHelper'); import type {PlatformConfig} from '../AnimatedPlatformConfig'; import type AnimatedValue from '../nodes/AnimatedValue'; +import NativeAnimatedHelper from '../NativeAnimatedHelper'; + export type EndResult = {finished: boolean, ...}; export type EndCallback = (result: EndResult) => void; @@ -30,7 +31,7 @@ let startNativeAnimationNextId = 1; // Important note: start() and stop() will only be called at most once. // Once an animation has been stopped or finished its course, it will // not be reused. -class Animation { +export default class Animation { __active: boolean; __isInteraction: boolean; __nativeId: number; @@ -85,5 +86,3 @@ class Animation { } } } - -module.exports = Animation; diff --git a/Libraries/Animated/animations/DecayAnimation.js b/Libraries/Animated/animations/DecayAnimation.js index c53ff08ac0fe..f0042c5881de 100644 --- a/Libraries/Animated/animations/DecayAnimation.js +++ b/Libraries/Animated/animations/DecayAnimation.js @@ -10,14 +10,13 @@ 'use strict'; -const Animation = require('./Animation'); - -const {shouldUseNativeDriver} = require('../NativeAnimatedHelper'); - import type {PlatformConfig} from '../AnimatedPlatformConfig'; import type AnimatedValue from '../nodes/AnimatedValue'; import type {AnimationConfig, EndCallback} from './Animation'; +import NativeAnimatedHelper from '../NativeAnimatedHelper'; +import Animation from './Animation'; + export type DecayAnimationConfig = { ...AnimationConfig, velocity: @@ -36,7 +35,7 @@ export type DecayAnimationConfigSingle = { deceleration?: number, }; -class DecayAnimation extends Animation { +export default class DecayAnimation extends Animation { _startTime: number; _lastValue: number; _fromValue: number; @@ -51,7 +50,7 @@ class DecayAnimation extends Animation { super(); this._deceleration = config.deceleration ?? 0.998; this._velocity = config.velocity; - this._useNativeDriver = shouldUseNativeDriver(config); + this._useNativeDriver = NativeAnimatedHelper.shouldUseNativeDriver(config); this._platformConfig = config.platformConfig; this.__isInteraction = config.isInteraction ?? !this._useNativeDriver; this.__iterations = config.iterations ?? 1; @@ -123,5 +122,3 @@ class DecayAnimation extends Animation { this.__debouncedOnEnd({finished: false}); } } - -module.exports = DecayAnimation; diff --git a/Libraries/Animated/animations/SpringAnimation.js b/Libraries/Animated/animations/SpringAnimation.js index e02ed31872da..69101dab030e 100644 --- a/Libraries/Animated/animations/SpringAnimation.js +++ b/Libraries/Animated/animations/SpringAnimation.js @@ -10,20 +10,17 @@ 'use strict'; -const AnimatedValue = require('../nodes/AnimatedValue'); -const AnimatedValueXY = require('../nodes/AnimatedValueXY'); -const AnimatedInterpolation = require('../nodes/AnimatedInterpolation'); -const Animation = require('./Animation'); -const SpringConfig = require('../SpringConfig'); - -const invariant = require('invariant'); - -const {shouldUseNativeDriver} = require('../NativeAnimatedHelper'); - import type {PlatformConfig} from '../AnimatedPlatformConfig'; +import type AnimatedInterpolation from '../nodes/AnimatedInterpolation'; +import type AnimatedValue from '../nodes/AnimatedValue'; +import type AnimatedValueXY from '../nodes/AnimatedValueXY'; import type {AnimationConfig, EndCallback} from './Animation'; +import NativeAnimatedHelper from '../NativeAnimatedHelper'; import AnimatedColor from '../nodes/AnimatedColor'; +import * as SpringConfig from '../SpringConfig'; +import Animation from './Animation'; +import invariant from 'invariant'; export type SpringAnimationConfig = { ...AnimationConfig, @@ -82,7 +79,7 @@ export type SpringAnimationConfigSingle = { delay?: number, }; -class SpringAnimation extends Animation { +export default class SpringAnimation extends Animation { _overshootClamping: boolean; _restDisplacementThreshold: number; _restSpeedThreshold: number; @@ -115,7 +112,7 @@ class SpringAnimation extends Animation { this._lastVelocity = config.velocity ?? 0; this._toValue = config.toValue; this._delay = config.delay ?? 0; - this._useNativeDriver = shouldUseNativeDriver(config); + this._useNativeDriver = NativeAnimatedHelper.shouldUseNativeDriver(config); this._platformConfig = config.platformConfig; this.__isInteraction = config.isInteraction ?? !this._useNativeDriver; this.__iterations = config.iterations ?? 1; @@ -371,5 +368,3 @@ class SpringAnimation extends Animation { this.__debouncedOnEnd({finished: false}); } } - -module.exports = SpringAnimation; diff --git a/Libraries/Animated/animations/TimingAnimation.js b/Libraries/Animated/animations/TimingAnimation.js index 94c4b3a4b836..5c0c3ce38144 100644 --- a/Libraries/Animated/animations/TimingAnimation.js +++ b/Libraries/Animated/animations/TimingAnimation.js @@ -10,18 +10,16 @@ 'use strict'; -const AnimatedValue = require('../nodes/AnimatedValue'); -const AnimatedValueXY = require('../nodes/AnimatedValueXY'); -const AnimatedInterpolation = require('../nodes/AnimatedInterpolation'); -const Animation = require('./Animation'); - -const {shouldUseNativeDriver} = require('../NativeAnimatedHelper'); - import type {PlatformConfig} from '../AnimatedPlatformConfig'; -import type {AnimationConfig, EndCallback} from './Animation'; import type {RgbaValue} from '../nodes/AnimatedColor'; +import type AnimatedInterpolation from '../nodes/AnimatedInterpolation'; +import type AnimatedValue from '../nodes/AnimatedValue'; +import type AnimatedValueXY from '../nodes/AnimatedValueXY'; +import type {AnimationConfig, EndCallback} from './Animation'; +import NativeAnimatedHelper from '../NativeAnimatedHelper'; import AnimatedColor from '../nodes/AnimatedColor'; +import Animation from './Animation'; export type TimingAnimationConfig = $ReadOnly<{ ...AnimationConfig, @@ -53,13 +51,13 @@ export type TimingAnimationConfigSingle = $ReadOnly<{ let _easeInOut; function easeInOut() { if (!_easeInOut) { - const Easing = require('../Easing'); + const Easing = require('../Easing').default; _easeInOut = Easing.inOut(Easing.ease); } return _easeInOut; } -class TimingAnimation extends Animation { +export default class TimingAnimation extends Animation { _startTime: number; _fromValue: number; _toValue: number; @@ -79,7 +77,7 @@ class TimingAnimation extends Animation { this._duration = config.duration ?? 500; this._delay = config.delay ?? 0; this.__iterations = config.iterations ?? 1; - this._useNativeDriver = shouldUseNativeDriver(config); + this._useNativeDriver = NativeAnimatedHelper.shouldUseNativeDriver(config); this._platformConfig = config.platformConfig; this.__isInteraction = config.isInteraction ?? !this._useNativeDriver; } @@ -172,5 +170,3 @@ class TimingAnimation extends Animation { this.__debouncedOnEnd({finished: false}); } } - -module.exports = TimingAnimation; diff --git a/Libraries/Animated/bezier.js b/Libraries/Animated/bezier.js index da60afa0ec32..f18cef48e2da 100644 --- a/Libraries/Animated/bezier.js +++ b/Libraries/Animated/bezier.js @@ -92,7 +92,7 @@ function newtonRaphsonIterate( return aGuessT; } -module.exports = function bezier( +export default function bezier( mX1: number, mY1: number, mX2: number, @@ -161,4 +161,4 @@ module.exports = function bezier( } return calcBezier(getTForX(x), mY1, mY2); }; -}; +} diff --git a/Libraries/Animated/components/AnimatedFlatList.js b/Libraries/Animated/components/AnimatedFlatList.js index ebe160f12e75..afe8bd88061c 100644 --- a/Libraries/Animated/components/AnimatedFlatList.js +++ b/Libraries/Animated/components/AnimatedFlatList.js @@ -8,13 +8,12 @@ * @format */ -import * as React from 'react'; - -const FlatList = require('../../Lists/FlatList'); -const createAnimatedComponent = require('../createAnimatedComponent'); - import type {AnimatedComponentType} from '../createAnimatedComponent'; +import FlatList from '../../Lists/FlatList'; +import createAnimatedComponent from '../createAnimatedComponent'; +import * as React from 'react'; + /** * @see https://github.com/facebook/react-native/commit/b8c8562 */ @@ -22,7 +21,7 @@ const FlatListWithEventThrottle = React.forwardRef((props, ref) => ( )); -module.exports = (createAnimatedComponent( +export default (createAnimatedComponent( FlatListWithEventThrottle, ): AnimatedComponentType< React.ElementConfig, diff --git a/Libraries/Animated/components/AnimatedImage.js b/Libraries/Animated/components/AnimatedImage.js index b0e7b9042020..c178480fe73a 100644 --- a/Libraries/Animated/components/AnimatedImage.js +++ b/Libraries/Animated/components/AnimatedImage.js @@ -8,14 +8,13 @@ * @format */ -import * as React from 'react'; - -const Image = require('../../Image/Image'); -const createAnimatedComponent = require('../createAnimatedComponent'); - import type {AnimatedComponentType} from '../createAnimatedComponent'; -module.exports = (createAnimatedComponent( +import Image from '../../Image/Image'; +import createAnimatedComponent from '../createAnimatedComponent'; +import * as React from 'react'; + +export default (createAnimatedComponent( (Image: $FlowFixMe), ): AnimatedComponentType< React.ElementConfig, diff --git a/Libraries/Animated/components/AnimatedScrollView.js b/Libraries/Animated/components/AnimatedScrollView.js index 887948366ac8..a3ba2d272cae 100644 --- a/Libraries/Animated/components/AnimatedScrollView.js +++ b/Libraries/Animated/components/AnimatedScrollView.js @@ -8,23 +8,119 @@ * @format */ +import type {____ViewStyle_Internal} from '../../StyleSheet/StyleSheetTypes'; +import type {AnimatedComponentType} from '../createAnimatedComponent'; + +import RefreshControl from '../../Components/RefreshControl/RefreshControl'; +import ScrollView from '../../Components/ScrollView/ScrollView'; +import flattenStyle from '../../StyleSheet/flattenStyle'; +import splitLayoutProps from '../../StyleSheet/splitLayoutProps'; +import StyleSheet from '../../StyleSheet/StyleSheet'; +import Platform from '../../Utilities/Platform'; +import useMergeRefs from '../../Utilities/useMergeRefs'; +import createAnimatedComponent from '../createAnimatedComponent'; +import useAnimatedProps from '../useAnimatedProps'; import * as React from 'react'; +import {useMemo} from 'react'; -const ScrollView = require('../../Components/ScrollView/ScrollView'); -const createAnimatedComponent = require('../createAnimatedComponent'); - -import type {AnimatedComponentType} from '../createAnimatedComponent'; +type Props = React.ElementConfig; +type Instance = React.ElementRef; /** * @see https://github.com/facebook/react-native/commit/b8c8562 */ -const ScrollViewWithEventThrottle = React.forwardRef((props, ref) => ( - -)); - -module.exports = (createAnimatedComponent( - ScrollViewWithEventThrottle, -): AnimatedComponentType< - React.ElementConfig, - React.ElementRef, ->); +const AnimatedScrollView: AnimatedComponentType = + React.forwardRef((props, forwardedRef) => { + // (Android only) When a ScrollView has a RefreshControl and + // any `style` property set with an Animated.Value, the CSS + // gets incorrectly applied twice. This is because ScrollView + // swaps the parent/child relationship of itself and the + // RefreshControl component (see ScrollView.js for more details). + if ( + Platform.OS === 'android' && + props.refreshControl != null && + props.style != null + ) { + return ( + + ); + } else { + return ( + + ); + } + }); + +const AnimatedScrollViewWithInvertedRefreshControl = React.forwardRef( + ( + props: { + ...React.ElementConfig, + // $FlowFixMe[unclear-type] Same Flow type as `refreshControl` in ScrollView + refreshControl: React.Element, + }, + forwardedRef, + ) => { + // Split `props` into the animate-able props for the parent (RefreshControl) + // and child (ScrollView). + const {intermediatePropsForRefreshControl, intermediatePropsForScrollView} = + useMemo(() => { + const {outer, inner} = splitLayoutProps(flattenStyle(props.style)); + return { + intermediatePropsForRefreshControl: {style: outer}, + intermediatePropsForScrollView: {...props, style: inner}, + }; + }, [props]); + + // Handle animated props on `refreshControl`. + const [refreshControlAnimatedProps, refreshControlRef] = useAnimatedProps< + {style: ?____ViewStyle_Internal}, + $FlowFixMe, + >(intermediatePropsForRefreshControl); + // NOTE: Assumes that refreshControl.ref` and `refreshControl.style` can be + // safely clobbered. + const refreshControl: React.Element = + React.cloneElement(props.refreshControl, { + ...refreshControlAnimatedProps, + ref: refreshControlRef, + }); + + // Handle animated props on `NativeDirectionalScrollView`. + const [scrollViewAnimatedProps, scrollViewRef] = useAnimatedProps< + Props, + Instance, + >(intermediatePropsForScrollView); + const ref = useMergeRefs(scrollViewRef, forwardedRef); + + return ( + // $FlowFixMe[incompatible-use] Investigate useAnimatedProps return value + + ); + }, +); + +const AnimatedScrollViewWithoutInvertedRefreshControl = + createAnimatedComponent(ScrollView); + +export default AnimatedScrollView; diff --git a/Libraries/Animated/components/AnimatedSectionList.js b/Libraries/Animated/components/AnimatedSectionList.js index 2c467a669982..955691f6e2b2 100644 --- a/Libraries/Animated/components/AnimatedSectionList.js +++ b/Libraries/Animated/components/AnimatedSectionList.js @@ -8,12 +8,11 @@ * @format */ -import * as React from 'react'; +import type {AnimatedComponentType} from '../createAnimatedComponent'; import SectionList from '../../Lists/SectionList'; -const createAnimatedComponent = require('../createAnimatedComponent'); - -import type {AnimatedComponentType} from '../createAnimatedComponent'; +import createAnimatedComponent from '../createAnimatedComponent'; +import * as React from 'react'; /** * @see https://github.com/facebook/react-native/commit/b8c8562 @@ -22,7 +21,7 @@ const SectionListWithEventThrottle = React.forwardRef((props, ref) => ( )); -module.exports = (createAnimatedComponent( +export default (createAnimatedComponent( SectionListWithEventThrottle, ): AnimatedComponentType< React.ElementConfig, diff --git a/Libraries/Animated/components/AnimatedText.js b/Libraries/Animated/components/AnimatedText.js index 0cd6cd5245d3..02679f1a8013 100644 --- a/Libraries/Animated/components/AnimatedText.js +++ b/Libraries/Animated/components/AnimatedText.js @@ -8,14 +8,13 @@ * @format */ -import * as React from 'react'; - -const Text = require('../../Text/Text'); -const createAnimatedComponent = require('../createAnimatedComponent'); - import type {AnimatedComponentType} from '../createAnimatedComponent'; -module.exports = (createAnimatedComponent( +import Text from '../../Text/Text'; +import createAnimatedComponent from '../createAnimatedComponent'; +import * as React from 'react'; + +export default (createAnimatedComponent( (Text: $FlowFixMe), ): AnimatedComponentType< React.ElementConfig, diff --git a/Libraries/Animated/components/AnimatedView.js b/Libraries/Animated/components/AnimatedView.js index 75358b003d5d..5eca94417e67 100644 --- a/Libraries/Animated/components/AnimatedView.js +++ b/Libraries/Animated/components/AnimatedView.js @@ -8,14 +8,13 @@ * @format */ -import * as React from 'react'; - -const View = require('../../Components/View/View'); -const createAnimatedComponent = require('../createAnimatedComponent'); - import type {AnimatedComponentType} from '../createAnimatedComponent'; -module.exports = (createAnimatedComponent(View): AnimatedComponentType< +import View from '../../Components/View/View'; +import createAnimatedComponent from '../createAnimatedComponent'; +import * as React from 'react'; + +export default (createAnimatedComponent(View): AnimatedComponentType< React.ElementConfig, React.ElementRef, >); diff --git a/Libraries/Animated/createAnimatedComponent.js b/Libraries/Animated/createAnimatedComponent.js index c9dbe808ed71..fb64ba05ffd3 100644 --- a/Libraries/Animated/createAnimatedComponent.js +++ b/Libraries/Animated/createAnimatedComponent.js @@ -10,16 +10,14 @@ 'use strict'; +import View from '../Components/View/View'; +import setAndForwardRef from '../Utilities/setAndForwardRef'; +import {AnimatedEvent} from './AnimatedEvent'; import * as createAnimatedComponentInjection from './createAnimatedComponentInjection'; - -const View = require('../Components/View/View'); -const {AnimatedEvent} = require('./AnimatedEvent'); -const AnimatedProps = require('./nodes/AnimatedProps'); -const React = require('react'); -const NativeAnimatedHelper = require('./NativeAnimatedHelper'); - -const invariant = require('invariant'); -const setAndForwardRef = require('../Utilities/setAndForwardRef'); +import NativeAnimatedHelper from './NativeAnimatedHelper'; +import AnimatedProps from './nodes/AnimatedProps'; +import invariant from 'invariant'; +import * as React from 'react'; let animatedComponentNextId = 1; @@ -55,7 +53,6 @@ function createAnimatedComponent( _prevComponent: any; _propsAnimated: AnimatedProps; _eventDetachers: Array = []; - _initialAnimatedProps: Object; // Only to be used in this file, and only in Fabric. _animatedComponentId: string = `${animatedComponentNextId++}:animatedComponent`; @@ -140,7 +137,7 @@ function createAnimatedComponent( // components. If you want to animate a composite component, you need to // re-render it. In this case, we have a fallback that uses forceUpdate. // This fallback is also called in Fabric. - _animatedPropsCallback = () => { + _animatedPropsCallback = (): void => { if (this._component == null) { // AnimatedProps is created in will-mount because it's used in render. // But this callback may be invoked before mount in async mode, @@ -192,7 +189,7 @@ function createAnimatedComponent( } } - _setComponentRef = setAndForwardRef({ + _setComponentRef: (ref: React.ElementRef) => void = setAndForwardRef({ getForwardedRef: () => this.props.forwardedRef, setLocalRef: ref => { this._prevComponent = this._component; @@ -200,18 +197,14 @@ function createAnimatedComponent( }, }); - render() { - const animatedProps = - this._propsAnimated.__getValue(this._initialAnimatedProps) || {}; + render(): React.Node { + const animatedProps = this._propsAnimated.__getValue() || {}; + const {style = {}, ...props} = animatedProps; const {style: passthruStyle = {}, ...passthruProps} = this.props.passthroughAnimatedPropExplicitValues || {}; const mergedStyle = {...style, ...passthruStyle}; - if (!this._initialAnimatedProps) { - this._initialAnimatedProps = animatedProps; - } - // Force `collapsable` to be false so that native view is not flattened. // Flattened views cannot be accurately referenced by a native driver. return ( @@ -277,5 +270,5 @@ function createAnimatedComponent( } // $FlowIgnore[incompatible-cast] - Will be compatible after refactors. -module.exports = (createAnimatedComponentInjection.recordAndRetrieve() ?? +export default (createAnimatedComponentInjection.recordAndRetrieve() ?? createAnimatedComponent: typeof createAnimatedComponent); diff --git a/Libraries/Animated/createAnimatedComponent_EXPERIMENTAL.js b/Libraries/Animated/createAnimatedComponent_EXPERIMENTAL.js index b2cd04ac56e4..eb7c78cecc81 100644 --- a/Libraries/Animated/createAnimatedComponent_EXPERIMENTAL.js +++ b/Libraries/Animated/createAnimatedComponent_EXPERIMENTAL.js @@ -8,9 +8,9 @@ * @format */ -import useAnimatedProps from './useAnimatedProps'; -import useMergeRefs from '../Utilities/useMergeRefs'; import StyleSheet from '../StyleSheet/StyleSheet'; +import useMergeRefs from '../Utilities/useMergeRefs'; +import useAnimatedProps from './useAnimatedProps'; import * as React from 'react'; /** diff --git a/Libraries/Animated/nodes/AnimatedAddition.js b/Libraries/Animated/nodes/AnimatedAddition.js index b311b39a6606..7a48965f5dd5 100644 --- a/Libraries/Animated/nodes/AnimatedAddition.js +++ b/Libraries/Animated/nodes/AnimatedAddition.js @@ -10,15 +10,15 @@ 'use strict'; -const AnimatedInterpolation = require('./AnimatedInterpolation'); -const AnimatedNode = require('./AnimatedNode'); -const AnimatedValue = require('./AnimatedValue'); -const AnimatedWithChildren = require('./AnimatedWithChildren'); - import type {PlatformConfig} from '../AnimatedPlatformConfig'; import type {InterpolationConfigType} from './AnimatedInterpolation'; +import type AnimatedNode from './AnimatedNode'; + +import AnimatedInterpolation from './AnimatedInterpolation'; +import AnimatedValue from './AnimatedValue'; +import AnimatedWithChildren from './AnimatedWithChildren'; -class AnimatedAddition extends AnimatedWithChildren { +export default class AnimatedAddition extends AnimatedWithChildren { _a: AnimatedNode; _b: AnimatedNode; @@ -62,5 +62,3 @@ class AnimatedAddition extends AnimatedWithChildren { }; } } - -module.exports = AnimatedAddition; diff --git a/Libraries/Animated/nodes/AnimatedColor.js b/Libraries/Animated/nodes/AnimatedColor.js index 16a87f528003..d6fba11337f7 100644 --- a/Libraries/Animated/nodes/AnimatedColor.js +++ b/Libraries/Animated/nodes/AnimatedColor.js @@ -10,16 +10,16 @@ 'use strict'; -import AnimatedValue from './AnimatedValue'; -import AnimatedWithChildren from './AnimatedWithChildren'; +import type {NativeColorValue} from '../../StyleSheet/PlatformColorValueTypes'; +import type {ProcessedColorValue} from '../../StyleSheet/processColor'; +import type {ColorValue} from '../../StyleSheet/StyleSheet'; +import type {PlatformConfig} from '../AnimatedPlatformConfig'; + import normalizeColor from '../../StyleSheet/normalizeColor'; import {processColorObject} from '../../StyleSheet/PlatformColorValueTypes'; import NativeAnimatedHelper from '../NativeAnimatedHelper'; - -import type {PlatformConfig} from '../AnimatedPlatformConfig'; -import type {ColorValue} from '../../StyleSheet/StyleSheet'; -import type {NativeColorValue} from '../../StyleSheet/PlatformColorValueTypes'; -import type {ProcessedColorValue} from '../../StyleSheet/processColor'; +import AnimatedValue from './AnimatedValue'; +import AnimatedWithChildren from './AnimatedWithChildren'; export type AnimatedColorConfig = $ReadOnly<{ useNativeDriver: boolean, diff --git a/Libraries/Animated/nodes/AnimatedDiffClamp.js b/Libraries/Animated/nodes/AnimatedDiffClamp.js index 61b167c2fcba..d2ac11ae764a 100644 --- a/Libraries/Animated/nodes/AnimatedDiffClamp.js +++ b/Libraries/Animated/nodes/AnimatedDiffClamp.js @@ -10,14 +10,14 @@ 'use strict'; -const AnimatedInterpolation = require('./AnimatedInterpolation'); -const AnimatedNode = require('./AnimatedNode'); -const AnimatedWithChildren = require('./AnimatedWithChildren'); - -import type {InterpolationConfigType} from './AnimatedInterpolation'; import type {PlatformConfig} from '../AnimatedPlatformConfig'; +import type {InterpolationConfigType} from './AnimatedInterpolation'; +import type AnimatedNode from './AnimatedNode'; -class AnimatedDiffClamp extends AnimatedWithChildren { +import AnimatedInterpolation from './AnimatedInterpolation'; +import AnimatedWithChildren from './AnimatedWithChildren'; + +export default class AnimatedDiffClamp extends AnimatedWithChildren { _a: AnimatedNode; _min: number; _max: number; @@ -70,5 +70,3 @@ class AnimatedDiffClamp extends AnimatedWithChildren { }; } } - -module.exports = AnimatedDiffClamp; diff --git a/Libraries/Animated/nodes/AnimatedDivision.js b/Libraries/Animated/nodes/AnimatedDivision.js index 3d77e2de6453..158a0f3d7805 100644 --- a/Libraries/Animated/nodes/AnimatedDivision.js +++ b/Libraries/Animated/nodes/AnimatedDivision.js @@ -10,15 +10,15 @@ 'use strict'; -const AnimatedInterpolation = require('./AnimatedInterpolation'); -const AnimatedNode = require('./AnimatedNode'); -const AnimatedValue = require('./AnimatedValue'); -const AnimatedWithChildren = require('./AnimatedWithChildren'); - -import type {InterpolationConfigType} from './AnimatedInterpolation'; import type {PlatformConfig} from '../AnimatedPlatformConfig'; +import type {InterpolationConfigType} from './AnimatedInterpolation'; -class AnimatedDivision extends AnimatedWithChildren { +import AnimatedInterpolation from './AnimatedInterpolation'; +import AnimatedNode from './AnimatedNode'; +import AnimatedValue from './AnimatedValue'; +import AnimatedWithChildren from './AnimatedWithChildren'; + +export default class AnimatedDivision extends AnimatedWithChildren { _a: AnimatedNode; _b: AnimatedNode; _warnedAboutDivideByZero: boolean = false; @@ -78,5 +78,3 @@ class AnimatedDivision extends AnimatedWithChildren { }; } } - -module.exports = AnimatedDivision; diff --git a/Libraries/Animated/nodes/AnimatedInterpolation.js b/Libraries/Animated/nodes/AnimatedInterpolation.js index ee5ea2194624..be85be6e89d7 100644 --- a/Libraries/Animated/nodes/AnimatedInterpolation.js +++ b/Libraries/Animated/nodes/AnimatedInterpolation.js @@ -12,14 +12,13 @@ 'use strict'; -const AnimatedNode = require('./AnimatedNode'); -const AnimatedWithChildren = require('./AnimatedWithChildren'); -const NativeAnimatedHelper = require('../NativeAnimatedHelper'); - -const invariant = require('invariant'); -const normalizeColor = require('../../StyleSheet/normalizeColor'); - import type {PlatformConfig} from '../AnimatedPlatformConfig'; +import type AnimatedNode from './AnimatedNode'; + +import normalizeColor from '../../StyleSheet/normalizeColor'; +import NativeAnimatedHelper from '../NativeAnimatedHelper'; +import AnimatedWithChildren from './AnimatedWithChildren'; +import invariant from 'invariant'; type ExtrapolateType = 'extend' | 'identity' | 'clamp'; @@ -299,7 +298,7 @@ function checkInfiniteRange(name: string, arr: $ReadOnlyArray) { ); } -class AnimatedInterpolation< +export default class AnimatedInterpolation< OutputT: number | string, > extends AnimatedWithChildren { // Export for testing. @@ -368,5 +367,3 @@ class AnimatedInterpolation< }; } } - -module.exports = AnimatedInterpolation; diff --git a/Libraries/Animated/nodes/AnimatedModulo.js b/Libraries/Animated/nodes/AnimatedModulo.js index bc3bc1b3ad27..d5334478afd4 100644 --- a/Libraries/Animated/nodes/AnimatedModulo.js +++ b/Libraries/Animated/nodes/AnimatedModulo.js @@ -10,14 +10,14 @@ 'use strict'; -const AnimatedInterpolation = require('./AnimatedInterpolation'); -const AnimatedNode = require('./AnimatedNode'); -const AnimatedWithChildren = require('./AnimatedWithChildren'); - -import type {InterpolationConfigType} from './AnimatedInterpolation'; import type {PlatformConfig} from '../AnimatedPlatformConfig'; +import type {InterpolationConfigType} from './AnimatedInterpolation'; +import type AnimatedNode from './AnimatedNode'; -class AnimatedModulo extends AnimatedWithChildren { +import AnimatedInterpolation from './AnimatedInterpolation'; +import AnimatedWithChildren from './AnimatedWithChildren'; + +export default class AnimatedModulo extends AnimatedWithChildren { _a: AnimatedNode; _modulus: number; @@ -61,5 +61,3 @@ class AnimatedModulo extends AnimatedWithChildren { }; } } - -module.exports = AnimatedModulo; diff --git a/Libraries/Animated/nodes/AnimatedMultiplication.js b/Libraries/Animated/nodes/AnimatedMultiplication.js index 419927b71f53..b3cfaf3aed3f 100644 --- a/Libraries/Animated/nodes/AnimatedMultiplication.js +++ b/Libraries/Animated/nodes/AnimatedMultiplication.js @@ -10,15 +10,15 @@ 'use strict'; -const AnimatedInterpolation = require('./AnimatedInterpolation'); -const AnimatedNode = require('./AnimatedNode'); -const AnimatedValue = require('./AnimatedValue'); -const AnimatedWithChildren = require('./AnimatedWithChildren'); - -import type {InterpolationConfigType} from './AnimatedInterpolation'; import type {PlatformConfig} from '../AnimatedPlatformConfig'; +import type {InterpolationConfigType} from './AnimatedInterpolation'; +import type AnimatedNode from './AnimatedNode'; -class AnimatedMultiplication extends AnimatedWithChildren { +import AnimatedInterpolation from './AnimatedInterpolation'; +import AnimatedValue from './AnimatedValue'; +import AnimatedWithChildren from './AnimatedWithChildren'; + +export default class AnimatedMultiplication extends AnimatedWithChildren { _a: AnimatedNode; _b: AnimatedNode; @@ -61,5 +61,3 @@ class AnimatedMultiplication extends AnimatedWithChildren { }; } } - -module.exports = AnimatedMultiplication; diff --git a/Libraries/Animated/nodes/AnimatedNode.js b/Libraries/Animated/nodes/AnimatedNode.js index b4a649ff302a..890e9e865bc1 100644 --- a/Libraries/Animated/nodes/AnimatedNode.js +++ b/Libraries/Animated/nodes/AnimatedNode.js @@ -10,12 +10,13 @@ 'use strict'; -const NativeAnimatedHelper = require('../NativeAnimatedHelper'); +import type {PlatformConfig} from '../AnimatedPlatformConfig'; -const NativeAnimatedAPI = NativeAnimatedHelper.API; -const invariant = require('invariant'); +import ReactNativeFeatureFlags from '../../ReactNative/ReactNativeFeatureFlags'; +import NativeAnimatedHelper from '../NativeAnimatedHelper'; +import invariant from 'invariant'; -import type {PlatformConfig} from '../AnimatedPlatformConfig'; +const NativeAnimatedAPI = NativeAnimatedHelper.API; type ValueListenerCallback = (state: {value: number, ...}) => mixed; @@ -23,12 +24,15 @@ let _uniqueId = 1; // Note(vjeux): this would be better as an interface but flow doesn't // support them yet -class AnimatedNode { +export default class AnimatedNode { _listeners: {[key: string]: ValueListenerCallback, ...}; _platformConfig: ?PlatformConfig; __nativeAnimatedValueListener: ?any; __attach(): void {} __detach(): void { + if (ReactNativeFeatureFlags.removeListenersOnDetach()) { + this.removeAllListeners(); + } if (this.__isNative && this.__nativeTag != null) { NativeAnimatedHelper.API.dropAnimatedNode(this.__nativeTag); this.__nativeTag = undefined; @@ -53,7 +57,7 @@ class AnimatedNode { this._listeners = {}; } - __makeNative(platformConfig: ?PlatformConfig) { + __makeNative(platformConfig: ?PlatformConfig): void { if (!this.__isNative) { throw new Error('This node cannot be made a "native" animated node'); } @@ -193,5 +197,3 @@ class AnimatedNode { this._platformConfig = platformConfig; } } - -module.exports = AnimatedNode; diff --git a/Libraries/Animated/nodes/AnimatedProps.js b/Libraries/Animated/nodes/AnimatedProps.js index d4ed70da8d6e..ef15cdf13120 100644 --- a/Libraries/Animated/nodes/AnimatedProps.js +++ b/Libraries/Animated/nodes/AnimatedProps.js @@ -12,14 +12,14 @@ import type {PlatformConfig} from '../AnimatedPlatformConfig'; -const ReactNative = require('../../Renderer/shims/ReactNative'); -const {AnimatedEvent} = require('../AnimatedEvent'); -const NativeAnimatedHelper = require('../NativeAnimatedHelper'); -const AnimatedNode = require('./AnimatedNode'); -const AnimatedStyle = require('./AnimatedStyle'); -const invariant = require('invariant'); - -class AnimatedProps extends AnimatedNode { +import {findNodeHandle} from '../../ReactNative/RendererProxy'; +import {AnimatedEvent} from '../AnimatedEvent'; +import NativeAnimatedHelper from '../NativeAnimatedHelper'; +import AnimatedNode from './AnimatedNode'; +import AnimatedStyle from './AnimatedStyle'; +import invariant from 'invariant'; + +export default class AnimatedProps extends AnimatedNode { _props: Object; _animatedView: any; _callback: () => void; @@ -36,24 +36,12 @@ class AnimatedProps extends AnimatedNode { this._callback = callback; } - __getValue(initialProps: ?Object): Object { + __getValue(): Object { const props: {[string]: any | ((...args: any) => void)} = {}; for (const key in this._props) { const value = this._props[key]; if (value instanceof AnimatedNode) { - // During initial render we want to use the initial value of both natively and non-natively - // driven nodes. On subsequent renders, we cannot use the value of natively driven nodes - // as they may not be up to date, so we use the initial value to ensure that values of - // native animated nodes do not impact rerenders. - if (value instanceof AnimatedStyle) { - props[key] = value.__getValue( - initialProps ? initialProps.style : null, - ); - } else if (!initialProps || !value.__isNative) { - props[key] = value.__getValue(); - } else if (initialProps.hasOwnProperty(key)) { - props[key] = initialProps[key]; - } + props[key] = value.__getValue(); } else if (value instanceof AnimatedEvent) { props[key] = value.__getHandler(); } else { @@ -134,9 +122,7 @@ class AnimatedProps extends AnimatedNode { __connectAnimatedView(): void { invariant(this.__isNative, 'Expected node to be marked as "native"'); - const nativeViewTag: ?number = ReactNative.findNodeHandle( - this._animatedView, - ); + const nativeViewTag: ?number = findNodeHandle(this._animatedView); invariant( nativeViewTag != null, 'Unable to locate attached view in the native tree', @@ -149,9 +135,7 @@ class AnimatedProps extends AnimatedNode { __disconnectAnimatedView(): void { invariant(this.__isNative, 'Expected node to be marked as "native"'); - const nativeViewTag: ?number = ReactNative.findNodeHandle( - this._animatedView, - ); + const nativeViewTag: ?number = findNodeHandle(this._animatedView); invariant( nativeViewTag != null, 'Unable to locate attached view in the native tree', @@ -187,5 +171,3 @@ class AnimatedProps extends AnimatedNode { }; } } - -module.exports = AnimatedProps; diff --git a/Libraries/Animated/nodes/AnimatedStyle.js b/Libraries/Animated/nodes/AnimatedStyle.js index c64706a81585..7cb11bb1b556 100644 --- a/Libraries/Animated/nodes/AnimatedStyle.js +++ b/Libraries/Animated/nodes/AnimatedStyle.js @@ -12,13 +12,13 @@ import type {PlatformConfig} from '../AnimatedPlatformConfig'; -const flattenStyle = require('../../StyleSheet/flattenStyle'); -const NativeAnimatedHelper = require('../NativeAnimatedHelper'); -const AnimatedNode = require('./AnimatedNode'); -const AnimatedTransform = require('./AnimatedTransform'); -const AnimatedWithChildren = require('./AnimatedWithChildren'); +import flattenStyle from '../../StyleSheet/flattenStyle'; +import NativeAnimatedHelper from '../NativeAnimatedHelper'; +import AnimatedNode from './AnimatedNode'; +import AnimatedTransform from './AnimatedTransform'; +import AnimatedWithChildren from './AnimatedWithChildren'; -class AnimatedStyle extends AnimatedWithChildren { +export default class AnimatedStyle extends AnimatedWithChildren { _style: Object; constructor(style: any) { @@ -34,23 +34,15 @@ class AnimatedStyle extends AnimatedWithChildren { } // Recursively get values for nested styles (like iOS's shadowOffset) - _walkStyleAndGetValues(style: any, initialStyle: ?Object) { + _walkStyleAndGetValues(style: any): {[string]: any | {...}} { const updatedStyle: {[string]: any | {...}} = {}; for (const key in style) { const value = style[key]; if (value instanceof AnimatedNode) { - // During initial render we want to use the initial value of both natively and non-natively - // driven nodes. On subsequent renders, we cannot use the value of natively driven nodes - // as they may not be up to date, so we use the initial value to ensure that values of - // native animated nodes do not impact rerenders. - if (!initialStyle || !value.__isNative) { - updatedStyle[key] = value.__getValue(); - } else if (initialStyle.hasOwnProperty(key)) { - updatedStyle[key] = initialStyle[key]; - } + updatedStyle[key] = value.__getValue(); } else if (value && !Array.isArray(value) && typeof value === 'object') { // Support animating nested values (for example: shadowOffset.height) - updatedStyle[key] = this._walkStyleAndGetValues(value, initialStyle); + updatedStyle[key] = this._walkStyleAndGetValues(value); } else { updatedStyle[key] = value; } @@ -58,12 +50,12 @@ class AnimatedStyle extends AnimatedWithChildren { return updatedStyle; } - __getValue(initialStyle: ?Object): Object { - return this._walkStyleAndGetValues(this._style, initialStyle); + __getValue(): Object { + return this._walkStyleAndGetValues(this._style); } // Recursively get animated values for nested styles (like iOS's shadowOffset) - _walkStyleAndGetAnimatedValues(style: any) { + _walkStyleAndGetAnimatedValues(style: any): {[string]: any | {...}} { const updatedStyle: {[string]: any | {...}} = {}; for (const key in style) { const value = style[key]; @@ -128,5 +120,3 @@ class AnimatedStyle extends AnimatedWithChildren { }; } } - -module.exports = AnimatedStyle; diff --git a/Libraries/Animated/nodes/AnimatedSubtraction.js b/Libraries/Animated/nodes/AnimatedSubtraction.js index b3a9198c662b..d9a423b4cbbc 100644 --- a/Libraries/Animated/nodes/AnimatedSubtraction.js +++ b/Libraries/Animated/nodes/AnimatedSubtraction.js @@ -10,15 +10,15 @@ 'use strict'; -const AnimatedInterpolation = require('./AnimatedInterpolation'); -const AnimatedNode = require('./AnimatedNode'); -const AnimatedValue = require('./AnimatedValue'); -const AnimatedWithChildren = require('./AnimatedWithChildren'); - -import type {InterpolationConfigType} from './AnimatedInterpolation'; import type {PlatformConfig} from '../AnimatedPlatformConfig'; +import type {InterpolationConfigType} from './AnimatedInterpolation'; +import type AnimatedNode from './AnimatedNode'; -class AnimatedSubtraction extends AnimatedWithChildren { +import AnimatedInterpolation from './AnimatedInterpolation'; +import AnimatedValue from './AnimatedValue'; +import AnimatedWithChildren from './AnimatedWithChildren'; + +export default class AnimatedSubtraction extends AnimatedWithChildren { _a: AnimatedNode; _b: AnimatedNode; @@ -62,5 +62,3 @@ class AnimatedSubtraction extends AnimatedWithChildren { }; } } - -module.exports = AnimatedSubtraction; diff --git a/Libraries/Animated/nodes/AnimatedTracking.js b/Libraries/Animated/nodes/AnimatedTracking.js index e8f74eaa54ac..bfaeafed47c4 100644 --- a/Libraries/Animated/nodes/AnimatedTracking.js +++ b/Libraries/Animated/nodes/AnimatedTracking.js @@ -10,17 +10,14 @@ 'use strict'; -const AnimatedValue = require('./AnimatedValue'); -const AnimatedNode = require('./AnimatedNode'); -const { - generateNewAnimationId, - shouldUseNativeDriver, -} = require('../NativeAnimatedHelper'); - import type {PlatformConfig} from '../AnimatedPlatformConfig'; import type {EndCallback} from '../animations/Animation'; +import type AnimatedValue from './AnimatedValue'; + +import NativeAnimatedHelper from '../NativeAnimatedHelper'; +import AnimatedNode from './AnimatedNode'; -class AnimatedTracking extends AnimatedNode { +export default class AnimatedTracking extends AnimatedNode { _value: AnimatedValue; _parent: AnimatedNode; _callback: ?EndCallback; @@ -40,7 +37,8 @@ class AnimatedTracking extends AnimatedNode { this._parent = parent; this._animationClass = animationClass; this._animationConfig = animationConfig; - this._useNativeDriver = shouldUseNativeDriver(animationConfig); + this._useNativeDriver = + NativeAnimatedHelper.shouldUseNativeDriver(animationConfig); this._callback = callback; this.__attach(); } @@ -93,12 +91,10 @@ class AnimatedTracking extends AnimatedNode { const animationConfig = animation.__getNativeAnimationConfig(); return { type: 'tracking', - animationId: generateNewAnimationId(), + animationId: NativeAnimatedHelper.generateNewAnimationId(), animationConfig, toValue: this._parent.__getNativeTag(), value: this._value.__getNativeTag(), }; } } - -module.exports = AnimatedTracking; diff --git a/Libraries/Animated/nodes/AnimatedTransform.js b/Libraries/Animated/nodes/AnimatedTransform.js index c3d4175fc6f4..3b553c9a3d36 100644 --- a/Libraries/Animated/nodes/AnimatedTransform.js +++ b/Libraries/Animated/nodes/AnimatedTransform.js @@ -12,11 +12,11 @@ import type {PlatformConfig} from '../AnimatedPlatformConfig'; -const NativeAnimatedHelper = require('../NativeAnimatedHelper'); -const AnimatedNode = require('./AnimatedNode'); -const AnimatedWithChildren = require('./AnimatedWithChildren'); +import NativeAnimatedHelper from '../NativeAnimatedHelper'; +import AnimatedNode from './AnimatedNode'; +import AnimatedWithChildren from './AnimatedWithChildren'; -class AnimatedTransform extends AnimatedWithChildren { +export default class AnimatedTransform extends AnimatedWithChildren { _transforms: $ReadOnlyArray; constructor(transforms: $ReadOnlyArray) { @@ -37,34 +37,11 @@ class AnimatedTransform extends AnimatedWithChildren { } __getValue(): $ReadOnlyArray { - return this._transforms.map(transform => { - const result: {[string]: any} = {}; - for (const key in transform) { - const value = transform[key]; - if (value instanceof AnimatedNode) { - result[key] = value.__getValue(); - } else { - result[key] = value; - } - } - return result; - }); + return this._get(animatedNode => animatedNode.__getValue()); } __getAnimatedValue(): $ReadOnlyArray { - return this._transforms.map(transform => { - const result: {[string]: any} = {}; - for (const key in transform) { - const value = transform[key]; - if (value instanceof AnimatedNode) { - result[key] = value.__getAnimatedValue(); - } else { - // All transform components needed to recompose matrix - result[key] = value; - } - } - return result; - }); + return this._get(animatedNode => animatedNode.__getAnimatedValue()); } __attach(): void { @@ -118,6 +95,36 @@ class AnimatedTransform extends AnimatedWithChildren { transforms: transConfigs, }; } -} -module.exports = AnimatedTransform; + _get(getter: AnimatedNode => any): $ReadOnlyArray { + return this._transforms.map(transform => { + const result: {[string]: any} = {}; + for (const key in transform) { + const value = transform[key]; + if (value instanceof AnimatedNode) { + result[key] = getter(value); + } else if (Array.isArray(value)) { + result[key] = value.map(element => { + if (element instanceof AnimatedNode) { + return getter(element); + } else { + return element; + } + }); + } else if (typeof value === 'object') { + result[key] = {}; + for (const [nestedKey, nestedValue] of Object.entries(value)) { + if (nestedValue instanceof AnimatedNode) { + result[key][nestedKey] = getter(nestedValue); + } else { + result[key][nestedKey] = nestedValue; + } + } + } else { + result[key] = value; + } + } + return result; + }); + } +} diff --git a/Libraries/Animated/nodes/AnimatedValue.js b/Libraries/Animated/nodes/AnimatedValue.js index 9530e090f923..f20307b310e2 100644 --- a/Libraries/Animated/nodes/AnimatedValue.js +++ b/Libraries/Animated/nodes/AnimatedValue.js @@ -10,16 +10,16 @@ 'use strict'; -const AnimatedInterpolation = require('./AnimatedInterpolation'); -const AnimatedWithChildren = require('./AnimatedWithChildren'); -const InteractionManager = require('../../Interaction/InteractionManager'); -const NativeAnimatedHelper = require('../NativeAnimatedHelper'); - -import type AnimatedNode from './AnimatedNode'; import type Animation, {EndCallback} from '../animations/Animation'; import type {InterpolationConfigType} from './AnimatedInterpolation'; +import type AnimatedNode from './AnimatedNode'; import type AnimatedTracking from './AnimatedTracking'; +import InteractionManager from '../../Interaction/InteractionManager'; +import NativeAnimatedHelper from '../NativeAnimatedHelper'; +import AnimatedInterpolation from './AnimatedInterpolation'; +import AnimatedWithChildren from './AnimatedWithChildren'; + export type AnimatedValueConfig = $ReadOnly<{ useNativeDriver: boolean, }>; @@ -49,7 +49,7 @@ const NativeAnimatedAPI = NativeAnimatedHelper.API; * transform which can receive values from multiple parents. */ function _flush(rootNode: AnimatedValue): void { - const animatedStyles = new Set(); + const animatedStyles = new Set(); function findAnimatedStyles(node: AnimatedValue | AnimatedNode) { /* $FlowFixMe[prop-missing] (>=0.68.0 site=react_native_fb) This comment * suppresses an error found when Flow v0.68 was deployed. To see the error @@ -84,13 +84,14 @@ function _executeAsAnimatedBatch(id: string, operation: () => void) { * * See https://reactnative.dev/docs/animatedvalue */ -class AnimatedValue extends AnimatedWithChildren { +export default class AnimatedValue extends AnimatedWithChildren { _value: number; _startingValue: number; _offset: number; _animation: ?Animation; _tracking: ?AnimatedTracking; + // $FlowFixMe[missing-local-annot] constructor(value: number, config?: ?AnimatedValueConfig) { super(); if (typeof value !== 'number') { @@ -303,5 +304,3 @@ class AnimatedValue extends AnimatedWithChildren { }; } } - -module.exports = AnimatedValue; diff --git a/Libraries/Animated/nodes/AnimatedValueXY.js b/Libraries/Animated/nodes/AnimatedValueXY.js index c54a9d9d6f55..dc24a674bba4 100644 --- a/Libraries/Animated/nodes/AnimatedValueXY.js +++ b/Libraries/Animated/nodes/AnimatedValueXY.js @@ -12,10 +12,9 @@ import type {PlatformConfig} from '../AnimatedPlatformConfig'; -const AnimatedValue = require('./AnimatedValue'); -const AnimatedWithChildren = require('./AnimatedWithChildren'); - -const invariant = require('invariant'); +import AnimatedValue from './AnimatedValue'; +import AnimatedWithChildren from './AnimatedWithChildren'; +import invariant from 'invariant'; export type AnimatedValueXYConfig = $ReadOnly<{ useNativeDriver: boolean, @@ -34,7 +33,7 @@ let _uniqueId = 1; * * See https://reactnative.dev/docs/animatedvaluexy */ -class AnimatedValueXY extends AnimatedWithChildren { +export default class AnimatedValueXY extends AnimatedWithChildren { x: AnimatedValue; y: AnimatedValue; _listeners: { @@ -249,5 +248,3 @@ class AnimatedValueXY extends AnimatedWithChildren { super.__makeNative(platformConfig); } } - -module.exports = AnimatedValueXY; diff --git a/Libraries/Animated/nodes/AnimatedWithChildren.js b/Libraries/Animated/nodes/AnimatedWithChildren.js index 435365d1f6ee..ac4ade64612f 100644 --- a/Libraries/Animated/nodes/AnimatedWithChildren.js +++ b/Libraries/Animated/nodes/AnimatedWithChildren.js @@ -11,10 +11,11 @@ 'use strict'; import type {PlatformConfig} from '../AnimatedPlatformConfig'; -const AnimatedNode = require('./AnimatedNode'); -const NativeAnimatedHelper = require('../NativeAnimatedHelper'); -class AnimatedWithChildren extends AnimatedNode { +import NativeAnimatedHelper from '../NativeAnimatedHelper'; +import AnimatedNode from './AnimatedNode'; + +export default class AnimatedWithChildren extends AnimatedNode { _children: Array; constructor() { @@ -85,5 +86,3 @@ class AnimatedWithChildren extends AnimatedNode { } } } - -module.exports = AnimatedWithChildren; diff --git a/Libraries/Animated/useAnimatedProps.js b/Libraries/Animated/useAnimatedProps.js index 9ab24a99ce17..1b7d81009fd8 100644 --- a/Libraries/Animated/useAnimatedProps.js +++ b/Libraries/Animated/useAnimatedProps.js @@ -10,10 +10,10 @@ 'use strict'; -import AnimatedProps from './nodes/AnimatedProps'; -import {AnimatedEvent} from './AnimatedEvent'; import useRefEffect from '../Utilities/useRefEffect'; +import {AnimatedEvent} from './AnimatedEvent'; import NativeAnimatedHelper from './NativeAnimatedHelper'; +import AnimatedProps from './nodes/AnimatedProps'; import { useCallback, useEffect, @@ -21,7 +21,6 @@ import { useMemo, useReducer, useRef, - useState, } from 'react'; type ReducedProps = { @@ -31,12 +30,10 @@ type ReducedProps = { }; type CallbackRef = T => mixed; -let animatedComponentNextId = 1; - export default function useAnimatedProps( props: TProps, ): [ReducedProps, CallbackRef] { - const [, scheduleUpdate] = useReducer(count => count + 1, 0); + const [, scheduleUpdate] = useReducer(count => count + 1, 0); const onUpdateRef = useRef void>(null); // TODO: Only invalidate `node` if animated props or `style` change. In the @@ -63,7 +60,7 @@ export default function useAnimatedProps( // But there is no way to transparently compose three separate callback refs, // so we just combine them all into one for now. const refEffect = useCallback( - instance => { + (instance: TInstance) => { // NOTE: This may be called more often than necessary (e.g. when `props` // changes), but `setNativeView` already optimizes for that. node.setNativeView(instance); @@ -141,16 +138,11 @@ function useAnimatedPropsLifecycle(node: AnimatedProps): void { const prevNodeRef = useRef(null); const isUnmountingRef = useRef(false); - const [animatedComponentId] = useState( - () => `${animatedComponentNextId++}:animatedComponent`, - ); - - useLayoutEffect(() => { - NativeAnimatedHelper.API.setWaitingForIdentifier(animatedComponentId); - }); - useEffect(() => { - NativeAnimatedHelper.API.unsetWaitingForIdentifier(animatedComponentId); + // It is ok for multiple components to call `flushQueue` because it noops + // if the queue is empty. When multiple animated components are mounted at + // the same time. Only first component flushes the queue and the others will noop. + NativeAnimatedHelper.API.flushQueue(); }); useLayoutEffect(() => { diff --git a/Libraries/Animated/useAnimatedValue.d.ts b/Libraries/Animated/useAnimatedValue.d.ts new file mode 100644 index 000000000000..69e5a59fcf09 --- /dev/null +++ b/Libraries/Animated/useAnimatedValue.d.ts @@ -0,0 +1,15 @@ +/** + * 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. + * + * @format + */ + +import type {Animated} from './Animated'; + +export function useAnimatedValue( + initialValue: number, + config?: Animated.AnimatedConfig, +): Animated.Value; diff --git a/Libraries/Animated/useAnimatedValue.js b/Libraries/Animated/useAnimatedValue.js new file mode 100644 index 000000000000..0a430ed5c3c0 --- /dev/null +++ b/Libraries/Animated/useAnimatedValue.js @@ -0,0 +1,25 @@ +/** + * 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 + */ + +import type {AnimatedValueConfig} from './nodes/AnimatedValue'; + +import Animated from './Animated'; +import {useRef} from 'react'; + +export default function useAnimatedValue( + initialValue: number, + config?: ?AnimatedValueConfig, +): Animated.Value { + const ref = useRef(null); + if (ref.current == null) { + ref.current = new Animated.Value(initialValue, config); + } + return ref.current; +} diff --git a/Libraries/AppDelegate/RCTAppDelegate.h b/Libraries/AppDelegate/RCTAppDelegate.h new file mode 100644 index 000000000000..b8d00bab93e7 --- /dev/null +++ b/Libraries/AppDelegate/RCTAppDelegate.h @@ -0,0 +1,130 @@ +/* + * 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 +#import +#import + +#if RCT_NEW_ARCH_ENABLED +// When the new architecture is enabled, the RCTAppDelegate imports some additional headers +#import +#import +#import + +#endif + +/** + * The RCTAppDelegate is an utility class that implements some base configurations for all the React Native apps. + * It is not mandatory to use it, but it could simplify your AppDelegate code. + * + * To use it, you just need to make your AppDelegate a subclass of RCTAppDelegate: + * + * ```objc + * #import + * @interface AppDelegate: RCTAppDelegate + * @end + * ``` + * + * All the methods implemented by the RCTAppDelegate can be overriden by your AppDelegate if you need to provide a + custom implementation. + * If you need to customize the default implementation, you can invoke `[super ]` and use the returned + object. + * + * Overridable methods + * Shared: + * - (RCTBridge *)createBridgeWithDelegate:(id)delegate launchOptions:(NSDictionary + *)launchOptions; + * - (UIView *)createRootViewWithBridge:(RCTBridge *)bridge moduleName:(NSString*)moduleName initProps:(NSDictionary + *)initProps; + * - (UIViewController *)createRootViewController; + * New Architecture: + * - (BOOL)concurrentRootEnabled + * - (BOOL)turboModuleEnabled; + * - (BOOL)fabricEnabled; + * - (NSDictionary *)prepareInitialProps + * - (Class)getModuleClassFromName:(const char *)name + * - (std::shared_ptr)getTurboModule:(const std::string &)name + jsInvoker:(std::shared_ptr)jsInvoker + * - (std::shared_ptr)getTurboModule:(const std::string &)name + initParams: + (const facebook::react::ObjCTurboModule::InitParams &)params + * - (id)getModuleInstanceFromClass:(Class)moduleClass + */ +@interface RCTAppDelegate : UIResponder + +/// The window object, used to render the UViewControllers +@property (nonatomic, strong) UIWindow *window; +@property (nonatomic, strong) RCTBridge *bridge; +@property (nonatomic, strong) NSString *moduleName; + +/** + * It creates a `RCTBridge` using a delegate and some launch options. + * By default, it is invoked passing `self` as a delegate. + * You can override this function to customize the logic that creates the RCTBridge + * + * @parameter: delegate - an object that implements the `RCTBridgeDelegate` protocol. + * @parameter: launchOptions - a dictionary with a set of options. + * + * @returns: a newly created instance of RCTBridge. + */ +- (RCTBridge *)createBridgeWithDelegate:(id)delegate launchOptions:(NSDictionary *)launchOptions; + +/** + * It creates a `UIView` starting from a bridge, a module name and a set of initial properties. + * By default, it is invoked using the bridge created by `createBridgeWithDelegate:launchOptions` and + * the name in the `self.moduleName` variable. + * You can override this function to customize the logic that creates the Root View. + * + * @parameter: bridge - an instance of the `RCTBridge` object. + * @parameter: moduleName - the name of the app, used by Metro to resolve the module. + * @parameter: initProps - a set of initial properties. + * + * @returns: a UIView properly configured with a bridge for React Native. + */ +- (UIView *)createRootViewWithBridge:(RCTBridge *)bridge + moduleName:(NSString *)moduleName + initProps:(NSDictionary *)initProps; + +/** + * It creates the RootViewController. + * By default, it creates a new instance of a `UIViewController`. + * You can override it to provide your own initial ViewController. + * + * @return: an instance of `UIViewController`. + */ +- (UIViewController *)createRootViewController; + +@end + +#if RCT_NEW_ARCH_ENABLED +/// Extension that makes the RCTAppDelegate conform to New Architecture delegates +@interface RCTAppDelegate () + +/// The TurboModule manager +@property (nonatomic, strong) RCTTurboModuleManager *turboModuleManager; +@property (nonatomic, strong) RCTSurfacePresenterBridgeAdapter *bridgeAdapter; + +/// This method controls whether the `concurrentRoot` feature of React18 is turned on or off. +/// +/// @see: https://reactjs.org/blog/2022/03/29/react-v18.html +/// @note: This requires to be rendering on Fabric (i.e. on the New Architecture). +/// @return: `true` if the `concurrentRoot` feature is enabled. Otherwise, it returns `false`. +- (BOOL)concurrentRootEnabled; + +/// This method controls whether the `turboModules` feature of the New Architecture is turned on or off. +/// +/// @note: This is required to be rendering on Fabric (i.e. on the New Architecture). +/// @return: `true` if the Turbo Native Module are enabled. Otherwise, it returns `false`. +- (BOOL)turboModuleEnabled; + +/// This method controls whether the App will use the Fabric renderer of the New Architecture or not. +/// +/// @return: `true` if the Fabric Renderer is enabled. Otherwise, it returns `false`. +- (BOOL)fabricEnabled; + +@end +#endif diff --git a/Libraries/AppDelegate/RCTAppDelegate.mm b/Libraries/AppDelegate/RCTAppDelegate.mm new file mode 100644 index 000000000000..39b29792245f --- /dev/null +++ b/Libraries/AppDelegate/RCTAppDelegate.mm @@ -0,0 +1,164 @@ +/* + * 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 "RCTAppDelegate.h" +#import +#import + +#if RCT_NEW_ARCH_ENABLED +#import +#import +#import +#import + +static NSString *const kRNConcurrentRoot = @"concurrentRoot"; + +@interface RCTAppDelegate () { + std::shared_ptr _reactNativeConfig; + facebook::react::ContextContainer::Shared _contextContainer; +} +@end + +#endif + +@implementation RCTAppDelegate + +- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions +{ + BOOL enableTM = NO; +#if RCT_NEW_ARCH_ENABLED + enableTM = self.turboModuleEnabled; +#endif + + RCTAppSetupPrepareApp(application, enableTM); + + if (!self.bridge) { + self.bridge = [self createBridgeWithDelegate:self launchOptions:launchOptions]; + } +#if RCT_NEW_ARCH_ENABLED + _contextContainer = std::make_shared(); + _reactNativeConfig = std::make_shared(); + _contextContainer->insert("ReactNativeConfig", _reactNativeConfig); + self.bridgeAdapter = [[RCTSurfacePresenterBridgeAdapter alloc] initWithBridge:self.bridge + contextContainer:_contextContainer]; + self.bridge.surfacePresenter = self.bridgeAdapter.surfacePresenter; +#endif + + NSDictionary *initProps = [self prepareInitialProps]; + UIView *rootView = [self createRootViewWithBridge:self.bridge moduleName:self.moduleName initProps:initProps]; + + if (@available(iOS 13.0, *)) { + rootView.backgroundColor = [UIColor systemBackgroundColor]; + } else { + rootView.backgroundColor = [UIColor whiteColor]; + } + + self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; + UIViewController *rootViewController = [self createRootViewController]; + rootViewController.view = rootView; + self.window.rootViewController = rootViewController; + [self.window makeKeyAndVisible]; + return YES; +} + +- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge +{ + [NSException raise:@"RCTBridgeDelegate::sourceURLForBridge not implemented" + format:@"Subclasses must implement a valid sourceURLForBridge method"]; + return nil; +} + +- (BOOL)concurrentRootEnabled +{ + [NSException raise:@"concurrentRootEnabled not implemented" + format:@"Subclasses must implement a valid concurrentRootEnabled method"]; + return true; +} + +- (NSDictionary *)prepareInitialProps +{ + NSMutableDictionary *initProps = [NSMutableDictionary new]; + +#ifdef RCT_NEW_ARCH_ENABLED + initProps[kRNConcurrentRoot] = @([self concurrentRootEnabled]); +#endif + + return initProps; +} + +- (RCTBridge *)createBridgeWithDelegate:(id)delegate launchOptions:(NSDictionary *)launchOptions +{ + return [[RCTBridge alloc] initWithDelegate:delegate launchOptions:launchOptions]; +} + +- (UIView *)createRootViewWithBridge:(RCTBridge *)bridge + moduleName:(NSString *)moduleName + initProps:(NSDictionary *)initProps +{ + BOOL enableFabric = NO; +#if RCT_NEW_ARCH_ENABLED + enableFabric = self.fabricEnabled; +#endif + return RCTAppSetupDefaultRootView(bridge, moduleName, initProps, enableFabric); +} + +- (UIViewController *)createRootViewController +{ + return [UIViewController new]; +} + +#if RCT_NEW_ARCH_ENABLED +#pragma mark - RCTCxxBridgeDelegate + +- (std::unique_ptr)jsExecutorFactoryForBridge:(RCTBridge *)bridge +{ + self.turboModuleManager = [[RCTTurboModuleManager alloc] initWithBridge:bridge + delegate:self + jsInvoker:bridge.jsCallInvoker]; + return RCTAppSetupDefaultJsExecutorFactory(bridge, _turboModuleManager); +} + +#pragma mark RCTTurboModuleManagerDelegate + +- (Class)getModuleClassFromName:(const char *)name +{ + return RCTCoreModulesClassProvider(name); +} + +- (std::shared_ptr)getTurboModule:(const std::string &)name + jsInvoker:(std::shared_ptr)jsInvoker +{ + return nullptr; +} + +- (std::shared_ptr)getTurboModule:(const std::string &)name + initParams: + (const facebook::react::ObjCTurboModule::InitParams &)params +{ + return nullptr; +} + +- (id)getModuleInstanceFromClass:(Class)moduleClass +{ + return RCTAppSetupDefaultModuleFromClass(moduleClass); +} + +#pragma mark - New Arch Enabled settings + +- (BOOL)turboModuleEnabled +{ + return YES; +} + +- (BOOL)fabricEnabled +{ + return YES; +} + +#endif + +@end diff --git a/Libraries/AppDelegate/React-RCTAppDelegate.podspec b/Libraries/AppDelegate/React-RCTAppDelegate.podspec new file mode 100644 index 000000000000..fff87118479c --- /dev/null +++ b/Libraries/AppDelegate/React-RCTAppDelegate.podspec @@ -0,0 +1,73 @@ +# 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. + +require "json" + +package = JSON.parse(File.read(File.join(__dir__, "..", "..", "package.json"))) +version = package['version'] + +source = { :git => 'https://github.com/facebook/react-native.git' } +if version == '1000.0.0' + # This is an unpublished version, use the latest commit hash of the react-native repo, which we’re presumably in. + source[:commit] = `git rev-parse HEAD`.strip if system("git rev-parse --git-dir > /dev/null 2>&1") +else + source[:tag] = "v#{version}" +end + +folly_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1' +folly_compiler_flags = folly_flags + ' ' + '-Wno-comma -Wno-shorten-64-to-32' + +new_arch_enabled_flag="RCT_NEW_ARCH_ENABLED" +is_new_arch_enabled = ENV[new_arch_enabled_flag] == "1" +other_cflags = "$(inherited) -DRN_FABRIC_ENABLED " + folly_flags + (is_new_arch_enabled ? " -D"+"RCT_NEW_ARCH_ENABLED" : "") + +use_hermes = ENV['USE_HERMES'] == '1' + +header_search_paths = [ + "$(PODS_TARGET_SRCROOT)/ReactCommon", + "$(PODS_ROOT)/Headers/Private/React-Core", + "$(PODS_ROOT)/boost", + "$(PODS_ROOT)/DoubleConversion", + "$(PODS_ROOT)/RCT-Folly", + "${PODS_ROOT}/Headers/Public/FlipperKit", + "$(PODS_ROOT)/Headers/Public/ReactCommon", + "$(PODS_ROOT)/Headers/Public/React-RCTFabric" +].concat(use_hermes ? [ + "$(PODS_ROOT)/Headers/Public/React-hermes", + "$(PODS_ROOT)/Headers/Public/hermes-engine" +] : []).map{|p| "\"#{p}\""}.join(" ") + +Pod::Spec.new do |s| + s.name = "React-RCTAppDelegate" + s.version = version + s.summary = "An utility library to simplify common operations for the New Architecture" + s.homepage = "https://reactnative.dev/" + s.documentation_url = "https://reactnative.dev/docs/actionsheetios" + s.license = package["license"] + s.author = "Facebook, Inc. and its affiliates" + s.platforms = { :ios => "12.4" } + s.source = source + s.source_files = "**/*.{c,h,m,mm,S,cpp}" + + # This guard prevent to install the dependencies when we run `pod install` in the old architecture. + s.compiler_flags = other_cflags + s.pod_target_xcconfig = { + "HEADER_SEARCH_PATHS" => header_search_paths, + "OTHER_CPLUSPLUSFLAGS" => other_cflags, + "CLANG_CXX_LANGUAGE_STANDARD" => "c++17" + } + s.user_target_xcconfig = { "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT)/Headers/Private/React-Core\""} + + s.dependency "React-Core" + s.dependency "RCT-Folly" + s.dependency "RCTRequired" + s.dependency "RCTTypeSafety" + s.dependency "ReactCommon/turbomodule/core" + + if is_new_arch_enabled + s.dependency "React-RCTFabric" + s.dependency "React-graphics" + end +end diff --git a/Libraries/AppState/AppState.d.ts b/Libraries/AppState/AppState.d.ts new file mode 100644 index 000000000000..45beaca9bde9 --- /dev/null +++ b/Libraries/AppState/AppState.d.ts @@ -0,0 +1,58 @@ +/** + * 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. + * + * @format + */ + +import {NativeEventSubscription} from '../EventEmitter/RCTNativeAppEventEmitter'; + +/** + * AppState can tell you if the app is in the foreground or background, + * and notify you when the state changes. + * + * AppState is frequently used to determine the intent and proper behavior + * when handling push notifications. + * + * App State Events + * change - This even is received when the app state has changed. + * focus [Android] - Received when the app gains focus (the user is interacting with the app). + * blur [Android] - Received when the user is not actively interacting with the app. + * + * App States + * active - The app is running in the foreground + * background - The app is running in the background. The user is either in another app or on the home screen + * inactive [iOS] - This is a transition state that currently never happens for typical React Native apps. + * unknown [iOS] - Initial value until the current app state is determined + * extension [iOS] - The app is running as an app extension + * + * For more information, see Apple's documentation: https://developer.apple.com/library/ios/documentation/iPhone/Conceptual/iPhoneOSProgrammingGuide/TheAppLifeCycle/TheAppLifeCycle.html + * + * @see https://reactnative.dev/docs/appstate#app-states + */ +export type AppStateEvent = 'change' | 'memoryWarning' | 'blur' | 'focus'; +export type AppStateStatus = + | 'active' + | 'background' + | 'inactive' + | 'unknown' + | 'extension'; + +export interface AppStateStatic { + currentState: AppStateStatus; + isAvailable: boolean; + + /** + * Add a handler to AppState changes by listening to the change event + * type and providing the handler + */ + addEventListener( + type: AppStateEvent, + listener: (state: AppStateStatus) => void, + ): NativeEventSubscription; +} + +export const AppState: AppStateStatic; +export type AppState = AppStateStatic; diff --git a/Libraries/AppState/AppState.js b/Libraries/AppState/AppState.js index 99e731bd43fb..f82afa0f6142 100644 --- a/Libraries/AppState/AppState.js +++ b/Libraries/AppState/AppState.js @@ -8,11 +8,11 @@ * @format */ -import {type EventSubscription} from '../vendor/emitter/EventEmitter'; import NativeEventEmitter from '../EventEmitter/NativeEventEmitter'; import logError from '../Utilities/logError'; -import NativeAppState from './NativeAppState'; import Platform from '../Utilities/Platform'; +import {type EventSubscription} from '../vendor/emitter/EventEmitter'; +import NativeAppState from './NativeAppState'; export type AppStateValues = 'inactive' | 'background' | 'active'; @@ -76,17 +76,13 @@ class AppState { // It's possible that the state will have changed here & listeners need to be notified if (!eventUpdated && this.currentState !== appStateData.app_state) { this.currentState = appStateData.app_state; + // $FlowFixMe[incompatible-call] emitter.emit('appStateDidChange', appStateData); } }, logError); } } - // TODO: now that AppState is a subclass of NativeEventEmitter, we could - // deprecate `addEventListener` and `removeEventListener` and just use - // addListener` and `listener.remove()` directly. That will be a breaking - // change though, as both the method and event names are different - // (addListener events are currently required to be globally unique). /** * Add a handler to AppState changes by listening to the `change` event type * and providing the handler. diff --git a/Libraries/AppState/NativeAppState.js b/Libraries/AppState/NativeAppState.js index d546e70051d0..177e167c6790 100644 --- a/Libraries/AppState/NativeAppState.js +++ b/Libraries/AppState/NativeAppState.js @@ -9,6 +9,7 @@ */ import type {TurboModule} from '../TurboModule/RCTExport'; + import * as TurboModuleRegistry from '../TurboModule/TurboModuleRegistry'; export interface Spec extends TurboModule { diff --git a/Libraries/BatchedBridge/MessageQueue.js b/Libraries/BatchedBridge/MessageQueue.js index 2ae720810ac9..a9810bc6f942 100644 --- a/Libraries/BatchedBridge/MessageQueue.js +++ b/Libraries/BatchedBridge/MessageQueue.js @@ -241,7 +241,7 @@ class MessageQueue { params: mixed[], onFail: ?(...mixed[]) => void, onSucc: ?(...mixed[]) => void, - ) { + ): void { this.processCallbacks(moduleID, methodID, params, onFail, onSucc); this._queue[MODULE_IDS].push(moduleID); @@ -252,7 +252,7 @@ class MessageQueue { // folly-convertible. As a special case, if a prop value is a // function it is permitted here, and special-cased in the // conversion. - const isValidArgument = (val: mixed) => { + const isValidArgument = (val: mixed): boolean => { switch (typeof val) { case 'undefined': case 'boolean': @@ -321,6 +321,7 @@ class MessageQueue { } Systrace.counterEvent('pending_js_to_native_queue', this._queue[0].length); if (__DEV__ && this.__spy && isFinite(moduleID)) { + // $FlowFixMe[not-a-function] this.__spy({ type: TO_NATIVE, module: this._remoteModuleTable[moduleID], @@ -380,7 +381,7 @@ class MessageQueue { return ( // $FlowFixMe[cannot-resolve-name] typeof DebuggerInternal !== 'undefined' && - DebuggerInternal.shouldPauseOnThrow === true // eslint-disable-line no-undef + DebuggerInternal.shouldPauseOnThrow === true ); } @@ -408,9 +409,12 @@ class MessageQueue { const callableModuleNames = Object.keys(this._lazyCallableModules); const n = callableModuleNames.length; const callableModuleNameList = callableModuleNames.join(', '); + + // TODO(T122225939): Remove after investigation: Why are we getting to this line in bridgeless mode? + const isBridgelessMode = global.RN$Bridgeless === true ? 'true' : 'false'; invariant( false, - `Failed to call into JavaScript module method ${module}.${method}(). Module has not been registered as callable. Registered callable JavaScript modules (n = ${n}): ${callableModuleNameList}. + `Failed to call into JavaScript module method ${module}.${method}(). Module has not been registered as callable. Bridgeless Mode: ${isBridgelessMode}. Registered callable JavaScript modules (n = ${n}): ${callableModuleNameList}. A frequent cause of the error is that the application entry file path is incorrect. This can also happen when the JS bundle is corrupt or there is an early initialization error when loading React Native.`, ); } @@ -424,7 +428,7 @@ class MessageQueue { Systrace.endEvent(); } - __invokeCallback(cbID: number, args: mixed[]) { + __invokeCallback(cbID: number, args: mixed[]): void { this._lastFlush = Date.now(); this._eventLoopStartTime = this._lastFlush; diff --git a/Libraries/BatchedBridge/NativeModules.d.ts b/Libraries/BatchedBridge/NativeModules.d.ts new file mode 100644 index 000000000000..ba35cca1e1bc --- /dev/null +++ b/Libraries/BatchedBridge/NativeModules.d.ts @@ -0,0 +1,25 @@ +/** + * 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. + * + * @format + */ + +/** + * Interface for NativeModules which allows to augment NativeModules with type informations. + * See react-native-sensor-manager for example. + */ +interface NativeModulesStatic { + [name: string]: any; +} + +/** + * Native Modules written in ObjectiveC/Swift/Java exposed via the RCTBridge + * Define lazy getters for each module. These will return the module if already loaded, or load it if not. + * See https://reactnative.dev/docs/native-modules-ios + * @example + * const MyModule = NativeModules.ModuleName + */ +export const NativeModules: NativeModulesStatic; diff --git a/Libraries/BatchedBridge/NativeModules.js b/Libraries/BatchedBridge/NativeModules.js index 30259e79b9fd..0ea6b7c5c4a8 100644 --- a/Libraries/BatchedBridge/NativeModules.js +++ b/Libraries/BatchedBridge/NativeModules.js @@ -155,6 +155,7 @@ function genMethod(moduleID: number, methodID: number, type: MethodType) { } }; } + // $FlowFixMe[prop-missing] fn.type = type; return fn; } diff --git a/Libraries/BatchedBridge/__tests__/MessageQueue-test.js b/Libraries/BatchedBridge/__tests__/MessageQueue-test.js index c94f580a1cbf..46304de89ad7 100644 --- a/Libraries/BatchedBridge/__tests__/MessageQueue-test.js +++ b/Libraries/BatchedBridge/__tests__/MessageQueue-test.js @@ -4,8 +4,8 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @emails oncall+react_native * @format + * @oncall react_native */ 'use strict'; @@ -108,7 +108,7 @@ describe('MessageQueue', function () { const unknownModule = 'UnknownModule', unknownMethod = 'UnknownMethod'; expect(() => queue.__callFunction(unknownModule, unknownMethod)).toThrow( - `Failed to call into JavaScript module method ${unknownModule}.${unknownMethod}(). Module has not been registered as callable. Registered callable JavaScript modules (n = 1): MessageQueueTestModule. + `Failed to call into JavaScript module method ${unknownModule}.${unknownMethod}(). Module has not been registered as callable. Bridgeless Mode: false. Registered callable JavaScript modules (n = 1): MessageQueueTestModule. A frequent cause of the error is that the application entry file path is incorrect. This can also happen when the JS bundle is corrupt or there is an early initialization error when loading React Native.`, ); }); diff --git a/Libraries/BatchedBridge/__tests__/NativeModules-test.js b/Libraries/BatchedBridge/__tests__/NativeModules-test.js index f4d4aa7a93a6..570c95127940 100644 --- a/Libraries/BatchedBridge/__tests__/NativeModules-test.js +++ b/Libraries/BatchedBridge/__tests__/NativeModules-test.js @@ -5,7 +5,7 @@ * LICENSE file in the root directory of this source tree. * * @format - * @emails oncall+react_native + * @oncall react_native */ 'use strict'; diff --git a/Libraries/Blob/BlobManager.js b/Libraries/Blob/BlobManager.js index 0d4cee7282ba..98e69112957d 100644 --- a/Libraries/Blob/BlobManager.js +++ b/Libraries/Blob/BlobManager.js @@ -8,13 +8,14 @@ * @format */ -const Blob = require('./Blob'); -const BlobRegistry = require('./BlobRegistry'); +import type {BlobCollector, BlobData, BlobOptions} from './BlobTypes'; -import type {BlobData, BlobOptions, BlobCollector} from './BlobTypes'; import NativeBlobModule from './NativeBlobModule'; import invariant from 'invariant'; +const Blob = require('./Blob'); +const BlobRegistry = require('./BlobRegistry'); + /*eslint-disable no-bitwise */ /*eslint-disable eqeqeq */ @@ -110,6 +111,7 @@ class BlobManager { */ static createFromOptions(options: BlobData): Blob { BlobRegistry.register(options.blobId); + // $FlowFixMe[prop-missing] return Object.assign(Object.create(Blob.prototype), { data: // Reuse the collector instance when creating from an existing blob. diff --git a/Libraries/Blob/File.js b/Libraries/Blob/File.js index 5aa2dd73c46f..6f643307c5d0 100644 --- a/Libraries/Blob/File.js +++ b/Libraries/Blob/File.js @@ -10,12 +10,11 @@ 'use strict'; -const Blob = require('./Blob'); +import type {BlobOptions} from './BlobTypes'; +const Blob = require('./Blob'); const invariant = require('invariant'); -import type {BlobOptions} from './BlobTypes'; - /** * The File interface provides information about files. */ diff --git a/Libraries/Blob/FileReader.js b/Libraries/Blob/FileReader.js index c0b7feaec713..57b3093cf6b0 100644 --- a/Libraries/Blob/FileReader.js +++ b/Libraries/Blob/FileReader.js @@ -8,11 +8,12 @@ * @format */ -const Blob = require('./Blob'); -const EventTarget = require('event-target-shim'); +import type Blob from './Blob'; import NativeFileReaderModule from './NativeFileReaderModule'; +const EventTarget = require('event-target-shim'); + type ReadyState = | 0 // EMPTY | 1 // LOADING @@ -73,11 +74,11 @@ class FileReader extends (EventTarget(...READER_EVENTS): any) { } } - readAsArrayBuffer() { + readAsArrayBuffer(): any { throw new Error('FileReader.readAsArrayBuffer is not implemented'); } - readAsDataURL(blob: ?Blob) { + readAsDataURL(blob: ?Blob): void { this._aborted = false; if (blob == null) { @@ -104,7 +105,7 @@ class FileReader extends (EventTarget(...READER_EVENTS): any) { ); } - readAsText(blob: ?Blob, encoding: string = 'UTF-8') { + readAsText(blob: ?Blob, encoding: string = 'UTF-8'): void { this._aborted = false; if (blob == null) { diff --git a/Libraries/Blob/NativeBlobModule.js b/Libraries/Blob/NativeBlobModule.js index 2b474d693527..00a86fae31bf 100644 --- a/Libraries/Blob/NativeBlobModule.js +++ b/Libraries/Blob/NativeBlobModule.js @@ -9,6 +9,7 @@ */ import type {TurboModule} from '../TurboModule/RCTExport'; + import * as TurboModuleRegistry from '../TurboModule/TurboModuleRegistry'; export interface Spec extends TurboModule { diff --git a/Libraries/Blob/NativeFileReaderModule.js b/Libraries/Blob/NativeFileReaderModule.js index ac964b3af42e..e581aba74d17 100644 --- a/Libraries/Blob/NativeFileReaderModule.js +++ b/Libraries/Blob/NativeFileReaderModule.js @@ -9,6 +9,7 @@ */ import type {TurboModule} from '../TurboModule/RCTExport'; + import * as TurboModuleRegistry from '../TurboModule/TurboModuleRegistry'; export interface Spec extends TurboModule { diff --git a/Libraries/Blob/RCTBlobCollector.h b/Libraries/Blob/RCTBlobCollector.h index fdd68112d682..4d0166aab7db 100644 --- a/Libraries/Blob/RCTBlobCollector.h +++ b/Libraries/Blob/RCTBlobCollector.h @@ -7,8 +7,6 @@ #import -using namespace facebook; - @class RCTBlobManager; namespace facebook { diff --git a/Libraries/Blob/RCTBlobCollector.mm b/Libraries/Blob/RCTBlobCollector.mm index b7387c445a50..60053c3a1403 100644 --- a/Libraries/Blob/RCTBlobCollector.mm +++ b/Libraries/Blob/RCTBlobCollector.mm @@ -7,16 +7,19 @@ #import "RCTBlobCollector.h" -#import #import +#import namespace facebook { namespace react { RCTBlobCollector::RCTBlobCollector(RCTBlobManager *blobManager, const std::string &blobId) -: blobId_(blobId), blobManager_(blobManager) {} + : blobId_(blobId), blobManager_(blobManager) +{ +} -RCTBlobCollector::~RCTBlobCollector() { +RCTBlobCollector::~RCTBlobCollector() +{ RCTBlobManager *blobManager = blobManager_; NSString *blobId = [NSString stringWithUTF8String:blobId_.c_str()]; dispatch_async([blobManager_ methodQueue], ^{ @@ -24,28 +27,29 @@ }); } -void RCTBlobCollector::install(RCTBlobManager *blobManager) { +void RCTBlobCollector::install(RCTBlobManager *blobManager) +{ __weak RCTCxxBridge *cxxBridge = (RCTCxxBridge *)blobManager.bridge; - [cxxBridge dispatchBlock:^{ - if (!cxxBridge || cxxBridge.runtime == nullptr) { - return; - } - jsi::Runtime &runtime = *(jsi::Runtime *)cxxBridge.runtime; - runtime.global().setProperty( - runtime, - "__blobCollectorProvider", - jsi::Function::createFromHostFunction( - runtime, - jsi::PropNameID::forAscii(runtime, "__blobCollectorProvider"), - 1, - [blobManager](jsi::Runtime& rt, const jsi::Value& thisVal, const jsi::Value* args, size_t count) { - auto blobId = args[0].asString(rt).utf8(rt); - auto blobCollector = std::make_shared(blobManager, blobId); - return jsi::Object::createFromHostObject(rt, blobCollector); + [cxxBridge + dispatchBlock:^{ + if (!cxxBridge || cxxBridge.runtime == nullptr) { + return; } - ) - ); - } queue:RCTJSThread]; + jsi::Runtime &runtime = *(jsi::Runtime *)cxxBridge.runtime; + runtime.global().setProperty( + runtime, + "__blobCollectorProvider", + jsi::Function::createFromHostFunction( + runtime, + jsi::PropNameID::forAscii(runtime, "__blobCollectorProvider"), + 1, + [blobManager](jsi::Runtime &rt, const jsi::Value &thisVal, const jsi::Value *args, size_t count) { + auto blobId = args[0].asString(rt).utf8(rt); + auto blobCollector = std::make_shared(blobManager, blobId); + return jsi::Object::createFromHostObject(rt, blobCollector); + })); + } + queue:RCTJSThread]; } } // namespace react diff --git a/Libraries/Blob/RCTBlobManager.h b/Libraries/Blob/RCTBlobManager.h index 34cd8b82cf90..317b202911df 100755 --- a/Libraries/Blob/RCTBlobManager.h +++ b/Libraries/Blob/RCTBlobManager.h @@ -7,8 +7,8 @@ #import #import -#import #import +#import @interface RCTBlobManager : NSObject diff --git a/Libraries/Blob/RCTBlobManager.mm b/Libraries/Blob/RCTBlobManager.mm index 567e16ffee56..14496f7e4910 100755 --- a/Libraries/Blob/RCTBlobManager.mm +++ b/Libraries/Blob/RCTBlobManager.mm @@ -15,17 +15,20 @@ #import #import -#import "RCTBlobPlugins.h" #import "RCTBlobCollector.h" +#import "RCTBlobPlugins.h" static NSString *const kBlobURIScheme = @"blob"; -@interface RCTBlobManager () +@interface RCTBlobManager () < + RCTNetworkingRequestHandler, + RCTNetworkingResponseHandler, + RCTWebSocketContentHandler, + NativeBlobModuleSpec> @end -@implementation RCTBlobManager -{ +@implementation RCTBlobManager { // Blobs should be thread safe since they are used from the websocket and networking module, // make sure to use proper locking when accessing this. NSMutableDictionary *_blobs; @@ -61,8 +64,8 @@ + (BOOL)requiresMainQueueSetup - (NSDictionary *)getConstants { return @{ - @"BLOB_URI_SCHEME": kBlobURIScheme, - @"BLOB_URI_HOST": [NSNull null], + @"BLOB_URI_SCHEME" : kBlobURIScheme, + @"BLOB_URI_HOST" : [NSNull null], }; } @@ -84,9 +87,7 @@ - (NSData *)resolve:(NSDictionary *)blob NSString *blobId = [RCTConvert NSString:blob[@"blobId"]]; NSNumber *offset = [RCTConvert NSNumber:blob[@"offset"]]; NSNumber *size = [RCTConvert NSNumber:blob[@"size"]]; - return [self resolve:blobId - offset:offset ? [offset integerValue] : 0 - size:size ? [size integerValue] : -1]; + return [self resolve:blobId offset:offset ? [offset integerValue] : 0 size:size ? [size integerValue] : -1]; } - (NSData *)resolve:(NSString *)blobId offset:(NSInteger)offset size:(NSInteger)size @@ -152,29 +153,32 @@ - (void)remove:(NSString *)blobId }); } -RCT_EXPORT_METHOD(addWebSocketHandler:(double)socketID) +RCT_EXPORT_METHOD(addWebSocketHandler : (double)socketID) { dispatch_async(((RCTWebSocketModule *)[_moduleRegistry moduleForName:"WebSocketModule"]).methodQueue, ^{ - [[self->_moduleRegistry moduleForName:"WebSocketModule"] setContentHandler:self forSocketID:[NSNumber numberWithDouble:socketID]]; + [[self->_moduleRegistry moduleForName:"WebSocketModule"] setContentHandler:self + forSocketID:[NSNumber numberWithDouble:socketID]]; }); } -RCT_EXPORT_METHOD(removeWebSocketHandler:(double)socketID) +RCT_EXPORT_METHOD(removeWebSocketHandler : (double)socketID) { dispatch_async(((RCTWebSocketModule *)[_moduleRegistry moduleForName:"WebSocketModule"]).methodQueue, ^{ - [[self->_moduleRegistry moduleForName:"WebSocketModule"] setContentHandler:nil forSocketID:[NSNumber numberWithDouble:socketID]]; + [[self->_moduleRegistry moduleForName:"WebSocketModule"] setContentHandler:nil + forSocketID:[NSNumber numberWithDouble:socketID]]; }); } // @lint-ignore FBOBJCUNTYPEDCOLLECTION1 -RCT_EXPORT_METHOD(sendOverSocket:(NSDictionary *)blob socketID:(double)socketID) +RCT_EXPORT_METHOD(sendOverSocket : (NSDictionary *)blob socketID : (double)socketID) { dispatch_async(((RCTWebSocketModule *)[_moduleRegistry moduleForName:"WebSocketModule"]).methodQueue, ^{ - [[self->_moduleRegistry moduleForName:"WebSocketModule"] sendData:[self resolve:blob] forSocketID:[NSNumber numberWithDouble:socketID]]; + [[self->_moduleRegistry moduleForName:"WebSocketModule"] sendData:[self resolve:blob] + forSocketID:[NSNumber numberWithDouble:socketID]]; }); } -RCT_EXPORT_METHOD(createFromParts:(NSArray *> *)parts withId:(NSString *)blobId) +RCT_EXPORT_METHOD(createFromParts : (NSArray *> *)parts withId : (NSString *)blobId) { NSMutableData *data = [NSMutableData new]; for (NSDictionary *part in parts) { @@ -193,7 +197,7 @@ - (void)remove:(NSString *)blobId [self store:data withId:blobId]; } -RCT_EXPORT_METHOD(release:(NSString *)blobId) +RCT_EXPORT_METHOD(release : (NSString *)blobId) { [self remove:blobId]; } @@ -267,7 +271,7 @@ - (NSDictionary *)handleNetworkingRequest:(NSDictionary *)data contentType = blob[@"type"]; } - return @{@"body": [self resolve:blob], @"contentType": contentType}; + return @{@"body" : [self resolve:blob], @"contentType" : contentType}; } - (BOOL)canHandleNetworkingResponse:(NSString *)responseType @@ -281,11 +285,11 @@ - (id)handleNetworkingResponse:(NSURLResponse *)response data:(NSData *)data // an empty blob as per the XMLHttpRequest spec. data = data ?: [NSData new]; return @{ - @"blobId": [self store:data], - @"offset": @0, - @"size": @(data.length), - @"name": RCTNullIfNil([response suggestedFilename]), - @"type": RCTNullIfNil([response MIMEType]), + @"blobId" : [self store:data], + @"offset" : @0, + @"size" : @(data.length), + @"name" : RCTNullIfNil([response suggestedFilename]), + @"type" : RCTNullIfNil([response MIMEType]), }; } @@ -302,19 +306,21 @@ - (id)processWebsocketMessage:(id)message *type = @"blob"; return @{ - @"blobId": [self store:message], - @"offset": @0, - @"size": @(((NSData *)message).length), + @"blobId" : [self store:message], + @"offset" : @0, + @"size" : @(((NSData *)message).length), }; } -- (std::shared_ptr)getTurboModule:(const facebook::react::ObjCTurboModule::InitParams &)params +- (std::shared_ptr)getTurboModule: + (const facebook::react::ObjCTurboModule::InitParams &)params { return std::make_shared(params); } @end -Class RCTBlobManagerCls(void) { +Class RCTBlobManagerCls(void) +{ return RCTBlobManager.class; } diff --git a/Libraries/Blob/RCTFileReaderModule.mm b/Libraries/Blob/RCTFileReaderModule.mm index f52a68aba98c..685d86b48d80 100644 --- a/Libraries/Blob/RCTFileReaderModule.mm +++ b/Libraries/Blob/RCTFileReaderModule.mm @@ -5,7 +5,6 @@ * LICENSE file in the root directory of this source tree. */ - #import #import @@ -16,7 +15,7 @@ #import "RCTBlobPlugins.h" -@interface RCTFileReaderModule() +@interface RCTFileReaderModule () @end @implementation RCTFileReaderModule @@ -25,25 +24,29 @@ @implementation RCTFileReaderModule @synthesize moduleRegistry = _moduleRegistry; -RCT_EXPORT_METHOD(readAsText:(NSDictionary *)blob - encoding:(NSString *)encoding - resolve:(RCTPromiseResolveBlock)resolve - reject:(RCTPromiseRejectBlock)reject) +RCT_EXPORT_METHOD(readAsText + : (NSDictionary *)blob encoding + : (NSString *)encoding resolve + : (RCTPromiseResolveBlock)resolve reject + : (RCTPromiseRejectBlock)reject) { RCTBlobManager *blobManager = [_moduleRegistry moduleForName:"BlobModule"]; dispatch_async(blobManager.methodQueue, ^{ NSData *data = [blobManager resolve:blob]; if (data == nil) { - reject(RCTErrorUnspecified, - [NSString stringWithFormat:@"Unable to resolve data for blob: %@", [RCTConvert NSString:blob[@"blobId"]]], nil); + reject( + RCTErrorUnspecified, + [NSString stringWithFormat:@"Unable to resolve data for blob: %@", [RCTConvert NSString:blob[@"blobId"]]], + nil); } else { NSStringEncoding stringEncoding; if (encoding == nil) { stringEncoding = NSUTF8StringEncoding; } else { - stringEncoding = CFStringConvertEncodingToNSStringEncoding(CFStringConvertIANACharSetNameToEncoding((CFStringRef) encoding)); + stringEncoding = + CFStringConvertEncodingToNSStringEncoding(CFStringConvertIANACharSetNameToEncoding((CFStringRef)encoding)); } NSString *text = [[NSString alloc] initWithData:data encoding:stringEncoding]; @@ -53,29 +56,33 @@ @implementation RCTFileReaderModule }); } -RCT_EXPORT_METHOD(readAsDataURL:(NSDictionary *)blob - resolve:(RCTPromiseResolveBlock)resolve - reject:(RCTPromiseRejectBlock)reject) +RCT_EXPORT_METHOD(readAsDataURL + : (NSDictionary *)blob resolve + : (RCTPromiseResolveBlock)resolve reject + : (RCTPromiseRejectBlock)reject) { RCTBlobManager *blobManager = [_moduleRegistry moduleForName:"BlobModule"]; dispatch_async(blobManager.methodQueue, ^{ NSData *data = [blobManager resolve:blob]; if (data == nil) { - reject(RCTErrorUnspecified, - [NSString stringWithFormat:@"Unable to resolve data for blob: %@", [RCTConvert NSString:blob[@"blobId"]]], nil); + reject( + RCTErrorUnspecified, + [NSString stringWithFormat:@"Unable to resolve data for blob: %@", [RCTConvert NSString:blob[@"blobId"]]], + nil); } else { NSString *type = [RCTConvert NSString:blob[@"type"]]; NSString *text = [NSString stringWithFormat:@"data:%@;base64,%@", - type != nil && [type length] > 0 ? type : @"application/octet-stream", - [data base64EncodedStringWithOptions:0]]; + type != nil && [type length] > 0 ? type : @"application/octet-stream", + [data base64EncodedStringWithOptions:0]]; resolve(text); } }); } -- (std::shared_ptr)getTurboModule:(const facebook::react::ObjCTurboModule::InitParams &)params +- (std::shared_ptr)getTurboModule: + (const facebook::react::ObjCTurboModule::InitParams &)params { return std::make_shared(params); } diff --git a/Libraries/Blob/React-RCTBlob.podspec b/Libraries/Blob/React-RCTBlob.podspec index ee488e04aa14..30cf7e141710 100644 --- a/Libraries/Blob/React-RCTBlob.podspec +++ b/Libraries/Blob/React-RCTBlob.podspec @@ -45,5 +45,4 @@ Pod::Spec.new do |s| s.dependency "React-Core/RCTBlobHeaders", version s.dependency "React-Core/RCTWebSocket", version s.dependency "React-RCTNetwork", version - s.dependency "React-jsi", version end diff --git a/Libraries/Blob/URL.js b/Libraries/Blob/URL.js index 77b4d7f94d6d..1cc3a9e8c4a2 100644 --- a/Libraries/Blob/URL.js +++ b/Libraries/Blob/URL.js @@ -8,7 +8,7 @@ * @flow */ -const Blob = require('./Blob'); +import type Blob from './Blob'; import NativeBlobModule from './NativeBlobModule'; @@ -54,7 +54,7 @@ if ( // Small subset from whatwg-url: https://github.com/jsdom/whatwg-url/tree/master/src // The reference code bloat comes from Unicode issues with URLs, so those won't work here. export class URLSearchParams { - _searchParams = []; + _searchParams: Array> = []; constructor(params: any) { if (typeof params === 'object') { @@ -62,35 +62,36 @@ export class URLSearchParams { } } - append(key: string, value: string) { + append(key: string, value: string): void { this._searchParams.push([key, value]); } - delete(name: string) { + delete(name: string): void { throw new Error('URLSearchParams.delete is not implemented'); } - get(name: string) { + get(name: string): void { throw new Error('URLSearchParams.get is not implemented'); } - getAll(name: string) { + getAll(name: string): void { throw new Error('URLSearchParams.getAll is not implemented'); } - has(name: string) { + has(name: string): void { throw new Error('URLSearchParams.has is not implemented'); } - set(name: string, value: string) { + set(name: string, value: string): void { throw new Error('URLSearchParams.set is not implemented'); } - sort() { + sort(): void { throw new Error('URLSearchParams.sort is not implemented'); } // $FlowFixMe[unsupported-syntax] + // $FlowFixMe[missing-local-annot] [Symbol.iterator]() { return this._searchParams[Symbol.iterator](); } @@ -134,6 +135,7 @@ export class URL { // Do nothing. } + // $FlowFixMe[missing-local-annot] constructor(url: string, base: string | URL) { let baseUrl = null; if (!base || validateBaseUrl(url)) { diff --git a/Libraries/Blob/__tests__/Blob-test.js b/Libraries/Blob/__tests__/Blob-test.js index 5e406f024dd0..9fdabab67ef5 100644 --- a/Libraries/Blob/__tests__/Blob-test.js +++ b/Libraries/Blob/__tests__/Blob-test.js @@ -5,7 +5,7 @@ * LICENSE file in the root directory of this source tree. * * @format - * @emails oncall+react_native + * @oncall react_native */ 'use strict'; diff --git a/Libraries/Blob/__tests__/BlobManager-test.js b/Libraries/Blob/__tests__/BlobManager-test.js index e03dd87d9d1c..856eac1ad869 100644 --- a/Libraries/Blob/__tests__/BlobManager-test.js +++ b/Libraries/Blob/__tests__/BlobManager-test.js @@ -5,7 +5,7 @@ * LICENSE file in the root directory of this source tree. * * @format - * @emails oncall+react_native + * @oncall react_native */ 'use strict'; diff --git a/Libraries/Blob/__tests__/File-test.js b/Libraries/Blob/__tests__/File-test.js index 21b50650a998..b6d6c31ec2fe 100644 --- a/Libraries/Blob/__tests__/File-test.js +++ b/Libraries/Blob/__tests__/File-test.js @@ -5,7 +5,7 @@ * LICENSE file in the root directory of this source tree. * * @format - * @emails oncall+react_native + * @oncall react_native */ 'use strict'; diff --git a/Libraries/Blob/__tests__/FileReader-test.js b/Libraries/Blob/__tests__/FileReader-test.js index 8ae1c81a31f8..1e1ac1ab7a4d 100644 --- a/Libraries/Blob/__tests__/FileReader-test.js +++ b/Libraries/Blob/__tests__/FileReader-test.js @@ -5,7 +5,7 @@ * LICENSE file in the root directory of this source tree. * * @format - * @emails oncall+react_native + * @oncall react_native */ 'use strict'; diff --git a/Libraries/Blob/__tests__/URL-test.js b/Libraries/Blob/__tests__/URL-test.js index 28e89950b267..c317217c8fe7 100644 --- a/Libraries/Blob/__tests__/URL-test.js +++ b/Libraries/Blob/__tests__/URL-test.js @@ -5,7 +5,7 @@ * LICENSE file in the root directory of this source tree. * * @format - * @emails oncall+react_native + * @oncall react_native */ 'use strict'; diff --git a/Libraries/BugReporting/NativeBugReporting.js b/Libraries/BugReporting/NativeBugReporting.js index 13a51a585c0b..f886208de3d3 100644 --- a/Libraries/BugReporting/NativeBugReporting.js +++ b/Libraries/BugReporting/NativeBugReporting.js @@ -9,6 +9,7 @@ */ import type {TurboModule} from '../TurboModule/RCTExport'; + import * as TurboModuleRegistry from '../TurboModule/TurboModuleRegistry'; export interface Spec extends TurboModule { diff --git a/Libraries/BugReporting/getReactData.js b/Libraries/BugReporting/getReactData.js index 4440cc4e0d0e..cc988ced1098 100644 --- a/Libraries/BugReporting/getReactData.js +++ b/Libraries/BugReporting/getReactData.js @@ -165,7 +165,7 @@ function copyWithSetImpl( path: Array, idx: number, value: any, -) { +): any { if (idx >= path.length) { return value; } diff --git a/Libraries/Components/AccessibilityInfo/AccessibilityInfo.d.ts b/Libraries/Components/AccessibilityInfo/AccessibilityInfo.d.ts new file mode 100644 index 000000000000..8ac2a29819f9 --- /dev/null +++ b/Libraries/Components/AccessibilityInfo/AccessibilityInfo.d.ts @@ -0,0 +1,157 @@ +/** + * 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. + * + * @format + */ + +import type * as React from 'react'; +import {HostComponent} from '../../../types/public/ReactNativeTypes'; +import {EmitterSubscription} from '../../vendor/emitter/EventEmitter'; + +type AccessibilityChangeEventName = + | 'change' // deprecated, maps to screenReaderChanged + | 'boldTextChanged' // iOS-only Event + | 'grayscaleChanged' // iOS-only Event + | 'invertColorsChanged' // iOS-only Event + | 'reduceMotionChanged' + | 'screenReaderChanged' + | 'reduceTransparencyChanged'; // iOS-only Event + +type AccessibilityChangeEvent = boolean; + +type AccessibilityChangeEventHandler = ( + event: AccessibilityChangeEvent, +) => void; + +type AccessibilityAnnouncementEventName = 'announcementFinished'; // iOS-only Event + +type AccessibilityAnnouncementFinishedEvent = { + announcement: string; + success: boolean; +}; + +type AccessibilityAnnouncementFinishedEventHandler = ( + event: AccessibilityAnnouncementFinishedEvent, +) => void; + +type AccessibilityEventTypes = 'click' | 'focus'; + +/** + * @see https://reactnative.dev/docs/accessibilityinfo + */ +export interface AccessibilityInfoStatic { + /** + * Query whether bold text is currently enabled. + * + * @platform ios + */ + isBoldTextEnabled: () => Promise; + + /** + * Query whether grayscale is currently enabled. + * + * @platform ios + */ + isGrayscaleEnabled: () => Promise; + + /** + * Query whether invert colors is currently enabled. + * + * @platform ios + */ + isInvertColorsEnabled: () => Promise; + + /** + * Query whether reduce motion is currently enabled. + */ + isReduceMotionEnabled: () => Promise; + + /** + * Query whether reduce motion and prefer cross-fade transitions settings are currently enabled. + * + * Returns a promise which resolves to a boolean. + * The result is `true` when prefer cross-fade transitions is enabled and `false` otherwise. + */ + prefersCrossFadeTransitions(): Promise; + + /** + * Query whether reduce transparency is currently enabled. + * + * @platform ios + */ + isReduceTransparencyEnabled: () => Promise; + + /** + * Query whether a screen reader is currently enabled. + */ + isScreenReaderEnabled: () => Promise; + + /** + * Query whether Accessibility Service is currently enabled. + * + * Returns a promise which resolves to a boolean. + * The result is `true` when any service is enabled and `false` otherwise. + * + * @platform android + */ + isAccessibilityServiceEnabled(): Promise; + + /** + * Add an event handler. Supported events: + * - announcementFinished: iOS-only event. Fires when the screen reader has finished making an announcement. + * The argument to the event handler is a dictionary with these keys: + * - announcement: The string announced by the screen reader. + * - success: A boolean indicating whether the announcement was successfully made. + * - AccessibilityEventName constants other than announcementFinished: Fires on accessibility feature change. + * The argument to the event handler is a boolean. + * The boolean is true when the related event's feature is enabled and false otherwise. + * + */ + addEventListener( + eventName: AccessibilityChangeEventName, + handler: AccessibilityChangeEventHandler, + ): EmitterSubscription; + addEventListener( + eventName: AccessibilityAnnouncementEventName, + handler: AccessibilityAnnouncementFinishedEventHandler, + ): EmitterSubscription; + + /** + * Set accessibility focus to a react component. + */ + setAccessibilityFocus: (reactTag: number) => void; + + /** + * Post a string to be announced by the screen reader. + */ + announceForAccessibility: (announcement: string) => void; + + /** + * Post a string to be announced by the screen reader. + * - `announcement`: The string announced by the screen reader. + * - `options`: An object that configures the reading options. + * - `queue`: The announcement will be queued behind existing announcements. iOS only. + */ + announceForAccessibilityWithOptions( + announcement: string, + options: {queue?: boolean}, + ): void; + + /** + * Gets the timeout in millisecond that the user needs. + * This value is set in "Time to take action (Accessibility timeout)" of "Accessibility" settings. + * + * @platform android + */ + getRecommendedTimeoutMillis: (originalTimeout: number) => Promise; + sendAccessibilityEvent: ( + handle: React.ElementRef>, + eventType: AccessibilityEventTypes, + ) => void; +} + +export const AccessibilityInfo: AccessibilityInfoStatic; +export type AccessibilityInfo = AccessibilityInfoStatic; diff --git a/Libraries/Components/AccessibilityInfo/AccessibilityInfo.flow.js b/Libraries/Components/AccessibilityInfo/AccessibilityInfo.flow.js new file mode 100644 index 000000000000..b40fd0230eef --- /dev/null +++ b/Libraries/Components/AccessibilityInfo/AccessibilityInfo.flow.js @@ -0,0 +1,208 @@ +/** + * 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 + */ + +import type {HostComponent} from '../../Renderer/shims/ReactNativeTypes'; +import type {EventSubscription} from '../../vendor/emitter/EventEmitter'; +import type {ElementRef} from 'react'; + +// Events that are only supported on Android. +type AccessibilityEventDefinitionsAndroid = { + accessibilityServiceChanged: [boolean], +}; + +// Events that are only supported on iOS. +type AccessibilityEventDefinitionsIOS = { + announcementFinished: [{announcement: string, success: boolean}], + boldTextChanged: [boolean], + grayscaleChanged: [boolean], + invertColorsChanged: [boolean], + reduceTransparencyChanged: [boolean], +}; + +type AccessibilityEventDefinitions = { + ...AccessibilityEventDefinitionsAndroid, + ...AccessibilityEventDefinitionsIOS, + change: [boolean], // screenReaderChanged + reduceMotionChanged: [boolean], + screenReaderChanged: [boolean], +}; + +type AccessibilityEventTypes = 'click' | 'focus'; +/** + * Sometimes it's useful to know whether or not the device has a screen reader + * that is currently active. The `AccessibilityInfo` API is designed for this + * purpose. You can use it to query the current state of the screen reader as + * well as to register to be notified when the state of the screen reader + * changes. + * + * See https://reactnative.dev/docs/accessibilityinfo + */ +export type AccessibilityInfoType = { + /** + * Query whether bold text is currently enabled. + * + * Returns a promise which resolves to a boolean. + * The result is `true` when bold text is enabled and `false` otherwise. + * + * See https://reactnative.dev/docs/accessibilityinfo#isBoldTextEnabled + */ + isBoldTextEnabled: () => Promise, + + /** + * Query whether grayscale is currently enabled. + * + * Returns a promise which resolves to a boolean. + * The result is `true` when grayscale is enabled and `false` otherwise. + * + * See https://reactnative.dev/docs/accessibilityinfo#isGrayscaleEnabled + */ + isGrayscaleEnabled: () => Promise, + + /** + * Query whether inverted colors are currently enabled. + * + * Returns a promise which resolves to a boolean. + * The result is `true` when invert color is enabled and `false` otherwise. + * + * See https://reactnative.dev/docs/accessibilityinfo#isInvertColorsEnabled + */ + isInvertColorsEnabled: () => Promise, + + /** + * Query whether reduced motion is currently enabled. + * + * Returns a promise which resolves to a boolean. + * The result is `true` when a reduce motion is enabled and `false` otherwise. + * + * See https://reactnative.dev/docs/accessibilityinfo#isReduceMotionEnabled + */ + isReduceMotionEnabled: () => Promise, + + /** + * Query whether reduce motion and prefer cross-fade transitions settings are currently enabled. + * + * Returns a promise which resolves to a boolean. + * The result is `true` when prefer cross-fade transitions is enabled and `false` otherwise. + * + * See https://reactnative.dev/docs/accessibilityinfo#prefersCrossFadeTransitions + */ + prefersCrossFadeTransitions: () => Promise, + + /** + * Query whether reduced transparency is currently enabled. + * + * Returns a promise which resolves to a boolean. + * The result is `true` when a reduce transparency is enabled and `false` otherwise. + * + * See https://reactnative.dev/docs/accessibilityinfo#isReduceTransparencyEnabled + */ + isReduceTransparencyEnabled: () => Promise, + + /** + * Query whether a screen reader is currently enabled. + * + * Returns a promise which resolves to a boolean. + * The result is `true` when a screen reader is enabled and `false` otherwise. + * + * See https://reactnative.dev/docs/accessibilityinfo#isScreenReaderEnabled + */ + isScreenReaderEnabled: () => Promise, + + /** + * Query whether Accessibility Service is currently enabled. + * + * Returns a promise which resolves to a boolean. + * The result is `true` when any service is enabled and `false` otherwise. + * + * @platform android + * + * See https://reactnative.dev/docs/accessibilityinfo/#isaccessibilityserviceenabled-android + */ + isAccessibilityServiceEnabled: () => Promise, + + /** + * Add an event handler. Supported events: + * + * - `reduceMotionChanged`: Fires when the state of the reduce motion toggle changes. + * The argument to the event handler is a boolean. The boolean is `true` when a reduce + * motion is enabled (or when "Transition Animation Scale" in "Developer options" is + * "Animation off") and `false` otherwise. + * - `screenReaderChanged`: Fires when the state of the screen reader changes. The argument + * to the event handler is a boolean. The boolean is `true` when a screen + * reader is enabled and `false` otherwise. + * + * These events are only supported on iOS: + * + * - `boldTextChanged`: iOS-only event. Fires when the state of the bold text toggle changes. + * The argument to the event handler is a boolean. The boolean is `true` when a bold text + * is enabled and `false` otherwise. + * - `grayscaleChanged`: iOS-only event. Fires when the state of the gray scale toggle changes. + * The argument to the event handler is a boolean. The boolean is `true` when a gray scale + * is enabled and `false` otherwise. + * - `invertColorsChanged`: iOS-only event. Fires when the state of the invert colors toggle + * changes. The argument to the event handler is a boolean. The boolean is `true` when a invert + * colors is enabled and `false` otherwise. + * - `reduceTransparencyChanged`: iOS-only event. Fires when the state of the reduce transparency + * toggle changes. The argument to the event handler is a boolean. The boolean is `true` + * when a reduce transparency is enabled and `false` otherwise. + * - `announcementFinished`: iOS-only event. Fires when the screen reader has + * finished making an announcement. The argument to the event handler is a + * dictionary with these keys: + * - `announcement`: The string announced by the screen reader. + * - `success`: A boolean indicating whether the announcement was + * successfully made. + * + * See https://reactnative.dev/docs/accessibilityinfo#addeventlistener + */ + addEventListener>( + eventName: K, + handler: (...$ElementType) => void, + ): EventSubscription, + + /** + * Set accessibility focus to a React component. + * + * See https://reactnative.dev/docs/accessibilityinfo#setaccessibilityfocus + */ + setAccessibilityFocus: (reactTag: number) => void, + + /** + * Send a named accessibility event to a HostComponent. + */ + sendAccessibilityEvent: ( + handle: ElementRef>, + eventType: AccessibilityEventTypes, + ) => void, + + /** + * Post a string to be announced by the screen reader. + * + * See https://reactnative.dev/docs/accessibilityinfo#announceforaccessibility + */ + announceForAccessibility: (announcement: string) => void, + + /** + * Post a string to be announced by the screen reader. + * - `announcement`: The string announced by the screen reader. + * - `options`: An object that configures the reading options. + * - `queue`: The announcement will be queued behind existing announcements. iOS only. + */ + announceForAccessibilityWithOptions: ( + announcement: string, + options: {queue?: boolean}, + ) => void, + + /** + * Get the recommended timeout for changes to the UI needed by this user. + * + * See https://reactnative.dev/docs/accessibilityinfo#getrecommendedtimeoutmillis + */ + getRecommendedTimeoutMillis: (originalTimeout: number) => Promise, +}; diff --git a/Libraries/Components/AccessibilityInfo/AccessibilityInfo.js b/Libraries/Components/AccessibilityInfo/AccessibilityInfo.js index 150635d8531a..195fe7476367 100644 --- a/Libraries/Components/AccessibilityInfo/AccessibilityInfo.js +++ b/Libraries/Components/AccessibilityInfo/AccessibilityInfo.js @@ -8,15 +8,17 @@ * @format */ -import RCTDeviceEventEmitter from '../../EventEmitter/RCTDeviceEventEmitter'; -import {sendAccessibilityEvent} from '../../Renderer/shims/ReactNative'; import type {HostComponent} from '../../Renderer/shims/ReactNativeTypes'; -import Platform from '../../Utilities/Platform'; import type {EventSubscription} from '../../vendor/emitter/EventEmitter'; +import type {AccessibilityInfoType} from './AccessibilityInfo.flow'; +import type {ElementRef} from 'react'; + +import RCTDeviceEventEmitter from '../../EventEmitter/RCTDeviceEventEmitter'; +import {sendAccessibilityEvent} from '../../ReactNative/RendererProxy'; +import Platform from '../../Utilities/Platform'; +import legacySendAccessibilityEvent from './legacySendAccessibilityEvent'; import NativeAccessibilityInfoAndroid from './NativeAccessibilityInfo'; import NativeAccessibilityManagerIOS from './NativeAccessibilityManager'; -import legacySendAccessibilityEvent from './legacySendAccessibilityEvent'; -import type {ElementRef} from 'react'; // Events that are only supported on Android. type AccessibilityEventDefinitionsAndroid = { @@ -73,7 +75,7 @@ const EventNames: Map< * * See https://reactnative.dev/docs/accessibilityinfo */ -const AccessibilityInfo = { +const AccessibilityInfo: AccessibilityInfoType = { /** * Query whether bold text is currently enabled. * @@ -178,6 +180,34 @@ const AccessibilityInfo = { }); }, + /** + * Query whether reduce motion and prefer cross-fade transitions settings are currently enabled. + * + * Returns a promise which resolves to a boolean. + * The result is `true` when prefer cross-fade transitions is enabled and `false` otherwise. + * + * See https://reactnative.dev/docs/accessibilityinfo#prefersCrossFadeTransitions + */ + prefersCrossFadeTransitions(): Promise { + return new Promise((resolve, reject) => { + if (Platform.OS === 'android') { + return Promise.resolve(false); + } else { + if ( + NativeAccessibilityManagerIOS?.getCurrentPrefersCrossFadeTransitionsState != + null + ) { + NativeAccessibilityManagerIOS.getCurrentPrefersCrossFadeTransitionsState( + resolve, + reject, + ); + } else { + reject(null); + } + } + }); + }, + /** * Query whether reduced transparency is currently enabled. * @@ -295,12 +325,14 @@ const AccessibilityInfo = { */ addEventListener>( eventName: K, + // $FlowIssue[incompatible-type] - Flow bug with unions and generics (T128099423) handler: (...$ElementType) => void, ): EventSubscription { const deviceEventName = EventNames.get(eventName); return deviceEventName == null ? {remove(): void {}} - : RCTDeviceEventEmitter.addListener(deviceEventName, handler); + : // $FlowFixMe[incompatible-call] + RCTDeviceEventEmitter.addListener(deviceEventName, handler); }, /** @@ -315,7 +347,7 @@ const AccessibilityInfo = { /** * Send a named accessibility event to a HostComponent. */ - sendAccessibilityEvent_unstable( + sendAccessibilityEvent( handle: ElementRef>, eventType: AccessibilityEventTypes, ) { diff --git a/Libraries/Components/AccessibilityInfo/NativeAccessibilityInfo.js b/Libraries/Components/AccessibilityInfo/NativeAccessibilityInfo.js index beee9e285175..e4661589749a 100644 --- a/Libraries/Components/AccessibilityInfo/NativeAccessibilityInfo.js +++ b/Libraries/Components/AccessibilityInfo/NativeAccessibilityInfo.js @@ -9,6 +9,7 @@ */ import type {TurboModule} from '../../TurboModule/RCTExport'; + import * as TurboModuleRegistry from '../../TurboModule/TurboModuleRegistry'; export interface Spec extends TurboModule { diff --git a/Libraries/Components/AccessibilityInfo/NativeAccessibilityManager.js b/Libraries/Components/AccessibilityInfo/NativeAccessibilityManager.js index d4c400ac3e99..f815b18c97ba 100644 --- a/Libraries/Components/AccessibilityInfo/NativeAccessibilityManager.js +++ b/Libraries/Components/AccessibilityInfo/NativeAccessibilityManager.js @@ -9,6 +9,7 @@ */ import type {TurboModule} from '../../TurboModule/RCTExport'; + import * as TurboModuleRegistry from '../../TurboModule/TurboModuleRegistry'; export interface Spec extends TurboModule { @@ -28,6 +29,10 @@ export interface Spec extends TurboModule { onSuccess: (isReduceMotionEnabled: boolean) => void, onError: (error: Object) => void, ) => void; + +getCurrentPrefersCrossFadeTransitionsState?: ( + onSuccess: (prefersCrossFadeTransitions: boolean) => void, + onError: (error: Object) => void, + ) => void; +getCurrentReduceTransparencyState: ( onSuccess: (isReduceTransparencyEnabled: boolean) => void, onError: (error: Object) => void, diff --git a/Libraries/Components/ActivityIndicator/ActivityIndicator.d.ts b/Libraries/Components/ActivityIndicator/ActivityIndicator.d.ts new file mode 100644 index 000000000000..f1a4b246dd44 --- /dev/null +++ b/Libraries/Components/ActivityIndicator/ActivityIndicator.d.ts @@ -0,0 +1,86 @@ +/** + * 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. + * + * @format + */ + +import type * as React from 'react'; +import {Constructor} from '../../../types/private/Utilities'; +import {NativeMethods} from '../../../types/public/ReactNativeTypes'; +import {ColorValue, StyleProp} from '../../StyleSheet/StyleSheet'; +import {ViewStyle} from '../../StyleSheet/StyleSheetTypes'; +import {LayoutChangeEvent} from '../../Types/CoreEventTypes'; +import {ViewProps} from '../View/ViewPropTypes'; + +/** + * @see https://reactnative.dev/docs/activityindicator#props + */ +export interface ActivityIndicatorProps extends ViewProps { + /** + * Whether to show the indicator (true, the default) or hide it (false). + */ + animating?: boolean | undefined; + + /** + * The foreground color of the spinner (default is gray). + */ + color?: ColorValue | undefined; + + /** + * Whether the indicator should hide when not animating (true by default). + */ + hidesWhenStopped?: boolean | undefined; + + /** + * Size of the indicator. + * Small has a height of 20, large has a height of 36. + * + * enum('small', 'large') + */ + size?: number | 'small' | 'large' | undefined; + + style?: StyleProp | undefined; +} + +declare class ActivityIndicatorComponent extends React.Component {} +declare const ActivityIndicatorBase: Constructor & + typeof ActivityIndicatorComponent; +export class ActivityIndicator extends ActivityIndicatorBase {} + +/** + * @see https://reactnative.dev/docs/activityindicatorios#props + */ +export interface ActivityIndicatorIOSProps extends ViewProps { + /** + * Whether to show the indicator (true, the default) or hide it (false). + */ + animating?: boolean | undefined; + + /** + * The foreground color of the spinner (default is gray). + */ + color?: ColorValue | undefined; + + /** + * Whether the indicator should hide when not animating (true by default). + */ + hidesWhenStopped?: boolean | undefined; + + /** + * Invoked on mount and layout changes with + */ + onLayout?: ((event: LayoutChangeEvent) => void) | undefined; + + /** + * Size of the indicator. + * Small has a height of 20, large has a height of 36. + * + * enum('small', 'large') + */ + size?: 'small' | 'large' | undefined; + + style?: StyleProp | undefined; +} diff --git a/Libraries/Components/ActivityIndicator/ActivityIndicator.flow.js b/Libraries/Components/ActivityIndicator/ActivityIndicator.flow.js new file mode 100644 index 000000000000..ebfb8272cb25 --- /dev/null +++ b/Libraries/Components/ActivityIndicator/ActivityIndicator.flow.js @@ -0,0 +1,58 @@ +/** + * 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. + * + * @format + * @flow + */ + +'use strict'; +import type {HostComponent} from '../../Renderer/shims/ReactNativeTypes'; +import type {ViewProps} from '../View/ViewPropTypes'; + +import {type ColorValue} from '../../StyleSheet/StyleSheet'; +import * as React from 'react'; + +type IndicatorSize = number | 'small' | 'large'; + +type IOSProps = $ReadOnly<{| + /** + Whether the indicator should hide when not animating. + + @platform ios + */ + hidesWhenStopped?: ?boolean, +|}>; + +type Props = $ReadOnly<{| + ...ViewProps, + ...IOSProps, + + /** + Whether to show the indicator (`true`) or hide it (`false`). + */ + animating?: ?boolean, + + /** + The foreground color of the spinner. + + @default {@platform android} `null` (system accent default color) + @default {@platform ios} '#999999' + */ + color?: ?ColorValue, + + /** + Size of the indicator. + + @type enum(`'small'`, `'large'`) + @type {@platform android} number + */ + size?: ?IndicatorSize, +|}>; + +export type ActivityIndicator = React.AbstractComponent< + Props, + HostComponent, +>; diff --git a/Libraries/Components/ActivityIndicator/ActivityIndicator.js b/Libraries/Components/ActivityIndicator/ActivityIndicator.js index eb8b733e15a2..0b57dc1273bd 100644 --- a/Libraries/Components/ActivityIndicator/ActivityIndicator.js +++ b/Libraries/Components/ActivityIndicator/ActivityIndicator.js @@ -10,12 +10,13 @@ */ 'use strict'; -import * as React from 'react'; -import Platform from '../../Utilities/Platform'; +import type {ViewProps} from '../View/ViewPropTypes'; +import type {ActivityIndicator as ActivityIndicatorType} from './ActivityIndicator.flow'; + import StyleSheet, {type ColorValue} from '../../StyleSheet/StyleSheet'; +import Platform from '../../Utilities/Platform'; import View from '../View/View'; -import type {HostComponent} from '../../Renderer/shims/ReactNativeTypes'; -import type {ViewProps} from '../View/ViewPropTypes'; +import * as React from 'react'; const PlatformActivityIndicator = Platform.OS === 'android' @@ -183,10 +184,8 @@ const ActivityIndicator = ( ``` */ -const ActivityIndicatorWithRef: React.AbstractComponent< - Props, - HostComponent, -> = React.forwardRef(ActivityIndicator); +const ActivityIndicatorWithRef: ActivityIndicatorType = + React.forwardRef(ActivityIndicator); ActivityIndicatorWithRef.displayName = 'ActivityIndicator'; const styles = StyleSheet.create({ diff --git a/Libraries/Components/ActivityIndicator/ActivityIndicatorViewNativeComponent.js b/Libraries/Components/ActivityIndicator/ActivityIndicatorViewNativeComponent.js index 548069e885d6..7394582902dc 100644 --- a/Libraries/Components/ActivityIndicator/ActivityIndicatorViewNativeComponent.js +++ b/Libraries/Components/ActivityIndicator/ActivityIndicatorViewNativeComponent.js @@ -8,13 +8,12 @@ * @flow strict-local */ -import type {WithDefault} from '../../Types/CodegenTypes'; - +import type {HostComponent} from '../../Renderer/shims/ReactNativeTypes'; import type {ColorValue} from '../../StyleSheet/StyleSheet'; +import type {WithDefault} from '../../Types/CodegenTypes'; import type {ViewProps} from '../View/ViewPropTypes'; import codegenNativeComponent from '../../Utilities/codegenNativeComponent'; -import type {HostComponent} from '../../Renderer/shims/ReactNativeTypes'; type NativeProps = $ReadOnly<{| ...ViewProps, diff --git a/Libraries/Components/ActivityIndicator/__tests__/ActivityIndicator-test.js b/Libraries/Components/ActivityIndicator/__tests__/ActivityIndicator-test.js index 91cb7bd6e292..1c1338618976 100644 --- a/Libraries/Components/ActivityIndicator/__tests__/ActivityIndicator-test.js +++ b/Libraries/Components/ActivityIndicator/__tests__/ActivityIndicator-test.js @@ -4,17 +4,16 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @format - * @emails oncall+react_native * @flow strict-local + * @format + * @oncall react_native */ 'use strict'; -const React = require('react'); -const ActivityIndicator = require('../ActivityIndicator'); - const ReactNativeTestTools = require('../../../Utilities/ReactNativeTestTools'); +const ActivityIndicator = require('../ActivityIndicator'); +const React = require('react'); describe('', () => { it('should set displayName to prevent regressions', () => { diff --git a/Libraries/Components/Button.d.ts b/Libraries/Components/Button.d.ts new file mode 100644 index 000000000000..706307156b85 --- /dev/null +++ b/Libraries/Components/Button.d.ts @@ -0,0 +1,42 @@ +/** + * 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. + * + * @format + */ + +import type * as React from 'react'; +import {ColorValue} from '../StyleSheet/StyleSheet'; +import {TouchableNativeFeedbackProps} from './Touchable/TouchableNativeFeedback'; +import {TouchableOpacityProps} from './Touchable/TouchableOpacity'; + +export interface ButtonProps + extends Pick< + TouchableNativeFeedbackProps & TouchableOpacityProps, + | 'accessibilityLabel' + | 'accessibilityState' + | 'hasTVPreferredFocus' + | 'nextFocusDown' + | 'nextFocusForward' + | 'nextFocusLeft' + | 'nextFocusRight' + | 'nextFocusUp' + | 'testID' + | 'disabled' + | 'onPress' + | 'touchSoundDisabled' + > { + /** + * Text to display inside the button. On Android the given title will be converted to the uppercased form. + */ + title: string; + + /** + * Color of the text (iOS), or background color of the button (Android). + */ + color?: ColorValue | undefined; +} + +export class Button extends React.Component {} diff --git a/Libraries/Components/Button.flow.js b/Libraries/Components/Button.flow.js new file mode 100644 index 000000000000..777c546508d7 --- /dev/null +++ b/Libraries/Components/Button.flow.js @@ -0,0 +1,265 @@ +/** + * 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. + * + * @format + * @flow + * @generate-docs + */ + +'use strict'; + +import type {PressEvent} from '../Types/CoreEventTypes'; +import type { + AccessibilityActionEvent, + AccessibilityActionInfo, + AccessibilityState, +} from './View/ViewAccessibility'; + +import {type ColorValue} from '../StyleSheet/StyleSheet'; +import * as React from 'react'; + +type ButtonProps = $ReadOnly<{| + /** + Text to display inside the button. On Android the given title will be + converted to the uppercased form. + */ + title: string, + + /** + Handler to be called when the user taps the button. The first function + argument is an event in form of [PressEvent](pressevent). + */ + onPress: (event?: PressEvent) => mixed, + + /** + If `true`, doesn't play system sound on touch. + + @platform android + + @default false + */ + touchSoundDisabled?: ?boolean, + + /** + Color of the text (iOS), or background color of the button (Android). + + @default {@platform android} '#2196F3' + @default {@platform ios} '#007AFF' + */ + color?: ?ColorValue, + + /** + TV preferred focus. + + @platform tv + + @default false + */ + hasTVPreferredFocus?: ?boolean, + + /** + Designates the next view to receive focus when the user navigates down. See + the [Android documentation][android:nextFocusDown]. + + [android:nextFocusDown]: + https://developer.android.com/reference/android/view/View.html#attr_android:nextFocusDown + + @platform android, tv + */ + nextFocusDown?: ?number, + + /** + Designates the next view to receive focus when the user navigates forward. + See the [Android documentation][android:nextFocusForward]. + + [android:nextFocusForward]: + https://developer.android.com/reference/android/view/View.html#attr_android:nextFocusForward + + @platform android, tv + */ + nextFocusForward?: ?number, + + /** + Designates the next view to receive focus when the user navigates left. See + the [Android documentation][android:nextFocusLeft]. + + [android:nextFocusLeft]: + https://developer.android.com/reference/android/view/View.html#attr_android:nextFocusLeft + + @platform android, tv + */ + nextFocusLeft?: ?number, + + /** + Designates the next view to receive focus when the user navigates right. See + the [Android documentation][android:nextFocusRight]. + + [android:nextFocusRight]: + https://developer.android.com/reference/android/view/View.html#attr_android:nextFocusRight + + @platform android, tv + */ + nextFocusRight?: ?number, + + /** + Designates the next view to receive focus when the user navigates up. See + the [Android documentation][android:nextFocusUp]. + + [android:nextFocusUp]: + https://developer.android.com/reference/android/view/View.html#attr_android:nextFocusUp + + @platform android, tv + */ + nextFocusUp?: ?number, + + /** + Text to display for blindness accessibility features. + */ + accessibilityLabel?: ?string, + /** + * Alias for accessibilityLabel https://reactnative.dev/docs/view#accessibilitylabel + * https://github.com/facebook/react-native/issues/34424 + */ + 'aria-label'?: ?string, + /** + If `true`, disable all interactions for this component. + + @default false + */ + disabled?: ?boolean, + + /** + Used to locate this view in end-to-end tests. + */ + testID?: ?string, + + /** + * Accessibility props. + */ + accessible?: ?boolean, + accessibilityActions?: ?$ReadOnlyArray, + onAccessibilityAction?: ?(event: AccessibilityActionEvent) => mixed, + accessibilityState?: ?AccessibilityState, + + /** + * [Android] Controlling if a view fires accessibility events and if it is reported to accessibility services. + */ + importantForAccessibility?: ?('auto' | 'yes' | 'no' | 'no-hide-descendants'), + accessibilityHint?: ?string, + accessibilityLanguage?: ?Stringish, +|}>; + +/** + A basic button component that should render nicely on any platform. Supports a + minimal level of customization. + + If this button doesn't look right for your app, you can build your own button + using [TouchableOpacity](touchableopacity) or + [TouchableWithoutFeedback](touchablewithoutfeedback). For inspiration, look at + the [source code for this button component][button:source]. Or, take a look at + the [wide variety of button components built by the community] + [button:examples]. + + [button:source]: + https://github.com/facebook/react-native/blob/HEAD/Libraries/Components/Button.js + + [button:examples]: + https://js.coach/?menu%5Bcollections%5D=React%20Native&page=1&query=button + + ```jsx +