diff --git a/src/components/IconSmall.svelte b/src/components/IconSmall.svelte index 2c336d1ee..b45934bc3 100644 --- a/src/components/IconSmall.svelte +++ b/src/components/IconSmall.svelte @@ -33,6 +33,8 @@ | "network" | "patch" | "plus" + | "tracking-on" + | "tracking-off" | "user"; @@ -375,6 +377,30 @@ fill-rule="evenodd" clip-rule="evenodd" d="M13.1667 8C13.1667 8.27614 12.9428 8.5 12.6667 8.5L3.33334 8.5C3.0572 8.5 2.83334 8.27614 2.83334 8C2.83334 7.72386 3.0572 7.5 3.33334 7.5L12.6667 7.5C12.9428 7.5 13.1667 7.72386 13.1667 8Z" /> + {:else if name === "tracking-on"} + + + + + + + {:else if name === "tracking-off"} + + {:else if name === "user"} diff --git a/src/views/projects/Commit.svelte b/src/views/projects/Commit.svelte index 9904a254b..c2092fd8b 100644 --- a/src/views/projects/Commit.svelte +++ b/src/views/projects/Commit.svelte @@ -12,6 +12,7 @@ export let baseUrl: BaseUrl; export let commit: Commit; export let project: Project; + export let tracking: boolean; $: header = commit.commit; @@ -41,7 +42,7 @@ } - +
diff --git a/src/views/projects/Header.svelte b/src/views/projects/Header.svelte index 8a1fc61b5..0b0aedc6b 100644 --- a/src/views/projects/Header.svelte +++ b/src/views/projects/Header.svelte @@ -71,21 +71,5 @@
- -
- -
diff --git a/src/views/projects/Header/TrackButton.svelte b/src/views/projects/Header/TrackButton.svelte new file mode 100644 index 000000000..45a21710e --- /dev/null +++ b/src/views/projects/Header/TrackButton.svelte @@ -0,0 +1,67 @@ + + + + + + + + +
+ Use the + Radicle CLI + + to {command} this project. +
+
+ Tracking means subscribing to updates from this repository. It also helps with + increasing its availability over the Radicle network. +
+ +
+
diff --git a/src/views/projects/History.svelte b/src/views/projects/History.svelte index 6e2cc998e..73614a5f2 100644 --- a/src/views/projects/History.svelte +++ b/src/views/projects/History.svelte @@ -29,6 +29,7 @@ export let revision: string | undefined; export let totalCommitCount: number; export let tree: Tree; + export let tracking: boolean; const api = new HttpdClient(baseUrl); @@ -114,7 +115,7 @@ } - +
- +
- +
{#if $httpdStore.state === "authenticated"} {@const session = $httpdStore.session} diff --git a/src/views/projects/Issues.svelte b/src/views/projects/Issues.svelte index cdb106370..ae947ed42 100644 --- a/src/views/projects/Issues.svelte +++ b/src/views/projects/Issues.svelte @@ -25,6 +25,7 @@ export let issues: Issue[]; export let project: Project; export let state: IssueState["status"]; + export let tracking: boolean; let loading = false; let page = 0; @@ -78,7 +79,7 @@ } - +
diff --git a/src/views/projects/Layout.svelte b/src/views/projects/Layout.svelte index e329911fb..1edd20296 100644 --- a/src/views/projects/Layout.svelte +++ b/src/views/projects/Layout.svelte @@ -7,16 +7,18 @@ import markdown from "@app/lib/markdown"; import { twemoji, isLocal } from "@app/lib/utils"; + import Button from "@app/components/Button.svelte"; import Clipboard from "@app/components/Clipboard.svelte"; import CloneButton from "@app/views/projects/Header/CloneButton.svelte"; import Link from "@app/components/Link.svelte"; - import Button from "@app/components/Button.svelte"; import Header from "./Header.svelte"; + import TrackButton from "./Header/TrackButton.svelte"; export let activeTab: ActiveTab = undefined; export let baseUrl: BaseUrl; export let project: Project; + export let tracking: boolean; const render = (content: string): string => dompurify.sanitize(markdown.parse(content) as string); @@ -117,7 +119,10 @@ {isLocal(baseUrl.hostname) ? "radicle.local" : baseUrl.hostname} - +
diff --git a/src/views/projects/Patch.svelte b/src/views/projects/Patch.svelte index d60ce49f1..e41a1dd25 100644 --- a/src/views/projects/Patch.svelte +++ b/src/views/projects/Patch.svelte @@ -74,6 +74,7 @@ export let patch: Patch; export let project: Project; export let view: PatchView; + export let tracking: boolean; $: api = new HttpdClient(baseUrl); @@ -440,7 +441,7 @@ } - +
diff --git a/src/views/projects/Patches.svelte b/src/views/projects/Patches.svelte index 8ca22a208..761677193 100644 --- a/src/views/projects/Patches.svelte +++ b/src/views/projects/Patches.svelte @@ -22,6 +22,7 @@ export let patches: Patch[]; export let project: Project; export let state: PatchState["status"]; + export let tracking: boolean; let loading = false; let page = 0; @@ -83,7 +84,7 @@ } - +
diff --git a/src/views/projects/Source.svelte b/src/views/projects/Source.svelte index 4b9361c02..edd43b976 100644 --- a/src/views/projects/Source.svelte +++ b/src/views/projects/Source.svelte @@ -25,6 +25,7 @@ export let project: Project; export let revision: string | undefined; export let tree: Tree; + export let tracking: boolean; // Whether the mobile file tree is visible. let mobileFileTree = false; @@ -125,7 +126,7 @@ } - +
{ + if (isLocal(route.node.hostname)) { + return true; + } else { + try { + await httpd.api.project.getById(route.project); + return true; + } catch (error: any) { + if (error.status === 404) { + return false; + } else { + // Either `radicle-httpd` isn't running or there was some other + // error. + return false; + } + } + } +} + export async function loadProjectRoute( route: ProjectRoute, ): Promise { const api = new HttpdClient(route.node); + try { if (route.resource === "project.source") { return await loadTreeView(route); } else if (route.resource === "project.history") { return await loadHistoryView(route); } else if (route.resource === "project.commit") { - const [project, commit] = await Promise.all([ + const [project, commit, tracking] = await Promise.all([ api.project.getById(route.project), api.project.getCommitBySha(route.project, route.commit), + isLocalNodeTracking(route), ]); return { @@ -253,12 +282,14 @@ export async function loadProjectRoute( baseUrl: route.node, project, commit, + tracking, }, }; } else if (route.resource === "project.issue") { - const [project, issue] = await Promise.all([ + const [project, issue, tracking] = await Promise.all([ api.project.getById(route.project), api.project.getIssueById(route.project, route.issue), + isLocalNodeTracking(route), ]); return { resource: "project.issue", @@ -266,6 +297,7 @@ export async function loadProjectRoute( baseUrl: route.node, project, issue, + tracking, }, }; } else if (route.resource === "project.patch") { @@ -273,12 +305,16 @@ export async function loadProjectRoute( } else if (route.resource === "project.issues") { return await loadIssuesView(route); } else if (route.resource === "project.newIssue") { - const project = await api.project.getById(route.project); + const [project, tracking] = await Promise.all([ + api.project.getById(route.project), + isLocalNodeTracking(route), + ]); return { resource: "project.newIssue", params: { baseUrl: route.node, project, + tracking, }, }; } else if (route.resource === "project.patches") { @@ -326,13 +362,14 @@ async function loadPatchesView( const searchParams = new URLSearchParams(route.search || ""); const state = (searchParams.get("state") as PatchState["status"]) || "open"; - const [project, patches] = await Promise.all([ + const [project, patches, tracking] = await Promise.all([ api.project.getById(route.project), api.project.getAllPatches(route.project, { state, page: 0, perPage: PATCHES_PER_PAGE, }), + isLocalNodeTracking(route), ]); return { @@ -342,6 +379,7 @@ async function loadPatchesView( patches, state, project, + tracking, }, }; } @@ -352,13 +390,14 @@ async function loadIssuesView( const api = new HttpdClient(route.node); const state = route.state || "open"; - const [project, issues] = await Promise.all([ + const [project, issues, tracking] = await Promise.all([ api.project.getById(route.project), api.project.getAllIssues(route.project, { state, page: 0, perPage: ISSUES_PER_PAGE, }), + isLocalNodeTracking(route), ]); return { @@ -368,6 +407,7 @@ async function loadIssuesView( issues, state, project, + tracking, }, }; } @@ -377,10 +417,11 @@ async function loadTreeView( ): Promise { const api = new HttpdClient(route.node); - const [project, peers, branchMap] = await Promise.all([ + const [project, peers, branchMap, tracking] = await Promise.all([ api.project.getById(route.project), api.project.getAllRemotes(route.project), getPeerBranches(api, route.project, route.peer), + isLocalNodeTracking(route), ]); if (route.route) { @@ -416,6 +457,7 @@ async function loadTreeView( tree, path, blobResult, + tracking, }, }; } @@ -486,13 +528,14 @@ async function loadHistoryView( ); } - const [tree, commitsResponse] = await Promise.all([ + const [tree, commitsResponse, tracking] = await Promise.all([ api.project.getTree(route.project, commitId), await api.project.getAllCommits(project.id, { parent: commitId, page: 0, perPage: COMMITS_PER_PAGE, }), + isLocalNodeTracking(route), ]); return { @@ -507,6 +550,7 @@ async function loadHistoryView( tree, commitHeaders: commitsResponse.commits.map(c => c.commit), totalCommitCount: commitsResponse.stats.commits, + tracking, }, }; } @@ -515,9 +559,10 @@ async function loadPatchView( route: ProjectPatchRoute, ): Promise { const api = new HttpdClient(route.node); - const [project, patch] = await Promise.all([ + const [project, patch, tracking] = await Promise.all([ api.project.getById(route.project), api.project.getPatchById(route.project, route.patch), + isLocalNodeTracking(route), ]); const latestRevision = patch.revisions[patch.revisions.length - 1]; @@ -572,6 +617,7 @@ async function loadPatchView( project, patch, view, + tracking, }, }; }