atom.io 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -8
- package/dist/index.d.ts +117 -62
- package/dist/index.js +577 -287
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +574 -285
- package/dist/index.mjs.map +1 -1
- package/package.json +16 -6
- package/react/dist/index.d.ts +12 -17
- package/react/dist/index.js +25 -34
- package/react/dist/index.js.map +1 -1
- package/react/dist/index.mjs +21 -34
- package/react/dist/index.mjs.map +1 -1
- package/react-devtools/dist/index.css +26 -0
- package/react-devtools/dist/index.css.map +1 -0
- package/react-devtools/dist/index.d.ts +15 -0
- package/react-devtools/dist/index.js +1579 -0
- package/react-devtools/dist/index.js.map +1 -0
- package/react-devtools/dist/index.mjs +1551 -0
- package/react-devtools/dist/index.mjs.map +1 -0
- package/react-devtools/package.json +15 -0
- package/src/index.ts +14 -8
- package/src/internal/atom-internal.ts +10 -5
- package/src/internal/families-internal.ts +7 -7
- package/src/internal/get.ts +9 -9
- package/src/internal/index.ts +2 -1
- package/src/internal/meta/attach-meta.ts +17 -0
- package/src/internal/meta/index.ts +4 -0
- package/src/internal/meta/meta-state.ts +135 -0
- package/src/internal/meta/meta-timelines.ts +1 -0
- package/src/internal/meta/meta-transactions.ts +1 -0
- package/src/internal/operation.ts +14 -3
- package/src/internal/selector-internal.ts +37 -15
- package/src/internal/store.ts +35 -6
- package/src/internal/time-travel-internal.ts +89 -0
- package/src/internal/timeline-internal.ts +110 -93
- package/src/internal/transaction-internal.ts +14 -5
- package/src/{internal/logger.ts → logger.ts} +2 -2
- package/src/react/index.ts +28 -46
- package/src/react-devtools/AtomIODevtools.tsx +107 -0
- package/src/react-devtools/StateEditor.tsx +73 -0
- package/src/react-devtools/TokenList.tsx +49 -0
- package/src/react-devtools/devtools.scss +130 -0
- package/src/react-devtools/index.ts +1 -0
- package/src/react-explorer/AtomIOExplorer.tsx +208 -0
- package/src/react-explorer/explorer-effects.ts +20 -0
- package/src/react-explorer/explorer-states.ts +224 -0
- package/src/react-explorer/index.ts +23 -0
- package/src/react-explorer/space-states.ts +73 -0
- package/src/react-explorer/view-states.ts +43 -0
- package/src/selector.ts +11 -11
- package/src/subscribe.ts +3 -3
- package/src/timeline.ts +3 -12
- package/src/transaction.ts +9 -4
- package/src/web-effects/index.ts +1 -0
- package/src/web-effects/storage.ts +30 -0
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import type { AtomToken, ReadonlySelectorToken, SelectorToken } from "../.."
|
|
2
|
+
import { selector, atom } from "../.."
|
|
3
|
+
import type { Store } from "../store"
|
|
4
|
+
import { IMPLICIT } from "../store"
|
|
5
|
+
|
|
6
|
+
export type StateTokenIndex<
|
|
7
|
+
Token extends
|
|
8
|
+
| AtomToken<unknown>
|
|
9
|
+
| ReadonlySelectorToken<unknown>
|
|
10
|
+
| SelectorToken<unknown>
|
|
11
|
+
> = Record<
|
|
12
|
+
string,
|
|
13
|
+
| Token
|
|
14
|
+
| {
|
|
15
|
+
key: string
|
|
16
|
+
familyMembers: Record<string, Token>
|
|
17
|
+
}
|
|
18
|
+
>
|
|
19
|
+
|
|
20
|
+
export type AtomTokenIndex = StateTokenIndex<AtomToken<unknown>>
|
|
21
|
+
export type SelectorTokenIndex = StateTokenIndex<
|
|
22
|
+
ReadonlySelectorToken<unknown> | SelectorToken<unknown>
|
|
23
|
+
>
|
|
24
|
+
|
|
25
|
+
export const attachMetaAtoms = (
|
|
26
|
+
store: Store = IMPLICIT.STORE
|
|
27
|
+
): ReadonlySelectorToken<AtomTokenIndex> => {
|
|
28
|
+
const atomTokenIndexState__INTERNAL = atom<AtomTokenIndex>({
|
|
29
|
+
key: `👁🗨_atom_token_index__INTERNAL`,
|
|
30
|
+
default: () =>
|
|
31
|
+
[...store.atoms].reduce<AtomTokenIndex>((acc, [key]) => {
|
|
32
|
+
acc[key] = { key, type: `atom` }
|
|
33
|
+
return acc
|
|
34
|
+
}, {}),
|
|
35
|
+
effects: [
|
|
36
|
+
({ setSelf }) => {
|
|
37
|
+
store.subject.atomCreation.subscribe((atomToken) => {
|
|
38
|
+
if (store.operation.open) {
|
|
39
|
+
return
|
|
40
|
+
}
|
|
41
|
+
setSelf((state) => {
|
|
42
|
+
const { key, family } = atomToken
|
|
43
|
+
if (family) {
|
|
44
|
+
const { key: familyKey, subKey } = family
|
|
45
|
+
const current = state[familyKey]
|
|
46
|
+
if (current === undefined || `familyMembers` in current) {
|
|
47
|
+
const familyKeyState = current || {
|
|
48
|
+
key: familyKey,
|
|
49
|
+
familyMembers: {},
|
|
50
|
+
}
|
|
51
|
+
return {
|
|
52
|
+
...state,
|
|
53
|
+
[familyKey]: {
|
|
54
|
+
...familyKeyState,
|
|
55
|
+
familyMembers: {
|
|
56
|
+
...familyKeyState.familyMembers,
|
|
57
|
+
[subKey]: atomToken,
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return {
|
|
64
|
+
...state,
|
|
65
|
+
[key]: atomToken,
|
|
66
|
+
}
|
|
67
|
+
})
|
|
68
|
+
})
|
|
69
|
+
},
|
|
70
|
+
],
|
|
71
|
+
})
|
|
72
|
+
return selector({
|
|
73
|
+
key: `👁🗨_atom_token_index`,
|
|
74
|
+
get: ({ get }) => get(atomTokenIndexState__INTERNAL),
|
|
75
|
+
})
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export const attachMetaSelectors = (
|
|
79
|
+
store: Store = IMPLICIT.STORE
|
|
80
|
+
): ReadonlySelectorToken<SelectorTokenIndex> => {
|
|
81
|
+
const readonlySelectorTokenIndexState__INTERNAL = atom<SelectorTokenIndex>({
|
|
82
|
+
key: `👁🗨_selector_token_index__INTERNAL`,
|
|
83
|
+
default: () =>
|
|
84
|
+
Object.assign(
|
|
85
|
+
[...store.readonlySelectors].reduce<SelectorTokenIndex>((acc, [key]) => {
|
|
86
|
+
acc[key] = { key, type: `readonly_selector` }
|
|
87
|
+
return acc
|
|
88
|
+
}, {}),
|
|
89
|
+
[...store.selectors].reduce<SelectorTokenIndex>((acc, [key]) => {
|
|
90
|
+
acc[key] = { key, type: `selector` }
|
|
91
|
+
return acc
|
|
92
|
+
}, {})
|
|
93
|
+
),
|
|
94
|
+
effects: [
|
|
95
|
+
({ setSelf }) => {
|
|
96
|
+
store.subject.selectorCreation.subscribe((selectorToken) => {
|
|
97
|
+
if (store.operation.open) {
|
|
98
|
+
return
|
|
99
|
+
}
|
|
100
|
+
setSelf((state) => {
|
|
101
|
+
const { key, family } = selectorToken
|
|
102
|
+
if (family) {
|
|
103
|
+
const { key: familyKey, subKey } = family
|
|
104
|
+
const current = state[familyKey]
|
|
105
|
+
if (current === undefined || `familyMembers` in current) {
|
|
106
|
+
const familyKeyState = current || {
|
|
107
|
+
key: familyKey,
|
|
108
|
+
familyMembers: {},
|
|
109
|
+
}
|
|
110
|
+
return {
|
|
111
|
+
...state,
|
|
112
|
+
[familyKey]: {
|
|
113
|
+
...familyKeyState,
|
|
114
|
+
familyMembers: {
|
|
115
|
+
...familyKeyState.familyMembers,
|
|
116
|
+
[subKey]: selectorToken,
|
|
117
|
+
},
|
|
118
|
+
},
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return {
|
|
123
|
+
...state,
|
|
124
|
+
[key]: selectorToken,
|
|
125
|
+
}
|
|
126
|
+
})
|
|
127
|
+
})
|
|
128
|
+
},
|
|
129
|
+
],
|
|
130
|
+
})
|
|
131
|
+
return selector({
|
|
132
|
+
key: `👁🗨_selector_token_index`,
|
|
133
|
+
get: ({ get }) => get(readonlySelectorTokenIndexState__INTERNAL),
|
|
134
|
+
})
|
|
135
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {}
|
|
@@ -5,6 +5,7 @@ import type { Atom, ReadonlySelector, Selector } from "."
|
|
|
5
5
|
import { target } from "."
|
|
6
6
|
import type { Store } from "./store"
|
|
7
7
|
import { IMPLICIT } from "./store"
|
|
8
|
+
import type { StateToken } from ".."
|
|
8
9
|
|
|
9
10
|
export type OperationProgress =
|
|
10
11
|
| {
|
|
@@ -14,21 +15,31 @@ export type OperationProgress =
|
|
|
14
15
|
open: true
|
|
15
16
|
done: Set<string>
|
|
16
17
|
prev: Hamt<any, string>
|
|
18
|
+
time: number
|
|
19
|
+
token: StateToken<any>
|
|
17
20
|
}
|
|
18
21
|
|
|
19
|
-
export const openOperation = (store: Store): void => {
|
|
22
|
+
export const openOperation = (token: StateToken<any>, store: Store): void => {
|
|
20
23
|
const core = target(store)
|
|
24
|
+
if (core.operation.open) {
|
|
25
|
+
store.config.logger?.error(
|
|
26
|
+
`❌ failed to setState to "${token.key}" during a setState for "${core.operation.token.key}"`
|
|
27
|
+
)
|
|
28
|
+
throw Symbol(`violation`)
|
|
29
|
+
}
|
|
21
30
|
core.operation = {
|
|
22
31
|
open: true,
|
|
23
32
|
done: new Set(),
|
|
24
33
|
prev: store.valueMap,
|
|
34
|
+
time: Date.now(),
|
|
35
|
+
token,
|
|
25
36
|
}
|
|
26
|
-
store.config.logger?.info(
|
|
37
|
+
store.config.logger?.info(`⭕ operation start from "${token.key}"`)
|
|
27
38
|
}
|
|
28
39
|
export const closeOperation = (store: Store): void => {
|
|
29
40
|
const core = target(store)
|
|
30
41
|
core.operation = { open: false }
|
|
31
|
-
store.config.logger?.info(
|
|
42
|
+
store.config.logger?.info(`🔴 operation done`)
|
|
32
43
|
}
|
|
33
44
|
|
|
34
45
|
export const isDone = (key: string, store: Store = IMPLICIT.STORE): boolean => {
|
|
@@ -18,7 +18,7 @@ import type {
|
|
|
18
18
|
AtomToken,
|
|
19
19
|
FamilyMetadata,
|
|
20
20
|
ReadonlySelectorOptions,
|
|
21
|
-
|
|
21
|
+
ReadonlySelectorToken,
|
|
22
22
|
SelectorOptions,
|
|
23
23
|
SelectorToken,
|
|
24
24
|
StateToken,
|
|
@@ -46,7 +46,7 @@ export const lookupSelectorSources = (
|
|
|
46
46
|
store: Store
|
|
47
47
|
): (
|
|
48
48
|
| AtomToken<unknown>
|
|
49
|
-
|
|
|
49
|
+
| ReadonlySelectorToken<unknown>
|
|
50
50
|
| SelectorToken<unknown>
|
|
51
51
|
)[] =>
|
|
52
52
|
target(store)
|
|
@@ -56,7 +56,7 @@ export const lookupSelectorSources = (
|
|
|
56
56
|
|
|
57
57
|
export const traceSelectorAtoms = (
|
|
58
58
|
selectorKey: string,
|
|
59
|
-
dependency:
|
|
59
|
+
dependency: ReadonlySelectorToken<unknown> | StateToken<unknown>,
|
|
60
60
|
store: Store
|
|
61
61
|
): AtomToken<unknown>[] => {
|
|
62
62
|
const roots: AtomToken<unknown>[] = []
|
|
@@ -97,12 +97,15 @@ export const traceAllSelectorAtoms = (
|
|
|
97
97
|
|
|
98
98
|
export const updateSelectorAtoms = (
|
|
99
99
|
selectorKey: string,
|
|
100
|
-
dependency:
|
|
100
|
+
dependency: ReadonlySelectorToken<unknown> | StateToken<unknown>,
|
|
101
101
|
store: Store
|
|
102
102
|
): void => {
|
|
103
103
|
const core = target(store)
|
|
104
104
|
if (dependency.type === `atom`) {
|
|
105
|
-
core.selectorAtoms = core.selectorAtoms.set(
|
|
105
|
+
core.selectorAtoms = core.selectorAtoms.set({
|
|
106
|
+
selectorKey,
|
|
107
|
+
atomKey: dependency.key,
|
|
108
|
+
})
|
|
106
109
|
store.config.logger?.info(
|
|
107
110
|
` || adding root for "${selectorKey}": ${dependency.key}`
|
|
108
111
|
)
|
|
@@ -114,7 +117,10 @@ export const updateSelectorAtoms = (
|
|
|
114
117
|
roots.map((r) => r.key)
|
|
115
118
|
)
|
|
116
119
|
for (const root of roots) {
|
|
117
|
-
core.selectorAtoms = core.selectorAtoms.set(
|
|
120
|
+
core.selectorAtoms = core.selectorAtoms.set({
|
|
121
|
+
selectorKey,
|
|
122
|
+
atomKey: root.key,
|
|
123
|
+
})
|
|
118
124
|
}
|
|
119
125
|
}
|
|
120
126
|
|
|
@@ -138,12 +144,16 @@ export const registerSelector = (
|
|
|
138
144
|
)
|
|
139
145
|
} else {
|
|
140
146
|
store.config.logger?.info(
|
|
141
|
-
`🔌 registerSelector "${selectorKey}" <- "${dependency.key}" =`,
|
|
142
|
-
dependencyValue
|
|
147
|
+
`🔌 registerSelector "${selectorKey}" <- ( "${dependency.key}" =`,
|
|
148
|
+
dependencyValue,
|
|
149
|
+
`)`
|
|
150
|
+
)
|
|
151
|
+
core.selectorGraph = core.selectorGraph.set(
|
|
152
|
+
{ from: dependency.key, to: selectorKey },
|
|
153
|
+
{
|
|
154
|
+
source: dependency.key,
|
|
155
|
+
}
|
|
143
156
|
)
|
|
144
|
-
core.selectorGraph = core.selectorGraph.set(selectorKey, dependency.key, {
|
|
145
|
-
source: dependency.key,
|
|
146
|
-
})
|
|
147
157
|
}
|
|
148
158
|
updateSelectorAtoms(selectorKey, dependency, store)
|
|
149
159
|
return dependencyValue
|
|
@@ -163,12 +173,12 @@ export function selector__INTERNAL<T>(
|
|
|
163
173
|
options: ReadonlySelectorOptions<T>,
|
|
164
174
|
family?: FamilyMetadata,
|
|
165
175
|
store?: Store
|
|
166
|
-
):
|
|
176
|
+
): ReadonlySelectorToken<T>
|
|
167
177
|
export function selector__INTERNAL<T>(
|
|
168
178
|
options: ReadonlySelectorOptions<T> | SelectorOptions<T>,
|
|
169
179
|
family?: FamilyMetadata,
|
|
170
180
|
store: Store = IMPLICIT.STORE
|
|
171
|
-
):
|
|
181
|
+
): ReadonlySelectorToken<T> | SelectorToken<T> {
|
|
172
182
|
const core = target(store)
|
|
173
183
|
if (HAMT.has(options.key, core.selectors)) {
|
|
174
184
|
store.config.logger?.error(
|
|
@@ -199,7 +209,13 @@ export function selector__INTERNAL<T>(
|
|
|
199
209
|
)
|
|
200
210
|
const initialValue = getSelf()
|
|
201
211
|
store.config.logger?.info(` ✨ "${options.key}" =`, initialValue)
|
|
202
|
-
|
|
212
|
+
const token: ReadonlySelectorToken<T> = {
|
|
213
|
+
key: options.key,
|
|
214
|
+
type: `readonly_selector`,
|
|
215
|
+
family,
|
|
216
|
+
}
|
|
217
|
+
store.subject.selectorCreation.next(token)
|
|
218
|
+
return token
|
|
203
219
|
}
|
|
204
220
|
const setSelf = (next: T | ((oldValue: T) => T)): void => {
|
|
205
221
|
store.config.logger?.info(` <- "${options.key}" became`, next)
|
|
@@ -223,5 +239,11 @@ export function selector__INTERNAL<T>(
|
|
|
223
239
|
core.selectors = HAMT.set(options.key, mySelector, core.selectors)
|
|
224
240
|
const initialValue = getSelf()
|
|
225
241
|
store.config.logger?.info(` ✨ "${options.key}" =`, initialValue)
|
|
226
|
-
|
|
242
|
+
const token: SelectorToken<T> = {
|
|
243
|
+
key: options.key,
|
|
244
|
+
type: `selector`,
|
|
245
|
+
family,
|
|
246
|
+
}
|
|
247
|
+
store.subject.selectorCreation.next(token)
|
|
248
|
+
return token
|
|
227
249
|
}
|
package/src/internal/store.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { Hamt } from "hamt_plus"
|
|
2
2
|
import HAMT from "hamt_plus"
|
|
3
|
+
import * as Rx from "rxjs"
|
|
3
4
|
|
|
4
5
|
import { doNothing } from "~/packages/anvl/src/function"
|
|
5
6
|
import { Join } from "~/packages/anvl/src/join"
|
|
@@ -10,11 +11,19 @@ import type {
|
|
|
10
11
|
ReadonlySelector,
|
|
11
12
|
Selector,
|
|
12
13
|
TransactionStatus,
|
|
13
|
-
Logger,
|
|
14
14
|
Timeline,
|
|
15
15
|
TimelineData,
|
|
16
16
|
} from "."
|
|
17
|
-
import type {
|
|
17
|
+
import type {
|
|
18
|
+
AtomToken,
|
|
19
|
+
Logger,
|
|
20
|
+
ReadonlySelectorToken,
|
|
21
|
+
SelectorToken,
|
|
22
|
+
TimelineToken,
|
|
23
|
+
Transaction,
|
|
24
|
+
TransactionToken,
|
|
25
|
+
ƒn,
|
|
26
|
+
} from ".."
|
|
18
27
|
|
|
19
28
|
export type StoreCore = Pick<
|
|
20
29
|
Store,
|
|
@@ -35,15 +44,24 @@ export interface Store {
|
|
|
35
44
|
atoms: Hamt<Atom<any>, string>
|
|
36
45
|
atomsThatAreDefault: Set<string>
|
|
37
46
|
readonlySelectors: Hamt<ReadonlySelector<any>, string>
|
|
38
|
-
selectorAtoms: Join
|
|
47
|
+
selectorAtoms: Join<null, `selectorKey`, `atomKey`>
|
|
39
48
|
selectorGraph: Join<{ source: string }>
|
|
40
49
|
selectors: Hamt<Selector<any>, string>
|
|
41
50
|
timelines: Hamt<Timeline, string>
|
|
42
|
-
timelineAtoms: Join
|
|
51
|
+
timelineAtoms: Join<null, `timelineKey`, `atomKey`>
|
|
43
52
|
timelineStore: Hamt<TimelineData, string>
|
|
44
53
|
transactions: Hamt<Transaction<any>, string>
|
|
45
54
|
valueMap: Hamt<any, string>
|
|
46
55
|
|
|
56
|
+
subject: {
|
|
57
|
+
atomCreation: Rx.Subject<AtomToken<unknown>>
|
|
58
|
+
selectorCreation: Rx.Subject<
|
|
59
|
+
ReadonlySelectorToken<unknown> | SelectorToken<unknown>
|
|
60
|
+
>
|
|
61
|
+
transactionCreation: Rx.Subject<TransactionToken<unknown>>
|
|
62
|
+
timelineCreation: Rx.Subject<TimelineToken>
|
|
63
|
+
}
|
|
64
|
+
|
|
47
65
|
operation: OperationProgress
|
|
48
66
|
transactionStatus: TransactionStatus<ƒn>
|
|
49
67
|
config: {
|
|
@@ -58,15 +76,26 @@ export const createStore = (name: string): Store =>
|
|
|
58
76
|
atoms: HAMT.make<Atom<any>, string>(),
|
|
59
77
|
atomsThatAreDefault: new Set(),
|
|
60
78
|
readonlySelectors: HAMT.make<ReadonlySelector<any>, string>(),
|
|
61
|
-
selectorAtoms: new Join({ relationType: `n:n` })
|
|
79
|
+
selectorAtoms: new Join({ relationType: `n:n` })
|
|
80
|
+
.from(`selectorKey`)
|
|
81
|
+
.to(`atomKey`),
|
|
62
82
|
selectorGraph: new Join({ relationType: `n:n` }),
|
|
63
83
|
selectors: HAMT.make<Selector<any>, string>(),
|
|
64
84
|
timelines: HAMT.make<Timeline, string>(),
|
|
65
|
-
timelineAtoms: new Join({ relationType: `1:n` })
|
|
85
|
+
timelineAtoms: new Join({ relationType: `1:n` })
|
|
86
|
+
.from(`timelineKey`)
|
|
87
|
+
.to(`atomKey`),
|
|
66
88
|
timelineStore: HAMT.make<TimelineData, string>(),
|
|
67
89
|
transactions: HAMT.make<Transaction<any>, string>(),
|
|
68
90
|
valueMap: HAMT.make<any, string>(),
|
|
69
91
|
|
|
92
|
+
subject: {
|
|
93
|
+
atomCreation: new Rx.Subject(),
|
|
94
|
+
selectorCreation: new Rx.Subject(),
|
|
95
|
+
transactionCreation: new Rx.Subject(),
|
|
96
|
+
timelineCreation: new Rx.Subject(),
|
|
97
|
+
},
|
|
98
|
+
|
|
70
99
|
operation: {
|
|
71
100
|
open: false,
|
|
72
101
|
},
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import type { Store } from "."
|
|
2
|
+
import { IMPLICIT } from "."
|
|
3
|
+
import { setState } from ".."
|
|
4
|
+
import type { TimelineToken } from ".."
|
|
5
|
+
|
|
6
|
+
export const redo__INTERNAL = (
|
|
7
|
+
token: TimelineToken,
|
|
8
|
+
store: Store = IMPLICIT.STORE
|
|
9
|
+
): void => {
|
|
10
|
+
store.config.logger?.info(`⏩ redo "${token.key}"`)
|
|
11
|
+
const timelineData = store.timelineStore.get(token.key)
|
|
12
|
+
if (!timelineData) {
|
|
13
|
+
store.config.logger?.error(
|
|
14
|
+
`Failed to redo on timeline "${token.key}". This timeline has not been initialized.`
|
|
15
|
+
)
|
|
16
|
+
return
|
|
17
|
+
}
|
|
18
|
+
if (timelineData.at === timelineData.history.length) {
|
|
19
|
+
store.config.logger?.warn(
|
|
20
|
+
`Failed to redo at the end of timeline "${token.key}". There is nothing to redo.`
|
|
21
|
+
)
|
|
22
|
+
return
|
|
23
|
+
}
|
|
24
|
+
timelineData.timeTraveling = true
|
|
25
|
+
const update = timelineData.history[timelineData.at]
|
|
26
|
+
switch (update.type) {
|
|
27
|
+
case `atom_update`: {
|
|
28
|
+
const { key, newValue } = update
|
|
29
|
+
setState({ key, type: `atom` }, newValue)
|
|
30
|
+
break
|
|
31
|
+
}
|
|
32
|
+
case `selector_update`:
|
|
33
|
+
case `transaction_update`: {
|
|
34
|
+
for (const atomUpdate of update.atomUpdates) {
|
|
35
|
+
const { key, newValue } = atomUpdate
|
|
36
|
+
setState({ key, type: `atom` }, newValue)
|
|
37
|
+
}
|
|
38
|
+
break
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
++timelineData.at
|
|
42
|
+
timelineData.timeTraveling = false
|
|
43
|
+
store.config.logger?.info(
|
|
44
|
+
`⏹️ "${token.key}" is now at ${timelineData.at} / ${timelineData.history.length}`
|
|
45
|
+
)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export const undo__INTERNAL = (
|
|
49
|
+
token: TimelineToken,
|
|
50
|
+
store: Store = IMPLICIT.STORE
|
|
51
|
+
): void => {
|
|
52
|
+
store.config.logger?.info(`⏪ undo "${token.key}"`)
|
|
53
|
+
const timelineData = store.timelineStore.get(token.key)
|
|
54
|
+
if (!timelineData) {
|
|
55
|
+
store.config.logger?.error(
|
|
56
|
+
`Failed to undo on timeline "${token.key}". This timeline has not been initialized.`
|
|
57
|
+
)
|
|
58
|
+
return
|
|
59
|
+
}
|
|
60
|
+
if (timelineData.at === 0) {
|
|
61
|
+
store.config.logger?.warn(
|
|
62
|
+
`Failed to undo at the beginning of timeline "${token.key}". There is nothing to undo.`
|
|
63
|
+
)
|
|
64
|
+
return
|
|
65
|
+
}
|
|
66
|
+
timelineData.timeTraveling = true
|
|
67
|
+
|
|
68
|
+
--timelineData.at
|
|
69
|
+
const update = timelineData.history[timelineData.at]
|
|
70
|
+
switch (update.type) {
|
|
71
|
+
case `atom_update`: {
|
|
72
|
+
const { key, oldValue } = update
|
|
73
|
+
setState({ key, type: `atom` }, oldValue)
|
|
74
|
+
break
|
|
75
|
+
}
|
|
76
|
+
case `selector_update`:
|
|
77
|
+
case `transaction_update`: {
|
|
78
|
+
for (const atomUpdate of update.atomUpdates) {
|
|
79
|
+
const { key, oldValue } = atomUpdate
|
|
80
|
+
setState({ key, type: `atom` }, oldValue)
|
|
81
|
+
}
|
|
82
|
+
break
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
timelineData.timeTraveling = false
|
|
86
|
+
store.config.logger?.info(
|
|
87
|
+
`⏹️ "${token.key}" is now at ${timelineData.at} / ${timelineData.history.length}`
|
|
88
|
+
)
|
|
89
|
+
}
|