mirror of
https://seed.radicle.dev/rad:z4V1sjrXqjvFdnCUbxPFqd5p4DtH5.git
synced 2026-10-31 01:00:47 +08:00
Add repo issues and patches routes
This commit is contained in:
@@ -9,6 +9,8 @@
|
||||
|
||||
import AuthenticationError from "@app/views/AuthenticationError.svelte";
|
||||
import Home from "@app/views/Home.svelte";
|
||||
import Issues from "@app/views/repo/Issues.svelte";
|
||||
import Patches from "@app/views/repo/Patches.svelte";
|
||||
|
||||
const activeRouteStore = router.activeRouteStore;
|
||||
|
||||
@@ -40,6 +42,10 @@
|
||||
<!-- Don't show anything -->
|
||||
{:else if $activeRouteStore.resource === "home"}
|
||||
<Home {...$activeRouteStore.params} />
|
||||
{:else if $activeRouteStore.resource === "repo.issues"}
|
||||
<Issues {...$activeRouteStore.params} />
|
||||
{:else if $activeRouteStore.resource === "repo.patches"}
|
||||
<Patches {...$activeRouteStore.params} />
|
||||
{:else if $activeRouteStore.resource === "authenticationError"}
|
||||
<AuthenticationError {...$activeRouteStore.params} />
|
||||
{:else}
|
||||
|
||||
@@ -31,7 +31,12 @@
|
||||
}
|
||||
</style>
|
||||
|
||||
<Border variant="ghost" styleWidth="100%" stylePadding="8px 12px" hoverable>
|
||||
<Border
|
||||
variant="ghost"
|
||||
styleWidth="100%"
|
||||
stylePadding="8px 12px"
|
||||
hoverable
|
||||
on:click>
|
||||
<div class="container txt-small">
|
||||
<div class="global-flex header">
|
||||
<div class="global-flex">
|
||||
|
||||
+12
-5
@@ -5,6 +5,7 @@ import { get, writable } from "svelte/store";
|
||||
import * as mutexExecutor from "@app/lib/mutexExecutor";
|
||||
import * as utils from "@app/lib/utils";
|
||||
import { loadRoute } from "@app/lib/router/definitions";
|
||||
import { repoRouteToPath, repoUrlToRoute } from "@app/views/repo/router";
|
||||
|
||||
export { type Route };
|
||||
|
||||
@@ -45,10 +46,8 @@ async function navigateToUrl(
|
||||
if (route) {
|
||||
await navigate(action, route);
|
||||
} else {
|
||||
await navigate(action, {
|
||||
// On 404 we redirect to the Home page, shouldn't happen.
|
||||
resource: "home",
|
||||
});
|
||||
console.error("Could not resolve route for URL: ", url);
|
||||
await navigate(action, { resource: "home" });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,12 +94,15 @@ export async function replace(newRoute: Route): Promise<void> {
|
||||
|
||||
function urlToRoute(url: URL): Route | null {
|
||||
const segments = url.pathname.substring(1).split("/");
|
||||
|
||||
const resource = segments.shift();
|
||||
|
||||
switch (resource) {
|
||||
case "": {
|
||||
return { resource: "home" };
|
||||
}
|
||||
case "repos": {
|
||||
return repoUrlToRoute(segments, url.searchParams);
|
||||
}
|
||||
case "authenticationError": {
|
||||
return { resource: "authenticationError", params: { error: "" } };
|
||||
}
|
||||
@@ -115,6 +117,11 @@ export function routeToPath(route: Route): string {
|
||||
return "/";
|
||||
} else if (route.resource === "authenticationError") {
|
||||
return "/authenticationError";
|
||||
} else if (
|
||||
route.resource === "repo.issues" ||
|
||||
route.resource === "repo.patches"
|
||||
) {
|
||||
return repoRouteToPath(route);
|
||||
} else if (route.resource === "booting") {
|
||||
return "";
|
||||
} else {
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
import type { RepoInfo } from "@bindings/RepoInfo";
|
||||
import type { Config } from "@bindings/Config";
|
||||
import type { RepoInfo } from "@bindings/RepoInfo";
|
||||
import type { LoadedRepoRoute, RepoRoute } from "@app/views/repo/router";
|
||||
|
||||
import { invoke } from "@tauri-apps/api/core";
|
||||
|
||||
import { loadIssues, loadPatches } from "@app/views/repo/router";
|
||||
|
||||
interface BootingRoute {
|
||||
resource: "booting";
|
||||
}
|
||||
@@ -24,12 +27,17 @@ interface LoadedHomeRoute {
|
||||
params: { repos: RepoInfo[]; config: Config };
|
||||
}
|
||||
|
||||
export type Route = BootingRoute | HomeRoute | AuthenticationErrorRoute;
|
||||
export type Route =
|
||||
| AuthenticationErrorRoute
|
||||
| BootingRoute
|
||||
| HomeRoute
|
||||
| RepoRoute;
|
||||
|
||||
export type LoadedRoute =
|
||||
| AuthenticationErrorRoute
|
||||
| BootingRoute
|
||||
| LoadedHomeRoute
|
||||
| AuthenticationErrorRoute;
|
||||
| LoadedRepoRoute;
|
||||
|
||||
export async function loadRoute(
|
||||
route: Route,
|
||||
@@ -39,6 +47,10 @@ export async function loadRoute(
|
||||
const repos: RepoInfo[] = await invoke("list_repos");
|
||||
const config: Config = await invoke("config");
|
||||
return { resource: "home", params: { repos, config } };
|
||||
} else if (route.resource === "repo.issues") {
|
||||
return loadIssues(route);
|
||||
} else if (route.resource === "repo.patches") {
|
||||
return loadPatches(route);
|
||||
}
|
||||
return route;
|
||||
}
|
||||
|
||||
+12
-16
@@ -2,26 +2,13 @@
|
||||
import type { Config } from "@bindings/Config";
|
||||
import type { RepoInfo } from "@bindings/RepoInfo";
|
||||
|
||||
import * as router from "@app/lib/router";
|
||||
|
||||
import Header from "@app/components/Header.svelte";
|
||||
import RepoCard from "@app/components/RepoCard.svelte";
|
||||
import { onMount } from "svelte";
|
||||
import { invoke } from "@tauri-apps/api/core";
|
||||
|
||||
export let repos: RepoInfo[];
|
||||
export let config: Config;
|
||||
|
||||
onMount(async () => {
|
||||
const patches = await invoke("list_patches", {
|
||||
rid: "rad:z3gqcJUoA1n9HaHKufZs5FCSGazv5",
|
||||
status: "open",
|
||||
});
|
||||
const issues = await invoke("list_issues", {
|
||||
rid: "rad:z3gqcJUoA1n9HaHKufZs5FCSGazv5",
|
||||
status: "open",
|
||||
});
|
||||
|
||||
console.log(patches, issues);
|
||||
});
|
||||
</script>
|
||||
|
||||
<style>
|
||||
@@ -40,7 +27,16 @@
|
||||
<div class="repo-grid">
|
||||
{#each repos as repo}
|
||||
{#if repo.payloads["xyz.radicle.project"]}
|
||||
<RepoCard {repo} selfDid={`did:key:${config.publicKey}`} />
|
||||
<RepoCard
|
||||
{repo}
|
||||
selfDid={`did:key:${config.publicKey}`}
|
||||
on:click={() => {
|
||||
void router.push({
|
||||
resource: "repo.issues",
|
||||
rid: repo.rid,
|
||||
status: "open",
|
||||
});
|
||||
}} />
|
||||
{/if}
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
<script lang="ts">
|
||||
import type { Issue } from "@bindings/Issue";
|
||||
import type { RepoInfo } from "@bindings/RepoInfo";
|
||||
|
||||
import Layout from "./Layout.svelte";
|
||||
|
||||
export let repo: RepoInfo;
|
||||
export let issues: Issue[];
|
||||
</script>
|
||||
|
||||
<Layout {repo}>
|
||||
<pre>
|
||||
<!-- prettier-ignore -->
|
||||
{#each issues as issue}
|
||||
- {issue.title}
|
||||
{:else}
|
||||
No issues.
|
||||
{/each}
|
||||
</pre>
|
||||
</Layout>
|
||||
@@ -0,0 +1,38 @@
|
||||
<script lang="ts">
|
||||
import type { RepoInfo } from "@bindings/RepoInfo";
|
||||
|
||||
import Header from "@app/components/Header.svelte";
|
||||
import Link from "@app/components/Link.svelte";
|
||||
|
||||
export let repo: RepoInfo;
|
||||
|
||||
$: project = repo.payloads["xyz.radicle.project"]!;
|
||||
</script>
|
||||
|
||||
<Header currentPage="Repositories" />
|
||||
<div>{project.data.name}</div>
|
||||
<div>{repo.rid}</div>
|
||||
|
||||
Issues
|
||||
<Link route={{ resource: "repo.issues", rid: repo.rid, status: "open" }}>
|
||||
Open
|
||||
</Link>
|
||||
<Link route={{ resource: "repo.issues", rid: repo.rid, status: "closed" }}>
|
||||
Closed
|
||||
</Link>
|
||||
|
||||
<br />
|
||||
Patches
|
||||
<Link route={{ resource: "repo.patches", rid: repo.rid, status: "draft" }}>
|
||||
Draft
|
||||
</Link>
|
||||
<Link route={{ resource: "repo.patches", rid: repo.rid, status: "open" }}>
|
||||
Open
|
||||
</Link>
|
||||
<Link route={{ resource: "repo.patches", rid: repo.rid, status: "archived" }}>
|
||||
Archived
|
||||
</Link>
|
||||
<Link route={{ resource: "repo.patches", rid: repo.rid, status: "merged" }}>
|
||||
Merged
|
||||
</Link>
|
||||
<slot />
|
||||
@@ -0,0 +1,20 @@
|
||||
<script lang="ts">
|
||||
import type { Patch } from "@bindings/Patch";
|
||||
import type { RepoInfo } from "@bindings/RepoInfo";
|
||||
|
||||
import Layout from "./Layout.svelte";
|
||||
|
||||
export let repo: RepoInfo;
|
||||
export let patches: Patch[];
|
||||
</script>
|
||||
|
||||
<Layout {repo}>
|
||||
<pre>
|
||||
<!-- prettier-ignore -->
|
||||
{#each patches as patch}
|
||||
- {patch.title}
|
||||
{:else}
|
||||
No patches.
|
||||
{/each}
|
||||
</pre>
|
||||
</Layout>
|
||||
@@ -0,0 +1,114 @@
|
||||
import type { RepoInfo } from "@bindings/RepoInfo";
|
||||
import type { Patch } from "@bindings/Patch";
|
||||
import type { Issue } from "@bindings/Issue";
|
||||
|
||||
import { invoke } from "@tauri-apps/api/core";
|
||||
import { unreachable } from "@app/lib/utils";
|
||||
|
||||
export interface RepoIssuesRoute {
|
||||
resource: "repo.issues";
|
||||
rid: string;
|
||||
status?: "open" | "closed";
|
||||
}
|
||||
|
||||
export interface LoadedRepoIssuesRoute {
|
||||
resource: "repo.issues";
|
||||
params: { repo: RepoInfo; issues: Issue[] };
|
||||
}
|
||||
|
||||
export interface RepoPatchesRoute {
|
||||
resource: "repo.patches";
|
||||
rid: string;
|
||||
status?: "draft" | "open" | "archived" | "merged";
|
||||
}
|
||||
|
||||
export interface LoadedRepoPatchesRoute {
|
||||
resource: "repo.patches";
|
||||
params: { repo: RepoInfo; patches: Patch[] };
|
||||
}
|
||||
|
||||
export type RepoRoute = RepoIssuesRoute | RepoPatchesRoute;
|
||||
export type LoadedRepoRoute = LoadedRepoIssuesRoute | LoadedRepoPatchesRoute;
|
||||
|
||||
export async function loadPatches(route: RepoRoute): Promise<LoadedRepoRoute> {
|
||||
const repo: RepoInfo = await invoke("repo_by_id", {
|
||||
rid: route.rid,
|
||||
});
|
||||
const patches: Patch[] = await invoke("list_patches", {
|
||||
rid: route.rid,
|
||||
status: route.status,
|
||||
});
|
||||
|
||||
return { resource: "repo.patches", params: { repo, patches } };
|
||||
}
|
||||
|
||||
export async function loadIssues(route: RepoRoute): Promise<LoadedRepoRoute> {
|
||||
const repo: RepoInfo = await invoke("repo_by_id", {
|
||||
rid: route.rid,
|
||||
});
|
||||
const issues: Issue[] = await invoke("list_issues", {
|
||||
rid: route.rid,
|
||||
status: route.status,
|
||||
});
|
||||
|
||||
return { resource: "repo.issues", params: { repo, issues } };
|
||||
}
|
||||
|
||||
export function repoRouteToPath(route: RepoRoute): string {
|
||||
const pathSegments = ["/repos", route.rid];
|
||||
|
||||
if (route.resource === "repo.issues") {
|
||||
let url = [...pathSegments, "issues"].join("/");
|
||||
const searchParams = new URLSearchParams();
|
||||
if (route.status) {
|
||||
searchParams.set("status", route.status);
|
||||
url += `?${searchParams}`;
|
||||
}
|
||||
return url;
|
||||
} else if (route.resource === "repo.patches") {
|
||||
let url = [...pathSegments, "patches"].join("/");
|
||||
const searchParams = new URLSearchParams();
|
||||
if (route.status) {
|
||||
searchParams.set("status", route.status);
|
||||
url += `?${searchParams}`;
|
||||
}
|
||||
return url;
|
||||
} else {
|
||||
return unreachable(route);
|
||||
}
|
||||
}
|
||||
|
||||
export function repoUrlToRoute(
|
||||
segments: string[],
|
||||
searchParams: URLSearchParams,
|
||||
): RepoRoute | null {
|
||||
const rid = segments.shift();
|
||||
const resource = segments.shift();
|
||||
|
||||
if (rid) {
|
||||
if (resource === "issues") {
|
||||
const status = searchParams.get("status");
|
||||
if (status === "open" || status === "closed") {
|
||||
return { resource: "repo.issues", rid, status };
|
||||
} else {
|
||||
return { resource: "repo.issues", rid };
|
||||
}
|
||||
} else if (resource === "patches") {
|
||||
const status = searchParams.get("status");
|
||||
if (
|
||||
status === "draft" ||
|
||||
status === "open" ||
|
||||
status === "archived" ||
|
||||
status === "merged"
|
||||
) {
|
||||
return { resource: "repo.patches", rid, status };
|
||||
} else {
|
||||
return { resource: "repo.patches", rid };
|
||||
}
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user