mirror of
https://github.com/Sonarr/Sonarr.git
synced 2026-04-21 22:05:38 -04:00
Convert Components to TypeScript
This commit is contained in:
committed by
Mark McDowall
parent
53d8c9ba8d
commit
e1cbc4a782
@@ -0,0 +1,237 @@
|
||||
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import Alert from 'Components/Alert';
|
||||
import PathInput from 'Components/Form/PathInput';
|
||||
import Button from 'Components/Link/Button';
|
||||
import LoadingIndicator from 'Components/Loading/LoadingIndicator';
|
||||
import InlineMarkdown from 'Components/Markdown/InlineMarkdown';
|
||||
import ModalBody from 'Components/Modal/ModalBody';
|
||||
import ModalContent from 'Components/Modal/ModalContent';
|
||||
import ModalFooter from 'Components/Modal/ModalFooter';
|
||||
import ModalHeader from 'Components/Modal/ModalHeader';
|
||||
import Scroller from 'Components/Scroller/Scroller';
|
||||
import Column from 'Components/Table/Column';
|
||||
import Table from 'Components/Table/Table';
|
||||
import TableBody from 'Components/Table/TableBody';
|
||||
import usePrevious from 'Helpers/Hooks/usePrevious';
|
||||
import { kinds, scrollDirections } from 'Helpers/Props';
|
||||
import { clearPaths, fetchPaths } from 'Store/Actions/pathActions';
|
||||
import createSystemStatusSelector from 'Store/Selectors/createSystemStatusSelector';
|
||||
import { InputChanged } from 'typings/inputs';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import createPathsSelector from './createPathsSelector';
|
||||
import FileBrowserRow from './FileBrowserRow';
|
||||
import styles from './FileBrowserModalContent.css';
|
||||
|
||||
const columns: Column[] = [
|
||||
{
|
||||
name: 'type',
|
||||
label: () => translate('Type'),
|
||||
isVisible: true,
|
||||
},
|
||||
{
|
||||
name: 'name',
|
||||
label: () => translate('Name'),
|
||||
isVisible: true,
|
||||
},
|
||||
];
|
||||
|
||||
const handleClearPaths = () => {};
|
||||
|
||||
export interface FileBrowserModalContentProps {
|
||||
name: string;
|
||||
value: string;
|
||||
includeFiles?: boolean;
|
||||
onChange: (args: InputChanged<string>) => unknown;
|
||||
onModalClose: () => void;
|
||||
}
|
||||
|
||||
function FileBrowserModalContent(props: FileBrowserModalContentProps) {
|
||||
const { name, value, includeFiles = true, onChange, onModalClose } = props;
|
||||
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const { isWindows, mode } = useSelector(createSystemStatusSelector());
|
||||
const { isFetching, isPopulated, error, parent, directories, files, paths } =
|
||||
useSelector(createPathsSelector());
|
||||
|
||||
const [currentPath, setCurrentPath] = useState(value);
|
||||
const scrollerRef = useRef(null);
|
||||
const previousValue = usePrevious(value);
|
||||
|
||||
const emptyParent = parent === '';
|
||||
const isWindowsService = isWindows && mode === 'service';
|
||||
|
||||
const handlePathInputChange = useCallback(
|
||||
({ value }: InputChanged<string>) => {
|
||||
setCurrentPath(value);
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
const handleRowPress = useCallback(
|
||||
(path: string) => {
|
||||
setCurrentPath(path);
|
||||
|
||||
dispatch(
|
||||
fetchPaths({
|
||||
path,
|
||||
allowFoldersWithoutTrailingSlashes: true,
|
||||
includeFiles,
|
||||
})
|
||||
);
|
||||
},
|
||||
[includeFiles, dispatch, setCurrentPath]
|
||||
);
|
||||
|
||||
const handleOkPress = useCallback(() => {
|
||||
onChange({
|
||||
name,
|
||||
value: currentPath,
|
||||
});
|
||||
|
||||
dispatch(clearPaths());
|
||||
onModalClose();
|
||||
}, [name, currentPath, dispatch, onChange, onModalClose]);
|
||||
|
||||
const handleFetchPaths = useCallback(
|
||||
(path: string) => {
|
||||
dispatch(
|
||||
fetchPaths({
|
||||
path,
|
||||
allowFoldersWithoutTrailingSlashes: true,
|
||||
includeFiles,
|
||||
})
|
||||
);
|
||||
},
|
||||
[includeFiles, dispatch]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (value !== previousValue && value !== currentPath) {
|
||||
setCurrentPath(value);
|
||||
}
|
||||
}, [value, previousValue, currentPath, setCurrentPath]);
|
||||
|
||||
useEffect(
|
||||
() => {
|
||||
dispatch(
|
||||
fetchPaths({
|
||||
path: currentPath,
|
||||
allowFoldersWithoutTrailingSlashes: true,
|
||||
includeFiles,
|
||||
})
|
||||
);
|
||||
|
||||
return () => {
|
||||
dispatch(clearPaths());
|
||||
};
|
||||
},
|
||||
// This should only run once when the component mounts,
|
||||
// so we don't need to include the other dependencies.
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
[dispatch]
|
||||
);
|
||||
|
||||
return (
|
||||
<ModalContent onModalClose={onModalClose}>
|
||||
<ModalHeader>{translate('FileBrowser')}</ModalHeader>
|
||||
|
||||
<ModalBody
|
||||
className={styles.modalBody}
|
||||
scrollDirection={scrollDirections.NONE}
|
||||
>
|
||||
{isWindowsService ? (
|
||||
<Alert className={styles.mappedDrivesWarning} kind={kinds.WARNING}>
|
||||
<InlineMarkdown
|
||||
data={translate('MappedNetworkDrivesWindowsService', {
|
||||
url: 'https://wiki.servarr.com/sonarr/faq#why-cant-sonarr-see-my-files-on-a-remote-server',
|
||||
})}
|
||||
/>
|
||||
</Alert>
|
||||
) : null}
|
||||
|
||||
<PathInput
|
||||
className={styles.pathInput}
|
||||
placeholder={translate('FileBrowserPlaceholderText')}
|
||||
hasFileBrowser={false}
|
||||
includeFiles={includeFiles}
|
||||
paths={paths}
|
||||
name={name}
|
||||
value={currentPath}
|
||||
onChange={handlePathInputChange}
|
||||
onFetchPaths={handleFetchPaths}
|
||||
onClearPaths={handleClearPaths}
|
||||
/>
|
||||
|
||||
<Scroller
|
||||
ref={scrollerRef}
|
||||
className={styles.scroller}
|
||||
scrollDirection="both"
|
||||
>
|
||||
{error ? <div>{translate('ErrorLoadingContents')}</div> : null}
|
||||
|
||||
{isPopulated && !error ? (
|
||||
<Table horizontalScroll={false} columns={columns}>
|
||||
<TableBody>
|
||||
{emptyParent ? (
|
||||
<FileBrowserRow
|
||||
type="computer"
|
||||
name={translate('MyComputer')}
|
||||
path={parent}
|
||||
onPress={handleRowPress}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
{!emptyParent && parent ? (
|
||||
<FileBrowserRow
|
||||
type="parent"
|
||||
name="..."
|
||||
path={parent}
|
||||
onPress={handleRowPress}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
{directories.map((directory) => {
|
||||
return (
|
||||
<FileBrowserRow
|
||||
key={directory.path}
|
||||
type={directory.type}
|
||||
name={directory.name}
|
||||
path={directory.path}
|
||||
onPress={handleRowPress}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
|
||||
{files.map((file) => {
|
||||
return (
|
||||
<FileBrowserRow
|
||||
key={file.path}
|
||||
type={file.type}
|
||||
name={file.name}
|
||||
path={file.path}
|
||||
onPress={handleRowPress}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</TableBody>
|
||||
</Table>
|
||||
) : null}
|
||||
</Scroller>
|
||||
</ModalBody>
|
||||
|
||||
<ModalFooter>
|
||||
{isFetching ? (
|
||||
<LoadingIndicator className={styles.loading} size={20} />
|
||||
) : null}
|
||||
|
||||
<Button onPress={onModalClose}>{translate('Cancel')}</Button>
|
||||
|
||||
<Button onPress={handleOkPress}>{translate('Ok')}</Button>
|
||||
</ModalFooter>
|
||||
</ModalContent>
|
||||
);
|
||||
}
|
||||
|
||||
export default FileBrowserModalContent;
|
||||
Reference in New Issue
Block a user