mirror of
https://github.com/hex248/sprint.git
synced 2026-02-08 10:33:01 +00:00
copy link to issue
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
import type { IssueResponse, ProjectResponse, UserRecord } from "@issue/shared";
|
import type { IssueResponse, ProjectResponse, UserRecord } from "@issue/shared";
|
||||||
import { Trash, X } from "lucide-react";
|
import { Check, Link, Trash, X } from "lucide-react";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useRef, useState } from "react";
|
||||||
import { useSession } from "@/components/session-provider";
|
import { useSession } from "@/components/session-provider";
|
||||||
import SmallUserDisplay from "@/components/small-user-display";
|
import SmallUserDisplay from "@/components/small-user-display";
|
||||||
import { StatusSelect } from "@/components/status-select";
|
import { StatusSelect } from "@/components/status-select";
|
||||||
@@ -36,12 +36,22 @@ export function IssueDetailPane({
|
|||||||
);
|
);
|
||||||
const [status, setStatus] = useState<string>(issueData.Issue.status);
|
const [status, setStatus] = useState<string>(issueData.Issue.status);
|
||||||
const [deleteOpen, setDeleteOpen] = useState(false);
|
const [deleteOpen, setDeleteOpen] = useState(false);
|
||||||
|
const [linkCopied, setLinkCopied] = useState(false);
|
||||||
|
const copyTimeoutRef = useRef<number | null>(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setAssigneeId(issueData.Issue.assigneeId?.toString() ?? "unassigned");
|
setAssigneeId(issueData.Issue.assigneeId?.toString() ?? "unassigned");
|
||||||
setStatus(issueData.Issue.status);
|
setStatus(issueData.Issue.status);
|
||||||
}, [issueData.Issue.assigneeId, issueData.Issue.status]);
|
}, [issueData.Issue.assigneeId, issueData.Issue.status]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
return () => {
|
||||||
|
if (copyTimeoutRef.current) {
|
||||||
|
window.clearTimeout(copyTimeoutRef.current);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
const handleAssigneeChange = async (value: string) => {
|
const handleAssigneeChange = async (value: string) => {
|
||||||
setAssigneeId(value);
|
setAssigneeId(value);
|
||||||
const newAssigneeId = value === "unassigned" ? null : Number(value);
|
const newAssigneeId = value === "unassigned" ? null : Number(value);
|
||||||
@@ -79,6 +89,22 @@ export function IssueDetailPane({
|
|||||||
setDeleteOpen(true);
|
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 () => {
|
const handleConfirmDelete = async () => {
|
||||||
await issue.delete({
|
await issue.delete({
|
||||||
issueId: issueData.Issue.id,
|
issueId: issueData.Issue.id,
|
||||||
@@ -100,12 +126,19 @@ export function IssueDetailPane({
|
|||||||
{issueID(project.Project.key, issueData.Issue.number)}
|
{issueID(project.Project.key, issueData.Issue.number)}
|
||||||
</p>
|
</p>
|
||||||
</span>
|
</span>
|
||||||
<div className="flex gap-1 items-center">
|
<div className="flex items-center">
|
||||||
|
<Button
|
||||||
|
variant="dummy"
|
||||||
|
onClick={handleCopyLink}
|
||||||
|
className="px-0 py-0 w-6 h-6 hover:text-foreground/70"
|
||||||
|
title={linkCopied ? "Copied" : "Copy link"}
|
||||||
|
>
|
||||||
|
{linkCopied ? <Check /> : <Link />}
|
||||||
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
variant="dummy"
|
variant="dummy"
|
||||||
size="none"
|
|
||||||
onClick={handleDelete}
|
onClick={handleDelete}
|
||||||
className="text-destructive hover:text-destructive/70"
|
className="px-0 py-0 w-6 h-6 text-destructive hover:text-destructive/70"
|
||||||
>
|
>
|
||||||
<Trash />
|
<Trash />
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
Reference in New Issue
Block a user