Skip to content

Commit

Permalink
Add single issue view
Browse files Browse the repository at this point in the history
  • Loading branch information
rudolfs committed Sep 25, 2024
1 parent 01756a0 commit 2639996
Show file tree
Hide file tree
Showing 12 changed files with 296 additions and 48 deletions.
7 changes: 3 additions & 4 deletions src-tauri/src/commands/cobs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ use radicle::cob::ObjectId;
use radicle::git::Oid;
use radicle::identity::RepoId;
use radicle::issue::cache::Issues;
use radicle::issue::IssueId;
use radicle::patch::cache::Patches;

use crate::error::Error;
Expand Down Expand Up @@ -41,14 +40,14 @@ pub fn list_issues(
pub fn issues_by_id(
ctx: tauri::State<AppState>,
rid: RepoId,
id: IssueId,
id: Oid,
) -> Result<Option<cobs::Issue>, Error> {
let (repo, _) = ctx.repo(rid)?;
let issues = ctx.profile.issues(&repo)?;
let issue = issues.get(&id)?;
let issue = issues.get(&id.into())?;

let aliases = &ctx.profile.aliases();
let issue = issue.map(|issue| cobs::Issue::new(id, issue, aliases));
let issue = issue.map(|issue| cobs::Issue::new(id.into(), issue, aliases));

Ok::<_, Error>(issue)
}
Expand Down
5 changes: 4 additions & 1 deletion src/App.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import { onMount } from "svelte";
import { invoke } from "@tauri-apps/api/core";
import { listen } from "@tauri-apps/api/event";
import * as router from "@app/lib/router";
import { nodeRunning } from "@app/lib/events";
Expand All @@ -10,9 +11,9 @@
import AuthenticationError from "@app/views/AuthenticationError.svelte";
import Home from "@app/views/Home.svelte";
import Issue from "@app/views/repo/Issue.svelte";
import Issues from "@app/views/repo/Issues.svelte";
import Patches from "@app/views/repo/Patches.svelte";
import { listen } from "@tauri-apps/api/event";
const activeRouteStore = router.activeRouteStore;
Expand Down Expand Up @@ -51,6 +52,8 @@
<!-- Don't show anything -->
{:else if $activeRouteStore.resource === "home"}
<Home {...$activeRouteStore.params} />
{:else if $activeRouteStore.resource === "repo.issue"}
<Issue {...$activeRouteStore.params} />
{:else if $activeRouteStore.resource === "repo.issues"}
<Issues {...$activeRouteStore.params} />
{:else if $activeRouteStore.resource === "repo.patches"}
Expand Down
1 change: 1 addition & 0 deletions src/components/InlineTitle.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
font-family: var(--font-family-monospace);
padding: 0.125rem 0.25rem;
background-color: var(--color-fill-ghost);
font-size: inherit;
}
</style>

Expand Down
31 changes: 18 additions & 13 deletions src/components/IssueTeaser.svelte
Original file line number Diff line number Diff line change
@@ -1,22 +1,20 @@
<script lang="ts">
import type { Issue } from "@bindings/Issue";
import { formatOid, formatTimestamp } from "@app/lib/utils";
import {
formatOid,
formatTimestamp,
issueStatusBackgroundColor,
issueStatusColor,
} from "@app/lib/utils";
import { push } from "@app/lib/router";
import Icon from "./Icon.svelte";
import InlineTitle from "./InlineTitle.svelte";
import NodeId from "./NodeId.svelte";
export let issue: Issue;
const statusColor: Record<Issue["state"]["status"], string> = {
open: "var(--color-fill-success)",
closed: "var(--color-foreground-red)",
};
const statusBackgroundColor: Record<Issue["state"]["status"], string> = {
open: "var(--color-fill-diff-green)",
closed: "var(--color-fill-diff-red)",
};
export let rid: string;
</script>

<style>
Expand Down Expand Up @@ -49,12 +47,19 @@
}
</style>

