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) => { 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) => { 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({ queryKey: ['/manualimport'], })[0]; if (!currentData) { return; } const requestPayload = ids.reduce( (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, }; };