Updates for Fri 10 Jul

This commit is contained in:
Nick Lockwood 2015-07-10 17:48:12 +01:00
commit 7fa08e5c3f
34 changed files with 567 additions and 127 deletions

View File

@ -130,10 +130,11 @@ var TouchableFeedbackEvents = React.createClass({
}, },
render: function() { render: function() {
return ( return (
<View> <View testID="touchable_feedback_events">
<View style={[styles.row, {justifyContent: 'center'}]}> <View style={[styles.row, {justifyContent: 'center'}]}>
<TouchableOpacity <TouchableOpacity
style={styles.wrapper} style={styles.wrapper}
testID="touchable_feedback_events_button"
onPress={() => this._appendEvent('press')} onPress={() => this._appendEvent('press')}
onPressIn={() => this._appendEvent('pressIn')} onPressIn={() => this._appendEvent('pressIn')}
onPressOut={() => this._appendEvent('pressOut')} onPressOut={() => this._appendEvent('pressOut')}
@ -143,7 +144,7 @@ var TouchableFeedbackEvents = React.createClass({
</Text> </Text>
</TouchableOpacity> </TouchableOpacity>
</View> </View>
<View style={styles.eventLogBox}> <View testID="touchable_feedback_events_console" style={styles.eventLogBox}>
{this.state.eventLog.map((e, ii) => <Text key={ii}>{e}</Text>)} {this.state.eventLog.map((e, ii) => <Text key={ii}>{e}</Text>)}
</View> </View>
</View> </View>
@ -165,10 +166,11 @@ var TouchableDelayEvents = React.createClass({
}, },
render: function() { render: function() {
return ( return (
<View> <View testID="touchable_delay_events">
<View style={[styles.row, {justifyContent: 'center'}]}> <View style={[styles.row, {justifyContent: 'center'}]}>
<TouchableOpacity <TouchableOpacity
style={styles.wrapper} style={styles.wrapper}
testID="touchable_delay_events_button"
onPress={() => this._appendEvent('press')} onPress={() => this._appendEvent('press')}
delayPressIn={400} delayPressIn={400}
onPressIn={() => this._appendEvent('pressIn - 400ms delay')} onPressIn={() => this._appendEvent('pressIn - 400ms delay')}
@ -181,7 +183,7 @@ var TouchableDelayEvents = React.createClass({
</Text> </Text>
</TouchableOpacity> </TouchableOpacity>
</View> </View>
<View style={styles.eventLogBox}> <View style={styles.eventLogBox} testID="touchable_delay_events_console">
{this.state.eventLog.map((e, ii) => <Text key={ii}>{e}</Text>)} {this.state.eventLog.map((e, ii) => <Text key={ii}>{e}</Text>)}
</View> </View>
</View> </View>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 72 KiB

After

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 87 KiB

After

Width:  |  Height:  |  Size: 86 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 265 KiB

After

Width:  |  Height:  |  Size: 266 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 95 KiB

After

Width:  |  Height:  |  Size: 97 KiB

View File

@ -190,6 +190,7 @@ class UIExplorerList extends React.Component {
onChangeText={this._search.bind(this)} onChangeText={this._search.bind(this)}
placeholder="Search..." placeholder="Search..."
style={[styles.searchTextInput, platformTextInputStyle]} style={[styles.searchTextInput, platformTextInputStyle]}
testID="explorer_search"
value={this.state.searchText} value={this.state.searchText}
/> />
</View>); </View>);

View File

@ -14,25 +14,25 @@
XCTAssertEqualObjects(font1, font2); \ XCTAssertEqualObjects(font1, font2); \
} }
- (void)DISABLED_testWeight // task #7118691 - (void)testWeight
{ {
{ {
UIFont *expected = [UIFont fontWithName:@"HelveticaNeue-Bold" size:14]; UIFont *expected = [UIFont systemFontOfSize:14 weight:UIFontWeightBold];
UIFont *result = [RCTConvert UIFont:@{@"fontWeight": @"bold"}]; UIFont *result = [RCTConvert UIFont:@{@"fontWeight": @"bold"}];
RCTAssertEqualFonts(expected, result); RCTAssertEqualFonts(expected, result);
} }
{ {
UIFont *expected = [UIFont fontWithName:@"HelveticaNeue-Medium" size:14]; UIFont *expected = [UIFont systemFontOfSize:14 weight:UIFontWeightMedium];
UIFont *result = [RCTConvert UIFont:@{@"fontWeight": @"500"}]; UIFont *result = [RCTConvert UIFont:@{@"fontWeight": @"500"}];
RCTAssertEqualFonts(expected, result); RCTAssertEqualFonts(expected, result);
} }
{ {
UIFont *expected = [UIFont fontWithName:@"HelveticaNeue-UltraLight" size:14]; UIFont *expected = [UIFont systemFontOfSize:14 weight:UIFontWeightUltraLight];
UIFont *result = [RCTConvert UIFont:@{@"fontWeight": @"100"}]; UIFont *result = [RCTConvert UIFont:@{@"fontWeight": @"100"}];
RCTAssertEqualFonts(expected, result); RCTAssertEqualFonts(expected, result);
} }
{ {
UIFont *expected = [UIFont fontWithName:@"HelveticaNeue" size:14]; UIFont *expected = [UIFont systemFontOfSize:14 weight:UIFontWeightRegular];
UIFont *result = [RCTConvert UIFont:@{@"fontWeight": @"normal"}]; UIFont *result = [RCTConvert UIFont:@{@"fontWeight": @"normal"}];
RCTAssertEqualFonts(expected, result); RCTAssertEqualFonts(expected, result);
} }
@ -41,7 +41,7 @@
- (void)testSize - (void)testSize
{ {
{ {
UIFont *expected = [UIFont fontWithName:@"HelveticaNeue" size:18.5]; UIFont *expected = [UIFont systemFontOfSize:18.5];
UIFont *result = [RCTConvert UIFont:@{@"fontSize": @18.5}]; UIFont *result = [RCTConvert UIFont:@{@"fontSize": @18.5}];
RCTAssertEqualFonts(expected, result); RCTAssertEqualFonts(expected, result);
} }
@ -69,32 +69,47 @@
- (void)testStyle - (void)testStyle
{ {
{ {
UIFont *expected = [UIFont fontWithName:@"HelveticaNeue-Italic" size:14]; UIFont *font = [UIFont systemFontOfSize:14];
UIFontDescriptor *fontDescriptor = [font fontDescriptor];
UIFontDescriptorSymbolicTraits symbolicTraits = fontDescriptor.symbolicTraits;
symbolicTraits |= UIFontDescriptorTraitItalic;
fontDescriptor = [fontDescriptor fontDescriptorWithSymbolicTraits:symbolicTraits];
UIFont *expected = [UIFont fontWithDescriptor:fontDescriptor size:14];
UIFont *result = [RCTConvert UIFont:@{@"fontStyle": @"italic"}]; UIFont *result = [RCTConvert UIFont:@{@"fontStyle": @"italic"}];
RCTAssertEqualFonts(expected, result); RCTAssertEqualFonts(expected, result);
} }
{ {
UIFont *expected = [UIFont fontWithName:@"HelveticaNeue" size:14]; UIFont *expected = [UIFont systemFontOfSize:14];
UIFont *result = [RCTConvert UIFont:@{@"fontStyle": @"normal"}]; UIFont *result = [RCTConvert UIFont:@{@"fontStyle": @"normal"}];
RCTAssertEqualFonts(expected, result); RCTAssertEqualFonts(expected, result);
} }
} }
- (void)DISABLED_testStyleAndWeight // task #7118691 - (void)testStyleAndWeight
{ {
{ {
UIFont *expected = [UIFont fontWithName:@"HelveticaNeue-UltraLightItalic" size:14]; UIFont *font = [UIFont systemFontOfSize:14 weight:UIFontWeightUltraLight];
UIFontDescriptor *fontDescriptor = [font fontDescriptor];
UIFontDescriptorSymbolicTraits symbolicTraits = fontDescriptor.symbolicTraits;
symbolicTraits |= UIFontDescriptorTraitItalic;
fontDescriptor = [fontDescriptor fontDescriptorWithSymbolicTraits:symbolicTraits];
UIFont *expected = [UIFont fontWithDescriptor:fontDescriptor size:14];
UIFont *result = [RCTConvert UIFont:@{@"fontStyle": @"italic", @"fontWeight": @"100"}]; UIFont *result = [RCTConvert UIFont:@{@"fontStyle": @"italic", @"fontWeight": @"100"}];
RCTAssertEqualFonts(expected, result); RCTAssertEqualFonts(expected, result);
} }
{ {
UIFont *expected = [UIFont fontWithName:@"HelveticaNeue-BoldItalic" size:14]; UIFont *font = [UIFont systemFontOfSize:14 weight:UIFontWeightBold];
UIFontDescriptor *fontDescriptor = [font fontDescriptor];
UIFontDescriptorSymbolicTraits symbolicTraits = fontDescriptor.symbolicTraits;
symbolicTraits |= UIFontDescriptorTraitItalic;
fontDescriptor = [fontDescriptor fontDescriptorWithSymbolicTraits:symbolicTraits];
UIFont *expected = [UIFont fontWithDescriptor:fontDescriptor size:14];
UIFont *result = [RCTConvert UIFont:@{@"fontStyle": @"italic", @"fontWeight": @"bold"}]; UIFont *result = [RCTConvert UIFont:@{@"fontStyle": @"italic", @"fontWeight": @"bold"}];
RCTAssertEqualFonts(expected, result); RCTAssertEqualFonts(expected, result);
} }
} }
- (void)DISABLED_testFamilyAndWeight // task #7118691 - (void)testFamilyAndWeight
{ {
{ {
UIFont *expected = [UIFont fontWithName:@"HelveticaNeue-Bold" size:14]; UIFont *expected = [UIFont fontWithName:@"HelveticaNeue-Bold" size:14];
@ -111,11 +126,6 @@
UIFont *result = [RCTConvert UIFont:@{@"fontFamily": @"Cochin", @"fontWeight": @"700"}]; UIFont *result = [RCTConvert UIFont:@{@"fontFamily": @"Cochin", @"fontWeight": @"700"}];
RCTAssertEqualFonts(expected, result); RCTAssertEqualFonts(expected, result);
} }
{
UIFont *expected = [UIFont fontWithName:@"Cochin" size:14];
UIFont *result = [RCTConvert UIFont:@{@"fontFamily": @"Cochin", @"fontWeight": @"500"}]; // regular Cochin is actually medium bold
RCTAssertEqualFonts(expected, result);
}
{ {
UIFont *expected = [UIFont fontWithName:@"Cochin" size:14]; UIFont *expected = [UIFont fontWithName:@"Cochin" size:14];
UIFont *result = [RCTConvert UIFont:@{@"fontFamily": @"Cochin", @"fontWeight": @"100"}]; UIFont *result = [RCTConvert UIFont:@{@"fontFamily": @"Cochin", @"fontWeight": @"100"}];
@ -137,7 +147,7 @@
} }
} }
- (void)DISABLED_testFamilyStyleAndWeight // task #7118691 - (void)testFamilyStyleAndWeight
{ {
{ {
UIFont *expected = [UIFont fontWithName:@"HelveticaNeue-UltraLightItalic" size:14]; UIFont *expected = [UIFont fontWithName:@"HelveticaNeue-UltraLightItalic" size:14];
@ -156,4 +166,18 @@
} }
} }
- (void)testInvalidFont
{
{
UIFont *expected = [UIFont systemFontOfSize:14];
UIFont *result = [RCTConvert UIFont:@{@"fontFamily": @"foobar"}];
RCTAssertEqualFonts(expected, result);
}
{
UIFont *expected = [UIFont boldSystemFontOfSize:14];
UIFont *result = [RCTConvert UIFont:@{@"fontFamily": @"foobar", @"fontWeight": @"bold"}];
RCTAssertEqualFonts(expected, result);
}
}
@end @end

View File

@ -382,7 +382,7 @@
XCTAssertEqualObjects(removeIndices, (@[@0, @1, @3, @4])); XCTAssertEqualObjects(removeIndices, (@[@0, @1, @3, @4]));
} }
- (void)testScenario1 - (void)DISABLED_testScenario1 // t7660646
{ {
RCTUIManager *uiManager = [[RCTUIManager alloc] init]; RCTUIManager *uiManager = [[RCTUIManager alloc] init];
RCTBridge *bridge = [[RCTBridge alloc] initWithBundleURL:nil moduleProvider:^{ return @[uiManager]; } launchOptions:nil]; RCTBridge *bridge = [[RCTBridge alloc] initWithBundleURL:nil moduleProvider:^{ return @[uiManager]; } launchOptions:nil];
@ -454,7 +454,7 @@
[self waitForExpectationsWithTimeout:1 handler:nil]; [self waitForExpectationsWithTimeout:1 handler:nil];
} }
- (void)testScenario2 - (void)DISABLED_testScenario2 // t7660646
{ {
RCTUIManager *uiManager = [[RCTUIManager alloc] init]; RCTUIManager *uiManager = [[RCTUIManager alloc] init];
RCTBridge *bridge = [[RCTBridge alloc] initWithBundleURL:nil moduleProvider:^{ return @[uiManager]; } launchOptions:nil]; RCTBridge *bridge = [[RCTBridge alloc] initWithBundleURL:nil moduleProvider:^{ return @[uiManager]; } launchOptions:nil];

View File

@ -19,15 +19,22 @@
_alignment = alignment; _alignment = alignment;
} }
static void ARTFreeTextFrame(ARTTextFrame frame)
{
if (frame.count) {
// We must release each line before freeing up this struct
for (int i = 0; i < frame.count; i++) {
CFRelease(frame.lines[i]);
}
free(frame.lines);
free(frame.widths);
}
}
- (void)setTextFrame:(ARTTextFrame)frame - (void)setTextFrame:(ARTTextFrame)frame
{ {
if (frame.lines != _textFrame.lines && _textFrame.count) { if (frame.lines != _textFrame.lines) {
// We must release each line before overriding the old one ARTFreeTextFrame(_textFrame);
for (int i = 0; i < _textFrame.count; i++) {
CFRelease(_textFrame.lines[0]);
}
free(_textFrame.lines);
free(_textFrame.widths);
} }
[self invalidate]; [self invalidate];
_textFrame = frame; _textFrame = frame;
@ -35,14 +42,7 @@
- (void)dealloc - (void)dealloc
{ {
if (_textFrame.count) { ARTFreeTextFrame(_textFrame);
// We must release each line before freeing up this struct
for (int i = 0; i < _textFrame.count; i++) {
CFRelease(_textFrame.lines[0]);
}
free(_textFrame.lines);
free(_textFrame.widths);
}
} }
- (void)renderLayerTo:(CGContextRef)context - (void)renderLayerTo:(CGContextRef)context

View File

@ -243,7 +243,7 @@ function checkValidInputRange(arr: Array<number>) {
* mean this implicit string conversion, you can do something like * mean this implicit string conversion, you can do something like
* String(myThing) * String(myThing)
*/ */
'inputRange must be monolithically increasing ' + arr 'inputRange must be monotonically increasing ' + arr
); );
} }
} }

View File

@ -104,7 +104,33 @@ var Image = React.createClass({
* *
* {nativeEvent: { layout: {x, y, width, height}}}. * {nativeEvent: { layout: {x, y, width, height}}}.
*/ */
onLayout: PropTypes.func, onLayout: PropTypes.func,
/**
* Invoked on load start
*/
onLoadStart: PropTypes.func,
/**
* Invoked on download progress with
*
* {nativeEvent: { written, total}}.
*/
onLoadProgress: PropTypes.func,
/**
* Invoked on load abort
*/
onLoadAbort: PropTypes.func,
/**
* Invoked on load error
*
* {nativeEvent: { error}}.
*/
onLoadError: PropTypes.func,
/**
* Invoked on load end
*
*/
onLoaded: PropTypes.func
}, },
statics: { statics: {
@ -161,6 +187,7 @@ var Image = React.createClass({
if (this.props.defaultSource) { if (this.props.defaultSource) {
nativeProps.defaultImageSrc = this.props.defaultSource.uri; nativeProps.defaultImageSrc = this.props.defaultSource.uri;
} }
nativeProps.progressHandlerRegistered = isNetwork && this.props.onLoadProgress;
return <RawImage {...nativeProps} />; return <RawImage {...nativeProps} />;
} }
}); });
@ -178,6 +205,7 @@ var nativeOnlyProps = {
src: true, src: true,
defaultImageSrc: true, defaultImageSrc: true,
imageTag: true, imageTag: true,
progressHandlerRegistered: true
}; };
if (__DEV__) { if (__DEV__) {
verifyPropTypes(Image, RCTStaticImage.viewConfig, nativeOnlyProps); verifyPropTypes(Image, RCTStaticImage.viewConfig, nativeOnlyProps);

View File

@ -0,0 +1,21 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#import <Foundation/Foundation.h>
typedef void (^RCTDataCompletionBlock)(NSURLResponse *response, NSData *data, NSError *error);
typedef void (^RCTDataProgressBlock)(int64_t written, int64_t total);
@interface RCTDownloadTaskWrapper : NSObject <NSURLSessionDownloadDelegate>
- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration delegateQueue:(NSOperationQueue *)delegateQueue;
- (NSURLSessionDownloadTask *)downloadData:(NSURL *)url progressBlock:(RCTDataProgressBlock)progressBlock completionBlock:(RCTDataCompletionBlock)completionBlock;
@end

View File

@ -0,0 +1,103 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#import "RCTDownloadTaskWrapper.h"
#import <objc/runtime.h>
static void *const RCTDownloadTaskWrapperCompletionBlockKey = (void *)&RCTDownloadTaskWrapperCompletionBlockKey;
static void *const RCTDownloadTaskWrapperProgressBlockKey = (void *)&RCTDownloadTaskWrapperProgressBlockKey;
@interface NSURLSessionTask (RCTDownloadTaskWrapper)
@property (nonatomic, copy, setter=rct_setCompletionBlock:) RCTDataCompletionBlock rct_completionBlock;
@property (nonatomic, copy, setter=rct_setProgressBlock:) RCTDataProgressBlock rct_progressBlock;
@end
@implementation NSURLSessionTask (RCTDownloadTaskWrapper)
- (RCTDataCompletionBlock)rct_completionBlock
{
return objc_getAssociatedObject(self, RCTDownloadTaskWrapperCompletionBlockKey);
}
- (void)rct_setCompletionBlock:(RCTDataCompletionBlock)completionBlock
{
objc_setAssociatedObject(self, RCTDownloadTaskWrapperCompletionBlockKey, completionBlock, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
- (RCTDataProgressBlock)rct_progressBlock
{
return objc_getAssociatedObject(self, RCTDownloadTaskWrapperProgressBlockKey);
}
- (void)rct_setProgressBlock:(RCTDataProgressBlock)progressBlock
{
objc_setAssociatedObject(self, RCTDownloadTaskWrapperProgressBlockKey, progressBlock, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
@end
@implementation RCTDownloadTaskWrapper
{
NSURLSession *_URLSession;
}
- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration delegateQueue:(NSOperationQueue *)delegateQueue
{
if ((self = [super init])) {
_URLSession = [NSURLSession sessionWithConfiguration:configuration delegate:self delegateQueue:nil];
}
return self;
}
- (NSURLSessionDownloadTask *)downloadData:(NSURL *)url progressBlock:(RCTDataProgressBlock)progressBlock completionBlock:(RCTDataCompletionBlock)completionBlock
{
NSURLSessionDownloadTask *task = [_URLSession downloadTaskWithURL:url completionHandler:nil];
task.rct_completionBlock = completionBlock;
task.rct_progressBlock = progressBlock;
[task resume];
return task;
}
#pragma mark - NSURLSessionTaskDelegate methods
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location
{
if (downloadTask.rct_completionBlock) {
NSData *data = [NSData dataWithContentsOfURL:location];
dispatch_async(dispatch_get_main_queue(), ^{
downloadTask.rct_completionBlock(downloadTask.response, data, nil);
});
}
}
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)didWriteData totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite;
{
if (downloadTask.rct_progressBlock) {
dispatch_async(dispatch_get_main_queue(), ^{
downloadTask.rct_progressBlock(totalBytesWritten, totalBytesExpectedToWrite);
});
}
}
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
{
if (error && task.rct_completionBlock) {
dispatch_async(dispatch_get_main_queue(), ^{
task.rct_completionBlock(nil, nil, error);
});
}
}
@end

View File

@ -7,6 +7,7 @@
objects = { objects = {
/* Begin PBXBuildFile section */ /* Begin PBXBuildFile section */
03559E7F1B064DAF00730281 /* RCTDownloadTaskWrapper.m in Sources */ = {isa = PBXBuildFile; fileRef = 03559E7E1B064DAF00730281 /* RCTDownloadTaskWrapper.m */; };
1304D5AB1AA8C4A30002E2BE /* RCTStaticImage.m in Sources */ = {isa = PBXBuildFile; fileRef = 1304D5A81AA8C4A30002E2BE /* RCTStaticImage.m */; }; 1304D5AB1AA8C4A30002E2BE /* RCTStaticImage.m in Sources */ = {isa = PBXBuildFile; fileRef = 1304D5A81AA8C4A30002E2BE /* RCTStaticImage.m */; };
1304D5AC1AA8C4A30002E2BE /* RCTStaticImageManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 1304D5AA1AA8C4A30002E2BE /* RCTStaticImageManager.m */; }; 1304D5AC1AA8C4A30002E2BE /* RCTStaticImageManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 1304D5AA1AA8C4A30002E2BE /* RCTStaticImageManager.m */; };
1304D5B21AA8C50D0002E2BE /* RCTGIFImage.m in Sources */ = {isa = PBXBuildFile; fileRef = 1304D5B11AA8C50D0002E2BE /* RCTGIFImage.m */; }; 1304D5B21AA8C50D0002E2BE /* RCTGIFImage.m in Sources */ = {isa = PBXBuildFile; fileRef = 1304D5B11AA8C50D0002E2BE /* RCTGIFImage.m */; };
@ -32,6 +33,8 @@
/* End PBXCopyFilesBuildPhase section */ /* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */ /* Begin PBXFileReference section */
03559E7D1B064D3A00730281 /* RCTDownloadTaskWrapper.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RCTDownloadTaskWrapper.h; sourceTree = "<group>"; };
03559E7E1B064DAF00730281 /* RCTDownloadTaskWrapper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTDownloadTaskWrapper.m; sourceTree = "<group>"; };
1304D5A71AA8C4A30002E2BE /* RCTStaticImage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTStaticImage.h; sourceTree = "<group>"; }; 1304D5A71AA8C4A30002E2BE /* RCTStaticImage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTStaticImage.h; sourceTree = "<group>"; };
1304D5A81AA8C4A30002E2BE /* RCTStaticImage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTStaticImage.m; sourceTree = "<group>"; }; 1304D5A81AA8C4A30002E2BE /* RCTStaticImage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTStaticImage.m; sourceTree = "<group>"; };
1304D5A91AA8C4A30002E2BE /* RCTStaticImageManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTStaticImageManager.h; sourceTree = "<group>"; }; 1304D5A91AA8C4A30002E2BE /* RCTStaticImageManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTStaticImageManager.h; sourceTree = "<group>"; };
@ -71,6 +74,8 @@
children = ( children = (
143879331AAD238D00F088A5 /* RCTCameraRollManager.h */, 143879331AAD238D00F088A5 /* RCTCameraRollManager.h */,
143879341AAD238D00F088A5 /* RCTCameraRollManager.m */, 143879341AAD238D00F088A5 /* RCTCameraRollManager.m */,
03559E7D1B064D3A00730281 /* RCTDownloadTaskWrapper.h */,
03559E7E1B064DAF00730281 /* RCTDownloadTaskWrapper.m */,
1304D5B01AA8C50D0002E2BE /* RCTGIFImage.h */, 1304D5B01AA8C50D0002E2BE /* RCTGIFImage.h */,
1304D5B11AA8C50D0002E2BE /* RCTGIFImage.m */, 1304D5B11AA8C50D0002E2BE /* RCTGIFImage.m */,
58B511891A9E6BD600147676 /* RCTImageDownloader.h */, 58B511891A9E6BD600147676 /* RCTImageDownloader.h */,
@ -168,6 +173,7 @@
1304D5B21AA8C50D0002E2BE /* RCTGIFImage.m in Sources */, 1304D5B21AA8C50D0002E2BE /* RCTGIFImage.m in Sources */,
143879351AAD238D00F088A5 /* RCTCameraRollManager.m in Sources */, 143879351AAD238D00F088A5 /* RCTCameraRollManager.m in Sources */,
143879381AAD32A300F088A5 /* RCTImageLoader.m in Sources */, 143879381AAD32A300F088A5 /* RCTImageLoader.m in Sources */,
03559E7F1B064DAF00730281 /* RCTDownloadTaskWrapper.m in Sources */,
1304D5AB1AA8C4A30002E2BE /* RCTStaticImage.m in Sources */, 1304D5AB1AA8C4A30002E2BE /* RCTStaticImage.m in Sources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;

View File

@ -9,8 +9,11 @@
#import <UIKit/UIKit.h> #import <UIKit/UIKit.h>
#import "RCTDownloadTaskWrapper.h"
typedef void (^RCTDataDownloadBlock)(NSData *data, NSError *error); typedef void (^RCTDataDownloadBlock)(NSData *data, NSError *error);
typedef void (^RCTImageDownloadBlock)(UIImage *image, NSError *error); typedef void (^RCTImageDownloadBlock)(UIImage *image, NSError *error);
typedef void (^RCTImageDownloadCancellationBlock)(void);
@interface RCTImageDownloader : NSObject @interface RCTImageDownloader : NSObject
@ -21,8 +24,9 @@ typedef void (^RCTImageDownloadBlock)(UIImage *image, NSError *error);
* will not be executed on the same thread you called the method from, nor on * will not be executed on the same thread you called the method from, nor on
* the main thread. Returns a token that can be used to cancel the download. * the main thread. Returns a token that can be used to cancel the download.
*/ */
- (id)downloadDataForURL:(NSURL *)url - (RCTImageDownloadCancellationBlock)downloadDataForURL:(NSURL *)url
block:(RCTDataDownloadBlock)block; progressBlock:(RCTDataProgressBlock)progressBlock
block:(RCTDataDownloadBlock)block;
/** /**
* Downloads an image and decompresses it a the size specified. The compressed * Downloads an image and decompresses it a the size specified. The compressed
@ -30,18 +34,19 @@ typedef void (^RCTImageDownloadBlock)(UIImage *image, NSError *error);
* will not be executed on the same thread you called the method from, nor on * will not be executed on the same thread you called the method from, nor on
* the main thread. Returns a token that can be used to cancel the download. * the main thread. Returns a token that can be used to cancel the download.
*/ */
- (id)downloadImageForURL:(NSURL *)url - (RCTImageDownloadCancellationBlock)downloadImageForURL:(NSURL *)url
size:(CGSize)size size:(CGSize)size
scale:(CGFloat)scale scale:(CGFloat)scale
resizeMode:(UIViewContentMode)resizeMode resizeMode:(UIViewContentMode)resizeMode
backgroundColor:(UIColor *)backgroundColor backgroundColor:(UIColor *)backgroundColor
block:(RCTImageDownloadBlock)block; progressBlock:(RCTDataProgressBlock)progressBlock
block:(RCTImageDownloadBlock)block;
/** /**
* Cancel an in-flight download. If multiple requets have been made for the * Cancel an in-flight download. If multiple requets have been made for the
* same image, only the request that relates to the token passed will be * same image, only the request that relates to the token passed will be
* cancelled. * cancelled.
*/ */
- (void)cancelDownload:(id)downloadToken; - (void)cancelDownload:(RCTImageDownloadCancellationBlock)downloadToken;
@end @end

View File

@ -9,6 +9,7 @@
#import "RCTImageDownloader.h" #import "RCTImageDownloader.h"
#import "RCTDownloadTaskWrapper.h"
#import "RCTLog.h" #import "RCTLog.h"
#import "RCTUtils.h" #import "RCTUtils.h"
@ -22,6 +23,7 @@ CGRect RCTClipRect(CGSize, CGFloat, CGSize, CGFloat, UIViewContentMode);
NSURLCache *_cache; NSURLCache *_cache;
dispatch_queue_t _processingQueue; dispatch_queue_t _processingQueue;
NSMutableDictionary *_pendingBlocks; NSMutableDictionary *_pendingBlocks;
RCTDownloadTaskWrapper *_downloadTaskWrapper;
} }
+ (RCTImageDownloader *)sharedInstance + (RCTImageDownloader *)sharedInstance
@ -40,19 +42,22 @@ CGRect RCTClipRect(CGSize, CGFloat, CGSize, CGFloat, UIViewContentMode);
_cache = [[NSURLCache alloc] initWithMemoryCapacity:5 * 1024 * 1024 diskCapacity:200 * 1024 * 1024 diskPath:@"React/RCTImageDownloader"]; _cache = [[NSURLCache alloc] initWithMemoryCapacity:5 * 1024 * 1024 diskCapacity:200 * 1024 * 1024 diskPath:@"React/RCTImageDownloader"];
_processingQueue = dispatch_queue_create("com.facebook.React.DownloadProcessingQueue", DISPATCH_QUEUE_SERIAL); _processingQueue = dispatch_queue_create("com.facebook.React.DownloadProcessingQueue", DISPATCH_QUEUE_SERIAL);
_pendingBlocks = [[NSMutableDictionary alloc] init]; _pendingBlocks = [[NSMutableDictionary alloc] init];
NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
_downloadTaskWrapper = [[RCTDownloadTaskWrapper alloc] initWithSessionConfiguration:config delegateQueue:nil];
} }
return self; return self;
} }
- (id)_downloadDataForURL:(NSURL *)url block:(RCTCachedDataDownloadBlock)block - (RCTImageDownloadCancellationBlock)_downloadDataForURL:(NSURL *)url progressBlock:progressBlock block:(RCTCachedDataDownloadBlock)block
{ {
NSString *cacheKey = url.absoluteString; NSString *const cacheKey = url.absoluteString;
__block BOOL cancelled = NO; __block BOOL cancelled = NO;
__block NSURLSessionDataTask *task = nil; __block NSURLSessionDownloadTask *task = nil;
dispatch_block_t cancel = ^{ RCTImageDownloadCancellationBlock cancel = ^{
cancelled = YES; cancelled = YES;
dispatch_async(_processingQueue, ^{ dispatch_async(_processingQueue, ^{
@ -85,7 +90,8 @@ CGRect RCTClipRect(CGSize, CGFloat, CGSize, CGFloat, UIViewContentMode);
}); });
}; };
task = [[NSURLSession sharedSession] dataTaskWithURL:url completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { NSURLRequest *request = [NSURLRequest requestWithURL:url];
task = [_downloadTaskWrapper downloadData:url progressBlock:progressBlock completionBlock:^(NSURLResponse *response, NSData *data, NSError *error) {
if (!cancelled) { if (!cancelled) {
runBlocks(NO, data, error); runBlocks(NO, data, error);
} }
@ -93,12 +99,12 @@ CGRect RCTClipRect(CGSize, CGFloat, CGSize, CGFloat, UIViewContentMode);
if (response) { if (response) {
RCTImageDownloader *strongSelf = weakSelf; RCTImageDownloader *strongSelf = weakSelf;
NSCachedURLResponse *cachedResponse = [[NSCachedURLResponse alloc] initWithResponse:response data:data userInfo:nil storagePolicy:NSURLCacheStorageAllowed]; NSCachedURLResponse *cachedResponse = [[NSCachedURLResponse alloc] initWithResponse:response data:data userInfo:nil storagePolicy:NSURLCacheStorageAllowed];
[strongSelf->_cache storeCachedResponse:cachedResponse forDataTask:task]; [strongSelf->_cache storeCachedResponse:cachedResponse forRequest:request];
} }
task = nil; task = nil;
}]; }];
[_cache getCachedResponseForDataTask:task completionHandler:^(NSCachedURLResponse *cachedResponse) { NSCachedURLResponse *cachedResponse = [_cache cachedResponseForRequest:request];
if (cancelled) { if (cancelled) {
return; return;
} }
@ -108,28 +114,29 @@ CGRect RCTClipRect(CGSize, CGFloat, CGSize, CGFloat, UIViewContentMode);
} else { } else {
[task resume]; [task resume];
} }
}];
} }
}); });
return [cancel copy]; return [cancel copy];
} }
- (id)downloadDataForURL:(NSURL *)url block:(RCTDataDownloadBlock)block - (RCTImageDownloadCancellationBlock)downloadDataForURL:(NSURL *)url progressBlock:(RCTDataProgressBlock)progressBlock block:(RCTDataDownloadBlock)block
{ {
return [self _downloadDataForURL:url block:^(BOOL cached, NSData *data, NSError *error) { return [self _downloadDataForURL:url progressBlock:progressBlock block:^(BOOL cached, NSData *data, NSError *error) {
block(data, error); block(data, error);
}]; }];
} }
- (id)downloadImageForURL:(NSURL *)url - (RCTImageDownloadCancellationBlock)downloadImageForURL:(NSURL *)url
size:(CGSize)size size:(CGSize)size
scale:(CGFloat)scale scale:(CGFloat)scale
resizeMode:(UIViewContentMode)resizeMode resizeMode:(UIViewContentMode)resizeMode
backgroundColor:(UIColor *)backgroundColor backgroundColor:(UIColor *)backgroundColor
block:(RCTImageDownloadBlock)block progressBlock:(RCTDataProgressBlock)progressBlock
block:(RCTImageDownloadBlock)block
{ {
return [self downloadDataForURL:url block:^(NSData *data, NSError *error) { return [self downloadDataForURL:url progressBlock:progressBlock block:^(NSData *data, NSError *error) {
if (!data || error) { if (!data || error) {
block(nil, error); block(nil, error);
return; return;
@ -176,10 +183,10 @@ CGRect RCTClipRect(CGSize, CGFloat, CGSize, CGFloat, UIViewContentMode);
}]; }];
} }
- (void)cancelDownload:(id)downloadToken - (void)cancelDownload:(RCTImageDownloadCancellationBlock)downloadToken
{ {
if (downloadToken) { if (downloadToken) {
((dispatch_block_t)downloadToken)(); downloadToken();
} }
} }

View File

@ -121,7 +121,7 @@ static dispatch_queue_t RCTImageLoaderQueue(void)
RCTDispatchCallbackOnMainQueue(callback, RCTErrorWithMessage(errorMessage), nil); RCTDispatchCallbackOnMainQueue(callback, RCTErrorWithMessage(errorMessage), nil);
return; return;
} }
[[RCTImageDownloader sharedInstance] downloadDataForURL:url block:^(NSData *data, NSError *error) { [[RCTImageDownloader sharedInstance] downloadDataForURL:url progressBlock:nil block:^(NSData *data, NSError *error) {
if (error) { if (error) {
RCTDispatchCallbackOnMainQueue(callback, error, nil); RCTDispatchCallbackOnMainQueue(callback, error, nil);
} else { } else {

View File

@ -9,11 +9,13 @@
#import <UIKit/UIKit.h> #import <UIKit/UIKit.h>
@class RCTEventDispatcher;
@class RCTImageDownloader; @class RCTImageDownloader;
@interface RCTNetworkImageView : UIView @interface RCTNetworkImageView : UIView
- (instancetype)initWithImageDownloader:(RCTImageDownloader *)imageDownloader NS_DESIGNATED_INITIALIZER; - (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher
imageDownloader:(RCTImageDownloader *)imageDownloader NS_DESIGNATED_INITIALIZER;
/** /**
* An image that will appear while the view is loading the image from the network, * An image that will appear while the view is loading the image from the network,

View File

@ -14,23 +14,27 @@
#import "RCTGIFImage.h" #import "RCTGIFImage.h"
#import "RCTImageDownloader.h" #import "RCTImageDownloader.h"
#import "RCTUtils.h" #import "RCTUtils.h"
#import "RCTBridgeModule.h"
#import "RCTEventDispatcher.h"
#import "UIView+React.h" #import "UIView+React.h"
@implementation RCTNetworkImageView @implementation RCTNetworkImageView
{ {
BOOL _deferred; BOOL _deferred;
BOOL _progressHandlerRegistered;
NSURL *_imageURL; NSURL *_imageURL;
NSURL *_deferredImageURL; NSURL *_deferredImageURL;
NSUInteger _deferSentinel; NSUInteger _deferSentinel;
RCTImageDownloader *_imageDownloader; RCTImageDownloader *_imageDownloader;
id _downloadToken; id _downloadToken;
RCTEventDispatcher *_eventDispatcher;
} }
- (instancetype)initWithImageDownloader:(RCTImageDownloader *)imageDownloader - (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher imageDownloader:(RCTImageDownloader *)imageDownloader
{ {
RCTAssertParam(imageDownloader);
if ((self = [super initWithFrame:CGRectZero])) { if ((self = [super initWithFrame:CGRectZero])) {
_eventDispatcher = eventDispatcher;
_progressHandlerRegistered = NO;
_deferSentinel = 0; _deferSentinel = 0;
_imageDownloader = imageDownloader; _imageDownloader = imageDownloader;
self.userInteractionEnabled = NO; self.userInteractionEnabled = NO;
@ -56,6 +60,11 @@ RCT_NOT_IMPLEMENTED(-initWithCoder:(NSCoder *)aDecoder)
[self _updateImage]; [self _updateImage];
} }
- (void)setProgressHandlerRegistered:(BOOL)progressHandlerRegistered
{
_progressHandlerRegistered = progressHandlerRegistered;
}
- (void)reactSetFrame:(CGRect)frame - (void)reactSetFrame:(CGRect)frame
{ {
[super reactSetFrame:frame]; [super reactSetFrame:frame];
@ -89,8 +98,34 @@ RCT_NOT_IMPLEMENTED(-initWithCoder:(NSCoder *)aDecoder)
self.layer.minificationFilter = kCAFilterTrilinear; self.layer.minificationFilter = kCAFilterTrilinear;
self.layer.magnificationFilter = kCAFilterTrilinear; self.layer.magnificationFilter = kCAFilterTrilinear;
} }
[_eventDispatcher sendInputEventWithName:@"loadStart" body:@{ @"target": self.reactTag }];
RCTDataProgressBlock progressHandler = ^(int64_t written, int64_t total) {
if (_progressHandlerRegistered) {
NSDictionary *event = @{
@"target": self.reactTag,
@"written": @(written),
@"total": @(total),
};
[_eventDispatcher sendInputEventWithName:@"loadProgress" body:event];
}
};
void (^errorHandler)(NSString *errorDescription) = ^(NSString *errorDescription) {
NSDictionary *event = @{
@"target": self.reactTag,
@"error": errorDescription,
};
[_eventDispatcher sendInputEventWithName:@"loadError" body:event];
};
void (^loadEndHandler)(void) = ^(void) {
NSDictionary *event = @{ @"target": self.reactTag };
[_eventDispatcher sendInputEventWithName:@"loaded" body:event];
};
if ([imageURL.pathExtension caseInsensitiveCompare:@"gif"] == NSOrderedSame) { if ([imageURL.pathExtension caseInsensitiveCompare:@"gif"] == NSOrderedSame) {
_downloadToken = [_imageDownloader downloadDataForURL:imageURL block:^(NSData *data, NSError *error) { _downloadToken = [_imageDownloader downloadDataForURL:imageURL progressBlock:progressHandler block:^(NSData *data, NSError *error) {
if (data) { if (data) {
dispatch_async(dispatch_get_main_queue(), ^{ dispatch_async(dispatch_get_main_queue(), ^{
if (imageURL != self.imageURL) { if (imageURL != self.imageURL) {
@ -102,13 +137,16 @@ RCT_NOT_IMPLEMENTED(-initWithCoder:(NSCoder *)aDecoder)
self.layer.minificationFilter = kCAFilterLinear; self.layer.minificationFilter = kCAFilterLinear;
self.layer.magnificationFilter = kCAFilterLinear; self.layer.magnificationFilter = kCAFilterLinear;
[self.layer addAnimation:animation forKey:@"contents"]; [self.layer addAnimation:animation forKey:@"contents"];
loadEndHandler();
}); });
} else if (error) { } else if (error) {
RCTLogWarn(@"Unable to download image data. Error: %@", error); errorHandler([error description]);
} }
}]; }];
} else { } else {
_downloadToken = [_imageDownloader downloadImageForURL:imageURL size:self.bounds.size scale:RCTScreenScale() resizeMode:self.contentMode backgroundColor:self.backgroundColor block:^(UIImage *image, NSError *error) { _downloadToken = [_imageDownloader downloadImageForURL:imageURL size:self.bounds.size scale:RCTScreenScale()
resizeMode:self.contentMode backgroundColor:self.backgroundColor
progressBlock:progressHandler block:^(UIImage *image, NSError *error) {
if (image) { if (image) {
dispatch_async(dispatch_get_main_queue(), ^{ dispatch_async(dispatch_get_main_queue(), ^{
if (imageURL != self.imageURL) { if (imageURL != self.imageURL) {
@ -118,9 +156,10 @@ RCT_NOT_IMPLEMENTED(-initWithCoder:(NSCoder *)aDecoder)
[self.layer removeAnimationForKey:@"contents"]; [self.layer removeAnimationForKey:@"contents"];
self.layer.contentsScale = image.scale; self.layer.contentsScale = image.scale;
self.layer.contents = (__bridge id)image.CGImage; self.layer.contents = (__bridge id)image.CGImage;
loadEndHandler();
}); });
} else if (error) { } else if (error) {
RCTLogWarn(@"Unable to download image. Error: %@", error); errorHandler([error description]);
} }
}]; }];
} }

View File

@ -9,24 +9,38 @@
#import "RCTNetworkImageViewManager.h" #import "RCTNetworkImageViewManager.h"
#import "RCTNetworkImageView.h" #import "RCTBridge.h"
#import "RCTConvert.h" #import "RCTConvert.h"
#import "RCTUtils.h"
#import "RCTImageDownloader.h" #import "RCTImageDownloader.h"
#import "RCTNetworkImageView.h"
#import "RCTUtils.h"
@implementation RCTNetworkImageViewManager @implementation RCTNetworkImageViewManager
RCT_EXPORT_MODULE() RCT_EXPORT_MODULE()
@synthesize bridge = _bridge;
@synthesize methodQueue = _methodQueue;
- (UIView *)view - (UIView *)view
{ {
return [[RCTNetworkImageView alloc] initWithImageDownloader:[RCTImageDownloader sharedInstance]]; return [[RCTNetworkImageView alloc] initWithEventDispatcher:self.bridge.eventDispatcher imageDownloader:[RCTImageDownloader sharedInstance]];
} }
RCT_REMAP_VIEW_PROPERTY(defaultImageSrc, defaultImage, UIImage) RCT_REMAP_VIEW_PROPERTY(defaultImageSrc, defaultImage, UIImage)
RCT_REMAP_VIEW_PROPERTY(src, imageURL, NSURL) RCT_REMAP_VIEW_PROPERTY(src, imageURL, NSURL)
RCT_REMAP_VIEW_PROPERTY(resizeMode, contentMode, UIViewContentMode) RCT_REMAP_VIEW_PROPERTY(resizeMode, contentMode, UIViewContentMode)
RCT_EXPORT_VIEW_PROPERTY(progressHandlerRegistered, BOOL)
- (NSDictionary *)customDirectEventTypes
{
return @{
@"loadStart": @{ @"registrationName": @"onLoadStart" },
@"loadProgress": @{ @"registrationName": @"onLoadProgress" },
@"loaded": @{ @"registrationName": @"onLoaded" },
@"loadError": @{ @"registrationName": @"onLoadError" },
@"loadAbort": @{ @"registrationName": @"onLoadAbort" },
};
}
@end @end

View File

@ -222,7 +222,7 @@ RCT_NOT_IMPLEMENTED(-initWithCoder:(NSCoder *)aDecoder)
- (UIFont *)defaultPlaceholderFont - (UIFont *)defaultPlaceholderFont
{ {
return [UIFont fontWithName:@"Helvetica" size:17]; return [UIFont systemFontOfSize:17];
} }
- (UIColor *)defaultPlaceholderTextColor - (UIColor *)defaultPlaceholderTextColor

View File

@ -26,6 +26,8 @@ let MODULE_IDS = 0;
let METHOD_IDS = 1; let METHOD_IDS = 1;
let PARAMS = 2; let PARAMS = 2;
let SPY_MODE = false;
let MethodTypes = keyMirror({ let MethodTypes = keyMirror({
local: null, local: null,
remote: null, remote: null,
@ -120,6 +122,10 @@ class MessageQueue {
(this._debugInfo[this._callbackID >> 5] = null); (this._debugInfo[this._callbackID >> 5] = null);
this._debugInfo[this._callbackID >> 1] = [module, method]; this._debugInfo[this._callbackID >> 1] = [module, method];
if (SPY_MODE && isFinite(module)) {
console.log('JS->N : ' + this._remoteModuleTable[module] + '.' +
this._remoteMethodTable[module][method] + '(' + JSON.stringify(params) + ')');
}
} }
onFail && params.push(this._callbackID); onFail && params.push(this._callbackID);
this._callbacks[this._callbackID++] = onFail; this._callbacks[this._callbackID++] = onFail;
@ -137,6 +143,9 @@ class MessageQueue {
method = this._methodTable[module][method]; method = this._methodTable[module][method];
module = this._moduleTable[module]; module = this._moduleTable[module];
} }
if (__DEV__ && SPY_MODE) {
console.log('N->JS : ' + module + '.' + method + '(' + JSON.stringify(args) + ')');
}
module = this._require(module); module = this._require(module);
module[method].apply(module, args); module[method].apply(module, args);
BridgeProfiling.profileEnd(); BridgeProfiling.profileEnd();
@ -146,11 +155,15 @@ class MessageQueue {
BridgeProfiling.profile( BridgeProfiling.profile(
() => `MessageQueue.invokeCallback(${cbID}, ${stringifySafe(args)})`); () => `MessageQueue.invokeCallback(${cbID}, ${stringifySafe(args)})`);
let callback = this._callbacks[cbID]; let callback = this._callbacks[cbID];
if (__DEV__ && !callback) { if (__DEV__) {
let debug = this._debugInfo[cbID >> 1]; let debug = this._debugInfo[cbID >> 1];
let module = this._remoteModuleTable[debug[0]]; let module = this._remoteModuleTable[debug[0]];
let method = this._remoteMethodTable[debug[0]][debug[1]]; let method = this._remoteMethodTable[debug[0]][debug[1]];
console.error(`Callback with id ${cbID}: ${module}.${method}() not found`); if (!callback) {
console.error(`Callback with id ${cbID}: ${module}.${method}() not found`);
} else if (SPY_MODE) {
console.log('N->JS : <callback for ' + module + '.' + method + '>(' + JSON.stringify(args) + ')');
}
} }
this._callbacks[cbID & ~1] = null; this._callbacks[cbID & ~1] = null;
this._callbacks[cbID | 1] = null; this._callbacks[cbID | 1] = null;

View File

@ -38,7 +38,6 @@ typedef void (^RCTPromiseResolveBlock)(id result);
*/ */
typedef void (^RCTPromiseRejectBlock)(NSError *error); typedef void (^RCTPromiseRejectBlock)(NSError *error);
/** /**
* This constant can be returned from +methodQueue to force module * This constant can be returned from +methodQueue to force module
* methods to be called on the JavaScript thread. This can have serious * methods to be called on the JavaScript thread. This can have serious

View File

@ -39,6 +39,7 @@
+ (NSUInteger)NSUInteger:(id)json; + (NSUInteger)NSUInteger:(id)json;
+ (NSArray *)NSArray:(id)json; + (NSArray *)NSArray:(id)json;
+ (NSSet *)NSSet:(id)json;
+ (NSDictionary *)NSDictionary:(id)json; + (NSDictionary *)NSDictionary:(id)json;
+ (NSString *)NSString:(id)json; + (NSString *)NSString:(id)json;
+ (NSNumber *)NSNumber:(id)json; + (NSNumber *)NSNumber:(id)json;

View File

@ -35,6 +35,7 @@ RCT_NUMBER_CONVERTER(NSInteger, integerValue)
RCT_NUMBER_CONVERTER(NSUInteger, unsignedIntegerValue) RCT_NUMBER_CONVERTER(NSUInteger, unsignedIntegerValue)
RCT_CUSTOM_CONVERTER(NSArray *, NSArray, [NSArray arrayWithArray:json]) RCT_CUSTOM_CONVERTER(NSArray *, NSArray, [NSArray arrayWithArray:json])
RCT_CUSTOM_CONVERTER(NSSet *, NSSet, [NSSet setWithArray:json])
RCT_CUSTOM_CONVERTER(NSDictionary *, NSDictionary, [NSDictionary dictionaryWithDictionary:json]) RCT_CUSTOM_CONVERTER(NSDictionary *, NSDictionary, [NSDictionary dictionaryWithDictionary:json])
RCT_CONVERTER(NSString *, NSString, description) RCT_CONVERTER(NSString *, NSString, description)
@ -671,8 +672,8 @@ RCT_CGSTRUCT_CONVERTER(CGAffineTransform, (@[
NSURL *url = [NSURL URLWithString:path]; NSURL *url = [NSURL URLWithString:path];
NSData *imageData = [NSData dataWithContentsOfURL:url]; NSData *imageData = [NSData dataWithContentsOfURL:url];
image = [UIImage imageWithData:imageData]; image = [UIImage imageWithData:imageData];
} else if ([path isAbsolutePath]) { } else if ([path isAbsolutePath] || [path hasPrefix:@"~"]) {
image = [UIImage imageWithContentsOfFile:path]; image = [UIImage imageWithContentsOfFile:path.stringByExpandingTildeInPath];
} else { } else {
image = [UIImage imageNamed:path]; image = [UIImage imageNamed:path];
if (!image) { if (!image) {
@ -788,7 +789,8 @@ static BOOL RCTFontIsCondensed(UIFont *font)
size:(id)size weight:(id)weight style:(id)style size:(id)size weight:(id)weight style:(id)style
{ {
// Defaults // Defaults
NSString *const RCTDefaultFontFamily = @"Helvetica Neue"; NSString *const RCTDefaultFontFamily = @"System";
NSString *const RCTIOS8SystemFontFamily = @"Helvetica Neue";
const RCTFontWeight RCTDefaultFontWeight = UIFontWeightRegular; const RCTFontWeight RCTDefaultFontWeight = UIFontWeightRegular;
const CGFloat RCTDefaultFontSize = 14; const CGFloat RCTDefaultFontSize = 14;
@ -807,11 +809,36 @@ static BOOL RCTFontIsCondensed(UIFont *font)
isCondensed = RCTFontIsCondensed(font); isCondensed = RCTFontIsCondensed(font);
} }
// Get font size // Get font attributes
fontSize = [self CGFloat:size] ?: fontSize; fontSize = [self CGFloat:size] ?: fontSize;
// Get font family
familyName = [self NSString:family] ?: familyName; familyName = [self NSString:family] ?: familyName;
isItalic = style ? [self RCTFontStyle:style] : isItalic;
fontWeight = weight ? [self RCTFontWeight:weight] : fontWeight;
// Handle system font as special case. This ensures that we preserve
// the specific metrics of the standard system font as closely as possible.
if ([familyName isEqual:RCTDefaultFontFamily]) {
if ([UIFont respondsToSelector:@selector(systemFontOfSize:weight:)]) {
font = [UIFont systemFontOfSize:fontSize weight:fontWeight];
if (isItalic || isCondensed) {
UIFontDescriptor *fontDescriptor = [font fontDescriptor];
UIFontDescriptorSymbolicTraits symbolicTraits = fontDescriptor.symbolicTraits;
if (isItalic) {
symbolicTraits |= UIFontDescriptorTraitItalic;
}
if (isCondensed) {
symbolicTraits |= UIFontDescriptorTraitCondensed;
}
fontDescriptor = [fontDescriptor fontDescriptorWithSymbolicTraits:symbolicTraits];
font = [UIFont fontWithDescriptor:fontDescriptor size:fontSize];
}
return font;
} else {
// systemFontOfSize:weight: isn't available prior to iOS 8.2, so we
// fall back to finding the correct font manually, by linear search.
familyName = RCTIOS8SystemFontFamily;
}
}
// Gracefully handle being given a font name rather than font family, for // Gracefully handle being given a font name rather than font family, for
// example: "Helvetica Light Oblique" rather than just "Helvetica". // example: "Helvetica Light Oblique" rather than just "Helvetica".
@ -821,30 +848,25 @@ static BOOL RCTFontIsCondensed(UIFont *font)
// It's actually a font name, not a font family name, // It's actually a font name, not a font family name,
// but we'll do what was meant, not what was said. // but we'll do what was meant, not what was said.
familyName = font.familyName; familyName = font.familyName;
fontWeight = RCTWeightOfFont(font); fontWeight = weight ? fontWeight : RCTWeightOfFont(font);
isItalic = RCTFontIsItalic(font); isItalic = style ? isItalic : RCTFontIsItalic(font);
isCondensed = RCTFontIsCondensed(font); isCondensed = RCTFontIsCondensed(font);
} else { } else {
// Not a valid font or family // Not a valid font or family
RCTLogError(@"Unrecognized font family '%@'", familyName); RCTLogError(@"Unrecognized font family '%@'", familyName);
familyName = RCTDefaultFontFamily; if ([UIFont respondsToSelector:@selector(systemFontOfSize:weight:)]) {
font = [UIFont systemFontOfSize:fontSize weight:fontWeight];
} else if (fontWeight > UIFontWeightRegular) {
font = [UIFont boldSystemFontOfSize:fontSize];
} else {
font = [UIFont systemFontOfSize:fontSize];
}
} }
} }
// Get font style
if (style) {
isItalic = [self RCTFontStyle:style];
}
// Get font weight
if (weight) {
fontWeight = [self RCTFontWeight:weight];
}
// Get the closest font that matches the given weight for the fontFamily // Get the closest font that matches the given weight for the fontFamily
UIFont *bestMatch = [UIFont fontWithName:font.fontName size: fontSize]; UIFont *bestMatch = font;
CGFloat closestWeight = INFINITY; CGFloat closestWeight = INFINITY;
for (NSString *name in [UIFont fontNamesForFamilyName:familyName]) { for (NSString *name in [UIFont fontNamesForFamilyName:familyName]) {
UIFont *match = [UIFont fontWithName:name size:fontSize]; UIFont *match = [UIFont fontWithName:name size:fontSize];
if (isItalic == RCTFontIsItalic(match) && if (isItalic == RCTFontIsItalic(match) &&
@ -857,14 +879,6 @@ static BOOL RCTFontIsCondensed(UIFont *font)
} }
} }
// Safety net
if (!bestMatch) {
RCTLogError(@"Could not find font with family: '%@', size: %@, \
weight: %@, style: %@", family, size, weight, style);
bestMatch = [UIFont fontWithName:[[UIFont fontNamesForFamilyName:familyName] firstObject]
size:fontSize];
}
return bestMatch; return bestMatch;
} }

View File

@ -32,6 +32,8 @@
NSArray *_argumentBlocks; NSArray *_argumentBlocks;
} }
RCT_NOT_IMPLEMENTED(-init)
- (instancetype)initWithObjCMethodName:(NSString *)objCMethodName - (instancetype)initWithObjCMethodName:(NSString *)objCMethodName
JSMethodName:(NSString *)JSMethodName JSMethodName:(NSString *)JSMethodName
moduleClass:(Class)moduleClass moduleClass:(Class)moduleClass

View File

@ -64,6 +64,8 @@ RCT_EXPORT_MODULE()
- (void)sendTimespans - (void)sendTimespans
{ {
[[NSNotificationCenter defaultCenter] removeObserver:self];
[_bridge enqueueJSCall:@"PerformanceLogger.addTimespans" args:@[ [_bridge enqueueJSCall:@"PerformanceLogger.addTimespans" args:@[
RCTPerformanceLoggerOutput(), RCTPerformanceLoggerOutput(),
@[ @[

View File

@ -64,10 +64,12 @@
"react-tools": "0.13.2", "react-tools": "0.13.2",
"rebound": "^0.0.12", "rebound": "^0.0.12",
"sane": "^1.1.2", "sane": "^1.1.2",
"semver": "^4.3.6",
"source-map": "0.1.31", "source-map": "0.1.31",
"stacktrace-parser": "frantic/stacktrace-parser#493c5e5638", "stacktrace-parser": "frantic/stacktrace-parser#493c5e5638",
"uglify-js": "~2.4.16", "uglify-js": "~2.4.16",
"underscore": "1.7.0", "underscore": "1.7.0",
"wordwrap": "^1.0.0",
"worker-farm": "^1.3.1", "worker-farm": "^1.3.1",
"ws": "0.4.31", "ws": "0.4.31",
"yargs": "1.3.2" "yargs": "1.3.2"

View File

@ -0,0 +1,40 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
'use strict';
var chalk = require('chalk');
var semver = require('semver');
var formatBanner = require('./formatBanner');
function checkNodeVersion() {
if (!semver.satisfies(process.version, '>=2.0.0')) {
var engine = semver.lt(process.version, '1.0.0') ? 'Node' : 'io.js';
var message = 'You are currently running ' + engine + ' ' +
process.version + '.\n' +
'\n' +
'React Native is moving to io.js 2.x. There are several ways to upgrade' +
'to io.js depending on your preference.\n' +
'\n' +
'nvm: nvm install iojs && nvm alias default iojs\n' +
'Homebrew: brew unlink node; brew install iojs && brew ln iojs --force\n' +
'Installer: download the Mac .pkg from https://iojs.org/\n' +
'\n' +
'About io.js: https://iojs.org\n' +
'Follow along at: https://github.com/facebook/react-native/issues/1737';
console.log(formatBanner(message, {
chalkFunction: chalk.green,
marginLeft: 1,
marginRight: 1,
paddingBottom: 1,
}));
}
}
module.exports = checkNodeVersion;

108
packager/formatBanner.js Normal file
View File

@ -0,0 +1,108 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
'use strict';
var _ = require('underscore');
var wordwrap = require('wordwrap');
var HORIZONTAL_LINE = '\u2500';
var VERTICAL_LINE = '\u2502';
var TOP_LEFT = '\u250c';
var TOP_RIGHT = '\u2510';
var BOTTOM_LEFT = '\u2514';
var BOTTOM_RIGHT = '\u2518';
/**
* Prints a banner with a border around it containing the given message. The
* following options are supported:
*
* type Options = {
* // A function to apply to each line of text to decorate it
* chalkFunction: (string: message) => string;
* // The total width (max line length) of the banner, including margin and
* // padding (default = 80)
* width: number;
* // How much leading space to prepend to each line (default = 0)
* marginLeft: number;
* // How much trailing space to append to each line (default = 0)
* marginRight: number;
* // Space between the top banner border and the text (default = 0)
* paddingTop: number;
* // Space between the bottom banner border and the text (default = 0)
* paddingBottom: number;
* // Space between the left banner border and the text (default = 2)
* paddingLeft: number;
* // Space between the right banner border and the text (default = 2)
* paddingRight: number;
* };
*/
function formatBanner(message, options) {
options = options || {};
_.defaults(options, {
chalkFunction: _.identity,
width: 80,
marginLeft: 0,
marginRight: 0,
paddingTop: 0,
paddingBottom: 0,
paddingLeft: 2,
paddingRight: 2,
});
var width = options.width;
var marginLeft = options.marginLeft;
var marginRight = options.marginRight;
var paddingTop = options.paddingTop;
var paddingBottom = options.paddingBottom;
var paddingLeft = options.paddingLeft;
var paddingRight = options.paddingRight;
var horizSpacing = marginLeft + paddingLeft + paddingRight + marginRight;
// 2 for the banner borders
var maxLineWidth = width - horizSpacing - 2;
var wrap = wordwrap(maxLineWidth);
var body = wrap(message);
var left = spaces(marginLeft) + VERTICAL_LINE + spaces(paddingLeft);
var right = spaces(paddingRight) + VERTICAL_LINE + spaces(marginRight);
var bodyLines = _.flatten([
arrayOf('', paddingTop),
body.split('\n'),
arrayOf('', paddingBottom),
]).map(function(line) {
var padding = spaces(Math.max(0, maxLineWidth - line.length));
return left + options.chalkFunction(line) + padding + right;
});
var horizontalBorderLine = repeatString(
HORIZONTAL_LINE,
width - marginLeft - marginRight - 2
);
var top = spaces(marginLeft) + TOP_LEFT + horizontalBorderLine + TOP_RIGHT +
spaces(marginRight);
var bottom = spaces(marginLeft) + BOTTOM_LEFT + horizontalBorderLine +
BOTTOM_RIGHT + spaces(marginRight);
return _.flatten([top, bodyLines, bottom]).join('\n');
}
function spaces(number) {
return repeatString(' ', number);
}
function repeatString(string, number) {
return new Array(number + 1).join(string);
}
function arrayOf(value, number) {
return _.range(number).map(function() {
return value;
});
}
module.exports = formatBanner;

View File

@ -23,7 +23,9 @@
"lint": "node linter.js Examples/", "lint": "node linter.js Examples/",
"start": "./packager/packager.sh" "start": "./packager/packager.sh"
}, },
"dependencies": {}, "dependencies": {
"wordwrap": "^1.0.0"
},
"devDependencies": { "devDependencies": {
"jest-cli": "git://github.com/facebook/jest#0.5.x", "jest-cli": "git://github.com/facebook/jest#0.5.x",
"eslint": "0.9.2" "eslint": "0.9.2"

View File

@ -30,6 +30,8 @@ var chalk = require('chalk');
var connect = require('connect'); var connect = require('connect');
var ReactPackager = require('./react-packager'); var ReactPackager = require('./react-packager');
var blacklist = require('./blacklist.js'); var blacklist = require('./blacklist.js');
var checkNodeVersion = require('./checkNodeVersion');
var formatBanner = require('./formatBanner');
var launchEditor = require('./launchEditor.js'); var launchEditor = require('./launchEditor.js');
var parseCommandLine = require('./parseCommandLine.js'); var parseCommandLine = require('./parseCommandLine.js');
var webSocketProxy = require('./webSocketProxy.js'); var webSocketProxy = require('./webSocketProxy.js');
@ -108,16 +110,19 @@ if (options.assetRoots) {
} }
} }
console.log('\n' + checkNodeVersion();
' ===============================================================\n' +
' | Running packager on port ' + options.port + '. \n' + console.log(formatBanner(
' | Keep this packager running while developing on any JS \n' + 'Running packager on port ' + options.port + '.\n'+
' | projects. Feel free to close this tab and run your own \n' + '\n' +
' | packager instance if you prefer. \n' + 'Keep this packager running while developing on any JS projects. Feel free ' +
' | \n' + 'to close this tab and run your own packager instance if you prefer.\n' +
' | https://github.com/facebook/react-native \n' + '\n' +
' | \n' + 'https://github.com/facebook/react-native', {
' ===============================================================\n' marginLeft: 1,
marginRight: 1,
paddingBottom: 1,
})
); );
console.log( console.log(