refactor: simplify init logics

This commit is contained in:
EnixCoda 2021-01-31 23:58:07 +08:00
parent 2a9ab02fda
commit f3f86a79ea
No known key found for this signature in database
GPG key ID: 0C1A07377913A1DD
6 changed files with 120 additions and 96 deletions

View file

@ -2,6 +2,7 @@ import { ConfigsContextShape } from 'containers/ConfigsContext'
import { GetCreatedMethod, MethodCreator } from 'driver/connect' import { GetCreatedMethod, MethodCreator } from 'driver/connect'
import { errors, platform, platformName } from 'platforms' import { errors, platform, platformName } from 'platforms'
import * as DOMHelper from 'utils/DOMHelper' import * as DOMHelper from 'utils/DOMHelper'
import { createPromiseQueue } from 'utils/general'
export type Props = { export type Props = {
configContext: ConfigsContextShape configContext: ConfigsContextShape
@ -26,7 +27,6 @@ export type ConnectorState = {
initializingPromise: Promise<void> | null initializingPromise: Promise<void> | null
} & { } & {
init: GetCreatedMethod<typeof init> init: GetCreatedMethod<typeof init>
setMetaData: GetCreatedMethod<typeof setMetaData>
setShouldShow: GetCreatedMethod<typeof setShouldShow> setShouldShow: GetCreatedMethod<typeof setShouldShow>
toggleShowSideBar: GetCreatedMethod<typeof toggleShowSideBar> toggleShowSideBar: GetCreatedMethod<typeof toggleShowSideBar>
toggleShowSettings: GetCreatedMethod<typeof toggleShowSettings> toggleShowSettings: GetCreatedMethod<typeof toggleShowSettings>
@ -34,18 +34,10 @@ export type ConnectorState = {
type BoundMethodCreator<Args extends any[] = []> = MethodCreator<Props, ConnectorState, Args> type BoundMethodCreator<Args extends any[] = []> = MethodCreator<Props, ConnectorState, Args>
export const init: BoundMethodCreator = dispatch => async () => { const promiseQueue = createPromiseQueue()
const {
state: { initializingPromise },
} = dispatch.get()
if (initializingPromise) await initializingPromise
let done: any = null // cannot use type `(() => void) | null` here export const init: BoundMethodCreator = dispatch => async () => {
dispatch.set({ const leave = await promiseQueue.enter()
initializingPromise: new Promise(resolve => {
done = () => resolve()
}),
})
try { try {
const metaData = platform.resolveMeta() const metaData = platform.resolveMeta()
@ -53,90 +45,85 @@ export const init: BoundMethodCreator = dispatch => async () => {
dispatch.set({ disabled: true }) dispatch.set({ disabled: true })
return return
} }
const { userName, repoName, branchName } = metaData
DOMHelper.markGitakoReadyState(true) DOMHelper.markGitakoReadyState(true)
dispatch.set({ dispatch.set({
errorDueToAuth: false, errorDueToAuth: false,
showSettings: false, showSettings: false,
logoContainerElement: DOMHelper.insertLogoMountPoint(), logoContainerElement: DOMHelper.insertLogoMountPoint(),
}) })
dispatch.call(setMetaData, metaData)
const { const {
props: { configContext }, props: { configContext },
} = dispatch.get() } = dispatch.get()
const { accessToken } = configContext.val const { accessToken } = configContext.val
if (!metaData.userName || !metaData.repoName) return const guessDefaultBranch = 'master' // when to switch to 'main'?
const guessDefaultBranch = 'master' let getTreeData = platform.getTreeData(
const getTreeDataAggressively = platform.getTreeData(
{ {
branchName: metaData.branchName || guessDefaultBranch, branchName: branchName || guessDefaultBranch,
userName: metaData.userName, userName,
repoName: metaData.repoName, repoName,
}, },
'/', '/',
true, true,
accessToken, accessToken,
) )
const caughtAggressiveError = getTreeDataAggressively?.catch(error => { getTreeData.catch(error => error) // catch it early to prevent the error being raised higher
// 1. the repo has no master branch
// 2. detect branch name from DOM failed const metaDataFromAPI = await platform.getMetaData({ userName, repoName }, accessToken)
// 3. not very possible...
// not handle this error immediately if (branchName) {
return error const safeMetaData = {
}) ...metaDataFromAPI,
let getTreeData = getTreeDataAggressively userName,
const metaDataFromAPI = await platform.getMetaData( repoName,
{ branchName,
userName: metaData.userName, }
repoName: metaData.repoName, dispatch.set({ metaData: safeMetaData })
}, getTreeData.catch(error => {
accessToken, dispatch.call(handleError, error)
) })
const projectDefaultBranchName = metaDataFromAPI?.defaultBranchName
const detectedBranchName = metaData.branchName
if (
!detectedBranchName &&
projectDefaultBranchName &&
projectDefaultBranchName !== metaData.branchName &&
metaData.type !== 'pull'
) {
// Accessing repository's non-homepage(no branch name in URL, nor in DOM)
// We predicted its default branch to be 'master' and sent aggressive request
// Throw that request due to the repo do not use {defaultBranchName} as default branch
metaData.branchName = projectDefaultBranchName
getTreeData = platform.getTreeData(
{
branchName: metaData.branchName,
userName: metaData.userName,
repoName: metaData.repoName,
},
'/',
true,
accessToken,
)
} else { } else {
caughtAggressiveError.then(error => { const { defaultBranchName } = metaDataFromAPI
// aggressive requested correct branch but ends in failure (e.g. project is empty)
if (error instanceof Error) { if (!defaultBranchName) {
dispatch.call(handleError, error) throw new Error(`Failed resolving default branch name`)
} }
})
const safeMetaData = {
...metaDataFromAPI,
userName,
repoName,
branchName: defaultBranchName,
}
dispatch.set({ metaData: safeMetaData })
if (defaultBranchName !== guessDefaultBranch && metaData.type !== 'pull') {
// Accessing repository's non-homepage(no branch name in URL, nor in DOM)
// We predicted its default branch to be 'master' and sent aggressive request
// Throw that request due to the repo do not use {defaultBranchName} as default branch
getTreeData = platform.getTreeData(
{
branchName: defaultBranchName,
userName,
repoName,
},
'/',
true,
accessToken,
)
}
} }
getTreeData
.then(async ({ root: treeData, defer }) => { const { root: treeData, defer } = await getTreeData
if (treeData) { dispatch.set({ treeData, defer })
dispatch.set({ treeData, defer })
}
})
.catch(err => dispatch.call(handleError, err))
Object.assign(metaData, metaDataFromAPI)
dispatch.call(setMetaData, metaData)
} catch (err) { } catch (err) {
dispatch.call(handleError, err) dispatch.call(handleError, err)
} finally {
if (done) done()
} }
leave()
} }
export const handleError: BoundMethodCreator<[Error]> = dispatch => async err => { export const handleError: BoundMethodCreator<[Error]> = dispatch => async err => {
@ -197,6 +184,3 @@ export const toggleShowSettings: BoundMethodCreator = dispatch => () =>
dispatch.set(({ showSettings }) => ({ dispatch.set(({ showSettings }) => ({
showSettings: !showSettings, showSettings: !showSettings,
})) }))
export const setMetaData: BoundMethodCreator<[ConnectorState['metaData']]> = dispatch => metaData =>
dispatch.set({ metaData })

View file

@ -1,6 +1,8 @@
import { raiseError } from 'analytics' import { raiseError } from 'analytics'
export function parse(): Pick<MetaData, 'userName' | 'repoName' | 'type'> & { path: string[] } { export function parse(): Partial<Pick<MetaData, 'userName' | 'repoName' | 'type'>> & {
path: string[]
} {
const { pathname } = window.location const { pathname } = window.location
let [ let [
, ,

View file

@ -101,10 +101,17 @@ export const GitHub: Platform = {
branchName = DOMHelper.getCurrentBranch() || URLHelper.parseSHA() branchName = DOMHelper.getCurrentBranch() || URLHelper.parseSHA()
} }
const { userName, repoName, type } = URLHelper.parse()
if (!userName || !repoName) {
return null
}
const metaData = { const metaData = {
...URLHelper.parse(), userName,
repoName,
type,
branchName, branchName,
} as MetaData }
return metaData return metaData
}, },
async getMetaData({ userName, repoName }, accessToken) { async getMetaData({ userName, repoName }, accessToken) {
@ -202,14 +209,14 @@ export const GitHub: Platform = {
})), })),
) )
const gitModules = root.contents?.find(item => item.name === '.gitmodules') const gitModules = root.contents?.find(
if (gitModules) { item => item.type === 'blob' && item.name === '.gitmodules',
if (userName && repoName && gitModules.sha) { )
const blobData = await API.getBlobData(userName, repoName, gitModules.sha, accessToken) if (gitModules?.sha) {
const blobData = await API.getBlobData(userName, repoName, gitModules.sha, accessToken)
if (blobData && blobData.encoding === 'base64' && blobData.content) { if (blobData && blobData.encoding === 'base64' && blobData.content) {
await resolveGitModules(root, Base64.decode(blobData.content)) await resolveGitModules(root, Base64.decode(blobData.content))
}
} }
} }

