From ee507215fb85eafb94a91a320abb29f4c43f7150 Mon Sep 17 00:00:00 2001 From: Sebastian Martinez Date: Mon, 14 Feb 2022 09:22:31 -0300 Subject: [PATCH] Refactor projects Signed-off-by: Sebastian Martinez --- src/base/projects/BranchSelector.spec.ts | 1 + src/base/projects/Browser.svelte | 16 +- src/base/projects/Commit.svelte | 9 +- src/base/projects/Header.svelte | 17 +- src/base/projects/History.svelte | 12 +- src/base/projects/Project.svelte | 34 ++- src/base/projects/ProjectRoute.svelte | 8 +- src/base/projects/View.svelte | 55 +---- src/base/seeds/Seed.ts | 10 +- src/project.ts | 272 +++++++++++++---------- 10 files changed, 217 insertions(+), 217 deletions(-) diff --git a/src/base/projects/BranchSelector.spec.ts b/src/base/projects/BranchSelector.spec.ts index f9d256fef..4c84610b0 100644 --- a/src/base/projects/BranchSelector.spec.ts +++ b/src/base/projects/BranchSelector.spec.ts @@ -5,6 +5,7 @@ import { styles } from "@test/support/index"; const defaultProps = { project: { head: "e678629cd37c770c640a2cd997fc76303c815772", + urn: "rad:git:hnrkqdpm9ub19oc8dccx44echy76hzfsezyio", name: "nakamoto", description: "Privacy-preserving Bitcoin light-client implementation in Rust", defaultBranch: "master", diff --git a/src/base/projects/Browser.svelte b/src/base/projects/Browser.svelte index 53040b611..037f968d1 100644 --- a/src/base/projects/Browser.svelte +++ b/src/base/projects/Browser.svelte @@ -17,15 +17,13 @@ { status: Status.Loading; path: string } | { status: Status.Loaded; path: string; blob: proj.Blob }; - export let source: proj.Source; + export let project: proj.Project; export let tree: proj.Tree; export let browserStore: Readable; - const { urn, project } = source; - $: browser = $browserStore; $: path = browser.path || "/"; - $: revision = browser.revision || source.branches[project.head]; + $: revision = browser.revision || project.branches[project.head]; // When the component is loaded the first time, the blob is yet to be loaded. let state: State = { status: Status.Loading, path }; @@ -39,8 +37,8 @@ const isMarkdownPath = utils.isMarkdownPath(path); const promise = path === "/" - ? proj.getReadme(urn, commit, source.seed.api) - : proj.getBlob(urn, commit, path, { highlight: !isMarkdownPath }, source.seed.api); + ? project.getReadme(commit) + : project.getBlob(commit, path, { highlight: !isMarkdownPath }); state = { status: Status.Loading, path }; state = { status: Status.Loaded, path, blob: await promise }; @@ -59,19 +57,19 @@ mobileFileTree = false; if (path) { - proj.navigateTo({ path: newPath, revision }, source); + project.navigateTo({ path: newPath, revision }); } }; const fetchTree = async (path: string) => { - return proj.getTree(urn, commit, path, source.seed.api); + return project.getTree(commit, path); }; const toggleMobileFileTree = () => { mobileFileTree = !mobileFileTree; }; - $: commit = proj.getOid(revision, source.branches) || project.head; + $: commit = proj.getOid(revision, project.branches) || project.head; $: getBlob = loadBlob(path); $: loadingPath = state.status == Status.Loading ? state.path : null; diff --git a/src/base/projects/Commit.svelte b/src/base/projects/Commit.svelte index 8077eae3f..bcc6dd253 100644 --- a/src/base/projects/Commit.svelte +++ b/src/base/projects/Commit.svelte @@ -4,16 +4,15 @@ import { formatCommitTime } from "@app/commit"; import { formatCommit } from "@app/utils"; - export let source: proj.Source; + export let project: proj.Project; export let commit: string; - const { seed, urn } = source; const onBrowse = (event: { detail: string }) => { - proj.navigateTo({ + project.navigateTo({ content: proj.ProjectContent.Tree, revision: commit, path: event.detail - }, source); + }); }; @@ -61,7 +60,7 @@ } -{#await proj.getCommit(urn, commit, seed.api) then commit} +{#await project.getCommit(commit) then commit}
diff --git a/src/base/projects/Header.svelte b/src/base/projects/Header.svelte index c11000f0e..92891bffe 100644 --- a/src/base/projects/Header.svelte +++ b/src/base/projects/Header.svelte @@ -2,20 +2,19 @@ import type { Writable } from 'svelte/store'; import { navigate } from 'svelte-routing'; import * as utils from '@app/utils'; - import * as proj from '@app/project'; - import { Browser, ProjectContent, Source } from '@app/project'; + import { Browser, ProjectContent, Project } from '@app/project'; import AnchorBadge from '@app/base/profiles/AnchorBadge.svelte'; import type { Tree } from "@app/project"; import BranchSelector from './BranchSelector.svelte'; import PeerSelector from './PeerSelector.svelte'; - export let source: Source; + export let project: Project; export let tree: Tree; export let commit: string; export let browserStore: Writable; export let peerSelector: boolean; // If peerSelector should be showed. - let { urn, project, peers, seed, anchors } = source; + let { urn, peers, branches, seed, anchors } = project; $: browser = $browserStore; $: revision = browser.revision || commit; @@ -31,19 +30,19 @@ // Switches between the browser and commit view. const toggleContent = (input: ProjectContent) => { - proj.navigateTo({ + project.navigateTo({ content: content === input ? ProjectContent.Tree : input - }, source); + }); }; const updatePeer = (peer: string) => { dropdownState.peer = false; - proj.navigateTo({ peer, revision: null }, source); + project.navigateTo({ peer, revision: null }); }; const updateRevision = (revision: string) => { dropdownState.branch = false; - proj.navigateTo({ revision }, source); + project.navigateTo({ revision }); }; @@ -157,7 +156,7 @@ bind:peersDropdown={dropdownState.peer} on:peerChanged={(event) => updatePeer(event.detail)} /> {/if} - updateRevision(event.detail)} />
diff --git a/src/base/projects/History.svelte b/src/base/projects/History.svelte index 1cb462e88..19e362a17 100644 --- a/src/base/projects/History.svelte +++ b/src/base/projects/History.svelte @@ -1,20 +1,18 @@ diff --git a/src/base/projects/Project.svelte b/src/base/projects/Project.svelte index 64064d3be..30c8caab4 100644 --- a/src/base/projects/Project.svelte +++ b/src/base/projects/Project.svelte @@ -14,22 +14,20 @@ export let peer: string | null = null; export let config: Config; - export let source: proj.Source; + export let project: proj.Project; export let content: proj.ProjectContent; export let revision: string; - const project = source.project; - - let parentName = source.profile ? formatProfile(source.profile.nameOrAddress, config) : null; + let parentName = project.profile ? formatProfile(project.profile.nameOrAddress, config) : null; let pageTitle = parentName ? `${parentName}/${project.name}` : project.name; - function rootPath(source: proj.Source): string { - return proj.pathTo({ + function rootPath(): string { + return project.pathTo({ content: proj.ProjectContent.Tree, peer: null, path: "/", revision: null, - }, source); + }); } const baseName = parentName @@ -107,30 +105,30 @@
- {#if source.profile} - - + {#if project.profile} + + / {/if} - {source.project.name} + {project.name} {#if peer} / {formatSeedId(peer)} {/if}
-
{source.urn}
-
{source.project.description}
+
{project.urn}
+
{project.description}
-{#await proj.getRoot(source.project, revision, source.branches, source.seed.api) then { tree, commit }} -
+{#await project.getRoot(revision) then { tree, commit }} +
{#if content == proj.ProjectContent.Tree} - + {:else if content == proj.ProjectContent.History} - + {:else if content == proj.ProjectContent.Commit} - + {/if} {:catch err}
diff --git a/src/base/projects/ProjectRoute.svelte b/src/base/projects/ProjectRoute.svelte index 6a36a2b18..771207a86 100644 --- a/src/base/projects/ProjectRoute.svelte +++ b/src/base/projects/ProjectRoute.svelte @@ -10,15 +10,15 @@ export let revision: string | null = null; export let peer: string | null; export let content: proj.ProjectContent = proj.ProjectContent.Tree; - export let source: proj.Source; + export let project: proj.Project; export let config: Config; const browse: proj.BrowseTo = { content, peer, path: "/" }; - const head = source.branches[source.project.defaultBranch]; + const head = project.branches[project.defaultBranch]; // route is passed when the URL has more params after e.g. /tree or /history $: if (route) { - const { path, revision } = proj.parseRoute(route, source.branches); + const { path, revision } = proj.parseRoute(route, project.branches); if (path) browse.path = path; if (revision) browse.revision = revision; @@ -36,6 +36,6 @@ peer={browser.peer} revision={browser.revision || head} content={browser.content} - {source} + {project} {config} /> diff --git a/src/base/projects/View.svelte b/src/base/projects/View.svelte index 46922a8b3..2c7442d64 100644 --- a/src/base/projects/View.svelte +++ b/src/base/projects/View.svelte @@ -1,11 +1,8 @@
- {#await getProject(peer)} + {#await Project.get(id, peer, profileName, seedHost, config)}
- {:then source} + {:then project} - + - + - + - + - + - + - + {:catch} diff --git a/src/base/seeds/Seed.ts b/src/base/seeds/Seed.ts index e2780787c..ec3c0cc2e 100644 --- a/src/base/seeds/Seed.ts +++ b/src/base/seeds/Seed.ts @@ -1,6 +1,6 @@ import * as api from '@app/api'; import type { Config } from '@app/config'; -import * as proj from '@app/project'; +import * as proj from "@app/project"; import { isDomain } from '@app/utils'; import { assert } from '@app/error'; @@ -65,15 +65,15 @@ export class Seed { } async getProject(urn: string): Promise { - return proj.getInfo(urn, this.api); + return proj.Project.getInfo(urn, this.api); } async getProjects(id?: string): Promise { const result = id - ? await proj.getDelegateProjects(id, this.api) - : await proj.getProjects(this.api); + ? await proj.Project.getDelegateProjects(id, this.api) + : await proj.Project.getProjects(this.api); - return result.map((project: any) => ({ ...project, id: project.urn })); + return result.map((project: proj.ProjectInfo) => ({ ...project, id: project.urn })); } static async getPeer({ host, port }: api.Host): Promise<{ id: string }> { diff --git a/src/project.ts b/src/project.ts index 7646274b1..1453a84b4 100644 --- a/src/project.ts +++ b/src/project.ts @@ -2,9 +2,10 @@ import { navigate } from 'svelte-routing'; import { get, writable } from 'svelte/store'; import * as api from '@app/api'; import type { Commit, CommitHeader, CommitsHistory } from '@app/commit'; -import { isOid } from '@app/utils'; -import type { Profile } from '@app/profile'; -import type { Seed } from '@app/base/seeds/Seed'; +import { isOid, isRadicleId } from '@app/utils'; +import { Profile, ProfileType } from '@app/profile'; +import { Seed } from '@app/base/seeds/Seed'; +import type { Config } from '@app/config'; export type Urn = string; export type PeerId = string; @@ -28,17 +29,6 @@ export interface PendingAnchor { }; } -// Params to render correctly source code related views -export interface Source { - urn: string; - project: ProjectInfo; - peers: PeerId[]; - branches: Branches; - anchors: string[]; - seed: Seed; - profile?: Profile | null; -} - // Enumerates the space below the Header component in the projects View component export enum ProjectContent { Tree, @@ -129,107 +119,6 @@ export function browse(browse: BrowseTo): void { browserStore.set({ ...browser, ...browse }); } -export function pathTo(browse: BrowseTo, source: Source): string { - const browser = get(browserStore); - const options: PathOptions = { - urn: source.urn, - ...browser, - ...browse - }; - - if (source.profile) { - options.profile = source.profile?.nameOrAddress; - } else { - options.seed = source.seed.host; - } - - return path(options); -} - -export function navigateTo(browse: BrowseTo, source: Source): void { - navigate(pathTo(browse, source)); -} - -export async function getInfo(nameOrUrn: string, host: api.Host): Promise { - const info = await api.get(`projects/${nameOrUrn}`, {}, host); - - return { - ...info, - ...info.meta // Nb. This is only needed while we are upgrading to the new http-api. - }; -} - -export async function getCommits(urn: string, commit: string, host: api.Host): Promise { - return api.get(`projects/${urn}/commits?from=${commit}`, {}, host); -} - -export async function getCommit(urn: string, commit: string, host: api.Host): Promise { - return api.get(`projects/${urn}/commits/${commit}`, {}, host); -} - -export async function getProjects(host: api.Host): Promise { - return api.get("projects", {}, host); -} - -export async function getDelegateProjects(delegate: string, host: api.Host): Promise { - return api.get(`delegates/${delegate}/projects`, {}, host); -} - -export async function getRemote(urn: string, peer: string, host: api.Host): Promise { - return api.get(`projects/${urn}/remotes/${peer}`, {}, host); -} - -export async function getRemotes(urn: string, host: api.Host): Promise { - return api.get(`projects/${urn}/remotes`, {}, host); -} - -export async function getRoot( - project: ProjectInfo, - revision: string | null, - heads: Branches, - host: api.Host -): Promise<{ tree: Tree; commit: string }> { - const urn = project.urn; - - const head = heads[project.defaultBranch]; - const commit = revision ? getOid(revision, heads) : head; - - if (! commit) { - throw new Error(`Revision ${revision} not found`); - } - const tree = await getTree(urn, commit, "/", host); - - return { tree, commit }; -} - -export async function getTree( - urn: string, - commit: string, - path: string, - host: api.Host -): Promise { - if (path === "/") path = ""; - return api.get(`projects/${urn}/tree/${commit}/${path}`, {}, host); -} - -export async function getBlob( - urn: string, - commit: string, - path: string, - options: { highlight: boolean }, - host: api.Host -): Promise { - return api.get(`projects/${urn}/blob/${commit}/${path}`, options, host); -} - -export async function getReadme( - urn: string, - commit: string, - host: api.Host -): Promise { - return api.get(`projects/${urn}/readme/${commit}`, {}, host); -} - export function path(opts: PathOptions): string { const { urn, profile, seed, peer, content, revision, path } = opts; const result = []; @@ -304,3 +193,156 @@ export function parseRoute(input: string, branches: Branches): { path?: string; } return parsed; } + +export class Project implements ProjectInfo { + urn: string; + head: string; + name: string; + description: string; + defaultBranch: string; + maintainers: Urn[]; + delegates: PeerId[]; + seed: Seed; + peers: string[]; + branches: Branches; + profile: Profile | null; + anchors: string[]; + + constructor(urn: string, info: ProjectInfo, seed: Seed, peers: string[], branches: Branches, profile: Profile | null, anchors: string[]) { + this.urn = urn; + this.head = info.head; + this.name = info.name; + this.description = info.description; + this.defaultBranch = info.defaultBranch; + this.maintainers = info.maintainers; + this.delegates = info.delegates; + this.seed = seed; + this.peers = peers; + this.branches = branches; + this.profile = profile; + this.anchors = anchors; + } + + async getRoot( + revision: string | null, + ): Promise<{ tree: Tree; commit: string }> { + const head = this.branches[this.defaultBranch]; + const commit = revision ? getOid(revision, this.branches) : head; + + if (! commit) { + throw new Error(`Revision ${revision} not found`); + } + const tree = await this.getTree(commit, "/"); + + return { tree, commit }; + } + + static async getInfo(nameOrUrn: string, host: api.Host): Promise { + const info = await api.get(`projects/${nameOrUrn}`, {}, host); + + return { + ...info, + ...info.meta // Nb. This is only needed while we are upgrading to the new http-api. + }; + } + + static async getProjects(host: api.Host): Promise { + return api.get("projects", {}, host); + } + + static async getDelegateProjects(delegate: string, host: api.Host): Promise { + return api.get(`delegates/${delegate}/projects`, {}, host); + } + + static async getRemote(urn: string, peer: string, host: api.Host): Promise { + return api.get(`projects/${urn}/remotes/${peer}`, {}, host); + } + + static async getRemotes(urn: string, host: api.Host): Promise { + return api.get(`projects/${urn}/remotes`, {}, host); + } + + async getCommits(commit: string): Promise { + return api.get(`projects/${this.urn}/commits?from=${commit}`, {}, this.seed.api); + } + + async getCommit(commit: string): Promise { + return api.get(`projects/${this.urn}/commits/${commit}`, {}, this.seed.api); + } + + async getTree( + commit: string, + path: string, + ): Promise { + if (path === "/") path = ""; + return api.get(`projects/${this.urn}/tree/${commit}/${path}`, {}, this.seed.api); + } + + async getBlob( + commit: string, + path: string, + options: { highlight: boolean }, + ): Promise { + return api.get(`projects/${this.urn}/blob/${commit}/${path}`, options, this.seed.api); + } + + async getReadme( + commit: string, + ): Promise { + return api.get(`projects/${this.urn}/readme/${commit}`, {}, this.seed.api); + } + + navigateTo(browse: BrowseTo): void { + navigate(this.pathTo(browse)); + } + + pathTo(browse: BrowseTo): string { + const browser = get(browserStore); + const options: PathOptions = { + urn: this.urn, + ...browser, + ...browse + }; + + if (this.profile) { + options.profile = this.profile?.nameOrAddress; + } else { + options.seed = this.seed.host; + } + + return path(options); + } + + static async get(id: string, peer: string | null, profileName: string | null, seedHost: string | null, config: Config): Promise { + const profile = profileName ? await Profile.get(profileName, ProfileType.Project, config) : null; + const seed = profile ? profile.seed : seedHost ? await Seed.lookup(seedHost, config) : null; + + if (!profile && !seed) { + throw new Error("Couldn't load project"); + } + if (! seed?.valid) { + throw new Error("Couldn't load project: invalid seed"); + } + + const info = await Project.getInfo(id, seed.api); + const urn = isRadicleId(id) ? id : info.urn; + const anchors = profile ? await profile.confirmedProjectAnchors(urn, config) : []; + + // Older versions of http-api don't include the URN. + if (! info.urn) info.urn = urn; + + const peers: PeerId[] = info.delegates + ? await Project.getRemotes(urn, seed.api) + : []; + + let remote: Remote = { + heads: { [info.defaultBranch]: info.head } + }; + + if (peer) { + remote = await Project.getRemote(urn, peer, seed.api); + } + + return new Project(urn, info, seed, peers, remote.heads, profile, anchors); + } +}