diff --git a/README.md b/README.md index a3b3065..7a14af6 100644 --- a/README.md +++ b/README.md @@ -19,9 +19,9 @@ Yet another extension for GitHub, available on both Chrome and Firefox. Inspired [![Install for Chrome](./ChromeWebStoreBadge.svg)](https://chrome.google.com/webstore/detail/gitako-github-file-tree/giljefjcheohhamkjphiebfjnlphnokk) -[Or install for Firefox](https://addons.mozilla.org/en-US/firefox/addon/gitako-github-file-tree/) +[for Edge](https://microsoftedge.microsoft.com/addons/detail/alpoloddcggjhakjemghahlkofjekbca) -> Gitako for Firefox is still in beta +[for Firefox(beta)](https://addons.mozilla.org/en-US/firefox/addon/gitako-github-file-tree/) ### Issues diff --git a/package.json b/package.json index 250205e..0be2289 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gitako", - "version": "0.8.2", + "version": "0.8.7", "description": "The missing part of GitHub.", "repository": "https://github.com/EnixCoda/Gitako", "author": "EnixCoda", @@ -56,8 +56,8 @@ "json-loader": "^0.5.7", "less": "^3.9.0", "less-loader": "^4.0.5", + "mini-css-extract-plugin": "^0.9.0", "raw-loader": "^4.0.0", - "style-loader": "^0.23.1", "typescript": "^3.7.2", "uglifyjs-webpack-plugin": "^2.1.2", "url-loader": "^1.1.2", diff --git a/src/analytics.ts b/src/analytics.ts index 0fdf838..c186d8e 100644 --- a/src/analytics.ts +++ b/src/analytics.ts @@ -5,13 +5,45 @@ import { IN_PRODUCTION_MODE, VERSION } from 'env' const PUBLIC_KEY = 'd22ec5c9cc874539a51c78388c12e3b0' const PROJECT_ID = '1406497' +const MAX_REPORT_COUNT = 10 // protect for error leaking +let countReportedError = 0 + +const errorSet = new Set([ + 'inThisSearch is null', // weird bug in Firefox + 'The quota has been exceeded.', // caused by other addons in Firefox + 'NetworkError when attempting to fetch resource.', // network fail in Firefox + 'Failed to fetch', // network fail in Chrome +]) + const sentryOptions: Sentry.BrowserOptions = { dsn: `https://${PUBLIC_KEY}@sentry.io/${PROJECT_ID}`, 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 - integrations: ints => ints.filter(({ name }) => name !== 'TryCatch'), + defaultIntegrations: IN_PRODUCTION_MODE ? undefined : false, + integrations: integrations => integrations.filter(({ name }) => name !== 'TryCatch'), + beforeSend(event) { + const message = event.exception?.values?.[0].value || event.message + if (message) { + if (errorSet.has(message)) return null + errorSet.add(message) // prevent reporting duplicated error + } + if (countReportedError < MAX_REPORT_COUNT) { + ++countReportedError + return event + } + return null + }, + beforeBreadcrumb(breadcrumb, hint) { + if (breadcrumb.category === 'ui.click') { + const ariaLabel = hint?.event?.target?.ariaLabel + if (ariaLabel) { + breadcrumb.message = ariaLabel + } + } + return breadcrumb + }, } Sentry.init(sentryOptions) diff --git a/src/components/FileExplorer.tsx b/src/components/FileExplorer.tsx index e8fa97c..369460c 100644 --- a/src/components/FileExplorer.tsx +++ b/src/components/FileExplorer.tsx @@ -6,7 +6,7 @@ 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 useEvent from 'react-use/esm/useEvent' import { FixedSizeList as List, ListChildComponentProps, ListProps } from 'react-window' import { cx } from 'utils/cx' import { useOnLocationChange, usePrevious } from 'utils/hooks' diff --git a/src/components/Icon.tsx b/src/components/Icon.tsx index 1407dbc..ec7ef44 100644 --- a/src/components/Icon.tsx +++ b/src/components/Icon.tsx @@ -125,16 +125,19 @@ type Props = { } export function Icon({ type, className = undefined, placeholder, ...otherProps }: Props) { - if (placeholder) return
- const { name, IconComponent } = getSVGIconComponent(type) - const mergedClassName = cx('octicon', name) + let children: React.ReactNode = null + if (!placeholder) { + const { name, IconComponent } = getSVGIconComponent(type) + const mergedClassName = cx('octicon', name) + children = React.createElement(Octicon, { + icon: IconComponent, + className: mergedClassName, + verticalAlign: 'text-bottom', + }) + } return (
- {React.createElement(Octicon, { - icon: IconComponent, - className: mergedClassName, - verticalAlign: 'text-bottom', - })} + {children}
) } diff --git a/src/components/Node.tsx b/src/components/Node.tsx index ce178ed..5ee3532 100644 --- a/src/components/Node.tsx +++ b/src/components/Node.tsx @@ -77,14 +77,19 @@ const NodeItemIcon = React.memo(function NodeItemIcon({ val: { icons }, } = useConfigs() - if (icons === 'native') return const src = React.useMemo( () => (node.type === 'tree' ? getFolderIconSrc(node, open) : getFileIconSrc(node)), [open], ) + + if (icons === 'native') return return ( <> - + {node.type === 'commit' ? ( ) : ( diff --git a/src/components/PJAXLink.tsx b/src/components/PJAXLink.tsx deleted file mode 100644 index 680214e..0000000 --- a/src/components/PJAXLink.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import * as React from 'react' -import * as DOMHelper from 'utils/DOMHelper' - -type Props

= { - to: string - children: React.ReactElement

-} - -export function PJAXLink

({ to, children }: Props

) { - return React.cloneElement(children, { - ...children.props, - onClick: () => DOMHelper.loadWithPJAX(to), - }) -} diff --git a/src/components/SideBar.tsx b/src/components/SideBar.tsx index ab69c79..68347b4 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 } from 'react-use' +import useEvent from 'react-use/esm/useEvent' import { cx } from 'utils/cx' import * as DOMHelper from 'utils/DOMHelper' import { JSONRequest, parseURLSearch } from 'utils/general' @@ -85,7 +85,7 @@ const RawGitako: React.FC = function RawGitako(props) { // init again when setting new accessToken useDidUpdate(() => { props.init() - }, [accessToken]) + }, [accessToken || '']) // fallback for preventing duplicated requests const { errorDueToAuth, diff --git a/src/content.less b/src/content.less index 81f6ee7..193302a 100644 --- a/src/content.less +++ b/src/content.less @@ -358,12 +358,29 @@ transition: all 0.5s ease; white-space: nowrap; - &-icon { + .icon-size() { width: 16px; height: 16px; + } + + .octicon-wrapper:first-child { + margin-right: 6px; + } + + &-type-icon { + margin-right: 4px; + .icon-size(); + + & + .octicon-wrapper, + & + .node-item-icon { + margin-right: 6px; + } + } + + &-icon { + .icon-size(); object-fit: contain; vertical-align: text-bottom; - padding-left: 4px; box-sizing: content-box; &.dim { @@ -379,12 +396,9 @@ .node-item-label { overflow: hidden; text-overflow: ellipsis; - - .node-item-name { - padding-left: 6px; - } } - .node-item:hover .node-item-name { + + &:hover .node-item-name { text-decoration: underline; } diff --git a/src/content.tsx b/src/content.tsx index c9eb923..2a9c318 100644 --- a/src/content.tsx +++ b/src/content.tsx @@ -7,7 +7,14 @@ import './content.less' addMiddleware(withErrorLog) -const SideBarElement = document.createElement('div') -document.body.appendChild(SideBarElement) +function init() { + const SideBarElement = document.createElement('div') + document.body.appendChild(SideBarElement) + ReactDOM.render(, SideBarElement) +} -ReactDOM.render(, SideBarElement) +if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', init) +} else { + init() +} diff --git a/src/manifest.json b/src/manifest.json index 8f7fb24..77b3276 100644 --- a/src/manifest.json +++ b/src/manifest.json @@ -1,7 +1,7 @@ { "manifest_version": 2, "name": "Gitako - Github file tree", - "version": "0.8.2", + "version": "0.8.7", "description": "The missing part of GitHub.", "author": "EnixCoda", "icons": { @@ -21,6 +21,9 @@ "matches": [ "https://github.com/*" ], + "css": [ + "content.css" + ], "js": [ "firefox-shim.js", "browser-polyfill.js", diff --git a/src/utils/URLHelper.ts b/src/utils/URLHelper.ts index 6d515bb..0907583 100644 --- a/src/utils/URLHelper.ts +++ b/src/utils/URLHelper.ts @@ -10,7 +10,7 @@ export function parse(): MetaData & { path: string[] } { repoName, type, ...path // should be [...branchName.split('/'), ...filePath.split('/')] - ] = pathname.split('/') + ] = unescape(decodeURIComponent(pathname)).split('/') return { userName, repoName, @@ -80,7 +80,10 @@ export function getCurrentPath(branchName = '') { splitBranchName.shift() path.shift() } else { - raiseError(new Error(`branch name and path prefix not match`)) + raiseError(new Error(`branch name and path prefix not match`), { + branchName, + path: parse().path, + }) return [] } } diff --git a/src/utils/hooks.ts b/src/utils/hooks.ts index 67cca1c..75aa7d5 100644 --- a/src/utils/hooks.ts +++ b/src/utils/hooks.ts @@ -1,5 +1,5 @@ import * as React from 'react' -import { useLocation } from 'react-use' +import useLocation from 'react-use/esm/useLocation' import { createStyleSheet, setStyleSheetMedia } from './general' export function useWindowSize( diff --git a/src/utils/parseIconMapCSV.tsx b/src/utils/parseIconMapCSV.tsx index ded7655..96c42e5 100644 --- a/src/utils/parseIconMapCSV.tsx +++ b/src/utils/parseIconMapCSV.tsx @@ -65,10 +65,14 @@ export function getFileIconSrc(node: TreeNode) { return getIconSrc('file', iconName) } +// memorize for +// 1. swap time with space +// 2. prevent app crash on when extension context invalidates +const extensionURL = browser.runtime.getURL('').replace(/\/$/, '') 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}`) + return extensionURL + `/icons/vscode/${filename}` } diff --git a/webpack.config.js b/webpack.config.js index bd7bdb0..34a2eab 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -3,6 +3,7 @@ const CopyWebpackPlugin = require('copy-webpack-plugin') const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin') const path = require('path') const Dotenv = require('dotenv-webpack') +const MiniCssExtractPlugin = require('mini-css-extract-plugin') const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin const srcPath = path.resolve(__dirname, 'src') @@ -33,6 +34,7 @@ const plugins = [ ]), new ForkTsCheckerWebpackPlugin(), new Dotenv(), + new MiniCssExtractPlugin(), ] const analyse = process.env.ANALYSE !== undefined @@ -75,7 +77,7 @@ module.exports = { }, { test: /\.less$/, - loader: ['style-loader', 'css-loader', 'less-loader'], + loader: [MiniCssExtractPlugin.loader, 'css-loader', 'less-loader'], include: [srcPath], }, { diff --git a/yarn.lock b/yarn.lock index c32608c..e278fc5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4685,7 +4685,7 @@ is-path-inside@^1.0.0: dependencies: path-is-inside "^1.0.1" -is-plain-obj@^1.1.0: +is-plain-obj@^1.0.0, is-plain-obj@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e" integrity sha1-caUMhCnfync8kqOQpKA7OfzVHT4= @@ -5373,6 +5373,16 @@ mimic-response@^1.0.0, mimic-response@^1.0.1: resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b" integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ== +mini-css-extract-plugin@^0.9.0: + version "0.9.0" + resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-0.9.0.tgz#47f2cf07aa165ab35733b1fc97d4c46c0564339e" + integrity sha512-lp3GeY7ygcgAmVIcRPBVhIkf8Us7FZjA+ILpal44qLdSu11wmjKQ3d9k15lfD7pO4esu9eUIAW7qiYIBppv40A== + dependencies: + loader-utils "^1.1.0" + normalize-url "1.9.1" + schema-utils "^1.0.0" + webpack-sources "^1.1.0" + minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" @@ -5707,6 +5717,16 @@ normalize-path@^3.0.0: resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== +normalize-url@1.9.1: + version "1.9.1" + resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-1.9.1.tgz#2cc0d66b31ea23036458436e3620d85954c66c3c" + integrity sha1-LMDWazHqIwNkWENuNiDYWVTGbDw= + dependencies: + object-assign "^4.0.1" + prepend-http "^1.0.0" + query-string "^4.1.0" + sort-keys "^1.0.0" + normalize-url@^4.1.0: version "4.5.0" resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.5.0.tgz#453354087e6ca96957bd8f5baf753f5982142129" @@ -6248,6 +6268,11 @@ prelude-ls@~1.1.2: resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ= +prepend-http@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc" + integrity sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw= + prepend-http@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897" @@ -6405,6 +6430,14 @@ qs@~6.5.1, qs@~6.5.2: resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA== +query-string@^4.1.0: + version "4.3.4" + resolved "https://registry.yarnpkg.com/query-string/-/query-string-4.3.4.tgz#bbb693b9ca915c232515b228b1a02b609043dbeb" + integrity sha1-u7aTucqRXCMlFbIosaArYJBD2+s= + dependencies: + object-assign "^4.1.0" + strict-uri-encode "^1.0.0" + querystring-es3@^0.2.0: version "0.2.1" resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73" @@ -7185,6 +7218,13 @@ sonic-boom@^0.7.5: dependencies: flatstr "^1.0.12" +sort-keys@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/sort-keys/-/sort-keys-1.1.2.tgz#441b6d4d346798f1b4e49e8920adfba0e543f9ad" + integrity sha1-RBttTTRnmPG05J6JIK37oOVD+a0= + dependencies: + is-plain-obj "^1.0.0" + source-list-map@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34" @@ -7395,6 +7435,11 @@ stream-to-promise@2.2.0: end-of-stream "~1.1.0" stream-to-array "~2.3.0" +strict-uri-encode@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713" + integrity sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM= + string-width@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" @@ -7517,14 +7562,6 @@ strip-json-comments@^2.0.1, strip-json-comments@~2.0.1: resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= -style-loader@^0.23.1: - version "0.23.1" - resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-0.23.1.tgz#cb9154606f3e771ab6c4ab637026a1049174d925" - integrity sha512-XK+uv9kWwhZMZ1y7mysB+zoihsEj4wneFWAS5qoiLwzW0WzSqMrrsIy+a3zkQJq0ipFtBpX5W3MqyRIBF/WFGg== - dependencies: - 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" @@ -8202,7 +8239,7 @@ webpack-log@^2.0.0: ansi-colors "^3.0.0" uuid "^3.3.2" -webpack-sources@^1.4.0, webpack-sources@^1.4.1: +webpack-sources@^1.1.0, webpack-sources@^1.4.0, webpack-sources@^1.4.1: version "1.4.3" resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.4.3.tgz#eedd8ec0b928fbf1cbfe994e22d2d890f330a933" integrity sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==