diff --git a/frontend/src/App/State/AppSectionState.ts b/frontend/src/App/State/AppSectionState.ts index cabc39b1c..a0bed6b2d 100644 --- a/frontend/src/App/State/AppSectionState.ts +++ b/frontend/src/App/State/AppSectionState.ts @@ -18,7 +18,10 @@ export interface AppSectionSaveState { } export interface PagedAppSectionState { + page: number; pageSize: number; + totalPages: number; + totalRecords?: number; } export interface AppSectionFilterState { @@ -38,6 +41,7 @@ export interface AppSectionItemState { isFetching: boolean; isPopulated: boolean; error: Error; + pendingChanges: Partial; item: T; } diff --git a/frontend/src/App/State/SettingsAppState.ts b/frontend/src/App/State/SettingsAppState.ts index a0bea0973..e4322db69 100644 --- a/frontend/src/App/State/SettingsAppState.ts +++ b/frontend/src/App/State/SettingsAppState.ts @@ -3,10 +3,12 @@ import AppSectionState, { AppSectionItemState, AppSectionSaveState, AppSectionSchemaState, + PagedAppSectionState, } from 'App/State/AppSectionState'; import Language from 'Language/Language'; import DownloadClient from 'typings/DownloadClient'; import ImportList from 'typings/ImportList'; +import ImportListExclusion from 'typings/ImportListExclusion'; import ImportListOptionsSettings from 'typings/ImportListOptionsSettings'; import Indexer from 'typings/Indexer'; import IndexerFlag from 'typings/IndexerFlag'; @@ -41,6 +43,14 @@ export interface ImportListOptionsSettingsAppState extends AppSectionItemState, AppSectionSaveState {} +export interface ImportListExclusionsSettingsAppState + extends AppSectionState, + AppSectionSaveState, + PagedAppSectionState, + AppSectionDeleteState { + pendingChanges: Partial; +} + export type IndexerFlagSettingsAppState = AppSectionState; export type LanguageSettingsAppState = AppSectionState; export type UiSettingsAppState = AppSectionItemState; @@ -48,6 +58,7 @@ export type UiSettingsAppState = AppSectionItemState; interface SettingsAppState { advancedSettings: boolean; downloadClients: DownloadClientAppState; + importListExclusions: ImportListExclusionsSettingsAppState; importListOptions: ImportListOptionsSettingsAppState; importLists: ImportListAppState; indexerFlags: IndexerFlagSettingsAppState; diff --git a/frontend/src/Components/Table/Column.ts b/frontend/src/Components/Table/Column.ts index 31a696df7..e49d67c44 100644 --- a/frontend/src/Components/Table/Column.ts +++ b/frontend/src/Components/Table/Column.ts @@ -5,6 +5,7 @@ type PropertyFunction = () => T; interface Column { name: string; label: string | PropertyFunction | React.ReactNode; + className?: string; columnLabel?: string; isSortable?: boolean; isVisible: boolean; diff --git a/frontend/src/Components/Table/Table.js b/frontend/src/Components/Table/Table.js index 8afbf9ea0..4c970e469 100644 --- a/frontend/src/Components/Table/Table.js +++ b/frontend/src/Components/Table/Table.js @@ -66,7 +66,9 @@ function Table(props) { columns.map((column) => { const { name, - isVisible + isVisible, + isSortable, + ...otherColumnProps } = column; if (!isVisible) { @@ -84,6 +86,7 @@ function Table(props) { name={name} isSortable={false} {...otherProps} + {...otherColumnProps} > void; +} + +function usePaging(options: PagingOptions) { + const { page, totalPages, gotoPage } = options; + const dispatch = useDispatch(); + + const handleFirstPagePress = useCallback(() => { + dispatch(gotoPage({ page: 1 })); + }, [dispatch, gotoPage]); + + const handlePreviousPagePress = useCallback(() => { + dispatch(gotoPage({ page: Math.max(page - 1, 1) })); + }, [page, dispatch, gotoPage]); + + const handleNextPagePress = useCallback(() => { + dispatch(gotoPage({ page: Math.min(page + 1, totalPages) })); + }, [page, totalPages, dispatch, gotoPage]); + + const handleLastPagePress = useCallback(() => { + dispatch(gotoPage({ page: totalPages })); + }, [totalPages, dispatch, gotoPage]); + + const handlePageSelect = useCallback( + (page: number) => { + dispatch(gotoPage({ page })); + }, + [dispatch, gotoPage] + ); + + return useMemo(() => { + return { + handleFirstPagePress, + handlePreviousPagePress, + handleNextPagePress, + handleLastPagePress, + handlePageSelect, + }; + }, [ + handleFirstPagePress, + handlePreviousPagePress, + handleNextPagePress, + handleLastPagePress, + handlePageSelect, + ]); +} + +export default usePaging; diff --git a/frontend/src/Helpers/Hooks/useCurrentPage.ts b/frontend/src/Helpers/Hooks/useCurrentPage.ts new file mode 100644 index 000000000..3caf66df2 --- /dev/null +++ b/frontend/src/Helpers/Hooks/useCurrentPage.ts @@ -0,0 +1,9 @@ +import { useHistory } from 'react-router-dom'; + +function useCurrentPage() { + const history = useHistory(); + + return history.action === 'POP'; +} + +export default useCurrentPage; diff --git a/frontend/src/Settings/ImportLists/ImportListExclusions/EditImportListExclusionModal.js b/frontend/src/Settings/ImportLists/ImportListExclusions/EditImportListExclusionModal.js deleted file mode 100644 index 57a7b0e2d..000000000 --- a/frontend/src/Settings/ImportLists/ImportListExclusions/EditImportListExclusionModal.js +++ /dev/null @@ -1,27 +0,0 @@ -import PropTypes from 'prop-types'; -import React from 'react'; -import Modal from 'Components/Modal/Modal'; -import { sizes } from 'Helpers/Props'; -import EditImportListExclusionModalContentConnector from './EditImportListExclusionModalContentConnector'; - -function EditImportListExclusionModal({ isOpen, onModalClose, ...otherProps }) { - return ( - - - - ); -} - -EditImportListExclusionModal.propTypes = { - isOpen: PropTypes.bool.isRequired, - onModalClose: PropTypes.func.isRequired -}; - -export default EditImportListExclusionModal; diff --git a/frontend/src/Settings/ImportLists/ImportListExclusions/EditImportListExclusionModal.tsx b/frontend/src/Settings/ImportLists/ImportListExclusions/EditImportListExclusionModal.tsx new file mode 100644 index 000000000..b889a8105 --- /dev/null +++ b/frontend/src/Settings/ImportLists/ImportListExclusions/EditImportListExclusionModal.tsx @@ -0,0 +1,41 @@ +import React, { useCallback } from 'react'; +import { useDispatch } from 'react-redux'; +import Modal from 'Components/Modal/Modal'; +import { sizes } from 'Helpers/Props'; +import { clearPendingChanges } from 'Store/Actions/baseActions'; +import EditImportListExclusionModalContent from './EditImportListExclusionModalContent'; + +interface EditImportListExclusionModalProps { + id?: number; + isOpen: boolean; + onModalClose: () => void; + onDeleteImportListExclusionPress?: () => void; +} + +function EditImportListExclusionModal( + props: EditImportListExclusionModalProps +) { + const { isOpen, onModalClose, ...otherProps } = props; + + const dispatch = useDispatch(); + + const onModalClosePress = useCallback(() => { + dispatch( + clearPendingChanges({ + section: 'settings.importListExclusions', + }) + ); + onModalClose(); + }, [dispatch, onModalClose]); + + return ( + + + + ); +} + +export default EditImportListExclusionModal; diff --git a/frontend/src/Settings/ImportLists/ImportListExclusions/EditImportListExclusionModalConnector.js b/frontend/src/Settings/ImportLists/ImportListExclusions/EditImportListExclusionModalConnector.js deleted file mode 100644 index cd4338621..000000000 --- a/frontend/src/Settings/ImportLists/ImportListExclusions/EditImportListExclusionModalConnector.js +++ /dev/null @@ -1,43 +0,0 @@ -import PropTypes from 'prop-types'; -import React, { Component } from 'react'; -import { connect } from 'react-redux'; -import { clearPendingChanges } from 'Store/Actions/baseActions'; -import EditImportListExclusionModal from './EditImportListExclusionModal'; - -function mapStateToProps() { - return {}; -} - -const mapDispatchToProps = { - clearPendingChanges -}; - -class EditImportListExclusionModalConnector extends Component { - - // - // Listeners - - onModalClose = () => { - this.props.clearPendingChanges({ section: 'settings.importListExclusions' }); - this.props.onModalClose(); - }; - - // - // Render - - render() { - return ( - - ); - } -} - -EditImportListExclusionModalConnector.propTypes = { - onModalClose: PropTypes.func.isRequired, - clearPendingChanges: PropTypes.func.isRequired -}; - -export default connect(mapStateToProps, mapDispatchToProps)(EditImportListExclusionModalConnector); diff --git a/frontend/src/Settings/ImportLists/ImportListExclusions/EditImportListExclusionModalContent.js b/frontend/src/Settings/ImportLists/ImportListExclusions/EditImportListExclusionModalContent.js deleted file mode 100644 index a430df34b..000000000 --- a/frontend/src/Settings/ImportLists/ImportListExclusions/EditImportListExclusionModalContent.js +++ /dev/null @@ -1,147 +0,0 @@ -import PropTypes from 'prop-types'; -import React from 'react'; -import Alert from 'Components/Alert'; -import Form from 'Components/Form/Form'; -import FormGroup from 'Components/Form/FormGroup'; -import FormInputGroup from 'Components/Form/FormInputGroup'; -import FormLabel from 'Components/Form/FormLabel'; -import Button from 'Components/Link/Button'; -import SpinnerErrorButton from 'Components/Link/SpinnerErrorButton'; -import LoadingIndicator from 'Components/Loading/LoadingIndicator'; -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 { inputTypes, kinds } from 'Helpers/Props'; -import translate from 'Utilities/String/translate'; -import styles from './EditImportListExclusionModalContent.css'; - -function EditImportListExclusionModalContent(props) { - const { - id, - isFetching, - error, - isSaving, - saveError, - item, - onInputChange, - onSavePress, - onModalClose, - onDeleteImportListExclusionPress, - ...otherProps - } = props; - - const { - movieTitle = '', - tmdbId, - movieYear - } = item; - - return ( - - - {id ? translate('EditImportListExclusion') : translate('AddImportListExclusion')} - - - - { - isFetching && - - } - - { - !isFetching && !!error && - - {translate('AddImportListExclusionError')} - - } - - { - !isFetching && !error && -
- - {translate('TMDBId')} - - - - - - {translate('MovieTitle')} - - - - - - {translate('MovieYear')} - - - - -
- } -
- - - { - id && - - } - - - - - {translate('Save')} - - -
- ); -} - -EditImportListExclusionModalContent.propTypes = { - id: PropTypes.number, - isFetching: PropTypes.bool.isRequired, - error: PropTypes.object, - isSaving: PropTypes.bool.isRequired, - saveError: PropTypes.object, - item: PropTypes.object.isRequired, - onInputChange: PropTypes.func.isRequired, - onSavePress: PropTypes.func.isRequired, - onModalClose: PropTypes.func.isRequired, - onDeleteImportListExclusionPress: PropTypes.func -}; - -export default EditImportListExclusionModalContent; diff --git a/frontend/src/Settings/ImportLists/ImportListExclusions/EditImportListExclusionModalContent.tsx b/frontend/src/Settings/ImportLists/ImportListExclusions/EditImportListExclusionModalContent.tsx new file mode 100644 index 000000000..d1aa0852d --- /dev/null +++ b/frontend/src/Settings/ImportLists/ImportListExclusions/EditImportListExclusionModalContent.tsx @@ -0,0 +1,201 @@ +import React, { useCallback, useEffect } from 'react'; +import { useDispatch, useSelector } from 'react-redux'; +import { createSelector } from 'reselect'; +import AppState from 'App/State/AppState'; +import Alert from 'Components/Alert'; +import Form from 'Components/Form/Form'; +import FormGroup from 'Components/Form/FormGroup'; +import FormInputGroup from 'Components/Form/FormInputGroup'; +import FormLabel from 'Components/Form/FormLabel'; +import Button from 'Components/Link/Button'; +import SpinnerErrorButton from 'Components/Link/SpinnerErrorButton'; +import LoadingIndicator from 'Components/Loading/LoadingIndicator'; +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 usePrevious from 'Helpers/Hooks/usePrevious'; +import { inputTypes, kinds } from 'Helpers/Props'; +import { + saveImportListExclusion, + setImportListExclusionValue, +} from 'Store/Actions/settingsActions'; +import selectSettings from 'Store/Selectors/selectSettings'; +import ImportListExclusion from 'typings/ImportListExclusion'; +import { PendingSection } from 'typings/pending'; +import translate from 'Utilities/String/translate'; +import styles from './EditImportListExclusionModalContent.css'; + +const newImportListExclusion = { + movieTitle: '', + movieYear: 0, + tmdbId: 0, +}; + +interface EditImportListExclusionModalContentProps { + id?: number; + onModalClose: () => void; + onDeleteImportListExclusionPress?: () => void; +} + +function createImportListExclusionSelector(id?: number) { + return createSelector( + (state: AppState) => state.settings.importListExclusions, + (importListExclusions) => { + const { isFetching, error, isSaving, saveError, pendingChanges, items } = + importListExclusions; + + const mapping = id + ? items.find((i) => i.id === id) + : newImportListExclusion; + const settings = selectSettings(mapping, pendingChanges, saveError); + + return { + id, + isFetching, + error, + isSaving, + saveError, + item: settings.settings as PendingSection, + ...settings, + }; + } + ); +} + +function EditImportListExclusionModalContent( + props: EditImportListExclusionModalContentProps +) { + const { id, onModalClose, onDeleteImportListExclusionPress } = props; + + const dispatch = useDispatch(); + + const dispatchSetImportListExclusionValue = (payload: { + name: string; + value: string | number; + }) => { + // @ts-expect-error 'setImportListExclusionValue' isn't typed yet + dispatch(setImportListExclusionValue(payload)); + }; + + const { isFetching, isSaving, item, error, saveError, ...otherProps } = + useSelector(createImportListExclusionSelector(props.id)); + const previousIsSaving = usePrevious(isSaving); + + const { movieTitle, movieYear, tmdbId } = item; + + useEffect(() => { + if (!id) { + Object.keys(newImportListExclusion).forEach((name) => { + dispatchSetImportListExclusionValue({ + name, + value: + newImportListExclusion[name as keyof typeof newImportListExclusion], + }); + }); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + useEffect(() => { + if (previousIsSaving && !isSaving && !saveError) { + onModalClose(); + } + }); + + const onSavePress = useCallback(() => { + dispatch(saveImportListExclusion({ id })); + }, [dispatch, id]); + + const onInputChange = useCallback( + (payload: { name: string; value: string | number }) => { + // @ts-expect-error 'setImportListExclusionValue' isn't typed yet + dispatch(setImportListExclusionValue(payload)); + }, + [dispatch] + ); + + return ( + + + {id + ? translate('EditImportListExclusion') + : translate('AddImportListExclusion')} + + + + {isFetching && } + + {!isFetching && !!error && ( + + {translate('AddImportListExclusionError')} + + )} + + {!isFetching && !error && ( +
+ + {translate('TMDBId')} + + + + + + {translate('Title')} + + + + + + {translate('Year')} + + + +
+ )} +
+ + + {id && ( + + )} + + + + + {translate('Save')} + + +
+ ); +} + +export default EditImportListExclusionModalContent; diff --git a/frontend/src/Settings/ImportLists/ImportListExclusions/EditImportListExclusionModalContentConnector.js b/frontend/src/Settings/ImportLists/ImportListExclusions/EditImportListExclusionModalContentConnector.js deleted file mode 100644 index 0e86d98fe..000000000 --- a/frontend/src/Settings/ImportLists/ImportListExclusions/EditImportListExclusionModalContentConnector.js +++ /dev/null @@ -1,118 +0,0 @@ -import PropTypes from 'prop-types'; -import React, { Component } from 'react'; -import { connect } from 'react-redux'; -import { createSelector } from 'reselect'; -import { saveImportListExclusion, setImportListExclusionValue } from 'Store/Actions/settingsActions'; -import selectSettings from 'Store/Selectors/selectSettings'; -import EditImportListExclusionModalContent from './EditImportListExclusionModalContent'; - -const newImportListExclusion = { - movieTitle: '', - tmdbId: 0, - movieYear: 0 -}; - -function createImportListExclusionSelector() { - return createSelector( - (state, { id }) => id, - (state) => state.settings.importListExclusions, - (id, importListExclusions) => { - const { - isFetching, - error, - isSaving, - saveError, - pendingChanges, - items - } = importListExclusions; - - const mapping = id ? items.find((i) => i.id === id) : newImportListExclusion; - const settings = selectSettings(mapping, pendingChanges, saveError); - - return { - id, - isFetching, - error, - isSaving, - saveError, - item: settings.settings, - ...settings - }; - } - ); -} - -function createMapStateToProps() { - return createSelector( - createImportListExclusionSelector(), - (importListExclusion) => { - return { - ...importListExclusion - }; - } - ); -} - -const mapDispatchToProps = { - setImportListExclusionValue, - saveImportListExclusion -}; - -class EditImportListExclusionModalContentConnector extends Component { - - // - // Lifecycle - - componentDidMount() { - if (!this.props.id) { - Object.keys(newImportListExclusion).forEach((name) => { - this.props.setImportListExclusionValue({ - name, - value: newImportListExclusion[name] - }); - }); - } - } - - componentDidUpdate(prevProps, prevState) { - if (prevProps.isSaving && !this.props.isSaving && !this.props.saveError) { - this.props.onModalClose(); - } - } - - // - // Listeners - - onInputChange = ({ name, value }) => { - this.props.setImportListExclusionValue({ name, value }); - }; - - onSavePress = () => { - this.props.saveImportListExclusion({ id: this.props.id }); - }; - - // - // Render - - render() { - return ( - - ); - } -} - -EditImportListExclusionModalContentConnector.propTypes = { - id: PropTypes.number, - isSaving: PropTypes.bool.isRequired, - saveError: PropTypes.object, - item: PropTypes.object.isRequired, - setImportListExclusionValue: PropTypes.func.isRequired, - saveImportListExclusion: PropTypes.func.isRequired, - onModalClose: PropTypes.func.isRequired -}; - -export default connect(createMapStateToProps, mapDispatchToProps)(EditImportListExclusionModalContentConnector); diff --git a/frontend/src/Settings/ImportLists/ImportListExclusions/ImportListExclusion.css b/frontend/src/Settings/ImportLists/ImportListExclusions/ImportListExclusion.css deleted file mode 100644 index 571abb59b..000000000 --- a/frontend/src/Settings/ImportLists/ImportListExclusions/ImportListExclusion.css +++ /dev/null @@ -1,26 +0,0 @@ -.importListExclusion { - display: flex; - align-items: stretch; - margin-bottom: 10px; - height: 30px; - border-bottom: 1px solid var(--borderColor); - line-height: 30px; -} - -.movieTitle { - @add-mixin truncate; - - flex: 0 1 600px; -} - -.tmdbId, -.movieYear { - flex: 0 0 70px; -} - -.actions { - display: flex; - justify-content: flex-end; - flex: 1 0 auto; - padding-right: 10px; -} diff --git a/frontend/src/Settings/ImportLists/ImportListExclusions/ImportListExclusion.js b/frontend/src/Settings/ImportLists/ImportListExclusions/ImportListExclusion.js deleted file mode 100644 index 632258cf2..000000000 --- a/frontend/src/Settings/ImportLists/ImportListExclusions/ImportListExclusion.js +++ /dev/null @@ -1,115 +0,0 @@ -import classNames from 'classnames'; -import PropTypes from 'prop-types'; -import React, { Component } from 'react'; -import Icon from 'Components/Icon'; -import Link from 'Components/Link/Link'; -import ConfirmModal from 'Components/Modal/ConfirmModal'; -import { icons, kinds } from 'Helpers/Props'; -import translate from 'Utilities/String/translate'; -import EditImportListExclusionModalConnector from './EditImportListExclusionModalConnector'; -import styles from './ImportListExclusion.css'; - -class ImportListExclusion extends Component { - - // - // Lifecycle - - constructor(props, context) { - super(props, context); - - this.state = { - isEditImportListExclusionModalOpen: false, - isDeleteImportListExclusionModalOpen: false - }; - } - - // - // Listeners - - onEditImportListExclusionPress = () => { - this.setState({ isEditImportListExclusionModalOpen: true }); - }; - - onEditImportListExclusionModalClose = () => { - this.setState({ isEditImportListExclusionModalOpen: false }); - }; - - onDeleteImportListExclusionPress = () => { - this.setState({ - isEditImportListExclusionModalOpen: false, - isDeleteImportListExclusionModalOpen: true - }); - }; - - onDeleteImportListExclusionModalClose = () => { - this.setState({ isDeleteImportListExclusionModalOpen: false }); - }; - - onConfirmDeleteImportListExclusion = () => { - this.props.onConfirmDeleteImportListExclusion(this.props.id); - }; - - // - // Render - - render() { - const { - id, - movieTitle, - tmdbId, - movieYear - } = this.props; - - return ( -
-
{tmdbId}
-
{movieTitle}
-
{movieYear}
- -
- - - -
- - - - -
- ); - } -} - -ImportListExclusion.propTypes = { - id: PropTypes.number.isRequired, - movieTitle: PropTypes.string.isRequired, - tmdbId: PropTypes.number.isRequired, - movieYear: PropTypes.number.isRequired, - onConfirmDeleteImportListExclusion: PropTypes.func.isRequired -}; - -ImportListExclusion.defaultProps = { - // The drag preview will not connect the drag handle. - connectDragSource: (node) => node -}; - -export default ImportListExclusion; diff --git a/frontend/src/Settings/ImportLists/ImportListExclusions/ImportListExclusionRow.css b/frontend/src/Settings/ImportLists/ImportListExclusions/ImportListExclusionRow.css new file mode 100644 index 000000000..c154fa5a3 --- /dev/null +++ b/frontend/src/Settings/ImportLists/ImportListExclusions/ImportListExclusionRow.css @@ -0,0 +1,6 @@ +.actions { + composes: cell from '~Components/Table/Cells/TableRowCell.css'; + + width: 35px; + white-space: nowrap; +} diff --git a/frontend/src/Settings/ImportLists/ImportListExclusions/ImportListExclusion.css.d.ts b/frontend/src/Settings/ImportLists/ImportListExclusions/ImportListExclusionRow.css.d.ts similarity index 65% rename from frontend/src/Settings/ImportLists/ImportListExclusions/ImportListExclusion.css.d.ts rename to frontend/src/Settings/ImportLists/ImportListExclusions/ImportListExclusionRow.css.d.ts index 7a3245d59..d8ea83dc1 100644 --- a/frontend/src/Settings/ImportLists/ImportListExclusions/ImportListExclusion.css.d.ts +++ b/frontend/src/Settings/ImportLists/ImportListExclusions/ImportListExclusionRow.css.d.ts @@ -2,10 +2,6 @@ // Please do not change this file! interface CssExports { 'actions': string; - 'importListExclusion': string; - 'movieTitle': string; - 'movieYear': string; - 'tmdbId': string; } export const cssExports: CssExports; export default cssExports; diff --git a/frontend/src/Settings/ImportLists/ImportListExclusions/ImportListExclusionRow.tsx b/frontend/src/Settings/ImportLists/ImportListExclusions/ImportListExclusionRow.tsx new file mode 100644 index 000000000..a03fcaa8f --- /dev/null +++ b/frontend/src/Settings/ImportLists/ImportListExclusions/ImportListExclusionRow.tsx @@ -0,0 +1,83 @@ +import React, { useCallback } from 'react'; +import { useDispatch } from 'react-redux'; +import IconButton from 'Components/Link/IconButton'; +import ConfirmModal from 'Components/Modal/ConfirmModal'; +import TableRowCell from 'Components/Table/Cells/TableRowCell'; +import TableSelectCell from 'Components/Table/Cells/TableSelectCell'; +import TableRow from 'Components/Table/TableRow'; +import useModalOpenState from 'Helpers/Hooks/useModalOpenState'; +import { icons, kinds } from 'Helpers/Props'; +import { deleteImportListExclusion } from 'Store/Actions/Settings/importListExclusions'; +import ImportListExclusion from 'typings/ImportListExclusion'; +import { SelectStateInputProps } from 'typings/props'; +import translate from 'Utilities/String/translate'; +import EditImportListExclusionModal from './EditImportListExclusionModal'; +import styles from './ImportListExclusionRow.css'; + +interface ImportListExclusionRowProps extends ImportListExclusion { + isSelected: boolean; + onSelectedChange: (options: SelectStateInputProps) => void; +} + +function ImportListExclusionRow(props: ImportListExclusionRowProps) { + const { id, tmdbId, movieTitle, movieYear, isSelected, onSelectedChange } = + props; + + const dispatch = useDispatch(); + + const [ + isEditImportListExclusionModalOpen, + setEditImportListExclusionModalOpen, + setEditImportListExclusionModalClosed, + ] = useModalOpenState(false); + + const [ + isDeleteImportListExclusionModalOpen, + setDeleteImportListExclusionModalOpen, + setDeleteImportListExclusionModalClosed, + ] = useModalOpenState(false); + + const handleDeletePress = useCallback(() => { + dispatch(deleteImportListExclusion({ id })); + }, [id, dispatch]); + + return ( + + + + {tmdbId} + {movieTitle} + {movieYear} + + + + + + + + + + ); +} + +export default ImportListExclusionRow; diff --git a/frontend/src/Settings/ImportLists/ImportListExclusions/ImportListExclusions.css b/frontend/src/Settings/ImportLists/ImportListExclusions/ImportListExclusions.css index 8c397be9c..e213a1c11 100644 --- a/frontend/src/Settings/ImportLists/ImportListExclusions/ImportListExclusions.css +++ b/frontend/src/Settings/ImportLists/ImportListExclusions/ImportListExclusions.css @@ -1,24 +1,6 @@ -.importListExclusionsHeader { - display: flex; - margin-bottom: 10px; - font-weight: bold; -} - -.title { - flex: 0 1 600px; -} - -.tmdbId, -.movieYear { - flex: 0 0 70px; -} - -.addImportListExclusion { - display: flex; - justify-content: flex-end; - padding-right: 10px; -} +.actions { + composes: headerCell from '~Components/Table/TableHeaderCell.css'; -.addButton { - text-align: center; + width: 35px; + white-space: nowrap; } diff --git a/frontend/src/Settings/ImportLists/ImportListExclusions/ImportListExclusions.css.d.ts b/frontend/src/Settings/ImportLists/ImportListExclusions/ImportListExclusions.css.d.ts index 3603c0092..d8ea83dc1 100644 --- a/frontend/src/Settings/ImportLists/ImportListExclusions/ImportListExclusions.css.d.ts +++ b/frontend/src/Settings/ImportLists/ImportListExclusions/ImportListExclusions.css.d.ts @@ -1,12 +1,7 @@ // This file is automatically generated. // Please do not change this file! interface CssExports { - 'addButton': string; - 'addImportListExclusion': string; - 'importListExclusionsHeader': string; - 'movieYear': string; - 'title': string; - 'tmdbId': string; + 'actions': string; } export const cssExports: CssExports; export default cssExports; diff --git a/frontend/src/Settings/ImportLists/ImportListExclusions/ImportListExclusions.js b/frontend/src/Settings/ImportLists/ImportListExclusions/ImportListExclusions.js deleted file mode 100644 index 159981af4..000000000 --- a/frontend/src/Settings/ImportLists/ImportListExclusions/ImportListExclusions.js +++ /dev/null @@ -1,108 +0,0 @@ -import PropTypes from 'prop-types'; -import React, { Component } from 'react'; -import FieldSet from 'Components/FieldSet'; -import Icon from 'Components/Icon'; -import Link from 'Components/Link/Link'; -import PageSectionContent from 'Components/Page/PageSectionContent'; -import { icons } from 'Helpers/Props'; -import translate from 'Utilities/String/translate'; -import EditImportListExclusionModalConnector from './EditImportListExclusionModalConnector'; -import ImportListExclusion from './ImportListExclusion'; -import styles from './ImportListExclusions.css'; - -class ImportListExclusions extends Component { - - // - // Lifecycle - - constructor(props, context) { - super(props, context); - - this.state = { - isAddImportListExclusionModalOpen: false - }; - } - - // - // Listeners - - onAddImportListExclusionPress = () => { - this.setState({ isAddImportListExclusionModalOpen: true }); - }; - - onModalClose = () => { - this.setState({ isAddImportListExclusionModalOpen: false }); - }; - - // - // Render - - render() { - const { - items, - onConfirmDeleteImportListExclusion, - ...otherProps - } = this.props; - - return ( -
- -
-
- {translate('TMDBId')} -
-
- {translate('Title')} -
-
- {translate('Year')} -
-
- -
- { - items.map((item, index) => { - return ( - - ); - }) - } -
- -
- - - -
- - - -
-
- ); - } -} - -ImportListExclusions.propTypes = { - isFetching: PropTypes.bool.isRequired, - error: PropTypes.object, - items: PropTypes.arrayOf(PropTypes.object).isRequired, - onConfirmDeleteImportListExclusion: PropTypes.func.isRequired -}; - -export default ImportListExclusions; diff --git a/frontend/src/Settings/ImportLists/ImportListExclusions/ImportListExclusions.tsx b/frontend/src/Settings/ImportLists/ImportListExclusions/ImportListExclusions.tsx new file mode 100644 index 000000000..dc5545e59 --- /dev/null +++ b/frontend/src/Settings/ImportLists/ImportListExclusions/ImportListExclusions.tsx @@ -0,0 +1,309 @@ +import React, { useCallback, useEffect, useMemo, useState } from 'react'; +import { useDispatch, useSelector } from 'react-redux'; +import { createSelector } from 'reselect'; +import AppState from 'App/State/AppState'; +import FieldSet from 'Components/FieldSet'; +import IconButton from 'Components/Link/IconButton'; +import SpinnerButton from 'Components/Link/SpinnerButton'; +import ConfirmModal from 'Components/Modal/ConfirmModal'; +import PageSectionContent from 'Components/Page/PageSectionContent'; +import TableRowCell from 'Components/Table/Cells/TableRowCell'; +import Column from 'Components/Table/Column'; +import Table from 'Components/Table/Table'; +import TableBody from 'Components/Table/TableBody'; +import TablePager from 'Components/Table/TablePager'; +import TableRow from 'Components/Table/TableRow'; +import usePaging from 'Components/Table/usePaging'; +import useCurrentPage from 'Helpers/Hooks/useCurrentPage'; +import useModalOpenState from 'Helpers/Hooks/useModalOpenState'; +import usePrevious from 'Helpers/Hooks/usePrevious'; +import useSelectState from 'Helpers/Hooks/useSelectState'; +import { icons, kinds } from 'Helpers/Props'; +import { + bulkDeleteImportListExclusions, + clearImportListExclusions, + fetchImportListExclusions, + gotoImportListExclusionPage, + setImportListExclusionSort, + setImportListExclusionTableOption, +} from 'Store/Actions/Settings/importListExclusions'; +import { CheckInputChanged } from 'typings/inputs'; +import { SelectStateInputProps } from 'typings/props'; +import { TableOptionsChangePayload } from 'typings/Table'; +import { + registerPagePopulator, + unregisterPagePopulator, +} from 'Utilities/pagePopulator'; +import translate from 'Utilities/String/translate'; +import getSelectedIds from 'Utilities/Table/getSelectedIds'; +import EditImportListExclusionModal from './EditImportListExclusionModal'; +import ImportListExclusionRow from './ImportListExclusionRow'; +import styles from './ImportListExclusions.css'; + +const COLUMNS: Column[] = [ + { + name: 'tmdbid', + label: () => translate('TMDBId'), + isVisible: true, + isSortable: true, + }, + { + name: 'movieTitle', + label: () => translate('Title'), + isVisible: true, + isSortable: true, + }, + { + name: 'movieYear', + label: () => translate('Year'), + isVisible: true, + isSortable: true, + }, + { + className: styles.actions, + name: 'actions', + label: '', + isVisible: true, + isSortable: false, + }, +]; + +function createImportListExclusionsSelector() { + return createSelector( + (state: AppState) => state.settings.importListExclusions, + (importListExclusions) => { + return { + ...importListExclusions, + }; + } + ); +} + +function ImportListExclusions() { + const requestCurrentPage = useCurrentPage(); + + const { + isFetching, + isPopulated, + items, + pageSize, + sortKey, + error, + sortDirection, + page, + totalPages, + totalRecords, + isDeleting, + deleteError, + } = useSelector(createImportListExclusionsSelector()); + + const dispatch = useDispatch(); + + const [isConfirmDeleteModalOpen, setIsConfirmDeleteModalOpen] = + useState(false); + const previousIsDeleting = usePrevious(isDeleting); + + const [selectState, setSelectState] = useSelectState(); + const { allSelected, allUnselected, selectedState } = selectState; + + const selectedIds = useMemo(() => { + return getSelectedIds(selectedState); + }, [selectedState]); + + const handleSelectAllChange = useCallback( + ({ value }: CheckInputChanged) => { + setSelectState({ type: value ? 'selectAll' : 'unselectAll', items }); + }, + [items, setSelectState] + ); + + const handleSelectedChange = useCallback( + ({ id, value, shiftKey = false }: SelectStateInputProps) => { + setSelectState({ + type: 'toggleSelected', + items, + id, + isSelected: value, + shiftKey, + }); + }, + [items, setSelectState] + ); + + const handleDeleteSelectedPress = useCallback(() => { + setIsConfirmDeleteModalOpen(true); + }, [setIsConfirmDeleteModalOpen]); + + const handleDeleteSelectedConfirmed = useCallback(() => { + dispatch(bulkDeleteImportListExclusions({ ids: selectedIds })); + setIsConfirmDeleteModalOpen(false); + }, [selectedIds, setIsConfirmDeleteModalOpen, dispatch]); + + const handleConfirmDeleteModalClose = useCallback(() => { + setIsConfirmDeleteModalOpen(false); + }, [setIsConfirmDeleteModalOpen]); + + const { + handleFirstPagePress, + handlePreviousPagePress, + handleNextPagePress, + handleLastPagePress, + handlePageSelect, + } = usePaging({ + page, + totalPages, + gotoPage: gotoImportListExclusionPage, + }); + + const handleSortPress = useCallback( + (sortKey: { sortKey: string }) => { + dispatch(setImportListExclusionSort({ sortKey })); + }, + [dispatch] + ); + + const handleTableOptionChange = useCallback( + (payload: TableOptionsChangePayload) => { + dispatch(setImportListExclusionTableOption(payload)); + + if (payload.pageSize) { + dispatch(gotoImportListExclusionPage({ page: 1 })); + } + }, + [dispatch] + ); + + useEffect(() => { + if (requestCurrentPage) { + dispatch(fetchImportListExclusions()); + } else { + dispatch(gotoImportListExclusionPage({ page: 1 })); + } + + return () => { + dispatch(clearImportListExclusions()); + }; + }, [requestCurrentPage, dispatch]); + + useEffect(() => { + const repopulate = () => { + dispatch(fetchImportListExclusions()); + }; + + registerPagePopulator(repopulate); + + return () => { + unregisterPagePopulator(repopulate); + }; + }, [dispatch]); + + useEffect(() => { + if (previousIsDeleting && !isDeleting && !deleteError) { + setSelectState({ type: 'unselectAll', items }); + + dispatch(fetchImportListExclusions()); + } + }, [ + previousIsDeleting, + isDeleting, + deleteError, + items, + dispatch, + setSelectState, + ]); + + const [ + isAddImportListExclusionModalOpen, + setAddImportListExclusionModalOpen, + setAddImportListExclusionModalClosed, + ] = useModalOpenState(false); + + const isFetchingForFirstTime = isFetching && !isPopulated; + + return ( +
+ + + + {items.map((item) => { + return ( + + ); + })} + + + + + {translate('Delete')} + + + + + + + + +
+ + + + + + +
+
+ ); +} + +export default ImportListExclusions; diff --git a/frontend/src/Settings/ImportLists/ImportListExclusions/ImportListExclusionsConnector.js b/frontend/src/Settings/ImportLists/ImportListExclusions/ImportListExclusionsConnector.js deleted file mode 100644 index 184788cec..000000000 --- a/frontend/src/Settings/ImportLists/ImportListExclusions/ImportListExclusionsConnector.js +++ /dev/null @@ -1,59 +0,0 @@ -import PropTypes from 'prop-types'; -import React, { Component } from 'react'; -import { connect } from 'react-redux'; -import { createSelector } from 'reselect'; -import { deleteImportListExclusion, fetchImportListExclusions } from 'Store/Actions/settingsActions'; -import ImportListExclusions from './ImportListExclusions'; - -function createMapStateToProps() { - return createSelector( - (state) => state.settings.importListExclusions, - (importListExclusions) => { - return { - ...importListExclusions - }; - } - ); -} - -const mapDispatchToProps = { - fetchImportListExclusions, - deleteImportListExclusion -}; - -class ImportListExclusionsConnector extends Component { - - // - // Lifecycle - - componentDidMount() { - this.props.fetchImportListExclusions(); - } - - // - // Listeners - - onConfirmDeleteImportListExclusion = (id) => { - this.props.deleteImportListExclusion({ id }); - }; - - // - // Render - - render() { - return ( - - ); - } -} - -ImportListExclusionsConnector.propTypes = { - fetchImportListExclusions: PropTypes.func.isRequired, - deleteImportListExclusion: PropTypes.func.isRequired -}; - -export default connect(createMapStateToProps, mapDispatchToProps)(ImportListExclusionsConnector); diff --git a/frontend/src/Settings/ImportLists/ImportListSettings.js b/frontend/src/Settings/ImportLists/ImportListSettings.js index f7dabfc6b..f2641f43c 100644 --- a/frontend/src/Settings/ImportLists/ImportListSettings.js +++ b/frontend/src/Settings/ImportLists/ImportListSettings.js @@ -7,7 +7,7 @@ import PageToolbarSeparator from 'Components/Page/Toolbar/PageToolbarSeparator'; import { icons } from 'Helpers/Props'; import SettingsToolbarConnector from 'Settings/SettingsToolbarConnector'; import translate from 'Utilities/String/translate'; -import ImportListExclusionsConnector from './ImportListExclusions/ImportListExclusionsConnector'; +import ImportListExclusions from './ImportListExclusions/ImportListExclusions'; import ImportListsConnector from './ImportLists/ImportListsConnector'; import ManageImportListsModal from './ImportLists/Manage/ManageImportListsModal'; import ImportListOptions from './Options/ImportListOptions'; @@ -103,7 +103,7 @@ class ImportListSettings extends Component { onChildStateChange={this.onChildStateChange} /> - + { return { section, @@ -45,27 +59,52 @@ export default { isFetching: false, isPopulated: false, error: null, + pageSize: 20, items: [], isSaving: false, saveError: null, + isDeleting: false, + deleteError: null, pendingChanges: {} }, // // Action Handlers - actionHandlers: { - [FETCH_IMPORT_LIST_EXCLUSIONS]: createFetchHandler(section, '/exclusions'), - + actionHandlers: handleThunks({ + ...createServerSideCollectionHandlers( + section, + '/exclusions/paged', + fetchImportListExclusions, + { + [serverSideCollectionHandlers.FETCH]: FETCH_IMPORT_LIST_EXCLUSIONS, + [serverSideCollectionHandlers.EXACT_PAGE]: GOTO_IMPORT_LIST_EXCLUSION_PAGE, + [serverSideCollectionHandlers.SORT]: SET_IMPORT_LIST_EXCLUSION_SORT + } + ), [SAVE_IMPORT_LIST_EXCLUSION]: createSaveProviderHandler(section, '/exclusions'), - [DELETE_IMPORT_LIST_EXCLUSION]: createRemoveItemHandler(section, '/exclusions') - }, + [DELETE_IMPORT_LIST_EXCLUSION]: createRemoveItemHandler(section, '/exclusions'), + [BULK_DELETE_IMPORT_LIST_EXCLUSIONS]: createBulkRemoveItemHandler(section, '/exclusions/bulk') + }), // // Reducers reducers: { - [SET_IMPORT_LIST_EXCLUSION_VALUE]: createSetSettingValueReducer(section) + [SET_IMPORT_LIST_EXCLUSION_VALUE]: createSetSettingValueReducer(section), + [SET_IMPORT_LIST_EXCLUSION_TABLE_OPTION]: createSetTableOptionReducer(section), + + [CLEAR_IMPORT_LIST_EXCLUSIONS]: createClearReducer(section, { + isFetching: false, + isPopulated: false, + error: null, + items: [], + isDeleting: false, + deleteError: null, + pendingChanges: {}, + totalPages: 0, + totalRecords: 0 + }) } }; diff --git a/frontend/src/Store/Actions/settingsActions.js b/frontend/src/Store/Actions/settingsActions.js index 72ac92a5d..f22abe809 100644 --- a/frontend/src/Store/Actions/settingsActions.js +++ b/frontend/src/Store/Actions/settingsActions.js @@ -94,7 +94,8 @@ export const defaultState = { }; export const persistState = [ - 'settings.advancedSettings' + 'settings.advancedSettings', + 'settings.importListExclusions.pageSize' ]; // diff --git a/frontend/src/Store/Selectors/createSettingsSectionSelector.ts b/frontend/src/Store/Selectors/createSettingsSectionSelector.ts index f43e4e59b..ad1e9cd6b 100644 --- a/frontend/src/Store/Selectors/createSettingsSectionSelector.ts +++ b/frontend/src/Store/Selectors/createSettingsSectionSelector.ts @@ -1,45 +1,44 @@ import { createSelector } from 'reselect'; -import AppSectionState, { - AppSectionItemState, -} from 'App/State/AppSectionState'; +import { AppSectionItemState } from 'App/State/AppSectionState'; import AppState from 'App/State/AppState'; +import SettingsAppState from 'App/State/SettingsAppState'; import selectSettings from 'Store/Selectors/selectSettings'; import { PendingSection } from 'typings/pending'; -type SettingNames = keyof Omit; -type GetSectionState = AppState['settings'][Name]; -type GetSettingsSectionItemType = - GetSectionState extends AppSectionItemState - ? R - : GetSectionState extends AppSectionState - ? R +type SectionsWithItemNames = { + [K in keyof SettingsAppState]: SettingsAppState[K] extends AppSectionItemState + ? K : never; +}[keyof SettingsAppState]; -type AppStateWithPending = { - item?: GetSettingsSectionItemType; - pendingChanges?: Partial>; - saveError?: Error; -} & GetSectionState; +type GetSectionState = + SettingsAppState[Name]; +type GetSettingsSectionItemType = + GetSectionState extends AppSectionItemState ? R : never; -function createSettingsSectionSelector( - section: Name -) { +function createSettingsSectionSelector< + Name extends SectionsWithItemNames, + T extends GetSettingsSectionItemType +>(section: Name) { return createSelector( (state: AppState) => state.settings[section], (sectionSettings) => { - const { item, pendingChanges, saveError, ...other } = - sectionSettings as AppStateWithPending; + const { item, pendingChanges, ...other } = sectionSettings; - const { settings, ...rest } = selectSettings( - item, - pendingChanges, - saveError - ); + const saveError = + 'saveError' in sectionSettings ? sectionSettings.saveError : undefined; + + const { + settings, + pendingChanges: selectedPendingChanges, + ...rest + } = selectSettings(item, pendingChanges, saveError); return { ...other, saveError, - settings: settings as PendingSection>, + settings: settings as PendingSection, + pendingChanges: selectedPendingChanges as Partial, ...rest, }; } diff --git a/frontend/src/typings/ImportListExclusion.ts b/frontend/src/typings/ImportListExclusion.ts new file mode 100644 index 000000000..5413de56e --- /dev/null +++ b/frontend/src/typings/ImportListExclusion.ts @@ -0,0 +1,7 @@ +import ModelBase from 'App/ModelBase'; + +export default interface ImportListExclusion extends ModelBase { + tmdbId: number; + movieTitle: string; + movieYear: number; +} diff --git a/frontend/src/typings/Table.ts b/frontend/src/typings/Table.ts new file mode 100644 index 000000000..4f99e2045 --- /dev/null +++ b/frontend/src/typings/Table.ts @@ -0,0 +1,6 @@ +import Column from 'Components/Table/Column'; + +export interface TableOptionsChangePayload { + pageSize?: number; + columns: Column[]; +} diff --git a/src/NzbDrone.Core/ImportLists/ImportExclusions/ImportListExclusionService.cs b/src/NzbDrone.Core/ImportLists/ImportExclusions/ImportListExclusionService.cs index 54781bf48..5251ddb5e 100644 --- a/src/NzbDrone.Core/ImportLists/ImportExclusions/ImportListExclusionService.cs +++ b/src/NzbDrone.Core/ImportLists/ImportExclusions/ImportListExclusionService.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using System.Linq; using NLog; +using NzbDrone.Core.Datastore; using NzbDrone.Core.Messaging.Events; using NzbDrone.Core.Movies.Events; @@ -11,6 +12,7 @@ namespace NzbDrone.Core.ImportLists.ImportExclusions ImportListExclusion Add(ImportListExclusion importListExclusion); List Add(List importListExclusions); List All(); + PagingSpec Paged(PagingSpec pagingSpec); bool IsMovieExcluded(int tmdbId); void Delete(int id); void Delete(List ids); @@ -78,6 +80,11 @@ namespace NzbDrone.Core.ImportLists.ImportExclusions return _repo.All().ToList(); } + public PagingSpec Paged(PagingSpec pagingSpec) + { + return _repo.GetPaged(pagingSpec); + } + public List AllExcludedTmdbIds() { return _repo.AllExcludedTmdbIds(); diff --git a/src/NzbDrone.Core/Localization/Core/ar.json b/src/NzbDrone.Core/Localization/Core/ar.json index 2bf10071b..2d98f89e6 100644 --- a/src/NzbDrone.Core/Localization/Core/ar.json +++ b/src/NzbDrone.Core/Localization/Core/ar.json @@ -111,7 +111,7 @@ "TorrentDelay": "تأخير السيل", "Tomorrow": "غدا", "Today": "اليوم", - "TmdbIdHelpText": "معرّف TMDb الخاص بالفيلم المراد استبعاده", + "TmdbIdExcludeHelpText": "معرّف TMDb الخاص بالفيلم المراد استبعاده", "TMDBId": "معرف TMDb", "TMDb": "TMDb", "Titles": "الألقاب", @@ -276,9 +276,9 @@ "MustNotContain": "يجب ألا يحتوي", "MustContain": "يجب أن يحتوي على", "MultiLanguage": "متعدد اللغات", - "MovieYearHelpText": "عام الفيلم المطلوب استبعاده", + "MovieYearToExcludeHelpText": "عام الفيلم المطلوب استبعاده", "MovieYear": "سنة الفيلم", - "MovieTitleHelpText": "عنوان الفيلم المراد استبعاده (يمكن أن يكون أي شيء ذي معنى)", + "MovieTitleToExcludeHelpText": "عنوان الفيلم المراد استبعاده (يمكن أن يكون أي شيء ذي معنى)", "MovieTitle": "عنوان الفيلم", "MoviesSelectedInterp": "تم تحديد {0} فيلم (أفلام)", "Movies": "أفلام", diff --git a/src/NzbDrone.Core/Localization/Core/bg.json b/src/NzbDrone.Core/Localization/Core/bg.json index 33697bf96..c5deb2852 100644 --- a/src/NzbDrone.Core/Localization/Core/bg.json +++ b/src/NzbDrone.Core/Localization/Core/bg.json @@ -572,7 +572,7 @@ "Title": "Заглавие", "Titles": "Заглавия", "TMDBId": "Идентификатор на TMDb", - "TmdbIdHelpText": "Идентификаторът на TMDb на филма за изключване", + "TmdbIdExcludeHelpText": "Идентификаторът на TMDb на филма за изключване", "Today": "Днес", "TorrentDelay": "Торент закъснение", "TorrentDelayHelpText": "Забавете за минути, за да изчакате, преди да вземете порой", @@ -883,9 +883,9 @@ "Movies": "Филми", "MoviesSelectedInterp": "{0} Избран / и филм / и", "MovieTitle": "Заглавие на филма", - "MovieTitleHelpText": "Заглавието на филма, който трябва да се изключи (може да бъде всичко смислено)", + "MovieTitleToExcludeHelpText": "Заглавието на филма, който трябва да се изключи (може да бъде всичко смислено)", "MovieYear": "Филмова година", - "MovieYearHelpText": "Годината на филма за изключване", + "MovieYearToExcludeHelpText": "Годината на филма за изключване", "MustContain": "Трябва да съдържа", "MustNotContain": "Не трябва да съдържа", "NamingSettings": "Настройки за именуване", diff --git a/src/NzbDrone.Core/Localization/Core/ca.json b/src/NzbDrone.Core/Localization/Core/ca.json index 6958af387..e74d06f06 100644 --- a/src/NzbDrone.Core/Localization/Core/ca.json +++ b/src/NzbDrone.Core/Localization/Core/ca.json @@ -99,9 +99,9 @@ "MovieFolderFormat": "Format de carpeta de pel·lícules", "MovieIndex": "Índex de pel·lícules", "MovieTitle": "Títol de la pel·lícula", - "MovieTitleHelpText": "El títol de la pel·lícula a excloure (pot ser qualsevol cosa amb sentit)", + "MovieTitleToExcludeHelpText": "El títol de la pel·lícula a excloure (pot ser qualsevol cosa amb sentit)", "MovieYear": "Any de la pel·lícula", - "MovieYearHelpText": "L'any de la pel·lícula a excloure", + "MovieYearToExcludeHelpText": "L'any de la pel·lícula a excloure", "NoAltTitle": "No hi ha títols alternatius.", "NoBackupsAreAvailable": "No hi ha còpies de seguretat disponibles", "NoChange": "Cap canvi", @@ -869,7 +869,7 @@ "Titles": "Títols", "TMDb": "TMDb", "TMDBId": "Identificador de TMDb", - "TmdbIdHelpText": "L'identificador de TMDb de la pel·lícula a excloure", + "TmdbIdExcludeHelpText": "L'identificador de TMDb de la pel·lícula a excloure", "TmdbVotes": "Vots de TMDb", "Today": "Avui", "Tomorrow": "Demà", diff --git a/src/NzbDrone.Core/Localization/Core/cs.json b/src/NzbDrone.Core/Localization/Core/cs.json index 6850c6594..8345f346c 100644 --- a/src/NzbDrone.Core/Localization/Core/cs.json +++ b/src/NzbDrone.Core/Localization/Core/cs.json @@ -376,9 +376,9 @@ "Movies": "Filmy", "MoviesSelectedInterp": "{0} Vybrané filmy", "MovieTitle": "Název filmu", - "MovieTitleHelpText": "Název filmu, který se má vyloučit (může mít cokoli smysluplného)", + "MovieTitleToExcludeHelpText": "Název filmu, který se má vyloučit (může mít cokoli smysluplného)", "MovieYear": "Filmový rok", - "MovieYearHelpText": "Rok filmu, který se má vyloučit", + "MovieYearToExcludeHelpText": "Rok filmu, který se má vyloučit", "MustContain": "Musí obsahovat", "MustNotContain": "Nesmí obsahovat", "NamingSettings": "Nastavení pojmenování", @@ -865,7 +865,7 @@ "Title": "Titul", "Titles": "Tituly", "TMDBId": "ID TMDb", - "TmdbIdHelpText": "ID TMDb filmu, které se má vyloučit", + "TmdbIdExcludeHelpText": "ID TMDb filmu, které se má vyloučit", "Today": "Dnes", "TorrentDelay": "Torrent Delay", "TorrentDelayHelpText": "Zpoždění v minutách čekání před popadnutím torrentu", diff --git a/src/NzbDrone.Core/Localization/Core/da.json b/src/NzbDrone.Core/Localization/Core/da.json index 92b0fd629..10bcb18d3 100644 --- a/src/NzbDrone.Core/Localization/Core/da.json +++ b/src/NzbDrone.Core/Localization/Core/da.json @@ -430,9 +430,9 @@ "MovieTitle": "Filmtitel", "EditDelayProfile": "Rediger forsinkelsesprofil", "Name": "Navn", - "MovieTitleHelpText": "Titlen på den film, der skal ekskluderes (kan være noget meningsfuldt)", + "MovieTitleToExcludeHelpText": "Titlen på den film, der skal ekskluderes (kan være noget meningsfuldt)", "MovieYear": "Filmår", - "MovieYearHelpText": "Året for filmen at udelukke", + "MovieYearToExcludeHelpText": "Året for filmen at udelukke", "MustContain": "Skal indeholde", "MustNotContain": "Må ikke indeholde", "NamingSettings": "Navngivningsindstillinger", @@ -845,7 +845,7 @@ "Title": "Titel", "Titles": "Titler", "TMDBId": "TMDb Id", - "TmdbIdHelpText": "TMDb-id for filmen, der skal ekskluderes", + "TmdbIdExcludeHelpText": "TMDb-id for filmen, der skal ekskluderes", "Today": "I dag", "TorrentDelay": "Torrentforsinkelse", "TorrentDelayHelpText": "Forsink i minutter, før du tager fat i en torrent", diff --git a/src/NzbDrone.Core/Localization/Core/de.json b/src/NzbDrone.Core/Localization/Core/de.json index 6834a3441..ddeec4f4e 100644 --- a/src/NzbDrone.Core/Localization/Core/de.json +++ b/src/NzbDrone.Core/Localization/Core/de.json @@ -413,9 +413,9 @@ "MovieID": "Film ID", "MovieInfoLanguageHelpTextWarning": "Seite muss neugeladen werden", "MovieIsDownloading": "Film ist am herunterladen", - "MovieTitleHelpText": "Der Titel des Filmes der augeschlossen werden soll", + "MovieTitleToExcludeHelpText": "Der Titel des Filmes der augeschlossen werden soll", "MovieYear": "Erscheinungsjahr", - "MovieYearHelpText": "Das Erscheinungsjahr des Filmes der ausgeschlossen werden soll", + "MovieYearToExcludeHelpText": "Das Erscheinungsjahr des Filmes der ausgeschlossen werden soll", "MustContain": "Muss beinhalten", "MustNotContain": "Darf nicht beinhalten", "NamingSettings": "Bennenungs Einstellungen", @@ -503,7 +503,7 @@ "Uptime": "Betriebszeit", "UrlBase": "URL-Basis", "TMDBId": "TMDb ID", - "TmdbIdHelpText": "Die TMDb ID für den Ausschluss", + "TmdbIdExcludeHelpText": "Die TMDb ID für den Ausschluss", "TorrentDelay": "Torrent-Verzögerung", "TorrentDelayHelpText": "Verzögerung in Minuten, bevor ein Torrent herunterladen wird", "Torrents": "Torrents", diff --git a/src/NzbDrone.Core/Localization/Core/el.json b/src/NzbDrone.Core/Localization/Core/el.json index 21c73ee36..edd509b00 100644 --- a/src/NzbDrone.Core/Localization/Core/el.json +++ b/src/NzbDrone.Core/Localization/Core/el.json @@ -372,9 +372,9 @@ "MoviesSelectedInterp": "{0} Επιλεγμένες ταινίες", "MovieTitle": "Τίτλος ταινίας", "Level": "Επίπεδο", - "MovieTitleHelpText": "Ο τίτλος της ταινίας προς εξαίρεση (μπορεί να είναι οτιδήποτε νόημα)", + "MovieTitleToExcludeHelpText": "Ο τίτλος της ταινίας προς εξαίρεση (μπορεί να είναι οτιδήποτε νόημα)", "MovieYear": "Έτος ταινίας", - "MovieYearHelpText": "Το έτος της ταινίας που αποκλείεται", + "MovieYearToExcludeHelpText": "Το έτος της ταινίας που αποκλείεται", "MustContain": "Πρέπει να περιέχει", "MustNotContain": "Δεν πρέπει να περιέχει", "NamingSettings": "Ρυθμίσεις ονομάτων", @@ -846,7 +846,7 @@ "Time": "χρόνος", "Title": "Τίτλος", "Titles": "Τίτλοι", - "TmdbIdHelpText": "Το αναγνωριστικό TMDb της ταινίας για εξαίρεση", + "TmdbIdExcludeHelpText": "Το αναγνωριστικό TMDb της ταινίας για εξαίρεση", "Today": "Σήμερα", "TorrentDelay": "Καθυστέρηση Torrent", "TorrentDelayHelpText": "Καθυστέρηση σε λίγα λεπτά για να περιμένετε πριν πιάσετε ένα χείμαρρο", diff --git a/src/NzbDrone.Core/Localization/Core/en.json b/src/NzbDrone.Core/Localization/Core/en.json index 3b4f83551..cbf7530f1 100644 --- a/src/NzbDrone.Core/Localization/Core/en.json +++ b/src/NzbDrone.Core/Localization/Core/en.json @@ -336,8 +336,10 @@ "DeleteRestrictionHelpText": "Are you sure you want to delete this restriction?", "DeleteRootFolder": "Delete Root Folder", "DeleteRootFolderMessageText": "Are you sure you want to delete the root folder '{path}'?", + "DeleteSelected": "Delete Selected", "DeleteSelectedDownloadClients": "Delete Download Client(s)", "DeleteSelectedDownloadClientsMessageText": "Are you sure you want to delete {count} selected download client(s)?", + "DeleteSelectedImportListExclusionsMessageText": "Are you sure you want to delete the selected import list exclusions?", "DeleteSelectedImportLists": "Delete Import List(s)", "DeleteSelectedImportListsMessageText": "Are you sure you want to delete {count} selected import list(s)?", "DeleteSelectedIndexers": "Delete Indexer(s)", @@ -975,9 +977,9 @@ "MovieOnly": "Movie Only", "MovieSearchResultsLoadError": "Unable to load results for this movie search. Try again later", "MovieTitle": "Movie Title", - "MovieTitleHelpText": "The title of the movie to exclude (can be anything meaningful)", + "MovieTitleToExcludeHelpText": "The title of the movie to exclude (can be anything meaningful)", "MovieYear": "Movie Year", - "MovieYearHelpText": "The year of the movie to exclude", + "MovieYearToExcludeHelpText": "The year of the movie to exclude", "Movies": "Movies", "MoviesSelectedInterp": "{count} Movie(s) Selected", "MultiLanguage": "Multi-Language", @@ -1674,7 +1676,7 @@ "Timeleft": "Time Left", "Title": "Title", "Titles": "Titles", - "TmdbIdHelpText": "The TMDb Id of the movie to exclude", + "TmdbIdExcludeHelpText": "The TMDb Id of the movie to exclude", "TmdbRating": "TMDb Rating", "TmdbVotes": "TMDb Votes", "Today": "Today", diff --git a/src/NzbDrone.Core/Localization/Core/es.json b/src/NzbDrone.Core/Localization/Core/es.json index d7cd9138b..6ac142f3e 100644 --- a/src/NzbDrone.Core/Localization/Core/es.json +++ b/src/NzbDrone.Core/Localization/Core/es.json @@ -408,7 +408,7 @@ "Torrents": "Torrents", "TorrentDelayHelpText": "Retraso en minutos a esperar antes de capturar un torrent", "TorrentDelay": "Retraso de torrent", - "TmdbIdHelpText": "La Id de TMDb de la película a exluir", + "TmdbIdExcludeHelpText": "La Id de TMDb de la película a exluir", "TMDBId": "TMDb Id", "TestAllLists": "Probar todas las listas", "TestAllIndexers": "Probar todos los indexadores", @@ -507,9 +507,9 @@ "NamingSettings": "Opciones de nombrado", "MustNotContain": "No debe contener", "MustContain": "Debe contener", - "MovieYearHelpText": "Año de la película a excluir", + "MovieYearToExcludeHelpText": "Año de la película a excluir", "MovieYear": "Año de la Película", - "MovieTitleHelpText": "Ttítulo de la película a excluir (puedes ser cualquier cosa)", + "MovieTitleToExcludeHelpText": "Ttítulo de la película a excluir (puedes ser cualquier cosa)", "MovieIsDownloading": "Le película está descargando", "MovieInfoLanguageHelpTextWarning": "Recargar el Navegador", "MovieID": "ID de Película", diff --git a/src/NzbDrone.Core/Localization/Core/fi.json b/src/NzbDrone.Core/Localization/Core/fi.json index 2ade660ad..238580b09 100644 --- a/src/NzbDrone.Core/Localization/Core/fi.json +++ b/src/NzbDrone.Core/Localization/Core/fi.json @@ -320,7 +320,7 @@ "RelativePath": "Suhteellinen sijainti", "ReleaseRejected": "Vapautus hylätty", "MovieTitle": "Elokuvan nimi", - "MovieTitleHelpText": "Poissuljettava elokuvan nimi (voi olla mikä tahansa mielekäs)", + "MovieTitleToExcludeHelpText": "Poissuljettava elokuvan nimi (voi olla mikä tahansa mielekäs)", "MovieYear": "Elokuvan vuosi", "TestAllClients": "Lataustyökalujen testaus", "TestAllIndexers": "Tietolähteiden testaus", @@ -328,7 +328,7 @@ "AddRemotePathMapping": "Lisää etäsijainnin kohdistus", "Apply": "Käytä", "Analytics": "Analytiikka", - "MovieYearHelpText": "Elokuvan vuosi, joka suljetaan pois", + "MovieYearToExcludeHelpText": "Elokuvan vuosi, joka suljetaan pois", "Date": "Päiväys", "MustContain": "Täytyy sisältää", "MustNotContain": "Ei voi sisältää", @@ -849,7 +849,7 @@ "Title": "Nimike", "Titles": "Nimikkeet", "TMDBId": "TMDB ID", - "TmdbIdHelpText": "Ohitettavan elokuvan TMDB ID.", + "TmdbIdExcludeHelpText": "Ohitettavan elokuvan TMDB ID.", "Today": "Tänään", "TorrentDelay": "Torrent-viive", "TorrentDelayHelpText": "Minuuttiviive, joka odotetaan ennen julkaisun Torrent-kaappausta.", diff --git a/src/NzbDrone.Core/Localization/Core/fr.json b/src/NzbDrone.Core/Localization/Core/fr.json index 24af3896a..c9d338008 100644 --- a/src/NzbDrone.Core/Localization/Core/fr.json +++ b/src/NzbDrone.Core/Localization/Core/fr.json @@ -590,7 +590,7 @@ "UiLanguage": "Langue de l'IU", "TotalFileSize": "Taille totale des fichiers", "Torrents": "Torrents", - "TmdbIdHelpText": "L'ID TMDb du film à exclure", + "TmdbIdExcludeHelpText": "L'ID TMDb du film à exclure", "TMDBId": "Identifiant TMDb", "TestAllLists": "Tester toutes les listes", "TestAllIndexers": "Testez tous les indexeurs", @@ -718,10 +718,10 @@ "NetCore": ".NET", "NamingSettings": "Paramètres de dénomination", "MustNotContain": "Ne doit pas contenir", - "MovieYearHelpText": "L'année de film à exclure", + "MovieYearToExcludeHelpText": "L'année de film à exclure", "MustContain": "Doit contenir", "MovieYear": "Année du film", - "MovieTitleHelpText": "Le titre du film à exclure (peut être quelque chose de significatif)", + "MovieTitleToExcludeHelpText": "Le titre du film à exclure (peut être quelque chose de significatif)", "MovieIndexScrollTop": "Index des films : faire défiler vers le haut", "MovieIndexScrollBottom": "Index des films : faire défiler vers le bas", "MovieDetailsPreviousMovie": "Détails du film : Film Précédent", diff --git a/src/NzbDrone.Core/Localization/Core/he.json b/src/NzbDrone.Core/Localization/Core/he.json index d4df0fb7e..afb3b3353 100644 --- a/src/NzbDrone.Core/Localization/Core/he.json +++ b/src/NzbDrone.Core/Localization/Core/he.json @@ -9,7 +9,7 @@ "AvailabilityDelayHelpText": "משך הזמן לפני או אחרי התאריך הזמין לחיפוש הסרט", "BackupNow": "גיבוי עכשיו", "ChownGroupHelpText": "שם הקבוצה או ה- gid. השתמש ב- gid עבור מערכות קבצים מרוחקות.", - "MovieYearHelpText": "שנת הסרט לא הכללה", + "MovieYearToExcludeHelpText": "שנת הסרט לא הכללה", "ImportErrors": "ייבוא שגיאות", "IndexersSettingsSummary": "אינדקסים ומגבלות שחרור", "ReplaceWithDash": "החלף ב- Dash", @@ -84,7 +84,7 @@ "WhitelistedSubtitleTags": "תגיות כתוביות ברשימת ההיתרים", "MissingMonitoredAndConsideredAvailable": "חסר (מנוטר)", "ListTagsHelpText": "פריטי רשימת תגים יתווספו עם", - "MovieTitleHelpText": "כותרת הסרט לאי הכללה (יכולה להיות כל דבר בעל משמעות)", + "MovieTitleToExcludeHelpText": "כותרת הסרט לאי הכללה (יכולה להיות כל דבר בעל משמעות)", "MovieYear": "שנת הסרט", "DownloadPropersAndRepacksHelpTextCustomFormat": "השתמש ב'אל תעדיף 'כדי למיין לפי ציון פורמט מותאם אישית על פני Propers / Repacks", "AddListExclusionMovieHelpText": "מנע מלהוסיף סרט לרדאר על ידי רשימות", @@ -850,7 +850,7 @@ "Title": "כותרת", "Titles": "כותרות", "TMDBId": "מזהה TMDb", - "TmdbIdHelpText": "מזהה ה- TMDb של הסרט לא לכלול", + "TmdbIdExcludeHelpText": "מזהה ה- TMDb של הסרט לא לכלול", "Today": "היום", "TorrentDelay": "עיכוב סיקור", "TorrentDelayHelpText": "התעכב תוך דקות להמתין לפני שתופס סיקור", diff --git a/src/NzbDrone.Core/Localization/Core/hi.json b/src/NzbDrone.Core/Localization/Core/hi.json index 631774373..3800fb9e6 100644 --- a/src/NzbDrone.Core/Localization/Core/hi.json +++ b/src/NzbDrone.Core/Localization/Core/hi.json @@ -516,9 +516,9 @@ "Movies": "चलचित्र", "MoviesSelectedInterp": "{0} मूवी (s) चयनित", "MovieTitle": "चलचित्र शीर्षक", - "MovieTitleHelpText": "बहिष्कृत करने के लिए फिल्म का शीर्षक (सार्थक कुछ भी हो सकता है)", + "MovieTitleToExcludeHelpText": "बहिष्कृत करने के लिए फिल्म का शीर्षक (सार्थक कुछ भी हो सकता है)", "MovieYear": "मूवी वर्ष", - "MovieYearHelpText": "फिल्म को बाहर करने का वर्ष", + "MovieYearToExcludeHelpText": "फिल्म को बाहर करने का वर्ष", "MustContain": "शामिल होना चाहिए", "MustNotContain": "कंटेनर नहीं होना चाहिए", "NamingSettings": "नामकरण सेटिंग्स", @@ -888,7 +888,7 @@ "ThisCannotBeCancelled": "यह एक बार रद्द नहीं किया जा सकता है जब तक कि रेडर को फिर से शुरू किए बिना।", "Time": "समय", "TMDBId": "TMDb Id", - "TmdbIdHelpText": "बाहर करने के लिए फिल्म की TMDb Id", + "TmdbIdExcludeHelpText": "बाहर करने के लिए फिल्म की TMDb Id", "Today": "आज", "TorrentDelayTime": "धार विलंब: {0}", "Torrents": "टोरेंट", diff --git a/src/NzbDrone.Core/Localization/Core/hu.json b/src/NzbDrone.Core/Localization/Core/hu.json index 27cf6b12c..2520fc8e3 100644 --- a/src/NzbDrone.Core/Localization/Core/hu.json +++ b/src/NzbDrone.Core/Localization/Core/hu.json @@ -482,9 +482,9 @@ "Name": "Név", "MustNotContain": "Nem tartalmazhat", "MustContain": "Tartalmaznia kell", - "MovieYearHelpText": "A kizárandó film(ek) éve", + "MovieYearToExcludeHelpText": "A kizárandó film(ek) éve", "MovieYear": "Kiadási év", - "MovieTitleHelpText": "A kizárandó film címe (bármi értelmes lehet)", + "MovieTitleToExcludeHelpText": "A kizárandó film címe (bármi értelmes lehet)", "MovieTitle": "Filmcím", "MoviesSelectedInterp": "{0} Kiválasztott film(ek)", "Movies": "Filmek", @@ -619,7 +619,7 @@ "Torrents": "Torrentek", "TorrentDelayHelpText": "Percek késése, hogy várjon, mielőtt megragad egy torrentet", "TorrentDelay": "Torrent Késleltetés", - "TmdbIdHelpText": "A kizárandó film TMDb azonosítója", + "TmdbIdExcludeHelpText": "A kizárandó film TMDb azonosítója", "TMDBId": "TMDb azonosító", "Titles": "Címek", "Title": "Cím", diff --git a/src/NzbDrone.Core/Localization/Core/is.json b/src/NzbDrone.Core/Localization/Core/is.json index 84817e3f4..03c098540 100644 --- a/src/NzbDrone.Core/Localization/Core/is.json +++ b/src/NzbDrone.Core/Localization/Core/is.json @@ -342,7 +342,7 @@ "Calendar": "Dagatal", "MoviesSelectedInterp": "{0} Kvikmynd (ir) valdar", "MovieTitle": "Kvikmyndatitill", - "MovieTitleHelpText": "Titill myndarinnar til að útiloka (getur verið hvað sem er þýðingarmikill)", + "MovieTitleToExcludeHelpText": "Titill myndarinnar til að útiloka (getur verið hvað sem er þýðingarmikill)", "ClientPriority": "Forgangur viðskiptavinar", "DeleteCustomFormat": "Eyða sérsniðnu sniði", "DockerUpdater": "uppfærðu bryggjugáminn til að fá uppfærsluna", @@ -358,7 +358,7 @@ "InvalidFormat": "Ógilt snið", "Medium": "Miðlungs", "Minutes": "Fundargerð", - "MovieYearHelpText": "Ár kvikmyndarinnar til að útiloka", + "MovieYearToExcludeHelpText": "Ár kvikmyndarinnar til að útiloka", "Profiles": "Snið", "Reason": "Ástæða", "Released": "Sleppt", @@ -859,7 +859,7 @@ "Title": "Titill", "Titles": "Titlar", "TMDBId": "TMDb kt", - "TmdbIdHelpText": "TMDb auðkenni myndarinnar til að útiloka", + "TmdbIdExcludeHelpText": "TMDb auðkenni myndarinnar til að útiloka", "Today": "Í dag", "TorrentDelay": "Torrent Delay", "TorrentDelayHelpText": "Seinkaðu í nokkrar mínútur til að bíða áður en þú grípur strauminn", diff --git a/src/NzbDrone.Core/Localization/Core/it.json b/src/NzbDrone.Core/Localization/Core/it.json index a3b80d9d6..e9cc0497c 100644 --- a/src/NzbDrone.Core/Localization/Core/it.json +++ b/src/NzbDrone.Core/Localization/Core/it.json @@ -466,9 +466,9 @@ "NamingSettings": "Impostazioni di denominazione", "MustNotContain": "Non Deve Contenere", "MustContain": "Deve Contenere", - "MovieYearHelpText": "Anno del film da escludere", + "MovieYearToExcludeHelpText": "Anno del film da escludere", "MovieYear": "Anno del film", - "MovieTitleHelpText": "Titolo del Film da escludere (può essere qualunque cosa significativa)", + "MovieTitleToExcludeHelpText": "Titolo del Film da escludere (può essere qualunque cosa significativa)", "MoviesSelectedInterp": "{count} Film selezionato(i)", "MovieIsUnmonitored": "Il film è non monitorato", "MovieIsOnImportExclusionList": "Il Film è nella lista di esclusione dell'importazione", @@ -654,7 +654,7 @@ "Torrents": "Torrents", "TorrentDelayHelpText": "Ritardo in minuti da aspettare prima di prendere un torrent", "TorrentDelay": "Ritardo del torrent", - "TmdbIdHelpText": "Id di TMDb del film da escludere", + "TmdbIdExcludeHelpText": "Id di TMDb del film da escludere", "TMDBId": "ID di TMDb", "TimeFormat": "Formato Orario", "TestAllLists": "Testa tutte le liste", diff --git a/src/NzbDrone.Core/Localization/Core/ja.json b/src/NzbDrone.Core/Localization/Core/ja.json index 44d35a853..5651e818e 100644 --- a/src/NzbDrone.Core/Localization/Core/ja.json +++ b/src/NzbDrone.Core/Localization/Core/ja.json @@ -89,7 +89,7 @@ "AuthenticationMethodHelpText": "{appName}にアクセスするにはユーザー名とパスワードが必要です", "Component": "成分", "Proxy": "プロキシ", - "MovieTitleHelpText": "除外する映画のタイトル(意味のあるものであれば何でもかまいません)", + "MovieTitleToExcludeHelpText": "除外する映画のタイトル(意味のあるものであれば何でもかまいません)", "TestAllIndexers": "すべてのインデクサーをテストする", "AnalyseVideoFiles": "ビデオファイルを分析する", "AppDataDirectory": "AppDataディレクトリ", @@ -333,7 +333,7 @@ "Actions": "行動", "AddMovies": "映画を追加する", "IndexersSettingsSummary": "インデクサーとリリース制限", - "MovieYearHelpText": "除外する映画の年", + "MovieYearToExcludeHelpText": "除外する映画の年", "Age": "年齢", "NetCore": ".NET Core", "NoBackupsAreAvailable": "バックアップは利用できません", @@ -847,7 +847,7 @@ "Title": "題名", "Titles": "タイトル", "TMDBId": "TMDbID", - "TmdbIdHelpText": "除外する映画のTMDbID", + "TmdbIdExcludeHelpText": "除外する映画のTMDbID", "Today": "今日", "TorrentDelay": "トレント遅延", "Trace": "痕跡", diff --git a/src/NzbDrone.Core/Localization/Core/ko.json b/src/NzbDrone.Core/Localization/Core/ko.json index b50eacda2..36bc27fcc 100644 --- a/src/NzbDrone.Core/Localization/Core/ko.json +++ b/src/NzbDrone.Core/Localization/Core/ko.json @@ -328,7 +328,7 @@ "Movies": "영화", "MoviesSelectedInterp": "선택한 영화 {0} 개", "MovieTitle": "영화 제목", - "MovieTitleHelpText": "제외 할 영화 제목 (의미있는 것은 무엇이든 가능)", + "MovieTitleToExcludeHelpText": "제외 할 영화 제목 (의미있는 것은 무엇이든 가능)", "Actions": "동작", "Added": "추가됨", "AddNew": "새로 추가하기", @@ -337,7 +337,7 @@ "Agenda": "일정", "Failed": "실패한", "MovieYear": "영화 연도", - "MovieYearHelpText": "제외 할 영화 연도", + "MovieYearToExcludeHelpText": "제외 할 영화 연도", "MustContain": "포함해야 함", "MustNotContain": "포함해서는 안 됨", "NamingSettings": "이름 지정 설정", @@ -854,7 +854,7 @@ "Title": "제목", "Titles": "제목", "TMDBId": "TMDb ID", - "TmdbIdHelpText": "제외 할 영화의 TMDb ID", + "TmdbIdExcludeHelpText": "제외 할 영화의 TMDb ID", "Today": "오늘", "TorrentDelay": "급류 지연", "TorrentDelayHelpText": "급류를 잡기 전에 대기하는 데 몇 분이 걸립니다.", diff --git a/src/NzbDrone.Core/Localization/Core/nl.json b/src/NzbDrone.Core/Localization/Core/nl.json index 7648bdc9a..bf1551bd0 100644 --- a/src/NzbDrone.Core/Localization/Core/nl.json +++ b/src/NzbDrone.Core/Localization/Core/nl.json @@ -483,8 +483,8 @@ "MinimumCustomFormatScoreHelpText": "Minimum eigen formaat score toegelaten om te downloaden", "MinimumFreeSpaceHelpText": "Voorkom importeren indien de resulterende schijfruimte minder is dan deze hoeveelheid", "MovieIsOnImportExclusionList": "Film staat op de uitzonderingenlijst voor importeren", - "MovieTitleHelpText": "De titel van de uit te sluiten film (kan van alles zijn)", - "MovieYearHelpText": "Het jaar van de uit te sluiten film", + "MovieTitleToExcludeHelpText": "De titel van de uit te sluiten film (kan van alles zijn)", + "MovieYearToExcludeHelpText": "Het jaar van de uit te sluiten film", "NotificationTriggers": "Melding Reactiestarters", "OpenBrowserOnStart": "Open de browser bij het starten", "NoLimitForAnyRuntime": "Geen limiet voor eender welke speelduur", @@ -554,7 +554,7 @@ "TestAllClients": "Test Alle Downloaders", "TestAllLists": "Test Alle Lijsten", "TMDBId": "TMDb Id", - "TmdbIdHelpText": "De TMDb Id van de uit te sluiten film", + "TmdbIdExcludeHelpText": "De TMDb Id van de uit te sluiten film", "TorrentDelay": "Torrent Vertraging", "Torrents": "Torrents", "TotalFileSize": "Totale Bestandsgrootte", diff --git a/src/NzbDrone.Core/Localization/Core/pl.json b/src/NzbDrone.Core/Localization/Core/pl.json index 50d6efeff..48a73a0f4 100644 --- a/src/NzbDrone.Core/Localization/Core/pl.json +++ b/src/NzbDrone.Core/Localization/Core/pl.json @@ -336,9 +336,9 @@ "MovieIsUnmonitored": "Film jest niemonitorowany", "MoviesSelectedInterp": "Wybrane filmy: {0}", "MovieTitle": "Tytuł filmu", - "MovieTitleHelpText": "Tytuł filmu do wykluczenia (może być dowolny znaczący)", + "MovieTitleToExcludeHelpText": "Tytuł filmu do wykluczenia (może być dowolny znaczący)", "MovieYear": "Rok filmowy", - "MovieYearHelpText": "Rok filmu do wykluczenia", + "MovieYearToExcludeHelpText": "Rok filmu do wykluczenia", "MustContain": "Musi zawierać", "MustNotContain": "Nie może zawierać", "NamingSettings": "Ustawienia nazewnictwa", @@ -859,7 +859,7 @@ "Title": "Tytuł", "Titles": "Tytuły", "TMDBId": "Identyfikator TMDb", - "TmdbIdHelpText": "Identyfikator TMDb filmu do wykluczenia", + "TmdbIdExcludeHelpText": "Identyfikator TMDb filmu do wykluczenia", "Today": "Dzisiaj", "TorrentDelay": "Opóźnienie Torrenta", "TorrentDelayHelpText": "Opóźnienie w ciągu kilku minut, aby poczekać przed złapaniem torrenta", diff --git a/src/NzbDrone.Core/Localization/Core/pt.json b/src/NzbDrone.Core/Localization/Core/pt.json index 2059cd517..a7320aaed 100644 --- a/src/NzbDrone.Core/Localization/Core/pt.json +++ b/src/NzbDrone.Core/Localization/Core/pt.json @@ -335,9 +335,9 @@ "NamingSettings": "Definições de nomenclatura", "MustNotContain": "Não deve conter", "MustContain": "Deve conter", - "MovieYearHelpText": "Ano do filme a eliminar", + "MovieYearToExcludeHelpText": "Ano do filme a eliminar", "MovieYear": "Ano do filme", - "MovieTitleHelpText": "Título do filme a eliminar (pode ser qualquer palavra)", + "MovieTitleToExcludeHelpText": "Título do filme a eliminar (pode ser qualquer palavra)", "MovieIsDownloading": "Transferindo filme", "MovieInfoLanguageHelpTextWarning": "É preciso reiniciar o browser", "MovieID": "ID do filme", @@ -569,7 +569,7 @@ "ExtraFileExtensionsHelpTextsExamples": "Exemplos: \".sub, .nfo\" ou \"sub,nfo\"", "ExtraFileExtensionsHelpText": "Lista separada por vírgulas de ficheiros adicionais a importar (.nfo será importado como .nfo-orig)", "ExistingTag": "Etiqueta existente", - "TmdbIdHelpText": "ID do TMDb do filme a eliminar", + "TmdbIdExcludeHelpText": "ID do TMDb do filme a eliminar", "MovieExcludedFromAutomaticAdd": "Filme eliminado da adição automática", "MovieAlreadyExcluded": "Filme já eliminado", "ExcludeMovie": "Eliminar filme", diff --git a/src/NzbDrone.Core/Localization/Core/pt_BR.json b/src/NzbDrone.Core/Localization/Core/pt_BR.json index 593eb3b3a..4aca6ceb5 100644 --- a/src/NzbDrone.Core/Localization/Core/pt_BR.json +++ b/src/NzbDrone.Core/Localization/Core/pt_BR.json @@ -462,9 +462,9 @@ "About": "Sobre", "Analytics": "Análises", "Month": "Mês", - "MovieYearHelpText": "O ano do filme a excluir", + "MovieYearToExcludeHelpText": "O ano do filme a excluir", "MovieYear": "Ano do filme", - "MovieTitleHelpText": "O título do filme a excluir (pode ser qualquer coisa significativa)", + "MovieTitleToExcludeHelpText": "O título do filme a excluir (pode ser qualquer coisa significativa)", "MovieTitle": "Título do filme", "MoviesSelectedInterp": "{count} Filme(s) selecionado(s)", "Movies": "Filmes", @@ -764,7 +764,7 @@ "Ui": "IU", "Type": "Tipo", "Torrents": "Torrents", - "TmdbIdHelpText": "A ID do TMDb do filme a excluir", + "TmdbIdExcludeHelpText": "A ID do TMDb do filme a excluir", "TMDBId": "ID do TMDb", "TMDb": "TMDb", "SslCertPathHelpText": "Caminho para o arquivo pfx", diff --git a/src/NzbDrone.Core/Localization/Core/ro.json b/src/NzbDrone.Core/Localization/Core/ro.json index d29a6a1e1..400714b5a 100644 --- a/src/NzbDrone.Core/Localization/Core/ro.json +++ b/src/NzbDrone.Core/Localization/Core/ro.json @@ -519,12 +519,12 @@ "BranchUpdate": "Sucursală de utilizat pentru actualizarea {appName}", "BranchUpdateMechanism": "Ramură utilizată de mecanismul extern de actualizare", "BypassProxyForLocalAddresses": "Nu folosiți Proxy pentru adrese locale", - "MovieTitleHelpText": "Titlul filmului de exclus (poate avea orice sens)", + "MovieTitleToExcludeHelpText": "Titlul filmului de exclus (poate avea orice sens)", "Local": "Local", "MovieYear": "Anul filmului", "RecyclingBinCleanup": "Curățarea coșului de reciclare", "RefreshMovie": "Reîmprospătați filmul", - "MovieYearHelpText": "Anul filmului de exclus", + "MovieYearToExcludeHelpText": "Anul filmului de exclus", "MustContain": "Trebuie sa contina", "MustNotContain": "Nu trebuie să conțină", "NamingSettings": "Setări de denumire", @@ -877,7 +877,7 @@ "TagIsNotUsedAndCanBeDeleted": "Eticheta nu este utilizată și poate fi ștearsă", "ICalTagsMoviesHelpText": "Se aplică filmelor cu cel puțin o etichetă potrivită", "TMDBId": "Cod TMDb", - "TmdbIdHelpText": "Codul TMDb al filmului de exclus", + "TmdbIdExcludeHelpText": "Codul TMDb al filmului de exclus", "Today": "Astăzi", "TorrentDelay": "Întârziere Torrent", "TorrentDelayTime": "Întârziere Torrent: {0}", diff --git a/src/NzbDrone.Core/Localization/Core/ru.json b/src/NzbDrone.Core/Localization/Core/ru.json index 12248740d..5c52ab228 100644 --- a/src/NzbDrone.Core/Localization/Core/ru.json +++ b/src/NzbDrone.Core/Localization/Core/ru.json @@ -490,9 +490,9 @@ "MustNotContain": "Не должен содержать", "MustContain": "Должен содержать", "MultiLanguage": "Многоязычный", - "MovieYearHelpText": "Год фильма для исключений", + "MovieYearToExcludeHelpText": "Год фильма для исключений", "MovieYear": "Год фильма", - "MovieTitleHelpText": "Название фильма для исключения (может быть любым)", + "MovieTitleToExcludeHelpText": "Название фильма для исключения (может быть любым)", "MovieTitle": "Название фильма", "MoviesSelectedInterp": "{count} фильм(ов) выбрано", "Movies": "Фильмы", @@ -771,7 +771,7 @@ "TorrentDelay": "Задержка торрента", "Tomorrow": "Завтра", "Today": "Сегодня", - "TmdbIdHelpText": "Идентификатор TMDb фильма, который нужно исключить", + "TmdbIdExcludeHelpText": "Идентификатор TMDb фильма, который нужно исключить", "TMDBId": "TMDb идентификатор", "TMDb": "TMDb", "SearchAll": "Искать все", diff --git a/src/NzbDrone.Core/Localization/Core/sv.json b/src/NzbDrone.Core/Localization/Core/sv.json index 6ec3b0a1d..c24f55aa2 100644 --- a/src/NzbDrone.Core/Localization/Core/sv.json +++ b/src/NzbDrone.Core/Localization/Core/sv.json @@ -709,9 +709,9 @@ "DatabaseMigration": "DB Migration", "ImportExtraFiles": "Importera extra filer", "ImportExtraFilesMovieHelpText": "Importera matchande extrafiler (undertexter, nfo, etc) efter import av en filmfil", - "MovieTitleHelpText": "Filmens titel att utesluta (kan vara något meningsfullt)", + "MovieTitleToExcludeHelpText": "Filmens titel att utesluta (kan vara något meningsfullt)", "MinimumCustomFormatScoreHelpText": "Lägsta möjliga anpassade formatpoäng tillåtet att ladda ner", - "MovieYearHelpText": "Året för filmen att utesluta", + "MovieYearToExcludeHelpText": "Året för filmen att utesluta", "ProxyType": "Proxy-typ", "NamingSettings": "Namninställningar", "LastDuration": "lastDuration", @@ -899,7 +899,7 @@ "TheLogLevelDefault": "Loggnivån är som standard 'Info' och kan ändras i", "ThisCannotBeCancelled": "Detta kan inte avbrytas en gång startat utan att {appName} startas om.", "TMDBId": "TMDb-id", - "TmdbIdHelpText": "TMDb-id för filmen att utesluta", + "TmdbIdExcludeHelpText": "TMDb-id för filmen att utesluta", "TorrentDelay": "Torrentfördröjning", "TorrentDelayHelpText": "Fördröja på några minuter för att vänta innan du tar en torrent", "TorrentDelayTime": "Torrentfördröjning: {0}", diff --git a/src/NzbDrone.Core/Localization/Core/th.json b/src/NzbDrone.Core/Localization/Core/th.json index 265bff871..63993aeef 100644 --- a/src/NzbDrone.Core/Localization/Core/th.json +++ b/src/NzbDrone.Core/Localization/Core/th.json @@ -169,7 +169,7 @@ "TagsSettingsSummary": "ดูแท็กทั้งหมดและวิธีการใช้งาน แท็กที่ไม่ได้ใช้สามารถลบออกได้", "TheLogLevelDefault": "ระดับการบันทึกมีค่าเริ่มต้นเป็น \"ข้อมูล\" และสามารถเปลี่ยนแปลงได้", "Titles": "ชื่อเรื่อง", - "TmdbIdHelpText": "TMDb Id ของภาพยนตร์ที่จะยกเว้น", + "TmdbIdExcludeHelpText": "TMDb Id ของภาพยนตร์ที่จะยกเว้น", "TorrentsDisabled": "Torrents ถูกปิดใช้งาน", "UiLanguageHelpText": "ภาษาที่ {appName} จะใช้สำหรับ UI", "UiSettingsSummary": "ตัวเลือกปฏิทินวันที่และสีบกพร่อง", @@ -442,9 +442,9 @@ "Movies": "ภาพยนตร์", "MoviesSelectedInterp": "{0} ภาพยนตร์ที่เลือก", "MovieTitle": "ชื่อหนัง", - "MovieTitleHelpText": "ชื่อของภาพยนตร์ที่จะไม่รวม (อาจมีความหมายอะไรก็ได้)", + "MovieTitleToExcludeHelpText": "ชื่อของภาพยนตร์ที่จะไม่รวม (อาจมีความหมายอะไรก็ได้)", "MovieYear": "ปีภาพยนตร์", - "MovieYearHelpText": "ปีของภาพยนตร์ที่จะไม่รวม", + "MovieYearToExcludeHelpText": "ปีของภาพยนตร์ที่จะไม่รวม", "MustContain": "ต้องมี", "MustNotContain": "ต้องไม่มี", "NamingSettings": "การตั้งชื่อการตั้งค่า", diff --git a/src/NzbDrone.Core/Localization/Core/tr.json b/src/NzbDrone.Core/Localization/Core/tr.json index 049af4c10..1d1dd96b9 100644 --- a/src/NzbDrone.Core/Localization/Core/tr.json +++ b/src/NzbDrone.Core/Localization/Core/tr.json @@ -299,7 +299,7 @@ "ThisCannotBeCancelled": "Bu, {appName} yeniden başlatılmadan başlatıldıktan sonra iptal edilemez.", "Title": "Başlık", "TMDBId": "TMDb Kimliği", - "TmdbIdHelpText": "Hariç tutulacak filmin TMDb Kimliği", + "TmdbIdExcludeHelpText": "Hariç tutulacak filmin TMDb Kimliği", "Today": "Bugün", "TorrentDelay": "Torrent Gecikmesi", "TorrentDelayHelpText": "Bir torrent almadan önce beklemek için dakikalar içinde gecikme", @@ -571,9 +571,9 @@ "MovieIsDownloading": "Film indiriliyor", "MovieIsUnmonitored": "Film takip edilmiyor", "MoviesSelectedInterp": "{count} Film Seçildi", - "MovieTitleHelpText": "Hariç tutulacak filmin başlığı (anlamlı herhangi bir şey olabilir)", + "MovieTitleToExcludeHelpText": "Hariç tutulacak filmin başlığı (anlamlı herhangi bir şey olabilir)", "MovieYear": "Film Yılı", - "MovieYearHelpText": "Hariç tutulacak film yılı", + "MovieYearToExcludeHelpText": "Hariç tutulacak film yılı", "PendingChangesDiscardChanges": "Değişiklikleri atın ve ayrıl", "PreferredSize": "Tercih Edilen Boyut", "Proper": "Uygun", diff --git a/src/NzbDrone.Core/Localization/Core/uk.json b/src/NzbDrone.Core/Localization/Core/uk.json index d037c249c..e044ec9c8 100644 --- a/src/NzbDrone.Core/Localization/Core/uk.json +++ b/src/NzbDrone.Core/Localization/Core/uk.json @@ -871,7 +871,7 @@ "Timeleft": "Час залишився", "Title": "Назва", "Titles": "Назви", - "TmdbIdHelpText": "Ідентифікатор TMDb фільму, який потрібно виключити", + "TmdbIdExcludeHelpText": "Ідентифікатор TMDb фільму, який потрібно виключити", "TmdbRating": "Рейтинг TMDb", "TmdbVotes": "Голоси TMDb", "TorrentDelayTime": "Затримка торрента: {0}", @@ -941,9 +941,9 @@ "InstallLatest": "Встановити останній", "MegabytesPerMinute": "Мегабайт за хвилину", "Message": "Повідомлення", - "MovieTitleHelpText": "Назва фільму, який потрібно виключити (може бути будь-яким значущим)", + "MovieTitleToExcludeHelpText": "Назва фільму, який потрібно виключити (може бути будь-яким значущим)", "MovieYear": "Фільм рік", - "MovieYearHelpText": "Рік фільму виключити", + "MovieYearToExcludeHelpText": "Рік фільму виключити", "NegateHelpText": "Якщо позначено, настроюваний формат не застосовуватиметься, якщо ця умова {0} збігається.", "Never": "Ніколи", "New": "Новий", diff --git a/src/NzbDrone.Core/Localization/Core/vi.json b/src/NzbDrone.Core/Localization/Core/vi.json index e25f06bfc..c07879b6e 100644 --- a/src/NzbDrone.Core/Localization/Core/vi.json +++ b/src/NzbDrone.Core/Localization/Core/vi.json @@ -469,9 +469,9 @@ "AddMovies": "Thêm phim", "MoviesSelectedInterp": "{0} Phim đã chọn", "MovieTitle": "Tên phim", - "MovieTitleHelpText": "Tiêu đề của bộ phim cần loại trừ (có thể là bất kỳ điều gì có ý nghĩa)", + "MovieTitleToExcludeHelpText": "Tiêu đề của bộ phim cần loại trừ (có thể là bất kỳ điều gì có ý nghĩa)", "MovieYear": "Năm phim", - "MovieYearHelpText": "Năm của bộ phim để loại trừ", + "MovieYearToExcludeHelpText": "Năm của bộ phim để loại trừ", "MustContain": "Phải chứa", "AddNewTmdbIdMessage": "Bạn cũng có thể tìm kiếm bằng cách sử dụng TMDb Id của một bộ phim. ví dụ. 'tmdb: 71663'", "Indexer": "Người lập chỉ mục", @@ -886,7 +886,7 @@ "Time": "Thời gian", "Title": "Tiêu đề", "TMDBId": "Id TMDb", - "TmdbIdHelpText": "Id TMDb của phim để loại trừ", + "TmdbIdExcludeHelpText": "Id TMDb của phim để loại trừ", "TorrentDelayTime": "Độ trễ Torrent: {0}", "Torrents": "Torrents", "Trailer": "Giới thiệu tóm tắt", diff --git a/src/NzbDrone.Core/Localization/Core/zh_CN.json b/src/NzbDrone.Core/Localization/Core/zh_CN.json index 5a740cd08..f8899441c 100644 --- a/src/NzbDrone.Core/Localization/Core/zh_CN.json +++ b/src/NzbDrone.Core/Localization/Core/zh_CN.json @@ -837,7 +837,7 @@ "TagsSettingsSummary": "显示全部标签和标签使用情况,可删除未使用的标签", "TMDb": "TMDb", "TMDBId": "TMDb ID", - "TmdbIdHelpText": "排除电影的TMDb ID", + "TmdbIdExcludeHelpText": "排除电影的TMDb ID", "TorrentDelay": "Torrent延时", "TorrentDelayTime": "Torrent延时:{0}", "Torrents": "种子", @@ -901,8 +901,8 @@ "NextExecution": "接下来执行", "New": "新的", "NegateHelpText": "如勾选,当条件 {0} 满足时不会应用自定义格式。", - "MovieYearHelpText": "排除的电影的年份", - "MovieTitleHelpText": "要排除的电影标题(可以是任何字段)", + "MovieYearToExcludeHelpText": "排除的电影的年份", + "MovieTitleToExcludeHelpText": "要排除的电影标题(可以是任何字段)", "MovieIsRecommend": "影片是根据最近添加的推荐", "MovieIndexScrollTop": "影片索引:滚动到顶部", "MovieIndexScrollBottom": "影片索引:滚动到底部", diff --git a/src/Radarr.Api.V3/ImportLists/ImportListExclusionController.cs b/src/Radarr.Api.V3/ImportLists/ImportListExclusionController.cs index 409480831..976b537e4 100644 --- a/src/Radarr.Api.V3/ImportLists/ImportListExclusionController.cs +++ b/src/Radarr.Api.V3/ImportLists/ImportListExclusionController.cs @@ -1,9 +1,11 @@ +using System; using System.Collections.Generic; using System.Linq; using FluentValidation; using Microsoft.AspNetCore.Mvc; using NzbDrone.Core.ImportLists.ImportExclusions; using Radarr.Http; +using Radarr.Http.Extensions; using Radarr.Http.REST; using Radarr.Http.REST.Attributes; @@ -25,6 +27,7 @@ namespace Radarr.Api.V3.ImportLists [HttpGet] [Produces("application/json")] + [Obsolete("Deprecated")] public List GetImportListExclusions() { return _importListExclusionService.All().ToResource(); @@ -35,6 +38,16 @@ namespace Radarr.Api.V3.ImportLists return _importListExclusionService.Get(id).ToResource(); } + [HttpGet("paged")] + [Produces("application/json")] + public PagingResource GetImportListExclusionsPaged([FromQuery] PagingRequestResource paging) + { + var pagingResource = new PagingResource(paging); + var pageSpec = pagingResource.MapToPagingSpec(); + + return pageSpec.ApplyToPage(_importListExclusionService.Paged, ImportListExclusionResourceMapper.ToResource); + } + [RestPostById] [Consumes("application/json")] public ActionResult AddImportListExclusion([FromBody] ImportListExclusionResource resource)