display mirror relations correctly on relation map

This commit is contained in:
azivner
2018-11-13 12:50:08 +01:00
parent 144e814b02
commit 0b85f87cb2
36 changed files with 843 additions and 115 deletions

View File

@@ -7,7 +7,9 @@ const repository = require('../services/repository');
const dateUtils = require('../services/date_utils');
const LABEL = 'label';
const LABEL_DEFINITION = 'label-definition';
const RELATION = 'relation';
const RELATION_DEFINITION = 'relation-definition';
/**
* This represents a Note which is a central object in the Trilium Notes project.
@@ -136,6 +138,14 @@ class Note extends Entity {
return (await this.getAttributes(name)).filter(attr => attr.type === LABEL);
}
/**
* @param {string} [name] - label name to filter
* @returns {Promise<Attribute[]>} all note's label definitions, including inherited ones
*/
async getLabelDefinitions(name) {
return (await this.getAttributes(name)).filter(attr => attr.type === LABEL_DEFINITION);
}
/**
* @param {string} [name] - relation name to filter
* @returns {Promise<Attribute[]>} all note's relations (attributes with type relation), including inherited ones
@@ -144,6 +154,14 @@ class Note extends Entity {
return (await this.getAttributes(name)).filter(attr => attr.type === RELATION);
}
/**
* @param {string} [name] - relation name to filter
* @returns {Promise<Attribute[]>} all note's relation definitions including inherited ones
*/
async getRelationDefinitions(name) {
return (await this.getAttributes(name)).filter(attr => attr.type === RELATION_DEFINITION);
}
/**
* Clear note's attributes cache to force fresh reload for next attribute request.
* Cache is note instance scoped.

View File

@@ -24,7 +24,7 @@ function initAttributeNameAutocomplete({ $el, attributeType, open }) {
});
if (result.length === 0) {
result.push({name: "No results"})
result.push({name: "No results"});
}
cb(result);

View File

@@ -48,6 +48,25 @@ const biDirectionalOverlays = [
} ]
];
const mirrorOverlays = [
[ "Arrow", {
location: 1,
id: "arrow",
length: 14,
foldback: 0.8
} ],
[ "Label", { label: "", location: 0.2, id: "label-source", cssClass: "connection-label" }],
[ "Label", { label: "", location: 0.8, id: "label-target", cssClass: "connection-label" }],
[ "Arrow", {
location: 0,
id: "arrow2",
length: 14,
direction: -1,
foldback: 0.8
} ]
];
function loadMapData() {
const currentNote = noteDetailService.getCurrentNote();
mapData = {
@@ -88,12 +107,12 @@ async function loadNotesAndRelations() {
for (const relation of data.relations) {
const match = relations.find(rel =>
rel.name === relation.name
rel.name === data.mirrorRelations[relation.name]
&& ((rel.sourceNoteId === relation.sourceNoteId && rel.targetNoteId === relation.targetNoteId)
|| (rel.sourceNoteId === relation.targetNoteId && rel.targetNoteId === relation.sourceNoteId)));
if (match) {
match.type = relation.type = 'biDirectional';
match.type = relation.type = relation.name === data.mirrorRelations[relation.name] ? 'biDirectional' : 'mirror';
relation.render = false; // don't render second relation
} else {
relation.type = 'uniDirectional';
@@ -128,7 +147,15 @@ async function loadNotesAndRelations() {
});
connection.id = relation.attributeId;
connection.getOverlay("label").setLabel(relation.name);
if (relation.type === 'mirror') {
connection.getOverlay("label-source").setLabel(relation.name);
connection.getOverlay("label-target").setLabel(data.mirrorRelations[relation.name]);
}
else {
connection.getOverlay("label").setLabel(relation.name);
}
connection.canvas.setAttribute("data-connection-id", connection.id);
}
});
@@ -222,6 +249,8 @@ function initJsPlumbInstance () {
jsPlumbInstance.registerConnectionType("biDirectional", { anchor:"Continuous", connector:"StateMachine", overlays: biDirectionalOverlays });
jsPlumbInstance.registerConnectionType("mirror", { anchor:"Continuous", connector:"StateMachine", overlays: mirrorOverlays });
jsPlumbInstance.bind("connection", connectionCreatedHandler);
// so that canvas is not panned when clicking/dragging note box
@@ -330,7 +359,7 @@ async function noteContextMenuHandler(event, cmd) {
}
else if (cmd === "edit-title") {
const $title = $noteBox.find(".title a");
const title = await promptDialog.ask("Enter new note title:", $title.text());
const title = await promptDialog.ask({ message: "Enter new note title:", defaultValue: $title.text() });
if (!title) {
return;

View File

@@ -95,8 +95,11 @@ async function setNoteTypeMime(req) {
async function getRelationMap(req) {
const noteIds = req.body.noteIds;
const resp = {
// noteId => title
noteTitles: {},
relations: []
relations: [],
// relation name => mirror relation name
mirrorRelations: {}
};
if (noteIds.length === 0) {
@@ -105,19 +108,26 @@ async function getRelationMap(req) {
const questionMarks = noteIds.map(noteId => '?').join(',');
(await repository.getEntities(`SELECT * FROM notes WHERE isDeleted = 0 AND noteId IN (${questionMarks})`, noteIds))
.forEach(note => resp.noteTitles[note.noteId] = note.title);
const notes = await repository.getEntities(`SELECT * FROM notes WHERE isDeleted = 0 AND noteId IN (${questionMarks})`, noteIds);
// 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 isDeleted = 0 AND type = 'relation' AND noteId IN (${questionMarks})`, noteIds))
.map(relation => { return {
attributeId: relation.attributeId,
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));
for (const note of notes) {
resp.noteTitles[note.noteId] = note.title;
resp.relations = resp.relations.concat((await note.getRelations())
.filter(relation => noteIds.includes(relation.value))
.map(relation => { return {
attributeId: relation.attributeId,
sourceNoteId: relation.noteId,
targetNoteId: relation.value,
name: relation.name
}; }));
for (const relationDefinition of await note.getRelationDefinitions()) {
if (relationDefinition.value.mirrorRelation) {
resp.mirrorRelations[relationDefinition.name] = relationDefinition.value.mirrorRelation;
}
}
}
return resp;
}