chore: update IO field names

This commit is contained in:
EnixCoda 2021-02-01 14:13:21 +08:00
parent 4be1d2d44d
commit 8f675f5c5b
No known key found for this signature in database
GPG key ID: 0C1A07377913A1DD
16 changed files with 88 additions and 93 deletions

View file

@ -32,7 +32,7 @@ const RawFileExplorer: React.FC<Props & ConnectorState> = function RawFileExplor
treeRoot,
defer,
} = props
const { val: config } = useConfigs()
const { value: config } = useConfigs()
React.useEffect(() => {
if (treeRoot) {

View file

@ -87,7 +87,7 @@ const NodeItemIcon = React.memo(function NodeItemIcon({
loading?: boolean
}) {
const {
val: { icons },
value: { icons },
} = useConfigs()
const src = React.useMemo(

View file

@ -38,7 +38,7 @@ export function Resizable({ baseSize, className, children }: React.PropsWithChil
}, [width, size])
useCSSVariable('--gitako-width', `${size}px`)
useDebounce(() => configContext.set({ sideBarWidth: size }), 100, [size])
useDebounce(() => configContext.onChange({ sideBarWidth: size }), 100, [size])
const onResize = React.useCallback((size: number) => {
// do NOT merge this with the above similar effect, side bar will jump otherwise

View file

@ -38,16 +38,16 @@ const RawGitako: React.FC<Props & ConnectorState> = function RawGitako(props) {
configContext,
} = props
const accessToken = configContext.val.accessToken || ''
const [baseSize] = React.useState(() => configContext.val.sideBarWidth)
const accessToken = configContext.value.accessToken || ''
const [baseSize] = React.useState(() => configContext.value.sideBarWidth)
useShrinkGitHubHeader(configContext.val.shrinkGitHubHeader)
useShrinkGitHubHeader(configContext.value.shrinkGitHubHeader)
React.useEffect(() => {
run(async function () {
if (!accessToken) {
const accessToken = await trySetUpAccessTokenWithCode()
if (accessToken) configContext.set({ accessToken })
if (accessToken) configContext.onChange({ accessToken })
}
})
}, [])
@ -58,21 +58,21 @@ const RawGitako: React.FC<Props & ConnectorState> = function RawGitako(props) {
React.useEffect(
function attachKeyDown() {
if (props.disabled || !configContext.val.shortcut) return
if (props.disabled || !configContext.value.shortcut) return
function onKeyDown(e: KeyboardEvent) {
const keys = keyHelper.parseEvent(e)
if (keys === configContext.val.shortcut) {
if (keys === configContext.value.shortcut) {
toggleShowSideBar()
}
}
window.addEventListener('keydown', onKeyDown)
return () => window.removeEventListener('keydown', onKeyDown)
},
[toggleShowSideBar, props.disabled, configContext.val.shortcut],
[toggleShowSideBar, props.disabled, configContext.value.shortcut],
)
const intelligentToggle = configContext.val.intelligentToggle
const intelligentToggle = configContext.value.intelligentToggle
React.useEffect(() => {
const shouldShow = intelligentToggle === null ? platform.shouldShow() : intelligentToggle
props.setShouldShow(shouldShow)
@ -88,8 +88,8 @@ const RawGitako: React.FC<Props & ConnectorState> = function RawGitako(props) {
)
useOnPJAXDone(updateSideBarVisibility)
useGitHubAttachCopyFileButton(configContext.val.copyFileButton)
useGitHubAttachCopySnippetButton(configContext.val.copySnippetButton)
useGitHubAttachCopyFileButton(configContext.value.copyFileButton)
useGitHubAttachCopySnippetButton(configContext.value.copySnippetButton)
usePJAX()
useProgressBar()
@ -122,7 +122,7 @@ const RawGitako: React.FC<Props & ConnectorState> = function RawGitako(props) {
freeze={showSettings}
accessToken={accessToken}
loadWithPJAX={loadWithPJAX}
config={configContext.val}
config={configContext.value}
defer={defer}
/>
)

View file

@ -24,7 +24,7 @@ type Props = {
export function SimpleToggleField({ field, onChange }: Props) {
const { overwrite } = field
const configContext = useConfigs()
const value = configContext.val[field.key]
const value = configContext.value[field.key]
return (
<Field
id={field.key}
@ -57,7 +57,7 @@ export function SimpleToggleField({ field, onChange }: Props) {
type={'checkbox'}
onChange={async e => {
const enabled = e.currentTarget.checked
configContext.set({ [field.key]: overwrite ? overwrite.onChange(enabled) : enabled })
configContext.onChange({ [field.key]: overwrite ? overwrite.onChange(enabled) : enabled })
if (onChange) onChange()
}}
checked={overwrite ? overwrite.value(value) : Boolean(value)}

View file

@ -11,7 +11,7 @@ type Props = {
export function ToggleShowButton({ error, onClick }: Props) {
const ref = React.useRef<HTMLDivElement>(null)
const config = useConfigs()
const [distance, setDistance] = React.useState(config.val.toggleButtonVerticalDistance)
const [distance, setDistance] = React.useState(config.value.toggleButtonVerticalDistance)
const { height } = useWindowSize()
const buttonHeight = 42
React.useEffect(() => {
@ -28,12 +28,12 @@ export function ToggleShowButton({ error, onClick }: Props) {
// updating context
useDebounce(
() => config.set({ toggleButtonVerticalDistance: distance }), // too slow
() => config.onChange({ toggleButtonVerticalDistance: distance }), // too slow
100,
[distance],
)
const toggleIconMode = config.val.toggleButtonContent
const toggleIconMode = config.value.toggleButtonContent
return (
<div ref={ref} className={'gitako-toggle-show-button-wrapper'}>
<button

View file

@ -5,7 +5,7 @@ import { platform } from 'platforms'
import { Gitea } from 'platforms/Gitea'
import { Gitee } from 'platforms/Gitee'
import * as React from 'react'
import { useStates } from 'utils/hooks/useStates'
import { useStateIO } from 'utils/hooks/useStateIO'
import { SettingsSection } from './SettingsSection'
const ACCESS_TOKEN_REGEXP = /^[0-9a-f]+$/
@ -14,23 +14,23 @@ type Props = {}
export function AccessTokenSettings(props: React.PropsWithChildren<Props>) {
const configContext = useConfigs()
const hasAccessToken = Boolean(configContext.val.accessToken)
const useAccessToken = useStates('')
const useAccessTokenHint = useStates<React.ReactNode>('')
const focusInput = useStates(false)
const hasAccessToken = Boolean(configContext.value.accessToken)
const useAccessToken = useStateIO('')
const useAccessTokenHint = useStateIO<React.ReactNode>('')
const focusInput = useStateIO(false)
const { val: accessTokenHint } = useAccessTokenHint
const { val: accessToken } = useAccessToken
const { value: accessTokenHint } = useAccessTokenHint
const { value: accessToken } = useAccessToken
React.useEffect(() => {
// clear input when access token updates
useAccessToken.set('')
}, [configContext.val.accessToken])
useAccessToken.onChange('')
}, [configContext.value.accessToken])
const onInputAccessToken = React.useCallback(
({ currentTarget: { value } }: React.FormEvent<HTMLInputElement>) => {
useAccessToken.set(value)
useAccessTokenHint.set(
useAccessToken.onChange(value)
useAccessTokenHint.onChange(
ACCESS_TOKEN_REGEXP.test(value) ? '' : 'Gitako does not recognize the token.',
)
},
@ -42,11 +42,11 @@ export function AccessTokenSettings(props: React.PropsWithChildren<Props>) {
}, [])
const saveToken = React.useCallback(
async (hint?: typeof useAccessTokenHint.val) => {
async (hint?: typeof useAccessTokenHint.value) => {
if (accessToken) {
configContext.set({ accessToken })
useAccessToken.set('')
useAccessTokenHint.set(
configContext.onChange({ accessToken })
useAccessToken.onChange('')
useAccessTokenHint.onChange(
hint || (
<span>
<a href="#" onClick={() => window.location.reload()}>
@ -79,7 +79,7 @@ export function AccessTokenSettings(props: React.PropsWithChildren<Props>) {
{hasAccessToken ? (
<div>
<Text as="p">Your token has been saved.</Text>
<Button onClick={() => configContext.set({ accessToken: '' })}>Clear</Button>
<Button onClick={() => configContext.onChange({ accessToken: '' })}>Clear</Button>
</div>
) : (
<div>
@ -110,8 +110,8 @@ export function AccessTokenSettings(props: React.PropsWithChildren<Props>) {
className={'access-token-input'}
value={accessToken}
placeholder="Or input here manually"
onFocus={() => focusInput.set(true)}
onBlur={() => focusInput.set(false)}
onFocus={() => focusInput.onChange(true)}
onBlur={() => focusInput.onChange(false)}
onChange={onInputAccessToken}
onKeyPress={onPressAccessToken}
/>
@ -121,7 +121,7 @@ export function AccessTokenSettings(props: React.PropsWithChildren<Props>) {
</div>
</div>
)}
{accessTokenHint && !focusInput.val && <span className={'hint'}>{accessTokenHint}</span>}
{accessTokenHint && !focusInput.value && <span className={'hint'}>{accessTokenHint}</span>}
</SettingsSection>
)
}

View file

@ -49,11 +49,11 @@ export function FileTreeSettings(props: React.PropsWithChildren<Props>) {
id="recursive-toggle-folder"
options={recursiveToggleFolderOptions}
onChange={v => {
configContext.set({
configContext.onChange({
recursiveToggleFolder: v,
})
}}
value={configContext.val.recursiveToggleFolder}
value={configContext.value.recursiveToggleFolder}
></SelectInput>
</Field>
<Field title="Icons" id="file-tree-icons">
@ -61,11 +61,11 @@ export function FileTreeSettings(props: React.PropsWithChildren<Props>) {
id="file-tree-icons"
options={iconOptions}
onChange={v => {
configContext.set({
configContext.onChange({
icons: v,
})
}}
value={configContext.val.icons}
value={configContext.value.icons}
/>
</Field>
<SimpleToggleField

View file

@ -4,7 +4,7 @@ import { VERSION } from 'env'
import { platform } from 'platforms'
import { GitHub } from 'platforms/GitHub'
import * as React from 'react'
import { useStates } from 'utils/hooks/useStates'
import { useStateIO } from 'utils/hooks/useStateIO'
import { SimpleField, SimpleToggleField } from '../SimpleToggleField'
import { AccessTokenSettings } from './AccessTokenSettings'
import { FileTreeSettings } from './FileTreeSettings'
@ -27,8 +27,8 @@ type Props = {
}
function SettingsBarContent() {
const useReloadHint = useStates<React.ReactNode>('')
const { val: reloadHint } = useReloadHint
const useReloadHint = useStateIO<React.ReactNode>('')
const { value: reloadHint } = useReloadHint
const moreFields: SimpleField[] =
platform === GitHub

View file

@ -5,7 +5,7 @@ import { useConfigs } from 'containers/ConfigsContext'
import * as React from 'react'
import { Config } from 'utils/configHelper'
import { friendlyFormatShortcut } from 'utils/general'
import { useStates } from 'utils/hooks/useStates'
import { useStateIO } from 'utils/hooks/useStateIO'
import * as keyHelper from 'utils/keyHelper'
import { Field } from './Field'
import { SettingsSection } from './SettingsSection'
@ -27,13 +27,13 @@ const toggleButtonContentOptions: Option<Config['toggleButtonContent']>[] = [
export function SidebarSettings(props: React.PropsWithChildren<Props>) {
const configContext = useConfigs()
const useToggleShowSideBarShortcut = useStates(configContext.val.shortcut)
const { val: toggleShowSideBarShortcut } = useToggleShowSideBarShortcut
const focused = useStates(false)
const useToggleShowSideBarShortcut = useStateIO(configContext.value.shortcut)
const { value: toggleShowSideBarShortcut } = useToggleShowSideBarShortcut
const focused = useStateIO(false)
React.useEffect(() => {
useToggleShowSideBarShortcut.set(configContext.val.shortcut)
}, [configContext.val.shortcut])
useToggleShowSideBarShortcut.onChange(configContext.value.shortcut)
}, [configContext.value.shortcut])
return (
<SettingsSection title={'Sidebar'}>
@ -43,24 +43,24 @@ export function SidebarSettings(props: React.PropsWithChildren<Props>) {
id="toggle-sidebar-shortcut"
marginRight={1}
className={'toggle-shortcut-input'}
onFocus={() => focused.set(true)}
onBlur={() => focused.set(false)}
placeholder={focused.val ? 'Press key combination' : 'Click here to set'}
onFocus={() => focused.onChange(true)}
onBlur={() => focused.onChange(false)}
placeholder={focused.value ? 'Press key combination' : 'Click here to set'}
value={friendlyFormatShortcut(toggleShowSideBarShortcut)}
onKeyDown={React.useCallback((e: React.KeyboardEvent<HTMLInputElement>) => {
e.preventDefault()
e.stopPropagation()
// Clear shortcut with backspace
const shortcut = e.key === 'Backspace' ? '' : keyHelper.parseEvent(e)
useToggleShowSideBarShortcut.set(shortcut)
useToggleShowSideBarShortcut.onChange(shortcut)
}, [])}
readOnly
/>
{configContext.val.shortcut === toggleShowSideBarShortcut ? (
{configContext.value.shortcut === toggleShowSideBarShortcut ? (
<Button
disabled={!configContext.val.shortcut}
disabled={!configContext.value.shortcut}
onClick={() => {
configContext.set({ shortcut: '' })
configContext.onChange({ shortcut: '' })
}}
>
Clear
@ -68,9 +68,9 @@ export function SidebarSettings(props: React.PropsWithChildren<Props>) {
) : (
<Button
onClick={() => {
const { val: toggleShowSideBarShortcut } = useToggleShowSideBarShortcut
const { value: toggleShowSideBarShortcut } = useToggleShowSideBarShortcut
if (typeof toggleShowSideBarShortcut !== 'string') return
configContext.set({ shortcut: toggleShowSideBarShortcut })
configContext.onChange({ shortcut: toggleShowSideBarShortcut })
}}
>
Save
@ -83,11 +83,11 @@ export function SidebarSettings(props: React.PropsWithChildren<Props>) {
id="toggle-button-content"
options={toggleButtonContentOptions}
onChange={v => {
configContext.set({
configContext.onChange({
toggleButtonContent: v,
})
}}
value={configContext.val.toggleButtonContent}
value={configContext.value.toggleButtonContent}
></SelectInput>
</Field>
<SimpleToggleField

View file

@ -4,12 +4,7 @@ import { Config } from 'utils/configHelper'
type Props = {}
type PartialValSet<T> = {
val: T
set: (val: Partial<T>) => void
}
type ContextShape = PartialValSet<Config>
type ContextShape = IO<Config, Partial<Config>>
export type ConfigsContextShape = ContextShape
export const ConfigsContext = React.createContext<ContextShape | null>(null)
@ -19,7 +14,7 @@ export function ConfigsContextWrapper(props: React.PropsWithChildren<Props>) {
React.useEffect(() => {
configsHelper.get().then(setConfigs)
}, [])
const set = React.useCallback(
const onChange = React.useCallback(
(updatedConfigs: Partial<Config>) => {
const mergedConfigs = { ...configs, ...updatedConfigs } as Config
configsHelper.set(mergedConfigs)
@ -29,7 +24,7 @@ export function ConfigsContextWrapper(props: React.PropsWithChildren<Props>) {
)
if (configs === null) return null
return (
<ConfigsContext.Provider value={{ val: configs, set }}>
<ConfigsContext.Provider value={{ value: configs, onChange: onChange }}>
{props.children}
</ConfigsContext.Provider>
)
@ -37,10 +32,10 @@ export function ConfigsContextWrapper(props: React.PropsWithChildren<Props>) {
export const useConfigs = useNonNullContext(ConfigsContext)
function useNonNullContext<T, R extends Exclude<T, null>>(theContext: React.Context<T>): () => R {
function useNonNullContext<T>(theContext: React.Context<T | null>): () => T {
return () => {
const context = React.useContext(theContext)
if (context === null) throw new Error(`Empty context`)
return context as R
return context
}
}

View file

@ -57,7 +57,7 @@ export const init: BoundMethodCreator = dispatch => async () => {
const {
props: { configContext },
} = dispatch.get()
const { accessToken } = configContext.val
const { accessToken } = configContext.value
const guessDefaultBranch = 'master' // when to switch to 'main'?
let getTreeData = platform.getTreeData(
@ -139,7 +139,7 @@ export const handleError: BoundMethodCreator<[Error]> = dispatch => async err =>
dispatch.set({ errorDueToAuth: true })
} else if (err.message === errors.CONNECTION_BLOCKED) {
const { props } = dispatch.get()
if (props.configContext.val.accessToken) {
if (props.configContext.value.accessToken) {
dispatch.call(setError, `Cannot connect to ${platformName}.`)
} else {
dispatch.set({ errorDueToAuth: true })
@ -161,10 +161,10 @@ export const toggleShowSideBar: BoundMethodCreator = dispatch => () => {
dispatch.call(setShouldShow, !shouldShow)
const {
val: { intelligentToggle },
value: { intelligentToggle },
} = configContext
if (intelligentToggle !== null) {
configContext.set({ intelligentToggle: !shouldShow })
configContext.onChange({ intelligentToggle: !shouldShow })
}
}

4
src/global.d.ts vendored
View file

@ -18,9 +18,9 @@ type TreeNode = {
accessDenied?: boolean
}
type IO<T> = {
type IO<T, ChangeT = T> = {
value: T
onChange(value: T): void
onChange(value: ChangeT): void
}
type Override<Original, Incoming> = Omit<Original, keyof Incoming> & Incoming

View file

@ -1,5 +1,5 @@
import * as React from 'react'
import { useStates } from './useStates'
import { useStateIO } from './useStateIO'
export function useAsyncMemo<T, D extends any[] | readonly any[]>(
factory: (dependencies: D) => T | Promise<T>,
@ -7,10 +7,10 @@ export function useAsyncMemo<T, D extends any[] | readonly any[]>(
initialValue: T,
): T {
const firstTime = React.useRef(true)
const state = useStates<T>(() => initialValue)
const state = useStateIO<T>(() => initialValue)
React.useEffect(() => {
if (firstTime.current) firstTime.current = false
Promise.resolve(factory(deps)).then(consumed => state.set(() => consumed))
Promise.resolve(factory(deps)).then(consumed => state.onChange(() => consumed))
}, deps)
return state.val
return state.value
}

View file

@ -0,0 +1,11 @@
import * as React from 'react'
export function useStateIO<S>(
initialState: S | (() => S),
): {
value: S
onChange: React.Dispatch<React.SetStateAction<S>>
} {
const [value, onChange] = React.useState(initialState)
return { value, onChange }
}

View file

@ -1,11 +0,0 @@
import * as React from 'react'
export function useStates<S>(
initialState: S | (() => S),
): {
val: S
set: React.Dispatch<React.SetStateAction<S>>
} {
const [val, set] = React.useState(initialState)
return { val, set }
}