fix duplicate subtree

This commit is contained in:
zadam 2021-10-02 23:26:18 +02:00
parent 8c2a5d19f2
commit e4a483aefe
7 changed files with 128 additions and 101 deletions

89
package-lock.json generated
View file

@ -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",

View file

@ -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": {

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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

View file

@ -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) {

View file

@ -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);