diff --git a/package.json b/package.json index 2574d07ea..96166c475 100644 --- a/package.json +++ b/package.json @@ -81,7 +81,7 @@ "electron-packager": "15.0.0", "electron-rebuild": "1.11.0", "esm": "3.2.25", - "jasmine": "3.6.0", + "jasmine": "3.6.1", "jsdoc": "3.6.5", "lorem-ipsum": "2.0.3", "webpack": "5.0.0-beta.22", diff --git a/src/entities/attribute.js b/src/entities/attribute.js index 0da8f511a..ad7a0f1df 100644 --- a/src/entities/attribute.js +++ b/src/entities/attribute.js @@ -41,14 +41,14 @@ class Attribute extends Entity { } /** - * @returns {Promise} + * @returns {Note|null} */ getNote() { return this.repository.getNote(this.noteId); } /** - * @returns {Promise} + * @returns {Note|null} */ getTargetNote() { if (this.type !== 'relation') { diff --git a/src/entities/branch.js b/src/entities/branch.js index b8651f989..5717c1b1e 100644 --- a/src/entities/branch.js +++ b/src/entities/branch.js @@ -27,12 +27,12 @@ class Branch extends Entity { // notePosition is not part of hash because it would produce a lot of updates in case of reordering static get hashedProperties() { return ["branchId", "noteId", "parentNoteId", "isDeleted", "deleteId", "prefix"]; } - /** @returns {Promise} */ + /** @returns {Note|null} */ getNote() { return this.repository.getNote(this.noteId); } - /** @returns {Promise} */ + /** @returns {Note|null} */ getParentNote() { return this.repository.getNote(this.parentNoteId); } diff --git a/src/entities/note.js b/src/entities/note.js index 6c759691a..21b7ecab7 100644 --- a/src/entities/note.js +++ b/src/entities/note.js @@ -70,7 +70,7 @@ class Note extends Entity { * - but to the user note content and title changes are one and the same - single dateModified (so all changes must go through Note and content is not a separate entity) */ - /** @returns {Promise<*>} */ + /** @returns {*} */ getContent(silentNotFoundError = false) { if (this.content === undefined) { const res = sql.getRow(`SELECT content, hash FROM note_contents WHERE noteId = ?`, [this.noteId]); @@ -106,7 +106,7 @@ class Note extends Entity { } } - /** @returns {Promise<*>} */ + /** @returns {*} */ getJsonContent() { const content = this.getContent(); @@ -157,7 +157,6 @@ class Note extends Entity { syncTableService.addNoteContentSync(this.noteId); } - /** @returns {Promise} */ setJsonContent(content) { this.setContent(JSON.stringify(content, null, '\t')); } @@ -218,7 +217,7 @@ class Note extends Entity { * * @param {string} [type] - (optional) attribute type to filter * @param {string} [name] - (optional) attribute name to filter - * @returns {Promise} note's "owned" attributes - excluding inherited ones + * @returns {Attribute[]} note's "owned" attributes - excluding inherited ones */ getOwnedAttributes(type, name) { if (!this.__ownedAttributeCache) { @@ -240,7 +239,7 @@ class Note extends Entity { } /** - * @returns {Promise} attribute belonging to this specific note (excludes inherited attributes) + * @returns {Attribute} attribute belonging to this specific note (excludes inherited attributes) * * This method can be significantly faster than the getAttribute() */ @@ -251,7 +250,7 @@ class Note extends Entity { } /** - * @returns {Promise} relations targetting this specific note + * @returns {Attribute[]} relations targetting this specific note */ getTargetRelations() { return this.repository.getEntities("SELECT * FROM attributes WHERE type = 'relation' AND isDeleted = 0 AND value = ?", [this.noteId]); @@ -260,7 +259,7 @@ class Note extends Entity { /** * @param {string} [type] - (optional) attribute type to filter * @param {string} [name] - (optional) attribute name to filter - * @returns {Promise} all note's attributes, including inherited ones + * @returns {Attribute[]} all note's attributes, including inherited ones */ getAttributes(type, name) { if (!this.__attributeCache) { @@ -283,7 +282,7 @@ class Note extends Entity { /** * @param {string} [name] - label name to filter - * @returns {Promise} all note's labels (attributes with type label), including inherited ones + * @returns {Attribute[]} all note's labels (attributes with type label), including inherited ones */ getLabels(name) { return this.getAttributes(LABEL, name); @@ -291,7 +290,7 @@ class Note extends Entity { /** * @param {string} [name] - label name to filter - * @returns {Promise} all note's labels (attributes with type label), excluding inherited ones + * @returns {Attribute[]} all note's labels (attributes with type label), excluding inherited ones */ getOwnedLabels(name) { return this.getOwnedAttributes(LABEL, name); @@ -299,7 +298,7 @@ class Note extends Entity { /** * @param {string} [name] - label name to filter - * @returns {Promise} all note's label definitions, including inherited ones + * @returns {Attribute[]} all note's label definitions, including inherited ones */ getLabelDefinitions(name) { return this.getAttributes(LABEL_DEFINITION, name); @@ -307,7 +306,7 @@ class Note extends Entity { /** * @param {string} [name] - relation name to filter - * @returns {Promise} all note's relations (attributes with type relation), including inherited ones + * @returns {Attribute[]} all note's relations (attributes with type relation), including inherited ones */ getRelations(name) { return this.getAttributes(RELATION, name); @@ -315,7 +314,7 @@ class Note extends Entity { /** * @param {string} [name] - relation name to filter - * @returns {Promise} all note's relations (attributes with type relation), excluding inherited ones + * @returns {Attribute[]} all note's relations (attributes with type relation), excluding inherited ones */ getOwnedRelations(name) { return this.getOwnedAttributes(RELATION, name); @@ -323,7 +322,7 @@ class Note extends Entity { /** * @param {string} [name] - relation name to filter - * @returns {Promise} + * @returns {Note[]} */ getRelationTargets(name) { const relations = this.getRelations(name); @@ -338,7 +337,7 @@ class Note extends Entity { /** * @param {string} [name] - relation name to filter - * @returns {Promise} all note's relation definitions including inherited ones + * @returns {Attribute[]} all note's relation definitions including inherited ones */ getRelationDefinitions(name) { return this.getAttributes(RELATION_DEFINITION, name); @@ -353,7 +352,6 @@ class Note extends Entity { this.__ownedAttributeCache = null; } - /** @returns {Promise} */ loadAttributesToCache() { const attributes = this.repository.getEntities(` WITH RECURSIVE @@ -420,7 +418,7 @@ class Note extends Entity { /** * @param {string} type - attribute type (label, relation, etc.) * @param {string} name - attribute name - * @returns {Promise} true if note has an attribute with given type and name (including inherited) + * @returns {boolean} true if note has an attribute with given type and name (including inherited) */ hasAttribute(type, name) { return !!this.getAttribute(type, name); @@ -429,7 +427,7 @@ class Note extends Entity { /** * @param {string} type - attribute type (label, relation, etc.) * @param {string} name - attribute name - * @returns {Promise} true if note has an attribute with given type and name (excluding inherited) + * @returns {boolean} true if note has an attribute with given type and name (excluding inherited) */ hasOwnedAttribute(type, name) { return !!this.getOwnedAttribute(type, name); @@ -438,7 +436,7 @@ class Note extends Entity { /** * @param {string} type - attribute type (label, relation, etc.) * @param {string} name - attribute name - * @returns {Promise} attribute of given type and name. If there's more such attributes, first is returned. Returns null if there's no such attribute belonging to this note. + * @returns {Attribute} attribute of given type and name. If there's more such attributes, first is returned. Returns null if there's no such attribute belonging to this note. */ getAttribute(type, name) { const attributes = this.getAttributes(); @@ -449,7 +447,7 @@ class Note extends Entity { /** * @param {string} type - attribute type (label, relation, etc.) * @param {string} name - attribute name - * @returns {Promise} attribute value of given type and name or null if no such attribute exists. + * @returns {string|null} attribute value of given type and name or null if no such attribute exists. */ getAttributeValue(type, name) { const attr = this.getAttribute(type, name); @@ -460,7 +458,7 @@ class Note extends Entity { /** * @param {string} type - attribute type (label, relation, etc.) * @param {string} name - attribute name - * @returns {Promise} attribute value of given type and name or null if no such attribute exists. + * @returns {string|null} attribute value of given type and name or null if no such attribute exists. */ getOwnedAttributeValue(type, name) { const attr = this.getOwnedAttribute(type, name); @@ -475,7 +473,6 @@ class Note extends Entity { * @param {boolean} enabled - toggle On or Off * @param {string} name - attribute name * @param {string} [value] - attribute value (optional) - * @returns {Promise} */ toggleAttribute(type, enabled, name, value) { if (enabled) { @@ -492,7 +489,6 @@ class Note extends Entity { * @param {string} type - attribute type (label, relation, etc.) * @param {string} name - attribute name * @param {string} [value] - attribute value (optional) - * @returns {Promise} */ setAttribute(type, name, value) { const attributes = this.loadOwnedAttributesToCache(); @@ -526,7 +522,6 @@ class Note extends Entity { * @param {string} type - attribute type (label, relation, etc.) * @param {string} name - attribute name * @param {string} [value] - attribute value (optional) - * @returns {Promise} */ removeAttribute(type, name, value) { const attributes = this.loadOwnedAttributesToCache(); @@ -542,7 +537,7 @@ class Note extends Entity { } /** - * @return {Promise} + * @return {Attribute} */ addAttribute(type, name, value = "", isInheritable = false, position = 1000) { const attr = new Attribute({ @@ -571,79 +566,79 @@ class Note extends Entity { /** * @param {string} name - label name - * @returns {Promise} true if label exists (including inherited) + * @returns {boolean} true if label exists (including inherited) */ hasLabel(name) { return this.hasAttribute(LABEL, name); } /** * @param {string} name - label name - * @returns {Promise} true if label exists (excluding inherited) + * @returns {boolean} true if label exists (excluding inherited) */ hasOwnedLabel(name) { return this.hasOwnedAttribute(LABEL, name); } /** * @param {string} name - relation name - * @returns {Promise} true if relation exists (including inherited) + * @returns {boolean} true if relation exists (including inherited) */ hasRelation(name) { return this.hasAttribute(RELATION, name); } /** * @param {string} name - relation name - * @returns {Promise} true if relation exists (excluding inherited) + * @returns {boolean} true if relation exists (excluding inherited) */ hasOwnedRelation(name) { return this.hasOwnedAttribute(RELATION, name); } /** * @param {string} name - label name - * @returns {Promise} label if it exists, null otherwise + * @returns {Attribute|null} label if it exists, null otherwise */ getLabel(name) { return this.getAttribute(LABEL, name); } /** * @param {string} name - label name - * @returns {Promise} label if it exists, null otherwise + * @returns {Attribute|null} label if it exists, null otherwise */ getOwnedLabel(name) { return this.getOwnedAttribute(LABEL, name); } /** * @param {string} name - relation name - * @returns {Promise} relation if it exists, null otherwise + * @returns {Attribute|null} relation if it exists, null otherwise */ getRelation(name) { return this.getAttribute(RELATION, name); } /** * @param {string} name - relation name - * @returns {Promise} relation if it exists, null otherwise + * @returns {Attribute|null} relation if it exists, null otherwise */ getOwnedRelation(name) { return this.getOwnedAttribute(RELATION, name); } /** * @param {string} name - label name - * @returns {Promise} label value if label exists, null otherwise + * @returns {string|null} label value if label exists, null otherwise */ getLabelValue(name) { return this.getAttributeValue(LABEL, name); } /** * @param {string} name - label name - * @returns {Promise} label value if label exists, null otherwise + * @returns {string|null} label value if label exists, null otherwise */ getOwnedLabelValue(name) { return this.getOwnedAttributeValue(LABEL, name); } /** * @param {string} name - relation name - * @returns {Promise} relation value if relation exists, null otherwise + * @returns {string|null} relation value if relation exists, null otherwise */ getRelationValue(name) { return this.getAttributeValue(RELATION, name); } /** * @param {string} name - relation name - * @returns {Promise} relation value if relation exists, null otherwise + * @returns {string|null} relation value if relation exists, null otherwise */ getOwnedRelationValue(name) { return this.getOwnedAttributeValue(RELATION, name); } /** * @param {string} name - * @returns {Promise|null} target note of the relation or null (if target is empty or note was not found) + * @returns {Note|null} target note of the relation or null (if target is empty or note was not found) */ getRelationTarget(name) { const relation = this.getRelation(name); @@ -653,7 +648,7 @@ class Note extends Entity { /** * @param {string} name - * @returns {Promise|null} target note of the relation or null (if target is empty or note was not found) + * @returns {Note|null} target note of the relation or null (if target is empty or note was not found) */ getOwnedRelationTarget(name) { const relation = this.getOwnedRelation(name); @@ -667,7 +662,6 @@ class Note extends Entity { * @param {boolean} enabled - toggle On or Off * @param {string} name - label name * @param {string} [value] - label value (optional) - * @returns {Promise} */ toggleLabel(enabled, name, value) { return this.toggleAttribute(LABEL, enabled, name, value); } @@ -677,7 +671,6 @@ class Note extends Entity { * @param {boolean} enabled - toggle On or Off * @param {string} name - relation name * @param {string} [value] - relation value (noteId) - * @returns {Promise} */ toggleRelation(enabled, name, value) { return this.toggleAttribute(RELATION, enabled, name, value); } @@ -686,7 +679,6 @@ class Note extends Entity { * * @param {string} name - label name * @param {string} [value] - label value - * @returns {Promise} */ setLabel(name, value) { return this.setAttribute(LABEL, name, value); } @@ -695,7 +687,6 @@ class Note extends Entity { * * @param {string} name - relation name * @param {string} [value] - relation value (noteId) - * @returns {Promise} */ setRelation(name, value) { return this.setAttribute(RELATION, name, value); } @@ -704,7 +695,6 @@ class Note extends Entity { * * @param {string} name - label name * @param {string} [value] - label value - * @returns {Promise} */ removeLabel(name, value) { return this.removeAttribute(LABEL, name, value); } @@ -713,12 +703,11 @@ class Note extends Entity { * * @param {string} name - relation name * @param {string} [value] - relation value (noteId) - * @returns {Promise} */ removeRelation(name, value) { return this.removeAttribute(RELATION, name, value); } /** - * @return {Promise} return list of all descendant noteIds of this note. Returning just noteIds because number of notes can be huge. Includes also this note's noteId + * @return {string[]} return list of all descendant noteIds of this note. Returning just noteIds because number of notes can be huge. Includes also this note's noteId */ getDescendantNoteIds() { return sql.getColumn(` @@ -741,7 +730,7 @@ class Note extends Entity { * @param {string} type - attribute type (label, relation, etc.) * @param {string} name - attribute name * @param {string} [value] - attribute value - * @returns {Promise} + * @returns {Note[]} */ getDescendantNotesWithAttribute(type, name, value) { const params = [this.noteId, name]; @@ -779,7 +768,7 @@ class Note extends Entity { * * @param {string} name - label name * @param {string} [value] - label value - * @returns {Promise} + * @returns {Note[]} */ getDescendantNotesWithLabel(name, value) { return this.getDescendantNotesWithAttribute(LABEL, name, value); } @@ -788,14 +777,14 @@ class Note extends Entity { * * @param {string} name - relation name * @param {string} [value] - relation value - * @returns {Promise} + * @returns {Note[]} */ getDescendantNotesWithRelation(name, value) { return this.getDescendantNotesWithAttribute(RELATION, name, value); } /** * Returns note revisions of this note. * - * @returns {Promise} + * @returns {NoteRevision[]} */ getRevisions() { return this.repository.getEntities("SELECT * FROM note_revisions WHERE noteId = ?", [this.noteId]); @@ -805,7 +794,7 @@ class Note extends Entity { * Get list of links coming out of this note. * * @deprecated - not intended for general use - * @returns {Promise} + * @returns {Attribute[]} */ getLinks() { return this.repository.getEntities(` @@ -818,7 +807,7 @@ class Note extends Entity { } /** - * @returns {Promise} + * @returns {Branch[]} */ getBranches() { return this.repository.getEntities("SELECT * FROM branches WHERE isDeleted = 0 AND noteId = ?", [this.noteId]); @@ -832,7 +821,7 @@ class Note extends Entity { } /** - * @returns {Promise} child notes of this note + * @returns {Note[]} child notes of this note */ getChildNotes() { return this.repository.getEntities(` @@ -846,7 +835,7 @@ class Note extends Entity { } /** - * @returns {Promise} child branches of this note + * @returns {Branch[]} child branches of this note */ getChildBranches() { return this.repository.getEntities(` @@ -858,7 +847,7 @@ class Note extends Entity { } /** - * @returns {Promise} parent notes of this note (note can have multiple parents because of cloning) + * @returns {Note[]} parent notes of this note (note can have multiple parents because of cloning) */ getParentNotes() { return this.repository.getEntities(` @@ -872,7 +861,7 @@ class Note extends Entity { } /** - * @return {Promise} - array of notePaths (each represented by array of noteIds constituting the particular note path) + * @return {string[][]} - array of notePaths (each represented by array of noteIds constituting the particular note path) */ getAllNotePaths() { if (this.noteId === 'root') { @@ -893,7 +882,7 @@ class Note extends Entity { /** * @param ancestorNoteId - * @return {Promise} - true if ancestorNoteId occurs in at least one of the note's paths + * @return {boolean} - true if ancestorNoteId occurs in at least one of the note's paths */ isDescendantOfNote(ancestorNoteId) { const notePaths = this.getAllNotePaths(); diff --git a/src/entities/note_revision.js b/src/entities/note_revision.js index 41ec8159c..461710344 100644 --- a/src/entities/note_revision.js +++ b/src/entities/note_revision.js @@ -64,7 +64,7 @@ class NoteRevision extends Entity { * This is the same approach as is used for Note's content. */ - /** @returns {Promise<*>} */ + /** @returns {*} */ getContent(silentNotFoundError = false) { if (this.content === undefined) { const res = sql.getRow(`SELECT content, hash FROM note_revision_contents WHERE noteRevisionId = ?`, [this.noteRevisionId]); diff --git a/src/public/app/widgets/attribute_list.js b/src/public/app/widgets/attribute_list.js index fbdc54f48..177391a1b 100644 --- a/src/public/app/widgets/attribute_list.js +++ b/src/public/app/widgets/attribute_list.js @@ -96,7 +96,7 @@ const TPL = `
-
+
@@ -128,7 +128,6 @@ export default class AttributeListWidget extends TabAwareWidget { }); this.$ownedAndInheritedWrapper = this.$widget.find('.owned-and-inherited-wrapper'); - this.$ownedAndInheritedWrapper.toggle(options.is('attributeListExpanded')); this.$ownedExpander = this.$widget.find('.attr-owned-and-inherited-expander'); this.$ownedExpander.on('click', async () => { @@ -141,15 +140,15 @@ export default class AttributeListWidget extends TabAwareWidget { this.$ownedExpanderText = this.$ownedExpander.find('.attr-expander-text'); - this.$inheritedAttributes = this.$widget.find('.inherited-attributes'); + this.$inheritedAttributesWrapper = this.$widget.find('.inherited-attributes-wrapper'); this.$inheritedExpander = this.$widget.find('.attr-inherited-expander'); this.$inheritedExpander.on('click', () => { - if (this.$inheritedAttributes.is(":visible")) { - this.$inheritedAttributes.slideUp(200); + if (this.$inheritedAttributesWrapper.is(":visible")) { + this.$inheritedAttributesWrapper.slideUp(200); } else { - this.$inheritedAttributes.slideDown(200); + this.$inheritedAttributesWrapper.slideDown(200); } }); @@ -163,6 +162,18 @@ export default class AttributeListWidget extends TabAwareWidget { } async refreshWithNote(note) { + const hasPromotedAttrs = this.promotedAttributesWidget.getPromotedAttributes().length > 0; + + if (hasPromotedAttrs) { + this.$allAttrWrapper.show(); + this.$ownedAndInheritedWrapper.hide(); + this.$inheritedAttributesWrapper.hide(); + } + else { + this.$promotedExpander.hide(); + this.$ownedAndInheritedWrapper.toggle(options.is('attributeListExpanded')); + } + const ownedAttributes = note.getOwnedAttributes().filter(attr => !attr.isAutoLink); this.$ownedExpanderText.text(ownedAttributes.length + ' owned ' + this.attrPlural(ownedAttributes.length)); @@ -180,9 +191,9 @@ export default class AttributeListWidget extends TabAwareWidget { this.$inheritedExpanderText.text(inheritedAttributes.length + ' inherited ' + this.attrPlural(inheritedAttributes.length)); - this.$inheritedAttributes.empty(); + this.$inheritedAttributesWrapper.empty(); - await this.renderInheritedAttributes(inheritedAttributes, this.$inheritedAttributes); + await this.renderInheritedAttributes(inheritedAttributes, this.$inheritedAttributesWrapper); } attrPlural(number) { diff --git a/src/public/app/widgets/promoted_attributes.js b/src/public/app/widgets/promoted_attributes.js index 42c0bb306..c5c4cf341 100644 --- a/src/public/app/widgets/promoted_attributes.js +++ b/src/public/app/widgets/promoted_attributes.js @@ -43,55 +43,60 @@ export default class PromotedAttributesWidget extends TabAwareWidget { async refreshWithNote(note) { this.$container.empty(); - const attributes = note.getAttributes(); + const promotedAttributes = this.getPromotedAttributes(); + const attributes = note.getAttributes();console.log(this.note.getAttributes()); - const promoted = attributes + const cells = []; + + if (promotedAttributes.length === 0) { + this.toggleInt(false); + return; + } + + for (const definitionAttr of promotedAttributes) { + const definitionType = definitionAttr.name.startsWith('label:') ? 'label' : 'relation'; + const valueName = definitionAttr.name.substr(definitionType.length + 1); + + let valueAttrs = attributes.filter(el => el.name === valueName && el.type === definitionType); + + if (valueAttrs.length === 0) { + valueAttrs.push({ + attributeId: "", + type: definitionType, + name: valueName, + value: "" + }); + } + + if (definitionAttr.value.multiplicity === 'single') { + valueAttrs = valueAttrs.slice(0, 1); + } + + for (const valueAttr of valueAttrs) { + const $cell = await this.createPromotedAttributeCell(definitionAttr, valueAttr, valueName); + + cells.push($cell); + } + } + + // we replace the whole content in one step so there can't be any race conditions + // (previously we saw promoted attributes doubling) + this.$container.empty().append(...cells); + this.toggleInt(true); + } + + getPromotedAttributes() { + if (this.note.hasLabel('hidePromotedAttributes')) { + return []; + } + + return this.note.getAttributes() .filter(attr => attr.isDefinition()) .filter(attr => { const def = attr.getDefinition(); return def && def.isPromoted; }); - - const cells = []; - - if (promoted.length > 0 && !note.hasLabel('hidePromotedAttributes')) { - for (const definitionAttr of promoted) { - const definitionType = definitionAttr.name.startsWith('label:') ? 'label' : 'relation'; - const valueName = definitionAttr.name.substr(definitionType.length + 1); - - let valueAttrs = attributes.filter(el => el.name === valueName && el.type === 'label'); - - if (valueAttrs.length === 0) { - valueAttrs.push({ - attributeId: "", - type: definitionType, - name: valueName, - value: "" - }); - } - - if (definitionAttr.value.multiplicity === 'single') { - valueAttrs = valueAttrs.slice(0, 1); - } - - for (const valueAttr of valueAttrs) { - const $cell = await this.createPromotedAttributeCell(definitionAttr, valueAttr, valueName); - - cells.push($cell); - } - } - - // we replace the whole content in one step so there can't be any race conditions - // (previously we saw promoted attributes doubling) - this.$container.empty().append(...cells); - this.toggleInt(true); - } - else { - this.toggleInt(false); - } - - return attributes; } async createPromotedAttributeCell(definitionAttr, valueAttr, valueName) {