Updates from Tue 5 May
This commit is contained in:
commit
5df5602f1a
|
@ -78,7 +78,7 @@ var TabBarExample = React.createClass({
|
|||
this.setState({
|
||||
selectedTab: 'greenTab',
|
||||
presses: this.state.presses + 1
|
||||
});
|
||||
});
|
||||
}}>
|
||||
{this._renderContent('#21551C', 'Green Tab')}
|
||||
</TabBarIOS.Item>
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
14AADF051AC3DBB1002390C9 /* libReact.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 14AADF041AC3DB95002390C9 /* libReact.a */; };
|
||||
14DC67F41AB71881001358AB /* libRCTPushNotification.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 14DC67F11AB71876001358AB /* libRCTPushNotification.a */; };
|
||||
58005BF21ABA80A60062E044 /* libRCTTest.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 58005BEE1ABA80530062E044 /* libRCTTest.a */; };
|
||||
834C36EC1AF8DED70019C93C /* libRCTSettings.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 834C36D21AF8DA610019C93C /* libRCTSettings.a */; };
|
||||
D85B829E1AB6D5D7003F4FE2 /* libRCTVibration.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D85B829C1AB6D5CE003F4FE2 /* libRCTVibration.a */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
|
@ -103,6 +104,13 @@
|
|||
remoteGlobalIDString = 580C376F1AB104AF0015E709;
|
||||
remoteInfo = RCTTest;
|
||||
};
|
||||
834C36D11AF8DA610019C93C /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 13CC9D481AEED2B90020D1C2 /* RCTSettings.xcodeproj */;
|
||||
proxyType = 2;
|
||||
remoteGlobalIDString = 134814201AA4EA6300B7C361;
|
||||
remoteInfo = RCTSettings;
|
||||
};
|
||||
D85B829B1AB6D5CE003F4FE2 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = D85B82911AB6D5CE003F4FE2 /* RCTVibration.xcodeproj */;
|
||||
|
@ -129,6 +137,7 @@
|
|||
13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = UIExplorer/Images.xcassets; sourceTree = "<group>"; };
|
||||
13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = UIExplorer/Info.plist; sourceTree = "<group>"; };
|
||||
13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = UIExplorer/main.m; sourceTree = "<group>"; };
|
||||
13CC9D481AEED2B90020D1C2 /* RCTSettings.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTSettings.xcodeproj; path = ../../Libraries/Settings/RCTSettings.xcodeproj; sourceTree = "<group>"; };
|
||||
14AADEFF1AC3DB95002390C9 /* React.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = React.xcodeproj; path = ../../React/React.xcodeproj; sourceTree = "<group>"; };
|
||||
14DC67E71AB71876001358AB /* RCTPushNotification.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTPushNotification.xcodeproj; path = ../../Libraries/PushNotificationIOS/RCTPushNotification.xcodeproj; sourceTree = "<group>"; };
|
||||
14E0EEC81AB118F7000DECC3 /* RCTActionSheet.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTActionSheet.xcodeproj; path = ../../Libraries/ActionSheetIOS/RCTActionSheet.xcodeproj; sourceTree = "<group>"; };
|
||||
|
@ -148,6 +157,7 @@
|
|||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
834C36EC1AF8DED70019C93C /* libRCTSettings.a in Frameworks */,
|
||||
14AADF051AC3DBB1002390C9 /* libReact.a in Frameworks */,
|
||||
00D2771A1AB8C3E100DC1E48 /* libRCTWebSocketDebugger.a in Frameworks */,
|
||||
58005BF21ABA80A60062E044 /* libRCTTest.a in Frameworks */,
|
||||
|
@ -200,6 +210,7 @@
|
|||
134A8A201AACED6A00945AAE /* RCTGeolocation.xcodeproj */,
|
||||
13417FE31AA91428003F314A /* RCTImage.xcodeproj */,
|
||||
134180261AA91779003F314A /* RCTNetwork.xcodeproj */,
|
||||
13CC9D481AEED2B90020D1C2 /* RCTSettings.xcodeproj */,
|
||||
58005BE41ABA80530062E044 /* RCTTest.xcodeproj */,
|
||||
13417FEA1AA914B8003F314A /* RCTText.xcodeproj */,
|
||||
00D2770E1AB8C2C700DC1E48 /* RCTWebSocketDebugger.xcodeproj */,
|
||||
|
@ -293,6 +304,14 @@
|
|||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
834C36CE1AF8DA610019C93C /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
834C36D21AF8DA610019C93C /* libRCTSettings.a */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
83CBB9F61A601CBA00E9B192 = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
@ -411,6 +430,10 @@
|
|||
ProductGroup = 14DC67E81AB71876001358AB /* Products */;
|
||||
ProjectRef = 14DC67E71AB71876001358AB /* RCTPushNotification.xcodeproj */;
|
||||
},
|
||||
{
|
||||
ProductGroup = 834C36CE1AF8DA610019C93C /* Products */;
|
||||
ProjectRef = 13CC9D481AEED2B90020D1C2 /* RCTSettings.xcodeproj */;
|
||||
},
|
||||
{
|
||||
ProductGroup = 58005BE51ABA80530062E044 /* Products */;
|
||||
ProjectRef = 58005BE41ABA80530062E044 /* RCTTest.xcodeproj */;
|
||||
|
@ -511,6 +534,13 @@
|
|||
remoteRef = 58005BED1ABA80530062E044 /* PBXContainerItemProxy */;
|
||||
sourceTree = BUILT_PRODUCTS_DIR;
|
||||
};
|
||||
834C36D21AF8DA610019C93C /* libRCTSettings.a */ = {
|
||||
isa = PBXReferenceProxy;
|
||||
fileType = archive.ar;
|
||||
path = libRCTSettings.a;
|
||||
remoteRef = 834C36D11AF8DA610019C93C /* PBXContainerItemProxy */;
|
||||
sourceTree = BUILT_PRODUCTS_DIR;
|
||||
};
|
||||
D85B829C1AB6D5CE003F4FE2 /* libRCTVibration.a */ = {
|
||||
isa = PBXReferenceProxy;
|
||||
fileType = archive.ar;
|
||||
|
@ -632,6 +662,7 @@
|
|||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = "$(inherited)";
|
||||
HEADER_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "0610"
|
||||
LastUpgradeVersion = "0630"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
|
|
|
@ -28,6 +28,7 @@ var {
|
|||
} = React;
|
||||
|
||||
var { TestModule } = React.addons;
|
||||
var Settings = require('Settings');
|
||||
|
||||
var createExamplePage = require('./createExamplePage');
|
||||
|
||||
|
@ -114,9 +115,14 @@ class UIExplorerList extends React.Component {
|
|||
components: COMPONENTS,
|
||||
apis: APIS,
|
||||
}),
|
||||
searchText: Settings.get('searchText'),
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this._search(this.state.searchText);
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<View style={styles.listContainer}>
|
||||
|
@ -128,6 +134,7 @@ class UIExplorerList extends React.Component {
|
|||
onChangeText={this._search.bind(this)}
|
||||
placeholder="Search..."
|
||||
style={styles.searchTextInput}
|
||||
value={this.state.searchText}
|
||||
/>
|
||||
</View>
|
||||
<ListView
|
||||
|
@ -177,8 +184,10 @@ class UIExplorerList extends React.Component {
|
|||
dataSource: ds.cloneWithRowsAndSections({
|
||||
components: COMPONENTS.filter(filter),
|
||||
apis: APIS.filter(filter),
|
||||
})
|
||||
}),
|
||||
searchText: text,
|
||||
});
|
||||
Settings.set({searchText: text});
|
||||
}
|
||||
|
||||
_onPressRow(example) {
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
'use strict';
|
||||
|
||||
var React = require('react-native');
|
||||
var ReactIOS = require('ReactIOS');
|
||||
var UIExplorerBlock = require('./UIExplorerBlock');
|
||||
var UIExplorerPage = require('./UIExplorerPage');
|
||||
|
||||
|
@ -46,20 +47,28 @@ var createExamplePage = function(title: ?string, exampleModule: ExampleModule)
|
|||
getBlock: function(example, i) {
|
||||
// Hack warning: This is a hack because the www UI explorer requires
|
||||
// renderComponent to be called.
|
||||
var originalRenderComponent = React.renderComponent;
|
||||
var originalRender = React.render;
|
||||
var originalRenderComponent = React.renderComponent;
|
||||
var originalIOSRender = ReactIOS.render;
|
||||
var originalIOSRenderComponent = ReactIOS.renderComponent;
|
||||
var renderedComponent;
|
||||
// TODO remove typecasts when Flow bug #6560135 is fixed
|
||||
// and workaround is removed from react-native.js
|
||||
(React: Object).render = (React: Object).renderComponent = function(element, container) {
|
||||
renderedComponent = element;
|
||||
};
|
||||
(React: Object).render =
|
||||
(React: Object).renderComponent =
|
||||
(ReactIOS: Object).render =
|
||||
(ReactIOS: Object).renderComponent =
|
||||
function(element, container) {
|
||||
renderedComponent = element;
|
||||
};
|
||||
var result = example.render(null);
|
||||
if (result) {
|
||||
renderedComponent = result;
|
||||
}
|
||||
(React: Object).renderComponent = originalRenderComponent;
|
||||
(React: Object).render = originalRender;
|
||||
(React: Object).renderComponent = originalRenderComponent;
|
||||
(ReactIOS: Object).render = originalIOSRender;
|
||||
(ReactIOS: Object).renderComponent = originalIOSRenderComponent;
|
||||
return (
|
||||
<UIExplorerBlock
|
||||
key={i}
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
@interface RCTConvert (ART)
|
||||
|
||||
+ (CGPathRef)CGPath:(id)json;
|
||||
+ (CTFontRef)CTFont:(id)json;
|
||||
+ (CTTextAlignment)CTTextAlignment:(id)json;
|
||||
+ (ARTTextFrame)ARTTextFrame:(id)json;
|
||||
+ (ARTCGFloatArray)ARTCGFloatArray:(id)json;
|
||||
|
|
|
@ -64,18 +64,6 @@
|
|||
return (CGPathRef)CFAutorelease(path);
|
||||
}
|
||||
|
||||
+ (CTFontRef)CTFont:(id)json
|
||||
{
|
||||
NSDictionary *dict = [self NSDictionary:json];
|
||||
if (!dict) {
|
||||
return nil;
|
||||
}
|
||||
CTFontDescriptorRef fontDescriptor = CTFontDescriptorCreateWithAttributes((__bridge CFDictionaryRef)dict);
|
||||
CTFontRef font = CTFontCreateWithFontDescriptor(fontDescriptor, 0.0, NULL);
|
||||
CFRelease(fontDescriptor);
|
||||
return (CTFontRef)CFAutorelease(font);
|
||||
}
|
||||
|
||||
RCT_ENUM_CONVERTER(CTTextAlignment, (@{
|
||||
@"auto": @(kCTTextAlignmentNatural),
|
||||
@"left": @(kCTTextAlignmentLeft),
|
||||
|
@ -98,7 +86,8 @@ RCT_ENUM_CONVERTER(CTTextAlignment, (@{
|
|||
return frame;
|
||||
}
|
||||
|
||||
CTFontRef font = [self CTFont:dict[@"font"]];
|
||||
NSDictionary *fontDict = dict[@"font"];
|
||||
CTFontRef font = (__bridge CTFontRef)[self UIFont:nil withFamily:fontDict[@"fontFamily"] size:fontDict[@"fontSize"] weight:fontDict[@"fontWeight"] style:fontDict[@"fontStyle"]];
|
||||
if (!font) {
|
||||
return frame;
|
||||
}
|
||||
|
|
|
@ -50,18 +50,11 @@ function fontAndLinesDiffer(a, b) {
|
|||
return true;
|
||||
}
|
||||
|
||||
var aTraits = a.font.NSCTFontTraitsAttribute;
|
||||
var bTraits = b.font.NSCTFontTraitsAttribute;
|
||||
|
||||
if (
|
||||
a.font.fontFamily !== b.font.fontFamily ||
|
||||
a.font.fontSize !== b.font.fontSize ||
|
||||
a.font.fontWeight !== b.font.fontWeight ||
|
||||
a.font.fontStyle !== b.font.fontStyle ||
|
||||
// TODO(6364240): remove iOS-specific attrs
|
||||
a.font.NSFontFamilyAttribute !== b.font.NSFontFamilyAttribute ||
|
||||
a.font.NSFontSizeAttribute !== b.font.NSFontSizeAttribute ||
|
||||
aTraits.NSCTFontSymbolicTrait !== bTraits.NSCTFontSymbolicTrait
|
||||
a.font.fontStyle !== b.font.fontStyle
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
@ -418,14 +411,6 @@ var Shape = React.createClass({
|
|||
|
||||
var cachedFontObjectsFromString = {};
|
||||
|
||||
function extractFontTraits(isBold, isItalic) {
|
||||
var italic = isItalic ? 1 : 0;
|
||||
var bold = isBold ? 2 : 0;
|
||||
return {
|
||||
NSCTFontSymbolicTrait: italic | bold
|
||||
};
|
||||
}
|
||||
|
||||
var fontFamilyPrefix = /^[\s"']*/;
|
||||
var fontFamilySuffix = /[\s"']*$/;
|
||||
|
||||
|
@ -456,10 +441,6 @@ function parseFontString(font) {
|
|||
fontSize: fontSize,
|
||||
fontWeight: isBold ? 'bold' : 'normal',
|
||||
fontStyle: isItalic ? 'italic' : 'normal',
|
||||
// TODO(6364240): remove iOS-specific attrs
|
||||
NSFontFamilyAttribute: fontFamily,
|
||||
NSFontSizeAttribute: fontSize,
|
||||
NSCTFontTraitsAttribute: extractFontTraits(isBold, isItalic)
|
||||
};
|
||||
return cachedFontObjectsFromString[font];
|
||||
}
|
||||
|
@ -479,13 +460,6 @@ function extractFont(font) {
|
|||
fontSize: fontSize,
|
||||
fontWeight: font.fontWeight,
|
||||
fontStyle: font.fontStyle,
|
||||
// TODO(6364240): remove iOS-specific attrs
|
||||
NSFontFamilyAttribute: fontFamily,
|
||||
NSFontSizeAttribute: fontSize,
|
||||
NSCTFontTraitsAttribute: extractFontTraits(
|
||||
font.fontWeight === 'bold',
|
||||
font.fontStyle === 'italic'
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -231,8 +231,10 @@ module.exports = {
|
|||
|
||||
var tickCount = Math.round(duration * ticksPerSecond / 1000);
|
||||
var samples = [];
|
||||
for (var i = 0; i <= tickCount; i++) {
|
||||
samples.push(easing.call(defaults, i / tickCount));
|
||||
if (tickCount > 0) {
|
||||
for (var i = 0; i <= tickCount; i++) {
|
||||
samples.push(easing.call(defaults, i / tickCount));
|
||||
}
|
||||
}
|
||||
|
||||
return samples;
|
||||
|
|
|
@ -114,7 +114,7 @@ RCT_EXPORT_METHOD(startAnimation:(NSNumber *)reactTag
|
|||
animationTag:(NSNumber *)animationTag
|
||||
duration:(NSTimeInterval)duration
|
||||
delay:(NSTimeInterval)delay
|
||||
easingSample:(NSArray *)easingSample
|
||||
easingSample:(NSNumberArray *)easingSample
|
||||
properties:(NSDictionary *)properties
|
||||
callback:(RCTResponseSenderBlock)callback)
|
||||
{
|
||||
|
|
|
@ -355,7 +355,7 @@ var ScrollResponderMixin = {
|
|||
|
||||
/**
|
||||
* A helper function to zoom to a specific rect in the scrollview.
|
||||
* @param {object} rect Should have shape {x, y, w, h}
|
||||
* @param {object} rect Should have shape {x, y, width, height}
|
||||
*/
|
||||
scrollResponderZoomTo: function(rect: { x: number; y: number; width: number; height: number; }) {
|
||||
RCTUIManagerDeprecated.zoomToRect(this.getNodeHandle(), rect);
|
||||
|
|
|
@ -211,11 +211,19 @@ var ScrollView = React.createClass({
|
|||
},
|
||||
|
||||
scrollTo: function(destY?: number, destX?: number) {
|
||||
RCTUIManager.scrollTo(
|
||||
this.getNodeHandle(),
|
||||
destX || 0,
|
||||
destY || 0
|
||||
);
|
||||
if (Platform.OS === 'android') {
|
||||
RCTUIManager.dispatchViewManagerCommand(
|
||||
this.getNodeHandle(),
|
||||
RCTUIManager.RCTScrollView.Commands.scrollTo,
|
||||
[destX || 0, destY || 0]
|
||||
);
|
||||
} else {
|
||||
RCTUIManager.scrollTo(
|
||||
this.getNodeHandle(),
|
||||
destX || 0,
|
||||
destY || 0
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
scrollWithoutAnimationTo: function(destY?: number, destX?: number) {
|
||||
|
@ -364,7 +372,7 @@ var validAttributes = {
|
|||
if (Platform.OS === 'android') {
|
||||
var AndroidScrollView = createReactIOSNativeComponentClass({
|
||||
validAttributes: validAttributes,
|
||||
uiViewClassName: 'AndroidScrollView',
|
||||
uiViewClassName: 'RCTScrollView',
|
||||
});
|
||||
var AndroidHorizontalScrollView = createReactIOSNativeComponentClass({
|
||||
validAttributes: validAttributes,
|
||||
|
|
|
@ -582,7 +582,7 @@ var TextInput = React.createClass({
|
|||
var counter = event.nativeEvent.eventCounter;
|
||||
if (counter > this.state.mostRecentEventCounter) {
|
||||
this.setState({mostRecentEventCounter: counter});
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
|
|
|
@ -11,14 +11,13 @@
|
|||
*/
|
||||
'use strict';
|
||||
|
||||
var NativeMethodsMixin = require('NativeMethodsMixin');
|
||||
var React = require('React');
|
||||
var POPAnimation = require('POPAnimation');
|
||||
var AnimationExperimental = require('AnimationExperimental');
|
||||
var NativeMethodsMixin = require('NativeMethodsMixin');
|
||||
var POPAnimation = require('POPAnimation');
|
||||
var React = require('React');
|
||||
var Touchable = require('Touchable');
|
||||
|
||||
var merge = require('merge');
|
||||
var copyProperties = require('copyProperties');
|
||||
var onlyChild = require('onlyChild');
|
||||
|
||||
type State = {
|
||||
|
@ -123,9 +122,8 @@ var TouchableBounce = React.createClass({
|
|||
},
|
||||
|
||||
render: function() {
|
||||
// Note(vjeux): use cloneWithProps once React has been upgraded
|
||||
var child = onlyChild(this.props.children);
|
||||
copyProperties(child.props, {
|
||||
return React.cloneElement(child, {
|
||||
accessible: true,
|
||||
testID: this.props.testID,
|
||||
onStartShouldSetResponder: this.touchableHandleStartShouldSetResponder,
|
||||
|
@ -135,7 +133,6 @@ var TouchableBounce = React.createClass({
|
|||
onResponderRelease: this.touchableHandleResponderRelease,
|
||||
onResponderTerminate: this.touchableHandleResponderTerminate
|
||||
});
|
||||
return child;
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -30,7 +30,7 @@ var ViewStylePropTypes = {
|
|||
overflow: ReactPropTypes.oneOf(['visible', 'hidden']),
|
||||
shadowColor: ReactPropTypes.string,
|
||||
shadowOffset: ReactPropTypes.shape(
|
||||
{h: ReactPropTypes.number, w: ReactPropTypes.number}
|
||||
{width: ReactPropTypes.number, height: ReactPropTypes.number}
|
||||
),
|
||||
shadowOpacity: ReactPropTypes.number,
|
||||
shadowRadius: ReactPropTypes.number,
|
||||
|
|
|
@ -109,15 +109,27 @@ var WebView = React.createClass({
|
|||
},
|
||||
|
||||
goForward: function() {
|
||||
RCTUIManager.webViewGoForward(this.getWebWiewHandle());
|
||||
RCTUIManager.dispatchViewManagerCommand(
|
||||
this.getWebWiewHandle(),
|
||||
RCTUIManager.RCTWebView.Commands.goForward,
|
||||
null
|
||||
);
|
||||
},
|
||||
|
||||
goBack: function() {
|
||||
RCTUIManager.webViewGoBack(this.getWebWiewHandle());
|
||||
RCTUIManager.dispatchViewManagerCommand(
|
||||
this.getWebWiewHandle(),
|
||||
RCTUIManager.RCTWebView.Commands.goBack,
|
||||
null
|
||||
);
|
||||
},
|
||||
|
||||
reload: function() {
|
||||
RCTUIManager.webViewReload(this.getWebWiewHandle());
|
||||
RCTUIManager.dispatchViewManagerCommand(
|
||||
this.getWebWiewHandle(),
|
||||
RCTUIManager.RCTWebView.Commands.reload,
|
||||
null
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
|
||||
var AnimationsDebugModule = require('NativeModules').AnimationsDebugModule;
|
||||
var BackAndroid = require('BackAndroid');
|
||||
var Dimensions = require('Dimensions');
|
||||
var InteractionMixin = require('InteractionMixin');
|
||||
var NavigatorBreadcrumbNavigationBar = require('NavigatorBreadcrumbNavigationBar');
|
||||
var NavigatorInterceptor = require('NavigatorInterceptor');
|
||||
|
@ -43,8 +44,9 @@ var Subscribable = require('Subscribable');
|
|||
var TimerMixin = require('react-timer-mixin');
|
||||
var View = require('View');
|
||||
|
||||
var getNavigatorContext = require('getNavigatorContext');
|
||||
var clamp = require('clamp');
|
||||
var flattenStyle = require('flattenStyle');
|
||||
var getNavigatorContext = require('getNavigatorContext');
|
||||
var invariant = require('invariant');
|
||||
var keyMirror = require('keyMirror');
|
||||
var merge = require('merge');
|
||||
|
@ -52,7 +54,17 @@ var rebound = require('rebound');
|
|||
|
||||
var PropTypes = React.PropTypes;
|
||||
|
||||
var OFF_SCREEN = {style: {opacity: 0}};
|
||||
// TODO: this is not ideal because there is no guarantee that the navigator
|
||||
// is full screen, hwoever we don't have a good way to measure the actual
|
||||
// size of the navigator right now, so this is the next best thing.
|
||||
var SCREEN_WIDTH = Dimensions.get('window').width;
|
||||
var SCREEN_HEIGHT = Dimensions.get('window').height;
|
||||
var SCENE_DISABLED_NATIVE_PROPS = {
|
||||
style: {
|
||||
left: SCREEN_WIDTH,
|
||||
opacity: 0,
|
||||
},
|
||||
};
|
||||
|
||||
var __uid = 0;
|
||||
function getuid() {
|
||||
|
@ -72,7 +84,7 @@ var styles = StyleSheet.create({
|
|||
bottom: 0,
|
||||
top: 0,
|
||||
},
|
||||
currentScene: {
|
||||
baseScene: {
|
||||
position: 'absolute',
|
||||
overflow: 'hidden',
|
||||
left: 0,
|
||||
|
@ -80,11 +92,8 @@ var styles = StyleSheet.create({
|
|||
bottom: 0,
|
||||
top: 0,
|
||||
},
|
||||
futureScene: {
|
||||
overflow: 'hidden',
|
||||
position: 'absolute',
|
||||
left: 0,
|
||||
opacity: 0,
|
||||
disabledScene: {
|
||||
left: SCREEN_WIDTH,
|
||||
},
|
||||
transitioner: {
|
||||
flex: 1,
|
||||
|
@ -231,16 +240,13 @@ var Navigator = React.createClass({
|
|||
initialRouteStack: PropTypes.arrayOf(PropTypes.object),
|
||||
|
||||
/**
|
||||
* Will emit the target route upon mounting and before each nav transition,
|
||||
* overriding the handler in this.props.navigator. This overrides the onDidFocus
|
||||
* handler that would be found in this.props.navigator
|
||||
* Will emit the target route upon mounting and before each nav transition
|
||||
*/
|
||||
onWillFocus: PropTypes.func,
|
||||
|
||||
/**
|
||||
* Will be called with the new route of each scene after the transition is
|
||||
* complete or after the initial mounting. This overrides the onDidFocus
|
||||
* handler that would be found in this.props.navigator
|
||||
* complete or after the initial mounting
|
||||
*/
|
||||
onDidFocus: PropTypes.func,
|
||||
|
||||
|
@ -390,12 +396,12 @@ var Navigator = React.createClass({
|
|||
return this._handleRequest.apply(null, arguments);
|
||||
},
|
||||
|
||||
requestPop: function() {
|
||||
return this.request('pop');
|
||||
requestPop: function(popToBeforeRoute) {
|
||||
return this.request('pop', popToBeforeRoute);
|
||||
},
|
||||
|
||||
requestPopTo: function(route) {
|
||||
return this.request('pop', route);
|
||||
return this.request('popTo', route);
|
||||
},
|
||||
|
||||
_handleRequest: function(action, arg1, arg2) {
|
||||
|
@ -406,6 +412,8 @@ var Navigator = React.createClass({
|
|||
switch (action) {
|
||||
case 'pop':
|
||||
return this._handlePop(arg1);
|
||||
case 'popTo':
|
||||
return this._handlePopTo(arg1);
|
||||
case 'push':
|
||||
return this._handlePush(arg1);
|
||||
default:
|
||||
|
@ -414,11 +422,31 @@ var Navigator = React.createClass({
|
|||
}
|
||||
},
|
||||
|
||||
_handlePop: function(route) {
|
||||
if (route) {
|
||||
var hasRoute = this.state.routeStack.indexOf(route) !== -1;
|
||||
_handlePop: function(popToBeforeRoute) {
|
||||
if (popToBeforeRoute) {
|
||||
var popToBeforeRouteIndex = this.state.routeStack.indexOf(popToBeforeRoute);
|
||||
if (popToBeforeRouteIndex === -1) {
|
||||
return false;
|
||||
}
|
||||
invariant(
|
||||
popToBeforeRouteIndex <= this.state.presentedIndex,
|
||||
'Cannot pop past a route that is forward in the navigator'
|
||||
);
|
||||
this._popN(this.state.presentedIndex - popToBeforeRouteIndex + 1);
|
||||
return true;
|
||||
}
|
||||
if (this.state.presentedIndex === 0) {
|
||||
return false;
|
||||
}
|
||||
this.pop();
|
||||
return true;
|
||||
},
|
||||
|
||||
_handlePopTo: function(destRoute) {
|
||||
if (destRoute) {
|
||||
var hasRoute = this.state.routeStack.indexOf(destRoute) !== -1;
|
||||
if (hasRoute) {
|
||||
this.popToRoute(route);
|
||||
this.popToRoute(destRoute);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
|
@ -552,10 +580,12 @@ var Navigator = React.createClass({
|
|||
},
|
||||
|
||||
/**
|
||||
* This happens at the end of a transition started by transitionTo
|
||||
* This happens at the end of a transition started by transitionTo, and when the spring catches up to a pending gesture
|
||||
*/
|
||||
_completeTransition: function() {
|
||||
if (this.spring.getCurrentValue() !== 1) {
|
||||
// The spring has finished catching up to a gesture in progress. Remove the pending progress
|
||||
// and we will be in a normal activeGesture state
|
||||
if (this.state.pendingGestureProgress) {
|
||||
this.state.pendingGestureProgress = null;
|
||||
}
|
||||
|
@ -580,11 +610,16 @@ var Navigator = React.createClass({
|
|||
this._interactionHandle = null;
|
||||
}
|
||||
if (this.state.pendingGestureProgress) {
|
||||
// A transition completed, but there is already another gesture happening.
|
||||
// Enable the scene and set the spring to catch up with the new gesture
|
||||
var gestureToIndex = this.state.presentedIndex + this._deltaForGestureAction(this.state.activeGesture);
|
||||
this._enableScene(gestureToIndex);
|
||||
this.spring.setEndValue(this.state.pendingGestureProgress);
|
||||
return;
|
||||
}
|
||||
if (this.state.transitionQueue.length) {
|
||||
var queuedTransition = this.state.transitionQueue.shift();
|
||||
this._enableScene(queuedTransition.destIndex);
|
||||
this._transitionTo(
|
||||
queuedTransition.destIndex,
|
||||
queuedTransition.velocity,
|
||||
|
@ -601,7 +636,8 @@ var Navigator = React.createClass({
|
|||
this._lastDidFocus = route;
|
||||
if (this.props.onDidFocus) {
|
||||
this.props.onDidFocus(route);
|
||||
} else if (this.parentNavigator && this.parentNavigator.onDidFocus) {
|
||||
}
|
||||
if (this.parentNavigator && this.parentNavigator.onDidFocus) {
|
||||
this.parentNavigator.onDidFocus(route);
|
||||
}
|
||||
},
|
||||
|
@ -617,27 +653,51 @@ var Navigator = React.createClass({
|
|||
}
|
||||
if (this.props.onWillFocus) {
|
||||
this.props.onWillFocus(route);
|
||||
} else if (this.parentNavigator && this.parentNavigator.onWillFocus) {
|
||||
}
|
||||
if (this.parentNavigator && this.parentNavigator.onWillFocus) {
|
||||
this.parentNavigator.onWillFocus(route);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Does not delete the scenes - merely hides them.
|
||||
* Hides scenes that we are not currently on or transitioning from
|
||||
*/
|
||||
_hideScenes: function() {
|
||||
for (var i = 0; i < this.state.routeStack.length; i++) {
|
||||
// This gets called when we detach a gesture, so there will not be a
|
||||
// current gesture, but there might be a transition in progress
|
||||
if (i === this.state.presentedIndex || i === this.state.transitionFromIndex) {
|
||||
continue;
|
||||
}
|
||||
var sceneRef = 'scene_' + i;
|
||||
this.refs[sceneRef] &&
|
||||
this.refs['scene_' + i].setNativeProps(OFF_SCREEN);
|
||||
this._disableScene(i);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Push a scene off the screen, so that opacity:0 scenes will not block touches sent to the presented scenes
|
||||
*/
|
||||
_disableScene: function(sceneIndex) {
|
||||
this.refs['scene_' + sceneIndex] &&
|
||||
this.refs['scene_' + sceneIndex].setNativeProps(SCENE_DISABLED_NATIVE_PROPS);
|
||||
},
|
||||
|
||||
/**
|
||||
* Put the scene back into the state as defined by props.sceneStyle, so transitions can happen normally
|
||||
*/
|
||||
_enableScene: function(sceneIndex) {
|
||||
// First, determine what the defined styles are for scenes in this navigator
|
||||
var sceneStyle = flattenStyle(this.props.sceneStyle);
|
||||
// Then restore the left value for this scene
|
||||
var enabledSceneNativeProps = {
|
||||
left: sceneStyle.left,
|
||||
};
|
||||
if (sceneIndex !== this.state.transitionFromIndex) {
|
||||
// If we are not in a transition from this index, make sure opacity is 0
|
||||
// to prevent the enabled scene from flashing over the presented scene
|
||||
enabledSceneNativeProps.opacity = 0;
|
||||
}
|
||||
this.refs['scene_' + sceneIndex] &&
|
||||
this.refs['scene_' + sceneIndex].setNativeProps(enabledSceneNativeProps);
|
||||
},
|
||||
|
||||
_onAnimationStart: function() {
|
||||
var fromIndex = this.state.presentedIndex;
|
||||
var toIndex = this.state.presentedIndex;
|
||||
|
@ -648,7 +708,6 @@ var Navigator = React.createClass({
|
|||
}
|
||||
this._setRenderSceneToHarwareTextureAndroid(fromIndex, true);
|
||||
this._setRenderSceneToHarwareTextureAndroid(toIndex, true);
|
||||
|
||||
var navBar = this._navBar;
|
||||
if (navBar && navBar.onAnimationStart) {
|
||||
navBar.onAnimationStart(fromIndex, toIndex);
|
||||
|
@ -780,6 +839,8 @@ var Navigator = React.createClass({
|
|||
|
||||
_attachGesture: function(gestureId) {
|
||||
this.state.activeGesture = gestureId;
|
||||
var gesturingToIndex = this.state.presentedIndex + this._deltaForGestureAction(this.state.activeGesture);
|
||||
this._enableScene(gesturingToIndex);
|
||||
},
|
||||
|
||||
_detachGesture: function() {
|
||||
|
@ -810,6 +871,7 @@ var Navigator = React.createClass({
|
|||
(gesture.fullDistance - gestureDetectMovement);
|
||||
if (nextProgress < 0 && gesture.isDetachable) {
|
||||
this._detachGesture();
|
||||
this.spring.setCurrentValue(0);
|
||||
}
|
||||
if (this._doesGestureOverswipe(this.state.activeGesture)) {
|
||||
var frictionConstant = gesture.overswipe.frictionConstant;
|
||||
|
@ -847,13 +909,17 @@ var Navigator = React.createClass({
|
|||
var travelDist = isTravelVertical ? gestureState.dy : gestureState.dx;
|
||||
var oppositeAxisTravelDist =
|
||||
isTravelVertical ? gestureState.dx : gestureState.dy;
|
||||
var edgeHitWidth = gesture.edgeHitWidth;
|
||||
if (isTravelInverted) {
|
||||
currentLoc = -currentLoc;
|
||||
travelDist = -travelDist;
|
||||
oppositeAxisTravelDist = -oppositeAxisTravelDist;
|
||||
edgeHitWidth = isTravelVertical ?
|
||||
-(SCREEN_HEIGHT - edgeHitWidth) :
|
||||
-(SCREEN_WIDTH - edgeHitWidth);
|
||||
}
|
||||
var moveStartedInRegion = gesture.edgeHitWidth == null ||
|
||||
currentLoc < gesture.edgeHitWidth;
|
||||
currentLoc < edgeHitWidth;
|
||||
var moveTravelledFarEnough =
|
||||
travelDist >= gesture.gestureDetectMovement &&
|
||||
travelDist > oppositeAxisTravelDist * gesture.directionRatio;
|
||||
|
@ -924,6 +990,7 @@ var Navigator = React.createClass({
|
|||
_jumpN: function(n) {
|
||||
var destIndex = this._getDestIndexWithinBounds(n);
|
||||
var requestTransitionAndResetUpdatingRange = () => {
|
||||
this._enableScene(destIndex);
|
||||
this._transitionTo(destIndex);
|
||||
this._resetUpdatingRange();
|
||||
};
|
||||
|
@ -957,12 +1024,14 @@ var Navigator = React.createClass({
|
|||
var activeIDStack = this.state.idStack.slice(0, activeLength);
|
||||
var activeAnimationConfigStack = this.state.sceneConfigStack.slice(0, activeLength);
|
||||
var nextStack = activeStack.concat([route]);
|
||||
var destIndex = nextStack.length - 1;
|
||||
var nextIDStack = activeIDStack.concat([getuid()]);
|
||||
var nextAnimationConfigStack = activeAnimationConfigStack.concat([
|
||||
this.props.configureScene(route),
|
||||
]);
|
||||
var requestTransitionAndResetUpdatingRange = () => {
|
||||
this._transitionTo(nextStack.length - 1);
|
||||
this._enableScene(destIndex);
|
||||
this._transitionTo(destIndex);
|
||||
this._resetUpdatingRange();
|
||||
};
|
||||
this.setState({
|
||||
|
@ -983,6 +1052,7 @@ var Navigator = React.createClass({
|
|||
'Cannot pop below zero'
|
||||
);
|
||||
var popIndex = this.state.presentedIndex - n;
|
||||
this._enableScene(popIndex);
|
||||
this._transitionTo(
|
||||
popIndex,
|
||||
null, // default velocity
|
||||
|
@ -1062,7 +1132,7 @@ var Navigator = React.createClass({
|
|||
indexOfRoute !== -1,
|
||||
'Calling pop to route for a route that doesn\'t exist!'
|
||||
);
|
||||
return this.state.routeStack.length - indexOfRoute - 1;
|
||||
return this.state.presentedIndex - indexOfRoute;
|
||||
},
|
||||
|
||||
popToRoute: function(route) {
|
||||
|
@ -1190,13 +1260,18 @@ var Navigator = React.createClass({
|
|||
route,
|
||||
sceneNavigatorContext
|
||||
);
|
||||
var initialSceneStyle = i === this.state.presentedIndex ?
|
||||
styles.currentScene : styles.futureScene;
|
||||
var disabledSceneStyle = null;
|
||||
if (i !== this.state.presentedIndex) {
|
||||
disabledSceneStyle = styles.disabledScene;
|
||||
}
|
||||
return (
|
||||
<View
|
||||
key={this.state.idStack[i]}
|
||||
ref={'scene_' + i}
|
||||
style={[initialSceneStyle, this.props.sceneStyle]}>
|
||||
onStartShouldSetResponderCapture={() => {
|
||||
return i !== this.state.presentedIndex;
|
||||
}}
|
||||
style={[styles.baseScene, this.props.sceneStyle, disabledSceneStyle]}>
|
||||
{React.cloneElement(child, {
|
||||
ref: this._handleItemRef.bind(null, this.state.idStack[i]),
|
||||
})}
|
||||
|
|
|
@ -78,9 +78,11 @@ var NavigatorInterceptor = React.createClass({
|
|||
}
|
||||
switch (action) {
|
||||
case 'pop':
|
||||
return this.props.onPopRequest && this.props.onPopRequest(action, arg1, arg2);
|
||||
return this.props.onPopRequest && this.props.onPopRequest(arg1, arg2);
|
||||
case 'popTo':
|
||||
return this.props.onPopToRequest && this.props.onPopToRequest(arg1, arg2);
|
||||
case 'push':
|
||||
return this.props.onPushRequest && this.props.onPushRequest(action, arg1, arg2);
|
||||
return this.props.onPushRequest && this.props.onPushRequest(arg1, arg2);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -28,9 +28,9 @@ typedef NS_ENUM(NSInteger, RCTPositionErrorCode) {
|
|||
#define RCT_DEFAULT_LOCATION_ACCURACY kCLLocationAccuracyHundredMeters
|
||||
|
||||
typedef struct {
|
||||
NSTimeInterval timeout;
|
||||
NSTimeInterval maximumAge;
|
||||
CLLocationAccuracy accuracy;
|
||||
double timeout;
|
||||
double maximumAge;
|
||||
double accuracy;
|
||||
} RCTLocationOptions;
|
||||
|
||||
@implementation RCTConvert (RCTLocationOptions)
|
||||
|
|
|
@ -29,19 +29,4 @@ var ImageStylePropTypes = {
|
|||
opacity: ReactPropTypes.number,
|
||||
};
|
||||
|
||||
// Image doesn't support padding correctly (#4841912)
|
||||
var unsupportedProps = Object.keys({
|
||||
padding: null,
|
||||
paddingTop: null,
|
||||
paddingLeft: null,
|
||||
paddingRight: null,
|
||||
paddingBottom: null,
|
||||
paddingVertical: null,
|
||||
paddingHorizontal: null,
|
||||
});
|
||||
|
||||
for (var i = 0; i < unsupportedProps.length; i++) {
|
||||
delete ImageStylePropTypes[unsupportedProps[i]];
|
||||
}
|
||||
|
||||
module.exports = ImageStylePropTypes;
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
*/
|
||||
'use strict';
|
||||
|
||||
var Platform = require('Platform');
|
||||
var RCTExceptionsManager = require('NativeModules').ExceptionsManager;
|
||||
|
||||
var loadSourceMap = require('loadSourceMap');
|
||||
|
|
|
@ -79,6 +79,16 @@ function setupRedBoxErrorHandler() {
|
|||
ErrorUtils.setGlobalHandler(handleErrorWithRedBox);
|
||||
}
|
||||
|
||||
function setupRedBoxConsoleErrorHandler() {
|
||||
// ExceptionsManager transitively requires Promise so we install it after
|
||||
var ExceptionsManager = require('ExceptionsManager');
|
||||
var Platform = require('Platform');
|
||||
// TODO (#6925182): Enable console.error redbox on Android
|
||||
if (__DEV__ && Platform.OS === 'ios') {
|
||||
ExceptionsManager.installConsoleErrorReporter();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up a set of window environment wrappers that ensure that the
|
||||
* BatchedBridge is flushed after each tick. In both the case of the
|
||||
|
@ -139,4 +149,5 @@ setupTimers();
|
|||
setupAlert();
|
||||
setupPromise();
|
||||
setupXHR();
|
||||
setupRedBoxConsoleErrorHandler();
|
||||
setupGeolocation();
|
||||
|
|
|
@ -143,10 +143,7 @@
|
|||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"DEBUG=1",
|
||||
"$(inherited)",
|
||||
);
|
||||
GCC_PREPROCESSOR_DEFINITIONS = "DEBUG=1";
|
||||
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
|
@ -182,6 +179,7 @@
|
|||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = "";
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
|
@ -199,6 +197,7 @@
|
|||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_STATIC_ANALYZER_MODE = deep;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = "$(inherited)";
|
||||
HEADER_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
|
||||
|
|
|
@ -13,8 +13,6 @@
|
|||
|
||||
@interface RCTPushNotificationManager : NSObject <RCTBridgeModule>
|
||||
|
||||
- (instancetype)initWithInitialNotification:(NSDictionary *)initialNotification NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
+ (void)application:(UIApplication *)application didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings;
|
||||
+ (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)notification;
|
||||
|
||||
|
|
|
@ -24,14 +24,8 @@ RCT_EXPORT_MODULE()
|
|||
@synthesize bridge = _bridge;
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
return [self initWithInitialNotification:nil];
|
||||
}
|
||||
|
||||
- (instancetype)initWithInitialNotification:(NSDictionary *)initialNotification
|
||||
{
|
||||
if ((self = [super init])) {
|
||||
_initialNotification = [initialNotification copy];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(handleRemoteNotificationReceived:)
|
||||
name:RCTRemoteNotificationReceived
|
||||
|
@ -45,6 +39,12 @@ RCT_EXPORT_MODULE()
|
|||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
}
|
||||
|
||||
- (void)setBridge:(RCTBridge *)bridge
|
||||
{
|
||||
_bridge = bridge;
|
||||
_initialNotification = [bridge.launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey] copy];
|
||||
}
|
||||
|
||||
+ (void)application:(UIApplication *)application didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings
|
||||
{
|
||||
if ([application respondsToSelector:@selector(registerForRemoteNotifications)]) {
|
||||
|
|
|
@ -10,7 +10,6 @@
|
|||
#import "RCTTestRunner.h"
|
||||
|
||||
#import "FBSnapshotTestController.h"
|
||||
#import "RCTDefines.h"
|
||||
#import "RCTRedBox.h"
|
||||
#import "RCTRootView.h"
|
||||
#import "RCTTestModule.h"
|
||||
|
@ -18,6 +17,12 @@
|
|||
|
||||
#define TIMEOUT_SECONDS 240
|
||||
|
||||
@interface RCTBridge (RCTTestRunner)
|
||||
|
||||
@property (nonatomic, weak) RCTBridge *batchedBridge;
|
||||
|
||||
@end
|
||||
|
||||
@implementation RCTTestRunner
|
||||
{
|
||||
FBSnapshotTestController *_testController;
|
||||
|
@ -67,7 +72,7 @@
|
|||
rootView.frame = CGRectMake(0, 0, 320, 2000); // Constant size for testing on multiple devices
|
||||
|
||||
NSString *testModuleName = RCTBridgeModuleNameForClass([RCTTestModule class]);
|
||||
RCTTestModule *testModule = rootView.bridge.modules[testModuleName];
|
||||
RCTTestModule *testModule = rootView.bridge.batchedBridge.modules[testModuleName];
|
||||
testModule.controller = _testController;
|
||||
testModule.testSelector = test;
|
||||
testModule.view = rootView;
|
||||
|
@ -76,8 +81,6 @@
|
|||
vc.view = [[UIView alloc] init];
|
||||
[vc.view addSubview:rootView]; // Add as subview so it doesn't get resized
|
||||
|
||||
#if RCT_DEBUG // Prevents build errors, as RCTRedBox is underfined if RCT_DEBUG=0
|
||||
|
||||
NSDate *date = [NSDate dateWithTimeIntervalSinceNow:TIMEOUT_SECONDS];
|
||||
NSString *error = [[RCTRedBox sharedInstance] currentErrorMessage];
|
||||
while ([date timeIntervalSinceNow] > 0 && ![testModule isDone] && error == nil) {
|
||||
|
@ -86,8 +89,6 @@
|
|||
error = [[RCTRedBox sharedInstance] currentErrorMessage];
|
||||
}
|
||||
[rootView removeFromSuperview];
|
||||
[rootView.bridge invalidate];
|
||||
[rootView invalidate];
|
||||
RCTAssert(vc.view.subviews.count == 0, @"There shouldn't be any other views: %@", vc.view);
|
||||
vc.view = nil;
|
||||
[[RCTRedBox sharedInstance] dismiss];
|
||||
|
@ -98,13 +99,6 @@
|
|||
} else {
|
||||
RCTAssert([testModule isDone], @"Test didn't finish within %d seconds", TIMEOUT_SECONDS);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
expectErrorBlock(@"RCTRedBox unavailable. Set RCT_DEBUG=1 for testing.");
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
@ -66,7 +66,8 @@ typedef void (^WSMessageCallback)(NSError *error, NSDictionary *reply);
|
|||
retries--;
|
||||
}
|
||||
if (!runtimeIsReady) {
|
||||
RCTLogError(@"Runtime is not ready. Do you have Chrome open?");
|
||||
RCTLogError(@"Runtime is not ready. Make sure Chrome is running and not "
|
||||
"paused on a breakpoint or exception and try reloading again.");
|
||||
[self invalidate];
|
||||
return nil;
|
||||
}
|
||||
|
|
|
@ -11,8 +11,6 @@
|
|||
*/
|
||||
'use strict';
|
||||
|
||||
require('ExceptionsManager').installConsoleErrorReporter();
|
||||
|
||||
var React = require('React');
|
||||
var StyleSheet = require('StyleSheet');
|
||||
var View = require('View');
|
||||
|
|
|
@ -0,0 +1,250 @@
|
|||
// !$*UTF8*$!
|
||||
{
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 46;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
13DBA45E1AEE749000A17CF8 /* RCTSettingsManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 13DBA45D1AEE749000A17CF8 /* RCTSettingsManager.m */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXCopyFilesBuildPhase section */
|
||||
58B511D91A9E6C8500147676 /* CopyFiles */ = {
|
||||
isa = PBXCopyFilesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
dstPath = "include/$(PRODUCT_NAME)";
|
||||
dstSubfolderSpec = 16;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXCopyFilesBuildPhase section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
134814201AA4EA6300B7C361 /* libRCTSettings.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRCTSettings.a; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
13DBA45C1AEE749000A17CF8 /* RCTSettingsManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTSettingsManager.h; sourceTree = "<group>"; };
|
||||
13DBA45D1AEE749000A17CF8 /* RCTSettingsManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTSettingsManager.m; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
58B511D81A9E6C8500147676 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
134814211AA4EA7D00B7C361 /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
134814201AA4EA6300B7C361 /* libRCTSettings.a */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
58B511D21A9E6C8500147676 = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
13DBA45C1AEE749000A17CF8 /* RCTSettingsManager.h */,
|
||||
13DBA45D1AEE749000A17CF8 /* RCTSettingsManager.m */,
|
||||
134814211AA4EA7D00B7C361 /* Products */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
58B511DA1A9E6C8500147676 /* RCTSettings */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 58B511EF1A9E6C8500147676 /* Build configuration list for PBXNativeTarget "RCTSettings" */;
|
||||
buildPhases = (
|
||||
58B511D71A9E6C8500147676 /* Sources */,
|
||||
58B511D81A9E6C8500147676 /* Frameworks */,
|
||||
58B511D91A9E6C8500147676 /* CopyFiles */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = RCTSettings;
|
||||
productName = RCTDataManager;
|
||||
productReference = 134814201AA4EA6300B7C361 /* libRCTSettings.a */;
|
||||
productType = "com.apple.product-type.library.static";
|
||||
};
|
||||
/* End PBXNativeTarget section */
|
||||
|
||||
/* Begin PBXProject section */
|
||||
58B511D31A9E6C8500147676 /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastUpgradeCheck = 0610;
|
||||
ORGANIZATIONNAME = Facebook;
|
||||
TargetAttributes = {
|
||||
58B511DA1A9E6C8500147676 = {
|
||||
CreatedOnToolsVersion = 6.1.1;
|
||||
};
|
||||
};
|
||||
};
|
||||
buildConfigurationList = 58B511D61A9E6C8500147676 /* Build configuration list for PBXProject "RCTSettings" */;
|
||||
compatibilityVersion = "Xcode 3.2";
|
||||
developmentRegion = English;
|
||||
hasScannedForEncodings = 0;
|
||||
knownRegions = (
|
||||
en,
|
||||
);
|
||||
mainGroup = 58B511D21A9E6C8500147676;
|
||||
productRefGroup = 58B511D21A9E6C8500147676;
|
||||
projectDirPath = "";
|
||||
projectRoot = "";
|
||||
targets = (
|
||||
58B511DA1A9E6C8500147676 /* RCTSettings */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
58B511D71A9E6C8500147676 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
13DBA45E1AEE749000A17CF8 /* RCTSettingsManager.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
58B511ED1A9E6C8500147676 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"DEBUG=1",
|
||||
"$(inherited)",
|
||||
);
|
||||
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 8.1;
|
||||
MTL_ENABLE_DEBUG_INFO = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
SDKROOT = iphoneos;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
58B511EE1A9E6C8500147676 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
COPY_PHASE_STRIP = YES;
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 8.1;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
SDKROOT = iphoneos;
|
||||
VALIDATE_PRODUCT = YES;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
58B511F01A9E6C8500147676 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
HEADER_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
|
||||
"$(SRCROOT)/../../React/**",
|
||||
);
|
||||
LIBRARY_SEARCH_PATHS = "$(inherited)";
|
||||
OTHER_LDFLAGS = "-ObjC";
|
||||
PRODUCT_NAME = RCTSettings;
|
||||
SKIP_INSTALL = YES;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
58B511F11A9E6C8500147676 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
HEADER_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
|
||||
"$(SRCROOT)/../../React/**",
|
||||
);
|
||||
LIBRARY_SEARCH_PATHS = "$(inherited)";
|
||||
OTHER_LDFLAGS = "-ObjC";
|
||||
PRODUCT_NAME = RCTSettings;
|
||||
SKIP_INSTALL = YES;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
58B511D61A9E6C8500147676 /* Build configuration list for PBXProject "RCTSettings" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
58B511ED1A9E6C8500147676 /* Debug */,
|
||||
58B511EE1A9E6C8500147676 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
58B511EF1A9E6C8500147676 /* Build configuration list for PBXNativeTarget "RCTSettings" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
58B511F01A9E6C8500147676 /* Debug */,
|
||||
58B511F11A9E6C8500147676 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
};
|
||||
rootObject = 58B511D31A9E6C8500147676 /* Project object */;
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
/**
|
||||
* 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>
|
||||
|
||||
#import "RCTBridgeModule.h"
|
||||
|
||||
@interface RCTSettingsManager : NSObject <RCTBridgeModule>
|
||||
|
||||
- (instancetype)initWithUserDefaults:(NSUserDefaults *)defaults NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
@end
|
|
@ -0,0 +1,100 @@
|
|||
/**
|
||||
* 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 "RCTSettingsManager.h"
|
||||
|
||||
#import "RCTBridge.h"
|
||||
#import "RCTConvert.h"
|
||||
#import "RCTEventDispatcher.h"
|
||||
|
||||
@implementation RCTSettingsManager
|
||||
{
|
||||
BOOL _ignoringUpdates;
|
||||
NSUserDefaults *_defaults;
|
||||
}
|
||||
|
||||
@synthesize bridge = _bridge;
|
||||
|
||||
RCT_EXPORT_MODULE()
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
return [self initWithUserDefaults:[NSUserDefaults standardUserDefaults]];
|
||||
}
|
||||
|
||||
- (instancetype)initWithUserDefaults:(NSUserDefaults *)defaults
|
||||
{
|
||||
if ((self = [super init])) {
|
||||
_defaults = defaults;
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(userDefaultsDidChange:)
|
||||
name:NSUserDefaultsDidChangeNotification
|
||||
object:_defaults];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
}
|
||||
|
||||
- (void)userDefaultsDidChange:(NSNotification *)note
|
||||
{
|
||||
if (_ignoringUpdates) {
|
||||
return;
|
||||
}
|
||||
|
||||
[_bridge.eventDispatcher sendDeviceEventWithName:@"settingsUpdated" body:[_defaults dictionaryRepresentation]];
|
||||
}
|
||||
|
||||
- (NSDictionary *)constantsToExport
|
||||
{
|
||||
return @{
|
||||
@"settings": [_defaults dictionaryRepresentation]
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Set one or more values in the settings.
|
||||
* TODO: would it be useful to have a callback for when this has completed?
|
||||
*/
|
||||
RCT_EXPORT_METHOD(setValues:(NSDictionary *)values)
|
||||
{
|
||||
_ignoringUpdates = YES;
|
||||
[values enumerateKeysAndObjectsUsingBlock:^(NSString *key, id json, BOOL *stop) {
|
||||
id plist = [RCTConvert NSPropertyList:json];
|
||||
if (plist) {
|
||||
[_defaults setObject:plist forKey:key];
|
||||
} else {
|
||||
[_defaults removeObjectForKey:key];
|
||||
}
|
||||
}];
|
||||
|
||||
[_defaults synchronize];
|
||||
_ignoringUpdates = NO;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove some values from the settings.
|
||||
*/
|
||||
RCT_EXPORT_METHOD(deleteValues:(NSStringArray *)keys)
|
||||
{
|
||||
_ignoringUpdates = YES;
|
||||
for (NSString *key in keys) {
|
||||
[_defaults removeObjectForKey:key];
|
||||
}
|
||||
|
||||
[_defaults synchronize];
|
||||
_ignoringUpdates = NO;
|
||||
}
|
||||
|
||||
@end
|
|
@ -0,0 +1,34 @@
|
|||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
* @providesModule Settings
|
||||
* @flow
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var Settings = {
|
||||
get(key: string): mixed {
|
||||
console.warn('Settings is not yet supported on Android');
|
||||
return null;
|
||||
},
|
||||
|
||||
set(settings: Object) {
|
||||
console.warn('Settings is not yet supported on Android');
|
||||
},
|
||||
|
||||
watchKeys(keys: string | Array<string>, callback: Function): number {
|
||||
console.warn('Settings is not yet supported on Android');
|
||||
return -1;
|
||||
},
|
||||
|
||||
clearWatch(watchId: number) {
|
||||
console.warn('Settings is not yet supported on Android');
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = Settings;
|
|
@ -0,0 +1,77 @@
|
|||
/**
|
||||
* Copyright (c) 2015-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
* @providesModule Settings
|
||||
* @flow
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var RCTDeviceEventEmitter = require('RCTDeviceEventEmitter');
|
||||
var RCTSettingsManager = require('NativeModules').SettingsManager;
|
||||
|
||||
var invariant = require('invariant');
|
||||
|
||||
var subscriptions: Array<{keys: Array<string>; callback: ?Function}> = [];
|
||||
|
||||
var Settings = {
|
||||
_settings: RCTSettingsManager.settings,
|
||||
|
||||
get(key: string): mixed {
|
||||
return this._settings[key];
|
||||
},
|
||||
|
||||
set(settings: Object) {
|
||||
this._settings = Object.assign(this._settings, settings);
|
||||
RCTSettingsManager.setValues(settings);
|
||||
},
|
||||
|
||||
watchKeys(keys: string | Array<string>, callback: Function): number {
|
||||
if (typeof keys == 'string') {
|
||||
keys = [keys];
|
||||
}
|
||||
|
||||
invariant(
|
||||
Array.isArray(keys),
|
||||
'keys should be a string or array of strings'
|
||||
);
|
||||
|
||||
var sid = subscriptions.length;
|
||||
subscriptions.push({keys: keys, callback: callback})
|
||||
return sid;
|
||||
},
|
||||
|
||||
clearWatch(watchId: number) {
|
||||
if (watchId < subscriptions.length) {
|
||||
subscriptions[watchId] = {keys: [], callback: null};
|
||||
}
|
||||
},
|
||||
|
||||
_sendObservations(body: Object) {
|
||||
var _this = this;
|
||||
Object.keys(body).forEach((key) => {
|
||||
var newValue = body[key];
|
||||
var didChange = _this._settings[key] !== newValue;
|
||||
_this._settings[key] = newValue;
|
||||
|
||||
if (didChange) {
|
||||
subscriptions.forEach((sub) => {
|
||||
if (~sub.keys.indexOf(key) && sub.callback) {
|
||||
sub.callback();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
RCTDeviceEventEmitter.addListener(
|
||||
'settingsUpdated',
|
||||
Settings._sendObservations.bind(Settings)
|
||||
);
|
||||
|
||||
module.exports = Settings;
|
|
@ -37,7 +37,7 @@ var AsyncStorage = {
|
|||
*/
|
||||
getItem: function(
|
||||
key: string,
|
||||
callback: (error: ?Error, result: ?string) => void
|
||||
callback?: ?(error: ?Error, result: ?string) => void
|
||||
): Promise {
|
||||
return new Promise((resolve, reject) => {
|
||||
RCTAsyncStorage.multiGet([key], function(errors, result) {
|
||||
|
@ -60,7 +60,7 @@ var AsyncStorage = {
|
|||
setItem: function(
|
||||
key: string,
|
||||
value: string,
|
||||
callback: ?(error: ?Error) => void
|
||||
callback?: ?(error: ?Error) => void
|
||||
): Promise {
|
||||
return new Promise((resolve, reject) => {
|
||||
RCTAsyncStorage.multiSet([[key,value]], function(errors) {
|
||||
|
@ -78,7 +78,7 @@ var AsyncStorage = {
|
|||
*/
|
||||
removeItem: function(
|
||||
key: string,
|
||||
callback: ?(error: ?Error) => void
|
||||
callback?: ?(error: ?Error) => void
|
||||
): Promise {
|
||||
return new Promise((resolve, reject) => {
|
||||
RCTAsyncStorage.multiRemove([key], function(errors) {
|
||||
|
@ -100,7 +100,7 @@ var AsyncStorage = {
|
|||
mergeItem: function(
|
||||
key: string,
|
||||
value: string,
|
||||
callback: ?(error: ?Error) => void
|
||||
callback?: ?(error: ?Error) => void
|
||||
): Promise {
|
||||
return new Promise((resolve, reject) => {
|
||||
RCTAsyncStorage.multiMerge([[key,value]], function(errors) {
|
||||
|
@ -119,7 +119,7 @@ var AsyncStorage = {
|
|||
* don't want to call this - use removeItem or multiRemove to clear only your
|
||||
* own keys instead. Returns a `Promise` object.
|
||||
*/
|
||||
clear: function(callback: ?(error: ?Error) => void): Promise {
|
||||
clear: function(callback?: ?(error: ?Error) => void): Promise {
|
||||
return new Promise((resolve, reject) => {
|
||||
RCTAsyncStorage.clear(function(error) {
|
||||
callback && callback(convertError(error));
|
||||
|
@ -135,7 +135,7 @@ var AsyncStorage = {
|
|||
/**
|
||||
* Gets *all* keys known to the system, for all callers, libraries, etc. Returns a `Promise` object.
|
||||
*/
|
||||
getAllKeys: function(callback: (error: ?Error) => void): Promise {
|
||||
getAllKeys: function(callback?: ?(error: ?Error, keys: ?Array<string>) => void): Promise {
|
||||
return new Promise((resolve, reject) => {
|
||||
RCTAsyncStorage.getAllKeys(function(error, keys) {
|
||||
callback && callback(convertError(error), keys);
|
||||
|
@ -166,7 +166,7 @@ var AsyncStorage = {
|
|||
*/
|
||||
multiGet: function(
|
||||
keys: Array<string>,
|
||||
callback: (errors: ?Array<Error>, result: ?Array<Array<string>>) => void
|
||||
callback?: ?(errors: ?Array<Error>, result: ?Array<Array<string>>) => void
|
||||
): Promise {
|
||||
return new Promise((resolve, reject) => {
|
||||
RCTAsyncStorage.multiGet(keys, function(errors, result) {
|
||||
|
@ -189,7 +189,7 @@ var AsyncStorage = {
|
|||
*/
|
||||
multiSet: function(
|
||||
keyValuePairs: Array<Array<string>>,
|
||||
callback: ?(errors: ?Array<Error>) => void
|
||||
callback?: ?(errors: ?Array<Error>) => void
|
||||
): Promise {
|
||||
return new Promise((resolve, reject) => {
|
||||
RCTAsyncStorage.multiSet(keyValuePairs, function(errors) {
|
||||
|
@ -209,7 +209,7 @@ var AsyncStorage = {
|
|||
*/
|
||||
multiRemove: function(
|
||||
keys: Array<string>,
|
||||
callback: ?(errors: ?Array<Error>) => void
|
||||
callback?: ?(errors: ?Array<Error>) => void
|
||||
): Promise {
|
||||
return new Promise((resolve, reject) => {
|
||||
RCTAsyncStorage.multiRemove(keys, function(errors) {
|
||||
|
@ -232,7 +232,7 @@ var AsyncStorage = {
|
|||
*/
|
||||
multiMerge: function(
|
||||
keyValuePairs: Array<Array<string>>,
|
||||
callback: ?(errors: ?Array<Error>) => void
|
||||
callback?: ?(errors: ?Array<Error>) => void
|
||||
): Promise {
|
||||
return new Promise((resolve, reject) => {
|
||||
RCTAsyncStorage.multiMerge(keyValuePairs, function(errors) {
|
||||
|
|
|
@ -17,7 +17,7 @@ RCT_EXPORT_MODULE()
|
|||
|
||||
- (UIView *)view
|
||||
{
|
||||
return nil;
|
||||
return [[UIView alloc] init]; // TODO(#1102) Remove useless views.
|
||||
}
|
||||
|
||||
- (RCTShadowView *)shadowView
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
/**
|
||||
* The examples provided by Facebook are for non-commercial testing and
|
||||
* evaluation purposes only.
|
||||
*
|
||||
* Facebook reserves all rights not expressly granted.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL
|
||||
* FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
|
||||
* AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* @providesModule TextUpdateTest
|
||||
* @flow
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var React = require('react-native');
|
||||
var TimerMixin = require('react-timer-mixin');
|
||||
var {
|
||||
NativeModules,
|
||||
StyleSheet,
|
||||
Text,
|
||||
} = React;
|
||||
|
||||
var TestManager = NativeModules.TestManager || NativeModules.SnapshotTestManager;
|
||||
|
||||
var TextUpdateTest = React.createClass({
|
||||
mixins: [TimerMixin],
|
||||
getInitialState: function() {
|
||||
return {seeMore: true};
|
||||
},
|
||||
componentDidMount: function() {
|
||||
this.requestAnimationFrame(
|
||||
() => this.setState(
|
||||
{seeMore: false},
|
||||
TestManager.markTestCompleted
|
||||
)
|
||||
);
|
||||
},
|
||||
render: function() {
|
||||
return (
|
||||
<Text
|
||||
style={styles.container}
|
||||
onPress={() => this.setState({seeMore: !this.state.seeMore})}>
|
||||
<Text>Tap to see more (bugs)...</Text>
|
||||
{this.state.seeMore && 'raw text'}
|
||||
</Text>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
var styles = StyleSheet.create({
|
||||
container: {
|
||||
margin: 10,
|
||||
marginTop: 100,
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = TextUpdateTest;
|
|
@ -322,23 +322,24 @@ var MessageQueueMixin = {
|
|||
|
||||
processBatch: function(batch) {
|
||||
var self = this;
|
||||
ReactUpdates.batchedUpdates(function() {
|
||||
batch.forEach(function(call) {
|
||||
invariant(
|
||||
call.module === 'BatchedBridge',
|
||||
'All the calls should pass through the BatchedBridge module'
|
||||
);
|
||||
if (call.method === 'callFunctionReturnFlushedQueue') {
|
||||
self.callFunction.apply(self, call.args);
|
||||
} else if (call.method === 'invokeCallbackAndReturnFlushedQueue') {
|
||||
self.invokeCallback.apply(self, call.args);
|
||||
} else {
|
||||
throw new Error(
|
||||
'Unrecognized method called on BatchedBridge: ' + call.method);
|
||||
}
|
||||
return guardReturn(function () {
|
||||
ReactUpdates.batchedUpdates(function() {
|
||||
batch.forEach(function(call) {
|
||||
invariant(
|
||||
call.module === 'BatchedBridge',
|
||||
'All the calls should pass through the BatchedBridge module'
|
||||
);
|
||||
if (call.method === 'callFunctionReturnFlushedQueue') {
|
||||
self._callFunction.apply(self, call.args);
|
||||
} else if (call.method === 'invokeCallbackAndReturnFlushedQueue') {
|
||||
self._invokeCallback.apply(self, call.args);
|
||||
} else {
|
||||
throw new Error(
|
||||
'Unrecognized method called on BatchedBridge: ' + call.method);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
return this.flushedQueue();
|
||||
}, null, this._flushedQueueUnguarded, this);
|
||||
},
|
||||
|
||||
setLoggingEnabled: function(enabled) {
|
||||
|
|
|
@ -5,14 +5,14 @@
|
|||
*/
|
||||
'use strict';
|
||||
|
||||
var dummySize = {w: undefined, h: undefined};
|
||||
var dummySize = {width: undefined, height: undefined};
|
||||
|
||||
var sizesDiffer = function(one, two) {
|
||||
one = one || dummySize;
|
||||
two = two || dummySize;
|
||||
return one !== two && (
|
||||
one.w !== two.w ||
|
||||
one.h !== two.h
|
||||
one.width !== two.width ||
|
||||
one.height !== two.height
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -17,12 +17,19 @@
|
|||
*/
|
||||
function stringifySafe(arg: any): string {
|
||||
var ret;
|
||||
var type = typeof arg;
|
||||
if (arg === undefined) {
|
||||
ret = 'undefined';
|
||||
} else if (arg === null) {
|
||||
ret = 'null';
|
||||
} else if (typeof arg === 'string') {
|
||||
} else if (type === 'string') {
|
||||
ret = '"' + arg + '"';
|
||||
} else if (type === 'function') {
|
||||
try {
|
||||
ret = arg.toString();
|
||||
} catch (e) {
|
||||
ret = '[function unknown]';
|
||||
}
|
||||
} else {
|
||||
// Perform a try catch, just in case the object has a circular
|
||||
// reference or stringify throws for some other reason.
|
||||
|
@ -36,7 +43,7 @@ function stringifySafe(arg: any): string {
|
|||
}
|
||||
}
|
||||
}
|
||||
return ret || '["' + typeof arg + '" failed to stringify]';
|
||||
return ret || '["' + type + '" failed to stringify]';
|
||||
}
|
||||
|
||||
module.exports = stringifySafe;
|
||||
|
|
|
@ -80,15 +80,6 @@ RCT_EXTERN NSString *RCTBridgeModuleNameForClass(Class bridgeModuleClass);
|
|||
__attribute__((used, section("__DATA,RCTImport"))) \
|
||||
static const char *__rct_import_##module##_##method##__ = #module"."#method;
|
||||
|
||||
/**
|
||||
* This method is used to execute a new application script. It is called
|
||||
* internally whenever a JS application bundle is loaded/reloaded, but should
|
||||
* probably not be used at any other time.
|
||||
*/
|
||||
- (void)enqueueApplicationScript:(NSString *)script
|
||||
url:(NSURL *)url
|
||||
onComplete:(RCTJavaScriptCompleteBlock)onComplete;
|
||||
|
||||
/**
|
||||
* URL of the script that was loaded into the bridge.
|
||||
*/
|
||||
|
@ -122,14 +113,4 @@ static const char *__rct_import_##module##_##method##__ = #module"."#method;
|
|||
*/
|
||||
- (void)reload;
|
||||
|
||||
/**
|
||||
* Add a new observer that will be called on every screen refresh.
|
||||
*/
|
||||
- (void)addFrameUpdateObserver:(id<RCTFrameUpdateObserver>)observer;
|
||||
|
||||
/**
|
||||
* Stop receiving screen refresh updates for the given observer.
|
||||
*/
|
||||
- (void)removeFrameUpdateObserver:(id<RCTFrameUpdateObserver>)observer;
|
||||
|
||||
@end
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#import "RCTProfile.h"
|
||||
#import "RCTRedBox.h"
|
||||
#import "RCTRootView.h"
|
||||
#import "RCTSourceCode.h"
|
||||
#import "RCTSparseArray.h"
|
||||
#import "RCTUtils.h"
|
||||
|
||||
|
@ -45,12 +46,6 @@ typedef NS_ENUM(NSUInteger, RCTBridgeFields) {
|
|||
RCTBridgeFieldFlushDateMillis
|
||||
};
|
||||
|
||||
/**
|
||||
* Temporarily allow to turn on and off the call batching in case someone wants
|
||||
* to profile both
|
||||
*/
|
||||
#define BATCHED_BRIDGE 1
|
||||
|
||||
#ifdef __LP64__
|
||||
typedef uint64_t RCTHeaderValue;
|
||||
typedef struct section_64 RCTHeaderSection;
|
||||
|
@ -61,6 +56,11 @@ typedef struct section RCTHeaderSection;
|
|||
#define RCTGetSectByNameFromHeader getsectbynamefromheader
|
||||
#endif
|
||||
|
||||
#define RCTAssertJSThread() \
|
||||
RCTAssert(![NSStringFromClass([_javaScriptExecutor class]) isEqualToString:@"RCTContextExecutor"] || \
|
||||
[[[NSThread currentThread] name] isEqualToString:@"com.facebook.React.JavaScript"], \
|
||||
@"This method must be called on JS thread")
|
||||
|
||||
NSString *const RCTEnqueueNotification = @"RCTEnqueueNotification";
|
||||
NSString *const RCTDequeueNotification = @"RCTDequeueNotification";
|
||||
|
||||
|
@ -122,6 +122,7 @@ static NSArray *RCTJSMethods(void)
|
|||
* RTCBridgeModule protocol to ensure they've been exported. This scanning
|
||||
* functionality is disabled in release mode to improve startup performance.
|
||||
*/
|
||||
static NSDictionary *RCTModuleIDsByName;
|
||||
static NSArray *RCTModuleNamesByID;
|
||||
static NSArray *RCTModuleClassesByID;
|
||||
static NSArray *RCTBridgeModuleClassesByModuleID(void)
|
||||
|
@ -129,8 +130,9 @@ static NSArray *RCTBridgeModuleClassesByModuleID(void)
|
|||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
|
||||
RCTModuleNamesByID = [NSMutableArray array];
|
||||
RCTModuleClassesByID = [NSMutableArray array];
|
||||
RCTModuleIDsByName = [[NSMutableDictionary alloc] init];
|
||||
RCTModuleNamesByID = [[NSMutableArray alloc] init];
|
||||
RCTModuleClassesByID = [[NSMutableArray alloc] init];
|
||||
|
||||
Dl_info info;
|
||||
dladdr(&RCTBridgeModuleClassesByModuleID, &info);
|
||||
|
@ -162,7 +164,9 @@ static NSArray *RCTBridgeModuleClassesByModuleID(void)
|
|||
NSStringFromClass(cls));
|
||||
|
||||
// Register module
|
||||
[(NSMutableArray *)RCTModuleNamesByID addObject:RCTBridgeModuleNameForClass(cls)];
|
||||
NSString *moduleName = RCTBridgeModuleNameForClass(cls);
|
||||
((NSMutableDictionary *)RCTModuleIDsByName)[moduleName] = @(RCTModuleNamesByID.count);
|
||||
[(NSMutableArray *)RCTModuleNamesByID addObject:moduleName];
|
||||
[(NSMutableArray *)RCTModuleClassesByID addObject:cls];
|
||||
}
|
||||
}
|
||||
|
@ -199,17 +203,32 @@ static NSArray *RCTBridgeModuleClassesByModuleID(void)
|
|||
return RCTModuleClassesByID;
|
||||
}
|
||||
|
||||
@class RCTBatchedBridge;
|
||||
|
||||
@interface RCTBridge ()
|
||||
|
||||
@property (nonatomic, strong) RCTBatchedBridge *batchedBridge;
|
||||
@property (nonatomic, strong) RCTBridgeModuleProviderBlock moduleProvider;
|
||||
@property (nonatomic, strong, readwrite) RCTEventDispatcher *eventDispatcher;
|
||||
|
||||
- (void)_invokeAndProcessModule:(NSString *)module
|
||||
method:(NSString *)method
|
||||
arguments:(NSArray *)args
|
||||
context:(NSNumber *)context;
|
||||
|
||||
@end
|
||||
|
||||
@interface RCTBatchedBridge : RCTBridge <RCTInvalidating>
|
||||
|
||||
@property (nonatomic, weak) RCTBridge *parentBridge;
|
||||
|
||||
- (instancetype)initWithParentBridge:(RCTBridge *)bridge;
|
||||
|
||||
- (void)_actuallyInvokeAndProcessModule:(NSString *)module
|
||||
method:(NSString *)method
|
||||
arguments:(NSArray *)args
|
||||
context:(NSNumber *)context;
|
||||
|
||||
@end
|
||||
|
||||
/**
|
||||
|
@ -234,8 +253,6 @@ static NSArray *RCTBridgeModuleClassesByModuleID(void)
|
|||
dispatch_block_t _methodQueue;
|
||||
}
|
||||
|
||||
static Class _globalExecutorClass;
|
||||
|
||||
static NSString *RCTStringUpToFirstArgument(NSString *methodName)
|
||||
{
|
||||
NSRange colonRange = [methodName rangeOfString:@":"];
|
||||
|
@ -381,10 +398,8 @@ static NSString *RCTStringUpToFirstArgument(NSString *methodName)
|
|||
|
||||
case '{': {
|
||||
[argumentBlocks addObject:^(RCTBridge *bridge, NSNumber *context, NSInvocation *invocation, NSUInteger index, id json) {
|
||||
NSUInteger size;
|
||||
NSGetSizeAndAlignment(argumentType, &size, NULL);
|
||||
void *returnValue = malloc(size);
|
||||
NSMethodSignature *methodSignature = [RCTConvert methodSignatureForSelector:selector];
|
||||
void *returnValue = malloc(methodSignature.methodReturnLength);
|
||||
NSInvocation *_invocation = [NSInvocation invocationWithMethodSignature:methodSignature];
|
||||
[_invocation setTarget:[RCTConvert class]];
|
||||
[_invocation setSelector:selector];
|
||||
|
@ -700,6 +715,7 @@ static NSDictionary *RCTLocalModulesConfig()
|
|||
@"methods": [[NSMutableDictionary alloc] init]
|
||||
};
|
||||
localModules[moduleName] = module;
|
||||
[RCTLocalModuleNames addObject:moduleName];
|
||||
}
|
||||
|
||||
// Add method if it doesn't already exist
|
||||
|
@ -710,72 +726,18 @@ static NSDictionary *RCTLocalModulesConfig()
|
|||
@"methodID": @(methods.count),
|
||||
@"type": @"local"
|
||||
};
|
||||
[RCTLocalMethodNames addObject:methodName];
|
||||
}
|
||||
|
||||
// Add module and method lookup
|
||||
RCTLocalModuleIDs[moduleDotMethod] = module[@"moduleID"];
|
||||
RCTLocalMethodIDs[moduleDotMethod] = methods[methodName][@"methodID"];
|
||||
[RCTLocalModuleNames addObject:moduleName];
|
||||
[RCTLocalMethodNames addObject:methodName];
|
||||
}
|
||||
});
|
||||
|
||||
return localModules;
|
||||
}
|
||||
|
||||
@interface RCTDisplayLink : NSObject <RCTInvalidating>
|
||||
|
||||
- (instancetype)initWithBridge:(RCTBridge *)bridge selector:(SEL)selector NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
@end
|
||||
|
||||
@interface RCTBridge (RCTDisplayLink)
|
||||
|
||||
- (void)_update:(CADisplayLink *)displayLink;
|
||||
|
||||
@end
|
||||
|
||||
@implementation RCTDisplayLink
|
||||
{
|
||||
__weak RCTBridge *_bridge;
|
||||
CADisplayLink *_displayLink;
|
||||
SEL _selector;
|
||||
}
|
||||
|
||||
- (instancetype)initWithBridge:(RCTBridge *)bridge selector:(SEL)selector
|
||||
{
|
||||
if ((self = [super init])) {
|
||||
_bridge = bridge;
|
||||
_selector = selector;
|
||||
_displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(_update:)];
|
||||
[_displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (BOOL)isValid
|
||||
{
|
||||
return _displayLink != nil;
|
||||
}
|
||||
|
||||
- (void)invalidate
|
||||
{
|
||||
if (self.isValid) {
|
||||
[_displayLink invalidate];
|
||||
_displayLink = nil;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)_update:(CADisplayLink *)displayLink
|
||||
{
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
|
||||
[_bridge performSelector:_selector withObject:displayLink];
|
||||
#pragma clang diagnostic pop
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@interface RCTFrameUpdate (Private)
|
||||
|
||||
- (instancetype)initWithDisplayLink:(CADisplayLink *)displayLink;
|
||||
|
@ -796,22 +758,6 @@ static NSDictionary *RCTLocalModulesConfig()
|
|||
@end
|
||||
|
||||
@implementation RCTBridge
|
||||
{
|
||||
RCTSparseArray *_modulesByID;
|
||||
RCTSparseArray *_queuesByID;
|
||||
dispatch_queue_t _methodQueue;
|
||||
NSDictionary *_modulesByName;
|
||||
id<RCTJavaScriptExecutor> _javaScriptExecutor;
|
||||
Class _executorClass;
|
||||
NSURL *_bundleURL;
|
||||
RCTBridgeModuleProviderBlock _moduleProvider;
|
||||
RCTDisplayLink *_displayLink;
|
||||
RCTDisplayLink *_vsyncDisplayLink;
|
||||
NSMutableSet *_frameUpdateObservers;
|
||||
NSMutableArray *_scheduledCalls;
|
||||
RCTSparseArray *_scheduledCallbacks;
|
||||
BOOL _loading;
|
||||
}
|
||||
|
||||
static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
||||
|
||||
|
@ -819,36 +765,231 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
|||
moduleProvider:(RCTBridgeModuleProviderBlock)block
|
||||
launchOptions:(NSDictionary *)launchOptions
|
||||
{
|
||||
RCTAssertMainThread();
|
||||
|
||||
if ((self = [super init])) {
|
||||
/**
|
||||
* Pre register modules
|
||||
*/
|
||||
RCTLocalModulesConfig();
|
||||
|
||||
_bundleURL = bundleURL;
|
||||
_moduleProvider = block;
|
||||
_launchOptions = [launchOptions copy];
|
||||
|
||||
[self setUp];
|
||||
[self bindKeys];
|
||||
[self setUp];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
/**
|
||||
* This runs only on the main thread, but crashes the subclass
|
||||
* RCTAssertMainThread();
|
||||
*/
|
||||
[self invalidate];
|
||||
}
|
||||
|
||||
- (void)bindKeys
|
||||
{
|
||||
RCTAssertMainThread();
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(reload)
|
||||
name:RCTReloadNotification
|
||||
object:nil];
|
||||
|
||||
#if TARGET_IPHONE_SIMULATOR
|
||||
|
||||
__weak RCTBridge *weakSelf = self;
|
||||
RCTKeyCommands *commands = [RCTKeyCommands sharedInstance];
|
||||
|
||||
// reload in current mode
|
||||
[commands registerKeyCommandWithInput:@"r"
|
||||
modifierFlags:UIKeyModifierCommand
|
||||
action:^(UIKeyCommand *command) {
|
||||
[weakSelf reload];
|
||||
}];
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
- (void)reload
|
||||
{
|
||||
/**
|
||||
* AnyThread
|
||||
*/
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[self invalidate];
|
||||
[self setUp];
|
||||
});
|
||||
}
|
||||
|
||||
- (void)setUp
|
||||
{
|
||||
Class executorClass = _executorClass ?: _globalExecutorClass ?: [RCTContextExecutor class];
|
||||
_javaScriptExecutor = RCTCreateExecutor(executorClass);
|
||||
_latestJSExecutor = _javaScriptExecutor;
|
||||
_eventDispatcher = [[RCTEventDispatcher alloc] initWithBridge:self];
|
||||
_methodQueue = dispatch_queue_create("com.facebook.React.BridgeMethodQueue", DISPATCH_QUEUE_SERIAL);
|
||||
_frameUpdateObservers = [[NSMutableSet alloc] init];
|
||||
_scheduledCalls = [[NSMutableArray alloc] init];
|
||||
_scheduledCallbacks = [[RCTSparseArray alloc] init];
|
||||
RCTAssertMainThread();
|
||||
|
||||
[_javaScriptExecutor executeBlockOnJavaScriptQueue:^{
|
||||
_displayLink = [[RCTDisplayLink alloc] initWithBridge:self selector:@selector(_jsThreadUpdate:)];
|
||||
}];
|
||||
_vsyncDisplayLink = [[RCTDisplayLink alloc] initWithBridge:self selector:@selector(_mainThreadUpdate:)];
|
||||
_batchedBridge = [[RCTBatchedBridge alloc] initWithParentBridge:self];
|
||||
}
|
||||
|
||||
- (BOOL)isValid
|
||||
{
|
||||
return _batchedBridge.isValid;
|
||||
}
|
||||
|
||||
- (void)invalidate
|
||||
{
|
||||
RCTAssertMainThread();
|
||||
|
||||
[_batchedBridge invalidate];
|
||||
_batchedBridge = nil;
|
||||
}
|
||||
|
||||
+ (void)logMessage:(NSString *)message level:(NSString *)level
|
||||
{
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
if (!_latestJSExecutor.isValid) {
|
||||
return;
|
||||
}
|
||||
|
||||
[_latestJSExecutor executeJSCall:@"RCTLog"
|
||||
method:@"logIfNoNativeHook"
|
||||
arguments:@[level, message]
|
||||
context:RCTGetExecutorID(_latestJSExecutor)
|
||||
callback:^(id json, NSError *error) {}];
|
||||
});
|
||||
}
|
||||
|
||||
- (NSDictionary *)modules
|
||||
{
|
||||
return _batchedBridge.modules;
|
||||
}
|
||||
|
||||
#define RCT_BRIDGE_WARN(...) \
|
||||
- (void)__VA_ARGS__ \
|
||||
{ \
|
||||
RCTLogMustFix(@"Called method \"%@\" on top level bridge. This method should \
|
||||
only be called from bridge instance in a bridge module", @(__func__)); \
|
||||
}
|
||||
|
||||
RCT_BRIDGE_WARN(enqueueJSCall:(NSString *)moduleDotMethod args:(NSArray *)args)
|
||||
RCT_BRIDGE_WARN(_invokeAndProcessModule:(NSString *)module method:(NSString *)method arguments:(NSArray *)args context:(NSNumber *)context)
|
||||
|
||||
@end
|
||||
|
||||
@implementation RCTBatchedBridge
|
||||
{
|
||||
BOOL _loading;
|
||||
id<RCTJavaScriptExecutor> _javaScriptExecutor;
|
||||
RCTSparseArray *_modulesByID;
|
||||
RCTSparseArray *_queuesByID;
|
||||
dispatch_queue_t _methodQueue;
|
||||
NSDictionary *_modulesByName;
|
||||
CADisplayLink *_mainDisplayLink;
|
||||
CADisplayLink *_jsDisplayLink;
|
||||
NSMutableSet *_frameUpdateObservers;
|
||||
NSMutableArray *_scheduledCalls;
|
||||
RCTSparseArray *_scheduledCallbacks;
|
||||
}
|
||||
|
||||
@synthesize valid = _valid;
|
||||
|
||||
- (instancetype)initWithParentBridge:(RCTBridge *)bridge
|
||||
{
|
||||
if (self = [super init]) {
|
||||
RCTAssertMainThread();
|
||||
|
||||
_parentBridge = bridge;
|
||||
|
||||
/**
|
||||
* Set Initial State
|
||||
*/
|
||||
_valid = YES;
|
||||
_loading = YES;
|
||||
_frameUpdateObservers = [[NSMutableSet alloc] init];
|
||||
_scheduledCalls = [[NSMutableArray alloc] init];
|
||||
_scheduledCallbacks = [[RCTSparseArray alloc] init];
|
||||
_queuesByID = [[RCTSparseArray alloc] init];
|
||||
_jsDisplayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(_jsThreadUpdate:)];
|
||||
|
||||
/**
|
||||
* Initialize executor to allow enqueueing calls
|
||||
*/
|
||||
Class executorClass = self.executorClass ?: [RCTContextExecutor class];
|
||||
_javaScriptExecutor = RCTCreateExecutor(executorClass);
|
||||
_latestJSExecutor = _javaScriptExecutor;
|
||||
|
||||
/**
|
||||
* Setup event dispatcher before initializing modules to allow init calls
|
||||
*/
|
||||
self.eventDispatcher = [[RCTEventDispatcher alloc] initWithBridge:self];
|
||||
|
||||
/**
|
||||
* Initialize and register bridge modules *before* adding the display link
|
||||
* so we don't have threading issues
|
||||
*/
|
||||
_methodQueue = dispatch_queue_create("com.facebook.React.BridgeMethodQueue", DISPATCH_QUEUE_SERIAL);
|
||||
[self registerModules];
|
||||
|
||||
/**
|
||||
* Start the application script
|
||||
*/
|
||||
[self initJS];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSURL *)bundleURL
|
||||
{
|
||||
return _parentBridge.bundleURL;
|
||||
}
|
||||
|
||||
- (NSDictionary *)launchOptions
|
||||
{
|
||||
return _parentBridge.launchOptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Override to ensure that we won't create another nested bridge
|
||||
*/
|
||||
- (void)setUp {}
|
||||
|
||||
- (void)reload
|
||||
{
|
||||
[_parentBridge reload];
|
||||
}
|
||||
|
||||
- (Class)executorClass
|
||||
{
|
||||
return _parentBridge.executorClass;
|
||||
}
|
||||
|
||||
- (void)setExecutorClass:(Class)executorClass
|
||||
{
|
||||
RCTAssertMainThread();
|
||||
|
||||
_parentBridge.executorClass = executorClass;
|
||||
}
|
||||
|
||||
- (BOOL)isLoading
|
||||
{
|
||||
return _loading;
|
||||
}
|
||||
|
||||
- (BOOL)isValid
|
||||
{
|
||||
return _valid;
|
||||
}
|
||||
|
||||
- (void)registerModules
|
||||
{
|
||||
RCTAssertMainThread();
|
||||
|
||||
// Register passed-in module instances
|
||||
NSMutableDictionary *preregisteredModules = [[NSMutableDictionary alloc] init];
|
||||
for (id<RCTBridgeModule> module in _moduleProvider ? _moduleProvider() : nil) {
|
||||
for (id<RCTBridgeModule> module in _parentBridge.moduleProvider ? _parentBridge.moduleProvider() : nil) {
|
||||
preregisteredModules[RCTBridgeModuleNameForClass([module class])] = module;
|
||||
}
|
||||
|
||||
|
@ -895,7 +1036,6 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
|||
}
|
||||
|
||||
// Get method queues
|
||||
_queuesByID = [[RCTSparseArray alloc] init];
|
||||
[_modulesByID enumerateObjectsUsingBlock:^(id<RCTBridgeModule> module, NSNumber *moduleID, BOOL *stop) {
|
||||
if ([module respondsToSelector:@selector(methodQueue)]) {
|
||||
dispatch_queue_t queue = [module methodQueue];
|
||||
|
@ -905,7 +1045,16 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
|||
_queuesByID[moduleID] = [NSNull null];
|
||||
}
|
||||
}
|
||||
|
||||
if ([module conformsToProtocol:@protocol(RCTFrameUpdateObserver)]) {
|
||||
[_frameUpdateObservers addObject:module];
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)initJS
|
||||
{
|
||||
RCTAssertMainThread();
|
||||
|
||||
// Inject module data into JS context
|
||||
NSString *configJSON = RCTJSONStringify(@{
|
||||
|
@ -918,7 +1067,9 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
|||
dispatch_semaphore_signal(semaphore);
|
||||
}];
|
||||
|
||||
_loading = YES;
|
||||
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_NOW);
|
||||
|
||||
NSURL *bundleURL = _parentBridge.bundleURL;
|
||||
if (_javaScriptExecutor == nil) {
|
||||
|
||||
/**
|
||||
|
@ -927,15 +1078,19 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
|||
*/
|
||||
_loading = NO;
|
||||
|
||||
} else if (_bundleURL) { // Allow testing without a script
|
||||
} else if (bundleURL) { // Allow testing without a script
|
||||
|
||||
RCTJavaScriptLoader *loader = [[RCTJavaScriptLoader alloc] initWithBridge:self];
|
||||
[loader loadBundleAtURL:_bundleURL onComplete:^(NSError *error) {
|
||||
[loader loadBundleAtURL:bundleURL onComplete:^(NSError *error, NSString *script) {
|
||||
_loading = NO;
|
||||
if (!self.isValid) {
|
||||
return;
|
||||
}
|
||||
RCTSourceCode *sourceCodeModule = self.modules[RCTBridgeModuleNameForClass([RCTSourceCode class])];
|
||||
sourceCodeModule.scriptURL = bundleURL;
|
||||
sourceCodeModule.scriptText = script;
|
||||
if (error != nil) {
|
||||
|
||||
#if RCT_DEBUG // Red box is only available in debug mode
|
||||
|
||||
NSArray *stack = [[error userInfo] objectForKey:@"stack"];
|
||||
if (stack) {
|
||||
[[RCTRedBox sharedInstance] showErrorMessage:[error localizedDescription]
|
||||
|
@ -945,72 +1100,27 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
|||
withDetails:[error localizedFailureReason]];
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
} else {
|
||||
[self enqueueApplicationScript:script url:bundleURL onComplete:^(NSError *loadError) {
|
||||
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:RCTJavaScriptDidLoadNotification
|
||||
object:self];
|
||||
if (!loadError) {
|
||||
/**
|
||||
* Register the display link to start sending js calls after everything
|
||||
* is setup
|
||||
*/
|
||||
NSRunLoop *targetRunLoop = [_javaScriptExecutor isKindOfClass:[RCTContextExecutor class]] ? [NSRunLoop currentRunLoop] : [NSRunLoop mainRunLoop];
|
||||
[_jsDisplayLink addToRunLoop:targetRunLoop forMode:NSRunLoopCommonModes];
|
||||
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:RCTJavaScriptDidLoadNotification
|
||||
object:_parentBridge
|
||||
userInfo:@{ @"bridge": self }];
|
||||
}
|
||||
}];
|
||||
}
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(reload)
|
||||
name:RCTReloadNotification
|
||||
object:nil];
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)bindKeys
|
||||
{
|
||||
|
||||
#if TARGET_IPHONE_SIMULATOR
|
||||
|
||||
__weak RCTBridge *weakSelf = self;
|
||||
RCTKeyCommands *commands = [RCTKeyCommands sharedInstance];
|
||||
|
||||
// Workaround around the first cmd+R not working: http://openradar.appspot.com/19613391
|
||||
// You can register just the cmd key and do nothing. This will trigger the bug and cmd+R
|
||||
// will work like a charm!
|
||||
[commands registerKeyCommandWithInput:@""
|
||||
modifierFlags:UIKeyModifierCommand
|
||||
action:NULL];
|
||||
// reload in current mode
|
||||
[commands registerKeyCommandWithInput:@"r"
|
||||
modifierFlags:UIKeyModifierCommand
|
||||
action:^(UIKeyCommand *command) {
|
||||
[weakSelf reload];
|
||||
}];
|
||||
// reset to normal mode
|
||||
[commands registerKeyCommandWithInput:@"n"
|
||||
modifierFlags:UIKeyModifierCommand
|
||||
action:^(UIKeyCommand *command) {
|
||||
__strong RCTBridge *strongSelf = weakSelf;
|
||||
strongSelf.executorClass = Nil;
|
||||
[strongSelf reload];
|
||||
}];
|
||||
|
||||
#if RCT_DEV // Debug executors are only available in dev mode
|
||||
|
||||
// reload in debug mode
|
||||
[commands registerKeyCommandWithInput:@"d"
|
||||
modifierFlags:UIKeyModifierCommand
|
||||
action:^(UIKeyCommand *command) {
|
||||
__strong RCTBridge *strongSelf = weakSelf;
|
||||
strongSelf.executorClass = NSClassFromString(@"RCTWebSocketExecutor");
|
||||
if (!strongSelf.executorClass) {
|
||||
strongSelf.executorClass = NSClassFromString(@"RCTWebViewExecutor");
|
||||
}
|
||||
if (!strongSelf.executorClass) {
|
||||
RCTLogError(@"WebSocket debugger is not available. "
|
||||
"Did you forget to include RCTWebSocketExecutor?");
|
||||
}
|
||||
[strongSelf reload];
|
||||
}];
|
||||
#endif
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
- (NSDictionary *)modules
|
||||
{
|
||||
RCTAssert(_modulesByName != nil, @"Bridge modules have not yet been initialized. "
|
||||
|
@ -1019,53 +1129,48 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
|||
return _modulesByName;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
[self invalidate];
|
||||
}
|
||||
|
||||
#pragma mark - RCTInvalidating
|
||||
|
||||
- (BOOL)isValid
|
||||
{
|
||||
return _javaScriptExecutor != nil;
|
||||
}
|
||||
|
||||
- (void)invalidate
|
||||
{
|
||||
if (!self.isValid && _modulesByID == nil) {
|
||||
if (!self.isValid) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (![NSThread isMainThread]) {
|
||||
[self performSelectorOnMainThread:@selector(invalidate) withObject:nil waitUntilDone:YES];
|
||||
return;
|
||||
}
|
||||
RCTAssertMainThread();
|
||||
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
|
||||
// Release executor
|
||||
_valid = NO;
|
||||
if (_latestJSExecutor == _javaScriptExecutor) {
|
||||
_latestJSExecutor = nil;
|
||||
}
|
||||
[_javaScriptExecutor invalidate];
|
||||
_javaScriptExecutor = nil;
|
||||
|
||||
[_displayLink invalidate];
|
||||
[_vsyncDisplayLink invalidate];
|
||||
_frameUpdateObservers = nil;
|
||||
/**
|
||||
* Main Thread deallocations
|
||||
*/
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
[_mainDisplayLink invalidate];
|
||||
|
||||
// Invalidate modules
|
||||
for (id target in _modulesByID.allObjects) {
|
||||
if ([target respondsToSelector:@selector(invalidate)]) {
|
||||
[(id<RCTInvalidating>)target invalidate];
|
||||
[_javaScriptExecutor executeBlockOnJavaScriptQueue:^{
|
||||
/**
|
||||
* JS Thread deallocations
|
||||
*/
|
||||
[_javaScriptExecutor invalidate];
|
||||
[_jsDisplayLink invalidate];
|
||||
|
||||
// Invalidate modules
|
||||
for (id target in _modulesByID.allObjects) {
|
||||
if ([target respondsToSelector:@selector(invalidate)]) {
|
||||
[(id<RCTInvalidating>)target invalidate];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Release modules (breaks retain cycle if module has strong bridge reference)
|
||||
_modulesByID = nil;
|
||||
_queuesByID = nil;
|
||||
_modulesByName = nil;
|
||||
// Release modules (breaks retain cycle if module has strong bridge reference)
|
||||
_javaScriptExecutor = nil;
|
||||
_frameUpdateObservers = nil;
|
||||
_modulesByID = nil;
|
||||
_queuesByID = nil;
|
||||
_modulesByName = nil;
|
||||
}];
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1091,7 +1196,7 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
|||
|
||||
[self _invokeAndProcessModule:@"BatchedBridge"
|
||||
method:@"callFunctionReturnFlushedQueue"
|
||||
arguments:@[moduleID, methodID, args ?: @[]]
|
||||
arguments:@[moduleID ?: @0, methodID ?: @0, args ?: @[]]
|
||||
context:RCTGetExecutorID(_javaScriptExecutor)];
|
||||
}
|
||||
|
||||
|
@ -1100,6 +1205,8 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
|||
*/
|
||||
- (void)_immediatelyCallTimer:(NSNumber *)timer
|
||||
{
|
||||
RCTAssertJSThread();
|
||||
|
||||
NSString *moduleDotMethod = @"RCTJSTimers.callTimers";
|
||||
NSNumber *moduleID = RCTLocalModuleIDs[moduleDotMethod];
|
||||
RCTAssert(moduleID != nil, @"Module '%@' not registered.",
|
||||
|
@ -1108,35 +1215,29 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
|||
NSNumber *methodID = RCTLocalMethodIDs[moduleDotMethod];
|
||||
RCTAssert(methodID != nil, @"Method '%@' not registered.", moduleDotMethod);
|
||||
|
||||
if (!_loading) {
|
||||
#if BATCHED_BRIDGE
|
||||
dispatch_block_t block = ^{
|
||||
[self _actuallyInvokeAndProcessModule:@"BatchedBridge"
|
||||
method:@"callFunctionReturnFlushedQueue"
|
||||
arguments:@[moduleID, methodID, @[@[timer]]]
|
||||
context:RCTGetExecutorID(_javaScriptExecutor)];
|
||||
};
|
||||
if ([_javaScriptExecutor respondsToSelector:@selector(executeAsyncBlockOnJavaScriptQueue:)]) {
|
||||
[_javaScriptExecutor executeAsyncBlockOnJavaScriptQueue:block];
|
||||
} else {
|
||||
[_javaScriptExecutor executeBlockOnJavaScriptQueue:block];
|
||||
}
|
||||
dispatch_block_t block = ^{
|
||||
[self _actuallyInvokeAndProcessModule:@"BatchedBridge"
|
||||
method:@"callFunctionReturnFlushedQueue"
|
||||
arguments:@[moduleID, methodID, @[@[timer]]]
|
||||
context:RCTGetExecutorID(_javaScriptExecutor)];
|
||||
};
|
||||
|
||||
#else
|
||||
|
||||
[self _invokeAndProcessModule:@"BatchedBridge"
|
||||
method:@"callFunctionReturnFlushedQueue"
|
||||
arguments:@[moduleID, methodID, @[@[timer]]]
|
||||
context:RCTGetExecutorID(_javaScriptExecutor)];
|
||||
#endif
|
||||
if ([_javaScriptExecutor respondsToSelector:@selector(executeAsyncBlockOnJavaScriptQueue:)]) {
|
||||
[_javaScriptExecutor executeAsyncBlockOnJavaScriptQueue:block];
|
||||
} else {
|
||||
[_javaScriptExecutor executeBlockOnJavaScriptQueue:block];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)enqueueApplicationScript:(NSString *)script url:(NSURL *)url onComplete:(RCTJavaScriptCompleteBlock)onComplete
|
||||
{
|
||||
RCTAssert(onComplete != nil, @"onComplete block passed in should be non-nil");
|
||||
|
||||
RCTProfileBeginEvent();
|
||||
|
||||
[_javaScriptExecutor executeApplicationScript:script sourceURL:url onComplete:^(NSError *scriptLoadError) {
|
||||
RCTAssertJSThread();
|
||||
|
||||
RCTProfileEndEvent(@"ApplicationScript", @"js_call,init", scriptLoadError);
|
||||
if (scriptLoadError) {
|
||||
onComplete(scriptLoadError);
|
||||
|
@ -1166,7 +1267,13 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
|||
|
||||
- (void)dispatchBlock:(dispatch_block_t)block forModule:(NSNumber *)moduleID
|
||||
{
|
||||
id queue = _queuesByID[moduleID];
|
||||
RCTAssertJSThread();
|
||||
|
||||
id queue = nil;
|
||||
if (moduleID) {
|
||||
queue = _queuesByID[moduleID];
|
||||
}
|
||||
|
||||
if (queue == [NSNull null]) {
|
||||
[_javaScriptExecutor executeBlockOnJavaScriptQueue:block];
|
||||
} else {
|
||||
|
@ -1180,17 +1287,16 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
|||
*/
|
||||
- (void)_invokeAndProcessModule:(NSString *)module method:(NSString *)method arguments:(NSArray *)args context:(NSNumber *)context
|
||||
{
|
||||
#if BATCHED_BRIDGE
|
||||
|
||||
__weak NSMutableArray *weakScheduledCalls = _scheduledCalls;
|
||||
__weak RCTSparseArray *weakScheduledCallbacks = _scheduledCallbacks;
|
||||
/**
|
||||
* AnyThread
|
||||
*/
|
||||
|
||||
__weak RCTBatchedBridge *weakSelf = self;
|
||||
[_javaScriptExecutor executeBlockOnJavaScriptQueue:^{
|
||||
RCTProfileBeginEvent();
|
||||
|
||||
NSMutableArray *scheduledCalls = weakScheduledCalls;
|
||||
RCTSparseArray *scheduledCallbacks = weakScheduledCallbacks;
|
||||
if (!scheduledCalls || !scheduledCallbacks) {
|
||||
RCTBatchedBridge *strongSelf = weakSelf;
|
||||
if (!strongSelf.isValid || !strongSelf->_scheduledCallbacks || !strongSelf->_scheduledCalls) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1206,7 +1312,7 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
|||
* Keep going if it any event emmiter, e.g. RCT(Device|NativeApp)?EventEmitter
|
||||
*/
|
||||
if ([moduleName hasSuffix:@"EventEmitter"]) {
|
||||
for (NSDictionary *call in [scheduledCalls copy]) {
|
||||
for (NSDictionary *call in [strongSelf->_scheduledCalls copy]) {
|
||||
NSArray *callArgs = call[@"args"];
|
||||
/**
|
||||
* If it's the same module && method call on the bridge &&
|
||||
|
@ -1227,9 +1333,9 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
|||
*/
|
||||
if (
|
||||
[args[2][0] isEqual:callArgs[2][0]] &&
|
||||
([moduleName isEqualToString:@"RCTEventEmitter"] ? [args[2][1] isEqual:callArgs[2][1]] : YES)
|
||||
(![moduleName isEqualToString:@"RCTEventEmitter"] || [args[2][1] isEqual:callArgs[2][1]])
|
||||
) {
|
||||
[scheduledCalls removeObject:call];
|
||||
[strongSelf->_scheduledCalls removeObject:call];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1244,9 +1350,9 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
|||
};
|
||||
|
||||
if ([method isEqualToString:@"invokeCallbackAndReturnFlushedQueue"]) {
|
||||
scheduledCallbacks[args[0]] = call;
|
||||
strongSelf->_scheduledCallbacks[args[0]] = call;
|
||||
} else {
|
||||
[scheduledCalls addObject:call];
|
||||
[strongSelf->_scheduledCalls addObject:call];
|
||||
}
|
||||
|
||||
RCTProfileEndEvent(@"enqueue_call", @"objc_call", call);
|
||||
|
@ -1255,10 +1361,14 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
|||
|
||||
- (void)_actuallyInvokeAndProcessModule:(NSString *)module method:(NSString *)method arguments:(NSArray *)args context:(NSNumber *)context
|
||||
{
|
||||
#endif
|
||||
RCTAssertJSThread();
|
||||
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:RCTEnqueueNotification object:nil userInfo:nil];
|
||||
|
||||
RCTJavaScriptCallback processResponse = ^(id json, NSError *error) {
|
||||
if (!self.isValid) {
|
||||
return;
|
||||
}
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:RCTDequeueNotification object:nil userInfo:nil];
|
||||
[self _handleBuffer:json context:context];
|
||||
};
|
||||
|
@ -1274,6 +1384,8 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
|||
|
||||
- (void)_handleBuffer:(id)buffer context:(NSNumber *)context
|
||||
{
|
||||
RCTAssertJSThread();
|
||||
|
||||
if (buffer == nil || buffer == (id)kCFNull) {
|
||||
return;
|
||||
}
|
||||
|
@ -1344,6 +1456,11 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
|||
params:(NSArray *)params
|
||||
context:(NSNumber *)context
|
||||
{
|
||||
RCTAssertJSThread();
|
||||
|
||||
if (!self.isValid) {
|
||||
return NO;
|
||||
}
|
||||
|
||||
if (RCT_DEBUG && ![params isKindOfClass:[NSArray class]]) {
|
||||
RCTLogError(@"Invalid module/method/params tuple for request #%zd", i);
|
||||
|
@ -1367,10 +1484,10 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
|||
return NO;
|
||||
}
|
||||
|
||||
__weak RCTBridge *weakSelf = self;
|
||||
__weak RCTBatchedBridge *weakSelf = self;
|
||||
[self dispatchBlock:^{
|
||||
RCTProfileBeginEvent();
|
||||
__strong RCTBridge *strongSelf = weakSelf;
|
||||
RCTBatchedBridge *strongSelf = weakSelf;
|
||||
|
||||
if (!strongSelf.isValid) {
|
||||
// strongSelf has been invalidated since the dispatch_async call and this
|
||||
|
@ -1404,18 +1521,21 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
|||
|
||||
- (void)_jsThreadUpdate:(CADisplayLink *)displayLink
|
||||
{
|
||||
RCTAssertJSThread();
|
||||
|
||||
RCTProfileImmediateEvent(@"JS Thread Tick", displayLink.timestamp, @"g");
|
||||
|
||||
RCTProfileBeginEvent();
|
||||
|
||||
RCTFrameUpdate *frameUpdate = [[RCTFrameUpdate alloc] initWithDisplayLink:displayLink];
|
||||
for (id<RCTFrameUpdateObserver> observer in _frameUpdateObservers) {
|
||||
if (![observer respondsToSelector:@selector(isPaused)] || ![observer isPaused]) {
|
||||
[observer didUpdateFrame:frameUpdate];
|
||||
[self dispatchBlock:^{
|
||||
[observer didUpdateFrame:frameUpdate];
|
||||
} forModule:RCTModuleIDsByName[RCTBridgeModuleNameForClass([observer class])]];
|
||||
}
|
||||
}
|
||||
|
||||
#if BATCHED_BRIDGE
|
||||
|
||||
NSArray *calls = [_scheduledCallbacks.allObjects arrayByAddingObjectsFromArray:_scheduledCalls];
|
||||
NSNumber *currentExecutorID = RCTGetExecutorID(_javaScriptExecutor);
|
||||
calls = [calls filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(NSDictionary *call, NSDictionary *bindings) {
|
||||
|
@ -1430,67 +1550,41 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
|||
context:RCTGetExecutorID(_javaScriptExecutor)];
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
RCTProfileEndEvent(@"DispatchFrameUpdate", @"objc_call", nil);
|
||||
}
|
||||
|
||||
- (void)_mainThreadUpdate:(CADisplayLink *)displayLink
|
||||
{
|
||||
RCTAssertMainThread();
|
||||
|
||||
RCTProfileImmediateEvent(@"VSYNC", displayLink.timestamp, @"g");
|
||||
}
|
||||
|
||||
- (void)addFrameUpdateObserver:(id<RCTFrameUpdateObserver>)observer
|
||||
{
|
||||
[_frameUpdateObservers addObject:observer];
|
||||
}
|
||||
|
||||
- (void)removeFrameUpdateObserver:(id<RCTFrameUpdateObserver>)observer
|
||||
{
|
||||
[_frameUpdateObservers removeObject:observer];
|
||||
}
|
||||
|
||||
- (void)reload
|
||||
{
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
if (!_loading) {
|
||||
// If the bridge has not loaded yet, the context will be already invalid at
|
||||
// the time the javascript gets executed.
|
||||
// It will crash the javascript, and even the next `load` won't render.
|
||||
[self invalidate];
|
||||
[self setUp];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
+ (void)logMessage:(NSString *)message level:(NSString *)level
|
||||
{
|
||||
if (![_latestJSExecutor isValid]) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Note: the js executor could get invalidated while we're trying to call
|
||||
// this...need to watch out for that.
|
||||
[_latestJSExecutor executeJSCall:@"RCTLog"
|
||||
method:@"logIfNoNativeHook"
|
||||
arguments:@[level, message]
|
||||
context:RCTGetExecutorID(_latestJSExecutor)
|
||||
callback:^(id json, NSError *error) {}];
|
||||
}
|
||||
|
||||
- (void)startProfiling
|
||||
{
|
||||
if (![_bundleURL.scheme isEqualToString:@"http"]) {
|
||||
RCTAssertMainThread();
|
||||
|
||||
if (![_parentBridge.bundleURL.scheme isEqualToString:@"http"]) {
|
||||
RCTLogError(@"To run the profiler you must be running from the dev server");
|
||||
return;
|
||||
}
|
||||
|
||||
[_mainDisplayLink invalidate];
|
||||
_mainDisplayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(_mainThreadUpdate:)];
|
||||
[_mainDisplayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
|
||||
|
||||
RCTProfileInit();
|
||||
}
|
||||
|
||||
- (void)stopProfiling
|
||||
{
|
||||
RCTAssertMainThread();
|
||||
|
||||
[_mainDisplayLink invalidate];
|
||||
|
||||
NSString *log = RCTProfileEnd();
|
||||
NSString *URLString = [NSString stringWithFormat:@"%@://%@:%@/profile", _bundleURL.scheme, _bundleURL.host, _bundleURL.port];
|
||||
NSURL *bundleURL = _parentBridge.bundleURL;
|
||||
NSString *URLString = [NSString stringWithFormat:@"%@://%@:%@/profile", bundleURL.scheme, bundleURL.host, bundleURL.port];
|
||||
NSURL *URL = [NSURL URLWithString:URLString];
|
||||
NSMutableURLRequest *URLRequest = [NSMutableURLRequest requestWithURL:URL];
|
||||
URLRequest.HTTPMethod = @"POST";
|
||||
|
|
|
@ -101,6 +101,12 @@ typedef NSArray UIColorArray;
|
|||
typedef NSArray CGColorArray;
|
||||
+ (CGColorArray *)CGColorArray:(id)json;
|
||||
|
||||
/**
|
||||
* Convert a JSON object to a Plist-safe equivalent by stripping null values.
|
||||
*/
|
||||
typedef id NSPropertyList;
|
||||
+ (NSPropertyList)NSPropertyList:(id)json;
|
||||
|
||||
typedef BOOL css_overflow;
|
||||
+ (css_overflow)css_overflow:(id)json;
|
||||
+ (css_flex_direction_t)css_flex_direction_t:(id)json;
|
||||
|
|
|
@ -733,11 +733,6 @@ static BOOL RCTFontIsCondensed(UIFont *font)
|
|||
isItalic = [self RCTFontStyle:style];
|
||||
}
|
||||
|
||||
// Get font weight
|
||||
if (weight) {
|
||||
fontWeight = [self RCTFontWeight:weight];
|
||||
}
|
||||
|
||||
// Gracefully handle being given a font name rather than font family, for
|
||||
// example: "Helvetica Light Oblique" rather than just "Helvetica".
|
||||
if ([UIFont fontNamesForFamilyName:familyName].count == 0) {
|
||||
|
@ -756,6 +751,11 @@ static BOOL RCTFontIsCondensed(UIFont *font)
|
|||
}
|
||||
}
|
||||
|
||||
// Get font weight
|
||||
if (weight) {
|
||||
fontWeight = [self RCTFontWeight:weight];
|
||||
}
|
||||
|
||||
// Get the closest font that matches the given weight for the fontFamily
|
||||
UIFont *bestMatch = [UIFont fontWithName:font.fontName size: fontSize];
|
||||
CGFloat closestWeight;
|
||||
|
@ -805,7 +805,9 @@ NSArray *RCTConvertArrayValue(SEL type, id json)
|
|||
for (NSInteger i = 0; i < idx; i++) {
|
||||
[(NSMutableArray *)values addObject:json[i]];
|
||||
}
|
||||
[(NSMutableArray *)values addObject:value];
|
||||
if (value) {
|
||||
[(NSMutableArray *)values addObject:value];
|
||||
}
|
||||
copy = YES;
|
||||
}
|
||||
}];
|
||||
|
@ -828,6 +830,52 @@ RCT_ARRAY_CONVERTER(UIColor)
|
|||
return colors;
|
||||
}
|
||||
|
||||
static id RCTConvertPropertyListValue(id json)
|
||||
{
|
||||
if (!json || json == (id)kCFNull) {
|
||||
return nil;
|
||||
} else if ([json isKindOfClass:[NSDictionary class]]) {
|
||||
__block BOOL copy = NO;
|
||||
NSMutableDictionary *values = [[NSMutableDictionary alloc] initWithCapacity:[json count]];
|
||||
[json enumerateKeysAndObjectsUsingBlock:^(NSString *key, id jsonValue, BOOL *stop) {
|
||||
id value = RCTConvertPropertyListValue(jsonValue);
|
||||
if (value) {
|
||||
values[key] = value;
|
||||
}
|
||||
copy |= value != jsonValue;
|
||||
}];
|
||||
return copy ? values : json;
|
||||
} else if ([json isKindOfClass:[NSArray class]]) {
|
||||
__block BOOL copy = NO;
|
||||
__block NSArray *values = json;
|
||||
[json enumerateObjectsUsingBlock:^(id jsonValue, NSUInteger idx, BOOL *stop) {
|
||||
id value = RCTConvertPropertyListValue(jsonValue);
|
||||
if (copy) {
|
||||
if (value) {
|
||||
[(NSMutableArray *)values addObject:value];
|
||||
}
|
||||
} else if (value != jsonValue) {
|
||||
// Converted value is different, so we'll need to copy the array
|
||||
values = [[NSMutableArray alloc] initWithCapacity:values.count];
|
||||
for (NSInteger i = 0; i < idx; i++) {
|
||||
[(NSMutableArray *)values addObject:json[i]];
|
||||
}
|
||||
[(NSMutableArray *)values addObject:value];
|
||||
copy = YES;
|
||||
}
|
||||
}];
|
||||
return values;
|
||||
} else {
|
||||
// All other JSON types are supported by property lists
|
||||
return json;
|
||||
}
|
||||
}
|
||||
|
||||
+ (NSPropertyList)NSPropertyList:(id)json
|
||||
{
|
||||
return RCTConvertPropertyListValue(json);
|
||||
}
|
||||
|
||||
RCT_ENUM_CONVERTER(css_overflow, (@{
|
||||
@"hidden": @NO,
|
||||
@"visible": @YES
|
||||
|
|
|
@ -16,10 +16,10 @@
|
|||
/**
|
||||
* Developer menu, useful for exposing extra functionality when debugging.
|
||||
*/
|
||||
@interface RCTDevMenu : NSObject <RCTBridgeModule, RCTInvalidating>
|
||||
@interface RCTDevMenu : NSObject
|
||||
|
||||
/**
|
||||
* Is the menu enabled. The menu is enabled by default in debug mode, but
|
||||
* Is the menu enabled. The menu is enabled by default if RCT_DEV=1, but
|
||||
* you may wish to disable it so that you can provide your own shake handler.
|
||||
*/
|
||||
@property (nonatomic, assign) BOOL shakeToShow;
|
||||
|
@ -36,15 +36,16 @@
|
|||
@property (nonatomic, assign) BOOL liveReloadEnabled;
|
||||
|
||||
/**
|
||||
* The time between checks for code changes. Defaults to 1 second.
|
||||
*/
|
||||
@property (nonatomic, assign) NSTimeInterval liveReloadPeriod;
|
||||
|
||||
/**
|
||||
* Manually show the menu. This will.
|
||||
* Manually show the dev menu (can be called from JS).
|
||||
*/
|
||||
- (void)show;
|
||||
|
||||
/**
|
||||
* Manually reload the application. Equivalent to calling [bridge reload]
|
||||
* directly, but can be called from JS.
|
||||
*/
|
||||
- (void)reload;
|
||||
|
||||
@end
|
||||
|
||||
/**
|
||||
|
|
|
@ -10,12 +10,16 @@
|
|||
#import "RCTDevMenu.h"
|
||||
|
||||
#import "RCTBridge.h"
|
||||
#import "RCTDefines.h"
|
||||
#import "RCTKeyCommands.h"
|
||||
#import "RCTLog.h"
|
||||
#import "RCTProfile.h"
|
||||
#import "RCTRootView.h"
|
||||
#import "RCTSourceCode.h"
|
||||
#import "RCTUtils.h"
|
||||
|
||||
#if RCT_DEV
|
||||
|
||||
@interface RCTBridge (Profiling)
|
||||
|
||||
- (void)startProfiling;
|
||||
|
@ -24,6 +28,7 @@
|
|||
@end
|
||||
|
||||
static NSString *const RCTShowDevMenuNotification = @"RCTShowDevMenuNotification";
|
||||
static NSString *const RCTDevMenuSettingsKey = @"RCTDevMenu";
|
||||
|
||||
@implementation UIWindow (RCTDevMenu)
|
||||
|
||||
|
@ -36,14 +41,20 @@ static NSString *const RCTShowDevMenuNotification = @"RCTShowDevMenuNotification
|
|||
|
||||
@end
|
||||
|
||||
@interface RCTDevMenu () <UIActionSheetDelegate>
|
||||
@interface RCTDevMenu () <RCTBridgeModule, UIActionSheetDelegate>
|
||||
|
||||
@property (nonatomic, strong) Class executorClass;
|
||||
|
||||
@end
|
||||
|
||||
@implementation RCTDevMenu
|
||||
{
|
||||
NSTimer *_updateTimer;
|
||||
UIActionSheet *_actionSheet;
|
||||
NSUserDefaults *_defaults;
|
||||
NSMutableDictionary *_settings;
|
||||
NSURLSessionDataTask *_updateTask;
|
||||
NSURL *_liveReloadURL;
|
||||
BOOL _jsLoaded;
|
||||
}
|
||||
|
||||
@synthesize bridge = _bridge;
|
||||
|
@ -55,28 +66,121 @@ RCT_EXPORT_MODULE()
|
|||
// We're swizzling here because it's poor form to override methods in a category,
|
||||
// however UIWindow doesn't actually implement motionEnded:withEvent:, so there's
|
||||
// no need to call the original implementation.
|
||||
#if RCT_DEV
|
||||
RCTSwapInstanceMethods([UIWindow class], @selector(motionEnded:withEvent:), @selector(RCT_motionEnded:withEvent:));
|
||||
#endif
|
||||
}
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
if ((self = [super init])) {
|
||||
|
||||
_shakeToShow = YES;
|
||||
_liveReloadPeriod = 1.0; // 1 second
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(showOnShake)
|
||||
name:RCTShowDevMenuNotification
|
||||
object:nil];
|
||||
_defaults = [NSUserDefaults standardUserDefaults];
|
||||
[self updateSettings];
|
||||
|
||||
NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
|
||||
|
||||
[notificationCenter addObserver:self
|
||||
selector:@selector(showOnShake)
|
||||
name:RCTShowDevMenuNotification
|
||||
object:nil];
|
||||
|
||||
[notificationCenter addObserver:self
|
||||
selector:@selector(updateSettings)
|
||||
name:NSUserDefaultsDidChangeNotification
|
||||
object:nil];
|
||||
|
||||
[notificationCenter addObserver:self
|
||||
selector:@selector(jsLoaded)
|
||||
name:RCTJavaScriptDidLoadNotification
|
||||
object:nil];
|
||||
|
||||
#if TARGET_IPHONE_SIMULATOR
|
||||
|
||||
__weak RCTDevMenu *weakSelf = self;
|
||||
RCTKeyCommands *commands = [RCTKeyCommands sharedInstance];
|
||||
|
||||
// toggle debug menu
|
||||
[commands registerKeyCommandWithInput:@"d"
|
||||
modifierFlags:UIKeyModifierCommand
|
||||
action:^(UIKeyCommand *command) {
|
||||
[weakSelf toggle];
|
||||
}];
|
||||
|
||||
// reload in normal mode
|
||||
[commands registerKeyCommandWithInput:@"n"
|
||||
modifierFlags:UIKeyModifierCommand
|
||||
action:^(UIKeyCommand *command) {
|
||||
weakSelf.executorClass = Nil;
|
||||
}];
|
||||
#endif
|
||||
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)updateSettings
|
||||
{
|
||||
_settings = [NSMutableDictionary dictionaryWithDictionary:[_defaults objectForKey:RCTDevMenuSettingsKey]];
|
||||
|
||||
__weak RCTDevMenu *weakSelf = self;
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
RCTDevMenu *strongSelf = weakSelf;
|
||||
if (!strongSelf) {
|
||||
return;
|
||||
}
|
||||
|
||||
strongSelf.shakeToShow = [strongSelf->_settings[@"shakeToShow"] ?: @YES boolValue];
|
||||
strongSelf.profilingEnabled = [strongSelf->_settings[@"profilingEnabled"] ?: @NO boolValue];
|
||||
strongSelf.liveReloadEnabled = [strongSelf->_settings[@"liveReloadEnabled"] ?: @NO boolValue];
|
||||
strongSelf.executorClass = NSClassFromString(strongSelf->_settings[@"executorClass"]);
|
||||
});
|
||||
}
|
||||
|
||||
- (void)jsLoaded
|
||||
{
|
||||
_jsLoaded = YES;
|
||||
|
||||
// Check if live reloading is available
|
||||
_liveReloadURL = nil;
|
||||
RCTSourceCode *sourceCodeModule = _bridge.modules[RCTBridgeModuleNameForClass([RCTSourceCode class])];
|
||||
if (!sourceCodeModule.scriptURL) {
|
||||
if (!sourceCodeModule) {
|
||||
RCTLogWarn(@"RCTSourceCode module not found");
|
||||
} else {
|
||||
RCTLogWarn(@"RCTSourceCode module scriptURL has not been set");
|
||||
}
|
||||
} else if (![sourceCodeModule.scriptURL isFileURL]) {
|
||||
// Live reloading is disabled when running from bundled JS file
|
||||
_liveReloadURL = [[NSURL alloc] initWithString:@"/onchange" relativeToURL:sourceCodeModule.scriptURL];
|
||||
}
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
// Hit these setters again after bridge has finished loading
|
||||
self.profilingEnabled = _profilingEnabled;
|
||||
self.liveReloadEnabled = _liveReloadEnabled;
|
||||
self.executorClass = _executorClass;
|
||||
});
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
[_updateTask cancel];
|
||||
[_actionSheet dismissWithClickedButtonIndex:_actionSheet.cancelButtonIndex animated:YES];
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
}
|
||||
|
||||
- (void)updateSetting:(NSString *)name value:(id)value
|
||||
{
|
||||
if (value) {
|
||||
_settings[name] = value;
|
||||
} else {
|
||||
[_settings removeObjectForKey:name];
|
||||
}
|
||||
[_defaults setObject:_settings forKey:RCTDevMenuSettingsKey];
|
||||
[_defaults synchronize];
|
||||
}
|
||||
|
||||
- (void)showOnShake
|
||||
{
|
||||
if (_shakeToShow) {
|
||||
|
@ -84,26 +188,54 @@ RCT_EXPORT_MODULE()
|
|||
}
|
||||
}
|
||||
|
||||
- (void)show
|
||||
- (void)toggle
|
||||
{
|
||||
if (_actionSheet) {
|
||||
[_actionSheet dismissWithClickedButtonIndex:_actionSheet.cancelButtonIndex animated:YES];
|
||||
_actionSheet = nil;
|
||||
} else {
|
||||
[self show];
|
||||
}
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(show)
|
||||
{
|
||||
if (_actionSheet || !_bridge) {
|
||||
return;
|
||||
}
|
||||
|
||||
NSString *debugTitleChrome = _bridge.executorClass && _bridge.executorClass == NSClassFromString(@"RCTWebSocketExecutor") ? @"Disable Chrome Debugging" : @"Enable Chrome Debugging";
|
||||
NSString *debugTitleSafari = _bridge.executorClass && _bridge.executorClass == NSClassFromString(@"RCTWebViewExecutor") ? @"Disable Safari Debugging" : @"Enable Safari Debugging";
|
||||
NSString *liveReloadTitle = _liveReloadEnabled ? @"Disable Live Reload" : @"Enable Live Reload";
|
||||
NSString *profilingTitle = RCTProfileIsProfiling() ? @"Stop Profiling" : @"Start Profiling";
|
||||
NSString *debugTitleChrome = _executorClass && _executorClass == NSClassFromString(@"RCTWebSocketExecutor") ? @"Disable Chrome Debugging" : @"Debug in Chrome";
|
||||
NSString *debugTitleSafari = _executorClass && _executorClass == NSClassFromString(@"RCTWebViewExecutor") ? @"Disable Safari Debugging" : @"Debug in Safari";
|
||||
|
||||
UIActionSheet *actionSheet =
|
||||
[[UIActionSheet alloc] initWithTitle:@"React Native: Development"
|
||||
delegate:self
|
||||
cancelButtonTitle:@"Cancel"
|
||||
cancelButtonTitle:nil
|
||||
destructiveButtonTitle:nil
|
||||
otherButtonTitles:@"Reload", debugTitleChrome, debugTitleSafari, liveReloadTitle, profilingTitle, nil];
|
||||
otherButtonTitles:@"Reload", debugTitleChrome, debugTitleSafari, nil];
|
||||
|
||||
if (_liveReloadURL) {
|
||||
|
||||
NSString *liveReloadTitle = _liveReloadEnabled ? @"Disable Live Reload" : @"Enable Live Reload";
|
||||
NSString *profilingTitle = RCTProfileIsProfiling() ? @"Stop Profiling" : @"Start Profiling";
|
||||
|
||||
[actionSheet addButtonWithTitle:liveReloadTitle];
|
||||
[actionSheet addButtonWithTitle:profilingTitle];
|
||||
}
|
||||
|
||||
[actionSheet addButtonWithTitle:@"Cancel"];
|
||||
actionSheet.cancelButtonIndex = [actionSheet numberOfButtons] - 1;
|
||||
|
||||
actionSheet.actionSheetStyle = UIBarStyleBlack;
|
||||
[actionSheet showInView:[UIApplication sharedApplication].keyWindow.rootViewController.view];
|
||||
_actionSheet = actionSheet;
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(reload)
|
||||
{
|
||||
_jsLoaded = NO;
|
||||
_liveReloadURL = nil;
|
||||
[_bridge reload];
|
||||
}
|
||||
|
||||
- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex
|
||||
|
@ -112,19 +244,17 @@ RCT_EXPORT_MODULE()
|
|||
|
||||
switch (buttonIndex) {
|
||||
case 0: {
|
||||
[_bridge reload];
|
||||
[self reload];
|
||||
break;
|
||||
}
|
||||
case 1: {
|
||||
Class cls = NSClassFromString(@"RCTWebSocketExecutor");
|
||||
_bridge.executorClass = (_bridge.executorClass != cls) ? cls : nil;
|
||||
[_bridge reload];
|
||||
self.executorClass = (_executorClass == cls) ? Nil : cls;
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
Class cls = NSClassFromString(@"RCTWebViewExecutor");
|
||||
_bridge.executorClass = (_bridge.executorClass != cls) ? cls : Nil;
|
||||
[_bridge reload];
|
||||
self.executorClass = (_executorClass == cls) ? Nil : cls;
|
||||
break;
|
||||
}
|
||||
case 3: {
|
||||
|
@ -140,84 +270,115 @@ RCT_EXPORT_MODULE()
|
|||
}
|
||||
}
|
||||
|
||||
- (void)setShakeToShow:(BOOL)shakeToShow
|
||||
{
|
||||
if (_shakeToShow != shakeToShow) {
|
||||
_shakeToShow = shakeToShow;
|
||||
[self updateSetting:@"shakeToShow" value: @(_shakeToShow)];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setProfilingEnabled:(BOOL)enabled
|
||||
{
|
||||
if (_profilingEnabled == enabled) {
|
||||
return;
|
||||
if (_profilingEnabled != enabled) {
|
||||
_profilingEnabled = enabled;
|
||||
[self updateSetting:@"profilingEnabled" value: @(_profilingEnabled)];
|
||||
}
|
||||
|
||||
_profilingEnabled = enabled;
|
||||
if (RCTProfileIsProfiling()) {
|
||||
[_bridge stopProfiling];
|
||||
} else {
|
||||
[_bridge startProfiling];
|
||||
if (_liveReloadURL && enabled != RCTProfileIsProfiling()) {
|
||||
if (enabled) {
|
||||
[_bridge startProfiling];
|
||||
} else {
|
||||
[_bridge stopProfiling];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setLiveReloadEnabled:(BOOL)enabled
|
||||
{
|
||||
if (_liveReloadEnabled == enabled) {
|
||||
if (_liveReloadEnabled != enabled) {
|
||||
_liveReloadEnabled = enabled;
|
||||
[self updateSetting:@"liveReloadEnabled" value: @(_liveReloadEnabled)];
|
||||
}
|
||||
|
||||
if (_liveReloadEnabled) {
|
||||
[self checkForUpdates];
|
||||
} else {
|
||||
[_updateTask cancel];
|
||||
_updateTask = nil;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setExecutorClass:(Class)executorClass
|
||||
{
|
||||
if (_executorClass != executorClass) {
|
||||
_executorClass = executorClass;
|
||||
[self updateSetting:@"executorClass" value: NSStringFromClass(executorClass)];
|
||||
}
|
||||
|
||||
if (_bridge.executorClass != executorClass) {
|
||||
|
||||
// TODO (6929129): we can remove this special case test once we have better
|
||||
// support for custom executors in the dev menu. But right now this is
|
||||
// needed to prevent overriding a custom executor with the default if a
|
||||
// custom executor has been set directly on the bridge
|
||||
if (executorClass == Nil &&
|
||||
(_bridge.executorClass != NSClassFromString(@"RCTWebSocketExecutor") &&
|
||||
_bridge.executorClass != NSClassFromString(@"RCTWebViewExecutor"))) {
|
||||
return;
|
||||
}
|
||||
|
||||
_bridge.executorClass = executorClass;
|
||||
[self reload];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)checkForUpdates
|
||||
{
|
||||
if (!_jsLoaded || !_liveReloadEnabled || !_liveReloadURL) {
|
||||
return;
|
||||
}
|
||||
|
||||
_liveReloadEnabled = enabled;
|
||||
if (_liveReloadEnabled) {
|
||||
|
||||
_updateTimer = [NSTimer scheduledTimerWithTimeInterval:_liveReloadPeriod
|
||||
target:self
|
||||
selector:@selector(pollForUpdates)
|
||||
userInfo:nil
|
||||
repeats:YES];
|
||||
} else {
|
||||
|
||||
[_updateTimer invalidate];
|
||||
_updateTimer = nil;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setLiveReloadPeriod:(NSTimeInterval)liveReloadPeriod
|
||||
{
|
||||
_liveReloadPeriod = liveReloadPeriod;
|
||||
if (_liveReloadEnabled) {
|
||||
self.liveReloadEnabled = NO;
|
||||
self.liveReloadEnabled = YES;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)pollForUpdates
|
||||
{
|
||||
RCTSourceCode *sourceCodeModule = _bridge.modules[RCTBridgeModuleNameForClass([RCTSourceCode class])];
|
||||
if (!sourceCodeModule) {
|
||||
RCTLogError(@"RCTSourceCode module not found");
|
||||
self.liveReloadEnabled = NO;
|
||||
if (_updateTask) {
|
||||
[_updateTask cancel];
|
||||
_updateTask = nil;
|
||||
return;
|
||||
}
|
||||
|
||||
NSURL *longPollURL = [[NSURL alloc] initWithString:@"/onchange" relativeToURL:sourceCodeModule.scriptURL];
|
||||
[NSURLConnection sendAsynchronousRequest:[NSURLRequest requestWithURL:longPollURL]
|
||||
queue:[[NSOperationQueue alloc] init]
|
||||
completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
|
||||
__weak RCTDevMenu *weakSelf = self;
|
||||
_updateTask = [[NSURLSession sharedSession] dataTaskWithURL:_liveReloadURL completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
|
||||
|
||||
NSHTTPURLResponse *HTTPResponse = (NSHTTPURLResponse *)response;
|
||||
if (_liveReloadEnabled && HTTPResponse.statusCode == 205) {
|
||||
[_bridge reload];
|
||||
}
|
||||
}];
|
||||
}
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
__strong RCTDevMenu *strongSelf = weakSelf;
|
||||
if (strongSelf && strongSelf->_liveReloadEnabled) {
|
||||
NSHTTPURLResponse *HTTPResponse = (NSHTTPURLResponse *)response;
|
||||
if (!error && HTTPResponse.statusCode == 205) {
|
||||
[strongSelf reload];
|
||||
} else {
|
||||
strongSelf->_updateTask = nil;
|
||||
[strongSelf checkForUpdates];
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
- (BOOL)isValid
|
||||
{
|
||||
return !_liveReloadEnabled || _updateTimer != nil;
|
||||
}
|
||||
}];
|
||||
|
||||
- (void)invalidate
|
||||
{
|
||||
[_actionSheet dismissWithClickedButtonIndex:_actionSheet.cancelButtonIndex animated:YES];
|
||||
[_updateTimer invalidate];
|
||||
_updateTimer = nil;
|
||||
[_updateTask resume];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#else // Unavailable when not in dev mode
|
||||
|
||||
@implementation RCTDevMenu
|
||||
|
||||
- (void)show {}
|
||||
- (void)reload {}
|
||||
|
||||
@end
|
||||
|
||||
#endif
|
||||
|
||||
@implementation RCTBridge (RCTDevMenu)
|
||||
|
||||
- (RCTDevMenu *)devMenu
|
||||
|
|
|
@ -22,6 +22,6 @@
|
|||
|
||||
- (instancetype)initWithBridge:(RCTBridge *)bridge NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
- (void)loadBundleAtURL:(NSURL *)moduleURL onComplete:(RCTJavaScriptCompleteBlock)onComplete;
|
||||
- (void)loadBundleAtURL:(NSURL *)moduleURL onComplete:(void (^)(NSError *, NSString *))onComplete;
|
||||
|
||||
@end
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
return self;
|
||||
}
|
||||
|
||||
- (void)loadBundleAtURL:(NSURL *)scriptURL onComplete:(void (^)(NSError *))onComplete
|
||||
- (void)loadBundleAtURL:(NSURL *)scriptURL onComplete:(void (^)(NSError *, NSString *))onComplete
|
||||
{
|
||||
// Sanitize the script URL
|
||||
scriptURL = [RCTConvert NSURL:scriptURL.absoluteString];
|
||||
|
@ -37,7 +37,7 @@
|
|||
NSError *error = [NSError errorWithDomain:@"JavaScriptLoader" code:1 userInfo:@{
|
||||
NSLocalizedDescriptionKey: scriptURL ? [NSString stringWithFormat:@"Script at '%@' could not be found.", scriptURL] : @"No script URL provided"
|
||||
}];
|
||||
onComplete(error);
|
||||
onComplete(error, nil);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -57,7 +57,7 @@
|
|||
code:error.code
|
||||
userInfo:userInfo];
|
||||
}
|
||||
onComplete(error);
|
||||
onComplete(error, nil);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -96,18 +96,10 @@
|
|||
code:[(NSHTTPURLResponse *)response statusCode]
|
||||
userInfo:userInfo];
|
||||
|
||||
onComplete(error);
|
||||
onComplete(error, nil);
|
||||
return;
|
||||
}
|
||||
RCTSourceCode *sourceCodeModule = _bridge.modules[RCTBridgeModuleNameForClass([RCTSourceCode class])];
|
||||
sourceCodeModule.scriptURL = scriptURL;
|
||||
sourceCodeModule.scriptText = rawText;
|
||||
|
||||
[_bridge enqueueApplicationScript:rawText url:scriptURL onComplete:^(NSError *scriptError) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
onComplete(scriptError);
|
||||
});
|
||||
}];
|
||||
onComplete(nil, rawText);
|
||||
}];
|
||||
|
||||
[task resume];
|
||||
|
|
|
@ -90,6 +90,17 @@ static RCTKeyCommands *RKKeyCommandsSharedInstance = nil;
|
|||
{
|
||||
RCTAssertMainThread();
|
||||
|
||||
if (input.length && flags) {
|
||||
|
||||
// Workaround around the first cmd not working: http://openradar.appspot.com/19613391
|
||||
// You can register just the cmd key and do nothing. This ensures that
|
||||
// command-key modified commands will work first time.
|
||||
|
||||
[self registerKeyCommandWithInput:@""
|
||||
modifierFlags:flags
|
||||
action:nil];
|
||||
}
|
||||
|
||||
UIKeyCommand *command = [UIKeyCommand keyCommandWithInput:input
|
||||
modifierFlags:flags
|
||||
action:@selector(RCT_handleKeyCommand:)];
|
||||
|
|
|
@ -7,10 +7,6 @@
|
|||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
#import "RCTDefines.h"
|
||||
|
||||
#if RCT_DEBUG // Red box is only available in debug mode
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface RCTRedBox : NSObject
|
||||
|
@ -27,5 +23,3 @@
|
|||
- (void)dismiss;
|
||||
|
||||
@end
|
||||
|
||||
#endif
|
||||
|
|
|
@ -7,15 +7,14 @@
|
|||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
#import "RCTDefines.h"
|
||||
|
||||
#if RCT_DEBUG // Red box is only available in debug mode
|
||||
|
||||
#import "RCTRedBox.h"
|
||||
|
||||
#import "RCTBridge.h"
|
||||
#import "RCTDefines.h"
|
||||
#import "RCTUtils.h"
|
||||
|
||||
#if RCT_DEBUG
|
||||
|
||||
@interface RCTRedBoxWindow : UIWindow <UITableViewDelegate, UITableViewDataSource>
|
||||
|
||||
@property (nonatomic, copy) NSString *lastErrorMessage;
|
||||
|
@ -92,7 +91,7 @@
|
|||
[request setValue:postLength forHTTPHeaderField:@"Content-Length"];
|
||||
[request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
|
||||
|
||||
[NSURLConnection sendAsynchronousRequest:request queue:[[NSOperationQueue alloc] init] completionHandler:nil];
|
||||
[[[NSURLSession sharedSession] dataTaskWithRequest:request] resume];
|
||||
}
|
||||
|
||||
- (void)showErrorMessage:(NSString *)message withStack:(NSArray *)stack showIfHidden:(BOOL)shouldShow
|
||||
|
@ -310,4 +309,19 @@
|
|||
|
||||
@end
|
||||
|
||||
#else // Disabled
|
||||
|
||||
@implementation RCTRedBox
|
||||
|
||||
+ (instancetype)sharedInstance { return nil; }
|
||||
- (void)showErrorMessage:(NSString *)message {}
|
||||
- (void)showErrorMessage:(NSString *)message withDetails:(NSString *)details {}
|
||||
- (void)showErrorMessage:(NSString *)message withStack:(NSArray *)stack {}
|
||||
- (void)updateErrorMessage:(NSString *)message withStack:(NSArray *)stack {}
|
||||
- (void)showErrorMessage:(NSString *)message withStack:(NSArray *)stack showIfHidden:(BOOL)shouldShow {}
|
||||
- (NSString *)currentErrorMessage { return nil; }
|
||||
- (void)dismiss {}
|
||||
|
||||
@end
|
||||
|
||||
#endif
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
|
||||
#import "RCTBridge.h"
|
||||
|
||||
@interface RCTRootView : UIView <RCTInvalidating>
|
||||
@interface RCTRootView : UIView
|
||||
|
||||
/**
|
||||
* - Designated initializer -
|
||||
|
|
|
@ -20,25 +20,37 @@
|
|||
#import "RCTTouchHandler.h"
|
||||
#import "RCTUIManager.h"
|
||||
#import "RCTUtils.h"
|
||||
#import "RCTView.h"
|
||||
#import "RCTWebViewExecutor.h"
|
||||
#import "UIView+React.h"
|
||||
|
||||
@interface RCTBridge (RCTRootView)
|
||||
|
||||
@property (nonatomic, weak, readonly) RCTBridge *batchedBridge;
|
||||
|
||||
@end
|
||||
|
||||
@interface RCTUIManager (RCTRootView)
|
||||
|
||||
- (NSNumber *)allocateRootTag;
|
||||
|
||||
@end
|
||||
|
||||
@interface RCTRootContentView : RCTView <RCTInvalidating>
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame bridge:(RCTBridge *)bridge;
|
||||
|
||||
@end
|
||||
|
||||
@implementation RCTRootView
|
||||
{
|
||||
RCTBridge *_bridge;
|
||||
RCTTouchHandler *_touchHandler;
|
||||
NSString *_moduleName;
|
||||
NSDictionary *_launchOptions;
|
||||
UIView *_contentView;
|
||||
RCTRootContentView *_contentView;
|
||||
}
|
||||
|
||||
- (instancetype)initWithBridge:(RCTBridge *)bridge
|
||||
- (instancetype)initWithBridge:(RCTBridge *)bridge
|
||||
moduleName:(NSString *)moduleName
|
||||
{
|
||||
RCTAssert(bridge, @"A bridge instance is required to create an RCTRootView");
|
||||
|
@ -52,11 +64,11 @@
|
|||
_moduleName = moduleName;
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(bundleFinishedLoading)
|
||||
selector:@selector(javaScriptDidLoad:)
|
||||
name:RCTJavaScriptDidLoadNotification
|
||||
object:_bridge];
|
||||
if (!_bridge.loading) {
|
||||
[self bundleFinishedLoading];
|
||||
if (!_bridge.batchedBridge.isLoading) {
|
||||
[self bundleFinishedLoading:_bridge.batchedBridge];
|
||||
}
|
||||
}
|
||||
return self;
|
||||
|
@ -73,25 +85,6 @@
|
|||
return [self initWithBridge:bridge moduleName:moduleName];
|
||||
}
|
||||
|
||||
- (BOOL)isValid
|
||||
{
|
||||
return _contentView.userInteractionEnabled;
|
||||
}
|
||||
|
||||
- (void)invalidate
|
||||
{
|
||||
_contentView.userInteractionEnabled = NO;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
if (_contentView) {
|
||||
[_bridge enqueueJSCall:@"ReactIOS.unmountComponentAtNodeAndRemoveContainer"
|
||||
args:@[_contentView.reactTag]];
|
||||
}
|
||||
}
|
||||
|
||||
- (UIViewController *)backingViewController
|
||||
{
|
||||
return _backingViewController ?: [super backingViewController];
|
||||
|
@ -105,9 +98,19 @@
|
|||
RCT_IMPORT_METHOD(AppRegistry, runApplication)
|
||||
RCT_IMPORT_METHOD(ReactIOS, unmountComponentAtNodeAndRemoveContainer)
|
||||
|
||||
- (void)bundleFinishedLoading
|
||||
|
||||
- (void)javaScriptDidLoad:(NSNotification *)notification
|
||||
{
|
||||
RCTBridge *bridge = notification.userInfo[@"bridge"];
|
||||
[self bundleFinishedLoading:bridge];
|
||||
}
|
||||
|
||||
- (void)bundleFinishedLoading:(RCTBridge *)bridge
|
||||
{
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
if (!bridge.isValid) {
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Every root view that is created must have a unique React tag.
|
||||
|
@ -117,19 +120,16 @@ RCT_IMPORT_METHOD(ReactIOS, unmountComponentAtNodeAndRemoveContainer)
|
|||
* the React tag is assigned every time we load new content.
|
||||
*/
|
||||
[_contentView removeFromSuperview];
|
||||
_contentView = [[UIView alloc] initWithFrame:self.bounds];
|
||||
_contentView.reactTag = [_bridge.uiManager allocateRootTag];
|
||||
_touchHandler = [[RCTTouchHandler alloc] initWithBridge:_bridge];
|
||||
[_contentView addGestureRecognizer:_touchHandler];
|
||||
_contentView = [[RCTRootContentView alloc] initWithFrame:self.bounds
|
||||
bridge:bridge];
|
||||
[self addSubview:_contentView];
|
||||
|
||||
NSString *moduleName = _moduleName ?: @"";
|
||||
NSDictionary *appParameters = @{
|
||||
@"rootTag": _contentView.reactTag,
|
||||
@"initialProps": self.initialProperties ?: @{},
|
||||
@"initialProps": _initialProperties ?: @{},
|
||||
};
|
||||
[_bridge.uiManager registerRootView:_contentView];
|
||||
[_bridge enqueueJSCall:@"AppRegistry.runApplication"
|
||||
[bridge enqueueJSCall:@"AppRegistry.runApplication"
|
||||
args:@[moduleName, appParameters]];
|
||||
});
|
||||
}
|
||||
|
@ -139,7 +139,6 @@ RCT_IMPORT_METHOD(ReactIOS, unmountComponentAtNodeAndRemoveContainer)
|
|||
[super layoutSubviews];
|
||||
if (_contentView) {
|
||||
_contentView.frame = self.bounds;
|
||||
[_bridge.uiManager setFrame:self.bounds forRootView:_contentView];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -148,6 +147,12 @@ RCT_IMPORT_METHOD(ReactIOS, unmountComponentAtNodeAndRemoveContainer)
|
|||
return _contentView.reactTag;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
[_contentView removeFromSuperview];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation RCTUIManager (RCTRootView)
|
||||
|
@ -160,3 +165,60 @@ RCT_IMPORT_METHOD(ReactIOS, unmountComponentAtNodeAndRemoveContainer)
|
|||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation RCTRootContentView
|
||||
{
|
||||
__weak RCTBridge *_bridge;
|
||||
RCTTouchHandler *_touchHandler;
|
||||
}
|
||||
|
||||
- (instancetype)initWithFrame:(CGRect)frame
|
||||
bridge:(RCTBridge *)bridge
|
||||
{
|
||||
if ((self = [super init])) {
|
||||
_bridge = bridge;
|
||||
[self setUp];
|
||||
self.frame = frame;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)setFrame:(CGRect)frame
|
||||
{
|
||||
[super setFrame:frame];
|
||||
if (self.reactTag && _bridge.isValid) {
|
||||
[_bridge.uiManager setFrame:self.bounds forRootView:self];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setUp
|
||||
{
|
||||
/**
|
||||
* Every root view that is created must have a unique react tag.
|
||||
* Numbering of these tags goes from 1, 11, 21, 31, etc
|
||||
*
|
||||
* NOTE: Since the bridge persists, the RootViews might be reused, so now
|
||||
* the react tag is assigned every time we load new content.
|
||||
*/
|
||||
self.reactTag = [_bridge.uiManager allocateRootTag];
|
||||
[self addGestureRecognizer:[[RCTTouchHandler alloc] initWithBridge:_bridge]];
|
||||
[_bridge.uiManager registerRootView:self];
|
||||
}
|
||||
|
||||
- (BOOL)isValid
|
||||
{
|
||||
return self.userInteractionEnabled;
|
||||
}
|
||||
|
||||
- (void)invalidate
|
||||
{
|
||||
self.userInteractionEnabled = NO;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
[_bridge enqueueJSCall:@"ReactIOS.unmountComponentAtNodeAndRemoveContainer"
|
||||
args:@[self.reactTag]];
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
@ -55,8 +55,9 @@
|
|||
_pendingTouches = [[NSMutableArray alloc] init];
|
||||
_bridgeInteractionTiming = [[NSMutableArray alloc] init];
|
||||
|
||||
// `cancelsTouchesInView` is needed in order to be used as a top level event delegated recognizer. Otherwise, lower
|
||||
// level components not build using RCT, will fail to recognize gestures.
|
||||
// `cancelsTouchesInView` is needed in order to be used as a top level
|
||||
// event delegated recognizer. Otherwise, lower-level components not built
|
||||
// using RCT, will fail to recognize gestures.
|
||||
self.cancelsTouchesInView = NO;
|
||||
}
|
||||
return self;
|
||||
|
@ -165,7 +166,9 @@ RCT_IMPORT_METHOD(RCTEventEmitter, receiveTouches);
|
|||
* (start/end/move/cancel) and the indices that represent "changed" `Touch`es
|
||||
* from that array.
|
||||
*/
|
||||
- (void)_updateAndDispatchTouches:(NSSet *)touches eventName:(NSString *)eventName originatingTime:(CFTimeInterval)originatingTime
|
||||
- (void)_updateAndDispatchTouches:(NSSet *)touches
|
||||
eventName:(NSString *)eventName
|
||||
originatingTime:(CFTimeInterval)originatingTime
|
||||
{
|
||||
// Update touches
|
||||
NSMutableArray *changedIndexes = [[NSMutableArray alloc] init];
|
||||
|
@ -196,15 +199,39 @@ RCT_IMPORT_METHOD(RCTEventEmitter, receiveTouches);
|
|||
|
||||
#pragma mark - Gesture Recognizer Delegate Callbacks
|
||||
|
||||
static BOOL RCTAllTouchesAreCancelldOrEnded(NSSet *touches)
|
||||
{
|
||||
for (UITouch *touch in touches) {
|
||||
if (touch.phase == UITouchPhaseBegan ||
|
||||
touch.phase == UITouchPhaseMoved ||
|
||||
touch.phase == UITouchPhaseStationary) {
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
static BOOL RCTAnyTouchesChanged(NSSet *touches)
|
||||
{
|
||||
for (UITouch *touch in touches) {
|
||||
if (touch.phase == UITouchPhaseBegan ||
|
||||
touch.phase == UITouchPhaseMoved) {
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
|
||||
{
|
||||
[super touchesBegan:touches withEvent:event];
|
||||
self.state = UIGestureRecognizerStateBegan;
|
||||
|
||||
// "start" has to record new touches before extracting the event.
|
||||
// "end"/"cancel" needs to remove the touch *after* extracting the event.
|
||||
[self _recordNewTouches:touches];
|
||||
[self _updateAndDispatchTouches:touches eventName:@"topTouchStart" originatingTime:event.timestamp];
|
||||
|
||||
self.state = UIGestureRecognizerStateBegan;
|
||||
}
|
||||
|
||||
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
|
||||
|
@ -213,7 +240,12 @@ RCT_IMPORT_METHOD(RCTEventEmitter, receiveTouches);
|
|||
if (self.state == UIGestureRecognizerStateFailed) {
|
||||
return;
|
||||
}
|
||||
|
||||
[self _updateAndDispatchTouches:touches eventName:@"topTouchMove" originatingTime:event.timestamp];
|
||||
|
||||
if (self.state == UIGestureRecognizerStateBegan) {
|
||||
self.state = UIGestureRecognizerStateChanged;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
|
||||
|
@ -221,6 +253,12 @@ RCT_IMPORT_METHOD(RCTEventEmitter, receiveTouches);
|
|||
[super touchesEnded:touches withEvent:event];
|
||||
[self _updateAndDispatchTouches:touches eventName:@"topTouchEnd" originatingTime:event.timestamp];
|
||||
[self _recordRemovedTouches:touches];
|
||||
|
||||
if (RCTAllTouchesAreCancelldOrEnded(event.allTouches)) {
|
||||
self.state = UIGestureRecognizerStateEnded;
|
||||
} else if (RCTAnyTouchesChanged(event.allTouches)) {
|
||||
self.state = UIGestureRecognizerStateChanged;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event
|
||||
|
@ -228,6 +266,12 @@ RCT_IMPORT_METHOD(RCTEventEmitter, receiveTouches);
|
|||
[super touchesCancelled:touches withEvent:event];
|
||||
[self _updateAndDispatchTouches:touches eventName:@"topTouchCancel" originatingTime:event.timestamp];
|
||||
[self _recordRemovedTouches:touches];
|
||||
|
||||
if (RCTAllTouchesAreCancelldOrEnded(event.allTouches)) {
|
||||
self.state = UIGestureRecognizerStateCancelled;
|
||||
} else if (RCTAnyTouchesChanged(event.allTouches)) {
|
||||
self.state = UIGestureRecognizerStateChanged;
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)canPreventGestureRecognizer:(UIGestureRecognizer *)preventedGestureRecognizer
|
||||
|
|
|
@ -42,13 +42,17 @@ static void RCTReportError(RCTJavaScriptCallback callback, NSString *fmt, ...)
|
|||
{
|
||||
UIWebView *_webView;
|
||||
NSMutableDictionary *_objectsToInject;
|
||||
NSRegularExpression *_commentsRegex;
|
||||
}
|
||||
|
||||
@synthesize valid = _valid;
|
||||
|
||||
- (instancetype)initWithWebView:(UIWebView *)webView
|
||||
{
|
||||
if ((self = [super init])) {
|
||||
_objectsToInject = [[NSMutableDictionary alloc] init];
|
||||
_webView = webView ?: [[UIWebView alloc] init];
|
||||
_commentsRegex = [NSRegularExpression regularExpressionWithPattern:@"(^ *?\\/\\/.*?$|\\/\\*\\*[\\s\\S]+?\\*\\/)" options:NSRegularExpressionAnchorsMatchLines error:NULL];
|
||||
_webView.delegate = self;
|
||||
}
|
||||
return self;
|
||||
|
@ -59,13 +63,9 @@ static void RCTReportError(RCTJavaScriptCallback callback, NSString *fmt, ...)
|
|||
return [self initWithWebView:nil];
|
||||
}
|
||||
|
||||
- (BOOL)isValid
|
||||
{
|
||||
return _webView != nil;
|
||||
}
|
||||
|
||||
- (void)invalidate
|
||||
{
|
||||
_valid = NO;
|
||||
_webView.delegate = nil;
|
||||
_webView = nil;
|
||||
}
|
||||
|
@ -129,10 +129,21 @@ static void RCTReportError(RCTJavaScriptCallback callback, NSString *fmt, ...)
|
|||
}
|
||||
|
||||
RCTAssert(onComplete != nil, @"");
|
||||
_onApplicationScriptLoaded = onComplete;
|
||||
__weak RCTWebViewExecutor *weakSelf = self;
|
||||
_onApplicationScriptLoaded = ^(NSError *error){
|
||||
RCTWebViewExecutor *strongSelf = weakSelf;
|
||||
if (!strongSelf) {
|
||||
return;
|
||||
}
|
||||
strongSelf->_valid = error == nil;
|
||||
onComplete(error);
|
||||
};
|
||||
|
||||
script = [_commentsRegex stringByReplacingMatchesInString:script
|
||||
options:0
|
||||
range:NSMakeRange(0, script.length)
|
||||
withTemplate:@""];
|
||||
|
||||
script = [script stringByReplacingOccurrencesOfString:@"<script>" withString:@""];
|
||||
script = [script stringByReplacingOccurrencesOfString:@"</script>" withString:@""];
|
||||
if (_objectsToInject.count > 0) {
|
||||
NSMutableString *scriptWithInjections = [[NSMutableString alloc] initWithString:@"/* BEGIN NATIVELY INJECTED OBJECTS */\n"];
|
||||
[_objectsToInject enumerateKeysAndObjectsUsingBlock:^(NSString *objectName, NSString *blockScript, BOOL *stop) {
|
||||
|
|
|
@ -1,21 +1,15 @@
|
|||
/**
|
||||
* @generated SignedSource<<24fa633b4dd81b7fb40c2b2b0b7c97d0>>
|
||||
*
|
||||
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
* !! This file is a check-in from github! !!
|
||||
* !! !!
|
||||
* !! You should not modify this file directly. Instead: !!
|
||||
* !! 1) Go to https://github.com/facebook/css-layout !!
|
||||
* !! 2) Make a pull request and get it merged !!
|
||||
* !! 3) Execute ./import.sh to pull in the latest version !!
|
||||
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
*
|
||||
* Copyright (c) 2014, 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.
|
||||
*
|
||||
* WARNING: You should not modify this file directly. Instead:
|
||||
* 1) Go to https://github.com/facebook/css-layout
|
||||
* 2) Make a pull request and get it merged
|
||||
* 3) Run import.sh to copy Layout.* to react-native-github
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
|
@ -44,6 +38,12 @@ void init_css_node(css_node_t *node) {
|
|||
node->style.dimensions[CSS_WIDTH] = CSS_UNDEFINED;
|
||||
node->style.dimensions[CSS_HEIGHT] = CSS_UNDEFINED;
|
||||
|
||||
node->style.minDimensions[CSS_WIDTH] = CSS_UNDEFINED;
|
||||
node->style.minDimensions[CSS_HEIGHT] = CSS_UNDEFINED;
|
||||
|
||||
node->style.maxDimensions[CSS_WIDTH] = CSS_UNDEFINED;
|
||||
node->style.maxDimensions[CSS_HEIGHT] = CSS_UNDEFINED;
|
||||
|
||||
node->style.position[CSS_LEFT] = CSS_UNDEFINED;
|
||||
node->style.position[CSS_TOP] = CSS_UNDEFINED;
|
||||
node->style.position[CSS_RIGHT] = CSS_UNDEFINED;
|
||||
|
@ -249,6 +249,10 @@ static float getPaddingAndBorder(css_node_t *node, int location) {
|
|||
return getPadding(node, location) + getBorder(node, location);
|
||||
}
|
||||
|
||||
static float getBorderAxis(css_node_t *node, css_flex_direction_t axis) {
|
||||
return getBorder(node, leading[axis]) + getBorder(node, trailing[axis]);
|
||||
}
|
||||
|
||||
static float getMarginAxis(css_node_t *node, css_flex_direction_t axis) {
|
||||
return getMargin(node, leading[axis]) + getMargin(node, trailing[axis]);
|
||||
}
|
||||
|
@ -298,7 +302,8 @@ static float getDimWithMargin(css_node_t *node, css_flex_direction_t axis) {
|
|||
}
|
||||
|
||||
static bool isDimDefined(css_node_t *node, css_flex_direction_t axis) {
|
||||
return !isUndefined(node->style.dimensions[dim[axis]]);
|
||||
float value = node->style.dimensions[dim[axis]];
|
||||
return !isUndefined(value) && value > 0.0;
|
||||
}
|
||||
|
||||
static bool isPosDefined(css_node_t *node, css_position_t position) {
|
||||
|
@ -317,6 +322,30 @@ static float getPosition(css_node_t *node, css_position_t position) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
static float boundAxis(css_node_t *node, css_flex_direction_t axis, float value) {
|
||||
float min = CSS_UNDEFINED;
|
||||
float max = CSS_UNDEFINED;
|
||||
|
||||
if (axis == CSS_FLEX_DIRECTION_COLUMN) {
|
||||
min = node->style.minDimensions[CSS_HEIGHT];
|
||||
max = node->style.maxDimensions[CSS_HEIGHT];
|
||||
} else if (axis == CSS_FLEX_DIRECTION_ROW) {
|
||||
min = node->style.minDimensions[CSS_WIDTH];
|
||||
max = node->style.maxDimensions[CSS_WIDTH];
|
||||
}
|
||||
|
||||
float boundValue = value;
|
||||
|
||||
if (!isUndefined(max) && max >= 0.0 && boundValue > max) {
|
||||
boundValue = max;
|
||||
}
|
||||
if (!isUndefined(min) && min >= 0.0 && boundValue < min) {
|
||||
boundValue = min;
|
||||
}
|
||||
|
||||
return boundValue;
|
||||
}
|
||||
|
||||
// When the user specifically sets a value for width or height
|
||||
static void setDimensionFromStyle(css_node_t *node, css_flex_direction_t axis) {
|
||||
// The parent already computed us a width or height. We just skip it
|
||||
|
@ -330,7 +359,7 @@ static void setDimensionFromStyle(css_node_t *node, css_flex_direction_t axis) {
|
|||
|
||||
// The dimensions can never be smaller than the padding and border
|
||||
node->layout.dimensions[dim[axis]] = fmaxf(
|
||||
node->style.dimensions[dim[axis]],
|
||||
boundAxis(node, axis, node->style.dimensions[dim[axis]]),
|
||||
getPaddingAndBorderAxis(node, axis)
|
||||
);
|
||||
}
|
||||
|
@ -347,6 +376,7 @@ static float getRelativePosition(css_node_t *node, css_flex_direction_t axis) {
|
|||
|
||||
static void layoutNodeImpl(css_node_t *node, float parentMaxWidth) {
|
||||
/** START_GENERATED **/
|
||||
|
||||
css_flex_direction_t mainAxis = getFlexDirection(node);
|
||||
css_flex_direction_t crossAxis = mainAxis == CSS_FLEX_DIRECTION_ROW ?
|
||||
CSS_FLEX_DIRECTION_COLUMN :
|
||||
|
@ -385,25 +415,31 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth) {
|
|||
|
||||
// Let's not measure the text if we already know both dimensions
|
||||
if (isRowUndefined || isColumnUndefined) {
|
||||
css_dim_t measure_dim = node->measure(
|
||||
css_dim_t measureDim = node->measure(
|
||||
node->context,
|
||||
|
||||
width
|
||||
);
|
||||
if (isRowUndefined) {
|
||||
node->layout.dimensions[CSS_WIDTH] = measure_dim.dimensions[CSS_WIDTH] +
|
||||
node->layout.dimensions[CSS_WIDTH] = measureDim.dimensions[CSS_WIDTH] +
|
||||
getPaddingAndBorderAxis(node, CSS_FLEX_DIRECTION_ROW);
|
||||
}
|
||||
if (isColumnUndefined) {
|
||||
node->layout.dimensions[CSS_HEIGHT] = measure_dim.dimensions[CSS_HEIGHT] +
|
||||
node->layout.dimensions[CSS_HEIGHT] = measureDim.dimensions[CSS_HEIGHT] +
|
||||
getPaddingAndBorderAxis(node, CSS_FLEX_DIRECTION_COLUMN);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
int i;
|
||||
int ii;
|
||||
css_node_t* child;
|
||||
css_flex_direction_t axis;
|
||||
|
||||
// Pre-fill some dimensions straight from the parent
|
||||
for (int i = 0; i < node->children_count; ++i) {
|
||||
css_node_t* child = node->get_child(node->context, i);
|
||||
for (i = 0; i < node->children_count; ++i) {
|
||||
child = node->get_child(node->context, i);
|
||||
// Pre-fill cross axis dimensions when the child is using stretch before
|
||||
// we call the recursive layout pass
|
||||
if (getAlignItem(node, child) == CSS_ALIGN_STRETCH &&
|
||||
|
@ -411,27 +447,27 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth) {
|
|||
!isUndefined(node->layout.dimensions[dim[crossAxis]]) &&
|
||||
!isDimDefined(child, crossAxis)) {
|
||||
child->layout.dimensions[dim[crossAxis]] = fmaxf(
|
||||
node->layout.dimensions[dim[crossAxis]] -
|
||||
boundAxis(child, crossAxis, node->layout.dimensions[dim[crossAxis]] -
|
||||
getPaddingAndBorderAxis(node, crossAxis) -
|
||||
getMarginAxis(child, crossAxis),
|
||||
getMarginAxis(child, crossAxis)),
|
||||
// You never want to go smaller than padding
|
||||
getPaddingAndBorderAxis(child, crossAxis)
|
||||
);
|
||||
} else if (getPositionType(child) == CSS_POSITION_ABSOLUTE) {
|
||||
// Pre-fill dimensions when using absolute position and both offsets for the axis are defined (either both
|
||||
// left and right or top and bottom).
|
||||
for (int ii = 0; ii < 2; ii++) {
|
||||
css_flex_direction_t axis = (ii != 0) ? CSS_FLEX_DIRECTION_ROW : CSS_FLEX_DIRECTION_COLUMN;
|
||||
for (ii = 0; ii < 2; ii++) {
|
||||
axis = (ii != 0) ? CSS_FLEX_DIRECTION_ROW : CSS_FLEX_DIRECTION_COLUMN;
|
||||
if (!isUndefined(node->layout.dimensions[dim[axis]]) &&
|
||||
!isDimDefined(child, axis) &&
|
||||
isPosDefined(child, leading[axis]) &&
|
||||
isPosDefined(child, trailing[axis])) {
|
||||
child->layout.dimensions[dim[axis]] = fmaxf(
|
||||
node->layout.dimensions[dim[axis]] -
|
||||
getPaddingAndBorderAxis(node, axis) -
|
||||
getMarginAxis(child, axis) -
|
||||
getPosition(child, leading[axis]) -
|
||||
getPosition(child, trailing[axis]),
|
||||
boundAxis(child, axis, node->layout.dimensions[dim[axis]] -
|
||||
getPaddingAndBorderAxis(node, axis) -
|
||||
getMarginAxis(child, axis) -
|
||||
getPosition(child, leading[axis]) -
|
||||
getPosition(child, trailing[axis])),
|
||||
// You never want to go smaller than padding
|
||||
getPaddingAndBorderAxis(child, axis)
|
||||
);
|
||||
|
@ -449,11 +485,12 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth) {
|
|||
// We want to execute the next two loops one per line with flex-wrap
|
||||
int startLine = 0;
|
||||
int endLine = 0;
|
||||
int nextLine = 0;
|
||||
// int nextOffset = 0;
|
||||
int alreadyComputedNextLayout = 0;
|
||||
// We aggregate the total dimensions of the container in those two variables
|
||||
float linesCrossDim = 0;
|
||||
float linesMainDim = 0;
|
||||
while (endLine != node->children_count) {
|
||||
while (endLine < node->children_count) {
|
||||
// <Loop A> Layout non flexible children and count children by type
|
||||
|
||||
// mainContentDim is accumulation of the dimensions and margin of all the
|
||||
|
@ -467,8 +504,10 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth) {
|
|||
int flexibleChildrenCount = 0;
|
||||
float totalFlexible = 0;
|
||||
int nonFlexibleChildrenCount = 0;
|
||||
for (int i = startLine; i < node->children_count; ++i) {
|
||||
css_node_t* child = node->get_child(node->context, i);
|
||||
|
||||
float maxWidth;
|
||||
for (i = startLine; i < node->children_count; ++i) {
|
||||
child = node->get_child(node->context, i);
|
||||
float nextContentDim = 0;
|
||||
|
||||
// It only makes sense to consider a child flexible if we have a computed
|
||||
|
@ -478,26 +517,27 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth) {
|
|||
totalFlexible += getFlex(child);
|
||||
|
||||
// Even if we don't know its exact size yet, we already know the padding,
|
||||
// border and margin. We'll use this partial information to compute the
|
||||
// remaining space.
|
||||
// border and margin. We'll use this partial information, which represents
|
||||
// the smallest possible size for the child, to compute the remaining
|
||||
// available space.
|
||||
nextContentDim = getPaddingAndBorderAxis(child, mainAxis) +
|
||||
getMarginAxis(child, mainAxis);
|
||||
|
||||
} else {
|
||||
float maxWidth = CSS_UNDEFINED;
|
||||
if (mainAxis == CSS_FLEX_DIRECTION_ROW) {
|
||||
// do nothing
|
||||
} else if (isDimDefined(node, CSS_FLEX_DIRECTION_ROW)) {
|
||||
maxWidth = node->layout.dimensions[dim[CSS_FLEX_DIRECTION_ROW]] -
|
||||
getPaddingAndBorderAxis(node, CSS_FLEX_DIRECTION_ROW);
|
||||
} else {
|
||||
maxWidth = CSS_UNDEFINED;
|
||||
if (mainAxis != CSS_FLEX_DIRECTION_ROW) {
|
||||
maxWidth = parentMaxWidth -
|
||||
getMarginAxis(node, CSS_FLEX_DIRECTION_ROW) -
|
||||
getPaddingAndBorderAxis(node, CSS_FLEX_DIRECTION_ROW);
|
||||
|
||||
if (isDimDefined(node, CSS_FLEX_DIRECTION_ROW)) {
|
||||
maxWidth = node->layout.dimensions[dim[CSS_FLEX_DIRECTION_ROW]] -
|
||||
getPaddingAndBorderAxis(node, CSS_FLEX_DIRECTION_ROW);
|
||||
}
|
||||
}
|
||||
|
||||
// This is the main recursive call. We layout non flexible children.
|
||||
if (nextLine == 0) {
|
||||
if (alreadyComputedNextLayout == 0) {
|
||||
layoutNode(child, maxWidth);
|
||||
}
|
||||
|
||||
|
@ -513,11 +553,14 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth) {
|
|||
// The element we are about to add would make us go to the next line
|
||||
if (isFlexWrap(node) &&
|
||||
!isUndefined(node->layout.dimensions[dim[mainAxis]]) &&
|
||||
mainContentDim + nextContentDim > definedMainDim) {
|
||||
nextLine = i + 1;
|
||||
mainContentDim + nextContentDim > definedMainDim &&
|
||||
// If there's only one element, then it's bigger than the content
|
||||
// and needs its own line
|
||||
i != startLine) {
|
||||
alreadyComputedNextLayout = 1;
|
||||
break;
|
||||
}
|
||||
nextLine = 0;
|
||||
alreadyComputedNextLayout = 0;
|
||||
mainContentDim += nextContentDim;
|
||||
endLine = i + 1;
|
||||
}
|
||||
|
@ -542,6 +585,26 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth) {
|
|||
// remaining space
|
||||
if (flexibleChildrenCount != 0) {
|
||||
float flexibleMainDim = remainingMainDim / totalFlexible;
|
||||
float baseMainDim;
|
||||
float boundMainDim;
|
||||
|
||||
// Iterate over every child in the axis. If the flex share of remaining
|
||||
// space doesn't meet min/max bounds, remove this child from flex
|
||||
// calculations.
|
||||
for (i = startLine; i < endLine; ++i) {
|
||||
child = node->get_child(node->context, i);
|
||||
if (isFlex(child)) {
|
||||
baseMainDim = flexibleMainDim * getFlex(child) +
|
||||
getPaddingAndBorderAxis(child, mainAxis);
|
||||
boundMainDim = boundAxis(child, mainAxis, baseMainDim);
|
||||
|
||||
if (baseMainDim != boundMainDim) {
|
||||
remainingMainDim -= boundMainDim;
|
||||
totalFlexible -= getFlex(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
flexibleMainDim = remainingMainDim / totalFlexible;
|
||||
|
||||
// The non flexible children can overflow the container, in this case
|
||||
// we should just assume that there is no space available.
|
||||
|
@ -551,21 +614,20 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth) {
|
|||
// We iterate over the full array and only apply the action on flexible
|
||||
// children. This is faster than actually allocating a new array that
|
||||
// contains only flexible children.
|
||||
for (int i = startLine; i < endLine; ++i) {
|
||||
css_node_t* child = node->get_child(node->context, i);
|
||||
for (i = startLine; i < endLine; ++i) {
|
||||
child = node->get_child(node->context, i);
|
||||
if (isFlex(child)) {
|
||||
// At this point we know the final size of the element in the main
|
||||
// dimension
|
||||
child->layout.dimensions[dim[mainAxis]] = flexibleMainDim * getFlex(child) +
|
||||
getPaddingAndBorderAxis(child, mainAxis);
|
||||
child->layout.dimensions[dim[mainAxis]] = boundAxis(child, mainAxis,
|
||||
flexibleMainDim * getFlex(child) + getPaddingAndBorderAxis(child, mainAxis)
|
||||
);
|
||||
|
||||
float maxWidth = CSS_UNDEFINED;
|
||||
if (mainAxis == CSS_FLEX_DIRECTION_ROW) {
|
||||
// do nothing
|
||||
} else if (isDimDefined(node, CSS_FLEX_DIRECTION_ROW)) {
|
||||
maxWidth = CSS_UNDEFINED;
|
||||
if (isDimDefined(node, CSS_FLEX_DIRECTION_ROW)) {
|
||||
maxWidth = node->layout.dimensions[dim[CSS_FLEX_DIRECTION_ROW]] -
|
||||
getPaddingAndBorderAxis(node, CSS_FLEX_DIRECTION_ROW);
|
||||
} else {
|
||||
} else if (mainAxis != CSS_FLEX_DIRECTION_ROW) {
|
||||
maxWidth = parentMaxWidth -
|
||||
getMarginAxis(node, CSS_FLEX_DIRECTION_ROW) -
|
||||
getPaddingAndBorderAxis(node, CSS_FLEX_DIRECTION_ROW);
|
||||
|
@ -580,9 +642,7 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth) {
|
|||
// space available
|
||||
} else {
|
||||
css_justify_t justifyContent = getJustifyContent(node);
|
||||
if (justifyContent == CSS_JUSTIFY_FLEX_START) {
|
||||
// Do nothing
|
||||
} else if (justifyContent == CSS_JUSTIFY_CENTER) {
|
||||
if (justifyContent == CSS_JUSTIFY_CENTER) {
|
||||
leadingMainDim = remainingMainDim / 2;
|
||||
} else if (justifyContent == CSS_JUSTIFY_FLEX_END) {
|
||||
leadingMainDim = remainingMainDim;
|
||||
|
@ -612,8 +672,8 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth) {
|
|||
float mainDim = leadingMainDim +
|
||||
getPaddingAndBorder(node, leading[mainAxis]);
|
||||
|
||||
for (int i = startLine; i < endLine; ++i) {
|
||||
css_node_t* child = node->get_child(node->context, i);
|
||||
for (i = startLine; i < endLine; ++i) {
|
||||
child = node->get_child(node->context, i);
|
||||
|
||||
if (getPositionType(child) == CSS_POSITION_ABSOLUTE &&
|
||||
isPosDefined(child, leading[mainAxis])) {
|
||||
|
@ -638,7 +698,7 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth) {
|
|||
mainDim += betweenMainDim + getDimWithMargin(child, mainAxis);
|
||||
// The cross dimension is the max of the elements dimension since there
|
||||
// can only be one element in that cross dimension.
|
||||
crossDim = fmaxf(crossDim, getDimWithMargin(child, crossAxis));
|
||||
crossDim = fmaxf(crossDim, boundAxis(child, crossAxis, getDimWithMargin(child, crossAxis)));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -648,15 +708,15 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth) {
|
|||
// For the cross dim, we add both sides at the end because the value
|
||||
// is aggregate via a max function. Intermediate negative values
|
||||
// can mess this computation otherwise
|
||||
crossDim + getPaddingAndBorderAxis(node, crossAxis),
|
||||
boundAxis(node, crossAxis, crossDim + getPaddingAndBorderAxis(node, crossAxis)),
|
||||
getPaddingAndBorderAxis(node, crossAxis)
|
||||
);
|
||||
}
|
||||
|
||||
// <Loop D> Position elements in the cross axis
|
||||
|
||||
for (int i = startLine; i < endLine; ++i) {
|
||||
css_node_t* child = node->get_child(node->context, i);
|
||||
for (i = startLine; i < endLine; ++i) {
|
||||
child = node->get_child(node->context, i);
|
||||
|
||||
if (getPositionType(child) == CSS_POSITION_ABSOLUTE &&
|
||||
isPosDefined(child, leading[crossAxis])) {
|
||||
|
@ -674,21 +734,19 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth) {
|
|||
// alignSelf (child) in order to determine the position in the cross axis
|
||||
if (getPositionType(child) == CSS_POSITION_RELATIVE) {
|
||||
css_align_t alignItem = getAlignItem(node, child);
|
||||
if (alignItem == CSS_ALIGN_FLEX_START) {
|
||||
// Do nothing
|
||||
} else if (alignItem == CSS_ALIGN_STRETCH) {
|
||||
if (alignItem == CSS_ALIGN_STRETCH) {
|
||||
// You can only stretch if the dimension has not already been set
|
||||
// previously.
|
||||
if (!isDimDefined(child, crossAxis)) {
|
||||
child->layout.dimensions[dim[crossAxis]] = fmaxf(
|
||||
containerCrossAxis -
|
||||
boundAxis(child, crossAxis, containerCrossAxis -
|
||||
getPaddingAndBorderAxis(node, crossAxis) -
|
||||
getMarginAxis(child, crossAxis),
|
||||
getMarginAxis(child, crossAxis)),
|
||||
// You never want to go smaller than padding
|
||||
getPaddingAndBorderAxis(child, crossAxis)
|
||||
);
|
||||
}
|
||||
} else {
|
||||
} else if (alignItem != CSS_ALIGN_FLEX_START) {
|
||||
// The remaining space between the parent dimensions+padding and child
|
||||
// dimensions+margin.
|
||||
float remainingCrossDim = containerCrossAxis -
|
||||
|
@ -719,7 +777,7 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth) {
|
|||
node->layout.dimensions[dim[mainAxis]] = fmaxf(
|
||||
// We're missing the last padding at this point to get the final
|
||||
// dimension
|
||||
linesMainDim + getPaddingAndBorder(node, trailing[mainAxis]),
|
||||
boundAxis(node, mainAxis, linesMainDim + getPaddingAndBorder(node, trailing[mainAxis])),
|
||||
// We can never assign a width smaller than the padding and borders
|
||||
getPaddingAndBorderAxis(node, mainAxis)
|
||||
);
|
||||
|
@ -730,37 +788,38 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth) {
|
|||
// For the cross dim, we add both sides at the end because the value
|
||||
// is aggregate via a max function. Intermediate negative values
|
||||
// can mess this computation otherwise
|
||||
linesCrossDim + getPaddingAndBorderAxis(node, crossAxis),
|
||||
boundAxis(node, crossAxis, linesCrossDim + getPaddingAndBorderAxis(node, crossAxis)),
|
||||
getPaddingAndBorderAxis(node, crossAxis)
|
||||
);
|
||||
}
|
||||
|
||||
// <Loop E> Calculate dimensions for absolutely positioned elements
|
||||
|
||||
for (int i = 0; i < node->children_count; ++i) {
|
||||
css_node_t* child = node->get_child(node->context, i);
|
||||
for (i = 0; i < node->children_count; ++i) {
|
||||
child = node->get_child(node->context, i);
|
||||
if (getPositionType(child) == CSS_POSITION_ABSOLUTE) {
|
||||
// Pre-fill dimensions when using absolute position and both offsets for the axis are defined (either both
|
||||
// left and right or top and bottom).
|
||||
for (int ii = 0; ii < 2; ii++) {
|
||||
css_flex_direction_t axis = (ii != 0) ? CSS_FLEX_DIRECTION_ROW : CSS_FLEX_DIRECTION_COLUMN;
|
||||
for (ii = 0; ii < 2; ii++) {
|
||||
axis = (ii != 0) ? CSS_FLEX_DIRECTION_ROW : CSS_FLEX_DIRECTION_COLUMN;
|
||||
if (!isUndefined(node->layout.dimensions[dim[axis]]) &&
|
||||
!isDimDefined(child, axis) &&
|
||||
isPosDefined(child, leading[axis]) &&
|
||||
isPosDefined(child, trailing[axis])) {
|
||||
child->layout.dimensions[dim[axis]] = fmaxf(
|
||||
node->layout.dimensions[dim[axis]] -
|
||||
getPaddingAndBorderAxis(node, axis) -
|
||||
getMarginAxis(child, axis) -
|
||||
getPosition(child, leading[axis]) -
|
||||
getPosition(child, trailing[axis]),
|
||||
boundAxis(child, axis, node->layout.dimensions[dim[axis]] -
|
||||
getBorderAxis(node, axis) -
|
||||
getMarginAxis(child, axis) -
|
||||
getPosition(child, leading[axis]) -
|
||||
getPosition(child, trailing[axis])
|
||||
),
|
||||
// You never want to go smaller than padding
|
||||
getPaddingAndBorderAxis(child, axis)
|
||||
);
|
||||
}
|
||||
}
|
||||
for (int ii = 0; ii < 2; ii++) {
|
||||
css_flex_direction_t axis = (ii != 0) ? CSS_FLEX_DIRECTION_ROW : CSS_FLEX_DIRECTION_COLUMN;
|
||||
for (ii = 0; ii < 2; ii++) {
|
||||
axis = (ii != 0) ? CSS_FLEX_DIRECTION_ROW : CSS_FLEX_DIRECTION_COLUMN;
|
||||
if (isPosDefined(child, trailing[axis]) &&
|
||||
!isPosDefined(child, leading[axis])) {
|
||||
child->layout.position[leading[axis]] =
|
||||
|
|
|
@ -1,21 +1,15 @@
|
|||
/**
|
||||
* @generated SignedSource<<58298c7a8815a8675e970b0347dedfed>>
|
||||
*
|
||||
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
* !! This file is a check-in from github! !!
|
||||
* !! !!
|
||||
* !! You should not modify this file directly. Instead: !!
|
||||
* !! 1) Go to https://github.com/facebook/css-layout !!
|
||||
* !! 2) Make a pull request and get it merged !!
|
||||
* !! 3) Execute ./import.sh to pull in the latest version !!
|
||||
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
*
|
||||
* Copyright (c) 2014, 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.
|
||||
*
|
||||
* WARNING: You should not modify this file directly. Instead:
|
||||
* 1) Go to https://github.com/facebook/css-layout
|
||||
* 2) Make a pull request and get it merged
|
||||
* 3) Run import.sh to copy Layout.* to react-native-github
|
||||
*/
|
||||
|
||||
#ifndef __LAYOUT_H
|
||||
|
@ -113,6 +107,8 @@ typedef struct {
|
|||
float padding[4];
|
||||
float border[4];
|
||||
float dimensions[2];
|
||||
float minDimensions[2];
|
||||
float maxDimensions[2];
|
||||
} css_style_t;
|
||||
|
||||
typedef struct css_node {
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
LAYOUT_C=`curl https://raw.githubusercontent.com/facebook/css-layout/master/src/Layout.c`
|
||||
LAYOUT_H=`curl https://raw.githubusercontent.com/facebook/css-layout/master/src/Layout.h`
|
||||
|
||||
REPLACE_STRING="*
|
||||
* WARNING: You should not modify this file directly. Instead:
|
||||
* 1) Go to https://github.com/facebook/css-layout
|
||||
* 2) Make a pull request and get it merged
|
||||
* 3) Run import.sh to copy Layout.* to react-native-github
|
||||
*/"
|
||||
|
||||
LAYOUT_C=${LAYOUT_C/\*\//$REPLACE_STRING}
|
||||
LAYOUT_H=${LAYOUT_H/\*\//$REPLACE_STRING}
|
||||
|
||||
echo "$LAYOUT_C" > Layout.c
|
||||
echo "$LAYOUT_H" > Layout.h
|
|
@ -61,12 +61,8 @@ RCT_EXPORT_METHOD(reportUnhandledException:(NSString *)message
|
|||
|
||||
} else {
|
||||
|
||||
// Filter out numbers so the same base errors are mapped to the same categories independent of incorrect values.
|
||||
NSString *pattern = @"[+-]?\\d+[,.]?\\d*";
|
||||
NSString *sanitizedMessage = [message stringByReplacingOccurrencesOfString:pattern withString:@"<num>" options:NSRegularExpressionSearch range:(NSRange){0, message.length}];
|
||||
|
||||
if (sanitizedMessage.length > maxMessageLength) {
|
||||
sanitizedMessage = [[sanitizedMessage substringToIndex:maxMessageLength] stringByAppendingString:@"..."];
|
||||
if (message.length > maxMessageLength) {
|
||||
message = [[message substringToIndex:maxMessageLength] stringByAppendingString:@"..."];
|
||||
}
|
||||
|
||||
NSMutableString *prettyStack = [NSMutableString stringWithString:@"\n"];
|
||||
|
@ -74,7 +70,7 @@ RCT_EXPORT_METHOD(reportUnhandledException:(NSString *)message
|
|||
[prettyStack appendFormat:@"%@@%@:%@\n", frame[@"methodName"], frame[@"lineNumber"], frame[@"column"]];
|
||||
}
|
||||
|
||||
NSString *name = [@"Unhandled JS Exception: " stringByAppendingString:sanitizedMessage];
|
||||
NSString *name = [@"Unhandled JS Exception: " stringByAppendingString:message];
|
||||
[NSException raise:name format:@"Message: %@, stack: %@", message, prettyStack];
|
||||
}
|
||||
|
||||
|
|
|
@ -70,6 +70,7 @@
|
|||
}
|
||||
|
||||
@synthesize bridge = _bridge;
|
||||
@synthesize paused = _paused;
|
||||
|
||||
RCT_EXPORT_MODULE()
|
||||
|
||||
|
@ -78,7 +79,7 @@ RCT_IMPORT_METHOD(RCTJSTimers, callTimers)
|
|||
- (instancetype)init
|
||||
{
|
||||
if ((self = [super init])) {
|
||||
|
||||
_paused = YES;
|
||||
_timers = [[RCTSparseArray alloc] init];
|
||||
|
||||
for (NSString *name in @[UIApplicationWillResignActiveNotification,
|
||||
|
@ -126,7 +127,7 @@ RCT_IMPORT_METHOD(RCTJSTimers, callTimers)
|
|||
|
||||
- (void)stopTimers
|
||||
{
|
||||
[_bridge removeFrameUpdateObserver:self];
|
||||
_paused = YES;
|
||||
}
|
||||
|
||||
- (void)startTimers
|
||||
|
@ -135,7 +136,7 @@ RCT_IMPORT_METHOD(RCTJSTimers, callTimers)
|
|||
return;
|
||||
}
|
||||
|
||||
[_bridge addFrameUpdateObserver:self];
|
||||
_paused = NO;
|
||||
}
|
||||
|
||||
- (void)didUpdateFrame:(RCTFrameUpdate *)update
|
||||
|
|
|
@ -267,11 +267,6 @@ static NSDictionary *RCTViewConfigForModule(Class managerClass, NSString *viewNa
|
|||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
RCTAssert(!self.valid, @"must call -invalidate before -dealloc");
|
||||
}
|
||||
|
||||
- (BOOL)isValid
|
||||
{
|
||||
return _viewRegistry != nil;
|
||||
|
@ -279,20 +274,24 @@ static NSDictionary *RCTViewConfigForModule(Class managerClass, NSString *viewNa
|
|||
|
||||
- (void)invalidate
|
||||
{
|
||||
RCTAssertMainThread();
|
||||
/**
|
||||
* Called on the JS Thread since all modules are invalidated on the JS thread
|
||||
*/
|
||||
|
||||
for (NSNumber *rootViewTag in _rootViewTags) {
|
||||
((UIView *)_viewRegistry[rootViewTag]).userInteractionEnabled = NO;
|
||||
}
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
for (NSNumber *rootViewTag in _rootViewTags) {
|
||||
((UIView *)_viewRegistry[rootViewTag]).userInteractionEnabled = NO;
|
||||
}
|
||||
|
||||
_rootViewTags = nil;
|
||||
_shadowViewRegistry = nil;
|
||||
_viewRegistry = nil;
|
||||
_bridge = nil;
|
||||
_rootViewTags = nil;
|
||||
_shadowViewRegistry = nil;
|
||||
_viewRegistry = nil;
|
||||
_bridge = nil;
|
||||
|
||||
[_pendingUIBlocksLock lock];
|
||||
_pendingUIBlocks = nil;
|
||||
[_pendingUIBlocksLock unlock];
|
||||
[_pendingUIBlocksLock lock];
|
||||
_pendingUIBlocks = nil;
|
||||
[_pendingUIBlocksLock unlock];
|
||||
});
|
||||
}
|
||||
|
||||
- (void)setBridge:(RCTBridge *)bridge
|
||||
|
|
|
@ -583,7 +583,9 @@
|
|||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"DEBUG=1",
|
||||
"$(inherited)",
|
||||
"RCT_DEBUG=1",
|
||||
"RCT_DEV=1",
|
||||
"RCT_NSASSERT=1",
|
||||
);
|
||||
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
|
@ -621,6 +623,7 @@
|
|||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = "";
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
|
@ -638,10 +641,7 @@
|
|||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_STATIC_ANALYZER_MODE = deep;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"DEBUG=1",
|
||||
"$(inherited)",
|
||||
);
|
||||
GCC_PREPROCESSOR_DEFINITIONS = "$(inherited)";
|
||||
GCC_WARN_ABOUT_MISSING_NEWLINE = YES;
|
||||
HEADER_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
|
@ -658,6 +658,7 @@
|
|||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_STATIC_ANALYZER_MODE = deep;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = "$(inherited)";
|
||||
GCC_WARN_ABOUT_MISSING_NEWLINE = YES;
|
||||
HEADER_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
|
|
|
@ -264,9 +264,13 @@ NSInteger kNeverProgressed = -10000;
|
|||
NSInteger _numberOfViewControllerMovesToIgnore;
|
||||
}
|
||||
|
||||
@synthesize paused = _paused;
|
||||
|
||||
- (id)initWithBridge:(RCTBridge *)bridge
|
||||
{
|
||||
if ((self = [super initWithFrame:CGRectZero])) {
|
||||
_paused = YES;
|
||||
|
||||
_bridge = bridge;
|
||||
_mostRecentProgress = kNeverProgressed;
|
||||
_dummyView = [[UIView alloc] initWithFrame:CGRectZero];
|
||||
|
@ -341,14 +345,14 @@ NSInteger kNeverProgressed = -10000;
|
|||
_dummyView.frame = (CGRect){{destination}};
|
||||
_currentlyTransitioningFrom = indexOfFrom;
|
||||
_currentlyTransitioningTo = indexOfTo;
|
||||
[_bridge addFrameUpdateObserver:self];
|
||||
_paused = NO;
|
||||
}
|
||||
completion:^(id<UIViewControllerTransitionCoordinatorContext> context) {
|
||||
[weakSelf freeLock];
|
||||
_currentlyTransitioningFrom = 0;
|
||||
_currentlyTransitioningTo = 0;
|
||||
_dummyView.frame = CGRectZero;
|
||||
[_bridge removeFrameUpdateObserver:self];
|
||||
_paused = YES;
|
||||
// Reset the parallel position tracker
|
||||
}];
|
||||
}
|
||||
|
@ -457,6 +461,10 @@ NSInteger kNeverProgressed = -10000;
|
|||
// --- previously caught up -------- ------- still caught up ----------
|
||||
viewControllerCount == previousReactCount && currentReactCount == previousReactCount;
|
||||
|
||||
BOOL jsGettingtooSlow =
|
||||
// --- previously not caught up -------- ------- no longer caught up ----------
|
||||
viewControllerCount < previousReactCount && currentReactCount < previousReactCount;
|
||||
|
||||
BOOL reactPushOne = jsGettingAhead && currentReactCount == previousReactCount + 1;
|
||||
BOOL reactPopN = jsGettingAhead && currentReactCount < previousReactCount;
|
||||
|
||||
|
@ -467,7 +475,8 @@ NSInteger kNeverProgressed = -10000;
|
|||
if (!(jsGettingAhead ||
|
||||
jsCatchingUp ||
|
||||
jsMakingNoProgressButNeedsToCatchUp ||
|
||||
jsMakingNoProgressAndDoesntNeedTo)) {
|
||||
jsMakingNoProgressAndDoesntNeedTo ||
|
||||
jsGettingtooSlow)) {
|
||||
RCTLogError(@"JS has only made partial progress to catch up to UIKit");
|
||||
}
|
||||
if (currentReactCount > _currentViews.count) {
|
||||
|
|
|
@ -18,24 +18,6 @@
|
|||
#import "RCTWrapperViewController.h"
|
||||
#import "UIView+React.h"
|
||||
|
||||
@interface RKCustomTabBarController : UITabBarController <RCTViewControllerProtocol>
|
||||
|
||||
@end
|
||||
|
||||
@implementation RKCustomTabBarController
|
||||
|
||||
@synthesize currentTopLayoutGuide = _currentTopLayoutGuide;
|
||||
@synthesize currentBottomLayoutGuide = _currentBottomLayoutGuide;
|
||||
|
||||
- (void)viewWillLayoutSubviews
|
||||
{
|
||||
[super viewWillLayoutSubviews];
|
||||
_currentTopLayoutGuide = self.topLayoutGuide;
|
||||
_currentBottomLayoutGuide = self.bottomLayoutGuide;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@interface RCTTabBar() <UITabBarControllerDelegate>
|
||||
|
||||
@end
|
||||
|
@ -53,7 +35,7 @@
|
|||
if ((self = [super initWithFrame:CGRectZero])) {
|
||||
_eventDispatcher = eventDispatcher;
|
||||
_tabViews = [[NSMutableArray alloc] init];
|
||||
_tabController = [[RKCustomTabBarController alloc] init];
|
||||
_tabController = [[UITabBarController alloc] init];
|
||||
_tabController.delegate = self;
|
||||
[self addSubview:_tabController.view];
|
||||
}
|
||||
|
|
|
@ -57,7 +57,7 @@
|
|||
"react-timer-mixin": "^0.13.1",
|
||||
"react-tools": "0.13.2",
|
||||
"rebound": "^0.0.12",
|
||||
"sane": "1.0.3",
|
||||
"sane": "^1.1.2",
|
||||
"source-map": "0.1.31",
|
||||
"stacktrace-parser": "git://github.com/frantic/stacktrace-parser.git#493c5e5638",
|
||||
"uglify-js": "~2.4.16",
|
||||
|
|
|
@ -35,25 +35,34 @@
|
|||
function getNativeLogFunction(level) {
|
||||
return function() {
|
||||
var str = Array.prototype.map.call(arguments, function(arg) {
|
||||
if (arg == null) {
|
||||
return arg === null ? 'null' : 'undefined';
|
||||
} else if (typeof arg === 'string') {
|
||||
return '"' + arg + '"';
|
||||
var ret;
|
||||
var type = typeof arg;
|
||||
if (arg === null) {
|
||||
ret = 'null';
|
||||
} else if (arg === undefined) {
|
||||
ret = 'undefined';
|
||||
} else if (type === 'string') {
|
||||
ret = '"' + arg + '"';
|
||||
} else if (type === 'function') {
|
||||
try {
|
||||
ret = arg.toString();
|
||||
} catch (e) {
|
||||
ret = '[function unknown]';
|
||||
}
|
||||
} else {
|
||||
// Perform a try catch, just in case the object has a circular
|
||||
// reference or stringify throws for some other reason.
|
||||
try {
|
||||
return JSON.stringify(arg);
|
||||
ret = JSON.stringify(arg);
|
||||
} catch (e) {
|
||||
if (typeof arg.toString === 'function') {
|
||||
try {
|
||||
return arg.toString();
|
||||
} catch (E) {
|
||||
return 'unknown';
|
||||
}
|
||||
ret = arg.toString();
|
||||
} catch (E) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret || '["' + type + '" failed to stringify]';
|
||||
}).join(', ');
|
||||
global.nativeLoggingHook(str, level);
|
||||
};
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
jest
|
||||
.dontMock('worker-farm')
|
||||
.dontMock('os')
|
||||
.dontMock('../../lib/ModuleTransport')
|
||||
.dontMock('../index');
|
||||
|
||||
var OPTIONS = {
|
||||
|
@ -37,7 +38,7 @@ describe('Transformer', function() {
|
|||
|
||||
pit('should loadFileAndTransform', function() {
|
||||
workers.mockImpl(function(data, callback) {
|
||||
callback(null, { code: 'transformed' });
|
||||
callback(null, { code: 'transformed', map: 'sourceMap' });
|
||||
});
|
||||
require('fs').readFile.mockImpl(function(file, callback) {
|
||||
callback(null, 'content');
|
||||
|
@ -47,6 +48,7 @@ describe('Transformer', function() {
|
|||
.then(function(data) {
|
||||
expect(data).toEqual({
|
||||
code: 'transformed',
|
||||
map: 'sourceMap',
|
||||
sourcePath: 'file',
|
||||
sourceCode: 'content'
|
||||
});
|
||||
|
|
|
@ -14,6 +14,7 @@ var Cache = require('./Cache');
|
|||
var workerFarm = require('worker-farm');
|
||||
var declareOpts = require('../lib/declareOpts');
|
||||
var util = require('util');
|
||||
var ModuleTransport = require('../lib/ModuleTransport');
|
||||
|
||||
var readFile = Promise.promisify(fs.readFile);
|
||||
|
||||
|
@ -100,11 +101,12 @@ Transformer.prototype.loadFileAndTransform = function(filePath) {
|
|||
throw formatError(res.error, filePath, sourceCode);
|
||||
}
|
||||
|
||||
return {
|
||||
return new ModuleTransport({
|
||||
code: res.code,
|
||||
map: res.map,
|
||||
sourcePath: filePath,
|
||||
sourceCode: sourceCode
|
||||
};
|
||||
sourceCode: sourceCode,
|
||||
});
|
||||
}
|
||||
);
|
||||
});
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
var _ = require('underscore');
|
||||
var base64VLQ = require('./base64-vlq');
|
||||
var UglifyJS = require('uglify-js');
|
||||
var ModuleTransport = require('../lib/ModuleTransport');
|
||||
|
||||
module.exports = Package;
|
||||
|
||||
|
@ -19,22 +20,25 @@ function Package(sourceMapUrl) {
|
|||
this._modules = [];
|
||||
this._assets = [];
|
||||
this._sourceMapUrl = sourceMapUrl;
|
||||
this._shouldCombineSourceMaps = false;
|
||||
}
|
||||
|
||||
Package.prototype.setMainModuleId = function(moduleId) {
|
||||
this._mainModuleId = moduleId;
|
||||
};
|
||||
|
||||
Package.prototype.addModule = function(
|
||||
transformedCode,
|
||||
sourceCode,
|
||||
sourcePath
|
||||
) {
|
||||
this._modules.push({
|
||||
transformedCode: transformedCode,
|
||||
sourceCode: sourceCode,
|
||||
sourcePath: sourcePath
|
||||
});
|
||||
Package.prototype.addModule = function(module) {
|
||||
if (!(module instanceof ModuleTransport)) {
|
||||
throw new Error('Expeceted a ModuleTransport object');
|
||||
}
|
||||
|
||||
// If we get a map from the transformer we'll switch to a mode
|
||||
// were we're combining the source maps as opposed to
|
||||
if (!this._shouldCombineSourceMaps && module.map != null) {
|
||||
this._shouldCombineSourceMaps = true;
|
||||
}
|
||||
|
||||
this._modules.push(module);
|
||||
};
|
||||
|
||||
Package.prototype.addAsset = function(asset) {
|
||||
|
@ -45,11 +49,12 @@ Package.prototype.finalize = function(options) {
|
|||
options = options || {};
|
||||
if (options.runMainModule) {
|
||||
var runCode = ';require("' + this._mainModuleId + '");';
|
||||
this.addModule(
|
||||
runCode,
|
||||
runCode,
|
||||
'RunMainModule.js'
|
||||
);
|
||||
this.addModule(new ModuleTransport({
|
||||
code: runCode,
|
||||
virtual: true,
|
||||
sourceCode: runCode,
|
||||
sourcePath: 'RunMainModule.js'
|
||||
}));
|
||||
}
|
||||
|
||||
Object.freeze(this._modules);
|
||||
|
@ -67,7 +72,7 @@ Package.prototype._assertFinalized = function() {
|
|||
|
||||
Package.prototype._getSource = function() {
|
||||
if (this._source == null) {
|
||||
this._source = _.pluck(this._modules, 'transformedCode').join('\n');
|
||||
this._source = _.pluck(this._modules, 'code').join('\n');
|
||||
}
|
||||
return this._source;
|
||||
};
|
||||
|
@ -136,10 +141,50 @@ Package.prototype.getMinifiedSourceAndMap = function() {
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* I found a neat trick in the sourcemap spec that makes it easy
|
||||
* to concat sourcemaps. The `sections` field allows us to combine
|
||||
* the sourcemap easily by adding an offset. Tested on chrome.
|
||||
* Seems like it's not yet in Firefox but that should be fine for
|
||||
* now.
|
||||
*/
|
||||
Package.prototype._getCombinedSourceMaps = function(options) {
|
||||
var result = {
|
||||
version: 3,
|
||||
file: 'bundle.js',
|
||||
sections: [],
|
||||
};
|
||||
|
||||
var line = 0;
|
||||
this._modules.forEach(function(module) {
|
||||
var map = module.map;
|
||||
if (module.virtual) {
|
||||
map = generateSourceMapForVirtualModule(module);
|
||||
}
|
||||
|
||||
if (options.excludeSource) {
|
||||
map = _.extend({}, map, {sourcesContent: []});
|
||||
}
|
||||
|
||||
result.sections.push({
|
||||
offset: { line: line, column: 0 },
|
||||
map: map,
|
||||
});
|
||||
line += module.code.split('\n').length;
|
||||
});
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
Package.prototype.getSourceMap = function(options) {
|
||||
this._assertFinalized();
|
||||
|
||||
options = options || {};
|
||||
|
||||
if (this._shouldCombineSourceMaps) {
|
||||
return this._getCombinedSourceMaps(options);
|
||||
}
|
||||
|
||||
var mappings = this._getMappings();
|
||||
var map = {
|
||||
file: 'bundle.js',
|
||||
|
@ -168,13 +213,14 @@ Package.prototype._getMappings = function() {
|
|||
// except for the lineno mappinp: curLineno - prevLineno = 1; Which is C.
|
||||
var line = 'AACA';
|
||||
|
||||
var moduleLines = Object.create(null);
|
||||
var mappings = '';
|
||||
for (var i = 0; i < modules.length; i++) {
|
||||
var module = modules[i];
|
||||
var transformedCode = module.transformedCode;
|
||||
var code = module.code;
|
||||
var lastCharNewLine = false;
|
||||
module.lines = 0;
|
||||
for (var t = 0; t < transformedCode.length; t++) {
|
||||
moduleLines[module.sourcePath] = 0;
|
||||
for (var t = 0; t < code.length; t++) {
|
||||
if (t === 0 && i === 0) {
|
||||
mappings += firstLine;
|
||||
} else if (t === 0) {
|
||||
|
@ -183,13 +229,15 @@ Package.prototype._getMappings = function() {
|
|||
// This is the only place were we actually don't know the mapping ahead
|
||||
// of time. When it's a new module (and not the first) the lineno
|
||||
// mapping is 0 (current) - number of lines in prev module.
|
||||
mappings += base64VLQ.encode(0 - modules[i - 1].lines);
|
||||
mappings += base64VLQ.encode(
|
||||
0 - moduleLines[modules[i - 1].sourcePath]
|
||||
);
|
||||
mappings += 'A';
|
||||
} else if (lastCharNewLine) {
|
||||
module.lines++;
|
||||
moduleLines[module.sourcePath]++;
|
||||
mappings += line;
|
||||
}
|
||||
lastCharNewLine = transformedCode[t] === '\n';
|
||||
lastCharNewLine = code[t] === '\n';
|
||||
if (lastCharNewLine) {
|
||||
mappings += ';';
|
||||
}
|
||||
|
@ -218,7 +266,25 @@ Package.prototype.getDebugInfo = function() {
|
|||
this._modules.map(function(m) {
|
||||
return '<div> <h4> Path: </h4>' + m.sourcePath + '<br/> <h4> Source: </h4>' +
|
||||
'<code><pre class="collapsed" onclick="this.classList.remove(\'collapsed\')">' +
|
||||
_.escape(m.transformedCode) + '</pre></code></div>';
|
||||
_.escape(m.code) + '</pre></code></div>';
|
||||
}).join('\n'),
|
||||
].join('\n');
|
||||
};
|
||||
|
||||
function generateSourceMapForVirtualModule(module) {
|
||||
// All lines map 1-to-1
|
||||
var mappings = 'AAAA;';
|
||||
|
||||
for (var i = 1; i < module.code.split('\n').length; i++) {
|
||||
mappings += 'AACA;';
|
||||
}
|
||||
|
||||
return {
|
||||
version: 3,
|
||||
sources: [ module.sourcePath ],
|
||||
names: [],
|
||||
mappings: mappings,
|
||||
file: module.sourcePath,
|
||||
sourcesContent: [ module.code ],
|
||||
};
|
||||
}
|
||||
|
|
|
@ -13,11 +13,13 @@ jest.autoMockOff();
|
|||
var SourceMapGenerator = require('source-map').SourceMapGenerator;
|
||||
|
||||
describe('Package', function() {
|
||||
var ModuleTransport;
|
||||
var Package;
|
||||
var ppackage;
|
||||
|
||||
beforeEach(function() {
|
||||
Package = require('../Package');
|
||||
ModuleTransport = require('../../lib/ModuleTransport');
|
||||
ppackage = new Package('test_url');
|
||||
ppackage.getSourceMap = jest.genMockFn().mockImpl(function() {
|
||||
return 'test-source-map';
|
||||
|
@ -26,8 +28,17 @@ describe('Package', function() {
|
|||
|
||||
describe('source package', function() {
|
||||
it('should create a package and get the source', function() {
|
||||
ppackage.addModule('transformed foo;', 'source foo', 'foo path');
|
||||
ppackage.addModule('transformed bar;', 'source bar', 'bar path');
|
||||
ppackage.addModule(new ModuleTransport({
|
||||
code: 'transformed foo;',
|
||||
sourceCode: 'source foo',
|
||||
sourcePath: 'foo path',
|
||||
}));
|
||||
ppackage.addModule(new ModuleTransport({
|
||||
code: 'transformed bar;',
|
||||
sourceCode: 'source bar',
|
||||
sourcePath: 'bar path',
|
||||
}));
|
||||
|
||||
ppackage.finalize({});
|
||||
expect(ppackage.getSource()).toBe([
|
||||
'transformed foo;',
|
||||
|
@ -37,8 +48,18 @@ describe('Package', function() {
|
|||
});
|
||||
|
||||
it('should create a package and add run module code', function() {
|
||||
ppackage.addModule('transformed foo;', 'source foo', 'foo path');
|
||||
ppackage.addModule('transformed bar;', 'source bar', 'bar path');
|
||||
ppackage.addModule(new ModuleTransport({
|
||||
code: 'transformed foo;',
|
||||
sourceCode: 'source foo',
|
||||
sourcePath: 'foo path'
|
||||
}));
|
||||
|
||||
ppackage.addModule(new ModuleTransport({
|
||||
code: 'transformed bar;',
|
||||
sourceCode: 'source bar',
|
||||
sourcePath: 'bar path'
|
||||
}));
|
||||
|
||||
ppackage.setMainModuleId('foo');
|
||||
ppackage.finalize({runMainModule: true});
|
||||
expect(ppackage.getSource()).toBe([
|
||||
|
@ -59,7 +80,11 @@ describe('Package', function() {
|
|||
return minified;
|
||||
};
|
||||
|
||||
ppackage.addModule('transformed foo;', 'source foo', 'foo path');
|
||||
ppackage.addModule(new ModuleTransport({
|
||||
code: 'transformed foo;',
|
||||
sourceCode: 'source foo',
|
||||
sourcePath: 'foo path'
|
||||
}));
|
||||
ppackage.finalize();
|
||||
expect(ppackage.getMinifiedSourceAndMap()).toBe(minified);
|
||||
});
|
||||
|
@ -68,13 +93,104 @@ describe('Package', function() {
|
|||
describe('sourcemap package', function() {
|
||||
it('should create sourcemap', function() {
|
||||
var p = new Package('test_url');
|
||||
p.addModule('transformed foo;\n', 'source foo', 'foo path');
|
||||
p.addModule('transformed bar;\n', 'source bar', 'bar path');
|
||||
p.addModule(new ModuleTransport({
|
||||
code: [
|
||||
'transformed foo',
|
||||
'transformed foo',
|
||||
'transformed foo',
|
||||
].join('\n'),
|
||||
sourceCode: [
|
||||
'source foo',
|
||||
'source foo',
|
||||
'source foo',
|
||||
].join('\n'),
|
||||
sourcePath: 'foo path',
|
||||
}));
|
||||
p.addModule(new ModuleTransport({
|
||||
code: [
|
||||
'transformed bar',
|
||||
'transformed bar',
|
||||
'transformed bar',
|
||||
].join('\n'),
|
||||
sourceCode: [
|
||||
'source bar',
|
||||
'source bar',
|
||||
'source bar',
|
||||
].join('\n'),
|
||||
sourcePath: 'bar path',
|
||||
}));
|
||||
|
||||
p.setMainModuleId('foo');
|
||||
p.finalize({runMainModule: true});
|
||||
var s = p.getSourceMap();
|
||||
expect(s).toEqual(genSourceMap(p._modules));
|
||||
});
|
||||
|
||||
it('should combine sourcemaps', function() {
|
||||
var p = new Package('test_url');
|
||||
|
||||
p.addModule(new ModuleTransport({
|
||||
code: 'transformed foo;\n',
|
||||
map: {name: 'sourcemap foo'},
|
||||
sourceCode: 'source foo',
|
||||
sourcePath: 'foo path'
|
||||
}));
|
||||
|
||||
p.addModule(new ModuleTransport({
|
||||
code: 'transformed foo;\n',
|
||||
map: {name: 'sourcemap bar'},
|
||||
sourceCode: 'source foo',
|
||||
sourcePath: 'foo path'
|
||||
}));
|
||||
|
||||
p.addModule(new ModuleTransport({
|
||||
code: 'image module;\nimage module;',
|
||||
virtual: true,
|
||||
sourceCode: 'image module;\nimage module;',
|
||||
sourcePath: 'image.png',
|
||||
}));
|
||||
|
||||
p.setMainModuleId('foo');
|
||||
p.finalize({runMainModule: true});
|
||||
|
||||
var s = p.getSourceMap();
|
||||
expect(s).toEqual({
|
||||
file: 'bundle.js',
|
||||
version: 3,
|
||||
sections: [
|
||||
{ offset: { line: 0, column: 0 }, map: { name: 'sourcemap foo' } },
|
||||
{ offset: { line: 2, column: 0 }, map: { name: 'sourcemap bar' } },
|
||||
{
|
||||
offset: {
|
||||
column: 0,
|
||||
line: 4
|
||||
},
|
||||
map: {
|
||||
file: 'image.png',
|
||||
mappings: 'AAAA;AACA;',
|
||||
names: {},
|
||||
sources: [ 'image.png' ],
|
||||
sourcesContent: ['image module;\nimage module;'],
|
||||
version: 3,
|
||||
}
|
||||
},
|
||||
{
|
||||
offset: {
|
||||
column: 0,
|
||||
line: 6
|
||||
},
|
||||
map: {
|
||||
file: 'RunMainModule.js',
|
||||
mappings: 'AAAA;',
|
||||
names: {},
|
||||
sources: [ 'RunMainModule.js' ],
|
||||
sourcesContent: [';require("foo");'],
|
||||
version: 3,
|
||||
}
|
||||
}
|
||||
],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('getAssets()', function() {
|
||||
|
@ -95,7 +211,7 @@ describe('Package', function() {
|
|||
var packageLineNo = 0;
|
||||
for (var i = 0; i < modules.length; i++) {
|
||||
var module = modules[i];
|
||||
var transformedCode = module.transformedCode;
|
||||
var transformedCode = module.code;
|
||||
var sourcePath = module.sourcePath;
|
||||
var sourceCode = module.sourceCode;
|
||||
var transformedLineCount = 0;
|
||||
|
|
|
@ -13,6 +13,7 @@ jest
|
|||
.dontMock('path')
|
||||
.dontMock('os')
|
||||
.dontMock('underscore')
|
||||
.dontMock('../../lib/ModuleTransport')
|
||||
.setMock('uglify-js')
|
||||
.dontMock('../');
|
||||
|
||||
|
@ -93,6 +94,7 @@ describe('Packager', function() {
|
|||
.mockImpl(function(path) {
|
||||
return Promise.resolve({
|
||||
code: 'transformed ' + path,
|
||||
map: 'sourcemap ' + path,
|
||||
sourceCode: 'source ' + path,
|
||||
sourcePath: path
|
||||
});
|
||||
|
@ -117,16 +119,19 @@ describe('Packager', function() {
|
|||
|
||||
return packager.package('/root/foo.js', true, 'source_map_url')
|
||||
.then(function(p) {
|
||||
expect(p.addModule.mock.calls[0]).toEqual([
|
||||
'lol transformed /root/foo.js lol',
|
||||
'source /root/foo.js',
|
||||
'/root/foo.js'
|
||||
]);
|
||||
expect(p.addModule.mock.calls[1]).toEqual([
|
||||
'lol transformed /root/bar.js lol',
|
||||
'source /root/bar.js',
|
||||
'/root/bar.js'
|
||||
]);
|
||||
expect(p.addModule.mock.calls[0][0]).toEqual({
|
||||
code: 'lol transformed /root/foo.js lol',
|
||||
map: 'sourcemap /root/foo.js',
|
||||
sourceCode: 'source /root/foo.js',
|
||||
sourcePath: '/root/foo.js',
|
||||
});
|
||||
|
||||
expect(p.addModule.mock.calls[1][0]).toEqual({
|
||||
code: 'lol transformed /root/bar.js lol',
|
||||
map: 'sourcemap /root/bar.js',
|
||||
sourceCode: 'source /root/bar.js',
|
||||
sourcePath: '/root/bar.js'
|
||||
});
|
||||
|
||||
var imgModule_DEPRECATED = {
|
||||
__packager_asset: true,
|
||||
|
@ -138,15 +143,15 @@ describe('Packager', function() {
|
|||
deprecated: true,
|
||||
};
|
||||
|
||||
expect(p.addModule.mock.calls[2]).toEqual([
|
||||
'lol module.exports = ' +
|
||||
expect(p.addModule.mock.calls[2][0]).toEqual({
|
||||
code: 'lol module.exports = ' +
|
||||
JSON.stringify(imgModule_DEPRECATED) +
|
||||
'; lol',
|
||||
'module.exports = ' +
|
||||
sourceCode: 'module.exports = ' +
|
||||
JSON.stringify(imgModule_DEPRECATED) +
|
||||
';',
|
||||
'/root/img/img.png'
|
||||
]);
|
||||
sourcePath: '/root/img/img.png'
|
||||
});
|
||||
|
||||
var imgModule = {
|
||||
__packager_asset: true,
|
||||
|
@ -160,21 +165,21 @@ describe('Packager', function() {
|
|||
type: 'png',
|
||||
};
|
||||
|
||||
expect(p.addModule.mock.calls[3]).toEqual([
|
||||
'lol module.exports = ' +
|
||||
expect(p.addModule.mock.calls[3][0]).toEqual({
|
||||
code: 'lol module.exports = ' +
|
||||
JSON.stringify(imgModule) +
|
||||
'; lol',
|
||||
'module.exports = ' +
|
||||
sourceCode: 'module.exports = ' +
|
||||
JSON.stringify(imgModule) +
|
||||
';',
|
||||
'/root/img/new_image.png'
|
||||
]);
|
||||
sourcePath: '/root/img/new_image.png'
|
||||
});
|
||||
|
||||
expect(p.addModule.mock.calls[4]).toEqual([
|
||||
'lol module.exports = {"json":true}; lol',
|
||||
'module.exports = {"json":true};',
|
||||
'/root/file.json'
|
||||
]);
|
||||
expect(p.addModule.mock.calls[4][0]).toEqual({
|
||||
code: 'lol module.exports = {"json":true}; lol',
|
||||
sourceCode: 'module.exports = {"json":true};',
|
||||
sourcePath: '/root/file.json'
|
||||
});
|
||||
|
||||
expect(p.finalize.mock.calls[0]).toEqual([
|
||||
{runMainModule: true}
|
||||
|
@ -189,5 +194,4 @@ describe('Packager', function() {
|
|||
]);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
|
|
@ -14,9 +14,9 @@ var path = require('path');
|
|||
var Promise = require('bluebird');
|
||||
var Transformer = require('../JSTransformer');
|
||||
var DependencyResolver = require('../DependencyResolver');
|
||||
var _ = require('underscore');
|
||||
var Package = require('./Package');
|
||||
var Activity = require('../Activity');
|
||||
var ModuleTransport = require('../lib/ModuleTransport');
|
||||
var declareOpts = require('../lib/declareOpts');
|
||||
var imageSize = require('image-size');
|
||||
|
||||
|
@ -125,12 +125,8 @@ Packager.prototype.package = function(main, runModule, sourceMapUrl, isDev) {
|
|||
.then(function(transformedModules) {
|
||||
Activity.endEvent(transformEventId);
|
||||
|
||||
transformedModules.forEach(function(transformed) {
|
||||
ppackage.addModule(
|
||||
transformed.code,
|
||||
transformed.sourceCode,
|
||||
transformed.sourcePath
|
||||
);
|
||||
transformedModules.forEach(function(moduleTransport) {
|
||||
ppackage.addModule(moduleTransport);
|
||||
});
|
||||
|
||||
ppackage.finalize({ runMainModule: runModule });
|
||||
|
@ -163,11 +159,13 @@ Packager.prototype._transformModule = function(ppackage, module) {
|
|||
|
||||
var resolver = this._resolver;
|
||||
return transform.then(function(transformed) {
|
||||
return _.extend(
|
||||
{},
|
||||
transformed,
|
||||
{code: resolver.wrapModule(module, transformed.code)}
|
||||
);
|
||||
var code = resolver.wrapModule(module, transformed.code);
|
||||
return new ModuleTransport({
|
||||
code: code,
|
||||
map: transformed.map,
|
||||
sourceCode: transformed.sourceCode,
|
||||
sourcePath: transformed.sourcePath,
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -191,11 +189,12 @@ Packager.prototype.generateAssetModule_DEPRECATED = function(ppackage, module) {
|
|||
|
||||
var code = 'module.exports = ' + JSON.stringify(img) + ';';
|
||||
|
||||
return {
|
||||
return new ModuleTransport({
|
||||
code: code,
|
||||
sourceCode: code,
|
||||
sourcePath: module.path,
|
||||
};
|
||||
virtual: true,
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -222,11 +221,12 @@ Packager.prototype.generateAssetModule = function(ppackage, module) {
|
|||
|
||||
var code = 'module.exports = ' + JSON.stringify(img) + ';';
|
||||
|
||||
return {
|
||||
return new ModuleTransport({
|
||||
code: code,
|
||||
sourceCode: code,
|
||||
sourcePath: module.path,
|
||||
};
|
||||
virtual: true,
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -234,11 +234,12 @@ function generateJSONModule(module) {
|
|||
return readFile(module.path).then(function(data) {
|
||||
var code = 'module.exports = ' + data.toString('utf8') + ';';
|
||||
|
||||
return {
|
||||
return new ModuleTransport({
|
||||
code: code,
|
||||
sourceCode: code,
|
||||
sourcePath: module.path,
|
||||
};
|
||||
virtual: true,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
/**
|
||||
* 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';
|
||||
|
||||
function ModuleTransport(data) {
|
||||
assertExists(data, 'code');
|
||||
this.code = data.code;
|
||||
|
||||
assertExists(data, 'sourceCode');
|
||||
this.sourceCode = data.sourceCode;
|
||||
|
||||
assertExists(data, 'sourcePath');
|
||||
this.sourcePath = data.sourcePath;
|
||||
|
||||
this.virtual = data.virtual;
|
||||
|
||||
if (this.virtual && data.map) {
|
||||
throw new Error('Virtual modules cannot have source maps');
|
||||
}
|
||||
|
||||
this.map = data.map;
|
||||
|
||||
Object.freeze(this);
|
||||
}
|
||||
|
||||
module.exports = ModuleTransport;
|
||||
|
||||
function assertExists(obj, field) {
|
||||
if (obj[field] == null) {
|
||||
throw new Error('Modules must have `' + field + '`');
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue