diff --git a/Examples/UIExplorer/NavigationExperimental/NavigationAnimatedExample.js b/Examples/UIExplorer/NavigationExperimental/NavigationAnimatedExample.js index 88b446d47..1df559843 100644 --- a/Examples/UIExplorer/NavigationExperimental/NavigationAnimatedExample.js +++ b/Examples/UIExplorer/NavigationExperimental/NavigationAnimatedExample.js @@ -37,10 +37,9 @@ const { Card: NavigationCard, Header: NavigationHeader, Reducer: NavigationReducer, - RootContainer: NavigationRootContainer, } = NavigationExperimental; -const NavigationBasicReducer = NavigationReducer.StackReducer({ +const ExampleReducer = NavigationReducer.StackReducer({ getPushedReducerForAction: (action) => { if (action.type === 'push') { return (state) => state || {key: action.key}; @@ -58,37 +57,41 @@ const NavigationBasicReducer = NavigationReducer.StackReducer({ }); class NavigationAnimatedExample extends React.Component { + constructor(props, context) { + super(props, context); + this.state = ExampleReducer(); + } + componentWillMount() { this._renderCard = this._renderCard.bind(this); this._renderHeader = this._renderHeader.bind(this); - this._renderNavigation = this._renderNavigation.bind(this); this._renderScene = this._renderScene.bind(this); this._renderTitleComponent = this._renderTitleComponent.bind(this); + this._handleAction = this._handleAction.bind(this); } + + _handleAction(action): boolean { + if (!action) { + return false; + } + const newState = ExampleReducer(this.state, action); + if (newState === this.state) { + return false; + } + this.setState(newState); + return true; + } + + handleBackAction(): boolean { + return this._handleAction({ type: 'BackAction', }); + } + render() { - return ( - { this.navRootContainer = navRootContainer; }} - persistenceKey="NavigationAnimExampleState" - renderNavigation={this._renderNavigation} - /> - ); - } - handleBackAction() { - return ( - this.navRootContainer && - this.navRootContainer.handleNavigation(NavigationRootContainer.getBackAction()) - ); - } - _renderNavigation(navigationState, onNavigate) { - if (!navigationState) { - return null; - } return ( { Animated.timing(pos, {toValue: navState.index, duration: 500}).start(); diff --git a/Examples/UIExplorer/NavigationExperimental/NavigationBasicExample.js b/Examples/UIExplorer/NavigationExperimental/NavigationBasicExample.js index e75e7731d..9922227bf 100644 --- a/Examples/UIExplorer/NavigationExperimental/NavigationBasicExample.js +++ b/Examples/UIExplorer/NavigationExperimental/NavigationBasicExample.js @@ -1,4 +1,11 @@ /** + * Copyright (c) 2013-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. + * * The examples provided by Facebook are for non-commercial testing and * evaluation purposes only. * @@ -22,12 +29,10 @@ const { } = ReactNative; const NavigationExampleRow = require('./NavigationExampleRow'); const { - RootContainer: NavigationRootContainer, Reducer: NavigationReducer, } = NavigationExperimental; -const StackReducer = NavigationReducer.StackReducer; -const NavigationBasicReducer = NavigationReducer.StackReducer({ +const ExampleReducer = NavigationReducer.StackReducer({ getPushedReducerForAction: (action) => { if (action.type === 'push') { return (state) => state || {key: action.key}; @@ -45,47 +50,51 @@ const NavigationBasicReducer = NavigationReducer.StackReducer({ }); const NavigationBasicExample = React.createClass({ + + getInitialState: function() { + return ExampleReducer(); + }, + render: function() { return ( - { this.navRootContainer = navRootContainer; }} - renderNavigation={(navState, onNavigate) => { - if (!navState) { return null; } - return ( - - - { - onNavigate({ type: 'push', key: 'page #' + navState.children.length }); - }} - /> - { - onNavigate(NavigationRootContainer.getBackAction()); - }} - /> - - - ); - }} - /> + + + { + this._handleAction({ type: 'push', key: 'page #' + this.state.children.length }); + }} + /> + { + this._handleAction({ type: 'BackAction' }); + }} + /> + + ); }, - handleBackAction: function() { - return ( - this.navRootContainer && - this.navRootContainer.handleNavigation(NavigationRootContainer.getBackAction()) - ); + _handleAction(action) { + if (!action) { + return false; + } + const newState = ExampleReducer(this.state, action); + if (newState === this.state) { + return false; + } + this.setState(newState); + return true; + }, + + handleBackAction() { + return this._handleAction({ type: 'BackAction' }); }, }); diff --git a/Examples/UIExplorer/NavigationExperimental/NavigationCardStackExample.js b/Examples/UIExplorer/NavigationExperimental/NavigationCardStackExample.js index 6a79dfd2e..412ada582 100644 --- a/Examples/UIExplorer/NavigationExperimental/NavigationCardStackExample.js +++ b/Examples/UIExplorer/NavigationExperimental/NavigationCardStackExample.js @@ -33,18 +33,15 @@ const { const { CardStack: NavigationCardStack, StateUtils: NavigationStateUtils, - RootContainer: NavigationRootContainer, } = NavigationExperimental; function createReducer(initialState) { - return (currentState, action) => { + return (currentState = initialState, action) => { switch (action.type) { - case 'RootContainerInitialAction': - return initialState; - case 'push': return NavigationStateUtils.push(currentState, {key: action.key}); + case 'BackAction': case 'back': case 'pop': return currentState.index > 0 ? @@ -67,35 +64,46 @@ class NavigationCardStackExample extends React.Component { constructor(props, context) { super(props, context); - this.state = {isHorizontal: true}; + this.state = { + isHorizontal: true, + navState: ExampleReducer(undefined, {}), + }; } componentWillMount() { - this._renderNavigation = this._renderNavigation.bind(this); this._renderScene = this._renderScene.bind(this); this._toggleDirection = this._toggleDirection.bind(this); + this._handleAction = this._handleAction.bind(this); } render() { return ( - ); } - _renderNavigation(navigationState, onNavigate) { - return ( - - ); + _handleAction(action): boolean { + if (!action) { + return false; + } + const newState = ExampleReducer(this.state.navState, action); + if (newState === this.state.navState) { + return false; + } + this.setState({ + navState: newState, + }); + return true; + } + + handleBackAction(): boolean { + return this._handleAction({ type: 'BackAction', }); } _renderScene(/*NavigationSceneRendererProps*/ props) { diff --git a/Examples/UIExplorer/NavigationExperimental/NavigationCompositionExample.js b/Examples/UIExplorer/NavigationExperimental/NavigationCompositionExample.js index b0bd41fb9..1c9529aed 100644 --- a/Examples/UIExplorer/NavigationExperimental/NavigationCompositionExample.js +++ b/Examples/UIExplorer/NavigationExperimental/NavigationCompositionExample.js @@ -36,10 +36,8 @@ const { const { CardStack: NavigationCardStack, - Container: NavigationContainer, Header: NavigationHeader, Reducer: NavigationReducer, - RootContainer: NavigationRootContainer, View: NavigationView, } = NavigationExperimental; @@ -177,6 +175,7 @@ class ExampleTabScreen extends React.Component { @@ -225,40 +224,42 @@ class ExampleTabScreen extends React.Component { ); } } -ExampleTabScreen = NavigationContainer.create(ExampleTabScreen); class NavigationCompositionExample extends React.Component { - navRootContainer: NavigationRootContainer; - - render() { - return ( - { this.navRootContainer = navRootContainer; }} - renderNavigation={this.renderApp.bind(this)} - /> - ); + state: NavigationParentState; + constructor() { + super(); + this.state = ExampleAppReducer(undefined, {}); + } + handleAction(action: Object): boolean { + if (!action) { + return false; + } + const newState = ExampleAppReducer(this.state, action); + if (newState === this.state) { + return false; + } + this.setState(newState); + return true; } handleBackAction(): boolean { - return ( - this.navRootContainer && - this.navRootContainer.handleNavigation(NavigationRootContainer.getBackAction()) - ); + return this.handleAction({ type: 'BackAction' }); } - renderApp(navigationState: NavigationParentState, onNavigate: Function) { - if (!navigationState) { + render() { + if (!this.state) { return null; } return ( ); @@ -267,15 +268,18 @@ class NavigationCompositionExample extends React.Component { class ExampleMainView extends React.Component { _renderScene: NavigationSceneRenderer; + _handleNavigation: Function; componentWillMount() { this._renderScene = this._renderScene.bind(this); + this._handleNavigation = this._handleNavigation.bind(this); } render() { return ( @@ -288,12 +292,12 @@ class ExampleMainView extends React.Component { ); } - _handleNavigation(tabKey, action) { + _handleNavigation(action: Object) { if (ExampleExitAction.match(action)) { this.props.onExampleExit(); return; @@ -302,8 +306,6 @@ class ExampleMainView extends React.Component { } } -ExampleMainView = NavigationContainer.create(ExampleMainView); - const styles = StyleSheet.create({ topView: { flex: 1, diff --git a/Examples/UIExplorer/NavigationExperimental/NavigationExampleTabBar.js b/Examples/UIExplorer/NavigationExperimental/NavigationExampleTabBar.js index 445a8274b..40f525466 100644 --- a/Examples/UIExplorer/NavigationExperimental/NavigationExampleTabBar.js +++ b/Examples/UIExplorer/NavigationExperimental/NavigationExampleTabBar.js @@ -1,4 +1,11 @@ /** + * Copyright (c) 2013-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. + * * The examples provided by Facebook are for non-commercial testing and * evaluation purposes only. * @@ -13,24 +20,20 @@ */ 'use strict'; -var React = require('react'); -var ReactNative = require('react-native'); -var { - NavigationExperimental, - StyleSheet, - Text, - TouchableOpacity, - View, -} = ReactNative; +const NavigationExperimental = require('NavigationExperimental'); +const React = require('react'); +const StyleSheet = require('StyleSheet'); +const Text = require('Text'); +const TouchableOpacity = require('TouchableOpacity'); +const View = require('View'); const { - Container: NavigationContainer, Reducer: NavigationReducer, } = NavigationExperimental; const { JumpToAction, } = NavigationReducer.TabsReducer; -var NavigationExampleTabBar = React.createClass({ +const NavigationExampleTabBar = React.createClass({ render: function() { return ( @@ -39,7 +42,7 @@ var NavigationExampleTabBar = React.createClass({ ); }, _renderTab: function(tab, index) { - var textStyle = [styles.tabButtonText]; + const textStyle = [styles.tabButtonText]; if (this.props.index === index) { textStyle.push(styles.selectedTabButtonText); } @@ -58,8 +61,6 @@ var NavigationExampleTabBar = React.createClass({ }, }); -NavigationExampleTabBar = NavigationContainer.create(NavigationExampleTabBar); - const styles = StyleSheet.create({ tabBar: { height: 50, diff --git a/Examples/UIExplorer/NavigationExperimental/NavigationExperimentalExample.js b/Examples/UIExplorer/NavigationExperimental/NavigationExperimentalExample.js index 3d2d54e5d..f0b0f922e 100644 --- a/Examples/UIExplorer/NavigationExperimental/NavigationExperimentalExample.js +++ b/Examples/UIExplorer/NavigationExperimental/NavigationExperimentalExample.js @@ -1,4 +1,11 @@ /** + * Copyright (c) 2013-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. + * * The examples provided by Facebook are for non-commercial testing and * evaluation purposes only. * @@ -13,22 +20,19 @@ */ 'use strict'; -var React = require('react'); -var ReactNative = require('react-native'); -var { - AsyncStorage, - ScrollView, - StyleSheet, - View, -} = ReactNative; -var NavigationExampleRow = require('./NavigationExampleRow'); +const AsyncStorage = require('AsyncStorage'); +const NavigationExampleRow = require('./NavigationExampleRow'); +const React = require('react'); +const ScrollView = require('ScrollView'); +const StyleSheet = require('StyleSheet'); +const View = require('View'); /* * Heads up! This file is not the real navigation example- only a utility to switch between them. * * To learn how to use the Navigation API, take a look at the following example files: */ -var EXAMPLES = { +const EXAMPLES = { 'Tabs': require('./NavigationTabsExample'), 'Basic': require('./NavigationBasicExample'), 'Animated Example': require('./NavigationAnimatedExample'), @@ -37,9 +41,9 @@ var EXAMPLES = { 'Tic Tac Toe': require('./NavigationTicTacToeExample'), }; -var EXAMPLE_STORAGE_KEY = 'NavigationExperimentalExample'; +const EXAMPLE_STORAGE_KEY = 'NavigationExperimentalExample'; -var NavigationExperimentalExample = React.createClass({ +const NavigationExperimentalExample = React.createClass({ statics: { title: 'Navigation (Experimental)', description: 'Upcoming navigation APIs and animated navigation views', @@ -74,7 +78,7 @@ var NavigationExperimentalExample = React.createClass({ }, _renderMenu: function() { - var exitRow = null; + let exitRow = null; if (this.props.onExampleExit) { exitRow = ( { this.navRootContainer = navRootContainer; }} - renderNavigation={(navigationState) => { - if (!navigationState) { return null; } - return ( - - - - - ); - }} - /> + + + + ); } + handleAction(action) { + if (!action) { + return false; + } + const newState = ExampleTabsReducer(this.state, action); + if (newState === this.state) { + return false; + } + this.setState(newState); + return true; + } handleBackAction() { - return ( - this.navRootContainer && - this.navRootContainer.handleNavigation(NavigationRootContainer.getBackAction()) - ); + return this.handleAction({ type: 'BackAction' }); } } diff --git a/Examples/UIExplorer/NavigationExperimental/NavigationTicTacToeExample.js b/Examples/UIExplorer/NavigationExperimental/NavigationTicTacToeExample.js index 24e81b85e..24ea3aee5 100644 --- a/Examples/UIExplorer/NavigationExperimental/NavigationTicTacToeExample.js +++ b/Examples/UIExplorer/NavigationExperimental/NavigationTicTacToeExample.js @@ -1,4 +1,11 @@ /** + * Copyright (c) 2013-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. + * * The examples provided by Facebook are for non-commercial testing and * evaluation purposes only. * @@ -16,19 +23,11 @@ */ 'use strict'; -var React = require('react'); -var ReactNative = require('react-native'); -var { - NavigationExperimental, - StyleSheet, - Text, - TouchableHighlight, - View, -} = ReactNative; -const { - Container: NavigationContainer, - RootContainer: NavigationRootContainer, -} = NavigationExperimental; +const React = require('react'); +const StyleSheet = require('StyleSheet'); +const Text = require('Text'); +const TouchableHighlight = require('TouchableHighlight'); +const View = require('View'); type GameGrid = Array>; @@ -41,7 +40,7 @@ function parseGame(game: string): GameGrid { for (let i = 0; i < 3; i++) { const row = Array(3); for (let j = 0; j < 3; j++) { - const turnIndex = gameTurns.indexOf(rowLetterMap[i]+j); + const turnIndex = gameTurns.indexOf(rowLetterMap[i] + j); if (turnIndex === -1) { row[j] = null; } else { @@ -60,13 +59,13 @@ function playTurn(game: string, row: number, col: number): string { function getWinner(gameString: string): ?string { const game = parseGame(gameString); - for (var i = 0; i < 3; i++) { + for (let i = 0; i < 3; i++) { if (game[i][0] !== null && game[i][0] === game[i][1] && game[i][0] === game[i][2]) { return game[i][0]; } } - for (var i = 0; i < 3; i++) { + for (let i = 0; i < 3; i++) { if (game[0][i] !== null && game[0][i] === game[1][i] && game[0][i] === game[2][i]) { return game[0][i]; @@ -88,8 +87,8 @@ function isGameOver(gameString: string): boolean { return true; } const game = parseGame(gameString); - for (var i = 0; i < 3; i++) { - for (var j = 0; j < 3; j++) { + for (let i = 0; i < 3; i++) { + for (let j = 0; j < 3; j++) { if (game[i][j] === null) { return false; } @@ -157,10 +156,9 @@ function GameEndOverlay(props) { ); } -GameEndOverlay = NavigationContainer.create(GameEndOverlay); function TicTacToeGame(props) { - var rows = parseGame(props.game).map((cells, row) => + const rows = parseGame(props.game).map((cells, row) => {cells.map((player, col) => ); } -TicTacToeGame = NavigationContainer.create(TicTacToeGame); const GameActions = { Turn: (row, col) => ({type: 'TicTacToeTurnAction', row, col }), @@ -208,21 +206,34 @@ function GameReducer(lastGame: ?string, action: Object): string { return lastGame; } +type AppState = { + game: string, +}; + class NavigationTicTacToeExample extends React.Component { - static GameView = TicTacToeGame; - static GameReducer = GameReducer; - static GameActions = GameActions; + _handleAction: Function; + state: AppState; + constructor() { + super(); + this._handleAction = this._handleAction.bind(this); + this.state = { + game: '' + }; + } + _handleAction(action: Object) { + const newState = GameReducer(this.state.game, action); + if (newState !== this.state.game) { + this.setState({ + game: newState, + }); + } + } render() { return ( - ( - - )} + ); } diff --git a/Examples/UIExplorer/UIExplorerActions.js b/Examples/UIExplorer/UIExplorerActions.js index 8f7a59f4e..cb79edc97 100644 --- a/Examples/UIExplorer/UIExplorerActions.js +++ b/Examples/UIExplorer/UIExplorerActions.js @@ -1,4 +1,11 @@ /** + * Copyright (c) 2013-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. + * * The examples provided by Facebook are for non-commercial testing and * evaluation purposes only. * @@ -25,9 +32,7 @@ export type UIExplorerExampleAction = { openExample: string; }; -import type {BackAction} from 'NavigationRootContainer'; - -export type UIExplorerAction = BackAction | UIExplorerListWithFilterAction | UIExplorerExampleAction; +export type UIExplorerAction = UIExplorerListWithFilterAction | UIExplorerExampleAction; function ExampleListWithFilter(filter: ?string): UIExplorerListWithFilterAction { return { diff --git a/Examples/UIExplorer/UIExplorerApp.android.js b/Examples/UIExplorer/UIExplorerApp.android.js index 2e9d5a3e7..a8e4d9239 100644 --- a/Examples/UIExplorer/UIExplorerApp.android.js +++ b/Examples/UIExplorer/UIExplorerApp.android.js @@ -1,4 +1,11 @@ /** + * Copyright (c) 2013-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. + * * The examples provided by Facebook are for non-commercial testing and * evaluation purposes only. * @@ -16,29 +23,24 @@ */ 'use strict'; +const AppRegistry = require('AppRegistry'); +const AsyncStorage = require('AsyncStorage'); +const BackAndroid = require('BackAndroid'); +const Dimensions = require('Dimensions'); +const DrawerLayoutAndroid = require('DrawerLayoutAndroid'); +const Linking = require('Linking'); const React = require('react'); -const ReactNative = require('react-native'); -const { - AppRegistry, - BackAndroid, - Dimensions, - DrawerLayoutAndroid, - NavigationExperimental, - StyleSheet, - ToolbarAndroid, - View, - StatusBar, -} = ReactNative; -const { - RootContainer: NavigationRootContainer, -} = NavigationExperimental; +const StatusBar = require('StatusBar'); +const StyleSheet = require('StyleSheet'); +const ToolbarAndroid = require('ToolbarAndroid'); const UIExplorerExampleList = require('./UIExplorerExampleList'); const UIExplorerList = require('./UIExplorerList'); const UIExplorerNavigationReducer = require('./UIExplorerNavigationReducer'); const UIExplorerStateTitleMap = require('./UIExplorerStateTitleMap'); const URIActionMap = require('./URIActionMap'); +const View = require('View'); -var DRAWER_WIDTH_LEFT = 56; +const DRAWER_WIDTH_LEFT = 56; type Props = { exampleFromAppetizeParams: string, @@ -49,14 +51,13 @@ type State = { }; class UIExplorerApp extends React.Component { - _handleOpenInitialExample: Function; + _handleAction: Function; + _renderDrawerContent: Function; state: State; constructor(props: Props) { super(props); - this._handleOpenInitialExample = this._handleOpenInitialExample.bind(this); - this.state = { - initialExampleUri: props.exampleFromAppetizeParams, - }; + this._handleAction = this._handleAction.bind(this); + this._renderDrawerContent = this._renderDrawerContent.bind(this); } componentWillMount() { @@ -64,38 +65,28 @@ class UIExplorerApp extends React.Component { } componentDidMount() { - // There's a race condition if we try to navigate to the specified example - // from the initial props at the same time the navigation logic is setting - // up the initial navigation state. This hack adds a delay to avoid this - // scenario. So after the initial example list is shown, we then transition - // to the initial example. - setTimeout(this._handleOpenInitialExample, 500); + Linking.getInitialURL().then((url) => { + AsyncStorage.getItem('UIExplorerAppState', (err, storedString) => { + const exampleAction = URIActionMap(this.props.exampleFromAppetizeParams); + const urlAction = URIActionMap(url); + const launchAction = exampleAction || urlAction; + if (err || !storedString) { + const initialAction = launchAction || {type: 'InitialAction'}; + this.setState(UIExplorerNavigationReducer(null, initialAction)); + return; + } + const storedState = JSON.parse(storedString); + if (launchAction) { + this.setState(UIExplorerNavigationReducer(storedState, launchAction)); + return; + } + this.setState(storedState); + }); + }); } render() { - return ( - { this._navigationRootRef = navRootRef; }} - reducer={UIExplorerNavigationReducer} - renderNavigation={this._renderApp.bind(this)} - linkingActionMap={URIActionMap} - /> - ); - } - - _handleOpenInitialExample() { - if (this.state.initialExampleUri) { - const exampleAction = URIActionMap(this.state.initialExampleUri); - if (exampleAction && this._navigationRootRef) { - this._navigationRootRef.handleNavigation(exampleAction); - } - } - this.setState({initialExampleUri: null}); - } - - _renderApp(navigationState, onNavigate) { - if (!navigationState) { + if (!this.state) { return null; } return ( @@ -110,42 +101,42 @@ class UIExplorerApp extends React.Component { this._overrideBackPressForDrawerLayout = false; }} ref={(drawer) => { this.drawer = drawer; }} - renderNavigationView={this._renderDrawerContent.bind(this, onNavigate)} + renderNavigationView={this._renderDrawerContent} statusBarBackgroundColor="#589c90"> - {this._renderNavigation(navigationState, onNavigate)} + {this._renderApp()} ); } - _renderDrawerContent(onNavigate) { + _renderDrawerContent() { return ( { - this.drawer && this.drawer.closeDrawer(); - onNavigate(action); - }} + onNavigate={this._handleAction} /> ); } - _renderNavigation(navigationState, onNavigate) { - if (navigationState.externalExample) { - var Component = UIExplorerList.Modules[navigationState.externalExample]; + _renderApp() { + const { + externalExample, + stack, + } = this.state; + if (externalExample) { + const Component = UIExplorerList.Modules[externalExample]; return ( { - onNavigate(NavigationRootContainer.getBackAction()); + this._handleAction({ type: 'BackAction' }); }} ref={(example) => { this._exampleRef = example; }} /> ); } - const {stack} = navigationState; const title = UIExplorerStateTitleMap(stack.children[stack.index]); const index = stack.children.length <= 1 ? 1 : stack.index; @@ -178,6 +169,7 @@ class UIExplorerApp extends React.Component { title={title} /> @@ -185,6 +177,17 @@ class UIExplorerApp extends React.Component { ); } + _handleAction(action: Object): boolean { + this.drawer && this.drawer.closeDrawer(); + const newState = UIExplorerNavigationReducer(this.state, action); + if (this.state !== newState) { + this.setState(newState); + AsyncStorage.setItem('UIExplorerAppState', JSON.stringify(this.state)); + return true; + } + return false; + } + _handleBackButtonPress() { if (this._overrideBackPressForDrawerLayout) { // This hack is necessary because drawer layout provides an imperative API @@ -200,12 +203,7 @@ class UIExplorerApp extends React.Component { ) { return true; } - if (this._navigationRootRef) { - return this._navigationRootRef.handleNavigation( - NavigationRootContainer.getBackAction() - ); - } - return false; + return this._handleAction({ type: 'BackAction' }); } } diff --git a/Examples/UIExplorer/UIExplorerApp.ios.js b/Examples/UIExplorer/UIExplorerApp.ios.js index be8366c09..6739b5645 100644 --- a/Examples/UIExplorer/UIExplorerApp.ios.js +++ b/Examples/UIExplorer/UIExplorerApp.ios.js @@ -23,6 +23,8 @@ */ 'use strict'; +const AsyncStorage = require('AsyncStorage'); +const Linking = require('Linking'); const React = require('react'); const ReactNative = require('react-native'); const UIExplorerList = require('./UIExplorerList.ios'); @@ -42,7 +44,6 @@ const { const { CardStack: NavigationCardStack, Header: NavigationHeader, - RootContainer: NavigationRootContainer, } = NavigationExperimental; import type { NavigationSceneRendererProps } from 'NavigationTypeDefinition'; @@ -55,81 +56,86 @@ type Props = { exampleFromAppetizeParams: string, }; -type State = { - initialExampleUri: ?string, +type State = UIExplorerNavigationState & { + externalExample?: string, }; class UIExplorerApp extends React.Component { - _navigationRootRef: ?NavigationRootContainer; - _renderNavigation: Function; _renderOverlay: Function; _renderScene: Function; _renderCard: Function; _renderTitleComponent: Function; - _handleOpenInitialExample: Function; + _handleAction: Function; state: State; + constructor(props: Props) { super(props); - this._handleOpenInitialExample = this._handleOpenInitialExample.bind(this); - this.state = { - initialExampleUri: props.exampleFromAppetizeParams, - }; } + componentWillMount() { - this._renderNavigation = this._renderNavigation.bind(this); + this._handleAction = this._handleAction.bind(this); this._renderOverlay = this._renderOverlay.bind(this); this._renderScene = this._renderScene.bind(this); this._renderTitleComponent = this._renderTitleComponent.bind(this); } + componentDidMount() { - // There's a race condition if we try to navigate to the specified example - // from the initial props at the same time the navigation logic is setting - // up the initial navigation state. This hack adds a delay to avoid this - // scenario. So after the initial example list is shown, we then transition - // to the initial example. - setTimeout(this._handleOpenInitialExample, 500); + Linking.getInitialURL().then((url) => { + AsyncStorage.getItem('UIExplorerAppState', (err, storedString) => { + const exampleAction = URIActionMap(this.props.exampleFromAppetizeParams); + const urlAction = URIActionMap(url); + const launchAction = exampleAction || urlAction; + if (err || !storedString) { + const initialAction = launchAction || {type: 'InitialAction'}; + this.setState(UIExplorerNavigationReducer(null, initialAction)); + return; + } + const storedState = JSON.parse(storedString); + if (launchAction) { + this.setState(UIExplorerNavigationReducer(storedState, launchAction)); + return; + } + this.setState(storedState); + }); + }); + + Linking.addEventListener('url', (url) => { + this._handleAction(URIActionMap(url)); + }); } - render() { - return ( - { this._navigationRootRef = navRootRef; }} - renderNavigation={this._renderNavigation} - linkingActionMap={URIActionMap} - /> - ); - } - _handleOpenInitialExample() { - if (this.state.initialExampleUri) { - const exampleAction = URIActionMap(this.state.initialExampleUri); - if (exampleAction && this._navigationRootRef) { - this._navigationRootRef.handleNavigation(exampleAction); - } + + _handleAction(action: Object) { + if (!action) { + return; + } + const newState = UIExplorerNavigationReducer(this.state, action); + if (this.state !== newState) { + this.setState(newState); + AsyncStorage.setItem('UIExplorerAppState', JSON.stringify(this.state)); } - this.setState({initialExampleUri: null}); } - _renderNavigation(navigationState: UIExplorerNavigationState, onNavigate: Function) { - if (!navigationState) { + + render() { + if (!this.state) { return null; } - if (navigationState.externalExample) { - var Component = UIExplorerList.Modules[navigationState.externalExample]; + if (this.state.externalExample) { + const Component = UIExplorerList.Modules[this.state.externalExample]; return ( { - onNavigate(NavigationRootContainer.getBackAction()); + this._handleAction({ type: 'BackAction' }); }} /> ); } - const {stack} = navigationState; return ( ); } @@ -156,6 +162,7 @@ class UIExplorerApp extends React.Component { if (state.key === 'AppList') { return ( UIExplorerApp); UIExplorerList.ComponentExamples.concat(UIExplorerList.APIExamples).forEach((Example: UIExplorerExample) => { const ExampleModule = Example.module; if (ExampleModule.displayName) { - var Snapshotter = React.createClass({ + const Snapshotter = React.createClass({ render: function() { - var Renderable = UIExplorerExampleList.makeRenderable(ExampleModule); + const Renderable = UIExplorerExampleList.makeRenderable(ExampleModule); return ( diff --git a/Examples/UIExplorer/UIExplorerExampleList.js b/Examples/UIExplorer/UIExplorerExampleList.js index 5eb6a6b44..f9cb279d6 100644 --- a/Examples/UIExplorer/UIExplorerExampleList.js +++ b/Examples/UIExplorer/UIExplorerExampleList.js @@ -27,7 +27,6 @@ const React = require('react'); const StyleSheet = require('StyleSheet'); const Text = require('Text'); const TextInput = require('TextInput'); -const NavigationContainer = require('NavigationContainer'); const TouchableHighlight = require('TouchableHighlight'); const View = require('View'); const UIExplorerActions = require('./UIExplorerActions'); @@ -56,6 +55,13 @@ class UIExplorerExampleList extends React.Component { }) { } + + static makeRenderable(example: any): ReactClass { + return example.examples ? + createExamplePage(null, example) : + example; + } + render(): ?ReactElement { const filterText = this.props.filter || ''; const filterRegex = new RegExp(String(filterText), 'i'); @@ -160,15 +166,6 @@ class UIExplorerExampleList extends React.Component { } } -function makeRenderable(example: any): ReactClass { - return example.examples ? - createExamplePage(null, example) : - example; -} - -UIExplorerExampleList = NavigationContainer.create(UIExplorerExampleList); -UIExplorerExampleList.makeRenderable = makeRenderable; - const styles = StyleSheet.create({ listContainer: { flex: 1, diff --git a/Libraries/CustomComponents/NavigationExperimental/NavigationCard.js b/Libraries/CustomComponents/NavigationExperimental/NavigationCard.js index 86ecffce8..403d7da65 100644 --- a/Libraries/CustomComponents/NavigationExperimental/NavigationCard.js +++ b/Libraries/CustomComponents/NavigationExperimental/NavigationCard.js @@ -35,7 +35,6 @@ const Animated = require('Animated'); const NavigationCardStackPanResponder = require('NavigationCardStackPanResponder'); const NavigationCardStackStyleInterpolator = require('NavigationCardStackStyleInterpolator'); -const NavigationContainer = require('NavigationContainer'); const NavigationPagerPanResponder = require('NavigationPagerPanResponder'); const NavigationPagerStyleInterpolator = require('NavigationPagerStyleInterpolator'); const NavigationPointerEventsContainer = require('NavigationPointerEventsContainer'); @@ -139,6 +138,11 @@ class NavigationCard extends React.Component { ); } + + static CardStackPanResponder = NavigationCardStackPanResponder; + static CardStackStyleInterpolator = NavigationCardStackStyleInterpolator; + static PagerPanResponder = NavigationPagerPanResponder; + static PagerStyleInterpolator = NavigationPagerStyleInterpolator; } const styles = StyleSheet.create({ @@ -157,12 +161,5 @@ const styles = StyleSheet.create({ }); NavigationCard = NavigationPointerEventsContainer.create(NavigationCard); -NavigationCard = NavigationContainer.create(NavigationCard); - -// Export these buil-in interaction modules. -NavigationCard.CardStackPanResponder = NavigationCardStackPanResponder; -NavigationCard.CardStackStyleInterpolator = NavigationCardStackStyleInterpolator; -NavigationCard.PagerPanResponder = NavigationPagerPanResponder; -NavigationCard.PagerStyleInterpolator = NavigationPagerStyleInterpolator; module.exports = NavigationCard; diff --git a/Libraries/CustomComponents/NavigationExperimental/NavigationCardStack.js b/Libraries/CustomComponents/NavigationExperimental/NavigationCardStack.js index e7256899d..90e0837b5 100644 --- a/Libraries/CustomComponents/NavigationExperimental/NavigationCardStack.js +++ b/Libraries/CustomComponents/NavigationExperimental/NavigationCardStack.js @@ -35,7 +35,6 @@ const NavigationAnimatedView = require('NavigationAnimatedView'); const NavigationCard = require('NavigationCard'); const NavigationCardStackStyleInterpolator = require('NavigationCardStackStyleInterpolator'); -const NavigationContainer = require('NavigationContainer'); const NavigationCardStackPanResponder = require('NavigationCardStackPanResponder'); const NavigationPropTypes = require('NavigationPropTypes'); const React = require('React'); @@ -48,6 +47,7 @@ const {PropTypes} = React; const {Directions} = NavigationCardStackPanResponder; import type { + NavigationActionCaller, NavigationParentState, NavigationSceneRenderer, NavigationSceneRendererProps, @@ -60,6 +60,7 @@ import type { type Props = { direction: NavigationGestureDirection, navigationState: NavigationParentState, + onNavigate: NavigationActionCaller, renderOverlay: ?NavigationSceneRenderer, renderScene: NavigationSceneRenderer, }; @@ -89,6 +90,7 @@ class NavigationCardStack extends React.Component { static propTypes = { direction: PropTypes.oneOf([Directions.HORIZONTAL, Directions.VERTICAL]), navigationState: NavigationPropTypes.navigationParentState.isRequired, + onNavigate: NavigationPropTypes.SceneRenderer.onNavigate, renderOverlay: PropTypes.func, renderScene: PropTypes.func.isRequired, }; @@ -120,6 +122,7 @@ class NavigationCardStack extends React.Component { navigationState={this.props.navigationState} renderOverlay={this.props.renderOverlay} renderScene={this._renderScene} + onNavigate={this.props.onNavigate} // $FlowFixMe - style should be declared style={[styles.animatedView, this.props.style]} /> @@ -155,4 +158,4 @@ const styles = StyleSheet.create({ }, }); -module.exports = NavigationContainer.create(NavigationCardStack); +module.exports = NavigationCardStack; diff --git a/Libraries/CustomComponents/NavigationExperimental/NavigationHeader.js b/Libraries/CustomComponents/NavigationExperimental/NavigationHeader.js index b1e58136c..f65beb616 100644 --- a/Libraries/CustomComponents/NavigationExperimental/NavigationHeader.js +++ b/Libraries/CustomComponents/NavigationExperimental/NavigationHeader.js @@ -34,7 +34,6 @@ const React = require('React'); const ReactNative = require('react-native'); -const NavigationContainer = require('NavigationContainer'); const NavigationHeaderTitle = require('NavigationHeaderTitle'); const NavigationHeaderBackButton = require('NavigationHeaderBackButton'); const NavigationPropTypes = require('NavigationPropTypes'); @@ -49,6 +48,7 @@ const { } = ReactNative; import type { + NavigationActionCaller, NavigationSceneRenderer, NavigationSceneRendererProps, NavigationStyleInterpolator, @@ -64,8 +64,9 @@ type Props = NavigationSceneRendererProps & { renderLeftComponent: NavigationSceneRenderer, renderRightComponent: NavigationSceneRenderer, renderTitleComponent: NavigationSceneRenderer, - style?: any; - viewProps?: any; + onNavigate: NavigationActionCaller, + style?: any, + viewProps?: any, }; type SubViewName = 'left' | 'title' | 'right'; @@ -86,7 +87,14 @@ class NavigationHeader extends React.Component { }, renderLeftComponent: (props: NavigationSceneRendererProps) => { - return props.scene.index > 0 ? : null; + if (props.scene.index === 0) { + return null; + } + return ( + + ); }, renderRightComponent: (props: NavigationSceneRendererProps) => { @@ -199,6 +207,11 @@ class NavigationHeader extends React.Component { ); } + + static HEIGHT = APPBAR_HEIGHT + STATUSBAR_HEIGHT; + static Title = NavigationHeaderTitle; + static BackButton = NavigationHeaderBackButton; + } const styles = StyleSheet.create({ @@ -244,10 +257,4 @@ const styles = StyleSheet.create({ }, }); -const NavigationHeaderContainer = NavigationContainer.create(NavigationHeader); - -NavigationHeaderContainer.HEIGHT = APPBAR_HEIGHT + STATUSBAR_HEIGHT; -NavigationHeaderContainer.Title = NavigationHeaderTitle; -NavigationHeaderContainer.BackButton = NavigationHeaderBackButton; - -module.exports = NavigationHeaderContainer; +module.exports = NavigationHeader; diff --git a/Libraries/CustomComponents/NavigationExperimental/NavigationHeaderBackButton.js b/Libraries/CustomComponents/NavigationExperimental/NavigationHeaderBackButton.js index f72ecda84..26dd0404b 100644 --- a/Libraries/CustomComponents/NavigationExperimental/NavigationHeaderBackButton.js +++ b/Libraries/CustomComponents/NavigationExperimental/NavigationHeaderBackButton.js @@ -1,4 +1,11 @@ /** + * Copyright (c) 2013-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. + * * The examples provided by Facebook are for non-commercial testing and * evaluation purposes only. * @@ -18,8 +25,6 @@ const React = require('react'); const ReactNative = require('react-native'); -const NavigationContainer = require('NavigationContainer'); -const NavigationRootContainer = require('NavigationRootContainer'); const { Image, @@ -33,7 +38,7 @@ type Props = { } const NavigationHeaderBackButton = (props: Props) => ( - props.onNavigate(NavigationRootContainer.getBackAction())}> + props.onNavigate({type: 'BackAction'})}> ); @@ -57,4 +62,4 @@ const styles = StyleSheet.create({ } }); -module.exports = NavigationContainer.create(NavigationHeaderBackButton); +module.exports = NavigationHeaderBackButton; diff --git a/Libraries/NavigationExperimental/NavigationAnimatedView.js b/Libraries/NavigationExperimental/NavigationAnimatedView.js index 009a26cb4..f9faa5cc4 100644 --- a/Libraries/NavigationExperimental/NavigationAnimatedView.js +++ b/Libraries/NavigationExperimental/NavigationAnimatedView.js @@ -12,7 +12,6 @@ 'use strict'; const Animated = require('Animated'); -const NavigationContainer = require('NavigationContainer'); const NavigationPropTypes = require('NavigationPropTypes'); const NavigationScenesReducer = require('NavigationScenesReducer'); const React = require('React'); @@ -243,6 +242,4 @@ const styles = StyleSheet.create({ }, }); -NavigationAnimatedView = NavigationContainer.create(NavigationAnimatedView); - module.exports = NavigationAnimatedView; diff --git a/Libraries/NavigationExperimental/NavigationContainer.js b/Libraries/NavigationExperimental/NavigationContainer.js deleted file mode 100644 index bd17a636f..000000000 --- a/Libraries/NavigationExperimental/NavigationContainer.js +++ /dev/null @@ -1,53 +0,0 @@ -/** - * 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 NavigationContainer - * @flow - */ -'use strict'; - -var React = require('React'); -var NavigationRootContainer = require('NavigationRootContainer'); - -function createNavigationContainer( - Component: ReactClass, -): ReactClass & Object { - class NavigationComponent extends React.Component { - render() { - return ( - - ); - } - getNavigationHandler() { - return this.props.onNavigate || this.context.onNavigate; - } - getChildContext() { - return { - onNavigate: this.getNavigationHandler(), - }; - } - } - NavigationComponent.contextTypes = { - onNavigate: React.PropTypes.func, - }; - NavigationComponent.childContextTypes = { - onNavigate: React.PropTypes.func, - }; - return NavigationComponent; -} - -var NavigationContainer = { - create: createNavigationContainer, - RootContainer: NavigationRootContainer, -}; - - -module.exports = NavigationContainer; diff --git a/Libraries/NavigationExperimental/NavigationExperimental.js b/Libraries/NavigationExperimental/NavigationExperimental.js index 2ace82853..14ecaca14 100644 --- a/Libraries/NavigationExperimental/NavigationExperimental.js +++ b/Libraries/NavigationExperimental/NavigationExperimental.js @@ -14,10 +14,8 @@ const NavigationAnimatedView = require('NavigationAnimatedView'); const NavigationCard = require('NavigationCard'); const NavigationCardStack = require('NavigationCardStack'); -const NavigationContainer = require('NavigationContainer'); const NavigationHeader = require('NavigationHeader'); const NavigationReducer = require('NavigationReducer'); -const NavigationRootContainer = require('NavigationRootContainer'); const NavigationStateUtils = require('NavigationStateUtils'); const NavigationView = require('NavigationView'); const NavigationPropTypes = require('NavigationPropTypes'); @@ -27,10 +25,6 @@ const NavigationExperimental = { StateUtils: NavigationStateUtils, Reducer: NavigationReducer, - // Containers - Container: NavigationContainer, - RootContainer: NavigationRootContainer, - // Views View: NavigationView, AnimatedView: NavigationAnimatedView, diff --git a/Libraries/NavigationExperimental/NavigationRootContainer.js b/Libraries/NavigationExperimental/NavigationRootContainer.js deleted file mode 100644 index 985a1fb32..000000000 --- a/Libraries/NavigationExperimental/NavigationRootContainer.js +++ /dev/null @@ -1,191 +0,0 @@ -/** - * 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 NavigationRootContainer - * @flow - */ -'use strict'; - -const AsyncStorage = require('AsyncStorage'); -const Linking = require('Linking'); -const NavigationPropTypes = require('NavigationPropTypes'); -const NavigationStateUtils = require('NavigationStateUtils'); -const Platform = require('Platform'); -const React = require('React'); - -import type { - NavigationAction, - NavigationParentState, - NavigationReducer, - NavigationRenderer, -} from 'NavigationTypeDefinition'; - -export type BackAction = { - type: 'BackAction', -}; - -type Props = { - /* - * The default action to be passed into the reducer when getting the first - * state. Defaults to {type: 'RootContainerInitialAction'} - */ - initialAction: NavigationAction, - - /* - * Provide linkingActionMap to instruct the container to subscribe to linking - * events, and use this mapper to convert URIs into actions that your app can - * handle - */ - linkingActionMap: ?((uri: ?string) => NavigationAction), - - /* - * Provide this key, and the container will store the navigation state in - * AsyncStorage through refreshes, with the provided key - */ - persistenceKey: ?string, - - - /* - * A function that will output the latest navigation state as a function of - * the (optional) previous state, and an action - */ - reducer: NavigationReducer, - - - /* - * Set up the rendering of the app for a given navigation state - */ - renderNavigation: NavigationRenderer, -}; - -type State = { - navState: ?NavigationParentState, -}; - -function getBackAction(): BackAction { - return { type: 'BackAction' }; -} - -const {PropTypes} = React; - -class NavigationRootContainer extends React.Component { - _handleOpenURLEvent: Function; - - props: Props; - state: State; - - static propTypes = { - initialAction: NavigationPropTypes.action.isRequired, - linkingActionMap: PropTypes.func, - persistenceKey: PropTypes.string, - reducer: PropTypes.func.isRequired, - renderNavigation: PropTypes.func.isRequired, - }; - - static defaultProps = { - initialAction: { type: 'RootContainerInitialAction' }, - }; - - static childContextTypes = { - onNavigate: PropTypes.func, - }; - - - static getBackAction = getBackAction; - - constructor(props: Props) { - super(props); - - let navState = null; - if (!this.props.persistenceKey) { - navState = NavigationStateUtils.getParent( - this.props.reducer(null, props.initialAction) - ); - } - this.state = { navState }; - } - - componentWillMount(): void { - (this: any).handleNavigation = this.handleNavigation.bind(this); - (this: any)._handleOpenURLEvent = this._handleOpenURLEvent.bind(this); - } - - componentDidMount(): void { - if (this.props.linkingActionMap) { - Linking.getInitialURL().then(this._handleOpenURL.bind(this)); - Platform.OS === 'ios' && Linking.addEventListener('url', this._handleOpenURLEvent); - } - if (this.props.persistenceKey) { - AsyncStorage.getItem(this.props.persistenceKey, (err, storedString) => { - if (err || !storedString) { - this.setState({ - // $FlowFixMe - navState is missing properties :( - navState: this.props.reducer(null, this.props.initialAction), - }); - return; - } - this.setState({ - navState: JSON.parse(storedString), - }); - }); - } - } - - componentWillUnmount(): void { - if (Platform.OS === 'ios') { - Linking.removeEventListener('url', this._handleOpenURLEvent); - } - } - - _handleOpenURLEvent(event: {url: string}): void { - this._handleOpenURL(event.url); - } - - _handleOpenURL(url: ?string): void { - if (!this.props.linkingActionMap) { - return; - } - const action = this.props.linkingActionMap(url); - if (action) { - this.handleNavigation(action); - } - } - - getChildContext(): Object { - return { - onNavigate: this.handleNavigation, - }; - } - - handleNavigation(action: Object): boolean { - const navState = this.props.reducer(this.state.navState, action); - if (navState === this.state.navState) { - return false; - } - this.setState({ - // $FlowFixMe - navState is missing properties :( - navState, - }); - - if (this.props.persistenceKey) { - AsyncStorage.setItem(this.props.persistenceKey, JSON.stringify(navState)); - } - - return true; - } - - render(): ReactElement { - const navigation = this.props.renderNavigation( - this.state.navState, - this.handleNavigation - ); - return navigation; - } -} - -module.exports = NavigationRootContainer; diff --git a/Libraries/NavigationExperimental/NavigationView.js b/Libraries/NavigationExperimental/NavigationView.js index 6da86c86d..533b9ea44 100644 --- a/Libraries/NavigationExperimental/NavigationView.js +++ b/Libraries/NavigationExperimental/NavigationView.js @@ -12,7 +12,6 @@ 'use strict'; const Animated = require('Animated'); -const NavigationContainer = require('NavigationContainer'); const React = require('React'); const StyleSheet = require('StyleSheet'); const View = require('View'); @@ -179,4 +178,4 @@ const styles = StyleSheet.create({ }, }); -module.exports = NavigationContainer.create(NavigationView); +module.exports = NavigationView; diff --git a/Libraries/NavigationExperimental/Reducer/NavigationStackReducer.js b/Libraries/NavigationExperimental/Reducer/NavigationStackReducer.js index f95198fa1..6fd3ac39d 100644 --- a/Libraries/NavigationExperimental/Reducer/NavigationStackReducer.js +++ b/Libraries/NavigationExperimental/Reducer/NavigationStackReducer.js @@ -11,7 +11,7 @@ */ 'use strict'; -var NavigationStateUtils = require('NavigationStateUtils'); +const NavigationStateUtils = require('NavigationStateUtils'); import type { NavigationState, @@ -19,14 +19,6 @@ import type { NavigationReducer, } from 'NavigationTypeDefinition'; -import type { - BackAction, -} from 'NavigationRootContainer'; - -export type NavigationStackReducerAction = BackAction | { - type: string, -}; - export type ReducerForStateHandler = (state: NavigationState) => NavigationReducer; export type PushedReducerForActionHandler = (action: any, lastState: NavigationParentState) => ?NavigationReducer;