import { NoteMapLink, NoteMapPostResponse } from "@triliumnext/commons"; import server from "../../services/server"; import { NodeObject } from "force-graph"; type MapType = "tree" | "link"; interface GroupedLink { id: string; sourceNoteId: string; targetNoteId: string; names: string[]; } interface Node extends NodeObject { id: string; name: string; type: string; color: string; } export interface NotesAndRelationsData { nodes: Node[]; links: { id: string; source: string; target: string; name: string; }[]; noteIdToSizeMap: Record; } export async function loadNotesAndRelations(mapRootNoteId: string, excludeRelations: string[], includeRelations: string[], mapType: MapType): Promise { const resp = await server.post(`note-map/${mapRootNoteId}/${mapType}`, { excludeRelations, includeRelations }); const noteIdToSizeMap = calculateNodeSizes(resp, mapType); const links = getGroupedLinks(resp.links); const nodes = resp.notes.map(([noteId, title, type, color]) => ({ id: noteId, name: title, type: type, color: color })); return { noteIdToSizeMap, nodes, links: links.map((link) => ({ id: `${link.sourceNoteId}-${link.targetNoteId}`, source: link.sourceNoteId, target: link.targetNoteId, name: link.names.join(", ") })) }; } function calculateNodeSizes(resp: NoteMapPostResponse, mapType: MapType) { const noteIdToSizeMap: Record = {}; if (mapType === "tree") { const { noteIdToDescendantCountMap } = resp; for (const noteId in noteIdToDescendantCountMap) { noteIdToSizeMap[noteId] = 4; const count = noteIdToDescendantCountMap[noteId]; if (count > 0) { noteIdToSizeMap[noteId] += 1 + Math.round(Math.log(count) / Math.log(1.5)); } } } else if (mapType === "link") { const noteIdToLinkCount: Record = {}; for (const link of resp.links) { noteIdToLinkCount[link.targetNoteId] = 1 + (noteIdToLinkCount[link.targetNoteId] || 0); } for (const [noteId] of resp.notes) { noteIdToSizeMap[noteId] = 4; if (noteId in noteIdToLinkCount) { noteIdToSizeMap[noteId] += Math.min(Math.pow(noteIdToLinkCount[noteId], 0.5), 15); } } } return noteIdToSizeMap; } function getGroupedLinks(links: NoteMapLink[]): GroupedLink[] { const linksGroupedBySourceTarget: Record = {}; for (const link of links) { const key = `${link.sourceNoteId}-${link.targetNoteId}`; if (key in linksGroupedBySourceTarget) { if (!linksGroupedBySourceTarget[key].names.includes(link.name)) { linksGroupedBySourceTarget[key].names.push(link.name); } } else { linksGroupedBySourceTarget[key] = { id: key, sourceNoteId: link.sourceNoteId, targetNoteId: link.targetNoteId, names: [link.name] }; } } return Object.values(linksGroupedBySourceTarget); }