mirror of
				https://github.com/zadam/trilium.git
				synced 2025-10-31 18:36:30 +01:00 
			
		
		
		
	relation map WIP
This commit is contained in:
		
							
								
								
									
										4
									
								
								.idea/encodings.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								.idea/encodings.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,4 @@ | |||||||
|  | <?xml version="1.0" encoding="UTF-8"?> | ||||||
|  | <project version="4"> | ||||||
|  |   <component name="Encoding" addBOMForNewFiles="with NO BOM" /> | ||||||
|  | </project> | ||||||
| @@ -30,24 +30,45 @@ async function show() { | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     jsPlumb.ready(function () { |     jsPlumb.ready(async function () { | ||||||
|  |         const uniDirectionalConnection = [ | ||||||
|  |             [ "Arrow", { | ||||||
|  |                 location: 1, | ||||||
|  |                 id: "arrow", | ||||||
|  |                 length: 14, | ||||||
|  |                 foldback: 0.8 | ||||||
|  |             } ], | ||||||
|  |             [ "Label", { label: "", id: "label", cssClass: "aLabel" }] | ||||||
|  |         ]; | ||||||
|  |  | ||||||
|  |         const biDirectionalConnection = [ | ||||||
|  |             [ "Arrow", { | ||||||
|  |                 location: 1, | ||||||
|  |                 id: "arrow", | ||||||
|  |                 length: 14, | ||||||
|  |                 foldback: 0.8 | ||||||
|  |             } ], | ||||||
|  |             [ "Label", { label: "", id: "label", cssClass: "aLabel" }], | ||||||
|  |             [ "Arrow", { | ||||||
|  |                 location: 0, | ||||||
|  |                 id: "arrow2", | ||||||
|  |                 length: 14, | ||||||
|  |                 direction: -1, | ||||||
|  |                 foldback: 0.8 | ||||||
|  |             } ] | ||||||
|  |         ]; | ||||||
|  |  | ||||||
|         instance = jsPlumb.getInstance({ |         instance = jsPlumb.getInstance({ | ||||||
|             Endpoint: ["Dot", {radius: 2}], |             Endpoint: ["Dot", {radius: 2}], | ||||||
|             Connector: "StateMachine", |             Connector: "StateMachine", | ||||||
|             HoverPaintStyle: {stroke: "#1e8151", strokeWidth: 2 }, |             HoverPaintStyle: {stroke: "#1e8151", strokeWidth: 2 }, | ||||||
|             ConnectionOverlays: [ |  | ||||||
|                 [ "Arrow", { |  | ||||||
|                     location: 1, |  | ||||||
|                     id: "arrow", |  | ||||||
|                     length: 14, |  | ||||||
|                     foldback: 0.8 |  | ||||||
|                 } ], |  | ||||||
|                 [ "Label", { label: "", id: "label", cssClass: "aLabel" }] |  | ||||||
|             ], |  | ||||||
|             Container: "relation-map-canvas" |             Container: "relation-map-canvas" | ||||||
|         }); |         }); | ||||||
|  |  | ||||||
|         instance.registerConnectionType("basic", { anchor:"Continuous", connector:"StateMachine" }); |  | ||||||
|  |         instance.registerConnectionType("uniDirectional", { anchor:"Continuous", connector:"StateMachine", overlays: uniDirectionalConnection }); | ||||||
|  |  | ||||||
|  |         instance.registerConnectionType("biDirectional", { anchor:"Continuous", connector:"StateMachine", overlays: biDirectionalConnection }); | ||||||
|  |  | ||||||
|         // instance.bind("connection", function (info) { |         // instance.bind("connection", function (info) { | ||||||
|         //     const connection = info.connection; |         //     const connection = info.connection; | ||||||
| @@ -153,6 +174,9 @@ async function show() { | |||||||
|             $relationMapCanvas.contextmenuRelation("open", e, { connection: c }); |             $relationMapCanvas.contextmenuRelation("open", e, { connection: c }); | ||||||
|         }); |         }); | ||||||
|  |  | ||||||
|  |         const noteIds = mapData.notes.map(note => note.noteId); | ||||||
|  |         const data = await server.post("notes/relation-map", { noteIds }); | ||||||
|  |  | ||||||
|         instance.batch(function () { |         instance.batch(function () { | ||||||
|             const maxY = mapData.notes.filter(note => !!note.y).map(note => note.y).reduce((a, b) => Math.max(a, b), 0); |             const maxY = mapData.notes.filter(note => !!note.y).map(note => note.y).reduce((a, b) => Math.max(a, b), 0); | ||||||
|             let curX = 100; |             let curX = 100; | ||||||
| @@ -179,12 +203,18 @@ async function show() { | |||||||
|             } |             } | ||||||
|  |  | ||||||
|             for (const relation of mapData.relations) { |             for (const relation of mapData.relations) { | ||||||
|                 const connection = instance.connect({ id: relation.id, source: relation.source, target: relation.target, type: "basic" }); |                 if (relation.name === 'isChildOf') { | ||||||
|  |                     continue; | ||||||
|  |                 } | ||||||
|  |  | ||||||
|  |                 const connection = instance.connect({ id: relation.id, source: relation.source, target: relation.target, type: "uniDirectional" }); | ||||||
|  |  | ||||||
|                 relation.connectionId = connection.id; |                 relation.connectionId = connection.id; | ||||||
|  |  | ||||||
|                 connection.getOverlay("label").setLabel(relation.name); |                 connection.getOverlay("label").setLabel(relation.name); | ||||||
|                 connection.canvas.setAttribute("data-connection-id", connection.id); |                 connection.canvas.setAttribute("data-connection-id", connection.id); | ||||||
|  |  | ||||||
|  |                 //instance.recalculateOffsets(connection); | ||||||
|             } |             } | ||||||
|  |  | ||||||
|             initDone = true; |             initDone = true; | ||||||
| @@ -200,8 +230,6 @@ async function show() { | |||||||
| } | } | ||||||
|  |  | ||||||
| function saveData() { | function saveData() { | ||||||
|     const currentNote = noteDetailService.getCurrentNote(); |  | ||||||
|  |  | ||||||
|     noteDetailService.saveNote(); |     noteDetailService.saveNote(); | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -296,9 +324,13 @@ $addChildNotesButton.click(async () => { | |||||||
|     let curY = maxY + 200; |     let curY = maxY + 200; | ||||||
|  |  | ||||||
|     for (const child of children) { |     for (const child of children) { | ||||||
|  |         if (mapData.notes.some(note => note.id === child.noteId)) { | ||||||
|  |             // note already exists | ||||||
|  |             continue; | ||||||
|  |         } | ||||||
|  |  | ||||||
|         const note = { |         const note = { | ||||||
|             id: child.noteId, |             id: child.noteId, | ||||||
|             title: child.title, |  | ||||||
|             width: "auto", |             width: "auto", | ||||||
|             height: "auto" |             height: "auto" | ||||||
|         }; |         }; | ||||||
|   | |||||||
| @@ -1,7 +1,11 @@ | |||||||
| #relation-map-canvas { | #note-detail-relation-map { | ||||||
|     height: 500px; |     height: 500px; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | #relation-map-canvas { | ||||||
|  |     position: absolute; /* needs to be absolute otherwise connections will be misplaced */ | ||||||
|  | } | ||||||
|  |  | ||||||
| .note-box { | .note-box { | ||||||
|     padding: 16px; |     padding: 16px; | ||||||
|     position: absolute !important; |     position: absolute !important; | ||||||
|   | |||||||
| @@ -92,6 +92,35 @@ async function setNoteTypeMime(req) { | |||||||
|     await note.save(); |     await note.save(); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | async function getRelationMap(req) { | ||||||
|  |     const noteIds = req.body.noteIds; | ||||||
|  |     const resp = { | ||||||
|  |         noteTitles: {}, | ||||||
|  |         relations: [] | ||||||
|  |     }; | ||||||
|  |  | ||||||
|  |     if (noteIds.length === 0) { | ||||||
|  |         return resp; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     const questionMarks = noteIds.map(noteId => '?').join(','); | ||||||
|  |  | ||||||
|  |     (await repository.getEntities(`SELECT * FROM notes WHERE noteId IN (${questionMarks})`, noteIds)) | ||||||
|  |         .forEach(note => resp.noteTitles[note.noteId] = note.title); | ||||||
|  |  | ||||||
|  |     // FIXME: this actually doesn't take into account inherited relations! But maybe it is better this way? | ||||||
|  |     resp.relations = (await repository.getEntities(`SELECT * FROM attributes WHERE type = 'relation' AND noteId IN (${questionMarks})`, noteIds)) | ||||||
|  |         .map(relation => { return { | ||||||
|  |             sourceNoteId: relation.noteId, | ||||||
|  |             targetNoteId: relation.value, | ||||||
|  |             name: relation.name | ||||||
|  |         }; }) | ||||||
|  |         // both sourceNoteId and targetNoteId has to be in the included notes, but source was already checked in the SQL query | ||||||
|  |         .filter(relation => noteIds.includes(relation.targetNoteId)); | ||||||
|  |  | ||||||
|  |     return resp; | ||||||
|  | } | ||||||
|  |  | ||||||
| module.exports = { | module.exports = { | ||||||
|     getNote, |     getNote, | ||||||
|     updateNote, |     updateNote, | ||||||
| @@ -99,5 +128,6 @@ module.exports = { | |||||||
|     sortNotes, |     sortNotes, | ||||||
|     protectSubtree, |     protectSubtree, | ||||||
|     setNoteTypeMime, |     setNoteTypeMime, | ||||||
|     getChildren |     getChildren, | ||||||
|  |     getRelationMap | ||||||
| }; | }; | ||||||
| @@ -121,6 +121,7 @@ function register(app) { | |||||||
|     apiRoute(PUT, '/api/notes/:noteId/protect/:isProtected', notesApiRoute.protectSubtree); |     apiRoute(PUT, '/api/notes/:noteId/protect/:isProtected', notesApiRoute.protectSubtree); | ||||||
|     apiRoute(PUT, /\/api\/notes\/(.*)\/type\/(.*)\/mime\/(.*)/, notesApiRoute.setNoteTypeMime); |     apiRoute(PUT, /\/api\/notes\/(.*)\/type\/(.*)\/mime\/(.*)/, notesApiRoute.setNoteTypeMime); | ||||||
|     apiRoute(GET, '/api/notes/:noteId/revisions', noteRevisionsApiRoute.getNoteRevisions); |     apiRoute(GET, '/api/notes/:noteId/revisions', noteRevisionsApiRoute.getNoteRevisions); | ||||||
|  |     apiRoute(POST, '/api/notes/relation-map', notesApiRoute.getRelationMap); | ||||||
|  |  | ||||||
|     apiRoute(PUT, '/api/notes/:noteId/clone-to/:parentNoteId', cloningApiRoute.cloneNoteToParent); |     apiRoute(PUT, '/api/notes/:noteId/clone-to/:parentNoteId', cloningApiRoute.cloneNoteToParent); | ||||||
|     apiRoute(PUT, '/api/notes/:noteId/clone-after/:afterBranchId', cloningApiRoute.cloneNoteAfter); |     apiRoute(PUT, '/api/notes/:noteId/clone-after/:afterBranchId', cloningApiRoute.cloneNoteAfter); | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user