refactor: separate loading states for comment operations

Separate loading states to distinguish between different operations:
- activeCommentDetailLoading: loading comment details, delete/resolve operations
- replySubmitting: sending new replies
- replyUpdating: editing existing replies

Changes:
- Add replySubmitting and replyUpdating states to comment store
- Restore full-screen loading overlay for comment detail loading
- Use inline spinner (RiLoader2Line) in send/save buttons for reply operations
- Update loading state usage in handleCommentReply and handleCommentReplyUpdate
- Pass separated loading states from workflow index to CommentThread component

Benefits:
- UI clarity: different loading states have appropriate visual feedback
- Better UX: users can still navigate while sending replies
- Clear separation of concerns: each operation has its own loading state
This commit is contained in:
lyzno1 2025-10-11 14:34:35 +08:00
parent d986659add
commit 9d93fda471
No known key found for this signature in database
4 changed files with 37 additions and 10 deletions

View File

@ -18,6 +18,8 @@ import { getUserColor } from '@/app/components/workflow/collaboration/utils/user
type CommentThreadProps = {
comment: WorkflowCommentDetail
loading?: boolean
replySubmitting?: boolean
replyUpdating?: boolean
onClose: () => void
onDelete?: () => void
onResolve?: () => void
@ -135,6 +137,8 @@ const ThreadMessage: FC<{
export const CommentThread: FC<CommentThreadProps> = memo(({
comment,
loading = false,
replySubmitting = false,
replyUpdating = false,
onClose,
onDelete,
onResolve,
@ -159,7 +163,7 @@ export const CommentThread: FC<CommentThreadProps> = memo(({
}, [comment.id])
const handleReplySubmit = useCallback(async (content: string, mentionedUserIds: string[]) => {
if (!onReply || loading) return
if (!onReply || replySubmitting) return
setReplyContent('')
@ -170,7 +174,7 @@ export const CommentThread: FC<CommentThreadProps> = memo(({
console.error('Failed to send reply', error)
setReplyContent(content)
}
}, [onReply, loading])
}, [onReply, replySubmitting])
const screenPosition = useMemo(() => {
return flowToScreenPosition({
@ -398,7 +402,7 @@ export const CommentThread: FC<CommentThreadProps> = memo(({
onCancel={handleCancelEdit}
placeholder={t('workflow.comments.placeholder.editReply')}
disabled={loading}
loading={loading}
loading={replyUpdating}
isEditing={true}
className="system-sm-regular"
autoFocus
@ -420,6 +424,11 @@ export const CommentThread: FC<CommentThreadProps> = memo(({
</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 && (
<div className='border-t border-components-panel-border px-4 py-3'>
<div className='flex items-center gap-3'>
@ -436,7 +445,7 @@ export const CommentThread: FC<CommentThreadProps> = memo(({
onSubmit={handleReplySubmit}
placeholder={t('workflow.comments.placeholder.reply')}
disabled={loading}
loading={loading}
loading={replySubmitting}
/>
</div>
</div>

View File

@ -25,6 +25,10 @@ export const useWorkflowComment = () => {
const setActiveComment = useStore(s => s.setActiveCommentDetail)
const activeCommentLoading = useStore(s => s.activeCommentDetailLoading)
const setActiveCommentLoading = useStore(s => s.setActiveCommentDetailLoading)
const replySubmitting = useStore(s => s.replySubmitting)
const setReplySubmitting = useStore(s => s.setReplySubmitting)
const replyUpdating = useStore(s => s.replyUpdating)
const setReplyUpdating = useStore(s => s.setReplyUpdating)
const commentDetailCache = useStore(s => s.commentDetailCache)
const setCommentDetailCache = useStore(s => s.setCommentDetailCache)
const commentDetailCacheRef = useRef<Record<string, WorkflowCommentDetail>>(commentDetailCache)
@ -302,7 +306,7 @@ export const useWorkflowComment = () => {
const trimmed = content.trim()
if (!trimmed) return
setActiveCommentLoading(true)
setReplySubmitting(true)
try {
await createWorkflowCommentReply(appId, commentId, { content: trimmed, mentioned_user_ids: mentionedUserIds })
@ -315,16 +319,16 @@ export const useWorkflowComment = () => {
console.error('Failed to create reply:', error)
}
finally {
setActiveCommentLoading(false)
setReplySubmitting(false)
}
}, [appId, loadComments, refreshActiveComment, setActiveCommentLoading])
}, [appId, loadComments, refreshActiveComment, setReplySubmitting])
const handleCommentReplyUpdate = useCallback(async (commentId: string, replyId: string, content: string, mentionedUserIds: string[] = []) => {
if (!appId) return
const trimmed = content.trim()
if (!trimmed) return
setActiveCommentLoading(true)
setReplyUpdating(true)
try {
await updateWorkflowCommentReply(appId, commentId, replyId, { content: trimmed, mentioned_user_ids: mentionedUserIds })
@ -337,9 +341,9 @@ export const useWorkflowComment = () => {
console.error('Failed to update reply:', error)
}
finally {
setActiveCommentLoading(false)
setReplyUpdating(false)
}
}, [appId, loadComments, refreshActiveComment, setActiveCommentLoading])
}, [appId, loadComments, refreshActiveComment, setReplyUpdating])
const handleCommentReplyDelete = useCallback(async (commentId: string, replyId: string) => {
if (!appId) return
@ -394,6 +398,8 @@ export const useWorkflowComment = () => {
pendingComment,
activeComment,
activeCommentLoading,
replySubmitting,
replyUpdating,
handleCommentSubmit,
handleCommentCancel,
handleCommentIconClick,

View File

@ -188,6 +188,8 @@ export const Workflow: FC<WorkflowProps> = memo(({
pendingComment,
activeComment,
activeCommentLoading,
replySubmitting,
replyUpdating,
handleCommentSubmit,
handleCommentCancel,
handleCommentIconClick,
@ -468,6 +470,8 @@ export const Workflow: FC<WorkflowProps> = memo(({
key={`${comment.id}-thread`}
comment={activeComment}
loading={activeCommentLoading}
replySubmitting={replySubmitting}
replyUpdating={replyUpdating}
onClose={handleActiveCommentClose}
onResolve={() => handleCommentResolve(comment.id)}
onDelete={() => handleCommentDeleteClick(comment.id)}

View File

@ -10,6 +10,10 @@ export type CommentSliceShape = {
setActiveCommentDetail: (comment: WorkflowCommentDetail | null) => void
activeCommentDetailLoading: boolean
setActiveCommentDetailLoading: (loading: boolean) => void
replySubmitting: boolean
setReplySubmitting: (loading: boolean) => void
replyUpdating: boolean
setReplyUpdating: (loading: boolean) => void
commentDetailCache: Record<string, WorkflowCommentDetail>
setCommentDetailCache: (cache: Record<string, WorkflowCommentDetail>) => void
mentionableUsersCache: Record<string, UserProfile[]>
@ -27,6 +31,10 @@ export const createCommentSlice: StateCreator<CommentSliceShape> = set => ({
setActiveCommentDetail: activeCommentDetail => set({ activeCommentDetail }),
activeCommentDetailLoading: false,
setActiveCommentDetailLoading: activeCommentDetailLoading => set({ activeCommentDetailLoading }),
replySubmitting: false,
setReplySubmitting: replySubmitting => set({ replySubmitting }),
replyUpdating: false,
setReplyUpdating: replyUpdating => set({ replyUpdating }),
commentDetailCache: {},
setCommentDetailCache: commentDetailCache => set({ commentDetailCache }),
mentionableUsersCache: {},