From 69429cb08f8e055af1ab450be7b048a07dba6e5f Mon Sep 17 00:00:00 2001 From: Thomas Scholtes Date: Wed, 28 Feb 2024 12:11:27 +0100 Subject: [PATCH] Re-use project-level data when browsing source code We make source code browsing faster by re-using project-level data when navigating between files. This comes at a cost: Project info (like the issue counter), remotes and branches are only updated when the page is hard-refreshed or the user navigates away from the source code view. --- src/lib/router.ts | 5 ++-- src/lib/router/definitions.ts | 7 +++-- src/views/projects/router.ts | 52 +++++++++++++++++++++++++++++------ 3 files changed, 51 insertions(+), 13 deletions(-) diff --git a/src/lib/router.ts b/src/lib/router.ts index 27cb4f31d..4885fe9b2 100644 --- a/src/lib/router.ts +++ b/src/lib/router.ts @@ -1,7 +1,7 @@ import type { BaseUrl } from "@httpd-client"; import type { LoadedRoute, Route } from "@app/lib/router/definitions"; -import { writable } from "svelte/store"; +import { get, writable } from "svelte/store"; import * as mutexExecutor from "@app/lib/mutexExecutor"; import * as utils from "@app/lib/utils"; @@ -101,9 +101,10 @@ async function navigate( window.history.replaceState(newRoute, ""); } currentUrl = new URL(window.location.href); + const currentLoadedRoute = get(activeRouteStore); const loadedRoute = await loadExecutor.run(async () => { - return loadRoute(newRoute); + return loadRoute(newRoute, currentLoadedRoute); }); // Only let the last request through. diff --git a/src/lib/router/definitions.ts b/src/lib/router/definitions.ts index c64597bf8..441befaa2 100644 --- a/src/lib/router/definitions.ts +++ b/src/lib/router/definitions.ts @@ -62,7 +62,10 @@ export type LoadedRoute = | NodesLoadedRoute | SessionRoute; -export async function loadRoute(route: Route): Promise { +export async function loadRoute( + route: Route, + previousLoaded: LoadedRoute, +): Promise { if (route.resource === "nodes") { return await loadNodeRoute(route.params); } else if (route.resource === "home") { @@ -77,7 +80,7 @@ export async function loadRoute(route: Route): Promise { route.resource === "project.patches" || route.resource === "project.patch" ) { - return await loadProjectRoute(route); + return await loadProjectRoute(route, previousLoaded); } else { return route; } diff --git a/src/views/projects/router.ts b/src/views/projects/router.ts index 008b92425..d6663462a 100644 --- a/src/views/projects/router.ts +++ b/src/views/projects/router.ts @@ -1,4 +1,8 @@ -import type { ErrorRoute, NotFoundRoute } from "@app/lib/router/definitions"; +import type { + ErrorRoute, + LoadedRoute, + NotFoundRoute, +} from "@app/lib/router/definitions"; import type { BaseUrl, Blob, @@ -254,6 +258,7 @@ async function isLocalNodeSeeding(route: ProjectRoute): Promise { export async function loadProjectRoute( route: ProjectRoute, + previousLoaded: LoadedRoute, ): Promise { const api = new HttpdClient(route.node); const rawPath = (commit?: string) => @@ -263,7 +268,7 @@ export async function loadProjectRoute( try { if (route.resource === "project.source") { - return await loadTreeView(route); + return await loadTreeView(route, previousLoaded); } else if (route.resource === "project.history") { return await loadHistoryView(route); } else if (route.resource === "project.commit") { @@ -374,20 +379,49 @@ async function loadIssuesView( async function loadTreeView( route: ProjectTreeRoute, -): Promise { + previousLoaded: LoadedRoute, +): Promise { const api = new HttpdClient(route.node); const rawPath = (commit?: string) => `${route.node.scheme}://${route.node.hostname}:${route.node.port}/raw/${ route.project }${commit ? `/${commit}` : ""}`; - const [project, peers, branchMap, seeding] = await Promise.all([ - api.project.getById(route.project), - api.project.getAllRemotes(route.project), - getPeerBranches(api, route.project, route.peer), + let projectPromise: Promise; + let peersPromise: Promise; + if ( + previousLoaded.resource === "project.source" && + previousLoaded.params.project.id === route.project && + previousLoaded.params.peer === route.peer + ) { + projectPromise = Promise.resolve(previousLoaded.params.project); + peersPromise = Promise.resolve(previousLoaded.params.peers); + } else { + projectPromise = api.project.getById(route.project); + peersPromise = api.project.getAllRemotes(route.project); + } + + const [project, peers, seeding] = await Promise.all([ + projectPromise, + peersPromise, isLocalNodeSeeding(route), ]); + let branchMap: Record; + if (route.peer) { + const peer = peers.find(peer => peer.id === route.peer); + if (!peer) { + return { + resource: "notFound", + params: { title: `Peer ${route.peer} could not be found` }, + }; + } else { + branchMap = peer.heads; + } + } else { + branchMap = { [project.defaultBranch]: project.head }; + } + if (route.route) { const { revision, path } = detectRevision( route.route, @@ -400,7 +434,7 @@ async function loadTreeView( const commit = parseRevisionToOid( route.revision, project.defaultBranch, - branchMap || { [project.defaultBranch]: project.head }, + branchMap, ); const path = route.path || "/"; @@ -416,7 +450,7 @@ async function loadTreeView( project, peers, peer: route.peer, - branches: Object.keys(branchMap || {}), + branches: Object.keys(branchMap), rawPath, revision: route.revision, tree,