From 2033facd146babaf00789f50da2d7817de42d015 Mon Sep 17 00:00:00 2001 From: Alan Daniel Date: Thu, 9 Apr 2026 22:49:15 -0400 Subject: [PATCH] Add loading spinners for uncached pages and fix UI polish issues - Add pendingComponent to list routes (overview, pulls, issues, reviews) so a spinner shows while loaders fetch data on cold loads - Set defaultPendingMs: 0 on the router for instant pending UI - Simplify component loading fallbacks to show spinner during SSR-to-hydration gap - Prefetch viewer query in pull detail loader to prevent review-requested banner layout shift - Remove double border on review page resizable panel handle --- .../src/components/pulls/review/review-page.tsx | 7 +------ apps/dashboard/src/router.tsx | 1 + .../routes/_protected/$owner/$repo/pull.$pullId.tsx | 12 ++++++++++-- apps/dashboard/src/routes/_protected/index.tsx | 8 ++------ apps/dashboard/src/routes/_protected/issues.tsx | 7 ++----- apps/dashboard/src/routes/_protected/pulls.tsx | 7 ++----- apps/dashboard/src/routes/_protected/reviews.tsx | 7 ++----- 7 files changed, 20 insertions(+), 29 deletions(-) diff --git a/apps/dashboard/src/components/pulls/review/review-page.tsx b/apps/dashboard/src/components/pulls/review/review-page.tsx index 4837621..141aaea 100644 --- a/apps/dashboard/src/components/pulls/review/review-page.tsx +++ b/apps/dashboard/src/components/pulls/review/review-page.tsx @@ -303,12 +303,7 @@ export function ReviewPage() { - +
diff --git a/apps/dashboard/src/router.tsx b/apps/dashboard/src/router.tsx index 5802a29..6e50999 100644 --- a/apps/dashboard/src/router.tsx +++ b/apps/dashboard/src/router.tsx @@ -16,6 +16,7 @@ export function getRouter() { scrollRestoration: true, defaultPreload: "intent", defaultPreloadStaleTime: 0, + defaultPendingMs: 0, Wrap: ({ children }) => ( {children} 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 ca5e369..6566f16 100644 --- a/apps/dashboard/src/routes/_protected/$owner/$repo/pull.$pullId.tsx +++ b/apps/dashboard/src/routes/_protected/$owner/$repo/pull.$pullId.tsx @@ -1,6 +1,9 @@ import { createFileRoute } from "@tanstack/react-router"; import { PullDetailPage } from "#/components/pulls/detail/pull-detail-page"; -import { githubPullPageQueryOptions } from "#/lib/github.query"; +import { + githubPullPageQueryOptions, + githubViewerQueryOptions, +} from "#/lib/github.query"; import { buildSeo, formatPageTitle, summarizeText } from "#/lib/seo"; export const Route = createFileRoute("/_protected/$owner/$repo/pull/$pullId")({ @@ -15,10 +18,15 @@ export const Route = createFileRoute("/_protected/$owner/$repo/pull/$pullId")({ const cachedData = context.queryClient.getQueryData(pageOptions.queryKey); if (cachedData !== undefined) { + void context.queryClient.ensureQueryData(githubViewerQueryOptions(scope)); return cachedData; } - return context.queryClient.ensureQueryData(pageOptions); + const [pageData] = await Promise.all([ + context.queryClient.ensureQueryData(pageOptions), + context.queryClient.ensureQueryData(githubViewerQueryOptions(scope)), + ]); + return pageData; }, head: ({ loaderData, match, params }) => { const pull = loaderData?.detail; diff --git a/apps/dashboard/src/routes/_protected/index.tsx b/apps/dashboard/src/routes/_protected/index.tsx index 1223bd5..4f2e9d7 100644 --- a/apps/dashboard/src/routes/_protected/index.tsx +++ b/apps/dashboard/src/routes/_protected/index.tsx @@ -14,12 +14,12 @@ import { useHasMounted } from "#/lib/use-has-mounted"; export const Route = createFileRoute("/_protected/")({ loader: async ({ context }) => { const scope = { userId: context.user.id }; - await Promise.all([ context.queryClient.ensureQueryData(githubMyPullsQueryOptions(scope)), context.queryClient.ensureQueryData(githubMyIssuesQueryOptions(scope)), ]); }, + pendingComponent: DashboardContentLoading, head: ({ match }) => buildSeo({ path: match.pathname, @@ -110,11 +110,7 @@ function OverviewPage() { ); } - if (hasMounted && (pullsQuery.isPending || issuesQuery.isPending)) { - return ; - } - - return null; + return ; } type MetricCardProps = { diff --git a/apps/dashboard/src/routes/_protected/issues.tsx b/apps/dashboard/src/routes/_protected/issues.tsx index a6485ba..632ebe8 100644 --- a/apps/dashboard/src/routes/_protected/issues.tsx +++ b/apps/dashboard/src/routes/_protected/issues.tsx @@ -23,6 +23,7 @@ export const Route = createFileRoute("/_protected/issues")({ githubMyIssuesQueryOptions(scope), ); }, + pendingComponent: DashboardContentLoading, head: ({ match }) => buildSeo({ path: match.pathname, @@ -113,11 +114,7 @@ function IssuesPage() {
); } - if (hasMounted && query.isPending) { - return ; - } - - return null; + return ; } type IssueGroupData = { diff --git a/apps/dashboard/src/routes/_protected/pulls.tsx b/apps/dashboard/src/routes/_protected/pulls.tsx index 28951b8..b8ab169 100644 --- a/apps/dashboard/src/routes/_protected/pulls.tsx +++ b/apps/dashboard/src/routes/_protected/pulls.tsx @@ -27,6 +27,7 @@ export const Route = createFileRoute("/_protected/pulls")({ const scope = { userId: context.user.id }; await context.queryClient.ensureQueryData(githubMyPullsQueryOptions(scope)); }, + pendingComponent: DashboardContentLoading, head: ({ match }) => buildSeo({ path: match.pathname, @@ -135,11 +136,7 @@ function PullRequestsPage() {
); } - if (hasMounted && query.isPending) { - return ; - } - - return null; + return ; } type PullGroupData = { diff --git a/apps/dashboard/src/routes/_protected/reviews.tsx b/apps/dashboard/src/routes/_protected/reviews.tsx index 57619c3..5eb8d16 100644 --- a/apps/dashboard/src/routes/_protected/reviews.tsx +++ b/apps/dashboard/src/routes/_protected/reviews.tsx @@ -13,6 +13,7 @@ export const Route = createFileRoute("/_protected/reviews")({ const scope = { userId: context.user.id }; await context.queryClient.ensureQueryData(githubMyPullsQueryOptions(scope)); }, + pendingComponent: DashboardContentLoading, head: ({ match }) => buildSeo({ path: match.pathname, @@ -80,9 +81,5 @@ function ReviewsPage() {
); } - if (hasMounted && query.isPending) { - return ; - } - - return null; + return ; }