Files
requestarr/src/hooks/useDiscover.ts
Brandon Cohen 14519ef555 fix(recommendations): only load more titles if there can be more than 40 (#2749)
* fix: fixed recommendations page causing infinite network requests to tmdb api

TMDB API would only return 40 results and the recommendations page expected more. This would cause
an infinite amount of network requests. I set a limit specifically for this solving the problem.

fix #2710
2022-05-13 23:15:53 +04:00

102 lines
2.4 KiB
TypeScript

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) ||
(!!data && (data[data?.length - 1]?.totalResults ?? 0) < 41);
return {
isLoadingInitialData,
isLoadingMore,
fetchMore,
isEmpty,
isReachingEnd,
error,
titles,
firstResultData: data?.[0],
};
};
export default useDiscover;