diff --git a/index.js b/index.js index 33a7dfb3b..e32cd8f01 100644 --- a/index.js +++ b/index.js @@ -1,4 +1,5 @@ import Actions from './src/Actions'; +import * as ActionConst from './src/ActionConst'; import DefaultRenderer from './src/DefaultRenderer'; import Modal from './src/Modal'; import NavBar from './src/NavBar'; @@ -12,6 +13,7 @@ import Util from './src/Util'; export { Actions, + ActionConst, DefaultRenderer, Modal, NavBar, diff --git a/src/ActionConst.js b/src/ActionConst.js new file mode 100644 index 000000000..de7c4ad8d --- /dev/null +++ b/src/ActionConst.js @@ -0,0 +1,9 @@ +export const JUMP = 'REACT_NATIVE_ROUTER_FLUX_JUMP'; +export const PUSH = 'REACT_NATIVE_ROUTER_FLUX_PUSH'; +export const REPLACE = 'REACT_NATIVE_ROUTER_FLUX_REPLACE'; +export const BACK = 'REACT_NATIVE_ROUTER_FLUX_BACK'; +export const BACK_ACTION = 'REACT_NATIVE_ROUTER_FLUX_BACK_ACTION'; +export const POP_TO = 'REACT_NATIVE_ROUTER_FLUX_POP_TO'; +export const REFRESH = 'REACT_NATIVE_ROUTER_FLUX_REFRESH'; +export const RESET = 'REACT_NATIVE_ROUTER_FLUX_RESET'; +export const FOCUS = 'REACT_NATIVE_ROUTER_FLUX_FOCUS'; diff --git a/src/Actions.js b/src/Actions.js index 42d384adc..81654cca8 100644 --- a/src/Actions.js +++ b/src/Actions.js @@ -8,15 +8,28 @@ */ import { assert } from './Util'; import Scene from './Scene'; -export const JUMP_ACTION = 'REACT_NATIVE_ROUTER_FLUX_JUMP'; -export const PUSH_ACTION = 'REACT_NATIVE_ROUTER_FLUX_PUSH'; -export const REPLACE_ACTION = 'REACT_NATIVE_ROUTER_FLUX_REPLACE'; -export const POP_ACTION2 = 'REACT_NATIVE_ROUTER_FLUX_BACK'; -export const POP_ACTION = 'REACT_NATIVE_ROUTER_FLUX_BACK_ACTION'; -export const POP_TO = 'REACT_NATIVE_ROUTER_FLUX_POP_TO'; -export const REFRESH_ACTION = 'REACT_NATIVE_ROUTER_FLUX_REFRESH'; -export const RESET_ACTION = 'REACT_NATIVE_ROUTER_FLUX_RESET'; -export const FOCUS_ACTION = 'REACT_NATIVE_ROUTER_FLUX_FOCUS'; +import * as ActionConst from './ActionConst'; + +export const ActionMap = { + jump: ActionConst.JUMP, + push: ActionConst.PUSH, + replace: ActionConst.REPLACE, + back: ActionConst.BACK, + BackAction: ActionConst.BACK_ACTION, + popTo: ActionConst.POP_TO, + refresh: ActionConst.REFRESH, + reset: ActionConst.RESET, + focus: ActionConst.FOCUS, + [ActionConst.JUMP]: ActionConst.JUMP, + [ActionConst.PUSH]: ActionConst.PUSH, + [ActionConst.REPLACE]: ActionConst.REPLACE, + [ActionConst.BACK]: ActionConst.BACK, + [ActionConst.BACK_ACTION]: ActionConst.BACK_ACTION, + [ActionConst.POP_TO]: ActionConst.POP_TO, + [ActionConst.REFRESH]: ActionConst.REFRESH, + [ActionConst.RESET]: ActionConst.RESET, + [ActionConst.FOCUS]: ActionConst.FOCUS, +}; function filterParam(data) { if (data.toString() !== '[object Object]') { @@ -31,19 +44,11 @@ function filterParam(data) { } const reservedKeys = [ - POP_ACTION, - POP_ACTION2, - POP_TO, - REFRESH_ACTION, - REPLACE_ACTION, - JUMP_ACTION, - PUSH_ACTION, - FOCUS_ACTION, - RESET_ACTION, 'create', 'callback', 'iterate', 'current', + ...Object.keys(ActionMap), ]; function getInheritProps(props) { @@ -72,9 +77,9 @@ class Actions { `'${key}' is not allowed as key name. Reserved keys: [${reservedKeys.join(', ')}]` ); const { children, component, ...staticProps } = root.props; - let type = root.props.type || (parentProps.tabs ? JUMP_ACTION : PUSH_ACTION); + let type = root.props.type || (parentProps.tabs ? ActionConst.JUMP : ActionConst.PUSH); if (type === 'switch') { - type = JUMP_ACTION; + type = ActionConst.JUMP; } const inheritProps = getInheritProps(parentProps); const componentProps = component ? { component: wrapBy(component) } : {}; @@ -117,7 +122,7 @@ class Actions { list = normalized; // normalize the list of scenes const condition = el => (!el.props.component && !el.props.children && !el.props.onPress && - (!el.props.type || el.props.type === REFRESH_ACTION)); + (!el.props.type || ActionMap[el.props.type] === ActionConst.REFRESH)); // determine sub-states let baseKey = root.key; let subStateParent = parentProps.key; @@ -135,7 +140,7 @@ class Actions { baseKey = innerKey; subStateParent = res.key; const inner = { ...res, name: key, key: innerKey, - sceneKey: innerKey, type: PUSH_ACTION, parent: res.key }; + sceneKey: innerKey, type: ActionConst.PUSH, parent: res.key }; refs[innerKey] = inner; res.children = [innerKey]; delete res.component; @@ -144,7 +149,7 @@ class Actions { } // process substates for (const el of subStates) { - refs[el.key] = { key: el.key, name: el.key, ...el.props, type: REFRESH_ACTION, + refs[el.key] = { key: el.key, name: el.key, ...el.props, type: ActionConst.REFRESH, base: baseKey, parent: subStateParent }; if (this[el.key]) { console.log(`Key ${el.key} is already defined!`); @@ -152,7 +157,7 @@ class Actions { this[el.key] = (props = {}) => { assert(this.callback, 'Actions.callback is not defined!'); - this.callback({ key: el.key, type: REFRESH_ACTION, ...filterParam(props) }); + this.callback({ key: el.key, type: ActionConst.REFRESH, ...filterParam(props) }); }; } if (this[key]) { @@ -169,23 +174,23 @@ class Actions { } popTo(props = {}) { - return this.callback({ ...filterParam(props), type: POP_TO }); + return this.callback({ ...filterParam(props), type: ActionConst.POP_TO }); } pop(props = {}) { - return this.callback({ ...filterParam(props), type: POP_ACTION }); + return this.callback({ ...filterParam(props), type: ActionConst.BACK_ACTION }); } jump(props = {}) { - return this.callback({ ...filterParam(props), type: JUMP_ACTION }); + return this.callback({ ...filterParam(props), type: ActionConst.JUMP }); } refresh(props = {}) { - return this.callback({ ...filterParam(props), type: REFRESH_ACTION }); + return this.callback({ ...filterParam(props), type: ActionConst.REFRESH }); } focus(props = {}) { - return this.callback({ ...filterParam(props), type: FOCUS_ACTION }); + return this.callback({ ...filterParam(props), type: ActionConst.FOCUS }); } create(scene:Scene, wrapBy = x => x) { diff --git a/src/Reducer.js b/src/Reducer.js index 510d72897..0a22e2135 100644 --- a/src/Reducer.js +++ b/src/Reducer.js @@ -9,17 +9,8 @@ /* eslint-disable no-param-reassign */ -import { - PUSH_ACTION, - POP_ACTION2, - POP_TO, - JUMP_ACTION, - REPLACE_ACTION, - RESET_ACTION, - POP_ACTION, - REFRESH_ACTION, -} from './Actions'; - +import * as ActionConst from './ActionConst'; +import { ActionMap } from './Actions'; import { assert } from './Util'; import { getInitialState } from './State'; @@ -37,7 +28,7 @@ function checkPropertiesEqual(action, lastAction) { } function inject(state, action, props, scenes) { - const condition = action.type === REFRESH_ACTION ? state.key === props.key || + const condition = ActionMap[action.type] === ActionConst.REFRESH ? state.key === props.key || state.sceneKey === action.key : state.sceneKey === props.parent; // console.log("INJECT:", action.key, state.sceneKey, condition); if (!condition) { @@ -58,8 +49,8 @@ function inject(state, action, props, scenes) { } let ind; - switch (action.type) { - case POP_TO: { + switch (ActionMap[action.type]) { + case ActionConst.POP_TO: { const targetIndex = action.targetIndex; return { @@ -69,8 +60,8 @@ function inject(state, action, props, scenes) { }; } - case POP_ACTION2: - case POP_ACTION: { + case ActionConst.BACK: + case ActionConst.BACK_ACTION: { assert(!state.tabs, 'pop() operation cannot be run on tab bar (tabs=true)'); if (state.index === 0) { return state; @@ -97,7 +88,7 @@ function inject(state, action, props, scenes) { children: state.children.slice(0, -1 * popNum), }; } - case REFRESH_ACTION: + case ActionConst.REFRESH: return props.base ? { navBar: state.navBar, ...scenes.rootProps, @@ -109,7 +100,7 @@ function inject(state, action, props, scenes) { key: state.key, from: null, }; - case PUSH_ACTION: + case ActionConst.PUSH: if (state.children[state.index].sceneKey === action.key && !props.clone && checkPropertiesEqual(action, state.children[state.index])) { return state; @@ -120,13 +111,13 @@ function inject(state, action, props, scenes) { from: null, children: [...state.children, getInitialState(props, scenes, state.index + 1, action)], }; - case JUMP_ACTION: + case ActionConst.JUMP: assert(state.tabs, `Parent=${state.key} is not tab bar, jump action is not valid`); ind = -1; state.children.forEach((c, i) => { if (c.sceneKey === action.key) { ind = i; } }); assert(ind !== -1, `Cannot find route with key=${action.key} for parent=${state.key}`); return { ...state, index: ind }; - case REPLACE_ACTION: + case ActionConst.REPLACE: if (state.children[state.index].sceneKey === action.key) { return state; } @@ -139,7 +130,7 @@ function inject(state, action, props, scenes) { ); return { ...state, children: state.children }; - case RESET_ACTION: + case ActionConst.RESET: if (state.children[state.index].sceneKey === action.key) { return state; } @@ -159,7 +150,7 @@ function inject(state, action, props, scenes) { } function findElement(state, key, type) { - if ((type === REFRESH_ACTION && state.key === key) || state.sceneKey === key) { + if ((ActionMap[type] === ActionConst.REFRESH && state.key === key) || state.sceneKey === key) { return state; } if (state.children) { @@ -198,7 +189,7 @@ function reducer({ initialState, scenes }) { assert(state.scenes, 'state.scenes is missed'); if (action.key) { - if (action.type === REFRESH_ACTION) { + if (ActionMap[action.type] === ActionConst.REFRESH) { let key = action.key; let child = findElement(state, key, action.type) || state.scenes[key]; let sceneKey = child.sceneKey; @@ -230,15 +221,17 @@ function reducer({ initialState, scenes }) { } } else { // set current route for pop action or refresh action - if (action.type === POP_ACTION || action.type === POP_ACTION2 || - action.type === REFRESH_ACTION || action.type === POP_TO) { + if (ActionMap[action.type] === ActionConst.BACK_ACTION || + ActionMap[action.type] === ActionConst.BACK || + ActionMap[action.type] === ActionConst.REFRESH || + ActionMap[action.type] === ActionConst.POP_TO) { if (!action.key && !action.parent) { action = { ...getCurrent(state), ...action }; } } // Find the parent and index of the future state - if (action.type === POP_TO) { + if (ActionMap[action.type] === ActionConst.POP_TO) { const target = action.data; assert(target, 'PopTo() must be called with scene name'); @@ -264,7 +257,8 @@ function reducer({ initialState, scenes }) { } // recursive pop parent - if (action.type === POP_ACTION || action.type === POP_ACTION2) { + if (ActionMap[action.type] === ActionConst.BACK_ACTION || + ActionMap[action.type] === ActionConst.BACK) { const parent = action.parent || state.scenes[action.key].parent; let el = findElement(state, parent, action.type); while (el.parent && (el.children.length <= 1 || el.tabs)) { @@ -275,15 +269,15 @@ function reducer({ initialState, scenes }) { } } - switch (action.type) { - case POP_ACTION2: - case POP_ACTION: - case POP_TO: - case REFRESH_ACTION: - case PUSH_ACTION: - case JUMP_ACTION: - case REPLACE_ACTION: - case RESET_ACTION: + switch (ActionMap[action.type]) { + case ActionConst.BACK: + case ActionConst.BACK_ACTION: + case ActionConst.POP_TO: + case ActionConst.REFRESH: + case ActionConst.PUSH: + case ActionConst.JUMP: + case ActionConst.REPLACE: + case ActionConst.RESET: return update(state, action); default: diff --git a/src/Router.js b/src/Router.js index 43ff28384..1abfa60a8 100644 --- a/src/Router.js +++ b/src/Router.js @@ -12,7 +12,7 @@ import React, { } from 'react'; import NavigationExperimental from 'react-native-experimental-navigation'; -import Actions from './Actions'; +import Actions, { ActionMap } from './Actions'; import getInitialState from './State'; import Reducer from './Reducer'; import DefaultRenderer from './DefaultRenderer'; @@ -88,7 +88,12 @@ class Router extends Component { } Actions.callback = props => { - if (this.props.dispatch) this.props.dispatch(props); + if (props.type && ActionMap[props.type]) { + props.type = ActionMap[props.type]; + } + if (this.props.dispatch) { + this.props.dispatch(props); + } return onNavigate(props); }; diff --git a/test/Reducer.test.js b/test/Reducer.test.js index 212338f2c..81506a656 100644 --- a/test/Reducer.test.js +++ b/test/Reducer.test.js @@ -3,6 +3,7 @@ import { expect } from 'chai'; import Scene from '../src/Scene'; import Actions from '../src/Actions'; +import * as ActionConst from '../src/ActionConst'; import createReducer from '../src/Reducer'; import getInitialState from '../src/State'; @@ -64,47 +65,47 @@ describe('createReducer', () => { // Normally actions came from Actions module, but we will generate it manually. latestState = reducer(latestState, { key: 'conversations', - type: 'REACT_NATIVE_ROUTER_FLUX_PUSH', + type: ActionConst.PUSH, param1: 'Hello world', }); currentScene = getCurrent(latestState); expect(currentScene.name).equal('conversations'); expect(currentScene.sceneKey).equal('conversations'); - expect(currentScene.type).equal('REACT_NATIVE_ROUTER_FLUX_PUSH'); + expect(currentScene.type).equal(ActionConst.PUSH); expect(currentScene.param1).equal('Hello world'); latestState = reducer(latestState, { key: 'login', // we go to `login` but first renderable child is `loginModal1` - type: 'REACT_NATIVE_ROUTER_FLUX_PUSH', + type: ActionConst.PUSH, param2: 'Hello world2', }); currentScene = getCurrent(latestState); expect(currentScene.name).equal('loginModal1'); expect(currentScene.sceneKey).equal('loginModal1'); - expect(currentScene.type).equal('REACT_NATIVE_ROUTER_FLUX_PUSH'); + expect(currentScene.type).equal(ActionConst.PUSH); expect(currentScene.param2).equal('Hello world2'); latestState = reducer(latestState, { key: 'privacyPolicy', - type: 'REACT_NATIVE_ROUTER_FLUX_PUSH', + type: ActionConst.PUSH, param3: 'Accept policy', }); currentScene = getCurrent(latestState); expect(currentScene.name).equal('privacyPolicy'); expect(currentScene.sceneKey).equal('privacyPolicy'); - expect(currentScene.type).equal('REACT_NATIVE_ROUTER_FLUX_PUSH'); + expect(currentScene.type).equal(ActionConst.PUSH); expect(currentScene.param3).equal('Accept policy'); latestState = reducer(latestState, { key: 'cubeBar', // we go to cubeBar but first renderable child is `conversations` - type: 'REACT_NATIVE_ROUTER_FLUX_PUSH', + type: ActionConst.PUSH, param1: 'Conversations new param', }); currentScene = getCurrent(latestState); expect(currentScene.name).equal('conversations'); expect(currentScene.sceneKey).equal('conversations'); - expect(currentScene.type).equal('REACT_NATIVE_ROUTER_FLUX_PUSH'); + expect(currentScene.type).equal(ActionConst.PUSH); expect(currentScene.param1).equal('Conversations new param'); }); });