Refresh folder pages for scoped thread updates
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
import { RealtimeRefresh } from "@/components/app-runtime";
|
||||
import {
|
||||
AppShell,
|
||||
ConversationList,
|
||||
@@ -22,6 +23,12 @@ export default async function ConversationFolderPage({
|
||||
|
||||
return (
|
||||
<AppShell bottomNav={false}>
|
||||
{folder ? (
|
||||
<RealtimeRefresh
|
||||
projectIds={folder.threads.map((thread) => thread.projectId)}
|
||||
events={["conversation.updated", "project.messages.updated"]}
|
||||
/>
|
||||
) : null}
|
||||
<StatusBar />
|
||||
<PageNav title={folder?.folderLabel ?? "项目线程"} backHref="/conversations" />
|
||||
<div className="space-y-3 px-[18px] pb-6">
|
||||
|
||||
@@ -254,16 +254,23 @@ export function NativeAppBridge() {
|
||||
export function RealtimeRefresh({
|
||||
events,
|
||||
projectId,
|
||||
projectIds,
|
||||
conversationUpdatedNotes,
|
||||
}: {
|
||||
events: BossEventName[];
|
||||
projectId?: string;
|
||||
projectIds?: string[];
|
||||
conversationUpdatedNotes?: string[];
|
||||
}) {
|
||||
const router = useRouter();
|
||||
|
||||
useEffect(() => {
|
||||
const source = new EventSource("/api/v1/events");
|
||||
const projectScopeIds = new Set(
|
||||
[projectId, ...(projectIds ?? [])]
|
||||
.filter((value): value is string => Boolean(value?.trim()))
|
||||
.map((value) => value.trim()),
|
||||
);
|
||||
const listeners = Array.from(new Set([
|
||||
"conversation.context_indicator.updated",
|
||||
"project.context_risk.updated",
|
||||
@@ -285,11 +292,11 @@ export function RealtimeRefresh({
|
||||
}
|
||||
}
|
||||
|
||||
if (projectId) {
|
||||
if (projectScopeIds.size > 0) {
|
||||
if (!payload || typeof payload.projectId !== "string" || !payload.projectId.trim()) {
|
||||
return true;
|
||||
}
|
||||
if (payload.projectId !== projectId) {
|
||||
if (!projectScopeIds.has(payload.projectId)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -327,7 +334,7 @@ export function RealtimeRefresh({
|
||||
}
|
||||
source.close();
|
||||
};
|
||||
}, [conversationUpdatedNotes, events, projectId, router]);
|
||||
}, [conversationUpdatedNotes, events, projectId, projectIds, router]);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ test("RealtimeRefresh supports project-scoped refresh filtering", async () => {
|
||||
const source = await readWorkspaceFile("src/components/app-runtime.tsx");
|
||||
|
||||
assert.match(source, /projectId\?: string/, "expected RealtimeRefresh to accept an optional projectId");
|
||||
assert.match(source, /projectIds\?: string\[]/, "expected RealtimeRefresh to accept optional projectIds");
|
||||
assert.match(
|
||||
source,
|
||||
/conversationUpdatedNotes\?: string\[]/,
|
||||
@@ -21,7 +22,7 @@ test("RealtimeRefresh supports project-scoped refresh filtering", async () => {
|
||||
);
|
||||
assert.match(
|
||||
source,
|
||||
/payload\.projectId !== projectId[\s\S]*return false/,
|
||||
/projectScopeIds\.has\(payload\.projectId\)/,
|
||||
"expected RealtimeRefresh to filter by matching projectId",
|
||||
);
|
||||
assert.match(
|
||||
@@ -62,3 +63,11 @@ test("project conversation pages wire project-scoped realtime refresh", async ()
|
||||
"expected versions page to refresh only on project goal markers",
|
||||
);
|
||||
});
|
||||
|
||||
test("folder conversation page wires folder thread ids into realtime refresh", async () => {
|
||||
const folderPage = await readWorkspaceFile("src/app/conversations/folders/[folderKey]/page.tsx");
|
||||
|
||||
assert.match(folderPage, /<RealtimeRefresh/, "expected folder page to render RealtimeRefresh");
|
||||
assert.match(folderPage, /projectIds=\{folder\.threads\.map\(\(thread\) => thread\.projectId\)\}/, "expected folder page to scope refreshes to folder thread project ids");
|
||||
assert.match(folderPage, /"conversation\.updated"/, "expected folder page to listen to conversation updates");
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user