mirror of
https://github.com/EnixCoda/Gitako.git
synced 2026-03-11 08:54:44 +00:00
refactor: catch network errors
This commit is contained in:
parent
72f825db2e
commit
3cfc5bc166
7 changed files with 127 additions and 87 deletions
12
src/components/ErrorContext.tsx
Normal file
12
src/components/ErrorContext.tsx
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
import * as React from 'react'
|
||||
import { useStateIO } from 'utils/hooks/useStateIO'
|
||||
|
||||
export type SideBarErrorContextShape = IO<string | null>
|
||||
|
||||
export const SideBarErrorContext = React.createContext<SideBarErrorContextShape | null>(null)
|
||||
|
||||
export function StateBarErrorContextWrapper({ children }: React.PropsWithChildren<{}>) {
|
||||
const $error = useStateIO<string | null>(null)
|
||||
|
||||
return <SideBarErrorContext.Provider value={$error}>{children}</SideBarErrorContext.Provider>
|
||||
}
|
||||
|
|
@ -4,6 +4,7 @@ import { ConfigsContextWrapper, useConfigs } from 'containers/ConfigsContext'
|
|||
import * as React from 'react'
|
||||
import { useLoadedContext } from 'utils/hooks/useLoadedContext'
|
||||
import { ErrorBoundary } from './ErrorBoundary'
|
||||
import { StateBarErrorContextWrapper } from './ErrorContext'
|
||||
import { RepoContext, RepoContextWrapper } from './RepoContext'
|
||||
import { SideBarStateContext, StateBarStateContextWrapper } from './SideBarState'
|
||||
|
||||
|
|
@ -12,17 +13,19 @@ export function Gitako() {
|
|||
<ErrorBoundary>
|
||||
<ConfigsContextWrapper>
|
||||
<StateBarStateContextWrapper>
|
||||
<RepoContextWrapper>
|
||||
<IIFC>
|
||||
{() => (
|
||||
<SideBar
|
||||
configContext={useConfigs()}
|
||||
stateContext={useLoadedContext(SideBarStateContext)}
|
||||
metaData={React.useContext(RepoContext)}
|
||||
/>
|
||||
)}
|
||||
</IIFC>
|
||||
</RepoContextWrapper>
|
||||
<StateBarErrorContextWrapper>
|
||||
<RepoContextWrapper>
|
||||
<IIFC>
|
||||
{() => (
|
||||
<SideBar
|
||||
configContext={useConfigs()}
|
||||
stateContext={useLoadedContext(SideBarStateContext)}
|
||||
metaData={React.useContext(RepoContext)}
|
||||
/>
|
||||
)}
|
||||
</IIFC>
|
||||
</RepoContextWrapper>
|
||||
</StateBarErrorContextWrapper>
|
||||
</StateBarStateContextWrapper>
|
||||
</ConfigsContextWrapper>
|
||||
</ErrorBoundary>
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
import { useConfigs } from 'containers/ConfigsContext'
|
||||
import { platform } from 'platforms'
|
||||
import * as React from 'react'
|
||||
import { run } from 'utils/general'
|
||||
import { useEffectOnSerializableUpdates } from 'utils/hooks/useEffectOnSerializableUpdates'
|
||||
import { useLoadedContext } from 'utils/hooks/useLoadedContext'
|
||||
import { useOnPJAXDone } from 'utils/hooks/usePJAX'
|
||||
import { useStateIO } from 'utils/hooks/useStateIO'
|
||||
import { useCatchNetworkError } from '../utils/hooks/useCatchNetworkError'
|
||||
import { SideBarStateContext } from './SideBarState'
|
||||
|
||||
export const RepoContext = React.createContext<MetaData | null>(null)
|
||||
|
|
@ -55,8 +55,9 @@ function useBranchName(): MetaData['branchName'] | null {
|
|||
function useDefaultBranch(partialMetaData: PartialMetaData | null) {
|
||||
const { accessToken } = useConfigs().value
|
||||
const $defaultBranch = useStateIO<string | null>(null)
|
||||
const catchNetworkError = useCatchNetworkError()
|
||||
React.useEffect(() => {
|
||||
run(async () => {
|
||||
catchNetworkError(async () => {
|
||||
if (!partialMetaData) return
|
||||
|
||||
const defaultBranch = await platform.getDefaultBranchName(partialMetaData, accessToken)
|
||||
|
|
|
|||
|
|
@ -15,12 +15,14 @@ import * as React from 'react'
|
|||
import { cx } from 'utils/cx'
|
||||
import * as DOMHelper from 'utils/DOMHelper'
|
||||
import { parseURLSearch, run } from 'utils/general'
|
||||
import { useCatchNetworkError } from 'utils/hooks/useCatchNetworkError'
|
||||
import { useLoadedContext } from 'utils/hooks/useLoadedContext'
|
||||
import { loadWithPJAX, useOnPJAXDone, usePJAX } from 'utils/hooks/usePJAX'
|
||||
import { useProgressBar } from 'utils/hooks/useProgressBar'
|
||||
import { useStateIO } from 'utils/hooks/useStateIO'
|
||||
import * as keyHelper from 'utils/keyHelper'
|
||||
import { Icon } from './Icon'
|
||||
import { IIFC } from './IIFC'
|
||||
import { LoadingIndicator } from './LoadingIndicator'
|
||||
import { SideBarStateContext } from './SideBarState'
|
||||
import { Theme } from './Theme'
|
||||
|
|
@ -142,13 +144,18 @@ const RawSideBar: React.FC<Props & ConnectorState> = function RawGitako(props) {
|
|||
<div className={'header'}>
|
||||
<MetaBar metaData={metaData} />
|
||||
</div>
|
||||
<FileExplorer
|
||||
metaData={metaData}
|
||||
freeze={showSettings}
|
||||
accessToken={accessToken}
|
||||
loadWithPJAX={loadWithPJAX}
|
||||
config={configContext.value}
|
||||
/>
|
||||
<IIFC>
|
||||
{() => (
|
||||
<FileExplorer
|
||||
metaData={metaData}
|
||||
freeze={showSettings}
|
||||
accessToken={accessToken}
|
||||
loadWithPJAX={loadWithPJAX}
|
||||
config={configContext.value}
|
||||
catchNetworkErrors={useCatchNetworkError()}
|
||||
/>
|
||||
)}
|
||||
</IIFC>
|
||||
</>
|
||||
) : null
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ export type Props = {
|
|||
accessToken: string | undefined
|
||||
config: Config
|
||||
loadWithPJAX(url: string): void
|
||||
catchNetworkErrors: <T>(fn: () => T) => T | undefined
|
||||
}
|
||||
|
||||
export type ConnectorState = {
|
||||
|
|
@ -47,48 +48,54 @@ export const setUpTree: BoundMethodCreator<
|
|||
config: Pick<Config, 'compressSingletonFolder' | 'accessToken'>
|
||||
},
|
||||
]
|
||||
> = dispatch => async ({ stateContext, metaData, config }) => {
|
||||
const { userName, repoName, branchName } = metaData
|
||||
> = dispatch => ({ stateContext, metaData, config }) => {
|
||||
const {
|
||||
props: { catchNetworkErrors },
|
||||
} = dispatch.get()
|
||||
|
||||
stateContext.onChange('tree-loading')
|
||||
const { root: treeRoot, defer = false } = await platform.getTreeData(
|
||||
{
|
||||
branchName: branchName,
|
||||
userName,
|
||||
repoName,
|
||||
},
|
||||
'/',
|
||||
true,
|
||||
config.accessToken,
|
||||
)
|
||||
catchNetworkErrors(async () => {
|
||||
const { userName, repoName, branchName } = metaData
|
||||
|
||||
stateContext.onChange('tree-rendering')
|
||||
dispatch.set({ defer })
|
||||
stateContext.onChange('tree-loading')
|
||||
const { root: treeRoot, defer = false } = await platform.getTreeData(
|
||||
{
|
||||
branchName: branchName,
|
||||
userName,
|
||||
repoName,
|
||||
},
|
||||
'/',
|
||||
true,
|
||||
config.accessToken,
|
||||
)
|
||||
|
||||
const visibleNodesGenerator = new VisibleNodesGenerator({
|
||||
root: treeRoot,
|
||||
compress: config.compressSingletonFolder,
|
||||
async getTreeData(path) {
|
||||
const { root } = await platform.getTreeData(metaData, path, false, config.accessToken)
|
||||
return root
|
||||
},
|
||||
})
|
||||
dispatch.set({ visibleNodesGenerator })
|
||||
visibleNodesGenerator.onUpdate(visibleNodes => dispatch.set({ visibleNodes }))
|
||||
stateContext.onChange('tree-rendering')
|
||||
dispatch.set({ defer })
|
||||
|
||||
if (platform.shouldExpandAll?.()) {
|
||||
const unsubscribe = visibleNodesGenerator.onUpdate(visibleNodes => {
|
||||
unsubscribe()
|
||||
visibleNodes.nodes.forEach(node =>
|
||||
dispatch.call(toggleNodeExpansion, node, { recursive: true }),
|
||||
)
|
||||
const visibleNodesGenerator = new VisibleNodesGenerator({
|
||||
root: treeRoot,
|
||||
compress: config.compressSingletonFolder,
|
||||
async getTreeData(path) {
|
||||
const { root } = await platform.getTreeData(metaData, path, false, config.accessToken)
|
||||
return root
|
||||
},
|
||||
})
|
||||
} else {
|
||||
const targetPath = platform.getCurrentPath(metaData.branchName)
|
||||
if (targetPath) dispatch.call(goTo, targetPath)
|
||||
}
|
||||
dispatch.set({ visibleNodesGenerator })
|
||||
visibleNodesGenerator.onUpdate(visibleNodes => dispatch.set({ visibleNodes }))
|
||||
|
||||
stateContext.onChange('tree-rendered')
|
||||
if (platform.shouldExpandAll?.()) {
|
||||
const unsubscribe = visibleNodesGenerator.onUpdate(visibleNodes => {
|
||||
unsubscribe()
|
||||
visibleNodes.nodes.forEach(node =>
|
||||
dispatch.call(toggleNodeExpansion, node, { recursive: true }),
|
||||
)
|
||||
})
|
||||
} else {
|
||||
const targetPath = platform.getCurrentPath(metaData.branchName)
|
||||
if (targetPath) dispatch.call(goTo, targetPath)
|
||||
}
|
||||
|
||||
stateContext.onChange('tree-rendered')
|
||||
})
|
||||
}
|
||||
|
||||
export const handleKeyDown: BoundMethodCreator<[React.KeyboardEvent]> = dispatch => event => {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
import { SideBarStateContextShape } from 'components/SideBarState'
|
||||
import { ConfigsContextShape } from 'containers/ConfigsContext'
|
||||
import { GetCreatedMethod, MethodCreator } from 'driver/connect'
|
||||
import { errors, platformName } from 'platforms'
|
||||
import * as DOMHelper from 'utils/DOMHelper'
|
||||
|
||||
export type Props = {
|
||||
|
|
@ -23,36 +22,6 @@ export type ConnectorState = {
|
|||
|
||||
type BoundMethodCreator<Args extends any[] = []> = MethodCreator<Props, ConnectorState, Args>
|
||||
|
||||
export const handleError: BoundMethodCreator<[Error]> = dispatch => async err => {
|
||||
const {
|
||||
props: { stateContext },
|
||||
} = dispatch.get()
|
||||
if (err.message === errors.EMPTY_PROJECT) {
|
||||
dispatch.call(setError, 'This project seems to be empty.')
|
||||
} else if (err.message === errors.BLOCKED_PROJECT) {
|
||||
dispatch.call(setError, 'Access to the project is blocked.')
|
||||
} else if (
|
||||
err.message === errors.NOT_FOUND ||
|
||||
err.message === errors.BAD_CREDENTIALS ||
|
||||
err.message === errors.API_RATE_LIMIT
|
||||
) {
|
||||
stateContext.onChange('error-due-to-auth')
|
||||
} else if (err.message === errors.CONNECTION_BLOCKED) {
|
||||
const { props } = dispatch.get()
|
||||
if (props.configContext.value.accessToken) {
|
||||
dispatch.call(setError, `Cannot connect to ${platformName}.`)
|
||||
} else {
|
||||
stateContext.onChange('error-due-to-auth')
|
||||
}
|
||||
} else if (err.message === errors.SERVER_FAULT) {
|
||||
dispatch.call(setError, `${platformName} server went down.`)
|
||||
} else {
|
||||
DOMHelper.markGitakoReadyState(false)
|
||||
dispatch.call(setError, 'Some thing went wrong.')
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
export const toggleShowSideBar: BoundMethodCreator = dispatch => () => {
|
||||
const {
|
||||
state: { shouldShow },
|
||||
|
|
|
|||
41
src/utils/hooks/useCatchNetworkError.tsx
Normal file
41
src/utils/hooks/useCatchNetworkError.tsx
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
import { useConfigs } from 'containers/ConfigsContext'
|
||||
import { errors, platformName } from 'platforms'
|
||||
import { useLoadedContext } from 'utils/hooks/useLoadedContext'
|
||||
import { SideBarErrorContext } from '../../components/ErrorContext'
|
||||
import { SideBarStateContext } from '../../components/SideBarState'
|
||||
|
||||
export function useCatchNetworkError() {
|
||||
const { accessToken } = useConfigs().value
|
||||
const stateContext = useLoadedContext(SideBarStateContext)
|
||||
const errorContext = useLoadedContext(SideBarErrorContext)
|
||||
|
||||
return function <T>(fn: () => T) {
|
||||
try {
|
||||
return fn()
|
||||
} catch (err) {
|
||||
if (err.message === errors.EMPTY_PROJECT) {
|
||||
errorContext.onChange('This project seems to be empty.')
|
||||
} else if (err.message === errors.BLOCKED_PROJECT) {
|
||||
errorContext.onChange('Access to the project is blocked.')
|
||||
} else if (
|
||||
err.message === errors.NOT_FOUND ||
|
||||
err.message === errors.BAD_CREDENTIALS ||
|
||||
err.message === errors.API_RATE_LIMIT
|
||||
) {
|
||||
stateContext.onChange('error-due-to-auth')
|
||||
} else if (err.message === errors.CONNECTION_BLOCKED) {
|
||||
if (accessToken) {
|
||||
errorContext.onChange(`Cannot connect to ${platformName}.`)
|
||||
} else {
|
||||
stateContext.onChange('error-due-to-auth')
|
||||
}
|
||||
} else if (err.message === errors.SERVER_FAULT) {
|
||||
errorContext.onChange(`${platformName} server went down.`)
|
||||
} else {
|
||||
stateContext.onChange('disabled')
|
||||
errorContext.onChange('Some thing went wrong.')
|
||||
throw err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue