diff --git a/apps/webclaw/src/screens/chat/chat-screen.tsx b/apps/webclaw/src/screens/chat/chat-screen.tsx index 338ef79..6c4fac7 100644 --- a/apps/webclaw/src/screens/chat/chat-screen.tsx +++ b/apps/webclaw/src/screens/chat/chat-screen.tsx @@ -1,5 +1,5 @@ import { useCallback, useEffect, useMemo, useRef, useState } from 'react' -import { useNavigate } from '@tanstack/react-router' +import { Navigate, useNavigate } from '@tanstack/react-router' import { useQuery, useQueryClient } from '@tanstack/react-query' import { @@ -38,7 +38,7 @@ import { useChatSessions } from './hooks/use-chat-sessions' import { useChatStream } from './hooks/use-chat-stream' import { useChatPendingSend } from './hooks/use-chat-pending-send' import { useChatGenerationGuard } from './hooks/use-chat-generation-guard' -import { useChatErrorState } from './hooks/use-chat-error-state' +import { shouldRedirectToConnect } from './hooks/use-chat-error-state' import { useChatRedirect } from './hooks/use-chat-redirect' import type { AttachmentFile } from '@/components/attachment-button' import type { ChatComposerHelpers } from './components/chat-composer' @@ -66,7 +66,6 @@ export function ChatScreen({ const queryClient = useQueryClient() const [sending, setSending] = useState(false) const [creatingSession, setCreatingSession] = useState(false) - const [error, setError] = useState(null) const [isRedirecting, setIsRedirecting] = useState(false) const { headerRef, composerRef, mainRef, pinGroupMinHeight, headerHeight } = useChatMeasurements() @@ -144,7 +143,6 @@ export function ChatScreen({ }, [gatewayStatusQuery]) const isSidebarCollapsed = uiQuery.data.isSidebarCollapsed const handleActiveSessionDelete = useCallback(() => { - setError(null) setIsRedirecting(true) navigate({ to: '/new', replace: true }) }, [navigate]) @@ -253,7 +251,6 @@ export function ChatScreen({ setPendingGeneration(true) setSending(true) - setError(null) setWaitingForResponse(true) setPinToTop(true) @@ -304,7 +301,6 @@ export function ChatScreen({ }, ) } - setError(`Failed to send message. ${messageText}`) setPendingGeneration(false) setWaitingForResponse(false) setPinToTop(false) @@ -383,7 +379,7 @@ export function ChatScreen({ replace: true, }) }) - .catch((err: unknown) => { + .catch(() => { removeHistoryMessageByClientId( queryClient, 'new', @@ -392,9 +388,6 @@ export function ChatScreen({ optimisticId, ) helpers.setValue(body) - setError( - `Failed to create session. ${err instanceof Error ? err.message : String(err)}`, - ) setPendingGeneration(false) setWaitingForResponse(false) setPinToTop(false) @@ -460,6 +453,15 @@ export function ChatScreen({ const showGatewayNotice = showGatewayDown && gatewayStatusQuery.errorUpdatedAt > gatewayStatusMountRef.current + const redirectToConnect = shouldRedirectToConnect({ + isRedirecting, + shouldRedirectToNew, + sessionsReady: sessionsQuery.isSuccess, + activeExists, + sessionsError, + historyError, + gatewayStatusError, + }) const historyEmpty = !historyLoading && displayMessages.length === 0 const gatewayNotice = useMemo(() => { if (!showGatewayNotice) return null @@ -512,18 +514,6 @@ export function ChatScreen({ }, }) - useChatErrorState({ - error, - setError, - isRedirecting, - shouldRedirectToNew, - sessionsReady: sessionsQuery.isSuccess, - activeExists, - sessionsError, - historyError, - gatewayStatusError, - }) - useChatRedirect({ activeFriendlyId, isNewChat, @@ -567,6 +557,10 @@ export function ChatScreen({ /> ) + if (redirectToConnect) { + return + } + return (
void isRedirecting: boolean shouldRedirectToNew: boolean sessionsReady: boolean @@ -15,9 +10,7 @@ type UseChatErrorStateInput = { gatewayStatusError: string | null } -export function useChatErrorState({ - error, - setError, +export function shouldRedirectToConnect({ isRedirecting, shouldRedirectToNew, sessionsReady, @@ -26,49 +19,9 @@ export function useChatErrorState({ historyError, gatewayStatusError, }: UseChatErrorStateInput) { - const navigate = useNavigate() - - useEffect(() => { - if (isRedirecting) { - if (error) setError(null) - return - } - if (shouldRedirectToNew) { - if (error) setError(null) - return - } - if (sessionsReady && !activeExists) { - if (error) setError(null) - return - } - const messageText = sessionsError ?? historyError ?? gatewayStatusError - if (!messageText) { - if (error?.startsWith('Failed to load')) { - setError(null) - } - return - } - if (isMissingGatewayAuth(messageText)) { - navigate({ to: '/connect', replace: true }) - } - const message = sessionsError - ? `Failed to load sessions. ${sessionsError}` - : historyError - ? `Failed to load history. ${historyError}` - : gatewayStatusError - ? `Gateway unavailable. ${gatewayStatusError}` - : null - if (message) setError(message) - }, [ - activeExists, - error, - gatewayStatusError, - historyError, - isRedirecting, - navigate, - sessionsError, - sessionsReady, - setError, - shouldRedirectToNew, - ]) + if (isRedirecting || shouldRedirectToNew) return false + if (sessionsReady && !activeExists) return false + const messageText = sessionsError ?? historyError ?? gatewayStatusError + if (!messageText) return false + return isMissingGatewayAuth(messageText) } diff --git a/apps/webclaw/src/screens/chat/hooks/use-chat-idle-finish.ts b/apps/webclaw/src/screens/chat/hooks/use-chat-idle-finish.ts deleted file mode 100644 index 7c6f7df..0000000 --- a/apps/webclaw/src/screens/chat/hooks/use-chat-idle-finish.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { useEffect, useRef } from 'react' - -import { textFromMessage } from '../utils' -import { setPendingGeneration } from '../pending-send' -import type { GatewayMessage } from '../types' - -type UseChatIdleFinishInput = { - historyMessages: Array - streamStop: () => void - setWaitingForResponse: (value: boolean) => void -} - -export function useChatIdleFinish({ - historyMessages, - streamStop, - setWaitingForResponse, -}: UseChatIdleFinishInput) { - const lastAssistantSignature = useRef('') - const streamIdleTimer = useRef(null) - - useEffect(() => { - if (historyMessages.length === 0) return - const latestMessage = historyMessages[historyMessages.length - 1] - if (latestMessage.role !== 'assistant') return - const signature = `${historyMessages.length}:${textFromMessage(latestMessage).slice(-64)}` - if (signature !== lastAssistantSignature.current) { - lastAssistantSignature.current = signature - if (streamIdleTimer.current) { - window.clearTimeout(streamIdleTimer.current) - } - streamIdleTimer.current = window.setTimeout(() => { - streamStop() - setPendingGeneration(false) - setWaitingForResponse(false) - }, 12000) - } - return () => { - if (streamIdleTimer.current) { - window.clearTimeout(streamIdleTimer.current) - streamIdleTimer.current = null - } - } - }, [historyMessages, setWaitingForResponse, streamStop]) -}