From 4a5a63d7b418e4ab9a7ec4d254c41c63cdb787be Mon Sep 17 00:00:00 2001 From: azivner Date: Thu, 25 Oct 2018 12:06:36 +0200 Subject: [PATCH] relation map WIP --- .idea/encodings.xml | 4 ++ .../services/note_detail_relation_map.js | 62 ++++++++++++++----- src/public/stylesheets/relation-map.css | 6 +- src/routes/api/notes.js | 32 +++++++++- src/routes/routes.js | 1 + 5 files changed, 88 insertions(+), 17 deletions(-) create mode 100644 .idea/encodings.xml diff --git a/.idea/encodings.xml b/.idea/encodings.xml new file mode 100644 index 000000000..15a15b218 --- /dev/null +++ b/.idea/encodings.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/public/javascripts/services/note_detail_relation_map.js b/src/public/javascripts/services/note_detail_relation_map.js index 984ee6610..de4308cab 100644 --- a/src/public/javascripts/services/note_detail_relation_map.js +++ b/src/public/javascripts/services/note_detail_relation_map.js @@ -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({ Endpoint: ["Dot", {radius: 2}], Connector: "StateMachine", 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" }); - 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) { // const connection = info.connection; @@ -153,6 +174,9 @@ async function show() { $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 () { const maxY = mapData.notes.filter(note => !!note.y).map(note => note.y).reduce((a, b) => Math.max(a, b), 0); let curX = 100; @@ -179,12 +203,18 @@ async function show() { } 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; connection.getOverlay("label").setLabel(relation.name); connection.canvas.setAttribute("data-connection-id", connection.id); + + //instance.recalculateOffsets(connection); } initDone = true; @@ -200,8 +230,6 @@ async function show() { } function saveData() { - const currentNote = noteDetailService.getCurrentNote(); - noteDetailService.saveNote(); } @@ -296,9 +324,13 @@ $addChildNotesButton.click(async () => { let curY = maxY + 200; for (const child of children) { + if (mapData.notes.some(note => note.id === child.noteId)) { + // note already exists + continue; + } + const note = { id: child.noteId, - title: child.title, width: "auto", height: "auto" }; diff --git a/src/public/stylesheets/relation-map.css b/src/public/stylesheets/relation-map.css index 7598723fb..72d006cda 100644 --- a/src/public/stylesheets/relation-map.css +++ b/src/public/stylesheets/relation-map.css @@ -1,7 +1,11 @@ -#relation-map-canvas { +#note-detail-relation-map { height: 500px; } +#relation-map-canvas { + position: absolute; /* needs to be absolute otherwise connections will be misplaced */ +} + .note-box { padding: 16px; position: absolute !important; diff --git a/src/routes/api/notes.js b/src/routes/api/notes.js index 12dd8e919..4f526897f 100644 --- a/src/routes/api/notes.js +++ b/src/routes/api/notes.js @@ -92,6 +92,35 @@ async function setNoteTypeMime(req) { 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 = { getNote, updateNote, @@ -99,5 +128,6 @@ module.exports = { sortNotes, protectSubtree, setNoteTypeMime, - getChildren + getChildren, + getRelationMap }; \ No newline at end of file diff --git a/src/routes/routes.js b/src/routes/routes.js index 0c887ac75..4625a3c09 100644 --- a/src/routes/routes.js +++ b/src/routes/routes.js @@ -121,6 +121,7 @@ function register(app) { apiRoute(PUT, '/api/notes/:noteId/protect/:isProtected', notesApiRoute.protectSubtree); apiRoute(PUT, /\/api\/notes\/(.*)\/type\/(.*)\/mime\/(.*)/, notesApiRoute.setNoteTypeMime); 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-after/:afterBranchId', cloningApiRoute.cloneNoteAfter);