Compare commits

...
253 Commits
Author SHA1 Message Date
Martin Bigio 53f688f07c Use default asset roots on default.config.js
Summary: We need to cherry pick this one as well.

cc ide
Closes https://github.com/facebook/react-native/pull/3640

Reviewed By: svcscm

Differential Revision: D2577961

Pulled By: mkonicek

fb-gh-sync-id: 3368fc02a931e0f259f3baa6706bd7d8586b0fbf
2015-10-27 17:20:31 +00:00
Martin Bigio ff6d9cc03e Fix webSocketProxy.js
Summary: cc mkonicek ide
Closes https://github.com/facebook/react-native/pull/3638

Reviewed By: svcscm

Differential Revision: D2576536

Pulled By: martinbigio

fb-gh-sync-id: 27f53eedf2bd94f71f4e405d3d4bb193eaabc918
2015-10-27 17:20:13 +00:00
Spencer Elliott 91df57c0a5 Replace applescript with https://github.com/sindresorhus/opn
Summary: This allows opening the Chrome debugger on OS X, Linux, and Windows, and succeeds the previous PR which used [browser-launcher2](https://github.com/benderjs/browser-launcher2) and included a `--dangerouslyDisableChromeDebuggerWebSecurity` option: https://github.com/facebook/react-native/pull/2406

[opn](https://github.com/sindresorhus/opn) is cross-platform and much simpler than browser-launcher2 (since we don't have to manage the opened Chrome instance; the OS will just use the default instance).
Closes https://github.com/facebook/react-native/pull/3394

Reviewed By: mkonicek

Differential Revision: D2550996

Pulled By: frantic

fb-gh-sync-id: fa4cbe55542562f30f77e0a6ab4bc53980ee13aa
2015-10-27 17:20:08 +00:00
Alex Kotliarskyi e7bb56d676 npm install --save opn
Reviewed By: mkonicek

Differential Revision: D2570884

fb-gh-sync-id: 89f9ab8e8b04081442da28e8c30caa80b454d696
2015-10-27 17:19:53 +00:00
Martin Konicek 444712052a [0.14.0-rc] Update npm and Pod version to 0.14.0-rc 2015-10-24 01:43:27 +01:00
Martin Konicek 035c181e77 [0.14.0-rc] Bump versions 2015-10-24 00:33:44 +01:00
Martín Bigio dad45aa8f2 Fix android command
Summary: public

We've recently tweak `Config.js` so that it changes the current working directory to be able to keep track of it when running functions on the config instances. Turns out we can't do this as some commands rely on the current path (i.e.: `android`).

Although for this specific command we could fix the issue by using several `../` I feel like changing the cwd could bring other weird problems in the future, so I'm reverting the last update I did to D2565954.

Reviewed By: foghina

Differential Revision: D2572172

fb-gh-sync-id: 8cba62228b19a7729efcfe240a2f00e9becda61f
2015-10-23 09:53:24 -07:00
Pieter De Baets 039dab078a Fix configuration for OSS UIExplorer tests
Reviewed By: nicklockwood

Differential Revision: D2575356

fb-gh-sync-id: 0ab5bd29aa3c17a8cb067a8a17abed3e178c5bcf
2015-10-23 09:35:25 -07:00
Nick Lockwood b0b9a56ef8 Fixed ActivityIndicatorIOS layout
Summary: public

This has been broken for a while. The styles in the JS were being set on the container instead of the activity view itself.

Also, due to the way frames were set directly on layer properties instead of the view, resizing didn't work properly because the UIImageView inside the UIActivityView was not being realligned when the bounds changed. This also caused problems for other controls such as maps, where it was fixed with a method override, but the simpler solution is just to set the view center and bounds directly.

Before: {F23631143}
After: {F23631144}

Reviewed By: javache, tadeuzagallo

Differential Revision: D2575156

fb-gh-sync-id: e82e56d36648e7c924df77da1750e03037b5d5be
2015-10-23 09:13:24 -07:00
Pieter De Baets ed0b45ef8e Merge pull request #3578 from javache/xcode-7
Use latest simulator version
2015-10-23 16:04:22 +01:00
Felix Oghină 864fdd1ebd Merge pull request #3580 from exponentjs/cli-version-check
[CLI] Make `react-native init` check your Node version
2015-10-23 11:57:25 +01:00
Felix Oghină 7e53ee1095 add upgrade cli command
Reviewed By: mkonicek

Differential Revision: D2559885

fb-gh-sync-id: 785bd6ad855da30c00b170e737a7dd0f3e756430
2015-10-23 03:37:28 -07:00
Felix Oghină 6f71b790f1 Merge pull request #3297 from exponentjs/node-4-cli
[CLI] Add Node >= 4 requirement to CLI tools
2015-10-23 11:18:44 +01:00
rocman 772010a9af Add controller to closest parent on time so as to fix the automaticallyAdjustContentInsets feature.
Summary: Here's the bug to fix:
https://github.com/facebook/react-native/issues/898
Closes https://github.com/facebook/react-native/pull/3509

Reviewed By: svcscm

Differential Revision: D2574869

Pulled By: javache

fb-gh-sync-id: 8d947c2a0edcfb695c2e209c7b44ae6417b6233e
2015-10-23 03:15:25 -07:00
James Ide bc409ba2da Merge pull request #3620 from jabbrass/patch-2
[docs] fix typos (docs/Animations)
2015-10-22 17:16:10 -07:00
J. Andrew Brassington 7c36138caa Fix typos (docs/Animations)
Line 461: "simlar" => "similar"
Line 545: "tighly" => "tightly"
2015-10-22 17:02:40 -07:00
Alexander Blom e6ad91b5a2 Fix showsHorizontalScrollIndicator for ScrollView
Differential Revision: D2573323

fb-gh-sync-id: 980a29fcf836326f9f581eb404d36638afb67f10
2015-10-22 16:50:25 -07:00
James Ide 6ec4d65aec Set up the RN JS environment before requiring other modules
Summary: Set up the polyfills for console and process before initializing other modules. Some modules do work (e.g. call `invariant`) when they are first required, so requiring the DefaultInjection module at the beginning ensures we have a consistent JS environment.
Closes https://github.com/facebook/react-native/pull/3526

Reviewed By: spicyj

Differential Revision: D2560340

Pulled By: frantic

fb-gh-sync-id: 44c743e3212ff1dcdbe0169041dfd3ea8f1a54f7
2015-10-22 15:48:28 -07:00
Martin Konicek 251a4086a7 Fix Chrome debugging
Reviewed By: dkoroskin

Differential Revision: D2572178

fb-gh-sync-id: 677f2eb5a7ff5ada2a4e66f86e90664787bef346
2015-10-22 14:26:27 -07:00
Martín Bigio dee96fbedc Update blacklist.js
Summary: Since we rely on a non npm version of `react-tools` which includes docs and other unnecessary files we need to make sure we blacklist some of them as otherwise the packager fails
Closes https://github.com/facebook/react-native/pull/3616

Reviewed By: svcscm

Differential Revision: D2572024

Pulled By: mkonicek

fb-gh-sync-id: 2082cd69e2ccddb275dfdc7db107d4ed6b826bbb
2015-10-22 14:20:26 -07:00
Martin Konicek 582655e6a8 Merge pull request #3612 from martinbigio/scripts-fix
[react-native-cli] Fix errors on tests refering `packager.js`
2015-10-22 20:45:24 +01:00
Martin Konicek eced31ffaf Merge pull request #3611 from foghina/bundle_upgrade_docs
[docs] add bundle and upgrade docs
2015-10-22 20:11:07 +01:00
Martin Bigio 26df016600 [react-native-cli] Fix errors on tests refering packager.js 2015-10-22 14:17:29 -04:00
Felix Oghină 880a06a4b2 [docs] add bundle and upgrade docs 2015-10-22 19:11:38 +01:00
Martin Konicek 3d489c3a6a Update GettingStarted.md 2015-10-22 19:05:11 +01:00
Martin Konicek 98aed73e1c Update GettingStarted.md 2015-10-22 19:02:19 +01:00
Martín Bigio 32aeeea313 Fix cli entry points
Reviewed By: frantic

Differential Revision: D2565954

fb-gh-sync-id: 6fde9e1dbefd71944e8f857e9a40619bf599ef75
2015-10-22 10:13:13 -07:00
Martin Konicek 17ef86d6bb Update LinuxWindowsSupport.md 2015-10-22 17:22:52 +01:00
Martin Konicek 58ea08dfdb Fix react-tools dependency
Reviewed By: foghina, spicyj

Differential Revision: D2570237

fb-gh-sync-id: 4340953702112af704cac97e7d27313c573eee20
2015-10-22 08:38:27 -07:00
Pieter De Baets 59bb57a002 Use latest simulator version 2015-10-22 16:10:09 +01:00
Martin Konicek 55f8f3e370 Explanatory message when starting packager on Windows or unknown platform
Reviewed By: martinbigio

Differential Revision: D2564756

fb-gh-sync-id: 097ec541267a54a45d82116a03e4c22b8f49eb99
2015-10-22 07:10:27 -07:00
Pieter De Baets afea653cbc Measure script conversion time in RCTPLScriptExecution
Reviewed By: alexeylang

Differential Revision: D2570036

fb-gh-sync-id: a3ca8cdf0420b0da12b65ac0a672179940b2f818
2015-10-22 06:41:28 -07:00
Felix Oghină ce7de93275 [android] remove deprecated gradle plugin 2015-10-22 14:25:03 +01:00
Nick Lockwood ae5de54f00 Reduced module config data
Summary: public

We're sending a lot of module config data when the app first starts, and much of this is redundant.

UIExplorer current sends 19061 bytes of module config JSON. This diff reduces that to 16104 (15% saving) by stripping modules that have no methods or constants, and removing method types unless method is async.

Reviewed By: tadeuzagallo, javache

Differential Revision: D2570010

fb-gh-sync-id: 8c0abbd1cdee3264b37a4f52e852008caaffb9c5
2015-10-22 05:53:26 -07:00
Charles Marsh 66717d802b Allow for styling of ToolbarAndroid's overflow icon
Summary: Fixes #2858.

Here's a screenshot of the custom overflow icon in action:
<img width="638" alt="overflow" src="https://cloud.githubusercontent.com/assets/1309177/10567090/693e395a-75ae-11e5-84cd-20b19149c620.png">
Closes https://github.com/facebook/react-native/pull/3497

Reviewed By: svcscm

Differential Revision: D2559787

Pulled By: foghina

fb-gh-sync-id: f188711ec094af3fa307722527f22aefff722e64
2015-10-22 04:43:25 -07:00
Tadeu Zagallo 71da2917e5 Remove retainArguments from RCTModuleMethod's invocation
Summary: public

Fixes #2527

We were re-using the same invocation for every module's method, but calling
`[NSInvocation retainArguments]`, so the arguments would never be released.

Reviewed By: nicklockwood

Differential Revision: D2559997

fb-gh-sync-id: eafa3b3517c7cab3539954e26e250f7f668eee50
2015-10-22 04:04:25 -07:00
Felix Oghină ff17789e98 consistent redbox theme, bigger text and spacing
Differential Revision: D2569981

fb-gh-sync-id: 20b9bfc6801544795d6d13aa029ae5e1f5ee1dee
2015-10-22 03:39:26 -07:00
Alexander Kotliarskyi 8c18a24277 Merge pull request #3586 from frantic/fix-e2e-tests
Fix e2e tests
2015-10-21 21:03:13 -07:00
Alex Kotliarskyi be70e32de2 Generate bundle and assets when building via Xcode
Summary: This integrates React Native into Xcode's build process, which lets us automatically handle bundling and packaging image assets.

Tested the script via https://github.com/frantic/ReactNativeAssetsExample

Loaded from packager:
<img width="432" alt="screen shot 2015-10-19 at 3 11 12 pm" src="https://cloud.githubusercontent.com/assets/192222/10593447/be5bc7e8-7678-11e5-8c70-ecc2a1ee24fc.png">

Loaded from offline bundle:
<img width="432" alt="screen shot 2015-10-19 at 3 10 58 pm" src="https://cloud.githubusercontent.com/assets/192222/10593448/be5d5194-7678-11e5-8b02-d492a26cfb81.png">

Android:
<img width="639" alt="screen shot 2015-10-19 at 3 11 20 pm" src="https://cloud.githubusercontent.com/assets/192222/10593449/be5de2d0-7678-11e5-8d3c-0378fc447f15.png">
Closes https://github.com/facebook/react-native/pull/3523

Reviewed By: mkonicek

Differential Revision: D2557923

Pulled By: frantic

fb-gh-sync-id: 19957e255993696e793b0162662772efd89f5c1a
2015-10-21 18:44:27 -07:00
Bhuwan Khattar aff4cfb3b3 Fix spacing
Reviewed By: cpojer

Differential Revision: D2568166

fb-gh-sync-id: 8cedfd49ae4ce0de57a4ff3b499057c748803bd3
2015-10-21 17:39:26 -07:00
Alex Kotliarskyi 2f5681d4f2 Fix e2e tests
Internally we don't mirror `scripts` folder from GitHub, which means
when importing #3523 changes to `scripts/e2e-test.sh` will not make it to
GitHub. This commit contains those changes.
2015-10-21 16:36:28 -07:00
Bhuwan Khattar 3a8f9e1080 split transformers
Reviewed By: martinbigio

Differential Revision: D2567545

fb-gh-sync-id: 2e6a341885c701c6a7da35830227dbff7705255c
2015-10-21 16:28:27 -07:00
cpojer 54b1402c43 Nuke unused scriptPreprocess file. 2015-10-21 15:25:11 -07:00
Jesse Ruder 56fef9b622 Allow duplicate keys in FormData
Summary: Right now `FormData` doesn't allow duplicate keys and uses the last value set for a duplicate key. I tested this in Chrome:
```
var formData = new FormData();
formData.append('key', 'value1');
formData.append('key', 'value2');

var request = new XMLHttpRequest();
request.open("POST", serverUrl);
request.send(formData);
```
and the request has both 'value1' and 'value2'.

I removed the duplicate key check in `FormData`. If people want to build appending or disallow duplicate keys, they can build either on top of this.
Closes https://github.com/facebook/react-native/pull/3556

Reviewed By: svcscm

Differential Revision: D2566999

Pulled By: nicklockwood

fb-gh-sync-id: 580e52e69376ebe9693e39a386cc540802b6d94f
2015-10-21 15:18:40 -07:00
Nick Lockwood 666833d2d2 Fixed RCTImageStoreManager bug
Reviewed By: tadeuzagallo

Differential Revision: D2565154

fb-gh-sync-id: a9b6df27e4c54d5c3a9ad42dc5b821729d445c9e
2015-10-21 13:19:28 -07:00
James Ide d061de1107 Merge pull request #3582 from levic92/patch-2
Add Okanagan News App to showcase
2015-10-21 13:05:05 -07:00
levic92 279a6cd52f Add Okanagan News App to showcase
This app was built using React Native and was created to provide a more optimized mobile experience for reading news articles. I would love to have it added to the showcase!
2015-10-21 13:01:31 -07:00
Krzysztof Magiera 44ff93e7bf Sync latest code from github
Differential Revision: D2566417

fb-gh-sync-id: 9eb9fbbb2dfbd5337de128ea4415120faf089aaa
2015-10-21 12:52:26 -07:00
James Ide 64c809345f [CLI] Make react-native init check your Node version
We get a bunch of bugs because people are running old versions of Node that don't support modern JS. We have "engines" entries in the package.json files to catch this earlier but printing an explicit error message will also make this clear.

Test Plan: Changed react-native's package.json to require Node >= 5 and got an error message when running the CLI with Node 4.
2015-10-21 12:51:14 -07:00
Oliver (Cunpu) Bo 4036eca570 Fix pixelated image in storyline
Reviewed By: seanxiao

Differential Revision: D2563069

fb-gh-sync-id: 86fa47801c34669b7b0c2826e0e776717f5605dc
2015-10-21 12:45:29 -07:00
Nick Lockwood a859d9e931 Fixed ActionSheetIOS on iOS 9 SDK
Summary: public

UIActionSheet no longer works on iPad when building with the iOS 9 SDK, as reported here: https://github.com/facebook/react-native/issues/3498

This diff implements an alernative code path that uses UIAlertController instead when its available.

Also added `anchor` support for action sheets (previously only supported for share sheet).

Reviewed By: javache

Differential Revision: D2560068

fb-gh-sync-id: feda20f6f1fee5e2ba624761fbeb812e23d96fff
2015-10-21 12:21:28 -07:00
James Ide 5227b10455 [README] Add clarification about GH issues being for bugs & feature requests 2015-10-21 11:26:50 -07:00
Alexey Lang 8ad09987e5 Log bundle size.
Reviewed By: javache

Differential Revision: D2564757

fb-gh-sync-id: c4429b735482ee6405b936e2dcdf610e9c6ad35f
2015-10-21 10:55:25 -07:00
Thomas Parslow 394dfae868 Support line number arguments for Emacs when using emacsclient
Summary: Many emacs users will want to use the `emacsclient` command to launch emacs. This opens the file in an existing emacs session rather than opening up a new instance.
Closes https://github.com/facebook/react-native/pull/3536

Reviewed By: svcscm

Differential Revision: D2565189

Pulled By: pcottle

fb-gh-sync-id: be12782aa92281eb649710ac22db2ffa1486a08a
2015-10-21 09:57:40 -07:00
Martin Konicek c771fb3072 Fix runAndroid
Reviewed By: kmagiera, martinbigio

Differential Revision: D2565100

fb-gh-sync-id: 3be393fbd164997ebaa7a9b133e6ebb72059a615
2015-10-21 09:39:27 -07:00
Martin Konicek 62ac37275f Actually revert https://github.com/facebook/react-native/commit/2608efccb6d81e2abc5571522455e641caf330ea
Summary: We need to keep both babel and babel-core top level. `npm-shrinkwrap` defines that to be at the top level but that's only used when cloning the github repo. When publishing to npm and installing from npm, it has no effect (it's not published anywhere).

This file assumes `babel-core` is at the top level: https://github.com/facebook/react-native/blob/master/packager/babelRegisterOnly.js

public

Reviewed By: davidaurelio

Differential Revision: D2565054

fb-gh-sync-id: 7d6a079d88e09c1ba0478cf4041948e330da639a
2015-10-21 09:34:26 -07:00
Pieter De Baets 68c52814d6 Enable ReactKit tests on iOS9
Reviewed By: majak

Differential Revision: D2565213

fb-gh-sync-id: 6a70f7e7403b396af8d10b844c5e41ea472ab9b5
2015-10-21 09:31:24 -07:00
James Ide 6a5b61eb88 Merge pull request #3534 from edo1493/master
CANDDi's iOS app
2015-10-21 09:27:05 -07:00
Martin Konicek cafeac0000 Update LinuxWindowsSupport.md 2015-10-21 17:06:19 +01:00
Krzysztof Magiera a5c693dd53 Fix typo in ReadableMapKeySetIterator.
Reviewed By: mkonicek

Differential Revision: D2564934

fb-gh-sync-id: 2ef1a0f5c1550d912dc751100c307659a5159fa3
2015-10-21 09:05:12 -07:00
Stephen Keep 852eba2a91 Documentation Update
Summary: The first change is to remove the word 'Hybrid' to a lot of javascript developers the word Hybrid is normally referred to as a webview app (such as Cordova) and this may cause confusion.

The second change is to refer to the property within Navigator that allows a scene to be presented from the bottom of the screen (like a modal).
Closes https://github.com/facebook/react-native/pull/3173

Reviewed By: svcscm

Differential Revision: D2561886

Pulled By: javache

fb-gh-sync-id: e37e5d06869ffadc152abaf304f77c93b81f71a3
2015-10-21 07:02:25 -07:00
Martin Konicek 586b1ee5df Update LinuxWindowsSupport.md 2015-10-21 14:08:13 +01:00
Martin Konicek 59563ff203 Update LinuxWindowsSupport.md 2015-10-21 14:06:53 +01:00
Martin Konicek 0b5179dce7 [docs] Add docs about Linux and Windows support 2015-10-21 14:02:27 +01:00
Martin Konicek 43d6b1bbfa Update DevelopmentSetupAndroid.md 2015-10-21 13:31:34 +01:00
Hermes Pique 9960878ba4 Revert D2564759
Differential Revision: D2564818

fb-gh-sync-id: 75e44551fc2c7e22be8096f12c06aa88b72a4508
2015-10-21 05:31:25 -07:00
Edoardo Moreni ea4ea90d77 canddi app 2015-10-21 13:03:34 +01:00
Krzysztof Magiera e1b2c84a81 Fix typo in ReadableMapKeySetIterator.
Differential Revision: D2564759

fb-gh-sync-id: 6a57fb71d4da1d467661a3ae786f637172b21e73
2015-10-21 04:14:12 -07:00
Martin Konicek f1ab581337 [react-native-cli] Make 'npm start' call 'react-native start' 2015-10-21 11:27:37 +01:00
Martín Bigio c6c97cbd9d Introduce default config
Reviewed By: frantic

Differential Revision: D2561344

fb-gh-sync-id: 651b8a199069f78e1ace2897ba4c0352aab7e3ea
2015-10-21 03:04:04 -07:00
Martín Bigio 8e7cfcd053 Kill unused files
Reviewed By: frantic

Differential Revision: D2561113

fb-gh-sync-id: a81fece0a948485e12bff7ec146e39d890664887
2015-10-21 03:04:01 -07:00
Martín Bigio 088c193a22 Start packager through cli
Reviewed By: frantic

Differential Revision: D2561066

fb-gh-sync-id: 1743153a84947da5d0e54aca7e1919cf5df3a965
2015-10-21 03:03:59 -07:00
Martín Bigio 5c4bb894bf Clean up cli.js
Reviewed By: frantic

Differential Revision: D2546201

fb-gh-sync-id: 214a12213674749e1b7270ccbe90fd3330c9cb1e
2015-10-21 03:03:57 -07:00
Aaron Chiu b4d4cf4444 bump up react bridge time out from 15s to 30s
Differential Revision: D2562848

fb-gh-sync-id: 62255e01d4ff599c03af5eebd87d8d40aeb7e6fe
2015-10-21 03:03:55 -07:00
Martín Bigio 34c91800d2 Revert 2608efc
Reviewed By: frantic

Differential Revision: D2561665

fb-gh-sync-id: fe7c3a63a8644e122703637c2d4ead21b75a373f
2015-10-21 03:03:53 -07:00
Felix Oghină 71adc24974 Revert "Update rn-cli.config.js"
This reverts commit 8d592650c9.
2015-10-21 11:02:08 +01:00
Martín Bigio 774bbc2f3a [react-native-cli] Remove reference to packager.sh
This script was removed, need to remove the reference we had here.
2015-10-20 22:15:30 -04:00
Sam Swarr c15ec71238 Create script to generate NSURL builders for RN Routes
Reviewed By: martinbigio

Differential Revision: D2513000

fb-gh-sync-id: 4ff695b4d5ea55ae9bb21df4000d2dead5b7ff97
2015-10-20 13:49:31 -07:00
Samarendra M Hedaoo 54b5b62f78 Adding http as failover for react-tools git repo.
Summary: Fixes #2797 for me.

Most times git://github.com/facebook/react-tools times out. So adding
http as a fail-over so that if git protocol doesn't work while cloning
the react-tools repo as a dependency, http would atleast work and ensure
that a simple "react-native init myProject" doesn't fail.

Submitting a PR so that http is used as a failover when cloning. (I guess that is what git+http does in package.json)
Closes https://github.com/facebook/react-native/pull/3514

Reviewed By: svcscm

Differential Revision: D2561417

Pulled By: spicyj

fb-gh-sync-id: ac421a7f57474124875a5cabdc5a34801019f6cd
2015-10-20 13:49:25 -07:00
James Ide 330b0538a9 Merge pull request #3548 from anttimo/fix-typo-in-DirectManipulation-doc
Fixed one typo and made one sentence more readable by adding 'and' to it.
2015-10-20 13:09:38 -07:00
Antti Moilanen d6f46c693a Fixed one typo and made one sentence more readable by adding 'and' to it. 2015-10-20 23:06:33 +03:00
David Aurelio 523769faa7 Add --verbose flag to react native packager cli
Reviewed By: martinbigio

Differential Revision: D2517347

fb-gh-sync-id: 02e64f99120b6636d6e7649deb11d0b2f58d9285
2015-10-20 11:52:25 -07:00
Nick Lockwood c76c22249d Clear cache entry when calling multiRemove
Reviewed By: javache

Differential Revision: D2560487

fb-gh-sync-id: 4d2c192cc659f118fd5667da2a029457328eae9f
2015-10-20 11:50:30 -07:00
Martín Bigio 02e39719c2 Move run-android to private-cli
Reviewed By: mkonicek

Differential Revision: D2544567

fb-gh-sync-id: 3c62f6c30b5be7f480d8f44a48fb551fc30d477e
2015-10-20 11:48:29 -07:00
Brent Vatne 93488a7898 Merge pull request #3541 from frantic/yglf-talk
Add "React Native: Under the Hood" to videos
2015-10-20 11:26:10 -07:00
Martin Konicek 5e2882846c Update breaking-changes.md 2015-10-20 19:20:16 +01:00
Hedger Wang 06a154e061 Fix Js error in Navigator
Summary: public

In Navigator, there are several places that mutate `this.state.presentedIndex`
with the express `this.state.presentedIndex = destIndex` instead of calling
`this.setState`, which creates the problem that not all internal states are
updated within the same React update cycle.

One of the symptoms is that over-swiping within the Navigator may throw JS error
due to `this.state.sceneConfigStack` and `this.state.presentedIndex` are not both
updated.

The workaround is to bypass the over-swiping gesture to avoid JS error.

Reviewed By: ericvicenti

Differential Revision: D2557140

fb-gh-sync-id: 1e5c9047ed17c04a63e2a568118848b00723fb1d
2015-10-20 11:04:23 -07:00
Martin Konicek 4ac3c4a853 Make 'npm start' an alias for 'packager.sh', don't silently no-op on Windows
Reviewed By: martinbigio

Differential Revision: D2559772

fb-gh-sync-id: d1cd2874c32459d6e1f0af7d084c3559ebc145aa
2015-10-20 10:15:22 -07:00
Martin Konicek e612690413 Add license headers to local-cli
Reviewed By: martinbigio

Differential Revision: D2559969

fb-gh-sync-id: d8cd52435213729ff73a1f039eb0378b28f8d10e
2015-10-20 09:55:21 -07:00
Martin Konicek 82fad33af7 Breaking change: Remove redundant packager entry point
Summary: We have too many ways to start the packager:
- react-native start
- npm start
- ./packager/packager.sh
- ./packager/launchPackager.command

This removes yet another one. According to the [npm docs](https://docs.npmjs.com/files/package.json)
this creates `/usr/local/bin/react-native-start`. Let's kill it.

public

Reviewed By: martinbigio

Differential Revision: D2559953

fb-gh-sync-id: e3b41a0622e6168fe686cdf9c93714dbf02f5965
2015-10-20 09:54:30 -07:00
James Ide 93c56a0e90 Expose the projectRoots option
Summary: This was already implemented -- just documenting it in the CLI.
Closes https://github.com/facebook/react-native/pull/3246

Reviewed By: svcscm

Differential Revision: D2560211

Pulled By: martinbigio

fb-gh-sync-id: c4e4d974db28d6251636ff38bfcb0afdf5148de7
2015-10-20 09:49:27 -07:00
Alex Kotliarskyi 3506f55ece Add "React Native: Under the Hood" to videos 2015-10-20 09:48:00 -07:00
David Aurelio fb46e39cad Make the modification time of the transformer file relevant for the cache.
Reviewed By: amasad

Differential Revision: D2554956

fb-gh-sync-id: cc76f083594aac04c74a4ef271305238b06710eb
2015-10-20 09:46:23 -07:00
Nick Lockwood dbe37e7898 Improved AsyncLocalStorage performance
Reviewed By: javache

Differential Revision: D2540626

fb-gh-sync-id: f94def7463075d40c2dcfea1cb4c01502aefa117
2015-10-20 09:42:23 -07:00
Krzysztof Magiera 4849aabecc Stop propagating requestLayout up the view hierarchy.
Differential Revision: D2560183

fb-gh-sync-id: e62acc3eb70a78ae4526bfe696e51349be957ae3
2015-10-20 08:46:21 -07:00
Andy Street 1b55e4657c Load JS bundle after CatalystInstance has been attached to the ReactContext
Reviewed By: andreicoman11

Differential Revision: D2550062

fb-gh-sync-id: 05790e8c092c7dd28669ffd48f7d7d2dffd6546e
2015-10-20 06:24:25 -07:00
Nick Lockwood 1d6d1189f0 Improved threading for image loader
Summary: public

The image loader was previously returning on the main thread, which could lead to poor performance due to various call sites doing further image processing (resizing, cropping, etc.) directly in the completion block.

This diff modifies the loader to return on a background thread (the same one used to load the image), and updates the call sites to dispatch to the explicit thread they need.

Reviewed By: javache

Differential Revision: D2549774

fb-gh-sync-id: fed73b7c163fdf67ff65bae72ab1986327e75815
2015-10-20 05:02:23 -07:00
Jimmy Mayoukou 77154a7581 Fix typos in example descriptions.
Summary: Closes https://github.com/facebook/react-native/pull/3095

Reviewed By: svcscm

Differential Revision: D2559729

Pulled By: foghina

fb-gh-sync-id: 70dbd39de86e6377d32d1628c77ff10510ac3ae5
2015-10-20 04:38:21 -07:00
Tadeu Zagallo 89c1747c33 Implement asm trampoline rather forwardInvocation
Summary: public

The profiler overrides all the methods of all the BridgeModules, and in order to
`start` and `end` the profiler at the function invocation time it used `NSInvocation`,
which is slow.

Replace it with a simple assembly method based on `objc_msgSend`.

Reviewed By: jspahrsummers

Differential Revision: D2550807

fb-gh-sync-id: 88ca08f9d6bfcd3035bda9304c93566c8818b46f
2015-10-20 04:14:09 -07:00
Martin Konicek 1b29690496 [docs] Tweak Android setup screenshot 2015-10-20 12:44:39 +02:00
Martin Konicek d41cedb8fc Update DevelopmentSetupAndroid.md 2015-10-20 12:35:48 +02:00
Martin Konicek 4d9b05cfb2 [docs] Simplify Android setup instructions 2015-10-20 12:30:54 +02:00
Edoardo Moreni 67498a4b6d CANDDi's iOS app 2015-10-20 10:23:50 +01:00
Martín Bigio 8d592650c9 Update rn-cli.config.js 2015-10-19 19:07:34 -04:00
Martín Bigio 17c59ace46 Add node-fetch node module to repo
Reviewed By: vjeux

Differential Revision: D2542225

fb-gh-sync-id: a94e799d5595eb7909b42bc8acb6d80259ca1609
2015-10-19 15:00:26 -07:00
Brent Vatne d03bf216b1 Remove link to IRC in CONTRIBUTING.md, not used anymore 2015-10-19 12:27:17 -07:00
Brent Vatne 61ad255629 Merge pull request #3519 from ptmt/patch-3
Add info about react-native channel on DiscordApp
2015-10-19 12:25:50 -07:00
Brent Vatne ac5e2a41bb Merge pull request #3520 from purposecampaigns/add-foodstand-to-showcase
Add Foodstand to RN showcase
2015-10-19 12:24:31 -07:00
Justin Spahr-Summers 123516d556 Reload RCTImageView when it's displaying the default image
Summary: public

If the frame is set, or the image view moves to a window, we should attempt to load the "real" image. The sizing logic shouldn't kick in if we're only displaying the default image.

Resolves https://github.com/facebook/react-native/issues/3460.

Reviewed By: tadeuzagallo

Differential Revision: D2555316

fb-gh-sync-id: c0c13070ee080bad2b30ca01d9d5173bead406e3
2015-10-19 10:26:24 -07:00
Ricky Reusser d760544652 Add Foodstand to RN showcase 2015-10-19 13:20:05 -04:00
Hedger Wang 73b80773ba supports event capturing and bubbling phases in Navigation context.
Summary: Adds the API that enables the navigation events capturing and bubbling which is the feature
that is enabled if the nested navigation contexts is created by the navigator.

This would allow developer to observe or reconcile navigation events within the navigation tree.

public

./Libraries/FBReactKit/jest

Reviewed By: zjj010104

Differential Revision: D2546451

fb-gh-sync-id: dfc9d16defaa563b9e80fd751a20570f6e524b74
2015-10-19 10:06:22 -07:00
Nick Lockwood 1076f4a172 Added RCTDataRequestHandler
Summary: public

Added RCTDataRequestHandler, which is responsible for loading data URLs. This moves the logic for data URL handling out of RCTImageDownloader (no longer needed) and into the RCTNetwork library, where it makes more sense.

This also means that it is now possible to load data URLs via XHR, and use them for purposes other than just images.

Reviewed By: javache

Differential Revision: D2540964

fb-gh-sync-id: 4f0418bd6b9186f047cc8297276bb970795af104
2015-10-19 09:07:06 -07:00
Dmitry 419549b116 Add info about react-native channel on DiscordApp 2015-10-19 20:47:03 +05:00
Tadeu Zagallo 31f9a690f3 Flush queue every 5ms during JS execution
Summary: public

Implement the iOS side of the optmisation previously implemented in android
(D2485402)

Depends on D2540746

Reviewed By: javache

Differential Revision: D2541118

fb-gh-sync-id: f3590600a6defa2da75c5b7b2cced6ad8bfea6cb
2015-10-19 08:04:24 -07:00
Joe Noon 607527c0d4 (upstream) Prevent SocketRocket killing the connection before notifying of final messages
Summary: wrap NSStreamEventEndEncountered in dispatch_async _workQueue per upstream:
https://github.com/square/SocketRocket/pull/294

CLA completed
Closes https://github.com/facebook/react-native/pull/3493

Reviewed By: svcscm

Differential Revision: D2554892

Pulled By: javache

fb-gh-sync-id: 347a37eb95b20b7e92b985b6908e15462672e83c
2015-10-19 06:47:22 -07:00
Felix Oghină 9dc036d2b9 add gradle tasks for bundling js and assets in template
Summary: This adds gradle tasks that call `react-native bundle` with the correct args to bundle dev/release JS and assets. The dev task is disabled by default, as in dev mode people only use reload JS in most cases.

Submitting this as a pull request to have community visibility, although we have this code internally as well now and this will require an import.

* generate a project with `react-native init`, add some assets, run `./gradlew assembleRelease`, sign & zipalign generated APK, run it -> works
* enable dev bundling task, run `./gradlew installDebug` (not `react-native run-android` so as to not start the packager) -> works

Reviewed By: svcscm

Differential Revision: D2555071

Pulled By: foghina

fb-gh-sync-id: c3d9fcd4c77862e6a4db4e4d8d8cc39ee9dff3ab
2015-10-19 06:33:22 -07:00
Pieter De Baets 59e670e453 Fix a warning about SnapshotView
Reviewed By: majak

Differential Revision: D2553669

fb-gh-sync-id: 2a2b47f9cf7a75403e4e2541b32a4b141b3d08be
2015-10-19 06:10:22 -07:00
Felix Oghină b09e92e1b3 Merge pull request #3499 from willchu/master
Add LoadDocs to showcase
2015-10-19 12:15:15 +01:00
Felix Oghină 81317bd27a Merge pull request #3495 from dsandor/master
Clarified ReactInstanceManager paragraph.
2015-10-19 12:09:35 +01:00
James Ide 45d2c691a3 Start to use the Obj-C JSContext instead of the C API
Summary: The Obj-C API is usually easier to work with, and also makes it very easy to use the C API when necessary (performance, for example). This diff just switches the designated initializer of RCTContextExecutor to take a JSContext instead of JSGlobalContextRef, but the old initializer still works if needed.

I was doing some memory leak investigation and it is easier with ARC so I wanted to incrementally move the executor to Obj-C.
Closes https://github.com/facebook/react-native/pull/2159

Reviewed By: svcscm

Differential Revision: D2554890

Pulled By: tadeuzagallo

fb-gh-sync-id: 75b96d04cddff68fa3daf5d0fafdffad21dae307
2015-10-19 03:51:20 -07:00
Andrei Coman 716f7e8ef2 Don't use MotionEvent timestamp for touch events
Differential Revision: D2554905

fb-gh-sync-id: 7f83e94948cc9ac1024e249764d445fb056f400e
2015-10-19 03:08:23 -07:00
James Ide e3f33ea261 Merge pull request #3500 from jadekler/master
Small readme updates
2015-10-18 21:47:36 -07:00
Jean de Klerk 2be0525b2a Small readme updates
- Link for firefox react dev tools
- Add missing word
2015-10-18 19:00:38 -06:00
Will ec6d41414d add LoadDocs to showcase 2015-10-18 17:34:59 -07:00
David Sandor c0a2c96d9b Clarified ReactInstanceManager paragraph. 2015-10-18 15:55:21 -05:00
Brent Vatne 7827b35443 [Website] Update support section to link to Discord 2015-10-18 12:39:43 -07:00
Brent Vatne 3759511951 Merge pull request #3490 from hswolff/ios-device-docs
Update documentation with improved Cocoapods workflow
2015-10-18 12:02:51 -07:00
Brent Vatne e59b28e6ca [README] Link to Reactiflux Discord invite 2015-10-18 11:58:59 -07:00
Harry Wolff 6949be63e4 Update documentation with improved Cocoapods workflow 2015-10-18 09:48:48 -04:00
Nick Lockwood 16a48ae0c3 Fixed "Unrecognized request token" red box
Summary: public

There was a race condition issue in RCTDownLoadTask whereby the request handler would sometimes call one of the delegate methods before setup was complete, causing an error to be logged because the request token had not been set, and causing te request to fail because the class was not yet set up.

This diff fixes that issue by adding an explicit `start` method to RCTDownloadTask, and changing the setup order to allow for the request to call back immediately without this being treated as an error.

Reviewed By: tadeuzagallo

Differential Revision: D2553628

fb-gh-sync-id: 5ca4e791574a632ccbf2e873e28ac88bffdf851d
2015-10-17 09:34:23 -07:00
Andrei Coman f01272b031 Fix touches with pointer events backtrack
Differential Revision: D2553642

fb-gh-sync-id: b1788879bfbb564a291ada0e7ac206f567780f8a
2015-10-17 06:31:26 -07:00
Hedger Wang a2524bac33 Support nested navigation context.
Reviewed By: fkgozali

Differential Revision: D2525434

fb-gh-sync-id: 18e7d672b2cbadc318a207c8812f38d8669bad30
2015-10-17 04:25:26 -07:00
Martin Konicek dcd82a2450 Update RunningOnDeviceIOS.md 2015-10-17 11:26:58 +02:00
Brent Vatne b39b97546a [Docs] Add ViewPagerAndroid to extractDocs 2015-10-16 19:25:43 -07:00
Martín Bigio 8bff2d30ab Use private-cli's server instead of packager.js
Reviewed By: vjeux

Differential Revision: D2542027

fb-gh-sync-id: e59f3be17590c1e256a903e04f2f0359c835c8b5
2015-10-16 15:01:23 -07:00
Martín Bigio 5977f494fc Allow user to configure transformer through cl argument
Reviewed By: frantic

Differential Revision: D2547435

fb-gh-sync-id: 77016ee5beba854a4cb58e9ac04b91a7dd76cf3e
2015-10-16 13:26:22 -07:00
James Ide 01f90054cc Merge pull request #3415 from exentrich/patch-1
Add Raindrop.io to showcase
2015-10-16 13:00:30 -07:00
Patrik Tomas Chamelo 1511d8ca1d Invoking devMenu from RCTBridge in prod will crash
Summary: While we shouldn't invoke `bridge.devMenu` in production, doing it will result into a crash.

1. `devMenu` internally calls `RCTBridgeModuleNameForClass([RCTDevMenu class])`
2. `RCTBridgeModuleNameForClass()` calls `moduleName`
3. In the release code `RCTDevMenu` doesn't export the `moduleName` class method.
Closes https://github.com/facebook/react-native/pull/2910

Reviewed By: svcscm

Differential Revision: D2550797

Pulled By: tadeuzagallo

fb-gh-sync-id: 5dfbf905e5a02d9fd3b52f8b3d6eefc4e3ff30b2
2015-10-16 12:15:25 -07:00
Bhuwan Khattar 256f69f4e6 fix HasteDependencyResolver
Reviewed By: javache

Differential Revision: D2550444

fb-gh-sync-id: f978efdd73c09e58bcdbc4711ee2a7662a0b1bf3
2015-10-16 11:16:25 -07:00
Martín Bigio b00c49725c Use new cli on fbt string extraction script
Reviewed By: frantic

Differential Revision: D2534971

fb-gh-sync-id: b008c21c2194c59e8b2b83402841292cee8d85e8
2015-10-16 10:57:39 -07:00
Adam Miskiewicz 24bfdda499 Adds support for ES6 export syntax to the packager's DependencyResolver.
Summary: This PR includes:
- A regex that allows for the `export` syntax.
- Updated tests.
Closes https://github.com/facebook/react-native/pull/3465

Reviewed By: svcscm

Differential Revision: D2550322

Pulled By: vjeux

fb-gh-sync-id: 7d35f436af13cf5b6b1287835a18a9693ec1be8a
2015-10-16 10:57:32 -07:00
Adam Terlson da250671e5 Add support for cancelAllLocalNotifications
Summary: Closes https://github.com/facebook/react-native/pull/2980

Reviewed By: svcscm

Differential Revision: D2550080

Pulled By: javache

fb-gh-sync-id: 76a7088f4f86191b02e6c176e4a8a7bc85f90835
2015-10-16 10:57:25 -07:00
James Ide 2608efccb6 exclusively use babel-core
Summary: We don't need babel the CLI tool... just babel-core suffices. Remove babel, which speeds up npm install, and just use babel-core.
Closes https://github.com/facebook/react-native/pull/2476

Reviewed By: svcscm

Differential Revision: D2550081

Pulled By: martinbigio

fb-gh-sync-id: 4390a48ff4cc4ea78217e8af00322b7342e98c95
2015-10-16 09:24:23 -07:00
Ryan Olson 1091ecd8e0 Respect destination vaules in LayoutAnimation create
Reviewed By: nicklockwood

Differential Revision: D2544389

fb-gh-sync-id: 9e6abdbd1f500578369e5f2c3e34d7f5ef047f5e
2015-10-16 08:39:22 -07:00
Martin Bigio c0b5e6ed67 [react-native-cli] Move new-library command to private-cli 2015-10-16 11:20:29 -04:00
Frédéric Sagnes 4a3857ef1d Use JSStringCreateWithUTF8CString and skip NSString decoding when loading the bundle
Summary: public

Benchmarking our startup path has shown we spend a lot of time decoding strings (iPhone 4S / iPhone 5):

* reading a 2MB JS bundle: 35ms / 15ms
* decoding is to an `NSString`: 186ms / 78ms
* transforming that to a `JSString`: 29ms / 10ms

Instead of going through an `NSString` transformation, we generate a null-terminated bundle (0.1ms / 0.05ms to copy the data) and use `JSStringCreateWithUTF8CString` (121ms / 53ms) to generate the string. That makes decoding 70% faster.

Reviewed By: javache

Differential Revision: D2541140

fb-gh-sync-id: 09a016b8edfd46a9b62682c76705564d2024e75e
2015-10-16 08:11:25 -07:00
Krzysztof Magiera 8e2ec64763 Use RecyclerViewBackedScrollView in groups feed on Android.
Differential Revision: D2549975

fb-gh-sync-id: 98689a58a69ce8425c695acf74701509816821ae
2015-10-16 07:45:22 -07:00
Krzysztof Magiera 0ed3048e39 Add deprecated transform props to View prop type definition.
Differential Revision: D2549973

fb-gh-sync-id: 96b5233fe4ed003760746d8cdb7f0496a67c63be
2015-10-16 07:30:26 -07:00
Bill Glover 8fb9cc8fc1 Fix 16 linter warnings in Examples/
Summary: Partially fixes #3316 by addressing 16 linter warnings:
 - Strings should be singlequote
 - Missing semicolon

Travis build jobs 1.1 through 1.4 complete successfully. 1.5 fails through what appears to be an unrelated issue on master.
Closes https://github.com/facebook/react-native/pull/3332

Reviewed By: @​svcscm

Differential Revision: D2531718

Pulled By: @javache

fb-gh-sync-id: ca22fbeac5fe3b4f725775a72f21b6dd7a75d94b
2015-10-16 04:03:23 -07:00
Jihyun Yu 79e33078f2 Fix minor typos on documentation
Summary: Closes https://github.com/facebook/react-native/pull/2742

Reviewed By: @​svcscm

Differential Revision: D2549690

Pulled By: @javache

fb-gh-sync-id: 84cb3aa78c2c75bae24a8cc7c1b96e219c9a0552
2015-10-16 03:49:25 -07:00
Pieter De Baets 74002b784b Fixes broken link to running on device
Summary: Closes https://github.com/facebook/react-native/pull/2420

@​public

Reviewed By: @majak

Differential Revision: D2549685

fb-gh-sync-id: 8ce3c9aa242cd65e8efc2bdd0065f9e20de8240d
2015-10-16 03:41:21 -07:00
Krzysztof Magiera f47e8ac6a1 Instrumentation test for native listview component.
Differential Revision: D2549707

fb-gh-sync-id: c9ff379383853c198274778e99a72b313d9dbda9
2015-10-16 03:41:18 -07:00
Mike Armstrong 3d40d622f9 Prevent ReactMarker class from being stripped in release
Differential Revision: D2547100

fb-gh-sync-id: c6a5d2f70bfdf5fa6fdd7f17f063de7ad837ece3
2015-10-16 03:41:16 -07:00
Ben Alpert 84df87ef64 Fix React warnings
Reviewed By: @sebmarkbage

Differential Revision: D2548854

fb-gh-sync-id: 9ace9ce57641e2dcaae997358e6404044d871f82
2015-10-16 03:41:14 -07:00
Bhuwan Khattar 15054ade7d Support cyclic dependencies in require polyfill
Reviewed By: @martinbigio

Differential Revision: D2548506

fb-gh-sync-id: fca534179a5f8fcf407917137cdd71d964f145e5
2015-10-16 03:41:08 -07:00
Jared Forsyth 0788e0b683 Devtools websocket
Summary: I thought this was already in here, but then it wasn't. It's connected to by RN [here](https://github.com/facebook/react-native/blob/master/Libraries/Devtools/setupDevtools.js#L17).
Closes https://github.com/facebook/react-native/pull/3431

Reviewed By: @​svcscm

Differential Revision: D2548286

Pulled By: @frantic

fb-gh-sync-id: ffd74426949196ca538c897c1a0d07d08b204630
2015-10-16 03:41:05 -07:00
Will Ngo b3c72a5632 Fix command to re-generate the static bundle in Examples
Summary: Updated the commands provided in the comments to re-generate the static bundle in the Movies and UIExplorer examples, making them match the URL used in `sourceURL` while in dev mode.
Closes https://github.com/facebook/react-native/pull/3132

Reviewed By: @​svcscm

Differential Revision: D2547140

Pulled By: @vjeux

fb-gh-sync-id: 450049d3c3d4eb9919d0a3a6bd6c611e90d0f958
2015-10-16 03:41:02 -07:00
Bhuwan Khattar 83b8a30beb Simplify require polyfill
Reviewed By: @vjeux

Differential Revision: D2544415

fb-gh-sync-id: ab48b5fba169c43c68dd24dc95ea1d0322ed67c4
2015-10-16 03:40:58 -07:00
Krzysztof Magiera 643769fc8e Fix dependency on recycler view in OSS.
Differential Revision: D2546027

fb-gh-sync-id: 7a2c2298332400fb216c18b8c569a859d1a3e1d0
2015-10-16 03:40:55 -07:00
Martín Bigio d96b750e56 Fix bundle command with --dev
Reviewed By: @vjeux

Differential Revision: D2538070

fb-gh-sync-id: 3720de8e60d20d6becc60035095e29b3a480a017
2015-10-16 03:40:51 -07:00
Martín Bigio 36697ae6a1 Move android generator to private-cli
Summary: @​public

Lets also make this more generic to allow to generate the template for any platform code.

Reviewed By: @vjeux

Differential Revision: D2536821

fb-gh-sync-id: 6fb99c6dc546b8e1f9839c96a473dc08b0f5285c
2015-10-16 03:40:48 -07:00
James Ide 72b713262f [CLI] Add Node >= 4 requirement to CLI tools
React Native's packager now relies on modern V8 so it depends on modern Node. The CLI tools actually run with older versions of Node but the version constraint in the CLI's package.json accomplishes two things:

- Lets us rely on modern V8 in the CLI so we don't have to think about which JS features we can use
- Fails faster up front. So if someone has Node 0.10 for example they will get an error when setting up the CLI tools instead of later on in the process.
2015-10-15 16:15:35 -07:00
Martín Bigio ca1bc35940 Update npm-shrinkwrap.json 2015-10-15 14:43:28 -04:00
James Ide c38d7fab97 isInteraction option to keep InteractionManager unblocked
Summary: Adds a new option to animation configs so that animations can be marked as non-interactions. This solves the "loading indicator problem" where an animated loading screen would deadlock as it waits for all interactions to complete.

As for the API, what we really want long term is better scheduling. This needs to be built at the runtime level (into the RN bridge) and take care of things like priority donation that are probably overkill right now. Basically this API is a medium-term fix and I won't be upset if it gets replaced with a finer-grained scheduler.
Closes https://github.com/facebook/react-native/pull/3433

Reviewed By: @​svcscm

Differential Revision: D2545300

Pulled By: @vjeux

fb-gh-sync-id: c1216bf69bfbbbae00c76c4697183fe96a6a01dd
2015-10-15 09:22:33 -07:00
Krzysztof Magiera 1e52b8297c Wrapper for android RecyclerView to be used by ListView.js
Differential Revision: D2545291

fb-gh-sync-id: 5d745939f6d63aea4cd9bba2f55e68336efc1e9a
2015-10-15 09:22:31 -07:00
Michal Aichinger d0e40c581d Fixes a path in touchableHandlerResponderGrant method when touchableGetPressRectOffset method is not present.
Summary: Current code is checking presence of touchableGetPressRectOffset method but fails immediately on trying to get 'left' value of null.
Closes https://github.com/facebook/react-native/pull/3438

Reviewed By: @​svcscm

Differential Revision: D2545068

Pulled By: @javache

fb-gh-sync-id: d16cb46af1fc6eae02f56b2096c2169ce475e022
2015-10-15 09:22:29 -07:00
Mike Armstrong 4ec5161685 timer markings from JS JNI
Differential Revision: D2545138

fb-gh-sync-id: f93670ad929dbe37d641968e506108c6aac0cac2
2015-10-15 09:22:26 -07:00
Christopher Chedeau 8ddd3e864e Merge pull request #3424 from rnplay/embedded-simulator-website
Documentation: Adjust styling and add Appetize link in the modal
2015-10-15 09:17:21 -07:00
Johan Ruokangas cb609f986c Remove indentation and force appetize link to open a new window/tab 2015-10-15 18:18:01 +03:00
Andrei-Ovidiu Coman 02958376ad Update KnownIssues.md 2015-10-15 14:07:47 +01:00
James Ide 498ef4f935 Merge pull request #3430 from adaojunior/patch-1
fix typo
2015-10-14 16:49:03 -07:00
Adão Júnior 5e844c75f8 fix typo 2015-10-14 20:44:36 -03:00
Martín Bigio ce0e839d27 Update breaking-changes.md 2015-10-14 19:14:40 -04:00
Christopher Chedeau bbb756d2cb Merge pull request #3428 from vjeux/s3_token
Adding s3 token to travis environment
2015-10-14 14:54:47 -07:00
Christopher Chedeau dfee3f30af Adding s3 token to travis environment 2015-10-14 14:54:02 -07:00
Christopher Chedeau a0f3d29c0b Merge pull request #3427 from vjeux/appetize_token
Add appetize.io token to travis environment
2015-10-14 14:47:53 -07:00
Christopher Chedeau 6ab4faa68b Add appetize.io token to travis environment
$APPETIZE_TOKEN is now available
2015-10-14 14:46:31 -07:00
James Ide f006571a41 Merge pull request #3418 from bluedaniel/master
Adding Kakapo to the app showcase
2015-10-14 13:55:35 -07:00
Christopher Chedeau 3f2199ddf0 Merge pull request #3425 from vjeux/add_files
[website] Support pushing new files
2015-10-14 13:29:21 -07:00
Christopher Chedeau c43b2da158 [website] Support pushing new files
In #3421 we created a new folder but it didn't get picked up by the publish script.

```
Untracked files:
  (use "git add <file>..." to include in what will be committed)
  js/
```

The reason is that `git diff-index --quiet HEAD --` returns 0 for untracked files. But, if we add before, it returns 1.

It still returns 0 if we add but there's nothing changed
2015-10-14 13:26:22 -07:00
Johan Ruokangas 080746c4c3 Add hover effect and appetize link 2015-10-14 23:04:31 +03:00
Daniel Levitt 62e238fd6a Place Kakapo in the correct alphabetical order 2015-10-14 20:02:08 +01:00
Christopher Chedeau ceb948d151 Merge pull request #3421 from rnplay/embedded-simulator-website
Documentation: Fix the site generator so the simulator's supporting javascript files get written
2015-10-14 11:45:04 -07:00
Joshua Sierles 21a8145fe8 instead of ignoring the documention javascript support files, just write them without trying to process them 2015-10-14 20:40:20 +02:00
Christopher Chedeau a4383f0a50 Merge pull request #3420 from rnplay/embedded-simulator-website
Documentation website changes for an embedded simulator in AlertIOS documentation
2015-10-14 11:05:03 -07:00
Joshua Sierles d9ee81c127 embed an appetize.io simulator for AlertIOS documentation 2015-10-14 19:57:23 +02:00
Daniel Levitt 2d18cae041 Adding Kakapo to the app showcase 2015-10-14 18:45:22 +01:00
Tadeu Zagallo 529687f923 Defer calls until bridge finishes loading
Summary: @​public

When de-batching the calls from native -> JS, some calls were being dispatched
before the bridge had finished loading, which would cause lost calls when running
on the `ContextExecutor` and redbox when running in the Chrome debugger

Reviewed By: @javache

Differential Revision: D2540746

fb-gh-sync-id: ece29406648d3cbcb42cef3b32b8774ff0c15fd8
2015-10-14 09:49:23 -07:00
Krzysztof Magiera 10a9b94b9c Skip unnecessary string allocs from frquently executed code paths.
Differential Revision: D2540814

fb-gh-sync-id: 045d012b52a6bc89d409bcc028532da1760c5775
2015-10-14 09:23:25 -07:00
exentrich 10cc72bb88 Add Raindrop.io to showcase 2015-10-14 19:12:07 +03:00
Felix Oghină aae9f0255f [docs] add docs for integrating with existing apps on Android 2015-10-14 17:10:47 +01:00
Harry Wolff 1b94586fe6 Add documentation for making PushNotificationIOS events work,
Summary: I was just implementing push notifications for my iOS app and was very confused why the `register` event wasn't working.

[Eventually I ran across this tutorial](https://medium.com/@​DannyvanderJagt/how-to-use-push-notifications-in-react-native-41e8b14aadae) that explained that I needed to update my AppDelegate to make that event work.

I've updated the documentation to include that note within the actual file.
Closes https://github.com/facebook/react-native/pull/3390

Reviewed By: @​svcscm

Differential Revision: D2537853

Pulled By: @javache

fb-gh-sync-id: 1e8860173c38f6b2d7f9d273019f201e4cc9c0f2
2015-10-14 09:09:26 -07:00
Martin Konicek 3df34f1ba8 Merge pull request #2953 from j27cai/and-patch
updated ReactiveAndroid installation info
2015-10-14 17:00:59 +01:00
Martin Konicek f8ef2c4b77 Update breaking-changes.md 2015-10-14 16:58:26 +01:00
Christopher Chedeau 92c7eb25a6 Revert changes to breaking-changes.md
This is synced internally, we can't update it on github directly. cc @mkonicek
2015-10-14 08:53:55 -07:00
Martin Konicek 9a48b7c336 Update RunningOnDeviceAndroid.md 2015-10-14 16:49:51 +01:00
Dave Sibiski c2ae790396 Removes hardcoded port 8081 from URL format strings
Summary: This allows the use of different bundle URLs & ports in Dev mode. (ie. ngrok url shared with a team member)

The bundle URL can be changed via the normal Dev Menu, or programmatically by setting the `debug_http_host` shared preference key.

For example, in your apps `MainActivity.java` before creating your `ReactRootView`:
```java
SharedPreferences mPreferences = PreferenceManager.getDefaultSharedPreferences(getApplication());
SharedPreferences.Editor editor = mPreferences.edit();
editor.putString("debug_http_host", "my.customurlandport.com:8888");
editor.commit();
```

**Breaking change**: Custom bundle URLs added via the Dev Menu will need to also include the port. This has been noted in the documentation and also on the preference description in the Dev Menu.

Addresses: https://github.com/facebook/react-native/issues/2704
Closes https://github.com/facebook/react-native/pull/2824

Reviewed By: @​svcscm

Differential Revision: D2540450

Pulled By: @javache

fb-gh-sync-id: a053de91e3095bb67640bb0f1b8761e55775bc9c
2015-10-14 08:46:15 -07:00
Yuri Zarubin 45644aab35 Check that WS connection is open before closing it on failed. Fixes #3346
Summary: Check that the WS state is set to OPEN before trying to close it when the ```websocketFailed``` event fires. Otherwise the app throws an error at the Android level.

Fixes #3346
Closes https://github.com/facebook/react-native/pull/3347

Reviewed By: @​svcscm

Differential Revision: D2535807

Pulled By: @mkonicek

fb-gh-sync-id: bb70c551ea2e582cfaa80139a265dbbca6d990d2
2015-10-14 08:46:12 -07:00
Martin Konicek 855f7be4b8 Make Android examples depend on source again
Summary: @​public

The idea was to depend on a fixed version from Maven to make running the examples easy, however that only works if we depend on both fixed version of JS and the Android artifacts.

The current way leads to version mismatches and causes confusion: https://github.com/facebook/react-native/issues/3329

This commit makes the Android examples consistent with iOS (always build the latest JS and native code from master).

Added docs here: https://github.com/facebook/react-native/commit/8abfb5e0e2015a0b8a80cba81ccf249df275adcb

Reviewed By: @foghina

Differential Revision: D2531525

fb-gh-sync-id: 7c0844b63ddeb94ad008a3f0e7a43e1af69031c4
2015-10-14 07:48:25 -07:00
Eric Jing Cai 6494746613 Update README.md 2015-10-14 10:44:14 -04:00
Mike Armstrong a041d770e4 PerfTest Stubs for OSS
Differential Revision: D2540549

fb-gh-sync-id: 01abb2e444f1636f50075df94d9275f8295340a3
2015-10-14 07:08:10 -07:00
Martin Konicek 31a02fb758 Merge pull request #2959 from BretJohnson/win-line-endings
Force LF line endings for Bash scripts, so they work on Windows
2015-10-14 14:43:21 +01:00
Krzysztof Magiera 8486a68b91 Share fontsize and numberoflines props between text and inputtext shadow nodes.
Differential Revision: D2540451

fb-gh-sync-id: 289eeaed6881efe429124a0e69195a025ac3cbb8
2015-10-14 05:41:24 -07:00
Martin Konicek ad4f589b65 Update breaking-changes.md 2015-10-14 12:53:43 +01:00
Martin Konicek 663ae6cbf7 Update breaking-changes.md 2015-10-14 12:53:16 +01:00
Martin Konicek 4b90db10da Update breaking-changes.md 2015-10-14 12:52:33 +01:00
Krzysztof Magiera 589df04846 Introduce @ReactProp-enabled LayoutShadowNode as a baseclass for most of the shadow nodes.
Differential Revision: D2540232

fb-gh-sync-id: 6dfed70c8253973897f4e447377ec5561862da23
2015-10-14 01:41:26 -07:00
James Ide 9493e96e13 Merge pull request #3396 from theinventor/patch-1
add RepairShopr to showcase
2015-10-13 22:05:34 -07:00
Troy Anderson c0654cfc96 add RepairShopr to showcase
Happy react-native users over here :)
2015-10-13 22:03:54 -07:00
Bret Johnson ea70525744 Added a .gitattributes file, ensuring that Bash script source files (gradlew and
*.sh) have normal Unix LF line endings even on Windows.  This change is needed
so gradlew, packager.sh, and other Bash scripts can run on Windows (under cygwin).
On Windows the rest of the source files will typically have CR+LF endings (Git
default), but Bash scripts need to have LF endings to work, thus this override
to force that.
2015-10-13 23:10:39 -04:00
Martin Konicek f03c7b59fd Merge pull request #3393 from passy/patch-1
Document opening rage menu via adb
2015-10-14 00:53:54 +01:00
Pascal Hartig 7aeae9778f Document opening rage menu via adb
You can access the rage menu via adb by sending the keycode manually if you don't have a physical device that you can shake.
2015-10-14 00:43:32 +01:00
Martin Konicek 0206347df3 Update KnownIssues.md 2015-10-13 23:24:44 +01:00
Krzysztof Magiera f4e2a670d3 Convert basic shadow nodes to use @ReactProp.
Differential Revision: D2537318

fb-gh-sync-id: 3061545f27953299904fe224e973fa409650d239
2015-10-13 12:57:24 -07:00
Martín Bigio 08e79deebd Update RunningOnDeviceIOS.md 2015-10-13 15:41:42 -04:00
Martín Bigio a21e226b68 Remove install command
Summary: @​public

The implementation wasn't working. Lets remove this for now and revisit if we trully want to support this.

Reviewed By: @frantic

Differential Revision: D2536442

fb-gh-sync-id: 4aca2d1d2584cd15ac855d69e6e9a5a08abf778e
2015-10-13 12:08:22 -07:00
Martín Bigio 1ab0c3b374 Update RunningOnDeviceIOS.md 2015-10-13 15:08:11 -04:00
Dave Miller 19e11232fe Move SnapshotView to SnapshotViewIOS and create an unimplemented android version
Reviewed By: @andreicoman11

Differential Revision: D2536373

fb-gh-sync-id: 0f671e8198175a15540b74d54e53eee941aa0c1b
2015-10-13 11:48:38 -07:00
Martín Bigio a102e6f587 Breaking change: Move bundle to private-cli
Summary: @​public

We need to start using the `private-cli` for the new asset system. Note this is a breaking change! People will have to upgrade their workflows/scripts to start using the new cli. Otherwise we'd need to build a thin adapter that is aware of both APIs which is something we;d like to avoid. The major API changes are:
  - API is now `entry-file` based instead of `url` based.
  - /s/--out/--bundle-output
  - /s/--out/--sourcemap-output

Also these parameters have no longer default values, you need to specify them. For instance, if you want to generate the sourcemaps you need to pass in the `--sourcemap-output` option

Lastly, additional project roots or asset roots need to be specified on the `rn-cli.config.js`.

Reviewed By: @frantic

Differential Revision: D2533877

fb-gh-sync-id: a45f9095fdf9442a9106ea7bb6a6b7f651d25273
2015-10-13 11:48:30 -07:00
Martín Bigio 793c3564ff Fail loud when module is not resolved correctly
Summary: @​public
We've been forgiving unresolved modules errors in the past but we've realized that doing so makes the codebase a bit unstable as people don't make sure to fix these errors. From now on we'll early fail and stop the packager when any module cannot be resolved (including assets)

Reviewed By: @amasad

Differential Revision: D2518076

fb-gh-sync-id: e170d95b905cc29afbe46e24b65425ddd887f77c
2015-10-13 11:48:23 -07:00
James Ide 49f7b68772 Merge pull request #3388 from paramaggarwal/patch-5
Add Myntra to showcase
2015-10-13 11:38:26 -07:00
Param Aggarwal 39bea42932 Add Myntra to showcase
We are using React Native on our app's homepage for the Daily Fashion Feed - a curated list of content to discover inspiring content. We recently went live to 100% and happy to share this news with the community.

Also, we are doing JS updates on the fly to our customers and loving it!
2015-10-13 23:53:19 +05:30
Krzysztof Magiera 05015e8d36 Extract @ReactProp annotated properties from shadow nodes.
Differential Revision: D2536419

fb-gh-sync-id: 643499d4fdcb481349dad1701391059d2362984e
2015-10-13 10:11:30 -07:00
James Ide 3d22547e31 Merge pull request #3384 from mosch/patch-2
Make platform name unselectable
2015-10-13 09:53:29 -07:00
Nick Lockwood a92f107712 Added RCTFileRequestHandler
Summary: @​public

We previously discovered that using an NSURLSessionDataTask to load local files is noticably less efficient than using regular filesystem methods.

This diff adds RCTFileRequestHandler as a replacement for RCTHTTPRequestHandler when loading local files. This reduces loading time when loading local files via XMLHttpRequest, as well as improving the performance for some image load requests.

Reviewed By: @javache

Differential Revision: D2531710

fb-gh-sync-id: 259714baac131784de494d24939f42ad52bff41a
2015-10-13 08:14:28 -07:00
Andy Street 28f6eba22d Make removeClippedSubviews default to true on ListView/RecyclingListView
Reviewed By: @satishsampath

Differential Revision: D2536046

fb-gh-sync-id: 7e0b4442bb0b3705bba3b5f7bdf873fb348a83f6
2015-10-13 08:06:26 -07:00
Andy Street e0b2c2e34e Batch native method calls in 5ms increments
Reviewed By: @mkonicek

Differential Revision: D2535803

fb-gh-sync-id: 8363a83d6966cfa4c2ea46358b10e4139333329f
2015-10-13 08:03:41 -07:00
Martín Bigio 88a880b1fc Pull into our repo cli.js
Reviewed By: @vjeux

Differential Revision: D2533584

fb-gh-sync-id: c73647202024c211ffa4a744e0f3566b09c4bc40
2015-10-13 08:03:34 -07:00
Martín Bigio 76e0f49338 Merge pull request #3385 from martinbigio/rm-cli
Temporarily remove cli.js
2015-10-13 11:03:04 -04:00
Param Aggarwal 8e03dfc99e Expose RCTTest for consumers via CocoaPods
Summary: Enables using `RCTTestRunner` in app code.
Closes https://github.com/facebook/react-native/pull/3382

Reviewed By: @​svcscm

Differential Revision: D2536089

Pulled By: @nicklockwood

fb-gh-sync-id: 35d3e57e43ac94ad3e6abf6852bf963c9dd2e1c9
2015-10-13 07:39:24 -07:00
Pieter De Baets 44c651037a Disable RCTLog completely in production
Reviewed By: @nicklockwood

Differential Revision: D2521718

fb-gh-sync-id: f06c9af3906ae8cebca0d318fc2b0e2d03671220
2015-10-13 07:31:25 -07:00
Pieter De Baets c70cbbaf61 Update npm-shrinkwrap
Reviewed By: @foghina

Differential Revision: D2535983

fb-gh-sync-id: 05769aba306e5772d399abb8084c31f9ef2cfc55
2015-10-13 07:23:26 -07:00
Felix Oghină b721c12516 import latest changes from github
Differential Revision: D2536028

fb-gh-sync-id: 2d28b10f06b4ab3a9113b7870e58faf6debba9b6
2015-10-13 06:59:42 -07:00
Martin Bigio 3f55bd1380 Temporarily remove cli.js 2015-10-13 09:57:32 -04:00
Quentin Adam c73ceff914 Corrected computation of RCText element height with padding
Summary: Corrected computation of RCText element height with padding (padding was counted twice).
See also issue #2466.
Closes https://github.com/facebook/react-native/pull/2838

Reviewed By: @​svcscm

Differential Revision: D2535924

Pulled By: @nicklockwood

fb-gh-sync-id: b9527803c0a5a6eed1db7e37c98b628750ab2045
2015-10-13 06:44:32 -07:00
Nick Lockwood 5b425278bb Removed references to isStatic from the codebase
Summary: @​public

The legacy 'isStatic' property for image sources is no longer used anywhere in our codebase, but was still being generated by the packager and referenced in the JS in various places.

This diff removes all the remaining references.

Reviewed By: @frantic

Differential Revision: D2531263

fb-gh-sync-id: 0bba0bb8473b1baa908ef7507cbf6d83efb0d9ee
2015-10-13 06:44:30 -07:00
Alexey Lang aabdc9cc9f Improve RCTPerfomanceLogger
Reviewed By: @mikearmstrong001

Differential Revision: D2531397

fb-gh-sync-id: 3f6c1831c9a7b62f857deda7a535589970cf2052
2015-10-13 06:44:27 -07:00
Tadeu Zagallo baf5b7b4d5 De-batch native->js calls and react updates
Summary: @​public

Take a step back and de-batch the bridge calls so we can have better profiling data and a better starting point to work on future optimisations. Also gave a 10~15% win on first render.

Reviewed By: @javache

Differential Revision: D2493674

fb-gh-sync-id: 05165fdd00645bdf43e844bb0c4300a2f63e7038
2015-10-13 06:44:25 -07:00
Nick Lockwood 261100d9d0 Fix data url support
Reviewed By: @tadeuzagallo

Differential Revision: D2535768

fb-gh-sync-id: 537ecc462e4a8a9e807ccad28a715419f4d5f355
2015-10-13 06:44:23 -07:00
Andrei Coman c456accbed Make text in text inputs selectable
Differential Revision: D2535762

fb-gh-sync-id: 49eaf403195fd210a87fe4babf266ee4b29b7ae8
2015-10-13 06:44:21 -07:00
Krzysztof Magiera 42e9189b77 Add getShadowNodeClass method to ViewManager interface.
Differential Revision: D2535731

fb-gh-sync-id: 7a68778ea4ca47b113d73b1d638d74d905ea2da5
2015-10-13 06:44:19 -07:00
Alex Kotliarskyi 393ead59dc Always return scale from resolveAssetSource
Reviewed By: @nicklockwood

Differential Revision: D2532565

fb-gh-sync-id: 113b8318a8f1a9b0dac4fab389a98ddd51d00e17
2015-10-13 06:44:16 -07:00
Martín Bigio 144f8598c2 Introduce configuration template
Summary: @​public

We're rolling out a replacement for `local-cli`. As part of the process we moved many hardcoded values into a JS based configuration file.

Reviewed By: @vjeux

Differential Revision: D2533111

fb-gh-sync-id: 7250bddee9989b089ded409104e098ef15d79498
2015-10-13 06:44:14 -07:00
Martín Bigio 12a68a8183 Move oss server to cli
Summary: @​public We're moving all the operation from `local-cli` to the `private-cli` and improving the code in the process. On this diff we introduce the `server` command which will be use to start the packager server. On follow up diffs we'll make changes to start using it from `local-cli`.

Reviewed By: @vjeux

Differential Revision: D2531443

fb-gh-sync-id: 5f7f12a250895265d83d1b076f6bbddb5cbdc257
2015-10-13 06:44:12 -07:00
Moritz Schwörer 0e98bc9651 Make platform unselectable
Copy method names by double clicking added platform name to clipboard. 
Annoyed me couple of times. Now double clicking on method name in documentation will only select the name of the method.
2015-10-13 15:12:12 +02:00
Martin Bigio 6f04687436 Include private-cli on npm export
Reviewed By: @vjeux

Differential Revision: D2532645

fb-gh-sync-id: 94e45cd0e799dd450feb6e1a8e9fc85a6655dffb
2015-10-12 13:02:24 -07:00
Martín Bigio 31aacd78e8 Fix error message when invoking wrong command.
Summary: @​public

The message we were showing before didn't include correctly the commands the private cli had.

Reviewed By: @vjeux

Differential Revision: D2530164

fb-gh-sync-id: 2b00c74cb41441686587588632815c5948f3a864
2015-10-12 13:01:00 -07:00
Krzysztof Magiera 3561c9ed2b Restructure ViewManagersPropertyCache.
Differential Revision: D2532365

fb-gh-sync-id: e5cbb55093310b47b6525839d7c5929264d158ad
2015-10-12 12:02:56 -07:00
Martín Bigio f39816eb38 Tweak cli config resolution
Summary: @​public
We need to tweak the resolution algorithm to start looking for the config file at the directory where the node script is running. This is needed to make development easier as internally we'll need 2 config files, one for open source and other one for internal use. This won't affect oss as for they we'll generate this file as part of the `init` command.

Reviewed By: @vjeux

Differential Revision: D2530161

fb-gh-sync-id: 17c21a56a26a91f2671197e8daa36c19adcea8ee
2015-10-12 12:02:50 -07:00
Jiajie Zhu aacd2d90a4 fix watchPosition keep updating location
Reviewed By: @nicklockwood

Differential Revision: D2519624

fb-gh-sync-id: 7366c5ac9e06082448b9fbba3c616aaf8e4183f9
2015-10-12 11:56:45 -07:00
Christopher Chedeau da359c312a Fix jest test that runs the polyfill 10 times
Summary: @​public

jest is running the polyfill multiple times on the same environment (cc @cpojer, need to fix that!). By default jest doesn't have XMLHttpRequest polyfilled so it'll define a property with writable to be false. It'll fatal the second time it tries to override XMLHttpRequest.

The hacky workaround is to make properties that do not exist with writable: true. But the long term fix would be to make jest stop running the polyfill multiple times.

Reviewed By: @javache

Differential Revision: D2532019

fb-gh-sync-id: a82abf69541781a64a0744798c736f90833e28cb
2015-10-12 11:52:28 -07:00
Alex Kotliarskyi 62d0586e59 Rely only on scriptURL when loading images
Reviewed By: @foghina

Differential Revision: D2527119

fb-gh-sync-id: e2a9618f729a2ec03b1959fa13fae46620cc379e
2015-10-12 11:36:26 -07:00
Eric Jing Cai a31d3a9d4f updated ReactiveAndroid installation info 2015-09-22 22:22:03 -04:00
321 changed files with 8527 additions and 5250 deletions
+6
View File
@@ -0,0 +1,6 @@
# 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
+5
View File
@@ -72,7 +72,12 @@ env:
- TEST_TYPE=build_website
- TEST_TYPE=e2e
global:
# $GITHUB_TOKEN
- secure: "HlmG8M2DmBUSBh6KH1yVIe/8gR4iibg4WfcHq1x/xYQxGbvleq7NOo04V6eFHnl9cvZCu+PKH0841WLnGR7c4BBf47GVu/o16nXzggPumHKy++lDzxFPlJ1faMDfjg/5vjbAxRUe7D3y98hQSeGHH4tedc8LvTaFLVu7iiGqvjU="
# $APPETIZE_TOKEN
- secure: "egsvVSpszTzrNd6bN62DsVAzMiSZI/OHgdizfPryqvqWBf655ztE6XFQSEFNpuIAzSKDDF25ioT8iPfVsbC1iK6HDWHfmqYxML0L+OoU0gi+hV2oKUBFZDZ1fwSnFoWuBdNdMDpLlUxvJp6N1WyfNOB2dxuZUt8eTt48Hi3+Hpc="
# $S3_TOKEN
- secure: "lY8JZPA0A7zT7L5KF9BBg34XYWIeR/RJiEvE7l7oVr88KnEPtyd//79eHhhVKnUnav7zsk5QJwkcX0MxKTp/dp4G0Am+zOX+sfA8kQrJ+2/+FzFW7AEsW/kHByfaIEIly9DQvUFt4I4oMm8nQZysJLahDgNWglyI3RTuJp//hcY="
branches:
only:
+1 -1
View File
@@ -46,8 +46,8 @@ Facebook has a [bounty program](https://www.facebook.com/whitehat/) for the safe
## How to Get in Touch
* IRC - [#reactnative on freenode](http://webchat.freenode.net/?channels=reactnative)
* [Facebook group](https://www.facebook.com/groups/react.native.community/)
* Reactiflux — [#react-native](http://join.reactiflux.com/)
## Style Guide
+1 -2
View File
@@ -23,7 +23,6 @@ var {
ProgressBarAndroid,
StyleSheet,
Text,
TextInput,
View,
} = React;
var TimerMixin = require('react-timer-mixin');
@@ -271,7 +270,7 @@ var SearchScreen = React.createClass({
style = [style, styles.rowSeparatorHide];
}
return (
<View key={"SEP_" + sectionID + "_" + rowID} style={style}/>
<View key={'SEP_' + sectionID + '_' + rowID} style={style}/>
);
},
+2 -6
View File
@@ -26,10 +26,6 @@ dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:appcompat-v7:23.0.1'
// Depend on pre-built React Native
compile 'com.facebook.react:react-native:0.11.+'
// Depend on React Native source.
// This is useful for testing your changes when working on React Native.
// compile project(':ReactAndroid')
// Build React Native from source
compile project(':ReactAndroid')
}
@@ -44,7 +44,7 @@ var AccessibilityIOSExample = React.createClass({
Accessibility label example
</Text>
</View>
<View accessibilityTraits={["button", "selected"]}
<View accessibilityTraits={['button', 'selected']}
accessible={true}>
<Text>
Accessibility traits example
+5 -5
View File
@@ -24,9 +24,9 @@ var {
} = React;
var BUTTONS = [
'Button Index: 0',
'Button Index: 1',
'Button Index: 2',
'Option 0',
'Option 1',
'Option 2',
'Destruct',
'Cancel',
];
@@ -47,7 +47,7 @@ var ActionSheetExample = React.createClass({
Click to show the ActionSheet
</Text>
<Text>
Clicked button at index: "{this.state.clicked}"
Clicked button: {this.state.clicked}
</Text>
</View>
);
@@ -99,7 +99,7 @@ var ShareActionSheetExample = React.createClass({
} else {
text = 'You didn\'t share';
}
this.setState({text})
this.setState({text});
});
}
});
+2 -2
View File
@@ -40,7 +40,7 @@ var AppStateSubscription = React.createClass({
AppStateIOS.removeEventListener('memoryWarning', this._handleMemoryWarning);
},
_handleMemoryWarning: function() {
this.setState({memoryWarnings: this.state.memoryWarnings + 1})
this.setState({memoryWarnings: this.state.memoryWarnings + 1});
},
_handleAppStateChange: function(appState) {
var previousAppStates = this.state.previousAppStates.slice();
@@ -92,7 +92,7 @@ exports.examples = [
},
{
title: 'Memory Warnings',
description: "In the simulator, hit Shift+Command+M to simulate a memory warning.",
description: 'In the simulator, hit Shift+Command+M to simulate a memory warning.',
render(): ReactElement { return <AppStateSubscription showMemoryWarnings={true} />; }
},
];
+19
View File
@@ -24,6 +24,8 @@ var {
ActivityIndicatorIOS
} = React;
var base64Icon = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEsAAABLCAQAAACSR7JhAAADtUlEQVR4Ac3YA2Bj6QLH0XPT1Fzbtm29tW3btm3bfLZtv7e2ObZnms7d8Uw098tuetPzrxv8wiISrtVudrG2JXQZ4VOv+qUfmqCGGl1mqLhoA52oZlb0mrjsnhKpgeUNEs91Z0pd1kvihA3ULGVHiQO2narKSHKkEMulm9VgUyE60s1aWoMQUbpZOWE+kaqs4eLEjdIlZTcFZB0ndc1+lhB1lZrIuk5P2aib1NBpZaL+JaOGIt0ls47SKzLC7CqrlGF6RZ09HGoNy1lYl2aRSWL5GuzqWU1KafRdoRp0iOQEiDzgZPnG6DbldcomadViflnl/cL93tOoVbsOLVM2jylvdWjXolWX1hmfZbGR/wjypDjFLSZIRov09BgYmtUqPQPlQrPapecLgTIy0jMgPKtTeob2zWtrGH3xvjUkPCtNg/tm1rjwrMa+mdUkPd3hWbH0jArPGiU9ufCsNNWFZ40wpwn+62/66R2RUtoso1OB34tnLOcy7YB1fUdc9e0q3yru8PGM773vXsuZ5YIZX+5xmHwHGVvlrGPN6ZSiP1smOsMMde40wKv2VmwPPVXNut4sVpUreZiLBHi0qln/VQeI/LTMYXpsJtFiclUN+5HVZazim+Ky+7sAvxWnvjXrJFneVtLWLyPJu9K3cXLWeOlbMTlrIelbMDlrLenrjEQOtIF+fuI9xRp9ZBFp6+b6WT8RrxEpdK64BuvHgDk+vUy+b5hYk6zfyfs051gRoNO1usU12WWRWL73/MMEy9pMi9qIrR4ZpV16Rrvduxazmy1FSvuFXRkqTnE7m2kdb5U8xGjLw/spRr1uTov4uOgQE+0N/DvFrG/Jt7i/FzwxbA9kDanhf2w+t4V97G8lrT7wc08aA2QNUkuTfW/KimT01wdlfK4yEw030VfT0RtZbzjeMprNq8m8tnSTASrTLti64oBNdpmMQm0eEwvfPwRbUBywG5TzjPCsdwk3IeAXjQblLCoXnDVeoAz6SfJNk5TTzytCNZk/POtTSV40NwOFWzw86wNJRpubpXsn60NJFlHeqlYRbslqZm2jnEZ3qcSKgm0kTli3zZVS7y/iivZTweYXJ26Y+RTbV1zh3hYkgyFGSTKPfRVbRqWWVReaxYeSLarYv1Qqsmh1s95S7G+eEWK0f3jYKTbV6bOwepjfhtafsvUsqrQvrGC8YhmnO9cSCk3yuY984F1vesdHYhWJ5FvASlacshUsajFt2mUM9pqzvKGcyNJW0arTKN1GGGzQlH0tXwLDgQTurS8eIQAAAABJRU5ErkJggg==';
var ImageCapInsetsExample = require('./ImageCapInsetsExample');
var NetworkImageExample = React.createClass({
@@ -335,6 +337,18 @@ exports.examples = [
},
platform: 'ios',
},
{
title: 'Base64 image',
render: function() {
return (
<Image
style={styles.base64}
source={{uri: base64Icon, scale: 3}}
/>
);
},
platform: 'ios',
},
{
title: 'Cap Insets',
description:
@@ -399,4 +413,9 @@ var styles = StyleSheet.create({
flex: 1,
height: 200,
},
base64: {
flex: 1,
height: 50,
resizeMode: 'contain',
},
});
@@ -21,8 +21,7 @@ var {
ScrollView,
Text,
TouchableHighlight,
TouchableOpacity,
View,
TouchableOpacity
} = React;
var _getRandomRoute = function() {
@@ -82,19 +81,19 @@ var BreadcrumbNavSample = React.createClass({
return (
<ScrollView style={styles.scene}>
<NavButton
onPress={() => { navigator.push(_getRandomRoute()) }}
onPress={() => { navigator.push(_getRandomRoute()); }}
text="Push"
/>
<NavButton
onPress={() => { navigator.immediatelyResetRouteStack([_getRandomRoute(), _getRandomRoute()]) }}
onPress={() => { navigator.immediatelyResetRouteStack([_getRandomRoute(), _getRandomRoute()]); }}
text="Reset w/ 2 scenes"
/>
<NavButton
onPress={() => { navigator.popToTop() }}
onPress={() => { navigator.popToTop(); }}
text="Pop to top"
/>
<NavButton
onPress={() => { navigator.replace(_getRandomRoute()) }}
onPress={() => { navigator.replace(_getRandomRoute()); }}
text="Replace"
/>
<NavButton
@@ -23,7 +23,6 @@ var {
Text,
TouchableHighlight,
TouchableOpacity,
View,
} = React;
var cssVar = require('cssVar');
@@ -134,7 +134,7 @@ var TabBarExample = React.createClass({
<Navigator
ref={this._setNavigatorRef}
style={styles.container}
initialRoute={{ message: "First Scene", }}
initialRoute={{ message: 'First Scene', }}
renderScene={this.renderScene}
configureScene={(route) => {
if (route.sceneConfig) {
@@ -56,7 +56,7 @@ var SwitchAndroidExample = React.createClass({
onValueChange={(value) => this.setState({eventSwitchIsOn: value})}
style={{marginBottom: 10}}
value={this.state.eventSwitchIsOn} />
<Text>{this.state.eventSwitchIsOn ? "On" : "Off"}</Text>
<Text>{this.state.eventSwitchIsOn ? 'On' : 'Off'}</Text>
</UIExplorerBlock>
<UIExplorerBlock title="Switches are controlled components">
<SwitchAndroid />
+2 -2
View File
@@ -107,7 +107,7 @@ var EventSwitchExample = React.createClass({
onValueChange={(value) => this.setState({eventSwitchIsOn: value})}
style={{marginBottom: 10}}
value={this.state.eventSwitchIsOn} />
<Text>{this.state.eventSwitchIsOn ? "On" : "Off"}</Text>
<Text>{this.state.eventSwitchIsOn ? 'On' : 'Off'}</Text>
</View>
<View>
<SwitchIOS
@@ -118,7 +118,7 @@ var EventSwitchExample = React.createClass({
onValueChange={(value) => this.setState({eventSwitchRegressionIsOn: value})}
style={{marginBottom: 10}}
value={this.state.eventSwitchRegressionIsOn} />
<Text>{this.state.eventSwitchRegressionIsOn ? "On" : "Off"}</Text>
<Text>{this.state.eventSwitchRegressionIsOn ? 'On' : 'Off'}</Text>
</View>
</View>
);
-2
View File
@@ -19,9 +19,7 @@ var React = require('react-native');
var {
AlertIOS,
Platform,
Text,
ToastAndroid,
TouchableHighlight,
View,
} = React;
var TimerMixin = require('react-timer-mixin');
@@ -31,7 +31,7 @@ var ToastExample = React.createClass({
statics: {
title: 'Toast Example',
description: 'Example that demostrates the use of an Android Toast to provide feedback.',
description: 'Example that demonstrates the use of an Android Toast to provide feedback.',
},
getInitialState: function() {
@@ -101,6 +101,12 @@ var ToolbarAndroidExample = React.createClass({
title="Bunny and Hawk"
style={styles.toolbar} />
</UIExplorerBlock>
<UIExplorerBlock title="Toolbar with custom overflowIcon">
<ToolbarAndroid
actions={toolbarActions}
overflowIcon={require('./bunny.png')}
style={styles.toolbar} />
</UIExplorerBlock>
</UIExplorerPage>
);
},
@@ -51,6 +51,7 @@
14D6D7291B2222EF001FB087 /* libReact.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 14AADF041AC3DB95002390C9 /* libReact.a */; };
14DC67F41AB71881001358AB /* libRCTPushNotification.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 14DC67F11AB71876001358AB /* libRCTPushNotification.a */; };
3578590A1B28D2CF00341EDB /* libRCTLinking.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 357859011B28D2C500341EDB /* libRCTLinking.a */; };
3D36915B1BDA8CBB007B22D8 /* uie_thumb_big.png in Resources */ = {isa = PBXBuildFile; fileRef = 3D36915A1BDA8CBB007B22D8 /* uie_thumb_big.png */; settings = {ASSET_TAGS = (); }; };
3DB99D0C1BA0340600302749 /* UIExplorerIntegrationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 3DB99D0B1BA0340600302749 /* UIExplorerIntegrationTests.m */; };
834C36EC1AF8DED70019C93C /* libRCTSettings.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 834C36D21AF8DA610019C93C /* libRCTSettings.a */; };
83636F8F1B53F22C009F943E /* RCTUIManagerScenarioTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 83636F8E1B53F22C009F943E /* RCTUIManagerScenarioTests.m */; };
@@ -222,6 +223,7 @@
14DC67E71AB71876001358AB /* RCTPushNotification.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTPushNotification.xcodeproj; path = ../../Libraries/PushNotificationIOS/RCTPushNotification.xcodeproj; sourceTree = "<group>"; };
14E0EEC81AB118F7000DECC3 /* RCTActionSheet.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTActionSheet.xcodeproj; path = ../../Libraries/ActionSheetIOS/RCTActionSheet.xcodeproj; sourceTree = "<group>"; };
357858F81B28D2C400341EDB /* RCTLinking.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTLinking.xcodeproj; path = ../../Libraries/LinkingIOS/RCTLinking.xcodeproj; sourceTree = "<group>"; };
3D36915A1BDA8CBB007B22D8 /* uie_thumb_big.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = uie_thumb_big.png; path = UIExplorer/Images.xcassets/uie_thumb_big.imageset/uie_thumb_big.png; sourceTree = SOURCE_ROOT; };
3DB99D0B1BA0340600302749 /* UIExplorerIntegrationTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UIExplorerIntegrationTests.m; sourceTree = "<group>"; };
58005BE41ABA80530062E044 /* RCTTest.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTTest.xcodeproj; path = ../../Libraries/RCTTest/RCTTest.xcodeproj; sourceTree = "<group>"; };
83636F8E1B53F22C009F943E /* RCTUIManagerScenarioTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTUIManagerScenarioTests.m; sourceTree = "<group>"; };
@@ -436,6 +438,7 @@
143BC5971B21E3E100462512 /* Supporting Files */ = {
isa = PBXGroup;
children = (
3D36915A1BDA8CBB007B22D8 /* uie_thumb_big.png */,
143BC5981B21E3E100462512 /* Info.plist */,
);
name = "Supporting Files";
@@ -817,6 +820,7 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
3D36915B1BDA8CBB007B22D8 /* uie_thumb_big.png in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -966,6 +970,7 @@
OTHER_LDFLAGS = "-ObjC";
PRODUCT_BUNDLE_IDENTIFIER = com.facebook.internal.uiexplorer.local;
PRODUCT_NAME = UIExplorer;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
@@ -985,6 +990,7 @@
OTHER_LDFLAGS = "-ObjC";
PRODUCT_BUNDLE_IDENTIFIER = com.facebook.internal.uiexplorer.local;
PRODUCT_NAME = UIExplorer;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Release;
};
+1 -1
View File
@@ -66,7 +66,7 @@
* Load from pre-bundled file on disk. To re-generate the static bundle, `cd`
* to your Xcode project folder and run
*
* $ curl 'http://localhost:8081/Examples/UIExplorer/UIExplorerApp.ios.bundle' -o main.jsbundle
* $ curl 'http://localhost:8081/Examples/UIExplorer/UIExplorerApp.ios.bundle?platform=ios' -o main.jsbundle
*
* then add the `main.jsbundle` file to your project and uncomment this line:
*/
@@ -0,0 +1,21 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x",
"filename" : "blue_square.png"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 128 B

@@ -0,0 +1,21 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x",
"filename" : "red_square.png"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 129 B

@@ -36,7 +36,7 @@
#endif
NSOperatingSystemVersion version = [NSProcessInfo processInfo].operatingSystemVersion;
RCTAssert(version.majorVersion == 8 || version.minorVersion >= 3, @"Tests should be run on iOS 8.3+, found %zd.%zd.%zd", version.majorVersion, version.minorVersion, version.patchVersion);
RCTAssert((version.majorVersion == 8 && version.minorVersion >= 3) || version.majorVersion >= 9, @"Tests should be run on iOS 8.3+, found %zd.%zd.%zd", version.majorVersion, version.minorVersion, version.patchVersion);
_runner = RCTInitRunnerForApp(@"Examples/UIExplorer/UIExplorerIntegrationTests/js/IntegrationTestsApp", nil);
}
@@ -62,9 +62,12 @@
RCT_TEST(IntegrationTestHarnessTest)
RCT_TEST(TimersTest)
RCT_TEST(AsyncStorageTest)
RCT_TEST(LayoutEventsTest)
RCT_TEST(AppEventsTest)
RCT_TEST(ImageSnapshotTest)
RCT_TEST(SimpleSnapshotTest)
RCT_TEST(PromiseTest)
// Disable due to flakiness: #8686784
//RCT_TEST(LayoutEventsTest)
//RCT_TEST(PromiseTest)
@end
@@ -37,7 +37,7 @@
#endif
NSOperatingSystemVersion version = [NSProcessInfo processInfo].operatingSystemVersion;
RCTAssert(version.majorVersion == 8 || version.minorVersion >= 3, @"Snapshot tests should be run on iOS 8.3+, found %zd.%zd.%zd", version.majorVersion, version.minorVersion, version.patchVersion);
RCTAssert((version.majorVersion == 8 && version.minorVersion >= 3) || version.majorVersion >= 9, @"Tests should be run on iOS 8.3+, found %zd.%zd.%zd", version.majorVersion, version.minorVersion, version.patchVersion);
_runner = RCTInitRunnerForApp(@"Examples/UIExplorer/UIExplorerApp.ios", nil);
_runner.recordMode = NO;
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 128 B

@@ -0,0 +1,42 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
'use strict';
var React = require('react-native');
var {
Image,
View,
} = React;
var { TestModule } = React.addons;
var ImageSnapshotTest = React.createClass({
componentDidMount() {
if (!TestModule.verifySnapshot) {
throw new Error('TestModule.verifySnapshot not defined.');
}
},
done(success) {
TestModule.markTestPassed(success);
},
render() {
return (
<Image
source={require('image!blue_square')}
defaultSource={require('image!red_square')}
onLoad={() => TestModule.verifySnapshot(this.done)} />
);
}
});
ImageSnapshotTest.displayName = 'ImageSnapshotTest';
module.exports = ImageSnapshotTest;
@@ -31,6 +31,7 @@ var TESTS = [
require('./LayoutEventsTest'),
require('./AppEventsTest'),
require('./SimpleSnapshotTest'),
require('./ImageSnapshotTest'),
require('./PromiseTest'),
];
Binary file not shown.

After

Width:  |  Height:  |  Size: 129 B

+3 -3
View File
@@ -19,7 +19,7 @@ var React = require('react-native');
var {
AppRegistry,
Settings,
SnapshotView,
SnapshotViewIOS,
StyleSheet,
} = React;
@@ -85,9 +85,9 @@ COMPONENTS.concat(APIS).forEach((Example) => {
render: function() {
var Renderable = UIExplorerListBase.makeRenderable(Example);
return (
<SnapshotView>
<SnapshotViewIOS>
<Renderable />
</SnapshotView>
</SnapshotViewIOS>
);
},
});
@@ -24,7 +24,7 @@
@property (nonatomic, strong, readonly) RCTBridge *batchedBridge;
- (void)_handleBuffer:(id)buffer;
- (void)handleBuffer:(id)buffer;
- (void)setUp;
@end
@@ -169,7 +169,7 @@ _Pragma("clang diagnostic pop")
NSArray *args = @[@1234, @5678, @"stringy", @{@"a": @1}, @42];
NSArray *buffer = @[@[testModuleID], @[testMethodID], @[args], @[], @1234567];
[_bridge.batchedBridge _handleBuffer:buffer];
[_bridge.batchedBridge handleBuffer:buffer];
dispatch_sync(_methodQueue, ^{
// clear the queue
@@ -180,7 +180,7 @@ _Pragma("clang diagnostic pop")
- (void)DISABLED_testBadArgumentsCount
{
//NSArray *bufferWithMissingArgument = @[@[@1], @[@0], @[@[@1234, @5678, @"stringy", @{@"a": @1}/*, @42*/]], @[], @1234567];
//[_bridge _handleBuffer:bufferWithMissingArgument];
//[_bridge handleBuffer:bufferWithMissingArgument];
NSLog(@"WARNING: testBadArgumentsCount is temporarily disabled until we have a better way to test cases that we expect to trigger redbox errors");
}
@@ -40,7 +40,7 @@
- (void)testNativeLoggingHookExceptionBehavior
{
dispatch_semaphore_t doneSem = dispatch_semaphore_create(0);
[_executor executeApplicationScript:@"var x = {toString: function() { throw 1; }}; nativeLoggingHook(x);"
[_executor executeApplicationScript:[@"var x = {toString: function() { throw 1; }}; nativeLoggingHook(x);" dataUsingEncoding:NSUTF8StringEncoding]
sourceURL:[NSURL URLWithString:@"file://"]
onComplete:^(__unused id error){
dispatch_semaphore_signal(doneSem);
@@ -128,7 +128,7 @@ static uint64_t _get_time_nanoseconds(void)
} \
";
[_executor executeApplicationScript:script sourceURL:[NSURL URLWithString:@"http://localhost:8081/"] onComplete:^(__unused NSError *error) {
[_executor executeApplicationScript:[script dataUsingEncoding:NSUTF8StringEncoding] sourceURL:[NSURL URLWithString:@"http://localhost:8081/"] onComplete:^(__unused NSError *error) {
NSMutableArray *params = [NSMutableArray new];
id data = @1;
for (int i = 0; i < 4; i++) {
@@ -25,14 +25,14 @@ typedef RCTImageLoaderCancellationBlock (^RCTImageURLLoaderLoadImageURLHandler)(
@end
typedef BOOL (^RCTImageDecoderCanDecodeImageDataHandler)(NSData *imageData);
typedef RCTImageLoaderCancellationBlock (^RCTImageDecoderDecodeImageDataHandler)(NSData *imageData, CGSize size, CGFloat scale, UIViewContentMode resizeMode, RCTImageLoaderCompletionBlock completionHandler);
typedef BOOL (^RCTImageDataDecoderCanDecodeImageDataHandler)(NSData *imageData);
typedef RCTImageLoaderCancellationBlock (^RCTImageDataDecoderDecodeImageDataHandler)(NSData *imageData, CGSize size, CGFloat scale, UIViewContentMode resizeMode, RCTImageLoaderCompletionBlock completionHandler);
@interface RCTConcreteImageDecoder : NSObject <RCTImageDecoder>
@interface RCTConcreteImageDecoder : NSObject <RCTImageDataDecoder>
- (instancetype)initWithPriority:(float)priority
canDecodeImageDataHandler:(RCTImageDecoderCanDecodeImageDataHandler)canDecodeImageDataHandler
decodeImageDataHandler:(RCTImageDecoderDecodeImageDataHandler)decodeImageDataHandler;
canDecodeImageDataHandler:(RCTImageDataDecoderCanDecodeImageDataHandler)canDecodeImageDataHandler
decodeImageDataHandler:(RCTImageDataDecoderDecodeImageDataHandler)decodeImageDataHandler;
@end
@@ -52,7 +52,7 @@
return _loadImageURLHandler(imageURL, size, scale, resizeMode, progressHandler, completionHandler);
}
- (float)imageLoaderPriority
- (float)loaderPriority
{
return _priority;
}
@@ -61,8 +61,8 @@
@implementation RCTConcreteImageDecoder
{
RCTImageDecoderCanDecodeImageDataHandler _canDecodeImageDataHandler;
RCTImageDecoderDecodeImageDataHandler _decodeImageDataHandler;
RCTImageDataDecoderCanDecodeImageDataHandler _canDecodeImageDataHandler;
RCTImageDataDecoderDecodeImageDataHandler _decodeImageDataHandler;
float _priority;
}
@@ -76,7 +76,7 @@
return nil;
}
- (instancetype)initWithPriority:(float)priority canDecodeImageDataHandler:(RCTImageDecoderCanDecodeImageDataHandler)canDecodeImageDataHandler decodeImageDataHandler:(RCTImageDecoderDecodeImageDataHandler)decodeImageDataHandler
- (instancetype)initWithPriority:(float)priority canDecodeImageDataHandler:(RCTImageDataDecoderCanDecodeImageDataHandler)canDecodeImageDataHandler decodeImageDataHandler:(RCTImageDataDecoderDecodeImageDataHandler)decodeImageDataHandler
{
if ((self = [super init])) {
_canDecodeImageDataHandler = [canDecodeImageDataHandler copy];
@@ -97,7 +97,7 @@
return _decodeImageDataHandler(imageData, size, scale, resizeMode, completionHandler);
}
- (float)imageDecoderPriority
- (float)decoderPriority
{
return _priority;
}
@@ -95,7 +95,7 @@ RCTDefineImageDecoder(RCTImageLoaderTestsDecoder2)
NSData *data = [NSData dataWithBytesNoCopy:blackGIF length:sizeof(blackGIF) freeWhenDone:NO];
UIImage *image = [[UIImage alloc] initWithData:data];
id<RCTImageDecoder> decoder = [[RCTImageLoaderTestsDecoder1 alloc] initWithPriority:1.0 canDecodeImageDataHandler:^BOOL(__unused NSData *imageData) {
id<RCTImageDataDecoder> decoder = [[RCTImageLoaderTestsDecoder1 alloc] initWithPriority:1.0 canDecodeImageDataHandler:^BOOL(__unused NSData *imageData) {
return YES;
} decodeImageDataHandler:^RCTImageLoaderCancellationBlock(NSData *imageData, __unused CGSize size, __unused CGFloat scale, __unused UIViewContentMode resizeMode, RCTImageLoaderCompletionBlock completionHandler) {
XCTAssertEqualObjects(imageData, data);
@@ -118,7 +118,7 @@ RCTDefineImageDecoder(RCTImageLoaderTestsDecoder2)
NSData *data = [NSData dataWithBytesNoCopy:blackGIF length:sizeof(blackGIF) freeWhenDone:NO];
UIImage *image = [[UIImage alloc] initWithData:data];
id<RCTImageDecoder> decoder1 = [[RCTImageLoaderTestsDecoder1 alloc] initWithPriority:1.0 canDecodeImageDataHandler:^BOOL(__unused NSData *imageData) {
id<RCTImageDataDecoder> decoder1 = [[RCTImageLoaderTestsDecoder1 alloc] initWithPriority:1.0 canDecodeImageDataHandler:^BOOL(__unused NSData *imageData) {
return YES;
} decodeImageDataHandler:^RCTImageLoaderCancellationBlock(NSData *imageData, __unused CGSize size, __unused CGFloat scale, __unused UIViewContentMode resizeMode, RCTImageLoaderCompletionBlock completionHandler) {
XCTAssertEqualObjects(imageData, data);
@@ -126,7 +126,7 @@ RCTDefineImageDecoder(RCTImageLoaderTestsDecoder2)
return nil;
}];
id<RCTImageDecoder> decoder2 = [[RCTImageLoaderTestsDecoder2 alloc] initWithPriority:0.5 canDecodeImageDataHandler:^BOOL(__unused NSData *imageData) {
id<RCTImageDataDecoder> decoder2 = [[RCTImageLoaderTestsDecoder2 alloc] initWithPriority:0.5 canDecodeImageDataHandler:^BOOL(__unused NSData *imageData) {
return YES;
} decodeImageDataHandler:^RCTImageLoaderCancellationBlock(__unused NSData *imageData, __unused CGSize size, __unused CGFloat scale, __unused UIViewContentMode resizeMode, __unused RCTImageLoaderCompletionBlock completionHandler) {
XCTFail(@"Should not have used decoder2");
+1 -1
View File
@@ -262,7 +262,7 @@ class FormUploader extends React.Component {
exports.framework = 'React';
exports.title = 'XMLHttpRequest';
exports.description = 'Example that demostrates upload and download requests ' +
exports.description = 'Example that demonstrates upload and download requests ' +
'using XMLHttpRequest.';
exports.examples = [{
title: 'File Download',
+2 -6
View File
@@ -26,10 +26,6 @@ dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:appcompat-v7:23.0.1'
// Depend on pre-built React Native
compile 'com.facebook.react:react-native:0.11.+'
// Depend on React Native source.
// This is useful for testing your changes when working on React Native.
// compile project(':ReactAndroid')
// Build React Native from source
compile project(':ReactAndroid')
}
@@ -27,7 +27,6 @@ var ActionSheetIOS = {
);
RCTActionSheetManager.showActionSheetWithOptions(
options,
() => {}, // RKActionSheet compatibility hack
callback
);
},
+127 -77
View File
@@ -21,64 +21,136 @@
@implementation RCTActionSheetManager
{
NSMutableDictionary *_callbacks;
// Use NSMapTable, as UIAlertViews do not implement <NSCopying>
// which is required for NSDictionary keys
NSMapTable *_callbacks;
}
RCT_EXPORT_MODULE()
- (instancetype)init
{
if ((self = [super init])) {
_callbacks = [NSMutableDictionary new];
}
return self;
}
- (dispatch_queue_t)methodQueue
{
return dispatch_get_main_queue();
}
/*
* 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.
*/
- (CGRect)sourceRectInView:(UIView *)sourceView
anchorViewTag:(NSNumber *)anchorViewTag
{
if (anchorViewTag) {
UIView *anchorView = [self.bridge.uiManager viewForReactTag:anchorViewTag];
return [anchorView convertRect:anchorView.bounds toView:sourceView];
} else {
return (CGRect){sourceView.center, {1, 1}};
}
}
RCT_EXPORT_METHOD(showActionSheetWithOptions:(NSDictionary *)options
failureCallback:(__unused RCTResponseSenderBlock)failureCallback
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"]];
NSArray *buttons = [RCTConvert NSStringArray:options[@"options"]];
NSInteger destructiveButtonIndex = options[@"destructiveButtonIndex"] ? [RCTConvert NSInteger:options[@"destructiveButtonIndex"]] : -1;
NSInteger cancelButtonIndex = options[@"cancelButtonIndex"] ? [RCTConvert NSInteger:options[@"cancelButtonIndex"]] : -1;
UIViewController *controller = RCTSharedApplication().delegate.window.rootViewController;
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"]];
UIView *sourceView = controller.view;
CGRect sourceRect = [self sourceRectInView:sourceView anchorViewTag:anchorViewTag];
#if __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_8_0
if ([UIAlertController class] == nil) {
UIActionSheet *actionSheet = [UIActionSheet new];
actionSheet.title = title;
for (NSString *option in buttons) {
[actionSheet addButtonWithTitle:option];
}
actionSheet.destructiveButtonIndex = destructiveButtonIndex;
actionSheet.cancelButtonIndex = cancelButtonIndex;
actionSheet.delegate = self;
[_callbacks setObject:callback forKey:actionSheet];
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
[actionSheet showFromRect:sourceRect inView:sourceView animated:YES];
} else {
[actionSheet showInView:sourceView];
}
} else
#endif
{
UIAlertController *alertController =
[UIAlertController alertControllerWithTitle:title
message:nil
preferredStyle:UIAlertControllerStyleActionSheet];
NSInteger index = 0;
for (NSString *option in buttons) {
UIAlertActionStyle style = UIAlertActionStyleDefault;
if (index == destructiveButtonIndex) {
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.modalPresentationStyle = UIModalPresentationPopover;
alertController.popoverPresentationController.sourceView = sourceView;
alertController.popoverPresentationController.sourceRect = sourceRect;
if (!anchorViewTag) {
alertController.popoverPresentationController.permittedArrowDirections = 0;
}
[controller presentViewController:alertController animated:YES completion:nil];
}
}
RCT_EXPORT_METHOD(showShareActionSheetWithOptions:(NSDictionary *)options
failureCallback:(RCTResponseErrorBlock)failureCallback
successCallback:(RCTResponseSenderBlock)successCallback)
{
if (RCTRunningInAppExtension()) {
RCTLogError(@"Unable to show action sheet from app extension");
return;
}
UIActionSheet *actionSheet = [UIActionSheet new];
actionSheet.title = options[@"title"];
for (NSString *option in options[@"options"]) {
[actionSheet addButtonWithTitle:option];
}
if (options[@"destructiveButtonIndex"]) {
actionSheet.destructiveButtonIndex = [options[@"destructiveButtonIndex"] integerValue];
}
if (options[@"cancelButtonIndex"]) {
actionSheet.cancelButtonIndex = [options[@"cancelButtonIndex"] integerValue];
}
actionSheet.delegate = self;
_callbacks[RCTKeyForInstance(actionSheet)] = successCallback;
UIWindow *appWindow = RCTSharedApplication().delegate.window;
if (appWindow == nil) {
RCTLogError(@"Tried to display action sheet but there is no application window. options: %@", options);
return;
}
[actionSheet showInView:appWindow];
}
RCT_EXPORT_METHOD(showShareActionSheetWithOptions:(NSDictionary *)options
failureCallback:(RCTResponseSenderBlock)failureCallback
successCallback:(RCTResponseSenderBlock)successCallback)
{
NSMutableArray *items = [NSMutableArray array];
NSString *message = [RCTConvert NSString:options[@"message"]];
if (message) {
@@ -89,22 +161,18 @@ RCT_EXPORT_METHOD(showShareActionSheetWithOptions:(NSDictionary *)options
[items addObject:URL];
}
if (items.count == 0) {
failureCallback(@[@"No `url` or `message` to share"]);
return;
}
if (RCTRunningInAppExtension()) {
failureCallback(@[@"Unable to show action sheet from app extension"]);
RCTLogError(@"No `url` or `message` to share");
return;
}
UIActivityViewController *share = [[UIActivityViewController alloc] initWithActivityItems:items applicationActivities:nil];
UIViewController *ctrl = RCTSharedApplication().delegate.window.rootViewController;
UIActivityViewController *shareController = [[UIActivityViewController alloc] initWithActivityItems:items applicationActivities:nil];
UIViewController *controller = RCTSharedApplication().delegate.window.rootViewController;
#if __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_8_0
if (![UIActivityViewController instancesRespondToSelector:@selector(setCompletionWithItemsHandler:)]) {
// Legacy iOS 7 implementation
share.completionHandler = ^(NSString *activityType, BOOL completed) {
shareController.completionHandler = ^(NSString *activityType, BOOL completed) {
successCallback(@[@(completed), RCTNullIfNil(activityType)]);
};
} else
@@ -113,45 +181,34 @@ RCT_EXPORT_METHOD(showShareActionSheetWithOptions:(NSDictionary *)options
{
// iOS 8 version
share.completionWithItemsHandler = ^(NSString *activityType, BOOL completed, __unused NSArray *returnedItems, NSError *activityError) {
shareController.completionWithItemsHandler = ^(NSString *activityType, BOOL completed, __unused NSArray *returnedItems, NSError *activityError) {
if (activityError) {
failureCallback(@[RCTNullIfNil(activityError.localizedDescription)]);
failureCallback(activityError);
} else {
successCallback(@[@(completed), RCTNullIfNil(activityType)]);
}
};
}
/*
* 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.
*/
if ([share respondsToSelector:@selector(popoverPresentationController)]) {
share.popoverPresentationController.sourceView = ctrl.view;
shareController.modalPresentationStyle = UIModalPresentationPopover;
NSNumber *anchorViewTag = [RCTConvert NSNumber:options[@"anchor"]];
if (anchorViewTag) {
UIView *anchorView = [self.bridge.uiManager viewForReactTag:anchorViewTag];
share.popoverPresentationController.sourceRect = [anchorView convertRect:anchorView.bounds toView:ctrl.view];
} else {
CGRect sourceRect = CGRectMake(ctrl.view.center.x, ctrl.view.center.y, 1, 1);
share.popoverPresentationController.sourceRect = sourceRect;
share.popoverPresentationController.permittedArrowDirections = 0;
if (!anchorViewTag) {
shareController.popoverPresentationController.permittedArrowDirections = 0;
}
shareController.popoverPresentationController.sourceView = controller.view;
shareController.popoverPresentationController.sourceRect = [self sourceRectInView:controller.view anchorViewTag:anchorViewTag];
}
[ctrl presentViewController:share animated:YES completion:nil];
[controller presentViewController:shareController animated:YES completion:nil];
}
#pragma mark UIActionSheetDelegate Methods
- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex
{
NSString *key = RCTKeyForInstance(actionSheet);
RCTResponseSenderBlock callback = _callbacks[key];
RCTResponseSenderBlock callback = [_callbacks objectForKey:actionSheet];
if (callback) {
callback(@[@(buttonIndex)]);
[_callbacks removeObjectForKey:key];
[_callbacks removeObjectForKey:actionSheet];
} else {
RCTLogWarn(@"No callback registered for action sheet: %@", actionSheet.title);
}
@@ -159,11 +216,4 @@ RCT_EXPORT_METHOD(showShareActionSheetWithOptions:(NSDictionary *)options
[RCTSharedApplication().delegate.window makeKeyWindow];
}
#pragma mark Private
static NSString *RCTKeyForInstance(id instance)
{
return [NSString stringWithFormat:@"%p", instance];
}
@end
@@ -40,11 +40,16 @@ class Animated {
__getChildren(): Array<Animated> { return []; }
}
type AnimationConfig = {
isInteraction?: bool;
};
// Important note: start() and stop() will only be called at most once.
// Once an animation has been stopped or finished its course, it will
// not be reused.
class Animation {
__active: bool;
__isInteraction: bool;
__onEnd: ?EndCallback;
start(
fromValue: number,
@@ -128,14 +133,14 @@ function _flush(rootNode: AnimatedValue): void {
animatedStyles.forEach(animatedStyle => animatedStyle.update());
}
type TimingAnimationConfig = {
type TimingAnimationConfig = AnimationConfig & {
toValue: number | AnimatedValue | {x: number, y: number} | AnimatedValueXY;
easing?: (value: number) => number;
duration?: number;
delay?: number;
};
type TimingAnimationConfigSingle = {
type TimingAnimationConfigSingle = AnimationConfig & {
toValue: number | AnimatedValue;
easing?: (value: number) => number;
duration?: number;
@@ -163,6 +168,7 @@ class TimingAnimation extends Animation {
this._easing = config.easing || easeInOut;
this._duration = config.duration !== undefined ? config.duration : 500;
this._delay = config.delay || 0;
this.__isInteraction = config.isInteraction !== undefined ? config.isInteraction : true;
}
start(
@@ -223,12 +229,12 @@ class TimingAnimation extends Animation {
}
}
type DecayAnimationConfig = {
type DecayAnimationConfig = AnimationConfig & {
velocity: number | {x: number, y: number};
deceleration?: number;
};
type DecayAnimationConfigSingle = {
type DecayAnimationConfigSingle = AnimationConfig & {
velocity: number;
deceleration?: number;
};
@@ -248,6 +254,7 @@ class DecayAnimation extends Animation {
super();
this._deceleration = config.deceleration || 0.998;
this._velocity = config.velocity;
this.__isInteraction = config.isInteraction !== undefined ? config.isInteraction : true;
}
start(
@@ -291,7 +298,7 @@ class DecayAnimation extends Animation {
}
}
type SpringAnimationConfig = {
type SpringAnimationConfig = AnimationConfig & {
toValue: number | AnimatedValue | {x: number, y: number} | AnimatedValueXY;
overshootClamping?: bool;
restDisplacementThreshold?: number;
@@ -303,7 +310,7 @@ type SpringAnimationConfig = {
friction?: number;
};
type SpringAnimationConfigSingle = {
type SpringAnimationConfigSingle = AnimationConfig & {
toValue: number | AnimatedValue;
overshootClamping?: bool;
restDisplacementThreshold?: number;
@@ -349,6 +356,7 @@ class SpringAnimation extends Animation {
this._initialVelocity = config.velocity;
this._lastVelocity = withDefault(config.velocity, 0);
this._toValue = config.toValue;
this.__isInteraction = config.isInteraction !== undefined ? config.isInteraction : true;
var springConfig;
if (config.bounciness !== undefined || config.speed !== undefined) {
@@ -611,7 +619,10 @@ class AnimatedValue extends AnimatedWithChildren {
* class.
*/
animate(animation: Animation, callback: ?EndCallback): void {
var handle = InteractionManager.createInteractionHandle();
var handle = null;
if (animation.__isInteraction) {
handle = InteractionManager.createInteractionHandle();
}
var previousAnimation = this._animation;
this._animation && this._animation.stop();
this._animation = animation;
@@ -622,7 +633,9 @@ class AnimatedValue extends AnimatedWithChildren {
},
(result) => {
this._animation = null;
InteractionManager.clearInteractionHandle(handle);
if (handle !== null) {
InteractionManager.clearInteractionHandle(handle);
}
callback && callback(result);
},
previousAnimation,
@@ -18,6 +18,7 @@ jest
var Animated = require('Animated');
describe('Animated', () => {
it('works end to end', () => {
var anim = new Animated.Value(0);
@@ -352,6 +353,54 @@ describe('Animated Events', () => {
});
});
describe('Animated Interactions', () => {
/*eslint-disable no-shadow*/
var Animated;
/*eslint-enable*/
var InteractionManager;
beforeEach(() => {
jest.mock('InteractionManager');
Animated = require('Animated');
InteractionManager = require('InteractionManager');
});
afterEach(()=> {
jest.dontMock('InteractionManager');
});
it('registers an interaction by default', () => {
InteractionManager.createInteractionHandle.mockReturnValue(777);
var value = new Animated.Value(0);
var callback = jest.genMockFunction();
Animated.timing(value, {
toValue: 100,
duration: 100,
}).start(callback);
jest.runAllTimers();
expect(InteractionManager.createInteractionHandle).toBeCalled();
expect(InteractionManager.clearInteractionHandle).toBeCalledWith(777);
expect(callback).toBeCalledWith({finished: true});
});
it('does not register an interaction when specified', () => {
var value = new Animated.Value(0);
var callback = jest.genMockFunction();
Animated.timing(value, {
toValue: 100,
duration: 100,
isInteraction: false,
}).start(callback);
jest.runAllTimers();
expect(InteractionManager.createInteractionHandle).not.toBeCalled();
expect(InteractionManager.clearInteractionHandle).not.toBeCalled();
expect(callback).toBeCalledWith({finished: true});
});
});
describe('Animated Tracking', () => {
it('should track values', () => {
var value1 = new Animated.Value(0);
@@ -15,11 +15,6 @@
@interface RCTBridge (RCTAssetsLibraryImageLoader)
/**
* The shared Assets Library image loader
*/
@property (nonatomic, readonly) RCTAssetsLibraryImageLoader *assetsLibraryImageLoader;
/**
* The shared asset library instance.
*/
@@ -41,10 +41,15 @@ RCT_EXPORT_MODULE()
- (BOOL)canLoadImageURL:(NSURL *)requestURL
{
return [requestURL.scheme.lowercaseString isEqualToString:@"assets-library"];
return [requestURL.scheme caseInsensitiveCompare:@"assets-library"] == NSOrderedSame;
}
- (RCTImageLoaderCancellationBlock)loadImageForURL:(NSURL *)imageURL size:(CGSize)size scale:(CGFloat)scale resizeMode:(UIViewContentMode)resizeMode progressHandler:(RCTImageLoaderProgressBlock)progressHandler completionHandler:(RCTImageLoaderCompletionBlock)completionHandler
- (RCTImageLoaderCancellationBlock)loadImageForURL:(NSURL *)imageURL
size:(CGSize)size
scale:(CGFloat)scale
resizeMode:(UIViewContentMode)resizeMode
progressHandler:(RCTImageLoaderProgressBlock)progressHandler
completionHandler:(RCTImageLoaderCompletionBlock)completionHandler
{
__block volatile uint32_t cancelled = 0;
@@ -69,7 +74,8 @@ RCT_EXPORT_MODULE()
BOOL useMaximumSize = CGSizeEqualToSize(size, CGSizeZero);
ALAssetRepresentation *representation = [asset defaultRepresentation];
#if RCT_DEV
#if RCT_DEV
CGSize sizeBeingLoaded = size;
if (useMaximumSize) {
CGSize pointSize = representation.dimensions;
@@ -78,7 +84,7 @@ RCT_EXPORT_MODULE()
CGSize screenSize;
if ([[[UIDevice currentDevice] systemVersion] compare:@"8.0" options:NSNumericSearch] == NSOrderedDescending) {
screenSize = UIScreen.mainScreen.nativeBounds.size;
screenSize = [UIScreen mainScreen].nativeBounds.size;
} else {
CGSize mainScreenSize = [UIScreen mainScreen].bounds.size;
CGFloat mainScreenScale = [[UIScreen mainScreen] scale];
@@ -87,9 +93,11 @@ RCT_EXPORT_MODULE()
CGFloat maximumPixelDimension = fmax(screenSize.width, screenSize.height);
if (sizeBeingLoaded.width > maximumPixelDimension || sizeBeingLoaded.height > maximumPixelDimension) {
RCTLogInfo(@"[PERF ASSETS] Loading %@ at size %@, which is larger than screen size %@", representation.filename, NSStringFromCGSize(sizeBeingLoaded), NSStringFromCGSize(screenSize));
RCTLogInfo(@"[PERF ASSETS] Loading %@ at size %@, which is larger than screen size %@",
representation.filename, NSStringFromCGSize(sizeBeingLoaded), NSStringFromCGSize(screenSize));
}
#endif
#endif
UIImage *image;
NSError *error = nil;
@@ -106,8 +114,7 @@ RCT_EXPORT_MODULE()
});
} else {
NSString *errorText = [NSString stringWithFormat:@"Failed to load asset at URL %@ with no error message.", imageURL];
NSError *error = RCTErrorWithMessage(errorText);
completionHandler(error, nil);
completionHandler(RCTErrorWithMessage(errorText), nil);
}
} failureBlock:^(NSError *loadError) {
if (cancelled) {
@@ -115,8 +122,7 @@ RCT_EXPORT_MODULE()
}
NSString *errorText = [NSString stringWithFormat:@"Failed to load asset at URL %@.\niOS Error: %@", imageURL, loadError];
NSError *error = RCTErrorWithMessage(errorText);
completionHandler(error, nil);
completionHandler(RCTErrorWithMessage(errorText), nil);
}];
return ^{
@@ -128,14 +134,9 @@ RCT_EXPORT_MODULE()
@implementation RCTBridge (RCTAssetsLibraryImageLoader)
- (RCTAssetsLibraryImageLoader *)assetsLibraryImageLoader
{
return self.modules[RCTBridgeModuleNameForClass([RCTAssetsLibraryImageLoader class])];
}
- (ALAssetsLibrary *)assetsLibrary
{
return [self.assetsLibraryImageLoader assetsLibrary];
return [self.modules[RCTBridgeModuleNameForClass([RCTAssetsLibraryImageLoader class])] assetsLibrary];
}
@end
@@ -154,7 +155,11 @@ static dispatch_queue_t RCTAssetsLibraryImageLoaderQueue(void)
// Why use a custom scaling method? Greater efficiency, reduced memory overhead:
// http://www.mindsea.com/2012/12/downscaling-huge-alassets-without-fear-of-sigkill
static UIImage *RCTScaledImageForAsset(ALAssetRepresentation *representation, CGSize size, CGFloat scale, UIViewContentMode resizeMode, NSError **error)
static UIImage *RCTScaledImageForAsset(ALAssetRepresentation *representation,
CGSize size,
CGFloat scale,
UIViewContentMode resizeMode,
NSError **error)
{
NSUInteger length = (NSUInteger)representation.size;
NSMutableData *data = [NSMutableData dataWithLength:length];
+11 -8
View File
@@ -35,14 +35,17 @@ RCT_EXPORT_METHOD(saveImageWithTag:(NSString *)imageTag
errorCallback(loadError);
return;
}
[_bridge.assetsLibrary writeImageToSavedPhotosAlbum:loadedImage.CGImage metadata:nil completionBlock:^(NSURL *assetURL, NSError *saveError) {
if (saveError) {
RCTLogWarn(@"Error saving cropped image: %@", saveError);
errorCallback(saveError);
} else {
successCallback(@[assetURL.absoluteString]);
}
}];
// It's unclear if writeImageToSavedPhotosAlbum is thread-safe
dispatch_async(dispatch_get_main_queue(), ^{
[_bridge.assetsLibrary writeImageToSavedPhotosAlbum:loadedImage.CGImage metadata:nil completionBlock:^(NSURL *assetURL, NSError *saveError) {
if (saveError) {
RCTLogWarn(@"Error saving cropped image: %@", saveError);
errorCallback(saveError);
} else {
successCallback(@[assetURL.absoluteString]);
}
}];
});
}];
}
@@ -24,35 +24,44 @@ RCT_EXPORT_MODULE()
- (BOOL)canLoadImageURL:(NSURL *)requestURL
{
return [requestURL.scheme.lowercaseString isEqualToString:@"ph"];
return [requestURL.scheme caseInsensitiveCompare:@"ph"] == NSOrderedSame;
}
- (RCTImageLoaderCancellationBlock)loadImageForURL:(NSURL *)imageURL size:(CGSize)size scale:(CGFloat)scale resizeMode:(UIViewContentMode)resizeMode progressHandler:(RCTImageLoaderProgressBlock)progressHandler completionHandler:(RCTImageLoaderCompletionBlock)completionHandler
- (RCTImageLoaderCancellationBlock)loadImageForURL:(NSURL *)imageURL
size:(CGSize)size
scale:(CGFloat)scale
resizeMode:(UIViewContentMode)resizeMode
progressHandler:(RCTImageLoaderProgressBlock)progressHandler
completionHandler:(RCTImageLoaderCompletionBlock)completionHandler
{
// Using PhotoKit for iOS 8+
// The 'ph://' prefix is used by FBMediaKit to differentiate between
// assets-library. It is prepended to the local ID so that it is in the
// form of an, NSURL which is what assets-library uses.
NSString *phAssetID = [imageURL.absoluteString substringFromIndex:[@"ph://" length]];
NSString *phAssetID = [imageURL.absoluteString substringFromIndex:@"ph://".length];
PHFetchResult *results = [PHAsset fetchAssetsWithLocalIdentifiers:@[phAssetID] options:nil];
if (results.count == 0) {
NSString *errorText = [NSString stringWithFormat:@"Failed to fetch PHAsset with local identifier %@ with no error message.", phAssetID];
NSError *error = RCTErrorWithMessage(errorText);
completionHandler(error, nil);
completionHandler(RCTErrorWithMessage(errorText), nil);
return ^{};
}
PHAsset *asset = [results firstObject];
PHImageRequestOptions *imageOptions = [PHImageRequestOptions new];
imageOptions.progressHandler = ^(double progress, NSError *error, BOOL *stop, NSDictionary *info) {
static const double multiplier = 1e6;
progressHandler(progress * multiplier, multiplier);
};
if (progressHandler) {
imageOptions.progressHandler = ^(double progress, NSError *error, BOOL *stop, NSDictionary *info) {
static const double multiplier = 1e6;
progressHandler(progress * multiplier, multiplier);
};
}
// Note: PhotoKit defaults to a deliveryMode of PHImageRequestOptionsDeliveryModeOpportunistic
// which means it may call back multiple times - we probably don't want that
imageOptions.deliveryMode = PHImageRequestOptionsDeliveryModeHighQualityFormat;
BOOL useMaximumSize = CGSizeEqualToSize(size, CGSizeZero);
CGSize targetSize;
if (useMaximumSize) {
targetSize = PHImageManagerMaximumSize;
imageOptions.resizeMode = PHImageRequestOptionsResizeModeNone;
@@ -66,7 +75,12 @@ RCT_EXPORT_MODULE()
contentMode = PHImageContentModeAspectFit;
}
PHImageRequestID requestID = [[PHImageManager defaultManager] requestImageForAsset:asset targetSize:targetSize contentMode:contentMode options:imageOptions resultHandler:^(UIImage *result, NSDictionary *info) {
PHImageRequestID requestID =
[[PHImageManager defaultManager] requestImageForAsset:asset
targetSize:targetSize
contentMode:contentMode
options:imageOptions
resultHandler:^(UIImage *result, NSDictionary *info) {
if (result) {
completionHandler(nil, result);
} else {
@@ -74,8 +74,8 @@ var ActivityIndicatorIOS = React.createClass({
return (
<View
onLayout={onLayout}
style={[styles.container, sizeStyle, style]}>
<RCTActivityIndicatorView {...props} />
style={[styles.container, style]}>
<RCTActivityIndicatorView {...props} style={sizeStyle} />
</View>
);
}
@@ -0,0 +1,105 @@
/**
* Copyright 2004-present Facebook. All Rights Reserved.
*
* @providesModule RecyclerViewBackedScrollView
*/
'use strict';
var NativeMethodsMixin = require('NativeMethodsMixin');
var React = require('React');
var ScrollResponder = require('ScrollResponder');
var ScrollView = require('ScrollView');
var requireNativeComponent = require('requireNativeComponent');
var INNERVIEW = 'InnerView';
/**
* Wrapper around android native recycler view.
*
* It simply renders rows passed as children in a separate recycler view cells
* similarily to how `ScrollView` is doing it. Thanks to the fact that it uses
* native `RecyclerView` though, rows that are out of sight are going to be
* automatically detached (similarily on how this would work with
* `removeClippedSubviews = true` on a `ScrollView.js`).
*
* CAUTION: This is an experimental component and should only be used together
* with javascript implementation of list view (see ListView.js). In order to
* use it pass this component as `renderScrollComponent` to the list view. For
* now only horizontal scrolling is supported.
*
* Example:
*
* ```
* getInitialState: function() {
* var ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
* return {
* dataSource: ds.cloneWithRows(['row 1', 'row 2']),
* };
* },
*
* render: function() {
* return (
* <ListView
* dataSource={this.state.dataSource}
* renderRow={rowData => <Text>{rowData}</Text>}
* renderScrollComponent={props => <RecyclerViewBackedScrollView {...props} />}
* />
* );
* },
* ```
*/
var RecyclerViewBackedScrollView = React.createClass({
propTypes: {
...ScrollView.propTypes,
},
mixins: [ScrollResponder.Mixin],
getInitialState: function() {
return this.scrollResponderMixinGetInitialState();
},
getScrollResponder: function() {
return this;
},
getInnerViewNode: function(): any {
return React.findNodeHandle(this.refs[INNERVIEW]);
},
setNativeProps: function(props: Object) {
this.refs[INNERVIEW].setNativeProps(props);
},
render: function() {
var props = {
...this.props,
onTouchStart: this.scrollResponderHandleTouchStart,
onTouchMove: this.scrollResponderHandleTouchMove,
onTouchEnd: this.scrollResponderHandleTouchEnd,
onScrollBeginDrag: this.scrollResponderHandleScrollBeginDrag,
onScrollEndDrag: this.scrollResponderHandleScrollEndDrag,
onMomentumScrollBegin: this.scrollResponderHandleMomentumScrollBegin,
onMomentumScrollEnd: this.scrollResponderHandleMomentumScrollEnd,
onStartShouldSetResponder: this.scrollResponderHandleStartShouldSetResponder,
onStartShouldSetResponderCapture: this.scrollResponderHandleStartShouldSetResponderCapture,
onScrollShouldSetResponder: this.scrollResponderHandleScrollShouldSetResponder,
onResponderGrant: this.scrollResponderHandleResponderGrant,
onResponderRelease: this.scrollResponderHandleResponderRelease,
onResponderReject: this.scrollResponderHandleResponderReject,
onScroll: this.scrollResponderHandleScroll,
style: ([{flex: 1}, this.props.style]: ?Array<any>),
ref: INNERVIEW,
};
return (
<NativeAndroidRecyclerView {...props}/>
);
},
});
var NativeAndroidRecyclerView = requireNativeComponent('AndroidRecyclerViewBackedScrollView', null);
module.exports = RecyclerViewBackedScrollView;
@@ -0,0 +1,8 @@
/**
* Copyright 2004-present Facebook. All Rights Reserved.
*
* @providesModule RecyclerViewBackedScrollView
*/
'use strict';
module.exports = require('UnimplementedView');
+1 -1
View File
@@ -108,7 +108,7 @@ var TextInput = React.createClass({
autoFocus: PropTypes.bool,
/**
* Set the position of the cursor from where editing will begin.
* @platorm android
* @platform android
*/
textAlign: PropTypes.oneOf([
'start',
@@ -103,6 +103,10 @@ var ToolbarAndroid = React.createClass({
* Callback called when the icon is selected.
*/
onIconClicked: ReactPropTypes.func,
/**
* Sets the overflow icon.
*/
overflowIcon: optionalImageSource,
/**
* Sets the toolbar subtitle.
*/
@@ -135,6 +139,9 @@ var ToolbarAndroid = React.createClass({
if (this.props.navIcon) {
nativeProps.navIcon = resolveAssetSource(this.props.navIcon);
}
if (this.props.overflowIcon) {
nativeProps.overflowIcon = resolveAssetSource(this.props.overflowIcon);
}
if (this.props.actions) {
nativeProps.actions = [];
for (var i = 0; i < this.props.actions.length; i++) {
@@ -169,6 +176,7 @@ var toolbarAttributes = {
actions: true,
logo: true,
navIcon: true,
overflowIcon: true,
subtitle: true,
subtitleColor: true,
title: true,
+12 -10
View File
@@ -420,16 +420,18 @@ var TouchableMixin = {
var positionOnActivate = this.state.touchable.positionOnActivate;
var dimensionsOnActivate = this.state.touchable.dimensionsOnActivate;
var pressRectOffset = this.touchableGetPressRectOffset ?
this.touchableGetPressRectOffset() : null;
var pressExpandLeft =
pressRectOffset.left != null ? pressRectOffset.left : PRESS_EXPAND_PX;
var pressExpandTop =
pressRectOffset.top != null ? pressRectOffset.top : PRESS_EXPAND_PX;
var pressExpandRight =
pressRectOffset.right != null ? pressRectOffset.right : PRESS_EXPAND_PX;
var pressExpandBottom =
pressRectOffset.bottom != null ? pressRectOffset.bottom : PRESS_EXPAND_PX;
this.touchableGetPressRectOffset() : {
left: PRESS_EXPAND_PX,
right: PRESS_EXPAND_PX,
top: PRESS_EXPAND_PX,
bottom: PRESS_EXPAND_PX
};
var pressExpandLeft = pressRectOffset.left;
var pressExpandTop = pressRectOffset.top;
var pressExpandRight = pressRectOffset.right;
var pressExpandBottom = pressRectOffset.bottom;
var touch = TouchEventUtils.extractSingleTouch(e.nativeEvent);
var pageX = touch && touch.pageX;
var pageY = touch && touch.pageY;
@@ -25,7 +25,7 @@ var processColor = require('processColor');
var rippleBackgroundPropType = createStrictShapeTypeChecker({
type: React.PropTypes.oneOf(['RippleAndroid']),
color: PropTypes.string,
color: PropTypes.number,
borderless: PropTypes.bool,
});
@@ -387,6 +387,9 @@ var ListView = React.createClass({
if (!props.scrollEventThrottle) {
props.scrollEventThrottle = DEFAULT_SCROLL_CALLBACK_THROTTLE;
}
if (props.removeClippedSubviews === undefined) {
props.removeClippedSubviews = true;
}
Object.assign(props, {
onScroll: this._onScroll,
stickyHeaderIndices: sectionHeaderIndices,
@@ -26,40 +26,71 @@
*/
'use strict';
var NavigationEvent = require('NavigationEvent');
var NavigationEventEmitter = require('NavigationEventEmitter');
var NavigationTreeNode = require('NavigationTreeNode');
var emptyFunction = require('emptyFunction');
var invariant = require('invariant');
import type * as NavigationEvent from 'NavigationEvent';
import type * as EventSubscription from 'EventSubscription';
var {
AT_TARGET,
BUBBLING_PHASE,
CAPTURING_PHASE,
} = NavigationEvent;
/**
* Class that contains the info and methods for app navigation.
*/
class NavigationContext {
_eventEmitter: ?NavigationEventEmitter;
__node: NavigationTreeNode;
_bubbleEventEmitter: ?NavigationEventEmitter;
_captureEventEmitter: ?NavigationEventEmitter;
_currentRoute: any;
_emitCounter: number;
_emitQueue: Array<any>;
constructor() {
this._eventEmitter = new NavigationEventEmitter(this);
this._bubbleEventEmitter = new NavigationEventEmitter(this);
this._captureEventEmitter = new NavigationEventEmitter(this);
this._currentRoute = null;
// Sets the protected property `__node`.
this.__node = new NavigationTreeNode(this);
this._emitCounter = 0;
this._emitQueue = [];
this.addListener('willfocus', this._onFocus, this);
this.addListener('didfocus', this._onFocus, this);
}
// TODO: @flow does not like this getter. Will add @flow check back once
// getter/setter is supported.
/* $FlowFixMe - get/set properties not yet supported */
get parent(): ?NavigationContext {
var parent = this.__node.getParent();
return parent ? parent.getValue() : null;
}
/* $FlowFixMe - get/set properties not yet supported */
get currentRoute(): any {
return this._currentRoute;
}
appendChild(childContext: NavigationContext): void {
this.__node.appendChild(childContext.__node);
}
addListener(
eventType: string,
listener: Function,
context: ?Object
context: ?Object,
useCapture: ?boolean
): EventSubscription {
var emitter = this._eventEmitter;
var emitter = useCapture ?
this._captureEventEmitter :
this._bubbleEventEmitter;
if (emitter) {
return emitter.addListener(eventType, listener, context);
} else {
@@ -68,19 +99,115 @@ class NavigationContext {
}
emit(eventType: String, data: any, didEmitCallback: ?Function): void {
var emitter = this._eventEmitter;
if (emitter) {
emitter.emit(eventType, data, didEmitCallback);
if (this._emitCounter > 0) {
// An event cycle that was previously created hasn't finished yet.
// Put this event cycle into the queue and will finish them later.
var args: any = Array.prototype.slice.call(arguments);
this._emitQueue.push(args);
return;
}
this._emitCounter++;
var targets = [this];
var parentTarget = this.parent;
while (parentTarget) {
targets.unshift(parentTarget);
parentTarget = parentTarget.parent;
}
var propagationStopped = false;
var defaultPrevented = false;
var callback = (event) => {
propagationStopped = propagationStopped || event.isPropagationStopped();
defaultPrevented = defaultPrevented || event.defaultPrevented;
};
// capture phase
targets.some((currentTarget) => {
if (propagationStopped) {
return true;
}
var extraInfo = {
defaultPrevented,
eventPhase: CAPTURING_PHASE,
propagationStopped,
target: this,
};
currentTarget.__emit(eventType, data, callback, extraInfo);
}, this);
// bubble phase
targets.reverse().some((currentTarget) => {
if (propagationStopped) {
return true;
}
var extraInfo = {
defaultPrevented,
eventPhase: BUBBLING_PHASE,
propagationStopped,
target: this,
};
currentTarget.__emit(eventType, data, callback, extraInfo);
}, this);
if (didEmitCallback) {
var event = NavigationEvent.pool(eventType, this, data);
propagationStopped && event.stopPropagation();
defaultPrevented && event.preventDefault();
didEmitCallback.call(this, event);
event.dispose();
}
this._emitCounter--;
while (this._emitQueue.length) {
var args: any = this._emitQueue.shift();
this.emit.apply(this, args);
}
}
dispose(): void {
var emitter = this._eventEmitter;
// clean up everything.
this._bubbleEventEmitter && this._bubbleEventEmitter.removeAllListeners();
this._captureEventEmitter && this._captureEventEmitter.removeAllListeners();
this._bubbleEventEmitter = null;
this._captureEventEmitter = null;
this._currentRoute = null;
}
// This method `__method` is protected.
__emit(
eventType: String,
data: any,
didEmitCallback: ?Function,
extraInfo: Object,
): void {
var emitter;
switch (extraInfo.eventPhase) {
case CAPTURING_PHASE: // phase = 1
emitter = this._captureEventEmitter;
break;
case BUBBLING_PHASE: // phase = 3
emitter = this._bubbleEventEmitter;
break;
default:
invariant(false, 'invalid event phase %s', extraInfo.eventPhase);
}
if (extraInfo.target === this) {
// phase = 2
extraInfo.eventPhase = AT_TARGET;
}
if (emitter) {
// clean up everything.
emitter.removeAllListeners();
this._eventEmitter = null;
this._currentRoute = null;
emitter.emit(
eventType,
data,
didEmitCallback,
extraInfo
);
}
}
@@ -36,13 +36,13 @@ class NavigationEventPool {
this._list = [];
}
get(type: string, target: Object, data: any): NavigationEvent {
get(type: string, currentTarget: Object, data: any): NavigationEvent {
var event;
if (this._list.length > 0) {
event = this._list.pop();
event.constructor.call(event, type, target, data);
event.constructor.call(event, type, currentTarget, data);
} else {
event = new NavigationEvent(type, target, data);
event = new NavigationEvent(type, currentTarget, data);
}
return event;
}
@@ -54,21 +54,57 @@ class NavigationEventPool {
var _navigationEventPool = new NavigationEventPool();
/**
* The NavigationEvent interface represents any event of the navigation.
* It contains common properties and methods to any event.
*
* == Important Properties ==
*
* - target:
* A reference to the navigation context that dispatched the event. It is
* different from event.currentTarget when the event handler is called during
* the bubbling or capturing phase of the event.
*
* - currentTarget:
* Identifies the current target for the event, as the event traverses the
* navigation context tree. It always refers to the navigation context the
* event handler has been attached to as opposed to event.target which
* identifies the navigation context on which the event occurred.
*
* - eventPhase:
* Returns an integer value which specifies the current evaluation phase of
* the event flow; possible values are listed in NavigationEvent phase
* constants below.
*/
class NavigationEvent {
static AT_TARGET: number;
static BUBBLING_PHASE: number;
static CAPTURING_PHASE: number;
static NONE: number;
_currentTarget: ?Object;
_data: any;
_defaultPrevented: boolean;
_propagationStopped: boolean;
_disposed: boolean;
_target: ?Object;
_propagationStopped: boolean;
_type: ?string;
static pool(type: string, target: Object, data: any): NavigationEvent {
return _navigationEventPool.get(type, target, data);
target: ?Object;
// Returns an integer value which specifies the current evaluation phase of
// the event flow.
eventPhase: number;
static pool(type: string, currentTarget: Object, data: any): NavigationEvent {
return _navigationEventPool.get(type, currentTarget, data);
}
constructor(type: string, target: Object, data: any) {
constructor(type: string, currentTarget: Object, data: any) {
this.target = currentTarget;
this.eventPhase = NavigationEvent.NONE;
this._type = type;
this._target = target;
this._currentTarget = currentTarget;
this._data = data;
this._defaultPrevented = false;
this._disposed = false;
@@ -81,8 +117,8 @@ class NavigationEvent {
}
/* $FlowFixMe - get/set properties not yet supported */
get target(): Object {
return this._target;
get currentTarget(): Object {
return this._currentTarget;
}
/* $FlowFixMe - get/set properties not yet supported */
@@ -122,8 +158,10 @@ class NavigationEvent {
this._disposed = true;
// Clean up.
this.target = null;
this.eventPhase = NavigationEvent.NONE;
this._type = null;
this._target = null;
this._currentTarget = null;
this._data = null;
this._defaultPrevented = false;
@@ -132,4 +170,26 @@ class NavigationEvent {
}
}
/**
* Event phase constants.
* These values describe which phase the event flow is currently being
* evaluated.
*/
// No event is being processed at this time.
NavigationEvent.NONE = 0;
// The event is being propagated through the currentTarget's ancestor objects.
NavigationEvent.CAPTURING_PHASE = 1;
// The event has arrived at the event's currentTarget. Event listeners registered for
// this phase are called at this time.
NavigationEvent.AT_TARGET = 2;
// The event is propagating back up through the currentTarget's ancestors in reverse
// order, starting with the parent. This is known as bubbling, and occurs only
// if event propagation isn't prevented. Event listeners registered for this
// phase are triggered during this process.
NavigationEvent.BUBBLING_PHASE = 3;
module.exports = NavigationEvent;
@@ -30,14 +30,15 @@
var EventEmitter = require('EventEmitter');
var NavigationEvent = require('NavigationEvent');
type EventParams = {
data: any;
didEmitCallback: ?Function;
eventType: string;
type ExtraInfo = {
defaultPrevented: ?boolean,
eventPhase: ?number,
propagationStopped: ?boolean,
target: ?Object,
};
class NavigationEventEmitter extends EventEmitter {
_emitQueue: Array<EventParams>;
_emitQueue: Array<any>;
_emitting: boolean;
_target: Object;
@@ -51,18 +52,38 @@ class NavigationEventEmitter extends EventEmitter {
emit(
eventType: string,
data: any,
didEmitCallback: ?Function
didEmitCallback: ?Function,
extraInfo: ?ExtraInfo
): void {
if (this._emitting) {
// An event cycle that was previously created hasn't finished yet.
// Put this event cycle into the queue and will finish them later.
this._emitQueue.push({eventType, data, didEmitCallback});
var args: any = Array.prototype.slice.call(arguments);
this._emitQueue.unshift(args);
return;
}
this._emitting = true;
var event = new NavigationEvent(eventType, this._target, data);
var event = NavigationEvent.pool(eventType, this._target, data);
if (extraInfo) {
if (extraInfo.target) {
event.target = extraInfo.target;
}
if (extraInfo.eventPhase) {
event.eventPhase = extraInfo.eventPhase;
}
if (extraInfo.defaultPrevented) {
event.preventDefault();
}
if (extraInfo.propagationStopped) {
event.stopPropagation();
}
}
// EventEmitter#emit only takes `eventType` as `String`. Casting `eventType`
// to `String` to make @flow happy.
@@ -76,8 +97,8 @@ class NavigationEventEmitter extends EventEmitter {
this._emitting = false;
while (this._emitQueue.length) {
var arg = this._emitQueue.shift();
this.emit(arg.eventType, arg.data, arg.didEmitCallback);
var args: any = this._emitQueue.shift();
this.emit.apply(this, args);
}
}
}
@@ -25,16 +25,11 @@
'use strict';
jest
.dontMock('EmitterSubscription')
.dontMock('EventSubscription')
.dontMock('EventEmitter')
.dontMock('EventSubscriptionVendor')
.dontMock('NavigationContext')
.dontMock('NavigationEvent')
.dontMock('NavigationEventEmitter')
.dontMock('invariant');
.autoMockOff()
.mock('ErrorUtils');
var NavigationContext = require('NavigationContext');
var NavigationEvent = require('NavigationEvent');
describe('NavigationContext', () => {
it('defaults `currentRoute` to null', () => {
@@ -47,6 +42,213 @@ describe('NavigationContext', () => {
context.emit('didfocus', {route: {name: 'a'}});
expect(context.currentRoute.name).toEqual('a');
});
it('has parent', () => {
var parent = new NavigationContext();
var child = new NavigationContext();
parent.appendChild(child);
expect(child.parent).toBe(parent);
});
it('captures event', () => {
var parent = new NavigationContext();
var child = new NavigationContext();
parent.appendChild(child);
var logs = [];
var listener = (event) => {
var {currentTarget, eventPhase, target, type} = event;
logs.push({
currentTarget,
eventPhase,
target,
type,
});
};
parent.addListener('yo', listener, null, true);
child.addListener('yo', listener, null, true);
child.emit('yo');
expect(logs).toEqual([
{
currentTarget: parent,
eventPhase: NavigationEvent.CAPTURING_PHASE,
target: child,
type: 'yo',
},
{
currentTarget: child,
eventPhase: NavigationEvent.AT_TARGET,
target: child,
type: 'yo',
}
]);
});
it('bubbles events', () => {
var parent = new NavigationContext();
var child = new NavigationContext();
parent.appendChild(child);
var logs = [];
var listener = (event) => {
var {currentTarget, eventPhase, target, type} = event;
logs.push({
currentTarget,
eventPhase,
target,
type,
});
};
parent.addListener('yo', listener);
child.addListener('yo', listener);
child.emit('yo');
expect(logs).toEqual([
{
currentTarget: child,
eventPhase: NavigationEvent.AT_TARGET,
target: child,
type: 'yo',
},
{
currentTarget: parent,
eventPhase: NavigationEvent.BUBBLING_PHASE,
target: child,
type: 'yo',
},
]);
});
it('stops event propagation at capture phase', () => {
var parent = new NavigationContext();
var child = new NavigationContext();
parent.appendChild(child);
var counter = 0;
parent.addListener('yo', event => event.stopPropagation(), null, true);
child.addListener('yo', event => counter++, null, true);
child.emit('yo');
expect(counter).toBe(0);
});
it('stops event propagation at bubbling phase', () => {
var parent = new NavigationContext();
var child = new NavigationContext();
parent.appendChild(child);
var counter = 0;
parent.addListener('yo', event => counter++);
child.addListener('yo', event => event.stopPropagation());
child.emit('yo');
expect(counter).toBe(0);
});
it('prevents event at capture phase', () => {
var parent = new NavigationContext();
var child = new NavigationContext();
parent.appendChild(child);
var val;
parent.addListener('yo', event => event.preventDefault(), null, true);
child.addListener('yo', event => val = event.defaultPrevented, null, true);
child.emit('yo');
expect(val).toBe(true);
});
it('prevents event at bubble phase', () => {
var parent = new NavigationContext();
var child = new NavigationContext();
parent.appendChild(child);
var val;
parent.addListener('yo', event => val = event.defaultPrevented);
child.addListener('yo', event => event.preventDefault());
child.emit('yo');
expect(val).toBe(true);
});
it('emits nested events in order at capture phase', () => {
var parent = new NavigationContext();
var child = new NavigationContext();
parent.appendChild(child);
var logs = [];
var listener = (event) => {
var {currentTarget, type} = event;
logs.push({
currentTarget,
type,
});
};
child.addListener('yo', event => {
// event `didyo` should be fired after the full propagation cycle of the
// `yo` event.
child.emit('didyo');
});
parent.addListener('yo', listener, null, true);
parent.addListener('didyo', listener, null, true);
child.addListener('yo', listener, null, true);
child.emit('yo');
expect(logs).toEqual([
{type: 'yo', currentTarget: parent},
{type: 'yo', currentTarget: child},
{type: 'didyo', currentTarget: parent},
]);
});
it('emits nested events in order at bubbling phase', () => {
var parent = new NavigationContext();
var child = new NavigationContext();
parent.appendChild(child);
var logs = [];
var listener = (event) => {
var {currentTarget, type} = event;
logs.push({
currentTarget,
type,
});
};
child.addListener('yo', event => {
// event `didyo` should be fired after the full propagation cycle of the
// `yo` event.
child.emit('didyo');
});
parent.addListener('yo', listener);
child.addListener('yo', listener);
parent.addListener('didyo', listener);
child.emit('yo');
expect(logs).toEqual([
{type: 'yo', currentTarget: child},
{type: 'yo', currentTarget: parent},
{type: 'didyo', currentTarget: parent},
]);
});
});
@@ -591,8 +591,11 @@ var Navigator = React.createClass({
_handleMoveShouldSetPanResponder: function(e, gestureState) {
var sceneConfig = this.state.sceneConfigStack[this.state.presentedIndex];
if (!sceneConfig) {
return false;
}
this._expectingGestureGrant = this._matchGestureAction(this._eligibleGestures, sceneConfig.gestures, gestureState);
return !! this._expectingGestureGrant;
return !!this._expectingGestureGrant;
},
_doesGestureOverswipe: function(gestureName) {
+10 -4
View File
@@ -284,8 +284,11 @@ RCT_EXPORT_METHOD(getCurrentPosition:(RCTLocationOptions)options
[_locationManager stopUpdatingLocation];
}
// Reset location accuracy
_locationManager.desiredAccuracy = RCT_DEFAULT_LOCATION_ACCURACY;
// Reset location accuracy if desiredAccuracy is changed.
// Otherwise update accuracy will force triggering didUpdateLocations, watchPosition would keeping receiving location updates, even there's no location changes.
if (ABS(_locationManager.desiredAccuracy - RCT_DEFAULT_LOCATION_ACCURACY) > 0.000001) {
_locationManager.desiredAccuracy = RCT_DEFAULT_LOCATION_ACCURACY;
}
}
- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error
@@ -318,8 +321,11 @@ RCT_EXPORT_METHOD(getCurrentPosition:(RCTLocationOptions)options
}
[_pendingRequests removeAllObjects];
// Reset location accuracy
_locationManager.desiredAccuracy = RCT_DEFAULT_LOCATION_ACCURACY;
// Reset location accuracy if desiredAccuracy is changed.
// Otherwise update accuracy will force triggering didUpdateLocations, watchPosition would keeping receiving location updates, even there's no location changes.
if (ABS(_locationManager.desiredAccuracy - RCT_DEFAULT_LOCATION_ACCURACY) > 0.000001) {
_locationManager.desiredAccuracy = RCT_DEFAULT_LOCATION_ACCURACY;
}
}
- (void)checkLocationConfig
-1
View File
@@ -16,7 +16,6 @@
module.exports = {
__packager_asset: true,
isStatic: true,
path: '/full/path/to/something.png',
uri: 'icon',
width: 100,
-6
View File
@@ -126,12 +126,6 @@ var Image = React.createClass({
}
if (source && source.uri) {
var isNetwork = source.uri.match(/^https?:/);
invariant(
!(isNetwork && source.isStatic),
'Static image URIs cannot start with "http": "' + source.uri + '"'
);
var {width, height} = source;
var style = flattenStyle([{width, height}, styles.base, this.props.style]);
-1
View File
@@ -13,5 +13,4 @@
type ImageSource = {
uri: string;
isStatic: boolean;
};
+1 -1
View File
@@ -9,6 +9,6 @@
#import "RCTImageLoader.h"
@interface RCTGIFImageDecoder : NSObject <RCTImageDecoder>
@interface RCTGIFImageDecoder : NSObject <RCTImageDataDecoder>
@end
@@ -17,7 +17,6 @@
143879381AAD32A300F088A5 /* RCTImageLoader.m in Sources */ = {isa = PBXBuildFile; fileRef = 143879371AAD32A300F088A5 /* RCTImageLoader.m */; };
35123E6B1B59C99D00EBAD80 /* RCTImageStoreManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 35123E6A1B59C99D00EBAD80 /* RCTImageStoreManager.m */; };
354631681B69857700AA0B86 /* RCTImageEditingManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 354631671B69857700AA0B86 /* RCTImageEditingManager.m */; };
58B5118F1A9E6BD600147676 /* RCTImageDownloader.m in Sources */ = {isa = PBXBuildFile; fileRef = 58B5118A1A9E6BD600147676 /* RCTImageDownloader.m */; };
/* End PBXBuildFile section */
/* Begin PBXCopyFilesBuildPhase section */
@@ -54,8 +53,6 @@
354631661B69857700AA0B86 /* RCTImageEditingManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTImageEditingManager.h; sourceTree = "<group>"; };
354631671B69857700AA0B86 /* RCTImageEditingManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTImageEditingManager.m; sourceTree = "<group>"; };
58B5115D1A9E6B3D00147676 /* libRCTImage.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRCTImage.a; sourceTree = BUILT_PRODUCTS_DIR; };
58B511891A9E6BD600147676 /* RCTImageDownloader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTImageDownloader.h; sourceTree = "<group>"; };
58B5118A1A9E6BD600147676 /* RCTImageDownloader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTImageDownloader.m; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -76,8 +73,6 @@
13EF7F7E1BC825B1003F47DD /* RCTXCAssetImageLoader.m */,
1304D5B01AA8C50D0002E2BE /* RCTGIFImageDecoder.h */,
1304D5B11AA8C50D0002E2BE /* RCTGIFImageDecoder.m */,
58B511891A9E6BD600147676 /* RCTImageDownloader.h */,
58B5118A1A9E6BD600147676 /* RCTImageDownloader.m */,
354631661B69857700AA0B86 /* RCTImageEditingManager.h */,
354631671B69857700AA0B86 /* RCTImageEditingManager.m */,
143879361AAD32A300F088A5 /* RCTImageLoader.h */,
@@ -166,7 +161,6 @@
files = (
13EF7F0C1BC42D4E003F47DD /* RCTVirtualImageManager.m in Sources */,
35123E6B1B59C99D00EBAD80 /* RCTImageStoreManager.m in Sources */,
58B5118F1A9E6BD600147676 /* RCTImageDownloader.m in Sources */,
1304D5AC1AA8C4A30002E2BE /* RCTImageViewManager.m in Sources */,
1304D5B21AA8C50D0002E2BE /* RCTGIFImageDecoder.m in Sources */,
143879381AAD32A300F088A5 /* RCTImageLoader.m in Sources */,
-196
View File
@@ -1,196 +0,0 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#import "RCTImageDownloader.h"
#import "RCTImageLoader.h"
#import "RCTImageUtils.h"
#import "RCTLog.h"
#import "RCTNetworking.h"
#import "RCTUtils.h"
@implementation RCTImageDownloader
{
NSURLCache *_cache;
dispatch_queue_t _processingQueue;
}
@synthesize bridge = _bridge;
RCT_EXPORT_MODULE()
- (instancetype)init
{
if ((self = [super init])) {
_cache = [[NSURLCache alloc] initWithMemoryCapacity:5 * 1024 * 1024 diskCapacity:200 * 1024 * 1024 diskPath:@"React/RCTImageDownloader"];
_processingQueue = dispatch_queue_create("com.facebook.React.DownloadProcessingQueue", DISPATCH_QUEUE_SERIAL);
}
return self;
}
- (BOOL)canLoadImageURL:(NSURL *)requestURL
{
static NSSet *schemes = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
schemes = [[NSSet alloc] initWithObjects:@"http", @"https", @"file", nil];
});
return [schemes containsObject:requestURL.scheme.lowercaseString];
}
/**
* Downloads a block of raw data and returns it. Note that the callback block
* will not be executed on the same thread you called the method from, nor on
* the main thread. Returns a token that can be used to cancel the download.
*/
- (RCTImageLoaderCancellationBlock)downloadDataForURL:(NSURL *)url
progressHandler:(RCTImageLoaderProgressBlock)progressBlock
completionHandler:(void (^)(NSError *error, NSData *data))completionBlock
{
if (![_bridge respondsToSelector:NSSelectorFromString(@"networking")]) {
RCTLogError(@"You need to import the RCTNetworking library in order to download remote images.");
return ^{};
}
__weak RCTImageDownloader *weakSelf = self;
RCTURLRequestCompletionBlock runBlocks = ^(NSURLResponse *response, NSData *data, NSError *error) {
if (!error && [response isKindOfClass:[NSHTTPURLResponse class]]) {
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
if (httpResponse.statusCode != 200) {
data = nil;
error = [[NSError alloc] initWithDomain:NSURLErrorDomain
code:httpResponse.statusCode
userInfo:nil];
}
}
dispatch_async(_processingQueue, ^{
completionBlock(error, data);
});
};
NSURLRequest *request = [NSURLRequest requestWithURL:url];
{
NSCachedURLResponse *cachedResponse = [_cache cachedResponseForRequest:request];
if (cachedResponse) {
runBlocks(cachedResponse.response, cachedResponse.data, nil);
return ^{};
}
}
RCTDownloadTask *task = [_bridge.networking downloadTaskWithRequest:request completionBlock:^(NSURLResponse *response, NSData *data, NSError *error) {
if (response && !error) {
RCTImageDownloader *strongSelf = weakSelf;
NSCachedURLResponse *cachedResponse = [[NSCachedURLResponse alloc] initWithResponse:response data:data userInfo:nil storagePolicy:NSURLCacheStorageAllowed];
[strongSelf->_cache storeCachedResponse:cachedResponse forRequest:request];
}
runBlocks(response, data, error);
}];
if (progressBlock) {
task.downloadProgressBlock = progressBlock;
}
return ^{ [task cancel]; };
}
- (RCTImageLoaderCancellationBlock)loadImageForURL:(NSURL *)imageURL
size:(CGSize)size
scale:(CGFloat)scale
resizeMode:(UIViewContentMode)resizeMode
progressHandler:(RCTImageLoaderProgressBlock)progressHandler
completionHandler:(RCTImageLoaderCompletionBlock)completionHandler
{
if ([imageURL.scheme.lowercaseString hasPrefix:@"http"]) {
__block RCTImageLoaderCancellationBlock decodeCancel = nil;
__weak RCTImageDownloader *weakSelf = self;
RCTImageLoaderCancellationBlock downloadCancel = [self downloadDataForURL:imageURL progressHandler:progressHandler completionHandler:^(NSError *error, NSData *imageData) {
if (error) {
completionHandler(error, nil);
} else {
decodeCancel = [weakSelf.bridge.imageLoader decodeImageData:imageData size:size scale:scale resizeMode:resizeMode completionBlock:completionHandler];
}
}];
return ^{
downloadCancel();
if (decodeCancel) {
decodeCancel();
}
};
} else if ([imageURL.scheme caseInsensitiveCompare:@"data"] == NSOrderedSame) {
__block BOOL cancelled = NO;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
if (cancelled) {
return;
}
// Normally -dataWithContentsOfURL: would be bad but this is a data URL.
NSData *data = [NSData dataWithContentsOfURL:imageURL];
UIImage *image = [UIImage imageWithData:data scale:scale];
if (image) {
if (progressHandler) {
progressHandler(1, 1);
}
if (completionHandler) {
completionHandler(nil, image);
}
} else {
if (completionHandler) {
NSString *message = [NSString stringWithFormat:@"Invalid image data for URL: %@", imageURL];
completionHandler(RCTErrorWithMessage(message), nil);
}
}
});
return ^{
cancelled = YES;
};
} else if ([imageURL.scheme isEqualToString:@"file"]) {
__block BOOL cancelled = NO;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
if (cancelled) {
return;
}
UIImage *image = [UIImage imageWithContentsOfFile:imageURL.resourceSpecifier];
if (image) {
if (progressHandler) {
progressHandler(1, 1);
}
if (completionHandler) {
completionHandler(nil, image);
}
} else {
if (completionHandler) {
NSString *message = [NSString stringWithFormat:@"Could not find image at path: %@", imageURL.absoluteString];
completionHandler(RCTErrorWithMessage(message), nil);
}
}
});
return ^{
cancelled = YES;
};
} else {
RCTLogError(@"Unexpected image schema %@", imageURL.scheme);
return ^{};
}
}
@end
@implementation RCTBridge (RCTImageDownloader)
- (RCTImageDownloader *)imageDownloader
{
return self.modules[RCTBridgeModuleNameForClass([RCTImageDownloader class])];
}
@end
+24 -14
View File
@@ -28,7 +28,7 @@ typedef void (^RCTImageLoaderCancellationBlock)(void);
/**
* Loads the specified image at the highest available resolution.
* Can be called from any thread, will always call callback on main thread.
* Can be called from any thread, will call back on an unspecified thread.
*/
- (RCTImageLoaderCancellationBlock)loadImageWithTag:(NSString *)imageTag
callback:(RCTImageLoaderCompletionBlock)callback;
@@ -46,7 +46,8 @@ typedef void (^RCTImageLoaderCancellationBlock)(void);
/**
* Finds an appropriate image decoder and passes the target size, scale and
* resizeMode for optimal image decoding.
* resizeMode for optimal image decoding. Can be called from any thread,
* will call callback on an unspecified thread.
*/
- (RCTImageLoaderCancellationBlock)decodeImageData:(NSData *)imageData
size:(CGSize)size
@@ -66,7 +67,7 @@ typedef void (^RCTImageLoaderCancellationBlock)(void);
@end
/**
* Provides the interface needed to register an image data loader. Image data
* Provides the interface needed to register an image loader. Image data
* loaders are also bridge modules, so should be registered using
* RCT_EXPORT_MODULE().
*/
@@ -85,18 +86,23 @@ typedef void (^RCTImageLoaderCancellationBlock)(void);
* has finished. The method should also return a cancellation block, if
* applicable.
*/
- (RCTImageLoaderCancellationBlock)loadImageForURL:(NSURL *)imageURL size:(CGSize)size scale:(CGFloat)scale resizeMode:(UIViewContentMode)resizeMode progressHandler:(RCTImageLoaderProgressBlock)progressHandler completionHandler:(RCTImageLoaderCompletionBlock)completionHandler;
- (RCTImageLoaderCancellationBlock)loadImageForURL:(NSURL *)imageURL
size:(CGSize)size
scale:(CGFloat)scale
resizeMode:(UIViewContentMode)resizeMode
progressHandler:(RCTImageLoaderProgressBlock)progressHandler
completionHandler:(RCTImageLoaderCompletionBlock)completionHandler;
@optional
/**
* If more than one RCTImageURLLoader responds YES to `-canLoadImageURL:`
* then `imageLoaderPriority` is used to determine which one to use. The handler
* then `loaderPriority` is used to determine which one to use. The loader
* with the highest priority will be selected. Default priority is zero. If
* two or more valid handlers have the same priority, the selection order is
* two or more valid loaders have the same priority, the selection order is
* undefined.
*/
- (float)imageLoaderPriority;
- (float)loaderPriority;
@end
@@ -104,7 +110,7 @@ typedef void (^RCTImageLoaderCancellationBlock)(void);
* Provides the interface needed to register an image decoder. Image decoders
* are also bridge modules, so should be registered using RCT_EXPORT_MODULE().
*/
@protocol RCTImageDecoder <RCTBridgeModule>
@protocol RCTImageDataDecoder <RCTBridgeModule>
/**
* Indicates whether this handler is capable of decoding the specified data.
@@ -118,17 +124,21 @@ typedef void (^RCTImageLoaderCancellationBlock)(void);
* completionHandler when the decoding operation has finished. The method
* should also return a cancellation block, if applicable.
*/
- (RCTImageLoaderCancellationBlock)decodeImageData:(NSData *)imageData size:(CGSize)size scale:(CGFloat)scale resizeMode:(UIViewContentMode)resizeMode completionHandler:(RCTImageLoaderCompletionBlock)completionHandler;
- (RCTImageLoaderCancellationBlock)decodeImageData:(NSData *)imageData
size:(CGSize)size
scale:(CGFloat)scale
resizeMode:(UIViewContentMode)resizeMode
completionHandler:(RCTImageLoaderCompletionBlock)completionHandler;
@optional
/**
* If more than one RCTImageDecoder responds YES to `-canDecodeImageData:`
* then `imageDecoderPriority` is used to determine which one to use. The
* handler with the highest priority will be selected. Default priority is zero.
* If two or more valid handlers have the same priority, the selection order is
* If more than one RCTImageDataDecoder responds YES to `-canDecodeImageData:`
* then `decoderPriority` is used to determine which one to use. The decoder
* with the highest priority will be selected. Default priority is zero.
* If two or more valid decoders have the same priority, the selection order is
* undefined.
*/
- (float)imageDecoderPriority;
- (float)decoderPriority;
@end
+258 -101
View File
@@ -9,26 +9,16 @@
#import "RCTImageLoader.h"
#import <libkern/OSAtomic.h>
#import <UIKit/UIKit.h>
#import "RCTConvert.h"
#import "RCTDefines.h"
#import "RCTImageDownloader.h"
#import "RCTImageUtils.h"
#import "RCTLog.h"
#import "RCTNetworking.h"
#import "RCTUtils.h"
static void RCTDispatchCallbackOnMainQueue(void (^callback)(NSError *, id), NSError *error, UIImage *image)
{
if ([NSThread isMainThread]) {
callback(error, image);
} else {
dispatch_async(dispatch_get_main_queue(), ^{
callback(error, image);
});
}
}
@implementation UIImage (React)
- (CAKeyframeAnimation *)reactKeyframeAnimation
@@ -44,11 +34,131 @@ static void RCTDispatchCallbackOnMainQueue(void (^callback)(NSError *, id), NSEr
@end
@implementation RCTImageLoader
{
NSArray *_loaders;
NSArray *_decoders;
NSURLCache *_cache;
}
@synthesize bridge = _bridge;
RCT_EXPORT_MODULE()
- (void)setBridge:(RCTBridge *)bridge
{
// Get image loaders and decoders
NSMutableArray *loaders = [NSMutableArray array];
NSMutableArray *decoders = [NSMutableArray array];
for (id<RCTBridgeModule> module in bridge.modules.allValues) {
if ([module conformsToProtocol:@protocol(RCTImageURLLoader)]) {
[loaders addObject:module];
}
if ([module conformsToProtocol:@protocol(RCTImageDataDecoder)]) {
[decoders addObject:module];
}
}
// Sort loaders in reverse priority order (highest priority first)
[loaders sortUsingComparator:^NSComparisonResult(id<RCTImageURLLoader> a, id<RCTImageURLLoader> b) {
float priorityA = [a respondsToSelector:@selector(loaderPriority)] ? [a loaderPriority] : 0;
float priorityB = [b respondsToSelector:@selector(loaderPriority)] ? [b loaderPriority] : 0;
if (priorityA > priorityB) {
return NSOrderedAscending;
} else if (priorityA < priorityB) {
return NSOrderedDescending;
} else {
return NSOrderedSame;
}
}];
// Sort decoders in reverse priority order (highest priority first)
[decoders sortUsingComparator:^NSComparisonResult(id<RCTImageDataDecoder> a, id<RCTImageDataDecoder> b) {
float priorityA = [a respondsToSelector:@selector(decoderPriority)] ? [a decoderPriority] : 0;
float priorityB = [b respondsToSelector:@selector(decoderPriority)] ? [b decoderPriority] : 0;
if (priorityA > priorityB) {
return NSOrderedAscending;
} else if (priorityA < priorityB) {
return NSOrderedDescending;
} else {
return NSOrderedSame;
}
}];
_bridge = bridge;
_loaders = loaders;
_decoders = decoders;
_cache = [[NSURLCache alloc] initWithMemoryCapacity:5 * 1024 * 1024 // 5MB
diskCapacity:200 * 1024 * 1024 // 200MB
diskPath:@"React/RCTImageDownloader"];
}
- (id<RCTImageURLLoader>)imageURLLoaderForURL:(NSURL *)URL
{
if (RCT_DEBUG) {
// Check for handler conflicts
float previousPriority = 0;
id<RCTImageURLLoader> previousLoader = nil;
for (id<RCTImageURLLoader> loader in _loaders) {
if ([loader canLoadImageURL:URL]) {
float priority = [loader respondsToSelector:@selector(loaderPriority)] ? [loader loaderPriority] : 0;
if (previousLoader) {
if (priority == previousPriority) {
RCTLogError(@"The RCTImageURLLoaders %@ and %@ both reported that"
" they can load the URL %@, and have equal priority"
" (%g). This could result in non-deterministic behavior.",
loader, previousLoader, URL, priority);
}
} else {
previousLoader = loader;
previousPriority = priority;
}
}
}
}
// Normal code path
for (id<RCTImageURLLoader> loader in _loaders) {
if ([loader canLoadImageURL:URL]) {
return loader;
}
}
return nil;
}
- (id<RCTImageDataDecoder>)imageDataDecoderForData:(NSData *)data
{
if (RCT_DEBUG) {
// Check for handler conflicts
float previousPriority = 0;
id<RCTImageDataDecoder> previousDecoder = nil;
for (id<RCTImageDataDecoder> decoder in _decoders) {
if ([decoder canDecodeImageData:data]) {
float priority = [decoder respondsToSelector:@selector(decoderPriority)] ? [decoder decoderPriority] : 0;
if (previousDecoder) {
if (priority == previousPriority) {
RCTLogError(@"The RCTImageDataDecoders %@ and %@ both reported that"
" they can decode the data <NSData %p; %tu bytes>, and"
" have equal priority (%g). This could result in"
" non-deterministic behavior.",
decoder, previousDecoder, data, data.length, priority);
}
} else {
previousDecoder = decoder;
previousPriority = priority;
}
}
}
}
// Normal code path
for (id<RCTImageDataDecoder> decoder in _decoders) {
if ([decoder canDecodeImageData:data]) {
return decoder;
}
}
return nil;
}
- (RCTImageLoaderCancellationBlock)loadImageWithTag:(NSString *)imageTag
callback:(RCTImageLoaderCompletionBlock)callback
{
@@ -60,40 +170,11 @@ RCT_EXPORT_MODULE()
completionBlock:callback];
}
- (id<RCTImageURLLoader>)imageURLLoaderForRequest:(NSURL *)requestURL
{
NSMutableArray *handlers = [NSMutableArray array];
for (id<RCTBridgeModule> module in _bridge.modules.allValues) {
if ([module conformsToProtocol:@protocol(RCTImageURLLoader)]) {
if ([(id<RCTImageURLLoader>)module canLoadImageURL:requestURL]) {
[handlers addObject:module];
}
}
}
[handlers sortUsingComparator:^NSComparisonResult(id<RCTImageURLLoader> a, id<RCTImageURLLoader> b) {
float priorityA = [a respondsToSelector:@selector(imageLoaderPriority)] ? [a imageLoaderPriority] : 0;
float priorityB = [b respondsToSelector:@selector(imageLoaderPriority)] ? [b imageLoaderPriority] : 0;
if (priorityA < priorityB) {
return NSOrderedAscending;
} else if (priorityA > priorityB) {
return NSOrderedDescending;
} else {
RCTLogError(@"The RCTImageLoader %@ and %@ both reported that they can"
" handle the load request %@, and have equal priority (%g)."
" This could result in non-deterministic behavior.",
a, b, requestURL, priorityA);
return NSOrderedSame;
}
}];
return [handlers lastObject];
}
- (RCTImageLoaderCancellationBlock)loadImageWithTag:(NSString *)imageTag
size:(CGSize)size
scale:(CGFloat)scale
resizeMode:(UIViewContentMode)resizeMode
progressBlock:(RCTImageLoaderProgressBlock)progressBlock
progressBlock:(RCTImageLoaderProgressBlock)progressHandler
completionBlock:(RCTImageLoaderCompletionBlock)completionBlock
{
if (imageTag.length == 0) {
@@ -101,83 +182,157 @@ RCT_EXPORT_MODULE()
return ^{};
}
NSURL *requestURL = [RCTConvert NSURL:imageTag];
id<RCTImageURLLoader> loadHandler = [self imageURLLoaderForRequest:requestURL];
if (!loadHandler) {
RCTLogError(@"No suitable image URL loader found for %@", imageTag);
__block volatile uint32_t cancelled = 0;
RCTImageLoaderCompletionBlock completionHandler = ^(NSError *error, UIImage *image) {
if ([NSThread isMainThread]) {
// Most loaders do not return on the main thread, so caller is probably not
// expecting it, and may do expensive post-processing in the callback
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
if (!cancelled) {
completionBlock(error, image);
}
});
} else if (!cancelled) {
completionBlock(error, image);
}
};
// Find suitable image URL loader
NSURLRequest *request = [RCTConvert NSURLRequest:imageTag];
id<RCTImageURLLoader> loadHandler = [self imageURLLoaderForURL:request.URL];
if (loadHandler) {
return [loadHandler loadImageForURL:request.URL
size:size
scale:scale
resizeMode:resizeMode
progressHandler:progressHandler
completionHandler:completionHandler] ?: ^{};
}
// Check if networking module is available
if (![_bridge respondsToSelector:@selector(networking)]) {
RCTLogError(@"No suitable image URL loader found for %@. You may need to "
" import the RCTNetworking library in order to load images.",
imageTag);
return ^{};
}
return [loadHandler loadImageForURL:requestURL size:size scale:scale resizeMode:resizeMode progressHandler:^(int64_t progress, int64_t total) {
if (!progressBlock) {
return;
}
// Use networking module to load image
if ([_bridge.networking canHandleRequest:request]) {
if ([NSThread isMainThread]) {
progressBlock(progress, total);
} else {
dispatch_async(dispatch_get_main_queue(), ^{
progressBlock(progress, total);
});
}
} completionHandler:^(NSError *error, UIImage *image) {
RCTDispatchCallbackOnMainQueue(completionBlock, error, image);
}];
}
__weak RCTImageLoader *weakSelf = self;
__block RCTImageLoaderCancellationBlock decodeCancel = nil;
RCTURLRequestCompletionBlock processResponse =
^(NSURLResponse *response, NSData *data, __unused NSError *error) {
- (id<RCTImageDecoder>)imageDecoderForRequest:(NSData *)imageData
{
NSMutableArray *handlers = [NSMutableArray array];
for (id<RCTBridgeModule> module in _bridge.modules.allValues) {
if ([module conformsToProtocol:@protocol(RCTImageDecoder)]) {
if ([(id<RCTImageDecoder>)module canDecodeImageData:imageData]) {
[handlers addObject:module];
// Check for http errors
if ([response isKindOfClass:[NSHTTPURLResponse class]]) {
NSInteger statusCode = ((NSHTTPURLResponse *)response).statusCode;
if (statusCode != 200) {
completionHandler([[NSError alloc] initWithDomain:NSURLErrorDomain
code:statusCode
userInfo:nil], nil);
return;
}
}
}
}
[handlers sortUsingComparator:^NSComparisonResult(id<RCTImageDecoder> a, id<RCTImageDecoder> b) {
float priorityA = [a respondsToSelector:@selector(imageDecoderPriority)] ? [a imageDecoderPriority] : 0;
float priorityB = [b respondsToSelector:@selector(imageDecoderPriority)] ? [b imageDecoderPriority] : 0;
if (priorityA < priorityB) {
return NSOrderedAscending;
} else if (priorityA > priorityB) {
return NSOrderedDescending;
} else {
RCTLogError(@"The RCTImageDecoder %@ and %@ both reported that they can"
" handle the decode request <NSData %p; %tu bytes>, and have"
" equal priority (%g). This could result in"
" non-deterministic behavior.",
a, b, imageData, imageData.length, priorityA);
return NSOrderedSame;
// Decode image
decodeCancel = [weakSelf decodeImageData:data
size:size
scale:scale
resizeMode:resizeMode
completionBlock:completionHandler];
};
// Check for cached response before reloading
// TODO: move URL cache out of RCTImageLoader into its own module
NSCachedURLResponse *cachedResponse = [_cache cachedResponseForRequest:request];
if (cachedResponse) {
processResponse(cachedResponse.response, cachedResponse.data, nil);
return ^{};
}
}];
return [handlers lastObject];
// Add missing png extension
if (request.URL.fileURL && request.URL.pathExtension.length == 0) {
NSMutableURLRequest *mutableRequest = [request mutableCopy];
mutableRequest.URL = [NSURL fileURLWithPath:[request.URL.path stringByAppendingPathExtension:@"png"]];
request = mutableRequest;
}
// Download image
RCTNetworkTask *task = [_bridge.networking networkTaskWithRequest:request completionBlock:
^(NSURLResponse *response, NSData *data, NSError *error) {
if (error) {
completionHandler(error, nil);
return;
}
// Cache the response
// TODO: move URL cache out of RCTImageLoader into its own module
RCTImageLoader *strongSelf = weakSelf;
BOOL isHTTPRequest = [request.URL.scheme hasPrefix:@"http"];
[strongSelf->_cache storeCachedResponse:
[[NSCachedURLResponse alloc] initWithResponse:response
data:data
userInfo:nil
storagePolicy:isHTTPRequest ? NSURLCacheStorageAllowed: NSURLCacheStorageAllowedInMemoryOnly]
forRequest:request];
// Process image data
processResponse(response, data, nil);
}];
task.downloadProgressBlock = progressHandler;
[task start];
return ^{
[task cancel];
if (decodeCancel) {
decodeCancel();
}
OSAtomicOr32Barrier(1, &cancelled);
};
}
RCTLogError(@"No suitable image URL loader found for %@", imageTag);
return ^{};
}
- (RCTImageLoaderCancellationBlock)decodeImageData:(NSData *)data
size:(CGSize)size
scale:(CGFloat)scale
resizeMode:(UIViewContentMode)resizeMode
completionBlock:(RCTImageLoaderCompletionBlock)completionBlock
completionBlock:(RCTImageLoaderCompletionBlock)completionHandler
{
id<RCTImageDecoder> imageDecoder = [self imageDecoderForRequest:data];
id<RCTImageDataDecoder> imageDecoder = [self imageDataDecoderForData:data];
if (imageDecoder) {
return [imageDecoder decodeImageData:data size:size scale:scale resizeMode:resizeMode completionHandler:^(NSError *error, UIImage *image) {
RCTDispatchCallbackOnMainQueue(completionBlock, error, image);
}];
return [imageDecoder decodeImageData:data
size:size
scale:scale
resizeMode:resizeMode
completionHandler:completionHandler];
} else {
__block volatile uint32_t cancelled = 0;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
if (cancelled) {
return;
}
UIImage *image = [UIImage imageWithData:data scale:scale];
if (image) {
RCTDispatchCallbackOnMainQueue(completionBlock, nil, image);
completionHandler(nil, image);
} else {
NSString *errorMessage = [NSString stringWithFormat:@"Error decoding image data <NSData %p; %tu bytes>", data, data.length];
NSError *finalError = RCTErrorWithMessage(errorMessage);
RCTDispatchCallbackOnMainQueue(completionBlock, finalError, nil);
completionHandler(finalError, nil);
}
});
return ^{};
return ^{
OSAtomicOr32Barrier(1, &cancelled);
};
}
}
@@ -185,18 +340,20 @@ RCT_EXPORT_MODULE()
- (BOOL)canHandleRequest:(NSURLRequest *)request
{
id<RCTImageURLLoader> handler = [self imageURLLoaderForRequest:request.URL];
// RCTImageDownloader is an image plugin that uses the networking stack.
// We don't want to route any network calls through the image downloader
// as that would cause cyclical dependencies.
return handler && ![handler isKindOfClass:[RCTImageDownloader class]];
NSURL *requestURL = request.URL;
for (id<RCTBridgeModule> module in _bridge.modules.allValues) {
if ([module conformsToProtocol:@protocol(RCTImageURLLoader)] &&
[(id<RCTImageURLLoader>)module canLoadImageURL:requestURL]) {
return YES;
}
}
return NO;
}
- (id)sendRequest:(NSURLRequest *)request withDelegate:(id<RCTURLRequestDelegate>)delegate
{
__block RCTImageLoaderCancellationBlock requestToken;
requestToken = [self.bridge.imageLoader loadImageWithTag:request.URL.absoluteString callback:^(NSError *error, UIImage *image) {
requestToken = [self loadImageWithTag:request.URL.absoluteString callback:^(NSError *error, UIImage *image) {
if (error) {
[delegate URLRequest:requestToken didCompleteWithError:error];
return;
+21 -18
View File
@@ -183,24 +183,26 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init)
resizeMode:self.contentMode
progressBlock:progressHandler
completionBlock:^(NSError *error, UIImage *image) {
if (image.reactKeyframeAnimation) {
[self.layer addAnimation:image.reactKeyframeAnimation forKey:@"contents"];
} else {
[self.layer removeAnimationForKey:@"contents"];
self.image = image;
}
if (error) {
if (_onError) {
_onError(@{ @"error": error.localizedDescription });
dispatch_async(dispatch_get_main_queue(), ^{
if (image.reactKeyframeAnimation) {
[self.layer addAnimation:image.reactKeyframeAnimation forKey:@"contents"];
} else {
[self.layer removeAnimationForKey:@"contents"];
self.image = image;
}
} else {
if (_onLoad) {
_onLoad(nil);
if (error) {
if (_onError) {
_onError(@{ @"error": error.localizedDescription });
}
} else {
if (_onLoad) {
_onLoad(nil);
}
}
}
if (_onLoadEnd) {
_onLoadEnd(nil);
}
if (_onLoadEnd) {
_onLoadEnd(nil);
}
});
}];
} else {
[self clearImage];
@@ -210,7 +212,8 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init)
- (void)reactSetFrame:(CGRect)frame
{
[super reactSetFrame:frame];
if (self.image == nil) {
if (!self.image || self.image == _defaultImage) {
_targetSize = frame.size;
[self reloadImage];
} else if ([RCTImageView srcNeedsReload:_src]) {
@@ -254,7 +257,7 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init)
[strongSelf clearImage];
}
});
} else if (!self.image && self.src) {
} else if (!self.image || self.image == _defaultImage) {
[self reloadImage];
}
}
+13 -4
View File
@@ -11,6 +11,7 @@
#import "RCTImageLoader.h"
#import "RCTBridge.h"
#import "RCTConvert.h"
#import "RCTUIManager.h"
@implementation RCTShadowVirtualImage
{
@@ -37,10 +38,18 @@ RCT_NOT_IMPLEMENTED(-(instancetype)init)
CGFloat scale = [RCTConvert CGFloat:_source[@"scale"]] ?: 1;
__weak RCTShadowVirtualImage *weakSelf = self;
[_bridge.imageLoader loadImageWithTag:imageTag size:CGSizeZero scale:scale resizeMode:UIViewContentModeScaleToFill progressBlock:nil completionBlock:^(NSError *error, UIImage *image) {
RCTShadowVirtualImage *strongSelf = weakSelf;
strongSelf->_image = image;
[strongSelf dirtyText];
[_bridge.imageLoader loadImageWithTag:imageTag
size:CGSizeZero
scale:scale
resizeMode:UIViewContentModeScaleToFill
progressBlock:nil
completionBlock:^(NSError *error, UIImage *image) {
dispatch_async(_bridge.uiManager.methodQueue, ^{
RCTShadowVirtualImage *strongSelf = weakSelf;
strongSelf->_image = image;
[strongSelf dirtyText];
});
}];
}
}
+14 -17
View File
@@ -9,6 +9,8 @@
#import "RCTXCAssetImageLoader.h"
#import <libkern/OSAtomic.h>
#import "RCTUtils.h"
@implementation RCTXCAssetImageLoader
@@ -20,39 +22,34 @@ RCT_EXPORT_MODULE()
return RCTIsXCAssetURL(requestURL);
}
- (float)imageLoaderPriority
- (RCTImageLoaderCancellationBlock)loadImageForURL:(NSURL *)imageURL
size:(CGSize)size
scale:(CGFloat)scale
resizeMode:(UIViewContentMode)resizeMode
progressHandler:(RCTImageLoaderProgressBlock)progressHandler
completionHandler:(RCTImageLoaderCompletionBlock)completionHandler
{
return 100; // higher priority than any ordinary file loader
}
- (RCTImageLoaderCancellationBlock)loadImageForURL:(NSURL *)imageURL size:(CGSize)size scale:(CGFloat)scale resizeMode:(UIViewContentMode)resizeMode progressHandler:(RCTImageLoaderProgressBlock)progressHandler completionHandler:(RCTImageLoaderCompletionBlock)completionHandler
{
__block BOOL cancelled = NO;
__block volatile uint32_t cancelled = 0;
dispatch_async(dispatch_get_main_queue(), ^{
if (cancelled) {
return;
}
NSString *imageName = RCTBundlePathForURL(imageURL);
UIImage *image = [UIImage imageNamed:imageName];
if (image) {
if (progressHandler) {
progressHandler(1, 1);
}
if (completionHandler) {
completionHandler(nil, image);
}
completionHandler(nil, image);
} else {
if (completionHandler) {
NSString *message = [NSString stringWithFormat:@"Could not find image named %@", imageName];
completionHandler(RCTErrorWithMessage(message), nil);
}
NSString *message = [NSString stringWithFormat:@"Could not find image named %@", imageName];
completionHandler(RCTErrorWithMessage(message), nil);
}
});
return ^{
cancelled = YES;
OSAtomicOr32Barrier(1, &cancelled);
};
}
@@ -25,26 +25,23 @@ function expectResolvesAsset(input, expectedSource) {
describe('resolveAssetSource', () => {
beforeEach(() => {
jest.resetModuleRegistry();
__DEV__ = true;
});
it('returns same source for simple static and network images', () => {
var source1 = {uri: 'https://www.facebook.com/logo'};
expect(resolveAssetSource(source1)).toBe(source1);
var source2 = {isStatic: true, uri: 'logo'};
var source2 = {uri: 'logo'};
expect(resolveAssetSource(source2)).toBe(source2);
});
it('does not change deprecated assets', () => {
expect(resolveAssetSource({
isStatic: true,
deprecated: true,
width: 100,
height: 200,
uri: 'logo',
})).toEqual({
isStatic: true,
deprecated: true,
width: 100,
height: 200,
@@ -78,7 +75,6 @@ describe('resolveAssetSource', () => {
type: 'png',
}, {
__packager_asset: true,
isStatic: false,
width: 100,
height: 200,
uri: 'http://10.0.0.1:8081/assets/module/a/logo.png?platform=ios&hash=5b6f00f',
@@ -99,7 +95,6 @@ describe('resolveAssetSource', () => {
type: 'png',
}, {
__packager_asset: true,
isStatic: false,
width: 100,
height: 200,
uri: 'http://10.0.0.1:8081/assets/module/a/logo@2x.png?platform=ios&hash=5b6f00f',
@@ -109,11 +104,10 @@ describe('resolveAssetSource', () => {
});
describe('bundle was loaded from file (PROD) on iOS', () => {
describe('bundle was loaded from file on iOS', () => {
beforeEach(() => {
NativeModules.SourceCode.scriptURL =
'file:///Path/To/Simulator/main.bundle';
__DEV__ = false;
Platform.OS = 'ios';
});
@@ -130,19 +124,18 @@ describe('resolveAssetSource', () => {
type: 'png',
}, {
__packager_asset: true,
isStatic: true,
width: 100,
height: 200,
uri: 'assets/module/a/logo.png',
scale: 1,
});
});
});
describe('bundle was loaded from file (PROD) on Android', () => {
describe('bundle was loaded from file on Android', () => {
beforeEach(() => {
NativeModules.SourceCode.scriptURL =
'file:///Path/To/Simulator/main.bundle';
__DEV__ = false;
Platform.OS = 'android';
});
@@ -159,10 +152,10 @@ describe('resolveAssetSource', () => {
type: 'png',
}, {
__packager_asset: true,
isStatic: true,
width: 100,
height: 200,
uri: 'awesomemodule_subdir_logo1_',
scale: 1,
});
});
});
+19 -25
View File
@@ -7,11 +7,20 @@
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule resolveAssetSource
* @flow
*
* Resolves an asset into a `source` for `Image`.
*/
'use strict';
export type ResolvedAssetSource = {
__packager_asset: boolean,
width: number,
height: number,
uri: string,
scale: number,
};
var AssetRegistry = require('AssetRegistry');
var PixelRatio = require('PixelRatio');
var Platform = require('Platform');
@@ -20,10 +29,6 @@ var SourceCode = require('NativeModules').SourceCode;
var _serverURL;
function getDevServerURL() {
if (!__DEV__) {
// In prod we want assets to be loaded from the archive
return null;
}
if (_serverURL === undefined) {
var scriptURL = SourceCode.scriptURL;
var match = scriptURL && scriptURL.match(/^https?:\/\/.*?\//);
@@ -90,7 +95,7 @@ function getScaledAssetPath(asset) {
return assetDir + '/' + asset.name + scaleSuffix + '.' + asset.type;
}
function pickScale(scales, deviceScale) {
function pickScale(scales: Array<number>, deviceScale: number): number {
// Packager guarantees that `scales` array is sorted
for (var i = 0; i < scales.length; i++) {
if (scales[i] >= deviceScale) {
@@ -104,7 +109,7 @@ function pickScale(scales, deviceScale) {
return scales[scales.length - 1] || 1;
}
function resolveAssetSource(source) {
function resolveAssetSource(source: any): ?ResolvedAssetSource {
if (typeof source === 'object') {
return source;
}
@@ -117,26 +122,15 @@ function resolveAssetSource(source) {
return null;
}
function assetToImageSource(asset) {
function assetToImageSource(asset): ResolvedAssetSource {
var devServerURL = getDevServerURL();
if (devServerURL) {
return {
__packager_asset: true,
width: asset.width,
height: asset.height,
uri: getPathOnDevserver(devServerURL, asset),
isStatic: false,
scale: pickScale(asset.scales, PixelRatio.get()),
};
} else {
return {
__packager_asset: true,
width: asset.width,
height: asset.height,
uri: getPathInArchive(asset),
isStatic: true,
};
}
return {
__packager_asset: true,
width: asset.width,
height: asset.height,
uri: devServerURL ? getPathOnDevserver(devServerURL, asset) : getPathInArchive(asset),
scale: pickScale(asset.scales, PixelRatio.get()),
};
}
module.exports = resolveAssetSource;
@@ -114,7 +114,7 @@ function stackFrameToString(stackFrame, maxLength) {
var fileName = fileNameParts[fileNameParts.length - 1];
if (fileName.length > 18) {
fileName = fileName.substr(0, 17) + '\u2026' /* ... */;
fileName = fileName.substr(0, 17) + '\u2026'; /* ... */
}
var spaces = fillSpaces(maxLength - stackFrame.methodName.length);
@@ -22,10 +22,6 @@
/* eslint strict: 0 */
/* globals GLOBAL: true, window: true */
// Just to make sure the JS gets packaged up.
require('RCTDebugComponentOwnership');
require('RCTDeviceEventEmitter');
require('PerformanceLogger');
require('regenerator/runtime');
if (typeof GLOBAL === 'undefined') {
@@ -46,26 +42,33 @@ function handleError(e, isFatal) {
/**
* Assigns a new global property, replacing the existing one if there is one.
*
*
* Existing properties are preserved as `originalPropertyName`. Both properties
* will maintain the same enumerability & configurability.
*
*
* This allows you to undo the more aggressive polyfills, should you need to.
* For example, if you want to route network requests through DevTools (to trace
* them):
*
* GLOBAL.XMLHTTPRequest = GLOBAL.originalXMLHTTPRequest;
*
* global.XMLHttpRequest = global.originalXMLHttpRequest;
*
* For more info on that particular case, see:
* https://github.com/facebook/react-native/issues/934
*/
function polyfillGlobal(name, newValue, scope=GLOBAL) {
var descriptor = Object.getOwnPropertyDescriptor(scope, name);
var descriptor = Object.getOwnPropertyDescriptor(scope, name) || {
// jest for some bad reasons runs the polyfill code multiple times. In jest
// environment, XmlHttpRequest doesn't exist so getOwnPropertyDescriptor
// returns undefined and defineProperty default for writable is false.
// Therefore, the second time it runs, defineProperty will fatal :(
writable: true,
};
if (scope[name] !== undefined) {
var backupName = `original${name[0].toUpperCase()}${name.substr(1)}`;
Object.defineProperty(scope, backupName, {...descriptor, value: scope[name]});
}
Object.defineProperty(scope, name, {...descriptor, value: newValue});
}
@@ -177,6 +180,7 @@ function setUpNumber() {
Number.MIN_SAFE_INTEGER = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
}
setUpProcessEnv();
setUpRedBoxErrorHandler();
setUpTimers();
setUpAlert();
@@ -186,6 +190,11 @@ setUpRedBoxConsoleErrorHandler();
setUpGeolocation();
setUpWebSockets();
setUpProfile();
setUpProcessEnv();
setUpFlowChecker();
setUpNumber();
// Just to make sure the JS gets packaged up. Wait until the JS environment has
// been initialized before requiring them.
require('RCTDebugComponentOwnership');
require('RCTDeviceEventEmitter');
require('PerformanceLogger');
@@ -48,7 +48,7 @@ function fetchSourceMap(): Promise {
return Promise.resolve(url);
})
.then(fetch)
.then(response => response.text())
.then(response => response.text());
}
function extractSourceMapURL({url, text, fullSourceMappingURL}): ?string {
+2 -1
View File
@@ -29,7 +29,8 @@ var RCTModalHostView = requireNativeComponent('RCTModalHostView', null);
*
* In apps written with React Native from the root view down, you should use
* Navigator instead of Modal. With a top-level Navigator, you have more control
* over how to present the modal scene over the rest of your app.
* over how to present the modal scene over the rest of your app by using the
* configureScene property.
*/
class Modal extends React.Component {
render(): ?ReactElement {
+6 -14
View File
@@ -47,26 +47,18 @@ type FormDataPart = {
*/
class FormData {
_parts: Array<FormDataNameValuePair>;
_partsByKey: {[key: string]: FormDataNameValuePair};
constructor() {
this._parts = [];
this._partsByKey = {};
}
append(key: string, value: FormDataValue) {
var parts = this._partsByKey[key];
if (parts) {
// It's a bit unclear what the behaviour should be in this case.
// The XMLHttpRequest spec doesn't specify it, while MDN says that
// the any new values should appended to existing values. We're not
// doing that for now -- it's tedious and doesn't seem worth the effort.
parts[1] = value;
return;
}
parts = [key, value];
this._parts.push(parts);
this._partsByKey[key] = parts;
// The XMLHttpRequest spec doesn't specify if duplicate keys are allowed.
// MDN says that any new values should be appended to existing values.
// In any case, major browsers allow duplicate keys, so that's what we'll do
// too. They'll simply get appended as additional form data parts in the
// request body, leaving the server to deal with them.
this._parts.push([key, value]);
}
getParts(): Array<FormDataPart> {
+18
View File
@@ -0,0 +1,18 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#import "RCTURLRequestHandler.h"
#import "RCTInvalidating.h"
/**
* This is the default RCTURLRequestHandler implementation for data URL requests.
*/
@interface RCTDataRequestHandler : NSObject <RCTURLRequestHandler, RCTInvalidating>
@end
+73
View File
@@ -0,0 +1,73 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#import "RCTDataRequestHandler.h"
@implementation RCTDataRequestHandler
{
NSOperationQueue *_queue;
}
RCT_EXPORT_MODULE()
- (void)invalidate
{
[_queue cancelAllOperations];
_queue = nil;
}
- (BOOL)canHandleRequest:(NSURLRequest *)request
{
return [request.URL.scheme caseInsensitiveCompare:@"data"] == NSOrderedSame;
}
- (NSOperation *)sendRequest:(NSURLRequest *)request
withDelegate:(id<RCTURLRequestDelegate>)delegate
{
// Lazy setup
if (!_queue) {
_queue = [NSOperationQueue new];
_queue.maxConcurrentOperationCount = 2;
}
__block NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
// Get mime type
NSRange firstSemicolon = [request.URL.resourceSpecifier rangeOfString:@";"];
NSString *mimeType = firstSemicolon.length ? [request.URL.resourceSpecifier substringToIndex:firstSemicolon.location] : nil;
// Send response
NSURLResponse *response = [[NSURLResponse alloc] initWithURL:request.URL
MIMEType:mimeType
expectedContentLength:-1
textEncodingName:nil];
[delegate URLRequest:op didReceiveResponse:response];
// Load data
NSError *error;
NSData *data = [NSData dataWithContentsOfURL:request.URL
options:NSDataReadingMappedIfSafe
error:&error];
if (data) {
[delegate URLRequest:op didReceiveData:data];
}
[delegate URLRequest:op didCompleteWithError:error];
}];
[_queue addOperation:op];
return op;
}
- (void)cancelRequest:(NSOperation *)op
{
[op cancel];
}
@end
@@ -7,15 +7,12 @@
* of patent rights can be found in the PATENTS file in the same directory.
*/
#import "RCTBridge.h"
#import "RCTImageLoader.h"
#import "RCTURLRequestHandler.h"
#import "RCTInvalidating.h"
@interface RCTImageDownloader : NSObject <RCTImageURLLoader>
@end
@interface RCTBridge (RCTImageDownloader)
@property (nonatomic, readonly) RCTImageDownloader *imageDownloader;
/**
* This is the default RCTURLRequestHandler implementation for file requests.
*/
@interface RCTFileRequestHandler : NSObject <RCTURLRequestHandler, RCTInvalidating>
@end
+90
View File
@@ -0,0 +1,90 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#import "RCTFileRequestHandler.h"
#import <MobileCoreServices/MobileCoreServices.h>
#import "RCTUtils.h"
@implementation RCTFileRequestHandler
{
NSOperationQueue *_fileQueue;
}
RCT_EXPORT_MODULE()
- (void)invalidate
{
[_fileQueue cancelAllOperations];
_fileQueue = nil;
}
- (BOOL)canHandleRequest:(NSURLRequest *)request
{
return
[request.URL.scheme caseInsensitiveCompare:@"file"] == NSOrderedSame
&& !RCTIsXCAssetURL(request.URL);
}
- (NSOperation *)sendRequest:(NSURLRequest *)request
withDelegate:(id<RCTURLRequestDelegate>)delegate
{
// Lazy setup
if (!_fileQueue) {
_fileQueue = [NSOperationQueue new];
_fileQueue.maxConcurrentOperationCount = 4;
}
__block NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
// Get content length
NSError *error = nil;
NSFileManager *fileManager = [NSFileManager new];
NSDictionary *fileAttributes = [fileManager attributesOfItemAtPath:request.URL.path error:&error];
if (error) {
[delegate URLRequest:op didCompleteWithError:error];
return;
}
// Get mime type
NSString *fileExtension = [request.URL pathExtension];
NSString *UTI = (__bridge_transfer NSString *)UTTypeCreatePreferredIdentifierForTag(
kUTTagClassFilenameExtension, (__bridge CFStringRef)fileExtension, NULL);
NSString *contentType = (__bridge_transfer NSString *)UTTypeCopyPreferredTagWithClass(
(__bridge CFStringRef)UTI, kUTTagClassMIMEType);
// Send response
NSURLResponse *response = [[NSURLResponse alloc] initWithURL:request.URL
MIMEType:contentType
expectedContentLength:[fileAttributes[NSFileSize] ?: @-1 integerValue]
textEncodingName:nil];
[delegate URLRequest:op didReceiveResponse:response];
// Load data
NSData *data = [NSData dataWithContentsOfURL:request.URL
options:NSDataReadingMappedIfSafe
error:&error];
if (data) {
[delegate URLRequest:op didReceiveData:data];
}
[delegate URLRequest:op didCompleteWithError:error];
}];
[_fileQueue addOperation:op];
return op;
}
- (void)cancelRequest:(NSOperation *)op
{
[op cancel];
}
@end
+3 -1
View File
@@ -50,7 +50,9 @@ RCT_EXPORT_MODULE()
static NSSet *schemes = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
schemes = [[NSSet alloc] initWithObjects:@"http", @"https", @"file", nil];
// technically, RCTHTTPRequestHandler can handle file:// as well,
// but it's less efficient than using RCTFileRequestHandler
schemes = [[NSSet alloc] initWithObjects:@"http", @"https", nil];
});
return [schemes containsObject:request.URL.scheme.lowercaseString];
}
@@ -7,8 +7,10 @@
objects = {
/* Begin PBXBuildFile section */
134E969A1BCEB7F800AFFDA1 /* RCTDataRequestHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 134E96991BCEB7F800AFFDA1 /* RCTDataRequestHandler.m */; settings = {ASSET_TAGS = (); }; };
1372B7371AB03E7B00659ED6 /* RCTNetInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = 1372B7361AB03E7B00659ED6 /* RCTNetInfo.m */; };
13D6D66A1B5FCF8200883BE9 /* RCTDownloadTask.m in Sources */ = {isa = PBXBuildFile; fileRef = 13D6D6691B5FCF8200883BE9 /* RCTDownloadTask.m */; };
13D6D66A1B5FCF8200883BE9 /* RCTNetworkTask.m in Sources */ = {isa = PBXBuildFile; fileRef = 13D6D6691B5FCF8200883BE9 /* RCTNetworkTask.m */; };
13EF800E1BCBE015003F47DD /* RCTFileRequestHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 13EF800D1BCBE015003F47DD /* RCTFileRequestHandler.m */; settings = {ASSET_TAGS = (); }; };
352DA0BA1B17855800AA15A8 /* RCTHTTPRequestHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 352DA0B81B17855800AA15A8 /* RCTHTTPRequestHandler.m */; };
58B512081A9E6CE300147676 /* RCTNetworking.m in Sources */ = {isa = PBXBuildFile; fileRef = 58B512071A9E6CE300147676 /* RCTNetworking.m */; };
/* End PBXBuildFile section */
@@ -26,10 +28,14 @@
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
134E96981BCEB7F800AFFDA1 /* RCTDataRequestHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTDataRequestHandler.h; sourceTree = "<group>"; };
134E96991BCEB7F800AFFDA1 /* RCTDataRequestHandler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTDataRequestHandler.m; sourceTree = "<group>"; };
1372B7351AB03E7B00659ED6 /* RCTNetInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = RCTNetInfo.h; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; };
1372B7361AB03E7B00659ED6 /* RCTNetInfo.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = RCTNetInfo.m; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objc; };
13D6D6681B5FCF8200883BE9 /* RCTDownloadTask.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTDownloadTask.h; sourceTree = "<group>"; };
13D6D6691B5FCF8200883BE9 /* RCTDownloadTask.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTDownloadTask.m; sourceTree = "<group>"; };
13D6D6681B5FCF8200883BE9 /* RCTNetworkTask.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTNetworkTask.h; sourceTree = "<group>"; };
13D6D6691B5FCF8200883BE9 /* RCTNetworkTask.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTNetworkTask.m; sourceTree = "<group>"; };
13EF800C1BCBE015003F47DD /* RCTFileRequestHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTFileRequestHandler.h; sourceTree = "<group>"; };
13EF800D1BCBE015003F47DD /* RCTFileRequestHandler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTFileRequestHandler.m; sourceTree = "<group>"; };
352DA0B71B17855800AA15A8 /* RCTHTTPRequestHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTHTTPRequestHandler.h; sourceTree = "<group>"; };
352DA0B81B17855800AA15A8 /* RCTHTTPRequestHandler.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTHTTPRequestHandler.m; sourceTree = "<group>"; };
58B511DB1A9E6C8500147676 /* libRCTNetwork.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRCTNetwork.a; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -51,10 +57,14 @@
58B511D21A9E6C8500147676 = {
isa = PBXGroup;
children = (
13D6D6681B5FCF8200883BE9 /* RCTDownloadTask.h */,
13D6D6691B5FCF8200883BE9 /* RCTDownloadTask.m */,
13D6D6681B5FCF8200883BE9 /* RCTNetworkTask.h */,
13D6D6691B5FCF8200883BE9 /* RCTNetworkTask.m */,
352DA0B71B17855800AA15A8 /* RCTHTTPRequestHandler.h */,
352DA0B81B17855800AA15A8 /* RCTHTTPRequestHandler.m */,
13EF800C1BCBE015003F47DD /* RCTFileRequestHandler.h */,
13EF800D1BCBE015003F47DD /* RCTFileRequestHandler.m */,
134E96981BCEB7F800AFFDA1 /* RCTDataRequestHandler.h */,
134E96991BCEB7F800AFFDA1 /* RCTDataRequestHandler.m */,
1372B7351AB03E7B00659ED6 /* RCTNetInfo.h */,
1372B7361AB03E7B00659ED6 /* RCTNetInfo.m */,
58B512061A9E6CE300147676 /* RCTNetworking.h */,
@@ -129,7 +139,9 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
13D6D66A1B5FCF8200883BE9 /* RCTDownloadTask.m in Sources */,
13D6D66A1B5FCF8200883BE9 /* RCTNetworkTask.m in Sources */,
13EF800E1BCBE015003F47DD /* RCTFileRequestHandler.m in Sources */,
134E969A1BCEB7F800AFFDA1 /* RCTDataRequestHandler.m in Sources */,
1372B7371AB03E7B00659ED6 /* RCTNetInfo.m in Sources */,
58B512081A9E6CE300147676 /* RCTNetworking.m in Sources */,
352DA0BA1B17855800AA15A8 /* RCTHTTPRequestHandler.m in Sources */,
@@ -18,7 +18,7 @@ typedef void (^RCTURLRequestIncrementalDataBlock)(NSData *data);
typedef void (^RCTURLRequestProgressBlock)(int64_t progress, int64_t total);
typedef void (^RCTURLRequestResponseBlock)(NSURLResponse *response);
@interface RCTDownloadTask : NSObject <RCTURLRequestDelegate>
@interface RCTNetworkTask : NSObject <RCTURLRequestDelegate>
@property (nonatomic, readonly) NSURLRequest *request;
@property (nonatomic, readonly) NSNumber *requestID;
@@ -35,6 +35,7 @@ typedef void (^RCTURLRequestResponseBlock)(NSURLResponse *response);
handler:(id<RCTURLRequestHandler>)handler
completionBlock:(RCTURLRequestCompletionBlock)completionBlock NS_DESIGNATED_INITIALIZER;
- (void)start;
- (void)cancel;
@end
@@ -7,15 +7,15 @@
* of patent rights can be found in the PATENTS file in the same directory.
*/
#import "RCTDownloadTask.h"
#import "RCTNetworkTask.h"
#import "RCTAssert.h"
#import "RCTLog.h"
@implementation RCTDownloadTask
@implementation RCTNetworkTask
{
NSMutableData *_data;
id<RCTURLRequestHandler> _handler;
RCTDownloadTask *_selfReference;
RCTNetworkTask *_selfReference;
}
- (instancetype)initWithRequest:(NSURLRequest *)request
@@ -29,18 +29,16 @@
static NSUInteger requestID = 0;
if ((self = [super init])) {
if (!(_requestToken = [handler sendRequest:request withDelegate:self])) {
return nil;
}
_requestID = @(requestID++);
_request = request;
_handler = handler;
_completionBlock = completionBlock;
_selfReference = self;
}
return self;
}
RCT_NOT_IMPLEMENTED(- (instancetype)init)
- (void)invalidate
{
_selfReference = nil;
@@ -51,7 +49,15 @@
_uploadProgressBlock = nil;
}
RCT_NOT_IMPLEMENTED(- (instancetype)init)
- (void)start
{
if (_requestToken == nil) {
if ([self validateRequestToken:[_handler sendRequest:_request
withDelegate:self]]) {
_selfReference = self;
}
}
}
- (void)cancel
{
@@ -63,10 +69,15 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init)
- (BOOL)validateRequestToken:(id)requestToken
{
if (_requestToken == nil) {
if (requestToken == nil) {
return NO;
}
_requestToken = requestToken;
}
if (![requestToken isEqual:_requestToken]) {
if (RCT_DEBUG) {
RCTAssert([requestToken isEqual:_requestToken],
@"Unrecognized request token: %@", requestToken);
RCTLogError(@"Unrecognized request token: %@ expected: %@", requestToken, _requestToken);
}
if (_completionBlock) {
_completionBlock(_response, _data, [NSError errorWithDomain:RCTErrorDomain code:0
+12 -3
View File
@@ -10,12 +10,21 @@
#import <Foundation/Foundation.h>
#import "RCTBridge.h"
#import "RCTDownloadTask.h"
#import "RCTNetworkTask.h"
@interface RCTNetworking : NSObject <RCTBridgeModule>
- (RCTDownloadTask *)downloadTaskWithRequest:(NSURLRequest *)request
completionBlock:(RCTURLRequestCompletionBlock)completionBlock;
/**
* Does a handler exist for the specified request?
*/
- (BOOL)canHandleRequest:(NSURLRequest *)request;
/**
* Return an RCTNetworkTask for the specified request. This is useful for
* invoking the React Native networking stack from within native code.
*/
- (RCTNetworkTask *)networkTaskWithRequest:(NSURLRequest *)request
completionBlock:(RCTURLRequestCompletionBlock)completionBlock;
@end
+107 -75
View File
@@ -11,7 +11,7 @@
#import "RCTAssert.h"
#import "RCTConvert.h"
#import "RCTDownloadTask.h"
#import "RCTNetworkTask.h"
#import "RCTURLRequestHandler.h"
#import "RCTEventDispatcher.h"
#import "RCTHTTPRequestHandler.h"
@@ -37,10 +37,10 @@ typedef RCTURLRequestCancellationBlock (^RCTHTTPQueryResult)(NSError *error, NSD
@implementation RCTHTTPFormDataHelper
{
NSMutableArray *parts;
NSMutableData *multipartBody;
NSMutableArray *_parts;
NSMutableData *_multipartBody;
RCTHTTPQueryResult _callback;
NSString *boundary;
NSString *_boundary;
}
static NSString *RCTGenerateFormBoundary()
@@ -56,19 +56,19 @@ static NSString *RCTGenerateFormBoundary()
return [[NSString alloc] initWithBytesNoCopy:bytes length:boundaryLength encoding:NSUTF8StringEncoding freeWhenDone:YES];
}
- (RCTURLRequestCancellationBlock)process:(NSArray *)formData
- (RCTURLRequestCancellationBlock)process:(NSDictionaryArray *)formData
callback:(RCTHTTPQueryResult)callback
{
if (formData.count == 0) {
return callback(nil, nil);
}
parts = [formData mutableCopy];
_parts = [formData mutableCopy];
_callback = callback;
multipartBody = [NSMutableData new];
boundary = RCTGenerateFormBoundary();
_multipartBody = [NSMutableData new];
_boundary = RCTGenerateFormBoundary();
return [_networker processDataForHTTPQuery:parts[0] callback:^(NSError *error, NSDictionary *result) {
return [_networker processDataForHTTPQuery:_parts[0] callback:^(NSError *error, NSDictionary *result) {
return [self handleResult:result error:error];
}];
}
@@ -81,37 +81,37 @@ static NSString *RCTGenerateFormBoundary()
}
// Start with boundary.
[multipartBody appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary]
dataUsingEncoding:NSUTF8StringEncoding]];
[_multipartBody appendData:[[NSString stringWithFormat:@"--%@\r\n", _boundary]
dataUsingEncoding:NSUTF8StringEncoding]];
// Print headers.
NSMutableDictionary *headers = [parts[0][@"headers"] mutableCopy];
NSMutableDictionary *headers = [_parts[0][@"headers"] mutableCopy];
NSString *partContentType = result[@"contentType"];
if (partContentType != nil) {
headers[@"content-type"] = partContentType;
}
[headers enumerateKeysAndObjectsUsingBlock:^(NSString *parameterKey, NSString *parameterValue, BOOL *stop) {
[multipartBody appendData:[[NSString stringWithFormat:@"%@: %@\r\n", parameterKey, parameterValue]
dataUsingEncoding:NSUTF8StringEncoding]];
[_multipartBody appendData:[[NSString stringWithFormat:@"%@: %@\r\n", parameterKey, parameterValue]
dataUsingEncoding:NSUTF8StringEncoding]];
}];
// Add the body.
[multipartBody appendData:[@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
[multipartBody appendData:result[@"body"]];
[multipartBody appendData:[@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
[_multipartBody appendData:[@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
[_multipartBody appendData:result[@"body"]];
[_multipartBody appendData:[@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
[parts removeObjectAtIndex:0];
if (parts.count) {
return [_networker processDataForHTTPQuery:parts[0] callback:^(NSError *err, NSDictionary *res) {
[_parts removeObjectAtIndex:0];
if (_parts.count) {
return [_networker processDataForHTTPQuery:_parts[0] callback:^(NSError *err, NSDictionary *res) {
return [self handleResult:res error:err];
}];
}
// We've processed the last item. Finish and return.
[multipartBody appendData:[[NSString stringWithFormat:@"--%@--\r\n", boundary]
dataUsingEncoding:NSUTF8StringEncoding]];
NSString *contentType = [NSString stringWithFormat:@"multipart/form-data; boundary=\"%@\"", boundary];
return _callback(nil, @{@"body": multipartBody, @"contentType": contentType});
[_multipartBody appendData:[[NSString stringWithFormat:@"--%@--\r\n", _boundary]
dataUsingEncoding:NSUTF8StringEncoding]];
NSString *contentType = [NSString stringWithFormat:@"multipart/form-data; boundary=\"%@\"", _boundary];
return _callback(nil, @{@"body": _multipartBody, @"contentType": contentType});
}
@end
@@ -122,6 +122,7 @@ static NSString *RCTGenerateFormBoundary()
@implementation RCTNetworking
{
NSMutableDictionary *_tasksByRequestID;
NSArray *_handlers;
}
@synthesize bridge = _bridge;
@@ -129,12 +130,65 @@ static NSString *RCTGenerateFormBoundary()
RCT_EXPORT_MODULE()
- (instancetype)init
- (void)setBridge:(RCTBridge *)bridge
{
if ((self = [super init])) {
_tasksByRequestID = [NSMutableDictionary new];
// get handlers
NSMutableArray *handlers = [NSMutableArray array];
for (id<RCTBridgeModule> module in bridge.modules.allValues) {
if ([module conformsToProtocol:@protocol(RCTURLRequestHandler)]) {
[handlers addObject:module];
}
}
return self;
// Sort handlers in reverse priority order (highest priority first)
[handlers sortUsingComparator:^NSComparisonResult(id<RCTURLRequestHandler> a, id<RCTURLRequestHandler> b) {
float priorityA = [a respondsToSelector:@selector(handlerPriority)] ? [a handlerPriority] : 0;
float priorityB = [b respondsToSelector:@selector(handlerPriority)] ? [b handlerPriority] : 0;
if (priorityA > priorityB) {
return NSOrderedAscending;
} else if (priorityA < priorityB) {
return NSOrderedDescending;
} else {
return NSOrderedSame;
}
}];
_bridge = bridge;
_handlers = handlers;
_tasksByRequestID = [NSMutableDictionary new];
}
- (id<RCTURLRequestHandler>)handlerForRequest:(NSURLRequest *)request
{
if (RCT_DEBUG) {
// Check for handler conflicts
float previousPriority = 0;
id<RCTURLRequestHandler> previousHandler = nil;
for (id<RCTURLRequestHandler> handler in _handlers) {
if ([handler canHandleRequest:request]) {
float priority = [handler respondsToSelector:@selector(handlerPriority)] ? [handler handlerPriority] : 0;
if (previousHandler) {
if (priority == previousPriority) {
RCTLogError(@"The RCTURLRequestHandlers %@ and %@ both reported that"
" they can handle the request %@, and have equal priority"
" (%g). This could result in non-deterministic behavior.",
handler, previousHandler, request, priority);
}
} else {
previousHandler = handler;
previousPriority = priority;
}
}
}
}
// Normal code path
for (id<RCTURLRequestHandler> handler in _handlers) {
if ([handler canHandleRequest:request]) {
return handler;
}
}
return nil;
}
- (RCTURLRequestCancellationBlock)buildRequest:(NSDictionary *)query
@@ -169,37 +223,9 @@ RCT_EXPORT_MODULE()
}];
}
- (id<RCTURLRequestHandler>)handlerForRequest:(NSURLRequest *)request
- (BOOL)canHandleRequest:(NSURLRequest *)request
{
NSMutableArray *handlers = [NSMutableArray array];
for (id<RCTBridgeModule> module in _bridge.modules.allValues) {
if ([module conformsToProtocol:@protocol(RCTURLRequestHandler)]) {
if ([(id<RCTURLRequestHandler>)module canHandleRequest:request]) {
[handlers addObject:module];
}
}
}
[handlers sortUsingComparator:^NSComparisonResult(id<RCTURLRequestHandler> a, id<RCTURLRequestHandler> b) {
float priorityA = [a respondsToSelector:@selector(handlerPriority)] ? [a handlerPriority] : 0;
float priorityB = [b respondsToSelector:@selector(handlerPriority)] ? [b handlerPriority] : 0;
if (priorityA < priorityB) {
return NSOrderedAscending;
} else if (priorityA > priorityB) {
return NSOrderedDescending;
} else {
RCTLogError(@"The RCTURLRequestHandlers %@ and %@ both reported that"
" they can handle the request %@, and have equal priority"
" (%g). This could result in non-deterministic behavior.",
a, b, request, priorityA);
return NSOrderedSame;
}
}];
id<RCTURLRequestHandler> handler = handlers.lastObject;
if (!handler) {
RCTLogError(@"No suitable request handler found for %@", request.URL);
}
return handler;
return [self handlerForRequest:request] != nil;
}
/**
@@ -234,11 +260,13 @@ RCT_EXPORT_MODULE()
if (request) {
__block RCTURLRequestCancellationBlock cancellationBlock = nil;
RCTDownloadTask *task = [self downloadTaskWithRequest:request completionBlock:^(NSURLResponse *response, NSData *data, NSError *error) {
RCTNetworkTask *task = [self networkTaskWithRequest:request completionBlock:^(NSURLResponse *response, NSData *data, NSError *error) {
cancellationBlock = callback(error, data ? @{@"body": data, @"contentType": RCTNullIfNil(response.MIMEType)} : nil);
}];
__weak RCTDownloadTask *weakTask = task;
[task start];
__weak RCTNetworkTask *weakTask = task;
return ^{
[weakTask cancel];
if (cancellationBlock) {
@@ -257,7 +285,7 @@ RCT_EXPORT_MODULE()
return callback(nil, nil);
}
- (void)sendData:(NSData *)data forTask:(RCTDownloadTask *)task
- (void)sendData:(NSData *)data forTask:(RCTNetworkTask *)task
{
if (data.length == 0) {
return;
@@ -276,7 +304,7 @@ RCT_EXPORT_MODULE()
if (!responseText && data.length) {
// We don't have an encoding, or the encoding is incorrect, so now we
// try to guess (unfortunately, this feature is available of iOS 8+ only)
// try to guess (unfortunately, this feature is available in iOS 8+ only)
if ([NSString respondsToSelector:@selector(stringEncodingForData:
encodingOptions:
convertedString:
@@ -303,7 +331,7 @@ RCT_EXPORT_MODULE()
incrementalUpdates:(BOOL)incrementalUpdates
responseSender:(RCTResponseSenderBlock)responseSender
{
__block RCTDownloadTask *task;
__block RCTNetworkTask *task;
RCTURLRequestProgressBlock uploadProgressBlock = ^(int64_t progress, int64_t total) {
dispatch_async(_methodQueue, ^{
@@ -314,16 +342,17 @@ RCT_EXPORT_MODULE()
void (^responseBlock)(NSURLResponse *) = ^(NSURLResponse *response) {
dispatch_async(_methodQueue, ^{
NSHTTPURLResponse *httpResponse = nil;
if ([response isKindOfClass:[NSHTTPURLResponse class]]) {
// Might be a local file request
httpResponse = (NSHTTPURLResponse *)response;
NSDictionary *headers;
NSInteger status;
if ([response isKindOfClass:[NSHTTPURLResponse class]]) { // Might be a local file request
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
headers = httpResponse.allHeaderFields ?: @{};
status = httpResponse.statusCode;
} else {
headers = response.MIMEType ? @{@"Content-Type": response.MIMEType} : @{};
status = 200;
}
NSArray *responseJSON = @[task.requestID,
@(httpResponse.statusCode ?: 200),
httpResponse.allHeaderFields ?: @{},
];
NSArray *responseJSON = @[task.requestID, @(status), headers];
[_bridge.eventDispatcher sendDeviceEventWithName:@"didReceiveNetworkResponse"
body:responseJSON];
});
@@ -352,7 +381,7 @@ RCT_EXPORT_MODULE()
});
};
task = [self downloadTaskWithRequest:request completionBlock:completionBlock];
task = [self networkTaskWithRequest:request completionBlock:completionBlock];
task.incrementalDataBlock = incrementalDataBlock;
task.responseBlock = responseBlock;
task.uploadProgressBlock = uploadProgressBlock;
@@ -361,19 +390,22 @@ RCT_EXPORT_MODULE()
_tasksByRequestID[task.requestID] = task;
responseSender(@[task.requestID]);
}
[task start];
}
#pragma mark - Public API
- (RCTDownloadTask *)downloadTaskWithRequest:(NSURLRequest *)request
- (RCTNetworkTask *)networkTaskWithRequest:(NSURLRequest *)request
completionBlock:(RCTURLRequestCompletionBlock)completionBlock
{
id<RCTURLRequestHandler> handler = [self handlerForRequest:request];
if (!handler) {
RCTLogError(@"No suitable URL request handler found for %@", request.URL);
return nil;
}
return [[RCTDownloadTask alloc] initWithRequest:request
return [[RCTNetworkTask alloc] initWithRequest:request
handler:handler
completionBlock:completionBlock];
}
@@ -29,6 +29,27 @@ var NOTIF_REGISTER_EVENT = 'remoteNotificationsRegistered';
*
* To get up and running, [configure your notifications with Apple](https://developer.apple.com/library/ios/documentation/IDEs/Conceptual/AppDistributionGuide/AddingCapabilities/AddingCapabilities.html#//apple_ref/doc/uid/TP40012582-CH26-SW6)
* and your server-side system. To get an idea, [this is the Parse guide](https://parse.com/tutorials/ios-push-notifications).
*
* To enable support for `notification` and `register` events you need to augment your AppDelegate.
*
* At the top of your `AppDelegate.m`:
*
* `#import "RCTPushNotificationManager.h"`
*
* And then in your AppDelegate implementation add the following:
*
* ```
* // Required for the register event.
* - (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
* {
* [RCTPushNotificationManager application:application didRegisterForRemoteNotificationsWithDeviceToken:deviceToken];
* }
* // Required for the notification event.
* - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)notification
* {
* [RCTPushNotificationManager application:application didReceiveRemoteNotification:notification];
* }
* ```
*/
class PushNotificationIOS {
_data: Object;
@@ -61,6 +82,13 @@ class PushNotificationIOS {
RCTPushNotificationManager.scheduleLocalNotification(details);
}
/**
* Cancels all scheduled localNotifications
*/
static cancelAllLocalNotifications() {
RCTPushNotificationManager.cancelAllLocalNotifications();
}
/**
* Sets the badge number for the app icon on the home screen
*/
@@ -212,10 +212,14 @@ RCT_EXPORT_METHOD(presentLocalNotification:(UILocalNotification *)notification)
[RCTSharedApplication() presentLocalNotificationNow:notification];
}
RCT_EXPORT_METHOD(scheduleLocalNotification:(UILocalNotification *)notification)
{
[RCTSharedApplication() scheduleLocalNotification:notification];
}
RCT_EXPORT_METHOD(cancelAllLocalNotifications)
{
[RCTSharedApplication() cancelAllLocalNotifications];
}
@end
@@ -0,0 +1,13 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule SnapshotViewIOS
*/
'use strict';
module.exports = require('UnimplementedView');
@@ -6,7 +6,7 @@
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule SnapshotView
* @providesModule SnapshotViewIOS
* @flow
*/
'use strict';
@@ -14,12 +14,12 @@
var Platform = require('Platform');
var React = require('React');
var StyleSheet = require('StyleSheet');
var { TestModule } = require('NativeModules');
var { TestModule, UIManager } = require('NativeModules');
var View = require('View');
var requireNativeComponent = require('requireNativeComponent');
var SnapshotView = React.createClass({
var SnapshotViewIOS = React.createClass({
onDefaultAction: function(event: Object) {
TestModule.verifySnapshot(TestModule.markTestPassed);
},
@@ -51,8 +51,11 @@ var style = StyleSheet.create({
},
});
var RCTSnapshot = Platform.OS === 'ios' ?
requireNativeComponent('RCTSnapshot', SnapshotView) :
// Verify that RCTSnapshot is part of the UIManager since it is only loaded
// if you have linked against RCTTest like in tests, otherwise we will have
// a warning printed out
var RCTSnapshot = UIManager.RCTSnapshot ?
requireNativeComponent('RCTSnapshot', SnapshotViewIOS) :
View;
module.exports = SnapshotView;
module.exports = SnapshotViewIOS;
+2 -2
View File
@@ -135,12 +135,12 @@ var WarningRow = React.createClass({
});
this.panGesture = PanResponder.create({
onStartShouldSetPanResponder: () => {
return !! this.dismissalSpring.getCurrentValue();
return !!this.dismissalSpring.getCurrentValue();
},
onMoveShouldSetPanResponder: () => true,
onPanResponderGrant: () => {
this.isResponderOnlyToBlockTouches =
!! this.dismissalSpring.getCurrentValue();
!!this.dismissalSpring.getCurrentValue();
},
onPanResponderMove: (e, gestureState) => {
if (this.isResponderOnlyToBlockTouches) {
+4 -1
View File
@@ -11,6 +11,10 @@
*/
'use strict';
// Require ReactNativeDefaultInjection first for its side effects of setting up
// the JS environment
var ReactNativeDefaultInjection = require('ReactNativeDefaultInjection');
var ReactChildren = require('ReactChildren');
var ReactClass = require('ReactClass');
var ReactComponent = require('ReactComponent');
@@ -18,7 +22,6 @@ var ReactCurrentOwner = require('ReactCurrentOwner');
var ReactElement = require('ReactElement');
var ReactElementValidator = require('ReactElementValidator');
var ReactInstanceHandles = require('ReactInstanceHandles');
var ReactNativeDefaultInjection = require('ReactNativeDefaultInjection');
var ReactNativeMount = require('ReactNativeMount');
var ReactPropTypes = require('ReactPropTypes');
var ReactUpdates = require('ReactUpdates');
@@ -47,6 +47,13 @@ var TransformPropTypes = {
])
),
transformMatrix: TransformMatrixPropType,
/* Deprecated transform props used on Android only */
scaleX: ReactPropTypes.number,
scaleY: ReactPropTypes.number,
rotation: ReactPropTypes.number,
translateX: ReactPropTypes.number,
translateY: ReactPropTypes.number,
};
module.exports = TransformPropTypes;
+4 -4
View File
@@ -86,7 +86,10 @@ static css_dim_t RCTMeasure(void *context, float width)
parentProperties = [super processUpdatedProperties:applierBlocks
parentProperties:parentProperties];
NSTextStorage *textStorage = [self buildTextStorageForWidth:self.frame.size.width];
UIEdgeInsets padding = self.paddingAsInsets;
CGFloat width = self.frame.size.width - (padding.left + padding.right);
NSTextStorage *textStorage = [self buildTextStorageForWidth:width];
[applierBlocks addObject:^(RCTSparseArray *viewRegistry) {
RCTText *view = viewRegistry[self.reactTag];
view.textStorage = textStorage;
@@ -105,9 +108,6 @@ static css_dim_t RCTMeasure(void *context, float width)
- (NSTextStorage *)buildTextStorageForWidth:(CGFloat)width
{
UIEdgeInsets padding = self.paddingAsInsets;
width -= (padding.left + padding.right);
if (_cachedTextStorage && width == _cachedTextStorageWidth) {
return _cachedTextStorage;
}
-1
View File
@@ -113,7 +113,6 @@ var Text = React.createClass({
isHighlighted: false,
});
},
getDefaultProps: function(): Object {
return {
allowFontScaling: true,
+33 -35
View File
@@ -25,6 +25,7 @@ let stringifySafe = require('stringifySafe');
let MODULE_IDS = 0;
let METHOD_IDS = 1;
let PARAMS = 2;
let MIN_TIME_BETWEEN_FLUSHES_MS = 5;
let SPY_MODE = false;
@@ -53,9 +54,9 @@ class MessageQueue {
this._methodTable = {};
this._callbacks = [];
this._callbackID = 0;
this._lastFlush = 0;
[
'processBatch',
'invokeCallbackAndReturnFlushedQueue',
'callFunctionReturnFlushedQueue',
'flushedQueue',
@@ -75,42 +76,30 @@ class MessageQueue {
/**
* Public APIs
*/
processBatch(batch) {
callFunctionReturnFlushedQueue(module, method, args) {
guard(() => {
ReactUpdates.batchedUpdates(() => {
batch.forEach((call) => {
let method = call.method === 'callFunctionReturnFlushedQueue' ?
'__callFunction' : '__invokeCallback';
guard(() => this[method].apply(this, call.args));
});
this.__callImmediates();
});
// batchedUpdates might still trigger setImmediates
while (JSTimersExecution.immediates.length) {
ReactUpdates.batchedUpdates(() => {
this.__callImmediates();
});
}
this.__callFunction(module, method, args);
this.__callImmediates();
});
return this.__flushedQueue();
}
callFunctionReturnFlushedQueue(module, method, args) {
guard(() => this.__callFunction(module, method, args));
return this.flushedQueue();
}
invokeCallbackAndReturnFlushedQueue(cbID, args) {
guard(() => this.__invokeCallback(cbID, args));
guard(() => {
this.__invokeCallback(cbID, args);
this.__callImmediates();
});
return this.flushedQueue();
}
flushedQueue() {
this.__callImmediates();
return this.__flushedQueue();
let queue = this._queue;
this._queue = [[],[],[]];
return queue[0].length ? queue : null;
}
/**
@@ -123,11 +112,6 @@ class MessageQueue {
BridgeProfiling.profileEnd();
}
__flushedQueue() {
let queue = this._queue;
this._queue = [[],[],[]];
return queue[0].length ? queue : null;
}
__nativeCall(module, method, params, onFail, onSucc) {
if (onFail || onSucc) {
// eventually delete old debug info
@@ -143,6 +127,14 @@ class MessageQueue {
this._queue[MODULE_IDS].push(module);
this._queue[METHOD_IDS].push(method);
this._queue[PARAMS].push(params);
var now = new Date().getTime();
if (global.nativeFlushQueueImmediate &&
now - this._lastFlush >= MIN_TIME_BETWEEN_FLUSHES_MS) {
global.nativeFlushQueueImmediate(this._queue);
this._queue = [[],[],[]];
this._lastFlush = now;
}
if (__DEV__ && SPY_MODE && isFinite(module)) {
console.log('JS->N : ' + this._remoteModuleTable[module] + '.' +
this._remoteMethodTable[module][method] + '(' + JSON.stringify(params) + ')');
@@ -151,6 +143,7 @@ class MessageQueue {
__callFunction(module, method, args) {
BridgeProfiling.profile(() => `${module}.${method}(${stringifySafe(args)})`);
this._lastFlush = new Date().getTime();
if (isFinite(module)) {
method = this._methodTable[module][method];
module = this._moduleTable[module];
@@ -166,6 +159,7 @@ class MessageQueue {
__invokeCallback(cbID, args) {
BridgeProfiling.profile(
() => `MessageQueue.invokeCallback(${cbID}, ${stringifySafe(args)})`);
this._lastFlush = new Date().getTime();
let callback = this._callbacks[cbID];
if (!callback || __DEV__) {
let debug = this._debugInfo[cbID >> 1];
@@ -192,7 +186,7 @@ class MessageQueue {
let moduleNames = Object.keys(localModules);
for (var i = 0, l = moduleNames.length; i < l; i++) {
let moduleName = moduleNames[i];
let methods = localModules[moduleName].methods;
let methods = localModules[moduleName].methods || {};
let moduleID = localModules[moduleName].moduleID;
moduleTable[moduleID] = moduleName;
methodTable[moduleID] = {};
@@ -216,12 +210,16 @@ class MessageQueue {
}
_genModule(module, moduleConfig) {
let methodNames = Object.keys(moduleConfig.methods);
let methods = moduleConfig.methods || {};
let methodNames = Object.keys(methods);
for (var i = 0, l = methodNames.length; i < l; i++) {
let methodName = methodNames[i];
let methodConfig = moduleConfig.methods[methodName];
let methodConfig = methods[methodName];
module[methodName] = this._genMethod(
moduleConfig.moduleID, methodConfig.methodID, methodConfig.type);
moduleConfig.moduleID,
methodConfig.methodID,
methodConfig.type || MethodTypes.remote
);
}
Object.assign(module, moduleConfig.constants);
return module;
@@ -266,7 +264,7 @@ class MessageQueue {
}
function createErrorFromErrorData(errorData: ErrorData): Error {
function createErrorFromErrorData(errorData: {message: string}): Error {
var {
message,
...extraErrorInfo,
@@ -95,29 +95,6 @@ describe('MessageQueue', () => {
queue.__invokeCallback(1);
expect(() => queue.__invokeCallback(0)).toThrow();
});
describe('processBatch', () => {
it('should call __invokeCallback for invokeCallbackAndReturnFlushedQueue', () => {
queue.__invokeCallback = jasmine.createSpy();
queue.processBatch([{
method: 'invokeCallbackAndReturnFlushedQueue',
args: [],
}]);
expect(queue.__invokeCallback.callCount).toEqual(1);
});
it('should call __callFunction for callFunctionReturnFlushedQueue', () => {
queue.__callFunction = jasmine.createSpy();
queue.processBatch([{
method: 'callFunctionReturnFlushedQueue',
args: [],
}]);
expect(queue.__callFunction.callCount).toEqual(1);
});
});
});
var remoteModulesConfig = {
+15 -13
View File
@@ -1381,20 +1381,22 @@ static const size_t RCTSRFrameHeaderOverhead = 32;
if (aStream.streamError) {
[self _failWithError:aStream.streamError];
} else {
if (self.readyState != RCTSR_CLOSED) {
self.readyState = RCTSR_CLOSED;
_selfRetain = nil;
}
dispatch_async(_workQueue, ^{
if (self.readyState != RCTSR_CLOSED) {
self.readyState = RCTSR_CLOSED;
_selfRetain = nil;
}
if (!_sentClose && !_failed) {
_sentClose = YES;
// If we get closed in this state it's probably not clean because we should be sending this when we send messages
[self _performDelegateBlock:^{
if ([self.delegate respondsToSelector:@selector(webSocket:didCloseWithCode:reason:wasClean:)]) {
[self.delegate webSocket:self didCloseWithCode:RCTSRStatusCodeGoingAway reason:@"Stream end encountered" wasClean:NO];
}
}];
}
if (!_sentClose && !_failed) {
_sentClose = YES;
// If we get closed in this state it's probably not clean because we should be sending this when we send messages
[self _performDelegateBlock:^{
if ([self.delegate respondsToSelector:@selector(webSocket:didCloseWithCode:reason:wasClean:)]) {
[self.delegate webSocket:self didCloseWithCode:RCTSRStatusCodeGoingAway reason:@"Stream end encountered" wasClean:NO];
}
}];
}
});
}
break;

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