feat: state inspector

This commit is contained in:
EnixCoda 2022-07-17 00:41:17 +08:00
parent b28c9e0fe8
commit 1d099d23b8
5 changed files with 87 additions and 15 deletions

7
src/common.d.ts vendored Normal file
View file

@ -0,0 +1,7 @@
// Similar to `global.d.ts` but with import/export
import { Dispatch, SetStateAction } from 'react'
type ReactIO<T> = {
value: T
onChange: Dispatch<SetStateAction<T>>
}

View file

@ -1,6 +1,7 @@
import { SideBar } from 'components/SideBar'
import { ConfigsContextWrapper } from 'containers/ConfigsContext'
import { ReloadContextWrapper } from 'containers/ReloadContext'
import { InspectorContextWrapper } from 'containers/StateInspector'
import * as React from 'react'
import { ErrorBoundary } from '../containers/ErrorBoundary'
import { StateBarErrorContextWrapper } from '../containers/ErrorContext'
@ -10,20 +11,22 @@ import { StateBarStateContextWrapper } from '../containers/SideBarState'
export function Gitako() {
return (
<ReloadContextWrapper>
<ErrorBoundary>
<ConfigsContextWrapper>
<StateBarStateContextWrapper>
<StateBarErrorContextWrapper>
<OAuthWrapper>
<RepoContextWrapper>
<SideBar />
</RepoContextWrapper>
</OAuthWrapper>
</StateBarErrorContextWrapper>
</StateBarStateContextWrapper>
</ConfigsContextWrapper>
</ErrorBoundary>
</ReloadContextWrapper>
<InspectorContextWrapper>
<ReloadContextWrapper>
<ErrorBoundary>
<ConfigsContextWrapper>
<StateBarStateContextWrapper>
<StateBarErrorContextWrapper>
<OAuthWrapper>
<RepoContextWrapper>
<SideBar />
</RepoContextWrapper>
</OAuthWrapper>
</StateBarErrorContextWrapper>
</StateBarStateContextWrapper>
</ConfigsContextWrapper>
</ErrorBoundary>
</ReloadContextWrapper>
</InspectorContextWrapper>
)
}

View file

@ -1,3 +1,4 @@
import { useInspector } from 'containers/StateInspector'
import * as React from 'react'
import { useStateIO } from 'utils/hooks/useStateIO'
@ -7,6 +8,7 @@ export const SideBarErrorContext = React.createContext<SideBarErrorContextShape
export function StateBarErrorContextWrapper({ children }: React.PropsWithChildren<{}>) {
const $error = useStateIO<string | null>(null)
useInspector('SideBarErrorContext', $error.value)
return <SideBarErrorContext.Provider value={$error}>{children}</SideBarErrorContext.Provider>
}

View file

@ -1,5 +1,6 @@
import * as React from 'react'
import { useStateIO } from 'utils/hooks/useStateIO'
import { useInspector } from './StateInspector'
export type SideBarState =
| 'disabled'
@ -19,6 +20,7 @@ export const SideBarStateContext = React.createContext<SideBarStateContextShape
export function StateBarStateContextWrapper({ children }: React.PropsWithChildren<{}>) {
const $state = useStateIO<SideBarState>('disabled')
useInspector('SideBarStateContext', $state.value)
return (
<SideBarStateContext.Provider value={$state}>

View file

@ -0,0 +1,58 @@
import { ReactIO } from 'common'
import { IN_PRODUCTION_MODE } from 'env'
import * as React from 'react'
import { useStateIO } from 'utils/hooks/useStateIO'
export type InspectorContextShape = ReactIO<JSONObject>
export const InspectorContext = React.createContext<InspectorContextShape | null>(null)
export const InspectorContextWrapper = IN_PRODUCTION_MODE
? React.Fragment
: function InspectorContextWrapper({ children }: React.PropsWithChildren<{}>) {
const $ = useStateIO<JSONObject>({})
const [show, setShow] = React.useState(true)
return (
<InspectorContext.Provider value={$}>
{show ? (
<div
style={{
position: 'fixed',
top: '0',
right: '0',
height: '100vh',
width: '360px',
overflow: 'auto',
background: 'rgba(255, 255, 255, 0.75)',
display: 'flex',
flexDirection: 'column',
}}
>
<pre style={{ flex: 1 }}>{JSON.stringify($.value, null, 2)}</pre>
<div>
<button onClick={() => setShow(false)}></button>
</div>
</div>
) : (
<div
style={{
position: 'fixed',
bottom: '0',
right: '0',
}}
>
<button onClick={() => setShow(true)}>🔎</button>
</div>
)}
{children}
</InspectorContext.Provider>
)
}
export function useInspector(key: string, value: JSONValue) {
const $ = React.useContext(InspectorContext)
React.useEffect(() => {
$?.onChange(prev => ({ ...prev, [key]: value }))
}, [key, value]) // eslint-disable-line react-hooks/exhaustive-deps
}