From 3fc1d9ae2e88ce9b410db9050114700b52235279 Mon Sep 17 00:00:00 2001 From: davidroeca Date: Fri, 9 Jun 2017 15:50:13 -0400 Subject: [PATCH 1/7] remove extraneous functions --- src/reducer.js | 51 ++------------------------------------------------ 1 file changed, 2 insertions(+), 49 deletions(-) diff --git a/src/reducer.js b/src/reducer.js index ec1d27f..708ff3b 100644 --- a/src/reducer.js +++ b/src/reducer.js @@ -33,53 +33,8 @@ function insert (history, state, limit, group) { } } -// undo: go back to the previous point in history -function undo (history) { - const { past, future, _latestUnfiltered } = history - - if (past.length <= 0) return history - - const newFuture = _latestUnfiltered != null - ? [ - _latestUnfiltered, - ...future - ] : future - - const newPresent = past[past.length - 1] - return { - past: past.slice(0, past.length - 1), // remove last element from past - present: newPresent, // set element as new present - _latestUnfiltered: newPresent, - future: newFuture, - group: null - } -} - -// redo: go to the next point in history -function redo (history) { - const { past, future, _latestUnfiltered } = history - - if (future.length <= 0) return history - - const newPast = _latestUnfiltered != null - ? [ - ...past, - _latestUnfiltered - ] : past - - const newPresent = future[0] - return { - future: future.slice(1, future.length), // remove element from future - present: newPresent, // set element as new present - _latestUnfiltered: newPresent, - past: newPast, - group: null - } -} - // jumpToFuture: jump to requested index in future history function jumpToFuture (history, index) { - if (index === 0) return redo(history) if (index < 0 || index >= history.future.length) return history const { past, future, _latestUnfiltered } = history @@ -92,13 +47,11 @@ function jumpToFuture (history, index) { _latestUnfiltered: newPresent, past: past.concat([_latestUnfiltered]).concat(future.slice(0, index)), group: null - } } // jumpToPast: jump to requested index in past history function jumpToPast (history, index) { - if (index === history.past.length - 1) return undo(history) if (index < 0 || index >= history.past.length) return history const { past, future, _latestUnfiltered } = history @@ -210,13 +163,13 @@ export default function undoable (reducer, rawConfig = {}) { return history case config.undoType: - res = undo(history) + res = jump(history, -1) debug.log('perform undo') debug.end(res) return skipReducer(res) case config.redoType: - res = redo(history) + res = jump(history, 1) debug.log('perform redo') debug.end(res) return skipReducer(res) From bdd0a494550e1f3932a210864aac5ebcc6bcb8cc Mon Sep 17 00:00:00 2001 From: davidroeca Date: Fri, 9 Jun 2017 16:22:32 -0400 Subject: [PATCH 2/7] abstract new histories as a function --- src/reducer.js | 92 ++++++++++++++++++++++---------------------------- 1 file changed, 40 insertions(+), 52 deletions(-) diff --git a/src/reducer.js b/src/reducer.js index 708ff3b..6bfdbab 100644 --- a/src/reducer.js +++ b/src/reducer.js @@ -2,6 +2,28 @@ import * as debug from './debug' import { ActionTypes } from './actions' import { parseActions, isHistory } from './helpers' +function newHistory (past, present, future, group = null) { + return { + past, + present, + future, + group, + _latestUnfiltered: present + } +} + +// createHistory +function createHistory (state, ignoreInitialState) { + // ignoreInitialState essentially prevents the user from undoing to the + // beginning, in the case that the undoable reducer handles initialization + // in a way that can't be redone simply + let history = newHistory([], state, []) + return ignoreInitialState ? { + ...history, + _latestUnfiltered: null + } : history +} + // lengthWithoutFuture: get length of history function lengthWithoutFuture (history) { return history.past.length + 1 @@ -24,13 +46,7 @@ function insert (history, state, limit, group) { _latestUnfiltered ] : pastSliced - return { - past: newPast, - present: state, - _latestUnfiltered: state, - future: [], - group: group - } + return newHistory(newPast, state, [], group) } // jumpToFuture: jump to requested index in future history @@ -39,15 +55,11 @@ function jumpToFuture (history, index) { const { past, future, _latestUnfiltered } = history + const newPast = past.concat([_latestUnfiltered]).concat(future.slice(0, index)) const newPresent = future[index] + const newFuture = future.slice(index + 1) - return { - future: future.slice(index + 1), - present: newPresent, - _latestUnfiltered: newPresent, - past: past.concat([_latestUnfiltered]).concat(future.slice(0, index)), - group: null - } + return newHistory(newPast, newPresent, newFuture) } // jumpToPast: jump to requested index in past history @@ -56,17 +68,13 @@ function jumpToPast (history, index) { const { past, future, _latestUnfiltered } = history + const newPast = past.slice(0, index) + const newFuture = past.slice(index + 1) + .concat([_latestUnfiltered]) + .concat(future) const newPresent = past[index] - return { - future: past.slice(index + 1) - .concat([_latestUnfiltered]) - .concat(future), - present: newPresent, - _latestUnfiltered: newPresent, - past: past.slice(0, index), - group: null - } + return newHistory(newPast, newPresent, newFuture) } // jump: jump n steps in the past or forward @@ -76,25 +84,6 @@ function jump (history, n) { return history } -// createHistory -function createHistory (state, ignoreInitialState) { - // ignoreInitialState essentially prevents the user from undoing to the - // beginning, in the case that the undoable reducer handles initialization - // in a way that can't be redone simply - return ignoreInitialState ? { - past: [], - present: state, - future: [], - group: null - } : { - past: [], - present: state, - _latestUnfiltered: state, - future: [], - group: null - } -} - // helper to dynamically match in the reducer's switch-case function actionTypeAmongClearHistoryType (actionType, clearHistoryType) { return clearHistoryType.indexOf(actionType) > -1 ? actionType : !actionType @@ -221,21 +210,20 @@ export default function undoable (reducer, rawConfig = {}) { if (typeof config.filter === 'function' && !config.filter(action, res, history)) { // if filtering an action, merely update the present - const filteredState = { - ...history, - present: res, - _latestUnfiltered: config.syncFilter ? res : history._latestUnfiltered, - group: null + let filteredState = newHistory(history.past, res, history.future) + if (!config.syncFilter) { + filteredState._latestUnfiltered = history._latestUnfiltered } debug.log('filter ignored action, not storing it in past') debug.end(filteredState) return filteredState } else if (group != null && group === history.group) { - const groupedState = { - ...history, - _latestUnfiltered: res, - present: res - } + const groupedState = newHistory( + history.past, + res, + history.future, + history.group, + ) debug.log('groupBy grouped the action with the previous action') debug.end(groupedState) return groupedState From 3f0df7a649a16554fb995ae4ff9e9723eb58886e Mon Sep 17 00:00:00 2001 From: davidroeca Date: Fri, 9 Jun 2017 16:32:17 -0400 Subject: [PATCH 3/7] switch let to const since there's no reassignment --- src/reducer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/reducer.js b/src/reducer.js index 6bfdbab..0d093b3 100644 --- a/src/reducer.js +++ b/src/reducer.js @@ -17,7 +17,7 @@ function createHistory (state, ignoreInitialState) { // ignoreInitialState essentially prevents the user from undoing to the // beginning, in the case that the undoable reducer handles initialization // in a way that can't be redone simply - let history = newHistory([], state, []) + const history = newHistory([], state, []) return ignoreInitialState ? { ...history, _latestUnfiltered: null From e574db3bade1bfb38cab7d35d87832b98aed1566 Mon Sep 17 00:00:00 2001 From: david Date: Sat, 10 Jun 2017 19:26:31 -0400 Subject: [PATCH 4/7] add index/limit, and resolve some testing issues --- src/reducer.js | 24 +++++++++++++----------- test/index.spec.js | 4 +++- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/src/reducer.js b/src/reducer.js index 0d093b3..bc06326 100644 --- a/src/reducer.js +++ b/src/reducer.js @@ -8,7 +8,9 @@ function newHistory (past, present, future, group = null) { present, future, group, - _latestUnfiltered: present + _latestUnfiltered: present, + index: past.length, + limit: past.length + future.length + 1 } } @@ -17,11 +19,11 @@ function createHistory (state, ignoreInitialState) { // ignoreInitialState essentially prevents the user from undoing to the // beginning, in the case that the undoable reducer handles initialization // in a way that can't be redone simply - const history = newHistory([], state, []) - return ignoreInitialState ? { - ...history, - _latestUnfiltered: null - } : history + let history = newHistory([], state, []) + if (ignoreInitialState) { + history._latestUnfiltered = null + } + return history } // lengthWithoutFuture: get length of history @@ -128,11 +130,11 @@ export default function undoable (reducer, rawConfig = {}) { debug.log('do not initialize on probe actions') } else if (isHistory(state)) { history = config.history = config.ignoreInitialState - ? state : { - ...state, - _latestUnfiltered: state.present, - group: null - } + ? state : newHistory( + state.past, + state.present, + state.future + ) debug.log('initialHistory initialized: initialState is a history', config.history) } else { history = config.history = createHistory(state) diff --git a/test/index.spec.js b/test/index.spec.js index 28aac80..8427459 100644 --- a/test/index.spec.js +++ b/test/index.spec.js @@ -181,7 +181,9 @@ function runTests (label, { undoableConfig = {}, initialStoreState, testConfig } present: initialStoreState, _latestUnfiltered: initialStoreState, future: [], - group: null + group: null, + index: 0, + limit: 1 }) } }) From 1ae4e2c68eb931c41a3489b9dc0e8aa848ec8291 Mon Sep 17 00:00:00 2001 From: david Date: Sat, 10 Jun 2017 19:33:03 -0400 Subject: [PATCH 5/7] update typescript definitions --- typings.d.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/typings.d.ts b/typings.d.ts index b50d330..0e65408 100644 --- a/typings.d.ts +++ b/typings.d.ts @@ -7,6 +7,8 @@ declare module 'redux-undo' { future: State[]; _latestUnfiltered: State[]; group: any; + index: number; + limit: number; } export type FilterFunction = (action: Action, currentState: State, previousHistory: StateWithHistory) => boolean; From 2bb22a0757dd20c2a4c9de058c870cff1b662884 Mon Sep 17 00:00:00 2001 From: david Date: Mon, 12 Jun 2017 00:09:03 -0400 Subject: [PATCH 6/7] modify lines that were too long, and switch from concat to spread operator --- src/reducer.js | 39 +++++++++++++++++++++++++++------------ 1 file changed, 27 insertions(+), 12 deletions(-) diff --git a/src/reducer.js b/src/reducer.js index bc06326..7dfa23b 100644 --- a/src/reducer.js +++ b/src/reducer.js @@ -57,7 +57,7 @@ function jumpToFuture (history, index) { const { past, future, _latestUnfiltered } = history - const newPast = past.concat([_latestUnfiltered]).concat(future.slice(0, index)) + const newPast = [...past, _latestUnfiltered, ...future.slice(0, index)] const newPresent = future[index] const newFuture = future.slice(index + 1) @@ -71,9 +71,7 @@ function jumpToPast (history, index) { const { past, future, _latestUnfiltered } = history const newPast = past.slice(0, index) - const newFuture = past.slice(index + 1) - .concat([_latestUnfiltered]) - .concat(future) + const newFuture = [...past.slice(index + 1), _latestUnfiltered, ...future] const newPresent = past[index] return newHistory(newPast, newPresent, newFuture) @@ -122,11 +120,14 @@ export default function undoable (reducer, rawConfig = {}) { debug.log('history is uninitialized') if (state === undefined) { - history = config.history = createHistory(reducer( - state, { type: '@@redux-undo/CREATE_HISTORY' }), - config.ignoreInitialState, - ...slices + const clearHistoryAction = { type: ActionTypes.CLEAR_HISTORY } + const start = reducer(state, clearHistoryAction, ...slices) + + history = config.history = createHistory( + start, + config.ignoreInitialState ) + debug.log('do not initialize on probe actions') } else if (isHistory(state)) { history = config.history = config.ignoreInitialState @@ -135,10 +136,19 @@ export default function undoable (reducer, rawConfig = {}) { state.present, state.future ) - debug.log('initialHistory initialized: initialState is a history', config.history) + debug.log( + 'initialHistory initialized: initialState is a history', + config.history + ) } else { - history = config.history = createHistory(state) - debug.log('initialHistory initialized: initialState is not a history', config.history) + history = config.history = createHistory( + state, + config.ignoreInitialState + ) + debug.log( + 'initialHistory initialized: initialState is not a history', + config.history + ) } } @@ -208,9 +218,14 @@ export default function undoable (reducer, rawConfig = {}) { return history } + const filtered = typeof config.filter === 'function' && !config.filter( + action, + res, + history + ) const group = config.groupBy(action, res, history) - if (typeof config.filter === 'function' && !config.filter(action, res, history)) { + if (filtered) { // if filtering an action, merely update the present let filteredState = newHistory(history.past, res, history.future) if (!config.syncFilter) { From 459af2634fc942b363065d6991be7afa5b690300 Mon Sep 17 00:00:00 2001 From: david Date: Tue, 27 Jun 2017 09:45:04 -0400 Subject: [PATCH 7/7] add the history's group to the filtered state ST it doesn't reset --- src/reducer.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/reducer.js b/src/reducer.js index 7dfa23b..8ef2a53 100644 --- a/src/reducer.js +++ b/src/reducer.js @@ -227,7 +227,12 @@ export default function undoable (reducer, rawConfig = {}) { if (filtered) { // if filtering an action, merely update the present - let filteredState = newHistory(history.past, res, history.future) + let filteredState = newHistory( + history.past, + res, + history.future, + history.group + ) if (!config.syncFilter) { filteredState._latestUnfiltered = history._latestUnfiltered }