feat: complete tree shell cutover and regression coverage
This commit is contained in:
@@ -40,21 +40,64 @@ const baseSidebarData: SidebarInitialData = {
|
||||
nodeId: "root",
|
||||
parentNodeId: null,
|
||||
nodeType: "page",
|
||||
projectionKind: "sidebar_tree",
|
||||
title: "Root",
|
||||
depth: 0,
|
||||
position: 0,
|
||||
childCount: 1,
|
||||
expandable: true,
|
||||
expandedByDefault: true,
|
||||
capabilities: [
|
||||
"expand",
|
||||
"open",
|
||||
"drag",
|
||||
"drop",
|
||||
"select",
|
||||
"create-child",
|
||||
"rename",
|
||||
"archive",
|
||||
"restore",
|
||||
"context-menu",
|
||||
"reorder",
|
||||
],
|
||||
resourceMeta: {
|
||||
resourceKind: "document",
|
||||
documentId: "root",
|
||||
workspaceId: "ws_1",
|
||||
iconHint: "page",
|
||||
},
|
||||
iconHint: "page",
|
||||
},
|
||||
{
|
||||
nodeId: "child",
|
||||
parentNodeId: "root",
|
||||
nodeType: "page",
|
||||
projectionKind: "sidebar_tree",
|
||||
title: "Child",
|
||||
depth: 1,
|
||||
position: 1,
|
||||
childCount: 0,
|
||||
expandable: false,
|
||||
expandedByDefault: true,
|
||||
capabilities: [
|
||||
"open",
|
||||
"drag",
|
||||
"drop",
|
||||
"select",
|
||||
"create-child",
|
||||
"rename",
|
||||
"archive",
|
||||
"restore",
|
||||
"context-menu",
|
||||
"reorder",
|
||||
],
|
||||
resourceMeta: {
|
||||
resourceKind: "document",
|
||||
documentId: "child",
|
||||
workspaceId: "ws_1",
|
||||
iconHint: "page",
|
||||
},
|
||||
iconHint: "page",
|
||||
},
|
||||
],
|
||||
edges: [
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
parseTreeStreamMessage,
|
||||
} from "@/lib/tree-stream/protocol";
|
||||
import { applyTreeStreamDelta, type TreeStreamDeltaEvent } from "@/lib/tree-stream/tree-delta";
|
||||
import { ensureMnoteWebAuthCookie } from "@/lib/mnote-web-auth";
|
||||
|
||||
export interface SidebarTreeStreamState {
|
||||
data: SidebarInitialData | null;
|
||||
@@ -60,9 +61,8 @@ export function useSidebarTreeStream(initialData: SidebarInitialData): SidebarTr
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const url = buildWorkspaceTreeStreamUrl(baseUrl, workspaceId, state.cursor);
|
||||
const eventSource = new EventSource(url, { withCredentials: true });
|
||||
eventSourceRef.current = eventSource;
|
||||
let cancelled = false;
|
||||
let eventSource: EventSource | null = null;
|
||||
|
||||
const handleMessage = (event: MessageEvent<string>) => {
|
||||
const envelope = parseTreeStreamMessage({
|
||||
@@ -121,21 +121,45 @@ export function useSidebarTreeStream(initialData: SidebarInitialData): SidebarTr
|
||||
status: previous.data ? "live" : "fallback",
|
||||
error: previous.error ?? new Error("tree stream 连接失败"),
|
||||
}));
|
||||
eventSource.close();
|
||||
eventSourceRef.current = null;
|
||||
eventSource?.close();
|
||||
if (eventSourceRef.current === eventSource) {
|
||||
eventSourceRef.current = null;
|
||||
}
|
||||
};
|
||||
|
||||
eventSource.addEventListener("snapshot", handleMessage as EventListener);
|
||||
eventSource.addEventListener("delta", handleMessage as EventListener);
|
||||
eventSource.addEventListener("resync", handleMessage as EventListener);
|
||||
eventSource.onmessage = handleMessage;
|
||||
eventSource.onerror = handleError;
|
||||
void (async () => {
|
||||
try {
|
||||
await ensureMnoteWebAuthCookie();
|
||||
if (cancelled) {
|
||||
return;
|
||||
}
|
||||
|
||||
const url = buildWorkspaceTreeStreamUrl(baseUrl, workspaceId, state.cursor);
|
||||
eventSource = new EventSource(url, { withCredentials: true });
|
||||
eventSourceRef.current = eventSource;
|
||||
eventSource.addEventListener("snapshot", handleMessage as EventListener);
|
||||
eventSource.addEventListener("delta", handleMessage as EventListener);
|
||||
eventSource.addEventListener("resync", handleMessage as EventListener);
|
||||
eventSource.onmessage = handleMessage;
|
||||
eventSource.onerror = handleError;
|
||||
} catch {
|
||||
if (cancelled) {
|
||||
return;
|
||||
}
|
||||
setState((previous) => ({
|
||||
...previous,
|
||||
status: previous.data ? "live" : "fallback",
|
||||
error: previous.error ?? new Error("tree stream 鉴权失败"),
|
||||
}));
|
||||
}
|
||||
})();
|
||||
|
||||
return () => {
|
||||
eventSource.removeEventListener("snapshot", handleMessage as EventListener);
|
||||
eventSource.removeEventListener("delta", handleMessage as EventListener);
|
||||
eventSource.removeEventListener("resync", handleMessage as EventListener);
|
||||
eventSource.close();
|
||||
cancelled = true;
|
||||
eventSource?.removeEventListener("snapshot", handleMessage as EventListener);
|
||||
eventSource?.removeEventListener("delta", handleMessage as EventListener);
|
||||
eventSource?.removeEventListener("resync", handleMessage as EventListener);
|
||||
eventSource?.close();
|
||||
if (eventSourceRef.current === eventSource) {
|
||||
eventSourceRef.current = null;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user