mirror of
https://github.com/langgenius/dify.git
synced 2026-05-06 10:28:10 +08:00
Sync log detail drawer with conversation_id query parameter, so that we can share a specific conversation (#26980)
Co-authored-by: crazywoola <100913391+crazywoola@users.noreply.github.com>
This commit is contained in:
@ -14,6 +14,7 @@ import timezone from 'dayjs/plugin/timezone'
|
|||||||
import { createContext, useContext } from 'use-context-selector'
|
import { createContext, useContext } from 'use-context-selector'
|
||||||
import { useShallow } from 'zustand/react/shallow'
|
import { useShallow } from 'zustand/react/shallow'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import { usePathname, useRouter, useSearchParams } from 'next/navigation'
|
||||||
import type { ChatItemInTree } from '../../base/chat/types'
|
import type { ChatItemInTree } from '../../base/chat/types'
|
||||||
import Indicator from '../../header/indicator'
|
import Indicator from '../../header/indicator'
|
||||||
import VarPanel from './var-panel'
|
import VarPanel from './var-panel'
|
||||||
@ -42,6 +43,10 @@ import cn from '@/utils/classnames'
|
|||||||
import { noop } from 'lodash-es'
|
import { noop } from 'lodash-es'
|
||||||
import PromptLogModal from '../../base/prompt-log-modal'
|
import PromptLogModal from '../../base/prompt-log-modal'
|
||||||
|
|
||||||
|
type AppStoreState = ReturnType<typeof useAppStore.getState>
|
||||||
|
type ConversationListItem = ChatConversationGeneralDetail | CompletionConversationGeneralDetail
|
||||||
|
type ConversationSelection = ConversationListItem | { id: string; isPlaceholder?: true }
|
||||||
|
|
||||||
dayjs.extend(utc)
|
dayjs.extend(utc)
|
||||||
dayjs.extend(timezone)
|
dayjs.extend(timezone)
|
||||||
|
|
||||||
@ -201,7 +206,7 @@ function DetailPanel({ detail, onFeedback }: IDetailPanel) {
|
|||||||
const { formatTime } = useTimestamp()
|
const { formatTime } = useTimestamp()
|
||||||
const { onClose, appDetail } = useContext(DrawerContext)
|
const { onClose, appDetail } = useContext(DrawerContext)
|
||||||
const { notify } = useContext(ToastContext)
|
const { notify } = useContext(ToastContext)
|
||||||
const { currentLogItem, setCurrentLogItem, showMessageLogModal, setShowMessageLogModal, showPromptLogModal, setShowPromptLogModal, currentLogModalActiveTab } = useAppStore(useShallow(state => ({
|
const { currentLogItem, setCurrentLogItem, showMessageLogModal, setShowMessageLogModal, showPromptLogModal, setShowPromptLogModal, currentLogModalActiveTab } = useAppStore(useShallow((state: AppStoreState) => ({
|
||||||
currentLogItem: state.currentLogItem,
|
currentLogItem: state.currentLogItem,
|
||||||
setCurrentLogItem: state.setCurrentLogItem,
|
setCurrentLogItem: state.setCurrentLogItem,
|
||||||
showMessageLogModal: state.showMessageLogModal,
|
showMessageLogModal: state.showMessageLogModal,
|
||||||
@ -893,20 +898,113 @@ const ChatConversationDetailComp: FC<{ appId?: string; conversationId?: string }
|
|||||||
const ConversationList: FC<IConversationList> = ({ logs, appDetail, onRefresh }) => {
|
const ConversationList: FC<IConversationList> = ({ logs, appDetail, onRefresh }) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const { formatTime } = useTimestamp()
|
const { formatTime } = useTimestamp()
|
||||||
|
const router = useRouter()
|
||||||
|
const pathname = usePathname()
|
||||||
|
const searchParams = useSearchParams()
|
||||||
|
const conversationIdInUrl = searchParams.get('conversation_id') ?? undefined
|
||||||
|
|
||||||
const media = useBreakpoints()
|
const media = useBreakpoints()
|
||||||
const isMobile = media === MediaType.mobile
|
const isMobile = media === MediaType.mobile
|
||||||
|
|
||||||
const [showDrawer, setShowDrawer] = useState<boolean>(false) // Whether to display the chat details drawer
|
const [showDrawer, setShowDrawer] = useState<boolean>(false) // Whether to display the chat details drawer
|
||||||
const [currentConversation, setCurrentConversation] = useState<ChatConversationGeneralDetail | CompletionConversationGeneralDetail | undefined>() // Currently selected conversation
|
const [currentConversation, setCurrentConversation] = useState<ConversationSelection | undefined>() // Currently selected conversation
|
||||||
|
const closingConversationIdRef = useRef<string | null>(null)
|
||||||
|
const pendingConversationIdRef = useRef<string | null>(null)
|
||||||
|
const pendingConversationCacheRef = useRef<ConversationSelection | undefined>(undefined)
|
||||||
const isChatMode = appDetail.mode !== 'completion' // Whether the app is a chat app
|
const isChatMode = appDetail.mode !== 'completion' // Whether the app is a chat app
|
||||||
const isChatflow = appDetail.mode === 'advanced-chat' // Whether the app is a chatflow app
|
const isChatflow = appDetail.mode === 'advanced-chat' // Whether the app is a chatflow app
|
||||||
const { setShowPromptLogModal, setShowAgentLogModal, setShowMessageLogModal } = useAppStore(useShallow(state => ({
|
const { setShowPromptLogModal, setShowAgentLogModal, setShowMessageLogModal } = useAppStore(useShallow((state: AppStoreState) => ({
|
||||||
setShowPromptLogModal: state.setShowPromptLogModal,
|
setShowPromptLogModal: state.setShowPromptLogModal,
|
||||||
setShowAgentLogModal: state.setShowAgentLogModal,
|
setShowAgentLogModal: state.setShowAgentLogModal,
|
||||||
setShowMessageLogModal: state.setShowMessageLogModal,
|
setShowMessageLogModal: state.setShowMessageLogModal,
|
||||||
})))
|
})))
|
||||||
|
|
||||||
|
const activeConversationId = conversationIdInUrl ?? pendingConversationIdRef.current ?? currentConversation?.id
|
||||||
|
|
||||||
|
const buildUrlWithConversation = useCallback((conversationId?: string) => {
|
||||||
|
const params = new URLSearchParams(searchParams.toString())
|
||||||
|
if (conversationId)
|
||||||
|
params.set('conversation_id', conversationId)
|
||||||
|
else
|
||||||
|
params.delete('conversation_id')
|
||||||
|
|
||||||
|
const queryString = params.toString()
|
||||||
|
return queryString ? `${pathname}?${queryString}` : pathname
|
||||||
|
}, [pathname, searchParams])
|
||||||
|
|
||||||
|
const handleRowClick = useCallback((log: ConversationListItem) => {
|
||||||
|
if (conversationIdInUrl === log.id) {
|
||||||
|
if (!showDrawer)
|
||||||
|
setShowDrawer(true)
|
||||||
|
|
||||||
|
if (!currentConversation || currentConversation.id !== log.id)
|
||||||
|
setCurrentConversation(log)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
pendingConversationIdRef.current = log.id
|
||||||
|
pendingConversationCacheRef.current = log
|
||||||
|
if (!showDrawer)
|
||||||
|
setShowDrawer(true)
|
||||||
|
|
||||||
|
if (currentConversation?.id !== log.id)
|
||||||
|
setCurrentConversation(undefined)
|
||||||
|
|
||||||
|
router.push(buildUrlWithConversation(log.id), { scroll: false })
|
||||||
|
}, [buildUrlWithConversation, conversationIdInUrl, currentConversation, router, showDrawer])
|
||||||
|
|
||||||
|
const currentConversationId = currentConversation?.id
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!conversationIdInUrl) {
|
||||||
|
if (pendingConversationIdRef.current)
|
||||||
|
return
|
||||||
|
|
||||||
|
if (showDrawer || currentConversationId) {
|
||||||
|
setShowDrawer(false)
|
||||||
|
setCurrentConversation(undefined)
|
||||||
|
}
|
||||||
|
closingConversationIdRef.current = null
|
||||||
|
pendingConversationCacheRef.current = undefined
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (closingConversationIdRef.current === conversationIdInUrl)
|
||||||
|
return
|
||||||
|
|
||||||
|
if (pendingConversationIdRef.current === conversationIdInUrl)
|
||||||
|
pendingConversationIdRef.current = null
|
||||||
|
|
||||||
|
const matchedConversation = logs?.data?.find((item: ConversationListItem) => item.id === conversationIdInUrl)
|
||||||
|
const nextConversation: ConversationSelection = matchedConversation
|
||||||
|
?? pendingConversationCacheRef.current
|
||||||
|
?? { id: conversationIdInUrl, isPlaceholder: true }
|
||||||
|
|
||||||
|
if (!showDrawer)
|
||||||
|
setShowDrawer(true)
|
||||||
|
|
||||||
|
if (!currentConversation || currentConversation.id !== conversationIdInUrl || (matchedConversation && currentConversation !== matchedConversation))
|
||||||
|
setCurrentConversation(nextConversation)
|
||||||
|
|
||||||
|
if (pendingConversationCacheRef.current?.id === conversationIdInUrl || matchedConversation)
|
||||||
|
pendingConversationCacheRef.current = undefined
|
||||||
|
}, [conversationIdInUrl, currentConversation, isChatMode, logs?.data, showDrawer])
|
||||||
|
|
||||||
|
const onCloseDrawer = useCallback(() => {
|
||||||
|
onRefresh()
|
||||||
|
setShowDrawer(false)
|
||||||
|
setCurrentConversation(undefined)
|
||||||
|
setShowPromptLogModal(false)
|
||||||
|
setShowAgentLogModal(false)
|
||||||
|
setShowMessageLogModal(false)
|
||||||
|
pendingConversationIdRef.current = null
|
||||||
|
pendingConversationCacheRef.current = undefined
|
||||||
|
closingConversationIdRef.current = conversationIdInUrl ?? null
|
||||||
|
|
||||||
|
if (conversationIdInUrl)
|
||||||
|
router.replace(buildUrlWithConversation(), { scroll: false })
|
||||||
|
}, [buildUrlWithConversation, conversationIdInUrl, onRefresh, router, setShowAgentLogModal, setShowMessageLogModal, setShowPromptLogModal])
|
||||||
|
|
||||||
// Annotated data needs to be highlighted
|
// Annotated data needs to be highlighted
|
||||||
const renderTdValue = (value: string | number | null, isEmptyStyle: boolean, isHighlight = false, annotation?: LogAnnotation) => {
|
const renderTdValue = (value: string | number | null, isEmptyStyle: boolean, isHighlight = false, annotation?: LogAnnotation) => {
|
||||||
return (
|
return (
|
||||||
@ -925,15 +1023,6 @@ const ConversationList: FC<IConversationList> = ({ logs, appDetail, onRefresh })
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const onCloseDrawer = () => {
|
|
||||||
onRefresh()
|
|
||||||
setShowDrawer(false)
|
|
||||||
setCurrentConversation(undefined)
|
|
||||||
setShowPromptLogModal(false)
|
|
||||||
setShowAgentLogModal(false)
|
|
||||||
setShowMessageLogModal(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!logs)
|
if (!logs)
|
||||||
return <Loading />
|
return <Loading />
|
||||||
|
|
||||||
@ -960,11 +1049,8 @@ const ConversationList: FC<IConversationList> = ({ logs, appDetail, onRefresh })
|
|||||||
const rightValue = get(log, isChatMode ? 'message_count' : 'message.answer')
|
const rightValue = get(log, isChatMode ? 'message_count' : 'message.answer')
|
||||||
return <tr
|
return <tr
|
||||||
key={log.id}
|
key={log.id}
|
||||||
className={cn('cursor-pointer border-b border-divider-subtle hover:bg-background-default-hover', currentConversation?.id !== log.id ? '' : 'bg-background-default-hover')}
|
className={cn('cursor-pointer border-b border-divider-subtle hover:bg-background-default-hover', activeConversationId !== log.id ? '' : 'bg-background-default-hover')}
|
||||||
onClick={() => {
|
onClick={() => handleRowClick(log)}>
|
||||||
setShowDrawer(true)
|
|
||||||
setCurrentConversation(log)
|
|
||||||
}}>
|
|
||||||
<td className='h-4'>
|
<td className='h-4'>
|
||||||
{!log.read_at && (
|
{!log.read_at && (
|
||||||
<div className='flex items-center p-3 pr-0.5'>
|
<div className='flex items-center p-3 pr-0.5'>
|
||||||
|
|||||||
Reference in New Issue
Block a user