From b05e99a53128d757237eada0a2bb38ce33a53848 Mon Sep 17 00:00:00 2001 From: Gabe Levi Date: Wed, 10 Jun 2015 13:42:41 -0700 Subject: [PATCH 01/28] [Flow] Fix or suppress react-native github errors for Flow v0.12.0 --- Examples/Movies/MovieCell.js | 5 ++++- Examples/Movies/MovieScreen.js | 3 +++ Examples/Movies/MoviesApp.js | 3 +++ Examples/UIExplorer/MapViewExample.js | 7 +++++-- Examples/UIExplorer/UIExplorerApp.ios.js | 3 +++ Examples/UIExplorer/UIExplorerPage.js | 4 ++-- Libraries/Components/ScrollView/ScrollView.js | 2 +- Libraries/Network/NetInfo.js | 14 +++++++------- .../PushNotificationIOS/PushNotificationIOS.js | 10 +++++----- .../ReactIOS/InspectorOverlay/resolveBoxStyle.js | 2 +- .../ReactNative/createReactNativeComponentClass.js | 4 ++-- Libraries/Utilities/BridgeProfiling.js | 2 +- 12 files changed, 37 insertions(+), 22 deletions(-) diff --git a/Examples/Movies/MovieCell.js b/Examples/Movies/MovieCell.js index e6a9b1078..341460e58 100644 --- a/Examples/Movies/MovieCell.js +++ b/Examples/Movies/MovieCell.js @@ -34,11 +34,14 @@ var MovieCell = React.createClass({ var criticsScore = this.props.movie.ratings.critics_score; return ( - + {/* $FlowIssue #7363964 - There's a bug in Flow where you cannot + * omit a property or set it to undefined if it's inside a shape, + * even if it isn't required */} + {/* $FlowIssue #7363964 - There's a bug in Flow where you cannot + * omit a property or set it to undefined if it's inside a shape, + * even if it isn't required */} + {/* $FlowIssue #7363964 - There's a bug in Flow where you cannot + * omit a property or set it to undefined if it's inside a shape, + * even if it isn't required */} ); } else { - ContentWrapper = ScrollView; + ContentWrapper = (ScrollView: ReactClass); wrapperProps.keyboardShouldPersistTaps = true; wrapperProps.keyboardDismissMode = 'interactive'; } diff --git a/Libraries/Components/ScrollView/ScrollView.js b/Libraries/Components/ScrollView/ScrollView.js index 8d274bdc5..5700ff964 100644 --- a/Libraries/Components/ScrollView/ScrollView.js +++ b/Libraries/Components/ScrollView/ScrollView.js @@ -299,7 +299,7 @@ var ScrollView = React.createClass({ onResponderRelease: this.scrollResponderHandleResponderRelease, onResponderReject: this.scrollResponderHandleResponderReject, }; - + var ScrollViewClass; if (Platform.OS === 'ios') { ScrollViewClass = RCTScrollView; diff --git a/Libraries/Network/NetInfo.js b/Libraries/Network/NetInfo.js index 47184d181..c62cb209a 100644 --- a/Libraries/Network/NetInfo.js +++ b/Libraries/Network/NetInfo.js @@ -147,7 +147,7 @@ var NetInfo = { eventName: ChangeEventName, handler: Function ): void { - _subscriptions[handler] = RCTDeviceEventEmitter.addListener( + _subscriptions[String(handler)] = RCTDeviceEventEmitter.addListener( DEVICE_REACHABILITY_EVENT, (appStateData) => { handler(appStateData.network_reachability); @@ -159,11 +159,11 @@ var NetInfo = { eventName: ChangeEventName, handler: Function ): void { - if (!_subscriptions[handler]) { + if (!_subscriptions[String(handler)]) { return; } - _subscriptions[handler].remove(); - _subscriptions[handler] = null; + _subscriptions[String(handler)].remove(); + _subscriptions[String(handler)] = null; }, fetch: function(): Promise { @@ -204,12 +204,12 @@ NetInfo.isConnected = { eventName: ChangeEventName, handler: Function ): void { - _isConnectedSubscriptions[handler] = (connection) => { + _isConnectedSubscriptions[String(handler)] = (connection) => { handler(_isConnected(connection)); }; NetInfo.addEventListener( eventName, - _isConnectedSubscriptions[handler] + _isConnectedSubscriptions[String(handler)] ); }, @@ -219,7 +219,7 @@ NetInfo.isConnected = { ): void { NetInfo.removeEventListener( eventName, - _isConnectedSubscriptions[handler] + _isConnectedSubscriptions[String(handler)] ); }, diff --git a/Libraries/PushNotificationIOS/PushNotificationIOS.js b/Libraries/PushNotificationIOS/PushNotificationIOS.js index 732d4c029..fa2be650a 100644 --- a/Libraries/PushNotificationIOS/PushNotificationIOS.js +++ b/Libraries/PushNotificationIOS/PushNotificationIOS.js @@ -66,14 +66,14 @@ class PushNotificationIOS { 'PushNotificationIOS only supports `notification` and `register` events' ); if (type === 'notification') { - _notifHandlers[handler] = RCTDeviceEventEmitter.addListener( + _notifHandlers[String(handler)] = RCTDeviceEventEmitter.addListener( DEVICE_NOTIF_EVENT, (notifData) => { handler(new PushNotificationIOS(notifData)); } ); } else if (type === 'register') { - _notifHandlers[handler] = RCTDeviceEventEmitter.addListener( + _notifHandlers[String(handler)] = RCTDeviceEventEmitter.addListener( NOTIF_REGISTER_EVENT, (registrationInfo) => { handler(registrationInfo.deviceToken); @@ -143,11 +143,11 @@ class PushNotificationIOS { type === 'notification' || type === 'register', 'PushNotificationIOS only supports `notification` and `register` events' ); - if (!_notifHandlers[handler]) { + if (!_notifHandlers[String(handler)]) { return; } - _notifHandlers[handler].remove(); - _notifHandlers[handler] = null; + _notifHandlers[String(handler)].remove(); + _notifHandlers[String(handler)] = null; } diff --git a/Libraries/ReactIOS/InspectorOverlay/resolveBoxStyle.js b/Libraries/ReactIOS/InspectorOverlay/resolveBoxStyle.js index e0bfb601c..0c2857bd0 100644 --- a/Libraries/ReactIOS/InspectorOverlay/resolveBoxStyle.js +++ b/Libraries/ReactIOS/InspectorOverlay/resolveBoxStyle.js @@ -20,7 +20,7 @@ * * If none are set, returns false. */ -function resolveBoxStyle(prefix: String, style: Object): ?Object { +function resolveBoxStyle(prefix: string, style: Object): ?Object { var res = {}; var subs = ['top', 'left', 'bottom', 'right']; var set = false; diff --git a/Libraries/ReactNative/createReactNativeComponentClass.js b/Libraries/ReactNative/createReactNativeComponentClass.js index 3940df012..5d6d87cd8 100644 --- a/Libraries/ReactNative/createReactNativeComponentClass.js +++ b/Libraries/ReactNative/createReactNativeComponentClass.js @@ -26,7 +26,7 @@ type ReactNativeBaseComponentViewConfig = { */ var createReactNativeComponentClass = function( viewConfig: ReactNativeBaseComponentViewConfig -): Function { // returning Function is lossy :/ +): ReactClass { var Constructor = function(element) { this._currentElement = element; @@ -39,7 +39,7 @@ var createReactNativeComponentClass = function( Constructor.prototype = new ReactNativeBaseComponent(viewConfig); Constructor.prototype.constructor = Constructor; - return Constructor; + return ((Constructor: any): ReactClass); }; module.exports = createReactNativeComponentClass; diff --git a/Libraries/Utilities/BridgeProfiling.js b/Libraries/Utilities/BridgeProfiling.js index e3c47907b..c94d84fdd 100644 --- a/Libraries/Utilities/BridgeProfiling.js +++ b/Libraries/Utilities/BridgeProfiling.js @@ -14,7 +14,7 @@ var GLOBAL = GLOBAL || this; var BridgeProfiling = { - profile(profileName: String, args?: any) { + profile(profileName: string, args?: any) { if (GLOBAL.__BridgeProfilingIsProfiling) { if (args) { try { From f8482411f96f355dd93793bf16b5aaf77231f7ba Mon Sep 17 00:00:00 2001 From: Eric Vicenti Date: Wed, 10 Jun 2015 23:37:27 -0700 Subject: [PATCH 02/28] [AdsManager] Improve animation configurations --- .../CustomComponents/Navigator/NavigatorSceneConfigs.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Libraries/CustomComponents/Navigator/NavigatorSceneConfigs.js b/Libraries/CustomComponents/Navigator/NavigatorSceneConfigs.js index 48be13ea1..439534ddd 100644 --- a/Libraries/CustomComponents/Navigator/NavigatorSceneConfigs.js +++ b/Libraries/CustomComponents/Navigator/NavigatorSceneConfigs.js @@ -313,14 +313,14 @@ var FromTheFrontAndroid = { opacity: { from: 0, to: 1, - min: 0, + min: 0.5, max: 1, type: 'linear', extrapolate: false, round: 100, }, transformTranslate: { - from: {x: 0, y: 50, z: 0}, + from: {x: 0, y: 100, z: 0}, to: {x: 0, y: 0, z: 0}, min: 0, max: 1, @@ -329,7 +329,7 @@ var FromTheFrontAndroid = { round: PixelRatio.get(), }, translateY: { - from: 50, + from: 100, to: 0, min: 0, max: 1, @@ -432,6 +432,8 @@ var NavigatorSceneConfigs = { FloatFromBottomAndroid: { ...BaseConfig, gestures: null, + defaultTransitionVelocity: 3, + springFriction: 20, animationInterpolators: { into: buildStyleInterpolator(FromTheFrontAndroid), out: buildStyleInterpolator(ToTheBackAndroid), From 61c1631cbd03145e5a0eaeef25e6d1278b0f51e4 Mon Sep 17 00:00:00 2001 From: Mark Edington Date: Thu, 11 Jun 2015 10:46:57 -0700 Subject: [PATCH 03/28] [Pods] Define a subspec for LinkingIOS Summary: Summary: Adds a CocoaPods subspec for LinkingIOS Discussed in issue #667 Closes https://github.com/facebook/react-native/pull/1586 Github Author: Mark Edington Test Plan: Pull in the subspec and run a sample which calls RCTLinkingManager.openURL(url); --- React.podspec | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/React.podspec b/React.podspec index 32f21edad..9e2d37ff9 100644 --- a/React.podspec +++ b/React.podspec @@ -103,4 +103,10 @@ Pod::Spec.new do |s| ss.source_files = "Libraries/WebSocket/*.{h,m}" ss.preserve_paths = "Libraries/WebSocket/*.js" end + + s.subspec 'RCTLinkingIOS' do |ss| + ss.dependency 'React/Core' + ss.source_files = "Libraries/LinkingIOS/*.{h,m}" + ss.preserve_paths = "Libraries/LinkingIOS/*.js" + end end From 2ee8410a718e0868c4d286b1cc5b5085dbf59d46 Mon Sep 17 00:00:00 2001 From: Nick Lockwood Date: Thu, 11 Jun 2015 10:42:00 -0700 Subject: [PATCH 04/28] Removed nullability attributes until Infer supports them --- Libraries/Image/RCTImageLoader.m | 2 +- Libraries/Network/RCTDataManager.m | 4 ++-- React/Base/RCTDefines.h | 15 --------------- 3 files changed, 3 insertions(+), 18 deletions(-) diff --git a/Libraries/Image/RCTImageLoader.m b/Libraries/Image/RCTImageLoader.m index f28502d7e..04fa17f5d 100644 --- a/Libraries/Image/RCTImageLoader.m +++ b/Libraries/Image/RCTImageLoader.m @@ -22,7 +22,7 @@ #import "RCTLog.h" #import "RCTUtils.h" -static void RCTDispatchCallbackOnMainQueue(void (^ __nonnull callback)(NSError *, id), NSError *error, UIImage *image) +static void RCTDispatchCallbackOnMainQueue(void (^callback)(NSError *, id), NSError *error, UIImage *image) { if ([NSThread isMainThread]) { callback(error, image); diff --git a/Libraries/Network/RCTDataManager.m b/Libraries/Network/RCTDataManager.m index a05e942fb..875d99f31 100644 --- a/Libraries/Network/RCTDataManager.m +++ b/Libraries/Network/RCTDataManager.m @@ -42,7 +42,7 @@ typedef void (^RCTHTTPQueryResult)(NSError *error, NSDictionary *result); NSString *boundary; } -- (void)process:(NSArray *)formData callback:(nonnull void (^)(NSError *error, NSDictionary *result))callback +- (void)process:(NSArray *)formData callback:(void (^)(NSError *error, NSDictionary *result))callback { if (![formData count]) { callback(nil, nil); @@ -298,7 +298,7 @@ RCT_EXPORT_MODULE() * - @"contentType" (NSString): the content type header of the request * */ -- (void)processDataForHTTPQuery:(NSDictionary *)query callback:(nonnull void (^)(NSError *error, NSDictionary *result))callback +- (void)processDataForHTTPQuery:(NSDictionary *)query callback:(void (^)(NSError *error, NSDictionary *result))callback { if (!query) { callback(nil, nil); diff --git a/React/Base/RCTDefines.h b/React/Base/RCTDefines.h index 33328789b..7c6d88409 100644 --- a/React/Base/RCTDefines.h +++ b/React/Base/RCTDefines.h @@ -18,21 +18,6 @@ #define RCT_EXTERN extern __attribute__((visibility("default"))) #endif -/** - * Nullability for Xcode 6.2 - */ -#if !__has_feature(nullability) -#define NS_ASSUME_NONNULL_BEGIN -#define NS_ASSUME_NONNULL_END -#define nullable -#define nonnull -#define null_unspecified -#define null_resettable -#define __nullable -#define __nonnull -#define __null_unspecified -#endif - /** * The RCT_DEBUG macro can be used to exclude error checking and logging code * from release builds to improve performance and reduce binary size. From 492e29a0919032e77e898dc0852773516ea2e6ea Mon Sep 17 00:00:00 2001 From: Spencer Ahrens Date: Thu, 11 Jun 2015 10:39:52 -0700 Subject: [PATCH 05/28] [ReactNative] backout orange box Summary: @public This probably needs more thought - might want to differentiate between console.error and reportError. Test Plan: console.error and reportError no longer redbox. invariant and RCTLogError still do. --- React/Base/RCTLog.m | 3 --- React/Modules/RCTExceptionsManager.m | 4 +--- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/React/Base/RCTLog.m b/React/Base/RCTLog.m index 9469d3015..d99bb4fe1 100644 --- a/React/Base/RCTLog.m +++ b/React/Base/RCTLog.m @@ -171,9 +171,6 @@ void _RCTLogFormat( // Log to red box if (level >= RCTLOG_REDBOX_LEVEL) { - if (level < RCTLOG_FATAL_LEVEL) { - [[RCTRedBox sharedInstance] setNextBackgroundColor:[UIColor colorWithRed:0.9 green:0.4 blue:0.2 alpha:1]]; - } [[RCTRedBox sharedInstance] showErrorMessage:message]; } diff --git a/React/Modules/RCTExceptionsManager.m b/React/Modules/RCTExceptionsManager.m index 4a9815f31..2894fac2c 100644 --- a/React/Modules/RCTExceptionsManager.m +++ b/React/Modules/RCTExceptionsManager.m @@ -44,9 +44,7 @@ RCT_EXPORT_METHOD(reportSoftException:(NSString *)message [_delegate handleSoftJSExceptionWithMessage:message stack:stack]; return; } - RCTRedBox *box = [RCTRedBox sharedInstance]; - [box setNextBackgroundColor:[UIColor colorWithRed:0.9 green:0.4 blue:0.2 alpha:1]]; - [box showErrorMessage:message withStack:stack]; + [[RCTRedBox sharedInstance] showErrorMessage:message withStack:stack]; } RCT_EXPORT_METHOD(reportFatalException:(NSString *)message From dcf15f84dcfab472b0e498a62a890b41783662bb Mon Sep 17 00:00:00 2001 From: Tadeu Zagallo Date: Thu, 11 Jun 2015 10:43:29 -0700 Subject: [PATCH 06/28] [ReactNative] Fix racing condition on RCTDataManager --- Libraries/Network/RCTDataManager.m | 78 ++++++++++++++++++------------ 1 file changed, 46 insertions(+), 32 deletions(-) diff --git a/Libraries/Network/RCTDataManager.m b/Libraries/Network/RCTDataManager.m index 875d99f31..05a51c169 100644 --- a/Libraries/Network/RCTDataManager.m +++ b/Libraries/Network/RCTDataManager.m @@ -202,6 +202,7 @@ typedef void (^RCTDataLoaderCallback)(NSData *data, NSString *MIMEType, NSError { NSInteger _currentRequestID; NSMapTable *_activeRequests; + dispatch_queue_t _methodQueue; } @synthesize bridge = _bridge; @@ -212,6 +213,7 @@ RCT_EXPORT_MODULE() { if ((self = [super init])) { _currentRequestID = 0; + _methodQueue = dispatch_queue_create("com.facebook.React.RCTDataManager", DISPATCH_QUEUE_SERIAL); _activeRequests = [[NSMapTable alloc] initWithKeyOptions:NSPointerFunctionsStrongMemory valueOptions:NSPointerFunctionsStrongMemory capacity:0]; @@ -219,6 +221,11 @@ RCT_EXPORT_MODULE() return self; } +- (dispatch_queue_t)methodQueue +{ + return _methodQueue; +} + - (void)buildRequest:(NSDictionary *)query responseSender:(RCTResponseSenderBlock)responseSender { @@ -381,55 +388,62 @@ RCT_EXPORT_MODULE() - (void)URLRequest:(id)requestToken didReceiveResponse:(NSURLResponse *)response { - RCTActiveURLRequest *request = [_activeRequests objectForKey:requestToken]; - RCTAssert(request != nil, @"Unrecognized request token: %@", requestToken); + dispatch_async(_methodQueue, ^{ + RCTActiveURLRequest *request = [_activeRequests objectForKey:requestToken]; + RCTAssert(request != nil, @"Unrecognized request token: %@", requestToken); - request.response = response; + request.response = response; - NSHTTPURLResponse *httpResponse = nil; - if ([response isKindOfClass:[NSHTTPURLResponse class]]) { - // Might be a local file request - httpResponse = (NSHTTPURLResponse *)response; - } + NSHTTPURLResponse *httpResponse = nil; + if ([response isKindOfClass:[NSHTTPURLResponse class]]) { + // Might be a local file request + httpResponse = (NSHTTPURLResponse *)response; + } - NSArray *responseJSON = @[request.requestID, - @(httpResponse.statusCode ?: 200), - httpResponse.allHeaderFields ?: @{}, - ]; + NSArray *responseJSON = @[request.requestID, + @(httpResponse.statusCode ?: 200), + httpResponse.allHeaderFields ?: @{}, + ]; - [_bridge.eventDispatcher sendDeviceEventWithName:@"didReceiveNetworkResponse" - body:responseJSON]; + [_bridge.eventDispatcher sendDeviceEventWithName:@"didReceiveNetworkResponse" + body:responseJSON]; + }); } - (void)URLRequest:(id)requestToken didReceiveData:(NSData *)data { - RCTActiveURLRequest *request = [_activeRequests objectForKey:requestToken]; - RCTAssert(request != nil, @"Unrecognized request token: %@", requestToken); + dispatch_async(_methodQueue, ^{ + RCTActiveURLRequest *request = [_activeRequests objectForKey:requestToken]; + RCTAssert(request != nil, @"Unrecognized request token: %@", requestToken); - if (request.incrementalUpdates) { - [self sendData:data forRequestToken:requestToken]; - } else { - [request.data appendData:data]; - } + if (request.incrementalUpdates) { + [self sendData:data forRequestToken:requestToken]; + } else { + [request.data appendData:data]; + } + }); } - (void)URLRequest:(id)requestToken didCompleteWithError:(NSError *)error { - RCTActiveURLRequest *request = [_activeRequests objectForKey:requestToken]; - RCTAssert(request != nil, @"Unrecognized request token: %@", requestToken); + dispatch_async(_methodQueue, ^{ + RCTActiveURLRequest *request = [_activeRequests objectForKey:requestToken]; + RCTAssert(request != nil, @"Unrecognized request token: %@", requestToken); - if (!request.incrementalUpdates) { - [self sendData:request.data forRequestToken:requestToken]; - } + if (!request.incrementalUpdates) { + [self sendData:request.data forRequestToken:requestToken]; + } - NSArray *responseJSON = @[request.requestID, - error.localizedDescription ?: [NSNull null] - ]; + NSArray *responseJSON = @[ + request.requestID, + error.localizedDescription ?: [NSNull null], + ]; - [_bridge.eventDispatcher sendDeviceEventWithName:@"didCompleteNetworkResponse" - body:responseJSON]; + [_bridge.eventDispatcher sendDeviceEventWithName:@"didCompleteNetworkResponse" + body:responseJSON]; - [_activeRequests removeObjectForKey:requestToken]; + [_activeRequests removeObjectForKey:requestToken]; + }); } #pragma mark - JS API From ae9e4089fc9120b87bc0dd47be6078927595fdc9 Mon Sep 17 00:00:00 2001 From: Tadeu Zagallo Date: Thu, 11 Jun 2015 10:43:15 -0700 Subject: [PATCH 07/28] [ReactNative] Revert packager ignoring node_modules --- package.json | 2 +- packager/packager.js | 4 ++++ packager/react-packager/src/FileWatcher/index.js | 1 - 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 19226bc08..08db6d6c1 100644 --- a/package.json +++ b/package.json @@ -60,7 +60,7 @@ "react-timer-mixin": "^0.13.1", "react-tools": "0.13.2", "rebound": "^0.0.12", - "sane": "tadeuzagallo/sane#a029f8b04a", + "sane": "^1.1.2", "source-map": "0.1.31", "stacktrace-parser": "frantic/stacktrace-parser#493c5e5638", "uglify-js": "~2.4.16", diff --git a/packager/packager.js b/packager/packager.js index 7c4be0a77..7dd013c67 100644 --- a/packager/packager.js +++ b/packager/packager.js @@ -53,6 +53,9 @@ var options = parseCommandLine([{ }, { command: 'skipflow', description: 'Disable flow checks' +}, { + command: 'nonPersistent', + description: 'Disable file watcher' }]); if (options.projectRoots) { @@ -199,6 +202,7 @@ function statusPageMiddleware(req, res, next) { function getAppMiddleware(options) { return ReactPackager.middleware({ + nonPersistent: options.nonPersistent, projectRoots: options.projectRoots, blacklistRE: blacklist(options.platform), cacheVersion: '2', diff --git a/packager/react-packager/src/FileWatcher/index.js b/packager/react-packager/src/FileWatcher/index.js index d90de452d..cd1a28e55 100644 --- a/packager/react-packager/src/FileWatcher/index.js +++ b/packager/react-packager/src/FileWatcher/index.js @@ -67,7 +67,6 @@ function createWatcher(rootConfig) { var watcher = new Watcher(rootConfig.dir, { glob: rootConfig.globs, dot: false, - ignore: '**/node_modules/**/*', }); return new Promise(function(resolve, reject) { From 04b3b527268bc4038bc911a5d234240841baaba0 Mon Sep 17 00:00:00 2001 From: Adam Krell Date: Thu, 11 Jun 2015 10:46:28 -0700 Subject: [PATCH 08/28] Add map type property (standard, satellite, hybrid) to MapView. Summary: Here is an example project demonstrating this pull request: [AKMapView](https://github.com/adamkrell/AKMapView) Closes https://github.com/facebook/react-native/pull/1503 Github Author: Adam Krell Test Plan: Imported from GitHub, without a `Test Plan:` line. --- Libraries/Components/MapView/MapView.js | 13 +++++++++++++ React/Views/RCTConvert+MapKit.h | 1 + React/Views/RCTConvert+MapKit.m | 6 ++++++ React/Views/RCTMapManager.m | 1 + 4 files changed, 21 insertions(+) diff --git a/Libraries/Components/MapView/MapView.js b/Libraries/Components/MapView/MapView.js index 3e387835c..50e36954b 100644 --- a/Libraries/Components/MapView/MapView.js +++ b/Libraries/Components/MapView/MapView.js @@ -82,6 +82,19 @@ var MapView = React.createClass({ */ scrollEnabled: React.PropTypes.bool, + /** + * The map type to be displayed. + * + * - standard: standard road map (default) + * - satellite: satellite view + * - hybrid: satellite view with roads and points of interest overlayed + */ + mapType: React.PropTypes.oneOf([ + 'standard', + 'satellite', + 'hybrid', + ]), + /** * The region to be displayed by the map. * diff --git a/React/Views/RCTConvert+MapKit.h b/React/Views/RCTConvert+MapKit.h index 8ad9316a1..d4bf8d2d7 100644 --- a/React/Views/RCTConvert+MapKit.h +++ b/React/Views/RCTConvert+MapKit.h @@ -15,6 +15,7 @@ + (MKCoordinateSpan)MKCoordinateSpan:(id)json; + (MKCoordinateRegion)MKCoordinateRegion:(id)json; + (MKShape *)MKShape:(id)json; ++ (MKMapType)MKMapType:(id)json; typedef NSArray MKShapeArray; + (MKShapeArray *)MKShapeArray:(id)json; diff --git a/React/Views/RCTConvert+MapKit.m b/React/Views/RCTConvert+MapKit.m index cd6c9fb41..6dc541a46 100644 --- a/React/Views/RCTConvert+MapKit.m +++ b/React/Views/RCTConvert+MapKit.m @@ -43,4 +43,10 @@ RCT_ARRAY_CONVERTER(MKShape) +RCT_ENUM_CONVERTER(MKMapType, (@{ + @"standard": @(MKMapTypeStandard), + @"satellite": @(MKMapTypeSatellite), + @"hybrid": @(MKMapTypeHybrid), +}), MKMapTypeStandard, integerValue) + @end diff --git a/React/Views/RCTMapManager.m b/React/Views/RCTMapManager.m index 8d334f60b..7a9401fdd 100644 --- a/React/Views/RCTMapManager.m +++ b/React/Views/RCTMapManager.m @@ -41,6 +41,7 @@ RCT_EXPORT_VIEW_PROPERTY(scrollEnabled, BOOL) RCT_EXPORT_VIEW_PROPERTY(maxDelta, CGFloat) RCT_EXPORT_VIEW_PROPERTY(minDelta, CGFloat) RCT_EXPORT_VIEW_PROPERTY(legalLabelInsets, UIEdgeInsets) +RCT_EXPORT_VIEW_PROPERTY(mapType, MKMapType) RCT_EXPORT_VIEW_PROPERTY(annotations, MKShapeArray) RCT_CUSTOM_VIEW_PROPERTY(region, MKCoordinateRegion, RCTMap) { From 2880f1aec02848196e676156c02df444e95887a6 Mon Sep 17 00:00:00 2001 From: Prathamesh Sonpatki Date: Thu, 11 Jun 2015 11:06:33 -0700 Subject: [PATCH 09/28] Fixed arguments to StatusBarIOS.setStyle in the NavigatorIOSColorExample Summary: - Followup of https://github.com/facebook/react-native/commit/45d8fb0ef64a8a41929d61fe822a64c2febe5455 Closes https://github.com/facebook/react-native/pull/1558 Github Author: Prathamesh Sonpatki Test Plan: Imported from GitHub, without a `Test Plan:` line. --- Examples/UIExplorer/AccessibilityIOSExample.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Examples/UIExplorer/AccessibilityIOSExample.js b/Examples/UIExplorer/AccessibilityIOSExample.js index c22c349bb..1a86e5459 100644 --- a/Examples/UIExplorer/AccessibilityIOSExample.js +++ b/Examples/UIExplorer/AccessibilityIOSExample.js @@ -55,7 +55,7 @@ var AccessibilityIOSExample = React.createClass({ }, }); -exports.title = 'AcccessibilityIOS'; +exports.title = 'AccessibilityIOS'; exports.description = 'Interface to show iOS\' accessibility samples'; exports.examples = [ { From dca0192b4b10ac75615561ecd4c9aab96b4ef85b Mon Sep 17 00:00:00 2001 From: Philipp von Weitershausen Date: Wed, 10 Jun 2015 13:16:16 -0700 Subject: [PATCH 10/28] [ReactNative] XHR FormData upload example --- .../UIExplorer.xcodeproj/project.pbxproj | 30 +++ Examples/UIExplorer/XHRExample.js | 232 +++++++++++++++++- 2 files changed, 260 insertions(+), 2 deletions(-) diff --git a/Examples/UIExplorer/UIExplorer.xcodeproj/project.pbxproj b/Examples/UIExplorer/UIExplorer.xcodeproj/project.pbxproj index 1976634a6..28361d706 100644 --- a/Examples/UIExplorer/UIExplorer.xcodeproj/project.pbxproj +++ b/Examples/UIExplorer/UIExplorer.xcodeproj/project.pbxproj @@ -45,6 +45,7 @@ 14D6D7281B2222EF001FB087 /* libRCTWebSocket.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 139FDED91B0651EA00C62182 /* libRCTWebSocket.a */; }; 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 */; }; 834C36EC1AF8DED70019C93C /* libRCTSettings.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 834C36D21AF8DA610019C93C /* libRCTSettings.a */; }; D85B829E1AB6D5D7003F4FE2 /* libRCTVibration.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D85B829C1AB6D5CE003F4FE2 /* libRCTVibration.a */; }; /* End PBXBuildFile section */ @@ -120,6 +121,13 @@ remoteGlobalIDString = 134814201AA4EA6300B7C361; remoteInfo = RCTPushNotification; }; + 357859001B28D2C500341EDB /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 357858F81B28D2C400341EDB /* RCTLinking.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 134814201AA4EA6300B7C361; + remoteInfo = RCTLinking; + }; 58005BED1ABA80530062E044 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 58005BE41ABA80530062E044 /* RCTTest.xcodeproj */; @@ -192,6 +200,7 @@ 14D6D7101B220EB3001FB087 /* libOCMock.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libOCMock.a; sourceTree = ""; }; 14DC67E71AB71876001358AB /* RCTPushNotification.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTPushNotification.xcodeproj; path = ../../Libraries/PushNotificationIOS/RCTPushNotification.xcodeproj; sourceTree = ""; }; 14E0EEC81AB118F7000DECC3 /* RCTActionSheet.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTActionSheet.xcodeproj; path = ../../Libraries/ActionSheetIOS/RCTActionSheet.xcodeproj; sourceTree = ""; }; + 357858F81B28D2C400341EDB /* RCTLinking.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTLinking.xcodeproj; path = ../../Libraries/LinkingIOS/RCTLinking.xcodeproj; sourceTree = ""; }; 58005BE41ABA80530062E044 /* RCTTest.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTTest.xcodeproj; path = ../../Libraries/RCTTest/RCTTest.xcodeproj; sourceTree = ""; }; D85B82911AB6D5CE003F4FE2 /* RCTVibration.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTVibration.xcodeproj; path = ../../Libraries/Vibration/RCTVibration.xcodeproj; sourceTree = ""; }; /* End PBXFileReference section */ @@ -226,6 +235,7 @@ 134454601AAFCABD003F0779 /* libRCTAdSupport.a in Frameworks */, 134A8A2A1AACED7A00945AAE /* libRCTGeolocation.a in Frameworks */, 13417FE91AA91432003F314A /* libRCTImage.a in Frameworks */, + 3578590A1B28D2CF00341EDB /* libRCTLinking.a in Frameworks */, 1341802C1AA9178B003F314A /* libRCTNetwork.a in Frameworks */, 14DC67F41AB71881001358AB /* libRCTPushNotification.a in Frameworks */, 834C36EC1AF8DED70019C93C /* libRCTSettings.a in Frameworks */, @@ -255,6 +265,7 @@ 134454551AAFCAAE003F0779 /* RCTAdSupport.xcodeproj */, 134A8A201AACED6A00945AAE /* RCTGeolocation.xcodeproj */, 13417FE31AA91428003F314A /* RCTImage.xcodeproj */, + 357858F81B28D2C400341EDB /* RCTLinking.xcodeproj */, 134180261AA91779003F314A /* RCTNetwork.xcodeproj */, 13CC9D481AEED2B90020D1C2 /* RCTSettings.xcodeproj */, 58005BE41ABA80530062E044 /* RCTTest.xcodeproj */, @@ -432,6 +443,14 @@ name = Products; sourceTree = ""; }; + 357858F91B28D2C400341EDB /* Products */ = { + isa = PBXGroup; + children = ( + 357859011B28D2C500341EDB /* libRCTLinking.a */, + ); + name = Products; + sourceTree = ""; + }; 58005BE51ABA80530062E044 /* Products */ = { isa = PBXGroup; children = ( @@ -581,6 +600,10 @@ ProductGroup = 13417FE41AA91428003F314A /* Products */; ProjectRef = 13417FE31AA91428003F314A /* RCTImage.xcodeproj */; }, + { + ProductGroup = 357858F91B28D2C400341EDB /* Products */; + ProjectRef = 357858F81B28D2C400341EDB /* RCTLinking.xcodeproj */; + }, { ProductGroup = 134180271AA91779003F314A /* Products */; ProjectRef = 134180261AA91779003F314A /* RCTNetwork.xcodeproj */; @@ -687,6 +710,13 @@ remoteRef = 14DC67F01AB71876001358AB /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; + 357859011B28D2C500341EDB /* libRCTLinking.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libRCTLinking.a; + remoteRef = 357859001B28D2C500341EDB /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; 58005BEE1ABA80530062E044 /* libRCTTest.a */ = { isa = PBXReferenceProxy; fileType = archive.ar; diff --git a/Examples/UIExplorer/XHRExample.js b/Examples/UIExplorer/XHRExample.js index c5b350c70..db172efc1 100644 --- a/Examples/UIExplorer/XHRExample.js +++ b/Examples/UIExplorer/XHRExample.js @@ -17,11 +17,17 @@ var React = require('react-native'); var { + AlertIOS, + CameraRoll, + Image, + LinkingIOS, + PixelRatio, ProgressViewIOS, StyleSheet, - View, Text, + TextInput, TouchableHighlight, + View, } = React; class Downloader extends React.Component { @@ -109,6 +115,177 @@ class Downloader extends React.Component { } } +var PAGE_SIZE = 20; + +class FormUploader extends React.Component { + + constructor(props) { + super(props); + this.state = { + isUploading: false, + randomPhoto: null, + textParams: [], + }; + this._isMounted = true; + this._fetchRandomPhoto = this._fetchRandomPhoto.bind(this); + this._addTextParam = this._addTextParam.bind(this); + this._upload = this._upload.bind(this); + + this._fetchRandomPhoto(); + } + + _fetchRandomPhoto() { + CameraRoll.getPhotos( + {first: PAGE_SIZE}, + (data) => { + console.log('isMounted', this._isMounted); + if (!this._isMounted) { + return; + } + var edges = data.edges; + var edge = edges[Math.floor(Math.random() * edges.length)]; + var randomPhoto = edge && edge.node && edge.node.image; + if (randomPhoto) { + this.setState({randomPhoto}); + } + }, + (error) => undefined + ); + } + + _addTextParam() { + var textParams = this.state.textParams; + textParams.push({name: '', value: ''}); + this.setState({textParams}); + } + + componentWillUnmount() { + this._isMounted = false; + } + + _onTextParamNameChange(index, text) { + var textParams = this.state.textParams; + textParams[index].name = text; + this.setState({textParams}); + } + + _onTextParamValueChange(index, text) { + var textParams = this.state.textParams; + textParams[index].value = text; + this.setState({textParams}); + } + + _upload() { + var xhr = new XMLHttpRequest(); + xhr.open('POST', 'http://posttestserver.com/post.php'); + xhr.onload = () => { + this.setState({isUploading: false}); + if (xhr.status !== 200) { + AlertIOS.alert( + 'Upload failed', + 'Expected HTTP 200 OK response, got ' + xhr.status + ); + return; + } + if (!xhr.responseText) { + AlertIOS.alert( + 'Upload failed', + 'No response payload.' + ); + return; + } + var index = xhr.responseText.indexOf('http://www.posttestserver.com/'); + if (index === -1) { + AlertIOS.alert( + 'Upload failed', + 'Invalid response payload.' + ); + return; + } + var url = xhr.responseText.slice(index).split('\n')[0]; + LinkingIOS.openURL(url); + }; + var formdata = new FormData(); + if (this.state.randomPhoto) { + formdata.append('image', {...this.state.randomPhoto, name: 'image.jpg'}); + } + this.state.textParams.forEach( + (param) => formdata.append(param.name, param.value) + ); + xhr.send(formdata); + this.setState({isUploading: true}); + } + + render() { + var image = null; + if (this.state.randomPhoto) { + image = ( + + ); + } + var textItems = this.state.textParams.map((item, index) => ( + + + = + + + )); + var uploadButtonLabel = this.state.isUploading ? 'Uploading...' : 'Upload'; + var uploadButton = ( + + {uploadButtonLabel} + + ); + if (!this.state.isUploading) { + uploadButton = ( + + {uploadButton} + + ); + } + return ( + + + + Random photo from your library + ( + update + ) + + {image} + + {textItems} + + + Add a text param + + + + {uploadButton} + + + ); + } +} + + exports.framework = 'React'; exports.title = 'XMLHttpRequest'; exports.description = 'XMLHttpRequest'; @@ -117,6 +294,11 @@ exports.examples = [{ render() { return ; } +}, { + title: 'multipart/form-data Upload', + render() { + return ; + } }]; var styles = StyleSheet.create({ @@ -126,6 +308,52 @@ var styles = StyleSheet.create({ }, button: { backgroundColor: '#eeeeee', - padding: 10, + padding: 8, + }, + paramRow: { + flexDirection: 'row', + paddingVertical: 8, + alignItems: 'center', + borderBottomWidth: 1 / PixelRatio.get(), + borderBottomColor: 'grey', + }, + photoLabel: { + flex: 1, + }, + randomPhoto: { + width: 50, + height: 50, + }, + textButton: { + color: 'blue', + }, + addTextParamButton: { + marginTop: 8, + }, + textInput: { + flex: 1, + borderRadius: 3, + borderColor: 'grey', + borderWidth: 1, + height: 30, + paddingLeft: 8, + }, + equalSign: { + paddingHorizontal: 4, + }, + uploadButton: { + marginTop: 16, + }, + uploadButtonBox: { + flex: 1, + paddingVertical: 12, + alignItems: 'center', + backgroundColor: 'blue', + borderRadius: 4, + }, + uploadButtonLabel: { + color: 'white', + fontSize: 16, + fontWeight: '500', }, }); From f0bba0c111363e9cbbedc463f3f7d4ed7657d32d Mon Sep 17 00:00:00 2001 From: Jiajie Zhu Date: Thu, 11 Jun 2015 13:21:57 -0700 Subject: [PATCH 11/28] [RN|madman] init AppStateIOS.currentState with 'active' --- Libraries/AppStateIOS/AppStateIOS.ios.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Libraries/AppStateIOS/AppStateIOS.ios.js b/Libraries/AppStateIOS/AppStateIOS.ios.js index e123d23be..f7dddcd57 100644 --- a/Libraries/AppStateIOS/AppStateIOS.ios.js +++ b/Libraries/AppStateIOS/AppStateIOS.ios.js @@ -122,7 +122,11 @@ var AppStateIOS = { _eventHandlers[type].delete(handler); }, - currentState: (null : ?String), + // TODO: getCurrentAppState callback seems to be called at a really late stage + // after app launch. Trying to get currentState when mounting App component + // will likely to have the initial value here. + // Initialize to 'active' instead of null. + currentState: ('active' : ?string), }; From 1b9067a3e3b5f5b7d3242b3b26a3b921e623e628 Mon Sep 17 00:00:00 2001 From: Eric Vicenti Date: Thu, 11 Jun 2015 13:34:38 -0700 Subject: [PATCH 12/28] [ReactNative] PushNotificationIOS listener Map --- .../PushNotificationIOS/PushNotificationIOS.js | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/Libraries/PushNotificationIOS/PushNotificationIOS.js b/Libraries/PushNotificationIOS/PushNotificationIOS.js index fa2be650a..adf8ecf5a 100644 --- a/Libraries/PushNotificationIOS/PushNotificationIOS.js +++ b/Libraries/PushNotificationIOS/PushNotificationIOS.js @@ -11,11 +11,12 @@ */ 'use strict'; +var Map = require('Map'); var RCTDeviceEventEmitter = require('RCTDeviceEventEmitter'); var RCTPushNotificationManager = require('NativeModules').PushNotificationManager; var invariant = require('invariant'); -var _notifHandlers = {}; +var _notifHandlers = new Map(); var _initialNotification = RCTPushNotificationManager && RCTPushNotificationManager.initialNotification; @@ -65,21 +66,23 @@ class PushNotificationIOS { type === 'notification' || type === 'register', 'PushNotificationIOS only supports `notification` and `register` events' ); + var listener; if (type === 'notification') { - _notifHandlers[String(handler)] = RCTDeviceEventEmitter.addListener( + listener = RCTDeviceEventEmitter.addListener( DEVICE_NOTIF_EVENT, (notifData) => { handler(new PushNotificationIOS(notifData)); } ); } else if (type === 'register') { - _notifHandlers[String(handler)] = RCTDeviceEventEmitter.addListener( + listener = RCTDeviceEventEmitter.addListener( NOTIF_REGISTER_EVENT, (registrationInfo) => { handler(registrationInfo.deviceToken); } ); } + _notifHandlers.set(handler, listener); } /** @@ -143,11 +146,12 @@ class PushNotificationIOS { type === 'notification' || type === 'register', 'PushNotificationIOS only supports `notification` and `register` events' ); - if (!_notifHandlers[String(handler)]) { + var listener = _notifHandlers.get(handler); + if (!listener) { return; } - _notifHandlers[String(handler)].remove(); - _notifHandlers[String(handler)] = null; + listener.remove(); + _notifHandlers.delete(handler); } From 15907419f3ed7197eb77f1ebc885e429f0ac15ec Mon Sep 17 00:00:00 2001 From: Jared Forsyth Date: Thu, 11 Jun 2015 13:50:48 -0700 Subject: [PATCH 13/28] [ReactNative] refactor the inspector Summary: The `InspectorOverlay` component was getting unwieldy, so I broke it into three components: - Inspector - InspectorOverlay - InspectorPanel and added @flow types. The inspector was also living under the `ReactIOS` directory, and I moved it up into the `Libraries` directory, as the inspector will soon be usable [on Android](https://phabricator.fb.com/D2138319). All features of the inspector should remain functional, with the addition of one feature: - you can toggle "touch to inspect" by tapping the "Inspect" button at the bottom of the inspection panel. When inspection is disabled, the panel remains, but you can interact with the app normally without touches being intercepted @public Test Plan: Open the inspector: - touch to inspect things, verify that margin, padding, size and position are reported correctly, and that the component hierarchy is navigable. - tap the "Inspect" button, and verify that you can interact with the app normally. {F22548949} [Video of toggling inspection](https://www.latest.facebook.com/pxlcld/mrs9) --- .../BorderBox.js | 0 .../BoxInspector.js | 0 .../ElementBox.js | 0 .../ElementProperties.js | 9 +- Libraries/Inspector/Inspector.js | 115 +++++++++++++++ Libraries/Inspector/InspectorOverlay.js | 81 +++++++++++ Libraries/Inspector/InspectorPanel.js | 119 ++++++++++++++++ .../InspectorUtils.js} | 2 +- .../StyleInspector.js | 0 .../resolveBoxStyle.js | 0 .../InspectorOverlay/InspectorOverlay.js | 132 ------------------ Libraries/ReactIOS/renderApplication.ios.js | 4 +- 12 files changed, 322 insertions(+), 140 deletions(-) rename Libraries/{ReactIOS/InspectorOverlay => Inspector}/BorderBox.js (100%) rename Libraries/{ReactIOS/InspectorOverlay => Inspector}/BoxInspector.js (100%) rename Libraries/{ReactIOS/InspectorOverlay => Inspector}/ElementBox.js (100%) rename Libraries/{ReactIOS/InspectorOverlay => Inspector}/ElementProperties.js (98%) create mode 100644 Libraries/Inspector/Inspector.js create mode 100644 Libraries/Inspector/InspectorOverlay.js create mode 100644 Libraries/Inspector/InspectorPanel.js rename Libraries/{Inspector.js => Inspector/InspectorUtils.js} (98%) rename Libraries/{ReactIOS/InspectorOverlay => Inspector}/StyleInspector.js (100%) rename Libraries/{ReactIOS/InspectorOverlay => Inspector}/resolveBoxStyle.js (100%) delete mode 100644 Libraries/ReactIOS/InspectorOverlay/InspectorOverlay.js diff --git a/Libraries/ReactIOS/InspectorOverlay/BorderBox.js b/Libraries/Inspector/BorderBox.js similarity index 100% rename from Libraries/ReactIOS/InspectorOverlay/BorderBox.js rename to Libraries/Inspector/BorderBox.js diff --git a/Libraries/ReactIOS/InspectorOverlay/BoxInspector.js b/Libraries/Inspector/BoxInspector.js similarity index 100% rename from Libraries/ReactIOS/InspectorOverlay/BoxInspector.js rename to Libraries/Inspector/BoxInspector.js diff --git a/Libraries/ReactIOS/InspectorOverlay/ElementBox.js b/Libraries/Inspector/ElementBox.js similarity index 100% rename from Libraries/ReactIOS/InspectorOverlay/ElementBox.js rename to Libraries/Inspector/ElementBox.js diff --git a/Libraries/ReactIOS/InspectorOverlay/ElementProperties.js b/Libraries/Inspector/ElementProperties.js similarity index 98% rename from Libraries/ReactIOS/InspectorOverlay/ElementProperties.js rename to Libraries/Inspector/ElementProperties.js index 310374fb1..855423845 100644 --- a/Libraries/ReactIOS/InspectorOverlay/ElementProperties.js +++ b/Libraries/Inspector/ElementProperties.js @@ -11,15 +11,15 @@ */ 'use strict'; +var BoxInspector = require('BoxInspector'); +var PropTypes = require('ReactPropTypes'); var React = require('React'); +var StyleInspector = require('StyleInspector'); var StyleSheet = require('StyleSheet'); var Text = require('Text'); -var View = require('View'); -var PropTypes = require('ReactPropTypes'); -var BoxInspector = require('BoxInspector'); -var StyleInspector = require('StyleInspector'); var TouchableHighlight = require('TouchableHighlight'); var TouchableWithoutFeedback = require('TouchableWithoutFeedback'); +var View = require('View'); var flattenStyle = require('flattenStyle'); var mapWithSeparator = require('mapWithSeparator'); @@ -93,7 +93,6 @@ var styles = StyleSheet.create({ justifyContent: 'space-between', }, info: { - backgroundColor: 'rgba(0, 0, 0, 0.7)', padding: 10, }, path: { diff --git a/Libraries/Inspector/Inspector.js b/Libraries/Inspector/Inspector.js new file mode 100644 index 000000000..46615d967 --- /dev/null +++ b/Libraries/Inspector/Inspector.js @@ -0,0 +1,115 @@ +/** + * 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 Inspector + * @flow + */ +'use strict'; + +var Dimensions = require('Dimensions'); +var InspectorOverlay = require('InspectorOverlay'); +var InspectorPanel = require('InspectorPanel'); +var InspectorUtils = require('InspectorUtils'); +var React = require('React'); +var StyleSheet = require('StyleSheet'); +var UIManager = require('NativeModules').UIManager; +var View = require('View'); + +class Inspector extends React.Component { + constructor(props: Object) { + super(props); + this.state = { + panelPos: 'bottom', + inspecting: true, + inspected: null, + }; + } + + setSelection(i: number) { + var instance = this.state.hierarchy[i]; + var publicInstance = instance.getPublicInstance(); + UIManager.measure(React.findNodeHandle(instance), (x, y, width, height, left, top) => { + this.setState({ + inspected: { + frame: {left, top, width, height}, + style: publicInstance.props ? publicInstance.props.style : {}, + }, + selection: i, + }); + }); + } + + onTouchInstance(instance: Object, frame: Object, pointerY: number) { + var hierarchy = InspectorUtils.getOwnerHierarchy(instance); + var publicInstance = instance.getPublicInstance(); + var props = publicInstance.props || {}; + this.setState({ + panelPos: pointerY > Dimensions.get('window').height / 2 ? 'top' : 'bottom', + selection: hierarchy.length - 1, + hierarchy, + inspected: { + style: props.style || {}, + frame, + }, + }); + } + + setInspecting(val: bool) { + this.setState({ + inspecting: val, + }); + } + + render() { + var panelPosition; + if (this.state.panelPos === 'bottom') { + panelPosition = {bottom: -Dimensions.get('window').height}; + } else { + panelPosition = {top: 0}; + } + return ( + + {this.state.inspecting && + } + + + + + ); + } +} + +var styles = StyleSheet.create({ + container: { + position: 'absolute', + backgroundColor: 'transparent', + top: 0, + left: 0, + right: 0, + height: 0, + }, + panelContainer: { + position: 'absolute', + left: 0, + right: 0, + }, +}); + +module.exports = Inspector; diff --git a/Libraries/Inspector/InspectorOverlay.js b/Libraries/Inspector/InspectorOverlay.js new file mode 100644 index 000000000..cf9dd8d68 --- /dev/null +++ b/Libraries/Inspector/InspectorOverlay.js @@ -0,0 +1,81 @@ +/** + * 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 InspectorOverlay + * @flow + */ +'use strict'; + +var Dimensions = require('Dimensions'); +var InspectorUtils = require('InspectorUtils'); +var React = require('React'); +var StyleSheet = require('StyleSheet'); +var UIManager = require('NativeModules').UIManager; +var View = require('View'); +var ElementBox = require('ElementBox'); + +var PropTypes = React.PropTypes; + +type EventLike = { + nativeEvent: Object; +}; + +var InspectorOverlay = React.createClass({ + propTypes: { + inspectedViewTag: PropTypes.object, + onTouchInstance: PropTypes.func.isRequired, + }, + + findViewForTouchEvent: function(e: EventLike) { + var {locationX, locationY} = e.nativeEvent.touches[0]; + UIManager.findSubviewIn( + this.props.inspectedViewTag, + [locationX, locationY], + (nativeViewTag, left, top, width, height) => { + var instance = InspectorUtils.findInstanceByNativeTag(this.props.rootTag, nativeViewTag); + if (!instance) { + return; + } + this.props.onTouchInstance(instance, {left, top, width, height}, locationY); + } + ); + }, + + shouldSetResponser: function(e: EventLike): bool { + this.findViewForTouchEvent(e); + return true; + }, + + render: function() { + var content = null; + if (this.props.inspected) { + content = ; + } + + return ( + + {content} + + ); + } +}); + +var styles = StyleSheet.create({ + inspector: { + backgroundColor: 'transparent', + position: 'absolute', + left: 0, + top: 0, + right: 0, + }, +}); + +module.exports = InspectorOverlay; diff --git a/Libraries/Inspector/InspectorPanel.js b/Libraries/Inspector/InspectorPanel.js new file mode 100644 index 000000000..7f49f19dc --- /dev/null +++ b/Libraries/Inspector/InspectorPanel.js @@ -0,0 +1,119 @@ +/** + * 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 InspectorPanel + * @flow + */ +'use strict'; + +var React = require('React'); +var StyleSheet = require('StyleSheet'); +var Text = require('Text'); +var View = require('View'); +var ElementProperties = require('ElementProperties'); +var TouchableHighlight = require('TouchableHighlight'); + +var PropTypes = React.PropTypes; + +class InspectorPanel extends React.Component { + renderWaiting() { + if (this.props.inspecting) { + return ( + + Tap something to inspect it + + ); + } + return Nothing is inspected; + } + + render() { + var contents; + if (this.props.inspected) { + contents = ( + + ); + } else { + contents = ( + + {this.renderWaiting()} + + ); + } + return ( + + {contents} + +