From 1acca01aa44d5d2533dd3037428a9c1fdc5a7c91 Mon Sep 17 00:00:00 2001 From: Gabe Levi Date: Wed, 10 Jun 2015 13:42:41 -0700 Subject: [PATCH 01/24] [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 a9f4069cedc192a7539634153de7d3a73d8f7725 Mon Sep 17 00:00:00 2001 From: Eric Vicenti Date: Wed, 10 Jun 2015 23:37:27 -0700 Subject: [PATCH 02/24] [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 a7aa336c6e41e0a5bb1805c66a461e55ee35138c Mon Sep 17 00:00:00 2001 From: Mark Edington Date: Thu, 11 Jun 2015 10:46:57 -0700 Subject: [PATCH 03/24] [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 792308e03..c7bfbbd7c 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 34f317695ccf3360978784b0b4ea732488205a83 Mon Sep 17 00:00:00 2001 From: Nick Lockwood Date: Thu, 11 Jun 2015 10:42:00 -0700 Subject: [PATCH 04/24] 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 7b7e0bf881dd82f14e3cc840782a688734e008a4 Mon Sep 17 00:00:00 2001 From: Spencer Ahrens Date: Thu, 11 Jun 2015 10:39:52 -0700 Subject: [PATCH 05/24] [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 6aea50d6f83019f68455edd00f4af00172a3d224 Mon Sep 17 00:00:00 2001 From: Tadeu Zagallo Date: Thu, 11 Jun 2015 10:43:29 -0700 Subject: [PATCH 06/24] [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 8d6d0ec4a4e5dfe107b3453745cd17fb43c8b05b Mon Sep 17 00:00:00 2001 From: Tadeu Zagallo Date: Thu, 11 Jun 2015 10:43:15 -0700 Subject: [PATCH 07/24] [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 373854a47f5470c2d7dc408dc2df8c215ea1fc8c Mon Sep 17 00:00:00 2001 From: Adam Krell Date: Thu, 11 Jun 2015 10:46:28 -0700 Subject: [PATCH 08/24] 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 f500a81afe2f1db1f561dd7433b371ffc95e3ee7 Mon Sep 17 00:00:00 2001 From: Prathamesh Sonpatki Date: Thu, 11 Jun 2015 11:06:33 -0700 Subject: [PATCH 09/24] 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 628609a0691335a3542e6ee069fc16b17c57b3f9 Mon Sep 17 00:00:00 2001 From: Philipp von Weitershausen Date: Wed, 10 Jun 2015 13:16:16 -0700 Subject: [PATCH 10/24] [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 7c0df4605abeb7a9cde992b73a8b599589d30115 Mon Sep 17 00:00:00 2001 From: Jiajie Zhu Date: Thu, 11 Jun 2015 13:21:57 -0700 Subject: [PATCH 11/24] [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 ca33cbd4b95ba3a8d0381bc1740e1feb8a2ff73a Mon Sep 17 00:00:00 2001 From: Eric Vicenti Date: Thu, 11 Jun 2015 13:34:38 -0700 Subject: [PATCH 12/24] [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 51e76fd166ef08efc4c6288297b61aa4e6bdbc0c Mon Sep 17 00:00:00 2001 From: Jared Forsyth Date: Thu, 11 Jun 2015 13:50:48 -0700 Subject: [PATCH 13/24] [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} + +