Updates for Fri 10 Jul
|
@ -130,10 +130,11 @@ var TouchableFeedbackEvents = React.createClass({
|
|||
},
|
||||
render: function() {
|
||||
return (
|
||||
<View>
|
||||
<View testID="touchable_feedback_events">
|
||||
<View style={[styles.row, {justifyContent: 'center'}]}>
|
||||
<TouchableOpacity
|
||||
style={styles.wrapper}
|
||||
testID="touchable_feedback_events_button"
|
||||
onPress={() => this._appendEvent('press')}
|
||||
onPressIn={() => this._appendEvent('pressIn')}
|
||||
onPressOut={() => this._appendEvent('pressOut')}
|
||||
|
@ -143,7 +144,7 @@ var TouchableFeedbackEvents = React.createClass({
|
|||
</Text>
|
||||
</TouchableOpacity>
|
||||
</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>)}
|
||||
</View>
|
||||
</View>
|
||||
|
@ -165,10 +166,11 @@ var TouchableDelayEvents = React.createClass({
|
|||
},
|
||||
render: function() {
|
||||
return (
|
||||
<View>
|
||||
<View testID="touchable_delay_events">
|
||||
<View style={[styles.row, {justifyContent: 'center'}]}>
|
||||
<TouchableOpacity
|
||||
style={styles.wrapper}
|
||||
testID="touchable_delay_events_button"
|
||||
onPress={() => this._appendEvent('press')}
|
||||
delayPressIn={400}
|
||||
onPressIn={() => this._appendEvent('pressIn - 400ms delay')}
|
||||
|
@ -181,7 +183,7 @@ var TouchableDelayEvents = React.createClass({
|
|||
</Text>
|
||||
</TouchableOpacity>
|
||||
</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>)}
|
||||
</View>
|
||||
</View>
|
||||
|
|
Before Width: | Height: | Size: 72 KiB After Width: | Height: | Size: 73 KiB |
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 22 KiB |
Before Width: | Height: | Size: 87 KiB After Width: | Height: | Size: 86 KiB |
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 25 KiB |
Before Width: | Height: | Size: 265 KiB After Width: | Height: | Size: 266 KiB |
Before Width: | Height: | Size: 95 KiB After Width: | Height: | Size: 97 KiB |
|
@ -190,6 +190,7 @@ class UIExplorerList extends React.Component {
|
|||
onChangeText={this._search.bind(this)}
|
||||
placeholder="Search..."
|
||||
style={[styles.searchTextInput, platformTextInputStyle]}
|
||||
testID="explorer_search"
|
||||
value={this.state.searchText}
|
||||
/>
|
||||
</View>);
|
||||
|
|
|
@ -14,25 +14,25 @@
|
|||
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"}];
|
||||
RCTAssertEqualFonts(expected, result);
|
||||
}
|
||||
{
|
||||
UIFont *expected = [UIFont fontWithName:@"HelveticaNeue-Medium" size:14];
|
||||
UIFont *expected = [UIFont systemFontOfSize:14 weight:UIFontWeightMedium];
|
||||
UIFont *result = [RCTConvert UIFont:@{@"fontWeight": @"500"}];
|
||||
RCTAssertEqualFonts(expected, result);
|
||||
}
|
||||
{
|
||||
UIFont *expected = [UIFont fontWithName:@"HelveticaNeue-UltraLight" size:14];
|
||||
UIFont *expected = [UIFont systemFontOfSize:14 weight:UIFontWeightUltraLight];
|
||||
UIFont *result = [RCTConvert UIFont:@{@"fontWeight": @"100"}];
|
||||
RCTAssertEqualFonts(expected, result);
|
||||
}
|
||||
{
|
||||
UIFont *expected = [UIFont fontWithName:@"HelveticaNeue" size:14];
|
||||
UIFont *expected = [UIFont systemFontOfSize:14 weight:UIFontWeightRegular];
|
||||
UIFont *result = [RCTConvert UIFont:@{@"fontWeight": @"normal"}];
|
||||
RCTAssertEqualFonts(expected, result);
|
||||
}
|
||||
|
@ -41,7 +41,7 @@
|
|||
- (void)testSize
|
||||
{
|
||||
{
|
||||
UIFont *expected = [UIFont fontWithName:@"HelveticaNeue" size:18.5];
|
||||
UIFont *expected = [UIFont systemFontOfSize:18.5];
|
||||
UIFont *result = [RCTConvert UIFont:@{@"fontSize": @18.5}];
|
||||
RCTAssertEqualFonts(expected, result);
|
||||
}
|
||||
|
@ -69,32 +69,47 @@
|
|||
- (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"}];
|
||||
RCTAssertEqualFonts(expected, result);
|
||||
}
|
||||
{
|
||||
UIFont *expected = [UIFont fontWithName:@"HelveticaNeue" size:14];
|
||||
UIFont *expected = [UIFont systemFontOfSize:14];
|
||||
UIFont *result = [RCTConvert UIFont:@{@"fontStyle": @"normal"}];
|
||||
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"}];
|
||||
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"}];
|
||||
RCTAssertEqualFonts(expected, result);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)DISABLED_testFamilyAndWeight // task #7118691
|
||||
- (void)testFamilyAndWeight
|
||||
{
|
||||
{
|
||||
UIFont *expected = [UIFont fontWithName:@"HelveticaNeue-Bold" size:14];
|
||||
|
@ -111,11 +126,6 @@
|
|||
UIFont *result = [RCTConvert UIFont:@{@"fontFamily": @"Cochin", @"fontWeight": @"700"}];
|
||||
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 *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];
|
||||
|
@ -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
|
||||
|
|
|
@ -382,7 +382,7 @@
|
|||
XCTAssertEqualObjects(removeIndices, (@[@0, @1, @3, @4]));
|
||||
}
|
||||
|
||||
- (void)testScenario1
|
||||
- (void)DISABLED_testScenario1 // t7660646
|
||||
{
|
||||
RCTUIManager *uiManager = [[RCTUIManager alloc] init];
|
||||
RCTBridge *bridge = [[RCTBridge alloc] initWithBundleURL:nil moduleProvider:^{ return @[uiManager]; } launchOptions:nil];
|
||||
|
@ -454,7 +454,7 @@
|
|||
[self waitForExpectationsWithTimeout:1 handler:nil];
|
||||
}
|
||||
|
||||
- (void)testScenario2
|
||||
- (void)DISABLED_testScenario2 // t7660646
|
||||
{
|
||||
RCTUIManager *uiManager = [[RCTUIManager alloc] init];
|
||||
RCTBridge *bridge = [[RCTBridge alloc] initWithBundleURL:nil moduleProvider:^{ return @[uiManager]; } launchOptions:nil];
|
||||
|
|
|
@ -19,15 +19,22 @@
|
|||
_alignment = alignment;
|
||||
}
|
||||
|
||||
static void ARTFreeTextFrame(ARTTextFrame frame)
|
||||
{
|
||||
if (frame.count) {
|
||||
// We must release each line before freeing up this struct
|
||||
for (int i = 0; i < frame.count; i++) {
|
||||
CFRelease(frame.lines[i]);
|
||||
}
|
||||
free(frame.lines);
|
||||
free(frame.widths);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setTextFrame:(ARTTextFrame)frame
|
||||
{
|
||||
if (frame.lines != _textFrame.lines && _textFrame.count) {
|
||||
// We must release each line before overriding the old one
|
||||
for (int i = 0; i < _textFrame.count; i++) {
|
||||
CFRelease(_textFrame.lines[0]);
|
||||
}
|
||||
free(_textFrame.lines);
|
||||
free(_textFrame.widths);
|
||||
if (frame.lines != _textFrame.lines) {
|
||||
ARTFreeTextFrame(_textFrame);
|
||||
}
|
||||
[self invalidate];
|
||||
_textFrame = frame;
|
||||
|
@ -35,14 +42,7 @@
|
|||
|
||||
- (void)dealloc
|
||||
{
|
||||
if (_textFrame.count) {
|
||||
// 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);
|
||||
}
|
||||
ARTFreeTextFrame(_textFrame);
|
||||
}
|
||||
|
||||
- (void)renderLayerTo:(CGContextRef)context
|
||||
|
|
|
@ -243,7 +243,7 @@ function checkValidInputRange(arr: Array<number>) {
|
|||
* mean this implicit string conversion, you can do something like
|
||||
* String(myThing)
|
||||
*/
|
||||
'inputRange must be monolithically increasing ' + arr
|
||||
'inputRange must be monotonically increasing ' + arr
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -105,6 +105,32 @@ var Image = React.createClass({
|
|||
* {nativeEvent: { layout: {x, y, width, height}}}.
|
||||
*/
|
||||
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: {
|
||||
|
@ -161,6 +187,7 @@ var Image = React.createClass({
|
|||
if (this.props.defaultSource) {
|
||||
nativeProps.defaultImageSrc = this.props.defaultSource.uri;
|
||||
}
|
||||
nativeProps.progressHandlerRegistered = isNetwork && this.props.onLoadProgress;
|
||||
return <RawImage {...nativeProps} />;
|
||||
}
|
||||
});
|
||||
|
@ -178,6 +205,7 @@ var nativeOnlyProps = {
|
|||
src: true,
|
||||
defaultImageSrc: true,
|
||||
imageTag: true,
|
||||
progressHandlerRegistered: true
|
||||
};
|
||||
if (__DEV__) {
|
||||
verifyPropTypes(Image, RCTStaticImage.viewConfig, nativeOnlyProps);
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -7,6 +7,7 @@
|
|||
objects = {
|
||||
|
||||
/* 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 */; };
|
||||
1304D5AC1AA8C4A30002E2BE /* RCTStaticImageManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 1304D5AA1AA8C4A30002E2BE /* RCTStaticImageManager.m */; };
|
||||
1304D5B21AA8C50D0002E2BE /* RCTGIFImage.m in Sources */ = {isa = PBXBuildFile; fileRef = 1304D5B11AA8C50D0002E2BE /* RCTGIFImage.m */; };
|
||||
|
@ -32,6 +33,8 @@
|
|||
/* End PBXCopyFilesBuildPhase 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>"; };
|
||||
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>"; };
|
||||
|
@ -71,6 +74,8 @@
|
|||
children = (
|
||||
143879331AAD238D00F088A5 /* RCTCameraRollManager.h */,
|
||||
143879341AAD238D00F088A5 /* RCTCameraRollManager.m */,
|
||||
03559E7D1B064D3A00730281 /* RCTDownloadTaskWrapper.h */,
|
||||
03559E7E1B064DAF00730281 /* RCTDownloadTaskWrapper.m */,
|
||||
1304D5B01AA8C50D0002E2BE /* RCTGIFImage.h */,
|
||||
1304D5B11AA8C50D0002E2BE /* RCTGIFImage.m */,
|
||||
58B511891A9E6BD600147676 /* RCTImageDownloader.h */,
|
||||
|
@ -168,6 +173,7 @@
|
|||
1304D5B21AA8C50D0002E2BE /* RCTGIFImage.m in Sources */,
|
||||
143879351AAD238D00F088A5 /* RCTCameraRollManager.m in Sources */,
|
||||
143879381AAD32A300F088A5 /* RCTImageLoader.m in Sources */,
|
||||
03559E7F1B064DAF00730281 /* RCTDownloadTaskWrapper.m in Sources */,
|
||||
1304D5AB1AA8C4A30002E2BE /* RCTStaticImage.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
|
|
|
@ -9,8 +9,11 @@
|
|||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#import "RCTDownloadTaskWrapper.h"
|
||||
|
||||
typedef void (^RCTDataDownloadBlock)(NSData *data, NSError *error);
|
||||
typedef void (^RCTImageDownloadBlock)(UIImage *image, NSError *error);
|
||||
typedef void (^RCTImageDownloadCancellationBlock)(void);
|
||||
|
||||
@interface RCTImageDownloader : NSObject
|
||||
|
||||
|
@ -21,7 +24,8 @@ typedef void (^RCTImageDownloadBlock)(UIImage *image, NSError *error);
|
|||
* 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.
|
||||
*/
|
||||
- (id)downloadDataForURL:(NSURL *)url
|
||||
- (RCTImageDownloadCancellationBlock)downloadDataForURL:(NSURL *)url
|
||||
progressBlock:(RCTDataProgressBlock)progressBlock
|
||||
block:(RCTDataDownloadBlock)block;
|
||||
|
||||
/**
|
||||
|
@ -30,11 +34,12 @@ typedef void (^RCTImageDownloadBlock)(UIImage *image, NSError *error);
|
|||
* 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.
|
||||
*/
|
||||
- (id)downloadImageForURL:(NSURL *)url
|
||||
- (RCTImageDownloadCancellationBlock)downloadImageForURL:(NSURL *)url
|
||||
size:(CGSize)size
|
||||
scale:(CGFloat)scale
|
||||
resizeMode:(UIViewContentMode)resizeMode
|
||||
backgroundColor:(UIColor *)backgroundColor
|
||||
progressBlock:(RCTDataProgressBlock)progressBlock
|
||||
block:(RCTImageDownloadBlock)block;
|
||||
|
||||
/**
|
||||
|
@ -42,6 +47,6 @@ typedef void (^RCTImageDownloadBlock)(UIImage *image, NSError *error);
|
|||
* same image, only the request that relates to the token passed will be
|
||||
* cancelled.
|
||||
*/
|
||||
- (void)cancelDownload:(id)downloadToken;
|
||||
- (void)cancelDownload:(RCTImageDownloadCancellationBlock)downloadToken;
|
||||
|
||||
@end
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
|
||||
#import "RCTImageDownloader.h"
|
||||
|
||||
#import "RCTDownloadTaskWrapper.h"
|
||||
#import "RCTLog.h"
|
||||
#import "RCTUtils.h"
|
||||
|
||||
|
@ -22,6 +23,7 @@ CGRect RCTClipRect(CGSize, CGFloat, CGSize, CGFloat, UIViewContentMode);
|
|||
NSURLCache *_cache;
|
||||
dispatch_queue_t _processingQueue;
|
||||
NSMutableDictionary *_pendingBlocks;
|
||||
RCTDownloadTaskWrapper *_downloadTaskWrapper;
|
||||
}
|
||||
|
||||
+ (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"];
|
||||
_processingQueue = dispatch_queue_create("com.facebook.React.DownloadProcessingQueue", DISPATCH_QUEUE_SERIAL);
|
||||
_pendingBlocks = [[NSMutableDictionary alloc] init];
|
||||
|
||||
NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
|
||||
_downloadTaskWrapper = [[RCTDownloadTaskWrapper alloc] initWithSessionConfiguration:config delegateQueue:nil];
|
||||
}
|
||||
|
||||
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 NSURLSessionDataTask *task = nil;
|
||||
__block NSURLSessionDownloadTask *task = nil;
|
||||
|
||||
dispatch_block_t cancel = ^{
|
||||
RCTImageDownloadCancellationBlock cancel = ^{
|
||||
cancelled = YES;
|
||||
|
||||
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) {
|
||||
runBlocks(NO, data, error);
|
||||
}
|
||||
|
@ -93,12 +99,12 @@ CGRect RCTClipRect(CGSize, CGFloat, CGSize, CGFloat, UIViewContentMode);
|
|||
if (response) {
|
||||
RCTImageDownloader *strongSelf = weakSelf;
|
||||
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;
|
||||
}];
|
||||
|
||||
[_cache getCachedResponseForDataTask:task completionHandler:^(NSCachedURLResponse *cachedResponse) {
|
||||
NSCachedURLResponse *cachedResponse = [_cache cachedResponseForRequest:request];
|
||||
if (cancelled) {
|
||||
return;
|
||||
}
|
||||
|
@ -108,28 +114,29 @@ CGRect RCTClipRect(CGSize, CGFloat, CGSize, CGFloat, UIViewContentMode);
|
|||
} else {
|
||||
[task resume];
|
||||
}
|
||||
}];
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
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);
|
||||
}];
|
||||
}
|
||||
|
||||
- (id)downloadImageForURL:(NSURL *)url
|
||||
- (RCTImageDownloadCancellationBlock)downloadImageForURL:(NSURL *)url
|
||||
size:(CGSize)size
|
||||
scale:(CGFloat)scale
|
||||
resizeMode:(UIViewContentMode)resizeMode
|
||||
backgroundColor:(UIColor *)backgroundColor
|
||||
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) {
|
||||
block(nil, error);
|
||||
return;
|
||||
|
@ -176,10 +183,10 @@ CGRect RCTClipRect(CGSize, CGFloat, CGSize, CGFloat, UIViewContentMode);
|
|||
}];
|
||||
}
|
||||
|
||||
- (void)cancelDownload:(id)downloadToken
|
||||
- (void)cancelDownload:(RCTImageDownloadCancellationBlock)downloadToken
|
||||
{
|
||||
if (downloadToken) {
|
||||
((dispatch_block_t)downloadToken)();
|
||||
downloadToken();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -121,7 +121,7 @@ static dispatch_queue_t RCTImageLoaderQueue(void)
|
|||
RCTDispatchCallbackOnMainQueue(callback, RCTErrorWithMessage(errorMessage), nil);
|
||||
return;
|
||||
}
|
||||
[[RCTImageDownloader sharedInstance] downloadDataForURL:url block:^(NSData *data, NSError *error) {
|
||||
[[RCTImageDownloader sharedInstance] downloadDataForURL:url progressBlock:nil block:^(NSData *data, NSError *error) {
|
||||
if (error) {
|
||||
RCTDispatchCallbackOnMainQueue(callback, error, nil);
|
||||
} else {
|
||||
|
|
|
@ -9,11 +9,13 @@
|
|||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@class RCTEventDispatcher;
|
||||
@class RCTImageDownloader;
|
||||
|
||||
@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,
|
||||
|
|
|
@ -14,23 +14,27 @@
|
|||
#import "RCTGIFImage.h"
|
||||
#import "RCTImageDownloader.h"
|
||||
#import "RCTUtils.h"
|
||||
#import "RCTBridgeModule.h"
|
||||
#import "RCTEventDispatcher.h"
|
||||
#import "UIView+React.h"
|
||||
|
||||
@implementation RCTNetworkImageView
|
||||
{
|
||||
BOOL _deferred;
|
||||
BOOL _progressHandlerRegistered;
|
||||
NSURL *_imageURL;
|
||||
NSURL *_deferredImageURL;
|
||||
NSUInteger _deferSentinel;
|
||||
RCTImageDownloader *_imageDownloader;
|
||||
id _downloadToken;
|
||||
RCTEventDispatcher *_eventDispatcher;
|
||||
}
|
||||
|
||||
- (instancetype)initWithImageDownloader:(RCTImageDownloader *)imageDownloader
|
||||
- (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher imageDownloader:(RCTImageDownloader *)imageDownloader
|
||||
{
|
||||
RCTAssertParam(imageDownloader);
|
||||
|
||||
if ((self = [super initWithFrame:CGRectZero])) {
|
||||
_eventDispatcher = eventDispatcher;
|
||||
_progressHandlerRegistered = NO;
|
||||
_deferSentinel = 0;
|
||||
_imageDownloader = imageDownloader;
|
||||
self.userInteractionEnabled = NO;
|
||||
|
@ -56,6 +60,11 @@ RCT_NOT_IMPLEMENTED(-initWithCoder:(NSCoder *)aDecoder)
|
|||
[self _updateImage];
|
||||
}
|
||||
|
||||
- (void)setProgressHandlerRegistered:(BOOL)progressHandlerRegistered
|
||||
{
|
||||
_progressHandlerRegistered = progressHandlerRegistered;
|
||||
}
|
||||
|
||||
- (void)reactSetFrame:(CGRect)frame
|
||||
{
|
||||
[super reactSetFrame:frame];
|
||||
|
@ -89,8 +98,34 @@ RCT_NOT_IMPLEMENTED(-initWithCoder:(NSCoder *)aDecoder)
|
|||
self.layer.minificationFilter = 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) {
|
||||
_downloadToken = [_imageDownloader downloadDataForURL:imageURL block:^(NSData *data, NSError *error) {
|
||||
_downloadToken = [_imageDownloader downloadDataForURL:imageURL progressBlock:progressHandler block:^(NSData *data, NSError *error) {
|
||||
if (data) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
if (imageURL != self.imageURL) {
|
||||
|
@ -102,13 +137,16 @@ RCT_NOT_IMPLEMENTED(-initWithCoder:(NSCoder *)aDecoder)
|
|||
self.layer.minificationFilter = kCAFilterLinear;
|
||||
self.layer.magnificationFilter = kCAFilterLinear;
|
||||
[self.layer addAnimation:animation forKey:@"contents"];
|
||||
loadEndHandler();
|
||||
});
|
||||
} else if (error) {
|
||||
RCTLogWarn(@"Unable to download image data. Error: %@", error);
|
||||
errorHandler([error description]);
|
||||
}
|
||||
}];
|
||||
} 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) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
if (imageURL != self.imageURL) {
|
||||
|
@ -118,9 +156,10 @@ RCT_NOT_IMPLEMENTED(-initWithCoder:(NSCoder *)aDecoder)
|
|||
[self.layer removeAnimationForKey:@"contents"];
|
||||
self.layer.contentsScale = image.scale;
|
||||
self.layer.contents = (__bridge id)image.CGImage;
|
||||
loadEndHandler();
|
||||
});
|
||||
} else if (error) {
|
||||
RCTLogWarn(@"Unable to download image. Error: %@", error);
|
||||
errorHandler([error description]);
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
|
|
@ -9,24 +9,38 @@
|
|||
|
||||
#import "RCTNetworkImageViewManager.h"
|
||||
|
||||
#import "RCTNetworkImageView.h"
|
||||
|
||||
#import "RCTBridge.h"
|
||||
#import "RCTConvert.h"
|
||||
#import "RCTUtils.h"
|
||||
|
||||
#import "RCTImageDownloader.h"
|
||||
#import "RCTNetworkImageView.h"
|
||||
#import "RCTUtils.h"
|
||||
|
||||
@implementation RCTNetworkImageViewManager
|
||||
|
||||
RCT_EXPORT_MODULE()
|
||||
|
||||
@synthesize bridge = _bridge;
|
||||
@synthesize methodQueue = _methodQueue;
|
||||
|
||||
- (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(src, imageURL, NSURL)
|
||||
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
|
||||
|
|
|
@ -222,7 +222,7 @@ RCT_NOT_IMPLEMENTED(-initWithCoder:(NSCoder *)aDecoder)
|
|||
|
||||
- (UIFont *)defaultPlaceholderFont
|
||||
{
|
||||
return [UIFont fontWithName:@"Helvetica" size:17];
|
||||
return [UIFont systemFontOfSize:17];
|
||||
}
|
||||
|
||||
- (UIColor *)defaultPlaceholderTextColor
|
||||
|
|
|
@ -26,6 +26,8 @@ let MODULE_IDS = 0;
|
|||
let METHOD_IDS = 1;
|
||||
let PARAMS = 2;
|
||||
|
||||
let SPY_MODE = false;
|
||||
|
||||
let MethodTypes = keyMirror({
|
||||
local: null,
|
||||
remote: null,
|
||||
|
@ -120,6 +122,10 @@ class MessageQueue {
|
|||
(this._debugInfo[this._callbackID >> 5] = null);
|
||||
|
||||
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);
|
||||
this._callbacks[this._callbackID++] = onFail;
|
||||
|
@ -137,6 +143,9 @@ class MessageQueue {
|
|||
method = this._methodTable[module][method];
|
||||
module = this._moduleTable[module];
|
||||
}
|
||||
if (__DEV__ && SPY_MODE) {
|
||||
console.log('N->JS : ' + module + '.' + method + '(' + JSON.stringify(args) + ')');
|
||||
}
|
||||
module = this._require(module);
|
||||
module[method].apply(module, args);
|
||||
BridgeProfiling.profileEnd();
|
||||
|
@ -146,11 +155,15 @@ class MessageQueue {
|
|||
BridgeProfiling.profile(
|
||||
() => `MessageQueue.invokeCallback(${cbID}, ${stringifySafe(args)})`);
|
||||
let callback = this._callbacks[cbID];
|
||||
if (__DEV__ && !callback) {
|
||||
if (__DEV__) {
|
||||
let debug = this._debugInfo[cbID >> 1];
|
||||
let module = this._remoteModuleTable[debug[0]];
|
||||
let method = this._remoteMethodTable[debug[0]][debug[1]];
|
||||
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;
|
||||
|
|
|
@ -38,7 +38,6 @@ typedef void (^RCTPromiseResolveBlock)(id result);
|
|||
*/
|
||||
typedef void (^RCTPromiseRejectBlock)(NSError *error);
|
||||
|
||||
|
||||
/**
|
||||
* This constant can be returned from +methodQueue to force module
|
||||
* methods to be called on the JavaScript thread. This can have serious
|
||||
|
|
|
@ -39,6 +39,7 @@
|
|||
+ (NSUInteger)NSUInteger:(id)json;
|
||||
|
||||
+ (NSArray *)NSArray:(id)json;
|
||||
+ (NSSet *)NSSet:(id)json;
|
||||
+ (NSDictionary *)NSDictionary:(id)json;
|
||||
+ (NSString *)NSString:(id)json;
|
||||
+ (NSNumber *)NSNumber:(id)json;
|
||||
|
|
|
@ -35,6 +35,7 @@ RCT_NUMBER_CONVERTER(NSInteger, integerValue)
|
|||
RCT_NUMBER_CONVERTER(NSUInteger, unsignedIntegerValue)
|
||||
|
||||
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_CONVERTER(NSString *, NSString, description)
|
||||
|
||||
|
@ -671,8 +672,8 @@ RCT_CGSTRUCT_CONVERTER(CGAffineTransform, (@[
|
|||
NSURL *url = [NSURL URLWithString:path];
|
||||
NSData *imageData = [NSData dataWithContentsOfURL:url];
|
||||
image = [UIImage imageWithData:imageData];
|
||||
} else if ([path isAbsolutePath]) {
|
||||
image = [UIImage imageWithContentsOfFile:path];
|
||||
} else if ([path isAbsolutePath] || [path hasPrefix:@"~"]) {
|
||||
image = [UIImage imageWithContentsOfFile:path.stringByExpandingTildeInPath];
|
||||
} else {
|
||||
image = [UIImage imageNamed:path];
|
||||
if (!image) {
|
||||
|
@ -788,7 +789,8 @@ static BOOL RCTFontIsCondensed(UIFont *font)
|
|||
size:(id)size weight:(id)weight style:(id)style
|
||||
{
|
||||
// Defaults
|
||||
NSString *const RCTDefaultFontFamily = @"Helvetica Neue";
|
||||
NSString *const RCTDefaultFontFamily = @"System";
|
||||
NSString *const RCTIOS8SystemFontFamily = @"Helvetica Neue";
|
||||
const RCTFontWeight RCTDefaultFontWeight = UIFontWeightRegular;
|
||||
const CGFloat RCTDefaultFontSize = 14;
|
||||
|
||||
|
@ -807,11 +809,36 @@ static BOOL RCTFontIsCondensed(UIFont *font)
|
|||
isCondensed = RCTFontIsCondensed(font);
|
||||
}
|
||||
|
||||
// Get font size
|
||||
// Get font attributes
|
||||
fontSize = [self CGFloat:size] ?: fontSize;
|
||||
|
||||
// Get font family
|
||||
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
|
||||
// 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,
|
||||
// but we'll do what was meant, not what was said.
|
||||
familyName = font.familyName;
|
||||
fontWeight = RCTWeightOfFont(font);
|
||||
isItalic = RCTFontIsItalic(font);
|
||||
fontWeight = weight ? fontWeight : RCTWeightOfFont(font);
|
||||
isItalic = style ? isItalic : RCTFontIsItalic(font);
|
||||
isCondensed = RCTFontIsCondensed(font);
|
||||
} else {
|
||||
// Not a valid font or family
|
||||
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
|
||||
UIFont *bestMatch = [UIFont fontWithName:font.fontName size: fontSize];
|
||||
UIFont *bestMatch = font;
|
||||
CGFloat closestWeight = INFINITY;
|
||||
|
||||
for (NSString *name in [UIFont fontNamesForFamilyName:familyName]) {
|
||||
UIFont *match = [UIFont fontWithName:name size:fontSize];
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -32,6 +32,8 @@
|
|||
NSArray *_argumentBlocks;
|
||||
}
|
||||
|
||||
RCT_NOT_IMPLEMENTED(-init)
|
||||
|
||||
- (instancetype)initWithObjCMethodName:(NSString *)objCMethodName
|
||||
JSMethodName:(NSString *)JSMethodName
|
||||
moduleClass:(Class)moduleClass
|
||||
|
|
|
@ -64,6 +64,8 @@ RCT_EXPORT_MODULE()
|
|||
|
||||
- (void)sendTimespans
|
||||
{
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
|
||||
[_bridge enqueueJSCall:@"PerformanceLogger.addTimespans" args:@[
|
||||
RCTPerformanceLoggerOutput(),
|
||||
@[
|
||||
|
|
|
@ -64,10 +64,12 @@
|
|||
"react-tools": "0.13.2",
|
||||
"rebound": "^0.0.12",
|
||||
"sane": "^1.1.2",
|
||||
"semver": "^4.3.6",
|
||||
"source-map": "0.1.31",
|
||||
"stacktrace-parser": "frantic/stacktrace-parser#493c5e5638",
|
||||
"uglify-js": "~2.4.16",
|
||||
"underscore": "1.7.0",
|
||||
"wordwrap": "^1.0.0",
|
||||
"worker-farm": "^1.3.1",
|
||||
"ws": "0.4.31",
|
||||
"yargs": "1.3.2"
|
||||
|
|
|
@ -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;
|
|
@ -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;
|
|
@ -23,7 +23,9 @@
|
|||
"lint": "node linter.js Examples/",
|
||||
"start": "./packager/packager.sh"
|
||||
},
|
||||
"dependencies": {},
|
||||
"dependencies": {
|
||||
"wordwrap": "^1.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"jest-cli": "git://github.com/facebook/jest#0.5.x",
|
||||
"eslint": "0.9.2"
|
||||
|
|
|
@ -30,6 +30,8 @@ var chalk = require('chalk');
|
|||
var connect = require('connect');
|
||||
var ReactPackager = require('./react-packager');
|
||||
var blacklist = require('./blacklist.js');
|
||||
var checkNodeVersion = require('./checkNodeVersion');
|
||||
var formatBanner = require('./formatBanner');
|
||||
var launchEditor = require('./launchEditor.js');
|
||||
var parseCommandLine = require('./parseCommandLine.js');
|
||||
var webSocketProxy = require('./webSocketProxy.js');
|
||||
|
@ -108,16 +110,19 @@ if (options.assetRoots) {
|
|||
}
|
||||
}
|
||||
|
||||
console.log('\n' +
|
||||
' ===============================================================\n' +
|
||||
' | Running packager on port ' + options.port + '. \n' +
|
||||
' | Keep this packager running while developing on any JS \n' +
|
||||
' | projects. Feel free to close this tab and run your own \n' +
|
||||
' | packager instance if you prefer. \n' +
|
||||
' | \n' +
|
||||
' | https://github.com/facebook/react-native \n' +
|
||||
' | \n' +
|
||||
' ===============================================================\n'
|
||||
checkNodeVersion();
|
||||
|
||||
console.log(formatBanner(
|
||||
'Running packager on port ' + options.port + '.\n'+
|
||||
'\n' +
|
||||
'Keep this packager running while developing on any JS projects. Feel free ' +
|
||||
'to close this tab and run your own packager instance if you prefer.\n' +
|
||||
'\n' +
|
||||
'https://github.com/facebook/react-native', {
|
||||
marginLeft: 1,
|
||||
marginRight: 1,
|
||||
paddingBottom: 1,
|
||||
})
|
||||
);
|
||||
|
||||
console.log(
|
||||
|
|