diff --git a/client/src/javascript/components/modals/torrent-details-modal/TorrentHeading.tsx b/client/src/javascript/components/modals/torrent-details-modal/TorrentHeading.tsx index 80a055b2..8443013d 100644 --- a/client/src/javascript/components/modals/torrent-details-modal/TorrentHeading.tsx +++ b/client/src/javascript/components/modals/torrent-details-modal/TorrentHeading.tsx @@ -43,7 +43,7 @@ class TorrentHeading extends React.Component { return null; } - const torrentClasses = torrentStatusClasses(torrent, 'torrent-details__header'); + const torrentClasses = torrentStatusClasses(torrent.status, 'torrent-details__header'); const torrentStatusIcon = torrentStatusIcons(torrent.status); this.torrentStatus = getCurrentStatus(torrent.status); diff --git a/client/src/javascript/components/torrent-list/TorrentList.tsx b/client/src/javascript/components/torrent-list/TorrentList.tsx index 27f65956..9d663e7d 100644 --- a/client/src/javascript/components/torrent-list/TorrentList.tsx +++ b/client/src/javascript/components/torrent-list/TorrentList.tsx @@ -7,7 +7,6 @@ import React from 'react'; import defaultFloodSettings from '@shared/constants/defaultFloodSettings'; import type {FloodSettings} from '@shared/types/FloodSettings'; -import type {TorrentProperties} from '@shared/types/Torrent'; import {Button} from '../../ui'; import ClientStatusStore from '../../stores/ClientStatusStore'; @@ -53,9 +52,9 @@ const getEmptyTorrentListNotification = (): React.ReactNode => { ); }; -const handleClick = (torrent: TorrentProperties, event: React.MouseEvent) => - UIActions.handleTorrentClick({hash: torrent.hash, event}); -const handleDoubleClick = (torrent: TorrentProperties) => TorrentListContextMenu.handleDetailsClick(torrent); +const handleClick = (hash: string, event: React.MouseEvent) => UIActions.handleTorrentClick({hash, event}); +const handleDoubleClick = (hash: string) => TorrentListContextMenu.handleDetailsClick(hash); + @observer class TorrentList extends React.Component { listContainer: HTMLDivElement | null = null; @@ -82,7 +81,7 @@ class TorrentList extends React.Component { }); }; - handleContextMenuClick = (torrent: TorrentProperties, event: React.MouseEvent | React.TouchEvent) => { + handleContextMenuClick = (hash: string, event: React.MouseEvent | React.TouchEvent) => { if (event.cancelable === true) { event.preventDefault(); } @@ -92,11 +91,12 @@ class TorrentList extends React.Component { const touchClientX = ((event as unknown) as TouchEvent).touches?.[0].clientX; const touchClientY = ((event as unknown) as TouchEvent).touches?.[0].clientY; - if (!TorrentStore.selectedTorrents.includes(torrent.hash)) { - UIActions.handleTorrentClick({hash: torrent.hash, event}); + if (!TorrentStore.selectedTorrents.includes(hash)) { + UIActions.handleTorrentClick({hash, event}); } const {torrentContextMenuActions = defaultFloodSettings.torrentContextMenuActions} = SettingStore.floodSettings; + const torrent = TorrentStore.torrents[hash]; UIActions.displayContextMenu({ id: 'torrent-list-item', diff --git a/client/src/javascript/components/torrent-list/TorrentListCell.tsx b/client/src/javascript/components/torrent-list/TorrentListCell.tsx index 7755ba82..e91ea6c1 100644 --- a/client/src/javascript/components/torrent-list/TorrentListCell.tsx +++ b/client/src/javascript/components/torrent-list/TorrentListCell.tsx @@ -1,23 +1,32 @@ import classnames from 'classnames'; +import {observer} from 'mobx-react'; import React from 'react'; +import type {TorrentProperties} from '@shared/types/Torrent'; + import DetailNotAvailableIcon from '../icons/DetailNotAvailableIcon'; +import {getTorrentListCellContent} from '../../util/torrentListCellContents'; import torrentPropertyIcons from '../../util/torrentPropertyIcons'; +import TorrentStore from '../../stores/TorrentStore'; import type {TorrentListColumn} from '../../constants/TorrentListColumns'; interface TorrentListCellProps { - content: React.ReactNode; + hash: string; column: TorrentListColumn; + content?: (torrent: TorrentProperties, column: TorrentListColumn) => React.ReactNode; className?: string; + classNameOverride?: boolean; width?: number; showIcon?: boolean; } const TorrentListCell: React.FC = ({ + hash, content, column, className, + classNameOverride, width, showIcon, }: TorrentListCellProps) => { @@ -25,18 +34,20 @@ const TorrentListCell: React.FC = ({ return (
{icon} - {content || } + {content?.(TorrentStore.torrents[hash], column) || }
); }; TorrentListCell.defaultProps = { className: undefined, + classNameOverride: false, + content: getTorrentListCellContent, width: undefined, showIcon: false, }; -export default React.memo(TorrentListCell); +export default observer(TorrentListCell); diff --git a/client/src/javascript/components/torrent-list/TorrentListContextMenu.tsx b/client/src/javascript/components/torrent-list/TorrentListContextMenu.tsx index 3b5184da..ec97f616 100644 --- a/client/src/javascript/components/torrent-list/TorrentListContextMenu.tsx +++ b/client/src/javascript/components/torrent-list/TorrentListContextMenu.tsx @@ -17,10 +17,10 @@ import type {TorrentContextMenuAction} from '../../constants/TorrentContextMenuA const priorityMeterRef: React.RefObject = React.createRef(); let prioritySelected = 1; -const handleDetailsClick = (torrent: TorrentProperties): void => { +const handleDetailsClick = (hash: string): void => { UIActions.displayModal({ id: 'torrent-details', - hash: torrent.hash, + hash, }); }; @@ -66,10 +66,10 @@ const handleItemClick = (action: TorrentContextMenuAction, event: React.MouseEve UIActions.displayModal({id: 'move-torrents'}); break; case 'torrentDetails': - handleDetailsClick(TorrentStore.torrents[selectedTorrents.pop() as string]); + handleDetailsClick(selectedTorrents[selectedTorrents.length - 1]); break; case 'torrentDownload': - handleTorrentDownload(TorrentStore.torrents[selectedTorrents.pop() as string], event); + handleTorrentDownload(TorrentStore.torrents[selectedTorrents[selectedTorrents.length - 1]], event); break; case 'setPriority': if (priorityMeterRef.current != null) { diff --git a/client/src/javascript/components/torrent-list/TorrentListRow.tsx b/client/src/javascript/components/torrent-list/TorrentListRow.tsx index f3777f7c..ff89b6b3 100644 --- a/client/src/javascript/components/torrent-list/TorrentListRow.tsx +++ b/client/src/javascript/components/torrent-list/TorrentListRow.tsx @@ -3,8 +3,6 @@ import {LongPressDetectEvents, useLongPress} from 'use-long-press'; import {observer} from 'mobx-react'; import React from 'react'; -import type {TorrentProperties} from '@shared/types/Torrent'; - import SettingStore from '../../stores/SettingStore'; import torrentStatusClasses from '../../util/torrentStatusClasses'; import TorrentStore from '../../stores/TorrentStore'; @@ -14,21 +12,20 @@ import TorrentListRowExpanded from './TorrentListRowExpanded'; interface TorrentListRowProps { hash: string; - handleClick: (torrent: TorrentProperties, event: React.MouseEvent) => void; - handleDoubleClick: (torrent: TorrentProperties, event: React.MouseEvent) => void; - handleRightClick: (torrent: TorrentProperties, event: React.MouseEvent | React.TouchEvent) => void; + handleClick: (hash: string, event: React.MouseEvent) => void; + handleDoubleClick: (hash: string, event: React.MouseEvent) => void; + handleRightClick: (hash: string, event: React.MouseEvent | React.TouchEvent) => void; } const TorrentListRow: React.FC = (props: TorrentListRowProps) => { const {hash, handleClick, handleDoubleClick, handleRightClick} = props; - const torrent = TorrentStore.torrents[hash]; const isCondensed = SettingStore.floodSettings.torrentListViewSize === 'condensed'; const torrentClasses = torrentStatusClasses( - torrent, + TorrentStore.torrents?.[hash].status, classnames({ - 'torrent--is-selected': TorrentStore.selectedTorrents.includes(torrent.hash), + 'torrent--is-selected': TorrentStore.selectedTorrents.includes(hash), 'torrent--is-condensed': isCondensed, 'torrent--is-expanded': !isCondensed, }), @@ -38,7 +35,7 @@ const TorrentListRow: React.FC = (props: TorrentListRowProp const longPressBind = useLongPress( (e) => { if (e != null) { - handleRightClick(torrent, e); + handleRightClick(hash, e); } }, { diff --git a/client/src/javascript/components/torrent-list/TorrentListRowCondensed.tsx b/client/src/javascript/components/torrent-list/TorrentListRowCondensed.tsx index ab62a59c..a83bb3fb 100644 --- a/client/src/javascript/components/torrent-list/TorrentListRowCondensed.tsx +++ b/client/src/javascript/components/torrent-list/TorrentListRowCondensed.tsx @@ -1,21 +1,17 @@ import {observer} from 'mobx-react'; import React from 'react'; -import type {TorrentProperties} from '@shared/types/Torrent'; - -import {getTorrentListCellContent} from '../../util/torrentListCellContents'; import ProgressBar from '../general/ProgressBar'; import SettingStore from '../../stores/SettingStore'; import TorrentListCell from './TorrentListCell'; import torrentStatusIcons from '../../util/torrentStatusIcons'; -import TorrentStore from '../../stores/TorrentStore'; interface TorrentListRowCondensedProps { className: string; hash: string; - handleClick: (torrent: TorrentProperties, event: React.MouseEvent) => void; - handleDoubleClick: (torrent: TorrentProperties, event: React.MouseEvent) => void; - handleRightClick: (torrent: TorrentProperties, event: React.MouseEvent) => void; + handleClick: (hash: string, event: React.MouseEvent) => void; + handleDoubleClick: (hash: string, event: React.MouseEvent) => void; + handleRightClick: (hash: string, event: React.MouseEvent) => void; handleTouchStart: (event: React.TouchEvent) => void; handleTouchEnd: (event: React.TouchEvent) => void; } @@ -33,26 +29,35 @@ const TorrentListRowCondensed = React.forwardRef { - const torrent = TorrentStore.torrents[hash]; const torrentListColumns = SettingStore.floodSettings.torrentListColumns.reduce( (accumulator: React.ReactNodeArray, {id, visible}) => { if (!visible) { return accumulator; } - const content: React.ReactNode = - id === 'percentComplete' ? ( - - ) : ( - getTorrentListCellContent(torrent, id) + if (id === 'percentComplete') { + accumulator.push( + ( + + )} + width={SettingStore.floodSettings.torrentListColumnWidths[id]} + />, ); + return accumulator; + } + accumulator.push( , ); @@ -65,9 +70,9 @@ const TorrentListRowCondensed = React.forwardRef diff --git a/client/src/javascript/components/torrent-list/TorrentListRowExpanded.tsx b/client/src/javascript/components/torrent-list/TorrentListRowExpanded.tsx index 0b5df29a..5977235b 100644 --- a/client/src/javascript/components/torrent-list/TorrentListRowExpanded.tsx +++ b/client/src/javascript/components/torrent-list/TorrentListRowExpanded.tsx @@ -2,22 +2,18 @@ import {FormattedNumber} from 'react-intl'; import {observer} from 'mobx-react'; import React from 'react'; -import type {TorrentProperties} from '@shared/types/Torrent'; - -import {getTorrentListCellContent} from '../../util/torrentListCellContents'; import ProgressBar from '../general/ProgressBar'; import SettingStore from '../../stores/SettingStore'; import Size from '../general/Size'; import TorrentListCell from './TorrentListCell'; import torrentStatusIcons from '../../util/torrentStatusIcons'; -import TorrentStore from '../../stores/TorrentStore'; interface TorrentListRowExpandedProps { className: string; hash: string; - handleClick: (torrent: TorrentProperties, event: React.MouseEvent) => void; - handleDoubleClick: (torrent: TorrentProperties, event: React.MouseEvent) => void; - handleRightClick: (torrent: TorrentProperties, event: React.MouseEvent) => void; + handleClick: (hash: string, event: React.MouseEvent) => void; + handleDoubleClick: (hash: string, event: React.MouseEvent) => void; + handleRightClick: (hash: string, event: React.MouseEvent) => void; handleTouchStart: (event: React.TouchEvent) => void; handleTouchEnd: (event: React.TouchEvent) => void; } @@ -35,42 +31,49 @@ const TorrentListRowExpanded = React.forwardRef { - const torrent = TorrentStore.torrents[hash]; const columns = SettingStore.floodSettings.torrentListColumns; const primarySection: React.ReactNodeArray = [ , ]; const secondarySection: React.ReactNodeArray = [ - , - , - , + , + , + , ]; const tertiarySection: React.ReactNodeArray = [ ( %  —  - } + )} showIcon />, ]; + const quaternarySection: React.ReactNodeArray = [ + ( + + )} + className="torrent__details__section torrent__details__section--quaternary" + classNameOverride + />, + ]; // Using a for loop to maximize performance. for (let index = 0; index < columns.length; index += 1) { @@ -88,9 +91,7 @@ const TorrentListRowExpanded = React.forwardRef, - ); + tertiarySection.push(); break; } } @@ -99,9 +100,9 @@ const TorrentListRowExpanded = React.forwardRef @@ -110,9 +111,7 @@ const TorrentListRowExpanded = React.forwardRef{secondarySection}
{tertiarySection}
-
- -
+ {quaternarySection} ); }, diff --git a/client/src/javascript/util/torrentStatusClasses.ts b/client/src/javascript/util/torrentStatusClasses.ts index 2c0f6301..f52ab59b 100644 --- a/client/src/javascript/util/torrentStatusClasses.ts +++ b/client/src/javascript/util/torrentStatusClasses.ts @@ -1,17 +1,18 @@ import classnames from 'classnames'; + import type {TorrentStatus} from '@shared/constants/torrentStatusMap'; -function torrentStatusClasses(torrent: {status: Array}, ...classes: Array) { +function torrentStatusClasses(status: Array, ...classes: Array) { return classnames(classes, { - 'torrent--has-error': torrent.status.includes('error'), - 'torrent--is-stopped': torrent.status.includes('stopped'), - 'torrent--is-downloading': torrent.status.includes('downloading'), - 'torrent--is-downloading--actively': torrent.status.includes('activelyDownloading'), - 'torrent--is-uploading--actively': torrent.status.includes('activelyUploading'), - 'torrent--is-seeding': torrent.status.includes('seeding'), - 'torrent--is-completed': torrent.status.includes('complete'), - 'torrent--is-checking': torrent.status.includes('checking'), - 'torrent--is-inactive': torrent.status.includes('inactive'), + 'torrent--has-error': status.includes('error'), + 'torrent--is-stopped': status.includes('stopped'), + 'torrent--is-downloading': status.includes('downloading'), + 'torrent--is-downloading--actively': status.includes('activelyDownloading'), + 'torrent--is-uploading--actively': status.includes('activelyUploading'), + 'torrent--is-seeding': status.includes('seeding'), + 'torrent--is-completed': status.includes('complete'), + 'torrent--is-checking': status.includes('checking'), + 'torrent--is-inactive': status.includes('inactive'), }); }