diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
new file mode 100644
index 0000000..0bd8935
--- /dev/null
+++ b/.github/FUNDING.yml
@@ -0,0 +1,12 @@
+# These are supported funding model platforms
+
+github: enixcoda
+patreon: enixcoda
+open_collective: enixcoda
+ko_fi: # Replace with a single Ko-fi username
+tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
+community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
+liberapay: # Replace with a single Liberapay username
+issuehunt: # Replace with a single IssueHunt username
+otechie: # Replace with a single Otechie username
+custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
diff --git a/__tests__/cases/homepage.not-render.ts b/__tests__/cases/homepage.not-render.ts
index 43cbac6..1a326a7 100644
--- a/__tests__/cases/homepage.not-render.ts
+++ b/__tests__/cases/homepage.not-render.ts
@@ -4,6 +4,6 @@ describe(`in GitHub homepage`, () => {
beforeAll(() => page.goto('https://github.com'))
it('should not render Gitako', async () => {
- await expectToNotFind('.gitako-side-bar .gitako-position-wrapper')
+ await expectToNotFind('.gitako-side-bar .gitako-side-bar-body-wrapper')
})
})
diff --git a/__tests__/cases/pull-request-page.gitako.ts b/__tests__/cases/pull-request-page.gitako.ts
index 4658ea1..00c7c18 100644
--- a/__tests__/cases/pull-request-page.gitako.ts
+++ b/__tests__/cases/pull-request-page.gitako.ts
@@ -4,7 +4,7 @@ describe(`in Gitako project page`, () => {
beforeAll(() => page.goto('https://github.com/EnixCoda/Gitako/pull/71'))
it('should render Gitako', async () => {
- await expectToFind('.gitako-side-bar .gitako-position-wrapper')
+ await expectToFind('.gitako-side-bar .gitako-side-bar-body-wrapper')
})
it('should render file list', async () => {
diff --git a/__tests__/cases/pjax.commits-page.ts b/__tests__/non-parallel-cases/pjax.commits-page.ts
similarity index 90%
rename from __tests__/cases/pjax.commits-page.ts
rename to __tests__/non-parallel-cases/pjax.commits-page.ts
index e1d219d..dde9484 100644
--- a/__tests__/cases/pjax.commits-page.ts
+++ b/__tests__/non-parallel-cases/pjax.commits-page.ts
@@ -4,8 +4,6 @@ describe(`in Gitako project page`, () => {
beforeAll(() => page.goto('https://github.com/EnixCoda/Gitako/commits/develop'))
it('should not break go back in history', async () => {
- // temporarily skip this test as GitHub has unexpected PJAX behaviors
- return
for (let i = 0; i < 3; i++) {
const commitLinks = await page.$$(
`#js-repo-pjax-container .TimelineItem-body ol li > div:nth-child(1) a[href*="/commit/"]`,
diff --git a/__tests__/cases/pjax.files-page.ts b/__tests__/non-parallel-cases/pjax.files-page.ts
similarity index 100%
rename from __tests__/cases/pjax.files-page.ts
rename to __tests__/non-parallel-cases/pjax.files-page.ts
diff --git a/__tests__/cases/pjax.mixed.ts b/__tests__/non-parallel-cases/pjax.general.ts
similarity index 54%
rename from __tests__/cases/pjax.mixed.ts
rename to __tests__/non-parallel-cases/pjax.general.ts
index 73912b3..d915bc1 100644
--- a/__tests__/cases/pjax.mixed.ts
+++ b/__tests__/non-parallel-cases/pjax.general.ts
@@ -1,18 +1,27 @@
-import { patientClick, selectFileTreeItem, sleep, waitForLegacyPJAXRedirect } from '../utils'
+import {
+ collapseFloatModeSidebar,
+ expandFloatModeSidebar,
+ patientClick,
+ selectFileTreeItem,
+ sleep,
+ waitForLegacyPJAXRedirect,
+} from '../utils'
describe(`in Gitako project page`, () => {
- beforeAll(() => page.goto('https://github.com/EnixCoda/Gitako/src'))
+ beforeAll(() => page.goto('https://github.com/EnixCoda/Gitako/tree/develop/src'))
it('should work with PJAX', async () => {
await sleep(3000)
- await patientClick(page, selectFileTreeItem('src/analytics.ts'))
+ await expandFloatModeSidebar()
+ await patientClick(selectFileTreeItem('src/analytics.ts'))
+ await waitForLegacyPJAXRedirect()
+ await collapseFloatModeSidebar()
+
+ await page.click('a[data-selected-links^="repo_issues "]')
await waitForLegacyPJAXRedirect()
- await page.click('a[data-tab-item="issues-tab"]')
- await waitForLegacyPJAXRedirect()
-
- await page.click('a[data-tab-item="pull-requests-tab"]')
+ await page.click('a[data-selected-links^="repo_pulls "]')
await waitForLegacyPJAXRedirect()
page.goBack()
diff --git a/__tests__/cases/pjax.internal.ts b/__tests__/non-parallel-cases/pjax.internal.ts
similarity index 84%
rename from __tests__/cases/pjax.internal.ts
rename to __tests__/non-parallel-cases/pjax.internal.ts
index c16412f..b70f5fd 100644
--- a/__tests__/cases/pjax.internal.ts
+++ b/__tests__/non-parallel-cases/pjax.internal.ts
@@ -1,4 +1,5 @@
import {
+ expandFloatModeSidebar,
expectToFind,
expectToNotFind,
patientClick,
@@ -12,7 +13,9 @@ describe(`in Gitako project page`, () => {
it('should work with PJAX', async () => {
await sleep(3000)
- await patientClick(page, selectFileTreeItem('.babelrc'))
+
+ await expandFloatModeSidebar()
+ await patientClick(selectFileTreeItem('.babelrc'))
await waitForPJAXAPIRedirect()
// The selector for file content
diff --git a/__tests__/cases/project-page.gitako.ts b/__tests__/non-parallel-cases/project-page.gitako.ts
similarity index 78%
rename from __tests__/cases/project-page.gitako.ts
rename to __tests__/non-parallel-cases/project-page.gitako.ts
index 6df7c19..fcd79fe 100644
--- a/__tests__/cases/project-page.gitako.ts
+++ b/__tests__/non-parallel-cases/project-page.gitako.ts
@@ -1,10 +1,16 @@
-import { expectToFind, expectToNotFind, scroll, selectFileTreeItem } from '../utils'
+import {
+ expandFloatModeSidebar,
+ expectToFind,
+ expectToNotFind,
+ scroll,
+ selectFileTreeItem,
+} from '../utils'
describe(`in Gitako project page`, () => {
beforeAll(() => page.goto('https://github.com/EnixCoda/Gitako'))
it('should render Gitako', async () => {
- await expectToFind('.gitako-side-bar .gitako-position-wrapper')
+ await expectToFind('.gitako-side-bar .gitako-side-bar-body-wrapper')
})
it('should render file list', async () => {
@@ -12,6 +18,8 @@ describe(`in Gitako project page`, () => {
})
it('should render while scroll', async () => {
+ await expandFloatModeSidebar()
+
const filesEle = await page.waitForSelector('.gitako-side-bar .files')
// node of tsconfig.json should NOT be rendered before scroll down
await expectToNotFind(selectFileTreeItem('tsconfig.json'))
diff --git a/__tests__/utils.ts b/__tests__/utils.ts
index 65b41c7..56e422b 100644
--- a/__tests__/utils.ts
+++ b/__tests__/utils.ts
@@ -1,5 +1,3 @@
-import { Page } from 'puppeteer'
-
export async function expectToFind(selector: string) {
await expect(page.waitForSelector(selector)).resolves.not.toBeNull()
}
@@ -82,7 +80,26 @@ export function selectFileTreeItem(path: string): string {
return `.gitako-side-bar .files a[title="${path}"]`
}
-export async function patientClick(page: Page, selector: string) {
+export async function patientClick(selector: string) {
await page.waitForSelector(selector)
await page.click(selector)
}
+
+export async function expandFloatModeSidebar() {
+ const rect = await (await page.$('.gitako-toggle-show-button'))?.evaluate(button => {
+ const { x, y, width, height } = button.getBoundingClientRect()
+ // pass required properties to avoid serialization issues
+ return { x, y, width, height }
+ })
+ if (rect) {
+ await page.mouse.move(rect.x + rect.width / 2, rect.y + rect.height / 2)
+ await sleep(500)
+ }
+}
+
+export async function collapseFloatModeSidebar() {
+ await page.mouse.move(600, 600, {
+ steps: 100,
+ })
+ await sleep(500)
+}
diff --git a/jest.config.js b/jest.config.js
index eafe22d..f13eb4d 100644
--- a/jest.config.js
+++ b/jest.config.js
@@ -61,7 +61,7 @@ module.exports = {
// globals: {},
// The maximum amount of workers used to run your tests. Can be specified as % or a number. E.g. maxWorkers: 10% will use 10% of your CPU amount + 1 as the maximum worker number. maxWorkers: 2 will use a maximum of 2 workers.
- maxWorkers: 1, // more than 1 workers may make tabs in background and never renders Gitako's file items.
+ maxWorkers: 8,
// An array of directory names to be searched recursively up from the requiring module's location
// moduleDirectories: [
diff --git a/jest.non.parallel.config.js b/jest.non.parallel.config.js
new file mode 100644
index 0000000..74dc653
--- /dev/null
+++ b/jest.non.parallel.config.js
@@ -0,0 +1,7 @@
+const baseConfig = require('./jest.config')
+
+module.exports = {
+ ...baseConfig,
+ maxWorkers: 1,
+ testMatch: ['**/__tests__/non-parallel-cases/*.ts?(x)'],
+}
diff --git a/package.json b/package.json
index e3fecc6..df90b7c 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "gitako",
- "version": "2.7.1",
+ "version": "3.1.0",
"description": "File tree for GitHub, and more than that.",
"repository": "https://github.com/EnixCoda/Gitako",
"author": "EnixCoda",
@@ -14,13 +14,15 @@
"postinstall": "rm -rf node_modules/@types/react-native && node scripts/fix-pjax-api",
"build": "VERSION=v$(node scripts/get-version.js) NODE_ENV=production webpack",
"roll": "make release",
- "test": "NODE_ENV=test jest"
+ "test": "yarn run test:parallel && yarn run test:non-parallel",
+ "test:parallel": "NODE_ENV=test jest",
+ "test:non-parallel": "NODE_ENV=test jest --config jest.non.parallel.config.js"
},
"dependencies": {
"@primer/components": "^22.0.2",
"@primer/css": "^15.2.0",
"@primer/octicons-react": "^10.0.0",
- "@sentry/browser": "^5.12.1",
+ "@sentry/browser": "^6.3.6",
"@types/history": "^4.7.5",
"@types/ini": "^1.3.30",
"@types/js-base64": "^2.3.1",
@@ -35,8 +37,8 @@
"nprogress": "^0.2.0",
"pjax-api": "^3.33.0",
"react": "^17.0.1",
- "react-dom": "^17.0.1",
- "react-use": "^13.8.0",
+ "react-dom": "^17.0.2",
+ "react-use": "^15.3.0",
"react-window": "^1.8.5",
"styled-components": "^5.2.0",
"webext-domain-permission-toggle": "^1.0.0",
@@ -51,7 +53,7 @@
"@babel/preset-env": "^7.3.4",
"@babel/preset-react": "^7.0.0",
"@babel/preset-typescript": "^7.3.3",
- "@sentry/cli": "^1.51.0",
+ "@sentry/cli": "^1.64.2",
"@types/firefox-webext-browser": "^70.0.1",
"@types/jest": "^24.0.25",
"@types/node": "^11.10.4",
@@ -71,7 +73,7 @@
"raw-loader": "^4.0.0",
"sass": "^1.26.2",
"sass-loader": "^8.0.2",
- "typescript": "^4.0.3",
+ "typescript": "^4.2.4",
"uglifyjs-webpack-plugin": "^2.1.2",
"url-loader": "^1.1.2",
"web-ext": "^4.2.0",
diff --git a/src/analytics.ts b/src/analytics.ts
index 9309680..1af5065 100644
--- a/src/analytics.ts
+++ b/src/analytics.ts
@@ -52,6 +52,7 @@ const sentryOptions: Sentry.BrowserOptions = {
}
return breadcrumb
},
+ autoSessionTracking: false, // this avoids the request when calling `init`
}
Sentry.init(sentryOptions)
diff --git a/src/components/AccessDeniedDescription.tsx b/src/components/AccessDeniedDescription.tsx
index 7071ce9..eb07778 100644
--- a/src/components/AccessDeniedDescription.tsx
+++ b/src/components/AccessDeniedDescription.tsx
@@ -1,12 +1,16 @@
+import { useConfigs } from 'containers/ConfigsContext'
import { GITHUB_OAUTH } from 'env'
import { platform } from 'platforms'
import { GitHub } from 'platforms/GitHub'
import * as React from 'react'
-export function AccessDeniedDescription({ hasToken }: { hasToken: boolean }) {
+export function AccessDeniedDescription() {
+ const configContext = useConfigs()
+ const hasToken = Boolean(configContext.value.accessToken)
+
return (
-
-
Access Denied
+
+
Access Denied
{hasToken ? (
<>
diff --git a/src/components/Clippy.tsx b/src/components/Clippy.tsx
index eacc560..2508ade 100644
--- a/src/components/Clippy.tsx
+++ b/src/components/Clippy.tsx
@@ -10,13 +10,13 @@ const className = 'clippy-wrapper'
export const ClippyClassName = className
export function Clippy({ codeSnippetElement }: Props) {
- const [status, setStatus] = React.useState<'normal' | 'success' | 'fail'>('normal')
+ const [state, setState] = React.useState<'normal' | 'success' | 'fail'>('normal')
React.useEffect(() => {
const timer = window.setTimeout(() => {
- setStatus('normal')
+ setState('normal')
}, 1000)
return () => window.clearTimeout(timer)
- }, [status])
+ }, [state])
// Temporary fix:
// React moved root node of event delegation since v17
@@ -26,7 +26,7 @@ export function Clippy({ codeSnippetElement }: Props) {
const element = elementRef.current
if (element) {
function onClippyClick() {
- setStatus(copyElementContent(codeSnippetElement) ? 'success' : 'fail')
+ setState(copyElementContent(codeSnippetElement) ? 'success' : 'fail')
}
element.addEventListener('click', onClippyClick)
return () => element.removeEventListener('click', onClippyClick)
@@ -36,7 +36,7 @@ export function Clippy({ codeSnippetElement }: Props) {
return (
)
diff --git a/src/components/FileExplorer.tsx b/src/components/FileExplorer.tsx
index fb66e48..8abcc59 100644
--- a/src/components/FileExplorer.tsx
+++ b/src/components/FileExplorer.tsx
@@ -1,4 +1,4 @@
-import { Text } from '@primer/components'
+import { Label, Text } from '@primer/components'
import { LoadingIndicator } from 'components/LoadingIndicator'
import { Node } from 'components/Node'
import { SearchBar } from 'components/SearchBar'
@@ -11,9 +11,12 @@ import * as React from 'react'
import { FixedSizeList, ListChildComponentProps } from 'react-window'
import { cx } from 'utils/cx'
import { focusFileExplorer } from 'utils/DOMHelper'
+import { run } from 'utils/general'
+import { useLoadedContext } from 'utils/hooks/useLoadedContext'
import { useOnLocationChange } from 'utils/hooks/useOnLocationChange'
import { useOnPJAXDone } from 'utils/hooks/usePJAX'
import { VisibleNodes } from 'utils/VisibleNodesGenerator'
+import { SideBarStateContext } from '../containers/SideBarState'
import { Icon } from './Icon'
import { SearchMode, searchModes } from './searchModes'
import { SizeObserver } from './SizeObserver'
@@ -27,7 +30,6 @@ type renderNodeContext = {
const RawFileExplorer: React.FC
= function RawFileExplorer(props) {
const {
- state,
visibleNodes,
visibleNodesGenerator,
freeze,
@@ -37,16 +39,14 @@ const RawFileExplorer: React.FC = function RawFileExplor
onFocusSearchBar,
goTo,
handleKeyDown,
- toggleShowSettings,
metaData,
expandTo,
setUpTree,
- treeRoot,
defer,
searched,
} = props
const {
- value: { accessToken, compressSingletonFolder, searchMode },
+ value: { accessToken, compressSingletonFolder, searchMode, commentToggle },
} = useConfigs()
const onSearch = React.useCallback(
@@ -59,18 +59,19 @@ const RawFileExplorer: React.FC = function RawFileExplor
[updateSearchKey, visibleNodesGenerator],
)
+ const stateContext = useLoadedContext(SideBarStateContext)
+ const state = stateContext.value
+
React.useEffect(() => {
- if (treeRoot) {
- setUpTree({
- treeRoot,
- metaData,
- config: {
- compressSingletonFolder,
- accessToken,
- },
- })
- }
- }, [setUpTree, treeRoot, metaData, compressSingletonFolder, accessToken])
+ setUpTree({
+ metaData,
+ config: {
+ compressSingletonFolder,
+ accessToken,
+ },
+ stateContext,
+ })
+ }, [setUpTree, metaData, compressSingletonFolder, accessToken])
React.useEffect(() => {
if (visibleNodes?.focusedNode) focusFileExplorer()
@@ -104,18 +105,26 @@ const RawFileExplorer: React.FC = function RawFileExplor
) : undefined
+ const renderFileCommentAmounts = (node: TreeNode): React.ReactNode =>
+ node.comments !== undefined &&
+ node.comments > 0 && (
+
+ {node.comments > 9 ? '9+' : node.comments}
+
+ )
const renders: ((node: TreeNode) => React.ReactNode)[] = []
+ if (commentToggle) renders.push(renderFileCommentAmounts)
if (searchMode === 'fuzzy') renders.push(renderFindInFolderButton)
if (searched) renders.push(renderGoToButton)
return renders.length
? node => renders.map((render, i) => {render(node)})
: undefined
- }, [goTo, onSearch, searched, searchMode])
+ }, [goTo, onSearch, searched, searchMode, commentToggle])
const renderLabelText = React.useCallback(
- node => searchModes[searchMode].renderNodeLabelText(node, searchKey),
+ (node: TreeNode) => searchModes[searchMode].renderNodeLabelText(node, searchKey),
[searchKey, searchMode],
)
@@ -131,59 +140,63 @@ const RawFileExplorer: React.FC = function RawFileExplor
)
return (
-
- {state !== 'done' ? (
-
- ) : (
- visibleNodes &&
- renderNodeContext && (
- <>
-
- {searched && visibleNodes.nodes.length === 0 && (
- <>
-
- No results found.
-
- {defer && (
-
- Lazy mode is ON. Search results are limited to loaded folders.
-
- )}
- >
- )}
-
- {({ width = 0, height = 0 }) => (
-
- )}
-
- >
- )
- )}
+
+ {run(() => {
+ switch (state) {
+ case 'tree-loading':
+ return
+ case 'tree-rendering':
+ return
+ case 'tree-rendered':
+ return (
+ visibleNodes &&
+ renderNodeContext && (
+ <>
+
+ {searched && visibleNodes.nodes.length === 0 && (
+ <>
+
+ No results found.
+
+ {defer && (
+
+ Lazy mode is ON. Search results are limited to loaded folders.
+
+ )}
+ >
+ )}
+
+ {({ width = 0, height = 0 }) => (
+
+ )}
+
+
+ {defer && (
+
+ )}
+ >
+ )
+ )
+ }
+ })}
)
}
RawFileExplorer.defaultProps = {
freeze: false,
- state: 'pulling',
searchKey: '',
visibleNodes: null,
}
@@ -248,13 +261,15 @@ function ListView({ width, height, metaData, expandTo, renderNodeContext }: List
useOnLocationChange(goToCurrentItem)
useOnPJAXDone(goToCurrentItem)
+ const { compactFileTree } = useConfigs().value
+
return (
visibleNodes?.nodes[index]?.path}
itemData={renderNodeContext}
itemCount={visibleNodes.nodes.length}
- itemSize={37}
+ itemSize={compactFileTree ? 24 : 37}
height={height}
width={width}
>
diff --git a/src/components/Gitako.tsx b/src/components/Gitako.tsx
index 7c68258..13039b3 100644
--- a/src/components/Gitako.tsx
+++ b/src/components/Gitako.tsx
@@ -1,15 +1,25 @@
import { SideBar } from 'components/SideBar'
-import { ConfigsContext, ConfigsContextWrapper } from 'containers/ConfigsContext'
+import { ConfigsContextWrapper } from 'containers/ConfigsContext'
import * as React from 'react'
-import { ErrorBoundary } from './ErrorBoundary'
+import { ErrorBoundary } from '../containers/ErrorBoundary'
+import { StateBarErrorContextWrapper } from '../containers/ErrorContext'
+import { OAuthWrapper } from '../containers/OAuthWrapper'
+import { RepoContextWrapper } from '../containers/RepoContext'
+import { StateBarStateContextWrapper } from '../containers/SideBarState'
export function Gitako() {
return (
-
- {configContext => configContext && }
-
+
+
+
+
+
+
+
+
+
)
diff --git a/src/components/IIFC.tsx b/src/components/IIFC.tsx
new file mode 100644
index 0000000..6a57b87
--- /dev/null
+++ b/src/components/IIFC.tsx
@@ -0,0 +1,8 @@
+import * as React from 'react'
+
+// I tried to install `react-iifc` but that causes TS build errors for unknown reason
+// So here the duplicated code is
+
+export function IIFC({ children }: { children(): React.ReactNode }) {
+ return <>{children()}>
+}
diff --git a/src/components/Icon.tsx b/src/components/Icon.tsx
index c5d9f07..d3f8eb4 100644
--- a/src/components/Icon.tsx
+++ b/src/components/Icon.tsx
@@ -2,6 +2,7 @@ import {
ChevronDownIcon as ChevronDown,
ChevronRightIcon as ChevronRight,
ClockIcon as Clock,
+ CommentIcon as Comment,
FileCodeIcon as FileCode,
FileIcon as File,
FileMediaIcon as FileMedia,
@@ -10,130 +11,76 @@ import {
GearIcon as Gear,
GrabberIcon as Grabber,
HourglassIcon as Hourglass,
- Icon as OcticonIcon,
IconProps,
MarkdownIcon as Markdown,
OctofaceIcon as Octoface,
+ PinIcon as Pin,
ReplyIcon as Reply,
SearchIcon as Search,
+ TabIcon as Tab,
XIcon as X,
} from '@primer/octicons-react'
import * as React from 'react'
import { cx } from 'utils/cx'
-function getSVGIconComponent(
- type: string,
-): {
- IconComponent: OcticonIcon
- name: string
-} {
- switch (type) {
- case 'search':
- return {
- IconComponent: Search,
- name: 'Search',
- }
- case 'loading':
- return {
- IconComponent: Clock,
- name: 'Clock',
- }
- case 'hourglass':
- return {
- IconComponent: Hourglass,
- name: 'Hourglass',
- }
- case 'submodule':
- return {
- IconComponent: Submodule,
- name: 'Submodule',
- }
- case 'grabber':
- return {
- IconComponent: Grabber,
- name: 'Grabber',
- }
- case 'octoface':
- return {
- IconComponent: Octoface,
- name: 'Octoface',
- }
- case 'chevron-down':
- return {
- IconComponent: ChevronDown,
- name: 'ChevronDown',
- }
- case 'x':
- return {
- IconComponent: X,
- name: 'X',
- }
- case 'gear':
- return {
- IconComponent: Gear,
- name: 'Gear',
- }
- case 'folder':
- return {
- IconComponent: ChevronRight,
- name: 'ChevronRight',
- }
- case 'go-to':
- return {
- IconComponent: Reply,
- name: 'Reply',
- }
- // not supported in octicon v2 yet
- // case '.pdf':
- // return {
- // IconComponent: FilePdf,
- // name: 'FilePdf',
- // }
- case '.zip':
- case '.rar':
- case '.7z':
- return {
- IconComponent: FileZip,
- name: 'FileZip',
- }
- case '.md':
- return {
- IconComponent: Markdown,
- name: 'Markdown',
- }
- case '.png':
- case '.jpg':
- case '.gif':
- case '.bmp':
- return {
- IconComponent: FileMedia,
- name: 'FileMedia',
- }
- case '.js':
- case '.jsx':
- case '.ts':
- case '.tsx':
- case '.es6':
- case '.coffee':
- case '.css':
- case '.less':
- case '.scss':
- case '.sass':
- return {
- IconComponent: FileCode,
- name: 'FileCode',
- }
- // TODO: adapt to more file types
- // case '': return FileBinary
- // case '': return FileSubmodule
- // case '': return FileSymlinkDirectory
- // case '': return FileSymlinkFile
- default:
- return {
- IconComponent: File,
- name: 'File',
- }
- }
+const iconToComponentMap = {
+ Search,
+ Clock,
+ Comment,
+ Hourglass,
+ Submodule,
+ Grabber,
+ Octoface,
+ ChevronDown,
+ X,
+ Gear,
+ ChevronRight,
+ Reply,
+ FileZip,
+ Markdown,
+ FileMedia,
+ FileCode,
+ File,
+ Pin,
+ Tab,
+}
+
+const defaultIcon = 'File'
+const typeToIconComponentMap: {
+ [type: string]: keyof typeof iconToComponentMap
+} = {
+ search: 'Search',
+ loading: 'Clock',
+ hourglass: 'Hourglass',
+ submodule: 'Submodule',
+ grabber: 'Grabber',
+ octoface: 'Octoface',
+ comment: 'Comment',
+ x: 'X',
+ pin: 'Pin',
+ tab: 'Tab',
+ gear: 'Gear',
+ folder: 'ChevronRight',
+ 'chevron-down': 'ChevronDown',
+ 'go-to': 'Reply',
+ '.zip': 'FileZip',
+ '.rar': 'FileZip',
+ '.7z': 'FileZip',
+ '.md': 'Markdown',
+ '.png': 'FileMedia',
+ '.jpg': 'FileMedia',
+ '.gif': 'FileMedia',
+ '.bmp': 'FileMedia',
+ '.js': 'FileCode',
+ '.jsx': 'FileCode',
+ '.ts': 'FileCode',
+ '.tsx': 'FileCode',
+ '.es6': 'FileCode',
+ '.coffee': 'FileCode',
+ '.css': 'FileCode',
+ '.less': 'FileCode',
+ '.scss': 'FileCode',
+ '.sass': 'FileCode',
}
type Props = {
@@ -151,7 +98,8 @@ export const Icon = React.memo(function Icon({
}: Props) {
let children: React.ReactNode = null
if (!placeholder) {
- const { name, IconComponent } = getSVGIconComponent(type)
+ const name = typeToIconComponentMap[type] || defaultIcon
+ const IconComponent = iconToComponentMap[name]
children =
}
return (
diff --git a/src/components/MetaBar.tsx b/src/components/MetaBar.tsx
index a210444..7696872 100644
--- a/src/components/MetaBar.tsx
+++ b/src/components/MetaBar.tsx
@@ -11,14 +11,14 @@ export function MetaBar({ metaData }: Props) {
const { userName, repoName, branchName } = metaData
const { repoUrl, userUrl } = platform.resolveUrlFromMetaData(metaData)
return (
-
+ <>
{userName}
{repoName}
-
+
@@ -26,6 +26,6 @@ export function MetaBar({ metaData }: Props) {
{branchName || '...'}
-
+ >
)
}
diff --git a/src/components/Node.tsx b/src/components/Node.tsx
index 5fa7942..8fa2ff0 100644
--- a/src/components/Node.tsx
+++ b/src/components/Node.tsx
@@ -2,7 +2,7 @@ import { useConfigs } from 'containers/ConfigsContext'
import * as React from 'react'
import { cx } from 'utils/cx'
import { OperatingSystems, os } from 'utils/general'
-import { getFileIconSrc, getFolderIconSrc } from '../utils/parseIconMapCSV'
+import { getFileIconSrc, getFolderIconSrc } from 'utils/parseIconMapCSV'
import { Icon } from './Icon'
function getIconType(node: TreeNode) {
@@ -38,6 +38,7 @@ export function Node({
style,
onClick,
}: Props) {
+ const { compactFileTree: compact } = useConfigs().value
return (
{renderLabelText(node)}
- {renderActions && {renderActions(node)}
}
+ {renderActions && {renderActions(node)}
}
)
}
diff --git a/src/components/ResizeHandler.tsx b/src/components/ResizeHandler.tsx
index 70e983a..177de70 100644
--- a/src/components/ResizeHandler.tsx
+++ b/src/components/ResizeHandler.tsx
@@ -1,14 +1,24 @@
import { Icon } from 'components/Icon'
import * as React from 'react'
-import { Size } from './Resizable'
+import { Size } from './SideBarBodyWrapper'
type Props = {
size: Size
onResize(size: Size): void
+ onResetSize?(): void
+ onResizeStateChange?(state: ResizeState): void
style?: React.CSSProperties
}
-export function HorizontalResizeHandler({ onResize, size, style }: Props) {
+type ResizeState = 'idle' | 'resizing'
+
+export function HorizontalResizeHandler({
+ onResize,
+ onResetSize,
+ onResizeStateChange,
+ size,
+ style,
+}: Props) {
const pointerDown = React.useRef(false)
const startX = React.useRef(0)
const baseSize = React.useRef(size)
@@ -22,6 +32,7 @@ export function HorizontalResizeHandler({ onResize, size, style }: Props) {
startX.current = clientX
pointerDown.current = true
baseSize.current = latestPropSize.current
+ onResizeStateChange?.('resizing')
}, [])
React.useEffect(() => {
@@ -39,6 +50,7 @@ export function HorizontalResizeHandler({ onResize, size, style }: Props) {
if (pointerDown.current) {
pointerDown.current = false
baseSize.current = latestPropSize.current
+ onResizeStateChange?.('idle')
}
}
window.addEventListener('mouseup', onPointerUp)
@@ -46,7 +58,12 @@ export function HorizontalResizeHandler({ onResize, size, style }: Props) {
}, [])
return (
-
+
)
diff --git a/src/components/SearchBar.tsx b/src/components/SearchBar.tsx
index d266f7d..9f21e50 100644
--- a/src/components/SearchBar.tsx
+++ b/src/components/SearchBar.tsx
@@ -35,7 +35,7 @@ export function SearchBar({ onSearch, onFocus, value }: Props) {
/>