View file

@ -82,15 +82,23 @@ export const Gitea: Platform = {
return null return null
} }
let detectedBranchName let branchName
if (DOMHelper.isInCodePage()) { if (DOMHelper.isInCodePage()) {
detectedBranchName = DOMHelper.getCurrentBranch() || URLHelper.parseSHA() branchName = DOMHelper.getCurrentBranch() || URLHelper.parseSHA()
}
const { userName, repoName, type } = URLHelper.parse()
if (!userName || !repoName) {
return null
} }
const metaData = { const metaData = {
...URLHelper.parse(), userName,
branchName: detectedBranchName, repoName,
} as MetaData type,
branchName,
}
return metaData return metaData
}, },
async getMetaData(partialMetaData, accessToken) { async getMetaData(partialMetaData, accessToken) {

View file

@ -78,17 +78,24 @@ export const Gitee: Platform = {
return null return null
} }
let detectedBranchName let branchName
if (DOMHelper.isInCodePage()) { if (DOMHelper.isInCodePage()) {
// not working well with non-branch blob // not working well with non-branch blob
// cannot handle '/' split branch name, should not use when possibly on branch page // cannot handle '/' split branch name, should not use when possibly on branch page
detectedBranchName = DOMHelper.getCurrentBranch() || URLHelper.parseSHA() branchName = DOMHelper.getCurrentBranch() || URLHelper.parseSHA()
}
const { userName, repoName, type } = URLHelper.parse()
if (!userName || !repoName) {
return null
} }
const metaData = { const metaData = {
...URLHelper.parse(), userName,
branchName: detectedBranchName, repoName,
} as MetaData type,
branchName,
}
return metaData return metaData
}, },
async getMetaData(partialMetaData, accessToken) { async getMetaData(partialMetaData, accessToken) {

View file

@ -187,3 +187,19 @@ export function withEffect<Method extends (...args: any[]) => any>(
export function run<T>(fn: () => T) { export function run<T>(fn: () => T) {
return fn() return fn()
} }
export function createPromiseQueue() {
let promise: Promise<void>
return {
async enter() {
let leave: () => void
const current = new Promise<void>(resolve => (leave = () => resolve()))
const lastPromise = promise
promise = current!
if (lastPromise) await lastPromise
return leave!
},
}
}