atom.io 0.6.4 → 0.6.6

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.
Files changed (61) hide show
  1. package/README.md +32 -78
  2. package/dist/index.d.mts +4 -35
  3. package/dist/index.d.ts +4 -35
  4. package/dist/index.js +17 -129
  5. package/dist/index.js.map +1 -1
  6. package/dist/index.mjs +17 -129
  7. package/dist/index.mjs.map +1 -1
  8. package/introspection/dist/index.js +312 -0
  9. package/introspection/dist/index.js.map +1 -0
  10. package/introspection/dist/index.mjs +289 -0
  11. package/introspection/dist/index.mjs.map +1 -0
  12. package/introspection/package.json +15 -0
  13. package/package.json +17 -8
  14. package/react-devtools/dist/index.css +22 -5
  15. package/react-devtools/dist/index.css.map +1 -1
  16. package/react-devtools/dist/index.d.mts +347 -8
  17. package/react-devtools/dist/index.d.ts +347 -8
  18. package/react-devtools/dist/index.js +2722 -674
  19. package/react-devtools/dist/index.js.map +1 -1
  20. package/react-devtools/dist/index.mjs +2669 -630
  21. package/react-devtools/dist/index.mjs.map +1 -1
  22. package/src/internal/index.ts +0 -1
  23. package/src/internal/operation.ts +1 -0
  24. package/src/internal/store.ts +3 -2
  25. package/src/internal/time-travel-internal.ts +2 -0
  26. package/src/internal/timeline/add-atom-to-timeline.ts +11 -12
  27. package/src/internal/timeline-internal.ts +5 -1
  28. package/src/introspection/attach-atom-index.ts +73 -0
  29. package/src/introspection/attach-introspection-states.ts +42 -0
  30. package/src/introspection/attach-selector-index.ts +77 -0
  31. package/src/introspection/attach-timeline-family.ts +49 -0
  32. package/src/introspection/attach-timeline-index.ts +36 -0
  33. package/src/introspection/attach-transaction-index.ts +38 -0
  34. package/src/introspection/attach-transaction-logs.ts +40 -0
  35. package/src/introspection/index.ts +20 -0
  36. package/src/react-devtools/AtomIODevtools.tsx +97 -96
  37. package/src/react-devtools/Button.tsx +24 -0
  38. package/src/react-devtools/StateEditor.tsx +14 -16
  39. package/src/react-devtools/StateIndex.tsx +153 -0
  40. package/src/react-devtools/TimelineIndex.tsx +92 -0
  41. package/src/react-devtools/TransactionIndex.tsx +70 -0
  42. package/src/react-devtools/Updates.tsx +145 -0
  43. package/src/react-devtools/devtools.scss +196 -15
  44. package/src/react-devtools/index.ts +71 -0
  45. package/src/react-explorer/AtomIOExplorer.tsx +0 -1
  46. package/src/react-explorer/explorer-effects.ts +3 -3
  47. package/src/react-explorer/explorer-states.ts +1 -1
  48. package/src/react-explorer/space-states.ts +6 -4
  49. package/src/react-explorer/view-states.ts +0 -2
  50. package/realtime-testing/dist/index.d.mts +0 -49
  51. package/realtime-testing/dist/index.d.ts +0 -49
  52. package/realtime-testing/dist/index.js +0 -165
  53. package/realtime-testing/dist/index.js.map +0 -1
  54. package/realtime-testing/dist/index.mjs +0 -129
  55. package/realtime-testing/dist/index.mjs.map +0 -1
  56. package/src/internal/meta/attach-meta.ts +0 -17
  57. package/src/internal/meta/index.ts +0 -4
  58. package/src/internal/meta/meta-state.ts +0 -135
  59. package/src/internal/meta/meta-timelines.ts +0 -1
  60. package/src/internal/meta/meta-transactions.ts +0 -1
  61. package/src/react-devtools/TokenList.tsx +0 -61
@@ -1,107 +1,108 @@
1
- import { atom, __INTERNAL__ } from "atom.io"
2
- import type { StoreHooks } from "atom.io/react"
3
- import { useI, useO, useIO } from "atom.io/react"
1
+ import { useO, useIO } from "atom.io/react"
4
2
  import { LayoutGroup, motion, spring } from "framer-motion"
5
3
  import { useRef } from "react"
6
4
  import type { FC } from "react"
7
5
 
