From ae0b060f017d54800e643b259727c90124d0c799 Mon Sep 17 00:00:00 2001 From: Stefan Zermatten Date: Tue, 5 Nov 2019 10:56:04 +0200 Subject: [PATCH] Added inserting library subtrees as character property subtrees --- .../api/creature/CreatureProperties.js | 96 ++++++++++++++++--- app/imports/api/parenting/order.js | 1 + app/imports/api/parenting/parenting.js | 40 ++++++++ app/imports/ui/components/tree/TreeNode.vue | 2 +- app/imports/ui/creature/character/TreeTab.vue | 14 ++- app/imports/ui/router.js | 12 +-- 6 files changed, 144 insertions(+), 21 deletions(-) diff --git a/app/imports/api/creature/CreatureProperties.js b/app/imports/api/creature/CreatureProperties.js index 39c8361a..3105f367 100644 --- a/app/imports/api/creature/CreatureProperties.js +++ b/app/imports/api/creature/CreatureProperties.js @@ -1,8 +1,15 @@ import SimpleSchema from 'simpl-schema'; -import ChildSchema from '/imports/api/parenting/ChildSchema.js'; +import ChildSchema, { RefSchema } from '/imports/api/parenting/ChildSchema.js'; import Creature from '/imports/api/creature/Creatures.js'; +import LibraryNodes from '/imports/api/library/LibraryNodes.js'; import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js'; import propertySchemasIndex from '/imports/api/properties/propertySchemasIndex.js'; +import { + setLineageOfDocs, + getAncestry, + renewDocIds +} from '/imports/api/parenting/parenting.js'; +import {setDocToLastOrder} from '/imports/api/parenting/order.js'; let CreatureProperties = new Mongo.Collection('creatureProperties'); @@ -11,12 +18,6 @@ let CreaturePropertySchema = new SimpleSchema({ type: String, allowedValues: Object.keys(propertySchemasIndex), }, - charId: { - type: String, - regEx: SimpleSchema.RegEx.Id, - index: 1, - optional: true, - }, }); for (let key in propertySchemasIndex){ @@ -44,12 +45,85 @@ function assertPropertyEditPermission(property, userId){ const insertProperty = new ValidatedMethod({ name: 'CreatureProperties.methods.insert', validate: null, - run(creatureProperty) { - assertPropertyEditPermission(creatureProperty, this.userId); - return CreatureProperties.insert(creatureProperty); + run({creatureProperty, parentRef}) { + // TODO insert a property with the correct ancestry and order + //assertPropertyEditPermission(creatureProperty, this.userId); + //return CreatureProperties.insert(creatureProperty); + // TODO trigger a recalculation of the creature }, }); +const insertPropertyFromLibraryNode = new ValidatedMethod({ + name: 'CreatureProperties.methods.insertPropertyFromLibraryNode', + validate: new SimpleSchema({ + nodeId: { + type: String, + regEx: SimpleSchema.RegEx.Id, + }, + parentRef: { + type: RefSchema, + }, + }).validator(), + run({nodeId, parentRef}) { + // get the new ancestry for the properties + let {parentDoc, ancestors} = getAncestry({parentRef}); + + // Check permission to edit + if (parentRef.collection === 'creatures'){ + assertEditPermission(parentDoc, this.userId); + } else if (parentRef.collection === 'creatureProperties'){ + assertPropertyEditPermission(parentDoc, this.userId); + } else { + throw `${parentRef.collection} is not a valid parent collection` + } + + // Fetch the library node and its decendents, provided they have not been + // removed + let node = LibraryNodes.findOne({ + _id: nodeId, + removed: {$ne: true}, + }); + if (!node) throw `Node not found for nodeId: ${nodeId}`; + let oldParent = node.parent; + let nodes = LibraryNodes.find({ + 'ancestors.id': nodeId, + removed: {$ne: true}, + }).fetch(); + // The root node is last in the array of nodes + nodes.push(node); + + // re-map all the ancestors + setLineageOfDocs({ + docArray: nodes, + newAncestry: ancestors, + oldParent, + }); + + // Give the docs new IDs without breaking internal references + renewDocIds({ + docArray: nodes, + collectionMap: {'libraryNodes': 'creatureProperties'} + }); + + // Order the root node + setDocToLastOrder({ + collection: CreatureProperties, + doc: node, + }); + + // Insert the creature properties + let docId; + nodes.forEach(doc => { + docId = CreatureProperties.insert(doc); + }); + + // TODO trigger a recalculation of the creature + + // Return the docId of the last property, the inserted root property + return docId; + }, +}) + /* const adjustAttribute = new ValidatedMethod({ name: 'Attributes.methods.adjust', @@ -97,4 +171,4 @@ const adjustAttribute = new ValidatedMethod({ */ export default CreatureProperties; -export { CreaturePropertySchema, insertProperty }; +export { CreaturePropertySchema, insertProperty, insertPropertyFromLibraryNode }; diff --git a/app/imports/api/parenting/order.js b/app/imports/api/parenting/order.js index cab63b09..1a43258b 100644 --- a/app/imports/api/parenting/order.js +++ b/app/imports/api/parenting/order.js @@ -26,6 +26,7 @@ export function compareOrder(docA, docB){ // Go through their ancestors after the root, and find the first order // difference + // TODO ancestors don't store order yet let i, difference; const length = Math.min(docA.ancestors.length, docB.ancestors.length); for (i = 1; i < length; i++){ diff --git a/app/imports/api/parenting/parenting.js b/app/imports/api/parenting/parenting.js index a622394a..33ea860d 100644 --- a/app/imports/api/parenting/parenting.js +++ b/app/imports/api/parenting/parenting.js @@ -94,6 +94,46 @@ export function getAncestry({parentRef, inheritedFields = {}}){ return {parentDoc, parent, ancestors}; } +export function setLineageOfDocs({docArray, oldParent, newAncestry}){ + //const oldParent = oldAncestry[oldAncestry.length - 1]; + const newParent = newAncestry[newAncestry.length - 1]; + docArray.forEach(doc => { + if(doc.parent.id === oldParent.id){ + doc.parent = newParent; + } + let oldAncestors = doc.ancestors; + let oldParentIndex = oldAncestors.findIndex(a => a.id === oldParent.id); + doc.ancestors = [...newAncestry, ...oldAncestors.slice(oldParentIndex + 1)]; + }); +} + + +/** + * Give documents new random ids and transform their references. + * Transform collections of re-IDed docs according to the collection map + */ +export function renewDocIds({docArray, collectionMap}){ + // map of {oldId: newId} + let idMap = {}; + // Give new ids and map the changes + docArray.forEach(doc => { + let oldId = doc._id; + let newId = Random.id(); + doc._id = newId; + idMap[oldId] = newId; + }); + const remapReference = ref => { + if (idMap[ref.id]){ + ref.id = idMap[ref.id]; + ref.collection = collectionMap[ref.collection] || ref.collection; + } + } + docArray.forEach(doc => { + remapReference(doc.parent); + doc.ancestors.forEach(remapReference); + }); +} + export function updateParent({docRef, parentRef}){ let collection = getCollectionByName(docRef.collection); let oldDoc = fetchDocByRef(docRef, {fields: { diff --git a/app/imports/ui/components/tree/TreeNode.vue b/app/imports/ui/components/tree/TreeNode.vue index 529f0389..909ea221 100644 --- a/app/imports/ui/components/tree/TreeNode.vue +++ b/app/imports/ui/components/tree/TreeNode.vue @@ -14,7 +14,7 @@ small icon :class="showExpanded ? 'rotate-90' : null" @click.stop="expanded = !expanded" - :disabled="!hasChildren && !organize" + :disabled="!hasChildren && !organize || !canExpand" > chevron_right diff --git a/app/imports/ui/creature/character/TreeTab.vue b/app/imports/ui/creature/character/TreeTab.vue index 462f6659..002a3b97 100644 --- a/app/imports/ui/creature/character/TreeTab.vue +++ b/app/imports/ui/creature/character/TreeTab.vue @@ -80,7 +80,10 @@