mirror of
https://github.com/EnixCoda/Gitako.git
synced 2026-03-11 08:54:44 +00:00
refactor: transform sidebar and more
This commit is contained in:
parent
29ac1d88b9
commit
8b0f331f27
8 changed files with 252 additions and 217 deletions
|
|
@ -1,4 +1,5 @@
|
|||
import { version } from '../package.json'
|
||||
import { Middleware } from 'driver/connect.js'
|
||||
// TODO: set this through ENV or something else
|
||||
const LOG_ENDPOINT = 'https://enix.one/gitako/log'
|
||||
|
||||
|
|
@ -6,7 +7,7 @@ export function raiseError(error: Error) {
|
|||
return reportError(error)
|
||||
}
|
||||
|
||||
export function withErrorLog(method: Function, args: any[]) {
|
||||
export const withErrorLog: Middleware = function withErrorLog(method, args) {
|
||||
return [
|
||||
function() {
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -8,6 +8,6 @@ export default class Gitako extends React.PureComponent {
|
|||
}
|
||||
|
||||
render() {
|
||||
return React.createElement(SideBar as any)
|
||||
return <SideBar />
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,51 +9,16 @@ import Portal from 'components/Portal'
|
|||
import Resizable from 'components/Resizable'
|
||||
import cx from 'utils/cx'
|
||||
import VisibleNodesGenerator from 'utils/VisibleNodesGenerator'
|
||||
import { ConnectorState } from 'driver/core/SideBar'
|
||||
|
||||
export type TreeData = any
|
||||
export type MetaData = any
|
||||
export type VisibleNodes = VisibleNodesGenerator['visibleNodes']
|
||||
|
||||
type Props = {
|
||||
// initial width of side bar
|
||||
baseSize?: number
|
||||
// error message
|
||||
error?: string
|
||||
// whether Gitako side bar should be shown
|
||||
shouldShow?: boolean
|
||||
// whether show settings pane
|
||||
showSettings?: boolean
|
||||
// whether failed loading the repo due to it is private
|
||||
errorDueToAuth?: boolean
|
||||
// access token for GitHub
|
||||
accessToken?: string
|
||||
// the shortcut string for toggle sidebar
|
||||
toggleShowSideBarShortcut?: string
|
||||
// meta data for the repository
|
||||
metaData: MetaData
|
||||
// file tree data
|
||||
treeData: TreeData
|
||||
// few settings
|
||||
compressSingletonFolder?: boolean
|
||||
copyFileButton?: boolean
|
||||
copySnippetButton?: boolean
|
||||
logoContainerElement: HTMLElement | null
|
||||
export type Props = {}
|
||||
|
||||
init: () => void
|
||||
onPJAXEnd: () => void
|
||||
toggleShowSideBar: () => void
|
||||
toggleShowSettings: () => void
|
||||
onAccessTokenChange: SettingsBar['props']['onAccessTokenChange']
|
||||
onKeyDown: EventListener
|
||||
onShortcutChange: SettingsBar['props']['onShortcutChange']
|
||||
setCopyFile: SettingsBar['props']['setCopyFile']
|
||||
setCopySnippet: SettingsBar['props']['setCopySnippet']
|
||||
setCompressSingleton: SettingsBar['props']['setCompressSingleton']
|
||||
}
|
||||
|
||||
@(connect(SideBarCore) as any)
|
||||
export default class Gitako extends React.PureComponent<Props> {
|
||||
static defaultProps: Partial<Props> = {
|
||||
class Gitako extends React.PureComponent<Props & ConnectorState> {
|
||||
static defaultProps: Partial<Props & ConnectorState> = {
|
||||
baseSize: 260,
|
||||
shouldShow: false,
|
||||
showSettings: false,
|
||||
|
|
@ -105,7 +70,15 @@ export default class Gitako extends React.PureComponent<Props> {
|
|||
}
|
||||
|
||||
renderContent() {
|
||||
const { errorDueToAuth, metaData, treeData, showSettings, toggleShowSettings } = this.props
|
||||
const {
|
||||
errorDueToAuth,
|
||||
metaData,
|
||||
treeData,
|
||||
showSettings,
|
||||
toggleShowSettings,
|
||||
compressSingletonFolder,
|
||||
accessToken,
|
||||
} = this.props
|
||||
return (
|
||||
<div className={'gitako-side-bar-content'}>
|
||||
{metaData && <MetaBar metaData={metaData} />}
|
||||
|
|
@ -113,10 +86,12 @@ export default class Gitako extends React.PureComponent<Props> {
|
|||
? this.renderAccessDeniedError()
|
||||
: metaData && (
|
||||
<FileExplorer
|
||||
compressSingletonFolder={compressSingletonFolder}
|
||||
toggleShowSettings={toggleShowSettings}
|
||||
metaData={metaData}
|
||||
treeData={treeData}
|
||||
freeze={showSettings}
|
||||
accessToken={accessToken}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
|
@ -175,3 +150,5 @@ export default class Gitako extends React.PureComponent<Props> {
|
|||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default connect<Props, ConnectorState>(SideBarCore)(Gitako)
|
||||
|
|
|
|||
|
|
@ -1,172 +0,0 @@
|
|||
import DOMHelper, { REPO_TYPE_PRIVATE } from 'utils/DOMHelper'
|
||||
import GitHubHelper, {
|
||||
NOT_FOUND,
|
||||
BAD_CREDENTIALS,
|
||||
API_RATE_LIMIT,
|
||||
EMPTY_PROJECT,
|
||||
} from 'utils/GitHubHelper'
|
||||
import configHelper from 'utils/configHelper'
|
||||
import URLHelper from 'utils/URLHelper'
|
||||
import keyHelper from 'utils/keyHelper'
|
||||
|
||||
const init = dispatch => async () => {
|
||||
try {
|
||||
if (!URLHelper.isInRepoPage()) return
|
||||
dispatch({ logoContainerElement: DOMHelper.insertLogoMountPoint() })
|
||||
let detectedBranchName
|
||||
const metaData = URLHelper.parse()
|
||||
if (DOMHelper.isInCodePage()) {
|
||||
detectedBranchName = DOMHelper.getCurrentBranch() // not working well with non-branch blob
|
||||
|| URLHelper.parseSHA() // cannot handle '/' split branch name, should not use when possibly on branch page
|
||||
}
|
||||
metaData.branchName = detectedBranchName || 'master'
|
||||
dispatch(setMetaData, metaData)
|
||||
const {
|
||||
access_token: accessToken,
|
||||
shortcut,
|
||||
compressSingletonFolder,
|
||||
copyFileButton,
|
||||
copySnippetButton,
|
||||
} = await configHelper.get()
|
||||
DOMHelper.decorateGitHubPageContent({ copyFileButton, copySnippetButton })
|
||||
dispatch({
|
||||
accessToken,
|
||||
toggleShowSideBarShortcut: shortcut,
|
||||
compressSingletonFolder,
|
||||
copyFileButton,
|
||||
copySnippetButton,
|
||||
})
|
||||
|
||||
const getTreeDataAggressively = GitHubHelper.getTreeData({
|
||||
...metaData,
|
||||
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 GitHubHelper.getRepoMeta({ ...metaData, accessToken })
|
||||
const projectDefaultBranchName = metaDataFromAPI['default_branch']
|
||||
if (!detectedBranchName && projectDefaultBranchName !== metaData.branchName) {
|
||||
// Accessing repo'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 = GitHubHelper.getTreeData({
|
||||
...metaData,
|
||||
accessToken,
|
||||
})
|
||||
} else {
|
||||
caughtAggressiveError.then(error => {
|
||||
// aggressive requested correct branch but ends in failure (e.g. project is empty)
|
||||
if (error instanceof Error) {
|
||||
dispatch(handleError, error)
|
||||
}
|
||||
})
|
||||
}
|
||||
getTreeData
|
||||
.then(treeData => {
|
||||
if (treeData) {
|
||||
// in an unknown rare case this NOT happen
|
||||
dispatch({ treeData })
|
||||
}
|
||||
})
|
||||
.catch(err => dispatch(handleError, err))
|
||||
Object.assign(metaData, { api: metaDataFromAPI })
|
||||
dispatch(setMetaData, metaData)
|
||||
const shouldShow = URLHelper.isInCodePage(metaData)
|
||||
dispatch(setShouldShow, shouldShow)
|
||||
} catch (err) {
|
||||
dispatch(handleError, err)
|
||||
}
|
||||
}
|
||||
|
||||
const handleError = dispatch => async err => {
|
||||
if (err.message === EMPTY_PROJECT) {
|
||||
dispatch(setError, 'This project seems to be empty.')
|
||||
} else if (
|
||||
err.message === NOT_FOUND ||
|
||||
err.message === BAD_CREDENTIALS ||
|
||||
err.message === API_RATE_LIMIT
|
||||
) {
|
||||
dispatch({ errorDueToAuth: true })
|
||||
dispatch(setShowSettings, true)
|
||||
dispatch(setShouldShow, true)
|
||||
} else {
|
||||
dispatch(setError, 'Gitako ate a bug, but it should recovery soon!')
|
||||
}
|
||||
}
|
||||
|
||||
const onPJAXEnd = dispatch => () => {
|
||||
dispatch(({ metaData, copyFileButton, copySnippetButton }) => {
|
||||
DOMHelper.unmountTopProgressBar()
|
||||
DOMHelper.decorateGitHubPageContent({ copyFileButton, copySnippetButton })
|
||||
const mergedMetaData = { ...metaData, ...URLHelper.parse() }
|
||||
dispatch(setShouldShow, URLHelper.isInCodePage(mergedMetaData))
|
||||
dispatch(setMetaData, mergedMetaData)
|
||||
})
|
||||
}
|
||||
|
||||
const onKeyDown = dispatch => e => {
|
||||
dispatch(({ toggleShowSideBarShortcut }) => {
|
||||
if (toggleShowSideBarShortcut) {
|
||||
const keys = keyHelper.parseEvent(e)
|
||||
if (keys === toggleShowSideBarShortcut) {
|
||||
dispatch(toggleShowSideBar)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const toggleShowSideBar = dispatch => () =>
|
||||
dispatch(({ shouldShow }) => dispatch(setShouldShow, !shouldShow))
|
||||
|
||||
const setShouldShow = dispatch => shouldShow => {
|
||||
dispatch({ shouldShow }, shouldShow ? DOMHelper.focusFileExplorer : null)
|
||||
DOMHelper.setBodyIndent(shouldShow)
|
||||
}
|
||||
|
||||
const setError = dispatch => error => {
|
||||
dispatch({ error })
|
||||
dispatch(setShouldShow, false)
|
||||
}
|
||||
|
||||
const toggleShowSettings = dispatch => () =>
|
||||
dispatch(({ showSettings }) => ({ showSettings: !showSettings }))
|
||||
|
||||
const setShowSettings = dispatch => showSettings => dispatch({ showSettings })
|
||||
|
||||
const onAccessTokenChange = dispatch => accessToken => dispatch({ accessToken })
|
||||
|
||||
const onShortcutChange = dispatch => shortcut => dispatch({ toggleShowSideBarShortcut: shortcut })
|
||||
|
||||
const setMetaData = dispatch => metaData => dispatch({ metaData })
|
||||
|
||||
const setCompressSingleton = dispatch => compressSingletonFolder =>
|
||||
dispatch({ compressSingletonFolder })
|
||||
|
||||
const setCopyFile = dispatch => copyFileButton => dispatch({ copyFileButton })
|
||||
|
||||
const setCopySnippet = dispatch => copySnippetButton => dispatch({ copySnippetButton })
|
||||
|
||||
export default {
|
||||
init,
|
||||
onPJAXEnd,
|
||||
onKeyDown,
|
||||
setShouldShow,
|
||||
setShowSettings,
|
||||
toggleShowSideBar,
|
||||
toggleShowSettings,
|
||||
onAccessTokenChange,
|
||||
onShortcutChange,
|
||||
setMetaData,
|
||||
setCompressSingleton,
|
||||
setCopyFile,
|
||||
setCopySnippet,
|
||||
setError,
|
||||
handleError,
|
||||
}
|
||||
226
src/driver/core/SideBar.ts
Normal file
226
src/driver/core/SideBar.ts
Normal file
|
|
@ -0,0 +1,226 @@
|
|||
import DOMHelper, { REPO_TYPE_PRIVATE } from 'utils/DOMHelper'
|
||||
import GitHubHelper, {
|
||||
NOT_FOUND,
|
||||
BAD_CREDENTIALS,
|
||||
API_RATE_LIMIT,
|
||||
EMPTY_PROJECT,
|
||||
MetaData,
|
||||
} from 'utils/GitHubHelper'
|
||||
import configHelper from 'utils/configHelper'
|
||||
import URLHelper from 'utils/URLHelper'
|
||||
import keyHelper from 'utils/keyHelper'
|
||||
import { MethodCreator, DispatchState, TriggerOtherMethod } from 'driver/connect'
|
||||
import { Props, TreeData } from 'components/SideBar'
|
||||
import SettingsBar from 'components/SettingsBar'
|
||||
|
||||
export type ConnectorState = {
|
||||
// initial width of side bar
|
||||
baseSize?: number
|
||||
// error message
|
||||
error?: string
|
||||
// whether Gitako side bar should be shown
|
||||
shouldShow?: boolean
|
||||
// whether show settings pane
|
||||
showSettings?: boolean
|
||||
// whether failed loading the repo due to it is private
|
||||
errorDueToAuth?: boolean
|
||||
// access token for GitHub
|
||||
accessToken?: string
|
||||
// the shortcut string for toggle sidebar
|
||||
toggleShowSideBarShortcut?: string
|
||||
// meta data for the repository
|
||||
metaData: MetaData
|
||||
// file tree data
|
||||
treeData: TreeData
|
||||
// few settings
|
||||
compressSingletonFolder?: boolean
|
||||
copyFileButton?: boolean
|
||||
copySnippetButton?: boolean
|
||||
logoContainerElement: HTMLElement | null
|
||||
|
||||
init: () => void
|
||||
onPJAXEnd: () => void
|
||||
toggleShowSideBar: () => void
|
||||
toggleShowSettings: () => void
|
||||
onAccessTokenChange: SettingsBar['props']['onAccessTokenChange']
|
||||
onKeyDown: EventListener
|
||||
onShortcutChange: SettingsBar['props']['onShortcutChange']
|
||||
setCopyFile: SettingsBar['props']['setCopyFile']
|
||||
setCopySnippet: SettingsBar['props']['setCopySnippet']
|
||||
setCompressSingleton: SettingsBar['props']['setCompressSingleton']
|
||||
}
|
||||
|
||||
const init: MethodCreator<Props> = dispatch => async () => {
|
||||
try {
|
||||
if (!URLHelper.isInRepoPage()) return
|
||||
dispatch.state({
|
||||
logoContainerElement: DOMHelper.insertLogoMountPoint(),
|
||||
})
|
||||
let detectedBranchName
|
||||
const metaData = URLHelper.parse()
|
||||
if (DOMHelper.isInCodePage()) {
|
||||
detectedBranchName = DOMHelper.getCurrentBranch() || URLHelper.parseSHA() // not working well with non-branch blob // cannot handle '/' split branch name, should not use when possibly on branch page
|
||||
}
|
||||
metaData.branchName = detectedBranchName || 'master'
|
||||
dispatch.for(setMetaData, metaData)
|
||||
const {
|
||||
access_token: accessToken,
|
||||
shortcut,
|
||||
compressSingletonFolder,
|
||||
copyFileButton,
|
||||
copySnippetButton,
|
||||
} = await configHelper.get()
|
||||
DOMHelper.decorateGitHubPageContent({ copyFileButton, copySnippetButton })
|
||||
dispatch.state({
|
||||
accessToken,
|
||||
toggleShowSideBarShortcut: shortcut,
|
||||
compressSingletonFolder,
|
||||
copyFileButton,
|
||||
copySnippetButton,
|
||||
})
|
||||
|
||||
if (!metaData.branchName || !metaData.userName) return
|
||||
const getTreeDataAggressively = GitHubHelper.getTreeData({
|
||||
branchName: metaData.branchName,
|
||||
userName: metaData.userName,
|
||||
repoName: metaData.repoName,
|
||||
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 GitHubHelper.getRepoMeta({ ...metaData, accessToken })
|
||||
const projectDefaultBranchName = metaDataFromAPI['default_branch']
|
||||
if (!detectedBranchName && projectDefaultBranchName !== metaData.branchName) {
|
||||
// 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 = GitHubHelper.getTreeData({
|
||||
branchName: metaData.branchName,
|
||||
userName: metaData.userName,
|
||||
repoName: metaData.repoName,
|
||||
accessToken,
|
||||
})
|
||||
} else {
|
||||
caughtAggressiveError.then(error => {
|
||||
// aggressive requested correct branch but ends in failure (e.g. project is empty)
|
||||
if (error instanceof Error) {
|
||||
dispatch.for(handleError, error)
|
||||
}
|
||||
})
|
||||
}
|
||||
getTreeData
|
||||
.then(treeData => {
|
||||
if (treeData) {
|
||||
// in an unknown rare case this NOT happen
|
||||
dispatch.state({ treeData })
|
||||
}
|
||||
})
|
||||
.catch(err => dispatch.for(handleError, err))
|
||||
Object.assign(metaData, { api: metaDataFromAPI })
|
||||
dispatch.for(setMetaData, metaData)
|
||||
const shouldShow = URLHelper.isInCodePage(metaData)
|
||||
dispatch.for(setShouldShow, shouldShow)
|
||||
} catch (err) {
|
||||
dispatch.for(handleError, err)
|
||||
}
|
||||
}
|
||||
|
||||
const handleError: MethodCreator = dispatch => async err => {
|
||||
if (err.message === EMPTY_PROJECT) {
|
||||
dispatch.for(setError, 'This project seems to be empty.')
|
||||
} else if (
|
||||
err.message === NOT_FOUND ||
|
||||
err.message === BAD_CREDENTIALS ||
|
||||
err.message === API_RATE_LIMIT
|
||||
) {
|
||||
dispatch.state({ errorDueToAuth: true })
|
||||
dispatch.for(setShowSettings, true)
|
||||
dispatch.for(setShouldShow, true)
|
||||
} else {
|
||||
dispatch.for(setError, 'Gitako ate a bug, but it should recovery soon!')
|
||||
}
|
||||
}
|
||||
|
||||
const onPJAXEnd: MethodCreator<ConnectorState> = dispatch => () => {
|
||||
dispatch.prepare(({ metaData, copyFileButton, copySnippetButton }) => {
|
||||
DOMHelper.unmountTopProgressBar()
|
||||
DOMHelper.decorateGitHubPageContent({ copyFileButton, copySnippetButton })
|
||||
const mergedMetaData = { ...metaData, ...URLHelper.parse() }
|
||||
dispatch.for(setShouldShow, URLHelper.isInCodePage(mergedMetaData))
|
||||
dispatch.for(setMetaData, mergedMetaData)
|
||||
})
|
||||
}
|
||||
|
||||
const onKeyDown: MethodCreator = dispatch => e => {
|
||||
dispatch.state(({ toggleShowSideBarShortcut }: { toggleShowSideBarShortcut: string }) => {
|
||||
if (toggleShowSideBarShortcut) {
|
||||
const keys = keyHelper.parseEvent(e)
|
||||
if (keys === toggleShowSideBarShortcut) {
|
||||
dispatch.for(toggleShowSideBar)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const toggleShowSideBar: MethodCreator = dispatch => () =>
|
||||
dispatch.state(({ shouldShow }: { shouldShow: boolean }) =>
|
||||
dispatch.for(setShouldShow, !shouldShow)
|
||||
)
|
||||
|
||||
const setShouldShow: MethodCreator = dispatch => shouldShow => {
|
||||
dispatch.state({ shouldShow }, shouldShow ? DOMHelper.focusFileExplorer : null)
|
||||
DOMHelper.setBodyIndent(shouldShow)
|
||||
}
|
||||
|
||||
const setError: MethodCreator = dispatch => error => {
|
||||
dispatch.state({ error })
|
||||
dispatch.for(setShouldShow, false)
|
||||
}
|
||||
|
||||
const toggleShowSettings: MethodCreator = dispatch => () =>
|
||||
dispatch.state(({ showSettings }: { showSettings: boolean }) => ({
|
||||
showSettings: !showSettings,
|
||||
}))
|
||||
|
||||
const setShowSettings: MethodCreator = dispatch => showSettings => dispatch.state({ showSettings })
|
||||
|
||||
const onAccessTokenChange: MethodCreator = dispatch => accessToken =>
|
||||
dispatch.state({ accessToken })
|
||||
|
||||
const onShortcutChange: MethodCreator = dispatch => shortcut =>
|
||||
dispatch.state({ toggleShowSideBarShortcut: shortcut })
|
||||
|
||||
const setMetaData: MethodCreator = dispatch => metaData => dispatch.state({ metaData })
|
||||
|
||||
const setCompressSingleton: MethodCreator = dispatch => compressSingletonFolder =>
|
||||
dispatch.state({ compressSingletonFolder })
|
||||
|
||||
const setCopyFile: MethodCreator = dispatch => copyFileButton => dispatch.state({ copyFileButton })
|
||||
|
||||
const setCopySnippet: MethodCreator = dispatch => copySnippetButton =>
|
||||
dispatch.state({ copySnippetButton })
|
||||
|
||||
export default {
|
||||
init,
|
||||
onPJAXEnd,
|
||||
onKeyDown,
|
||||
setShouldShow,
|
||||
setShowSettings,
|
||||
toggleShowSideBar,
|
||||
toggleShowSettings,
|
||||
onAccessTokenChange,
|
||||
onShortcutChange,
|
||||
setMetaData,
|
||||
setCompressSingleton,
|
||||
setCopyFile,
|
||||
setCopySnippet,
|
||||
setError,
|
||||
handleError,
|
||||
}
|
||||
|
|
@ -38,11 +38,14 @@ async function request(url: string, { accessToken }: Options = {}) {
|
|||
}
|
||||
}
|
||||
|
||||
type PageType = 'blob' | 'tree' | string
|
||||
|
||||
export type MetaData = {
|
||||
userName?: string
|
||||
repoName?: string
|
||||
branchName?: string
|
||||
accessToken?: string
|
||||
type?: PageType
|
||||
api?: any
|
||||
}
|
||||
|
||||
|
|
@ -63,7 +66,7 @@ export type TreeData = {
|
|||
tree: TreeItem[]
|
||||
}
|
||||
|
||||
async function getTreeData({ userName, repoName, branchName, accessToken }: TreeData) {
|
||||
async function getTreeData({ userName, repoName, branchName, accessToken }: MetaData) {
|
||||
const url = `https://api.github.com/repos/${userName}/${repoName}/git/trees/${branchName}?recursive=1`
|
||||
return await request(url, { accessToken })
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { raiseError } from 'analytics'
|
||||
import { MetaData } from './GitHubHelper'
|
||||
|
||||
function parse() {
|
||||
function parse(): MetaData & { path: string[] } {
|
||||
const { pathname } = window.location
|
||||
let [
|
||||
,
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import GitHubHelper, { TreeData, MetaData } from 'utils/GitHubHelper'
|
||||
import GitHubHelper, { MetaData, TreeData } from 'utils/GitHubHelper'
|
||||
|
||||
interface BasicItem {
|
||||
name: string | null
|
||||
|
|
|
|||
Loading…
Reference in a new issue