mirror of
https://github.com/langgenius/dify.git
synced 2026-04-26 02:06:35 +08:00
refactor reply code
This commit is contained in:
parent
29178d8adf
commit
ea17f41b5b
@ -80,8 +80,6 @@ export const CommentInput: FC<CommentInputProps> = memo(({ position, onSubmit, o
|
|||||||
onSubmit={handleMentionSubmit}
|
onSubmit={handleMentionSubmit}
|
||||||
placeholder="Add a comment"
|
placeholder="Add a comment"
|
||||||
autoFocus
|
autoFocus
|
||||||
minRows={1}
|
|
||||||
maxRows={4}
|
|
||||||
className="relative"
|
className="relative"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -15,16 +15,12 @@ type MentionInputProps = {
|
|||||||
value: string
|
value: string
|
||||||
onChange: (value: string) => void
|
onChange: (value: string) => void
|
||||||
onSubmit: (content: string, mentionedUserIds: string[]) => void
|
onSubmit: (content: string, mentionedUserIds: string[]) => void
|
||||||
onKeyDown?: (e: React.KeyboardEvent) => void
|
onCancel?: () => void
|
||||||
placeholder?: string
|
placeholder?: string
|
||||||
disabled?: boolean
|
disabled?: boolean
|
||||||
loading?: boolean
|
loading?: boolean
|
||||||
className?: string
|
className?: string
|
||||||
minRows?: number
|
isEditing?: boolean
|
||||||
maxRows?: number
|
|
||||||
showSubmitButton?: boolean
|
|
||||||
showMentionButton?: boolean
|
|
||||||
submitButtonIcon?: React.ReactNode
|
|
||||||
autoFocus?: boolean
|
autoFocus?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -32,13 +28,12 @@ export const MentionInput: FC<MentionInputProps> = memo(({
|
|||||||
value,
|
value,
|
||||||
onChange,
|
onChange,
|
||||||
onSubmit,
|
onSubmit,
|
||||||
onKeyDown,
|
onCancel,
|
||||||
placeholder = 'Add a comment',
|
placeholder = 'Add a comment',
|
||||||
disabled = false,
|
disabled = false,
|
||||||
loading = false,
|
loading = false,
|
||||||
className,
|
className,
|
||||||
showSubmitButton = true,
|
isEditing = false,
|
||||||
showMentionButton = true,
|
|
||||||
autoFocus = false,
|
autoFocus = false,
|
||||||
}) => {
|
}) => {
|
||||||
const params = useParams()
|
const params = useParams()
|
||||||
@ -140,14 +135,16 @@ export const MentionInput: FC<MentionInputProps> = memo(({
|
|||||||
|
|
||||||
onChange(newContent)
|
onChange(newContent)
|
||||||
setShowMentionDropdown(false)
|
setShowMentionDropdown(false)
|
||||||
setMentionedUserIds(prev => [...prev, user.id])
|
|
||||||
|
const newMentionedUserIds = [...mentionedUserIds, user.id]
|
||||||
|
setMentionedUserIds(newMentionedUserIds)
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
const newCursorPos = mentionPosition + user.name.length + 2 // @ + name + space
|
const newCursorPos = mentionPosition + user.name.length + 2 // @ + name + space
|
||||||
textarea.setSelectionRange(newCursorPos, newCursorPos)
|
textarea.setSelectionRange(newCursorPos, newCursorPos)
|
||||||
textarea.focus()
|
textarea.focus()
|
||||||
}, 0)
|
}, 0)
|
||||||
}, [value, mentionPosition, onChange])
|
}, [value, mentionPosition, onChange, mentionedUserIds])
|
||||||
|
|
||||||
const handleSubmit = useCallback((e?: React.MouseEvent) => {
|
const handleSubmit = useCallback((e?: React.MouseEvent) => {
|
||||||
if (e) {
|
if (e) {
|
||||||
@ -194,9 +191,7 @@ export const MentionInput: FC<MentionInputProps> = memo(({
|
|||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
handleSubmit()
|
handleSubmit()
|
||||||
}
|
}
|
||||||
|
}, [showMentionDropdown, filteredMentionUsers, selectedMentionIndex, insertMention, handleSubmit])
|
||||||
onKeyDown?.(e)
|
|
||||||
}, [showMentionDropdown, filteredMentionUsers, selectedMentionIndex, insertMention, handleSubmit, onKeyDown])
|
|
||||||
|
|
||||||
const resetMentionState = useCallback(() => {
|
const resetMentionState = useCallback(() => {
|
||||||
setMentionedUserIds([])
|
setMentionedUserIds([])
|
||||||
@ -221,7 +216,7 @@ export const MentionInput: FC<MentionInputProps> = memo(({
|
|||||||
)}
|
)}
|
||||||
placeholder={placeholder}
|
placeholder={placeholder}
|
||||||
autoFocus={autoFocus}
|
autoFocus={autoFocus}
|
||||||
minRows={1}
|
minRows={isEditing ? 4 : 1}
|
||||||
maxRows={4}
|
maxRows={4}
|
||||||
value={value}
|
value={value}
|
||||||
disabled={disabled || loading}
|
disabled={disabled || loading}
|
||||||
@ -229,17 +224,14 @@ export const MentionInput: FC<MentionInputProps> = memo(({
|
|||||||
onKeyDown={handleKeyDown}
|
onKeyDown={handleKeyDown}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{(showMentionButton || showSubmitButton) && (
|
{!isEditing && (
|
||||||
<div className="absolute bottom-0 right-1 z-20 flex items-end gap-1">
|
<div className="absolute bottom-0 right-1 z-20 flex items-end gap-1">
|
||||||
{showMentionButton && (
|
|
||||||
<div
|
<div
|
||||||
className="z-20 flex h-8 w-8 cursor-pointer items-center justify-center rounded-lg bg-components-button-secondary-bg hover:bg-state-base-hover"
|
className="z-20 flex h-8 w-8 cursor-pointer items-center justify-center rounded-lg bg-components-button-secondary-bg hover:bg-state-base-hover"
|
||||||
onClick={handleMentionButtonClick}
|
onClick={handleMentionButtonClick}
|
||||||
>
|
>
|
||||||
<RiAtLine className="h-4 w-4" />
|
<RiAtLine className="h-4 w-4" />
|
||||||
</div>
|
</div>
|
||||||
)}
|
|
||||||
{showSubmitButton && (
|
|
||||||
<Button
|
<Button
|
||||||
className='z-20 ml-2 w-8 px-0'
|
className='z-20 ml-2 w-8 px-0'
|
||||||
variant='primary'
|
variant='primary'
|
||||||
@ -248,7 +240,30 @@ export const MentionInput: FC<MentionInputProps> = memo(({
|
|||||||
>
|
>
|
||||||
<RiArrowUpLine className='h-4 w-4' />
|
<RiArrowUpLine className='h-4 w-4' />
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{isEditing && (
|
||||||
|
<div className="absolute bottom-0 left-1 right-1 z-20 flex items-end justify-between">
|
||||||
|
<div
|
||||||
|
className="z-20 flex h-8 w-8 cursor-pointer items-center justify-center rounded-lg bg-components-button-secondary-bg hover:bg-state-base-hover"
|
||||||
|
onClick={handleMentionButtonClick}
|
||||||
|
>
|
||||||
|
<RiAtLine className="h-4 w-4" />
|
||||||
|
</div>
|
||||||
|
<div className='flex items-center gap-2'>
|
||||||
|
<Button variant='secondary' size='small' onClick={onCancel} disabled={loading}>
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant='primary'
|
||||||
|
size='small'
|
||||||
|
disabled={loading || !value.trim()}
|
||||||
|
onClick={() => handleSubmit()}
|
||||||
|
>
|
||||||
|
Save
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,13 +1,10 @@
|
|||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
import { useParams } from 'next/navigation'
|
|
||||||
import type { FC } from 'react'
|
import type { FC } from 'react'
|
||||||
import { memo, useCallback, useEffect, useMemo, useState } from 'react'
|
import { memo, useCallback, useEffect, useMemo, useState } from 'react'
|
||||||
import { useReactFlow, useViewport } from 'reactflow'
|
import { useReactFlow, useViewport } from 'reactflow'
|
||||||
import { RiArrowDownSLine, RiArrowUpSLine, RiCheckboxCircleFill, RiCheckboxCircleLine, RiCloseLine, RiDeleteBinLine, RiMoreFill } from '@remixicon/react'
|
import { RiArrowDownSLine, RiArrowUpSLine, RiCheckboxCircleFill, RiCheckboxCircleLine, RiCloseLine, RiDeleteBinLine, RiMoreFill } from '@remixicon/react'
|
||||||
import Textarea from 'react-textarea-autosize'
|
|
||||||
import Avatar from '@/app/components/base/avatar'
|
import Avatar from '@/app/components/base/avatar'
|
||||||
import Button from '@/app/components/base/button'
|
|
||||||
import Divider from '@/app/components/base/divider'
|
import Divider from '@/app/components/base/divider'
|
||||||
import cn from '@/utils/classnames'
|
import cn from '@/utils/classnames'
|
||||||
import { useFormatTimeFromNow } from '@/app/components/workflow/hooks'
|
import { useFormatTimeFromNow } from '@/app/components/workflow/hooks'
|
||||||
@ -75,7 +72,6 @@ export const CommentThread: FC<CommentThreadProps> = memo(({
|
|||||||
onReplyEdit,
|
onReplyEdit,
|
||||||
onReplyDelete,
|
onReplyDelete,
|
||||||
}) => {
|
}) => {
|
||||||
const params = useParams()
|
|
||||||
const { flowToScreenPosition } = useReactFlow()
|
const { flowToScreenPosition } = useReactFlow()
|
||||||
const viewport = useViewport()
|
const viewport = useViewport()
|
||||||
const { userProfile } = useAppContext()
|
const { userProfile } = useAppContext()
|
||||||
@ -115,11 +111,11 @@ export const CommentThread: FC<CommentThreadProps> = memo(({
|
|||||||
setEditingReply({ id: '', content: '' })
|
setEditingReply({ id: '', content: '' })
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
const handleSaveEdit = useCallback(async () => {
|
const handleEditSubmit = useCallback(async (content: string, mentionedUserIds: string[]) => {
|
||||||
if (!onReplyEdit || !editingReply) return
|
if (!onReplyEdit || !editingReply) return
|
||||||
const trimmed = editingReply.content.trim()
|
const trimmed = content.trim()
|
||||||
if (!trimmed) return
|
if (!trimmed) return
|
||||||
await onReplyEdit(editingReply.id, trimmed, [])
|
await onReplyEdit(editingReply.id, trimmed, mentionedUserIds)
|
||||||
setEditingReply({ id: '', content: '' })
|
setEditingReply({ id: '', content: '' })
|
||||||
}, [editingReply, onReplyEdit])
|
}, [editingReply, onReplyEdit])
|
||||||
|
|
||||||
@ -235,17 +231,18 @@ export const CommentThread: FC<CommentThreadProps> = memo(({
|
|||||||
</div>
|
</div>
|
||||||
{isReplyEditing ? (
|
{isReplyEditing ? (
|
||||||
<div className='rounded-lg border border-components-chat-input-border bg-components-panel-bg-blur px-3 py-2 shadow-sm'>
|
<div className='rounded-lg border border-components-chat-input-border bg-components-panel-bg-blur px-3 py-2 shadow-sm'>
|
||||||
<Textarea
|
<MentionInput
|
||||||
minRows={1}
|
|
||||||
maxRows={4}
|
|
||||||
value={editingReply?.content ?? ''}
|
value={editingReply?.content ?? ''}
|
||||||
onChange={e => setEditingReply(prev => prev ? { ...prev, content: e.target.value } : prev)}
|
onChange={newContent => setEditingReply(prev => prev ? { ...prev, content: newContent } : prev)}
|
||||||
className='system-sm-regular w-full resize-none bg-transparent text-text-primary caret-primary-500 outline-none'
|
onSubmit={handleEditSubmit}
|
||||||
|
onCancel={handleCancelEdit}
|
||||||
|
placeholder="Edit reply"
|
||||||
|
disabled={loading}
|
||||||
|
loading={loading}
|
||||||
|
isEditing={true}
|
||||||
|
className="system-sm-regular"
|
||||||
|
autoFocus
|
||||||
/>
|
/>
|
||||||
<div className='mt-2 flex items-center justify-end gap-2'>
|
|
||||||
<Button variant='secondary' size='small' onClick={handleCancelEdit} disabled={loading}>Cancel</Button>
|
|
||||||
<Button variant='primary' size='small' disabled={loading || !(editingReply?.content?.trim())} onClick={handleSaveEdit}>Save</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<ThreadMessage
|
<ThreadMessage
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user