Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 8 additions & 10 deletions moon/apps/web/components/MarkdownEditor/MentionList.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { ComponentPropsWithoutRef } from 'react'
import { uniqBy } from 'remeda'

import { Mention } from '@gitmono/editor/extensions'
import { Mention, LinkIssue } from '@gitmono/editor/extensions'
import { OrganizationMember, SyncOrganizationMember } from '@gitmono/types/generated'
import { GitCommitIcon, UIText } from '@gitmono/ui'

Expand Down Expand Up @@ -43,7 +43,7 @@ export function MentionList({ editor, defaultMentions, modal }: Props) {
char='$'
allow={({ state, range }) => {
const $from = state.doc.resolve(range.from)
const type = state.schema.nodes[Mention.name]
const type = state.schema.nodes[LinkIssue.name]
const allow = !!$from.parent.type.contentMatch.matchType(type)

return allow
Expand Down Expand Up @@ -122,15 +122,15 @@ function InnerIssueList({ editor }: Pick<Props, 'editor'>) {
{
role: 'app',
issue: {
hash: '#1234',
name: 'This is an issue example.'
hash: 'OCN2RX13',
name: 'MR/Issue open close icon 需要替换'
}
},
{
role: 'app',
issue: {
hash: '#5678',
name: 'Another issue example.'
hash: 'DVUAVF2J',
name: 'cccc'
}
}
]
Expand All @@ -144,12 +144,10 @@ function InnerIssueList({ editor }: Pick<Props, 'editor'>) {
// downrank guest members
scoreModifier={member.role === 'guest' ? 0.3 : 1}
onSelect={({ editor, range }) =>
editor.commands.insertMention({
editor.commands.insertIssue({
range,
id: member.issue.hash,
label: member.issue.hash,
username: '',
role: member.role === 'app' ? 'app' : 'member'
label: member.issue.hash
})
}
>
Expand Down
4 changes: 2 additions & 2 deletions moon/apps/web/components/MrView/MRComment.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { getMarkdownExtensions } from '@gitmono/editor'
import { getNoteExtensions } from '@gitmono/editor'
import { RichTextRenderer } from '@/components/RichTextRenderer'
import { useMemo } from 'react'
import { Button, ConditionalWrap, FaceSmilePlusIcon, UIText } from '@gitmono/ui'
Expand All @@ -22,7 +22,7 @@ interface CommentProps {
const Comment = ({ conv, id, whoamI }: CommentProps) => {
const { data: member } = useGetOrganizationMember({ username: conv.username })

const extensions = useMemo(() => getMarkdownExtensions({ linkUnfurl: {} }), [])
const extensions = useMemo(() => getNoteExtensions({ linkUnfurl: {} }), [])
const handleReactionSelect = useHandleExpression({ conv, id, type: whoamI })

return (
Expand Down
21 changes: 21 additions & 0 deletions moon/apps/web/components/RichTextRenderer/handlers/LinkIssue.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { Link } from '@gitmono/ui/Link'
import { NodeHandler } from '.'
import { useScope } from '@/contexts/scope'

export const LinkIssue: NodeHandler = ({ node, children }) => {
const { scope } = useScope()

if (!node.attrs || !node.attrs.id) {
return <span>{children}</span>
}

const issueId = node.attrs?.id
const label = node.attrs?.label || issueId
const issueUrl = `/${scope}/issue/${issueId}`

return (
<Link data-type='issue' href={issueUrl} className="issue-link">
<span className='text-blue-500 border-b border-blue-500'>${label}</span>
</Link>
)
}
3 changes: 3 additions & 0 deletions moon/apps/web/components/RichTextRenderer/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { Blockquote } from '@/components/RichTextRenderer/handlers/Blockquote'
import { CodeBlock } from '@/components/RichTextRenderer/handlers/CodeBlock'
import { Hardbreak } from '@/components/RichTextRenderer/handlers/Hardbreak'
import { InlineResourceMention } from '@/components/RichTextRenderer/handlers/InlineResourceMention'
import { LinkIssue } from '@/components/RichTextRenderer/handlers/LinkIssue'
import { PostNoteAttachment } from '@/components/RichTextRenderer/handlers/PostNoteAttachment'
import { RelativeTime } from '@/components/RichTextRenderer/handlers/RelativeTime'

Expand Down Expand Up @@ -103,6 +104,8 @@ function RenderBlock({
return <LinkUnfurl {...props} />
case 'mention':
return <Mention {...props} />
case 'linkIssue':
return <LinkIssue {...props} />
case 'postNoteAttachment':
return <PostNoteAttachment {...props} {...options?.postNoteAttachment} />
case 'reaction':
Expand Down
12 changes: 12 additions & 0 deletions moon/apps/web/components/SimpleNoteEditor/SimpleNoteContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ export interface SimpleNoteContentRef {
clearAndBlur(): void
insertReaction: TTEditor['commands']['insertReaction']
uploadAndAppendAttachments: (files: File[]) => Promise<void>
getLinkedIssues(): string[]
}

export const SimpleNoteContent = memo(
Expand Down Expand Up @@ -117,6 +118,17 @@ export const SimpleNoteContent = memo(
}
}
},
getLinkedIssues: () => {
const issues: string[] = []

editor.state.doc.descendants((node) => {
if (node.type.name === 'linkIssue' && node.attrs.id) {
issues.push(node.attrs.id)
}
})

return Array.from(new Set(issues))
},
uploadAndAppendAttachments,
...imperativeHandlers,
editor
Expand Down
28 changes: 16 additions & 12 deletions moon/apps/web/pages/[org]/mr/[id].tsx
Original file line number Diff line number Diff line change
Expand Up @@ -82,19 +82,23 @@ const MRDetailPage:PageWithLayout<any> = () =>{

const send_comment = () => {
const currentContentHTML = editorRef.current?.editor?.getHTML() ?? '<p></p>';

if (trimHtml(currentContentHTML) === '') {
toast.error('Please enter the content.')
}else {
postMrComment(
{ content: currentContentHTML },
{
onSuccess: () =>{
editorRef.current?.clearAndBlur()
const issues = editorRef.current?.getLinkedIssues() || []

/* eslint-disable-next-line no-console */
console.log('commentIssues:',issues);
Comment thread
Ceron-CSS marked this conversation as resolved.
Comment thread
Ceron-CSS marked this conversation as resolved.

if (trimHtml(currentContentHTML) === '') {
toast.error('Please enter the content.')
} else {
postMrComment(
{ content: currentContentHTML },
{
onSuccess: () =>{
editorRef.current?.clearAndBlur()
}
}
}
);
}
);
}
}

const buttonClasses= 'cursor-pointer';
Expand Down
4 changes: 4 additions & 0 deletions moon/apps/web/styles/prose.css
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,10 @@
@apply text-primary cursor-pointer font-semibold;
}

.link-issue {
@apply text-blue-500 border-b border-blue-500 cursor-pointer;
}

:where(ul, ol):not(:where([class~='not-prose']) *) {
/* Erase margins in nested lists */
ul,
Expand Down
126 changes: 126 additions & 0 deletions moon/packages/editor/src/extensions/LinkIssue.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
import { mergeAttributes, Node, Range } from '@tiptap/core'

import { handleRestoreMentionBackspace } from '../utils/handleRestoreMentionBackspace'
import { insertContent } from '../utils/insertContent'

declare module '@tiptap/core' {
interface Commands<ReturnType> {
issue: {
insertIssue: (props: {
id: string
label: string
range: Range
}) => ReturnType
}
}
}

export interface IssueNodeAttrs {
/**
* The identifier for the selected issue, stored as a `data-id` attribute.
*/
id: string | null
/**
* The label to be rendered by the editor as the displayed text for this issue.
*/
label?: string | null
}

export type IssueOptions = {
HTMLAttributes: Record<string, any>
}

export const LinkIssue = Node.create<IssueOptions>({
name: 'linkIssue',

addOptions() {
return {
HTMLAttributes: {
class: 'link-issue'
}
}
},

group: 'inline',

inline: true,

selectable: false,

atom: true,

addAttributes() {
return {
id: {
default: null,
parseHTML: (element) => element.getAttribute('data-id'),
renderHTML: (attributes) => {
if (!attributes.id) {
return {}
}

return {
'data-id': attributes.id
}
}
},

label: {
default: null,
parseHTML: (element) => element.getAttribute('data-label'),
renderHTML: (attributes) => {
if (!attributes.label) {
return {}
}

return {
'data-label': attributes.label
}
}
}
}
},

parseHTML() {
return [
{
tag: `span[data-type="${this.name}"]`
}
]
},

renderHTML({ node, HTMLAttributes }) {
return [
'span',
mergeAttributes(this.options.HTMLAttributes, { 'data-type': this.name }, HTMLAttributes),
`$${node.attrs.label ?? node.attrs.id}`
]
},

renderText({ node }) {
return `$${node.attrs.label ?? node.attrs.id}`
},

addCommands() {
return {
insertIssue:
({ range, ...attrs }) =>
({ chain, state }) =>
insertContent({
chain,
range,
state,
content: { type: this.name, attrs }
})
}
},

addKeyboardShortcuts() {
return {
Backspace: () =>
this.editor.commands.command(({ tr, state }) =>
handleRestoreMentionBackspace({ transaction: tr, state, nodeName: this.name, char: '$' })
)
}
}
})
1 change: 1 addition & 0 deletions moon/packages/editor/src/extensions/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export * from './Italic'
export * from './Kbd'
export * from './Link'
export * from './LinkUnfurl'
export * from './LinkIssue'
export * from './ListItem'
export * from './ListKeyMap'
export * from './MediaGallery'
Expand Down
1 change: 1 addition & 0 deletions moon/packages/editor/src/note.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ export function getNoteExtensions(options?: GetNoteExtensionsOptions) {
E.TaskItem.configure(options?.taskItem),
E.TaskList,
E.Mention.configure(options?.mention),
E.LinkIssue,
E.Reaction,

E.Details.configure(options?.details),
Expand Down