diff --git a/frontend/src/Helpers/Hooks/usePrevious.tsx b/frontend/src/Helpers/Hooks/usePrevious.tsx new file mode 100644 index 000000000..b594e2632 --- /dev/null +++ b/frontend/src/Helpers/Hooks/usePrevious.tsx @@ -0,0 +1,11 @@ +import { useEffect, useRef } from 'react'; + +export default function usePrevious(value: T): T | undefined { + const ref = useRef(); + + useEffect(() => { + ref.current = value; + }, [value]); + + return ref.current; +} diff --git a/frontend/src/Indexer/Index/IndexerIndex.tsx b/frontend/src/Indexer/Index/IndexerIndex.tsx index b5b2fba6a..212edbd27 100644 --- a/frontend/src/Indexer/Index/IndexerIndex.tsx +++ b/frontend/src/Indexer/Index/IndexerIndex.tsx @@ -148,15 +148,15 @@ const IndexerIndex = withScrollPosition((props: IndexerIndexProps) => { ); const jumpBarItems = useMemo(() => { - // Reset if not sorting by sortTitle - if (sortKey !== 'sortTitle') { + // Reset if not sorting by sortName + if (sortKey !== 'sortName') { return { order: [], }; } const characters = items.reduce((acc, item) => { - let char = item.sortTitle.charAt(0); + let char = item.sortName.charAt(0); if (!isNaN(char)) { char = '#'; @@ -225,7 +225,7 @@ const IndexerIndex = withScrollPosition((props: IndexerIndexProps) => { label={ isSelectMode ? translate('StopSelecting') - : translate('SelectIndexer') + : translate('SelectIndexers') } iconName={isSelectMode ? icons.SERIES_ENDED : icons.CHECK} isSelectMode={isSelectMode} diff --git a/frontend/src/Indexer/Index/Select/Delete/DeleteIndexerModalContent.tsx b/frontend/src/Indexer/Index/Select/Delete/DeleteIndexerModalContent.tsx index 3d241428a..d2ba8c595 100644 --- a/frontend/src/Indexer/Index/Select/Delete/DeleteIndexerModalContent.tsx +++ b/frontend/src/Indexer/Index/Select/Delete/DeleteIndexerModalContent.tsx @@ -9,6 +9,7 @@ import ModalHeader from 'Components/Modal/ModalHeader'; import { kinds } from 'Helpers/Props'; import { bulkDeleteIndexers } from 'Store/Actions/indexerIndexActions'; import createAllIndexersSelector from 'Store/Selectors/createAllIndexersSelector'; +import translate from 'Utilities/String/translate'; import styles from './DeleteIndexerModalContent.css'; interface DeleteIndexerModalContentProps { @@ -19,16 +20,16 @@ interface DeleteIndexerModalContentProps { function DeleteIndexerModalContent(props: DeleteIndexerModalContentProps) { const { indexerIds, onModalClose } = props; - const allIndexer = useSelector(createAllIndexersSelector()); + const allIndexers = useSelector(createAllIndexersSelector()); const dispatch = useDispatch(); - const indexers = useMemo(() => { + const selectedIndexers = useMemo(() => { const indexers = indexerIds.map((id) => { - return allIndexer.find((s) => s.id === id); + return allIndexers.find((s) => s.id === id); }); - return orderBy(indexers, ['sortTitle']); - }, [indexerIds, allIndexer]); + return orderBy(indexers, ['sortName']); + }, [indexerIds, allIndexers]); const onDeleteIndexerConfirmed = useCallback(() => { dispatch( @@ -42,17 +43,19 @@ function DeleteIndexerModalContent(props: DeleteIndexerModalContentProps) { return ( - Delete Selected Indexer + {translate('DeleteSelectedIndexers')}
- {`Are you sure you want to delete ${indexers.length} selected indexers?`} + {translate('DeleteSelectedIndexersMessageText', [ + selectedIndexers.length, + ])}
    - {indexers.map((s) => { + {selectedIndexers.map((s) => { return ( -
  • +
  • {s.name}
  • ); @@ -61,10 +64,10 @@ function DeleteIndexerModalContent(props: DeleteIndexerModalContentProps) { - + diff --git a/frontend/src/Indexer/Index/Select/Edit/EditIndexerModalContent.tsx b/frontend/src/Indexer/Index/Select/Edit/EditIndexerModalContent.tsx index 05ad803b0..4831aff06 100644 --- a/frontend/src/Indexer/Index/Select/Edit/EditIndexerModalContent.tsx +++ b/frontend/src/Indexer/Index/Select/Edit/EditIndexerModalContent.tsx @@ -67,7 +67,7 @@ function EditIndexerModalContent(props: EditIndexerModalContentProps) { setAppProfileId(value); break; default: - console.warn('EditIndexerModalContent Unknown Input'); + console.warn(`EditIndexersModalContent Unknown Input: '${name}'`); } }, [setEnable] @@ -81,7 +81,7 @@ function EditIndexerModalContent(props: EditIndexerModalContentProps) { return ( - {translate('Edit Selected Indexer')} + {translate('EditSelectedIndexers')} @@ -112,14 +112,14 @@ function EditIndexerModalContent(props: EditIndexerModalContentProps) {
    - {translate('{0} indexers selected', selectedCount.toString())} + {translate('CountIndexersSelected', [selectedCount])}
    diff --git a/frontend/src/Indexer/Index/Select/IndexerIndexSelectFooter.tsx b/frontend/src/Indexer/Index/Select/IndexerIndexSelectFooter.tsx index 37828f8f4..7cd6adca7 100644 --- a/frontend/src/Indexer/Index/Select/IndexerIndexSelectFooter.tsx +++ b/frontend/src/Indexer/Index/Select/IndexerIndexSelectFooter.tsx @@ -4,6 +4,7 @@ import { createSelector } from 'reselect'; import { useSelect } from 'App/SelectContext'; import SpinnerButton from 'Components/Link/SpinnerButton'; import PageContentFooter from 'Components/Page/PageContentFooter'; +import usePrevious from 'Helpers/Hooks/usePrevious'; import { kinds } from 'Helpers/Props'; import { saveIndexerEditor } from 'Store/Actions/indexerIndexActions'; import translate from 'Utilities/String/translate'; @@ -13,7 +14,7 @@ import EditIndexerModal from './Edit/EditIndexerModal'; import TagsModal from './Tags/TagsModal'; import styles from './IndexerIndexSelectFooter.css'; -const seriesEditorSelector = createSelector( +const indexersEditorSelector = createSelector( (state) => state.indexers, (indexers) => { const { isSaving, isDeleting, deleteError } = indexers; @@ -27,8 +28,9 @@ const seriesEditorSelector = createSelector( ); function IndexerIndexSelectFooter() { - const { isSaving, isDeleting, deleteError } = - useSelector(seriesEditorSelector); + const { isSaving, isDeleting, deleteError } = useSelector( + indexersEditorSelector + ); const dispatch = useDispatch(); @@ -37,6 +39,7 @@ function IndexerIndexSelectFooter() { const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false); const [isSavingIndexer, setIsSavingIndexer] = useState(false); const [isSavingTags, setIsSavingTags] = useState(false); + const previousIsDeleting = usePrevious(isDeleting); const [selectState, selectDispatch] = useSelect(); const { selectedState } = selectState; @@ -110,10 +113,10 @@ function IndexerIndexSelectFooter() { }, [isSaving]); useEffect(() => { - if (!isDeleting && !deleteError) { + if (previousIsDeleting && !isDeleting && !deleteError) { selectDispatch({ type: 'unselectAll' }); } - }, [isDeleting, deleteError, selectDispatch]); + }, [previousIsDeleting, isDeleting, deleteError, selectDispatch]); const anySelected = selectedCount > 0; @@ -134,7 +137,7 @@ function IndexerIndexSelectFooter() { isDisabled={!anySelected} onPress={onTagsPress} > - {translate('Set Tags')} + {translate('SetTags')} @@ -151,7 +154,7 @@ function IndexerIndexSelectFooter() {
    - {translate('{0} indexers selected', selectedCount.toString())} + {translate('CountIndexersSelected', [selectedCount])}
    - Tags + {translate('Tags')}
    @@ -119,8 +119,8 @@ function TagsModalContent(props: TagsModalContentProps) { key={tag.id} title={ removeTag - ? translate('RemoveTagRemovingTag') - : translate('RemoveTagExistingTag') + ? translate('RemovingTag') + : translate('ExistingTag') } kind={removeTag ? kinds.INVERSE : kinds.INFO} size={sizes.LARGE} @@ -159,10 +159,10 @@ function TagsModalContent(props: TagsModalContentProps) { - + diff --git a/frontend/src/Store/Actions/indexerActions.js b/frontend/src/Store/Actions/indexerActions.js index 8a05011b4..508140a22 100644 --- a/frontend/src/Store/Actions/indexerActions.js +++ b/frontend/src/Store/Actions/indexerActions.js @@ -29,6 +29,8 @@ export const defaultState = { isFetching: false, isPopulated: false, error: null, + isDeleting: false, + deleteError: null, selectedSchema: {}, isSaving: false, saveError: null, diff --git a/frontend/src/Store/Selectors/createIndexerClientSideCollectionItemsSelector.js b/frontend/src/Store/Selectors/createIndexerClientSideCollectionItemsSelector.js index 931bddf23..098562877 100644 --- a/frontend/src/Store/Selectors/createIndexerClientSideCollectionItemsSelector.js +++ b/frontend/src/Store/Selectors/createIndexerClientSideCollectionItemsSelector.js @@ -14,7 +14,7 @@ function createUnoptimizedSelector(uiSection) { return { id, - sortTitle: name + sortName: name }; }); @@ -38,7 +38,7 @@ const createMovieEqualSelector = createSelectorCreator( function createIndexerClientSideCollectionItemsSelector(uiSection) { return createMovieEqualSelector( createUnoptimizedSelector(uiSection), - (movies) => movies + (indexers) => indexers ); } diff --git a/src/NzbDrone.Core/Localization/Core/en.json b/src/NzbDrone.Core/Localization/Core/en.json index d1ccc56ab..b993988ad 100644 --- a/src/NzbDrone.Core/Localization/Core/en.json +++ b/src/NzbDrone.Core/Localization/Core/en.json @@ -41,6 +41,7 @@ "ApplicationUrlHelpText": "This application's external URL including http(s)://, port and URL base", "Applications": "Applications", "Apply": "Apply", + "ApplyChanges": "Apply Changes", "ApplyTags": "Apply Tags", "ApplyTagsHelpTexts1": "How to apply tags to the selected indexers", "ApplyTagsHelpTexts2": "Add: Add the tags the existing list of tags", @@ -101,6 +102,7 @@ "ConnectionLostMessage": "Prowlarr has lost its connection to the backend and will need to be reloaded to restore functionality.", "Connections": "Connections", "CouldNotConnectSignalR": "Could not connect to SignalR, UI won't update", + "CountIndexersSelected": "{0} indexers selected", "Custom": "Custom", "CustomFilters": "Custom Filters", "DBMigration": "DB Migration", @@ -120,6 +122,9 @@ "DeleteIndexerProxyMessageText": "Are you sure you want to delete the proxy '{0}'?", "DeleteNotification": "Delete Notification", "DeleteNotificationMessageText": "Are you sure you want to delete the notification '{0}'?", + "DeleteSelectedIndexer": "Delete Selected Indexer", + "DeleteSelectedIndexers": "Delete Selected Indexers", + "DeleteSelectedIndexersMessageText": "Are you sure you want to delete {0} selected indexer(s)?", "DeleteTag": "Delete Tag", "DeleteTagMessageText": "Are you sure you want to delete the tag '{0}'?", "Description": "Description", @@ -140,6 +145,7 @@ "Duration": "Duration", "Edit": "Edit", "EditIndexer": "Edit Indexer", + "EditSelectedIndexers": "Edit Selected Indexers", "EditSyncProfile": "Edit Sync Profile", "ElapsedTime": "Elapsed Time", "Enable": "Enable", @@ -391,7 +397,7 @@ "Security": "Security", "Seeders": "Seeders", "SelectAll": "Select All", - "SelectIndexer": "Select Indexer", + "SelectIndexers": "Select Indexers", "SemiPrivate": "Semi-Private", "SendAnonymousUsageData": "Send Anonymous Usage Data", "SetTags": "Set Tags",