Skip to content

Commit 1d099d2

Browse files
committed
feat: state inspector
1 parent b28c9e0 commit 1d099d2

File tree

5 files changed

+87
-15
lines changed

5 files changed

+87
-15
lines changed

src/common.d.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// Similar to `global.d.ts` but with import/export
2+
import { Dispatch, SetStateAction } from 'react'
3+
4+
type ReactIO<T> = {
5+
value: T
6+
onChange: Dispatch<SetStateAction<T>>
7+
}

src/components/Gitako.tsx

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { SideBar } from 'components/SideBar'
22
import { ConfigsContextWrapper } from 'containers/ConfigsContext'
33
import { ReloadContextWrapper } from 'containers/ReloadContext'
4+
import { InspectorContextWrapper } from 'containers/StateInspector'
45
import * as React from 'react'
56
import { ErrorBoundary } from '../containers/ErrorBoundary'
67
import { StateBarErrorContextWrapper } from '../containers/ErrorContext'
@@ -10,20 +11,22 @@ import { StateBarStateContextWrapper } from '../containers/SideBarState'
1011

1112
export function Gitako() {
1213
return (
13-
<ReloadContextWrapper>
14-
<ErrorBoundary>
15-
<ConfigsContextWrapper>
16-
<StateBarStateContextWrapper>
17-
<StateBarErrorContextWrapper>
18-
<OAuthWrapper>
19-
<RepoContextWrapper>
20-
<SideBar />
21-
</RepoContextWrapper>
22-
</OAuthWrapper>
23-
</StateBarErrorContextWrapper>
24-
</StateBarStateContextWrapper>
25-
</ConfigsContextWrapper>
26-
</ErrorBoundary>
27-
</ReloadContextWrapper>
14+
<InspectorContextWrapper>
15+
<ReloadContextWrapper>
16+
<ErrorBoundary>
17+
<ConfigsContextWrapper>
18+
<StateBarStateContextWrapper>
19+
<StateBarErrorContextWrapper>
20+
<OAuthWrapper>
21+
<RepoContextWrapper>
22+
<SideBar />
23+
</RepoContextWrapper>
24+
</OAuthWrapper>
25+
</StateBarErrorContextWrapper>
26+
</StateBarStateContextWrapper>
27+
</ConfigsContextWrapper>
28+
</ErrorBoundary>
29+
</ReloadContextWrapper>
30+
</InspectorContextWrapper>
2831
)
2932
}

src/containers/ErrorContext.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { useInspector } from 'containers/StateInspector'
12
import * as React from 'react'
23
import { useStateIO } from 'utils/hooks/useStateIO'
34

@@ -7,6 +8,7 @@ export const SideBarErrorContext = React.createContext<SideBarErrorContextShape
78

89
export function StateBarErrorContextWrapper({ children }: React.PropsWithChildren<{}>) {
910
const $error = useStateIO<string | null>(null)
11+
useInspector('SideBarErrorContext', $error.value)
1012

1113
return <SideBarErrorContext.Provider value={$error}>{children}</SideBarErrorContext.Provider>
1214
}

src/containers/SideBarState.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import * as React from 'react'
22
import { useStateIO } from 'utils/hooks/useStateIO'
3+
import { useInspector } from './StateInspector'
34

45
export type SideBarState =
56
| 'disabled'
@@ -19,6 +20,7 @@ export const SideBarStateContext = React.createContext<SideBarStateContextShape
1920

2021
export function StateBarStateContextWrapper({ children }: React.PropsWithChildren<{}>) {
2122
const $state = useStateIO<SideBarState>('disabled')
23+
useInspector('SideBarStateContext', $state.value)
2224

2325
return (
2426
<SideBarStateContext.Provider value={$state}>

src/containers/StateInspector.tsx

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import { ReactIO } from 'common'
2+
import { IN_PRODUCTION_MODE } from 'env'
3+
import * as React from 'react'
4+
import { useStateIO } from 'utils/hooks/useStateIO'
5+
6+
export type InspectorContextShape = ReactIO<JSONObject>
7+
8+
export const InspectorContext = React.createContext<InspectorContextShape | null>(null)
9+
10+
export const InspectorContextWrapper = IN_PRODUCTION_MODE
11+
? React.Fragment
12+
: function InspectorContextWrapper({ children }: React.PropsWithChildren<{}>) {
13+
const $ = useStateIO<JSONObject>({})
14+
const [show, setShow] = React.useState(true)
15+
16+
return (
17+
<InspectorContext.Provider value={$}>
18+
{show ? (
19+
<div
20+
style={{
21+
position: 'fixed',
22+
top: '0',
23+
right: '0',
24+
height: '100vh',
25+
width: '360px',
26+
overflow: 'auto',
27+
background: 'rgba(255, 255, 255, 0.75)',
28+
display: 'flex',
29+
flexDirection: 'column',
30+
}}
31+
>
32+
<pre style={{ flex: 1 }}>{JSON.stringify($.value, null, 2)}</pre>
33+
<div>
34+
<button onClick={() => setShow(false)}></button>
35+
</div>
36+
</div>
37+
) : (
38+
<div
39+
style={{
40+
position: 'fixed',
41+
bottom: '0',
42+
right: '0',
43+
}}
44+
>
45+
<button onClick={() => setShow(true)}>🔎</button>
46+
</div>
47+
)}
48+
{children}
49+
</InspectorContext.Provider>
50+
)
51+
}
52+
53+
export function useInspector(key: string, value: JSONValue) {
54+
const $ = React.useContext(InspectorContext)
55+
React.useEffect(() => {
56+
$?.onChange(prev => ({ ...prev, [key]: value }))
57+
}, [key, value]) // eslint-disable-line react-hooks/exhaustive-deps
58+
}

0 commit comments

Comments
 (0)