Updates from Tue 8 Sep
|
@ -36,7 +36,7 @@
|
||||||
* on the same Wi-Fi network.
|
* on the same Wi-Fi network.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
jsCodeLocation = [NSURL URLWithString:@"http://localhost:8081/Examples/2048/Game2048.bundle?platform=ios"];
|
jsCodeLocation = [NSURL URLWithString:@"http://localhost:8081/Examples/2048/Game2048.bundle?platform=ios&dev=true"];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* OPTION 2
|
* OPTION 2
|
||||||
|
|
|
@ -216,7 +216,7 @@
|
||||||
83CBB9F71A601CBA00E9B192 /* Project object */ = {
|
83CBB9F71A601CBA00E9B192 /* Project object */ = {
|
||||||
isa = PBXProject;
|
isa = PBXProject;
|
||||||
attributes = {
|
attributes = {
|
||||||
LastUpgradeCheck = 0610;
|
LastUpgradeCheck = 0700;
|
||||||
ORGANIZATIONNAME = Facebook;
|
ORGANIZATIONNAME = Facebook;
|
||||||
};
|
};
|
||||||
buildConfigurationList = 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "Movies" */;
|
buildConfigurationList = 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "Movies" */;
|
||||||
|
@ -358,6 +358,7 @@
|
||||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||||
LIBRARY_SEARCH_PATHS = "$(inherited)";
|
LIBRARY_SEARCH_PATHS = "$(inherited)";
|
||||||
OTHER_LDFLAGS = "-ObjC";
|
OTHER_LDFLAGS = "-ObjC";
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = "com.facebook.$(PRODUCT_NAME:rfc1034identifier)";
|
||||||
PRODUCT_NAME = Movies;
|
PRODUCT_NAME = Movies;
|
||||||
USER_HEADER_SEARCH_PATHS = "$(SRCROOT)/../../Libraries/**";
|
USER_HEADER_SEARCH_PATHS = "$(SRCROOT)/../../Libraries/**";
|
||||||
};
|
};
|
||||||
|
@ -376,6 +377,7 @@
|
||||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||||
LIBRARY_SEARCH_PATHS = "$(inherited)";
|
LIBRARY_SEARCH_PATHS = "$(inherited)";
|
||||||
OTHER_LDFLAGS = "-ObjC";
|
OTHER_LDFLAGS = "-ObjC";
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = "com.facebook.$(PRODUCT_NAME:rfc1034identifier)";
|
||||||
PRODUCT_NAME = Movies;
|
PRODUCT_NAME = Movies;
|
||||||
USER_HEADER_SEARCH_PATHS = "$(SRCROOT)/../../Libraries/**";
|
USER_HEADER_SEARCH_PATHS = "$(SRCROOT)/../../Libraries/**";
|
||||||
};
|
};
|
||||||
|
@ -401,6 +403,7 @@
|
||||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||||
COPY_PHASE_STRIP = NO;
|
COPY_PHASE_STRIP = NO;
|
||||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||||
|
ENABLE_TESTABILITY = YES;
|
||||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||||
GCC_DYNAMIC_NO_PIC = NO;
|
GCC_DYNAMIC_NO_PIC = NO;
|
||||||
GCC_OPTIMIZATION_LEVEL = 0;
|
GCC_OPTIMIZATION_LEVEL = 0;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<Scheme
|
<Scheme
|
||||||
LastUpgradeVersion = "0610"
|
LastUpgradeVersion = "0700"
|
||||||
version = "1.3">
|
version = "1.3">
|
||||||
<BuildAction
|
<BuildAction
|
||||||
parallelizeBuildables = "YES"
|
parallelizeBuildables = "YES"
|
||||||
|
@ -38,6 +38,8 @@
|
||||||
ReferencedContainer = "container:Movies.xcodeproj">
|
ReferencedContainer = "container:Movies.xcodeproj">
|
||||||
</BuildableReference>
|
</BuildableReference>
|
||||||
</MacroExpansion>
|
</MacroExpansion>
|
||||||
|
<AdditionalOptions>
|
||||||
|
</AdditionalOptions>
|
||||||
</TestAction>
|
</TestAction>
|
||||||
<LaunchAction
|
<LaunchAction
|
||||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
|
@ -47,8 +49,10 @@
|
||||||
buildConfiguration = "Debug"
|
buildConfiguration = "Debug"
|
||||||
ignoresPersistentStateOnLaunch = "NO"
|
ignoresPersistentStateOnLaunch = "NO"
|
||||||
debugDocumentVersioning = "YES"
|
debugDocumentVersioning = "YES"
|
||||||
|
debugServiceExtension = "internal"
|
||||||
allowLocationSimulation = "YES">
|
allowLocationSimulation = "YES">
|
||||||
<BuildableProductRunnable>
|
<BuildableProductRunnable
|
||||||
|
runnableDebuggingMode = "0">
|
||||||
<BuildableReference
|
<BuildableReference
|
||||||
BuildableIdentifier = "primary"
|
BuildableIdentifier = "primary"
|
||||||
BlueprintIdentifier = "13B07F861A680F5B00A75B9A"
|
BlueprintIdentifier = "13B07F861A680F5B00A75B9A"
|
||||||
|
@ -66,7 +70,8 @@
|
||||||
useCustomWorkingDirectory = "NO"
|
useCustomWorkingDirectory = "NO"
|
||||||
buildConfiguration = "Release"
|
buildConfiguration = "Release"
|
||||||
debugDocumentVersioning = "YES">
|
debugDocumentVersioning = "YES">
|
||||||
<BuildableProductRunnable>
|
<BuildableProductRunnable
|
||||||
|
runnableDebuggingMode = "0">
|
||||||
<BuildableReference
|
<BuildableReference
|
||||||
BuildableIdentifier = "primary"
|
BuildableIdentifier = "primary"
|
||||||
BlueprintIdentifier = "13B07F861A680F5B00A75B9A"
|
BlueprintIdentifier = "13B07F861A680F5B00A75B9A"
|
||||||
|
|
|
@ -37,7 +37,7 @@
|
||||||
* on the same Wi-Fi network.
|
* on the same Wi-Fi network.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
jsCodeLocation = [NSURL URLWithString:@"http://localhost:8081/Examples/Movies/MoviesApp.includeRequire.runModule.bundle"];
|
jsCodeLocation = [NSURL URLWithString:@"http://localhost:8081/Examples/Movies/MoviesApp.ios.bundle?platform=ios&dev=true"];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* OPTION 2
|
* OPTION 2
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
<key>CFBundleExecutable</key>
|
<key>CFBundleExecutable</key>
|
||||||
<string>$(EXECUTABLE_NAME)</string>
|
<string>$(EXECUTABLE_NAME)</string>
|
||||||
<key>CFBundleIdentifier</key>
|
<key>CFBundleIdentifier</key>
|
||||||
<string>com.facebook.$(PRODUCT_NAME:rfc1034identifier)</string>
|
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||||
<key>CFBundleInfoDictionaryVersion</key>
|
<key>CFBundleInfoDictionaryVersion</key>
|
||||||
<string>6.0</string>
|
<string>6.0</string>
|
||||||
<key>CFBundleName</key>
|
<key>CFBundleName</key>
|
||||||
|
@ -33,6 +33,11 @@
|
||||||
<string>1</string>
|
<string>1</string>
|
||||||
<key>LSRequiresIPhoneOS</key>
|
<key>LSRequiresIPhoneOS</key>
|
||||||
<true/>
|
<true/>
|
||||||
|
<key>NSAppTransportSecurity</key>
|
||||||
|
<dict>
|
||||||
|
<key>NSAllowsArbitraryLoads</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
<key>UILaunchStoryboardName</key>
|
<key>UILaunchStoryboardName</key>
|
||||||
<string>LaunchScreen</string>
|
<string>LaunchScreen</string>
|
||||||
<key>UIRequiredDeviceCapabilities</key>
|
<key>UIRequiredDeviceCapabilities</key>
|
||||||
|
@ -47,11 +52,5 @@
|
||||||
</array>
|
</array>
|
||||||
<key>UIViewControllerBasedStatusBarAppearance</key>
|
<key>UIViewControllerBasedStatusBarAppearance</key>
|
||||||
<false/>
|
<false/>
|
||||||
<key>NSAppTransportSecurity</key>
|
|
||||||
<dict>
|
|
||||||
<!--See http://ste.vn/2015/06/10/configuring-app-transport-security-ios-9-osx-10-11/-->
|
|
||||||
<key>NSAllowsArbitraryLoads</key>
|
|
||||||
<true/>
|
|
||||||
</dict>
|
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|
|
@ -22,7 +22,9 @@ var {
|
||||||
|
|
||||||
var MAX_VALUE = 200;
|
var MAX_VALUE = 200;
|
||||||
|
|
||||||
function getStyleFromScore(score: number): {color: string} {
|
import type { StyleObj } from 'StyleSheetTypes';
|
||||||
|
|
||||||
|
function getStyleFromScore(score: number): StyleObj {
|
||||||
if (score < 0) {
|
if (score < 0) {
|
||||||
return styles.noScore;
|
return styles.noScore;
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,7 +31,7 @@
|
||||||
* on the same Wi-Fi network.
|
* on the same Wi-Fi network.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
jsCodeLocation = [NSURL URLWithString:@"http://localhost:8081/Examples/SampleApp/index.ios.bundle"];
|
jsCodeLocation = [NSURL URLWithString:@"http://localhost:8081/Examples/SampleApp/index.ios.bundle?platform=ios&dev=true"];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* OPTION 2
|
* OPTION 2
|
||||||
|
|
|
@ -36,7 +36,7 @@
|
||||||
* on the same Wi-Fi network.
|
* on the same Wi-Fi network.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
jsCodeLocation = [NSURL URLWithString:@"http://localhost:8081/Examples/TicTacToe/TicTacToeApp.bundle?platform=ios"];
|
jsCodeLocation = [NSURL URLWithString:@"http://localhost:8081/Examples/TicTacToe/TicTacToeApp.bundle?platform=ios&dev=true"];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* OPTION 2
|
* OPTION 2
|
||||||
|
|
|
@ -0,0 +1,230 @@
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* @flow
|
||||||
|
*/
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var React = require('react-native');
|
||||||
|
var {
|
||||||
|
Animated,
|
||||||
|
Easing,
|
||||||
|
StyleSheet,
|
||||||
|
Text,
|
||||||
|
View,
|
||||||
|
} = React;
|
||||||
|
var UIExplorerButton = require('./UIExplorerButton');
|
||||||
|
|
||||||
|
exports.framework = 'React';
|
||||||
|
exports.title = 'Animated - Examples';
|
||||||
|
exports.description = 'Animated provides a powerful ' +
|
||||||
|
'and easy-to-use API for building modern, ' +
|
||||||
|
'interactive user experiences.';
|
||||||
|
|
||||||
|
exports.examples = [
|
||||||
|
{
|
||||||
|
title: 'FadeInView',
|
||||||
|
description: 'Uses a simple timing animation to ' +
|
||||||
|
'bring opacity from 0 to 1 when the component ' +
|
||||||
|
'mounts.',
|
||||||
|
render: function() {
|
||||||
|
class FadeInView extends React.Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.state = {
|
||||||
|
fadeAnim: new Animated.Value(0), // opacity 0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
componentDidMount() {
|
||||||
|
Animated.timing( // Uses easing functions
|
||||||
|
this.state.fadeAnim, // The value to drive
|
||||||
|
{
|
||||||
|
toValue: 1, // Target
|
||||||
|
duration: 2000, // Configuration
|
||||||
|
},
|
||||||
|
).start(); // Don't forget start!
|
||||||
|
}
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<Animated.View // Special animatable View
|
||||||
|
style={{
|
||||||
|
opacity: this.state.fadeAnim, // Binds
|
||||||
|
}}>
|
||||||
|
{this.props.children}
|
||||||
|
</Animated.View>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
class FadeInExample extends React.Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.state = {
|
||||||
|
show: true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<View>
|
||||||
|
<UIExplorerButton onPress={() => {
|
||||||
|
this.setState((state) => (
|
||||||
|
{show: !state.show}
|
||||||
|
));
|
||||||
|
}}>
|
||||||
|
Press to {this.state.show ?
|
||||||
|
'Hide' : 'Show'}
|
||||||
|
</UIExplorerButton>
|
||||||
|
{this.state.show && <FadeInView>
|
||||||
|
<View style={styles.content}>
|
||||||
|
<Text>FadeInView</Text>
|
||||||
|
</View>
|
||||||
|
</FadeInView>}
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return <FadeInExample />;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Transform Bounce',
|
||||||
|
description: 'One `Animated.Value` is driven by a ' +
|
||||||
|
'spring with custom constants and mapped to an ' +
|
||||||
|
'ordered set of transforms. Each transform has ' +
|
||||||
|
'an interpolation to convert the value into the ' +
|
||||||
|
'right range and units.',
|
||||||
|
render: function() {
|
||||||
|
this.anim = this.anim || new Animated.Value(0);
|
||||||
|
return (
|
||||||
|
<View>
|
||||||
|
<UIExplorerButton onPress={() => {
|
||||||
|
Animated.spring(this.anim, {
|
||||||
|
toValue: 0, // Returns to the start
|
||||||
|
velocity: 3, // Velocity makes it move
|
||||||
|
tension: -10, // Slow
|
||||||
|
friction: 1, // Oscillate a lot
|
||||||
|
}).start(); }}>
|
||||||
|
Press to Fling it!
|
||||||
|
</UIExplorerButton>
|
||||||
|
<Animated.View
|
||||||
|
style={[styles.content, {
|
||||||
|
transform: [ // Array order matters
|
||||||
|
{scale: this.anim.interpolate({
|
||||||
|
inputRange: [0, 1],
|
||||||
|
outputRange: [1, 4],
|
||||||
|
})},
|
||||||
|
{translateX: this.anim.interpolate({
|
||||||
|
inputRange: [0, 1],
|
||||||
|
outputRange: [0, 500],
|
||||||
|
})},
|
||||||
|
{rotate: this.anim.interpolate({
|
||||||
|
inputRange: [0, 1],
|
||||||
|
outputRange: [
|
||||||
|
'0deg', '360deg' // 'deg' or 'rad'
|
||||||
|
],
|
||||||
|
})},
|
||||||
|
]}
|
||||||
|
]}>
|
||||||
|
<Text>Transforms!</Text>
|
||||||
|
</Animated.View>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Composite Animations with Easing',
|
||||||
|
description: 'Sequence, parallel, delay, and ' +
|
||||||
|
'stagger with different easing functions.',
|
||||||
|
render: function() {
|
||||||
|
this.anims = this.anims || [1,2,3].map(
|
||||||
|
() => new Animated.Value(0)
|
||||||
|
);
|
||||||
|
return (
|
||||||
|
<View>
|
||||||
|
<UIExplorerButton onPress={() => {
|
||||||
|
var timing = Animated.timing;
|
||||||
|
Animated.sequence([ // One after the other
|
||||||
|
timing(this.anims[0], {
|
||||||
|
toValue: 200,
|
||||||
|
easing: Easing.linear,
|
||||||
|
}),
|
||||||
|
Animated.delay(400), // Use with sequence
|
||||||
|
timing(this.anims[0], {
|
||||||
|
toValue: 0,
|
||||||
|
easing: Easing.elastic(2), // Springy
|
||||||
|
}),
|
||||||
|
Animated.delay(400),
|
||||||
|
Animated.stagger(200,
|
||||||
|
this.anims.map((anim) => timing(
|
||||||
|
anim, {toValue: 200}
|
||||||
|
)).concat(
|
||||||
|
this.anims.map((anim) => timing(
|
||||||
|
anim, {toValue: 0}
|
||||||
|
))),
|
||||||
|
),
|
||||||
|
Animated.delay(400),
|
||||||
|
Animated.parallel([
|
||||||
|
Easing.inOut(Easing.quad), // Symmetric
|
||||||
|
Easing.back(1.5), // Goes backwards first
|
||||||
|
Easing.ease // Default bezier
|
||||||
|
].map((easing, ii) => (
|
||||||
|
timing(this.anims[ii], {
|
||||||
|
toValue: 320, easing, duration: 3000,
|
||||||
|
})
|
||||||
|
))),
|
||||||
|
Animated.delay(400),
|
||||||
|
Animated.stagger(200,
|
||||||
|
this.anims.map((anim) => timing(anim, {
|
||||||
|
toValue: 0,
|
||||||
|
easing: Easing.bounce, // Like a ball
|
||||||
|
duration: 2000,
|
||||||
|
})),
|
||||||
|
),
|
||||||
|
]).start(); }}>
|
||||||
|
Press to Animate
|
||||||
|
</UIExplorerButton>
|
||||||
|
{['Composite', 'Easing', 'Animations!'].map(
|
||||||
|
(text, ii) => (
|
||||||
|
<Animated.View
|
||||||
|
style={[styles.content, {
|
||||||
|
left: this.anims[ii]
|
||||||
|
}]}>
|
||||||
|
<Text>{text}</Text>
|
||||||
|
</Animated.View>
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Continuous Interactions',
|
||||||
|
description: 'Gesture events, chaining, 2D ' +
|
||||||
|
'values, interrupting and transitioning ' +
|
||||||
|
'animations, etc.',
|
||||||
|
render: () => (
|
||||||
|
<Text>Checkout the Gratuitous Animation App!</Text>
|
||||||
|
),
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
var styles = StyleSheet.create({
|
||||||
|
content: {
|
||||||
|
backgroundColor: 'deepskyblue',
|
||||||
|
borderWidth: 1,
|
||||||
|
borderColor: 'dodgerblue',
|
||||||
|
padding: 20,
|
||||||
|
margin: 20,
|
||||||
|
borderRadius: 10,
|
||||||
|
alignItems: 'center',
|
||||||
|
},
|
||||||
|
});
|
|
@ -325,6 +325,18 @@ exports.examples = [
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: 'Animated GIF',
|
||||||
|
render: function() {
|
||||||
|
return (
|
||||||
|
<Image
|
||||||
|
style={styles.gif}
|
||||||
|
source={{uri: 'http://38.media.tumblr.com/9e9bd08c6e2d10561dd1fb4197df4c4e/tumblr_mfqekpMktw1rn90umo1_500.gif'}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
platform: 'ios',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: 'Cap Insets',
|
title: 'Cap Insets',
|
||||||
description:
|
description:
|
||||||
|
@ -384,5 +396,9 @@ var styles = StyleSheet.create({
|
||||||
},
|
},
|
||||||
horizontal: {
|
horizontal: {
|
||||||
flexDirection: 'row',
|
flexDirection: 'row',
|
||||||
}
|
},
|
||||||
|
gif: {
|
||||||
|
flex: 1,
|
||||||
|
height: 200,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -18,27 +18,12 @@
|
||||||
var React = require('react-native');
|
var React = require('react-native');
|
||||||
var {
|
var {
|
||||||
AlertIOS,
|
AlertIOS,
|
||||||
StyleSheet,
|
|
||||||
Text,
|
Text,
|
||||||
TouchableHighlight,
|
TouchableHighlight,
|
||||||
View,
|
View,
|
||||||
} = React;
|
} = React;
|
||||||
var TimerMixin = require('react-timer-mixin');
|
var TimerMixin = require('react-timer-mixin');
|
||||||
|
var UIExplorerButton = require('./UIExplorerButton');
|
||||||
var Button = React.createClass({
|
|
||||||
render: function() {
|
|
||||||
return (
|
|
||||||
<TouchableHighlight
|
|
||||||
onPress={this.props.onPress}
|
|
||||||
style={styles.button}
|
|
||||||
underlayColor="#eeeeee">
|
|
||||||
<Text>
|
|
||||||
{this.props.children}
|
|
||||||
</Text>
|
|
||||||
</TouchableHighlight>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
var TimerTester = React.createClass({
|
var TimerTester = React.createClass({
|
||||||
mixins: [TimerMixin],
|
mixins: [TimerMixin],
|
||||||
|
@ -52,9 +37,9 @@ var TimerTester = React.createClass({
|
||||||
render: function() {
|
render: function() {
|
||||||
var args = 'fn' + (this.props.dt !== undefined ? ', ' + this.props.dt : '');
|
var args = 'fn' + (this.props.dt !== undefined ? ', ' + this.props.dt : '');
|
||||||
return (
|
return (
|
||||||
<Button onPress={this._run}>
|
<UIExplorerButton onPress={this._run}>
|
||||||
Measure: {this.props.type}({args}) - {this._ii || 0}
|
Measure: {this.props.type}({args}) - {this._ii || 0}
|
||||||
</Button>
|
</UIExplorerButton>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -112,18 +97,6 @@ var TimerTester = React.createClass({
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
var styles = StyleSheet.create({
|
|
||||||
button: {
|
|
||||||
borderColor: 'gray',
|
|
||||||
borderRadius: 8,
|
|
||||||
borderWidth: 1,
|
|
||||||
padding: 10,
|
|
||||||
margin: 5,
|
|
||||||
alignItems: 'center',
|
|
||||||
justifyContent: 'center',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
exports.framework = 'React';
|
exports.framework = 'React';
|
||||||
exports.title = 'Timers, TimerMixin';
|
exports.title = 'Timers, TimerMixin';
|
||||||
exports.description = 'The TimerMixin provides timer functions for executing ' +
|
exports.description = 'The TimerMixin provides timer functions for executing ' +
|
||||||
|
@ -183,9 +156,9 @@ exports.examples = [
|
||||||
if (this.state.showTimer) {
|
if (this.state.showTimer) {
|
||||||
var timer = [
|
var timer = [
|
||||||
<TimerTester ref="interval" dt={25} type="setInterval" />,
|
<TimerTester ref="interval" dt={25} type="setInterval" />,
|
||||||
<Button onPress={() => this.refs.interval.clear() }>
|
<UIExplorerButton onPress={() => this.refs.interval.clear() }>
|
||||||
Clear interval
|
Clear interval
|
||||||
</Button>
|
</UIExplorerButton>
|
||||||
];
|
];
|
||||||
var toggleText = 'Unmount timer';
|
var toggleText = 'Unmount timer';
|
||||||
} else {
|
} else {
|
||||||
|
@ -195,9 +168,9 @@ exports.examples = [
|
||||||
return (
|
return (
|
||||||
<View>
|
<View>
|
||||||
{timer}
|
{timer}
|
||||||
<Button onPress={this._toggleTimer}>
|
<UIExplorerButton onPress={this._toggleTimer}>
|
||||||
{toggleText}
|
{toggleText}
|
||||||
</Button>
|
</UIExplorerButton>
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
134A8A2A1AACED7A00945AAE /* libRCTGeolocation.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 134A8A251AACED6A00945AAE /* libRCTGeolocation.a */; };
|
134A8A2A1AACED7A00945AAE /* libRCTGeolocation.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 134A8A251AACED6A00945AAE /* libRCTGeolocation.a */; };
|
||||||
138D6A171B53CD440074A87E /* RCTCacheTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 138D6A151B53CD440074A87E /* RCTCacheTests.m */; };
|
138D6A171B53CD440074A87E /* RCTCacheTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 138D6A151B53CD440074A87E /* RCTCacheTests.m */; };
|
||||||
138D6A181B53CD440074A87E /* RCTShadowViewTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 138D6A161B53CD440074A87E /* RCTShadowViewTests.m */; };
|
138D6A181B53CD440074A87E /* RCTShadowViewTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 138D6A161B53CD440074A87E /* RCTShadowViewTests.m */; };
|
||||||
|
138DEE241B9EDFB6007F4EA5 /* libRCTCameraRoll.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 138DEE091B9EDDDB007F4EA5 /* libRCTCameraRoll.a */; };
|
||||||
1393D0381B68CD1300E1B601 /* RCTModuleMethodTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 1393D0371B68CD1300E1B601 /* RCTModuleMethodTests.m */; };
|
1393D0381B68CD1300E1B601 /* RCTModuleMethodTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 1393D0371B68CD1300E1B601 /* RCTModuleMethodTests.m */; };
|
||||||
139FDEDB1B0651FB00C62182 /* libRCTWebSocket.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 139FDED91B0651EA00C62182 /* libRCTWebSocket.a */; };
|
139FDEDB1B0651FB00C62182 /* libRCTWebSocket.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 139FDED91B0651EA00C62182 /* libRCTWebSocket.a */; };
|
||||||
13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.m */; };
|
13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.m */; };
|
||||||
|
@ -96,6 +97,13 @@
|
||||||
remoteGlobalIDString = 134814201AA4EA6300B7C361;
|
remoteGlobalIDString = 134814201AA4EA6300B7C361;
|
||||||
remoteInfo = RCTGeolocation;
|
remoteInfo = RCTGeolocation;
|
||||||
};
|
};
|
||||||
|
138DEE081B9EDDDB007F4EA5 /* PBXContainerItemProxy */ = {
|
||||||
|
isa = PBXContainerItemProxy;
|
||||||
|
containerPortal = 138DEE021B9EDDDB007F4EA5 /* RCTCameraRoll.xcodeproj */;
|
||||||
|
proxyType = 2;
|
||||||
|
remoteGlobalIDString = 58B5115D1A9E6B3D00147676;
|
||||||
|
remoteInfo = RCTImage;
|
||||||
|
};
|
||||||
139FDED81B0651EA00C62182 /* PBXContainerItemProxy */ = {
|
139FDED81B0651EA00C62182 /* PBXContainerItemProxy */ = {
|
||||||
isa = PBXContainerItemProxy;
|
isa = PBXContainerItemProxy;
|
||||||
containerPortal = 139FDECA1B0651EA00C62182 /* RCTWebSocket.xcodeproj */;
|
containerPortal = 139FDECA1B0651EA00C62182 /* RCTWebSocket.xcodeproj */;
|
||||||
|
@ -171,6 +179,7 @@
|
||||||
134A8A201AACED6A00945AAE /* RCTGeolocation.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTGeolocation.xcodeproj; path = ../../Libraries/Geolocation/RCTGeolocation.xcodeproj; sourceTree = "<group>"; };
|
134A8A201AACED6A00945AAE /* RCTGeolocation.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTGeolocation.xcodeproj; path = ../../Libraries/Geolocation/RCTGeolocation.xcodeproj; sourceTree = "<group>"; };
|
||||||
138D6A151B53CD440074A87E /* RCTCacheTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTCacheTests.m; sourceTree = "<group>"; };
|
138D6A151B53CD440074A87E /* RCTCacheTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTCacheTests.m; sourceTree = "<group>"; };
|
||||||
138D6A161B53CD440074A87E /* RCTShadowViewTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTShadowViewTests.m; sourceTree = "<group>"; };
|
138D6A161B53CD440074A87E /* RCTShadowViewTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTShadowViewTests.m; sourceTree = "<group>"; };
|
||||||
|
138DEE021B9EDDDB007F4EA5 /* RCTCameraRoll.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTCameraRoll.xcodeproj; path = ../../Libraries/CameraRoll/RCTCameraRoll.xcodeproj; sourceTree = "<group>"; };
|
||||||
1393D0371B68CD1300E1B601 /* RCTModuleMethodTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTModuleMethodTests.m; sourceTree = "<group>"; };
|
1393D0371B68CD1300E1B601 /* RCTModuleMethodTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTModuleMethodTests.m; sourceTree = "<group>"; };
|
||||||
139FDECA1B0651EA00C62182 /* RCTWebSocket.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTWebSocket.xcodeproj; path = ../../Libraries/WebSocket/RCTWebSocket.xcodeproj; sourceTree = "<group>"; };
|
139FDECA1B0651EA00C62182 /* RCTWebSocket.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTWebSocket.xcodeproj; path = ../../Libraries/WebSocket/RCTWebSocket.xcodeproj; sourceTree = "<group>"; };
|
||||||
13B07F961A680F5B00A75B9A /* UIExplorer.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = UIExplorer.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
13B07F961A680F5B00A75B9A /* UIExplorer.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = UIExplorer.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
|
@ -254,6 +263,7 @@
|
||||||
14AADF051AC3DBB1002390C9 /* libReact.a in Frameworks */,
|
14AADF051AC3DBB1002390C9 /* libReact.a in Frameworks */,
|
||||||
147CED4C1AB3532B00DA3E4C /* libRCTActionSheet.a in Frameworks */,
|
147CED4C1AB3532B00DA3E4C /* libRCTActionSheet.a in Frameworks */,
|
||||||
134454601AAFCABD003F0779 /* libRCTAdSupport.a in Frameworks */,
|
134454601AAFCABD003F0779 /* libRCTAdSupport.a in Frameworks */,
|
||||||
|
138DEE241B9EDFB6007F4EA5 /* libRCTCameraRoll.a in Frameworks */,
|
||||||
134A8A2A1AACED7A00945AAE /* libRCTGeolocation.a in Frameworks */,
|
134A8A2A1AACED7A00945AAE /* libRCTGeolocation.a in Frameworks */,
|
||||||
13417FE91AA91432003F314A /* libRCTImage.a in Frameworks */,
|
13417FE91AA91432003F314A /* libRCTImage.a in Frameworks */,
|
||||||
3578590A1B28D2CF00341EDB /* libRCTLinking.a in Frameworks */,
|
3578590A1B28D2CF00341EDB /* libRCTLinking.a in Frameworks */,
|
||||||
|
@ -281,13 +291,14 @@
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
14AADEFF1AC3DB95002390C9 /* React.xcodeproj */,
|
14AADEFF1AC3DB95002390C9 /* React.xcodeproj */,
|
||||||
14DC67E71AB71876001358AB /* RCTPushNotification.xcodeproj */,
|
|
||||||
14E0EEC81AB118F7000DECC3 /* RCTActionSheet.xcodeproj */,
|
14E0EEC81AB118F7000DECC3 /* RCTActionSheet.xcodeproj */,
|
||||||
134454551AAFCAAE003F0779 /* RCTAdSupport.xcodeproj */,
|
134454551AAFCAAE003F0779 /* RCTAdSupport.xcodeproj */,
|
||||||
|
138DEE021B9EDDDB007F4EA5 /* RCTCameraRoll.xcodeproj */,
|
||||||
134A8A201AACED6A00945AAE /* RCTGeolocation.xcodeproj */,
|
134A8A201AACED6A00945AAE /* RCTGeolocation.xcodeproj */,
|
||||||
13417FE31AA91428003F314A /* RCTImage.xcodeproj */,
|
13417FE31AA91428003F314A /* RCTImage.xcodeproj */,
|
||||||
357858F81B28D2C400341EDB /* RCTLinking.xcodeproj */,
|
357858F81B28D2C400341EDB /* RCTLinking.xcodeproj */,
|
||||||
134180261AA91779003F314A /* RCTNetwork.xcodeproj */,
|
134180261AA91779003F314A /* RCTNetwork.xcodeproj */,
|
||||||
|
14DC67E71AB71876001358AB /* RCTPushNotification.xcodeproj */,
|
||||||
13CC9D481AEED2B90020D1C2 /* RCTSettings.xcodeproj */,
|
13CC9D481AEED2B90020D1C2 /* RCTSettings.xcodeproj */,
|
||||||
58005BE41ABA80530062E044 /* RCTTest.xcodeproj */,
|
58005BE41ABA80530062E044 /* RCTTest.xcodeproj */,
|
||||||
13417FEA1AA914B8003F314A /* RCTText.xcodeproj */,
|
13417FEA1AA914B8003F314A /* RCTText.xcodeproj */,
|
||||||
|
@ -337,6 +348,14 @@
|
||||||
name = Products;
|
name = Products;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
138DEE031B9EDDDB007F4EA5 /* Products */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
138DEE091B9EDDDB007F4EA5 /* libRCTCameraRoll.a */,
|
||||||
|
);
|
||||||
|
name = Products;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
139FDECB1B0651EA00C62182 /* Products */ = {
|
139FDECB1B0651EA00C62182 /* Products */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
@ -624,6 +643,10 @@
|
||||||
ProductGroup = 134454561AAFCAAE003F0779 /* Products */;
|
ProductGroup = 134454561AAFCAAE003F0779 /* Products */;
|
||||||
ProjectRef = 134454551AAFCAAE003F0779 /* RCTAdSupport.xcodeproj */;
|
ProjectRef = 134454551AAFCAAE003F0779 /* RCTAdSupport.xcodeproj */;
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
ProductGroup = 138DEE031B9EDDDB007F4EA5 /* Products */;
|
||||||
|
ProjectRef = 138DEE021B9EDDDB007F4EA5 /* RCTCameraRoll.xcodeproj */;
|
||||||
|
},
|
||||||
{
|
{
|
||||||
ProductGroup = 134A8A211AACED6A00945AAE /* Products */;
|
ProductGroup = 134A8A211AACED6A00945AAE /* Products */;
|
||||||
ProjectRef = 134A8A201AACED6A00945AAE /* RCTGeolocation.xcodeproj */;
|
ProjectRef = 134A8A201AACED6A00945AAE /* RCTGeolocation.xcodeproj */;
|
||||||
|
@ -714,6 +737,13 @@
|
||||||
remoteRef = 134A8A241AACED6A00945AAE /* PBXContainerItemProxy */;
|
remoteRef = 134A8A241AACED6A00945AAE /* PBXContainerItemProxy */;
|
||||||
sourceTree = BUILT_PRODUCTS_DIR;
|
sourceTree = BUILT_PRODUCTS_DIR;
|
||||||
};
|
};
|
||||||
|
138DEE091B9EDDDB007F4EA5 /* libRCTCameraRoll.a */ = {
|
||||||
|
isa = PBXReferenceProxy;
|
||||||
|
fileType = archive.ar;
|
||||||
|
path = libRCTCameraRoll.a;
|
||||||
|
remoteRef = 138DEE081B9EDDDB007F4EA5 /* PBXContainerItemProxy */;
|
||||||
|
sourceTree = BUILT_PRODUCTS_DIR;
|
||||||
|
};
|
||||||
139FDED91B0651EA00C62182 /* libRCTWebSocket.a */ = {
|
139FDED91B0651EA00C62182 /* libRCTWebSocket.a */ = {
|
||||||
isa = PBXReferenceProxy;
|
isa = PBXReferenceProxy;
|
||||||
fileType = archive.ar;
|
fileType = archive.ar;
|
||||||
|
|
|
@ -59,7 +59,7 @@
|
||||||
* on the same Wi-Fi network.
|
* on the same Wi-Fi network.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
sourceURL = [NSURL URLWithString:@"http://localhost:8081/Examples/UIExplorer/UIExplorerApp.ios.bundle?dev=true"];
|
sourceURL = [NSURL URLWithString:@"http://localhost:8081/Examples/UIExplorer/UIExplorerApp.ios.bundle?platform=ios&dev=true"];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* OPTION 2
|
* OPTION 2
|
||||||
|
|
|
@ -0,0 +1,56 @@
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* @flow
|
||||||
|
*/
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var React = require('react-native');
|
||||||
|
var {
|
||||||
|
StyleSheet,
|
||||||
|
Text,
|
||||||
|
TouchableHighlight,
|
||||||
|
} = React;
|
||||||
|
|
||||||
|
var UIExplorerButton = React.createClass({
|
||||||
|
propTypes: {
|
||||||
|
onPress: React.PropTypes.func,
|
||||||
|
},
|
||||||
|
render: function() {
|
||||||
|
return (
|
||||||
|
<TouchableHighlight
|
||||||
|
onPress={this.props.onPress}
|
||||||
|
style={styles.button}
|
||||||
|
underlayColor="grey">
|
||||||
|
<Text>
|
||||||
|
{this.props.children}
|
||||||
|
</Text>
|
||||||
|
</TouchableHighlight>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
var styles = StyleSheet.create({
|
||||||
|
button: {
|
||||||
|
borderColor: 'dimgray',
|
||||||
|
borderRadius: 8,
|
||||||
|
borderWidth: 1,
|
||||||
|
padding: 10,
|
||||||
|
margin: 5,
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
backgroundColor: 'lightgrey',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = UIExplorerButton;
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 22 KiB |
Before Width: | Height: | Size: 86 KiB After Width: | Height: | Size: 86 KiB |
Before Width: | Height: | Size: 266 KiB After Width: | Height: | Size: 267 KiB |
Before Width: | Height: | Size: 97 KiB After Width: | Height: | Size: 97 KiB |
|
@ -14,11 +14,17 @@
|
||||||
|
|
||||||
#import "RCTAssert.h"
|
#import "RCTAssert.h"
|
||||||
|
|
||||||
@interface IntegrationTests : XCTestCase
|
#define RCT_TEST(name) \
|
||||||
|
- (void)test##name \
|
||||||
|
{ \
|
||||||
|
[_runner runTest:_cmd module:@#name]; \
|
||||||
|
}
|
||||||
|
|
||||||
|
@interface UIExplorerIntegrationTests : XCTestCase
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@implementation IntegrationTests
|
@implementation UIExplorerIntegrationTests
|
||||||
{
|
{
|
||||||
RCTTestRunner *_runner;
|
RCTTestRunner *_runner;
|
||||||
}
|
}
|
||||||
|
@ -36,11 +42,6 @@
|
||||||
|
|
||||||
#pragma mark Logic Tests
|
#pragma mark Logic Tests
|
||||||
|
|
||||||
- (void)testTheTester
|
|
||||||
{
|
|
||||||
[_runner runTest:_cmd module:@"IntegrationTestHarnessTest"];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testTheTester_waitOneFrame
|
- (void)testTheTester_waitOneFrame
|
||||||
{
|
{
|
||||||
[_runner runTest:_cmd
|
[_runner runTest:_cmd
|
||||||
|
@ -57,38 +58,12 @@
|
||||||
expectErrorRegex:@"because shouldThrow"];
|
expectErrorRegex:@"because shouldThrow"];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)testTimers
|
RCT_TEST(TimersTest)
|
||||||
{
|
RCT_TEST(IntegrationTestHarnessTest)
|
||||||
[_runner runTest:_cmd module:@"TimersTest"];
|
RCT_TEST(AsyncStorageTest)
|
||||||
}
|
// RCT_TEST(LayoutEventsTest) -- Disabled: #8153468
|
||||||
|
RCT_TEST(AppEventsTest)
|
||||||
- (void)testAsyncStorage
|
RCT_TEST(PromiseTest)
|
||||||
{
|
// RCT_TEST(SimpleSnapshotTest) -- Disabled: #8153475
|
||||||
[_runner runTest:_cmd module:@"AsyncStorageTest"];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)DISABLED_testLayoutEvents // #7149037
|
|
||||||
{
|
|
||||||
[_runner runTest:_cmd module:@"LayoutEventsTest"];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testAppEvents
|
|
||||||
{
|
|
||||||
[_runner runTest:_cmd module:@"AppEventsTest"];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testPromises
|
|
||||||
{
|
|
||||||
[_runner runTest:_cmd module:@"PromiseTest"];
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark Snapshot Tests
|
|
||||||
|
|
||||||
- (void)testSimpleSnapshot
|
|
||||||
{
|
|
||||||
// If tests have changes, set recordMode = YES below and re-run
|
|
||||||
_runner.recordMode = NO;
|
|
||||||
[_runner runTest:_cmd module:@"SimpleSnapshotTest"];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
@end
|
|
@ -19,7 +19,7 @@ var {
|
||||||
Text,
|
Text,
|
||||||
View,
|
View,
|
||||||
} = React;
|
} = React;
|
||||||
var TestModule = NativeModules.TestModule || NativeModules.SnapshotTestManager;
|
var TestModule = NativeModules.TestModule;
|
||||||
|
|
||||||
var deepDiffer = require('deepDiffer');
|
var deepDiffer = require('deepDiffer');
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,7 @@ var {
|
||||||
Text,
|
Text,
|
||||||
View,
|
View,
|
||||||
} = React;
|
} = React;
|
||||||
var TestModule = NativeModules.TestModule || NativeModules.SnapshotTestManager;
|
var TestModule = NativeModules.TestModule;
|
||||||
|
|
||||||
var deepDiffer = require('deepDiffer');
|
var deepDiffer = require('deepDiffer');
|
||||||
|
|
||||||
|
|
|
@ -60,7 +60,8 @@ var APIS = [
|
||||||
require('./ActionSheetIOSExample'),
|
require('./ActionSheetIOSExample'),
|
||||||
require('./AdSupportIOSExample'),
|
require('./AdSupportIOSExample'),
|
||||||
require('./AlertIOSExample'),
|
require('./AlertIOSExample'),
|
||||||
require('./AnimationExample/AnExApp'),
|
require('./AnimatedExample'),
|
||||||
|
require('./AnimatedGratuitousApp/AnExApp'),
|
||||||
require('./AppStateIOSExample'),
|
require('./AppStateIOSExample'),
|
||||||
require('./AsyncStorageExample'),
|
require('./AsyncStorageExample'),
|
||||||
require('./BorderExample'),
|
require('./BorderExample'),
|
||||||
|
|
|
@ -4,7 +4,7 @@ SDK_PATH = /Applications/Xcode.app/Contents/Developer/Platforms/$1.platform/Deve
|
||||||
|
|
||||||
SDK_VERSION = $(shell plutil -convert json -o - $(call SDK_PATH,iPhoneOS)/SDKSettings.plist | awk -f parseSDKVersion.awk)
|
SDK_VERSION = $(shell plutil -convert json -o - $(call SDK_PATH,iPhoneOS)/SDKSettings.plist | awk -f parseSDKVersion.awk)
|
||||||
|
|
||||||
CERT ?= "iPhone Developer"
|
CERT ?= iPhone Developer
|
||||||
|
|
||||||
ARCHS = x86_64 arm64 armv7 i386
|
ARCHS = x86_64 arm64 armv7 i386
|
||||||
|
|
||||||
|
@ -26,13 +26,12 @@ else
|
||||||
cp $^
|
cp $^
|
||||||
endif
|
endif
|
||||||
|
|
||||||
/tmp/JSCProfiler:
|
/tmp/RCTJSCProfiler:
|
||||||
mkdir -p $@
|
mkdir -p $@
|
||||||
|
|
||||||
.PRECIOUS: RCTJSCProfiler.ios8.dylib
|
|
||||||
RCTJSCProfiler.ios8.dylib: RCTJSCProfiler_unsigned.ios8.dylib
|
RCTJSCProfiler.ios8.dylib: RCTJSCProfiler_unsigned.ios8.dylib
|
||||||
cp $< $@
|
cp $< $@
|
||||||
codesign -f -s ${CERT} $@ || rm $@
|
codesign -f -s "${CERT}" $@
|
||||||
|
|
||||||
.PRECIOUS: RCTJSCProfiler_unsigned.ios8.dylib
|
.PRECIOUS: RCTJSCProfiler_unsigned.ios8.dylib
|
||||||
RCTJSCProfiler_unsigned.ios8.dylib: $(patsubst %,RCTJSCProfiler_%.ios8.dylib,$(ARCHS))
|
RCTJSCProfiler_unsigned.ios8.dylib: $(patsubst %,RCTJSCProfiler_%.ios8.dylib,$(ARCHS))
|
||||||
|
@ -77,7 +76,7 @@ libyajl_%.a: download/yajl-2.1.0
|
||||||
|
|
||||||
.PRECIOUS: download/yajl-2.1.0
|
.PRECIOUS: download/yajl-2.1.0
|
||||||
download/yajl-2.1.0: download/yajl-2.1.0.tar.gz
|
download/yajl-2.1.0: download/yajl-2.1.0.tar.gz
|
||||||
tar -zxvf $< -C download
|
tar -zxvf $< -C download > /dev/null
|
||||||
mkdir -p download/yajl-2.1.0/build && cd download/yajl-2.1.0/build && cmake ..
|
mkdir -p download/yajl-2.1.0/build && cd download/yajl-2.1.0/build && cmake ..
|
||||||
|
|
||||||
.PRECIOUS: download/yajl-2.1.0.tar.gz
|
.PRECIOUS: download/yajl-2.1.0.tar.gz
|
||||||
|
@ -87,7 +86,7 @@ download/yajl-2.1.0.tar.gz:
|
||||||
|
|
||||||
.PRECIOUS: download/%
|
.PRECIOUS: download/%
|
||||||
download/%: download/%.tar.gz
|
download/%: download/%.tar.gz
|
||||||
tar -zxvf $< -C `dirname $@`
|
tar -zxvf $< -C `dirname $@` > /dev/null
|
||||||
|
|
||||||
.PRECIOUS: %.tar.gz
|
.PRECIOUS: %.tar.gz
|
||||||
%.tar.gz:
|
%.tar.gz:
|
||||||
|
|
|
@ -503,6 +503,12 @@ type ValueListenerCallback = (state: {value: number}) => void;
|
||||||
|
|
||||||
var _uniqueId = 1;
|
var _uniqueId = 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Standard value for driving animations. One `Animated.Value` can drive
|
||||||
|
* multiple properties in a synchronized fashion, but can only be driven by one
|
||||||
|
* mechanism at a time. Using a new mechanism (e.g. starting a new animation,
|
||||||
|
* or calling `setValue`) will stop any previous ones.
|
||||||
|
*/
|
||||||
class AnimatedValue extends AnimatedWithChildren {
|
class AnimatedValue extends AnimatedWithChildren {
|
||||||
_value: number;
|
_value: number;
|
||||||
_offset: number;
|
_offset: number;
|
||||||
|
@ -526,6 +532,10 @@ class AnimatedValue extends AnimatedWithChildren {
|
||||||
return this._value + this._offset;
|
return this._value + this._offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Directly set the value. This will stop any animations running on the value
|
||||||
|
* and udpate all the bound properties.
|
||||||
|
*/
|
||||||
setValue(value: number): void {
|
setValue(value: number): void {
|
||||||
if (this._animation) {
|
if (this._animation) {
|
||||||
this._animation.stop();
|
this._animation.stop();
|
||||||
|
@ -534,15 +544,29 @@ class AnimatedValue extends AnimatedWithChildren {
|
||||||
this._updateValue(value);
|
this._updateValue(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets an offset that is applied on top of whatever value is set, whether via
|
||||||
|
* `setValue`, an animation, or `Animated.event`. Useful for compensating
|
||||||
|
* things like the start of a pan gesture.
|
||||||
|
*/
|
||||||
setOffset(offset: number): void {
|
setOffset(offset: number): void {
|
||||||
this._offset = offset;
|
this._offset = offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Merges the offset value into the base value and resets the offset to zero.
|
||||||
|
* The final output of the value is unchanged.
|
||||||
|
*/
|
||||||
flattenOffset(): void {
|
flattenOffset(): void {
|
||||||
this._value += this._offset;
|
this._value += this._offset;
|
||||||
this._offset = 0;
|
this._offset = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds an asynchronous listener to the value so you can observe updates from
|
||||||
|
* animations or whathaveyou. This is useful because there is no way to
|
||||||
|
* syncronously read the value because it might be driven natively.
|
||||||
|
*/
|
||||||
addListener(callback: ValueListenerCallback): string {
|
addListener(callback: ValueListenerCallback): string {
|
||||||
var id = String(_uniqueId++);
|
var id = String(_uniqueId++);
|
||||||
this._listeners[id] = callback;
|
this._listeners[id] = callback;
|
||||||
|
@ -557,6 +581,30 @@ class AnimatedValue extends AnimatedWithChildren {
|
||||||
this._listeners = {};
|
this._listeners = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stops any running animation or tracking. `callback` is invoked with the
|
||||||
|
* final value after stopping the animation, which is useful for updating
|
||||||
|
* state to match the animation position with layout.
|
||||||
|
*/
|
||||||
|
stopAnimation(callback?: ?(value: number) => void): void {
|
||||||
|
this.stopTracking();
|
||||||
|
this._animation && this._animation.stop();
|
||||||
|
this._animation = null;
|
||||||
|
callback && callback(this.__getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interpolates the value before updating the property, e.g. mapping 0-1 to
|
||||||
|
* 0-10.
|
||||||
|
*/
|
||||||
|
interpolate(config: InterpolationConfigType): AnimatedInterpolation {
|
||||||
|
return new AnimatedInterpolation(this, Interpolation.create(config));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Typically only used internally, but could be used by a custom Animation
|
||||||
|
* class.
|
||||||
|
*/
|
||||||
animate(animation: Animation, callback: ?EndCallback): void {
|
animate(animation: Animation, callback: ?EndCallback): void {
|
||||||
var handle = InteractionManager.createInteractionHandle();
|
var handle = InteractionManager.createInteractionHandle();
|
||||||
var previousAnimation = this._animation;
|
var previousAnimation = this._animation;
|
||||||
|
@ -576,27 +624,22 @@ class AnimatedValue extends AnimatedWithChildren {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
stopAnimation(callback?: ?(value: number) => void): void {
|
/**
|
||||||
this.stopTracking();
|
* Typically only used internally.
|
||||||
this._animation && this._animation.stop();
|
*/
|
||||||
this._animation = null;
|
|
||||||
callback && callback(this.__getValue());
|
|
||||||
}
|
|
||||||
|
|
||||||
stopTracking(): void {
|
stopTracking(): void {
|
||||||
this._tracking && this._tracking.__detach();
|
this._tracking && this._tracking.__detach();
|
||||||
this._tracking = null;
|
this._tracking = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Typically only used internally.
|
||||||
|
*/
|
||||||
track(tracking: Animated): void {
|
track(tracking: Animated): void {
|
||||||
this.stopTracking();
|
this.stopTracking();
|
||||||
this._tracking = tracking;
|
this._tracking = tracking;
|
||||||
}
|
}
|
||||||
|
|
||||||
interpolate(config: InterpolationConfigType): AnimatedInterpolation {
|
|
||||||
return new AnimatedInterpolation(this, Interpolation.create(config));
|
|
||||||
}
|
|
||||||
|
|
||||||
_updateValue(value: number): void {
|
_updateValue(value: number): void {
|
||||||
this._value = value;
|
this._value = value;
|
||||||
_flush(this);
|
_flush(this);
|
||||||
|
@ -607,6 +650,45 @@ class AnimatedValue extends AnimatedWithChildren {
|
||||||
}
|
}
|
||||||
|
|
||||||
type ValueXYListenerCallback = (value: {x: number; y: number}) => void;
|
type ValueXYListenerCallback = (value: {x: number; y: number}) => void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 2D Value for driving 2D animations, such as pan gestures. Almost identical
|
||||||
|
* API to normal `Animated.Value`, but multiplexed. Contains two regular
|
||||||
|
* `Animated.Value`s under the hood. Example:
|
||||||
|
*
|
||||||
|
*```javascript
|
||||||
|
* class DraggableView extends React.Component {
|
||||||
|
* constructor(props) {
|
||||||
|
* super(props);
|
||||||
|
* this.state = {
|
||||||
|
* pan: new Animated.ValueXY(), // inits to zero
|
||||||
|
* };
|
||||||
|
* this.state.panResponder = PanResponder.create({
|
||||||
|
* onStartShouldSetPanResponder: () => true,
|
||||||
|
* onPanResponderMove: Animated.event([null, {
|
||||||
|
* dx: this.state.pan.x, // x,y are Animated.Value
|
||||||
|
* dy: this.state.pan.y,
|
||||||
|
* }]),
|
||||||
|
* onPanResponderRelease: () => {
|
||||||
|
* Animated.spring(
|
||||||
|
* this.state.pan, // Auto-multiplexed
|
||||||
|
* {toValue: {x: 0, y: 0}} // Back to zero
|
||||||
|
* ).start();
|
||||||
|
* },
|
||||||
|
* });
|
||||||
|
* }
|
||||||
|
* render() {
|
||||||
|
* return (
|
||||||
|
* <Animated.View
|
||||||
|
* {...this.state.panResponder.panHandlers}
|
||||||
|
* style={this.state.pan.getLayout()}>
|
||||||
|
* {this.props.children}
|
||||||
|
* </Animated.View>
|
||||||
|
* );
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
*```
|
||||||
|
*/
|
||||||
class AnimatedValueXY extends AnimatedWithChildren {
|
class AnimatedValueXY extends AnimatedWithChildren {
|
||||||
x: AnimatedValue;
|
x: AnimatedValue;
|
||||||
y: AnimatedValue;
|
y: AnimatedValue;
|
||||||
|
@ -677,6 +759,13 @@ class AnimatedValueXY extends AnimatedWithChildren {
|
||||||
delete this._listeners[id];
|
delete this._listeners[id];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts `{x, y}` into `{left, top}` for use in style, e.g.
|
||||||
|
*
|
||||||
|
*```javascript
|
||||||
|
* style={this.state.anim.getLayout()}
|
||||||
|
*```
|
||||||
|
*/
|
||||||
getLayout(): {[key: string]: AnimatedValue} {
|
getLayout(): {[key: string]: AnimatedValue} {
|
||||||
return {
|
return {
|
||||||
left: this.x,
|
left: this.x,
|
||||||
|
@ -684,6 +773,15 @@ class AnimatedValueXY extends AnimatedWithChildren {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts `{x, y}` into a useable translation transform, e.g.
|
||||||
|
*
|
||||||
|
*```javascript
|
||||||
|
* style={{
|
||||||
|
* transform: this.state.anim.getTranslateTransform()
|
||||||
|
* }}
|
||||||
|
*```
|
||||||
|
*/
|
||||||
getTranslateTransform(): Array<{[key: string]: AnimatedValue}> {
|
getTranslateTransform(): Array<{[key: string]: AnimatedValue}> {
|
||||||
return [
|
return [
|
||||||
{translateX: this.x},
|
{translateX: this.x},
|
||||||
|
@ -1235,21 +1333,6 @@ var stagger = function(
|
||||||
|
|
||||||
type Mapping = {[key: string]: Mapping} | AnimatedValue;
|
type Mapping = {[key: string]: Mapping} | AnimatedValue;
|
||||||
|
|
||||||
/**
|
|
||||||
* Takes an array of mappings and extracts values from each arg accordingly,
|
|
||||||
* then calls setValue on the mapped outputs. e.g.
|
|
||||||
*
|
|
||||||
* onScroll={this.AnimatedEvent(
|
|
||||||
* [{nativeEvent: {contentOffset: {x: this._scrollX}}}]
|
|
||||||
* {listener} // optional listener invoked asynchronously
|
|
||||||
* )
|
|
||||||
* ...
|
|
||||||
* onPanResponderMove: this.AnimatedEvent([
|
|
||||||
* null, // raw event arg
|
|
||||||
* {dx: this._panX}, // gestureState arg
|
|
||||||
* ]),
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
type EventConfig = {listener?: ?Function};
|
type EventConfig = {listener?: ?Function};
|
||||||
var event = function(
|
var event = function(
|
||||||
argMapping: Array<?Mapping>,
|
argMapping: Array<?Mapping>,
|
||||||
|
@ -1287,23 +1370,179 @@ var event = function(
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Animations are an important part of modern UX, and the `Animated`
|
||||||
|
* library is designed to make them fluid, powerful, and easy to build and
|
||||||
|
* maintain.
|
||||||
|
*
|
||||||
|
* The simplest workflow is to create an `Animated.Value`, hook it up to one or
|
||||||
|
* more style attributes of an animated component, and then drive updates either
|
||||||
|
* via animations, such as `Animated.timing`, or by hooking into gestures like
|
||||||
|
* panning or scolling via `Animated.event`. `Animated.Value` can also bind to
|
||||||
|
* props other than style, and can be interpolated as well. Here is a basic
|
||||||
|
* example of a container view that will fade in when it's mounted:
|
||||||
|
*
|
||||||
|
*```javascript
|
||||||
|
* class FadeInView extends React.Component {
|
||||||
|
* constructor(props) {
|
||||||
|
* super(props);
|
||||||
|
* this.state = {
|
||||||
|
* fadeAnim: new Animated.Value(0), // init opacity 0
|
||||||
|
* };
|
||||||
|
* }
|
||||||
|
* componentDidMount() {
|
||||||
|
* Animated.timing( // Uses easing functions
|
||||||
|
* this.state.fadeAnim, // The value to drive
|
||||||
|
* {toValue: 1}, // Configuration
|
||||||
|
* ).start(); // Don't forget start!
|
||||||
|
* }
|
||||||
|
* render() {
|
||||||
|
* return (
|
||||||
|
* <Animated.View // Special animatable View
|
||||||
|
* style={{opacity: this.state.fadeAnim}}> // Binds
|
||||||
|
* {this.props.children}
|
||||||
|
* </Animated.View>
|
||||||
|
* );
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
*```
|
||||||
|
*
|
||||||
|
* Note that only animatable components can be animated. `View`, `Text`, and
|
||||||
|
* `Image` are already provided, and you can create custom ones with
|
||||||
|
* `createAnimatedComponent`. These special components do the magic of binding
|
||||||
|
* the animated values to the properties, and do targetted native updates to
|
||||||
|
* avoid the cost of the react render and reconciliation process on every frame.
|
||||||
|
* They also handle cleanup on unmount so they are safe by default.
|
||||||
|
*
|
||||||
|
* Animations are heavily configurable. Custom and pre-defined easing
|
||||||
|
* functions, delays, durations, decay factors, spring constants, and more can
|
||||||
|
* all be tweaked depending on the type of animation.
|
||||||
|
*
|
||||||
|
* A single `Animated.Value` can drive any number of properties, and each
|
||||||
|
* property can be run through an interpolation first. An interpolation maps
|
||||||
|
* input ranges to output ranges, typically using a linear interpolation but
|
||||||
|
* also supports easing functions. By default, it will extrapolate the curve
|
||||||
|
* beyond the ranges given, but you can also have it clamp the output value.
|
||||||
|
*
|
||||||
|
* For example, you may want to think about your `Animated.Value` as going from
|
||||||
|
* 0 to 1, but animate the position from 150px to 0px and the opacity from 0 to
|
||||||
|
* 1. This can easily be done by modifying `style` in the example above like so:
|
||||||
|
*
|
||||||
|
*```javascript
|
||||||
|
* style={{
|
||||||
|
* opacity: this.state.fadeAnim, // Binds directly
|
||||||
|
* transform: [{
|
||||||
|
* translateY: this.state.fadeAnim.interpolate({
|
||||||
|
* inputRange: [0, 1],
|
||||||
|
* outputRange: [150, 0] // 0 : 150, 0.5 : 75, 1 : 0
|
||||||
|
* }),
|
||||||
|
* }],
|
||||||
|
* }}>
|
||||||
|
*```
|
||||||
|
*
|
||||||
|
* Animations can also be combined in complex ways using composition functions
|
||||||
|
* such as `sequence` and `parallel`, and can also be chained together simply
|
||||||
|
* by setting the `toValue` of one animation to be another `Animated.Value`.
|
||||||
|
*
|
||||||
|
* `Animated.ValueXY` is handy for 2D animations, like panning, and there are
|
||||||
|
* other helpful additions like `setOffset` and `getLayout` to aid with typical
|
||||||
|
* interaction patterns, like drag-and-drop.
|
||||||
|
*
|
||||||
|
* You can see more example usage in `AnimationExample.js`, the Gratuitous
|
||||||
|
* Animation App, and [Animations documentation guide](http://facebook.github.io/react-native/docs/animations.html).
|
||||||
|
*
|
||||||
|
* Note that `Animated` is designed to be fully serializable so that animations
|
||||||
|
* can be run in a high performace way, independent of the normal JavaScript
|
||||||
|
* event loop. This does influence the API, so keep that in mind when it seems a
|
||||||
|
* little trickier to do something compared to a fully synchronous system.
|
||||||
|
* Checkout `Animated.Value.addListener` as a way to work around some of these
|
||||||
|
* limitations, but use it sparingly since it might have performance
|
||||||
|
* implications in the future.
|
||||||
|
*/
|
||||||
module.exports = {
|
module.exports = {
|
||||||
delay,
|
/**
|
||||||
sequence,
|
* Standard value class for driving animations. Typically initialized with
|
||||||
parallel,
|
* `new Animated.Value(0);`
|
||||||
stagger,
|
*/
|
||||||
|
Value: AnimatedValue,
|
||||||
|
/**
|
||||||
|
* 2D value class for driving 2D animations, such as pan gestures.
|
||||||
|
*/
|
||||||
|
ValueXY: AnimatedValueXY,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An animatable View component.
|
||||||
|
*/
|
||||||
|
View: createAnimatedComponent(View),
|
||||||
|
/**
|
||||||
|
* An animatable Text component.
|
||||||
|
*/
|
||||||
|
Text: createAnimatedComponent(Text),
|
||||||
|
/**
|
||||||
|
* An animatable Image component.
|
||||||
|
*/
|
||||||
|
Image: createAnimatedComponent(Image),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Animates a value from an initial velocity to zero based on a decay
|
||||||
|
* coefficient.
|
||||||
|
*/
|
||||||
decay,
|
decay,
|
||||||
|
/**
|
||||||
|
* Animates a value along a timed easing curve. The `Easing` module has tons
|
||||||
|
* of pre-defined curves, or you can use your own function.
|
||||||
|
*/
|
||||||
timing,
|
timing,
|
||||||
|
/**
|
||||||
|
* Spring animation based on Rebound and Origami. Tracks velocity state to
|
||||||
|
* create fluid motions as the `toValue` updates, and can be chained together.
|
||||||
|
*/
|
||||||
spring,
|
spring,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts an animation after the given delay.
|
||||||
|
*/
|
||||||
|
delay,
|
||||||
|
/**
|
||||||
|
* Starts an array of animations in order, waiting for each to complete
|
||||||
|
* before starting the next. If the current running animation is stopped, no
|
||||||
|
* following animations will be started.
|
||||||
|
*/
|
||||||
|
sequence,
|
||||||
|
/**
|
||||||
|
* Starts an array of animations all at the same time. By default, if one
|
||||||
|
* of the animations is stopped, they will all be stopped. You can override
|
||||||
|
* this with the `stopTogether` flag.
|
||||||
|
*/
|
||||||
|
parallel,
|
||||||
|
/**
|
||||||
|
* Array of animations may run in parallel (overlap), but are started in
|
||||||
|
* sequence with successive delays. Nice for doing trailing effects.
|
||||||
|
*/
|
||||||
|
stagger,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Takes an array of mappings and extracts values from each arg accordingly,
|
||||||
|
* then calls `setValue` on the mapped outputs. e.g.
|
||||||
|
*
|
||||||
|
*```javascript
|
||||||
|
* onScroll={this.AnimatedEvent(
|
||||||
|
* [{nativeEvent: {contentOffset: {x: this._scrollX}}}]
|
||||||
|
* {listener}, // Optional async listener
|
||||||
|
* )
|
||||||
|
* ...
|
||||||
|
* onPanResponderMove: this.AnimatedEvent([
|
||||||
|
* null, // raw event arg ignored
|
||||||
|
* {dx: this._panX}, // gestureState arg
|
||||||
|
* ]),
|
||||||
|
*```
|
||||||
|
*/
|
||||||
event,
|
event,
|
||||||
|
|
||||||
Value: AnimatedValue,
|
/**
|
||||||
ValueXY: AnimatedValueXY,
|
* Make any React component Animatable. Used to create `Animated.View`, etc.
|
||||||
__PropsOnlyForTests: AnimatedProps,
|
*/
|
||||||
View: createAnimatedComponent(View),
|
|
||||||
Text: createAnimatedComponent(Text),
|
|
||||||
Image: createAnimatedComponent(Image),
|
|
||||||
createAnimatedComponent,
|
createAnimatedComponent,
|
||||||
|
|
||||||
|
__PropsOnlyForTests: AnimatedProps,
|
||||||
};
|
};
|
||||||
|
|
|
@ -123,12 +123,18 @@ class Easing {
|
||||||
return easing;
|
return easing;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runs an easing function backwards.
|
||||||
|
*/
|
||||||
static out(
|
static out(
|
||||||
easing: (t: number) => number,
|
easing: (t: number) => number,
|
||||||
): (t: number) => number {
|
): (t: number) => number {
|
||||||
return (t) => 1 - easing(1 - t);
|
return (t) => 1 - easing(1 - t);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Makes any easing function symmetrical.
|
||||||
|
*/
|
||||||
static inOut(
|
static inOut(
|
||||||
easing: (t: number) => number,
|
easing: (t: number) => number,
|
||||||
): (t: number) => number {
|
): (t: number) => number {
|
||||||
|
|
|
@ -0,0 +1,296 @@
|
||||||
|
// !$*UTF8*$!
|
||||||
|
{
|
||||||
|
archiveVersion = 1;
|
||||||
|
classes = {
|
||||||
|
};
|
||||||
|
objectVersion = 46;
|
||||||
|
objects = {
|
||||||
|
|
||||||
|
/* Begin PBXBuildFile section */
|
||||||
|
137620351B31C53500677FF0 /* RCTImagePickerManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 137620341B31C53500677FF0 /* RCTImagePickerManager.m */; };
|
||||||
|
143879351AAD238D00F088A5 /* RCTCameraRollManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 143879341AAD238D00F088A5 /* RCTCameraRollManager.m */; };
|
||||||
|
8312EAEE1B85EB7C001867A2 /* RCTAssetsLibraryImageLoader.m in Sources */ = {isa = PBXBuildFile; fileRef = 8312EAED1B85EB7C001867A2 /* RCTAssetsLibraryImageLoader.m */; };
|
||||||
|
8312EAF11B85F071001867A2 /* RCTPhotoLibraryImageLoader.m in Sources */ = {isa = PBXBuildFile; fileRef = 8312EAF01B85F071001867A2 /* RCTPhotoLibraryImageLoader.m */; };
|
||||||
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
|
/* Begin PBXCopyFilesBuildPhase section */
|
||||||
|
58B5115B1A9E6B3D00147676 /* CopyFiles */ = {
|
||||||
|
isa = PBXCopyFilesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
dstPath = "include/$(PRODUCT_NAME)";
|
||||||
|
dstSubfolderSpec = 16;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
/* End PBXCopyFilesBuildPhase section */
|
||||||
|
|
||||||
|
/* Begin PBXFileReference section */
|
||||||
|
137620331B31C53500677FF0 /* RCTImagePickerManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTImagePickerManager.h; sourceTree = "<group>"; };
|
||||||
|
137620341B31C53500677FF0 /* RCTImagePickerManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTImagePickerManager.m; sourceTree = "<group>"; };
|
||||||
|
143879331AAD238D00F088A5 /* RCTCameraRollManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTCameraRollManager.h; sourceTree = "<group>"; };
|
||||||
|
143879341AAD238D00F088A5 /* RCTCameraRollManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTCameraRollManager.m; sourceTree = "<group>"; };
|
||||||
|
58B5115D1A9E6B3D00147676 /* libRCTCameraRoll.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRCTCameraRoll.a; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
|
8312EAEC1B85EB7C001867A2 /* RCTAssetsLibraryImageLoader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTAssetsLibraryImageLoader.h; sourceTree = "<group>"; };
|
||||||
|
8312EAED1B85EB7C001867A2 /* RCTAssetsLibraryImageLoader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTAssetsLibraryImageLoader.m; sourceTree = "<group>"; };
|
||||||
|
8312EAEF1B85F071001867A2 /* RCTPhotoLibraryImageLoader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTPhotoLibraryImageLoader.h; sourceTree = "<group>"; };
|
||||||
|
8312EAF01B85F071001867A2 /* RCTPhotoLibraryImageLoader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTPhotoLibraryImageLoader.m; sourceTree = "<group>"; };
|
||||||
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
|
/* Begin PBXFrameworksBuildPhase section */
|
||||||
|
58B5115A1A9E6B3D00147676 /* Frameworks */ = {
|
||||||
|
isa = PBXFrameworksBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
/* End PBXFrameworksBuildPhase section */
|
||||||
|
|
||||||
|
/* Begin PBXGroup section */
|
||||||
|
58B511541A9E6B3D00147676 = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
8312EAEC1B85EB7C001867A2 /* RCTAssetsLibraryImageLoader.h */,
|
||||||
|
8312EAED1B85EB7C001867A2 /* RCTAssetsLibraryImageLoader.m */,
|
||||||
|
143879331AAD238D00F088A5 /* RCTCameraRollManager.h */,
|
||||||
|
143879341AAD238D00F088A5 /* RCTCameraRollManager.m */,
|
||||||
|
137620331B31C53500677FF0 /* RCTImagePickerManager.h */,
|
||||||
|
137620341B31C53500677FF0 /* RCTImagePickerManager.m */,
|
||||||
|
8312EAEF1B85F071001867A2 /* RCTPhotoLibraryImageLoader.h */,
|
||||||
|
8312EAF01B85F071001867A2 /* RCTPhotoLibraryImageLoader.m */,
|
||||||
|
58B5115E1A9E6B3D00147676 /* Products */,
|
||||||
|
);
|
||||||
|
indentWidth = 2;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
tabWidth = 2;
|
||||||
|
};
|
||||||
|
58B5115E1A9E6B3D00147676 /* Products */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
58B5115D1A9E6B3D00147676 /* libRCTCameraRoll.a */,
|
||||||
|
);
|
||||||
|
name = Products;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
|
/* End PBXGroup section */
|
||||||
|
|
||||||
|
/* Begin PBXNativeTarget section */
|
||||||
|
58B5115C1A9E6B3D00147676 /* RCTCameraRoll */ = {
|
||||||
|
isa = PBXNativeTarget;
|
||||||
|
buildConfigurationList = 58B511711A9E6B3D00147676 /* Build configuration list for PBXNativeTarget "RCTCameraRoll" */;
|
||||||
|
buildPhases = (
|
||||||
|
58B511591A9E6B3D00147676 /* Sources */,
|
||||||
|
58B5115A1A9E6B3D00147676 /* Frameworks */,
|
||||||
|
58B5115B1A9E6B3D00147676 /* CopyFiles */,
|
||||||
|
);
|
||||||
|
buildRules = (
|
||||||
|
);
|
||||||
|
dependencies = (
|
||||||
|
);
|
||||||
|
name = RCTCameraRoll;
|
||||||
|
productName = RCTNetworkImage;
|
||||||
|
productReference = 58B5115D1A9E6B3D00147676 /* libRCTCameraRoll.a */;
|
||||||
|
productType = "com.apple.product-type.library.static";
|
||||||
|
};
|
||||||
|
/* End PBXNativeTarget section */
|
||||||
|
|
||||||
|
/* Begin PBXProject section */
|
||||||
|
58B511551A9E6B3D00147676 /* Project object */ = {
|
||||||
|
isa = PBXProject;
|
||||||
|
attributes = {
|
||||||
|
LastUpgradeCheck = 0610;
|
||||||
|
ORGANIZATIONNAME = Facebook;
|
||||||
|
TargetAttributes = {
|
||||||
|
58B5115C1A9E6B3D00147676 = {
|
||||||
|
CreatedOnToolsVersion = 6.1.1;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
buildConfigurationList = 58B511581A9E6B3D00147676 /* Build configuration list for PBXProject "RCTCameraRoll" */;
|
||||||
|
compatibilityVersion = "Xcode 3.2";
|
||||||
|
developmentRegion = English;
|
||||||
|
hasScannedForEncodings = 0;
|
||||||
|
knownRegions = (
|
||||||
|
en,
|
||||||
|
);
|
||||||
|
mainGroup = 58B511541A9E6B3D00147676;
|
||||||
|
productRefGroup = 58B5115E1A9E6B3D00147676 /* Products */;
|
||||||
|
projectDirPath = "";
|
||||||
|
projectRoot = "";
|
||||||
|
targets = (
|
||||||
|
58B5115C1A9E6B3D00147676 /* RCTCameraRoll */,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
/* End PBXProject section */
|
||||||
|
|
||||||
|
/* Begin PBXSourcesBuildPhase section */
|
||||||
|
58B511591A9E6B3D00147676 /* Sources */ = {
|
||||||
|
isa = PBXSourcesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
8312EAEE1B85EB7C001867A2 /* RCTAssetsLibraryImageLoader.m in Sources */,
|
||||||
|
8312EAF11B85F071001867A2 /* RCTPhotoLibraryImageLoader.m in Sources */,
|
||||||
|
137620351B31C53500677FF0 /* RCTImagePickerManager.m in Sources */,
|
||||||
|
143879351AAD238D00F088A5 /* RCTCameraRollManager.m in Sources */,
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
/* End PBXSourcesBuildPhase section */
|
||||||
|
|
||||||
|
/* Begin XCBuildConfiguration section */
|
||||||
|
58B5116F1A9E6B3D00147676 /* 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_INITIALIZER_NOT_FULLY_BRACKETED = YES;
|
||||||
|
GCC_WARN_SHADOW = YES;
|
||||||
|
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||||
|
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||||
|
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||||
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||||
|
IPHONEOS_DEPLOYMENT_TARGET = 7.0;
|
||||||
|
MTL_ENABLE_DEBUG_INFO = YES;
|
||||||
|
ONLY_ACTIVE_ARCH = YES;
|
||||||
|
SDKROOT = iphoneos;
|
||||||
|
WARNING_CFLAGS = (
|
||||||
|
"-Werror",
|
||||||
|
"-Wall",
|
||||||
|
);
|
||||||
|
};
|
||||||
|
name = Debug;
|
||||||
|
};
|
||||||
|
58B511701A9E6B3D00147676 /* 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_INITIALIZER_NOT_FULLY_BRACKETED = YES;
|
||||||
|
GCC_WARN_SHADOW = YES;
|
||||||
|
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||||
|
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||||
|
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||||
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||||
|
IPHONEOS_DEPLOYMENT_TARGET = 7.0;
|
||||||
|
MTL_ENABLE_DEBUG_INFO = NO;
|
||||||
|
SDKROOT = iphoneos;
|
||||||
|
VALIDATE_PRODUCT = YES;
|
||||||
|
WARNING_CFLAGS = (
|
||||||
|
"-Werror",
|
||||||
|
"-Wall",
|
||||||
|
);
|
||||||
|
};
|
||||||
|
name = Release;
|
||||||
|
};
|
||||||
|
58B511721A9E6B3D00147676 /* Debug */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
CLANG_STATIC_ANALYZER_MODE = deep;
|
||||||
|
HEADER_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
|
||||||
|
"$(SRCROOT)/../../React/**",
|
||||||
|
"$(SRCROOT)/../Image/**",
|
||||||
|
"$(SRCROOT)/../Network/**",
|
||||||
|
);
|
||||||
|
LIBRARY_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"$(USER_LIBRARY_DIR)/Developer/Xcode/DerivedData/UIExplorer-gjaibsjtheitasdxdtcvxxqavkvy/Build/Products/Debug-iphoneos",
|
||||||
|
);
|
||||||
|
OTHER_LDFLAGS = "-ObjC";
|
||||||
|
PRODUCT_NAME = RCTCameraRoll;
|
||||||
|
RUN_CLANG_STATIC_ANALYZER = YES;
|
||||||
|
SKIP_INSTALL = YES;
|
||||||
|
};
|
||||||
|
name = Debug;
|
||||||
|
};
|
||||||
|
58B511731A9E6B3D00147676 /* Release */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
CLANG_STATIC_ANALYZER_MODE = deep;
|
||||||
|
HEADER_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
|
||||||
|
"$(SRCROOT)/../../React/**",
|
||||||
|
"$(SRCROOT)/../Image/**",
|
||||||
|
"$(SRCROOT)/../Network/**",
|
||||||
|
);
|
||||||
|
LIBRARY_SEARCH_PATHS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"$(USER_LIBRARY_DIR)/Developer/Xcode/DerivedData/UIExplorer-gjaibsjtheitasdxdtcvxxqavkvy/Build/Products/Debug-iphoneos",
|
||||||
|
);
|
||||||
|
OTHER_LDFLAGS = "-ObjC";
|
||||||
|
PRODUCT_NAME = RCTCameraRoll;
|
||||||
|
RUN_CLANG_STATIC_ANALYZER = NO;
|
||||||
|
SKIP_INSTALL = YES;
|
||||||
|
};
|
||||||
|
name = Release;
|
||||||
|
};
|
||||||
|
/* End XCBuildConfiguration section */
|
||||||
|
|
||||||
|
/* Begin XCConfigurationList section */
|
||||||
|
58B511581A9E6B3D00147676 /* Build configuration list for PBXProject "RCTCameraRoll" */ = {
|
||||||
|
isa = XCConfigurationList;
|
||||||
|
buildConfigurations = (
|
||||||
|
58B5116F1A9E6B3D00147676 /* Debug */,
|
||||||
|
58B511701A9E6B3D00147676 /* Release */,
|
||||||
|
);
|
||||||
|
defaultConfigurationIsVisible = 0;
|
||||||
|
defaultConfigurationName = Release;
|
||||||
|
};
|
||||||
|
58B511711A9E6B3D00147676 /* Build configuration list for PBXNativeTarget "RCTCameraRoll" */ = {
|
||||||
|
isa = XCConfigurationList;
|
||||||
|
buildConfigurations = (
|
||||||
|
58B511721A9E6B3D00147676 /* Debug */,
|
||||||
|
58B511731A9E6B3D00147676 /* Release */,
|
||||||
|
);
|
||||||
|
defaultConfigurationIsVisible = 0;
|
||||||
|
defaultConfigurationName = Release;
|
||||||
|
};
|
||||||
|
/* End XCConfigurationList section */
|
||||||
|
};
|
||||||
|
rootObject = 58B511551A9E6B3D00147676 /* Project object */;
|
||||||
|
}
|
|
@ -49,6 +49,7 @@ var AndroidTextInputAttributes = {
|
||||||
keyboardType: true,
|
keyboardType: true,
|
||||||
mostRecentEventCount: true,
|
mostRecentEventCount: true,
|
||||||
multiline: true,
|
multiline: true,
|
||||||
|
numberOfLines: true,
|
||||||
password: true,
|
password: true,
|
||||||
placeholder: true,
|
placeholder: true,
|
||||||
placeholderTextColor: true,
|
placeholderTextColor: true,
|
||||||
|
@ -193,6 +194,12 @@ var TextInput = React.createClass({
|
||||||
* @platform ios
|
* @platform ios
|
||||||
*/
|
*/
|
||||||
maxLength: PropTypes.number,
|
maxLength: PropTypes.number,
|
||||||
|
/**
|
||||||
|
* Sets the number of lines for a TextInput. Use it with multiline set to
|
||||||
|
* true to be able to fill the lines.
|
||||||
|
* @platform android
|
||||||
|
*/
|
||||||
|
numberOfLines: PropTypes.number,
|
||||||
/**
|
/**
|
||||||
* If true, the keyboard disables the return key when there is no text and
|
* If true, the keyboard disables the return key when there is no text and
|
||||||
* automatically enables it when there is text. The default value is false.
|
* automatically enables it when there is text. The default value is false.
|
||||||
|
@ -484,6 +491,7 @@ var TextInput = React.createClass({
|
||||||
keyboardType={this.props.keyboardType}
|
keyboardType={this.props.keyboardType}
|
||||||
mostRecentEventCount={this.state.mostRecentEventCount}
|
mostRecentEventCount={this.state.mostRecentEventCount}
|
||||||
multiline={this.props.multiline}
|
multiline={this.props.multiline}
|
||||||
|
numberOfLines={this.props.numberOfLines}
|
||||||
onFocus={this._onFocus}
|
onFocus={this._onFocus}
|
||||||
onBlur={this._onBlur}
|
onBlur={this._onBlur}
|
||||||
onChange={this._onChange}
|
onChange={this._onChange}
|
||||||
|
|
|
@ -209,6 +209,8 @@ var TouchableHighlight = React.createClass({
|
||||||
return (
|
return (
|
||||||
<View
|
<View
|
||||||
accessible={true}
|
accessible={true}
|
||||||
|
accessibilityComponentType={this.props.accessibilityComponentType}
|
||||||
|
accessibilityTraits={this.props.accessibilityTraits}
|
||||||
ref={UNDERLAY_REF}
|
ref={UNDERLAY_REF}
|
||||||
style={this.state.underlayStyle}
|
style={this.state.underlayStyle}
|
||||||
onLayout={this.props.onLayout}
|
onLayout={this.props.onLayout}
|
||||||
|
|
|
@ -156,6 +156,8 @@ var TouchableOpacity = React.createClass({
|
||||||
return (
|
return (
|
||||||
<Animated.View
|
<Animated.View
|
||||||
accessible={true}
|
accessible={true}
|
||||||
|
accessibilityComponentType={this.props.accessibilityComponentType}
|
||||||
|
accessibilityTraits={this.props.accessibilityTraits}
|
||||||
style={[this.props.style, {opacity: this.state.anim}]}
|
style={[this.props.style, {opacity: this.state.anim}]}
|
||||||
testID={this.props.testID}
|
testID={this.props.testID}
|
||||||
onLayout={this.props.onLayout}
|
onLayout={this.props.onLayout}
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
var React = require('React');
|
var React = require('React');
|
||||||
var TimerMixin = require('react-timer-mixin');
|
var TimerMixin = require('react-timer-mixin');
|
||||||
var Touchable = require('Touchable');
|
var Touchable = require('Touchable');
|
||||||
|
var View = require('View');
|
||||||
var ensurePositiveDelayProps = require('ensurePositiveDelayProps');
|
var ensurePositiveDelayProps = require('ensurePositiveDelayProps');
|
||||||
var onlyChild = require('onlyChild');
|
var onlyChild = require('onlyChild');
|
||||||
|
|
||||||
|
@ -36,11 +37,16 @@ var TouchableWithoutFeedback = React.createClass({
|
||||||
mixins: [TimerMixin, Touchable.Mixin],
|
mixins: [TimerMixin, Touchable.Mixin],
|
||||||
|
|
||||||
propTypes: {
|
propTypes: {
|
||||||
|
accessible: React.PropTypes.bool,
|
||||||
|
accessibilityComponentType: React.PropTypes.oneOf(View.AccessibilityComponentType),
|
||||||
|
accessibilityTraits: React.PropTypes.oneOfType([
|
||||||
|
React.PropTypes.oneOf(View.AccessibilityTraits),
|
||||||
|
React.PropTypes.arrayOf(React.PropTypes.oneOf(View.AccessibilityTraits)),
|
||||||
|
]),
|
||||||
/**
|
/**
|
||||||
* Called when the touch is released, but not if cancelled (e.g. by a scroll
|
* Called when the touch is released, but not if cancelled (e.g. by a scroll
|
||||||
* that steals the responder lock).
|
* that steals the responder lock).
|
||||||
*/
|
*/
|
||||||
accessible: React.PropTypes.bool,
|
|
||||||
onPress: React.PropTypes.func,
|
onPress: React.PropTypes.func,
|
||||||
onPressIn: React.PropTypes.func,
|
onPressIn: React.PropTypes.func,
|
||||||
onPressOut: React.PropTypes.func,
|
onPressOut: React.PropTypes.func,
|
||||||
|
@ -120,6 +126,8 @@ var TouchableWithoutFeedback = React.createClass({
|
||||||
// Note(avik): remove dynamic typecast once Flow has been upgraded
|
// Note(avik): remove dynamic typecast once Flow has been upgraded
|
||||||
return (React: any).cloneElement(onlyChild(this.props.children), {
|
return (React: any).cloneElement(onlyChild(this.props.children), {
|
||||||
accessible: this.props.accessible !== false,
|
accessible: this.props.accessible !== false,
|
||||||
|
accessibilityComponentType: this.props.accessibilityComponentType,
|
||||||
|
accessibilityTraits: this.props.accessibilityTraits,
|
||||||
testID: this.props.testID,
|
testID: this.props.testID,
|
||||||
onLayout: this.props.onLayout,
|
onLayout: this.props.onLayout,
|
||||||
onStartShouldSetResponder: this.touchableHandleStartShouldSetResponder,
|
onStartShouldSetResponder: this.touchableHandleStartShouldSetResponder,
|
||||||
|
|
|
@ -44,6 +44,13 @@ var AccessibilityTraits = [
|
||||||
'pageTurn',
|
'pageTurn',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
var AccessibilityComponentType = [
|
||||||
|
'none',
|
||||||
|
'button',
|
||||||
|
'radiobutton_checked',
|
||||||
|
'radiobutton_unchecked',
|
||||||
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The most fundamental component for building UI, `View` is a
|
* The most fundamental component for building UI, `View` is a
|
||||||
* container that supports layout with flexbox, style, some touch handling, and
|
* container that supports layout with flexbox, style, some touch handling, and
|
||||||
|
@ -76,6 +83,11 @@ var View = React.createClass({
|
||||||
validAttributes: ReactNativeViewAttributes.RCTView
|
validAttributes: ReactNativeViewAttributes.RCTView
|
||||||
},
|
},
|
||||||
|
|
||||||
|
statics: {
|
||||||
|
AccessibilityTraits,
|
||||||
|
AccessibilityComponentType,
|
||||||
|
},
|
||||||
|
|
||||||
propTypes: {
|
propTypes: {
|
||||||
/**
|
/**
|
||||||
* When true, indicates that the view is an accessibility element. By default,
|
* When true, indicates that the view is an accessibility element. By default,
|
||||||
|
@ -95,12 +107,7 @@ var View = React.createClass({
|
||||||
* native one. Works for Android only.
|
* native one. Works for Android only.
|
||||||
* @platform android
|
* @platform android
|
||||||
*/
|
*/
|
||||||
accessibilityComponentType: PropTypes.oneOf([
|
accessibilityComponentType: PropTypes.oneOf(AccessibilityComponentType),
|
||||||
'none',
|
|
||||||
'button',
|
|
||||||
'radiobutton_checked',
|
|
||||||
'radiobutton_unchecked',
|
|
||||||
]),
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Indicates to accessibility services whether the user should be notified
|
* Indicates to accessibility services whether the user should be notified
|
||||||
|
@ -152,7 +159,7 @@ var View = React.createClass({
|
||||||
* When `accessible` is true, the system will try to invoke this function
|
* When `accessible` is true, the system will try to invoke this function
|
||||||
* when the user performs accessibility tap gesture.
|
* when the user performs accessibility tap gesture.
|
||||||
*/
|
*/
|
||||||
onAcccessibilityTap: PropTypes.func,
|
onAccessibilityTap: PropTypes.func,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* When `accessible` is true, the system will invoke this function when the
|
* When `accessible` is true, the system will invoke this function when the
|
||||||
|
|
|
@ -72,7 +72,9 @@ function setupDevtools() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleMessage(evt) {
|
function handleMessage(evt) {
|
||||||
var data;
|
// It's hard to handle JSON in a safe manner without inspecting it at
|
||||||
|
// runtime, hence the any
|
||||||
|
var data: any;
|
||||||
try {
|
try {
|
||||||
data = JSON.parse(evt.data);
|
data = JSON.parse(evt.data);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
|
@ -27,60 +27,85 @@ RCT_EXPORT_MODULE()
|
||||||
return !strcmp(header, "GIF87a") || !strcmp(header, "GIF89a");
|
return !strcmp(header, "GIF87a") || !strcmp(header, "GIF89a");
|
||||||
}
|
}
|
||||||
|
|
||||||
- (RCTImageLoaderCancellationBlock)decodeImageData:(NSData *)imageData size:(CGSize)size scale:(CGFloat)scale resizeMode:(UIViewContentMode)resizeMode completionHandler:(RCTImageLoaderCompletionBlock)completionHandler
|
- (RCTImageLoaderCancellationBlock)decodeImageData:(NSData *)imageData
|
||||||
|
size:(CGSize)size
|
||||||
|
scale:(CGFloat)scale
|
||||||
|
resizeMode:(UIViewContentMode)resizeMode
|
||||||
|
completionHandler:(RCTImageLoaderCompletionBlock)completionHandler
|
||||||
{
|
{
|
||||||
CGImageSourceRef imageSource = CGImageSourceCreateWithData((CFDataRef)imageData, NULL);
|
CGImageSourceRef imageSource = CGImageSourceCreateWithData((CFDataRef)imageData, NULL);
|
||||||
NSDictionary *properties = (__bridge_transfer NSDictionary *)CGImageSourceCopyProperties(imageSource, NULL);
|
NSDictionary *properties = (__bridge_transfer NSDictionary *)CGImageSourceCopyProperties(imageSource, NULL);
|
||||||
NSUInteger loopCount = [properties[(id)kCGImagePropertyGIFDictionary][(id)kCGImagePropertyGIFLoopCount] unsignedIntegerValue];
|
NSUInteger loopCount = [properties[(id)kCGImagePropertyGIFDictionary][(id)kCGImagePropertyGIFLoopCount] unsignedIntegerValue];
|
||||||
|
|
||||||
|
UIImage *image = nil;
|
||||||
size_t imageCount = CGImageSourceGetCount(imageSource);
|
size_t imageCount = CGImageSourceGetCount(imageSource);
|
||||||
NSTimeInterval duration = 0;
|
if (imageCount > 1) {
|
||||||
NSMutableArray *delays = [NSMutableArray arrayWithCapacity:imageCount];
|
|
||||||
NSMutableArray *images = [NSMutableArray arrayWithCapacity:imageCount];
|
|
||||||
for (size_t i = 0; i < imageCount; i++) {
|
|
||||||
CGImageRef image = CGImageSourceCreateImageAtIndex(imageSource, i, NULL);
|
|
||||||
NSDictionary *frameProperties = (__bridge_transfer NSDictionary *)CGImageSourceCopyPropertiesAtIndex(imageSource, i, NULL);
|
|
||||||
NSDictionary *frameGIFProperties = frameProperties[(id)kCGImagePropertyGIFDictionary];
|
|
||||||
|
|
||||||
const NSTimeInterval kDelayTimeIntervalDefault = 0.1;
|
NSTimeInterval duration = 0;
|
||||||
NSNumber *delayTime = frameGIFProperties[(id)kCGImagePropertyGIFUnclampedDelayTime] ?: frameGIFProperties[(id)kCGImagePropertyGIFDelayTime];
|
NSMutableArray *delays = [NSMutableArray arrayWithCapacity:imageCount];
|
||||||
if (delayTime == nil) {
|
NSMutableArray *images = [NSMutableArray arrayWithCapacity:imageCount];
|
||||||
if (i == 0) {
|
for (size_t i = 0; i < imageCount; i++) {
|
||||||
delayTime = @(kDelayTimeIntervalDefault);
|
|
||||||
} else {
|
CGImageRef imageRef = CGImageSourceCreateImageAtIndex(imageSource, i, NULL);
|
||||||
delayTime = delays[i - 1];
|
if (!image) {
|
||||||
|
image = [UIImage imageWithCGImage:imageRef];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NSDictionary *frameProperties = (__bridge_transfer NSDictionary *)CGImageSourceCopyPropertiesAtIndex(imageSource, i, NULL);
|
||||||
|
NSDictionary *frameGIFProperties = frameProperties[(id)kCGImagePropertyGIFDictionary];
|
||||||
|
|
||||||
|
const NSTimeInterval kDelayTimeIntervalDefault = 0.1;
|
||||||
|
NSNumber *delayTime = frameGIFProperties[(id)kCGImagePropertyGIFUnclampedDelayTime] ?: frameGIFProperties[(id)kCGImagePropertyGIFDelayTime];
|
||||||
|
if (delayTime == nil) {
|
||||||
|
if (i == 0) {
|
||||||
|
delayTime = @(kDelayTimeIntervalDefault);
|
||||||
|
} else {
|
||||||
|
delayTime = delays[i - 1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const NSTimeInterval kDelayTimeIntervalMinimum = 0.02;
|
||||||
|
if (delayTime.floatValue < (float)kDelayTimeIntervalMinimum - FLT_EPSILON) {
|
||||||
|
delayTime = @(kDelayTimeIntervalDefault);
|
||||||
|
}
|
||||||
|
|
||||||
|
duration += delayTime.doubleValue;
|
||||||
|
delays[i] = delayTime;
|
||||||
|
images[i] = (__bridge_transfer id)imageRef;
|
||||||
|
}
|
||||||
|
CFRelease(imageSource);
|
||||||
|
|
||||||
|
NSMutableArray *keyTimes = [NSMutableArray arrayWithCapacity:delays.count];
|
||||||
|
NSTimeInterval runningDuration = 0;
|
||||||
|
for (NSNumber *delayNumber in delays) {
|
||||||
|
[keyTimes addObject:@(runningDuration / duration)];
|
||||||
|
runningDuration += delayNumber.doubleValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const NSTimeInterval kDelayTimeIntervalMinimum = 0.02;
|
[keyTimes addObject:@1.0];
|
||||||
if (delayTime.floatValue < (float)kDelayTimeIntervalMinimum - FLT_EPSILON) {
|
|
||||||
delayTime = @(kDelayTimeIntervalDefault);
|
// Create animation
|
||||||
|
CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:@"contents"];
|
||||||
|
animation.calculationMode = kCAAnimationDiscrete;
|
||||||
|
animation.repeatCount = loopCount == 0 ? HUGE_VALF : loopCount;
|
||||||
|
animation.keyTimes = keyTimes;
|
||||||
|
animation.values = images;
|
||||||
|
animation.duration = duration;
|
||||||
|
image.reactKeyframeAnimation = animation;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
// Don't bother creating an animation
|
||||||
|
CGImageRef imageRef = CGImageSourceCreateImageAtIndex(imageSource, 0, NULL);
|
||||||
|
if (imageRef) {
|
||||||
|
image = [UIImage imageWithCGImage:imageRef];
|
||||||
|
CFRelease(imageRef);
|
||||||
}
|
}
|
||||||
|
CFRelease(imageSource);
|
||||||
duration += delayTime.doubleValue;
|
|
||||||
delays[i] = delayTime;
|
|
||||||
images[i] = (__bridge_transfer id)image;
|
|
||||||
}
|
|
||||||
CFRelease(imageSource);
|
|
||||||
|
|
||||||
NSMutableArray *keyTimes = [NSMutableArray arrayWithCapacity:delays.count];
|
|
||||||
NSTimeInterval runningDuration = 0;
|
|
||||||
for (NSNumber *delayNumber in delays) {
|
|
||||||
[keyTimes addObject:@(runningDuration / duration)];
|
|
||||||
runningDuration += delayNumber.doubleValue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[keyTimes addObject:@1.0];
|
completionHandler(nil, image);
|
||||||
|
return ^{};
|
||||||
CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:@"contents"];
|
|
||||||
animation.calculationMode = kCAAnimationDiscrete;
|
|
||||||
animation.repeatCount = loopCount == 0 ? HUGE_VALF : loopCount;
|
|
||||||
animation.keyTimes = keyTimes;
|
|
||||||
animation.values = images;
|
|
||||||
animation.duration = duration;
|
|
||||||
completionHandler(nil, animation);
|
|
||||||
|
|
||||||
return nil;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
|
@ -11,14 +11,10 @@
|
||||||
1304D5AC1AA8C4A30002E2BE /* RCTImageViewManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 1304D5AA1AA8C4A30002E2BE /* RCTImageViewManager.m */; };
|
1304D5AC1AA8C4A30002E2BE /* RCTImageViewManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 1304D5AA1AA8C4A30002E2BE /* RCTImageViewManager.m */; };
|
||||||
1304D5B21AA8C50D0002E2BE /* RCTGIFImageDecoder.m in Sources */ = {isa = PBXBuildFile; fileRef = 1304D5B11AA8C50D0002E2BE /* RCTGIFImageDecoder.m */; };
|
1304D5B21AA8C50D0002E2BE /* RCTGIFImageDecoder.m in Sources */ = {isa = PBXBuildFile; fileRef = 1304D5B11AA8C50D0002E2BE /* RCTGIFImageDecoder.m */; };
|
||||||
134B00A21B54232B00EC8DFB /* RCTImageUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = 134B00A11B54232B00EC8DFB /* RCTImageUtils.m */; };
|
134B00A21B54232B00EC8DFB /* RCTImageUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = 134B00A11B54232B00EC8DFB /* RCTImageUtils.m */; };
|
||||||
137620351B31C53500677FF0 /* RCTImagePickerManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 137620341B31C53500677FF0 /* RCTImagePickerManager.m */; };
|
|
||||||
143879351AAD238D00F088A5 /* RCTCameraRollManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 143879341AAD238D00F088A5 /* RCTCameraRollManager.m */; };
|
|
||||||
143879381AAD32A300F088A5 /* RCTImageLoader.m in Sources */ = {isa = PBXBuildFile; fileRef = 143879371AAD32A300F088A5 /* RCTImageLoader.m */; };
|
143879381AAD32A300F088A5 /* RCTImageLoader.m in Sources */ = {isa = PBXBuildFile; fileRef = 143879371AAD32A300F088A5 /* RCTImageLoader.m */; };
|
||||||
35123E6B1B59C99D00EBAD80 /* RCTImageStoreManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 35123E6A1B59C99D00EBAD80 /* RCTImageStoreManager.m */; };
|
35123E6B1B59C99D00EBAD80 /* RCTImageStoreManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 35123E6A1B59C99D00EBAD80 /* RCTImageStoreManager.m */; };
|
||||||
354631681B69857700AA0B86 /* RCTImageEditingManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 354631671B69857700AA0B86 /* RCTImageEditingManager.m */; };
|
354631681B69857700AA0B86 /* RCTImageEditingManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 354631671B69857700AA0B86 /* RCTImageEditingManager.m */; };
|
||||||
58B5118F1A9E6BD600147676 /* RCTImageDownloader.m in Sources */ = {isa = PBXBuildFile; fileRef = 58B5118A1A9E6BD600147676 /* RCTImageDownloader.m */; };
|
58B5118F1A9E6BD600147676 /* RCTImageDownloader.m in Sources */ = {isa = PBXBuildFile; fileRef = 58B5118A1A9E6BD600147676 /* RCTImageDownloader.m */; };
|
||||||
8312EAEE1B85EB7C001867A2 /* RCTAssetsLibraryImageLoader.m in Sources */ = {isa = PBXBuildFile; fileRef = 8312EAED1B85EB7C001867A2 /* RCTAssetsLibraryImageLoader.m */; };
|
|
||||||
8312EAF11B85F071001867A2 /* RCTPhotoLibraryImageLoader.m in Sources */ = {isa = PBXBuildFile; fileRef = 8312EAF01B85F071001867A2 /* RCTPhotoLibraryImageLoader.m */; };
|
|
||||||
83DDA1571B8DCA5800892A1C /* RCTAssetBundleImageLoader.m in Sources */ = {isa = PBXBuildFile; fileRef = 83DDA1561B8DCA5800892A1C /* RCTAssetBundleImageLoader.m */; };
|
83DDA1571B8DCA5800892A1C /* RCTAssetBundleImageLoader.m in Sources */ = {isa = PBXBuildFile; fileRef = 83DDA1561B8DCA5800892A1C /* RCTAssetBundleImageLoader.m */; };
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
|
@ -43,10 +39,6 @@
|
||||||
1304D5B11AA8C50D0002E2BE /* RCTGIFImageDecoder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTGIFImageDecoder.m; sourceTree = "<group>"; };
|
1304D5B11AA8C50D0002E2BE /* RCTGIFImageDecoder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTGIFImageDecoder.m; sourceTree = "<group>"; };
|
||||||
134B00A01B54232B00EC8DFB /* RCTImageUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTImageUtils.h; sourceTree = "<group>"; };
|
134B00A01B54232B00EC8DFB /* RCTImageUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTImageUtils.h; sourceTree = "<group>"; };
|
||||||
134B00A11B54232B00EC8DFB /* RCTImageUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTImageUtils.m; sourceTree = "<group>"; };
|
134B00A11B54232B00EC8DFB /* RCTImageUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTImageUtils.m; sourceTree = "<group>"; };
|
||||||
137620331B31C53500677FF0 /* RCTImagePickerManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTImagePickerManager.h; sourceTree = "<group>"; };
|
|
||||||
137620341B31C53500677FF0 /* RCTImagePickerManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTImagePickerManager.m; sourceTree = "<group>"; };
|
|
||||||
143879331AAD238D00F088A5 /* RCTCameraRollManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTCameraRollManager.h; sourceTree = "<group>"; };
|
|
||||||
143879341AAD238D00F088A5 /* RCTCameraRollManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTCameraRollManager.m; sourceTree = "<group>"; };
|
|
||||||
143879361AAD32A300F088A5 /* RCTImageLoader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTImageLoader.h; sourceTree = "<group>"; };
|
143879361AAD32A300F088A5 /* RCTImageLoader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTImageLoader.h; sourceTree = "<group>"; };
|
||||||
143879371AAD32A300F088A5 /* RCTImageLoader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTImageLoader.m; sourceTree = "<group>"; };
|
143879371AAD32A300F088A5 /* RCTImageLoader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTImageLoader.m; sourceTree = "<group>"; };
|
||||||
35123E691B59C99D00EBAD80 /* RCTImageStoreManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTImageStoreManager.h; sourceTree = "<group>"; };
|
35123E691B59C99D00EBAD80 /* RCTImageStoreManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTImageStoreManager.h; sourceTree = "<group>"; };
|
||||||
|
@ -56,10 +48,6 @@
|
||||||
58B5115D1A9E6B3D00147676 /* libRCTImage.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRCTImage.a; sourceTree = BUILT_PRODUCTS_DIR; };
|
58B5115D1A9E6B3D00147676 /* libRCTImage.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRCTImage.a; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
58B511891A9E6BD600147676 /* RCTImageDownloader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTImageDownloader.h; sourceTree = "<group>"; };
|
58B511891A9E6BD600147676 /* RCTImageDownloader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTImageDownloader.h; sourceTree = "<group>"; };
|
||||||
58B5118A1A9E6BD600147676 /* RCTImageDownloader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTImageDownloader.m; sourceTree = "<group>"; };
|
58B5118A1A9E6BD600147676 /* RCTImageDownloader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTImageDownloader.m; sourceTree = "<group>"; };
|
||||||
8312EAEC1B85EB7C001867A2 /* RCTAssetsLibraryImageLoader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTAssetsLibraryImageLoader.h; sourceTree = "<group>"; };
|
|
||||||
8312EAED1B85EB7C001867A2 /* RCTAssetsLibraryImageLoader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTAssetsLibraryImageLoader.m; sourceTree = "<group>"; };
|
|
||||||
8312EAEF1B85F071001867A2 /* RCTPhotoLibraryImageLoader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTPhotoLibraryImageLoader.h; sourceTree = "<group>"; };
|
|
||||||
8312EAF01B85F071001867A2 /* RCTPhotoLibraryImageLoader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTPhotoLibraryImageLoader.m; sourceTree = "<group>"; };
|
|
||||||
83DDA1551B8DCA5800892A1C /* RCTAssetBundleImageLoader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTAssetBundleImageLoader.h; sourceTree = "<group>"; };
|
83DDA1551B8DCA5800892A1C /* RCTAssetBundleImageLoader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTAssetBundleImageLoader.h; sourceTree = "<group>"; };
|
||||||
83DDA1561B8DCA5800892A1C /* RCTAssetBundleImageLoader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTAssetBundleImageLoader.m; sourceTree = "<group>"; };
|
83DDA1561B8DCA5800892A1C /* RCTAssetBundleImageLoader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTAssetBundleImageLoader.m; sourceTree = "<group>"; };
|
||||||
/* End PBXFileReference section */
|
/* End PBXFileReference section */
|
||||||
|
@ -80,10 +68,6 @@
|
||||||
children = (
|
children = (
|
||||||
83DDA1551B8DCA5800892A1C /* RCTAssetBundleImageLoader.h */,
|
83DDA1551B8DCA5800892A1C /* RCTAssetBundleImageLoader.h */,
|
||||||
83DDA1561B8DCA5800892A1C /* RCTAssetBundleImageLoader.m */,
|
83DDA1561B8DCA5800892A1C /* RCTAssetBundleImageLoader.m */,
|
||||||
8312EAEC1B85EB7C001867A2 /* RCTAssetsLibraryImageLoader.h */,
|
|
||||||
8312EAED1B85EB7C001867A2 /* RCTAssetsLibraryImageLoader.m */,
|
|
||||||
143879331AAD238D00F088A5 /* RCTCameraRollManager.h */,
|
|
||||||
143879341AAD238D00F088A5 /* RCTCameraRollManager.m */,
|
|
||||||
1304D5B01AA8C50D0002E2BE /* RCTGIFImageDecoder.h */,
|
1304D5B01AA8C50D0002E2BE /* RCTGIFImageDecoder.h */,
|
||||||
1304D5B11AA8C50D0002E2BE /* RCTGIFImageDecoder.m */,
|
1304D5B11AA8C50D0002E2BE /* RCTGIFImageDecoder.m */,
|
||||||
58B511891A9E6BD600147676 /* RCTImageDownloader.h */,
|
58B511891A9E6BD600147676 /* RCTImageDownloader.h */,
|
||||||
|
@ -92,8 +76,6 @@
|
||||||
354631671B69857700AA0B86 /* RCTImageEditingManager.m */,
|
354631671B69857700AA0B86 /* RCTImageEditingManager.m */,
|
||||||
143879361AAD32A300F088A5 /* RCTImageLoader.h */,
|
143879361AAD32A300F088A5 /* RCTImageLoader.h */,
|
||||||
143879371AAD32A300F088A5 /* RCTImageLoader.m */,
|
143879371AAD32A300F088A5 /* RCTImageLoader.m */,
|
||||||
137620331B31C53500677FF0 /* RCTImagePickerManager.h */,
|
|
||||||
137620341B31C53500677FF0 /* RCTImagePickerManager.m */,
|
|
||||||
1304D5A71AA8C4A30002E2BE /* RCTImageView.h */,
|
1304D5A71AA8C4A30002E2BE /* RCTImageView.h */,
|
||||||
1304D5A81AA8C4A30002E2BE /* RCTImageView.m */,
|
1304D5A81AA8C4A30002E2BE /* RCTImageView.m */,
|
||||||
1304D5A91AA8C4A30002E2BE /* RCTImageViewManager.h */,
|
1304D5A91AA8C4A30002E2BE /* RCTImageViewManager.h */,
|
||||||
|
@ -102,8 +84,6 @@
|
||||||
35123E6A1B59C99D00EBAD80 /* RCTImageStoreManager.m */,
|
35123E6A1B59C99D00EBAD80 /* RCTImageStoreManager.m */,
|
||||||
134B00A01B54232B00EC8DFB /* RCTImageUtils.h */,
|
134B00A01B54232B00EC8DFB /* RCTImageUtils.h */,
|
||||||
134B00A11B54232B00EC8DFB /* RCTImageUtils.m */,
|
134B00A11B54232B00EC8DFB /* RCTImageUtils.m */,
|
||||||
8312EAEF1B85F071001867A2 /* RCTPhotoLibraryImageLoader.h */,
|
|
||||||
8312EAF01B85F071001867A2 /* RCTPhotoLibraryImageLoader.m */,
|
|
||||||
58B5115E1A9E6B3D00147676 /* Products */,
|
58B5115E1A9E6B3D00147676 /* Products */,
|
||||||
);
|
);
|
||||||
indentWidth = 2;
|
indentWidth = 2;
|
||||||
|
@ -174,14 +154,10 @@
|
||||||
isa = PBXSourcesBuildPhase;
|
isa = PBXSourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
8312EAEE1B85EB7C001867A2 /* RCTAssetsLibraryImageLoader.m in Sources */,
|
|
||||||
35123E6B1B59C99D00EBAD80 /* RCTImageStoreManager.m in Sources */,
|
35123E6B1B59C99D00EBAD80 /* RCTImageStoreManager.m in Sources */,
|
||||||
8312EAF11B85F071001867A2 /* RCTPhotoLibraryImageLoader.m in Sources */,
|
|
||||||
58B5118F1A9E6BD600147676 /* RCTImageDownloader.m in Sources */,
|
58B5118F1A9E6BD600147676 /* RCTImageDownloader.m in Sources */,
|
||||||
137620351B31C53500677FF0 /* RCTImagePickerManager.m in Sources */,
|
|
||||||
1304D5AC1AA8C4A30002E2BE /* RCTImageViewManager.m in Sources */,
|
1304D5AC1AA8C4A30002E2BE /* RCTImageViewManager.m in Sources */,
|
||||||
1304D5B21AA8C50D0002E2BE /* RCTGIFImageDecoder.m in Sources */,
|
1304D5B21AA8C50D0002E2BE /* RCTGIFImageDecoder.m in Sources */,
|
||||||
143879351AAD238D00F088A5 /* RCTCameraRollManager.m in Sources */,
|
|
||||||
143879381AAD32A300F088A5 /* RCTImageLoader.m in Sources */,
|
143879381AAD32A300F088A5 /* RCTImageLoader.m in Sources */,
|
||||||
354631681B69857700AA0B86 /* RCTImageEditingManager.m in Sources */,
|
354631681B69857700AA0B86 /* RCTImageEditingManager.m in Sources */,
|
||||||
1304D5AB1AA8C4A30002E2BE /* RCTImageView.m in Sources */,
|
1304D5AB1AA8C4A30002E2BE /* RCTImageView.m in Sources */,
|
||||||
|
|
|
@ -50,7 +50,7 @@ RCT_EXPORT_MODULE()
|
||||||
*/
|
*/
|
||||||
- (RCTImageLoaderCancellationBlock)downloadDataForURL:(NSURL *)url
|
- (RCTImageLoaderCancellationBlock)downloadDataForURL:(NSURL *)url
|
||||||
progressHandler:(RCTImageLoaderProgressBlock)progressBlock
|
progressHandler:(RCTImageLoaderProgressBlock)progressBlock
|
||||||
completionHandler:(RCTImageLoaderCompletionBlock)completionBlock
|
completionHandler:(void (^)(NSError *error, NSData *data))completionBlock
|
||||||
{
|
{
|
||||||
if (![_bridge respondsToSelector:NSSelectorFromString(@"networking")]) {
|
if (![_bridge respondsToSelector:NSSelectorFromString(@"networking")]) {
|
||||||
RCTLogError(@"You need to import the RCTNetworking library in order to download remote images.");
|
RCTLogError(@"You need to import the RCTNetworking library in order to download remote images.");
|
||||||
|
|
|
@ -15,10 +15,15 @@
|
||||||
@class ALAssetsLibrary;
|
@class ALAssetsLibrary;
|
||||||
|
|
||||||
typedef void (^RCTImageLoaderProgressBlock)(int64_t progress, int64_t total);
|
typedef void (^RCTImageLoaderProgressBlock)(int64_t progress, int64_t total);
|
||||||
typedef void (^RCTImageLoaderCompletionBlock)(NSError *error, id image /* UIImage or CAAnimation */);
|
typedef void (^RCTImageLoaderCompletionBlock)(NSError *error, UIImage *image);
|
||||||
typedef void (^RCTImageLoaderCompletionBlock)(NSError *error, id image /* NSData, UIImage, CAAnimation */);
|
|
||||||
typedef void (^RCTImageLoaderCancellationBlock)(void);
|
typedef void (^RCTImageLoaderCancellationBlock)(void);
|
||||||
|
|
||||||
|
@interface UIImage (React)
|
||||||
|
|
||||||
|
@property (nonatomic, copy) CAKeyframeAnimation *reactKeyframeAnimation;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
@interface RCTImageLoader : NSObject <RCTBridgeModule, RCTURLRequestHandler>
|
@interface RCTImageLoader : NSObject <RCTBridgeModule, RCTURLRequestHandler>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -28,6 +28,20 @@ static void RCTDispatchCallbackOnMainQueue(void (^callback)(NSError *, id), NSEr
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@implementation UIImage (React)
|
||||||
|
|
||||||
|
- (CAKeyframeAnimation *)reactKeyframeAnimation
|
||||||
|
{
|
||||||
|
return objc_getAssociatedObject(self, _cmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setReactKeyframeAnimation:(CAKeyframeAnimation *)reactKeyframeAnimation
|
||||||
|
{
|
||||||
|
objc_setAssociatedObject(self, @selector(reactKeyframeAnimation), reactKeyframeAnimation, OBJC_ASSOCIATION_COPY_NONATOMIC);
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
@implementation RCTImageLoader
|
@implementation RCTImageLoader
|
||||||
|
|
||||||
@synthesize bridge = _bridge;
|
@synthesize bridge = _bridge;
|
||||||
|
@ -99,7 +113,7 @@ RCT_EXPORT_MODULE()
|
||||||
progressBlock(progress, total);
|
progressBlock(progress, total);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} completionHandler:^(NSError *error, id image) {
|
} completionHandler:^(NSError *error, UIImage *image) {
|
||||||
RCTDispatchCallbackOnMainQueue(completionBlock, error, image);
|
RCTDispatchCallbackOnMainQueue(completionBlock, error, image);
|
||||||
}] ?: ^{};
|
}] ?: ^{};
|
||||||
}
|
}
|
||||||
|
@ -142,7 +156,7 @@ RCT_EXPORT_MODULE()
|
||||||
{
|
{
|
||||||
id<RCTImageDecoder> imageDecoder = [self imageDecoderForRequest:data];
|
id<RCTImageDecoder> imageDecoder = [self imageDecoderForRequest:data];
|
||||||
if (imageDecoder) {
|
if (imageDecoder) {
|
||||||
return [imageDecoder decodeImageData:data size:size scale:scale resizeMode:resizeMode completionHandler:^(NSError *error, id image) {
|
return [imageDecoder decodeImageData:data size:size scale:scale resizeMode:resizeMode completionHandler:^(NSError *error, UIImage *image) {
|
||||||
RCTDispatchCallbackOnMainQueue(completionBlock, error, image);
|
RCTDispatchCallbackOnMainQueue(completionBlock, error, image);
|
||||||
}];
|
}];
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -31,6 +31,7 @@
|
||||||
@implementation RCTImageView
|
@implementation RCTImageView
|
||||||
{
|
{
|
||||||
RCTBridge *_bridge;
|
RCTBridge *_bridge;
|
||||||
|
CGSize _targetSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (instancetype)initWithBridge:(RCTBridge *)bridge
|
- (instancetype)initWithBridge:(RCTBridge *)bridge
|
||||||
|
@ -142,10 +143,10 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init)
|
||||||
scale:RCTScreenScale()
|
scale:RCTScreenScale()
|
||||||
resizeMode:self.contentMode
|
resizeMode:self.contentMode
|
||||||
progressBlock:progressHandler
|
progressBlock:progressHandler
|
||||||
completionBlock:^(NSError *error, id image) {
|
completionBlock:^(NSError *error, UIImage *image) {
|
||||||
|
|
||||||
if ([image isKindOfClass:[CAAnimation class]]) {
|
if (image.reactKeyframeAnimation) {
|
||||||
[self.layer addAnimation:image forKey:@"contents"];
|
[self.layer addAnimation:image.reactKeyframeAnimation forKey:@"contents"];
|
||||||
} else {
|
} else {
|
||||||
[self.layer removeAnimationForKey:@"contents"];
|
[self.layer removeAnimationForKey:@"contents"];
|
||||||
self.image = image;
|
self.image = image;
|
||||||
|
@ -173,19 +174,17 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init)
|
||||||
{
|
{
|
||||||
[super reactSetFrame:frame];
|
[super reactSetFrame:frame];
|
||||||
if (self.image == nil) {
|
if (self.image == nil) {
|
||||||
|
_targetSize = frame.size;
|
||||||
[self reloadImage];
|
[self reloadImage];
|
||||||
} else if ([RCTImageView srcNeedsReload:_src]) {
|
} else if ([RCTImageView srcNeedsReload:_src]) {
|
||||||
|
|
||||||
// Get optimal image size
|
|
||||||
CGSize currentSize = self.image.size;
|
|
||||||
CGSize idealSize = RCTTargetSize(self.image.size, self.image.scale, frame.size,
|
CGSize idealSize = RCTTargetSize(self.image.size, self.image.scale, frame.size,
|
||||||
RCTScreenScale(), self.contentMode, YES);
|
RCTScreenScale(), self.contentMode, YES);
|
||||||
|
CGFloat widthChangeFraction = ABS(_targetSize.width - idealSize.width) / _targetSize.width;
|
||||||
CGFloat widthChangeFraction = ABS(currentSize.width - idealSize.width) / currentSize.width;
|
CGFloat heightChangeFraction = ABS(_targetSize.height - idealSize.height) / _targetSize.height;
|
||||||
CGFloat heightChangeFraction = ABS(currentSize.height - idealSize.height) / currentSize.height;
|
|
||||||
|
|
||||||
// If the combined change is more than 20%, reload the asset in case there is a better size.
|
// If the combined change is more than 20%, reload the asset in case there is a better size.
|
||||||
if (widthChangeFraction + heightChangeFraction > 0.2) {
|
if (widthChangeFraction + heightChangeFraction > 0.2) {
|
||||||
|
_targetSize = idealSize;
|
||||||
[self reloadImage];
|
[self reloadImage];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,6 +33,7 @@ class Modal extends React.Component {
|
||||||
<RCTModalHostView
|
<RCTModalHostView
|
||||||
animated={this.props.animated}
|
animated={this.props.animated}
|
||||||
transparent={this.props.transparent}
|
transparent={this.props.transparent}
|
||||||
|
onDismiss={this.props.onDismiss}
|
||||||
style={styles.modal}>
|
style={styles.modal}>
|
||||||
<View style={[styles.container, containerBackgroundColor]}>
|
<View style={[styles.container, containerBackgroundColor]}>
|
||||||
{this.props.children}
|
{this.props.children}
|
||||||
|
@ -45,6 +46,7 @@ class Modal extends React.Component {
|
||||||
Modal.propTypes = {
|
Modal.propTypes = {
|
||||||
animated: PropTypes.bool,
|
animated: PropTypes.bool,
|
||||||
transparent: PropTypes.bool,
|
transparent: PropTypes.bool,
|
||||||
|
onDismiss: PropTypes.func,
|
||||||
};
|
};
|
||||||
|
|
||||||
var styles = StyleSheet.create({
|
var styles = StyleSheet.create({
|
||||||
|
|
|
@ -138,6 +138,23 @@ type ConnectivityStateAndroid = $Enum<{
|
||||||
|
|
||||||
var _subscriptions = new Map();
|
var _subscriptions = new Map();
|
||||||
|
|
||||||
|
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';
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
var _isConnectedSubscriptions = new Map();
|
||||||
|
|
||||||
var NetInfo = {
|
var NetInfo = {
|
||||||
addEventListener: function (
|
addEventListener: function (
|
||||||
eventName: ChangeEventName,
|
eventName: ChangeEventName,
|
||||||
|
@ -175,60 +192,41 @@ var NetInfo = {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
isConnected: {},
|
isConnected: {
|
||||||
|
addEventListener: function (
|
||||||
|
eventName: ChangeEventName,
|
||||||
|
handler: Function
|
||||||
|
): void {
|
||||||
|
var listener = (connection) => {
|
||||||
|
handler(_isConnected(connection));
|
||||||
|
};
|
||||||
|
_isConnectedSubscriptions.set(handler, listener);
|
||||||
|
NetInfo.addEventListener(
|
||||||
|
eventName,
|
||||||
|
listener
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
isConnectionMetered: {},
|
removeEventListener: function(
|
||||||
};
|
eventName: ChangeEventName,
|
||||||
|
handler: Function
|
||||||
|
): void {
|
||||||
|
var listener = _isConnectedSubscriptions.get(handler);
|
||||||
|
NetInfo.removeEventListener(
|
||||||
|
eventName,
|
||||||
|
listener
|
||||||
|
);
|
||||||
|
_isConnectedSubscriptions.delete(handler);
|
||||||
|
},
|
||||||
|
|
||||||
if (Platform.OS === 'ios') {
|
fetch: function(): Promise {
|
||||||
var _isConnected = function(
|
return NetInfo.fetch().then(
|
||||||
reachability: ReachabilityStateIOS
|
(connection) => _isConnected(connection)
|
||||||
): bool {
|
);
|
||||||
return reachability !== 'none' &&
|
},
|
||||||
reachability !== 'unknown';
|
|
||||||
};
|
|
||||||
} else if (Platform.OS === 'android') {
|
|
||||||
var _isConnected = function(
|
|
||||||
connectionType: ConnectivityStateAndroid
|
|
||||||
): bool {
|
|
||||||
return connectionType !== 'NONE' && connectionType !== 'UNKNOWN';
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
var _isConnectedSubscriptions = new Map();
|
|
||||||
|
|
||||||
NetInfo.isConnected = {
|
|
||||||
addEventListener: function (
|
|
||||||
eventName: ChangeEventName,
|
|
||||||
handler: Function
|
|
||||||
): void {
|
|
||||||
var listener = (connection) => {
|
|
||||||
handler(_isConnected(connection));
|
|
||||||
};
|
|
||||||
_isConnectedSubscriptions.set(handler, listener);
|
|
||||||
NetInfo.addEventListener(
|
|
||||||
eventName,
|
|
||||||
listener
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
removeEventListener: function(
|
isConnectionMetered: ({}: {} | (callback:Function) => void),
|
||||||
eventName: ChangeEventName,
|
|
||||||
handler: Function
|
|
||||||
): void {
|
|
||||||
var listener = _isConnectedSubscriptions.get(handler);
|
|
||||||
NetInfo.removeEventListener(
|
|
||||||
eventName,
|
|
||||||
listener
|
|
||||||
);
|
|
||||||
_isConnectedSubscriptions.delete(handler);
|
|
||||||
},
|
|
||||||
|
|
||||||
fetch: function(): Promise {
|
|
||||||
return NetInfo.fetch().then(
|
|
||||||
(connection) => _isConnected(connection)
|
|
||||||
);
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if (Platform.OS === 'android') {
|
if (Platform.OS === 'android') {
|
||||||
|
|
|
@ -56,6 +56,7 @@ RCT_EXPORT_MODULE()
|
||||||
// Lazy setup
|
// Lazy setup
|
||||||
if (!_session && [self isValid]) {
|
if (!_session && [self isValid]) {
|
||||||
NSOperationQueue *callbackQueue = [NSOperationQueue new];
|
NSOperationQueue *callbackQueue = [NSOperationQueue new];
|
||||||
|
callbackQueue.maxConcurrentOperationCount = 1;
|
||||||
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
|
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
|
||||||
_session = [NSURLSession sessionWithConfiguration:configuration
|
_session = [NSURLSession sessionWithConfiguration:configuration
|
||||||
delegate:self
|
delegate:self
|
||||||
|
|
|
@ -111,6 +111,12 @@ var styles = StyleSheet.create({
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
var RCTPickerIOS = requireNativeComponent('RCTPicker', null);
|
var RCTPickerIOS = requireNativeComponent('RCTPicker', PickerIOS, {
|
||||||
|
nativeOnly: {
|
||||||
|
items: true,
|
||||||
|
onChange: true,
|
||||||
|
selectedIndex: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
module.exports = PickerIOS;
|
module.exports = PickerIOS;
|
||||||
|
|
|
@ -16,13 +16,8 @@
|
||||||
#import "RCTTestModule.h"
|
#import "RCTTestModule.h"
|
||||||
#import "RCTUtils.h"
|
#import "RCTUtils.h"
|
||||||
|
|
||||||
#define TIMEOUT_SECONDS 60
|
static const NSTimeInterval kTestTimeoutSeconds = 60;
|
||||||
|
static const NSTimeInterval kTestTeardownTimeoutSeconds = 30;
|
||||||
@interface RCTBridge (RCTTestRunner)
|
|
||||||
|
|
||||||
@property (nonatomic, weak) RCTBridge *batchedBridge;
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation RCTTestRunner
|
@implementation RCTTestRunner
|
||||||
{
|
{
|
||||||
|
@ -49,7 +44,7 @@
|
||||||
_scriptURL = [[NSBundle bundleForClass:[RCTBridge class]] URLForResource:@"main" withExtension:@"jsbundle"];
|
_scriptURL = [[NSBundle bundleForClass:[RCTBridge class]] URLForResource:@"main" withExtension:@"jsbundle"];
|
||||||
RCTAssert(_scriptURL != nil, @"Could not locate main.jsBundle");
|
RCTAssert(_scriptURL != nil, @"Could not locate main.jsBundle");
|
||||||
#else
|
#else
|
||||||
_scriptURL = [NSURL URLWithString:[NSString stringWithFormat:@"http://localhost:8081/%@.bundle?dev=true&platform=ios", app]];
|
_scriptURL = [NSURL URLWithString:[NSString stringWithFormat:@"http://localhost:8081/%@.bundle?platform=ios&dev=true", app]];
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
return self;
|
return self;
|
||||||
|
@ -83,52 +78,69 @@ RCT_NOT_IMPLEMENTED(- (instancetype)init)
|
||||||
- (void)runTest:(SEL)test module:(NSString *)moduleName
|
- (void)runTest:(SEL)test module:(NSString *)moduleName
|
||||||
initialProps:(NSDictionary *)initialProps expectErrorBlock:(BOOL(^)(NSString *error))expectErrorBlock
|
initialProps:(NSDictionary *)initialProps expectErrorBlock:(BOOL(^)(NSString *error))expectErrorBlock
|
||||||
{
|
{
|
||||||
__block NSString *error = nil;
|
__weak id weakJSContext;
|
||||||
RCTSetLogFunction(^(RCTLogLevel level, NSString *fileName, NSNumber *lineNumber, NSString *message) {
|
|
||||||
if (level >= RCTLogLevelError) {
|
@autoreleasepool {
|
||||||
error = message;
|
__block NSString *error = nil;
|
||||||
|
RCTSetLogFunction(^(RCTLogLevel level, NSString *fileName, NSNumber *lineNumber, NSString *message) {
|
||||||
|
if (level >= RCTLogLevelError) {
|
||||||
|
error = message;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
RCTBridge *bridge = [[RCTBridge alloc] initWithBundleURL:_scriptURL
|
||||||
|
moduleProvider:_moduleProvider
|
||||||
|
launchOptions:nil];
|
||||||
|
|
||||||
|
RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge moduleName:moduleName initialProperties:initialProps];
|
||||||
|
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];
|
||||||
|
RCTAssert(_testController != nil, @"_testController should not be nil");
|
||||||
|
testModule.controller = _testController;
|
||||||
|
testModule.testSelector = test;
|
||||||
|
testModule.view = rootView;
|
||||||
|
|
||||||
|
UIViewController *vc = [UIApplication sharedApplication].delegate.window.rootViewController;
|
||||||
|
vc.view = [UIView new];
|
||||||
|
[vc.view addSubview:rootView]; // Add as subview so it doesn't get resized
|
||||||
|
|
||||||
|
NSDate *date = [NSDate dateWithTimeIntervalSinceNow:kTestTimeoutSeconds];
|
||||||
|
while (date.timeIntervalSinceNow > 0 && testModule.status == RCTTestStatusPending && error == nil) {
|
||||||
|
[[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
|
||||||
|
[[NSRunLoop mainRunLoop] runMode:NSRunLoopCommonModes beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
RCTBridge *bridge = [[RCTBridge alloc] initWithBundleURL:_scriptURL
|
// Take a weak reference to the JS context, so we track its deallocation later
|
||||||
moduleProvider:_moduleProvider
|
// (we can only do this now, since it's been lazily initialized)
|
||||||
launchOptions:nil];
|
weakJSContext = [[[bridge valueForKey:@"batchedBridge"] valueForKey:@"javaScriptExecutor"] valueForKey:@"context"];
|
||||||
|
[rootView removeFromSuperview];
|
||||||
|
|
||||||
RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge moduleName:moduleName initialProperties:initialProps];
|
RCTSetLogFunction(RCTDefaultLogFunction);
|
||||||
rootView.frame = CGRectMake(0, 0, 320, 2000); // Constant size for testing on multiple devices
|
|
||||||
|
|
||||||
NSString *testModuleName = RCTBridgeModuleNameForClass([RCTTestModule class]);
|
NSArray *nonLayoutSubviews = [vc.view.subviews filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id subview, NSDictionary *bindings) {
|
||||||
RCTTestModule *testModule = rootView.bridge.batchedBridge.modules[testModuleName];
|
return ![NSStringFromClass([subview class]) isEqualToString:@"_UILayoutGuide"];
|
||||||
RCTAssert(_testController != nil, @"_testController should not be nil");
|
}]];
|
||||||
testModule.controller = _testController;
|
RCTAssert(nonLayoutSubviews.count == 0, @"There shouldn't be any other views: %@", nonLayoutSubviews);
|
||||||
testModule.testSelector = test;
|
|
||||||
testModule.view = rootView;
|
|
||||||
|
|
||||||
UIViewController *vc = [UIApplication sharedApplication].delegate.window.rootViewController;
|
if (expectErrorBlock) {
|
||||||
vc.view = [UIView new];
|
RCTAssert(expectErrorBlock(error), @"Expected an error but nothing matched.");
|
||||||
[vc.view addSubview:rootView]; // Add as subview so it doesn't get resized
|
} else {
|
||||||
|
RCTAssert(error == nil, @"RedBox error: %@", error);
|
||||||
|
RCTAssert(testModule.status != RCTTestStatusPending, @"Test didn't finish within %0.f seconds", kTestTimeoutSeconds);
|
||||||
|
RCTAssert(testModule.status == RCTTestStatusPassed, @"Test failed");
|
||||||
|
}
|
||||||
|
[bridge invalidate];
|
||||||
|
}
|
||||||
|
|
||||||
NSDate *date = [NSDate dateWithTimeIntervalSinceNow:TIMEOUT_SECONDS];
|
// Wait for the executor to have shut down completely before returning
|
||||||
while (date.timeIntervalSinceNow > 0 && testModule.status == RCTTestStatusPending && error == nil) {
|
NSDate *teardownTimeout = [NSDate dateWithTimeIntervalSinceNow:kTestTeardownTimeoutSeconds];
|
||||||
|
while (teardownTimeout.timeIntervalSinceNow > 0 && weakJSContext) {
|
||||||
[[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
|
[[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
|
||||||
[[NSRunLoop mainRunLoop] runMode:NSRunLoopCommonModes beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
|
[[NSRunLoop mainRunLoop] runMode:NSRunLoopCommonModes beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
|
||||||
}
|
}
|
||||||
[rootView removeFromSuperview];
|
RCTAssert(!weakJSContext, @"JS context was not deallocated after being invalidated");
|
||||||
|
|
||||||
RCTSetLogFunction(RCTDefaultLogFunction);
|
|
||||||
|
|
||||||
NSArray *nonLayoutSubviews = [vc.view.subviews filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(id subview, NSDictionary *bindings) {
|
|
||||||
return ![NSStringFromClass([subview class]) isEqualToString:@"_UILayoutGuide"];
|
|
||||||
}]];
|
|
||||||
RCTAssert(nonLayoutSubviews.count == 0, @"There shouldn't be any other views: %@", nonLayoutSubviews);
|
|
||||||
|
|
||||||
if (expectErrorBlock) {
|
|
||||||
RCTAssert(expectErrorBlock(error), @"Expected an error but nothing matched.");
|
|
||||||
} else {
|
|
||||||
RCTAssert(error == nil, @"RedBox error: %@", error);
|
|
||||||
RCTAssert(testModule.status != RCTTestStatusPending, @"Test didn't finish within %d seconds", TIMEOUT_SECONDS);
|
|
||||||
RCTAssert(testModule.status == RCTTestStatusPassed, @"Test failed");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
/**
|
||||||
|
* 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 StyleSheetTypes
|
||||||
|
* @flow
|
||||||
|
*/
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
type Atom = number | bool | Object | Array<?Atom>;
|
||||||
|
export type StyleObj = Atom | Array<?StyleObj>;
|
|
@ -14,8 +14,7 @@
|
||||||
var StyleSheetRegistry = require('StyleSheetRegistry');
|
var StyleSheetRegistry = require('StyleSheetRegistry');
|
||||||
var invariant = require('invariant');
|
var invariant = require('invariant');
|
||||||
|
|
||||||
type Atom = number | bool | Object | Array<?Atom>
|
import type { StyleObj } from 'StyleSheetTypes';
|
||||||
type StyleObj = Atom | Array<?StyleObj>
|
|
||||||
|
|
||||||
function getStyle(style) {
|
function getStyle(style) {
|
||||||
if (typeof style === 'number') {
|
if (typeof style === 'number') {
|
||||||
|
|
|
@ -117,8 +117,15 @@ RCT_EXTERN NSArray *RCTGetModuleClasses(void);
|
||||||
|
|
||||||
dispatch_group_t initModulesAndLoadSource = dispatch_group_create();
|
dispatch_group_t initModulesAndLoadSource = dispatch_group_create();
|
||||||
dispatch_group_enter(initModulesAndLoadSource);
|
dispatch_group_enter(initModulesAndLoadSource);
|
||||||
|
__weak RCTBatchedBridge *weakSelf = self;
|
||||||
__block NSString *sourceCode;
|
__block NSString *sourceCode;
|
||||||
[self loadSource:^(__unused NSError *error, NSString *source) {
|
[self loadSource:^(NSError *error, NSString *source) {
|
||||||
|
if (error) {
|
||||||
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
|
[weakSelf stopLoadingWithError:error];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
sourceCode = source;
|
sourceCode = source;
|
||||||
dispatch_group_leave(initModulesAndLoadSource);
|
dispatch_group_leave(initModulesAndLoadSource);
|
||||||
}];
|
}];
|
||||||
|
@ -131,7 +138,6 @@ RCT_EXTERN NSArray *RCTGetModuleClasses(void);
|
||||||
RCTProfileHookModules(self);
|
RCTProfileHookModules(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
__weak RCTBatchedBridge *weakSelf = self;
|
|
||||||
__block NSString *config;
|
__block NSString *config;
|
||||||
dispatch_group_enter(initModulesAndLoadSource);
|
dispatch_group_enter(initModulesAndLoadSource);
|
||||||
dispatch_async(bridgeQueue, ^{
|
dispatch_async(bridgeQueue, ^{
|
||||||
|
@ -150,16 +156,24 @@ RCT_EXTERN NSArray *RCTGetModuleClasses(void);
|
||||||
// We're not waiting for this complete to leave the dispatch group, since
|
// We're not waiting for this complete to leave the dispatch group, since
|
||||||
// injectJSONConfiguration and executeSourceCode will schedule operations on the
|
// injectJSONConfiguration and executeSourceCode will schedule operations on the
|
||||||
// same queue anyway.
|
// same queue anyway.
|
||||||
[weakSelf injectJSONConfiguration:config onComplete:^(__unused NSError *error) {
|
[weakSelf injectJSONConfiguration:config onComplete:^(NSError *error) {
|
||||||
RCTPerformanceLoggerEnd(RCTPLNativeModuleInit);
|
RCTPerformanceLoggerEnd(RCTPLNativeModuleInit);
|
||||||
|
if (error) {
|
||||||
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
|
[weakSelf stopLoadingWithError:error];
|
||||||
|
});
|
||||||
|
}
|
||||||
}];
|
}];
|
||||||
dispatch_group_leave(initModulesAndLoadSource);
|
dispatch_group_leave(initModulesAndLoadSource);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
dispatch_group_notify(initModulesAndLoadSource, bridgeQueue, ^{
|
dispatch_group_notify(initModulesAndLoadSource, dispatch_get_main_queue(), ^{
|
||||||
if (sourceCode) {
|
RCTBatchedBridge *strongSelf = weakSelf;
|
||||||
[weakSelf executeSourceCode:sourceCode];
|
if (sourceCode && strongSelf.loading) {
|
||||||
|
dispatch_async(bridgeQueue, ^{
|
||||||
|
[weakSelf executeSourceCode:sourceCode];
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -172,23 +186,6 @@ RCT_EXTERN NSArray *RCTGetModuleClasses(void);
|
||||||
RCTSourceLoadBlock onSourceLoad = ^(NSError *error, NSString *source) {
|
RCTSourceLoadBlock onSourceLoad = ^(NSError *error, NSString *source) {
|
||||||
RCTProfileEndAsyncEvent(0, @"init,download", cookie, @"JavaScript download", nil);
|
RCTProfileEndAsyncEvent(0, @"init,download", cookie, @"JavaScript download", nil);
|
||||||
RCTPerformanceLoggerEnd(RCTPLScriptDownload);
|
RCTPerformanceLoggerEnd(RCTPLScriptDownload);
|
||||||
|
|
||||||
if (error) {
|
|
||||||
NSArray *stack = error.userInfo[@"stack"];
|
|
||||||
if (stack) {
|
|
||||||
[self.redBox showErrorMessage:error.localizedDescription
|
|
||||||
withStack:stack];
|
|
||||||
} else {
|
|
||||||
[self.redBox showErrorMessage:error.localizedDescription
|
|
||||||
withDetails:error.localizedFailureReason];
|
|
||||||
}
|
|
||||||
|
|
||||||
NSDictionary *userInfo = @{@"bridge": self, @"error": error};
|
|
||||||
[[NSNotificationCenter defaultCenter] postNotificationName:RCTJavaScriptDidFailToLoadNotification
|
|
||||||
object:_parentBridge
|
|
||||||
userInfo:userInfo];
|
|
||||||
}
|
|
||||||
|
|
||||||
_onSourceLoad(error, source);
|
_onSourceLoad(error, source);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -283,7 +280,6 @@ RCT_EXTERN NSArray *RCTGetModuleClasses(void);
|
||||||
object:self];
|
object:self];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
- (void)setupExecutor
|
- (void)setupExecutor
|
||||||
{
|
{
|
||||||
[_javaScriptExecutor setUp];
|
[_javaScriptExecutor setUp];
|
||||||
|
@ -313,12 +309,7 @@ RCT_EXTERN NSArray *RCTGetModuleClasses(void);
|
||||||
|
|
||||||
[_javaScriptExecutor injectJSONText:configJSON
|
[_javaScriptExecutor injectJSONText:configJSON
|
||||||
asGlobalObjectNamed:@"__fbBatchedBridgeConfig"
|
asGlobalObjectNamed:@"__fbBatchedBridgeConfig"
|
||||||
callback:^(NSError *error) {
|
callback:onComplete];
|
||||||
if (error) {
|
|
||||||
[self.redBox showError:error];
|
|
||||||
}
|
|
||||||
onComplete(error);
|
|
||||||
}];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)executeSourceCode:(NSString *)sourceCode
|
- (void)executeSourceCode:(NSString *)sourceCode
|
||||||
|
@ -333,7 +324,9 @@ RCT_EXTERN NSArray *RCTGetModuleClasses(void);
|
||||||
|
|
||||||
[self enqueueApplicationScript:sourceCode url:self.bundleURL onComplete:^(NSError *loadError) {
|
[self enqueueApplicationScript:sourceCode url:self.bundleURL onComplete:^(NSError *loadError) {
|
||||||
if (loadError) {
|
if (loadError) {
|
||||||
[self.redBox showError:loadError];
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
|
[self stopLoadingWithError:loadError];
|
||||||
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -352,6 +345,28 @@ RCT_EXTERN NSArray *RCTGetModuleClasses(void);
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)stopLoadingWithError:(NSError *)error
|
||||||
|
{
|
||||||
|
RCTAssertMainThread();
|
||||||
|
|
||||||
|
if (!self.isValid || !self.loading) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_loading = NO;
|
||||||
|
|
||||||
|
NSArray *stack = error.userInfo[@"stack"];
|
||||||
|
if (stack) {
|
||||||
|
[self.redBox showErrorMessage:error.localizedDescription withStack:stack];
|
||||||
|
} else {
|
||||||
|
[self.redBox showError:error];
|
||||||
|
}
|
||||||
|
|
||||||
|
NSDictionary *userInfo = @{@"bridge": self, @"error": error};
|
||||||
|
[[NSNotificationCenter defaultCenter] postNotificationName:RCTJavaScriptDidFailToLoadNotification
|
||||||
|
object:_parentBridge
|
||||||
|
userInfo:userInfo];
|
||||||
|
}
|
||||||
|
|
||||||
RCT_NOT_IMPLEMENTED(- (instancetype)initWithBundleURL:(__unused NSURL *)bundleURL
|
RCT_NOT_IMPLEMENTED(- (instancetype)initWithBundleURL:(__unused NSURL *)bundleURL
|
||||||
moduleProvider:(__unused RCTBridgeModuleProviderBlock)block
|
moduleProvider:(__unused RCTBridgeModuleProviderBlock)block
|
||||||
|
|
|
@ -13,7 +13,6 @@
|
||||||
|
|
||||||
#import "RCTAssert.h"
|
#import "RCTAssert.h"
|
||||||
#import "RCTBridge.h"
|
#import "RCTBridge.h"
|
||||||
#import "RCTContextExecutor.h"
|
|
||||||
#import "RCTEventDispatcher.h"
|
#import "RCTEventDispatcher.h"
|
||||||
#import "RCTKeyCommands.h"
|
#import "RCTKeyCommands.h"
|
||||||
#import "RCTLog.h"
|
#import "RCTLog.h"
|
||||||
|
@ -246,6 +245,7 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:(NSCoder *)aDecoder)
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RCT_NOT_IMPLEMENTED(-(instancetype)initWithFrame:(CGRect)frame)
|
||||||
RCT_NOT_IMPLEMENTED(-(instancetype)initWithCoder:(nonnull NSCoder *)aDecoder)
|
RCT_NOT_IMPLEMENTED(-(instancetype)initWithCoder:(nonnull NSCoder *)aDecoder)
|
||||||
|
|
||||||
- (void)insertReactSubview:(id<RCTComponent>)subview atIndex:(NSInteger)atIndex
|
- (void)insertReactSubview:(id<RCTComponent>)subview atIndex:(NSInteger)atIndex
|
||||||
|
|
|
@ -23,7 +23,7 @@
|
||||||
#import "RCTUtils.h"
|
#import "RCTUtils.h"
|
||||||
|
|
||||||
#ifndef RCT_JSC_PROFILER
|
#ifndef RCT_JSC_PROFILER
|
||||||
#if RCT_DEV && RCT_DEBUG
|
#if RCT_DEV
|
||||||
#define RCT_JSC_PROFILER 1
|
#define RCT_JSC_PROFILER 1
|
||||||
#else
|
#else
|
||||||
#define RCT_JSC_PROFILER 0
|
#define RCT_JSC_PROFILER 0
|
||||||
|
@ -34,7 +34,7 @@
|
||||||
#include <dlfcn.h>
|
#include <dlfcn.h>
|
||||||
|
|
||||||
#ifndef RCT_JSC_PROFILER_DYLIB
|
#ifndef RCT_JSC_PROFILER_DYLIB
|
||||||
#define RCT_JSC_PROFILER_DYLIB [[[NSBundle mainBundle] pathForResource:[NSString stringWithFormat:@"RCTJSCProfiler.ios%zd", [[[UIDevice currentDevice] systemVersion] integerValue]] ofType:@"dylib" inDirectory:@"Frameworks"] UTF8String]
|
#define RCT_JSC_PROFILER_DYLIB [[[NSBundle mainBundle] pathForResource:[NSString stringWithFormat:@"RCTJSCProfiler.ios%zd", [[[UIDevice currentDevice] systemVersion] integerValue]] ofType:@"dylib" inDirectory:@"RCTJSCProfiler"] UTF8String]
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -11,4 +11,6 @@
|
||||||
|
|
||||||
@interface RCTDevLoadingView : NSObject <RCTBridgeModule>
|
@interface RCTDevLoadingView : NSObject <RCTBridgeModule>
|
||||||
|
|
||||||
|
+ (void)setEnabled:(BOOL)enabled;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
|
@ -16,6 +16,8 @@
|
||||||
|
|
||||||
#if RCT_DEV
|
#if RCT_DEV
|
||||||
|
|
||||||
|
static BOOL isEnabled = YES;
|
||||||
|
|
||||||
@implementation RCTDevLoadingView
|
@implementation RCTDevLoadingView
|
||||||
{
|
{
|
||||||
UIWindow *_window;
|
UIWindow *_window;
|
||||||
|
@ -27,6 +29,11 @@
|
||||||
|
|
||||||
RCT_EXPORT_MODULE()
|
RCT_EXPORT_MODULE()
|
||||||
|
|
||||||
|
+ (void)setEnabled:(BOOL)enabled
|
||||||
|
{
|
||||||
|
isEnabled = enabled;
|
||||||
|
}
|
||||||
|
|
||||||
- (instancetype)init
|
- (instancetype)init
|
||||||
{
|
{
|
||||||
if ((self = [super init])) {
|
if ((self = [super init])) {
|
||||||
|
@ -57,6 +64,10 @@ RCT_EXPORT_MODULE()
|
||||||
|
|
||||||
- (void)showWithURL:(NSURL *)URL
|
- (void)showWithURL:(NSURL *)URL
|
||||||
{
|
{
|
||||||
|
if (!isEnabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
|
|
||||||
_showDate = [NSDate date];
|
_showDate = [NSDate date];
|
||||||
|
@ -90,6 +101,10 @@ RCT_EXPORT_MODULE()
|
||||||
|
|
||||||
- (void)hide
|
- (void)hide
|
||||||
{
|
{
|
||||||
|
if (!isEnabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
|
|
||||||
const NSTimeInterval MIN_PRESENTED_TIME = 0.6;
|
const NSTimeInterval MIN_PRESENTED_TIME = 0.6;
|
||||||
|
@ -117,6 +132,7 @@ RCT_EXPORT_MODULE()
|
||||||
@implementation RCTDevLoadingView
|
@implementation RCTDevLoadingView
|
||||||
|
|
||||||
+ (NSString *)moduleName { return nil; }
|
+ (NSString *)moduleName { return nil; }
|
||||||
|
+ (void)setEnabled:(BOOL)enabled { }
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
|
|
@ -488,6 +488,11 @@ extern NSString *RCTBridgeModuleNameForClass(Class cls);
|
||||||
// Perform layout (possibly animated)
|
// Perform layout (possibly animated)
|
||||||
return ^(__unused RCTUIManager *uiManager, RCTSparseArray *viewRegistry) {
|
return ^(__unused RCTUIManager *uiManager, RCTSparseArray *viewRegistry) {
|
||||||
RCTResponseSenderBlock callback = self->_layoutAnimation.callback;
|
RCTResponseSenderBlock callback = self->_layoutAnimation.callback;
|
||||||
|
|
||||||
|
// It's unsafe to call this callback more than once, so we nil it out here
|
||||||
|
// to make sure that doesn't happen.
|
||||||
|
_layoutAnimation.callback = nil;
|
||||||
|
|
||||||
__block NSUInteger completionsCalled = 0;
|
__block NSUInteger completionsCalled = 0;
|
||||||
for (NSUInteger ii = 0; ii < frames.count; ii++) {
|
for (NSUInteger ii = 0; ii < frames.count; ii++) {
|
||||||
NSNumber *reactTag = frameReactTags[ii];
|
NSNumber *reactTag = frameReactTags[ii];
|
||||||
|
|
|
@ -589,7 +589,7 @@
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
shellPath = /bin/sh;
|
shellPath = /bin/sh;
|
||||||
shellScript = "if [[ \"$CONFIGURATION\" == \"Debug\" ]] && [[ -d \"/tmp/RCTJSCProfiler\" ]]; then\n find \"${CONFIGURATION_BUILD_DIR}\" -name '*.app' | xargs -I{} sh -c 'mkdir -p \"$1/Frameworks\" && cp -r /tmp/RCTJSCProfiler/* \"$1/Frameworks\"' -- {}\nfi";
|
shellScript = "if [[ \"$CONFIGURATION\" == \"Debug\" ]] && [[ -d \"/tmp/RCTJSCProfiler\" ]]; then\n find \"${CONFIGURATION_BUILD_DIR}\" -name '*.app' | xargs -I{} sh -c 'cp -r /tmp/RCTJSCProfiler \"$1\"' -- {}\nfi";
|
||||||
showEnvVarsInLog = 0;
|
showEnvVarsInLog = 0;
|
||||||
};
|
};
|
||||||
/* End PBXShellScriptBuildPhase section */
|
/* End PBXShellScriptBuildPhase section */
|
||||||
|
|
|
@ -17,4 +17,13 @@
|
||||||
@property (nonatomic, assign, readwrite) UIEdgeInsets contentInset;
|
@property (nonatomic, assign, readwrite) UIEdgeInsets contentInset;
|
||||||
@property (nonatomic, assign, readwrite) BOOL automaticallyAdjustContentInsets;
|
@property (nonatomic, assign, readwrite) BOOL automaticallyAdjustContentInsets;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Automatically adjusted content inset depends on view controller's top and bottom
|
||||||
|
* layout guides so if you've changed one of them (e.g. after rotation or manually) you should call this method
|
||||||
|
* to recalculate and refresh content inset.
|
||||||
|
* To handle case with changing navigation bar height call this method from viewDidLayoutSubviews:
|
||||||
|
* of your view controller.
|
||||||
|
*/
|
||||||
|
- (void)refreshContentInset;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
|
@ -24,6 +24,7 @@ RCT_EXPORT_MODULE()
|
||||||
|
|
||||||
RCT_EXPORT_VIEW_PROPERTY(items, NSDictionaryArray)
|
RCT_EXPORT_VIEW_PROPERTY(items, NSDictionaryArray)
|
||||||
RCT_EXPORT_VIEW_PROPERTY(selectedIndex, NSInteger)
|
RCT_EXPORT_VIEW_PROPERTY(selectedIndex, NSInteger)
|
||||||
|
RCT_EXPORT_VIEW_PROPERTY(onChange, RCTBubblingEventBlock)
|
||||||
|
|
||||||
- (NSDictionary *)constantsToExport
|
- (NSDictionary *)constantsToExport
|
||||||
{
|
{
|
||||||
|
|
|
@ -460,10 +460,6 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:(NSCoder *)aDecoder)
|
||||||
_scrollView.frame = self.bounds;
|
_scrollView.frame = self.bounds;
|
||||||
_scrollView.contentOffset = originalOffset;
|
_scrollView.contentOffset = originalOffset;
|
||||||
|
|
||||||
[RCTView autoAdjustInsetsForView:self
|
|
||||||
withScrollView:_scrollView
|
|
||||||
updateOffset:YES];
|
|
||||||
|
|
||||||
[self updateClippedSubviews];
|
[self updateClippedSubviews];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -523,6 +519,13 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:(NSCoder *)aDecoder)
|
||||||
[_scrollView zoomToRect:rect animated:animated];
|
[_scrollView zoomToRect:rect animated:animated];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)refreshContentInset
|
||||||
|
{
|
||||||
|
[RCTView autoAdjustInsetsForView:self
|
||||||
|
withScrollView:_scrollView
|
||||||
|
updateOffset:YES];
|
||||||
|
}
|
||||||
|
|
||||||
#pragma mark - ScrollView delegate
|
#pragma mark - ScrollView delegate
|
||||||
|
|
||||||
#define RCT_SCROLL_EVENT_HANDLER(delegateMethod, eventName) \
|
#define RCT_SCROLL_EVENT_HANDLER(delegateMethod, eventName) \
|
||||||
|
|
|
@ -95,9 +95,6 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:(NSCoder *)aDecoder)
|
||||||
{
|
{
|
||||||
[super layoutSubviews];
|
[super layoutSubviews];
|
||||||
_webView.frame = self.bounds;
|
_webView.frame = self.bounds;
|
||||||
[RCTView autoAdjustInsetsForView:self
|
|
||||||
withScrollView:_webView.scrollView
|
|
||||||
updateOffset:YES];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setContentInset:(UIEdgeInsets)contentInset
|
- (void)setContentInset:(UIEdgeInsets)contentInset
|
||||||
|
@ -133,6 +130,13 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:(NSCoder *)aDecoder)
|
||||||
return event;
|
return event;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)refreshContentInset
|
||||||
|
{
|
||||||
|
[RCTView autoAdjustInsetsForView:self
|
||||||
|
withScrollView:_webView.scrollView
|
||||||
|
updateOffset:YES];
|
||||||
|
}
|
||||||
|
|
||||||
#pragma mark - UIWebViewDelegate methods
|
#pragma mark - UIWebViewDelegate methods
|
||||||
|
|
||||||
- (BOOL)webView:(__unused UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request
|
- (BOOL)webView:(__unused UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request
|
||||||
|
|
|
@ -16,13 +16,15 @@
|
||||||
#import "RCTUtils.h"
|
#import "RCTUtils.h"
|
||||||
#import "RCTViewControllerProtocol.h"
|
#import "RCTViewControllerProtocol.h"
|
||||||
#import "UIView+React.h"
|
#import "UIView+React.h"
|
||||||
|
#import "RCTAutoInsetsProtocol.h"
|
||||||
|
|
||||||
@implementation RCTWrapperViewController
|
@implementation RCTWrapperViewController
|
||||||
{
|
{
|
||||||
UIView *_wrapperView;
|
UIView *_wrapperView;
|
||||||
UIView *_contentView;
|
UIView *_contentView;
|
||||||
CGFloat _previousTopLayout;
|
RCTEventDispatcher *_eventDispatcher;
|
||||||
CGFloat _previousBottomLayout;
|
CGFloat _previousTopLayoutLength;
|
||||||
|
CGFloat _previousBottomLayoutLength;
|
||||||
}
|
}
|
||||||
|
|
||||||
@synthesize currentTopLayoutGuide = _currentTopLayoutGuide;
|
@synthesize currentTopLayoutGuide = _currentTopLayoutGuide;
|
||||||
|
@ -58,6 +60,32 @@ RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:(NSCoder *)aDecoder)
|
||||||
_currentBottomLayoutGuide = self.bottomLayoutGuide;
|
_currentBottomLayoutGuide = self.bottomLayoutGuide;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static BOOL RCTFindScrollViewAndRefreshContentInsetInView(UIView *view)
|
||||||
|
{
|
||||||
|
if ([view conformsToProtocol:@protocol(RCTAutoInsetsProtocol)]) {
|
||||||
|
[(id <RCTAutoInsetsProtocol>) view refreshContentInset];
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
for (UIView *subview in view.subviews) {
|
||||||
|
if (RCTFindScrollViewAndRefreshContentInsetInView(subview)) {
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)viewDidLayoutSubviews
|
||||||
|
{
|
||||||
|
[super viewDidLayoutSubviews];
|
||||||
|
|
||||||
|
if (_previousTopLayoutLength != _currentTopLayoutGuide.length ||
|
||||||
|
_previousBottomLayoutLength != _currentBottomLayoutGuide.length) {
|
||||||
|
RCTFindScrollViewAndRefreshContentInsetInView(_contentView);
|
||||||
|
_previousTopLayoutLength = _currentTopLayoutGuide.length;
|
||||||
|
_previousBottomLayoutLength = _currentBottomLayoutGuide.length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static UIView *RCTFindNavBarShadowViewInView(UIView *view)
|
static UIView *RCTFindNavBarShadowViewInView(UIView *view)
|
||||||
{
|
{
|
||||||
if ([view isKindOfClass:[UIImageView class]] && view.bounds.size.height <= 1) {
|
if ([view isKindOfClass:[UIImageView class]] && view.bounds.size.height <= 1) {
|
||||||
|
|
54
package.json
|
@ -47,42 +47,42 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"absolute-path": "0.0.0",
|
"absolute-path": "0.0.0",
|
||||||
"babel": "5.8.21",
|
"babel": "5.8.23",
|
||||||
"babel-core": "5.8.21",
|
"babel-core": "5.8.23",
|
||||||
"bser": "1.0.0",
|
"bser": "1.0.2",
|
||||||
"chalk": "1.0.0",
|
"chalk": "1.1.1",
|
||||||
"connect": "2.8.3",
|
"connect": "2.8.3",
|
||||||
"debug": "2.1.0",
|
"debug": "2.2.0",
|
||||||
"graceful-fs": "4.1.2",
|
"graceful-fs": "4.1.2",
|
||||||
"image-size": "0.3.5",
|
"image-size": "0.3.5",
|
||||||
"immutable": "^3.7.4",
|
"immutable": "3.7.5",
|
||||||
"joi": "5.1.0",
|
"joi": "6.6.1",
|
||||||
"jstransform": "11.0.1",
|
"jstransform": "11.0.3",
|
||||||
"module-deps": "3.5.6",
|
"module-deps": "3.9.1",
|
||||||
"optimist": "0.6.1",
|
"optimist": "0.6.1",
|
||||||
"progress": "^1.1.8",
|
"progress": "1.1.8",
|
||||||
"promise": "^7.0.3",
|
"promise": "7.0.4",
|
||||||
"react-timer-mixin": "^0.13.1",
|
"react-timer-mixin": "0.13.2",
|
||||||
"react-tools": "git://github.com/facebook/react#b4e74e38e43ac53af8acd62c78c9213be0194245",
|
"react-tools": "git://github.com/facebook/react#b4e74e38e43ac53af8acd62c78c9213be0194245",
|
||||||
"rebound": "^0.0.12",
|
"rebound": "0.0.13",
|
||||||
"regenerator": "0.8.36",
|
"regenerator": "0.8.36",
|
||||||
"sane": "^1.1.2",
|
"sane": "^1.2.0",
|
||||||
"semver": "^4.3.6",
|
"semver": "5.0.1",
|
||||||
"source-map": "0.1.31",
|
"source-map": "0.4.4",
|
||||||
"stacktrace-parser": "0.1.3",
|
"stacktrace-parser": "0.1.3",
|
||||||
"uglify-js": "2.4.16",
|
"uglify-js": "2.4.24",
|
||||||
"underscore": "1.7.0",
|
"underscore": "1.8.3",
|
||||||
"wordwrap": "^1.0.0",
|
"wordwrap": "1.0.0",
|
||||||
"worker-farm": "^1.3.1",
|
"worker-farm": "1.3.1",
|
||||||
"ws": "0.8.0",
|
"ws": "0.8.0",
|
||||||
"yargs": "1.3.2",
|
"yargs": "3.24.0",
|
||||||
"yeoman-environment": "^1.2.7",
|
"yeoman-environment": "1.2.7",
|
||||||
"yeoman-generator": "^0.20.2"
|
"yeoman-generator": "0.20.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"jest-cli": "0.5.0",
|
"jest-cli": "0.5.1",
|
||||||
"babel-eslint": "3.1.5",
|
"babel-eslint": "4.1.1",
|
||||||
"eslint": "0.21.2",
|
"eslint": "1.3.1",
|
||||||
"eslint-plugin-react": "2.3.0"
|
"eslint-plugin-react": "3.3.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,6 +59,14 @@ var options = parseCommandLine([{
|
||||||
type: 'string',
|
type: 'string',
|
||||||
default: require.resolve('./transformer.js'),
|
default: require.resolve('./transformer.js'),
|
||||||
description: 'Specify a custom transformer to be used (absolute path)'
|
description: 'Specify a custom transformer to be used (absolute path)'
|
||||||
|
}, {
|
||||||
|
command: 'resetCache',
|
||||||
|
description: 'Removes cached files',
|
||||||
|
default: false,
|
||||||
|
}, {
|
||||||
|
command: 'reset-cache',
|
||||||
|
description: 'Removes cached files',
|
||||||
|
default: false,
|
||||||
}]);
|
}]);
|
||||||
|
|
||||||
if (options.projectRoots) {
|
if (options.projectRoots) {
|
||||||
|
@ -229,6 +237,7 @@ function getAppMiddleware(options) {
|
||||||
transformModulePath: transformerPath,
|
transformModulePath: transformerPath,
|
||||||
assetRoots: options.assetRoots,
|
assetRoots: options.assetRoots,
|
||||||
assetExts: ['png', 'jpeg', 'jpg'],
|
assetExts: ['png', 'jpeg', 'jpg'],
|
||||||
|
resetCache: options.resetCache || options['reset-cache'],
|
||||||
polyfillModuleNames: [
|
polyfillModuleNames: [
|
||||||
require.resolve(
|
require.resolve(
|
||||||
'../Libraries/JavaScriptAppEngine/polyfills/document.js'
|
'../Libraries/JavaScriptAppEngine/polyfills/document.js'
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
jest
|
jest
|
||||||
|
.dontMock('../../lib/getPlatformExtension')
|
||||||
.dontMock('../../lib/getAssetDataFromName')
|
.dontMock('../../lib/getAssetDataFromName')
|
||||||
.dontMock('../');
|
.dontMock('../');
|
||||||
|
|
||||||
|
@ -47,6 +48,43 @@ describe('AssetServer', () => {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
pit('should work for the simple case with platform ext', () => {
|
||||||
|
const server = new AssetServer({
|
||||||
|
projectRoots: ['/root'],
|
||||||
|
assetExts: ['png'],
|
||||||
|
});
|
||||||
|
|
||||||
|
fs.__setMockFilesystem({
|
||||||
|
'root': {
|
||||||
|
imgs: {
|
||||||
|
'b.ios.png': 'b ios image',
|
||||||
|
'b.android.png': 'b android image',
|
||||||
|
'c.png': 'c general image',
|
||||||
|
'c.android.png': 'c android image',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return Promise.all([
|
||||||
|
server.get('imgs/b.png', 'ios').then(
|
||||||
|
data => expect(data).toBe('b ios image')
|
||||||
|
),
|
||||||
|
server.get('imgs/b.png', 'android').then(
|
||||||
|
data => expect(data).toBe('b android image')
|
||||||
|
),
|
||||||
|
server.get('imgs/c.png', 'android').then(
|
||||||
|
data => expect(data).toBe('c android image')
|
||||||
|
),
|
||||||
|
server.get('imgs/c.png', 'ios').then(
|
||||||
|
data => expect(data).toBe('c general image')
|
||||||
|
),
|
||||||
|
server.get('imgs/c.png').then(
|
||||||
|
data => expect(data).toBe('c general image')
|
||||||
|
),
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
pit('should work for the simple case with jpg', () => {
|
pit('should work for the simple case with jpg', () => {
|
||||||
const server = new AssetServer({
|
const server = new AssetServer({
|
||||||
projectRoots: ['/root'],
|
projectRoots: ['/root'],
|
||||||
|
@ -95,6 +133,37 @@ describe('AssetServer', () => {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
pit('should pick the bigger one with platform ext', () => {
|
||||||
|
const server = new AssetServer({
|
||||||
|
projectRoots: ['/root'],
|
||||||
|
assetExts: ['png'],
|
||||||
|
});
|
||||||
|
|
||||||
|
fs.__setMockFilesystem({
|
||||||
|
'root': {
|
||||||
|
imgs: {
|
||||||
|
'b@1x.png': 'b1 image',
|
||||||
|
'b@2x.png': 'b2 image',
|
||||||
|
'b@4x.png': 'b4 image',
|
||||||
|
'b@4.5x.png': 'b4.5 image',
|
||||||
|
'b@1x.ios.png': 'b1 ios image',
|
||||||
|
'b@2x.ios.png': 'b2 ios image',
|
||||||
|
'b@4x.ios.png': 'b4 ios image',
|
||||||
|
'b@4.5x.ios.png': 'b4.5 ios image',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return Promise.all([
|
||||||
|
server.get('imgs/b@3x.png').then(data =>
|
||||||
|
expect(data).toBe('b4 image')
|
||||||
|
),
|
||||||
|
server.get('imgs/b@3x.png', 'ios').then(data =>
|
||||||
|
expect(data).toBe('b4 ios image')
|
||||||
|
),
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
pit('should support multiple project roots', () => {
|
pit('should support multiple project roots', () => {
|
||||||
const server = new AssetServer({
|
const server = new AssetServer({
|
||||||
projectRoots: ['/root', '/root2'],
|
projectRoots: ['/root', '/root2'],
|
||||||
|
|
|
@ -20,7 +20,6 @@ const stat = Promise.denodeify(fs.stat);
|
||||||
const readDir = Promise.denodeify(fs.readdir);
|
const readDir = Promise.denodeify(fs.readdir);
|
||||||
const readFile = Promise.denodeify(fs.readFile);
|
const readFile = Promise.denodeify(fs.readFile);
|
||||||
|
|
||||||
|
|
||||||
const validateOpts = declareOpts({
|
const validateOpts = declareOpts({
|
||||||
projectRoots: {
|
projectRoots: {
|
||||||
type: 'array',
|
type: 'array',
|
||||||
|
@ -39,9 +38,9 @@ class AssetServer {
|
||||||
this._assetExts = opts.assetExts;
|
this._assetExts = opts.assetExts;
|
||||||
}
|
}
|
||||||
|
|
||||||
get(assetPath) {
|
get(assetPath, platform = null) {
|
||||||
const assetData = getAssetDataFromName(assetPath);
|
const assetData = getAssetDataFromName(assetPath);
|
||||||
return this._getAssetRecord(assetPath).then(record => {
|
return this._getAssetRecord(assetPath, platform).then(record => {
|
||||||
for (let i = 0; i < record.scales.length; i++) {
|
for (let i = 0; i < record.scales.length; i++) {
|
||||||
if (record.scales[i] >= assetData.resolution) {
|
if (record.scales[i] >= assetData.resolution) {
|
||||||
return readFile(record.files[i]);
|
return readFile(record.files[i]);
|
||||||
|
@ -52,14 +51,14 @@ class AssetServer {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
getAssetData(assetPath) {
|
getAssetData(assetPath, platform = null) {
|
||||||
const nameData = getAssetDataFromName(assetPath);
|
const nameData = getAssetDataFromName(assetPath);
|
||||||
const data = {
|
const data = {
|
||||||
name: nameData.name,
|
name: nameData.name,
|
||||||
type: nameData.type,
|
type: nameData.type,
|
||||||
};
|
};
|
||||||
|
|
||||||
return this._getAssetRecord(assetPath).then(record => {
|
return this._getAssetRecord(assetPath, platform).then(record => {
|
||||||
data.scales = record.scales;
|
data.scales = record.scales;
|
||||||
|
|
||||||
return Promise.all(
|
return Promise.all(
|
||||||
|
@ -85,9 +84,10 @@ class AssetServer {
|
||||||
* 1. We first parse the directory of the asset
|
* 1. We first parse the directory of the asset
|
||||||
* 2. We check to find a matching directory in one of the project roots
|
* 2. We check to find a matching directory in one of the project roots
|
||||||
* 3. We then build a map of all assets and their scales in this directory
|
* 3. We then build a map of all assets and their scales in this directory
|
||||||
* 4. Then pick the closest resolution (rounding up) to the requested one
|
* 4. Then try to pick platform-specific asset records
|
||||||
|
* 5. Then pick the closest resolution (rounding up) to the requested one
|
||||||
*/
|
*/
|
||||||
_getAssetRecord(assetPath) {
|
_getAssetRecord(assetPath, platform = null) {
|
||||||
const filename = path.basename(assetPath);
|
const filename = path.basename(assetPath);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -104,11 +104,20 @@ class AssetServer {
|
||||||
const files = res[1];
|
const files = res[1];
|
||||||
const assetData = getAssetDataFromName(filename);
|
const assetData = getAssetDataFromName(filename);
|
||||||
|
|
||||||
const map = this._buildAssetMap(dir, files);
|
const map = this._buildAssetMap(dir, files, platform);
|
||||||
const record = map[assetData.assetName];
|
|
||||||
|
let record;
|
||||||
|
if (platform != null){
|
||||||
|
record = map[getAssetKey(assetData.assetName, platform)] ||
|
||||||
|
map[assetData.assetName];
|
||||||
|
} else {
|
||||||
|
record = map[assetData.assetName];
|
||||||
|
}
|
||||||
|
|
||||||
if (!record) {
|
if (!record) {
|
||||||
throw new Error('Asset not found');
|
throw new Error(
|
||||||
|
`Asset not found: ${assetPath} for platform: ${platform}`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return record;
|
return record;
|
||||||
|
@ -141,9 +150,10 @@ class AssetServer {
|
||||||
const map = Object.create(null);
|
const map = Object.create(null);
|
||||||
assets.forEach(function(asset, i) {
|
assets.forEach(function(asset, i) {
|
||||||
const file = files[i];
|
const file = files[i];
|
||||||
let record = map[asset.assetName];
|
const assetKey = getAssetKey(asset.assetName, asset.platform);
|
||||||
|
let record = map[assetKey];
|
||||||
if (!record) {
|
if (!record) {
|
||||||
record = map[asset.assetName] = {
|
record = map[assetKey] = {
|
||||||
scales: [],
|
scales: [],
|
||||||
files: [],
|
files: [],
|
||||||
};
|
};
|
||||||
|
@ -151,6 +161,7 @@ class AssetServer {
|
||||||
|
|
||||||
let insertIndex;
|
let insertIndex;
|
||||||
const length = record.scales.length;
|
const length = record.scales.length;
|
||||||
|
|
||||||
for (insertIndex = 0; insertIndex < length; insertIndex++) {
|
for (insertIndex = 0; insertIndex < length; insertIndex++) {
|
||||||
if (asset.resolution < record.scales[insertIndex]) {
|
if (asset.resolution < record.scales[insertIndex]) {
|
||||||
break;
|
break;
|
||||||
|
@ -164,4 +175,12 @@ class AssetServer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getAssetKey(assetName, platform) {
|
||||||
|
if (platform != null) {
|
||||||
|
return `${assetName} : ${platform}`;
|
||||||
|
} else {
|
||||||
|
return assetName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = AssetServer;
|
module.exports = AssetServer;
|
||||||
|
|
|
@ -115,13 +115,18 @@ class Bundle {
|
||||||
getMinifiedSourceAndMap() {
|
getMinifiedSourceAndMap() {
|
||||||
this._assertFinalized();
|
this._assertFinalized();
|
||||||
|
|
||||||
|
if (this._minifiedSourceAndMap) {
|
||||||
|
return this._minifiedSourceAndMap;
|
||||||
|
}
|
||||||
|
|
||||||
const source = this._getSource();
|
const source = this._getSource();
|
||||||
try {
|
try {
|
||||||
return UglifyJS.minify(source, {
|
this._minifiedSourceAndMap = UglifyJS.minify(source, {
|
||||||
fromString: true,
|
fromString: true,
|
||||||
outSourceMap: 'bundle.js',
|
outSourceMap: 'bundle.js',
|
||||||
inSourceMap: this.getSourceMap(),
|
inSourceMap: this.getSourceMap(),
|
||||||
});
|
});
|
||||||
|
return this._minifiedSourceAndMap;
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
// Sometimes, when somebody is using a new syntax feature that we
|
// Sometimes, when somebody is using a new syntax feature that we
|
||||||
// don't yet have transform for, the untransformed line is sent to
|
// don't yet have transform for, the untransformed line is sent to
|
||||||
|
@ -186,6 +191,10 @@ class Bundle {
|
||||||
|
|
||||||
options = options || {};
|
options = options || {};
|
||||||
|
|
||||||
|
if (options.minify) {
|
||||||
|
return this.getMinifiedSourceAndMap().map;
|
||||||
|
}
|
||||||
|
|
||||||
if (this._shouldCombineSourceMaps) {
|
if (this._shouldCombineSourceMaps) {
|
||||||
return this._getCombinedSourceMaps(options);
|
return this._getCombinedSourceMaps(options);
|
||||||
}
|
}
|
||||||
|
|
|
@ -154,7 +154,7 @@ class Bundler {
|
||||||
bundle.setMainModuleId(result.mainModuleId);
|
bundle.setMainModuleId(result.mainModuleId);
|
||||||
return Promise.all(
|
return Promise.all(
|
||||||
result.dependencies.map(
|
result.dependencies.map(
|
||||||
module => this._transformModule(bundle, module).then(transformed => {
|
module => this._transformModule(bundle, module, platform).then(transformed => {
|
||||||
if (bar) {
|
if (bar) {
|
||||||
bar.tick();
|
bar.tick();
|
||||||
}
|
}
|
||||||
|
@ -182,13 +182,13 @@ class Bundler {
|
||||||
return this._resolver.getDependencies(main, { dev: isDev, platform });
|
return this._resolver.getDependencies(main, { dev: isDev, platform });
|
||||||
}
|
}
|
||||||
|
|
||||||
_transformModule(bundle, module) {
|
_transformModule(bundle, module, platform = null) {
|
||||||
let transform;
|
let transform;
|
||||||
|
|
||||||
if (module.isAsset_DEPRECATED()) {
|
if (module.isAsset_DEPRECATED()) {
|
||||||
transform = this.generateAssetModule_DEPRECATED(bundle, module);
|
transform = this.generateAssetModule_DEPRECATED(bundle, module);
|
||||||
} else if (module.isAsset()) {
|
} else if (module.isAsset()) {
|
||||||
transform = this.generateAssetModule(bundle, module);
|
transform = this.generateAssetModule(bundle, module, platform);
|
||||||
} else if (module.isJSON()) {
|
} else if (module.isJSON()) {
|
||||||
transform = generateJSONModule(module);
|
transform = generateJSONModule(module);
|
||||||
} else {
|
} else {
|
||||||
|
@ -243,12 +243,12 @@ class Bundler {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
generateAssetModule(bundle, module) {
|
generateAssetModule(bundle, module, platform = null) {
|
||||||
const relPath = getPathRelativeToRoot(this._projectRoots, module.path);
|
const relPath = getPathRelativeToRoot(this._projectRoots, module.path);
|
||||||
|
|
||||||
return Promise.all([
|
return Promise.all([
|
||||||
sizeOf(module.path),
|
sizeOf(module.path),
|
||||||
this._assetServer.getAssetData(relPath),
|
this._assetServer.getAssetData(relPath, platform),
|
||||||
]).then(function(res) {
|
]).then(function(res) {
|
||||||
const dimensions = res[0];
|
const dimensions = res[0];
|
||||||
const assetData = res[1];
|
const assetData = res[1];
|
||||||
|
|
|
@ -16,6 +16,7 @@ jest
|
||||||
.dontMock('../../crawlers')
|
.dontMock('../../crawlers')
|
||||||
.dontMock('../../crawlers/node')
|
.dontMock('../../crawlers/node')
|
||||||
.dontMock('../../replacePatterns')
|
.dontMock('../../replacePatterns')
|
||||||
|
.dontMock('../../../lib/getPlatformExtension')
|
||||||
.dontMock('../../../lib/getAssetDataFromName')
|
.dontMock('../../../lib/getAssetDataFromName')
|
||||||
.dontMock('../../fastfs')
|
.dontMock('../../fastfs')
|
||||||
.dontMock('../../AssetModule_DEPRECATED')
|
.dontMock('../../AssetModule_DEPRECATED')
|
||||||
|
@ -424,6 +425,90 @@ describe('DependencyGraph', function() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
pit('should respect platform extension in assets', function() {
|
||||||
|
var root = '/root';
|
||||||
|
fs.__setMockFilesystem({
|
||||||
|
'root': {
|
||||||
|
'index.js': [
|
||||||
|
'/**',
|
||||||
|
' * @providesModule index',
|
||||||
|
' */',
|
||||||
|
'require("./imgs/a.png");',
|
||||||
|
'require("./imgs/b.png");',
|
||||||
|
'require("./imgs/c.png");',
|
||||||
|
].join('\n'),
|
||||||
|
'imgs': {
|
||||||
|
'a@1.5x.ios.png': '',
|
||||||
|
'b@.7x.ios.png': '',
|
||||||
|
'c.ios.png': '',
|
||||||
|
'c@2x.ios.png': '',
|
||||||
|
},
|
||||||
|
'package.json': JSON.stringify({
|
||||||
|
name: 'rootPackage'
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var dgraph = new DependencyGraph({
|
||||||
|
roots: [root],
|
||||||
|
fileWatcher: fileWatcher,
|
||||||
|
assetExts: ['png', 'jpg'],
|
||||||
|
cache: cache,
|
||||||
|
});
|
||||||
|
|
||||||
|
dgraph.setup({ platform: 'ios' });
|
||||||
|
|
||||||
|
return getOrderedDependenciesAsJSON(dgraph, '/root/index.js').then(function(deps) {
|
||||||
|
expect(deps)
|
||||||
|
.toEqual([
|
||||||
|
{
|
||||||
|
id: 'index',
|
||||||
|
path: '/root/index.js',
|
||||||
|
dependencies: [
|
||||||
|
'./imgs/a.png',
|
||||||
|
'./imgs/b.png',
|
||||||
|
'./imgs/c.png',
|
||||||
|
],
|
||||||
|
isAsset: false,
|
||||||
|
isAsset_DEPRECATED: false,
|
||||||
|
isJSON: false,
|
||||||
|
isPolyfill: false,
|
||||||
|
resolution: undefined,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'rootPackage/imgs/a.png',
|
||||||
|
path: '/root/imgs/a@1.5x.ios.png',
|
||||||
|
resolution: 1.5,
|
||||||
|
dependencies: [],
|
||||||
|
isAsset: true,
|
||||||
|
isAsset_DEPRECATED: false,
|
||||||
|
isJSON: false,
|
||||||
|
isPolyfill: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'rootPackage/imgs/b.png',
|
||||||
|
path: '/root/imgs/b@.7x.ios.png',
|
||||||
|
resolution: 0.7,
|
||||||
|
dependencies: [],
|
||||||
|
isAsset: true,
|
||||||
|
isAsset_DEPRECATED: false,
|
||||||
|
isJSON: false,
|
||||||
|
isPolyfill: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'rootPackage/imgs/c.png',
|
||||||
|
path: '/root/imgs/c.ios.png',
|
||||||
|
resolution: 1,
|
||||||
|
dependencies: [],
|
||||||
|
isAsset: true,
|
||||||
|
isAsset_DEPRECATED: false,
|
||||||
|
isJSON: false,
|
||||||
|
isPolyfill: false,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
pit('Deprecated and relative assets can live together', function() {
|
pit('Deprecated and relative assets can live together', function() {
|
||||||
var root = '/root';
|
var root = '/root';
|
||||||
fs.__setMockFilesystem({
|
fs.__setMockFilesystem({
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
/**
|
/**
|
||||||
* Copyright (c) 2015-present, Facebook, Inc.
|
* Copyright (c) 2015-present, Facebook, Inc.
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
|
@ -17,6 +17,7 @@ const crawl = require('../crawlers');
|
||||||
const debug = require('debug')('DependencyGraph');
|
const debug = require('debug')('DependencyGraph');
|
||||||
const declareOpts = require('../../lib/declareOpts');
|
const declareOpts = require('../../lib/declareOpts');
|
||||||
const getAssetDataFromName = require('../../lib/getAssetDataFromName');
|
const getAssetDataFromName = require('../../lib/getAssetDataFromName');
|
||||||
|
const getPontentialPlatformExt = require('../../lib/getPlatformExtension');
|
||||||
const isAbsolutePath = require('absolute-path');
|
const isAbsolutePath = require('absolute-path');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const util = require('util');
|
const util = require('util');
|
||||||
|
@ -274,7 +275,7 @@ class DependencyGraph {
|
||||||
|
|
||||||
// `platformExt` could be set in the `setup` method.
|
// `platformExt` could be set in the `setup` method.
|
||||||
if (!this._platformExt) {
|
if (!this._platformExt) {
|
||||||
const platformExt = getPlatformExt(entryPath);
|
const platformExt = getPontentialPlatformExt(entryPath);
|
||||||
if (platformExt && this._opts.platforms.indexOf(platformExt) > -1) {
|
if (platformExt && this._opts.platforms.indexOf(platformExt) > -1) {
|
||||||
this._platformExt = platformExt;
|
this._platformExt = platformExt;
|
||||||
} else {
|
} else {
|
||||||
|
@ -390,12 +391,18 @@ class DependencyGraph {
|
||||||
return Promise.resolve().then(() => {
|
return Promise.resolve().then(() => {
|
||||||
if (this._isAssetFile(potentialModulePath)) {
|
if (this._isAssetFile(potentialModulePath)) {
|
||||||
const {name, type} = getAssetDataFromName(potentialModulePath);
|
const {name, type} = getAssetDataFromName(potentialModulePath);
|
||||||
const pattern = new RegExp('^' + name + '(@[\\d\\.]+x)?\\.' + type);
|
|
||||||
|
let pattern = '^' + name + '(@[\\d\\.]+x)?';
|
||||||
|
if (this._platformExt != null) {
|
||||||
|
pattern += '(\\.' + this._platformExt + ')?';
|
||||||
|
}
|
||||||
|
pattern += '\\.' + type;
|
||||||
|
|
||||||
// We arbitrarly grab the first one, because scale selection
|
// We arbitrarly grab the first one, because scale selection
|
||||||
// will happen somewhere
|
// will happen somewhere
|
||||||
const [assetFile] = this._fastfs.matches(
|
const [assetFile] = this._fastfs.matches(
|
||||||
path.dirname(potentialModulePath),
|
path.dirname(potentialModulePath),
|
||||||
pattern
|
new RegExp(pattern)
|
||||||
);
|
);
|
||||||
|
|
||||||
if (assetFile) {
|
if (assetFile) {
|
||||||
|
@ -496,7 +503,7 @@ class DependencyGraph {
|
||||||
const modules = this._hasteMap[name];
|
const modules = this._hasteMap[name];
|
||||||
if (this._platformExt != null) {
|
if (this._platformExt != null) {
|
||||||
for (let i = 0; i < modules.length; i++) {
|
for (let i = 0; i < modules.length; i++) {
|
||||||
if (getPlatformExt(modules[i].path) === this._platformExt) {
|
if (getPontentialPlatformExt(modules[i].path) === this._platformExt) {
|
||||||
return modules[i];
|
return modules[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -662,15 +669,6 @@ function normalizePath(modulePath) {
|
||||||
return modulePath.replace(/\/$/, '');
|
return modulePath.replace(/\/$/, '');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extract platform extension: index.ios.js -> ios
|
|
||||||
function getPlatformExt(file) {
|
|
||||||
const parts = path.basename(file).split('.');
|
|
||||||
if (parts.length < 3) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return parts[parts.length - 2];
|
|
||||||
}
|
|
||||||
|
|
||||||
util.inherits(NotFoundError, Error);
|
util.inherits(NotFoundError, Error);
|
||||||
|
|
||||||
module.exports = DependencyGraph;
|
module.exports = DependencyGraph;
|
||||||
|
|
|
@ -245,8 +245,16 @@ describe('processRequest', () => {
|
||||||
expect(res.end).toBeCalledWith('i am image');
|
expect(res.end).toBeCalledWith('i am image');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return 404', () => {
|
it('should parse the platform option', () => {
|
||||||
|
const req = {url: '/assets/imgs/a.png?platform=ios'};
|
||||||
|
const res = {end: jest.genMockFn()};
|
||||||
|
|
||||||
|
AssetServer.prototype.get.mockImpl(() => Promise.resolve('i am image'));
|
||||||
|
|
||||||
|
server.processRequest(req, res);
|
||||||
|
jest.runAllTimers();
|
||||||
|
expect(AssetServer.prototype.get).toBeCalledWith('imgs/a.png', 'ios');
|
||||||
|
expect(res.end).toBeCalledWith('i am image');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -278,7 +278,8 @@ class Server {
|
||||||
_processAssetsRequest(req, res) {
|
_processAssetsRequest(req, res) {
|
||||||
const urlObj = url.parse(req.url, true);
|
const urlObj = url.parse(req.url, true);
|
||||||
const assetPath = urlObj.pathname.match(/^\/assets\/(.+)$/);
|
const assetPath = urlObj.pathname.match(/^\/assets\/(.+)$/);
|
||||||
this._assetServer.get(assetPath[1])
|
const assetEvent = Activity.startEvent(`processing asset request ${assetPath[1]}`);
|
||||||
|
this._assetServer.get(assetPath[1], urlObj.query.platform)
|
||||||
.then(
|
.then(
|
||||||
data => res.end(data),
|
data => res.end(data),
|
||||||
error => {
|
error => {
|
||||||
|
@ -286,7 +287,7 @@ class Server {
|
||||||
res.writeHead('404');
|
res.writeHead('404');
|
||||||
res.end('Asset not found');
|
res.end('Asset not found');
|
||||||
}
|
}
|
||||||
).done();
|
).done(() => Activity.endEvent(assetEvent));
|
||||||
}
|
}
|
||||||
|
|
||||||
_processProfile(req, res) {
|
_processProfile(req, res) {
|
||||||
|
@ -370,7 +371,9 @@ class Server {
|
||||||
res.end(bundleSource);
|
res.end(bundleSource);
|
||||||
Activity.endEvent(startReqEventId);
|
Activity.endEvent(startReqEventId);
|
||||||
} else if (requestType === 'map') {
|
} else if (requestType === 'map') {
|
||||||
var sourceMap = JSON.stringify(p.getSourceMap());
|
var sourceMap = JSON.stringify(p.getSourceMap({
|
||||||
|
minify: options.minify,
|
||||||
|
}));
|
||||||
res.setHeader('Content-Type', 'application/json');
|
res.setHeader('Content-Type', 'application/json');
|
||||||
res.end(sourceMap);
|
res.end(sourceMap);
|
||||||
Activity.endEvent(startReqEventId);
|
Activity.endEvent(startReqEventId);
|
||||||
|
|
|
@ -12,6 +12,7 @@ const Bundle = require('../Bundler/Bundle');
|
||||||
const Promise = require('promise');
|
const Promise = require('promise');
|
||||||
const bser = require('bser');
|
const bser = require('bser');
|
||||||
const debug = require('debug')('ReactPackager:SocketClient');
|
const debug = require('debug')('ReactPackager:SocketClient');
|
||||||
|
const fs = require('fs');
|
||||||
const net = require('net');
|
const net = require('net');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const tmpdir = require('os').tmpdir();
|
const tmpdir = require('os').tmpdir();
|
||||||
|
@ -29,7 +30,16 @@ class SocketClient {
|
||||||
this._sock = net.connect(sockPath);
|
this._sock = net.connect(sockPath);
|
||||||
this._ready = new Promise((resolve, reject) => {
|
this._ready = new Promise((resolve, reject) => {
|
||||||
this._sock.on('connect', () => resolve(this));
|
this._sock.on('connect', () => resolve(this));
|
||||||
this._sock.on('error', (e) => reject(e));
|
this._sock.on('error', (e) => {
|
||||||
|
e.message = `Error connecting to server on ${sockPath} ` +
|
||||||
|
`with error: ${e.message}`;
|
||||||
|
|
||||||
|
if (fs.existsSync(LOG_PATH)) {
|
||||||
|
e.message += '\nServer logs:\n' + fs.readFileSync(LOG_PATH, 'utf8');
|
||||||
|
}
|
||||||
|
|
||||||
|
reject(e);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
this._resolvers = Object.create(null);
|
this._resolvers = Object.create(null);
|
||||||
|
|
|
@ -16,6 +16,7 @@ const fs = require('fs');
|
||||||
const net = require('net');
|
const net = require('net');
|
||||||
|
|
||||||
const MAX_IDLE_TIME = 30 * 1000;
|
const MAX_IDLE_TIME = 30 * 1000;
|
||||||
|
const MAX_STARTUP_TIME = 5 * 60 * 1000;
|
||||||
|
|
||||||
class SocketServer {
|
class SocketServer {
|
||||||
constructor(sockPath, options) {
|
constructor(sockPath, options) {
|
||||||
|
@ -35,13 +36,15 @@ class SocketServer {
|
||||||
process.on('exit', () => fs.unlinkSync(sockPath));
|
process.on('exit', () => fs.unlinkSync(sockPath));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this._numConnections = 0;
|
||||||
this._server.on('connection', (sock) => this._handleConnection(sock));
|
this._server.on('connection', (sock) => this._handleConnection(sock));
|
||||||
|
|
||||||
// Disable the file watcher.
|
// Disable the file watcher.
|
||||||
options.nonPersistent = true;
|
options.nonPersistent = true;
|
||||||
this._packagerServer = new Server(options);
|
this._packagerServer = new Server(options);
|
||||||
this._jobs = 0;
|
this._jobs = 0;
|
||||||
this._dieEventually();
|
this._dieEventually(MAX_STARTUP_TIME);
|
||||||
}
|
}
|
||||||
|
|
||||||
onReady() {
|
onReady() {
|
||||||
|
@ -50,10 +53,13 @@ class SocketServer {
|
||||||
|
|
||||||
_handleConnection(sock) {
|
_handleConnection(sock) {
|
||||||
debug('connection to server', process.pid);
|
debug('connection to server', process.pid);
|
||||||
|
this._numConnections++;
|
||||||
|
sock.on('close', () => this._numConnections--);
|
||||||
|
|
||||||
const bunser = new bser.BunserBuf();
|
const bunser = new bser.BunserBuf();
|
||||||
sock.on('data', (buf) => bunser.append(buf));
|
sock.on('data', (buf) => bunser.append(buf));
|
||||||
bunser.on('value', (m) => this._handleMessage(sock, m));
|
bunser.on('value', (m) => this._handleMessage(sock, m));
|
||||||
|
bunser.on('error', (e) => console.error(e));
|
||||||
}
|
}
|
||||||
|
|
||||||
_handleMessage(sock, m) {
|
_handleMessage(sock, m) {
|
||||||
|
@ -113,15 +119,15 @@ class SocketServer {
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
_dieEventually() {
|
_dieEventually(delay = MAX_IDLE_TIME) {
|
||||||
clearTimeout(this._deathTimer);
|
clearTimeout(this._deathTimer);
|
||||||
this._deathTimer = setTimeout(() => {
|
this._deathTimer = setTimeout(() => {
|
||||||
if (this._jobs <= 0) {
|
if (this._jobs <= 0 && this._numConnections <= 0) {
|
||||||
debug('server dying', process.pid);
|
debug('server dying', process.pid);
|
||||||
process.exit();
|
process.exit();
|
||||||
}
|
}
|
||||||
this._dieEventually();
|
this._dieEventually();
|
||||||
}, MAX_IDLE_TIME);
|
}, delay);
|
||||||
}
|
}
|
||||||
|
|
||||||
static listenOnServerIPCMessages() {
|
static listenOnServerIPCMessages() {
|
||||||
|
|
|
@ -20,7 +20,7 @@ const path = require('path');
|
||||||
const tmpdir = require('os').tmpdir();
|
const tmpdir = require('os').tmpdir();
|
||||||
const {spawn} = require('child_process');
|
const {spawn} = require('child_process');
|
||||||
|
|
||||||
const CREATE_SERVER_TIMEOUT = 60000;
|
const CREATE_SERVER_TIMEOUT = 5 * 60 * 1000;
|
||||||
|
|
||||||
const SocketInterface = {
|
const SocketInterface = {
|
||||||
getOrCreateSocketFor(options) {
|
getOrCreateSocketFor(options) {
|
||||||
|
@ -42,8 +42,16 @@ const SocketInterface = {
|
||||||
if (fs.existsSync(sockPath)) {
|
if (fs.existsSync(sockPath)) {
|
||||||
var sock = net.connect(sockPath);
|
var sock = net.connect(sockPath);
|
||||||
sock.on('connect', () => {
|
sock.on('connect', () => {
|
||||||
sock.end();
|
SocketClient.create(sockPath).then(
|
||||||
resolve(SocketClient.create(sockPath));
|
client => {
|
||||||
|
sock.end();
|
||||||
|
resolve(client);
|
||||||
|
},
|
||||||
|
error => {
|
||||||
|
sock.end();
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
);
|
||||||
});
|
});
|
||||||
sock.on('error', (e) => {
|
sock.on('error', (e) => {
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -1,52 +0,0 @@
|
||||||
'use strict';
|
|
||||||
|
|
||||||
jest.autoMockOff();
|
|
||||||
var getAssetDataFromName = require('../getAssetDataFromName');
|
|
||||||
|
|
||||||
describe('getAssetDataFromName', function() {
|
|
||||||
it('should extract resolution simple case', function() {
|
|
||||||
var data = getAssetDataFromName('test@2x.png');
|
|
||||||
expect(data).toEqual({
|
|
||||||
assetName: 'test.png',
|
|
||||||
resolution: 2,
|
|
||||||
type: 'png',
|
|
||||||
name: 'test',
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should default resolution to 1', function() {
|
|
||||||
var data = getAssetDataFromName('test.png');
|
|
||||||
expect(data).toEqual({
|
|
||||||
assetName: 'test.png',
|
|
||||||
resolution: 1,
|
|
||||||
type: 'png',
|
|
||||||
name: 'test',
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should support float', function() {
|
|
||||||
var data = getAssetDataFromName('test@1.1x.png');
|
|
||||||
expect(data).toEqual({
|
|
||||||
assetName: 'test.png',
|
|
||||||
resolution: 1.1,
|
|
||||||
type: 'png',
|
|
||||||
name: 'test',
|
|
||||||
});
|
|
||||||
|
|
||||||
data = getAssetDataFromName('test@.1x.png');
|
|
||||||
expect(data).toEqual({
|
|
||||||
assetName: 'test.png',
|
|
||||||
resolution: 0.1,
|
|
||||||
type: 'png',
|
|
||||||
name: 'test',
|
|
||||||
});
|
|
||||||
|
|
||||||
data = getAssetDataFromName('test@0.2x.png');
|
|
||||||
expect(data).toEqual({
|
|
||||||
assetName: 'test.png',
|
|
||||||
resolution: 0.2,
|
|
||||||
type: 'png',
|
|
||||||
name: 'test',
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -0,0 +1,123 @@
|
||||||
|
/**
|
||||||
|
* 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';
|
||||||
|
|
||||||
|
jest.dontMock('../getPlatformExtension')
|
||||||
|
.dontMock('../getAssetDataFromName');
|
||||||
|
|
||||||
|
describe('getAssetDataFromName', () => {
|
||||||
|
let getAssetDataFromName;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
getAssetDataFromName = require('../getAssetDataFromName');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should get data from name', () => {
|
||||||
|
expect(getAssetDataFromName('a/b/c.png')).toEqual({
|
||||||
|
resolution: 1,
|
||||||
|
assetName: 'a/b/c.png',
|
||||||
|
type: 'png',
|
||||||
|
name: 'c',
|
||||||
|
platform: null,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(getAssetDataFromName('a/b/c@1x.png')).toEqual({
|
||||||
|
resolution: 1,
|
||||||
|
assetName: 'a/b/c.png',
|
||||||
|
type: 'png',
|
||||||
|
name: 'c',
|
||||||
|
platform: null,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(getAssetDataFromName('a/b/c@2.5x.png')).toEqual({
|
||||||
|
resolution: 2.5,
|
||||||
|
assetName: 'a/b/c.png',
|
||||||
|
type: 'png',
|
||||||
|
name: 'c',
|
||||||
|
platform: null,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(getAssetDataFromName('a/b/c.ios.png')).toEqual({
|
||||||
|
resolution: 1,
|
||||||
|
assetName: 'a/b/c.png',
|
||||||
|
type: 'png',
|
||||||
|
name: 'c',
|
||||||
|
platform: 'ios',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(getAssetDataFromName('a/b/c@1x.ios.png')).toEqual({
|
||||||
|
resolution: 1,
|
||||||
|
assetName: 'a/b/c.png',
|
||||||
|
type: 'png',
|
||||||
|
name: 'c',
|
||||||
|
platform: 'ios',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(getAssetDataFromName('a/b/c@2.5x.ios.png')).toEqual({
|
||||||
|
resolution: 2.5,
|
||||||
|
assetName: 'a/b/c.png',
|
||||||
|
type: 'png',
|
||||||
|
name: 'c',
|
||||||
|
platform: 'ios',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('resolution extraction', () => {
|
||||||
|
it('should extract resolution simple case', () => {
|
||||||
|
var data = getAssetDataFromName('test@2x.png');
|
||||||
|
expect(data).toEqual({
|
||||||
|
assetName: 'test.png',
|
||||||
|
resolution: 2,
|
||||||
|
type: 'png',
|
||||||
|
name: 'test',
|
||||||
|
platform: null,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should default resolution to 1', () => {
|
||||||
|
var data = getAssetDataFromName('test.png');
|
||||||
|
expect(data).toEqual({
|
||||||
|
assetName: 'test.png',
|
||||||
|
resolution: 1,
|
||||||
|
type: 'png',
|
||||||
|
name: 'test',
|
||||||
|
platform: null,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should support float', () => {
|
||||||
|
var data = getAssetDataFromName('test@1.1x.png');
|
||||||
|
expect(data).toEqual({
|
||||||
|
assetName: 'test.png',
|
||||||
|
resolution: 1.1,
|
||||||
|
type: 'png',
|
||||||
|
name: 'test',
|
||||||
|
platform: null,
|
||||||
|
});
|
||||||
|
|
||||||
|
data = getAssetDataFromName('test@.1x.png');
|
||||||
|
expect(data).toEqual({
|
||||||
|
assetName: 'test.png',
|
||||||
|
resolution: 0.1,
|
||||||
|
type: 'png',
|
||||||
|
name: 'test',
|
||||||
|
platform: null,
|
||||||
|
});
|
||||||
|
|
||||||
|
data = getAssetDataFromName('test@0.2x.png');
|
||||||
|
expect(data).toEqual({
|
||||||
|
assetName: 'test.png',
|
||||||
|
resolution: 0.2,
|
||||||
|
type: 'png',
|
||||||
|
name: 'test',
|
||||||
|
platform: null,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,24 @@
|
||||||
|
/**
|
||||||
|
* 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';
|
||||||
|
|
||||||
|
jest.dontMock('../getPlatformExtension');
|
||||||
|
|
||||||
|
describe('getPlatformExtension', function() {
|
||||||
|
it('should get platform ext', function() {
|
||||||
|
var getPlatformExtension = require('../getPlatformExtension');
|
||||||
|
expect(getPlatformExtension('a.ios.js')).toBe('ios');
|
||||||
|
expect(getPlatformExtension('a.android.js')).toBe('android');
|
||||||
|
expect(getPlatformExtension('/b/c/a.ios.js')).toBe('ios');
|
||||||
|
expect(getPlatformExtension('/b/c.android/a.ios.js')).toBe('ios');
|
||||||
|
expect(getPlatformExtension('/b/c/a@1.5x.ios.png')).toBe('ios');
|
||||||
|
expect(getPlatformExtension('/b/c/a@1.5x.lol.png')).toBe(null);
|
||||||
|
expect(getPlatformExtension('/b/c/a.lol.png')).toBe(null);
|
||||||
|
});
|
||||||
|
});
|
|
@ -1,14 +1,29 @@
|
||||||
|
/**
|
||||||
|
* 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';
|
'use strict';
|
||||||
|
|
||||||
var path = require('path');
|
const path = require('path');
|
||||||
|
const getPlatformExtension = require('./getPlatformExtension');
|
||||||
|
|
||||||
function getAssetDataFromName(filename) {
|
function getAssetDataFromName(filename) {
|
||||||
var ext = path.extname(filename);
|
const ext = path.extname(filename);
|
||||||
|
const platformExt = getPlatformExtension(filename);
|
||||||
|
|
||||||
var re = new RegExp('@([\\d\\.]+)x\\' + ext + '$');
|
let pattern = '@([\\d\\.]+)x';
|
||||||
|
if (platformExt != null) {
|
||||||
|
pattern += '(\\.' + platformExt + ')?';
|
||||||
|
}
|
||||||
|
pattern += '\\' + ext + '$';
|
||||||
|
const re = new RegExp(pattern);
|
||||||
|
|
||||||
var match = filename.match(re);
|
const match = filename.match(re);
|
||||||
var resolution;
|
let resolution;
|
||||||
|
|
||||||
if (!(match && match[1])) {
|
if (!(match && match[1])) {
|
||||||
resolution = 1;
|
resolution = 1;
|
||||||
|
@ -19,12 +34,21 @@ function getAssetDataFromName(filename) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var assetName = match ? filename.replace(re, ext) : filename;
|
let assetName;
|
||||||
|
if (match) {
|
||||||
|
assetName = filename.replace(re, ext);
|
||||||
|
} else if (platformExt != null) {
|
||||||
|
assetName = filename.replace(new RegExp(`\\.${platformExt}\\${ext}`), ext);
|
||||||
|
} else {
|
||||||
|
assetName = filename;
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
resolution: resolution,
|
resolution: resolution,
|
||||||
assetName: assetName,
|
assetName: assetName,
|
||||||
type: ext.slice(1),
|
type: ext.slice(1),
|
||||||
name: path.basename(assetName, ext)
|
name: path.basename(assetName, ext),
|
||||||
|
platform: platformExt,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
/**
|
||||||
|
* 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';
|
||||||
|
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
const SUPPORTED_PLATFORM_EXTS = ['android', 'ios'];
|
||||||
|
|
||||||
|
const re = new RegExp(
|
||||||
|
'[^\\.]+\\.(' + SUPPORTED_PLATFORM_EXTS.join('|') + ')\\.\\w+$'
|
||||||
|
);
|
||||||
|
|
||||||
|
// Extract platform extension: index.ios.js -> ios
|
||||||
|
function getPlatformExtension(file) {
|
||||||
|
const match = file.match(re);
|
||||||
|
if (match && match[1]) {
|
||||||
|
return match[1];
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = getPlatformExtension;
|
|
@ -39,6 +39,7 @@ function transform(srcTxt, filename, options) {
|
||||||
'es7.objectRestSpread',
|
'es7.objectRestSpread',
|
||||||
'flow',
|
'flow',
|
||||||
'react',
|
'react',
|
||||||
|
'react.displayName',
|
||||||
'regenerator',
|
'regenerator',
|
||||||
],
|
],
|
||||||
plugins: plugins,
|
plugins: plugins,
|
||||||
|
|