Compare commits
21 Commits
2.0-beta.3
...
2.0-beta.3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f043c41e12 | ||
|
|
0277de76c4 | ||
|
|
ffc3211ff9 | ||
|
|
8162c76185 | ||
|
|
e21586e9ce | ||
|
|
4c2155d8ff | ||
|
|
44cc46ed22 | ||
|
|
d2b5d5f01d | ||
|
|
4492c47b00 | ||
|
|
6600cea9fa | ||
|
|
741a9b080a | ||
|
|
b041db22e4 | ||
|
|
a465e2ce87 | ||
|
|
8ecefb03ad | ||
|
|
9f62a78eb0 | ||
|
|
16e2b1249f | ||
|
|
a35f9221a2 | ||
|
|
6364549d50 | ||
|
|
d999fb46a7 | ||
|
|
ec01a2adb5 | ||
|
|
1f64558100 |
@@ -9,6 +9,8 @@ import { recomputeCreatureByDoc } from '/imports/api/creature/computation/method
|
|||||||
import { doActionWork } from '/imports/api/creature/actions/doAction.js';
|
import { doActionWork } from '/imports/api/creature/actions/doAction.js';
|
||||||
import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor.js';
|
import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor.js';
|
||||||
import getAncestorContext from '/imports/api/creature/actions/getAncestorContext.js';
|
import getAncestorContext from '/imports/api/creature/actions/getAncestorContext.js';
|
||||||
|
import recomputeInventory from '/imports/api/creature/denormalise/recomputeInventory';
|
||||||
|
import recomputeInactiveProperties from '/imports/api/creature/denormalise/recomputeInactiveProperties';
|
||||||
|
|
||||||
const castSpellWithSlot = new ValidatedMethod({
|
const castSpellWithSlot = new ValidatedMethod({
|
||||||
name: 'creatureProperties.castSpellWithSlot',
|
name: 'creatureProperties.castSpellWithSlot',
|
||||||
@@ -64,16 +66,24 @@ const castSpellWithSlot = new ValidatedMethod({
|
|||||||
}
|
}
|
||||||
let actionContext = getAncestorContext(spell);
|
let actionContext = getAncestorContext(spell);
|
||||||
|
|
||||||
doActionWork({
|
doActionWork({
|
||||||
action: spell,
|
action: spell,
|
||||||
actionContext: {slotLevel, ...actionContext},
|
actionContext: {slotLevel, ...actionContext},
|
||||||
creature,
|
creature,
|
||||||
targets: target ? [target] : [],
|
targets: target ? [target] : [],
|
||||||
method: this,
|
method: this,
|
||||||
});
|
});
|
||||||
// Note this only recomputes the top-level creature, not the nearest one
|
|
||||||
recomputeCreatureByDoc(creature);
|
// Note these lines only recompute the top-level creature, not the nearest one
|
||||||
|
// The acting creature might have a new item
|
||||||
|
recomputeInventory(creature._id);
|
||||||
|
// The spell might add properties which need to be activated
|
||||||
|
recomputeInactiveProperties(creature._id);
|
||||||
|
recomputeCreatureByDoc(creature);
|
||||||
|
|
||||||
if (target){
|
if (target){
|
||||||
|
recomputeInventory(target._id);
|
||||||
|
recomputeInactiveProperties(target._id);
|
||||||
recomputeCreatureByDoc(target);
|
recomputeCreatureByDoc(target);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import SimpleSchema from 'simpl-schema';
|
import SimpleSchema from 'simpl-schema';
|
||||||
|
import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS.js';
|
||||||
|
|
||||||
let CreatureFolders = new Mongo.Collection('creatureFolders');
|
let CreatureFolders = new Mongo.Collection('creatureFolders');
|
||||||
|
|
||||||
@@ -7,6 +8,7 @@ let creatureFolderSchema = new SimpleSchema({
|
|||||||
type: String,
|
type: String,
|
||||||
trim: false,
|
trim: false,
|
||||||
optional: true,
|
optional: true,
|
||||||
|
max: STORAGE_LIMITS.name,
|
||||||
},
|
},
|
||||||
creatures: {
|
creatures: {
|
||||||
type: Array,
|
type: Array,
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import ChildSchema from '/imports/api/parenting/ChildSchema.js';
|
|||||||
import SoftRemovableSchema from '/imports/api/parenting/SoftRemovableSchema.js';
|
import SoftRemovableSchema from '/imports/api/parenting/SoftRemovableSchema.js';
|
||||||
import propertySchemasIndex from '/imports/api/properties/computedPropertySchemasIndex.js';
|
import propertySchemasIndex from '/imports/api/properties/computedPropertySchemasIndex.js';
|
||||||
import { storedIconsSchema } from '/imports/api/icons/Icons.js';
|
import { storedIconsSchema } from '/imports/api/icons/Icons.js';
|
||||||
|
import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS.js';
|
||||||
|
|
||||||
let CreatureProperties = new Mongo.Collection('creatureProperties');
|
let CreatureProperties = new Mongo.Collection('creatureProperties');
|
||||||
|
|
||||||
@@ -16,9 +17,11 @@ let CreaturePropertySchema = new SimpleSchema({
|
|||||||
tags: {
|
tags: {
|
||||||
type: Array,
|
type: Array,
|
||||||
defaultValue: [],
|
defaultValue: [],
|
||||||
|
maxCount: STORAGE_LIMITS.tagCount,
|
||||||
},
|
},
|
||||||
'tags.$': {
|
'tags.$': {
|
||||||
type: String,
|
type: String,
|
||||||
|
max: STORAGE_LIMITS.tagLength,
|
||||||
},
|
},
|
||||||
disabled: {
|
disabled: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
@@ -27,6 +30,13 @@ let CreaturePropertySchema = new SimpleSchema({
|
|||||||
icon: {
|
icon: {
|
||||||
type: storedIconsSchema,
|
type: storedIconsSchema,
|
||||||
optional: true,
|
optional: true,
|
||||||
|
max: STORAGE_LIMITS.icon,
|
||||||
|
},
|
||||||
|
// Reference to the library node that this property was copied from
|
||||||
|
libraryNodeId: {
|
||||||
|
type: String,
|
||||||
|
regEx: SimpleSchema.RegEx.Id,
|
||||||
|
optional: true,
|
||||||
},
|
},
|
||||||
// Denormalised flag if this property is inactive on the sheet for any reason
|
// Denormalised flag if this property is inactive on the sheet for any reason
|
||||||
// Including being disabled, or a decendent of a disabled property
|
// Including being disabled, or a decendent of a disabled property
|
||||||
|
|||||||
@@ -0,0 +1,44 @@
|
|||||||
|
export default function getSlotFillFilter({slot, libraryIds}){
|
||||||
|
let filter = {
|
||||||
|
removed: {$ne: true},
|
||||||
|
$and: []
|
||||||
|
};
|
||||||
|
if (libraryIds){
|
||||||
|
filter['ancestors.id'] = {$in: libraryIds};
|
||||||
|
}
|
||||||
|
if (slot.slotType){
|
||||||
|
filter.$and.push({
|
||||||
|
$or: [{
|
||||||
|
type: slot.slotType
|
||||||
|
},{
|
||||||
|
type: 'slotFiller',
|
||||||
|
slotFillerType: slot.slotType,
|
||||||
|
}]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
let tagsOr = [];
|
||||||
|
let tagsNor = [];
|
||||||
|
if (slot.slotTags && slot.slotTags.length){
|
||||||
|
tagsOr.push({tags: {$all: slot.slotTags}});
|
||||||
|
}
|
||||||
|
if (slot.extraTags && slot.extraTags.length){
|
||||||
|
slot.extraTags.forEach(extra => {
|
||||||
|
if (!extra.tags || !extra.tags.length) return;
|
||||||
|
if (extra.operation === 'OR'){
|
||||||
|
tagsOr.push({tags: {$all: extra.tags}});
|
||||||
|
} else if (extra.operation === 'NOT'){
|
||||||
|
tagsNor.push({tags: {$all: extra.tags}});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (tagsOr.length){
|
||||||
|
filter.$and.push({$or: tagsOr});
|
||||||
|
}
|
||||||
|
if (tagsNor.length){
|
||||||
|
filter.$and.push({$nor: tagsNor});
|
||||||
|
}
|
||||||
|
if (!filter.$and.length){
|
||||||
|
delete filter.$and;
|
||||||
|
}
|
||||||
|
return filter;
|
||||||
|
}
|
||||||
@@ -107,6 +107,9 @@ function insertPropertyFromNode(nodeId, ancestors, order){
|
|||||||
// It must get the first generated ID to prevent flickering
|
// It must get the first generated ID to prevent flickering
|
||||||
nodes = [node, ...nodes];
|
nodes = [node, ...nodes];
|
||||||
|
|
||||||
|
// set libraryNodeIds
|
||||||
|
storeLibraryNodeReferences(nodes, nodeId);
|
||||||
|
|
||||||
// re-map all the ancestors
|
// re-map all the ancestors
|
||||||
setLineageOfDocs({
|
setLineageOfDocs({
|
||||||
docArray: nodes,
|
docArray: nodes,
|
||||||
@@ -135,6 +138,13 @@ function insertPropertyFromNode(nodeId, ancestors, order){
|
|||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function storeLibraryNodeReferences(nodes){
|
||||||
|
nodes.forEach(node => {
|
||||||
|
node.libraryNodeId = node._id;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Covert node references into actual nodes
|
// Covert node references into actual nodes
|
||||||
// TODO: check permissions for each library a reference node references
|
// TODO: check permissions for each library a reference node references
|
||||||
function reifyNodeReferences(nodes, visitedRefs = new Set(), depth = 0){
|
function reifyNodeReferences(nodes, visitedRefs = new Set(), depth = 0){
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import CreatureProperties from '/imports/api/creature/creatureProperties/Creatur
|
|||||||
import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js';
|
import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js';
|
||||||
import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor.js';
|
import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor.js';
|
||||||
import { recomputeCreatureByDoc } from '/imports/api/creature/computation/methods/recomputeCreature.js';
|
import { recomputeCreatureByDoc } from '/imports/api/creature/computation/methods/recomputeCreature.js';
|
||||||
|
import { get } from 'lodash';
|
||||||
|
|
||||||
const pushToProperty = new ValidatedMethod({
|
const pushToProperty = new ValidatedMethod({
|
||||||
name: 'creatureProperties.push',
|
name: 'creatureProperties.push',
|
||||||
@@ -19,9 +20,26 @@ const pushToProperty = new ValidatedMethod({
|
|||||||
let rootCreature = getRootCreatureAncestor(property);
|
let rootCreature = getRootCreatureAncestor(property);
|
||||||
assertEditPermission(rootCreature, this.userId);
|
assertEditPermission(rootCreature, this.userId);
|
||||||
|
|
||||||
|
let joinedPath = path.join('.');
|
||||||
|
|
||||||
|
// Respect maxCount
|
||||||
|
let schema = CreatureProperties.simpleSchema(property);
|
||||||
|
let maxCount = schema.get(joinedPath, 'maxCount');
|
||||||
|
|
||||||
|
if (Number.isFinite(maxCount)){
|
||||||
|
let array = get(property, path);
|
||||||
|
let currentCount = array ? array.length : 0;
|
||||||
|
if (currentCount >= maxCount){
|
||||||
|
throw new Meteor.Error(
|
||||||
|
'Array is full',
|
||||||
|
`Cannot have more than ${maxCount} values`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Do work
|
// Do work
|
||||||
CreatureProperties.update(_id, {
|
CreatureProperties.update(_id, {
|
||||||
$push: {[path.join('.')]: value},
|
$push: {[joinedPath]: value},
|
||||||
}, {
|
}, {
|
||||||
selector: {type: property.type},
|
selector: {type: property.type},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import SimpleSchema from 'simpl-schema';
|
|||||||
import deathSaveSchema from '/imports/api/properties/subSchemas/DeathSavesSchema.js'
|
import deathSaveSchema from '/imports/api/properties/subSchemas/DeathSavesSchema.js'
|
||||||
import ColorSchema from '/imports/api/properties/subSchemas/ColorSchema.js';
|
import ColorSchema from '/imports/api/properties/subSchemas/ColorSchema.js';
|
||||||
import SharingSchema from '/imports/api/sharing/SharingSchema.js';
|
import SharingSchema from '/imports/api/sharing/SharingSchema.js';
|
||||||
|
import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS.js';
|
||||||
|
|
||||||
//set up the collection for creatures
|
//set up the collection for creatures
|
||||||
let Creatures = new Mongo.Collection('creatures');
|
let Creatures = new Mongo.Collection('creatures');
|
||||||
@@ -47,7 +48,7 @@ let CreatureSettingsSchema = new SimpleSchema({
|
|||||||
discordWebhook: {
|
discordWebhook: {
|
||||||
type: String,
|
type: String,
|
||||||
optional: true,
|
optional: true,
|
||||||
max: 200,
|
max: STORAGE_LIMITS.url,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -57,22 +58,27 @@ let CreatureSchema = new SimpleSchema({
|
|||||||
type: String,
|
type: String,
|
||||||
defaultValue: '',
|
defaultValue: '',
|
||||||
optional: true,
|
optional: true,
|
||||||
|
max: STORAGE_LIMITS.name,
|
||||||
},
|
},
|
||||||
alignment: {
|
alignment: {
|
||||||
type: String,
|
type: String,
|
||||||
optional: true
|
optional: true,
|
||||||
|
max: STORAGE_LIMITS.name,
|
||||||
},
|
},
|
||||||
gender: {
|
gender: {
|
||||||
type: String,
|
type: String,
|
||||||
optional: true
|
optional: true,
|
||||||
|
max: STORAGE_LIMITS.name,
|
||||||
},
|
},
|
||||||
picture: {
|
picture: {
|
||||||
type: String,
|
type: String,
|
||||||
optional: true
|
optional: true,
|
||||||
|
max: STORAGE_LIMITS.url,
|
||||||
},
|
},
|
||||||
avatarPicture: {
|
avatarPicture: {
|
||||||
type: String,
|
type: String,
|
||||||
optional: true,
|
optional: true,
|
||||||
|
max: STORAGE_LIMITS.url,
|
||||||
},
|
},
|
||||||
// Mechanics
|
// Mechanics
|
||||||
deathSave: {
|
deathSave: {
|
||||||
|
|||||||
5
app/imports/api/creature/creatures/getCreatureUrlName.js
Normal file
5
app/imports/api/creature/creatures/getCreatureUrlName.js
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
import getSlug from 'speakingurl';
|
||||||
|
|
||||||
|
export default function getCreatureUrlName({name}){
|
||||||
|
return getSlug(name, {maintainCase: true}) || '-';
|
||||||
|
}
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
import '/imports/api/creature/creatures/methods/insertCreature.js';
|
import '/imports/api/creature/creatures/methods/insertCreature.js';
|
||||||
import '/imports/api/creature/creatures/methods/removeCreature.js';
|
import '/imports/api/creature/creatures/methods/removeCreature.js';
|
||||||
import '/imports/api/creature/creatures/methods/restCreature.js';
|
import '/imports/api/creature/creatures/methods/restCreature.js';
|
||||||
import '/imports/api/creature/creatures/methods/transferCreatureOwnership.js';
|
|
||||||
import '/imports/api/creature/creatures/methods/updateCreature.js';
|
import '/imports/api/creature/creatures/methods/updateCreature.js';
|
||||||
|
|||||||
@@ -1,55 +0,0 @@
|
|||||||
import SimpleSchema from 'simpl-schema';
|
|
||||||
import { ValidatedMethod } from 'meteor/mdg:validated-method';
|
|
||||||
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
|
|
||||||
import Creatures from '/imports/api/creature/creatures/Creatures.js';
|
|
||||||
import { assertOwnership } from '/imports/api/creature/creatures/creaturePermissions.js';
|
|
||||||
import { getUserTier } from '/imports/api/users/patreon/tiers.js';
|
|
||||||
|
|
||||||
const transferCreatureOwnership = new ValidatedMethod({
|
|
||||||
|
|
||||||
name: 'creatures.methods.transferOwnership',
|
|
||||||
|
|
||||||
validate: new SimpleSchema({
|
|
||||||
creatureId: {
|
|
||||||
type: String,
|
|
||||||
regEx: SimpleSchema.RegEx.Id,
|
|
||||||
},
|
|
||||||
userId: {
|
|
||||||
type: String,
|
|
||||||
regEx: SimpleSchema.RegEx.Id,
|
|
||||||
},
|
|
||||||
}).validator(),
|
|
||||||
|
|
||||||
mixins: [RateLimiterMixin],
|
|
||||||
rateLimit: {
|
|
||||||
numRequests: 5,
|
|
||||||
timeInterval: 5000,
|
|
||||||
},
|
|
||||||
|
|
||||||
run({creatureId, userId}) {
|
|
||||||
assertOwnership(creatureId, this.userId);
|
|
||||||
|
|
||||||
let tier = getUserTier(userId);
|
|
||||||
let currentCharacterCount = Creatures.find({
|
|
||||||
owner: userId,
|
|
||||||
}, {
|
|
||||||
fields: {_id: 1},
|
|
||||||
}).count();
|
|
||||||
|
|
||||||
if (
|
|
||||||
tier.characterSlots !== -1 &&
|
|
||||||
currentCharacterCount >= tier.characterSlots
|
|
||||||
){
|
|
||||||
throw new Meteor.Error('Creatures.methods.transferOwnership.denied',
|
|
||||||
'The new owner is already at their character limit')
|
|
||||||
}
|
|
||||||
|
|
||||||
Creatures.update(creatureId, {
|
|
||||||
$set: {owner: userId},
|
|
||||||
});
|
|
||||||
|
|
||||||
return creatureId;
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
export default transferCreatureOwnership;
|
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
import SimpleSchema from 'simpl-schema';
|
import SimpleSchema from 'simpl-schema';
|
||||||
import { ValidatedMethod } from 'meteor/mdg:validated-method';
|
import { ValidatedMethod } from 'meteor/mdg:validated-method';
|
||||||
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
|
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
|
||||||
import { getUserTier } from '/imports/api/users/patreon/tiers.js';
|
|
||||||
import { assertEditPermission } from '/imports/api/creature/creatures/creaturePermissions.js';
|
import { assertEditPermission } from '/imports/api/creature/creatures/creaturePermissions.js';
|
||||||
import Creatures from '/imports/api/creature/creatures/Creatures.js';
|
import Creatures from '/imports/api/creature/creatures/Creatures.js';
|
||||||
import { recomputeCreatureById } from '/imports/api/creature/computation/methods/recomputeCreature.js';
|
import { recomputeCreatureById } from '/imports/api/creature/computation/methods/recomputeCreature.js';
|
||||||
|
import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS.js';
|
||||||
|
|
||||||
let Experiences = new Mongo.Collection('experiences');
|
let Experiences = new Mongo.Collection('experiences');
|
||||||
|
|
||||||
@@ -12,6 +12,7 @@ let ExperienceSchema = new SimpleSchema({
|
|||||||
name: {
|
name: {
|
||||||
type: String,
|
type: String,
|
||||||
optional: true,
|
optional: true,
|
||||||
|
max: STORAGE_LIMITS.name,
|
||||||
},
|
},
|
||||||
// The amount of XP this experience gives
|
// The amount of XP this experience gives
|
||||||
xp: {
|
xp: {
|
||||||
@@ -90,11 +91,6 @@ const insertExperience = new ValidatedMethod({
|
|||||||
throw new Meteor.Error('Experiences.methods.insert.denied',
|
throw new Meteor.Error('Experiences.methods.insert.denied',
|
||||||
'You need to be logged in to insert an experience');
|
'You need to be logged in to insert an experience');
|
||||||
}
|
}
|
||||||
let tier = getUserTier(this.userId);
|
|
||||||
if (!tier.paidBenefits){
|
|
||||||
throw new Meteor.Error('Experiences.methods.insert.denied',
|
|
||||||
`The ${tier.name} tier does not allow you to grant experience`);
|
|
||||||
}
|
|
||||||
let insertedIds = [];
|
let insertedIds = [];
|
||||||
creatureIds.forEach(creatureId => {
|
creatureIds.forEach(creatureId => {
|
||||||
let id = insertExperienceForCreature({experience, creatureId, userId});
|
let id = insertExperienceForCreature({experience, creatureId, userId});
|
||||||
@@ -123,11 +119,6 @@ const removeExperience = new ValidatedMethod({
|
|||||||
throw new Meteor.Error('Experiences.methods.remove.denied',
|
throw new Meteor.Error('Experiences.methods.remove.denied',
|
||||||
'You need to be logged in to remove an experience');
|
'You need to be logged in to remove an experience');
|
||||||
}
|
}
|
||||||
let tier = getUserTier(this.userId);
|
|
||||||
if (!tier.paidBenefits){
|
|
||||||
throw new Meteor.Error('Experiences.methods.remove.denied',
|
|
||||||
`The ${tier.name} tier does not allow you to remove an experience`);
|
|
||||||
}
|
|
||||||
let experience = Experiences.findOne(experienceId);
|
let experience = Experiences.findOne(experienceId);
|
||||||
if (!experience) return;
|
if (!experience) return;
|
||||||
let creatureId = experience.creatureId
|
let creatureId = experience.creatureId
|
||||||
@@ -168,11 +159,6 @@ const recomputeExperiences = new ValidatedMethod({
|
|||||||
throw new Meteor.Error('Experiences.methods.recompute.denied',
|
throw new Meteor.Error('Experiences.methods.recompute.denied',
|
||||||
'You need to be logged in to recompute a creature\'s experiences');
|
'You need to be logged in to recompute a creature\'s experiences');
|
||||||
}
|
}
|
||||||
let tier = getUserTier(this.userId);
|
|
||||||
if (!tier.paidBenefits){
|
|
||||||
throw new Meteor.Error('Experiences.methods.recompute.denied',
|
|
||||||
`The ${tier.name} tier does not allow you to recompute a creature's experiences`);
|
|
||||||
}
|
|
||||||
assertEditPermission(creatureId, userId);
|
assertEditPermission(creatureId, userId);
|
||||||
|
|
||||||
let xp = 0;
|
let xp = 0;
|
||||||
|
|||||||
@@ -1,14 +1,17 @@
|
|||||||
import SimpleSchema from 'simpl-schema';
|
import SimpleSchema from 'simpl-schema';
|
||||||
|
import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS.js';
|
||||||
|
|
||||||
let ExperienceSchema = new SimpleSchema({
|
let ExperienceSchema = new SimpleSchema({
|
||||||
title: {
|
title: {
|
||||||
type: String,
|
type: String,
|
||||||
optional: true,
|
optional: true,
|
||||||
|
max: STORAGE_LIMITS.name,
|
||||||
},
|
},
|
||||||
// Potentially long description of the event
|
// Potentially long description of the event
|
||||||
description: {
|
description: {
|
||||||
type: String,
|
type: String,
|
||||||
optional: true,
|
optional: true,
|
||||||
|
max: STORAGE_LIMITS.description,
|
||||||
},
|
},
|
||||||
// The real-world date that it occured
|
// The real-world date that it occured
|
||||||
date: {
|
date: {
|
||||||
@@ -24,14 +27,17 @@ let ExperienceSchema = new SimpleSchema({
|
|||||||
worldDate: {
|
worldDate: {
|
||||||
type: String,
|
type: String,
|
||||||
optional: true,
|
optional: true,
|
||||||
|
max: STORAGE_LIMITS.name,
|
||||||
},
|
},
|
||||||
// Tags to better find this entry later
|
// Tags to better find this entry later
|
||||||
tags: {
|
tags: {
|
||||||
type: Array,
|
type: Array,
|
||||||
defaultValue: [],
|
defaultValue: [],
|
||||||
|
maxCount: STORAGE_LIMITS.tagCount,
|
||||||
},
|
},
|
||||||
'tags.$': {
|
'tags.$': {
|
||||||
type: String,
|
type: String,
|
||||||
|
max: STORAGE_LIMITS.tagLength,
|
||||||
},
|
},
|
||||||
// ID of the journal this entry belongs to
|
// ID of the journal this entry belongs to
|
||||||
journalId: {
|
journalId: {
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import {
|
|||||||
prettifyParseError
|
prettifyParseError
|
||||||
} from '/imports/parser/parser.js';
|
} from '/imports/parser/parser.js';
|
||||||
const PER_CREATURE_LOG_LIMIT = 100;
|
const PER_CREATURE_LOG_LIMIT = 100;
|
||||||
|
import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS.js';
|
||||||
|
|
||||||
if (Meteor.isServer){
|
if (Meteor.isServer){
|
||||||
var sendWebhookAsCreature = require('/imports/server/discord/sendWebhook.js').sendWebhookAsCreature;
|
var sendWebhookAsCreature = require('/imports/server/discord/sendWebhook.js').sendWebhookAsCreature;
|
||||||
@@ -21,7 +22,7 @@ let CreatureLogSchema = new SimpleSchema({
|
|||||||
content: {
|
content: {
|
||||||
type: Array,
|
type: Array,
|
||||||
defaultValue: [],
|
defaultValue: [],
|
||||||
maxCount: 25,
|
maxCount: STORAGE_LIMITS.logContentCount,
|
||||||
},
|
},
|
||||||
'content.$': {
|
'content.$': {
|
||||||
type: LogContentSchema,
|
type: LogContentSchema,
|
||||||
@@ -45,6 +46,7 @@ let CreatureLogSchema = new SimpleSchema({
|
|||||||
creatureName: {
|
creatureName: {
|
||||||
type: String,
|
type: String,
|
||||||
optional: true,
|
optional: true,
|
||||||
|
max: STORAGE_LIMITS.name,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,18 +1,21 @@
|
|||||||
import SimpleSchema from 'simpl-schema';
|
import SimpleSchema from 'simpl-schema';
|
||||||
import ErrorSchema from '/imports/api/properties/subSchemas/ErrorSchema.js';
|
import ErrorSchema from '/imports/api/properties/subSchemas/ErrorSchema.js';
|
||||||
import RollDetailsSchema from '/imports/api/properties/subSchemas/RollDetailsSchema.js';
|
import RollDetailsSchema from '/imports/api/properties/subSchemas/RollDetailsSchema.js';
|
||||||
|
import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS.js';
|
||||||
|
|
||||||
let LogContentSchema = new SimpleSchema({
|
let LogContentSchema = new SimpleSchema({
|
||||||
// The name of the field, included in discord webhook message
|
// The name of the field, included in discord webhook message
|
||||||
name: {
|
name: {
|
||||||
type: String,
|
type: String,
|
||||||
optional: true,
|
optional: true,
|
||||||
|
max: STORAGE_LIMITS.name,
|
||||||
},
|
},
|
||||||
// The details of the field, included in discord webhook message
|
// The details of the field, included in discord webhook message
|
||||||
// Markdown support
|
// Markdown support
|
||||||
value: {
|
value: {
|
||||||
type: String,
|
type: String,
|
||||||
optional: true,
|
optional: true,
|
||||||
|
max: STORAGE_LIMITS.summary,
|
||||||
},
|
},
|
||||||
context: {
|
context: {
|
||||||
type: Object,
|
type: Object,
|
||||||
@@ -21,6 +24,7 @@ let LogContentSchema = new SimpleSchema({
|
|||||||
'context.errors':{
|
'context.errors':{
|
||||||
type: Array,
|
type: Array,
|
||||||
defaultValue: [],
|
defaultValue: [],
|
||||||
|
maxCount: STORAGE_LIMITS.errorCount,
|
||||||
},
|
},
|
||||||
'context.errors.$': {
|
'context.errors.$': {
|
||||||
type: ErrorSchema,
|
type: ErrorSchema,
|
||||||
@@ -28,6 +32,7 @@ let LogContentSchema = new SimpleSchema({
|
|||||||
'context.rolls': {
|
'context.rolls': {
|
||||||
type: Array,
|
type: Array,
|
||||||
defaultValue: [],
|
defaultValue: [],
|
||||||
|
maxCount: STORAGE_LIMITS.rollCount,
|
||||||
},
|
},
|
||||||
'context.rolls.$': {
|
'context.rolls.$': {
|
||||||
type: RollDetailsSchema,
|
type: RollDetailsSchema,
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import SimpleSchema from 'simpl-schema';
|
|||||||
import { ValidatedMethod } from 'meteor/mdg:validated-method';
|
import { ValidatedMethod } from 'meteor/mdg:validated-method';
|
||||||
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
|
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
|
||||||
import { assertAdmin } from '/imports/api/sharing/sharingPermissions.js';
|
import { assertAdmin } from '/imports/api/sharing/sharingPermissions.js';
|
||||||
|
import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS.js';
|
||||||
|
|
||||||
let Icons = new Mongo.Collection('icons');
|
let Icons = new Mongo.Collection('icons');
|
||||||
|
|
||||||
@@ -9,22 +10,27 @@ let iconsSchema = new SimpleSchema({
|
|||||||
name: {
|
name: {
|
||||||
type: String,
|
type: String,
|
||||||
unique: true,
|
unique: true,
|
||||||
|
max: STORAGE_LIMITS.name,
|
||||||
index: 1,
|
index: 1,
|
||||||
},
|
},
|
||||||
description: {
|
description: {
|
||||||
type: String,
|
type: String,
|
||||||
optional: true,
|
optional: true,
|
||||||
|
max: STORAGE_LIMITS.description,
|
||||||
},
|
},
|
||||||
tags: {
|
tags: {
|
||||||
type: Array,
|
type: Array,
|
||||||
optional: true,
|
optional: true,
|
||||||
|
maxCount: STORAGE_LIMITS.tagCount,
|
||||||
index: 1,
|
index: 1,
|
||||||
},
|
},
|
||||||
'tags.$': {
|
'tags.$': {
|
||||||
type: String,
|
type: String,
|
||||||
|
max: STORAGE_LIMITS.tagLength,
|
||||||
},
|
},
|
||||||
shape: {
|
shape: {
|
||||||
type: String,
|
type: String,
|
||||||
|
max: STORAGE_LIMITS.icon,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import simpleSchemaMixin from '/imports/api/creature/mixins/simpleSchemaMixin.js
|
|||||||
import { assertEditPermission, assertOwnership } from '/imports/api/sharing/sharingPermissions.js';
|
import { assertEditPermission, assertOwnership } from '/imports/api/sharing/sharingPermissions.js';
|
||||||
import LibraryNodes from '/imports/api/library/LibraryNodes.js';
|
import LibraryNodes from '/imports/api/library/LibraryNodes.js';
|
||||||
import { getUserTier } from '/imports/api/users/patreon/tiers.js'
|
import { getUserTier } from '/imports/api/users/patreon/tiers.js'
|
||||||
|
import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Libraries are trees of library nodes where each node represents a character
|
* Libraries are trees of library nodes where each node represents a character
|
||||||
@@ -21,11 +22,8 @@ let Libraries = new Mongo.Collection('libraries');
|
|||||||
let LibrarySchema = new SimpleSchema({
|
let LibrarySchema = new SimpleSchema({
|
||||||
name: {
|
name: {
|
||||||
type: String,
|
type: String,
|
||||||
|
max: STORAGE_LIMITS.name,
|
||||||
},
|
},
|
||||||
isDefault: {
|
|
||||||
type: Boolean,
|
|
||||||
optional: true,
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
LibrarySchema.extend(SharingSchema);
|
LibrarySchema.extend(SharingSchema);
|
||||||
@@ -39,7 +37,7 @@ const insertLibrary = new ValidatedMethod({
|
|||||||
mixins: [
|
mixins: [
|
||||||
simpleSchemaMixin,
|
simpleSchemaMixin,
|
||||||
],
|
],
|
||||||
schema: LibrarySchema.omit('owner', 'isDefault'),
|
schema: LibrarySchema.omit('owner'),
|
||||||
run(library) {
|
run(library) {
|
||||||
if (!this.userId) {
|
if (!this.userId) {
|
||||||
throw new Meteor.Error('Libraries.methods.insert.denied',
|
throw new Meteor.Error('Libraries.methods.insert.denied',
|
||||||
@@ -78,30 +76,6 @@ const updateLibraryName = new ValidatedMethod({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const setLibraryDefault = new ValidatedMethod({
|
|
||||||
name: 'libraries.makeLibraryDefault',
|
|
||||||
validate: new SimpleSchema({
|
|
||||||
_id: {
|
|
||||||
type: String,
|
|
||||||
regEx: SimpleSchema.RegEx.id
|
|
||||||
},
|
|
||||||
isDefault: {
|
|
||||||
type: Boolean,
|
|
||||||
},
|
|
||||||
}).validator(),
|
|
||||||
mixins: [RateLimiterMixin],
|
|
||||||
rateLimit: {
|
|
||||||
numRequests: 5,
|
|
||||||
timeInterval: 5000,
|
|
||||||
},
|
|
||||||
run({_id, isDefault}) {
|
|
||||||
if (!Meteor.users.isAdmin()){
|
|
||||||
throw new Meteor.Error('Permission denied', 'User must be admin to set libraries as default');
|
|
||||||
}
|
|
||||||
return Libraries.update(_id, {$set: {isDefault}});
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const removeLibrary = new ValidatedMethod({
|
const removeLibrary = new ValidatedMethod({
|
||||||
name: 'libraries.remove',
|
name: 'libraries.remove',
|
||||||
validate: new SimpleSchema({
|
validate: new SimpleSchema({
|
||||||
@@ -128,4 +102,4 @@ export function removeLibaryWork(libraryId){
|
|||||||
LibraryNodes.remove({'ancestors.id': libraryId});
|
LibraryNodes.remove({'ancestors.id': libraryId});
|
||||||
}
|
}
|
||||||
|
|
||||||
export { LibrarySchema, insertLibrary, setLibraryDefault, updateLibraryName, removeLibrary };
|
export { LibrarySchema, insertLibrary, updateLibraryName, removeLibrary };
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import SoftRemovableSchema from '/imports/api/parenting/SoftRemovableSchema.js';
|
|||||||
import { storedIconsSchema } from '/imports/api/icons/Icons.js';
|
import { storedIconsSchema } from '/imports/api/icons/Icons.js';
|
||||||
import '/imports/api/library/methods/index.js';
|
import '/imports/api/library/methods/index.js';
|
||||||
import { updateReferenceNodeWork } from '/imports/api/library/methods/updateReferenceNode.js';
|
import { updateReferenceNodeWork } from '/imports/api/library/methods/updateReferenceNode.js';
|
||||||
|
import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS.js';
|
||||||
|
|
||||||
let LibraryNodes = new Mongo.Collection('libraryNodes');
|
let LibraryNodes = new Mongo.Collection('libraryNodes');
|
||||||
|
|
||||||
@@ -24,13 +25,16 @@ let LibraryNodeSchema = new SimpleSchema({
|
|||||||
tags: {
|
tags: {
|
||||||
type: Array,
|
type: Array,
|
||||||
defaultValue: [],
|
defaultValue: [],
|
||||||
|
maxCount: STORAGE_LIMITS.tagCount,
|
||||||
},
|
},
|
||||||
'tags.$': {
|
'tags.$': {
|
||||||
type: String,
|
type: String,
|
||||||
|
max: STORAGE_LIMITS.tagLength,
|
||||||
},
|
},
|
||||||
icon: {
|
icon: {
|
||||||
type: storedIconsSchema,
|
type: storedIconsSchema,
|
||||||
optional: true,
|
optional: true,
|
||||||
|
max: STORAGE_LIMITS.icon,
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +0,0 @@
|
|||||||
import SimpleSchema from 'simpl-schema';
|
|
||||||
|
|
||||||
Blacklist = new Mongo.Collection("blacklist");
|
|
||||||
|
|
||||||
Schemas.Blacklist = new SimpleSchema({
|
|
||||||
userId: {
|
|
||||||
type: String,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
Blacklist.attachSchema(Schemas.Blacklist);
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
import SimpleSchema from 'simpl-schema';
|
|
||||||
|
|
||||||
ChangeLogs = new Mongo.Collection("changeLogs");
|
|
||||||
|
|
||||||
Schemas.ChangeLog = new SimpleSchema({
|
|
||||||
version: {
|
|
||||||
type: String,
|
|
||||||
},
|
|
||||||
changes: {
|
|
||||||
type: [String],
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
ChangeLogs.attachSchema(Schemas.ChangeLog);
|
|
||||||
|
|
||||||
ChangeLogs.allow({
|
|
||||||
insert: function(userId, doc) {
|
|
||||||
var user = Meteor.users.findOne(userId);
|
|
||||||
if (user) return _.contains(user.roles, "admin");
|
|
||||||
},
|
|
||||||
update: function(userId, doc, fields, modifier) {
|
|
||||||
var user = Meteor.users.findOne(userId);
|
|
||||||
if (user) return _.contains(user.roles, "admin");
|
|
||||||
},
|
|
||||||
remove: function(userId, doc) {
|
|
||||||
var user = Meteor.users.findOne(userId);
|
|
||||||
if (user) return _.contains(user.roles, "admin");
|
|
||||||
},
|
|
||||||
});
|
|
||||||
@@ -1,81 +0,0 @@
|
|||||||
import SimpleSchema from 'simpl-schema';
|
|
||||||
|
|
||||||
Reports = new Mongo.Collection("reports");
|
|
||||||
|
|
||||||
Schemas.Report = new SimpleSchema({
|
|
||||||
owner: {
|
|
||||||
type: String,
|
|
||||||
regEx: SimpleSchema.RegEx.Id,
|
|
||||||
},
|
|
||||||
title: {
|
|
||||||
type: String,
|
|
||||||
trim: false,
|
|
||||||
optional: true,
|
|
||||||
},
|
|
||||||
description: {
|
|
||||||
type: String,
|
|
||||||
trim: false,
|
|
||||||
optional: true,
|
|
||||||
},
|
|
||||||
type: {
|
|
||||||
type: String,
|
|
||||||
allowedValues: ["General Feedback", "Bug", "Suggested Change", "Feature Request"],
|
|
||||||
defaultValue: "General Feedback",
|
|
||||||
},
|
|
||||||
//the immediate impact of doing this action (eg. -1 rages)
|
|
||||||
severity: {
|
|
||||||
type: SimpleSchema.Integer,
|
|
||||||
defaultValue: 5,
|
|
||||||
min: 1,
|
|
||||||
max: 10,
|
|
||||||
},
|
|
||||||
metaData: {
|
|
||||||
type: Object,
|
|
||||||
blackbox: true,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
Reports.attachSchema(Schemas.Report);
|
|
||||||
|
|
||||||
Meteor.methods({
|
|
||||||
insertReport: function(report) {
|
|
||||||
check(report, {
|
|
||||||
title: String,
|
|
||||||
description: String,
|
|
||||||
type: String,
|
|
||||||
severity: Number,
|
|
||||||
metaData: Object,
|
|
||||||
});
|
|
||||||
report.owner = this.userId;
|
|
||||||
var id = Reports.insert(report);
|
|
||||||
var user = Meteor.users.findOne(this.userId);
|
|
||||||
var sender = user &&
|
|
||||||
user.emails &&
|
|
||||||
user.emails[0] &&
|
|
||||||
user.emails[0].address ||
|
|
||||||
user.services &&
|
|
||||||
user.services.google &&
|
|
||||||
user.services.google.email ||
|
|
||||||
"reports@dicecloud.com";
|
|
||||||
var bodyText = "Report ID: " + id +
|
|
||||||
"\nSeverity: " + report.severity +
|
|
||||||
"\nType: " + report.type +
|
|
||||||
"\n\n" + report.description;
|
|
||||||
Email.send({
|
|
||||||
from: sender,
|
|
||||||
to: "stefan.zermatten@gmail.com",
|
|
||||||
subject: "DiceCloud feedback - " + report.title,
|
|
||||||
text: bodyText,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
deleteReport: function(id) {
|
|
||||||
var user = Meteor.users.findOne(this.userId);
|
|
||||||
if (!_.contains(user.roles, "admin")){
|
|
||||||
throw new Meteor.Error(
|
|
||||||
"not admin",
|
|
||||||
"The user must be an administrator to delete feedback"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
Reports.remove(id);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
import SimpleSchema from 'simpl-schema';
|
import SimpleSchema from 'simpl-schema';
|
||||||
|
import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS.js';
|
||||||
|
|
||||||
const RefSchema = new SimpleSchema({
|
const RefSchema = new SimpleSchema({
|
||||||
id: {
|
id: {
|
||||||
@@ -12,7 +13,8 @@ const RefSchema = new SimpleSchema({
|
|||||||
index: 1
|
index: 1
|
||||||
},
|
},
|
||||||
collection: {
|
collection: {
|
||||||
type: String
|
type: String,
|
||||||
|
max: STORAGE_LIMITS.collectionName,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -27,7 +29,7 @@ let ChildSchema = new SimpleSchema({
|
|||||||
ancestors: {
|
ancestors: {
|
||||||
type: Array,
|
type: Array,
|
||||||
defaultValue: [],
|
defaultValue: [],
|
||||||
max: 100,
|
maxCount: STORAGE_LIMITS.ancestorCount,
|
||||||
},
|
},
|
||||||
'ancestors.$': {
|
'ancestors.$': {
|
||||||
type: RefSchema,
|
type: RefSchema,
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
import SimpleSchema from 'simpl-schema';
|
import SimpleSchema from 'simpl-schema';
|
||||||
import ErrorSchema from '/imports/api/properties/subSchemas/ErrorSchema.js';
|
import ErrorSchema from '/imports/api/properties/subSchemas/ErrorSchema.js';
|
||||||
import InlineComputationSchema from '/imports/api/properties/subSchemas/InlineComputationSchema.js';
|
import InlineComputationSchema from '/imports/api/properties/subSchemas/InlineComputationSchema.js';
|
||||||
import { storedIconsSchema } from '/imports/api/icons/Icons.js'
|
import { storedIconsSchema } from '/imports/api/icons/Icons.js';
|
||||||
|
import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS.js';
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Actions are things a character can do
|
* Actions are things a character can do
|
||||||
* Any rolls that are children of actions will be rolled when taking the action
|
* Any rolls that are children of actions will be rolled when taking the action
|
||||||
@@ -12,14 +14,17 @@ let ActionSchema = new SimpleSchema({
|
|||||||
name: {
|
name: {
|
||||||
type: String,
|
type: String,
|
||||||
optional: true,
|
optional: true,
|
||||||
|
max: STORAGE_LIMITS.name,
|
||||||
},
|
},
|
||||||
summary: {
|
summary: {
|
||||||
type: String,
|
type: String,
|
||||||
optional: true,
|
optional: true,
|
||||||
|
max: STORAGE_LIMITS.summary,
|
||||||
},
|
},
|
||||||
description: {
|
description: {
|
||||||
type: String,
|
type: String,
|
||||||
optional: true,
|
optional: true,
|
||||||
|
max: STORAGE_LIMITS.description,
|
||||||
},
|
},
|
||||||
// What time-resource is used to take the action in combat
|
// What time-resource is used to take the action in combat
|
||||||
// long actions take longer than 1 round to cast
|
// long actions take longer than 1 round to cast
|
||||||
@@ -38,13 +43,6 @@ let ActionSchema = new SimpleSchema({
|
|||||||
'multipleTargets',
|
'multipleTargets',
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
tags: {
|
|
||||||
type: Array,
|
|
||||||
defaultValue: [],
|
|
||||||
},
|
|
||||||
'tags.$': {
|
|
||||||
type: String,
|
|
||||||
},
|
|
||||||
// Duplicate the ResourceSchema here so we can extend it elegantly.
|
// Duplicate the ResourceSchema here so we can extend it elegantly.
|
||||||
resources: {
|
resources: {
|
||||||
type: Object,
|
type: Object,
|
||||||
@@ -53,6 +51,7 @@ let ActionSchema = new SimpleSchema({
|
|||||||
'resources.itemsConsumed': {
|
'resources.itemsConsumed': {
|
||||||
type: Array,
|
type: Array,
|
||||||
defaultValue: [],
|
defaultValue: [],
|
||||||
|
maxCount: STORAGE_LIMITS.resourcesCount,
|
||||||
},
|
},
|
||||||
'resources.itemsConsumed.$': {
|
'resources.itemsConsumed.$': {
|
||||||
type: Object,
|
type: Object,
|
||||||
@@ -67,6 +66,7 @@ let ActionSchema = new SimpleSchema({
|
|||||||
'resources.itemsConsumed.$.tag': {
|
'resources.itemsConsumed.$.tag': {
|
||||||
type: String,
|
type: String,
|
||||||
optional: true,
|
optional: true,
|
||||||
|
max: STORAGE_LIMITS.tagLength,
|
||||||
},
|
},
|
||||||
'resources.itemsConsumed.$.quantity': {
|
'resources.itemsConsumed.$.quantity': {
|
||||||
type: Number,
|
type: Number,
|
||||||
@@ -75,10 +75,12 @@ let ActionSchema = new SimpleSchema({
|
|||||||
'resources.itemsConsumed.$.itemId': {
|
'resources.itemsConsumed.$.itemId': {
|
||||||
type: String,
|
type: String,
|
||||||
optional: true,
|
optional: true,
|
||||||
|
max: STORAGE_LIMITS.name,
|
||||||
},
|
},
|
||||||
'resources.attributesConsumed': {
|
'resources.attributesConsumed': {
|
||||||
type: Array,
|
type: Array,
|
||||||
defaultValue: [],
|
defaultValue: [],
|
||||||
|
maxCount: STORAGE_LIMITS.resourcesCount,
|
||||||
},
|
},
|
||||||
'resources.attributesConsumed.$': {
|
'resources.attributesConsumed.$': {
|
||||||
type: Object,
|
type: Object,
|
||||||
@@ -93,6 +95,7 @@ let ActionSchema = new SimpleSchema({
|
|||||||
'resources.attributesConsumed.$.variableName': {
|
'resources.attributesConsumed.$.variableName': {
|
||||||
type: String,
|
type: String,
|
||||||
optional: true,
|
optional: true,
|
||||||
|
max: STORAGE_LIMITS.variableName,
|
||||||
},
|
},
|
||||||
'resources.attributesConsumed.$.quantity': {
|
'resources.attributesConsumed.$.quantity': {
|
||||||
type: Number,
|
type: Number,
|
||||||
@@ -102,6 +105,7 @@ let ActionSchema = new SimpleSchema({
|
|||||||
uses: {
|
uses: {
|
||||||
type: String,
|
type: String,
|
||||||
optional: true,
|
optional: true,
|
||||||
|
max: STORAGE_LIMITS.calculation,
|
||||||
},
|
},
|
||||||
// Integer of how many times it has already been used
|
// Integer of how many times it has already been used
|
||||||
usesUsed: {
|
usesUsed: {
|
||||||
@@ -120,14 +124,14 @@ const ComputedOnlyActionSchema = new SimpleSchema({
|
|||||||
summaryCalculations: {
|
summaryCalculations: {
|
||||||
type: Array,
|
type: Array,
|
||||||
defaultValue: [],
|
defaultValue: [],
|
||||||
maxCount: 32,
|
maxCount: STORAGE_LIMITS.inlineCalculationCount,
|
||||||
},
|
},
|
||||||
'summaryCalculations.$': InlineComputationSchema,
|
'summaryCalculations.$': InlineComputationSchema,
|
||||||
|
|
||||||
descriptionCalculations: {
|
descriptionCalculations: {
|
||||||
type: Array,
|
type: Array,
|
||||||
defaultValue: [],
|
defaultValue: [],
|
||||||
maxCount: 32,
|
maxCount: STORAGE_LIMITS.inlineCalculationCount,
|
||||||
},
|
},
|
||||||
'descriptionCalculations.$': InlineComputationSchema,
|
'descriptionCalculations.$': InlineComputationSchema,
|
||||||
|
|
||||||
@@ -138,6 +142,7 @@ const ComputedOnlyActionSchema = new SimpleSchema({
|
|||||||
usesErrors: {
|
usesErrors: {
|
||||||
type: Array,
|
type: Array,
|
||||||
optional: true,
|
optional: true,
|
||||||
|
maxCount: STORAGE_LIMITS.errorCount,
|
||||||
},
|
},
|
||||||
'usesErrors.$':{
|
'usesErrors.$':{
|
||||||
type: ErrorSchema,
|
type: ErrorSchema,
|
||||||
@@ -158,15 +163,18 @@ const ComputedOnlyActionSchema = new SimpleSchema({
|
|||||||
},
|
},
|
||||||
'resources.itemsConsumed.$.itemName': {
|
'resources.itemsConsumed.$.itemName': {
|
||||||
type: String,
|
type: String,
|
||||||
|
max: STORAGE_LIMITS.name,
|
||||||
optional: true,
|
optional: true,
|
||||||
},
|
},
|
||||||
'resources.itemsConsumed.$.itemIcon': {
|
'resources.itemsConsumed.$.itemIcon': {
|
||||||
type: storedIconsSchema,
|
type: storedIconsSchema,
|
||||||
optional: true,
|
optional: true,
|
||||||
|
max: STORAGE_LIMITS.icon,
|
||||||
},
|
},
|
||||||
'resources.itemsConsumed.$.itemColor': {
|
'resources.itemsConsumed.$.itemColor': {
|
||||||
type: String,
|
type: String,
|
||||||
optional: true,
|
optional: true,
|
||||||
|
max: STORAGE_LIMITS.color,
|
||||||
},
|
},
|
||||||
'resources.attributesConsumed': Array,
|
'resources.attributesConsumed': Array,
|
||||||
'resources.attributesConsumed.$': Object,
|
'resources.attributesConsumed.$': Object,
|
||||||
@@ -182,6 +190,7 @@ const ComputedOnlyActionSchema = new SimpleSchema({
|
|||||||
'resources.attributesConsumed.$.statName': {
|
'resources.attributesConsumed.$.statName': {
|
||||||
type: String,
|
type: String,
|
||||||
optional: true,
|
optional: true,
|
||||||
|
max: STORAGE_LIMITS.name,
|
||||||
},
|
},
|
||||||
// True if the uses left is zero, or any item or attribute consumed is
|
// True if the uses left is zero, or any item or attribute consumed is
|
||||||
// insufficient
|
// insufficient
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import SimpleSchema from 'simpl-schema';
|
import SimpleSchema from 'simpl-schema';
|
||||||
import ErrorSchema from '/imports/api/properties/subSchemas/ErrorSchema.js';
|
import ErrorSchema from '/imports/api/properties/subSchemas/ErrorSchema.js';
|
||||||
|
import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS.js';
|
||||||
|
|
||||||
const AdjustmentSchema = new SimpleSchema({
|
const AdjustmentSchema = new SimpleSchema({
|
||||||
// The roll that determines how much to change the attribute
|
// The roll that determines how much to change the attribute
|
||||||
@@ -8,6 +9,7 @@ const AdjustmentSchema = new SimpleSchema({
|
|||||||
type: String,
|
type: String,
|
||||||
optional: true,
|
optional: true,
|
||||||
defaultValue: '1',
|
defaultValue: '1',
|
||||||
|
max: STORAGE_LIMITS.calculation,
|
||||||
},
|
},
|
||||||
// Who this adjustment applies to
|
// Who this adjustment applies to
|
||||||
target: {
|
target: {
|
||||||
@@ -23,6 +25,7 @@ const AdjustmentSchema = new SimpleSchema({
|
|||||||
stat: {
|
stat: {
|
||||||
type: String,
|
type: String,
|
||||||
optional: true,
|
optional: true,
|
||||||
|
max: STORAGE_LIMITS.variableName,
|
||||||
},
|
},
|
||||||
operation: {
|
operation: {
|
||||||
type: String,
|
type: String,
|
||||||
@@ -39,6 +42,7 @@ const ComputedOnlyAdjustmentSchema = new SimpleSchema({
|
|||||||
amountErrors: {
|
amountErrors: {
|
||||||
type: Array,
|
type: Array,
|
||||||
optional: true,
|
optional: true,
|
||||||
|
maxCount: STORAGE_LIMITS.errorCount,
|
||||||
},
|
},
|
||||||
'amountErrors.$':{
|
'amountErrors.$':{
|
||||||
type: ErrorSchema,
|
type: ErrorSchema,
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import SimpleSchema from 'simpl-schema';
|
import SimpleSchema from 'simpl-schema';
|
||||||
import { ActionSchema, ComputedOnlyActionSchema } from '/imports/api/properties/Actions.js';
|
import { ActionSchema, ComputedOnlyActionSchema } from '/imports/api/properties/Actions.js';
|
||||||
import ErrorSchema from '/imports/api/properties/subSchemas/ErrorSchema.js';
|
import ErrorSchema from '/imports/api/properties/subSchemas/ErrorSchema.js';
|
||||||
|
import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS.js';
|
||||||
|
|
||||||
// Attacks are special instances of actions
|
// Attacks are special instances of actions
|
||||||
let AttackSchema = new SimpleSchema()
|
let AttackSchema = new SimpleSchema()
|
||||||
@@ -11,18 +12,22 @@ let AttackSchema = new SimpleSchema()
|
|||||||
type: String,
|
type: String,
|
||||||
defaultValue: 'strength.modifier + proficiencyBonus',
|
defaultValue: 'strength.modifier + proficiencyBonus',
|
||||||
optional: true,
|
optional: true,
|
||||||
|
max: STORAGE_LIMITS.calculation,
|
||||||
},
|
},
|
||||||
// Set better defaults for the action
|
// Set better defaults for the action
|
||||||
actionType: {
|
actionType: {
|
||||||
type: String,
|
type: String,
|
||||||
defaultValue: 'attack',
|
defaultValue: 'attack',
|
||||||
|
max: STORAGE_LIMITS.name,
|
||||||
},
|
},
|
||||||
tags: {
|
tags: {
|
||||||
type: Array,
|
type: Array,
|
||||||
defaultValue: ['attack'],
|
defaultValue: ['attack'],
|
||||||
|
maxCount: STORAGE_LIMITS.tagCount,
|
||||||
},
|
},
|
||||||
'tags.$': {
|
'tags.$': {
|
||||||
type: String,
|
type: String,
|
||||||
|
max: STORAGE_LIMITS.tagLength,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -36,6 +41,7 @@ const ComputedOnlyAttackSchema = new SimpleSchema()
|
|||||||
rollBonusErrors: {
|
rollBonusErrors: {
|
||||||
type: Array,
|
type: Array,
|
||||||
optional: true,
|
optional: true,
|
||||||
|
maxCount: STORAGE_LIMITS.errorCount,
|
||||||
},
|
},
|
||||||
'rollBonusErrors.$':{
|
'rollBonusErrors.$':{
|
||||||
type: ErrorSchema,
|
type: ErrorSchema,
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import SimpleSchema from 'simpl-schema';
|
|||||||
import ErrorSchema from '/imports/api/properties/subSchemas/ErrorSchema.js';
|
import ErrorSchema from '/imports/api/properties/subSchemas/ErrorSchema.js';
|
||||||
import VARIABLE_NAME_REGEX from '/imports/constants/VARIABLE_NAME_REGEX.js';
|
import VARIABLE_NAME_REGEX from '/imports/constants/VARIABLE_NAME_REGEX.js';
|
||||||
import InlineComputationSchema from '/imports/api/properties/subSchemas/InlineComputationSchema.js';
|
import InlineComputationSchema from '/imports/api/properties/subSchemas/InlineComputationSchema.js';
|
||||||
|
import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS.js';
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Attributes are numbered stats of a character
|
* Attributes are numbered stats of a character
|
||||||
@@ -10,6 +11,7 @@ let AttributeSchema = new SimpleSchema({
|
|||||||
name: {
|
name: {
|
||||||
type: String,
|
type: String,
|
||||||
defaultValue: 'New Attribute',
|
defaultValue: 'New Attribute',
|
||||||
|
max: STORAGE_LIMITS.name,
|
||||||
},
|
},
|
||||||
// The technical, lowercase, single-word name used in formulae
|
// The technical, lowercase, single-word name used in formulae
|
||||||
variableName: {
|
variableName: {
|
||||||
@@ -17,6 +19,7 @@ let AttributeSchema = new SimpleSchema({
|
|||||||
regEx: VARIABLE_NAME_REGEX,
|
regEx: VARIABLE_NAME_REGEX,
|
||||||
min: 2,
|
min: 2,
|
||||||
defaultValue: 'newAttribute',
|
defaultValue: 'newAttribute',
|
||||||
|
max: STORAGE_LIMITS.variableName,
|
||||||
},
|
},
|
||||||
// How it is displayed and computed is determined by type
|
// How it is displayed and computed is determined by type
|
||||||
attributeType: {
|
attributeType: {
|
||||||
@@ -45,16 +48,19 @@ let AttributeSchema = new SimpleSchema({
|
|||||||
spellSlotLevelCalculation: {
|
spellSlotLevelCalculation: {
|
||||||
type: String,
|
type: String,
|
||||||
optional: true,
|
optional: true,
|
||||||
|
max: STORAGE_LIMITS.calculation,
|
||||||
},
|
},
|
||||||
// The starting value, before effects
|
// The starting value, before effects
|
||||||
baseValueCalculation: {
|
baseValueCalculation: {
|
||||||
type: String,
|
type: String,
|
||||||
optional: true,
|
optional: true,
|
||||||
|
max: STORAGE_LIMITS.calculation,
|
||||||
},
|
},
|
||||||
// Description of what the attribute is used for
|
// Description of what the attribute is used for
|
||||||
description: {
|
description: {
|
||||||
type: String,
|
type: String,
|
||||||
optional: true,
|
optional: true,
|
||||||
|
max: STORAGE_LIMITS.description,
|
||||||
},
|
},
|
||||||
// The damage done to the attribute, always positive
|
// The damage done to the attribute, always positive
|
||||||
damage: {
|
damage: {
|
||||||
@@ -79,7 +85,7 @@ let ComputedOnlyAttributeSchema = new SimpleSchema({
|
|||||||
descriptionCalculations: {
|
descriptionCalculations: {
|
||||||
type: Array,
|
type: Array,
|
||||||
defaultValue: [],
|
defaultValue: [],
|
||||||
maxCount: 32,
|
maxCount: STORAGE_LIMITS.inlineCalculationCount,
|
||||||
},
|
},
|
||||||
'descriptionCalculations.$': InlineComputationSchema,
|
'descriptionCalculations.$': InlineComputationSchema,
|
||||||
// The result of baseValueCalculation
|
// The result of baseValueCalculation
|
||||||
@@ -93,6 +99,7 @@ let ComputedOnlyAttributeSchema = new SimpleSchema({
|
|||||||
},
|
},
|
||||||
'baseValueErrors.$': {
|
'baseValueErrors.$': {
|
||||||
type: ErrorSchema,
|
type: ErrorSchema,
|
||||||
|
maxCount: STORAGE_LIMITS.errorCount,
|
||||||
},
|
},
|
||||||
// The result of spellSlotLevelCalculation
|
// The result of spellSlotLevelCalculation
|
||||||
spellSlotLevelValue: {
|
spellSlotLevelValue: {
|
||||||
@@ -102,6 +109,7 @@ let ComputedOnlyAttributeSchema = new SimpleSchema({
|
|||||||
spellSlotLevelErrors: {
|
spellSlotLevelErrors: {
|
||||||
type: Array,
|
type: Array,
|
||||||
optional: true,
|
optional: true,
|
||||||
|
maxCount: STORAGE_LIMITS.errorCount,
|
||||||
},
|
},
|
||||||
'spellSlotLevelErrors.$': {
|
'spellSlotLevelErrors.$': {
|
||||||
type: ErrorSchema,
|
type: ErrorSchema,
|
||||||
|
|||||||
@@ -1,18 +1,22 @@
|
|||||||
import SimpleSchema from 'simpl-schema';
|
import SimpleSchema from 'simpl-schema';
|
||||||
import InlineComputationSchema from '/imports/api/properties/subSchemas/InlineComputationSchema.js';
|
import InlineComputationSchema from '/imports/api/properties/subSchemas/InlineComputationSchema.js';
|
||||||
|
import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS.js';
|
||||||
|
|
||||||
let BuffSchema = new SimpleSchema({
|
let BuffSchema = new SimpleSchema({
|
||||||
name: {
|
name: {
|
||||||
type: String,
|
type: String,
|
||||||
optional: true,
|
optional: true,
|
||||||
|
max: STORAGE_LIMITS.name,
|
||||||
},
|
},
|
||||||
description: {
|
description: {
|
||||||
type: String,
|
type: String,
|
||||||
optional: true,
|
optional: true,
|
||||||
|
max: STORAGE_LIMITS.description,
|
||||||
},
|
},
|
||||||
duration: {
|
duration: {
|
||||||
type: String,
|
type: String,
|
||||||
optional: true,
|
optional: true,
|
||||||
|
max: STORAGE_LIMITS.name,
|
||||||
},
|
},
|
||||||
applied: {
|
applied: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
@@ -34,7 +38,7 @@ let ComputedOnlyBuffSchema = new SimpleSchema({
|
|||||||
descriptionCalculations: {
|
descriptionCalculations: {
|
||||||
type: Array,
|
type: Array,
|
||||||
defaultValue: [],
|
defaultValue: [],
|
||||||
maxCount: 32,
|
maxCount: STORAGE_LIMITS.inlineCalculationCount,
|
||||||
},
|
},
|
||||||
'descriptionCalculations.$': InlineComputationSchema,
|
'descriptionCalculations.$': InlineComputationSchema,
|
||||||
durationSpent: {
|
durationSpent: {
|
||||||
@@ -48,6 +52,7 @@ let ComputedOnlyBuffSchema = new SimpleSchema({
|
|||||||
},
|
},
|
||||||
'appliedBy.name': {
|
'appliedBy.name': {
|
||||||
type: String,
|
type: String,
|
||||||
|
max: STORAGE_LIMITS.name,
|
||||||
},
|
},
|
||||||
'appliedBy.id': {
|
'appliedBy.id': {
|
||||||
type: String,
|
type: String,
|
||||||
@@ -55,6 +60,7 @@ let ComputedOnlyBuffSchema = new SimpleSchema({
|
|||||||
},
|
},
|
||||||
'appliedBy.collection': {
|
'appliedBy.collection': {
|
||||||
type: String,
|
type: String,
|
||||||
|
max: STORAGE_LIMITS.collectionName,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -1,21 +1,25 @@
|
|||||||
import SimpleSchema from 'simpl-schema';
|
import SimpleSchema from 'simpl-schema';
|
||||||
import VARIABLE_NAME_REGEX from '/imports/constants/VARIABLE_NAME_REGEX.js';
|
import VARIABLE_NAME_REGEX from '/imports/constants/VARIABLE_NAME_REGEX.js';
|
||||||
|
import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS.js';
|
||||||
|
|
||||||
let ClassLevelSchema = new SimpleSchema({
|
let ClassLevelSchema = new SimpleSchema({
|
||||||
name: {
|
name: {
|
||||||
type: String,
|
type: String,
|
||||||
optional: true,
|
optional: true,
|
||||||
|
max: STORAGE_LIMITS.name,
|
||||||
},
|
},
|
||||||
// Only used by slot filling dialog, not computed
|
// Only used by slot filling dialog, not computed
|
||||||
description: {
|
description: {
|
||||||
type: String,
|
type: String,
|
||||||
optional: true,
|
optional: true,
|
||||||
|
max: STORAGE_LIMITS.description,
|
||||||
},
|
},
|
||||||
// The name of this class level's variable
|
// The name of this class level's variable
|
||||||
variableName: {
|
variableName: {
|
||||||
type: String,
|
type: String,
|
||||||
min: 2,
|
min: 2,
|
||||||
regEx: VARIABLE_NAME_REGEX,
|
regEx: VARIABLE_NAME_REGEX,
|
||||||
|
max: STORAGE_LIMITS.variableName,
|
||||||
},
|
},
|
||||||
level: {
|
level: {
|
||||||
type: SimpleSchema.Integer,
|
type: SimpleSchema.Integer,
|
||||||
@@ -32,6 +36,7 @@ let ClassLevelSchema = new SimpleSchema({
|
|||||||
slotFillerCondition: {
|
slotFillerCondition: {
|
||||||
type: String,
|
type: String,
|
||||||
optional: true,
|
optional: true,
|
||||||
|
max: STORAGE_LIMITS.calculation,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ import {
|
|||||||
} from '/imports/parser/parser.js';
|
} from '/imports/parser/parser.js';
|
||||||
import AccessorNode from '/imports/parser/parseTree/AccessorNode.js';
|
import AccessorNode from '/imports/parser/parseTree/AccessorNode.js';
|
||||||
import SymbolNode from '/imports/parser/parseTree/SymbolNode.js';
|
import SymbolNode from '/imports/parser/parseTree/SymbolNode.js';
|
||||||
|
import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS.js';
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Constants are primitive values that can be used elsewhere in computations
|
* Constants are primitive values that can be used elsewhere in computations
|
||||||
*/
|
*/
|
||||||
@@ -15,6 +17,7 @@ let ConstantSchema = new SimpleSchema({
|
|||||||
name: {
|
name: {
|
||||||
type: String,
|
type: String,
|
||||||
optional: true,
|
optional: true,
|
||||||
|
max: STORAGE_LIMITS.name,
|
||||||
},
|
},
|
||||||
// The technical, lowercase, single-word name used in formulae
|
// The technical, lowercase, single-word name used in formulae
|
||||||
variableName: {
|
variableName: {
|
||||||
@@ -22,15 +25,18 @@ let ConstantSchema = new SimpleSchema({
|
|||||||
regEx: VARIABLE_NAME_REGEX,
|
regEx: VARIABLE_NAME_REGEX,
|
||||||
min: 2,
|
min: 2,
|
||||||
defaultValue: 'newConstant',
|
defaultValue: 'newConstant',
|
||||||
|
max: STORAGE_LIMITS.variableName,
|
||||||
},
|
},
|
||||||
// The input value to be parsed, must return a constant node or an array
|
// The input value to be parsed, must return a constant node or an array
|
||||||
// of constant nodes to be valid
|
// of constant nodes to be valid
|
||||||
calculation: {
|
calculation: {
|
||||||
type: String,
|
type: String,
|
||||||
optional: true,
|
optional: true,
|
||||||
|
max: STORAGE_LIMITS.calculation,
|
||||||
},
|
},
|
||||||
errors: {
|
errors: {
|
||||||
type: Array,
|
type: Array,
|
||||||
|
maxCount: STORAGE_LIMITS.errorCount,
|
||||||
autoValue(){
|
autoValue(){
|
||||||
let calc = this.field('calculation');
|
let calc = this.field('calculation');
|
||||||
if (!calc.isSet && this.isModifier) {
|
if (!calc.isSet && this.isModifier) {
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
import SimpleSchema from 'simpl-schema';
|
import SimpleSchema from 'simpl-schema';
|
||||||
import InlineComputationSchema from '/imports/api/properties/subSchemas/InlineComputationSchema.js';
|
import InlineComputationSchema from '/imports/api/properties/subSchemas/InlineComputationSchema.js';
|
||||||
|
import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS.js';
|
||||||
|
|
||||||
let ContainerSchema = new SimpleSchema({
|
let ContainerSchema = new SimpleSchema({
|
||||||
name: {
|
name: {
|
||||||
type: String,
|
type: String,
|
||||||
optional: true,
|
optional: true,
|
||||||
trim: false
|
trim: false,
|
||||||
|
max: STORAGE_LIMITS.name,
|
||||||
},
|
},
|
||||||
carried: {
|
carried: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
@@ -29,7 +31,8 @@ let ContainerSchema = new SimpleSchema({
|
|||||||
description: {
|
description: {
|
||||||
type: String,
|
type: String,
|
||||||
optional: true,
|
optional: true,
|
||||||
trim: false
|
trim: false,
|
||||||
|
max: STORAGE_LIMITS.description,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -37,7 +40,7 @@ const ComputedOnlyContainerSchema = new SimpleSchema({
|
|||||||
descriptionCalculations: {
|
descriptionCalculations: {
|
||||||
type: Array,
|
type: Array,
|
||||||
defaultValue: [],
|
defaultValue: [],
|
||||||
maxCount: 32,
|
maxCount: STORAGE_LIMITS.inlineCalculationCount,
|
||||||
},
|
},
|
||||||
'descriptionCalculations.$': InlineComputationSchema,
|
'descriptionCalculations.$': InlineComputationSchema,
|
||||||
// Weight of all the contents, zero if `contentsWeightless` is true
|
// Weight of all the contents, zero if `contentsWeightless` is true
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import SimpleSchema from 'simpl-schema';
|
import SimpleSchema from 'simpl-schema';
|
||||||
import DAMAGE_TYPES from '/imports/constants/DAMAGE_TYPES.js';
|
import DAMAGE_TYPES from '/imports/constants/DAMAGE_TYPES.js';
|
||||||
|
import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS.js';
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* DamageMultipliers are multipliers that affect how much damage is taken from
|
* DamageMultipliers are multipliers that affect how much damage is taken from
|
||||||
@@ -9,10 +10,12 @@ let DamageMultiplierSchema = new SimpleSchema({
|
|||||||
name: {
|
name: {
|
||||||
type: String,
|
type: String,
|
||||||
optional: true,
|
optional: true,
|
||||||
|
max: STORAGE_LIMITS.name,
|
||||||
},
|
},
|
||||||
damageTypes: {
|
damageTypes: {
|
||||||
type: Array,
|
type: Array,
|
||||||
defaultValue: [],
|
defaultValue: [],
|
||||||
|
maxCount: STORAGE_LIMITS.damageTypeCount,
|
||||||
},
|
},
|
||||||
// The technical, lowercase, single-word name used in formulae
|
// The technical, lowercase, single-word name used in formulae
|
||||||
'damageTypes.$': {
|
'damageTypes.$': {
|
||||||
@@ -29,17 +32,21 @@ let DamageMultiplierSchema = new SimpleSchema({
|
|||||||
excludeTags: {
|
excludeTags: {
|
||||||
type: Array,
|
type: Array,
|
||||||
defaultValue: [],
|
defaultValue: [],
|
||||||
|
maxCount: STORAGE_LIMITS.tagCount,
|
||||||
},
|
},
|
||||||
'excludeTags.$': {
|
'excludeTags.$': {
|
||||||
type: String,
|
type: String,
|
||||||
|
max: STORAGE_LIMITS.tagLength,
|
||||||
},
|
},
|
||||||
// Tags which must be present to be affected by this multiplier (AND)
|
// Tags which must be present to be affected by this multiplier (AND)
|
||||||
includeTags: {
|
includeTags: {
|
||||||
type: Array,
|
type: Array,
|
||||||
defaultValue: [],
|
defaultValue: [],
|
||||||
|
maxCount: STORAGE_LIMITS.tagCount,
|
||||||
},
|
},
|
||||||
'includeTags.$': {
|
'includeTags.$': {
|
||||||
type: String,
|
type: String,
|
||||||
|
max: STORAGE_LIMITS.tagLength,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import SimpleSchema from 'simpl-schema';
|
import SimpleSchema from 'simpl-schema';
|
||||||
import DAMAGE_TYPES from '/imports/constants/DAMAGE_TYPES.js';
|
import DAMAGE_TYPES from '/imports/constants/DAMAGE_TYPES.js';
|
||||||
import ErrorSchema from '/imports/api/properties/subSchemas/ErrorSchema.js';
|
import ErrorSchema from '/imports/api/properties/subSchemas/ErrorSchema.js';
|
||||||
|
import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS.js';
|
||||||
|
|
||||||
const DamageSchema = new SimpleSchema({
|
const DamageSchema = new SimpleSchema({
|
||||||
// The roll that determines how much to damage the attribute
|
// The roll that determines how much to damage the attribute
|
||||||
@@ -9,6 +10,7 @@ const DamageSchema = new SimpleSchema({
|
|||||||
type: String,
|
type: String,
|
||||||
optional: true,
|
optional: true,
|
||||||
defaultValue: '1d8 + strength.modifier',
|
defaultValue: '1d8 + strength.modifier',
|
||||||
|
max: STORAGE_LIMITS.calculation,
|
||||||
},
|
},
|
||||||
// Who this damage applies to
|
// Who this damage applies to
|
||||||
target: {
|
target: {
|
||||||
@@ -35,6 +37,7 @@ const ComputedOnlyDamageSchema = new SimpleSchema({
|
|||||||
amountErrors: {
|
amountErrors: {
|
||||||
type: Array,
|
type: Array,
|
||||||
optional: true,
|
optional: true,
|
||||||
|
maxCount: STORAGE_LIMITS.errorCount,
|
||||||
},
|
},
|
||||||
'amountErrors.$':{
|
'amountErrors.$':{
|
||||||
type: ErrorSchema,
|
type: ErrorSchema,
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
import SimpleSchema from 'simpl-schema';
|
import SimpleSchema from 'simpl-schema';
|
||||||
import ErrorSchema from '/imports/api/properties/subSchemas/ErrorSchema.js';
|
import ErrorSchema from '/imports/api/properties/subSchemas/ErrorSchema.js';
|
||||||
|
import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS.js';
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Effects are reason-value attached to skills and abilities
|
* Effects are reason-value attached to skills and abilities
|
||||||
* that modify their final value or presentation in some way
|
* that modify their final value or presentation in some way
|
||||||
@@ -8,6 +10,7 @@ let EffectSchema = new SimpleSchema({
|
|||||||
name: {
|
name: {
|
||||||
type: String,
|
type: String,
|
||||||
optional: true,
|
optional: true,
|
||||||
|
max: STORAGE_LIMITS.name,
|
||||||
},
|
},
|
||||||
operation: {
|
operation: {
|
||||||
type: String,
|
type: String,
|
||||||
@@ -30,14 +33,17 @@ let EffectSchema = new SimpleSchema({
|
|||||||
calculation: {
|
calculation: {
|
||||||
type: String,
|
type: String,
|
||||||
optional: true,
|
optional: true,
|
||||||
|
max: STORAGE_LIMITS.calculation,
|
||||||
},
|
},
|
||||||
//which stats the effect is applied to
|
//which stats the effect is applied to
|
||||||
stats: {
|
stats: {
|
||||||
type: Array,
|
type: Array,
|
||||||
defaultValue: [],
|
defaultValue: [],
|
||||||
|
maxCount: STORAGE_LIMITS.statsToTarget,
|
||||||
},
|
},
|
||||||
'stats.$': {
|
'stats.$': {
|
||||||
type: String,
|
type: String,
|
||||||
|
max: STORAGE_LIMITS.variableName,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -51,6 +57,7 @@ const ComputedOnlyEffectSchema = new SimpleSchema({
|
|||||||
errors: {
|
errors: {
|
||||||
type: Array,
|
type: Array,
|
||||||
optional: true,
|
optional: true,
|
||||||
|
maxCount: STORAGE_LIMITS.errorCount,
|
||||||
},
|
},
|
||||||
'errors.$':{
|
'errors.$':{
|
||||||
type: ErrorSchema,
|
type: ErrorSchema,
|
||||||
|
|||||||
@@ -1,17 +1,21 @@
|
|||||||
import SimpleSchema from 'simpl-schema';
|
import SimpleSchema from 'simpl-schema';
|
||||||
import InlineComputationSchema from '/imports/api/properties/subSchemas/InlineComputationSchema.js';
|
import InlineComputationSchema from '/imports/api/properties/subSchemas/InlineComputationSchema.js';
|
||||||
|
import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS.js';
|
||||||
|
|
||||||
let FeatureSchema = new SimpleSchema({
|
let FeatureSchema = new SimpleSchema({
|
||||||
name: {
|
name: {
|
||||||
type: String,
|
type: String,
|
||||||
|
max: STORAGE_LIMITS.name,
|
||||||
},
|
},
|
||||||
summary: {
|
summary: {
|
||||||
type: String,
|
type: String,
|
||||||
optional: true,
|
optional: true,
|
||||||
|
max: STORAGE_LIMITS.summary,
|
||||||
},
|
},
|
||||||
description: {
|
description: {
|
||||||
type: String,
|
type: String,
|
||||||
optional: true,
|
optional: true,
|
||||||
|
max: STORAGE_LIMITS.description,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -20,14 +24,14 @@ let ComputedOnlyFeatureSchema = new SimpleSchema({
|
|||||||
summaryCalculations: {
|
summaryCalculations: {
|
||||||
type: Array,
|
type: Array,
|
||||||
defaultValue: [],
|
defaultValue: [],
|
||||||
maxCount: 32,
|
maxCount: STORAGE_LIMITS.inlineCalculationCount,
|
||||||
},
|
},
|
||||||
'summaryCalculations.$': InlineComputationSchema,
|
'summaryCalculations.$': InlineComputationSchema,
|
||||||
|
|
||||||
descriptionCalculations: {
|
descriptionCalculations: {
|
||||||
type: Array,
|
type: Array,
|
||||||
defaultValue: [],
|
defaultValue: [],
|
||||||
maxCount: 32,
|
maxCount: STORAGE_LIMITS.inlineCalculationCount,
|
||||||
},
|
},
|
||||||
'descriptionCalculations.$': InlineComputationSchema,
|
'descriptionCalculations.$': InlineComputationSchema,
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
import SimpleSchema from 'simpl-schema';
|
import SimpleSchema from 'simpl-schema';
|
||||||
|
import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS.js';
|
||||||
|
|
||||||
// Folders organize a character sheet into a tree, particularly to group things
|
// Folders organize a character sheet into a tree, particularly to group things
|
||||||
// like 'race' and 'background'
|
// like 'race' and 'background'
|
||||||
let FolderSchema = new SimpleSchema({
|
let FolderSchema = new SimpleSchema({
|
||||||
name: {
|
name: {
|
||||||
type: String,
|
type: String,
|
||||||
|
max: STORAGE_LIMITS.name,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,19 +1,23 @@
|
|||||||
import SimpleSchema from 'simpl-schema';
|
import SimpleSchema from 'simpl-schema';
|
||||||
import InlineComputationSchema from '/imports/api/properties/subSchemas/InlineComputationSchema.js';
|
import InlineComputationSchema from '/imports/api/properties/subSchemas/InlineComputationSchema.js';
|
||||||
|
import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS.js';
|
||||||
|
|
||||||
const ItemSchema = new SimpleSchema({
|
const ItemSchema = new SimpleSchema({
|
||||||
name: {
|
name: {
|
||||||
type: String,
|
type: String,
|
||||||
optional: true,
|
optional: true,
|
||||||
|
max: STORAGE_LIMITS.name,
|
||||||
},
|
},
|
||||||
// Plural name of the item, if there is more than one
|
// Plural name of the item, if there is more than one
|
||||||
plural: {
|
plural: {
|
||||||
type: String,
|
type: String,
|
||||||
optional: true,
|
optional: true,
|
||||||
|
max: STORAGE_LIMITS.name,
|
||||||
},
|
},
|
||||||
description: {
|
description: {
|
||||||
type: String,
|
type: String,
|
||||||
optional: true,
|
optional: true,
|
||||||
|
max: STORAGE_LIMITS.description,
|
||||||
},
|
},
|
||||||
// Number currently held
|
// Number currently held
|
||||||
quantity: {
|
quantity: {
|
||||||
@@ -58,7 +62,7 @@ let ComputedOnlyItemSchema = new SimpleSchema({
|
|||||||
descriptionCalculations: {
|
descriptionCalculations: {
|
||||||
type: Array,
|
type: Array,
|
||||||
defaultValue: [],
|
defaultValue: [],
|
||||||
maxCount: 32,
|
maxCount: STORAGE_LIMITS.inlineCalculationCount,
|
||||||
},
|
},
|
||||||
'descriptionCalculations.$': InlineComputationSchema,
|
'descriptionCalculations.$': InlineComputationSchema,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,18 +1,22 @@
|
|||||||
import SimpleSchema from 'simpl-schema';
|
import SimpleSchema from 'simpl-schema';
|
||||||
import InlineComputationSchema from '/imports/api/properties/subSchemas/InlineComputationSchema.js';
|
import InlineComputationSchema from '/imports/api/properties/subSchemas/InlineComputationSchema.js';
|
||||||
|
import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS.js';
|
||||||
|
|
||||||
let NoteSchema = new SimpleSchema({
|
let NoteSchema = new SimpleSchema({
|
||||||
name: {
|
name: {
|
||||||
type: String,
|
type: String,
|
||||||
optional: true,
|
optional: true,
|
||||||
|
max: STORAGE_LIMITS.name,
|
||||||
},
|
},
|
||||||
summary: {
|
summary: {
|
||||||
type: String,
|
type: String,
|
||||||
optional: true,
|
optional: true,
|
||||||
|
max: STORAGE_LIMITS.summary,
|
||||||
},
|
},
|
||||||
description: {
|
description: {
|
||||||
type: String,
|
type: String,
|
||||||
optional: true,
|
optional: true,
|
||||||
|
max: STORAGE_LIMITS.description,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -21,14 +25,14 @@ let ComputedOnlyNoteSchema = new SimpleSchema({
|
|||||||
summaryCalculations: {
|
summaryCalculations: {
|
||||||
type: Array,
|
type: Array,
|
||||||
defaultValue: [],
|
defaultValue: [],
|
||||||
maxCount: 32,
|
maxCount: STORAGE_LIMITS.inlineCalculationCount,
|
||||||
},
|
},
|
||||||
'summaryCalculations.$': InlineComputationSchema,
|
'summaryCalculations.$': InlineComputationSchema,
|
||||||
|
|
||||||
descriptionCalculations: {
|
descriptionCalculations: {
|
||||||
type: Array,
|
type: Array,
|
||||||
defaultValue: [],
|
defaultValue: [],
|
||||||
maxCount: 32,
|
maxCount: STORAGE_LIMITS.inlineCalculationCount,
|
||||||
},
|
},
|
||||||
'descriptionCalculations.$': InlineComputationSchema,
|
'descriptionCalculations.$': InlineComputationSchema,
|
||||||
|
|
||||||
|
|||||||
@@ -1,17 +1,21 @@
|
|||||||
import SimpleSchema from 'simpl-schema';
|
import SimpleSchema from 'simpl-schema';
|
||||||
|
import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS.js';
|
||||||
|
|
||||||
let ProficiencySchema = new SimpleSchema({
|
let ProficiencySchema = new SimpleSchema({
|
||||||
name: {
|
name: {
|
||||||
type: String,
|
type: String,
|
||||||
optional: true,
|
optional: true,
|
||||||
|
max: STORAGE_LIMITS.name,
|
||||||
},
|
},
|
||||||
// The variableNames of the skills, tags, or attributes to apply proficiency to
|
// The variableNames of the skills, tags, or attributes to apply proficiency to
|
||||||
stats: {
|
stats: {
|
||||||
type: Array,
|
type: Array,
|
||||||
defaultValue: [],
|
defaultValue: [],
|
||||||
|
maxCount: STORAGE_LIMITS.statsToTarget,
|
||||||
},
|
},
|
||||||
'stats.$': {
|
'stats.$': {
|
||||||
type: String,
|
type: String,
|
||||||
|
max: STORAGE_LIMITS.variableName,
|
||||||
},
|
},
|
||||||
// A number representing how proficient the character is
|
// A number representing how proficient the character is
|
||||||
// where 0.49 is half rounded down and 0.5 is half rounded up
|
// where 0.49 is half rounded down and 0.5 is half rounded up
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import SimpleSchema from 'simpl-schema';
|
import SimpleSchema from 'simpl-schema';
|
||||||
|
import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS.js';
|
||||||
|
|
||||||
let ReferenceSchema = new SimpleSchema({
|
let ReferenceSchema = new SimpleSchema({
|
||||||
ref: {
|
ref: {
|
||||||
@@ -13,6 +14,7 @@ let ReferenceSchema = new SimpleSchema({
|
|||||||
'ref.collection': {
|
'ref.collection': {
|
||||||
type: String,
|
type: String,
|
||||||
optional: true,
|
optional: true,
|
||||||
|
max: STORAGE_LIMITS.collectionName,
|
||||||
},
|
},
|
||||||
// Denormalised store of referenced property's details
|
// Denormalised store of referenced property's details
|
||||||
cache: {
|
cache: {
|
||||||
@@ -22,6 +24,7 @@ let ReferenceSchema = new SimpleSchema({
|
|||||||
'cache.error': {
|
'cache.error': {
|
||||||
type: String,
|
type: String,
|
||||||
optional: true,
|
optional: true,
|
||||||
|
max: STORAGE_LIMITS.errorMessage,
|
||||||
},
|
},
|
||||||
'cache.node': {
|
'cache.node': {
|
||||||
type: Object,
|
type: Object,
|
||||||
@@ -30,9 +33,11 @@ let ReferenceSchema = new SimpleSchema({
|
|||||||
'cache.node.name': {
|
'cache.node.name': {
|
||||||
type: String,
|
type: String,
|
||||||
optional: true,
|
optional: true,
|
||||||
|
max: STORAGE_LIMITS.name,
|
||||||
},
|
},
|
||||||
'cache.node.type': {
|
'cache.node.type': {
|
||||||
type: String,
|
type: String,
|
||||||
|
max: STORAGE_LIMITS.variableName,
|
||||||
},
|
},
|
||||||
'cache.node.level': {
|
'cache.node.level': {
|
||||||
type: Number,
|
type: Number,
|
||||||
@@ -49,6 +54,7 @@ let ReferenceSchema = new SimpleSchema({
|
|||||||
'cache.library.name': {
|
'cache.library.name': {
|
||||||
type: String,
|
type: String,
|
||||||
optional: true,
|
optional: true,
|
||||||
|
max: STORAGE_LIMITS.name,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import SimpleSchema from 'simpl-schema';
|
import SimpleSchema from 'simpl-schema';
|
||||||
import ErrorSchema from '/imports/api/properties/subSchemas/ErrorSchema.js';
|
import ErrorSchema from '/imports/api/properties/subSchemas/ErrorSchema.js';
|
||||||
import VARIABLE_NAME_REGEX from '/imports/constants/VARIABLE_NAME_REGEX.js';
|
import VARIABLE_NAME_REGEX from '/imports/constants/VARIABLE_NAME_REGEX.js';
|
||||||
|
import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Rolls are children to actions or other rolls, they are triggered with 0 or
|
* Rolls are children to actions or other rolls, they are triggered with 0 or
|
||||||
@@ -24,6 +25,7 @@ let RollSchema = new SimpleSchema({
|
|||||||
name: {
|
name: {
|
||||||
type: String,
|
type: String,
|
||||||
defaultValue: 'New Roll',
|
defaultValue: 'New Roll',
|
||||||
|
max: STORAGE_LIMITS.name,
|
||||||
},
|
},
|
||||||
// The technical, lowercase, single-word name used in formulae
|
// The technical, lowercase, single-word name used in formulae
|
||||||
variableName: {
|
variableName: {
|
||||||
@@ -31,20 +33,13 @@ let RollSchema = new SimpleSchema({
|
|||||||
regEx: VARIABLE_NAME_REGEX,
|
regEx: VARIABLE_NAME_REGEX,
|
||||||
min: 2,
|
min: 2,
|
||||||
defaultValue: 'newRoll',
|
defaultValue: 'newRoll',
|
||||||
|
max: STORAGE_LIMITS.variableName,
|
||||||
},
|
},
|
||||||
// The roll, can be simplified, but only computed in context
|
// The roll, can be simplified, but only computed in context
|
||||||
roll: {
|
roll: {
|
||||||
type: String,
|
type: String,
|
||||||
optional: true,
|
optional: true,
|
||||||
},
|
max: STORAGE_LIMITS.calculation,
|
||||||
// Effects can apply to this tag specifically
|
|
||||||
// Ranged spell attack, Ranged weapon attack, etc.
|
|
||||||
tags: {
|
|
||||||
type: Array,
|
|
||||||
defaultValue: [],
|
|
||||||
},
|
|
||||||
'tags.$': {
|
|
||||||
type: String,
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -56,6 +51,7 @@ let ComputedOnlyRollSchema = new SimpleSchema({
|
|||||||
rollErrors: {
|
rollErrors: {
|
||||||
type: Array,
|
type: Array,
|
||||||
optional: true,
|
optional: true,
|
||||||
|
maxCount: STORAGE_LIMITS.errorCount,
|
||||||
},
|
},
|
||||||
'rollErrors.$':{
|
'rollErrors.$':{
|
||||||
type: ErrorSchema,
|
type: ErrorSchema,
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import SimpleSchema from 'simpl-schema';
|
import SimpleSchema from 'simpl-schema';
|
||||||
import ErrorSchema from '/imports/api/properties/subSchemas/ErrorSchema.js';
|
import ErrorSchema from '/imports/api/properties/subSchemas/ErrorSchema.js';
|
||||||
|
import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS.js';
|
||||||
|
|
||||||
// These are the rolls made when saves are called for
|
// These are the rolls made when saves are called for
|
||||||
// For the saving throw bonus or proficiency, see ./Skills.js
|
// For the saving throw bonus or proficiency, see ./Skills.js
|
||||||
@@ -7,11 +8,13 @@ let SavingThrowSchema = new SimpleSchema ({
|
|||||||
name: {
|
name: {
|
||||||
type: String,
|
type: String,
|
||||||
optional: true,
|
optional: true,
|
||||||
|
max: STORAGE_LIMITS.name,
|
||||||
},
|
},
|
||||||
// The computed DC
|
// The computed DC
|
||||||
dc: {
|
dc: {
|
||||||
type: String,
|
type: String,
|
||||||
optional: true,
|
optional: true,
|
||||||
|
max: STORAGE_LIMITS.calculation,
|
||||||
},
|
},
|
||||||
// Who this saving throw applies to
|
// Who this saving throw applies to
|
||||||
target: {
|
target: {
|
||||||
@@ -27,6 +30,7 @@ let SavingThrowSchema = new SimpleSchema ({
|
|||||||
stat: {
|
stat: {
|
||||||
type: String,
|
type: String,
|
||||||
optional: true,
|
optional: true,
|
||||||
|
max: STORAGE_LIMITS.variableName,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -38,6 +42,7 @@ const ComputedOnlySavingThrowSchema = new SimpleSchema({
|
|||||||
dcErrors: {
|
dcErrors: {
|
||||||
type: Array,
|
type: Array,
|
||||||
optional: true,
|
optional: true,
|
||||||
|
maxCount: STORAGE_LIMITS.errorCount,
|
||||||
},
|
},
|
||||||
'dcErrors.$':{
|
'dcErrors.$':{
|
||||||
type: ErrorSchema,
|
type: ErrorSchema,
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import SimpleSchema from 'simpl-schema';
|
import SimpleSchema from 'simpl-schema';
|
||||||
import VARIABLE_NAME_REGEX from '/imports/constants/VARIABLE_NAME_REGEX.js';
|
import VARIABLE_NAME_REGEX from '/imports/constants/VARIABLE_NAME_REGEX.js';
|
||||||
import ErrorSchema from '/imports/api/properties/subSchemas/ErrorSchema.js';
|
import ErrorSchema from '/imports/api/properties/subSchemas/ErrorSchema.js';
|
||||||
|
import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS.js';
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Skills are anything that results in a modifier to be added to a D20
|
* Skills are anything that results in a modifier to be added to a D20
|
||||||
@@ -10,6 +11,7 @@ let SkillSchema = new SimpleSchema({
|
|||||||
name: {
|
name: {
|
||||||
type: String,
|
type: String,
|
||||||
optional: true,
|
optional: true,
|
||||||
|
max: STORAGE_LIMITS.name,
|
||||||
},
|
},
|
||||||
// The technical, lowercase, single-word name used in formulae
|
// The technical, lowercase, single-word name used in formulae
|
||||||
// Ignored for skilltype = save
|
// Ignored for skilltype = save
|
||||||
@@ -17,11 +19,13 @@ let SkillSchema = new SimpleSchema({
|
|||||||
type: String,
|
type: String,
|
||||||
regEx: VARIABLE_NAME_REGEX,
|
regEx: VARIABLE_NAME_REGEX,
|
||||||
min: 2,
|
min: 2,
|
||||||
|
max: STORAGE_LIMITS.variableName,
|
||||||
},
|
},
|
||||||
// The variable name of the ability this skill relies on
|
// The variable name of the ability this skill relies on
|
||||||
ability: {
|
ability: {
|
||||||
type: String,
|
type: String,
|
||||||
optional: true,
|
optional: true,
|
||||||
|
max: STORAGE_LIMITS.variableName,
|
||||||
},
|
},
|
||||||
// What type of skill is this
|
// What type of skill is this
|
||||||
skillType: {
|
skillType: {
|
||||||
@@ -42,6 +46,7 @@ let SkillSchema = new SimpleSchema({
|
|||||||
baseValueCalculation: {
|
baseValueCalculation: {
|
||||||
type: String,
|
type: String,
|
||||||
optional: true,
|
optional: true,
|
||||||
|
max: STORAGE_LIMITS.calculation,
|
||||||
},
|
},
|
||||||
// The base proficiency of this skill
|
// The base proficiency of this skill
|
||||||
baseProficiency: {
|
baseProficiency: {
|
||||||
@@ -52,6 +57,7 @@ let SkillSchema = new SimpleSchema({
|
|||||||
description: {
|
description: {
|
||||||
type: String,
|
type: String,
|
||||||
optional: true,
|
optional: true,
|
||||||
|
max: STORAGE_LIMITS.description,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -69,6 +75,7 @@ let ComputedOnlySkillSchema = new SimpleSchema({
|
|||||||
baseValueErrors: {
|
baseValueErrors: {
|
||||||
type: Array,
|
type: Array,
|
||||||
optional: true,
|
optional: true,
|
||||||
|
maxCount: STORAGE_LIMITS.errorCount,
|
||||||
},
|
},
|
||||||
'baseValueErrors.$': {
|
'baseValueErrors.$': {
|
||||||
type: ErrorSchema,
|
type: ErrorSchema,
|
||||||
@@ -107,6 +114,7 @@ let ComputedOnlySkillSchema = new SimpleSchema({
|
|||||||
rollBonuses: {
|
rollBonuses: {
|
||||||
type: Array,
|
type: Array,
|
||||||
optional: true,
|
optional: true,
|
||||||
|
maxCount: STORAGE_LIMITS.rollBonusCount,
|
||||||
},
|
},
|
||||||
'rollBonuses.$': {
|
'rollBonuses.$': {
|
||||||
type: String,
|
type: String,
|
||||||
|
|||||||
@@ -1,25 +1,29 @@
|
|||||||
|
import SimpleSchema from 'simpl-schema';
|
||||||
|
import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS.js';
|
||||||
|
|
||||||
// SlotFiller fillers specifically fill a slot with a bit more control than
|
// SlotFiller fillers specifically fill a slot with a bit more control than
|
||||||
// other properties
|
// other properties
|
||||||
|
|
||||||
import SimpleSchema from 'simpl-schema';
|
|
||||||
|
|
||||||
let SlotFillerSchema = new SimpleSchema({
|
let SlotFillerSchema = new SimpleSchema({
|
||||||
name: {
|
name: {
|
||||||
type: String,
|
type: String,
|
||||||
optional: true,
|
optional: true,
|
||||||
|
max: STORAGE_LIMITS.name,
|
||||||
},
|
},
|
||||||
picture: {
|
picture: {
|
||||||
type: String,
|
type: String,
|
||||||
optional: true,
|
optional: true,
|
||||||
|
max: STORAGE_LIMITS.url,
|
||||||
},
|
},
|
||||||
description: {
|
description: {
|
||||||
type: String,
|
type: String,
|
||||||
optional: true,
|
optional: true,
|
||||||
|
max: STORAGE_LIMITS.description,
|
||||||
},
|
},
|
||||||
// Overrides the type when searching for properties
|
// Overrides the type when searching for properties
|
||||||
slotFillerType: {
|
slotFillerType: {
|
||||||
type: String,
|
type: String,
|
||||||
optional: true,
|
optional: true,
|
||||||
|
max: STORAGE_LIMITS.variableName,
|
||||||
},
|
},
|
||||||
// Fill more than one quantity in a slot, like feats and ability score
|
// Fill more than one quantity in a slot, like feats and ability score
|
||||||
// improvements, filtered out of UI if there isn't space in quantityExpected
|
// improvements, filtered out of UI if there isn't space in quantityExpected
|
||||||
@@ -32,6 +36,7 @@ let SlotFillerSchema = new SimpleSchema({
|
|||||||
slotFillerCondition: {
|
slotFillerCondition: {
|
||||||
type: String,
|
type: String,
|
||||||
optional: true,
|
optional: true,
|
||||||
|
max: STORAGE_LIMITS.calculation,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,30 +1,66 @@
|
|||||||
import SimpleSchema from 'simpl-schema';
|
import SimpleSchema from 'simpl-schema';
|
||||||
import ErrorSchema from '/imports/api/properties/subSchemas/ErrorSchema.js';
|
import ErrorSchema from '/imports/api/properties/subSchemas/ErrorSchema.js';
|
||||||
|
import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS.js';
|
||||||
|
|
||||||
let SlotSchema = new SimpleSchema({
|
let SlotSchema = new SimpleSchema({
|
||||||
name: {
|
name: {
|
||||||
type: String,
|
type: String,
|
||||||
optional: true,
|
optional: true,
|
||||||
|
max: STORAGE_LIMITS.name,
|
||||||
},
|
},
|
||||||
description: {
|
description: {
|
||||||
type: String,
|
type: String,
|
||||||
optional: true,
|
optional: true,
|
||||||
|
max: STORAGE_LIMITS.description,
|
||||||
},
|
},
|
||||||
slotType: {
|
slotType: {
|
||||||
type: String,
|
type: String,
|
||||||
optional: true,
|
optional: true,
|
||||||
|
max: STORAGE_LIMITS.variableName,
|
||||||
},
|
},
|
||||||
slotTags: {
|
slotTags: {
|
||||||
type: Array,
|
type: Array,
|
||||||
defaultValue: [],
|
defaultValue: [],
|
||||||
|
maxCount: STORAGE_LIMITS.tagCount,
|
||||||
},
|
},
|
||||||
'slotTags.$': {
|
'slotTags.$': {
|
||||||
type: String,
|
type: String,
|
||||||
|
max: STORAGE_LIMITS.tagLength,
|
||||||
|
},
|
||||||
|
extraTags: {
|
||||||
|
type: Array,
|
||||||
|
defaultValue: [],
|
||||||
|
maxCount: STORAGE_LIMITS.extraTagsCount,
|
||||||
|
},
|
||||||
|
'extraTags.$': {
|
||||||
|
type: Object,
|
||||||
|
},
|
||||||
|
'extraTags.$._id': {
|
||||||
|
type: String,
|
||||||
|
regEx: SimpleSchema.RegEx.Id,
|
||||||
|
autoValue(){
|
||||||
|
if (!this.isSet) return Random.id();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'extraTags.$.operation': {
|
||||||
|
type: String,
|
||||||
|
allowedValues: ['OR', 'NOT'],
|
||||||
|
defaultValue: 'OR',
|
||||||
|
},
|
||||||
|
'extraTags.$.tags': {
|
||||||
|
type: Array,
|
||||||
|
defaultValue: [],
|
||||||
|
maxCount: STORAGE_LIMITS.tagCount,
|
||||||
|
},
|
||||||
|
'extraTags.$.tags.$': {
|
||||||
|
type: String,
|
||||||
|
max: STORAGE_LIMITS.tagLength,
|
||||||
},
|
},
|
||||||
quantityExpected: {
|
quantityExpected: {
|
||||||
type: String,
|
type: String,
|
||||||
optional: true,
|
optional: true,
|
||||||
defaultValue: '1',
|
defaultValue: '1',
|
||||||
|
max: STORAGE_LIMITS.calculation,
|
||||||
},
|
},
|
||||||
ignored: {
|
ignored: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
@@ -33,11 +69,24 @@ let SlotSchema = new SimpleSchema({
|
|||||||
slotCondition: {
|
slotCondition: {
|
||||||
type: String,
|
type: String,
|
||||||
optional: true,
|
optional: true,
|
||||||
|
max: STORAGE_LIMITS.calculation,
|
||||||
},
|
},
|
||||||
hideWhenFull: {
|
hideWhenFull: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
optional: true,
|
optional: true,
|
||||||
}
|
defaultValue: true,
|
||||||
|
},
|
||||||
|
unique: {
|
||||||
|
type: String,
|
||||||
|
allowedValues: [
|
||||||
|
// Can't choose the same slot filler twice in this slot
|
||||||
|
'uniqueInSlot',
|
||||||
|
// Can't choose the same slot filler twice accross the whole creature
|
||||||
|
'uniqueInCreature'
|
||||||
|
],
|
||||||
|
optional: true,
|
||||||
|
defaultValue: 'uniqueInSlot',
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const ComputedOnlySlotSchema = new SimpleSchema({
|
const ComputedOnlySlotSchema = new SimpleSchema({
|
||||||
@@ -49,6 +98,7 @@ const ComputedOnlySlotSchema = new SimpleSchema({
|
|||||||
slotConditionErrors: {
|
slotConditionErrors: {
|
||||||
type: Array,
|
type: Array,
|
||||||
optional: true,
|
optional: true,
|
||||||
|
maxCount: STORAGE_LIMITS.errorCount,
|
||||||
},
|
},
|
||||||
'slotConditionErrors.$':{
|
'slotConditionErrors.$':{
|
||||||
type: ErrorSchema,
|
type: ErrorSchema,
|
||||||
@@ -62,6 +112,7 @@ const ComputedOnlySlotSchema = new SimpleSchema({
|
|||||||
quantityExpectedErrors: {
|
quantityExpectedErrors: {
|
||||||
type: Array,
|
type: Array,
|
||||||
optional: true,
|
optional: true,
|
||||||
|
maxCount: STORAGE_LIMITS.errorCount,
|
||||||
},
|
},
|
||||||
'quantityExpectedErrors.$':{
|
'quantityExpectedErrors.$':{
|
||||||
type: ErrorSchema,
|
type: ErrorSchema,
|
||||||
|
|||||||
@@ -1,30 +1,36 @@
|
|||||||
import SimpleSchema from 'simpl-schema';
|
import SimpleSchema from 'simpl-schema';
|
||||||
import ErrorSchema from '/imports/api/properties/subSchemas/ErrorSchema.js';
|
import ErrorSchema from '/imports/api/properties/subSchemas/ErrorSchema.js';
|
||||||
import InlineComputationSchema from '/imports/api/properties/subSchemas/InlineComputationSchema.js';
|
import InlineComputationSchema from '/imports/api/properties/subSchemas/InlineComputationSchema.js';
|
||||||
|
import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS.js';
|
||||||
|
|
||||||
let SpellListSchema = new SimpleSchema({
|
let SpellListSchema = new SimpleSchema({
|
||||||
name: {
|
name: {
|
||||||
type: String,
|
type: String,
|
||||||
optional: true,
|
optional: true,
|
||||||
|
max: STORAGE_LIMITS.name,
|
||||||
},
|
},
|
||||||
description: {
|
description: {
|
||||||
type: String,
|
type: String,
|
||||||
optional: true,
|
optional: true,
|
||||||
|
max: STORAGE_LIMITS.description,
|
||||||
},
|
},
|
||||||
// Calculation of how many spells in this list can be prepared
|
// Calculation of how many spells in this list can be prepared
|
||||||
maxPrepared: {
|
maxPrepared: {
|
||||||
type: String,
|
type: String,
|
||||||
optional: true,
|
optional: true,
|
||||||
|
max: STORAGE_LIMITS.calculation,
|
||||||
},
|
},
|
||||||
// Calculation of The attack roll bonus used by spell attacks in this list
|
// Calculation of The attack roll bonus used by spell attacks in this list
|
||||||
attackRollBonus: {
|
attackRollBonus: {
|
||||||
type: String,
|
type: String,
|
||||||
optional: true,
|
optional: true,
|
||||||
|
max: STORAGE_LIMITS.calculation,
|
||||||
},
|
},
|
||||||
// Calculation of the save dc used by spells in this list
|
// Calculation of the save dc used by spells in this list
|
||||||
dc: {
|
dc: {
|
||||||
type: String,
|
type: String,
|
||||||
optional: true,
|
optional: true,
|
||||||
|
max: STORAGE_LIMITS.calculation,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -32,7 +38,7 @@ const ComputedOnlySpellListSchema = new SimpleSchema({
|
|||||||
descriptionCalculations: {
|
descriptionCalculations: {
|
||||||
type: Array,
|
type: Array,
|
||||||
defaultValue: [],
|
defaultValue: [],
|
||||||
maxCount: 32,
|
maxCount: STORAGE_LIMITS.inlineCalculationCount,
|
||||||
},
|
},
|
||||||
'descriptionCalculations.$': InlineComputationSchema,
|
'descriptionCalculations.$': InlineComputationSchema,
|
||||||
|
|
||||||
@@ -44,6 +50,7 @@ const ComputedOnlySpellListSchema = new SimpleSchema({
|
|||||||
maxPreparedErrors: {
|
maxPreparedErrors: {
|
||||||
type: Array,
|
type: Array,
|
||||||
optional: true,
|
optional: true,
|
||||||
|
maxCount: STORAGE_LIMITS.errorCount,
|
||||||
},
|
},
|
||||||
'maxPreparedErrors.$':{
|
'maxPreparedErrors.$':{
|
||||||
type: ErrorSchema,
|
type: ErrorSchema,
|
||||||
@@ -57,6 +64,7 @@ const ComputedOnlySpellListSchema = new SimpleSchema({
|
|||||||
attackRollBonusErrors: {
|
attackRollBonusErrors: {
|
||||||
type: Array,
|
type: Array,
|
||||||
optional: true,
|
optional: true,
|
||||||
|
maxCount: STORAGE_LIMITS.errorCount,
|
||||||
},
|
},
|
||||||
'attackRollBonusErrors.$':{
|
'attackRollBonusErrors.$':{
|
||||||
type: ErrorSchema,
|
type: ErrorSchema,
|
||||||
@@ -70,6 +78,7 @@ const ComputedOnlySpellListSchema = new SimpleSchema({
|
|||||||
dcErrors: {
|
dcErrors: {
|
||||||
type: Array,
|
type: Array,
|
||||||
optional: true,
|
optional: true,
|
||||||
|
maxCount: STORAGE_LIMITS.errorCount,
|
||||||
},
|
},
|
||||||
'dcErrors.$':{
|
'dcErrors.$':{
|
||||||
type: ErrorSchema,
|
type: ErrorSchema,
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { ActionSchema, ComputedOnlyActionSchema } from '/imports/api/properties/Actions.js';
|
import { ActionSchema, ComputedOnlyActionSchema } from '/imports/api/properties/Actions.js';
|
||||||
import SimpleSchema from 'simpl-schema';
|
import SimpleSchema from 'simpl-schema';
|
||||||
|
import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS.js';
|
||||||
|
|
||||||
const magicSchools = [
|
const magicSchools = [
|
||||||
'abjuration',
|
'abjuration',
|
||||||
@@ -18,6 +19,7 @@ let SpellSchema = new SimpleSchema({})
|
|||||||
name: {
|
name: {
|
||||||
type: String,
|
type: String,
|
||||||
optional: true,
|
optional: true,
|
||||||
|
max: STORAGE_LIMITS.name,
|
||||||
},
|
},
|
||||||
// If it's always prepared, it doesn't count against the number of spells
|
// If it's always prepared, it doesn't count against the number of spells
|
||||||
// prepared in a spell list, and enabled should be true
|
// prepared in a spell list, and enabled should be true
|
||||||
@@ -42,15 +44,18 @@ let SpellSchema = new SimpleSchema({})
|
|||||||
type: String,
|
type: String,
|
||||||
optional: true,
|
optional: true,
|
||||||
defaultValue: 'action',
|
defaultValue: 'action',
|
||||||
|
max: STORAGE_LIMITS.name,
|
||||||
},
|
},
|
||||||
range: {
|
range: {
|
||||||
type: String,
|
type: String,
|
||||||
optional: true,
|
optional: true,
|
||||||
|
max: STORAGE_LIMITS.name,
|
||||||
},
|
},
|
||||||
duration: {
|
duration: {
|
||||||
type: String,
|
type: String,
|
||||||
optional: true,
|
optional: true,
|
||||||
defaultValue: 'Instantaneous',
|
defaultValue: 'Instantaneous',
|
||||||
|
max: STORAGE_LIMITS.name,
|
||||||
},
|
},
|
||||||
verbal: {
|
verbal: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
@@ -67,6 +72,7 @@ let SpellSchema = new SimpleSchema({})
|
|||||||
material: {
|
material: {
|
||||||
type: String,
|
type: String,
|
||||||
optional: true,
|
optional: true,
|
||||||
|
max: STORAGE_LIMITS.name,
|
||||||
},
|
},
|
||||||
ritual: {
|
ritual: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
import SimpleSchema from 'simpl-schema';
|
import SimpleSchema from 'simpl-schema';
|
||||||
import ErrorSchema from '/imports/api/properties/subSchemas/ErrorSchema.js';
|
import ErrorSchema from '/imports/api/properties/subSchemas/ErrorSchema.js';
|
||||||
|
import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS.js';
|
||||||
|
|
||||||
const ToggleSchema = new SimpleSchema({
|
const ToggleSchema = new SimpleSchema({
|
||||||
name: {
|
name: {
|
||||||
type: String,
|
type: String,
|
||||||
optional: true,
|
optional: true,
|
||||||
|
max: STORAGE_LIMITS.name,
|
||||||
},
|
},
|
||||||
disabled: {
|
disabled: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
@@ -19,6 +21,7 @@ const ToggleSchema = new SimpleSchema({
|
|||||||
condition: {
|
condition: {
|
||||||
type: String,
|
type: String,
|
||||||
optional: true,
|
optional: true,
|
||||||
|
max: STORAGE_LIMITS.calculation,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -32,6 +35,7 @@ const ComputedOnlyToggleSchema = new SimpleSchema({
|
|||||||
errors: {
|
errors: {
|
||||||
type: Array,
|
type: Array,
|
||||||
optional: true,
|
optional: true,
|
||||||
|
maxCount: STORAGE_LIMITS.errorCount,
|
||||||
},
|
},
|
||||||
'errors.$': {
|
'errors.$': {
|
||||||
type: ErrorSchema,
|
type: ErrorSchema,
|
||||||
|
|||||||
@@ -1,11 +1,14 @@
|
|||||||
import SimpleSchema from 'simpl-schema';
|
import SimpleSchema from 'simpl-schema';
|
||||||
|
import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS.js';
|
||||||
|
|
||||||
const ErrorSchema = new SimpleSchema({
|
const ErrorSchema = new SimpleSchema({
|
||||||
message: {
|
message: {
|
||||||
type: String,
|
type: String,
|
||||||
|
max: STORAGE_LIMITS.errorMessage,
|
||||||
},
|
},
|
||||||
type: {
|
type: {
|
||||||
type: String,
|
type: String,
|
||||||
|
max: STORAGE_LIMITS.name,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,18 +1,22 @@
|
|||||||
import SimpleSchema from 'simpl-schema';
|
import SimpleSchema from 'simpl-schema';
|
||||||
import ErrorSchema from '/imports/api/properties/subSchemas/ErrorSchema.js';
|
import ErrorSchema from '/imports/api/properties/subSchemas/ErrorSchema.js';
|
||||||
|
import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS.js';
|
||||||
|
|
||||||
const InlineComputationSchema = new SimpleSchema({
|
const InlineComputationSchema = new SimpleSchema({
|
||||||
// The part between bracers {}
|
// The part between bracers {}
|
||||||
calculation: {
|
calculation: {
|
||||||
type: String,
|
type: String,
|
||||||
|
max: STORAGE_LIMITS.calculation,
|
||||||
},
|
},
|
||||||
result: {
|
result: {
|
||||||
type: String,
|
type: String,
|
||||||
optional: true,
|
optional: true,
|
||||||
|
max: STORAGE_LIMITS.calculation,
|
||||||
},
|
},
|
||||||
errors: {
|
errors: {
|
||||||
type: Array,
|
type: Array,
|
||||||
optional: true,
|
optional: true,
|
||||||
|
maxCount: STORAGE_LIMITS.errorCount,
|
||||||
},
|
},
|
||||||
'errors.$': ErrorSchema,
|
'errors.$': ErrorSchema,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import SimpleSchema from 'simpl-schema';
|
import SimpleSchema from 'simpl-schema';
|
||||||
|
import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS.js';
|
||||||
|
|
||||||
const RollDetailsSchema = new SimpleSchema({
|
const RollDetailsSchema = new SimpleSchema({
|
||||||
number: {
|
number: {
|
||||||
@@ -10,6 +11,7 @@ const RollDetailsSchema = new SimpleSchema({
|
|||||||
values: {
|
values: {
|
||||||
type: Array,
|
type: Array,
|
||||||
defaultValue: [],
|
defaultValue: [],
|
||||||
|
maxCount: STORAGE_LIMITS.diceRollValuesCount,
|
||||||
},
|
},
|
||||||
'values.$': {
|
'values.$': {
|
||||||
type: Number,
|
type: Number,
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import SimpleSchema from 'simpl-schema';
|
import SimpleSchema from 'simpl-schema';
|
||||||
import '/imports/api/sharing/sharing.js';
|
import '/imports/api/sharing/sharing.js';
|
||||||
|
import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS.js';
|
||||||
|
|
||||||
let SharingSchema = new SimpleSchema({
|
let SharingSchema = new SimpleSchema({
|
||||||
owner: {
|
owner: {
|
||||||
@@ -11,9 +12,9 @@ let SharingSchema = new SimpleSchema({
|
|||||||
type: Array,
|
type: Array,
|
||||||
defaultValue: [],
|
defaultValue: [],
|
||||||
index: 1,
|
index: 1,
|
||||||
max: 50,
|
maxCount: STORAGE_LIMITS.readersCount,
|
||||||
},
|
},
|
||||||
"readers.$": {
|
'readers.$': {
|
||||||
type: String,
|
type: String,
|
||||||
regEx: SimpleSchema.RegEx.Id
|
regEx: SimpleSchema.RegEx.Id
|
||||||
},
|
},
|
||||||
@@ -21,9 +22,9 @@ let SharingSchema = new SimpleSchema({
|
|||||||
type: Array,
|
type: Array,
|
||||||
defaultValue: [],
|
defaultValue: [],
|
||||||
index: 1,
|
index: 1,
|
||||||
max: 20,
|
maxCount: STORAGE_LIMITS.writersCount,
|
||||||
},
|
},
|
||||||
"writers.$": {
|
'writers.$': {
|
||||||
type: String,
|
type: String,
|
||||||
regEx: SimpleSchema.RegEx.Id
|
regEx: SimpleSchema.RegEx.Id
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import getCollectionByName from '/imports/api/parenting/getCollectionByName.js';
|
|||||||
import { RefSchema } from '/imports/api/parenting/ChildSchema.js';
|
import { RefSchema } from '/imports/api/parenting/ChildSchema.js';
|
||||||
import { ValidatedMethod } from 'meteor/mdg:validated-method';
|
import { ValidatedMethod } from 'meteor/mdg:validated-method';
|
||||||
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
|
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
|
||||||
|
import { getUserTier } from '/imports/api/users/patreon/tiers.js';
|
||||||
|
|
||||||
const setPublic = new ValidatedMethod({
|
const setPublic = new ValidatedMethod({
|
||||||
name: 'sharing.setPublic',
|
name: 'sharing.setPublic',
|
||||||
@@ -47,7 +48,7 @@ const updateUserSharePermissions = new ValidatedMethod({
|
|||||||
run({docRef, userId, role}){
|
run({docRef, userId, role}){
|
||||||
let doc = fetchDocByRef(docRef);
|
let doc = fetchDocByRef(docRef);
|
||||||
if (role === 'none'){
|
if (role === 'none'){
|
||||||
// only asser ownership if you aren't removing yourself
|
// only assert ownership if you aren't removing yourself
|
||||||
if (this.userId !== userId){
|
if (this.userId !== userId){
|
||||||
assertOwnership(doc, this.userId);
|
assertOwnership(doc, this.userId);
|
||||||
}
|
}
|
||||||
@@ -74,4 +75,58 @@ const updateUserSharePermissions = new ValidatedMethod({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export { setPublic, updateUserSharePermissions };
|
const transferOwnership = new ValidatedMethod({
|
||||||
|
name: 'sharing.transferOwnership',
|
||||||
|
validate: new SimpleSchema({
|
||||||
|
docRef: RefSchema,
|
||||||
|
userId: {
|
||||||
|
type: String,
|
||||||
|
regEx: SimpleSchema.RegEx.Id,
|
||||||
|
},
|
||||||
|
}).validator(),
|
||||||
|
mixins: [RateLimiterMixin],
|
||||||
|
rateLimit: {
|
||||||
|
numRequests: 5,
|
||||||
|
timeInterval: 5000,
|
||||||
|
},
|
||||||
|
run({docRef, userId}){
|
||||||
|
let doc = fetchDocByRef(docRef);
|
||||||
|
assertOwnership(doc, this.userId);
|
||||||
|
|
||||||
|
let collection = getCollectionByName(docRef.collection);
|
||||||
|
|
||||||
|
let tier = getUserTier(userId);
|
||||||
|
if (docRef.collection === 'creatures'){
|
||||||
|
let currentCharacterCount = collection.find({
|
||||||
|
owner: userId,
|
||||||
|
}, {
|
||||||
|
fields: {_id: 1},
|
||||||
|
}).count();
|
||||||
|
|
||||||
|
if (
|
||||||
|
tier.characterSlots !== -1 &&
|
||||||
|
currentCharacterCount >= tier.characterSlots
|
||||||
|
){
|
||||||
|
throw new Meteor.Error('Sharing.methods.transferOwnership.denied',
|
||||||
|
'The new owner is already at their character limit')
|
||||||
|
}
|
||||||
|
} else if (docRef.collection === 'libraries'){
|
||||||
|
if (!tier.paidBenefits){
|
||||||
|
throw new Meteor.Error('Sharing.methods.transferOwnership.denied',
|
||||||
|
'The new owner\'s Patreon tier does not have access to library ownership');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// First remove current permissions for the user
|
||||||
|
collection.update(docRef.id, {
|
||||||
|
$pullAll: { writers: userId, readers: userId },
|
||||||
|
});
|
||||||
|
// Then make the user the owner and the current owner a writer
|
||||||
|
return collection.update(docRef.id, {
|
||||||
|
$set: {owner: userId},
|
||||||
|
$addToSet: { writers: this.userId },
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export { setPublic, updateUserSharePermissions, transferOwnership };
|
||||||
|
|||||||
32
app/imports/constants/STORAGE_LIMITS.js
Normal file
32
app/imports/constants/STORAGE_LIMITS.js
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
const STORAGE_LIMITS = Object.freeze({
|
||||||
|
// String lengths
|
||||||
|
calculation: 256,
|
||||||
|
collectionName: 64,
|
||||||
|
color: 10000,
|
||||||
|
description: 49473, //the length of the Bee Movie script
|
||||||
|
errorMessage: 256,
|
||||||
|
icon: 10000,
|
||||||
|
name: 128,
|
||||||
|
summary: 10000,
|
||||||
|
tagLength: 128,
|
||||||
|
url: 256,
|
||||||
|
variableName: 64,
|
||||||
|
|
||||||
|
//Array counts
|
||||||
|
ancestorCount: 100,
|
||||||
|
damageTypeCount: 32,
|
||||||
|
diceRollValuesCount: 100,
|
||||||
|
errorCount: 32,
|
||||||
|
extraTagsCount: 5,
|
||||||
|
inlineCalculationCount: 32,
|
||||||
|
logContentCount: 32,
|
||||||
|
readersCount: 50,
|
||||||
|
resourcesCount: 32,
|
||||||
|
rollCount: 64,
|
||||||
|
rollBonusCount: 32,
|
||||||
|
statsToTarget: 32,
|
||||||
|
tagCount: 64,
|
||||||
|
writersCount: 20,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default STORAGE_LIMITS;
|
||||||
@@ -2,6 +2,7 @@ import ParseNode from '/imports/parser/parseTree/ParseNode.js';
|
|||||||
import RollArrayNode from '/imports/parser/parseTree/RollArrayNode.js';
|
import RollArrayNode from '/imports/parser/parseTree/RollArrayNode.js';
|
||||||
import ErrorNode from '/imports/parser/parseTree/ErrorNode.js';
|
import ErrorNode from '/imports/parser/parseTree/ErrorNode.js';
|
||||||
import roll from '/imports/parser/roll.js';
|
import roll from '/imports/parser/roll.js';
|
||||||
|
import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS.js';
|
||||||
|
|
||||||
export default class RollNode extends ParseNode {
|
export default class RollNode extends ParseNode {
|
||||||
constructor({left, right}) {
|
constructor({left, right}) {
|
||||||
@@ -42,9 +43,9 @@ export default class RollNode extends ParseNode {
|
|||||||
if (context.doubleRolls){
|
if (context.doubleRolls){
|
||||||
number *= 2;
|
number *= 2;
|
||||||
}
|
}
|
||||||
if (number > 100) return new ErrorNode({
|
if (number > STORAGE_LIMITS.diceRollValuesCount) return new ErrorNode({
|
||||||
node: this,
|
node: this,
|
||||||
error: 'Can\'t roll more than 100 dice at once',
|
error: `Can't roll more than ${STORAGE_LIMITS.diceRollValuesCount} dice at once`,
|
||||||
context,
|
context,
|
||||||
});
|
});
|
||||||
let diceSize = right.value;
|
let diceSize = right.value;
|
||||||
|
|||||||
@@ -2,8 +2,11 @@ import { check } from 'meteor/check';
|
|||||||
import Libraries from '/imports/api/library/Libraries.js';
|
import Libraries from '/imports/api/library/Libraries.js';
|
||||||
import LibraryNodes from '/imports/api/library/LibraryNodes.js';
|
import LibraryNodes from '/imports/api/library/LibraryNodes.js';
|
||||||
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
|
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
|
||||||
|
import getSlotFillFilter from '/imports/api/creature/creatureProperties/methods/getSlotFillFilter.js'
|
||||||
|
|
||||||
|
Meteor.publish('slotFillers', function(slotId, searchTerm){
|
||||||
|
if (searchTerm) check(searchTerm, String);
|
||||||
|
|
||||||
Meteor.publish('slotFillers', function(slotId){
|
|
||||||
let self = this;
|
let self = this;
|
||||||
this.autorun(function (){
|
this.autorun(function (){
|
||||||
let userId = this.userId;
|
let userId = this.userId;
|
||||||
@@ -21,7 +24,7 @@ Meteor.publish('slotFillers', function(slotId){
|
|||||||
fields: {subscribedLibraries: 1}
|
fields: {subscribedLibraries: 1}
|
||||||
});
|
});
|
||||||
const subs = user && user.subscribedLibraries || [];
|
const subs = user && user.subscribedLibraries || [];
|
||||||
let libraryIds = Libraries.find({
|
let libraries = Libraries.find({
|
||||||
$or: [
|
$or: [
|
||||||
{owner: this.userId},
|
{owner: this.userId},
|
||||||
{writers: this.userId},
|
{writers: this.userId},
|
||||||
@@ -29,45 +32,29 @@ Meteor.publish('slotFillers', function(slotId){
|
|||||||
{_id: {$in: subs}},
|
{_id: {$in: subs}},
|
||||||
]
|
]
|
||||||
}, {
|
}, {
|
||||||
fields: {_id: 1},
|
fields: {_id: 1, name: 1},
|
||||||
}).map(lib => lib._id);
|
});
|
||||||
|
let libraryIds = libraries.map(lib => lib._id);
|
||||||
|
|
||||||
// Build a filter for nodes in those libraries that match the slot
|
// Build a filter for nodes in those libraries that match the slot
|
||||||
let filter = {
|
let filter = getSlotFillFilter({slot, libraryIds});
|
||||||
'ancestors.id': {$in: libraryIds},
|
|
||||||
removed: {$ne: true},
|
|
||||||
};
|
|
||||||
if (slot.slotTags && slot.slotTags.length){
|
|
||||||
filter.tags = {$all: slot.slotTags};
|
|
||||||
}
|
|
||||||
if (slot.slotType){
|
|
||||||
filter.$or = [{
|
|
||||||
type: slot.slotType
|
|
||||||
},{
|
|
||||||
type: 'slotFiller',
|
|
||||||
slotFillerType: slot.slotType,
|
|
||||||
}];
|
|
||||||
}
|
|
||||||
this.autorun(function(){
|
this.autorun(function(){
|
||||||
// Get the limit of the documents the user can fetch
|
// Get the limit of the documents the user can fetch
|
||||||
var limit = self.data('limit') || 20;
|
var limit = self.data('limit') || 50;
|
||||||
check(limit, Number);
|
check(limit, Number);
|
||||||
|
|
||||||
// Get the search term
|
|
||||||
let searchTerm = self.data('searchTerm') || '';
|
|
||||||
check(searchTerm, String);
|
|
||||||
|
|
||||||
let options = undefined;
|
let options = undefined;
|
||||||
if (searchTerm){
|
if (searchTerm){
|
||||||
filter.$text = {$search: searchTerm};
|
filter.$text = {$search: searchTerm};
|
||||||
options = {
|
options = {
|
||||||
// relevant documents have a higher score.
|
// relevant documents have a higher score.
|
||||||
fields: {
|
fields: {
|
||||||
score: { $meta: 'textScore' }
|
_score: { $meta: 'textScore' }
|
||||||
},
|
},
|
||||||
sort: {
|
sort: {
|
||||||
// `score` property specified in the projection fields above.
|
// `score` property specified in the projection fields above.
|
||||||
score: { $meta: 'textScore' },
|
_score: { $meta: 'textScore' },
|
||||||
name: 1,
|
name: 1,
|
||||||
order: 1,
|
order: 1,
|
||||||
}
|
}
|
||||||
@@ -85,7 +72,8 @@ Meteor.publish('slotFillers', function(slotId){
|
|||||||
self.setData('countAll', LibraryNodes.find(filter).count());
|
self.setData('countAll', LibraryNodes.find(filter).count());
|
||||||
});
|
});
|
||||||
self.autorun(function () {
|
self.autorun(function () {
|
||||||
return LibraryNodes.find(filter, options);
|
Meteor._sleepForMs(1000);
|
||||||
|
return [LibraryNodes.find(filter, options), libraries];
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<template>
|
if<template>
|
||||||
<div class="character-sheet fill-height">
|
<div class="character-sheet fill-height">
|
||||||
<v-fade-transition mode="out-in">
|
<v-fade-transition mode="out-in">
|
||||||
<div
|
<div
|
||||||
@@ -35,6 +35,10 @@
|
|||||||
class="fill-height"
|
class="fill-height"
|
||||||
>
|
>
|
||||||
<v-tabs-items
|
<v-tabs-items
|
||||||
|
:key=" '' +
|
||||||
|
creature.settings.hideSpellsTab +
|
||||||
|
creature.settings.showTreeTab
|
||||||
|
"
|
||||||
:value="$store.getters.tabById($route.params.id)"
|
:value="$store.getters.tabById($route.params.id)"
|
||||||
class="card-background"
|
class="card-background"
|
||||||
@change="e => $store.commit(
|
@change="e => $store.commit(
|
||||||
@@ -51,13 +55,17 @@
|
|||||||
<v-tab-item>
|
<v-tab-item>
|
||||||
<inventory-tab :creature-id="creatureId" />
|
<inventory-tab :creature-id="creatureId" />
|
||||||
</v-tab-item>
|
</v-tab-item>
|
||||||
<v-tab-item v-show="!creature.settings.hideSpellsTab">
|
<v-tab-item
|
||||||
|
v-if="!creature.settings.hideSpellsTab"
|
||||||
|
>
|
||||||
<spells-tab :creature-id="creatureId" />
|
<spells-tab :creature-id="creatureId" />
|
||||||
</v-tab-item>
|
</v-tab-item>
|
||||||
<v-tab-item>
|
<v-tab-item>
|
||||||
<character-tab :creature-id="creatureId" />
|
<character-tab :creature-id="creatureId" />
|
||||||
</v-tab-item>
|
</v-tab-item>
|
||||||
<v-tab-item v-if="creature.settings.showTreeTab">
|
<v-tab-item
|
||||||
|
v-if="creature.settings.showTreeTab"
|
||||||
|
>
|
||||||
<tree-tab :creature-id="creatureId" />
|
<tree-tab :creature-id="creatureId" />
|
||||||
</v-tab-item>
|
</v-tab-item>
|
||||||
</v-tabs-items>
|
</v-tabs-items>
|
||||||
|
|||||||
@@ -31,7 +31,7 @@
|
|||||||
:key="type"
|
:key="type"
|
||||||
color="primary"
|
color="primary"
|
||||||
:data-id="`insert-creature-property-type-${type}`"
|
:data-id="`insert-creature-property-type-${type}`"
|
||||||
:label="type ? 'New ' + properties[type].name : 'New Property'"
|
:label="getPropertyLabel(type)"
|
||||||
:icon="type ? properties[type].icon : 'mdi-plus'"
|
:icon="type ? properties[type].icon : 'mdi-plus'"
|
||||||
:disabled="!editPermission"
|
:disabled="!editPermission"
|
||||||
@click="addProperty(type)"
|
@click="addProperty(type)"
|
||||||
@@ -44,6 +44,7 @@
|
|||||||
import { getHighestOrder } from '/imports/api/parenting/order.js';
|
import { getHighestOrder } from '/imports/api/parenting/order.js';
|
||||||
import insertProperty from '/imports/api/creature/creatureProperties/methods/insertProperty.js';
|
import insertProperty from '/imports/api/creature/creatureProperties/methods/insertProperty.js';
|
||||||
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
|
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
|
||||||
|
import Creatures from '/imports/api/creature/creatures/Creatures.js';
|
||||||
import PROPERTIES from '/imports/constants/PROPERTIES.js';
|
import PROPERTIES from '/imports/constants/PROPERTIES.js';
|
||||||
import insertPropertyFromLibraryNode from '/imports/api/creature/creatureProperties/methods/insertPropertyFromLibraryNode.js';
|
import insertPropertyFromLibraryNode from '/imports/api/creature/creatureProperties/methods/insertPropertyFromLibraryNode.js';
|
||||||
import fetchDocByRef from '/imports/api/parenting/fetchDocByRef.js';
|
import fetchDocByRef from '/imports/api/parenting/fetchDocByRef.js';
|
||||||
@@ -119,13 +120,17 @@
|
|||||||
return this.$route.params.id;
|
return this.$route.params.id;
|
||||||
},
|
},
|
||||||
tabNumber(){
|
tabNumber(){
|
||||||
return this.$store.getters.tabById(this.creatureId);
|
let tabNumber = this.$store.getters.tabById(this.creatureId);
|
||||||
|
if (this.hideSpellsTab && tabNumber > 2){
|
||||||
|
tabNumber += 1;
|
||||||
|
}
|
||||||
|
return tabNumber;
|
||||||
},
|
},
|
||||||
speedDials(){
|
speedDials(){
|
||||||
return this.speedDialsByTab[tabs[this.tabNumber]];
|
return this.speedDialsByTab[tabs[this.tabNumber]];
|
||||||
},
|
},
|
||||||
speedDialsByTab() { return {
|
speedDialsByTab() { return {
|
||||||
'stats': ['attribute', 'skill', 'action', 'attack'],
|
'stats': ['attribute', 'skill', 'action', 'attack', 'buff'],
|
||||||
'features': ['feature'],
|
'features': ['feature'],
|
||||||
'inventory': ['item', 'container'],
|
'inventory': ['item', 'container'],
|
||||||
'spells': ['spellList', 'spell'],
|
'spells': ['spellList', 'spell'],
|
||||||
@@ -136,7 +141,17 @@
|
|||||||
return PROPERTIES;
|
return PROPERTIES;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
meteor: {
|
||||||
|
hideSpellsTab(){
|
||||||
|
let creature = Creatures.findOne(this.creatureId);
|
||||||
|
return creature?.settings.hideSpellsTab;
|
||||||
|
},
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
getPropertyLabel(type){
|
||||||
|
if (type === 'buff') return 'Buff or Condition';
|
||||||
|
return type ? PROPERTIES[type].name : 'Property'
|
||||||
|
},
|
||||||
addProperty(forcedType){
|
addProperty(forcedType){
|
||||||
let creatureId = this.creatureId;
|
let creatureId = this.creatureId;
|
||||||
let fab = hideFab();
|
let fab = hideFab();
|
||||||
|
|||||||
@@ -80,6 +80,10 @@
|
|||||||
>
|
>
|
||||||
<v-tabs
|
<v-tabs
|
||||||
v-if="creature && creature.settings"
|
v-if="creature && creature.settings"
|
||||||
|
:key=" '' +
|
||||||
|
creature.settings.hideSpellsTab +
|
||||||
|
creature.settings.showTreeTab
|
||||||
|
"
|
||||||
class="flex"
|
class="flex"
|
||||||
style="min-width: 0"
|
style="min-width: 0"
|
||||||
centered
|
centered
|
||||||
@@ -102,7 +106,7 @@
|
|||||||
<v-tab>
|
<v-tab>
|
||||||
Inventory
|
Inventory
|
||||||
</v-tab>
|
</v-tab>
|
||||||
<v-tab v-show="!creature.settings.hideSpellsTab">
|
<v-tab v-if="!creature.settings.hideSpellsTab">
|
||||||
Spells
|
Spells
|
||||||
</v-tab>
|
</v-tab>
|
||||||
<v-tab>
|
<v-tab>
|
||||||
@@ -134,13 +138,13 @@ import getThemeColor from '/imports/ui/utility/getThemeColor.js';
|
|||||||
import SharedIcon from '/imports/ui/components/SharedIcon.vue';
|
import SharedIcon from '/imports/ui/components/SharedIcon.vue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
inject: {
|
|
||||||
context: { default: {} }
|
|
||||||
},
|
|
||||||
components: {
|
components: {
|
||||||
CharacterSheetFab,
|
CharacterSheetFab,
|
||||||
SharedIcon,
|
SharedIcon,
|
||||||
},
|
},
|
||||||
|
inject: {
|
||||||
|
context: { default: {} }
|
||||||
|
},
|
||||||
computed: {
|
computed: {
|
||||||
creatureId(){
|
creatureId(){
|
||||||
return this.$route.params.id;
|
return this.$route.params.id;
|
||||||
|
|||||||
@@ -8,145 +8,173 @@
|
|||||||
{{ model.name }}
|
{{ model.name }}
|
||||||
</v-toolbar-title>
|
</v-toolbar-title>
|
||||||
<v-spacer />
|
<v-spacer />
|
||||||
<text-field
|
<v-text-field
|
||||||
|
v-model="searchInput"
|
||||||
prepend-inner-icon="mdi-magnify"
|
prepend-inner-icon="mdi-magnify"
|
||||||
regular
|
regular
|
||||||
|
clearable
|
||||||
hide-details
|
hide-details
|
||||||
:value="searchValue"
|
class="flex-grow-0"
|
||||||
:debounce="300"
|
style="flex-basis: 300px;"
|
||||||
@change="searchChanged"
|
:loading="searchLoading"
|
||||||
@keyup.enter="insert"
|
@change="searchValue = searchInput || undefined"
|
||||||
|
@click:clear="searchValue = undefined"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
<div
|
<property-description
|
||||||
class="library-nodes"
|
:string="model.description"
|
||||||
|
/>
|
||||||
|
<p>
|
||||||
|
{{ slotPropertyTypeName }} with tags:
|
||||||
|
<template v-for="(tags, index) in tagsSearched.or">
|
||||||
|
<property-tags
|
||||||
|
:key="index"
|
||||||
|
:tags="tags"
|
||||||
|
:prefix="index ? 'OR' : undefined"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<template v-for="(tags, index) in tagsSearched.not">
|
||||||
|
<property-tags
|
||||||
|
:key="index"
|
||||||
|
:tags="tags"
|
||||||
|
prefix="NOT"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</p>
|
||||||
|
<v-expansion-panels
|
||||||
|
multiple
|
||||||
|
inset
|
||||||
>
|
>
|
||||||
<v-fade-transition mode="out-in">
|
<template v-for="libraryNode in libraryNodes">
|
||||||
<div v-if="libraryNodes && libraryNodes.length">
|
<v-expansion-panel
|
||||||
<section
|
v-if="showDisabled || !libraryNode._disabledBySlotFillerCondition"
|
||||||
class="layout wrap justify-between"
|
:key="libraryNode._id"
|
||||||
>
|
:model="libraryNode"
|
||||||
<v-card
|
:data-id="libraryNode._id"
|
||||||
v-for="node in libraryNodes"
|
:class="{disabled: isDisabled(libraryNode)}"
|
||||||
:key="node._id"
|
>
|
||||||
hover
|
<v-expansion-panel-header>
|
||||||
ripple
|
<template #default="{ open }">
|
||||||
class="slot-card layout column justify-end"
|
<v-layout
|
||||||
:class="{'selected': node._id === (selectedNode && selectedNode._id)}"
|
align-center
|
||||||
:dark="node._id === (selectedNode && selectedNode._id)"
|
class="flex-grow-0 mr-2"
|
||||||
@click="selectedNode = node"
|
|
||||||
>
|
|
||||||
<v-img
|
|
||||||
v-if="node.picture"
|
|
||||||
:src="node.picture"
|
|
||||||
:height="200"
|
|
||||||
contain
|
|
||||||
class="slot-card-image"
|
|
||||||
/>
|
|
||||||
<v-card-title primary-title>
|
|
||||||
<tree-node-view
|
|
||||||
class="mr-2 text-h6 mb-0"
|
|
||||||
:class="{'theme--dark': node._id === (selectedNode && selectedNode._id)}"
|
|
||||||
:hide-icon="node.picture"
|
|
||||||
:model="node"
|
|
||||||
:color="node.color"
|
|
||||||
/>
|
|
||||||
</v-card-title>
|
|
||||||
<v-card-text
|
|
||||||
v-if="node.description"
|
|
||||||
class="pt-0"
|
|
||||||
>
|
>
|
||||||
<property-description
|
<v-checkbox
|
||||||
class="slot-card-text line-clamp"
|
v-if="libraryNode._disabledByAlreadyAdded"
|
||||||
:string="node.description"
|
class="my-0 py-0"
|
||||||
|
hide-details
|
||||||
|
:input-value="true"
|
||||||
|
disabled
|
||||||
/>
|
/>
|
||||||
</v-card-text>
|
<v-checkbox
|
||||||
</v-card>
|
v-else
|
||||||
</section>
|
v-model="selectedNodeIds"
|
||||||
</div>
|
class="my-0 py-0"
|
||||||
<div
|
hide-details
|
||||||
v-else-if="countAll"
|
:disabled="isDisabled(libraryNode)"
|
||||||
class="ma-4"
|
:value="libraryNode._id"
|
||||||
>
|
@click.stop
|
||||||
<h4 v-if="numFiltered">
|
/>
|
||||||
Requirements of {{ numFiltered }} library properties were not met.
|
</v-layout>
|
||||||
</h4>
|
<v-layout column>
|
||||||
<h4 v-else>
|
<v-layout align-center>
|
||||||
Nothing suitable was found in your libraries.
|
<tree-node-view :model="libraryNode" />
|
||||||
</h4>
|
<div
|
||||||
</div>
|
v-if="libraryNode._disabledBySlotFillerCondition"
|
||||||
<div
|
class="error--text text-no-wrap text-truncate"
|
||||||
v-else-if="$subReady.slotFillers"
|
>
|
||||||
class="ma-4"
|
{{ libraryNode.slotFillerCondition }}
|
||||||
>
|
</div>
|
||||||
<h4>
|
</v-layout>
|
||||||
Nothing suitable was found in your libraries
|
<div class="text-caption text-no-wrap text-truncate">
|
||||||
<span v-if="searchValue">
|
{{ libraryNames[libraryNode.ancestors[0].id ] }}
|
||||||
matching "{{ searchValue }}"
|
</div>
|
||||||
</span>
|
</v-layout>
|
||||||
</h4>
|
<div
|
||||||
<p>
|
v-if="libraryNode.slotQuantityFilled !== undefined && libraryNode.slotQuantityFilled !== 1"
|
||||||
This slot requires a {{ slotPropertyTypeName }}
|
class="text-overline flex-grow-0 text-no-wrap"
|
||||||
<template v-if="model.slotTags.length == 1">
|
:class="{
|
||||||
with the tag <code>{{ model.slotTags[0] }}</code>,
|
'error--text': isDisabled(libraryNode) &&
|
||||||
</template>
|
libraryNode._disabledByQuantityFilled
|
||||||
<template v-else-if="model.slotTags.length > 1">
|
}"
|
||||||
with the following tags:
|
|
||||||
<span
|
|
||||||
v-for="(tag, index) in model.slotTags"
|
|
||||||
:key="index"
|
|
||||||
>
|
>
|
||||||
<code>{{ tag }}</code>,
|
{{ libraryNode.slotQuantityFilled }} slots
|
||||||
</span>
|
</div>
|
||||||
|
<template v-if="open">
|
||||||
|
<v-btn
|
||||||
|
icon
|
||||||
|
class="flex-grow-0"
|
||||||
|
@click.stop="openPropertyDetails(libraryNode._id)"
|
||||||
|
>
|
||||||
|
<v-icon>mdi-window-restore</v-icon>
|
||||||
|
</v-btn>
|
||||||
|
</template>
|
||||||
</template>
|
</template>
|
||||||
<span v-if="model.spaceLeft">
|
</v-expansion-panel-header>
|
||||||
that fills less than {{ model.spaceLeft }} {{ model.spaceLeft == 1 && 'slot' || 'slots' }}
|
<v-expansion-panel-content>
|
||||||
</span>
|
<library-node-expansion-content :model="libraryNode" />
|
||||||
</p>
|
</v-expansion-panel-content>
|
||||||
|
</v-expansion-panel>
|
||||||
|
</template>
|
||||||
|
</v-expansion-panels>
|
||||||
|
<v-layout
|
||||||
|
v-if="(!$subReady.slotFillers && !searchValue) || currentLimit < countAll"
|
||||||
|
column
|
||||||
|
align-center
|
||||||
|
justify-center
|
||||||
|
class="ma-3"
|
||||||
|
>
|
||||||
|
<v-btn
|
||||||
|
:loading="!$subReady.slotFillers"
|
||||||
|
color="accent"
|
||||||
|
@click="loadMore"
|
||||||
|
>
|
||||||
|
Load More
|
||||||
|
</v-btn>
|
||||||
|
</v-layout>
|
||||||
|
<template v-if="!showDisabled && disabledNodeCount">
|
||||||
|
<v-layout
|
||||||
|
column
|
||||||
|
align-center
|
||||||
|
justify-center
|
||||||
|
class="ma-3"
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
Requirements of {{ disabledNodeCount }} properties were not met
|
||||||
</div>
|
</div>
|
||||||
</v-fade-transition>
|
<v-btn
|
||||||
<v-fade-transition mode="out-in">
|
class="mt-2"
|
||||||
<div
|
elevation="0"
|
||||||
v-if="!$subReady.slotFillers"
|
color="accent"
|
||||||
key="character-loading"
|
@click="showDisabled = true"
|
||||||
class="fill-height layout justify-center align-center"
|
|
||||||
>
|
>
|
||||||
<v-progress-circular
|
Show All
|
||||||
indeterminate
|
</v-btn>
|
||||||
color="primary"
|
</v-layout>
|
||||||
size="64"
|
</template>
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</v-fade-transition>
|
|
||||||
<v-fade-transition mode="out-in">
|
|
||||||
<div
|
|
||||||
v-if="currentLimit < countAll"
|
|
||||||
class="layout justify-center align-stretch"
|
|
||||||
>
|
|
||||||
<v-btn
|
|
||||||
:loading="!$subReady.slotFillers"
|
|
||||||
class="primary"
|
|
||||||
@click="loadMore"
|
|
||||||
>
|
|
||||||
Load More
|
|
||||||
</v-btn>
|
|
||||||
</div>
|
|
||||||
</v-fade-transition>
|
|
||||||
</div>
|
|
||||||
<template slot="actions">
|
<template slot="actions">
|
||||||
<v-spacer />
|
|
||||||
<v-btn
|
<v-btn
|
||||||
text
|
text
|
||||||
@click="$store.dispatch('popDialogStack')"
|
@click="$store.dispatch('popDialogStack')"
|
||||||
>
|
>
|
||||||
Cancel
|
Cancel
|
||||||
</v-btn>
|
</v-btn>
|
||||||
|
<v-spacer />
|
||||||
<v-btn
|
<v-btn
|
||||||
text
|
text
|
||||||
:disabled="!selectedNode"
|
color="primary"
|
||||||
@click="insert"
|
:disabled="!dummySlot && !selectedNodeIds.length"
|
||||||
|
@click="$store.dispatch('popDialogStack', selectedNodeIds)"
|
||||||
>
|
>
|
||||||
Insert
|
<template v-if="model.spaceLeft">
|
||||||
|
{{ totalQuantitySelected }} / {{ model.spaceLeft }}
|
||||||
|
</template>
|
||||||
|
<template v-if="slotId">
|
||||||
|
Insert
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
Close Test
|
||||||
|
</template>
|
||||||
</v-btn>
|
</v-btn>
|
||||||
</template>
|
</template>
|
||||||
</dialog-base>
|
</dialog-base>
|
||||||
@@ -157,127 +185,201 @@ import Creatures from '/imports/api/creature/creatures/Creatures.js';
|
|||||||
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
|
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js';
|
||||||
import LibraryNodes from '/imports/api/library/LibraryNodes.js';
|
import LibraryNodes from '/imports/api/library/LibraryNodes.js';
|
||||||
import DialogBase from '/imports/ui/dialogStack/DialogBase.vue';
|
import DialogBase from '/imports/ui/dialogStack/DialogBase.vue';
|
||||||
import { getPropertyName } from '/imports/constants/PROPERTIES.js';
|
|
||||||
import PROPERTIES from '/imports/constants/PROPERTIES.js';
|
|
||||||
import TreeNodeView from '/imports/ui/properties/treeNodeViews/TreeNodeView.vue';
|
import TreeNodeView from '/imports/ui/properties/treeNodeViews/TreeNodeView.vue';
|
||||||
import PropertyDescription from '/imports/ui/properties/viewers/shared/PropertyDescription.vue'
|
import PropertyDescription from '/imports/ui/properties/viewers/shared/PropertyDescription.vue'
|
||||||
import evaluateString from '/imports/api/creature/computation/afterComputation/evaluateString.js';
|
import evaluateString from '/imports/api/creature/computation/afterComputation/evaluateString.js';
|
||||||
|
import getSlotFillFilter from '/imports/api/creature/creatureProperties/methods/getSlotFillFilter.js'
|
||||||
|
import Libraries from '/imports/api/library/Libraries.js';
|
||||||
|
import LibraryNodeExpansionContent from '/imports/ui/library/LibraryNodeExpansionContent.vue';
|
||||||
|
import PropertyTags from '/imports/ui/properties/viewers/shared/PropertyTags.vue';
|
||||||
|
import { getPropertyName } from '/imports/constants/PROPERTIES.js';
|
||||||
|
import { clone } from 'lodash';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
DialogBase,
|
DialogBase,
|
||||||
TreeNodeView,
|
TreeNodeView,
|
||||||
PropertyDescription,
|
PropertyDescription,
|
||||||
|
LibraryNodeExpansionContent,
|
||||||
|
PropertyTags,
|
||||||
},
|
},
|
||||||
props:{
|
props:{
|
||||||
slotId: {
|
slotId: {
|
||||||
type: String,
|
type: String,
|
||||||
required: true,
|
default: undefined,
|
||||||
},
|
},
|
||||||
creatureId: {
|
creatureId: {
|
||||||
type: String,
|
type: String,
|
||||||
required: true,
|
default: undefined,
|
||||||
|
},
|
||||||
|
dummySlot: {
|
||||||
|
type: Object,
|
||||||
|
default: undefined,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
data(){return {
|
data(){return {
|
||||||
selectedNode: undefined,
|
selectedNodeIds: [],
|
||||||
|
searchInput: undefined,
|
||||||
searchValue: undefined,
|
searchValue: undefined,
|
||||||
numFiltered: 0,
|
showDisabled: false,
|
||||||
|
disabledNodeCount: undefined,
|
||||||
}},
|
}},
|
||||||
computed: {
|
|
||||||
slotPropertyTypeName(){
|
|
||||||
if (!this.model) return;
|
|
||||||
if (!this.model.slotType) return 'property';
|
|
||||||
let propName = getPropertyName(this.model.slotType);
|
|
||||||
return propName && propName.toLowerCase();
|
|
||||||
},
|
|
||||||
},
|
|
||||||
reactiveProvide: {
|
reactiveProvide: {
|
||||||
name: 'context',
|
name: 'context',
|
||||||
include: ['creatureId'],
|
include: ['creatureId'],
|
||||||
},
|
},
|
||||||
methods:{
|
computed: {
|
||||||
getTitle(model){
|
tagsSearched(){
|
||||||
if (!model) return;
|
let or = [];
|
||||||
if (model.name) return model.name;
|
let not = [];
|
||||||
let prop = PROPERTIES[model.type]
|
if (this.model.slotTags && this.model.slotTags.length){
|
||||||
return prop && prop.name;
|
or.push(this.model.slotTags);
|
||||||
|
}
|
||||||
|
this.model.extraTags?.forEach(extras => {
|
||||||
|
if (extras.tags?.length){
|
||||||
|
if(extras.operation === 'OR'){
|
||||||
|
or.push(extras.tags);
|
||||||
|
} else if (extras.operation === 'NOT'){
|
||||||
|
not.push(extras.tags);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return {or, not};
|
||||||
},
|
},
|
||||||
searchChanged(val, ack){
|
slotPropertyTypeName(){
|
||||||
this._subs['slotFillers'].setData('searchTerm', val);
|
if (!this.model) return;
|
||||||
this._subs['slotFillers'].setData('limit', undefined);
|
if (!this.model.slotType) return 'Property';
|
||||||
this.selectedNode = undefined;
|
let propName = getPropertyName(this.model.slotType);
|
||||||
this.searchValue = val;
|
return propName;
|
||||||
setTimeout(ack, 200);
|
|
||||||
},
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
loadMore(){
|
loadMore(){
|
||||||
if (this.currentLimit >= this.countAll) return;
|
if (this.currentLimit >= this.countAll) return;
|
||||||
this._subs['slotFillers'].setData('limit', this.currentLimit + 20);
|
this._subs['slotFillers'].setData('limit', this.currentLimit + 50);
|
||||||
|
},
|
||||||
|
openPropertyDetails(id){
|
||||||
|
this.$store.commit('pushDialogStack', {
|
||||||
|
component: 'library-node-dialog',
|
||||||
|
elementId: id,
|
||||||
|
data: {
|
||||||
|
_id: id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
isDisabled(node){
|
||||||
|
return node._disabledBySlotFillerCondition ||
|
||||||
|
node._disabledByAlreadyAdded ||
|
||||||
|
(
|
||||||
|
node._disabledByQuantityFilled &&
|
||||||
|
!this.selectedNodeIds.includes(node._id)
|
||||||
|
)
|
||||||
},
|
},
|
||||||
insert(){
|
|
||||||
if (!this.selectedNode) return;
|
|
||||||
this.$store.dispatch('popDialogStack', this.selectedNode);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
meteor: {
|
meteor: {
|
||||||
$subscribe: {
|
$subscribe: {
|
||||||
'slotFillers'(){
|
'slotFillers'(){
|
||||||
return [this.slotId]
|
return [this.slotId, this.searchValue || undefined]
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
searchLoading(){
|
||||||
|
return !!this.searchValue && !this.$subReady.slotFillers;
|
||||||
|
},
|
||||||
model(){
|
model(){
|
||||||
return CreatureProperties.findOne(this.slotId);
|
if (this.slotId){
|
||||||
|
return CreatureProperties.findOne(this.slotId);
|
||||||
|
} else if (this.dummySlot) {
|
||||||
|
let model = clone(this.dummySlot)
|
||||||
|
model.quantityExpectedResult = +model.quantityExpected;
|
||||||
|
model.spaceLeft = model.quantityExpectedResult;
|
||||||
|
return model;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
creature(){
|
creature(){
|
||||||
|
if (!this.creatureId) return {variables: {}};
|
||||||
return Creatures.findOne(this.creatureId);
|
return Creatures.findOne(this.creatureId);
|
||||||
},
|
},
|
||||||
currentLimit(){
|
currentLimit(){
|
||||||
return this._subs['slotFillers'].data('limit') || 20;
|
return this._subs['slotFillers'].data('limit') || 50;
|
||||||
},
|
},
|
||||||
countAll(){
|
countAll(){
|
||||||
return this._subs['slotFillers'].data('countAll');
|
return this._subs['slotFillers'].data('countAll');
|
||||||
},
|
},
|
||||||
libraryNodes(){
|
alreadyAdded(){
|
||||||
let filter = {
|
let added = new Set();
|
||||||
|
if (!this.model.unique) return added;
|
||||||
|
let ancestorId;
|
||||||
|
if (this.model.unique === 'uniqueInSlot'){
|
||||||
|
ancestorId = this.model._id;
|
||||||
|
} else if (this.model.unique === 'uniqueInCreature'){
|
||||||
|
ancestorId = this.creatureId;
|
||||||
|
}
|
||||||
|
CreatureProperties.find({
|
||||||
|
'ancestors.id': ancestorId,
|
||||||
|
libraryNodeId: {$exists: true},
|
||||||
removed: {$ne: true},
|
removed: {$ne: true},
|
||||||
};
|
}, {
|
||||||
if (this.model.slotTags && this.model.slotTags.length){
|
fields: {libraryNodeId: 1},
|
||||||
filter.tags = {$all: this.model.slotTags};
|
}).forEach(prop => {
|
||||||
}
|
added.add(prop.libraryNodeId);
|
||||||
if (this.model.slotType){
|
});
|
||||||
filter.$or = [{
|
return added;
|
||||||
type: this.model.slotType
|
},
|
||||||
},{
|
totalQuantitySelected(){
|
||||||
type: 'slotFiller',
|
let quantitySelected = 0;
|
||||||
slotFillerType: this.model.slotType,
|
LibraryNodes.find({
|
||||||
}];
|
_id: {$in: this.selectedNodeIds}
|
||||||
}
|
}, {
|
||||||
|
fields: {slotQuantityFilled: 1},
|
||||||
|
}).forEach(node => {
|
||||||
|
if (Number.isFinite(node.slotQuantityFilled)){
|
||||||
|
quantitySelected += node.slotQuantityFilled;
|
||||||
|
} else {
|
||||||
|
quantitySelected += 1;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return quantitySelected;
|
||||||
|
},
|
||||||
|
spaceLeft(){
|
||||||
|
if (this.model.quantityExpectedResult === 0) return undefined;
|
||||||
|
return this.model.spaceLeft - this.totalQuantitySelected;
|
||||||
|
},
|
||||||
|
libraryNames(){
|
||||||
|
let names = {};
|
||||||
|
Libraries.find().forEach(lib => names[lib._id] = lib.name)
|
||||||
|
return names;
|
||||||
|
},
|
||||||
|
libraryNodes(){
|
||||||
|
let filter = getSlotFillFilter({slot: this.model});
|
||||||
let nodes = LibraryNodes.find(filter, {
|
let nodes = LibraryNodes.find(filter, {
|
||||||
sort: {name: 1, order: 1}
|
sort: {name: 1, order: 1}
|
||||||
}).fetch();
|
}).fetch();
|
||||||
let totalNodes = nodes.length;
|
let disabledNodeCount = 0;
|
||||||
// Filter out slotFillers whose condition isn't met or are too big to fit
|
// Mark slotFillers whose condition isn't met or are too big to fit
|
||||||
// the quantity to fill
|
// the quantity to fill
|
||||||
nodes = nodes.filter(node => {
|
nodes.forEach(node => {
|
||||||
if (node.slotFillerCondition){
|
if (node.slotFillerCondition){
|
||||||
let {result} = evaluateString({
|
let {result} = evaluateString({
|
||||||
string: node.slotFillerCondition,
|
string: node.slotFillerCondition,
|
||||||
scope: this.creature.variables,
|
scope: this.creature.variables,
|
||||||
fn: 'reduce',
|
fn: 'reduce',
|
||||||
});
|
});
|
||||||
if (!result.value) return false;
|
if (!result.value){
|
||||||
|
node._disabledBySlotFillerCondition = true;
|
||||||
|
disabledNodeCount += 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
let quantityToFill = node.type === 'slotFiller' ? node.slotQuantityFilled : 1;
|
||||||
if (
|
if (
|
||||||
node.type === 'slotFiller' &&
|
quantityToFill > this.spaceLeft
|
||||||
this.model.spaceLeft > 0 &&
|
|
||||||
node.slotQuantityFilled > this.model.spaceLeft
|
|
||||||
){
|
){
|
||||||
return false;
|
node._disabledByQuantityFilled = true;
|
||||||
|
}
|
||||||
|
if (this.alreadyAdded.has(node._id)){
|
||||||
|
node._disabledByAlreadyAdded = true;
|
||||||
}
|
}
|
||||||
return true;
|
|
||||||
});
|
});
|
||||||
this.numFiltered = totalNodes - nodes.length;
|
this.disabledNodeCount = disabledNodeCount;
|
||||||
if (nodes.length === 1) this.selectedNode = nodes[0];
|
|
||||||
return nodes;
|
return nodes;
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -285,17 +387,7 @@ export default {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="css" scoped>
|
<style lang="css" scoped>
|
||||||
.slot-card {
|
.disabled {
|
||||||
max-width: 500px;
|
opacity: 0.7;
|
||||||
width: 300px;
|
|
||||||
flex-grow: 1;
|
|
||||||
flex-shrink: 1;
|
|
||||||
margin: 4px;
|
|
||||||
}
|
|
||||||
.slot-card-text.line-clamp {
|
|
||||||
-webkit-line-clamp: 5;
|
|
||||||
}
|
|
||||||
.slot-card.selected {
|
|
||||||
background: #8E1B1B;
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -90,10 +90,10 @@ export default {
|
|||||||
slotId,
|
slotId,
|
||||||
creatureId,
|
creatureId,
|
||||||
},
|
},
|
||||||
callback(node){
|
callback(nodeIds){
|
||||||
if(!node) return;
|
if (!nodeIds || !nodeIds.length) return;
|
||||||
let newPropertyId = insertPropertyFromLibraryNode.call({
|
let newPropertyId = insertPropertyFromLibraryNode.call({
|
||||||
nodeIds: [node._id],
|
nodeIds,
|
||||||
parentRef: {
|
parentRef: {
|
||||||
'id': slotId,
|
'id': slotId,
|
||||||
'collection': 'creatureProperties',
|
'collection': 'creatureProperties',
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import ShareDialog from '/imports/ui/sharing/ShareDialog.vue';
|
|||||||
import SlotDetailsDialog from '/imports/ui/creature/slots/SlotDetailsDialog.vue';
|
import SlotDetailsDialog from '/imports/ui/creature/slots/SlotDetailsDialog.vue';
|
||||||
import SlotFillDialog from '/imports/ui/creature/slots/SlotFillDialog.vue';
|
import SlotFillDialog from '/imports/ui/creature/slots/SlotFillDialog.vue';
|
||||||
import TierTooLowDialog from '/imports/ui/user/TierTooLowDialog.vue';
|
import TierTooLowDialog from '/imports/ui/user/TierTooLowDialog.vue';
|
||||||
|
import TransferOwnershipDialog from '/imports/ui/sharing/TransferOwnershipDialog.vue';
|
||||||
import UsernameDialog from '/imports/ui/user/UsernameDialog.vue';
|
import UsernameDialog from '/imports/ui/user/UsernameDialog.vue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
@@ -47,5 +48,6 @@ export default {
|
|||||||
SlotDetailsDialog,
|
SlotDetailsDialog,
|
||||||
SlotFillDialog,
|
SlotFillDialog,
|
||||||
TierTooLowDialog,
|
TierTooLowDialog,
|
||||||
|
TransferOwnershipDialog,
|
||||||
UsernameDialog,
|
UsernameDialog,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -66,9 +66,10 @@
|
|||||||
import Creatures from '/imports/api/creature/creatures/Creatures.js';
|
import Creatures from '/imports/api/creature/creatures/Creatures.js';
|
||||||
import CreatureFolders from '/imports/api/creature/creatureFolders/CreatureFolders.js';
|
import CreatureFolders from '/imports/api/creature/creatureFolders/CreatureFolders.js';
|
||||||
import CreatureFolderList from '/imports/ui/creature/creatureList/CreatureFolderList.vue';
|
import CreatureFolderList from '/imports/ui/creature/creatureList/CreatureFolderList.vue';
|
||||||
|
import getCreatureUrlName from '/imports/api/creature/creatures/getCreatureUrlName.js';
|
||||||
|
|
||||||
const characterTransform = function(char){
|
const characterTransform = function(char){
|
||||||
char.url = `/character/${char._id}/${char.urlName || '-'}`;
|
char.url = `/character/${char._id}/${getCreatureUrlName(char)}`;
|
||||||
char.initial = char.name && char.name[0] || '?';
|
char.initial = char.name && char.name[0] || '?';
|
||||||
return char;
|
return char;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
class="property-viewer"
|
class="property-viewer"
|
||||||
/>
|
/>
|
||||||
<tree-node-list
|
<tree-node-list
|
||||||
|
v-if="$subReady.descendantLibraryNodes"
|
||||||
group="library-node-expansion"
|
group="library-node-expansion"
|
||||||
:children="propertyChildren"
|
:children="propertyChildren"
|
||||||
@selected="clickChild"
|
@selected="clickChild"
|
||||||
|
|||||||
@@ -82,9 +82,10 @@
|
|||||||
import {snackbar} from '/imports/ui/components/snackbars/SnackbarQueue.js';
|
import {snackbar} from '/imports/ui/components/snackbars/SnackbarQueue.js';
|
||||||
import CreatureFolderList from '/imports/ui/creature/creatureList/CreatureFolderList.vue';
|
import CreatureFolderList from '/imports/ui/creature/creatureList/CreatureFolderList.vue';
|
||||||
import ArchiveButton from '/imports/ui/creature/creatureList/ArchiveButton.vue';
|
import ArchiveButton from '/imports/ui/creature/creatureList/ArchiveButton.vue';
|
||||||
|
import getCreatureUrlName from '/imports/api/creature/creatures/getCreatureUrlName.js';
|
||||||
|
|
||||||
const characterTransform = function(char){
|
const characterTransform = function(char){
|
||||||
char.url = `/character/${char._id}/${char.urlName || '-'}`;
|
char.url = `/character/${char._id}/${getCreatureUrlName(char)}`;
|
||||||
char.initial = char.name && char.name[0] || '?';
|
char.initial = char.name && char.name[0] || '?';
|
||||||
return char;
|
return char;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -80,12 +80,6 @@ export default {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="css" scoped>
|
<style lang="css" scoped>
|
||||||
.skill-list-tile >>> .v-list__tile {
|
|
||||||
height: 34px;
|
|
||||||
}
|
|
||||||
.skill-list-tile{
|
|
||||||
background: inherit;
|
|
||||||
}
|
|
||||||
.prof-icon {
|
.prof-icon {
|
||||||
min-width: 30px;
|
min-width: 30px;
|
||||||
}
|
}
|
||||||
@@ -95,7 +89,3 @@ export default {
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<style lang="scss">
|
|
||||||
$list-item-min-height: 32px;
|
|
||||||
</style>
|
|
||||||
|
|||||||
@@ -7,6 +7,25 @@
|
|||||||
:error-messages="errors.name"
|
:error-messages="errors.name"
|
||||||
@change="change('name', ...arguments)"
|
@change="change('name', ...arguments)"
|
||||||
/>
|
/>
|
||||||
|
<smart-switch
|
||||||
|
label="Applied"
|
||||||
|
class="mt-0"
|
||||||
|
:value="model.applied"
|
||||||
|
:error-messages="errors.applied"
|
||||||
|
@change="change('applied', ...arguments)"
|
||||||
|
/>
|
||||||
|
<v-expand-transition>
|
||||||
|
<div v-if="model.applied">
|
||||||
|
<v-alert
|
||||||
|
type="info"
|
||||||
|
outlined
|
||||||
|
>
|
||||||
|
When buffs are applied they become active on a creature.
|
||||||
|
Turn this off if the buff needs to be applied to a target by an action
|
||||||
|
or spell.
|
||||||
|
</v-alert>
|
||||||
|
</div>
|
||||||
|
</v-expand-transition>
|
||||||
<text-area
|
<text-area
|
||||||
label="Description"
|
label="Description"
|
||||||
:value="model.description"
|
:value="model.description"
|
||||||
@@ -24,15 +43,18 @@
|
|||||||
@change="change('duration', ...arguments)"
|
@change="change('duration', ...arguments)"
|
||||||
/>
|
/>
|
||||||
-->
|
-->
|
||||||
<smart-select
|
<v-expand-transition>
|
||||||
label="Target"
|
<smart-select
|
||||||
:hint="targetOptionHint"
|
v-if="!model.applied"
|
||||||
:items="targetOptions"
|
label="Target"
|
||||||
:value="model.target"
|
:hint="targetOptionHint"
|
||||||
:error-messages="errors.target"
|
:items="targetOptions"
|
||||||
:menu-props="{auto: true, lazy: true}"
|
:value="model.target"
|
||||||
@change="change('target', ...arguments)"
|
:error-messages="errors.target"
|
||||||
/>
|
:menu-props="{auto: true, lazy: true}"
|
||||||
|
@change="change('target', ...arguments)"
|
||||||
|
/>
|
||||||
|
</v-expand-transition>
|
||||||
<smart-combobox
|
<smart-combobox
|
||||||
label="Tags"
|
label="Tags"
|
||||||
multiple
|
multiple
|
||||||
|
|||||||
@@ -17,16 +17,63 @@
|
|||||||
:error-messages="errors.slotType"
|
:error-messages="errors.slotType"
|
||||||
@change="change('slotType', ...arguments)"
|
@change="change('slotType', ...arguments)"
|
||||||
/>
|
/>
|
||||||
<smart-combobox
|
<v-layout align-center>
|
||||||
label="Tags Required"
|
<v-btn
|
||||||
hint="The slot must be filled with a property which has all the listed tags"
|
icon
|
||||||
multiple
|
style="margin-top: -30px;"
|
||||||
chips
|
class="mr-2"
|
||||||
deletable-chips
|
:loading="addExtraTagsLoading"
|
||||||
:value="model.slotTags"
|
:disabled="extraTagsFull"
|
||||||
:error-messages="errors.slotTags"
|
@click="addExtraTags"
|
||||||
@change="change('slotTags', ...arguments)"
|
>
|
||||||
/>
|
<v-icon>
|
||||||
|
mdi-plus
|
||||||
|
</v-icon>
|
||||||
|
</v-btn>
|
||||||
|
<smart-combobox
|
||||||
|
label="Tags Required"
|
||||||
|
hint="The slot must be filled with a property which has all the listed tags"
|
||||||
|
multiple
|
||||||
|
chips
|
||||||
|
deletable-chips
|
||||||
|
:value="model.slotTags"
|
||||||
|
:error-messages="errors.slotTags"
|
||||||
|
@change="change('slotTags', ...arguments)"
|
||||||
|
/>
|
||||||
|
</v-layout>
|
||||||
|
<v-slide-x-transition group>
|
||||||
|
<div
|
||||||
|
v-for="(extras, i) in model.extraTags"
|
||||||
|
:key="extras._id"
|
||||||
|
class="extra-tags layout align-center justify-space-between"
|
||||||
|
>
|
||||||
|
<smart-select
|
||||||
|
label="Operation"
|
||||||
|
style="width: 90px; flex-grow: 0;"
|
||||||
|
:items="extraTagOperations"
|
||||||
|
:value="extras.operation"
|
||||||
|
:error-messages="errors.extraTags && errors.extraTags[i]"
|
||||||
|
@change="change(['extraTags', i, 'operation'], ...arguments)"
|
||||||
|
/>
|
||||||
|
<smart-combobox
|
||||||
|
label="Tags"
|
||||||
|
:hint="extras.operation === 'OR' ? 'The slot can be filled with a property that has all of these tags instead' : 'The slot cannot be filled with a property that has any of these tags'"
|
||||||
|
class="mx-2"
|
||||||
|
multiple
|
||||||
|
chips
|
||||||
|
deletable-chips
|
||||||
|
:value="extras.tags"
|
||||||
|
@change="change(['extraTags', i, 'tags'], ...arguments)"
|
||||||
|
/>
|
||||||
|
<v-btn
|
||||||
|
icon
|
||||||
|
style="margin-top: -30px;"
|
||||||
|
@click="$emit('pull', {path: ['extraTags', i]})"
|
||||||
|
>
|
||||||
|
<v-icon>mdi-delete</v-icon>
|
||||||
|
</v-btn>
|
||||||
|
</div>
|
||||||
|
</v-slide-x-transition>
|
||||||
<text-field
|
<text-field
|
||||||
label="Quantity"
|
label="Quantity"
|
||||||
hint="How many matching properties must be used to fill this slot, 0 is unlimited"
|
hint="How many matching properties must be used to fill this slot, 0 is unlimited"
|
||||||
@@ -45,6 +92,29 @@
|
|||||||
/>
|
/>
|
||||||
<calculation-error-list :errors="model.slotConditionErrors" />
|
<calculation-error-list :errors="model.slotConditionErrors" />
|
||||||
|
|
||||||
|
<smart-select
|
||||||
|
label="Unique"
|
||||||
|
style="flex-basis: 300px;"
|
||||||
|
clearable
|
||||||
|
hint="Do the properties that fill this slot need to be unique?"
|
||||||
|
:items="uniqueOptions"
|
||||||
|
:value="model.unique"
|
||||||
|
:error-messages="errors.unique"
|
||||||
|
@change="change('unique', ...arguments)"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<v-layout justify-center>
|
||||||
|
<v-btn
|
||||||
|
v-if="context.isLibraryForm"
|
||||||
|
color="accent"
|
||||||
|
class="ma-2 mb-4"
|
||||||
|
data-id="test-slot-button"
|
||||||
|
@click="testSlot"
|
||||||
|
>
|
||||||
|
Test Slot
|
||||||
|
</v-btn>
|
||||||
|
</v-layout>
|
||||||
|
|
||||||
<text-area
|
<text-area
|
||||||
label="Description"
|
label="Description"
|
||||||
:value="model.description"
|
:value="model.description"
|
||||||
@@ -77,6 +147,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<smart-combobox
|
<smart-combobox
|
||||||
label="Tags"
|
label="Tags"
|
||||||
|
hint="This slot's own tags which will be used to fill other slots"
|
||||||
multiple
|
multiple
|
||||||
chips
|
chips
|
||||||
deletable-chips
|
deletable-chips
|
||||||
@@ -92,6 +163,7 @@
|
|||||||
import FormSection from '/imports/ui/properties/forms/shared/FormSection.vue';
|
import FormSection from '/imports/ui/properties/forms/shared/FormSection.vue';
|
||||||
import CalculationErrorList from '/imports/ui/properties/forms/shared/CalculationErrorList.vue';
|
import CalculationErrorList from '/imports/ui/properties/forms/shared/CalculationErrorList.vue';
|
||||||
import PROPERTIES from '/imports/constants/PROPERTIES.js';
|
import PROPERTIES from '/imports/constants/PROPERTIES.js';
|
||||||
|
import { SlotSchema } from '/imports/api/properties/Slots.js';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
@@ -99,12 +171,60 @@
|
|||||||
CalculationErrorList,
|
CalculationErrorList,
|
||||||
},
|
},
|
||||||
mixins: [propertyFormMixin],
|
mixins: [propertyFormMixin],
|
||||||
|
inject: {
|
||||||
|
context: { default: {} }
|
||||||
|
},
|
||||||
data(){
|
data(){
|
||||||
let slotTypes = [];
|
let slotTypes = [];
|
||||||
for (let key in PROPERTIES){
|
for (let key in PROPERTIES){
|
||||||
slotTypes.push({text: PROPERTIES[key].name, value: key});
|
slotTypes.push({text: PROPERTIES[key].name, value: key});
|
||||||
}
|
}
|
||||||
return {slotTypes};
|
return {
|
||||||
|
slotTypes,
|
||||||
|
addExtraTagsLoading: false,
|
||||||
|
extraTagOperations: ['OR', 'NOT'],
|
||||||
|
uniqueOptions: [{
|
||||||
|
text: 'Each property inside this slot should be unique',
|
||||||
|
value: 'uniqueInSlot',
|
||||||
|
}, {
|
||||||
|
text: 'Properties in this slot should be unique accross the whole character',
|
||||||
|
value: 'uniqueInCreature',
|
||||||
|
}],
|
||||||
|
};
|
||||||
},
|
},
|
||||||
|
computed: {
|
||||||
|
extraTagsFull(){
|
||||||
|
if (!this.model.extraTags) return false;
|
||||||
|
let maxCount = SlotSchema.get('extraTags', 'maxCount');
|
||||||
|
return this.model.extraTags.length >= maxCount;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
acknowledgeAddResult(){
|
||||||
|
this.addExtraTagsLoading = false;
|
||||||
|
},
|
||||||
|
addExtraTags(){
|
||||||
|
this.addExtraTagsLoading = true;
|
||||||
|
this.$emit('push', {
|
||||||
|
path: ['extraTags'],
|
||||||
|
value: {
|
||||||
|
_id: Random.id(),
|
||||||
|
operation: 'OR',
|
||||||
|
tags: [],
|
||||||
|
},
|
||||||
|
ack: this.acknowledgeAddResult,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
testSlot(){
|
||||||
|
if (!this.context.isLibraryForm) return;
|
||||||
|
this.$store.commit('pushDialogStack', {
|
||||||
|
component: 'slot-fill-dialog',
|
||||||
|
elementId: 'test-slot-button',
|
||||||
|
data: {
|
||||||
|
dummySlot: this.model,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ export default {
|
|||||||
title(){
|
title(){
|
||||||
let model = this.model;
|
let model = this.model;
|
||||||
if (!model) return;
|
if (!model) return;
|
||||||
if (model.quantity !== 1){
|
if (Number.isFinite(model.quantity) && model.quantity !== 1){
|
||||||
if (model.plural){
|
if (model.plural){
|
||||||
return `${model.quantity} ${model.plural}`;
|
return `${model.quantity} ${model.plural}`;
|
||||||
} else if (model.name){
|
} else if (model.name){
|
||||||
|
|||||||
@@ -22,12 +22,12 @@
|
|||||||
|
|
||||||
<script lang="js">
|
<script lang="js">
|
||||||
import treeNodeViewMixin from '/imports/ui/properties/treeNodeViews/treeNodeViewMixin.js';
|
import treeNodeViewMixin from '/imports/ui/properties/treeNodeViews/treeNodeViewMixin.js';
|
||||||
import TreeNodeView from '/imports/ui/properties/treeNodeViews/TreeNodeView.vue';
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
name: 'ReferenceTreeNode',
|
||||||
TreeNodeView,
|
|
||||||
},
|
|
||||||
mixins: [treeNodeViewMixin],
|
mixins: [treeNodeViewMixin],
|
||||||
|
beforeCreate () {
|
||||||
|
this.$options.components.TreeNodeView = require('/imports/ui/properties/treeNodeViews/TreeNodeView.vue').default
|
||||||
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
import treeNodeViewIndex from '/imports/ui/properties/treeNodeViews/treeNodeViewIndex.js';
|
import treeNodeViewIndex from '/imports/ui/properties/treeNodeViews/treeNodeViewIndex.js';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
name: 'TreeNodeView',
|
||||||
components: {
|
components: {
|
||||||
...treeNodeViewIndex
|
...treeNodeViewIndex
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,10 +1,22 @@
|
|||||||
<template lang="html">
|
<template lang="html">
|
||||||
<div
|
<div
|
||||||
v-if="tagString"
|
v-if="tags.length"
|
||||||
class="tags"
|
class="tags"
|
||||||
:class="{'ma-3': !noMargin}"
|
:class="{'ma-2': !noMargin}"
|
||||||
>
|
>
|
||||||
{{ tagString }}
|
<span
|
||||||
|
v-if="prefix"
|
||||||
|
class="mx-1 text-overline"
|
||||||
|
>
|
||||||
|
{{ prefix }}
|
||||||
|
</span>
|
||||||
|
<v-chip
|
||||||
|
v-for="(tag, i) in tags"
|
||||||
|
:key="tag + i"
|
||||||
|
class="mx-1"
|
||||||
|
>
|
||||||
|
{{ tag }}
|
||||||
|
</v-chip>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -16,17 +28,13 @@ export default {
|
|||||||
default: () => [],
|
default: () => [],
|
||||||
},
|
},
|
||||||
noMargin: Boolean,
|
noMargin: Boolean,
|
||||||
|
prefix: {
|
||||||
|
type: String,
|
||||||
|
default: undefined,
|
||||||
|
}
|
||||||
},
|
},
|
||||||
computed:{
|
|
||||||
tagString(){
|
|
||||||
return this.tags.join(', ');
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="css" scoped>
|
<style lang="css" scoped>
|
||||||
.tags {
|
|
||||||
font-style: italic;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -130,18 +130,9 @@ RouterFactory.configure(factory => {
|
|||||||
meta: {
|
meta: {
|
||||||
title: 'Library',
|
title: 'Library',
|
||||||
},
|
},
|
||||||
},{
|
|
||||||
path: '/character/:id/:urlName',
|
|
||||||
components: {
|
|
||||||
default: CharacterSheetPage,
|
|
||||||
toolbar: CharacterSheetToolbar,
|
|
||||||
rightDrawer: CharacterSheetRightDrawer,
|
|
||||||
},
|
|
||||||
meta: {
|
|
||||||
title: 'Character Sheet',
|
|
||||||
},
|
|
||||||
},{
|
},{
|
||||||
path: '/character/:id',
|
path: '/character/:id',
|
||||||
|
alias: '/character/:id/:urlName',
|
||||||
components: {
|
components: {
|
||||||
default: CharacterSheetPage,
|
default: CharacterSheetPage,
|
||||||
toolbar: CharacterSheetToolbar,
|
toolbar: CharacterSheetToolbar,
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
v-if="model.public && docRef.collection === 'libraries'"
|
v-if="model.public && docRef.collection === 'libraries'"
|
||||||
readonly
|
readonly
|
||||||
label="Link"
|
label="Link"
|
||||||
:value="'https://beta.dicecloud.com' + this.$router.resolve({
|
:value="'https://beta.dicecloud.com' + $router.resolve({
|
||||||
name: 'singleLibrary',
|
name: 'singleLibrary',
|
||||||
params: { id: model._id },
|
params: { id: model._id },
|
||||||
}).href"
|
}).href"
|
||||||
@@ -56,6 +56,7 @@
|
|||||||
<v-menu
|
<v-menu
|
||||||
bottom
|
bottom
|
||||||
left
|
left
|
||||||
|
:data-id="'menu-' + user._id"
|
||||||
>
|
>
|
||||||
<template #activator="{ on }">
|
<template #activator="{ on }">
|
||||||
<v-btn
|
<v-btn
|
||||||
@@ -84,6 +85,15 @@
|
|||||||
</v-list-item-action>
|
</v-list-item-action>
|
||||||
<v-list-item-title>View only</v-list-item-title>
|
<v-list-item-title>View only</v-list-item-title>
|
||||||
</v-list-item>
|
</v-list-item>
|
||||||
|
<v-list-item
|
||||||
|
v-if="user.permission === 'writer'"
|
||||||
|
@click="makeOwner(user)"
|
||||||
|
>
|
||||||
|
<v-list-item-action>
|
||||||
|
<v-icon>mdi-signature</v-icon>
|
||||||
|
</v-list-item-action>
|
||||||
|
<v-list-item-title>Transfer Onwership</v-list-item-title>
|
||||||
|
</v-list-item>
|
||||||
<v-list-item @click="updateSharing(user._id, 'none')">
|
<v-list-item @click="updateSharing(user._id, 'none')">
|
||||||
<v-list-item-action>
|
<v-list-item-action>
|
||||||
<v-icon>mdi-delete</v-icon>
|
<v-icon>mdi-delete</v-icon>
|
||||||
@@ -181,6 +191,16 @@ export default {
|
|||||||
userId,
|
userId,
|
||||||
role,
|
role,
|
||||||
});
|
});
|
||||||
|
},
|
||||||
|
makeOwner(user){
|
||||||
|
this.$store.commit('pushDialogStack', {
|
||||||
|
component: 'transfer-ownership-dialog',
|
||||||
|
elementId: 'menu-' + user._id,
|
||||||
|
data: {
|
||||||
|
docRef: this.docRef,
|
||||||
|
user,
|
||||||
|
},
|
||||||
|
});
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
meteor: {
|
meteor: {
|
||||||
|
|||||||
79
app/imports/ui/sharing/TransferOwnershipDialog.vue
Normal file
79
app/imports/ui/sharing/TransferOwnershipDialog.vue
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
<template lang="html">
|
||||||
|
<dialog-base>
|
||||||
|
<v-toolbar-title slot="toolbar">
|
||||||
|
Transfer Ownership
|
||||||
|
</v-toolbar-title>
|
||||||
|
<v-alert
|
||||||
|
type="error"
|
||||||
|
outlined
|
||||||
|
>
|
||||||
|
<template v-if="error">
|
||||||
|
<p>
|
||||||
|
{{ error }}
|
||||||
|
</p>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<p>
|
||||||
|
Are you sure you want to transfer ownership to {{ user.username || user._id }}?
|
||||||
|
</p><p>
|
||||||
|
This can only be undone by the user you are transferring ownership to.
|
||||||
|
</p><p>
|
||||||
|
You will still have edit permission.
|
||||||
|
</p>
|
||||||
|
</template>
|
||||||
|
</v-alert>
|
||||||
|
<v-layout justify-center>
|
||||||
|
<v-btn
|
||||||
|
color="accent"
|
||||||
|
@click="transfer"
|
||||||
|
>
|
||||||
|
Transfer
|
||||||
|
<template v-if="user.username">
|
||||||
|
to {{ user.username }}
|
||||||
|
</template>
|
||||||
|
</v-btn>
|
||||||
|
</v-layout>
|
||||||
|
</dialog-base>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="js">
|
||||||
|
import DialogBase from '/imports/ui/dialogStack/DialogBase.vue';
|
||||||
|
import { transferOwnership } from '/imports/api/sharing/sharing.js';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
DialogBase,
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
docRef: {
|
||||||
|
type: Object,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
user: {
|
||||||
|
type: Object,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data(){ return {
|
||||||
|
error: undefined,
|
||||||
|
}},
|
||||||
|
methods: {
|
||||||
|
transfer(){
|
||||||
|
transferOwnership.call({
|
||||||
|
docRef: this.docRef,
|
||||||
|
userId: this.user._id
|
||||||
|
}, error => {
|
||||||
|
if (!error){
|
||||||
|
this.error = undefined;
|
||||||
|
this.$store.dispatch('popDialogStack')
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.error = error.reason || error.message || error.toString();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="css" scoped>
|
||||||
|
</style>
|
||||||
23
app/package-lock.json
generated
23
app/package-lock.json
generated
@@ -2330,6 +2330,11 @@
|
|||||||
"yallist": "^4.0.0"
|
"yallist": "^4.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"mkdirp": {
|
||||||
|
"version": "1.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
|
||||||
|
"integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw=="
|
||||||
|
},
|
||||||
"mongo-object": {
|
"mongo-object": {
|
||||||
"version": "0.1.4",
|
"version": "0.1.4",
|
||||||
"resolved": "https://registry.npmjs.org/mongo-object/-/mongo-object-0.1.4.tgz",
|
"resolved": "https://registry.npmjs.org/mongo-object/-/mongo-object-0.1.4.tgz",
|
||||||
@@ -2805,6 +2810,11 @@
|
|||||||
"source-map": "^0.6.0"
|
"source-map": "^0.6.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"speakingurl": {
|
||||||
|
"version": "14.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/speakingurl/-/speakingurl-14.0.1.tgz",
|
||||||
|
"integrity": "sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ=="
|
||||||
|
},
|
||||||
"sprintf-js": {
|
"sprintf-js": {
|
||||||
"version": "1.0.3",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
|
||||||
@@ -2956,9 +2966,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"tar": {
|
"tar": {
|
||||||
"version": "6.1.0",
|
"version": "6.1.6",
|
||||||
"resolved": "https://registry.npmjs.org/tar/-/tar-6.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/tar/-/tar-6.1.6.tgz",
|
||||||
"integrity": "sha512-DUCttfhsnLCjwoDoFcI+B2iJgYa93vBnDUATYEeRx6sntCTdN01VnqsIuTlALXla/LWooNg0yEGeB+Y8WdFxGA==",
|
"integrity": "sha512-oaWyu5dQbHaYcyZCTfyPpC+VmI62/OM2RTUYavTk1MDr1cwW5Boi3baeYQKiZbY2uSQJGr+iMOzb/JFxLrft+g==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"chownr": "^2.0.0",
|
"chownr": "^2.0.0",
|
||||||
"fs-minipass": "^2.0.0",
|
"fs-minipass": "^2.0.0",
|
||||||
@@ -2966,13 +2976,6 @@
|
|||||||
"minizlib": "^2.1.1",
|
"minizlib": "^2.1.1",
|
||||||
"mkdirp": "^1.0.3",
|
"mkdirp": "^1.0.3",
|
||||||
"yallist": "^4.0.0"
|
"yallist": "^4.0.0"
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"mkdirp": {
|
|
||||||
"version": "1.0.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
|
|
||||||
"integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw=="
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"text-table": {
|
"text-table": {
|
||||||
|
|||||||
@@ -39,6 +39,7 @@
|
|||||||
"request": "^2.88.2",
|
"request": "^2.88.2",
|
||||||
"simpl-schema": "^1.12.0",
|
"simpl-schema": "^1.12.0",
|
||||||
"source-map-support": "^0.5.16",
|
"source-map-support": "^0.5.16",
|
||||||
|
"speakingurl": "^14.0.1",
|
||||||
"styles": "^0.2.1",
|
"styles": "^0.2.1",
|
||||||
"underscore": "^1.13.1",
|
"underscore": "^1.13.1",
|
||||||
"vue": "2.6.10",
|
"vue": "2.6.10",
|
||||||
|
|||||||
Reference in New Issue
Block a user