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 @@ - - - - - -
-
Status
- -
- -
- -
-
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/LabelInput.svelte b/src/components/LabelInput.svelte new file mode 100644 index 0000000..1d040b3 --- /dev/null +++ b/src/components/LabelInput.svelte @@ -0,0 +1,169 @@ + + + + +
+
+
Labels
+ + {#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} +
{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} - + + + + + + + + + + +