mirror of
https://github.com/langgenius/dify.git
synced 2026-05-02 15:17:39 +08:00
feat: improve mention input loading state and prevent button flash on submit
This commit is contained in:
parent
a4add403fb
commit
00dab7ca5f
@ -13,7 +13,7 @@ import {
|
|||||||
import { createPortal } from 'react-dom'
|
import { createPortal } from 'react-dom'
|
||||||
import { useParams } from 'next/navigation'
|
import { useParams } from 'next/navigation'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { RiArrowUpLine, RiAtLine } from '@remixicon/react'
|
import { RiArrowUpLine, RiAtLine, RiLoader2Line } from '@remixicon/react'
|
||||||
import Textarea from 'react-textarea-autosize'
|
import Textarea from 'react-textarea-autosize'
|
||||||
import Button from '@/app/components/base/button'
|
import Button from '@/app/components/base/button'
|
||||||
import Avatar from '@/app/components/base/avatar'
|
import Avatar from '@/app/components/base/avatar'
|
||||||
@ -347,11 +347,9 @@ export const MentionInput: FC<MentionInputProps> = memo(({
|
|||||||
const cursorPosition = textarea.selectionStart || 0
|
const cursorPosition = textarea.selectionStart || 0
|
||||||
const textBeforeCursor = value.slice(0, cursorPosition)
|
const textBeforeCursor = value.slice(0, cursorPosition)
|
||||||
|
|
||||||
// 🔒 如果已经在 mention 模式,不插入新的 @
|
|
||||||
if (showMentionDropdown)
|
if (showMentionDropdown)
|
||||||
return
|
return
|
||||||
|
|
||||||
// 🔒 如果光标前已有未完成的 @,不插入新的 @
|
|
||||||
if (/@\w*$/.test(textBeforeCursor))
|
if (/@\w*$/.test(textBeforeCursor))
|
||||||
return
|
return
|
||||||
|
|
||||||
@ -409,16 +407,21 @@ export const MentionInput: FC<MentionInputProps> = memo(({
|
|||||||
}, 0)
|
}, 0)
|
||||||
}, [value, mentionPosition, onChange, mentionedUserIds, evaluateContentLayout, syncHighlightScroll])
|
}, [value, mentionPosition, onChange, mentionedUserIds, evaluateContentLayout, syncHighlightScroll])
|
||||||
|
|
||||||
const handleSubmit = useCallback((e?: React.MouseEvent) => {
|
const handleSubmit = useCallback(async (e?: React.MouseEvent) => {
|
||||||
if (e) {
|
if (e) {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (value.trim()) {
|
if (value.trim()) {
|
||||||
onSubmit(value.trim(), mentionedUserIds)
|
try {
|
||||||
setMentionedUserIds([])
|
await onSubmit(value.trim(), mentionedUserIds)
|
||||||
setShowMentionDropdown(false)
|
setMentionedUserIds([])
|
||||||
|
setShowMentionDropdown(false)
|
||||||
|
}
|
||||||
|
catch (error) {
|
||||||
|
console.error('Failed to submit', error)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}, [value, mentionedUserIds, onSubmit])
|
}, [value, mentionedUserIds, onSubmit])
|
||||||
|
|
||||||
@ -540,7 +543,9 @@ export const MentionInput: FC<MentionInputProps> = memo(({
|
|||||||
disabled={!value.trim() || disabled || loading}
|
disabled={!value.trim() || disabled || loading}
|
||||||
onClick={handleSubmit}
|
onClick={handleSubmit}
|
||||||
>
|
>
|
||||||
<RiArrowUpLine className='h-4 w-4 text-components-button-primary-text' />
|
{loading
|
||||||
|
? <RiLoader2Line className='h-4 w-4 animate-spin text-components-button-primary-text' />
|
||||||
|
: <RiArrowUpLine className='h-4 w-4 text-components-button-primary-text' />}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@ -574,6 +579,7 @@ export const MentionInput: FC<MentionInputProps> = memo(({
|
|||||||
disabled={loading || !value.trim()}
|
disabled={loading || !value.trim()}
|
||||||
onClick={() => handleSubmit()}
|
onClick={() => handleSubmit()}
|
||||||
>
|
>
|
||||||
|
{loading && <RiLoader2Line className='mr-1 h-3.5 w-3.5 animate-spin' />}
|
||||||
{t('common.operation.save')}
|
{t('common.operation.save')}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -161,12 +161,14 @@ export const CommentThread: FC<CommentThreadProps> = memo(({
|
|||||||
const handleReplySubmit = useCallback(async (content: string, mentionedUserIds: string[]) => {
|
const handleReplySubmit = useCallback(async (content: string, mentionedUserIds: string[]) => {
|
||||||
if (!onReply || loading) return
|
if (!onReply || loading) return
|
||||||
|
|
||||||
|
setReplyContent('')
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await onReply(content, mentionedUserIds)
|
await onReply(content, mentionedUserIds)
|
||||||
setReplyContent('')
|
|
||||||
}
|
}
|
||||||
catch (error) {
|
catch (error) {
|
||||||
console.error('Failed to send reply', error)
|
console.error('Failed to send reply', error)
|
||||||
|
setReplyContent(content)
|
||||||
}
|
}
|
||||||
}, [onReply, loading])
|
}, [onReply, loading])
|
||||||
|
|
||||||
@ -418,11 +420,6 @@ export const CommentThread: FC<CommentThreadProps> = memo(({
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
{loading && (
|
|
||||||
<div className='bg-components-panel-bg/70 absolute inset-0 z-30 flex items-center justify-center text-sm text-text-tertiary'>
|
|
||||||
{t('workflow.comments.loading')}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{onReply && (
|
{onReply && (
|
||||||
<div className='border-t border-components-panel-border px-4 py-3'>
|
<div className='border-t border-components-panel-border px-4 py-3'>
|
||||||
<div className='flex items-center gap-3'>
|
<div className='flex items-center gap-3'>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user