From 78611b2f145d5133297aa4bc70d9465606ccc454 Mon Sep 17 00:00:00 2001 From: Jesse Chan Date: Mon, 12 Jul 2021 00:50:50 +0800 Subject: [PATCH] client: fix and improve memoization of "ProgressBar" --- .../components/general/ProgressBar.tsx | 15 ++++--- .../components/general/TorrentStatusIcon.tsx | 43 +++++++------------ .../torrent-details-modal/TorrentHeading.tsx | 8 ++-- .../torrent-list/TorrentListCell.tsx | 12 +++++- .../torrent-list/TorrentListRow.tsx | 2 +- .../torrent-list/TorrentListRowCondensed.tsx | 22 ---------- .../torrent-list/TorrentListRowExpanded.tsx | 36 ++++++++-------- ...rrentStatusClasses.ts => torrentStatus.ts} | 21 ++++++--- 8 files changed, 76 insertions(+), 83 deletions(-) rename client/src/javascript/util/{torrentStatusClasses.ts => torrentStatus.ts} (57%) diff --git a/client/src/javascript/components/general/ProgressBar.tsx b/client/src/javascript/components/general/ProgressBar.tsx index 4f83bd87..610cfdd8 100644 --- a/client/src/javascript/components/general/ProgressBar.tsx +++ b/client/src/javascript/components/general/ProgressBar.tsx @@ -1,13 +1,18 @@ -import {FC, memo, ReactNode} from 'react'; +import {FC} from 'react'; +import {observer} from 'mobx-react'; + +import type {TorrentStatus} from '@shared/constants/torrentStatusMap'; + +import TorrentStatusIcon from './TorrentStatusIcon'; interface ProgressBarProps { percent: number; - icon?: ReactNode; + status?: TorrentStatus; } -const ProgressBar: FC = memo(({percent, icon}: ProgressBarProps) => ( +const ProgressBar: FC = observer(({percent, status}: ProgressBarProps) => (
-
{icon}
+
{status && }
@@ -15,7 +20,7 @@ const ProgressBar: FC = memo(({percent, icon}: ProgressBarProp )); ProgressBar.defaultProps = { - icon: undefined, + status: undefined, }; export default ProgressBar; diff --git a/client/src/javascript/components/general/TorrentStatusIcon.tsx b/client/src/javascript/components/general/TorrentStatusIcon.tsx index ec6547a6..ff6c0c1e 100644 --- a/client/src/javascript/components/general/TorrentStatusIcon.tsx +++ b/client/src/javascript/components/general/TorrentStatusIcon.tsx @@ -4,34 +4,23 @@ import {Error, Spinner, Start, Stop} from '@client/ui/icons'; import type {TorrentStatus} from '@shared/constants/torrentStatusMap'; -const STATUS_ICON_MAP: Partial> = { - error: , - checking: , - stopped: , - downloading: , - seeding: , -} as const; - interface TorrentStatusIconProps { - statuses: TorrentStatus[]; + status: TorrentStatus; } -const TorrentStatusIcon: FC = ({statuses}: TorrentStatusIconProps) => { - let resultIcon = ; +const TorrentStatusIcon: FC = memo(({status}: TorrentStatusIconProps) => { + switch (status) { + case 'error': + return ; + case 'checking': + return ; + case 'downloading': + return ; + case 'seeding': + return ; + default: + return ; + } +}); - Object.keys(STATUS_ICON_MAP).some((key) => { - const status = key as TorrentStatus; - if (statuses.includes(status)) { - const icon = STATUS_ICON_MAP[status]; - if (icon != null) { - resultIcon = icon; - return true; - } - } - return false; - }); - - return resultIcon; -}; - -export default memo(TorrentStatusIcon); +export default TorrentStatusIcon; 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 2921fddb..f8460547 100644 --- a/client/src/javascript/components/modals/torrent-details-modal/TorrentHeading.tsx +++ b/client/src/javascript/components/modals/torrent-details-modal/TorrentHeading.tsx @@ -1,18 +1,18 @@ import classnames from 'classnames'; +import {computed} from 'mobx'; import {FC, useEffect, useState} from 'react'; import {observer} from 'mobx-react'; import {Trans, useLingui} from '@lingui/react'; import {Clock, DownloadThick, Ratio, Start, Stop, UploadThick} from '@client/ui/icons'; import TorrentActions from '@client/actions/TorrentActions'; -import torrentStatusClasses from '@client/util/torrentStatusClasses'; +import {torrentStatusClasses, torrentStatusEffective} from '@client/util/torrentStatus'; import TorrentStore from '@client/stores/TorrentStore'; import UIStore from '@client/stores/UIStore'; import Duration from '../../general/Duration'; import PriorityMeter from '../../general/PriorityMeter'; import ProgressBar from '../../general/ProgressBar'; -import TorrentStatusIcon from '../../general/TorrentStatusIcon'; import Size from '../../general/Size'; const TorrentHeading: FC = observer(() => { @@ -118,8 +118,8 @@ const TorrentHeading: FC = observer(() => {
} + percent={computed(() => Math.ceil(torrent.percentComplete)).get()} + status={computed(() => torrentStatusEffective(torrent.status)).get()} />
); diff --git a/client/src/javascript/components/torrent-list/TorrentListCell.tsx b/client/src/javascript/components/torrent-list/TorrentListCell.tsx index d5f3c6ca..51d1279d 100644 --- a/client/src/javascript/components/torrent-list/TorrentListCell.tsx +++ b/client/src/javascript/components/torrent-list/TorrentListCell.tsx @@ -1,4 +1,5 @@ import classnames from 'classnames'; +import {computed} from 'mobx'; import {FC} from 'react'; import {observer} from 'mobx-react'; import {Trans, useLingui} from '@lingui/react'; @@ -22,7 +23,9 @@ import { UploadThick, } from '@client/ui/icons'; import Duration from '@client/components/general/Duration'; +import ProgressBar from '@client/components/general/ProgressBar'; import Size from '@client/components/general/Size'; +import {torrentStatusEffective} from '@client/util/torrentStatus'; import TorrentStore from '@client/stores/TorrentStore'; import type {TorrentListColumn} from '@client/constants/TorrentListColumns'; @@ -101,7 +104,7 @@ const TrackersCell: FC<{trackers: string[]}> = observer(({trackers}: {trackers: {trackers.join(', ')} )); -interface TorrentListCellContentProps { +export interface TorrentListCellContentProps { torrent: TorrentProperties; column: TorrentListColumn; } @@ -137,6 +140,13 @@ const DefaultTorrentListCellContent: FC = observer( return ; case 'peers': return ; + case 'percentComplete': + return ( + Math.ceil(torrent.percentComplete)).get()} + status={computed(() => torrentStatusEffective(torrent.status)).get()} + /> + ); default: return {torrent[column]}; } diff --git a/client/src/javascript/components/torrent-list/TorrentListRow.tsx b/client/src/javascript/components/torrent-list/TorrentListRow.tsx index 4bf7366f..a0b31402 100644 --- a/client/src/javascript/components/torrent-list/TorrentListRow.tsx +++ b/client/src/javascript/components/torrent-list/TorrentListRow.tsx @@ -10,7 +10,7 @@ import SettingStore from '../../stores/SettingStore'; import TorrentListContextMenu from './TorrentListContextMenu'; import TorrentListRowCondensed from './TorrentListRowCondensed'; import TorrentListRowExpanded from './TorrentListRowExpanded'; -import torrentStatusClasses from '../../util/torrentStatusClasses'; +import {torrentStatusClasses} from '../../util/torrentStatus'; import TorrentStore from '../../stores/TorrentStore'; import UIActions from '../../actions/UIActions'; diff --git a/client/src/javascript/components/torrent-list/TorrentListRowCondensed.tsx b/client/src/javascript/components/torrent-list/TorrentListRowCondensed.tsx index 71323ceb..f703030d 100644 --- a/client/src/javascript/components/torrent-list/TorrentListRowCondensed.tsx +++ b/client/src/javascript/components/torrent-list/TorrentListRowCondensed.tsx @@ -1,11 +1,9 @@ import {CSSProperties, forwardRef, KeyboardEvent, MouseEvent, ReactNodeArray, TouchEvent} from 'react'; import {observer} from 'mobx-react'; -import ProgressBar from '../general/ProgressBar'; import SettingStore from '../../stores/SettingStore'; import TorrentListCell from './TorrentListCell'; import TorrentListColumns from '../../constants/TorrentListColumns'; -import TorrentStatusIcon from '../general/TorrentStatusIcon'; interface TorrentListRowCondensedProps { className: string; @@ -45,26 +43,6 @@ const TorrentListRowCondensed = observer( return accumulator; } - if (id === 'percentComplete') { - accumulator.push( - ( - } - /> - )} - width={SettingStore.floodSettings.torrentListColumnWidths[id]} - />, - ); - - return accumulator; - } - accumulator.push( void; } +const TorrentListCellPercentageNumber: FC = observer( + ({torrent}: TorrentListCellContentProps) => { + const {i18n} = useLingui(); + return ( + + {i18n.number(torrent.percentComplete, {maximumFractionDigits: 1})} + % +  —  + + + ); + }, +); + const TorrentListRowExpanded = observer( forwardRef( ( @@ -38,7 +52,6 @@ const TorrentListRowExpanded = observer( }: TorrentListRowExpandedProps, ref, ) => { - const {i18n} = useLingui(); const columns = SettingStore.floodSettings.torrentListColumns; const primarySection: ReactNodeArray = [ @@ -59,14 +72,7 @@ const TorrentListRowExpanded = observer( key="percentComplete" hash={hash} column="percentComplete" - content={({torrent}) => ( - - {i18n.number(torrent.percentComplete, {maximumFractionDigits: 1})} - % -  —  - - - )} + content={TorrentListCellPercentageNumber} showIcon />, ]; @@ -75,12 +81,6 @@ const TorrentListRowExpanded = observer( key="percentBar" hash={hash} column="percentComplete" - content={({torrent}) => ( - } - /> - )} className="torrent__details__section torrent__details__section--quaternary" classNameOverride />, diff --git a/client/src/javascript/util/torrentStatusClasses.ts b/client/src/javascript/util/torrentStatus.ts similarity index 57% rename from client/src/javascript/util/torrentStatusClasses.ts rename to client/src/javascript/util/torrentStatus.ts index ba3c9f76..f0288c27 100644 --- a/client/src/javascript/util/torrentStatusClasses.ts +++ b/client/src/javascript/util/torrentStatus.ts @@ -2,11 +2,11 @@ import classnames from 'classnames'; import type {TorrentProperties} from '@shared/types/Torrent'; -function torrentStatusClasses( +export const torrentStatusClasses = ( {status, downRate, upRate}: Pick, ...classes: Array -): string { - return classnames(classes, { +): string => + classnames(classes, { 'torrent--has-error': status.includes('error'), 'torrent--is-stopped': status.includes('stopped'), 'torrent--is-downloading': status.includes('downloading'), @@ -17,6 +17,17 @@ function torrentStatusClasses( 'torrent--is-checking': status.includes('checking'), 'torrent--is-inactive': status.includes('inactive'), }); -} -export default torrentStatusClasses; +export const torrentStatusEffective = (status: TorrentProperties['status']): TorrentProperties['status'][number] => { + let result: TorrentProperties['status'][number] = 'stopped'; + + ['error', 'checking', 'stopped', 'downloading', 'seeding'].some((state) => { + if (status.includes(state as TorrentProperties['status'][number])) { + result = state as TorrentProperties['status'][number]; + return true; + } + return false; + }); + + return result; +};