From aea43cee29da074cc0754d309e79a7c6a8fe0acf Mon Sep 17 00:00:00 2001 From: ilkin-overleaf <100852799+ilkin-overleaf@users.noreply.github.com> Date: Tue, 9 May 2023 11:56:26 +0300 Subject: [PATCH] Merge pull request #12999 from overleaf/ii-history-react-file-tree-optimisation [web] History file tree list items optimisation GitOrigin-RevId: b5e9b72b7df857479db3b2a276e6c064d8f9427a --- .../file-tree/history-file-tree-doc.tsx | 23 +++++---- .../history-file-tree-folder-list.tsx | 51 +++++++++++++++++-- .../file-tree/history-file-tree-folder.tsx | 6 ++- .../history/components/history-file-tree.tsx | 22 ++++++-- .../hooks/use-file-tree-item-selection.tsx | 33 ------------ 5 files changed, 84 insertions(+), 51 deletions(-) delete mode 100644 services/web/frontend/js/features/history/context/hooks/use-file-tree-item-selection.tsx diff --git a/services/web/frontend/js/features/history/components/file-tree/history-file-tree-doc.tsx b/services/web/frontend/js/features/history/components/file-tree/history-file-tree-doc.tsx index c8acfb9df7..90309c67e5 100644 --- a/services/web/frontend/js/features/history/components/file-tree/history-file-tree-doc.tsx +++ b/services/web/frontend/js/features/history/components/file-tree/history-file-tree-doc.tsx @@ -1,29 +1,32 @@ +import { memo } from 'react' import classNames from 'classnames' import HistoryFileTreeItem from './history-file-tree-item' import iconTypeFromName from '../../../file-tree/util/icon-type-from-name' import Icon from '../../../../shared/components/icon' -import { useFileTreeItemSelection } from '../../context/hooks/use-file-tree-item-selection' import type { FileDiff } from '../../services/types/file' type HistoryFileTreeDocProps = { file: FileDiff name: string + selected: boolean + onClick: (file: FileDiff) => void + onKeyDown: (file: FileDiff, event: React.KeyboardEvent) => void } -export default function HistoryFileTreeDoc({ +function HistoryFileTreeDoc({ file, name, + selected, + onClick, + onKeyDown, }: HistoryFileTreeDocProps) { - const { isSelected, handleClick, handleKeyDown } = - useFileTreeItemSelection(file) - return (
  • onClick(file)} + onKeyDown={e => onKeyDown(file, e)} + aria-selected={selected} aria-label={name} tabIndex={0} > @@ -41,3 +44,5 @@ export default function HistoryFileTreeDoc({
  • ) } + +export default memo(HistoryFileTreeDoc) diff --git a/services/web/frontend/js/features/history/components/file-tree/history-file-tree-folder-list.tsx b/services/web/frontend/js/features/history/components/file-tree/history-file-tree-folder-list.tsx index aeea5e1adb..50f3981cb3 100644 --- a/services/web/frontend/js/features/history/components/file-tree/history-file-tree-folder-list.tsx +++ b/services/web/frontend/js/features/history/components/file-tree/history-file-tree-folder-list.tsx @@ -2,8 +2,10 @@ import classNames from 'classnames' import HistoryFileTreeDoc from './history-file-tree-doc' import HistoryFileTreeFolder from './history-file-tree-folder' -import type { ReactNode } from 'react' +import { ReactNode, useCallback } from 'react' import type { HistoryFileTree, HistoryDoc } from '../../utils/file-tree' +import { useHistoryContext } from '../../context/history-context' +import { FileDiff } from '../../services/types/file' type HistoryFileTreeFolderListProps = { folders: HistoryFileTree[] @@ -12,12 +14,46 @@ type HistoryFileTreeFolderListProps = { children?: ReactNode } -export default function HistoryFileTreeFolderList({ +function HistoryFileTreeFolderList({ folders, docs, rootClassName, children, }: HistoryFileTreeFolderListProps) { + const { selection, setSelection } = useHistoryContext() + + const handleEvent = useCallback( + (file: FileDiff) => { + setSelection(prevSelection => { + if (file.pathname !== prevSelection.selectedFile?.pathname) { + return { + ...prevSelection, + selectedFile: file, + } + } + + return prevSelection + }) + }, + [setSelection] + ) + + const handleClick = useCallback( + (file: FileDiff) => { + handleEvent(file) + }, + [handleEvent] + ) + + const handleKeyDown = useCallback( + (file: FileDiff, event: React.KeyboardEvent) => { + if (event.key === 'Enter' || event.key === ' ') { + handleEvent(file) + } + }, + [handleEvent] + ) + return ( ) } + +export default HistoryFileTreeFolderList diff --git a/services/web/frontend/js/features/history/components/file-tree/history-file-tree-folder.tsx b/services/web/frontend/js/features/history/components/file-tree/history-file-tree-folder.tsx index 1361fd6613..23d354870c 100644 --- a/services/web/frontend/js/features/history/components/file-tree/history-file-tree-folder.tsx +++ b/services/web/frontend/js/features/history/components/file-tree/history-file-tree-folder.tsx @@ -1,4 +1,4 @@ -import { useState } from 'react' +import { useState, memo } from 'react' import { useTranslation } from 'react-i18next' import HistoryFileTreeItem from './history-file-tree-item' @@ -13,7 +13,7 @@ type HistoryFileTreeFolderProps = { docs: HistoryDoc[] } -export default function HistoryFileTreeFolder({ +function HistoryFileTreeFolder({ name, folders, docs, @@ -61,3 +61,5 @@ export default function HistoryFileTreeFolder({ ) } + +export default memo(HistoryFileTreeFolder) diff --git a/services/web/frontend/js/features/history/components/history-file-tree.tsx b/services/web/frontend/js/features/history/components/history-file-tree.tsx index 3bccf40e3a..296e15115f 100644 --- a/services/web/frontend/js/features/history/components/history-file-tree.tsx +++ b/services/web/frontend/js/features/history/components/history-file-tree.tsx @@ -1,3 +1,4 @@ +import { useMemo } from 'react' import { orderBy, reduce } from 'lodash' import { useHistoryContext } from '../context/history-context' import { @@ -9,13 +10,26 @@ import HistoryFileTreeFolderList from './file-tree/history-file-tree-folder-list export default function HistoryFileTree() { const { selection, error } = useHistoryContext() - const fileTree = reduce(selection.files, reducePathsToTree, []) + const fileTree = useMemo( + () => reduce(selection.files, reducePathsToTree, []), + [selection.files] + ) - const sortedFileTree = orderBy(fileTree, ['-type', 'operation', 'name']) + const sortedFileTree = useMemo( + () => orderBy(fileTree, ['-type', 'operation', 'name']), + [fileTree] + ) - const mappedFileTree = fileTreeDiffToFileTreeData(sortedFileTree) + const mappedFileTree = useMemo( + () => fileTreeDiffToFileTreeData(sortedFileTree), + [sortedFileTree] + ) - return error ? null : ( + if (error) { + return null + } + + return ( { - if (file.pathname !== selection.selectedFile?.pathname) { - setSelection({ - ...selection, - selectedFile: file, - }) - } - }, [file, selection, setSelection]) - - const handleClick = useCallback(() => { - handleEvent() - }, [handleEvent]) - - const handleKeyDown = useCallback( - event => { - if (event.key === 'Enter' || event.key === ' ') { - handleEvent() - } - }, - [handleEvent] - ) - - const isSelected = selection.selectedFile?.pathname === file.pathname - - return { isSelected, handleClick, handleKeyDown } -}