From d0747b125c49eced4520de44deb7f074fb48d8db Mon Sep 17 00:00:00 2001 From: zadam Date: Wed, 25 Aug 2021 22:49:24 +0200 Subject: [PATCH] improvements in frontend sync (WIP) --- src/public/app/entities/attribute.js | 38 ---------------- src/public/app/services/attributes.js | 42 +++++++++++++++++- .../attribute_widgets/attribute_editor.js | 3 +- src/public/app/widgets/note_detail.js | 6 +-- src/public/app/widgets/note_icon.js | 2 +- src/public/app/widgets/note_tree.js | 44 +++++++++---------- .../inherited_attribute_list.js | 3 +- .../ribbon_widgets/owned_attribute_list.js | 3 +- .../ribbon_widgets/promoted_attributes.js | 3 +- src/public/app/widgets/tab_row.js | 3 +- 10 files changed, 77 insertions(+), 70 deletions(-) diff --git a/src/public/app/entities/attribute.js b/src/public/app/entities/attribute.js index e1e34cbfb..126ad4797 100644 --- a/src/public/app/entities/attribute.js +++ b/src/public/app/entities/attribute.js @@ -41,44 +41,6 @@ class Attribute { return `Attribute(attributeId=${this.attributeId}, type=${this.type}, name=${this.name}, value=${this.value})`; } - /** - * @return {boolean} - returns true if this attribute has the potential to influence the note in the argument. - * That can happen in multiple ways: - * 1. attribute is owned by the note - * 2. attribute is owned by the template of the note - * 3. attribute is owned by some note's ancestor and is inheritable - */ - isAffecting(affectedNote) { - if (!affectedNote) { - return false; - } - - const attrNote = this.getNote(); - - if (!attrNote) { - // the note (owner of the attribute) is not even loaded into the cache so it should not affect anything else - return false; - } - - const owningNotes = [affectedNote, ...affectedNote.getTemplateNotes()]; - - for (const owningNote of owningNotes) { - if (owningNote.noteId === attrNote.noteId) { - return true; - } - } - - if (this.isInheritable) { - for (const owningNote of owningNotes) { - if (owningNote.hasAncestor(attrNote)) { - return true; - } - } - } - - return false; - } - isDefinition() { return this.type === 'label' && (this.name.startsWith('label:') || this.name.startsWith('relation:')); } diff --git a/src/public/app/services/attributes.js b/src/public/app/services/attributes.js index 2d175ba2b..d677a40e1 100644 --- a/src/public/app/services/attributes.js +++ b/src/public/app/services/attributes.js @@ -1,4 +1,5 @@ import server from './server.js'; +import froca from './froca.js'; async function addLabel(noteId, name, value = "") { await server.put(`notes/${noteId}/attribute`, { @@ -20,8 +21,47 @@ async function removeAttributeById(noteId, attributeId) { await server.remove(`notes/${noteId}/attributes/${attributeId}`); } +/** + * @return {boolean} - returns true if this attribute has the potential to influence the note in the argument. + * That can happen in multiple ways: + * 1. attribute is owned by the note + * 2. attribute is owned by the template of the note + * 3. attribute is owned by some note's ancestor and is inheritable + */ +function isAffecting(attrRow, affectedNote) { + if (!affectedNote || !attrRow) { + return false; + } + + const attrNote = froca.notes[attrRow.noteId]; + + if (!attrNote) { + // the note (owner of the attribute) is not even loaded into the cache so it should not affect anything else + return false; + } + + const owningNotes = [affectedNote, ...affectedNote.getTemplateNotes()]; + + for (const owningNote of owningNotes) { + if (owningNote.noteId === attrNote.noteId) { + return true; + } + } + + if (this.isInheritable) { + for (const owningNote of owningNotes) { + if (owningNote.hasAncestor(attrNote)) { + return true; + } + } + } + + return false; +} + export default { addLabel, setLabel, - removeAttributeById + removeAttributeById, + isAffecting } diff --git a/src/public/app/widgets/attribute_widgets/attribute_editor.js b/src/public/app/widgets/attribute_widgets/attribute_editor.js index 4a35be33c..96890e096 100644 --- a/src/public/app/widgets/attribute_widgets/attribute_editor.js +++ b/src/public/app/widgets/attribute_widgets/attribute_editor.js @@ -8,6 +8,7 @@ import froca from "../../services/froca.js"; import attributeRenderer from "../../services/attribute_renderer.js"; import noteCreateService from "../../services/note_create.js"; import treeService from "../../services/tree.js"; +import attributeService from "../../services/attributes.js"; const HELP_TEXT = `

To add label, just type e.g. #rock or if you want to add also value then e.g. #year = 2020

@@ -511,7 +512,7 @@ export default class AttributeEditorWidget extends NoteContextAwareWidget { } entitiesReloadedEvent({loadResults}) { - if (loadResults.getAttributes(this.componentId).find(attr => attr.isAffecting(this.note))) { + if (loadResults.getAttributes(this.componentId).find(attr => attributeService.isAffecting(attr, this.note))) { this.refresh(); } } diff --git a/src/public/app/widgets/note_detail.js b/src/public/app/widgets/note_detail.js index fa37a4683..8027393eb 100644 --- a/src/public/app/widgets/note_detail.js +++ b/src/public/app/widgets/note_detail.js @@ -1,5 +1,4 @@ import NoteContextAwareWidget from "./note_context_aware_widget.js"; -import utils from "../services/utils.js"; import protectedSessionHolder from "../services/protected_session_holder.js"; import SpacedUpdate from "../services/spaced_update.js"; import server from "../services/server.js"; @@ -20,6 +19,7 @@ import DeletedTypeWidget from "./type_widgets/deleted.js"; import ReadOnlyTextTypeWidget from "./type_widgets/read_only_text.js"; import ReadOnlyCodeTypeWidget from "./type_widgets/read_only_code.js"; import NoneTypeWidget from "./type_widgets/none.js"; +import attributeService from "../services/attributes.js"; const TPL = `
@@ -246,12 +246,12 @@ export default class NoteDetailWidget extends NoteContextAwareWidget { const label = attrs.find(attr => attr.type === 'label' && ['readOnly', 'autoReadOnlyDisabled', 'cssClass', 'displayRelations'].includes(attr.name) - && attr.isAffecting(this.note)); + && attributeService.isAffecting(attr, this.note)); const relation = attrs.find(attr => attr.type === 'relation' && ['template', 'renderNote'].includes(attr.name) - && attr.isAffecting(this.note)); + && attributeService.isAffecting(attr, this.note)); if (label || relation) { // probably incorrect event diff --git a/src/public/app/widgets/note_icon.js b/src/public/app/widgets/note_icon.js index ffc1ae526..f43895da1 100644 --- a/src/public/app/widgets/note_icon.js +++ b/src/public/app/widgets/note_icon.js @@ -126,7 +126,7 @@ export default class NoteIconWidget extends NoteContextAwareWidget { for (const attr of loadResults.getAttributes()) { if (attr.type === 'label' && ['iconClass', 'workspaceIconClass'].includes(attr.name) - && attr.isAffecting(this.note)) { + && attributeService.isAffecting(attr, this.note)) { this.refresh(); break; diff --git a/src/public/app/widgets/note_tree.js b/src/public/app/widgets/note_tree.js index bdb418902..d63da777c 100644 --- a/src/public/app/widgets/note_tree.js +++ b/src/public/app/widgets/note_tree.js @@ -972,36 +972,36 @@ export default class NoteTreeWidget extends NoteContextAwareWidget { const noteIdsToUpdate = new Set(); const noteIdsToReload = new Set(); - for (const attr of loadResults.getAttributes()) { - if (attr.type === 'label' && ['iconClass', 'cssClass', 'workspace', 'workspaceIconClass', 'archived'].includes(attr.name)) { - if (attr.isInheritable) { - noteIdsToReload.add(attr.noteId); + for (const ecAttr of loadResults.getAttributes()) { + if (ecAttr.type === 'label' && ['iconClass', 'cssClass', 'workspace', 'workspaceIconClass', 'archived'].includes(ecAttr.name)) { + if (ecAttr.isInheritable) { + noteIdsToReload.add(ecAttr.noteId); } else { - noteIdsToUpdate.add(attr.noteId); + noteIdsToUpdate.add(ecAttr.noteId); } } - else if (attr.type === 'relation' && attr.name === 'template') { + else if (ecAttr.type === 'relation' && ecAttr.name === 'template') { // missing handling of things inherited from template - noteIdsToReload.add(attr.noteId); + noteIdsToReload.add(ecAttr.noteId); } - else if (attr.type === 'relation' && attr.name === 'imageLink') { - const note = froca.getNoteFromCache(attr.noteId); + else if (ecAttr.type === 'relation' && ecAttr.name === 'imageLink') { + const note = froca.getNoteFromCache(ecAttr.noteId); - if (note && note.getChildNoteIds().includes(attr.value)) { + if (note && note.getChildNoteIds().includes(ecAttr.value)) { // there's new/deleted imageLink betwen note and its image child - which can show/hide // the image (if there is a imageLink relation between parent and child then it is assumed to be "contained" in the note and thus does not have to be displayed in the tree) - noteIdsToReload.add(attr.noteId); + noteIdsToReload.add(ecAttr.noteId); } } } - for (const branch of loadResults.getBranches()) { + for (const ecBranch of loadResults.getBranches()) { // adding noteId itself to update all potential clones - noteIdsToUpdate.add(branch.noteId); + noteIdsToUpdate.add(ecBranch.noteId); - for (const node of this.getNodesByBranch(branch)) { - if (branch.isDeleted) { + for (const node of this.getNodesByBranch(ecBranch)) { + if (ecBranch.isDeleted) { if (node.isActive()) { const newActiveNode = node.getNextSibling() || node.getPrevSibling() @@ -1016,22 +1016,22 @@ export default class NoteTreeWidget extends NoteContextAwareWidget { node.remove(); } - noteIdsToUpdate.add(branch.parentNoteId); + noteIdsToUpdate.add(ecBranch.parentNoteId); } } - if (!branch.isDeleted) { - for (const parentNode of this.getNodesByNoteId(branch.parentNoteId)) { + if (!ecBranch.isDeleted) { + for (const parentNode of this.getNodesByNoteId(ecBranch.parentNoteId)) { if (parentNode.isFolder() && !parentNode.isLoaded()) { continue; } - const found = (parentNode.getChildren() || []).find(child => child.data.noteId === branch.noteId); + const found = (parentNode.getChildren() || []).find(child => child.data.noteId === ecBranch.noteId); if (!found) { // make sure it's loaded - await froca.getNote(branch.noteId); - const frocaBranch = froca.getBranch(branch.branchId); + await froca.getNote(ecBranch.noteId); + const frocaBranch = froca.getBranch(ecBranch.branchId); // we're forcing lazy since it's not clear if the whole required subtree is in froca parentNode.addChildren([this.prepareNode(frocaBranch, true)]); @@ -1039,7 +1039,7 @@ export default class NoteTreeWidget extends NoteContextAwareWidget { this.sortChildren(parentNode); // this might be a first child which would force an icon change - noteIdsToUpdate.add(branch.parentNoteId); + noteIdsToUpdate.add(ecBranch.parentNoteId); } } } diff --git a/src/public/app/widgets/ribbon_widgets/inherited_attribute_list.js b/src/public/app/widgets/ribbon_widgets/inherited_attribute_list.js index c1a041bdb..cb91cae8a 100644 --- a/src/public/app/widgets/ribbon_widgets/inherited_attribute_list.js +++ b/src/public/app/widgets/ribbon_widgets/inherited_attribute_list.js @@ -1,6 +1,7 @@ import NoteContextAwareWidget from "../note_context_aware_widget.js"; import AttributeDetailWidget from "../attribute_widgets/attribute_detail.js"; import attributeRenderer from "../../services/attribute_renderer.js"; +import attributeService from "../../services/attributes.js"; const TPL = `
@@ -88,7 +89,7 @@ export default class InheritedAttributesWidget extends NoteContextAwareWidget { } entitiesReloadedEvent({loadResults}) { - if (loadResults.getAttributes(this.componentId).find(attr => attr.isAffecting(this.note))) { + if (loadResults.getAttributes(this.componentId).find(attr => attributeService.isAffecting(attr, this.note))) { this.refresh(); } } diff --git a/src/public/app/widgets/ribbon_widgets/owned_attribute_list.js b/src/public/app/widgets/ribbon_widgets/owned_attribute_list.js index 2088b24fc..4f294979d 100644 --- a/src/public/app/widgets/ribbon_widgets/owned_attribute_list.js +++ b/src/public/app/widgets/ribbon_widgets/owned_attribute_list.js @@ -1,6 +1,7 @@ import NoteContextAwareWidget from "../note_context_aware_widget.js"; import AttributeDetailWidget from "../attribute_widgets/attribute_detail.js"; import AttributeEditorWidget from "../attribute_widgets/attribute_editor.js"; +import attributeService from "../../services/attributes.js"; const TPL = `
@@ -75,7 +76,7 @@ export default class OwnedAttributeListWidget extends NoteContextAwareWidget { } entitiesReloadedEvent({loadResults}) { - if (loadResults.getAttributes(this.componentId).find(attr => attr.isAffecting(this.note))) { + if (loadResults.getAttributes(this.componentId).find(attr => attributeService.isAffecting(attr, this.note))) { this.refreshWithNote(this.note, true); this.getTitle(this.note); diff --git a/src/public/app/widgets/ribbon_widgets/promoted_attributes.js b/src/public/app/widgets/ribbon_widgets/promoted_attributes.js index 533b49df5..dab794ae8 100644 --- a/src/public/app/widgets/ribbon_widgets/promoted_attributes.js +++ b/src/public/app/widgets/ribbon_widgets/promoted_attributes.js @@ -3,6 +3,7 @@ import ws from "../../services/ws.js"; import treeService from "../../services/tree.js"; import noteAutocompleteService from "../../services/note_autocomplete.js"; import NoteContextAwareWidget from "../note_context_aware_widget.js"; +import attributeService from "../../services/attributes.js"; const TPL = `
@@ -294,7 +295,7 @@ export default class PromotedAttributesWidget extends NoteContextAwareWidget { } entitiesReloadedEvent({loadResults}) { - if (loadResults.getAttributes(this.componentId).find(attr => attr.isAffecting(this.note))) { + if (loadResults.getAttributes(this.componentId).find(attr => attributeService.isAffecting(attr, this.note))) { this.refresh(); this.getTitle(this.note); diff --git a/src/public/app/widgets/tab_row.js b/src/public/app/widgets/tab_row.js index ccec6d9ab..3ff68605a 100644 --- a/src/public/app/widgets/tab_row.js +++ b/src/public/app/widgets/tab_row.js @@ -4,6 +4,7 @@ import utils from "../services/utils.js"; import keyboardActionService from "../services/keyboard_actions.js"; import appContext from "../services/app_context.js"; import froca from "../services/froca.js"; +import attributeService from "../services/attributes.js"; /*! * Draggabilly v2.3.0 @@ -668,7 +669,7 @@ export default class TabRowWidget extends BasicWidget { if (loadResults.isNoteReloaded(noteContext.noteId) || loadResults.getAttributes().find(attr => ['workspace', 'workspaceIconClass', 'workspaceTabBackgroundColor'].includes(attr.name) - && attr.isAffecting(noteContext.note)) + && attributeService.isAffecting(attr, noteContext.note)) ) { const $tab = this.getTabById(noteContext.ntxId);