@@ -14,6 +14,7 @@ import timezone from 'dayjs/plugin/timezone'
1414import { createContext , useContext } from 'use-context-selector'
1515import { useShallow } from 'zustand/react/shallow'
1616import { useTranslation } from 'react-i18next'
17+ import { usePathname , useRouter , useSearchParams } from 'next/navigation'
1718import type { ChatItemInTree } from '../../base/chat/types'
1819import Indicator from '../../header/indicator'
1920import VarPanel from './var-panel'
@@ -42,6 +43,10 @@ import cn from '@/utils/classnames'
4243import { noop } from 'lodash-es'
4344import PromptLogModal from '../../base/prompt-log-modal'
4445
46+ type AppStoreState = ReturnType < typeof useAppStore . getState >
47+ type ConversationListItem = ChatConversationGeneralDetail | CompletionConversationGeneralDetail
48+ type ConversationSelection = ConversationListItem | { id : string ; isPlaceholder ?: true }
49+
4550dayjs . extend ( utc )
4651dayjs . extend ( timezone )
4752
@@ -201,7 +206,7 @@ function DetailPanel({ detail, onFeedback }: IDetailPanel) {
201206 const { formatTime } = useTimestamp ( )
202207 const { onClose, appDetail } = useContext ( DrawerContext )
203208 const { notify } = useContext ( ToastContext )
204- const { currentLogItem, setCurrentLogItem, showMessageLogModal, setShowMessageLogModal, showPromptLogModal, setShowPromptLogModal, currentLogModalActiveTab } = useAppStore ( useShallow ( state => ( {
209+ const { currentLogItem, setCurrentLogItem, showMessageLogModal, setShowMessageLogModal, showPromptLogModal, setShowPromptLogModal, currentLogModalActiveTab } = useAppStore ( useShallow ( ( state : AppStoreState ) => ( {
205210 currentLogItem : state . currentLogItem ,
206211 setCurrentLogItem : state . setCurrentLogItem ,
207212 showMessageLogModal : state . showMessageLogModal ,
@@ -893,20 +898,113 @@ const ChatConversationDetailComp: FC<{ appId?: string; conversationId?: string }
893898const ConversationList : FC < IConversationList > = ( { logs, appDetail, onRefresh } ) => {
894899 const { t } = useTranslation ( )
895900 const { formatTime } = useTimestamp ( )
901+ const router = useRouter ( )
902+ const pathname = usePathname ( )
903+ const searchParams = useSearchParams ( )
904+ const conversationIdInUrl = searchParams . get ( 'conversation_id' ) ?? undefined
896905
897906 const media = useBreakpoints ( )
898907 const isMobile = media === MediaType . mobile
899908
900909 const [ showDrawer , setShowDrawer ] = useState < boolean > ( false ) // Whether to display the chat details drawer
901- const [ currentConversation , setCurrentConversation ] = useState < ChatConversationGeneralDetail | CompletionConversationGeneralDetail | undefined > ( ) // Currently selected conversation
910+ const [ currentConversation , setCurrentConversation ] = useState < ConversationSelection | undefined > ( ) // Currently selected conversation
911+ const closingConversationIdRef = useRef < string | null > ( null )
912+ const pendingConversationIdRef = useRef < string | null > ( null )
913+ const pendingConversationCacheRef = useRef < ConversationSelection | undefined > ( undefined )
902914 const isChatMode = appDetail . mode !== 'completion' // Whether the app is a chat app
903915 const isChatflow = appDetail . mode === 'advanced-chat' // Whether the app is a chatflow app
904- const { setShowPromptLogModal, setShowAgentLogModal, setShowMessageLogModal } = useAppStore ( useShallow ( state => ( {
916+ const { setShowPromptLogModal, setShowAgentLogModal, setShowMessageLogModal } = useAppStore ( useShallow ( ( state : AppStoreState ) => ( {
905917 setShowPromptLogModal : state . setShowPromptLogModal ,
906918 setShowAgentLogModal : state . setShowAgentLogModal ,
907919 setShowMessageLogModal : state . setShowMessageLogModal ,
908920 } ) ) )
909921
922+ const activeConversationId = conversationIdInUrl ?? pendingConversationIdRef . current ?? currentConversation ?. id
923+
924+ const buildUrlWithConversation = useCallback ( ( conversationId ?: string ) => {
925+ const params = new URLSearchParams ( searchParams . toString ( ) )
926+ if ( conversationId )
927+ params . set ( 'conversation_id' , conversationId )
928+ else
929+ params . delete ( 'conversation_id' )
930+
931+ const queryString = params . toString ( )
932+ return queryString ? `${ pathname } ?${ queryString } ` : pathname
933+ } , [ pathname , searchParams ] )
934+
935+ const handleRowClick = useCallback ( ( log : ConversationListItem ) => {
936+ if ( conversationIdInUrl === log . id ) {
937+ if ( ! showDrawer )
938+ setShowDrawer ( true )
939+
940+ if ( ! currentConversation || currentConversation . id !== log . id )
941+ setCurrentConversation ( log )
942+ return
943+ }
944+
945+ pendingConversationIdRef . current = log . id
946+ pendingConversationCacheRef . current = log
947+ if ( ! showDrawer )
948+ setShowDrawer ( true )
949+
950+ if ( currentConversation ?. id !== log . id )
951+ setCurrentConversation ( undefined )
952+
953+ router . push ( buildUrlWithConversation ( log . id ) , { scroll : false } )
954+ } , [ buildUrlWithConversation , conversationIdInUrl , currentConversation , router , showDrawer ] )
955+
956+ const currentConversationId = currentConversation ?. id
957+
958+ useEffect ( ( ) => {
959+ if ( ! conversationIdInUrl ) {
960+ if ( pendingConversationIdRef . current )
961+ return
962+
963+ if ( showDrawer || currentConversationId ) {
964+ setShowDrawer ( false )
965+ setCurrentConversation ( undefined )
966+ }
967+ closingConversationIdRef . current = null
968+ pendingConversationCacheRef . current = undefined
969+ return
970+ }
971+
972+ if ( closingConversationIdRef . current === conversationIdInUrl )
973+ return
974+
975+ if ( pendingConversationIdRef . current === conversationIdInUrl )
976+ pendingConversationIdRef . current = null
977+
978+ const matchedConversation = logs ?. data ?. find ( ( item : ConversationListItem ) => item . id === conversationIdInUrl )
979+ const nextConversation : ConversationSelection = matchedConversation
980+ ?? pendingConversationCacheRef . current
981+ ?? { id : conversationIdInUrl , isPlaceholder : true }
982+
983+ if ( ! showDrawer )
984+ setShowDrawer ( true )
985+
986+ if ( ! currentConversation || currentConversation . id !== conversationIdInUrl || ( ! ( 'created_at' in currentConversation ) && matchedConversation ) )
987+ setCurrentConversation ( nextConversation )
988+
989+ if ( pendingConversationCacheRef . current ?. id === conversationIdInUrl || matchedConversation )
990+ pendingConversationCacheRef . current = undefined
991+ } , [ conversationIdInUrl , currentConversation , isChatMode , logs ?. data , showDrawer ] )
992+
993+ const onCloseDrawer = useCallback ( ( ) => {
994+ onRefresh ( )
995+ setShowDrawer ( false )
996+ setCurrentConversation ( undefined )
997+ setShowPromptLogModal ( false )
998+ setShowAgentLogModal ( false )
999+ setShowMessageLogModal ( false )
1000+ pendingConversationIdRef . current = null
1001+ pendingConversationCacheRef . current = undefined
1002+ closingConversationIdRef . current = conversationIdInUrl ?? null
1003+
1004+ if ( conversationIdInUrl )
1005+ router . replace ( buildUrlWithConversation ( ) , { scroll : false } )
1006+ } , [ buildUrlWithConversation , conversationIdInUrl , onRefresh , router , setShowAgentLogModal , setShowMessageLogModal , setShowPromptLogModal ] )
1007+
9101008 // Annotated data needs to be highlighted
9111009 const renderTdValue = ( value : string | number | null , isEmptyStyle : boolean , isHighlight = false , annotation ?: LogAnnotation ) => {
9121010 return (
@@ -925,15 +1023,6 @@ const ConversationList: FC<IConversationList> = ({ logs, appDetail, onRefresh })
9251023 )
9261024 }
9271025
928- const onCloseDrawer = ( ) => {
929- onRefresh ( )
930- setShowDrawer ( false )
931- setCurrentConversation ( undefined )
932- setShowPromptLogModal ( false )
933- setShowAgentLogModal ( false )
934- setShowMessageLogModal ( false )
935- }
936-
9371026 if ( ! logs )
9381027 return < Loading />
9391028
@@ -960,11 +1049,8 @@ const ConversationList: FC<IConversationList> = ({ logs, appDetail, onRefresh })
9601049 const rightValue = get ( log , isChatMode ? 'message_count' : 'message.answer' )
9611050 return < tr
9621051 key = { log . id }
963- className = { cn ( 'cursor-pointer border-b border-divider-subtle hover:bg-background-default-hover' , currentConversation ?. id !== log . id ? '' : 'bg-background-default-hover' ) }
964- onClick = { ( ) => {
965- setShowDrawer ( true )
966- setCurrentConversation ( log )
967- } } >
1052+ className = { cn ( 'cursor-pointer border-b border-divider-subtle hover:bg-background-default-hover' , activeConversationId !== log . id ? '' : 'bg-background-default-hover' ) }
1053+ onClick = { ( ) => handleRowClick ( log ) } >
9681054 < td className = 'h-4' >
9691055 { ! log . read_at && (
9701056 < div className = 'flex items-center p-3 pr-0.5' >
0 commit comments