Keep focused agent timelines live and catch up instantly (#2196)
* fix(app): keep focused agent timelines live Window focus was conflated with app visibility, so switching OS windows could remove the selected timeline subscription. Separate those signals, grace every visibility-driven removal, and cover retained history until authoritative catch-up completes. * fix(app): keep timeline catch-up failures non-blocking * fix(app): surface timeline catch-up failures * fix(app): preserve file preview refocus * fix(app): preserve optimistic catch-up flow
This commit is contained in:
@@ -1,7 +1,15 @@
|
||||
import type { DaemonClient } from "@getpaseo/client/internal/daemon-client";
|
||||
import type { TFunction } from "i18next";
|
||||
import { SquarePen } from "lucide-react-native";
|
||||
import React, { memo, useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||
import React, {
|
||||
memo,
|
||||
useCallback,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useRef,
|
||||
useState,
|
||||
useSyncExternalStore,
|
||||
} from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { ActivityIndicator, Text, View } from "react-native";
|
||||
import ReanimatedAnimated from "react-native-reanimated";
|
||||
@@ -14,6 +22,7 @@ import { AgentStreamView, type AgentStreamViewHandle } from "@/agent-stream/view
|
||||
import { ArchivedAgentCallout } from "@/components/archived-agent-callout";
|
||||
import { FileDropZone } from "@/components/file-drop/file-drop-zone";
|
||||
import { useRetainedPanelActive } from "@/components/retained-panel";
|
||||
import { SidebarCallout } from "@/components/sidebar-callout";
|
||||
import { Composer } from "@/composer";
|
||||
import { AgentModeControl } from "@/composer/agent-controls/mode-control";
|
||||
import { RewindComposerRestoreProvider } from "@/components/rewind/composer-restore";
|
||||
@@ -751,6 +760,26 @@ function ChatAgentContent({
|
||||
const agentHistorySyncGeneration = useSessionStore((state) =>
|
||||
agentId ? (state.sessions[serverId]?.agentHistorySyncGeneration?.get(agentId) ?? -1) : -1,
|
||||
);
|
||||
const viewedTimelineSync = useSessionStore(
|
||||
(state) => state.sessions[serverId]?.viewedTimelineSync ?? null,
|
||||
);
|
||||
const subscribeToVisibilityCatchUp = useCallback(
|
||||
(listener: () => void) => viewedTimelineSync?.subscribe(listener) ?? (() => {}),
|
||||
[viewedTimelineSync],
|
||||
);
|
||||
const readTimelineStatus = useCallback(
|
||||
() =>
|
||||
!agentId || !viewedTimelineSync
|
||||
? ("ready" as const)
|
||||
: viewedTimelineSync.getAgentTimelineStatus(agentId),
|
||||
[agentId, viewedTimelineSync],
|
||||
);
|
||||
const timelineStatus = useSyncExternalStore(
|
||||
subscribeToVisibilityCatchUp,
|
||||
readTimelineStatus,
|
||||
readTimelineStatus,
|
||||
);
|
||||
const visibilityCatchUpStatus = isPaneVisible ? timelineStatus : "ready";
|
||||
const hasActiveCreateHandoff = useCreateFlowStore((state) =>
|
||||
findActiveCreateHandoff({ pendingByDraftId: state.pendingByDraftId, serverId, agentId }),
|
||||
);
|
||||
@@ -862,6 +891,7 @@ function ChatAgentContent({
|
||||
isArchivingCurrentAgent,
|
||||
isHistorySyncing,
|
||||
needsAuthoritativeSync,
|
||||
visibilityCatchUpStatus,
|
||||
continuity,
|
||||
hasHydratedHistoryBefore,
|
||||
},
|
||||
@@ -1004,6 +1034,7 @@ function ChatAgentContent({
|
||||
viewState.tag === "ready" &&
|
||||
viewState.sync.status === "catching_up" &&
|
||||
viewState.sync.ui === "overlay";
|
||||
const showHistorySyncError = viewState.tag === "ready" && viewState.sync.status === "sync_error";
|
||||
|
||||
return (
|
||||
<ChatAgentReadyContent
|
||||
@@ -1023,6 +1054,7 @@ function ChatAgentContent({
|
||||
handleComposerHeightChange={handleComposerHeightChange}
|
||||
handleMessageSent={handleMessageSent}
|
||||
showHistorySyncOverlay={showHistorySyncOverlay}
|
||||
showHistorySyncError={showHistorySyncError}
|
||||
cwd={agentCwd}
|
||||
onAttentionInputFocus={attentionController.clearOnInputFocus}
|
||||
onAttentionPromptSend={attentionController.clearOnPromptSend}
|
||||
@@ -1048,6 +1080,7 @@ const ChatAgentReadyContent = memo(function ChatAgentReadyContent({
|
||||
handleComposerHeightChange,
|
||||
handleMessageSent,
|
||||
showHistorySyncOverlay,
|
||||
showHistorySyncError,
|
||||
cwd,
|
||||
onAttentionInputFocus,
|
||||
onAttentionPromptSend,
|
||||
@@ -1069,6 +1102,7 @@ const ChatAgentReadyContent = memo(function ChatAgentReadyContent({
|
||||
handleComposerHeightChange: (height: number) => void;
|
||||
handleMessageSent: () => void;
|
||||
showHistorySyncOverlay: boolean;
|
||||
showHistorySyncError: boolean;
|
||||
cwd: string;
|
||||
onAttentionInputFocus: () => void;
|
||||
onAttentionPromptSend: () => void;
|
||||
@@ -1140,6 +1174,14 @@ const ChatAgentReadyContent = memo(function ChatAgentReadyContent({
|
||||
<FileDropZone style={styles.container} disabled={isArchivingCurrentAgent}>
|
||||
{contentContainer}
|
||||
|
||||
{showHistorySyncError ? (
|
||||
<SidebarCallout
|
||||
title={t("agentPanel.states.timelineSyncFailed")}
|
||||
variant="error"
|
||||
testID="agent-timeline-sync-error"
|
||||
/>
|
||||
) : null}
|
||||
|
||||
{composerSection}
|
||||
|
||||
{showHistorySyncOverlay ? (
|
||||
|
||||
Reference in New Issue
Block a user