From da930bc35f057e4f32ae5436b41b76d00e33dbac Mon Sep 17 00:00:00 2001 From: EnixCoda Date: Tue, 7 Sep 2021 23:54:51 +0800 Subject: [PATCH 1/2] feat: optimize resizing performance --- src/components/SideBar.tsx | 1 + src/components/SideBarBodyWrapper.tsx | 76 +++++++++++++++++++-------- src/styles/index.scss | 3 -- src/utils/DOMHelper.ts | 14 +++-- 4 files changed, 62 insertions(+), 32 deletions(-) diff --git a/src/components/SideBar.tsx b/src/components/SideBar.tsx index 9622f08..a351b38 100644 --- a/src/components/SideBar.tsx +++ b/src/components/SideBar.tsx @@ -126,6 +126,7 @@ export function SideBar() { })} baseSize={baseSize} onLeave={sidebarToggleMode === 'float' ? () => setShowSideBar(false) : undefined} + sizeVariableMountPoint={sidebarToggleMode === 'persistent' ? document.body : undefined} >
['onMouseLeave'] + sizeVariableMountPoint?: HTMLElement } const MINIMAL_CONTENT_VIEWPORT_WIDTH = 100 const MINIMAL_WIDTH = 240 +function getSafeSize(size: number, width: number) { + if (size > width - MINIMAL_CONTENT_VIEWPORT_WIDTH) return width - MINIMAL_CONTENT_VIEWPORT_WIDTH + if (size < MINIMAL_WIDTH) return MINIMAL_WIDTH + return size +} + export function SideBarBodyWrapper({ baseSize, className, children, onLeave, + sizeVariableMountPoint, }: React.PropsWithChildren) { const [size, setSize] = React.useState(baseSize) const configContext = useConfigs() @@ -34,27 +41,50 @@ export function SideBarBodyWrapper({ const { width } = useWindowSize() React.useEffect(() => { - if (size > width - MINIMAL_CONTENT_VIEWPORT_WIDTH) - setSize(width - MINIMAL_CONTENT_VIEWPORT_WIDTH) - else if (size < MINIMAL_WIDTH) setSize(MINIMAL_WIDTH) + const safeSize = getSafeSize(size, width) + if (safeSize !== size) setSize(safeSize) }, [width, size]) - - React.useEffect(() => { - setResizingState(true) - const timer = setTimeout(() => setResizingState(false), 100) - return () => clearTimeout(timer) - }, [width, size]) - - useCSSVariable('--gitako-width', `${size}px`) + const bodyWrapperRef = React.useRef(null) useDebounce(() => configContext.onChange({ sideBarWidth: size }), 100, [size]) - const onResize = React.useCallback((size: number) => { - // do NOT merge this with the above similar effect, side bar will jump otherwise - if (size > width - MINIMAL_CONTENT_VIEWPORT_WIDTH) - setSize(width - MINIMAL_CONTENT_VIEWPORT_WIDTH) - else if (size < MINIMAL_WIDTH) setSize(MINIMAL_WIDTH) - else setSize(size) - }, []) + function apply(sizeVariableMountPoint: HTMLElement | undefined, size: number) { + if (sizeVariableMountPoint) + setCSSVariable( + '--gitako-width', + sizeVariableMountPoint ? `${size}px` : undefined, + sizeVariableMountPoint, + ) + + if (bodyWrapperRef.current) + setCSSVariable( + '--gitako-width', + sizeVariableMountPoint ? undefined : `${size}px`, + bodyWrapperRef.current, + ) + } + + // Update size using useEffect would cause delay + const onResize = React.useMemo(() => { + let sizeToApply: number, + applied = true + return (size: number) => { + // do NOT merge this with the above similar effect, side bar will jump otherwise + sizeToApply = getSafeSize(size, width) + setSize(sizeToApply) + + if (applied) { + applied = false + requestAnimationFrame(() => { + applied = true + apply(sizeVariableMountPoint, sizeToApply) + }) + } + } + }, [width, sizeVariableMountPoint]) + + React.useEffect(() => { + apply(sizeVariableMountPoint, size) + }, [sizeVariableMountPoint]) const onMouseLeave = React.useCallback( e => { @@ -65,7 +95,11 @@ export function SideBarBodyWrapper({ ) return ( -
+
{children}
{features.resize && ( Date: Thu, 23 Dec 2021 11:35:36 +0800 Subject: [PATCH 2/2] feat: optimize file tree performance --- Safari/Gitako | 1 + src/analytics.ts | 4 ++-- src/components/FileExplorer.tsx | 17 +++++++++++++---- src/driver/core/FileExplorer.ts | 6 ++++-- src/platforms/GitHub/index.ts | 21 +++++++++++++++------ src/utils/VisibleNodesGenerator.ts | 4 ++-- 6 files changed, 37 insertions(+), 16 deletions(-) create mode 160000 Safari/Gitako diff --git a/Safari/Gitako b/Safari/Gitako new file mode 160000 index 0000000..b82c858 --- /dev/null +++ b/Safari/Gitako @@ -0,0 +1 @@ +Subproject commit b82c8588b9cc5864c3738f903ac1930ceda5c996 diff --git a/src/analytics.ts b/src/analytics.ts index 1af5065..3ac4bd5 100644 --- a/src/analytics.ts +++ b/src/analytics.ts @@ -58,9 +58,9 @@ Sentry.init(sentryOptions) export const withErrorLog: Middleware = function withErrorLog(method, args) { return [ - async function (...args: any[]) { + async function () { try { - await method.apply(null, args) + await method.apply(null, arguments as any) } catch (error) { raiseError(error) } diff --git a/src/components/FileExplorer.tsx b/src/components/FileExplorer.tsx index 4b57938..f1f64c5 100644 --- a/src/components/FileExplorer.tsx +++ b/src/components/FileExplorer.tsx @@ -46,14 +46,23 @@ const RawFileExplorer: React.FC = function RawFileExplor searched, } = props const { - value: { accessToken, compressSingletonFolder, searchMode, commentToggle, restoreExpandedFolders }, + value: { + accessToken, + compressSingletonFolder, + searchMode, + commentToggle, + restoreExpandedFolders, + }, } = useConfigs() const onSearch = React.useCallback( (searchKey: string, searchMode: SearchMode) => { updateSearchKey(searchKey) if (visibleNodesGenerator) { - visibleNodesGenerator.search(searchModes[searchMode].getSearchParams(searchKey), restoreExpandedFolders) + visibleNodesGenerator.search( + searchModes[searchMode].getSearchParams(searchKey), + restoreExpandedFolders, + ) } }, [updateSearchKey, visibleNodesGenerator, restoreExpandedFolders], @@ -74,8 +83,8 @@ const RawFileExplorer: React.FC = function RawFileExplor }, [setUpTree, metaData, compressSingletonFolder, accessToken]) React.useEffect(() => { - if (visibleNodes?.focusedNode) focusFileExplorer() - }) + focusFileExplorer() + }, []) const renderActions: ((node: TreeNode) => React.ReactNode) | undefined = React.useMemo(() => { const renderGoToButton = (node: TreeNode): React.ReactNode => ( diff --git a/src/driver/core/FileExplorer.ts b/src/driver/core/FileExplorer.ts index 2bb9ab6..b17371e 100644 --- a/src/driver/core/FileExplorer.ts +++ b/src/driver/core/FileExplorer.ts @@ -264,8 +264,10 @@ export const toggleNodeExpansion: BoundMethodCreator< } = dispatch.get() if (!visibleNodesGenerator) return - visibleNodesGenerator.focusNode(node) - await visibleNodesGenerator.toggleExpand(node, recursive) + if (node.type === 'tree') { + visibleNodesGenerator.focusNode(node) + await visibleNodesGenerator.toggleExpand(node, recursive) + } } export const focusNode: BoundMethodCreator<[TreeNode | null]> = diff --git a/src/platforms/GitHub/index.ts b/src/platforms/GitHub/index.ts index dab3215..a163c16 100644 --- a/src/platforms/GitHub/index.ts +++ b/src/platforms/GitHub/index.ts @@ -305,20 +305,29 @@ async function getPullRequestTreeData( } const docs = await API.getPullPageDocuments(userName, repoName, pullId) + // query all elements at once to make getFileElementHash run faster + const elementsHavePath = docs.map(doc => doc.querySelectorAll(`[data-path]`)) const getFileElementHash = (path: string) => { - for (const doc of docs) { - const id = doc.querySelector(`*[data-path^="${path}"]`)?.parentElement?.id - if (id) return id + let e + for (const group of elementsHavePath) { + for (let i = 0; i < group.length; i++) { + const element = group[i] + if (element.getAttribute('data-path')?.startsWith(path)) { + e = element + break + } + } + if (e) break } + return e?.parentElement?.id } + const urlMainPart = `https://${window.location.host}/${userName}/${repoName}/pull/${pullId}/files${window.location.search}` const nodes: TreeNode[] = treeData.map(item => ({ path: item.filename || '', type: 'blob', name: item.filename?.replace(/^.*\//, '') || '', - url: `https://${window.location.host}/${userName}/${repoName}/pull/${pullId}/files${ - window.location.search - }${formatHash(getFileElementHash(item.filename))}`, + url: `${urlMainPart}${formatHash(getFileElementHash(item.filename))}`, sha: item.sha, comments: commentData?.filter(comment => item.filename === comment.path).length, })) diff --git a/src/utils/VisibleNodesGenerator.ts b/src/utils/VisibleNodesGenerator.ts index 9ae231f..b4858c8 100644 --- a/src/utils/VisibleNodesGenerator.ts +++ b/src/utils/VisibleNodesGenerator.ts @@ -273,8 +273,8 @@ class FlattenLayer extends CompressLayer { const expand = !this.expandedNodes.has(node.path) await traverse( [node], - async node => { - await this.$setExpand(node, expand) + node => { + this.$setExpand(node, expand) return recursive }, node => node.contents || [],