mirror of
https://seed.radicle.dev/rad:z4V1sjrXqjvFdnCUbxPFqd5p4DtH5.git
synced 2026-10-27 15:40:36 +08:00
Add single patch route
This commit is contained in:
@@ -13,6 +13,7 @@
|
||||
import Home from "@app/views/Home.svelte";
|
||||
import Issue from "@app/views/repo/Issue.svelte";
|
||||
import Issues from "@app/views/repo/Issues.svelte";
|
||||
import Patch from "@app/views/repo/Patch.svelte";
|
||||
import Patches from "@app/views/repo/Patches.svelte";
|
||||
|
||||
const activeRouteStore = router.activeRouteStore;
|
||||
@@ -56,6 +57,8 @@
|
||||
<Issue {...$activeRouteStore.params} />
|
||||
{:else if $activeRouteStore.resource === "repo.issues"}
|
||||
<Issues {...$activeRouteStore.params} />
|
||||
{:else if $activeRouteStore.resource === "repo.patch"}
|
||||
<Patch {...$activeRouteStore.params} />
|
||||
{:else if $activeRouteStore.resource === "repo.patches"}
|
||||
<Patches {...$activeRouteStore.params} />
|
||||
{:else if $activeRouteStore.resource === "authenticationError"}
|
||||
|
||||
@@ -2,8 +2,14 @@
|
||||
import type { Patch } from "@bindings/Patch";
|
||||
import type { Stats } from "@bindings/Stats";
|
||||
|
||||
import { formatOid, formatTimestamp } from "@app/lib/utils";
|
||||
import {
|
||||
formatOid,
|
||||
formatTimestamp,
|
||||
patchStatusBackgroundColor,
|
||||
patchStatusColor,
|
||||
} from "@app/lib/utils";
|
||||
import { invoke } from "@tauri-apps/api/core";
|
||||
import { push } from "@app/lib/router";
|
||||
|
||||
import DiffStatBadge from "./DiffStatBadge.svelte";
|
||||
import Icon from "./Icon.svelte";
|
||||
@@ -12,20 +18,6 @@
|
||||
|
||||
export let patch: Patch;
|
||||
export let rid: string;
|
||||
|
||||
const statusColor: Record<Patch["state"]["status"], string> = {
|
||||
draft: "var(--color-fill-gray)",
|
||||
open: "var(--color-fill-success)",
|
||||
archived: "var(--color-foreground-yellow)",
|
||||
merged: "var(--color-fill-primary)",
|
||||
};
|
||||
|
||||
const statusBackgroundColor: Record<Patch["state"]["status"], string> = {
|
||||
draft: "var(--color-fill-ghost)",
|
||||
open: "var(--color-fill-diff-green)",
|
||||
archived: "var(--color-fill-private)",
|
||||
merged: "var(--color-fill-delegate)",
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
@@ -58,12 +50,19 @@
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="patch-teaser">
|
||||
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
||||
<div
|
||||
tabindex="0"
|
||||
role="button"
|
||||
class="patch-teaser"
|
||||
onclick={() => {
|
||||
void push({ resource: "repo.patch", rid, patch: patch.id });
|
||||
}}>
|
||||
<div class="global-flex">
|
||||
<div
|
||||
class="global-counter status"
|
||||
style:color={statusColor[patch.state.status]}
|
||||
style:background-color={statusBackgroundColor[patch.state.status]}>
|
||||
style:color={patchStatusColor[patch.state.status]}
|
||||
style:background-color={patchStatusBackgroundColor[patch.state.status]}>
|
||||
<Icon name="patch" />
|
||||
</div>
|
||||
<div
|
||||
|
||||
@@ -120,6 +120,7 @@ export function routeToPath(route: Route): string {
|
||||
} else if (
|
||||
route.resource === "repo.issue" ||
|
||||
route.resource === "repo.issues" ||
|
||||
route.resource === "repo.patch" ||
|
||||
route.resource === "repo.patches"
|
||||
) {
|
||||
return repoRouteToPath(route);
|
||||
|
||||
@@ -3,15 +3,22 @@ import type { RepoInfo } from "@bindings/RepoInfo";
|
||||
import type {
|
||||
LoadedRepoIssueRoute,
|
||||
LoadedRepoIssuesRoute,
|
||||
LoadedRepoPatchRoute,
|
||||
LoadedRepoPatchesRoute,
|
||||
RepoIssueRoute,
|
||||
RepoIssuesRoute,
|
||||
RepoPatchRoute,
|
||||
RepoPatchesRoute,
|
||||
} from "@app/views/repo/router";
|
||||
|
||||
import { invoke } from "@tauri-apps/api/core";
|
||||
|
||||
import { loadIssues, loadIssue, loadPatches } from "@app/views/repo/router";
|
||||
import {
|
||||
loadIssue,
|
||||
loadIssues,
|
||||
loadPatch,
|
||||
loadPatches,
|
||||
} from "@app/views/repo/router";
|
||||
|
||||
interface BootingRoute {
|
||||
resource: "booting";
|
||||
@@ -40,6 +47,7 @@ export type Route =
|
||||
| HomeRoute
|
||||
| RepoIssueRoute
|
||||
| RepoIssuesRoute
|
||||
| RepoPatchRoute
|
||||
| RepoPatchesRoute;
|
||||
|
||||
export type LoadedRoute =
|
||||
@@ -48,6 +56,7 @@ export type LoadedRoute =
|
||||
| LoadedHomeRoute
|
||||
| LoadedRepoIssueRoute
|
||||
| LoadedRepoIssuesRoute
|
||||
| LoadedRepoPatchRoute
|
||||
| LoadedRepoPatchesRoute;
|
||||
|
||||
export async function loadRoute(
|
||||
@@ -62,6 +71,8 @@ export async function loadRoute(
|
||||
return loadIssue(route);
|
||||
} else if (route.resource === "repo.issues") {
|
||||
return loadIssues(route);
|
||||
} else if (route.resource === "repo.patch") {
|
||||
return loadPatch(route);
|
||||
} else if (route.resource === "repo.patches") {
|
||||
return loadPatches(route);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { Issue } from "@bindings/Issue";
|
||||
import type { Patch } from "@bindings/Patch";
|
||||
|
||||
import bs58 from "bs58";
|
||||
import twemojiModule from "twemoji";
|
||||
@@ -115,3 +116,20 @@ export const issueStatusBackgroundColor: Record<
|
||||
open: "var(--color-fill-diff-green)",
|
||||
closed: "var(--color-fill-diff-red)",
|
||||
};
|
||||
|
||||
export const patchStatusColor: Record<Patch["state"]["status"], string> = {
|
||||
draft: "var(--color-fill-gray)",
|
||||
open: "var(--color-fill-success)",
|
||||
archived: "var(--color-foreground-yellow)",
|
||||
merged: "var(--color-fill-primary)",
|
||||
};
|
||||
|
||||
export const patchStatusBackgroundColor: Record<
|
||||
Patch["state"]["status"],
|
||||
string
|
||||
> = {
|
||||
draft: "var(--color-fill-ghost)",
|
||||
open: "var(--color-fill-diff-green)",
|
||||
archived: "var(--color-fill-private)",
|
||||
merged: "var(--color-fill-delegate)",
|
||||
};
|
||||
|
||||
@@ -0,0 +1,152 @@
|
||||
<script lang="ts">
|
||||
import type { Config } from "@bindings/Config";
|
||||
import type { Patch } from "@bindings/Patch";
|
||||
import type { RepoInfo } from "@bindings/RepoInfo";
|
||||
import type { Revision } from "@bindings/Revision";
|
||||
|
||||
import { formatTimestamp, formatOid, patchStatusColor } from "@app/lib/utils";
|
||||
|
||||
import Border from "@app/components/Border.svelte";
|
||||
import CopyableId from "@app/components/CopyableId.svelte";
|
||||
import Icon from "@app/components/Icon.svelte";
|
||||
import InlineTitle from "@app/components/InlineTitle.svelte";
|
||||
import Layout from "./Layout.svelte";
|
||||
import Link from "@app/components/Link.svelte";
|
||||
import NodeId from "@app/components/NodeId.svelte";
|
||||
import Markdown from "@app/components/Markdown.svelte";
|
||||
|
||||
export let repo: RepoInfo;
|
||||
export let patch: Patch;
|
||||
export let patches: Patch[];
|
||||
export let revisions: Revision[];
|
||||
export let config: Config;
|
||||
|
||||
$: project = repo.payloads["xyz.radicle.project"]!;
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.title {
|
||||
font-size: var(--font-size-medium);
|
||||
font-weight: var(--font-weight-medium);
|
||||
-webkit-user-select: text;
|
||||
user-select: text;
|
||||
margin-bottom: 1rem;
|
||||
margin-top: 0.35rem;
|
||||
}
|
||||
.patch-teaser {
|
||||
max-width: 11rem;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.patch-list {
|
||||
margin-top: 0.5rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.5rem;
|
||||
padding-bottom: 1rem;
|
||||
}
|
||||
.content {
|
||||
padding: 0 1rem 1rem 1rem;
|
||||
}
|
||||
|
||||
.body {
|
||||
background-color: var(--color-background-float);
|
||||
padding: 1rem;
|
||||
}
|
||||
</style>
|
||||
|
||||
<Layout {repo} selfDid={`did:key:${config.publicKey}`}>
|
||||
<svelte:fragment slot="breadcrumbs">
|
||||
<Link route={{ resource: "home" }}>
|
||||
<NodeId
|
||||
nodeId={config.publicKey}
|
||||
alias={config.alias}
|
||||
styleFontFamily="var(--font-family-sans-serif)"
|
||||
styleFontSize="var(--font-size-tiny)" />
|
||||
</Link>
|
||||
<Link route={{ resource: "repo.patches", rid: repo.rid, status: "open" }}>
|
||||
<div class="global-flex">
|
||||
<Icon name="chevron-right" />
|
||||
{project.data.name}
|
||||
</div>
|
||||
</Link>
|
||||
<Icon name="chevron-right" />
|
||||
Patches
|
||||
</svelte:fragment>
|
||||
|
||||
<svelte:fragment slot="header-center">
|
||||
<CopyableId id={patch.id} />
|
||||
</svelte:fragment>
|
||||
|
||||
<svelte:fragment slot="sidebar">
|
||||
<Border
|
||||
hoverable={false}
|
||||
variant="ghost"
|
||||
styleWidth="100%"
|
||||
styleHeight="32px">
|
||||
<div style:margin-left="0.5rem">
|
||||
<Icon name="patch" />
|
||||
</div>
|
||||
<span class="txt-small txt-semibold">Patches</span>
|
||||
<div class="global-flex txt-small" style:margin-left="auto">
|
||||
<div
|
||||
class="global-counter"
|
||||
style:padding="0 6px"
|
||||
style:background-color="var(--color-fill-ghost)"
|
||||
style:gap="4px">
|
||||
{project.meta.patches.draft +
|
||||
project.meta.patches.open +
|
||||
project.meta.patches.merged +
|
||||
project.meta.patches.archived}
|
||||
</div>
|
||||
</div>
|
||||
</Border>
|
||||
|
||||
<div class="patch-list">
|
||||
{#each patches as sidebarPatch}
|
||||
<Link
|
||||
variant="tab"
|
||||
route={{
|
||||
resource: "repo.patch",
|
||||
rid: repo.rid,
|
||||
patch: sidebarPatch.id,
|
||||
}}>
|
||||
<div class="global-flex">
|
||||
<div
|
||||
style:color={patchStatusColor[sidebarPatch.state.status]}
|
||||
style:margin-left="2px">
|
||||
<Icon name="patch" />
|
||||
</div>
|
||||
<span class="txt-small patch-teaser txt-overflow">
|
||||
<InlineTitle content={sidebarPatch.title} fontSize="small" />
|
||||
</span>
|
||||
</div>
|
||||
</Link>
|
||||
{/each}
|
||||
</div>
|
||||
</svelte:fragment>
|
||||
|
||||
<div class="content">
|
||||
<div class="title">
|
||||
<InlineTitle content={patch.title} fontSize="medium" />
|
||||
</div>
|
||||
<div class="txt-small body">
|
||||
{#if revisions[0].description.slice(-1)[0].body !== ""}
|
||||
<Markdown breaks content={revisions[0].description.slice(-1)[0].body} />
|
||||
{:else}
|
||||
<span class="txt-missing">No description.</span>
|
||||
{/if}
|
||||
<div class="global-flex txt-small" style:margin-top="1.5rem">
|
||||
<NodeId
|
||||
nodeId={patch.author.did.replace("did:key:", "")}
|
||||
alias={patch.author.alias} />
|
||||
opened
|
||||
<div class="global-oid">{formatOid(patch.id)}</div>
|
||||
{formatTimestamp(patch.timestamp)}
|
||||
</div>
|
||||
</div>
|
||||
<div class="txt-small" style:margin-top="1rem">Revisions</div>
|
||||
{#each revisions as revision}
|
||||
<div class="global-oid">{formatOid(revision.oid)}</div>
|
||||
{/each}
|
||||
</div>
|
||||
</Layout>
|
||||
+89
-28
@@ -2,6 +2,7 @@ import type { Config } from "@bindings/Config";
|
||||
import type { Issue } from "@bindings/Issue";
|
||||
import type { Patch } from "@bindings/Patch";
|
||||
import type { RepoInfo } from "@bindings/RepoInfo";
|
||||
import type { Revision } from "@bindings/Revision";
|
||||
|
||||
import { invoke } from "@tauri-apps/api/core";
|
||||
import { unreachable } from "@app/lib/utils";
|
||||
@@ -42,6 +43,23 @@ export interface LoadedRepoIssuesRoute {
|
||||
|
||||
export type PatchStatus = "all" | Patch["state"]["status"];
|
||||
|
||||
export interface RepoPatchRoute {
|
||||
resource: "repo.patch";
|
||||
rid: string;
|
||||
patch: string;
|
||||
}
|
||||
|
||||
export interface LoadedRepoPatchRoute {
|
||||
resource: "repo.patch";
|
||||
params: {
|
||||
repo: RepoInfo;
|
||||
config: Config;
|
||||
patch: Patch;
|
||||
patches: Patch[];
|
||||
revisions: Revision[];
|
||||
};
|
||||
}
|
||||
|
||||
export interface RepoPatchesRoute {
|
||||
resource: "repo.patches";
|
||||
rid: string;
|
||||
@@ -58,12 +76,43 @@ export interface LoadedRepoPatchesRoute {
|
||||
};
|
||||
}
|
||||
|
||||
export type RepoRoute = RepoIssueRoute | RepoIssuesRoute | RepoPatchesRoute;
|
||||
export type RepoRoute =
|
||||
| RepoIssueRoute
|
||||
| RepoIssuesRoute
|
||||
| RepoPatchRoute
|
||||
| RepoPatchesRoute;
|
||||
export type LoadedRepoRoute =
|
||||
| LoadedRepoIssueRoute
|
||||
| LoadedRepoIssuesRoute
|
||||
| LoadedRepoPatchRoute
|
||||
| LoadedRepoPatchesRoute;
|
||||
|
||||
export async function loadPatch(
|
||||
route: RepoPatchRoute,
|
||||
): Promise<LoadedRepoPatchRoute> {
|
||||
const repo: RepoInfo = await invoke("repo_by_id", {
|
||||
rid: route.rid,
|
||||
});
|
||||
const config: Config = await invoke("config");
|
||||
const patches: Patch[] = await invoke("list_patches", {
|
||||
rid: route.rid,
|
||||
status: "all",
|
||||
});
|
||||
const patch: Patch = await invoke("patch_by_id", {
|
||||
rid: route.rid,
|
||||
id: route.patch,
|
||||
});
|
||||
const revisions: Revision[] = await invoke("revisions_by_patch", {
|
||||
rid: route.rid,
|
||||
id: route.patch,
|
||||
});
|
||||
|
||||
return {
|
||||
resource: "repo.patch",
|
||||
params: { repo, config, patch, patches, revisions },
|
||||
};
|
||||
}
|
||||
|
||||
export async function loadPatches(
|
||||
route: RepoPatchesRoute,
|
||||
): Promise<LoadedRepoPatchesRoute> {
|
||||
@@ -82,24 +131,6 @@ export async function loadPatches(
|
||||
};
|
||||
}
|
||||
|
||||
export async function loadIssues(
|
||||
route: RepoIssuesRoute,
|
||||
): Promise<LoadedRepoIssuesRoute> {
|
||||
const repo: RepoInfo = await invoke("repo_by_id", {
|
||||
rid: route.rid,
|
||||
});
|
||||
const config: Config = await invoke("config");
|
||||
const issues: Issue[] = await invoke("list_issues", {
|
||||
rid: route.rid,
|
||||
status: route.status,
|
||||
});
|
||||
|
||||
return {
|
||||
resource: "repo.issues",
|
||||
params: { repo, config, issues, status: route.status },
|
||||
};
|
||||
}
|
||||
|
||||
export async function loadIssue(
|
||||
route: RepoIssueRoute,
|
||||
): Promise<LoadedRepoIssueRoute> {
|
||||
@@ -122,6 +153,24 @@ export async function loadIssue(
|
||||
};
|
||||
}
|
||||
|
||||
export async function loadIssues(
|
||||
route: RepoIssuesRoute,
|
||||
): Promise<LoadedRepoIssuesRoute> {
|
||||
const repo: RepoInfo = await invoke("repo_by_id", {
|
||||
rid: route.rid,
|
||||
});
|
||||
const config: Config = await invoke("config");
|
||||
const issues: Issue[] = await invoke("list_issues", {
|
||||
rid: route.rid,
|
||||
status: route.status,
|
||||
});
|
||||
|
||||
return {
|
||||
resource: "repo.issues",
|
||||
params: { repo, config, issues, status: route.status },
|
||||
};
|
||||
}
|
||||
|
||||
export function repoRouteToPath(route: RepoRoute): string {
|
||||
const pathSegments = ["/repos", route.rid];
|
||||
|
||||
@@ -136,6 +185,9 @@ export function repoRouteToPath(route: RepoRoute): string {
|
||||
url += `?${searchParams}`;
|
||||
}
|
||||
return url;
|
||||
} else if (route.resource === "repo.patch") {
|
||||
const url = [...pathSegments, "patches", route.patch].join("/");
|
||||
return url;
|
||||
} else if (route.resource === "repo.patches") {
|
||||
let url = [...pathSegments, "patches"].join("/");
|
||||
const searchParams = new URLSearchParams();
|
||||
@@ -174,16 +226,25 @@ export function repoUrlToRoute(
|
||||
}
|
||||
}
|
||||
} else if (resource === "patches") {
|
||||
const status = searchParams.get("status");
|
||||
if (
|
||||
status === "draft" ||
|
||||
status === "open" ||
|
||||
status === "archived" ||
|
||||
status === "merged"
|
||||
) {
|
||||
return { resource: "repo.patches", rid, status };
|
||||
const id = segments.shift();
|
||||
if (id) {
|
||||
return {
|
||||
resource: "repo.patch",
|
||||
rid,
|
||||
patch: id,
|
||||
};
|
||||
} else {
|
||||
return { resource: "repo.patches", rid, status: "all" };
|
||||
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, status: "all" };
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return null;
|
||||
|
||||
Reference in New Issue
Block a user