Add single patch route

This commit is contained in:
Rūdolfs Ošiņš
2024-09-27 16:59:16 +02:00
parent 1a0de05a58
commit ea9a425cf0
7 changed files with 292 additions and 47 deletions
+3
View File
@@ -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"}
+17 -18
View File
@@ -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
+1
View File
@@ -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);
+12 -1
View File
@@ -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);
}
+18
View File
@@ -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)",
};
+152
View File
@@ -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
View File
@@ -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;