diff --git a/src/public/app/widgets/note_attributes.js b/src/public/app/widgets/note_attributes.js
index 328529bbd..5af12943f 100644
--- a/src/public/app/widgets/note_attributes.js
+++ b/src/public/app/widgets/note_attributes.js
@@ -7,6 +7,8 @@ import utils from "../services/utils.js";
import ws from "../services/ws.js";
import SpacedUpdate from "../services/spaced_update.js";
import attributesParser from "../services/attribute_parser.js";
+import linkService from "../services/link.js";
+import treeService from "../services/tree.js";
const mentionSetup = {
feeds: [
@@ -72,10 +74,41 @@ const mentionSetup = {
const TPL = `
`;
+const DISPLAYED_NOTES = 10;
+
export default class NoteAttributesWidget extends TabAwareWidget {
constructor() {
super();
@@ -90,6 +123,10 @@ export default class NoteAttributesWidget extends TabAwareWidget {
doRender() {
this.$widget = $(TPL);
this.$editor = this.$widget.find('.note-attributes-editor');
+ this.$attrExtras = this.$widget.find('.attr-extras');
+ this.$attrExtrasTitle = this.$widget.find('.attr-extras-title');
+ this.$attrExtrasList = this.$widget.find('.attr-extras-list');
+ this.$attrExtrasMoreNotes = this.$widget.find('.attr-extras-more-notes');
this.initialized = this.initEditor();
this.$editor.keypress(async e => {
@@ -114,18 +151,85 @@ export default class NoteAttributesWidget extends TabAwareWidget {
// display of $widget in both branches.
this.$widget.show();
- this.$editor.on("click", () => {
+ this.$editor.on("click", async e => {
const pos = this.textEditor.model.document.selection.getFirstPosition();
if (pos && pos.textNode && pos.textNode.data) {
- const attr = pos.textNode.data;
- const index = pos.offset - pos.textNode.startOffset;
+ const attrText = pos.textNode.data;
+ const clickIndex = pos.offset - pos.textNode.startOffset;
- const attrs = attributesParser.lexAndParse(attr, true);
+ const parsedAttrs = attributesParser.lexAndParse(attrText, true);
- console.log(attrs);
+ let matchedAttr = null;
+ let matchedPart = null;
- console.log(attr.substr(0, index) + '|' + attr.substr(index));
+ for (const attr of parsedAttrs) {
+ if (clickIndex >= attr.nameStartIndex && clickIndex <= attr.nameEndIndex) {
+ matchedAttr = attr;
+ matchedPart = 'name';
+ break;
+ }
+
+ if (clickIndex >= attr.valueStartIndex && clickIndex <= attr.valueEndIndex) {
+ matchedAttr = attr;
+ matchedPart = 'value';
+ break;
+ }
+ }
+
+ if (!matchedAttr) {
+ console.log(`Not found attribute for index ${clickIndex}, attr: ${JSON.stringify(parsedAttrs)}, text: ${attrText}`);
+
+ return;
+ }
+
+ let noteIds = await server.post('attributes/notes-with-attribute', {
+ type: matchedAttr.type,
+ name: matchedAttr.name,
+ value: matchedPart === 'value' ? matchedAttr.value : undefined
+ });
+
+ noteIds = noteIds.filter(noteId => noteId !== this.noteId);
+
+ if (noteIds.length === 0) {
+ this.$attrExtrasTitle.text(
+ `There are no other notes with ${matchedAttr.type} name "${matchedAttr.name}"`
+ // not displaying value since it can be long
+ + (matchedPart === 'value' ? " and matching value" : ""));
+ }
+ else {
+ this.$attrExtrasTitle.text(
+ `Notes with ${matchedAttr.type} name "${matchedAttr.name}"`
+ // not displaying value since it can be long
+ + (matchedPart === 'value' ? " and matching value" : "")
+ + ":"
+ );
+ }
+
+ this.$attrExtrasList.empty();
+
+ const displayedNoteIds = noteIds.length <= DISPLAYED_NOTES ? noteIds : noteIds.slice(0, DISPLAYED_NOTES);
+ const displayedNotes = await treeCache.getNotes(displayedNoteIds);
+console.log(displayedNoteIds, displayedNotes);
+ for (const note of displayedNotes) {
+ const notePath = treeService.getSomeNotePath(note);
+ const $noteLink = await linkService.createNoteLink(notePath, {showNotePath: true});
+
+ this.$attrExtrasList.append(
+ $("").append($noteLink)
+ );
+ }
+
+ if (noteIds.length > DISPLAYED_NOTES) {
+ this.$attrExtrasMoreNotes.show().text(`... and ${noteIds.length - DISPLAYED_NOTES} more.`);
+ }
+ else {
+ this.$attrExtrasMoreNotes.hide();
+ }
+
+ this.$attrExtras.css("left", e.pageX - this.$attrExtras.width() / 2);
+ this.$attrExtras.css("top", e.pageY + 20);
+ this.$attrExtras.show();
}
});
diff --git a/src/public/app/widgets/side_pane_toggles.js b/src/public/app/widgets/side_pane_toggles.js
index df443ae13..43ba442f2 100644
--- a/src/public/app/widgets/side_pane_toggles.js
+++ b/src/public/app/widgets/side_pane_toggles.js
@@ -9,14 +9,14 @@ const TPL = `
position: fixed;
bottom: 10px;
right: 10px;
- z-index: 1000;
+ z-index: 100;
}
.hide-left-pane-button, .show-left-pane-button {
position: fixed;
bottom: 10px;
left: 10px;
- z-index: 1000;
+ z-index: 100;
}
diff --git a/src/public/stylesheets/style.css b/src/public/stylesheets/style.css
index 6c1dbed32..d1a4a20af 100644
--- a/src/public/stylesheets/style.css
+++ b/src/public/stylesheets/style.css
@@ -730,7 +730,7 @@ body {
#context-menu-container, #context-menu-container .dropdown-menu {
padding: 3px 0 0;
- z-index: 1111;
+ z-index: 1000;
}
#context-menu-container .dropdown-item {
diff --git a/src/routes/api/attributes.js b/src/routes/api/attributes.js
index 4f6baa4b6..c5988d210 100644
--- a/src/routes/api/attributes.js
+++ b/src/routes/api/attributes.js
@@ -258,6 +258,14 @@ async function deleteRelation(req) {
}
}
+async function getNotesWithAttribute(req) {
+ const {type, name, value} = req.body;
+
+ const notes = await attributeService.getNotesWithAttribute(type, name, value);
+
+ return notes.map(note => note.noteId);
+}
+
module.exports = {
updateNoteAttributes,
updateNoteAttributes2,
@@ -267,5 +275,6 @@ module.exports = {
getValuesForAttribute,
getEffectiveNoteAttributes,
createRelation,
- deleteRelation
+ deleteRelation,
+ getNotesWithAttribute
};
diff --git a/src/routes/routes.js b/src/routes/routes.js
index 7f202f1dc..3748b01e2 100644
--- a/src/routes/routes.js
+++ b/src/routes/routes.js
@@ -177,6 +177,7 @@ function register(app) {
apiRoute(DELETE, '/api/notes/:noteId/attributes/:attributeId', attributesRoute.deleteNoteAttribute);
apiRoute(GET, '/api/attributes/names', attributesRoute.getAttributeNames);
apiRoute(GET, '/api/attributes/values/:attributeName', attributesRoute.getValuesForAttribute);
+ apiRoute(POST, '/api/attributes/notes-with-attribute', attributesRoute.getNotesWithAttribute);
apiRoute(POST, '/api/notes/:noteId/link-map', linkMapRoute.getLinkMap);
diff --git a/src/services/attributes.js b/src/services/attributes.js
index 571f9e332..0f8fcd783 100644
--- a/src/services/attributes.js
+++ b/src/services/attributes.js
@@ -41,6 +41,27 @@ const BUILTIN_ATTRIBUTES = [
{ type: 'relation', name: 'renderNote', isDangerous: true }
];
+async function getNotesWithAttribute(type, name, value) {
+ let valueCondition = "";
+ let params = [type, name];
+
+ if (value !== undefined) {
+ valueCondition = " AND attributes.value = ?";
+ params.push(value);
+ }
+
+ return await repository.getEntities(`
+ SELECT notes.*
+ FROM notes
+ JOIN attributes USING (noteId)
+ WHERE notes.isDeleted = 0
+ AND attributes.isDeleted = 0
+ AND attributes.type = ?
+ AND attributes.name = ?
+ ${valueCondition}
+ ORDER BY position`, params);
+}
+
async function getNotesWithLabel(name, value) {
let valueCondition = "";
let params = [name];
@@ -134,6 +155,7 @@ function getBuiltinAttributeNames() {
}
module.exports = {
+ getNotesWithAttribute,
getNotesWithLabel,
getNotesWithLabels,
getNoteWithLabel,