From 11a55d10aea56d1f2a70d630b31fe297fa5aa39e Mon Sep 17 00:00:00 2001 From: Alan Daniel Date: Sat, 11 Apr 2026 01:22:13 -0400 Subject: [PATCH] Fix detail pages hanging on Cloudflare Workers Route loaders for pull, issue, and review detail pages used ensureQueryData which blocked rendering on server function calls. On Cloudflare Workers, these async operations created cross-request promises that crashed the Worker (Error 1101) and returned undefined data to the client, causing infinite loading. Replace async loaders with synchronous cache reads via getQueryData. Components already fetch data client-side via useQuery with proper skeleton and error boundary handling. Add pendingComponent to all three detail routes for loading feedback during navigation. --- .../$owner/$repo/issues.$issueId.tsx | 22 +++++----- .../_protected/$owner/$repo/pull.$pullId.tsx | 32 +++++--------- .../$owner/$repo/review.$pullId.tsx | 42 +++++-------------- 3 files changed, 31 insertions(+), 65 deletions(-) diff --git a/apps/dashboard/src/routes/_protected/$owner/$repo/issues.$issueId.tsx b/apps/dashboard/src/routes/_protected/$owner/$repo/issues.$issueId.tsx index 323fc6d..fa30796 100644 --- a/apps/dashboard/src/routes/_protected/$owner/$repo/issues.$issueId.tsx +++ b/apps/dashboard/src/routes/_protected/$owner/$repo/issues.$issueId.tsx @@ -1,26 +1,23 @@ import { createFileRoute } from "@tanstack/react-router"; import { IssueDetailPage } from "#/components/issues/detail/issue-detail-page"; +import { DashboardContentLoading } from "#/components/layouts/dashboard-content-loading"; import { githubIssuePageQueryOptions } from "#/lib/github.query"; import { buildSeo, formatPageTitle, summarizeText } from "#/lib/seo"; export const Route = createFileRoute( "/_protected/$owner/$repo/issues/$issueId", )({ - loader: async ({ context, params }) => { + loader: ({ context, params }) => { const issueNumber = Number(params.issueId); const scope = { userId: context.user.id }; - const pageOptions = githubIssuePageQueryOptions(scope, { - owner: params.owner, - repo: params.repo, - issueNumber, - }); - - const cachedData = context.queryClient.getQueryData(pageOptions.queryKey); - if (cachedData !== undefined) { - return cachedData; - } - return context.queryClient.ensureQueryData(pageOptions); + return context.queryClient.getQueryData( + githubIssuePageQueryOptions(scope, { + owner: params.owner, + repo: params.repo, + issueNumber, + }).queryKey, + ); }, head: ({ loaderData, match, params }) => { const issue = loaderData?.detail; @@ -40,5 +37,6 @@ export const Route = createFileRoute( robots: "noindex", }); }, + pendingComponent: DashboardContentLoading, component: IssueDetailPage, }); diff --git a/apps/dashboard/src/routes/_protected/$owner/$repo/pull.$pullId.tsx b/apps/dashboard/src/routes/_protected/$owner/$repo/pull.$pullId.tsx index a4ff290..ec09f6b 100644 --- a/apps/dashboard/src/routes/_protected/$owner/$repo/pull.$pullId.tsx +++ b/apps/dashboard/src/routes/_protected/$owner/$repo/pull.$pullId.tsx @@ -1,32 +1,21 @@ import { createFileRoute } from "@tanstack/react-router"; +import { DashboardContentLoading } from "#/components/layouts/dashboard-content-loading"; import { PullDetailPage } from "#/components/pulls/detail/pull-detail-page"; -import { - githubPullPageQueryOptions, - githubViewerQueryOptions, -} from "#/lib/github.query"; +import { githubPullPageQueryOptions } from "#/lib/github.query"; import { buildSeo, formatPageTitle, summarizeText } from "#/lib/seo"; export const Route = createFileRoute("/_protected/$owner/$repo/pull/$pullId")({ - loader: async ({ context, params }) => { + loader: ({ context, params }) => { const pullNumber = Number(params.pullId); const scope = { userId: context.user.id }; - const pageOptions = githubPullPageQueryOptions(scope, { - owner: params.owner, - repo: params.repo, - pullNumber, - }); - - const cachedData = context.queryClient.getQueryData(pageOptions.queryKey); - if (cachedData !== undefined) { - void context.queryClient.ensureQueryData(githubViewerQueryOptions(scope)); - return cachedData; - } - const [pageData] = await Promise.all([ - context.queryClient.ensureQueryData(pageOptions), - context.queryClient.ensureQueryData(githubViewerQueryOptions(scope)), - ]); - return pageData; + return context.queryClient.getQueryData( + githubPullPageQueryOptions(scope, { + owner: params.owner, + repo: params.repo, + pullNumber, + }).queryKey, + ); }, head: ({ loaderData, match, params }) => { const pull = loaderData?.detail; @@ -46,5 +35,6 @@ export const Route = createFileRoute("/_protected/$owner/$repo/pull/$pullId")({ robots: "noindex", }); }, + pendingComponent: DashboardContentLoading, component: PullDetailPage, }); diff --git a/apps/dashboard/src/routes/_protected/$owner/$repo/review.$pullId.tsx b/apps/dashboard/src/routes/_protected/$owner/$repo/review.$pullId.tsx index f3ff7b8..36300d6 100644 --- a/apps/dashboard/src/routes/_protected/$owner/$repo/review.$pullId.tsx +++ b/apps/dashboard/src/routes/_protected/$owner/$repo/review.$pullId.tsx @@ -1,54 +1,31 @@ import { createFileRoute } from "@tanstack/react-router"; +import { DashboardContentLoading } from "#/components/layouts/dashboard-content-loading"; import { ReviewPage } from "#/components/pulls/review/review-page"; -import { getPullFiles } from "#/lib/github.functions"; import { githubPullFileSummariesQueryOptions, githubPullPageQueryOptions, - githubQueryKeys, } from "#/lib/github.query"; import { buildSeo, formatPageTitle, summarizeText } from "#/lib/seo"; -const PULL_FILES_PAGE_SIZE = 25; - export const Route = createFileRoute("/_protected/$owner/$repo/review/$pullId")( { - loader: async ({ context, params }) => { + loader: ({ context, params }) => { const pullNumber = Number(params.pullId); const scope = { userId: context.user.id }; const input = { owner: params.owner, repo: params.repo, pullNumber }; - const pageOptions = githubPullPageQueryOptions(scope, input); - const fileSummariesOptions = githubPullFileSummariesQueryOptions( - scope, - input, - ); const cachedPageData = context.queryClient.getQueryData( - pageOptions.queryKey, + githubPullPageQueryOptions(scope, input).queryKey, ); const cachedFileSummaries = context.queryClient.getQueryData( - fileSummariesOptions.queryKey, + githubPullFileSummariesQueryOptions(scope, input).queryKey, ); - // Check if infinite query already has data - const filesQueryKey = githubQueryKeys.pulls.files(scope, input); - const cachedFilesData = context.queryClient.getQueryData(filesQueryKey); - - const [pageData, fileSummaries, firstFilesPage] = await Promise.all([ - cachedPageData ?? context.queryClient.ensureQueryData(pageOptions), - cachedFileSummaries ?? - context.queryClient.ensureQueryData(fileSummariesOptions), - cachedFilesData - ? null - : getPullFiles({ - data: { - ...input, - page: 1, - perPage: PULL_FILES_PAGE_SIZE, - }, - }), - ]); - - return { pageData, fileSummaries, firstFilesPage }; + return { + pageData: cachedPageData ?? null, + fileSummaries: cachedFileSummaries ?? null, + firstFilesPage: null, + }; }, head: ({ loaderData, match, params }) => { const pull = loaderData?.pageData?.detail; @@ -68,6 +45,7 @@ export const Route = createFileRoute("/_protected/$owner/$repo/review/$pullId")( robots: "noindex", }); }, + pendingComponent: DashboardContentLoading, component: ReviewPage, }, );