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
90 changes: 57 additions & 33 deletions moon/apps/web/components/MarkdownEditor/MentionList.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,27 @@
import { ComponentPropsWithoutRef } from 'react'
import { uniqBy } from 'remeda'

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

import { AppBadge } from '@/components/AppBadge'
import { GuestBadge } from '@/components/GuestBadge'
import { MemberAvatar } from '@/components/MemberAvatar'
import { SuggestionItem, SuggestionRoot, useSuggestionEmpty } from '@/components/SuggestionList'
import { SuggestionItem, SuggestionRoot, useSuggestionEmpty, useSuggestionQuery } from '@/components/SuggestionList'
import { useGetIssueIssueSuggester } from '@/hooks/issues/useGetIssueIssueSuggester'
import { useGetOauthApplications } from '@/hooks/useGetOauthApplications'
import { useSyncedMembers } from '@/hooks/useSyncedMembers'
import { useDebounce } from 'use-debounce'

type Props = Pick<ComponentPropsWithoutRef<typeof SuggestionRoot>, 'editor'> & {
defaultMentions?: (OrganizationMember | SyncOrganizationMember)[]
modal?: boolean
}

export function MentionList({ editor, defaultMentions, modal }: Props) {
useGetIssueIssueSuggester({ query: '' });
Comment thread
Ceron-CSS marked this conversation as resolved.

return (
<>
<SuggestionRoot
Expand Down Expand Up @@ -49,8 +53,9 @@ export function MentionList({ editor, defaultMentions, modal }: Props) {
return allow
}}
minScore={0.2}
contentClassName='p-0 max-h-[min(480px,var(--radix-popover-content-available-height))] w-[min(350px,var(--radix-popover-content-available-width))]'
contentClassName='p-0 max-h-[min(265px,var(--radix-popover-content-available-height))] w-[min(450px,var(--radix-popover-content-available-width))]'
listClassName='p-1'
disableEmptyState={true}
>
<InnerIssueList editor={editor} />
</SuggestionRoot>
Expand Down Expand Up @@ -118,44 +123,63 @@ function InnerMentionList({ editor, defaultMentions }: Pick<Props, 'editor' | 'd
}

function InnerIssueList({ editor }: Pick<Props, 'editor'>) {
const items = [
{
role: 'app',
issue: {
hash: 'OCN2RX13',
name: 'MR/Issue open close icon 需要替换'
}
},
{
role: 'app',
issue: {
hash: 'DVUAVF2J',
name: 'cccc'
}
}
]
const query = useSuggestionQuery();
const [debouncedQuery] = useDebounce(query, 400);

return items.map((member) => (
const { data: issueSuggestions } = useGetIssueIssueSuggester(
{ query: debouncedQuery }
);

if (!issueSuggestions?.data || issueSuggestions.data.length === 0) {
Comment thread
Ceron-CSS marked this conversation as resolved.
return (
<div className='flex items-center gap-2 p-2 text-sm'>
<UIText>No results</UIText>
</div>
);
}

return issueSuggestions.data.map((item) => {

let suggestionType = ''

switch (item.type) {
case 'issue_closed':
case 'issue_open':
suggestionType = 'issue'
break
case 'merge_request_closed':
case 'merge_request':
suggestionType = 'merge_request'
break
default:
break
}

return (
<SuggestionItem
key={member.issue.hash}
key={item.link}
editor={editor}
value={member.issue.hash}
keywords={[member.issue.hash]}
// downrank guest members
scoreModifier={member.role === 'guest' ? 0.3 : 1}
value={item.link}
forceMount={true}
onSelect={({ editor, range }) =>
editor.commands.insertIssue({
range,
id: member.issue.hash,
label: member.issue.hash
id: item.link,
label: item.link,
suggestionType: suggestionType
})
}
>
<GitCommitIcon />
<span className="h-5 w-5">
{suggestionType === 'merge_request'
? <GitCommitIcon />
: <ChatBubblePlusIcon />}
</span>
<div className='flex flex-1 items-center justify-between'>
<UIText className='truncate'>{member.issue.name}</UIText>
<UIText className='justify-self-end truncate text-sm text-gray-500'>{member.issue.hash}</UIText>
<UIText className='max-w-[300px] truncate'>{item.title}</UIText>
<UIText className='justify-self-end truncate text-sm text-gray-500'>{item.link}</UIText>
</div>
</SuggestionItem>
))
}
)
})
}
8 changes: 8 additions & 0 deletions moon/apps/web/components/Post/Notes/SlashCommand.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
Heading3Icon,
HorizontalRuleIcon,
KeyboardShortcut,
LinkIcon,
OrderedListIcon,
PlayIcon,
PostIcon,
Expand Down Expand Up @@ -170,6 +171,13 @@ const COMMANDS: CommandItemProps[] = [
command: ({ editor, range }: CommandProps) => editor.chain().focus().deleteRange(range).insertContent('@').run(),
kbd: '@'
},
{
title: 'Link Issues/PRs',
searchTerms: ['issue', 'link', 'reference'],
icon: <LinkIcon />,
command: ({ editor, range }: CommandProps) => editor.chain().focus().deleteRange(range).insertContent('$').run(),
kbd: '$'
},
{
title: 'Reference',
searchTerms: ['link', 'reference', 'post', 'call', 'doc'],
Expand Down
19 changes: 15 additions & 4 deletions moon/apps/web/components/RichTextRenderer/handlers/LinkIssue.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,23 @@ export const LinkIssue: NodeHandler = ({ node, children }) => {
return <span>{children}</span>
}

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

switch (node.attrs?.suggestionType) {
case 'issue':
url = `/${scope}/issue/${id}`
break
case 'merge_request':
url = `/${scope}/mr/${id}`
break
default:
break
}

return (
<Link data-type='issue' href={issueUrl} className="issue-link">
<Link data-type='issue' href={url} className="issue-link">
<span className='text-blue-500 border-b border-blue-500'>${label}</span>
</Link>
)
Expand Down
27 changes: 16 additions & 11 deletions moon/apps/web/components/SuggestionList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ type SuggestionProps = React.PropsWithChildren &
char: string
// allow controlled query changes. providing this value will disable cmdk filtering.
onControlledQueryChange?: (query: string) => void
// disable the default "No results" empty state
disableEmptyState?: boolean
contentClassName?: string
listClassName?: string
}
Expand Down Expand Up @@ -76,6 +78,7 @@ export function SuggestionRoot(props: SuggestionProps) {
modal = true,
filter,
onControlledQueryChange,
disableEmptyState,
minScore,
...etc
} = props
Expand Down Expand Up @@ -310,17 +313,19 @@ export function SuggestionRoot(props: SuggestionProps) {
<SuggestionRangeContext.Provider value={rangeRef}>{children}</SuggestionRangeContext.Provider>
</SuggestionEmptyContext.Provider>
</SuggestionQueryContext.Provider>
<Command.Empty
className='flex items-center gap-2 p-2 text-sm'
onClick={() => {
close()
// clicking will steal focus, so return it
editor.commands.focus()
}}
>
<UIText>No results</UIText>
<UIText tertiary>Dismiss</UIText>
</Command.Empty>
{!disableEmptyState && (
<Command.Empty
className='flex items-center gap-2 p-2 text-sm'
onClick={() => {
close()
// clicking will steal focus, so return it
editor.commands.focus()
}}
>
<UIText>No results</UIText>
<UIText tertiary>Dismiss</UIText>
</Command.Empty>
)}
</Command.List>
</Command>
</PopoverContent>
Expand Down
13 changes: 13 additions & 0 deletions moon/apps/web/hooks/issues/useGetIssueIssueSuggester.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { keepPreviousData, useQuery } from '@tanstack/react-query'

import { GetApiIssueIssueSuggesterParams } from '@gitmono/types'

import { legacyApiClient } from '@/utils/queryClient'

export function useGetIssueIssueSuggester(query: GetApiIssueIssueSuggesterParams) {
return useQuery({
queryKey: legacyApiClient.v1.getApiIssueIssueSuggester().requestKey(query),
queryFn: () => legacyApiClient.v1.getApiIssueIssueSuggester().request(query),
placeholderData: keepPreviousData
})
}
15 changes: 15 additions & 0 deletions moon/packages/editor/src/extensions/LinkIssue.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ declare module '@tiptap/core' {
insertIssue: (props: {
id: string
label: string
suggestionType: string
range: Range
}) => ReturnType
}
Expand Down Expand Up @@ -77,6 +78,20 @@ export const LinkIssue = Node.create<IssueOptions>({
'data-label': attributes.label
}
}
},

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

return {
'data-suggestionType': attributes.suggestionType
}
}
}
}
},
Expand Down