From 44f98d9f7197df37be3894711f640c06450d15ff Mon Sep 17 00:00:00 2001 From: Enix Date: Wed, 13 Jun 2018 10:44:43 +0800 Subject: [PATCH 001/119] fix(settings-bar): prevent placeholder bar shrink --- src/content.less | 1 + 1 file changed, 1 insertion(+) diff --git a/src/content.less b/src/content.less index 6c88504..354c551 100644 --- a/src/content.less +++ b/src/content.less @@ -344,6 +344,7 @@ } } .placeholder-row { + flex-shrink: 0; display: flex; justify-content: flex-end; align-items: center; From 126c1226b736046720b4fddbdbe3e8ac91b4acac Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Wed, 13 Jun 2018 22:15:40 +0800 Subject: [PATCH 002/119] feat(build): combat with React --- packages/react-dom/index.js | 5 +++++ packages/react/index.js | 31 ++++++++++++++++++++++++++++++ src/components/FileExplorer.js | 17 ++++++++-------- src/components/Icon.js | 5 ++--- src/components/MetaBar.js | 3 +-- src/components/Node.js | 13 ++++--------- src/components/PJAXLink.js | 5 ++--- src/components/Portal.js | 3 +++ src/components/ResizeHandler.js | 5 ++--- src/components/SearchBar.js | 3 +-- src/components/SettingsBar.js | 6 ++---- src/components/SideBar.js | 9 ++++----- src/components/ToggleShowButton.js | 3 +-- src/content.js | 6 +++--- src/utils/general.js | 19 +----------------- webpack.config.js | 6 ++---- 16 files changed, 72 insertions(+), 67 deletions(-) create mode 100644 packages/react-dom/index.js create mode 100644 packages/react/index.js create mode 100644 src/components/Portal.js diff --git a/packages/react-dom/index.js b/packages/react-dom/index.js new file mode 100644 index 0000000..8681d19 --- /dev/null +++ b/packages/react-dom/index.js @@ -0,0 +1,5 @@ +import preact from 'preact' + +export default { + render: preact.render, +} diff --git a/packages/react/index.js b/packages/react/index.js new file mode 100644 index 0000000..003a8d1 --- /dev/null +++ b/packages/react/index.js @@ -0,0 +1,31 @@ +import preact from 'preact' +/** @jsx preact.h */ + +function shallowEqual(a, b) { + if (a === b) return true + if (typeof a === 'object' && typeof a === typeof b) { + if (a === null || b === null) return false + for (const key in a) { + if (a[key] !== b[key]) return false + } + for (const key in b) { + if (!Object.prototype.hasOwnProperty.call(a, key)) return false + } + return true + } + return false +} + +class PureComponent extends preact.Component { + shouldComponentUpdate(nextProps, nextState) { + return !shallowEqual(this.props, nextProps) || !shallowEqual(this.state, nextState) + } +} + +const React = { + ...preact, + PureComponent, + createElement: preact.h, +} + +export default React diff --git a/src/components/FileExplorer.js b/src/components/FileExplorer.js index dc1b32f..499547b 100644 --- a/src/components/FileExplorer.js +++ b/src/components/FileExplorer.js @@ -1,5 +1,4 @@ -import preact from 'preact' -/** @jsx preact.h */ +import React from 'react' import SearchBar from './SearchBar' import Node from './Node' @@ -9,18 +8,18 @@ import treeParser from '../utils/treeParser' import URLHelper from '../utils/URLHelper' import VisibleNodesGenerator from '../utils/VisibleNodesGenerator' -export default class List extends preact.Component { - state = { - // generated by this.visibleNodesGenerator - visibleNodes: null, - } - - props = { +export default class List extends React.Component { + static defaultProps = { treeData: null, metaData: null, freeze: false, } + state = { + // generated by this.visibleNodesGenerator + visibleNodes: null, + } + tasksAfterRender = [] visibleNodesGenerator = new VisibleNodesGenerator() diff --git a/src/components/Icon.js b/src/components/Icon.js index ca64f43..2673590 100644 --- a/src/components/Icon.js +++ b/src/components/Icon.js @@ -1,5 +1,4 @@ -import preact from 'preact' -/** @jsx preact.h */ +import React from 'react' import Grabber from '../assets/icons/octicons/grabber.svg?svgr' import Octoface from '../assets/icons/octicons/octoface.svg?svgr' @@ -71,7 +70,7 @@ function getSVGIconComponent(type) { export default function Icon({ type, className, ...otherProps }) { return (
- {preact.h(getSVGIconComponent(type), { width: '100%', height: '100%' })} + {React.createElement(getSVGIconComponent(type), { width: '100%', height: '100%' })}
) } diff --git a/src/components/MetaBar.js b/src/components/MetaBar.js index eee74dd..740c4d0 100644 --- a/src/components/MetaBar.js +++ b/src/components/MetaBar.js @@ -1,5 +1,4 @@ -import preact from 'preact' -/** @jsx preact.h */ +import React from 'react' export default function MetaBar({ metaData }) { const userUrl = metaData ? metaData.api && metaData.api.owner.html_url : undefined diff --git a/src/components/Node.js b/src/components/Node.js index 0db724b..0395d3f 100644 --- a/src/components/Node.js +++ b/src/components/Node.js @@ -1,5 +1,4 @@ -import preact from 'preact' -/** @jsx preact.h */ +import React from 'react' import PJAX from 'pjax' import Icon from './Icon' @@ -17,11 +16,7 @@ function getIconType(node) { } } -export default class Node extends preact.Component { - shouldComponentUpdate(nextProps) { - return !general.shallowEqual(this.props, nextProps) - } - +export default class Node extends React.PureComponent { onNodeClick = (...args) => { const { node, toggleExpand } = this.props if (node.type === 'tree') { @@ -36,14 +31,14 @@ export default class Node extends preact.Component { const { name, path } = node return (
-

{name} -

+
) } diff --git a/src/components/PJAXLink.js b/src/components/PJAXLink.js index 79eaa0b..6d4c723 100644 --- a/src/components/PJAXLink.js +++ b/src/components/PJAXLink.js @@ -1,9 +1,8 @@ -import preact from 'preact' -/** @jsx preact.h */ +import React from 'react' import DOMHelper from '../utils/DOMHelper' export default function PJAXLink({ to, children }) { - return preact.cloneElement(children, { + return React.cloneElement(children, { onClick: () => DOMHelper.loadWithPJAX(to), }) } diff --git a/src/components/Portal.js b/src/components/Portal.js new file mode 100644 index 0000000..2ac09ea --- /dev/null +++ b/src/components/Portal.js @@ -0,0 +1,3 @@ +import Portal from 'preact-portal' + +export default Portal diff --git a/src/components/ResizeHandler.js b/src/components/ResizeHandler.js index b95b5b0..4fac68d 100644 --- a/src/components/ResizeHandler.js +++ b/src/components/ResizeHandler.js @@ -1,9 +1,8 @@ -import preact from 'preact' -/** @jsx preact.h */ +import React from 'react' import Icon from './Icon' -export default class ResizeHandler extends preact.Component { +export default class ResizeHandler extends React.Component { pointerDown = false startX = 0 delta = 0 diff --git a/src/components/SearchBar.js b/src/components/SearchBar.js index bd50951..9b8508e 100644 --- a/src/components/SearchBar.js +++ b/src/components/SearchBar.js @@ -1,5 +1,4 @@ -import preact from 'preact' -/** @jsx preact.h */ +import React from 'react' export default function SearchBar({ onSearchKeyChange }) { return ( diff --git a/src/components/SettingsBar.js b/src/components/SettingsBar.js index 97b4737..2cae7b8 100644 --- a/src/components/SettingsBar.js +++ b/src/components/SettingsBar.js @@ -1,6 +1,4 @@ -import preact from 'preact' -/** @jsx preact.h */ - +import React from 'react' import Icon from './Icon' import storageHelper from '../utils/storageHelper' import keyHelper from '../utils/keyHelper' @@ -39,7 +37,7 @@ function friendlyFormatShortcut(shortcut) { } } -export default class SettingsBar extends preact.Component { +export default class SettingsBar extends React.Component { state = { accessTokenHint: null, accessToken: '', diff --git a/src/components/SideBar.js b/src/components/SideBar.js index e0ea8e0..ed561ef 100644 --- a/src/components/SideBar.js +++ b/src/components/SideBar.js @@ -1,13 +1,12 @@ -import preact from 'preact' -import Portal from 'preact-portal' +import React from 'react' import NProgress from 'nprogress' -/** @jsx preact.h */ import FileExplorer from './FileExplorer' import ToggleShowButton from './ToggleShowButton' import MetaBar from './MetaBar' import SettingsBar from './SettingsBar' import ResizeHandler from './ResizeHandler' +import Portal from './Portal' import cx from '../utils/cx' import DOMHelper, { REPO_TYPE_PRIVATE } from '../utils/DOMHelper' @@ -18,7 +17,7 @@ import keyHelper from '../utils/keyHelper' // initial width of side bar const baseSize = 260 -export default class SideBar extends preact.Component { +export default class SideBar extends React.Component { state = { // current width of side bar size: 260, @@ -58,8 +57,8 @@ export default class SideBar extends preact.Component { NProgress.start() } const treeData = await GitHubHelper.getTreeData({ ...metaData, accessToken }) - this.setState({ treeData }) this.logoContainerElement = DOMHelper.insertLogo() + this.setState({ treeData }) if (shouldShow) { NProgress.done() } diff --git a/src/components/ToggleShowButton.js b/src/components/ToggleShowButton.js index a38a445..8d46312 100644 --- a/src/components/ToggleShowButton.js +++ b/src/components/ToggleShowButton.js @@ -1,5 +1,4 @@ -import preact from 'preact' -/** @jsx preact.h */ +import React from 'react' import Icon from './Icon' import cx from '../utils/cx' diff --git a/src/content.js b/src/content.js index ad6f38b..8fecdf4 100644 --- a/src/content.js +++ b/src/content.js @@ -1,5 +1,5 @@ -import preact from 'preact' -/** @jsx preact.h */ +import React from 'react' +import ReactDOM from 'react-dom' import './content.less' @@ -7,4 +7,4 @@ import SideBar from './components/SideBar' const SideBarElement = document.createElement('div') document.body.appendChild(SideBarElement) -preact.render(, SideBarElement) +ReactDOM.render(, SideBarElement) diff --git a/src/utils/general.js b/src/utils/general.js index 27f4b95..b1c6ea4 100644 --- a/src/utils/general.js +++ b/src/utils/general.js @@ -1,18 +1 @@ -function shallowEqual(a, b) { - if (a === b) return true - if (typeof a === 'object' && typeof a === typeof b) { - if (a === null || b === null) return false - for (const key in a) { - if (a[key] !== b[key]) return false - } - for (const key in b) { - if (!Object.prototype.hasOwnProperty.call(a, key)) return false - } - return true - } - return false -} - -export default { - shallowEqual, -} +export default {} diff --git a/webpack.config.js b/webpack.config.js index fb31c6f..33d2ef8 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -4,6 +4,7 @@ const CopyWebpackPlugin = require('copy-webpack-plugin') const UglifyJSWebpackPlugin = require('uglifyjs-webpack-plugin') const srcPath = path.resolve(__dirname, 'src') +const packagesPath = path.resolve(__dirname, 'packages') const plugins = [ new CopyWebpackPlugin([ @@ -46,9 +47,6 @@ module.exports = { filename: '[name].js', }, resolve: { - alias: { - react: 'preact', // for svgr - }, modules: ['packages', 'node_modules'] }, module: { @@ -59,7 +57,7 @@ module.exports = { options: { cacheDirectory: true, }, - include: [srcPath], + include: [srcPath, packagesPath], }, { test: /\.less$/, From 46436b0575ea66e472c2ce9b7e6d5d03fdd2299b Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Wed, 13 Jun 2018 23:22:52 +0800 Subject: [PATCH 003/119] refactor(settings): key code & controlled component --- src/components/SettingsBar.js | 4 ++-- src/utils/keyHelper.js | 5 +---- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/components/SettingsBar.js b/src/components/SettingsBar.js index 2cae7b8..9c41f00 100644 --- a/src/components/SettingsBar.js +++ b/src/components/SettingsBar.js @@ -21,7 +21,7 @@ function detectOS() { } function friendlyFormatShortcut(shortcut) { - if (!shortcut) return + if (typeof shortcut !== 'string') return '' const OS = detectOS() if (OS === OperatingSystems.Windows) { return shortcut.replace(/meta/, 'win') @@ -158,9 +158,9 @@ export default class SettingsBar extends React.Component { ) : ( - )} diff --git a/src/components/SideBar.js b/src/components/SideBar.js index 0ca37d0..fdf6dca 100644 --- a/src/components/SideBar.js +++ b/src/components/SideBar.js @@ -24,8 +24,8 @@ export default class Gitako extends React.PureComponent { showSettings: PropTypes.bool, // whether failed loading the repo due to it is private errorDueToAuth: PropTypes.bool, - // got access token for GitHub - hasAccessToken: PropTypes.bool, + // access token for GitHub + accessToken: PropTypes.string, // the shortcut string for toggle sidebar toggleShowSideBarShortcut: PropTypes.string, // meta data for the repository @@ -38,7 +38,7 @@ export default class Gitako extends React.PureComponent { setShouldShow: PropTypes.func.isRequired, toggleShowSideBar: PropTypes.func.isRequired, toggleShowSettings: PropTypes.func.isRequired, - onHasAccessTokenChange: PropTypes.func.isRequired, + onAccessTokenChange: PropTypes.func.isRequired, onKeyDown: PropTypes.func.isRequired, onShortcutChange: PropTypes.func.isRequired, onResize: PropTypes.func.isRequired, @@ -51,7 +51,7 @@ export default class Gitako extends React.PureComponent { shouldShow: false, showSettings: false, errorDueToAuth: false, - hasAccessToken: false, + accessToken: '', toggleShowSideBarShortcut: '', metaData: null, treeData: null, @@ -88,14 +88,14 @@ export default class Gitako extends React.PureComponent { } renderContent() { - const { errorDueToAuth, metaData, treeData, showSettings } = this.props + const { errorDueToAuth, metaData, treeData, showSettings, accessToken } = this.props return (
{metaData && } {errorDueToAuth && this.renderAccessDeniedError()} {metaData && treeData && ( - + )}
) @@ -106,14 +106,14 @@ export default class Gitako extends React.PureComponent { size, shouldShow, showSettings, - hasAccessToken, + accessToken, toggleShowSideBarShortcut, logoContainerElement, toggleShowSideBar, onResize, toggleShowSettings, onShortcutChange, - onHasAccessTokenChange, + onAccessTokenChange, } = this.props return (
@@ -127,9 +127,9 @@ export default class Gitako extends React.PureComponent {
diff --git a/src/driver/core.js b/src/driver/core.js index ab964ec..eae8012 100644 --- a/src/driver/core.js +++ b/src/driver/core.js @@ -15,7 +15,7 @@ const init = dispatch => async () => { storageHelper.getAccessToken(), storageHelper.getShortcut(), ]) - dispatch({ hasAccessToken: Boolean(accessToken), toggleShowSideBarShortcut: shortcut }) + dispatch({ accessToken, toggleShowSideBarShortcut: shortcut }) const metaDataFromAPI = await GitHubHelper.getRepoMeta({ ...metaData, accessToken }) const branchName = metaData.branchName || metaDataFromAPI['default_branch'] Object.assign(metaData, { branchName, api: metaDataFromAPI }) @@ -81,23 +81,21 @@ const onResize = dispatch => size => dispatch({ size }) const toggleShowSettings = dispatch => () => dispatch(({ showSettings }) => ({ showSettings: !showSettings })) -const onHasAccessTokenChange = dispatch => hasAccessToken => dispatch({ hasAccessToken }) +const onAccessTokenChange = dispatch => accessToken => dispatch({ accessToken }) const onShortcutChange = dispatch => shortcut => dispatch({ toggleShowSideBarShortcut: shortcut }) const setMetaData = dispatch => metaData => dispatch({ metaData }) -export default function(instance) { - return link(instance.setState.bind(instance), { - init, - onPJAXEnd, - onKeyDown, - setShouldShow, - toggleShowSideBar, - toggleShowSettings, - onHasAccessTokenChange, - onShortcutChange, - onResize, - setMetaData, - }) +export default { + init, + onPJAXEnd, + onKeyDown, + setShouldShow, + toggleShowSideBar, + toggleShowSettings, + onAccessTokenChange, + onShortcutChange, + onResize, + setMetaData, } From 84d1859991c82c51cee4920961d08d1bac994d75 Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Fri, 22 Jun 2018 21:42:44 +0800 Subject: [PATCH 022/119] refactor: remove link --- src/driver/connect.js | 33 ++++++++++++++++++++++++++++++--- src/driver/core.js | 2 -- src/driver/link.js | 20 -------------------- 3 files changed, 30 insertions(+), 25 deletions(-) delete mode 100644 src/driver/link.js diff --git a/src/driver/connect.js b/src/driver/connect.js index 76d633a..c526482 100644 --- a/src/driver/connect.js +++ b/src/driver/connect.js @@ -1,16 +1,43 @@ import React from 'react' -export default function connect(core) { +function isObject(target) { + return typeof target === 'object' && target !== null +} + +function link(instance, sources) { + const wrappedMethods = {/* sources[key] -> wrappedMethods.method */} + const map = new Map(/* sources.creator -> wrappedMethods.method */) + + function dispatch(...args) { + if (Object.values(sources).includes(args[0])) { + map.get(args[0])(...args.slice(1)) + } else { + setTimeout( + instance.setState.bind(instance, ...args), + ) + } + } + + Object.entries(sources).forEach(([key, createMethod]) => { + const method = createMethod(dispatch) + wrappedMethods[key] = method + map.set(createMethod, method) + }) + + return wrappedMethods +} + +export default function connect(mapping) { return function linkComponent(ComponentClass) { return class AwesomeApp extends React.PureComponent { static displayName = `Driven${ComponentClass.name}` state = {} - boundCore = core(this) + boundCore = link(this, mapping) render() { return ( - + ) } } diff --git a/src/driver/core.js b/src/driver/core.js index eae8012..ea80664 100644 --- a/src/driver/core.js +++ b/src/driver/core.js @@ -1,5 +1,3 @@ -import link from './link' - import DOMHelper, { REPO_TYPE_PRIVATE } from '../utils/DOMHelper' import GitHubHelper, { NOT_FOUND, BAD_CREDENTIALS } from '../utils/GitHubHelper' import storageHelper from '../utils/storageHelper' diff --git a/src/driver/link.js b/src/driver/link.js deleted file mode 100644 index 2e1a5e7..0000000 --- a/src/driver/link.js +++ /dev/null @@ -1,20 +0,0 @@ -export default function link(setState, sources) { - const wrappedMethods = {/* sources[key] -> wrappedMethods.method */} - const map = new Map(/* sources.creator -> wrappedMethods.method */) - - function dispatch(...args) { - if (Object.values(sources).includes(args[0])) { - map.get(args[0])(...args.slice(1)) - } else { - setState(...args) - } - } - - Object.entries(sources).forEach(([key, createMethod]) => { - const method = createMethod(dispatch) - wrappedMethods[key] = method - map.set(createMethod, method) - }) - - return wrappedMethods -} From 0bfca704a484bd9c9d1259bfe5776397abdf5671 Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Fri, 22 Jun 2018 22:00:07 +0800 Subject: [PATCH 023/119] refactor(FileExplorer): split logic --- src/components/FileExplorer.js | 394 ++++++++++++++++------------- src/utils/visibleNodesGenerator.js | 5 +- 2 files changed, 223 insertions(+), 176 deletions(-) diff --git a/src/components/FileExplorer.js b/src/components/FileExplorer.js index 43f54c9..d50a282 100644 --- a/src/components/FileExplorer.js +++ b/src/components/FileExplorer.js @@ -1,4 +1,8 @@ import React from 'react' +import PropTypes from "prop-types"; + +import connect from '../driver/connect' + import SearchBar from './SearchBar' import Node from './Node' @@ -22,219 +26,259 @@ function getVisibleParentNode(nodes, focusedNode, depths) { return parentNode } -export default class List extends React.Component { - static defaultProps = { - treeData: null, - metaData: null, - freeze: false, - } +const tasksAfterRender = [] +const visibleNodesGenerator = new VisibleNodesGenerator() - state = { - // generated by this.visibleNodesGenerator - visibleNodes: null, - } - - tasksAfterRender = [] - visibleNodesGenerator = new VisibleNodesGenerator() - - componentWillMount() { - const { treeData, metaData } = this.props - const { root, nodes } = treeParser.parse(treeData, metaData) - this.visibleNodesGenerator.plantTree(root, nodes) - const currentPath = URLHelper.getCurrentPath(true) - this.tasksAfterRender.push(DOMHelper.focusSearchInput) - if (currentPath.length) { - const nodeExpandedTo = this.visibleNodesGenerator.expandTo(currentPath) - if (nodeExpandedTo) { - this.visibleNodesGenerator.focusNode(nodeExpandedTo) - const { nodes } = this.visibleNodesGenerator.visibleNodes - this.tasksAfterRender.push(() => DOMHelper.scrollToNodeElement(nodes.indexOf(nodeExpandedTo))) - } +const init = dispatch => () => dispatch(async (state, { treeData, metaData, accessToken }) => { + const { root, nodes } = treeParser.parse(treeData, metaData) + visibleNodesGenerator.plantTree(root, nodes) + const currentPath = URLHelper.getCurrentPath(true) + tasksAfterRender.push(DOMHelper.focusSearchInput) + if (currentPath.length) { + const nodeExpandedTo = visibleNodesGenerator.expandTo(currentPath) + if (nodeExpandedTo) { + visibleNodesGenerator.focusNode(nodeExpandedTo) + const { nodes } = visibleNodesGenerator.visibleNodes + tasksAfterRender.push(() => DOMHelper.scrollToNodeElement(nodes.indexOf(nodeExpandedTo))) } - this.updateVisibleNodes() } + dispatch(updateVisibleNodes) +}) - componentDidMount() { - this.execAfterRender() +const execAfterRender = dispatch => () => { + for (const task of tasksAfterRender) { + task() } + tasksAfterRender.length = 0 +} - componentDidUpdate(prevProps, prevState) { - this.execAfterRender() - } +const handleKeyDown = dispatch => ({ key }) => dispatch(({ visibleNodes: { nodes, focusedNode, expandedNodes, depths } }) => { + let shouldStopPropagation = true // prevent body scrolling + if (focusedNode) { + const focusedNodeIndex = nodes.indexOf(focusedNode) + switch (key) { + case 'ArrowUp': + // focus on previous node + if (focusedNodeIndex === 0) { + dispatch(focusNode, null) + tasksAfterRender.push(DOMHelper.focusSearchInput) + } else { + dispatch(focusNode, nodes[focusedNodeIndex - 1]) + } + break - execAfterRender() { - for (const task of this.tasksAfterRender) { - task() - } - this.tasksAfterRender.length = 0 - } + case 'ArrowDown': + // focus on next node + if (focusedNodeIndex + 1 < nodes.length) { + dispatch(focusNode, nodes[focusedNodeIndex + 1]) + } else { + dispatch(focusNode, null) + tasksAfterRender.push(DOMHelper.focusSearchInput) + } + break - updateVisibleNodes() { - const { visibleNodes } = this.visibleNodesGenerator - this.setState({ visibleNodes }) - } - - handleKeyDown = event => { - const { key } = event - const { visibleNodes: { nodes, focusedNode, expandedNodes, depths } } = this.state - let shouldStopPropagation = true // prevent body scrolling - if (focusedNode) { - const focusedNodeIndex = nodes.indexOf(focusedNode) - switch (key) { - case 'ArrowUp': - // focus on previous node - if (focusedNodeIndex === 0) { - this.focusNode(null) - this.tasksAfterRender.push(DOMHelper.focusSearchInput) - } else { - this.focusNode(nodes[focusedNodeIndex - 1]) + case 'ArrowLeft': + // collapse node or go to parent node + if (expandedNodes.has(focusedNode)) { + dispatch(setExpand, focusedNode, false) + } else { + // go forward to the start of the list, find the closest node with lower depth + const parentNode = getVisibleParentNode(nodes, focusedNode, depths) + if (parentNode) { + dispatch(focusNode, parentNode) } - break + } + break - case 'ArrowDown': - // focus on next node - if (focusedNodeIndex + 1 < nodes.length) { - this.focusNode(nodes[focusedNodeIndex + 1]) - } else { - this.focusNode(null) - this.tasksAfterRender.push(DOMHelper.focusSearchInput) - } - break - - case 'ArrowLeft': - // collapse node or go to parent node + // consider the two keys as 'confirm' key + case 'ArrowRight': + // expand node or focus on first content node or redirect to file page + if (focusedNode.type === 'tree') { if (expandedNodes.has(focusedNode)) { - this.setExpand(focusedNode, false) + const nextNode = nodes[focusedNodeIndex + 1] + if (depths.get(nextNode) > depths.get(focusedNode)) { + dispatch(focusNode, nextNode) + } } else { - // go forward to the start of the list, find the closest node with lower depth - const parentNode = getVisibleParentNode(nodes, focusedNode, depths) - if (parentNode) { - this.focusNode(parentNode) - } + dispatch(setExpand, focusedNode, true) } - break + } else if (focusedNode.type === 'blob') { + DOMHelper.loadWithPJAX(focusedNode.url) + } else if (focusedNode.type === 'commit') { + // redirect to its parent folder + DOMHelper.loadWithPJAX(focusedNode.parent.url) + } + break + case 'Enter': + // expand node or redirect to file page + if (focusedNode.type === 'tree') { + dispatch(setExpand, focusedNode, true) + } else if (focusedNode.type === 'blob') { + DOMHelper.loadWithPJAX(focusedNode.url) + } else if (focusedNode.type === 'commit') { + // redirect to its parent folder + DOMHelper.loadWithPJAX(focusedNode.parent.url) + } + break - // consider the two keys as 'confirm' key - case 'ArrowRight': - // expand node or focus on first content node or redirect to file page - if (focusedNode.type === 'tree') { - if (expandedNodes.has(focusedNode)) { - const nextNode = nodes[focusedNodeIndex + 1] - if (depths.get(nextNode) > depths.get(focusedNode)) { - this.focusNode(nextNode) - } - } else { - this.setExpand(focusedNode, true) - } - } else if (focusedNode.type === 'blob') { - DOMHelper.loadWithPJAX(focusedNode.url) - } else if (focusedNode.type === 'commit') { - // redirect to its parent folder - DOMHelper.loadWithPJAX(focusedNode.parent.url) - } + default: + shouldStopPropagation = false + } + } else { + // now search input is focused + if (nodes.length) { + switch (key) { + case 'ArrowDown': + dispatch(focusNode, nodes[0]) break - case 'Enter': - // expand node or redirect to file page - if (focusedNode.type === 'tree') { - this.setExpand(focusedNode, true) - } else if (focusedNode.type === 'blob') { - DOMHelper.loadWithPJAX(focusedNode.url) - } else if (focusedNode.type === 'commit') { - // redirect to its parent folder - DOMHelper.loadWithPJAX(focusedNode.parent.url) - } + case 'ArrowUp': + dispatch(focusNode, nodes[nodes.length - 1]) break - default: shouldStopPropagation = false } } else { - // now search input is focused - if (nodes.length) { - switch (key) { - case 'ArrowDown': - this.focusNode(nodes[0]) - break - case 'ArrowUp': - this.focusNode(nodes[nodes.length - 1]) - break - default: - shouldStopPropagation = false - } - } else { - shouldStopPropagation = false - } - } - if (shouldStopPropagation) { - event.stopPropagation() - event.preventDefault() + shouldStopPropagation = false } } + if (shouldStopPropagation) { + event.stopPropagation() + event.preventDefault() + } +}) - handleSearchKeyChange = async event => { - const searchKey = event.target.value - await this.visibleNodesGenerator.search(searchKey) - this.updateVisibleNodes() +const handleSearchKeyChange = dispatch => async event => { + const searchKey = event.target.value + await visibleNodesGenerator.search(searchKey) + dispatch(updateVisibleNodes) +} + +const setExpand = dispatch => (node, expand) => { + visibleNodesGenerator.setExpand(node, expand) + dispatch(focusNode, node) + tasksAfterRender.push(DOMHelper.focusSearchInput) +} + +const toggleNodeExpansion = dispatch => (node, skipScrollToNode) => { + visibleNodesGenerator.toggleExpand(node) + dispatch(focusNode, node, skipScrollToNode) + tasksAfterRender.push(DOMHelper.focusFileExplorer) +} + +const focusNode = dispatch => (node, skipScroll) => dispatch(({ visibleNodes: { nodes } }) => { + visibleNodesGenerator.focusNode(node) + if (node && !skipScroll) { + // when focus a node not in viewport(by keyboard), scroll to it + const indexOfToBeFocusedNode = nodes.indexOf(node) + tasksAfterRender.push(() => DOMHelper.scrollToNodeElement(indexOfToBeFocusedNode)) + tasksAfterRender.push(DOMHelper.focusSearchInput) + } + dispatch(updateVisibleNodes) +}) + +const onNodeClick = dispatch => (node) => { + if (node.type === 'tree') { + dispatch(toggleNodeExpansion, node, true) + } else if (node.type === 'blob') { + dispatch(focusNode, node, true) + DOMHelper.loadWithPJAX(node.url) + } else if (node.type === 'commit') { + DOMHelper.loadWithPJAX(node.parent.url) + } +} + +const updateVisibleNodes = dispatch => () => { + const { visibleNodes } = visibleNodesGenerator + dispatch({ visibleNodes }) +} + +class List extends React.Component { + static propTyps = { + treeData: PropTypes.object, + metaData: PropTypes.object, + freeze: PropTypes.boolean, + visibleNodes: PropTypes.object, + + init: PropTypes.func.isRequired, + execAfterRender: PropTypes.func.isRequired, + handleKeyDown: PropTypes.func.isRequired, + handleSearchKeyChange: PropTypes.func.isRequired, + setExpand: PropTypes.func.isRequired, + toggleNodeExpansion: PropTypes.func.isRequired, + focusNode: PropTypes.func.isRequired, + onNodeClick: PropTypes.func.isRequired, + updateVisibleNodes: PropTypes.func.isRequired, } - setExpand = (node, expand) => { - this.visibleNodesGenerator.setExpand(node, expand) - this.focusNode(node) - this.tasksAfterRender.push(DOMHelper.focusSearchInput) + static defaultProps = { + treeData: null, + metaData: null, + freeze: false, + visibleNodes: null, } - toggleNodeExpand = (node, skipScrollToNode) => { - this.visibleNodesGenerator.toggleExpand(node) - this.focusNode(node, skipScrollToNode) - this.tasksAfterRender.push(DOMHelper.focusFileExplorer) + constructor(props) { + super(props) + const { init } = props + init() } - focusNode = (node, skipScroll) => { - this.visibleNodesGenerator.focusNode(node) - if (node && !skipScroll) { - // when focus a node not in viewport(by keyboard), scroll to it - const { visibleNodes: { nodes } } = this.state - const indexOfToBeFocusedNode = nodes.indexOf(node) - this.tasksAfterRender.push(() => DOMHelper.scrollToNodeElement(indexOfToBeFocusedNode)) - this.tasksAfterRender.push(DOMHelper.focusSearchInput) - } - this.updateVisibleNodes() + componentDidMount() { + const { execAfterRender } = this.props + execAfterRender() } - onNodeClick = (node) => { - if (node.type === 'tree') { - this.toggleNodeExpand(node, true) - } else if (node.type === 'blob') { - this.focusNode(node, true) - DOMHelper.loadWithPJAX(node.url) - } else if (node.type === 'commit') { - DOMHelper.loadWithPJAX(node.parent.url) - } + componentDidUpdate() { + const { execAfterRender } = this.props + execAfterRender() } render() { - const { visibleNodes: { nodes, depths, focusedNode, expandedNodes } } = this.state - const { freeze } = this.props + const { + visibleNodes, + freeze, + handleKeyDown, + handleSearchKeyChange, + onNodeClick + } = this.props + const { + nodes, + depths, + focusedNode, + expandedNodes, + } = visibleNodes || {} return ( -
- - {nodes.length === 0 ? ( +
+ + {!visibleNodes || !nodes || nodes.length === 0 ? ( ) : ( -
- {nodes.map(node => ( - - ))} -
- )} +
+ {nodes.map(node => ( + + ))} +
+ )}
) } } + +export default connect({ + init, + execAfterRender, + handleKeyDown, + handleSearchKeyChange, + setExpand, + toggleNodeExpansion, + focusNode, + onNodeClick, + updateVisibleNodes, +})(List) diff --git a/src/utils/visibleNodesGenerator.js b/src/utils/visibleNodesGenerator.js index 53a4c66..8a735ef 100644 --- a/src/utils/visibleNodesGenerator.js +++ b/src/utils/visibleNodesGenerator.js @@ -131,6 +131,9 @@ export default class VisibleNodesGenerator { focusedNode = null focusNode(node) { this.focusedNode = node - this.visibleNodes.focusedNode = node + this.visibleNodes = { + ...this.visibleNodes, + focusedNode: node, + } } } From 568221171d06b7f2d9ed767634dfbe30299bce29 Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Fri, 22 Jun 2018 22:44:08 +0800 Subject: [PATCH 024/119] refactor: transition for all Fixes #12 --- src/content.less | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/content.less b/src/content.less index 354c551..936c787 100644 --- a/src/content.less +++ b/src/content.less @@ -83,12 +83,9 @@ height: 30px; will-change: transform; border: 1px solid transparent; + transition: all @animation-duration ease; - @media screen and (max-width: @width-with-gitako) { - transition: all @animation-duration ease; - } @media screen and (min-width: @width-with-gitako) { - transition: border-color, border-radius, background @animation-duration ease; transform: translate(~'calc(50vw - (' @github-content-width ~') / 2 - 30px)'); } From 6c7823097b32e27016e76d823a41e2c6be9c4bfa Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Sun, 24 Jun 2018 18:39:20 +0800 Subject: [PATCH 025/119] feat: compress singleton folder --- src/components/FileExplorer.js | 6 +-- src/utils/treeParser.js | 3 +- src/utils/visibleNodesGenerator.js | 79 ++++++++++++++++++++++++------ 3 files changed, 67 insertions(+), 21 deletions(-) diff --git a/src/components/FileExplorer.js b/src/components/FileExplorer.js index d50a282..82e3869 100644 --- a/src/components/FileExplorer.js +++ b/src/components/FileExplorer.js @@ -30,12 +30,12 @@ const tasksAfterRender = [] const visibleNodesGenerator = new VisibleNodesGenerator() const init = dispatch => () => dispatch(async (state, { treeData, metaData, accessToken }) => { - const { root, nodes } = treeParser.parse(treeData, metaData) - visibleNodesGenerator.plantTree(root, nodes) + const { root } = treeParser.parse(treeData, metaData) + await visibleNodesGenerator.plantTree(root) const currentPath = URLHelper.getCurrentPath(true) tasksAfterRender.push(DOMHelper.focusSearchInput) if (currentPath.length) { - const nodeExpandedTo = visibleNodesGenerator.expandTo(currentPath) + const nodeExpandedTo = visibleNodesGenerator.expandTo(currentPath.join('/')) if (nodeExpandedTo) { visibleNodesGenerator.focusNode(nodeExpandedTo) const { nodes } = visibleNodesGenerator.visibleNodes diff --git a/src/utils/treeParser.js b/src/utils/treeParser.js index 37f75ee..39df1f4 100644 --- a/src/utils/treeParser.js +++ b/src/utils/treeParser.js @@ -11,7 +11,7 @@ const nodeTemplate = { function sortFoldersToFront(root) { const isFolder = node => node.type === 'tree' - const isNotFolder = (...args) => !isFolder(...args) + const isNotFolder = node => !isFolder(node) function depthFirstSearch(root) { const nodes = root.contents if (nodes) { @@ -73,7 +73,6 @@ function parse(treeData, metaData) { setParentNode(root) return { root: sortFoldersToFront(root), - nodes: Array.from(pathToNode.values()), } } diff --git a/src/utils/visibleNodesGenerator.js b/src/utils/visibleNodesGenerator.js index 8a735ef..06a400c 100644 --- a/src/utils/visibleNodesGenerator.js +++ b/src/utils/visibleNodesGenerator.js @@ -26,7 +26,7 @@ function getFilterFunc(keyRegex) { } function search(treeNodes, searchKey) { - if (!searchKey) return + if (!searchKey) return treeNodes /** * if searchKey is 'abcd' * then keyRegex will be /a.*?b.*?c.*?d/i @@ -53,27 +53,67 @@ function debounce(func, delay) { export const debouncedSearch = debounce(search, 250) +function getNodes(root) { + if (!root.contents) return [] + return [].concat( + ...root.contents.map(node => [node, ...getNodes(node)]) + ) +} + +function compressTree(root, prefix = []) { + if (root.contents) { + if (root.contents.length === 1) { + const singleton = root.contents[0] + if (singleton.type === 'tree') { + return compressTree(singleton, [...prefix, root.name]) + } + } + } + return { + ...root, + name: [...prefix, root.name].join('/'), + contents: root.contents + ? root.contents.map(node => compressTree(node)) + : undefined, + } +} + export default class VisibleNodesGenerator { // LEVEL 1 root = null nodes = null - plantTree(root, nodes) { - this.root = root - this.nodes = nodes + compressed = false - // a simplified sync 'search' - this.searchedNodes = this.root.contents - this.generateVisibleNodes() + getRoot() { + return this.compress && this.compressed + ? this.compressedRoot + : this.root + } + + async plantTree(root) { + this.root = root + this.nodes = getNodes(root) + this.compressedRoot = compressTree(root) + + await this.search() } // LEVEL 2 searchedNodes = null async search(searchKey) { - this.searchedNodes = (await debouncedSearch(this.nodes, searchKey)) || this.root.contents + this.compressed = !Boolean(searchKey) + this.searchedNodes = searchKey + ? await debouncedSearch(this.nodes, searchKey) + : this.getRoot().contents + this.expandedNodes.clear() this.generateVisibleNodes() } + setCompress(compress) { + this.compress = compress + } + // LEVEL 3 expandedNodes = new Set() depths = new Map() @@ -92,15 +132,22 @@ export default class VisibleNodesGenerator { } expandTo(path) { - let rootNode = this.root - let targetPath - for (const step of path) { - targetPath = rootNode.path ? `${rootNode.path}/${step}` : step - rootNode = rootNode.contents.find(node => node.path === targetPath) - if (!rootNode) return - this.setExpand(rootNode, true) + let root = this.getRoot() + const findNode = (root) => { + if (path.indexOf(root.path) === 0) { + if (root.path === path) return root + this.setExpand(root, true) + if (root.contents) { + for (const content of root.contents) { + const node = findNode(content) + if (node) return node + } + } + } } - return rootNode + const node = findNode(root) + this.focusNode(node) + return node } visibleNodes = null From be9f258be3fd58d3c2fe9db38987dff41c43363c Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Sun, 24 Jun 2018 20:02:26 +0800 Subject: [PATCH 026/119] refactor(FileExplorer): stop blocking event --- src/components/FileExplorer.js | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/components/FileExplorer.js b/src/components/FileExplorer.js index 82e3869..eac0f62 100644 --- a/src/components/FileExplorer.js +++ b/src/components/FileExplorer.js @@ -53,7 +53,6 @@ const execAfterRender = dispatch => () => { } const handleKeyDown = dispatch => ({ key }) => dispatch(({ visibleNodes: { nodes, focusedNode, expandedNodes, depths } }) => { - let shouldStopPropagation = true // prevent body scrolling if (focusedNode) { const focusedNodeIndex = nodes.indexOf(focusedNode) switch (key) { @@ -121,8 +120,6 @@ const handleKeyDown = dispatch => ({ key }) => dispatch(({ visibleNodes: { nodes } break - default: - shouldStopPropagation = false } } else { // now search input is focused @@ -134,17 +131,9 @@ const handleKeyDown = dispatch => ({ key }) => dispatch(({ visibleNodes: { nodes case 'ArrowUp': dispatch(focusNode, nodes[nodes.length - 1]) break - default: - shouldStopPropagation = false } - } else { - shouldStopPropagation = false } } - if (shouldStopPropagation) { - event.stopPropagation() - event.preventDefault() - } }) const handleSearchKeyChange = dispatch => async event => { From ddd3820afc9a04450960a828707a8f0fa82b5524 Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Sun, 24 Jun 2018 20:06:14 +0800 Subject: [PATCH 027/119] =?UTF-8?q?build(eslint):=20add=20global=20variabl?= =?UTF-8?q?e=20=E2=80=98chrome=E2=80=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .eslintrc.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.eslintrc.json b/.eslintrc.json index 3d09433..9affda5 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -32,5 +32,8 @@ "plugins": [ "babel", "react" - ] + ], + "globals": { + "chrome": true + } } From 8cd73671c0196005dd0324c8a9340b5bfd6886ac Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Sun, 24 Jun 2018 23:10:54 +0800 Subject: [PATCH 028/119] feat(configHelper): create --- src/components/SettingsBar.js | 8 ++++---- src/driver/core.js | 7 ++----- src/utils/configHelper.js | 34 ++++++++++++++++++++++++++++++++++ src/utils/general.js | 18 +++++++++++++++++- src/utils/storageHelper.js | 30 ++++-------------------------- 5 files changed, 61 insertions(+), 36 deletions(-) create mode 100644 src/utils/configHelper.js diff --git a/src/components/SettingsBar.js b/src/components/SettingsBar.js index efa7431..2904424 100644 --- a/src/components/SettingsBar.js +++ b/src/components/SettingsBar.js @@ -1,6 +1,6 @@ import React from 'react' import Icon from './Icon' -import storageHelper from '../utils/storageHelper' +import configHelper, { config } from '../utils/configHelper' import keyHelper from '../utils/keyHelper' const ACCESS_TOKEN_REGEXP = /^[0-9a-f]{40}$/ @@ -66,7 +66,7 @@ export default class SettingsBar extends React.PureComponent { const { onAccessTokenChange } = this.props const { accessToken } = this.state if (accessToken) { - await storageHelper.setAccessToken(accessToken) + await configHelper.setOne(config.accessToken, accessToken) onAccessTokenChange(accessToken) this.setState({ accessToken: '', @@ -77,7 +77,7 @@ export default class SettingsBar extends React.PureComponent { clearToken = async () => { const { onAccessTokenChange } = this.props - await storageHelper.setAccessToken('') + await configHelper.setOne(config.accessToken, '') onAccessTokenChange('') this.setState({ accessToken: '' }) } @@ -85,7 +85,7 @@ export default class SettingsBar extends React.PureComponent { saveShortcut = async () => { const { onShortcutChange } = this.props const { toggleShowSideBarShortcut } = this.state - await storageHelper.setShortcut(toggleShowSideBarShortcut) + await configHelper.setOne(config.shortcut, toggleShowSideBarShortcut) onShortcutChange(toggleShowSideBarShortcut) this.setState({ shortcutHint: 'Shortcut is saved!', diff --git a/src/driver/core.js b/src/driver/core.js index ea80664..9f03175 100644 --- a/src/driver/core.js +++ b/src/driver/core.js @@ -1,6 +1,6 @@ import DOMHelper, { REPO_TYPE_PRIVATE } from '../utils/DOMHelper' import GitHubHelper, { NOT_FOUND, BAD_CREDENTIALS } from '../utils/GitHubHelper' -import storageHelper from '../utils/storageHelper' +import configHelper from '../utils/configHelper' import URLHelper from '../utils/URLHelper' import keyHelper from '../utils/keyHelper' @@ -9,10 +9,7 @@ const init = dispatch => async () => { DOMHelper.decorateGitHubPageContent() const metaData = URLHelper.parse() dispatch(setMetaData, metaData) - const [accessToken, shortcut] = await Promise.all([ - storageHelper.getAccessToken(), - storageHelper.getShortcut(), - ]) + const { access_token: accessToken, shortcut } = await configHelper.get() dispatch({ accessToken, toggleShowSideBarShortcut: shortcut }) const metaDataFromAPI = await GitHubHelper.getRepoMeta({ ...metaData, accessToken }) const branchName = metaData.branchName || metaDataFromAPI['default_branch'] diff --git a/src/utils/configHelper.js b/src/utils/configHelper.js new file mode 100644 index 0000000..dceba79 --- /dev/null +++ b/src/utils/configHelper.js @@ -0,0 +1,34 @@ +import storageHelper from './storageHelper' +import { pick } from './general' + +export const config = { + shortcut: 'shortcut', + accessToken: 'access_token', +} + +const configKeys = Object.values(config) + +function get() { + return storageHelper.get(configKeys) +} + +function getOne(key) { + return get()[key] +} + +function set(partialConfig) { + return storageHelper.set(pick(partialConfig, configKeys)) +} + +function setOne(key, value) { + return set({ + [key]: value, + }) +} + +export default { + get, + getOne, + set, + setOne, +} diff --git a/src/utils/general.js b/src/utils/general.js index b1c6ea4..5deebbc 100644 --- a/src/utils/general.js +++ b/src/utils/general.js @@ -1 +1,17 @@ -export default {} + +/** + * @param {Object} source + * @param {Object|Array} keys + * @returns + */ +export function pick(source, keys) { + if (keys && typeof keys === 'object') { + return ( + Array.isArray(keys) ? keys : Object.keys(keys) + ).reduce((copy, key) => { + copy[key] = source[key] + return copy + }, {}) + } + return {} +} diff --git a/src/utils/storageHelper.js b/src/utils/storageHelper.js index a5f1f19..8f06782 100644 --- a/src/utils/storageHelper.js +++ b/src/utils/storageHelper.js @@ -1,36 +1,14 @@ const localStorage = chrome.storage.local -const ACCESS_TOKEN_KEY = 'access_token' -const SHORTCUT_KEY = 'shortcut' -function get(key) { - return new Promise(resolve => localStorage.get(key, items => resolve(items[key]))) +function get(mapping) { + return new Promise(resolve => localStorage.get(mapping, resolve)) } -function set(key, value) { - return new Promise(resolve => localStorage.set({ [key]: value }, resolve)) -} - -function getAccessToken() { - return get(ACCESS_TOKEN_KEY) -} - -function setAccessToken(accessToken) { - return set(ACCESS_TOKEN_KEY, accessToken) -} - -function getShortcut() { - return get(SHORTCUT_KEY) -} - -function setShortcut(shortcut) { - return set(SHORTCUT_KEY, shortcut) +function set(value) { + return new Promise(resolve => localStorage.set(value, resolve)) } export default { get, set, - getAccessToken, - setAccessToken, - getShortcut, - setShortcut, } From 9811cede1e82e13983a8c1469c3ab25ce463486b Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Tue, 26 Jun 2018 21:34:52 +0800 Subject: [PATCH 029/119] feat(SettingsBar): config for compress singleton folders --- src/components/FileExplorer.js | 3 +- src/components/SettingsBar.js | 55 +++++++++++++++++++++++++++++----- src/components/SideBar.js | 11 +++++-- src/content.less | 5 ++++ src/driver/core.js | 7 +++-- src/utils/configHelper.js | 1 + 6 files changed, 69 insertions(+), 13 deletions(-) diff --git a/src/components/FileExplorer.js b/src/components/FileExplorer.js index eac0f62..fa72dca 100644 --- a/src/components/FileExplorer.js +++ b/src/components/FileExplorer.js @@ -29,8 +29,9 @@ function getVisibleParentNode(nodes, focusedNode, depths) { const tasksAfterRender = [] const visibleNodesGenerator = new VisibleNodesGenerator() -const init = dispatch => () => dispatch(async (state, { treeData, metaData, accessToken }) => { +const init = dispatch => () => dispatch(async (state, { treeData, metaData, compressSingletonFolder }) => { const { root } = treeParser.parse(treeData, metaData) + visibleNodesGenerator.setCompress(compressSingletonFolder) await visibleNodesGenerator.plantTree(root) const currentPath = URLHelper.getCurrentPath(true) tasksAfterRender.push(DOMHelper.focusSearchInput) diff --git a/src/components/SettingsBar.js b/src/components/SettingsBar.js index 2904424..79ba35a 100644 --- a/src/components/SettingsBar.js +++ b/src/components/SettingsBar.js @@ -1,8 +1,14 @@ import React from 'react' +import PropTypes from 'prop-types' + import Icon from './Icon' import configHelper, { config } from '../utils/configHelper' import keyHelper from '../utils/keyHelper' +const wikiLinks = { + compressSingletonFolder: 'https://github.com/EnixCoda/Gitako/wiki/Compress-Singleton-Folder', +} + const ACCESS_TOKEN_REGEXP = /^[0-9a-f]{40}$/ const OperatingSystems = { @@ -38,20 +44,32 @@ function friendlyFormatShortcut(shortcut) { } export default class SettingsBar extends React.PureComponent { + static propTypes = { + accessToken: PropTypes.string.isRequired, + activated: PropTypes.bool.isRequired, + compressSingletonFolder: PropTypes.bool.isRequired, + onAccessTokenChange: PropTypes.func.isRequired, + onShortcutChange: PropTypes.func.isRequired, + setCompressSingleton: PropTypes.func.isRequired, + toggleShowSettings: PropTypes.func.isRequired, + toggleShowSideBarShortcut: PropTypes.string.isRequired, + } + state = { - accessTokenHint: null, accessToken: '', - shortcutHint: null, + accessTokenHint: '', + shortcutHint: '', toggleShowSideBarShortcut: '', + compressHint: '', } componentWillMount() { - const { toggleShowSideBarShortcut } = this.props - this.setState({ toggleShowSideBarShortcut }) + const { toggleShowSideBarShortcut, compressSingletonFolder } = this.props + this.setState({ toggleShowSideBarShortcut, compressSingletonFolder }) } - componentWillReceiveProps({ toggleShowSideBarShortcut }) { - this.setState({ toggleShowSideBarShortcut }) + componentWillReceiveProps({ toggleShowSideBarShortcut, compressSingletonFolder }) { + this.setState({ toggleShowSideBarShortcut, compressSingletonFolder }) } onInputAccessToken = event => { @@ -101,8 +119,18 @@ export default class SettingsBar extends React.PureComponent { this.setState({ toggleShowSideBarShortcut: shortcut }) } + setCompressSingletonFolder = async e => { + const compress = e.target.checked + await configHelper.setOne(config.compressSingletonFolder, compress) + const { setCompressSingleton } = this.props + setCompressSingleton(compress) + this.setState({ + compressHint: 'Saved, reload page to apply!', + }) + } + render() { - const { accessTokenHint, toggleShowSideBarShortcut, shortcutHint, accessToken } = this.state + const { accessTokenHint, toggleShowSideBarShortcut, compressSingletonFolder, shortcutHint, accessToken, compressHint } = this.state const { toggleShowSettings, activated, accessToken: hasAccessToken } = this.props return (
@@ -168,6 +196,17 @@ export default class SettingsBar extends React.PureComponent {
{shortcutHint && {shortcutHint}}
+
+
+ Compress singleton folder  + (?) +
+ + {compressHint &&
{compressHint}
} +
Position of Gitako (WIP)
-  enable +   disabled
diff --git a/src/components/SideBar.js b/src/components/SideBar.js index fdf6dca..fbf89b8 100644 --- a/src/components/SideBar.js +++ b/src/components/SideBar.js @@ -32,6 +32,8 @@ export default class Gitako extends React.PureComponent { metaData: PropTypes.object, // file tree data treeData: PropTypes.object, + // compress singleton folder + compressSingletonFolder: PropTypes.bool, init: PropTypes.func.isRequired, onPJAXEnd: PropTypes.func.isRequired, @@ -55,6 +57,7 @@ export default class Gitako extends React.PureComponent { toggleShowSideBarShortcut: '', metaData: null, treeData: null, + compressSingletonFolder: false, } constructor(props) { @@ -88,14 +91,14 @@ export default class Gitako extends React.PureComponent { } renderContent() { - const { errorDueToAuth, metaData, treeData, showSettings, accessToken } = this.props + const { errorDueToAuth, metaData, treeData, showSettings, accessToken, compressSingletonFolder } = this.props return (
{metaData && } {errorDueToAuth && this.renderAccessDeniedError()} {metaData && treeData && ( - + )}
) @@ -107,6 +110,7 @@ export default class Gitako extends React.PureComponent { shouldShow, showSettings, accessToken, + compressSingletonFolder, toggleShowSideBarShortcut, logoContainerElement, toggleShowSideBar, @@ -114,6 +118,7 @@ export default class Gitako extends React.PureComponent { toggleShowSettings, onShortcutChange, onAccessTokenChange, + setCompressSingleton, } = this.props return (
@@ -130,7 +135,9 @@ export default class Gitako extends React.PureComponent { onAccessTokenChange={onAccessTokenChange} activated={showSettings} accessToken={accessToken} + compressSingletonFolder={compressSingletonFolder} toggleShowSideBarShortcut={toggleShowSideBarShortcut} + setCompressSingleton={setCompressSingleton} />
diff --git a/src/content.less b/src/content.less index 936c787..b0bb0c5 100644 --- a/src/content.less +++ b/src/content.less @@ -340,6 +340,11 @@ box-shadow: none; } } + .singleton { + .hint { + color: #6a737d; + } + } .placeholder-row { flex-shrink: 0; display: flex; diff --git a/src/driver/core.js b/src/driver/core.js index 9f03175..298f9d7 100644 --- a/src/driver/core.js +++ b/src/driver/core.js @@ -9,8 +9,8 @@ const init = dispatch => async () => { DOMHelper.decorateGitHubPageContent() const metaData = URLHelper.parse() dispatch(setMetaData, metaData) - const { access_token: accessToken, shortcut } = await configHelper.get() - dispatch({ accessToken, toggleShowSideBarShortcut: shortcut }) + const { access_token: accessToken, shortcut, compressSingletonFolder } = await configHelper.get() + dispatch({ accessToken, toggleShowSideBarShortcut: shortcut, compressSingletonFolder }) const metaDataFromAPI = await GitHubHelper.getRepoMeta({ ...metaData, accessToken }) const branchName = metaData.branchName || metaDataFromAPI['default_branch'] Object.assign(metaData, { branchName, api: metaDataFromAPI }) @@ -82,6 +82,8 @@ const onShortcutChange = dispatch => shortcut => dispatch({ toggleShowSideBarSho const setMetaData = dispatch => metaData => dispatch({ metaData }) +const setCompressSingleton = dispatch => compressSingletonFolder => dispatch({ compressSingletonFolder }) + export default { init, onPJAXEnd, @@ -93,4 +95,5 @@ export default { onShortcutChange, onResize, setMetaData, + setCompressSingleton, } diff --git a/src/utils/configHelper.js b/src/utils/configHelper.js index dceba79..17f67be 100644 --- a/src/utils/configHelper.js +++ b/src/utils/configHelper.js @@ -4,6 +4,7 @@ import { pick } from './general' export const config = { shortcut: 'shortcut', accessToken: 'access_token', + compressSingletonFolder: 'compressSingletonFolder', } const configKeys = Object.values(config) From c6251217352a0cc6f45569056bb88c79ca5facbe Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Fri, 29 Jun 2018 00:22:22 +0800 Subject: [PATCH 030/119] feat(FileExplorer): enable open blob&tree in new tab --- src/components/Node.js | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/components/Node.js b/src/components/Node.js index da83634..fe2aeae 100644 --- a/src/components/Node.js +++ b/src/components/Node.js @@ -17,7 +17,9 @@ function getIconType(node) { } export default class Node extends React.PureComponent { - onClick = () => { + onClick = (event) => { + if (event.metaKey) return + event.preventDefault() const { node, onClick } = this.props onClick(node) } @@ -27,14 +29,15 @@ export default class Node extends React.PureComponent { const { name, path } = node return (
-
- - {name} -
+ +
+ + {name} +
+
) } From 0ae743e10c15b1e29e91998f1f7da0776dc59046 Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Fri, 29 Jun 2018 00:22:59 +0800 Subject: [PATCH 031/119] perf(PJAX): set proper url for blobs Gitako used to request tree path for blobs, which causes 301 --- src/utils/GitHubHelper.js | 4 ++-- src/utils/treeParser.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/utils/GitHubHelper.js b/src/utils/GitHubHelper.js index e4760c2..32605eb 100644 --- a/src/utils/GitHubHelper.js +++ b/src/utils/GitHubHelper.js @@ -24,8 +24,8 @@ async function getTreeData({ userName, repoName, branchName, accessToken }) { return await request(url, { accessToken }) } -function getUrlForRedirect({ userName, repoName, branchName }, path) { - return `https://github.com/${userName}/${repoName}/tree/${branchName}/${path}` +function getUrlForRedirect({ userName, repoName, branchName }, type = 'blob', path) { + return `https://github.com/${userName}/${repoName}/${type}/${branchName}/${path}` } export default { diff --git a/src/utils/treeParser.js b/src/utils/treeParser.js index 39df1f4..e25488e 100644 --- a/src/utils/treeParser.js +++ b/src/utils/treeParser.js @@ -60,7 +60,7 @@ function parse(treeData, metaData) { ...item, name: item.path.replace(/^.*\//, ''), url: item.url - ? GitHubHelper.getUrlForRedirect(metaData, item.path) + ? GitHubHelper.getUrlForRedirect(metaData, item.type, item.path) : null, contents: item.type === 'tree' ? [] : null, } From 10a239acec3093cfbee276116bbe6aa827a67f92 Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Fri, 29 Jun 2018 00:24:03 +0800 Subject: [PATCH 032/119] 0.2.6 --- package.json | 2 +- src/manifest.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index b7d80a2..32057d3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gitako", - "version": "0.2.5", + "version": "0.2.6", "description": "yet another extension for GitHub", "repository": "https://github.com/EnixCoda/Gitako", "author": "EnixCoda", diff --git a/src/manifest.json b/src/manifest.json index 3fc60d8..6c0b983 100644 --- a/src/manifest.json +++ b/src/manifest.json @@ -1,7 +1,7 @@ { "manifest_version": 2, "name": "Gitako", - "version": "0.2.5", + "version": "0.2.6", "author": "EnixCoda", "icons": { "128": "icons/Gitako-128x128.png" From 5695771fffc7e3a0b68ae6ac2d060fd18f5998af Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Sat, 30 Jun 2018 12:31:21 +0800 Subject: [PATCH 033/119] refactor: extract cores --- .babelrc | 1 + package.json | 1 + src/components/FileExplorer.js | 190 +----------------------- src/components/SideBar.js | 4 + src/content.js | 6 +- src/driver/connect.js | 4 - src/driver/core/FileExplorer.js | 186 +++++++++++++++++++++++ src/driver/{core.js => core/SideBar.js} | 10 +- src/driver/core/index.js | 2 + 9 files changed, 203 insertions(+), 201 deletions(-) create mode 100644 src/driver/core/FileExplorer.js rename src/driver/{core.js => core/SideBar.js} (91%) create mode 100644 src/driver/core/index.js diff --git a/.babelrc b/.babelrc index b16cc73..6464282 100644 --- a/.babelrc +++ b/.babelrc @@ -11,6 +11,7 @@ "react" ], "plugins": [ + "transform-decorators-legacy", "transform-class-properties", ["transform-object-rest-spread", { "useBuiltIns": true }] ] diff --git a/package.json b/package.json index 32057d3..f1beb95 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,7 @@ "babel-loader": "^7.1.2", "babel-plugin-react-require": "^3.0.0", "babel-plugin-transform-class-properties": "^6.24.1", + "babel-plugin-transform-decorators-legacy": "^1.3.5", "babel-plugin-transform-object-rest-spread": "^6.26.0", "babel-preset-env": "^1.6.1", "babel-preset-react": "^6.24.1", diff --git a/src/components/FileExplorer.js b/src/components/FileExplorer.js index fa72dca..69bee78 100644 --- a/src/components/FileExplorer.js +++ b/src/components/FileExplorer.js @@ -2,187 +2,15 @@ import React from 'react' import PropTypes from "prop-types"; import connect from '../driver/connect' +import { FileExplorer as FileExplorerCore } from '../driver/core' import SearchBar from './SearchBar' import Node from './Node' import cx from '../utils/cx' -import DOMHelper from '../utils/DOMHelper' -import treeParser from '../utils/treeParser' -import URLHelper from '../utils/URLHelper' -import VisibleNodesGenerator from '../utils/VisibleNodesGenerator' -function getVisibleParentNode(nodes, focusedNode, depths) { - const focusedNodeIndex = nodes.indexOf(focusedNode) - const focusedNodeDepth = depths.get(focusedNode) - let indexOfParentNode = focusedNodeIndex - 1 - while ( - indexOfParentNode !== -1 && - depths.get(nodes[indexOfParentNode]) >= focusedNodeDepth - ) { - --indexOfParentNode - } - const parentNode = nodes[indexOfParentNode] - return parentNode -} - -const tasksAfterRender = [] -const visibleNodesGenerator = new VisibleNodesGenerator() - -const init = dispatch => () => dispatch(async (state, { treeData, metaData, compressSingletonFolder }) => { - const { root } = treeParser.parse(treeData, metaData) - visibleNodesGenerator.setCompress(compressSingletonFolder) - await visibleNodesGenerator.plantTree(root) - const currentPath = URLHelper.getCurrentPath(true) - tasksAfterRender.push(DOMHelper.focusSearchInput) - if (currentPath.length) { - const nodeExpandedTo = visibleNodesGenerator.expandTo(currentPath.join('/')) - if (nodeExpandedTo) { - visibleNodesGenerator.focusNode(nodeExpandedTo) - const { nodes } = visibleNodesGenerator.visibleNodes - tasksAfterRender.push(() => DOMHelper.scrollToNodeElement(nodes.indexOf(nodeExpandedTo))) - } - } - dispatch(updateVisibleNodes) -}) - -const execAfterRender = dispatch => () => { - for (const task of tasksAfterRender) { - task() - } - tasksAfterRender.length = 0 -} - -const handleKeyDown = dispatch => ({ key }) => dispatch(({ visibleNodes: { nodes, focusedNode, expandedNodes, depths } }) => { - if (focusedNode) { - const focusedNodeIndex = nodes.indexOf(focusedNode) - switch (key) { - case 'ArrowUp': - // focus on previous node - if (focusedNodeIndex === 0) { - dispatch(focusNode, null) - tasksAfterRender.push(DOMHelper.focusSearchInput) - } else { - dispatch(focusNode, nodes[focusedNodeIndex - 1]) - } - break - - case 'ArrowDown': - // focus on next node - if (focusedNodeIndex + 1 < nodes.length) { - dispatch(focusNode, nodes[focusedNodeIndex + 1]) - } else { - dispatch(focusNode, null) - tasksAfterRender.push(DOMHelper.focusSearchInput) - } - break - - case 'ArrowLeft': - // collapse node or go to parent node - if (expandedNodes.has(focusedNode)) { - dispatch(setExpand, focusedNode, false) - } else { - // go forward to the start of the list, find the closest node with lower depth - const parentNode = getVisibleParentNode(nodes, focusedNode, depths) - if (parentNode) { - dispatch(focusNode, parentNode) - } - } - break - - // consider the two keys as 'confirm' key - case 'ArrowRight': - // expand node or focus on first content node or redirect to file page - if (focusedNode.type === 'tree') { - if (expandedNodes.has(focusedNode)) { - const nextNode = nodes[focusedNodeIndex + 1] - if (depths.get(nextNode) > depths.get(focusedNode)) { - dispatch(focusNode, nextNode) - } - } else { - dispatch(setExpand, focusedNode, true) - } - } else if (focusedNode.type === 'blob') { - DOMHelper.loadWithPJAX(focusedNode.url) - } else if (focusedNode.type === 'commit') { - // redirect to its parent folder - DOMHelper.loadWithPJAX(focusedNode.parent.url) - } - break - case 'Enter': - // expand node or redirect to file page - if (focusedNode.type === 'tree') { - dispatch(setExpand, focusedNode, true) - } else if (focusedNode.type === 'blob') { - DOMHelper.loadWithPJAX(focusedNode.url) - } else if (focusedNode.type === 'commit') { - // redirect to its parent folder - DOMHelper.loadWithPJAX(focusedNode.parent.url) - } - break - - } - } else { - // now search input is focused - if (nodes.length) { - switch (key) { - case 'ArrowDown': - dispatch(focusNode, nodes[0]) - break - case 'ArrowUp': - dispatch(focusNode, nodes[nodes.length - 1]) - break - } - } - } -}) - -const handleSearchKeyChange = dispatch => async event => { - const searchKey = event.target.value - await visibleNodesGenerator.search(searchKey) - dispatch(updateVisibleNodes) -} - -const setExpand = dispatch => (node, expand) => { - visibleNodesGenerator.setExpand(node, expand) - dispatch(focusNode, node) - tasksAfterRender.push(DOMHelper.focusSearchInput) -} - -const toggleNodeExpansion = dispatch => (node, skipScrollToNode) => { - visibleNodesGenerator.toggleExpand(node) - dispatch(focusNode, node, skipScrollToNode) - tasksAfterRender.push(DOMHelper.focusFileExplorer) -} - -const focusNode = dispatch => (node, skipScroll) => dispatch(({ visibleNodes: { nodes } }) => { - visibleNodesGenerator.focusNode(node) - if (node && !skipScroll) { - // when focus a node not in viewport(by keyboard), scroll to it - const indexOfToBeFocusedNode = nodes.indexOf(node) - tasksAfterRender.push(() => DOMHelper.scrollToNodeElement(indexOfToBeFocusedNode)) - tasksAfterRender.push(DOMHelper.focusSearchInput) - } - dispatch(updateVisibleNodes) -}) - -const onNodeClick = dispatch => (node) => { - if (node.type === 'tree') { - dispatch(toggleNodeExpansion, node, true) - } else if (node.type === 'blob') { - dispatch(focusNode, node, true) - DOMHelper.loadWithPJAX(node.url) - } else if (node.type === 'commit') { - DOMHelper.loadWithPJAX(node.parent.url) - } -} - -const updateVisibleNodes = dispatch => () => { - const { visibleNodes } = visibleNodesGenerator - dispatch({ visibleNodes }) -} - -class List extends React.Component { +@connect(FileExplorerCore) +export default class FileExplorer extends React.Component { static propTyps = { treeData: PropTypes.object, metaData: PropTypes.object, @@ -260,15 +88,3 @@ class List extends React.Component { ) } } - -export default connect({ - init, - execAfterRender, - handleKeyDown, - handleSearchKeyChange, - setExpand, - toggleNodeExpansion, - focusNode, - onNodeClick, - updateVisibleNodes, -})(List) diff --git a/src/components/SideBar.js b/src/components/SideBar.js index fbf89b8..4cf539a 100644 --- a/src/components/SideBar.js +++ b/src/components/SideBar.js @@ -1,6 +1,9 @@ import React from 'react' import PropTypes from 'prop-types' +import { SideBar as SideBarCore } from '../driver/core' +import connect from '../driver/connect' + import FileExplorer from './FileExplorer' import ToggleShowButton from './ToggleShowButton' import MetaBar from './MetaBar' @@ -12,6 +15,7 @@ import cx from '../utils/cx' const baseSize = 260 +@connect(SideBarCore) export default class Gitako extends React.PureComponent { static propTypes = { // initial width of side bar diff --git a/src/content.js b/src/content.js index 5090739..f1ed56b 100644 --- a/src/content.js +++ b/src/content.js @@ -1,15 +1,11 @@ import React from 'react' import ReactDOM from 'react-dom' -import connect from './driver/connect' -import core from './driver/core' import SideBar from './components/SideBar' import './content.less' -const ConnectedSideBar = connect(core)(SideBar) - const SideBarElement = document.createElement('div') document.body.appendChild(SideBarElement) -ReactDOM.render(, SideBarElement) +ReactDOM.render(, SideBarElement) diff --git a/src/driver/connect.js b/src/driver/connect.js index c526482..0d11a62 100644 --- a/src/driver/connect.js +++ b/src/driver/connect.js @@ -1,9 +1,5 @@ import React from 'react' -function isObject(target) { - return typeof target === 'object' && target !== null -} - function link(instance, sources) { const wrappedMethods = {/* sources[key] -> wrappedMethods.method */} const map = new Map(/* sources.creator -> wrappedMethods.method */) diff --git a/src/driver/core/FileExplorer.js b/src/driver/core/FileExplorer.js new file mode 100644 index 0000000..c876a65 --- /dev/null +++ b/src/driver/core/FileExplorer.js @@ -0,0 +1,186 @@ +import DOMHelper from '../../utils/DOMHelper' +import treeParser from '../../utils/treeParser' +import URLHelper from '../../utils/URLHelper' +import VisibleNodesGenerator from '../../utils/VisibleNodesGenerator' + +function getVisibleParentNode(nodes, focusedNode, depths) { + const focusedNodeIndex = nodes.indexOf(focusedNode) + const focusedNodeDepth = depths.get(focusedNode) + let indexOfParentNode = focusedNodeIndex - 1 + while ( + indexOfParentNode !== -1 && + depths.get(nodes[indexOfParentNode]) >= focusedNodeDepth + ) { + --indexOfParentNode + } + const parentNode = nodes[indexOfParentNode] + return parentNode +} + +const tasksAfterRender = [] +const visibleNodesGenerator = new VisibleNodesGenerator() + +const init = dispatch => () => dispatch(async (state, { treeData, metaData, compressSingletonFolder }) => { + const { root } = treeParser.parse(treeData, metaData) + visibleNodesGenerator.setCompress(compressSingletonFolder) + await visibleNodesGenerator.plantTree(root) + const currentPath = URLHelper.getCurrentPath(true) + tasksAfterRender.push(DOMHelper.focusSearchInput) + if (currentPath.length) { + const nodeExpandedTo = visibleNodesGenerator.expandTo(currentPath.join('/')) + if (nodeExpandedTo) { + visibleNodesGenerator.focusNode(nodeExpandedTo) + const { nodes } = visibleNodesGenerator.visibleNodes + tasksAfterRender.push(() => DOMHelper.scrollToNodeElement(nodes.indexOf(nodeExpandedTo))) + } + } + dispatch(updateVisibleNodes) +}) + +const execAfterRender = dispatch => () => { + for (const task of tasksAfterRender) { + task() + } + tasksAfterRender.length = 0 +} + +const handleKeyDown = dispatch => ({ key }) => dispatch(({ visibleNodes: { nodes, focusedNode, expandedNodes, depths } }) => { + if (focusedNode) { + const focusedNodeIndex = nodes.indexOf(focusedNode) + switch (key) { + case 'ArrowUp': + // focus on previous node + if (focusedNodeIndex === 0) { + dispatch(focusNode, null) + tasksAfterRender.push(DOMHelper.focusSearchInput) + } else { + dispatch(focusNode, nodes[focusedNodeIndex - 1]) + } + break + + case 'ArrowDown': + // focus on next node + if (focusedNodeIndex + 1 < nodes.length) { + dispatch(focusNode, nodes[focusedNodeIndex + 1]) + } else { + dispatch(focusNode, null) + tasksAfterRender.push(DOMHelper.focusSearchInput) + } + break + + case 'ArrowLeft': + // collapse node or go to parent node + if (expandedNodes.has(focusedNode)) { + dispatch(setExpand, focusedNode, false) + } else { + // go forward to the start of the list, find the closest node with lower depth + const parentNode = getVisibleParentNode(nodes, focusedNode, depths) + if (parentNode) { + dispatch(focusNode, parentNode) + } + } + break + + // consider the two keys as 'confirm' key + case 'ArrowRight': + // expand node or focus on first content node or redirect to file page + if (focusedNode.type === 'tree') { + if (expandedNodes.has(focusedNode)) { + const nextNode = nodes[focusedNodeIndex + 1] + if (depths.get(nextNode) > depths.get(focusedNode)) { + dispatch(focusNode, nextNode) + } + } else { + dispatch(setExpand, focusedNode, true) + } + } else if (focusedNode.type === 'blob') { + DOMHelper.loadWithPJAX(focusedNode.url) + } else if (focusedNode.type === 'commit') { + // redirect to its parent folder + DOMHelper.loadWithPJAX(focusedNode.parent.url) + } + break + case 'Enter': + // expand node or redirect to file page + if (focusedNode.type === 'tree') { + dispatch(setExpand, focusedNode, true) + } else if (focusedNode.type === 'blob') { + DOMHelper.loadWithPJAX(focusedNode.url) + } else if (focusedNode.type === 'commit') { + // redirect to its parent folder + DOMHelper.loadWithPJAX(focusedNode.parent.url) + } + break + + } + } else { + // now search input is focused + if (nodes.length) { + switch (key) { + case 'ArrowDown': + dispatch(focusNode, nodes[0]) + break + case 'ArrowUp': + dispatch(focusNode, nodes[nodes.length - 1]) + break + } + } + } +}) + +const handleSearchKeyChange = dispatch => async event => { + const searchKey = event.target.value + await visibleNodesGenerator.search(searchKey) + dispatch(updateVisibleNodes) +} + +const setExpand = dispatch => (node, expand) => { + visibleNodesGenerator.setExpand(node, expand) + dispatch(focusNode, node) + tasksAfterRender.push(DOMHelper.focusSearchInput) +} + +const toggleNodeExpansion = dispatch => (node, skipScrollToNode) => { + visibleNodesGenerator.toggleExpand(node) + dispatch(focusNode, node, skipScrollToNode) + tasksAfterRender.push(DOMHelper.focusFileExplorer) +} + +const focusNode = dispatch => (node, skipScroll) => dispatch(({ visibleNodes: { nodes } }) => { + visibleNodesGenerator.focusNode(node) + if (node && !skipScroll) { + // when focus a node not in viewport(by keyboard), scroll to it + const indexOfToBeFocusedNode = nodes.indexOf(node) + tasksAfterRender.push(() => DOMHelper.scrollToNodeElement(indexOfToBeFocusedNode)) + tasksAfterRender.push(DOMHelper.focusSearchInput) + } + dispatch(updateVisibleNodes) +}) + +const onNodeClick = dispatch => (node) => { + if (node.type === 'tree') { + dispatch(toggleNodeExpansion, node, true) + } else if (node.type === 'blob') { + dispatch(focusNode, node, true) + DOMHelper.loadWithPJAX(node.url) + } else if (node.type === 'commit') { + DOMHelper.loadWithPJAX(node.parent.url) + } +} + +const updateVisibleNodes = dispatch => () => { + const { visibleNodes } = visibleNodesGenerator + dispatch({ visibleNodes }) +} + +export default { + init, + execAfterRender, + handleKeyDown, + handleSearchKeyChange, + setExpand, + toggleNodeExpansion, + focusNode, + onNodeClick, + updateVisibleNodes, +} diff --git a/src/driver/core.js b/src/driver/core/SideBar.js similarity index 91% rename from src/driver/core.js rename to src/driver/core/SideBar.js index 298f9d7..c380c3d 100644 --- a/src/driver/core.js +++ b/src/driver/core/SideBar.js @@ -1,8 +1,8 @@ -import DOMHelper, { REPO_TYPE_PRIVATE } from '../utils/DOMHelper' -import GitHubHelper, { NOT_FOUND, BAD_CREDENTIALS } from '../utils/GitHubHelper' -import configHelper from '../utils/configHelper' -import URLHelper from '../utils/URLHelper' -import keyHelper from '../utils/keyHelper' +import DOMHelper, { REPO_TYPE_PRIVATE } from '../../utils/DOMHelper' +import GitHubHelper, { NOT_FOUND, BAD_CREDENTIALS } from '../../utils/GitHubHelper' +import configHelper from '../../utils/configHelper' +import URLHelper from '../../utils/URLHelper' +import keyHelper from '../../utils/keyHelper' const init = dispatch => async () => { try { diff --git a/src/driver/core/index.js b/src/driver/core/index.js new file mode 100644 index 0000000..071ab89 --- /dev/null +++ b/src/driver/core/index.js @@ -0,0 +1,2 @@ +export { default as SideBar } from './SideBar' +export { default as FileExplorer } from './FileExplorer' From 4687a33cb2705c2ad140c7a89a16ea5374870715 Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Sat, 30 Jun 2018 13:38:51 +0800 Subject: [PATCH 034/119] feat: show version & add change log link --- package.json | 1 + src/components/SettingsBar.js | 5 +++++ src/content.less | 5 ++++- webpack.config.js | 5 +++++ yarn.lock | 18 +++++++++++++++--- 5 files changed, 30 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index f1beb95..76b0745 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,7 @@ "eslint-plugin-class-property": "^1.1.0", "eslint-plugin-react": "^7.9.1", "file-loader": "^1.1.5", + "json-loader": "^0.5.7", "less": "^2.7.3", "less-loader": "^4.0.5", "style-loader": "^0.19.0", diff --git a/src/components/SettingsBar.js b/src/components/SettingsBar.js index 79ba35a..25a43ee 100644 --- a/src/components/SettingsBar.js +++ b/src/components/SettingsBar.js @@ -1,12 +1,14 @@ import React from 'react' import PropTypes from 'prop-types' +import { version } from '../../package' import Icon from './Icon' import configHelper, { config } from '../utils/configHelper' import keyHelper from '../utils/keyHelper' const wikiLinks = { compressSingletonFolder: 'https://github.com/EnixCoda/Gitako/wiki/Compress-Singleton-Folder', + changeLog: 'https://github.com/EnixCoda/Gitako/wiki/Change-Log', } const ACCESS_TOKEN_REGEXP = /^[0-9a-f]{40}$/ @@ -232,6 +234,9 @@ export default class SettingsBar extends React.PureComponent { )}
+ + v{version} + {activated ? ( Date: Sat, 30 Jun 2018 13:41:14 +0800 Subject: [PATCH 035/119] refactor: remove 2 deprecated features - position & MCToC --- src/components/SettingsBar.js | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/src/components/SettingsBar.js b/src/components/SettingsBar.js index 25a43ee..87b2041 100644 --- a/src/components/SettingsBar.js +++ b/src/components/SettingsBar.js @@ -209,19 +209,6 @@ export default class SettingsBar extends React.PureComponent { {compressHint &&
{compressHint}
}
-
-
Position of Gitako (WIP)
- -
-
-
Table of Markdown Content (WIP)
- -

Issue

From ec102c9d6a4ed754c07499ebb12fd2cd7f0e93e3 Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Sat, 30 Jun 2018 13:44:23 +0800 Subject: [PATCH 036/119] style(SettingsBar): minor changes --- src/components/SettingsBar.js | 158 +++++++++++++++++----------------- 1 file changed, 81 insertions(+), 77 deletions(-) diff --git a/src/components/SettingsBar.js b/src/components/SettingsBar.js index 87b2041..dc2562c 100644 --- a/src/components/SettingsBar.js +++ b/src/components/SettingsBar.js @@ -137,88 +137,88 @@ export default class SettingsBar extends React.PureComponent { return (
{activated && ( -

- Settings -

- )} - {activated && ( -
-
-
-

Access Token

- With access token provided, Gitako can access more repositories. -
- - Help: how to create access token? - -
- - Gitako stores the token in  - - chrome local storage + +

+ Settings +

+
+
+
+

Access Token

+ With access token provided, Gitako can access more repositories. +
+
+ Help: how to create access token? -  locally and safely. - -
-
- - {hasAccessToken && !accessToken ? ( - - ) : ( - + ) : ( + + )} +
+ {accessTokenHint && {accessTokenHint}} +
+
+

Toggle shortcut

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

Toggle shortcut

- Set a combination of keys for toggling Gitako sidebar. -
-
- - +
+
+ Compress singleton folder  + (?) +
+ + {compressHint &&
{compressHint}
} +
+
+

Issue

+ + + Draft a issue on Github + +  for bug report or feature request. +
- {shortcutHint && {shortcutHint}}
-
-
- Compress singleton folder  - (?) -
- - {compressHint &&
{compressHint}
} -
-
-

Issue

- - - Draft a issue on Github - -  for bug report or feature request. - -
-
+ )}
From bae918302e6a9e8538e4556d1884695b200d9bbd Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Sat, 30 Jun 2018 15:13:11 +0800 Subject: [PATCH 037/119] refactor: create and root component --- src/components/Gitako.js | 14 + src/components/Resizable.js | 29 +++ src/components/ResizeHandler.js | 2 +- src/components/SideBar.js | 24 +- src/content.js | 4 +- src/content.less | 442 ++++++++++++++++---------------- src/driver/core/Gitako.js | 9 + src/driver/core/SideBar.js | 6 +- src/driver/core/index.js | 1 + src/utils/DOMHelper.js | 4 +- 10 files changed, 287 insertions(+), 248 deletions(-) create mode 100644 src/components/Gitako.js create mode 100644 src/components/Resizable.js create mode 100644 src/driver/core/Gitako.js diff --git a/src/components/Gitako.js b/src/components/Gitako.js new file mode 100644 index 0000000..832d5cb --- /dev/null +++ b/src/components/Gitako.js @@ -0,0 +1,14 @@ +import React from 'react' +import SideBar from './SideBar' + +import { Gitako as GitakoCore } from '../driver/core' +import connect from '../driver/connect' + +@connect(GitakoCore) +export default class Gitako extends React.PureComponent { + render() { + return ( + + ) + } +} diff --git a/src/components/Resizable.js b/src/components/Resizable.js new file mode 100644 index 0000000..da6114d --- /dev/null +++ b/src/components/Resizable.js @@ -0,0 +1,29 @@ +import React from 'react' +import PropTypes from 'prop-types' +import ResizeHandler from './ResizeHandler' +import cx from '../utils/cx'; + +export default class Resizable extends React.PureComponent { + static propTypes = { + baseSize: PropTypes.number.isRequired, + } + + state = { + size: this.props.baseSize, + } + + onResize = size => this.setState({ size }) + + render() { + const { className, children } = this.props + const { size } = this.state + return ( +
+ +
+ {children} +
+
+ ) + } +} diff --git a/src/components/ResizeHandler.js b/src/components/ResizeHandler.js index 2599e81..ea15733 100644 --- a/src/components/ResizeHandler.js +++ b/src/components/ResizeHandler.js @@ -6,7 +6,7 @@ export default class ResizeHandler extends React.PureComponent { pointerDown = false startX = 0 delta = 0 - baseSize = this.props.baseSize + baseSize = this.props.size subscribeEvents = () => { window.addEventListener('mousemove', this.onPointerMove) diff --git a/src/components/SideBar.js b/src/components/SideBar.js index 4cf539a..4e4ff4f 100644 --- a/src/components/SideBar.js +++ b/src/components/SideBar.js @@ -8,20 +8,16 @@ import FileExplorer from './FileExplorer' import ToggleShowButton from './ToggleShowButton' import MetaBar from './MetaBar' import SettingsBar from './SettingsBar' -import ResizeHandler from './ResizeHandler' import Portal from './Portal' +import Resizable from './Resizable' import cx from '../utils/cx' -const baseSize = 260 - @connect(SideBarCore) export default class Gitako extends React.PureComponent { static propTypes = { // initial width of side bar baseSize: PropTypes.number, - // current width of side bar - size: PropTypes.number, // whether Gitako side bar should be shown shouldShow: PropTypes.bool, // whether show settings pane @@ -47,13 +43,11 @@ export default class Gitako extends React.PureComponent { onAccessTokenChange: PropTypes.func.isRequired, onKeyDown: PropTypes.func.isRequired, onShortcutChange: PropTypes.func.isRequired, - onResize: PropTypes.func.isRequired, setMetaData: PropTypes.func.isRequired, } static defaultProps = { - baseSize, - size: baseSize, + baseSize: 260, shouldShow: false, showSettings: false, errorDueToAuth: false, @@ -110,7 +104,7 @@ export default class Gitako extends React.PureComponent { render() { const { - size, + baseSize, shouldShow, showSettings, accessToken, @@ -118,20 +112,18 @@ export default class Gitako extends React.PureComponent { toggleShowSideBarShortcut, logoContainerElement, toggleShowSideBar, - onResize, toggleShowSettings, onShortcutChange, onAccessTokenChange, setCompressSingleton, } = this.props return ( -
+
-
- -
+ +
{this.renderContent()} + />
-
+
) } diff --git a/src/content.js b/src/content.js index f1ed56b..0bb24d4 100644 --- a/src/content.js +++ b/src/content.js @@ -1,11 +1,11 @@ import React from 'react' import ReactDOM from 'react-dom' -import SideBar from './components/SideBar' +import Gitako from './components/Gitako' import './content.less' const SideBarElement = document.createElement('div') document.body.appendChild(SideBarElement) -ReactDOM.render(, SideBarElement) +ReactDOM.render(, SideBarElement) diff --git a/src/content.less b/src/content.less index 93a5193..9e8f2a5 100644 --- a/src/content.less +++ b/src/content.less @@ -111,7 +111,7 @@ } } -.@{name} { +.@{name}-side-bar { .@{name}-position-wrapper { position: fixed; top: 0; @@ -119,255 +119,253 @@ height: 100vh; z-index: 2; min-width: @side-bar-base-width; + display: flex; + justify-content: flex-end; @media screen and (min-width: @min-screen-width) { width: ~'calc((100vw - ' @github-content-width ~') / 2)'; } - .@{name}-resize-handler { - @resize-handler-width: 16px; - @side-bar-and-handler-width: (@min-screen-width + @resize-handler-width); - position: absolute; - top: 0; - display: none; - width: @resize-handler-width; - height: 100vh; - background: #fafbfc; - cursor: ew-resize; - user-select: none; - border-left: 1px solid #e1e4e8; - - @media screen and (min-width: @min-screen-width) { - display: flex; - align-items: center; - } - - .grabber-icon { - transform: rotate(90deg); - width: 12px; - } - } - .@{name}-side-bar { - position: absolute; - right: 0; - width: @side-bar-base-width; + .gitako-position-content { max-width: 100%; - height: 100%; + } + } + .@{name}-resize-handler { + @resize-handler-width: 16px; + @side-bar-and-handler-width: (@min-screen-width + @resize-handler-width); + display: none; + width: @resize-handler-width; + background: #fafbfc; + cursor: ew-resize; + user-select: none; + border-left: 1px solid #e1e4e8; + + @media screen and (min-width: @min-screen-width) { display: flex; + align-items: center; + } + + .grabber-icon { + transform: rotate(90deg); + width: 12px; + } + } + .@{name}-side-bar-body { + width: 100%; + height: 100%; + display: flex; + flex-direction: column; + background: #fafbfc; + border-right: 1px solid #e1e4e8; + border-left: 1px solid #e1e4e8; + overflow: hidden; + + &.hidden { + .hidden; + } + + .octicon { + transition: 0.3s ease; + color: rgba(3, 47, 98, 0.55); + vertical-align: initial; + } + + .octicon-wrapper { + display: inline-block; + width: 16px; + height: 16px; + text-align: center; + vertical-align: middle; + } + + .octicon-color { + color: rgba(3, 47, 98, 0.55); + } + + .@{name}-side-bar-content { + display: flex; + flex: 1; flex-direction: column; - background: #fafbfc; - border-right: 1px solid #e1e4e8; - overflow: hidden; - will-change: transform; - border-left: 1px solid #e1e4e8; - &.hidden { - .hidden; - } + .meta-bar { + position: relative; + padding: 10px; + padding-right: 30px; + font-size: 13px; + line-height: 20px; + color: #586069; + background-color: #f1f8ff; + border-bottom: 1px solid #c8e1ff; - .octicon { - transition: 0.3s ease; - color: rgba(3, 47, 98, 0.55); - vertical-align: initial; - } - - .octicon-wrapper { - display: inline-block; - width: 16px; - height: 16px; - text-align: center; - vertical-align: middle; - } - - .octicon-color { - color: rgba(3, 47, 98, 0.55); - } - - .@{name}-side-bar-content { - display: flex; - flex: 1; - flex-direction: column; - - .meta-bar { - position: relative; - padding: 10px; - padding-right: 30px; - font-size: 13px; - line-height: 20px; - color: #586069; - background-color: #f1f8ff; - border-bottom: 1px solid #c8e1ff; - - a { - // fix a weird bug: - // when gitako failed loading repo, cursor hovering
in meta bar will be 'text' - cursor: pointer; - } - - .repo-name { - font-weight: bolder; - } + a { + // fix a weird bug: + // when gitako failed loading repo, cursor hovering in meta bar will be 'text' + cursor: pointer; } - .description { - padding: 4px 10px; - } - - .file-explorer { - width: 100%; - height: 100%; - outline: none; - display: flex; - flex-direction: column; - flex-wrap: nowrap; - &.freeze { - filter: blur(1.5px) opacity(0.6) grayscale(0.9); - pointer-events: none; - } - - /* search input */ - .search-input-wrapper { - input[type='text'].form-control { - box-shadow: none; - width: 100%; - } - } - - .no-results { - padding: 0px 10px; - color: #666; - } - - .files { - flex: 1; - overflow: auto; - } - - .node-item-row { - background: #fff; - &:hover { - background: #f6f8fa; - } - &.focused { - background: #f0f0f6; - } - - .node-item { - word-wrap: normal; - word-break: break-all; - margin: 0; - color: #0366d6; - line-height: 20px; - padding: 6px 0; - cursor: pointer; - border-top: 1px solid #eaecef; - transition: all 0.5s ease; - - // folder icon rotate when expand - &.expanded .octicon.octicon-triangle-right { - transform: rotate(90deg); - } - - .node-item-name { - padding-left: 6px; - vertical-align: middle; - } - - .node-item:hover .node-item-name { - text-decoration: underline; - } - } - } + .repo-name { + font-weight: bolder; } } - .@{name}-settings-bar { - z-index: 2; - background: #fafbfc; + .description { + padding: 4px 10px; + } + + .file-explorer { + width: 100%; + height: 100%; + outline: none; display: flex; flex-direction: column; - &-title { - border-top: 1px solid #eaecef; - padding: 6px 10px; - box-shadow: 0 1px 8px 0 rgba(0,0,0,.2), 0 3px 4px 0 rgba(0,0,0,.14), 0 3px 3px -2px rgba(0,0,0,.12); - z-index: 1; + flex-wrap: nowrap; + &.freeze { + filter: blur(1.5px) opacity(0.6) grayscale(0.9); + pointer-events: none; } - &-content { - padding: 0 10px; + + /* search input */ + .search-input-wrapper { + input[type='text'].form-control { + box-shadow: none; + width: 100%; + } + } + + .no-results { + padding: 0px 10px; + color: #666; + } + + .files { flex: 1; overflow: auto; - position: relative; - - .shadow-shelter { - position: absolute; - width: 100%; - height: 6px; - left: 0; - background: #fafbfc; - z-index: 1; - } - &-section { - padding-bottom: 10px; - } } - .access-token { - border-bottom: none; // prevent overwrite by github style - .hint { - color: #6a737d; - } - } - .access-token-input-control { - display: flex; - margin-top: 8px; - .access-token-input { - flex: 1; - box-shadow: none; - &:disabled { - cursor: not-allowed; + .node-item-row { + background: #fff; + &:hover { + background: #f6f8fa; + } + &.focused { + background: #f0f0f6; + } + + .node-item { + word-wrap: normal; + word-break: break-all; + margin: 0; + color: #0366d6; + line-height: 20px; + padding: 6px 0; + cursor: pointer; + border-top: 1px solid #eaecef; + transition: all 0.5s ease; + + // folder icon rotate when expand + &.expanded .octicon.octicon-triangle-right { + transform: rotate(90deg); + } + + .node-item-name { + padding-left: 6px; + vertical-align: middle; + } + + .node-item:hover .node-item-name { + text-decoration: underline; } } } - .toggle-shortcut { - .hint { - color: #6a737d; - } - } - .toggle-shortcut-input-control { - display: flex; - margin-top: 8px; - .toggle-shortcut-input { - flex: 1; - box-shadow: none; - } - } - .singleton { - .hint { - color: #6a737d; - } - } - .placeholder-row { - flex-shrink: 0; - display: flex; - justify-content: space-between; - align-items: center; - padding: 6px 10px; - border-top: 1px solid #eaecef; + } + } - .version { - color: #999999; - } - .show-settings-icon { - width: 20px; - height: 20px; - cursor: pointer; - } - .hide-settings-icon { - width: 20px; - height: 20px; - cursor: pointer; + .@{name}-settings-bar { + z-index: 2; + background: #fafbfc; + display: flex; + flex-direction: column; + &-title { + border-top: 1px solid #eaecef; + padding: 6px 10px; + box-shadow: 0 1px 8px 0 rgba(0,0,0,.2), 0 3px 4px 0 rgba(0,0,0,.14), 0 3px 3px -2px rgba(0,0,0,.12); + z-index: 1; + } + &-content { + padding: 0 10px; + flex: 1; + overflow: auto; + position: relative; + + .shadow-shelter { + position: absolute; + width: 100%; + height: 6px; + left: 0; + background: #fafbfc; + z-index: 1; + } + &-section { + padding-bottom: 10px; + } + } + .access-token { + border-bottom: none; // prevent overwrite by github style + .hint { + color: #6a737d; + } + } + .access-token-input-control { + display: flex; + margin-top: 8px; + .access-token-input { + flex: 1; + box-shadow: none; + + &:disabled { + cursor: not-allowed; } } } + .toggle-shortcut { + .hint { + color: #6a737d; + } + } + .toggle-shortcut-input-control { + display: flex; + margin-top: 8px; + .toggle-shortcut-input { + flex: 1; + box-shadow: none; + } + } + .singleton { + .hint { + color: #6a737d; + } + } + .placeholder-row { + flex-shrink: 0; + display: flex; + justify-content: space-between; + align-items: center; + padding: 6px 10px; + border-top: 1px solid #eaecef; + + .version { + color: #999999; + } + .show-settings-icon { + width: 20px; + height: 20px; + cursor: pointer; + } + .hide-settings-icon { + width: 20px; + height: 20px; + cursor: pointer; + } + } } } } diff --git a/src/driver/core/Gitako.js b/src/driver/core/Gitako.js new file mode 100644 index 0000000..d23a533 --- /dev/null +++ b/src/driver/core/Gitako.js @@ -0,0 +1,9 @@ +import DOMHelper from '../../utils/DOMHelper' + +const init = dispatch => () => { + DOMHelper.decorateGitHubPageContent() +} + +export default { + init, +} diff --git a/src/driver/core/SideBar.js b/src/driver/core/SideBar.js index c380c3d..139f914 100644 --- a/src/driver/core/SideBar.js +++ b/src/driver/core/SideBar.js @@ -6,7 +6,6 @@ import keyHelper from '../../utils/keyHelper' const init = dispatch => async () => { try { - DOMHelper.decorateGitHubPageContent() const metaData = URLHelper.parse() dispatch(setMetaData, metaData) const { access_token: accessToken, shortcut, compressSingletonFolder } = await configHelper.get() @@ -21,7 +20,7 @@ const init = dispatch => async () => { DOMHelper.mountTopProgressBar() } const treeData = await GitHubHelper.getTreeData({ ...metaData, accessToken }) - dispatch({ logoContainerElement: DOMHelper.insertLogo() }) + dispatch({ logoContainerElement: DOMHelper.insertLogoMountPoint() }) dispatch({ treeData }) if (shouldShow) { DOMHelper.unmountTopProgressBar() @@ -72,8 +71,6 @@ const setShouldShow = dispatch => shouldShow => { DOMHelper.setBodyIndent(shouldShow) } -const onResize = dispatch => size => dispatch({ size }) - const toggleShowSettings = dispatch => () => dispatch(({ showSettings }) => ({ showSettings: !showSettings })) const onAccessTokenChange = dispatch => accessToken => dispatch({ accessToken }) @@ -93,7 +90,6 @@ export default { toggleShowSettings, onAccessTokenChange, onShortcutChange, - onResize, setMetaData, setCompressSingleton, } diff --git a/src/driver/core/index.js b/src/driver/core/index.js index 071ab89..18fb34f 100644 --- a/src/driver/core/index.js +++ b/src/driver/core/index.js @@ -1,2 +1,3 @@ +export { default as Gitako } from './Gitako' export { default as SideBar } from './SideBar' export { default as FileExplorer } from './FileExplorer' diff --git a/src/utils/DOMHelper.js b/src/utils/DOMHelper.js index c7e5ddf..50776e1 100644 --- a/src/utils/DOMHelper.js +++ b/src/utils/DOMHelper.js @@ -24,7 +24,7 @@ function setBodyIndent(shouldShowGitako) { * add the logo element into DOM * */ -function insertLogo() { +function insertLogoMountPoint() { const logoSelector = '.gitako .gitako-logo' const logoElement = document.querySelector(logoSelector) if (logoElement) { @@ -329,7 +329,7 @@ export default { focusFileExplorer, getCurrentPageType, getRepoPageType, - insertLogo, + insertLogoMountPoint, setBodyIndent, scrollToNodeElement, scrollToRepoContent, From 8bec682eab0d196976390ab84d04cd22b37ec402 Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Sat, 30 Jun 2018 19:36:55 +0800 Subject: [PATCH 038/119] fix(resize): limit minimal width & handler show up conditions --- src/components/Resizable.js | 2 +- src/components/ResizeHandler.js | 2 +- src/content.less | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/Resizable.js b/src/components/Resizable.js index da6114d..bb92be8 100644 --- a/src/components/Resizable.js +++ b/src/components/Resizable.js @@ -12,7 +12,7 @@ export default class Resizable extends React.PureComponent { size: this.props.baseSize, } - onResize = size => this.setState({ size }) + onResize = size => this.setState({ size: Math.max(this.props.baseSize, size) }) render() { const { className, children } = this.props diff --git a/src/components/ResizeHandler.js b/src/components/ResizeHandler.js index ea15733..b79a6d8 100644 --- a/src/components/ResizeHandler.js +++ b/src/components/ResizeHandler.js @@ -40,7 +40,7 @@ export default class ResizeHandler extends React.PureComponent { onPointerUp = () => { this.pointerDown = false - this.baseSize += this.delta + this.baseSize = Math.max(this.baseSize + this.delta, this.props.size) this.unsubscribeEvents() } diff --git a/src/content.less b/src/content.less index 9e8f2a5..4d61099 100644 --- a/src/content.less +++ b/src/content.less @@ -139,7 +139,7 @@ user-select: none; border-left: 1px solid #e1e4e8; - @media screen and (min-width: @min-screen-width) { + @media screen and (min-width: (@min-screen-width + @side-bar-base-width + @resize-handler-width * 2)) { display: flex; align-items: center; } From 1c76f7d5c3951c60954d3cd829e224b0702bad65 Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Sat, 30 Jun 2018 19:47:57 +0800 Subject: [PATCH 039/119] fix(SideBar): .hidden was not working --- src/content.less | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/content.less b/src/content.less index 4d61099..99ab67b 100644 --- a/src/content.less +++ b/src/content.less @@ -125,6 +125,10 @@ width: ~'calc((100vw - ' @github-content-width ~') / 2)'; } + &.hidden { + .hidden; + } + .gitako-position-content { max-width: 100%; } From 51f2db8bb72efaeceb373b1ac68ddd50aa0fde74 Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Sat, 30 Jun 2018 19:49:23 +0800 Subject: [PATCH 040/119] fix(Settings): one title not in correct size --- src/components/SettingsBar.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/SettingsBar.js b/src/components/SettingsBar.js index dc2562c..71082dc 100644 --- a/src/components/SettingsBar.js +++ b/src/components/SettingsBar.js @@ -198,10 +198,10 @@ export default class SettingsBar extends React.PureComponent { {shortcutHint && {shortcutHint}}
-
+

Compress singleton folder  (?) -

+
) } diff --git a/src/content.less b/src/content.less index 99ab67b..5520364 100644 --- a/src/content.less +++ b/src/content.less @@ -215,6 +215,25 @@ padding: 4px 10px; } + .loading-indicator-container { + width: 100%; + height: 100%; + display: flex; + justify-content: center; + align-items: center; + + .loading-indicator { + display: flex; + align-items: center; + } + + .loading-indicator-icon { + width: 20px; + height: 20px; + animation: rotate 2s infinite linear; + } + } + .file-explorer { width: 100%; height: 100%; @@ -373,3 +392,11 @@ } } } + +@keyframes rotate { + from { + transform: rotateZ(0); + } to { + transform: rotateZ(360deg); + } +} diff --git a/src/driver/core/FileExplorer.js b/src/driver/core/FileExplorer.js index 12b5b8c..6db7b95 100644 --- a/src/driver/core/FileExplorer.js +++ b/src/driver/core/FileExplorer.js @@ -21,9 +21,15 @@ const tasksAfterRender = [] const visibleNodesGenerator = new VisibleNodesGenerator() const init = dispatch => () => dispatch(async (state, { treeData, metaData, compressSingletonFolder }) => { + if (!treeData) { + dispatch(setStateText, 'Fetching Tree Data...') + return + } + dispatch(setStateText, 'Rendering File List...') const { root } = treeParser.parse(treeData, metaData) visibleNodesGenerator.setCompress(compressSingletonFolder) await visibleNodesGenerator.plantTree(root) + dispatch(setStateText, null) const currentPath = URLHelper.getCurrentPath(true) if (currentPath.length) { const nodeExpandedTo = visibleNodesGenerator.expandTo(currentPath.join('/')) @@ -44,6 +50,10 @@ const execAfterRender = dispatch => () => { tasksAfterRender.length = 0 } +const setStateText = dispatch => text => dispatch({ + stateText: text, +}) + const handleKeyDown = dispatch => ({ key }) => dispatch(({ visibleNodes: { nodes, focusedNode, expandedNodes, depths } }) => { if (focusedNode) { const focusedNodeIndex = nodes.indexOf(focusedNode) @@ -174,6 +184,7 @@ const updateVisibleNodes = dispatch => () => { export default { init, execAfterRender, + setStateText, handleKeyDown, handleSearchKeyChange, setExpand, diff --git a/src/driver/core/SideBar.js b/src/driver/core/SideBar.js index df1ad1a..4bf3ae7 100644 --- a/src/driver/core/SideBar.js +++ b/src/driver/core/SideBar.js @@ -16,15 +16,9 @@ const init = dispatch => async () => { dispatch(setMetaData, metaData) const shouldShow = URLHelper.isInCodePage(metaData) dispatch(setShouldShow, shouldShow) - if (shouldShow) { - DOMHelper.mountTopProgressBar() - } const treeData = await GitHubHelper.getTreeData({ ...metaData, accessToken }) dispatch({ logoContainerElement: DOMHelper.insertLogoMountPoint() }) dispatch({ treeData }) - if (shouldShow) { - DOMHelper.unmountTopProgressBar() - } } catch (err) { // TODO: detect request time exceeds limit if (err.message === NOT_FOUND || err.message === BAD_CREDENTIALS) { From 995421642d48b688f718d9fbffbacea46be68559 Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Tue, 7 Aug 2018 21:32:13 +0800 Subject: [PATCH 049/119] fix: prevent scroll errors --- src/utils/DOMHelper.js | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/utils/DOMHelper.js b/src/utils/DOMHelper.js index 50776e1..d9546da 100644 --- a/src/utils/DOMHelper.js +++ b/src/utils/DOMHelper.js @@ -58,10 +58,13 @@ function scrollToRepoContent() { function scrollToNodeElement(index) { const nodeElementSelector = '.node-item' const nodeElements = document.querySelectorAll(nodeElementSelector) - nodeElements[index].scrollIntoView({ - behavior: 'smooth', - block: 'center', - }) + const targetElement = nodeElements[index] + if (targetElement) { + targetElement.scrollIntoView({ + behavior: 'smooth', + block: 'center', + }) + } } const pjax = new PJAX({ From 7aacc07429d2976ca74845196d32659c10fda152 Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Sun, 2 Sep 2018 12:59:23 +0800 Subject: [PATCH 050/119] fix: typos --- src/components/FileExplorer.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/FileExplorer.js b/src/components/FileExplorer.js index fb56863..13a77a8 100644 --- a/src/components/FileExplorer.js +++ b/src/components/FileExplorer.js @@ -12,10 +12,10 @@ import cx from '../utils/cx' @connect(FileExplorerCore) export default class FileExplorer extends React.Component { - static propTyps = { + static propTypes = { treeData: PropTypes.object, metaData: PropTypes.object, - freeze: PropTypes.boolean, + freeze: PropTypes.bool, visibleNodes: PropTypes.object, init: PropTypes.func.isRequired, From d808c8963a5044ad49a5da78e7a3d7ce4a5be53a Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Mon, 10 Sep 2018 20:27:30 +0800 Subject: [PATCH 051/119] feat: import react-life-hook --- package.json | 1 + yarn.lock | 10 ++++++++++ 2 files changed, 11 insertions(+) diff --git a/package.json b/package.json index 76b0745..96af9a8 100644 --- a/package.json +++ b/package.json @@ -38,6 +38,7 @@ "json-loader": "^0.5.7", "less": "^2.7.3", "less-loader": "^4.0.5", + "react-life-hook": "^0.2.0", "style-loader": "^0.19.0", "svgr": "^1.6.0", "uglifyjs-webpack-plugin": "^1.1.1", diff --git a/yarn.lock b/yarn.lock index dc10f61..c4e1aca 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1426,6 +1426,10 @@ commondir@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" +compare-versions@^3.4.0: + version "3.4.0" + resolved "https://registry.yarnpkg.com/compare-versions/-/compare-versions-3.4.0.tgz#e0747df5c9cb7f054d6d3dc3e1dbc444f9e92b26" + component-emitter@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.2.1.tgz#137918d6d78283f7df7a6b7c5a63e140e69425e6" @@ -4471,6 +4475,12 @@ react-dom@^16.4.0: object-assign "^4.1.1" prop-types "^15.6.0" +react-life-hook@^0.2.0: + version "0.2.8" + resolved "https://registry.yarnpkg.com/react-life-hook/-/react-life-hook-0.2.8.tgz#d38bdeb82a30010c11c9a5e45f27805fdd25e7e7" + dependencies: + compare-versions "^3.4.0" + react@^16.4.0: version "16.4.0" resolved "https://registry.yarnpkg.com/react/-/react-16.4.0.tgz#402c2db83335336fba1962c08b98c6272617d585" From 2ebd8c8fa7ad0c8a4588a81851969a1b34e50318 Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Tue, 11 Sep 2018 00:01:06 +0800 Subject: [PATCH 052/119] refactor(connect): remove sync and async --- src/driver/connect.js | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/driver/connect.js b/src/driver/connect.js index 49a7ebd..19f1902 100644 --- a/src/driver/connect.js +++ b/src/driver/connect.js @@ -2,14 +2,6 @@ import React from 'react' import { withErrorLog } from "../analytics" -function async(func) { - return new Promise(resolve => setTimeout(() => resolve(func()))) -} - -function sync(func) { - return func() -} - function link(instance, sources) { const wrappedMethods = {/* [keyof sources] -> wrappedMethods.method */} const map = new Map(/* sources.creator -> wrappedMethods.method */) @@ -24,9 +16,17 @@ function link(instance, sources) { function dispatch(...args) { const isFromSource = Object.values(sources).includes(args[0]) if (isFromSource) { - sync(withErrorLog(() => map.get(args[0])(...args.slice(1)))) + withErrorLog(() => map.get(args[0])(...args.slice(1)))() } else { - async(() => instance.setState(...args)) + // by doing so, no async updater is available anymore + // luckily I don't need them :) + let [updater, callback] = args + if (typeof updater === 'function') { + callback = updater + callback(instance.state, instance.props) + } else { + instance.setState(updater, callback) + } } } From 9be44e4e73490c460283ad73d0303486113c5649 Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Tue, 11 Sep 2018 21:42:25 +0800 Subject: [PATCH 053/119] =?UTF-8?q?fix:=20handle=20updater=E2=80=99s=20ret?= =?UTF-8?q?urn=20value?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/driver/connect.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/driver/connect.js b/src/driver/connect.js index 19f1902..8451576 100644 --- a/src/driver/connect.js +++ b/src/driver/connect.js @@ -22,11 +22,9 @@ function link(instance, sources) { // luckily I don't need them :) let [updater, callback] = args if (typeof updater === 'function') { - callback = updater - callback(instance.state, instance.props) - } else { - instance.setState(updater, callback) + updater = updater(instance.state, instance.props) } + instance.setState(updater, callback) } } From ad73e142f4b5371eece6b21a48df84e8403eb758 Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Tue, 11 Sep 2018 22:10:06 +0800 Subject: [PATCH 054/119] fix: focus on item when opening page Moving these code up and down then it works again, weird... --- src/driver/core/FileExplorer.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/driver/core/FileExplorer.js b/src/driver/core/FileExplorer.js index 6db7b95..928757f 100644 --- a/src/driver/core/FileExplorer.js +++ b/src/driver/core/FileExplorer.js @@ -29,6 +29,8 @@ const init = dispatch => () => dispatch(async (state, { treeData, metaData, comp const { root } = treeParser.parse(treeData, metaData) visibleNodesGenerator.setCompress(compressSingletonFolder) await visibleNodesGenerator.plantTree(root) + + tasksAfterRender.push(DOMHelper.focusSearchInput) dispatch(setStateText, null) const currentPath = URLHelper.getCurrentPath(true) if (currentPath.length) { @@ -39,7 +41,6 @@ const init = dispatch => () => dispatch(async (state, { treeData, metaData, comp tasksAfterRender.push(() => DOMHelper.scrollToNodeElement(nodes.indexOf(nodeExpandedTo))) } } - tasksAfterRender.push(DOMHelper.focusSearchInput) dispatch(updateVisibleNodes) }) From ef932114b96930afb38c966d8df2fd1b6955536a Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Tue, 11 Sep 2018 22:15:32 +0800 Subject: [PATCH 055/119] 0.3.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 96af9a8..4345d70 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gitako", - "version": "0.2.6", + "version": "0.3.0", "description": "yet another extension for GitHub", "repository": "https://github.com/EnixCoda/Gitako", "author": "EnixCoda", From e727e3bfce263af1fe391d92d76214a0d446136e Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Wed, 12 Sep 2018 23:11:00 +0800 Subject: [PATCH 056/119] feat(analytics): set log endpoint --- src/analytics.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/analytics.js b/src/analytics.js index 245cdde..0eac5b9 100644 --- a/src/analytics.js +++ b/src/analytics.js @@ -1,4 +1,5 @@ -const LOG_ENDPOINT = '' +// TODO: set this through ENV or something else +const LOG_ENDPOINT = 'https://enix.one/gitako/log' export function raiseError(error) { return reportError(error) From 7041e17b783915a42f8ed861f6d86c1969fa61c6 Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Thu, 13 Sep 2018 00:34:10 +0800 Subject: [PATCH 057/119] =?UTF-8?q?feat:=20add=20middleware=20functionalit?= =?UTF-8?q?y=20to=20=E2=80=98connect=E2=80=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/analytics.js | 19 +++++++++++-------- src/content.js | 4 ++++ src/driver/connect.js | 32 ++++++++++++++++++++++++++++---- 3 files changed, 43 insertions(+), 12 deletions(-) diff --git a/src/analytics.js b/src/analytics.js index 0eac5b9..a0d2e04 100644 --- a/src/analytics.js +++ b/src/analytics.js @@ -5,14 +5,17 @@ export function raiseError(error) { return reportError(error) } -export function withErrorLog(func) { - return function () { - try { - func() - } catch (error) { - raiseError(error) - } - } +export function withErrorLog(method, args) { + return [ + function() { + try { + method.apply(this, arguments) + } catch (error) { + raiseError(error) + } + }, + args, + ] } function encodeParams(params) { diff --git a/src/content.js b/src/content.js index 0bb24d4..b553ce6 100644 --- a/src/content.js +++ b/src/content.js @@ -2,9 +2,13 @@ import React from 'react' import ReactDOM from 'react-dom' import Gitako from './components/Gitako' +import { addMiddleware } from './driver/connect' +import { withErrorLog } from './analytics' import './content.less' +addMiddleware(withErrorLog) + const SideBarElement = document.createElement('div') document.body.appendChild(SideBarElement) diff --git a/src/driver/connect.js b/src/driver/connect.js index 8451576..f0f9f03 100644 --- a/src/driver/connect.js +++ b/src/driver/connect.js @@ -1,22 +1,46 @@ import React from 'react' -import { withErrorLog } from "../analytics" +const middlewares = [] + +export function addMiddleware(middleware) { + if (typeof middleware !== 'function') return null + const m = middleware.bind(null) + middlewares.push(m) + return function removeMiddleware() { + const index = middlewares.indexOf(m) + if (index === -1) return + middlewares.splice(index, 1) + } +} + +function applyMiddlewares(method, args) { + for(const middleware of middlewares) { + [method = method, args = args] = middleware(method, args) || [] + } + return [method, args] +} + +function run([method, args]) { + method.apply(null, args) +} function link(instance, sources) { const wrappedMethods = {/* [keyof sources] -> wrappedMethods.method */} const map = new Map(/* sources.creator -> wrappedMethods.method */) - Object.entries(sources).forEach(([key, createMethod]) => { const method = createMethod(dispatch) wrappedMethods[key] = method map.set(createMethod, method) }) + const sourcesValues = Object.values(sources) function dispatch(...args) { - const isFromSource = Object.values(sources).includes(args[0]) + const isFromSource = sourcesValues.includes(args[0]) if (isFromSource) { - withErrorLog(() => map.get(args[0])(...args.slice(1)))() + const [createMethod, ...otherArgs] = args + const method = map.get(createMethod) + run(applyMiddlewares(method, otherArgs)) } else { // by doing so, no async updater is available anymore // luckily I don't need them :) From 5c1e714b18e1d1f89ff2a4b5cd6fd314eb9319c6 Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Thu, 13 Sep 2018 00:35:39 +0800 Subject: [PATCH 058/119] style: format with prettier --- src/analytics.js | 14 +++++++++----- src/content.less | 8 +++++--- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/src/analytics.js b/src/analytics.js index a0d2e04..9cf2a31 100644 --- a/src/analytics.js +++ b/src/analytics.js @@ -19,12 +19,16 @@ export function withErrorLog(method, args) { } function encodeParams(params) { - return Object.keys(params).map(key => `${key}=${encodeURIComponent(JSON.stringify(params[key]))}`).join('&') + return Object.keys(params) + .map(key => `${key}=${encodeURIComponent(JSON.stringify(params[key]))}`) + .join('&') } function reportError(error) { - return fetch(`${LOG_ENDPOINT}?${encodeParams({ - error: error && error.message || error, - path: window.location.href, - })}`) + return fetch( + `${LOG_ENDPOINT}?${encodeParams({ + error: (error && error.message) || error, + path: window.location.href, + })}` + ) } diff --git a/src/content.less b/src/content.less index 5520364..77b6a6e 100644 --- a/src/content.less +++ b/src/content.less @@ -310,7 +310,8 @@ &-title { border-top: 1px solid #eaecef; padding: 6px 10px; - box-shadow: 0 1px 8px 0 rgba(0,0,0,.2), 0 3px 4px 0 rgba(0,0,0,.14), 0 3px 3px -2px rgba(0,0,0,.12); + box-shadow: 0 1px 8px 0 rgba(0, 0, 0, 0.2), 0 3px 4px 0 rgba(0, 0, 0, 0.14), + 0 3px 3px -2px rgba(0, 0, 0, 0.12); z-index: 1; } &-content { @@ -318,7 +319,7 @@ flex: 1; overflow: auto; position: relative; - + .shadow-shelter { position: absolute; width: 100%; @@ -396,7 +397,8 @@ @keyframes rotate { from { transform: rotateZ(0); - } to { + } + to { transform: rotateZ(360deg); } } From eaa4c94e5380d4074ed22045e239fb93d048be05 Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Thu, 13 Sep 2018 22:09:45 +0800 Subject: [PATCH 059/119] feat: script for sync manifest version --- .eslintrc.json | 3 ++- package.json | 1 + scripts/version.js | 10 ++++++++++ 3 files changed, 13 insertions(+), 1 deletion(-) create mode 100644 scripts/version.js diff --git a/.eslintrc.json b/.eslintrc.json index 9affda5..37d4b01 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -3,7 +3,8 @@ "env": { "browser": true, "commonjs": true, - "es6": true + "es6": true, + "node": true }, "parserOptions": { "ecmaFeatures": { diff --git a/package.json b/package.json index 4345d70..fdfb088 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,7 @@ "scripts": { "start": "webpack --watch", "dev": "webpack", + "stamp": "node scripts/version.js", "prod": "NODE_ENV=production webpack && cd dist && rm -f ./gitako.zip && zip gitako.zip -r *" }, "dependencies": { diff --git a/scripts/version.js b/scripts/version.js new file mode 100644 index 0000000..d9d847c --- /dev/null +++ b/scripts/version.js @@ -0,0 +1,10 @@ +const fs = require('fs') +const path = require('path') + +const rootPath = path.resolve(__dirname, '../') +const packageJSON = require(path.resolve(rootPath, 'package.json')) +const manifestPath = path.resolve(rootPath, 'src/manifest.json'); +const manifest = require(manifestPath) + +manifest.version = packageJSON.version +fs.writeFileSync(manifestPath, JSON.stringify(manifest), 'utf-8') From 42db8a8eecb8c36c9669a0226a182b33ec96cb9f Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Thu, 13 Sep 2018 22:11:00 +0800 Subject: [PATCH 060/119] chore: update manifest version --- src/manifest.json | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) diff --git a/src/manifest.json b/src/manifest.json index 6c0b983..068d527 100644 --- a/src/manifest.json +++ b/src/manifest.json @@ -1,18 +1 @@ -{ - "manifest_version": 2, - "name": "Gitako", - "version": "0.2.6", - "author": "EnixCoda", - "icons": { - "128": "icons/Gitako-128x128.png" - }, - "description": "yet another GitHub extension, inspired by Octotree and GayHub.", - "homepage_url": "https://github.com/EnixCoda/Gitako", - "permissions": ["tabs", "storage", "*://github.com/*"], - "content_scripts": [ - { - "matches": ["https://github.com/*"], - "js": ["content.js"] - } - ] -} +{"manifest_version":2,"name":"Gitako","version":"0.3.0","author":"EnixCoda","icons":{"128":"icons/Gitako-128x128.png"},"description":"yet another GitHub extension, inspired by Octotree and GayHub.","homepage_url":"https://github.com/EnixCoda/Gitako","permissions":["tabs","storage","*://github.com/*"],"content_scripts":[{"matches":["https://github.com/*"],"js":["content.js"]}]} \ No newline at end of file From 5b1a7c6b898a23d176ae5dc29016015832c40b1e Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Sat, 15 Sep 2018 12:39:31 +0800 Subject: [PATCH 061/119] perf(nodes-generator): 4x faster node generating refactored recurse to loop, time for processing 4000 items reduces from 6ms to 1.4ms --- src/utils/visibleNodesGenerator.js | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/src/utils/visibleNodesGenerator.js b/src/utils/visibleNodesGenerator.js index 3f0526c..36723e7 100644 --- a/src/utils/visibleNodesGenerator.js +++ b/src/utils/visibleNodesGenerator.js @@ -171,19 +171,26 @@ export default class VisibleNodesGenerator { this.focusedNode = null this.depths.clear() const nodesSet = new Set() // prevent duplication - const get = (nodes, depth = 0) => { - return [].concat( - ...nodes.map(node => { - if (nodesSet.has(node)) return [] - this.depths.set(node, depth) - nodesSet.add(node) - const children = this.expandedNodes.has(node) ? get(node.contents, depth + 1) : [] - return [node, ...children] - }) - ) + const nodes = [], stack = this.searchedNodes.slice().reverse() + let current, depth = 0 + while (stack.length) { + current = stack.pop() + if (current === null) { + depth -= 1 + continue + } + if (nodesSet.has(current)) continue + nodes.push(current) + nodesSet.add(current) + this.depths.set(current, depth) + if (this.expandedNodes.has(current)) { + stack.push(null) // use null as pop depth flag + stack.push(...current.contents.slice().reverse()) + depth += 1 + } } this.visibleNodes = { - nodes: get(this.searchedNodes), + nodes, depths: this.depths, expandedNodes: this.expandedNodes, } From 48e213a1588a53028843f47c8db57e529b007b18 Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Sat, 15 Sep 2018 16:15:12 +0800 Subject: [PATCH 062/119] feat: loading indicator when expanding node --- src/components/Node.js | 14 +++++++++- src/driver/core/FileExplorer.js | 46 ++++++++++++++++++++++++++++++--- 2 files changed, 55 insertions(+), 5 deletions(-) diff --git a/src/components/Node.js b/src/components/Node.js index fe2aeae..26e8e08 100644 --- a/src/components/Node.js +++ b/src/components/Node.js @@ -4,6 +4,7 @@ import Icon from './Icon' import cx from '../utils/cx' import DOMHelper from '../utils/DOMHelper' +import LoadingIndicator from './LoadingIndicator'; function getIconType(node) { switch (node.type) { @@ -26,7 +27,18 @@ export default class Node extends React.PureComponent { render() { const { node, depth, expanded, focused } = this.props - const { name, path } = node + const { name, path, virtual } = node + if (virtual) { + // this is not a real node + // for now, all virtual nodes are indicators for pending state + return ( +
+
+ +
+
+ ) + } return (
diff --git a/src/driver/core/FileExplorer.js b/src/driver/core/FileExplorer.js index 928757f..67762f4 100644 --- a/src/driver/core/FileExplorer.js +++ b/src/driver/core/FileExplorer.js @@ -143,16 +143,38 @@ const handleSearchKeyChange = dispatch => async event => { dispatch(updateVisibleNodes) } +const delayExpandThreshold = 400 +function shouldDelayExpand(node) { + return visibleNodesGenerator.visibleNodes.expandedNodes.has(node) + && node.contents.length > delayExpandThreshold +} + const setExpand = dispatch => (node, expand) => { visibleNodesGenerator.setExpand(node, expand) - dispatch(focusNode, node) - tasksAfterRender.push(DOMHelper.focusSearchInput) + const applyChanges = () => { + dispatch(focusNode, node) + tasksAfterRender.push(DOMHelper.focusSearchInput) + } + if (shouldDelayExpand(node)) { + dispatch(mountExpandingIndicator, node) + tasksAfterRender.push(() => setTimeout(applyChanges, 0)) + } else { + applyChanges() + } } const toggleNodeExpansion = dispatch => (node, skipScrollToNode) => { visibleNodesGenerator.toggleExpand(node) - dispatch(focusNode, node, skipScrollToNode) - tasksAfterRender.push(DOMHelper.focusFileExplorer) + const applyChanges = () => { + dispatch(focusNode, node, skipScrollToNode) + tasksAfterRender.push(DOMHelper.focusFileExplorer) + } + if (shouldDelayExpand(node)) { + dispatch(mountExpandingIndicator, node) + tasksAfterRender.push(() => setTimeout(applyChanges, 0)) + } else { + applyChanges() + } } const focusNode = dispatch => (node, skipScroll) => dispatch(({ visibleNodes: { nodes } }) => { @@ -177,6 +199,21 @@ const onNodeClick = dispatch => (node) => { } } +const mountExpandingIndicator = dispatch => node => dispatch(({ visibleNodes }) => { + const dummyVisibleNodes = { + ...visibleNodes, + nodes: visibleNodes.nodes.slice(), + } + dummyVisibleNodes.nodes.splice( + dummyVisibleNodes.nodes.indexOf(node) + 1, + 0, + { virtual: true, name: 'Loading' }, + ) + return { + visibleNodes: dummyVisibleNodes + } +}) + const updateVisibleNodes = dispatch => () => { const { visibleNodes } = visibleNodesGenerator dispatch({ visibleNodes }) @@ -193,4 +230,5 @@ export default { focusNode, onNodeClick, updateVisibleNodes, + mountExpandingIndicator, } From cd5b9836cea5589d0663186eb4cd137d6f3c2d72 Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Sat, 15 Sep 2018 22:00:28 +0800 Subject: [PATCH 063/119] build: postversion command --- package.json | 4 ++-- scripts/version.js | 4 ++-- src/manifest.json | 27 ++++++++++++++++++++++++++- 3 files changed, 30 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index fdfb088..94bb125 100644 --- a/package.json +++ b/package.json @@ -8,9 +8,9 @@ "private": true, "scripts": { "start": "webpack --watch", - "dev": "webpack", "stamp": "node scripts/version.js", - "prod": "NODE_ENV=production webpack && cd dist && rm -f ./gitako.zip && zip gitako.zip -r *" + "postversion": "npm run stamp && git add src/manifest.json && git commit --amend --no-edit", + "prod": "npm run stamp && NODE_ENV=production webpack && cd dist && rm -f ./gitako.zip && zip gitako.zip -r *" }, "dependencies": { "nprogress": "^0.2.0", diff --git a/scripts/version.js b/scripts/version.js index d9d847c..3aa7456 100644 --- a/scripts/version.js +++ b/scripts/version.js @@ -3,8 +3,8 @@ const path = require('path') const rootPath = path.resolve(__dirname, '../') const packageJSON = require(path.resolve(rootPath, 'package.json')) -const manifestPath = path.resolve(rootPath, 'src/manifest.json'); +const manifestPath = path.resolve(rootPath, 'src/manifest.json') const manifest = require(manifestPath) manifest.version = packageJSON.version -fs.writeFileSync(manifestPath, JSON.stringify(manifest), 'utf-8') +fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, ' '), 'utf-8') diff --git a/src/manifest.json b/src/manifest.json index 068d527..6cd153e 100644 --- a/src/manifest.json +++ b/src/manifest.json @@ -1 +1,26 @@ -{"manifest_version":2,"name":"Gitako","version":"0.3.0","author":"EnixCoda","icons":{"128":"icons/Gitako-128x128.png"},"description":"yet another GitHub extension, inspired by Octotree and GayHub.","homepage_url":"https://github.com/EnixCoda/Gitako","permissions":["tabs","storage","*://github.com/*"],"content_scripts":[{"matches":["https://github.com/*"],"js":["content.js"]}]} \ No newline at end of file +{ + "manifest_version": 2, + "name": "Gitako", + "version": "0.3.0", + "author": "EnixCoda", + "icons": { + "128": "icons/Gitako-128x128.png" + }, + "description": "yet another GitHub extension, inspired by Octotree and GayHub.", + "homepage_url": "https://github.com/EnixCoda/Gitako", + "permissions": [ + "tabs", + "storage", + "*://github.com/*" + ], + "content_scripts": [ + { + "matches": [ + "https://github.com/*" + ], + "js": [ + "content.js" + ] + } + ] +} \ No newline at end of file From 5713489656fdec84a1a6dbc4ba37b79f34ac4ba2 Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Mon, 17 Sep 2018 22:03:44 +0800 Subject: [PATCH 064/119] 0.3.1 --- package.json | 2 +- src/manifest.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 94bb125..d1516ff 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gitako", - "version": "0.3.0", + "version": "0.3.1", "description": "yet another extension for GitHub", "repository": "https://github.com/EnixCoda/Gitako", "author": "EnixCoda", diff --git a/src/manifest.json b/src/manifest.json index 6cd153e..07754b4 100644 --- a/src/manifest.json +++ b/src/manifest.json @@ -1,7 +1,7 @@ { "manifest_version": 2, "name": "Gitako", - "version": "0.3.0", + "version": "0.3.1", "author": "EnixCoda", "icons": { "128": "icons/Gitako-128x128.png" From 4b92b9cfd02de8130659fc4929515fd6e06f65aa Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Mon, 17 Sep 2018 23:28:59 +0800 Subject: [PATCH 065/119] feat: aggressive load tree data --- src/driver/core/SideBar.js | 41 ++++++++++++++++++++++++-------------- 1 file changed, 26 insertions(+), 15 deletions(-) diff --git a/src/driver/core/SideBar.js b/src/driver/core/SideBar.js index 4bf3ae7..b9b61d7 100644 --- a/src/driver/core/SideBar.js +++ b/src/driver/core/SideBar.js @@ -10,28 +10,38 @@ const init = dispatch => async () => { dispatch(setMetaData, metaData) const { access_token: accessToken, shortcut, compressSingletonFolder } = await configHelper.get() dispatch({ accessToken, toggleShowSideBarShortcut: shortcut, compressSingletonFolder }) + const aggressivelyGotTreeData = GitHubHelper.getTreeData({ branchName: 'master', ...metaData, accessToken }).catch(() => {}) const metaDataFromAPI = await GitHubHelper.getRepoMeta({ ...metaData, accessToken }) const branchName = metaData.branchName || metaDataFromAPI['default_branch'] Object.assign(metaData, { branchName, api: metaDataFromAPI }) dispatch(setMetaData, metaData) const shouldShow = URLHelper.isInCodePage(metaData) dispatch(setShouldShow, shouldShow) - const treeData = await GitHubHelper.getTreeData({ ...metaData, accessToken }) - dispatch({ logoContainerElement: DOMHelper.insertLogoMountPoint() }) - dispatch({ treeData }) - } catch (err) { - // TODO: detect request time exceeds limit - if (err.message === NOT_FOUND || err.message === BAD_CREDENTIALS) { - const repoPageType = await DOMHelper.getRepoPageType() - const errorDueToAuth = repoPageType === REPO_TYPE_PRIVATE || err.message === BAD_CREDENTIALS - dispatch({ - showSettings: repoPageType !== null, - errorDueToAuth, + aggressivelyGotTreeData + .then(treeData => { + dispatch({ logoContainerElement: DOMHelper.insertLogoMountPoint() }) + dispatch({ treeData }) }) - dispatch(setShouldShow, errorDueToAuth) - } else { - dispatch(setShouldShow, false) - } + .catch(err => { + dispatch(handleError, err) + }) + } catch (err) { + dispatch(handleError, err) + } +} + +const handleError = dispatch => async (err) => { + // TODO: detect request time exceeds limit + if (err.message === NOT_FOUND || err.message === BAD_CREDENTIALS) { + const repoPageType = await DOMHelper.getRepoPageType() + const errorDueToAuth = repoPageType === REPO_TYPE_PRIVATE || err.message === BAD_CREDENTIALS + dispatch({ + showSettings: repoPageType !== null, + errorDueToAuth, + }) + dispatch(setShouldShow, errorDueToAuth) + } else { + dispatch(setShouldShow, false) } } @@ -85,4 +95,5 @@ export default { onShortcutChange, setMetaData, setCompressSingleton, + handleError, } From 017abd98868cd7ea93a3bbef7127c5a32eed18e3 Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Wed, 19 Sep 2018 20:19:37 +0800 Subject: [PATCH 066/119] fix(SideBar): branchName was overwrote by undefined --- src/driver/core/SideBar.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/driver/core/SideBar.js b/src/driver/core/SideBar.js index b9b61d7..1191ecc 100644 --- a/src/driver/core/SideBar.js +++ b/src/driver/core/SideBar.js @@ -10,7 +10,11 @@ const init = dispatch => async () => { dispatch(setMetaData, metaData) const { access_token: accessToken, shortcut, compressSingletonFolder } = await configHelper.get() dispatch({ accessToken, toggleShowSideBarShortcut: shortcut, compressSingletonFolder }) - const aggressivelyGotTreeData = GitHubHelper.getTreeData({ branchName: 'master', ...metaData, accessToken }).catch(() => {}) + const aggressivelyGotTreeData = GitHubHelper.getTreeData({ + ...metaData, + branchName: metaData.branchName || 'master', + accessToken, + }).catch(() => {}) const metaDataFromAPI = await GitHubHelper.getRepoMeta({ ...metaData, accessToken }) const branchName = metaData.branchName || metaDataFromAPI['default_branch'] Object.assign(metaData, { branchName, api: metaDataFromAPI }) From 0bd652b80ebe3d641eab1462dd6649e294cc6c1b Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Wed, 19 Sep 2018 20:23:00 +0800 Subject: [PATCH 067/119] chore: move script gengerate-octicon --- generate-octicon.js => scripts/generate-octicon.js | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename generate-octicon.js => scripts/generate-octicon.js (100%) diff --git a/generate-octicon.js b/scripts/generate-octicon.js similarity index 100% rename from generate-octicon.js rename to scripts/generate-octicon.js From e75a06a7ed4d8a77f877fed4adc7485d1f01acb8 Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Wed, 19 Sep 2018 20:27:20 +0800 Subject: [PATCH 068/119] 0.3.2 --- package.json | 2 +- src/manifest.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index d1516ff..de4b11b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gitako", - "version": "0.3.1", + "version": "0.3.2", "description": "yet another extension for GitHub", "repository": "https://github.com/EnixCoda/Gitako", "author": "EnixCoda", diff --git a/src/manifest.json b/src/manifest.json index 07754b4..99f4c3a 100644 --- a/src/manifest.json +++ b/src/manifest.json @@ -1,7 +1,7 @@ { "manifest_version": 2, "name": "Gitako", - "version": "0.3.1", + "version": "0.3.2", "author": "EnixCoda", "icons": { "128": "icons/Gitako-128x128.png" From 197d0778d4b0ba02fada2bee6d16c2ab1dc77a8d Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Wed, 19 Sep 2018 21:48:32 +0800 Subject: [PATCH 069/119] feat: raise GitHub helper errors --- src/utils/GitHubHelper.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/utils/GitHubHelper.js b/src/utils/GitHubHelper.js index 32605eb..76535c3 100644 --- a/src/utils/GitHubHelper.js +++ b/src/utils/GitHubHelper.js @@ -1,3 +1,4 @@ +import { raiseError } from '../analytics' export const NOT_FOUND = 'Repo Not Found' export const BAD_CREDENTIALS = 'Bad credentials' @@ -7,6 +8,7 @@ async function request(url, { accessToken } = {}) { headers.Authorization = `token ${accessToken}` } const res = await fetch(url, { headers }) + if (!res.ok) raiseError(new Error(`Got ${res.statusText} when requesting ${url}`)) if (res.status === 200) return res.json() // for private repo, GitHub api also responses with 404 when unauthorized if (res.status === 404) throw new Error(NOT_FOUND) From 23937ab646a7c3cada76fc991e6838617053acaa Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Wed, 19 Sep 2018 21:49:41 +0800 Subject: [PATCH 070/119] feat: handle more error and block UI --- src/components/SideBar.js | 7 +++++-- src/components/ToggleShowButton.js | 11 +++++++++-- src/content.less | 20 +++++++++++++++++++- src/driver/core/SideBar.js | 25 ++++++++++++++++--------- 4 files changed, 49 insertions(+), 14 deletions(-) diff --git a/src/components/SideBar.js b/src/components/SideBar.js index 43069b3..2e88f48 100644 --- a/src/components/SideBar.js +++ b/src/components/SideBar.js @@ -18,6 +18,8 @@ export default class Gitako extends React.PureComponent { static propTypes = { // initial width of side bar baseSize: PropTypes.number, + // error message + error: PropTypes.string, // whether Gitako side bar should be shown shouldShow: PropTypes.bool, // whether show settings pane @@ -103,6 +105,7 @@ export default class Gitako extends React.PureComponent { render() { const { baseSize, + error, shouldShow, showSettings, accessToken, @@ -118,9 +121,9 @@ export default class Gitako extends React.PureComponent { return (
- + - +
{this.renderContent()} +
+ {error && {error}}
) } diff --git a/src/content.less b/src/content.less index 77b6a6e..fc5f427 100644 --- a/src/content.less +++ b/src/content.less @@ -79,7 +79,7 @@ justify-content: center; align-items: center; cursor: pointer; - width: 30px; + min-width: 30px; height: 30px; will-change: transform; border: 1px solid transparent; @@ -89,6 +89,12 @@ transform: translate(~'calc(50vw - (' @github-content-width ~') / 2 - 30px)'); } + &:hover { + .error-message { + display: inline; + } + } + &.collapsed { border-color: #999999; border-radius: 3px; @@ -103,12 +109,24 @@ } } + &.error { + .action-icon { + color: #cb2431; + } + } + .action-icon { color: #666666; width: 16px; height: 20px; + margin: 5px 6px; transition: all @animation-duration ease; } + + .error-message { + display: none; + margin: 0 4px; + } } .@{name}-side-bar { diff --git a/src/driver/core/SideBar.js b/src/driver/core/SideBar.js index 1191ecc..12d7b82 100644 --- a/src/driver/core/SideBar.js +++ b/src/driver/core/SideBar.js @@ -6,6 +6,7 @@ import keyHelper from '../../utils/keyHelper' const init = dispatch => async () => { try { + let nothingWentWrong = true const metaData = URLHelper.parse() dispatch(setMetaData, metaData) const { access_token: accessToken, shortcut, compressSingletonFolder } = await configHelper.get() @@ -14,21 +15,21 @@ const init = dispatch => async () => { ...metaData, branchName: metaData.branchName || 'master', accessToken, - }).catch(() => {}) + }).catch(err => { + nothingWentWrong = false + dispatch(handleError, err) + }) const metaDataFromAPI = await GitHubHelper.getRepoMeta({ ...metaData, accessToken }) const branchName = metaData.branchName || metaDataFromAPI['default_branch'] Object.assign(metaData, { branchName, api: metaDataFromAPI }) dispatch(setMetaData, metaData) const shouldShow = URLHelper.isInCodePage(metaData) - dispatch(setShouldShow, shouldShow) + dispatch(setShouldShow, nothingWentWrong && shouldShow) aggressivelyGotTreeData .then(treeData => { dispatch({ logoContainerElement: DOMHelper.insertLogoMountPoint() }) dispatch({ treeData }) }) - .catch(err => { - dispatch(handleError, err) - }) } catch (err) { dispatch(handleError, err) } @@ -39,13 +40,14 @@ const handleError = dispatch => async (err) => { if (err.message === NOT_FOUND || err.message === BAD_CREDENTIALS) { const repoPageType = await DOMHelper.getRepoPageType() const errorDueToAuth = repoPageType === REPO_TYPE_PRIVATE || err.message === BAD_CREDENTIALS - dispatch({ - showSettings: repoPageType !== null, - errorDueToAuth, - }) + dispatch({ errorDueToAuth }) dispatch(setShouldShow, errorDueToAuth) + if (!errorDueToAuth) { + dispatch(setError, 'Gitako ate a bug, but it should recovery soon!') + } } else { dispatch(setShouldShow, false) + dispatch(setError, 'Gitako ate a bug, but it should recovery soon!') } } @@ -78,6 +80,10 @@ const setShouldShow = dispatch => shouldShow => { DOMHelper.setBodyIndent(shouldShow) } +const setError = dispatch => error => { + dispatch({ error }) +} + const toggleShowSettings = dispatch => () => dispatch(({ showSettings }) => ({ showSettings: !showSettings })) const onAccessTokenChange = dispatch => accessToken => dispatch({ accessToken }) @@ -99,5 +105,6 @@ export default { onShortcutChange, setMetaData, setCompressSingleton, + setError, handleError, } From 2a5e517fab234e995bd63f7dd2da38a9bb1a0be1 Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Wed, 19 Sep 2018 21:49:47 +0800 Subject: [PATCH 071/119] 0.3.3 --- package.json | 2 +- src/manifest.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index de4b11b..15e8668 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gitako", - "version": "0.3.2", + "version": "0.3.3", "description": "yet another extension for GitHub", "repository": "https://github.com/EnixCoda/Gitako", "author": "EnixCoda", diff --git a/src/manifest.json b/src/manifest.json index 99f4c3a..eec0488 100644 --- a/src/manifest.json +++ b/src/manifest.json @@ -1,7 +1,7 @@ { "manifest_version": 2, "name": "Gitako", - "version": "0.3.2", + "version": "0.3.3", "author": "EnixCoda", "icons": { "128": "icons/Gitako-128x128.png" From 07163774e71827aebb7199f038ab8f715d6fc656 Mon Sep 17 00:00:00 2001 From: Enix Date: Thu, 20 Sep 2018 10:33:24 +0800 Subject: [PATCH 072/119] fix: prevent loading at non-project page --- src/driver/core/SideBar.js | 1 + src/utils/URLHelper.js | 16 +++++++++++++--- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/driver/core/SideBar.js b/src/driver/core/SideBar.js index 12d7b82..489698c 100644 --- a/src/driver/core/SideBar.js +++ b/src/driver/core/SideBar.js @@ -6,6 +6,7 @@ import keyHelper from '../../utils/keyHelper' const init = dispatch => async () => { try { + if (!URLHelper.isInRepoPage()) return let nothingWentWrong = true const metaData = URLHelper.parse() dispatch(setMetaData, metaData) diff --git a/src/utils/URLHelper.js b/src/utils/URLHelper.js index 07bdfde..4fc890a 100644 --- a/src/utils/URLHelper.js +++ b/src/utils/URLHelper.js @@ -33,12 +33,21 @@ const TYPES = { // known but not related types: issues, pulls, wiki, insight, // TODO: record more types } -function isInCodePage(metaData = {}) { - const { userName, repoName, type, branchName } = { ...parseRaw(), ...metaData } + +function isInRepoPage(metaData) { + const { userName, repoName } = metaData || parseRaw() return Boolean( userName && !RESERVED_NAME.find(_ => _ === userName) && - repoName && + repoName + ) +} + +function isInCodePage(metaData = {}) { + const mergedRepo = { ...parseRaw(), ...metaData } + const { type, branchName } = mergedRepo + return Boolean( + isInRepoPage(mergedRepo) && (!type || type === TYPES.TREE || type === TYPES.BLOB) && type !== TYPES.COMMIT && (branchName || (!type && !branchName)) @@ -52,6 +61,7 @@ function getCurrentPath(decode = false) { export default { getCurrentPath, + isInRepoPage, isInCodePage, parse, } From 55ce652610df2bc72e984bb11922432276f60d01 Mon Sep 17 00:00:00 2001 From: Enix Date: Thu, 20 Sep 2018 10:33:46 +0800 Subject: [PATCH 073/119] =?UTF-8?q?fix:=20handle=20projects=20not=20set=20?= =?UTF-8?q?=E2=80=98master=E2=80=99=20as=20default=20branch?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/driver/core/SideBar.js | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/src/driver/core/SideBar.js b/src/driver/core/SideBar.js index 489698c..24c1686 100644 --- a/src/driver/core/SideBar.js +++ b/src/driver/core/SideBar.js @@ -12,16 +12,29 @@ const init = dispatch => async () => { dispatch(setMetaData, metaData) const { access_token: accessToken, shortcut, compressSingletonFolder } = await configHelper.get() dispatch({ accessToken, toggleShowSideBarShortcut: shortcut, compressSingletonFolder }) - const aggressivelyGotTreeData = GitHubHelper.getTreeData({ + const defaultBranchName = 'master' + let aggressivelyGotTreeData = GitHubHelper.getTreeData({ ...metaData, - branchName: metaData.branchName || 'master', + branchName: metaData.branchName || defaultBranchName, accessToken, }).catch(err => { nothingWentWrong = false dispatch(handleError, err) }) const metaDataFromAPI = await GitHubHelper.getRepoMeta({ ...metaData, accessToken }) - const branchName = metaData.branchName || metaDataFromAPI['default_branch'] + const projectDefaultBranchName = metaDataFromAPI['default_branch'] + if (!metaData.branchName) { + // User accessed repo's homepage(no branch name in URL) and we predicted its default branch to be 'master' + if (projectDefaultBranchName !== defaultBranchName) { + // And the repo do not use {defaultBranchName} as default branch, + aggressivelyGotTreeData = GitHubHelper.getTreeData({ + ...metaData, + branchName: projectDefaultBranchName, + accessToken, + }) + } + } + const branchName = metaData.branchName || projectDefaultBranchName Object.assign(metaData, { branchName, api: metaDataFromAPI }) dispatch(setMetaData, metaData) const shouldShow = URLHelper.isInCodePage(metaData) From 27ab86cac85a239c210d2af35bef9197067db2e5 Mon Sep 17 00:00:00 2001 From: Enix Date: Thu, 20 Sep 2018 11:08:57 +0800 Subject: [PATCH 074/119] feat: mount toggle button anyway --- src/driver/core/SideBar.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/driver/core/SideBar.js b/src/driver/core/SideBar.js index 24c1686..4dd3192 100644 --- a/src/driver/core/SideBar.js +++ b/src/driver/core/SideBar.js @@ -41,11 +41,18 @@ const init = dispatch => async () => { dispatch(setShouldShow, nothingWentWrong && shouldShow) aggressivelyGotTreeData .then(treeData => { - dispatch({ logoContainerElement: DOMHelper.insertLogoMountPoint() }) dispatch({ treeData }) }) + .catch(err => { + dispatch(handleError, err) + }) + .then(() => { + dispatch({ logoContainerElement: DOMHelper.insertLogoMountPoint() }) + }) } catch (err) { dispatch(handleError, err) + } finally { + dispatch({ logoContainerElement: DOMHelper.insertLogoMountPoint() }) } } From 5ed4bcb7c48bc3d059b267647de2dff0a06a7a28 Mon Sep 17 00:00:00 2001 From: Enix Date: Thu, 20 Sep 2018 14:02:42 +0800 Subject: [PATCH 075/119] perf: lower CPU usage for dev mode --- package.json | 1 + yarn.lock | 43 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/package.json b/package.json index 15e8668..2182354 100644 --- a/package.json +++ b/package.json @@ -36,6 +36,7 @@ "eslint-plugin-class-property": "^1.1.0", "eslint-plugin-react": "^7.9.1", "file-loader": "^1.1.5", + "fsevents": "^1.2.4", "json-loader": "^0.5.7", "less": "^2.7.3", "less-loader": "^4.0.5", diff --git a/yarn.lock b/yarn.lock index c4e1aca..5f169df 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1754,6 +1754,10 @@ decode-uri-component@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" +deep-extend@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" + deep-extend@~0.4.0: version "0.4.2" resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.4.2.tgz#48b699c27e334bf89f10892be432f6e4c7d34a7f" @@ -2465,6 +2469,13 @@ fsevents@^1.1.2: nan "^2.9.2" node-pre-gyp "^0.9.0" +fsevents@^1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.2.4.tgz#f41dcb1af2582af3692da36fc55cbd8e1041c426" + dependencies: + nan "^2.9.2" + node-pre-gyp "^0.10.0" + function-bind@^1.0.2, function-bind@^1.1.0, function-bind@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" @@ -3652,6 +3663,14 @@ needle@^2.2.0: iconv-lite "^0.4.4" sax "^1.2.4" +needle@^2.2.1: + version "2.2.3" + resolved "https://registry.yarnpkg.com/needle/-/needle-2.2.3.tgz#c1b04da378cd634d8befe2de965dc2cfb0fd65ca" + dependencies: + debug "^2.1.2" + iconv-lite "^0.4.4" + sax "^1.2.4" + neo-async@^2.5.0: version "2.5.1" resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.5.1.tgz#acb909e327b1e87ec9ef15f41b8a269512ad41ee" @@ -3695,6 +3714,21 @@ node-libs-browser@^2.0.0: util "^0.10.3" vm-browserify "0.0.4" +node-pre-gyp@^0.10.0: + version "0.10.3" + resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.10.3.tgz#3070040716afdc778747b61b6887bf78880b80fc" + dependencies: + detect-libc "^1.0.2" + mkdirp "^0.5.1" + needle "^2.2.1" + nopt "^4.0.1" + npm-packlist "^1.1.6" + npmlog "^4.0.2" + rc "^1.2.7" + rimraf "^2.6.1" + semver "^5.3.0" + tar "^4" + node-pre-gyp@^0.9.0: version "0.9.1" resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.9.1.tgz#f11c07516dd92f87199dbc7e1838eab7cd56c9e0" @@ -4466,6 +4500,15 @@ rc@^1.1.7: minimist "^1.2.0" strip-json-comments "~2.0.1" +rc@^1.2.7: + version "1.2.8" + resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" + dependencies: + deep-extend "^0.6.0" + ini "~1.3.0" + minimist "^1.2.0" + strip-json-comments "~2.0.1" + react-dom@^16.4.0: version "16.4.0" resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.4.0.tgz#099f067dd5827ce36a29eaf9a6cdc7cbf6216b1e" From f755ad9e713e7ce257c8ae0847e14852373ecb1a Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Fri, 21 Sep 2018 00:02:35 +0800 Subject: [PATCH 076/119] 0.3.4 --- package.json | 2 +- src/manifest.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 2182354..52467ce 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gitako", - "version": "0.3.3", + "version": "0.3.4", "description": "yet another extension for GitHub", "repository": "https://github.com/EnixCoda/Gitako", "author": "EnixCoda", diff --git a/src/manifest.json b/src/manifest.json index eec0488..ea06521 100644 --- a/src/manifest.json +++ b/src/manifest.json @@ -1,7 +1,7 @@ { "manifest_version": 2, "name": "Gitako", - "version": "0.3.3", + "version": "0.3.4", "author": "EnixCoda", "icons": { "128": "icons/Gitako-128x128.png" From bb5f745b45e5e9249f363b878ca18a0ad39caada Mon Sep 17 00:00:00 2001 From: Enix Date: Fri, 21 Sep 2018 17:30:19 +0800 Subject: [PATCH 077/119] fix(search): make search more stable --- src/driver/core/FileExplorer.js | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/driver/core/FileExplorer.js b/src/driver/core/FileExplorer.js index 67762f4..6484ced 100644 --- a/src/driver/core/FileExplorer.js +++ b/src/driver/core/FileExplorer.js @@ -137,10 +137,14 @@ const handleKeyDown = dispatch => ({ key }) => dispatch(({ visibleNodes: { nodes } }) -const handleSearchKeyChange = dispatch => async event => { - const searchKey = event.target.value - await visibleNodesGenerator.search(searchKey) - dispatch(updateVisibleNodes) +const handleSearchKeyChange = dispatch => { + let i = 0 + return async event => { + const searchKey = event.target.value + const j = i += 1 + await visibleNodesGenerator.search(searchKey) + if (i === j) dispatch(updateVisibleNodes) + } } const delayExpandThreshold = 400 From 6593bf6121037ad421366395b339ab4820d66228 Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Fri, 21 Sep 2018 22:24:29 +0800 Subject: [PATCH 078/119] fix: prevent covered by header --- src/content.less | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/content.less b/src/content.less index fc5f427..d829aac 100644 --- a/src/content.less +++ b/src/content.less @@ -74,7 +74,7 @@ top: 5px; left: 0; transform: translate((@side-bar-base-width - 30px)); - z-index: 2; + z-index: 32; display: inline-flex; justify-content: center; align-items: center; @@ -135,7 +135,7 @@ top: 0; left: 0; height: 100vh; - z-index: 2; + z-index: 32; // be more aggressive, against header in guest mode min-width: @side-bar-base-width; display: flex; justify-content: flex-end; From f4630f0607c688b18120739c6370b7caffc14e62 Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Fri, 21 Sep 2018 22:52:14 +0800 Subject: [PATCH 079/119] fix: prevent error of keys when opening large dir --- src/driver/core/FileExplorer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/driver/core/FileExplorer.js b/src/driver/core/FileExplorer.js index 6484ced..4a59a03 100644 --- a/src/driver/core/FileExplorer.js +++ b/src/driver/core/FileExplorer.js @@ -211,7 +211,7 @@ const mountExpandingIndicator = dispatch => node => dispatch(({ visibleNodes }) dummyVisibleNodes.nodes.splice( dummyVisibleNodes.nodes.indexOf(node) + 1, 0, - { virtual: true, name: 'Loading' }, + { virtual: true, name: 'Loading', path: '-' }, ) return { visibleNodes: dummyVisibleNodes From f75eeadc4f7aeb8a20c3edbc5e753e5d969e82a3 Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Fri, 21 Sep 2018 22:52:45 +0800 Subject: [PATCH 080/119] feat: hide search input when loading file tree --- src/components/FileExplorer.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/components/FileExplorer.js b/src/components/FileExplorer.js index 13a77a8..240de61 100644 --- a/src/components/FileExplorer.js +++ b/src/components/FileExplorer.js @@ -87,11 +87,15 @@ export default class FileExplorer extends React.Component { const { stateText, visibleNodes, freeze, handleKeyDown, handleSearchKeyChange, onNodeClick } = this.props return (
- { - !visibleNodes || stateText + stateText ? - : this.renderFiles(visibleNodes, onNodeClick) + : visibleNodes && ( + + + {this.renderFiles(visibleNodes, onNodeClick)} + + ) }
) From 0b3cc7f61a3117ee3f1f817325e413bc5501c33f Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Fri, 21 Sep 2018 23:39:59 +0800 Subject: [PATCH 081/119] refactor(FileExplorer): split logic of setting up tree data --- src/components/FileExplorer.js | 7 ++++--- src/driver/core/FileExplorer.js | 12 +++++++----- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/components/FileExplorer.js b/src/components/FileExplorer.js index 240de61..568a436 100644 --- a/src/components/FileExplorer.js +++ b/src/components/FileExplorer.js @@ -37,8 +37,9 @@ export default class FileExplorer extends React.Component { } componentWillMount() { - const { init } = this.props + const { init, setUpTree, treeData } = this.props init() + setUpTree(treeData) } componentDidMount() { @@ -48,8 +49,8 @@ export default class FileExplorer extends React.Component { componentWillReceiveProps(nextProps) { if (nextProps.treeData !== this.props.treeData) { - const { init } = nextProps - init() + const { setUpTree } = nextProps + setUpTree() } } diff --git a/src/driver/core/FileExplorer.js b/src/driver/core/FileExplorer.js index 4a59a03..5c4294d 100644 --- a/src/driver/core/FileExplorer.js +++ b/src/driver/core/FileExplorer.js @@ -20,11 +20,12 @@ function getVisibleParentNode(nodes, focusedNode, depths) { const tasksAfterRender = [] const visibleNodesGenerator = new VisibleNodesGenerator() -const init = dispatch => () => dispatch(async (state, { treeData, metaData, compressSingletonFolder }) => { - if (!treeData) { - dispatch(setStateText, 'Fetching Tree Data...') - return - } +const init = dispatch => () => dispatch(async () => { + dispatch(setStateText, 'Fetching File List...') +}) + +const setUpTree = dispatch => () => dispatch(async (state, { treeData, metaData, compressSingletonFolder }) => { + if (!treeData) return dispatch(setStateText, 'Rendering File List...') const { root } = treeParser.parse(treeData, metaData) visibleNodesGenerator.setCompress(compressSingletonFolder) @@ -225,6 +226,7 @@ const updateVisibleNodes = dispatch => () => { export default { init, + setUpTree, execAfterRender, setStateText, handleKeyDown, From b8037e2755483fd89c84b71de9c53c04a844419c Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Fri, 21 Sep 2018 23:43:17 +0800 Subject: [PATCH 082/119] feat: show branch name at beta bar --- src/components/MetaBar.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/components/MetaBar.js b/src/components/MetaBar.js index 740c4d0..b49b901 100644 --- a/src/components/MetaBar.js +++ b/src/components/MetaBar.js @@ -12,6 +12,8 @@ export default function MetaBar({ metaData }) {
{metaData.repoName} +  /  + {metaData.branchName}
) } From de1d1b07f07da762b3f752d128c0bf5c82b6023d Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Sat, 22 Sep 2018 00:42:37 +0800 Subject: [PATCH 083/119] 0.3.5 --- package.json | 2 +- src/manifest.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 52467ce..c1a5ba1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gitako", - "version": "0.3.4", + "version": "0.3.5", "description": "yet another extension for GitHub", "repository": "https://github.com/EnixCoda/Gitako", "author": "EnixCoda", diff --git a/src/manifest.json b/src/manifest.json index ea06521..a702c32 100644 --- a/src/manifest.json +++ b/src/manifest.json @@ -1,7 +1,7 @@ { "manifest_version": 2, "name": "Gitako", - "version": "0.3.4", + "version": "0.3.5", "author": "EnixCoda", "icons": { "128": "icons/Gitako-128x128.png" From 3c81f960793ebef8bcd159614f56b18b823c7c39 Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Sat, 22 Sep 2018 17:04:59 +0800 Subject: [PATCH 084/119] fix: handle more branch name types MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Handles projects not use ‘master’ as default branch Handles projects has branches whose name contains ‘/‘ --- src/driver/core/SideBar.js | 6 +++--- src/utils/DOMHelper.js | 21 +++++++++++++++++++++ 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/src/driver/core/SideBar.js b/src/driver/core/SideBar.js index 4dd3192..edace74 100644 --- a/src/driver/core/SideBar.js +++ b/src/driver/core/SideBar.js @@ -12,10 +12,10 @@ const init = dispatch => async () => { dispatch(setMetaData, metaData) const { access_token: accessToken, shortcut, compressSingletonFolder } = await configHelper.get() dispatch({ accessToken, toggleShowSideBarShortcut: shortcut, compressSingletonFolder }) - const defaultBranchName = 'master' + const detectedBranchName = DOMHelper.getCurrentBranch() || 'master' let aggressivelyGotTreeData = GitHubHelper.getTreeData({ ...metaData, - branchName: metaData.branchName || defaultBranchName, + branchName: detectedBranchName, accessToken, }).catch(err => { nothingWentWrong = false @@ -25,7 +25,7 @@ const init = dispatch => async () => { const projectDefaultBranchName = metaDataFromAPI['default_branch'] if (!metaData.branchName) { // User accessed repo's homepage(no branch name in URL) and we predicted its default branch to be 'master' - if (projectDefaultBranchName !== defaultBranchName) { + if (projectDefaultBranchName !== detectedBranchName) { // And the repo do not use {defaultBranchName} as default branch, aggressivelyGotTreeData = GitHubHelper.getTreeData({ ...metaData, diff --git a/src/utils/DOMHelper.js b/src/utils/DOMHelper.js index d9546da..ff96e08 100644 --- a/src/utils/DOMHelper.js +++ b/src/utils/DOMHelper.js @@ -20,6 +20,24 @@ function setBodyIndent(shouldShowGitako) { } } +function isInCodePage() { + const branchListSelector = '.branch-select-menu' + const listElement = document.querySelector(branchListSelector) + return Boolean(listElement) +} + +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() { + const selectedBranchSelector = '.select-menu.branch-select-menu .select-menu-modal .select-menu-list .select-menu-item.selected svg.select-menu-item-icon + span' + const selectedBranchElement = document.querySelector(selectedBranchSelector) + return selectedBranchElement ? selectedBranchElement.textContent.trim() : null +} + /** * add the logo element into DOM * @@ -338,4 +356,7 @@ export default { scrollToRepoContent, mountTopProgressBar, unmountTopProgressBar, + isInCodePage, + getBranches, + getCurrentBranch, } From 063a7edfa39de399626b5f8b8c72169bc0bf260c Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Sat, 22 Sep 2018 17:05:24 +0800 Subject: [PATCH 085/119] 0.3.6 --- package.json | 2 +- src/manifest.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index c1a5ba1..a7fab8f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gitako", - "version": "0.3.5", + "version": "0.3.6", "description": "yet another extension for GitHub", "repository": "https://github.com/EnixCoda/Gitako", "author": "EnixCoda", diff --git a/src/manifest.json b/src/manifest.json index a702c32..d18d630 100644 --- a/src/manifest.json +++ b/src/manifest.json @@ -1,7 +1,7 @@ { "manifest_version": 2, "name": "Gitako", - "version": "0.3.5", + "version": "0.3.6", "author": "EnixCoda", "icons": { "128": "icons/Gitako-128x128.png" From 7e57bf38122a744b82d37fb4327355184b50d558 Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Sat, 22 Sep 2018 22:26:29 +0800 Subject: [PATCH 086/119] fix: hide file explorer when auth error occurs --- src/components/SideBar.js | 7 ++++--- src/driver/core/SideBar.js | 4 ++++ 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/components/SideBar.js b/src/components/SideBar.js index 2e88f48..96c024f 100644 --- a/src/components/SideBar.js +++ b/src/components/SideBar.js @@ -94,9 +94,10 @@ export default class Gitako extends React.PureComponent { return (
{metaData && } - {errorDueToAuth && this.renderAccessDeniedError()} - {metaData && - + {errorDueToAuth + ? this.renderAccessDeniedError() + : metaData && + }
) diff --git a/src/driver/core/SideBar.js b/src/driver/core/SideBar.js index edace74..63ecf23 100644 --- a/src/driver/core/SideBar.js +++ b/src/driver/core/SideBar.js @@ -62,6 +62,7 @@ const handleError = dispatch => async (err) => { const repoPageType = await DOMHelper.getRepoPageType() const errorDueToAuth = repoPageType === REPO_TYPE_PRIVATE || err.message === BAD_CREDENTIALS dispatch({ errorDueToAuth }) + dispatch(setShowSettings, true) dispatch(setShouldShow, errorDueToAuth) if (!errorDueToAuth) { dispatch(setError, 'Gitako ate a bug, but it should recovery soon!') @@ -107,6 +108,8 @@ const setError = dispatch => error => { const toggleShowSettings = dispatch => () => dispatch(({ showSettings }) => ({ showSettings: !showSettings })) +const setShowSettings = dispatch => showSettings => dispatch({ showSettings }) + const onAccessTokenChange = dispatch => accessToken => dispatch({ accessToken }) const onShortcutChange = dispatch => shortcut => dispatch({ toggleShowSideBarShortcut: shortcut }) @@ -120,6 +123,7 @@ export default { onPJAXEnd, onKeyDown, setShouldShow, + setShowSettings, toggleShowSideBar, toggleShowSettings, onAccessTokenChange, From 3875d262943b45ea9e56d944c17c5624ea9cf2dc Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Sat, 22 Sep 2018 23:45:23 +0800 Subject: [PATCH 087/119] fix: be more tolerant for aggressive retrieved data --- src/driver/core/SideBar.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/driver/core/SideBar.js b/src/driver/core/SideBar.js index 63ecf23..81a793d 100644 --- a/src/driver/core/SideBar.js +++ b/src/driver/core/SideBar.js @@ -17,9 +17,8 @@ const init = dispatch => async () => { ...metaData, branchName: detectedBranchName, accessToken, - }).catch(err => { + }).catch(() => { nothingWentWrong = false - dispatch(handleError, err) }) const metaDataFromAPI = await GitHubHelper.getRepoMeta({ ...metaData, accessToken }) const projectDefaultBranchName = metaDataFromAPI['default_branch'] From da9c98cb83683f620ef52676746bf95b4c308a2a Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Sat, 22 Sep 2018 23:45:38 +0800 Subject: [PATCH 088/119] 0.3.7 --- package.json | 2 +- src/manifest.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index a7fab8f..ca9168c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gitako", - "version": "0.3.6", + "version": "0.3.7", "description": "yet another extension for GitHub", "repository": "https://github.com/EnixCoda/Gitako", "author": "EnixCoda", diff --git a/src/manifest.json b/src/manifest.json index d18d630..5ec78c0 100644 --- a/src/manifest.json +++ b/src/manifest.json @@ -1,7 +1,7 @@ { "manifest_version": 2, "name": "Gitako", - "version": "0.3.6", + "version": "0.3.7", "author": "EnixCoda", "icons": { "128": "icons/Gitako-128x128.png" From c1daf82f33fdc520345b1ed0089ce44dc9547b9f Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Sun, 23 Sep 2018 13:19:15 +0800 Subject: [PATCH 089/119] fix: recovery decorating page content --- src/components/Gitako.js | 4 ++++ src/utils/DOMHelper.js | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/components/Gitako.js b/src/components/Gitako.js index 9a057bc..d9d3c8f 100644 --- a/src/components/Gitako.js +++ b/src/components/Gitako.js @@ -8,6 +8,10 @@ import { raiseError } from '../analytics' @connect(GitakoCore) export default class Gitako extends React.PureComponent { + componentDidMount() { + const { init } = this.props + init() + } componentDidCatch(error) { raiseError(error) diff --git a/src/utils/DOMHelper.js b/src/utils/DOMHelper.js index ff96e08..5d7bf0d 100644 --- a/src/utils/DOMHelper.js +++ b/src/utils/DOMHelper.js @@ -268,7 +268,7 @@ const clippy = createClippy() let currentCodeSnippetElement function attachCopySnippet() { - const readmeSelector = '.repository-content .readme article' + const readmeSelector = '.repository-content #readme article' const readmeElement = document.querySelector(readmeSelector) if (readmeElement) { readmeElement.addEventListener('mouseover', ({ target }) => { From 9cec5e269292097da51bb2de59df477b2c32a0e2 Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Sun, 23 Sep 2018 13:25:10 +0800 Subject: [PATCH 090/119] fix: do not mount icon button when not in repo page --- src/driver/core/SideBar.js | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/driver/core/SideBar.js b/src/driver/core/SideBar.js index 81a793d..47d427c 100644 --- a/src/driver/core/SideBar.js +++ b/src/driver/core/SideBar.js @@ -7,6 +7,7 @@ import keyHelper from '../../utils/keyHelper' const init = dispatch => async () => { try { if (!URLHelper.isInRepoPage()) return + dispatch({ logoContainerElement: DOMHelper.insertLogoMountPoint() }) let nothingWentWrong = true const metaData = URLHelper.parse() dispatch(setMetaData, metaData) @@ -45,13 +46,8 @@ const init = dispatch => async () => { .catch(err => { dispatch(handleError, err) }) - .then(() => { - dispatch({ logoContainerElement: DOMHelper.insertLogoMountPoint() }) - }) } catch (err) { dispatch(handleError, err) - } finally { - dispatch({ logoContainerElement: DOMHelper.insertLogoMountPoint() }) } } From 36d54731329e06fd14621ff6e2c081bff91a22ce Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Sun, 23 Sep 2018 13:28:23 +0800 Subject: [PATCH 091/119] 0.3.8 --- package.json | 2 +- src/manifest.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index ca9168c..2bcf72a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gitako", - "version": "0.3.7", + "version": "0.3.8", "description": "yet another extension for GitHub", "repository": "https://github.com/EnixCoda/Gitako", "author": "EnixCoda", diff --git a/src/manifest.json b/src/manifest.json index 5ec78c0..eb67ff5 100644 --- a/src/manifest.json +++ b/src/manifest.json @@ -1,7 +1,7 @@ { "manifest_version": 2, "name": "Gitako", - "version": "0.3.7", + "version": "0.3.8", "author": "EnixCoda", "icons": { "128": "icons/Gitako-128x128.png" From 4c58531b7eec54f730432def3df6273b5ac77904 Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Sun, 23 Sep 2018 13:42:12 +0800 Subject: [PATCH 092/119] docs: rename --- src/manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/manifest.json b/src/manifest.json index eb67ff5..f875a2d 100644 --- a/src/manifest.json +++ b/src/manifest.json @@ -1,6 +1,6 @@ { "manifest_version": 2, - "name": "Gitako", + "name": "Gitako - enhanced Github code tree", "version": "0.3.8", "author": "EnixCoda", "icons": { From 1b3686221831c77e6fd4498fa097ea2ea12998ea Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Sun, 23 Sep 2018 13:42:15 +0800 Subject: [PATCH 093/119] 0.3.9 --- package.json | 2 +- src/manifest.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 2bcf72a..6c9c56f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gitako", - "version": "0.3.8", + "version": "0.3.9", "description": "yet another extension for GitHub", "repository": "https://github.com/EnixCoda/Gitako", "author": "EnixCoda", diff --git a/src/manifest.json b/src/manifest.json index f875a2d..e113a99 100644 --- a/src/manifest.json +++ b/src/manifest.json @@ -1,7 +1,7 @@ { "manifest_version": 2, "name": "Gitako - enhanced Github code tree", - "version": "0.3.8", + "version": "0.3.9", "author": "EnixCoda", "icons": { "128": "icons/Gitako-128x128.png" From de35aa3d35b6d8821d5a1a7bdcfc59b557ce3a0f Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Sat, 6 Oct 2018 20:04:05 +0800 Subject: [PATCH 094/119] fix: update title after pjax Fix #16 --- src/utils/DOMHelper.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/DOMHelper.js b/src/utils/DOMHelper.js index 5d7bf0d..85773e2 100644 --- a/src/utils/DOMHelper.js +++ b/src/utils/DOMHelper.js @@ -87,7 +87,7 @@ function scrollToNodeElement(index) { const pjax = new PJAX({ elements: '.pjax-link', - selectors: ['.repository-content'], + selectors: ['.repository-content', 'title'], scrollTo: false, analytics: false, cacheBust: false, From f72d96fb34f496f9e03631edd16d2935ff14a973 Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Sat, 6 Oct 2018 20:05:57 +0800 Subject: [PATCH 095/119] 0.3.10 --- package.json | 2 +- src/manifest.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 6c9c56f..75bd9ec 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gitako", - "version": "0.3.9", + "version": "0.3.10", "description": "yet another extension for GitHub", "repository": "https://github.com/EnixCoda/Gitako", "author": "EnixCoda", diff --git a/src/manifest.json b/src/manifest.json index e113a99..29266cc 100644 --- a/src/manifest.json +++ b/src/manifest.json @@ -1,7 +1,7 @@ { "manifest_version": 2, "name": "Gitako - enhanced Github code tree", - "version": "0.3.9", + "version": "0.3.10", "author": "EnixCoda", "icons": { "128": "icons/Gitako-128x128.png" From a3df17967a31f7ec0a1a85f29e835cce6d30a521 Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Sun, 7 Oct 2018 21:07:49 +0800 Subject: [PATCH 096/119] fix: hide when in github settings page --- src/utils/URLHelper.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/URLHelper.js b/src/utils/URLHelper.js index 4fc890a..a5a20a8 100644 --- a/src/utils/URLHelper.js +++ b/src/utils/URLHelper.js @@ -24,7 +24,7 @@ function parse() { return parsedData } -const RESERVED_NAME = ['blog'] +const RESERVED_NAME = ['blog', 'settings'] // route types related to determining if sidebar should show const TYPES = { TREE: 'tree', From 106513e0517aa223079cbf61cffeb6777ef4b423 Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Sun, 7 Oct 2018 21:07:53 +0800 Subject: [PATCH 097/119] 0.3.11 --- package.json | 2 +- src/manifest.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 75bd9ec..fcce540 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gitako", - "version": "0.3.10", + "version": "0.3.11", "description": "yet another extension for GitHub", "repository": "https://github.com/EnixCoda/Gitako", "author": "EnixCoda", diff --git a/src/manifest.json b/src/manifest.json index 29266cc..1829c3d 100644 --- a/src/manifest.json +++ b/src/manifest.json @@ -1,7 +1,7 @@ { "manifest_version": 2, "name": "Gitako - enhanced Github code tree", - "version": "0.3.10", + "version": "0.3.11", "author": "EnixCoda", "icons": { "128": "icons/Gitako-128x128.png" From ece8080fc44a12c51c354ae273ea972e911079ff Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Mon, 8 Oct 2018 22:09:29 +0800 Subject: [PATCH 098/119] feat: handle organizations page --- src/utils/URLHelper.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/URLHelper.js b/src/utils/URLHelper.js index a5a20a8..61f93fa 100644 --- a/src/utils/URLHelper.js +++ b/src/utils/URLHelper.js @@ -24,7 +24,7 @@ function parse() { return parsedData } -const RESERVED_NAME = ['blog', 'settings'] +const RESERVED_NAME = ['blog', 'settings', 'orgs'] // route types related to determining if sidebar should show const TYPES = { TREE: 'tree', From d60f85dfe81d3175c270e456f493b7a14dbbd089 Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Mon, 8 Oct 2018 22:23:29 +0800 Subject: [PATCH 099/119] feat: handle new github header style --- src/content.less | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/content.less b/src/content.less index d829aac..1ea57eb 100644 --- a/src/content.less +++ b/src/content.less @@ -15,6 +15,11 @@ @media (min-width: @width-with-gitako) { margin-left: 0; } + + .Header > div { + width: 1012px; + margin: 0 auto; + } } .markdown-body { From 29ad80f6ac4a726ef1c192a1818f686aa00f0c8d Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Mon, 8 Oct 2018 22:33:19 +0800 Subject: [PATCH 100/119] feat: detect in repo page with DOM element --- src/utils/URLHelper.js | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/src/utils/URLHelper.js b/src/utils/URLHelper.js index 61f93fa..d7d1e82 100644 --- a/src/utils/URLHelper.js +++ b/src/utils/URLHelper.js @@ -24,7 +24,11 @@ function parse() { return parsedData } -const RESERVED_NAME = ['blog', 'settings', 'orgs'] +function isInRepoPage() { + const repoHeaderSelector = '.repohead' + return Boolean(document.querySelector(repoHeaderSelector)) +} + // route types related to determining if sidebar should show const TYPES = { TREE: 'tree', @@ -34,15 +38,6 @@ const TYPES = { // TODO: record more types } -function isInRepoPage(metaData) { - const { userName, repoName } = metaData || parseRaw() - return Boolean( - userName && - !RESERVED_NAME.find(_ => _ === userName) && - repoName - ) -} - function isInCodePage(metaData = {}) { const mergedRepo = { ...parseRaw(), ...metaData } const { type, branchName } = mergedRepo From 013a6706a46d76b5d73fa9ef99b6acd733140455 Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Mon, 8 Oct 2018 22:34:06 +0800 Subject: [PATCH 101/119] 0.3.12 --- package.json | 2 +- src/manifest.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index fcce540..740ce31 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gitako", - "version": "0.3.11", + "version": "0.3.12", "description": "yet another extension for GitHub", "repository": "https://github.com/EnixCoda/Gitako", "author": "EnixCoda", diff --git a/src/manifest.json b/src/manifest.json index 1829c3d..76dbb05 100644 --- a/src/manifest.json +++ b/src/manifest.json @@ -1,7 +1,7 @@ { "manifest_version": 2, "name": "Gitako - enhanced Github code tree", - "version": "0.3.11", + "version": "0.3.12", "author": "EnixCoda", "icons": { "128": "icons/Gitako-128x128.png" From 76135e59feab2dbbe6f91f95591d25e5ee817dcc Mon Sep 17 00:00:00 2001 From: Enix Date: Tue, 16 Oct 2018 15:54:48 +0800 Subject: [PATCH 102/119] build: update resolve & eslint rules --- .eslintrc.json | 14 ++++++++-- jsconfig.json | 16 +++++++++++ package.json | 1 + webpack.config.js | 2 +- yarn.lock | 69 +++++++++++++++++++++++++++++++++++++++++++++-- 5 files changed, 97 insertions(+), 5 deletions(-) create mode 100644 jsconfig.json diff --git a/.eslintrc.json b/.eslintrc.json index 37d4b01..632602f 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -28,11 +28,21 @@ "babel/object-curly-spacing": 0, "babel/quotes": 0, "babel/semi": 0, - "babel/no-unused-expressions": 1 + "babel/no-unused-expressions": 1, + "import/order": [ + 1, + { + "groups": ["external", "internal", "builtin", ["parent", "sibling", "index"]], + "newlines-between": "never" + } + ], + "babel/no-invalid-this": 0, + "no-unused-vars": 0 }, "plugins": [ "babel", - "react" + "react", + "import" ], "globals": { "chrome": true diff --git a/jsconfig.json b/jsconfig.json new file mode 100644 index 0000000..53d7052 --- /dev/null +++ b/jsconfig.json @@ -0,0 +1,16 @@ +{ + "compilerOptions": { + "experimentalDecorators": true, + "baseUrl": "./", + "paths": { + "*" : ["src/*", "packages/*"] + } + }, + "exclude": [ + "node_modules", + ], + "include": [ + "src/**/*", + "packages/**/*", + ] +} diff --git a/package.json b/package.json index 740ce31..bf0f16f 100644 --- a/package.json +++ b/package.json @@ -34,6 +34,7 @@ "eslint": "^4.19.1", "eslint-plugin-babel": "^5.1.0", "eslint-plugin-class-property": "^1.1.0", + "eslint-plugin-import": "^2.14.0", "eslint-plugin-react": "^7.9.1", "file-loader": "^1.1.5", "fsevents": "^1.2.4", diff --git a/webpack.config.js b/webpack.config.js index 362368c..c9b4b95 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -47,7 +47,7 @@ module.exports = { filename: '[name].js', }, resolve: { - modules: ['packages', 'node_modules'] + modules: [srcPath, packagesPath, 'node_modules'] }, module: { rules: [ diff --git a/yarn.lock b/yarn.lock index 5f169df..0febcce 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1469,6 +1469,10 @@ constants-browserify@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/constants-browserify/-/constants-browserify-1.0.0.tgz#c20b96d8c617748aaf1c16021760cd27fcb8cb75" +contains-path@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/contains-path/-/contains-path-0.1.0.tgz#fe8cf184ff6670b6baef01a9d4861a5cbec4120a" + content-type-parser@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/content-type-parser/-/content-type-parser-1.0.2.tgz#caabe80623e63638b2502fd4c7f12ff4ce2352e7" @@ -1734,7 +1738,7 @@ date-now@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/date-now/-/date-now-0.1.4.tgz#eaf439fd4d4848ad74e5cc7dbef200672b9e345b" -debug@^2.1.1, debug@^2.1.2, debug@^2.2.0, debug@^2.3.3, debug@^2.6.8: +debug@^2.1.1, debug@^2.1.2, debug@^2.2.0, debug@^2.3.3, debug@^2.6.8, debug@^2.6.9: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" dependencies: @@ -1848,6 +1852,13 @@ dir-glob@^2.0.0: arrify "^1.0.1" path-type "^3.0.0" +doctrine@1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-1.5.0.tgz#379dce730f6166f76cefa4e6707a159b02c5a6fa" + dependencies: + esutils "^2.0.2" + isarray "^1.0.0" + doctrine@^2.0.0, doctrine@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d" @@ -2057,6 +2068,20 @@ escope@^3.6.0: esrecurse "^4.1.0" estraverse "^4.1.1" +eslint-import-resolver-node@^0.3.1: + version "0.3.2" + resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.2.tgz#58f15fb839b8d0576ca980413476aab2472db66a" + dependencies: + debug "^2.6.9" + resolve "^1.5.0" + +eslint-module-utils@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.2.0.tgz#b270362cd88b1a48ad308976ce7fa54e98411746" + dependencies: + debug "^2.6.8" + pkg-dir "^1.0.0" + eslint-plugin-babel@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/eslint-plugin-babel/-/eslint-plugin-babel-5.1.0.tgz#9c76e476162041e50b6ba69aa4eae3bdd6a4e1c3" @@ -2069,6 +2094,21 @@ eslint-plugin-class-property@^1.1.0: dependencies: eslint "^3.19.0" +eslint-plugin-import@^2.14.0: + version "2.14.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.14.0.tgz#6b17626d2e3e6ad52cfce8807a845d15e22111a8" + dependencies: + contains-path "^0.1.0" + debug "^2.6.8" + doctrine "1.5.0" + eslint-import-resolver-node "^0.3.1" + eslint-module-utils "^2.2.0" + has "^1.0.1" + lodash "^4.17.4" + minimatch "^3.0.3" + read-pkg-up "^2.0.0" + resolve "^1.6.0" + eslint-plugin-react@^7.9.1: version "7.9.1" resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.9.1.tgz#101aadd15e7c7b431ed025303ac7b421a8e3dc15" @@ -2376,6 +2416,13 @@ find-cache-dir@^1.0.0: make-dir "^1.0.0" pkg-dir "^2.0.0" +find-up@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f" + dependencies: + path-exists "^2.0.0" + pinkie-promise "^2.0.0" + find-up@^2.0.0, find-up@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7" @@ -3529,7 +3576,7 @@ minimatch@3.0.3: dependencies: brace-expansion "^1.0.0" -minimatch@^3.0.2, minimatch@^3.0.4: +minimatch@^3.0.2, minimatch@^3.0.3, minimatch@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" dependencies: @@ -4013,6 +4060,12 @@ path-dirname@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/path-dirname/-/path-dirname-1.0.2.tgz#cc33d24d525e099a5388c0336c6e32b9160609e0" +path-exists@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-2.1.0.tgz#0feb6c64f0fc518d9a754dd5efb62c7022761f4b" + dependencies: + pinkie-promise "^2.0.0" + path-exists@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" @@ -4081,6 +4134,12 @@ pinkie@^2.0.0: version "2.0.4" resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" +pkg-dir@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-1.0.0.tgz#7a4b508a8d5bb2d629d447056ff4e9c9314cf3d4" + dependencies: + find-up "^1.0.0" + pkg-dir@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-2.0.0.tgz#f6d5d1109e19d63edf428e0bd57e12777615334b" @@ -4771,6 +4830,12 @@ resolve@^1.1.6: dependencies: path-parse "^1.0.5" +resolve@^1.5.0, resolve@^1.6.0: + version "1.8.1" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.8.1.tgz#82f1ec19a423ac1fbd080b0bab06ba36e84a7a26" + dependencies: + path-parse "^1.0.5" + restore-cursor@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-1.0.1.tgz#34661f46886327fed2991479152252df92daa541" From 8029e07e2061e4f6224e82139816e960ff40fd9f Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Tue, 16 Oct 2018 15:55:31 +0800 Subject: [PATCH 103/119] style: format code --- src/components/FileExplorer.js | 15 +++++-------- src/components/Gitako.js | 10 ++++----- src/components/Icon.js | 36 ++++++++++++++---------------- src/components/LoadingIndicator.js | 2 +- src/components/Node.js | 10 ++++----- src/components/PJAXLink.js | 2 +- src/components/Resizable.js | 4 ++-- src/components/ResizeHandler.js | 5 ++--- src/components/SettingsBar.js | 7 +++--- src/components/SideBar.js | 21 ++++++++--------- src/components/ToggleShowButton.js | 5 ++--- src/content.js | 7 +++--- src/driver/core/FileExplorer.js | 8 +++---- src/driver/core/Gitako.js | 2 +- src/driver/core/SideBar.js | 10 ++++----- src/utils/GitHubHelper.js | 2 +- src/utils/configHelper.js | 4 ++-- src/utils/treeParser.js | 2 +- 18 files changed, 68 insertions(+), 84 deletions(-) diff --git a/src/components/FileExplorer.js b/src/components/FileExplorer.js index 568a436..333837a 100644 --- a/src/components/FileExplorer.js +++ b/src/components/FileExplorer.js @@ -1,14 +1,11 @@ import React from 'react' import PropTypes from 'prop-types' - -import connect from '../driver/connect' -import { FileExplorer as FileExplorerCore } from '../driver/core' - -import SearchBar from './SearchBar' -import Node from './Node' -import LoadingIndicator from './LoadingIndicator' - -import cx from '../utils/cx' +import connect from 'driver/connect' +import { FileExplorer as FileExplorerCore } from 'driver/core' +import SearchBar from 'components/SearchBar' +import Node from 'components/Node' +import LoadingIndicator from 'components/LoadingIndicator' +import cx from 'utils/cx' @connect(FileExplorerCore) export default class FileExplorer extends React.Component { diff --git a/src/components/Gitako.js b/src/components/Gitako.js index d9d3c8f..aec2575 100644 --- a/src/components/Gitako.js +++ b/src/components/Gitako.js @@ -1,10 +1,8 @@ import React from 'react' -import SideBar from './SideBar' - -import { Gitako as GitakoCore } from '../driver/core' -import connect from '../driver/connect' - -import { raiseError } from '../analytics' +import SideBar from 'components/SideBar' +import { Gitako as GitakoCore } from 'driver/core' +import connect from 'driver/connect' +import { raiseError } from 'analytics' @connect(GitakoCore) export default class Gitako extends React.PureComponent { diff --git a/src/components/Icon.js b/src/components/Icon.js index 1723529..4f952e4 100644 --- a/src/components/Icon.js +++ b/src/components/Icon.js @@ -1,23 +1,21 @@ import React from 'react' - -import Submodule from '../assets/icons/octicons/file-submodule.svg?svgr' -import Grabber from '../assets/icons/octicons/grabber.svg?svgr' -import Octoface from '../assets/icons/octicons/octoface.svg?svgr' -import ChevronDown from '../assets/icons/octicons/chevron-down.svg?svgr' -import TriangleRight from '../assets/icons/octicons/triangle-right.svg?svgr' -import FilePdf from '../assets/icons/octicons/file-pdf.svg?svgr' -import File from '../assets/icons/octicons/file.svg?svgr' -import FileZip from '../assets/icons/octicons/file-zip.svg?svgr' -import Markdown from '../assets/icons/octicons/markdown.svg?svgr' -import FileMedia from '../assets/icons/octicons/file-media.svg?svgr' -import FileCode from '../assets/icons/octicons/file-code.svg?svgr' -// import FileBinary from '../assets/icons/octicons/file-binary.svg?svgr' -// import FileSymlinkDirectory from '../assets/icons/octicons/file-symlink-directory.svg?svgr' -// import FileSymlinkFile from '../assets/icons/octicons/file-symlink-file.svg?svgr' -import X from '../assets/icons/octicons/x.svg?svgr' -import Gear from '../assets/icons/octicons/gear.svg?svgr' - -import cx from '../utils/cx' +import Submodule from 'assets/icons/octicons/file-submodule.svg?svgr' +import Grabber from 'assets/icons/octicons/grabber.svg?svgr' +import Octoface from 'assets/icons/octicons/octoface.svg?svgr' +import ChevronDown from 'assets/icons/octicons/chevron-down.svg?svgr' +import TriangleRight from 'assets/icons/octicons/triangle-right.svg?svgr' +import FilePdf from 'assets/icons/octicons/file-pdf.svg?svgr' +import File from 'assets/icons/octicons/file.svg?svgr' +import FileZip from 'assets/icons/octicons/file-zip.svg?svgr' +import Markdown from 'assets/icons/octicons/markdown.svg?svgr' +import FileMedia from 'assets/icons/octicons/file-media.svg?svgr' +import FileCode from 'assets/icons/octicons/file-code.svg?svgr' +// import FileBinary from 'assets/icons/octicons/file-binary.svg?svgr' +// import FileSymlinkDirectory from 'assets/icons/octicons/file-symlink-directory.svg?svgr' +// import FileSymlinkFile from 'assets/icons/octicons/file-symlink-file.svg?svgr' +import X from 'assets/icons/octicons/x.svg?svgr' +import Gear from 'assets/icons/octicons/gear.svg?svgr' +import cx from 'utils/cx' function getSVGIconComponent(type) { switch (type) { diff --git a/src/components/LoadingIndicator.js b/src/components/LoadingIndicator.js index dfecccd..5aca587 100644 --- a/src/components/LoadingIndicator.js +++ b/src/components/LoadingIndicator.js @@ -1,5 +1,5 @@ import React from 'react' -import Icon from './Icon'; +import Icon from 'components/Icon'; export default function LoadingIndicator({ text }) { return ( diff --git a/src/components/Node.js b/src/components/Node.js index 26e8e08..3b6130f 100644 --- a/src/components/Node.js +++ b/src/components/Node.js @@ -1,10 +1,8 @@ import React from 'react' - -import Icon from './Icon' - -import cx from '../utils/cx' -import DOMHelper from '../utils/DOMHelper' -import LoadingIndicator from './LoadingIndicator'; +import Icon from 'components/Icon' +import cx from 'utils/cx' +import DOMHelper from 'utils/DOMHelper' +import LoadingIndicator from 'components/LoadingIndicator'; function getIconType(node) { switch (node.type) { diff --git a/src/components/PJAXLink.js b/src/components/PJAXLink.js index 6d4c723..6e87cc3 100644 --- a/src/components/PJAXLink.js +++ b/src/components/PJAXLink.js @@ -1,5 +1,5 @@ import React from 'react' -import DOMHelper from '../utils/DOMHelper' +import DOMHelper from 'utils/DOMHelper' export default function PJAXLink({ to, children }) { return React.cloneElement(children, { diff --git a/src/components/Resizable.js b/src/components/Resizable.js index bb92be8..1bef6a7 100644 --- a/src/components/Resizable.js +++ b/src/components/Resizable.js @@ -1,7 +1,7 @@ import React from 'react' import PropTypes from 'prop-types' -import ResizeHandler from './ResizeHandler' -import cx from '../utils/cx'; +import ResizeHandler from 'components/ResizeHandler' +import cx from 'utils/cx'; export default class Resizable extends React.PureComponent { static propTypes = { diff --git a/src/components/ResizeHandler.js b/src/components/ResizeHandler.js index b79a6d8..328742a 100644 --- a/src/components/ResizeHandler.js +++ b/src/components/ResizeHandler.js @@ -1,6 +1,5 @@ import React from 'react' - -import Icon from './Icon' +import Icon from 'components/Icon' export default class ResizeHandler extends React.PureComponent { pointerDown = false @@ -56,4 +55,4 @@ export default class ResizeHandler extends React.PureComponent {
) } -} \ No newline at end of file +} diff --git a/src/components/SettingsBar.js b/src/components/SettingsBar.js index 71082dc..d6a442d 100644 --- a/src/components/SettingsBar.js +++ b/src/components/SettingsBar.js @@ -1,10 +1,9 @@ import React from 'react' import PropTypes from 'prop-types' - +import Icon from 'components/Icon' +import configHelper, { config } from 'utils/configHelper' +import keyHelper from 'utils/keyHelper' import { version } from '../../package' -import Icon from './Icon' -import configHelper, { config } from '../utils/configHelper' -import keyHelper from '../utils/keyHelper' const wikiLinks = { compressSingletonFolder: 'https://github.com/EnixCoda/Gitako/wiki/Compress-Singleton-Folder', diff --git a/src/components/SideBar.js b/src/components/SideBar.js index 96c024f..6b7611d 100644 --- a/src/components/SideBar.js +++ b/src/components/SideBar.js @@ -1,17 +1,14 @@ import React from 'react' import PropTypes from 'prop-types' - -import { SideBar as SideBarCore } from '../driver/core' -import connect from '../driver/connect' - -import FileExplorer from './FileExplorer' -import ToggleShowButton from './ToggleShowButton' -import MetaBar from './MetaBar' -import SettingsBar from './SettingsBar' -import Portal from './Portal' -import Resizable from './Resizable' - -import cx from '../utils/cx' +import { SideBar as SideBarCore } from 'driver/core' +import connect from 'driver/connect' +import FileExplorer from 'components/FileExplorer' +import ToggleShowButton from 'components/ToggleShowButton' +import MetaBar from 'components/MetaBar' +import SettingsBar from 'components/SettingsBar' +import Portal from 'components/Portal' +import Resizable from 'components/Resizable' +import cx from 'utils/cx' @connect(SideBarCore) export default class Gitako extends React.PureComponent { diff --git a/src/components/ToggleShowButton.js b/src/components/ToggleShowButton.js index f57e607..df15d4a 100644 --- a/src/components/ToggleShowButton.js +++ b/src/components/ToggleShowButton.js @@ -1,7 +1,6 @@ import React from 'react' -import Icon from './Icon' - -import cx from '../utils/cx' +import Icon from 'components/Icon' +import cx from 'utils/cx' export default function Logo({ error, shouldShow, toggleShowSideBar }) { return ( diff --git a/src/content.js b/src/content.js index b553ce6..b86067f 100644 --- a/src/content.js +++ b/src/content.js @@ -1,9 +1,8 @@ import React from 'react' import ReactDOM from 'react-dom' - -import Gitako from './components/Gitako' -import { addMiddleware } from './driver/connect' -import { withErrorLog } from './analytics' +import Gitako from 'components/Gitako' +import { addMiddleware } from 'driver/connect' +import { withErrorLog } from 'analytics' import './content.less' diff --git a/src/driver/core/FileExplorer.js b/src/driver/core/FileExplorer.js index 5c4294d..d072c9a 100644 --- a/src/driver/core/FileExplorer.js +++ b/src/driver/core/FileExplorer.js @@ -1,7 +1,7 @@ -import DOMHelper from '../../utils/DOMHelper' -import treeParser from '../../utils/treeParser' -import URLHelper from '../../utils/URLHelper' -import VisibleNodesGenerator from '../../utils/VisibleNodesGenerator' +import DOMHelper from 'utils/DOMHelper' +import treeParser from 'utils/treeParser' +import URLHelper from 'utils/URLHelper' +import VisibleNodesGenerator from 'utils/VisibleNodesGenerator' function getVisibleParentNode(nodes, focusedNode, depths) { const focusedNodeIndex = nodes.indexOf(focusedNode) diff --git a/src/driver/core/Gitako.js b/src/driver/core/Gitako.js index d23a533..b0512c1 100644 --- a/src/driver/core/Gitako.js +++ b/src/driver/core/Gitako.js @@ -1,4 +1,4 @@ -import DOMHelper from '../../utils/DOMHelper' +import DOMHelper from 'utils/DOMHelper' const init = dispatch => () => { DOMHelper.decorateGitHubPageContent() diff --git a/src/driver/core/SideBar.js b/src/driver/core/SideBar.js index 47d427c..6b2853c 100644 --- a/src/driver/core/SideBar.js +++ b/src/driver/core/SideBar.js @@ -1,8 +1,8 @@ -import DOMHelper, { REPO_TYPE_PRIVATE } from '../../utils/DOMHelper' -import GitHubHelper, { NOT_FOUND, BAD_CREDENTIALS } from '../../utils/GitHubHelper' -import configHelper from '../../utils/configHelper' -import URLHelper from '../../utils/URLHelper' -import keyHelper from '../../utils/keyHelper' +import DOMHelper, { REPO_TYPE_PRIVATE } from 'utils/DOMHelper' +import GitHubHelper, { NOT_FOUND, BAD_CREDENTIALS } from 'utils/GitHubHelper' +import configHelper from 'utils/configHelper' +import URLHelper from 'utils/URLHelper' +import keyHelper from 'utils/keyHelper' const init = dispatch => async () => { try { diff --git a/src/utils/GitHubHelper.js b/src/utils/GitHubHelper.js index 76535c3..ccf1c36 100644 --- a/src/utils/GitHubHelper.js +++ b/src/utils/GitHubHelper.js @@ -1,4 +1,4 @@ -import { raiseError } from '../analytics' +import { raiseError } from 'analytics' export const NOT_FOUND = 'Repo Not Found' export const BAD_CREDENTIALS = 'Bad credentials' diff --git a/src/utils/configHelper.js b/src/utils/configHelper.js index 17f67be..f892577 100644 --- a/src/utils/configHelper.js +++ b/src/utils/configHelper.js @@ -1,5 +1,5 @@ -import storageHelper from './storageHelper' -import { pick } from './general' +import storageHelper from 'utils/storageHelper' +import { pick } from 'utils/general' export const config = { shortcut: 'shortcut', diff --git a/src/utils/treeParser.js b/src/utils/treeParser.js index e25488e..2ebbe62 100644 --- a/src/utils/treeParser.js +++ b/src/utils/treeParser.js @@ -1,4 +1,4 @@ -import GitHubHelper from './GitHubHelper' +import GitHubHelper from 'utils/GitHubHelper' const nodeTemplate = { name: null, From 71343e4af339249ca8201a6cb59a85e24276fa99 Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Mon, 15 Oct 2018 23:50:06 +0800 Subject: [PATCH 104/119] feat(FileExplorer): handle git submodules --- package.json | 1 + src/driver/core/FileExplorer.js | 39 ++++++++++++++++++++++++++++----- src/utils/GitHubHelper.js | 6 +++++ src/utils/treeParser.js | 12 ++++++++++ yarn.lock | 2 +- 5 files changed, 54 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index bf0f16f..f750c5e 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "prod": "npm run stamp && NODE_ENV=production webpack && cd dist && rm -f ./gitako.zip && zip gitako.zip -r *" }, "dependencies": { + "ini": "^1.3.5", "nprogress": "^0.2.0", "octicons": "^7.1.0", "prop-types": "^15.6.1", diff --git a/src/driver/core/FileExplorer.js b/src/driver/core/FileExplorer.js index d072c9a..6c616b1 100644 --- a/src/driver/core/FileExplorer.js +++ b/src/driver/core/FileExplorer.js @@ -1,7 +1,9 @@ +import ini from 'ini' import DOMHelper from 'utils/DOMHelper' import treeParser from 'utils/treeParser' import URLHelper from 'utils/URLHelper' import VisibleNodesGenerator from 'utils/VisibleNodesGenerator' +import GitHubHelper from 'utils/GitHubHelper' function getVisibleParentNode(nodes, focusedNode, depths) { const focusedNodeIndex = nodes.indexOf(focusedNode) @@ -24,10 +26,37 @@ const init = dispatch => () => dispatch(async () => { dispatch(setStateText, 'Fetching File List...') }) -const setUpTree = dispatch => () => dispatch(async (state, { treeData, metaData, compressSingletonFolder }) => { +function resolveGitModules(root, blobData) { + if (blobData) { + if (blobData.encoding === 'base64') { + const content = atob(blobData.content) + const parsed = ini.parse(content) + Object.values(parsed).map(value => { + const { url, path } = value + // for now, handle modules at root only + const node = root.contents.find(node => node.path === path) + node.url = url + }) + } + } +} + +const setUpTree = dispatch => () => dispatch(async (state, { treeData, metaData, compressSingletonFolder, accessToken }) => { if (!treeData) return dispatch(setStateText, 'Rendering File List...') - const { root } = treeParser.parse(treeData, metaData) + const { root, gitModules } = treeParser.parse(treeData, metaData) + + if (gitModules) { + const blobData = await GitHubHelper.getBlobData({ + userName: metaData.userName, + repoName: metaData.repoName, + fileSHA: gitModules.sha, + accessToken, + }) + + resolveGitModules(root, blobData) + } + visibleNodesGenerator.setCompress(compressSingletonFolder) await visibleNodesGenerator.plantTree(root) @@ -193,16 +222,16 @@ const focusNode = dispatch => (node, skipScroll) => dispatch(({ visibleNodes: { dispatch(updateVisibleNodes) }) -const onNodeClick = dispatch => (node) => { +const onNodeClick = dispatch => (node) => dispatch((state, { metaData, accessToken }) => { if (node.type === 'tree') { dispatch(toggleNodeExpansion, node, true) } else if (node.type === 'blob') { dispatch(focusNode, node, true) DOMHelper.loadWithPJAX(node.url) } else if (node.type === 'commit') { - DOMHelper.loadWithPJAX(node.parent.url) + window.open(node.url, '_blank') } -} +}) const mountExpandingIndicator = dispatch => node => dispatch(({ visibleNodes }) => { const dummyVisibleNodes = { diff --git a/src/utils/GitHubHelper.js b/src/utils/GitHubHelper.js index ccf1c36..aab2694 100644 --- a/src/utils/GitHubHelper.js +++ b/src/utils/GitHubHelper.js @@ -26,6 +26,11 @@ async function getTreeData({ userName, repoName, branchName, accessToken }) { return await request(url, { accessToken }) } +async function getBlobData({ userName, repoName, accessToken, fileSHA }) { + const url = `https://api.github.com/repos/${userName}/${repoName}/git/blobs/${fileSHA}` + return await request(url, { accessToken }) +} + function getUrlForRedirect({ userName, repoName, branchName }, type = 'blob', path) { return `https://github.com/${userName}/${repoName}/${type}/${branchName}/${path}` } @@ -33,5 +38,6 @@ function getUrlForRedirect({ userName, repoName, branchName }, type = 'blob', pa export default { getRepoMeta, getTreeData, + getBlobData, getUrlForRedirect, } diff --git a/src/utils/treeParser.js b/src/utils/treeParser.js index 2ebbe62..69ec360 100644 --- a/src/utils/treeParser.js +++ b/src/utils/treeParser.js @@ -30,6 +30,16 @@ function setParentNode(root, parent = null) { } } +function findGitModules(root) { + if (root.contents) { + const modulesFile = root.contents.find(content => content.name === '.gitmodules') + if (modulesFile) { + return modulesFile + } + } + return null +} + function parse(treeData, metaData) { const { tree } = treeData @@ -71,7 +81,9 @@ function parse(treeData, metaData) { }) setParentNode(root) + return { + gitModules: findGitModules(root), root: sortFoldersToFront(root), } } diff --git a/yarn.lock b/yarn.lock index 0febcce..ae9c87a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2913,7 +2913,7 @@ inherits@2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.1.tgz#b17d08d326b4423e568eff719f91b0b1cbdf69f1" -ini@~1.3.0: +ini@^1.3.5, ini@~1.3.0: version "1.3.5" resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927" From 8066d6cabc328e2f717e04c8d0228ced0d558d83 Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Tue, 16 Oct 2018 23:59:15 +0800 Subject: [PATCH 105/119] build: edit jsconfig --- jsconfig.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/jsconfig.json b/jsconfig.json index 53d7052..b50e39b 100644 --- a/jsconfig.json +++ b/jsconfig.json @@ -1,5 +1,7 @@ { "compilerOptions": { + "jsx": "react", + "target": "es2017", "experimentalDecorators": true, "baseUrl": "./", "paths": { From 37163b6701d0f3273e30f79557f5f21199e79fd0 Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Tue, 16 Oct 2018 23:59:25 +0800 Subject: [PATCH 106/119] 0.3.13 --- package.json | 2 +- src/manifest.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index f750c5e..aa7bb4e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gitako", - "version": "0.3.12", + "version": "0.3.13", "description": "yet another extension for GitHub", "repository": "https://github.com/EnixCoda/Gitako", "author": "EnixCoda", diff --git a/src/manifest.json b/src/manifest.json index 76dbb05..eb8da88 100644 --- a/src/manifest.json +++ b/src/manifest.json @@ -1,7 +1,7 @@ { "manifest_version": 2, "name": "Gitako - enhanced Github code tree", - "version": "0.3.12", + "version": "0.3.13", "author": "EnixCoda", "icons": { "128": "icons/Gitako-128x128.png" From 624ab8a806f362eb544369b0c95fce6b7c85ecd8 Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Wed, 17 Oct 2018 00:14:52 +0800 Subject: [PATCH 107/119] feat(SettingsBar): make reload easier for user --- src/components/SettingsBar.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/SettingsBar.js b/src/components/SettingsBar.js index d6a442d..9fcf448 100644 --- a/src/components/SettingsBar.js +++ b/src/components/SettingsBar.js @@ -89,7 +89,7 @@ export default class SettingsBar extends React.PureComponent { onAccessTokenChange(accessToken) this.setState({ accessToken: '', - accessTokenHint: 'Your token is saved, will work after reloading the page!', + accessTokenHint: window.location.reload()}>Reload to activate!, }) } } @@ -126,7 +126,7 @@ export default class SettingsBar extends React.PureComponent { const { setCompressSingleton } = this.props setCompressSingleton(compress) this.setState({ - compressHint: 'Saved, reload page to apply!', + compressHint: Saved, window.location.reload()}>reload to apply., }) } From 1dce80d78e4c018975d9c3f750bbedd46ebc482b Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Wed, 17 Oct 2018 22:22:56 +0800 Subject: [PATCH 108/119] feat: allow hide settings by clicking file explorer --- src/components/FileExplorer.js | 4 ++-- src/components/SideBar.js | 4 ++-- src/content.less | 7 ++++++- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/components/FileExplorer.js b/src/components/FileExplorer.js index 333837a..3dd07c6 100644 --- a/src/components/FileExplorer.js +++ b/src/components/FileExplorer.js @@ -82,9 +82,9 @@ export default class FileExplorer extends React.Component { } render() { - const { stateText, visibleNodes, freeze, handleKeyDown, handleSearchKeyChange, onNodeClick } = this.props + const { stateText, visibleNodes, freeze, handleKeyDown, handleSearchKeyChange, onNodeClick, toggleShowSettings } = this.props return ( -
+
{ stateText ? diff --git a/src/components/SideBar.js b/src/components/SideBar.js index 6b7611d..c43c8d7 100644 --- a/src/components/SideBar.js +++ b/src/components/SideBar.js @@ -87,14 +87,14 @@ export default class Gitako extends React.PureComponent { } renderContent() { - const { errorDueToAuth, metaData, treeData, showSettings, accessToken, compressSingletonFolder } = this.props + const { errorDueToAuth, metaData, treeData, showSettings, accessToken, compressSingletonFolder, toggleShowSettings } = this.props return (
{metaData && } {errorDueToAuth ? this.renderAccessDeniedError() : metaData && - + }
) diff --git a/src/content.less b/src/content.less index 1ea57eb..c8dd374 100644 --- a/src/content.less +++ b/src/content.less @@ -266,7 +266,12 @@ flex-wrap: nowrap; &.freeze { filter: blur(1.5px) opacity(0.6) grayscale(0.9); - pointer-events: none; + cursor: pointer; + + > * { + pointer-events: none; + user-select: none; + } } /* search input */ From b663ca052afaef50c1fc6c2ef891246dada657ec Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Wed, 17 Oct 2018 22:46:15 +0800 Subject: [PATCH 109/119] feat: focus search input on opening sidebar --- src/driver/core/SideBar.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/driver/core/SideBar.js b/src/driver/core/SideBar.js index 6b2853c..6ec6415 100644 --- a/src/driver/core/SideBar.js +++ b/src/driver/core/SideBar.js @@ -93,7 +93,7 @@ const onKeyDown = dispatch => e => { const toggleShowSideBar = dispatch => () => dispatch(({ shouldShow }) => dispatch(setShouldShow, !shouldShow)) const setShouldShow = dispatch => shouldShow => { - dispatch({ shouldShow }) + dispatch({ shouldShow }, shouldShow ? DOMHelper.focusSearchInput : null) DOMHelper.setBodyIndent(shouldShow) } From ff5806af68a0087955763cec558bbf0352533f69 Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Sat, 27 Oct 2018 18:12:20 +0800 Subject: [PATCH 110/119] feat: not upload 404 error --- src/utils/GitHubHelper.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/utils/GitHubHelper.js b/src/utils/GitHubHelper.js index aab2694..25bd775 100644 --- a/src/utils/GitHubHelper.js +++ b/src/utils/GitHubHelper.js @@ -8,12 +8,12 @@ async function request(url, { accessToken } = {}) { headers.Authorization = `token ${accessToken}` } const res = await fetch(url, { headers }) - if (!res.ok) raiseError(new Error(`Got ${res.statusText} when requesting ${url}`)) if (res.status === 200) return res.json() // for private repo, GitHub api also responses with 404 when unauthorized - if (res.status === 404) throw new Error(NOT_FOUND) + else if (res.status === 404) throw new Error(NOT_FOUND) + else if (!res.ok) raiseError(new Error(`Got ${res.statusText} when requesting ${url}`)) const content = await res.json() - throw new Error(content.message) + throw new Error(content && content.message) } async function getRepoMeta({ userName, repoName, accessToken }) { From 68b78216c90b1d33da65ed7c6c160c69f915d95e Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Sat, 27 Oct 2018 18:12:52 +0800 Subject: [PATCH 111/119] 0.3.14 --- package.json | 2 +- src/manifest.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index aa7bb4e..0af6d45 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gitako", - "version": "0.3.13", + "version": "0.3.14", "description": "yet another extension for GitHub", "repository": "https://github.com/EnixCoda/Gitako", "author": "EnixCoda", diff --git a/src/manifest.json b/src/manifest.json index eb8da88..d0ec204 100644 --- a/src/manifest.json +++ b/src/manifest.json @@ -1,7 +1,7 @@ { "manifest_version": 2, "name": "Gitako - enhanced Github code tree", - "version": "0.3.13", + "version": "0.3.14", "author": "EnixCoda", "icons": { "128": "icons/Gitako-128x128.png" From 6916850d85fae44aab2fa203e82167c2b5499c18 Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Sat, 27 Oct 2018 19:05:31 +0800 Subject: [PATCH 112/119] feat: handle API rate limit --- src/components/SettingsBar.js | 74 +++++++++++++++++++++++------------ src/components/SideBar.js | 43 ++++++++++++++++---- src/driver/core/SideBar.js | 6 +-- src/utils/GitHubHelper.js | 14 +++++-- 4 files changed, 97 insertions(+), 40 deletions(-) diff --git a/src/components/SettingsBar.js b/src/components/SettingsBar.js index 9fcf448..c36cdb2 100644 --- a/src/components/SettingsBar.js +++ b/src/components/SettingsBar.js @@ -89,7 +89,14 @@ export default class SettingsBar extends React.PureComponent { onAccessTokenChange(accessToken) this.setState({ accessToken: '', - accessTokenHint: window.location.reload()}>Reload to activate!, + accessTokenHint: ( + + window.location.reload()}> + Reload + {' '} + to activate! + + ), }) } } @@ -126,38 +133,44 @@ export default class SettingsBar extends React.PureComponent { const { setCompressSingleton } = this.props setCompressSingleton(compress) this.setState({ - compressHint: Saved, window.location.reload()}>reload to apply., + compressHint: ( + + Saved,{' '} + window.location.reload()}> + reload + {' '} + to apply. + + ), }) } render() { - const { accessTokenHint, toggleShowSideBarShortcut, compressSingletonFolder, shortcutHint, accessToken, compressHint } = this.state + const { + accessTokenHint, + toggleShowSideBarShortcut, + compressSingletonFolder, + shortcutHint, + accessToken, + compressHint, + } = this.state const { toggleShowSettings, activated, accessToken: hasAccessToken } = this.props return (
{activated && ( -

- Settings -

+

Settings

Access Token

- With access token provided, Gitako can access more repositories. -
- - Help: how to create access token? + + Why & how to create it?
- - Gitako stores the token in  - - chrome local storage - -  locally and safely. - -

Compress singleton folder  - (?) + + (?) +

{compressHint &&
{compressHint}
} @@ -220,7 +241,12 @@ export default class SettingsBar extends React.PureComponent { )}
- + v{version} {activated ? ( @@ -230,11 +256,7 @@ export default class SettingsBar extends React.PureComponent { onClick={toggleShowSettings} /> ) : ( - + )}
diff --git a/src/components/SideBar.js b/src/components/SideBar.js index c43c8d7..e1d995c 100644 --- a/src/components/SideBar.js +++ b/src/components/SideBar.js @@ -79,23 +79,46 @@ export default class Gitako extends React.PureComponent {
Access Denied

- Gitako needs access token with proper scopes (recommended: repo) to access this - repository. Please save it in the settings below. + 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, accessToken, compressSingletonFolder, toggleShowSettings } = this.props + const { + errorDueToAuth, + metaData, + treeData, + showSettings, + accessToken, + compressSingletonFolder, + toggleShowSettings, + } = this.props return (
{metaData && } {errorDueToAuth ? this.renderAccessDeniedError() - : metaData && - - } + : metaData && ( + + )}
) } @@ -119,7 +142,11 @@ export default class Gitako extends React.PureComponent { return (
- +
@@ -133,7 +160,7 @@ export default class Gitako extends React.PureComponent { compressSingletonFolder={compressSingletonFolder} toggleShowSideBarShortcut={toggleShowSideBarShortcut} setCompressSingleton={setCompressSingleton} - /> + />
diff --git a/src/driver/core/SideBar.js b/src/driver/core/SideBar.js index 6ec6415..bad4925 100644 --- a/src/driver/core/SideBar.js +++ b/src/driver/core/SideBar.js @@ -1,5 +1,5 @@ import DOMHelper, { REPO_TYPE_PRIVATE } from 'utils/DOMHelper' -import GitHubHelper, { NOT_FOUND, BAD_CREDENTIALS } from 'utils/GitHubHelper' +import GitHubHelper, { NOT_FOUND, BAD_CREDENTIALS, API_RATE_LIMIT } from 'utils/GitHubHelper' import configHelper from 'utils/configHelper' import URLHelper from 'utils/URLHelper' import keyHelper from 'utils/keyHelper' @@ -53,9 +53,9 @@ const init = dispatch => async () => { const handleError = dispatch => async (err) => { // TODO: detect request time exceeds limit - if (err.message === NOT_FOUND || err.message === BAD_CREDENTIALS) { + if (err.message === NOT_FOUND || err.message === BAD_CREDENTIALS || err.message === API_RATE_LIMIT ) { const repoPageType = await DOMHelper.getRepoPageType() - const errorDueToAuth = repoPageType === REPO_TYPE_PRIVATE || err.message === BAD_CREDENTIALS + const errorDueToAuth = repoPageType === REPO_TYPE_PRIVATE || err.message === BAD_CREDENTIALS || err.message === API_RATE_LIMIT dispatch({ errorDueToAuth }) dispatch(setShowSettings, true) dispatch(setShouldShow, errorDueToAuth) diff --git a/src/utils/GitHubHelper.js b/src/utils/GitHubHelper.js index 25bd775..5b11313 100644 --- a/src/utils/GitHubHelper.js +++ b/src/utils/GitHubHelper.js @@ -1,6 +1,11 @@ import { raiseError } from 'analytics' export const NOT_FOUND = 'Repo Not Found' export const BAD_CREDENTIALS = 'Bad credentials' +export const API_RATE_LIMIT = `API rate limit` + +function apiRateLimitExceeded(content) { + return content && content['documentation_url'] === 'https://developer.github.com/v3/#rate-limiting' +} async function request(url, { accessToken } = {}) { const headers = {} @@ -11,9 +16,12 @@ async function request(url, { accessToken } = {}) { if (res.status === 200) return res.json() // for private repo, GitHub api also responses with 404 when unauthorized else if (res.status === 404) throw new Error(NOT_FOUND) - else if (!res.ok) raiseError(new Error(`Got ${res.statusText} when requesting ${url}`)) - const content = await res.json() - throw new Error(content && content.message) + else { + const content = await res.json() + if (apiRateLimitExceeded(content)) throw new Error(API_RATE_LIMIT) + else if (!res.ok) raiseError(new Error(`Got ${res.statusText} when requesting ${url}`)) + throw new Error(content && content.message) + } } async function getRepoMeta({ userName, repoName, accessToken }) { From 9384e2d45f4811f8ceb073755b2864c2343968b4 Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Sat, 27 Oct 2018 19:05:41 +0800 Subject: [PATCH 113/119] 0.3.15 --- package.json | 2 +- src/manifest.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 0af6d45..b193e33 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gitako", - "version": "0.3.14", + "version": "0.3.15", "description": "yet another extension for GitHub", "repository": "https://github.com/EnixCoda/Gitako", "author": "EnixCoda", diff --git a/src/manifest.json b/src/manifest.json index d0ec204..5e9bd12 100644 --- a/src/manifest.json +++ b/src/manifest.json @@ -1,7 +1,7 @@ { "manifest_version": 2, "name": "Gitako - enhanced Github code tree", - "version": "0.3.14", + "version": "0.3.15", "author": "EnixCoda", "icons": { "128": "icons/Gitako-128x128.png" From 50c4eaae70e219c4d3cbcaf5892eced9f97c080d Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Sun, 28 Oct 2018 15:44:22 +0800 Subject: [PATCH 114/119] feat(settings): toggle copy buttons --- src/components/SettingsBar.js | 62 ++++++++++++++++++++++++++++++++--- src/components/SideBar.js | 8 +++++ src/driver/core/Gitako.js | 3 -- src/driver/core/SideBar.js | 15 ++++++--- src/utils/DOMHelper.js | 6 ++-- src/utils/configHelper.js | 2 ++ 6 files changed, 82 insertions(+), 14 deletions(-) diff --git a/src/components/SettingsBar.js b/src/components/SettingsBar.js index c36cdb2..0c61084 100644 --- a/src/components/SettingsBar.js +++ b/src/components/SettingsBar.js @@ -8,6 +8,8 @@ import { version } from '../../package' const wikiLinks = { compressSingletonFolder: 'https://github.com/EnixCoda/Gitako/wiki/Compress-Singleton-Folder', changeLog: 'https://github.com/EnixCoda/Gitako/wiki/Change-Log', + copyFileButton: 'https://github.com/EnixCoda/Gitako/wiki/Copy-file-and-snippet', + copySnippet: 'https://github.com/EnixCoda/Gitako/wiki/Copy-file-and-snippet', } const ACCESS_TOKEN_REGEXP = /^[0-9a-f]{40}$/ @@ -65,12 +67,12 @@ export default class SettingsBar extends React.PureComponent { } componentWillMount() { - const { toggleShowSideBarShortcut, compressSingletonFolder } = this.props - this.setState({ toggleShowSideBarShortcut, compressSingletonFolder }) + const { toggleShowSideBarShortcut, compressSingletonFolder, copyFileButton, copySnippetButton } = this.props + this.setState({ toggleShowSideBarShortcut, compressSingletonFolder, copyFileButton, copySnippetButton }) } - componentWillReceiveProps({ toggleShowSideBarShortcut, compressSingletonFolder }) { - this.setState({ toggleShowSideBarShortcut, compressSingletonFolder }) + componentWillReceiveProps({ toggleShowSideBarShortcut, compressSingletonFolder, copyFileButton, copySnippetButton }) { + this.setState({ toggleShowSideBarShortcut, compressSingletonFolder, copyFileButton, copySnippetButton }) } onInputAccessToken = event => { @@ -145,11 +147,27 @@ export default class SettingsBar extends React.PureComponent { }) } + setCopyFile = async e => { + const enabled = e.target.checked + await configHelper.setOne(config.copyFileButton, enabled) + const { setCopyFile } = this.props + setCopyFile(enabled) + } + + setCopySnippet = async e => { + const enabled = e.target.checked + await configHelper.setOne(config.copySnippetButton, enabled) + const { setCopySnippet } = this.props + setCopySnippet(enabled) + } + render() { const { accessTokenHint, toggleShowSideBarShortcut, compressSingletonFolder, + copyFileButton, + copySnippetButton, shortcutHint, accessToken, compressHint, @@ -228,6 +246,42 @@ export default class SettingsBar extends React.PureComponent { {compressHint &&
{compressHint}
}
+
+

+ Copy File  + + (?) + +

+ +
+
+

+ Copy Snippet  + + (?) + +

+ +

Issue

diff --git a/src/components/SideBar.js b/src/components/SideBar.js index e1d995c..51a2d82 100644 --- a/src/components/SideBar.js +++ b/src/components/SideBar.js @@ -131,6 +131,8 @@ export default class Gitako extends React.PureComponent { showSettings, accessToken, compressSingletonFolder, + copyFileButton, + copySnippetButton, toggleShowSideBarShortcut, logoContainerElement, toggleShowSideBar, @@ -138,6 +140,8 @@ export default class Gitako extends React.PureComponent { onShortcutChange, onAccessTokenChange, setCompressSingleton, + setCopyFile, + setCopySnippet, } = this.props return (
@@ -158,8 +162,12 @@ export default class Gitako extends React.PureComponent { activated={showSettings} accessToken={accessToken} compressSingletonFolder={compressSingletonFolder} + copyFileButton={copyFileButton} + copySnippetButton={copySnippetButton} toggleShowSideBarShortcut={toggleShowSideBarShortcut} setCompressSingleton={setCompressSingleton} + setCopyFile={setCopyFile} + setCopySnippet={setCopySnippet} />
diff --git a/src/driver/core/Gitako.js b/src/driver/core/Gitako.js index b0512c1..922294c 100644 --- a/src/driver/core/Gitako.js +++ b/src/driver/core/Gitako.js @@ -1,7 +1,4 @@ -import DOMHelper from 'utils/DOMHelper' - const init = dispatch => () => { - DOMHelper.decorateGitHubPageContent() } export default { diff --git a/src/driver/core/SideBar.js b/src/driver/core/SideBar.js index bad4925..f5cbf9d 100644 --- a/src/driver/core/SideBar.js +++ b/src/driver/core/SideBar.js @@ -11,8 +11,9 @@ const init = dispatch => async () => { let nothingWentWrong = true const metaData = URLHelper.parse() dispatch(setMetaData, metaData) - const { access_token: accessToken, shortcut, compressSingletonFolder } = await configHelper.get() - dispatch({ accessToken, toggleShowSideBarShortcut: shortcut, compressSingletonFolder }) + const { access_token: accessToken, shortcut, compressSingletonFolder, copyFileButton, copySnippetButton } = await configHelper.get() + DOMHelper.decorateGitHubPageContent({ copyFileButton, copySnippetButton }) + dispatch({ accessToken, toggleShowSideBarShortcut: shortcut, compressSingletonFolder, copyFileButton, copySnippetButton }) const detectedBranchName = DOMHelper.getCurrentBranch() || 'master' let aggressivelyGotTreeData = GitHubHelper.getTreeData({ ...metaData, @@ -69,9 +70,9 @@ const handleError = dispatch => async (err) => { } const onPJAXEnd = dispatch => () => { - dispatch(({ metaData }) => { + dispatch(({ metaData, copyFileButton, copySnippetButton }) => { DOMHelper.unmountTopProgressBar() - DOMHelper.decorateGitHubPageContent() + DOMHelper.decorateGitHubPageContent({ copyFileButton, copySnippetButton }) DOMHelper.focusSearchInput() const mergedMetaData = { ...metaData, ...URLHelper.parse() } dispatch(setShouldShow, URLHelper.isInCodePage(mergedMetaData)) @@ -113,6 +114,10 @@ const setMetaData = dispatch => metaData => dispatch({ metaData }) const setCompressSingleton = dispatch => compressSingletonFolder => dispatch({ compressSingletonFolder }) +const setCopyFile = dispatch => copyFileButton => dispatch({ copyFileButton }) + +const setCopySnippet = dispatch => copySnippetButton => dispatch({ copySnippetButton }) + export default { init, onPJAXEnd, @@ -125,6 +130,8 @@ export default { onShortcutChange, setMetaData, setCompressSingleton, + setCopyFile, + setCopySnippet, setError, handleError, } diff --git a/src/utils/DOMHelper.js b/src/utils/DOMHelper.js index 85773e2..dddca68 100644 --- a/src/utils/DOMHelper.js +++ b/src/utils/DOMHelper.js @@ -327,9 +327,9 @@ function clickOnNodeElement(index = 0) { /** * a combination of few above functions */ -function decorateGitHubPageContent() { - attachCopyFileBtn() - attachCopySnippet() +function decorateGitHubPageContent({ copyFileButton, copySnippetButton }) { + if (copyFileButton) attachCopyFileBtn() + if (copySnippetButton) attachCopySnippet() } function mountTopProgressBar() { diff --git a/src/utils/configHelper.js b/src/utils/configHelper.js index f892577..160f629 100644 --- a/src/utils/configHelper.js +++ b/src/utils/configHelper.js @@ -5,6 +5,8 @@ export const config = { shortcut: 'shortcut', accessToken: 'access_token', compressSingletonFolder: 'compressSingletonFolder', + copyFileButton: 'copyFileButton', + copySnippetButton: 'copySnippetButton', } const configKeys = Object.values(config) From 78e4bb8d24207fce5554852def201ff7176524d6 Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Sun, 28 Oct 2018 16:12:14 +0800 Subject: [PATCH 115/119] feat(SettingsBar): merge options --- src/components/SettingsBar.js | 134 +++++++++++++++------------------- 1 file changed, 57 insertions(+), 77 deletions(-) diff --git a/src/components/SettingsBar.js b/src/components/SettingsBar.js index 0c61084..16af412 100644 --- a/src/components/SettingsBar.js +++ b/src/components/SettingsBar.js @@ -63,7 +63,30 @@ export default class SettingsBar extends React.PureComponent { accessTokenHint: '', shortcutHint: '', toggleShowSideBarShortcut: '', - compressHint: '', + reloadHint: '', + moreOptions: [ + { + key: 'compress-singleton', + label: 'Compress singleton folder', + onChange: this.createOnChange(config.compressSingletonFolder, this.props.setCompressSingleton), + getValue: () => this.props.compressSingletonFolder, + wikiLink: wikiLinks.compressSingletonFolder, + }, + { + key: 'copy-file', + label: 'Copy File', + onChange: this.createOnChange(config.copyFileButton, this.props.setCopyFile), + getValue: () => this.props.copyFileButton, + wikiLink: wikiLinks.copyFileButton, + }, + { + key: 'copy-snippet', + label: 'Copy Snippet', + onChange: this.createOnChange(config.copySnippetButton, this.props.setCopySnippet), + getValue: () => this.props.copySnippetButton, + wikiLink: wikiLinks.copySnippet, + }, + ] } componentWillMount() { @@ -129,13 +152,9 @@ export default class SettingsBar extends React.PureComponent { this.setState({ toggleShowSideBarShortcut: shortcut }) } - setCompressSingletonFolder = async e => { - const compress = e.target.checked - await configHelper.setOne(config.compressSingletonFolder, compress) - const { setCompressSingleton } = this.props - setCompressSingleton(compress) + setReloadHint = () => { this.setState({ - compressHint: ( + reloadHint: ( Saved,{' '} window.location.reload()}> @@ -147,30 +166,24 @@ export default class SettingsBar extends React.PureComponent { }) } - setCopyFile = async e => { - const enabled = e.target.checked - await configHelper.setOne(config.copyFileButton, enabled) - const { setCopyFile } = this.props - setCopyFile(enabled) - } - - setCopySnippet = async e => { - const enabled = e.target.checked - await configHelper.setOne(config.copySnippetButton, enabled) - const { setCopySnippet } = this.props - setCopySnippet(enabled) + // writing this method as arrow function would be more verbose + createOnChange(configKey, set ) { + return async e => { + const enabled = e.target.checked + await configHelper.setOne(configKey, enabled) + set(enabled) + this.setReloadHint() + } } render() { const { accessTokenHint, toggleShowSideBarShortcut, - compressSingletonFolder, - copyFileButton, - copySnippetButton, shortcutHint, accessToken, - compressHint, + reloadHint, + moreOptions, } = this.state const { toggleShowSettings, activated, accessToken: hasAccessToken } = this.props return ( @@ -227,60 +240,27 @@ export default class SettingsBar extends React.PureComponent {
{shortcutHint && {shortcutHint}}
-
-

- Compress singleton folder  - - (?) - -

- - {compressHint &&
{compressHint}
} -
-
-

- Copy File  - - (?) - -

- -
-
-

- Copy Snippet  - - (?) - -

- +
+

More Options

+ {moreOptions.map(option => ( + + +
+
+ ))} + {reloadHint &&
{reloadHint}
}

Issue

From a76e02d2d01fba8407bee38ea8af1c23a3e7c48b Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Sun, 28 Oct 2018 16:30:38 +0800 Subject: [PATCH 116/119] chore: sorting --- src/components/SettingsBar.js | 6 +++++- src/components/SideBar.js | 9 +++++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/components/SettingsBar.js b/src/components/SettingsBar.js index 16af412..1e62884 100644 --- a/src/components/SettingsBar.js +++ b/src/components/SettingsBar.js @@ -50,9 +50,13 @@ export default class SettingsBar extends React.PureComponent { static propTypes = { accessToken: PropTypes.string.isRequired, activated: PropTypes.bool.isRequired, - compressSingletonFolder: PropTypes.bool.isRequired, onAccessTokenChange: PropTypes.func.isRequired, onShortcutChange: PropTypes.func.isRequired, + compressSingletonFolder: PropTypes.bool.isRequired, + copyFileButton: PropTypes.bool.isRequired, + copySnippetButton: PropTypes.bool.isRequired, + setCopyFile: PropTypes.func.isRequired, + setCopySnippet: PropTypes.func.isRequired, setCompressSingleton: PropTypes.func.isRequired, toggleShowSettings: PropTypes.func.isRequired, toggleShowSideBarShortcut: PropTypes.string.isRequired, diff --git a/src/components/SideBar.js b/src/components/SideBar.js index 51a2d82..55da877 100644 --- a/src/components/SideBar.js +++ b/src/components/SideBar.js @@ -31,8 +31,10 @@ export default class Gitako extends React.PureComponent { metaData: PropTypes.object, // file tree data treeData: PropTypes.object, - // compress singleton folder + // few settings compressSingletonFolder: PropTypes.bool, + copyFileButton: PropTypes.bool, + copySnippetButton: PropTypes.bool, init: PropTypes.func.isRequired, onPJAXEnd: PropTypes.func.isRequired, @@ -43,6 +45,9 @@ export default class Gitako extends React.PureComponent { onKeyDown: PropTypes.func.isRequired, onShortcutChange: PropTypes.func.isRequired, setMetaData: PropTypes.func.isRequired, + setCopyFile: PropTypes.func.isRequired, + setCopySnippet: PropTypes.func.isRequired, + setCompressSingleton: PropTypes.func.isRequired, } static defaultProps = { @@ -161,10 +166,10 @@ export default class Gitako extends React.PureComponent { onAccessTokenChange={onAccessTokenChange} activated={showSettings} accessToken={accessToken} + toggleShowSideBarShortcut={toggleShowSideBarShortcut} compressSingletonFolder={compressSingletonFolder} copyFileButton={copyFileButton} copySnippetButton={copySnippetButton} - toggleShowSideBarShortcut={toggleShowSideBarShortcut} setCompressSingleton={setCompressSingleton} setCopyFile={setCopyFile} setCopySnippet={setCopySnippet} From 91372f40592e143b8602f8055d906f1f2e6eae01 Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Sun, 28 Oct 2018 17:04:13 +0800 Subject: [PATCH 117/119] docs: minor changes --- src/components/SettingsBar.js | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/components/SettingsBar.js b/src/components/SettingsBar.js index 1e62884..fe19ca1 100644 --- a/src/components/SettingsBar.js +++ b/src/components/SettingsBar.js @@ -227,7 +227,7 @@ export default class SettingsBar extends React.PureComponent { {accessTokenHint && {accessTokenHint}}
-

Toggle shortcut

+

Toggle Shortcut

Set a combination of keys for toggling Gitako sidebar.
@@ -267,13 +267,10 @@ export default class SettingsBar extends React.PureComponent { {reloadHint &&
{reloadHint}
}
-

Issue

- - - Draft a issue on Github - -  for bug report or feature request. - +

Contact

+ + Bug report / feature request. +
From e493a47d02eff8cb551cd6de68af5260680f3da9 Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Sun, 28 Oct 2018 17:17:10 +0800 Subject: [PATCH 118/119] 0.3.16 --- package.json | 2 +- src/manifest.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index b193e33..71d8a6d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gitako", - "version": "0.3.15", + "version": "0.3.16", "description": "yet another extension for GitHub", "repository": "https://github.com/EnixCoda/Gitako", "author": "EnixCoda", diff --git a/src/manifest.json b/src/manifest.json index 5e9bd12..70a4fab 100644 --- a/src/manifest.json +++ b/src/manifest.json @@ -1,7 +1,7 @@ { "manifest_version": 2, "name": "Gitako - enhanced Github code tree", - "version": "0.3.15", + "version": "0.3.16", "author": "EnixCoda", "icons": { "128": "icons/Gitako-128x128.png" From 91ad9892e694717eb62c6c001888d8d5130cf607 Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Sun, 28 Oct 2018 17:59:51 +0800 Subject: [PATCH 119/119] docs: update readme --- README.md | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index d8a5d44..722a26d 100644 --- a/README.md +++ b/README.md @@ -1,35 +1,35 @@ # Gitako -Yet another Chrome extension for GitHub. Inspired by [Octotree](https://github.com/buunguyen/octotree) & [GayHub](https://github.com/jawil/GayHub). Aimed at providing better UX, consistent UI regards to GitHub and more essential features. +Yet another Chrome extension for GitHub. Inspired by [Octotree](https://github.com/buunguyen/octotree) & [GayHub](https://github.com/jawil/GayHub), thanks to them! Gitako is aimed at providing better UX, consistent UI regards to GitHub and more essential features. ### Features - [x] neat file tree - [x] instant file search -- [x] keyboard accessibility - - press 'up' and 'down' to nav between search input and files in the list - - press 'enter' or 'right' to redirect to file or expand folder - - press 'left' to jump to parent folder or collapse current folder +- [x] fast even in gigantic projects +- [x] intuitive keyboard accessibility + - file navigation using arrow keys + - set shortcut to toggle sidebar - [x] redirect smoothly with pjax -- [x] enlargeable when window is wide enough -- [x] copy snippets with one click - - copy code snippets inside markdown preview - - copy all contents of a file -- [x] support access token (for private repo and frequent usage) -- [x] consistent UI with GitHub - - [x] use [octicons](https://octicons.github.com) +- [x] resize-able when window is wide enough +- [x] copy snippets with single click + - copy code snippets from markdown preview + - copy whole content of a file +- [x] support private repo and frequent usages +- [x] support git submodule +- [x] UI consistent to GitHub's - [x] light size (~60kb) & low memory usage - [more WIP](https://github.com/EnixCoda/Gitako/projects/1) [Change Log](https://github.com/EnixCoda/Gitako/wiki/ChangeLog) -### Download +### Install -[install from Chrome web store](https://chrome.google.com/webstore/detail/gitako/giljefjcheohhamkjphiebfjnlphnokk) +[Please install from Chrome Web Store](https://chrome.google.com/webstore/detail/gitako/giljefjcheohhamkjphiebfjnlphnokk) ### Issues -Any bug report or feature requests are welcomed, feel free to draft a [issue](https://github.com/EnixCoda/Gitako/issues/)! +Any bug report or feature request discussions are welcomed, feel free to draft a [issue](https://github.com/EnixCoda/Gitako/issues/)! ### About