From 511a2d4bea3fd6698a880ab664e1f50016b3a00a Mon Sep 17 00:00:00 2001 From: Oliver Bryan <04oliverbryan@gmail.com> Date: Sun, 11 Jan 2026 16:40:40 +0000 Subject: [PATCH] copy link to issue --- .../src/components/issue-detail-pane.tsx | 43 ++++++++++++++++--- 1 file changed, 38 insertions(+), 5 deletions(-) diff --git a/packages/frontend/src/components/issue-detail-pane.tsx b/packages/frontend/src/components/issue-detail-pane.tsx index 11af218..0eca03d 100644 --- a/packages/frontend/src/components/issue-detail-pane.tsx +++ b/packages/frontend/src/components/issue-detail-pane.tsx @@ -1,6 +1,6 @@ import type { IssueResponse, ProjectResponse, UserRecord } from "@issue/shared"; -import { Trash, X } from "lucide-react"; -import { useEffect, useState } from "react"; +import { Check, Link, Trash, X } from "lucide-react"; +import { useEffect, useRef, useState } from "react"; import { useSession } from "@/components/session-provider"; import SmallUserDisplay from "@/components/small-user-display"; import { StatusSelect } from "@/components/status-select"; @@ -36,12 +36,22 @@ export function IssueDetailPane({ ); const [status, setStatus] = useState(issueData.Issue.status); const [deleteOpen, setDeleteOpen] = useState(false); + const [linkCopied, setLinkCopied] = useState(false); + const copyTimeoutRef = useRef(null); useEffect(() => { setAssigneeId(issueData.Issue.assigneeId?.toString() ?? "unassigned"); setStatus(issueData.Issue.status); }, [issueData.Issue.assigneeId, issueData.Issue.status]); + useEffect(() => { + return () => { + if (copyTimeoutRef.current) { + window.clearTimeout(copyTimeoutRef.current); + } + }; + }, []); + const handleAssigneeChange = async (value: string) => { setAssigneeId(value); const newAssigneeId = value === "unassigned" ? null : Number(value); @@ -79,6 +89,22 @@ export function IssueDetailPane({ setDeleteOpen(true); }; + const handleCopyLink = async () => { + try { + await navigator.clipboard.writeText(window.location.href); + setLinkCopied(true); + if (copyTimeoutRef.current) { + window.clearTimeout(copyTimeoutRef.current); + } + copyTimeoutRef.current = window.setTimeout(() => { + setLinkCopied(false); + copyTimeoutRef.current = null; + }, 1500); + } catch (error) { + console.error("error copying issue link:", error); + } + }; + const handleConfirmDelete = async () => { await issue.delete({ issueId: issueData.Issue.id, @@ -100,12 +126,19 @@ export function IssueDetailPane({ {issueID(project.Project.key, issueData.Issue.number)}

-
+
+