| 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103 | 1x
1x
1x
1x
1x
1x
3x
3x
3x
1x
3x
3x
3x
1x
17x
17x
68x
6x
| export const UNDO = 'UNDO';
export const REDO = 'REDO';
export const REPLAY_FINISHED = '@@redux-undoable/REPLAY_FINISHED';
const replayFinished = () => {
return {
type: REPLAY_FINISHED
};
};
const replay = function(initial, actions, reducer) {
return actions
.map(action => {
// create a copy of the action with metadata to indicate that a replay is in progress
return { ...action, meta: { replay: true } };
})
.concat(replayFinished())
.reduce((state, action) => reducer(state, action), initial);
};
const undo = function(initial, past, present, future, reducer) {
const action = past[past.length - 1];
const newPast = past.slice(0, past.length - 1);
const previous = replay(initial, newPast, reducer);
return {
initial,
past: newPast,
present: previous,
future: [ action, ...future ]
};
};
const redo = function(initial, past, present, future, reducer){
const action = future[0];
const newFuture = future.slice(1);
const newPresent = reducer(present, action);
return {
initial,
past: [ ...past, action ],
present: newPresent,
future: newFuture
};
};
const defaultConfig = {
init: ['@@redux/INIT'],
include: []
};
export default function(reducer, config) {
const initialState = {
initial: null,
past: [],
present: reducer(undefined, {}),
future: []
};
const mergedConfig = Object.assign(defaultConfig, config);
return function (state = initialState, action) {
const { initial, past, present, future } = state;
if (mergedConfig.init.includes(action.type) && !state.present) {
return {
initial,
past,
present: replay(initial, past, reducer),
future
};
}
switch (action.type) {
case UNDO:
return undo(initial, past, present, future, reducer);
case REDO:
return redo(initial, past, present, future, reducer);
default:
const newPresent = reducer(present, action);
if (present === newPresent) {
return state;
}
if (!mergedConfig.include.includes(action.type)) {
return {
initial,
past,
present: newPresent,
future
};
}
return {
initial,
past: [ ...past, action ],
present: newPresent,
future: []
};
}
};
}
|