diff --git a/Examples/UIExplorer/UIExplorerApp.ios.js b/Examples/UIExplorer/UIExplorerApp.ios.js index fe643dec5..e5bfd22a3 100644 --- a/Examples/UIExplorer/UIExplorerApp.ios.js +++ b/Examples/UIExplorer/UIExplorerApp.ios.js @@ -43,22 +43,23 @@ var UIExplorerApp = React.createClass({ /> ); } - return ( - { - this.setState({ openExternalExample: example, }); - }, - } - }} - itemWrapperStyle={styles.itemWrapper} - tintColor="#008888" - /> - ); + + return ( + { + this.setState({ openExternalExample: example, }); + }, + } + }} + itemWrapperStyle={styles.itemWrapper} + tintColor="#008888" + /> + ); } }); diff --git a/Examples/UIExplorer/UIExplorerIntegrationTests/ReferenceImages/Examples-UIExplorer-UIExplorerApp.ios/testLayoutExampleSnapshot_1@2x.png b/Examples/UIExplorer/UIExplorerIntegrationTests/ReferenceImages/Examples-UIExplorer-UIExplorerApp.ios/testLayoutExampleSnapshot_1@2x.png index bfa2091b3..263875acd 100644 Binary files a/Examples/UIExplorer/UIExplorerIntegrationTests/ReferenceImages/Examples-UIExplorer-UIExplorerApp.ios/testLayoutExampleSnapshot_1@2x.png and b/Examples/UIExplorer/UIExplorerIntegrationTests/ReferenceImages/Examples-UIExplorer-UIExplorerApp.ios/testLayoutExampleSnapshot_1@2x.png differ diff --git a/Examples/UIExplorer/UIExplorerIntegrationTests/ReferenceImages/Examples-UIExplorer-UIExplorerApp.ios/testSliderExampleSnapshot_1@2x.png b/Examples/UIExplorer/UIExplorerIntegrationTests/ReferenceImages/Examples-UIExplorer-UIExplorerApp.ios/testSliderExampleSnapshot_1@2x.png index 80fca00ad..66b2645fd 100644 Binary files a/Examples/UIExplorer/UIExplorerIntegrationTests/ReferenceImages/Examples-UIExplorer-UIExplorerApp.ios/testSliderExampleSnapshot_1@2x.png and b/Examples/UIExplorer/UIExplorerIntegrationTests/ReferenceImages/Examples-UIExplorer-UIExplorerApp.ios/testSliderExampleSnapshot_1@2x.png differ diff --git a/Examples/UIExplorer/UIExplorerIntegrationTests/ReferenceImages/Examples-UIExplorer-UIExplorerApp.ios/testSwitchExampleSnapshot_1@2x.png b/Examples/UIExplorer/UIExplorerIntegrationTests/ReferenceImages/Examples-UIExplorer-UIExplorerApp.ios/testSwitchExampleSnapshot_1@2x.png index 7cf2516fa..6ed97e55c 100644 Binary files a/Examples/UIExplorer/UIExplorerIntegrationTests/ReferenceImages/Examples-UIExplorer-UIExplorerApp.ios/testSwitchExampleSnapshot_1@2x.png and b/Examples/UIExplorer/UIExplorerIntegrationTests/ReferenceImages/Examples-UIExplorer-UIExplorerApp.ios/testSwitchExampleSnapshot_1@2x.png differ diff --git a/Examples/UIExplorer/UIExplorerIntegrationTests/ReferenceImages/Examples-UIExplorer-UIExplorerApp.ios/testTextExampleSnapshot_1@2x.png b/Examples/UIExplorer/UIExplorerIntegrationTests/ReferenceImages/Examples-UIExplorer-UIExplorerApp.ios/testTextExampleSnapshot_1@2x.png index 1e764f3c8..3ac73f3ad 100644 Binary files a/Examples/UIExplorer/UIExplorerIntegrationTests/ReferenceImages/Examples-UIExplorer-UIExplorerApp.ios/testTextExampleSnapshot_1@2x.png and b/Examples/UIExplorer/UIExplorerIntegrationTests/ReferenceImages/Examples-UIExplorer-UIExplorerApp.ios/testTextExampleSnapshot_1@2x.png differ diff --git a/Examples/UIExplorer/UIExplorerIntegrationTests/ReferenceImages/Examples-UIExplorer-UIExplorerApp.ios/testViewExampleSnapshot_1@2x.png b/Examples/UIExplorer/UIExplorerIntegrationTests/ReferenceImages/Examples-UIExplorer-UIExplorerApp.ios/testViewExampleSnapshot_1@2x.png index d37b70879..927c52347 100644 Binary files a/Examples/UIExplorer/UIExplorerIntegrationTests/ReferenceImages/Examples-UIExplorer-UIExplorerApp.ios/testViewExampleSnapshot_1@2x.png and b/Examples/UIExplorer/UIExplorerIntegrationTests/ReferenceImages/Examples-UIExplorer-UIExplorerApp.ios/testViewExampleSnapshot_1@2x.png differ diff --git a/Examples/UIExplorer/UIExplorerList.js b/Examples/UIExplorer/UIExplorerList.js index 1803c7ebd..8a7782484 100644 --- a/Examples/UIExplorer/UIExplorerList.js +++ b/Examples/UIExplorer/UIExplorerList.js @@ -32,6 +32,7 @@ var { var { TestModule } = React.addons; import type { ExampleModule } from 'ExampleTypes'; +import type { NavigationContext } from 'NavigationContext'; var createExamplePage = require('./createExamplePage'); @@ -129,7 +130,10 @@ COMPONENTS.concat(APIS).forEach((Example) => { }); type Props = { - navigator: Array<{title: string, component: ReactClass}>, + navigator: { + navigationContext: NavigationContext, + push: (route: {title: string, component: ReactClass}) => void, + }, onExternalExampleRequested: Function, onSelectExample: Function, isInDrawer: bool, @@ -149,8 +153,25 @@ class UIExplorerList extends React.Component { }; } + componentWillMount() { + this.props.navigator.navigationContext.addListener('didfocus', function(event) { + if (event.data.route.title === 'UIExplorer') { + Settings.set({visibleExample: null}); + } + }); + } + componentDidMount() { this._search(this.state.searchText); + + var visibleExampleTitle = Settings.get('visibleExample'); + if (visibleExampleTitle) { + var predicate = (example) => example.title === visibleExampleTitle; + var foundExample = APIS.find(predicate) || COMPONENTS.find(predicate); + if (foundExample) { + setTimeout(() => this._openExample(foundExample), 100); + } + } } render() { @@ -240,11 +261,12 @@ class UIExplorerList extends React.Component { Settings.set({searchText: text}); } - _onPressRow(example: any) { + _openExample(example: any) { if (example.external) { this.props.onExternalExampleRequested(example); return; } + var Component = makeRenderable(example); if (Platform.OS === 'ios') { this.props.navigator.push({ @@ -258,6 +280,11 @@ class UIExplorerList extends React.Component { }); } } + + _onPressRow(example: any) { + Settings.set({visibleExample: example.title}); + this._openExample(example); + } } var styles = StyleSheet.create({ diff --git a/Examples/UIExplorer/UIExplorerPage.js b/Examples/UIExplorer/UIExplorerPage.js index 02c8370e3..2c74497a7 100644 --- a/Examples/UIExplorer/UIExplorerPage.js +++ b/Examples/UIExplorer/UIExplorerPage.js @@ -40,6 +40,7 @@ var UIExplorerPage = React.createClass({ ContentWrapper = (View: ReactClass); } else { ContentWrapper = (ScrollView: ReactClass); + wrapperProps.automaticallyAdjustContentInsets = !this.props.title; wrapperProps.keyboardShouldPersistTaps = true; wrapperProps.keyboardDismissMode = 'interactive'; } @@ -64,7 +65,6 @@ var UIExplorerPage = React.createClass({ var styles = StyleSheet.create({ container: { backgroundColor: '#e9eaed', - paddingTop: 15, flex: 1, }, spacer: { @@ -72,6 +72,7 @@ var styles = StyleSheet.create({ }, wrapper: { flex: 1, + paddingTop: 10, }, }); diff --git a/Examples/UIExplorer/UIExplorerTitle.js b/Examples/UIExplorer/UIExplorerTitle.js index 334cb4115..165249893 100644 --- a/Examples/UIExplorer/UIExplorerTitle.js +++ b/Examples/UIExplorer/UIExplorerTitle.js @@ -41,6 +41,7 @@ var styles = StyleSheet.create({ borderWidth: 0.5, borderColor: '#d6d7da', margin: 10, + marginBottom: 0, height: 45, padding: 10, backgroundColor: 'white', diff --git a/Examples/UIExplorer/ViewExample.js b/Examples/UIExplorer/ViewExample.js index 5381f2397..312fbe230 100644 --- a/Examples/UIExplorer/ViewExample.js +++ b/Examples/UIExplorer/ViewExample.js @@ -15,12 +15,14 @@ */ 'use strict'; +var Platform = require('Platform'); var React = require('react-native'); var { StyleSheet, Text, View, } = React; +var TouchableWithoutFeedback = require('TouchableWithoutFeedback'); var styles = StyleSheet.create({ box: { @@ -30,6 +32,58 @@ var styles = StyleSheet.create({ } }); +var ViewBorderStyleExample = React.createClass({ + getInitialState() { + return { + showBorder: true + }; + }, + + render() { + if (Platform.OS !== 'android') { + return ( + + + borderStyle is only supported on android for now. + + + ); + } + + return ( + + + + + Dashed border style + + + + + Dotted border style + + + + + ); + }, + + _handlePress() { + this.setState({showBorder: !this.state.showBorder}); + } +}); + exports.title = ''; exports.description = 'Basic building block of all UI.'; exports.displayName = 'ViewExample'; @@ -89,6 +143,11 @@ exports.examples = [ ); }, + }, { + title: 'Border Style', + render: function() { + return ; + }, }, { title: 'Circle with Border Radius', render: function() { diff --git a/Libraries/Animation/POPAnimationMixin.js b/Libraries/Animation/POPAnimationMixin.js index 115e58a01..bf95f41bf 100644 --- a/Libraries/Animation/POPAnimationMixin.js +++ b/Libraries/Animation/POPAnimationMixin.js @@ -120,7 +120,10 @@ var POPAnimationMixin = { } doneCallback && doneCallback(finished); }; - POPAnimation.addAnimation(nodeHandle, animID, cleanupWrapper); + // Hack to aviod race condition. This delay should be imperceptible: + setTimeout(() => { + POPAnimation.addAnimation(nodeHandle, animID, cleanupWrapper); + }, 10); }, /** diff --git a/Libraries/Components/Navigation/NavigatorIOS.ios.js b/Libraries/Components/Navigation/NavigatorIOS.ios.js index 788273ec5..053ab0ee3 100644 --- a/Libraries/Components/Navigation/NavigatorIOS.ios.js +++ b/Libraries/Components/Navigation/NavigatorIOS.ios.js @@ -13,6 +13,7 @@ var EventEmitter = require('EventEmitter'); var Image = require('Image'); +var NavigationContext = require('NavigationContext'); var React = require('React'); var ReactNativeViewAttributes = require('ReactNativeViewAttributes'); var RCTNavigatorManager = require('NativeModules').NavigatorManager; @@ -309,6 +310,7 @@ var NavigatorIOS = React.createClass({ }, navigator: (undefined: ?Object), + navigationContext: new NavigationContext(), componentWillMount: function() { // Precompute a pack of callbacks that's frequently generated and passed to @@ -323,7 +325,18 @@ var NavigatorIOS = React.createClass({ resetTo: this.resetTo, popToRoute: this.popToRoute, popToTop: this.popToTop, + navigationContext: this.navigationContext, }; + this._emitWillFocus(this.state.routeStack[this.state.observedTopOfStack]); + }, + + componentDidMount: function() { + this._emitDidFocus(this.state.routeStack[this.state.observedTopOfStack]); + }, + + componentWillUnmount: function() { + this.navigationContext.dispose(); + this.navigationContext = new NavigationContext(); }, getInitialState: function(): State { @@ -397,6 +410,8 @@ var NavigatorIOS = React.createClass({ _handleNavigatorStackChanged: function(e: Event) { var newObservedTopOfStack = e.nativeEvent.stackLength - 1; + this._emitDidFocus(this.state.routeStack[newObservedTopOfStack]); + invariant( newObservedTopOfStack <= this.state.requestedTopOfStack, 'No navigator item should be pushed without JS knowing about it %s %s', newObservedTopOfStack, this.state.requestedTopOfStack @@ -448,11 +463,21 @@ var NavigatorIOS = React.createClass({ }); }, + _emitDidFocus: function(route: Route) { + this.navigationContext.emit('didfocus', {route: route}); + }, + + _emitWillFocus: function(route: Route) { + this.navigationContext.emit('willfocus', {route: route}); + }, + push: function(route: Route) { invariant(!!route, 'Must supply route to push'); // Make sure all previous requests are caught up first. Otherwise reject. if (this.state.requestedTopOfStack === this.state.observedTopOfStack) { this._tryLockNavigator(() => { + this._emitWillFocus(route); + var nextStack = this.state.routeStack.concat([route]); var nextIDStack = this.state.idStack.concat([getuid()]); this.setState({ @@ -476,12 +501,11 @@ var NavigatorIOS = React.createClass({ if (this.state.requestedTopOfStack === this.state.observedTopOfStack) { if (this.state.requestedTopOfStack > 0) { this._tryLockNavigator(() => { - invariant( - this.state.requestedTopOfStack - n >= 0, - 'Cannot pop below 0' - ); + var newRequestedTopOfStack = this.state.requestedTopOfStack - n; + invariant(newRequestedTopOfStack >= 0, 'Cannot pop below 0'); + this._emitWillFocus(this.state.routeStack[newRequestedTopOfStack]); this.setState({ - requestedTopOfStack: this.state.requestedTopOfStack - n, + requestedTopOfStack: newRequestedTopOfStack, makingNavigatorRequest: true, // Not actually updating the indices yet until we get the native // `onNavigationComplete`. @@ -525,6 +549,9 @@ var NavigatorIOS = React.createClass({ makingNavigatorRequest: false, updatingAllIndicesAtOrBeyond: index, }); + + this._emitWillFocus(route); + this._emitDidFocus(route); }, /** @@ -615,7 +642,7 @@ var NavigatorIOS = React.createClass({ navigationBarHidden={this.props.navigationBarHidden} tintColor={this.props.tintColor} barTintColor={this.props.barTintColor} - translucent={this.props.translucent} + translucent={this.props.translucent !== false} titleTextColor={this.props.titleTextColor}> ); - } + }, }); var styles = StyleSheet.create({ diff --git a/Libraries/Components/View/ViewStylePropTypes.js b/Libraries/Components/View/ViewStylePropTypes.js index f2271f4d8..ce147e096 100644 --- a/Libraries/Components/View/ViewStylePropTypes.js +++ b/Libraries/Components/View/ViewStylePropTypes.js @@ -32,6 +32,7 @@ var ViewStylePropTypes = { borderTopRightRadius: ReactPropTypes.number, borderBottomLeftRadius: ReactPropTypes.number, borderBottomRightRadius: ReactPropTypes.number, + borderStyle: ReactPropTypes.oneOf(['solid', 'dotted', 'dashed']), opacity: ReactPropTypes.number, overflow: ReactPropTypes.oneOf(['visible', 'hidden']), shadowColor: ReactPropTypes.string, diff --git a/Libraries/Image/RCTImageDownloader.m b/Libraries/Image/RCTImageDownloader.m index a8a68c0b4..ea448220b 100644 --- a/Libraries/Image/RCTImageDownloader.m +++ b/Libraries/Image/RCTImageDownloader.m @@ -90,9 +90,11 @@ CGRect RCTClipRect(CGSize, CGFloat, CGSize, CGFloat, UIViewContentMode); runBlocks(NO, data, error); } - RCTImageDownloader *strongSelf = weakSelf; - NSCachedURLResponse *cachedResponse = [[NSCachedURLResponse alloc] initWithResponse:response data:data userInfo:nil storagePolicy:NSURLCacheStorageAllowed]; - [strongSelf->_cache storeCachedResponse:cachedResponse forDataTask:task]; + if (response) { + RCTImageDownloader *strongSelf = weakSelf; + NSCachedURLResponse *cachedResponse = [[NSCachedURLResponse alloc] initWithResponse:response data:data userInfo:nil storagePolicy:NSURLCacheStorageAllowed]; + [strongSelf->_cache storeCachedResponse:cachedResponse forDataTask:task]; + } task = nil; }]; diff --git a/React/Base/RCTAssert.m b/React/Base/RCTAssert.m index 3e76518dd..72aa0420a 100644 --- a/React/Base/RCTAssert.m +++ b/React/Base/RCTAssert.m @@ -78,7 +78,12 @@ NSString *RCTCurrentThreadName(void) #if DEBUG // This is DEBUG not RCT_DEBUG because it *really* must not ship in RC #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wdeprecated-declarations" - threadName = @(dispatch_queue_get_label(dispatch_get_current_queue())); + const char *label = dispatch_queue_get_label(dispatch_get_current_queue()); + if (label && strlen(label) > 0) { + threadName = @(label); + } else { + threadName = [NSString stringWithFormat:@"%p", thread]; + } #pragma clang diagnostic pop #else threadName = [NSString stringWithFormat:@"%p", thread];