diff --git a/src/components/IssueMetadata.svelte b/src/components/IssueMetadata.svelte
deleted file mode 100644
index 7101f3a..0000000
--- a/src/components/IssueMetadata.svelte
+++ /dev/null
@@ -1,70 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
Labels
-
- {#each issue.labels as label}
-
{label}
- {:else}
-
No labels.
- {/each}
-
-
-
-
-
-
-
Assignees
-
- {#each issue.assignees as assignee}
-
- {:else}
- Not assigned to anyone.
- {/each}
-
-
-
diff --git a/src/components/IssueTeaser.svelte b/src/components/IssueTeaser.svelte
index c6bf9dd..62a55e2 100644
--- a/src/components/IssueTeaser.svelte
+++ b/src/components/IssueTeaser.svelte
@@ -12,6 +12,7 @@
import Icon from "./Icon.svelte";
import Id from "./Id.svelte";
import InlineTitle from "./InlineTitle.svelte";
+ import Label from "./Label.svelte";
import NodeId from "./NodeId.svelte";
interface Props {
@@ -91,7 +92,7 @@
{#if !compact}
{#each issue.labels as label}
-
{label}
+
{/each}
{/if}
diff --git a/src/components/Label.svelte b/src/components/Label.svelte
new file mode 100644
index 0000000..84f3549
--- /dev/null
+++ b/src/components/Label.svelte
@@ -0,0 +1,11 @@
+
+
+
diff --git a/src/components/LabelInput.svelte b/src/components/LabelInput.svelte
new file mode 100644
index 0000000..f6ed6b7
--- /dev/null
+++ b/src/components/LabelInput.svelte
@@ -0,0 +1,170 @@
+
+
+
+
+
+
+
+
+ {#if allowedToEdit}
+
+ {#if showInput}
+
+ {
+ inputValue = "";
+ showInput = false;
+ }}
+ name="cross"
+ styleCursor="pointer" />
+ {:else}
+ (showInput = true)}
+ styleCursor="pointer">
+ {/if}
+
+ {/if}
+
+
+
+ {#if allowedToEdit}
+ {#each updatedLabels as label}
+
+ {:else}
+
No labels.
+ {/each}
+ {:else}
+ {#each updatedLabels as label}
+
+ {:else}
+
No labels.
+ {/each}
+ {/if}
+
+
+ {#if showInput}
+
+
+ {#if !valid && validationMessage}
+
+ {validationMessage}
+
+ {/if}
+
+ {/if}
+
diff --git a/src/views/repo/Issue.svelte b/src/views/repo/Issue.svelte
index 1da20f0..25a3688 100644
--- a/src/views/repo/Issue.svelte
+++ b/src/views/repo/Issue.svelte
@@ -12,19 +12,25 @@
import * as roles from "@app/lib/roles";
import { invoke } from "@app/lib/invoke";
- import { publicKeyFromDid, scrollIntoView } from "@app/lib/utils";
+ import {
+ publicKeyFromDid,
+ scrollIntoView,
+ authorForNodeId,
+ } from "@app/lib/utils";
import { announce } from "@app/components/AnnounceSwitch.svelte";
+ import Border from "@app/components/Border.svelte";
import CommentComponent from "@app/components/Comment.svelte";
import CommentToggleInput from "@app/components/CommentToggleInput.svelte";
import CopyableId from "@app/components/CopyableId.svelte";
import Icon from "@app/components/Icon.svelte";
import InlineTitle from "@app/components/InlineTitle.svelte";
- import IssueMetadata from "@app/components/IssueMetadata.svelte";
import IssueSecondColumn from "@app/components/IssueSecondColumn.svelte";
+ import IssueStateBadge from "@app/components/IssueStateBadge.svelte";
import IssueStateButton from "@app/components/IssueStateButton.svelte";
import IssueTimelineLifecycleAction from "@app/components/IssueTimelineLifecycleAction.svelte";
+ import LabelInput from "@app/components/LabelInput.svelte";
import Link from "@app/components/Link.svelte";
import NodeId from "@app/components/NodeId.svelte";
import TextInput from "@app/components/TextInput.svelte";
@@ -54,26 +60,48 @@
/* eslint-enable prefer-const */
const issues = $state(initialIssues);
+
let topLevelReplyOpen = $state(false);
let editingTitle = $state(false);
- let updatedTitle = $state(issue.title);
+ let updatedTitle = $state("");
+ let labelSaveInProgress: boolean = $state(false);
- // The view doesn't get destroyed when we switch between different issues in
- // the second column and because of that the top-level state gets retained
- // when the issue changes. This reactive statement makes sure we always load
- // the new issue and reset the state to defaults.
- let issueId = issue.id;
$effect(() => {
- if (issueId !== issue.id) {
- issueId = issue.id;
- topLevelReplyOpen = false;
- editingTitle = false;
- updatedTitle = issue.title;
- }
+ // The component doesn't get destroyed when we switch between different
+ // issues in the second column and because of that the top-level state
+ // gets retained when the issue changes. This reactive statement makes
+ // sure we always reset the state to defaults.
+
+ // eslint-disable-next-line @typescript-eslint/no-unused-expressions
+ issue.id;
+
+ topLevelReplyOpen = false;
+ editingTitle = false;
+ updatedTitle = issue.title;
});
const project = $derived(repo.payloads["xyz.radicle.project"]!);
+ async function saveLabels(labels: string[]) {
+ try {
+ labelSaveInProgress = true;
+ await invoke("edit_issue", {
+ rid: repo.rid,
+ cobId: issue.id,
+ action: {
+ type: "label",
+ labels,
+ },
+ opts: { announce: $announce },
+ });
+ } catch (error) {
+ console.error("Editing labels failed", error);
+ } finally {
+ labelSaveInProgress = false;
+ await reload();
+ }
+ }
+
async function toggleReply() {
topLevelReplyOpen = !topLevelReplyOpen;
if (!topLevelReplyOpen) {
@@ -293,6 +321,26 @@
margin-left: 1rem;
align-items: center;
}
+
+ .metadata-divider {
+ width: 2px;
+ background-color: var(--color-fill-ghost);
+ height: calc(100% + 4px);
+ top: 0;
+ position: relative;
+ }
+ .metadata-section {
+ padding: 0.5rem;
+ font-size: var(--font-size-small);
+ display: flex;
+ flex-direction: column;
+ align-items: flex-start;
+ height: 100%;
+ }
+ .metadata-section-title {
+ margin-bottom: 0.5rem;
+ color: var(--color-foreground-dim);
+ }
@@ -386,7 +434,39 @@
{/if}
-