mirror of
https://github.com/Sonarr/Sonarr.git
synced 2026-04-22 22:16:13 -04:00
211 lines
5.7 KiB
TypeScript
211 lines
5.7 KiB
TypeScript
import { useQueryClient } from '@tanstack/react-query';
|
|
import { useCallback, useMemo } from 'react';
|
|
import ModelBase from 'App/ModelBase';
|
|
import useApiMutation from 'Helpers/Hooks/useApiMutation';
|
|
import useApiQuery from 'Helpers/Hooks/useApiQuery';
|
|
import Language from 'Language/Language';
|
|
import { QualityModel } from 'Quality/Quality';
|
|
import clientSideFilterAndSort from 'Utilities/Filter/clientSideFilterAndSort';
|
|
import InteractiveImport from './InteractiveImport';
|
|
import { useInteractiveImportOptions } from './interactiveImportOptionsStore';
|
|
import ReleaseType from './ReleaseType';
|
|
|
|
const DEFAULT_ITEMS: InteractiveImport[] = [];
|
|
|
|
interface InteractiveImportParams {
|
|
downloadIds?: string[];
|
|
seriesId?: number;
|
|
seasonNumber?: number;
|
|
folder?: string;
|
|
filterExistingFiles?: boolean;
|
|
}
|
|
|
|
const useInteractiveImport = (params: InteractiveImportParams) => {
|
|
const { sortKey, sortDirection } = useInteractiveImportOptions();
|
|
|
|
const { data, isFetching, isFetched, error, refetch } = useApiQuery<
|
|
InteractiveImport[]
|
|
>({
|
|
path: '/manualimport',
|
|
queryParams: { ...params },
|
|
queryOptions: {
|
|
// Set to 0 so we don't persist the data after the modal is closed and the query becomes inactive
|
|
gcTime: 0,
|
|
// Disable refetch on window focus to prevent refetching when the user switch tabs
|
|
refetchOnWindowFocus: false,
|
|
},
|
|
});
|
|
|
|
const items = data ?? DEFAULT_ITEMS;
|
|
const originalItems = [...items];
|
|
|
|
const { data: sortedItems } = useMemo(() => {
|
|
const sortPredicates = {
|
|
series: (item: InteractiveImport) => item.series?.title || '',
|
|
quality: (item: InteractiveImport) => item.quality?.quality?.name || '',
|
|
languages: (item: InteractiveImport) =>
|
|
item.languages?.map((l) => l.name).join(', ') || '',
|
|
};
|
|
|
|
return clientSideFilterAndSort(items, {
|
|
sortKey,
|
|
sortDirection,
|
|
sortPredicates,
|
|
});
|
|
}, [items, sortKey, sortDirection]);
|
|
|
|
return {
|
|
data: sortedItems,
|
|
originalItems,
|
|
isFetching,
|
|
isFetched,
|
|
error,
|
|
refetch,
|
|
};
|
|
};
|
|
|
|
export default useInteractiveImport;
|
|
|
|
export const useUpdateInteractiveImportItem = () => {
|
|
const queryClient = useQueryClient();
|
|
|
|
const updateInteractiveImportItem = useCallback(
|
|
(id: number, updates: Partial<InteractiveImport>) => {
|
|
queryClient.setQueriesData(
|
|
{ queryKey: ['/manualimport'] },
|
|
(oldData: InteractiveImport[] | undefined) => {
|
|
if (!oldData) {
|
|
return oldData;
|
|
}
|
|
|
|
return oldData.map((item) => {
|
|
return item.id === id ? { ...item, ...updates } : item;
|
|
});
|
|
}
|
|
);
|
|
},
|
|
[queryClient]
|
|
);
|
|
|
|
return { updateInteractiveImportItem };
|
|
};
|
|
|
|
export const useUpdateInteractiveImportItems = () => {
|
|
const queryClient = useQueryClient();
|
|
|
|
const updateInteractiveImportItems = useCallback(
|
|
(ids: number[], updates: Partial<InteractiveImport>) => {
|
|
queryClient.setQueriesData(
|
|
{ queryKey: ['/manualimport'] },
|
|
(oldData: InteractiveImport[] | undefined) => {
|
|
if (!oldData) {
|
|
return oldData;
|
|
}
|
|
|
|
return oldData.map((item) => {
|
|
return ids.includes(item.id) ? { ...item, ...updates } : item;
|
|
});
|
|
}
|
|
);
|
|
},
|
|
[queryClient]
|
|
);
|
|
|
|
return { updateInteractiveImportItems };
|
|
};
|
|
|
|
interface ReprocessInteractiveImportItem extends ModelBase {
|
|
path: string;
|
|
relativePath: string;
|
|
seriesId: number | undefined;
|
|
seasonNumber: number | undefined;
|
|
episodeIds: number[] | undefined;
|
|
quality: QualityModel | undefined;
|
|
languages: Language[];
|
|
releaseGroup: string | undefined;
|
|
downloadId: string | undefined;
|
|
indexerFlags: number;
|
|
releaseType: ReleaseType;
|
|
}
|
|
|
|
export const useReprocessInteractiveImportItems = () => {
|
|
const queryClient = useQueryClient();
|
|
|
|
const { mutate, isPending, error } = useApiMutation<
|
|
InteractiveImport[],
|
|
ReprocessInteractiveImportItem[]
|
|
>({
|
|
path: '/manualimport',
|
|
method: 'POST',
|
|
mutationOptions: {
|
|
onSuccess: (updatedItems) => {
|
|
queryClient.setQueriesData(
|
|
{ queryKey: ['/manualimport'] },
|
|
(oldData: InteractiveImport[] | undefined) => {
|
|
if (!oldData) {
|
|
return oldData;
|
|
}
|
|
|
|
return oldData.map((oldItem: InteractiveImport) => {
|
|
const reprocessedItem = updatedItems.find(
|
|
(updatedItem) => updatedItem.id === oldItem.id
|
|
);
|
|
|
|
return reprocessedItem ? reprocessedItem : oldItem;
|
|
});
|
|
}
|
|
);
|
|
},
|
|
},
|
|
});
|
|
|
|
const reprocessInteractiveImportItems = useCallback(
|
|
(ids: number[]) => {
|
|
const [, currentData] = queryClient.getQueriesData<InteractiveImport[]>({
|
|
queryKey: ['/manualimport'],
|
|
})[0];
|
|
|
|
if (!currentData) {
|
|
return;
|
|
}
|
|
|
|
const requestPayload = ids.reduce<ReprocessInteractiveImportItem[]>(
|
|
(acc, id) => {
|
|
const item = currentData.find((i) => i.id === id);
|
|
|
|
if (!item) {
|
|
return acc;
|
|
}
|
|
|
|
acc.push({
|
|
id,
|
|
path: item.path,
|
|
relativePath: item.relativePath,
|
|
seriesId: item.series ? item.series.id : undefined,
|
|
seasonNumber: item.seasonNumber,
|
|
episodeIds: (item.episodes || []).map((e) => e.id),
|
|
quality: item.quality,
|
|
languages: item.languages,
|
|
releaseGroup: item.releaseGroup,
|
|
indexerFlags: item.indexerFlags,
|
|
releaseType: item.releaseType,
|
|
downloadId: item.downloadId,
|
|
});
|
|
|
|
return acc;
|
|
},
|
|
[]
|
|
);
|
|
|
|
mutate(requestPayload);
|
|
},
|
|
[queryClient, mutate]
|
|
);
|
|
|
|
return {
|
|
reprocessInteractiveImportItems,
|
|
isReprocessing: isPending,
|
|
error,
|
|
};
|
|
};
|