From 573350549c6d5204b32df03ba66399709dfb24cf Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Mon, 4 Nov 2019 20:49:30 +0800 Subject: [PATCH 01/98] 0.6.0 --- package.json | 2 +- src/manifest.json | 20 +++++++++++++++----- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 094a2cc..5fb2e6a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gitako", - "version": "0.5.15", + "version": "0.6.0", "description": "The missing part of GitHub.", "repository": "https://github.com/EnixCoda/Gitako", "author": "EnixCoda", diff --git a/src/manifest.json b/src/manifest.json index 9a8f6e4..aafe65d 100644 --- a/src/manifest.json +++ b/src/manifest.json @@ -1,18 +1,28 @@ { "manifest_version": 2, "name": "Gitako - Github file tree", - "version": "0.5.15", + "version": "0.6.0", "description": "The missing part of GitHub.", "author": "EnixCoda", "icons": { "128": "icons/Gitako-128x128.png" }, "homepage_url": "https://github.com/EnixCoda/Gitako", - "permissions": ["storage", "*://*.github.com/*", "*://*.sentry.io/*"], + "permissions": [ + "storage", + "*://*.github.com/*", + "*://*.sentry.io/*" + ], "content_scripts": [ { - "matches": ["https://github.com/*"], - "js": ["firefox-shim.js", "browser-polyfill.js", "content.js"] + "matches": [ + "https://github.com/*" + ], + "js": [ + "firefox-shim.js", + "browser-polyfill.js", + "content.js" + ] } ] -} +} \ No newline at end of file From 75e914de404e5460780500d4b5afc97a9c121238 Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Tue, 5 Nov 2019 01:25:46 +0800 Subject: [PATCH 02/98] fix: toggle show button could not expand when hover --- src/content.less | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/src/content.less b/src/content.less index 22fb21b..25ad4a5 100644 --- a/src/content.less +++ b/src/content.less @@ -92,8 +92,6 @@ justify-content: center; align-items: center; cursor: pointer; - width: 30px; - height: 30px; padding: 4px; will-change: transform; border: 1px solid transparent; @@ -141,8 +139,8 @@ .action-icon { color: #666666; - width: 100%; - height: 100%; + width: 20px; + height: 20px; text-align: center; transition: all @animation-duration ease; .octicon { @@ -151,21 +149,22 @@ } } - &:hover { - .error-message { - display: inline; - } - } - &.error { .action-icon { - color: #cb2431; + color: #d73a49; } } .error-message { display: none; - margin: 0 4px; + margin-left: 4px; + font-size: 12px; + } + + &:hover { + .error-message { + display: inline; + } } } From aae82fbd2eb754a7fbbd77892016dd279e68b0f6 Mon Sep 17 00:00:00 2001 From: Enix Date: Tue, 5 Nov 2019 14:27:52 +0800 Subject: [PATCH 03/98] fix: catch oauth error --- src/components/SettingsBar.tsx | 29 +++++++++++++++++------------ src/utils/general.ts | 7 ++++--- 2 files changed, 21 insertions(+), 15 deletions(-) diff --git a/src/components/SettingsBar.tsx b/src/components/SettingsBar.tsx index 856a384..1aa686d 100644 --- a/src/components/SettingsBar.tsx +++ b/src/components/SettingsBar.tsx @@ -1,3 +1,4 @@ +import { raiseError } from 'analytics' import Icon from 'components/Icon' import { oauth } from 'env' import * as React from 'react' @@ -110,19 +111,23 @@ export default class SettingsBar extends React.PureComponent { } private async trySetUpAccessTokenWithCode() { - const search = parseURLSearch() - if ('code' in search) { - const res = await JSONRequest('https://github.com/login/oauth/access_token', { - code: search.code, - client_id: oauth.clientId, - client_secret: oauth.clientSecret, - }) - const { access_token: accessToken, scope } = res - if (scope !== 'repo' || !accessToken) { - throw new Error(`Cannot resolve token response: '${JSON.stringify(res)}'`) + try { + const search = parseURLSearch() + if ('code' in search) { + const res = await JSONRequest('https://github.com/login/oauth/access_token', { + code: search.code, + client_id: oauth.clientId, + client_secret: oauth.clientSecret, + }) + const { access_token: accessToken, scope } = res + if (scope !== 'repo' || !accessToken) { + throw new Error(`Cannot resolve token response: '${JSON.stringify(res)}'`) + } + window.history.pushState({}, 'removed code', window.location.pathname.replace(/#.*$/, '')) + this.setState({ accessToken }, () => this.saveToken('')) } - window.history.pushState({}, 'removed code', window.location.pathname.replace(/#.*$/, '')) - this.setState({ accessToken }, () => this.saveToken('')) + } catch (err) { + raiseError(err) } } diff --git a/src/utils/general.ts b/src/utils/general.ts index 9161a75..9fc3281 100644 --- a/src/utils/general.ts +++ b/src/utils/general.ts @@ -113,9 +113,8 @@ export function parseURLSearch(search: string = window.location.search) { return parsed } -export async function JSONRequest(url: string, data: any, method = 'post') { +export async function JSONRequest(url: string, data: any, extra: RequestInit = { method: 'post' }) { return (await fetch(url, { - method, mode: 'cors', cache: 'no-cache', credentials: 'same-origin', @@ -124,8 +123,10 @@ export async function JSONRequest(url: string, data: any, method = 'post') { Accept: 'application/json', }, redirect: 'follow', - referrer: 'no-referrer', + referrerPolicy: 'no-referrer', + method: extra.method || 'post', body: JSON.stringify(data), + ...extra, })).json() } From 033c97b648e66f33d3a9cbc3a7b3c6b1f8cd6b21 Mon Sep 17 00:00:00 2001 From: Enix Date: Tue, 5 Nov 2019 14:51:06 +0800 Subject: [PATCH 04/98] 0.6.1 --- package.json | 2 +- src/manifest.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 5fb2e6a..cdc4f4f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gitako", - "version": "0.6.0", + "version": "0.6.1", "description": "The missing part of GitHub.", "repository": "https://github.com/EnixCoda/Gitako", "author": "EnixCoda", diff --git a/src/manifest.json b/src/manifest.json index aafe65d..78d41c0 100644 --- a/src/manifest.json +++ b/src/manifest.json @@ -1,7 +1,7 @@ { "manifest_version": 2, "name": "Gitako - Github file tree", - "version": "0.6.0", + "version": "0.6.1", "description": "The missing part of GitHub.", "author": "EnixCoda", "icons": { From e432207baf8718eae989446cfbb1987af84a20e9 Mon Sep 17 00:00:00 2001 From: Enix Date: Wed, 6 Nov 2019 11:13:01 +0800 Subject: [PATCH 05/98] refactor: stop reporting submodule-not-found error --- src/driver/core/FileExplorer.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/driver/core/FileExplorer.ts b/src/driver/core/FileExplorer.ts index 1b37705..ba075c5 100644 --- a/src/driver/core/FileExplorer.ts +++ b/src/driver/core/FileExplorer.ts @@ -1,4 +1,3 @@ -import { raiseError } from 'analytics' import { Props } from 'components/FileExplorer' import { GetCreatedMethod, MethodCreator } from 'driver/connect' import * as ini from 'ini' @@ -118,7 +117,9 @@ function handleParsed(root: TreeNode, parsed: Parsed) { node.accessDenied = true } } else { - raiseError(new Error(`Sub-module node not found`), { path }) + // It turns out that we did not miss any submodule after a lot of tests. + // Turning this off. + // raiseError(new Error(`Submodule node not found`), { path }) } } else { handleParsed(root, value as Parsed) From c10b1950c091fb7d4cb5c4735ead52c7938e1ce4 Mon Sep 17 00:00:00 2001 From: Enix Date: Thu, 7 Nov 2019 10:45:01 +0800 Subject: [PATCH 06/98] build: push tags --- scripts/roll.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/roll.sh b/scripts/roll.sh index 1a27d27..bff0774 100755 --- a/scripts/roll.sh +++ b/scripts/roll.sh @@ -7,7 +7,7 @@ VERSION=v$(node scripts/get-version.js) echo "Got version $VERSION" # sentry -git push # make sure sentry can retrieve current commit on remote +git push --tags # make sure sentry can retrieve current commit on remote yarn sentry-cli releases new "$VERSION" yarn sentry-cli releases set-commits "$VERSION" --auto yarn sentry-cli releases files "$VERSION" upload-sourcemaps dist --no-rewrite From 4256211dbe9503708f329889cb8b75a82cac7970 Mon Sep 17 00:00:00 2001 From: Enix Date: Fri, 8 Nov 2019 14:22:15 +0800 Subject: [PATCH 07/98] build: update command and script --- package.json | 6 +++--- scripts/{roll.sh => release.sh} | 0 2 files changed, 3 insertions(+), 3 deletions(-) rename scripts/{roll.sh => release.sh} (100%) diff --git a/package.json b/package.json index cdc4f4f..2c37274 100644 --- a/package.json +++ b/package.json @@ -9,10 +9,10 @@ "scripts": { "start": "webpack --watch", "debug-firefox": "web-ext run -s dist", - "postversion": "node scripts/version.js && npm run roll", - "build": "NODE_ENV=production webpack", "analyse-bundle": "ANALYSE= NODE_ENV=production webpack", - "roll": "./scripts/roll.sh" + "build": "NODE_ENV=production webpack", + "postversion": "node scripts/version.js", + "roll": "./scripts/release.sh" }, "dependencies": { "@primer/octicons": "^9.1.1", diff --git a/scripts/roll.sh b/scripts/release.sh similarity index 100% rename from scripts/roll.sh rename to scripts/release.sh From a03d28bcd61028921cd9ce865f5e63a0a301848b Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Fri, 8 Nov 2019 15:44:58 +0800 Subject: [PATCH 08/98] build: use raw octicons --- .gitignore | 1 - package.json | 2 +- scripts/generate-octicon.js | 34 ---------------------------------- src/content.less | 6 +++--- webpack.config.js | 1 - yarn.lock | 8 ++++---- 6 files changed, 8 insertions(+), 44 deletions(-) delete mode 100644 scripts/generate-octicon.js diff --git a/.gitignore b/.gitignore index 92b0d2b..92bb6eb 100644 --- a/.gitignore +++ b/.gitignore @@ -3,5 +3,4 @@ node_modules tmp dist -src/assets/icons/octicons yarn-error.log diff --git a/package.json b/package.json index 2c37274..844282f 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ "roll": "./scripts/release.sh" }, "dependencies": { - "@primer/octicons": "^9.1.1", + "@primer/octicons": "^9.2.0", "@primer/octicons-react": "^9.2.0", "@sentry/browser": "^5.5.0", "@types/firefox-webext-browser": "^70.0.1", diff --git a/scripts/generate-octicon.js b/scripts/generate-octicon.js deleted file mode 100644 index fcbf236..0000000 --- a/scripts/generate-octicon.js +++ /dev/null @@ -1,34 +0,0 @@ -const octicons = require('@primer/octicons') -const fs = require('fs') -const path = require('path') - -const pathToIconsFolder = path.resolve(__dirname, `../src/assets/icons/octicons`) - -function generateIconSVGFiles() { - Object.values(octicons).forEach(icon => { - fs.writeFile( - `${pathToIconsFolder}/${icon.symbol}.svg`, - icon.toSVG({ xmlns: 'http://www.w3.org/2000/svg' }), - err => { - if (err) throw err - }, - ) - }) -} - -fs.exists(pathToIconsFolder, exists => { - if (exists) { - fs.lstat(pathToIconsFolder, (err, stats) => { - if (err) throw err - if (!stats.isDirectory()) { - throw new Error(`${pathToIconsFolder} is not a folder!`) - } - generateIconSVGFiles() - }) - } else { - fs.mkdir(pathToIconsFolder, err => { - if (err) throw err - generateIconSVGFiles() - }) - } -}) diff --git a/src/content.less b/src/content.less index 25ad4a5..1c6fdbc 100644 --- a/src/content.less +++ b/src/content.less @@ -56,18 +56,18 @@ width: 100%; height: 100%; display: block; - background-image: url('./assets/icons/octicons/clippy.svg?inline'); + background-image: url('~@primer/octicons/build/svg/clippy.svg?inline'); background-position: center; background-repeat: no-repeat; } &.success { .icon { - background-image: url('./assets/icons/octicons/check.svg?inline'); + background-image: url('~@primer/octicons/build/svg/check.svg?inline'); } } &.fail { .icon { - background-image: url('./assets/icons/octicons/x.svg?inline'); + background-image: url('~@primer/octicons/build/svg/x.svg?inline'); } } } diff --git a/webpack.config.js b/webpack.config.js index 773dfd5..02c11e1 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -81,7 +81,6 @@ module.exports = { test: /\.svg$/, resourceQuery: /inline/, loader: ['url-loader'], - include: [srcPath], }, { test: /\.json$/, diff --git a/yarn.lock b/yarn.lock index b297b89..39ce74f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -810,10 +810,10 @@ dependencies: prop-types "^15.6.1" -"@primer/octicons@^9.1.1": - version "9.1.1" - resolved "https://registry.yarnpkg.com/@primer/octicons/-/octicons-9.1.1.tgz#a72a59e8ec77521cc2eefb36c345f780a61c79fb" - integrity sha512-7EGM0+Kx39bIgaYr9bTCzFvBCxm+fqh/YJIoSns8zfCwss32ZJ2GDP3024UH709VQtM5cKFU4JcIYPHyGdSfIg== +"@primer/octicons@^9.2.0": + version "9.2.0" + resolved "https://registry.yarnpkg.com/@primer/octicons/-/octicons-9.2.0.tgz#705783d57335c4ffbde4650e4d22302947208c6e" + integrity sha512-3vv7bBqVUHhU7ChpmQhmzz3WmHEKKSk9z/xEK5KnU8Egh96RoqKIim07geRj825ISa+IWj9cPdOA1ShqW5k3Yw== dependencies: object-assign "^4.1.1" From 0458fc36f031d913399380872ebf3a3d68f23c39 Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Fri, 8 Nov 2019 15:45:17 +0800 Subject: [PATCH 09/98] build: update dependencies --- package.json | 14 +++++++------- yarn.lock | 8 ++++---- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/package.json b/package.json index 844282f..b034a84 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,12 @@ "@primer/octicons-react": "^9.2.0", "@sentry/browser": "^5.5.0", "@types/firefox-webext-browser": "^70.0.1", + "@types/ini": "^1.3.30", + "@types/js-base64": "^2.3.1", + "@types/node": "^11.10.4", + "@types/nprogress": "^0.0.29", + "@types/react": "^16.8.24", + "@types/react-dom": "^16.8.5", "@types/react-window": "^1.8.1", "ini": "^1.3.5", "js-base64": "^2.5.1", @@ -38,12 +44,6 @@ "@babel/preset-react": "^7.0.0", "@babel/preset-typescript": "^7.3.3", "@sentry/cli": "^1.47.1", - "@types/ini": "^1.3.30", - "@types/js-base64": "^2.3.1", - "@types/node": "^11.10.4", - "@types/nprogress": "^0.0.29", - "@types/react": "^16.8.24", - "@types/react-dom": "^16.8.5", "babel-loader": "^8.0.5", "copy-webpack-plugin": "^5.0.0", "css-loader": "^2.1.0", @@ -55,7 +55,7 @@ "less": "^3.9.0", "less-loader": "^4.0.5", "style-loader": "^0.23.1", - "typescript": "^3.6.3", + "typescript": "^3.7.2", "uglifyjs-webpack-plugin": "^2.1.2", "url-loader": "^1.1.2", "web-ext": "^3.2.0", diff --git a/yarn.lock b/yarn.lock index 39ce74f..4ab28d4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7634,10 +7634,10 @@ typedarray@^0.0.6: resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= -typescript@^3.6.3: - version "3.6.3" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.6.3.tgz#fea942fabb20f7e1ca7164ff626f1a9f3f70b4da" - integrity sha512-N7bceJL1CtRQ2RiG0AQME13ksR7DiuQh/QehubYcghzv20tnh+MQnQIuJddTmsbqYj+dztchykemz0zFzlvdQw== +typescript@^3.7.2: + version "3.7.2" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.7.2.tgz#27e489b95fa5909445e9fef5ee48d81697ad18fb" + integrity sha512-ml7V7JfiN2Xwvcer+XAf2csGO1bPBdRbFCkYBczNZggrBZ9c7G3riSUeJmqEU5uOtXNPMhE3n+R4FA/3YOAWOQ== uglify-js@^3.6.0: version "3.6.0" From 0fc3b263375f7fe2e998f69cb2320326d0e81eb3 Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Fri, 8 Nov 2019 16:12:15 +0800 Subject: [PATCH 10/98] build: makefile --- Makefile | 21 +++++++++++++++++++++ package.json | 2 +- scripts/release.sh | 18 ------------------ 3 files changed, 22 insertions(+), 19 deletions(-) create mode 100755 Makefile delete mode 100755 scripts/release.sh diff --git a/Makefile b/Makefile new file mode 100755 index 0000000..a9c4084 --- /dev/null +++ b/Makefile @@ -0,0 +1,21 @@ +build: + rm -rf dist + yarn build + +upload-for-analytics: + VERSION=v$(node scripts/get-version.js) + # make sure sentry can retrieve current commit on remote + git push --tags + yarn sentry-cli releases new "$(VERSION)" + yarn sentry-cli releases set-commits "$(VERSION)" --auto + yarn sentry-cli releases files "$(VERSION)" upload-sourcemaps dist --no-rewrite + yarn sentry-cli releases finalize "$(VERSION)" + +compress: + rm -f dist/gitako.zip + cd dist && zip -r gitako.zip * -x *.map + +release: + $(MAKE) build + $(MAKE) upload-for-analytics + $(MAKE) compress diff --git a/package.json b/package.json index b034a84..2f45dc2 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "analyse-bundle": "ANALYSE= NODE_ENV=production webpack", "build": "NODE_ENV=production webpack", "postversion": "node scripts/version.js", - "roll": "./scripts/release.sh" + "roll": "make release" }, "dependencies": { "@primer/octicons": "^9.2.0", diff --git a/scripts/release.sh b/scripts/release.sh deleted file mode 100755 index bff0774..0000000 --- a/scripts/release.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/sh -rm -rf dist -yarn build - -GIT_SHA=$(git rev-parse HEAD) -VERSION=v$(node scripts/get-version.js) -echo "Got version $VERSION" - -# sentry -git push --tags # make sure sentry can retrieve current commit on remote -yarn sentry-cli releases new "$VERSION" -yarn sentry-cli releases set-commits "$VERSION" --auto -yarn sentry-cli releases files "$VERSION" upload-sourcemaps dist --no-rewrite -yarn sentry-cli releases finalize "$VERSION" - -cd dist -rm -f ./gitako.zip -zip -r gitako.zip * -x *.map From 405d863913c88a78e62b5e88ad6ef3744993bd6b Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Fri, 8 Nov 2019 17:01:39 +0800 Subject: [PATCH 11/98] build: sanitize build scope, pass version through env --- package.json | 4 ++-- src/analytics.ts | 5 ++--- src/components/SettingsBar.tsx | 5 ++--- src/env.ts | 2 ++ webpack.config.js | 15 ++++++--------- 5 files changed, 14 insertions(+), 17 deletions(-) diff --git a/package.json b/package.json index 2f45dc2..b5d5721 100644 --- a/package.json +++ b/package.json @@ -7,10 +7,10 @@ "license": "MIT", "private": true, "scripts": { - "start": "webpack --watch", + "start": "VERSION=dev-v$(node scripts/get-version.js) webpack --watch", "debug-firefox": "web-ext run -s dist", "analyse-bundle": "ANALYSE= NODE_ENV=production webpack", - "build": "NODE_ENV=production webpack", + "build": "VERSION=v$(node scripts/get-version.js) NODE_ENV=production webpack", "postversion": "node scripts/version.js", "roll": "make release" }, diff --git a/src/analytics.ts b/src/analytics.ts index 492c2f2..0fdf838 100644 --- a/src/analytics.ts +++ b/src/analytics.ts @@ -1,14 +1,13 @@ import * as Sentry from '@sentry/browser' import { Middleware } from 'driver/connect.js' -import { IN_PRODUCTION_MODE } from 'env' -import { version } from '../package.json' +import { IN_PRODUCTION_MODE, VERSION } from 'env' const PUBLIC_KEY = 'd22ec5c9cc874539a51c78388c12e3b0' const PROJECT_ID = '1406497' const sentryOptions: Sentry.BrowserOptions = { dsn: `https://${PUBLIC_KEY}@sentry.io/${PROJECT_ID}`, - release: `v${version}`, + release: VERSION, environment: IN_PRODUCTION_MODE ? 'production' : 'development', // Not safe to activate all integrations in non-Chrome environments where Gitako may not run in top context // https://docs.sentry.io/platforms/javascript/#sdk-integrations diff --git a/src/components/SettingsBar.tsx b/src/components/SettingsBar.tsx index 1aa686d..71ff569 100644 --- a/src/components/SettingsBar.tsx +++ b/src/components/SettingsBar.tsx @@ -1,11 +1,10 @@ import { raiseError } from 'analytics' import Icon from 'components/Icon' -import { oauth } from 'env' +import { oauth, VERSION } from 'env' import * as React from 'react' import configHelper, { Config, configKeys } from 'utils/configHelper' import { friendlyFormatShortcut, JSONRequest, parseURLSearch } from 'utils/general' import keyHelper from 'utils/keyHelper' -import { version } from '../../package.json' const WIKI_HOME_LINK = 'https://github.com/EnixCoda/Gitako/wiki' const wikiLinks = { @@ -348,7 +347,7 @@ export default class SettingsBar extends React.PureComponent { target={'_blank'} title={'Check out new features!'} > - v{version} + {VERSION} {activated ? ( Date: Sat, 9 Nov 2019 00:50:27 +0800 Subject: [PATCH 12/98] fix: update DOM structure --- src/utils/DOMHelper.ts | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/utils/DOMHelper.ts b/src/utils/DOMHelper.ts index 31d681a..3b76393 100644 --- a/src/utils/DOMHelper.ts +++ b/src/utils/DOMHelper.ts @@ -154,7 +154,7 @@ const PAGE_TYPES = { * TODO: distinguish type 'preview' */ function getCurrentPageType() { - const blobWrapperSelector = '.repository-content .file .blob-wrapper table' + const blobWrapperSelector = '.repository-content .blob-wrapper table' const readmeSelector = '.repository-content .readme' return ( $(blobWrapperSelector, () => PAGE_TYPES.RAW_TEXT) || @@ -188,7 +188,7 @@ function attachCopyFileBtn() { */ function getCodeElement() { if (getCurrentPageType() === PAGE_TYPES.RAW_TEXT) { - const codeContentSelector = '.repository-content .file .data table' + const codeContentSelector = '.repository-content .data table' const codeContentElement = $(codeContentSelector) if (!codeContentElement) { raiseError(new Error('cannot find code content element')) @@ -209,10 +209,8 @@ function attachCopyFileBtn() { if (getCurrentPageType() === PAGE_TYPES.RAW_TEXT) { const btnGroupSelector = [ - // the button group next to navigation bar - '.repository-content .file-navigation.js-zeroclipboard-container .BtnGroup', // the button group in file content header - '.repository-content .file .file-header .file-actions .BtnGroup', + '.repository-content > .Box > .Box-header .BtnGroup', ].join(', ') const btnGroups = document.querySelectorAll(btnGroupSelector) From 54d7c675d7b51b83d1f8d98dfeab5913862ab6a1 Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Sat, 9 Nov 2019 00:50:54 +0800 Subject: [PATCH 13/98] feat: CopyFileButton --- src/components/CopyFileButton.tsx | 38 +++++++++++++++ src/utils/DOMHelper.ts | 78 +++++++++++++------------------ 2 files changed, 71 insertions(+), 45 deletions(-) create mode 100644 src/components/CopyFileButton.tsx diff --git a/src/components/CopyFileButton.tsx b/src/components/CopyFileButton.tsx new file mode 100644 index 0000000..11bcda4 --- /dev/null +++ b/src/components/CopyFileButton.tsx @@ -0,0 +1,38 @@ +import * as React from 'react' +import { copyElementContent, getCodeElement } from 'utils/DOMHelper' + +type Props = {} + +export function CopyFileButton(props: React.PropsWithChildren) { + const contents = { + success: 'Success!', + error: 'Copy failed!', + normal: 'Copy file', + } + const [content, setContent] = React.useState(contents.normal) + React.useEffect(() => { + if (content !== contents.normal) { + const timer = setTimeout(() => { + setContent(contents.normal) + }, 1000) + return () => clearTimeout(timer) + } + }, [content]) + return ( + { + const codeElement = getCodeElement() + if (codeElement) { + if (copyElementContent(codeElement)) { + setContent(contents.success) + } else { + setContent(contents.error) + } + } + }} + > + {content} + + ) +} diff --git a/src/utils/DOMHelper.ts b/src/utils/DOMHelper.ts index 3b76393..0919f72 100644 --- a/src/utils/DOMHelper.ts +++ b/src/utils/DOMHelper.ts @@ -1,10 +1,12 @@ /** * this helper helps manipulating DOM */ - import { raiseError } from 'analytics' +import { CopyFileButton } from 'components/CopyFileButton' import * as NProgress from 'nprogress' import * as PJAX from 'pjax' +import * as React from 'react' +import * as ReactDOM from 'react-dom' NProgress.configure({ showSpinner: false }) @@ -92,7 +94,6 @@ function getCurrentBranch() { /** * add the logo element into DOM - * */ function insertLogoMountPoint() { const logoSelector = '.gitako .gitako-logo' @@ -178,57 +179,42 @@ function getRepoPageType() { }) } +/** + * get text content of raw text content + */ +function getCodeElement() { + if (getCurrentPageType() === PAGE_TYPES.RAW_TEXT) { + const codeContentSelector = '.repository-content .data table' + const codeContentElement = $(codeContentSelector) + if (!codeContentElement) { + raiseError(new Error('cannot find code content element')) + } + return codeContentElement + } +} + /** * add copy file content buttons to button groups * click these buttons will copy file content to clipboard */ function attachCopyFileBtn() { - /** - * get text content of raw text content - */ - function getCodeElement() { - if (getCurrentPageType() === PAGE_TYPES.RAW_TEXT) { - const codeContentSelector = '.repository-content .data table' - const codeContentElement = $(codeContentSelector) - if (!codeContentElement) { - raiseError(new Error('cannot find code content element')) - } - return codeContentElement - } - } - - /** - * change inner text of copy file button to give feedback - * @param {element} copyFileBtn - * @param {string} text - */ - function setTempCopyFileBtnText(copyFileBtn: HTMLButtonElement, text: string) { - copyFileBtn.innerText = text - window.setTimeout(() => (copyFileBtn.innerText = 'Copy file'), 1000) - } - if (getCurrentPageType() === PAGE_TYPES.RAW_TEXT) { - const btnGroupSelector = [ // the button group in file content header - '.repository-content > .Box > .Box-header .BtnGroup', - ].join(', ') - const btnGroups = document.querySelectorAll(btnGroupSelector) + const buttonGroupSelector = '.repository-content > .Box > .Box-header .BtnGroup' + const buttonGroups = document.querySelectorAll(buttonGroupSelector) - btnGroups.forEach(btnGroup => { - const copyFileBtn = document.createElement('button') - copyFileBtn.classList.add('btn', 'btn-sm', 'BtnGroup-item', 'copy-file-btn') - copyFileBtn.innerText = 'Copy file' - copyFileBtn.addEventListener('click', () => { - const codeElement = getCodeElement() - if (codeElement) { - if (copyElementContent(codeElement)) { - setTempCopyFileBtnText(copyFileBtn, 'Success!') - } else { - setTempCopyFileBtnText(copyFileBtn, 'Copy failed!') - } - } - }) - btnGroup.insertBefore(copyFileBtn, btnGroup.lastChild) + if (buttonGroups.length === 0) { + raiseError(new Error(`No button groups found`)) + } + + buttonGroups.forEach(buttonGroup => { + if (!buttonGroup.lastElementChild) return + const portal = ReactDOM.createPortal(React.createElement(CopyFileButton), buttonGroup) + // creating a element for mounting button into button group, somehow hack + const seedElementForButton = document.createElement('a') + buttonGroup.appendChild(seedElementForButton) + ReactDOM.render(portal, seedElementForButton) + buttonGroup.removeChild(seedElementForButton) }) } } @@ -384,9 +370,11 @@ export default { loadWithPJAX, attachCopyFileBtn, attachCopySnippet, + copyElementContent, decorateGitHubPageContent, focusSearchInput, focusFileExplorer, + getCodeElement, getCurrentPageType, getRepoPageType, insertLogoMountPoint, From d9aaf72b03402325eab352e486b2787c27540ee2 Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Sat, 9 Nov 2019 01:08:12 +0800 Subject: [PATCH 14/98] build: deprecate default export --- src/components/PJAXLink.tsx | 2 +- src/content.tsx | 7 ++-- src/driver/core/FileExplorer.ts | 2 +- src/driver/core/SideBar.ts | 2 +- src/utils/DOMHelper.ts | 62 +++++++++++---------------------- 5 files changed, 26 insertions(+), 49 deletions(-) diff --git a/src/components/PJAXLink.tsx b/src/components/PJAXLink.tsx index bc0e7d4..1b78927 100644 --- a/src/components/PJAXLink.tsx +++ b/src/components/PJAXLink.tsx @@ -1,5 +1,5 @@ import * as React from 'react' -import DOMHelper from 'utils/DOMHelper' +import * as DOMHelper from 'utils/DOMHelper' type Props

= { to: string diff --git a/src/content.tsx b/src/content.tsx index 06b7c41..3ba5177 100644 --- a/src/content.tsx +++ b/src/content.tsx @@ -1,9 +1,8 @@ -import * as React from 'react' -import * as ReactDOM from 'react-dom' +import { withErrorLog } from 'analytics' import Gitako from 'components/Gitako' import { addMiddleware } from 'driver/connect' -import { withErrorLog } from 'analytics' - +import * as React from 'react' +import * as ReactDOM from 'react-dom' import './content.less' addMiddleware(withErrorLog) diff --git a/src/driver/core/FileExplorer.ts b/src/driver/core/FileExplorer.ts index ba075c5..60489cb 100644 --- a/src/driver/core/FileExplorer.ts +++ b/src/driver/core/FileExplorer.ts @@ -2,7 +2,7 @@ import { Props } from 'components/FileExplorer' import { GetCreatedMethod, MethodCreator } from 'driver/connect' import * as ini from 'ini' import { Base64 } from 'js-base64' -import DOMHelper from 'utils/DOMHelper' +import * as DOMHelper from 'utils/DOMHelper' import { findNode, searchKeyToRegexps } from 'utils/general' import GitHubHelper, { BlobData } from 'utils/GitHubHelper' import treeParser from 'utils/treeParser' diff --git a/src/driver/core/SideBar.ts b/src/driver/core/SideBar.ts index d738340..15b9ecf 100644 --- a/src/driver/core/SideBar.ts +++ b/src/driver/core/SideBar.ts @@ -1,7 +1,7 @@ import { Props } from 'components/SideBar' import { GetCreatedMethod, MethodCreator } from 'driver/connect' import configHelper, { Config, configKeys } from 'utils/configHelper' -import DOMHelper from 'utils/DOMHelper' +import * as DOMHelper from 'utils/DOMHelper' import GitHubHelper, { API_RATE_LIMIT, BAD_CREDENTIALS, diff --git a/src/utils/DOMHelper.ts b/src/utils/DOMHelper.ts index 0919f72..0917b96 100644 --- a/src/utils/DOMHelper.ts +++ b/src/utils/DOMHelper.ts @@ -13,7 +13,7 @@ NProgress.configure({ showSpinner: false }) /** * when gitako is ready, make page's header narrower */ -function markGitakoReadyState() { +export function markGitakoReadyState() { const readyClassName = 'gitako-ready' document.body.classList.add(readyClassName) } @@ -23,7 +23,7 @@ function markGitakoReadyState() { * otherwise, hide the space */ export const bodySpacingClassName = 'with-gitako-spacing' -function setBodyIndent(shouldShowGitako: boolean) { +export function setBodyIndent(shouldShowGitako: boolean) { if (shouldShowGitako) { document.body.classList.add(bodySpacingClassName) } else { @@ -49,18 +49,18 @@ function $ any, O extends () => a return otherwise ? otherwise() : null } -function isInCodePage() { +export function isInCodePage() { const branchListSelector = '#branch-select-menu.branch-select-menu' return Boolean($(branchListSelector)) } -function getBranches() { +export function getBranches() { const branchSelector = '.branch-select-menu .select-menu-list > div .select-menu-item-text' const branchElements = Array.from(document.querySelectorAll(branchSelector)) return branchElements.map(element => element.innerHTML.trim()) } -function getCurrentBranch() { +export function getCurrentBranch() { const selectedBranchButtonSelector = '.repository-content .branch-select-menu summary' const branchButtonElement: HTMLElement = $(selectedBranchButtonSelector) if (branchButtonElement) { @@ -95,7 +95,7 @@ function getCurrentBranch() { /** * add the logo element into DOM */ -function insertLogoMountPoint() { +export function insertLogoMountPoint() { const logoSelector = '.gitako .gitako-logo' return $(logoSelector) || createLogoMountPoint() } @@ -111,7 +111,7 @@ function createLogoMountPoint() { * content above the file navigation bar is same for all pages of the repo * use this function to scroll down a bit to hide them */ -function scrollToRepoContent() { +export function scrollToRepoContent() { const repositoryContentSelector = '.repository-content' // do NOT use behavior: smooth here as it will scroll horizontally $(repositoryContentSelector, repositoryContentElement => @@ -128,7 +128,7 @@ const pjax = new PJAX({ forceCache: true, // TODO: merge namespace, add forceCache }) -function loadWithPJAX(URL: string) { +export function loadWithPJAX(URL: string) { NProgress.start() pjax.loadUrl(URL, { scrollTo: 0 }) } @@ -154,7 +154,7 @@ const PAGE_TYPES = { * * TODO: distinguish type 'preview' */ -function getCurrentPageType() { +export function getCurrentPageType() { const blobWrapperSelector = '.repository-content .blob-wrapper table' const readmeSelector = '.repository-content .readme' return ( @@ -166,7 +166,7 @@ function getCurrentPageType() { export const REPO_TYPE_PRIVATE = 'private' export const REPO_TYPE_PUBLIC = 'public' -function getRepoPageType() { +export function getRepoPageType() { const headerSelector = `#js-repo-pjax-container .pagehead.repohead h1` return $(headerSelector, header => { const repoPageTypes = [REPO_TYPE_PRIVATE, REPO_TYPE_PUBLIC] @@ -182,7 +182,7 @@ function getRepoPageType() { /** * get text content of raw text content */ -function getCodeElement() { +export function getCodeElement() { if (getCurrentPageType() === PAGE_TYPES.RAW_TEXT) { const codeContentSelector = '.repository-content .data table' const codeContentElement = $(codeContentSelector) @@ -197,9 +197,9 @@ function getCodeElement() { * add copy file content buttons to button groups * click these buttons will copy file content to clipboard */ -function attachCopyFileBtn() { +export function attachCopyFileBtn() { if (getCurrentPageType() === PAGE_TYPES.RAW_TEXT) { - // the button group in file content header + // the button group in file content header const buttonGroupSelector = '.repository-content > .Box > .Box-header .BtnGroup' const buttonGroups = document.querySelectorAll(buttonGroupSelector) @@ -224,7 +224,7 @@ function attachCopyFileBtn() { * @param {element} element * @returns {boolean} whether copy is successful */ -function copyElementContent(element: Element) { +export function copyElementContent(element: Element) { let selection = window.getSelection() if (selection) selection.removeAllRanges() const range = document.createRange() @@ -285,7 +285,7 @@ function createClippy() { const clippy = createClippy() let currentCodeSnippetElement: Element -function attachCopySnippet() { +export function attachCopySnippet() { const readmeSelector = '.repository-content div#readme' return $(readmeSelector, () => { const readmeArticleSelector = '.repository-content div#readme article' @@ -325,14 +325,14 @@ function attachCopySnippet() { /** * focus to side bar, user will be able to manipulate it with keyboard */ -function focusFileExplorer() { +export function focusFileExplorer() { const sideBarContentSelector = '.gitako-side-bar .file-explorer' $(sideBarContentSelector, sideBarElement => { if (sideBarElement instanceof HTMLElement) sideBarElement.focus() }) } -function focusSearchInput() { +export function focusSearchInput() { const searchInputSelector = '.search-input' $(searchInputSelector, searchInputElement => { if ( @@ -347,7 +347,7 @@ function focusSearchInput() { /** * a combination of few above functions */ -function decorateGitHubPageContent({ +export function decorateGitHubPageContent({ copyFileButton, copySnippetButton, }: { @@ -358,32 +358,10 @@ function decorateGitHubPageContent({ if (copySnippetButton) attachCopySnippet() } -function mountTopProgressBar() { +export function mountTopProgressBar() { NProgress.start() } -function unmountTopProgressBar() { +export function unmountTopProgressBar() { NProgress.done() } - -export default { - loadWithPJAX, - attachCopyFileBtn, - attachCopySnippet, - copyElementContent, - decorateGitHubPageContent, - focusSearchInput, - focusFileExplorer, - getCodeElement, - getCurrentPageType, - getRepoPageType, - insertLogoMountPoint, - markGitakoReadyState, - setBodyIndent, - scrollToRepoContent, - mountTopProgressBar, - unmountTopProgressBar, - isInCodePage, - getBranches, - getCurrentBranch, -} From 8ed78e2a98349feff9dd3d4d5450df41bfad2230 Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Sat, 9 Nov 2019 01:38:02 +0800 Subject: [PATCH 15/98] refactor: abandon default export --- src/components/FileExplorer.tsx | 20 +++---- src/components/Gitako.tsx | 4 +- src/components/Icon.tsx | 6 +-- src/components/LoadingIndicator.tsx | 4 +- src/components/MetaBar.tsx | 4 +- src/components/Node.tsx | 6 +-- src/components/PJAXLink.tsx | 2 +- src/components/Portal.tsx | 4 +- src/components/Resizable.tsx | 16 +++--- src/components/ResizeHandler.tsx | 4 +- src/components/SearchBar.tsx | 4 +- src/components/SettingsBar.tsx | 18 +++---- src/components/SideBar.tsx | 22 ++++---- src/components/SizeObserver.tsx | 2 +- src/components/ToggleShowButton.tsx | 7 +-- src/content.tsx | 2 +- src/driver/connect.ts | 2 +- src/driver/core/FileExplorer.ts | 55 ++++++++----------- src/driver/core/SideBar.ts | 84 ++++++++++------------------- src/driver/core/index.ts | 7 ++- src/utils/GitHubHelper.ts | 19 +++---- src/utils/URLHelper.ts | 18 ++----- src/utils/configHelper.ts | 17 ++---- src/utils/cx.ts | 2 +- src/utils/keyHelper.ts | 6 +-- src/utils/storageHelper.ts | 9 +--- src/utils/treeParser.ts | 10 ++-- src/utils/visibleNodesGenerator.ts | 2 +- 28 files changed, 142 insertions(+), 214 deletions(-) diff --git a/src/components/FileExplorer.tsx b/src/components/FileExplorer.tsx index 0caa9ec..a7b606e 100644 --- a/src/components/FileExplorer.tsx +++ b/src/components/FileExplorer.tsx @@ -1,17 +1,17 @@ -import LoadingIndicator from 'components/LoadingIndicator' -import Node from 'components/Node' -import SearchBar from 'components/SearchBar' -import connect from 'driver/connect' -import { FileExplorer as FileExplorerCore } from 'driver/core' +import { LoadingIndicator } from 'components/LoadingIndicator' +import { Node } from 'components/Node' +import { SearchBar } from 'components/SearchBar' +import { connect } from 'driver/connect' +import { FileExplorerCore } from 'driver/core' import { ConnectorState } from 'driver/core/FileExplorer' import * as React from 'react' import { FixedSizeList as List, ListChildComponentProps } from 'react-window' -import cx from 'utils/cx' +import { cx } from 'utils/cx' import { MetaData, TreeData } from 'utils/GitHubHelper' import { usePrevious } from 'utils/hooks' import { TreeNode, VisibleNodes } from 'utils/VisibleNodesGenerator' -import Icon from './Icon' -import SizeObserver from './SizeObserver' +import { Icon } from './Icon' +import { SizeObserver } from './SizeObserver' export type Props = { treeData?: TreeData @@ -22,7 +22,7 @@ export type Props = { toggleShowSettings: React.MouseEventHandler } -class FileExplorer extends React.Component { +class RawFileExplorer extends React.Component { static defaultProps: Partial = { freeze: false, searchKey: '', @@ -183,4 +183,4 @@ class FileExplorer extends React.Component { } } -export default connect(FileExplorerCore)(FileExplorer) +export const FileExplorer = connect(FileExplorerCore)(RawFileExplorer) diff --git a/src/components/Gitako.tsx b/src/components/Gitako.tsx index 5a889b2..16ba16a 100644 --- a/src/components/Gitako.tsx +++ b/src/components/Gitako.tsx @@ -1,8 +1,8 @@ import { raiseError } from 'analytics' -import SideBar from 'components/SideBar' +import { SideBar } from 'components/SideBar' import * as React from 'react' -export default class Gitako extends React.PureComponent { +export class Gitako extends React.PureComponent { componentDidCatch(error: Error, errorInfo: React.ErrorInfo) { raiseError(error, errorInfo) } diff --git a/src/components/Icon.tsx b/src/components/Icon.tsx index a128ed5..5ffd0e4 100644 --- a/src/components/Icon.tsx +++ b/src/components/Icon.tsx @@ -16,7 +16,7 @@ import Octicon, { X, } from '@primer/octicons-react' import * as React from 'react' -import cx from 'utils/cx' +import { cx } from 'utils/cx' function getSVGIconComponent( type: string, @@ -123,7 +123,7 @@ type Props = { onClick?: (event: React.MouseEvent) => void } -const Icon: React.SFC = function Icon({ type, className = undefined, ...otherProps }) { +export function Icon({ type, className = undefined, ...otherProps }: Props) { const { name, IconComponent } = getSVGIconComponent(type) const mergedClassName = cx('octicon', name) return ( @@ -135,5 +135,3 @@ const Icon: React.SFC = function Icon({ type, className = undefined, ...o ) } - -export default Icon diff --git a/src/components/LoadingIndicator.tsx b/src/components/LoadingIndicator.tsx index 20e8f5a..86bc16f 100644 --- a/src/components/LoadingIndicator.tsx +++ b/src/components/LoadingIndicator.tsx @@ -1,10 +1,10 @@ -import Icon from 'components/Icon' +import { Icon } from 'components/Icon' import * as React from 'react' type Props = { text: React.ReactNode } -export default function LoadingIndicator({ text }: Props) { +export function LoadingIndicator({ text }: Props) { return (

diff --git a/src/components/MetaBar.tsx b/src/components/MetaBar.tsx index 7175272..2808240 100644 --- a/src/components/MetaBar.tsx +++ b/src/components/MetaBar.tsx @@ -1,12 +1,12 @@ import * as React from 'react' -import { MetaData } from 'utils/GitHubHelper' import { safeTouch } from 'safe-touch' +import { MetaData } from 'utils/GitHubHelper' type Props = { metaData: MetaData } -export default function MetaBar({ metaData }: Props) { +export function MetaBar({ metaData }: Props) { const userUrl = safeTouch(metaData).api.owner.html_url() const repoUrl = safeTouch(metaData).api.html_url() return ( diff --git a/src/components/Node.tsx b/src/components/Node.tsx index 4641c50..70947d9 100644 --- a/src/components/Node.tsx +++ b/src/components/Node.tsx @@ -1,6 +1,6 @@ -import Icon from 'components/Icon' +import { Icon } from 'components/Icon' import * as React from 'react' -import cx from 'utils/cx' +import { cx } from 'utils/cx' import { OperatingSystems, os } from 'utils/general' import { TreeNode } from 'utils/VisibleNodesGenerator' @@ -24,7 +24,7 @@ type Props = { renderActions?(node: TreeNode): React.ReactNode style?: React.CSSProperties } -export default class Node extends React.PureComponent { +export class Node extends React.PureComponent { onClick: React.MouseEventHandler = event => { if ( (os === OperatingSystems.macOS && event.metaKey) || diff --git a/src/components/PJAXLink.tsx b/src/components/PJAXLink.tsx index 1b78927..680214e 100644 --- a/src/components/PJAXLink.tsx +++ b/src/components/PJAXLink.tsx @@ -6,7 +6,7 @@ type Props

= { children: React.ReactElement

} -export default function PJAXLink

({ to, children }: Props

) { +export function PJAXLink

({ to, children }: Props

) { return React.cloneElement(children, { ...children.props, onClick: () => DOMHelper.loadWithPJAX(to), diff --git a/src/components/Portal.tsx b/src/components/Portal.tsx index 04ebd72..3a8cd3e 100644 --- a/src/components/Portal.tsx +++ b/src/components/Portal.tsx @@ -5,12 +5,10 @@ type Props = { into: Element | null } -class Portal extends React.PureComponent { +export class Portal extends React.PureComponent { render() { const { into, children } = this.props if (!(into instanceof Element)) return null return ReactDOM.createPortal(children, into) } } - -export default Portal diff --git a/src/components/Resizable.tsx b/src/components/Resizable.tsx index 39133fb..b14f40a 100644 --- a/src/components/Resizable.tsx +++ b/src/components/Resizable.tsx @@ -1,10 +1,10 @@ +import { HorizontalResizeHandler } from 'components/ResizeHandler' import * as React from 'react' -import HorizontalResizeHandler from 'components/ResizeHandler' -import cx from 'utils/cx' -import { useWindowSize, useMediaStyleSheet } from 'utils/hooks' +import { configKeys, setOne } from 'utils/configHelper' +import { cx } from 'utils/cx' import { bodySpacingClassName } from 'utils/DOMHelper' -import configHelper, { configKeys } from 'utils/configHelper' import * as features from 'utils/features' +import { useMediaStyleSheet, useWindowSize } from 'utils/hooks' export type Size = number type Props = { @@ -15,11 +15,7 @@ type Props = { const MINIMAL_CONTENT_VIEWPORT_WIDTH = 100 const GITHUB_WIDTH = 1020 -export default function Resizable({ - baseSize, - className, - children, -}: React.PropsWithChildren) { +export function Resizable({ baseSize, className, children }: React.PropsWithChildren) { const [size, setSize] = React.useState(baseSize) React.useEffect(() => { @@ -36,7 +32,7 @@ export default function Resizable({ React.useEffect(() => { document.documentElement.style.setProperty('--gitako-width', size + 'px') - configHelper.setOne(configKeys.sideBarWidth, size) + setOne(configKeys.sideBarWidth, size) }, [size]) useMediaStyleSheet( diff --git a/src/components/ResizeHandler.tsx b/src/components/ResizeHandler.tsx index 9886d13..d49e562 100644 --- a/src/components/ResizeHandler.tsx +++ b/src/components/ResizeHandler.tsx @@ -1,4 +1,4 @@ -import Icon from 'components/Icon' +import { Icon } from 'components/Icon' import * as React from 'react' import { Size } from './Resizable' @@ -8,7 +8,7 @@ type Props = { style?: React.CSSProperties } -export default class HorizontalResizeHandler extends React.PureComponent { +export class HorizontalResizeHandler extends React.PureComponent { pointerDown = false startX = 0 baseSize = this.props.size diff --git a/src/components/SearchBar.tsx b/src/components/SearchBar.tsx index 252b63a..f69895d 100644 --- a/src/components/SearchBar.tsx +++ b/src/components/SearchBar.tsx @@ -1,5 +1,5 @@ import * as React from 'react' -import cx from 'utils/cx' +import { cx } from 'utils/cx' type Props = { onSearch: (searchKey: string) => void @@ -7,7 +7,7 @@ type Props = { searchKey: string } -export default function SearchBar({ onSearch, onFocus, searchKey }: Props) { +export function SearchBar({ onSearch, onFocus, searchKey }: Props) { return (

{ +export class SettingsBar extends React.PureComponent { state = { accessToken: '', accessTokenHint: '', @@ -91,7 +91,7 @@ export default class SettingsBar extends React.PureComponent { onChange: async (e: React.FormEvent) => { const { checked } = e.currentTarget const intelligentToggle = checked ? null : true - await configHelper.setOne(configKeys.intelligentToggle, intelligentToggle) + await setOne(configKeys.intelligentToggle, intelligentToggle) this.props.setIntelligentToggle(intelligentToggle) }, getValue: () => this.props.intelligentToggle === null, @@ -159,7 +159,7 @@ export default class SettingsBar extends React.PureComponent { const { onAccessTokenChange } = this.props const { accessToken } = this.state if (accessToken) { - await configHelper.setOne(configKeys.accessToken, accessToken) + await setOne(configKeys.accessToken, accessToken) onAccessTokenChange(accessToken) this.setState({ accessToken: '', @@ -170,7 +170,7 @@ export default class SettingsBar extends React.PureComponent { clearToken = async () => { const { onAccessTokenChange } = this.props - await configHelper.setOne(configKeys.accessToken, '') + await setOne(configKeys.accessToken, '') onAccessTokenChange('') this.setState({ accessToken: '' }) } @@ -178,7 +178,7 @@ export default class SettingsBar extends React.PureComponent { saveShortcut = async () => { const { onShortcutChange } = this.props const { toggleShowSideBarShortcut } = this.state - await configHelper.setOne(configKeys.shortcut, toggleShowSideBarShortcut) + await setOne(configKeys.shortcut, toggleShowSideBarShortcut) if (typeof toggleShowSideBarShortcut === 'string') { onShortcutChange(toggleShowSideBarShortcut) this.setState({ @@ -214,7 +214,7 @@ export default class SettingsBar extends React.PureComponent { ): (e: React.FormEvent) => Promise { return async e => { const enabled = e.currentTarget.checked - await configHelper.setOne(configKey, enabled) + await setOne(configKey, enabled) set(enabled) this.showReloadHint() } diff --git a/src/components/SideBar.tsx b/src/components/SideBar.tsx index 781ac8d..b39ac59 100644 --- a/src/components/SideBar.tsx +++ b/src/components/SideBar.tsx @@ -1,18 +1,18 @@ -import FileExplorer from 'components/FileExplorer' -import MetaBar from 'components/MetaBar' -import Portal from 'components/Portal' -import Resizable from 'components/Resizable' -import SettingsBar from 'components/SettingsBar' -import ToggleShowButton from 'components/ToggleShowButton' -import connect from 'driver/connect' -import { SideBar as SideBarCore } from 'driver/core' +import { FileExplorer } from 'components/FileExplorer' +import { MetaBar } from 'components/MetaBar' +import { Portal } from 'components/Portal' +import { Resizable } from 'components/Resizable' +import { SettingsBar } from 'components/SettingsBar' +import { ToggleShowButton } from 'components/ToggleShowButton' +import { connect } from 'driver/connect' +import { SideBarCore } from 'driver/core' import { ConnectorState } from 'driver/core/SideBar' import * as React from 'react' -import cx from 'utils/cx' +import { cx } from 'utils/cx' export type Props = {} -class Gitako extends React.PureComponent { +class RawGitako extends React.PureComponent { static defaultProps: Partial = { baseSize: 260, shouldShow: false, @@ -147,4 +147,4 @@ class Gitako extends React.PureComponent { } } -export default connect(SideBarCore)(Gitako) +export const SideBar = connect(SideBarCore)(RawGitako) diff --git a/src/components/SizeObserver.tsx b/src/components/SizeObserver.tsx index c3c443c..4b394c4 100644 --- a/src/components/SizeObserver.tsx +++ b/src/components/SizeObserver.tsx @@ -11,7 +11,7 @@ type Props = { children(size: Partial): React.ReactNode } & React.HTMLAttributes -export default function SizeObserver({ type = 'div', children, ...rest }: Props) { +export function SizeObserver({ type = 'div', children, ...rest }: Props) { const ref = React.useRef() const [size, setSize] = React.useState>({ diff --git a/src/components/ToggleShowButton.tsx b/src/components/ToggleShowButton.tsx index fa45455..219b06d 100644 --- a/src/components/ToggleShowButton.tsx +++ b/src/components/ToggleShowButton.tsx @@ -1,13 +1,14 @@ +import { Icon } from 'components/Icon' import * as React from 'react' -import Icon from 'components/Icon' -import cx from 'utils/cx' +import { cx } from 'utils/cx' type Props = { error?: string shouldShow: boolean toggleShowSideBar: React.MouseEventHandler } -export default function Logo({ error, shouldShow, toggleShowSideBar }: Props) { + +export function ToggleShowButton({ error, shouldShow, toggleShowSideBar }: Props) { return (
(instance: React.Component, sources: Sources): Wr return wrappedMethods } -export default function connect(mapping: Sources) { +export function connect(mapping: Sources) { return function linkComponent( ComponentClass: React.ComponentClass, ): React.ComponentClass { diff --git a/src/driver/core/FileExplorer.ts b/src/driver/core/FileExplorer.ts index 60489cb..ba82b9d 100644 --- a/src/driver/core/FileExplorer.ts +++ b/src/driver/core/FileExplorer.ts @@ -4,10 +4,11 @@ import * as ini from 'ini' import { Base64 } from 'js-base64' import * as DOMHelper from 'utils/DOMHelper' import { findNode, searchKeyToRegexps } from 'utils/general' -import GitHubHelper, { BlobData } from 'utils/GitHubHelper' -import treeParser from 'utils/treeParser' -import URLHelper from 'utils/URLHelper' -import VisibleNodesGenerator, { TreeNode, VisibleNodes } from 'utils/VisibleNodesGenerator' +import * as GitHubHelper from 'utils/GitHubHelper' +import { BlobData } from 'utils/GitHubHelper' +import * as treeParser from 'utils/treeParser' +import * as URLHelper from 'utils/URLHelper' +import { TreeNode, VisibleNodes, VisibleNodesGenerator } from 'utils/VisibleNodesGenerator' export type ConnectorState = { stateText: string @@ -49,7 +50,7 @@ let visibleNodesGenerator: VisibleNodesGenerator type BoundMethodCreator = MethodCreator -const init: BoundMethodCreator = dispatch => () => +export const init: BoundMethodCreator = dispatch => () => dispatch.call(setStateText, 'Fetching File List...') const githubSubModuleURLRegex = { @@ -127,7 +128,7 @@ function handleParsed(root: TreeNode, parsed: Parsed) { }) } -const setUpTree: BoundMethodCreator< +export const setUpTree: BoundMethodCreator< [Pick] > = dispatch => async ({ treeData, metaData, compressSingletonFolder, accessToken }) => { if (!treeData) return @@ -158,21 +159,21 @@ const setUpTree: BoundMethodCreator< dispatch.call(goTo, URLHelper.getCurrentPath(metaData.branchName)) } -const execAfterRender: BoundMethodCreator = dispatch => () => { +export const execAfterRender: BoundMethodCreator = dispatch => () => { for (const task of tasksAfterRender) { task() } tasksAfterRender.length = 0 } -const setStateText: BoundMethodCreator<[ConnectorState['stateText']]> = dispatch => ( +export const setStateText: BoundMethodCreator<[ConnectorState['stateText']]> = dispatch => ( text: string, ) => dispatch.set({ stateText: text, }) -const handleKeyDown: BoundMethodCreator<[React.KeyboardEvent]> = dispatch => event => { +export const handleKeyDown: BoundMethodCreator<[React.KeyboardEvent]> = dispatch => event => { const { searched, visibleNodes } = dispatch.get() if (!visibleNodes) return const { nodes, focusedNode, expandedNodes, depths } = visibleNodes @@ -277,16 +278,17 @@ const handleKeyDown: BoundMethodCreator<[React.KeyboardEvent]> = dispatch => eve } } -const onFocusSearchBar: BoundMethodCreator = dispatch => () => dispatch.call(focusNode, null, false) +export const onFocusSearchBar: BoundMethodCreator = dispatch => () => + dispatch.call(focusNode, null, false) -const search: BoundMethodCreator<[string]> = dispatch => searchKey => { +export const search: BoundMethodCreator<[string]> = dispatch => searchKey => { dispatch.set({ searchKey, searched: searchKey !== '' }) const regexps = searchKeyToRegexps(searchKey) visibleNodesGenerator.search(regexps) dispatch.call(updateVisibleNodes) } -const goTo: BoundMethodCreator<[string[]]> = dispatch => async currentPath => { +export const goTo: BoundMethodCreator<[string[]]> = dispatch => async currentPath => { visibleNodesGenerator.search([]) tasksAfterRender.push(() => { const nodeExpandedTo = visibleNodesGenerator.expandTo(currentPath.join('/')) @@ -298,12 +300,15 @@ const goTo: BoundMethodCreator<[string[]]> = dispatch => async currentPath => { dispatch.set({ searchKey: '', searched: false }) } -const setExpand: BoundMethodCreator<[TreeNode, boolean]> = dispatch => (node, expand = false) => { +export const setExpand: BoundMethodCreator<[TreeNode, boolean]> = dispatch => ( + node, + expand = false, +) => { visibleNodesGenerator.setExpand(node, expand) dispatch.call(focusNode, node, false) } -const toggleNodeExpansion: BoundMethodCreator<[TreeNode, boolean]> = dispatch => ( +export const toggleNodeExpansion: BoundMethodCreator<[TreeNode, boolean]> = dispatch => ( node, skipScrollToNode, ) => { @@ -312,7 +317,7 @@ const toggleNodeExpansion: BoundMethodCreator<[TreeNode, boolean]> = dispatch => tasksAfterRender.push(DOMHelper.focusFileExplorer) } -const focusNode: BoundMethodCreator<[TreeNode | null, boolean]> = dispatch => ( +export const focusNode: BoundMethodCreator<[TreeNode | null, boolean]> = dispatch => ( node: TreeNode | null, skipScroll = false, ) => { @@ -322,7 +327,7 @@ const focusNode: BoundMethodCreator<[TreeNode | null, boolean]> = dispatch => ( dispatch.call(updateVisibleNodes) } -const onNodeClick: BoundMethodCreator<[TreeNode]> = dispatch => node => { +export const onNodeClick: BoundMethodCreator<[TreeNode]> = dispatch => node => { if (node.type === 'tree') { dispatch.call(toggleNodeExpansion, node, true) } else if (node.type === 'blob') { @@ -335,23 +340,7 @@ const onNodeClick: BoundMethodCreator<[TreeNode]> = dispatch => node => { } } -const updateVisibleNodes: BoundMethodCreator = dispatch => () => { +export const updateVisibleNodes: BoundMethodCreator = dispatch => () => { const { visibleNodes } = visibleNodesGenerator dispatch.set({ visibleNodes }) } - -export default { - init, - setUpTree, - execAfterRender, - setStateText, - handleKeyDown, - onFocusSearchBar, - search, - setExpand, - goTo, - toggleNodeExpansion, - focusNode, - onNodeClick, - updateVisibleNodes, -} diff --git a/src/driver/core/SideBar.ts b/src/driver/core/SideBar.ts index 15b9ecf..93e0347 100644 --- a/src/driver/core/SideBar.ts +++ b/src/driver/core/SideBar.ts @@ -1,18 +1,12 @@ import { Props } from 'components/SideBar' import { GetCreatedMethod, MethodCreator } from 'driver/connect' -import configHelper, { Config, configKeys } from 'utils/configHelper' +import * as configHelper from 'utils/configHelper' +import { Config } from 'utils/configHelper' import * as DOMHelper from 'utils/DOMHelper' -import GitHubHelper, { - API_RATE_LIMIT, - BAD_CREDENTIALS, - BLOCKED_PROJECT, - EMPTY_PROJECT, - MetaData, - NOT_FOUND, - TreeData, -} from 'utils/GitHubHelper' -import keyHelper from 'utils/keyHelper' -import URLHelper from 'utils/URLHelper' +import * as GitHubHelper from 'utils/GitHubHelper' +import { MetaData, TreeData } from 'utils/GitHubHelper' +import * as keyHelper from 'utils/keyHelper' +import * as URLHelper from 'utils/URLHelper' export type ConnectorState = { // error message @@ -54,7 +48,7 @@ export type ConnectorState = { type BoundMethodCreator = MethodCreator -const init: BoundMethodCreator = dispatch => async () => { +export const init: BoundMethodCreator = dispatch => async () => { const { initializingPromise } = dispatch.get() if (initializingPromise) await initializingPromise @@ -159,15 +153,15 @@ const init: BoundMethodCreator = dispatch => async () => { } } -const handleError: BoundMethodCreator<[Error]> = dispatch => async err => { - if (err.message === EMPTY_PROJECT) { +export const handleError: BoundMethodCreator<[Error]> = dispatch => async err => { + if (err.message === GitHubHelper.EMPTY_PROJECT) { dispatch.call(setError, 'This project seems to be empty.') - } else if (err.message === BLOCKED_PROJECT) { + } else if (err.message === GitHubHelper.BLOCKED_PROJECT) { dispatch.call(setError, 'This project is blocked.') } else if ( - err.message === NOT_FOUND || - err.message === BAD_CREDENTIALS || - err.message === API_RATE_LIMIT + err.message === GitHubHelper.NOT_FOUND || + err.message === GitHubHelper.BAD_CREDENTIALS || + err.message === GitHubHelper.API_RATE_LIMIT ) { dispatch.set({ errorDueToAuth: true }) dispatch.call(setShowSettings, true) @@ -179,7 +173,7 @@ const handleError: BoundMethodCreator<[Error]> = dispatch => async err => { } } -const onPJAXEnd: BoundMethodCreator = dispatch => () => { +export const onPJAXEnd: BoundMethodCreator = dispatch => () => { const { metaData, copyFileButton, copySnippetButton, intelligentToggle } = dispatch.get() DOMHelper.unmountTopProgressBar() DOMHelper.decorateGitHubPageContent({ copyFileButton, copySnippetButton }) @@ -191,7 +185,7 @@ const onPJAXEnd: BoundMethodCreator = dispatch => () => { } } -const onKeyDown: BoundMethodCreator<[KeyboardEvent]> = dispatch => e => { +export const onKeyDown: BoundMethodCreator<[KeyboardEvent]> = dispatch => e => { const { toggleShowSideBarShortcut } = dispatch.get() if (toggleShowSideBarShortcut) { const keys = keyHelper.parseEvent(e) @@ -201,7 +195,7 @@ const onKeyDown: BoundMethodCreator<[KeyboardEvent]> = dispatch => e => { } } -const toggleShowSideBar: BoundMethodCreator = dispatch => () => { +export const toggleShowSideBar: BoundMethodCreator = dispatch => () => { const { intelligentToggle } = dispatch.get() const shouldShow = !dispatch.get().shouldShow dispatch.call(setShouldShow, shouldShow) @@ -211,28 +205,28 @@ const toggleShowSideBar: BoundMethodCreator = dispatch => () => { } } -const setShouldShow: BoundMethodCreator< +export const setShouldShow: BoundMethodCreator< [ConnectorState['shouldShow']] > = dispatch => shouldShow => { dispatch.set({ shouldShow }, shouldShow ? DOMHelper.focusFileExplorer : undefined) DOMHelper.setBodyIndent(shouldShow) } -const setError: BoundMethodCreator<[ConnectorState['error']]> = dispatch => error => { +export const setError: BoundMethodCreator<[ConnectorState['error']]> = dispatch => error => { dispatch.set({ error }) dispatch.call(setShouldShow, false) } -const toggleShowSettings: BoundMethodCreator = dispatch => () => +export const toggleShowSettings: BoundMethodCreator = dispatch => () => dispatch.set(({ showSettings }) => ({ showSettings: !showSettings, })) -const setShowSettings: BoundMethodCreator< +export const setShowSettings: BoundMethodCreator< [ConnectorState['showSettings']] > = dispatch => showSettings => dispatch.set({ showSettings }) -const onAccessTokenChange: BoundMethodCreator< +export const onAccessTokenChange: BoundMethodCreator< [ConnectorState['accessToken']] > = dispatch => accessToken => { dispatch.set({ accessToken }) @@ -242,33 +236,33 @@ const onAccessTokenChange: BoundMethodCreator< } } -const onShortcutChange: BoundMethodCreator< +export const onShortcutChange: BoundMethodCreator< [ConnectorState['toggleShowSideBarShortcut']] > = dispatch => shortcut => dispatch.set({ toggleShowSideBarShortcut: shortcut }) -const setMetaData: BoundMethodCreator<[ConnectorState['metaData']]> = dispatch => metaData => +export const setMetaData: BoundMethodCreator<[ConnectorState['metaData']]> = dispatch => metaData => dispatch.set({ metaData }) -const setCompressSingleton: BoundMethodCreator< +export const setCompressSingleton: BoundMethodCreator< [ConnectorState['compressSingletonFolder']] > = dispatch => compressSingletonFolder => dispatch.set({ compressSingletonFolder }) -const setCopyFile: BoundMethodCreator< +export const setCopyFile: BoundMethodCreator< [ConnectorState['copyFileButton']] > = dispatch => copyFileButton => dispatch.set({ copyFileButton }) -const setCopySnippet: BoundMethodCreator< +export const setCopySnippet: BoundMethodCreator< [ConnectorState['copySnippetButton']] > = dispatch => copySnippetButton => dispatch.set({ copySnippetButton }) -const setIntelligentToggle: BoundMethodCreator< +export const setIntelligentToggle: BoundMethodCreator< [ConnectorState['intelligentToggle']] > = dispatch => intelligentToggle => { - configHelper.setOne(configKeys.intelligentToggle, intelligentToggle) + configHelper.setOne(configHelper.configKeys.intelligentToggle, intelligentToggle) dispatch.set({ intelligentToggle }) } -const useListeners: BoundMethodCreator<[boolean]> = dispatch => { +export const useListeners: BoundMethodCreator<[boolean]> = dispatch => { const $onPJAXEnd = () => dispatch.call(onPJAXEnd) const $onKeyDown = (e: KeyboardEvent) => dispatch.call(onKeyDown, e) return on => { @@ -282,23 +276,3 @@ const useListeners: BoundMethodCreator<[boolean]> = dispatch => { } } } - -export default { - init, - onPJAXEnd, - onKeyDown, - setShouldShow, - setShowSettings, - toggleShowSideBar, - toggleShowSettings, - onAccessTokenChange, - onShortcutChange, - setMetaData, - setCompressSingleton, - setCopyFile, - setCopySnippet, - setIntelligentToggle, - setError, - handleError, - useListeners, -} diff --git a/src/driver/core/index.ts b/src/driver/core/index.ts index 071ab89..0ba5dda 100644 --- a/src/driver/core/index.ts +++ b/src/driver/core/index.ts @@ -1,2 +1,5 @@ -export { default as SideBar } from './SideBar' -export { default as FileExplorer } from './FileExplorer' +import * as FileExplorer from './FileExplorer' +import * as SideBar from './SideBar' + +export const SideBarCore = SideBar +export const FileExplorerCore = FileExplorer diff --git a/src/utils/GitHubHelper.ts b/src/utils/GitHubHelper.ts index 42cbf54..140e8b8 100644 --- a/src/utils/GitHubHelper.ts +++ b/src/utils/GitHubHelper.ts @@ -70,7 +70,11 @@ type RepoMetaData = { } } -async function getRepoMeta({ userName, repoName, accessToken }: MetaData): Promise { +export async function getRepoMeta({ + userName, + repoName, + accessToken, +}: MetaData): Promise { const url = `https://api.github.com/repos/${userName}/${repoName}` return await request(url, { accessToken }) } @@ -91,7 +95,7 @@ export type TreeData = { url: string } -async function getTreeData({ +export async function getTreeData({ userName, repoName, branchName, @@ -109,7 +113,7 @@ export type BlobData = { url: string } -async function getBlobData({ +export async function getBlobData({ userName, repoName, accessToken, @@ -121,17 +125,10 @@ async function getBlobData({ return await request(url, { accessToken }) } -function getUrlForRedirect( +export function getUrlForRedirect( { userName, repoName, branchName }: MetaData, type = 'blob', path?: string, ) { return `https://github.com/${userName}/${repoName}/${type}/${branchName}/${path}` } - -export default { - getRepoMeta, - getTreeData, - getBlobData, - getUrlForRedirect, -} diff --git a/src/utils/URLHelper.ts b/src/utils/URLHelper.ts index 2e2cac6..6d515bb 100644 --- a/src/utils/URLHelper.ts +++ b/src/utils/URLHelper.ts @@ -1,7 +1,7 @@ import { raiseError } from 'analytics' import { MetaData } from './GitHubHelper' -function parse(): MetaData & { path: string[] } { +export function parse(): MetaData & { path: string[] } { const { pathname } = window.location let [ , @@ -19,12 +19,12 @@ function parse(): MetaData & { path: string[] } { } } -function parseSHA() { +export function parseSHA() { const { type, path } = parse() return type === 'blob' || type === 'tree' ? path[0] : undefined } -function isInRepoPage() { +export function isInRepoPage() { const repoHeaderSelector = '.repohead' return Boolean(document.querySelector(repoHeaderSelector)) } @@ -38,7 +38,7 @@ const TYPES = { // TODO: record more types } -function isInCodePage(metaData: MetaData = {}) { +export function isInCodePage(metaData: MetaData = {}) { const mergedRepo = { ...parse(), ...metaData } const { type, branchName } = mergedRepo return Boolean( @@ -57,7 +57,7 @@ function isCompleteCommitSHA(sha?: string) { return typeof sha === 'string' && /^[abcdef0-9]{40}$/i.test(sha) } -function getCurrentPath(branchName = '') { +export function getCurrentPath(branchName = '') { const { path, type } = parse() if (type === 'blob' || type === 'tree') { if (isCommitPath(path)) { @@ -90,11 +90,3 @@ function getCurrentPath(branchName = '') { } return [] } - -export default { - getCurrentPath, - isInRepoPage, - isInCodePage, - parse, - parseSHA, -} diff --git a/src/utils/configHelper.ts b/src/utils/configHelper.ts index 9a84d91..9e093ac 100644 --- a/src/utils/configHelper.ts +++ b/src/utils/configHelper.ts @@ -1,4 +1,4 @@ -import storageHelper from 'utils/storageHelper' +import * as storageHelper from 'utils/storageHelper' export type Config = { sideBarWidth: number @@ -43,27 +43,20 @@ function applyDefaultConfigs(configs: Config) { ) } -async function getAll(): Promise { +export async function getAll(): Promise { return applyDefaultConfigs(await storageHelper.get(configKeyArray)) } -async function getOne(key: configKeys) { +export async function getOne(key: configKeys) { return (await getAll())[key] } -async function setAll(partialConfig: Partial) { +export async function setAll(partialConfig: Partial) { return await storageHelper.set(partialConfig) } -async function setOne(key: configKeys, value: any) { +export async function setOne(key: configKeys, value: any) { return await setAll({ [key]: value, }) } - -export default { - getAll, - getOne, - setAll, - setOne, -} diff --git a/src/utils/cx.ts b/src/utils/cx.ts index be8031f..179b6d3 100644 --- a/src/utils/cx.ts +++ b/src/utils/cx.ts @@ -1,7 +1,7 @@ /** * cx('class1', { class2: true, class3: false }) --> 'class1 class2' */ -export default function cx(...classNames: any[]): string { +export function cx(...classNames: any[]): string { return classNames .filter(Boolean) .map(className => { diff --git a/src/utils/keyHelper.ts b/src/utils/keyHelper.ts index a06850c..3394516 100644 --- a/src/utils/keyHelper.ts +++ b/src/utils/keyHelper.ts @@ -36,7 +36,7 @@ function parseKeyCode(code: string) { return code.toLowerCase().replace(/^control$/, 'ctrl') } -function parseEvent(e: KeyboardEvent | React.KeyboardEvent) { +export function parseEvent(e: KeyboardEvent | React.KeyboardEvent) { const { altKey: alt, shiftKey: shift, metaKey: meta, ctrlKey: ctrl } = e try { const code = parseKeyCode(e.key) @@ -57,7 +57,3 @@ function parseEvent(e: KeyboardEvent | React.KeyboardEvent) { throw new Error(`Error parse keyboard event: ${serializedKeyData}`) } } - -export default { - parseEvent, -} diff --git a/src/utils/storageHelper.ts b/src/utils/storageHelper.ts index 6f62d20..8c52640 100644 --- a/src/utils/storageHelper.ts +++ b/src/utils/storageHelper.ts @@ -1,14 +1,9 @@ const localStorage = browser.storage.local -function get(mapping: string[] | null): Promise { +export function get(mapping: string[] | null): Promise { return localStorage.get(mapping || undefined) } -function set(value: any): Promise { +export function set(value: any): Promise { return localStorage.set(value) } - -export default { - get, - set, -} diff --git a/src/utils/treeParser.ts b/src/utils/treeParser.ts index 0e64998..db9dd8d 100644 --- a/src/utils/treeParser.ts +++ b/src/utils/treeParser.ts @@ -1,4 +1,4 @@ -import GitHubHelper, { MetaData, TreeData } from 'utils/GitHubHelper' +import { getUrlForRedirect, MetaData, TreeData } from 'utils/GitHubHelper' import { TreeNode } from './VisibleNodesGenerator' interface RawItem { @@ -36,7 +36,7 @@ function findGitModules(root: TreeNode) { return null } -function parse(treeData: TreeData, metaData: MetaData) { +export function parse(treeData: TreeData, metaData: MetaData) { const { tree } = treeData // nodes are created from items and put onto tree @@ -70,7 +70,7 @@ function parse(treeData: TreeData, metaData: MetaData) { name: item.path && item.path.replace(/^.*\//, ''), url: item.url && item.type && item.path - ? GitHubHelper.getUrlForRedirect(metaData, item.type, item.path) + ? getUrlForRedirect(metaData, item.type, item.path) : null, contents: item.type === 'tree' ? [] : null, } as TreeNode @@ -88,7 +88,3 @@ function parse(treeData: TreeData, metaData: MetaData) { root: sortFoldersToFront(root), } } - -export default { - parse, -} diff --git a/src/utils/visibleNodesGenerator.ts b/src/utils/visibleNodesGenerator.ts index 5212889..bc8dc9e 100644 --- a/src/utils/visibleNodesGenerator.ts +++ b/src/utils/visibleNodesGenerator.ts @@ -202,7 +202,7 @@ type Options = { compress?: boolean } -export default class VisibleNodesGenerator { +export class VisibleNodesGenerator { l1: L1 l2: L2 l3: L3 From 570ab3915eab9d69380d0c71b08fe4e0b0e52c4c Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Sat, 9 Nov 2019 13:28:51 +0800 Subject: [PATCH 16/98] refactor: move Props --- src/components/FileExplorer.tsx | 14 ++------------ src/components/SideBar.tsx | 6 ++---- src/driver/connect.ts | 2 +- src/driver/core/FileExplorer.ts | 10 +++++++++- src/driver/core/SideBar.ts | 3 ++- src/driver/core/index.ts | 10 ++++++++-- 6 files changed, 24 insertions(+), 21 deletions(-) diff --git a/src/components/FileExplorer.tsx b/src/components/FileExplorer.tsx index a7b606e..e0e2b35 100644 --- a/src/components/FileExplorer.tsx +++ b/src/components/FileExplorer.tsx @@ -3,25 +3,15 @@ import { Node } from 'components/Node' import { SearchBar } from 'components/SearchBar' import { connect } from 'driver/connect' import { FileExplorerCore } from 'driver/core' -import { ConnectorState } from 'driver/core/FileExplorer' +import { ConnectorState, Props } from 'driver/core/FileExplorer' import * as React from 'react' import { FixedSizeList as List, ListChildComponentProps } from 'react-window' import { cx } from 'utils/cx' -import { MetaData, TreeData } from 'utils/GitHubHelper' import { usePrevious } from 'utils/hooks' import { TreeNode, VisibleNodes } from 'utils/VisibleNodesGenerator' import { Icon } from './Icon' import { SizeObserver } from './SizeObserver' -export type Props = { - treeData?: TreeData - metaData: MetaData - freeze: boolean - compressSingletonFolder: boolean - accessToken: string | undefined - toggleShowSettings: React.MouseEventHandler -} - class RawFileExplorer extends React.Component { static defaultProps: Partial = { freeze: false, @@ -183,4 +173,4 @@ class RawFileExplorer extends React.Component { } } -export const FileExplorer = connect(FileExplorerCore)(RawFileExplorer) +export const FileExplorer = connect(FileExplorerCore)(RawFileExplorer) diff --git a/src/components/SideBar.tsx b/src/components/SideBar.tsx index b39ac59..f7f16e7 100644 --- a/src/components/SideBar.tsx +++ b/src/components/SideBar.tsx @@ -6,12 +6,10 @@ import { SettingsBar } from 'components/SettingsBar' import { ToggleShowButton } from 'components/ToggleShowButton' import { connect } from 'driver/connect' import { SideBarCore } from 'driver/core' -import { ConnectorState } from 'driver/core/SideBar' +import { ConnectorState, Props } from 'driver/core/SideBar' import * as React from 'react' import { cx } from 'utils/cx' -export type Props = {} - class RawGitako extends React.PureComponent { static defaultProps: Partial = { baseSize: 260, @@ -147,4 +145,4 @@ class RawGitako extends React.PureComponent { } } -export const SideBar = connect(SideBarCore)(RawGitako) +export const SideBar = connect(SideBarCore)(RawGitako) diff --git a/src/driver/connect.ts b/src/driver/connect.ts index e73308a..66933a3 100644 --- a/src/driver/connect.ts +++ b/src/driver/connect.ts @@ -47,7 +47,7 @@ export type MethodCreator = ( dispatch: Dispatch, ) => Method -type Sources = { +export type Sources = { [key: string]: MethodCreator } type WrappedMethods = { diff --git a/src/driver/core/FileExplorer.ts b/src/driver/core/FileExplorer.ts index ba82b9d..307a494 100644 --- a/src/driver/core/FileExplorer.ts +++ b/src/driver/core/FileExplorer.ts @@ -1,4 +1,3 @@ -import { Props } from 'components/FileExplorer' import { GetCreatedMethod, MethodCreator } from 'driver/connect' import * as ini from 'ini' import { Base64 } from 'js-base64' @@ -10,6 +9,15 @@ import * as treeParser from 'utils/treeParser' import * as URLHelper from 'utils/URLHelper' import { TreeNode, VisibleNodes, VisibleNodesGenerator } from 'utils/VisibleNodesGenerator' +export type Props = { + treeData?: GitHubHelper.TreeData + metaData: GitHubHelper.MetaData + freeze: boolean + compressSingletonFolder: boolean + accessToken: string | undefined + toggleShowSettings: React.MouseEventHandler +} + export type ConnectorState = { stateText: string visibleNodes: VisibleNodes | null diff --git a/src/driver/core/SideBar.ts b/src/driver/core/SideBar.ts index 93e0347..410e86a 100644 --- a/src/driver/core/SideBar.ts +++ b/src/driver/core/SideBar.ts @@ -1,4 +1,3 @@ -import { Props } from 'components/SideBar' import { GetCreatedMethod, MethodCreator } from 'driver/connect' import * as configHelper from 'utils/configHelper' import { Config } from 'utils/configHelper' @@ -8,6 +7,8 @@ import { MetaData, TreeData } from 'utils/GitHubHelper' import * as keyHelper from 'utils/keyHelper' import * as URLHelper from 'utils/URLHelper' +export type Props = {} + export type ConnectorState = { // error message error?: string diff --git a/src/driver/core/index.ts b/src/driver/core/index.ts index 0ba5dda..4eb403b 100644 --- a/src/driver/core/index.ts +++ b/src/driver/core/index.ts @@ -1,5 +1,11 @@ +import { Sources } from 'driver/connect' import * as FileExplorer from './FileExplorer' +import { + ConnectorState as FileExplorerConnectorState, + Props as FileExplorerProps, +} from './FileExplorer' import * as SideBar from './SideBar' +import { ConnectorState as SideBarConnectorState, Props as SideBarProps } from './SideBar' -export const SideBarCore = SideBar -export const FileExplorerCore = FileExplorer +export const FileExplorerCore: Sources = FileExplorer +export const SideBarCore: Sources = SideBar From 9f4e2b51e0a9d85e3785757a757057ba08fbba01 Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Sat, 9 Nov 2019 13:59:20 +0800 Subject: [PATCH 17/98] refactor: enhance connect --- src/driver/connect.ts | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/driver/connect.ts b/src/driver/connect.ts index 66933a3..b061f31 100644 --- a/src/driver/connect.ts +++ b/src/driver/connect.ts @@ -94,19 +94,20 @@ function link(instance: React.Component, sources: Sources): Wr } export function connect(mapping: Sources) { - return function linkComponent( - ComponentClass: React.ComponentClass, - ): React.ComponentClass { - return class AwesomeApp extends React.PureComponent { - static displayName = `Connected(${ComponentClass.displayName || ComponentClass.name})` - static defaultProps = ComponentClass.defaultProps + return function linkComponent< + State, + ComponentClass extends React.ComponentClass + >(Component: ComponentClass) { + return class ConnectedComponent extends React.PureComponent { + static displayName = `Connected(${Component.displayName || Component.name})` + static defaultProps = Component.defaultProps - state = {} as ExtraP - connectedMethods = link(this, mapping) as WrappedMethods + state: ExtraP = {} as ExtraP + connectedMethods: WrappedMethods = link(this, mapping) render() { const props = Object.assign({}, this.props, this.connectedMethods, this.state) - return React.createElement(ComponentClass, props) + return React.createElement(Component, props) } } } From 79c1c82b65e5f539d2c37749d0865d074a529809 Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Sun, 10 Nov 2019 15:06:53 +0800 Subject: [PATCH 18/98] refactor: remove warnings --- src/components/FileExplorer.tsx | 16 ++----- src/components/Node.tsx | 72 ++++++++++++++-------------- src/components/Portal.tsx | 10 ++-- src/components/ResizeHandler.tsx | 81 +++++++++++++++----------------- src/components/SettingsBar.tsx | 4 +- src/components/SideBar.tsx | 8 +--- src/components/SizeObserver.tsx | 20 ++++---- src/utils/DOMHelper.ts | 2 +- 8 files changed, 98 insertions(+), 115 deletions(-) diff --git a/src/components/FileExplorer.tsx b/src/components/FileExplorer.tsx index e0e2b35..8f4bb73 100644 --- a/src/components/FileExplorer.tsx +++ b/src/components/FileExplorer.tsx @@ -19,25 +19,19 @@ class RawFileExplorer extends React.Component { visibleNodes: null, } - componentWillMount() { + componentDidMount() { const { init, setUpTree, treeData, metaData, compressSingletonFolder, accessToken } = this.props init() setUpTree({ treeData, metaData, compressSingletonFolder, accessToken }) - } - - componentDidMount() { const { execAfterRender } = this.props execAfterRender() } - componentWillReceiveProps(nextProps: Props & ConnectorState) { - if (nextProps.treeData !== this.props.treeData) { - const { setUpTree, treeData, metaData, compressSingletonFolder, accessToken } = nextProps + componentDidUpdate(prevProps: Props & ConnectorState) { + if (this.props.treeData !== prevProps.treeData) { + const { setUpTree, treeData, metaData, compressSingletonFolder, accessToken } = this.props setUpTree({ treeData, metaData, compressSingletonFolder, accessToken }) } - } - - componentDidUpdate() { const { execAfterRender } = this.props execAfterRender() } @@ -114,7 +108,7 @@ class RawFileExplorer extends React.Component { ) }) - private renderActions: Node['props']['renderActions'] = node => { + private renderActions: React.ComponentProps['renderActions'] = node => { const { searchKey, goTo } = this.props return ( searchKey && ( diff --git a/src/components/Node.tsx b/src/components/Node.tsx index 70947d9..943e9bc 100644 --- a/src/components/Node.tsx +++ b/src/components/Node.tsx @@ -24,42 +24,42 @@ type Props = { renderActions?(node: TreeNode): React.ReactNode style?: React.CSSProperties } -export class Node extends React.PureComponent { - onClick: React.MouseEventHandler = event => { - if ( - (os === OperatingSystems.macOS && event.metaKey) || - (os === OperatingSystems.Windows && event.ctrlKey) - ) { - // Open in new tab - return - } - event.preventDefault() - const { node, onClick } = this.props - onClick(node) - } +export function Node({ node, depth, expanded, focused, renderActions, style, onClick }: Props) { + const onClickNode: React.MouseEventHandler = React.useCallback( + event => { + if ( + (os === OperatingSystems.macOS && event.metaKey) || + (os === OperatingSystems.Windows && event.ctrlKey) + ) { + // The default behavior, open in new tab + return + } + event.preventDefault() - render() { - const { node, depth, expanded, focused, renderActions, style } = this.props - const { name, path } = node - return ( -
- - + ) } diff --git a/src/components/Portal.tsx b/src/components/Portal.tsx index 3a8cd3e..2497954 100644 --- a/src/components/Portal.tsx +++ b/src/components/Portal.tsx @@ -5,10 +5,8 @@ type Props = { into: Element | null } -export class Portal extends React.PureComponent { - render() { - const { into, children } = this.props - if (!(into instanceof Element)) return null - return ReactDOM.createPortal(children, into) - } +export function Portal(props: React.PropsWithChildren) { + const { into, children } = props + if (!(into instanceof Element)) return null + return ReactDOM.createPortal(children, into) } diff --git a/src/components/ResizeHandler.tsx b/src/components/ResizeHandler.tsx index d49e562..a3b9fd8 100644 --- a/src/components/ResizeHandler.tsx +++ b/src/components/ResizeHandler.tsx @@ -8,51 +8,46 @@ type Props = { style?: React.CSSProperties } -export class HorizontalResizeHandler extends React.PureComponent { - pointerDown = false - startX = 0 - baseSize = this.props.size +export function HorizontalResizeHandler({ onResize, size, style }: Props) { + const pointerDown = React.useRef(false) + const startX = React.useRef(0) + const baseSize = React.useRef(size) + const latestPropSize = React.useRef(size) - componentWillReceiveProps(nextProps: Props) { - if (!this.pointerDown) { - // update baseSize when not resizing - this.baseSize = nextProps.size + React.useEffect(() => { + latestPropSize.current = size + }, [size]) + + const onPointerDown = React.useCallback(({ clientX }: React.MouseEvent) => { + startX.current = clientX + pointerDown.current = true + baseSize.current = latestPropSize.current + }, []) + + React.useEffect(() => { + const onPointerMove = ({ clientX }: MouseEvent) => { + if (!pointerDown.current) return + const shift = clientX - startX.current + onResize(baseSize.current + shift) } - } + window.addEventListener('mousemove', onPointerMove) + return () => window.removeEventListener('mousemove', onPointerMove) + }, [onResize]) - subscribeEvents = () => { - window.addEventListener('mousemove', this.onPointerMove) - window.addEventListener('mouseup', this.onPointerUp) - } + React.useEffect(() => { + const onPointerUp = () => { + if (pointerDown.current) { + pointerDown.current = false + baseSize.current = latestPropSize.current + } + } + window.addEventListener('mouseup', onPointerUp) + return () => window.removeEventListener('mouseup', onPointerUp) + }, []) - unsubscribeEvents = () => { - window.removeEventListener('mousemove', this.onPointerMove) - window.removeEventListener('mouseup', this.onPointerUp) - } - - onPointerDown = ({ clientX }: React.MouseEvent) => { - this.startX = clientX - this.pointerDown = true - this.subscribeEvents() - } - - onPointerMove = ({ clientX }: MouseEvent) => { - if (!this.pointerDown) return - this.props.onResize(clientX - this.startX + this.baseSize) - } - - onPointerUp = () => { - this.pointerDown = false - this.baseSize = this.props.size - this.unsubscribeEvents() - } - - render() { - const { style } = this.props - return ( -
- -
- ) - } + return ( +
+ +
+ ) } diff --git a/src/components/SettingsBar.tsx b/src/components/SettingsBar.tsx index 8f8a57d..85a6087 100644 --- a/src/components/SettingsBar.tsx +++ b/src/components/SettingsBar.tsx @@ -104,9 +104,9 @@ export class SettingsBar extends React.PureComponent { if (!this.props.accessToken) this.trySetUpAccessTokenWithCode() } - componentWillReceiveProps({ toggleShowSideBarShortcut }: Props) { + componentDidUpdate({ toggleShowSideBarShortcut }: Props) { if (toggleShowSideBarShortcut !== this.props.toggleShowSideBarShortcut) { - this.setState({ toggleShowSideBarShortcut }) + this.setState({ toggleShowSideBarShortcut: this.props.toggleShowSideBarShortcut }) } } diff --git a/src/components/SideBar.tsx b/src/components/SideBar.tsx index f7f16e7..6b4cbbe 100644 --- a/src/components/SideBar.tsx +++ b/src/components/SideBar.tsx @@ -24,13 +24,9 @@ class RawGitako extends React.PureComponent { disabled: false, } - componentWillMount() { - const { init } = this.props - init() - } - componentDidMount() { - const { useListeners } = this.props + const { init, useListeners } = this.props + init() useListeners(true) } diff --git a/src/components/SizeObserver.tsx b/src/components/SizeObserver.tsx index 4b394c4..ca64c30 100644 --- a/src/components/SizeObserver.tsx +++ b/src/components/SizeObserver.tsx @@ -19,6 +19,16 @@ export function SizeObserver({ type = 'div', children, ...rest }: Props) { height: undefined, }) + const safeSetSize = React.useCallback(function safeSetSize(rect: DOMRectReadOnly) { + // requestAnimationFrame fixes "ResizeObserver loop limit exceeded" error + requestAnimationFrame(() => + setSize({ + width: rect.width, + height: rect.height, + }), + ) + }, []) + React.useLayoutEffect(() => { if (features.resize) { const observer = new window.ResizeObserver(entries => { @@ -43,14 +53,4 @@ export function SizeObserver({ type = 'div', children, ...rest }: Props) { const props: any = { ...rest, ref } // :) return React.createElement(type, props, children(size)) - - function safeSetSize(rect: DOMRectReadOnly) { - // requestAnimationFrame fixes "ResizeObserver loop limit exceeded" error - requestAnimationFrame(() => - setSize({ - width: rect.width, - height: rect.height, - }), - ) - } } diff --git a/src/utils/DOMHelper.ts b/src/utils/DOMHelper.ts index 0917b96..19dab59 100644 --- a/src/utils/DOMHelper.ts +++ b/src/utils/DOMHelper.ts @@ -102,7 +102,7 @@ export function insertLogoMountPoint() { function createLogoMountPoint() { const logoMountElement = document.createElement('div') - logoMountElement.setAttribute('class', 'gitako-logo-mount-point') + logoMountElement.classList.add('gitako-logo-mount-point') document.body.appendChild(logoMountElement) return logoMountElement } From db23b69d7f97c389efca022840b74799b746ca4b Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Sun, 10 Nov 2019 17:36:43 +0800 Subject: [PATCH 19/98] feat: configs context --- src/components/Gitako.tsx | 7 +++++- src/components/Resizable.tsx | 6 +++-- src/containers/ConfigsContext.tsx | 38 +++++++++++++++++++++++++++++++ src/global.d.ts | 9 ++++++++ src/utils/configHelper.ts | 2 +- 5 files changed, 58 insertions(+), 4 deletions(-) create mode 100644 src/containers/ConfigsContext.tsx create mode 100644 src/global.d.ts diff --git a/src/components/Gitako.tsx b/src/components/Gitako.tsx index 16ba16a..4088a27 100644 --- a/src/components/Gitako.tsx +++ b/src/components/Gitako.tsx @@ -1,5 +1,6 @@ import { raiseError } from 'analytics' import { SideBar } from 'components/SideBar' +import { ConfigsContextWrapper } from 'containers/ConfigsContext' import * as React from 'react' export class Gitako extends React.PureComponent { @@ -8,6 +9,10 @@ export class Gitako extends React.PureComponent { } render() { - return + return ( + + + + ) } } diff --git a/src/components/Resizable.tsx b/src/components/Resizable.tsx index b14f40a..2cba2fa 100644 --- a/src/components/Resizable.tsx +++ b/src/components/Resizable.tsx @@ -1,6 +1,7 @@ import { HorizontalResizeHandler } from 'components/ResizeHandler' +import { useConfigs } from 'containers/ConfigsContext' import * as React from 'react' -import { configKeys, setOne } from 'utils/configHelper' +import { configKeys } from 'utils/configHelper' import { cx } from 'utils/cx' import { bodySpacingClassName } from 'utils/DOMHelper' import * as features from 'utils/features' @@ -17,6 +18,7 @@ const GITHUB_WIDTH = 1020 export function Resizable({ baseSize, className, children }: React.PropsWithChildren) { const [size, setSize] = React.useState(baseSize) + const configContext = useConfigs() React.useEffect(() => { setSize(baseSize) @@ -32,7 +34,7 @@ export function Resizable({ baseSize, className, children }: React.PropsWithChil React.useEffect(() => { document.documentElement.style.setProperty('--gitako-width', size + 'px') - setOne(configKeys.sideBarWidth, size) + configContext.set({ [configKeys.sideBarWidth]: size }) }, [size]) useMediaStyleSheet( diff --git a/src/containers/ConfigsContext.tsx b/src/containers/ConfigsContext.tsx new file mode 100644 index 0000000..1be1551 --- /dev/null +++ b/src/containers/ConfigsContext.tsx @@ -0,0 +1,38 @@ +import * as React from 'react' +import { Config, getAll, setAll } from 'utils/configHelper' + +type Props = {} + +type ContextShape = PartialValSet + +const ConfigsContext = React.createContext(null) + +export function ConfigsContextWrapper(props: React.PropsWithChildren) { + const [configs, setConfigs] = React.useState(null) + React.useEffect(() => { + getAll().then(setConfigs) + }, []) + const set = React.useCallback( + () => (configs: Config) => { + setAll(configs) + setConfigs(configs) + }, + [setConfigs], + ) + if (configs === null) return null + return ( + + {props.children} + + ) +} + +export const useConfigs = useNonNullContext(ConfigsContext) + +function useNonNullContext>(theContext: React.Context): () => R { + return () => { + const context = React.useContext(theContext) + if (context === null) throw new Error(`Empty context`) + return context as R + } +} diff --git a/src/global.d.ts b/src/global.d.ts new file mode 100644 index 0000000..fac9ed3 --- /dev/null +++ b/src/global.d.ts @@ -0,0 +1,9 @@ +type ValSet = { + val: T + set: (val: T) => void +} + +type PartialValSet = { + val: T + set: (val: Partial) => void +} diff --git a/src/utils/configHelper.ts b/src/utils/configHelper.ts index 9e093ac..f00dae7 100644 --- a/src/utils/configHelper.ts +++ b/src/utils/configHelper.ts @@ -20,7 +20,7 @@ export enum configKeys { intelligentToggle = 'intelligentToggle', } -const defaultConfigs: Config = { +export const defaultConfigs: Config = { sideBarWidth: 260, shortcut: undefined, access_token: undefined, From 8141dfa3b4edc3cb001b8080f2f73fe0b067c139 Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Sun, 10 Nov 2019 17:37:51 +0800 Subject: [PATCH 20/98] feat: expose props when get --- src/driver/connect.ts | 6 +++--- src/driver/core/FileExplorer.ts | 5 ++--- src/driver/core/SideBar.ts | 13 ++++++------- 3 files changed, 11 insertions(+), 13 deletions(-) diff --git a/src/driver/connect.ts b/src/driver/connect.ts index b061f31..6f6165b 100644 --- a/src/driver/connect.ts +++ b/src/driver/connect.ts @@ -31,7 +31,7 @@ function run([method, args]: [M, Parameters]) { } export type DispatchState = React.Component['setState'] -export type GetState = () => State +export type GetState = () => [State, Props] export type TriggerOtherMethod = ( methodCreator: MethodCreator, ...args: Parameters>> @@ -39,7 +39,7 @@ export type TriggerOtherMethod = ( export type Dispatch = { set: DispatchState - get: GetState + get: GetState call: TriggerOtherMethod } @@ -75,7 +75,7 @@ function link(instance: React.Component, sources: Sources): Wr const dispatchState: DispatchState = (updater, callback) => { instance.setState(updater, callback) } - const prepareState: GetState = () => instance.state + const prepareState: GetState = () => [instance.state, instance.props] const dispatch: Dispatch = { call: dispatchCall, get: prepareState, diff --git a/src/driver/core/FileExplorer.ts b/src/driver/core/FileExplorer.ts index 307a494..4d5a4d4 100644 --- a/src/driver/core/FileExplorer.ts +++ b/src/driver/core/FileExplorer.ts @@ -182,7 +182,7 @@ export const setStateText: BoundMethodCreator<[ConnectorState['stateText']]> = d }) export const handleKeyDown: BoundMethodCreator<[React.KeyboardEvent]> = dispatch => event => { - const { searched, visibleNodes } = dispatch.get() + const [{ searched, visibleNodes }] = dispatch.get() if (!visibleNodes) return const { nodes, focusedNode, expandedNodes, depths } = visibleNodes function handleVerticalMove(index: number) { @@ -327,9 +327,8 @@ export const toggleNodeExpansion: BoundMethodCreator<[TreeNode, boolean]> = disp export const focusNode: BoundMethodCreator<[TreeNode | null, boolean]> = dispatch => ( node: TreeNode | null, - skipScroll = false, ) => { - const { visibleNodes } = dispatch.get() + const [{ visibleNodes }] = dispatch.get() if (!visibleNodes) return visibleNodesGenerator.focusNode(node) dispatch.call(updateVisibleNodes) diff --git a/src/driver/core/SideBar.ts b/src/driver/core/SideBar.ts index 410e86a..f3c797a 100644 --- a/src/driver/core/SideBar.ts +++ b/src/driver/core/SideBar.ts @@ -50,7 +50,7 @@ export type ConnectorState = { type BoundMethodCreator = MethodCreator export const init: BoundMethodCreator = dispatch => async () => { - const { initializingPromise } = dispatch.get() + const [{ initializingPromise }] = dispatch.get() if (initializingPromise) await initializingPromise let done: any = null // cannot use type `(() => void) | null` here @@ -175,7 +175,7 @@ export const handleError: BoundMethodCreator<[Error]> = dispatch => async err => } export const onPJAXEnd: BoundMethodCreator = dispatch => () => { - const { metaData, copyFileButton, copySnippetButton, intelligentToggle } = dispatch.get() + const [{ metaData, copyFileButton, copySnippetButton, intelligentToggle }] = dispatch.get() DOMHelper.unmountTopProgressBar() DOMHelper.decorateGitHubPageContent({ copyFileButton, copySnippetButton }) const mergedMetaData = { ...metaData, ...URLHelper.parse() } @@ -187,7 +187,7 @@ export const onPJAXEnd: BoundMethodCreator = dispatch => () => { } export const onKeyDown: BoundMethodCreator<[KeyboardEvent]> = dispatch => e => { - const { toggleShowSideBarShortcut } = dispatch.get() + const [{ toggleShowSideBarShortcut }] = dispatch.get() if (toggleShowSideBarShortcut) { const keys = keyHelper.parseEvent(e) if (keys === toggleShowSideBarShortcut) { @@ -197,9 +197,8 @@ export const onKeyDown: BoundMethodCreator<[KeyboardEvent]> = dispatch => e => { } export const toggleShowSideBar: BoundMethodCreator = dispatch => () => { - const { intelligentToggle } = dispatch.get() - const shouldShow = !dispatch.get().shouldShow - dispatch.call(setShouldShow, shouldShow) + const [{ intelligentToggle, shouldShow }] = dispatch.get() + dispatch.call(setShouldShow, !shouldShow) if (intelligentToggle !== null) { dispatch.call(setIntelligentToggle, shouldShow) @@ -267,7 +266,7 @@ export const useListeners: BoundMethodCreator<[boolean]> = dispatch => { const $onPJAXEnd = () => dispatch.call(onPJAXEnd) const $onKeyDown = (e: KeyboardEvent) => dispatch.call(onKeyDown, e) return on => { - const { disabled } = dispatch.get() + const [{ disabled }] = dispatch.get() if (on && !disabled) { window.addEventListener('pjax:complete', $onPJAXEnd) window.addEventListener('keydown', $onKeyDown) From f677d7df5a594f681cc0f8d91388bb58440a7bbd Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Sun, 10 Nov 2019 20:25:51 +0800 Subject: [PATCH 21/98] refactor: use more config --- src/components/Gitako.tsx | 6 ++++-- src/components/Resizable.tsx | 3 +-- src/containers/ConfigsContext.tsx | 10 ++++++---- src/driver/core/SideBar.ts | 12 ++++++++---- src/utils/configHelper.ts | 14 ++------------ src/utils/hooks.ts | 7 +++++++ 6 files changed, 28 insertions(+), 24 deletions(-) diff --git a/src/components/Gitako.tsx b/src/components/Gitako.tsx index 4088a27..28ace02 100644 --- a/src/components/Gitako.tsx +++ b/src/components/Gitako.tsx @@ -1,6 +1,6 @@ import { raiseError } from 'analytics' import { SideBar } from 'components/SideBar' -import { ConfigsContextWrapper } from 'containers/ConfigsContext' +import { ConfigsContext, ConfigsContextWrapper } from 'containers/ConfigsContext' import * as React from 'react' export class Gitako extends React.PureComponent { @@ -11,7 +11,9 @@ export class Gitako extends React.PureComponent { render() { return ( - + + {configContext => configContext && } + ) } diff --git a/src/components/Resizable.tsx b/src/components/Resizable.tsx index 2cba2fa..8a8fc0d 100644 --- a/src/components/Resizable.tsx +++ b/src/components/Resizable.tsx @@ -1,7 +1,6 @@ import { HorizontalResizeHandler } from 'components/ResizeHandler' import { useConfigs } from 'containers/ConfigsContext' import * as React from 'react' -import { configKeys } from 'utils/configHelper' import { cx } from 'utils/cx' import { bodySpacingClassName } from 'utils/DOMHelper' import * as features from 'utils/features' @@ -34,7 +33,7 @@ export function Resizable({ baseSize, className, children }: React.PropsWithChil React.useEffect(() => { document.documentElement.style.setProperty('--gitako-width', size + 'px') - configContext.set({ [configKeys.sideBarWidth]: size }) + configContext.set({ sideBarWidth: size }) }, [size]) useMediaStyleSheet( diff --git a/src/containers/ConfigsContext.tsx b/src/containers/ConfigsContext.tsx index 1be1551..9bfb22f 100644 --- a/src/containers/ConfigsContext.tsx +++ b/src/containers/ConfigsContext.tsx @@ -1,20 +1,22 @@ import * as React from 'react' -import { Config, getAll, setAll } from 'utils/configHelper' +import * as configsHelper from 'utils/configHelper' +import { Config } from 'utils/configHelper' type Props = {} type ContextShape = PartialValSet +export type ConfigsContextShape = ContextShape -const ConfigsContext = React.createContext(null) +export const ConfigsContext = React.createContext(null) export function ConfigsContextWrapper(props: React.PropsWithChildren) { const [configs, setConfigs] = React.useState(null) React.useEffect(() => { - getAll().then(setConfigs) + configsHelper.get().then(setConfigs) }, []) const set = React.useCallback( () => (configs: Config) => { - setAll(configs) + configsHelper.set(configs) setConfigs(configs) }, [setConfigs], diff --git a/src/driver/core/SideBar.ts b/src/driver/core/SideBar.ts index f3c797a..4695afb 100644 --- a/src/driver/core/SideBar.ts +++ b/src/driver/core/SideBar.ts @@ -1,5 +1,5 @@ +import { ConfigsContextShape } from 'containers/ConfigsContext' import { GetCreatedMethod, MethodCreator } from 'driver/connect' -import * as configHelper from 'utils/configHelper' import { Config } from 'utils/configHelper' import * as DOMHelper from 'utils/DOMHelper' import * as GitHubHelper from 'utils/GitHubHelper' @@ -7,7 +7,9 @@ import { MetaData, TreeData } from 'utils/GitHubHelper' import * as keyHelper from 'utils/keyHelper' import * as URLHelper from 'utils/URLHelper' -export type Props = {} +export type Props = { + configContext: ConfigsContextShape +} export type ConnectorState = { // error message @@ -77,6 +79,7 @@ export const init: BoundMethodCreator = dispatch => async () => { } metaData.branchName = detectedBranchName || 'master' dispatch.call(setMetaData, metaData) + const [, { configContext }] = dispatch.get() const { sideBarWidth, access_token: accessToken, @@ -85,7 +88,7 @@ export const init: BoundMethodCreator = dispatch => async () => { copyFileButton, copySnippetButton, intelligentToggle, - } = await configHelper.getAll() + } = configContext.val DOMHelper.decorateGitHubPageContent({ copyFileButton, copySnippetButton }) dispatch.set({ baseSize: sideBarWidth, @@ -258,7 +261,8 @@ export const setCopySnippet: BoundMethodCreator< export const setIntelligentToggle: BoundMethodCreator< [ConnectorState['intelligentToggle']] > = dispatch => intelligentToggle => { - configHelper.setOne(configHelper.configKeys.intelligentToggle, intelligentToggle) + const [, { configContext }] = dispatch.get() + configContext.set({ intelligentToggle }) dispatch.set({ intelligentToggle }) } diff --git a/src/utils/configHelper.ts b/src/utils/configHelper.ts index f00dae7..470a005 100644 --- a/src/utils/configHelper.ts +++ b/src/utils/configHelper.ts @@ -43,20 +43,10 @@ function applyDefaultConfigs(configs: Config) { ) } -export async function getAll(): Promise { +export async function get(): Promise { return applyDefaultConfigs(await storageHelper.get(configKeyArray)) } -export async function getOne(key: configKeys) { - return (await getAll())[key] -} - -export async function setAll(partialConfig: Partial) { +export async function set(partialConfig: Partial) { return await storageHelper.set(partialConfig) } - -export async function setOne(key: configKeys, value: any) { - return await setAll({ - [key]: value, - }) -} diff --git a/src/utils/hooks.ts b/src/utils/hooks.ts index 578ae1f..258481d 100644 --- a/src/utils/hooks.ts +++ b/src/utils/hooks.ts @@ -47,3 +47,10 @@ export function usePrevious(newValue: T) { }) return previousRef.current } + +export function useStates( + initialState: S | (() => S), +): { val: S; set: React.Dispatch> } { + const [val, set] = React.useState(initialState) + return { val, set } +} From 5c2842702c4a412d109ca79b850f30f695ef9f31 Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Mon, 11 Nov 2019 10:44:05 +0800 Subject: [PATCH 22/98] docs: better access token help --- src/components/SideBar.tsx | 31 +++++++++++++++++-------------- src/driver/core/SideBar.ts | 1 - 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/src/components/SideBar.tsx b/src/components/SideBar.tsx index 781ac8d..ab6baa1 100644 --- a/src/components/SideBar.tsx +++ b/src/components/SideBar.tsx @@ -41,22 +41,25 @@ class Gitako extends React.PureComponent { useListeners(false) } - renderAccessDeniedError() { + renderAccessDeniedError(hasToken: boolean) { return (
Access Denied
-

- Due to{' '} - - limitation of GitHub - {' '} - or{' '} - - auth needs - - , Gitako needs access token to continue. Please follow the instructions in the settings - panel below. -

+ {hasToken ? ( +

Current access token is not granted with permissions to access this project.

+ ) : ( +

+ Gitako needs access token to read this project due to{' '} + + GitHub rate limiting + {' '} + and{' '} + + auth needs + + . Please setup access token in the settings panel below. +

+ )}
) } @@ -75,7 +78,7 @@ class Gitako extends React.PureComponent {
{metaData && } {errorDueToAuth - ? this.renderAccessDeniedError() + ? this.renderAccessDeniedError(Boolean(accessToken)) : metaData && ( = dispatch => async err => { ) { dispatch.set({ errorDueToAuth: true }) dispatch.call(setShowSettings, true) - dispatch.call(setShouldShow, true) } else { dispatch.call(useListeners, false) dispatch.call(setError, 'Gitako ate a bug, but it should recovery soon!') From ddb86fa4efc1e2d60569ce875ff4cc2267aed673 Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Mon, 11 Nov 2019 23:56:20 +0800 Subject: [PATCH 23/98] changes are out of control --- src/components/FileExplorer.tsx | 282 +++++++++------- src/components/MoreOption.tsx | 52 +++ src/components/SettingsBar.tsx | 542 +++++++++++++----------------- src/components/SideBar.tsx | 205 +++++------ src/containers/ConfigsContext.tsx | 9 +- src/content.less | 2 +- src/driver/connect.ts | 7 +- src/driver/core/FileExplorer.ts | 4 +- src/driver/core/SideBar.ts | 84 ++--- src/utils/hooks.ts | 14 + tsconfig.json | 1 - 11 files changed, 569 insertions(+), 633 deletions(-) create mode 100644 src/components/MoreOption.tsx diff --git a/src/components/FileExplorer.tsx b/src/components/FileExplorer.tsx index 8f4bb73..60e95ab 100644 --- a/src/components/FileExplorer.tsx +++ b/src/components/FileExplorer.tsx @@ -1,129 +1,91 @@ import { LoadingIndicator } from 'components/LoadingIndicator' import { Node } from 'components/Node' import { SearchBar } from 'components/SearchBar' +import { useConfigs } from 'containers/ConfigsContext' import { connect } from 'driver/connect' import { FileExplorerCore } from 'driver/core' import { ConnectorState, Props } from 'driver/core/FileExplorer' import * as React from 'react' -import { FixedSizeList as List, ListChildComponentProps } from 'react-window' +import { FixedSizeList as List, ListChildComponentProps, ListProps } from 'react-window' import { cx } from 'utils/cx' import { usePrevious } from 'utils/hooks' import { TreeNode, VisibleNodes } from 'utils/VisibleNodesGenerator' import { Icon } from './Icon' import { SizeObserver } from './SizeObserver' -class RawFileExplorer extends React.Component { - static defaultProps: Partial = { - freeze: false, - searchKey: '', - visibleNodes: null, - } +const VisibleNodesContext = React.createContext(null) - componentDidMount() { - const { init, setUpTree, treeData, metaData, compressSingletonFolder, accessToken } = this.props +const RawFileExplorer: React.FC = function RawFileExplorer(props) { + const { visibleNodes, freeze, onNodeClick, searchKey } = props + const { + val: { access_token: accessToken, compressSingletonFolder }, + } = useConfigs() + + React.useEffect(() => { + const { init } = props init() + }, []) + + React.useEffect(() => { + const { setUpTree, treeData, metaData } = props setUpTree({ treeData, metaData, compressSingletonFolder, accessToken }) - const { execAfterRender } = this.props + }, [props.setUpTree, props.treeData, props.metaData, compressSingletonFolder, accessToken]) + + React.useEffect(() => { + const { execAfterRender } = props execAfterRender() - } - - componentDidUpdate(prevProps: Props & ConnectorState) { - if (this.props.treeData !== prevProps.treeData) { - const { setUpTree, treeData, metaData, compressSingletonFolder, accessToken } = this.props - setUpTree({ treeData, metaData, compressSingletonFolder, accessToken }) - } - const { execAfterRender } = this.props - execAfterRender() - } - - renderFiles(visibleNodes: VisibleNodes) { - const { nodes, focusedNode } = visibleNodes - const { searchKey } = this.props - const inSearch = searchKey !== '' - if (inSearch && nodes.length === 0) { - return - } - return ( - - {({ width = 0, height = 0 }) => ( - - )} - - ) - } - - ListV = React.memo<{ - nodes: TreeNode[] - height: number - width: number - focusedNode: TreeNode | null - }>(({ nodes, width, height, focusedNode }) => { - const listRef = React.useRef(null) - React.useEffect(() => { - if (focusedNode && listRef.current) { - listRef.current.scrollToItem(nodes.indexOf(focusedNode), 'smart') - } - }, [listRef.current, focusedNode]) - - const lastNodeLength = usePrevious(nodes.length) - React.useEffect(() => { - if (listRef.current && !focusedNode && lastNodeLength !== nodes.length) { - listRef.current.scrollTo(0) - } - }, [listRef.current, focusedNode, nodes.length]) - return ( - { - const node = nodes[index] - return node && node.path - }} - itemData={{ nodes }} - itemCount={nodes.length} - itemSize={35} - height={height} - width={width} - > - {this.VirtualNode} - - ) }) - VirtualNode = React.memo(({ index, style }) => { - const { visibleNodes, onNodeClick } = this.props - if (!visibleNodes) return null - const { nodes, depths, focusedNode, expandedNodes } = visibleNodes - const node = nodes[index] - return ( - - ) - }) - - private renderActions: React.ComponentProps['renderActions'] = node => { - const { searchKey, goTo } = this.props - return ( - searchKey && ( + const renderActions: React.ComponentProps['renderActions'] = React.useCallback( + node => + searchKey ? ( - ) - ) - } + ) : null, + [searchKey, props.goTo], + ) - revealNode( + const renderNode = React.useCallback( + ({ index, style }: ListChildComponentProps) => ( + + ), + [renderActions, onNodeClick], + ) + + const renderFiles = React.useCallback( + ({ nodes, focusedNode }: VisibleNodes) => { + const inSearch = searchKey !== '' + if (inSearch && nodes.length === 0) { + return + } + return ( + + {({ width = 0, height = 0 }) => ( + + )} + + ) + }, + [searchKey, ListView, renderNode], + ) + + const revealNode = React.useCallback(function revealNode( goTo: (path: string[]) => void, node: TreeNode, ): (event: React.MouseEvent) => void { @@ -132,39 +94,113 @@ class RawFileExplorer extends React.Component { e.preventDefault() goTo(node.path.split('/')) } - } + }, + []) - render() { - const { - stateText, - visibleNodes, - freeze, - handleKeyDown, - search, - toggleShowSettings, - onFocusSearchBar, - searchKey, - } = this.props - return ( + return ( +
- {stateText ? ( - + {props.stateText ? ( + ) : ( visibleNodes && ( - - - {this.renderFiles(visibleNodes)} - + <> + + {renderFiles(visibleNodes)} + ) )}
- ) - } +
+ ) +} + +RawFileExplorer.defaultProps = { + freeze: false, + searchKey: '', + visibleNodes: null, } export const FileExplorer = connect(FileExplorerCore)(RawFileExplorer) + +function VirtualNode({ + index, + style, + onNodeClick, + renderActions, +}: { + index: number + style: React.CSSProperties + onNodeClick: (treeNode: TreeNode) => void + renderActions: ((node: TreeNode) => React.ReactNode) | undefined +}) { + const visibleNodes = React.useContext(VisibleNodesContext) + if (!visibleNodes) return null + const { nodes, depths, focusedNode, expandedNodes } = visibleNodes + const node = nodes[index] + return ( + + ) +} + +function ListView({ + nodes, + width, + height, + focusedNode, + renderNode, +}: { + nodes: TreeNode[] + height: number + width: number + focusedNode: TreeNode | null + renderNode: ListProps['children'] +}) { + const listRef = React.useRef(null) + React.useEffect(() => { + if (focusedNode && listRef.current) { + listRef.current.scrollToItem(nodes.indexOf(focusedNode), 'smart') + } + }, [listRef.current, focusedNode]) + + const lastNodeLength = usePrevious(nodes.length) + React.useEffect(() => { + if (listRef.current && !focusedNode && lastNodeLength !== nodes.length) { + listRef.current.scrollTo(0) + } + }, [listRef.current, focusedNode, nodes.length]) + return ( + { + const node = nodes[index] + return node && node.path + }} + itemData={{ nodes }} + itemCount={nodes.length} + itemSize={35} + height={height} + width={width} + > + {renderNode} + + ) +} diff --git a/src/components/MoreOption.tsx b/src/components/MoreOption.tsx new file mode 100644 index 0000000..65a9d66 --- /dev/null +++ b/src/components/MoreOption.tsx @@ -0,0 +1,52 @@ +import { useConfigs } from 'containers/ConfigsContext' +import * as React from 'react' +import { Config } from 'utils/configHelper' + +export type SimpleField = { + key: keyof Config + label: string + wikiLink?: string + description?: string + overwrite?: Props['overwrite'] +} + +type Props = { + field: SimpleField + onChange?(): void + overwrite?: { + value: (value: T) => boolean + onChange: (checked: boolean) => any + } +} + +export function SimpleFieldInput({ field, overwrite, onChange }: Props) { + const configContext = useConfigs() + const value = configContext.val[field.key] + return ( + + ) +} diff --git a/src/components/SettingsBar.tsx b/src/components/SettingsBar.tsx index f5fa77e..98ac7e1 100644 --- a/src/components/SettingsBar.tsx +++ b/src/components/SettingsBar.tsx @@ -1,10 +1,12 @@ import { raiseError } from 'analytics' import { Icon } from 'components/Icon' +import { useConfigs } from 'containers/ConfigsContext' import { oauth, VERSION } from 'env' import * as React from 'react' -import { Config, configKeys, setOne } from 'utils/configHelper' import { friendlyFormatShortcut, JSONRequest, parseURLSearch } from 'utils/general' +import { useStates } from 'utils/hooks' import * as keyHelper from 'utils/keyHelper' +import { SimpleField, SimpleFieldInput } from './MoreOption' const WIKI_HOME_LINK = 'https://github.com/EnixCoda/Gitako/wiki' const wikiLinks = { @@ -18,348 +20,268 @@ const wikiLinks = { const ACCESS_TOKEN_REGEXP = /^[0-9a-f]{40}$/ type Props = { - accessToken?: string activated: boolean - onAccessTokenChange: (accessToken: string) => void - onShortcutChange: (shortcut: string) => void - setCopyFile: (copyFileButton: Props['copyFileButton']) => void - setCopySnippet: (copySnippetButton: Props['copySnippetButton']) => void - setCompressSingleton: (compressSingletonFolder: Props['compressSingletonFolder']) => void - setIntelligentToggle: (intelligentToggle: Props['intelligentToggle']) => void toggleShowSettings: () => void - toggleShowSideBarShortcut?: string -} & Pick< - Config, - 'compressSingletonFolder' | 'copyFileButton' | 'copySnippetButton' | 'intelligentToggle' -> - -type State = { - accessToken?: string - accessTokenHint: React.ReactNode - shortcutHint: string - toggleShowSideBarShortcut?: string - reloadHint: React.ReactNode - varyOptions: { - key: string - label: string - onChange: (e: React.FormEvent) => Promise | void - getValue: () => boolean - wikiLink?: string - description?: string - }[] } -export class SettingsBar extends React.PureComponent { - state = { - accessToken: '', - accessTokenHint: '', - shortcutHint: '', - toggleShowSideBarShortcut: this.props.toggleShowSideBarShortcut, - reloadHint: '', - varyOptions: [ - { - key: 'compress-singleton', - label: 'Compress singleton folder', - onChange: this.createOnToggleChecked( - configKeys.compressSingletonFolder, - this.props.setCompressSingleton, - ), - getValue: () => this.props.compressSingletonFolder, - wikiLink: wikiLinks.compressSingletonFolder, - }, - { - key: 'copy-file', - label: 'Copy File Shortcut', - onChange: this.createOnToggleChecked(configKeys.copyFileButton, this.props.setCopyFile), - getValue: () => this.props.copyFileButton, - wikiLink: wikiLinks.copyFileButton, - }, - { - key: 'copy-snippet', - label: 'Copy Snippet Shortcut', - onChange: this.createOnToggleChecked( - configKeys.copySnippetButton, - this.props.setCopySnippet, - ), - getValue: () => this.props.copySnippetButton, - wikiLink: wikiLinks.copySnippet, - }, - { - key: 'intelligent-toggle', - label: 'Intelligent Toggle', - onChange: async (e: React.FormEvent) => { - const { checked } = e.currentTarget - const intelligentToggle = checked ? null : true - await setOne(configKeys.intelligentToggle, intelligentToggle) - this.props.setIntelligentToggle(intelligentToggle) - }, - getValue: () => this.props.intelligentToggle === null, - description: `Gitako will open/close automatically according to page content when this is enabled.`, - }, - ], - } +const moreFields: SimpleField[] = [ + { + key: 'compressSingletonFolder', + label: 'Compress singleton folder', + wikiLink: wikiLinks.compressSingletonFolder, + }, + { + key: 'copyFileButton', + label: 'Copy File Shortcut', + wikiLink: wikiLinks.copyFileButton, + }, + { + key: 'copySnippetButton', + label: 'Copy Snippet Shortcut', + wikiLink: wikiLinks.copySnippet, + }, + { + key: 'intelligentToggle', + label: 'Intelligent Toggle', + description: `Gitako will open/close automatically according to page content when this is enabled.`, + overwrite: { + value: enabled => enabled === null, + onChange: checked => (checked ? null : true), + }, + }, +] - componentDidMount() { - if (!this.props.accessToken) this.trySetUpAccessTokenWithCode() - } +function SettingsBarContent() { + const configContext = useConfigs() + const hasAccessToken = Boolean(configContext.val.access_token) + const useAccessToken = useStates('') + const useAccessTokenHint = useStates('') + const useShortcutHint = useStates('') + const useToggleShowSideBarShortcut = useStates(configContext.val.shortcut) + const useReloadHint = useStates('') - componentDidUpdate({ toggleShowSideBarShortcut }: Props) { - if (toggleShowSideBarShortcut !== this.props.toggleShowSideBarShortcut) { - this.setState({ toggleShowSideBarShortcut: this.props.toggleShowSideBarShortcut }) + React.useEffect(() => { + if (!configContext.val.access_token) { + trySetUpAccessTokenWithCode().then(accessToken => { + useAccessToken.set(accessToken) + saveToken('') + }) } - } + }, []) - private async trySetUpAccessTokenWithCode() { - try { - const search = parseURLSearch() - if ('code' in search) { - const res = await JSONRequest('https://github.com/login/oauth/access_token', { - code: search.code, - client_id: oauth.clientId, - client_secret: oauth.clientSecret, - }) - const { access_token: accessToken, scope } = res - if (scope !== 'repo' || !accessToken) { - throw new Error(`Cannot resolve token response: '${JSON.stringify(res)}'`) - } - window.history.pushState({}, 'removed code', window.location.pathname.replace(/#.*$/, '')) - this.setState({ accessToken }, () => this.saveToken('')) - } - } catch (err) { - raiseError(err) - } - } + React.useEffect(() => { + useToggleShowSideBarShortcut.set(configContext.val.shortcut) + }, [configContext.val.shortcut]) - onInputAccessToken = (event: React.FormEvent) => { - const { value } = event.currentTarget - this.setState({ - accessToken: value, - accessTokenHint: ACCESS_TOKEN_REGEXP.test(value) ? '' : 'This token is in unknown format.', - }) - } + const onInputAccessToken = React.useCallback( + ({ currentTarget: { value } }: React.FormEvent) => { + useAccessToken.set(value) + useAccessTokenHint.set( + ACCESS_TOKEN_REGEXP.test(value) ? '' : 'This token is in unknown format.', + ) + }, + [], + ) - onPressAccessToken = (event: React.KeyboardEvent) => { - const { key } = event - if (key === 'Enter') { - this.saveToken() - } - } + const onPressAccessToken = React.useCallback(({ key }: React.KeyboardEvent) => { + if (key === 'Enter') saveToken() + }, []) - saveToken = async ( - hint: State['accessTokenHint'] = ( - - window.location.reload()}> - Reload - {' '} - to activate! - - ), - ) => { - const { onAccessTokenChange } = this.props - const { accessToken } = this.state + const saveToken = React.useCallback(async (hint?: typeof useAccessTokenHint.val) => { + const { val: accessToken } = useAccessToken if (accessToken) { - await setOne(configKeys.accessToken, accessToken) - onAccessTokenChange(accessToken) - this.setState({ - accessToken: '', - accessTokenHint: hint, - }) + configContext.set({ access_token: accessToken }) + useAccessToken.set('') + useAccessTokenHint.set( + hint || ( + + window.location.reload()}> + Reload + {' '} + to activate! + + ), + ) } - } + }, []) - clearToken = async () => { - const { onAccessTokenChange } = this.props - await setOne(configKeys.accessToken, '') - onAccessTokenChange('') - this.setState({ accessToken: '' }) - } + const clearToken = React.useCallback(async () => { + configContext.set({ access_token: '' }) + useAccessToken.set('') + }, []) - saveShortcut = async () => { - const { onShortcutChange } = this.props - const { toggleShowSideBarShortcut } = this.state - await setOne(configKeys.shortcut, toggleShowSideBarShortcut) + const saveShortcut = React.useCallback(async () => { + const { val: toggleShowSideBarShortcut } = useToggleShowSideBarShortcut + configContext.set({ shortcut: toggleShowSideBarShortcut }) if (typeof toggleShowSideBarShortcut === 'string') { - onShortcutChange(toggleShowSideBarShortcut) - this.setState({ - shortcutHint: 'Shortcut is saved!', - }) + useShortcutHint.set('Shortcut is saved!') } - } + }, [useToggleShowSideBarShortcut.val]) - onShortCutInputKeyDown = (e: React.KeyboardEvent) => { + const onShortCutInputKeyDown = React.useCallback((e: React.KeyboardEvent) => { e.preventDefault() + e.stopPropagation() // Clear shortcut with backspace const shortcut = e.key === 'Backspace' ? '' : keyHelper.parseEvent(e) - this.setState({ toggleShowSideBarShortcut: shortcut }) - } + useToggleShowSideBarShortcut.set(shortcut) + }, []) - showReloadHint = () => { - this.setState({ - reloadHint: ( + const showReloadHint = React.useCallback( + () => + useReloadHint.set( Saved,{' '} window.location.reload()}> reload {' '} to apply. - + , ), - }) - } + [], + ) - createOnToggleChecked( - configKey: configKeys, - set: (value: boolean) => void, - ): (e: React.FormEvent) => Promise { - return async e => { - const enabled = e.currentTarget.checked - await setOne(configKey, enabled) - set(enabled) - this.showReloadHint() - } - } + const { val: accessTokenHint } = useAccessTokenHint + const { val: toggleShowSideBarShortcut } = useToggleShowSideBarShortcut + const { val: shortcutHint } = useShortcutHint + const { val: accessToken } = useAccessToken + const { val: reloadHint } = useReloadHint - render() { - const { - accessTokenHint, - toggleShowSideBarShortcut, - shortcutHint, - accessToken, - reloadHint, - varyOptions, - } = this.state - const { toggleShowSettings, activated } = this.props - const hasAccessToken = Boolean(this.props.accessToken) - return ( -
- {activated && ( - -

Settings

-
-
-
-

- Access Token - -  (?) - -

- {!hasAccessToken && ( - { - // use js here to make sure redirect_uri is latest url - const url = `https://github.com/login/oauth/authorize?client_id=${ - oauth.clientId - }&scope=repo&redirect_uri=${encodeURIComponent(window.location.href)}` - window.location.href = url - }} - > - Create with OAuth (recommended) - - )} -
- - {hasAccessToken && !accessToken ? ( - - ) : ( - - )} -
- {accessTokenHint && {accessTokenHint}} -
-
-

Toggle Shortcut

- Set a combination of keys for toggling Gitako sidebar. -
-
- - -
- {shortcutHint && {shortcutHint}} -
-
-

More Options

- {varyOptions.map(option => ( - - -
-
- ))} - {reloadHint &&
{reloadHint}
} -
- -
- - )} -
- - {VERSION} - - {activated ? ( - - ) : ( - + return ( + <> +

Settings

+
+
+
+

+ Access Token{' '} + + (?) + +

+ {!hasAccessToken && ( + { + // use js here to make sure redirect_uri is latest url + const url = `https://github.com/login/oauth/authorize?client_id=${ + oauth.clientId + }&scope=repo&redirect_uri=${encodeURIComponent(window.location.href)}` + window.location.href = url + }} + > + Create with OAuth (recommended) + )} +
+ + {hasAccessToken && !accessToken ? ( + + ) : ( + + )} +
+ {accessTokenHint && {accessTokenHint}} +
+
+

Toggle Shortcut

+ Set a combination of keys for toggling Gitako sidebar. +
+
+ + +
+ {shortcutHint && {shortcutHint}} +
+
+

More Options

+ {moreFields.map(field => ( + + +
+
+ ))} + + {reloadHint &&
{reloadHint}
} +
+
- ) + + ) +} + +export function SettingsBar(props: Props) { + const { toggleShowSettings, activated } = props + return ( +
+ {activated && } +
+ + {VERSION} + + {activated ? ( + + ) : ( + + )} +
+
+ ) +} + +async function trySetUpAccessTokenWithCode() { + try { + const search = parseURLSearch() + if ('code' in search) { + const res = await JSONRequest('https://github.com/login/oauth/access_token', { + code: search.code, + client_id: oauth.clientId, + client_secret: oauth.clientSecret, + }) + const { access_token: accessToken, scope } = res + if (scope !== 'repo' || !accessToken) { + throw new Error(`Cannot resolve token response: '${JSON.stringify(res)}'`) + } + window.history.pushState({}, 'removed code', window.location.pathname.replace(/#.*$/, '')) + return accessToken + } + } catch (err) { + raiseError(err) } } diff --git a/src/components/SideBar.tsx b/src/components/SideBar.tsx index 6b4cbbe..3a303c5 100644 --- a/src/components/SideBar.tsx +++ b/src/components/SideBar.tsx @@ -10,135 +10,92 @@ import { ConnectorState, Props } from 'driver/core/SideBar' import * as React from 'react' import { cx } from 'utils/cx' -class RawGitako extends React.PureComponent { - static defaultProps: Partial = { - baseSize: 260, - shouldShow: false, - showSettings: false, - errorDueToAuth: false, - accessToken: '', - toggleShowSideBarShortcut: '', - compressSingletonFolder: true, - copyFileButton: true, - copySnippetButton: true, - disabled: false, - } - - componentDidMount() { - const { init, useListeners } = this.props +const RawGitako: React.FC = function RawGitako(props) { + React.useEffect(() => { + const { init, useListeners } = props init() useListeners(true) - } + return () => useListeners(false) + }, []) - componentWillUnmount() { - const { useListeners } = this.props - useListeners(false) - } + const accessToken = props.configContext.val.access_token + React.useEffect(() => { + if (accessToken) { + // reload when setting new accessToken + if (accessToken) props.init() + } + }, [accessToken, props.init]) - renderAccessDeniedError() { - return ( -
-
Access Denied
-

- Due to{' '} - - limitation of GitHub - {' '} - or{' '} - - auth needs - - , Gitako needs access token to continue. Please follow the instructions in the settings - panel below. -

-
- ) - } - - renderContent() { - const { - errorDueToAuth, - metaData, - treeData, - showSettings, - toggleShowSettings, - compressSingletonFolder, - accessToken, - } = this.props - return ( -
- {metaData && } - {errorDueToAuth - ? this.renderAccessDeniedError() - : metaData && ( - - )} -
- ) - } - - render() { - const { - baseSize, - error, - shouldShow, - showSettings, - accessToken, - compressSingletonFolder, - copyFileButton, - copySnippetButton, - intelligentToggle, - toggleShowSideBarShortcut, - logoContainerElement, - toggleShowSideBar, - toggleShowSettings, - onShortcutChange, - onAccessTokenChange, - setCompressSingleton, - setCopyFile, - setCopySnippet, - setIntelligentToggle, - } = this.props - return ( -
- - - - -
- {this.renderContent()} - + const { + errorDueToAuth, + metaData, + treeData, + baseSize, + error, + shouldShow, + showSettings, + logoContainerElement, + toggleShowSideBar, + toggleShowSettings, + } = props + return ( +
+ + + + +
+
+ {metaData && } + {errorDueToAuth + ? renderAccessDeniedError() + : metaData && ( + + )}
- -
- ) - } + +
+ +
+ ) +} + +RawGitako.defaultProps = { + baseSize: 260, + shouldShow: false, + showSettings: false, + errorDueToAuth: false, + disabled: false, } export const SideBar = connect(SideBarCore)(RawGitako) + +function renderAccessDeniedError() { + return ( +
+
Access Denied
+

+ Due to{' '} + + limitation of GitHub + {' '} + or{' '} + + auth needs + + , Gitako needs access token to continue. Please follow the instructions in the settings + panel below. +

+
+ ) +} diff --git a/src/containers/ConfigsContext.tsx b/src/containers/ConfigsContext.tsx index 9bfb22f..714ce3a 100644 --- a/src/containers/ConfigsContext.tsx +++ b/src/containers/ConfigsContext.tsx @@ -15,11 +15,12 @@ export function ConfigsContextWrapper(props: React.PropsWithChildren) { configsHelper.get().then(setConfigs) }, []) const set = React.useCallback( - () => (configs: Config) => { - configsHelper.set(configs) - setConfigs(configs) + (updatedConfigs: Partial) => { + const mergedConfigs = { ...configs, ...updatedConfigs } as Config + configsHelper.set(mergedConfigs) + setConfigs(mergedConfigs) }, - [setConfigs], + [configs, setConfigs], ) if (configs === null) return null return ( diff --git a/src/content.less b/src/content.less index 1c6fdbc..b5b97c8 100644 --- a/src/content.less +++ b/src/content.less @@ -473,7 +473,7 @@ color: #6a737d; } } - .placeholder-row { + .header-row { flex-shrink: 0; display: flex; justify-content: space-between; diff --git a/src/driver/connect.ts b/src/driver/connect.ts index 6f6165b..0f31303 100644 --- a/src/driver/connect.ts +++ b/src/driver/connect.ts @@ -94,10 +94,9 @@ function link(instance: React.Component, sources: Sources): Wr } export function connect(mapping: Sources) { - return function linkComponent< - State, - ComponentClass extends React.ComponentClass - >(Component: ComponentClass) { + return function linkComponent>( + Component: ComponentType, + ) { return class ConnectedComponent extends React.PureComponent { static displayName = `Connected(${Component.displayName || Component.name})` static defaultProps = Component.defaultProps diff --git a/src/driver/core/FileExplorer.ts b/src/driver/core/FileExplorer.ts index 4d5a4d4..1373e83 100644 --- a/src/driver/core/FileExplorer.ts +++ b/src/driver/core/FileExplorer.ts @@ -1,6 +1,7 @@ import { GetCreatedMethod, MethodCreator } from 'driver/connect' import * as ini from 'ini' import { Base64 } from 'js-base64' +import { Config } from 'utils/configHelper' import * as DOMHelper from 'utils/DOMHelper' import { findNode, searchKeyToRegexps } from 'utils/general' import * as GitHubHelper from 'utils/GitHubHelper' @@ -13,7 +14,6 @@ export type Props = { treeData?: GitHubHelper.TreeData metaData: GitHubHelper.MetaData freeze: boolean - compressSingletonFolder: boolean accessToken: string | undefined toggleShowSettings: React.MouseEventHandler } @@ -137,7 +137,7 @@ function handleParsed(root: TreeNode, parsed: Parsed) { } export const setUpTree: BoundMethodCreator< - [Pick] + [Pick & Pick] > = dispatch => async ({ treeData, metaData, compressSingletonFolder, accessToken }) => { if (!treeData) return dispatch.call(setStateText, 'Rendering File List...') diff --git a/src/driver/core/SideBar.ts b/src/driver/core/SideBar.ts index 4695afb..6de60da 100644 --- a/src/driver/core/SideBar.ts +++ b/src/driver/core/SideBar.ts @@ -1,6 +1,5 @@ import { ConfigsContextShape } from 'containers/ConfigsContext' import { GetCreatedMethod, MethodCreator } from 'driver/connect' -import { Config } from 'utils/configHelper' import * as DOMHelper from 'utils/DOMHelper' import * as GitHubHelper from 'utils/GitHubHelper' import { MetaData, TreeData } from 'utils/GitHubHelper' @@ -34,20 +33,9 @@ export type ConnectorState = { toggleShowSideBar: GetCreatedMethod toggleShowSettings: GetCreatedMethod useListeners: GetCreatedMethod - onAccessTokenChange: GetCreatedMethod - onShortcutChange: GetCreatedMethod - setCopyFile: GetCreatedMethod - setCopySnippet: GetCreatedMethod - setCompressSingleton: GetCreatedMethod - setIntelligentToggle: GetCreatedMethod } & { baseSize: number - toggleShowSideBarShortcut?: string - accessToken?: string -} & Pick< - Config, - 'compressSingletonFolder' | 'copyFileButton' | 'copySnippetButton' | 'intelligentToggle' - > +} type BoundMethodCreator = MethodCreator @@ -83,8 +71,6 @@ export const init: BoundMethodCreator = dispatch => async () => { const { sideBarWidth, access_token: accessToken, - shortcut, - compressSingletonFolder, copyFileButton, copySnippetButton, intelligentToggle, @@ -92,12 +78,6 @@ export const init: BoundMethodCreator = dispatch => async () => { DOMHelper.decorateGitHubPageContent({ copyFileButton, copySnippetButton }) dispatch.set({ baseSize: sideBarWidth, - accessToken, - toggleShowSideBarShortcut: shortcut, - compressSingletonFolder, - copyFileButton, - copySnippetButton, - intelligentToggle, }) if (!metaData.branchName || !metaData.userName) return @@ -178,7 +158,14 @@ export const handleError: BoundMethodCreator<[Error]> = dispatch => async err => } export const onPJAXEnd: BoundMethodCreator = dispatch => () => { - const [{ metaData, copyFileButton, copySnippetButton, intelligentToggle }] = dispatch.get() + const [ + { metaData }, + { + configContext: { + val: { intelligentToggle, copyFileButton, copySnippetButton }, + }, + }, + ] = dispatch.get() DOMHelper.unmountTopProgressBar() DOMHelper.decorateGitHubPageContent({ copyFileButton, copySnippetButton }) const mergedMetaData = { ...metaData, ...URLHelper.parse() } @@ -190,22 +177,25 @@ export const onPJAXEnd: BoundMethodCreator = dispatch => () => { } export const onKeyDown: BoundMethodCreator<[KeyboardEvent]> = dispatch => e => { - const [{ toggleShowSideBarShortcut }] = dispatch.get() - if (toggleShowSideBarShortcut) { + const [ + , + { + configContext: { + val: { shortcut }, + }, + }, + ] = dispatch.get() + if (shortcut) { const keys = keyHelper.parseEvent(e) - if (keys === toggleShowSideBarShortcut) { + if (keys === shortcut) { dispatch.call(toggleShowSideBar) } } } export const toggleShowSideBar: BoundMethodCreator = dispatch => () => { - const [{ intelligentToggle, shouldShow }] = dispatch.get() + const [{ shouldShow }] = dispatch.get() dispatch.call(setShouldShow, !shouldShow) - - if (intelligentToggle !== null) { - dispatch.call(setIntelligentToggle, shouldShow) - } } export const setShouldShow: BoundMethodCreator< @@ -229,43 +219,9 @@ export const setShowSettings: BoundMethodCreator< [ConnectorState['showSettings']] > = dispatch => showSettings => dispatch.set({ showSettings }) -export const onAccessTokenChange: BoundMethodCreator< - [ConnectorState['accessToken']] -> = dispatch => accessToken => { - dispatch.set({ accessToken }) - // reload when setting new accessToken - if (accessToken) { - dispatch.call(init) - } -} - -export const onShortcutChange: BoundMethodCreator< - [ConnectorState['toggleShowSideBarShortcut']] -> = dispatch => shortcut => dispatch.set({ toggleShowSideBarShortcut: shortcut }) - export const setMetaData: BoundMethodCreator<[ConnectorState['metaData']]> = dispatch => metaData => dispatch.set({ metaData }) -export const setCompressSingleton: BoundMethodCreator< - [ConnectorState['compressSingletonFolder']] -> = dispatch => compressSingletonFolder => dispatch.set({ compressSingletonFolder }) - -export const setCopyFile: BoundMethodCreator< - [ConnectorState['copyFileButton']] -> = dispatch => copyFileButton => dispatch.set({ copyFileButton }) - -export const setCopySnippet: BoundMethodCreator< - [ConnectorState['copySnippetButton']] -> = dispatch => copySnippetButton => dispatch.set({ copySnippetButton }) - -export const setIntelligentToggle: BoundMethodCreator< - [ConnectorState['intelligentToggle']] -> = dispatch => intelligentToggle => { - const [, { configContext }] = dispatch.get() - configContext.set({ intelligentToggle }) - dispatch.set({ intelligentToggle }) -} - export const useListeners: BoundMethodCreator<[boolean]> = dispatch => { const $onPJAXEnd = () => dispatch.call(onPJAXEnd) const $onKeyDown = (e: KeyboardEvent) => dispatch.call(onKeyDown, e) diff --git a/src/utils/hooks.ts b/src/utils/hooks.ts index 258481d..8f24fde 100644 --- a/src/utils/hooks.ts +++ b/src/utils/hooks.ts @@ -54,3 +54,17 @@ export function useStates( const [val, set] = React.useState(initialState) return { val, set } } + +export function useAsyncMemo( + factory: (dependencies: D) => T | Promise, + deps: D, + initialValue: T, +): T { + const firstTime = React.useRef(true) + const state = useStates(() => initialValue) + React.useEffect(() => { + if (firstTime.current) firstTime.current = false + Promise.resolve(factory(deps)).then(consumed => state.set(() => consumed)) + }, deps) + return state.val +} diff --git a/tsconfig.json b/tsconfig.json index e181701..08918c4 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,5 +1,4 @@ { - "files": ["src/content.tsx"], "compilerOptions": { "target": "es2016", "outDir": "dist", From 819fc17faf3093d35313ac848cf12c040946798e Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Tue, 12 Nov 2019 23:36:17 +0800 Subject: [PATCH 24/98] refactor: access token and intelligent toggle --- src/components/SettingsBar.tsx | 91 ++++++++++++---------------------- src/components/SideBar.tsx | 52 +++++++++++++++---- src/driver/core/SideBar.ts | 9 +++- 3 files changed, 82 insertions(+), 70 deletions(-) diff --git a/src/components/SettingsBar.tsx b/src/components/SettingsBar.tsx index 98ac7e1..3d96cd6 100644 --- a/src/components/SettingsBar.tsx +++ b/src/components/SettingsBar.tsx @@ -1,9 +1,8 @@ -import { raiseError } from 'analytics' import { Icon } from 'components/Icon' import { useConfigs } from 'containers/ConfigsContext' import { oauth, VERSION } from 'env' import * as React from 'react' -import { friendlyFormatShortcut, JSONRequest, parseURLSearch } from 'utils/general' +import { friendlyFormatShortcut } from 'utils/general' import { useStates } from 'utils/hooks' import * as keyHelper from 'utils/keyHelper' import { SimpleField, SimpleFieldInput } from './MoreOption' @@ -60,19 +59,21 @@ function SettingsBarContent() { const useToggleShowSideBarShortcut = useStates(configContext.val.shortcut) const useReloadHint = useStates('') - React.useEffect(() => { - if (!configContext.val.access_token) { - trySetUpAccessTokenWithCode().then(accessToken => { - useAccessToken.set(accessToken) - saveToken('') - }) - } - }, []) + const { val: accessTokenHint } = useAccessTokenHint + const { val: toggleShowSideBarShortcut } = useToggleShowSideBarShortcut + const { val: shortcutHint } = useShortcutHint + const { val: accessToken } = useAccessToken + const { val: reloadHint } = useReloadHint React.useEffect(() => { useToggleShowSideBarShortcut.set(configContext.val.shortcut) }, [configContext.val.shortcut]) + React.useEffect(() => { + // clear input when access token updates + useAccessToken.set('') + }, [configContext.val.access_token]) + const onInputAccessToken = React.useCallback( ({ currentTarget: { value } }: React.FormEvent) => { useAccessToken.set(value) @@ -87,28 +88,25 @@ function SettingsBarContent() { if (key === 'Enter') saveToken() }, []) - const saveToken = React.useCallback(async (hint?: typeof useAccessTokenHint.val) => { - const { val: accessToken } = useAccessToken - if (accessToken) { - configContext.set({ access_token: accessToken }) - useAccessToken.set('') - useAccessTokenHint.set( - hint || ( - - window.location.reload()}> - Reload - {' '} - to activate! - - ), - ) - } - }, []) - - const clearToken = React.useCallback(async () => { - configContext.set({ access_token: '' }) - useAccessToken.set('') - }, []) + const saveToken = React.useCallback( + async (hint?: typeof useAccessTokenHint.val) => { + if (accessToken) { + configContext.set({ access_token: accessToken }) + useAccessToken.set('') + useAccessTokenHint.set( + hint || ( + + window.location.reload()}> + Reload + {' '} + to activate! + + ), + ) + } + }, + [accessToken], + ) const saveShortcut = React.useCallback(async () => { const { val: toggleShowSideBarShortcut } = useToggleShowSideBarShortcut @@ -140,12 +138,6 @@ function SettingsBarContent() { [], ) - const { val: accessTokenHint } = useAccessTokenHint - const { val: toggleShowSideBarShortcut } = useToggleShowSideBarShortcut - const { val: shortcutHint } = useShortcutHint - const { val: accessToken } = useAccessToken - const { val: reloadHint } = useReloadHint - return ( <>

Settings

@@ -182,7 +174,7 @@ function SettingsBarContent() { onKeyPress={onPressAccessToken} /> {hasAccessToken && !accessToken ? ( - ) : ( @@ -264,24 +256,3 @@ export function SettingsBar(props: Props) {
) } - -async function trySetUpAccessTokenWithCode() { - try { - const search = parseURLSearch() - if ('code' in search) { - const res = await JSONRequest('https://github.com/login/oauth/access_token', { - code: search.code, - client_id: oauth.clientId, - client_secret: oauth.clientSecret, - }) - const { access_token: accessToken, scope } = res - if (scope !== 'repo' || !accessToken) { - throw new Error(`Cannot resolve token response: '${JSON.stringify(res)}'`) - } - window.history.pushState({}, 'removed code', window.location.pathname.replace(/#.*$/, '')) - return accessToken - } - } catch (err) { - raiseError(err) - } -} diff --git a/src/components/SideBar.tsx b/src/components/SideBar.tsx index 3a303c5..e4eae82 100644 --- a/src/components/SideBar.tsx +++ b/src/components/SideBar.tsx @@ -1,30 +1,43 @@ +import { raiseError } from 'analytics' import { FileExplorer } from 'components/FileExplorer' import { MetaBar } from 'components/MetaBar' import { Portal } from 'components/Portal' import { Resizable } from 'components/Resizable' import { SettingsBar } from 'components/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' +import { oauth } from 'env' import * as React from 'react' import { cx } from 'utils/cx' +import { JSONRequest, parseURLSearch } from 'utils/general' const RawGitako: React.FC = function RawGitako(props) { + const configContext = useConfigs() + const accessToken = props.configContext.val.access_token + React.useEffect(() => { - const { init, useListeners } = props - init() + const { init } = props + ;(async function() { + if (!accessToken) { + const accessToken = await trySetUpAccessTokenWithCode() + configContext.set({ access_token: accessToken }) + } + init() + })() + }, []) + + React.useEffect(() => { + const { useListeners } = props useListeners(true) return () => useListeners(false) }, []) - const accessToken = props.configContext.val.access_token - React.useEffect(() => { - if (accessToken) { - // reload when setting new accessToken - if (accessToken) props.init() - } - }, [accessToken, props.init]) + // reload when setting new accessToken + // special way to implement didUpdate + React.useEffect(() => () => props.init(), [accessToken]) const { errorDueToAuth, @@ -99,3 +112,24 @@ function renderAccessDeniedError() {
) } + +async function trySetUpAccessTokenWithCode() { + try { + const search = parseURLSearch() + if ('code' in search) { + const res = await JSONRequest('https://github.com/login/oauth/access_token', { + code: search.code, + client_id: oauth.clientId, + client_secret: oauth.clientSecret, + }) + const { access_token: accessToken, scope } = res + if (scope !== 'repo' || !accessToken) { + throw new Error(`Cannot resolve token response: '${JSON.stringify(res)}'`) + } + window.history.pushState({}, 'removed code', window.location.pathname.replace(/#.*$/, '')) + return accessToken + } + } catch (err) { + raiseError(err) + } +} diff --git a/src/driver/core/SideBar.ts b/src/driver/core/SideBar.ts index 6de60da..2a594d1 100644 --- a/src/driver/core/SideBar.ts +++ b/src/driver/core/SideBar.ts @@ -194,8 +194,15 @@ export const onKeyDown: BoundMethodCreator<[KeyboardEvent]> = dispatch => e => { } export const toggleShowSideBar: BoundMethodCreator = dispatch => () => { - const [{ shouldShow }] = dispatch.get() + const [{ shouldShow }, { configContext }] = dispatch.get() dispatch.call(setShouldShow, !shouldShow) + + const { + val: { intelligentToggle }, + } = configContext + if (intelligentToggle !== null) { + configContext.set({ intelligentToggle: !shouldShow }) + } } export const setShouldShow: BoundMethodCreator< From 943a2ccc4b159273a70e1fdb05ee8c133972a921 Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Wed, 13 Nov 2019 13:44:57 +0800 Subject: [PATCH 25/98] build: support optional chaining --- .babelrc | 5 +++-- package.json | 1 + yarn.lock | 15 +++++++++++++++ 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/.babelrc b/.babelrc index e952314..4d21215 100644 --- a/.babelrc +++ b/.babelrc @@ -5,13 +5,14 @@ { "targets": { "chrome": "67" - }, + } } ], "@babel/preset-typescript", - "@babel/preset-react", + "@babel/preset-react" ], "plugins": [ + "@babel/plugin-proposal-optional-chaining", "@babel/plugin-proposal-class-properties", "@babel/plugin-proposal-object-rest-spread" ] diff --git a/package.json b/package.json index b5d5721..8643643 100644 --- a/package.json +++ b/package.json @@ -40,6 +40,7 @@ "@babel/core": "^7.3.4", "@babel/plugin-proposal-class-properties": "^7.3.4", "@babel/plugin-proposal-object-rest-spread": "^7.3.4", + "@babel/plugin-proposal-optional-chaining": "^7.6.0", "@babel/preset-env": "^7.3.4", "@babel/preset-react": "^7.0.0", "@babel/preset-typescript": "^7.3.3", diff --git a/yarn.lock b/yarn.lock index 4ab28d4..6f77807 100644 --- a/yarn.lock +++ b/yarn.lock @@ -304,6 +304,14 @@ "@babel/helper-plugin-utils" "^7.0.0" "@babel/plugin-syntax-optional-catch-binding" "^7.2.0" +"@babel/plugin-proposal-optional-chaining@^7.6.0": + version "7.6.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.6.0.tgz#e9bf1f9b9ba10c77c033082da75f068389041af8" + integrity sha512-kj4gkZ6qUggkprRq3Uh5KP8XnE1MdIO0J7MhdDX8+rAbB6dJ2UrensGIS+0NPZAaaJ1Vr0PN6oLUgXMU1uMcSg== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/plugin-syntax-optional-chaining" "^7.2.0" + "@babel/plugin-proposal-unicode-property-regex@^7.4.4": version "7.4.4" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.4.4.tgz#501ffd9826c0b91da22690720722ac7cb1ca9c78" @@ -355,6 +363,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.0.0" +"@babel/plugin-syntax-optional-chaining@^7.2.0": + version "7.2.0" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.2.0.tgz#a59d6ae8c167e7608eaa443fda9fa8fa6bf21dff" + integrity sha512-HtGCtvp5Uq/jH/WNUPkK6b7rufnCPLLlDAFN7cmACoIjaOOiXxUt3SswU5loHqrhtqTsa/WoLQ1OQ1AGuZqaWA== + dependencies: + "@babel/helper-plugin-utils" "^7.0.0" + "@babel/plugin-syntax-typescript@^7.2.0": version "7.3.3" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.3.3.tgz#a7cc3f66119a9f7ebe2de5383cce193473d65991" From 7bee13c6c3177cefc5b6031f0430a1c85424f1d4 Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Wed, 13 Nov 2019 13:45:33 +0800 Subject: [PATCH 26/98] feat: make clippy button react --- src/components/Clippy.tsx | 33 +++++++++++++++++ src/content.less | 8 ++--- src/utils/DOMHelper.ts | 74 +++++++++------------------------------ src/utils/general.ts | 56 +++++++++++++++++------------ 4 files changed, 85 insertions(+), 86 deletions(-) create mode 100644 src/components/Clippy.tsx diff --git a/src/components/Clippy.tsx b/src/components/Clippy.tsx new file mode 100644 index 0000000..bd397f1 --- /dev/null +++ b/src/components/Clippy.tsx @@ -0,0 +1,33 @@ +import * as React from 'react' +import { cx } from 'utils/cx' +import { copyElementContent } from 'utils/DOMHelper' + +type Props = { + codeSnippetElement: Element +} + +export function Clippy({ codeSnippetElement }: Props) { + const [status, setStatus] = React.useState<'normal' | 'success' | 'fail'>('normal') + React.useEffect(() => { + const timer = window.setTimeout(() => { + setStatus('normal') + }, 1000) + return () => window.clearTimeout(timer) + }, [status]) + + const onClippyClick = React.useCallback(function onClippyClick() { + if (copyElementContent(codeSnippetElement)) { + setStatus('success') + } else { + setStatus('fail') + } + }, []) + + return ( +
+ +
+ ) +} diff --git a/src/content.less b/src/content.less index b5b97c8..b0ac42f 100644 --- a/src/content.less +++ b/src/content.less @@ -59,14 +59,10 @@ background-image: url('~@primer/octicons/build/svg/clippy.svg?inline'); background-position: center; background-repeat: no-repeat; - } - &.success { - .icon { + &.success { background-image: url('~@primer/octicons/build/svg/check.svg?inline'); } - } - &.fail { - .icon { + &.fail { background-image: url('~@primer/octicons/build/svg/x.svg?inline'); } } diff --git a/src/utils/DOMHelper.ts b/src/utils/DOMHelper.ts index 19dab59..46d6c47 100644 --- a/src/utils/DOMHelper.ts +++ b/src/utils/DOMHelper.ts @@ -2,11 +2,13 @@ * this helper helps manipulating DOM */ import { raiseError } from 'analytics' +import { Clippy } from 'components/Clippy' import { CopyFileButton } from 'components/CopyFileButton' import * as NProgress from 'nprogress' import * as PJAX from 'pjax' import * as React from 'react' import * as ReactDOM from 'react-dom' +import { renderReact } from './general' NProgress.configure({ showSpinner: false }) @@ -37,10 +39,10 @@ function $ any, O extends () => a otherwise?: O, ): E extends never ? O extends never - ? (Element | null) + ? Element | null : ReturnType | null : O extends never - ? (ReturnType | null) + ? ReturnType | null : ReturnType | ReturnType { const element = document.querySelector(selector) if (element) { @@ -221,10 +223,8 @@ export function attachCopyFileBtn() { /** * copy content of a DOM element to clipboard - * @param {element} element - * @returns {boolean} whether copy is successful */ -export function copyElementContent(element: Element) { +export function copyElementContent(element: Element): boolean { let selection = window.getSelection() if (selection) selection.removeAllRanges() const range = document.createRange() @@ -237,54 +237,6 @@ export function copyElementContent(element: Element) { return isCopySuccessful } -/** - * create a copy file content button `clippy` - * once mouse enters a code snippet of markdown, move clippy into it - * user can copy the snippet's content by click it - * - * TODO: 'reactify' it - */ -function createClippy() { - function setTempClippyIconFeedback(clippy: Element, type: 'success' | 'fail') { - const tempIconClassName = type === 'success' ? 'success' : 'fail' - clippy.classList.add(tempIconClassName) - window.setTimeout(() => { - clippy.classList.remove(tempIconClassName) - }, 1000) - } - - /** - *
- * - *
- */ - const clippyWrapper = document.createElement('div') - clippyWrapper.classList.add('clippy-wrapper') - const clippy = document.createElement('button') - clippy.classList.add('clippy') - const clippyIcon = document.createElement('i') - clippyIcon.classList.add('icon') - - clippyWrapper.appendChild(clippy) - clippy.appendChild(clippyIcon) - - // set clipboard with current code snippet element's content - clippy.addEventListener('click', function onClippyClick() { - if (copyElementContent(currentCodeSnippetElement)) { - setTempClippyIconFeedback(clippy, 'success') - } else { - setTempClippyIconFeedback(clippy, 'fail') - } - }) - - return clippyWrapper -} - -const clippy = createClippy() - -let currentCodeSnippetElement: Element export function attachCopySnippet() { const readmeSelector = '.repository-content div#readme' return $(readmeSelector, () => { @@ -292,12 +244,15 @@ export function attachCopySnippet() { $( readmeArticleSelector, readmeElement => - readmeElement.addEventListener('mouseover', e => { + readmeElement.addEventListener('mouseover', async e => { // only move clippy when mouse is over a new snippet(
)
           const target = e.target as Element
           if (target.nodeName === 'PRE') {
-            if (currentCodeSnippetElement !== target) {
-              currentCodeSnippetElement = target
+            if (
+              target.previousSibling === null ||
+              !(target.previousSibling instanceof Element) ||
+              !target.previousSibling.classList.contains('clippy-wrapper')
+            ) {
               /**
                *  
*
     
@@ -306,7 +261,12 @@ export function attachCopySnippet() {
                *    
* */ - if (target.parentNode) target.parentNode.insertBefore(clippy, target) + if (target.parentNode) { + const clippyElement = await renderReact( + React.createElement(Clippy, { codeSnippetElement: target }), + ) + target.parentNode.insertBefore(clippyElement, target) + } } } }), diff --git a/src/utils/general.ts b/src/utils/general.ts index 9fc3281..cde3a0d 100644 --- a/src/utils/general.ts +++ b/src/utils/general.ts @@ -1,16 +1,15 @@ +import { ReactElement } from 'react' +import * as ReactDOM from 'react-dom' import { TreeNode } from './VisibleNodesGenerator' export function pick(source: T, keys: string[]): Partial { if (keys && typeof keys === 'object') { - return (Array.isArray(keys) ? keys : Object.keys(keys)).reduce( - (copy, key) => { - if (key in source) { - copy[key as keyof T] = source[key as keyof T] - } - return copy - }, - {} as Partial, - ) + return (Array.isArray(keys) ? keys : Object.keys(keys)).reduce((copy, key) => { + if (key in source) { + copy[key as keyof T] = source[key as keyof T] + } + return copy + }, {} as Partial) } return {} as Partial } @@ -114,20 +113,22 @@ export function parseURLSearch(search: string = window.location.search) { } export async function JSONRequest(url: string, data: any, extra: RequestInit = { method: 'post' }) { - return (await fetch(url, { - mode: 'cors', - cache: 'no-cache', - credentials: 'same-origin', - headers: { - 'Content-Type': 'application/json', - Accept: 'application/json', - }, - redirect: 'follow', - referrerPolicy: 'no-referrer', - method: extra.method || 'post', - body: JSON.stringify(data), - ...extra, - })).json() + return ( + await fetch(url, { + mode: 'cors', + cache: 'no-cache', + credentials: 'same-origin', + headers: { + 'Content-Type': 'application/json', + Accept: 'application/json', + }, + redirect: 'follow', + referrerPolicy: 'no-referrer', + method: extra.method || 'post', + body: JSON.stringify(data), + ...extra, + }) + ).json() } export function searchKeyToRegexps(searchKey: string) { @@ -140,3 +141,12 @@ export function searchKeyToRegexps(searchKey: string) { return [/$^/] // matching nothing if failed transforming regexp } } + +export async function renderReact(element: ReactElement) { + return new Promise(resolve => { + const mount = document.createElement('div') + ReactDOM.render(element, mount, () => { + resolve(mount.childNodes[0]) + }) + }) +} From d4e4f777775eb7607af52310145510458ebd8be3 Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Wed, 13 Nov 2019 14:45:58 +0800 Subject: [PATCH 27/98] refactor: extract useListener --- src/components/SideBar.tsx | 31 +++++++++++++++++++-- src/driver/core/SideBar.ts | 57 +++++--------------------------------- 2 files changed, 35 insertions(+), 53 deletions(-) diff --git a/src/components/SideBar.tsx b/src/components/SideBar.tsx index e4eae82..a58639e 100644 --- a/src/components/SideBar.tsx +++ b/src/components/SideBar.tsx @@ -12,7 +12,9 @@ import { ConnectorState, Props } from 'driver/core/SideBar' import { oauth } from 'env' import * as React from 'react' import { cx } from 'utils/cx' +import * as DOMHelper from 'utils/DOMHelper' import { JSONRequest, parseURLSearch } from 'utils/general' +import * as keyHelper from 'utils/keyHelper' const RawGitako: React.FC = function RawGitako(props) { const configContext = useConfigs() @@ -29,10 +31,33 @@ const RawGitako: React.FC = function RawGitako(props) { })() }, []) + const onKeyDown = React.useCallback( + configContext.val.shortcut + ? (e: KeyboardEvent) => { + const keys = keyHelper.parseEvent(e) + if (keys === configContext.val.shortcut) { + props.toggleShowSideBar() + } + } + : () => {}, + [configContext.val.shortcut], + ) + React.useEffect(() => { - const { useListeners } = props - useListeners(true) - return () => useListeners(false) + if (props.disabled) return + window.addEventListener('keydown', onKeyDown) + return () => window.removeEventListener('keydown', onKeyDown) + }, [props.disabled, onKeyDown]) + + React.useEffect(() => { + if (props.disabled) return + window.addEventListener('pjax:complete', props.onPJAXEnd) + return () => window.removeEventListener('pjax:complete', props.onPJAXEnd) + }, [props.disabled]) + + React.useEffect(() => { + const { copyFileButton, copySnippetButton } = configContext.val + DOMHelper.decorateGitHubPageContent({ copyFileButton, copySnippetButton }) }, []) // reload when setting new accessToken diff --git a/src/driver/core/SideBar.ts b/src/driver/core/SideBar.ts index 2a594d1..3fd03d8 100644 --- a/src/driver/core/SideBar.ts +++ b/src/driver/core/SideBar.ts @@ -3,7 +3,6 @@ import { GetCreatedMethod, MethodCreator } from 'driver/connect' import * as DOMHelper from 'utils/DOMHelper' import * as GitHubHelper from 'utils/GitHubHelper' import { MetaData, TreeData } from 'utils/GitHubHelper' -import * as keyHelper from 'utils/keyHelper' import * as URLHelper from 'utils/URLHelper' export type Props = { @@ -29,10 +28,8 @@ export type ConnectorState = { } & { init: GetCreatedMethod onPJAXEnd: GetCreatedMethod - onKeyDown: GetCreatedMethod toggleShowSideBar: GetCreatedMethod toggleShowSettings: GetCreatedMethod - useListeners: GetCreatedMethod } & { baseSize: number } @@ -68,14 +65,7 @@ export const init: BoundMethodCreator = dispatch => async () => { metaData.branchName = detectedBranchName || 'master' dispatch.call(setMetaData, metaData) const [, { configContext }] = dispatch.get() - const { - sideBarWidth, - access_token: accessToken, - copyFileButton, - copySnippetButton, - intelligentToggle, - } = configContext.val - DOMHelper.decorateGitHubPageContent({ copyFileButton, copySnippetButton }) + const { sideBarWidth, access_token: accessToken, intelligentToggle } = configContext.val dispatch.set({ baseSize: sideBarWidth, }) @@ -151,7 +141,6 @@ export const handleError: BoundMethodCreator<[Error]> = dispatch => async err => dispatch.call(setShowSettings, true) dispatch.call(setShouldShow, true) } else { - dispatch.call(useListeners, false) dispatch.call(setError, 'Gitako ate a bug, but it should recovery soon!') throw err } @@ -176,23 +165,6 @@ export const onPJAXEnd: BoundMethodCreator = dispatch => () => { } } -export const onKeyDown: BoundMethodCreator<[KeyboardEvent]> = dispatch => e => { - const [ - , - { - configContext: { - val: { shortcut }, - }, - }, - ] = dispatch.get() - if (shortcut) { - const keys = keyHelper.parseEvent(e) - if (keys === shortcut) { - dispatch.call(toggleShowSideBar) - } - } -} - export const toggleShowSideBar: BoundMethodCreator = dispatch => () => { const [{ shouldShow }, { configContext }] = dispatch.get() dispatch.call(setShouldShow, !shouldShow) @@ -205,9 +177,9 @@ export const toggleShowSideBar: BoundMethodCreator = dispatch => () => { } } -export const setShouldShow: BoundMethodCreator< - [ConnectorState['shouldShow']] -> = dispatch => shouldShow => { +export const setShouldShow: BoundMethodCreator<[ + ConnectorState['shouldShow'], +]> = dispatch => shouldShow => { dispatch.set({ shouldShow }, shouldShow ? DOMHelper.focusFileExplorer : undefined) DOMHelper.setBodyIndent(shouldShow) } @@ -222,24 +194,9 @@ export const toggleShowSettings: BoundMethodCreator = dispatch => () => showSettings: !showSettings, })) -export const setShowSettings: BoundMethodCreator< - [ConnectorState['showSettings']] -> = dispatch => showSettings => dispatch.set({ showSettings }) +export const setShowSettings: BoundMethodCreator<[ + ConnectorState['showSettings'], +]> = dispatch => showSettings => dispatch.set({ showSettings }) export const setMetaData: BoundMethodCreator<[ConnectorState['metaData']]> = dispatch => metaData => dispatch.set({ metaData }) - -export const useListeners: BoundMethodCreator<[boolean]> = dispatch => { - const $onPJAXEnd = () => dispatch.call(onPJAXEnd) - const $onKeyDown = (e: KeyboardEvent) => dispatch.call(onKeyDown, e) - return on => { - const [{ disabled }] = dispatch.get() - if (on && !disabled) { - window.addEventListener('pjax:complete', $onPJAXEnd) - window.addEventListener('keydown', $onKeyDown) - } else { - window.removeEventListener('pjax:complete', $onPJAXEnd) - window.removeEventListener('keydown', $onKeyDown) - } - } -} From be6d95355c314ea82a8a34cafa6e4d3eaa670605 Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Wed, 13 Nov 2019 14:57:03 +0800 Subject: [PATCH 28/98] fix: deprecate hacky did-update --- src/components/SideBar.tsx | 6 +++--- src/utils/hooks.ts | 11 +++++++++++ 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/components/SideBar.tsx b/src/components/SideBar.tsx index a58639e..c2d40ca 100644 --- a/src/components/SideBar.tsx +++ b/src/components/SideBar.tsx @@ -14,6 +14,7 @@ import * as React from 'react' import { cx } from 'utils/cx' import * as DOMHelper from 'utils/DOMHelper' import { JSONRequest, parseURLSearch } from 'utils/general' +import { useDidUpdate } from 'utils/hooks' import * as keyHelper from 'utils/keyHelper' const RawGitako: React.FC = function RawGitako(props) { @@ -60,9 +61,8 @@ const RawGitako: React.FC = function RawGitako(props) { DOMHelper.decorateGitHubPageContent({ copyFileButton, copySnippetButton }) }, []) - // reload when setting new accessToken - // special way to implement didUpdate - React.useEffect(() => () => props.init(), [accessToken]) + // init again when setting new accessToken + useDidUpdate(() => props.init(), [accessToken]) const { errorDueToAuth, diff --git a/src/utils/hooks.ts b/src/utils/hooks.ts index 8f24fde..fe55030 100644 --- a/src/utils/hooks.ts +++ b/src/utils/hooks.ts @@ -68,3 +68,14 @@ export function useAsyncMemo( }, deps) return state.val } + +export function useDidUpdate(effect: React.EffectCallback, deps?: React.DependencyList) { + const firstTime = React.useRef(true) + React.useEffect(() => { + if (firstTime.current) { + firstTime.current = false + return + } + return effect() + }, deps) +} From 2c4af434851682a9cd9f464e63737c01263f3e59 Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Wed, 13 Nov 2019 15:05:14 +0800 Subject: [PATCH 29/98] refactor: extract onPJAXEnd --- src/components/SideBar.tsx | 19 +++++++++++++++---- src/driver/core/SideBar.ts | 22 ++-------------------- 2 files changed, 17 insertions(+), 24 deletions(-) diff --git a/src/components/SideBar.tsx b/src/components/SideBar.tsx index c2d40ca..f02d6c5 100644 --- a/src/components/SideBar.tsx +++ b/src/components/SideBar.tsx @@ -16,6 +16,7 @@ import * as DOMHelper from 'utils/DOMHelper' import { JSONRequest, parseURLSearch } from 'utils/general' import { useDidUpdate } from 'utils/hooks' import * as keyHelper from 'utils/keyHelper' +import * as URLHelper from 'utils/URLHelper' const RawGitako: React.FC = function RawGitako(props) { const configContext = useConfigs() @@ -50,16 +51,26 @@ const RawGitako: React.FC = function RawGitako(props) { return () => window.removeEventListener('keydown', onKeyDown) }, [props.disabled, onKeyDown]) + const onPJAXEnd = React.useCallback(() => { + DOMHelper.unmountTopProgressBar() + const mergedMetaData = { ...props.metaData, ...URLHelper.parse() } + props.setMetaData(mergedMetaData) + + if (configContext.val.intelligentToggle === null) { + props.setShouldShow(URLHelper.isInCodePage(mergedMetaData)) + } + }, [props.metaData, configContext.val.intelligentToggle]) + React.useEffect(() => { if (props.disabled) return - window.addEventListener('pjax:complete', props.onPJAXEnd) - return () => window.removeEventListener('pjax:complete', props.onPJAXEnd) - }, [props.disabled]) + window.addEventListener('pjax:complete', onPJAXEnd) + return () => window.removeEventListener('pjax:complete', onPJAXEnd) + }, [props.disabled, onPJAXEnd]) React.useEffect(() => { const { copyFileButton, copySnippetButton } = configContext.val DOMHelper.decorateGitHubPageContent({ copyFileButton, copySnippetButton }) - }, []) + }, [props.metaData]) // init again when setting new accessToken useDidUpdate(() => props.init(), [accessToken]) diff --git a/src/driver/core/SideBar.ts b/src/driver/core/SideBar.ts index 3fd03d8..f8f282d 100644 --- a/src/driver/core/SideBar.ts +++ b/src/driver/core/SideBar.ts @@ -27,7 +27,8 @@ export type ConnectorState = { initializingPromise: Promise | null } & { init: GetCreatedMethod - onPJAXEnd: GetCreatedMethod + setMetaData: GetCreatedMethod + setShouldShow: GetCreatedMethod toggleShowSideBar: GetCreatedMethod toggleShowSettings: GetCreatedMethod } & { @@ -146,25 +147,6 @@ export const handleError: BoundMethodCreator<[Error]> = dispatch => async err => } } -export const onPJAXEnd: BoundMethodCreator = dispatch => () => { - const [ - { metaData }, - { - configContext: { - val: { intelligentToggle, copyFileButton, copySnippetButton }, - }, - }, - ] = dispatch.get() - DOMHelper.unmountTopProgressBar() - DOMHelper.decorateGitHubPageContent({ copyFileButton, copySnippetButton }) - const mergedMetaData = { ...metaData, ...URLHelper.parse() } - dispatch.call(setMetaData, mergedMetaData) - - if (intelligentToggle === null) { - dispatch.call(setShouldShow, URLHelper.isInCodePage(mergedMetaData)) - } -} - export const toggleShowSideBar: BoundMethodCreator = dispatch => () => { const [{ shouldShow }, { configContext }] = dispatch.get() dispatch.call(setShouldShow, !shouldShow) From 0f884e26dd9e9c23b6d0c8923cee4ab00cdad886 Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Wed, 13 Nov 2019 15:45:36 +0800 Subject: [PATCH 30/98] refactor: cancel add buttons --- src/components/SideBar.tsx | 24 +++++++++++++++--------- src/utils/DOMHelper.ts | 28 +++++++++------------------- 2 files changed, 24 insertions(+), 28 deletions(-) diff --git a/src/components/SideBar.tsx b/src/components/SideBar.tsx index f02d6c5..cf38dc8 100644 --- a/src/components/SideBar.tsx +++ b/src/components/SideBar.tsx @@ -53,13 +53,8 @@ const RawGitako: React.FC = function RawGitako(props) { const onPJAXEnd = React.useCallback(() => { DOMHelper.unmountTopProgressBar() - const mergedMetaData = { ...props.metaData, ...URLHelper.parse() } - props.setMetaData(mergedMetaData) - - if (configContext.val.intelligentToggle === null) { - props.setShouldShow(URLHelper.isInCodePage(mergedMetaData)) - } - }, [props.metaData, configContext.val.intelligentToggle]) + props.setMetaData({ ...props.metaData, ...URLHelper.parse() }) + }, [props.metaData]) React.useEffect(() => { if (props.disabled) return @@ -68,8 +63,19 @@ const RawGitako: React.FC = function RawGitako(props) { }, [props.disabled, onPJAXEnd]) React.useEffect(() => { - const { copyFileButton, copySnippetButton } = configContext.val - DOMHelper.decorateGitHubPageContent({ copyFileButton, copySnippetButton }) + if (configContext.val.intelligentToggle === null) { + props.setShouldShow(URLHelper.isInCodePage(props.metaData)) + } + }, [props.metaData, configContext.val.intelligentToggle]) + + React.useEffect(() => { + const { copyFileButton } = configContext.val + if (copyFileButton) return DOMHelper.attachCopyFileBtn() + }, [props.metaData]) + + React.useEffect(() => { + const { copySnippetButton } = configContext.val + if (copySnippetButton) DOMHelper.attachCopySnippet() }, [props.metaData]) // init again when setting new accessToken diff --git a/src/utils/DOMHelper.ts b/src/utils/DOMHelper.ts index 46d6c47..3de1476 100644 --- a/src/utils/DOMHelper.ts +++ b/src/utils/DOMHelper.ts @@ -209,6 +209,7 @@ export function attachCopyFileBtn() { raiseError(new Error(`No button groups found`)) } + const buttons: HTMLElement[] = [] buttonGroups.forEach(buttonGroup => { if (!buttonGroup.lastElementChild) return const portal = ReactDOM.createPortal(React.createElement(CopyFileButton), buttonGroup) @@ -217,7 +218,12 @@ export function attachCopyFileBtn() { buttonGroup.appendChild(seedElementForButton) ReactDOM.render(portal, seedElementForButton) buttonGroup.removeChild(seedElementForButton) + buttons.push(seedElementForButton) }) + return () => + buttons.forEach(button => { + button.parentElement?.removeChild(button) + }) } } @@ -241,13 +247,11 @@ export function attachCopySnippet() { const readmeSelector = '.repository-content div#readme' return $(readmeSelector, () => { const readmeArticleSelector = '.repository-content div#readme article' - $( + return $( readmeArticleSelector, readmeElement => - readmeElement.addEventListener('mouseover', async e => { - // only move clippy when mouse is over a new snippet(
)
-          const target = e.target as Element
-          if (target.nodeName === 'PRE') {
+        readmeElement.addEventListener('mouseover', async ({ target }) => {
+          if (target instanceof Element && target.nodeName === 'PRE') {
             if (
               target.previousSibling === null ||
               !(target.previousSibling instanceof Element) ||
@@ -304,20 +308,6 @@ export function focusSearchInput() {
   })
 }
 
-/**
- * a combination of few above functions
- */
-export function decorateGitHubPageContent({
-  copyFileButton,
-  copySnippetButton,
-}: {
-  copyFileButton: boolean
-  copySnippetButton: boolean
-}) {
-  if (copyFileButton) attachCopyFileBtn()
-  if (copySnippetButton) attachCopySnippet()
-}
-
 export function mountTopProgressBar() {
   NProgress.start()
 }

From 6f4c26d36be175a86b18a0e34ae5c909217ba553 Mon Sep 17 00:00:00 2001
From: EnixCoda 
Date: Wed, 13 Nov 2019 16:20:03 +0800
Subject: [PATCH 31/98] feat: apply settings updates instantly

---
 src/components/SettingsBar.tsx | 20 +-------------------
 src/components/SideBar.tsx     | 15 ++++++++-------
 src/utils/DOMHelper.ts         | 30 ++++++++++++++++++------------
 3 files changed, 27 insertions(+), 38 deletions(-)

diff --git a/src/components/SettingsBar.tsx b/src/components/SettingsBar.tsx
index 3d96cd6..c7d75a2 100644
--- a/src/components/SettingsBar.tsx
+++ b/src/components/SettingsBar.tsx
@@ -124,20 +124,6 @@ function SettingsBarContent() {
     useToggleShowSideBarShortcut.set(shortcut)
   }, [])
 
-  const showReloadHint = React.useCallback(
-    () =>
-      useReloadHint.set(
-        
-          Saved,{' '}
-           window.location.reload()}>
-            reload
-          {' '}
-          to apply.
-        ,
-      ),
-    [],
-  )
-
   return (
     <>
       

Settings

@@ -207,11 +193,7 @@ function SettingsBarContent() {

More Options

{moreFields.map(field => ( - +
))} diff --git a/src/components/SideBar.tsx b/src/components/SideBar.tsx index cf38dc8..8ff4539 100644 --- a/src/components/SideBar.tsx +++ b/src/components/SideBar.tsx @@ -54,7 +54,10 @@ const RawGitako: React.FC = function RawGitako(props) { const onPJAXEnd = React.useCallback(() => { DOMHelper.unmountTopProgressBar() props.setMetaData({ ...props.metaData, ...URLHelper.parse() }) - }, [props.metaData]) + // TODO: update state to re-trigger DOM effects + // if (configContext.val.copyFileButton) DOMHelper.attachCopyFileBtn() + // if (configContext.val.copySnippetButton) DOMHelper.attachCopySnippet() + }, [props.metaData, configContext.val]) React.useEffect(() => { if (props.disabled) return @@ -69,14 +72,12 @@ const RawGitako: React.FC = function RawGitako(props) { }, [props.metaData, configContext.val.intelligentToggle]) React.useEffect(() => { - const { copyFileButton } = configContext.val - if (copyFileButton) return DOMHelper.attachCopyFileBtn() - }, [props.metaData]) + if (configContext.val.copyFileButton) return DOMHelper.attachCopyFileBtn() + }, [configContext.val.copyFileButton]) React.useEffect(() => { - const { copySnippetButton } = configContext.val - if (copySnippetButton) DOMHelper.attachCopySnippet() - }, [props.metaData]) + if (configContext.val.copySnippetButton) return DOMHelper.attachCopySnippet() + }, [configContext.val.copySnippetButton]) // init again when setting new accessToken useDidUpdate(() => props.init(), [accessToken]) diff --git a/src/utils/DOMHelper.ts b/src/utils/DOMHelper.ts index 3de1476..b736540 100644 --- a/src/utils/DOMHelper.ts +++ b/src/utils/DOMHelper.ts @@ -7,7 +7,6 @@ import { CopyFileButton } from 'components/CopyFileButton' import * as NProgress from 'nprogress' import * as PJAX from 'pjax' import * as React from 'react' -import * as ReactDOM from 'react-dom' import { renderReact } from './general' NProgress.configure({ showSpinner: false }) @@ -210,15 +209,13 @@ export function attachCopyFileBtn() { } const buttons: HTMLElement[] = [] - buttonGroups.forEach(buttonGroup => { + buttonGroups.forEach(async buttonGroup => { if (!buttonGroup.lastElementChild) return - const portal = ReactDOM.createPortal(React.createElement(CopyFileButton), buttonGroup) - // creating a element for mounting button into button group, somehow hack - const seedElementForButton = document.createElement('a') - buttonGroup.appendChild(seedElementForButton) - ReactDOM.render(portal, seedElementForButton) - buttonGroup.removeChild(seedElementForButton) - buttons.push(seedElementForButton) + const button = await renderReact(React.createElement(CopyFileButton)) + if (button instanceof HTMLElement) { + buttonGroup.appendChild(button) + buttons.push(button) + } }) return () => buttons.forEach(button => { @@ -249,7 +246,8 @@ export function attachCopySnippet() { const readmeArticleSelector = '.repository-content div#readme article' return $( readmeArticleSelector, - readmeElement => + readmeElement => { + const buttons: HTMLElement[] = [] readmeElement.addEventListener('mouseover', async ({ target }) => { if (target instanceof Element && target.nodeName === 'PRE') { if ( @@ -269,11 +267,19 @@ export function attachCopySnippet() { const clippyElement = await renderReact( React.createElement(Clippy, { codeSnippetElement: target }), ) - target.parentNode.insertBefore(clippyElement, target) + if (clippyElement instanceof HTMLElement) { + target.parentNode.insertBefore(clippyElement, target) + buttons.push(clippyElement) + } } } } - }), + }) + return () => + buttons.forEach(button => { + button.parentElement?.removeChild(button) + }) + }, () => { const plainReadmeSelector = '.repository-content div#readme .plain' $(plainReadmeSelector, undefined, () => From 26d0b189bc731ea21d55a3b611039eee87304acc Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Wed, 13 Nov 2019 16:23:23 +0800 Subject: [PATCH 32/98] fix: resolve return value for effects --- src/components/SideBar.tsx | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/components/SideBar.tsx b/src/components/SideBar.tsx index 8ff4539..08a0a21 100644 --- a/src/components/SideBar.tsx +++ b/src/components/SideBar.tsx @@ -72,15 +72,17 @@ const RawGitako: React.FC = function RawGitako(props) { }, [props.metaData, configContext.val.intelligentToggle]) React.useEffect(() => { - if (configContext.val.copyFileButton) return DOMHelper.attachCopyFileBtn() + if (configContext.val.copyFileButton) return DOMHelper.attachCopyFileBtn() || undefined // undefined is friendlier to React }, [configContext.val.copyFileButton]) React.useEffect(() => { - if (configContext.val.copySnippetButton) return DOMHelper.attachCopySnippet() + if (configContext.val.copySnippetButton) return DOMHelper.attachCopySnippet() || undefined // undefined is friendlier to React }, [configContext.val.copySnippetButton]) // init again when setting new accessToken - useDidUpdate(() => props.init(), [accessToken]) + useDidUpdate(() => { + props.init() + }, [accessToken]) const { errorDueToAuth, From d830741968d4b27b2439af5bfbf2dc1cb9e28be5 Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Fri, 15 Nov 2019 16:26:15 +0800 Subject: [PATCH 33/98] feat: ErrorBoundary --- src/components/ErrorBoundary.tsx | 12 ++++++++++++ src/components/Gitako.tsx | 16 ++++++---------- 2 files changed, 18 insertions(+), 10 deletions(-) create mode 100644 src/components/ErrorBoundary.tsx diff --git a/src/components/ErrorBoundary.tsx b/src/components/ErrorBoundary.tsx new file mode 100644 index 0000000..0f8c74e --- /dev/null +++ b/src/components/ErrorBoundary.tsx @@ -0,0 +1,12 @@ +import { raiseError } from 'analytics' +import * as React from 'react' + +export class ErrorBoundary extends React.PureComponent { + componentDidCatch(error: Error, errorInfo: React.ErrorInfo) { + raiseError(error, errorInfo) + } + + render() { + return this.props.children + } +} diff --git a/src/components/Gitako.tsx b/src/components/Gitako.tsx index 28ace02..7c68258 100644 --- a/src/components/Gitako.tsx +++ b/src/components/Gitako.tsx @@ -1,20 +1,16 @@ -import { raiseError } from 'analytics' import { SideBar } from 'components/SideBar' import { ConfigsContext, ConfigsContextWrapper } from 'containers/ConfigsContext' import * as React from 'react' +import { ErrorBoundary } from './ErrorBoundary' -export class Gitako extends React.PureComponent { - componentDidCatch(error: Error, errorInfo: React.ErrorInfo) { - raiseError(error, errorInfo) - } - - render() { - return ( +export function Gitako() { + return ( + {configContext => configContext && } - ) - } + + ) } From dd9cc2d0c0299daa6ddc53b6d05ba2c7bb2f37cf Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Fri, 15 Nov 2019 16:28:38 +0800 Subject: [PATCH 34/98] fix: prevent reset file tree on pjax --- src/components/FileExplorer.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/FileExplorer.tsx b/src/components/FileExplorer.tsx index 60e95ab..a2a9020 100644 --- a/src/components/FileExplorer.tsx +++ b/src/components/FileExplorer.tsx @@ -29,7 +29,7 @@ const RawFileExplorer: React.FC = function RawFileExplor React.useEffect(() => { const { setUpTree, treeData, metaData } = props setUpTree({ treeData, metaData, compressSingletonFolder, accessToken }) - }, [props.setUpTree, props.treeData, props.metaData, compressSingletonFolder, accessToken]) + }, [props.setUpTree, props.treeData, compressSingletonFolder, accessToken]) React.useEffect(() => { const { execAfterRender } = props From e1cd6c95b8c19271ed9d54f85f58629b8f0d0054 Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Fri, 15 Nov 2019 17:15:02 +0800 Subject: [PATCH 35/98] fix: time function for Firefox --- src/firefox-shim.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/firefox-shim.js b/src/firefox-shim.js index 8e949c4..205efab 100644 --- a/src/firefox-shim.js +++ b/src/firefox-shim.js @@ -1 +1,3 @@ window.requestAnimationFrame = window.requestAnimationFrame.bind(window) +window.setTimeout = window.setTimeout.bind(window) +window.clearTimeout = window.clearTimeout.bind(window) From e09935230d2111406308d48e70f0958fb6f16de0 Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Fri, 15 Nov 2019 17:33:31 +0800 Subject: [PATCH 36/98] feat: onPJAXComplete --- src/components/SideBar.tsx | 30 +++++++++++++++++++++--------- src/utils/DOMHelper.ts | 2 ++ 2 files changed, 23 insertions(+), 9 deletions(-) diff --git a/src/components/SideBar.tsx b/src/components/SideBar.tsx index 08a0a21..ecdbee6 100644 --- a/src/components/SideBar.tsx +++ b/src/components/SideBar.tsx @@ -51,19 +51,24 @@ const RawGitako: React.FC = function RawGitako(props) { return () => window.removeEventListener('keydown', onKeyDown) }, [props.disabled, onKeyDown]) - const onPJAXEnd = React.useCallback(() => { + const updateMeta = React.useCallback(() => { + if (props.disabled) return DOMHelper.unmountTopProgressBar() props.setMetaData({ ...props.metaData, ...URLHelper.parse() }) - // TODO: update state to re-trigger DOM effects - // if (configContext.val.copyFileButton) DOMHelper.attachCopyFileBtn() - // if (configContext.val.copySnippetButton) DOMHelper.attachCopySnippet() - }, [props.metaData, configContext.val]) + }, [props.disabled, props.metaData, configContext.val]) + useOnPJAXComplete(updateMeta) - React.useEffect(() => { + const attachCopyFileButton = React.useCallback(() => { if (props.disabled) return - window.addEventListener('pjax:complete', onPJAXEnd) - return () => window.removeEventListener('pjax:complete', onPJAXEnd) - }, [props.disabled, onPJAXEnd]) + if (configContext.val.copyFileButton) return DOMHelper.attachCopyFileBtn() + }, [props.disabled, configContext.val.copyFileButton]) + useOnPJAXComplete(attachCopyFileButton) + + const attachCopySnippetButton = React.useCallback(() => { + if (props.disabled) return + if (configContext.val.copySnippetButton) return DOMHelper.attachCopySnippet() + }, [props.disabled, configContext.val.copySnippetButton]) + useOnPJAXComplete(attachCopySnippetButton) React.useEffect(() => { if (configContext.val.intelligentToggle === null) { @@ -138,6 +143,13 @@ RawGitako.defaultProps = { export const SideBar = connect(SideBarCore)(RawGitako) +function useOnPJAXComplete(onPJAXComplete: () => void, deps: React.DependencyList = []) { + React.useEffect(() => { + window.addEventListener('pjax:complete', onPJAXComplete) + return () => window.removeEventListener('pjax:complete', onPJAXComplete) + }, [onPJAXComplete, ...deps]) +} + function renderAccessDeniedError() { return (
diff --git a/src/utils/DOMHelper.ts b/src/utils/DOMHelper.ts index b736540..8c5782e 100644 --- a/src/utils/DOMHelper.ts +++ b/src/utils/DOMHelper.ts @@ -217,6 +217,7 @@ export function attachCopyFileBtn() { buttons.push(button) } }) + // TODO: query from DOM again when detach return () => buttons.forEach(button => { button.parentElement?.removeChild(button) @@ -275,6 +276,7 @@ export function attachCopySnippet() { } } }) + // TODO: query from DOM again when detach return () => buttons.forEach(button => { button.parentElement?.removeChild(button) From 10f73cdb2619c20b9b9639b486efe6583a1b8b31 Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Fri, 15 Nov 2019 18:09:33 +0800 Subject: [PATCH 37/98] fix: search case-insensitive --- src/utils/general.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/utils/general.ts b/src/utils/general.ts index 9fc3281..a642357 100644 --- a/src/utils/general.ts +++ b/src/utils/general.ts @@ -134,8 +134,9 @@ export function searchKeyToRegexps(searchKey: string) { if (!searchKey) return [] try { + const flags = /[A-Z]/.test(searchKey) ? '' : 'i' // case-sensitive when searchKey contains uppercase char - return [new RegExp(searchKey, /[A-Z]/i.test(searchKey) ? '' : 'i')] + return [new RegExp(searchKey, flags)] } catch (err) { return [/$^/] // matching nothing if failed transforming regexp } From c4718c1ff66fed69eaf94907eb2bccc3a8ca6d3d Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Fri, 15 Nov 2019 18:10:42 +0800 Subject: [PATCH 38/98] 0.6.2 --- package.json | 2 +- src/manifest.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index b034a84..f095f8a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gitako", - "version": "0.6.1", + "version": "0.6.2", "description": "The missing part of GitHub.", "repository": "https://github.com/EnixCoda/Gitako", "author": "EnixCoda", diff --git a/src/manifest.json b/src/manifest.json index 78d41c0..23ba281 100644 --- a/src/manifest.json +++ b/src/manifest.json @@ -1,7 +1,7 @@ { "manifest_version": 2, "name": "Gitako - Github file tree", - "version": "0.6.1", + "version": "0.6.2", "description": "The missing part of GitHub.", "author": "EnixCoda", "icons": { From a642a701d03bc9df7c5a9d0afd043b4da2d8e028 Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Sat, 16 Nov 2019 14:48:00 +0800 Subject: [PATCH 39/98] refactor: useEvent --- src/components/SideBar.tsx | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/components/SideBar.tsx b/src/components/SideBar.tsx index ecdbee6..82bdbdc 100644 --- a/src/components/SideBar.tsx +++ b/src/components/SideBar.tsx @@ -143,13 +143,19 @@ RawGitako.defaultProps = { export const SideBar = connect(SideBarCore)(RawGitako) -function useOnPJAXComplete(onPJAXComplete: () => void, deps: React.DependencyList = []) { +function useEvent< + T extends { + addEventListener: Function + } +>(target: T, event: string, callback: () => void, deps: React.DependencyList = []) { React.useEffect(() => { - window.addEventListener('pjax:complete', onPJAXComplete) - return () => window.removeEventListener('pjax:complete', onPJAXComplete) - }, [onPJAXComplete, ...deps]) + window.addEventListener('pjax:complete', callback) + return () => window.removeEventListener('pjax:complete', callback) + }, [callback, ...deps]) } +const useOnPJAXComplete = useEvent.bind(null, window, 'pjax:complete') + function renderAccessDeniedError() { return (
From d84f7f07fcd37eedfa64d0e2387250218089392f Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Sat, 16 Nov 2019 14:48:22 +0800 Subject: [PATCH 40/98] fix: detach button --- src/components/Clippy.tsx | 5 ++++- src/components/CopyFileButton.tsx | 6 +++++- src/utils/DOMHelper.ts | 28 ++++++++++++++-------------- 3 files changed, 23 insertions(+), 16 deletions(-) diff --git a/src/components/Clippy.tsx b/src/components/Clippy.tsx index bd397f1..86ba87f 100644 --- a/src/components/Clippy.tsx +++ b/src/components/Clippy.tsx @@ -6,6 +6,9 @@ type Props = { codeSnippetElement: Element } +const className = 'clippy-wrapper' +export const ClippyClassName = className + export function Clippy({ codeSnippetElement }: Props) { const [status, setStatus] = React.useState<'normal' | 'success' | 'fail'>('normal') React.useEffect(() => { @@ -24,7 +27,7 @@ export function Clippy({ codeSnippetElement }: Props) { }, []) return ( -
+
diff --git a/src/components/CopyFileButton.tsx b/src/components/CopyFileButton.tsx index 11bcda4..dba3066 100644 --- a/src/components/CopyFileButton.tsx +++ b/src/components/CopyFileButton.tsx @@ -1,8 +1,12 @@ import * as React from 'react' +import { cx } from 'utils/cx' import { copyElementContent, getCodeElement } from 'utils/DOMHelper' type Props = {} +const className = 'gitako-copy-file-button' +export const copyFileButtonClassName = className + export function CopyFileButton(props: React.PropsWithChildren) { const contents = { success: 'Success!', @@ -20,7 +24,7 @@ export function CopyFileButton(props: React.PropsWithChildren) { }, [content]) return ( { const codeElement = getCodeElement() if (codeElement) { diff --git a/src/utils/DOMHelper.ts b/src/utils/DOMHelper.ts index 8c5782e..613f5c4 100644 --- a/src/utils/DOMHelper.ts +++ b/src/utils/DOMHelper.ts @@ -2,8 +2,8 @@ * this helper helps manipulating DOM */ import { raiseError } from 'analytics' -import { Clippy } from 'components/Clippy' -import { CopyFileButton } from 'components/CopyFileButton' +import { Clippy, ClippyClassName } from 'components/Clippy' +import { CopyFileButton, copyFileButtonClassName } from 'components/CopyFileButton' import * as NProgress from 'nprogress' import * as PJAX from 'pjax' import * as React from 'react' @@ -130,7 +130,7 @@ const pjax = new PJAX({ }) export function loadWithPJAX(URL: string) { - NProgress.start() + mountTopProgressBar() pjax.loadUrl(URL, { scrollTo: 0 }) } @@ -208,20 +208,19 @@ export function attachCopyFileBtn() { raiseError(new Error(`No button groups found`)) } - const buttons: HTMLElement[] = [] buttonGroups.forEach(async buttonGroup => { if (!buttonGroup.lastElementChild) return const button = await renderReact(React.createElement(CopyFileButton)) if (button instanceof HTMLElement) { buttonGroup.appendChild(button) - buttons.push(button) } }) - // TODO: query from DOM again when detach - return () => + return () => { + const buttons = document.querySelectorAll(`.${copyFileButtonClassName}`) buttons.forEach(button => { button.parentElement?.removeChild(button) }) + } } } @@ -248,13 +247,12 @@ export function attachCopySnippet() { return $( readmeArticleSelector, readmeElement => { - const buttons: HTMLElement[] = [] - readmeElement.addEventListener('mouseover', async ({ target }) => { + const mouseOverCallback = async ({ target }: Event): Promise => { if (target instanceof Element && target.nodeName === 'PRE') { if ( target.previousSibling === null || !(target.previousSibling instanceof Element) || - !target.previousSibling.classList.contains('clippy-wrapper') + !target.previousSibling.classList.contains(ClippyClassName) ) { /** *
@@ -270,17 +268,19 @@ export function attachCopySnippet() { ) if (clippyElement instanceof HTMLElement) { target.parentNode.insertBefore(clippyElement, target) - buttons.push(clippyElement) } } } } - }) - // TODO: query from DOM again when detach - return () => + } + readmeElement.addEventListener('mouseover', mouseOverCallback) + return () => { + readmeElement.removeEventListener('mouseover', mouseOverCallback) + const buttons = document.querySelectorAll(`.${ClippyClassName}`) buttons.forEach(button => { button.parentElement?.removeChild(button) }) + } }, () => { const plainReadmeSelector = '.repository-content div#readme .plain' From e2dfe89901c2feb8174d2e73367668de47c71853 Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Fri, 15 Nov 2019 18:09:33 +0800 Subject: [PATCH 41/98] fix: search case-insensitive --- src/utils/general.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/utils/general.ts b/src/utils/general.ts index cde3a0d..0b4e218 100644 --- a/src/utils/general.ts +++ b/src/utils/general.ts @@ -135,8 +135,9 @@ export function searchKeyToRegexps(searchKey: string) { if (!searchKey) return [] try { + const flags = /[A-Z]/.test(searchKey) ? '' : 'i' // case-sensitive when searchKey contains uppercase char - return [new RegExp(searchKey, /[A-Z]/i.test(searchKey) ? '' : 'i')] + return [new RegExp(searchKey, flags)] } catch (err) { return [/$^/] // matching nothing if failed transforming regexp } From e0de62e968204d49522b5b52af2e202b1373a9e1 Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Sat, 16 Nov 2019 15:11:04 +0800 Subject: [PATCH 42/98] 0.7.0 --- package.json | 2 +- src/manifest.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index d5a4d62..bd1ada6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gitako", - "version": "0.6.2", + "version": "0.7.0", "description": "The missing part of GitHub.", "repository": "https://github.com/EnixCoda/Gitako", "author": "EnixCoda", diff --git a/src/manifest.json b/src/manifest.json index 23ba281..44df8ed 100644 --- a/src/manifest.json +++ b/src/manifest.json @@ -1,7 +1,7 @@ { "manifest_version": 2, "name": "Gitako - Github file tree", - "version": "0.6.2", + "version": "0.7.0", "description": "The missing part of GitHub.", "author": "EnixCoda", "icons": { From a5ddde1f7fd797bb4535fa9310f6c84b43b5fa39 Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Sun, 17 Nov 2019 15:50:20 +0800 Subject: [PATCH 43/98] feat: handle search result page type --- src/utils/DOMHelper.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/utils/DOMHelper.ts b/src/utils/DOMHelper.ts index 613f5c4..b37c3db 100644 --- a/src/utils/DOMHelper.ts +++ b/src/utils/DOMHelper.ts @@ -143,6 +143,7 @@ export function loadWithPJAX(URL: string) { const PAGE_TYPES = { RAW_TEXT: 'raw_text', RENDERED: 'rendered', + SEARCH: 'search', // PREVIEW: 'preview', OTHERS: 'others', } @@ -158,7 +159,9 @@ const PAGE_TYPES = { export function getCurrentPageType() { const blobWrapperSelector = '.repository-content .blob-wrapper table' const readmeSelector = '.repository-content .readme' + const searchResultSelector = '.codesearch-results' return ( + $(searchResultSelector, () => PAGE_TYPES.SEARCH) || $(blobWrapperSelector, () => PAGE_TYPES.RAW_TEXT) || $(readmeSelector, () => PAGE_TYPES.RENDERED) || PAGE_TYPES.OTHERS From 48d15221e96fc24c65921bb9e74e2d241debc5c0 Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Sun, 17 Nov 2019 15:55:41 +0800 Subject: [PATCH 44/98] build: get version in makefile --- Makefile | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/Makefile b/Makefile index a9c4084..c054032 100755 --- a/Makefile +++ b/Makefile @@ -1,15 +1,17 @@ +RAW_VERSION?=$(shell node scripts/get-version.js) +FULL_VERSION=v$(RAW_VERSION) + build: rm -rf dist yarn build upload-for-analytics: - VERSION=v$(node scripts/get-version.js) # make sure sentry can retrieve current commit on remote git push --tags - yarn sentry-cli releases new "$(VERSION)" - yarn sentry-cli releases set-commits "$(VERSION)" --auto - yarn sentry-cli releases files "$(VERSION)" upload-sourcemaps dist --no-rewrite - yarn sentry-cli releases finalize "$(VERSION)" + yarn sentry-cli releases new "$(FULL_VERSION)" + yarn sentry-cli releases set-commits "$(FULL_VERSION)" --auto + yarn sentry-cli releases files "$(FULL_VERSION)" upload-sourcemaps dist --no-rewrite + yarn sentry-cli releases finalize "$(FULL_VERSION)" compress: rm -f dist/gitako.zip From 583c99baeef433dbce875656e144f564924e674b Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Sun, 17 Nov 2019 16:04:27 +0800 Subject: [PATCH 45/98] 0.7.1 --- package.json | 2 +- src/manifest.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index bd1ada6..8ef13b1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gitako", - "version": "0.7.0", + "version": "0.7.1", "description": "The missing part of GitHub.", "repository": "https://github.com/EnixCoda/Gitako", "author": "EnixCoda", diff --git a/src/manifest.json b/src/manifest.json index 44df8ed..4036f3b 100644 --- a/src/manifest.json +++ b/src/manifest.json @@ -1,7 +1,7 @@ { "manifest_version": 2, "name": "Gitako - Github file tree", - "version": "0.7.0", + "version": "0.7.1", "description": "The missing part of GitHub.", "author": "EnixCoda", "icons": { From 8e5761bffba68ccf82d63ac3f8af552e5d942cb3 Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Mon, 18 Nov 2019 18:01:26 +0800 Subject: [PATCH 46/98] fix: attach useEvent target and event --- src/components/SideBar.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/components/SideBar.tsx b/src/components/SideBar.tsx index 82bdbdc..8fc853b 100644 --- a/src/components/SideBar.tsx +++ b/src/components/SideBar.tsx @@ -146,11 +146,12 @@ export const SideBar = connect(SideBarCore)(RawGitako) function useEvent< T extends { addEventListener: Function + removeEventListener: Function } >(target: T, event: string, callback: () => void, deps: React.DependencyList = []) { React.useEffect(() => { - window.addEventListener('pjax:complete', callback) - return () => window.removeEventListener('pjax:complete', callback) + target.addEventListener(event, callback) + return () => target.removeEventListener(event, callback) }, [callback, ...deps]) } From 4076e4cb493b1b05fc17ad6aa2d7b626a447694e Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Mon, 18 Nov 2019 18:02:10 +0800 Subject: [PATCH 47/98] build: add react-use --- package.json | 1 + yarn.lock | 187 ++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 187 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 8ef13b1..33477bd 100644 --- a/package.json +++ b/package.json @@ -31,6 +31,7 @@ "nprogress": "^0.2.0", "react": "^16.8.6", "react-dom": "^16.8.6", + "react-use": "^13.8.0", "react-window": "^1.8.5", "safe-touch": "^0.2.2", "webextension-polyfill": "^0.5.0" diff --git a/yarn.lock b/yarn.lock index 6f77807..6e4f4f7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -769,6 +769,13 @@ dependencies: regenerator-runtime "^0.13.2" +"@babel/runtime@^7.1.2": + version "7.7.2" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.7.2.tgz#111a78002a5c25fc8e3361bedc9529c696b85a6a" + integrity sha512-JONRbXbTXc9WQE2mAZd1p0Z3DZ/6vaQIkgYMSTP3KjRCyd7rCZCcfhCyX+YjwcKxcZ82UrxbRD358bpExNgrjw== + dependencies: + regenerator-runtime "^0.13.2" + "@babel/template@^7.1.0", "@babel/template@^7.4.4", "@babel/template@^7.6.0": version "7.6.0" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.6.0.tgz#7f0159c7f5012230dad64cca42ec9bdb5c9536e6" @@ -1732,6 +1739,11 @@ boolbase@~1.0.0: resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" integrity sha1-aN/1++YMUes3cl6p4+0xDcwed24= +bowser@^1.7.3: + version "1.9.4" + resolved "https://registry.yarnpkg.com/bowser/-/bowser-1.9.4.tgz#890c58a2813a9d3243704334fa81b96a5c150c9a" + integrity sha512-9IdMmj2KjigRq6oWhmwv1W36pDuA4STQZ8q6YO9um+x07xgYNCD3Oou+WP/3L1HNz7iqythGet3/p4wvc8AAwQ== + boxen@^3.0.0: version "3.2.0" resolved "https://registry.yarnpkg.com/boxen/-/boxen-3.2.0.tgz#fbdff0de93636ab4450886b6ff45b92d098f45eb" @@ -2372,6 +2384,13 @@ copy-descriptor@^0.1.0: resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40= +copy-to-clipboard@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/copy-to-clipboard/-/copy-to-clipboard-3.2.0.tgz#d2724a3ccbfed89706fac8a894872c979ac74467" + integrity sha512-eOZERzvCmxS8HWzugj4Uxl8OJxa7T2k1Gi0X5qavwydHIfuSHq2dTD09LOg/XyGq4Zpb5IsR/2OJ5lbOegz78w== + dependencies: + toggle-selection "^1.0.6" + copy-webpack-plugin@^5.0.0: version "5.0.4" resolved "https://registry.yarnpkg.com/copy-webpack-plugin/-/copy-webpack-plugin-5.0.4.tgz#c78126f604e24f194c6ec2f43a64e232b5d43655" @@ -2496,6 +2515,14 @@ crypto-random-string@^1.0.0: resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-1.0.0.tgz#a230f64f568310e1498009940790ec99545bca7e" integrity sha1-ojD2T1aDEOFJgAmUB5DsmVRbyn4= +css-in-js-utils@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/css-in-js-utils/-/css-in-js-utils-2.0.1.tgz#3b472b398787291b47cfe3e44fecfdd9e914ba99" + integrity sha512-PJF0SpJT+WdbVVt0AOYp9C8GnuruRlL/UFW7932nLWmFLQTaWEzTBQEx7/hn4BuV+WON75iAViSUJLiU3PKbpA== + dependencies: + hyphenate-style-name "^1.0.2" + isobject "^3.0.1" + css-loader@^2.1.0: version "2.1.1" resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-2.1.1.tgz#d8254f72e412bb2238bb44dd674ffbef497333ea" @@ -2523,6 +2550,14 @@ css-select@~1.2.0: domutils "1.5.1" nth-check "~1.0.1" +css-tree@^1.0.0-alpha.28: + version "1.0.0-alpha.37" + resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.0.0-alpha.37.tgz#98bebd62c4c1d9f960ec340cf9f7522e30709a22" + integrity sha512-DMxWJg0rnz7UgxKT0Q1HU/L9BeJI0M6ksor0OgqOnF+aRCDWg/N2641HmVyU9KVIu0OVVWOb2IpC9A+BJRnejg== + dependencies: + mdn-data "2.0.4" + source-map "^0.6.1" + css-what@2.1: version "2.1.3" resolved "https://registry.yarnpkg.com/css-what/-/css-what-2.1.3.tgz#a6d7604573365fe74686c3f311c56513d88285f2" @@ -2538,6 +2573,11 @@ csstype@^2.2.0: resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.6.6.tgz#c34f8226a94bbb10c32cc0d714afdf942291fc41" integrity sha512-RpFbQGUE74iyPgvr46U9t1xoQBM8T4BL8SxrN66Le2xYAPSaDJJKeztV3awugusb3g3G9iL8StmkBBXhcbbXhg== +csstype@^2.5.5: + version "2.6.7" + resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.6.7.tgz#20b0024c20b6718f4eda3853a1f5a1cce7f5e4a5" + integrity sha512-9Mcn9sFbGBAdmimWb2gLVDtFJzeKtDGIr76TUqmjZrw9LFXBMSU70lcs+C0/7fyCd6iBDqmksUcCOUIkisPHsQ== + cyclist@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-1.0.1.tgz#596e9698fd0c80e12038c2b82d6eb1b35b6224d9" @@ -2991,6 +3031,13 @@ error-ex@^1.3.1: dependencies: is-arrayish "^0.2.1" +error-stack-parser@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/error-stack-parser/-/error-stack-parser-2.0.4.tgz#a757397dc5d9de973ac9a5d7d4e8ade7cfae9101" + integrity sha512-fZ0KkoxSjLFmhW5lHbUT3tLwy3nX1qEzMYo8koY1vrsAco53CMT1djnBSeC/wUjTEZRhZl9iRw7PaMaxfJ4wzQ== + dependencies: + stackframe "^1.1.0" + es-abstract@^1.5.1: version "1.16.0" resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.16.0.tgz#d3a26dc9c3283ac9750dca569586e976d9dcc06d" @@ -3520,6 +3567,11 @@ fast-safe-stringify@^2.0.7: resolved "https://registry.yarnpkg.com/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz#124aa885899261f68aedb42a7c080de9da608743" integrity sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA== +fastest-stable-stringify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/fastest-stable-stringify/-/fastest-stable-stringify-1.0.1.tgz#9122d406d4c9d98bea644a6b6853d5874b87b028" + integrity sha1-kSLUBtTJ2YvqZEpraFPVh0uHsCg= + fd-slicer@~1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.1.0.tgz#25c7c89cb1f9077f8891bbe61d8f390eae256f1e" @@ -4252,6 +4304,11 @@ https-proxy-agent@^2.2.1: agent-base "^4.3.0" debug "^3.1.0" +hyphenate-style-name@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/hyphenate-style-name/-/hyphenate-style-name-1.0.3.tgz#097bb7fa0b8f1a9cf0bd5c734cf95899981a9b48" + integrity sha512-EcuixamT82oplpoJ2XU4pDtKGWQ7b00CD9f1ug9IaQ3p1bkHMiKCZ9ut9QDI6qsa6cpUuB+A/I+zLtdNK4n2DQ== + iconv-lite@0.4.24, iconv-lite@^0.4.24, iconv-lite@^0.4.4, iconv-lite@~0.4.13: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" @@ -4367,6 +4424,14 @@ ini@^1.3.4, ini@^1.3.5, ini@~1.3.0, ini@~1.3.3: resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927" integrity sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw== +inline-style-prefixer@^4.0.0: + version "4.0.2" + resolved "https://registry.yarnpkg.com/inline-style-prefixer/-/inline-style-prefixer-4.0.2.tgz#d390957d26f281255fe101da863158ac6eb60911" + integrity sha512-N8nVhwfYga9MiV9jWlwfdj1UDIaZlBFu4cJSJkIr7tZX7sHpHhGR5su1qdpW+7KPL8ISTvCIkcaFi/JdBknvPg== + dependencies: + bowser "^1.7.3" + css-in-js-utils "^2.0.0" + inquirer@^0.12.0: version "0.12.0" resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-0.12.0.tgz#1ef2bfd63504df0bc75785fff8c2c41df12f077e" @@ -5193,6 +5258,11 @@ mdn-browser-compat-data@0.0.94: dependencies: extend "3.0.2" +mdn-data@2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.4.tgz#699b3c38ac6f1d728091a64650b65d388502fd5b" + integrity sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA== + media-typer@0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" @@ -5461,6 +5531,20 @@ nan@^2.12.1, nan@^2.14.0: resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.0.tgz#7818f722027b2459a86f0295d434d1fc2336c52c" integrity sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg== +nano-css@^5.2.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/nano-css/-/nano-css-5.2.1.tgz#73b8470fa40b028a134d3393ae36bbb34b9fa332" + integrity sha512-T54okxMAha0+de+W8o3qFtuWhTxYvqQh2ku1cYEqTTP9mR62nWV2lLK9qRuAGWmoaYWhU7K4evT9Lc1iF65wuw== + dependencies: + css-tree "^1.0.0-alpha.28" + csstype "^2.5.5" + fastest-stable-stringify "^1.0.1" + inline-style-prefixer "^4.0.0" + rtl-css-js "^1.9.0" + sourcemap-codec "^1.4.1" + stacktrace-js "^2.0.0" + stylis "3.5.0" + nanomatch@^1.2.9: version "1.2.13" resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119" @@ -6386,11 +6470,31 @@ react-dom@^16.8.6: prop-types "^15.6.2" scheduler "^0.15.0" +react-fast-compare@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-2.0.4.tgz#e84b4d455b0fec113e0402c329352715196f81f9" + integrity sha512-suNP+J1VU1MWFKcyt7RtjiSWUjvidmQSlqu+eHslq+342xCbGTYmC0mEhPCOHxlW0CywylOC1u2DFAT+bv4dBw== + react-is@^16.8.1: version "16.9.0" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.9.0.tgz#21ca9561399aad0ff1a7701c01683e8ca981edcb" integrity sha512-tJBzzzIgnnRfEm046qRcURvwQnZVXmuCbscxUO5RWrGTXpon2d4c8mI0D8WE6ydVIm29JiLB6+RslkIvym9Rjw== +react-use@^13.8.0: + version "13.8.0" + resolved "https://registry.yarnpkg.com/react-use/-/react-use-13.8.0.tgz#5e8badaaa5614a1925fd28ad22d01cc1c52e0ff1" + integrity sha512-J4ZWIqC1h2eSS6ObmHJUWDQeJSKZvHdBEINCu51plz7RS+ijrt6JEHfDWbNXx+aAoR5AmbRK+mzYwd3jiu3xpA== + dependencies: + copy-to-clipboard "^3.2.0" + nano-css "^5.2.1" + react-fast-compare "^2.0.4" + resize-observer-polyfill "^1.5.1" + screenfull "^5.0.0" + set-harmonic-interval "^1.0.1" + throttle-debounce "^2.1.0" + ts-easing "^0.2.0" + tslib "^1.10.0" + react-window@^1.8.5: version "1.8.5" resolved "https://registry.yarnpkg.com/react-window/-/react-window-1.8.5.tgz#a56b39307e79979721021f5d06a67742ecca52d1" @@ -6646,6 +6750,11 @@ require-uncached@^1.0.2: caller-path "^0.1.0" resolve-from "^1.0.0" +resize-observer-polyfill@^1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz#0e9020dd3d21024458d4ebd27e23e40269810464" + integrity sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg== + resolve-cwd@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-2.0.0.tgz#00a9f7387556e27038eae232caa372a6a59b665a" @@ -6745,6 +6854,13 @@ ripemd160@^2.0.0, ripemd160@^2.0.1: hash-base "^3.0.0" inherits "^2.0.1" +rtl-css-js@^1.9.0: + version "1.13.1" + resolved "https://registry.yarnpkg.com/rtl-css-js/-/rtl-css-js-1.13.1.tgz#80deabf6e8f36d6767d495cd3eb60fecb20c67e1" + integrity sha512-jgkIDj6Xi25kAEm5oYM3ZMFiOQhpLEcXi2LY/6bVr91cVz73hciHKneL5AMVPxOcks/JuizSaaNsvNRkeAWe3w== + dependencies: + "@babel/runtime" "^7.1.2" + run-async@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/run-async/-/run-async-0.1.0.tgz#c8ad4a5e110661e402a7d21b530e009f25f8e389" @@ -6832,6 +6948,11 @@ schema-utils@^1.0.0: ajv-errors "^1.0.0" ajv-keywords "^3.1.0" +screenfull@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/screenfull/-/screenfull-5.0.0.tgz#5c2010c0e84fd4157bf852877698f90b8cbe96f6" + integrity sha512-yShzhaIoE9OtOhWVyBBffA6V98CDCoyHTsp8228blmqYy1Z5bddzE/4FPiJKlr8DVR4VBiiUyfPzIQPIYDkeMA== + semver-diff@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/semver-diff/-/semver-diff-2.1.0.tgz#4bbb8437c8d37e4b0cf1a68fd726ec6d645d6d36" @@ -6888,6 +7009,11 @@ set-blocking@^2.0.0, set-blocking@~2.0.0: resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= +set-harmonic-interval@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/set-harmonic-interval/-/set-harmonic-interval-1.0.1.tgz#e1773705539cdfb80ce1c3d99e7f298bb3995249" + integrity sha512-AhICkFV84tBP1aWqPwLZqFvAwqEoVA9kxNMniGEUvzOlm4vLmOFLiTT3UZ6bziJTy4bOVpzWGTfSCbmaayGx8g== + set-value@^2.0.0, set-value@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.1.tgz#a18d40530e6f07de4228c7defe4227af8cad005b" @@ -7087,6 +7213,11 @@ source-map-url@^0.4.0: resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3" integrity sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM= +source-map@0.5.6: + version "0.5.6" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.6.tgz#75ce38f52bf0733c5a7f0c118d81334a2bb5f412" + integrity sha1-dc449SvwczxafwwRjYEzSiu19BI= + source-map@^0.5.0, source-map@^0.5.3, source-map@^0.5.6: version "0.5.7" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" @@ -7097,6 +7228,11 @@ source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.0, source-map@~0.6.1: resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== +sourcemap-codec@^1.4.1: + version "1.4.6" + resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.6.tgz#e30a74f0402bad09807640d39e971090a08ce1e9" + integrity sha512-1ZooVLYFxC448piVLBbtOxFcXwnymH9oUF8nRd3CuYDVvkRBxRl6pB4Mtas5a4drtL+E8LDgFkQNcgIw6tc8Hg== + spawn-sync@1.0.15: version "1.0.15" resolved "https://registry.yarnpkg.com/spawn-sync/-/spawn-sync-1.0.15.tgz#b00799557eb7fb0c8376c29d44e8a1ea67e57476" @@ -7146,6 +7282,35 @@ ssri@^6.0.1: dependencies: figgy-pudding "^3.5.1" +stack-generator@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/stack-generator/-/stack-generator-2.0.4.tgz#027513eab2b195bbb43b9c8360ba2dd0ab54de09" + integrity sha512-ha1gosTNcgxwzo9uKTQ8zZ49aUp5FIUW58YHFxCqaAHtE0XqBg0chGFYA1MfmW//x1KWq3F4G7Ug7bJh4RiRtg== + dependencies: + stackframe "^1.1.0" + +stackframe@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/stackframe/-/stackframe-1.1.0.tgz#e3fc2eb912259479c9822f7d1f1ff365bd5cbc83" + integrity sha512-Vx6W1Yvy+AM1R/ckVwcHQHV147pTPBKWCRLrXMuPrFVfvBUc3os7PR1QLIWCMhPpRg5eX9ojzbQIMLGBwyLjqg== + +stacktrace-gps@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/stacktrace-gps/-/stacktrace-gps-3.0.3.tgz#b89f84cc13bb925b96607e737b617c8715facf57" + integrity sha512-51Rr7dXkyFUKNmhY/vqZWK+EvdsfFSRiQVtgHTFlAdNIYaDD7bVh21yBHXaNWAvTD+w+QSjxHg7/v6Tz4veExA== + dependencies: + source-map "0.5.6" + stackframe "^1.1.0" + +stacktrace-js@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/stacktrace-js/-/stacktrace-js-2.0.1.tgz#ebdb0e9a16e6f171f96ca7878404e7f15c3d42ba" + integrity sha512-13oDNgBSeWtdGa4/2BycNyKqe+VktCoJ8VLx4pDoJkwGGJVtiHdfMOAj3aW9xTi8oR2v34z9IcvfCvT6XNdNAw== + dependencies: + error-stack-parser "^2.0.4" + stack-generator "^2.0.4" + stacktrace-gps "^3.0.3" + static-extend@^0.1.1: version "0.1.2" resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6" @@ -7344,6 +7509,11 @@ style-loader@^0.23.1: loader-utils "^1.1.0" schema-utils "^1.0.0" +stylis@3.5.0: + version "3.5.0" + resolved "https://registry.yarnpkg.com/stylis/-/stylis-3.5.0.tgz#016fa239663d77f868fef5b67cf201c4b7c701e1" + integrity sha512-pP7yXN6dwMzAR29Q0mBrabPCe0/mNO1MSr93bhay+hcZondvMMTpeGyd8nbhYJdyperNT2DRxONQuUGcJr5iPw== + supports-color@6.1.0, supports-color@^6.1.0: version "6.1.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-6.1.0.tgz#0764abc69c63d5ac842dd4867e8d025e880df8f3" @@ -7466,6 +7636,11 @@ thenify-all@^1.0.0: dependencies: any-promise "^1.0.0" +throttle-debounce@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/throttle-debounce/-/throttle-debounce-2.1.0.tgz#257e648f0a56bd9e54fe0f132c4ab8611df4e1d5" + integrity sha512-AOvyNahXQuU7NN+VVvOOX+uW6FPaWdAOdRP5HfwYxAfCzXTFKRMoIMk+n+po318+ktcChx+F1Dd91G3YHeMKyg== + through2@^2.0.0: version "2.0.5" resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd" @@ -7545,6 +7720,11 @@ to-regex@^3.0.1, to-regex@^3.0.2: regex-not "^1.0.2" safe-regex "^1.1.0" +toggle-selection@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/toggle-selection/-/toggle-selection-1.0.6.tgz#6e45b1263f2017fa0acc7d89d78b15b8bf77da32" + integrity sha1-bkWxJj8gF/oKzH2J14sVuL932jI= + toidentifier@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553" @@ -7592,7 +7772,12 @@ tryer@^1.0.1: resolved "https://registry.yarnpkg.com/tryer/-/tryer-1.0.1.tgz#f2c85406800b9b0f74c9f7465b81eaad241252f8" integrity sha512-c3zayb8/kWWpycWYg87P71E1S1ZL6b6IJxfb5fvsUgsf0S2MVGaDhDXXjDMpdCpfWXqptc+4mXwmiy1ypXqRAA== -tslib@^1.9.0, tslib@^1.9.3: +ts-easing@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/ts-easing/-/ts-easing-0.2.0.tgz#c8a8a35025105566588d87dbda05dd7fbfa5a4ec" + integrity sha512-Z86EW+fFFh/IFB1fqQ3/+7Zpf9t2ebOAxNI/V6Wo7r5gqiqtxmgTlQ1qbqQcjLKYeSHPTsEmvlJUDg/EuL0uHQ== + +tslib@^1.10.0, tslib@^1.9.0, tslib@^1.9.3: version "1.10.0" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.10.0.tgz#c3c19f95973fb0a62973fb09d90d961ee43e5c8a" integrity sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ== From aca84b3b2b2d0aa4f527ab2f114a6f3e15ae40fb Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Mon, 18 Nov 2019 18:11:47 +0800 Subject: [PATCH 48/98] refactor: remove pjax listener --- src/components/SideBar.tsx | 28 +++++++++++----------------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/src/components/SideBar.tsx b/src/components/SideBar.tsx index 8fc853b..83c01b9 100644 --- a/src/components/SideBar.tsx +++ b/src/components/SideBar.tsx @@ -11,6 +11,7 @@ import { SideBarCore } from 'driver/core' import { ConnectorState, Props } from 'driver/core/SideBar' import { oauth } from 'env' import * as React from 'react' +import { useLocation } from 'react-use' import { cx } from 'utils/cx' import * as DOMHelper from 'utils/DOMHelper' import { JSONRequest, parseURLSearch } from 'utils/general' @@ -56,19 +57,19 @@ const RawGitako: React.FC = function RawGitako(props) { DOMHelper.unmountTopProgressBar() props.setMetaData({ ...props.metaData, ...URLHelper.parse() }) }, [props.disabled, props.metaData, configContext.val]) - useOnPJAXComplete(updateMeta) + useOnLocationChange(updateMeta) const attachCopyFileButton = React.useCallback(() => { if (props.disabled) return if (configContext.val.copyFileButton) return DOMHelper.attachCopyFileBtn() }, [props.disabled, configContext.val.copyFileButton]) - useOnPJAXComplete(attachCopyFileButton) + useOnLocationChange(attachCopyFileButton) const attachCopySnippetButton = React.useCallback(() => { if (props.disabled) return if (configContext.val.copySnippetButton) return DOMHelper.attachCopySnippet() }, [props.disabled, configContext.val.copySnippetButton]) - useOnPJAXComplete(attachCopySnippetButton) + useOnLocationChange(attachCopySnippetButton) React.useEffect(() => { if (configContext.val.intelligentToggle === null) { @@ -143,20 +144,6 @@ RawGitako.defaultProps = { export const SideBar = connect(SideBarCore)(RawGitako) -function useEvent< - T extends { - addEventListener: Function - removeEventListener: Function - } ->(target: T, event: string, callback: () => void, deps: React.DependencyList = []) { - React.useEffect(() => { - target.addEventListener(event, callback) - return () => target.removeEventListener(event, callback) - }, [callback, ...deps]) -} - -const useOnPJAXComplete = useEvent.bind(null, window, 'pjax:complete') - function renderAccessDeniedError() { return (
@@ -197,3 +184,10 @@ async function trySetUpAccessTokenWithCode() { raiseError(err) } } + +function useOnLocationChange(callback: () => void) { + const { href, pathname, search } = useLocation() + useDidUpdate(() => { + callback() + }, [href, pathname, search]) +} From cb2bfaee59b9e388e02360f89df884295bf3dfb6 Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Mon, 18 Nov 2019 18:14:07 +0800 Subject: [PATCH 49/98] chore: remove "pjax-link" --- src/components/MetaBar.tsx | 2 +- src/utils/DOMHelper.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/MetaBar.tsx b/src/components/MetaBar.tsx index 2808240..4d9e655 100644 --- a/src/components/MetaBar.tsx +++ b/src/components/MetaBar.tsx @@ -15,7 +15,7 @@ export function MetaBar({ metaData }: Props) { {metaData.userName}  /  - + {metaData.repoName}  /  diff --git a/src/utils/DOMHelper.ts b/src/utils/DOMHelper.ts index b37c3db..fbc2cf6 100644 --- a/src/utils/DOMHelper.ts +++ b/src/utils/DOMHelper.ts @@ -121,7 +121,7 @@ export function scrollToRepoContent() { } const pjax = new PJAX({ - elements: '.pjax-link', + elements: '', selectors: ['.repository-content', 'title'], scrollTo: false, analytics: false, From 8b086ce1325a56a17b61a0722af8102ac12685c6 Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Sun, 17 Nov 2019 16:34:23 +0800 Subject: [PATCH 50/98] fix: handle server fault --- src/utils/GitHubHelper.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/utils/GitHubHelper.ts b/src/utils/GitHubHelper.ts index 140e8b8..22b4fa9 100644 --- a/src/utils/GitHubHelper.ts +++ b/src/utils/GitHubHelper.ts @@ -1,4 +1,5 @@ import { raiseError } from 'analytics' +export const SERVER_FAULT = 'Server Fault' export const NOT_FOUND = 'Repo Not Found' export const BAD_CREDENTIALS = 'Bad credentials' export const API_RATE_LIMIT = `API rate limit` @@ -31,6 +32,10 @@ async function request(url: string, { accessToken }: Options = {}) { headers.Authorization = `token ${accessToken}` } const res = await fetch(url, { headers }) + const contentType = res.headers.get('Content-Type') + if (!contentType || !contentType.includes('application/json')) { + throw new Error(`Response content is not JSON`) + } // About res.ok: // True if res.status between 200~299 // Ref: https://developer.mozilla.org/en-US/docs/Web/API/Response/ok @@ -39,6 +44,7 @@ async function request(url: string, { accessToken }: Options = {}) { } else { // for private repo, GitHub api also responses with 404 when unauthorized if (res.status === 404) throw new Error(NOT_FOUND) + else if (res.status === 500) throw new Error(SERVER_FAULT) else { const content = await res.json() if (apiRateLimitExceeded(content)) throw new Error(API_RATE_LIMIT) From 4a08a0ec922cea051dd49ba76b2025a535903579 Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Sun, 17 Nov 2019 16:50:33 +0800 Subject: [PATCH 51/98] feat: handle OAuth token expiration error --- src/components/SettingsBar.tsx | 2 +- src/components/SideBar.tsx | 17 ++++++++++++++--- src/content.less | 3 +++ 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/src/components/SettingsBar.tsx b/src/components/SettingsBar.tsx index c7d75a2..b9f461e 100644 --- a/src/components/SettingsBar.tsx +++ b/src/components/SettingsBar.tsx @@ -138,7 +138,7 @@ function SettingsBarContent() { {!hasAccessToken && ( { // use js here to make sure redirect_uri is latest url const url = `https://github.com/login/oauth/authorize?client_id=${ diff --git a/src/components/SideBar.tsx b/src/components/SideBar.tsx index 83c01b9..4b41aac 100644 --- a/src/components/SideBar.tsx +++ b/src/components/SideBar.tsx @@ -173,11 +173,22 @@ async function trySetUpAccessTokenWithCode() { client_id: oauth.clientId, client_secret: oauth.clientSecret, }) - const { access_token: accessToken, scope } = res - if (scope !== 'repo' || !accessToken) { + const { access_token: accessToken, scope, error_description: errorDescription } = res + if (errorDescription) { + const TOKEN_EXPIRED_DESCRIPTION = `The code passed is incorrect or expired.` + if (errorDescription === TOKEN_EXPIRED_DESCRIPTION) { + alert(`Gitako: The OAuth token has expired, please try again.`) + } else { + throw new Error(errorDescription) + } + } else if (scope !== 'repo' || !accessToken) { throw new Error(`Cannot resolve token response: '${JSON.stringify(res)}'`) } - window.history.pushState({}, 'removed code', window.location.pathname.replace(/#.*$/, '')) + window.history.pushState( + {}, + 'removed search param', + window.location.pathname.replace(window.location.search, ''), + ) return accessToken } } catch (err) { diff --git a/src/content.less b/src/content.less index b0ac42f..dbea7de 100644 --- a/src/content.less +++ b/src/content.less @@ -435,6 +435,9 @@ } .access-token { border-bottom: none; // prevent overwrite by github style + .link-button { + cursor: pointer; + } .hint { color: #6a737d; } From 42dadbd8db5e5e4888ddb88f7a2887f6390719c9 Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Sun, 17 Nov 2019 17:01:46 +0800 Subject: [PATCH 52/98] fix: handle 401 --- src/utils/GitHubHelper.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/utils/GitHubHelper.ts b/src/utils/GitHubHelper.ts index 22b4fa9..2d7ef96 100644 --- a/src/utils/GitHubHelper.ts +++ b/src/utils/GitHubHelper.ts @@ -42,8 +42,7 @@ async function request(url: string, { accessToken }: Options = {}) { if (res.ok) { return res.json() } else { - // for private repo, GitHub api also responses with 404 when unauthorized - if (res.status === 404) throw new Error(NOT_FOUND) + if (res.status === 404 || res.status === 401) throw new Error(NOT_FOUND) else if (res.status === 500) throw new Error(SERVER_FAULT) else { const content = await res.json() From 5359275874fefaab6c3f72b7255384f2e0b2d1cc Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Mon, 18 Nov 2019 19:49:58 +0800 Subject: [PATCH 53/98] build: build all scripts --- webpack.config.js | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/webpack.config.js b/webpack.config.js index a3d34fd..2c62aba 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -18,14 +18,6 @@ const plugins = [ from: './src/assets/icons/*', to: 'icons/[name].[ext]', }, - { - from: 'node_modules/webextension-polyfill/dist/browser-polyfill.js', - to: 'browser-polyfill.js', - }, - { - from: './src/firefox-shim.js', - to: 'firefox-shim.js', - }, ]), new ForkTsCheckerWebpackPlugin(), new Dotenv(), @@ -48,6 +40,8 @@ plugins.push( module.exports = { entry: { content: './src/content.tsx', + 'browser-polyfill': './node_modules/webextension-polyfill/dist/browser-polyfill.js', + 'firefox-shim': './src/firefox-shim.js', }, devtool: IN_PRODUCTION_MODE ? 'source-map' : 'eval-source-map', mode: IN_PRODUCTION_MODE ? 'production' : 'development', From cd889cf2926eac95cfb09b0caebfedba3bf55607 Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Mon, 18 Nov 2019 19:50:04 +0800 Subject: [PATCH 54/98] 0.7.2 --- package.json | 2 +- src/manifest.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 33477bd..569d64d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gitako", - "version": "0.7.1", + "version": "0.7.2", "description": "The missing part of GitHub.", "repository": "https://github.com/EnixCoda/Gitako", "author": "EnixCoda", diff --git a/src/manifest.json b/src/manifest.json index 4036f3b..40d74c0 100644 --- a/src/manifest.json +++ b/src/manifest.json @@ -1,7 +1,7 @@ { "manifest_version": 2, "name": "Gitako - Github file tree", - "version": "0.7.1", + "version": "0.7.2", "description": "The missing part of GitHub.", "author": "EnixCoda", "icons": { From 1189237dd441a6a632cc0490719965f6e64b1f05 Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Tue, 19 Nov 2019 09:53:55 +0800 Subject: [PATCH 55/98] Revert "build: build all scripts" This reverts commit 5359275874fefaab6c3f72b7255384f2e0b2d1cc. --- webpack.config.js | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/webpack.config.js b/webpack.config.js index 2c62aba..a3d34fd 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -18,6 +18,14 @@ const plugins = [ from: './src/assets/icons/*', to: 'icons/[name].[ext]', }, + { + from: 'node_modules/webextension-polyfill/dist/browser-polyfill.js', + to: 'browser-polyfill.js', + }, + { + from: './src/firefox-shim.js', + to: 'firefox-shim.js', + }, ]), new ForkTsCheckerWebpackPlugin(), new Dotenv(), @@ -40,8 +48,6 @@ plugins.push( module.exports = { entry: { content: './src/content.tsx', - 'browser-polyfill': './node_modules/webextension-polyfill/dist/browser-polyfill.js', - 'firefox-shim': './src/firefox-shim.js', }, devtool: IN_PRODUCTION_MODE ? 'source-map' : 'eval-source-map', mode: IN_PRODUCTION_MODE ? 'production' : 'development', From 85efec66f35eb700bec3abe4622cf89ddee8c86d Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Tue, 19 Nov 2019 09:54:30 +0800 Subject: [PATCH 56/98] 0.7.3 --- package.json | 2 +- src/manifest.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 569d64d..532355a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gitako", - "version": "0.7.2", + "version": "0.7.3", "description": "The missing part of GitHub.", "repository": "https://github.com/EnixCoda/Gitako", "author": "EnixCoda", diff --git a/src/manifest.json b/src/manifest.json index 40d74c0..2effc1d 100644 --- a/src/manifest.json +++ b/src/manifest.json @@ -1,7 +1,7 @@ { "manifest_version": 2, "name": "Gitako - Github file tree", - "version": "0.7.2", + "version": "0.7.3", "description": "The missing part of GitHub.", "author": "EnixCoda", "icons": { From 6a7c3a6a7857d1b43a91d51fcb5c4f7599c24952 Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Tue, 19 Nov 2019 10:13:20 +0800 Subject: [PATCH 57/98] Revert "chore: remove "pjax-link"" This reverts commit cb2bfaee59b9e388e02360f89df884295bf3dfb6. --- src/components/MetaBar.tsx | 2 +- src/utils/DOMHelper.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/MetaBar.tsx b/src/components/MetaBar.tsx index 4d9e655..2808240 100644 --- a/src/components/MetaBar.tsx +++ b/src/components/MetaBar.tsx @@ -15,7 +15,7 @@ export function MetaBar({ metaData }: Props) { {metaData.userName}  /  - + {metaData.repoName}  /  diff --git a/src/utils/DOMHelper.ts b/src/utils/DOMHelper.ts index fbc2cf6..b37c3db 100644 --- a/src/utils/DOMHelper.ts +++ b/src/utils/DOMHelper.ts @@ -121,7 +121,7 @@ export function scrollToRepoContent() { } const pjax = new PJAX({ - elements: '', + elements: '.pjax-link', selectors: ['.repository-content', 'title'], scrollTo: false, analytics: false, From 53906be1b150374e04b19b6b7800f6ee832ebccd Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Tue, 19 Nov 2019 10:13:27 +0800 Subject: [PATCH 58/98] Revert "refactor: remove pjax listener" This reverts commit aca84b3b2b2d0aa4f527ab2f114a6f3e15ae40fb. --- src/components/SideBar.tsx | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/src/components/SideBar.tsx b/src/components/SideBar.tsx index 4b41aac..48b8865 100644 --- a/src/components/SideBar.tsx +++ b/src/components/SideBar.tsx @@ -11,7 +11,6 @@ import { SideBarCore } from 'driver/core' import { ConnectorState, Props } from 'driver/core/SideBar' import { oauth } from 'env' import * as React from 'react' -import { useLocation } from 'react-use' import { cx } from 'utils/cx' import * as DOMHelper from 'utils/DOMHelper' import { JSONRequest, parseURLSearch } from 'utils/general' @@ -57,19 +56,19 @@ const RawGitako: React.FC = function RawGitako(props) { DOMHelper.unmountTopProgressBar() props.setMetaData({ ...props.metaData, ...URLHelper.parse() }) }, [props.disabled, props.metaData, configContext.val]) - useOnLocationChange(updateMeta) + useOnPJAXComplete(updateMeta) const attachCopyFileButton = React.useCallback(() => { if (props.disabled) return if (configContext.val.copyFileButton) return DOMHelper.attachCopyFileBtn() }, [props.disabled, configContext.val.copyFileButton]) - useOnLocationChange(attachCopyFileButton) + useOnPJAXComplete(attachCopyFileButton) const attachCopySnippetButton = React.useCallback(() => { if (props.disabled) return if (configContext.val.copySnippetButton) return DOMHelper.attachCopySnippet() }, [props.disabled, configContext.val.copySnippetButton]) - useOnLocationChange(attachCopySnippetButton) + useOnPJAXComplete(attachCopySnippetButton) React.useEffect(() => { if (configContext.val.intelligentToggle === null) { @@ -144,6 +143,20 @@ RawGitako.defaultProps = { export const SideBar = connect(SideBarCore)(RawGitako) +function useEvent< + T extends { + addEventListener: Function + removeEventListener: Function + } +>(target: T, event: string, callback: () => void, deps: React.DependencyList = []) { + React.useEffect(() => { + target.addEventListener(event, callback) + return () => target.removeEventListener(event, callback) + }, [callback, ...deps]) +} + +const useOnPJAXComplete = useEvent.bind(null, window, 'pjax:complete') + function renderAccessDeniedError() { return (
@@ -195,10 +208,3 @@ async function trySetUpAccessTokenWithCode() { raiseError(err) } } - -function useOnLocationChange(callback: () => void) { - const { href, pathname, search } = useLocation() - useDidUpdate(() => { - callback() - }, [href, pathname, search]) -} From 379dff9de4673ecebb10c00ff134704e6494c427 Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Tue, 19 Nov 2019 10:13:49 +0800 Subject: [PATCH 59/98] Revert "build: add react-use" This reverts commit 4076e4cb493b1b05fc17ad6aa2d7b626a447694e. --- package.json | 1 - yarn.lock | 187 +-------------------------------------------------- 2 files changed, 1 insertion(+), 187 deletions(-) diff --git a/package.json b/package.json index 532355a..190e238 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,6 @@ "nprogress": "^0.2.0", "react": "^16.8.6", "react-dom": "^16.8.6", - "react-use": "^13.8.0", "react-window": "^1.8.5", "safe-touch": "^0.2.2", "webextension-polyfill": "^0.5.0" diff --git a/yarn.lock b/yarn.lock index 6e4f4f7..6f77807 100644 --- a/yarn.lock +++ b/yarn.lock @@ -769,13 +769,6 @@ dependencies: regenerator-runtime "^0.13.2" -"@babel/runtime@^7.1.2": - version "7.7.2" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.7.2.tgz#111a78002a5c25fc8e3361bedc9529c696b85a6a" - integrity sha512-JONRbXbTXc9WQE2mAZd1p0Z3DZ/6vaQIkgYMSTP3KjRCyd7rCZCcfhCyX+YjwcKxcZ82UrxbRD358bpExNgrjw== - dependencies: - regenerator-runtime "^0.13.2" - "@babel/template@^7.1.0", "@babel/template@^7.4.4", "@babel/template@^7.6.0": version "7.6.0" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.6.0.tgz#7f0159c7f5012230dad64cca42ec9bdb5c9536e6" @@ -1739,11 +1732,6 @@ boolbase@~1.0.0: resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" integrity sha1-aN/1++YMUes3cl6p4+0xDcwed24= -bowser@^1.7.3: - version "1.9.4" - resolved "https://registry.yarnpkg.com/bowser/-/bowser-1.9.4.tgz#890c58a2813a9d3243704334fa81b96a5c150c9a" - integrity sha512-9IdMmj2KjigRq6oWhmwv1W36pDuA4STQZ8q6YO9um+x07xgYNCD3Oou+WP/3L1HNz7iqythGet3/p4wvc8AAwQ== - boxen@^3.0.0: version "3.2.0" resolved "https://registry.yarnpkg.com/boxen/-/boxen-3.2.0.tgz#fbdff0de93636ab4450886b6ff45b92d098f45eb" @@ -2384,13 +2372,6 @@ copy-descriptor@^0.1.0: resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40= -copy-to-clipboard@^3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/copy-to-clipboard/-/copy-to-clipboard-3.2.0.tgz#d2724a3ccbfed89706fac8a894872c979ac74467" - integrity sha512-eOZERzvCmxS8HWzugj4Uxl8OJxa7T2k1Gi0X5qavwydHIfuSHq2dTD09LOg/XyGq4Zpb5IsR/2OJ5lbOegz78w== - dependencies: - toggle-selection "^1.0.6" - copy-webpack-plugin@^5.0.0: version "5.0.4" resolved "https://registry.yarnpkg.com/copy-webpack-plugin/-/copy-webpack-plugin-5.0.4.tgz#c78126f604e24f194c6ec2f43a64e232b5d43655" @@ -2515,14 +2496,6 @@ crypto-random-string@^1.0.0: resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-1.0.0.tgz#a230f64f568310e1498009940790ec99545bca7e" integrity sha1-ojD2T1aDEOFJgAmUB5DsmVRbyn4= -css-in-js-utils@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/css-in-js-utils/-/css-in-js-utils-2.0.1.tgz#3b472b398787291b47cfe3e44fecfdd9e914ba99" - integrity sha512-PJF0SpJT+WdbVVt0AOYp9C8GnuruRlL/UFW7932nLWmFLQTaWEzTBQEx7/hn4BuV+WON75iAViSUJLiU3PKbpA== - dependencies: - hyphenate-style-name "^1.0.2" - isobject "^3.0.1" - css-loader@^2.1.0: version "2.1.1" resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-2.1.1.tgz#d8254f72e412bb2238bb44dd674ffbef497333ea" @@ -2550,14 +2523,6 @@ css-select@~1.2.0: domutils "1.5.1" nth-check "~1.0.1" -css-tree@^1.0.0-alpha.28: - version "1.0.0-alpha.37" - resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.0.0-alpha.37.tgz#98bebd62c4c1d9f960ec340cf9f7522e30709a22" - integrity sha512-DMxWJg0rnz7UgxKT0Q1HU/L9BeJI0M6ksor0OgqOnF+aRCDWg/N2641HmVyU9KVIu0OVVWOb2IpC9A+BJRnejg== - dependencies: - mdn-data "2.0.4" - source-map "^0.6.1" - css-what@2.1: version "2.1.3" resolved "https://registry.yarnpkg.com/css-what/-/css-what-2.1.3.tgz#a6d7604573365fe74686c3f311c56513d88285f2" @@ -2573,11 +2538,6 @@ csstype@^2.2.0: resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.6.6.tgz#c34f8226a94bbb10c32cc0d714afdf942291fc41" integrity sha512-RpFbQGUE74iyPgvr46U9t1xoQBM8T4BL8SxrN66Le2xYAPSaDJJKeztV3awugusb3g3G9iL8StmkBBXhcbbXhg== -csstype@^2.5.5: - version "2.6.7" - resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.6.7.tgz#20b0024c20b6718f4eda3853a1f5a1cce7f5e4a5" - integrity sha512-9Mcn9sFbGBAdmimWb2gLVDtFJzeKtDGIr76TUqmjZrw9LFXBMSU70lcs+C0/7fyCd6iBDqmksUcCOUIkisPHsQ== - cyclist@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-1.0.1.tgz#596e9698fd0c80e12038c2b82d6eb1b35b6224d9" @@ -3031,13 +2991,6 @@ error-ex@^1.3.1: dependencies: is-arrayish "^0.2.1" -error-stack-parser@^2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/error-stack-parser/-/error-stack-parser-2.0.4.tgz#a757397dc5d9de973ac9a5d7d4e8ade7cfae9101" - integrity sha512-fZ0KkoxSjLFmhW5lHbUT3tLwy3nX1qEzMYo8koY1vrsAco53CMT1djnBSeC/wUjTEZRhZl9iRw7PaMaxfJ4wzQ== - dependencies: - stackframe "^1.1.0" - es-abstract@^1.5.1: version "1.16.0" resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.16.0.tgz#d3a26dc9c3283ac9750dca569586e976d9dcc06d" @@ -3567,11 +3520,6 @@ fast-safe-stringify@^2.0.7: resolved "https://registry.yarnpkg.com/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz#124aa885899261f68aedb42a7c080de9da608743" integrity sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA== -fastest-stable-stringify@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/fastest-stable-stringify/-/fastest-stable-stringify-1.0.1.tgz#9122d406d4c9d98bea644a6b6853d5874b87b028" - integrity sha1-kSLUBtTJ2YvqZEpraFPVh0uHsCg= - fd-slicer@~1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.1.0.tgz#25c7c89cb1f9077f8891bbe61d8f390eae256f1e" @@ -4304,11 +4252,6 @@ https-proxy-agent@^2.2.1: agent-base "^4.3.0" debug "^3.1.0" -hyphenate-style-name@^1.0.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/hyphenate-style-name/-/hyphenate-style-name-1.0.3.tgz#097bb7fa0b8f1a9cf0bd5c734cf95899981a9b48" - integrity sha512-EcuixamT82oplpoJ2XU4pDtKGWQ7b00CD9f1ug9IaQ3p1bkHMiKCZ9ut9QDI6qsa6cpUuB+A/I+zLtdNK4n2DQ== - iconv-lite@0.4.24, iconv-lite@^0.4.24, iconv-lite@^0.4.4, iconv-lite@~0.4.13: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" @@ -4424,14 +4367,6 @@ ini@^1.3.4, ini@^1.3.5, ini@~1.3.0, ini@~1.3.3: resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927" integrity sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw== -inline-style-prefixer@^4.0.0: - version "4.0.2" - resolved "https://registry.yarnpkg.com/inline-style-prefixer/-/inline-style-prefixer-4.0.2.tgz#d390957d26f281255fe101da863158ac6eb60911" - integrity sha512-N8nVhwfYga9MiV9jWlwfdj1UDIaZlBFu4cJSJkIr7tZX7sHpHhGR5su1qdpW+7KPL8ISTvCIkcaFi/JdBknvPg== - dependencies: - bowser "^1.7.3" - css-in-js-utils "^2.0.0" - inquirer@^0.12.0: version "0.12.0" resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-0.12.0.tgz#1ef2bfd63504df0bc75785fff8c2c41df12f077e" @@ -5258,11 +5193,6 @@ mdn-browser-compat-data@0.0.94: dependencies: extend "3.0.2" -mdn-data@2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.4.tgz#699b3c38ac6f1d728091a64650b65d388502fd5b" - integrity sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA== - media-typer@0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" @@ -5531,20 +5461,6 @@ nan@^2.12.1, nan@^2.14.0: resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.0.tgz#7818f722027b2459a86f0295d434d1fc2336c52c" integrity sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg== -nano-css@^5.2.1: - version "5.2.1" - resolved "https://registry.yarnpkg.com/nano-css/-/nano-css-5.2.1.tgz#73b8470fa40b028a134d3393ae36bbb34b9fa332" - integrity sha512-T54okxMAha0+de+W8o3qFtuWhTxYvqQh2ku1cYEqTTP9mR62nWV2lLK9qRuAGWmoaYWhU7K4evT9Lc1iF65wuw== - dependencies: - css-tree "^1.0.0-alpha.28" - csstype "^2.5.5" - fastest-stable-stringify "^1.0.1" - inline-style-prefixer "^4.0.0" - rtl-css-js "^1.9.0" - sourcemap-codec "^1.4.1" - stacktrace-js "^2.0.0" - stylis "3.5.0" - nanomatch@^1.2.9: version "1.2.13" resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119" @@ -6470,31 +6386,11 @@ react-dom@^16.8.6: prop-types "^15.6.2" scheduler "^0.15.0" -react-fast-compare@^2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-2.0.4.tgz#e84b4d455b0fec113e0402c329352715196f81f9" - integrity sha512-suNP+J1VU1MWFKcyt7RtjiSWUjvidmQSlqu+eHslq+342xCbGTYmC0mEhPCOHxlW0CywylOC1u2DFAT+bv4dBw== - react-is@^16.8.1: version "16.9.0" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.9.0.tgz#21ca9561399aad0ff1a7701c01683e8ca981edcb" integrity sha512-tJBzzzIgnnRfEm046qRcURvwQnZVXmuCbscxUO5RWrGTXpon2d4c8mI0D8WE6ydVIm29JiLB6+RslkIvym9Rjw== -react-use@^13.8.0: - version "13.8.0" - resolved "https://registry.yarnpkg.com/react-use/-/react-use-13.8.0.tgz#5e8badaaa5614a1925fd28ad22d01cc1c52e0ff1" - integrity sha512-J4ZWIqC1h2eSS6ObmHJUWDQeJSKZvHdBEINCu51plz7RS+ijrt6JEHfDWbNXx+aAoR5AmbRK+mzYwd3jiu3xpA== - dependencies: - copy-to-clipboard "^3.2.0" - nano-css "^5.2.1" - react-fast-compare "^2.0.4" - resize-observer-polyfill "^1.5.1" - screenfull "^5.0.0" - set-harmonic-interval "^1.0.1" - throttle-debounce "^2.1.0" - ts-easing "^0.2.0" - tslib "^1.10.0" - react-window@^1.8.5: version "1.8.5" resolved "https://registry.yarnpkg.com/react-window/-/react-window-1.8.5.tgz#a56b39307e79979721021f5d06a67742ecca52d1" @@ -6750,11 +6646,6 @@ require-uncached@^1.0.2: caller-path "^0.1.0" resolve-from "^1.0.0" -resize-observer-polyfill@^1.5.1: - version "1.5.1" - resolved "https://registry.yarnpkg.com/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz#0e9020dd3d21024458d4ebd27e23e40269810464" - integrity sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg== - resolve-cwd@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-2.0.0.tgz#00a9f7387556e27038eae232caa372a6a59b665a" @@ -6854,13 +6745,6 @@ ripemd160@^2.0.0, ripemd160@^2.0.1: hash-base "^3.0.0" inherits "^2.0.1" -rtl-css-js@^1.9.0: - version "1.13.1" - resolved "https://registry.yarnpkg.com/rtl-css-js/-/rtl-css-js-1.13.1.tgz#80deabf6e8f36d6767d495cd3eb60fecb20c67e1" - integrity sha512-jgkIDj6Xi25kAEm5oYM3ZMFiOQhpLEcXi2LY/6bVr91cVz73hciHKneL5AMVPxOcks/JuizSaaNsvNRkeAWe3w== - dependencies: - "@babel/runtime" "^7.1.2" - run-async@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/run-async/-/run-async-0.1.0.tgz#c8ad4a5e110661e402a7d21b530e009f25f8e389" @@ -6948,11 +6832,6 @@ schema-utils@^1.0.0: ajv-errors "^1.0.0" ajv-keywords "^3.1.0" -screenfull@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/screenfull/-/screenfull-5.0.0.tgz#5c2010c0e84fd4157bf852877698f90b8cbe96f6" - integrity sha512-yShzhaIoE9OtOhWVyBBffA6V98CDCoyHTsp8228blmqYy1Z5bddzE/4FPiJKlr8DVR4VBiiUyfPzIQPIYDkeMA== - semver-diff@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/semver-diff/-/semver-diff-2.1.0.tgz#4bbb8437c8d37e4b0cf1a68fd726ec6d645d6d36" @@ -7009,11 +6888,6 @@ set-blocking@^2.0.0, set-blocking@~2.0.0: resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= -set-harmonic-interval@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/set-harmonic-interval/-/set-harmonic-interval-1.0.1.tgz#e1773705539cdfb80ce1c3d99e7f298bb3995249" - integrity sha512-AhICkFV84tBP1aWqPwLZqFvAwqEoVA9kxNMniGEUvzOlm4vLmOFLiTT3UZ6bziJTy4bOVpzWGTfSCbmaayGx8g== - set-value@^2.0.0, set-value@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.1.tgz#a18d40530e6f07de4228c7defe4227af8cad005b" @@ -7213,11 +7087,6 @@ source-map-url@^0.4.0: resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3" integrity sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM= -source-map@0.5.6: - version "0.5.6" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.6.tgz#75ce38f52bf0733c5a7f0c118d81334a2bb5f412" - integrity sha1-dc449SvwczxafwwRjYEzSiu19BI= - source-map@^0.5.0, source-map@^0.5.3, source-map@^0.5.6: version "0.5.7" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" @@ -7228,11 +7097,6 @@ source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.0, source-map@~0.6.1: resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== -sourcemap-codec@^1.4.1: - version "1.4.6" - resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.6.tgz#e30a74f0402bad09807640d39e971090a08ce1e9" - integrity sha512-1ZooVLYFxC448piVLBbtOxFcXwnymH9oUF8nRd3CuYDVvkRBxRl6pB4Mtas5a4drtL+E8LDgFkQNcgIw6tc8Hg== - spawn-sync@1.0.15: version "1.0.15" resolved "https://registry.yarnpkg.com/spawn-sync/-/spawn-sync-1.0.15.tgz#b00799557eb7fb0c8376c29d44e8a1ea67e57476" @@ -7282,35 +7146,6 @@ ssri@^6.0.1: dependencies: figgy-pudding "^3.5.1" -stack-generator@^2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/stack-generator/-/stack-generator-2.0.4.tgz#027513eab2b195bbb43b9c8360ba2dd0ab54de09" - integrity sha512-ha1gosTNcgxwzo9uKTQ8zZ49aUp5FIUW58YHFxCqaAHtE0XqBg0chGFYA1MfmW//x1KWq3F4G7Ug7bJh4RiRtg== - dependencies: - stackframe "^1.1.0" - -stackframe@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/stackframe/-/stackframe-1.1.0.tgz#e3fc2eb912259479c9822f7d1f1ff365bd5cbc83" - integrity sha512-Vx6W1Yvy+AM1R/ckVwcHQHV147pTPBKWCRLrXMuPrFVfvBUc3os7PR1QLIWCMhPpRg5eX9ojzbQIMLGBwyLjqg== - -stacktrace-gps@^3.0.3: - version "3.0.3" - resolved "https://registry.yarnpkg.com/stacktrace-gps/-/stacktrace-gps-3.0.3.tgz#b89f84cc13bb925b96607e737b617c8715facf57" - integrity sha512-51Rr7dXkyFUKNmhY/vqZWK+EvdsfFSRiQVtgHTFlAdNIYaDD7bVh21yBHXaNWAvTD+w+QSjxHg7/v6Tz4veExA== - dependencies: - source-map "0.5.6" - stackframe "^1.1.0" - -stacktrace-js@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/stacktrace-js/-/stacktrace-js-2.0.1.tgz#ebdb0e9a16e6f171f96ca7878404e7f15c3d42ba" - integrity sha512-13oDNgBSeWtdGa4/2BycNyKqe+VktCoJ8VLx4pDoJkwGGJVtiHdfMOAj3aW9xTi8oR2v34z9IcvfCvT6XNdNAw== - dependencies: - error-stack-parser "^2.0.4" - stack-generator "^2.0.4" - stacktrace-gps "^3.0.3" - static-extend@^0.1.1: version "0.1.2" resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6" @@ -7509,11 +7344,6 @@ style-loader@^0.23.1: loader-utils "^1.1.0" schema-utils "^1.0.0" -stylis@3.5.0: - version "3.5.0" - resolved "https://registry.yarnpkg.com/stylis/-/stylis-3.5.0.tgz#016fa239663d77f868fef5b67cf201c4b7c701e1" - integrity sha512-pP7yXN6dwMzAR29Q0mBrabPCe0/mNO1MSr93bhay+hcZondvMMTpeGyd8nbhYJdyperNT2DRxONQuUGcJr5iPw== - supports-color@6.1.0, supports-color@^6.1.0: version "6.1.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-6.1.0.tgz#0764abc69c63d5ac842dd4867e8d025e880df8f3" @@ -7636,11 +7466,6 @@ thenify-all@^1.0.0: dependencies: any-promise "^1.0.0" -throttle-debounce@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/throttle-debounce/-/throttle-debounce-2.1.0.tgz#257e648f0a56bd9e54fe0f132c4ab8611df4e1d5" - integrity sha512-AOvyNahXQuU7NN+VVvOOX+uW6FPaWdAOdRP5HfwYxAfCzXTFKRMoIMk+n+po318+ktcChx+F1Dd91G3YHeMKyg== - through2@^2.0.0: version "2.0.5" resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd" @@ -7720,11 +7545,6 @@ to-regex@^3.0.1, to-regex@^3.0.2: regex-not "^1.0.2" safe-regex "^1.1.0" -toggle-selection@^1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/toggle-selection/-/toggle-selection-1.0.6.tgz#6e45b1263f2017fa0acc7d89d78b15b8bf77da32" - integrity sha1-bkWxJj8gF/oKzH2J14sVuL932jI= - toidentifier@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553" @@ -7772,12 +7592,7 @@ tryer@^1.0.1: resolved "https://registry.yarnpkg.com/tryer/-/tryer-1.0.1.tgz#f2c85406800b9b0f74c9f7465b81eaad241252f8" integrity sha512-c3zayb8/kWWpycWYg87P71E1S1ZL6b6IJxfb5fvsUgsf0S2MVGaDhDXXjDMpdCpfWXqptc+4mXwmiy1ypXqRAA== -ts-easing@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/ts-easing/-/ts-easing-0.2.0.tgz#c8a8a35025105566588d87dbda05dd7fbfa5a4ec" - integrity sha512-Z86EW+fFFh/IFB1fqQ3/+7Zpf9t2ebOAxNI/V6Wo7r5gqiqtxmgTlQ1qbqQcjLKYeSHPTsEmvlJUDg/EuL0uHQ== - -tslib@^1.10.0, tslib@^1.9.0, tslib@^1.9.3: +tslib@^1.9.0, tslib@^1.9.3: version "1.10.0" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.10.0.tgz#c3c19f95973fb0a62973fb09d90d961ee43e5c8a" integrity sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ== From 69b9449c33011074e321884828674218c8e83d4c Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Tue, 19 Nov 2019 10:15:49 +0800 Subject: [PATCH 60/98] 0.7.4 --- package.json | 2 +- src/manifest.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 190e238..ad35659 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gitako", - "version": "0.7.3", + "version": "0.7.4", "description": "The missing part of GitHub.", "repository": "https://github.com/EnixCoda/Gitako", "author": "EnixCoda", diff --git a/src/manifest.json b/src/manifest.json index 2effc1d..b1c4c40 100644 --- a/src/manifest.json +++ b/src/manifest.json @@ -1,7 +1,7 @@ { "manifest_version": 2, "name": "Gitako - Github file tree", - "version": "0.7.3", + "version": "0.7.4", "description": "The missing part of GitHub.", "author": "EnixCoda", "icons": { From 2d000c8ced32829156182658ff8c0b3150714f0a Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Tue, 19 Nov 2019 10:40:03 +0800 Subject: [PATCH 61/98] fix: drop attach-form --- packages/pjax/index.js | 2 -- packages/pjax/lib/proto/parse-element.js | 7 ------- 2 files changed, 9 deletions(-) diff --git a/packages/pjax/index.js b/packages/pjax/index.js index 2f2763a..a1919d1 100644 --- a/packages/pjax/index.js +++ b/packages/pjax/index.js @@ -75,8 +75,6 @@ Pjax.prototype = { attachLink: require("./lib/proto/attach-link.js"), - attachForm: require("./lib/proto/attach-form.js"), - forEachSelectors: function(cb, context, DOMContext) { return forEachSelectors.bind(this)(this.options.selectors, cb, context, DOMContext) }, diff --git a/packages/pjax/lib/proto/parse-element.js b/packages/pjax/lib/proto/parse-element.js index 8214a67..153fe82 100644 --- a/packages/pjax/lib/proto/parse-element.js +++ b/packages/pjax/lib/proto/parse-element.js @@ -9,13 +9,6 @@ module.exports = function(el) { } break - case "form": - // only attach link if el does not already have link attached - if (!el.hasAttribute(attrState)) { - this.attachForm(el) - } - break - default: throw "Pjax can only be applied on or
submit" } From 3b8990e340addf3f899807399b5a4f5b1f3b69ac Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Tue, 19 Nov 2019 10:51:23 +0800 Subject: [PATCH 62/98] Revert "Revert "build: add react-use"" This reverts commit 379dff9de4673ecebb10c00ff134704e6494c427. --- package.json | 1 + yarn.lock | 187 ++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 187 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index ad35659..548f0ae 100644 --- a/package.json +++ b/package.json @@ -31,6 +31,7 @@ "nprogress": "^0.2.0", "react": "^16.8.6", "react-dom": "^16.8.6", + "react-use": "^13.8.0", "react-window": "^1.8.5", "safe-touch": "^0.2.2", "webextension-polyfill": "^0.5.0" diff --git a/yarn.lock b/yarn.lock index 6f77807..6e4f4f7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -769,6 +769,13 @@ dependencies: regenerator-runtime "^0.13.2" +"@babel/runtime@^7.1.2": + version "7.7.2" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.7.2.tgz#111a78002a5c25fc8e3361bedc9529c696b85a6a" + integrity sha512-JONRbXbTXc9WQE2mAZd1p0Z3DZ/6vaQIkgYMSTP3KjRCyd7rCZCcfhCyX+YjwcKxcZ82UrxbRD358bpExNgrjw== + dependencies: + regenerator-runtime "^0.13.2" + "@babel/template@^7.1.0", "@babel/template@^7.4.4", "@babel/template@^7.6.0": version "7.6.0" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.6.0.tgz#7f0159c7f5012230dad64cca42ec9bdb5c9536e6" @@ -1732,6 +1739,11 @@ boolbase@~1.0.0: resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" integrity sha1-aN/1++YMUes3cl6p4+0xDcwed24= +bowser@^1.7.3: + version "1.9.4" + resolved "https://registry.yarnpkg.com/bowser/-/bowser-1.9.4.tgz#890c58a2813a9d3243704334fa81b96a5c150c9a" + integrity sha512-9IdMmj2KjigRq6oWhmwv1W36pDuA4STQZ8q6YO9um+x07xgYNCD3Oou+WP/3L1HNz7iqythGet3/p4wvc8AAwQ== + boxen@^3.0.0: version "3.2.0" resolved "https://registry.yarnpkg.com/boxen/-/boxen-3.2.0.tgz#fbdff0de93636ab4450886b6ff45b92d098f45eb" @@ -2372,6 +2384,13 @@ copy-descriptor@^0.1.0: resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40= +copy-to-clipboard@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/copy-to-clipboard/-/copy-to-clipboard-3.2.0.tgz#d2724a3ccbfed89706fac8a894872c979ac74467" + integrity sha512-eOZERzvCmxS8HWzugj4Uxl8OJxa7T2k1Gi0X5qavwydHIfuSHq2dTD09LOg/XyGq4Zpb5IsR/2OJ5lbOegz78w== + dependencies: + toggle-selection "^1.0.6" + copy-webpack-plugin@^5.0.0: version "5.0.4" resolved "https://registry.yarnpkg.com/copy-webpack-plugin/-/copy-webpack-plugin-5.0.4.tgz#c78126f604e24f194c6ec2f43a64e232b5d43655" @@ -2496,6 +2515,14 @@ crypto-random-string@^1.0.0: resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-1.0.0.tgz#a230f64f568310e1498009940790ec99545bca7e" integrity sha1-ojD2T1aDEOFJgAmUB5DsmVRbyn4= +css-in-js-utils@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/css-in-js-utils/-/css-in-js-utils-2.0.1.tgz#3b472b398787291b47cfe3e44fecfdd9e914ba99" + integrity sha512-PJF0SpJT+WdbVVt0AOYp9C8GnuruRlL/UFW7932nLWmFLQTaWEzTBQEx7/hn4BuV+WON75iAViSUJLiU3PKbpA== + dependencies: + hyphenate-style-name "^1.0.2" + isobject "^3.0.1" + css-loader@^2.1.0: version "2.1.1" resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-2.1.1.tgz#d8254f72e412bb2238bb44dd674ffbef497333ea" @@ -2523,6 +2550,14 @@ css-select@~1.2.0: domutils "1.5.1" nth-check "~1.0.1" +css-tree@^1.0.0-alpha.28: + version "1.0.0-alpha.37" + resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.0.0-alpha.37.tgz#98bebd62c4c1d9f960ec340cf9f7522e30709a22" + integrity sha512-DMxWJg0rnz7UgxKT0Q1HU/L9BeJI0M6ksor0OgqOnF+aRCDWg/N2641HmVyU9KVIu0OVVWOb2IpC9A+BJRnejg== + dependencies: + mdn-data "2.0.4" + source-map "^0.6.1" + css-what@2.1: version "2.1.3" resolved "https://registry.yarnpkg.com/css-what/-/css-what-2.1.3.tgz#a6d7604573365fe74686c3f311c56513d88285f2" @@ -2538,6 +2573,11 @@ csstype@^2.2.0: resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.6.6.tgz#c34f8226a94bbb10c32cc0d714afdf942291fc41" integrity sha512-RpFbQGUE74iyPgvr46U9t1xoQBM8T4BL8SxrN66Le2xYAPSaDJJKeztV3awugusb3g3G9iL8StmkBBXhcbbXhg== +csstype@^2.5.5: + version "2.6.7" + resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.6.7.tgz#20b0024c20b6718f4eda3853a1f5a1cce7f5e4a5" + integrity sha512-9Mcn9sFbGBAdmimWb2gLVDtFJzeKtDGIr76TUqmjZrw9LFXBMSU70lcs+C0/7fyCd6iBDqmksUcCOUIkisPHsQ== + cyclist@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-1.0.1.tgz#596e9698fd0c80e12038c2b82d6eb1b35b6224d9" @@ -2991,6 +3031,13 @@ error-ex@^1.3.1: dependencies: is-arrayish "^0.2.1" +error-stack-parser@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/error-stack-parser/-/error-stack-parser-2.0.4.tgz#a757397dc5d9de973ac9a5d7d4e8ade7cfae9101" + integrity sha512-fZ0KkoxSjLFmhW5lHbUT3tLwy3nX1qEzMYo8koY1vrsAco53CMT1djnBSeC/wUjTEZRhZl9iRw7PaMaxfJ4wzQ== + dependencies: + stackframe "^1.1.0" + es-abstract@^1.5.1: version "1.16.0" resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.16.0.tgz#d3a26dc9c3283ac9750dca569586e976d9dcc06d" @@ -3520,6 +3567,11 @@ fast-safe-stringify@^2.0.7: resolved "https://registry.yarnpkg.com/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz#124aa885899261f68aedb42a7c080de9da608743" integrity sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA== +fastest-stable-stringify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/fastest-stable-stringify/-/fastest-stable-stringify-1.0.1.tgz#9122d406d4c9d98bea644a6b6853d5874b87b028" + integrity sha1-kSLUBtTJ2YvqZEpraFPVh0uHsCg= + fd-slicer@~1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.1.0.tgz#25c7c89cb1f9077f8891bbe61d8f390eae256f1e" @@ -4252,6 +4304,11 @@ https-proxy-agent@^2.2.1: agent-base "^4.3.0" debug "^3.1.0" +hyphenate-style-name@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/hyphenate-style-name/-/hyphenate-style-name-1.0.3.tgz#097bb7fa0b8f1a9cf0bd5c734cf95899981a9b48" + integrity sha512-EcuixamT82oplpoJ2XU4pDtKGWQ7b00CD9f1ug9IaQ3p1bkHMiKCZ9ut9QDI6qsa6cpUuB+A/I+zLtdNK4n2DQ== + iconv-lite@0.4.24, iconv-lite@^0.4.24, iconv-lite@^0.4.4, iconv-lite@~0.4.13: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" @@ -4367,6 +4424,14 @@ ini@^1.3.4, ini@^1.3.5, ini@~1.3.0, ini@~1.3.3: resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927" integrity sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw== +inline-style-prefixer@^4.0.0: + version "4.0.2" + resolved "https://registry.yarnpkg.com/inline-style-prefixer/-/inline-style-prefixer-4.0.2.tgz#d390957d26f281255fe101da863158ac6eb60911" + integrity sha512-N8nVhwfYga9MiV9jWlwfdj1UDIaZlBFu4cJSJkIr7tZX7sHpHhGR5su1qdpW+7KPL8ISTvCIkcaFi/JdBknvPg== + dependencies: + bowser "^1.7.3" + css-in-js-utils "^2.0.0" + inquirer@^0.12.0: version "0.12.0" resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-0.12.0.tgz#1ef2bfd63504df0bc75785fff8c2c41df12f077e" @@ -5193,6 +5258,11 @@ mdn-browser-compat-data@0.0.94: dependencies: extend "3.0.2" +mdn-data@2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.4.tgz#699b3c38ac6f1d728091a64650b65d388502fd5b" + integrity sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA== + media-typer@0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" @@ -5461,6 +5531,20 @@ nan@^2.12.1, nan@^2.14.0: resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.0.tgz#7818f722027b2459a86f0295d434d1fc2336c52c" integrity sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg== +nano-css@^5.2.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/nano-css/-/nano-css-5.2.1.tgz#73b8470fa40b028a134d3393ae36bbb34b9fa332" + integrity sha512-T54okxMAha0+de+W8o3qFtuWhTxYvqQh2ku1cYEqTTP9mR62nWV2lLK9qRuAGWmoaYWhU7K4evT9Lc1iF65wuw== + dependencies: + css-tree "^1.0.0-alpha.28" + csstype "^2.5.5" + fastest-stable-stringify "^1.0.1" + inline-style-prefixer "^4.0.0" + rtl-css-js "^1.9.0" + sourcemap-codec "^1.4.1" + stacktrace-js "^2.0.0" + stylis "3.5.0" + nanomatch@^1.2.9: version "1.2.13" resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119" @@ -6386,11 +6470,31 @@ react-dom@^16.8.6: prop-types "^15.6.2" scheduler "^0.15.0" +react-fast-compare@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-2.0.4.tgz#e84b4d455b0fec113e0402c329352715196f81f9" + integrity sha512-suNP+J1VU1MWFKcyt7RtjiSWUjvidmQSlqu+eHslq+342xCbGTYmC0mEhPCOHxlW0CywylOC1u2DFAT+bv4dBw== + react-is@^16.8.1: version "16.9.0" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.9.0.tgz#21ca9561399aad0ff1a7701c01683e8ca981edcb" integrity sha512-tJBzzzIgnnRfEm046qRcURvwQnZVXmuCbscxUO5RWrGTXpon2d4c8mI0D8WE6ydVIm29JiLB6+RslkIvym9Rjw== +react-use@^13.8.0: + version "13.8.0" + resolved "https://registry.yarnpkg.com/react-use/-/react-use-13.8.0.tgz#5e8badaaa5614a1925fd28ad22d01cc1c52e0ff1" + integrity sha512-J4ZWIqC1h2eSS6ObmHJUWDQeJSKZvHdBEINCu51plz7RS+ijrt6JEHfDWbNXx+aAoR5AmbRK+mzYwd3jiu3xpA== + dependencies: + copy-to-clipboard "^3.2.0" + nano-css "^5.2.1" + react-fast-compare "^2.0.4" + resize-observer-polyfill "^1.5.1" + screenfull "^5.0.0" + set-harmonic-interval "^1.0.1" + throttle-debounce "^2.1.0" + ts-easing "^0.2.0" + tslib "^1.10.0" + react-window@^1.8.5: version "1.8.5" resolved "https://registry.yarnpkg.com/react-window/-/react-window-1.8.5.tgz#a56b39307e79979721021f5d06a67742ecca52d1" @@ -6646,6 +6750,11 @@ require-uncached@^1.0.2: caller-path "^0.1.0" resolve-from "^1.0.0" +resize-observer-polyfill@^1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz#0e9020dd3d21024458d4ebd27e23e40269810464" + integrity sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg== + resolve-cwd@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-2.0.0.tgz#00a9f7387556e27038eae232caa372a6a59b665a" @@ -6745,6 +6854,13 @@ ripemd160@^2.0.0, ripemd160@^2.0.1: hash-base "^3.0.0" inherits "^2.0.1" +rtl-css-js@^1.9.0: + version "1.13.1" + resolved "https://registry.yarnpkg.com/rtl-css-js/-/rtl-css-js-1.13.1.tgz#80deabf6e8f36d6767d495cd3eb60fecb20c67e1" + integrity sha512-jgkIDj6Xi25kAEm5oYM3ZMFiOQhpLEcXi2LY/6bVr91cVz73hciHKneL5AMVPxOcks/JuizSaaNsvNRkeAWe3w== + dependencies: + "@babel/runtime" "^7.1.2" + run-async@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/run-async/-/run-async-0.1.0.tgz#c8ad4a5e110661e402a7d21b530e009f25f8e389" @@ -6832,6 +6948,11 @@ schema-utils@^1.0.0: ajv-errors "^1.0.0" ajv-keywords "^3.1.0" +screenfull@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/screenfull/-/screenfull-5.0.0.tgz#5c2010c0e84fd4157bf852877698f90b8cbe96f6" + integrity sha512-yShzhaIoE9OtOhWVyBBffA6V98CDCoyHTsp8228blmqYy1Z5bddzE/4FPiJKlr8DVR4VBiiUyfPzIQPIYDkeMA== + semver-diff@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/semver-diff/-/semver-diff-2.1.0.tgz#4bbb8437c8d37e4b0cf1a68fd726ec6d645d6d36" @@ -6888,6 +7009,11 @@ set-blocking@^2.0.0, set-blocking@~2.0.0: resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= +set-harmonic-interval@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/set-harmonic-interval/-/set-harmonic-interval-1.0.1.tgz#e1773705539cdfb80ce1c3d99e7f298bb3995249" + integrity sha512-AhICkFV84tBP1aWqPwLZqFvAwqEoVA9kxNMniGEUvzOlm4vLmOFLiTT3UZ6bziJTy4bOVpzWGTfSCbmaayGx8g== + set-value@^2.0.0, set-value@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.1.tgz#a18d40530e6f07de4228c7defe4227af8cad005b" @@ -7087,6 +7213,11 @@ source-map-url@^0.4.0: resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3" integrity sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM= +source-map@0.5.6: + version "0.5.6" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.6.tgz#75ce38f52bf0733c5a7f0c118d81334a2bb5f412" + integrity sha1-dc449SvwczxafwwRjYEzSiu19BI= + source-map@^0.5.0, source-map@^0.5.3, source-map@^0.5.6: version "0.5.7" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" @@ -7097,6 +7228,11 @@ source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.0, source-map@~0.6.1: resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== +sourcemap-codec@^1.4.1: + version "1.4.6" + resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.6.tgz#e30a74f0402bad09807640d39e971090a08ce1e9" + integrity sha512-1ZooVLYFxC448piVLBbtOxFcXwnymH9oUF8nRd3CuYDVvkRBxRl6pB4Mtas5a4drtL+E8LDgFkQNcgIw6tc8Hg== + spawn-sync@1.0.15: version "1.0.15" resolved "https://registry.yarnpkg.com/spawn-sync/-/spawn-sync-1.0.15.tgz#b00799557eb7fb0c8376c29d44e8a1ea67e57476" @@ -7146,6 +7282,35 @@ ssri@^6.0.1: dependencies: figgy-pudding "^3.5.1" +stack-generator@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/stack-generator/-/stack-generator-2.0.4.tgz#027513eab2b195bbb43b9c8360ba2dd0ab54de09" + integrity sha512-ha1gosTNcgxwzo9uKTQ8zZ49aUp5FIUW58YHFxCqaAHtE0XqBg0chGFYA1MfmW//x1KWq3F4G7Ug7bJh4RiRtg== + dependencies: + stackframe "^1.1.0" + +stackframe@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/stackframe/-/stackframe-1.1.0.tgz#e3fc2eb912259479c9822f7d1f1ff365bd5cbc83" + integrity sha512-Vx6W1Yvy+AM1R/ckVwcHQHV147pTPBKWCRLrXMuPrFVfvBUc3os7PR1QLIWCMhPpRg5eX9ojzbQIMLGBwyLjqg== + +stacktrace-gps@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/stacktrace-gps/-/stacktrace-gps-3.0.3.tgz#b89f84cc13bb925b96607e737b617c8715facf57" + integrity sha512-51Rr7dXkyFUKNmhY/vqZWK+EvdsfFSRiQVtgHTFlAdNIYaDD7bVh21yBHXaNWAvTD+w+QSjxHg7/v6Tz4veExA== + dependencies: + source-map "0.5.6" + stackframe "^1.1.0" + +stacktrace-js@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/stacktrace-js/-/stacktrace-js-2.0.1.tgz#ebdb0e9a16e6f171f96ca7878404e7f15c3d42ba" + integrity sha512-13oDNgBSeWtdGa4/2BycNyKqe+VktCoJ8VLx4pDoJkwGGJVtiHdfMOAj3aW9xTi8oR2v34z9IcvfCvT6XNdNAw== + dependencies: + error-stack-parser "^2.0.4" + stack-generator "^2.0.4" + stacktrace-gps "^3.0.3" + static-extend@^0.1.1: version "0.1.2" resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6" @@ -7344,6 +7509,11 @@ style-loader@^0.23.1: loader-utils "^1.1.0" schema-utils "^1.0.0" +stylis@3.5.0: + version "3.5.0" + resolved "https://registry.yarnpkg.com/stylis/-/stylis-3.5.0.tgz#016fa239663d77f868fef5b67cf201c4b7c701e1" + integrity sha512-pP7yXN6dwMzAR29Q0mBrabPCe0/mNO1MSr93bhay+hcZondvMMTpeGyd8nbhYJdyperNT2DRxONQuUGcJr5iPw== + supports-color@6.1.0, supports-color@^6.1.0: version "6.1.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-6.1.0.tgz#0764abc69c63d5ac842dd4867e8d025e880df8f3" @@ -7466,6 +7636,11 @@ thenify-all@^1.0.0: dependencies: any-promise "^1.0.0" +throttle-debounce@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/throttle-debounce/-/throttle-debounce-2.1.0.tgz#257e648f0a56bd9e54fe0f132c4ab8611df4e1d5" + integrity sha512-AOvyNahXQuU7NN+VVvOOX+uW6FPaWdAOdRP5HfwYxAfCzXTFKRMoIMk+n+po318+ktcChx+F1Dd91G3YHeMKyg== + through2@^2.0.0: version "2.0.5" resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd" @@ -7545,6 +7720,11 @@ to-regex@^3.0.1, to-regex@^3.0.2: regex-not "^1.0.2" safe-regex "^1.1.0" +toggle-selection@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/toggle-selection/-/toggle-selection-1.0.6.tgz#6e45b1263f2017fa0acc7d89d78b15b8bf77da32" + integrity sha1-bkWxJj8gF/oKzH2J14sVuL932jI= + toidentifier@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553" @@ -7592,7 +7772,12 @@ tryer@^1.0.1: resolved "https://registry.yarnpkg.com/tryer/-/tryer-1.0.1.tgz#f2c85406800b9b0f74c9f7465b81eaad241252f8" integrity sha512-c3zayb8/kWWpycWYg87P71E1S1ZL6b6IJxfb5fvsUgsf0S2MVGaDhDXXjDMpdCpfWXqptc+4mXwmiy1ypXqRAA== -tslib@^1.9.0, tslib@^1.9.3: +ts-easing@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/ts-easing/-/ts-easing-0.2.0.tgz#c8a8a35025105566588d87dbda05dd7fbfa5a4ec" + integrity sha512-Z86EW+fFFh/IFB1fqQ3/+7Zpf9t2ebOAxNI/V6Wo7r5gqiqtxmgTlQ1qbqQcjLKYeSHPTsEmvlJUDg/EuL0uHQ== + +tslib@^1.10.0, tslib@^1.9.0, tslib@^1.9.3: version "1.10.0" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.10.0.tgz#c3c19f95973fb0a62973fb09d90d961ee43e5c8a" integrity sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ== From 23cb285d4a57bc1f9181105d0889aa11af9f3e8a Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Tue, 19 Nov 2019 10:51:31 +0800 Subject: [PATCH 63/98] Revert "Revert "refactor: remove pjax listener"" This reverts commit 53906be1b150374e04b19b6b7800f6ee832ebccd. --- src/components/SideBar.tsx | 28 +++++++++++----------------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/src/components/SideBar.tsx b/src/components/SideBar.tsx index 48b8865..4b41aac 100644 --- a/src/components/SideBar.tsx +++ b/src/components/SideBar.tsx @@ -11,6 +11,7 @@ import { SideBarCore } from 'driver/core' import { ConnectorState, Props } from 'driver/core/SideBar' import { oauth } from 'env' import * as React from 'react' +import { useLocation } from 'react-use' import { cx } from 'utils/cx' import * as DOMHelper from 'utils/DOMHelper' import { JSONRequest, parseURLSearch } from 'utils/general' @@ -56,19 +57,19 @@ const RawGitako: React.FC = function RawGitako(props) { DOMHelper.unmountTopProgressBar() props.setMetaData({ ...props.metaData, ...URLHelper.parse() }) }, [props.disabled, props.metaData, configContext.val]) - useOnPJAXComplete(updateMeta) + useOnLocationChange(updateMeta) const attachCopyFileButton = React.useCallback(() => { if (props.disabled) return if (configContext.val.copyFileButton) return DOMHelper.attachCopyFileBtn() }, [props.disabled, configContext.val.copyFileButton]) - useOnPJAXComplete(attachCopyFileButton) + useOnLocationChange(attachCopyFileButton) const attachCopySnippetButton = React.useCallback(() => { if (props.disabled) return if (configContext.val.copySnippetButton) return DOMHelper.attachCopySnippet() }, [props.disabled, configContext.val.copySnippetButton]) - useOnPJAXComplete(attachCopySnippetButton) + useOnLocationChange(attachCopySnippetButton) React.useEffect(() => { if (configContext.val.intelligentToggle === null) { @@ -143,20 +144,6 @@ RawGitako.defaultProps = { export const SideBar = connect(SideBarCore)(RawGitako) -function useEvent< - T extends { - addEventListener: Function - removeEventListener: Function - } ->(target: T, event: string, callback: () => void, deps: React.DependencyList = []) { - React.useEffect(() => { - target.addEventListener(event, callback) - return () => target.removeEventListener(event, callback) - }, [callback, ...deps]) -} - -const useOnPJAXComplete = useEvent.bind(null, window, 'pjax:complete') - function renderAccessDeniedError() { return (
@@ -208,3 +195,10 @@ async function trySetUpAccessTokenWithCode() { raiseError(err) } } + +function useOnLocationChange(callback: () => void) { + const { href, pathname, search } = useLocation() + useDidUpdate(() => { + callback() + }, [href, pathname, search]) +} From 84f95e0ac82486c5c6a7dcb51c679dba2a117460 Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Tue, 19 Nov 2019 10:51:35 +0800 Subject: [PATCH 64/98] Revert "Revert "chore: remove "pjax-link""" This reverts commit 6a7c3a6a7857d1b43a91d51fcb5c4f7599c24952. --- src/components/MetaBar.tsx | 2 +- src/utils/DOMHelper.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/MetaBar.tsx b/src/components/MetaBar.tsx index 2808240..4d9e655 100644 --- a/src/components/MetaBar.tsx +++ b/src/components/MetaBar.tsx @@ -15,7 +15,7 @@ export function MetaBar({ metaData }: Props) { {metaData.userName}  /  - + {metaData.repoName}  /  diff --git a/src/utils/DOMHelper.ts b/src/utils/DOMHelper.ts index b37c3db..fbc2cf6 100644 --- a/src/utils/DOMHelper.ts +++ b/src/utils/DOMHelper.ts @@ -121,7 +121,7 @@ export function scrollToRepoContent() { } const pjax = new PJAX({ - elements: '.pjax-link', + elements: '', selectors: ['.repository-content', 'title'], scrollTo: false, analytics: false, From 79258f0874ff136453803a1507348b8cebbee23f Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Tue, 19 Nov 2019 10:53:22 +0800 Subject: [PATCH 65/98] 0.7.5 --- package.json | 2 +- src/manifest.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 548f0ae..4cc191f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gitako", - "version": "0.7.4", + "version": "0.7.5", "description": "The missing part of GitHub.", "repository": "https://github.com/EnixCoda/Gitako", "author": "EnixCoda", diff --git a/src/manifest.json b/src/manifest.json index b1c4c40..13fda39 100644 --- a/src/manifest.json +++ b/src/manifest.json @@ -1,7 +1,7 @@ { "manifest_version": 2, "name": "Gitako - Github file tree", - "version": "0.7.4", + "version": "0.7.5", "description": "The missing part of GitHub.", "author": "EnixCoda", "icons": { From 5c617aa374bfa15d6a07a62cdb2480cf586e2cf7 Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Tue, 19 Nov 2019 14:53:48 +0800 Subject: [PATCH 66/98] fix: parse form element --- packages/pjax/lib/proto/parse-element.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/pjax/lib/proto/parse-element.js b/packages/pjax/lib/proto/parse-element.js index 153fe82..347ba86 100644 --- a/packages/pjax/lib/proto/parse-element.js +++ b/packages/pjax/lib/proto/parse-element.js @@ -9,6 +9,9 @@ module.exports = function(el) { } break + case "form": + break + default: throw "Pjax can only be applied on or submit" } From 44454152eac4d5b41ea5f2028a6d87469fb37f82 Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Tue, 19 Nov 2019 15:07:22 +0800 Subject: [PATCH 67/98] fix: toggle top progress bar --- src/utils/DOMHelper.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/utils/DOMHelper.ts b/src/utils/DOMHelper.ts index fbc2cf6..cd507bb 100644 --- a/src/utils/DOMHelper.ts +++ b/src/utils/DOMHelper.ts @@ -129,6 +129,9 @@ const pjax = new PJAX({ forceCache: true, // TODO: merge namespace, add forceCache }) +window.addEventListener('pjax:send', () => mountTopProgressBar()) +window.addEventListener('pjax:complete', () => unmountTopProgressBar()) + export function loadWithPJAX(URL: string) { mountTopProgressBar() pjax.loadUrl(URL, { scrollTo: 0 }) From 1ac5189c6f8c7c769c8634e46ec1a2e55e1cbe80 Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Tue, 19 Nov 2019 17:12:13 +0800 Subject: [PATCH 68/98] fix: adopt simpler pjax events and elements --- src/components/SideBar.tsx | 21 +++++++++------------ src/utils/DOMHelper.ts | 3 +-- 2 files changed, 10 insertions(+), 14 deletions(-) diff --git a/src/components/SideBar.tsx b/src/components/SideBar.tsx index 4b41aac..0a77440 100644 --- a/src/components/SideBar.tsx +++ b/src/components/SideBar.tsx @@ -52,13 +52,6 @@ const RawGitako: React.FC = function RawGitako(props) { return () => window.removeEventListener('keydown', onKeyDown) }, [props.disabled, onKeyDown]) - const updateMeta = React.useCallback(() => { - if (props.disabled) return - DOMHelper.unmountTopProgressBar() - props.setMetaData({ ...props.metaData, ...URLHelper.parse() }) - }, [props.disabled, props.metaData, configContext.val]) - useOnLocationChange(updateMeta) - const attachCopyFileButton = React.useCallback(() => { if (props.disabled) return if (configContext.val.copyFileButton) return DOMHelper.attachCopyFileBtn() @@ -71,11 +64,15 @@ const RawGitako: React.FC = function RawGitako(props) { }, [props.disabled, configContext.val.copySnippetButton]) useOnLocationChange(attachCopySnippetButton) - React.useEffect(() => { + useOnLocationChange(() => { if (configContext.val.intelligentToggle === null) { - props.setShouldShow(URLHelper.isInCodePage(props.metaData)) + props.setShouldShow( + URLHelper.isInCodePage({ + branchName: props.metaData?.branchName, + }), + ) } - }, [props.metaData, configContext.val.intelligentToggle]) + }, [props.metaData?.branchName, configContext.val.intelligentToggle]) React.useEffect(() => { if (configContext.val.copyFileButton) return DOMHelper.attachCopyFileBtn() || undefined // undefined is friendlier to React @@ -196,9 +193,9 @@ async function trySetUpAccessTokenWithCode() { } } -function useOnLocationChange(callback: () => void) { +function useOnLocationChange(callback: () => void, deps: React.DependencyList = []) { const { href, pathname, search } = useLocation() useDidUpdate(() => { callback() - }, [href, pathname, search]) + }, [href, pathname, search, ...deps]) } diff --git a/src/utils/DOMHelper.ts b/src/utils/DOMHelper.ts index cd507bb..cfa51d2 100644 --- a/src/utils/DOMHelper.ts +++ b/src/utils/DOMHelper.ts @@ -122,7 +122,7 @@ export function scrollToRepoContent() { const pjax = new PJAX({ elements: '', - selectors: ['.repository-content', 'title'], + selectors: ['.repository-content', 'title', '[data-pjax="#js-repo-pjax-container"]'], scrollTo: false, analytics: false, cacheBust: false, @@ -133,7 +133,6 @@ window.addEventListener('pjax:send', () => mountTopProgressBar()) window.addEventListener('pjax:complete', () => unmountTopProgressBar()) export function loadWithPJAX(URL: string) { - mountTopProgressBar() pjax.loadUrl(URL, { scrollTo: 0 }) } From 83c3b3e0e8530136419d82a958f74e2acb65b09b Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Tue, 19 Nov 2019 18:29:26 +0800 Subject: [PATCH 69/98] refactor: pjax events occur after document state change --- src/components/SideBar.tsx | 70 +++++++++++++++++--------------------- 1 file changed, 32 insertions(+), 38 deletions(-) diff --git a/src/components/SideBar.tsx b/src/components/SideBar.tsx index 0a77440..36d31ad 100644 --- a/src/components/SideBar.tsx +++ b/src/components/SideBar.tsx @@ -11,7 +11,7 @@ import { SideBarCore } from 'driver/core' import { ConnectorState, Props } from 'driver/core/SideBar' import { oauth } from 'env' import * as React from 'react' -import { useLocation } from 'react-use' +import { useEffectOnce, useEvent, useLocation } from 'react-use' import { cx } from 'utils/cx' import * as DOMHelper from 'utils/DOMHelper' import { JSONRequest, parseURLSearch } from 'utils/general' @@ -34,36 +34,22 @@ const RawGitako: React.FC = function RawGitako(props) { })() }, []) - const onKeyDown = React.useCallback( - configContext.val.shortcut - ? (e: KeyboardEvent) => { - const keys = keyHelper.parseEvent(e) - if (keys === configContext.val.shortcut) { - props.toggleShowSideBar() - } + React.useEffect( + function attachKeyDown() { + if (props.disabled || !configContext.val.shortcut) return + + function onKeyDown(e: KeyboardEvent) { + const keys = keyHelper.parseEvent(e) + if (keys === configContext.val.shortcut) { + props.toggleShowSideBar() } - : () => {}, - [configContext.val.shortcut], + } + window.addEventListener('keydown', onKeyDown) + return () => window.removeEventListener('keydown', onKeyDown) + }, + [props.disabled, configContext.val.shortcut], ) - React.useEffect(() => { - if (props.disabled) return - window.addEventListener('keydown', onKeyDown) - return () => window.removeEventListener('keydown', onKeyDown) - }, [props.disabled, onKeyDown]) - - const attachCopyFileButton = React.useCallback(() => { - if (props.disabled) return - if (configContext.val.copyFileButton) return DOMHelper.attachCopyFileBtn() - }, [props.disabled, configContext.val.copyFileButton]) - useOnLocationChange(attachCopyFileButton) - - const attachCopySnippetButton = React.useCallback(() => { - if (props.disabled) return - if (configContext.val.copySnippetButton) return DOMHelper.attachCopySnippet() - }, [props.disabled, configContext.val.copySnippetButton]) - useOnLocationChange(attachCopySnippetButton) - useOnLocationChange(() => { if (configContext.val.intelligentToggle === null) { props.setShouldShow( @@ -74,13 +60,23 @@ const RawGitako: React.FC = function RawGitako(props) { } }, [props.metaData?.branchName, configContext.val.intelligentToggle]) - React.useEffect(() => { - if (configContext.val.copyFileButton) return DOMHelper.attachCopyFileBtn() || undefined // undefined is friendlier to React - }, [configContext.val.copyFileButton]) + const attachCopyFileButton = React.useCallback( + function attachCopyFileButton() { + if (configContext.val.copyFileButton) return DOMHelper.attachCopyFileBtn() || undefined // for the sake of react effect + }, + [configContext.val.copyFileButton], + ) + useEffectOnce(attachCopyFileButton) + useEvent('pjax:complete', attachCopyFileButton, window) - React.useEffect(() => { - if (configContext.val.copySnippetButton) return DOMHelper.attachCopySnippet() || undefined // undefined is friendlier to React - }, [configContext.val.copySnippetButton]) + const attachCopySnippetButton = React.useCallback( + function attachCopySnippetButton() { + if (configContext.val.copySnippetButton) return DOMHelper.attachCopySnippet() || undefined // for the sake of react effect + }, + [configContext.val.copySnippetButton], + ) + useEffectOnce(attachCopySnippetButton) + useEvent('pjax:complete', attachCopySnippetButton, window) // init again when setting new accessToken useDidUpdate(() => { @@ -193,9 +189,7 @@ async function trySetUpAccessTokenWithCode() { } } -function useOnLocationChange(callback: () => void, deps: React.DependencyList = []) { +function useOnLocationChange(callback: React.EffectCallback, extraDeps: React.DependencyList = []) { const { href, pathname, search } = useLocation() - useDidUpdate(() => { - callback() - }, [href, pathname, search, ...deps]) + React.useEffect(callback, [href, pathname, search, ...extraDeps]) } From eddc53e7ae1f8e267ab7ec5e3ef9b74ef335b252 Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Tue, 19 Nov 2019 18:29:47 +0800 Subject: [PATCH 70/98] fix: resolve page type more strictly --- src/utils/DOMHelper.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/utils/DOMHelper.ts b/src/utils/DOMHelper.ts index cfa51d2..7d490d9 100644 --- a/src/utils/DOMHelper.ts +++ b/src/utils/DOMHelper.ts @@ -159,12 +159,13 @@ const PAGE_TYPES = { * TODO: distinguish type 'preview' */ export function getCurrentPageType() { + const blobPathSelector = '#blob-path' // path next to branch switcher const blobWrapperSelector = '.repository-content .blob-wrapper table' const readmeSelector = '.repository-content .readme' const searchResultSelector = '.codesearch-results' return ( $(searchResultSelector, () => PAGE_TYPES.SEARCH) || - $(blobWrapperSelector, () => PAGE_TYPES.RAW_TEXT) || + $(blobWrapperSelector, () => $(blobPathSelector, () => PAGE_TYPES.RAW_TEXT)) || $(readmeSelector, () => PAGE_TYPES.RENDERED) || PAGE_TYPES.OTHERS ) From a0f18827991346dc45361c48a98a99899fcc2557 Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Tue, 19 Nov 2019 22:10:20 +0800 Subject: [PATCH 71/98] fix: did not listen to config changes --- src/components/SideBar.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/SideBar.tsx b/src/components/SideBar.tsx index 36d31ad..b884626 100644 --- a/src/components/SideBar.tsx +++ b/src/components/SideBar.tsx @@ -11,7 +11,7 @@ import { SideBarCore } from 'driver/core' import { ConnectorState, Props } from 'driver/core/SideBar' import { oauth } from 'env' import * as React from 'react' -import { useEffectOnce, useEvent, useLocation } from 'react-use' +import { useEvent, useLocation } from 'react-use' import { cx } from 'utils/cx' import * as DOMHelper from 'utils/DOMHelper' import { JSONRequest, parseURLSearch } from 'utils/general' @@ -66,7 +66,7 @@ const RawGitako: React.FC = function RawGitako(props) { }, [configContext.val.copyFileButton], ) - useEffectOnce(attachCopyFileButton) + React.useEffect(attachCopyFileButton, [configContext.val.copyFileButton]) useEvent('pjax:complete', attachCopyFileButton, window) const attachCopySnippetButton = React.useCallback( @@ -75,7 +75,7 @@ const RawGitako: React.FC = function RawGitako(props) { }, [configContext.val.copySnippetButton], ) - useEffectOnce(attachCopySnippetButton) + React.useEffect(attachCopySnippetButton, [configContext.val.copySnippetButton]) useEvent('pjax:complete', attachCopySnippetButton, window) // init again when setting new accessToken From 2dbf33bcee169ee8bd6471575cc5fd2388d9e57d Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Tue, 19 Nov 2019 22:21:48 +0800 Subject: [PATCH 72/98] 0.7.6 --- package.json | 2 +- src/manifest.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 4cc191f..0e7e613 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gitako", - "version": "0.7.5", + "version": "0.7.6", "description": "The missing part of GitHub.", "repository": "https://github.com/EnixCoda/Gitako", "author": "EnixCoda", diff --git a/src/manifest.json b/src/manifest.json index 13fda39..e640d1a 100644 --- a/src/manifest.json +++ b/src/manifest.json @@ -1,7 +1,7 @@ { "manifest_version": 2, "name": "Gitako - Github file tree", - "version": "0.7.5", + "version": "0.7.6", "description": "The missing part of GitHub.", "author": "EnixCoda", "icons": { From 1a7eae91c009347c79b740d9bd30a0f4c21a00d0 Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Wed, 20 Nov 2019 11:09:31 +0800 Subject: [PATCH 73/98] fix: generalized pjax container --- src/utils/DOMHelper.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/utils/DOMHelper.ts b/src/utils/DOMHelper.ts index 7d490d9..ecbf274 100644 --- a/src/utils/DOMHelper.ts +++ b/src/utils/DOMHelper.ts @@ -122,7 +122,12 @@ export function scrollToRepoContent() { const pjax = new PJAX({ elements: '', - selectors: ['.repository-content', 'title', '[data-pjax="#js-repo-pjax-container"]'], + selectors: [ + '.repository-content', + 'title', + '[data-pjax="#js-repo-pjax-container"]', + '[data-pjax-container]', + ], scrollTo: false, analytics: false, cacheBust: false, From 2f971670e8c125e3114178245d9724301a92f1a6 Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Wed, 20 Nov 2019 12:18:02 +0800 Subject: [PATCH 74/98] fix: PJAX selectors Content in personal settings page did not update --- src/utils/DOMHelper.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/DOMHelper.ts b/src/utils/DOMHelper.ts index ecbf274..9d550d9 100644 --- a/src/utils/DOMHelper.ts +++ b/src/utils/DOMHelper.ts @@ -126,7 +126,7 @@ const pjax = new PJAX({ '.repository-content', 'title', '[data-pjax="#js-repo-pjax-container"]', - '[data-pjax-container]', + '.page-content', ], scrollTo: false, analytics: false, From 2c879db9979705ab2ef3264b91770f3bb3dfcf34 Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Wed, 20 Nov 2019 12:21:50 +0800 Subject: [PATCH 75/98] 0.7.7 --- package.json | 2 +- src/manifest.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 0e7e613..af3d4d6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gitako", - "version": "0.7.6", + "version": "0.7.7", "description": "The missing part of GitHub.", "repository": "https://github.com/EnixCoda/Gitako", "author": "EnixCoda", diff --git a/src/manifest.json b/src/manifest.json index e640d1a..aa2a6e9 100644 --- a/src/manifest.json +++ b/src/manifest.json @@ -1,7 +1,7 @@ { "manifest_version": 2, "name": "Gitako - Github file tree", - "version": "0.7.6", + "version": "0.7.7", "description": "The missing part of GitHub.", "author": "EnixCoda", "icons": { From 94e6f5167805094f8ca895dd7329093fd87491c7 Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Wed, 20 Nov 2019 18:09:07 +0800 Subject: [PATCH 76/98] fix: match no PJAX links --- src/utils/DOMHelper.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/utils/DOMHelper.ts b/src/utils/DOMHelper.ts index 9d550d9..3be70e8 100644 --- a/src/utils/DOMHelper.ts +++ b/src/utils/DOMHelper.ts @@ -121,7 +121,7 @@ export function scrollToRepoContent() { } const pjax = new PJAX({ - elements: '', + elements: 'match-nothing-selector', selectors: [ '.repository-content', 'title', @@ -134,10 +134,12 @@ const pjax = new PJAX({ forceCache: true, // TODO: merge namespace, add forceCache }) -window.addEventListener('pjax:send', () => mountTopProgressBar()) +// Note: shall not enable below pjax:send listener as there would be dual bar when GitHub PJAX links are triggered +// window.addEventListener('pjax:send', () => mountTopProgressBar()) window.addEventListener('pjax:complete', () => unmountTopProgressBar()) export function loadWithPJAX(URL: string) { + mountTopProgressBar() pjax.loadUrl(URL, { scrollTo: 0 }) } From 619d18a6bb87d6f470e9fff77ade8caa90afc322 Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Wed, 20 Nov 2019 18:09:17 +0800 Subject: [PATCH 77/98] 0.7.8 --- package.json | 2 +- src/manifest.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index af3d4d6..0abce11 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gitako", - "version": "0.7.7", + "version": "0.7.8", "description": "The missing part of GitHub.", "repository": "https://github.com/EnixCoda/Gitako", "author": "EnixCoda", diff --git a/src/manifest.json b/src/manifest.json index aa2a6e9..4cfc5fe 100644 --- a/src/manifest.json +++ b/src/manifest.json @@ -1,7 +1,7 @@ { "manifest_version": 2, "name": "Gitako - Github file tree", - "version": "0.7.7", + "version": "0.7.8", "description": "The missing part of GitHub.", "author": "EnixCoda", "icons": { From 8138a0539ac814cae325e0d3ef78cbecb748b2c2 Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Thu, 21 Nov 2019 14:32:15 +0800 Subject: [PATCH 78/98] feat: overwrite header style more aggressive --- src/driver/core/SideBar.ts | 3 ++- src/utils/DOMHelper.ts | 7 +++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/driver/core/SideBar.ts b/src/driver/core/SideBar.ts index 10ef1b7..97d6a51 100644 --- a/src/driver/core/SideBar.ts +++ b/src/driver/core/SideBar.ts @@ -53,6 +53,7 @@ export const init: BoundMethodCreator = dispatch => async () => { dispatch.set({ disabled: true }) return } + DOMHelper.markGitakoReadyState(true) dispatch.set({ errorDueToAuth: false, showSettings: false, @@ -120,7 +121,6 @@ export const init: BoundMethodCreator = dispatch => async () => { const shouldShow = intelligentToggle === null ? URLHelper.isInCodePage(metaData) : intelligentToggle dispatch.call(setShouldShow, shouldShow) - DOMHelper.markGitakoReadyState() } catch (err) { dispatch.call(handleError, err) } finally { @@ -141,6 +141,7 @@ export const handleError: BoundMethodCreator<[Error]> = dispatch => async err => dispatch.set({ errorDueToAuth: true }) dispatch.call(setShowSettings, true) } else { + DOMHelper.markGitakoReadyState(false) dispatch.call(setError, 'Gitako ate a bug, but it should recovery soon!') throw err } diff --git a/src/utils/DOMHelper.ts b/src/utils/DOMHelper.ts index 3be70e8..c072c4b 100644 --- a/src/utils/DOMHelper.ts +++ b/src/utils/DOMHelper.ts @@ -13,10 +13,13 @@ NProgress.configure({ showSpinner: false }) /** * when gitako is ready, make page's header narrower + * or cancel it */ -export function markGitakoReadyState() { +export function markGitakoReadyState(ready: boolean) { const readyClassName = 'gitako-ready' - document.body.classList.add(readyClassName) + const classList = document.body.classList + if (ready) classList.add(readyClassName) + else classList.remove(readyClassName) } /** From b71a97e11f38b7147c47ab598a455d9e993ec9d4 Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Thu, 21 Nov 2019 14:36:28 +0800 Subject: [PATCH 79/98] fix: intelligent toggle --- src/components/SideBar.tsx | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/src/components/SideBar.tsx b/src/components/SideBar.tsx index ffe9643..a2966dc 100644 --- a/src/components/SideBar.tsx +++ b/src/components/SideBar.tsx @@ -50,15 +50,19 @@ const RawGitako: React.FC = function RawGitako(props) { [props.disabled, configContext.val.shortcut], ) - useOnLocationChange(() => { - if (configContext.val.intelligentToggle === null) { - props.setShouldShow( - URLHelper.isInCodePage({ - branchName: props.metaData?.branchName, - }), - ) - } - }, [props.metaData?.branchName, configContext.val.intelligentToggle]) + const updateSideBarVisibility = React.useCallback( + function updateSideBarVisibility() { + if (configContext.val.intelligentToggle === null) { + props.setShouldShow( + URLHelper.isInCodePage({ + branchName: props.metaData?.branchName, + }), + ) + } + }, + [props.metaData?.branchName, configContext.val.intelligentToggle], + ) + useEvent('pjax:complete', updateSideBarVisibility, window) const attachCopyFileButton = React.useCallback( function attachCopyFileButton() { From 94d6f9a3fc857987f157c9429a122553c996b629 Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Wed, 27 Nov 2019 10:34:31 +0800 Subject: [PATCH 80/98] 0.7.9 --- package.json | 2 +- src/manifest.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 0abce11..b09af92 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gitako", - "version": "0.7.8", + "version": "0.7.9", "description": "The missing part of GitHub.", "repository": "https://github.com/EnixCoda/Gitako", "author": "EnixCoda", diff --git a/src/manifest.json b/src/manifest.json index 4cfc5fe..3bab0e5 100644 --- a/src/manifest.json +++ b/src/manifest.json @@ -1,7 +1,7 @@ { "manifest_version": 2, "name": "Gitako - Github file tree", - "version": "0.7.8", + "version": "0.7.9", "description": "The missing part of GitHub.", "author": "EnixCoda", "icons": { From e4468b1f01a1e64a7bfb4c69d864603a88c73048 Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Wed, 27 Nov 2019 21:34:27 +0800 Subject: [PATCH 81/98] scripts: generate icon index --- scripts/generate-file-icon-index.js | 62 ++++++ scripts/generate-folder-icon-index.js | 41 ++++ scripts/language-id-ext.json | 276 ++++++++++++++++++++++++++ 3 files changed, 379 insertions(+) create mode 100644 scripts/generate-file-icon-index.js create mode 100644 scripts/generate-folder-icon-index.js create mode 100644 scripts/language-id-ext.json diff --git a/scripts/generate-file-icon-index.js b/scripts/generate-file-icon-index.js new file mode 100644 index 0000000..a3fe1b3 --- /dev/null +++ b/scripts/generate-file-icon-index.js @@ -0,0 +1,62 @@ +const languageIds = require('./language-id-ext.json') + +const records = [] + +document.body + .querySelector('table') + .querySelectorAll('tbody tr') + .forEach(tr => { + const [name, id, dark, light] = Array.from(tr.querySelectorAll('td')) + const exts = [] + const names = [] + id.innerHTML + .replace(/|<\/sub>/g, '') + .split(', ') + .map(part => { + const match = part.match(/<(\w+)>(.*?)<\/\1>/) + if (match) { + const [, tag, content] = match + if (tag === 'strong') { + // filenames in bold + names.push(content) + } else if (tag === 'code') { + // language ids in code block + const map = Object.values(languageIds).find(({ ids }) => + (Array.isArray(ids) ? ids : [ids]).includes(content), + ) + if (map && map.exts) exts.push(map.exts) + } else { + console.warn(`Found unrecognized format`, part) // unknown + } + } else if (part) { + // extensions are in regular fonts + exts.push(part.replace(/^\./, '')) + } + }) + records.push({ + name: name.innerText, + exts, + names, + icon: getSrc(dark.querySelector('img')) || getSrc(light.querySelector('img')), + }) + }) + +function getSrc(img) { + return img && img.src +} + +const prepend = 'https://github.com/vscode-icons/vscode-icons/raw/master/icons/file_type_' +const append = '.svg?sanitize=true' +const separator = ':' +const csv = records + .map(({ name, names, exts, icon }) => + [ + name, + names.join(separator), + exts.join(separator), + // icon.replace(prepend, '').replace(append, ''), // assumption: name is equal to this + ].join(','), + ) + .join('\n') + +console.log(csv) diff --git a/scripts/generate-folder-icon-index.js b/scripts/generate-folder-icon-index.js new file mode 100644 index 0000000..8ddd743 --- /dev/null +++ b/scripts/generate-folder-icon-index.js @@ -0,0 +1,41 @@ +function parsePageContent() { + const records = [] + document.body + .querySelector('table') + .querySelectorAll('tbody tr') + .forEach(tr => { + const [name, folderNames, closed, open] = Array.from(tr.querySelectorAll('td')) + const names = folderNames.innerHTML.split(', ') + records.push({ + name: name.innerText, + names, + icon: { + closed: getSrc(closed.querySelector('img')), + open: getSrc(open.querySelector('img')), + }, + }) + }) + return records +} + +function getSrc(img) { + return img && img.src +} + +function generateCSV(records) { + const prepend = 'https://github.com/vscode-icons/vscode-icons/raw/master/icons/folder_type_' + const append = '.svg?sanitize=true' + const separator = ':' + const csv = records + .map(({ name, names, icon }) => + [ + name, + names.join(separator), + // icon.replace(prepend, '').replace(append, ''), // assumption: name is equal to this + ].join(','), + ) + .join('\n') + return csv +} + +console.log(generateCSV(parsePageContent())) diff --git a/scripts/language-id-ext.json b/scripts/language-id-ext.json new file mode 100644 index 0000000..b4254a5 --- /dev/null +++ b/scripts/language-id-ext.json @@ -0,0 +1,276 @@ +{ + "actionscript": { "ids": "nextgenas", "exts": "as" }, + "ada": { "ids": "ada", "exts": "ada" }, + "advpl": { "ids": "advpl", "exts": "prw" }, + "affectscript": { "ids": "affectscript", "exts": "affect" }, + "al": { "ids": "al", "exts": "al" }, + "ansible": { "ids": "ansible", "exts": "ansible" }, + "antlr": { "ids": "antlr", "exts": "g4" }, + "anyscript": { "ids": "anyscript", "exts": "any" }, + "apache": { "ids": "apacheconf", "exts": "htaccess" }, + "apex": { "ids": "apex", "exts": "cls" }, + "apib": { "ids": "apiblueprint", "exts": "apib" }, + "apl": { "ids": "apl", "exts": "apl" }, + "applescript": { "ids": "applescript", "exts": "applescript" }, + "asciidoc": { "ids": "asciidoc", "exts": "adoc" }, + "asp": { "ids": ["asp", "asp (html)"], "exts": "asp" }, + "assembly": { "ids": ["arm", "asm"], "exts": "asm" }, + "ats": { "ids": ["ats"], "exts": "ats" }, + "autohotkey": { "ids": "ahk", "exts": "ahk" }, + "autoit": { "ids": "autoit", "exts": "au3" }, + "avro": { "ids": "avro", "exts": "avcs" }, + "azcli": { "ids": "azcli", "exts": "azcli" }, + "azurepipelines": { + "ids": "azure-pipelines", + "exts": "azure-pipelines.yml" + }, + "ballerina": { "ids": "ballerina", "exts": "bal" }, + "bat": { "ids": "bat", "exts": "bat" }, + "bazel": { "ids": "bazel", "exts": "bzl" }, + "befunge": { "ids": ["befunge", "befunge98"], "exts": "bf" }, + "bibtex": { "ids": "bibtex", "exts": "bib" }, + "biml": { "ids": "biml", "exts": "biml" }, + "blade": { "ids": ["blade", "laravel-blade"], "exts": "blade.php" }, + "bolt": { "ids": "bolt", "exts": "bolt" }, + "c": { "ids": "c", "exts": "c" }, + "c_al": { "ids": "c-al", "exts": "cal" }, + "cabal": { "ids": "cabal", "exts": "cabal" }, + "caddyfile": { "ids": "caddyfile", "exts": "Caddyfile" }, + "ceylon": { "ids": "ceylon", "exts": "ceylon" }, + "cfc": { "ids": "cfc", "exts": "cfc" }, + "cfm": { "ids": "cfmhtml", "exts": "cfm" }, + "clojure": { "ids": "clojure", "exts": "clojure" }, + "clojurescript": { "ids": "clojurescript", "exts": "clojurescript" }, + "cmake": { "ids": "cmake", "exts": "cmake" }, + "cmakecache": { "ids": "cmake-cache", "exts": "CMakeCache.txt" }, + "cobol": { "ids": "cobol", "exts": "cbl" }, + "coffeescript": { "ids": "coffeescript", "exts": "coffee" }, + "coldfusion": { "ids": ["cfml", "lang-cfml"], "exts": "cfml" }, + "confluence": { "ids": ["confluence"], "exts": "confluence" }, + "cookbook": { "ids": "cookbook", "exts": "ckbk" }, + "cpp": { "ids": "cpp", "exts": "cpp" }, + "crystal": { "ids": "crystal", "exts": "cr" }, + "csharp": { "ids": "csharp", "exts": "cs" }, + "css": { "ids": "css", "exts": "css" }, + "cucumber": { "ids": "feature", "exts": "feature" }, + "cuda": { "ids": "cuda", "exts": "cu" }, + "cython": { "ids": "cython", "exts": "pyx" }, + "dal": { "ids": "dal", "exts": "dal" }, + "dart": { "ids": "dart", "exts": "dart" }, + "django": { "ids": ["django-html", "django-txt"], "exts": "html" }, + "diff": { "ids": "diff", "exts": "diff" }, + "dlang": { "ids": ["d", "dscript", "dml", "diet"], "exts": "d" }, + "dockerfile": { "ids": "dockerfile", "exts": "dockerfile" }, + "dockerignore": { "ids": "ignore", "exts": "dockerignore" }, + "doctex": { "ids": "doctex", "exts": "dtx" }, + "dotenv": { "ids": "dotenv", "exts": "env" }, + "dotjs": { "ids": "dotjs", "exts": "dot" }, + "doxygen": { "ids": "doxygen", "exts": "dox" }, + "drools": { "ids": "drools", "exts": "drl" }, + "dylanlang": { "ids": ["dylan", "dylan-lid"], "exts": "dylan" }, + "dustjs": { "ids": "dustjs", "exts": "dust" }, + "edge": { "ids": "edge", "exts": "edge" }, + "eex": { "ids": ["eex", "html-eex"], "exts": "eex" }, + "elastic": { "ids": "es", "exts": "es" }, + "elixir": { "ids": "elixir", "exts": "ex" }, + "elm": { "ids": "elm", "exts": "elm" }, + "erb": { "ids": "erb", "exts": "erb" }, + "erlang": { "ids": "erlang", "exts": "erl" }, + "falcon": { "ids": "falcon", "exts": "falcon" }, + "fortran": { + "ids": ["fortran", "fortran-modern", "FortranFreeForm", "fortran_fixed-form"], + "exts": "f" + }, + "freemarker": { "ids": "ftl", "exts": "ftl" }, + "fsharp": { "ids": "fsharp", "exts": "fs" }, + "galen": { "ids": "galen", "exts": "gspec" }, + "gamemaker": { "ids": "gml-gms", "exts": "gml" }, + "gamemaker2": { "ids": "gml-gms2", "exts": "gml" }, + "gamemaker81": { "ids": "gml-gm81", "exts": "gml" }, + "gcode": { "ids": "gcode", "exts": "gcode" }, + "git": { "ids": ["git-commit", "git-rebase"], "exts": "git" }, + "glsl": { "ids": "glsl", "exts": "glsl" }, + "go": { "ids": "go", "exts": "go" }, + "godot": { "ids": "gdscript", "exts": "gd" }, + "graphql": { "ids": "graphql", "exts": "gql" }, + "graphviz": { "ids": "dot", "exts": "gv" }, + "groovy": { "ids": "groovy", "exts": "groovy" }, + "haml": { "ids": "haml", "exts": "haml" }, + "handlebars": { "ids": "handlebars", "exts": "hbs" }, + "harbour": { "ids": "harbour", "exts": "prg" }, + "haskell": { "ids": "haskell", "exts": "hs" }, + "haxe": { "ids": ["haxe", "hxml", "Haxe AST dump"], "exts": "haxe" }, + "helm": { "ids": "helm", "exts": "helm.tpl" }, + "hjson": { "ids": "hjson", "exts": "hjson" }, + "hlsl": { "ids": "hlsl", "exts": "hlsl" }, + "homeassistant": { "ids": "home-assistant", "exts": "yaml" }, + "hosts": { "ids": "hosts", "exts": "hosts" }, + "html": { "ids": "html", "exts": "html" }, + "http": { "ids": "http", "exts": "http" }, + "hunspell": { "ids": ["hunspell.aff", "hunspell.dic"], "exts": "aff" }, + "icl": { "ids": "icl", "exts": "icl" }, + "imba": { "ids": "imba", "exts": "imba" }, + "informix": { "ids": "4GL", "exts": "4gl" }, + "ini": { "ids": "ini", "exts": "ini" }, + "ink": { "ids": "ink", "exts": "ink" }, + "innosetup": { "ids": "innosetup", "exts": "iss" }, + "io": { "ids": "io", "exts": "io" }, + "latex": { "ids": "latex", "exts": "tex" }, + "janet": { "ids": "janet", "exts": "janet" }, + "java": { "ids": "java", "exts": "java" }, + "javascript": { "ids": "javascript", "exts": "js" }, + "javascriptreact": { "ids": "javascriptreact", "exts": "jsx" }, + "jekyll": { "ids": "jekyll", "exts": "jekyll" }, + "jenkins": { + "ids": ["jenkins", "declarative", "jenkinsfile"], + "exts": "jenkins" + }, + "jinja": { "ids": "jinja", "exts": "jinja" }, + "json": { "ids": "json", "exts": "json" }, + "jsonc": { "ids": "jsonc", "exts": "jsonc" }, + "jsonnet": { "ids": "jsonnet", "exts": "jsonnet" }, + "json5": { "ids": "json5", "exts": "json5" }, + "julia": { "ids": ["julia", "juliamarkdown"], "exts": "jl" }, + "iodine": { "ids": "iodine", "exts": "id" }, + "kivy": { "ids": "kivy", "exts": "kv" }, + "kos": { "ids": "kos", "exts": "ks" }, + "kotlin": { "ids": "kotlin", "exts": "kt" }, + "less": { "ids": "less", "exts": "less" }, + "lisp": { "ids": "lisp", "exts": "lisp" }, + "literatehaskell": { "ids": ["literate haskell"], "exts": "lhs" }, + "lolcode": { "ids": "lolcode", "exts": "lol" }, + "lsl": { "ids": "lsl", "exts": "lsl" }, + "lua": { "ids": "lua", "exts": "lua" }, + "makefile": { "ids": "makefile", "exts": "mk" }, + "markdown": { "ids": "markdown", "exts": "md" }, + "marko": { "ids": "marko", "exts": "marko" }, + "matlab": { "ids": "matlab", "exts": "mat" }, + "maxscript": { "ids": "maxscript", "exts": "ms" }, + "mediawiki": { "ids": "mediawiki", "exts": "mediawiki" }, + "mel": { "ids": "mel", "exts": "mel" }, + "meson": { "ids": "meson", "exts": "meson.build" }, + "mjml": { "ids": "mjml", "exts": "mjml" }, + "mlang": { "ids": ["mlang", "powerquerymlanguage"], "exts": "pq" }, + "mojolicious": { "ids": "mojolicious", "exts": "ep" }, + "mongo": { "ids": "mongo", "exts": "mongo" }, + "mson": { "ids": "mson", "exts": "mson" }, + "nearley": { "ids": "nearley", "exts": "ne" }, + "nim": { "ids": ["nim", "nimble"], "exts": "nim" }, + "nsis": { "ids": ["nsis", "nfl", "nsl", "bridlensis"], "exts": "nsi" }, + "nunjucks": { "ids": "nunjucks", "exts": "nunjucks" }, + "objectivec": { "ids": "objective-c", "exts": "m" }, + "objectivecpp": { "ids": "objective-cpp", "exts": "mm" }, + "ocaml": { "ids": ["ocaml", "ocamllex", "menhir"], "exts": "ml" }, + "openEdge": { "ids": "abl", "exts": "w" }, + "openHAB": { "ids": "openhab", "exts": "things" }, + "pascal": { "ids": ["pascal", "objectpascal"], "exts": "pas" }, + "pddl": { "ids": "pddl", "exts": "pddl" }, + "pddlplan": { "ids": "plan", "exts": "plan" }, + "pddlhappenings": { "ids": "happenings", "exts": "happenings" }, + "perl": { "ids": "perl", "exts": "pl" }, + "perl6": { "ids": "perl6", "exts": "pl6" }, + "pgsql": { "ids": "pgsql", "exts": "pgsql" }, + "php": { "ids": "php", "exts": "php" }, + "pine": { "ids": "pine", "exts": "pine" }, + "pip": { "ids": "pip-requirements", "exts": "requirements.txt" }, + "plaintext": { "ids": "plaintext", "exts": "txt" }, + "platformio": { + "ids": ["platformio-debug.disassembly", "platformio-debug.memoryview", "platformio-debug.asm"], + "exts": "dbgasm" + }, + "plsql": { "ids": ["plsql", "oracle"], "exts": "ddl" }, + "polymer": { "ids": "polymer", "exts": "polymer" }, + "pony": { "ids": "pony", "exts": "pony" }, + "postcss": { "ids": "postcss", "exts": "pcss" }, + "powershell": { "ids": "powershell", "exts": "ps1" }, + "prisma": { "ids": "prisma", "exts": "prisma" }, + "processinglang": { "ids": "pde", "exts": "pde" }, + "prolog": { "ids": "prolog", "exts": "pro" }, + "prometheus": { "ids": "prometheus", "exts": "rules" }, + "properties": { "ids": "properties", "exts": "properties" }, + "protobuf": { "ids": ["proto3", "proto"], "exts": "proto" }, + "pug": { "ids": "jade", "exts": "pug" }, + "puppet": { "ids": "puppet", "exts": "pp" }, + "purescript": { "ids": "purescript", "exts": "purs" }, + "pyret": { "ids": "pyret", "exts": "arr" }, + "python": { "ids": "python", "exts": "py" }, + "qlik": { "ids": "qlik", "exts": "qvs" }, + "qml": { "ids": "qml", "exts": "qml" }, + "qsharp": { "ids": "qsharp", "exts": "qs" }, + "r": { "ids": "r", "exts": "r" }, + "racket": { "ids": "racket", "exts": "rkt" }, + "razor": { "ids": ["razor", "aspnetcorerazor"], "exts": "cshtml" }, + "raml": { "ids": "raml", "exts": "raml" }, + "reason": { "ids": "reason", "exts": "re" }, + "red": { "ids": "red", "exts": "red" }, + "restructuredtext": { "ids": "restructuredtext", "exts": "rst" }, + "riot": { "ids": "riot", "exts": "tag" }, + "robot": { "ids": "robot", "exts": "robot" }, + "ruby": { "ids": "ruby", "exts": "rb" }, + "rust": { "ids": "rust", "exts": "rs" }, + "san": { "ids": "san", "exts": "san" }, + "sbt": { "ids": "sbt", "exts": "sbt" }, + "scala": { "ids": "scala", "exts": "scala" }, + "scilab": { "ids": "scilab", "exts": "sce" }, + "scss": { "ids": "scss", "exts": "scss" }, + "sdlang": { "ids": "sdl", "exts": "sdl" }, + "shaderlab": { "ids": "shaderlab", "exts": "shader" }, + "shellscript": { "ids": "shellscript", "exts": "sh" }, + "slang": { "ids": "slang", "exts": "slang" }, + "slice": { "ids": ["slice"], "exts": "ice" }, + "slim": { "ids": ["slim"], "exts": "slim" }, + "silverstripe": { "ids": "silverstripe", "exts": "ss" }, + "skipper": { "ids": ["eskip"], "exts": "eskip" }, + "smarty": { "ids": ["smarty"], "exts": "tpl" }, + "snort": { "ids": ["snort"], "exts": "snort" }, + "solidity": { "ids": ["solidity"], "exts": "sol" }, + "sqf": { "ids": "sqf", "exts": "sqf" }, + "sql": { "ids": "sql", "exts": "sql" }, + "squirrel": { "ids": "squirrel", "exts": "nut" }, + "stan": { "ids": "stan", "exts": "stan" }, + "stata": { "ids": "stata", "exts": "do" }, + "stencil": { "ids": "stencil", "exts": "stencil" }, + "stencilhtml": { "ids": "stencil-html", "exts": "html.stencil" }, + "stylable": { "ids": "stylable", "exts": "st.css" }, + "styled": { "ids": "source.css.styled", "exts": "styled" }, + "stylus": { "ids": "stylus", "exts": "styl" }, + "svelte": { "ids": "svelte", "exts": "svelte" }, + "swagger": { "ids": ["Swagger", "swagger"], "exts": "swagger" }, + "swift": { "ids": "swift", "exts": "swift" }, + "swig": { "ids": "swig", "exts": "swig" }, + "systemd": { "ids": "systemd-unit-file", "exts": "link" }, + "systemverilog": { "ids": "systemverilog", "exts": "sv" }, + "t4": { "ids": "t4", "exts": "tt" }, + "templatetoolkit": { "ids": "tt", "exts": "tt3" }, + "tera": { "ids": "tera", "exts": "tera" }, + "terraform": { "ids": "terraform", "exts": "tf" }, + "tex": { "ids": "tex", "exts": "sty" }, + "textile": { "ids": "textile", "exts": "textile" }, + "textmatejson": { "ids": "json-tmlanguage", "exts": "JSON-tmLanguage" }, + "textmateyaml": { "ids": "yaml-tmlanguage", "exts": "YAML-tmLanguage" }, + "toml": { "ids": "toml", "exts": "toml" }, + "ttcn": { "ids": "ttcn", "exts": "ttcn3" }, + "twig": { "ids": "twig", "exts": "twig" }, + "typescript": { "ids": "typescript", "exts": "ts" }, + "typescriptreact": { "ids": "typescriptreact", "exts": "tsx" }, + "typo3": { "ids": "typoscript", "exts": "typoscript" }, + "vb": { "ids": "vb", "exts": "vb" }, + "vba": { "ids": "vba", "exts": "cls" }, + "vbscript": { "ids": "vbscript", "exts": "wsf" }, + "velocity": { "ids": "velocity", "exts": "vm" }, + "verilog": { "ids": "verilog", "exts": "v" }, + "vhdl": { "ids": "vhdl", "exts": "vhdl" }, + "viml": { "ids": "viml", "exts": "vim" }, + "vlang": { "ids": "v", "exts": "v" }, + "volt": { "ids": "volt", "exts": "volt" }, + "vue": { "ids": "vue", "exts": "vue" }, + "wasm": { "ids": ["wasm", "wat"], "exts": "wasm" }, + "wolfram": { "ids": "wolfram", "exts": "wl" }, + "wurst": { "ids": ["wurstlang", "wurst"], "exts": "wurst" }, + "wxml": { "ids": "wxml", "exts": "wxml" }, + "xml": { "ids": "xml", "exts": "xml" }, + "xquery": { "ids": "xquery", "exts": "xquery" }, + "xsl": { "ids": "xsl", "exts": "xsl" }, + "yaml": { "ids": "yaml", "exts": "yaml" }, + "yang": { "ids": "yang", "exts": "yang" } +} From 4ac447be2ebec4bce35facb7ca126f19e32d6a65 Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Wed, 27 Nov 2019 21:33:33 +0800 Subject: [PATCH 82/98] feat: rich icons --- .gitignore | 1 + package.json | 1 + src/assets/icons/file-icons-index.csv | 648 ++++++++++++++++++++++++ src/assets/icons/folder-icons-index.csv | 125 +++++ src/components/Icon.tsx | 6 +- src/components/Node.tsx | 24 +- src/content.less | 15 +- src/global.d.ts | 5 + src/manifest.json | 19 +- src/utils/parseIconMapCSV.tsx | 67 +++ webpack.config.js | 8 + yarn.lock | 16 + 12 files changed, 915 insertions(+), 20 deletions(-) create mode 100644 src/assets/icons/file-icons-index.csv create mode 100644 src/assets/icons/folder-icons-index.csv create mode 100644 src/utils/parseIconMapCSV.tsx diff --git a/.gitignore b/.gitignore index 92bb6eb..c4ca72a 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ node_modules tmp dist yarn-error.log +vscode-icons diff --git a/package.json b/package.json index b09af92..42da19c 100644 --- a/package.json +++ b/package.json @@ -56,6 +56,7 @@ "json-loader": "^0.5.7", "less": "^3.9.0", "less-loader": "^4.0.5", + "raw-loader": "^4.0.0", "style-loader": "^0.23.1", "typescript": "^3.7.2", "uglifyjs-webpack-plugin": "^2.1.2", diff --git a/src/assets/icons/file-icons-index.csv b/src/assets/icons/file-icons-index.csv new file mode 100644 index 0000000..91d3c08 --- /dev/null +++ b/src/assets/icons/file-icons-index.csv @@ -0,0 +1,648 @@ +file,, +access,,accdb:accdt:mdb:accda:accdc:accde:accdp:accdr:accdu:ade:adp:laccdb:ldb:mam:maq:mdw +access2,,accdb:accdt:mdb:accda:accdc:accde:accdp:accdr:accdu:ade:adp:laccdb:ldb:mam:maq:mdw +actionscript,,as +actionscript2,,as +ada,,ada +advpl,,prw +ai,,ai +ai2,,ai +al,,al +affinitydesigner,,afdesign:affinitydesigner +affinityphoto,,afphoto:affinityphoto +affinitypublisher,,afpub:affinitypublisher +angular,.angular-cli.json:angular-cli.json:angular.json:.angular.json, +ng_component_dart,,component.dart +ng_component_ts,,component.ts +ng_component_js,,component.js +ng_controller_ts,,controller.ts +ng_controller_js,,controller.js +ng_directive_dart,,directive.dart +ng_directive_ts,,directive.ts +ng_directive_js,,directive.js +ng_guard_dart,,guard.dart +ng_guard_ts,,guard.ts +ng_guard_js,,guard.js +ng_module_dart,,module.dart +ng_module_ts,,module.ts +ng_module_js,,module.js +ng_pipe_dart,,pipe.dart +ng_pipe_ts,,pipe.ts +ng_pipe_js,,pipe.js +ng_routing_dart,,routing.dart +ng_routing_ts,,routing.ts +ng_routing_js,,routing.js +ng_routing_dart,app-routing.module.dart, +ng_routing_ts,app-routing.module.ts, +ng_routing_js,app-routing.module.js, +ng_smart_component_dart,,page.dart:container.dart +ng_smart_component_ts,,page.ts:container.ts +ng_smart_component_js,,page.js:container.js +ng_service_dart,,service.dart +ng_service_ts,,service.ts +ng_service_js,,service.js +ng_interceptor_dart,,interceptor.dart +ng_interceptor_ts,,interceptor.ts +ng_interceptor_js,,interceptor.js +ng_component_ts2,,component.ts +ng_component_js2,,component.js +ng_directive_ts2,,directive.ts +ng_directive_js2,,directive.js +ng_module_ts2,,module.ts +ng_module_js2,,module.js +ng_pipe_ts2,,pipe.ts +ng_pipe_js2,,pipe.js +ng_routing_ts2,,routing.ts +ng_routing_js2,,routing.js +ng_routing_ts2,app-routing.module.ts, +ng_routing_js2,app-routing.module.js, +ng_smart_component_ts2,,page.ts:container.ts +ng_smart_component_js2,,page.js:container.js +ng_service_ts2,,service.ts +ng_service_js2,,service.js +affectscript,,affect +ansible,,ansible +antlr,,g4 +anyscript,,any +apache,,htaccess +apex,,cls +apib,,apib +apl,,apl +applescript,,applescript +appveyor,appveyor.yml:.appveyor.yml, +arduino,,ino:pde +asciidoc,,adoc +asp,,asp:asp +aspx,,aspx:ascx +assembly,,asm:asm +ats,,ats +audio,,aac:act:aiff:amr:ape:au:dct:dss:dvf:flac:gsm:iklax:ivs:m4a:m4b:m4p:mmf:mogg:mp3:mpc:msv:oga:ogg:opus:ra:raw:tta:vox:wav:wma +aurelia,aurelia.json, +autohotkey,,ahk +autoit,,au3 +avro,,avcs +aws,, +azure,,azcli +azurepipelines,azure-pipelines.yml:.vsts-ci.yml,azure-pipelines.yml +babel,.babelrc:babelrc.js:.babelrc.js:babelrc.json:babel.config.js:.babelignore, +babel2,.babelrc:babelrc.js:.babelrc.js:babelrc.json:babel.config.js:.babelignore, +ballerina,,bal +bat,,bat +bazaar,.bzrignore, +bazel,.bazelrc:bazel.rc:bazel.bazelrc,bzl +befunge,,bf:bf +biml,,biml +binary,,a:app:bin:cmo:cmx:cma:cmxa:cmi:dll:exe:hl:ilk:lib:n:ndll:o:obj:pyc:pyd:pyo:pdb:scpt:scptd:so +bithound,.bithoundrc, +bitbucketpipeline,bitbucket-pipelines.yml, +blade,,blade.php:blade.php +bolt,,bolt +bower,.bowerrc:bower.json, +browserslist,.browserslistrc:browserslist, +buckbuild,.buckconfig, +bundler,,gemfile:gemfile.lock +bundler,gemfile:gemfile.lock, +c,,c +c2,,c +c3,,c +c_al,,cal +cabal,,cabal +caddy,,Caddyfile +cake,,cake +cakephp,, +capacitor,capacitor.config.json, +cargo,cargo.toml:cargo.lock, +cert,,csr:crt:cer:der:pfx:p12:p7b:p7r:src:crl:sst:stl +ceylon,,ceylon +cf,,lucee:cfml:cfml +cf2,,lucee:cfml:cfml +cfc,,cfc +cfc2,,cfc +cfm,,cfm +cfm2,,cfm +cheader,,h +chef,chefignore:berksfile:berksfile.lock:policyfile, +class,,class +circleci,circle.yml, +clojure,,cjm:cljc:clojure +clojurescript,,cljs:clojurescript +cloudfoundry,.cfignore, +cmake,,cmake:CMakeCache.txt +cobol,,cbl +codacy,.codacy.yml:.codacy.yaml, +codeclimate,.codeclimate.yml, +codecov,codecov.yml:.codecov.yml, +codekit,,kit +codekit,config.codekit:config.codekit2:config.codekit3:.config.codekit:.config.codekit2:.config.codekit3, +coffeelint,coffeelint.json:.coffeelintignore, +coffeescript,,coffee +conan,conanfile.txt:conanfile.py, +conda,.condarc, +config,,plist:properties:env +compass,, +composer,composer.json:composer.lock, +chef_cookbook,,ckbk +confluence,,confluence +coveralls,.coveralls.yml, +cpp,,cpp +cpp2,,cpp +cpp3,,cpp +cppheader,,hpp +crowdin,crowdin.yml, +crystal,,cr +csharp,,csx:cs +csharp2,,csx:cs +csproj,,csproj +css,,css +csscomb,.csscomb.json, +csslint,.csslintrc, +cssmap,,css.map +cucumber,,feature +cuda,,cu +cython,,pyx +cypress,cypress.json:cypress.env.json, +cvs,.cvsignore, +dal,,dal +darcs,.boringignore, +dartlang,,dart +db,,db +dependencies,dependencies.yml, +delphi,,pas:pas +django,,djt:html:html +dlang,,d:d:d:d +diff,,diff +docker,docker-compose.yml:docker-compose.ci-build.yml:docker-compose.override.yml:docker-compose.vs.debug.yml:docker-compose.vs.release.yml:docker-cloud.yml,dockerignore +docker2,docker-compose.yml:docker-compose.ci-build.yml:docker-compose.override.yml:docker-compose.vs.debug.yml:docker-compose.vs.release.yml:docker-cloud.yml,dockerignore +dockertest,docker-compose.test.yml, +dockertest2,docker-compose.test.yml, +docpad,,eco +docz,.doczrc:docz.js:docz.json:.docz.js:.docz.json:doczrc.js:doczrc.json:docz.config.js:docz.config.json, +dojo,.dojorc, +doxygen,,dox +drone,.drone.yml:.drone.yml.sig, +drools,,drl +dotjs,,dot +dustjs,,dust +dylan,,dylan:dylan +editorconfig,.editorconfig, +edge,,edge +edge2,,edge +eex,,eex:eex +ejs,,ejs +elastic,,es +elasticbeanstalk,, +elixir,,ex +elm,elm-package.json,elm +elm2,elm-package.json,elm +emacs,,el:elc +ember,.ember-cli, +ensime,,ensime +eps,,eps +erb,,erb +erlang,emakefile:.emakerfile,erl +erlang2,emakefile:.emakerfile,erl +eslint,.eslintrc:.eslintignore:.eslintcache:.eslintrc.js:.eslintrc.json:.eslintrc.yaml:.eslintrc.yml, +eslint2,.eslintrc:.eslintignore:.eslintcache:.eslintrc.js:.eslintrc.json:.eslintrc.yaml:.eslintrc.yml, +excel,,xls:xlsx:xlsm:ods:fods +excel2,,xls:xlsx:xlsm:ods:fods +falcon,,falcon +favicon,favicon.ico, +fbx,,fbx +firebase,.firebaserc, +firebasehosting,firebase.json, +firestore,firestore.rules:firestore.indexes.json, +flash,,swf:swc +fla,,fla +floobits,.flooignore, +flow,,js.flow +flow,.flowconfig, +flutter,.flutter-plugins:.metadata, +flutter_package,pubspec.lock:pubspec.yaml:.packages, +font,,woff:woff2:ttf:otf:eot:pfa:pfb:sfd +fortran,,f:f:f:f +fossa,.fossaignore, +fossil,ignore-glob, +fsharp,,fs +fsproj,,fsproj +freemarker,,ftl +fusebox,fuse.js, +galen,,gspec +galen2,,gspec +git,.gitattributes:.gitconfig:.gitignore:.gitmodules:.gitkeep:.mailmap,git:git +gamemaker,,gmx:gml +gamemaker2,,yy:yyp:gml +gamemaker81,,gml +gatsby,gatsby-config.js:gatsby-config.ts:gatsby-node.js:gatsby-node.ts:gatsby-browser.js:gatsby-browser.ts:gatsby-ssr.js:gatsby-ssr.ts, +gcode,,gcode +gitlab,.gitlab-ci.yml, +glide,glide.yml, +glsl,,glsl +go,,go +go_package,go.sum:go.mod, +godot,,gd +gradle,,gradle +graphql,.gqlconfig,gql +graphviz,,gv +greenkeeper,greenkeeper.json, +gridsome,gridsome.config.js:gridsome.config.ts:gridsome.server.js:gridsome.server.ts:gridsome.client.js:gridsome.client.ts, +groovy,,groovy +groovy2,,groovy +grunt,gruntfile.js:gruntfile.coffee:gruntfile.ts:gruntfile.babel.js:gruntfile.babel.coffee:gruntfile.babel.ts, +gulp,gulpfile.js:gulpfile.coffee:gulpfile.ts:gulpfile.babel.js:gulpfile.babel.coffee:gulpfile.babel.ts, +haml,,haml +handlebars,,hbs +handlebars2,,hbs +harbour,,prg +haskell,,hs:lhs +haskell2,,hs:lhs +haxe,haxelib.json,haxe:haxe:haxe +haxecheckstyle,checkstyle.json, +haxedevelop,,hxproj +helix,.p4ignore, +helm,,helm.tpl +hjson,,hjson +hlsl,,hlsl +homeassistant,,yaml +host,,hosts +html,,html +htmlhint,.htmlhintrc, +http,,http +hunspell,,aff:aff +husky,.huskyrc:.huskyrc.js:.huskyrc.json:.huskyrc.yaml:.huskyrc.yml, +icl,,icl +idris,,idr:lidr +idrisbin,,ibc +idrispkg,,ipkg +image,,jpeg:jpg:gif:png:bmp:tiff:ico +imba,,imba +inc,,inc:include +infopath,,infopathxml:xsn:xsf:xtp2 +informix,,4gl +ini,,ini +ink,,ink +innosetup,,iss +ionic,ionic.project:ionic.config.json, +jake,jakefile:jakefile.js, +janet,,janet +jar,,jar +java,,java +jbuilder,,jbuilder +jest,jest.config.js:jest.json:jest.config.json:.jestrc:.jestrc.js:.jestrc.json, +jest_snapshot,,js.snap:jsx.snap:ts.snap:tsx.snap +jekyll,,jekyll +jenkins,,jenkins:jenkins:jenkins +jinja,,jinja +jpm,.jpmignore, +js,,js +js_official,,js +jsbeautify,.jsbeautifyrc:jsbeautifyrc:.jsbeautify:jsbeautify, +jsconfig,jsconfig.json, +jshint,.jshintrc:.jshintignore, +jsmap,,js.map +json,,json:JSON-tmLanguage:jsonc +json_official,,json:JSON-tmLanguage:jsonc +json2,,json:JSON-tmLanguage:jsonc +jsonnet,,jsonnet +json5,,json5:json5 +jsonld,,jsonld:json-ld +jsp,,jsp +jss,,jss +julia,,jl:jl +julia2,,jl:jl +jupyter,,ipynb +io,,io +iodine,,id +karma,karma.conf.js:karma.conf.coffee:karma.conf.ts, +key,,key:pem +kite,.kiteignore, +kitchenci,.kitchen.yml, +kivy,,kv +kos,,ks +kotlin,,kt +layout,,master:layout.html:layout.htm +layout,layout.html:layout.htm, +lerna,lerna.json, +less,,less +license,,enc +license,license:licence:license.md:license.txt:licence.md:licence.txt, +lisp,,lisp +lime,,hxp +lime,include.xml, +lintstagedrc,.lintstagedrc:lint-staged.config.js:.lintstagedrc.js:.lintstagedrc.json:.lintstagedrc.yaml:.lintstagedrc.yml, +liquid,,liquid +livescript,,ls +locale,, +log,,log:tlg +lolcode,,lol +lsl,,lsl +lua,,lua +lync,,crec:ocrec +makefile,,makefile:mk +manifest,manifest, +manifest_skip,manifest.skip, +manifest_bak,manifest.bak, +map,,map +markdown,,mdown:markdown:md +markdownlint,.markdownlint.json, +marko,,marko +markojs,,marko.js +matlab,,fig:mex:mexn:mexrs6:mn:mum:mx:mx3:rwd:slx:slddc:smv:xvc:mat +maxscript,,ms +maven,maven.config:pom.xml:extensions.xml:settings.xml, +maya,,mel +mdx,,mdx +mediawiki,,mediawiki +mercurial,.hgignore, +meson,,meson.build +meteor,, +mjml,,mjml +mlang,,pq:pq +mocha,mocha.opts:.mocharc.js:.mocharc.json:.mocharc.jsonc:.mocharc.yaml:.mocharc.yml, +mojolicious,,ep +moleculer,moleculer.config.js:moleculer.config.json:moleculer.config.ts, +mongo,,mongo +monotone,.mtn-ignore, +mson,,mson +mustache,,mustache:mst +nearly,,ne +nestjs,.nest-cli.json:nest-cli.json:nestconfig.json:.nestconfig.json, +nest_adapter_js,,adapter.js +nest_adapter_ts,,adapter.ts +nest_controller_js,,controller.js +nest_controller_ts,,controller.ts +nest_decorator_js,,decorator.js +nest_decorator_ts,,decorator.ts +nest_filter_js,,filter.js +nest_filter_ts,,filter.ts +nest_gateway_js,,gateway.js +nest_gateway_ts,,gateway.ts +nest_guard_js,,guard.js +nest_guard_ts,,guard.ts +nest_interceptor_js,,interceptor.js +nest_interceptor_ts,,interceptor.ts +nest_middleware_js,,middleware.js +nest_middleware_ts,,middleware.ts +nest_module_js,,module.js +nest_module_ts,,module.ts +nest_pipe_js,,pipe.js +nest_pipe_ts,,pipe.ts +nest_service_js,,service.js +nest_service_ts,,service.ts +netlify,netlify.toml, +nginx,nginx.conf, +nim,,nim:nim +ninja,build.ninja, +njsproj,,njsproj +node,.node-version:.nvmrc, +node2,.node-version:.nvmrc, +nodemon,nodemon.json, +npm,.npmignore:.npmrc:package.json:package-lock.json:npm-shrinkwrap.json, +nsi,,nsi:nsi:nsi:nsi +nsri,.nsrirc:.nsriignore:.nsrirc.js:.nsrirc.json:.nsrirc.yaml:.nsrirc.yml:.nsrirc.config.js, +nsri-integrity,.integrity.json,,nsri-integrity +nuget,,nupkg:nuspec:psmdcp +nunjucks,,nunj:njs:nunjucks +nuxt,nuxt.config.js:nuxt.config.ts, +nyc,.nycrc:.nycrc.json, +objectivec,,m +objectivecpp,,mm +ocaml,.merlin,ml:ml:ml +onenote,,one:onepkg:onetoc:onetoc2:sig +opencl,,cl:opencl +openHAB,,things +org,,org +outlook,,pst:bcmx:otm:msg:oft +ovpn,,ovpn +package,,pkg +paket,paket.dependencies:paket.lock:paket.references:paket.template:paket.local, +patch,,patch +pcl,,pcd +pddl,,pddl +pddl_plan,,plan +pddl_happenings,,happenings +pdf,,pdf +pdf2,,pdf +perl,,pl +perl2,,pl +perl6,,pl6 +pgsql,,pgsql +photoshop,,psd +photoshop2,,psd +php,,php1:php2:php3:php4:php5:php6:phps:phpsa:phpt:phtml:phar:php +php2,,php1:php2:php3:php4:php5:php6:phps:phpsa:phpt:phtml:phar:php +php3,,php1:php2:php3:php4:php5:php6:phps:phpsa:phpt:phtml:phar:php +phpcsfixer,.php_cs:.php_cs.dist, +phpunit,phpunit:phpunit.xml:phpunit.xml.dist, +phraseapp,.phraseapp.yml, +pine,,pine +pip,pipfile:pipfile.lock,requirements.txt +platformio,platformio.ini,dbgasm:dbgasm:dbgasm +plantuml,,pu:plantuml:iuml:puml +plsql,,ddl:ddl +plsql_package,,pck +plsql_package_body,,pkb +plsql_package_header,,pkh +plsql_package_spec,,pks +poedit,,po:mo +polymer,,polymer +pony,,pony +postcss,,pcss +postcssconfig,.postcssrc:.postcssrc.json:.postcssrc.yml:.postcssrc.js:postcss.config.js, +powerpoint,,pot:potx:potm:pps:ppsx:ppsm:ppt:pptx:pptm:pa:ppa:ppam:sldm:sldx +powerpoint2,,pot:potx:potm:pps:ppsx:ppsm:ppt:pptx:pptm:pa:ppa:ppam:sldm:sldx +powershell,,ps1 +powershell_psm,,psm1 +powershell_psd,,psd1 +powershell_format,,format.ps1xml +powershell_types,,types.ps1xml +powershell2,,ps1 +powershell_psm2,,psm1 +powershell_psd2,,psd1 +precommit,.pre-commit-config.yaml, +prettier,.prettierrc:.prettierignore, +prettier,prettier.config.js:prettier.config.ts:prettier.config.coffee, +prettier,.prettierrc.js:.prettierrc.json:.prettierrc.yml:.prettierrc.yaml, +prisma,,prisma +processinglang,,pde +procfile,procfile, +progress,,w +prolog,,pro:P:pro +prometheus,,rules +protobuf,,proto:proto +protractor,protractor.conf.js:protractor.conf.coffee:protractor.conf.ts, +publisher,,pub:puz +puppet,,pp +pug,.jade-lintrc:.pug-lintrc:.jade-lint.json:.pug-lintrc.js:.pug-lintrc.json,pug +purescript,,purs +pyret,,arr +python,,py +pyup,.pyup:.pyup.yml, +q,,q +qbs,,qbs +qlikview,,qvd:qvw:qvs +qml,,qml +qmldir,qmldir, +qsharp,,qs +quasar,quasar.conf.js, +r,,r +racket,,rkt +rails,, +rake,,rake +rake,rakefile, +raml,,raml +razor,,cshtml:cshtml +razzle,razzle.config.js, +reactjs,,jsx +reacttemplate,,rt +reactts,,tsx +reason,,re +red,,red +registry,,reg +rehype,.rehyperc:.rehypeignore:.rehyperc.js:.rehyperc.json:.rehyperc.yml:.rehyperc.yaml, +remark,.remarkrc:.remarkignore:.remarkrc.js:.remarkrc.json:.remarkrc.yml:.remarkrc.yaml, +renovate,.renovaterc:renovate.json:.renovaterc.json, +rest,,rst +retext,.retextrc:.retextignore:.retextrc.js:.retextrc.json:.retextrc.yml:.retextrc.yaml, +riot,,tag +robotframework,,robot +robots,robots.txt, +rollup,rollup.config.js:rollup.config.mjs:rollup.config.coffee:rollup.config.ts:rollup.config.common.js:rollup.config.common.mjs:rollup.config.common.coffee:rollup.config.common.ts:rollup.config.dev.js:rollup.config.dev.mjs:rollup.config.dev.coffee:rollup.config.dev.ts:rollup.config.prod.js:rollup.config.prod.mjs:rollup.config.prod.coffee:rollup.config.prod.ts, +rproj,,rproj +rspec,.rspec, +rubocop,.rubocop.yml:.rubocop_todo.yml, +ruby,,rb +rust,,rs +saltstack,,sls +san,,san +sass,,sass +sbt,,sbt +scala,,scala +script,,wsf +scss,,scssm:scss +scilab,,sce +sdlang,,sdl +sentry,.sentryclirc, +serverless,serverless.yml, +sequelize,.sequelizerc:.sequelizerc.js:.sequelizerc.json, +shaderlab,,shader +shell,,fish:sh +sketch,,sketch +slang,,slang +slice,,ice +slim,,slim +sln,,sln +sln2,,sln +silverstripe,,ss +skipper,,eskip:eskip +smarty,,tpl +snapcraft,snapcraft.yaml, +snort,,snort +snyk,.snyk, +solidarity,.solidarity:.solidarity.json, +solidity,,sol +source,, +sqf,,sqf +sql,,sql +sqlite,,sqlite:sqlite3:db3 +squirrel,,nut +sss,,sss +stan,,stan +stata,,dta:do +stencil,,stencil:html.stencil +style,, +stylelint,.stylelintrc:.stylelintignore:.stylelintcache:stylelint.config.js:stylelint.config.json:stylelint.config.yaml:stylelint.config.yml:stylelint.config.ts:.stylelintrc.js:.stylelintrc.json:.stylelintrc.yaml:.stylelintrc.yml:.stylelintrc.ts, +stylable,,st.css +styled,,styled +stylus,,styl +storyboard,,storyboard +storybook,,story.js:story.jsx:story.ts:story.tsx:stories.js:stories.jsx:stories.ts:stories.tsx +subversion,.svnignore, +svelte,,svelte +svg,,svg +swagger,,swagger:swagger +swift,package.pins,swift +swig,,swig +symfony,symfony.lock, +systemd,,link +systemverilog,,sv +t4tt,,tt +tailwind,tailwind.js:tailwind.coffee:tailwind.ts:tailwind.config.js:tailwind.config.coffee:tailwind.config.ts, +tt,,tt2:tt3 +tcl,,tcl:exp +tera,,tera +terraform,,tfstate:tf +test,,tst +testjs,,test.js:test.jsx:test.mjs:spec.js:spec.jsx:spec.mjs +testts,,test.ts:test.tsx:spec.ts:spec.tsx:e2e-test.ts:e2e-test.tsx:e2e-spec.ts:e2e-spec.tsx +tex,,texi:tikz:sty:tex:bib:dtx +text,,csv:txt +textile,,textile +tfs,.tfignore, +todo,,todo +toml,,toml +tox,.ini, +travis,.travis.yml, +tsconfig,tsconfig.json:tsconfig.app.json:tsconfig.spec.json:tsconfig.e2e.json:tsconfig.base.json:tsconfig.common.json:tsconfig.dev.json:tsconfig.development.json:tsconfig.staging.json:tsconfig.test.json:tsconfig.prod.json:tsconfig.production.json, +tslint,tslint.json:tslint.yaml:tslint.yml, +ttcn,,ttcn3 +twig,,twig +typescript,,ts +typescript_official,,ts +typescriptdef,,d.ts +typescriptdef_official,,d.ts +typo3,,typoscript +unibeautify,.unibeautifyrc:unibeautify.config.js:.unibeautifyrc.js:.unibeautifyrc.json:.unibeautifyrc.yaml:.unibeautifyrc.yml, +vagrant,vagrantfile, +vala,,vala +vapi,,vapi +vash,,vash +vb,,vb +vba,,cls +vbhtml,,vbhtml +vbproj,,vbproj +vcxproj,,vcxproj +velocity,,vm +verilog,,v +vhdl,,vhdl +video,,3g2:3gp:asf:amv:avi:divx:qt:f4a:f4b:f4p:f4v:flv:m2v:m4v:mkv:mk3d:mov:mp2:mp4:mpe:mpeg:mpeg2:mpg:mpv:nsv:ogv:rm:rmvb:svi:vob:webm:wmv +view,, +vim,.vimrc:.gvimrc,vim +vlang,,v +volt,,volt +vscode,.vscodeignore:launch.json:tasks.json:vscodeignore.json, +vscode2,.vscodeignore:launch.json:tasks.json:vscodeignore.json, +vscode3,.vscodeignore:launch.json:tasks.json:vscodeignore.json, +vscode-insiders,.vscodeignore:launch.json:tasks.json:vscodeignore.json,,vscode-insiders +vsix,,vsix +vsixmanifest,,vsixmanifest +vue,,vue +vueconfig,.vuerc:vue.config.js, +wallaby,wallaby.json:wallaby.js:wallaby.ts:wallaby.coffee:wallaby.conf.json:wallaby.conf.js:wallaby.conf.ts:wallaby.conf.coffee:.wallaby.json:.wallaby.js:.wallaby.ts:.wallaby.coffee:.wallaby.conf.json:.wallaby.conf.js:.wallaby.conf.ts:.wallaby.conf.coffee, +watchmanconfig,.watchmanconfig, +wasm,,wasm:wasm:wasm +webp,,webp +webpack,webpack.base.conf.js:webpack.base.conf.coffee:webpack.base.conf.ts:webpack.common.js:webpack.common.coffee:webpack.common.ts:webpack.config.js:webpack.config.coffee:webpack.config.ts:webpack.config.base.js:webpack.config.base.coffee:webpack.config.base.ts:webpack.config.common.js:webpack.config.common.coffee:webpack.config.common.ts:webpack.config.dev.js:webpack.config.dev.coffee:webpack.config.dev.ts:webpack.config.development.js:webpack.config.development.coffee:webpack.config.development.ts:webpack.config.staging.js:webpack.config.staging.coffee:webpack.config.staging.ts:webpack.config.test.js:webpack.config.test.coffee:webpack.config.test.ts:webpack.config.prod.js:webpack.config.prod.coffee:webpack.config.prod.ts:webpack.config.production.js:webpack.config.production.coffee:webpack.config.production.ts:webpack.config.babel.js:webpack.config.babel.coffee:webpack.config.babel.ts:webpack.config.base.babel.js:webpack.config.base.babel.coffee:webpack.config.base.babel.ts:webpack.config.common.babel.js:webpack.config.common.babel.coffee:webpack.config.common.babel.ts:webpack.config.dev.babel.js:webpack.config.dev.babel.coffee:webpack.config.dev.babel.ts:webpack.config.development.babel.js:webpack.config.development.babel.coffee:webpack.config.development.babel.ts:webpack.config.staging.babel.js:webpack.config.staging.babel.coffee:webpack.config.staging.babel.ts:webpack.config.test.babel.js:webpack.config.test.babel.coffee:webpack.config.test.babel.ts:webpack.config.prod.babel.js:webpack.config.prod.babel.coffee:webpack.config.prod.babel.ts:webpack.config.production.babel.js:webpack.config.production.babel.coffee:webpack.config.production.babel.ts:webpack.dev.js:webpack.dev.coffee:webpack.dev.ts:webpack.dev.conf.js:webpack.dev.conf.coffee:webpack.dev.conf.ts:webpack.prod.js:webpack.prod.coffee:webpack.prod.ts:webpack.prod.conf.js:webpack.prod.conf.coffee:webpack.prod.conf.ts:webpack.mix.js:webpack.mix.coffee:webpack.mix.ts:webpack.test.conf.js:webpack.test.conf.coffee:webpack.test.conf.ts, +wercker,wercker.yml, +wolfram,,wl +word,,doc:docx:docm:dot:dotx:dotm:wll +word2,,doc:docx:docm:dot:dotx:dotm:wll +wpml,wpml-config.xml, +wurst,,wurst:wurst +wxml,,wxml +wxss,,wxss +xcode,,xcodeproj +xfl,,xfl +xib,,xib +xliff,,xliff:xlf +xml,,pex:tmlanguage:xml +xquery,,xquery +xsl,,xsl +yaml,,yaml:YAML-tmLanguage +yamllint,.yamllint, +yandex,.yaspellerrc:.yaspeller.json, +yang,,yang +yarn,yarn.lock:.yarnrc:.yarnclean:.yarn-integrity:.yarn-metadata.json:.yarnignore, +yeoman,.yo-rc.json, +zeit,now.json:.nowignore, +zip,,zip:rar:7z:tar:gz:bzip2:xz:bz2 +zip2,,zip:rar:7z:tar:gz:bzip2:xz:bz2 diff --git a/src/assets/icons/folder-icons-index.csv b/src/assets/icons/folder-icons-index.csv new file mode 100644 index 0000000..234657c --- /dev/null +++ b/src/assets/icons/folder-icons-index.csv @@ -0,0 +1,125 @@ +folder, +root_folder, +android,android +api,api:.api +app,app:.app +arangodb,arangodb:arango +asset,assets:.assets +aurelia,aurelia_project +audio,audio:.audio:audios:.audios:sound:.sound:sounds:.sounds +aws,aws:.aws +azure,azure:.azure +azurepipelines,azure-pipelines:.azure-pipelines +binary,bin:.bin +blueprint,blueprint:.blueprint:blueprints:.blueprints +bower,bower_components +buildkite,.buildkite +cake,cake:.cake +certificate,certificates:.certificates:certs:certs. +chef,chef:.chef +circleci,.circleci +controller,controllers:.controllers:handlers:.handlers +component,components:.components:widgets +composer,composer:.composer +cli,cli:cmd:command:commands:commandline:console +client,client +cmake,.cmake:cmake +config,config:.config:configs:.configs:configuration:.configuration:configurations:.configurations:setting:.setting:settings:.settings:ini:.ini:initializers:.initializers +coverage,coverage +css,css:_css +cypress,cypress +db,db:database:sql:data:repo:repository:repositories +debian,debian +dist,dist:dists:out:outs:export:exports:build:builds:release:releases:target:targets +docker,docker:.docker +docs,docs:doc +e2e,e2e +elasticbeanstalk,.elasticbeanstalk:.ebextensions +electron,electron +favicon,favicon:favicons +flow,flow:flow-typed +fonts,fonts:font:fnt +gcp,gcp:.gcp +git,.git:submodules:.submodules +github,.github +gitlab,.gitlab +gradle,gradle:.gradle +graphql,graphql +grunt,grunt +gulp,gulp:gulpfile.js:gulpfile.coffee:gulpfile.ts:gulpfile.babel.js:gulpfile.babel.coffee:gulpfile.babel.ts +haxelib,.haxelib:haxe_libraries +helper,helpers:.helpers +idea,.idea +images,images:image:img:icons:icon:ico:screenshot:screenshots:svg +include,include:includes:incl:inc:.include:.includes:.incl:.inc:_include:_includes:_incl:_inc +interfaces,interfaces +ios,ios +js,js +json,json +json_official,json +kubernetes,kubernetes:k8s:kube:kuber:.kubernetes:.k8s:.kube:.kuber +less,less:_less +library,lib:.lib:library +linux,linux +locale,lang:language:languages:locale:locales:_locale:_locales:internationalization:globalization:localization:i18n:g11n:l10n +log,log:logs +macos,macos:darwin +mariadb,mariadb:maria +maven,.mvn +memcached,memcached:.memcached +middleware,middleware +mjml,mjml:.mjml +minikube,minikube:minik8s:minikuber +mock,mocks:.mocks:__mocks__ +model,models:.models:entities:.entities +module,modules +mongodb,mongodb:mongo +mysql,mysqldb:mysql +nginx,nginx:conf.d +node,node_modules +notification,notification:notifications:event:events +nuget,.nuget +package,package:packages:.package:.packages:pkg +paket,.paket +php,php +platformio,.pio:.pioenvs +plugin,plugin:.plugin:plugins:.plugins:extension:.extension:extensions:.extensions +private,private:.private +public,public:.public +python,.venv:.virtualenv +redis,redis +ravendb,ravendb +route,route:routes:_route:_routes:routers +redux,redux +meteor,.meteor +sass,sass:scss:_sass:_scss +script,script:scripts +server,server +services,services +src,src:source:sources +sso,sso +story,stories:__stories__ +style,style:styles +test,tests:.tests:test:.test:__tests__:__test__:spec:.spec:specs:.specs +temp,temp:.temp:tmp:.tmp +template,template:.template:templates:.templates +theme,theme:themes +travis,.travis +tools,tools:.tools:util:utils +typescript,typescript:ts +typings,typings:@types +typings2,typings:@types +vagrant,vagrant:.vagrant +video,video:.video:videos:.videos +view,html:view:views:layout:layouts:page:pages:_view:_views:_layout:_layouts:_page:_pages +vs,.vs +vs2,.vs +vscode,.vscode:vscode +vscode2,.vscode:vscode +vscode3,.vscode:vscode +vscode_test,.vscode-test +vscode_test2,.vscode-test +vscode_test3,.vscode-test +webpack,webpack +windows,windows:win32 +www,www:wwwroot diff --git a/src/components/Icon.tsx b/src/components/Icon.tsx index 5ffd0e4..71499e5 100644 --- a/src/components/Icon.tsx +++ b/src/components/Icon.tsx @@ -1,5 +1,6 @@ import Octicon, { ChevronDown, + ChevronRight, File, FileCode, FileMedia, @@ -12,7 +13,6 @@ import Octicon, { Markdown, Octoface, Reply, - TriangleRight, X, } from '@primer/octicons-react' import * as React from 'react' @@ -57,8 +57,8 @@ function getSVGIconComponent( } case 'folder': return { - IconComponent: TriangleRight, - name: 'TriangleRight', + IconComponent: ChevronRight, + name: 'ChevronRight', } case 'go-to': return { diff --git a/src/components/Node.tsx b/src/components/Node.tsx index 943e9bc..f978e1b 100644 --- a/src/components/Node.tsx +++ b/src/components/Node.tsx @@ -1,8 +1,9 @@ -import { Icon } from 'components/Icon' import * as React from 'react' import { cx } from 'utils/cx' import { OperatingSystems, os } from 'utils/general' import { TreeNode } from 'utils/VisibleNodesGenerator' +import { getFileIconSrc, getFolderIconSrc } from '../utils/parseIconMapCSV' +import { Icon } from './Icon' function getIconType(node: TreeNode) { switch (node.type) { @@ -54,7 +55,7 @@ export function Node({ node, depth, expanded, focused, renderActions, style, onC style={{ paddingLeft: `${10 + 20 * depth}px` }} >
- + {name}
{renderActions &&
{renderActions(node)}
} @@ -63,3 +64,22 @@ export function Node({ node, depth, expanded, focused, renderActions, style, onC
) } + +const NodeItemIcon = React.memo(function NodeItemIcon({ + node, + open = false, +}: { + node: TreeNode + open?: boolean +}) { + const src = React.useMemo( + () => (node.type === 'tree' ? getFolderIconSrc(node, open) : getFileIconSrc(node)), + [open], + ) + return ( + <> + {node.type !== 'blob' && } + {node.name} + + ) +}) diff --git a/src/content.less b/src/content.less index dbea7de..ec998e8 100644 --- a/src/content.less +++ b/src/content.less @@ -358,8 +358,21 @@ transition: all 0.5s ease; white-space: nowrap; + &-icon { + width: 16px; + height: 16px; + object-fit: contain; + vertical-align: middle; + padding-left: 6px; + box-sizing: content-box; + + &.dim { + filter: sepia(1) hue-rotate(180deg); + } + } + // folder icon rotate when expand - &.expanded .octicon.TriangleRight { + &.expanded .octicon.ChevronRight { transform: rotate(90deg); } diff --git a/src/global.d.ts b/src/global.d.ts index fac9ed3..4c6539f 100644 --- a/src/global.d.ts +++ b/src/global.d.ts @@ -7,3 +7,8 @@ type PartialValSet = { val: T set: (val: Partial) => void } + +declare module '*.csv' { + const content: string + export default content +} diff --git a/src/manifest.json b/src/manifest.json index 3bab0e5..965775f 100644 --- a/src/manifest.json +++ b/src/manifest.json @@ -8,21 +8,12 @@ "128": "icons/Gitako-128x128.png" }, "homepage_url": "https://github.com/EnixCoda/Gitako", - "permissions": [ - "storage", - "*://*.github.com/*", - "*://*.sentry.io/*" - ], + "permissions": ["storage", "*://*.github.com/*", "*://*.sentry.io/*"], + "web_accessible_resources": ["icons/vscode/*"], "content_scripts": [ { - "matches": [ - "https://github.com/*" - ], - "js": [ - "firefox-shim.js", - "browser-polyfill.js", - "content.js" - ] + "matches": ["https://github.com/*"], + "js": ["firefox-shim.js", "browser-polyfill.js", "content.js"] } ] -} \ No newline at end of file +} diff --git a/src/utils/parseIconMapCSV.tsx b/src/utils/parseIconMapCSV.tsx new file mode 100644 index 0000000..28ce618 --- /dev/null +++ b/src/utils/parseIconMapCSV.tsx @@ -0,0 +1,67 @@ +import rawFileIconIndex from 'assets/icons/file-icons-index.csv' +import rawFolderIconIndex from 'assets/icons/folder-icons-index.csv' +import { TreeNode } from 'utils/VisibleNodesGenerator' + +function parseFileIconMapCSV() { + const filenameIndex = new Map() + const fileExtensionIndex = new Map() + rawFileIconIndex.split('\n').forEach(line => { + if (!line) return + const [name, names, exts] = line.split(',') + if (names) { + names.split(':').forEach(filename => { + if (!filename) return + filenameIndex.set(filename, name) + }) + } + if (exts) { + exts.split(':').forEach(ext => { + if (!ext) return + fileExtensionIndex.set(ext, name) + }) + } + }) + return { + filenameIndex, + fileExtensionIndex, + } +} + +function parseFolderIconMapCSV() { + const folderNameIndex = new Map() + rawFolderIconIndex.split('\n').forEach(line => { + if (!line) return + const [name, names] = line.split(',') + names.split(':').forEach(folderName => { + if (!folderName) return + folderNameIndex.set(folderName, name) + }) + }) + return { + folderNameIndex, + } +} + +const { folderNameIndex } = parseFolderIconMapCSV() + +export function getFolderIconSrc(node: TreeNode, open: boolean) { + const name = folderNameIndex.get(node.name.toLowerCase()) + return getIconSrc('folder', name, open) +} + +const { filenameIndex, fileExtensionIndex } = parseFileIconMapCSV() + +export function getFileIconSrc(node: TreeNode) { + const name = + filenameIndex.get(node.name.toLowerCase()) || + fileExtensionIndex.get((node.name.split('.').pop() as string).toLowerCase()) + return getIconSrc('file', name) +} + +export function getIconSrc(type: 'folder' | 'file', name: string = 'default', open?: boolean) { + const filename = + (name === 'default' ? 'default_' + type : type + '_type_' + name) + + (open ? '_opened' : '') + + '.svg' + return browser.runtime.getURL(`icons/vscode/${filename}`) +} diff --git a/webpack.config.js b/webpack.config.js index a3d34fd..bd7bdb0 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -18,6 +18,10 @@ const plugins = [ from: './src/assets/icons/*', to: 'icons/[name].[ext]', }, + { + from: './vscode-icons/icons/*', + to: 'icons/vscode/[name].[ext]', + }, { from: 'node_modules/webextension-polyfill/dist/browser-polyfill.js', to: 'browser-polyfill.js', @@ -79,6 +83,10 @@ module.exports = { resourceQuery: /inline/, loader: ['url-loader'], }, + { + test: /\.csv$/, + loader: ['raw-loader'], + }, { test: /\.json$/, loader: ['json-loader'], diff --git a/yarn.lock b/yarn.lock index 6e4f4f7..c32608c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6450,6 +6450,14 @@ raw-body@2.4.0: iconv-lite "0.4.24" unpipe "1.0.0" +raw-loader@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/raw-loader/-/raw-loader-4.0.0.tgz#d639c40fb9d72b5c7f8abc1fb2ddb25b29d3d540" + integrity sha512-iINUOYvl1cGEmfoaLjnZXt4bKfT2LJnZZib5N/LLyAphC+Dd11vNP9CNVb38j+SAJpFI1uo8j9frmih53ASy7Q== + dependencies: + loader-utils "^1.2.3" + schema-utils "^2.5.0" + rc@^1.2.7, rc@^1.2.8: version "1.2.8" resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" @@ -6948,6 +6956,14 @@ schema-utils@^1.0.0: ajv-errors "^1.0.0" ajv-keywords "^3.1.0" +schema-utils@^2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-2.5.0.tgz#8f254f618d402cc80257486213c8970edfd7c22f" + integrity sha512-32ISrwW2scPXHUSusP8qMg5dLUawKkyV+/qIEV9JdXKx+rsM6mi8vZY8khg2M69Qom16rtroWXD3Ybtiws38gQ== + dependencies: + ajv "^6.10.2" + ajv-keywords "^3.4.1" + screenfull@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/screenfull/-/screenfull-5.0.0.tgz#5c2010c0e84fd4157bf852877698f90b8cbe96f6" From 255aa7eb1d7ba31da913c497f790b1c699b37524 Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Sun, 1 Dec 2019 21:39:09 +0800 Subject: [PATCH 83/98] refactor: align item with icon --- src/components/Icon.tsx | 5 ++++- src/components/Node.tsx | 4 ++-- src/content.less | 2 +- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/components/Icon.tsx b/src/components/Icon.tsx index 71499e5..eaaf357 100644 --- a/src/components/Icon.tsx +++ b/src/components/Icon.tsx @@ -120,10 +120,12 @@ function getSVGIconComponent( type Props = { type: string className?: string + placeholder?: boolean onClick?: (event: React.MouseEvent) => void } -export function Icon({ type, className = undefined, ...otherProps }: Props) { +export function Icon({ type, className = undefined, placeholder, ...otherProps }: Props) { + if (placeholder) return
const { name, IconComponent } = getSVGIconComponent(type) const mergedClassName = cx('octicon', name) return ( @@ -131,6 +133,7 @@ export function Icon({ type, className = undefined, ...otherProps }: Props) { {React.createElement(Octicon, { icon: IconComponent, className: mergedClassName, + verticalAlign: 'middle', })}
) diff --git a/src/components/Node.tsx b/src/components/Node.tsx index f978e1b..d429ddf 100644 --- a/src/components/Node.tsx +++ b/src/components/Node.tsx @@ -78,8 +78,8 @@ const NodeItemIcon = React.memo(function NodeItemIcon({ ) return ( <> - {node.type !== 'blob' && } - {node.name} + + {node.type !== 'commit' && {node.name}} ) }) diff --git a/src/content.less b/src/content.less index ec998e8..6e34e52 100644 --- a/src/content.less +++ b/src/content.less @@ -363,7 +363,7 @@ height: 16px; object-fit: contain; vertical-align: middle; - padding-left: 6px; + padding-left: 4px; box-sizing: content-box; &.dim { From eab2790d038c90cc27ac50ace9173e90a369b68e Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Sun, 1 Dec 2019 21:59:54 +0800 Subject: [PATCH 84/98] refactor: destruct settings bar --- src/components/SettingsBar.tsx | 161 +++--------------- .../{MoreOption.tsx => SimpleToggleField.tsx} | 17 +- .../settings/AccessTokenSettings.tsx | 103 +++++++++++ src/components/settings/ShortcutSettings.tsx | 56 ++++++ 4 files changed, 183 insertions(+), 154 deletions(-) rename src/components/{MoreOption.tsx => SimpleToggleField.tsx} (73%) create mode 100644 src/components/settings/AccessTokenSettings.tsx create mode 100644 src/components/settings/ShortcutSettings.tsx diff --git a/src/components/SettingsBar.tsx b/src/components/SettingsBar.tsx index b9f461e..2c365b8 100644 --- a/src/components/SettingsBar.tsx +++ b/src/components/SettingsBar.tsx @@ -1,14 +1,14 @@ import { Icon } from 'components/Icon' -import { useConfigs } from 'containers/ConfigsContext' -import { oauth, VERSION } from 'env' +import { VERSION } from 'env' import * as React from 'react' -import { friendlyFormatShortcut } from 'utils/general' +import { Config } from 'utils/configHelper' import { useStates } from 'utils/hooks' -import * as keyHelper from 'utils/keyHelper' -import { SimpleField, SimpleFieldInput } from './MoreOption' +import { AccessTokenSettings } from './settings/AccessTokenSettings' +import { ShortcutSettings } from './settings/ShortcutSettings' +import { SimpleToggleField } from './SimpleToggleField' const WIKI_HOME_LINK = 'https://github.com/EnixCoda/Gitako/wiki' -const wikiLinks = { +export const wikiLinks = { compressSingletonFolder: `${WIKI_HOME_LINK}/Compress-Singleton-Folder`, changeLog: `${WIKI_HOME_LINK}/Change-Log`, copyFileButton: `${WIKI_HOME_LINK}/Copy-file-and-snippet`, @@ -16,13 +16,22 @@ const wikiLinks = { createAccessToken: `${WIKI_HOME_LINK}/How-to-create-access-token-for-Gitako%3F`, } -const ACCESS_TOKEN_REGEXP = /^[0-9a-f]{40}$/ - type Props = { activated: boolean toggleShowSettings: () => void } +export type SimpleField = { + key: keyof Config + label: string + wikiLink?: string + description?: string + overwrite?: { + value: (value: T) => boolean + onChange: (checked: boolean) => any + } +} + const moreFields: SimpleField[] = [ { key: 'compressSingletonFolder', @@ -51,149 +60,21 @@ const moreFields: SimpleField[] = [ ] function SettingsBarContent() { - const configContext = useConfigs() - const hasAccessToken = Boolean(configContext.val.access_token) - const useAccessToken = useStates('') - const useAccessTokenHint = useStates('') - const useShortcutHint = useStates('') - const useToggleShowSideBarShortcut = useStates(configContext.val.shortcut) const useReloadHint = useStates('') - - const { val: accessTokenHint } = useAccessTokenHint - const { val: toggleShowSideBarShortcut } = useToggleShowSideBarShortcut - const { val: shortcutHint } = useShortcutHint - const { val: accessToken } = useAccessToken const { val: reloadHint } = useReloadHint - React.useEffect(() => { - useToggleShowSideBarShortcut.set(configContext.val.shortcut) - }, [configContext.val.shortcut]) - - React.useEffect(() => { - // clear input when access token updates - useAccessToken.set('') - }, [configContext.val.access_token]) - - const onInputAccessToken = React.useCallback( - ({ currentTarget: { value } }: React.FormEvent) => { - useAccessToken.set(value) - useAccessTokenHint.set( - ACCESS_TOKEN_REGEXP.test(value) ? '' : 'This token is in unknown format.', - ) - }, - [], - ) - - const onPressAccessToken = React.useCallback(({ key }: React.KeyboardEvent) => { - if (key === 'Enter') saveToken() - }, []) - - const saveToken = React.useCallback( - async (hint?: typeof useAccessTokenHint.val) => { - if (accessToken) { - configContext.set({ access_token: accessToken }) - useAccessToken.set('') - useAccessTokenHint.set( - hint || ( - -
window.location.reload()}> - Reload - {' '} - to activate! - - ), - ) - } - }, - [accessToken], - ) - - const saveShortcut = React.useCallback(async () => { - const { val: toggleShowSideBarShortcut } = useToggleShowSideBarShortcut - configContext.set({ shortcut: toggleShowSideBarShortcut }) - if (typeof toggleShowSideBarShortcut === 'string') { - useShortcutHint.set('Shortcut is saved!') - } - }, [useToggleShowSideBarShortcut.val]) - - const onShortCutInputKeyDown = React.useCallback((e: React.KeyboardEvent) => { - e.preventDefault() - e.stopPropagation() - // Clear shortcut with backspace - const shortcut = e.key === 'Backspace' ? '' : keyHelper.parseEvent(e) - useToggleShowSideBarShortcut.set(shortcut) - }, []) - return ( <>

Settings

-
-

- Access Token{' '} - - (?) - -

- {!hasAccessToken && ( - { - // use js here to make sure redirect_uri is latest url - const url = `https://github.com/login/oauth/authorize?client_id=${ - oauth.clientId - }&scope=repo&redirect_uri=${encodeURIComponent(window.location.href)}` - window.location.href = url - }} - > - Create with OAuth (recommended) - - )} -
- - {hasAccessToken && !accessToken ? ( - - ) : ( - - )} -
- {accessTokenHint && {accessTokenHint}} -
-
-

Toggle Shortcut

- Set a combination of keys for toggling Gitako sidebar. -
-
- - -
- {shortcutHint && {shortcutHint}} -
+ +
-

More Options

+

More

{moreFields.map(field => ( - +
))} diff --git a/src/components/MoreOption.tsx b/src/components/SimpleToggleField.tsx similarity index 73% rename from src/components/MoreOption.tsx rename to src/components/SimpleToggleField.tsx index 65a9d66..62aeeed 100644 --- a/src/components/MoreOption.tsx +++ b/src/components/SimpleToggleField.tsx @@ -1,25 +1,14 @@ import { useConfigs } from 'containers/ConfigsContext' import * as React from 'react' -import { Config } from 'utils/configHelper' - -export type SimpleField = { - key: keyof Config - label: string - wikiLink?: string - description?: string - overwrite?: Props['overwrite'] -} +import { SimpleField } from './SettingsBar' type Props = { field: SimpleField onChange?(): void - overwrite?: { - value: (value: T) => boolean - onChange: (checked: boolean) => any - } } -export function SimpleFieldInput({ field, overwrite, onChange }: Props) { +export function SimpleToggleField({ field, onChange }: Props) { + const { overwrite } = field const configContext = useConfigs() const value = configContext.val[field.key] return ( diff --git a/src/components/settings/AccessTokenSettings.tsx b/src/components/settings/AccessTokenSettings.tsx new file mode 100644 index 0000000..8aa9c2b --- /dev/null +++ b/src/components/settings/AccessTokenSettings.tsx @@ -0,0 +1,103 @@ +import { wikiLinks } from 'components/SettingsBar' +import { useConfigs } from 'containers/ConfigsContext' +import { oauth } from 'env' +import * as React from 'react' +import { useStates } from 'utils/hooks' + +const ACCESS_TOKEN_REGEXP = /^[0-9a-f]{40}$/ + +type Props = {} + +export function AccessTokenSettings(props: React.PropsWithChildren) { + const configContext = useConfigs() + const hasAccessToken = Boolean(configContext.val.access_token) + const useAccessToken = useStates('') + const useAccessTokenHint = useStates('') + + const { val: accessTokenHint } = useAccessTokenHint + const { val: accessToken } = useAccessToken + + React.useEffect(() => { + // clear input when access token updates + useAccessToken.set('') + }, [configContext.val.access_token]) + + const onInputAccessToken = React.useCallback( + ({ currentTarget: { value } }: React.FormEvent) => { + useAccessToken.set(value) + useAccessTokenHint.set( + ACCESS_TOKEN_REGEXP.test(value) ? '' : 'This token is in unknown format.', + ) + }, + [], + ) + + const onPressAccessToken = React.useCallback(({ key }: React.KeyboardEvent) => { + if (key === 'Enter') saveToken() + }, []) + + const saveToken = React.useCallback( + async (hint?: typeof useAccessTokenHint.val) => { + if (accessToken) { + configContext.set({ access_token: accessToken }) + useAccessToken.set('') + useAccessTokenHint.set( + hint || ( + + window.location.reload()}> + Reload + {' '} + to activate! + + ), + ) + } + }, + [accessToken], + ) + + return ( +
+

+ Access Token{' '} + + (?) + +

+ {!hasAccessToken && ( + { + // use js here to make sure redirect_uri is latest url + const url = `https://github.com/login/oauth/authorize?client_id=${ + oauth.clientId + }&scope=repo&redirect_uri=${encodeURIComponent(window.location.href)}` + window.location.href = url + }} + > + Create with OAuth (recommended) + + )} +
+ + {hasAccessToken && !accessToken ? ( + + ) : ( + + )} +
+ {accessTokenHint && {accessTokenHint}} +
+ ) +} diff --git a/src/components/settings/ShortcutSettings.tsx b/src/components/settings/ShortcutSettings.tsx new file mode 100644 index 0000000..f7454b0 --- /dev/null +++ b/src/components/settings/ShortcutSettings.tsx @@ -0,0 +1,56 @@ +import { useConfigs } from 'containers/ConfigsContext' +import * as React from 'react' +import { friendlyFormatShortcut } from 'utils/general' +import { useStates } from 'utils/hooks' +import * as keyHelper from 'utils/keyHelper' + +type Props = {} + +export function ShortcutSettings(props: React.PropsWithChildren) { + const configContext = useConfigs() + const useShortcutHint = useStates('') + const useToggleShowSideBarShortcut = useStates(configContext.val.shortcut) + const { val: toggleShowSideBarShortcut } = useToggleShowSideBarShortcut + const { val: shortcutHint } = useShortcutHint + + React.useEffect(() => { + useToggleShowSideBarShortcut.set(configContext.val.shortcut) + }, [configContext.val.shortcut]) + + const saveShortcut = React.useCallback(async () => { + const { val: toggleShowSideBarShortcut } = useToggleShowSideBarShortcut + configContext.set({ shortcut: toggleShowSideBarShortcut }) + if (typeof toggleShowSideBarShortcut === 'string') { + useShortcutHint.set('Shortcut is saved!') + } + }, [useToggleShowSideBarShortcut.val]) + + const onShortCutInputKeyDown = React.useCallback((e: React.KeyboardEvent) => { + e.preventDefault() + e.stopPropagation() + // Clear shortcut with backspace + const shortcut = e.key === 'Backspace' ? '' : keyHelper.parseEvent(e) + useToggleShowSideBarShortcut.set(shortcut) + }, []) + + return ( +
+

Toggle Shortcut

+ Set a combination of keys for toggling Gitako sidebar. +
+
+ + +
+ {shortcutHint && {shortcutHint}} +
+ ) +} From b5d9275e631b23bb9ff01a611e79cbeac6240d16 Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Sun, 1 Dec 2019 22:32:03 +0800 Subject: [PATCH 85/98] feat: settings for icons --- src/components/Node.tsx | 14 ++++- src/components/SettingsBar.tsx | 2 + .../settings/FileTreeIconSettings.tsx | 56 +++++++++++++++++++ src/utils/configHelper.ts | 16 +++--- 4 files changed, 78 insertions(+), 10 deletions(-) create mode 100644 src/components/settings/FileTreeIconSettings.tsx diff --git a/src/components/Node.tsx b/src/components/Node.tsx index d429ddf..ce178ed 100644 --- a/src/components/Node.tsx +++ b/src/components/Node.tsx @@ -1,3 +1,4 @@ +import { useConfigs } from 'containers/ConfigsContext' import * as React from 'react' import { cx } from 'utils/cx' import { OperatingSystems, os } from 'utils/general' @@ -72,14 +73,23 @@ const NodeItemIcon = React.memo(function NodeItemIcon({ node: TreeNode open?: boolean }) { + const { + val: { icons }, + } = useConfigs() + + if (icons === 'native') return const src = React.useMemo( () => (node.type === 'tree' ? getFolderIconSrc(node, open) : getFileIconSrc(node)), [open], ) return ( <> - - {node.type !== 'commit' && {node.name}} + + {node.type === 'commit' ? ( + + ) : ( + {node.name} + )} ) }) diff --git a/src/components/SettingsBar.tsx b/src/components/SettingsBar.tsx index 2c365b8..b77ccea 100644 --- a/src/components/SettingsBar.tsx +++ b/src/components/SettingsBar.tsx @@ -4,6 +4,7 @@ import * as React from 'react' import { Config } from 'utils/configHelper' import { useStates } from 'utils/hooks' import { AccessTokenSettings } from './settings/AccessTokenSettings' +import { FileTreeIconSettings } from './settings/FileTreeIconSettings' import { ShortcutSettings } from './settings/ShortcutSettings' import { SimpleToggleField } from './SimpleToggleField' @@ -70,6 +71,7 @@ function SettingsBarContent() {
+

More

{moreFields.map(field => ( diff --git a/src/components/settings/FileTreeIconSettings.tsx b/src/components/settings/FileTreeIconSettings.tsx new file mode 100644 index 0000000..53dd023 --- /dev/null +++ b/src/components/settings/FileTreeIconSettings.tsx @@ -0,0 +1,56 @@ +import { useConfigs } from 'containers/ConfigsContext' +import * as React from 'react' +import { Config } from 'utils/configHelper' + +const options: { + key: Config['icons'] + value: Config['icons'] + label: string +}[] = [ + { + key: 'rich', + value: 'rich', + label: `VSCode icons`, + }, + { + key: 'dim', + value: 'dim', + label: `VSCode icons (single color)`, + }, + { + key: 'native', + value: 'native', + label: `Native GitHub icons`, + }, +] + +type Props = {} + +export function FileTreeIconSettings(props: React.PropsWithChildren) { + const configContext = useConfigs() + return ( +
+

File Tree Icons

+ Icons can make a difference. +
+
+ +
+
+ ) +} diff --git a/src/utils/configHelper.ts b/src/utils/configHelper.ts index 470a005..61aab6f 100644 --- a/src/utils/configHelper.ts +++ b/src/utils/configHelper.ts @@ -8,6 +8,7 @@ export type Config = { copyFileButton: boolean copySnippetButton: boolean intelligentToggle: boolean | null // `null` stands for intelligent, boolean for sidebar open status + icons: 'rich' | 'dim' | 'native' } export enum configKeys { @@ -18,6 +19,7 @@ export enum configKeys { copyFileButton = 'copyFileButton', copySnippetButton = 'copySnippetButton', intelligentToggle = 'intelligentToggle', + icons = 'icons', } export const defaultConfigs: Config = { @@ -28,19 +30,17 @@ export const defaultConfigs: Config = { copyFileButton: true, copySnippetButton: true, intelligentToggle: null, + icons: 'rich', } const configKeyArray = Object.values(configKeys) function applyDefaultConfigs(configs: Config) { - return configKeyArray.reduce( - (applied, configKey) => { - const key = configKey as keyof Config - Object.assign(applied, { [key]: key in configs ? configs[key] : defaultConfigs[key] }) - return applied - }, - {} as Config, - ) + return configKeyArray.reduce((applied, configKey) => { + const key = configKey as keyof Config + Object.assign(applied, { [key]: key in configs ? configs[key] : defaultConfigs[key] }) + return applied + }, {} as Config) } export async function get(): Promise { From f54f2ea440f19a6c8fdd4118722691463906273d Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Sun, 1 Dec 2019 22:59:44 +0800 Subject: [PATCH 86/98] fix: look for longer extension names --- src/utils/parseIconMapCSV.tsx | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/utils/parseIconMapCSV.tsx b/src/utils/parseIconMapCSV.tsx index 28ce618..ded7655 100644 --- a/src/utils/parseIconMapCSV.tsx +++ b/src/utils/parseIconMapCSV.tsx @@ -52,10 +52,17 @@ export function getFolderIconSrc(node: TreeNode, open: boolean) { const { filenameIndex, fileExtensionIndex } = parseFileIconMapCSV() export function getFileIconSrc(node: TreeNode) { - const name = - filenameIndex.get(node.name.toLowerCase()) || - fileExtensionIndex.get((node.name.split('.').pop() as string).toLowerCase()) - return getIconSrc('file', name) + const fileName = node.name.toLowerCase() + let iconName = filenameIndex.get(fileName) + if (!iconName) { + const tail = fileName.split('.') + tail.shift() + while (!iconName && tail.length > 0) { + iconName = fileExtensionIndex.get(tail.join('.')) + tail.shift() + } + } + return getIconSrc('file', iconName) } export function getIconSrc(type: 'folder' | 'file', name: string = 'default', open?: boolean) { From effaca1f2cbaf570e6380c12cf9fdc5234bec65e Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Sun, 1 Dec 2019 23:03:07 +0800 Subject: [PATCH 87/98] fix: protect stale extension context App would crash on storage manipulation when context is stale --- src/utils/storageHelper.ts | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/utils/storageHelper.ts b/src/utils/storageHelper.ts index 8c52640..558a041 100644 --- a/src/utils/storageHelper.ts +++ b/src/utils/storageHelper.ts @@ -1,9 +1,13 @@ const localStorage = browser.storage.local -export function get(mapping: string[] | null): Promise { - return localStorage.get(mapping || undefined) +export function get(mapping: string[] | null): Promise | any { + try { + return localStorage.get(mapping || undefined) + } catch (err) {} } -export function set(value: any): Promise { - return localStorage.set(value) +export function set(value: any): Promise | void { + try { + return localStorage.set(value) + } catch (err) {} } From 8c9007ddd02860acc3244f1f4fa83faee023926b Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Mon, 2 Dec 2019 19:24:37 +0800 Subject: [PATCH 88/98] fix: hide ugly select style on FireFox --- src/content.less | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/content.less b/src/content.less index 6e34e52..b6ea0e2 100644 --- a/src/content.less +++ b/src/content.less @@ -446,6 +446,9 @@ min-width: 100px; } } + select { + -moz-appearance: none; + } .access-token { border-bottom: none; // prevent overwrite by github style .link-button { From 2127afba4b61a27e3de345d8cdf51266afb16339 Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Mon, 2 Dec 2019 19:30:15 +0800 Subject: [PATCH 89/98] docs: update contact text --- src/components/SettingsBar.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/SettingsBar.tsx b/src/components/SettingsBar.tsx index b77ccea..a8da63a 100644 --- a/src/components/SettingsBar.tsx +++ b/src/components/SettingsBar.tsx @@ -86,7 +86,7 @@ function SettingsBarContent() {
From 982ad39ef355f34ec0422614094a7dfa50656249 Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Mon, 2 Dec 2019 19:46:24 +0800 Subject: [PATCH 90/98] fix: icon alignments --- src/components/Icon.tsx | 2 +- src/content.less | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/Icon.tsx b/src/components/Icon.tsx index eaaf357..1407dbc 100644 --- a/src/components/Icon.tsx +++ b/src/components/Icon.tsx @@ -133,7 +133,7 @@ export function Icon({ type, className = undefined, placeholder, ...otherProps } {React.createElement(Octicon, { icon: IconComponent, className: mergedClassName, - verticalAlign: 'middle', + verticalAlign: 'text-bottom', })}
) diff --git a/src/content.less b/src/content.less index b6ea0e2..81f6ee7 100644 --- a/src/content.less +++ b/src/content.less @@ -362,7 +362,7 @@ width: 16px; height: 16px; object-fit: contain; - vertical-align: middle; + vertical-align: text-bottom; padding-left: 4px; box-sizing: content-box; From 02b33776b7eda46fef4acbb2ab2c105bf7f7e4a9 Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Mon, 2 Dec 2019 19:46:52 +0800 Subject: [PATCH 91/98] 0.8.0 --- package.json | 2 +- src/manifest.json | 24 ++++++++++++++++++------ 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/package.json b/package.json index 42da19c..56c9319 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gitako", - "version": "0.7.9", + "version": "0.8.0", "description": "The missing part of GitHub.", "repository": "https://github.com/EnixCoda/Gitako", "author": "EnixCoda", diff --git a/src/manifest.json b/src/manifest.json index 965775f..a871091 100644 --- a/src/manifest.json +++ b/src/manifest.json @@ -1,19 +1,31 @@ { "manifest_version": 2, "name": "Gitako - Github file tree", - "version": "0.7.9", + "version": "0.8.0", "description": "The missing part of GitHub.", "author": "EnixCoda", "icons": { "128": "icons/Gitako-128x128.png" }, "homepage_url": "https://github.com/EnixCoda/Gitako", - "permissions": ["storage", "*://*.github.com/*", "*://*.sentry.io/*"], - "web_accessible_resources": ["icons/vscode/*"], + "permissions": [ + "storage", + "*://*.github.com/*", + "*://*.sentry.io/*" + ], + "web_accessible_resources": [ + "icons/vscode/*" + ], "content_scripts": [ { - "matches": ["https://github.com/*"], - "js": ["firefox-shim.js", "browser-polyfill.js", "content.js"] + "matches": [ + "https://github.com/*" + ], + "js": [ + "firefox-shim.js", + "browser-polyfill.js", + "content.js" + ] } ] -} +} \ No newline at end of file From c62ea95e12d2f617ece3022b46eddcb1f7048c82 Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Wed, 4 Dec 2019 11:47:10 +0800 Subject: [PATCH 92/98] fix: handle not well-formatted icon data input --- scripts/generate-file-icon-index.js | 115 ++++++++++++++------------ src/assets/icons/file-icons-index.csv | 8 +- 2 files changed, 66 insertions(+), 57 deletions(-) diff --git a/scripts/generate-file-icon-index.js b/scripts/generate-file-icon-index.js index a3fe1b3..3c36d2f 100644 --- a/scripts/generate-file-icon-index.js +++ b/scripts/generate-file-icon-index.js @@ -1,62 +1,71 @@ const languageIds = require('./language-id-ext.json') -const records = [] +function generateCSV() { + const records = [] -document.body - .querySelector('table') - .querySelectorAll('tbody tr') - .forEach(tr => { - const [name, id, dark, light] = Array.from(tr.querySelectorAll('td')) - const exts = [] - const names = [] - id.innerHTML - .replace(/|<\/sub>/g, '') - .split(', ') - .map(part => { - const match = part.match(/<(\w+)>(.*?)<\/\1>/) - if (match) { - const [, tag, content] = match - if (tag === 'strong') { - // filenames in bold - names.push(content) - } else if (tag === 'code') { - // language ids in code block - const map = Object.values(languageIds).find(({ ids }) => - (Array.isArray(ids) ? ids : [ids]).includes(content), - ) - if (map && map.exts) exts.push(map.exts) - } else { - console.warn(`Found unrecognized format`, part) // unknown + document.body + .querySelector('table') + .querySelectorAll('tbody tr') + .forEach(tr => { + const [name, id, dark, light] = Array.from(tr.querySelectorAll('td')) + const exts = [] + const names = [] + id.innerHTML + .replace(/|<\/sub>/g, '') + .split(', ') + .map(part => { + const tags = part.match(/<(\w+)>(.*?)<\/\1>/g) + if (tags) { + tags.forEach(subPart => { + const match = subPart.match(/<(\w+)>(.*?)<\/\1>/) + if (match) { + const [, tag, content] = match + if (tag === 'strong') { + // filenames in bold + names.push(content) + } else if (tag === 'code') { + // language ids in code block + const map = Object.values(languageIds).find(({ ids }) => + (Array.isArray(ids) ? ids : [ids]).includes(content), + ) + if (map && map.exts) exts.push(map.exts) + } else { + console.warn(`Found unrecognized format`, subPart, tag) // unknown + } + } + }) + } else if (part) { + // extensions are in regular fonts + exts.push(part.replace(/^\./, '')) } - } else if (part) { - // extensions are in regular fonts - exts.push(part.replace(/^\./, '')) - } + }) + records.push({ + name: name.innerText, + exts, + names, + icon: getSrc(dark.querySelector('img')) || getSrc(light.querySelector('img')), }) - records.push({ - name: name.innerText, - exts, - names, - icon: getSrc(dark.querySelector('img')) || getSrc(light.querySelector('img')), }) - }) -function getSrc(img) { - return img && img.src + function getSrc(img) { + return img && img.src + } + + const prepend = 'https://github.com/vscode-icons/vscode-icons/raw/master/icons/file_type_' + const append = '.svg?sanitize=true' + const separator = ':' + const csv = records + .map(({ name, names, exts, icon }) => + [ + name, + names.join(separator), + exts.join(separator), + // icon.replace(prepend, '').replace(append, ''), // assumption: name is equal to this + ].join(','), + ) + .join('\n') + + return csv } -const prepend = 'https://github.com/vscode-icons/vscode-icons/raw/master/icons/file_type_' -const append = '.svg?sanitize=true' -const separator = ':' -const csv = records - .map(({ name, names, exts, icon }) => - [ - name, - names.join(separator), - exts.join(separator), - // icon.replace(prepend, '').replace(append, ''), // assumption: name is equal to this - ].join(','), - ) - .join('\n') - -console.log(csv) +console.log(generateCSV()) diff --git a/src/assets/icons/file-icons-index.csv b/src/assets/icons/file-icons-index.csv index 91d3c08..553de50 100644 --- a/src/assets/icons/file-icons-index.csv +++ b/src/assets/icons/file-icons-index.csv @@ -171,8 +171,8 @@ delphi,,pas:pas django,,djt:html:html dlang,,d:d:d:d diff,,diff -docker,docker-compose.yml:docker-compose.ci-build.yml:docker-compose.override.yml:docker-compose.vs.debug.yml:docker-compose.vs.release.yml:docker-cloud.yml,dockerignore -docker2,docker-compose.yml:docker-compose.ci-build.yml:docker-compose.override.yml:docker-compose.vs.debug.yml:docker-compose.vs.release.yml:docker-cloud.yml,dockerignore +docker,docker-compose.yml:docker-compose.ci-build.yml:docker-compose.override.yml:docker-compose.vs.debug.yml:docker-compose.vs.release.yml:docker-cloud.yml,dockerfile:dockerignore +docker2,docker-compose.yml:docker-compose.ci-build.yml:docker-compose.override.yml:docker-compose.vs.debug.yml:docker-compose.vs.release.yml:docker-cloud.yml,dockerfile:dockerignore dockertest,docker-compose.test.yml, dockertest2,docker-compose.test.yml, docpad,,eco @@ -399,7 +399,7 @@ nodemon,nodemon.json, npm,.npmignore:.npmrc:package.json:package-lock.json:npm-shrinkwrap.json, nsi,,nsi:nsi:nsi:nsi nsri,.nsrirc:.nsriignore:.nsrirc.js:.nsrirc.json:.nsrirc.yaml:.nsrirc.yml:.nsrirc.config.js, -nsri-integrity,.integrity.json,,nsri-integrity +nsri-integrity,.integrity.json, nuget,,nupkg:nuspec:psmdcp nunjucks,,nunj:njs:nunjucks nuxt,nuxt.config.js:nuxt.config.ts, @@ -612,7 +612,7 @@ volt,,volt vscode,.vscodeignore:launch.json:tasks.json:vscodeignore.json, vscode2,.vscodeignore:launch.json:tasks.json:vscodeignore.json, vscode3,.vscodeignore:launch.json:tasks.json:vscodeignore.json, -vscode-insiders,.vscodeignore:launch.json:tasks.json:vscodeignore.json,,vscode-insiders +vscode-insiders,.vscodeignore:launch.json:tasks.json:vscodeignore.json, vsix,,vsix vsixmanifest,,vsixmanifest vue,,vue From 5af875185b569e9296c818d742fae24e61562e4c Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Sat, 7 Dec 2019 15:43:13 +0800 Subject: [PATCH 93/98] 0.8.1 --- package.json | 2 +- src/manifest.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 56c9319..dd9e9a6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gitako", - "version": "0.8.0", + "version": "0.8.1", "description": "The missing part of GitHub.", "repository": "https://github.com/EnixCoda/Gitako", "author": "EnixCoda", diff --git a/src/manifest.json b/src/manifest.json index a871091..9003196 100644 --- a/src/manifest.json +++ b/src/manifest.json @@ -1,7 +1,7 @@ { "manifest_version": 2, "name": "Gitako - Github file tree", - "version": "0.8.0", + "version": "0.8.1", "description": "The missing part of GitHub.", "author": "EnixCoda", "icons": { From 568edc9cbabbfd3c95e8867721f6175e0d6212e6 Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Sat, 7 Dec 2019 15:55:31 +0800 Subject: [PATCH 94/98] build: add version to name, compress source code --- Makefile | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index c054032..02da407 100755 --- a/Makefile +++ b/Makefile @@ -15,9 +15,13 @@ upload-for-analytics: compress: rm -f dist/gitako.zip - cd dist && zip -r gitako.zip * -x *.map + cd dist && zip -r gitako-$(FULL_VERSION).zip * -x *.map -x *.DS_Store release: $(MAKE) build $(MAKE) upload-for-analytics $(MAKE) compress + $(MAKE) compress-source + +compress-source: + git archive -o dist/source-$(FULL_VERSION).zip HEAD From 9202ff37f38979378abb35e39a8280cde50c3a00 Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Sat, 7 Dec 2019 16:00:15 +0800 Subject: [PATCH 95/98] docs: update readme shorten features fix katakana --- README.md | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 9c6afef..a3b3065 100644 --- a/README.md +++ b/README.md @@ -8,15 +8,12 @@ Yet another extension for GitHub, available on both Chrome and Firefox. Inspired - 🔎 instant file search - 🏎 extremely fast even in gigantic projects - ⌨️ intuitive keyboard accessibility - - navigate file using arrow keys - - shortcut for toggling sidebar -- 🚀 boosts loading speed +- 🚀 boosts page performance - ↔️ customizable layout -- 📋 shortcuts for copy snippets and file +- 📋 copy snippets and file - 🕶️ support private repositories - 🗂 support git submodule -- 🎨 GitHub-friendly UI -- 🎈 light weight in size and memory usage +- 🎨 friendly UI and rich icons ### Install @@ -34,7 +31,7 @@ Any bug report or feature request discussions are welcomed, feel free to draft a #### Why named 'Gitako'? -GitHub's totem is a cute octopus. And octopus in Japanese is `たこ`(tako). +GitHub's totem is a cute octopus. And octopus in Japanese is `タコ`(tako). Then link them together: git + tako -> gitako From 4419d02815601fed0a139faabc7081db98680522 Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Thu, 19 Dec 2019 21:52:35 +0800 Subject: [PATCH 96/98] refactor: extract useOnLocationChange --- src/components/SideBar.tsx | 7 +------ src/utils/hooks.ts | 9 +++++++++ 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/components/SideBar.tsx b/src/components/SideBar.tsx index a2966dc..ab69c79 100644 --- a/src/components/SideBar.tsx +++ b/src/components/SideBar.tsx @@ -11,7 +11,7 @@ import { SideBarCore } from 'driver/core' import { ConnectorState, Props } from 'driver/core/SideBar' import { oauth } from 'env' import * as React from 'react' -import { useEvent, useLocation } from 'react-use' +import { useEvent } from 'react-use' import { cx } from 'utils/cx' import * as DOMHelper from 'utils/DOMHelper' import { JSONRequest, parseURLSearch } from 'utils/general' @@ -207,8 +207,3 @@ async function trySetUpAccessTokenWithCode() { raiseError(err) } } - -function useOnLocationChange(callback: React.EffectCallback, extraDeps: React.DependencyList = []) { - const { href, pathname, search } = useLocation() - React.useEffect(callback, [href, pathname, search, ...extraDeps]) -} diff --git a/src/utils/hooks.ts b/src/utils/hooks.ts index fe55030..67cca1c 100644 --- a/src/utils/hooks.ts +++ b/src/utils/hooks.ts @@ -1,4 +1,5 @@ import * as React from 'react' +import { useLocation } from 'react-use' import { createStyleSheet, setStyleSheetMedia } from './general' export function useWindowSize( @@ -79,3 +80,11 @@ export function useDidUpdate(effect: React.EffectCallback, deps?: React.Dependen return effect() }, deps) } + +export function useOnLocationChange( + callback: React.EffectCallback, + extraDeps: React.DependencyList = [], +) { + const { href, pathname, search } = useLocation() + React.useEffect(callback, [href, pathname, search, ...extraDeps]) +} From 6f86fd5b2d15f94d6117dad5a536b862678f896f Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Thu, 19 Dec 2019 21:55:22 +0800 Subject: [PATCH 97/98] feat: focus item on redirect --- src/components/FileExplorer.tsx | 17 +++++++++++++++-- src/driver/core/FileExplorer.ts | 23 ++++++++++++++--------- 2 files changed, 29 insertions(+), 11 deletions(-) diff --git a/src/components/FileExplorer.tsx b/src/components/FileExplorer.tsx index a2a9020..e8fa97c 100644 --- a/src/components/FileExplorer.tsx +++ b/src/components/FileExplorer.tsx @@ -6,9 +6,11 @@ import { connect } from 'driver/connect' import { FileExplorerCore } from 'driver/core' import { ConnectorState, Props } from 'driver/core/FileExplorer' import * as React from 'react' +import { useEvent } from 'react-use' import { FixedSizeList as List, ListChildComponentProps, ListProps } from 'react-window' import { cx } from 'utils/cx' -import { usePrevious } from 'utils/hooks' +import { useOnLocationChange, usePrevious } from 'utils/hooks' +import { getCurrentPath } from 'utils/URLHelper' import { TreeNode, VisibleNodes } from 'utils/VisibleNodesGenerator' import { Icon } from './Icon' import { SizeObserver } from './SizeObserver' @@ -77,6 +79,8 @@ const RawFileExplorer: React.FC = function RawFileExplor nodes={nodes} height={height} width={width} + expandTo={props.expandTo} + metaData={props.metaData} /> )} @@ -167,13 +171,16 @@ function ListView({ height, focusedNode, renderNode, + metaData, + expandTo, }: { nodes: TreeNode[] height: number width: number focusedNode: TreeNode | null renderNode: ListProps['children'] -}) { +} & Pick & + Pick) { const listRef = React.useRef(null) React.useEffect(() => { if (focusedNode && listRef.current) { @@ -187,6 +194,12 @@ function ListView({ listRef.current.scrollTo(0) } }, [listRef.current, focusedNode, nodes.length]) + + const goToCurrentItem = React.useCallback(() => { + expandTo(getCurrentPath(metaData.branchName)) + }, [metaData.branchName]) + useOnLocationChange(goToCurrentItem) + useEvent('pjax:complete', goToCurrentItem, window) return ( setUpTree: GetCreatedMethod goTo: GetCreatedMethod + expandTo: GetCreatedMethod } type DepthMap = Map @@ -53,7 +54,7 @@ function getVisibleParentNode(nodes: TreeNode[], focusedNode: TreeNode, depths: } type Task = () => void -const tasksAfterRender: (Task)[] = [] +const tasksAfterRender: Task[] = [] let visibleNodesGenerator: VisibleNodesGenerator type BoundMethodCreator = MethodCreator @@ -136,9 +137,9 @@ function handleParsed(root: TreeNode, parsed: Parsed) { }) } -export const setUpTree: BoundMethodCreator< - [Pick & Pick] -> = dispatch => async ({ treeData, metaData, compressSingletonFolder, accessToken }) => { +export const setUpTree: BoundMethodCreator<[ + Pick & Pick, +]> = dispatch => async ({ treeData, metaData, compressSingletonFolder, accessToken }) => { if (!treeData) return dispatch.call(setStateText, 'Rendering File List...') const { root, gitModules } = treeParser.parse(treeData, metaData) @@ -299,11 +300,7 @@ export const search: BoundMethodCreator<[string]> = dispatch => searchKey => { export const goTo: BoundMethodCreator<[string[]]> = dispatch => async currentPath => { visibleNodesGenerator.search([]) tasksAfterRender.push(() => { - const nodeExpandedTo = visibleNodesGenerator.expandTo(currentPath.join('/')) - if (nodeExpandedTo) { - visibleNodesGenerator.focusNode(nodeExpandedTo) - } - dispatch.call(updateVisibleNodes) + dispatch.call(expandTo, currentPath) }) dispatch.set({ searchKey: '', searched: false }) } @@ -347,6 +344,14 @@ export const onNodeClick: BoundMethodCreator<[TreeNode]> = dispatch => node => { } } +export const expandTo: BoundMethodCreator<[string[]]> = dispatch => currentPath => { + const nodeExpandedTo = visibleNodesGenerator.expandTo(currentPath.join('/')) + if (nodeExpandedTo) { + visibleNodesGenerator.focusNode(nodeExpandedTo) + } + dispatch.call(updateVisibleNodes) +} + export const updateVisibleNodes: BoundMethodCreator = dispatch => () => { const { visibleNodes } = visibleNodesGenerator dispatch.set({ visibleNodes }) From 193fb1f31358320574881f712544382e9d359ee3 Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Thu, 19 Dec 2019 21:55:34 +0800 Subject: [PATCH 98/98] 0.8.2 --- package.json | 2 +- src/manifest.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index dd9e9a6..250205e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gitako", - "version": "0.8.1", + "version": "0.8.2", "description": "The missing part of GitHub.", "repository": "https://github.com/EnixCoda/Gitako", "author": "EnixCoda", diff --git a/src/manifest.json b/src/manifest.json index 9003196..8f7fb24 100644 --- a/src/manifest.json +++ b/src/manifest.json @@ -1,7 +1,7 @@ { "manifest_version": 2, "name": "Gitako - Github file tree", - "version": "0.8.1", + "version": "0.8.2", "description": "The missing part of GitHub.", "author": "EnixCoda", "icons": {