Compare commits

..
38 Commits
Author SHA1 Message Date
grabbou 3293792b1f [0.61.0] Bump version numbers 2019-09-24 21:27:29 +02:00
grabbou 6830ec87ff Revert "Update Gradle wrapper to 5.6 (#26079)"
This reverts commit afd35296f2.
2019-09-24 21:05:26 +02:00
grabbou 8750eb2538 Revert "bump android gradle plugin to 3.5.0 (#26129)"
This reverts commit 8d4e8a3775.
2019-09-24 20:55:02 +02:00
Frieder Bluemle afd35296f2 Update Gradle wrapper to 5.6 (#26079)
Summary:
```
Welcome to Gradle 5.6!

Here are the highlights of this release:
 - Incremental Groovy compilation
 - Groovy compile avoidance
 - Test fixtures for Java projects
 - Manage plugin versions via settings script

For more details see https://docs.gradle.org/5.6/release-notes.html
```

## Changelog

[Android] [Changed] - Gradle wrapper 5.6
Pull Request resolved: https://github.com/facebook/react-native/pull/26079

Test Plan: Ran build and tests locally.

Differential Revision: D17054310

Pulled By: mdvacca

fbshipit-source-id: de7ba3a6d04058e51b8bc6a21d5a3f828ef8bc25
2019-09-24 20:41:49 +02:00
Dulmandakh 8d4e8a3775 bump android gradle plugin to 3.5.0 (#26129)
Summary:
Android Gradle Plugin 3.5.0 released with a lot of improvements and bug fixes. It's important to have this change merged before 0.61 release. See https://developer.android.com/studio/releases/gradle-plugin

## Changelog

[Android] [Changed] - bump android gradle plugin to 3.5.0
Pull Request resolved: https://github.com/facebook/react-native/pull/26129

Test Plan: RNTester builds and runs as expected

Reviewed By: mdvacca

Differential Revision: D17091520

Pulled By: osdnk

fbshipit-source-id: 232b9209526e62a7344d74422fd8471a03dec7f4
2019-09-24 20:41:43 +02:00
Alexander Kawrykow 245ac945fb React Partial Sync
Summary:
Base: 85d05b3a4d439c504ee43652d586ee253a01faf6

Author: Dan Abramov <dan.abramov@gmail.com>
Date:   Wed Sep 18 16:32:11 2019 +0100

    [Fresh] Always remount classes (#16823)

commit acfdc9760b4dc55d192b08de42b2c45e5e5bb531
Author: Ricky <rickhanlonii@gmail.com>
Date:   Wed Sep 18 15:31:00 2019 +0100

    [React Native] Fix for view config registrations (#16821)

commit dbe593d96c35b33fcc1f1bec70af86c24779392b
Author: Dominic Gannaway <trueadm@users.noreply.github.com>
Date:   Wed Sep 18 14:18:32 2019 +0200

    [react-core] Do not null fiber.sibling in detachFiber (#16820)

commit 30fa2f5ea3313b4b7932783d8ac71b5d59b8a8c7
Author: Dominic Gannaway <trueadm@users.noreply.github.com>
Date:   Tue Sep 17 17:30:22 2019 +0200

    [react-core] Clear more properties in detachFiber (#16807)

commit ea374e751b164ed029b35063b84c825305024a0a
Author: Dan Abramov <dan.abramov@gmail.com>
Date:   Wed Aug 28 16:55:56 2019 +0100

    Don't ignore dependencies for render phase update (#16574)

Reviewed By: gaearon

Differential Revision: D17456249

fbshipit-source-id: 87bad63fed4a571f65592ee904606828e3aa39af
2019-09-24 20:41:23 +02:00
sunnylqm 06c64f5f1b Bump hermes to v0.2.1 (#26451)
Summary:
Bump hermes to v0.2.1

allow-large-files

## Changelog

See https://github.com/facebook/hermes/releases/tag/v0.2.1

[Android] [Changed] - Bump hermes to v0.2.1
Pull Request resolved: https://github.com/facebook/react-native/pull/26451

Test Plan: RNTester builds and runs as expected

Differential Revision: D17394921

Pulled By: cpojer

fbshipit-source-id: 07ce5da3517b7aa24bfb5e1f6eefed6cdc9f5cb5
2019-09-19 09:38:41 +02:00
grabbou a5353c0361 Revert "Fix onDismiss in Modal"
This reverts commit bd2b7d6c03.
2019-09-19 09:36:45 +02:00
REDMOND\acoates 930ffa28a3 Fix build break in MSVC (#26462)
Summary:
Merging react-native-windows to 0.60 - the visual studio compiler seems to take issue with the existing code

## Changelog

[Internal] [Fixed] - Build fix for react-native-windows (MSVC)
Pull Request resolved: https://github.com/facebook/react-native/pull/26462

Test Plan:
No real change, just making compilers happy.

### Side Note
We'll want this change cherry-pickered to RN 0.60 and RN 0.61 so users of react-native-windows dont have to use our fork of react-native.

Reviewed By: mhorowitz

Differential Revision: D17406081

Pulled By: TheSavior

fbshipit-source-id: bc056e1a545c6478fdcbd5645f3a8dea657162c8
2019-09-19 09:31:21 +02:00
Gabriel Furini e0d9ceaa79 Fixing Yoga imports and includes on case-sensitive machines (#26416)
Summary:
In addition of the issue resolved by https://github.com/facebook/react-native/pull/26360 (already merged), machines with case-sensitive disks are still not able to build a project on Xcode due not found `<yoga/...` imports:

```
'yoga/Yoga.h' file not found
```

![Screen Shot 2019-09-12 at 11 15 54](https://user-images.githubusercontent.com/1728387/64791885-c58c9180-d54e-11e9-95cb-40befaab7acc.png)

## Changelog

[iOS] [Fixed] - Fix Yoga imports and includes
Pull Request resolved: https://github.com/facebook/react-native/pull/26416

Test Plan: `Build` command on Xcode now runs successfully in a Mac with disk in case-sensitive mode

Differential Revision: D17370392

Pulled By: PeteTheHeat

fbshipit-source-id: 2a225f47046113267adb154a4c6a9ef4664a12c3
2019-09-19 09:31:05 +02:00
grabbou fc6580e2e0 [0.61.0-rc.3] Bump version numbers 2019-09-10 19:01:50 +02:00
grabbou a648a7176a Rename Yoga 2019-09-10 13:44:11 +02:00
Max Thirouin cf5ca2659a Fix incorrect module.name_mapper in template .flowconfig (#26330)
Summary:
Has explained in https://github.com/facebook/react-native/issues/26233, current template is incorrect & can create error, like having require() of png that are considered as `string` instead of `number. This can probably hide tons of similar mistakes.

The change in this PR should resolve the previous behavior (& for example, some places in previous version of the flowconfig have the full path like here https://github.com/facebook/react-native/blob/35300147ca66677f42e8544264be72ac0e9d1b45/template/_flowconfig#L61)

Closes https://github.com/facebook/react-native/issues/26233

## Changelog

```
[General] [Fixed] Fix incorrect `module.name_mapper` in template .flowconfig
```

Alternatively, message could be

```
[General] [Internal] Fix incorrect `module.name_mapper` in template .flowconfig
```

As it hasn't this "bug" hasn't been released in a public stable release. You decide
Pull Request resolved: https://github.com/facebook/react-native/pull/26330

Test Plan: I just tested this in my project, thymikee might be able to confirm & approve this PR.

Differential Revision: D17258891

Pulled By: cpojer

fbshipit-source-id: 3904ffbc6f076ee0e435311249d694b8604fc7b8
2019-09-10 13:37:34 +02:00
James Treanor 231581c256 Add missing dependencies to React-CoreModules.podspec 2019-09-10 13:36:41 +02:00
James Treanor 2ec83bec5f Revert "Revert "Remove 's.static_framework = true' requirement for podspec (#25816)""
This reverts commit 612c033918.
2019-09-10 13:36:24 +02:00
Kevin Gozali 50d9551f87 TM iOS: Disable in test environment
Summary:
For now, disable TM completely in test environment, like RNTester integration/unit tests. See details in T53341772

This also fixes the failure discussed in https://github.com/facebook/react-native/pull/26151

Reviewed By: PeteTheHeat

Differential Revision: D17147915

fbshipit-source-id: 1c48ebb9c3b81fc08bc33606dcc38c29297d6010
2019-09-10 13:33:40 +02:00
grabbou 0356b1fa13 [0.61.0-rc.2] Bump version numbers 2019-09-04 17:55:42 +02:00
grabbou b153a05e2f Update release script 2019-09-04 17:55:17 +02:00
grabbou dcf56a43fa Sync Podfile 2019-09-04 17:54:59 +02:00
Ram N e0e9a1cb4f Update the version of yoga podspec to match the actual version of Yoga published
Summary: Yoga is currently published in cocoapods. While we don't use the Yoga from Cocoapods in React Native, we should atleast try to follow that version, so that other integrations with Yoga are possible

Reviewed By: shergin

Differential Revision: D17127625

fbshipit-source-id: bca2e1e33ad775e2a0d7a6f1e4177c3b480c023a
2019-09-04 17:32:51 +02:00
Ram N 8d02e1723f Change podspec name of yoga to Yoga
Summary:
Needed to capitalize the name, since this is the convention used elsewhere too

[iOS] [Changed] - Renamed yoga podspec to Yoga

Reviewed By: shergin

Differential Revision: D17127104

fbshipit-source-id: 14047bf452edda000037701f4ba7f4a02a0e717b
2019-09-04 17:32:15 +02:00
Dan Abramov eb0216d8be Partial RN sync
Summary:
This cherry-picks one commit: https://github.com/facebook/react/commit/01fb68b9bf680ab8bbf96e86501e0fc540b3cc97

It fixes a bug in Fast Refresh.

Reviewed By: threepointone

Differential Revision: D17140543

fbshipit-source-id: a7654152d1cc7c27e7c4024380349b44ac496b22
2019-09-04 17:20:31 +02:00
Dan Abramov c0fa590a03 Fix Redbox on iOS
Summary:
Looks like we broke iOS redbox in D16812212. It stopped showing up because the feature detection stopped working, and we started calling noops. The fix is an explicit platform check.

Fixes #26260

Reviewed By: motiz88

Differential Revision: D17139310

fbshipit-source-id: 829eec23cbb49151ac250889c34ab28d36b05e6a
2019-09-04 17:20:17 +02:00
SachinTotale ba5222f3cc Memory Leak due to JSStringRelease not called in multiple places in JSCRuntime.cpp (#25884)
Summary:
Memory Leak due to JSStringRelease not called in multiple places in JSCRuntime.cpp
Issue: https://github.com/facebook/react-native/issues/25664
Reproducible repo: https://github.com/bhandarijiwan/memory_issue_repro

## Changelog
[JSC] [JSCRuntime.cpp] - Added missing JSStringRelease calls in missing places
Pull Request resolved: https://github.com/facebook/react-native/pull/25884

Test Plan: Tested that is no memory leak with various NativeModule to JS call flows

Reviewed By: JoshuaGross

Differential Revision: D16928985

Pulled By: TheSavior

fbshipit-source-id: 65ce15ae32482d0db39bad7e22a2fed9ee04f230
2019-09-04 17:11:16 +02:00
grabbou c57bfea01e [0.61.0-rc.0] Bump version numbers 2019-08-27 16:20:02 +02:00
Mike Grabowski fafc58b62d Update CircleCI config as per support request 2019-08-27 16:18:55 +02:00
grabbou eded25d532 Revert "[0.61.0-rc.0] Bump version numbers"
This reverts commit bc28eee87f.
2019-08-27 16:16:31 +02:00
grabbou bc28eee87f [0.61.0-rc.0] Bump version numbers 2019-08-26 16:29:47 +02:00
Mehdi Mulani 104b0cc862 Remove RCTUIImageViewAnimated WeakProxy gating
Summary:
To help determine how severe this issue is, put the fix behind a MC.
We will only pick the parent diff to the RC branch so that the fix immediately goes to master and we don't have to worry about fixing this any further.

Reviewed By: fkgozali

Differential Revision: D16940181

fbshipit-source-id: 91eb08181f82f51aea6a20b3fd489a33bdc0e424
2019-08-26 15:52:43 +02:00
Mehdi Mulani 6d6b532f8f Add RCTWeakProxy to properly deallocate RCTUIImageViewAnimated
Summary:
@public
CADisplayLink strongly holds onto its target, so you have to use a weak proxy object to pass the target into the CADisplayLink.

Previously we passed a weak-self point (i.e. weakSelf) but this did not have the intended effect, since the pointer to self would still be passed to CADisplayLink, and thus it would hold onto the RCTUIImageViewAnimated strongly.

So is weakSelf doing anything other than using self?
It is but it's very minor and not useful. In the case that the object got de-allocated between assigning self to weakSelf and creating the CADisplayLink, then we would pass a nil target. This is actually impossible though because we are running an instance method, so self is implicitly retained! So semantically it is something different but in practice it is the same as passing self through.

Notes:
* This system was added originally in https://github.com/facebook/react-native/pull/24822
* https://github.com/facebook/react-native/pull/25636 then "enabled" this system by deprecating existing approach

Reviewed By: fkgozali

Differential Revision: D16939869

fbshipit-source-id: 7a0e947896f23aa30ad074d1dcb4d4db7543e00a
2019-08-26 15:52:37 +02:00
grabbou 1dd1152025 Revert "[0.61.0-rc.0] Bump version numbers"
This reverts commit 2575eb318f.
2019-08-26 15:52:30 +02:00
grabbou 2575eb318f [0.61.0-rc.0] Bump version numbers 2019-08-22 12:40:24 +02:00
grabbou 1dd68819ab Revert "[0.61.0-rc.0] Bump version numbers"
This reverts commit 9296ab1a61.
2019-08-22 12:39:59 +02:00
grabbou 9296ab1a61 [0.61.0-rc.0] Bump version numbers 2019-08-22 12:21:46 +02:00
grabbou 612c033918 Revert "Remove 's.static_framework = true' requirement for podspec (#25816)"
This reverts commit ca9e108110.
2019-08-22 08:59:52 +02:00
grabbou f827ea90b0 Revert "Remove vendored proguard annotation (#26069)"
This reverts commit 35fc0add2d.
2019-08-21 14:40:10 +02:00
Mike Grabowski b356ac7769 Tweak script to work with no global CLI 2019-08-20 23:20:02 +02:00
Mike Grabowski 5485238bb4 Bump test version for cache issues 2019-08-20 23:18:17 +02:00
4806 changed files with 264759 additions and 376877 deletions
+47
View File
@@ -0,0 +1,47 @@
environment:
ANDROID_HOME: "C:\\android-sdk-windows"
ANDROID_NDK: "C:\\android-sdk-windows\\android-ndk-r19c"
ANDROID_BUILD_VERSION: 28
ANDROID_TOOLS_VERSION: 28.0.3
GRADLE_OPTS: -Dorg.gradle.daemon=false
SDK_TOOLS_URL: https://dl.google.com/android/repository/sdk-tools-windows-3859397.zip
NDK_TOOLS_URL: https://dl.google.com/android/repository/android-ndk-r19c-windows-x86_64.zip
matrix:
- nodejs_version: 8
- nodejs_version: 10
install:
# Install Android SDK Tools
- mkdir "%ANDROID_HOME%"
- appveyor DownloadFile "%SDK_TOOLS_URL%" -FileName "%TMP%/sdk-tools.zip"
- 7z x "%TMP%/sdk-tools.zip" -o"%ANDROID_HOME%" > nul
- set PATH=%PATH%;"%ANDROID_HOME%\tools\bin"
- yes 2> nul | sdkmanager --licenses > nul
- yes 2> nul | sdkmanager "system-images;android-19;google_apis;armeabi-v7a"
- yes 2> nul | sdkmanager "platforms;android-%ANDROID_BUILD_VERSION%"
- yes 2> nul | sdkmanager "build-tools;%ANDROID_TOOLS_VERSION%"
- yes 2> nul | sdkmanager "add-ons;addon-google_apis-google-23"
- yes 2> nul | sdkmanager "extras;android;m2repository"
- appveyor DownloadFile "%NDK_TOOLS_URL%" -FileName "%TMP%/ndk.zip"
- 7z x "%TMP%/ndk.zip" -o"%ANDROID_HOME%" > nul
- ps: Install-Product node $env:nodejs_version x64
- npx envinfo@latest
- appveyor-retry yarn install
build_script:
- yarn run flow-check-android
- yarn run flow-check-ios
- yarn run test
# - gradlew.bat RNTester:android:app:assembleRelease
cache:
- node_modules
- "%LOCALAPPDATA%/Yarn"
- "%USERPROFILE%/.gradle/caches"
- "%USERPROFILE%/.gradle/wrapper"
+3 -3
View File
@@ -1,13 +1,13 @@
[android]
target = android-30
target = android-28
[download]
max_number_of_retries = 3
[maven_repositories]
central = https://repo1.maven.org/maven2
google = https://maven.google.com/
google = https://dl.google.com/dl/android/maven2/
[alias]
rntester = //packages/rn-tester/android/app:app
rntester = //RNTester/android/app:app
+3 -5
View File
@@ -14,7 +14,7 @@
# and build a Android application that can be used to run the
# tests specified in the scripts/ directory.
#
FROM reactnativecommunity/react-native-android:4.0
FROM reactnativecommunity/react-native-android
LABEL Description="React Native Android Test Image"
LABEL maintainer="Héctor Ramos <hector@fb.com>"
@@ -25,14 +25,11 @@ ENV JAVA_TOOL_OPTIONS="-Dfile.encoding=UTF8"
ADD .buckconfig /app/.buckconfig
ADD .buckjavaargs /app/.buckjavaargs
ADD BUCK /app/BUCK
ADD Libraries /app/Libraries
ADD tools /app/tools
ADD ReactAndroid /app/ReactAndroid
ADD ReactCommon /app/ReactCommon
ADD React /app/React
ADD keystores /app/keystores
ADD packages/react-native-codegen /app/packages/react-native-codegen
ADD tools /app/tools
WORKDIR /app
@@ -52,3 +49,4 @@ RUN yarn
RUN ./gradlew :ReactAndroid:downloadBoost :ReactAndroid:downloadDoubleConversion :ReactAndroid:downloadFolly :ReactAndroid:downloadGlog
RUN ./gradlew :ReactAndroid:packageReactNdkLibsForBuck -Pjobs=1
@@ -1,8 +1,10 @@
#!/bin/bash
#
# Copyright (c) Facebook, Inc. and its affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
#
# for buck gen
mount -o remount,exec /dev/shm
@@ -10,7 +12,7 @@ mount -o remount,exec /dev/shm
AVD_UUID=$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 8 | head -n 1)
# create virtual device
echo no | android create avd -n "$AVD_UUID" -f -t android-21 --abi default/armeabi-v7a
echo no | android create avd -n "$AVD_UUID" -f -t android-19 --abi default/armeabi-v7a
# emulator setup
emulator64-arm -avd $AVD_UUID -no-skin -no-audio -no-window -no-boot-anim &
@@ -1,8 +1,10 @@
#!/bin/bash
#
# Copyright (c) Facebook, Inc. and its affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
#
# set default environment variables
UNIT_TESTS_BUILD_THREADS="${UNIT_TESTS_BUILD_THREADS:-1}"
@@ -1,8 +1,10 @@
#!/bin/bash
#
# Copyright (c) Facebook, Inc. and its affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
#
set -ex
@@ -67,6 +69,11 @@ while :; do
shift
;;
--tvos)
RUN_IOS=1
shift
;;
*)
break
esac
@@ -109,7 +116,7 @@ function e2e_suite() {
# create virtual device
if ! android list avd | grep "$AVD_UUID" > /dev/null; then
echo no | android create avd -n "$AVD_UUID" -f -t android-21 --abi default/armeabi-v7a
echo no | android create avd -n "$AVD_UUID" -f -t android-19 --abi default/armeabi-v7a
fi
# newline at end of adb devices call and first line is headers
@@ -194,7 +201,7 @@ function e2e_suite() {
return 1
fi
echo "Starting Metro"
echo "Starting packager server"
npm start >> /dev/null &
SERVER_PID=$!
sleep 15
@@ -1,8 +1,10 @@
#!/bin/bash
#
# Copyright (c) Facebook, Inc. and its affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
#
# shellcheck disable=SC1117
# Python script to run instrumentation tests, copied from https://github.com/circleci/circle-dummy-android
+199 -421
View File
@@ -1,12 +1,5 @@
version: 2.1
# -------------------------
# ORBS
# -------------------------
orbs:
win: circleci/windows@2.4.0
# -------------------------
# DEFAULTS
# -------------------------
@@ -14,29 +7,23 @@ defaults: &defaults
working_directory: ~/react-native
environment:
- GIT_COMMIT_DESC: git log --format=oneline -n 1 $CIRCLE_SHA1
# The public github tokens are publicly visible by design
- PUBLIC_PULLBOT_GITHUB_TOKEN_A: &github_pullbot_token_a "a6edf8e8d40ce4e8b11a"
- PUBLIC_PULLBOT_GITHUB_TOKEN_B: &github_pullbot_token_b "150e1341f4dd9c944d2a"
- PUBLIC_ANALYSISBOT_GITHUB_TOKEN_A: &github_analysisbot_token_a "312d354b5c36f082cfe9"
- PUBLIC_ANALYSISBOT_GITHUB_TOKEN_B: &github_analysisbot_token_b "07973d757026bdd9f196"
# -------------------------
# EXECUTORS
# -------------------------
executors:
node8:
<<: *defaults
docker:
- image: circleci/node:8
nodelts:
<<: *defaults
docker:
# Note: Version set separately for Windows builds, see below.
- image: circleci/node:14
nodeprevlts:
<<: *defaults
docker:
- image: circleci/node:12
- image: circleci/node:lts
reactnativeandroid:
<<: *defaults
docker:
- image: reactnativecommunity/react-native-android:4.0
- image: reactnativecommunity/react-native-android:2019-6-4
resource_class: "large"
environment:
- TERM: "dumb"
@@ -44,15 +31,10 @@ executors:
- _JAVA_OPTIONS: "-XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap"
- GRADLE_OPTS: '-Dorg.gradle.daemon=false -Dorg.gradle.jvmargs="-XX:+HeapDumpOnOutOfMemoryError"'
- BUILD_THREADS: 2
# Repeated here, as the environment key in this executor will overwrite the one in defaults
- PUBLIC_ANALYSISBOT_GITHUB_TOKEN_A: *github_analysisbot_token_a
- PUBLIC_ANALYSISBOT_GITHUB_TOKEN_B: *github_analysisbot_token_b
- PUBLIC_PULLBOT_GITHUB_TOKEN_A: *github_pullbot_token_a
- PUBLIC_PULLBOT_GITHUB_TOKEN_B: *github_pullbot_token_b
reactnativeios:
<<: *defaults
macos:
xcode: &_XCODE_VERSION "12.5.0"
xcode: "10.2.1"
# -------------------------
# COMMANDS
@@ -71,15 +53,16 @@ commands:
steps:
- run:
name: Initial Setup
command: mkdir -p ./reports/{buck,build,junit,outputs}
command: mkdir -p ~/reports/{buck,build,junit,outputs}
run_yarn:
steps:
- restore_cache:
keys:
- v4-yarn-cache-{{ arch }}-{{ checksum "yarn.lock" }}
- v3-yarn-cache-{{ arch }}-{{ checksum "yarn.lock" }}
- v3-yarn-cache-{{ arch }}
- run:
name: "Yarn: Install Dependencies"
name: Run Yarn
command: |
# Skip yarn install on metro bump commits as the package is not yet
# available on npm
@@ -89,13 +72,14 @@ commands:
- save_cache:
paths:
- ~/.cache/yarn
key: v4-yarn-cache-{{ arch }}-{{ checksum "yarn.lock" }}
key: v3-yarn-cache-{{ arch }}-{{ checksum "yarn.lock" }}
install_buck_tooling:
steps:
- restore_cache:
keys:
- v3-buck-v2019.01.10.01-{{ checksum "scripts/circleci/buck_fetch.sh" }}}
- v3-buck-v2019.01.10.01-
- run:
name: Install BUCK
command: |
@@ -110,21 +94,13 @@ commands:
- ~/okbuck
key: v3-buck-v2019.01.10.01-{{ checksum "scripts/circleci/buck_fetch.sh" }}
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:
description: Homebrew package to install
type: string
steps:
- run:
name: "Brew: Install << parameters.package >>"
command: HOMEBREW_NO_AUTO_UPDATE=1 brew install << parameters.package >> >/dev/null
- run: HOMEBREW_NO_AUTO_UPDATE=1 brew install << parameters.package >> >/dev/null
with_brew_cache_span:
parameters:
@@ -133,39 +109,39 @@ commands:
steps:
- restore_cache:
keys:
- v4-brew
- v1-brew
- steps: << parameters.steps >>
- save_cache:
paths:
- /usr/local/Homebrew
- ~/Library/Caches/Homebrew
key: v4-brew
key: v1-brew
with_rntester_pods_cache_span:
with_pods_cache_span:
parameters:
steps:
type: steps
steps:
- run:
name: Setup CocoaPods cache
# Copy packages/rn-tester/Podfile.lock since it can be changed by pod install
command: cp packages/rn-tester/Podfile.lock packages/rn-tester/Podfile.lock.bak
# Copy RNTester/Podfile.lock since it can be changed by pod install
command: cp RNTester/Podfile.lock RNTester/Podfile.lock.bak
- restore_cache:
keys:
# The committed lockfile is generated using USE_FRAMEWORKS=0 and USE_HERMES=0 so it could load an outdated cache if a change
# only affects the frameworks or hermes config. To help prevent this also cache based on the content of Podfile.
- v3-pods-{{ .Environment.CIRCLE_JOB }}-{{ checksum "packages/rn-tester/Podfile.lock.bak" }}-{{ checksum "packages/rn-tester/Podfile" }}
- v1-pods-{{ .Environment.CIRCLE_JOB }}-{{ checksum "RNTester/Podfile.lock.bak" }}
- v1-pods-{{ .Environment.CIRCLE_JOB }}-
- steps: << parameters.steps >>
- save_cache:
paths:
- packages/rn-tester/Pods
key: v3-pods-{{ .Environment.CIRCLE_JOB }}-{{ checksum "packages/rn-tester/Podfile.lock.bak" }}-{{ checksum "packages/rn-tester/Podfile" }}
- RNTester/Pods
key: v1-pods-{{ .Environment.CIRCLE_JOB }}-{{ checksum "RNTester/Podfile.lock.bak" }}
download_gradle_dependencies:
steps:
- restore_cache:
keys:
- v1-gradle-{{ checksum "ReactAndroid/build.gradle" }}-{{ checksum "scripts/circleci/gradle_download_deps.sh" }}
- v1-gradle-
- run:
name: Download Dependencies Using Gradle
command: ./scripts/circleci/gradle_download_deps.sh
@@ -182,33 +158,24 @@ commands:
name: Download Dependencies Using Buck
command: ./scripts/circleci/buck_fetch.sh
run_e2e:
parameters:
platform:
description: Target platform
type: enum
enum: ["android", "ios", "js"]
default: "js"
retries:
description: How many times the job should try to run these tests
type: integer
default: 3
# -------------------------
# COMMANDS: Disabled Tests
# -------------------------
run_podspec_tests:
steps:
- run:
name: "Run Tests: << parameters.platform >> End-to-End Tests"
command: node ./scripts/run-ci-e2e-tests.js --<< parameters.platform >> --retries << parameters.retries >>
report_bundle_size:
parameters:
platform:
description: Target platform
type: enum
enum: ["android", "ios"]
name: Test CocoaPods
command: ./scripts/process-podspecs.sh
run_e2e_tests:
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 >>
name: Full End-to-End Test Suite
command: node ./scripts/run-ci-e2e-tests.js --android --ios --js --retries 3;
run_android_e2e_tests:
steps:
- run:
name: Android End-to-End Test Suite
command: node ./scripts/run-ci-e2e-tests.js --android --retries 3;
# -------------------------
# JOBS
@@ -216,12 +183,12 @@ commands:
jobs:
setup:
parameters:
executor:
type: executor
default: nodelts
checkout_type:
type: string
default: node
executor:
type: executor
default: node8
checkout_type:
type: string
default: node
executor: << parameters.executor >>
steps:
- checkout
@@ -237,20 +204,24 @@ jobs:
# Issues will be posted to the PR itself via GitHub bots.
# This workflow should only fail if the bots fail to run.
analyze_pr:
executor: reactnativeandroid
executor: nodelts
# The public github tokens are publicly visible by design
environment:
- PUBLIC_PULLBOT_GITHUB_TOKEN_A: "a6edf8e8d40ce4e8b11a"
- PUBLIC_PULLBOT_GITHUB_TOKEN_B: "150e1341f4dd9c944d2a"
- PUBLIC_ANALYSISBOT_GITHUB_TOKEN_A: "78a72af35445ca3f8180"
- PUBLIC_ANALYSISBOT_GITHUB_TOKEN_B: "b1a98e0bbd56ff1ccba1"
steps:
- restore_cache_checkout:
checkout_type: android
checkout_type: node
- 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:
name: Install additional GitHub bot dependencies
name: Install dependencies
command: |
curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add -
apt update && apt install -y shellcheck jq
sudo apt update && sudo apt install -y shellcheck jq
cd bots && yarn install --non-interactive --cache-folder ~/.cache/yarn
- run:
name: Run linters against modified files (analysis-bot)
@@ -264,25 +235,21 @@ jobs:
DANGER_GITHUB_API_TOKEN="$PUBLIC_PULLBOT_GITHUB_TOKEN_A""$PUBLIC_PULLBOT_GITHUB_TOKEN_B" yarn danger ci --use-github-checks
when: always
# -------------------------
# JOBS: Analyze Code
# -------------------------
analyze_code:
executor: reactnativeandroid
executor: node8
steps:
- restore_cache_checkout:
checkout_type: android
checkout_type: node
- setup_artifacts
- run_yarn
- run:
name: Lint code
command: scripts/circleci/exec_swallow_error.sh yarn lint --format junit -o ./reports/junit/eslint/results.xml
when: always
- run:
name: Lint Java
command: scripts/circleci/exec_swallow_error.sh yarn lint-java --check
command: scripts/circleci/exec_swallow_error.sh yarn lint --format junit -o ~/reports/junit/eslint/results.xml
when: always
- run:
@@ -308,73 +275,42 @@ jobs:
when: always
- store_test_results:
path: ./reports/junit
path: ~/reports/junit
# -------------------------
# JOBS: Test JavaScript
# -------------------------
# Runs JavaScript tests
test_js:
parameters:
executor:
type: executor
default: nodelts
run_disabled_tests:
type: boolean
default: false
default: node8
executor: << parameters.executor >>
steps:
- restore_cache_checkout:
checkout_type: node
- setup_artifacts
- run_yarn
- run:
name: Install rsync
command: sudo apt-get install rsync
# -------------------------
# Run JavaScript tests
- run:
name: "Run Tests: JavaScript Tests"
name: JavaScript Test Suite
command: node ./scripts/run-ci-javascript-tests.js --maxWorkers 2
- run_e2e:
platform: js
# Optionally, run disabled tests
- when:
condition: << parameters.run_disabled_tests >>
steps:
- run: echo "Failing tests may be moved here temporarily."
# -------------------------
- store_test_results:
path: ./reports/junit
path: ~/reports/junit
# -------------------------
# JOBS: Test iOS
# -------------------------
# Runs unit tests on iOS devices
test_ios:
executor: reactnativeios
parameters:
use_frameworks:
type: boolean
default: false
use_hermes:
type: boolean
default: false
run_unit_tests:
description: Specifies whether unit tests should run.
type: boolean
default: false
run_detox_tests:
description: Specifies whether Detox e2e tests should run.
type: boolean
default: false
run_disabled_tests:
description: Specifies whether disabled tests should run. Set this to true to debug failing tests.
type: boolean
default: false
environment:
- REPORTS_DIR: "./reports/junit"
- REPORTS_DIR: "./reports"
steps:
- restore_cache_checkout:
checkout_type: ios
@@ -382,33 +318,16 @@ jobs:
- run_yarn
- run: |
cd packages/rn-tester
cd RNTester
bundle check || bundle install
- run:
name: Boot iPhone Simulator
command: source scripts/.tests.env && xcrun simctl boot "$IOS_DEVICE" || true
- run:
name: Configure Environment Variables
name: Fetch CocoaPods Specs
command: |
echo 'export PATH=/usr/local/opt/node@12/bin:$PATH' >> $BASH_ENV
source $BASH_ENV
- with_brew_cache_span:
steps:
- brew_install:
package: watchman
- brew_install:
package: node@12
- run:
name: "Brew: Tap wix/brew"
command: HOMEBREW_NO_AUTO_UPDATE=1 brew tap wix/brew >/dev/null
- brew_install:
package: applesimutils
- run:
name: Configure Watchman
command: touch .watchmanconfig
curl https://cocoapods-specs.circleci.com/fetch-cocoapods-repo-from-s3.sh | bash -s cf
- when:
condition: << parameters.use_frameworks >>
@@ -417,73 +336,107 @@ jobs:
name: Set USE_FRAMEWORKS=1
command: echo "export USE_FRAMEWORKS=1" >> $BASH_ENV
- when:
condition: << parameters.use_hermes >>
steps:
- run:
name: Set USE_HERMES=1
command: echo "export USE_HERMES=1" >> $BASH_ENV
- run:
name: Setup the CocoaPods environment
command: pod setup
- with_rntester_pods_cache_span:
- with_pods_cache_span:
steps:
- run:
name: Generate RNTesterPods Workspace
command: cd packages/rn-tester && USE_FABRIC=1 bundle exec pod install --verbose
command: cd RNTester && pod install --verbose
# -------------------------
# Runs iOS unit tests
- when:
condition: << parameters.run_unit_tests >>
- with_brew_cache_span:
steps:
- run:
name: "Run Tests: iOS Unit and Integration Tests"
command: yarn test-ios
# Runs iOS Detox e2e tests
- when:
condition: << parameters.run_detox_tests >>
steps:
- run:
name: "Run Tests: Detox iOS End-to-End Tests"
command: yarn run build-ios-e2e && yarn run test-ios-e2e
- brew_install:
package: watchman
- run: touch .watchmanconfig
# Optionally, run disabled tests
- when:
condition: << parameters.run_disabled_tests >>
steps:
- run: echo "Failing tests may be moved here temporarily."
- run:
name: "Run Tests: CocoaPods"
command: ./scripts/process-podspecs.sh
- run:
name: Free up port 8081 for iOS End-to-End Tests
command: |
# free up port 8081 for the packager before running tests
set +eo pipefail
lsof -i tcp:8081 | awk 'NR!=1 {print $2}' | xargs kill
set -eo pipefail
- run_e2e:
platform: ios
# -------------------------
# Collect Results
- report_bundle_size:
platform: ios
- run: yarn test-ios
- store_test_results:
path: ./reports/junit
path: ~/reports/junit
# Runs iOS end-to-end tests
test_ios_e2e:
executor: reactnativeios
steps:
- restore_cache_checkout:
checkout_type: ios
- setup_artifacts
- run_yarn
- run:
name: Boot iPhone Simulator
command: source scripts/.tests.env && xcrun simctl boot "$IOS_DEVICE" || true
- run:
name: Configure Environment Variables
command: |
echo 'export PATH=/usr/local/opt/node@8/bin:$PATH' >> $BASH_ENV
source $BASH_ENV
# Brew
- with_brew_cache_span:
steps:
- brew_install:
package: node@8
- run: HOMEBREW_NO_AUTO_UPDATE=1 brew tap wix/brew >/dev/null
- brew_install:
package: applesimutils
- brew_install:
package: watchman
# Configure Watchman
- run: touch .watchmanconfig
- restore_cache:
keys:
- v1-cocoapods-{{ checksum "template/ios/Podfile" }}
- v1-cocoapods-
- run: pod setup
- run:
name: Generate RNTesterPods Workspace
command: pushd RNTester && pod install --verbose && popd
- run:
name: Run Detox iOS End-to-End Tests
command: yarn run build-ios-e2e && yarn run test-ios-e2e
when: always
- run:
name: Run iOS End-to-End Tests
command: |
# free up port 8081 for the packager before running tests
set +eo pipefail
lsof -i tcp:8081 | awk 'NR!=1 {print $2}' | xargs kill
set -eo pipefail
node ./scripts/run-ci-e2e-tests.js --ios --retries 3;
when: always
- save_cache:
paths:
- ~/.cocoapods/repos
key: v1-cocoapods-{{ checksum "template/ios/Podfile" }}
test_js_e2e:
executor: node8
steps:
- restore_cache_checkout:
checkout_type: node
- setup_artifacts
- run_yarn
- run: sudo apt-get install rsync
- run:
name: Run JavaScript End-to-End Tests
command: node ./scripts/run-ci-e2e-tests.js --js --retries 3
- store_test_results:
path: ~/reports/junit
# -------------------------
# JOBS: Test Android
# -------------------------
# Run Android tests
test_android:
executor: reactnativeandroid
parameters:
run_disabled_tests:
type: boolean
default: false
steps:
- restore_cache_checkout:
checkout_type: android
@@ -504,6 +457,8 @@ jobs:
command: source scripts/android-setup.sh && launchAVD
background: true
# Keep configuring Android dependencies while AVD boots up
# Install Buck
- install_buck_tooling
@@ -536,49 +491,37 @@ jobs:
name: Wait for Android Virtual Device
command: source scripts/android-setup.sh && waitForAVD
# Test Suite
- run:
name: Assemble RNTester App
command: ./gradlew packages:rn-tester:android:app:assembleRelease
name: Run Unit Tests
command: buck test ReactAndroid/src/test/... --config build.threads=$BUILD_THREADS --xml ~/reports/buck/all-results-raw.xml
# -------------------------
# Run Android tests
- run:
name: "Run Tests: Android Unit Tests"
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"
# Here, just build the instrumentation tests. There is a known issue with installing the APK to android-21+ emulator.
name: Run Instrumentation Tests
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
source scripts/android-setup.sh && NO_BUCKD=1 retry3 timeout 300 buck install ReactAndroid/src/androidTest/buck-runner:instrumentation-tests --config build.threads=$BUILD_THREADS
# Optionally, run disabled tests
- when:
condition: << parameters.run_disabled_tests >>
steps:
- run: echo "Failing tests may be moved here temporarily."
- run_e2e:
platform: android
# -------------------------
- run:
name: Build Android RNTester App
command: ./gradlew RNTester:android:app:assembleRelease
# 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/ \;
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
./tooling/junit/buck_to_junit.sh ~/react-native/reports/buck/all-results-raw.xml ~/react-native/reports/junit/all-results-junit.xml
fi
when: always
- store_test_results:
path: ./reports/junit
path: ~/reports/junit
# -------------------------
# JOBS: Test Docker
@@ -593,101 +536,16 @@ jobs:
source ~/.bashrc
nvm i node
npm i -g yarn
echo y | npx envinfo@latest
npx envinfo@latest
yarn run docker-setup-android
yarn run docker-build-android
# -------------------------
# JOBS: Windows
# -------------------------
test_windows:
executor:
name: win/default
parameters:
run_disabled_tests:
type: boolean
default: false
environment:
- ANDROID_HOME: "C:\\Android\\android-sdk"
- ANDROID_NDK: "C:\\Android\\android-sdk\\ndk\\20.1.5948944"
- ANDROID_BUILD_VERSION: 30
- ANDROID_TOOLS_VERSION: 30.0.2
- GRADLE_OPTS: -Dorg.gradle.daemon=false
- NDK_VERSION: 21.4.7075529
steps:
- checkout
- run:
name: Install Node
# Note: Version set separately for non-Windows builds, see above.
command: |
nvm install 14.17.0
nvm use 14.17.0
# Setup Dependencies
- run:
name: Install Yarn
command: choco install yarn
- run:
name: Display Environment info
command: npx envinfo@latest
- restore_cache:
keys:
- v1-win-yarn-cache-{{ arch }}-{{ checksum "yarn.lock" }}
- run:
name: "Yarn: Install Dependencies"
command: yarn install --frozen-lockfile --non-interactive
- save_cache:
key: v1-win-yarn-cache-{{ arch }}-{{ checksum "yarn.lock" }}
paths:
- C:\Users\circleci\AppData\Local\Yarn
# Try to install the SDK up to 3 times, since network flakiness can cause install failures
# Using a timeout of 9 mins, as circle ci will timeout if there is no output for 10 mins
- run:
name: Install Android SDK Tools
command: choco install android-sdk --timeout 540; if (!$?) { choco install android-sdk --timeout 540 --force --forcedependencies}; if (!$?) { choco install android-sdk --force --forcedependencies}
- run:
name: Setup Android SDKs
command: |
sdkmanager --licenses
sdkmanager "system-images;android-21;google_apis;armeabi-v7a"
sdkmanager "platforms;android-%ANDROID_BUILD_VERSION%"
sdkmanager "build-tools;%ANDROID_TOOLS_VERSION%"
sdkmanager "add-ons;addon-google_apis-google-23"
sdkmanager "extras;android;m2repository"
sdkmanager "ndk;%NDK_VERSION%"
# -------------------------
# Run Tests
- run:
name: "Flow: Check Android"
command: yarn flow-check-android
- run:
name: "Flow: Check iOS"
command: yarn flow-check-ios
- run:
name: "Run Tests: JavaScript Tests"
command: yarn test
# Optionally, run disabled tests
- when:
condition: << parameters.run_disabled_tests >>
steps:
- run: echo "Failing tests may be moved here temporarily."
- run:
name: Android Build
command: ./gradlew.bat packages:rn-tester:android:app:assembleRelease
# -------------------------
# JOBS: Coverage
# -------------------------
# Collect JavaScript test coverage
js_coverage:
executor: nodelts
executor: node8
environment:
- CI_BRANCH: $CIRCLE_BRANCH
- CI_PULL_REQUEST: $CIRCLE_PULL_REQUEST
@@ -699,31 +557,22 @@ jobs:
- setup_artifacts
- run_yarn
- run:
name: Collect test coverage information
name: Test coverage
command: |
scripts/circleci/exec_swallow_error.sh yarn test --coverage --maxWorkers=2
if [[ -e ./coverage/lcov.info ]]; then
cat ./coverage/lcov.info | scripts/circleci/exec_swallow_error.sh ./node_modules/.bin/coveralls
fi
yarn test --coverage --maxWorkers=2
cat ./coverage/lcov.info | ./node_modules/.bin/coveralls
when: always
- store_artifacts:
path: ~/react-native/coverage/
# -------------------------
# JOBS: Releases
# -------------------------
# Publishes a new version onto npm
# Publishes new version onto npm
# Only works on stable branches when a properly tagged commit is pushed
publish_npm_package:
parameters:
publish_npm_args:
type: string
default: --nonightly
executor: reactnativeandroid
steps:
- run:
name: Add github.com to SSH known hosts
command: |
mkdir -p ~/.ssh
echo '|1|If6MU203eXTaaWL678YEfWkVMrw=|kqLeIAyTy8pzpj8x8Ae4Fr8Mtlc= ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAq2A7hRGmdnm9tUDbO9IDSwBK6TbQa+PXYPCPy6rbTrTtw7PHkccKrpp0yVhp5HdEIcKr6pLlVDBfOLX9QUsyCOV0wzfjIJNlGEYsdlLJizHhbn2mUjvSAHQqZETYP81eFzLQNnPHt4EVVUh7VfDESU84KezmD5QlWpXLmvU31/yMf+Se8xhHTvKSCZIFImWwoG6mbUoWf9nzpIoaSjB+weqqUUmpaaasXVal72J+UX2B+2RPW3RcT0eOzQgqlJL3RKrTJvdsjE3JEAvGq3lGHSZXy28G3skua2SmVi/w4yCE6gbODqnTWlg7+wC604ydGXA8VJiS5ap43JXiUFFAaQ==' >> ~/.ssh/known_hosts
- restore_cache_checkout:
checkout_type: android
- run_yarn
@@ -735,25 +584,12 @@ jobs:
git config --global user.email "react-native-bot@users.noreply.github.com"
git config --global user.name "npm Deployment Script"
echo "machine github.com login react-native-bot password $GITHUB_TOKEN" > ~/.netrc
- run: node ./scripts/publish-npm.js << parameters.publish_npm_args >>
# -------------------------
# JOBS: Nightly
# -------------------------
nightly_job:
machine: true
steps:
- run:
name: Nightly
command: |
echo "Nightly build run"
- run: node ./scripts/publish-npm.js
# -------------------------
# WORK FLOWS
# -------------------------
workflows:
version: 2
tests:
jobs:
- setup:
@@ -776,67 +612,36 @@ workflows:
branches:
ignore: gh-pages
- test_js:
run_disabled_tests: false
requires:
- setup_js
- test_js_e2e:
requires:
- setup_js
- test_js
- test_android:
run_disabled_tests: false
requires:
- setup_android
- test_ios:
name: test_ios_unit_jsc
run_unit_tests: true
requires:
- setup_ios
# DISABLED: USE_FRAMEWORKS=1 not supported by Flipper
# - test_ios:
# name: test_ios_unit_frameworks_jsc
# use_frameworks: true
# run_unit_tests: true
# requires:
# - setup_ios
- test_ios:
name: test_ios_unit_hermes
use_hermes: true
run_unit_tests: true
name: test_ios_frameworks
use_frameworks: true
requires:
- setup_ios
# DISABLED: USE_FRAMEWORKS=1 not supported by Flipper
# - test_ios:
# name: test_ios_unit_frameworks_hermes
# use_hermes: true
# use_frameworks: true
# run_unit_tests: true
# requires:
# - setup_ios
# DISABLED: Detox tests need to be fixed
# - test_ios:
# name: test_ios_detox
# run_detox_tests: true
# requires:
# - setup_ios
# DISABLED: USE_FRAMEWORKS=1 not supported by Flipper
# - test_ios:
# name: test_ios_detox_frameworks
# use_frameworks: true
# run_detox_tests: true
# requires:
# - setup_ios
- test_ios_e2e:
requires:
- setup_ios
- test_js
- test_js:
name: test_js_prev_lts
executor: nodeprevlts
name: test_js_lts
executor: nodelts
requires:
- setup_js
- test_docker:
filters:
branches:
ignore: gh-pages
- test_windows:
filters:
branches:
ignore: gh-pages
run_disabled_tests: false
releases:
jobs:
- setup:
@@ -865,18 +670,11 @@ workflows:
analysis:
jobs:
- setup:
name: setup_js
- setup:
name: setup_android
checkout_type: android
executor: reactnativeandroid
- setup
# Run lints on every commit other than those to the gh-pages branch
- analyze_code:
requires:
- setup_android
- setup
filters:
branches:
ignore: gh-pages
@@ -884,7 +682,7 @@ workflows:
# Run code checks on PRs from forks
- analyze_pr:
requires:
- setup_android
- setup
filters:
branches:
only: /^pull\/.*$/
@@ -892,24 +690,4 @@ workflows:
# Gather coverage
- js_coverage:
requires:
- setup_js
nightly:
triggers:
- schedule:
cron: "0 0 * * *"
filters:
branches:
only:
- main
jobs:
- nightly_job
- setup:
name: setup_android
checkout_type: android
executor: reactnativeandroid
- publish_npm_package:
publish_npm_args: --nightly
requires:
- setup_android
- setup
-7
View File
@@ -13,12 +13,5 @@ indent_size = 2
[*.gradle]
indent_size = 4
[*.kts]
indent_size = 4
[BUCK]
indent_size = 4
# Windows files
[*.bat]
end_of_line = crlf
+8 -7
View File
@@ -1,10 +1,11 @@
**/main.js
# node_modules ignored by default
**/staticBundle.js
bots/node_modules
docs/generatedComponentApiDocs.js
flow/
Libraries/Renderer/*
**/main.js
Libraries/vendor/**/*
node_modules/
Libraries/Renderer/*
packages/*/node_modules
packages/react-native-codegen/lib
pr-inactivity-bookmarklet.js
question-bookmarklet.js
flow/
bots/node_modules
+7 -22
View File
@@ -5,28 +5,13 @@
"./packages/eslint-config-react-native-community/index.js"
],
"plugins": [
"@react-native/eslint-plugin-codegen"
],
"overrides": [
{
"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/codegen/react-native-modules": 2
}
},
{
"files": [
"flow-typed/**/*.js",
],
"rules": {
quotes: 0
rules: {
'@react-native-community/no-haste-imports': 2
}
},
{
@@ -35,7 +20,7 @@
"**/__mocks__/**/*.js",
"**/__tests__/**/*.js",
"jest/**/*.js",
"packages/rn-tester/**/*.js",
"RNTester/**/*.js",
],
"globals": {
// Expose some Jest globals for test helpers
@@ -53,8 +38,8 @@
],
"env": {
"jasmine": true,
"jest": true
}
}
]
"jest": true,
},
},
],
}
+17 -12
View File
@@ -11,6 +11,10 @@
; Ignore "BUCK" generated dirs
<PROJECT_ROOT>/\.buckd/
; These should not be required directly
; require from fbjs/lib instead: require('fbjs/lib/warning')
.*/node_modules/warning/.*
; Flow doesn't support platforms
.*/Libraries/Utilities/LoadingView.js
@@ -19,20 +23,15 @@
[include]
[declarations]
.*/node_modules/.*
[libs]
interface.js
Libraries/react-native/react-native-interface.js
flow/
[options]
emoji=true
exact_by_default=true
indexed_access=false
format.bracket_spacing=false
esproposal.optional_chaining=enable
esproposal.nullish_coalescing=enable
module.file_ext=.js
module.file_ext=.json
@@ -40,9 +39,9 @@ module.file_ext=.ios.js
munge_underscores=true
module.name_mapper='^react-native$' -> '<PROJECT_ROOT>/index.js'
module.name_mapper='^react-native$' -> '<PROJECT_ROOT>/Libraries/react-native/react-native-implementation'
module.name_mapper='^react-native/\(.*\)$' -> '<PROJECT_ROOT>/\1'
module.name_mapper='^@?[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\|pdf\)$' -> '<PROJECT_ROOT>/Libraries/Image/RelativeImageStub'
module.name_mapper='^[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\|pdf\)$' -> '<PROJECT_ROOT>/Libraries/Image/RelativeImageStub'
suppress_type=$FlowIssue
suppress_type=$FlowFixMe
@@ -50,16 +49,22 @@ suppress_type=$FlowFixMeProps
suppress_type=$FlowFixMeState
suppress_type=$FlowFixMeEmpty
suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(<VERSION>\\)? *\\(site=[a-z,_]*react_native\\(_ios\\)?_\\(oss\\|fb\\)[a-z,_]*\\)?)\\)
suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(<VERSION>\\)? *\\(site=[a-z,_]*react_native\\(_ios\\)?_\\(oss\\|fb\\)[a-z,_]*\\)?)\\)?:? #[0-9]+
suppress_comment=\\(.\\|\n\\)*\\$FlowExpectedError
[lints]
sketchy-null-number=warn
sketchy-null-mixed=warn
sketchy-number=warn
untyped-type-import=warn
nonstrict-import=warn
deprecated-type=error
deprecated-type=warn
unsafe-getters-setters=warn
inexact-spread=warn
unnecessary-invariant=warn
signature-verification-failure=warn
deprecated-utility=error
[strict]
deprecated-type
@@ -71,4 +76,4 @@ untyped-import
untyped-type-import
[version]
^0.158.0
^0.105.0
+17 -12
View File
@@ -11,6 +11,10 @@
; Ignore "BUCK" generated dirs
<PROJECT_ROOT>/\.buckd/
; These should not be required directly
; require from fbjs/lib instead: require('fbjs/lib/warning')
.*/node_modules/warning/.*
; Flow doesn't support platforms
.*/Libraries/Utilities/LoadingView.js
@@ -19,20 +23,15 @@
[include]
[declarations]
.*/node_modules/.*
[libs]
interface.js
Libraries/react-native/react-native-interface.js
flow/
[options]
emoji=true
exact_by_default=true
indexed_access=false
format.bracket_spacing=false
esproposal.optional_chaining=enable
esproposal.nullish_coalescing=enable
module.file_ext=.js
module.file_ext=.json
@@ -40,9 +39,9 @@ module.file_ext=.android.js
munge_underscores=true
module.name_mapper='^react-native$' -> '<PROJECT_ROOT>/index.js'
module.name_mapper='^react-native$' -> '<PROJECT_ROOT>/Libraries/react-native/react-native-implementation'
module.name_mapper='^react-native/\(.*\)$' -> '<PROJECT_ROOT>/\1'
module.name_mapper='^@?[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\|pdf\)$' -> '<PROJECT_ROOT>/Libraries/Image/RelativeImageStub'
module.name_mapper='^[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\|pdf\)$' -> '<PROJECT_ROOT>/Libraries/Image/RelativeImageStub'
suppress_type=$FlowIssue
suppress_type=$FlowFixMe
@@ -50,16 +49,22 @@ suppress_type=$FlowFixMeProps
suppress_type=$FlowFixMeState
suppress_type=$FlowFixMeEmpty
suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(<VERSION>\\)? *\\(site=[a-z,_]*react_native\\(_android\\)?_\\(oss\\|fb\\)[a-z,_]*\\)?)\\)
suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(<VERSION>\\)? *\\(site=[a-z,_]*react_native\\(_android\\)?_\\(oss\\|fb\\)[a-z,_]*\\)?)\\)?:? #[0-9]+
suppress_comment=\\(.\\|\n\\)*\\$FlowExpectedError
[lints]
sketchy-null-number=warn
sketchy-null-mixed=warn
sketchy-number=warn
untyped-type-import=warn
nonstrict-import=warn
deprecated-type=error
deprecated-type=warn
unsafe-getters-setters=warn
inexact-spread=warn
unnecessary-invariant=warn
signature-verification-failure=warn
deprecated-utility=error
[strict]
deprecated-type
@@ -71,4 +76,4 @@ untyped-import
untyped-type-import
[version]
^0.158.0
^0.105.0
+6 -3
View File
@@ -1,3 +1,6 @@
# Windows files should use crlf line endings
# https://help.github.com/articles/dealing-with-line-endings/
*.bat text eol=crlf
# Force LF line endings for Bash scripts. On Windows the rest of the source
# files will typically have CR+LF endings (Git default on Windows), but Bash
# scripts need to have LF endings to work (under Cygwin), thus override to force
# that.
gradlew text eol=lf
*.sh text eol=lf
+1 -4
View File
@@ -29,12 +29,9 @@ Libraries/NativeAnimation/* @janicduplessis
Libraries/Image/* @shergin
Libraries/Text/* @shergin
# Modifications to package.json typically require
# Modifications to package.json typically require
# additional effort from a Facebook employee to land
/package.json @hramos @cpojer
# These should not be modified through a GitHub PR
LICENSE* @hramos @cpojer @yungsters
# The eslint-config-react-native-community package requires manual publishing after merging
/packages/eslint-config-react-native-community/* @matt-oakes
+3 -3
View File
@@ -1,4 +1,4 @@
✋ To keep the backlog clean and actionable, issues will be
🚫 closed if they do not follow one of the issue templates:
👉 https://github.com/facebook/react-native/issues/new/choose
👉 Please follow one of these issue templates:
- https://github.com/facebook/react-native/issues/new/choose
Note: to keep the backlog clean and actionable, issues may be immediately closed if they do not follow one of the above issue templates.
+25 -15
View File
@@ -2,29 +2,39 @@
name: "🐛 Bug Report"
about: Report a reproducible bug or regression in React Native.
title: ''
labels: 'Needs: Triage :mag:'
labels: 'Bug'
---
Please provide all the information requested. Issues that do not follow this format are likely to stall.
<!--
Please provide a clear and concise description of what the bug is.
Include screenshots if needed.
Please test using the latest React Native release to make sure your issue has not already been fixed: http://facebook.github.io/react-native/docs/upgrading.html
-->
## Description
Please provide a clear and concise description of what the bug is. Include screenshots if needed.
Please test using the latest React Native release to make sure your issue has not already been fixed: https://reactnative.dev/docs/upgrading.html
## React Native version:
Run `react-native info` in your terminal and copy the results here.
React Native version:
<!--
Run `react-native info` in your terminal and copy the results here.
-->
## Steps To Reproduce
Provide a detailed list of steps that reproduce the issue.
1.
2.
## Expected Results
Describe what you expected to happen.
<!--
Issues without reproduction steps or code are likely to stall.
-->
Describe what you expected to happen:
Snack, code example, screenshot, or link to a repository:
<!--
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.
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. --
-->
## Snack, code example, screenshot, or link to a repository:
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.
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
-14
View File
@@ -1,14 +0,0 @@
blank_issues_enabled: false
contact_links:
- name: 📃 Documentation Issue
url: https://github.com/facebook/react-native-website/issues
about: Please report documentation issues in the React Native website repository.
- name: ⤴️ Upgrade Issue
url: https://github.com/react-native-community/upgrade-support
about: Need help upgrading to a newer React Native version? Visit the Upgrade Support repository.
- name: 🤔 Questions and Help
url: https://reactnative.dev/help
about: Looking for help with your app? Please refer to the React Native community's support resources.
- name: 🚀 Discussions and Proposals
url: https://github.com/react-native-community/discussions-and-proposals
about: Discuss the future of React Native in the React Native community's discussions and proposals repository.
+14
View File
@@ -0,0 +1,14 @@
---
name: "📃 Documentation Issue"
about: Documentation issues are handled in https://github.com/facebook/react-native-website.
title: 'Docs:'
labels: 'Type: Docs'
---
🚨 Please do not open a documentation issue in the core React Native repository. 🚨
The React Native website is hosted on a separate repository. You may let the
team know about any issues with the documentation by opening an issue there:
- https://github.com/facebook/react-native-website
- https://github.com/facebook/react-native-website/issues
+34
View File
@@ -0,0 +1,34 @@
---
name: "🤔 Questions and Help"
about: The issue tracker is not for questions. Please ask questions on https://stackoverflow.com/questions/tagged/react-native.
title: 'Question: '
labels: 'Type: Question'
---
🚨 The issue tracker is not for questions. 🚨
As it happens, support requests that are created as issues are likely to be closed. We want to make sure you are able to find the help you seek. Please take a look at the following resources.
## Coding Questions
### https://stackoverflow.com/questions/tagged/react-native
If you have a coding question related to React Native, it might be better suited for Stack Overflow. It's a great place to browse through frequent questions about using React Native, as well as ask for help with specific questions.
## Talk to other React Native developers
### https://www.reactiflux.com/
Reactiflux is an active community of React and React Native developers. If you are looking for immediate assistance or have a general question about React Native, the #react-native channel is a good place to start.
If you want to participate in casual discussions about the use of React Native, consider participating in one of the following forums:
- Discord Community: https://discord.gg/0ZcbPKXt5bZjGY5n
- Spectrum Chat: https://spectrum.chat/react-native
- Facebook Group: https://www.facebook.com/groups/react.native.community
If you'd like to discuss topics related to the future of React Native, or would like to propose a new feature or change before sending a pull request, please check out the discussions and proposals repo:
- https://github.com/react-native-community/discussions-and-proposals
> For a full list of community resources, check out React Native's Community page at https://facebook.github.io/react-native/help.
+1 -3
View File
@@ -6,9 +6,7 @@
## Changelog
<!-- Help reviewers and the release process by writing your own changelog entry. For an example, see:
https://github.com/facebook/react-native/wiki/Changelog
-->
<!-- Help reviewers and the release process by writing your own changelog entry. See https://github.com/facebook/react-native/wiki/Changelog for an example. -->
[CATEGORY] [TYPE] - Message
+2 -2
View File
@@ -27,9 +27,9 @@ If you'd like to discuss topics related to the future of React Native, please ch
If you want to participate in casual discussions about the use of React Native, consider participating in one of the following forums:
- [Reactiflux Discord Server](https://www.reactiflux.com)
- [Reactiflux Discord Server](https://www.reactiflux)
- [Spectrum Chat](https://spectrum.chat/react-native)
- [React Native Community Facebook Group](https://www.facebook.com/groups/react.native.community)
> For a full list of community resources, check out [React Native's Community page](https://reactnative.dev/help).
> For a full list of community resources, check out [React Native's Community page](https://facebook.github.io/react-native/help).
@@ -1,56 +0,0 @@
# Configuration for Respond To Issue Based on Label https://github.com/marketplace/actions/respond-to-issue-based-on-label
"Type: Invalid":
close: true
"Type: Question":
comment: >
We are using GitHub issues exclusively to track bugs in React Native. GitHub may not be the ideal place to ask a question, but you can try asking over on [Stack Overflow](http://stackoverflow.com/questions/tagged/react-native), or on [Reactiflux](https://www.reactiflux.com/).
close: true
"Type: Docs":
comment: >
Please report documentation issues in the [`react-native-website`](https://github.com/facebook/react-native-website/issues) repository.
close: true
"Type: Upgrade Issue":
comment: >
Do you need help upgrading to a newer React Native version? Visit the [Upgrade Support repository](https://github.com/react-native-community/upgrade-support) or use the [upgrade helper](https://react-native-community.github.io/upgrade-helper/) to see the changes that need to be made to upgrade your app.
close: true
"Resolution: For Stack Overflow":
comment: >
We are using GitHub issues exclusively to track bugs in the core React Native library. Please try asking over on [Stack Overflow](http://stackoverflow.com/questions/tagged/react-native) as it is better suited for this type of question.
close: true
"Needs: Issue Template":
comment: >
<table><tbody><tr><th width="50">:warning:</th><th width="100%">
Missing Required Fields
</th></tr><tr><td>:information_source:</td><td>
It looks like your issue may be missing some necessary information. GitHub provides an example template whenever a <a href="https://github.com/facebook/react-native/issues/new?template=bug_report.md">new issue is created</a>. Could you go back and make sure to fill out the template? You may edit this issue, or close it and open a new one.
</td></tr></tbody></table>
labels:
- "Needs: Author Feedback"
"Needs: Environment Info":
comment: >
<table><tbody><tr><th width="50">:warning:</th><th width="100%">
Missing Environment Information
</th></tr><tr><td>:information_source:</td><td>
Your issue may be missing information about your development environment. You can obtain the missing information by running <code>react-native info</code> in a console.
</td></tr></tbody></table>
labels:
- "Needs: Author Feedback"
"Needs: Verify on Latest Version":
comment: >
<table><tbody><tr><th width="50">:warning:</th><th width="100%">
Using Old Version
</th></tr><tr><td>:information_source:</td><td>
It looks like you are using an older version of React Native. Please <a href="https://reactnative.dev/docs/upgrading">upgrade</a> to the latest version, and verify if the issue persists. If it does not, please let us know so we can close out this issue. This helps us ensure we are looking at issues that still exist in the current release.
</td></tr></tbody></table>
labels:
- "Needs: Author Feedback"
"Needs: Repro":
comment: >
<table><tbody><tr><th width="50">:warning:</th><th width="100%">
Missing Reproducible Example
</th></tr><tr><td>:information_source:</td><td>
It looks like your issue is missing a reproducible example. Please provide a <a href="https://snack.expo.io">Snack</a> or a repository that demonstrates the issue you are reporting in a <a href="https://stackoverflow.com/help/minimal-reproducible-example">minimal, complete, and reproducible</a> manner.
</td></tr></tbody></table>
labels:
- "Needs: Author Feedback"
-21
View File
@@ -1,21 +0,0 @@
name: Issue Needs Attention
# This workflow is triggered on issue comments.
on:
issue_comment:
types: created
jobs:
applyNeedsAttentionLabel:
name: Apply Needs Attention Label
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Apply Needs Attention Label
uses: hramos/needs-attention@v1
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
response-required-label: "Needs: Author Feedback"
needs-attention-label: "Needs: Attention"
id: needs-attention
- name: Result
run: echo '${{ steps.needs-attention.outputs.result }}'
-16
View File
@@ -1,16 +0,0 @@
name: On Issue Labeled
# This workflow is triggered when a label is added to an issue.
on:
issues:
types: labeled
jobs:
respondToIssueBasedOnLabel:
name: Respond to Issue Based on Label
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Respond to Issue Based on Label
uses: hramos/respond-to-issue-based-on-label@v2
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
+10 -38
View File
@@ -23,31 +23,19 @@ project.xcworkspace
# Gradle
/build/
/packages/react-native-codegen/android/build/
/packages/react-native-gradle-plugin/build/
/packages/rn-tester/android/app/.cxx/
/packages/rn-tester/android/app/build/
/packages/rn-tester/android/app/gradle/
/packages/rn-tester/android/app/gradlew
/packages/rn-tester/android/app/gradlew.bat
/RNTester/android/app/build/
/RNTester/android/app/gradle/
/RNTester/android/app/gradlew
/RNTester/android/app/gradlew.bat
/ReactAndroid/build/
/ReactAndroid/gradle/
/ReactAndroid/gradlew
/ReactAndroid/gradlew.bat
# Buck
.buckd
buck-out
/.lsp.buckd
/.lsp-buck-out
/ReactAndroid/src/main/jni/prebuilt/lib/
/ReactAndroid/src/main/jni/prebuilt/lib/armeabi-v7a/
/ReactAndroid/src/main/jni/prebuilt/lib/x86/
/ReactAndroid/src/main/gen
# Android Studio
.project
.settings
.classpath
# Watchman
.watchmanconfig
@@ -80,33 +68,17 @@ package-lock.json
# ReactCommon subdir shouldn't have Xcode project
/ReactCommon/**/*.xcodeproj
/packages/rn-tester/build
/packages/rn-tester/android/app/build/*
RNTester/build
# Libs that shouldn't have Xcode project
/Libraries/FBLazyVector/**/*.xcodeproj
/Libraries/FBReactNativeSpec/**/*.xcodeproj
/Libraries/RCTRequired/**/*.xcodeproj
/React/CoreModules/**/*.xcodeproj
/React/FBReactNativeSpec/**/*.xcodeproj
/packages/react-native-codegen/**/*.xcodeproj
# CocoaPods
/template/ios/Pods/
/template/ios/Podfile.lock
/packages/rn-tester/Gemfile.lock
# Ignore RNTester specific Pods, but keep the __offline_mirrors__ here.
/packages/rn-tester/Pods/*
!/packages/rn-tester/Pods/__offline_mirrors__
# react-native-codegen
/packages/react-native-codegen/lib
/ReactCommon/react/renderer/components/rncore/
/packages/rn-tester/NativeModuleExample/ScreenshotManagerSpec*
# Visual studio
.vscode
.vs
# Android memory profiler files
*.hprof
RNTester/Pods/Local Podspecs
RNTester/Pods/Target Support Files
+1
View File
@@ -0,0 +1 @@
8.11.3
+1 -2
View File
@@ -3,6 +3,5 @@
"singleQuote": true,
"trailingComma": "all",
"bracketSpacing": false,
"jsxBracketSameLine": true,
"arrowParens": "avoid"
"jsxBracketSameLine": true
}
-1386
View File
File diff suppressed because it is too large Load Diff
+3 -75
View File
@@ -1,77 +1,5 @@
# Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to make participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, sex characteristics, gender identity and expression,
level of experience, education, socio-economic status, nationality, personal
appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment
include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
## Scope
This Code of Conduct applies within all project spaces, and it also applies when
an individual is representing the project or its community in public spaces.
Examples of representing a project or community include using an official
project e-mail address, posting via an official social media account, or acting
as an appointed representative at an online or offline event. Representation of
a project may be further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at <opensource-conduct@fb.com>. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see
https://www.contributor-covenant.org/faq
Facebook has adopted a Code of Conduct that we expect project participants to adhere to.
Please read the [full text](https://code.fb.com/codeofconduct/)
so that you can understand what actions will and will not be tolerated.
+22 -26
View File
@@ -8,9 +8,9 @@ The [Open Source Guides](https://opensource.guide/) website has a collection of
* [Building Welcoming Communities](https://opensource.guide/building-community/)
### [Code of Conduct](https://github.com/facebook/react-native/blob/HEAD/CODE_OF_CONDUCT.md)
### [Code of Conduct](https://code.fb.com/codeofconduct/)
As a reminder, all contributors are expected to adhere to the [Code of Conduct](https://github.com/facebook/react-native/blob/HEAD/CODE_OF_CONDUCT.md).
As a reminder, all contributors are expected to adhere to the [Code of Conduct](https://code.facebook.com/codeofconduct).
## Ways to Contribute
@@ -18,7 +18,7 @@ If you are eager to start contributing code right away, we have a list of [good
There are other ways you can contribute without writing a single line of code. Here are a few things you can do to help out:
1. **Replying and handling open issues.** We get a lot of issues every day, and some of them may lack necessary information. You can help out by guiding people through the process of filling out the issue template, asking for clarifying information, or pointing them to existing issues that match their description of the problem. We cover more about this process in the [Issue Triage wiki](https://github.com/facebook/react-native/wiki/Triaging-GitHub-Issues).
1. **Replying and handling open issues.** We get a lot of issues every day, and some of them may lack necessary information. You can help out by guiding people through the process of filling out the issue template, asking for clarifying information, or pointing them to existing issues that match their description of the problem. We cover more about this process in the [Issue Triage wiki](https://github.com/facebook/react-native/wiki/Issues#triage).
2. **Reviewing pull requests for the docs.** Reviewing [documentation updates](https://github.com/facebook/react-native-website/pulls) can be as simple as checking for spelling and grammar. If you encounter situations that can be explained better in the docs, click **Edit** at the top of most docs pages to get started with your own contribution.
3. **Help people write test plans.** Some pull requests sent to the main repository may lack a proper test plan. These help reviewers understand how the change was tested, and can speed up the time it takes for a contribution to be accepted.
@@ -30,8 +30,8 @@ We use GitHub issues and pull requests to keep track of bug reports and contribu
You can learn more about the contribution process in the following documents:
* [Issues](https://github.com/facebook/react-native/wiki/Triaging-GitHub-Issues)
* [Pull Requests](https://github.com/facebook/react-native/wiki/Managing-Pull-Requests)
* [Issues](https://github.com/facebook/react-native/wiki/Issues)
* [Pull Requests](https://github.com/facebook/react-native/wiki/Pull-Requests)
We also have a thriving community of contributors who would be happy to help you get set up. You can reach out to us through [@ReactNative](http://twitter.com/reactnative) (the React Native team) and [@ReactNativeComm](http://twitter.com/reactnativecomm) (the React Native Community organization).
@@ -42,7 +42,7 @@ The main repository, <https://github.com/facebook/react-native>, contains the Re
There are a few other repositories you might want to familiarize yourself with:
* **React Native website** which contains the source code for the website, including the documentation, located at <https://github.com/facebook/react-native-website>
* **Releases** are coordinated through the <https://github.com/react-native-community/releases> repository. This includes important documents such as the Changelog.
* **Releases** are coordinated through the <https://github.com/react-native-community/react-native-releases> repository. This includes important documents such as the Changelog.
* **Discussions** about the future of React Native take place in the <https://github.com/react-native-community/discussions-and-proposals> repository.
* **High-quality plugins** for React Native can be found throughout the [React Native Community GitHub Organization](http://github.com/react-native-community/).
@@ -50,7 +50,7 @@ Browsing through these repositories should provide some insight into how the Rea
## GitHub Issues
We use GitHub issues to track bugs exclusively. We have documented our issue handling processes in the [Issues wiki](https://github.com/facebook/react-native/wiki/Triaging-GitHub-Issues).
We use GitHub issues to track bugs exclusively. We have documented our issue handling processes in the [Issues wiki](https://github.com/facebook/react-native/wiki/Issues).
### Security Bugs
@@ -58,46 +58,42 @@ Facebook has a [bounty program](https://www.facebook.com/whitehat/) for the safe
## Helping with Documentation
The React Native documentation is hosted as part of the React Native website repository at https://github.com/facebook/react-native-website. The website itself is located at <https://reactnative.dev/> and it is built using [Docusaurus](https://docusaurus.io/). If there's anything you'd like to change in the docs, you can get started by clicking on the "Edit" button located on the upper right of most pages in the website.
The React Native documentation is hosted as part of the React Native website repository at https://github.com/facebook/react-native-website. The website itself is located at <https://facebook.github.io/react-native> and it is built using [Docusaurus](https://docusaurus.io/). If there's anything you'd like to change in the docs, you can get started by clicking on the "Edit" button located on the upper right of most pages in the website.
If you are adding new functionality or introducing a change in behavior, we will ask you to update the documentation to reflect your changes.
### Contributing to the Blog
The React Native blog is generated [from the Markdown sources for the blog](https://github.com/facebook/react-native-website/tree/HEAD/website/blog).
The React Native blog is generated [from the Markdown sources for the blog](https://github.com/facebook/react-native-website/tree/master/website/blog).
Please open an issue in the https://github.com/facebook/react-native-website repository or tag us on [@ReactNative on Twitter](http://twitter.com/reactnative) and get the go-ahead from a maintainer before writing an article intended for the React Native blog. In most cases, you might want to share your article on your own blog or writing medium instead. It's worth asking, though, in case we find your article is a good fit for the blog.
We recommend referring to the [CONTRIBUTING](https://github.com/facebook/react-native-website/blob/HEAD/CONTRIBUTING.md) document for the `react-native-website` repository to learn more about contributing to the website in general.
We recommend referring to the [CONTRIBUTING](https://github.com/facebook/react-native-website/blob/master/CONTRIBUTING.md) document for the `react-native-website` repository to learn more about contributing to the website in general.
## Contributing Code
Code-level contributions to React Native generally come in the form of [pull requests](https://help.github.com/en/articles/about-pull-requests). These are done by forking the repo and making changes locally. Directly in the repo, there is the [`rn-tester` app](/packages/rn-tester) that you can install on your device (or simulators) and use to test the changes you're making to React Native sources.
Code-level contributions to React Native generally come in the form of [pull requests](https://help.github.com/en/articles/about-pull-requests). The process of proposing a change to React Native can be summarized as follows:
The process of proposing a change to React Native can be summarized as follows:
1. Fork the React Native repository and create your branch from `main`.
2. Make the desired changes to React Native sources. Use the `packages/rn-tester` app to test them out.
3. If you've added code that should be tested, add tests.
4. If you've changed APIs, update the documentation, which lives in [another repo](https://github.com/facebook/react-native-website/).
5. Ensure the test suite passes, either locally or on CI once you opened a pull request.
6. Make sure your code lints (for example via `yarn lint --fix`).
7. Push the changes to your fork.
8. Create a pull request to the React Native repository.
9. Review and address comments on your pull request.
1. Fork the React Native repository and create your branch from `master`.
2. If you've added code that should be tested, add tests.
3. If you've changed APIs, update the documentation.
4. Ensure the test suite passes, either locally or on CI once you opened a pull request.
5. Make sure your code lints (for example via `yarn lint --fix`).
6. Push the changes to your fork.
7. Create a pull request to the React Native repository.
8. Review and address comments on your pull request.
1. A bot may comment with suggestions. Generally we ask you to resolve these first before a maintainer will review your code.
2. If changes are requested and addressed, please [request review](https://docs.github.com/en/github/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/requesting-a-pull-request-review) to notify reviewers to take another look.
10. If you haven't already, please complete the [Contributor License Agreement](https://github.com/facebook/react-native/wiki/Contributor-License-Agreement) ("CLA"). **[Complete your CLA here.](https://code.facebook.com/cla)**
9. If you haven't already, please complete the [Contributor License Agreement](https://github.com/facebook/react-native/wiki/Contributor-License-Agreement) ("CLA"). **[Complete your CLA here.](https://code.facebook.com/cla)**
If all goes well, your pull request will be merged. If it is not merged, maintainers will do their best to explain the reason why.
### Step-by-step Guide
Whenever you are ready to contribute code, check out our [step-by-step guide to sending your first pull request](https://github.com/facebook/react-native/wiki/How-to-Open-a-Pull-Request), or read the [How to Contribute Code](https://github.com/facebook/react-native/wiki/How-to-Contribute-Code) wiki for more details.
Whenever you are ready to contribute code, check out our [step-by-step guide to sending your first pull request](https://github.com/facebook/react-native/wiki/Pull-Requests#getting-ready-to-submit-your-first-pull-request), or read the [How to Contribute Code](https://github.com/facebook/react-native/wiki/How-to-Contribute) wiki for more details.
### Tests
Tests help us prevent regressions from being introduced to the codebase. The GitHub repository is continuously tested using Circle and Appveyor, the results of which are available through the Checks functionality on [commits](https://github.com/facebook/react-native/commits/HEAD) and pull requests. You can learn more about running and writing tests in the [Tests wiki](http://github.com/facebook/react-native/wiki/Tests).
Tests help us prevent regressions from being introduced to the codebase. The GitHub repository is continuously tested using Circle and Appveyor, the results of which are available through the Checks functionality on [commits](https://github.com/facebook/react-native/commits/master) and pull requests. You can learn more about running and writing tests in the [Tests wiki](http://github.com/facebook/react-native/wiki/Tests).
## Community Contributions
+4 -5
View File
@@ -10,15 +10,14 @@ There are three types of stakeholders:
## 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 have been for years. Informed by their use of React Native, they push for improvements of the core and/or the ecosystem around it. Facebook's 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.
React Native's current set of partners include Callstack, Expo, Facebook, Infinite Red, Microsoft and Software Mansion. Many engineers from these companies are core contributors, and their partner responsibilities also include:
Our current set of partners include Callstack, Expo, Infinite Red, Microsoft and Software Mansion. Many engineers from these companies are core contributors, and their partner responsibilities also include:
* **[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.io/):** Builds [expo](https://github.com/expo/expo) on top of React Native to simplify app development
* **[Facebook](https://opensource.facebook.com):** Oversees the React Native product and maintains the [React Native core repo](https://reactnative.dev/)
* **[Infinite Red](https://infinite.red/):** Maintains the [ignite cli/boilerplate](https://github.com/infinitered/ignite), organizes [Chain React Conf](https://cr.infinite.red/)
* **[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
* **[Infinite Red](https://infinite.red/):** Maintains the [ignite cli/boilerplate](https://github.com/infinitered/ignite), organizes [Chain React Conf](https://infinite.red/ChainReactConf)
* **[Microsoft](https://www.microsoft.com/en-gb/):** Develops [React Native Windows](https://github.com/Microsoft/react-native-windows) for the Universal Windows Platform (UWP)
* **[Software Mansion](https://swmansion.com/):** Maintain core infrastructure including JSC, Animated, and other popular third-party plugins.
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:
+10 -7
View File
@@ -8,14 +8,17 @@
* @flow strict-local
*/
import invariant from 'invariant';
'use strict';
const React = require('react');
const ReactNative = require('react-native');
const {View} = ReactNative;
const RCTDeviceEventEmitter = require('react-native/Libraries/EventEmitter/RCTDeviceEventEmitter');
const {TestModule} = ReactNative.NativeModules;
import NativeAccessibilityManager from 'react-native/Libraries/Components/AccessibilityInfo/NativeAccessibilityManager';
import {DeviceEventEmitter, NativeModules, View} from 'react-native';
import * as React from 'react';
import invariant from 'invariant';
const {TestModule} = NativeModules;
class AccessibilityManagerTest extends React.Component<{...}> {
class AccessibilityManagerTest extends React.Component<{}> {
componentDidMount() {
invariant(
NativeAccessibilityManager,
@@ -36,7 +39,7 @@ class AccessibilityManagerTest extends React.Component<{...}> {
accessibilityExtraExtraLarge: 11.0,
accessibilityExtraExtraExtraLarge: 12.0,
});
DeviceEventEmitter.addListener('didUpdateDimensions', update => {
RCTDeviceEventEmitter.addListener('didUpdateDimensions', update => {
TestModule.markTestPassed(update.window.fontScale === 4.0);
});
}
+2 -7
View File
@@ -19,19 +19,14 @@ const deepDiffer = require('react-native/Libraries/Utilities/differ/deepDiffer')
const TEST_PAYLOAD = {foo: 'bar'};
type AppEvent = {
data: Object,
ts: number,
...
};
type AppEvent = {data: Object, ts: number};
type State = {
sent: 'none' | AppEvent,
received: 'none' | AppEvent,
elapsed?: string,
...
};
class AppEventsTest extends React.Component<{...}, State> {
class AppEventsTest extends React.Component<{}, State> {
state: State = {sent: 'none', received: 'none'};
componentDidMount() {
+20 -46
View File
@@ -16,7 +16,6 @@ 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;
@@ -44,32 +43,15 @@ function expectTrue(condition: boolean, message: string) {
}
}
// Type-safe wrapper around JSON.stringify
function stringify(
value:
| void
| null
| string
| number
| boolean
| {...}
| $ReadOnlyArray<mixed>,
): string {
if (typeof value === 'undefined') {
return 'undefined';
}
return JSON.stringify(value);
}
function expectEqual(lhs, rhs, testname: string) {
expectTrue(
!deepDiffer(lhs, rhs),
'Error in test ' +
testname +
': expected\n' +
stringify(rhs) +
JSON.stringify(rhs) +
'\ngot\n' +
stringify(lhs),
JSON.stringify(lhs),
);
}
@@ -79,7 +61,7 @@ function expectAsyncNoError(place, err) {
}
expectTrue(
err === null,
'Unexpected error in ' + place + ': ' + stringify(err),
'Unexpected error in ' + place + ': ' + JSON.stringify(err),
);
}
@@ -89,7 +71,7 @@ function testSetAndGet() {
AsyncStorage.getItem(KEY_1, (err2, result) => {
expectAsyncNoError('testSetAndGet/getItem', err2);
expectEqual(result, VAL_1, 'testSetAndGet setItem');
updateMessage('get(key_1) correctly returned ' + String(result));
updateMessage('get(key_1) correctly returned ' + result);
runTestCase('should get null for missing key', testMissingGet);
});
});
@@ -99,7 +81,7 @@ 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));
updateMessage('missing get(key_2) correctly returned ' + result);
runTestCase('check set twice results in a single key', testSetTwice);
});
}
@@ -123,9 +105,8 @@ function testRemoveItem() {
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() + ')',
result.indexOf(KEY_1) >= 0 && result.indexOf(KEY_2) >= 0,
'Missing KEY_1 or KEY_2 in ' + '(' + result + ')',
);
updateMessage('testRemoveItem - add two items');
AsyncStorage.removeItem(KEY_1, err2 => {
@@ -142,8 +123,8 @@ function testRemoveItem() {
AsyncStorage.getAllKeys((err4, result3) => {
expectAsyncNoError('testRemoveItem/getAllKeys', err4);
expectTrue(
nullthrows(result3).indexOf(KEY_1) === -1,
'Unexpected: KEY_1 present in ' + nullthrows(result3).join(),
result3.indexOf(KEY_1) === -1,
'Unexpected: KEY_1 present in ' + result3,
);
updateMessage('proper length returned.');
runTestCase('should merge values', testMerge);
@@ -156,17 +137,13 @@ function testRemoveItem() {
}
function testMerge() {
AsyncStorage.setItem(KEY_MERGE, stringify(VAL_MERGE_1), err1 => {
AsyncStorage.setItem(KEY_MERGE, JSON.stringify(VAL_MERGE_1), err1 => {
expectAsyncNoError('testMerge/setItem', err1);
AsyncStorage.mergeItem(KEY_MERGE, stringify(VAL_MERGE_2), err2 => {
AsyncStorage.mergeItem(KEY_MERGE, JSON.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',
);
expectEqual(JSON.parse(result), VAL_MERGE_EXPECT, 'testMerge');
updateMessage('objects deeply merged\nDone!');
runTestCase('multi set and get', testOptimizedMultiGet);
});
@@ -175,10 +152,7 @@ function testMerge() {
}
function testOptimizedMultiGet() {
let batch = [
[KEY_1, VAL_1],
[KEY_2, VAL_2],
];
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
@@ -188,7 +162,8 @@ function testOptimizedMultiGet() {
expectAsyncNoError(`${i} testOptimizedMultiGet/multiGet`, err2);
expectEqual(result, batch, `${i} testOptimizedMultiGet multiGet`);
updateMessage(
'multiGet([key_1, key_2]) correctly returned ' + stringify(result),
'multiGet([key_1, key_2]) correctly returned ' +
JSON.stringify(result),
);
done();
});
@@ -196,8 +171,8 @@ function testOptimizedMultiGet() {
});
}
class AsyncStorageTest extends React.Component<{...}, $FlowFixMeState> {
state: any | {|done: boolean, messages: string|} = {
class AsyncStorageTest extends React.Component<{}, $FlowFixMeState> {
state: any | $TEMPORARY$object<{|done: boolean, messages: string|}> = {
messages: 'Initializing...',
done: false,
};
@@ -218,10 +193,9 @@ class AsyncStorageTest extends React.Component<{...}, $FlowFixMeState> {
return (
<View style={styles.container}>
<Text>
{/* $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.
*/
{/* $FlowFixMe(>=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}
-30
View File
@@ -1,30 +0,0 @@
load("@fbsource//tools/build_defs:js_glob.bzl", "js_glob")
load("@fbsource//tools/build_defs/oss:metro_defs.bzl", "rn_library")
# This file was generated by running
# js1 build buckfiles
rn_library(
name = "IntegrationTests",
srcs = js_glob(
[
"**/*",
],
excludes = [
"**/__*__/**",
"**/*.command",
"**/*.md",
"websocket_integration_test_server.js",
],
),
labels = ["supermodule:xplat/default/public.react_native.tests"],
skip_processors = True,
visibility = ["PUBLIC"],
deps = [
"//xplat/js:node_modules__invariant",
"//xplat/js:node_modules__nullthrows",
"//xplat/js/RKJSModules/vendor/react:react",
"//xplat/js/react-native-github:react-native",
"//xplat/js/react-native-github/packages/assets:assets",
],
)
@@ -10,8 +10,6 @@
'use strict';
import type {ExtendedError} from 'react-native/Libraries/Core/ExtendedError';
const React = require('react');
const ReactNative = require('react-native');
const parseErrorStack = require('react-native/Libraries/Core/Devtools/parseErrorStack');
@@ -19,7 +17,7 @@ const {View} = ReactNative;
const {TestModule} = ReactNative.NativeModules;
class GlobalEvalWithSourceUrlTest extends React.Component<{...}> {
class GlobalEvalWithSourceUrlTest extends React.Component<{}> {
componentDidMount() {
if (typeof global.globalEvalWithSourceUrl !== 'function') {
throw new Error(
@@ -33,7 +31,7 @@ class GlobalEvalWithSourceUrlTest extends React.Component<{...}> {
'Expected globalEvalWithSourceUrl(expression) to return a value',
);
}
let syntaxError: ?ExtendedError;
let syntaxError;
try {
global.globalEvalWithSourceUrl('{');
} catch (e) {
@@ -44,12 +42,7 @@ class GlobalEvalWithSourceUrlTest extends React.Component<{...}> {
'Expected globalEvalWithSourceUrl to throw on a syntax error',
);
}
// Hermes throws an Error instead of a SyntaxError
// https://github.com/facebook/hermes/issues/400
if (
syntaxError.jsEngine !== 'hermes' &&
!(syntaxError instanceof SyntaxError)
) {
if (!(syntaxError instanceof SyntaxError)) {
throw new Error(
'Expected globalEvalWithSourceUrl to throw SyntaxError on a syntax error',
);
@@ -66,7 +59,7 @@ class GlobalEvalWithSourceUrlTest extends React.Component<{...}> {
'Expected globalEvalWithSourceUrl to throw an Error object',
);
}
const parsedStack = parseErrorStack(error?.stack);
const parsedStack = parseErrorStack(error);
if (parsedStack[0].file !== url) {
throw new Error(
`Expected first eval stack frame to be in ${url} but found ${String(
+6 -7
View File
@@ -27,17 +27,16 @@ const {TestModule} = ReactNative.NativeModules;
const TESTS = ['only-if-cached', 'default', 'reload', 'force-cache'];
type Props = {...};
type Props = {};
type State = {
'only-if-cached'?: boolean,
default?: boolean,
reload?: boolean,
'force-cache'?: boolean,
...
};
class ImageCachePolicyTest extends React.Component<Props, $FlowFixMeState> {
state: $FlowFixMe | {...} = {};
state: $FlowFixMe | $TEMPORARY$object<{||}> = {};
shouldComponentUpdate(nextProps: Props, nextState: State): boolean {
const results: Array<?boolean> = TESTS.map(x => nextState[x]);
@@ -64,7 +63,7 @@ class ImageCachePolicyTest extends React.Component<Props, $FlowFixMeState> {
<Image
source={{
uri:
'https://raw.githubusercontent.com/facebook/react-native/HEAD/Libraries/NewAppScreen/components/logo.png?cacheBust=notinCache' +
'https://facebook.github.io/react-native/img/favicon.png?cacheBust=notinCache' +
Date.now(),
cache: 'only-if-cached',
}}
@@ -75,7 +74,7 @@ class ImageCachePolicyTest extends React.Component<Props, $FlowFixMeState> {
<Image
source={{
uri:
'https://raw.githubusercontent.com/facebook/react-native/HEAD/Libraries/NewAppScreen/components/logo.png?cacheBust=notinCache' +
'https://facebook.github.io/react-native/img/favicon.png?cacheBust=notinCache' +
Date.now(),
cache: 'default',
}}
@@ -86,7 +85,7 @@ class ImageCachePolicyTest extends React.Component<Props, $FlowFixMeState> {
<Image
source={{
uri:
'https://raw.githubusercontent.com/facebook/react-native/HEAD/Libraries/NewAppScreen/components/logo.png?cacheBust=notinCache' +
'https://facebook.github.io/react-native/img/favicon.png?cacheBust=notinCache' +
Date.now(),
cache: 'reload',
}}
@@ -97,7 +96,7 @@ class ImageCachePolicyTest extends React.Component<Props, $FlowFixMeState> {
<Image
source={{
uri:
'https://raw.githubusercontent.com/facebook/react-native/HEAD/Libraries/NewAppScreen/components/logo.png?cacheBust=notinCache' +
'https://facebook.github.io/react-native/img/favicon.png?cacheBust=notinCache' +
Date.now(),
cache: 'force-cache',
}}
+1 -1
View File
@@ -15,7 +15,7 @@ const ReactNative = require('react-native');
const {Image} = ReactNative;
const {TestModule} = ReactNative.NativeModules;
class ImageSnapshotTest extends React.Component<{...}> {
class ImageSnapshotTest extends React.Component<{}> {
componentDidMount() {
if (!TestModule.verifySnapshot) {
throw new Error('TestModule.verifySnapshot not defined.');
@@ -13,6 +13,7 @@
const React = require('react');
const ReactNative = require('react-native');
const requestAnimationFrame = require('fbjs/lib/requestAnimationFrame');
const {Text, View, StyleSheet} = ReactNative;
const {TestModule} = ReactNative.NativeModules;
@@ -56,10 +57,9 @@ class IntegrationTestHarnessTest extends React.Component<Props, State> {
return (
<View style={styles.container}>
<Text>
{/* $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.
*/
{/* $FlowFixMe(>=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...'}
</Text>
+7 -12
View File
@@ -40,9 +40,9 @@ const TESTS = [
];
TESTS.forEach(
/* $FlowFixMe[incompatible-call] (>=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. */
/* $FlowFixMe(>=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. */
test => AppRegistry.registerComponent(test.displayName, () => test),
);
@@ -51,7 +51,7 @@ require('./LoggingTestModule');
type Test = any;
class IntegrationTestsApp extends React.Component<{...}, $FlowFixMeState> {
class IntegrationTestsApp extends React.Component<{}, $FlowFixMeState> {
state = {
test: (null: ?Test),
};
@@ -60,10 +60,9 @@ class IntegrationTestsApp extends React.Component<{...}, $FlowFixMeState> {
if (this.state.test) {
return (
<ScrollView>
{/* $FlowFixMe[type-as-value] (>=0.53.0 site=react_native_fb,react_
* native_oss) This comment suppresses an error when upgrading
* Flow's support for React. To see the error delete this comment
* and run Flow. */}
{/* $FlowFixMe(>=0.53.0 site=react_native_fb,react_native_oss) This
* comment suppresses an error when upgrading Flow's support for
* React. To see the error delete this comment and run Flow. */}
<this.state.test />
</ScrollView>
);
@@ -80,10 +79,6 @@ class IntegrationTestsApp extends React.Component<{...}, $FlowFixMeState> {
{TESTS.map(test => [
<TouchableOpacity
onPress={() => this.setState({test})}
/* $FlowFixMe[incompatible-type] (>=0.115.0 site=react_native_fb)
* This comment suppresses an error found when Flow v0.115 was
* deployed. To see the error, delete this comment and run Flow.
*/
style={styles.row}>
<Text style={styles.testName}>{test.displayName}</Text>
</TouchableOpacity>,
+1 -3
View File
@@ -5,7 +5,7 @@
* LICENSE file in the root directory of this source tree.
*
* @format
* @flow strict-local
* @flow
*/
'use strict';
@@ -38,7 +38,6 @@ type State = {
viewLayout?: Layout,
viewStyle?: ViewStyleProp,
containerStyle?: ViewStyleProp,
...
};
class LayoutEventsTest extends React.Component<Props, State> {
@@ -197,5 +196,4 @@ const styles = StyleSheet.create({
},
});
LayoutEventsTest.displayName = 'LayoutEventsTest';
module.exports = LayoutEventsTest;
+2 -1
View File
@@ -11,6 +11,7 @@
const BatchedBridge = require('react-native/Libraries/BatchedBridge/BatchedBridge');
const warning = require('fbjs/lib/warning');
const invariant = require('invariant');
const LoggingTestModule = {
@@ -23,7 +24,7 @@ const LoggingTestModule = {
}, timeout_ms);
},
warning: function(str) {
console.warn(str);
warning(false, str);
},
invariant: function(str) {
invariant(false, str);
+1 -1
View File
@@ -15,7 +15,7 @@ const ReactNative = require('react-native');
const {View} = ReactNative;
const {TestModule} = ReactNative.NativeModules;
class PromiseTest extends React.Component<{...}> {
class PromiseTest extends React.Component<{}> {
shouldResolve: boolean = false;
shouldReject: boolean = false;
shouldSucceedAsync: boolean = false;
@@ -5,9 +5,11 @@
* LICENSE file in the root directory of this source tree.
*
* @format
* @flow strict-local
* @flow
*/
'use strict';
const RCTNativeAppEventEmitter = require('react-native/Libraries/EventEmitter/RCTNativeAppEventEmitter');
const React = require('react');
const ReactNative = require('react-native');
@@ -15,7 +17,7 @@ const ReactNative = require('react-native');
const {View} = ReactNative;
const {TestModule} = ReactNative.NativeModules;
import {type EventSubscription} from 'react-native/Libraries/vendor/emitter/EventEmitter';
import type EmitterSubscription from 'react-native/Libraries/vendor/emitter/EmitterSubscription';
const reactViewWidth = 101;
const reactViewHeight = 102;
@@ -31,7 +33,7 @@ type State = {|
class ReactContentSizeUpdateTest extends React.Component<Props, State> {
_timeoutID: ?TimeoutID = null;
_subscription: ?EventSubscription = null;
_subscription: ?EmitterSubscription = null;
state: State = {
height: reactViewHeight,
+4 -2
View File
@@ -5,7 +5,7 @@
* LICENSE file in the root directory of this source tree.
*
* @format
* @flow strict-local
* @flow
*/
'use strict';
@@ -13,10 +13,12 @@
const React = require('react');
const ReactNative = require('react-native');
const requestAnimationFrame = require('fbjs/lib/requestAnimationFrame');
const {StyleSheet, View} = ReactNative;
const {TestModule} = ReactNative.NativeModules;
class SimpleSnapshotTest extends React.Component<{...}> {
class SimpleSnapshotTest extends React.Component<{}> {
componentDidMount() {
if (!TestModule.verifySnapshot) {
throw new Error('TestModule.verifySnapshot not defined.');
@@ -5,16 +5,18 @@
* LICENSE file in the root directory of this source tree.
*
* @format
* @flow strict-local
* @flow
*/
'use strict';
const RCTNativeAppEventEmitter = require('react-native/Libraries/EventEmitter/RCTNativeAppEventEmitter');
const React = require('react');
const ReactNative = require('react-native');
const {View} = ReactNative;
const {TestModule} = ReactNative.NativeModules;
import {type EventSubscription} from 'react-native/Libraries/vendor/emitter/EventEmitter';
import type EmitterSubscription from 'react-native/Libraries/vendor/emitter/EmitterSubscription';
const reactViewWidth = 111;
const reactViewHeight = 222;
@@ -29,7 +31,7 @@ type Props = $ReadOnly<{|
|}>;
class SizeFlexibilityUpdateTest extends React.Component<Props> {
_subscription: ?EventSubscription = null;
_subscription: ?EmitterSubscription = null;
UNSAFE_componentWillMount() {
this._subscription = RCTNativeAppEventEmitter.addListener(
@@ -52,8 +54,7 @@ class SizeFlexibilityUpdateTest extends React.Component<Props> {
rootViewDidChangeIntrinsicSize: (intrinsicSize: {
height: number,
width: number,
...
}) => void = (intrinsicSize: {width: number, height: number, ...}) => {
}) => void = (intrinsicSize: {width: number, height: number}) => {
if (finalState) {
// If a test reaches its final state, it is not expected to do anything more
TestModule.markTestPassed(false);
+1 -1
View File
@@ -16,7 +16,7 @@ const {View} = ReactNative;
const {TestModule, RNTesterTestModule} = ReactNative.NativeModules;
class SyncMethodTest extends React.Component<{...}> {
class SyncMethodTest extends React.Component<{}> {
componentDidMount() {
if (
RNTesterTestModule.echoString('test string value') !== 'test string value'
-15
View File
@@ -97,46 +97,36 @@ class TimersTest extends React.Component<Props, State> {
}
componentDidMount() {
// $FlowFixMe[method-unbinding] added when improving typing for this parameters
this.setTimeout(this.testSetTimeout0, 1000);
}
testSetTimeout0() {
// $FlowFixMe[method-unbinding] added when improving typing for this parameters
this.setTimeout(this.testSetTimeout1, 0);
}
testSetTimeout1() {
// $FlowFixMe[method-unbinding] added when improving typing for this parameters
this.setTimeout(this.testSetTimeout50, 1);
}
testSetTimeout50() {
// $FlowFixMe[method-unbinding] added when improving typing for this parameters
this.setTimeout(this.testRequestAnimationFrame, 50);
}
testRequestAnimationFrame() {
// $FlowFixMe[method-unbinding] added when improving typing for this parameters
this.requestAnimationFrame(this.testSetInterval0);
}
testSetInterval0() {
// $FlowFixMe[method-unbinding] added when improving typing for this parameters
this._nextTest = this.testSetInterval20;
// $FlowFixMe[method-unbinding] added when improving typing for this parameters
this._interval = this.setInterval(this._incrementInterval, 0);
}
testSetInterval20() {
// $FlowFixMe[method-unbinding] added when improving typing for this parameters
this._nextTest = this.testSetImmediate;
// $FlowFixMe[method-unbinding] added when improving typing for this parameters
this._interval = this.setInterval(this._incrementInterval, 20);
}
testSetImmediate() {
// $FlowFixMe[method-unbinding] added when improving typing for this parameters
this.setImmediate(this.testClearTimeout0);
}
@@ -149,7 +139,6 @@ class TimersTest extends React.Component<Props, State> {
testClearTimeout30() {
const timeout = this.setTimeout(() => this._fail('testClearTimeout30'), 30);
this.clearTimeout(timeout);
// $FlowFixMe[method-unbinding] added when improving typing for this parameters
this.setTimeout(this.testClearMulti, 50);
}
@@ -167,7 +156,6 @@ class TimersTest extends React.Component<Props, State> {
fails.forEach(timeout => this.clearTimeout(timeout));
this.setTimeout(() => this.clearTimeout(delayClear), 20);
// $FlowFixMe[method-unbinding] added when improving typing for this parameters
this.setTimeout(this.testOrdering, 50);
}
@@ -203,7 +191,6 @@ class TimersTest extends React.Component<Props, State> {
),
25,
);
// $FlowFixMe[method-unbinding] added when improving typing for this parameters
this.setTimeout(this.done, 50);
}
@@ -262,7 +249,6 @@ class TimersTest extends React.Component<Props, State> {
this.clearInterval(this._interval);
this._interval = null;
}
// $FlowFixMe[method-unbinding]
this.setState({count: 0}, this._nextTest);
return;
}
@@ -281,5 +267,4 @@ const styles = StyleSheet.create({
},
});
TimersTest.displayName = 'TimersTest';
module.exports = TimersTest;
+1 -2
View File
@@ -28,10 +28,9 @@ type State = {
lastMessage: ?string | ?ArrayBuffer,
testMessage: string,
testExpectedResponse: string,
...
};
class WebSocketTest extends React.Component<{...}, State> {
class WebSocketTest extends React.Component<{}, State> {
state: State = {
url: DEFAULT_WS_URL,
fetchStatus: null,
@@ -1,4 +1,5 @@
#!/usr/bin/env node
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
@@ -8,7 +9,6 @@
* @flow
* @format
*/
'use strict';
/* eslint-env node */
+18
View File
@@ -0,0 +1,18 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
// A little helper to make sure we have the right memory allocation ready for use.
// We assume that we will only this in one place so no reference counting is necessary.
// Needs to be freed when deallocated.
// This is fragile since this relies on these values not getting reused. Consider
// wrapping these in an Obj-C class or some ARC hackery to get refcounting.
typedef struct {
size_t count;
CGFloat *array;
} ARTCGFloatArray;
+16
View File
@@ -0,0 +1,16 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <Foundation/Foundation.h>
@protocol ARTContainer <NSObject>
// This is used as a hook for child to mark it's parent as dirty.
// This bubbles up to the root which gets marked as dirty.
- (void)invalidate;
@end
+17
View File
@@ -0,0 +1,17 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <Foundation/Foundation.h>
#import "ARTContainer.h"
#import "ARTNode.h"
@interface ARTGroup : ARTNode <ARTContainer>
@property (nonatomic, assign) CGRect clipping;
@end
+24
View File
@@ -0,0 +1,24 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <React/ARTGroup.h>
@implementation ARTGroup
- (void)renderLayerTo:(CGContextRef)context
{
if (!CGRectIsEmpty(self.clipping)) {
CGContextClipToRect(context, self.clipping);
}
for (ARTNode *node in self.subviews) {
[node renderTo:context];
}
}
@end
+30
View File
@@ -0,0 +1,30 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <React/UIView+React.h>
/**
* ART nodes are implemented as empty UIViews but this is just an implementation detail to fit
* into the existing view management. They should also be shadow views and painted on a background
* thread.
*/
@interface ARTNode : UIView
@property (nonatomic, assign) CGFloat opacity;
- (void)invalidate;
- (void)renderTo:(CGContextRef)context;
/**
* renderTo will take opacity into account and draw renderLayerTo off-screen if there is opacity
* specified, then composite that onto the context. renderLayerTo always draws at opacity=1.
* @abstract
*/
- (void)renderLayerTo:(CGContextRef)context;
@end
+80
View File
@@ -0,0 +1,80 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <React/ARTNode.h>
#import <React/ARTContainer.h>
@implementation ARTNode
- (void)insertReactSubview:(UIView *)subview atIndex:(NSInteger)atIndex
{
[super insertReactSubview:subview atIndex:atIndex];
[self insertSubview:subview atIndex:atIndex];
[self invalidate];
}
- (void)removeReactSubview:(UIView *)subview
{
[super removeReactSubview:subview];
[self invalidate];
}
- (void)didUpdateReactSubviews
{
// Do nothing, as subviews are inserted by insertReactSubview:
}
- (void)setOpacity:(CGFloat)opacity
{
[self invalidate];
_opacity = opacity;
}
- (void)setTransform:(CGAffineTransform)transform
{
[self invalidate];
super.transform = transform;
}
- (void)invalidate
{
id<ARTContainer> container = (id<ARTContainer>)self.superview;
[container invalidate];
}
- (void)renderTo:(CGContextRef)context
{
if (self.opacity <= 0) {
// Nothing to paint
return;
}
if (self.opacity >= 1) {
// Just paint at full opacity
CGContextSaveGState(context);
CGContextConcatCTM(context, self.transform);
CGContextSetAlpha(context, 1);
[self renderLayerTo:context];
CGContextRestoreGState(context);
return;
}
// This needs to be painted on a layer before being composited.
CGContextSaveGState(context);
CGContextConcatCTM(context, self.transform);
CGContextSetAlpha(context, self.opacity);
CGContextBeginTransparencyLayer(context, NULL);
[self renderLayerTo:context];
CGContextEndTransparencyLayer(context);
CGContextRestoreGState(context);
}
- (void)renderLayerTo:(CGContextRef)context
{
// abstract
}
@end
+23
View File
@@ -0,0 +1,23 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <Foundation/Foundation.h>
#import "ARTBrush.h"
#import "ARTCGFloatArray.h"
#import "ARTNode.h"
@interface ARTRenderable : ARTNode
@property (nonatomic, strong) ARTBrush *fill;
@property (nonatomic, assign) CGColorRef stroke;
@property (nonatomic, assign) CGFloat strokeWidth;
@property (nonatomic, assign) CGLineCap strokeCap;
@property (nonatomic, assign) CGLineJoin strokeJoin;
@property (nonatomic, assign) ARTCGFloatArray strokeDash;
@end
+87
View File
@@ -0,0 +1,87 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <React/ARTRenderable.h>
@implementation ARTRenderable
- (void)setFill:(ARTBrush *)fill
{
[self invalidate];
_fill = fill;
}
- (void)setStroke:(CGColorRef)stroke
{
if (stroke == _stroke) {
return;
}
[self invalidate];
CGColorRelease(_stroke);
_stroke = CGColorRetain(stroke);
}
- (void)setStrokeWidth:(CGFloat)strokeWidth
{
[self invalidate];
_strokeWidth = strokeWidth;
}
- (void)setStrokeCap:(CGLineCap)strokeCap
{
[self invalidate];
_strokeCap = strokeCap;
}
- (void)setStrokeJoin:(CGLineJoin)strokeJoin
{
[self invalidate];
_strokeJoin = strokeJoin;
}
- (void)setStrokeDash:(ARTCGFloatArray)strokeDash
{
if (strokeDash.array == _strokeDash.array) {
return;
}
if (_strokeDash.array) {
free(_strokeDash.array);
}
[self invalidate];
_strokeDash = strokeDash;
}
- (void)dealloc
{
CGColorRelease(_stroke);
if (_strokeDash.array) {
free(_strokeDash.array);
}
}
- (void)renderTo:(CGContextRef)context
{
if (self.opacity <= 0 || self.opacity >= 1 || (self.fill && self.stroke)) {
// If we have both fill and stroke, we will need to paint this using normal compositing
[super renderTo: context];
return;
}
// This is a terminal with only one painting. Therefore we don't need to paint this
// off-screen. We can just composite it straight onto the buffer.
CGContextSaveGState(context);
CGContextConcatCTM(context, self.transform);
CGContextSetAlpha(context, self.opacity);
[self renderLayerTo:context];
CGContextRestoreGState(context);
}
- (void)renderLayerTo:(CGContextRef)context
{
// abstract
}
@end
+84
View File
@@ -0,0 +1,84 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
*/
'use strict';
// TODO: Move this into an ART mode called "serialized" or something
const Class = require('art/core/class.js');
const Path = require('art/core/path.js');
const MOVE_TO = 0;
const CLOSE = 1;
const LINE_TO = 2;
const CURVE_TO = 3;
const ARC = 4;
const SerializablePath = Class(Path, {
initialize: function(path) {
this.reset();
if (path instanceof SerializablePath) {
this.path = path.path.slice(0);
} else if (path) {
if (path.applyToPath) {
path.applyToPath(this);
} else {
this.push(path);
}
}
},
onReset: function() {
this.path = [];
},
onMove: function(sx, sy, x, y) {
this.path.push(MOVE_TO, x, y);
},
onLine: function(sx, sy, x, y) {
this.path.push(LINE_TO, x, y);
},
onBezierCurve: function(sx, sy, p1x, p1y, p2x, p2y, x, y) {
this.path.push(CURVE_TO, p1x, p1y, p2x, p2y, x, y);
},
_arcToBezier: Path.prototype.onArc,
onArc: function(sx, sy, ex, ey, cx, cy, rx, ry, sa, ea, ccw, rotation) {
if (rx !== ry || rotation) {
return this._arcToBezier(
sx,
sy,
ex,
ey,
cx,
cy,
rx,
ry,
sa,
ea,
ccw,
rotation,
);
}
this.path.push(ARC, cx, cy, rx, sa, ea, ccw ? 0 : 1);
},
onClose: function() {
this.path.push(CLOSE);
},
toJSON: function() {
return this.path;
},
});
module.exports = SerializablePath;
+16
View File
@@ -0,0 +1,16 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <Foundation/Foundation.h>
#import "ARTRenderable.h"
@interface ARTShape : ARTRenderable
@property (nonatomic, assign) CGPathRef d;
@end
+66
View File
@@ -0,0 +1,66 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <React/ARTShape.h>
@implementation ARTShape
- (void)setD:(CGPathRef)d
{
if (d == _d) {
return;
}
[self invalidate];
CGPathRelease(_d);
_d = CGPathRetain(d);
}
- (void)dealloc
{
CGPathRelease(_d);
}
- (void)renderLayerTo:(CGContextRef)context
{
if ((!self.fill && !self.stroke) || !self.d) {
return;
}
CGPathDrawingMode mode = kCGPathStroke;
if (self.fill) {
if ([self.fill applyFillColor:context]) {
mode = kCGPathFill;
} else {
CGContextSaveGState(context);
CGContextAddPath(context, self.d);
CGContextClip(context);
[self.fill paint:context];
CGContextRestoreGState(context);
if (!self.stroke) {
return;
}
}
}
if (self.stroke) {
CGContextSetStrokeColorWithColor(context, self.stroke);
CGContextSetLineWidth(context, self.strokeWidth);
CGContextSetLineCap(context, self.strokeCap);
CGContextSetLineJoin(context, self.strokeJoin);
ARTCGFloatArray dash = self.strokeDash;
if (dash.count) {
CGContextSetLineDash(context, 0, dash.array, dash.count);
}
if (mode == kCGPathFill) {
mode = kCGPathFillStroke;
}
}
CGContextAddPath(context, self.d);
CGContextDrawPath(context, mode);
}
@end
+14
View File
@@ -0,0 +1,14 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <UIKit/UIKit.h>
#import "ARTContainer.h"
@interface ARTSurfaceView : UIView <ARTContainer>
@end
+56
View File
@@ -0,0 +1,56 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <React/ARTSurfaceView.h>
#import <React/RCTLog.h>
#import <React/ARTNode.h>
@implementation ARTSurfaceView
- (instancetype)initWithFrame:(CGRect)frame
{
if (self = [super initWithFrame:frame]) {
self.opaque = NO;
}
return self;
}
- (void)insertReactSubview:(UIView *)subview atIndex:(NSInteger)atIndex
{
[super insertReactSubview:subview atIndex:atIndex];
[self insertSubview:subview atIndex:atIndex];
[self invalidate];
}
- (void)removeReactSubview:(UIView *)subview
{
[super removeReactSubview:subview];
[self invalidate];
}
- (void)didUpdateReactSubviews
{
// Do nothing, as subviews are inserted by insertReactSubview:
}
- (void)invalidate
{
[self setNeedsDisplay];
}
- (void)drawRect:(CGRect)rect
{
CGContextRef context = UIGraphicsGetCurrentContext();
for (ARTNode *node in self.subviews) {
[node renderTo:context];
}
}
@end
+18
View File
@@ -0,0 +1,18 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <Foundation/Foundation.h>
#import "ARTRenderable.h"
#import "ARTTextFrame.h"
@interface ARTText : ARTRenderable
@property (nonatomic, assign) CTTextAlignment alignment;
@property (nonatomic, assign) ARTTextFrame textFrame;
@end
+125
View File
@@ -0,0 +1,125 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <React/ARTText.h>
#import <CoreText/CoreText.h>
@implementation ARTText
- (void)setAlignment:(CTTextAlignment)alignment
{
[self invalidate];
_alignment = alignment;
}
static void ARTFreeTextFrame(ARTTextFrame frame)
{
if (frame.count) {
// We must release each line before freeing up this struct
for (int i = 0; i < frame.count; i++) {
CFRelease(frame.lines[i]);
}
free(frame.lines);
free(frame.widths);
}
}
- (void)setTextFrame:(ARTTextFrame)frame
{
if (frame.lines != _textFrame.lines) {
ARTFreeTextFrame(_textFrame);
}
[self invalidate];
_textFrame = frame;
}
- (void)dealloc
{
ARTFreeTextFrame(_textFrame);
}
- (void)renderLayerTo:(CGContextRef)context
{
ARTTextFrame frame = self.textFrame;
if ((!self.fill && !self.stroke) || !frame.count) {
return;
}
// to-do: draw along a path
CGTextDrawingMode mode = kCGTextStroke;
if (self.fill) {
if ([self.fill applyFillColor:context]) {
mode = kCGTextFill;
} else {
for (int i = 0; i < frame.count; i++) {
CGContextSaveGState(context);
// Inverse the coordinate space since CoreText assumes a bottom-up coordinate space
CGContextScaleCTM(context, 1.0, -1.0);
CGContextSetTextDrawingMode(context, kCGTextClip);
[self renderLineTo:context atIndex:i];
// Inverse the coordinate space back to the original before filling
CGContextScaleCTM(context, 1.0, -1.0);
[self.fill paint:context];
// Restore the state so that the next line can be clipped separately
CGContextRestoreGState(context);
}
if (!self.stroke) {
return;
}
}
}
if (self.stroke) {
CGContextSetStrokeColorWithColor(context, self.stroke);
CGContextSetLineWidth(context, self.strokeWidth);
CGContextSetLineCap(context, self.strokeCap);
CGContextSetLineJoin(context, self.strokeJoin);
ARTCGFloatArray dash = self.strokeDash;
if (dash.count) {
CGContextSetLineDash(context, 0, dash.array, dash.count);
}
if (mode == kCGTextFill) {
mode = kCGTextFillStroke;
}
}
CGContextSetTextDrawingMode(context, mode);
// Inverse the coordinate space since CoreText assumes a bottom-up coordinate space
CGContextScaleCTM(context, 1.0, -1.0);
for (int i = 0; i < frame.count; i++) {
[self renderLineTo:context atIndex:i];
}
}
- (void)renderLineTo:(CGContextRef)context atIndex:(int)index
{
ARTTextFrame frame = self.textFrame;
CGFloat shift;
switch (self.alignment) {
case kCTTextAlignmentRight:
shift = frame.widths[index];
break;
case kCTTextAlignmentCenter:
shift = (frame.widths[index] / 2);
break;
default:
shift = 0;
break;
}
// We should consider snapping this shift to device pixels to improve rendering quality
// when a line has subpixel width.
CGContextSetTextPosition(context, -shift, -frame.baseLine - frame.lineHeight * index);
CTLineRef line = frame.lines[index];
CTLineDraw(line, context);
}
@end
+23
View File
@@ -0,0 +1,23 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <CoreText/CoreText.h>
// A little helper to make sure we have a set of lines including width ready for use.
// We assume that we will only this in one place so no reference counting is necessary.
// Needs to be freed when deallocated.
// This is fragile since this relies on these values not getting reused. Consider
// wrapping these in an Obj-C class or some ARC hackery to get refcounting.
typedef struct {
size_t count;
CGFloat baseLine; // Distance from the origin to the base line of the first line
CGFloat lineHeight; // Distance between lines
CTLineRef *lines;
CGFloat *widths; // Width of each line
} ARTTextFrame;
+33
View File
@@ -0,0 +1,33 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <CoreGraphics/CoreGraphics.h>
#import <Foundation/Foundation.h>
@interface ARTBrush : NSObject
/* @abstract */
- (instancetype)initWithArray:(NSArray *)data NS_DESIGNATED_INITIALIZER;
/**
* For certain brushes we can fast path a combined fill and stroke.
* For those brushes we override applyFillColor which sets the fill
* color to be used by those batch paints. Those return YES.
* We can't batch gradient painting in CoreGraphics, so those will
* return NO and paint gets called instead.
* @abstract
*/
- (BOOL)applyFillColor:(CGContextRef)context;
/**
* paint fills the context with a brush. The context is assumed to
* be clipped.
* @abstract
*/
- (void)paint:(CGContextRef)context;
@end
+31
View File
@@ -0,0 +1,31 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <React/ARTBrush.h>
#import <React/RCTDefines.h>
@implementation ARTBrush
- (instancetype)initWithArray:(NSArray *)data
{
return [super init];
}
RCT_NOT_IMPLEMENTED(- (instancetype)init)
- (BOOL)applyFillColor:(CGContextRef)context
{
return NO;
}
- (void)paint:(CGContextRef)context
{
// abstract
}
@end
+12
View File
@@ -0,0 +1,12 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "ARTBrush.h"
@interface ARTLinearGradient : ARTBrush
@end
+48
View File
@@ -0,0 +1,48 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <React/ARTLinearGradient.h>
#import <React/RCTLog.h>
#import "RCTConvert+ART.h"
@implementation ARTLinearGradient
{
CGGradientRef _gradient;
CGPoint _startPoint;
CGPoint _endPoint;
}
- (instancetype)initWithArray:(NSArray<NSNumber *> *)array
{
if ((self = [super initWithArray:array])) {
if (array.count < 5) {
RCTLogError(@"-[%@ %@] expects 5 elements, received %@",
self.class, NSStringFromSelector(_cmd), array);
return nil;
}
_startPoint = [RCTConvert CGPoint:array offset:1];
_endPoint = [RCTConvert CGPoint:array offset:3];
_gradient = CGGradientRetain([RCTConvert CGGradient:array offset:5]);
}
return self;
}
- (void)dealloc
{
CGGradientRelease(_gradient);
}
- (void)paint:(CGContextRef)context
{
CGGradientDrawingOptions extendOptions =
kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation;
CGContextDrawLinearGradient(context, _gradient, _startPoint, _endPoint, extendOptions);
}
@end
+12
View File
@@ -0,0 +1,12 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "ARTBrush.h"
@interface ARTPattern : ARTBrush
@end
+49
View File
@@ -0,0 +1,49 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <React/ARTPattern.h>
#import <React/RCTLog.h>
#import "RCTConvert+ART.h"
@implementation ARTPattern
{
CGImageRef _image;
CGRect _rect;
}
- (instancetype)initWithArray:(NSArray<id /* imagesource + numbers */> *)array
{
if ((self = [super initWithArray:array])) {
if (array.count < 6) {
RCTLogError(@"-[%@ %@] expects 6 elements, received %@",
self.class, NSStringFromSelector(_cmd), array);
return nil;
}
_image = CGImageRetain([RCTConvert CGImage:array[1]]);
_rect = [RCTConvert CGRect:array offset:2];
}
return self;
}
- (void)dealloc
{
CGImageRelease(_image);
}
// Note: This could use applyFillColor with a pattern. This could be more efficient but
// to do that, we need to calculate our own user space CTM.
- (void)paint:(CGContextRef)context
{
CGContextDrawTiledImage(context, _rect, _image);
}
@end
+12
View File
@@ -0,0 +1,12 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "ARTBrush.h"
@interface ARTRadialGradient : ARTBrush
@end
+55
View File
@@ -0,0 +1,55 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <React/ARTRadialGradient.h>
#import <React/RCTLog.h>
#import "RCTConvert+ART.h"
@implementation ARTRadialGradient
{
CGGradientRef _gradient;
CGPoint _focusPoint;
CGPoint _centerPoint;
CGFloat _radius;
CGFloat _radiusRatio;
}
- (instancetype)initWithArray:(NSArray<NSNumber *> *)array
{
if ((self = [super initWithArray:array])) {
if (array.count < 7) {
RCTLogError(@"-[%@ %@] expects 7 elements, received %@",
self.class, NSStringFromSelector(_cmd), array);
return nil;
}
_radius = [RCTConvert CGFloat:array[3]];
_radiusRatio = [RCTConvert CGFloat:array[4]] / _radius;
_focusPoint.x = [RCTConvert CGFloat:array[1]];
_focusPoint.y = [RCTConvert CGFloat:array[2]] / _radiusRatio;
_centerPoint.x = [RCTConvert CGFloat:array[5]];
_centerPoint.y = [RCTConvert CGFloat:array[6]] / _radiusRatio;
_gradient = CGGradientRetain([RCTConvert CGGradient:array offset:7]);
}
return self;
}
- (void)dealloc
{
CGGradientRelease(_gradient);
}
- (void)paint:(CGContextRef)context
{
CGAffineTransform transform = CGAffineTransformMakeScale(1, _radiusRatio);
CGContextConcatCTM(context, transform);
CGGradientDrawingOptions extendOptions = kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation;
CGContextDrawRadialGradient(context, _gradient, _focusPoint, 0, _centerPoint, _radius, extendOptions);
}
@end
+12
View File
@@ -0,0 +1,12 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "ARTBrush.h"
@interface ARTSolidColor : ARTBrush
@end
+38
View File
@@ -0,0 +1,38 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <React/ARTSolidColor.h>
#import <React/RCTLog.h>
#import "RCTConvert+ART.h"
@implementation ARTSolidColor
{
CGColorRef _color;
}
- (instancetype)initWithArray:(NSArray<NSNumber *> *)array
{
if ((self = [super initWithArray:array])) {
_color = CGColorRetain([RCTConvert CGColor:array offset:1]);
}
return self;
}
- (void)dealloc
{
CGColorRelease(_color);
}
- (BOOL)applyFillColor:(CGContextRef)context
{
CGContextSetFillColorWithColor(context, _color);
return YES;
}
@end
+29
View File
@@ -0,0 +1,29 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <QuartzCore/QuartzCore.h>
#import <React/RCTConvert.h>
#import "ARTBrush.h"
#import "ARTCGFloatArray.h"
#import "ARTTextFrame.h"
@interface RCTConvert (ART)
+ (CGPathRef)CGPath:(id)json CF_RETURNS_NOT_RETAINED;
+ (CTTextAlignment)CTTextAlignment:(id)json;
+ (ARTTextFrame)ARTTextFrame:(id)json;
+ (ARTCGFloatArray)ARTCGFloatArray:(id)json;
+ (ARTBrush *)ARTBrush:(id)json;
+ (CGPoint)CGPoint:(id)json offset:(NSUInteger)offset;
+ (CGRect)CGRect:(id)json offset:(NSUInteger)offset;
+ (CGColorRef)CGColor:(id)json offset:(NSUInteger)offset CF_RETURNS_NOT_RETAINED;
+ (CGGradientRef)CGGradient:(id)json offset:(NSUInteger)offset CF_RETURNS_NOT_RETAINED;
@end
+222
View File
@@ -0,0 +1,222 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "RCTConvert+ART.h"
#import <React/RCTFont.h>
#import <React/RCTLog.h>
#import <React/ARTLinearGradient.h>
#import <React/ARTPattern.h>
#import <React/ARTRadialGradient.h>
#import <React/ARTSolidColor.h>
@implementation RCTConvert (ART)
+ (CGPathRef)CGPath:(id)json
{
NSArray *arr = [self NSNumberArray:json];
NSUInteger count = [arr count];
#define NEXT_VALUE [self double:arr[i++]]
CGMutablePathRef path = CGPathCreateMutable();
CGPathMoveToPoint(path, NULL, 0, 0);
@try {
NSUInteger i = 0;
while (i < count) {
NSUInteger type = [arr[i++] unsignedIntegerValue];
switch (type) {
case 0:
CGPathMoveToPoint(path, NULL, NEXT_VALUE, NEXT_VALUE);
break;
case 1:
CGPathCloseSubpath(path);
break;
case 2:
CGPathAddLineToPoint(path, NULL, NEXT_VALUE, NEXT_VALUE);
break;
case 3:
CGPathAddCurveToPoint(path, NULL, NEXT_VALUE, NEXT_VALUE, NEXT_VALUE, NEXT_VALUE, NEXT_VALUE, NEXT_VALUE);
break;
case 4:
CGPathAddArc(path, NULL, NEXT_VALUE, NEXT_VALUE, NEXT_VALUE, NEXT_VALUE, NEXT_VALUE, NEXT_VALUE == 0);
break;
default:
RCTLogError(@"Invalid CGPath type %llu at element %llu of %@", (unsigned long long)type, (unsigned long long)i, arr);
CGPathRelease(path);
return NULL;
}
}
}
@catch (NSException *exception) {
RCTLogError(@"Invalid CGPath format: %@", arr);
CGPathRelease(path);
return NULL;
}
return (CGPathRef)CFAutorelease(path);
}
RCT_ENUM_CONVERTER(CTTextAlignment, (@{
@"auto": @(kCTTextAlignmentNatural),
@"left": @(kCTTextAlignmentLeft),
@"center": @(kCTTextAlignmentCenter),
@"right": @(kCTTextAlignmentRight),
@"justify": @(kCTTextAlignmentJustified),
}), kCTTextAlignmentNatural, integerValue)
// This takes a tuple of text lines and a font to generate a CTLine for each text line.
// This prepares everything for rendering a frame of text in ARTText.
+ (ARTTextFrame)ARTTextFrame:(id)json
{
NSDictionary *dict = [self NSDictionary:json];
ARTTextFrame frame;
frame.count = 0;
NSArray *lines = [self NSArray:dict[@"lines"]];
NSUInteger lineCount = [lines count];
if (lineCount == 0) {
return frame;
}
CTFontRef font = (__bridge CTFontRef)[self UIFont:dict[@"font"]];
if (!font) {
return frame;
}
// Create a dictionary for this font
CFDictionaryRef attributes = (__bridge CFDictionaryRef)@{
(NSString *)kCTFontAttributeName:(__bridge id)font,
(NSString *)kCTForegroundColorFromContextAttributeName: @YES
};
// Set up text frame with font metrics
CGFloat size = CTFontGetSize(font);
frame.count = lineCount;
frame.baseLine = size; // estimate base line
frame.lineHeight = size * 1.1; // Base on ART canvas line height estimate
frame.lines = malloc(sizeof(CTLineRef) * lineCount);
frame.widths = malloc(sizeof(CGFloat) * lineCount);
[lines enumerateObjectsUsingBlock:^(NSString *text, NSUInteger i, BOOL *stop) {
CFStringRef string = (__bridge CFStringRef)text;
CFAttributedStringRef attrString = CFAttributedStringCreate(kCFAllocatorDefault, string, attributes);
CTLineRef line = CTLineCreateWithAttributedString(attrString);
CFRelease(attrString);
frame.lines[i] = line;
frame.widths[i] = CTLineGetTypographicBounds(line, NULL, NULL, NULL);
}];
return frame;
}
+ (ARTCGFloatArray)ARTCGFloatArray:(id)json
{
NSArray *arr = [self NSNumberArray:json];
NSUInteger count = arr.count;
ARTCGFloatArray array;
array.count = count;
array.array = NULL;
if (count) {
// Ideally, these arrays should already use the same memory layout.
// In that case we shouldn't need this new malloc.
array.array = malloc(sizeof(CGFloat) * count);
for (NSUInteger i = 0; i < count; i++) {
array.array[i] = [arr[i] doubleValue];
}
}
return array;
}
+ (ARTBrush *)ARTBrush:(id)json
{
NSArray *arr = [self NSArray:json];
NSUInteger type = [self NSUInteger:arr.firstObject];
switch (type) {
case 0: // solid color
// These are probably expensive allocations since it's often the same value.
// We should memoize colors but look ups may be just as expensive.
return [[ARTSolidColor alloc] initWithArray:arr];
case 1: // linear gradient
return [[ARTLinearGradient alloc] initWithArray:arr];
case 2: // radial gradient
return [[ARTRadialGradient alloc] initWithArray:arr];
case 3: // pattern
return [[ARTPattern alloc] initWithArray:arr];
default:
RCTLogError(@"Unknown brush type: %llu", (unsigned long long)type);
return nil;
}
}
+ (CGPoint)CGPoint:(id)json offset:(NSUInteger)offset
{
NSArray *arr = [self NSArray:json];
if (arr.count < offset + 2) {
RCTLogError(@"Too few elements in array (expected at least %llu): %@", (unsigned long long)(2 + offset), arr);
return CGPointZero;
}
return (CGPoint){
[self CGFloat:arr[offset]],
[self CGFloat:arr[offset + 1]],
};
}
+ (CGRect)CGRect:(id)json offset:(NSUInteger)offset
{
NSArray *arr = [self NSArray:json];
if (arr.count < offset + 4) {
RCTLogError(@"Too few elements in array (expected at least %llu): %@", (unsigned long long)(4 + offset), arr);
return CGRectZero;
}
return (CGRect){
{[self CGFloat:arr[offset]], [self CGFloat:arr[offset + 1]]},
{[self CGFloat:arr[offset + 2]], [self CGFloat:arr[offset + 3]]},
};
}
+ (CGColorRef)CGColor:(id)json offset:(NSUInteger)offset
{
NSArray *arr = [self NSArray:json];
if (arr.count < offset + 4) {
RCTLogError(@"Too few elements in array (expected at least %llu): %@", (unsigned long long)(4 + offset), arr);
return NULL;
}
return [self CGColor:[arr subarrayWithRange:(NSRange){offset, 4}]];
}
+ (CGGradientRef)CGGradient:(id)json offset:(NSUInteger)offset
{
NSArray *arr = [self NSArray:json];
if (arr.count < offset) {
RCTLogError(@"Too few elements in array (expected at least %llu): %@", (unsigned long long)offset, arr);
return NULL;
}
arr = [arr subarrayWithRange:(NSRange){offset, arr.count - offset}];
ARTCGFloatArray colorsAndOffsets = [self ARTCGFloatArray:arr];
size_t stops = colorsAndOffsets.count / 5;
CGColorSpaceRef rgb = CGColorSpaceCreateDeviceRGB();
CGGradientRef gradient = CGGradientCreateWithColorComponents(
rgb,
colorsAndOffsets.array,
colorsAndOffsets.array + stops * 4,
stops
);
CGColorSpaceRelease(rgb);
free(colorsAndOffsets.array);
return (CGGradientRef)CFAutorelease(gradient);
}
@end
+34
View File
@@ -0,0 +1,34 @@
# coding: utf-8
# Copyright (c) Facebook, Inc. and its 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 were presumably in.
source[:commit] = `git rev-parse HEAD`.strip
else
source[:tag] = "v#{version}"
end
Pod::Spec.new do |s|
s.name = "React-ART"
s.version = version
s.summary = "A library for drawing vector graphics."
s.homepage = "http://facebook.github.io/react-native/"
s.license = package["license"]
s.author = "Facebook, Inc. and its affiliates"
s.platforms = { :ios => "9.0", :tvos => "9.2" }
s.source = source
s.source_files = "**/*.{m}"
s.preserve_paths = "package.json", "LICENSE", "LICENSE-docs"
s.header_dir = "ART"
s.dependency "React-Core/ARTHeaders", version
end
+612
View File
@@ -0,0 +1,612 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
*/
'use strict';
const Color = require('art/core/color');
const Path = require('./ARTSerializablePath');
const Transform = require('art/core/transform');
const React = require('react');
const PropTypes = require('prop-types');
const ReactNativeViewAttributes = require('../Components/View/ReactNativeViewAttributes');
const createReactNativeComponentClass = require('../Renderer/shims/createReactNativeComponentClass');
const merge = require('../vendor/core/merge');
const invariant = require('invariant');
// Diff Helpers
function arrayDiffer(a, b) {
if (a == null || b == null) {
return true;
}
if (a.length !== b.length) {
return true;
}
for (let i = 0; i < a.length; i++) {
if (a[i] !== b[i]) {
return true;
}
}
return false;
}
function fontAndLinesDiffer(a, b) {
if (a === b) {
return false;
}
if (a.font !== b.font) {
if (a.font === null) {
return true;
}
if (b.font === null) {
return true;
}
if (
a.font.fontFamily !== b.font.fontFamily ||
a.font.fontSize !== b.font.fontSize ||
a.font.fontWeight !== b.font.fontWeight ||
a.font.fontStyle !== b.font.fontStyle
) {
return true;
}
}
return arrayDiffer(a.lines, b.lines);
}
// Native Attributes
const SurfaceViewAttributes = merge(ReactNativeViewAttributes.UIView, {
// This should contain pixel information such as width, height and
// resolution to know what kind of buffer needs to be allocated.
// Currently we rely on UIViews and style to figure that out.
});
const NodeAttributes = {
transform: {diff: arrayDiffer},
opacity: true,
};
const GroupAttributes = merge(NodeAttributes, {
clipping: {diff: arrayDiffer},
});
const RenderableAttributes = merge(NodeAttributes, {
fill: {diff: arrayDiffer},
stroke: {diff: arrayDiffer},
strokeWidth: true,
strokeCap: true,
strokeJoin: true,
strokeDash: {diff: arrayDiffer},
});
const ShapeAttributes = merge(RenderableAttributes, {
d: {diff: arrayDiffer},
});
const TextAttributes = merge(RenderableAttributes, {
alignment: true,
frame: {diff: fontAndLinesDiffer},
path: {diff: arrayDiffer},
});
// Native Components
const NativeSurfaceView = createReactNativeComponentClass(
'ARTSurfaceView',
() => ({
validAttributes: SurfaceViewAttributes,
uiViewClassName: 'ARTSurfaceView',
}),
);
const NativeGroup = createReactNativeComponentClass('ARTGroup', () => ({
validAttributes: GroupAttributes,
uiViewClassName: 'ARTGroup',
}));
const NativeShape = createReactNativeComponentClass('ARTShape', () => ({
validAttributes: ShapeAttributes,
uiViewClassName: 'ARTShape',
}));
const NativeText = createReactNativeComponentClass('ARTText', () => ({
validAttributes: TextAttributes,
uiViewClassName: 'ARTText',
}));
// Utilities
function childrenAsString(children) {
if (!children) {
return '';
}
if (typeof children === 'string') {
return children;
}
if (children.length) {
return children.join('\n');
}
return '';
}
// Surface - Root node of all ART
class Surface extends React.Component {
static childContextTypes = {
isInSurface: PropTypes.bool,
};
getChildContext() {
return {isInSurface: true};
}
render() {
const height = extractNumber(this.props.height, 0);
const width = extractNumber(this.props.width, 0);
return (
<NativeSurfaceView style={[this.props.style, {height, width}]}>
{this.props.children}
</NativeSurfaceView>
);
}
}
// Node Props
// TODO: The desktop version of ART has title and cursor. We should have
// accessibility support here too even though hovering doesn't work.
function extractNumber(value, defaultValue) {
if (value == null) {
return defaultValue;
}
return +value;
}
const pooledTransform = new Transform();
function extractTransform(props) {
const scaleX =
props.scaleX != null ? props.scaleX : props.scale != null ? props.scale : 1;
const scaleY =
props.scaleY != null ? props.scaleY : props.scale != null ? props.scale : 1;
pooledTransform
.transformTo(1, 0, 0, 1, 0, 0)
.move(props.x || 0, props.y || 0)
.rotate(props.rotation || 0, props.originX, props.originY)
.scale(scaleX, scaleY, props.originX, props.originY);
if (props.transform != null) {
pooledTransform.transform(props.transform);
}
return [
pooledTransform.xx,
pooledTransform.yx,
pooledTransform.xy,
pooledTransform.yy,
pooledTransform.x,
pooledTransform.y,
];
}
function extractOpacity(props) {
// TODO: visible === false should also have no hit detection
if (props.visible === false) {
return 0;
}
if (props.opacity == null) {
return 1;
}
return +props.opacity;
}
// Groups
// Note: ART has a notion of width and height on Group but AFAIK it's a noop in
// ReactART.
class Group extends React.Component {
static contextTypes = {
isInSurface: PropTypes.bool.isRequired,
};
render() {
const props = this.props;
invariant(
this.context.isInSurface,
'ART: <Group /> must be a child of a <Surface />',
);
return (
<NativeGroup
opacity={extractOpacity(props)}
transform={extractTransform(props)}>
{this.props.children}
</NativeGroup>
);
}
}
class ClippingRectangle extends React.Component {
render() {
const props = this.props;
const x = extractNumber(props.x, 0);
const y = extractNumber(props.y, 0);
const w = extractNumber(props.width, 0);
const h = extractNumber(props.height, 0);
const clipping = [x, y, w, h];
// The current clipping API requires x and y to be ignored in the transform
const propsExcludingXAndY = merge(props);
delete propsExcludingXAndY.x;
delete propsExcludingXAndY.y;
return (
<NativeGroup
clipping={clipping}
opacity={extractOpacity(props)}
transform={extractTransform(propsExcludingXAndY)}>
{this.props.children}
</NativeGroup>
);
}
}
// Renderables
const SOLID_COLOR = 0;
const LINEAR_GRADIENT = 1;
const RADIAL_GRADIENT = 2;
const PATTERN = 3;
function insertColorIntoArray(color, targetArray, atIndex) {
const c = new Color(color);
targetArray[atIndex + 0] = c.red / 255;
targetArray[atIndex + 1] = c.green / 255;
targetArray[atIndex + 2] = c.blue / 255;
targetArray[atIndex + 3] = c.alpha;
}
function insertColorsIntoArray(stops, targetArray, atIndex) {
let i = 0;
if ('length' in stops) {
while (i < stops.length) {
insertColorIntoArray(stops[i], targetArray, atIndex + i * 4);
i++;
}
} else {
for (const offset in stops) {
insertColorIntoArray(stops[offset], targetArray, atIndex + i * 4);
i++;
}
}
return atIndex + i * 4;
}
function insertOffsetsIntoArray(stops, targetArray, atIndex, multi, reverse) {
let offsetNumber;
let i = 0;
if ('length' in stops) {
while (i < stops.length) {
offsetNumber = (i / (stops.length - 1)) * multi;
targetArray[atIndex + i] = reverse ? 1 - offsetNumber : offsetNumber;
i++;
}
} else {
for (const offsetString in stops) {
offsetNumber = +offsetString * multi;
targetArray[atIndex + i] = reverse ? 1 - offsetNumber : offsetNumber;
i++;
}
}
return atIndex + i;
}
function insertColorStopsIntoArray(stops, targetArray, atIndex) {
const lastIndex = insertColorsIntoArray(stops, targetArray, atIndex);
insertOffsetsIntoArray(stops, targetArray, lastIndex, 1, false);
}
function insertDoubleColorStopsIntoArray(stops, targetArray, atIndex) {
let lastIndex = insertColorsIntoArray(stops, targetArray, atIndex);
lastIndex = insertColorsIntoArray(stops, targetArray, lastIndex);
lastIndex = insertOffsetsIntoArray(stops, targetArray, lastIndex, 0.5, false);
insertOffsetsIntoArray(stops, targetArray, lastIndex, 0.5, true);
}
function applyBoundingBoxToBrushData(brushData, props) {
const type = brushData[0];
const width = +props.width;
const height = +props.height;
if (type === LINEAR_GRADIENT) {
brushData[1] *= width;
brushData[2] *= height;
brushData[3] *= width;
brushData[4] *= height;
} else if (type === RADIAL_GRADIENT) {
brushData[1] *= width;
brushData[2] *= height;
brushData[3] *= width;
brushData[4] *= height;
brushData[5] *= width;
brushData[6] *= height;
} else if (type === PATTERN) {
// todo
}
}
function extractBrush(colorOrBrush, props) {
if (colorOrBrush == null) {
return null;
}
if (colorOrBrush._brush) {
if (colorOrBrush._bb) {
// The legacy API for Gradients allow for the bounding box to be used
// as a convenience for specifying gradient positions. This should be
// deprecated. It's not properly implemented in canvas mode. ReactART
// doesn't handle update to the bounding box correctly. That's why we
// mutate this so that if it's reused, we reuse the same resolved box.
applyBoundingBoxToBrushData(colorOrBrush._brush, props);
colorOrBrush._bb = false;
}
return colorOrBrush._brush;
}
const c = new Color(colorOrBrush);
return [SOLID_COLOR, c.red / 255, c.green / 255, c.blue / 255, c.alpha];
}
function extractColor(color) {
if (color == null) {
return null;
}
const c = new Color(color);
return [c.red / 255, c.green / 255, c.blue / 255, c.alpha];
}
function extractStrokeCap(strokeCap) {
switch (strokeCap) {
case 'butt':
return 0;
case 'square':
return 2;
default:
return 1; // round
}
}
function extractStrokeJoin(strokeJoin) {
switch (strokeJoin) {
case 'miter':
return 0;
case 'bevel':
return 2;
default:
return 1; // round
}
}
// Shape
// Note: ART has a notion of width and height on Shape but AFAIK it's a noop in
// ReactART.
export type ShapeProps = {|
fill?: mixed,
stroke?: mixed,
strokeCap?: mixed,
strokeDash?: mixed,
strokeJoin?: mixed,
strokeWidth?: mixed,
x?: number,
y?: number,
opacity?: mixed,
|};
class Shape extends React.Component<ShapeProps> {
render() {
const props = this.props;
const path = props.d || childrenAsString(props.children);
const d = (path instanceof Path ? path : new Path(path)).toJSON();
return (
<NativeShape
fill={extractBrush(props.fill, props)}
opacity={extractOpacity(props)}
stroke={extractColor(props.stroke)}
strokeCap={extractStrokeCap(props.strokeCap)}
strokeDash={props.strokeDash || null}
strokeJoin={extractStrokeJoin(props.strokeJoin)}
strokeWidth={extractNumber(props.strokeWidth, 1)}
transform={extractTransform(props)}
d={d}
/>
);
}
}
// Text
const cachedFontObjectsFromString = {};
const fontFamilyPrefix = /^[\s"']*/;
const fontFamilySuffix = /[\s"']*$/;
function extractSingleFontFamily(fontFamilyString) {
// ART on the web allows for multiple font-families to be specified.
// For compatibility, we extract the first font-family, hoping
// we'll get a match.
return fontFamilyString
.split(',')[0]
.replace(fontFamilyPrefix, '')
.replace(fontFamilySuffix, '');
}
function parseFontString(font) {
if (cachedFontObjectsFromString.hasOwnProperty(font)) {
return cachedFontObjectsFromString[font];
}
const regexp = /^\s*((?:(?:normal|bold|italic)\s+)*)(?:(\d+(?:\.\d+)?)[ptexm\%]*(?:\s*\/.*?)?\s+)?\s*\"?([^\"]*)/i;
const match = regexp.exec(font);
if (!match) {
return null;
}
const fontFamily = extractSingleFontFamily(match[3]);
const fontSize = +match[2] || 12;
const isBold = /bold/.exec(match[1]);
const isItalic = /italic/.exec(match[1]);
cachedFontObjectsFromString[font] = {
fontFamily: fontFamily,
fontSize: fontSize,
fontWeight: isBold ? 'bold' : 'normal',
fontStyle: isItalic ? 'italic' : 'normal',
};
return cachedFontObjectsFromString[font];
}
function extractFont(font) {
if (font == null) {
return null;
}
if (typeof font === 'string') {
return parseFontString(font);
}
const fontFamily = extractSingleFontFamily(font.fontFamily);
const fontSize = +font.fontSize || 12;
const fontWeight =
font.fontWeight != null ? font.fontWeight.toString() : '400';
return {
// Normalize
fontFamily: fontFamily,
fontSize: fontSize,
fontWeight: fontWeight,
fontStyle: font.fontStyle,
};
}
const newLine = /\n/g;
function extractFontAndLines(font, text) {
return {font: extractFont(font), lines: text.split(newLine)};
}
function extractAlignment(alignment) {
switch (alignment) {
case 'right':
return 1;
case 'center':
return 2;
default:
return 0;
}
}
class Text extends React.Component {
render() {
const props = this.props;
const path = props.path;
const textPath = path
? (path instanceof Path ? path : new Path(path)).toJSON()
: null;
const textFrame = extractFontAndLines(
props.font,
childrenAsString(props.children),
);
return (
<NativeText
fill={extractBrush(props.fill, props)}
opacity={extractOpacity(props)}
stroke={extractColor(props.stroke)}
strokeCap={extractStrokeCap(props.strokeCap)}
strokeDash={props.strokeDash || null}
strokeJoin={extractStrokeJoin(props.strokeJoin)}
strokeWidth={extractNumber(props.strokeWidth, 1)}
transform={extractTransform(props)}
alignment={extractAlignment(props.alignment)}
frame={textFrame}
path={textPath}
/>
);
}
}
// Declarative fill type objects - API design not finalized
function LinearGradient(stops, x1, y1, x2, y2) {
const type = LINEAR_GRADIENT;
if (arguments.length < 5) {
const angle = ((x1 == null ? 270 : x1) * Math.PI) / 180;
let x = Math.cos(angle);
let y = -Math.sin(angle);
const l = (Math.abs(x) + Math.abs(y)) / 2;
x *= l;
y *= l;
x1 = 0.5 - x;
x2 = 0.5 + x;
y1 = 0.5 - y;
y2 = 0.5 + y;
this._bb = true;
} else {
this._bb = false;
}
const brushData = [type, +x1, +y1, +x2, +y2];
insertColorStopsIntoArray(stops, brushData, 5);
this._brush = brushData;
}
function RadialGradient(stops, fx, fy, rx, ry, cx, cy) {
if (ry == null) {
ry = rx;
}
if (cx == null) {
cx = fx;
}
if (cy == null) {
cy = fy;
}
if (fx == null) {
// As a convenience we allow the whole radial gradient to cover the
// bounding box. We should consider dropping this API.
fx = fy = rx = ry = cx = cy = 0.5;
this._bb = true;
} else {
this._bb = false;
}
// The ART API expects the radial gradient to be repeated at the edges.
// To simulate this we render the gradient twice as large and add double
// color stops. Ideally this API would become more restrictive so that this
// extra work isn't needed.
const brushData = [RADIAL_GRADIENT, +fx, +fy, +rx * 2, +ry * 2, +cx, +cy];
insertDoubleColorStopsIntoArray(stops, brushData, 7);
this._brush = brushData;
}
function Pattern(url, width, height, left, top) {
this._brush = [PATTERN, url, +left || 0, +top || 0, +width, +height];
}
const ReactART = {
LinearGradient: LinearGradient,
RadialGradient: RadialGradient,
Pattern: Pattern,
Transform: Transform,
Path: Path,
Surface: Surface,
Group: Group,
ClippingRectangle: ClippingRectangle,
Shape: Shape,
Text: Text,
};
module.exports = ReactART;
@@ -0,0 +1,12 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "ARTNodeManager.h"
@interface ARTGroupManager : ARTNodeManager
@end
@@ -0,0 +1,24 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <React/ARTGroupManager.h>
#import <React/ARTGroup.h>
#import "RCTConvert+ART.h"
@implementation ARTGroupManager
RCT_EXPORT_MODULE()
- (ARTNode *)node
{
return [ARTGroup new];
}
RCT_EXPORT_VIEW_PROPERTY(clipping, CGRect)
@end
@@ -0,0 +1,16 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <React/RCTViewManager.h>
@class ARTNode;
@interface ARTNodeManager : RCTViewManager
- (ARTNode *)node;
@end
@@ -0,0 +1,34 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <React/ARTNodeManager.h>
#import <React/ARTNode.h>
@implementation ARTNodeManager
RCT_EXPORT_MODULE()
- (ARTNode *)node
{
return [ARTNode new];
}
- (UIView *)view
{
return [self node];
}
- (RCTShadowView *)shadowView
{
return nil;
}
RCT_EXPORT_VIEW_PROPERTY(opacity, CGFloat)
RCT_EXPORT_VIEW_PROPERTY(transform, CGAffineTransform)
@end
@@ -0,0 +1,15 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "ARTNodeManager.h"
#import "ARTRenderable.h"
@interface ARTRenderableManager : ARTNodeManager
- (ARTRenderable *)node;
@end
@@ -0,0 +1,28 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <React/ARTRenderableManager.h>
#import "RCTConvert+ART.h"
@implementation ARTRenderableManager
RCT_EXPORT_MODULE()
- (ARTRenderable *)node
{
return [ARTRenderable new];
}
RCT_EXPORT_VIEW_PROPERTY(strokeWidth, CGFloat)
RCT_EXPORT_VIEW_PROPERTY(strokeCap, CGLineCap)
RCT_EXPORT_VIEW_PROPERTY(strokeJoin, CGLineJoin)
RCT_EXPORT_VIEW_PROPERTY(fill, ARTBrush)
RCT_EXPORT_VIEW_PROPERTY(stroke, CGColor)
RCT_EXPORT_VIEW_PROPERTY(strokeDash, ARTCGFloatArray)
@end
@@ -0,0 +1,12 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "ARTRenderableManager.h"
@interface ARTShapeManager : ARTRenderableManager
@end
@@ -0,0 +1,24 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <React/ARTShapeManager.h>
#import <React/ARTShape.h>
#import "RCTConvert+ART.h"
@implementation ARTShapeManager
RCT_EXPORT_MODULE()
- (ARTRenderable *)node
{
return [ARTShape new];
}
RCT_EXPORT_VIEW_PROPERTY(d, CGPath)
@end
@@ -0,0 +1,12 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <React/RCTViewManager.h>
@interface ARTSurfaceViewManager : RCTViewManager
@end
@@ -0,0 +1,21 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <React/ARTSurfaceViewManager.h>
#import <React/ARTSurfaceView.h>
@implementation ARTSurfaceViewManager
RCT_EXPORT_MODULE()
- (UIView *)view
{
return [ARTSurfaceView new];
}
@end
@@ -0,0 +1,12 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "ARTRenderableManager.h"
@interface ARTTextManager : ARTRenderableManager
@end
@@ -0,0 +1,25 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <React/ARTTextManager.h>
#import <React/ARTText.h>
#import "RCTConvert+ART.h"
@implementation ARTTextManager
RCT_EXPORT_MODULE()
- (ARTRenderable *)node
{
return [ARTText new];
}
RCT_EXPORT_VIEW_PROPERTY(alignment, CTTextAlignment)
RCT_REMAP_VIEW_PROPERTY(frame, textFrame, ARTTextFrame)
@end
+10 -30
View File
@@ -7,18 +7,17 @@
* @flow
* @format
*/
'use strict';
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';
/**
* Display action sheets and share sheets on iOS.
*
* See https://reactnative.dev/docs/actionsheetios.html
* See http://facebook.github.io/react-native/docs/actionsheetios.html
*/
const ActionSheetIOS = {
/**
@@ -31,24 +30,21 @@ const ActionSheetIOS = {
* - `destructiveButtonIndex` (int or array of ints) - index or indices of destructive buttons in `options`
* - `title` (string) - a title to show above the action sheet
* - `message` (string) - a message to show below the title
* - `disabledButtonIndices` (array of numbers) - a list of button indices which should be disabled
*
* The 'callback' function takes one parameter, the zero-based index
* of the selected item.
*
* See https://reactnative.dev/docs/actionsheetios.html#showactionsheetwithoptions
* See http://facebook.github.io/react-native/docs/actionsheetios.html#showactionsheetwithoptions
*/
showActionSheetWithOptions(
options: {|
+title?: ?string,
+message?: ?string,
+options: Array<string>,
+destructiveButtonIndex?: ?number | ?Array<number>,
+destructiveButtonIndex?: ?number,
+cancelButtonIndex?: ?number,
+anchor?: ?number,
+tintColor?: ColorValue | ProcessedColorValue,
+userInterfaceStyle?: string,
+disabledButtonIndices?: Array<number>,
+tintColor?: number | string,
|},
callback: (buttonIndex: number) => void,
) {
@@ -57,28 +53,12 @@ const ActionSheetIOS = {
'Options must be a valid object',
);
invariant(typeof callback === 'function', 'Must provide a valid callback');
invariant(RCTActionSheetManager, "ActionSheetManager doesn't exist");
invariant(RCTActionSheetManager, "ActionSheetManager does't exist");
const {tintColor, destructiveButtonIndex, ...remainingOptions} = options;
let destructiveButtonIndices = null;
const {tintColor, ...remainingOptions} = options;
if (Array.isArray(destructiveButtonIndex)) {
destructiveButtonIndices = destructiveButtonIndex;
} else if (typeof destructiveButtonIndex === 'number') {
destructiveButtonIndices = [destructiveButtonIndex];
}
const processedTintColor = processColor(tintColor);
invariant(
processedTintColor == null || typeof processedTintColor === 'number',
'Unexpected color given for ActionSheetIOS.showActionSheetWithOptions tintColor',
);
RCTActionSheetManager.showActionSheetWithOptions(
{
...remainingOptions,
tintColor: processedTintColor,
destructiveButtonIndices,
},
{...remainingOptions, tintColor: processColor(tintColor)},
callback,
);
},
@@ -104,7 +84,7 @@ const ActionSheetIOS = {
* - a boolean value signifying success or failure
* - a string that, in the case of success, indicates the method of sharing
*
* See https://reactnative.dev/docs/actionsheetios.html#showshareactionsheetwithoptions
* See http://facebook.github.io/react-native/docs/actionsheetios.html#showshareactionsheetwithoptions
*/
showShareActionSheetWithOptions(
options: Object,
@@ -123,7 +103,7 @@ const ActionSheetIOS = {
typeof successCallback === 'function',
'Must provide a valid successCallback',
);
invariant(RCTActionSheetManager, "ActionSheetManager doesn't exist");
invariant(RCTActionSheetManager, "ActionSheetManager does't exist");
RCTActionSheetManager.showShareActionSheetWithOptions(
{...options, tintColor: processColor(options.tintColor)},
failureCallback,
@@ -8,6 +8,8 @@
* @format
*/
'use strict';
import type {TurboModule} from '../TurboModule/RCTExport';
import * as TurboModuleRegistry from '../TurboModule/TurboModuleRegistry';
@@ -18,12 +20,11 @@ export interface Spec extends TurboModule {
+title?: ?string,
+message?: ?string,
+options: ?Array<string>,
+destructiveButtonIndices?: ?Array<number>,
// Supports Array<number> as well.
+destructiveButtonIndex?: ?number,
+cancelButtonIndex?: ?number,
+anchor?: ?number,
+tintColor?: ?number,
+userInterfaceStyle?: ?string,
+disabledButtonIndices?: Array<number>,
|},
callback: (buttonIndex: number) => void,
) => void;
@@ -35,7 +36,6 @@ export interface Spec extends TurboModule {
+anchor?: ?number,
+tintColor?: ?number,
+excludedActivityTypes?: ?Array<string>,
+userInterfaceStyle?: ?string,
|},
failureCallback: (error: {|
+domain: string,
@@ -1,4 +1,4 @@
/*
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
@@ -0,0 +1,180 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import <React/RCTActionSheetManager.h>
#import <React/RCTBridge.h>
#import <React/RCTConvert.h>
#import <React/RCTLog.h>
#import <React/RCTUIManager.h>
#import <React/RCTUtils.h>
@interface RCTActionSheetManager () <UIActionSheetDelegate>
@end
@implementation RCTActionSheetManager
{
// Use NSMapTable, as UIAlertViews do not implement <NSCopying>
// which is required for NSDictionary keys
NSMapTable *_callbacks;
}
RCT_EXPORT_MODULE()
@synthesize bridge = _bridge;
- (dispatch_queue_t)methodQueue
{
return dispatch_get_main_queue();
}
- (void)presentViewController:(UIViewController *)alertController
onParentViewController:(UIViewController *)parentViewController
anchorViewTag:(NSNumber *)anchorViewTag
{
alertController.modalPresentationStyle = UIModalPresentationPopover;
UIView *sourceView = parentViewController.view;
if (anchorViewTag) {
sourceView = [self.bridge.uiManager viewForReactTag:anchorViewTag];
} else {
alertController.popoverPresentationController.permittedArrowDirections = 0;
}
alertController.popoverPresentationController.sourceView = sourceView;
alertController.popoverPresentationController.sourceRect = sourceView.bounds;
[parentViewController presentViewController:alertController animated:YES completion:nil];
}
RCT_EXPORT_METHOD(showActionSheetWithOptions:(NSDictionary *)options
callback:(RCTResponseSenderBlock)callback)
{
if (RCTRunningInAppExtension()) {
RCTLogError(@"Unable to show action sheet from app extension");
return;
}
if (!_callbacks) {
_callbacks = [NSMapTable strongToStrongObjectsMapTable];
}
NSString *title = [RCTConvert NSString:options[@"title"]];
NSString *message = [RCTConvert NSString:options[@"message"]];
NSArray<NSString *> *buttons = [RCTConvert NSStringArray:options[@"options"]];
NSInteger cancelButtonIndex = options[@"cancelButtonIndex"] ? [RCTConvert NSInteger:options[@"cancelButtonIndex"]] : -1;
NSArray<NSNumber *> *destructiveButtonIndices;
if ([options[@"destructiveButtonIndex"] isKindOfClass:[NSArray class]]) {
destructiveButtonIndices = [RCTConvert NSArray:options[@"destructiveButtonIndex"]];
} else {
NSNumber *destructiveButtonIndex = options[@"destructiveButtonIndex"] ? [RCTConvert NSNumber:options[@"destructiveButtonIndex"]] : @-1;
destructiveButtonIndices = @[destructiveButtonIndex];
}
UIViewController *controller = RCTPresentedViewController();
if (controller == nil) {
RCTLogError(@"Tried to display action sheet but there is no application window. options: %@", options);
return;
}
/*
* The `anchor` option takes a view to set as the anchor for the share
* popup to point to, on iPads running iOS 8. If it is not passed, it
* defaults to centering the share popup on screen without any arrows.
*/
NSNumber *anchorViewTag = [RCTConvert NSNumber:options[@"anchor"]];
UIAlertController *alertController =
[UIAlertController alertControllerWithTitle:title
message:message
preferredStyle:UIAlertControllerStyleActionSheet];
NSInteger index = 0;
for (NSString *option in buttons) {
UIAlertActionStyle style = UIAlertActionStyleDefault;
if ([destructiveButtonIndices containsObject:@(index)]) {
style = UIAlertActionStyleDestructive;
} else if (index == cancelButtonIndex) {
style = UIAlertActionStyleCancel;
}
NSInteger localIndex = index;
[alertController addAction:[UIAlertAction actionWithTitle:option
style:style
handler:^(__unused UIAlertAction *action){
callback(@[@(localIndex)]);
}]];
index++;
}
alertController.view.tintColor = [RCTConvert UIColor:options[@"tintColor"]];
[self presentViewController:alertController onParentViewController:controller anchorViewTag:anchorViewTag];
}
RCT_EXPORT_METHOD(showShareActionSheetWithOptions:(NSDictionary *)options
failureCallback:(RCTResponseErrorBlock)failureCallback
successCallback:(RCTResponseSenderBlock)successCallback)
{
if (RCTRunningInAppExtension()) {
RCTLogError(@"Unable to show action sheet from app extension");
return;
}
NSMutableArray<id> *items = [NSMutableArray array];
NSString *message = [RCTConvert NSString:options[@"message"]];
if (message) {
[items addObject:message];
}
NSURL *URL = [RCTConvert NSURL:options[@"url"]];
if (URL) {
if ([URL.scheme.lowercaseString isEqualToString:@"data"]) {
NSError *error;
NSData *data = [NSData dataWithContentsOfURL:URL
options:(NSDataReadingOptions)0
error:&error];
if (!data) {
failureCallback(error);
return;
}
[items addObject:data];
} else {
[items addObject:URL];
}
}
if (items.count == 0) {
RCTLogError(@"No `url` or `message` to share");
return;
}
UIActivityViewController *shareController = [[UIActivityViewController alloc] initWithActivityItems:items applicationActivities:nil];
NSString *subject = [RCTConvert NSString:options[@"subject"]];
if (subject) {
[shareController setValue:subject forKey:@"subject"];
}
NSArray *excludedActivityTypes = [RCTConvert NSStringArray:options[@"excludedActivityTypes"]];
if (excludedActivityTypes) {
shareController.excludedActivityTypes = excludedActivityTypes;
}
UIViewController *controller = RCTPresentedViewController();
shareController.completionWithItemsHandler = ^(NSString *activityType, BOOL completed, __unused NSArray *returnedItems, NSError *activityError) {
if (activityError) {
failureCallback(activityError);
} else {
successCallback(@[@(completed), RCTNullIfNil(activityType)]);
}
};
NSNumber *anchorViewTag = [RCTConvert NSNumber:options[@"anchor"]];
shareController.view.tintColor = [RCTConvert UIColor:options[@"tintColor"]];
[self presentViewController:shareController onParentViewController:controller anchorViewTag:anchorViewTag];
}
@end
@@ -1,3 +1,4 @@
# coding: utf-8
# Copyright (c) Facebook, Inc. and its affiliates.
#
# This source code is licensed under the MIT license found in the
@@ -11,7 +12,7 @@ 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 were presumably in.
source[:commit] = `git rev-parse HEAD`.strip if system("git rev-parse --git-dir > /dev/null 2>&1")
source[:commit] = `git rev-parse HEAD`.strip
else
source[:tag] = "v#{version}"
end
@@ -20,14 +21,14 @@ Pod::Spec.new do |s|
s.name = "React-RCTActionSheet"
s.version = version
s.summary = "An API for displaying iOS action sheets and share sheets."
s.homepage = "https://reactnative.dev/"
s.documentation_url = "https://reactnative.dev/docs/actionsheetios"
s.homepage = "http://facebook.github.io/react-native/"
s.documentation_url = "https://facebook.github.io/react-native/docs/actionsheetios"
s.license = package["license"]
s.author = "Facebook, Inc. and its affiliates"
s.platforms = { :ios => "11.0" }
s.platforms = { :ios => "9.0", :tvos => "9.2" }
s.source = source
s.source_files = "*.{m}"
s.preserve_paths = "package.json", "LICENSE", "LICENSE-docs"
s.preserve_paths = "package.json", "LICENSE", "LICENSE-docs"
s.header_dir = "RCTActionSheet"
s.dependency "React-Core/RCTActionSheetHeaders", version
+28 -6
View File
@@ -8,8 +8,12 @@
* @flow
*/
'use strict';
import Platform from '../Utilities/Platform';
import type {DialogOptions} from '../NativeModules/specs/NativeDialogManagerAndroid';
import NativeDialogManagerAndroid, {
type DialogOptions,
} from '../NativeModules/specs/NativeDialogManagerAndroid';
import RCTAlertManager from './RCTAlertManager';
export type AlertType =
@@ -22,19 +26,17 @@ export type Buttons = Array<{
text?: string,
onPress?: ?Function,
style?: AlertButtonStyle,
...
}>;
type Options = {
cancelable?: ?boolean,
onDismiss?: ?() => void,
...
};
/**
* Launches an alert dialog with the specified title and message.
*
* See https://reactnative.dev/docs/alert.html
* See http://facebook.github.io/react-native/docs/alert.html
*/
class Alert {
static alert(
@@ -46,8 +48,6 @@ class Alert {
if (Platform.OS === 'ios') {
Alert.prompt(title, message, buttons, 'default');
} else if (Platform.OS === 'android') {
const NativeDialogManagerAndroid = require('../NativeModules/specs/NativeDialogManagerAndroid')
.default;
if (!NativeDialogManagerAndroid) {
return;
}
@@ -109,6 +109,28 @@ class Alert {
keyboardType?: string,
): void {
if (Platform.OS === 'ios') {
if (typeof type === 'function') {
console.warn(
'You passed a callback function as the "type" argument to Alert.prompt(). React Native is ' +
'assuming you want to use the deprecated Alert.prompt(title, defaultValue, buttons, callback) ' +
'signature. The current signature is Alert.prompt(title, message, callbackOrButtons, type, defaultValue, ' +
'keyboardType) and the old syntax will be removed in a future version.',
);
const callback = type;
RCTAlertManager.alertWithArgs(
{
title: title || '',
type: 'plain-text',
defaultValue: message || '',
},
(id, value) => {
callback(value);
},
);
return;
}
let callbacks = [];
const buttons = [];
let cancelButtonKey;
+3 -1
View File
@@ -8,13 +8,15 @@
* @format
*/
'use strict';
import type {TurboModule} from '../TurboModule/RCTExport';
import * as TurboModuleRegistry from '../TurboModule/TurboModuleRegistry';
export type Args = {|
title?: string,
message?: string,
buttons?: Array<Object>, // TODO(T67565166): have a better type
buttons?: Object, // TODO: have a better type
type?: string,
defaultValue?: string,
cancelButtonKey?: string,

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