diff --git a/app/imports/api/creature/CreatureProperties.js b/app/imports/api/creature/CreatureProperties.js index 5e74aeec..39c8361a 100644 --- a/app/imports/api/creature/CreatureProperties.js +++ b/app/imports/api/creature/CreatureProperties.js @@ -1,16 +1,15 @@ import SimpleSchema from 'simpl-schema'; import ChildSchema from '/imports/api/parenting/ChildSchema.js'; -import propertySchemas from '/imports/api/properties/propertySchemas.js'; -import Libraries from '/imports/api/library/Libraries.js'; +import Creature from '/imports/api/creature/Creatures.js'; import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js'; -import getModifierFields from '/imports/api/getModifierFields.js'; +import propertySchemasIndex from '/imports/api/properties/propertySchemasIndex.js'; let CreatureProperties = new Mongo.Collection('creatureProperties'); let CreaturePropertySchema = new SimpleSchema({ type: { type: String, - allowedValues: Object.keys(propertySchemas), + allowedValues: Object.keys(propertySchemasIndex), }, charId: { type: String, @@ -20,9 +19,9 @@ let CreaturePropertySchema = new SimpleSchema({ }, }); -for (let key in propertySchemas){ +for (let key in propertySchemasIndex){ let schema = new SimpleSchema({}); - schema.extend(propertySchemas[key]); + schema.extend(propertySchemasIndex[key]); schema.extend(CreaturePropertySchema); schema.extend(ChildSchema); CreatureProperties.attachSchema(schema, { @@ -30,6 +29,28 @@ for (let key in propertySchemas){ }); } +function getCreature(property){ + if (!property) throw new Meteor.Error('No property provided'); + let creature = Creatures.findOne(property.ancestors[0].id); + if (!creature) throw new Meteor.Error('Creature does not exist'); + return creature; +} + +function assertPropertyEditPermission(property, userId){ + let creature = getCreature(property); + return assertEditPermission(creature, userId); +} + +const insertProperty = new ValidatedMethod({ + name: 'CreatureProperties.methods.insert', + validate: null, + run(creatureProperty) { + assertPropertyEditPermission(creatureProperty, this.userId); + return CreatureProperties.insert(creatureProperty); + }, +}); + +/* const adjustAttribute = new ValidatedMethod({ name: 'Attributes.methods.adjust', mixins: [ @@ -73,6 +94,7 @@ const adjustAttribute = new ValidatedMethod({ } }, }); +*/ export default CreatureProperties; -export { CreaturePropertySchema}; +export { CreaturePropertySchema, insertProperty }; diff --git a/app/imports/api/creature/Creatures.js b/app/imports/api/creature/Creatures.js index b3b2f394..6820a880 100644 --- a/app/imports/api/creature/Creatures.js +++ b/app/imports/api/creature/Creatures.js @@ -50,38 +50,10 @@ let CreatureSchema = new SimpleSchema({ type: String, optional: true }, - race: { - type: String, - optional: true - }, picture: { type: String, optional: true }, - description: { - type: String, - optional: true - }, - personality: { - type: String, - optional: true - }, - ideals: { - type: String, - optional: true - }, - bonds: { - type: String, - optional: true - }, - flaws: { - type: String, - optional: true - }, - backstory: { - type: String, - optional: true - }, // Mechanics deathSave: { @@ -105,8 +77,6 @@ let CreatureSchema = new SimpleSchema({ defaultValue: "pc", allowedValues: ["pc", "npc", "monster"], }, - - // Computed variables: { type: Object, blackbox: true, diff --git a/app/imports/api/library/LibraryNodes.js b/app/imports/api/library/LibraryNodes.js index e9baef40..1651f57c 100644 --- a/app/imports/api/library/LibraryNodes.js +++ b/app/imports/api/library/LibraryNodes.js @@ -1,6 +1,6 @@ import SimpleSchema from 'simpl-schema'; import ChildSchema from '/imports/api/parenting/ChildSchema.js'; -import librarySchemas from '/imports/api/library/librarySchemas.js'; +import propertySchemasIndex from '/imports/api/properties/propertySchemasIndex.js'; import Libraries from '/imports/api/library/Libraries.js'; import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js'; import { softRemove } from '/imports/api/parenting/softRemove.js'; @@ -11,14 +11,14 @@ let LibraryNodes = new Mongo.Collection('libraryNodes'); let LibraryNodeSchema = new SimpleSchema({ type: { type: String, - allowedValues: Object.keys(librarySchemas), + allowedValues: Object.keys(propertySchemasIndex), }, }); -for (let key in librarySchemas){ +for (let key in propertySchemasIndex){ let schema = new SimpleSchema({}); schema.extend(LibraryNodeSchema); - schema.extend(librarySchemas[key]); + schema.extend(propertySchemasIndex[key]); schema.extend(ChildSchema); schema.extend(SoftRemovableSchema); LibraryNodes.attachSchema(schema, { @@ -112,36 +112,6 @@ const softRemoveLibraryNode = new ValidatedMethod({ } }); -function libraryNodesToTree(ancestorId){ - // Store a dict of all the nodes - let nodeIndex = {}; - let nodeList = []; - LibraryNodes.find({ - 'ancestors.id': ancestorId, - removed: {$ne: true}, - }, { - sort: {order: 1} - }).forEach( node => { - let treeNode = { - node: node, - children: [], - }; - nodeIndex[node._id] = treeNode; - nodeList.push(treeNode); - }); - // Create a forest of trees - let forest = []; - // Either the node is a child of another node, or in the forest as a root - nodeList.forEach(node => { - if (nodeIndex[node.node.parent.id]){ - nodeIndex[node.node.parent.id].children.push(node); - } else { - forest.push(node); - } - }); - return forest; -} - export default LibraryNodes; export { LibraryNodeSchema, diff --git a/app/imports/api/parenting/parenting.js b/app/imports/api/parenting/parenting.js index 5fc34263..a622394a 100644 --- a/app/imports/api/parenting/parenting.js +++ b/app/imports/api/parenting/parenting.js @@ -165,3 +165,33 @@ export function getName(doc){ if (ancestors[i].name) return ancestors[i].name; } } + +export function nodesToTree({collection, ancestorId}){ + // Store a dict of all the nodes + let nodeIndex = {}; + let nodeList = []; + collection.find({ + 'ancestors.id': ancestorId, + removed: {$ne: true}, + }, { + sort: {order: 1} + }).forEach( node => { + let treeNode = { + node: node, + children: [], + }; + nodeIndex[node._id] = treeNode; + nodeList.push(treeNode); + }); + // Create a forest of trees + let forest = []; + // Either the node is a child of another node, or in the forest as a root + nodeList.forEach(node => { + if (nodeIndex[node.node.parent.id]){ + nodeIndex[node.node.parent.id].children.push(node); + } else { + forest.push(node); + } + }); + return forest; +} diff --git a/app/imports/api/properties/propertySchemas.js b/app/imports/api/properties/propertySchemas.js deleted file mode 100644 index 0f45e1ca..00000000 --- a/app/imports/api/properties/propertySchemas.js +++ /dev/null @@ -1,43 +0,0 @@ -import { ActionSchema } from '/imports/api/properties/Actions.js'; -import { AttackSchema } from '/imports/api/properties/Attacks.js'; -import { AttributeSchema } from '/imports/api/properties/Attributes.js'; -import { StoredBuffSchema } from '/imports/api/properties/Buffs.js'; -import { ClassLevelSchema } from '/imports/api/properties/ClassLevels.js'; -import { DamageMultiplierSchema } from '/imports/api/properties/DamageMultipliers.js'; -import { EffectSchema } from '/imports/api/properties/Effects.js'; -import { ExperienceSchema } from '/imports/api/properties/Experiences.js'; -import { FeatureSchema } from '/imports/api/properties/Features.js'; -import { FolderSchema } from '/imports/api/properties/Folders.js'; -import { NoteSchema } from '/imports/api/properties/Notes.js'; -import { ProficiencySchema } from '/imports/api/properties/Proficiencies.js'; -import { RollSchema } from '/imports/api/properties/Rolls.js'; -import { SavingThrowSchema } from '/imports/api/properties/SavingThrows.js'; -import { SkillSchema } from '/imports/api/properties/Skills.js'; -import { SpellListSchema } from '/imports/api/properties/SpellLists.js'; -import { SpellSchema } from '/imports/api/properties/Spells.js'; -import { ContainerSchema } from '/imports/api/properties/Containers.js'; -import { ItemSchema } from '/imports/api/properties/Items.js'; - -const propertySchemas = { - action: ActionSchema, - attack: AttackSchema, - attribute: AttributeSchema, - buff: StoredBuffSchema, - classLevel: ClassLevelSchema, - damageMultiplier: DamageMultiplierSchema, - effect: EffectSchema, - experience: ExperienceSchema, - feature: FeatureSchema, - folder: FolderSchema, - note: NoteSchema, - proficiency: ProficiencySchema, - roll: RollSchema, - savingThrow: SavingThrowSchema, - skill: SkillSchema, - spellList: SpellListSchema, - spell: SpellSchema, - container: ContainerSchema, - item: ItemSchema, -}; - -export default propertySchemas; diff --git a/app/imports/api/library/librarySchemas.js b/app/imports/api/properties/propertySchemasIndex.js similarity index 96% rename from app/imports/api/library/librarySchemas.js rename to app/imports/api/properties/propertySchemasIndex.js index ea00d431..62e853fa 100644 --- a/app/imports/api/library/librarySchemas.js +++ b/app/imports/api/properties/propertySchemasIndex.js @@ -19,7 +19,7 @@ import { SpellSchema } from '/imports/api/properties/Spells.js'; import { ContainerSchema } from '/imports/api/properties/Containers.js'; import { ItemSchema } from '/imports/api/properties/Items.js'; -const librarySchemas = { +const propertySchemasIndex = { action: ActionSchema, attack: AttackSchema, attribute: AttributeSchema, @@ -42,4 +42,4 @@ const librarySchemas = { any: new SimpleSchema({}), }; -export default librarySchemas; +export default propertySchemasIndex; diff --git a/app/imports/server/publications/singleCharacter.js b/app/imports/server/publications/singleCharacter.js index 33fa3622..3faf9d2f 100644 --- a/app/imports/server/publications/singleCharacter.js +++ b/app/imports/server/publications/singleCharacter.js @@ -1,7 +1,7 @@ -import Creatures from "/imports/api/creature/Creatures.js"; -import creatureCollections from '/imports/api/creature/creatureCollections.js'; +import Creatures from '/imports/api/creature/Creatures.js'; +import CreatureProperties from '/imports/api/creature/CreatureProperties.js'; -Meteor.publish("singleCharacter", function(charId){ +Meteor.publish('singleCharacter', function(charId){ userId = this.userId; var char = Creatures.findOne({ _id: charId, @@ -9,15 +9,15 @@ Meteor.publish("singleCharacter", function(charId){ {readers: userId}, {writers: userId}, {owner: userId}, - {"settings.viewPermission": "public"}, + {'settings.viewPermission': 'public'}, ], }); if (char){ return [ Creatures.find({_id: charId}), - ...creatureCollections.map( - collection => collection.find({charId}) - ) + CreatureProperties.find({ + 'ancestors.id': charId, + }), ]; } else { return []; @@ -25,8 +25,8 @@ Meteor.publish("singleCharacter", function(charId){ }); DDPRateLimiter.addRule({ - name: "singleCharacter", - type: "subscription", + name: 'singleCharacter', + type: 'subscription', userId: null, connectionId(){ return true; }, }, 8, 10000, function(reply, ruleInput){ @@ -35,7 +35,7 @@ DDPRateLimiter.addRule({ } }); -Meteor.publish("singleCharacterName", function(charId){ +Meteor.publish('singleCharacterName', function(charId){ userId = this.userId; return Creatures.find({ _id: charId, @@ -43,9 +43,9 @@ Meteor.publish("singleCharacterName", function(charId){ {readers: userId}, {writers: userId}, {owner: userId}, - {"settings.viewPermission": "public"}, + {'settings.viewPermission': 'public'}, ], }, { - fields:{"name": 1} + fields:{'name': 1} }); }); diff --git a/app/imports/ui/creature/CreatureTreeContainer.vue b/app/imports/ui/creature/CreatureTreeContainer.vue new file mode 100644 index 00000000..0d5e9130 --- /dev/null +++ b/app/imports/ui/creature/CreatureTreeContainer.vue @@ -0,0 +1,81 @@ + + + $emit('selected', e)" + @reordered="reordered" + @reorganized="reorganized" + /> + + + + + + diff --git a/app/imports/ui/creature/character/CharacterSheet.vue b/app/imports/ui/creature/character/CharacterSheet.vue index 2ae3ac72..d549c6a1 100644 --- a/app/imports/ui/creature/character/CharacterSheet.vue +++ b/app/imports/ui/creature/character/CharacterSheet.vue @@ -13,12 +13,6 @@ v-model="tab" centered > - - Stats - - - Features - Tree @@ -26,14 +20,8 @@ - - - - - - - + @@ -48,20 +36,16 @@ import isDarkColor from '/imports/ui/utility/isDarkColor.js'; import { mapMutations } from "vuex"; import { theme } from '/imports/ui/theme.js'; - import StatsTab from '/imports/ui/creature/character/StatsTab.vue'; - import FeaturesTab from '/imports/ui/creature/character/FeaturesTab.vue'; - import CharacterTreeView from '/imports/ui/creature/character/CharacterTreeView.vue'; + import TreeTab from '/imports/ui/creature/character/TreeTab.vue'; import { recomputeCreature } from '/imports/api/creature/creatureComputation.js' export default { props: { showMenuButton: Boolean, - charId: String, + creatureId: String, }, components: { - StatsTab, - FeaturesTab, - CharacterTreeView, + TreeTab, }, data(){return { theme, @@ -71,19 +55,19 @@ ...mapMutations([ "toggleDrawer", ]), - recompute(charId){ - recomputeCreature.call({charId}); + recompute(creatureId){ + recomputeCreature.call({creatureId}); }, isDarkColor, }, meteor: { $subscribe: { 'singleCharacter'(){ - return [this.charId]; + return [this.creatureId]; }, }, character(){ - return Creatures.findOne(this.charId) || {}; + return Creatures.findOne(this.creatureId) || {}; }, }, } diff --git a/app/imports/ui/creature/character/CharacterTreeView.vue b/app/imports/ui/creature/character/CharacterTreeView.vue deleted file mode 100644 index 5f8c5049..00000000 --- a/app/imports/ui/creature/character/CharacterTreeView.vue +++ /dev/null @@ -1,86 +0,0 @@ - - - - {{item.collection}}: - - {{item.name}} {{item.value}} - - - {{item.name}}: {{item.stat}} {{item.operation}} {{item.result}} - - - {{item.name}} - - - - - - - - diff --git a/app/imports/ui/creature/character/TreeTab.vue b/app/imports/ui/creature/character/TreeTab.vue new file mode 100644 index 00000000..4194a1c1 --- /dev/null +++ b/app/imports/ui/creature/character/TreeTab.vue @@ -0,0 +1,109 @@ + + + + + + + + + selected = e" + :selected-node-id="selected" + /> + + + + + + + {{getPropertyName(selectedProperty && selectedProperty.type)}} + + + + create + + + + + + + + + add + + + + + + + diff --git a/app/imports/ui/creature/creatureProperties/CreaturePropertyCreationDialog.vue b/app/imports/ui/creature/creatureProperties/CreaturePropertyCreationDialog.vue new file mode 100644 index 00000000..cc5e720a --- /dev/null +++ b/app/imports/ui/creature/creatureProperties/CreaturePropertyCreationDialog.vue @@ -0,0 +1,31 @@ + + + + + + + + + diff --git a/app/imports/ui/creature/creatureProperties/CreaturePropertyInsertForm.vue b/app/imports/ui/creature/creatureProperties/CreaturePropertyInsertForm.vue new file mode 100644 index 00000000..e48bc2fe --- /dev/null +++ b/app/imports/ui/creature/creatureProperties/CreaturePropertyInsertForm.vue @@ -0,0 +1,64 @@ + + + Add {{propertyName}} + + + Insert + + + + + + + diff --git a/app/imports/ui/dialogStack/DialogComponentIndex.js b/app/imports/ui/dialogStack/DialogComponentIndex.js index 5a63f32f..8f8884b1 100644 --- a/app/imports/ui/dialogStack/DialogComponentIndex.js +++ b/app/imports/ui/dialogStack/DialogComponentIndex.js @@ -1,6 +1,7 @@ import AttributeDialog from '/imports/ui/properties/attributes/AttributeDialog.vue'; import AttributeDialogContainer from '/imports/ui/properties/attributes/AttributeDialogContainer.vue'; import AttributeCreationDialog from '/imports/ui/properties/attributes/AttributeCreationDialog.vue'; +import CreaturePropertyCreationDialog from '/imports/ui/creature/creatureProperties/CreaturePropertyCreationDialog.vue'; import FeatureCreationDialog from '/imports/ui/properties/features/FeatureCreationDialog.vue'; import FeatureDialogContainer from '/imports/ui/properties/features/FeatureDialogContainer.vue'; import LibraryCreationDialog from '/imports/ui/library/LibraryCreationDialog.vue'; @@ -12,6 +13,7 @@ export default { AttributeDialog, AttributeDialogContainer, AttributeCreationDialog, + CreaturePropertyCreationDialog, FeatureCreationDialog, FeatureDialogContainer, LibraryCreationDialog, diff --git a/app/imports/ui/library/LibraryContentsContainer.vue b/app/imports/ui/library/LibraryContentsContainer.vue index aa02157e..4b29834e 100644 --- a/app/imports/ui/library/LibraryContentsContainer.vue +++ b/app/imports/ui/library/LibraryContentsContainer.vue @@ -15,7 +15,8 @@ diff --git a/app/imports/ui/library/LibraryNodeEditDialog.vue b/app/imports/ui/library/LibraryNodeEditDialog.vue index c869b05c..8b57150a 100644 --- a/app/imports/ui/library/LibraryNodeEditDialog.vue +++ b/app/imports/ui/library/LibraryNodeEditDialog.vue @@ -39,7 +39,6 @@ pullFromLibraryNode, softRemoveLibraryNode, } from '/imports/api/library/LibraryNodes.js'; - import librarySchemas from '/imports/api/library/librarySchemas.js'; import DialogBase from '/imports/ui/dialogStack/DialogBase.vue'; import { getPropertyName } from '/imports/constants/PROPERTIES.js'; import PropertyIcon from '/imports/ui/properties/PropertyIcon.vue'; diff --git a/app/imports/ui/library/LibraryNodeInsertForm.vue b/app/imports/ui/library/LibraryNodeInsertForm.vue index d1b38cb8..4612ef24 100644 --- a/app/imports/ui/library/LibraryNodeInsertForm.vue +++ b/app/imports/ui/library/LibraryNodeInsertForm.vue @@ -26,10 +26,11 @@ + + diff --git a/app/imports/ui/router.js b/app/imports/ui/router.js index 2a25cf16..dba5c69f 100644 --- a/app/imports/ui/router.js +++ b/app/imports/ui/router.js @@ -31,8 +31,7 @@ RouterFactory.configure(factory => { component: Home, },{ path: '/characterList', - //component: CharacterList, - component: NotImplemented, + component: CharacterList, },{ path: '/library', component: Libraries, @@ -41,12 +40,11 @@ RouterFactory.configure(factory => { component: Library, },{ path: '/character/:id/:urlName', - //component: CharacterSheetPage, - component: NotImplemented, + component: CharacterSheetPage, + //component: NotImplemented, },{ path: '/character/:id', - //component: CharacterSheetPage, - component: NotImplemented, + component: CharacterSheetPage, },{ path: '/sign-in', component: SignIn,