@@ -51,7 +50,7 @@ export function FileViewer({ blob, loading = false }: FileViewerProps) {
};
}, [blob]);
- if (loading || highlighted === null) return
;
+ if (highlighted === null) return
;
const { html, lineCount } = highlighted;
function copyToClipboard() {
diff --git a/webui2/src/routes/$repo/_code/blob/$ref/$.tsx b/webui2/src/routes/$repo/_code/blob/$ref/$.tsx
index 235199cb010f5c7234accd56baf9bf62b89a847d..33b10bb597e7505d012a47720a007e792a779db6 100644
--- a/webui2/src/routes/$repo/_code/blob/$ref/$.tsx
+++ b/webui2/src/routes/$repo/_code/blob/$ref/$.tsx
@@ -1,11 +1,12 @@
// Blob (file) view: /$repo/blob/$ref/...path
import { gql } from "@apollo/client";
-import { useQuery } from "@apollo/client/react";
+import { useReadQuery } from "@apollo/client/react";
import { createFileRoute } from "@tanstack/react-router";
import type { GitBlob } from "@/__generated__/graphql";
import { FileViewer } from "@/components/code/FileViewer";
+import { Skeleton } from "@/components/ui/skeleton";
const BLOB_QUERY = gql`
query CodePageBlob($repo: String, $ref: String!, $path: String!) {
@@ -26,19 +27,34 @@ interface BlobQueryData {
repository: { blob: GitBlob | null } | null;
}
+function BlobSkeleton() {
+ return (
+
+ );
+}
+
export const Route = createFileRoute("/$repo/_code/blob/$ref/$")({
component: BlobView,
+ pendingComponent: BlobSkeleton,
beforeLoad: () => ({ viewMode: "blob" as const }),
+ loader: async ({ context: { preloadQuery, ref }, params: { ref: gitRef, _splat: path } }) => {
+ const blobRef = preloadQuery
(BLOB_QUERY, {
+ variables: { repo: ref, ref: gitRef, path: path || "" },
+ });
+ return { blobRef: await preloadQuery.toPromise(blobRef) };
+ },
});
function BlobView() {
- const { ref: currentRef, _splat: currentPath = "" } = Route.useParams();
- const { ref: repoRef } = Route.useRouteContext();
-
- const { data, loading } = useQuery(BLOB_QUERY, {
- variables: { repo: repoRef, ref: currentRef, path: currentPath },
- skip: !currentPath,
- });
+ const { blobRef } = Route.useLoaderData();
+ const { data } = useReadQuery(blobRef);
- return ;
+ return ;
}
diff --git a/webui2/src/routes/$repo/_code/tree/$ref/$.tsx b/webui2/src/routes/$repo/_code/tree/$ref/$.tsx
index 98c7818bb17015b38a2d83c42d2e4d83e4faff12..1a0bbf50c591d674784639b7945eb60b59c9fca1 100644
--- a/webui2/src/routes/$repo/_code/tree/$ref/$.tsx
+++ b/webui2/src/routes/$repo/_code/tree/$ref/$.tsx
@@ -1,7 +1,7 @@
// Tree view: /$repo/tree/$ref/...path
import { gql } from "@apollo/client";
-import { useQuery } from "@apollo/client/react";
+import { useQuery, useReadQuery } from "@apollo/client/react";
import { createFileRoute } from "@tanstack/react-router";
import {
@@ -13,6 +13,7 @@ import {
import { FileTree } from "@/components/code/FileTree";
import type { TreeEntryWithCommit } from "@/components/code/FileTree";
import { Markdown } from "@/components/content/Markdown";
+import { Skeleton } from "@/components/ui/skeleton";
const TREE_QUERY = gql`
query CodePageTree($repo: String, $ref: String!, $path: String) {
@@ -42,7 +43,7 @@ const LAST_COMMITS_QUERY = gql`
}
`;
-const BLOB_QUERY = gql`
+const README_QUERY = gql`
query CodePageReadme($repo: String, $ref: String!, $path: String!) {
repository(ref: $repo) {
blob(ref: $ref, path: $path) {
@@ -62,20 +63,43 @@ interface ReadmeQueryData {
repository: { blob: GitBlob | null } | null;
}
+function TreeSkeleton() {
+ return (
+
+
+ {Array.from({ length: 8 }).map((_, i) => (
+
+
+
+
+
+
+ ))}
+
+
+ );
+}
+
export const Route = createFileRoute("/$repo/_code/tree/$ref/$")({
component: TreeView,
+ pendingComponent: TreeSkeleton,
beforeLoad: () => ({ viewMode: "tree" as const }),
+ loader: async ({ context: { preloadQuery, ref }, params: { ref: gitRef, _splat: path } }) => {
+ const treeRef = preloadQuery(TREE_QUERY, {
+ variables: { repo: ref, ref: gitRef, path: path || null },
+ });
+ return { treeRef: await preloadQuery.toPromise(treeRef) };
+ },
});
function TreeView() {
const { repo, ref: currentRef, _splat: currentPath = "" } = Route.useParams();
const { ref: repoRef } = Route.useRouteContext();
-
- const { data: treeData, loading: treeLoading } = useQuery(TREE_QUERY, {
- variables: { repo: repoRef, ref: currentRef, path: currentPath || null },
- });
+ const { treeRef } = Route.useLoaderData();
+ const { data: treeData } = useReadQuery(treeRef);
const entries: GitTreeEntry[] = treeData?.repository?.tree ?? [];
+ // Last commits and readme are cascading queries — they depend on the tree result
const entryNames = entries.map((e) => e.name);
const { data: lastCommitsData } = useQuery(LAST_COMMITS_QUERY, {
variables: { repo: repoRef, ref: currentRef, path: currentPath || null, names: entryNames },
@@ -97,7 +121,7 @@ function TreeView() {
? `${currentPath}/${readmeEntry.name}`
: readmeEntry.name
: null;
- const { data: readmeBlobData } = useQuery(BLOB_QUERY, {
+ const { data: readmeBlobData } = useQuery(README_QUERY, {
variables: { repo: repoRef, ref: currentRef, path: readmePath },
skip: !readmePath,
});
@@ -110,7 +134,6 @@ function TreeView() {
currentRef={currentRef}
currentPath={currentPath}
entries={entriesWithCommits}
- loading={treeLoading}
/>
{readme && (