From 2fe39a90c36ad47c4fa2fd26d38b125b8a1f4dd9 Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Sun, 31 Jan 2021 21:33:24 +0800 Subject: [PATCH 1/8] refactor: simplify code --- src/components/SideBar.tsx | 25 +++++++++++---------- src/content.tsx | 14 ++++++------ src/driver/core/FileExplorer.ts | 4 +--- src/platforms/GitHub/URLHelper.ts | 5 +++-- src/platforms/GitHub/index.ts | 36 ++++++++++--------------------- src/utils/configHelper.ts | 2 +- 6 files changed, 37 insertions(+), 49 deletions(-) diff --git a/src/components/SideBar.tsx b/src/components/SideBar.tsx index 9aa21ca..89cb0c8 100644 --- a/src/components/SideBar.tsx +++ b/src/components/SideBar.tsx @@ -30,17 +30,7 @@ const RawGitako: React.FC = function RawGitako(props) { const accessToken = props.configContext.val.accessToken const [baseSize] = React.useState(() => configContext.val.sideBarWidth) - const { shrinkGitHubHeader } = configContext.val - React.useEffect(() => { - if (platform === GitHub) { - const ele = document.body - if (shrinkGitHubHeader) { - ele.classList.add('shrink-github-header') - } else { - ele.classList.remove('shrink-github-header') - } - } - }, [shrinkGitHubHeader]) + useShrinkGitHubHeader(configContext.val.shrinkGitHubHeader) const intelligentToggle = configContext.val.intelligentToggle React.useEffect(() => { @@ -168,6 +158,19 @@ RawGitako.defaultProps = { export const SideBar = connect(SideBarCore)(RawGitako) +function useShrinkGitHubHeader(shrinkGitHubHeader: boolean) { + React.useEffect(() => { + if (platform === GitHub) { + const target = document.body + if (shrinkGitHubHeader) { + target.classList.add('shrink-github-header') + } else { + target.classList.remove('shrink-github-header') + } + } + }, [shrinkGitHubHeader]) +} + function AccessDeniedError({ hasToken }: { hasToken: boolean }) { return } diff --git a/src/content.tsx b/src/content.tsx index 32da934..91c1db0 100644 --- a/src/content.tsx +++ b/src/content.tsx @@ -9,13 +9,6 @@ import './content.scss' if (platform.resolveMeta()) { addMiddleware(withErrorLog) - async function init() { - await injectStyles(browser.extension.getURL('content.css')) - const SideBarElement = document.createElement('div') - document.body.appendChild(SideBarElement) - ReactDOM.render(, SideBarElement) - } - if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', init) } else { @@ -23,6 +16,13 @@ if (platform.resolveMeta()) { } } +async function init() { + await injectStyles(browser.extension.getURL('content.css')) + const SideBarElement = document.createElement('div') + document.body.appendChild(SideBarElement) + ReactDOM.render(, SideBarElement) +} + // injects a copy of stylesheets so that other extensions(e.g. dark reader) could read // resolves when style is loaded to prevent render without proper styles async function injectStyles(url: string) { diff --git a/src/driver/core/FileExplorer.ts b/src/driver/core/FileExplorer.ts index a28f651..2b2b78c 100644 --- a/src/driver/core/FileExplorer.ts +++ b/src/driver/core/FileExplorer.ts @@ -51,11 +51,9 @@ export const setUpTree: BoundMethodCreator< if (!treeRoot) return dispatch.set({ state: 'rendering' }) - const { compressSingletonFolder } = config - visibleNodesGenerator = new VisibleNodesGenerator({ root: treeRoot, - compress: compressSingletonFolder, + compress: config.compressSingletonFolder, async getTreeData(path) { const { root } = await platform.getTreeData(metaData, path, false, config.accessToken) return root diff --git a/src/platforms/GitHub/URLHelper.ts b/src/platforms/GitHub/URLHelper.ts index fcdc3d5..f8093cd 100644 --- a/src/platforms/GitHub/URLHelper.ts +++ b/src/platforms/GitHub/URLHelper.ts @@ -1,6 +1,6 @@ import { raiseError } from 'analytics' -export function parse(): Partial & { path: string[] } { +export function parse(): Pick & { path: string[] } { const { pathname } = window.location let [ , @@ -13,12 +13,13 @@ export function parse(): Partial & { path: string[] } { return { userName, repoName, - branchName: undefined, type, path, } } +// not working well with non-branch blob +// cannot handle '/' split branch name, should not use when possibly in branch page export function parseSHA() { const { type, path } = parse() return type === 'blob' || type === 'tree' ? path[0] : undefined diff --git a/src/platforms/GitHub/index.ts b/src/platforms/GitHub/index.ts index ad1a6e7..d17ade2 100644 --- a/src/platforms/GitHub/index.ts +++ b/src/platforms/GitHub/index.ts @@ -91,26 +91,23 @@ export const GitHub: Platform = { return null } - let detectedBranchName + let branchName if (URLHelper.isInPullPage()) { - detectedBranchName = DOMHelper.getIssueTitle() + branchName = DOMHelper.getIssueTitle() } else if ( DOMHelper.isInCodePage() && !['releases', 'tags'].includes(URLHelper.parse().type || '') // resolve sentry issue #-CK ) { - // 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 metaData = { ...URLHelper.parse(), - branchName: detectedBranchName, + branchName, } as MetaData return metaData }, - async getMetaData(partialMetaData, accessToken) { - const { userName, repoName } = partialMetaData + async getMetaData({ userName, repoName }, accessToken) { const data = await API.getRepoMeta(userName, repoName, accessToken) return { userUrl: data?.owner?.html_url, @@ -156,9 +153,9 @@ export const GitHub: Platform = { path: item.filename || '', type: 'blob', name: item.filename?.replace(/^.*\//, '') || '', - url: `https://${window.location.host}/${metaData.userName}/${ - metaData.repoName - }/pull/${pullId}/files${window.location.search}#${creator(item.filename) || ''}`, + url: `https://${window.location.host}/${userName}/${repoName}/pull/${pullId}/files${ + window.location.search + }#${creator(item.filename) || ''}`, sha: item.sha, })) @@ -198,13 +195,7 @@ export const GitHub: Platform = { name: item.path?.replace(/^.*\//, '') || '', url: item.url && item.type && item.path - ? getUrlForRedirect( - metaData.userName, - metaData.repoName, - metaData.branchName, - item.type, - item.path, - ) + ? getUrlForRedirect(userName, repoName, branchName, item.type, item.path) : undefined, contents: item.type === 'tree' ? [] : undefined, sha: item.sha, @@ -213,13 +204,8 @@ export const GitHub: Platform = { const gitModules = root.contents?.find(item => item.name === '.gitmodules') if (gitModules) { - if (metaData.userName && metaData.repoName && gitModules.sha) { - const blobData = await API.getBlobData( - metaData.userName, - metaData.repoName, - gitModules.sha, - accessToken, - ) + if (userName && repoName && 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)) diff --git a/src/utils/configHelper.ts b/src/utils/configHelper.ts index c6cb4b0..4c73b14 100644 --- a/src/utils/configHelper.ts +++ b/src/utils/configHelper.ts @@ -33,7 +33,7 @@ export enum configKeys { const defaultConfigs: Config = { sideBarWidth: 260, shortcut: undefined, - accessToken: undefined, + accessToken: '', compressSingletonFolder: true, copyFileButton: true, copySnippetButton: true, From b67905ec8cf17c9dbf139bd044bfe27863ce6f55 Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Sun, 31 Jan 2021 23:56:36 +0800 Subject: [PATCH 2/8] chore: more accurate type --- src/global.d.ts | 4 ++++ src/platforms/platform.d.ts | 3 ++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/global.d.ts b/src/global.d.ts index 3b22a28..a60c9a0 100644 --- a/src/global.d.ts +++ b/src/global.d.ts @@ -24,6 +24,10 @@ type IO = { } type Override = Omit & Incoming +type MakeOptional = Override< + Original, + Partial> +> type VoidFN = (payload: T) => void diff --git a/src/platforms/platform.d.ts b/src/platforms/platform.d.ts index fb5de6e..d2b20a2 100644 --- a/src/platforms/platform.d.ts +++ b/src/platforms/platform.d.ts @@ -1,6 +1,7 @@ type Platform = { isEnterprise(): boolean - resolveMeta(): MetaData | null + // branch name might not be available when resolving from DOM and URL + resolveMeta(): MakeOptional | null getMetaData( metaData: Pick, accessToken?: string, From 2a9ab02fda3a857ed2824ea4747736f160c3b390 Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Sun, 31 Jan 2021 23:57:43 +0800 Subject: [PATCH 3/8] chore: simplify logics about access token --- src/components/SideBar.tsx | 24 ++++++++++-------------- src/utils/general.ts | 4 ++++ 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/components/SideBar.tsx b/src/components/SideBar.tsx index 89cb0c8..f039bca 100644 --- a/src/components/SideBar.tsx +++ b/src/components/SideBar.tsx @@ -16,9 +16,8 @@ import { useGitHubAttachCopySnippetButton, } from 'platforms/GitHub' import * as React from 'react' -import { useUpdateEffect } from 'react-use' import { cx } from 'utils/cx' -import { parseURLSearch } from 'utils/general' +import { parseURLSearch, run } from 'utils/general' import { loadWithPJAX, useOnPJAXDone, usePJAX } from 'utils/hooks/usePJAX' import { useProgressBar } from 'utils/hooks/useProgressBar' import * as keyHelper from 'utils/keyHelper' @@ -27,7 +26,7 @@ import { Theme } from './Theme' const RawGitako: React.FC = function RawGitako(props) { const configContext = useConfigs() - const accessToken = props.configContext.val.accessToken + const accessToken = props.configContext.val.accessToken || '' const [baseSize] = React.useState(() => configContext.val.sideBarWidth) useShrinkGitHubHeader(configContext.val.shrinkGitHubHeader) @@ -39,16 +38,18 @@ const RawGitako: React.FC = function RawGitako(props) { }, [intelligentToggle, props.metaData]) React.useEffect(() => { - const { init } = props - ;(async function () { + run(async function () { if (!accessToken) { - const accessToken = (await trySetUpAccessTokenWithCode()) || undefined - configContext.set({ accessToken }) + const accessToken = await trySetUpAccessTokenWithCode() + if (accessToken) configContext.set({ accessToken }) } - init() - })() + }) }, []) + React.useEffect(() => { + props.init() + }, [accessToken]) + React.useEffect( function attachKeyDown() { if (props.disabled || !configContext.val.shortcut) return @@ -81,11 +82,6 @@ const RawGitako: React.FC = function RawGitako(props) { const copySnippetButton = configContext.val.copySnippetButton useGitHubAttachCopySnippetButton(copySnippetButton) - // init again when setting new accessToken - useUpdateEffect(() => { - props.init() - }, [accessToken || '']) // '' prevents duplicated requests - usePJAX() useProgressBar() diff --git a/src/utils/general.ts b/src/utils/general.ts index d07b343..8b6d1a6 100644 --- a/src/utils/general.ts +++ b/src/utils/general.ts @@ -183,3 +183,7 @@ export function withEffect any>( return returnValue } } + +export function run(fn: () => T) { + return fn() +} From f3f86a79eafef9c67a2fcf6d1879330c367ba957 Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Sun, 31 Jan 2021 23:58:07 +0800 Subject: [PATCH 4/8] refactor: simplify init logics --- src/driver/core/SideBar.ts | 136 +++++++++++++----------------- src/platforms/GitHub/URLHelper.ts | 4 +- src/platforms/GitHub/index.ts | 25 ++++-- src/platforms/Gitea/index.ts | 18 ++-- src/platforms/Gitee/index.ts | 17 ++-- src/utils/general.ts | 16 ++++ 6 files changed, 120 insertions(+), 96 deletions(-) diff --git a/src/driver/core/SideBar.ts b/src/driver/core/SideBar.ts index aec4513..b3fbb98 100644 --- a/src/driver/core/SideBar.ts +++ b/src/driver/core/SideBar.ts @@ -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 | null } & { init: GetCreatedMethod - setMetaData: GetCreatedMethod setShouldShow: GetCreatedMethod toggleShowSideBar: GetCreatedMethod toggleShowSettings: GetCreatedMethod @@ -34,18 +34,10 @@ export type ConnectorState = { type BoundMethodCreator = MethodCreator -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 }) diff --git a/src/platforms/GitHub/URLHelper.ts b/src/platforms/GitHub/URLHelper.ts index f8093cd..e388fd0 100644 --- a/src/platforms/GitHub/URLHelper.ts +++ b/src/platforms/GitHub/URLHelper.ts @@ -1,6 +1,8 @@ import { raiseError } from 'analytics' -export function parse(): Pick & { path: string[] } { +export function parse(): Partial> & { + path: string[] +} { const { pathname } = window.location let [ , diff --git a/src/platforms/GitHub/index.ts b/src/platforms/GitHub/index.ts index d17ade2..7562c0d 100644 --- a/src/platforms/GitHub/index.ts +++ b/src/platforms/GitHub/index.ts @@ -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)) } } diff --git a/src/platforms/Gitea/index.ts b/src/platforms/Gitea/index.ts index e5bf1dd..f89681d 100644 --- a/src/platforms/Gitea/index.ts +++ b/src/platforms/Gitea/index.ts @@ -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) { diff --git a/src/platforms/Gitee/index.ts b/src/platforms/Gitee/index.ts index 14c8da1..3e93575 100644 --- a/src/platforms/Gitee/index.ts +++ b/src/platforms/Gitee/index.ts @@ -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) { diff --git a/src/utils/general.ts b/src/utils/general.ts index 8b6d1a6..4a78db9 100644 --- a/src/utils/general.ts +++ b/src/utils/general.ts @@ -187,3 +187,19 @@ export function withEffect any>( export function run(fn: () => T) { return fn() } + +export function createPromiseQueue() { + let promise: Promise + return { + async enter() { + let leave: () => void + const current = new Promise(resolve => (leave = () => resolve())) + + const lastPromise = promise + promise = current! + if (lastPromise) await lastPromise + + return leave! + }, + } +} From f32dfd0722df73dd9f12c4e642374cf62c30af32 Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Sun, 31 Jan 2021 23:58:31 +0800 Subject: [PATCH 5/8] chore: change the position to fixed --- src/components/SideBar.tsx | 14 ++++---- src/styles/index.scss | 72 +++++++++++++++++++------------------- 2 files changed, 42 insertions(+), 44 deletions(-) diff --git a/src/components/SideBar.tsx b/src/components/SideBar.tsx index f039bca..b49916e 100644 --- a/src/components/SideBar.tsx +++ b/src/components/SideBar.tsx @@ -107,15 +107,13 @@ const RawGitako: React.FC = function RawGitako(props) {
+
+ +
-
- {metaData ? :
} -
- -
-
+
{metaData ? :
}
{errorDueToAuth ? ( ) : ( diff --git a/src/styles/index.scss b/src/styles/index.scss index b7703ac..b722d97 100644 --- a/src/styles/index.scss +++ b/src/styles/index.scss @@ -303,7 +303,9 @@ $minimal-z-index: max($github-header-z-index, $github-pull-request-float-header- font-size: 0; } } + .#{$name}-side-bar-body { + $button-size: 32px; width: 100%; height: 100%; display: flex; @@ -334,6 +336,37 @@ $minimal-z-index: max($github-header-z-index, $github-pull-request-float-header- text-align: center; } + .close-side-bar-button-position { + position: absolute; + right: 6px; + top: 6px; + z-index: 1; // prevent being covered by following elements + + .close-side-bar-button { + @include icon-button; + @include button-color; + width: $button-size; + height: $button-size; + border-radius: $button-size; + + // feedback to click should be instant + &:not(:active) { + transition: background linear 0.3s; + } + + .action-icon { + color: var(--gitako-icon-tertiary); + width: 20px; + height: 20px; + text-align: center; + .octicon { + width: 100%; + height: 100%; + } + } + } + } + .#{$name}-side-bar-content { display: flex; flex: 1; @@ -342,7 +375,6 @@ $minimal-z-index: max($github-header-z-index, $github-pull-request-float-header- min-height: 0; // make content shrinkable .header { - $button-size: 32px; position: relative; .meta-bar { @@ -364,38 +396,6 @@ $minimal-z-index: max($github-header-z-index, $github-pull-request-float-header- background-color: var(--gitako-branch-name-bg); } } - - .close-side-bar-button-position { - position: absolute; - right: 6px; - top: 0; - height: 100%; - display: inline-flex; - align-items: center; - .close-side-bar-button { - @include icon-button; - @include button-color; - width: $button-size; - height: $button-size; - border-radius: $button-size; - - // feedback to click should be instant - &:not(:active) { - transition: background linear 0.3s; - } - - .action-icon { - color: var(--gitako-icon-tertiary); - width: 20px; - height: 20px; - text-align: center; - .octicon { - width: 100%; - height: 100%; - } - } - } - } } .description { @@ -478,9 +478,9 @@ $minimal-z-index: max($github-header-z-index, $github-pull-request-float-header- } &.disabled { - pointer-events: none; - color: var(--gitako-text-disabled); - } + pointer-events: none; + color: var(--gitako-text-disabled); + } display: flex; justify-content: space-between; align-items: center; From 4be1d2d44d81426c62a3a55bc784fad5039e188c Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Mon, 1 Feb 2021 01:09:23 +0800 Subject: [PATCH 6/8] refactor: check meta data usages --- src/components/FileExplorer.tsx | 14 ++++++-- src/components/SideBar.tsx | 59 ++++++++++++++++----------------- src/driver/core/FileExplorer.ts | 8 +++-- 3 files changed, 44 insertions(+), 37 deletions(-) diff --git a/src/components/FileExplorer.tsx b/src/components/FileExplorer.tsx index b2311c7..cef7d03 100644 --- a/src/components/FileExplorer.tsx +++ b/src/components/FileExplorer.tsx @@ -35,9 +35,17 @@ const RawFileExplorer: React.FC = function RawFileExplor const { val: config } = useConfigs() React.useEffect(() => { - const { setUpTree, treeRoot, metaData } = props - setUpTree({ treeRoot, metaData, config }) - }, [setUpTree, treeRoot, config.compressSingletonFolder, config.accessToken]) + if (treeRoot) { + setUpTree({ + treeRoot, + metaData, + config: { + compressSingletonFolder: config.compressSingletonFolder, + accessToken: config.accessToken, + }, + }) + } + }, [setUpTree, treeRoot, metaData, config.compressSingletonFolder, config.accessToken]) React.useEffect(() => { if (visibleNodes?.focusedNode) focusFileExplorer() diff --git a/src/components/SideBar.tsx b/src/components/SideBar.tsx index b49916e..704c675 100644 --- a/src/components/SideBar.tsx +++ b/src/components/SideBar.tsx @@ -5,7 +5,6 @@ import { Portal } from 'components/Portal' import { Resizable } from 'components/Resizable' import { SettingsBar } from 'components/settings/SettingsBar' import { ToggleShowButton } from 'components/ToggleShowButton' -import { useConfigs } from 'containers/ConfigsContext' import { connect } from 'driver/connect' import { SideBarCore } from 'driver/core' import { ConnectorState, Props } from 'driver/core/SideBar' @@ -25,18 +24,25 @@ import { Icon } from './Icon' import { Theme } from './Theme' const RawGitako: React.FC = function RawGitako(props) { - const configContext = useConfigs() - const accessToken = props.configContext.val.accessToken || '' + const { + errorDueToAuth, + metaData, + treeData, + defer, + error, + shouldShow, + showSettings, + logoContainerElement, + toggleShowSideBar, + toggleShowSettings, + configContext, + } = props + + const accessToken = configContext.val.accessToken || '' const [baseSize] = React.useState(() => configContext.val.sideBarWidth) useShrinkGitHubHeader(configContext.val.shrinkGitHubHeader) - const intelligentToggle = configContext.val.intelligentToggle - React.useEffect(() => { - const shouldShow = intelligentToggle === null ? platform.shouldShow() : intelligentToggle - props.setShouldShow(shouldShow) - }, [intelligentToggle, props.metaData]) - React.useEffect(() => { run(async function () { if (!accessToken) { @@ -57,46 +63,37 @@ const RawGitako: React.FC = function RawGitako(props) { function onKeyDown(e: KeyboardEvent) { const keys = keyHelper.parseEvent(e) if (keys === configContext.val.shortcut) { - props.toggleShowSideBar() + toggleShowSideBar() } } window.addEventListener('keydown', onKeyDown) return () => window.removeEventListener('keydown', onKeyDown) }, - [props.disabled, configContext.val.shortcut], + [toggleShowSideBar, props.disabled, configContext.val.shortcut], ) + const intelligentToggle = configContext.val.intelligentToggle + React.useEffect(() => { + const shouldShow = intelligentToggle === null ? platform.shouldShow() : intelligentToggle + props.setShouldShow(shouldShow) + }, [intelligentToggle, props.metaData]) + const updateSideBarVisibility = React.useCallback( function updateSideBarVisibility() { - if (configContext.val.intelligentToggle === null) { + if (intelligentToggle === null) { props.setShouldShow(platform.shouldShow()) } }, - [props.metaData?.branchName, configContext.val.intelligentToggle], + [props.metaData?.branchName, intelligentToggle], ) useOnPJAXDone(updateSideBarVisibility) - const copyFileButton = configContext.val.copyFileButton - useGitHubAttachCopyFileButton(copyFileButton) - - const copySnippetButton = configContext.val.copySnippetButton - useGitHubAttachCopySnippetButton(copySnippetButton) + useGitHubAttachCopyFileButton(configContext.val.copyFileButton) + useGitHubAttachCopySnippetButton(configContext.val.copySnippetButton) usePJAX() useProgressBar() - const { - errorDueToAuth, - metaData, - treeData: treeRoot, - defer, - error, - shouldShow, - showSettings, - logoContainerElement, - toggleShowSideBar, - toggleShowSettings, - } = props return (
@@ -121,7 +118,7 @@ const RawGitako: React.FC = function RawGitako(props) { void let visibleNodesGenerator: VisibleNodesGenerator type BoundMethodCreator = MethodCreator export const setUpTree: BoundMethodCreator< - [Pick & { config: Config }] + [ + Required> & { + config: Pick + }, + ] > = dispatch => async ({ treeRoot, metaData, config }) => { - if (!treeRoot) return dispatch.set({ state: 'rendering' }) visibleNodesGenerator = new VisibleNodesGenerator({ From 8f675f5c5b7003131c1620dd525e35ecade08e3d Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Mon, 1 Feb 2021 14:13:21 +0800 Subject: [PATCH 7/8] chore: update IO field names --- src/components/FileExplorer.tsx | 2 +- src/components/Node.tsx | 2 +- src/components/Resizable.tsx | 2 +- src/components/SideBar.tsx | 22 +++++------ src/components/SimpleToggleField.tsx | 4 +- src/components/ToggleShowButton.tsx | 6 +-- .../settings/AccessTokenSettings.tsx | 38 +++++++++---------- src/components/settings/FileTreeSettings.tsx | 8 ++-- src/components/settings/SettingsBar.tsx | 6 +-- src/components/settings/SidebarSettings.tsx | 34 ++++++++--------- src/containers/ConfigsContext.tsx | 15 +++----- src/driver/core/SideBar.ts | 8 ++-- src/global.d.ts | 4 +- src/utils/hooks/useAsyncMemo.ts | 8 ++-- src/utils/hooks/useStateIO.ts | 11 ++++++ src/utils/hooks/useStates.ts | 11 ------ 16 files changed, 88 insertions(+), 93 deletions(-) create mode 100644 src/utils/hooks/useStateIO.ts delete mode 100644 src/utils/hooks/useStates.ts diff --git a/src/components/FileExplorer.tsx b/src/components/FileExplorer.tsx index cef7d03..91fc119 100644 --- a/src/components/FileExplorer.tsx +++ b/src/components/FileExplorer.tsx @@ -32,7 +32,7 @@ const RawFileExplorer: React.FC = function RawFileExplor treeRoot, defer, } = props - const { val: config } = useConfigs() + const { value: config } = useConfigs() React.useEffect(() => { if (treeRoot) { diff --git a/src/components/Node.tsx b/src/components/Node.tsx index ea023fc..f721c1f 100644 --- a/src/components/Node.tsx +++ b/src/components/Node.tsx @@ -87,7 +87,7 @@ const NodeItemIcon = React.memo(function NodeItemIcon({ loading?: boolean }) { const { - val: { icons }, + value: { icons }, } = useConfigs() const src = React.useMemo( diff --git a/src/components/Resizable.tsx b/src/components/Resizable.tsx index fa58bb6..bc1a786 100644 --- a/src/components/Resizable.tsx +++ b/src/components/Resizable.tsx @@ -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 diff --git a/src/components/SideBar.tsx b/src/components/SideBar.tsx index 704c675..96d044e 100644 --- a/src/components/SideBar.tsx +++ b/src/components/SideBar.tsx @@ -38,16 +38,16 @@ const RawGitako: React.FC = 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 = 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 = 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 = function RawGitako(props) { freeze={showSettings} accessToken={accessToken} loadWithPJAX={loadWithPJAX} - config={configContext.val} + config={configContext.value} defer={defer} /> ) diff --git a/src/components/SimpleToggleField.tsx b/src/components/SimpleToggleField.tsx index 52c75ba..343aae0 100644 --- a/src/components/SimpleToggleField.tsx +++ b/src/components/SimpleToggleField.tsx @@ -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 ( { 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)} diff --git a/src/components/ToggleShowButton.tsx b/src/components/ToggleShowButton.tsx index adeb194..4a07863 100644 --- a/src/components/ToggleShowButton.tsx +++ b/src/components/ToggleShowButton.tsx @@ -11,7 +11,7 @@ type Props = { export function ToggleShowButton({ error, onClick }: Props) { const ref = React.useRef(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 (
+
) : (
@@ -110,8 +110,8 @@ export function AccessTokenSettings(props: React.PropsWithChildren) { 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) {
)} - {accessTokenHint && !focusInput.val && {accessTokenHint}} + {accessTokenHint && !focusInput.value && {accessTokenHint}} ) } diff --git a/src/components/settings/FileTreeSettings.tsx b/src/components/settings/FileTreeSettings.tsx index 3b2dbaf..72c3ca7 100644 --- a/src/components/settings/FileTreeSettings.tsx +++ b/src/components/settings/FileTreeSettings.tsx @@ -49,11 +49,11 @@ export function FileTreeSettings(props: React.PropsWithChildren) { id="recursive-toggle-folder" options={recursiveToggleFolderOptions} onChange={v => { - configContext.set({ + configContext.onChange({ recursiveToggleFolder: v, }) }} - value={configContext.val.recursiveToggleFolder} + value={configContext.value.recursiveToggleFolder} > @@ -61,11 +61,11 @@ export function FileTreeSettings(props: React.PropsWithChildren) { id="file-tree-icons" options={iconOptions} onChange={v => { - configContext.set({ + configContext.onChange({ icons: v, }) }} - value={configContext.val.icons} + value={configContext.value.icons} /> ('') - const { val: reloadHint } = useReloadHint + const useReloadHint = useStateIO('') + const { value: reloadHint } = useReloadHint const moreFields: SimpleField[] = platform === GitHub diff --git a/src/components/settings/SidebarSettings.tsx b/src/components/settings/SidebarSettings.tsx index 157607c..8f805b0 100644 --- a/src/components/settings/SidebarSettings.tsx +++ b/src/components/settings/SidebarSettings.tsx @@ -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[] = [ export function SidebarSettings(props: React.PropsWithChildren) { 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 ( @@ -43,24 +43,24 @@ export function SidebarSettings(props: React.PropsWithChildren) { 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) => { 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 ? (