mirror of
https://github.com/status-im/react-native.git
synced 2025-01-12 02:24:23 +00:00
Updates from Wed Feb 18
- [reactnative] s/SpinnerIOS/ActivityIndicatorIOS/ | Dan Witte - [react-packager] Add a non-persistent mode for static builds | Amjad Masad - [React Native] Fix stored file rejection when initializing cache | Alex Akers - [React Native] Consolidate network requests in image downloader | Alex Akers - [React Native] Update RCTCache | Alex Akers - Converted all low-hanging RKBridgeModules in FBReactKit to RCTBridgeModules | Nick Lockwood
This commit is contained in:
parent
89db1a64aa
commit
c88a1cd9b8
@ -9,7 +9,7 @@ var {
|
||||
ListView,
|
||||
ListViewDataSource,
|
||||
ScrollView,
|
||||
SpinnerIOS,
|
||||
ActivityIndicatorIOS,
|
||||
StyleSheet,
|
||||
Text,
|
||||
TextInput,
|
||||
@ -215,7 +215,7 @@ var SearchScreen = React.createClass({
|
||||
if (!this.hasMore() || !this.state.isLoadingTail) {
|
||||
return <View style={styles.scrollSpinner} />;
|
||||
}
|
||||
return <SpinnerIOS style={styles.scrollSpinner} />;
|
||||
return <ActivityIndicatorIOS style={styles.scrollSpinner} />;
|
||||
},
|
||||
|
||||
renderRow: function(movie: Object) {
|
||||
@ -290,7 +290,7 @@ var SearchBar = React.createClass({
|
||||
onFocus={this.props.onFocus}
|
||||
style={styles.searchBarInput}
|
||||
/>
|
||||
<SpinnerIOS
|
||||
<ActivityIndicatorIOS
|
||||
animating={this.props.isLoading}
|
||||
style={styles.spinner}
|
||||
/>
|
||||
|
@ -1,19 +1,19 @@
|
||||
/**
|
||||
* Copyright 2004-present Facebook. All Rights Reserved.
|
||||
*
|
||||
* @providesModule SpinnerExample
|
||||
* @providesModule ActivityIndicatorExample
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var React = require('react-native');
|
||||
var {
|
||||
SpinnerIOS,
|
||||
ActivityIndicatorIOS,
|
||||
StyleSheet,
|
||||
TimerMixin,
|
||||
View,
|
||||
} = React;
|
||||
|
||||
var ToggleAnimatingSpinner = React.createClass({
|
||||
var ToggleAnimatingActivityIndicator = React.createClass({
|
||||
mixins: [TimerMixin],
|
||||
|
||||
getInitialState: function() {
|
||||
@ -38,17 +38,17 @@ var ToggleAnimatingSpinner = React.createClass({
|
||||
|
||||
render: function() {
|
||||
return (
|
||||
<SpinnerIOS
|
||||
<ActivityIndicatorIOS
|
||||
animating={this.state.animating}
|
||||
style={[styles.centering, {height: 80}]}
|
||||
size={SpinnerIOS.size.large}
|
||||
size={ActivityIndicatorIOS.size.large}
|
||||
/>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
exports.framework = 'React';
|
||||
exports.title = '<SpinnerIOS>';
|
||||
exports.title = '<ActivityIndicatorIOS>';
|
||||
exports.description = 'Animated loading indicators.';
|
||||
|
||||
exports.examples = [
|
||||
@ -56,7 +56,7 @@ exports.examples = [
|
||||
title: 'Default (small, white)',
|
||||
render: function() {
|
||||
return (
|
||||
<SpinnerIOS
|
||||
<ActivityIndicatorIOS
|
||||
style={[styles.centering, styles.gray, {height: 40}]}
|
||||
color="white"
|
||||
/>
|
||||
@ -68,10 +68,10 @@ exports.examples = [
|
||||
render: function() {
|
||||
return (
|
||||
<View>
|
||||
<SpinnerIOS
|
||||
<ActivityIndicatorIOS
|
||||
style={[styles.centering, {height: 40}]}
|
||||
/>
|
||||
<SpinnerIOS
|
||||
<ActivityIndicatorIOS
|
||||
style={[styles.centering, {backgroundColor: '#eeeeee', height: 40}]}
|
||||
/>
|
||||
</View>
|
||||
@ -83,10 +83,10 @@ exports.examples = [
|
||||
render: function() {
|
||||
return (
|
||||
<View style={styles.horizontal}>
|
||||
<SpinnerIOS color="#0000ff" />
|
||||
<SpinnerIOS color="#aa00aa" />
|
||||
<SpinnerIOS color="#aa3300" />
|
||||
<SpinnerIOS color="#00aa00" />
|
||||
<ActivityIndicatorIOS color="#0000ff" />
|
||||
<ActivityIndicatorIOS color="#aa00aa" />
|
||||
<ActivityIndicatorIOS color="#aa3300" />
|
||||
<ActivityIndicatorIOS color="#00aa00" />
|
||||
</View>
|
||||
);
|
||||
}
|
||||
@ -95,10 +95,10 @@ exports.examples = [
|
||||
title: 'Large',
|
||||
render: function() {
|
||||
return (
|
||||
<SpinnerIOS
|
||||
<ActivityIndicatorIOS
|
||||
style={[styles.centering, styles.gray, {height: 80}]}
|
||||
color="white"
|
||||
size={SpinnerIOS.size.large}
|
||||
size={ActivityIndicatorIOS.size.large}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@ -108,20 +108,20 @@ exports.examples = [
|
||||
render: function() {
|
||||
return (
|
||||
<View style={styles.horizontal}>
|
||||
<SpinnerIOS
|
||||
size={SpinnerIOS.size.large}
|
||||
<ActivityIndicatorIOS
|
||||
size={ActivityIndicatorIOS.size.large}
|
||||
color="#0000ff"
|
||||
/>
|
||||
<SpinnerIOS
|
||||
size={SpinnerIOS.size.large}
|
||||
<ActivityIndicatorIOS
|
||||
size={ActivityIndicatorIOS.size.large}
|
||||
color="#aa00aa"
|
||||
/>
|
||||
<SpinnerIOS
|
||||
size={SpinnerIOS.size.large}
|
||||
<ActivityIndicatorIOS
|
||||
size={ActivityIndicatorIOS.size.large}
|
||||
color="#aa3300"
|
||||
/>
|
||||
<SpinnerIOS
|
||||
size={SpinnerIOS.size.large}
|
||||
<ActivityIndicatorIOS
|
||||
size={ActivityIndicatorIOS.size.large}
|
||||
color="#00aa00"
|
||||
/>
|
||||
</View>
|
||||
@ -131,7 +131,7 @@ exports.examples = [
|
||||
{
|
||||
title: 'Start/stop',
|
||||
render: function() {
|
||||
return <ToggleAnimatingSpinner />;
|
||||
return <ToggleAnimatingActivityIndicator />;
|
||||
}
|
||||
},
|
||||
];
|
@ -29,7 +29,7 @@ var EXAMPLES = [
|
||||
require('./StatusBarIOSExample'),
|
||||
require('./PointerEventsExample'),
|
||||
require('./TouchableExample'),
|
||||
require('./SpinnerExample'),
|
||||
require('./ActivityIndicatorExample'),
|
||||
require('./ScrollViewExample'),
|
||||
];
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
/**
|
||||
* Copyright 2004-present Facebook. All Rights Reserved.
|
||||
*
|
||||
* @providesModule SpinnerIOS
|
||||
* @providesModule ActivityIndicatorIOS
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
@ -25,7 +25,7 @@ var SpinnerSize = keyMirror({
|
||||
|
||||
var GRAY = '#999999';
|
||||
|
||||
var SpinnerIOS = React.createClass({
|
||||
var ActivityIndicatorIOS = React.createClass({
|
||||
mixins: [NativeMethodsMixin],
|
||||
|
||||
propTypes: {
|
||||
@ -39,8 +39,8 @@ var SpinnerIOS = React.createClass({
|
||||
color: PropTypes.string,
|
||||
/**
|
||||
* The size of the spinner, must be one of:
|
||||
* - SpinnerIOS.size.large
|
||||
* - SpinnerIOS.size.small (default)
|
||||
* - ActivityIndicatorIOS.size.large
|
||||
* - ActivityIndicatorIOS.size.small (default)
|
||||
*/
|
||||
size: PropTypes.oneOf([SpinnerSize.large, SpinnerSize.small]),
|
||||
},
|
||||
@ -101,4 +101,4 @@ var UIActivityIndicatorView = createReactIOSNativeComponentClass({
|
||||
uiViewClassName: 'UIActivityIndicatorView',
|
||||
});
|
||||
|
||||
module.exports = SpinnerIOS;
|
||||
module.exports = ActivityIndicatorIOS;
|
@ -11,7 +11,7 @@ declare module "react-native" {
|
||||
declare var NavigatorItemIOS: ReactClass<any, any, any>;
|
||||
declare var PixelRatio: ReactClass<any, any, any>;
|
||||
declare var ScrollView: ReactClass<any, any, any>;
|
||||
declare var SpinnerIOS: ReactClass<any, any, any>;
|
||||
declare var ActivityIndicatorIOS: ReactClass<any, any, any>;
|
||||
declare var StyleSheet: ReactClass<any, any, any>;
|
||||
declare var Text: ReactClass<any, any, any>;
|
||||
declare var TextInput: ReactClass<any, any, any>;
|
||||
|
2
Libraries/react-native/react-native.js
vendored
2
Libraries/react-native/react-native.js
vendored
@ -16,7 +16,7 @@ var ReactNative = {
|
||||
NavigatorIOS: require('NavigatorIOS'),
|
||||
PixelRatio: require('PixelRatio'),
|
||||
ScrollView: require('ScrollView'),
|
||||
SpinnerIOS: require('SpinnerIOS'),
|
||||
ActivityIndicatorIOS: require('ActivityIndicatorIOS'),
|
||||
StatusBarIOS: require('StatusBarIOS'),
|
||||
StyleSheet: require('StyleSheet'),
|
||||
Text: require('Text'),
|
||||
|
@ -235,8 +235,9 @@ static NSDictionary *RCTRemoteModulesConfig()
|
||||
RCTRemoteModulesByID = [[NSMutableDictionary alloc] init];
|
||||
|
||||
remoteModules = [[NSMutableDictionary alloc] init];
|
||||
[RCTExportedMethodsByModule() enumerateKeysAndObjectsUsingBlock:^(NSString *moduleName, NSArray *rawMethods, BOOL *stop) {
|
||||
|
||||
[RCTBridgeModuleClasses() enumerateKeysAndObjectsUsingBlock:^(NSString *moduleName, Class moduleClass, BOOL *stop) {
|
||||
|
||||
NSArray *rawMethods = RCTExportedMethodsByModule()[moduleName];
|
||||
NSMutableDictionary *methods = [NSMutableDictionary dictionaryWithCapacity:rawMethods.count];
|
||||
[rawMethods enumerateObjectsUsingBlock:^(RCTModuleMethod *method, NSUInteger methodID, BOOL *stop) {
|
||||
methods[method.JSMethodName] = @{
|
||||
@ -249,13 +250,15 @@ static NSDictionary *RCTRemoteModulesConfig()
|
||||
@"moduleID": @(remoteModules.count),
|
||||
@"methods": methods
|
||||
};
|
||||
|
||||
Class cls = RCTBridgeModuleClasses()[moduleName];
|
||||
if (RCTClassOverridesClassMethod(cls, @selector(constantsToExport))) {
|
||||
module = [module mutableCopy];
|
||||
((NSMutableDictionary *)module)[@"constants"] = [cls constantsToExport];
|
||||
|
||||
if (RCTClassOverridesClassMethod(moduleClass, @selector(constantsToExport))) {
|
||||
NSDictionary *constants = [moduleClass constantsToExport];
|
||||
if (constants.count) {
|
||||
module = [module mutableCopy];
|
||||
((NSMutableDictionary *)module)[@"constants"] = constants;
|
||||
}
|
||||
}
|
||||
remoteModules[moduleName] = module;
|
||||
remoteModules[moduleName] = [module copy];
|
||||
|
||||
// Add module lookup
|
||||
RCTRemoteModulesByID[module[@"moduleID"]] = moduleName;
|
||||
@ -303,13 +306,13 @@ static NSDictionary *RCTLocalModulesConfig()
|
||||
// Add globally used methods
|
||||
[JSMethods addObjectsFromArray:@[
|
||||
@"Bundler.runApplication",
|
||||
@"RCTDeviceEventEmitter.emit",
|
||||
@"RCTEventEmitter.receiveEvent",
|
||||
@"RCTEventEmitter.receiveTouches",
|
||||
]];
|
||||
|
||||
// NOTE: these methods are currently unused in the OSS project
|
||||
// @"Dimensions.set",
|
||||
// @"RCTDeviceEventEmitter.emit",
|
||||
// @"RCTNativeAppEventEmitter.emit",
|
||||
// @"ReactIOS.unmountComponentAtNodeAndRemoveContainer",
|
||||
|
||||
@ -376,10 +379,15 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
||||
_moduleInstances = [[NSMutableDictionary alloc] init];
|
||||
[RCTBridgeModuleClasses() enumerateKeysAndObjectsUsingBlock:^(NSString *moduleName, Class moduleClass, BOOL *stop) {
|
||||
if (_moduleInstances[moduleName] == nil) {
|
||||
id<RCTBridgeModule> moduleInstance;
|
||||
if ([moduleClass instancesRespondToSelector:@selector(initWithBridge:)]) {
|
||||
_moduleInstances[moduleName] = [[moduleClass alloc] initWithBridge:self];
|
||||
moduleInstance = [[moduleClass alloc] initWithBridge:self];
|
||||
} else {
|
||||
_moduleInstances[moduleName] = [[moduleClass alloc] init];
|
||||
moduleInstance = [[moduleClass alloc] init];
|
||||
}
|
||||
if (moduleInstance) {
|
||||
// If nil, the module doesn't support auto-instantiation
|
||||
_moduleInstances[moduleName] = moduleInstance;
|
||||
}
|
||||
}
|
||||
}];
|
||||
@ -522,7 +530,7 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
||||
}
|
||||
|
||||
if (![buffer isKindOfClass:[NSArray class]]) {
|
||||
RCTLogMustFix(@"Buffer must be an instance of NSArray, got %@", NSStringFromClass([buffer class]));
|
||||
RCTLogError(@"Buffer must be an instance of NSArray, got %@", NSStringFromClass([buffer class]));
|
||||
return;
|
||||
}
|
||||
|
||||
@ -530,14 +538,14 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
||||
NSUInteger bufferRowCount = [requestsArray count];
|
||||
NSUInteger expectedFieldsCount = RCTBridgeFieldResponseReturnValues + 1;
|
||||
if (bufferRowCount != expectedFieldsCount) {
|
||||
RCTLogMustFix(@"Must pass all fields to buffer - expected %zd, saw %zd", expectedFieldsCount, bufferRowCount);
|
||||
RCTLogError(@"Must pass all fields to buffer - expected %zd, saw %zd", expectedFieldsCount, bufferRowCount);
|
||||
return;
|
||||
}
|
||||
|
||||
for (NSUInteger fieldIndex = RCTBridgeFieldRequestModuleIDs; fieldIndex <= RCTBridgeFieldParamss; fieldIndex++) {
|
||||
id field = [requestsArray objectAtIndex:fieldIndex];
|
||||
if (![field isKindOfClass:[NSArray class]]) {
|
||||
RCTLogMustFix(@"Field at index %zd in buffer must be an instance of NSArray, got %@", fieldIndex, NSStringFromClass([field class]));
|
||||
RCTLogError(@"Field at index %zd in buffer must be an instance of NSArray, got %@", fieldIndex, NSStringFromClass([field class]));
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -549,7 +557,7 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
||||
NSUInteger numRequests = [moduleIDs count];
|
||||
BOOL allSame = numRequests == [methodIDs count] && numRequests == [paramsArrays count];
|
||||
if (!allSame) {
|
||||
RCTLogMustFix(@"Invalid data message - all must be length: %zd", numRequests);
|
||||
RCTLogError(@"Invalid data message - all must be length: %zd", numRequests);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -578,30 +586,30 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
||||
params:(NSArray *)params
|
||||
{
|
||||
if (![params isKindOfClass:[NSArray class]]) {
|
||||
RCTLogMustFix(@"Invalid module/method/params tuple for request #%zd", i);
|
||||
RCTLogError(@"Invalid module/method/params tuple for request #%zd", i);
|
||||
return NO;
|
||||
}
|
||||
|
||||
NSString *moduleName = RCTRemoteModulesByID[moduleID];
|
||||
if (!moduleName) {
|
||||
RCTLogMustFix(@"Unknown moduleID: %@", moduleID);
|
||||
RCTLogError(@"Unknown moduleID: %@", moduleID);
|
||||
return NO;
|
||||
}
|
||||
|
||||
NSArray *methods = RCTExportedMethodsByModule()[moduleName];
|
||||
if (methodID >= methods.count) {
|
||||
RCTLogMustFix(@"Unknown methodID: %zd for module: %@", methodID, moduleName);
|
||||
RCTLogError(@"Unknown methodID: %zd for module: %@", methodID, moduleName);
|
||||
return NO;
|
||||
}
|
||||
|
||||
RCTModuleMethod *method = methods[methodID];
|
||||
NSUInteger methodArity = method.arity;
|
||||
if (params.count != methodArity) {
|
||||
RCTLogMustFix(@"Expected %tu arguments but got %tu invoking %@.%@",
|
||||
methodArity,
|
||||
params.count,
|
||||
moduleName,
|
||||
method.JSMethodName);
|
||||
RCTLogError(@"Expected %tu arguments but got %tu invoking %@.%@",
|
||||
methodArity,
|
||||
params.count,
|
||||
moduleName,
|
||||
method.JSMethodName);
|
||||
return NO;
|
||||
}
|
||||
|
||||
@ -663,13 +671,13 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
||||
// TODO: it seems like an error if the param doesn't respond
|
||||
// so we should probably surface that error rather than failing silently
|
||||
#define CASE(_value, _type, _selector) \
|
||||
case _value: \
|
||||
if ([param respondsToSelector:@selector(_selector)]) { \
|
||||
_type value = [param _selector]; \
|
||||
[invocation setArgument:&value atIndex:argIdx]; \
|
||||
shouldSet = NO; \
|
||||
} \
|
||||
break;
|
||||
case _value: \
|
||||
if ([param respondsToSelector:@selector(_selector)]) { \
|
||||
_type value = [param _selector]; \
|
||||
[invocation setArgument:&value atIndex:argIdx]; \
|
||||
shouldSet = NO; \
|
||||
} \
|
||||
break;
|
||||
|
||||
CASE('c', char, charValue)
|
||||
CASE('C', unsigned char, unsignedCharValue)
|
||||
@ -698,7 +706,7 @@ break;
|
||||
[invocation invoke];
|
||||
}
|
||||
@catch (NSException *exception) {
|
||||
RCTLogMustFix(@"Exception thrown while invoking %@ on target %@ with params %@: %@", method.JSMethodName, target, params, exception);
|
||||
RCTLogError(@"Exception thrown while invoking %@ on target %@ with params %@: %@", method.JSMethodName, target, params, exception);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -5,21 +5,90 @@
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <sys/xattr.h>
|
||||
|
||||
static NSString *const CacheSubdirectoryName = @"ReactKit";
|
||||
static NSString *const KeyExtendedAttributeName = @"com.facebook.ReactKit.RCTCacheManager.Key";
|
||||
static dispatch_queue_t Queue;
|
||||
static NSString *const RCTCacheSubdirectoryName = @"ReactKit";
|
||||
static NSString *const RCTKeyExtendedAttributeName = @"com.facebook.ReactKit.RCTCacheManager.Key";
|
||||
static NSMapTable *RCTLivingCachesByName;
|
||||
|
||||
static NSError *RCTPOSIXError(int errorNumber)
|
||||
{
|
||||
NSDictionary *userInfo = @{
|
||||
NSLocalizedDescriptionKey: @(strerror(errorNumber))
|
||||
};
|
||||
return [NSError errorWithDomain:NSPOSIXErrorDomain code:errorNumber userInfo:userInfo];
|
||||
}
|
||||
|
||||
static NSString *RCTGetExtendedAttribute(NSURL *fileURL, NSString *key, NSError **error)
|
||||
{
|
||||
const char *path = fileURL.fileSystemRepresentation;
|
||||
ssize_t length = getxattr(path, key.UTF8String, NULL, 0, 0, 0);
|
||||
if (length <= 0) {
|
||||
if (error) *error = RCTPOSIXError(errno);
|
||||
return nil;
|
||||
}
|
||||
|
||||
char *buffer = malloc(length);
|
||||
length = getxattr(path, key.UTF8String, buffer, length, 0, 0);
|
||||
if (length > 0) {
|
||||
return [[NSString alloc] initWithBytesNoCopy:buffer length:length encoding:NSUTF8StringEncoding freeWhenDone:YES];
|
||||
}
|
||||
|
||||
free(buffer);
|
||||
if (error) *error = RCTPOSIXError(errno);
|
||||
return nil;
|
||||
}
|
||||
|
||||
static BOOL RCTSetExtendedAttribute(NSURL *fileURL, NSString *key, NSString *value, NSError **error)
|
||||
{
|
||||
const char *path = fileURL.fileSystemRepresentation;
|
||||
|
||||
int result;
|
||||
if (value) {
|
||||
const char *valueUTF8String = value.UTF8String;
|
||||
result = setxattr(path, key.UTF8String, valueUTF8String, strlen(valueUTF8String), 0, 0);
|
||||
} else {
|
||||
result = removexattr(path, key.UTF8String, 0);
|
||||
}
|
||||
|
||||
if (result) {
|
||||
if (error) *error = RCTPOSIXError(errno);
|
||||
return NO;
|
||||
}
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
#pragma mark - Cache Record -
|
||||
|
||||
@interface RCTCacheRecord : NSObject
|
||||
|
||||
@property (nonatomic, copy) NSUUID *UUID;
|
||||
@property (readonly) NSUUID *UUID;
|
||||
@property (readonly, weak) dispatch_queue_t queue;
|
||||
@property (nonatomic, copy) NSData *data;
|
||||
|
||||
@end
|
||||
|
||||
@implementation RCTCacheRecord
|
||||
|
||||
- (instancetype)initWithUUID:(NSUUID *)UUID
|
||||
{
|
||||
if ((self = [super init])) {
|
||||
_UUID = [UUID copy];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)enqueueBlock:(dispatch_block_t)block
|
||||
{
|
||||
dispatch_queue_t queue = _queue;
|
||||
if (!queue) {
|
||||
NSString *queueName = [NSString stringWithFormat:@"com.facebook.ReactKit.RCTCache.%@", _UUID.UUIDString];
|
||||
queue = dispatch_queue_create(queueName.UTF8String, DISPATCH_QUEUE_SERIAL);
|
||||
_queue = queue;
|
||||
}
|
||||
|
||||
dispatch_async(queue, block);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark - Cache
|
||||
@ -35,9 +104,10 @@ static dispatch_queue_t Queue;
|
||||
+ (void)initialize
|
||||
{
|
||||
if (self == [RCTCache class]) {
|
||||
Queue = dispatch_queue_create("com.facebook.ReactKit.RCTCache", DISPATCH_QUEUE_SERIAL);
|
||||
RCTLivingCachesByName = [NSMapTable strongToWeakObjectsMapTable];
|
||||
}
|
||||
}
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
return [self initWithName:@"default"];
|
||||
@ -46,46 +116,39 @@ static dispatch_queue_t Queue;
|
||||
- (instancetype)initWithName:(NSString *)name
|
||||
{
|
||||
NSParameterAssert(name.length < NAME_MAX);
|
||||
RCTCache *cachedCache = [RCTLivingCachesByName objectForKey:name];
|
||||
if (cachedCache) {
|
||||
self = cachedCache;
|
||||
return self;
|
||||
}
|
||||
|
||||
if ((self = [super init])) {
|
||||
_name = [name copy];
|
||||
_fileManager = [[NSFileManager alloc] init];
|
||||
_storage = [NSMutableDictionary dictionary];
|
||||
|
||||
NSURL *cacheDirectoryURL = [[_fileManager URLsForDirectory:NSCachesDirectory inDomains:NSUserDomainMask] lastObject];
|
||||
cacheDirectoryURL = [cacheDirectoryURL URLByAppendingPathComponent:CacheSubdirectoryName isDirectory:YES];
|
||||
cacheDirectoryURL = [cacheDirectoryURL URLByAppendingPathComponent:RCTCacheSubdirectoryName isDirectory:YES];
|
||||
_cacheDirectoryURL = [cacheDirectoryURL URLByAppendingPathComponent:name isDirectory:YES];
|
||||
[_fileManager createDirectoryAtURL:_cacheDirectoryURL withIntermediateDirectories:YES attributes:nil error:NULL];
|
||||
|
||||
NSArray *fileURLs = [_fileManager contentsOfDirectoryAtURL:_cacheDirectoryURL includingPropertiesForKeys:nil options:NSDirectoryEnumerationSkipsHiddenFiles error:NULL];
|
||||
for (NSURL *fileURL in fileURLs) {
|
||||
NSUUID *uuid = [[NSUUID alloc] initWithUUIDString:fileURL.lastPathComponent];
|
||||
if (!uuid) continue;
|
||||
NSUUID *UUID = [[NSUUID alloc] initWithUUIDString:fileURL.lastPathComponent];
|
||||
if (!UUID) continue;
|
||||
|
||||
NSString *key = [self keyOfItemAtURL:fileURL error:NULL];
|
||||
NSString *key = RCTGetExtendedAttribute(fileURL, RCTKeyExtendedAttributeName, NULL);
|
||||
if (!key) {
|
||||
[_fileManager removeItemAtURL:fileURL error:NULL];
|
||||
continue;
|
||||
}
|
||||
|
||||
RCTCacheRecord *record = [[RCTCacheRecord alloc] init];
|
||||
record.UUID = uuid;
|
||||
_storage[key] = record;
|
||||
_storage[key] = [[RCTCacheRecord alloc] initWithUUID:UUID];
|
||||
}
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)runOnQueue:(dispatch_block_t)block
|
||||
{
|
||||
UIBackgroundTaskIdentifier identifier = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:nil];
|
||||
dispatch_async(Queue, ^{
|
||||
if (block) block();
|
||||
if (identifier != UIBackgroundTaskInvalid) {
|
||||
[[UIApplication sharedApplication] endBackgroundTask:identifier];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
- (BOOL)hasDataForKey:(NSString *)key
|
||||
{
|
||||
return _storage[key] != nil;
|
||||
@ -95,115 +158,67 @@ static dispatch_queue_t Queue;
|
||||
{
|
||||
NSParameterAssert(key.length > 0);
|
||||
NSParameterAssert(completionHandler != nil);
|
||||
[self runOnQueue:^{
|
||||
RCTCacheRecord *record = _storage[key];
|
||||
if (record && !record.data) {
|
||||
RCTCacheRecord *record = _storage[key];
|
||||
if (!record) {
|
||||
completionHandler(nil);
|
||||
return;
|
||||
}
|
||||
|
||||
[record enqueueBlock:^{
|
||||
if (!record.data) {
|
||||
record.data = [NSData dataWithContentsOfURL:[_cacheDirectoryURL URLByAppendingPathComponent:record.UUID.UUIDString]];
|
||||
}
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
completionHandler(record.data);
|
||||
});
|
||||
completionHandler(record.data);
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)setData:(NSData *)data forKey:(NSString *)key
|
||||
{
|
||||
NSParameterAssert(key.length > 0);
|
||||
[self runOnQueue:^{
|
||||
RCTCacheRecord *record = _storage[key];
|
||||
RCTCacheRecord *record = _storage[key];
|
||||
if (!record) {
|
||||
if (!data) return;
|
||||
|
||||
record = [[RCTCacheRecord alloc] initWithUUID:[NSUUID UUID]];
|
||||
_storage[key] = record;
|
||||
}
|
||||
|
||||
NSURL *fileURL = [_cacheDirectoryURL URLByAppendingPathComponent:record.UUID.UUIDString];
|
||||
|
||||
UIBackgroundTaskIdentifier identifier = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:nil];
|
||||
[record enqueueBlock:^{
|
||||
if (data) {
|
||||
if (!record) {
|
||||
record = [[RCTCacheRecord alloc] init];
|
||||
record.UUID = [NSUUID UUID];
|
||||
_storage[key] = record;
|
||||
}
|
||||
|
||||
record.data = data;
|
||||
|
||||
NSURL *fileURL = [_cacheDirectoryURL URLByAppendingPathComponent:record.UUID.UUIDString];
|
||||
[data writeToURL:fileURL options:NSDataWritingAtomic error:NULL];
|
||||
} else if (record) {
|
||||
[_storage removeObjectForKey:key];
|
||||
|
||||
NSURL *fileURL = [_cacheDirectoryURL URLByAppendingPathComponent:record.UUID.UUIDString];
|
||||
RCTSetExtendedAttribute(fileURL, RCTKeyExtendedAttributeName, key, NULL);
|
||||
} else {
|
||||
[_fileManager removeItemAtURL:fileURL error:NULL];
|
||||
}
|
||||
|
||||
if (identifier != UIBackgroundTaskInvalid) {
|
||||
[[UIApplication sharedApplication] endBackgroundTask:identifier];
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)removeAllData
|
||||
{
|
||||
[self runOnQueue:^{
|
||||
[_storage removeAllObjects];
|
||||
UIBackgroundTaskIdentifier identifier = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:nil];
|
||||
dispatch_group_t group = dispatch_group_create();
|
||||
|
||||
NSDirectoryEnumerator *enumerator = [_fileManager enumeratorAtURL:_cacheDirectoryURL includingPropertiesForKeys:nil options:NSDirectoryEnumerationSkipsHiddenFiles errorHandler:nil];
|
||||
for (NSURL *fileURL in enumerator) {
|
||||
[_storage enumerateKeysAndObjectsUsingBlock:^(NSString *key, RCTCacheRecord *record, BOOL *stop) {
|
||||
NSURL *fileURL = [_cacheDirectoryURL URLByAppendingPathComponent:record.UUID.UUIDString];
|
||||
dispatch_group_async(group, record.queue, ^{
|
||||
[_fileManager removeItemAtURL:fileURL error:NULL];
|
||||
}
|
||||
});
|
||||
}];
|
||||
}
|
||||
|
||||
#pragma mark - Extended Attributes
|
||||
|
||||
- (NSError *)errorWithPOSIXErrorNumber:(int)errorNumber
|
||||
{
|
||||
NSDictionary *userInfo = @{
|
||||
NSLocalizedDescriptionKey: @(strerror(errorNumber))
|
||||
};
|
||||
return [NSError errorWithDomain:NSPOSIXErrorDomain code:errorNumber userInfo:userInfo];
|
||||
}
|
||||
|
||||
- (BOOL)setAttribute:(NSString *)key value:(NSString *)value ofItemAtURL:(NSURL *)fileURL error:(NSError **)error
|
||||
{
|
||||
const char *path = fileURL.fileSystemRepresentation;
|
||||
|
||||
int result;
|
||||
if (value) {
|
||||
const char *valueUTF8String = value.UTF8String;
|
||||
result = setxattr(path, key.UTF8String, valueUTF8String, strlen(valueUTF8String), 0, 0);
|
||||
} else {
|
||||
result = removexattr(path, key.UTF8String, 0);
|
||||
if (identifier != UIBackgroundTaskInvalid) {
|
||||
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
|
||||
[[UIApplication sharedApplication] endBackgroundTask:identifier];
|
||||
});
|
||||
}
|
||||
|
||||
if (result) {
|
||||
if (error) *error = [self errorWithPOSIXErrorNumber:errno];
|
||||
return NO;
|
||||
}
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (NSString *)attribute:(NSString *)key ofItemAtURL:(NSURL *)fileURL error:(NSError **)error
|
||||
{
|
||||
const char *path = fileURL.fileSystemRepresentation;
|
||||
const ssize_t length = getxattr(path, key.UTF8String, NULL, 0, 0, 0);
|
||||
if (length <= 0) {
|
||||
if (error) *error = [self errorWithPOSIXErrorNumber:errno];
|
||||
return nil;
|
||||
}
|
||||
|
||||
char *buffer = malloc(length);
|
||||
ssize_t result = getxattr(path, key.UTF8String, buffer, length, 0, 0);
|
||||
if (result == 0) {
|
||||
return [[NSString alloc] initWithBytesNoCopy:buffer length:length encoding:NSUTF8StringEncoding freeWhenDone:YES];
|
||||
}
|
||||
|
||||
free(buffer);
|
||||
if (error) *error = [self errorWithPOSIXErrorNumber:errno];
|
||||
return nil;
|
||||
}
|
||||
|
||||
#pragma mark - Extended Attributes - Key
|
||||
|
||||
- (NSString *)keyOfItemAtURL:(NSURL *)fileURL error:(NSError **)error
|
||||
{
|
||||
return [self attribute:KeyExtendedAttributeName ofItemAtURL:fileURL error:error];
|
||||
}
|
||||
|
||||
- (BOOL)setKey:(NSString *)key ofItemAtURL:(NSURL *)fileURL error:(NSError **)error
|
||||
{
|
||||
return [self setAttribute:KeyExtendedAttributeName value:key ofItemAtURL:fileURL error:error];
|
||||
[_storage removeAllObjects];
|
||||
}
|
||||
|
||||
@end
|
||||
|
@ -21,27 +21,36 @@ typedef NS_ENUM(NSInteger, RCTScrollEventType) {
|
||||
RCTScrollEventTypeEndAnimation,
|
||||
};
|
||||
|
||||
/**
|
||||
* This class wraps the -[RCTBridge enqueueJSCall:args:] method, and
|
||||
* provides some convenience methods for generating event calls.
|
||||
*/
|
||||
@interface RCTEventDispatcher : NSObject
|
||||
|
||||
- (instancetype)initWithBridge:(RCTBridge *)bridge;
|
||||
|
||||
/**
|
||||
* Send a named event. For most purposes, use the an
|
||||
* event type of RCTEventTypeDefault, the other types
|
||||
* are used internally by the React framework.
|
||||
* Send a device or application event that does not relate to a specific
|
||||
* view, e.g. rotation, location, keyboard show/hide, background/awake, etc.
|
||||
*/
|
||||
- (void)sendEventWithName:(NSString *)name body:(NSDictionary *)body;
|
||||
- (void)sendDeviceEventWithName:(NSString *)name body:(NSDictionary *)body;
|
||||
|
||||
/**
|
||||
* Send text events
|
||||
* Send a user input event. The body dictionary must contain a "target"
|
||||
* parameter, representing the react tag of the view sending the event
|
||||
*/
|
||||
- (void)sendInputEventWithName:(NSString *)name body:(NSDictionary *)body;
|
||||
|
||||
/**
|
||||
* Send a text input/focus event.
|
||||
*/
|
||||
- (void)sendTextEventWithType:(RCTTextEventType)type
|
||||
reactTag:(NSNumber *)reactTag
|
||||
text:(NSString *)text;
|
||||
|
||||
/**
|
||||
* Send scroll events
|
||||
* (You can send a fake scroll event by passing nil for scrollView)
|
||||
* Send a scroll event.
|
||||
* (You can send a fake scroll event by passing nil for scrollView).
|
||||
*/
|
||||
- (void)sendScrollEventWithType:(RCTScrollEventType)type
|
||||
reactTag:(NSNumber *)reactTag
|
||||
|
@ -4,7 +4,6 @@
|
||||
|
||||
#import "RCTAssert.h"
|
||||
#import "RCTBridge.h"
|
||||
#import "UIView+ReactKit.h"
|
||||
|
||||
@implementation RCTEventDispatcher
|
||||
{
|
||||
@ -19,7 +18,14 @@
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)sendEventWithName:(NSString *)name body:(NSDictionary *)body
|
||||
- (void)sendDeviceEventWithName:(NSString *)name body:(NSDictionary *)body
|
||||
{
|
||||
[_bridge enqueueJSCall:@"RCTDeviceEventEmitter.emit"
|
||||
args:body ? @[name, body] : @[name]];
|
||||
}
|
||||
|
||||
|
||||
- (void)sendInputEventWithName:(NSString *)name body:(NSDictionary *)body
|
||||
{
|
||||
RCTAssert([body[@"target"] isKindOfClass:[NSNumber class]],
|
||||
@"Event body dictionary must include a 'target' property containing a react tag");
|
||||
@ -40,7 +46,7 @@
|
||||
@"topEndEditing",
|
||||
};
|
||||
|
||||
[self sendEventWithName:events[type] body:@{
|
||||
[self sendInputEventWithName:events[type] body:@{
|
||||
@"text": text,
|
||||
@"target": reactTag
|
||||
}];
|
||||
@ -91,7 +97,7 @@
|
||||
body = mutableBody;
|
||||
}
|
||||
|
||||
[self sendEventWithName:events[type] body:body];
|
||||
[self sendInputEventWithName:events[type] body:body];
|
||||
}
|
||||
|
||||
@end
|
||||
|
@ -7,9 +7,12 @@
|
||||
|
||||
// TODO: something a bit more sophisticated
|
||||
|
||||
typedef void (^RCTCachedDataDownloadBlock)(BOOL cached, NSData *data, NSError *error);
|
||||
|
||||
@implementation RCTImageDownloader
|
||||
{
|
||||
RCTCache *_cache;
|
||||
NSMutableDictionary *_pendingBlocks;
|
||||
}
|
||||
|
||||
+ (instancetype)sharedInstance
|
||||
@ -27,6 +30,7 @@
|
||||
{
|
||||
if ((self = [super init])) {
|
||||
_cache = [[RCTCache alloc] initWithName:@"RCTImageDownloader"];
|
||||
_pendingBlocks = [NSMutableDictionary dictionary];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
@ -36,8 +40,7 @@
|
||||
return url.absoluteString;
|
||||
}
|
||||
|
||||
- (id)_downloadDataForURL:(NSURL *)url
|
||||
block:(RCTDataDownloadBlock)block
|
||||
- (id)_downloadDataForURL:(NSURL *)url block:(RCTCachedDataDownloadBlock)block
|
||||
{
|
||||
NSString *cacheKey = [self cacheKeyForURL:url];
|
||||
|
||||
@ -45,36 +48,58 @@
|
||||
__block NSURLSessionDataTask *task = nil;
|
||||
dispatch_block_t cancel = ^{
|
||||
cancelled = YES;
|
||||
|
||||
NSMutableArray *pendingBlocks = _pendingBlocks[cacheKey];
|
||||
[pendingBlocks removeObject:block];
|
||||
|
||||
if (task) {
|
||||
[task cancel];
|
||||
task = nil;
|
||||
}
|
||||
};
|
||||
|
||||
if ([_cache hasDataForKey:cacheKey]) {
|
||||
[_cache fetchDataForKey:cacheKey completionHandler:^(NSData *data) {
|
||||
if (cancelled) return;
|
||||
block(data, nil);
|
||||
}];
|
||||
NSMutableArray *pendingBlocks = _pendingBlocks[cacheKey];
|
||||
if (pendingBlocks) {
|
||||
[pendingBlocks addObject:block];
|
||||
} else {
|
||||
task = [[NSURLSession sharedSession] dataTaskWithURL:url completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
|
||||
block(data, error);
|
||||
}];
|
||||
_pendingBlocks[cacheKey] = [NSMutableArray arrayWithObject:block];
|
||||
|
||||
[task resume];
|
||||
__weak RCTImageDownloader *weakSelf = self;
|
||||
RCTCachedDataDownloadBlock runBlocks = ^(BOOL cached, NSData *data, NSError *error) {
|
||||
RCTImageDownloader *strongSelf = weakSelf;
|
||||
NSArray *blocks = strongSelf->_pendingBlocks[cacheKey];
|
||||
[strongSelf->_pendingBlocks removeObjectForKey:cacheKey];
|
||||
|
||||
for (RCTCachedDataDownloadBlock block in blocks) {
|
||||
block(cached, data, error);
|
||||
}
|
||||
};
|
||||
|
||||
if ([_cache hasDataForKey:cacheKey]) {
|
||||
[_cache fetchDataForKey:cacheKey completionHandler:^(NSData *data) {
|
||||
if (!cancelled) runBlocks(YES, data, nil);
|
||||
}];
|
||||
} else {
|
||||
task = [[NSURLSession sharedSession] dataTaskWithURL:url completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
|
||||
if (!cancelled) runBlocks(NO, data, error);
|
||||
}];
|
||||
|
||||
[task resume];
|
||||
}
|
||||
}
|
||||
|
||||
return [cancel copy];
|
||||
}
|
||||
|
||||
- (id)downloadDataForURL:(NSURL *)url
|
||||
block:(RCTDataDownloadBlock)block
|
||||
- (id)downloadDataForURL:(NSURL *)url block:(RCTDataDownloadBlock)block
|
||||
{
|
||||
NSString *cacheKey = [self cacheKeyForURL:url];
|
||||
__weak RCTImageDownloader *weakSelf = self;
|
||||
return [self _downloadDataForURL:url block:^(NSData *data, NSError *error) {
|
||||
RCTImageDownloader *strongSelf = weakSelf;
|
||||
[strongSelf->_cache setData:data forKey:cacheKey];
|
||||
return [self _downloadDataForURL:url block:^(BOOL cached, NSData *data, NSError *error) {
|
||||
if (!cached) {
|
||||
RCTImageDownloader *strongSelf = weakSelf;
|
||||
[strongSelf->_cache setData:data forKey:cacheKey];
|
||||
}
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
block(data, error);
|
||||
@ -82,52 +107,51 @@
|
||||
}];
|
||||
}
|
||||
|
||||
- (id)downloadImageForURL:(NSURL *)url
|
||||
size:(CGSize)size
|
||||
scale:(CGFloat)scale
|
||||
block:(RCTImageDownloadBlock)block
|
||||
- (id)downloadImageForURL:(NSURL *)url size:(CGSize)size scale:(CGFloat)scale block:(RCTImageDownloadBlock)block
|
||||
{
|
||||
NSString *cacheKey = [self cacheKeyForURL:url];
|
||||
__weak RCTImageDownloader *weakSelf = self;
|
||||
return [self _downloadDataForURL:url block:^(NSData *data, NSError *error) {
|
||||
if (data) {
|
||||
UIImage *image = [UIImage imageWithData:data scale:scale];
|
||||
|
||||
if (image) {
|
||||
CGSize imageSize = size;
|
||||
if (CGSizeEqualToSize(imageSize, CGSizeZero)) {
|
||||
imageSize = image.size;
|
||||
}
|
||||
|
||||
CGFloat imageScale = scale;
|
||||
if (imageScale == 0 || imageScale > image.scale) {
|
||||
imageScale = image.scale;
|
||||
}
|
||||
|
||||
UIGraphicsBeginImageContextWithOptions(imageSize, NO, imageScale);
|
||||
[image drawInRect:(CGRect){{0, 0}, imageSize}];
|
||||
image = UIGraphicsGetImageFromCurrentImageContext();
|
||||
UIGraphicsEndImageContext();
|
||||
|
||||
RCTImageDownloader *strongSelf = weakSelf;
|
||||
[strongSelf->_cache setData:UIImagePNGRepresentation(image) forKey:cacheKey];
|
||||
}
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
block(image, nil);
|
||||
});
|
||||
} else {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
return [self _downloadDataForURL:url block:^(BOOL cached, NSData *data, NSError *error) {
|
||||
if (!data) {
|
||||
return dispatch_async(dispatch_get_main_queue(), ^{
|
||||
block(nil, error);
|
||||
});
|
||||
}
|
||||
|
||||
UIImage *image = [UIImage imageWithData:data scale:scale];
|
||||
|
||||
if (image) {
|
||||
CGSize imageSize = size;
|
||||
if (CGSizeEqualToSize(imageSize, CGSizeZero)) {
|
||||
imageSize = image.size;
|
||||
}
|
||||
|
||||
CGFloat imageScale = scale;
|
||||
if (imageScale == 0 || imageScale > image.scale) {
|
||||
imageScale = image.scale;
|
||||
}
|
||||
|
||||
UIGraphicsBeginImageContextWithOptions(imageSize, NO, imageScale);
|
||||
[image drawInRect:(CGRect){{0, 0}, imageSize}];
|
||||
image = UIGraphicsGetImageFromCurrentImageContext();
|
||||
UIGraphicsEndImageContext();
|
||||
|
||||
if (!cached) {
|
||||
RCTImageDownloader *strongSelf = weakSelf;
|
||||
[strongSelf->_cache setData:UIImagePNGRepresentation(image) forKey:cacheKey];
|
||||
}
|
||||
}
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
block(image, nil);
|
||||
});
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)cancelDownload:(id)downloadToken
|
||||
{
|
||||
if (downloadToken) {
|
||||
dispatch_block_t block = downloadToken;
|
||||
dispatch_block_t block = (id)downloadToken;
|
||||
block();
|
||||
}
|
||||
}
|
||||
|
@ -677,7 +677,10 @@ static void RCTSetShadowViewProps(NSDictionary *props, RCTShadowView *shadowView
|
||||
RCTLogWarn(@"No manager class found for view with module name \"%@\"", moduleName);
|
||||
manager = [[RCTViewManager alloc] init];
|
||||
}
|
||||
|
||||
|
||||
// Register manager
|
||||
_viewManagerRegistry[reactTag] = manager;
|
||||
|
||||
// Generate default view, used for resetting default props
|
||||
if (!_defaultShadowViews[moduleName]) {
|
||||
_defaultShadowViews[moduleName] = [manager shadowView];
|
||||
@ -691,10 +694,7 @@ static void RCTSetShadowViewProps(NSDictionary *props, RCTShadowView *shadowView
|
||||
|
||||
[self addUIBlock:^(RCTUIManager *uiManager, RCTSparseArray *viewRegistry){
|
||||
RCTCAssertMainThread();
|
||||
|
||||
// Register manager
|
||||
uiManager->_viewManagerRegistry[reactTag] = manager;
|
||||
|
||||
|
||||
// Generate default view, used for resetting default props
|
||||
if (!uiManager->_defaultViews[moduleName]) {
|
||||
// Note the default is setup after the props are read for the first time ever
|
||||
|
@ -324,7 +324,7 @@ NSInteger kNeverProgressed = -10000;
|
||||
return;
|
||||
}
|
||||
_mostRecentProgress = nextProgress;
|
||||
[_eventDispatcher sendEventWithName:@"topNavigationProgress" body:@{
|
||||
[_eventDispatcher sendInputEventWithName:@"topNavigationProgress" body:@{
|
||||
@"fromIndex": @(_currentlyTransitioningFrom),
|
||||
@"toIndex": @(_currentlyTransitioningTo),
|
||||
@"progress": @(nextProgress),
|
||||
@ -447,7 +447,7 @@ NSInteger kNeverProgressed = -10000;
|
||||
|
||||
- (void)handleTopOfStackChanged
|
||||
{
|
||||
[_eventDispatcher sendEventWithName:@"topNavigateBack" body:@{
|
||||
[_eventDispatcher sendInputEventWithName:@"topNavigateBack" body:@{
|
||||
@"target":self.reactTag,
|
||||
@"stackLength":@(_navigationController.viewControllers.count)
|
||||
}];
|
||||
|
@ -96,7 +96,7 @@
|
||||
|
||||
- (void)rightButtonTapped
|
||||
{
|
||||
[_eventDispatcher sendEventWithName:@"topNavRightButtonTap" body:@{@"target":_navItem.reactTag}];
|
||||
[_eventDispatcher sendInputEventWithName:@"topNavRightButtonTap" body:@{@"target":_navItem.reactTag}];
|
||||
}
|
||||
|
||||
- (void)didMoveToParentViewController:(UIViewController *)parent
|
||||
|
22
packager/react-packager/index.js
vendored
22
packager/react-packager/index.js
vendored
@ -10,10 +10,30 @@ exports.middleware = function(options) {
|
||||
|
||||
exports.buildPackageFromUrl = function(options, reqUrl) {
|
||||
Activity.disable();
|
||||
// Don't start the filewatcher or the cache.
|
||||
if (options.nonPersistent == null) {
|
||||
options.nonPersistent = true;
|
||||
}
|
||||
|
||||
var server = new Server(options);
|
||||
return server.buildPackageFromUrl(reqUrl)
|
||||
.then(function(p) {
|
||||
server.kill();
|
||||
server.end();
|
||||
return p;
|
||||
});
|
||||
};
|
||||
|
||||
exports.getDependencies = function(options, main) {
|
||||
Activity.disable();
|
||||
// Don't start the filewatcher or the cache.
|
||||
if (options.nonPersistent == null) {
|
||||
options.nonPersistent = true;
|
||||
}
|
||||
|
||||
var server = new Server(options);
|
||||
return server.getDependencies(main)
|
||||
.then(function(r) {
|
||||
server.end();
|
||||
return r.dependencies;
|
||||
});
|
||||
};
|
||||
|
@ -55,6 +55,25 @@ describe('HasteDependencyResolver', function() {
|
||||
isPolyfill: true,
|
||||
dependencies: ['polyfills/prelude.js', 'polyfills/require.js']
|
||||
},
|
||||
{ id: 'polyfills/console.js',
|
||||
isPolyfill: true,
|
||||
path: 'polyfills/console.js',
|
||||
dependencies: [
|
||||
'polyfills/prelude.js',
|
||||
'polyfills/require.js',
|
||||
'polyfills/polyfills.js'
|
||||
],
|
||||
},
|
||||
{ id: 'polyfills/error-guard.js',
|
||||
isPolyfill: true,
|
||||
path: 'polyfills/error-guard.js',
|
||||
dependencies: [
|
||||
'polyfills/prelude.js',
|
||||
'polyfills/require.js',
|
||||
'polyfills/polyfills.js',
|
||||
'polyfills/console.js'
|
||||
],
|
||||
},
|
||||
module
|
||||
]);
|
||||
});
|
||||
@ -97,11 +116,35 @@ describe('HasteDependencyResolver', function() {
|
||||
isPolyfill: true,
|
||||
dependencies: ['polyfills/prelude.js', 'polyfills/require.js']
|
||||
},
|
||||
{ id: 'polyfills/console.js',
|
||||
isPolyfill: true,
|
||||
path: 'polyfills/console.js',
|
||||
dependencies: [
|
||||
'polyfills/prelude.js',
|
||||
'polyfills/require.js',
|
||||
'polyfills/polyfills.js'
|
||||
],
|
||||
},
|
||||
{ id: 'polyfills/error-guard.js',
|
||||
isPolyfill: true,
|
||||
path: 'polyfills/error-guard.js',
|
||||
dependencies: [
|
||||
'polyfills/prelude.js',
|
||||
'polyfills/require.js',
|
||||
'polyfills/polyfills.js',
|
||||
'polyfills/console.js'
|
||||
],
|
||||
},
|
||||
{ path: 'some module',
|
||||
id: 'some module',
|
||||
isPolyfill: true,
|
||||
dependencies: [ 'polyfills/prelude.js', 'polyfills/require.js',
|
||||
'polyfills/polyfills.js']
|
||||
dependencies: [
|
||||
'polyfills/prelude.js',
|
||||
'polyfills/require.js',
|
||||
'polyfills/polyfills.js',
|
||||
'polyfills/console.js',
|
||||
'polyfills/error-guard.js',
|
||||
]
|
||||
},
|
||||
module
|
||||
]);
|
||||
|
@ -19,7 +19,10 @@ var DEFINE_MODULE_REPLACE_RE = /_moduleName_|_code_|_deps_/g;
|
||||
var REL_REQUIRE_STMT = /require\(['"]([\.\/0-9A-Z_$\-]*)['"]\)/gi;
|
||||
|
||||
function HasteDependencyResolver(config) {
|
||||
this._fileWatcher = new FileWatcher(config.projectRoots);
|
||||
this._fileWatcher = config.nonPersistent
|
||||
? FileWatcher.createDummyWatcher()
|
||||
: new FileWatcher(config.projectRoots);
|
||||
|
||||
this._depGraph = new DependencyGraph({
|
||||
roots: config.projectRoots,
|
||||
ignoreFilePath: function(filepath) {
|
||||
@ -97,7 +100,6 @@ HasteDependencyResolver.prototype.wrapModule = function(module, code) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var relativizedCode =
|
||||
code.replace(REL_REQUIRE_STMT, function(codeMatch, depName) {
|
||||
var dep = resolvedDeps[depName];
|
||||
@ -117,7 +119,6 @@ HasteDependencyResolver.prototype.wrapModule = function(module, code) {
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
HasteDependencyResolver.prototype.end = function() {
|
||||
return this._fileWatcher.end();
|
||||
};
|
||||
|
@ -76,3 +76,11 @@ function createWatcher(root) {
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
FileWatcher.createDummyWatcher = function() {
|
||||
var ev = new EventEmitter();
|
||||
ev.end = function() {
|
||||
return q();
|
||||
};
|
||||
return ev;
|
||||
};
|
||||
|
@ -6,6 +6,10 @@ jest
|
||||
.dontMock('os')
|
||||
.dontMock('../index');
|
||||
|
||||
var OPTIONS = {
|
||||
transformModulePath: '/foo/bar'
|
||||
};
|
||||
|
||||
describe('Transformer', function() {
|
||||
var Transformer;
|
||||
var workers;
|
||||
@ -32,7 +36,7 @@ describe('Transformer', function() {
|
||||
callback(null, 'content');
|
||||
});
|
||||
|
||||
return new Transformer({}).loadFileAndTransform([], 'file', {})
|
||||
return new Transformer(OPTIONS).loadFileAndTransform([], 'file', {})
|
||||
.then(function(data) {
|
||||
expect(data).toEqual({
|
||||
code: 'transformed',
|
||||
@ -55,7 +59,7 @@ describe('Transformer', function() {
|
||||
callback(null, {error: esprimaError});
|
||||
});
|
||||
|
||||
return new Transformer({}).loadFileAndTransform([], 'foo-file.js', {})
|
||||
return new Transformer(OPTIONS).loadFileAndTransform([], 'foo-file.js', {})
|
||||
.catch(function(error) {
|
||||
expect(error.type).toEqual('TransformError');
|
||||
expect(error.snippet).toEqual([
|
||||
|
@ -14,15 +14,21 @@ module.exports = Transformer;
|
||||
Transformer.TransformError = TransformError;
|
||||
|
||||
function Transformer(projectConfig) {
|
||||
this._cache = new Cache(projectConfig);
|
||||
this._workers = workerFarm(
|
||||
{autoStart: true},
|
||||
projectConfig.transformModulePath
|
||||
);
|
||||
this._cache = projectConfig.nonPersistent
|
||||
? new DummyCache() : new Cache(projectConfig);
|
||||
|
||||
if (projectConfig.transformModulePath == null) {
|
||||
this._failedToStart = q.Promise.reject(new Error('No transfrom module'));
|
||||
} else {
|
||||
this._workers = workerFarm(
|
||||
{autoStart: true},
|
||||
projectConfig.transformModulePath
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Transformer.prototype.kill = function() {
|
||||
workerFarm.end(this._workers);
|
||||
this._workers && workerFarm.end(this._workers);
|
||||
return this._cache.end();
|
||||
};
|
||||
|
||||
@ -37,6 +43,10 @@ Transformer.prototype.loadFileAndTransform = function(
|
||||
filePath,
|
||||
options
|
||||
) {
|
||||
if (this._failedToStart) {
|
||||
return this._failedToStart;
|
||||
}
|
||||
|
||||
var workers = this._workers;
|
||||
return this._cache.get(filePath, function() {
|
||||
return readFile(filePath)
|
||||
@ -93,3 +103,10 @@ function formatEsprimaError(err, filename, source) {
|
||||
error.description = err.description;
|
||||
return error;
|
||||
}
|
||||
|
||||
function DummyCache() {}
|
||||
DummyCache.prototype.get = function(filePath, loaderCb) {
|
||||
return loaderCb();
|
||||
};
|
||||
DummyCache.prototype.end =
|
||||
DummyCache.prototype.invalidate = function(){};
|
||||
|
18
packager/react-packager/src/Packager/index.js
vendored
18
packager/react-packager/src/Packager/index.js
vendored
@ -34,15 +34,7 @@ var DEFAULT_CONFIG = {
|
||||
*/
|
||||
polyfillModuleNames: [],
|
||||
|
||||
/**
|
||||
* DEPRECATED
|
||||
*
|
||||
* A string of code to be appended to the top of a package.
|
||||
*
|
||||
* TODO: THIS RUINS SOURCE MAPS. THIS OPTION SHOULD BE REMOVED ONCE WE GET
|
||||
* config.polyfillModuleNames WORKING!
|
||||
*/
|
||||
runtimeCode: ''
|
||||
nonPersistent: false,
|
||||
};
|
||||
|
||||
function Packager(projectConfig) {
|
||||
@ -72,7 +64,7 @@ Packager.prototype.package = function(main, runModule, sourceMapUrl) {
|
||||
var findEventId = Activity.startEvent('find dependencies');
|
||||
var transformEventId;
|
||||
|
||||
return this._resolver.getDependencies(main)
|
||||
return this.getDependencies(main)
|
||||
.then(function(result) {
|
||||
Activity.endEvent(findEventId);
|
||||
transformEventId = Activity.startEvent('transform');
|
||||
@ -98,10 +90,14 @@ Packager.prototype.package = function(main, runModule, sourceMapUrl) {
|
||||
});
|
||||
};
|
||||
|
||||
Packager.prototype.invalidateFile = function(filePath){
|
||||
Packager.prototype.invalidateFile = function(filePath) {
|
||||
this._transformer.invalidateFile(filePath);
|
||||
}
|
||||
|
||||
Packager.prototype.getDependencies = function(main) {
|
||||
return this._resolver.getDependencies(main);
|
||||
};
|
||||
|
||||
Packager.prototype._transformModule = function(module) {
|
||||
var resolver = this._resolver;
|
||||
return this._transformer.loadFileAndTransform(
|
||||
|
13
packager/react-packager/src/Server/index.js
vendored
13
packager/react-packager/src/Server/index.js
vendored
@ -18,10 +18,13 @@ function Server(options) {
|
||||
cacheVersion: options.cacheVersion,
|
||||
resetCache: options.resetCache,
|
||||
dev: options.dev,
|
||||
transformModulePath: options.transformModulePath
|
||||
transformModulePath: options.transformModulePath,
|
||||
nonPersistent: options.nonPersistent,
|
||||
});
|
||||
|
||||
this._fileWatcher = new FileWatcher(options.projectRoots);
|
||||
this._fileWatcher = options.nonPersistent
|
||||
? FileWatcher.createDummyWatcher()
|
||||
: new FileWatcher(options.projectRoots);
|
||||
|
||||
var onFileChange = this._onFileChange.bind(this);
|
||||
this._fileWatcher.on('all', onFileChange);
|
||||
@ -48,7 +51,7 @@ Server.prototype._rebuildPackages = function(filepath) {
|
||||
});
|
||||
};
|
||||
|
||||
Server.prototype.kill = function() {
|
||||
Server.prototype.end = function() {
|
||||
q.all([
|
||||
this._fileWatcher.end(),
|
||||
this._packager.kill(),
|
||||
@ -68,6 +71,10 @@ Server.prototype.buildPackageFromUrl = function(reqUrl) {
|
||||
return this._buildPackage(options);
|
||||
};
|
||||
|
||||
Server.prototype.getDependencies = function(main) {
|
||||
return this._packager.getDependencies(main);
|
||||
};
|
||||
|
||||
Server.prototype._processDebugRequest = function(reqUrl, res) {
|
||||
var ret = '<!doctype html>';
|
||||
var pathname = url.parse(reqUrl).pathname;
|
||||
|
Loading…
x
Reference in New Issue
Block a user