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

View file

@ -1,6 +1,8 @@
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
let [
,

View file

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

View file

@ -82,15 +82,23 @@ export const Gitea: Platform = {
return null
}
let detectedBranchName
let branchName
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 = {
...URLHelper.parse(),
branchName: detectedBranchName,
} as MetaData
userName,
repoName,
type,
branchName,
}
return metaData
},
async getMetaData(partialMetaData, accessToken) {

View file

@ -78,17 +78,24 @@ export const Gitee: Platform = {
return null
}
let detectedBranchName
let branchName
if (DOMHelper.isInCodePage()) {
// not working well with non-branch blob
// 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 = {
...URLHelper.parse(),
branchName: detectedBranchName,
} as MetaData
userName,
repoName,
type,
branchName,
}
return metaData
},
async getMetaData(partialMetaData, accessToken) {

View file

@ -187,3 +187,19 @@ export function withEffect<Method extends (...args: any[]) => any>(
export function run<T>(fn: () => T) {
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!
},
}
}