52 lines
2.7 KiB
TypeScript
52 lines
2.7 KiB
TypeScript
import { useCallback, useEffect, useRef, useState } from "react";
|
|
import useSWR from "swr";
|
|
import type { AgentApiClient } from "../api/client";
|
|
|
|
export function useAgentMessageInbox(client: AgentApiClient, sessionId: string | null) {
|
|
const inbox = useSWR(sessionId ? ["agent-message-inbox", sessionId] : null,
|
|
([, id]) => client.listAgentMessages(id), { revalidateOnMount: false, revalidateOnFocus: true, shouldRetryOnError: false });
|
|
const [loadingMore, setLoadingMore] = useState(false);
|
|
const mutate = inbox.mutate;
|
|
const refresh = useCallback(() => { void mutate().catch(() => undefined); }, [mutate]);
|
|
const refreshRef = useRef(refresh);
|
|
useEffect(() => { refreshRef.current = refresh; }, [refresh]);
|
|
useEffect(() => {
|
|
if (!sessionId) return;
|
|
const controller = new AbortController();
|
|
let timer: ReturnType<typeof setTimeout> | undefined;
|
|
let retryDelay = 1500;
|
|
let initialized = false;
|
|
// Prefer the initial stream notification, but do not wait indefinitely for its first frame.
|
|
const initialLoadTimer = setTimeout(() => { initialized = true; refreshRef.current(); }, 1500);
|
|
function subscribe() {
|
|
void client.streamAgentMessages(sessionId!, { signal: controller.signal, onMessage: () => { initialized = true; clearTimeout(initialLoadTimer); retryDelay = 1500; refreshRef.current(); } })
|
|
.catch(() => {
|
|
// A failed subscription must not prevent the initial list from loading.
|
|
if (!initialized && !controller.signal.aborted) { initialized = true; clearTimeout(initialLoadTimer); refreshRef.current(); }
|
|
retryDelay = Math.min(retryDelay * 2, 30_000);
|
|
})
|
|
.finally(() => {
|
|
if (!controller.signal.aborted) timer = setTimeout(subscribe, retryDelay);
|
|
});
|
|
}
|
|
subscribe();
|
|
return () => { controller.abort(); clearTimeout(timer); clearTimeout(initialLoadTimer); };
|
|
}, [client, sessionId]);
|
|
return {
|
|
messages: inbox.data?.items ?? [],
|
|
hasMore: Boolean(inbox.data?.next_cursor),
|
|
loading: Boolean(sessionId) && (inbox.isLoading || (!inbox.data && !inbox.error)),
|
|
error: inbox.error ? "任务消息暂时无法加载" : null,
|
|
refresh,
|
|
loadingMore,
|
|
loadMore: async () => {
|
|
if (!sessionId || !inbox.data?.next_cursor || loadingMore) return;
|
|
setLoadingMore(true);
|
|
try {
|
|
const page = await client.listAgentMessages(sessionId, { cursor: inbox.data.next_cursor });
|
|
await inbox.mutate((current) => current ? { items: [...current.items, ...page.items.filter((item) => !current.items.some((existing) => existing.message_id === item.message_id))], next_cursor: page.next_cursor } : page, { revalidate: false });
|
|
} finally { setLoadingMore(false); }
|
|
}
|
|
};
|
|
}
|