67 lines
4.3 KiB
TypeScript
67 lines
4.3 KiB
TypeScript
import { useEffect, useRef, useState } from 'react';
|
||
import { Button } from '@/components/ui/button';
|
||
import { cloudAgentsApi } from '@/lib/cloud-agents-api';
|
||
import type { CloudAgentEntry, CloudThread } from '../../../shared/cloud-agents';
|
||
import { cloudStatus } from './CloudChat';
|
||
|
||
export function CloudOverview({ section, onAgent, onThread }: {
|
||
section: 'received' | 'activity'; onAgent: (entry: CloudAgentEntry) => void; onThread: (thread: CloudThread) => void;
|
||
}) {
|
||
const [agents, setAgents] = useState<CloudAgentEntry[]>([]);
|
||
const [threads, setThreads] = useState<CloudThread[]>([]);
|
||
const [error, setError] = useState('');
|
||
const [loading, setLoading] = useState(true);
|
||
const [offset, setOffset] = useState<number | null>(null);
|
||
const [refresh, setRefresh] = useState(0);
|
||
const paged = useRef(false);
|
||
useEffect(() => {
|
||
let live = true;
|
||
paged.current = false;
|
||
setLoading(true);
|
||
const load = async () => {
|
||
try {
|
||
if (section === 'received') {
|
||
const result = await cloudAgentsApi.call('received', {});
|
||
if (live) setAgents(result.agents);
|
||
} else {
|
||
const result = await cloudAgentsApi.call('threads', {});
|
||
if (live) { setThreads(result.threads); setOffset(result.next_offset); }
|
||
}
|
||
if (live) setError('');
|
||
} catch(e) { if (live) setError(e instanceof Error ? e.message : '暂时无法读取'); }
|
||
finally { if (live) setLoading(false); }
|
||
};
|
||
void load();
|
||
const timer = window.setInterval(() => { if (!paged.current) void load(); }, 15000);
|
||
return () => { live = false; window.clearInterval(timer); };
|
||
}, [section, refresh]);
|
||
const more = async () => {
|
||
if (offset === null) return;
|
||
paged.current = true;
|
||
setLoading(true);
|
||
try { const page = await cloudAgentsApi.call('threads', { offset }); setThreads(v => [...v, ...page.threads]); setOffset(page.next_offset); }
|
||
catch(e) { setError(e instanceof Error ? e.message : '加载失败'); } finally { setLoading(false); }
|
||
};
|
||
return <section className="space-y-4">
|
||
{error && <p role="alert" className="text-sm text-destructive">{error}<Button variant="ghost" onClick={() => setRefresh(v => v + 1)}>重新连接</Button></p>}
|
||
{section === 'received' ? <>
|
||
<p className="text-sm text-muted-foreground">这里显示其他创作者授权你使用的智能体。由创建者承担费用,你的会话和文件仅属于你。</p>
|
||
{!loading && !agents.length && <p className="py-12 text-center text-sm text-muted-foreground">还没有收到分享</p>}
|
||
<div className="grid gap-4 sm:grid-cols-2 lg:grid-cols-3">{agents.map(agent => <button key={agent.slug} onClick={() => onAgent(agent)}
|
||
className="rounded-xl border border-border p-5 text-left transition-colors hover:bg-muted/30 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring">
|
||
<h2 className="font-medium">{agent.name}</h2><p className="mt-2 line-clamp-3 text-sm leading-6 text-muted-foreground">{agent.purpose}</p>
|
||
<p className="mt-4 text-xs text-muted-foreground">版本 {agent.published_version} · 打开对话</p>
|
||
</button>)}</div></> : <>
|
||
<p className="text-sm text-muted-foreground">查看自己的对话、自动任务结果和待确认事项。</p>
|
||
{!loading && !threads.length && <p className="py-12 text-center text-sm text-muted-foreground">还没有运行记录</p>}
|
||
{threads.map(thread => <button key={thread.thread_id} onClick={() => onThread(thread)}
|
||
className="flex min-h-20 w-full items-center justify-between gap-4 rounded-xl border border-border p-4 text-left transition-colors hover:bg-muted/30 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring">
|
||
<div className="min-w-0"><h2 className="truncate text-sm font-medium">{thread.unread && <span className="mr-2 inline-block h-2 w-2 rounded-full bg-violet-500" aria-label="未读" />}{thread.title || '未命名对话'}</h2>
|
||
<p className="mt-2 text-xs text-muted-foreground">{thread.mode === 'preview' ? '草稿预览' : '正式运行'} · {new Date(thread.updated_at).toLocaleString()}</p></div>
|
||
<span className="shrink-0 text-sm text-muted-foreground">{cloudStatus(thread.status)}</span>
|
||
</button>)}
|
||
{offset !== null && <Button variant="outline" disabled={loading} onClick={() => void more()}>加载更早记录</Button>}
|
||
</>}
|
||
</section>;
|
||
}
|