Files
mnote/wolai-frontend/src/hooks/use-convex-sidebar-data.ts
T

147 lines
4.8 KiB
TypeScript
Raw Normal View History

2026-01-18 19:01:31 +08:00
import { useMemo } from "react";
import { useQuery, useConvexAuth } from "convex/react";
import type { SidebarInitialData } from "@/components/sidebar/types";
import type { MediaAsset } from "@/types/media";
import { api } from "@/lib/convex/api";
/**
* Convex 模式下的侧边栏数据 hook
* 使用 Convex 的 useQuery 实现实时订阅,无需手动 refetch
*
* 注意:此 hook 仅应在 Convex 模式下使用
* 后端从 ctx.auth.getUserIdentity() 获取用户身份,前端无需传入 userId
*/
export function useConvexSidebarData(workspaceId: string): {
data: SidebarInitialData | null;
isLoading: boolean;
error: Error | null;
refetch: () => Promise<void>;
} {
const { isAuthenticated } = useConvexAuth();
// 显式转为 boolean,确保类型正确
const shouldFetch = Boolean(isAuthenticated && workspaceId);
// 使用 Convex 的 useQuery,自动订阅实时更新
// 后端从 ctx.auth.getUserIdentity() 获取用户身份,无需前端传入 userId
const documents = useQuery(
api.documents.listByWorkspace,
shouldFetch ? { workspaceId } : "skip"
);
const trashedDocuments = useQuery(
api.documents.listTrashedByWorkspace,
shouldFetch ? { workspaceId } : "skip"
);
const mindmaps = useQuery(
api.mindmaps.listByWorkspace,
shouldFetch ? { workspaceId, includeDeleted: true } : "skip"
);
const workspacesResult = useQuery(
api.workspaces.fetchWorkspaceSummaries,
shouldFetch ? undefined : "skip"
);
// 组合数据,格式与 SidebarInitialData 一致
const data: SidebarInitialData | null = useMemo(() => {
// 当 skip 时,返回值是 undefined
if (documents === undefined ||
trashedDocuments === undefined ||
mindmaps === undefined ||
workspacesResult === undefined) {
return null;
}
const activeMindmaps = (mindmaps ?? []).filter((r) => !r.deleted_at);
const trashedMindmaps = (mindmaps ?? []).filter((r) => !!r.deleted_at);
const mindmapDocs = Array.from(new Set(activeMindmaps.map((r) => r.document_id)));
const mindmapAssets: MediaAsset[] = activeMindmaps.map((r) => {
const isLegacy = r.mindmap_id.startsWith("legacy-");
return {
id: r.mindmap_id,
workspace_id: r.workspace_id ?? workspaceId,
document_id: r.document_id,
asset_type: "mindmap",
file_url: null,
thumbnail_url: null,
bucket: null,
storage_path: null,
file_name: isLegacy ? "mindmap.json" : `mindmap-${r.mindmap_id}.json`,
file_size: null,
mime_type: "application/json",
ocr_payload: undefined,
ocr_strategy: null,
ocr_text: null,
ocr_status: null,
signed_url: null,
created_at: r.created_at ?? "",
updated_at: r.updated_at ?? "",
};
});
const trashedMindmapAssets: MediaAsset[] = trashedMindmaps.map((r) => {
const isLegacy = r.mindmap_id.startsWith("legacy-");
return {
id: r.mindmap_id,
workspace_id: r.workspace_id ?? workspaceId,
document_id: r.document_id,
asset_type: "mindmap",
file_url: null,
thumbnail_url: null,
bucket: null,
storage_path: null,
file_name: isLegacy ? "mindmap.json" : `mindmap-${r.mindmap_id}.json`,
file_size: null,
mime_type: "application/json",
ocr_payload: undefined,
ocr_strategy: null,
ocr_text: null,
ocr_status: null,
deleted_at: r.deleted_at ?? null,
deleted_by: r.deleted_by ?? null,
purged_at: null,
signed_url: null,
created_at: r.created_at ?? "",
updated_at: r.updated_at ?? "",
};
});
return {
activeWorkspaceId: workspacesResult.activeWorkspaceId || workspaceId,
workspaces: workspacesResult.workspaces,
documents,
trashedDocuments,
trashedMediaAssets: [],
trashedMindmapAssets,
mindmapDocs,
mindmapAssets,
mindmapAssetChildren: {},
tableAssets: [],
mediaAssets: [],
};
}, [documents, trashedDocuments, mindmaps, workspacesResult, workspaceId]);
// loading 状态:只有当 shouldFetch 为 true 且数据未加载时才算 loading
// 使用 === undefined 判断,因为 skip 时返回 undefined
const isLoading = shouldFetch && (
documents === undefined ||
trashedDocuments === undefined ||
mindmaps === undefined ||
workspacesResult === undefined
);
const error = null;
// Convex 模式下数据自动实时同步,refetch 是空操作
// Convex 的 useQuery 会自动实时同步,无需手动 refetch
// 这个方法只是为了保持与 useSidebarData 的接口兼容
const refetch = async () => {
// 空操作 - Convex 会自动同步数据
};
return { data, isLoading, error, refetch };
}