From 41a443f393ecb46f872a28cfa95bd2a96d20cea4 Mon Sep 17 00:00:00 2001 From: Quentin Gliech Date: Sun, 29 Mar 2026 23:16:04 +0200 Subject: [PATCH] fix(web): move refs preload to $repo context, fix blinking on ref switch the refs query only depends on the repo slug, not the child route (tree/blob/commits/ref). Preloading it in _code.tsx beforeLoad caused a new QueryRef on every navigation, triggering suspense flashes. move refs preload to $repo.tsx beforeLoad so it's loaded once per repo and shared via context. The _code layout and /$repo redirect both read from context instead of firing their own queries. also replace window.location.href ref switch with router navigation Co-Authored-By: Claude Opus 4.6 (1M context) --- webui2/src/routes/$repo.tsx | 38 +++++++++++++++++++-- webui2/src/routes/$repo/_code.tsx | 56 ++++++++++--------------------- webui2/src/routes/$repo/index.tsx | 24 +++---------- 3 files changed, 57 insertions(+), 61 deletions(-) diff --git a/webui2/src/routes/$repo.tsx b/webui2/src/routes/$repo.tsx index 881315009cfd0a8870ba5311189a224be795d91f..24a609bdb111155ae0ada8305764cd9526a07a05 100644 --- a/webui2/src/routes/$repo.tsx +++ b/webui2/src/routes/$repo.tsx @@ -1,9 +1,43 @@ +import { gql } from "@apollo/client"; import { createFileRoute } from "@tanstack/react-router"; +import type { GitRef } from "@/__generated__/graphql"; + +export const REFS_QUERY = gql` + query CodePageRefs($repo: String) { + repository(ref: $repo) { + name + refs { + nodes { + name + shortName + type + hash + isDefault + } + } + } + } +`; + +export interface RefsQueryData { + repository: { + name: string; + refs: { nodes: GitRef[] } | null; + } | null; +} + export const Route = createFileRoute("/$repo")({ - beforeLoad: ({ params: { repo } }) => { + beforeLoad: ({ params: { repo }, context: { preloadQuery } }) => { // Normalize the repo slug: "_" means the default (null) repo const ref = repo === "_" ? null : repo; - return { ref }; + + // Preload refs once for the entire repo — shared by code browser, + // and used for the /$repo → tree redirect. + const refsRef = preloadQuery(REFS_QUERY, { + variables: { repo: ref }, + }); + + return { ref, refsRef }; }, }); diff --git a/webui2/src/routes/$repo/_code.tsx b/webui2/src/routes/$repo/_code.tsx index b027f08dba49cbe5dc5412c514ab44f01895d120..9a07d1f9014153e7f30c79141c82b522775cff65 100644 --- a/webui2/src/routes/$repo/_code.tsx +++ b/webui2/src/routes/$repo/_code.tsx @@ -1,10 +1,15 @@ -// Pathless layout for the code browser. Preloads refs (branches/tags) -// and renders the shared header (breadcrumb + ref selector + history toggle). -// Child routes (tree, blob, commits) render inside the Outlet. +// Pathless layout for the code browser. Reads preloaded refs from the +// $repo context and renders the shared header (breadcrumb + ref selector +// + history toggle). Child routes (tree, blob, commits) render in Outlet. -import { gql } from "@apollo/client"; import { useReadQuery } from "@apollo/client/react"; -import { createFileRoute, Outlet, useMatchRoute, useParams } from "@tanstack/react-router"; +import { + createFileRoute, + Outlet, + useMatchRoute, + useNavigate, + useParams, +} from "@tanstack/react-router"; import { GitCommit } from "lucide-react"; import type { GitRef } from "@/__generated__/graphql"; @@ -13,45 +18,14 @@ import { RefSelector } from "@/components/code/RefSelector"; import { ButtonLink } from "@/components/ui/button-link"; import { Skeleton } from "@/components/ui/skeleton"; -export const REFS_QUERY = gql` - query CodePageRefs($repo: String) { - repository(ref: $repo) { - name - refs { - nodes { - name - shortName - type - hash - isDefault - } - } - } - } -`; - -export interface RefsQueryData { - repository: { - name: string; - refs: { nodes: GitRef[] } | null; - } | null; -} - export const Route = createFileRoute("/$repo/_code")({ component: CodeLayout, pendingComponent: CodeLayoutSkeleton, - beforeLoad: ({ context: { preloadQuery, ref } }) => { - const refsRef = preloadQuery(REFS_QUERY, { - variables: { repo: ref }, - }); - return { refsRef }; - }, }); function CodeLayout() { const { repo } = Route.useParams(); - const { ref: repoRef } = Route.useRouteContext(); - const { refsRef } = Route.useRouteContext(); + const { ref: repoRef, refsRef } = Route.useRouteContext(); const { data: refsData } = useReadQuery(refsRef); const refs: GitRef[] = refsData?.repository?.refs?.nodes ?? []; const repoName = refsData?.repository?.name ?? repoRef ?? "default-repo"; @@ -71,9 +45,13 @@ function CodeLayout() { fuzzy: true, }); + const navigate = useNavigate(); + function handleRefSelect(ref: GitRef) { - // When switching refs, always go to tree root - window.location.href = `/${repo}/tree/${ref.shortName}`; + void navigate({ + to: "/$repo/tree/$ref/$", + params: { repo, ref: ref.shortName, _splat: "" }, + }); } return ( diff --git a/webui2/src/routes/$repo/index.tsx b/webui2/src/routes/$repo/index.tsx index cd8b036d531d833156a19669e12fd71698b8bf78..0560434ba398f3e841bb743c9f73da4de83cff4b 100644 --- a/webui2/src/routes/$repo/index.tsx +++ b/webui2/src/routes/$repo/index.tsx @@ -1,31 +1,15 @@ // /$repo index — redirects to the tree view with the default ref. +// The refs are already being preloaded by $repo.tsx beforeLoad, so we +// use client.query() which will hit the Apollo cache. -import { gql } from "@apollo/client"; import { createFileRoute, redirect } from "@tanstack/react-router"; -import type { GitRef } from "@/__generated__/graphql"; import { client } from "@/lib/apollo"; - -const REFS_QUERY = gql` - query RepoDefaultRef($repo: String) { - repository(ref: $repo) { - refs { - nodes { - shortName - isDefault - } - } - } - } -`; - -interface DefaultRefQueryData { - repository: { refs: { nodes: Pick[] } | null } | null; -} +import { REFS_QUERY, type RefsQueryData } from "@/routes/$repo"; export const Route = createFileRoute("/$repo/")({ beforeLoad: async ({ context: { ref }, params: { repo } }) => { - const { data } = await client.query({ + const { data } = await client.query({ query: REFS_QUERY, variables: { repo: ref }, });