A personal media tracker built on the AT Protocol opnshelf.xyz
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

fix: use inline filmography with client-side pagination for person page

The generated infiniteQueryOptions lacked initialPageParam and
getNextPageParam, causing the filmography grid to always appear empty.
Switch to the already-loaded person.filmography data with a simple
client-side Load more button. Also fix the usePersonFilmography hook
for future consumers.

+34 -34
+7
apps/web/src/lib/hooks/usePerson.ts
··· 22 22 query: { pageSize }, 23 23 }), 24 24 enabled: !!personId, 25 + initialPageParam: 1, 26 + getNextPageParam: (lastPage) => { 27 + if (lastPage.page < lastPage.totalPages) { 28 + return lastPage.page + 1; 29 + } 30 + return undefined; 31 + }, 25 32 }); 26 33 }
+27 -34
apps/web/src/routes/people/$personId/$personName.tsx
··· 1 - import { 2 - peopleControllerGetPersonDetailsOptions, 3 - peopleControllerGetPersonFilmographyInfiniteOptions, 4 - } from "@opnshelf/api"; 5 - import { useInfiniteQuery } from "@tanstack/react-query"; 1 + import { peopleControllerGetPersonDetailsOptions } from "@opnshelf/api"; 6 2 import { createFileRoute, Link } from "@tanstack/react-router"; 7 3 import { 8 4 Calendar, 9 5 ChevronLeft, 10 6 Clapperboard, 11 - Loader2, 12 7 MapPin, 13 8 Star, 14 9 } from "lucide-react"; 15 - import { useMemo } from "react"; 10 + import { useMemo, useState } from "react"; 16 11 import { setupApiClient } from "#/lib/api"; 17 12 import { useAuth } from "#/lib/auth-context"; 18 13 import { formatDate } from "#/lib/date-utils"; ··· 24 19 import LoadingState from "../../../components/LoadingState"; 25 20 26 21 setupApiClient(); 22 + 23 + const FILMOGRAPHY_PAGE_SIZE = 20; 27 24 28 25 export const Route = createFileRoute("/people/$personId/$personName")({ 29 26 loader: async ({ context, params }) => { ··· 54 51 const userTimezone = userSettings?.timezone; 55 52 56 53 const { data: person, isLoading, error } = usePersonDetails(personId); 54 + const [filmographyLimit, setFilmographyLimit] = useState( 55 + FILMOGRAPHY_PAGE_SIZE, 56 + ); 57 57 58 - const { 59 - data: filmographyData, 60 - fetchNextPage, 61 - hasNextPage, 62 - isFetchingNextPage, 63 - } = useInfiniteQuery({ 64 - ...peopleControllerGetPersonFilmographyInfiniteOptions({ 65 - path: { personId }, 66 - query: { pageSize: 20 }, 67 - }), 68 - enabled: !!personId, 69 - }); 58 + const allFilmography = useMemo(() => { 59 + if (!person?.filmography) return []; 60 + return [...person.filmography].sort((a, b) => { 61 + const dateA = a.release_date || a.first_air_date || ""; 62 + const dateB = b.release_date || b.first_air_date || ""; 63 + return dateB.localeCompare(dateA); 64 + }); 65 + }, [person?.filmography]); 70 66 71 67 const filmographyItems = useMemo(() => { 72 - if (!filmographyData?.pages) return []; 73 - return filmographyData.pages.flatMap((page) => page.items); 74 - }, [filmographyData]); 68 + return allFilmography.slice(0, filmographyLimit); 69 + }, [allFilmography, filmographyLimit]); 70 + 71 + const hasMoreFilmography = filmographyItems.length < allFilmography.length; 75 72 76 73 const knownForItems = useMemo(() => { 77 74 if (!person?.filmography) return []; ··· 254 251 {/* Full Filmography */} 255 252 <section> 256 253 <h2 className="mb-4 text-display-3">Filmography</h2> 257 - {filmographyItems.length === 0 && !isFetchingNextPage ? ( 254 + {allFilmography.length === 0 ? ( 258 255 <p className="text-(--foreground-muted) text-sm"> 259 256 No filmography available. 260 257 </p> ··· 282 279 /> 283 280 ))} 284 281 </div> 285 - {hasNextPage && ( 282 + {hasMoreFilmography && ( 286 283 <div className="mt-6 flex justify-center"> 287 284 <button 288 285 type="button" 289 - onClick={() => fetchNextPage()} 290 - disabled={isFetchingNextPage} 286 + onClick={() => 287 + setFilmographyLimit( 288 + (prev) => prev + FILMOGRAPHY_PAGE_SIZE, 289 + ) 290 + } 291 291 className="btn btn-secondary gap-2" 292 292 > 293 - {isFetchingNextPage ? ( 294 - <> 295 - <Loader2 className="size-4 animate-spin" /> 296 - Loading... 297 - </> 298 - ) : ( 299 - "Load more" 300 - )} 293 + Load more 301 294 </button> 302 295 </div> 303 296 )}