From e4a483aefe0692dbfb63bdd43ac0295fdd739a43 Mon Sep 17 00:00:00 2001 From: zadam Date: Sat, 2 Oct 2021 23:26:18 +0200 Subject: [PATCH] fix duplicate subtree --- package-lock.json | 89 +++++++++-------- package.json | 10 +- src/public/app/services/froca_updater.js | 2 + src/public/app/services/note_context.js | 10 -- .../widgets/containers/ribbon_container.js | 2 +- src/services/notes.js | 98 ++++++++++--------- src/services/ws.js | 18 ++++ 7 files changed, 128 insertions(+), 101 deletions(-) diff --git a/package-lock.json b/package-lock.json index 2530fd354..df43c2055 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1334,11 +1334,11 @@ "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==" }, "axios": { - "version": "0.21.4", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", - "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", + "version": "0.22.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.22.0.tgz", + "integrity": "sha512-Z0U3uhqQeg1oNcihswf4ZD57O3NrR1+ZXhxaROaWpDmsDTx7T2HNBV2ulBtie2hwJptu8UvgnJoK+BIqdzh/1w==", "requires": { - "follow-redirects": "^1.14.0" + "follow-redirects": "^1.14.4" } }, "bagpipe": { @@ -1602,15 +1602,15 @@ "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==" }, "browserslist": { - "version": "4.17.1", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.17.1.tgz", - "integrity": "sha512-aLD0ZMDSnF4lUt4ZDNgqi5BUn9BZ7YdQdI/cYlILrhdSSZJLU9aNZoD5/NBmM4SK34APB2e83MOsRt1EnkuyaQ==", + "version": "4.17.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.17.2.tgz", + "integrity": "sha512-jSDZyqJmkKMEMi7SZAgX5UltFdR5NAO43vY0AwTpu4X3sGH7GLLQ83KiUomgrnvZRCeW0yPPnKqnxPqQOER9zQ==", "dev": true, "requires": { - "caniuse-lite": "^1.0.30001259", - "electron-to-chromium": "^1.3.846", + "caniuse-lite": "^1.0.30001261", + "electron-to-chromium": "^1.3.854", "escalade": "^3.1.1", - "nanocolors": "^0.1.5", + "nanocolors": "^0.2.12", "node-releases": "^1.1.76" } }, @@ -1891,9 +1891,9 @@ "dev": true }, "caniuse-lite": { - "version": "1.0.30001261", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001261.tgz", - "integrity": "sha512-vM8D9Uvp7bHIN0fZ2KQ4wnmYFpJo/Etb4Vwsuc+ka0tfGDHvOPrFm6S/7CCNLSOkAUjenT2HnUPESdOIL91FaA==", + "version": "1.0.30001263", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001263.tgz", + "integrity": "sha512-doiV5dft6yzWO1WwU19kt8Qz8R0/8DgEziz6/9n2FxUasteZNwNNYSmJO3GLBH8lCVE73AB1RPDPAeYbcO5Cvw==", "dev": true }, "caseless": { @@ -3578,9 +3578,9 @@ } }, "electron-to-chromium": { - "version": "1.3.854", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.854.tgz", - "integrity": "sha512-00/IIC1mFPkq32MhUJyLdcTp7+wsKK2G3Sb65GSas9FKJQGYkDcZ4GwJkkxf5YyM3ETvl6n+toV8OmtXl4IA/g==", + "version": "1.3.857", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.857.tgz", + "integrity": "sha512-a5kIr2lajm4bJ5E4D3fp8Y/BRB0Dx2VOcCRE5Gtb679mXIME/OFhWler8Gy2ksrf8gFX+EFCSIGA33FB3gqYpg==", "dev": true }, "electron-window-state": { @@ -3684,9 +3684,9 @@ } }, "es-module-lexer": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.1.tgz", - "integrity": "sha512-17Ed9misDnpyNBJh63g1OhW3qUFecDgGOivI85JeZY/LGhDum8e+cltukbkSK8pcJnXXEkya56sp4vSS1nzoUw==", + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.2.tgz", + "integrity": "sha512-YkAGWqxZq2B4FxQ5y687UwywDwvLQhIMCZ+SDU7ZW729SDHOEI6wVFXwTRecz+yiwJzCsVwC6V7bxyNbZSB1rg==", "dev": true }, "es6-error": { @@ -3887,9 +3887,9 @@ "integrity": "sha512-o1JrraDGpMFaPtkuvtZ4cIBC/xuJn90KBGlxRrm3FxcfER1bPaBnBsTnypF65p+CMTXul2KrZodb3Vv3MScB4A==" }, "express-rate-limit": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-5.3.0.tgz", - "integrity": "sha512-qJhfEgCnmteSeZAeuOKQ2WEIFTX5ajrzE0xS6gCOBCoRQcU+xEzQmgYQQTpzCcqUAAzTEtu4YEih4pnLfvNtew==" + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-5.4.0.tgz", + "integrity": "sha512-sT+rk1wvj06+0MpEiij7y3kGdB4hoMyQ+a5zcESUpDMLhbLXoYIQI6JfsvLBz1wOhmfF//ALG/Q59FKMI0x2Eg==" }, "express-session": { "version": "1.17.2", @@ -4141,9 +4141,9 @@ } }, "follow-redirects": { - "version": "1.14.3", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.3.tgz", - "integrity": "sha512-3MkHxknWMUtb23apkgz/83fDoe+y+qr0TdgacGIA7bew+QLBo3vdgEN2xEsuXNivpFy4CyDhBBZnNZOtalmenw==" + "version": "1.14.4", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.4.tgz", + "integrity": "sha512-zwGkiSXC1MUJG/qmeIFH2HBJx9u0V46QGUe3YR1fXG8bXQxq7fLj0RjLZQ5nubr9qNJUZrH+xUcwXEoXNpfS+g==" }, "forever-agent": { "version": "0.6.1", @@ -5669,11 +5669,18 @@ "integrity": "sha512-CIc8j9URtOVApSFCQIF+VBkX1RwXp/oMMOrqdyXSBXq5RWNEsRfyj1kiRnQgmNXmHxPoFIxOroKA3zcU9P+nAA==" }, "mime-types": { - "version": "2.1.32", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.32.tgz", - "integrity": "sha512-hJGaVS4G4c9TSMYh2n6SQAGrC4RnfU+daP8G7cSCmaqNjiOoUY0VHCMS42pxnQmVF1GWwFhbHWn3RIxCqTmZ9A==", + "version": "2.1.33", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.33.tgz", + "integrity": "sha512-plLElXp7pRDd0bNZHw+nMd52vRYjLwQjygaNg7ddJ2uJtTlmnTCjWuPKxVu6//AdaRuME84SvLW91sIkBqGT0g==", "requires": { - "mime-db": "1.49.0" + "mime-db": "1.50.0" + }, + "dependencies": { + "mime-db": { + "version": "1.50.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.50.0.tgz", + "integrity": "sha512-9tMZCDlYHqeERXEHO9f/hKfNXhre5dK2eE/krIvUjZbS2KPcqGDfNShIWS1uW9XOTKQKqK6qbeOci18rbfW77A==" + } } }, "mimic-fn": { @@ -5818,9 +5825,9 @@ } }, "nanocolors": { - "version": "0.1.12", - "resolved": "https://registry.npmjs.org/nanocolors/-/nanocolors-0.1.12.tgz", - "integrity": "sha512-2nMHqg1x5PU+unxX7PGY7AuYxl2qDx7PSrTRjizr8sxdd3l/3hBuWWaki62qmtYm2U5i4Z5E7GbjlyDFhs9/EQ==", + "version": "0.2.12", + "resolved": "https://registry.npmjs.org/nanocolors/-/nanocolors-0.2.12.tgz", + "integrity": "sha512-SFNdALvzW+rVlzqexid6epYdt8H9Zol7xDoQarioEFcFN0JHo4CYNztAxmtfgGTVRCmFlEOqqhBpoFGKqSAMug==", "dev": true }, "nanoid": { @@ -5893,9 +5900,9 @@ "dev": true }, "node-releases": { - "version": "1.1.76", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.76.tgz", - "integrity": "sha512-9/IECtNr8dXNmPWmFXepT0/7o5eolGesHUa3mtr0KlgnCvnZxwh2qensKL42JJY2vQKC3nIBXetFAqR+PW1CmA==", + "version": "1.1.77", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.77.tgz", + "integrity": "sha512-rB1DUFUNAN4Gn9keO2K1efO35IDK7yKHCdCaIMvFO7yUYmmZYeDjnGKle26G4rwj+LKRQpjyUUvMkPglwGCYNQ==", "dev": true }, "nopt": { @@ -8091,9 +8098,9 @@ "integrity": "sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==" }, "webpack": { - "version": "5.55.1", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.55.1.tgz", - "integrity": "sha512-EYp9lwaOOAs+AA/KviNZ7bQiITHm4bXQvyTPewD2+f5YGjv6sfiClm40yeX5FgBMxh5bxcB6LryiFoP09B97Ug==", + "version": "5.56.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.56.0.tgz", + "integrity": "sha512-pJ7esw2AGkpZL0jqsEAKnDEfRZdrc9NVjAWA+d1mFkwj68ng9VQ6+Wnrl+kS5dlDHvrat5ASK5vd7wp6I7f53Q==", "dev": true, "requires": { "@types/eslint-scope": "^3.7.0", @@ -8363,9 +8370,9 @@ } }, "ws": { - "version": "8.2.2", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.2.2.tgz", - "integrity": "sha512-Q6B6H2oc8QY3llc3cB8kVmQ6pnJWVQbP7Q5algTcIxx7YEpc0oU4NBVHlztA7Ekzfhw2r0rPducMUiCGWKQRzw==" + "version": "8.2.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.2.3.tgz", + "integrity": "sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==" }, "xdg-basedir": { "version": "4.0.0", diff --git a/package.json b/package.json index 4e3e34964..6785d2c07 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,7 @@ "dependencies": { "archiver": "5.3.0", "async-mutex": "0.3.2", - "axios": "0.21.4", + "axios": "0.22.0", "better-sqlite3": "7.4.3", "body-parser": "1.19.0", "chokidar": "3.5.2", @@ -42,7 +42,7 @@ "electron-window-state": "5.0.3", "express": "4.17.1", "express-partial-content": "^1.0.2", - "express-rate-limit": "5.3.0", + "express-rate-limit": "5.4.0", "express-session": "1.17.2", "fs-extra": "10.0.0", "helmet": "4.6.0", @@ -57,7 +57,7 @@ "jimp": "0.16.1", "joplin-turndown-plugin-gfm": "1.0.12", "jsdom": "17.0.0", - "mime-types": "2.1.32", + "mime-types": "2.1.33", "multer": "1.4.3", "node-abi": "3.2.0", "open": "8.2.1", @@ -76,7 +76,7 @@ "tmp": "^0.2.1", "turndown": "7.1.1", "unescape": "1.0.1", - "ws": "8.2.2", + "ws": "8.2.3", "yauzl": "2.10.0" }, "devDependencies": { @@ -90,7 +90,7 @@ "jsdoc": "3.6.7", "lorem-ipsum": "2.0.4", "rcedit": "3.0.1", - "webpack": "5.55.1", + "webpack": "5.56.0", "webpack-cli": "4.8.0" }, "optionalDependencies": { diff --git a/src/public/app/services/froca_updater.js b/src/public/app/services/froca_updater.js index 384da83d6..7c7640872 100644 --- a/src/public/app/services/froca_updater.js +++ b/src/public/app/services/froca_updater.js @@ -82,6 +82,8 @@ function processNoteChange(loadResults, ec) { const note = froca.notes[ec.entityId]; if (!note) { + // if this note has not been requested before then it's not part of froca's cached subset and + // we're not interested in it return; } diff --git a/src/public/app/services/note_context.js b/src/public/app/services/note_context.js index b9588ebd2..a6f1df928 100644 --- a/src/public/app/services/note_context.js +++ b/src/public/app/services/note_context.js @@ -91,13 +91,6 @@ class NoteContext extends Component { } async getResolvedNotePath(inputNotePath) { - const noteId = treeService.getNoteIdFromNotePath(inputNotePath); - - if ((await froca.getNote(noteId)).isDeleted) { - // no point in trying to resolve canonical notePath - return inputNotePath; - } - const resolvedNotePath = await treeService.resolveNotePath(inputNotePath, this.hoistedNoteId); if (!resolvedNotePath) { @@ -113,9 +106,6 @@ class NoteContext extends Component { return; // note is outside of hoisted subtree and user chose not to unhoist } - // if user choise to unhoist, cache was reloaded, but might not contain this note (since it's on unexpanded path) - await froca.getNote(noteId); - return resolvedNotePath; } diff --git a/src/public/app/widgets/containers/ribbon_container.js b/src/public/app/widgets/containers/ribbon_container.js index 99254da1d..5b398cda8 100644 --- a/src/public/app/widgets/containers/ribbon_container.js +++ b/src/public/app/widgets/containers/ribbon_container.js @@ -310,7 +310,7 @@ export default class RibbonContainer extends NoteContextAwareWidget { // won't trigger .refresh(); await super.handleEventInChildren('setNoteContext', data); } - else { + else if (this.isEnabled()) { const activeRibbonWidget = this.getActiveRibbonWidget(); // forward events only to active ribbon tab, inactive ones don't need to be updated diff --git a/src/services/notes.js b/src/services/notes.js index aa424709f..7ccdf8a61 100644 --- a/src/services/notes.js +++ b/src/services/notes.js @@ -812,61 +812,71 @@ function duplicateSubtreeInner(origNote, origBranch, newParentNoteId, noteIdMapp const newNoteId = noteIdMapping[origNote.noteId]; - const newBranch = new Branch({ - noteId: newNoteId, - parentNoteId: newParentNoteId, - // here increasing just by 1 to make sure it's directly after original - notePosition: origBranch ? origBranch.notePosition + 1 : null - }).save(); + function createDuplicatedBranch() { + return new Branch({ + noteId: newNoteId, + parentNoteId: newParentNoteId, + // here increasing just by 1 to make sure it's directly after original + notePosition: origBranch ? origBranch.notePosition + 1 : null + }).save(); + } + + function createDuplicatedNote() { + const newNote = new Note({ + ...origNote, + noteId: newNoteId, + dateCreated: dateUtils.localNowDateTime(), + utcDateCreated: dateUtils.utcNowDateTime() + }).save(); + + let content = origNote.getContent(); + + if (['text', 'relation-map', 'search'].includes(origNote.type)) { + // fix links in the content + content = replaceByMap(content, noteIdMapping); + } + + newNote.setContent(content); + + for (const attribute of origNote.getOwnedAttributes()) { + const attr = new Attribute({ + ...attribute, + attributeId: undefined, + noteId: newNote.noteId + }); + + // if relation points to within the duplicated tree then replace the target to the duplicated note + // if it points outside of duplicated tree then keep the original target + if (attr.type === 'relation' && attr.value in noteIdMapping) { + attr.value = noteIdMapping[attr.value]; + } + + attr.save(); + } + + for (const childBranch of origNote.getChildBranches()) { + duplicateSubtreeInner(childBranch.getNote(), childBranch, newNote.noteId, noteIdMapping); + } + return newNote; + } const existingNote = becca.notes[newNoteId]; - if (existingNote.title !== undefined) { // checking that it's not just note's skeleton created because of Branch above + if (existingNote && existingNote.title !== undefined) { // checking that it's not just note's skeleton created because of Branch above // note has multiple clones and was already created from another placement in the tree // so a branch is all we need for this clone return { note: existingNote, - branch: newBranch + branch: createDuplicatedBranch() } } - - const newNote = new Note(origNote); - newNote.noteId = newNoteId; - newNote.dateCreated = dateUtils.localNowDateTime(); - newNote.utcDateCreated = dateUtils.utcNowDateTime(); - newNote.save(); - - let content = origNote.getContent(); - - if (['text', 'relation-map', 'search'].includes(origNote.type)) { - // fix links in the content - content = replaceByMap(content, noteIdMapping); - } - - newNote.setContent(content); - - for (const attribute of origNote.getOwnedAttributes()) { - const attr = new Attribute(attribute); - attr.attributeId = undefined; // force creation of new attribute - attr.noteId = newNote.noteId; - - // if relation points to within the duplicated tree then replace the target to the duplicated note - // if it points outside of duplicated tree then keep the original target - if (attr.type === 'relation' && attr.value in noteIdMapping) { - attr.value = noteIdMapping[attr.value]; + else { + return { + // order here is important, note needs to be created first to not mess up the becca + note: createDuplicatedNote(), + branch: createDuplicatedBranch() } - - attr.save(); } - - for (const childBranch of origNote.getChildBranches()) { - duplicateSubtreeInner(childBranch.getNote(), childBranch, newNote.noteId, noteIdMapping); - } - - return { - note: newNote, - branch: newBranch - }; } function getNoteIdMapping(origNote) { diff --git a/src/services/ws.js b/src/services/ws.js index 58300b3b8..3d090d21b 100644 --- a/src/services/ws.js +++ b/src/services/ws.js @@ -137,6 +137,19 @@ function fillInAdditionalProperties(entityChange) { } } +// entities with higher number can reference the entities with lower number +const ORDERING = { + "api_tokens": 0, + "attributes": 1, + "branches": 1, + "note_contents": 1, + "note_reordering": 1, + "note_revision_contents": 2, + "note_revisions": 1, + "notes": 0, + "options": 0 +}; + function sendPing(client, entityChangeIds = []) { if (entityChangeIds.length === 0) { sendMessage(client, { type: 'ping' }); @@ -146,6 +159,11 @@ function sendPing(client, entityChangeIds = []) { const entityChanges = sql.getManyRows(`SELECT * FROM entity_changes WHERE id IN (???)`, entityChangeIds); + // sort entity changes since froca expects "referential order", i.e. referenced entities should already exist + // in froca. + // Froca needs this since it is incomplete copy, it can't create "skeletons" like becca. + entityChanges.sort((a, b) => ORDERING[a.entityName] - ORDERING[b.entityName]); + for (const entityChange of entityChanges) { try { fillInAdditionalProperties(entityChange);