mirror of
https://github.com/humanlayer/humanlayer.git
synced 2025-08-20 19:01:22 +03:00
Fix syncing of conversation events in SessionDetail (#226)
* Fix syncing of conversation events in SessionDetail * ellipsis fixes
This commit is contained in:
@@ -6,7 +6,7 @@ import { Fragment, jsx, jsxs } from 'react/jsx-runtime'
|
||||
import { ConversationEvent, ConversationEventType, SessionInfo } from '@/lib/daemon/types'
|
||||
import { Card, CardContent } from '../ui/card'
|
||||
import { useHotkeys } from 'react-hotkeys-hook'
|
||||
import { useFormattedConversation, useConversation } from '@/hooks/useConversation'
|
||||
import { useConversation } from '@/hooks/useConversation'
|
||||
import { Skeleton } from '../ui/skeleton'
|
||||
import { Suspense, useEffect, useRef, useState } from 'react'
|
||||
import { Bot, MessageCircleDashed, Wrench } from 'lucide-react'
|
||||
@@ -231,9 +231,8 @@ function ConversationContent({
|
||||
setExpandedEventId: (id: number | null) => void
|
||||
isWideView: boolean
|
||||
}) {
|
||||
const { formattedEvents, loading, error } = useFormattedConversation(sessionId)
|
||||
const { events } = useConversation(sessionId)
|
||||
console.log('raw events', events)
|
||||
// const { formattedEvents, loading, error } = useFormattedConversation(sessionId)
|
||||
const { events, loading, error, isInitialLoad } = useConversation(sessionId, undefined, 1000)
|
||||
const displayObjects = events.map(eventToDisplayObject)
|
||||
const nonEmptyDisplayObjects = displayObjects.filter(displayObject => displayObject !== null)
|
||||
|
||||
@@ -280,13 +279,13 @@ function ConversationContent({
|
||||
if (!starryNight) {
|
||||
createStarryNight([textMd]).then(sn => (starryNight = sn))
|
||||
}
|
||||
}, [loading, formattedEvents])
|
||||
}, [loading, events])
|
||||
|
||||
if (error) {
|
||||
return <div className="text-destructive">Error loading conversation: {error}</div>
|
||||
}
|
||||
|
||||
if (loading) {
|
||||
if (loading && isInitialLoad) {
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<Skeleton className="h-4 w-3/4" />
|
||||
@@ -297,7 +296,7 @@ function ConversationContent({
|
||||
}
|
||||
|
||||
// No events yet.
|
||||
if (formattedEvents.length === 0) {
|
||||
if (events.length === 0) {
|
||||
return (
|
||||
<div className="flex flex-col items-center justify-center py-8 text-center">
|
||||
<div className="text-muted-foreground mb-2">
|
||||
@@ -392,7 +391,11 @@ function SessionDetail({ session, onClose }: SessionDetailProps) {
|
||||
<section className="flex flex-col gap-4">
|
||||
<hgroup className="flex flex-col gap-1">
|
||||
<h2 className="text-lg font-medium text-foreground font-mono">{session.query} </h2>
|
||||
<small className="text-muted-foreground font-mono text-xs uppercase tracking-wider">
|
||||
<small
|
||||
className={`font-mono text-xs uppercase tracking-wider ${
|
||||
session.status === 'running' ? 'text-green-600 font-bold' : 'text-muted-foreground'
|
||||
}`}
|
||||
>
|
||||
{`${session.status}${session.model ? `/ ${session.model}` : ''}`}
|
||||
</small>
|
||||
</hgroup>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useState, useEffect, useCallback } from 'react'
|
||||
import { useState, useEffect, useCallback, useRef } from 'react'
|
||||
import { daemonClient, ConversationEvent } from '@/lib/daemon'
|
||||
import { formatError } from '@/utils/errors'
|
||||
|
||||
@@ -6,15 +6,26 @@ interface UseConversationReturn {
|
||||
events: ConversationEvent[]
|
||||
loading: boolean
|
||||
error: string | null
|
||||
isInitialLoad: boolean
|
||||
refresh: () => Promise<void>
|
||||
}
|
||||
|
||||
export function useConversation(sessionId?: string, claudeSessionId?: string): UseConversationReturn {
|
||||
export function useConversation(
|
||||
sessionId?: string,
|
||||
claudeSessionId?: string,
|
||||
pollInterval: number = 1000,
|
||||
): UseConversationReturn {
|
||||
const [events, setEvents] = useState<ConversationEvent[]>([])
|
||||
const [loading, setLoading] = useState(true)
|
||||
const [error, setError] = useState<string | null>(null)
|
||||
const [errorCount, setErrorCount] = useState(0)
|
||||
const [isInitialLoad, setIsInitialLoad] = useState(true)
|
||||
|
||||
const fetchConversation = useCallback(async () => {
|
||||
if (errorCount > 3) {
|
||||
return
|
||||
}
|
||||
|
||||
if (!sessionId && !claudeSessionId) {
|
||||
setError('Either sessionId or claudeSessionId must be provided')
|
||||
setLoading(false)
|
||||
@@ -27,22 +38,39 @@ export function useConversation(sessionId?: string, claudeSessionId?: string): U
|
||||
|
||||
const response = await daemonClient.getConversation(sessionId, claudeSessionId)
|
||||
setEvents(response.events)
|
||||
setErrorCount(0)
|
||||
setIsInitialLoad(false)
|
||||
} catch (err) {
|
||||
setError(formatError(err))
|
||||
setErrorCount(prev => prev + 1)
|
||||
} finally {
|
||||
setLoading(false)
|
||||
}
|
||||
}, [sessionId, claudeSessionId])
|
||||
}, [sessionId, claudeSessionId, errorCount])
|
||||
|
||||
// Store the latest fetchConversation function in a ref
|
||||
const fetchConversationRef = useRef(fetchConversation)
|
||||
fetchConversationRef.current = fetchConversation
|
||||
|
||||
useEffect(() => {
|
||||
fetchConversation()
|
||||
}, [fetchConversation])
|
||||
// Initial fetch
|
||||
fetchConversationRef.current()
|
||||
|
||||
const interval = setInterval(() => {
|
||||
fetchConversationRef.current()
|
||||
}, pollInterval)
|
||||
|
||||
return () => {
|
||||
clearInterval(interval)
|
||||
}
|
||||
}, []) // Empty dependency array - only runs once on mount
|
||||
|
||||
return {
|
||||
events,
|
||||
loading,
|
||||
error,
|
||||
refresh: fetchConversation,
|
||||
isInitialLoad,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user