<div class="issue-teaser">
<!-- svelte-ignore a11y_click_events_have_key_events -->
<div
tabindex="0"
role="button"
class="issue-teaser"
onclick={() => {
void push({ resource: "repo.issue", rid, issue: issue.id });
}}>
<div class="global-flex">
<div
class="global-counter status"
style:color={statusColor[issue.state.status]}
style:background-color={statusBackgroundColor[issue.state.status]}>
style:color={issueStatusColor[issue.state.status]}
style:background-color={issueStatusBackgroundColor[issue.state.status]}>
<Icon name="issue" />
</div>
<div
Expand Down
1 change: 1 addition & 0 deletions src/lib/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ export function routeToPath(route: Route): string {
} else if (route.resource === "authenticationError") {
return "/authenticationError";
} else if (
route.resource === "repo.issue" ||
route.resource === "repo.issues" ||
route.resource === "repo.patches"
) {
Expand Down
12 changes: 9 additions & 3 deletions src/lib/router/definitions.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
import type { Config } from "@bindings/Config";
import type { RepoInfo } from "@bindings/RepoInfo";
import type {
RepoIssuesRoute,
RepoPatchesRoute,
LoadedRepoIssueRoute,
LoadedRepoIssuesRoute,
LoadedRepoPatchesRoute,
RepoIssueRoute,
RepoIssuesRoute,
RepoPatchesRoute,
} from "@app/views/repo/router";

import { invoke } from "@tauri-apps/api/core";

import { loadIssues, loadPatches } from "@app/views/repo/router";
import { loadIssues, loadIssue, loadPatches } from "@app/views/repo/router";

interface BootingRoute {
resource: "booting";
Expand All @@ -36,13 +38,15 @@ export type Route =
| AuthenticationErrorRoute
| BootingRoute
| HomeRoute
| RepoIssueRoute
| RepoIssuesRoute
| RepoPatchesRoute;

export type LoadedRoute =
| AuthenticationErrorRoute
| BootingRoute
| LoadedHomeRoute
| LoadedRepoIssueRoute
| LoadedRepoIssuesRoute
| LoadedRepoPatchesRoute;

Expand All @@ -54,6 +58,8 @@ 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.issue") {
return loadIssue(route);
} else if (route.resource === "repo.issues") {
return loadIssues(route);
} else if (route.resource === "repo.patches") {
Expand Down
15 changes: 15 additions & 0 deletions src/lib/utils.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import type { Issue } from "@bindings/Issue";

import bs58 from "bs58";
import twemojiModule from "twemoji";

Expand Down Expand Up @@ -100,3 +102,16 @@ export function scrollIntoView(id: string, options?: ScrollIntoViewOptions) {
const lineElement = document.getElementById(id);
if (lineElement) lineElement.scrollIntoView(options);
}

export const issueStatusColor: Record<Issue["state"]["status"], string> = {
open: "var(--color-fill-success)",
closed: "var(--color-foreground-red)",
};

export const issueStatusBackgroundColor: Record<
Issue["state"]["status"],
string
> = {
open: "var(--color-fill-diff-green)",
closed: "var(--color-fill-diff-red)",
};
144 changes: 144 additions & 0 deletions src/views/repo/Issue.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
<script lang="ts">
import type { Config } from "@bindings/Config";
import type { Issue } from "@bindings/Issue";
import type { RepoInfo } from "@bindings/RepoInfo";
import { formatTimestamp, formatOid, issueStatusColor } 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 Markdown from "@app/components/Markdown.svelte";
import NodeId from "@app/components/NodeId.svelte";
export let repo: RepoInfo;
export let issue: Issue;
export let issues: Issue[];
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;
}
.issue-teaser {
max-width: 11rem;
white-space: nowrap;
}
.issue-list {
margin-top: 0.5rem;
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.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.issues", rid: repo.rid, status: "open" }}>
<div class="global-flex">
<Icon name="chevron-right" />
{project.data.name}
</div>
</Link>
<Icon name="chevron-right" />
Issues
</svelte:fragment>

<svelte:fragment slot="header-center">
<CopyableId id={issue.id} />
</svelte:fragment>

<svelte:fragment slot="sidebar">
<Border
hoverable={false}
variant="ghost"
styleWidth="100%"
styleHeight="32px">
<div style:margin-left="0.5rem">
<Icon name="issue" />
</div>
<span class="txt-small txt-semibold">Issues</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.issues.open + project.meta.issues.closed}
</div>
</div>
</Border>

<div class="issue-list">
{#each issues as sidebarIssue}
<Link
variant="tab"
route={{
resource: "repo.issue",
rid: repo.rid,
issue: sidebarIssue.id,
}}>
<div class="global-flex">
<div
style:color={issueStatusColor[sidebarIssue.state.status]}
style:margin-left="2px">
<Icon name="issue" />
</div>
<span class="txt-small issue-teaser txt-overflow">
<InlineTitle content={sidebarIssue.title} fontSize="small" />
</span>
</div>
</Link>
{/each}
</div>
</svelte:fragment>

<div class="content">
<div class="title">
<InlineTitle content={issue.title} fontSize="medium" />
</div>
<div class="txt-small body">
{#if issue.discussion[0].edits.slice(-1)[0].body !== ""}
<Markdown
breaks
content={issue.discussion[0].edits.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={issue.author.did.replace("did:key:", "")}
alias={issue.author.alias} />
opened
<div class="global-oid">{formatOid(issue.id)}</div>
{formatTimestamp(issue.timestamp)}
</div>
</div>
</div>
</Layout>
22 changes: 20 additions & 2 deletions src/views/repo/Issues.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,13 @@
import Layout from "./Layout.svelte";
import Border from "@app/components/Border.svelte";
import CopyableId from "@app/components/CopyableId.svelte";
import Icon from "@app/components/Icon.svelte";
import IssueTeaser from "@app/components/IssueTeaser.svelte";
import Link from "@app/components/Link.svelte";
import NodeId from "@app/components/NodeId.svelte";
import RepoHeader from "@app/components/RepoHeader.svelte";
export let repo: RepoInfo;
export let issues: Issue[];
Expand All @@ -28,7 +31,7 @@
}
</style>

<Layout {repo} selfDid={`did:key:${config.publicKey}`}>
<Layout>
<svelte:fragment slot="breadcrumbs">
<Link route={{ resource: "home" }}>
<NodeId
Expand All @@ -47,7 +50,22 @@
Issues
</svelte:fragment>

<svelte:fragment slot="header-center">
<CopyableId id={repo.rid} />
</svelte:fragment>

<svelte:fragment slot="sidebar">
<Border
hoverable={false}
variant="ghost"
styleWidth="100%"
styleHeight="32px">
<RepoHeader
{repo}
selfDid={`did:key:${config.publicKey}`}
emphasizedTitle={false} />
</Border>

<div class="global-flex txt-small" style:margin="0.5rem 0">
<Link
variant={status === "all" ? "active" : "tab"}
Expand Down Expand Up @@ -98,7 +116,7 @@

<div class="list">
{#each issues as issue}
<IssueTeaser {issue} />
<IssueTeaser {issue} rid={repo.rid} />
{/each}

{#if issues.length === 0}
Expand Down
Loading

0 comments on commit 2639996

Please sign in to comment.