From b0e95480d48a9ce7a7ee47cc50014621497ac10f Mon Sep 17 00:00:00 2001 From: Joe Noon Date: Thu, 24 Mar 2016 10:05:37 -0700 Subject: [PATCH 1/5] - Added action type: 'pushToCurrent': first finds the current scene's parent - and sets it as the parent of the new scene. Scenes defined at the root - level can be pushed into tab scenes. - pushed scenes are added to the root state and removed on pop - break early in findElement - change getCurrent to return entire scene --- src/Actions.js | 3 ++- src/Reducer.js | 41 ++++++++++++++++++++++++----------------- 2 files changed, 26 insertions(+), 18 deletions(-) diff --git a/src/Actions.js b/src/Actions.js index f3cec6ce9..9ce54d3ff 100644 --- a/src/Actions.js +++ b/src/Actions.js @@ -10,6 +10,7 @@ import assert from 'assert'; import Scene from './Scene'; export const JUMP_ACTION = 'jump'; export const PUSH_ACTION = 'push'; +export const PUSH_TO_CURRENT_ACTION = 'pushToCurrent'; export const REPLACE_ACTION = 'replace'; export const POP_ACTION2 = 'back'; export const POP_ACTION = 'BackAction'; @@ -51,7 +52,7 @@ class Actions { assert(root.props, "props should be defined for stack"); const key = root.key; assert(key, "unique key should be defined ",root); - assert([POP_ACTION, POP_ACTION2, REFRESH_ACTION, REPLACE_ACTION, JUMP_ACTION, PUSH_ACTION, RESET_ACTION, 'create', + assert([POP_ACTION, POP_ACTION2, REFRESH_ACTION, REPLACE_ACTION, JUMP_ACTION, PUSH_ACTION, PUSH_TO_CURRENT_ACTION, RESET_ACTION, 'create', 'init','callback','iterate','current'].indexOf(key)==-1, key+" is not allowed as key name"); const {children, ...staticProps} = root.props; let type = root.props.type || (parentProps.tabs ? JUMP_ACTION : PUSH_ACTION); diff --git a/src/Reducer.js b/src/Reducer.js index 7afcc9f79..7632b1b01 100644 --- a/src/Reducer.js +++ b/src/Reducer.js @@ -7,7 +7,7 @@ * */ -import {PUSH_ACTION, POP_ACTION2, FOCUS_ACTION, JUMP_ACTION, INIT_ACTION, REPLACE_ACTION, RESET_ACTION, POP_ACTION, REFRESH_ACTION} from './Actions'; +import {PUSH_ACTION, PUSH_TO_CURRENT_ACTION, POP_ACTION2, FOCUS_ACTION, JUMP_ACTION, INIT_ACTION, REPLACE_ACTION, RESET_ACTION, POP_ACTION, REFRESH_ACTION} from './Actions'; import assert from 'assert'; import Immutable from 'immutable'; import {getInitialState} from './State'; @@ -16,13 +16,13 @@ function findElement(state, key) { if (state.sceneKey != key){ if (state.children){ let result = undefined; - state.children.forEach(el=>{ + for (let el of state.children) { let res = findElement(el, key); if (res){ result = res; - return res; + break; } - }); + } return result; } else { return false; @@ -34,27 +34,24 @@ function findElement(state, key) { function getCurrent(state){ if (!state.children){ - return state.key; + return state; } return getCurrent(state.children[state.index]); } - - function update(state,action){ - // clone state, TODO: clone effectively? - if (!state.scenes[action.key] && action.key.indexOf('_')!=-1){ - action.key = action.key.substring(action.key.indexOf('_')+1); - //console.log("Transform to key="+action.key); + if (!state.scenes[action.key]) { + console.log("No scene for key="+action.key); + return state; } - const newProps = {...state.scenes[action.key], ...action}; + let newProps = {...state.scenes[action.key], ...action}; let newState = Immutable.fromJS(state).toJS(); // change route property //newState.scenes[action.key] = newProps; // get parent - const parent = newProps.parent; + let parent = newProps.parent; assert(parent, "No parent is defined for route="+action.key); // find parent in the state @@ -72,7 +69,8 @@ function update(state,action){ if (el.children.length > 1) { el.children.pop(); el.index = el.children.length - 1; - newState.scenes.current = getCurrent(newState); + delete newState.scenes[newState.scenes.current]; + newState.scenes.current = getCurrent(newState).key; return newState; } else { console.log("Cannot do pop"); @@ -86,10 +84,18 @@ function update(state,action){ el.children[ind] = getInitialState(newProps, newState.scenes, ind, action); return newState; + case PUSH_TO_CURRENT_ACTION: + parent = getCurrent(newState).parent; + newProps.parent = parent; + el = findElement(newState, parent); + assert(el, "Cannot find element for parent="+parent+" within current state:"+JSON.stringify(newState)); + // fall through to PUSH_ACTION + case PUSH_ACTION: el.children.push(getInitialState(newProps, newState.scenes, el.children.length, action)); el.index = el.children.length - 1; - newState.scenes.current = getCurrent(newState); + newState.scenes.current = getCurrent(newState).key; + newState.scenes[newState.scenes.current] = newProps; return newState; case JUMP_ACTION: @@ -110,7 +116,7 @@ function update(state,action){ } else { el.children = [getInitialState(newProps, newState.scenes, 0, action)]; } - newState.scenes.current = getCurrent(newState); + newState.scenes.current = getCurrent(newState).key; return newState; default: @@ -146,6 +152,7 @@ function reducer({initialState, scenes}){ case POP_ACTION2: case POP_ACTION: case REFRESH_ACTION: + case PUSH_TO_CURRENT_ACTION: case PUSH_ACTION: case JUMP_ACTION: case REPLACE_ACTION: @@ -161,4 +168,4 @@ function reducer({initialState, scenes}){ } -export default reducer; \ No newline at end of file +export default reducer; From 86454102815511aa4ed34d256f3a049ebc6bd430 Mon Sep 17 00:00:00 2001 From: Joe Noon Date: Thu, 24 Mar 2016 13:52:41 -0700 Subject: [PATCH 2/5] create ephemeral scenes during pushToCurrent. because these scenes are not defined ahead of time, they are given a unique prefix when added at runtime. this allows for pushing the same Scene multiple times (profile for user 123) and ensuring on pop that only scenes marked ephemeral will get removed from the scenes map. --- src/Reducer.js | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/Reducer.js b/src/Reducer.js index 7632b1b01..5db861386 100644 --- a/src/Reducer.js +++ b/src/Reducer.js @@ -39,6 +39,8 @@ function getCurrent(state){ return getCurrent(state.children[state.index]); } +var _uniqPushed = 0; + function update(state,action){ if (!state.scenes[action.key]) { console.log("No scene for key="+action.key); @@ -67,9 +69,11 @@ function update(state,action){ assert(el, "Cannot find element for parent="+el.parent+" within current state"); } if (el.children.length > 1) { - el.children.pop(); + let popped = el.children.pop(); el.index = el.children.length - 1; - delete newState.scenes[newState.scenes.current]; + if (popped.ephemeral) { + delete newState.scenes[popped.key]; + } newState.scenes.current = getCurrent(newState).key; return newState; } else { @@ -79,13 +83,15 @@ function update(state,action){ case REFRESH_ACTION: let ind = -1; - el.children.forEach((c,i)=>{if (c.sceneKey==action.key){ind=i}}); + el.children.forEach((c,i)=>{if (c.key==action.key){ind=i}}); assert(ind!=-1, "Cannot find route with key="+action.key+" for parent="+el.key); el.children[ind] = getInitialState(newProps, newState.scenes, ind, action); return newState; case PUSH_TO_CURRENT_ACTION: parent = getCurrent(newState).parent; + newProps.ephemeral = true; + newProps.key = `${_uniqPushed++}$${newProps.key}`; newProps.parent = parent; el = findElement(newState, parent); assert(el, "Cannot find element for parent="+parent+" within current state:"+JSON.stringify(newState)); @@ -95,7 +101,9 @@ function update(state,action){ el.children.push(getInitialState(newProps, newState.scenes, el.children.length, action)); el.index = el.children.length - 1; newState.scenes.current = getCurrent(newState).key; - newState.scenes[newState.scenes.current] = newProps; + if (newProps.ephemeral) { + newState.scenes[newState.scenes.current] = newProps; + } return newState; case JUMP_ACTION: From 97612c8480b1ce322f89bf97f70e0f5de45e6422 Mon Sep 17 00:00:00 2001 From: Joe Noon Date: Thu, 24 Mar 2016 14:02:07 -0700 Subject: [PATCH 3/5] ensure we dont overwrite anything in scenes map --- src/Reducer.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Reducer.js b/src/Reducer.js index 5db861386..f2d41c7c6 100644 --- a/src/Reducer.js +++ b/src/Reducer.js @@ -102,6 +102,7 @@ function update(state,action){ el.index = el.children.length - 1; newState.scenes.current = getCurrent(newState).key; if (newProps.ephemeral) { + assert(!newState.scenes.hasOwnProperty(newState.scenes.current), "scenes should not contain ephemeral key="+newState.scenes.current); newState.scenes[newState.scenes.current] = newProps; } return newState; From 6e30fe3ebd27a8a4f842cbaad881b81d07a1a5a7 Mon Sep 17 00:00:00 2001 From: Joe Noon Date: Thu, 24 Mar 2016 14:14:06 -0700 Subject: [PATCH 4/5] treat all pushes as ephemeral. they get cloned with a unique key --- src/Reducer.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Reducer.js b/src/Reducer.js index f2d41c7c6..64fb8bc11 100644 --- a/src/Reducer.js +++ b/src/Reducer.js @@ -90,14 +90,14 @@ function update(state,action){ case PUSH_TO_CURRENT_ACTION: parent = getCurrent(newState).parent; - newProps.ephemeral = true; - newProps.key = `${_uniqPushed++}$${newProps.key}`; newProps.parent = parent; el = findElement(newState, parent); assert(el, "Cannot find element for parent="+parent+" within current state:"+JSON.stringify(newState)); // fall through to PUSH_ACTION case PUSH_ACTION: + newProps.ephemeral = true; + newProps.key = `${_uniqPushed++}$${newProps.key}`; el.children.push(getInitialState(newProps, newState.scenes, el.children.length, action)); el.index = el.children.length - 1; newState.scenes.current = getCurrent(newState).key; From 7230d162aa6cb5e784c769fbab770fc8310782cc Mon Sep 17 00:00:00 2001 From: Joe Noon Date: Thu, 24 Mar 2016 17:48:37 -0700 Subject: [PATCH 5/5] handle ephemeral case different for refresh. --- src/Reducer.js | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/Reducer.js b/src/Reducer.js index 64fb8bc11..172e06184 100644 --- a/src/Reducer.js +++ b/src/Reducer.js @@ -83,7 +83,20 @@ function update(state,action){ case REFRESH_ACTION: let ind = -1; - el.children.forEach((c,i)=>{if (c.key==action.key){ind=i}}); + for (let i=0; i < el.children.length; i++) { + let c = el.children[i]; + if (c.ephemeral) { + if (c.key === action.key) { + ind = i; + break; + } + } else { + if (c.sceneKey === action.key) { + ind = i; + break; + } + } + } assert(ind!=-1, "Cannot find route with key="+action.key+" for parent="+el.key); el.children[ind] = getInitialState(newProps, newState.scenes, ind, action); return newState;