You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
overseerr/src/hooks/useDiscover.ts

100 lines
2.4 KiB

import useSWRInfinite from 'swr/infinite';
import { MediaStatus } from '../../server/constants/media';
import useSettings from './useSettings';
export interface BaseSearchResult<T> {
page: number;
totalResults: number;
totalPages: number;
results: T[];
}
interface BaseMedia {
mediaType: string;
mediaInfo?: {
status: MediaStatus;
};
}
interface DiscoverResult<T, S> {
isLoadingInitialData: boolean;
isLoadingMore: boolean;
fetchMore: () => void;
isEmpty: boolean;
isReachingEnd: boolean;
error: unknown;
titles: T[];
firstResultData?: BaseSearchResult<T> & S;
}
const useDiscover = <T extends BaseMedia, S = Record<string, never>>(
endpoint: string,
options?: Record<string, unknown>,
{ hideAvailable = true } = {}
): DiscoverResult<T, S> => {
const settings = useSettings();
const { data, error, size, setSize, isValidating } = useSWRInfinite<
BaseSearchResult<T> & S
>(
(pageIndex: number, previousPageData) => {
if (previousPageData && pageIndex + 1 > previousPageData.totalPages) {
return null;
}
const params: Record<string, unknown> = {
page: pageIndex + 1,
...options,
};
const finalQueryString = Object.keys(params)
.map((paramKey) => `${paramKey}=${params[paramKey]}`)
.join('&');
return `${endpoint}?${finalQueryString}`;
},
{
initialSize: 3,
}
);
const isLoadingInitialData = !data && !error;
const isLoadingMore =
isLoadingInitialData ||
(size > 0 &&
!!data &&
typeof data[size - 1] === 'undefined' &&
isValidating);
const fetchMore = () => {
setSize(size + 1);
};
let titles = (data ?? []).reduce((a, v) => [...a, ...v.results], [] as T[]);
if (settings.currentSettings.hideAvailable && hideAvailable) {
titles = titles.filter(
(i) =>
(i.mediaType === 'movie' || i.mediaType === 'tv') &&
i.mediaInfo?.status !== MediaStatus.AVAILABLE &&
i.mediaInfo?.status !== MediaStatus.PARTIALLY_AVAILABLE
);
}
const isEmpty = !isLoadingInitialData && titles?.length === 0;
const isReachingEnd =
isEmpty || (!!data && (data[data?.length - 1]?.results.length ?? 0) < 20);
return {
isLoadingInitialData,
isLoadingMore,
fetchMore,
isEmpty,
isReachingEnd,
error,
titles,
firstResultData: data?.[0],
};
};
export default useDiscover;