mirror of
https://github.com/jakejarvis/sofa.git
synced 2026-08-29 02:45:39 -04:00
fix: throw notFound() for missing titles/people and improve error handling
- Web: wrap title and person detail loaders in try/catch; throw `notFound()` when the API returns `TITLE_NOT_FOUND` or `PERSON_NOT_FOUND` so TanStack Router renders the 404 component instead of crashing - Web: fix `QuickAddButton` to reset local status to `null` (not skip the update) when `userStatus` becomes undefined, so removing a title from the library reflects immediately - Native: add a generic error state to the title detail screen with a "Try again" / "Go back" recovery UI, distinct from the existing "title not found" state - Native: pass `getItemType` to `FlashList` in the search screen and recently-viewed list to enable correct item recycling across person and title cell types
This commit is contained in:
@@ -89,9 +89,7 @@ function QuickAddButton({ id, userStatus }: { id: string; userStatus?: TitleStat
|
||||
|
||||
// Sync local state when prop changes (e.g. after navigation or SWR revalidation)
|
||||
useEffect(() => {
|
||||
if (userStatus) {
|
||||
setAddedStatus(userStatus);
|
||||
}
|
||||
setAddedStatus(userStatus ?? null);
|
||||
}, [userStatus]);
|
||||
|
||||
const quickAddMutation = useMutation(
|
||||
|
||||
@@ -1,25 +1,33 @@
|
||||
import { Trans } from "@lingui/react/macro";
|
||||
import { createFileRoute, Link } from "@tanstack/react-router";
|
||||
import { createFileRoute, Link, notFound } from "@tanstack/react-router";
|
||||
|
||||
import { PersonDetailClient, PersonDetailSkeleton } from "@/components/people/person-detail-client";
|
||||
import { getAppErrorCode } from "@/lib/error-messages";
|
||||
import { orpc } from "@/lib/orpc/client";
|
||||
|
||||
export const Route = createFileRoute("/_app/people/$id")({
|
||||
staleTime: 60_000,
|
||||
loader: async ({ params, context }) => {
|
||||
const data = await context.queryClient.ensureInfiniteQueryData(
|
||||
orpc.people.detail.infiniteOptions({
|
||||
input: (pageParam: number) => ({
|
||||
id: params.id,
|
||||
page: pageParam,
|
||||
limit: 20,
|
||||
try {
|
||||
const data = await context.queryClient.ensureInfiniteQueryData(
|
||||
orpc.people.detail.infiniteOptions({
|
||||
input: (pageParam: number) => ({
|
||||
id: params.id,
|
||||
page: pageParam,
|
||||
limit: 20,
|
||||
}),
|
||||
initialPageParam: 1,
|
||||
getNextPageParam: (lastPage) =>
|
||||
lastPage.page < lastPage.totalPages ? lastPage.page + 1 : undefined,
|
||||
}),
|
||||
initialPageParam: 1,
|
||||
getNextPageParam: (lastPage) =>
|
||||
lastPage.page < lastPage.totalPages ? lastPage.page + 1 : undefined,
|
||||
}),
|
||||
);
|
||||
return { personName: data.pages[0]?.person.name };
|
||||
);
|
||||
return { personName: data.pages[0]?.person.name };
|
||||
} catch (error) {
|
||||
if (getAppErrorCode(error) === "PERSON_NOT_FOUND") {
|
||||
throw notFound();
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
head: ({ loaderData }) => {
|
||||
if (!loaderData?.personName) return {};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Trans } from "@lingui/react/macro";
|
||||
import { createFileRoute, Link } from "@tanstack/react-router";
|
||||
import { createFileRoute, Link, notFound } from "@tanstack/react-router";
|
||||
|
||||
import { TitleActions } from "@/components/titles/title-actions";
|
||||
import { TitleAvailability } from "@/components/titles/title-availability";
|
||||
@@ -11,21 +11,29 @@ import { TitleRecommendations } from "@/components/titles/title-recommendations"
|
||||
import { TitleSeasons } from "@/components/titles/title-seasons";
|
||||
import { TitleTheme } from "@/components/titles/title-theme";
|
||||
import { Skeleton } from "@/components/ui/skeleton";
|
||||
import { getAppErrorCode } from "@/lib/error-messages";
|
||||
import { orpc } from "@/lib/orpc/client";
|
||||
import { getThemeCssProperties } from "@/lib/theme";
|
||||
|
||||
export const Route = createFileRoute("/_app/titles/$id")({
|
||||
staleTime: 60_000,
|
||||
loader: async ({ params, context }) => {
|
||||
const [titleResult, userInfo] = await Promise.all([
|
||||
context.queryClient.ensureQueryData(
|
||||
orpc.titles.detail.queryOptions({ input: { id: params.id } }),
|
||||
),
|
||||
context.queryClient
|
||||
.ensureQueryData(orpc.titles.userInfo.queryOptions({ input: { id: params.id } }))
|
||||
.catch(() => null),
|
||||
]);
|
||||
return { ...titleResult, userInfo };
|
||||
try {
|
||||
const [titleResult, userInfo] = await Promise.all([
|
||||
context.queryClient.ensureQueryData(
|
||||
orpc.titles.detail.queryOptions({ input: { id: params.id } }),
|
||||
),
|
||||
context.queryClient
|
||||
.ensureQueryData(orpc.titles.userInfo.queryOptions({ input: { id: params.id } }))
|
||||
.catch(() => null),
|
||||
]);
|
||||
return { ...titleResult, userInfo };
|
||||
} catch (error) {
|
||||
if (getAppErrorCode(error) === "TITLE_NOT_FOUND") {
|
||||
throw notFound();
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
head: ({ loaderData }) => {
|
||||
if (!loaderData) return {};
|
||||
|
||||
Reference in New Issue
Block a user