0.1.07 文件树拖拽与多思维导图
This commit is contained in:
@@ -9,11 +9,18 @@ type AssetsChangedPayload = {
|
||||
asset?: unknown;
|
||||
assetIds?: string[];
|
||||
mindmapDeleted?: boolean;
|
||||
mindmapAssetIds?: string[];
|
||||
};
|
||||
|
||||
export function emitAssetsChanged(docId?: string, asset?: unknown, assetIds?: string[], mindmapDeleted?: boolean) {
|
||||
export function emitAssetsChanged(
|
||||
docId?: string,
|
||||
asset?: unknown,
|
||||
assetIds?: string[],
|
||||
mindmapDeleted?: boolean,
|
||||
mindmapAssetIds?: string[],
|
||||
) {
|
||||
if (typeof window === "undefined") return;
|
||||
const detail: AssetsChangedPayload = { docId, asset, assetIds, mindmapDeleted };
|
||||
const detail: AssetsChangedPayload = { docId, asset, assetIds, mindmapDeleted, mindmapAssetIds };
|
||||
window.dispatchEvent(new CustomEvent(ASSETS_CHANGED_EVENT, { detail }));
|
||||
}
|
||||
|
||||
|
||||
@@ -16,9 +16,21 @@ const tryAccess = async (file: string) => {
|
||||
export async function detectLocalMindmapDocs(docIds: string[]): Promise<string[]> {
|
||||
const results: string[] = [];
|
||||
for (const id of docIds) {
|
||||
const preferred = path.join(preferredBaseDir, id, "mindmap.json");
|
||||
const folder = path.join(preferredBaseDir, id);
|
||||
const preferredLegacy = path.join(folder, "mindmap.json");
|
||||
const legacy = path.join(legacyBaseDir, id, "mindmap.json");
|
||||
if ((await tryAccess(preferred)) || (await tryAccess(legacy))) {
|
||||
let has = false;
|
||||
if ((await tryAccess(preferredLegacy)) || (await tryAccess(legacy))) {
|
||||
has = true;
|
||||
} else {
|
||||
try {
|
||||
const entries = await fs.readdir(folder);
|
||||
has = entries.some((name) => /^mindmap-.+\.json$/i.test(name));
|
||||
} catch {
|
||||
has = false;
|
||||
}
|
||||
}
|
||||
if (has) {
|
||||
results.push(id);
|
||||
}
|
||||
}
|
||||
@@ -26,3 +38,50 @@ export async function detectLocalMindmapDocs(docIds: string[]): Promise<string[]
|
||||
}
|
||||
|
||||
export { preferredBaseDir, legacyBaseDir };
|
||||
|
||||
export type LocalMindmapFile = {
|
||||
documentId: string;
|
||||
mindmapId: string;
|
||||
fileName: string;
|
||||
source: "preferred" | "legacy";
|
||||
};
|
||||
|
||||
export async function detectLocalMindmapFiles(docIds: string[]): Promise<LocalMindmapFile[]> {
|
||||
const results: LocalMindmapFile[] = [];
|
||||
for (const id of docIds) {
|
||||
const folder = path.join(preferredBaseDir, id);
|
||||
try {
|
||||
const entries = await fs.readdir(folder);
|
||||
for (const name of entries) {
|
||||
if (name === "mindmap.json") {
|
||||
// mindmap.json 属于旧版单文件导图:mindmapId 必须包含 docId,避免不同页面间 ID 冲突
|
||||
results.push({
|
||||
documentId: id,
|
||||
mindmapId: `legacy-${id}`,
|
||||
fileName: name,
|
||||
source: "preferred",
|
||||
});
|
||||
continue;
|
||||
}
|
||||
const m = /^mindmap-(.+)\.json$/i.exec(name);
|
||||
if (m && m[1]) {
|
||||
results.push({ documentId: id, mindmapId: m[1], fileName: name, source: "preferred" });
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
|
||||
// 旧目录(仅支持 mindmap.json)
|
||||
const legacy = path.join(legacyBaseDir, id, "mindmap.json");
|
||||
if (await tryAccess(legacy)) {
|
||||
results.push({
|
||||
documentId: id,
|
||||
mindmapId: `legacy-${id}`,
|
||||
fileName: "mindmap.json",
|
||||
source: "legacy",
|
||||
});
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
@@ -28,12 +28,16 @@ const wrapCookies = (store: RequestCookies) => {
|
||||
getAll: (...args: Parameters<RequestCookies["getAll"]>) =>
|
||||
store.getAll(...args).map((cookie) => ({ ...cookie, value: decodeValue(cookie.value) })),
|
||||
set: (...args: Parameters<RequestCookies["set"]>) => {
|
||||
const [name, value, options] = args;
|
||||
if (typeof value === "string") {
|
||||
store.set(name, encodeValue(value), options);
|
||||
} else {
|
||||
store.set(name, value);
|
||||
const [name, value, options] = args as unknown as [
|
||||
unknown,
|
||||
unknown,
|
||||
unknown,
|
||||
];
|
||||
if (typeof name === "string" && typeof value === "string") {
|
||||
(store as any).set(name, encodeValue(value), options as any);
|
||||
return;
|
||||
}
|
||||
(store as any).set(...(args as any));
|
||||
},
|
||||
delete: (...args: Parameters<RequestCookies["delete"]>) => store.delete(...args),
|
||||
has: (...args: Parameters<RequestCookies["has"]>) => store.has(...args),
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import type { SupabaseClient } from "@supabase/supabase-js";
|
||||
import type { SupabaseClient } from "@supabase/supabase-js";
|
||||
import type { DocumentRecord, DocumentNode } from "@/lib/documents";
|
||||
import type { SidebarSectionId, TrashRecord } from "@/components/sidebar/types";
|
||||
import type { Database } from "@/types/supabase";
|
||||
@@ -36,7 +35,8 @@ export async function fetchSidebarDataset(
|
||||
throw new Error(`获取工作空间文档列表失败:${error.message}`);
|
||||
}
|
||||
|
||||
const documents: DocumentRecord[] = (documentRows ?? []).map((row) => ({
|
||||
const documentRowsAny = (documentRows ?? []) as any[];
|
||||
const documents: DocumentRecord[] = documentRowsAny.map((row) => ({
|
||||
access_scope: (row.access_scope ?? "private") as DocumentRecord["access_scope"],
|
||||
id: row.id,
|
||||
workspace_id: row.workspace_id,
|
||||
@@ -61,7 +61,8 @@ export async function fetchSidebarDataset(
|
||||
throw new Error(`获取垃圾桶内容失败:${trashError.message}`);
|
||||
}
|
||||
|
||||
const trashedDocuments: TrashRecord[] = (trashRows ?? []).map((row) => ({
|
||||
const trashRowsAny = (trashRows ?? []) as any[];
|
||||
const trashedDocuments: TrashRecord[] = trashRowsAny.map((row) => ({
|
||||
id: row.id,
|
||||
title: row.title,
|
||||
parent_id: row.parent_id,
|
||||
@@ -69,8 +70,7 @@ export async function fetchSidebarDataset(
|
||||
access_scope: (row.access_scope ?? "private") as DocumentRecord["access_scope"],
|
||||
}));
|
||||
|
||||
const mindmapDocs =
|
||||
documentRows?.filter((row) => row.mindmap_data != null).map((row) => row.id) ?? [];
|
||||
const mindmapDocs = documentRowsAny.filter((row) => row.mindmap_data != null).map((row) => row.id);
|
||||
|
||||
const { data: assetRows, error: assetError } = await client
|
||||
.from("media_assets")
|
||||
|
||||
@@ -4,13 +4,13 @@ import { getDecodedCookies } from "@/lib/server-cookies";
|
||||
export const createSupabaseServerClient = async () => {
|
||||
const cookieStore = await getDecodedCookies();
|
||||
return createServerComponentClient({
|
||||
cookies: () => cookieStore,
|
||||
});
|
||||
cookies: () => cookieStore as any,
|
||||
} as any);
|
||||
};
|
||||
|
||||
export const createSupabaseRouteClient = async () => {
|
||||
const cookieStore = await getDecodedCookies();
|
||||
return createRouteHandlerClient({
|
||||
cookies: () => cookieStore,
|
||||
});
|
||||
cookies: () => cookieStore as any,
|
||||
} as any);
|
||||
};
|
||||
|
||||
@@ -1,17 +1,24 @@
|
||||
import type { SupabaseClient } from "@supabase/supabase-js";
|
||||
import type { Database } from "@/types/supabase";
|
||||
|
||||
type TypedClient = SupabaseClient<Database>;
|
||||
type TypedClient = SupabaseClient<any>;
|
||||
|
||||
interface WorkspaceMembershipRow {
|
||||
workspace_id: string;
|
||||
is_default: boolean;
|
||||
workspaces: {
|
||||
id: string;
|
||||
name: string;
|
||||
type: "personal" | "team";
|
||||
icon_url: string | null;
|
||||
} | null;
|
||||
workspaces:
|
||||
| {
|
||||
id: string;
|
||||
name: string;
|
||||
type: "personal" | "team";
|
||||
icon_url: string | null;
|
||||
}
|
||||
| Array<{
|
||||
id: string;
|
||||
name: string;
|
||||
type: "personal" | "team";
|
||||
icon_url: string | null;
|
||||
}>
|
||||
| null;
|
||||
}
|
||||
|
||||
export interface WorkspaceSummary {
|
||||
@@ -80,7 +87,7 @@ export async function fetchWorkspaceSummaries(
|
||||
throw new Error(`拉取工作空间列表失败:${error.message}`);
|
||||
}
|
||||
|
||||
const rows: WorkspaceMembershipRow[] = memberRows ?? [];
|
||||
const rows: WorkspaceMembershipRow[] = (memberRows ?? []) as any;
|
||||
const workspaceIds = rows.map((row) => row.workspace_id);
|
||||
|
||||
const memberCountMap: Record<string, number> = {};
|
||||
@@ -102,15 +109,16 @@ export async function fetchWorkspaceSummaries(
|
||||
|
||||
const summaries: WorkspaceSummary[] = rows
|
||||
.map((row) => {
|
||||
if (!row.workspaces) {
|
||||
const workspace = Array.isArray(row.workspaces) ? row.workspaces[0] : row.workspaces;
|
||||
if (!workspace) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
id: row.workspaces.id,
|
||||
name: row.workspaces.name,
|
||||
type: row.workspaces.type,
|
||||
iconUrl: row.workspaces.icon_url,
|
||||
memberCount: memberCountMap[row.workspaces.id] ?? 1,
|
||||
id: workspace.id,
|
||||
name: workspace.name,
|
||||
type: workspace.type,
|
||||
iconUrl: workspace.icon_url,
|
||||
memberCount: memberCountMap[workspace.id] ?? 1,
|
||||
isDefault: row.is_default,
|
||||
} satisfies WorkspaceSummary;
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user