diff --git a/frontend/src/App/State/SettingsAppState.ts b/frontend/src/App/State/SettingsAppState.ts index 910071dc1..ece88ef93 100644 --- a/frontend/src/App/State/SettingsAppState.ts +++ b/frontend/src/App/State/SettingsAppState.ts @@ -4,7 +4,6 @@ import AppSectionState, { AppSectionListState, AppSectionSaveState, AppSectionSchemaState, - PagedAppSectionState, } from 'App/State/AppSectionState'; import AutoTagging, { AutoTaggingSpecification } from 'typings/AutoTagging'; import CustomFormat from 'typings/CustomFormat'; @@ -12,7 +11,6 @@ import CustomFormatSpecification from 'typings/CustomFormatSpecification'; import DelayProfile from 'typings/DelayProfile'; import DownloadClient from 'typings/DownloadClient'; import ImportList from 'typings/ImportList'; -import ImportListExclusion from 'typings/ImportListExclusion'; import ImportListOptionsSettings from 'typings/ImportListOptionsSettings'; import DownloadClientOptions from 'typings/Settings/DownloadClientOptions'; @@ -71,14 +69,6 @@ export interface ImportListOptionsSettingsAppState extends AppSectionItemState, AppSectionSaveState {} -export interface ImportListExclusionsSettingsAppState - extends AppSectionState, - AppSectionSaveState, - PagedAppSectionState, - AppSectionDeleteState { - pendingChanges: Partial; -} - interface SettingsAppState { autoTaggings: AutoTaggingAppState; autoTaggingSpecifications: AutoTaggingSpecificationAppState; @@ -87,7 +77,6 @@ interface SettingsAppState { delayProfiles: DelayProfileAppState; downloadClients: DownloadClientAppState; downloadClientOptions: DownloadClientOptionsAppState; - importListExclusions: ImportListExclusionsSettingsAppState; importListOptions: ImportListOptionsSettingsAppState; importLists: ImportListAppState; } diff --git a/frontend/src/Helpers/Hooks/usePage.ts b/frontend/src/Helpers/Hooks/usePage.ts index af30c28a0..e17d94dec 100644 --- a/frontend/src/Helpers/Hooks/usePage.ts +++ b/frontend/src/Helpers/Hooks/usePage.ts @@ -7,6 +7,7 @@ interface PageStore { cutoffUnmet: number; events: number; history: number; + importListExclusion: number; missing: number; queue: number; } @@ -16,6 +17,7 @@ const pageStore = create(() => ({ cutoffUnmet: 1, events: 1, history: 1, + importListExclusion: 1, missing: 1, queue: 1, })); diff --git a/frontend/src/Settings/ImportLists/ImportListExclusions/EditImportListExclusionModal.tsx b/frontend/src/Settings/ImportLists/ImportListExclusions/EditImportListExclusionModal.tsx index 7f5feafab..4b2cbb213 100644 --- a/frontend/src/Settings/ImportLists/ImportListExclusions/EditImportListExclusionModal.tsx +++ b/frontend/src/Settings/ImportLists/ImportListExclusions/EditImportListExclusionModal.tsx @@ -1,12 +1,12 @@ -import React, { useCallback } from 'react'; -import { useDispatch } from 'react-redux'; +import React from 'react'; 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; + title?: string; + tvdbId?: number; isOpen: boolean; onModalClose: () => void; onDeleteImportListExclusionPress?: () => void; @@ -17,22 +17,11 @@ function EditImportListExclusionModal( ) { const { isOpen, onModalClose, ...otherProps } = props; - const dispatch = useDispatch(); - - const handleModalClose = useCallback(() => { - dispatch( - clearPendingChanges({ - section: 'settings.importListExclusions', - }) - ); - onModalClose(); - }, [dispatch, onModalClose]); - return ( - + ); diff --git a/frontend/src/Settings/ImportLists/ImportListExclusions/EditImportListExclusionModalContent.css b/frontend/src/Settings/ImportLists/ImportListExclusions/EditImportListExclusionModalContent.css index 97e132552..a2b6014df 100644 --- a/frontend/src/Settings/ImportLists/ImportListExclusions/EditImportListExclusionModalContent.css +++ b/frontend/src/Settings/ImportLists/ImportListExclusions/EditImportListExclusionModalContent.css @@ -1,9 +1,3 @@ -.body { - composes: modalBody from '~Components/Modal/ModalBody.css'; - - flex: 1 1 430px; -} - .deleteButton { composes: button from '~Components/Link/Button.css'; diff --git a/frontend/src/Settings/ImportLists/ImportListExclusions/EditImportListExclusionModalContent.css.d.ts b/frontend/src/Settings/ImportLists/ImportListExclusions/EditImportListExclusionModalContent.css.d.ts index 7881f9867..c5f0ef8a7 100644 --- a/frontend/src/Settings/ImportLists/ImportListExclusions/EditImportListExclusionModalContent.css.d.ts +++ b/frontend/src/Settings/ImportLists/ImportListExclusions/EditImportListExclusionModalContent.css.d.ts @@ -1,7 +1,6 @@ // This file is automatically generated. // Please do not change this file! interface CssExports { - 'body': string; 'deleteButton': string; } export const cssExports: CssExports; diff --git a/frontend/src/Settings/ImportLists/ImportListExclusions/EditImportListExclusionModalContent.tsx b/frontend/src/Settings/ImportLists/ImportListExclusions/EditImportListExclusionModalContent.tsx index 31e92005d..e161083c2 100644 --- a/frontend/src/Settings/ImportLists/ImportListExclusions/EditImportListExclusionModalContent.tsx +++ b/frontend/src/Settings/ImportLists/ImportListExclusions/EditImportListExclusionModalContent.tsx @@ -1,115 +1,70 @@ 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 { InputChanged } from 'typings/inputs'; -import { PendingSection } from 'typings/pending'; import translate from 'Utilities/String/translate'; +import { useManageImportListExclusion } from './useImportListExclusions'; import styles from './EditImportListExclusionModalContent.css'; -const newImportListExclusion = { - title: '', - tvdbId: 0, -}; - -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 { - isFetching, - error, - isSaving, - saveError, - item: settings.settings as PendingSection, - ...settings, - }; - } - ); -} - interface EditImportListExclusionModalContentProps { id?: number; + title?: string; + tvdbId?: number; onModalClose: () => void; onDeleteImportListExclusionPress?: () => void; } function EditImportListExclusionModalContent({ id, + title: existingTitle, + tvdbId: existingTvdbId, onModalClose, onDeleteImportListExclusionPress, }: EditImportListExclusionModalContentProps) { - const { isFetching, isSaving, item, error, saveError, ...otherProps } = - useSelector(createImportListExclusionSelector(id)); + const { + item, + isSaving, + saveError, + validationErrors, + validationWarnings, + updateValue, + save, + } = useManageImportListExclusion({ + id, + title: existingTitle, + tvdbId: existingTvdbId, + }); const { title, tvdbId } = item; - - const dispatch = useDispatch(); - const previousIsSaving = usePrevious(isSaving); - - const dispatchSetImportListExclusionValue = (payload: { - name: string; - value: string | number; - }) => { - // @ts-expect-error 'setImportListExclusionValue' isn't typed yet - dispatch(setImportListExclusionValue(payload)); - }; + const wasSaving = usePrevious(isSaving); useEffect(() => { - if (!id) { - Object.entries(newImportListExclusion).forEach(([name, value]) => { - dispatchSetImportListExclusionValue({ name, value }); - }); - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); - - useEffect(() => { - if (previousIsSaving && !isSaving && !saveError) { + if (wasSaving && !isSaving && !saveError) { onModalClose(); } - }, [previousIsSaving, isSaving, saveError, onModalClose]); + }, [isSaving, wasSaving, saveError, onModalClose]); - const onSavePress = useCallback(() => { - dispatch(saveImportListExclusion({ id })); - }, [dispatch, id]); - - const onInputChange = useCallback( - (change: InputChanged) => { - // @ts-expect-error 'setImportListExclusionValue' isn't typed yet - dispatch(setImportListExclusionValue(change)); + const handleInputChange = useCallback( + ({ name, value }: InputChanged) => { + updateValue(name, value); }, - [dispatch] + [updateValue] ); + const handleSavePress = useCallback(() => { + save(); + }, [save]); + return ( @@ -118,42 +73,35 @@ function EditImportListExclusionModalContent({ : translate('AddImportListExclusion')} - - {isFetching ? : null} + +
+ + {translate('Title')} - {!isFetching && error ? ( - - {translate('AddImportListExclusionError')} - - ) : null} + + - {!isFetching && !error ? ( - - - {translate('Title')} + + {translate('TvdbId')} - - - - - {translate('TvdbId')} - - - - - ) : null} + +
+
@@ -172,7 +120,7 @@ function EditImportListExclusionModalContent({ {translate('Save')} diff --git a/frontend/src/Settings/ImportLists/ImportListExclusions/ImportListExclusionRow.tsx b/frontend/src/Settings/ImportLists/ImportListExclusions/ImportListExclusionRow.tsx index 176a558a2..6e09c917c 100644 --- a/frontend/src/Settings/ImportLists/ImportListExclusions/ImportListExclusionRow.tsx +++ b/frontend/src/Settings/ImportLists/ImportListExclusions/ImportListExclusionRow.tsx @@ -1,5 +1,4 @@ import React, { useCallback } from 'react'; -import { useDispatch } from 'react-redux'; import { useSelect } from 'App/Select/SelectContext'; import IconButton from 'Components/Link/IconButton'; import ConfirmModal from 'Components/Modal/ConfirmModal'; @@ -8,23 +7,30 @@ 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 { + ImportListExclusion, + useDeleteImportListExclusion, +} from './useImportListExclusions'; import styles from './ImportListExclusionRow.css'; -type ImportListExclusionRowProps = ImportListExclusion; +interface ImportListExclusionRowProps extends ImportListExclusion { + onModalClose: () => void; +} function ImportListExclusionRow({ id, tvdbId, title, + onModalClose, }: ImportListExclusionRowProps) { const { toggleSelected, useIsSelected } = useSelect(); const isSelected = useIsSelected(id); + const { deleteImportListExclusion } = useDeleteImportListExclusion(id); + const handleSelectedChange = useCallback( ({ id, value, shiftKey = false }: SelectStateInputProps) => { toggleSelected({ @@ -36,14 +42,17 @@ function ImportListExclusionRow({ [toggleSelected] ); - const dispatch = useDispatch(); - const [ isEditImportListExclusionModalOpen, setEditImportListExclusionModalOpen, setEditImportListExclusionModalClosed, ] = useModalOpenState(false); + const handleEditModalClose = useCallback(() => { + setEditImportListExclusionModalClosed(); + onModalClose(); + }, [setEditImportListExclusionModalClosed, onModalClose]); + const [ isDeleteImportListExclusionModalOpen, setDeleteImportListExclusionModalOpen, @@ -51,8 +60,8 @@ function ImportListExclusionRow({ ] = useModalOpenState(false); const handleDeletePress = useCallback(() => { - dispatch(deleteImportListExclusion({ id })); - }, [id, dispatch]); + deleteImportListExclusion(); + }, [deleteImportListExclusion]); return ( @@ -74,8 +83,10 @@ function ImportListExclusionRow({ diff --git a/frontend/src/Settings/ImportLists/ImportListExclusions/ImportListExclusions.tsx b/frontend/src/Settings/ImportLists/ImportListExclusions/ImportListExclusions.tsx index f4fd46903..954cf8b7f 100644 --- a/frontend/src/Settings/ImportLists/ImportListExclusions/ImportListExclusions.tsx +++ b/frontend/src/Settings/ImportLists/ImportListExclusions/ImportListExclusions.tsx @@ -1,8 +1,5 @@ import React, { useCallback, useEffect, useState } from 'react'; -import { useDispatch, useSelector } from 'react-redux'; -import { createSelector } from 'reselect'; import { SelectProvider, useSelect } from 'App/Select/SelectContext'; -import AppState from 'App/State/AppState'; import FieldSet from 'Components/FieldSet'; import IconButton from 'Components/Link/IconButton'; import SpinnerButton from 'Components/Link/SpinnerButton'; @@ -14,21 +11,9 @@ 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 { icons, kinds } from 'Helpers/Props'; import { SortDirection } from 'Helpers/Props/sortDirections'; -import { - bulkDeleteImportListExclusions, - clearImportListExclusions, - fetchImportListExclusions, - gotoImportListExclusionPage, - setImportListExclusionSort, - setImportListExclusionTableOption, -} from 'Store/Actions/Settings/importListExclusions'; -import ImportListExclusion from 'typings/ImportListExclusion'; import { CheckInputChanged } from 'typings/inputs'; import { TableOptionsChangePayload } from 'typings/Table'; import { @@ -37,7 +22,16 @@ import { } from 'Utilities/pagePopulator'; import translate from 'Utilities/String/translate'; import EditImportListExclusionModal from './EditImportListExclusionModal'; +import { + setImportListExclusionOption, + setImportListExclusionSort, + useImportListExclusionOptions, +} from './importListExclusionOptionsStore'; import ImportListExclusionRow from './ImportListExclusionRow'; +import useImportListExclusions, { + ImportListExclusion, + useDeleteImportListExclusions, +} from './useImportListExclusions'; import styles from './ImportListExclusions.css'; const COLUMNS: Column[] = [ @@ -62,40 +56,27 @@ const COLUMNS: Column[] = [ }, ]; -function createImportListExclusionsSelector() { - return createSelector( - (state: AppState) => state.settings.importListExclusions, - (importListExclusions) => { - return { - ...importListExclusions, - }; - } - ); -} - function ImportListExclusionsContent() { - const requestCurrentPage = useCurrentPage(); - const { - isFetching, - isPopulated, - items, - pageSize, - sortKey, - error, - sortDirection, - page, + records, totalPages, totalRecords, - isDeleting, - deleteError, - } = useSelector(createImportListExclusionsSelector()); + isFetching, + isFetched, + isLoading, + error, + page, + goToPage, + refetch, + } = useImportListExclusions(); - const dispatch = useDispatch(); + const { pageSize, sortKey, sortDirection } = useImportListExclusionOptions(); + + const { deleteImportListExclusions, isDeleting } = + useDeleteImportListExclusions(); const [isConfirmDeleteModalOpen, setIsConfirmDeleteModalOpen] = useState(false); - const previousIsDeleting = usePrevious(isDeleting); const { allSelected, @@ -119,100 +100,64 @@ function ImportListExclusionsContent() { const handleDeleteSelectedPress = useCallback(() => { setIsConfirmDeleteModalOpen(true); - }, [setIsConfirmDeleteModalOpen]); + }, []); const handleDeleteSelectedConfirmed = useCallback(() => { - dispatch(bulkDeleteImportListExclusions({ ids: getSelectedIds() })); + deleteImportListExclusions({ ids: getSelectedIds() }); setIsConfirmDeleteModalOpen(false); - }, [getSelectedIds, setIsConfirmDeleteModalOpen, dispatch]); + unselectAll(); + }, [getSelectedIds, deleteImportListExclusions, unselectAll]); const handleConfirmDeleteModalClose = useCallback(() => { setIsConfirmDeleteModalOpen(false); - }, [setIsConfirmDeleteModalOpen]); - - const { - handleFirstPagePress, - handlePreviousPagePress, - handleNextPagePress, - handleLastPagePress, - handlePageSelect, - } = usePaging({ - page, - totalPages, - gotoPage: gotoImportListExclusionPage, - }); + }, []); const handleSortPress = useCallback( (sortKey: string, sortDirection?: SortDirection) => { - dispatch(setImportListExclusionSort({ sortKey, sortDirection })); + setImportListExclusionSort({ sortKey, sortDirection }); }, - [dispatch] + [] ); const handleTableOptionChange = useCallback( (payload: TableOptionsChangePayload) => { - dispatch(setImportListExclusionTableOption(payload)); - if (payload.pageSize) { - dispatch(gotoImportListExclusionPage({ page: 1 })); + setImportListExclusionOption('pageSize', payload.pageSize as number); + goToPage(1); } }, - [dispatch] + [goToPage] ); - 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) { - unselectAll(); - - dispatch(fetchImportListExclusions()); - } - }, [ - previousIsDeleting, - isDeleting, - deleteError, - items, - dispatch, - unselectAll, - ]); - const [ isAddImportListExclusionModalOpen, setAddImportListExclusionModalOpen, setAddImportListExclusionModalClosed, ] = useModalOpenState(false); - const isFetchingForFirstTime = isFetching && !isPopulated; + const handleAddModalClose = useCallback(() => { + setAddImportListExclusionModalClosed(); + refetch(); + }, [setAddImportListExclusionModalClosed, refetch]); + + useEffect(() => { + const repopulate = () => { + refetch(); + }; + + registerPagePopulator(repopulate); + + return () => { + unregisterPagePopulator(repopulate); + }; + }, [refetch]); return (
- {items.map((item) => { - return ; + {records.map((item) => { + return ( + + ); })} @@ -260,16 +211,12 @@ function ImportListExclusionsContent() { totalPages={totalPages} totalRecords={totalRecords} isFetching={isFetching} - onFirstPagePress={handleFirstPagePress} - onPreviousPagePress={handlePreviousPagePress} - onNextPagePress={handleNextPagePress} - onLastPagePress={handleLastPagePress} - onPageSelect={handlePageSelect} + onPageSelect={goToPage} /> + items={records}> ); diff --git a/frontend/src/Settings/ImportLists/ImportListExclusions/importListExclusionOptionsStore.ts b/frontend/src/Settings/ImportLists/ImportListExclusions/importListExclusionOptionsStore.ts new file mode 100644 index 000000000..d0c043d1f --- /dev/null +++ b/frontend/src/Settings/ImportLists/ImportListExclusions/importListExclusionOptionsStore.ts @@ -0,0 +1,25 @@ +import { createOptionsStore } from 'Helpers/Hooks/useOptionsStore'; +import { SortDirection } from 'Helpers/Props/sortDirections'; + +export interface ImportListExclusionOptions { + pageSize: number; + sortKey: string; + sortDirection: SortDirection; +} + +const { useOptions, setOptions, setOption, setSort } = + createOptionsStore( + 'import_list_exclusion_options', + () => { + return { + pageSize: 20, + sortKey: 'id', + sortDirection: 'descending', + }; + } + ); + +export const useImportListExclusionOptions = useOptions; +export const setImportListExclusionOptions = setOptions; +export const setImportListExclusionOption = setOption; +export const setImportListExclusionSort = setSort; diff --git a/frontend/src/Settings/ImportLists/ImportListExclusions/useImportListExclusions.ts b/frontend/src/Settings/ImportLists/ImportListExclusions/useImportListExclusions.ts new file mode 100644 index 000000000..076677b40 --- /dev/null +++ b/frontend/src/Settings/ImportLists/ImportListExclusions/useImportListExclusions.ts @@ -0,0 +1,163 @@ +import { keepPreviousData, useQueryClient } from '@tanstack/react-query'; +import { useCallback, useMemo } from 'react'; +import ModelBase from 'App/ModelBase'; +import useApiMutation from 'Helpers/Hooks/useApiMutation'; +import usePage from 'Helpers/Hooks/usePage'; +import usePagedApiQuery from 'Helpers/Hooks/usePagedApiQuery'; +import { usePendingChangesStore } from 'Helpers/Hooks/usePendingChangesStore'; +import selectSettings from 'Store/Selectors/selectSettings'; +import { useImportListExclusionOptions } from './importListExclusionOptionsStore'; + +export interface ImportListExclusion extends ModelBase { + tvdbId: number; + title: string; +} + +const PATH = '/importlistexclusion'; + +const NEW_IMPORT_LIST_EXCLUSION = { + title: '', + tvdbId: 0, +}; + +interface BulkImportListExclusionData { + ids: number[]; +} + +const useImportListExclusions = () => { + const { page, goToPage } = usePage('importListExclusion'); + const { pageSize, sortKey, sortDirection } = useImportListExclusionOptions(); + + const { refetch, ...query } = usePagedApiQuery({ + path: PATH, + page, + pageSize, + sortKey, + sortDirection, + queryOptions: { + placeholderData: keepPreviousData, + }, + }); + + return { + ...query, + goToPage, + page, + refetch, + }; +}; + +export default useImportListExclusions; + +interface ManageImportListExclusionOptions { + id?: number; + title?: string; + tvdbId?: number; +} + +export const useManageImportListExclusion = ({ + id, + title, + tvdbId, +}: ManageImportListExclusionOptions) => { + const queryClient = useQueryClient(); + + const item = useMemo(() => { + return id + ? { title: title ?? '', tvdbId: tvdbId ?? 0 } + : NEW_IMPORT_LIST_EXCLUSION; + }, [id, title, tvdbId]); + + const { pendingChanges, setPendingChange } = + usePendingChangesStore({}); + + const { + mutate, + isPending: isSaving, + error: saveError, + } = useApiMutation({ + path: id ? `${PATH}/${id}` : PATH, + method: id ? 'PUT' : 'POST', + mutationOptions: { + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: [PATH] }); + }, + }, + }); + + const { settings, validationErrors, validationWarnings } = useMemo(() => { + return selectSettings(item, pendingChanges, saveError); + }, [item, pendingChanges, saveError]); + + const updateValue = useCallback( + (name: string, value: unknown) => { + // @ts-expect-error - name is not yet typed + setPendingChange(name, value); + }, + [setPendingChange] + ); + + const save = useCallback(() => { + const payload = { + ...item, + ...pendingChanges, + } as ImportListExclusion; + + if (id) { + payload.id = id; + } + + mutate(payload); + }, [id, item, pendingChanges, mutate]); + + return { + item: settings, + isSaving, + saveError, + validationErrors, + validationWarnings, + updateValue, + save, + }; +}; + +export const useDeleteImportListExclusion = (id: number) => { + const queryClient = useQueryClient(); + + const { mutate, isPending } = useApiMutation({ + path: `${PATH}/${id}`, + method: 'DELETE', + mutationOptions: { + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: [PATH] }); + }, + }, + }); + + return { + deleteImportListExclusion: mutate, + isDeleting: isPending, + }; +}; + +export const useDeleteImportListExclusions = () => { + const queryClient = useQueryClient(); + + const { mutate, isPending } = useApiMutation< + unknown, + BulkImportListExclusionData + >({ + path: `${PATH}/bulk`, + method: 'DELETE', + mutationOptions: { + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: [PATH] }); + }, + }, + }); + + return { + deleteImportListExclusions: mutate, + isDeleting: isPending, + }; +}; diff --git a/frontend/src/Store/Actions/Settings/importListExclusions.js b/frontend/src/Store/Actions/Settings/importListExclusions.js deleted file mode 100644 index a89d65208..000000000 --- a/frontend/src/Store/Actions/Settings/importListExclusions.js +++ /dev/null @@ -1,110 +0,0 @@ -import { createAction } from 'redux-actions'; -import createBulkRemoveItemHandler from 'Store/Actions/Creators/createBulkRemoveItemHandler'; -import createRemoveItemHandler from 'Store/Actions/Creators/createRemoveItemHandler'; -import createSaveProviderHandler from 'Store/Actions/Creators/createSaveProviderHandler'; -import createServerSideCollectionHandlers from 'Store/Actions/Creators/createServerSideCollectionHandlers'; -import createClearReducer from 'Store/Actions/Creators/Reducers/createClearReducer'; -import createSetSettingValueReducer from 'Store/Actions/Creators/Reducers/createSetSettingValueReducer'; -import createSetTableOptionReducer from 'Store/Actions/Creators/Reducers/createSetTableOptionReducer'; -import { createThunk, handleThunks } from 'Store/thunks'; -import serverSideCollectionHandlers from 'Utilities/State/serverSideCollectionHandlers'; - -// -// Variables - -const section = 'settings.importListExclusions'; - -// -// Actions Types - -export const FETCH_IMPORT_LIST_EXCLUSIONS = 'settings/importListExclusions/fetchImportListExclusions'; -export const GOTO_IMPORT_LIST_EXCLUSION_PAGE = 'settings/importListExclusions/gotoImportListExclusionPage'; -export const SET_IMPORT_LIST_EXCLUSION_SORT = 'settings/importListExclusions/setImportListExclusionSort'; -export const SAVE_IMPORT_LIST_EXCLUSION = 'settings/importListExclusions/saveImportListExclusion'; -export const DELETE_IMPORT_LIST_EXCLUSION = 'settings/importListExclusions/deleteImportListExclusion'; -export const BULK_DELETE_IMPORT_LIST_EXCLUSIONS = 'settings/importListExclusions/bulkDeleteImportListExclusions'; -export const CLEAR_IMPORT_LIST_EXCLUSIONS = 'settings/importListExclusions/clearImportListExclusions'; - -export const SET_IMPORT_LIST_EXCLUSION_TABLE_OPTION = 'settings/importListExclusions/setImportListExclusionTableOption'; -export const SET_IMPORT_LIST_EXCLUSION_VALUE = 'settings/importListExclusions/setImportListExclusionValue'; - -// -// Action Creators - -export const fetchImportListExclusions = createThunk(FETCH_IMPORT_LIST_EXCLUSIONS); -export const gotoImportListExclusionPage = createThunk(GOTO_IMPORT_LIST_EXCLUSION_PAGE); -export const setImportListExclusionSort = createThunk(SET_IMPORT_LIST_EXCLUSION_SORT); -export const saveImportListExclusion = createThunk(SAVE_IMPORT_LIST_EXCLUSION); -export const deleteImportListExclusion = createThunk(DELETE_IMPORT_LIST_EXCLUSION); -export const bulkDeleteImportListExclusions = createThunk(BULK_DELETE_IMPORT_LIST_EXCLUSIONS); -export const clearImportListExclusions = createAction(CLEAR_IMPORT_LIST_EXCLUSIONS); - -export const setImportListExclusionTableOption = createAction(SET_IMPORT_LIST_EXCLUSION_TABLE_OPTION); -export const setImportListExclusionValue = createAction(SET_IMPORT_LIST_EXCLUSION_VALUE, (payload) => { - return { - section, - ...payload - }; -}); - -// -// Details - -export default { - - // - // State - - defaultState: { - isFetching: false, - isPopulated: false, - error: null, - pageSize: 20, - items: [], - isSaving: false, - saveError: null, - isDeleting: false, - deleteError: null, - pendingChanges: {} - }, - - // - // Action Handlers - - actionHandlers: handleThunks({ - ...createServerSideCollectionHandlers( - section, - '/importlistexclusion/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, '/importlistexclusion'), - [DELETE_IMPORT_LIST_EXCLUSION]: createRemoveItemHandler(section, '/importlistexclusion'), - [BULK_DELETE_IMPORT_LIST_EXCLUSIONS]: createBulkRemoveItemHandler(section, '/importlistexclusion/bulk') - }), - - // - // Reducers - - reducers: { - [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 19a34d9eb..294d4dca0 100644 --- a/frontend/src/Store/Actions/settingsActions.js +++ b/frontend/src/Store/Actions/settingsActions.js @@ -7,7 +7,6 @@ import customFormatSpecifications from './Settings/customFormatSpecifications'; import delayProfiles from './Settings/delayProfiles'; import downloadClientOptions from './Settings/downloadClientOptions'; import downloadClients from './Settings/downloadClients'; -import importListExclusions from './Settings/importListExclusions'; import importListOptions from './Settings/importListOptions'; import importLists from './Settings/importLists'; @@ -20,7 +19,6 @@ export * from './Settings/downloadClients'; export * from './Settings/downloadClientOptions'; export * from './Settings/importListOptions'; export * from './Settings/importLists'; -export * from './Settings/importListExclusions'; // // Variables @@ -40,7 +38,6 @@ export const defaultState = { downloadClients: downloadClients.defaultState, downloadClientOptions: downloadClientOptions.defaultState, importLists: importLists.defaultState, - importListExclusions: importListExclusions.defaultState, importListOptions: importListOptions.defaultState }; @@ -60,7 +57,6 @@ export const actionHandlers = handleThunks({ ...downloadClients.actionHandlers, ...downloadClientOptions.actionHandlers, ...importLists.actionHandlers, - ...importListExclusions.actionHandlers, ...importListOptions.actionHandlers }); @@ -76,7 +72,6 @@ export const reducers = createHandleActions({ ...downloadClients.reducers, ...downloadClientOptions.reducers, ...importLists.reducers, - ...importListExclusions.reducers, ...importListOptions.reducers }, defaultState, section); diff --git a/frontend/src/typings/ImportListExclusion.ts b/frontend/src/typings/ImportListExclusion.ts deleted file mode 100644 index ec9add4dd..000000000 --- a/frontend/src/typings/ImportListExclusion.ts +++ /dev/null @@ -1,6 +0,0 @@ -import ModelBase from 'App/ModelBase'; - -export default interface ImportListExclusion extends ModelBase { - tvdbId: number; - title: string; -}