diff --git a/web/core/components/issues/issue-detail/issue-activity/comments/comment-block.tsx b/web/core/components/issues/issue-detail/issue-activity/comments/comment-block.tsx index 9a35ef1da4f..53ee0134335 100644 --- a/web/core/components/issues/issue-detail/issue-activity/comments/comment-block.tsx +++ b/web/core/components/issues/issue-detail/issue-activity/comments/comment-block.tsx @@ -25,7 +25,10 @@ export const IssueCommentBlock: FC = observer((props) => { if (!comment) return <>; return ( -
+
{comment.actor_detail?.avatar_url && comment.actor_detail?.avatar_url !== "" ? ( diff --git a/web/core/components/issues/issue-detail/issue-activity/comments/comment-card.tsx b/web/core/components/issues/issue-detail/issue-activity/comments/comment-card.tsx index 96f0960b605..22e10fce4b4 100644 --- a/web/core/components/issues/issue-detail/issue-activity/comments/comment-card.tsx +++ b/web/core/components/issues/issue-detail/issue-activity/comments/comment-card.tsx @@ -2,8 +2,9 @@ import { FC, useEffect, useRef, useState } from "react"; import { observer } from "mobx-react"; +import { useRouter, usePathname } from "next/navigation"; import { useForm } from "react-hook-form"; -import { Check, Globe2, Lock, Pencil, Trash2, X } from "lucide-react"; +import { Check, Globe2, Lock, Pencil, Trash2, X, Link } from "lucide-react"; // plane constants import { EIssueCommentAccessSpecifier } from "@plane/constants"; // plane editor @@ -13,13 +14,14 @@ import { useTranslation } from "@plane/i18n"; // plane types import { TIssueComment } from "@plane/types"; // plane ui -import { CustomMenu } from "@plane/ui"; +import { CustomMenu, TOAST_TYPE, setToast } from "@plane/ui"; // plane utils import { cn } from "@plane/utils"; // components import { LiteTextEditor, LiteTextReadOnlyEditor } from "@/components/editor"; // helpers -import { isCommentEmpty } from "@/helpers/string.helper"; +import { HIGHLIGHT_CLASS } from "@/components/issues/issue-layouts/utils"; +import { copyUrlToClipboard, isCommentEmpty } from "@/helpers/string.helper"; // hooks import { useIssueDetail, useUser, useWorkspace } from "@/hooks/store"; // components @@ -96,8 +98,42 @@ export const IssueCommentCard: FC = observer((props) => { } }, [isEditing, setFocus]); + useEffect(() => { + const hash = window.location.hash; + if (hash === `#comment-${commentId}`) { + setTimeout(() => { + const element = document.getElementById(`comment-${commentId}`); + if (element) { + element.scrollIntoView({ behavior: "smooth", block: "center" }); + element.classList.add(HIGHLIGHT_CLASS); + setTimeout(() => { + element.classList.remove(HIGHLIGHT_CLASS); + }, 2000); + } + }, 100); + } + }, [commentId]); + if (!comment || !currentUser) return <>; + const pathName = usePathname(); + const handleCommentLink = (id: string) => { + let path = pathName; + if (path.startsWith("/")) { + path = path.slice(1); + } + if (path.endsWith("/")) { + path = path.slice(0, -1); + } + const commentUrl = path + `#comment-${id}`; + copyUrlToClipboard(commentUrl).then(() => + setToast({ + type: TOAST_TYPE.SUCCESS, + title: "Link copied", + message: "Comment link copied to clipboard", + }) + ); + }; return ( = observer((props) => { <> {!disabled && currentUser?.id === comment.actor && ( + handleCommentLink(commentId)} className="flex items-center gap-1"> + + {t("common.actions.copy_link")} + setIsEditing(true)} className="flex items-center gap-1"> {t("common.actions.edit")}