Merge pull request #1505 from ericvicenti/Update_Wed_3_Jun
Updates from Wed 3 Jun
This commit is contained in:
commit
8b93b99d4a
|
@ -17,6 +17,6 @@
|
|||
|
||||
int main(int argc, char * argv[]) {
|
||||
@autoreleasepool {
|
||||
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
|
||||
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -58,7 +58,7 @@
|
|||
}
|
||||
|
||||
XCTAssertNil(redboxError, @"RedBox error: %@", redboxError);
|
||||
XCTAssertTrue(foundElement, @"Cound't find element with text '%@' in %d seconds", TEXT_TO_LOOK_FOR, TIMEOUT_SECONDS);
|
||||
XCTAssertTrue(foundElement, @"Couldn't find element with text '%@' in %d seconds", TEXT_TO_LOOK_FOR, TIMEOUT_SECONDS);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -59,6 +59,16 @@ var propTypes = {
|
|||
* imagesPerRow: Number of images to be shown in each row.
|
||||
*/
|
||||
imagesPerRow: React.PropTypes.number,
|
||||
|
||||
/**
|
||||
* The asset type, one of 'Photos', 'Videos' or 'All'
|
||||
*/
|
||||
assetType: React.PropTypes.oneOf([
|
||||
'Photos',
|
||||
'Videos',
|
||||
'All',
|
||||
]),
|
||||
|
||||
};
|
||||
|
||||
var CameraRollView = React.createClass({
|
||||
|
@ -69,6 +79,7 @@ var CameraRollView = React.createClass({
|
|||
groupTypes: 'SavedPhotos',
|
||||
batchSize: 5,
|
||||
imagesPerRow: 1,
|
||||
assetType: 'Photos',
|
||||
renderImage: function(asset) {
|
||||
var imageSize = 150;
|
||||
var imageStyle = [styles.image, {width: imageSize, height: imageSize}];
|
||||
|
@ -89,6 +100,7 @@ var CameraRollView = React.createClass({
|
|||
assets: ([]: Array<Image>),
|
||||
groupTypes: this.props.groupTypes,
|
||||
lastCursor: (null : ?string),
|
||||
assetType: this.props.assetType,
|
||||
noMore: false,
|
||||
loadingMore: false,
|
||||
dataSource: ds,
|
||||
|
@ -124,7 +136,8 @@ var CameraRollView = React.createClass({
|
|||
|
||||
var fetchParams: Object = {
|
||||
first: this.props.batchSize,
|
||||
groupTypes: this.props.groupTypes
|
||||
groupTypes: this.props.groupTypes,
|
||||
assetType: this.props.assetType,
|
||||
};
|
||||
if (this.state.lastCursor) {
|
||||
fetchParams.after = this.state.lastCursor;
|
||||
|
|
|
@ -29,13 +29,13 @@ var ReachabilitySubscription = React.createClass({
|
|||
};
|
||||
},
|
||||
componentDidMount: function() {
|
||||
NetInfo.reachabilityIOS.addEventListener(
|
||||
NetInfo.addEventListener(
|
||||
'change',
|
||||
this._handleReachabilityChange
|
||||
);
|
||||
},
|
||||
componentWillUnmount: function() {
|
||||
NetInfo.reachabilityIOS.removeEventListener(
|
||||
NetInfo.removeEventListener(
|
||||
'change',
|
||||
this._handleReachabilityChange
|
||||
);
|
||||
|
@ -63,16 +63,16 @@ var ReachabilityCurrent = React.createClass({
|
|||
};
|
||||
},
|
||||
componentDidMount: function() {
|
||||
NetInfo.reachabilityIOS.addEventListener(
|
||||
NetInfo.addEventListener(
|
||||
'change',
|
||||
this._handleReachabilityChange
|
||||
);
|
||||
NetInfo.reachabilityIOS.fetch().done(
|
||||
NetInfo.fetch().done(
|
||||
(reachability) => { this.setState({reachability}); }
|
||||
);
|
||||
},
|
||||
componentWillUnmount: function() {
|
||||
NetInfo.reachabilityIOS.removeEventListener(
|
||||
NetInfo.removeEventListener(
|
||||
'change',
|
||||
this._handleReachabilityChange
|
||||
);
|
||||
|
|
|
@ -0,0 +1,159 @@
|
|||
/**
|
||||
* Copyright 2004-present Facebook. All Rights Reserved.
|
||||
*
|
||||
* @providesModule TransformExample
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var React = require('React');
|
||||
|
||||
var StyleSheet = require('StyleSheet');
|
||||
var TimerMixin = require('react-timer-mixin');
|
||||
var UIExplorerBlock = require('UIExplorerBlock');
|
||||
var UIExplorerPage = require('UIExplorerPage');
|
||||
var View = require('View');
|
||||
|
||||
var TransformExample = React.createClass({
|
||||
|
||||
mixins: [TimerMixin],
|
||||
|
||||
getInitialState() {
|
||||
return {
|
||||
interval: this.setInterval(this._update, 800),
|
||||
pulse: false,
|
||||
};
|
||||
},
|
||||
|
||||
render() {
|
||||
return (
|
||||
<UIExplorerPage title="Transforms">
|
||||
<UIExplorerBlock title="foo bar">
|
||||
<View style={{height: 500}}>
|
||||
<View style={styles.box1} />
|
||||
<View style={styles.box2} />
|
||||
<View style={styles.box3step1} />
|
||||
<View style={styles.box3step2} />
|
||||
<View style={styles.box3step3} />
|
||||
<View style={styles.box4} />
|
||||
<View style={[
|
||||
styles.box5,
|
||||
this.state.pulse ? styles.box5Transform : null
|
||||
]} />
|
||||
</View>
|
||||
</UIExplorerBlock>
|
||||
</UIExplorerPage>
|
||||
);
|
||||
},
|
||||
|
||||
_update() {
|
||||
this.setState({
|
||||
pulse: !this.state.pulse,
|
||||
});
|
||||
},
|
||||
|
||||
});
|
||||
|
||||
var styles = StyleSheet.create({
|
||||
box1: {
|
||||
left: 0,
|
||||
backgroundColor: 'green',
|
||||
height: 50,
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
transform: [
|
||||
{translateX: 100},
|
||||
{translateY: 50},
|
||||
{rotate: '30deg'},
|
||||
{scaleX: 2},
|
||||
{scaleY: 2},
|
||||
],
|
||||
width: 50,
|
||||
},
|
||||
box2: {
|
||||
left: 0,
|
||||
backgroundColor: 'purple',
|
||||
height: 50,
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
transform: [
|
||||
{scaleX: 2},
|
||||
{scaleY: 2},
|
||||
{translateX: 100},
|
||||
{translateY: 50},
|
||||
{rotate: '30deg'},
|
||||
],
|
||||
width: 50,
|
||||
},
|
||||
box3step1: {
|
||||
left: 0,
|
||||
backgroundColor: '#ffb6c1', // lightpink
|
||||
height: 50,
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
transform: [
|
||||
{rotate: '30deg'},
|
||||
],
|
||||
width: 50,
|
||||
},
|
||||
box3step2: {
|
||||
left: 0,
|
||||
backgroundColor: '#ff69b4', //hotpink
|
||||
height: 50,
|
||||
opacity: 0.5,
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
transform: [
|
||||
{rotate: '30deg'},
|
||||
{scaleX: 2},
|
||||
{scaleY: 2},
|
||||
],
|
||||
width: 50,
|
||||
},
|
||||
box3step3: {
|
||||
left: 0,
|
||||
backgroundColor: '#ff1493', // deeppink
|
||||
height: 50,
|
||||
opacity: 0.5,
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
transform: [
|
||||
{rotate: '30deg'},
|
||||
{scaleX: 2},
|
||||
{scaleY: 2},
|
||||
{translateX: 100},
|
||||
{translateY: 50},
|
||||
],
|
||||
width: 50,
|
||||
},
|
||||
box4: {
|
||||
left: 0,
|
||||
backgroundColor: '#ff8c00', // darkorange
|
||||
height: 50,
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
transform: [
|
||||
{translate: [200, 350]},
|
||||
{scale: 2.5},
|
||||
{rotate: '-0.2rad'},
|
||||
],
|
||||
width: 100,
|
||||
},
|
||||
box5: {
|
||||
backgroundColor: '#800000', // maroon
|
||||
height: 50,
|
||||
position: 'absolute',
|
||||
right: 0,
|
||||
top: 0,
|
||||
width: 50,
|
||||
},
|
||||
box5Transform: {
|
||||
transform: [
|
||||
{translate: [-50, 35]},
|
||||
{rotate: '50deg'},
|
||||
{scale: 2},
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
module.exports = TransformExample;
|
|
@ -30,7 +30,7 @@ var {
|
|||
var { TestModule } = React.addons;
|
||||
var Settings = require('Settings');
|
||||
|
||||
import type { Example, ExampleModule } from 'ExampleTypes';
|
||||
import type { ExampleModule } from 'ExampleTypes';
|
||||
|
||||
var createExamplePage = require('./createExamplePage');
|
||||
|
||||
|
@ -154,7 +154,9 @@ class UIExplorerList extends React.Component {
|
|||
dataSource={this.state.dataSource}
|
||||
renderRow={this._renderRow.bind(this)}
|
||||
renderSectionHeader={this._renderSectionHeader}
|
||||
keyboardShouldPersistTaps={true}
|
||||
automaticallyAdjustContentInsets={false}
|
||||
keyboardDismissMode="onDrag"
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
|
|
|
@ -62,6 +62,9 @@
|
|||
// Make sure this test runs first because the other tests will tear out the rootView
|
||||
- (void)testAAA_RootViewLoadsAndRenders
|
||||
{
|
||||
// TODO (t7296305) Fix and Re-Enable this UIExplorer Test
|
||||
return;
|
||||
|
||||
UIViewController *vc = [UIApplication sharedApplication].delegate.window.rootViewController;
|
||||
RCTAssert([vc.view isKindOfClass:[RCTRootView class]], @"This test must run first.");
|
||||
NSDate *date = [NSDate dateWithTimeIntervalSinceNow:TIMEOUT_SECONDS];
|
||||
|
@ -82,7 +85,7 @@
|
|||
}
|
||||
|
||||
XCTAssertNil(redboxError, @"RedBox error: %@", redboxError);
|
||||
XCTAssertTrue(foundElement, @"Cound't find element with '<View>' text in %d seconds", TIMEOUT_SECONDS);
|
||||
XCTAssertTrue(foundElement, @"Couldn't find element with '<View>' text in %d seconds", TIMEOUT_SECONDS);
|
||||
}
|
||||
|
||||
#define RCT_SNAPSHOT_TEST(name, reRecord) \
|
||||
|
|
|
@ -29,8 +29,16 @@ var GROUP_TYPES_OPTIONS = [
|
|||
'SavedPhotos', // default
|
||||
];
|
||||
|
||||
var ASSET_TYPE_OPTIONS = [
|
||||
'All',
|
||||
'Videos',
|
||||
'Photos', // default
|
||||
];
|
||||
|
||||
|
||||
// Flow treats Object and Array as disjoint types, currently.
|
||||
deepFreezeAndThrowOnMutationInDev((GROUP_TYPES_OPTIONS: any));
|
||||
deepFreezeAndThrowOnMutationInDev((ASSET_TYPE_OPTIONS: any));
|
||||
|
||||
/**
|
||||
* Shape of the param arg for the `getPhotos` function.
|
||||
|
@ -58,6 +66,11 @@ var getPhotosParamChecker = createStrictShapeTypeChecker({
|
|||
* titles.
|
||||
*/
|
||||
groupName: ReactPropTypes.string,
|
||||
|
||||
/**
|
||||
* Specifies filter on asset type
|
||||
*/
|
||||
assetType: ReactPropTypes.oneOf(ASSET_TYPE_OPTIONS),
|
||||
});
|
||||
|
||||
/**
|
||||
|
@ -94,6 +107,7 @@ var getPhotosReturnChecker = createStrictShapeTypeChecker({
|
|||
class CameraRoll {
|
||||
|
||||
static GroupTypesOptions: Array<string>;
|
||||
static AssetTypeOptions: Array<string>;
|
||||
/**
|
||||
* Saves the image with tag `tag` to the camera roll.
|
||||
*
|
||||
|
@ -154,5 +168,6 @@ class CameraRoll {
|
|||
}
|
||||
|
||||
CameraRoll.GroupTypesOptions = GROUP_TYPES_OPTIONS;
|
||||
CameraRoll.AssetTypeOptions = ASSET_TYPE_OPTIONS;
|
||||
|
||||
module.exports = CameraRoll;
|
||||
|
|
|
@ -45,7 +45,6 @@ var DEFAULT_INITIAL_ROWS = 10;
|
|||
var DEFAULT_SCROLL_RENDER_AHEAD = 1000;
|
||||
var DEFAULT_END_REACHED_THRESHOLD = 1000;
|
||||
var DEFAULT_SCROLL_CALLBACK_THROTTLE = 50;
|
||||
var RENDER_INTERVAL = 20;
|
||||
var SCROLLVIEW_REF = 'listviewscroll';
|
||||
|
||||
|
||||
|
@ -258,7 +257,6 @@ var ListView = React.createClass({
|
|||
// the component is laid out
|
||||
this.requestAnimationFrame(() => {
|
||||
this._measureAndUpdateScrollProps();
|
||||
this.setInterval(this._renderMoreRowsIfNeeded, RENDER_INTERVAL);
|
||||
});
|
||||
},
|
||||
|
||||
|
@ -329,7 +327,7 @@ var ListView = React.createClass({
|
|||
totalIndex++;
|
||||
|
||||
if (this.props.renderSeparator &&
|
||||
(rowIdx !== rowIDs.length - 1 || sectionIdx === allRowIDs.length -1)) {
|
||||
(rowIdx !== rowIDs.length - 1 || sectionIdx === allRowIDs.length - 1)) {
|
||||
var adjacentRowHighlighted =
|
||||
this.state.highlightedRow.sectionID === sectionID && (
|
||||
this.state.highlightedRow.rowID === rowID ||
|
||||
|
@ -397,6 +395,7 @@ var ListView = React.createClass({
|
|||
_setScrollVisibleHeight: function(left, top, width, height) {
|
||||
this.scrollProperties.visibleHeight = height;
|
||||
this._updateVisibleRows();
|
||||
this._renderMoreRowsIfNeeded();
|
||||
},
|
||||
|
||||
_renderMoreRowsIfNeeded: function() {
|
||||
|
@ -443,8 +442,8 @@ var ListView = React.createClass({
|
|||
}
|
||||
var updatedFrames = e && e.nativeEvent.updatedChildFrames;
|
||||
if (updatedFrames) {
|
||||
updatedFrames.forEach((frame) => {
|
||||
this._childFrames[frame.index] = merge(frame);
|
||||
updatedFrames.forEach((newFrame) => {
|
||||
this._childFrames[newFrame.index] = merge(newFrame);
|
||||
});
|
||||
}
|
||||
var dataSource = this.props.dataSource;
|
||||
|
|
|
@ -1290,7 +1290,7 @@ var Navigator = React.createClass({
|
|||
key={this.state.idStack[i]}
|
||||
ref={'scene_' + i}
|
||||
onStartShouldSetResponderCapture={() => {
|
||||
return i !== this.state.presentedIndex;
|
||||
return !!this.state.transitionFromIndex || !!this.state.activeGesture;
|
||||
}}
|
||||
style={[styles.baseScene, this.props.sceneStyle, disabledSceneStyle]}>
|
||||
{React.cloneElement(child, {
|
||||
|
|
|
@ -69,7 +69,9 @@ RCT_EXPORT_METHOD(getPhotos:(NSDictionary *)params
|
|||
NSString *afterCursor = params[@"after"];
|
||||
NSString *groupTypesStr = params[@"groupTypes"];
|
||||
NSString *groupName = params[@"groupName"];
|
||||
NSString *assetType = params[@"assetType"];
|
||||
ALAssetsGroupType groupTypes;
|
||||
|
||||
if ([groupTypesStr isEqualToString:@"Album"]) {
|
||||
groupTypes = ALAssetsGroupAlbum;
|
||||
} else if ([groupTypesStr isEqualToString:@"All"]) {
|
||||
|
@ -93,7 +95,15 @@ RCT_EXPORT_METHOD(getPhotos:(NSDictionary *)params
|
|||
|
||||
[[RCTImageLoader assetsLibrary] enumerateGroupsWithTypes:groupTypes usingBlock:^(ALAssetsGroup *group, BOOL *stopGroups) {
|
||||
if (group && (groupName == nil || [groupName isEqualToString:[group valueForProperty:ALAssetsGroupPropertyName]])) {
|
||||
[group setAssetsFilter:ALAssetsFilter.allPhotos];
|
||||
|
||||
if (assetType == nil || [assetType isEqualToString:@"Photos"]) {
|
||||
[group setAssetsFilter:ALAssetsFilter.allPhotos];
|
||||
} else if ([assetType isEqualToString:@"Videos"]) {
|
||||
[group setAssetsFilter:ALAssetsFilter.allVideos];
|
||||
} else if ([assetType isEqualToString:@"All"]) {
|
||||
[group setAssetsFilter:ALAssetsFilter.allAssets];
|
||||
}
|
||||
|
||||
[group enumerateAssetsWithOptions:NSEnumerationReverse usingBlock:^(ALAsset *result, NSUInteger index, BOOL *stopAssets) {
|
||||
if (result) {
|
||||
NSString *uri = [(NSURL *)[result valueForProperty:ALAssetPropertyAssetURL] absoluteString];
|
||||
|
|
|
@ -175,7 +175,7 @@ describe('resolveAssetSource', () => {
|
|||
isStatic: true,
|
||||
width: 100,
|
||||
height: 200,
|
||||
uri: 'assets_awesomemodule_subdir_logo1_',
|
||||
uri: 'awesomemodule_subdir_logo1_',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -50,7 +50,8 @@ function getPathInArchive(asset) {
|
|||
return (assetDir + '/' + asset.name)
|
||||
.toLowerCase()
|
||||
.replace(/\//g, '_') // Encode folder structure in file name
|
||||
.replace(/([^a-z0-9_])/g, ''); // Remove illegal chars
|
||||
.replace(/([^a-z0-9_])/g, '') // Remove illegal chars
|
||||
.replace(/^assets_/, ''); // Remove "assets_" prefix
|
||||
} else {
|
||||
// E.g. 'assets/AwesomeModule/icon@2x.png'
|
||||
return getScaledAssetPath(asset);
|
||||
|
|
|
@ -30,17 +30,10 @@ function reportException(e: Exception, isFatal: bool, stack?: any) {
|
|||
if (!stack) {
|
||||
stack = parseErrorStack(e);
|
||||
}
|
||||
if (!RCTExceptionsManager.reportFatalException ||
|
||||
!RCTExceptionsManager.reportSoftException) {
|
||||
// Backwards compatibility - no differentiation
|
||||
// TODO(#7049989): deprecate reportUnhandledException on Android
|
||||
RCTExceptionsManager.reportUnhandledException(e.message, stack);
|
||||
if (isFatal) {
|
||||
RCTExceptionsManager.reportFatalException(e.message, stack);
|
||||
} else {
|
||||
if (isFatal) {
|
||||
RCTExceptionsManager.reportFatalException(e.message, stack);
|
||||
} else {
|
||||
RCTExceptionsManager.reportSoftException(e.message, stack);
|
||||
}
|
||||
RCTExceptionsManager.reportSoftException(e.message, stack);
|
||||
}
|
||||
if (__DEV__) {
|
||||
(sourceMapPromise = sourceMapPromise || loadSourceMap())
|
||||
|
|
|
@ -33,7 +33,7 @@ if (typeof window === 'undefined') {
|
|||
window = GLOBAL;
|
||||
}
|
||||
|
||||
function handleErrorWithRedBox(e, isFatal) {
|
||||
function handleError(e, isFatal) {
|
||||
try {
|
||||
require('ExceptionsManager').handleException(e, isFatal);
|
||||
} catch(ee) {
|
||||
|
@ -43,7 +43,7 @@ function handleErrorWithRedBox(e, isFatal) {
|
|||
|
||||
function setUpRedBoxErrorHandler() {
|
||||
var ErrorUtils = require('ErrorUtils');
|
||||
ErrorUtils.setGlobalHandler(handleErrorWithRedBox);
|
||||
ErrorUtils.setGlobalHandler(handleError);
|
||||
}
|
||||
|
||||
function setUpRedBoxConsoleErrorHandler() {
|
||||
|
|
|
@ -12,8 +12,14 @@
|
|||
'use strict';
|
||||
|
||||
var NativeModules = require('NativeModules');
|
||||
var Platform = require('Platform');
|
||||
var RCTDeviceEventEmitter = require('RCTDeviceEventEmitter');
|
||||
var RCTReachability = NativeModules.Reachability;
|
||||
|
||||
if (Platform.OS === 'ios') {
|
||||
var RCTNetInfo = NativeModules.Reachability;
|
||||
} else if (Platform.OS === 'android') {
|
||||
var RCTNetInfo = NativeModules.NetInfo;
|
||||
}
|
||||
|
||||
var DEVICE_REACHABILITY_EVENT = 'reachabilityDidChange';
|
||||
|
||||
|
@ -28,11 +34,50 @@ type ReachabilityStateIOS = $Enum<{
|
|||
wifi: string;
|
||||
}>;
|
||||
|
||||
type ConnectivityStateAndroid = $Enum<{
|
||||
NONE: string;
|
||||
MOBILE: string;
|
||||
WIFI: string;
|
||||
MOBILE_MMS: string;
|
||||
MOBILE_SUPL: string;
|
||||
MOBILE_DUN: string;
|
||||
MOBILE_HIPRI: string;
|
||||
WIMAX: string;
|
||||
BLUETOOTH: string;
|
||||
DUMMY: string;
|
||||
ETHERNET: string;
|
||||
MOBILE_FOTA: string;
|
||||
MOBILE_IMS: string;
|
||||
MOBILE_CBS: string;
|
||||
WIFI_P2P: string;
|
||||
MOBILE_IA: string;
|
||||
MOBILE_EMERGENCY: string;
|
||||
PROXY: string;
|
||||
VPN: string;
|
||||
UNKNOWN: string;
|
||||
}>;
|
||||
|
||||
/**
|
||||
* NetInfo exposes info about online/offline status
|
||||
*
|
||||
* ### reachabilityIOS
|
||||
* ```
|
||||
* NetInfo.fetch().done((reach) => {
|
||||
* console.log('Initial: ' + reach);
|
||||
* });
|
||||
* function handleFirstConnectivityChange(reach) {
|
||||
* console.log('First change: ' + reach);
|
||||
* NetInfo.removeEventListener(
|
||||
* 'change',
|
||||
* handleFirstConnectivityChange
|
||||
* );
|
||||
* }
|
||||
* NetInfo.addEventListener(
|
||||
* 'change',
|
||||
* handleFirstConnectivityChange
|
||||
* );
|
||||
* ```
|
||||
*
|
||||
* ### IOS
|
||||
*
|
||||
* Asynchronously determine if the device is online and on a cellular network.
|
||||
*
|
||||
|
@ -41,21 +86,35 @@ type ReachabilityStateIOS = $Enum<{
|
|||
* - `cell` - device is connected via Edge, 3G, WiMax, or LTE
|
||||
* - `unknown` - error case and the network status is unknown
|
||||
*
|
||||
* ```
|
||||
* NetInfo.reachabilityIOS.fetch().done((reach) => {
|
||||
* console.log('Initial: ' + reach);
|
||||
* ### Android
|
||||
*
|
||||
* Asynchronously determine if the device is connected and details about that connection.
|
||||
*
|
||||
* Android Connectivity Types
|
||||
* - `NONE` - device is offline
|
||||
* - `BLUETOOTH` - The Bluetooth data connection.
|
||||
* - `DUMMY` - Dummy data connection.
|
||||
* - `ETHERNET` - The Ethernet data connection.
|
||||
* - `MOBILE` - The Mobile data connection.
|
||||
* - `MOBILE_DUN` - A DUN-specific Mobile data connection.
|
||||
* - `MOBILE_HIPRI` - A High Priority Mobile data connection.
|
||||
* - `MOBILE_MMS` - An MMS-specific Mobile data connection.
|
||||
* - `MOBILE_SUPL` - A SUPL-specific Mobile data connection.
|
||||
* - `VPN` - A virtual network using one or more native bearers. Requires API Level 21
|
||||
* - `WIFI` - The WIFI data connection.
|
||||
* - `WIMAX` - The WiMAX data connection.
|
||||
* - `UNKNOWN` - Unknown data connection.
|
||||
* The rest ConnectivityStates are hidden by the Android API, but can be used if necessary.
|
||||
*
|
||||
* ### isConnectionMetered
|
||||
*
|
||||
* Available on Android. Detect if the current active connection is metered or not. A network is
|
||||
* classified as metered when the user is sensitive to heavy data usage on that connection due to
|
||||
* monetary costs, data limitations or battery/performance issues.
|
||||
*
|
||||
* NetInfo.isConnectionMetered((isConnectionMetered) => {
|
||||
* console.log('Connection is ' + (isConnectionMetered ? 'Metered' : 'Not Metered'));
|
||||
* });
|
||||
* function handleFirstReachabilityChange(reach) {
|
||||
* console.log('First change: ' + reach);
|
||||
* NetInfo.reachabilityIOS.removeEventListener(
|
||||
* 'change',
|
||||
* handleFirstReachabilityChange
|
||||
* );
|
||||
* }
|
||||
* NetInfo.reachabilityIOS.addEventListener(
|
||||
* 'change',
|
||||
* handleFirstReachabilityChange
|
||||
* );
|
||||
* ```
|
||||
*
|
||||
* ### isConnected
|
||||
|
@ -81,89 +140,101 @@ type ReachabilityStateIOS = $Enum<{
|
|||
* ```
|
||||
*/
|
||||
|
||||
var NetInfo = {};
|
||||
var _subscriptions = {};
|
||||
|
||||
if (RCTReachability) {
|
||||
|
||||
// RCTReachability is exposed, so this is an iOS-like environment and we will
|
||||
// expose reachabilityIOS
|
||||
|
||||
var _reachabilitySubscriptions = {};
|
||||
|
||||
NetInfo.reachabilityIOS = {
|
||||
addEventListener: function (
|
||||
eventName: ChangeEventName,
|
||||
handler: Function
|
||||
): void {
|
||||
_reachabilitySubscriptions[handler] = RCTDeviceEventEmitter.addListener(
|
||||
DEVICE_REACHABILITY_EVENT,
|
||||
(appStateData) => {
|
||||
handler(appStateData.network_reachability);
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
removeEventListener: function(
|
||||
eventName: ChangeEventName,
|
||||
handler: Function
|
||||
): void {
|
||||
if (!_reachabilitySubscriptions[handler]) {
|
||||
return;
|
||||
var NetInfo = {
|
||||
addEventListener: function (
|
||||
eventName: ChangeEventName,
|
||||
handler: Function
|
||||
): void {
|
||||
_subscriptions[handler] = RCTDeviceEventEmitter.addListener(
|
||||
DEVICE_REACHABILITY_EVENT,
|
||||
(appStateData) => {
|
||||
handler(appStateData.network_reachability);
|
||||
}
|
||||
_reachabilitySubscriptions[handler].remove();
|
||||
_reachabilitySubscriptions[handler] = null;
|
||||
},
|
||||
);
|
||||
},
|
||||
|
||||
fetch: function(): Promise {
|
||||
return new Promise((resolve, reject) => {
|
||||
RCTReachability.getCurrentReachability(
|
||||
function(resp) {
|
||||
resolve(resp.network_reachability);
|
||||
},
|
||||
reject
|
||||
);
|
||||
});
|
||||
},
|
||||
};
|
||||
removeEventListener: function(
|
||||
eventName: ChangeEventName,
|
||||
handler: Function
|
||||
): void {
|
||||
if (!_subscriptions[handler]) {
|
||||
return;
|
||||
}
|
||||
_subscriptions[handler].remove();
|
||||
_subscriptions[handler] = null;
|
||||
},
|
||||
|
||||
var _isConnectedSubscriptions = {};
|
||||
fetch: function(): Promise {
|
||||
return new Promise((resolve, reject) => {
|
||||
RCTNetInfo.getCurrentReachability(
|
||||
function(resp) {
|
||||
resolve(resp.network_reachability);
|
||||
},
|
||||
reject
|
||||
);
|
||||
});
|
||||
},
|
||||
|
||||
var _iosReachabilityIsConnected = function(
|
||||
isConnected: {},
|
||||
|
||||
isConnectionMetered: {},
|
||||
};
|
||||
|
||||
if (Platform.OS === 'ios') {
|
||||
var _isConnected = function(
|
||||
reachability: ReachabilityStateIOS
|
||||
): bool {
|
||||
return reachability !== 'none' &&
|
||||
reachability !== 'unknown';
|
||||
};
|
||||
} else if (Platform.OS === 'android') {
|
||||
var _isConnected = function(
|
||||
connectionType: ConnectivityStateAndroid
|
||||
): bool {
|
||||
return connectionType !== 'NONE' && connectionType !== 'UNKNOWN';
|
||||
};
|
||||
}
|
||||
|
||||
NetInfo.isConnected = {
|
||||
addEventListener: function (
|
||||
eventName: ChangeEventName,
|
||||
handler: Function
|
||||
): void {
|
||||
_isConnectedSubscriptions[handler] = (reachability) => {
|
||||
handler(_iosReachabilityIsConnected(reachability));
|
||||
};
|
||||
NetInfo.reachabilityIOS.addEventListener(
|
||||
eventName,
|
||||
_isConnectedSubscriptions[handler]
|
||||
);
|
||||
},
|
||||
var _isConnectedSubscriptions = {};
|
||||
|
||||
removeEventListener: function(
|
||||
eventName: ChangeEventName,
|
||||
handler: Function
|
||||
): void {
|
||||
NetInfo.reachabilityIOS.removeEventListener(
|
||||
eventName,
|
||||
_isConnectedSubscriptions[handler]
|
||||
);
|
||||
},
|
||||
NetInfo.isConnected = {
|
||||
addEventListener: function (
|
||||
eventName: ChangeEventName,
|
||||
handler: Function
|
||||
): void {
|
||||
_isConnectedSubscriptions[handler] = (connection) => {
|
||||
handler(_isConnected(connection));
|
||||
};
|
||||
NetInfo.addEventListener(
|
||||
eventName,
|
||||
_isConnectedSubscriptions[handler]
|
||||
);
|
||||
},
|
||||
|
||||
fetch: function(): Promise {
|
||||
return NetInfo.reachabilityIOS.fetch().then(
|
||||
(reachability) => _iosReachabilityIsConnected(reachability)
|
||||
);
|
||||
},
|
||||
removeEventListener: function(
|
||||
eventName: ChangeEventName,
|
||||
handler: Function
|
||||
): void {
|
||||
NetInfo.removeEventListener(
|
||||
eventName,
|
||||
_isConnectedSubscriptions[handler]
|
||||
);
|
||||
},
|
||||
|
||||
fetch: function(): Promise {
|
||||
return NetInfo.fetch().then(
|
||||
(connection) => _isConnected(connection)
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
if (Platform.OS === 'android') {
|
||||
NetInfo.isConnectionMetered = function(callback): void {
|
||||
RCTNetInfo.isConnectionMetered((_isMetered) => {
|
||||
callback(_isMetered);
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -24,7 +24,6 @@ RCT_EXPORT_MODULE()
|
|||
*/
|
||||
RCT_EXPORT_METHOD(queryData:(NSString *)queryType
|
||||
withQuery:(NSDictionary *)query
|
||||
queryHash:(__unused NSString *)queryHash
|
||||
responseSender:(RCTResponseSenderBlock)responseSender)
|
||||
{
|
||||
if ([queryType isEqualToString:@"http"]) {
|
||||
|
@ -39,34 +38,35 @@ RCT_EXPORT_METHOD(queryData:(NSString *)queryType
|
|||
// Build data task
|
||||
NSURLSessionDataTask *task = [[NSURLSession sharedSession] dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *connectionError) {
|
||||
|
||||
NSHTTPURLResponse *httpResponse = nil;
|
||||
if ([response isKindOfClass:[NSHTTPURLResponse class]]) {
|
||||
// Might be a local file request
|
||||
httpResponse = (NSHTTPURLResponse *)response;
|
||||
}
|
||||
|
||||
// Build response
|
||||
NSDictionary *responseJSON;
|
||||
NSArray *responseJSON;
|
||||
if (connectionError == nil) {
|
||||
NSStringEncoding encoding = NSUTF8StringEncoding;
|
||||
if (response.textEncodingName) {
|
||||
CFStringEncoding cfEncoding = CFStringConvertIANACharSetNameToEncoding((CFStringRef)response.textEncodingName);
|
||||
encoding = CFStringConvertEncodingToNSStringEncoding(cfEncoding);
|
||||
}
|
||||
NSHTTPURLResponse *httpResponse = nil;
|
||||
if ([response isKindOfClass:[NSHTTPURLResponse class]]) {
|
||||
// Might be a local file request
|
||||
httpResponse = (NSHTTPURLResponse *)response;
|
||||
}
|
||||
responseJSON = @{
|
||||
@"status": @([httpResponse statusCode] ?: 200),
|
||||
@"responseHeaders": [httpResponse allHeaderFields] ?: @{},
|
||||
@"responseText": [[NSString alloc] initWithData:data encoding:encoding] ?: @""
|
||||
};
|
||||
responseJSON = @[
|
||||
@(httpResponse.statusCode ?: 200),
|
||||
httpResponse.allHeaderFields ?: @{},
|
||||
[[NSString alloc] initWithData:data encoding:encoding] ?: @"",
|
||||
];
|
||||
} else {
|
||||
responseJSON = @{
|
||||
@"status": @0,
|
||||
@"responseHeaders": @{},
|
||||
@"responseText": [connectionError localizedDescription] ?: [NSNull null]
|
||||
};
|
||||
responseJSON = @[
|
||||
@(httpResponse.statusCode),
|
||||
httpResponse.allHeaderFields ?: @{},
|
||||
connectionError.localizedDescription ?: [NSNull null],
|
||||
];
|
||||
}
|
||||
|
||||
// Send response (won't be sent on same thread as caller)
|
||||
responseSender(@[RCTJSONStringify(responseJSON, NULL)]);
|
||||
responseSender(responseJSON);
|
||||
|
||||
}];
|
||||
|
||||
|
|
|
@ -13,8 +13,6 @@
|
|||
|
||||
var RCTDataManager = require('NativeModules').DataManager;
|
||||
|
||||
var crc32 = require('crc32');
|
||||
|
||||
var XMLHttpRequestBase = require('XMLHttpRequestBase');
|
||||
|
||||
class XMLHttpRequest extends XMLHttpRequestBase {
|
||||
|
@ -28,12 +26,7 @@ class XMLHttpRequest extends XMLHttpRequestBase {
|
|||
data: data,
|
||||
headers: headers,
|
||||
},
|
||||
// TODO: Do we need this? is it used anywhere?
|
||||
'h' + crc32(method + '|' + url + '|' + data),
|
||||
(result) => {
|
||||
result = JSON.parse(result);
|
||||
this.callback(result.status, result.responseHeaders, result.responseText);
|
||||
}
|
||||
this.callback.bind(this)
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -59,7 +59,7 @@ var InspectorOverlay = React.createClass({
|
|||
? 'flex-start'
|
||||
: 'flex-end';
|
||||
|
||||
content.push(<View style={[styles.frame, this.state.frame]} />);
|
||||
content.push(<View pointerEvents="none" style={[styles.frame, this.state.frame]} />);
|
||||
content.push(<ElementProperties hierarchy={this.state.hierarchy} />);
|
||||
}
|
||||
return (
|
||||
|
|
|
@ -28,7 +28,6 @@ extern NSString *const RCTReactTagAttributeName;
|
|||
@property (nonatomic, strong) UIColor *textBackgroundColor;
|
||||
@property (nonatomic, assign) NSWritingDirection writingDirection;
|
||||
|
||||
- (NSTextStorage *)buildTextStorageForWidth:(CGFloat)width;
|
||||
- (void)recomputeText;
|
||||
|
||||
@end
|
||||
|
|
|
@ -12,6 +12,8 @@
|
|||
#import "RCTConvert.h"
|
||||
#import "RCTLog.h"
|
||||
#import "RCTShadowRawText.h"
|
||||
#import "RCTSparseArray.h"
|
||||
#import "RCTText.h"
|
||||
#import "RCTUtils.h"
|
||||
|
||||
NSString *const RCTIsHighlightedAttributeName = @"IsHighlightedAttributeName";
|
||||
|
@ -19,6 +21,8 @@ NSString *const RCTReactTagAttributeName = @"ReactTagAttributeName";
|
|||
|
||||
@implementation RCTShadowText
|
||||
{
|
||||
NSTextStorage *_cachedTextStorage;
|
||||
CGFloat _cachedTextStorageWidth;
|
||||
NSAttributedString *_cachedAttributedString;
|
||||
CGFloat _effectiveLetterSpacing;
|
||||
}
|
||||
|
@ -50,8 +54,35 @@ static css_dim_t RCTMeasure(void *context, float width)
|
|||
return self;
|
||||
}
|
||||
|
||||
- (NSDictionary *)processUpdatedProperties:(NSMutableSet *)applierBlocks
|
||||
parentProperties:(NSDictionary *)parentProperties
|
||||
{
|
||||
parentProperties = [super processUpdatedProperties:applierBlocks
|
||||
parentProperties:parentProperties];
|
||||
|
||||
NSTextStorage *textStorage = [self buildTextStorageForWidth:self.frame.size.width];
|
||||
[applierBlocks addObject:^(RCTSparseArray *viewRegistry) {
|
||||
RCTText *view = viewRegistry[self.reactTag];
|
||||
view.textStorage = textStorage;
|
||||
}];
|
||||
|
||||
return parentProperties;
|
||||
}
|
||||
|
||||
- (void)applyLayoutNode:(css_node_t *)node
|
||||
viewsWithNewFrame:(NSMutableSet *)viewsWithNewFrame
|
||||
absolutePosition:(CGPoint)absolutePosition
|
||||
{
|
||||
[super applyLayoutNode:node viewsWithNewFrame:viewsWithNewFrame absolutePosition:absolutePosition];
|
||||
[self dirtyPropagation];
|
||||
}
|
||||
|
||||
- (NSTextStorage *)buildTextStorageForWidth:(CGFloat)width
|
||||
{
|
||||
if (_cachedTextStorage && width == _cachedTextStorageWidth) {
|
||||
return _cachedTextStorage;
|
||||
}
|
||||
|
||||
NSLayoutManager *layoutManager = [[NSLayoutManager alloc] init];
|
||||
|
||||
NSTextStorage *textStorage = [[NSTextStorage alloc] initWithAttributedString:self.attributedString];
|
||||
|
@ -69,13 +100,23 @@ static css_dim_t RCTMeasure(void *context, float width)
|
|||
[layoutManager addTextContainer:textContainer];
|
||||
[layoutManager ensureLayoutForTextContainer:textContainer];
|
||||
|
||||
_cachedTextStorage = textStorage;
|
||||
_cachedTextStorageWidth = width;
|
||||
|
||||
return textStorage;
|
||||
}
|
||||
|
||||
- (void)dirtyText
|
||||
{
|
||||
[super dirtyText];
|
||||
_cachedTextStorage = nil;
|
||||
}
|
||||
|
||||
- (void)recomputeText
|
||||
{
|
||||
[self attributedString];
|
||||
[self setTextComputed];
|
||||
[self dirtyPropagation];
|
||||
}
|
||||
|
||||
- (NSAttributedString *)attributedString
|
||||
|
|
|
@ -96,12 +96,10 @@ RCT_EXPORT_SHADOW_PROPERTY(numberOfLines, NSUInteger)
|
|||
{
|
||||
NSNumber *reactTag = shadowView.reactTag;
|
||||
UIEdgeInsets padding = shadowView.paddingAsInsets;
|
||||
NSTextStorage *textStorage = [shadowView buildTextStorageForWidth:shadowView.frame.size.width];
|
||||
|
||||
return ^(RCTUIManager *uiManager, RCTSparseArray *viewRegistry) {
|
||||
RCTText *text = viewRegistry[reactTag];
|
||||
text.contentInset = padding;
|
||||
text.textStorage = textStorage;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
/**
|
||||
* 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 BridgeProfiling
|
||||
* @flow
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
var GLOBAL = GLOBAL || this;
|
||||
|
||||
var BridgeProfiling = {
|
||||
profile(profileName: String, args?: any) {
|
||||
if (GLOBAL.__BridgeProfilingIsProfiling) {
|
||||
if (args) {
|
||||
try {
|
||||
args = JSON.stringify(args);
|
||||
} catch(err) {
|
||||
args = err.message;
|
||||
}
|
||||
}
|
||||
console.profile(profileName, args);
|
||||
}
|
||||
},
|
||||
|
||||
profileEnd() {
|
||||
if (GLOBAL.__BridgeProfilingIsProfiling) {
|
||||
console.profileEnd();
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = BridgeProfiling;
|
|
@ -29,6 +29,7 @@ if (dimensions && dimensions.windowPhysicalPixels) {
|
|||
width: windowPhysicalPixels.width / windowPhysicalPixels.scale,
|
||||
height: windowPhysicalPixels.height / windowPhysicalPixels.scale,
|
||||
scale: windowPhysicalPixels.scale,
|
||||
fontScale: windowPhysicalPixels.fontScale,
|
||||
};
|
||||
|
||||
// delete so no callers rely on this existing
|
||||
|
|
|
@ -22,6 +22,6 @@ var GLOBAL = this;
|
|||
*
|
||||
* However, we still want to treat ErrorUtils as a module so that other modules
|
||||
* that use it aren't just using a global variable, so simply export the global
|
||||
* variable here. ErrorUtils is original defined in a file named error-guard.js.
|
||||
* variable here. ErrorUtils is originally defined in a file named error-guard.js.
|
||||
*/
|
||||
module.exports = GLOBAL.ErrorUtils;
|
||||
|
|
|
@ -17,6 +17,7 @@ var ReactUpdates = require('ReactUpdates');
|
|||
var invariant = require('invariant');
|
||||
var warning = require('warning');
|
||||
|
||||
var BridgeProfiling = require('BridgeProfiling');
|
||||
var JSTimersExecution = require('JSTimersExecution');
|
||||
|
||||
var INTERNAL_ERROR = 'Error in MessageQueue implementation';
|
||||
|
@ -277,7 +278,9 @@ var MessageQueueMixin = {
|
|||
if (DEBUG_SPY_MODE) {
|
||||
console.log('N->JS: Callback#' + cbID + '(' + JSON.stringify(args) + ')');
|
||||
}
|
||||
BridgeProfiling.profile('Callback#' + cbID + '(' + JSON.stringify(args) + ')');
|
||||
cb.apply(scope, args);
|
||||
BridgeProfiling.profileEnd();
|
||||
} catch(ie_requires_catch) {
|
||||
throw ie_requires_catch;
|
||||
} finally {
|
||||
|
@ -311,7 +314,9 @@ var MessageQueueMixin = {
|
|||
'N->JS: ' + moduleName + '.' + methodName +
|
||||
'(' + JSON.stringify(params) + ')');
|
||||
}
|
||||
BridgeProfiling.profile(moduleName + '.' + methodName + '(' + JSON.stringify(params) + ')');
|
||||
var ret = jsCall(this._requireFunc(moduleName), methodName, params);
|
||||
BridgeProfiling.profileEnd();
|
||||
|
||||
return ret;
|
||||
},
|
||||
|
@ -330,7 +335,8 @@ var MessageQueueMixin = {
|
|||
|
||||
processBatch: function(batch) {
|
||||
var self = this;
|
||||
return guardReturn(function () {
|
||||
BridgeProfiling.profile('MessageQueue.processBatch()');
|
||||
var flushedQueue = guardReturn(function () {
|
||||
ReactUpdates.batchedUpdates(function() {
|
||||
batch.forEach(function(call) {
|
||||
invariant(
|
||||
|
@ -346,8 +352,12 @@ var MessageQueueMixin = {
|
|||
'Unrecognized method called on BatchedBridge: ' + call.method);
|
||||
}
|
||||
});
|
||||
BridgeProfiling.profile('React.batchedUpdates()');
|
||||
});
|
||||
BridgeProfiling.profileEnd();
|
||||
}, null, this._flushedQueueUnguarded, this);
|
||||
BridgeProfiling.profileEnd();
|
||||
return flushedQueue;
|
||||
},
|
||||
|
||||
setLoggingEnabled: function(enabled) {
|
||||
|
@ -472,8 +482,10 @@ var MessageQueueMixin = {
|
|||
},
|
||||
|
||||
_flushedQueueUnguarded: function() {
|
||||
// Call the functions registred via setImmediate
|
||||
BridgeProfiling.profile('JSTimersExecution.callImmediates()');
|
||||
// Call the functions registered via setImmediate
|
||||
JSTimersExecution.callImmediates();
|
||||
BridgeProfiling.profileEnd();
|
||||
|
||||
var currentOutgoingItems = this._outgoingItems;
|
||||
this._swapAndReinitializeBuffer();
|
||||
|
|
|
@ -59,6 +59,20 @@ class PixelRatio {
|
|||
return Dimensions.get('window').scale;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the scaling factor for font sizes. This is the ratio that is used to calculate the
|
||||
* absolute font size, so any elements that heavily depend on that should use this to do
|
||||
* calculations.
|
||||
*
|
||||
* If a font scale is not set, this returns the device pixel ratio.
|
||||
*
|
||||
* Currently this is only implemented on Android and reflects the user preference set in
|
||||
* Settings > Display > Font size, on iOS it will always return the default pixel ratio.
|
||||
*/
|
||||
static getFontScale(): number {
|
||||
return Dimensions.get('window').fontScale || PixelRatio.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a layout size (dp) to pixel size (px).
|
||||
*
|
||||
|
|
|
@ -1339,8 +1339,11 @@ RCT_INNER_BRIDGE_ONLY(_invokeAndProcessModule:(NSString *)module method:(NSStrin
|
|||
* AnyThread
|
||||
*/
|
||||
|
||||
RCTProfileBeginFlowEvent();
|
||||
|
||||
__weak RCTBatchedBridge *weakSelf = self;
|
||||
[_javaScriptExecutor executeBlockOnJavaScriptQueue:^{
|
||||
RCTProfileEndFlowEvent();
|
||||
RCTProfileBeginEvent();
|
||||
|
||||
RCTBatchedBridge *strongSelf = weakSelf;
|
||||
|
@ -1348,13 +1351,17 @@ RCT_INNER_BRIDGE_ONLY(_invokeAndProcessModule:(NSString *)module method:(NSStrin
|
|||
return;
|
||||
}
|
||||
|
||||
id call = @{
|
||||
@"module": module,
|
||||
@"method": method,
|
||||
@"args": args,
|
||||
@"context": context ?: @0,
|
||||
};
|
||||
|
||||
RCT_IF_DEV(NSNumber *callID = _RCTProfileBeginFlowEvent();)
|
||||
id call = @{
|
||||
@"js_args": @{
|
||||
@"module": module,
|
||||
@"method": method,
|
||||
@"args": args,
|
||||
},
|
||||
@"context": context ?: @0,
|
||||
RCT_IF_DEV(@"call_id": callID,)
|
||||
};
|
||||
if ([method isEqualToString:@"invokeCallbackAndReturnFlushedQueue"]) {
|
||||
strongSelf->_scheduledCallbacks[args[0]] = call;
|
||||
} else {
|
||||
|
@ -1490,8 +1497,10 @@ RCT_INNER_BRIDGE_ONLY(_invokeAndProcessModule:(NSString *)module method:(NSStrin
|
|||
return NO;
|
||||
}
|
||||
|
||||
RCTProfileBeginFlowEvent();
|
||||
__weak RCTBatchedBridge *weakSelf = self;
|
||||
[self dispatchBlock:^{
|
||||
RCTProfileEndFlowEvent();
|
||||
RCTProfileBeginEvent();
|
||||
RCTBatchedBridge *strongSelf = weakSelf;
|
||||
|
||||
|
@ -1525,16 +1534,18 @@ RCT_INNER_BRIDGE_ONLY(_invokeAndProcessModule:(NSString *)module method:(NSStrin
|
|||
- (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]) {
|
||||
RCT_IF_DEV(NSString *name = [NSString stringWithFormat:@"[%@ didUpdateFrame:%f]", observer, displayLink.timestamp];)
|
||||
RCTProfileBeginFlowEvent();
|
||||
[self dispatchBlock:^{
|
||||
RCTProfileEndFlowEvent();
|
||||
RCTProfileBeginEvent();
|
||||
[observer didUpdateFrame:frameUpdate];
|
||||
RCTProfileEndEvent(name, @"objc_call,fps", nil);
|
||||
} forModule:RCTModuleIDsByName[RCTBridgeModuleNameForClass([observer class])]];
|
||||
}
|
||||
}
|
||||
|
@ -1544,18 +1555,29 @@ RCT_INNER_BRIDGE_ONLY(_invokeAndProcessModule:(NSString *)module method:(NSStrin
|
|||
calls = [calls filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(NSDictionary *call, NSDictionary *bindings) {
|
||||
return [call[@"context"] isEqualToNumber:currentExecutorID];
|
||||
}]];
|
||||
|
||||
RCT_IF_DEV(
|
||||
RCTProfileImmediateEvent(@"JS Thread Tick", displayLink.timestamp, @"g");
|
||||
|
||||
for (NSDictionary *call in calls) {
|
||||
_RCTProfileEndFlowEvent(call[@"call_id"]);
|
||||
}
|
||||
)
|
||||
|
||||
if (calls.count > 0) {
|
||||
_scheduledCalls = [[NSMutableArray alloc] init];
|
||||
_scheduledCallbacks = [[RCTSparseArray alloc] init];
|
||||
[self _actuallyInvokeAndProcessModule:@"BatchedBridge"
|
||||
method:@"processBatch"
|
||||
arguments:@[calls]
|
||||
arguments:@[[calls valueForKey:@"js_args"]]
|
||||
context:RCTGetExecutorID(_javaScriptExecutor)];
|
||||
}
|
||||
|
||||
RCTProfileEndEvent(@"DispatchFrameUpdate", @"objc_call", nil);
|
||||
|
||||
[self.perfStats.jsGraph tick:displayLink.timestamp];
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[self.perfStats.jsGraph tick:displayLink.timestamp];
|
||||
});
|
||||
}
|
||||
|
||||
- (void)_mainThreadUpdate:(CADisplayLink *)displayLink
|
||||
|
|
|
@ -42,6 +42,12 @@
|
|||
#endif
|
||||
#endif
|
||||
|
||||
#if RCT_DEV
|
||||
#define RCT_IF_DEV(...) __VA_ARGS__
|
||||
#else
|
||||
#define RCT_IF_DEV(...)
|
||||
#endif
|
||||
|
||||
/**
|
||||
* By default, only raise an NSAssertion in debug mode
|
||||
* (custom assert functions will still be called).
|
||||
|
|
|
@ -11,11 +11,10 @@
|
|||
|
||||
#import "RCTAssert.h"
|
||||
#import "RCTBridge.h"
|
||||
#import "RCTSparseArray.h"
|
||||
|
||||
static uint64_t RCTGetEventID(id<RCTEvent> event)
|
||||
static NSNumber *RCTGetEventID(id<RCTEvent> event)
|
||||
{
|
||||
return (
|
||||
return @(
|
||||
[event.viewTag intValue] |
|
||||
(((uint64_t)event.eventName.hash & 0xFFFF) << 32) |
|
||||
(((uint64_t)event.coalescingKey) << 48)
|
||||
|
@ -68,7 +67,7 @@ static uint64_t RCTGetEventID(id<RCTEvent> event)
|
|||
|
||||
@implementation RCTEventDispatcher
|
||||
{
|
||||
RCTSparseArray *_eventQueue;
|
||||
NSMutableDictionary *_eventQueue;
|
||||
NSLock *_eventQueueLock;
|
||||
}
|
||||
|
||||
|
@ -79,7 +78,7 @@ RCT_EXPORT_MODULE()
|
|||
- (instancetype)init
|
||||
{
|
||||
if ((self = [super init])) {
|
||||
_eventQueue = [[RCTSparseArray alloc] init];
|
||||
_eventQueue = [[NSMutableDictionary alloc] init];
|
||||
_eventQueueLock = [[NSLock alloc] init];
|
||||
}
|
||||
return self;
|
||||
|
@ -139,7 +138,7 @@ RCT_IMPORT_METHOD(RCTEventEmitter, receiveEvent);
|
|||
|
||||
[_eventQueueLock lock];
|
||||
|
||||
uint64_t eventID = RCTGetEventID(event);
|
||||
NSNumber *eventID = RCTGetEventID(event);
|
||||
id<RCTEvent> previousEvent = _eventQueue[eventID];
|
||||
|
||||
if (previousEvent) {
|
||||
|
@ -176,14 +175,14 @@ RCT_IMPORT_METHOD(RCTEventEmitter, receiveEvent);
|
|||
|
||||
- (void)didUpdateFrame:(RCTFrameUpdate *)update
|
||||
{
|
||||
RCTSparseArray *eventQueue;
|
||||
NSDictionary *eventQueue;
|
||||
|
||||
[_eventQueueLock lock];
|
||||
eventQueue = _eventQueue;
|
||||
_eventQueue = [[RCTSparseArray alloc] init];
|
||||
_eventQueue = [[NSMutableDictionary alloc] init];
|
||||
[_eventQueueLock unlock];
|
||||
|
||||
for (id<RCTEvent> event in eventQueue.allObjects) {
|
||||
for (id<RCTEvent> event in eventQueue.allValues) {
|
||||
[self dispatchEvent:event];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,8 +20,23 @@
|
|||
* before before using it.
|
||||
*/
|
||||
|
||||
NSString *const RCTProfileDidStartProfiling;
|
||||
NSString *const RCTProfileDidEndProfiling;
|
||||
|
||||
#if RCT_DEV
|
||||
|
||||
#define RCTProfileBeginFlowEvent() \
|
||||
_Pragma("clang diagnostic push") \
|
||||
_Pragma("clang diagnostic ignored \"-Wshadow\"") \
|
||||
NSNumber *__rct_profile_flow_id = _RCTProfileBeginFlowEvent(); \
|
||||
_Pragma("clang diagnostic pop")
|
||||
|
||||
#define RCTProfileEndFlowEvent() \
|
||||
_RCTProfileEndFlowEvent(__rct_profile_flow_id)
|
||||
|
||||
RCT_EXTERN NSNumber *_RCTProfileBeginFlowEvent(void);
|
||||
RCT_EXTERN void _RCTProfileEndFlowEvent(NSNumber *);
|
||||
|
||||
/**
|
||||
* Returns YES if the profiling information is currently being collected
|
||||
*/
|
||||
|
@ -88,6 +103,12 @@ RCT_EXTERN void RCTProfileImmediateEvent(NSString *, NSTimeInterval , NSString *
|
|||
|
||||
#else
|
||||
|
||||
#define RCTProfileBeginFlowEvent()
|
||||
#define _RCTProfileBeginFlowEvent() @0
|
||||
|
||||
#define RCTProfileEndFlowEvent()
|
||||
#define _RCTProfileEndFlowEvent()
|
||||
|
||||
#define RCTProfileIsProfiling(...) NO
|
||||
#define RCTProfileInit(...)
|
||||
#define RCTProfileEnd(...) @""
|
||||
|
|
|
@ -17,6 +17,9 @@
|
|||
#import "RCTDefines.h"
|
||||
#import "RCTUtils.h"
|
||||
|
||||
NSString *const RCTProfileDidStartProfiling = @"RCTProfileDidStartProfiling";
|
||||
NSString *const RCTProfileDidEndProfiling = @"RCTProfileDidEndProfiling";
|
||||
|
||||
#if RCT_DEV
|
||||
|
||||
#pragma mark - Prototypes
|
||||
|
@ -113,10 +116,16 @@ void RCTProfileInit(void)
|
|||
RCTProfileSamples: [[NSMutableArray alloc] init],
|
||||
};
|
||||
);
|
||||
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:RCTProfileDidStartProfiling
|
||||
object:nil];
|
||||
}
|
||||
|
||||
NSString *RCTProfileEnd(void)
|
||||
{
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:RCTProfileDidEndProfiling
|
||||
object:nil];
|
||||
|
||||
RCTProfileLock(
|
||||
NSString *log = RCTJSONStringify(RCTProfileInfo, NULL);
|
||||
RCTProfileEventID = 0;
|
||||
|
@ -171,4 +180,32 @@ void RCTProfileImmediateEvent(NSString *name, NSTimeInterval timestamp, NSString
|
|||
);
|
||||
}
|
||||
|
||||
NSNumber *_RCTProfileBeginFlowEvent(void)
|
||||
{
|
||||
static NSUInteger flowID = 0;
|
||||
|
||||
CHECK(@0);
|
||||
RCTProfileAddEvent(RCTProfileTraceEvents,
|
||||
@"name": @"flow",
|
||||
@"id": @(++flowID),
|
||||
@"cat": @"flow",
|
||||
@"ph": @"s",
|
||||
@"ts": RCTProfileTimestamp(CACurrentMediaTime()),
|
||||
);
|
||||
|
||||
return @(flowID);
|
||||
}
|
||||
|
||||
void _RCTProfileEndFlowEvent(NSNumber *flowID)
|
||||
{
|
||||
CHECK();
|
||||
RCTProfileAddEvent(RCTProfileTraceEvents,
|
||||
@"name": @"flow",
|
||||
@"id": flowID,
|
||||
@"cat": @"flow",
|
||||
@"ph": @"f",
|
||||
@"ts": RCTProfileTimestamp(CACurrentMediaTime()),
|
||||
);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -133,22 +133,27 @@ static JSValueRef RCTConsoleProfile(JSContextRef context, JSObjectRef object, JS
|
|||
profileName = [NSString stringWithFormat:@"Profile %d", profileCounter++];
|
||||
}
|
||||
|
||||
[profiles addObjectsFromArray:@[profileName, profileID]];
|
||||
id profileInfo = [NSNull null];
|
||||
if (argumentCount > 1 && !JSValueIsUndefined(context, arguments[1])) {
|
||||
profileInfo = @[RCTJSValueToNSString(context, arguments[1])];
|
||||
}
|
||||
|
||||
[profiles addObjectsFromArray:@[profileName, profileID, profileInfo]];
|
||||
|
||||
RCTLog(@"Profile '%@' finished.", profileName);
|
||||
return JSValueMakeUndefined(context);
|
||||
}
|
||||
|
||||
static JSValueRef RCTConsoleProfileEnd(JSContextRef context, JSObjectRef object, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef *exception)
|
||||
{
|
||||
NSString *profileInfo = [profiles lastObject];
|
||||
[profiles removeLastObject];
|
||||
NSNumber *profileID = [profiles lastObject];
|
||||
[profiles removeLastObject];
|
||||
NSString *profileName = [profiles lastObject];
|
||||
[profiles removeLastObject];
|
||||
|
||||
_RCTProfileEndEvent(profileID, profileName, @"console", nil);
|
||||
_RCTProfileEndEvent(profileID, profileName, @"console", profileInfo);
|
||||
|
||||
RCTLog(@"Profile '%@' started.", profileName);
|
||||
return JSValueMakeUndefined(context);
|
||||
}
|
||||
|
||||
|
@ -244,6 +249,13 @@ static NSError *RCTNSErrorFromJSError(JSContextRef context, JSValueRef jsError)
|
|||
#if RCT_DEV
|
||||
[strongSelf _addNativeHook:RCTConsoleProfile withName:"consoleProfile"];
|
||||
[strongSelf _addNativeHook:RCTConsoleProfileEnd withName:"consoleProfileEnd"];
|
||||
|
||||
for (NSString *event in @[RCTProfileDidStartProfiling, RCTProfileDidEndProfiling]) {
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(toggleProfilingFlag:)
|
||||
name:event
|
||||
object:nil];
|
||||
}
|
||||
#endif
|
||||
|
||||
}];
|
||||
|
@ -252,6 +264,21 @@ static NSError *RCTNSErrorFromJSError(JSContextRef context, JSValueRef jsError)
|
|||
return self;
|
||||
}
|
||||
|
||||
- (void)toggleProfilingFlag:(NSNotification *)notification
|
||||
{
|
||||
JSObjectRef globalObject = JSContextGetGlobalObject(_context.ctx);
|
||||
|
||||
bool enabled = [notification.name isEqualToString:RCTProfileDidStartProfiling];
|
||||
JSStringRef JSName = JSStringCreateWithUTF8CString("__BridgeProfilingIsProfiling");
|
||||
JSObjectSetProperty(_context.ctx,
|
||||
globalObject,
|
||||
JSName,
|
||||
JSValueMakeBoolean(_context.ctx, enabled),
|
||||
kJSPropertyAttributeNone,
|
||||
NULL);
|
||||
JSStringRelease(JSName);
|
||||
}
|
||||
|
||||
- (void)_addNativeHook:(JSObjectCallAsFunctionCallback)hook withName:(const char *)name
|
||||
{
|
||||
JSObjectRef globalObject = JSContextGetGlobalObject(_context.ctx);
|
||||
|
@ -269,6 +296,10 @@ static NSError *RCTNSErrorFromJSError(JSContextRef context, JSValueRef jsError)
|
|||
|
||||
- (void)invalidate
|
||||
{
|
||||
#if RCT_DEV
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
#endif
|
||||
|
||||
[_context performSelector:@selector(invalidate) onThread:_javaScriptThread withObject:nil waitUntilDone:NO];
|
||||
}
|
||||
|
||||
|
|
|
@ -44,8 +44,7 @@ RCT_EXPORT_METHOD(reportSoftException:(NSString *)message
|
|||
[_delegate handleSoftJSExceptionWithMessage:message stack:stack];
|
||||
return;
|
||||
}
|
||||
|
||||
[[RCTRedBox sharedInstance] showErrorMessage:message withStack:stack];
|
||||
// JS already logs the error via console.
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(reportFatalException:(NSString *)message
|
||||
|
|
|
@ -481,6 +481,10 @@ static NSDictionary *RCTViewConfigForModule(Class managerClass, NSString *viewNa
|
|||
shadowView.newView = NO;
|
||||
}
|
||||
|
||||
// These are blocks to be executed on each view, immediately after
|
||||
// reactSetFrame: has been called. Note that if reactSetFrame: is not called,
|
||||
// these won't be called either, so this is not a suitable place to update
|
||||
// properties that aren't related to layout.
|
||||
NSMutableArray *updateBlocks = [[NSMutableArray alloc] init];
|
||||
for (RCTShadowView *shadowView in viewsWithNewFrames) {
|
||||
RCTViewManager *manager = _viewManagerRegistry[shadowView.reactTag];
|
||||
|
@ -917,6 +921,7 @@ RCT_EXPORT_METHOD(findSubviewIn:(NSNumber *)reactTag atPoint:(CGPoint)point call
|
|||
|
||||
- (void)batchDidComplete
|
||||
{
|
||||
RCTProfileBeginEvent();
|
||||
// Gather blocks to be executed now that all view hierarchy manipulations have
|
||||
// been completed (note that these may still take place before layout has finished)
|
||||
for (RCTViewManager *manager in _viewManagers.allValues) {
|
||||
|
@ -947,6 +952,9 @@ RCT_EXPORT_METHOD(findSubviewIn:(NSNumber *)reactTag atPoint:(CGPoint)point call
|
|||
_nextLayoutAnimation = nil;
|
||||
}
|
||||
|
||||
RCTProfileEndEvent(@"[RCTUIManager batchDidComplete]", @"uimanager", @{
|
||||
@"view_count": @([_viewRegistry count]),
|
||||
});
|
||||
[self flushUIBlocks];
|
||||
}
|
||||
|
||||
|
|
|
@ -20,8 +20,7 @@ typedef NS_ENUM(NSUInteger, RCTUpdateLifecycle) {
|
|||
RCTUpdateLifecycleDirtied,
|
||||
};
|
||||
|
||||
// TODO: is this redundact now?
|
||||
typedef void (^RCTApplierBlock)(RCTSparseArray *);
|
||||
typedef void (^RCTApplierBlock)(RCTSparseArray *viewRegistry);
|
||||
|
||||
/**
|
||||
* ShadowView tree mirrors RCT view tree. Every node is highly stateful.
|
||||
|
@ -117,34 +116,48 @@ typedef void (^RCTApplierBlock)(RCTSparseArray *);
|
|||
* The applierBlocks set contains RCTApplierBlock functions that must be applied
|
||||
* on the main thread in order to update the view.
|
||||
*/
|
||||
- (void)collectUpdatedProperties:(NSMutableSet *)applierBlocks parentProperties:(NSDictionary *)parentProperties;
|
||||
- (void)collectUpdatedProperties:(NSMutableSet *)applierBlocks
|
||||
parentProperties:(NSDictionary *)parentProperties;
|
||||
|
||||
/**
|
||||
* Process the updated properties and apply them to view. Shadow view classes
|
||||
* that add additional propagating properties should override this method.
|
||||
*/
|
||||
- (NSDictionary *)processUpdatedProperties:(NSMutableSet *)applierBlocks
|
||||
parentProperties:(NSDictionary *)parentProperties NS_REQUIRES_SUPER;
|
||||
|
||||
/**
|
||||
* Calculate all views whose frame needs updating after layout has been calculated.
|
||||
* The viewsWithNewFrame set contains the reactTags of the views that need updating.
|
||||
*/
|
||||
- (void)collectRootUpdatedFrames:(NSMutableSet *)viewsWithNewFrame parentConstraint:(CGSize)parentConstraint;
|
||||
- (void)collectRootUpdatedFrames:(NSMutableSet *)viewsWithNewFrame
|
||||
parentConstraint:(CGSize)parentConstraint;
|
||||
|
||||
/**
|
||||
* Recursively apply layout to children.
|
||||
*/
|
||||
- (void)applyLayoutNode:(css_node_t *)node
|
||||
viewsWithNewFrame:(NSMutableSet *)viewsWithNewFrame
|
||||
absolutePosition:(CGPoint)absolutePosition NS_REQUIRES_SUPER;
|
||||
|
||||
/**
|
||||
* The following are implementation details exposed to subclasses. Do not call them directly
|
||||
*/
|
||||
- (void)fillCSSNode:(css_node_t *)node;
|
||||
- (void)dirtyLayout;
|
||||
- (void)fillCSSNode:(css_node_t *)node NS_REQUIRES_SUPER;
|
||||
- (void)dirtyLayout NS_REQUIRES_SUPER;
|
||||
- (BOOL)isLayoutDirty;
|
||||
|
||||
// TODO: is this still needed?
|
||||
- (void)dirtyPropagation;
|
||||
- (void)dirtyPropagation NS_REQUIRES_SUPER;
|
||||
- (BOOL)isPropagationDirty;
|
||||
|
||||
// TODO: move this to text node?
|
||||
- (void)dirtyText;
|
||||
- (void)dirtyText NS_REQUIRES_SUPER;
|
||||
- (void)setTextComputed NS_REQUIRES_SUPER;
|
||||
- (BOOL)isTextDirty;
|
||||
- (void)setTextComputed;
|
||||
|
||||
/**
|
||||
* Triggers a recalculation of the shadow view's layout.
|
||||
*/
|
||||
- (void)updateLayout;
|
||||
- (void)updateLayout NS_REQUIRES_SUPER;
|
||||
|
||||
/**
|
||||
* Computes the recursive offset, meaning the sum of all descendant offsets -
|
||||
|
|
|
@ -120,7 +120,9 @@ static void RCTProcessMetaProps(const float metaProps[META_PROP_COUNT], float st
|
|||
// width = 213.5 - 106.5 = 107
|
||||
// You'll notice that this is the same width we calculated for the parent view because we've taken its position into account.
|
||||
|
||||
- (void)applyLayoutNode:(css_node_t *)node viewsWithNewFrame:(NSMutableSet *)viewsWithNewFrame absolutePosition:(CGPoint)absolutePosition
|
||||
- (void)applyLayoutNode:(css_node_t *)node
|
||||
viewsWithNewFrame:(NSMutableSet *)viewsWithNewFrame
|
||||
absolutePosition:(CGPoint)absolutePosition
|
||||
{
|
||||
if (!node->layout.should_update) {
|
||||
return;
|
||||
|
@ -161,12 +163,19 @@ static void RCTProcessMetaProps(const float metaProps[META_PROP_COUNT], float st
|
|||
|
||||
for (int i = 0; i < node->children_count; ++i) {
|
||||
RCTShadowView *child = (RCTShadowView *)_reactSubviews[i];
|
||||
[child applyLayoutNode:node->get_child(node->context, i) viewsWithNewFrame:viewsWithNewFrame absolutePosition:absolutePosition];
|
||||
[child applyLayoutNode:node->get_child(node->context, i)
|
||||
viewsWithNewFrame:viewsWithNewFrame
|
||||
absolutePosition:absolutePosition];
|
||||
}
|
||||
}
|
||||
|
||||
- (NSDictionary *)processBackgroundColor:(NSMutableSet *)applierBlocks parentProperties:(NSDictionary *)parentProperties
|
||||
- (NSDictionary *)processUpdatedProperties:(NSMutableSet *)applierBlocks
|
||||
parentProperties:(NSDictionary *)parentProperties
|
||||
{
|
||||
// TODO: we always refresh all propagated properties when propagation is
|
||||
// dirtied, but really we should track which properties have changed and
|
||||
// only update those.
|
||||
|
||||
if (!_backgroundColor) {
|
||||
UIColor *parentBackgroundColor = parentProperties[RCTBackgroundColorProp];
|
||||
if (parentBackgroundColor) {
|
||||
|
@ -190,14 +199,15 @@ static void RCTProcessMetaProps(const float metaProps[META_PROP_COUNT], float st
|
|||
return parentProperties;
|
||||
}
|
||||
|
||||
- (void)collectUpdatedProperties:(NSMutableSet *)applierBlocks parentProperties:(NSDictionary *)parentProperties
|
||||
- (void)collectUpdatedProperties:(NSMutableSet *)applierBlocks
|
||||
parentProperties:(NSDictionary *)parentProperties
|
||||
{
|
||||
if (_propagationLifecycle == RCTUpdateLifecycleComputed && [parentProperties isEqualToDictionary:_lastParentProperties]) {
|
||||
return;
|
||||
}
|
||||
_propagationLifecycle = RCTUpdateLifecycleComputed;
|
||||
_lastParentProperties = parentProperties;
|
||||
NSDictionary *nextProps = [self processBackgroundColor:applierBlocks parentProperties:parentProperties];
|
||||
NSDictionary *nextProps = [self processUpdatedProperties:applierBlocks parentProperties:parentProperties];
|
||||
for (RCTShadowView *child in _reactSubviews) {
|
||||
[child collectUpdatedProperties:applierBlocks parentProperties:nextProps];
|
||||
}
|
||||
|
@ -212,21 +222,19 @@ static void RCTProcessMetaProps(const float metaProps[META_PROP_COUNT], float st
|
|||
|
||||
- (CGRect)measureLayoutRelativeToAncestor:(RCTShadowView *)ancestor
|
||||
{
|
||||
CGFloat totalOffsetTop = 0.0;
|
||||
CGFloat totalOffsetLeft = 0.0;
|
||||
CGSize size = self.frame.size;
|
||||
CGPoint offset = CGPointZero;
|
||||
NSInteger depth = 30; // max depth to search
|
||||
RCTShadowView *shadowView = self;
|
||||
while (depth && shadowView && shadowView != ancestor) {
|
||||
totalOffsetTop += shadowView.frame.origin.y;
|
||||
totalOffsetLeft += shadowView.frame.origin.x;
|
||||
offset.x += shadowView.frame.origin.x;
|
||||
offset.y += shadowView.frame.origin.y;
|
||||
shadowView = shadowView->_superview;
|
||||
depth--;
|
||||
}
|
||||
if (ancestor != shadowView) {
|
||||
return CGRectNull;
|
||||
}
|
||||
return (CGRect){{totalOffsetLeft, totalOffsetTop}, size};
|
||||
return (CGRect){offset, self.frame.size};
|
||||
}
|
||||
|
||||
- (instancetype)init
|
||||
|
|
|
@ -277,7 +277,7 @@ static NSString *RCTRecursiveAccessibilityLabel(UIView *view)
|
|||
// View has cliping enabled, so we can easily test if it is partially
|
||||
// or completely within the clipRect, and mount or unmount it accordingly
|
||||
|
||||
if (CGRectIntersectsRect(clipRect, view.frame)) {
|
||||
if (!CGRectIsEmpty(CGRectIntersection(clipRect, view.frame))) {
|
||||
|
||||
// View is at least partially visible, so remount it if unmounted
|
||||
if (view.superview == nil) {
|
||||
|
|
|
@ -60,9 +60,9 @@
|
|||
"react-timer-mixin": "^0.13.1",
|
||||
"react-tools": "0.13.2",
|
||||
"rebound": "^0.0.12",
|
||||
"sane": "git://github.com/tadeuzagallo/sane.git#a029f8b04a",
|
||||
"sane": "tadeuzagallo/sane#a029f8b04a",
|
||||
"source-map": "0.1.31",
|
||||
"stacktrace-parser": "git://github.com/frantic/stacktrace-parser.git#493c5e5638",
|
||||
"stacktrace-parser": "frantic/stacktrace-parser#493c5e5638",
|
||||
"uglify-js": "~2.4.16",
|
||||
"underscore": "1.7.0",
|
||||
"worker-farm": "^1.3.1",
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
var exec = require('child_process').exec;
|
||||
var execFile = require('child_process').execFile;
|
||||
var http = require('http');
|
||||
|
||||
var getFlowTypeCheckMiddleware = require('./getFlowTypeCheckMiddleware');
|
||||
|
@ -172,7 +172,7 @@ function getDevToolsLauncher(options) {
|
|||
var debuggerURL = 'http://localhost:' + options.port + '/debugger-ui';
|
||||
var script = 'launchChromeDevTools.applescript';
|
||||
console.log('Launching Dev Tools...');
|
||||
exec(path.join(__dirname, script) + ' ' + debuggerURL, function(err, stdout, stderr) {
|
||||
execFile(path.join(__dirname, script), [debuggerURL], function(err, stdout, stderr) {
|
||||
if (err) {
|
||||
console.log('Failed to run ' + script, err);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue