mirror of
https://github.com/Radarr/Radarr.git
synced 2026-04-23 22:25:14 -04:00
New: Favorite folders in Manual Import
(cherry picked from commit 3ddc6ac6de5c27a9aab915672321c8818dc5da48) Closes #10630
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
.actions {
|
||||
composes: cell from '~Components/Table/Cells/TableRowCell.css';
|
||||
|
||||
width: 70px;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
// This file is automatically generated.
|
||||
// Please do not change this file!
|
||||
interface CssExports {
|
||||
'actions': string;
|
||||
}
|
||||
export const cssExports: CssExports;
|
||||
export default cssExports;
|
||||
@@ -0,0 +1,48 @@
|
||||
import React, { SyntheticEvent, useCallback } from 'react';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import IconButton from 'Components/Link/IconButton';
|
||||
import TableRowCell from 'Components/Table/Cells/TableRowCell';
|
||||
import TableRowButton from 'Components/Table/TableRowButton';
|
||||
import { icons } from 'Helpers/Props';
|
||||
import { removeFavoriteFolder } from 'Store/Actions/interactiveImportActions';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import styles from './FavoriteFolderRow.css';
|
||||
|
||||
interface FavoriteFolderRowProps {
|
||||
folder: string;
|
||||
onPress: (folder: string) => unknown;
|
||||
}
|
||||
|
||||
function FavoriteFolderRow({ folder, onPress }: FavoriteFolderRowProps) {
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const handlePress = useCallback(() => {
|
||||
onPress(folder);
|
||||
}, [folder, onPress]);
|
||||
|
||||
const handleRemoveFavoritePress = useCallback(
|
||||
(e: SyntheticEvent) => {
|
||||
e.stopPropagation();
|
||||
|
||||
dispatch(removeFavoriteFolder({ folder }));
|
||||
},
|
||||
[folder, dispatch]
|
||||
);
|
||||
|
||||
return (
|
||||
<TableRowButton onPress={handlePress}>
|
||||
<TableRowCell>{folder}</TableRowCell>
|
||||
|
||||
<TableRowCell className={styles.actions}>
|
||||
<IconButton
|
||||
title={translate('FavoriteFolderRemove')}
|
||||
kind="danger"
|
||||
name={icons.HEART}
|
||||
onPress={handleRemoveFavoritePress}
|
||||
/>
|
||||
</TableRowCell>
|
||||
</TableRowButton>
|
||||
);
|
||||
}
|
||||
|
||||
export default FavoriteFolderRow;
|
||||
@@ -1,7 +1,12 @@
|
||||
.recentFoldersContainer {
|
||||
.foldersContainer {
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
.foldersTitle {
|
||||
border-bottom: 1px solid var(--borderColor);
|
||||
font-size: 21px;
|
||||
}
|
||||
|
||||
.buttonsContainer {
|
||||
margin-top: 30px;
|
||||
}
|
||||
|
||||
+2
-1
@@ -5,7 +5,8 @@ interface CssExports {
|
||||
'buttonContainer': string;
|
||||
'buttonIcon': string;
|
||||
'buttonsContainer': string;
|
||||
'recentFoldersContainer': string;
|
||||
'foldersContainer': string;
|
||||
'foldersTitle': string;
|
||||
}
|
||||
export const cssExports: CssExports;
|
||||
export default cssExports;
|
||||
|
||||
+53
-18
@@ -1,4 +1,4 @@
|
||||
import React, { useCallback, useState } from 'react';
|
||||
import React, { useCallback, useMemo, useState } from 'react';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { createSelector } from 'reselect';
|
||||
import AppState from 'App/State/AppState';
|
||||
@@ -14,14 +14,23 @@ import Table from 'Components/Table/Table';
|
||||
import TableBody from 'Components/Table/TableBody';
|
||||
import { icons, kinds, sizes } from 'Helpers/Props';
|
||||
import { executeCommand } from 'Store/Actions/commandActions';
|
||||
import {
|
||||
addRecentFolder,
|
||||
removeRecentFolder,
|
||||
} from 'Store/Actions/interactiveImportActions';
|
||||
import { addRecentFolder } from 'Store/Actions/interactiveImportActions';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import FavoriteFolderRow from './FavoriteFolderRow';
|
||||
import RecentFolderRow from './RecentFolderRow';
|
||||
import styles from './InteractiveImportSelectFolderModalContent.css';
|
||||
|
||||
const favoriteFoldersColumns = [
|
||||
{
|
||||
name: 'folder',
|
||||
label: () => translate('Folder'),
|
||||
},
|
||||
{
|
||||
name: 'actions',
|
||||
label: '',
|
||||
},
|
||||
];
|
||||
|
||||
const recentFoldersColumns = [
|
||||
{
|
||||
name: 'folder',
|
||||
@@ -49,15 +58,22 @@ function InteractiveImportSelectFolderModalContent(
|
||||
const { modalTitle, onFolderSelect, onModalClose } = props;
|
||||
const [folder, setFolder] = useState('');
|
||||
const dispatch = useDispatch();
|
||||
const recentFolders = useSelector(
|
||||
const { favoriteFolders, recentFolders } = useSelector(
|
||||
createSelector(
|
||||
(state: AppState) => state.interactiveImport.recentFolders,
|
||||
(recentFolders) => {
|
||||
return recentFolders;
|
||||
(state: AppState) => state.interactiveImport,
|
||||
(interactiveImport) => {
|
||||
return {
|
||||
favoriteFolders: interactiveImport.favoriteFolders,
|
||||
recentFolders: interactiveImport.recentFolders,
|
||||
};
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
const favoriteFolderMap = useMemo(() => {
|
||||
return new Map(favoriteFolders.map((f) => [f.folder, f]));
|
||||
}, [favoriteFolders]);
|
||||
|
||||
const onPathChange = useCallback(
|
||||
({ value }: { value: string }) => {
|
||||
setFolder(value);
|
||||
@@ -90,13 +106,6 @@ function InteractiveImportSelectFolderModalContent(
|
||||
onFolderSelect(folder);
|
||||
}, [folder, onFolderSelect, dispatch]);
|
||||
|
||||
const onRemoveRecentFolderPress = useCallback(
|
||||
(folderToRemove: string) => {
|
||||
dispatch(removeRecentFolder({ folder: folderToRemove }));
|
||||
},
|
||||
[dispatch]
|
||||
);
|
||||
|
||||
return (
|
||||
<ModalContent onModalClose={onModalClose}>
|
||||
<ModalHeader>
|
||||
@@ -110,8 +119,34 @@ function InteractiveImportSelectFolderModalContent(
|
||||
onChange={onPathChange}
|
||||
/>
|
||||
|
||||
{favoriteFolders.length ? (
|
||||
<div className={styles.foldersContainer}>
|
||||
<div className={styles.foldersTitle}>
|
||||
{translate('FavoriteFolders')}
|
||||
</div>
|
||||
|
||||
<Table columns={favoriteFoldersColumns}>
|
||||
<TableBody>
|
||||
{favoriteFolders.map((favoriteFolder) => {
|
||||
return (
|
||||
<FavoriteFolderRow
|
||||
key={favoriteFolder.folder}
|
||||
folder={favoriteFolder.folder}
|
||||
onPress={onRecentPathPress}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
{recentFolders.length ? (
|
||||
<div className={styles.recentFoldersContainer}>
|
||||
<div className={styles.foldersContainer}>
|
||||
<div className={styles.foldersTitle}>
|
||||
{translate('RecentFolders')}
|
||||
</div>
|
||||
|
||||
<Table columns={recentFoldersColumns}>
|
||||
<TableBody>
|
||||
{recentFolders
|
||||
@@ -123,8 +158,8 @@ function InteractiveImportSelectFolderModalContent(
|
||||
key={recentFolder.folder}
|
||||
folder={recentFolder.folder}
|
||||
lastUsed={recentFolder.lastUsed}
|
||||
isFavorite={favoriteFolderMap.has(recentFolder.folder)}
|
||||
onPress={onRecentPathPress}
|
||||
onRemoveRecentFolderPress={onRemoveRecentFolderPress}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
interface RecentFolder {
|
||||
folder: string;
|
||||
lastUsed: string;
|
||||
}
|
||||
|
||||
export default RecentFolder;
|
||||
@@ -1,5 +1,5 @@
|
||||
.actions {
|
||||
composes: cell from '~Components/Table/Cells/TableRowCell.css';
|
||||
|
||||
width: 40px;
|
||||
width: 70px;
|
||||
}
|
||||
|
||||
@@ -1,65 +0,0 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { Component } from 'react';
|
||||
import IconButton from 'Components/Link/IconButton';
|
||||
import RelativeDateCell from 'Components/Table/Cells/RelativeDateCell';
|
||||
import TableRowCell from 'Components/Table/Cells/TableRowCell';
|
||||
import TableRowButton from 'Components/Table/TableRowButton';
|
||||
import { icons } from 'Helpers/Props';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import styles from './RecentFolderRow.css';
|
||||
|
||||
class RecentFolderRow extends Component {
|
||||
|
||||
//
|
||||
// Listeners
|
||||
|
||||
onPress = () => {
|
||||
this.props.onPress(this.props.folder);
|
||||
};
|
||||
|
||||
onRemovePress = (event) => {
|
||||
event.stopPropagation();
|
||||
|
||||
const {
|
||||
folder,
|
||||
onRemoveRecentFolderPress
|
||||
} = this.props;
|
||||
|
||||
onRemoveRecentFolderPress(folder);
|
||||
};
|
||||
|
||||
//
|
||||
// Render
|
||||
|
||||
render() {
|
||||
const {
|
||||
folder,
|
||||
lastUsed
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
<TableRowButton onPress={this.onPress}>
|
||||
<TableRowCell>{folder}</TableRowCell>
|
||||
|
||||
<RelativeDateCell date={lastUsed} />
|
||||
|
||||
<TableRowCell className={styles.actions}>
|
||||
<IconButton
|
||||
title={translate('Remove')}
|
||||
name={icons.REMOVE}
|
||||
onPress={this.onRemovePress}
|
||||
/>
|
||||
</TableRowCell>
|
||||
</TableRowButton>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
RecentFolderRow.propTypes = {
|
||||
folder: PropTypes.string.isRequired,
|
||||
lastUsed: PropTypes.string.isRequired,
|
||||
onPress: PropTypes.func.isRequired,
|
||||
onRemoveRecentFolderPress: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
export default RecentFolderRow;
|
||||
@@ -0,0 +1,85 @@
|
||||
import React, { SyntheticEvent, useCallback } from 'react';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import IconButton from 'Components/Link/IconButton';
|
||||
import RelativeDateCell from 'Components/Table/Cells/RelativeDateCell';
|
||||
import TableRowCell from 'Components/Table/Cells/TableRowCell';
|
||||
import TableRowButton from 'Components/Table/TableRowButton';
|
||||
import { icons } from 'Helpers/Props';
|
||||
import {
|
||||
addFavoriteFolder,
|
||||
removeFavoriteFolder,
|
||||
removeRecentFolder,
|
||||
} from 'Store/Actions/interactiveImportActions';
|
||||
import translate from 'Utilities/String/translate';
|
||||
import styles from './RecentFolderRow.css';
|
||||
|
||||
interface RecentFolderRowProps {
|
||||
folder: string;
|
||||
lastUsed: string;
|
||||
isFavorite: boolean;
|
||||
onPress: (folder: string) => unknown;
|
||||
}
|
||||
|
||||
function RecentFolderRow({
|
||||
folder,
|
||||
lastUsed,
|
||||
isFavorite,
|
||||
onPress,
|
||||
}: RecentFolderRowProps) {
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const handlePress = useCallback(() => {
|
||||
onPress(folder);
|
||||
}, [folder, onPress]);
|
||||
|
||||
const handleFavoritePress = useCallback(
|
||||
(e: SyntheticEvent) => {
|
||||
e.stopPropagation();
|
||||
|
||||
if (isFavorite) {
|
||||
dispatch(removeFavoriteFolder({ folder }));
|
||||
} else {
|
||||
dispatch(addFavoriteFolder({ folder }));
|
||||
}
|
||||
},
|
||||
[folder, isFavorite, dispatch]
|
||||
);
|
||||
|
||||
const handleRemovePress = useCallback(
|
||||
(e: SyntheticEvent) => {
|
||||
e.stopPropagation();
|
||||
|
||||
dispatch(removeRecentFolder({ folder }));
|
||||
},
|
||||
[folder, dispatch]
|
||||
);
|
||||
|
||||
return (
|
||||
<TableRowButton onPress={handlePress}>
|
||||
<TableRowCell>{folder}</TableRowCell>
|
||||
|
||||
<RelativeDateCell date={lastUsed} />
|
||||
|
||||
<TableRowCell className={styles.actions}>
|
||||
<IconButton
|
||||
title={
|
||||
isFavorite
|
||||
? translate('FavoriteFolderRemove')
|
||||
: translate('FavoriteFolderAdd')
|
||||
}
|
||||
kind={isFavorite ? 'danger' : 'default'}
|
||||
name={isFavorite ? icons.HEART : icons.HEART_OUTLINE}
|
||||
onPress={handleFavoritePress}
|
||||
/>
|
||||
|
||||
<IconButton
|
||||
title={translate('Remove')}
|
||||
name={icons.REMOVE}
|
||||
onPress={handleRemovePress}
|
||||
/>
|
||||
</TableRowCell>
|
||||
</TableRowButton>
|
||||
);
|
||||
}
|
||||
|
||||
export default RecentFolderRow;
|
||||
Reference in New Issue
Block a user