Skip to content

Instantly share code, notes, and snippets.

@mmazzarolo
Last active August 11, 2016 00:05
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mmazzarolo/5a4f235b6b07fa14247c0fb081b1f13f to your computer and use it in GitHub Desktop.
Save mmazzarolo/5a4f235b6b07fa14247c0fb081b1f13f to your computer and use it in GitHub Desktop.
NavigationExperimental setup
// Redux navigations tore
{
key: '1',
index: 0,
children: [{ key: 'museumList', title: 'I musei' }],
isDrawerOpen: false
}
// Navigation structure
src
├── actions
│ ├── types.js
│ ├── navigationActions.js
│ └── index.js
├── components
│ ├── NavBar.ios.js
│ └── NavBar.android.js
├── navigation
│ ├── Drawer.js // In my opinion the Drawer should be tied to the navigation
│ ├── index.js // Connects Redux to navigation experimental
│ ├── NavigationRouter.js // The Navigator Experimental component
│ └── routes.js // Routes with each property (should be seen in drawer? etc...)
└──reducers
├── index.js
└── NavigationReducer.js
import createAction from '../actions/createAction'
import Types from '../actions/types'
import routes from '../navigation/routes'
const navigatePush = (child) => {
child = typeof child === 'string' ? { key: child, title: child } : child
return createAction(Types.NAV_PUSH, { child })
}
const navigatePop = () =>
createAction(Types.NAV_POP)
const navigateJumpToKey = (key) =>
createAction(Types.NAV_JUMP_TO_KEY, { key })
const navigateJumpToIndex = (index) =>
createAction(Types.NAV_JUMP_TO_INDEX, { index })
const navigateReset = (children, index) =>
createAction(Types.NAV_RESET, { children, index })
const openDrawer = () =>
createAction(Types.SET_DRAWER_OPEN, { isDrawerOpen: true })
const closeDrawer = () =>
createAction(Types.SET_DRAWER_OPEN, { isDrawerOpen: false })
const navigateToMuseumList = () =>
navigatePush(routes.museumList)
const navigateToMuseumDetail = () =>
navigatePush(routes.museumDetail)
export default {
navigatePush,
navigatePop,
navigateJumpToKey,
navigateJumpToIndex,
navigateReset,
openDrawer,
closeDrawer,
navigateToMuseumList,
navigateToMuseumDetail,
navigateToArtifactDetail,
navigateToBookmarkedArtifacts
}
import React, { Component } from 'react'
import { StyleSheet } from 'react-native'
import Colors from '../theme/colors'
import Metrics from '../theme/metrics'
import Icon from 'react-native-vector-icons/Ionicons'
export default class NavBar extends Component {
static propTypes = {
title: React.PropTypes.string,
onLeftPress: React.PropTypes.func,
showDrawer: React.PropTypes.bool
}
render () {
const { title, onLeftPress, showDrawer } = this.props
return (
<Icon.ToolbarAndroid
navIconName={showDrawer ? 'navicon-round' : 'android-arrow-back'}
onIconClicked={onLeftPress}
style={styles.toolbar}
titleColor='white'
title={title}
/>
)
}
}
const styles = StyleSheet.create({
toolbar: {
backgroundColor: Colors.PRIMARY,
height: Metrics.NAVBAR_HEIGHT,
position: 'absolute', top: 0, left: 0, width: Metrics.DEVICE_WIDTH // TO-DO
}
})
import React, { Component, PropTypes } from 'react'
import { NavigationExperimental, StyleSheet } from 'react-native'
import Colors from '../theme/colors'
import Icon from 'react-native-vector-icons/Ionicons'
import TouchableView from '../components/TouchableView'
const { Header } = NavigationExperimental
export default class NavigationHeader extends Component {
static propTypes = {
title: PropTypes.string,
onLeftPress: PropTypes.func,
showDrawer: PropTypes.bool
}
_renderTitleComponent = () => {
const { title } = this.props
return (
<Header.Title textStyle={styles.titleText}>
{title}
</Header.Title>
)
}
_renderLeftComponent = () => {
const { onLeftPress, showDrawer } = this.props
return (
<TouchableView onPress={onLeftPress} style={styles.leftButton}>
<Icon name={showDrawer ? 'navicon-round' : 'chevron-left'} size={18} color='white' />
</TouchableView>
)
}
render () {
return (
<Header
{...this.props}
style={styles.container}
renderTitleComponent={this._renderTitleComponent}
renderLeftComponent={this._renderLeftComponent}
/>
)
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: Colors.PRIMARY
},
titleText: {
color: 'white'
},
leftButton: {
padding: 14
}
})
import React, { Component, PropTypes } from 'react'
import { Image, StyleSheet, View } from 'react-native'
import R from 'ramda'
import { connect } from 'react-redux'
import ReactNativeDrawer from 'react-native-drawer'
import Actions from '../actions'
import DrawerItem from '../components/DrawerItem'
import Colors from '../theme/colors'
import Metrics from '../theme/metrics'
import drawerSelector from '../selectors/drawerSelector'
import routes from '../navigation/routes'
class Drawer extends Component {
static propTypes = {
navigationState: PropTypes.object,
currentRoute: PropTypes.string,
isDrawerOpen: PropTypes.bool,
isDrawerEnabled: PropTypes.bool,
navigateReset: PropTypes.func,
navigatePush: PropTypes.func,
openDrawer: PropTypes.func,
closeDrawer: PropTypes.func,
children: PropTypes.any
}
_renderContent = () => {
return (
<View style={styles.container}>
<View style={styles.header}>
</View>
<View style={styles.body}>
{this._createDrawerItems()}
</View>
</View>
)
}
_createDrawerItems = () => {
const { currentRoute, navigateReset } = this.props
const drawerItems = []
R.values(routes).forEach(r => {
if (!r.showInDrawer) return
drawerItems.push(
<DrawerItem
key={r.key}
text={r.title}
iconName={r.drawerIcon}
onPress={() => {
// SAD SAD SAD :( NOT WORKING AS INTENDED
navigateReset([{ key: r.key, title: r.title }], 0)
// this.props.navigateToMuseumList()
// navigateReset([{ key: r.key, title: r.title }], 0)
// navigateJumpToIndex(0)
// // navigatePush()
}}
isSelected={r.key === currentRoute}
/>
)
})
return drawerItems
}
render () {
const { children, isDrawerOpen, isDrawerEnabled, openDrawer, closeDrawer } = this.props
const style = {
drawer: { shadowColor: '#000000', shadowOpacity: 0.8, shadowRadius: 3 },
main: { paddingLeft: 3 },
mainOverlay: { backgroundColor: 'black', opacity: 0 }
}
const tweenHandler = (ratio) => ({ mainOverlay: { opacity: (ratio / 2) } })
return (
<ReactNativeDrawer
ref='drawer'
type='overlay'
content={this._renderContent()}
captureGestures='open'
tapToClose={true}
acceptPan={true}
openDrawerOffset={0.2}
closedDrawerOffset={-3}
panOpenMask={0.2}
negotiatePan={true}
styles={style}
tweenHandler={tweenHandler}
open={isDrawerOpen}
onOpen={openDrawer}
onClose={closeDrawer}
disabled={!isDrawerEnabled}
>
{children}
</ReactNativeDrawer>
)
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: 'white'
},
header: {
backgroundColor: Colors.PRIMARY_LIGHTER,
height: Metrics.DEVICE_HEIGHT / 4
}
})
export default connect(drawerSelector, Actions)(Drawer)
import { NavigationExperimental } from 'react-native'
import { connect } from 'react-redux'
import NavigationRouter from './NavigationRouter'
import Actions from '../actions'
const { Card, RootContainer } = NavigationExperimental
const mapStateToProps = (state) => ({ state.navigation })
const mapDispatchToProps = (dispatch) => ({
onNavigate: (action) => {
console.log('mapDispatchToProps', action)
// Two types of actions are likely to be passed, both representing "back"
// style actions. Check if a type has been indicated, and try to match it.
if (action.type && (
action.type === RootContainer.getBackAction().type ||
action.type === Card.CardStackPanResponder.Actions.BACK.type)
) {
dispatch(Actions.navigatePop())
} else {
// Currently unused by NavigationExperimental (only passes back actions),
// but could potentially be used by custom components.
dispatch(Actions.navigatePush(action))
}
},
openDrawer: () => dispatch(Actions.openDrawer()),
closeDrawer: () => dispatch(Actions.closeDrawer()),
enableDrawer: () => dispatch(Actions.enableDrawer()),
disableDrawer: () => dispatch(Actions.disableDrawer())
})
export default connect(mapStateToProps, mapDispatchToProps)(NavigationRouter)
import React, { Component, PropTypes } from 'react'
import { NavigationExperimental, StyleSheet } from 'react-native'
import routes from './routes'
import NavBar from '../components/NavBar'
import Metrics from '../theme/metrics'
import Drawer from './Drawer'
import MuseumListScreen from '../containers/MuseumListScreen'
import MuseumDetailScreen from '../containers/MuseumDetailScreen'
const { AnimatedView, Card, RootContainer } = NavigationExperimental
export default class NavigationRouter extends Component {
static propTypes = {
navigationState: PropTypes.object,
onNavigate: PropTypes.func.isRequired,
openDrawer: PropTypes.func.isRequired
}
_renderScene = (navigatorProps) => {
const { navigationState } = navigatorProps.scene
const key = navigationState.key
switch (key) {
case 'museumList': return <MuseumListScreen />
case 'museumDetail': return <MuseumDetailScreen />
}
}
_renderToolbar = (navigatorProps) => {
const { key, title } = navigatorProps.scene.navigationState
const { openDrawer, onNavigate } = this.props
const showDrawer = routes[key] && routes[key].showInDrawer
const onLeftPress = () => {
showDrawer ? openDrawer() : onNavigate(RootContainer.getBackAction())
}
return (
<NavBar title={title} onLeftPress={onLeftPress} {...navigatorProps} showDrawer={showDrawer} />
)
}
render () {
const { navigationState, onNavigate } = this.props
return (
// Note that we are not using a NavigationRootContainer here because Redux is handling
// the reduction of our state for us. Instead, we grab the navigationState we have in
// our Redux store and pass it directly to the <NavigationAnimatedView />.
<Drawer>
<AnimatedView
navigationState={navigationState}
style={styles.container}
onNavigate={onNavigate}
renderOverlay={this._renderToolbar}
renderScene={props => (
// Again, we pass our navigationState from the Redux store to <NavigationCard />.
// Finally, we'll render out our scene based on navigationState in _renderScene().
<Card
{...props}
panHandlers={null}
renderScene={this._renderScene}
key={props.scene.navigationState.key}
/>
)}
/>
</Drawer>
)
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
paddingTop: Metrics.NAVBAR_HEIGHT
}
})
import i18n from '../i18n'
export default {
museumList: {
key: 'museumList',
title: i18n.t('SCREEN_TITLE_MUSEUM_LIST'),
showInDrawer: true,
drawerIcon: 'home'
},
museumDetail: {
key: 'museumDetail',
title: i18n.t('SCREEN_TITLE_MUSEUM_DETAIL'),
showInDrawer: false
},
news: {
key: 'news',
title: i18n.t('SCREEN_TITLE_NEWS'),
showInDrawer: true,
drawerIcon: 'speakerphone'
},
settings: {
key: 'settings',
title: i18n.t('SCREEN_TITLE_SETTINGS'),
showInDrawer: true,
drawerIcon: 'android-settings'
}
}
import Immutable from 'seamless-immutable'
import * as NavigationStateUtils from 'NavigationStateUtils'
import Types from '../actions/types'
import createReducer from '../reducers/createReducer'
export const INITIAL_STATE = Immutable({
key: 'MAIN',
index: 0,
children: [{ key: 'museumList', title: 'Museums' }],
isDrawerOpen: false
})
const navPush = (state, { child }) => {
if (state.children[state.index].key === (child && child.key)) return state
const newState = NavigationStateUtils.push(state.asMutable({ deep: true }), child)
return Immutable(newState)
}
const navPop = (state) => {
if (state.index === 0 || state.children.length === 1) return state
const newState = NavigationStateUtils.pop(state.asMutable({ deep: true }))
return Immutable(newState)
}
const navJumpToKey = (state, { key }) => {
const newState = NavigationStateUtils.jumpTo(state.asMutable({ deep: true }), key)
return Immutable(newState)
}
const navJumpToIndex = (state, { index }) => {
const newState = NavigationStateUtils.jumpTo(state.asMutable({ deep: true }), index)
return Immutable(newState)
}
const navReset = (state, { index, children }) => {
// SAD SAD SAD :( NOT WORKING AS INTENDED
// const emptyState = R.clone(INITIAL_STATE)
// emptyState.children = children
// return Immutable(NavigationStateUtils.set(null, (parseInt(state.key) + 1).toString(), children, 0))
// return Immutable(NavigationStateUtils.set(null, 'MainNavigation', children, 0))
// const newState = ({ ...state, index, children })
// console.log(newState)
// return newState
}
const setDrawerOpen = (state, { isDrawerOpen }) =>
state.merge({ isDrawerOpen })
const ACTION_HANDLERS = {
[Types.NAV_PUSH]: navPush,
[Types.NAV_POP]: navPop,
[Types.NAV_JUMP_TO_KEY]: navJumpToKey,
[Types.NAV_JUMP_TO_INDEX]: navJumpToIndex,
[Types.NAV_RESET]: navReset,
[Types.SET_DRAWER_OPEN]: setDrawerOpen
}
export default createReducer(INITIAL_STATE, ACTION_HANDLERS)
@yonahforst
Copy link

Any work around for the sad, sad, sad state of reset?

@cyprusglobe
Copy link

@mmazzarolo any idea if you can throw this together in an example react-native app repo?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment