diff --git a/client/src/javascript/components/torrent-list/TorrentList.tsx b/client/src/javascript/components/torrent-list/TorrentList.tsx index 798b122b..4f86c9f2 100644 --- a/client/src/javascript/components/torrent-list/TorrentList.tsx +++ b/client/src/javascript/components/torrent-list/TorrentList.tsx @@ -1,8 +1,7 @@ -import {Component, createRef, FC, ReactNode} from 'react'; -import {FormattedMessage, injectIntl, WrappedComponentProps} from 'react-intl'; +import {FC, ReactNode, useEffect, useRef} from 'react'; +import {FormattedMessage} from 'react-intl'; import {observer} from 'mobx-react'; -import {observable, reaction} from 'mobx'; -import {useDropzone} from 'react-dropzone'; +import {reaction} from 'mobx'; import type {FixedSizeList} from 'react-window'; @@ -18,210 +17,141 @@ import ListViewport from '../general/ListViewport'; import SettingActions from '../../actions/SettingActions'; import SettingStore from '../../stores/SettingStore'; import TableHeading from './TableHeading'; -import TorrentActions from '../../actions/TorrentActions'; import TorrentFilterStore from '../../stores/TorrentFilterStore'; +import TorrentListDropzone from './TorrentListDropzone'; import TorrentListRow from './TorrentListRow'; import TorrentStore from '../../stores/TorrentStore'; import type {TorrentListColumn} from '../../constants/TorrentListColumns'; -const TorrentDropzone: FC<{children: ReactNode}> = ({children}: {children: ReactNode}) => { - const handleFileDrop = (files: Array) => { - const filesData: Array = []; +const TorrentList: FC = observer(() => { + const listHeaderRef = useRef(null); + const listViewportRef = useRef(null); - const callback = (data: string) => { - filesData.push(data); - - if (filesData.length === files.length && filesData[0] != null) { - TorrentActions.addTorrentsByFiles({ - files: filesData as [string, ...string[]], - destination: - SettingStore.floodSettings.torrentDestinations?.[''] ?? SettingStore.clientSettings?.directoryDefault ?? '', - isBasePath: false, - start: SettingStore.floodSettings.startTorrentsOnLoad, - }); - } - }; - - files.forEach((file) => { - const reader = new FileReader(); - reader.onload = (e) => { - if (e.target?.result != null && typeof e.target.result === 'string') { - callback(e.target.result.split('base64,')[1]); + useEffect(() => { + const dispose = reaction( + () => TorrentFilterStore.filters, + () => { + if (listViewportRef.current != null) { + listViewportRef.current.scrollTo(0); } - }; - reader.readAsDataURL(file); - }); - }; - const {getRootProps, isDragActive} = useDropzone({ - onDrop: handleFileDrop, - noClick: true, - noKeyboard: true, - }); + }, + ); - return ( -
evt.preventDefault()})} - className={`dropzone dropzone--with-overlay torrents ${isDragActive ? 'dropzone--is-dragging' : ''}`}> - {children} -
- ); -}; + return dispose; + }, []); -const getEmptyTorrentListNotification = (): ReactNode => { - let clearFilters = null; + const torrents = TorrentStore.filteredTorrents; + const {torrentListViewSize = 'condensed'} = SettingStore.floodSettings; - if (TorrentFilterStore.isFilterActive) { - clearFilters = ( -
- + const isCondensed = torrentListViewSize === 'condensed'; + const isListEmpty = torrents == null || torrents.length === 0; + + let content: ReactNode = null; + let torrentListHeading: ReactNode = null; + if (!ClientStatusStore.isConnected) { + content = ( +
+
+ +
); - } - - return ( -
-
- -
- {clearFilters} -
- ); -}; - -@observer -class TorrentList extends Component { - listHeaderRef = createRef(); - listViewportRef = createRef(); - - torrentListViewportSize = observable.object<{ - width: number; - height: number; - }>({ - width: window.innerWidth, - height: window.innerHeight, - }); - - constructor(props: WrappedComponentProps) { - super(props); - - reaction(() => TorrentFilterStore.filters, this.handleTorrentFilterChange); - } - - handleColumnWidthChange = (column: TorrentListColumn, width: number) => { - const {torrentListColumnWidths = defaultFloodSettings.torrentListColumnWidths} = SettingStore.floodSettings; - - SettingActions.saveSetting('torrentListColumnWidths', { - ...torrentListColumnWidths, - [column]: width, - }); - }; - - handleTorrentFilterChange = () => { - if (this.listViewportRef.current != null) { - this.listViewportRef.current.scrollTo(0); - } - }; - - handleViewportScroll = (scrollLeft: number) => { - if (this.listHeaderRef.current != null) { - this.listHeaderRef.current.scrollLeft = scrollLeft; - } - }; - - render() { - const torrents = TorrentStore.filteredTorrents; - const {torrentListViewSize = 'condensed'} = SettingStore.floodSettings; - - const isCondensed = torrentListViewSize === 'condensed'; - const isListEmpty = torrents == null || torrents.length === 0; - - let content: ReactNode = null; - let torrentListHeading: ReactNode = null; - if (!ClientStatusStore.isConnected) { - content = ( -
-
- -
+ } else if (isListEmpty || torrents == null) { + content = ( +
+
+
- ); - } else if (isListEmpty || torrents == null) { - content = getEmptyTorrentListNotification(); - } else { - if (isCondensed) { - torrentListHeading = ( - { - const currentSort = SettingStore.floodSettings.sortTorrents; + {TorrentFilterStore.isFilterActive && ( +
+ +
+ )} +
+ ); + } else { + if (isCondensed) { + torrentListHeading = ( + { + const currentSort = SettingStore.floodSettings.sortTorrents; - let nextDirection: FloodSettings['sortTorrents']['direction'] = 'asc'; + let nextDirection: FloodSettings['sortTorrents']['direction'] = 'asc'; - if (currentSort.property === property) { - nextDirection = currentSort.direction === 'asc' ? 'desc' : 'asc'; - } - - const sortBy = { - property, - direction: nextDirection, - }; - - SettingActions.saveSetting('sortTorrents', sortBy); - }} - onWidthsChange={this.handleColumnWidthChange} - ref={this.listHeaderRef} - /> - ); - } - - // itemSize must sync with styles &--is-condensed and &--is-expanded - content = ( - { - const {hash} = TorrentStore.filteredTorrents[index]; - - return ; - }} - itemSize={isCondensed ? 30 : 70} - listLength={torrents.length} - ref={this.listViewportRef} - outerRef={(ref) => { - const viewportDiv = ref; - if (viewportDiv != null && viewportDiv.onscroll == null) { - viewportDiv.onscroll = () => { - this.handleViewportScroll(viewportDiv.scrollLeft); - }; + if (currentSort.property === property) { + nextDirection = currentSort.direction === 'asc' ? 'desc' : 'asc'; } + + const sortBy = { + property, + direction: nextDirection, + }; + + SettingActions.saveSetting('sortTorrents', sortBy); }} + onWidthsChange={(column: TorrentListColumn, width: number) => { + const {torrentListColumnWidths = defaultFloodSettings.torrentListColumnWidths} = SettingStore.floodSettings; + + SettingActions.saveSetting('torrentListColumnWidths', { + ...torrentListColumnWidths, + [column]: width, + }); + }} + ref={listHeaderRef} /> ); } - return ( - -
- - {torrentListHeading} - {content} -
-
-
-
- -
- -
-
-
+ // itemSize must sync with styles &--is-condensed and &--is-expanded + content = ( + { + const {hash} = TorrentStore.filteredTorrents[index]; + + return ; + }} + itemSize={isCondensed ? 30 : 70} + listLength={torrents.length} + ref={listViewportRef} + outerRef={(ref) => { + const viewportDiv = ref; + if (viewportDiv != null && viewportDiv.onscroll == null) { + viewportDiv.onscroll = () => { + if (listHeaderRef.current != null) { + listHeaderRef.current.scrollLeft = viewportDiv.scrollLeft; + } + }; + } + }} + /> ); } -} -export default injectIntl(TorrentList); + return ( + +
+ + {torrentListHeading} + {content} +
+
+
+
+ +
+ +
+
+
+ ); +}); + +export default TorrentList; diff --git a/client/src/javascript/components/torrent-list/TorrentListDropzone.tsx b/client/src/javascript/components/torrent-list/TorrentListDropzone.tsx new file mode 100644 index 00000000..b2b7a732 --- /dev/null +++ b/client/src/javascript/components/torrent-list/TorrentListDropzone.tsx @@ -0,0 +1,51 @@ +import {FC, ReactNode} from 'react'; +import {useDropzone} from 'react-dropzone'; + +import SettingStore from '../../stores/SettingStore'; +import TorrentActions from '../../actions/TorrentActions'; + +const handleFileDrop = (files: Array) => { + const filesData: Array = []; + + const callback = (data: string) => { + filesData.push(data); + + if (filesData.length === files.length && filesData[0] != null) { + TorrentActions.addTorrentsByFiles({ + files: filesData as [string, ...string[]], + destination: + SettingStore.floodSettings.torrentDestinations?.[''] ?? SettingStore.clientSettings?.directoryDefault ?? '', + isBasePath: false, + start: SettingStore.floodSettings.startTorrentsOnLoad, + }); + } + }; + + files.forEach((file) => { + const reader = new FileReader(); + reader.onload = (e) => { + if (e.target?.result != null && typeof e.target.result === 'string') { + callback(e.target.result.split('base64,')[1]); + } + }; + reader.readAsDataURL(file); + }); +}; + +const TorrentListDropzone: FC<{children: ReactNode}> = ({children}: {children: ReactNode}) => { + const {getRootProps, isDragActive} = useDropzone({ + onDrop: handleFileDrop, + noClick: true, + noKeyboard: true, + }); + + return ( +
evt.preventDefault()})} + className={`dropzone dropzone--with-overlay torrents ${isDragActive ? 'dropzone--is-dragging' : ''}`}> + {children} +
+ ); +}; + +export default TorrentListDropzone;