8
- import { TokenList } from "./TokenList"
9
- import { lazyLocalStorageEffect } from "../web-effects"
6
+ import {
7
+ atomIndex,
8
+ devtoolsAreOpenState,
9
+ devtoolsViewOptionsState,
10
+ devtoolsViewSelectionState,
11
+ selectorIndex,
12
+ } from "."
13
+ import { StateIndex } from "./StateIndex"
14
+ import { TimelineIndex } from "./TimelineIndex"
15
+ import { TransactionIndex } from "./TransactionIndex"
10
16
 
11
17
  import "./devtools.scss"
12
18
 
13
- const { atomTokenIndexState, selectorTokenIndexState } =
14
- __INTERNAL__.META.attachMetaState()
19
+ export const AtomIODevtools: FC = () => {
20
+ const constraintsRef = useRef(null)
15
21
 
16
- const devtoolsAreOpenState = atom<boolean>({
17
- key: `👁‍🗨_devtools_are_open`,
18
- default: true,
19
- effects: [lazyLocalStorageEffect(`👁‍🗨_devtools_are_open`)],
20
- })
22
+ const [devtoolsAreOpen, setDevtoolsAreOpen] = useIO(devtoolsAreOpenState)
23
+ const [devtoolsView, setDevtoolsView] = useIO(devtoolsViewSelectionState)
24
+ const devtoolsViewOptions = useO(devtoolsViewOptionsState)
21
25
 
22
- export const composeDevtools = (storeHooks: StoreHooks): FC => {
23
- const Devtools: FC = () => {
24
- const constraintsRef = useRef(null)
26
+ const mouseHasMoved = useRef(false)
25
27
 
26
- const [devtoolsAreOpen, setDevtoolsAreOpen] =
27
- storeHooks.useIO(devtoolsAreOpenState)
28
-
29
- const mouseHasMoved = useRef(false)
30
-
31
- return (
32
- <>
33
- <motion.span
34
- ref={constraintsRef}
35
- className="atom_io_devtools_zone"
36
- style={{
37
- position: `fixed`,
38
- top: 0,
39
- left: 0,
40
- right: 0,
41
- bottom: 0,
42
- pointerEvents: `none`,
43
- }}
44
- />
45
- <motion.main
46
- drag
47
- dragConstraints={constraintsRef}
48
- className="atom_io_devtools"
49
- transition={spring}
50
- style={
51
- devtoolsAreOpen
52
- ? {}
53
- : {
54
- backgroundColor: `#0000`,
55
- borderColor: `#0000`,
56
- maxHeight: 28,
57
- maxWidth: 33,
58
- }
59
- }
60
- >
61
- {devtoolsAreOpen ? (
62
- <>
63
- <motion.header>
64
- <h1>atom.io</h1>
65
- </motion.header>
66
- <motion.main>
67
- <LayoutGroup>
68
- <section>
69
- <h2>atoms</h2>
70
- <TokenList
71
- storeHooks={storeHooks}
72
- tokenIndex={atomTokenIndexState}
73
- />
74
- </section>
75
- <section>
76
- <h2>selectors</h2>
77
- <TokenList
78
- storeHooks={storeHooks}
79
- tokenIndex={selectorTokenIndexState}
80
- />
81
- </section>
82
- </LayoutGroup>
83
- </motion.main>
84
- </>
85
- ) : null}
86
- <footer>
87
- <button
88
- type="button"
89
- onMouseDown={() => (mouseHasMoved.current = false)}
90
- onMouseMove={() => (mouseHasMoved.current = true)}
91
- onMouseUp={() => {
92
- if (!mouseHasMoved.current) {
93
- setDevtoolsAreOpen((open) => !open)
94
- }
95
- }}
96
- >
97
- 👁‍🗨
98
- </button>
99
- </footer>
100
- </motion.main>
101
- </>
102
- )
103
- }
104
- return Devtools
28
+ return (
29
+ <>
30
+ <motion.span
31
+ ref={constraintsRef}
32
+ className="atom_io_devtools_zone"
33
+ style={{
34
+ position: `fixed`,
35
+ top: 0,
36
+ left: 0,
37
+ right: 0,
38
+ bottom: 0,
39
+ pointerEvents: `none`,
40
+ }}
41
+ />
42
+ <motion.main
43
+ drag
44
+ dragConstraints={constraintsRef}
45
+ className="atom_io_devtools"
46
+ transition={spring}
47
+ style={
48
+ devtoolsAreOpen
49
+ ? {}
50
+ : {
51
+ backgroundColor: `#0000`,
52
+ borderColor: `#0000`,
53
+ maxHeight: 28,
54
+ maxWidth: 33,
55
+ }
56
+ }
57
+ >
58
+ {devtoolsAreOpen ? (
59
+ <>
60
+ <motion.header>
61
+ <h1>atom.io</h1>
62
+ <nav>
63
+ {devtoolsViewOptions.map((viewOption) => (
64
+ <button
65
+ key={viewOption}
66
+ type="button"
67
+ className={viewOption === devtoolsView ? `active` : ``}
68
+ onClick={() => setDevtoolsView(viewOption)}
69
+ disabled={viewOption === devtoolsView}
70
+ >
71
+ {viewOption}
72
+ </button>
73
+ ))}
74
+ </nav>
75
+ </motion.header>
76
+ <motion.main>
77
+ <LayoutGroup>
78
+ {devtoolsView === `atoms` ? (
79
+ <StateIndex tokenIndex={atomIndex} />
80
+ ) : devtoolsView === `selectors` ? (
81
+ <StateIndex tokenIndex={selectorIndex} />
82
+ ) : devtoolsView === `transactions` ? (
83
+ <TransactionIndex />
84
+ ) : devtoolsView === `timelines` ? (
85
+ <TimelineIndex />
86
+ ) : null}
87
+ </LayoutGroup>
88
+ </motion.main>
89
+ </>
90
+ ) : null}
91
+ <footer>
92
+ <button
93
+ type="button"
94
+ onMouseDown={() => (mouseHasMoved.current = false)}
95
+ onMouseMove={() => (mouseHasMoved.current = true)}
96
+ onMouseUp={() => {
97
+ if (!mouseHasMoved.current) {
98
+ setDevtoolsAreOpen((open) => !open)
99
+ }
100
+ }}
101
+ >
102
+ 👁‍🗨
103
+ </button>
104
+ </footer>
105
+ </motion.main>
106
+ </>
107
+ )
105
108
  }
106
-
107
- export const AtomIODevtools = composeDevtools({ useI, useO, useIO })
@@ -0,0 +1,24 @@
1
+ import type { FC } from "react"
2
+
3
+ import type { Modifier } from "~/packages/anvl/src/function"
4
+
5
+ export const OpenClose: FC<{
6
+ isOpen: boolean
7
+ setIsOpen: (next: Modifier<boolean> | boolean) => void
8
+ disabled?: boolean
9
+ }> = ({ isOpen, setIsOpen, disabled }) => {
10
+ return (
11
+ <button
12
+ type="button"
13
+ className={`carat ${isOpen ? `open` : `closed`}`}
14
+ onClick={() => setIsOpen((isOpen) => !isOpen)}
15
+ disabled={disabled}
16
+ >
17
+
18
+ </button>
19
+ )
20
+ }
21
+
22
+ export const button = {
23
+ OpenClose,
24
+ }
@@ -1,17 +1,17 @@
1
1
  import type { ReadonlySelectorToken, StateToken } from "atom.io"
2
- import type { StoreHooks } from "atom.io/react"
2
+ import { useO, useIO } from "atom.io/react"
3
3
  import type { FC } from "react"
4
4
 
5
- import { isPlainJson } from "~/packages/anvl/src/json"
5
+ import { fallback } from "~/packages/anvl/src/function"
6
+ import { isJson } from "~/packages/anvl/src/json"
6
7
  import { ElasticInput } from "~/packages/hamr/src/react-elastic-input"
7
8
  import { JsonEditor } from "~/packages/hamr/src/react-json-editor"
8
9
 
9
10
  export const StateEditor: FC<{
10
- storeHooks: StoreHooks
11
11
  token: StateToken<unknown>
12
- }> = ({ storeHooks, token }) => {
13
- const [data, set] = storeHooks.useIO(token)
14
- return isPlainJson(data) ? (
12
+ }> = ({ token }) => {
13
+ const [data, set] = useIO(token)
14
+ return isJson(data) ? (
15
15
  <JsonEditor data={data} set={set} schema={true} />
16
16
  ) : (
17
17
  <div className="json_editor">
@@ -23,7 +23,7 @@ export const StateEditor: FC<{
23
23
  ? `Map ` + JSON.stringify([...data])
24
24
  : Object.getPrototypeOf(data).constructor.name +
25
25
  ` ` +
26
- JSON.stringify(data)
26
+ fallback(() => JSON.stringify(data), `?`)
27
27
  }
28
28
  disabled={true}
29
29
  />
@@ -31,12 +31,11 @@ export const StateEditor: FC<{
31
31
  )
32
32
  }
33
33
 
34
- export const ReadonlySelectorEditor: FC<{
35
- storeHooks: StoreHooks
34
+ export const ReadonlySelectorViewer: FC<{
36
35
  token: ReadonlySelectorToken<unknown>
37
- }> = ({ storeHooks, token }) => {
38
- const data = storeHooks.useO(token)
39
- return isPlainJson(data) ? (
36
+ }> = ({ token }) => {
37
+ const data = useO(token)
38
+ return isJson(data) ? (
40
39
  <JsonEditor
41
40
  data={data}
42
41
  set={() => null}
@@ -62,11 +61,10 @@ export const ReadonlySelectorEditor: FC<{
62
61
  }
63
62
 
64
63
  export const StoreEditor: FC<{
65
- storeHooks: StoreHooks
66
64
  token: ReadonlySelectorToken<unknown> | StateToken<unknown>
67
- }> = ({ storeHooks, token }) => {
65
+ }> = ({ token }) => {
68
66
  if (token.type === `readonly_selector`) {
69
- return <ReadonlySelectorEditor storeHooks={storeHooks} token={token} />
67
+ return <ReadonlySelectorViewer token={token} />
70
68
  }
71
- return <StateEditor storeHooks={storeHooks} token={token} />
69
+ return <StateEditor token={token} />
72
70
  }
@@ -0,0 +1,153 @@
1
+ import type { AtomToken, ReadonlySelectorToken, SelectorToken } from "atom.io"
2
+ import { getState, selectorFamily } from "atom.io"
3
+ import { useO, useIO } from "atom.io/react"
4
+ import type { FC } from "react"
5
+
6
+ import { isJson, refineJsonType } from "~/packages/anvl/src/json"
7
+
8
+ import { findViewIsOpenState, primitiveRefinery } from "."
9
+ import { button } from "./Button"
10
+ import { StoreEditor } from "./StateEditor"
11
+ import type { FamilyNode, StateTokenIndex } from "../introspection"
12
+
13
+ const findStateTypeState = selectorFamily<string, { key: string }>({
14
+ key: `👁‍🗨 State Type`,
15
+ get: (token) => ({ get }) => {
16
+ let state: unknown
17
+ try {
18
+ state = get(token as any)
19
+ } catch (error) {
20
+ return `error`
21
+ }
22
+ if (state === undefined) return `undefined`
23
+ if (isJson(state)) return refineJsonType(state).type
24
+ return Object.getPrototypeOf(state).constructor.name
25
+ },
26
+ })
27
+
28
+ export const StateIndexLeafNode: FC<{
29
+ node:
30
+ | AtomToken<unknown>
31
+ | ReadonlySelectorToken<unknown>
32
+ | SelectorToken<unknown>
33
+ isOpenState: AtomToken<boolean>
34
+ typeState: ReadonlySelectorToken<string>
35
+ }> = ({ node, isOpenState, typeState }) => {
36
+ const [isOpen, setIsOpen] = useIO(isOpenState)
37
+ const state = useO(node)
38
+ const stateType = useO(typeState)
39
+
40
+ const isPrimitive = Boolean(primitiveRefinery.refine(state))
41
+
42
+ return (
43
+ <>
44
+ <header>
45
+ <button.OpenClose
46
+ isOpen={isOpen && !isPrimitive}
47
+ setIsOpen={setIsOpen}
48
+ disabled={isPrimitive}
49
+ />
50
+ <label
51
+ onClick={() => console.log(node, getState(node))}
52
+ onKeyUp={() => console.log(node, getState(node))}
53
+ >
54
+ <h2>{node.family?.subKey ?? node.key}</h2>
55
+ <span className="type detail">({stateType})</span>
56
+ </label>
57
+ {isPrimitive ? <StoreEditor token={node} /> : null}
58
+ </header>
59
+ {isOpen && !isPrimitive ? (
60
+ <main>
61
+ <StoreEditor token={node} />
62
+ </main>
63
+ ) : null}
64
+ </>
65
+ )
66
+ }
67
+ export const StateIndexTreeNode: FC<{
68
+ node: FamilyNode<
69
+ AtomToken<unknown> | ReadonlySelectorToken<unknown> | SelectorToken<unknown>
70
+ >
71
+ isOpenState: AtomToken<boolean>
72
+ }> = ({ node, isOpenState }) => {
73
+ const [isOpen, setIsOpen] = useIO(isOpenState)
74
+ Object.entries(node.familyMembers).forEach(([key, childNode]) => {
75
+ findViewIsOpenState(key)
76
+ findStateTypeState(childNode)
77
+ })
78
+ return (
79
+ <>
80
+ <header>
81
+ <button.OpenClose isOpen={isOpen} setIsOpen={setIsOpen} />
82
+ <label>
83
+ <h2>{node.key}</h2>
84
+ <span className="type detail"> (family)</span>
85
+ </label>
86
+ </header>
87
+ {isOpen
88
+ ? Object.entries(node.familyMembers).map(([key, childNode]) => (
89
+ <StateIndexNode
90
+ key={key}
91
+ node={childNode}
92
+ isOpenState={findViewIsOpenState(childNode.key)}
93
+ typeState={findStateTypeState(childNode)}
94
+ />
95
+ ))
96
+ : null}
97
+ </>
98
+ )
99
+ }
100
+
101
+ export const StateIndexNode: FC<{
102
+ node: StateTokenIndex<
103
+ AtomToken<unknown> | ReadonlySelectorToken<unknown> | SelectorToken<unknown>
104
+ >[string]
105
+ isOpenState: AtomToken<boolean>
106
+ typeState: ReadonlySelectorToken<string>
107
+ }> = ({ node, isOpenState, typeState }) => {
108
+ if (node.key.startsWith(`👁‍🗨`)) {
109
+ return null
110
+ }
111
+ return (
112
+ <section className="node state">
113
+ {`type` in node ? (
114
+ <StateIndexLeafNode
115
+ node={node}
116
+ isOpenState={isOpenState}
117
+ typeState={typeState}
118
+ />
119
+ ) : (
120
+ <StateIndexTreeNode node={node} isOpenState={isOpenState} />
121
+ )}
122
+ </section>
123
+ )
124
+ }
125
+
126
+ export const StateIndex: FC<{
127
+ tokenIndex: ReadonlySelectorToken<
128
+ StateTokenIndex<
129
+ | AtomToken<unknown>
130
+ | ReadonlySelectorToken<unknown>
131
+ | SelectorToken<unknown>
132
+ >
133
+ >
134
+ }> = ({ tokenIndex }) => {
135
+ const tokenIds = useO(tokenIndex)
136
+ return (
137
+ <article className="index state_index">
138
+ {Object.entries(tokenIds)
139
+ .filter(([key]) => !key.startsWith(`👁‍🗨`))
140
+ .sort()
141
+ .map(([key, node]) => {
142
+ return (
143
+ <StateIndexNode
144
+ key={key}
145
+ node={node}
146
+ isOpenState={findViewIsOpenState(node.key)}
147
+ typeState={findStateTypeState(node)}
148
+ />
149
+ )
150
+ })}
151
+ </article>
152
+ )
153
+ }
@@ -0,0 +1,92 @@
1
+ import {
2
+ undo,
3
+ type AtomToken,
4
+ type ReadonlySelectorToken,
5
+ type TimelineToken,
6
+ redo,
7
+ } from "atom.io"
8
+ import { useIO, useO } from "atom.io/react"
9
+ import { Fragment, type FC } from "react"
10
+
11
+ import { findTimelineState, findViewIsOpenState, timelineIndex } from "."
12
+ import { button } from "./Button"
13
+ import { article } from "./Updates"
14
+ import type { Timeline } from "../internal"
15
+
16
+ export const YouAreHere: FC = () => {
17
+ return <span className="you_are_here">you are here</span>
18
+ }
19
+
20
+ export const TimelineLog: FC<{
21
+ token: TimelineToken
22
+ isOpenState: AtomToken<boolean>
23
+ timelineState: ReadonlySelectorToken<Timeline>
24
+ }> = ({ token, isOpenState, timelineState }) => {
25
+ const timeline = useO(timelineState)
26
+ const [isOpen, setIsOpen] = useIO(isOpenState)
27
+
28
+ return (
29
+ <section className="node timeline_log">
30
+ <header>
31
+ <button.OpenClose isOpen={isOpen} setIsOpen={setIsOpen} />
32
+ <label>
33
+ <h2>{token.key}</h2>
34
+ <span className="detail length">
35
+ ({timeline.at}/{timeline.history.length})
36
+ </span>
37
+ <span className="gap" />
38
+ <nav>
39
+ <button
40
+ type="button"
41
+ onClick={() => undo(token)}
42
+ disabled={timeline.at === 0}
43
+ >
44
+ undo
45
+ </button>
46
+ <button
47
+ type="button"
48
+ onClick={() => redo(token)}
49
+ disabled={timeline.at === timeline.history.length}
50
+ >
51
+ redo
52
+ </button>
53
+ </nav>
54
+ </label>
55
+ </header>
56
+ {isOpen ? (
57
+ <main>
58
+ {timeline.history.map((update, index) => (
59
+ <Fragment key={update.key + index + timeline.at}>
60
+ {index === timeline.at ? <YouAreHere /> : null}
61
+ <article.TimelineUpdate timelineUpdate={update} />
62
+ {index === timeline.history.length - 1 &&
63
+ timeline.at === timeline.history.length ? (
64
+ <YouAreHere />
65
+ ) : null}
66
+ </Fragment>
67
+ ))}
68
+ </main>
69
+ ) : null}
70
+ </section>
71
+ )
72
+ }
73
+
74
+ export const TimelineIndex: FC = () => {
75
+ const tokenIds = useO(timelineIndex)
76
+ return (
77
+ <article className="index timeline_index">
78
+ {tokenIds
79
+ .filter((token) => !token.key.startsWith(`👁‍🗨`))
80
+ .map((token) => {
81
+ return (
82
+ <TimelineLog
83
+ key={token.key}
84
+ token={token}
85
+ isOpenState={findViewIsOpenState(token.key)}
86
+ timelineState={findTimelineState(token.key)}
87
+ />
88
+ )
89
+ })}
90
+ </article>
91
+ )
92
+ }
@@ -0,0 +1,70 @@
1
+ import type {
2
+ AtomToken,
3
+ ReadonlySelectorToken,
4
+ TransactionToken,
5
+ TransactionUpdate,
6
+ } from "atom.io"
7
+ import { useIO, useO } from "atom.io/react"
8
+ import type { FC } from "react"
9
+
10
+ import type { ƒn } from "~/packages/anvl/src/function"
11
+
12
+ import {
13
+ findTransactionLogState,
14
+ findViewIsOpenState,
15
+ transactionIndex,
16
+ } from "."
17
+ import { button } from "./Button"
18
+ import { article } from "./Updates"
19
+
20
+ export const TransactionLog: FC<{
21
+ token: TransactionToken<ƒn>
22
+ isOpenState: AtomToken<boolean>
23
+ logState: ReadonlySelectorToken<TransactionUpdate<ƒn>[]>
24
+ }> = ({ token, isOpenState, logState }) => {
25
+ const log = useO(logState)
26
+ const [isOpen, setIsOpen] = useIO(isOpenState)
27
+
28
+ return (
29
+ <section className="node transaction_log">
30
+ <header>
31
+ <button.OpenClose isOpen={isOpen} setIsOpen={setIsOpen} />
32
+ <label>
33
+ <h2>{token.key}</h2>
34
+ <span className="detail length">({log.length})</span>
35
+ </label>
36
+ </header>
37
+ {isOpen ? (
38
+ <main>
39
+ {log.map((update, index) => (
40
+ <article.TransactionUpdate
41
+ key={update.key + index}
42
+ serialNumber={index}
43
+ transactionUpdate={update}
44
+ />
45
+ ))}
46
+ </main>
47
+ ) : null}
48
+ </section>
49
+ )
50
+ }
51
+
52
+ export const TransactionIndex: FC = () => {
53
+ const tokenIds = useO(transactionIndex)
54
+ return (
55
+ <article className="index transaction_index">
56
+ {tokenIds
57
+ .filter((token) => !token.key.startsWith(`👁‍🗨`))
58
+ .map((token) => {
59
+ return (
60
+ <TransactionLog
61
+ key={token.key}
62
+ token={token}
63
+ isOpenState={findViewIsOpenState(token.key)}
64
+ logState={findTransactionLogState(token.key)}
65
+ />
66
+ )
67
+ })}
68
+ </article>
69
+ )
70
+ }