From 76092ed734005f385db0fc07ba002297a77c26ae Mon Sep 17 00:00:00 2001 From: Jesse Chan Date: Fri, 12 Mar 2021 11:19:16 +0800 Subject: [PATCH] client: migrate file tree componenets to FC --- .../general/filesystem/DirectoryFileList.tsx | 280 +++++++----------- .../general/filesystem/DirectoryTree.tsx | 9 +- .../general/filesystem/DirectoryTreeNode.tsx | 191 +++++------- .../torrent-details-modal/TorrentContents.tsx | 1 + 4 files changed, 188 insertions(+), 293 deletions(-) diff --git a/client/src/javascript/components/general/filesystem/DirectoryFileList.tsx b/client/src/javascript/components/general/filesystem/DirectoryFileList.tsx index 48b18d52..9ff36842 100644 --- a/client/src/javascript/components/general/filesystem/DirectoryFileList.tsx +++ b/client/src/javascript/components/general/filesystem/DirectoryFileList.tsx @@ -1,12 +1,12 @@ import classnames from 'classnames'; -import {Component, ReactNode, ReactText} from 'react'; +import {FC, ReactText, useRef, useState} from 'react'; import {Checkbox} from '@client/ui'; import {Checkmark, Clipboard, File as FileIcon} from '@client/ui/icons'; import ConfigStore from '@client/stores/ConfigStore'; import TorrentActions from '@client/actions/TorrentActions'; -import type {TorrentContent, TorrentContentSelection, TorrentContentSelectionTree} from '@shared/types/TorrentContent'; +import type {TorrentContentSelection, TorrentContentSelectionTree} from '@shared/types/TorrentContent'; import type {TorrentProperties} from '@shared/types/Torrent'; import PriorityMeter from '../PriorityMeter'; @@ -20,177 +20,127 @@ interface DirectoryFilesProps { onItemSelect: (selection: TorrentContentSelection) => void; } -interface DirectoryFilesStates { - copiedToClipboard: number | null; -} +const DirectoryFiles: FC = ({depth, items, hash, path, onItemSelect}: DirectoryFilesProps) => { + const [copiedToClipboard, setCopiedToClipboard] = useState(null); + const contentPermalinks = useRef>({}); -class DirectoryFiles extends Component { - contentPermalinks: Record = {}; - - static defaultProps = { - path: [], - items: {}, - }; - - constructor(props: DirectoryFilesProps) { - super(props); - - this.state = { - copiedToClipboard: null, - }; + if (items == null) { + return null; } - getCurrentPath(file: TorrentContent): string[] { - const {path} = this.props; + const files = Object.values(items) + .sort((a, b) => a.filename.localeCompare(b.filename)) + .map((file) => { + const isSelected = (items && items[file.filename] && items[file.filename].isSelected) || false; + const classes = classnames( + 'directory-tree__node file', + 'directory-tree__node--file directory-tree__node--selectable', + { + 'directory-tree__node--selected': isSelected, + }, + ); - return [...path, file.filename]; - } - - getIcon(file: TorrentContent, isSelected: boolean): ReactNode { - return ( -
-
- this.handleFileSelect(file, isSelected)} /> -
-
- -
-
- ); - } - - handlePriorityChange = (fileIndex: ReactText, priorityLevel: number): void => { - const {hash} = this.props; - - TorrentActions.setFilePriority(hash, { - indices: [Number(fileIndex)], - priority: priorityLevel, - }); - }; - - handleFileSelect = (file: TorrentContent, isSelected: boolean): void => { - const {depth, onItemSelect} = this.props; - - onItemSelect({ - type: 'file', - depth, - path: this.getCurrentPath(file), - select: !isSelected, - }); - }; - - render(): ReactNode { - const {items, hash} = this.props; - - if (items == null) { - return null; - } - - const files = Object.values(items) - .sort((a, b) => a.filename.localeCompare(b.filename)) - .map((file) => { - const {copiedToClipboard} = this.state; - - const isSelected = (items && items[file.filename] && items[file.filename].isSelected) || false; - const classes = classnames( - 'directory-tree__node file', - 'directory-tree__node--file directory-tree__node--selectable', - { - 'directory-tree__node--selected': isSelected, - }, - ); - - return ( -
-
- {this.getIcon(file, isSelected)} -
- {/* TODO: Add a WebAssembly decoding player if the feature is popular */} - - {file.filename} - + return ( +
+
+
+
+ + onItemSelect({ + type: 'file', + depth, + path: [...path, file.filename], + select: !isSelected, + }) + } + /> +
+
+
-
- +
+ {/* TODO: Add a WebAssembly decoding player if the feature is popular */} + + {file.filename} +
-
{Math.trunc(file.percentComplete)}%
-
- -
- {typeof navigator.clipboard?.writeText === 'function' && ( - - )}
- ); - }); +
+ +
+
{Math.trunc(file.percentComplete)}%
+
+ + TorrentActions.setFilePriority(hash, { + indices: [Number(fileIndex)], + priority: priorityLevel, + }) + } + priorityType="file" + /> +
+ {typeof navigator.clipboard?.writeText === 'function' && ( + + )} +
+ ); + }); - return
{files}
; - } -} + return
{files}
; +}; export default DirectoryFiles; diff --git a/client/src/javascript/components/general/filesystem/DirectoryTree.tsx b/client/src/javascript/components/general/filesystem/DirectoryTree.tsx index a240479b..fe30ac70 100644 --- a/client/src/javascript/components/general/filesystem/DirectoryTree.tsx +++ b/client/src/javascript/components/general/filesystem/DirectoryTree.tsx @@ -9,8 +9,8 @@ import DirectoryFileList from './DirectoryFileList'; import DirectoryTreeNode from './DirectoryTreeNode'; interface DirectoryTreeProps { - depth?: number; - path?: Array; + depth: number; + path: Array; hash: TorrentProperties['hash']; itemsTree: TorrentContentSelectionTree; onItemSelect: (selection: TorrentContentSelection) => void; @@ -68,9 +68,4 @@ const DirectoryTree: FC = (props: DirectoryTreeProps) => { return
{directoryNodes.concat(fileList)}
; }; -DirectoryTree.defaultProps = { - depth: 0, - path: [], -}; - export default DirectoryTree; diff --git a/client/src/javascript/components/general/filesystem/DirectoryTreeNode.tsx b/client/src/javascript/components/general/filesystem/DirectoryTreeNode.tsx index d73c5475..bbcda6b7 100644 --- a/client/src/javascript/components/general/filesystem/DirectoryTreeNode.tsx +++ b/client/src/javascript/components/general/filesystem/DirectoryTreeNode.tsx @@ -1,5 +1,5 @@ import classnames from 'classnames'; -import {Component, ReactNode} from 'react'; +import {FC, useState} from 'react'; import {Checkbox} from '@client/ui'; import {FolderClosedSolid, FolderOpenSolid} from '@client/ui/icons'; @@ -22,138 +22,87 @@ interface DirectoryTreeNodeProps { onItemSelect: (selection: TorrentContentSelection) => void; } -interface DirectoryTreeNodeStates { - expanded: boolean; -} +const DirectoryTreeNode: FC = ({ + depth, + directoryName, + id, + itemsTree, + hash, + path, + isSelected, + onItemSelect, +}: DirectoryTreeNodeProps) => { + const [expanded, setExpanded] = useState(false); + const currentPath = [...path, directoryName]; -class DirectoryTreeNode extends Component { - static defaultProps = { - path: [], - selectedItems: {}, - }; - - constructor(props: DirectoryTreeNodeProps) { - super(props); - - this.state = { - expanded: false, - }; - } - - getCurrentPath(): string[] { - const {path, directoryName} = this.props; - - return [...path, directoryName]; - } - - getIcon(): ReactNode { - const {id, isSelected} = this.props; - const {expanded} = this.state; - - let icon = null; - if (expanded) { - icon = ; - } else { - icon = ; - } - - return ( -
-
setExpanded(!expanded)} + title={directoryName}> +
+
+
- -
-
- {icon} + {expanded ? : } +
+
+
{directoryName}
-
- ); - } - - getSubTree(): ReactNode { - const {depth, itemsTree, hash, onItemSelect} = this.props; - const {expanded} = this.state; - - if (expanded) { - return ( + + {expanded ? (
- ); - } - - return null; - } - - handleDirectoryClick = (): void => { - this.setState((state) => ({ - expanded: !state.expanded, - })); - }; - - handleDirectorySelection = (): void => { - const {depth, isSelected, onItemSelect} = this.props; - - onItemSelect({ - type: 'directory', - depth, - path: this.getCurrentPath(), - select: !isSelected, - }); - }; - - render(): ReactNode { - const {depth, directoryName, isSelected} = this.props; - const {expanded} = this.state; - - const branchClasses = classnames('directory-tree__branch', `directory-tree__branch--depth-${depth}`, { - 'directory-tree__node--selected': isSelected, - }); - const directoryClasses = classnames( - 'directory-tree__node', - 'directory-tree__node--selectable directory-tree__node--directory', - { - 'is-expanded': expanded, - }, - ); - - return ( -
- - {this.getSubTree()} -
- ); - } -} + ) : null} +
+ ); +}; export default DirectoryTreeNode; diff --git a/client/src/javascript/components/modals/torrent-details-modal/TorrentContents.tsx b/client/src/javascript/components/modals/torrent-details-modal/TorrentContents.tsx index 177714ce..194d3e6b 100644 --- a/client/src/javascript/components/modals/torrent-details-modal/TorrentContents.tsx +++ b/client/src/javascript/components/modals/torrent-details-modal/TorrentContents.tsx @@ -79,6 +79,7 @@ const TorrentContents: FC = observer(() => { setSelectedIndices(selectionTree.getSelectedItems(newItemsTree)); }} hash={hash} + path={[]} itemsTree={itemsTree} /> );