Updates for Fri 10 Jul
|
@ -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>
|
||||||
|
|
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)}
|
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>);
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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];
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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 = {
|
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;
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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]);
|
||||||
}
|
}
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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(),
|
||||||
@[
|
@[
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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/",
|
"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"
|
||||||
|
|
|
@ -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(
|
||||||
|
|