diff --git a/moon/apps/web/components/Issues/IssueDetailPage.tsx b/moon/apps/web/components/Issues/IssueDetailPage.tsx
index f8f1bdf69..3438f4666 100644
--- a/moon/apps/web/components/Issues/IssueDetailPage.tsx
+++ b/moon/apps/web/components/Issues/IssueDetailPage.tsx
@@ -384,7 +384,7 @@ export default function IssueDetailPage({ link }: { link: string }) {
) : (
-
+
)}
{info && info.status === 'open' && (
diff --git a/moon/apps/web/components/MrView/CommentDropdownMenu.tsx b/moon/apps/web/components/MrView/CommentDropdownMenu.tsx
index 292038dff..7e6e55b43 100644
--- a/moon/apps/web/components/MrView/CommentDropdownMenu.tsx
+++ b/moon/apps/web/components/MrView/CommentDropdownMenu.tsx
@@ -18,17 +18,25 @@ import { useDeleteIssueComment } from '@/hooks/issues/useDeleteIssueComment'
import { useDeleteMrCommentDelete } from '@/hooks/useDeleteMrCommentDelete'
import { editIdAtom } from '../Issues/utils/store'
+import { SimpleNoteContentRef } from '../SimpleNoteEditor/SimpleNoteContent';
+import { generateJSON } from '@tiptap/core';
+import toast from 'react-hot-toast';
interface CommentDropdownMenuProps {
id: string
Conversation: ConversationItem
CommentType: 'mr' | 'issue' | (string & {})
+ editorRef: React.RefObject
}
-export function CommentDropdownMenu({ Conversation, id, CommentType }: CommentDropdownMenuProps) {
+
+export function CommentDropdownMenu({ Conversation, id, CommentType, editorRef }: CommentDropdownMenuProps) {
const { mutate: deleteComment } = useDeleteMrCommentDelete(id)
const { mutate: deleteIssueComment } = useDeleteIssueComment(id)
const [_editId, setEditId] = useAtom(editIdAtom)
+ const commentContent = typeof Conversation.comment === 'string'
+ ? Conversation.comment
+ : String(Conversation.comment)
const handleDelete = () => {
switch (CommentType) {
@@ -43,16 +51,107 @@ export function CommentDropdownMenu({ Conversation, id, CommentType }: CommentDr
}
}
+ const handleQuote = () => {
+ if (!editorRef?.current?.editor || !Conversation.comment) return
+
+ try {
+ const jsonContent = generateJSON(commentContent, editorRef.current.editor.extensionManager.extensions)
+
+ editorRef.current.editor.chain().insertContent([
+ {
+ type: 'blockquote',
+ content: jsonContent.content,
+ },
+ {
+ type: 'paragraph',
+ content: []
+ }
+ ]).run()
+
+ setTimeout(() => {
+ const editorDom = editorRef.current?.editor?.view?.dom
+
+ if (editorDom) {
+ editorDom.scrollIntoView({ behavior: 'smooth', block: 'center' })
+
+ setTimeout(() => {
+ editorRef.current?.editor?.commands.focus('end', { scrollIntoView: false })
+ }, 150)
+ }
+ }, 300)
+
+ } catch (error) {
+ toast.error('Quote failed, Please try again later.')
+ }
+ }
+
+
+ const handleCopy = () => {
+ let value = commentContent;
+
+ if (value.includes('<') && value.includes('>')) {
+ const tempDiv = document.createElement('div');
+
+ tempDiv.innerHTML = value;
+ value = tempDiv.textContent || tempDiv.innerText || '';
+ }
+
+ value = value.trim();
+
+ if (!value) return;
+
+ if (navigator.clipboard && window.isSecureContext) {
+ navigator.clipboard.writeText(value)
+ .catch(() => {
+ toast.error('Copy failed, Please try again later.')
+ })
+ } else {
+ const textArea = document.createElement('textarea');
+
+ textArea.value = value;
+ textArea.style.position = 'fixed';
+ textArea.style.left = '-9999px';
+ textArea.style.top = '-9999px';
+ textArea.style.opacity = '0';
+ textArea.style.pointerEvents = 'none';
+ textArea.style.zIndex = '-1000';
+ document.body.appendChild(textArea);
+
+ setTimeout(() => {
+ try {
+ textArea.focus();
+
+ setTimeout(() => {
+ textArea.select();
+ textArea.setSelectionRange(0, textArea.value.length);
+
+ const successful = document.execCommand('copy');
+
+ if (!successful) {
+ toast.error('Copy failed, Please try again later.')
+ }
+
+ document.body.removeChild(textArea);
+ }, 10);
+ } catch (err) {
+ document.body.removeChild(textArea);
+ }
+ }, 10);
+ }
+ };
+
const items = buildMenuItems([
{
type: 'item',
label: 'Copy',
- leftSlot:
+ leftSlot: ,
+ onSelect: () => handleCopy(),
},
{
type: 'item',
label: 'Quote',
- leftSlot:
+ leftSlot: ,
+ onSelect: () => handleQuote(),
},
{
type: 'item',
diff --git a/moon/apps/web/components/MrView/MRComment.tsx b/moon/apps/web/components/MrView/MRComment.tsx
index 17ecbee0f..234f73eb1 100644
--- a/moon/apps/web/components/MrView/MRComment.tsx
+++ b/moon/apps/web/components/MrView/MRComment.tsx
@@ -32,9 +32,10 @@ interface CommentProps {
conv: ConversationItem
id: string
whoamI: string
+ editorRef: React.RefObject
}
-const Comment = ({ conv, id, whoamI }: CommentProps) => {
+const Comment = ({ conv, id, whoamI, editorRef }: CommentProps) => {
const { data: member } = useGetOrganizationMember({ username: conv.username })
const extensions = useMemo(() => getNoteExtensions({ linkUnfurl: {} }), [])
@@ -93,7 +94,7 @@ const Comment = ({ conv, id, whoamI }: CommentProps) => {
}
onReactionSelect={handleReactionSelect}
/>
-
+
diff --git a/moon/apps/web/components/MrView/TimelineItems.tsx b/moon/apps/web/components/MrView/TimelineItems.tsx
index 322a69ce2..5582e96e4 100644
--- a/moon/apps/web/components/MrView/TimelineItems.tsx
+++ b/moon/apps/web/components/MrView/TimelineItems.tsx
@@ -22,6 +22,7 @@ import MergedItem from './MergedItem'
import ReopenItem from './ReopenItem'
import LabelItem from '@/components/MrView/LabelItem'
import ForcePushItem from './item/ForcePushItem'
+import { SimpleNoteContentRef } from '../SimpleNoteEditor/SimpleNoteContent'
interface TimelineItemProps {
badge?: React.ReactNode
@@ -68,7 +69,7 @@ const TimelineWrapper: React.FC = ({ convItems = [] }) =>
)
}
-const TimelineItems: React.FC<{ detail: any; id: string; type: string }> = ({ detail, id, type }) => {
+const TimelineItems: React.FC<{ detail: any; id: string; type: string; editorRef: React.RefObject }> = ({ detail, id, type, editorRef }) => {
const convItems: ConvItem[] = detail.conversations.map((conv: ConversationItem) => {
let icon
let children
@@ -77,7 +78,7 @@ const TimelineItems: React.FC<{ detail: any; id: string; type: string }> = ({ de
switch (conv.conv_type) {
case 'Comment':
icon =
- children =
+ children =
break
case 'Merged':
icon =
diff --git a/moon/apps/web/components/SimpleNoteEditor/SimpleNoteContent.tsx b/moon/apps/web/components/SimpleNoteEditor/SimpleNoteContent.tsx
index 89d56af1f..baafb81d0 100644
--- a/moon/apps/web/components/SimpleNoteEditor/SimpleNoteContent.tsx
+++ b/moon/apps/web/components/SimpleNoteEditor/SimpleNoteContent.tsx
@@ -160,7 +160,7 @@ export const SimpleNoteContent = memo(
}, [editor, onChange])
return (
-
+
{
diff --git a/moon/apps/web/pages/[org]/mr/[link]/index.tsx b/moon/apps/web/pages/[org]/mr/[link]/index.tsx
index c50c930f0..c2ad4f309 100644
--- a/moon/apps/web/pages/[org]/mr/[link]/index.tsx
+++ b/moon/apps/web/pages/[org]/mr/[link]/index.tsx
@@ -290,7 +290,7 @@ const MRDetailPage: PageWithLayout = () => {
) : (
- mrDetail &&
+ mrDetail &&
)}