From 6b724cf365993a685ce09b7cb855a315e0cd4b30 Mon Sep 17 00:00:00 2001 From: Stefan Zermatten Date: Mon, 25 Apr 2022 11:16:02 +0200 Subject: [PATCH 001/142] Dicecloud instances without db version numbers won't go into migration mode --- app/imports/constants/MAINTENANCE_MODE.js | 1 + 1 file changed, 1 insertion(+) diff --git a/app/imports/constants/MAINTENANCE_MODE.js b/app/imports/constants/MAINTENANCE_MODE.js index f81e01e4..42045410 100644 --- a/app/imports/constants/MAINTENANCE_MODE.js +++ b/app/imports/constants/MAINTENANCE_MODE.js @@ -6,6 +6,7 @@ if (Meteor.isServer){ const dbVersion = Migrations.getVersion(); if ( !Meteor.settings.public.maintenanceMode && + dbVersion !== undefined && SCHEMA_VERSION !== dbVersion ){ Meteor.settings.public.maintenanceMode = { From ea32c54f57cd700a87dac91081d3c4903bd33489 Mon Sep 17 00:00:00 2001 From: Stefan Zermatten Date: Mon, 25 Apr 2022 13:57:39 +0200 Subject: [PATCH 002/142] Fixed massive writes to creature.variables on calc Now only writes changed variables, preventing oplog from being polluted with massive updates --- .../engine/computation/CreatureComputation.js | 3 ++- .../computation/buildCreatureComputation.js | 2 +- .../writeComputation/writeScope.js | 27 ++++++++++++++++--- app/imports/api/engine/computeCreature.js | 2 +- 4 files changed, 28 insertions(+), 6 deletions(-) diff --git a/app/imports/api/engine/computation/CreatureComputation.js b/app/imports/api/engine/computation/CreatureComputation.js index 90e28f44..278b3276 100644 --- a/app/imports/api/engine/computation/CreatureComputation.js +++ b/app/imports/api/engine/computation/CreatureComputation.js @@ -2,7 +2,7 @@ import { EJSON } from 'meteor/ejson'; import createGraph from 'ngraph.graph'; export default class CreatureComputation { - constructor(properties){ + constructor(properties, creature){ // Set up fields this.originalPropsById = {}; this.propsById = {}; @@ -11,6 +11,7 @@ export default class CreatureComputation { this.props = properties; this.dependencyGraph = createGraph(); this.errors = []; + this.creature = creature; // Store properties for easy access later properties.forEach(prop => { diff --git a/app/imports/api/engine/computation/buildCreatureComputation.js b/app/imports/api/engine/computation/buildCreatureComputation.js index 8e81e314..ebf06518 100644 --- a/app/imports/api/engine/computation/buildCreatureComputation.js +++ b/app/imports/api/engine/computation/buildCreatureComputation.js @@ -54,7 +54,7 @@ function getCreature(creatureId){ export function buildComputationFromProps(properties, creature){ - const computation = new CreatureComputation(properties); + const computation = new CreatureComputation(properties, creature); // Dependency graph where edge(a, b) means a depends on b // The graph includes all dependencies even of inactive properties // such that any properties changing without changing their dependencies diff --git a/app/imports/api/engine/computation/writeComputation/writeScope.js b/app/imports/api/engine/computation/writeComputation/writeScope.js index 3661df4a..c318fb9e 100644 --- a/app/imports/api/engine/computation/writeComputation/writeScope.js +++ b/app/imports/api/engine/computation/writeComputation/writeScope.js @@ -1,10 +1,31 @@ import Creatures from '/imports/api/creature/creatures/Creatures.js'; +import { EJSON } from 'meteor/ejson'; -export default function writeScope(creatureId, scope){ - // Remove large properties that aren't likely to be accessed +export default function writeScope(creatureId, computation) { + const scope = computation.scope; + const variables = computation.creature.variables || {}; + let $set; for (const key in scope){ + // Remove large properties that aren't likely to be accessed delete scope[key].parent; delete scope[key].ancestors; + + // Remove empty keys + for (const subKey in scope[key]) { + if (scope[key][subKey] === undefined) { + delete scope[key][subKey] + } + } + + // Only update changed fields + if (!EJSON.equals(variables[key], scope[key])) { + if (!$set) $set = {}; + + // Set the changed key in the creature variables + $set[`variables.${key}`] = scope[key]; + } + } + if ($set) { + Creatures.update(creatureId, {$set}); } - Creatures.update(creatureId, {$set: {variables: scope}}); } diff --git a/app/imports/api/engine/computeCreature.js b/app/imports/api/engine/computeCreature.js index a45188bf..cc3c0da9 100644 --- a/app/imports/api/engine/computeCreature.js +++ b/app/imports/api/engine/computeCreature.js @@ -10,7 +10,7 @@ export default function computeCreature(creatureId){ try { computeCreatureComputation(computation); writeAlteredProperties(computation); - writeScope(creatureId, computation.scope); + writeScope(creatureId, computation); } catch (e){ const errorText = e.reason || e.message || e.toString(); computation.errors.push({ From 3e97baaaaaccb66cb7ee70404763700cb679a25d Mon Sep 17 00:00:00 2001 From: Stefan Zermatten Date: Mon, 25 Apr 2022 16:16:17 +0200 Subject: [PATCH 003/142] Progress on storing user images --- app/imports/api/files/UserImages.js | 3 +- app/imports/api/files/s3FileStorage.js | 6 +- .../server/imageProcessing/createThumbnail.js | 8 + app/imports/server/publications/index.js | 1 + app/imports/server/publications/userImages.js | 7 + .../ui/components/ImageUploadInput.vue | 77 +++++ app/imports/ui/files/UserImageCard.vue | 56 ++++ app/imports/ui/pages/Files.vue | 111 +++++-- app/package-lock.json | 295 +++++++++++++++++- app/package.json | 1 + 10 files changed, 532 insertions(+), 33 deletions(-) create mode 100644 app/imports/server/imageProcessing/createThumbnail.js create mode 100644 app/imports/server/publications/userImages.js create mode 100644 app/imports/ui/components/ImageUploadInput.vue create mode 100644 app/imports/ui/files/UserImageCard.vue diff --git a/app/imports/api/files/UserImages.js b/app/imports/api/files/UserImages.js index ead7271b..fad4576b 100644 --- a/app/imports/api/files/UserImages.js +++ b/app/imports/api/files/UserImages.js @@ -9,9 +9,10 @@ const UserImages = createS3FilesCollection({ return 'Please upload with size equal or less than 10MB'; } // Allow common image extensions - if (/gif|png|jpe?g|webp/i.test(file.extension || '')) { + if (!/gif|png|jpe?g|webp/i.test(file.extension || '')) { return 'Please upload an image file only'; } + return true } }); diff --git a/app/imports/api/files/s3FileStorage.js b/app/imports/api/files/s3FileStorage.js index e26e6ac5..affdf9db 100644 --- a/app/imports/api/files/s3FileStorage.js +++ b/app/imports/api/files/s3FileStorage.js @@ -47,6 +47,7 @@ if (Meteor.isServer && Meteor.settings.useS3) { collectionName, storagePath, onBeforeUpload, + onAfterUpload, debug = Meteor.isProduction, allowClientCode = false, }){ @@ -54,7 +55,10 @@ if (Meteor.isServer && Meteor.settings.useS3) { collectionName, storagePath, onBeforeUpload, - onAfterUpload(fileRef){ + onAfterUpload(fileRef) { + // Call the provided afterUpload hook first + onAfterUpload(fileRef); + // Start moving files to AWS:S3 // after fully received by the Meteor server diff --git a/app/imports/server/imageProcessing/createThumbnail.js b/app/imports/server/imageProcessing/createThumbnail.js new file mode 100644 index 00000000..4837133d --- /dev/null +++ b/app/imports/server/imageProcessing/createThumbnail.js @@ -0,0 +1,8 @@ +import * as sharp from 'sharp'; + +export default async function createThumbnail(image) { + await sharp(image) + .resize(320, 240) + .png() + .toBuffer(); +} diff --git a/app/imports/server/publications/index.js b/app/imports/server/publications/index.js index 3bdc770a..ce8385af 100644 --- a/app/imports/server/publications/index.js +++ b/app/imports/server/publications/index.js @@ -11,3 +11,4 @@ import '/imports/server/publications/ownedDocuments.js'; import '/imports/server/publications/archivedCreatures.js'; import '/imports/server/publications/searchLibraryNodes.js'; import '/imports/server/publications/archiveFiles.js'; +import '/imports/server/publications/userImages.js'; diff --git a/app/imports/server/publications/userImages.js b/app/imports/server/publications/userImages.js new file mode 100644 index 00000000..765b607a --- /dev/null +++ b/app/imports/server/publications/userImages.js @@ -0,0 +1,7 @@ +import UserImages from '/imports/api/files/UserImages.js'; + +Meteor.publish('userImages', function () { + return UserImages.find({ + userId: this.userId, + }).cursor; +}); diff --git a/app/imports/ui/components/ImageUploadInput.vue b/app/imports/ui/components/ImageUploadInput.vue new file mode 100644 index 00000000..c93235b4 --- /dev/null +++ b/app/imports/ui/components/ImageUploadInput.vue @@ -0,0 +1,77 @@ + + + + + \ No newline at end of file diff --git a/app/imports/ui/files/UserImageCard.vue b/app/imports/ui/files/UserImageCard.vue new file mode 100644 index 00000000..e70141a6 --- /dev/null +++ b/app/imports/ui/files/UserImageCard.vue @@ -0,0 +1,56 @@ + + + diff --git a/app/imports/ui/pages/Files.vue b/app/imports/ui/pages/Files.vue index 8d77f097..050b6064 100644 --- a/app/imports/ui/pages/Files.vue +++ b/app/imports/ui/pages/Files.vue @@ -7,65 +7,103 @@ + + Archived Characters + + + + + + mdi-file-upload-outline + + + + + + + + + Images + + + + + + + diff --git a/app/imports/ui/creature/buildTree/BuildTreeNodeList.vue b/app/imports/ui/creature/buildTree/BuildTreeNodeList.vue new file mode 100644 index 00000000..f6a22de6 --- /dev/null +++ b/app/imports/ui/creature/buildTree/BuildTreeNodeList.vue @@ -0,0 +1,30 @@ + + + diff --git a/app/imports/ui/creature/buildTree/FillSlotButton.vue b/app/imports/ui/creature/buildTree/FillSlotButton.vue new file mode 100644 index 00000000..0718f64c --- /dev/null +++ b/app/imports/ui/creature/buildTree/FillSlotButton.vue @@ -0,0 +1,56 @@ + + + + + \ No newline at end of file diff --git a/app/imports/ui/creature/character/characterSheetTabs/BuildTab.vue b/app/imports/ui/creature/character/characterSheetTabs/BuildTab.vue index 15f3eb49..cfcf3fe8 100644 --- a/app/imports/ui/creature/character/characterSheetTabs/BuildTab.vue +++ b/app/imports/ui/creature/character/characterSheetTabs/BuildTab.vue @@ -1,8 +1,14 @@ - - diff --git a/app/imports/server/publications/singleCharacter.js b/app/imports/server/publications/singleCharacter.js index 611f1d23..c2ca8c0c 100644 --- a/app/imports/server/publications/singleCharacter.js +++ b/app/imports/server/publications/singleCharacter.js @@ -5,6 +5,7 @@ import CreatureLogs from '/imports/api/creature/log/CreatureLogs.js'; import { assertViewPermission } from '/imports/api/creature/creatures/creaturePermissions.js'; import computeCreature from '/imports/api/engine/computeCreature.js'; import VERSION from '/imports/constants/VERSION.js'; +import { loadCreature } from '/imports/api/engine/loadCreatures.js'; let schema = new SimpleSchema({ creatureId: { @@ -13,7 +14,8 @@ let schema = new SimpleSchema({ }, }); -Meteor.publish('singleCharacter', function(creatureId){ +Meteor.publish('singleCharacter', function (creatureId) { + const self = this; try { schema.validate({ creatureId }); } catch (e){ @@ -21,21 +23,24 @@ Meteor.publish('singleCharacter', function(creatureId){ } this.autorun(function (computation){ let userId = this.userId; - let creatureCursor - creatureCursor = Creatures.find({ + let permissionCreature = Creatures.findOne({ _id: creatureId, + }, { + fields: { owner: 1, readers: 1, writers: 1, public: 1, computeVersion: 1 } }); - let creature = creatureCursor.fetch()[0]; - try { assertViewPermission(creature, userId) } - catch(e){ return [] } - if (creature.computeVersion !== VERSION && computation.firstRun){ + try { assertViewPermission(permissionCreature, userId) } + catch (e) { return [] } + loadCreature(creatureId, self); + if (permissionCreature.computeVersion !== VERSION && computation.firstRun){ try { computeCreature(creatureId) } catch(e){ console.error(e) } } return [ - creatureCursor, + Creatures.find({ + _id: creatureId, + }), CreatureProperties.find({ 'ancestors.id': creatureId, }), From 7a35c66904c48765d6db639ca1deeab548b14fc6 Mon Sep 17 00:00:00 2001 From: Stefan Zermatten Date: Wed, 11 May 2022 13:33:56 +0200 Subject: [PATCH 014/142] Removed performance logging from loading creatures --- .../api/engine/computation/buildCreatureComputation.js | 4 ++-- app/imports/api/engine/computeCreature.js | 2 -- app/imports/api/engine/loadCreatures.js | 4 ---- 3 files changed, 2 insertions(+), 8 deletions(-) diff --git a/app/imports/api/engine/computation/buildCreatureComputation.js b/app/imports/api/engine/computation/buildCreatureComputation.js index f2b07158..47612830 100644 --- a/app/imports/api/engine/computation/buildCreatureComputation.js +++ b/app/imports/api/engine/computation/buildCreatureComputation.js @@ -44,7 +44,7 @@ function getProperties(creatureId) { const props = Array.from(creature.properties.values()); return props; } - console.time(`fetching from db: ${creatureId}`) + console.time(`Cache miss fetching from db: ${creatureId}`) const props = CreatureProperties.find({ 'ancestors.id': creatureId, 'removed': {$ne: true}, @@ -52,7 +52,7 @@ function getProperties(creatureId) { sort: { order: 1 }, fields: { icon: 0 }, }).fetch(); - console.timeEnd(`fetching from db: ${creatureId}`); + console.timeEnd(`Cache miss fetching from db: ${creatureId}`); return props; } diff --git a/app/imports/api/engine/computeCreature.js b/app/imports/api/engine/computeCreature.js index 84c90e8a..75064776 100644 --- a/app/imports/api/engine/computeCreature.js +++ b/app/imports/api/engine/computeCreature.js @@ -6,10 +6,8 @@ import writeErrors from './computation/writeComputation/writeErrors.js'; export default function computeCreature(creatureId){ if (Meteor.isClient) return; - console.time('Compute'); const computation = buildCreatureComputation(creatureId); computeComputation(computation, creatureId); - console.timeEnd('Compute'); } function computeComputation(computation, creatureId) { diff --git a/app/imports/api/engine/loadCreatures.js b/app/imports/api/engine/loadCreatures.js index 46090140..f7870440 100644 --- a/app/imports/api/engine/loadCreatures.js +++ b/app/imports/api/engine/loadCreatures.js @@ -9,11 +9,8 @@ export function loadCreature(creatureId, subscription) { if (loadedCreatures.has(creatureId)) { creature.subs.add(subscription); } else { - console.time(`loading to memory ${creatureId}`); creature = new LoadedCreature(subscription, creatureId); loadedCreatures.set(creatureId, creature); - console.timeEnd(`loading to memory ${creatureId}`); - console.log('Creatures in memory: ', loadedCreatures.size); } subscription.onStop(() => { unloadCreature(creatureId, subscription); @@ -29,7 +26,6 @@ function unloadCreature(creatureId, subscription) { creature.stop(); loadedCreatures.delete(creatureId); } - console.log('Creatures in memory: ', loadedCreatures.size); } class LoadedCreature { From 1b3efae81a6d335bf4d34ef2ac992d103ddaeed2 Mon Sep 17 00:00:00 2001 From: Stefan Zermatten Date: Wed, 11 May 2022 15:42:29 +0200 Subject: [PATCH 015/142] Replaced manual recompute calls with dirty flag settings --- .../creatureProperties/CreatureProperties.js | 7 ++++ .../methods/adjustQuantity.js | 10 ++---- .../methods/damageProperty.js | 4 +-- .../creatureProperties/methods/dealDamage.js | 2 -- .../methods/duplicateProperty.js | 7 ++-- .../creatureProperties/methods/equipItem.js | 5 +-- .../creatureProperties/methods/flipToggle.js | 5 +-- .../methods/insertProperty.js | 4 +-- .../methods/insertPropertyFromLibraryNode.js | 7 ++-- .../methods/pullFromProperty.js | 8 ++--- .../methods/pushToProperty.js | 7 ++-- .../methods/restoreProperty.js | 12 ++++--- .../methods/selectAmmoItem.js | 8 +---- .../methods/softRemoveProperty.js | 4 --- .../methods/updateCreatureProperty.js | 9 ++--- .../api/creature/creatures/Creatures.js | 5 +++ .../creatures/methods/restCreature.js | 19 ++++++---- .../api/creature/experience/Experiences.js | 35 ++++++++++--------- app/imports/api/engine/actions/doAction.js | 8 ++--- app/imports/api/engine/actions/doCastSpell.js | 13 ++++--- app/imports/api/engine/actions/doCheck.js | 4 --- .../computation/buildCreatureComputation.js | 18 ++++++++-- .../writeAlteredProperties.js | 1 + .../writeComputation/writeScope.js | 1 - app/imports/api/engine/computeCreature.js | 1 + app/imports/api/engine/loadCreatures.js | 25 ++++++++----- app/imports/api/parenting/organizeMethods.js | 22 +++++++----- app/imports/api/parenting/softRemove.js | 15 +++++--- 28 files changed, 138 insertions(+), 128 deletions(-) diff --git a/app/imports/api/creature/creatureProperties/CreatureProperties.js b/app/imports/api/creature/creatureProperties/CreatureProperties.js index 33183ddc..e4cc728e 100644 --- a/app/imports/api/creature/creatureProperties/CreatureProperties.js +++ b/app/imports/api/creature/creatureProperties/CreatureProperties.js @@ -82,6 +82,13 @@ const DenormalisedOnlyCreaturePropertySchema = new SimpleSchema({ index: 1, removeBeforeCompute: true, }, + // When this is true on any property, the creature needs to be recomputed + dirty: { + type: Boolean, + // Default to true because new properties cause a recomputation + defaultValue: true, + optional: true, + }, }); CreaturePropertySchema.extend(DenormalisedOnlyCreaturePropertySchema); diff --git a/app/imports/api/creature/creatureProperties/methods/adjustQuantity.js b/app/imports/api/creature/creatureProperties/methods/adjustQuantity.js index b9958fcb..06ca6459 100644 --- a/app/imports/api/creature/creatureProperties/methods/adjustQuantity.js +++ b/app/imports/api/creature/creatureProperties/methods/adjustQuantity.js @@ -4,7 +4,6 @@ import SimpleSchema from 'simpl-schema'; import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js'; import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor.js'; import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js'; -import computeCreature from '/imports/api/engine/computeCreature.js'; const adjustQuantity = new ValidatedMethod({ name: 'creatureProperties.adjustQuantity', @@ -29,10 +28,6 @@ const adjustQuantity = new ValidatedMethod({ // Do work adjustQuantityWork({property, operation, value}); - - // Changing quantity does not change dependencies, but recomputing the - // inventory changes many deps at once, so recompute fully - computeCreature(rootCreature._id); }, }); @@ -47,7 +42,7 @@ export function adjustQuantityWork({property, operation, value}){ } if (operation === 'set'){ CreatureProperties.update(property._id, { - $set: {quantity: value} + $set: {quantity: value, dirty: true} }, { selector: property }); @@ -57,7 +52,8 @@ export function adjustQuantityWork({property, operation, value}){ let currentQuantity = property.quantity; if (currentQuantity + value < 0) value = -currentQuantity; CreatureProperties.update(property._id, { - $inc: {quantity: value} + $inc: { quantity: value }, + $set: { dirty: true } }, { selector: property }); diff --git a/app/imports/api/creature/creatureProperties/methods/damageProperty.js b/app/imports/api/creature/creatureProperties/methods/damageProperty.js index 51c37749..315b6f2c 100644 --- a/app/imports/api/creature/creatureProperties/methods/damageProperty.js +++ b/app/imports/api/creature/creatureProperties/methods/damageProperty.js @@ -4,7 +4,6 @@ import SimpleSchema from 'simpl-schema'; import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js'; import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor.js'; import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js'; -import computeCreature from '/imports/api/engine/computeCreature.js'; const damageProperty = new ValidatedMethod({ name: 'creatureProperties.damage', @@ -38,7 +37,6 @@ const damageProperty = new ValidatedMethod({ ); } let result = damagePropertyWork({ property, operation, value }); - computeCreature(rootCreature._id); return result; }, }); @@ -69,7 +67,7 @@ export function damagePropertyWork({property, operation, value}){ // Write the results CreatureProperties.update(property._id, { - $set: {damage, value: newValue} + $set: {damage, value: newValue, dirty: true} }, { selector: property }); diff --git a/app/imports/api/creature/creatureProperties/methods/dealDamage.js b/app/imports/api/creature/creatureProperties/methods/dealDamage.js index b93aa495..ddb3dcc1 100644 --- a/app/imports/api/creature/creatureProperties/methods/dealDamage.js +++ b/app/imports/api/creature/creatureProperties/methods/dealDamage.js @@ -5,7 +5,6 @@ import CreatureProperties from '/imports/api/creature/creatureProperties/Creatur import Creatures from '/imports/api/creature/creatures/Creatures.js'; import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js'; import { damagePropertyWork } from '/imports/api/creature/creatureProperties/methods/damageProperty.js'; -import computeCreature from '/imports/api/engine/computeCreature.js'; const dealDamage = new ValidatedMethod({ name: 'creatureProperties.dealDamage', @@ -33,7 +32,6 @@ const dealDamage = new ValidatedMethod({ assertEditPermission(creature, this.userId); const totalDamage = dealDamageWork({creature, damageType, amount}) - computeCreature(creatureId); return totalDamage; }, }); diff --git a/app/imports/api/creature/creatureProperties/methods/duplicateProperty.js b/app/imports/api/creature/creatureProperties/methods/duplicateProperty.js index 03c505c2..3e640714 100644 --- a/app/imports/api/creature/creatureProperties/methods/duplicateProperty.js +++ b/app/imports/api/creature/creatureProperties/methods/duplicateProperty.js @@ -9,7 +9,6 @@ import { renewDocIds } from '/imports/api/parenting/parenting.js'; import { reorderDocs } from '/imports/api/parenting/order.js'; -import computeCreature from '/imports/api/engine/computeCreature.js'; var snackbar; if (Meteor.isClient){ snackbar = require( @@ -77,6 +76,9 @@ const duplicateProperty = new ValidatedMethod({ // Order the root node property.order += 0.5; + + // Mark the sheet as needing recompute + property.dirty = true; // Insert the properties CreatureProperties.batchInsert([property, ...nodes]); @@ -87,9 +89,6 @@ const duplicateProperty = new ValidatedMethod({ ancestorId: property.ancestors[0].id, }); - // Inserting a creature property invalidates dependencies: full recompute - computeCreature(creature._id); - return propertyId; }, }); diff --git a/app/imports/api/creature/creatureProperties/methods/equipItem.js b/app/imports/api/creature/creatureProperties/methods/equipItem.js index 24cb51fa..a8bbb911 100644 --- a/app/imports/api/creature/creatureProperties/methods/equipItem.js +++ b/app/imports/api/creature/creatureProperties/methods/equipItem.js @@ -4,7 +4,6 @@ import { RateLimiterMixin } from 'ddp-rate-limiter-mixin'; import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js'; import { organizeDoc } from '/imports/api/parenting/organizeMethods.js'; import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor.js'; -import computeCreature from '/imports/api/engine/computeCreature.js'; import BUILT_IN_TAGS from '/imports/constants/BUILT_IN_TAGS.js'; import getParentRefByTag from '/imports/api/creature/creatureProperties/methods/getParentRefByTag.js'; @@ -29,7 +28,7 @@ const equipItem = new ValidatedMethod({ let creature = getRootCreatureAncestor(item); assertEditPermission(creature, this.userId); CreatureProperties.update(_id, { - $set: {equipped}, + $set: { equipped, dirty: true }, }, { selector: {type: 'item'}, }); @@ -46,8 +45,6 @@ const equipItem = new ValidatedMethod({ order: Number.MAX_SAFE_INTEGER, skipRecompute: true, }); - - computeCreature(creature._id); }, }); diff --git a/app/imports/api/creature/creatureProperties/methods/flipToggle.js b/app/imports/api/creature/creatureProperties/methods/flipToggle.js index e39d0c75..df563cdc 100644 --- a/app/imports/api/creature/creatureProperties/methods/flipToggle.js +++ b/app/imports/api/creature/creatureProperties/methods/flipToggle.js @@ -3,7 +3,6 @@ import { RateLimiterMixin } from 'ddp-rate-limiter-mixin'; import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js'; import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js'; import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor.js'; -import computeCreature from '/imports/api/engine/computeCreature.js'; const flipToggle = new ValidatedMethod({ name: 'creatureProperties.flipToggle', @@ -36,12 +35,10 @@ const flipToggle = new ValidatedMethod({ CreatureProperties.update(_id, {$set: { enabled: !currentValue, disabled: currentValue, + dirty: true, }}, { selector: {type: 'toggle'}, }); - - // Updating a toggle is likely to change the whole tree, do a full recompute - computeCreature(rootCreature._id); }, }); diff --git a/app/imports/api/creature/creatureProperties/methods/insertProperty.js b/app/imports/api/creature/creatureProperties/methods/insertProperty.js index 4535e1fe..5d3567b0 100644 --- a/app/imports/api/creature/creatureProperties/methods/insertProperty.js +++ b/app/imports/api/creature/creatureProperties/methods/insertProperty.js @@ -5,7 +5,6 @@ import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/ge import SimpleSchema from 'simpl-schema'; import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js'; import { reorderDocs } from '/imports/api/parenting/order.js'; -import computeCreature from '/imports/api/engine/computeCreature.js'; import { getAncestry } from '/imports/api/parenting/parenting.js'; import getParentRefByTag from '/imports/api/creature/creatureProperties/methods/getParentRefByTag.js'; import { RefSchema } from '/imports/api/parenting/ChildSchema.js'; @@ -132,14 +131,13 @@ const insertPropertyAsChildOfTag = new ValidatedMethod({ export function insertPropertyWork({property, creature}){ delete property._id; + property.dirty = true; let _id = CreatureProperties.insert(property); // Tree structure changed by insert, reorder the tree reorderDocs({ collection: CreatureProperties, ancestorId: creature._id, }); - // Inserting a creature property invalidates dependencies: full recompute - computeCreature(creature._id); return _id; } diff --git a/app/imports/api/creature/creatureProperties/methods/insertPropertyFromLibraryNode.js b/app/imports/api/creature/creatureProperties/methods/insertPropertyFromLibraryNode.js index e73cbe33..1360ffc3 100644 --- a/app/imports/api/creature/creatureProperties/methods/insertPropertyFromLibraryNode.js +++ b/app/imports/api/creature/creatureProperties/methods/insertPropertyFromLibraryNode.js @@ -5,7 +5,6 @@ import CreatureProperties from '/imports/api/creature/creatureProperties/Creatur import LibraryNodes from '/imports/api/library/LibraryNodes.js'; import { RefSchema } from '/imports/api/parenting/ChildSchema.js'; import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor.js'; -import computeCreature from '/imports/api/engine/computeCreature.js'; import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js'; import { setLineageOfDocs, @@ -71,9 +70,6 @@ const insertPropertyFromLibraryNode = new ValidatedMethod({ collection: CreatureProperties, ancestorId: rootCreature._id, }); - - // Inserting a creature property invalidates dependencies: full recompute - computeCreature(rootCreature._id); // Return the docId of the last property, the inserted root property return rootId; }, @@ -135,6 +131,9 @@ function insertPropertyFromNode(nodeId, ancestors, order){ node.order = order; } + // Mark root as dirty + node.dirty = true; + // Insert the creature properties CreatureProperties.batchInsert(nodes); return node; diff --git a/app/imports/api/creature/creatureProperties/methods/pullFromProperty.js b/app/imports/api/creature/creatureProperties/methods/pullFromProperty.js index fa2acf78..3d8f99ea 100644 --- a/app/imports/api/creature/creatureProperties/methods/pullFromProperty.js +++ b/app/imports/api/creature/creatureProperties/methods/pullFromProperty.js @@ -3,7 +3,6 @@ import { RateLimiterMixin } from 'ddp-rate-limiter-mixin'; import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js'; import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js'; import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor.js'; -import computeCreature from '/imports/api/engine/computeCreature.js'; const pullFromProperty = new ValidatedMethod({ name: 'creatureProperties.pull', @@ -21,15 +20,12 @@ const pullFromProperty = new ValidatedMethod({ // Do work CreatureProperties.update(_id, { - $pull: {[path.join('.')]: {_id: itemId}}, + $pull: { [path.join('.')]: { _id: itemId } }, + $set: { dirty: true } }, { selector: {type: property.type}, getAutoValues: false, }); - - // TODO figure out if this method can change deps or not - computeCreature(rootCreature._id); - // recomputePropertyDependencies(property); } }); diff --git a/app/imports/api/creature/creatureProperties/methods/pushToProperty.js b/app/imports/api/creature/creatureProperties/methods/pushToProperty.js index e730065c..0ec4719c 100644 --- a/app/imports/api/creature/creatureProperties/methods/pushToProperty.js +++ b/app/imports/api/creature/creatureProperties/methods/pushToProperty.js @@ -3,7 +3,6 @@ import { RateLimiterMixin } from 'ddp-rate-limiter-mixin'; import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js'; import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js'; import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor.js'; -import computeCreature from '/imports/api/engine/computeCreature.js'; import { get } from 'lodash'; const pushToProperty = new ValidatedMethod({ @@ -39,13 +38,11 @@ const pushToProperty = new ValidatedMethod({ // Do work CreatureProperties.update(_id, { - $push: {[joinedPath]: value}, + $push: { [joinedPath]: value }, + $set: { dirty: true }, }, { selector: {type: property.type}, }); - - // TODO figure out if this method can change deps or not - computeCreature(rootCreature._id); } }); diff --git a/app/imports/api/creature/creatureProperties/methods/restoreProperty.js b/app/imports/api/creature/creatureProperties/methods/restoreProperty.js index b612e6c7..02ed7637 100644 --- a/app/imports/api/creature/creatureProperties/methods/restoreProperty.js +++ b/app/imports/api/creature/creatureProperties/methods/restoreProperty.js @@ -5,7 +5,6 @@ import CreatureProperties from '/imports/api/creature/creatureProperties/Creatur import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js'; import { restore } from '/imports/api/parenting/softRemove.js'; import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor.js'; -import computeCreature from '/imports/api/engine/computeCreature.js'; const restoreProperty = new ValidatedMethod({ name: 'creatureProperties.restore', @@ -24,10 +23,13 @@ const restoreProperty = new ValidatedMethod({ assertEditPermission(rootCreature, this.userId); // Do work - restore({_id, collection: CreatureProperties}); - - // Changes dependency tree by restoring children - computeCreature(rootCreature._id); + restore({ + _id, + collection: CreatureProperties, + extraUpdates: { + $set: { dirty: true } + }, + }); } }); diff --git a/app/imports/api/creature/creatureProperties/methods/selectAmmoItem.js b/app/imports/api/creature/creatureProperties/methods/selectAmmoItem.js index c1e4baa3..4f460287 100644 --- a/app/imports/api/creature/creatureProperties/methods/selectAmmoItem.js +++ b/app/imports/api/creature/creatureProperties/methods/selectAmmoItem.js @@ -4,7 +4,6 @@ import SimpleSchema from 'simpl-schema'; import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js'; import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor.js'; import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js'; -import computeCreature from '/imports/api/engine/computeCreature.js'; const selectAmmoItem = new ValidatedMethod({ name: 'creatureProperties.selectAmmoItem', @@ -37,15 +36,10 @@ const selectAmmoItem = new ValidatedMethod({ } let path = `resources.itemsConsumed.${itemConsumedIndex}.itemId`; CreatureProperties.update(actionId, { - $set: {[path]: itemId} + $set: { [path]: itemId, dirty: true } }, { selector: action, }); - - // Changing the linked item does change the dependency tree - // TODO: We can predict exactly which deps will be affected instead of - // recomputing the entire creature - computeCreature(rootCreature._id); }, }); diff --git a/app/imports/api/creature/creatureProperties/methods/softRemoveProperty.js b/app/imports/api/creature/creatureProperties/methods/softRemoveProperty.js index 86df9247..96110e8e 100644 --- a/app/imports/api/creature/creatureProperties/methods/softRemoveProperty.js +++ b/app/imports/api/creature/creatureProperties/methods/softRemoveProperty.js @@ -5,7 +5,6 @@ import CreatureProperties from '/imports/api/creature/creatureProperties/Creatur import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js'; import { softRemove } from '/imports/api/parenting/softRemove.js'; import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor.js'; -import computeCreature from '/imports/api/engine/computeCreature.js'; const softRemoveProperty = new ValidatedMethod({ name: 'creatureProperties.softRemove', @@ -25,9 +24,6 @@ const softRemoveProperty = new ValidatedMethod({ // Do work softRemove({_id, collection: CreatureProperties}); - - // Changes dependency tree by removing children - computeCreature(rootCreature._id); } }); diff --git a/app/imports/api/creature/creatureProperties/methods/updateCreatureProperty.js b/app/imports/api/creature/creatureProperties/methods/updateCreatureProperty.js index d53ddc6e..d6bf733c 100644 --- a/app/imports/api/creature/creatureProperties/methods/updateCreatureProperty.js +++ b/app/imports/api/creature/creatureProperties/methods/updateCreatureProperty.js @@ -3,7 +3,6 @@ import { RateLimiterMixin } from 'ddp-rate-limiter-mixin'; import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js'; import { assertEditPermission } from '/imports/api/sharing/sharingPermissions.js'; import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/getRootCreatureAncestor.js'; -import computeCreature from '/imports/api/engine/computeCreature.js'; const updateCreatureProperty = new ValidatedMethod({ name: 'creatureProperties.update', @@ -37,17 +36,13 @@ const updateCreatureProperty = new ValidatedMethod({ let modifier; // unset empty values if (value === null || value === undefined){ - modifier = {$unset: {[pathString]: 1}}; + modifier = { $unset: {[pathString]: 1}, $set: { dirty: true } }; } else { - modifier = {$set: {[pathString]: value}}; + modifier = { $set: {[pathString]: value, dirty: true } }; } CreatureProperties.update(_id, modifier, { selector: {type: property.type}, }); - - // Updating a property is likely to change dependencies, do a full recompute - // denormalised stats might change, so fetch the creature again - computeCreature(rootCreature._id); }, }); diff --git a/app/imports/api/creature/creatures/Creatures.js b/app/imports/api/creature/creatures/Creatures.js index 1c118fee..ef05ff40 100644 --- a/app/imports/api/creature/creatures/Creatures.js +++ b/app/imports/api/creature/creatures/Creatures.js @@ -100,6 +100,11 @@ let CreatureSchema = new SimpleSchema({ type: SimpleSchema.Integer, defaultValue: 0, }, + // Does the character need a recompute? + dirty: { + type: Boolean, + optional: true, + }, // Version of computation engine that was last used to compute this creature computeVersion: { type: String, diff --git a/app/imports/api/creature/creatures/methods/restCreature.js b/app/imports/api/creature/creatures/methods/restCreature.js index 07a607ee..c75e3cd8 100644 --- a/app/imports/api/creature/creatures/methods/restCreature.js +++ b/app/imports/api/creature/creatures/methods/restCreature.js @@ -4,10 +4,9 @@ import { RateLimiterMixin } from 'ddp-rate-limiter-mixin'; import Creatures from '/imports/api/creature/creatures/Creatures.js'; import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js'; import { assertEditPermission } from '/imports/api/creature/creatures/creaturePermissions.js'; -import computeCreature from '/imports/api/engine/computeCreature.js'; const restCreature = new ValidatedMethod({ - name: 'creature.methods.longRest', + name: 'creature.methods.rest', validate: new SimpleSchema({ creatureId: { type: String, @@ -51,7 +50,10 @@ const restCreature = new ValidatedMethod({ // update all attribute's damage filter.type = 'attribute'; CreatureProperties.update(filter, { - $set: {damage: 0} + $set: { + damage: 0, + dirty: true, + } }, { selector: {type: 'attribute'}, multi: true, @@ -63,7 +65,10 @@ const restCreature = new ValidatedMethod({ 'spell' ]}; CreatureProperties.update(filter, { - $set: {usesUsed: 0} + $set: { + usesUsed: 0, + dirty: true, + } }, { selector: {type: 'action'}, multi: true, @@ -103,13 +108,15 @@ const restCreature = new ValidatedMethod({ recoverableHd -= amountToRecover; resultingDamage = hd.damage - amountToRecover; CreatureProperties.update(hd._id, { - $set: {damage: resultingDamage} + $set: { + damage: resultingDamage, + dirty: true, + } }, { selector: {type: 'attribute'}, }); }); } - computeCreature(creatureId); }, }); diff --git a/app/imports/api/creature/experience/Experiences.js b/app/imports/api/creature/experience/Experiences.js index 7c0bb002..60501d02 100644 --- a/app/imports/api/creature/experience/Experiences.js +++ b/app/imports/api/creature/experience/Experiences.js @@ -3,7 +3,6 @@ import { ValidatedMethod } from 'meteor/mdg:validated-method'; import { RateLimiterMixin } from 'ddp-rate-limiter-mixin'; import { assertEditPermission } from '/imports/api/creature/creatures/creaturePermissions.js'; import Creatures from '/imports/api/creature/creatures/Creatures.js'; -import computeCreature from '/imports/api/engine/computeCreature.js'; import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS.js'; let Experiences = new Mongo.Collection('experiences'); @@ -50,18 +49,19 @@ Experiences.attachSchema(ExperienceSchema); const insertExperienceForCreature = function({experience, creatureId, userId}){ assertEditPermission(creatureId, userId); if (experience.xp){ - Creatures.update(creatureId, {$inc: { - 'denormalizedStats.xp': experience.xp - }}); + Creatures.update(creatureId, { + $inc: { 'denormalizedStats.xp': experience.xp }, + $set: { dirty: true }, + }); } if (experience.levels) { - Creatures.update(creatureId, {$inc: { - 'denormalizedStats.milestoneLevels': experience.levels - }}); + Creatures.update(creatureId, { + $inc: { 'denormalizedStats.milestoneLevels': experience.levels }, + $set: { dirty: true }, + }); } experience.creatureId = creatureId; let id = Experiences.insert(experience); - computeCreature(creatureId); return id; }; @@ -124,18 +124,19 @@ const removeExperience = new ValidatedMethod({ let creatureId = experience.creatureId assertEditPermission(creatureId, userId); if (experience.xp){ - Creatures.update(creatureId, {$inc: { - 'denormalizedStats.xp': -experience.xp - }}); + Creatures.update(creatureId, { + $inc: { 'denormalizedStats.xp': -experience.xp }, + $set: { dirty: true }, + }); } if (experience.levels) { - Creatures.update(creatureId, {$inc: { - 'denormalizedStats.milestoneLevels': -experience.levels - }}); + Creatures.update(creatureId, { + $inc: { 'denormalizedStats.milestoneLevels': -experience.levels }, + $set: { dirty: true }, + }); } experience.creatureId = creatureId; let numRemoved = Experiences.remove(experienceId); - computeCreature(creatureId); return numRemoved; }, }); @@ -173,9 +174,9 @@ const recomputeExperiences = new ValidatedMethod({ }); Creatures.update(creatureId, {$set: { 'denormalizedStats.xp': xp, - 'denormalizedStats.milestoneLevels': milestoneLevels + 'denormalizedStats.milestoneLevels': milestoneLevels, + dirty: true, }}); - computeCreature(creatureId); }, }); diff --git a/app/imports/api/engine/actions/doAction.js b/app/imports/api/engine/actions/doAction.js index e72e43bc..a53f628d 100644 --- a/app/imports/api/engine/actions/doAction.js +++ b/app/imports/api/engine/actions/doAction.js @@ -8,7 +8,6 @@ import { CreatureLogSchema, insertCreatureLogWork } from '/imports/api/creature/ import { assertEditPermission } from '/imports/api/creature/creatures/creaturePermissions.js'; import { nodeArrayToTree } from '/imports/api/parenting/nodesToTree.js'; import applyProperty from './applyProperty.js'; -import computeCreature from '/imports/api/engine/computeCreature.js'; const doAction = new ValidatedMethod({ name: 'creatureProperties.doAction', @@ -77,9 +76,10 @@ const doAction = new ValidatedMethod({ doActionWork({creature, targets, properties, ancestors, method: this, methodScope: scope}); // Recompute all involved creatures - computeCreature(creature._id); - targets.forEach(target => { - computeCreature(target._id); + Creatures.update({ + _id: { $in: [creature._id, ...targetIds] } + }, { + dirty: true }); }, }); diff --git a/app/imports/api/engine/actions/doCastSpell.js b/app/imports/api/engine/actions/doCastSpell.js index 890c4879..8e0da001 100644 --- a/app/imports/api/engine/actions/doCastSpell.js +++ b/app/imports/api/engine/actions/doCastSpell.js @@ -7,7 +7,6 @@ import CreatureProperties from '/imports/api/creature/creatureProperties/Creatur import { assertEditPermission } from '/imports/api/creature/creatures/creaturePermissions.js'; import { damagePropertyWork } from '/imports/api/creature/creatureProperties/methods/damageProperty.js'; import { doActionWork } from '/imports/api/engine/actions/doAction.js'; -import computeCreature from '/imports/api/engine/computeCreature.js'; import { CreatureLogSchema } from '/imports/api/creature/log/CreatureLogs.js'; const doAction = new ValidatedMethod({ @@ -129,12 +128,12 @@ const doAction = new ValidatedMethod({ } // Do the action - doActionWork({creature, targets, properties, ancestors, method: this, methodScope: scope, log}); - - // Recompute all involved creatures - computeCreature(creature._id); - targets.forEach(target => { - computeCreature(target._id); + doActionWork({ creature, targets, properties, ancestors, method: this, methodScope: scope, log }); + + Creatures.update({ + _id: { $in: [creature._id, ...targetIds] } + }, { + dirty: true }); }, }); diff --git a/app/imports/api/engine/actions/doCheck.js b/app/imports/api/engine/actions/doCheck.js index ce66db02..9f139f2c 100644 --- a/app/imports/api/engine/actions/doCheck.js +++ b/app/imports/api/engine/actions/doCheck.js @@ -5,7 +5,6 @@ import getRootCreatureAncestor from '/imports/api/creature/creatureProperties/ge import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js'; import { CreatureLogSchema, insertCreatureLogWork } from '/imports/api/creature/log/CreatureLogs.js'; import { assertEditPermission } from '/imports/api/creature/creatures/creaturePermissions.js'; -import computeCreature from '/imports/api/engine/computeCreature.js'; import rollDice from '/imports/parser/rollDice.js'; import numberToSignedString from '/imports/ui/utility/numberToSignedString.js'; @@ -32,9 +31,6 @@ const doCheck = new ValidatedMethod({ // Do the check doCheckWork({creature, prop, method: this, methodScope: scope}); - - // Recompute all involved creatures - computeCreature(creature._id); }, }); diff --git a/app/imports/api/engine/computation/buildCreatureComputation.js b/app/imports/api/engine/computation/buildCreatureComputation.js index 47612830..0da96e21 100644 --- a/app/imports/api/engine/computation/buildCreatureComputation.js +++ b/app/imports/api/engine/computation/buildCreatureComputation.js @@ -42,7 +42,8 @@ function getProperties(creatureId) { if (loadedCreatures.has(creatureId)) { const creature = loadedCreatures.get(creatureId); const props = Array.from(creature.properties.values()); - return props; + const cloneProps = EJSON.clone(props); + return cloneProps } console.time(`Cache miss fetching from db: ${creatureId}`) const props = CreatureProperties.find({ @@ -56,10 +57,19 @@ function getProperties(creatureId) { return props; } -function getCreature(creatureId){ - return Creatures.findOne(creatureId, { +function getCreature(creatureId) { + if (loadedCreatures.has(creatureId)) { + const loadedCreature = loadedCreatures.get(creatureId); + const creature = loadedCreature.creatures.get(creatureId); + if (creature) return creature; + } + console.time(`Cache miss on Creature: ${creatureId}`); + const creature = Creatures.findOne(creatureId, { denormalizedStats: 1, + variables: 1, }); + console.timeEnd(`Cache miss on Creature: ${creatureId}`); + return creature; } export function buildComputationFromProps(properties, creature){ @@ -91,6 +101,8 @@ export function buildComputationFromProps(properties, creature){ // Process the properties one by one properties.forEach(prop => { + // The prop has been processed, it's no longer dirty + delete prop.dirty; const computedSchema = computedOnlySchemas[prop.type]; removeSchemaFields([computedSchema, denormSchema], prop); diff --git a/app/imports/api/engine/computation/writeComputation/writeAlteredProperties.js b/app/imports/api/engine/computation/writeComputation/writeAlteredProperties.js index ccf8246e..fcad024c 100644 --- a/app/imports/api/engine/computation/writeComputation/writeAlteredProperties.js +++ b/app/imports/api/engine/computation/writeComputation/writeAlteredProperties.js @@ -21,6 +21,7 @@ export default function writeAlteredProperties(computation){ 'deactivatedByAncestor', 'deactivatedByToggle', 'damage', + 'dirty', ...schema.objectKeys(), ]; op = addChangedKeysToOp(op, keys, original, changed); diff --git a/app/imports/api/engine/computation/writeComputation/writeScope.js b/app/imports/api/engine/computation/writeComputation/writeScope.js index c318fb9e..abee4290 100644 --- a/app/imports/api/engine/computation/writeComputation/writeScope.js +++ b/app/imports/api/engine/computation/writeComputation/writeScope.js @@ -20,7 +20,6 @@ export default function writeScope(creatureId, computation) { // Only update changed fields if (!EJSON.equals(variables[key], scope[key])) { if (!$set) $set = {}; - // Set the changed key in the creature variables $set[`variables.${key}`] = scope[key]; } diff --git a/app/imports/api/engine/computeCreature.js b/app/imports/api/engine/computeCreature.js index 75064776..7bc1a953 100644 --- a/app/imports/api/engine/computeCreature.js +++ b/app/imports/api/engine/computeCreature.js @@ -6,6 +6,7 @@ import writeErrors from './computation/writeComputation/writeErrors.js'; export default function computeCreature(creatureId){ if (Meteor.isClient) return; + console.log('compute') const computation = buildCreatureComputation(creatureId); computeComputation(computation, creatureId); } diff --git a/app/imports/api/engine/loadCreatures.js b/app/imports/api/engine/loadCreatures.js index f7870440..0a60250b 100644 --- a/app/imports/api/engine/loadCreatures.js +++ b/app/imports/api/engine/loadCreatures.js @@ -1,6 +1,7 @@ +import { debounce } from 'lodash'; import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js'; import Creatures from '/imports/api/creature/creatures/Creatures.js'; - +import computeCreature from './computeCreature'; export const loadedCreatures = new Map(); // creatureId => {creature, properties, etc.} export function loadCreature(creatureId, subscription) { @@ -35,27 +36,33 @@ class LoadedCreature { // the required documents const self = this; Tracker.nonreactive(() => { - self.subs = new Set([sub]); + const compute = debounce(Meteor.bindEnvironment(() => { + computeCreature(creatureId); + }), 100); + self.properties = new Map(); // Observe all creature properties which are needed for computation self.propertyObserver = CreatureProperties.find({ 'ancestors.id': creatureId, removed: { $ne: true }, }, { - // sort: { order: 1 }, + sort: { order: 1 }, fields: { icon: 0 }, }).observeChanges({ added(id, fields) { fields._id = id; - return self.addProperty(fields) + self.addProperty(fields); + if (fields.dirty) compute(); }, changed(id, fields) { - return self.changeProperty(id, fields); + self.changeProperty(id, fields); + if (fields.dirty) compute(); }, removed(id) { - return self.removeProperty(id); + self.removeProperty(id); + compute(); }, }); @@ -67,12 +74,14 @@ class LoadedCreature { added(id, fields) { fields._id = id; self.addCreature(fields) + if (fields.dirty) compute(); }, changed(id, fields) { - return self.changeCreature(id, fields); + self.changeCreature(id, fields); + if (fields.dirty) compute(); }, removed(id) { - return self.removeCreature(id); + self.removeCreature(id); }, }); diff --git a/app/imports/api/parenting/organizeMethods.js b/app/imports/api/parenting/organizeMethods.js index f104154c..c4b822b2 100644 --- a/app/imports/api/parenting/organizeMethods.js +++ b/app/imports/api/parenting/organizeMethods.js @@ -8,7 +8,7 @@ import { RefSchema } from '/imports/api/parenting/ChildSchema.js'; import { assertDocEditPermission } from '/imports/api/sharing/sharingPermissions.js'; import fetchDocByRef from '/imports/api/parenting/fetchDocByRef.js'; import getCollectionByName from '/imports/api/parenting/getCollectionByName.js'; -import computeCreature from '/imports/api/engine/computeCreature.js'; +import Creatures from '/imports/api/creature/creatures/Creatures.js'; const organizeDoc = new ValidatedMethod({ name: 'organize.organizeDoc', @@ -57,10 +57,11 @@ const organizeDoc = new ValidatedMethod({ let parentCreatures = getCreatureAncestors(parent); if (!skipRecompute){ let creaturesToRecompute = union(docCreatures, parentCreatures); - // Recompute the creatures - creaturesToRecompute.forEach(id => { - // Some Dependencies depend on ancestry, so a full recompute is needed - computeCreature(id); + // Mark the creatures for recompute + Creatures.update({ + _id: { $in: creaturesToRecompute } + }, { + dirty: true }); } }, @@ -85,9 +86,14 @@ const reorderDoc = new ValidatedMethod({ assertDocEditPermission(doc, this.userId); safeUpdateDocOrder({docRef, order}); // Recompute the affected creatures - getCreatureAncestors(doc).forEach(id => { - computeCreature(id); - }); + const ancestors = getCreatureAncestors(doc); + if (ancestors.length) { + Creatures.update({ + _id: { $in: ancestors } + }, { + dirty: true + }); + } }, }); diff --git a/app/imports/api/parenting/softRemove.js b/app/imports/api/parenting/softRemove.js index 9ef16f8c..099abfc2 100644 --- a/app/imports/api/parenting/softRemove.js +++ b/app/imports/api/parenting/softRemove.js @@ -40,17 +40,22 @@ const restoreError = function(){ ); }; -export function restore({_id, collection}){ +export function restore({ _id, collection, extraUpdates}){ if (typeof collection === 'string') { collection = getCollectionByName(collection); } + const update = { + $unset: { + removed: 1, + removedAt: 1, + }, + ...extraUpdates + } + let numUpdated = collection.update({ _id, removedWith: {$exists: false} - }, { $unset: { - removed: 1, - removedAt: 1, - }}, { + }, update , { selector: {type: 'any'}, },); if (numUpdated === 0) restoreError(); From 6702f431d0590091d4fb3b4ca9d69559b5ba977a Mon Sep 17 00:00:00 2001 From: Stefan Zermatten Date: Wed, 11 May 2022 15:52:02 +0200 Subject: [PATCH 016/142] Fixed bug where removed library nodes kept showing --- app/imports/server/publications/library.js | 1 + 1 file changed, 1 insertion(+) diff --git a/app/imports/server/publications/library.js b/app/imports/server/publications/library.js index 10553aab..19211a55 100644 --- a/app/imports/server/publications/library.js +++ b/app/imports/server/publications/library.js @@ -73,6 +73,7 @@ Meteor.publish('libraryNodes', function(libraryId){ order: 1, parent: 1, ancestors: 1, + removed: 1, // Effect operation: 1, targetTags: 1, From ccc861b6fa51a44b295c348a1318a37c1e3806a7 Mon Sep 17 00:00:00 2001 From: Stefan Zermatten Date: Wed, 11 May 2022 15:52:44 +0200 Subject: [PATCH 017/142] Improved character subscription performance By limiting fields that can trigger an autorun --- .../server/publications/singleCharacter.js | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/app/imports/server/publications/singleCharacter.js b/app/imports/server/publications/singleCharacter.js index 611f1d23..d27be56f 100644 --- a/app/imports/server/publications/singleCharacter.js +++ b/app/imports/server/publications/singleCharacter.js @@ -20,12 +20,18 @@ Meteor.publish('singleCharacter', function(creatureId){ this.error(e); } this.autorun(function (computation){ - let userId = this.userId; - let creatureCursor - creatureCursor = Creatures.find({ + const userId = this.userId; + const creature = Creatures.findOne({ _id: creatureId, + }, { + fields: { + owner: 1, + readers: 1, + writers: 1, + public: 1, + computeVersion: 1, + } }); - let creature = creatureCursor.fetch()[0]; try { assertViewPermission(creature, userId) } catch(e){ return [] } if (creature.computeVersion !== VERSION && computation.firstRun){ @@ -35,7 +41,9 @@ Meteor.publish('singleCharacter', function(creatureId){ catch(e){ console.error(e) } } return [ - creatureCursor, + Creatures.find({ + _id: creatureId, + }), CreatureProperties.find({ 'ancestors.id': creatureId, }), From a04935c5b4be683f6402f563da245d0697f3135e Mon Sep 17 00:00:00 2001 From: Stefan Zermatten Date: Tue, 7 Jun 2022 18:51:07 +0200 Subject: [PATCH 018/142] Updated packages --- app/.meteor/packages | 10 +-- app/.meteor/release | 2 +- app/.meteor/versions | 48 ++++++------- app/package-lock.json | 160 ++++++++++++++++++++++-------------------- app/package.json | 24 +++---- 5 files changed, 127 insertions(+), 117 deletions(-) diff --git a/app/.meteor/packages b/app/.meteor/packages index 17aedfd9..6bfb61c4 100644 --- a/app/.meteor/packages +++ b/app/.meteor/packages @@ -3,24 +3,24 @@ # 'meteor add' and 'meteor remove' will edit this file for you, # but you can also edit it by hand. -accounts-password@2.2.0 +accounts-password@2.3.1 random@1.2.0 underscore@1.0.10 dburles:mongo-collection-instances accounts-google@1.4.0 -email@2.2.0 +email@2.2.1 meteor-base@1.5.1 mobile-experience@1.1.0 -mongo@1.14.6 +mongo@1.15.0 session@1.2.0 tracker@1.2.0 logging@1.3.1 reload@1.3.1 -ejson@1.1.1 +ejson@1.1.2 check@1.3.1 standard-minifier-js@2.8.0 shell-server@0.5.0 -ecmascript@0.16.1 +ecmascript@0.16.2 es5-shim@4.8.0 service-configuration@1.3.0 dynamic-import@0.7.2 diff --git a/app/.meteor/release b/app/.meteor/release index b1b0cceb..66dd7b66 100644 --- a/app/.meteor/release +++ b/app/.meteor/release @@ -1 +1 @@ -METEOR@2.6.1 +METEOR@2.7.3 diff --git a/app/.meteor/versions b/app/.meteor/versions index 65dfa36d..dbabbb8b 100644 --- a/app/.meteor/versions +++ b/app/.meteor/versions @@ -1,7 +1,7 @@ -accounts-base@2.2.1 +accounts-base@2.2.3 accounts-google@1.4.0 -accounts-oauth@1.4.0 -accounts-password@2.2.0 +accounts-oauth@1.4.1 +accounts-password@2.3.1 accounts-patreon@0.1.0 akryum:npm-check@0.1.2 akryum:vue-component@0.15.2 @@ -12,13 +12,13 @@ aldeed:collection2@3.5.0 aldeed:schema-index@3.0.0 allow-deny@1.1.1 autoupdate@1.8.0 -babel-compiler@7.8.1 -babel-runtime@1.5.0 +babel-compiler@7.9.0 +babel-runtime@1.5.1 base64@1.0.12 binary-heap@1.0.11 -blaze-tools@1.1.2 +blaze-tools@1.1.3 boilerplate-generator@1.7.1 -bozhao:link-accounts@2.4.0 +bozhao:link-accounts@2.6.1 caching-compiler@1.2.2 caching-html-compiler@1.2.1 callback-hook@1.4.0 @@ -33,18 +33,18 @@ ddp-rate-limiter@1.1.0 ddp-server@2.5.0 diff-sequence@1.1.1 dynamic-import@0.7.2 -ecmascript@0.16.1 +ecmascript@0.16.2 ecmascript-runtime@0.8.0 ecmascript-runtime-client@0.12.1 ecmascript-runtime-server@0.11.0 -ejson@1.1.1 -email@2.2.0 +ejson@1.1.2 +email@2.2.1 es5-shim@4.8.0 fetch@0.1.1 geojson-utils@1.0.10 -google-oauth@1.4.1 +google-oauth@1.4.2 hot-code-push@1.0.4 -html-tools@1.1.2 +html-tools@1.1.3 htmljs@1.1.1 http@2.0.0 id-map@1.1.1 @@ -64,23 +64,23 @@ meteortesting:mocha@2.0.3 meteortesting:mocha-core@8.1.2 mikowals:batch-insert@1.3.0 minifier-css@1.6.0 -minifier-js@2.7.3 +minifier-js@2.7.4 minimongo@1.8.0 mobile-experience@1.1.0 mobile-status-bar@1.1.0 -modern-browsers@0.1.7 +modern-browsers@0.1.8 modules@0.18.0 -modules-runtime@0.12.0 -mongo@1.14.6 -mongo-decimal@0.1.2 +modules-runtime@0.13.0 +mongo@1.15.0 +mongo-decimal@0.1.3 mongo-dev-server@1.1.0 mongo-id@1.0.8 mongo-livedata@1.0.12 npm-mongo@4.3.1 -oauth@2.1.1 +oauth@2.1.2 oauth2@1.3.1 ordered-dict@1.1.0 -ostrio:cookies@2.7.0 +ostrio:cookies@2.7.2 ostrio:files@2.0.1 patreon-oauth@0.1.0 peerlibrary:assert@0.3.0 @@ -98,7 +98,7 @@ promise@0.12.0 raix:eventemitter@1.0.0 random@1.2.0 rate-limit@1.0.9 -react-fast-refresh@0.2.2 +react-fast-refresh@0.2.3 reactive-dict@1.3.0 reactive-var@1.0.11 reload@1.3.1 @@ -114,14 +114,14 @@ simple:rest@1.2.1 simple:rest-bearer-token-parser@1.1.1 simple:rest-json-error-handler@1.1.1 simple:rest-method-mixin@1.1.0 -socket-stream-client@0.4.0 -spacebars-compiler@1.3.0 +socket-stream-client@0.5.0 +spacebars-compiler@1.3.1 standard-minifier-js@2.8.0 static-html@1.3.2 -templating-tools@1.2.1 +templating-tools@1.2.2 tmeasday:check-npm-versions@1.0.2 tracker@1.2.0 -typescript@4.4.1 +typescript@4.5.4 underscore@1.0.10 url@1.3.2 webapp@1.13.1 diff --git a/app/package-lock.json b/app/package-lock.json index a5119f93..3ce518a8 100644 --- a/app/package-lock.json +++ b/app/package-lock.json @@ -50,9 +50,9 @@ } }, "@babel/runtime": { - "version": "7.16.7", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.16.7.tgz", - "integrity": "sha512-9E9FJowqAsytyOY6LG+1KuueckRL+aQW+mKvXRXnuFGyRAyepJPmEo9vgMfXUA6O9u3IeEdv9MAkppFcaQwogQ==", + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.18.3.tgz", + "integrity": "sha512-38Y8f7YUhce/K7RMwTp7m0uCumpv9hZkitCbBClqQIow1qSbCvGkcegKOXpEWCQLfWmevgRiWokZ1GkpfhbZug==", "requires": { "regenerator-runtime": "^0.13.4" } @@ -279,25 +279,25 @@ "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" }, "aws-sdk": { - "version": "2.1059.0", - "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1059.0.tgz", - "integrity": "sha512-Q+6T9kpO6aobUNboTOk9MVAmWbs/KK0pxgCNFK0M8YO+7EWUFkNOLHM9tdYOP5vsJK5pLz6D2t2w3lHQjKzGlg==", + "version": "2.1148.0", + "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1148.0.tgz", + "integrity": "sha512-FUYAyveKmS5eqIiGQgrGVsLZwwtI+K6S6Gz8oJf56pgypZCo9dV+cXO4aaS+vN0+LSmGh6dSKc6G8h8FYASIJg==", "requires": { "buffer": "4.9.2", "events": "1.1.1", "ieee754": "1.1.13", - "jmespath": "0.15.0", + "jmespath": "0.16.0", "querystring": "0.2.0", "sax": "1.2.1", "url": "0.10.3", - "uuid": "3.3.2", + "uuid": "8.0.0", "xml2js": "0.4.19" }, "dependencies": { "uuid": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", - "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==" + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.0.0.tgz", + "integrity": "sha512-jOXGuXZAWdsTH7eZLtyXMqUb9EcWMGZNbL9YcGBJl4MH4nrxHmZJhEHvyLFrkxo+28uLb/NYRcStH48fnD0Vzw==" } } }, @@ -448,15 +448,16 @@ "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" }, "chai": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.4.tgz", - "integrity": "sha512-yS5H68VYOCtN1cjfwumDSuzn/9c+yza4f3reKXlE5rUg7SFcCEy90gJvydNgOYtblyf4Zi6jIWRnXOgErta0KA==", + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.6.tgz", + "integrity": "sha512-bbcp3YfHCUzMOvKqsztczerVgBKSsEijCySNlHHbX3VG1nskvqjz5Rfso1gGwD6w6oOV3eI60pKuMOV5MV7p3Q==", "dev": true, "requires": { "assertion-error": "^1.1.0", "check-error": "^1.0.2", "deep-eql": "^3.0.1", "get-func-name": "^2.0.0", + "loupe": "^2.3.1", "pathval": "^1.1.1", "type-detect": "^4.0.5" } @@ -515,13 +516,13 @@ "check-error": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", - "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=", + "integrity": "sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA==", "dev": true }, "chokidar": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz", - "integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==", + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", "dev": true, "requires": { "anymatch": "~3.1.2", @@ -587,7 +588,7 @@ "clone": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", - "integrity": "sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=" + "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==" }, "code-point-at": { "version": "1.1.0", @@ -802,9 +803,9 @@ } }, "dompurify": { - "version": "2.3.4", - "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.3.4.tgz", - "integrity": "sha512-6BVcgOAVFXjI0JTjEvZy901Rghm+7fDQOrNIcxB4+gdhj6Kwp6T9VBhBY/AbagKHJocRkDYGd6wvI+p4/10xtQ==" + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.3.8.tgz", + "integrity": "sha512-eVhaWoVibIzqdGYjwsBWodIQIaXFSB+cKDf4cfxLMsK0xiud6SE+/WCVx/Xw/UwQsa4cS3T2eITcdtmTg2UKcw==" }, "ecc-jsbn": { "version": "0.1.2", @@ -1067,7 +1068,7 @@ "events": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", - "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=" + "integrity": "sha512-kEcvvCBByWXGnZy6JUlgAp2gBIUjfCAV6P6TgT1/aaQKcmuAEC4OZTV1I4EWQLz2gxZw76atuVyvHhTxvi0Flw==" }, "expand-template": { "version": "2.0.3", @@ -1212,7 +1213,7 @@ "get-func-name": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", - "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=", + "integrity": "sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig==", "dev": true }, "getpass": { @@ -1226,7 +1227,7 @@ "github-from-package": { "version": "0.0.0", "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", - "integrity": "sha1-l/tdlr/eiXMxPyDoKI75oWf6ZM4=" + "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==" }, "glob": { "version": "7.1.6", @@ -1333,9 +1334,9 @@ "integrity": "sha1-tJ7yJ0va/NikiAqWa/440aC/RnE=" }, "immutable": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.0.0.tgz", - "integrity": "sha512-zIE9hX70qew5qTUjSS7wi1iwj/l7+m54KWU247nhM3v806UdGj1yDndXj+IOYxxtW9zyLI+xqFNZjTuDaLUqFw==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.1.0.tgz", + "integrity": "sha512-oNkuqVTA8jqG1Q6c+UglTOD1xhC1BtjKI7XkCXRkZHrN5m18/XsnUp8Q89GkQO/z+0WjonSvl0FLhDYftp46nQ==", "dev": true }, "import-fresh": { @@ -1438,9 +1439,9 @@ "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" }, "jmespath": { - "version": "0.15.0", - "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.15.0.tgz", - "integrity": "sha1-o/Iiqarp+Wb10nx5ZRDigJF2Qhc=" + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.16.0.tgz", + "integrity": "sha512-9FzQjJ7MATs1tSpnco1K6ayiYE3figslrXA72G2HQ/n76RzvYlofyi5QM+iX4YRs/pu3yzxlVQSST23+dMDknw==" }, "js-tokens": { "version": "4.0.0", @@ -1540,7 +1541,7 @@ "lodash._reinterpolate": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz", - "integrity": "sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0=" + "integrity": "sha512-xYHt68QRoYGjeeM/XOE1uJtvXQAgvszfBhjV4yvsQH0u2i9I6cI6c6/eG4Hh3UAOVn0y/xAXwmTzEay49Q//HA==" }, "lodash.merge": { "version": "4.6.2", @@ -1576,6 +1577,15 @@ "integrity": "sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM=", "dev": true }, + "loupe": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.4.tgz", + "integrity": "sha512-OvKfgCC2Ndby6aSTREl5aCCPTNIzlDfQZvZxNUrBrihDhL3xcrYegTblhmEiCrg2kKQz4XsFIaemE5BF4ybSaQ==", + "dev": true, + "requires": { + "get-func-name": "^2.0.0" + } + }, "lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -1609,9 +1619,9 @@ } }, "marked": { - "version": "4.0.10", - "resolved": "https://registry.npmjs.org/marked/-/marked-4.0.10.tgz", - "integrity": "sha512-+QvuFj0nGgO970fySghXGmuw+Fd0gD2x3+MqCWLIPf5oxdv1Ka6b2q+z9RP01P/IaKPMEramy+7cNy/Lw8c3hw==" + "version": "4.0.16", + "resolved": "https://registry.npmjs.org/marked/-/marked-4.0.16.tgz", + "integrity": "sha512-wahonIQ5Jnyatt2fn8KqF/nIqZM8mh3oRu2+l5EANGMhu6RFjiSG52QNE2eWzFMI94HqYSgN184NurgNG6CztA==" }, "mem": { "version": "6.1.1", @@ -1632,13 +1642,13 @@ } }, "meteor-node-stubs": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/meteor-node-stubs/-/meteor-node-stubs-1.1.0.tgz", - "integrity": "sha512-YvMQb4zcfWA82wFdRVTyxq28GO+Us7GSdtP+bTtC/mV35yipKnWo4W4665O57AmLVFnz4zR+WIZW11b4sfCtJw==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/meteor-node-stubs/-/meteor-node-stubs-1.2.3.tgz", + "integrity": "sha512-2kyYFh45428+q8EjydBhyHqPO30CG09yQ6xRNHMJSiFLqHaVoRJE1tWr7QrBKstjy8HkNH4UuKSp5S11HeZv/w==", "requires": { "assert": "^2.0.0", "browserify-zlib": "^0.2.0", - "buffer": "^6.0.3", + "buffer": "^5.7.1", "console-browserify": "^1.2.0", "constants-browserify": "^1.0.0", "crypto-browserify": "^3.12.0", @@ -1649,7 +1659,7 @@ "os-browserify": "^0.3.0", "path-browserify": "^1.0.0", "process": "^0.11.10", - "punycode": "^2.1.1", + "punycode": "^1.4.1", "querystring-es3": "^0.2.1", "readable-stream": "^3.6.0", "stream-browserify": "^3.0.0", @@ -1766,11 +1776,11 @@ } }, "buffer": { - "version": "6.0.3", + "version": "5.7.1", "bundled": true, "requires": { "base64-js": "^1.3.1", - "ieee754": "^1.2.1" + "ieee754": "^1.1.13" } }, "buffer-xor": { @@ -2219,7 +2229,7 @@ } }, "punycode": { - "version": "2.1.1", + "version": "1.4.1", "bundled": true }, "querystring": { @@ -2539,9 +2549,9 @@ "integrity": "sha512-yJZay4tP0wcjqkkf8zlMQ/T+JOgU+EWfdE4w4TG8OS94B12J/+Z44UOYxVJErE8E6/wFunX1hMZEB1/GHsBYHg==" }, "node-abi": { - "version": "3.15.0", - "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.15.0.tgz", - "integrity": "sha512-Ic6z/j6I9RLm4ov7npo1I48UQr2BEyFCqh6p7S1dhEx9jPO0GPGq/e2Rb7x7DroQrmiVMz/Bw1vJm9sPAl2nxA==", + "version": "3.22.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.22.0.tgz", + "integrity": "sha512-u4uAs/4Zzmp/jjsD9cyFYDXeISfUWaAVWshPmDZOFOv4Xl4SbzTXm53I04C2uRueYJ+0t5PEtLH/owbn2Npf/w==", "requires": { "semver": "^7.3.5" } @@ -2787,7 +2797,7 @@ "querystring": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", - "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=" + "integrity": "sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g==" }, "railroad-diagrams": { "version": "1.0.0", @@ -2934,9 +2944,9 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, "sass": { - "version": "1.48.0", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.48.0.tgz", - "integrity": "sha512-hQi5g4DcfjcipotoHZ80l7GNJHGqQS5LwMBjVYB/TaT0vcSSpbgM8Ad7cgfsB2M0MinbkEQQPO9+sjjSiwxqmw==", + "version": "1.52.2", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.52.2.tgz", + "integrity": "sha512-mfHB2VSeFS7sZlPv9YohB9GB7yWIgQNTGniQwfQ04EoQN0wsQEv7SwpCwy/x48Af+Z3vDeFXz+iuXM3HK/phZQ==", "dev": true, "requires": { "chokidar": ">=3.0.0 <4.0.0", @@ -2968,14 +2978,14 @@ "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=" }, "sharp": { - "version": "0.30.4", - "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.30.4.tgz", - "integrity": "sha512-3Onig53Y6lji4NIZo69s14mERXXY/GV++6CzOYx/Rd8bnTwbhFbL09WZd7Ag/CCnA0WxFID8tkY0QReyfL6v0Q==", + "version": "0.30.6", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.30.6.tgz", + "integrity": "sha512-lSdVxFxcndzcXggDrak6ozdGJgmIgES9YVZWtAFrwi+a/H5vModaf51TghBtMPw+71sLxUsTy2j+aB7qLIODQg==", "requires": { "color": "^4.2.3", "detect-libc": "^2.0.1", - "node-addon-api": "^4.3.0", - "prebuild-install": "^7.0.1", + "node-addon-api": "^5.0.0", + "prebuild-install": "^7.1.0", "semver": "^7.3.7", "simple-get": "^4.0.1", "tar-fs": "^2.1.1", @@ -2988,9 +2998,9 @@ "integrity": "sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w==" }, "node-addon-api": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-4.3.0.tgz", - "integrity": "sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ==" + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.0.0.tgz", + "integrity": "sha512-CvkDw2OEnme7ybCykJpVcKH+uAOLV2qLqiyla128dN9TkEWfrYmxG6C2boDe5KcNQqZF3orkqzGgOMvZ/JNekA==" }, "semver": { "version": "7.3.7", @@ -3019,13 +3029,13 @@ }, "signal-exit": { "version": "3.0.2", - "resolved": false, + "resolved": "", "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" }, "simpl-schema": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/simpl-schema/-/simpl-schema-1.12.0.tgz", - "integrity": "sha512-lzXC3L8jJbPhNXGR3cjlyIauqqrC5WUJS4O34Ym/wLIvb8K3ZieK+1OfTzs4mBpDc3Y8u53gQFAr1X37DmTcEg==", + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/simpl-schema/-/simpl-schema-1.12.2.tgz", + "integrity": "sha512-FaisAjfJEt7Ie7K39wNqb/0F7FQ1q7yXmZcNa5JEBiPA9hIt4MpWMouL9mLqNB89alGpZAEiU7U9BelDxRqCVg==", "requires": { "clone": "^2.1.2", "message-box": "^0.2.7", @@ -3109,9 +3119,9 @@ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" }, "source-map-js": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.1.tgz", - "integrity": "sha512-4+TN2b3tqOCd/kaGRJ/sTYA0tR0mdXx26ipdolxcwtJVqEnqNYvlCAt1q3ypy4QMlYus+Zh34RNtYLoq2oQ4IA==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", + "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", "dev": true }, "source-map-support": { @@ -3390,9 +3400,9 @@ "dev": true }, "underscore": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.2.tgz", - "integrity": "sha512-ekY1NhRzq0B08g4bGuX4wd2jZx5GnKz6mKSqFL4nqBlfyMGiG10gDFhDTMEfYmDL6Jy0FUIZp7wiRB+0BP7J2g==" + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.4.tgz", + "integrity": "sha512-BQFnUDuAQ4Yf/cYY5LNrK9NCJFKriaRbD9uR1fTeXnBeoa97W0i41qkZfGO9pSo8I5KzjAcSY2XYtdf0oKd7KQ==" }, "uri-js": { "version": "4.2.2", @@ -3414,7 +3424,7 @@ "punycode": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", - "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=" + "integrity": "sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw==" } } }, @@ -3503,9 +3513,9 @@ "integrity": "sha512-hx2JtRPRvne9NY4s1r7ASsCaO8CIby30qwC1kGQRxsrWApO3he+rziGOzTDSfvmr852zWMb11n6qAwHCz6C/vw==" }, "vue-router": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-3.5.3.tgz", - "integrity": "sha512-FUlILrW3DGitS2h+Xaw8aRNvGTwtuaxrRkNSHWTizOfLUie7wuYwezeZ50iflRn8YPV5kxmU2LQuu3nM/b3Zsg==" + "version": "3.5.4", + "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-3.5.4.tgz", + "integrity": "sha512-x+/DLAJZv2mcQ7glH2oV9ze8uPwcI+H+GgTgTmb5I55bCgY3+vXWIsqbYUzbBSZnwFHEJku4eoaH/x98veyymQ==" }, "vuedraggable": { "version": "2.24.3", @@ -3516,9 +3526,9 @@ } }, "vuetify": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/vuetify/-/vuetify-2.6.2.tgz", - "integrity": "sha512-nx3uZkO8MZNMshUEh1xKaQ1hQYepNwWFn3FVxKt+XBVf7ZFscd0GS/a3KZo4B3baXQmziCQAZKNIQF5IWeaIUw==" + "version": "2.6.6", + "resolved": "https://registry.npmjs.org/vuetify/-/vuetify-2.6.6.tgz", + "integrity": "sha512-H4KtxDFmDN8QiTRiGfBySyjMhVaHAJTKB0llGGKZT5jKxtnx9gvEtMWXKtVuRP0NJJP0H6xBPJHNOH7nT18qiQ==" }, "vuetify-upload-button": { "version": "2.0.2", diff --git a/app/package.json b/app/package.json index 0467b8ef..bc90f96d 100644 --- a/app/package.json +++ b/app/package.json @@ -10,7 +10,7 @@ "author": "Stefan Zermatten", "scripts": { "run": "meteor", - "debug": "meteor debug", + "debug": "meteor --inspect", "test": "meteor test --driver-package meteortesting:mocha --port 3001" }, "engines": { @@ -18,11 +18,11 @@ "npm": "6.13.x" }, "dependencies": { - "@babel/runtime": "^7.16.7", + "@babel/runtime": "^7.18.3", "@chenfengyuan/vue-countdown": "^1.1.5", "@tozd/vue-observer-utils": "^0.5.0", "animejs": "^2.2.0", - "aws-sdk": "^2.1059.0", + "aws-sdk": "^2.1148.0", "bcrypt": "^5.0.0", "chroma-js": "^2.4.2", "core-js": "^2.6.11", @@ -30,12 +30,12 @@ "date-fns": "^1.30.1", "ddp-rate-limiter-mixin": "^1.1.10", "discord.js": "^12.5.3", - "dompurify": "^2.3.4", + "dompurify": "^2.3.8", "ignore": "^5.2.0", "ignore-styles": "^5.0.1", "lodash": "^4.17.20", - "marked": "^4.0.10", - "meteor-node-stubs": "^1.1.0", + "marked": "^4.0.16", + "meteor-node-stubs": "^1.2.3", "minify-css-string": "^1.0.0", "moo": "^0.5.1", "nearley": "^2.19.1", @@ -45,27 +45,27 @@ "qrcode": "^1.5.0", "request": "^2.88.2", "sharp": "^0.30.4", - "simpl-schema": "^1.12.0", + "simpl-schema": "^1.12.2", "source-map-support": "^0.5.21", "speakingurl": "^14.0.1", "styles": "^0.2.1", - "underscore": "^1.13.2", + "underscore": "^1.13.4", "vue": "2.6.10", "vue-meteor-tracker": "^2.0.0-beta.5", "vue-reactive-provide": "^0.3.0", - "vue-router": "^3.5.3", + "vue-router": "^3.5.4", "vuedraggable": "^2.23.2", - "vuetify": "^2.6.2", + "vuetify": "^2.6.6", "vuetify-upload-button": "^2.0.2", "vuex": "^3.1.3" }, "devDependencies": { - "chai": "^4.3.4", + "chai": "^4.3.6", "eslint": "^7.32.0", "eslint-plugin-vue": "^7.20.0", "eslint-plugin-vuetify": "^1.1.0", "mem": "^6.1.1", - "sass": "^1.48.0" + "sass": "^1.52.2" }, "eslintConfig": { "extends": [ From 385ac17812b88d9cb740f37b182c984eaa2c62d0 Mon Sep 17 00:00:00 2001 From: Stefan Zermatten Date: Tue, 7 Jun 2022 21:43:35 +0200 Subject: [PATCH 019/142] Added big slot cards to build tab, improved build tab --- .../creatures/methods/restCreature.js | 4 +- app/imports/api/engine/computeCreature.js | 2 +- app/imports/api/engine/loadCreatures.js | 4 +- app/imports/ui/components/CardHighlight.vue | 2 +- .../ui/creature/buildTree/BuildTreeNode.vue | 23 ++- .../ui/creature/buildTree/FillSlotButton.vue | 2 +- .../character/characterSheetTabs/BuildTab.vue | 65 ++++--- .../characterSheetTabs/JournalTab.vue | 2 - app/imports/ui/creature/slots/SlotCard.vue | 70 ++++++++ .../ui/creature/slots/SlotCardsToFill.vue | 104 ++++++++++++ .../ui/creature/slots/SlotDetailsDialog.vue | 36 ---- app/imports/ui/creature/slots/Slots.vue | 160 ------------------ .../ui/dialogStack/DialogComponentIndex.js | 2 - 13 files changed, 228 insertions(+), 248 deletions(-) create mode 100644 app/imports/ui/creature/slots/SlotCard.vue create mode 100644 app/imports/ui/creature/slots/SlotCardsToFill.vue delete mode 100644 app/imports/ui/creature/slots/SlotDetailsDialog.vue delete mode 100644 app/imports/ui/creature/slots/Slots.vue diff --git a/app/imports/api/creature/creatures/methods/restCreature.js b/app/imports/api/creature/creatures/methods/restCreature.js index c75e3cd8..0115db52 100644 --- a/app/imports/api/creature/creatures/methods/restCreature.js +++ b/app/imports/api/creature/creatures/methods/restCreature.js @@ -44,8 +44,8 @@ const restCreature = new ValidatedMethod({ let filter = { 'ancestors.id': creatureId, reset: resetFilter, - removed: {$ne: true}, - inactive: {$ne: true}, + removed: { $ne: true }, + inactive: { $ne: true }, }; // update all attribute's damage filter.type = 'attribute'; diff --git a/app/imports/api/engine/computeCreature.js b/app/imports/api/engine/computeCreature.js index 7bc1a953..c1de8c44 100644 --- a/app/imports/api/engine/computeCreature.js +++ b/app/imports/api/engine/computeCreature.js @@ -6,7 +6,7 @@ import writeErrors from './computation/writeComputation/writeErrors.js'; export default function computeCreature(creatureId){ if (Meteor.isClient) return; - console.log('compute') + // console.log('compute ' + creatureId); const computation = buildCreatureComputation(creatureId); computeComputation(computation, creatureId); } diff --git a/app/imports/api/engine/loadCreatures.js b/app/imports/api/engine/loadCreatures.js index 0a60250b..c6021b22 100644 --- a/app/imports/api/engine/loadCreatures.js +++ b/app/imports/api/engine/loadCreatures.js @@ -2,6 +2,8 @@ import { debounce } from 'lodash'; import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js'; import Creatures from '/imports/api/creature/creatures/Creatures.js'; import computeCreature from './computeCreature'; + +const COMPUTE_DEBOUNCE_TIME = 100; // ms export const loadedCreatures = new Map(); // creatureId => {creature, properties, etc.} export function loadCreature(creatureId, subscription) { @@ -40,7 +42,7 @@ class LoadedCreature { const compute = debounce(Meteor.bindEnvironment(() => { computeCreature(creatureId); - }), 100); + }), COMPUTE_DEBOUNCE_TIME); self.properties = new Map(); // Observe all creature properties which are needed for computation diff --git a/app/imports/ui/components/CardHighlight.vue b/app/imports/ui/components/CardHighlight.vue index 42a25fcc..714cdb28 100644 --- a/app/imports/ui/components/CardHighlight.vue +++ b/app/imports/ui/components/CardHighlight.vue @@ -18,7 +18,7 @@ props: { active: Boolean, dark: Boolean, - } + }, } diff --git a/app/imports/ui/creature/buildTree/BuildTreeNode.vue b/app/imports/ui/creature/buildTree/BuildTreeNode.vue index 42e66f4f..45d4d07d 100644 --- a/app/imports/ui/creature/buildTree/BuildTreeNode.vue +++ b/app/imports/ui/creature/buildTree/BuildTreeNode.vue @@ -4,7 +4,7 @@ :class="{ 'empty': !hasChildren, }" - :data-id="`tree-node-${node._id}`" + :data-id="`build-tree-node-${node._id}`" >
- - - mdi-plus - - Child Property - - + + + + diff --git a/app/imports/ui/properties/forms/AttributeForm.vue b/app/imports/ui/properties/forms/AttributeForm.vue index 5d23e2bf..d5473933 100644 --- a/app/imports/ui/properties/forms/AttributeForm.vue +++ b/app/imports/ui/properties/forms/AttributeForm.vue @@ -93,6 +93,10 @@ + + + + diff --git a/app/imports/ui/properties/forms/BranchForm.vue b/app/imports/ui/properties/forms/BranchForm.vue index 03e39587..1d6fe81c 100644 --- a/app/imports/ui/properties/forms/BranchForm.vue +++ b/app/imports/ui/properties/forms/BranchForm.vue @@ -38,6 +38,12 @@ :value="model.tags" @change="change('tags', ...arguments)" /> + + +
diff --git a/app/imports/ui/properties/forms/BuffForm.vue b/app/imports/ui/properties/forms/BuffForm.vue index f55f05b0..36630bfa 100644 --- a/app/imports/ui/properties/forms/BuffForm.vue +++ b/app/imports/ui/properties/forms/BuffForm.vue @@ -45,6 +45,12 @@ :value="model.tags" @change="change('tags', ...arguments)" /> + + +
diff --git a/app/imports/ui/properties/forms/ClassLevelForm.vue b/app/imports/ui/properties/forms/ClassLevelForm.vue index e34fe57f..7768cbcb 100644 --- a/app/imports/ui/properties/forms/ClassLevelForm.vue +++ b/app/imports/ui/properties/forms/ClassLevelForm.vue @@ -55,6 +55,12 @@ :error-messages="errors.tags" @change="change('tags', ...arguments)" /> + + + diff --git a/app/imports/ui/properties/forms/ConstantForm.vue b/app/imports/ui/properties/forms/ConstantForm.vue index cf160914..1db29569 100644 --- a/app/imports/ui/properties/forms/ConstantForm.vue +++ b/app/imports/ui/properties/forms/ConstantForm.vue @@ -24,6 +24,12 @@ @change="change('calculation', ...arguments)" /> + + + diff --git a/app/imports/ui/properties/forms/ContainerForm.vue b/app/imports/ui/properties/forms/ContainerForm.vue index 39b9defd..0efce16f 100644 --- a/app/imports/ui/properties/forms/ContainerForm.vue +++ b/app/imports/ui/properties/forms/ContainerForm.vue @@ -55,30 +55,35 @@ $emit('change', {path: ['description', ...path], value, ack})" /> - - -
-
- + + + + + + + +
+
+ +
-
- + +
diff --git a/app/imports/ui/properties/forms/DamageForm.vue b/app/imports/ui/properties/forms/DamageForm.vue index 7acf3e36..cdcd0b54 100644 --- a/app/imports/ui/properties/forms/DamageForm.vue +++ b/app/imports/ui/properties/forms/DamageForm.vue @@ -53,6 +53,12 @@ :error-messages="errors.tags" @change="change('tags', ...arguments)" /> + + + diff --git a/app/imports/ui/properties/forms/DamageMultiplierForm.vue b/app/imports/ui/properties/forms/DamageMultiplierForm.vue index 7f0b25bc..7aeb23ec 100644 --- a/app/imports/ui/properties/forms/DamageMultiplierForm.vue +++ b/app/imports/ui/properties/forms/DamageMultiplierForm.vue @@ -46,6 +46,9 @@ + + + diff --git a/app/imports/ui/properties/forms/EffectForm.vue b/app/imports/ui/properties/forms/EffectForm.vue index c4fcecdb..4e3b59e1 100644 --- a/app/imports/ui/properties/forms/EffectForm.vue +++ b/app/imports/ui/properties/forms/EffectForm.vue @@ -149,30 +149,38 @@ - - - + + + + + + + + + - - - + + diff --git a/app/imports/ui/properties/forms/FeatureForm.vue b/app/imports/ui/properties/forms/FeatureForm.vue index 77f3a77b..a423f35b 100644 --- a/app/imports/ui/properties/forms/FeatureForm.vue +++ b/app/imports/ui/properties/forms/FeatureForm.vue @@ -35,6 +35,12 @@ :error-messages="errors.tags" @change="change('tags', ...arguments)" /> + + + diff --git a/app/imports/ui/properties/forms/FolderForm.vue b/app/imports/ui/properties/forms/FolderForm.vue index 8f55fe54..c17e1de4 100644 --- a/app/imports/ui/properties/forms/FolderForm.vue +++ b/app/imports/ui/properties/forms/FolderForm.vue @@ -9,19 +9,27 @@ :error-messages="errors.name" @change="change('name', ...arguments)" /> - - - + + + + + + + + + diff --git a/app/imports/ui/properties/forms/ItemForm.vue b/app/imports/ui/properties/forms/ItemForm.vue index 08a9e586..e3525717 100644 --- a/app/imports/ui/properties/forms/ItemForm.vue +++ b/app/imports/ui/properties/forms/ItemForm.vue @@ -100,47 +100,53 @@ $emit('change', {path: ['description', ...path], value, ack})" /> - - - - - -
- -
-
-
+ + + + + + + + + + +
+ +
+
+
+
diff --git a/app/imports/ui/properties/forms/NoteForm.vue b/app/imports/ui/properties/forms/NoteForm.vue index a328de80..470c9280 100644 --- a/app/imports/ui/properties/forms/NoteForm.vue +++ b/app/imports/ui/properties/forms/NoteForm.vue @@ -35,6 +35,12 @@ :value="model.tags" @change="change('tags', ...arguments)" /> + + + diff --git a/app/imports/ui/properties/forms/ProficiencyForm.vue b/app/imports/ui/properties/forms/ProficiencyForm.vue index 41961c69..604553bc 100644 --- a/app/imports/ui/properties/forms/ProficiencyForm.vue +++ b/app/imports/ui/properties/forms/ProficiencyForm.vue @@ -37,6 +37,12 @@ :value="model.tags" @change="change('tags', ...arguments)" /> + + + diff --git a/app/imports/ui/properties/forms/ResourcesForm.vue b/app/imports/ui/properties/forms/ResourcesForm.vue index 886ba8c4..5c3e92d9 100644 --- a/app/imports/ui/properties/forms/ResourcesForm.vue +++ b/app/imports/ui/properties/forms/ResourcesForm.vue @@ -24,35 +24,33 @@ @push="({path, value, ack}) => $emit('push', {path: ['itemsConsumed', ...path], value, ack})" @pull="({path, ack}) => $emit('pull', {path: ['itemsConsumed', ...path], ack})" /> -
- - - - - Add Resource - - - Add Ammo - - - -
+ + + + + Add Resource + + + Add Ammo + + + diff --git a/app/imports/ui/properties/forms/RollForm.vue b/app/imports/ui/properties/forms/RollForm.vue index 0355018f..803217e3 100644 --- a/app/imports/ui/properties/forms/RollForm.vue +++ b/app/imports/ui/properties/forms/RollForm.vue @@ -35,6 +35,10 @@ $emit('change', {path: ['roll', ...path], value, ack})" /> + + + + + + + diff --git a/app/imports/ui/properties/forms/SlotFillerForm.vue b/app/imports/ui/properties/forms/SlotFillerForm.vue index d0f84567..5ea5ee44 100644 --- a/app/imports/ui/properties/forms/SlotFillerForm.vue +++ b/app/imports/ui/properties/forms/SlotFillerForm.vue @@ -1,20 +1,37 @@ diff --git a/app/imports/ui/properties/forms/SlotForm.vue b/app/imports/ui/properties/forms/SlotForm.vue index df857437..394808e7 100644 --- a/app/imports/ui/properties/forms/SlotForm.vue +++ b/app/imports/ui/properties/forms/SlotForm.vue @@ -157,38 +157,43 @@ $emit('change', {path: ['description', ...path], value, ack})" /> - -
- + + + + + +
+ + +
+ - -
- -
+
+
diff --git a/app/imports/ui/properties/forms/SpellForm.vue b/app/imports/ui/properties/forms/SpellForm.vue index 7fafb173..88f609d4 100644 --- a/app/imports/ui/properties/forms/SpellForm.vue +++ b/app/imports/ui/properties/forms/SpellForm.vue @@ -238,7 +238,7 @@ $emit('change', {path: ['description', ...path], value, ack})" /> - + + @@ -291,6 +292,11 @@ @change="change('reset', ...arguments)" /> + + + + + + + + + diff --git a/app/imports/ui/properties/forms/ToggleForm.vue b/app/imports/ui/properties/forms/ToggleForm.vue index a10de795..abbff445 100644 --- a/app/imports/ui/properties/forms/ToggleForm.vue +++ b/app/imports/ui/properties/forms/ToggleForm.vue @@ -84,6 +84,13 @@ :value="model.tags" @change="change('tags', ...arguments)" /> + + + + diff --git a/app/imports/ui/properties/forms/shared/propertyFormIndex.js b/app/imports/ui/properties/forms/shared/propertyFormIndex.js index 2e28b01c..09627057 100644 --- a/app/imports/ui/properties/forms/shared/propertyFormIndex.js +++ b/app/imports/ui/properties/forms/shared/propertyFormIndex.js @@ -1,6 +1,5 @@ const ActionForm = () => import('/imports/ui/properties/forms/ActionForm.vue'); const AdjustmentForm = () => import('/imports/ui/properties/forms/AdjustmentForm.vue'); -const AttackForm = () => import('/imports/ui/properties/forms/AttackForm.vue'); const AttributeForm = () => import('/imports/ui/properties/forms/AttributeForm.vue'); const BuffForm = () => import('/imports/ui/properties/forms/BuffForm.vue'); const BranchForm = () => import('/imports/ui/properties/forms/BranchForm.vue'); @@ -28,7 +27,6 @@ const ToggleForm = () => import('/imports/ui/properties/forms/ToggleForm.vue'); export default { action: ActionForm, adjustment: AdjustmentForm, - attack: AttackForm, attribute: AttributeForm, buff: BuffForm, branch: BranchForm, diff --git a/app/imports/ui/properties/forms/shared/propertyFormMixin.js b/app/imports/ui/properties/forms/shared/propertyFormMixin.js index e9eed2f4..172db58f 100644 --- a/app/imports/ui/properties/forms/shared/propertyFormMixin.js +++ b/app/imports/ui/properties/forms/shared/propertyFormMixin.js @@ -1,10 +1,13 @@ import ComputedField from '/imports/ui/properties/forms/shared/ComputedField.vue'; import InlineComputationField from '/imports/ui/properties/forms/shared/InlineComputationField.vue'; +import FormSection, { FormSections } from '/imports/ui/properties/forms/shared/FormSection.vue'; export default { components: { ComputedField, InlineComputationField, + FormSection, + FormSections, }, props: { model: { diff --git a/app/imports/ui/properties/viewers/SlotViewer.vue b/app/imports/ui/properties/viewers/SlotViewer.vue index bdd07cd5..2c5bb3f6 100644 --- a/app/imports/ui/properties/viewers/SlotViewer.vue +++ b/app/imports/ui/properties/viewers/SlotViewer.vue @@ -39,18 +39,28 @@ + + + + mdi-plus + Fill Slot + + - - From 9cc4186171b7e51681b398d06b5f40afde72f5b3 Mon Sep 17 00:00:00 2001 From: Stefan Zermatten Date: Tue, 21 Jun 2022 11:00:50 +0200 Subject: [PATCH 028/142] Reduced data load in slot fill dialog --- app/imports/server/publications/library.js | 64 ++++++++++--------- .../server/publications/slotFillers.js | 22 +++++-- .../ui/creature/slots/SlotFillDialog.vue | 26 ++++---- .../library/LibraryNodeExpansionContent.vue | 3 + 4 files changed, 65 insertions(+), 50 deletions(-) diff --git a/app/imports/server/publications/library.js b/app/imports/server/publications/library.js index 10553aab..778d08a8 100644 --- a/app/imports/server/publications/library.js +++ b/app/imports/server/publications/library.js @@ -3,6 +3,39 @@ import Libraries from '/imports/api/library/Libraries.js'; import LibraryNodes from '/imports/api/library/LibraryNodes.js'; import { assertViewPermission, assertDocViewPermission } from '/imports/api/sharing/sharingPermissions.js'; +const LIBRARY_NODE_TREE_FIELDS = { + _id: 1, + name: 1, + type: 1, + icon: 1, + color: 1, + order: 1, + parent: 1, + ancestors: 1, + // Effect + operation: 1, + targetTags: 1, + stats: 1, + // Item + quantity: 1, + plural: 1, + equipped: 1, + // Branch + branchType: 1, + // Damage: + damageType: 1, + stat: 1, + amount: 1, + // Class level + level: 1, + // Proficiency + value: 1, + // Reference + cache: 1, +} + +export { LIBRARY_NODE_TREE_FIELDS }; + Meteor.publish('libraries', function(){ this.autorun(function (){ let userId = this.userId; @@ -64,36 +97,7 @@ Meteor.publish('libraryNodes', function(libraryId){ 'ancestors.id': libraryId, }, { sort: { order: 1 }, - fields: { - _id: 1, - name: 1, - type: 1, - icon: 1, - color: 1, - order: 1, - parent: 1, - ancestors: 1, - // Effect - operation: 1, - targetTags: 1, - stats: 1, - // Item - quantity: 1, - plural: 1, - equipped: 1, - // Branch - branchType: 1, - // Damage: - damageType: 1, - stat: 1, - amount: 1, - // Class level - level: 1, - // Proficiency - value: 1, - // Reference - cache: 1, - } + fields: LIBRARY_NODE_TREE_FIELDS, }), ]; }); diff --git a/app/imports/server/publications/slotFillers.js b/app/imports/server/publications/slotFillers.js index c5b3397f..3dd6d141 100644 --- a/app/imports/server/publications/slotFillers.js +++ b/app/imports/server/publications/slotFillers.js @@ -3,6 +3,13 @@ import Libraries from '/imports/api/library/Libraries.js'; import LibraryNodes from '/imports/api/library/LibraryNodes.js'; import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js'; import getSlotFillFilter from '/imports/api/creature/creatureProperties/methods/getSlotFillFilter.js' +import { LIBRARY_NODE_TREE_FIELDS } from '/imports/server/publications/library.js'; + +const FIELDS = { + ...LIBRARY_NODE_TREE_FIELDS, + slotFillerCondition: 1, + tags: 1, +} Meteor.publish('slotFillers', function(slotId, searchTerm){ if (searchTerm) check(searchTerm, String); @@ -50,7 +57,8 @@ Meteor.publish('slotFillers', function(slotId, searchTerm){ options = { // relevant documents have a higher score. fields: { - _score: { $meta: 'textScore' } + _score: { $meta: 'textScore' }, + ...FIELDS, }, sort: { // `score` property specified in the projection fields above. @@ -61,10 +69,13 @@ Meteor.publish('slotFillers', function(slotId, searchTerm){ } } else { delete filter.$text - options = {sort: { - name: 1, - order: 1, - }}; + options = { + sort: { + name: 1, + order: 1, + }, + fields: FIELDS, + }; } options.limit = limit; @@ -72,7 +83,6 @@ Meteor.publish('slotFillers', function(slotId, searchTerm){ self.setData('countAll', LibraryNodes.find(filter).count()); }); self.autorun(function () { - Meteor._sleepForMs(1000); return [LibraryNodes.find(filter, options), libraries]; }); }); diff --git a/app/imports/ui/creature/slots/SlotFillDialog.vue b/app/imports/ui/creature/slots/SlotFillDialog.vue index 0b63870f..497b0401 100644 --- a/app/imports/ui/creature/slots/SlotFillDialog.vue +++ b/app/imports/ui/creature/slots/SlotFillDialog.vue @@ -27,20 +27,18 @@ />

{{ slotPropertyTypeName }} with tags: - - + +

Date: Tue, 21 Jun 2022 11:08:45 +0200 Subject: [PATCH 029/142] Iterated on class UI --- app/imports/api/properties/Classes.js | 93 ++++++-- app/imports/ui/properties/forms/ClassForm.vue | 222 ++++++++++++++++++ .../forms/shared/propertyFormIndex.js | 3 +- .../ui/properties/viewers/ClassViewer.vue | 91 +++++++ .../ui/properties/viewers/SlotViewer.vue | 15 +- .../viewers/shared/propertyViewerIndex.js | 3 +- 6 files changed, 405 insertions(+), 22 deletions(-) create mode 100644 app/imports/ui/properties/forms/ClassForm.vue create mode 100644 app/imports/ui/properties/viewers/ClassViewer.vue diff --git a/app/imports/api/properties/Classes.js b/app/imports/api/properties/Classes.js index ee631246..e6e6471f 100644 --- a/app/imports/api/properties/Classes.js +++ b/app/imports/api/properties/Classes.js @@ -1,34 +1,95 @@ import SimpleSchema from 'simpl-schema'; import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS.js'; import createPropertySchema from '/imports/api/properties/subSchemas/createPropertySchema.js'; -import { SlotSchema, ComputedOnlySlotSchema } from './Slots.js'; // Classes are like slots, except they only take class levels and enforce that // lower levels are taken before higher levels let ClassSchema = createPropertySchema({ + name: { + type: String, + optional: true, + max: STORAGE_LIMITS.name, + }, + description: { + type: 'inlineCalculationFieldToCompute', + optional: true, + }, // Only `classLevel`s with the same variable name can fill the class variableName: { type: String, optional: true, max: STORAGE_LIMITS.variableName, }, -}).extend(SlotSchema); + slotTags: { + type: Array, + defaultValue: [], + maxCount: STORAGE_LIMITS.tagCount, + }, + 'slotTags.$': { + 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, + }, + slotCondition: { + type: 'fieldToCompute', + optional: true, + }, +}); const ComputedOnlyClassSchema = createPropertySchema({ - level: { - type: SimpleSchema.Integer, - optional: true, - removeBeforeCompute: true, - }, - missingLevels: { - type: Array, - optional: true, - removeBeforeCompute: true, - }, - 'missingLevels.$': { - type: SimpleSchema.Integer, - }, - }).extend(ComputedOnlySlotSchema); + // Computed fields + description: { + type: 'computedOnlyInlineCalculationField', + optional: true, + }, + slotCondition: { + type: 'computedOnlyField', + optional: true, + }, + + // Denormalised fields + level: { + type: SimpleSchema.Integer, + optional: true, + removeBeforeCompute: true, + }, + missingLevels: { + type: Array, + optional: true, + removeBeforeCompute: true, + }, + 'missingLevels.$': { + type: SimpleSchema.Integer, + }, +}); const ComputedClassSchema = new SimpleSchema() .extend(ClassSchema) diff --git a/app/imports/ui/properties/forms/ClassForm.vue b/app/imports/ui/properties/forms/ClassForm.vue new file mode 100644 index 00000000..50003919 --- /dev/null +++ b/app/imports/ui/properties/forms/ClassForm.vue @@ -0,0 +1,222 @@ + + + diff --git a/app/imports/ui/properties/forms/shared/propertyFormIndex.js b/app/imports/ui/properties/forms/shared/propertyFormIndex.js index 09627057..1687a2cb 100644 --- a/app/imports/ui/properties/forms/shared/propertyFormIndex.js +++ b/app/imports/ui/properties/forms/shared/propertyFormIndex.js @@ -3,6 +3,7 @@ const AdjustmentForm = () => import('/imports/ui/properties/forms/AdjustmentForm const AttributeForm = () => import('/imports/ui/properties/forms/AttributeForm.vue'); const BuffForm = () => import('/imports/ui/properties/forms/BuffForm.vue'); const BranchForm = () => import('/imports/ui/properties/forms/BranchForm.vue'); +const ClassForm = () => import('/imports/ui/properties/forms/ClassForm.vue'); const ClassLevelForm = () => import('/imports/ui/properties/forms/ClassLevelForm.vue'); const ConstantForm = () => import('/imports/ui/properties/forms/ConstantForm.vue'); const ContainerForm = () => import('/imports/ui/properties/forms/ContainerForm.vue'); @@ -32,8 +33,8 @@ export default { branch: BranchForm, constant: ConstantForm, container: ContainerForm, + class: ClassForm, classLevel: ClassLevelForm, - class: SlotForm, damage: DamageForm, damageMultiplier: DamageMultiplierForm, effect: EffectForm, diff --git a/app/imports/ui/properties/viewers/ClassViewer.vue b/app/imports/ui/properties/viewers/ClassViewer.vue new file mode 100644 index 00000000..26e4ffbd --- /dev/null +++ b/app/imports/ui/properties/viewers/ClassViewer.vue @@ -0,0 +1,91 @@ + + + + + diff --git a/app/imports/ui/properties/viewers/SlotViewer.vue b/app/imports/ui/properties/viewers/SlotViewer.vue index 2c5bb3f6..36e432f1 100644 --- a/app/imports/ui/properties/viewers/SlotViewer.vue +++ b/app/imports/ui/properties/viewers/SlotViewer.vue @@ -16,7 +16,7 @@ /> - mdi-plus + + mdi-plus + Fill Slot @@ -68,9 +70,14 @@ } export default { - mixins: [propertyViewerMixin], components: { FillSlotButton, + }, + mixins: [propertyViewerMixin], + inject: { + context: { + default: {}, + }, }, computed: { slotTypeName(){ diff --git a/app/imports/ui/properties/viewers/shared/propertyViewerIndex.js b/app/imports/ui/properties/viewers/shared/propertyViewerIndex.js index 8845fac6..1df889ee 100644 --- a/app/imports/ui/properties/viewers/shared/propertyViewerIndex.js +++ b/app/imports/ui/properties/viewers/shared/propertyViewerIndex.js @@ -4,6 +4,7 @@ const AttributeViewer = () => import ('/imports/ui/properties/viewers/AttributeV const BuffViewer = () => import ('/imports/ui/properties/viewers/BuffViewer.vue'); const BranchViewer = () => import ('/imports/ui/properties/viewers/BranchViewer.vue'); const ContainerViewer = () => import ('/imports/ui/properties/viewers/ContainerViewer.vue'); +const ClassViewer = () => import ('/imports/ui/properties/viewers/ClassViewer.vue'); const ClassLevelViewer = () => import ('/imports/ui/properties/viewers/ClassLevelViewer.vue'); const ConstantViewer = () => import ('/imports/ui/properties/viewers/ConstantViewer.vue'); const DamageViewer = () => import ('/imports/ui/properties/viewers/DamageViewer.vue'); @@ -31,7 +32,7 @@ export default { buff: BuffViewer, branch: BranchViewer, container: ContainerViewer, - class: SlotViewer, + class: ClassViewer, classLevel: ClassLevelViewer, constant: ConstantViewer, damage: DamageViewer, From b484a27637e004ca4bce5a387261a6cde2ccc5c9 Mon Sep 17 00:00:00 2001 From: Stefan Zermatten Date: Thu, 23 Jun 2022 08:39:15 +0200 Subject: [PATCH 030/142] iterated on class form to match new schema --- app/imports/ui/properties/forms/ClassForm.vue | 55 +++---------------- 1 file changed, 9 insertions(+), 46 deletions(-) diff --git a/app/imports/ui/properties/forms/ClassForm.vue b/app/imports/ui/properties/forms/ClassForm.vue index 50003919..b97e6b3b 100644 --- a/app/imports/ui/properties/forms/ClassForm.vue +++ b/app/imports/ui/properties/forms/ClassForm.vue @@ -22,7 +22,7 @@ @@ -43,7 +43,7 @@ - - - - - Test Slot - - - - + -
- - -
diff --git a/app/imports/ui/properties/treeNodeViews/treeNodeViewIndex.js b/app/imports/ui/properties/treeNodeViews/treeNodeViewIndex.js index 26d52ca0..1f9772e8 100644 --- a/app/imports/ui/properties/treeNodeViews/treeNodeViewIndex.js +++ b/app/imports/ui/properties/treeNodeViews/treeNodeViewIndex.js @@ -7,6 +7,7 @@ import EffectTreeNode from '/imports/ui/properties/treeNodeViews/EffectTreeNode. import ClassLevelTreeNode from '/imports/ui/properties/treeNodeViews/ClassLevelTreeNode.vue'; import ProficiencyTreeNode from '/imports/ui/properties/treeNodeViews/ProficiencyTreeNode.vue'; import ReferenceTreeNode from '/imports/ui/properties/treeNodeViews/ReferenceTreeNode.vue'; +import SavingThrowTreeNode from '/imports/ui/properties/treeNodeViews/SavingThrowTreeNode.vue'; export default { default: DefaultTreeNode, @@ -18,4 +19,5 @@ export default { item: ItemTreeNode, proficiency: ProficiencyTreeNode, reference: ReferenceTreeNode, + savingThrow: SavingThrowTreeNode, } From 292388dead31517932f39aa8ccf187d6aa9f448a Mon Sep 17 00:00:00 2001 From: Stefan Zermatten Date: Wed, 29 Jun 2022 15:35:12 +0200 Subject: [PATCH 035/142] Iterated on class UI --- app/imports/ui/properties/forms/ClassForm.vue | 136 +++++++++--------- .../ui/properties/viewers/ClassViewer.vue | 1 + 2 files changed, 69 insertions(+), 68 deletions(-) diff --git a/app/imports/ui/properties/forms/ClassForm.vue b/app/imports/ui/properties/forms/ClassForm.vue index b97e6b3b..d301b750 100644 --- a/app/imports/ui/properties/forms/ClassForm.vue +++ b/app/imports/ui/properties/forms/ClassForm.vue @@ -28,73 +28,6 @@ /> - - - - mdi-plus - - - - - -
- - - - mdi-delete - -
-
- - + + + + mdi-plus + + + + + +
+ + + + mdi-delete + +
+
+ +
diff --git a/app/imports/ui/properties/viewers/ClassViewer.vue b/app/imports/ui/properties/viewers/ClassViewer.vue index 26e4ffbd..44322a52 100644 --- a/app/imports/ui/properties/viewers/ClassViewer.vue +++ b/app/imports/ui/properties/viewers/ClassViewer.vue @@ -11,6 +11,7 @@ :value="model.slotCondition && (model.slotCondition.value || model.slotCondition.calculation)" /> From 1a71c2cfa7feed3a82d4ccbe192221d8e7b0c391 Mon Sep 17 00:00:00 2001 From: Stefan Zermatten Date: Mon, 4 Jul 2022 13:55:41 +0200 Subject: [PATCH 036/142] Began implementing class level up UI --- .../methods/getSlotFillFilter.js | 29 +- app/imports/server/publications/library.js | 1 + .../server/publications/slotFillers.js | 59 +++ .../ui/creature/slots/LevelUpDialog.vue | 406 ++++++++++++++++++ .../ui/dialogStack/DialogComponentIndex.js | 2 + .../ui/properties/viewers/ClassViewer.vue | 32 +- 6 files changed, 513 insertions(+), 16 deletions(-) create mode 100644 app/imports/ui/creature/slots/LevelUpDialog.vue diff --git a/app/imports/api/creature/creatureProperties/methods/getSlotFillFilter.js b/app/imports/api/creature/creatureProperties/methods/getSlotFillFilter.js index 1bba9421..177225b0 100644 --- a/app/imports/api/creature/creatureProperties/methods/getSlotFillFilter.js +++ b/app/imports/api/creature/creatureProperties/methods/getSlotFillFilter.js @@ -15,9 +15,30 @@ export default function getSlotFillFilter({slot, libraryIds}){ slotFillerType: slot.slotType, }] }); + } else if (slot.type === 'class') { + filter.$and.push({ + $or: [{ + type: 'classLevel', + },{ + type: 'slotFiller', + slotFillerType: 'classLevel', + }] + }); + filter.variableName = slot.variableName; + + // Only search for levels the class needs + const levels = []; + if (slot.missingLevels && slot.missingLevels.length) { + levels.push(...slot.missingLevels); + } else if (slot.level) { + levels.push(slot.level); + } + if (levels.length) { + filter.level = {$or: levels}; + } } let tagsOr = []; - let tagsNor = []; + let tagsNin = []; if (slot.slotTags && slot.slotTags.length){ tagsOr.push({tags: {$all: slot.slotTags}}); } @@ -27,15 +48,15 @@ export default function getSlotFillFilter({slot, libraryIds}){ if (extra.operation === 'OR'){ tagsOr.push({tags: {$all: extra.tags}}); } else if (extra.operation === 'NOT'){ - tagsNor.push({tags: {$all: extra.tags}}); + tagsNin.push(...extra.tags); } }); } if (tagsOr.length){ filter.$and.push({$or: tagsOr}); } - if (tagsNor.length){ - filter.$and.push({$nor: tagsNor}); + if (tagsNin.length){ + filter.$and.push({$nin: tagsNin}); } if (!filter.$and.length){ delete filter.$and; diff --git a/app/imports/server/publications/library.js b/app/imports/server/publications/library.js index f1d04175..00a45a5b 100644 --- a/app/imports/server/publications/library.js +++ b/app/imports/server/publications/library.js @@ -28,6 +28,7 @@ const LIBRARY_NODE_TREE_FIELDS = { amount: 1, // Class level level: 1, + variableName: 1, // Proficiency value: 1, // Reference diff --git a/app/imports/server/publications/slotFillers.js b/app/imports/server/publications/slotFillers.js index 3dd6d141..e97fa20f 100644 --- a/app/imports/server/publications/slotFillers.js +++ b/app/imports/server/publications/slotFillers.js @@ -88,3 +88,62 @@ Meteor.publish('slotFillers', function(slotId, searchTerm){ }); }); }); + +Meteor.publish('classFillers', function(classId){ + let self = this; + if (!classId) return []; + + this.autorun(function (){ + let userId = this.userId; + if (!userId) { + return []; + } + // Get the class + let classProp = CreatureProperties.findOne(classId); + if (!classProp){ + return []; + } + + // Get all the ids of libraries the user can access + const user = Meteor.users.findOne(userId, { + fields: {subscribedLibraries: 1} + }); + const subs = user && user.subscribedLibraries || []; + let libraries = Libraries.find({ + $or: [ + {owner: userId}, + {writers: userId}, + {readers: userId}, + {_id: {$in: subs}}, + ] + }, { + fields: {_id: 1, name: 1}, + }); + let libraryIds = libraries.map(lib => lib._id); + + // Build a filter for nodes in those libraries that match the slot + let filter = getSlotFillFilter({slot: classProp, libraryIds}); + + this.autorun(function(){ + // Get the limit of the documents the user can fetch + var limit = self.data('limit') || 50; + check(limit, Number); + + let options = { + sort: { + name: 1, + order: 1, + }, + fields: FIELDS, + limit, + }; + + self.autorun(function () { + self.setData('countAll', LibraryNodes.find(filter).count()); + }); + self.autorun(function () { + return [LibraryNodes.find(filter, options), libraries]; + }); + }); + }); +}); diff --git a/app/imports/ui/creature/slots/LevelUpDialog.vue b/app/imports/ui/creature/slots/LevelUpDialog.vue new file mode 100644 index 00000000..4eba1043 --- /dev/null +++ b/app/imports/ui/creature/slots/LevelUpDialog.vue @@ -0,0 +1,406 @@ + + + + + diff --git a/app/imports/ui/dialogStack/DialogComponentIndex.js b/app/imports/ui/dialogStack/DialogComponentIndex.js index 4f0f96e0..8fb87bc7 100644 --- a/app/imports/ui/dialogStack/DialogComponentIndex.js +++ b/app/imports/ui/dialogStack/DialogComponentIndex.js @@ -10,6 +10,7 @@ const DeleteUserAccountDialog = () => import('/imports/ui/user/DeleteUserAccount const ExperienceInsertDialog = () => import( '/imports/ui/creature/experiences/ExperienceInsertDialog.vue'); const ExperienceListDialog = () => import( '/imports/ui/creature/experiences/ExperienceListDialog.vue'); const InviteDialog = () => import('/imports/ui/user/InviteDialog.vue'); +const LevelUpDialog = () => import('/imports/ui/creature/slots/LevelUpDialog.vue'); const LibraryCreationDialog = () => import('/imports/ui/library/LibraryCreationDialog.vue'); const LibraryEditDialog = () => import('/imports/ui/library/LibraryEditDialog.vue'); const LibraryNodeCreationDialog = () => import('/imports/ui/library/LibraryNodeCreationDialog.vue'); @@ -36,6 +37,7 @@ export default { ExperienceInsertDialog, ExperienceListDialog, InviteDialog, + LevelUpDialog, LibraryCreationDialog, LibraryEditDialog, LibraryNodeCreationDialog, diff --git a/app/imports/ui/properties/viewers/ClassViewer.vue b/app/imports/ui/properties/viewers/ClassViewer.vue index 44322a52..d474faec 100644 --- a/app/imports/ui/properties/viewers/ClassViewer.vue +++ b/app/imports/ui/properties/viewers/ClassViewer.vue @@ -47,24 +47,20 @@ :cols="{cols: 12}" > mdi-plus - Get Missing Levels - - - - mdi-plus - - Level Up + + From b8a03245ea5baf0a02851bbe01c43f37ad24c7db Mon Sep 17 00:00:00 2001 From: Stefan Zermatten Date: Tue, 5 Jul 2022 15:40:55 +0200 Subject: [PATCH 037/142] Level up dialog now working --- .../methods/getSlotFillFilter.js | 10 +++------- app/imports/server/publications/library.js | 2 ++ .../ui/creature/slots/LevelUpDialog.vue | 9 +-------- .../ui/properties/viewers/ClassViewer.vue | 19 ++++++++++++++++--- 4 files changed, 22 insertions(+), 18 deletions(-) diff --git a/app/imports/api/creature/creatureProperties/methods/getSlotFillFilter.js b/app/imports/api/creature/creatureProperties/methods/getSlotFillFilter.js index 177225b0..21df0bdb 100644 --- a/app/imports/api/creature/creatureProperties/methods/getSlotFillFilter.js +++ b/app/imports/api/creature/creatureProperties/methods/getSlotFillFilter.js @@ -27,14 +27,10 @@ export default function getSlotFillFilter({slot, libraryIds}){ filter.variableName = slot.variableName; // Only search for levels the class needs - const levels = []; if (slot.missingLevels && slot.missingLevels.length) { - levels.push(...slot.missingLevels); - } else if (slot.level) { - levels.push(slot.level); - } - if (levels.length) { - filter.level = {$or: levels}; + filter.level = {$in: slot.missingLevels}; + } else { + filter.level = (slot.level || 0) + 1; } } let tagsOr = []; diff --git a/app/imports/server/publications/library.js b/app/imports/server/publications/library.js index 00a45a5b..ebfdc936 100644 --- a/app/imports/server/publications/library.js +++ b/app/imports/server/publications/library.js @@ -12,6 +12,8 @@ const LIBRARY_NODE_TREE_FIELDS = { order: 1, parent: 1, ancestors: 1, + // SlotFillers + slotQuantityFilled: 1, // Effect operation: 1, targetTags: 1, diff --git a/app/imports/ui/creature/slots/LevelUpDialog.vue b/app/imports/ui/creature/slots/LevelUpDialog.vue index 4eba1043..7a09eb1b 100644 --- a/app/imports/ui/creature/slots/LevelUpDialog.vue +++ b/app/imports/ui/creature/slots/LevelUpDialog.vue @@ -26,7 +26,6 @@ :string="model.description" />

- {{ slotPropertyTypeName }} with tags: - import propertyViewerMixin from '/imports/ui/properties/viewers/shared/propertyViewerMixin.js'; +import insertPropertyFromLibraryNode from '/imports/api/creature/creatureProperties/methods/insertPropertyFromLibraryNode.js'; export default { mixins: [propertyViewerMixin], @@ -83,13 +84,25 @@ export default { }, methods: { levelUpDialog(){ + let classId = this.model._id; this.$store.commit('pushDialogStack', { component: 'level-up-dialog', elementId: 'level-up-btn', data: { - creatureId: this.creatureId, + creatureId: this.context.creatureId, classId: this.model._id, }, + callback(nodeIds){ + if (!nodeIds || !nodeIds.length) return; + let newPropertyId = insertPropertyFromLibraryNode.call({ + nodeIds, + parentRef: { + 'id': classId, + 'collection': 'creatureProperties', + }, + }); + return `tree-node-${newPropertyId}`; + } }); }, } From 59ef7527b7720f61b7c63ee42be2f6157317baa6 Mon Sep 17 00:00:00 2001 From: Stefan Zermatten Date: Sat, 9 Jul 2022 12:53:44 +0200 Subject: [PATCH 038/142] Fixed some errors with character insertion/deletion --- .../creatures/methods/removeCreature.js | 2 ++ .../computation/writeComputation/writeScope.js | 4 ++-- .../ui/creature/character/CharacterSheet.vue | 11 +++++++++++ .../character/characterSheetTabs/BuildTab.vue | 6 ------ .../character/characterSheetTabs/StatsTab.vue | 5 +++-- .../ui/dialogStack/DialogComponentIndex.js | 2 ++ app/imports/ui/pages/CharacterList.vue | 17 +++++++++++++---- 7 files changed, 33 insertions(+), 14 deletions(-) diff --git a/app/imports/api/creature/creatures/methods/removeCreature.js b/app/imports/api/creature/creatures/methods/removeCreature.js index 66eaa0b4..d157f5d7 100644 --- a/app/imports/api/creature/creatures/methods/removeCreature.js +++ b/app/imports/api/creature/creatures/methods/removeCreature.js @@ -3,11 +3,13 @@ import { ValidatedMethod } from 'meteor/mdg:validated-method'; import { RateLimiterMixin } from 'ddp-rate-limiter-mixin'; import { assertOwnership } from '/imports/api/creature/creatures/creaturePermissions.js'; import Creatures from '/imports/api/creature/creatures/Creatures.js'; +import CreatureVariables from '/imports/api/creature/creatures/CreatureVariables.js'; import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js'; import CreatureLogs from '/imports/api/creature/log/CreatureLogs.js'; import Experiences from '/imports/api/creature/experience/Experiences.js'; function removeRelatedDocuments(creatureId){ + CreatureVariables.remove({_creatureId: creatureId}); CreatureProperties.remove({'ancestors.id': creatureId}); CreatureLogs.remove({creatureId}); Experiences.remove({creatureId}); diff --git a/app/imports/api/engine/computation/writeComputation/writeScope.js b/app/imports/api/engine/computation/writeComputation/writeScope.js index 04858b8f..1c5bfde3 100644 --- a/app/imports/api/engine/computation/writeComputation/writeScope.js +++ b/app/imports/api/engine/computation/writeComputation/writeScope.js @@ -1,9 +1,9 @@ import CreatureVariables from '/imports/api/creature/creatures/CreatureVariables.js'; import Creatures from '/imports/api/creature/creatures/Creatures.js'; import { EJSON } from 'meteor/ejson'; -import { omitBy } from 'lodash'; export default function writeScope(creatureId, computation) { + if (!creatureId) throw 'creatureId is required'; const scope = computation.scope; const variables = computation.variables || {}; delete variables._id; @@ -50,7 +50,7 @@ export default function writeScope(creatureId, computation) { if ($unset) update.$unset = $unset; CreatureVariables.upsert({_creatureId: creatureId}, update); } - if (computation.creature.dirty) { + if (computation.creature?.dirty) { Creatures.update({_creatureId: creatureId}, {$unset: { dirty: 1 }}); } } diff --git a/app/imports/ui/creature/character/CharacterSheet.vue b/app/imports/ui/creature/character/CharacterSheet.vue index 9edaf0fb..0b4870f0 100644 --- a/app/imports/ui/creature/character/CharacterSheet.vue +++ b/app/imports/ui/creature/character/CharacterSheet.vue @@ -129,6 +129,16 @@ if diff --git a/app/imports/ui/pages/Library.vue b/app/imports/ui/pages/Library.vue index a1f1866a..65a2d18f 100644 --- a/app/imports/ui/pages/Library.vue +++ b/app/imports/ui/pages/Library.vue @@ -10,38 +10,7 @@ xl="8" > - - - - - - - + { meta: { title: 'Library Collection', }, - },{ + }, { + name: 'characterSheet', path: '/character/:id', alias: '/character/:id/:urlName', components: { diff --git a/app/server/main.js b/app/server/main.js index 31140ade..642113e0 100644 --- a/app/server/main.js +++ b/app/server/main.js @@ -15,4 +15,4 @@ import '/imports/migrations/methods/index.js' import '/imports/constants/MAINTENANCE_MODE.js'; import '/imports/api/creature/creatureProperties/methods/index.js'; import '/imports/api/creature/archive/methods/index.js'; - +import '/imports/api/creature/creatures/methods/index.js'; From 0c06f60b7e568f0001edd4882a5b793c43fbdbf2 Mon Sep 17 00:00:00 2001 From: Stefan Zermatten Date: Tue, 19 Jul 2022 19:25:12 +0200 Subject: [PATCH 042/142] Fixed typo "Transfer Onwership" --- app/imports/ui/sharing/ShareDialog.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/imports/ui/sharing/ShareDialog.vue b/app/imports/ui/sharing/ShareDialog.vue index 728688e6..181b23fe 100644 --- a/app/imports/ui/sharing/ShareDialog.vue +++ b/app/imports/ui/sharing/ShareDialog.vue @@ -92,7 +92,7 @@ mdi-signature - Transfer Onwership + Transfer Ownership From 6f7e742eb9c104f62f743d79de1d77588cfa59f3 Mon Sep 17 00:00:00 2001 From: Stefan Zermatten Date: Wed, 20 Jul 2022 00:09:58 +0200 Subject: [PATCH 043/142] More of the sheet conforms to library allowances --- .../methods/changeAllowedLibraries.js | 90 ++++++ .../api/creature/creatures/methods/index.js | 1 + .../creatures/methods/insertCreature.js | 8 +- .../server/publications/searchLibraryNodes.js | 28 +- .../server/publications/slotFillers.js | 5 +- app/imports/ui/creature/CreatureForm.vue | 118 ++++++- .../character/CharacterCreationDialog.vue | 2 +- .../creature/character/CharacterSheetFab.vue | 1 + .../AddCreaturePropertyDialog.vue | 296 +++++++++--------- .../CreaturePropertyDialog.vue | 1 + 10 files changed, 377 insertions(+), 173 deletions(-) create mode 100644 app/imports/api/creature/creatures/methods/changeAllowedLibraries.js diff --git a/app/imports/api/creature/creatures/methods/changeAllowedLibraries.js b/app/imports/api/creature/creatures/methods/changeAllowedLibraries.js new file mode 100644 index 00000000..29057f55 --- /dev/null +++ b/app/imports/api/creature/creatures/methods/changeAllowedLibraries.js @@ -0,0 +1,90 @@ +import { ValidatedMethod } from 'meteor/mdg:validated-method'; +import { RateLimiterMixin } from 'ddp-rate-limiter-mixin'; +import Creatures from '/imports/api/creature/creatures/Creatures.js'; +import {assertEditPermission} from '/imports/api/sharing/sharingPermissions.js'; +import SimpleSchema from 'simpl-schema'; +import simpleSchemaMixin from '/imports/api/creature/mixins/simpleSchemaMixin.js'; + +const changeAllowedLibraries = new ValidatedMethod({ + name: 'creatures.changeAllowedLibraries', + mixins: [RateLimiterMixin, simpleSchemaMixin], + schema: new SimpleSchema({ + _id: { + type: String, + regEx: SimpleSchema.RegEx.Id, + }, + allowedLibraries: { + type: Array, + optional: true, + maxCount: 100, + }, + 'allowedLibraries.$': { + type: String, + regEx: SimpleSchema.RegEx.Id, + }, + allowedLibraryCollections: { + type: Array, + optional: true, + maxCount: 100, + }, + 'allowedLibraryCollections.$': { + type: String, + regEx: SimpleSchema.RegEx.Id, + }, + }), + rateLimit: { + numRequests: 10, + timeInterval: 5000, + }, + run({_id, allowedLibraries, allowedLibraryCollections}) { + let creature = Creatures.findOne(_id); + assertEditPermission(creature, this.userId); + let $set; + if (allowedLibraries) { + $set = { allowedLibraries } + } + if (allowedLibraryCollections) { + if (!$set) $set = {}; + $set.allowedLibraryCollections = allowedLibraryCollections; + } + if (!$set) return; + Creatures.update(_id, {$set}); + }, +}); + +const toggleAllUserLibraries = new ValidatedMethod({ + name: 'creatures.removeLibraryLimits', + mixins: [RateLimiterMixin, simpleSchemaMixin], + schema: new SimpleSchema({ + _id: { + type: String, + regEx: SimpleSchema.RegEx.Id, + }, + value: { + type: Boolean, + }, + }), + rateLimit: { + numRequests: 10, + timeInterval: 5000, + }, + run({_id, value}) { + if (value) { + Creatures.update(_id, { + $unset: { + allowedLibraryCollections: 1, + allowedLibraries: 1, + }, + }); + } else { + Creatures.update(_id, { + $set: { + allowedLibraryCollections: [], + allowedLibraries: [], + }, + }); + } + }, +}); + +export {changeAllowedLibraries, toggleAllUserLibraries}; diff --git a/app/imports/api/creature/creatures/methods/index.js b/app/imports/api/creature/creatures/methods/index.js index 42e9c99e..85aefc77 100644 --- a/app/imports/api/creature/creatures/methods/index.js +++ b/app/imports/api/creature/creatures/methods/index.js @@ -2,3 +2,4 @@ import '/imports/api/creature/creatures/methods/insertCreature.js'; import '/imports/api/creature/creatures/methods/removeCreature.js'; import '/imports/api/creature/creatures/methods/restCreature.js'; import '/imports/api/creature/creatures/methods/updateCreature.js'; +import '/imports/api/creature/creatures/methods/changeAllowedLibraries.js'; diff --git a/app/imports/api/creature/creatures/methods/insertCreature.js b/app/imports/api/creature/creatures/methods/insertCreature.js index 210f7b49..da72f6ed 100644 --- a/app/imports/api/creature/creatures/methods/insertCreature.js +++ b/app/imports/api/creature/creatures/methods/insertCreature.js @@ -7,7 +7,7 @@ import defaultCharacterProperties from '/imports/api/creature/creatures/defaultC import insertPropertyFromLibraryNode from '/imports/api/creature/creatureProperties/methods/insertPropertyFromLibraryNode.js'; import assertHasCharactersSlots from '/imports/api/creature/creatures/methods/assertHasCharacterSlots.js'; import getSlotFillFilter from '/imports/api/creature/creatureProperties/methods/getSlotFillFilter.js'; -import getUserLibraryIds from '/imports/api/library/getUserLibraryIds.js'; +import getCreatureLibraryIds from '/imports/api/library/getCreatureLibraryIds.js'; import LibraryNodes from '/imports/api/library/LibraryNodes.js'; import { insertExperienceForCreature } from '/imports/api/creature/experience/Experiences.js'; import SimpleSchema from 'simpl-schema'; @@ -78,7 +78,7 @@ const insertCreature = new ValidatedMethod({ // If the user only has a single ruleset subscribed, use it by default if (Meteor.isServer) { - insertDefaultRuleset(baseId, userId, rulesetSlot); + insertDefaultRuleset(creatureId, baseId, userId, rulesetSlot); } return creatureId; @@ -86,8 +86,8 @@ const insertCreature = new ValidatedMethod({ }); // If the user only has a single ruleset subscribed, insert it by default -function insertDefaultRuleset(baseId, userId, slot) { - const libraryIds = getUserLibraryIds(userId); +function insertDefaultRuleset(creatureId, baseId, userId, slot) { + const libraryIds = getCreatureLibraryIds(creatureId, userId); const filter = getSlotFillFilter({ slot, libraryIds }); const fillCursor = LibraryNodes.find(filter, { fields: { _id: 1 } }); const numRulesets = fillCursor.count(); diff --git a/app/imports/server/publications/searchLibraryNodes.js b/app/imports/server/publications/searchLibraryNodes.js index ddbbf4ba..cedbc7a8 100644 --- a/app/imports/server/publications/searchLibraryNodes.js +++ b/app/imports/server/publications/searchLibraryNodes.js @@ -1,6 +1,8 @@ import { check } from 'meteor/check'; import Libraries from '/imports/api/library/Libraries.js'; import LibraryNodes from '/imports/api/library/LibraryNodes.js'; +import getCreatureLibraryIds from '/imports/api/library/getCreatureLibraryIds.js'; +import getUserLibraryIds from '/imports/api/library/getUserLibraryIds.js'; import { assertViewPermission } from '/imports/api/sharing/sharingPermissions.js'; Meteor.publish('selectedLibraryNodes', function(selectedNodeIds){ @@ -37,7 +39,7 @@ Meteor.publish('selectedLibraryNodes', function(selectedNodeIds){ })]; }); -Meteor.publish('searchLibraryNodes', function(){ +Meteor.publish('searchLibraryNodes', function(creatureId){ let self = this; this.autorun(function (){ let type = self.data('type'); @@ -49,23 +51,12 @@ Meteor.publish('searchLibraryNodes', function(){ } // Get all the ids of libraries the user can access - const user = Meteor.users.findOne(userId, { - fields: {subscribedLibraries: 1} - }); - if (!user) return []; - - const subs = user.subscribedLibraries || []; - let libraries = Libraries.find({ - $or: [ - {owner: this.userId}, - {writers: this.userId}, - {readers: this.userId}, - {_id: {$in: subs}}, - ] - }, { - fields: {_id: 1, name: 1}, - }); - let libraryIds = libraries.map(lib => lib._id); + let libraryIds; + if (creatureId) { + libraryIds = getCreatureLibraryIds(creatureId, userId) + } else { + libraryIds = getUserLibraryIds(userId) + } // Build a filter for nodes in those libraries that match the type let filter = { @@ -122,6 +113,7 @@ Meteor.publish('searchLibraryNodes', function(){ }); let cursor = LibraryNodes.find(filter, options); + const libraries = Libraries.find({ _id: { $in: libraryIds } }); Mongo.Collection._publishCursor(libraries, self, 'libraries'); diff --git a/app/imports/server/publications/slotFillers.js b/app/imports/server/publications/slotFillers.js index 95a77c6c..ab6e781d 100644 --- a/app/imports/server/publications/slotFillers.js +++ b/app/imports/server/publications/slotFillers.js @@ -3,7 +3,7 @@ import Libraries from '/imports/api/library/Libraries.js'; import LibraryNodes from '/imports/api/library/LibraryNodes.js'; import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js'; import getSlotFillFilter from '/imports/api/creature/creatureProperties/methods/getSlotFillFilter.js' -import getUserLibraryIds from '/imports/api/library/getUserLibraryIds.js'; +import getCreatureLibraryIds from '/imports/api/library/getCreatureLibraryIds.js'; import { LIBRARY_NODE_TREE_FIELDS } from '/imports/server/publications/library.js'; const FIELDS = { @@ -28,7 +28,8 @@ Meteor.publish('slotFillers', function(slotId, searchTerm){ } // Get all the ids of libraries the user can access - const libraryIds = getUserLibraryIds(userId); + const creatureId = slot.ancestors[0].id; + const libraryIds = getCreatureLibraryIds(creatureId, userId); const libraries = Libraries.find({ $or: [ { owner: userId }, diff --git a/app/imports/ui/creature/CreatureForm.vue b/app/imports/ui/creature/CreatureForm.vue index f5506393..b9103ff8 100644 --- a/app/imports/ui/creature/CreatureForm.vue +++ b/app/imports/ui/creature/CreatureForm.vue @@ -33,7 +33,7 @@ @change="(value, ack) => $emit('change', {path: ['avatarPicture'], value, ack})" /> - + --> + + + + +

+ {{ libraryWriteError }} +

+
diff --git a/app/imports/ui/creature/character/CharacterCreationDialog.vue b/app/imports/ui/creature/character/CharacterCreationDialog.vue index 69e33330..568ae394 100644 --- a/app/imports/ui/creature/character/CharacterCreationDialog.vue +++ b/app/imports/ui/creature/character/CharacterCreationDialog.vue @@ -195,7 +195,7 @@ export default { } else { this.$store.commit( 'setTabForCharacterSheet', - {id: creatureId, tab: 4} + {id: creatureId, tab: 5} ); this.$emit('pop', creatureId); defer(() => { diff --git a/app/imports/ui/creature/character/CharacterSheetFab.vue b/app/imports/ui/creature/character/CharacterSheetFab.vue index 4fee5c36..49590d75 100644 --- a/app/imports/ui/creature/character/CharacterSheetFab.vue +++ b/app/imports/ui/creature/character/CharacterSheetFab.vue @@ -170,6 +170,7 @@ data: { parentDoc: forcedType ? undefined : parent, forcedType, + creatureId: this.creatureId, }, callback(result){ if (!result){ diff --git a/app/imports/ui/creature/creatureProperties/AddCreaturePropertyDialog.vue b/app/imports/ui/creature/creatureProperties/AddCreaturePropertyDialog.vue index 133d713b..944c7642 100644 --- a/app/imports/ui/creature/creatureProperties/AddCreaturePropertyDialog.vue +++ b/app/imports/ui/creature/creatureProperties/AddCreaturePropertyDialog.vue @@ -171,160 +171,166 @@ diff --git a/app/imports/ui/creature/creatureProperties/Breadcrumbs.vue b/app/imports/ui/creature/creatureProperties/Breadcrumbs.vue index 2cd94af0..abc790c6 100644 --- a/app/imports/ui/creature/creatureProperties/Breadcrumbs.vue +++ b/app/imports/ui/creature/creatureProperties/Breadcrumbs.vue @@ -3,9 +3,16 @@ class="breadcrumbs layout align-center wrap" :class="{'no-icons': noIcons}" > + + + mdi-account + + diff --git a/app/imports/ui/creature/experiences/ExperienceInsertDialog.vue b/app/imports/ui/creature/experiences/ExperienceInsertDialog.vue index 28756d1f..9b9ecc72 100644 --- a/app/imports/ui/creature/experiences/ExperienceInsertDialog.vue +++ b/app/imports/ui/creature/experiences/ExperienceInsertDialog.vue @@ -34,9 +34,9 @@ import schemaFormMixin from '/imports/ui/properties/forms/shared/schemaFormMixin export default { components: { - DialogBase, + DialogBase, ExperienceForm, - }, + }, mixins: [schemaFormMixin], provide: { context: { @@ -52,10 +52,10 @@ export default { type: Boolean, }, }, - data(){ + data() { let schema = ExperienceSchema.omit('creatureId'); let startingModel = {}; - if (this.startAsMilestone){ + if (this.startAsMilestone) { startingModel.levels = 1; } return { @@ -65,14 +65,14 @@ export default { debounceTime: 0, }; }, - methods:{ - insertExperience(){ + methods: { + insertExperience() { let experience = this.schema.clean(this.model); let id = insertExperience.call({ experience, creatureIds: this.creatureIds, - }, (error) => { - if (error){ + }, (error) => { + if (error) { console.error(error); } }); @@ -83,4 +83,5 @@ export default { diff --git a/app/imports/ui/creature/slots/LevelUpDialog.vue b/app/imports/ui/creature/slots/LevelUpDialog.vue index 7a09eb1b..4503a96e 100644 --- a/app/imports/ui/creature/slots/LevelUpDialog.vue +++ b/app/imports/ui/creature/slots/LevelUpDialog.vue @@ -192,18 +192,17 @@ import getSlotFillFilter from '/imports/api/creature/creatureProperties/methods/ 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 { components: { - DialogBase, + DialogBase, TreeNodeView, PropertyDescription, LibraryNodeExpansionContent, PropertyTags, - }, - props:{ + }, + props: { classId: { type: String, default: undefined, @@ -217,42 +216,44 @@ export default { default: undefined, }, }, - data(){return { - selectedNodeIds: [], - searchInput: undefined, - searchValue: undefined, - showDisabled: false, - disabledNodeCount: undefined, - }}, + data() { + return { + selectedNodeIds: [], + searchInput: undefined, + searchValue: undefined, + showDisabled: false, + disabledNodeCount: undefined, + } + }, reactiveProvide: { name: 'context', include: ['creatureId'], }, computed: { - tagsSearched(){ + tagsSearched() { let or = []; let not = []; - if (this.model.slotTags && this.model.slotTags.length){ + if (this.model.slotTags && this.model.slotTags.length) { or.push(this.model.slotTags); } this.model.extraTags?.forEach(extras => { - if (extras.tags?.length){ - if(extras.operation === 'OR'){ + if (extras.tags?.length) { + if (extras.operation === 'OR') { or.push(extras.tags); - } else if (extras.operation === 'NOT'){ + } else if (extras.operation === 'NOT') { not.push(extras.tags); } } }); - return {or, not}; + return { or, not }; }, }, methods: { - loadMore(){ + loadMore() { if (this.currentLimit >= this.countAll) return; this._subs['classFillers'].setData('limit', this.currentLimit + 50); }, - openPropertyDetails(id){ + openPropertyDetails(id) { this.$store.commit('pushDialogStack', { component: 'library-node-dialog', elementId: id, @@ -261,26 +262,26 @@ export default { }, }); }, - isDisabled(node){ + isDisabled(node) { return node._disabledBySlotFillerCondition || node._disabledByAlreadyAdded || - ( - node._disabledByQuantityFilled && - !this.selectedNodeIds.includes(node._id) - ) + ( + node._disabledByQuantityFilled && + !this.selectedNodeIds.includes(node._id) + ) }, }, meteor: { $subscribe: { - 'classFillers'(){ + 'classFillers'() { return [this.classId, this.searchValue || undefined] }, }, - searchLoading(){ + searchLoading() { return !!this.searchValue && !this.$subReady.classFillers; }, - model(){ - if (this.classId){ + model() { + if (this.classId) { return CreatureProperties.findOne(this.classId); } else if (this.dummySlot) { let model = clone(this.dummySlot) @@ -294,40 +295,40 @@ export default { if (!this.creatureId) return {}; return CreatureVariables.findOne({ _creatureId: this.creatureId }) || {}; }, - currentLimit(){ + currentLimit() { return this._subs['classFillers'].data('limit') || 50; }, - countAll(){ + countAll() { return this._subs['classFillers'].data('countAll'); }, - alreadyAdded(){ + alreadyAdded() { let added = new Set(); if (!this.model.unique) return added; let ancestorId; - if (this.model.unique === 'uniqueInSlot'){ + if (this.model.unique === 'uniqueInSlot') { ancestorId = this.model._id; - } else if (this.model.unique === 'uniqueInCreature'){ + } else if (this.model.unique === 'uniqueInCreature') { ancestorId = this.creatureId; } CreatureProperties.find({ 'ancestors.id': ancestorId, - libraryNodeId: {$exists: true}, - removed: {$ne: true}, + libraryNodeId: { $exists: true }, + removed: { $ne: true }, }, { - fields: {libraryNodeId: 1}, + fields: { libraryNodeId: 1 }, }).forEach(prop => { added.add(prop.libraryNodeId); }); return added; }, - totalQuantitySelected(){ + totalQuantitySelected() { let quantitySelected = 0; LibraryNodes.find({ - _id: {$in: this.selectedNodeIds} + _id: { $in: this.selectedNodeIds } }, { - fields: {slotQuantityFilled: 1}, + fields: { slotQuantityFilled: 1 }, }).forEach(node => { - if (Number.isFinite(node.slotQuantityFilled)){ + if (Number.isFinite(node.slotQuantityFilled)) { quantitySelected += node.slotQuantityFilled; } else { quantitySelected += 1; @@ -335,30 +336,30 @@ export default { }); return quantitySelected; }, - spaceLeft(){ + spaceLeft() { if (!this.model.quantityExpected || this.model.quantityExpected.value === 0) return undefined; return this.model.spaceLeft - this.totalQuantitySelected; }, - libraryNames(){ + libraryNames() { let names = {}; Libraries.find().forEach(lib => names[lib._id] = lib.name) return names; }, - libraryNodes(){ + libraryNodes() { let filter = getSlotFillFilter({ slot: this.model }); let nodes = LibraryNodes.find(filter, { - sort: {name: 1, order: 1} + sort: { name: 1, order: 1 } }).fetch(); let disabledNodeCount = 0; // Mark classFillers whose condition isn't met or are too big to fit // the quantity to fill nodes.forEach(node => { - if (node.slotFillerCondition){ + if (node.slotFillerCondition) { try { let parseNode = parse(node.slotFillerCondition); - const {result: resultNode} = resolve('reduce', parseNode, this.variables); - if (resultNode?.parseType === 'constant'){ - if (!resultNode.value){ + const { result: resultNode } = resolve('reduce', parseNode, this.variables); + if (resultNode?.parseType === 'constant') { + if (!resultNode.value) { node._disabledBySlotFillerCondition = true; disabledNodeCount += 1; } @@ -367,7 +368,7 @@ export default { node._conditionError = toString(resultNode); disabledNodeCount += 1; } - } catch (e){ + } catch (e) { console.warn(e); let error = prettifyParseError(e); node._disabledBySlotFillerCondition = true; @@ -378,10 +379,10 @@ export default { let quantityToFill = node.type === 'slotFiller' ? node.slotQuantityFilled : 1; if ( quantityToFill > this.spaceLeft - ){ + ) { node._disabledByQuantityFilled = true; } - if (this.alreadyAdded.has(node._id)){ + if (this.alreadyAdded.has(node._id)) { node._disabledByAlreadyAdded = true; } }); @@ -393,7 +394,7 @@ export default { diff --git a/app/imports/ui/creature/slots/SlotFillDialog.vue b/app/imports/ui/creature/slots/SlotFillDialog.vue index caee497e..cbad8fb9 100644 --- a/app/imports/ui/creature/slots/SlotFillDialog.vue +++ b/app/imports/ui/creature/slots/SlotFillDialog.vue @@ -198,13 +198,13 @@ import { clone } from 'lodash'; export default { components: { - DialogBase, + DialogBase, TreeNodeView, PropertyDescription, LibraryNodeExpansionContent, PropertyTags, - }, - props:{ + }, + props: { slotId: { type: String, default: undefined, @@ -218,36 +218,38 @@ export default { default: undefined, }, }, - data(){return { - selectedNodeIds: [], - searchInput: undefined, - searchValue: undefined, - showDisabled: false, - disabledNodeCount: undefined, - }}, + data() { + return { + selectedNodeIds: [], + searchInput: undefined, + searchValue: undefined, + showDisabled: false, + disabledNodeCount: undefined, + } + }, reactiveProvide: { name: 'context', include: ['creatureId'], }, computed: { - tagsSearched(){ + tagsSearched() { let or = []; let not = []; - if (this.model.slotTags && this.model.slotTags.length){ + if (this.model.slotTags && this.model.slotTags.length) { or.push(this.model.slotTags); } this.model.extraTags?.forEach(extras => { - if (extras.tags?.length){ - if(extras.operation === 'OR'){ + if (extras.tags?.length) { + if (extras.operation === 'OR') { or.push(extras.tags); - } else if (extras.operation === 'NOT'){ + } else if (extras.operation === 'NOT') { not.push(extras.tags); } } }); - return {or, not}; + return { or, not }; }, - slotPropertyTypeName(){ + slotPropertyTypeName() { if (!this.model) return; if (!this.model.slotType) return 'Property'; let propName = getPropertyName(this.model.slotType); @@ -255,11 +257,11 @@ export default { }, }, methods: { - loadMore(){ + loadMore() { if (this.currentLimit >= this.countAll) return; this._subs['slotFillers'].setData('limit', this.currentLimit + 50); }, - openPropertyDetails(id){ + openPropertyDetails(id) { this.$store.commit('pushDialogStack', { component: 'library-node-dialog', elementId: id, @@ -268,26 +270,26 @@ export default { }, }); }, - isDisabled(node){ + isDisabled(node) { return node._disabledBySlotFillerCondition || node._disabledByAlreadyAdded || - ( - node._disabledByQuantityFilled && - !this.selectedNodeIds.includes(node._id) - ) + ( + node._disabledByQuantityFilled && + !this.selectedNodeIds.includes(node._id) + ) }, }, meteor: { $subscribe: { - 'slotFillers'(){ + 'slotFillers'() { return [this.slotId, this.searchValue || undefined] }, }, - searchLoading(){ + searchLoading() { return !!this.searchValue && !this.$subReady.slotFillers; }, - model(){ - if (this.slotId){ + model() { + if (this.slotId) { return CreatureProperties.findOne(this.slotId); } else if (this.dummySlot) { let model = clone(this.dummySlot) @@ -301,40 +303,40 @@ export default { if (!this.creatureId) return {}; return CreatureVariables.findOne({ _creatureId: this.creatureId }) || {}; }, - currentLimit(){ + currentLimit() { return this._subs['slotFillers'].data('limit') || 50; }, - countAll(){ + countAll() { return this._subs['slotFillers'].data('countAll'); }, - alreadyAdded(){ + alreadyAdded() { let added = new Set(); if (!this.model.unique) return added; let ancestorId; - if (this.model.unique === 'uniqueInSlot'){ + if (this.model.unique === 'uniqueInSlot') { ancestorId = this.model._id; - } else if (this.model.unique === 'uniqueInCreature'){ + } else if (this.model.unique === 'uniqueInCreature') { ancestorId = this.creatureId; } CreatureProperties.find({ 'ancestors.id': ancestorId, - libraryNodeId: {$exists: true}, - removed: {$ne: true}, + libraryNodeId: { $exists: true }, + removed: { $ne: true }, }, { - fields: {libraryNodeId: 1}, + fields: { libraryNodeId: 1 }, }).forEach(prop => { added.add(prop.libraryNodeId); }); return added; }, - totalQuantitySelected(){ + totalQuantitySelected() { let quantitySelected = 0; LibraryNodes.find({ - _id: {$in: this.selectedNodeIds} + _id: { $in: this.selectedNodeIds } }, { - fields: {slotQuantityFilled: 1}, + fields: { slotQuantityFilled: 1 }, }).forEach(node => { - if (Number.isFinite(node.slotQuantityFilled)){ + if (Number.isFinite(node.slotQuantityFilled)) { quantitySelected += node.slotQuantityFilled; } else { quantitySelected += 1; @@ -342,30 +344,30 @@ export default { }); return quantitySelected; }, - spaceLeft(){ + spaceLeft() { if (!this.model.quantityExpected || this.model.quantityExpected.value === 0) return undefined; return this.model.spaceLeft - this.totalQuantitySelected; }, - libraryNames(){ + libraryNames() { let names = {}; Libraries.find().forEach(lib => names[lib._id] = lib.name) return names; }, - libraryNodes(){ - let filter = getSlotFillFilter({slot: this.model}); + libraryNodes() { + let filter = getSlotFillFilter({ slot: this.model }); let nodes = LibraryNodes.find(filter, { - sort: {name: 1, order: 1} + sort: { name: 1, order: 1 } }).fetch(); let disabledNodeCount = 0; // Mark slotFillers whose condition isn't met or are too big to fit // the quantity to fill nodes.forEach(node => { - if (node.slotFillerCondition){ + if (node.slotFillerCondition) { try { let parseNode = parse(node.slotFillerCondition); - const {result: resultNode} = resolve('reduce', parseNode, this.variables); - if (resultNode?.parseType === 'constant'){ - if (!resultNode.value){ + const { result: resultNode } = resolve('reduce', parseNode, this.variables); + if (resultNode?.parseType === 'constant') { + if (!resultNode.value) { node._disabledBySlotFillerCondition = true; disabledNodeCount += 1; } @@ -374,7 +376,7 @@ export default { node._conditionError = toString(resultNode); disabledNodeCount += 1; } - } catch (e){ + } catch (e) { console.warn(e); let error = prettifyParseError(e); node._disabledBySlotFillerCondition = true; @@ -385,10 +387,10 @@ export default { let quantityToFill = node.type === 'slotFiller' ? node.slotQuantityFilled : 1; if ( quantityToFill > this.spaceLeft - ){ + ) { node._disabledByQuantityFilled = true; } - if (this.alreadyAdded.has(node._id)){ + if (this.alreadyAdded.has(node._id)) { node._disabledByAlreadyAdded = true; } }); @@ -400,7 +402,7 @@ export default { diff --git a/app/imports/ui/dialogStack/DeleteConfirmationDialog.vue b/app/imports/ui/dialogStack/DeleteConfirmationDialog.vue index d7f90460..c8b76735 100644 --- a/app/imports/ui/dialogStack/DeleteConfirmationDialog.vue +++ b/app/imports/ui/dialogStack/DeleteConfirmationDialog.vue @@ -4,7 +4,10 @@ Delete {{ typeName }}
- + This can't be undone

@@ -12,9 +15,9 @@

diff --git a/app/imports/ui/dialogStack/DialogBase.vue b/app/imports/ui/dialogStack/DialogBase.vue index 14a1f1cf..4fcdd912 100644 --- a/app/imports/ui/dialogStack/DialogBase.vue +++ b/app/imports/ui/dialogStack/DialogBase.vue @@ -50,63 +50,69 @@ diff --git a/app/imports/ui/dialogStack/HelpDialog.vue b/app/imports/ui/dialogStack/HelpDialog.vue index 158bc71e..9fe700b2 100644 --- a/app/imports/ui/dialogStack/HelpDialog.vue +++ b/app/imports/ui/dialogStack/HelpDialog.vue @@ -43,16 +43,16 @@ import MarkdownText from '/imports/ui/components/MarkdownText.vue'; import Docs from '/imports/api/docs/Docs.js'; export default { - components: { + components: { DialogBase, MarkdownText, - }, - props: { + }, + props: { path: { type: String, required: true, } - }, + }, computed: { prop() { return propsByDocsPath.get(this.path); @@ -63,7 +63,7 @@ export default { } else { const titleCase = this.path.replace( /(\w*)(\W+)/g, - function(txt, word) { + function (txt, word) { return word.charAt(0).toUpperCase() + word.substr(1).toLowerCase() + ' '; } ); @@ -73,7 +73,7 @@ export default { }, meteor: { $subscribe: { - 'docs'(){ + 'docs'() { return [this.path]; }, }, @@ -104,4 +104,5 @@ export default { diff --git a/app/imports/ui/dialogStack/mockElement.js b/app/imports/ui/dialogStack/mockElement.js index de843514..e3f19654 100644 --- a/app/imports/ui/dialogStack/mockElement.js +++ b/app/imports/ui/dialogStack/mockElement.js @@ -2,7 +2,7 @@ import { parse, stringify } from 'css-box-shadow'; // Only supports border radius defined like "20px" or "100%" const transformedRadius = (radiusString, deltaWidth, deltaHeight) => { - if (/^\d+\.?\d*px$/.test(radiusString)){ + if (/^\d+\.?\d*px$/.test(radiusString)) { //The radius is defined in pixel units, so get the radius as a number const rad = +radiusString.match(/\d+\.?\d*/)[0]; // Set the x and y radius of the "to" element, compensating for scale @@ -15,7 +15,7 @@ const transformedRadius = (radiusString, deltaWidth, deltaHeight) => { const transformedBoxShadow = (shadowString, deltaWidth, deltaHeight) => { if (shadowString === 'none') return shadowString; - if (shadowString[0] === 'r'){ + if (shadowString[0] === 'r') { let strings = shadowString.match(/rgba\([^)]+\)[^,]+/g); strings = strings.map(string => { // Move color to end @@ -35,24 +35,24 @@ const transformedBoxShadow = (shadowString, deltaWidth, deltaHeight) => { return stringify(shadows); } -export default function mockElement({source, target, offset = {x: 0, y: 0}}){ - if (!source || !target) throw `Can't mock without ${source ? 'target' : 'source'}` ; +export default function mockElement({ source, target, offset = { x: 0, y: 0 } }) { + if (!source || !target) throw `Can't mock without ${source ? 'target' : 'source'}`; let sourceRect = source.getBoundingClientRect(); - let targetRect = target.getBoundingClientRect(); + let targetRect = target.getBoundingClientRect(); - // Get how must the target change to become the source - const deltaWidth = sourceRect.width / targetRect.width; - const deltaHeight = sourceRect.height / targetRect.height; - const deltaLeft = sourceRect.left - targetRect.left + offset.x; - const deltaTop = sourceRect.top - targetRect.top + offset.y; - // Mock the source - target.style.transform = `translate(${deltaLeft}px, ${deltaTop}px) ` + + // Get how must the target change to become the source + const deltaWidth = sourceRect.width / targetRect.width; + const deltaHeight = sourceRect.height / targetRect.height; + const deltaLeft = sourceRect.left - targetRect.left + offset.x; + const deltaTop = sourceRect.top - targetRect.top + offset.y; + // Mock the source + target.style.transform = `translate(${deltaLeft}px, ${deltaTop}px) ` + `scale(${deltaWidth}, ${deltaHeight})`; - // Mock the background color unless it's completely transparent - let backgroundColor = getComputedStyle(source).backgroundColor - if (backgroundColor !== 'rgba(0, 0, 0, 0)'){ - target.style.backgroundColor = backgroundColor; - } + // Mock the background color unless it's completely transparent + let backgroundColor = getComputedStyle(source).backgroundColor + if (backgroundColor !== 'rgba(0, 0, 0, 0)') { + target.style.backgroundColor = backgroundColor; + } // Edge might not combine all border radii into a single value, // So we just sample the top left one if we need to let oldRadius = getComputedStyle(source).borderRadius || diff --git a/app/imports/ui/files/ArchiveFileCard.vue b/app/imports/ui/files/ArchiveFileCard.vue index cab9edde..d8ec1f25 100644 --- a/app/imports/ui/files/ArchiveFileCard.vue +++ b/app/imports/ui/files/ArchiveFileCard.vue @@ -44,17 +44,19 @@ export default { required: true, }, }, - data(){return { - restoreLoading: false, - removeLoading: false, - }}, - meteor: { - characterSlots(){ + data() { + return { + restoreLoading: false, + removeLoading: false, + } + }, + meteor: { + characterSlots() { return characterSlotsRemaining(Meteor.userId()); }, - }, + }, methods: { - restore(){ + restore() { this.restoreLoading = true; restoreCreatureFromFile.call({ fileId: this.model._id, @@ -62,26 +64,26 @@ export default { this.restoreLoading = false; if (!error) return; console.error(error); - snackbar({text: error.reason}); + snackbar({ text: error.reason }); + }); + }, + removeArchiveCharacter() { + let that = this; + this.$store.commit('pushDialogStack', { + component: 'delete-confirmation-dialog', + elementId: `${that.model._id}-archive-card`, + data: { + name: this.model.meta.creatureName, + typeName: 'Character Archive' + }, + callback(confirmation) { + if (!confirmation) return; + removeArchiveCreature.call({ fileId: that.model._id }, (error) => { + if (error) console.error(error); + }); + } }); }, - removeArchiveCharacter(){ - let that = this; - this.$store.commit('pushDialogStack', { - component: 'delete-confirmation-dialog', - elementId: `${that.model._id}-archive-card`, - data: { - name: this.model.meta.creatureName, - typeName: 'Character Archive' - }, - callback(confirmation){ - if(!confirmation) return; - removeArchiveCreature.call({fileId: that.model._id}, (error) => { - if (error) console.error(error); - }); - } - }); - }, }, } diff --git a/app/imports/ui/files/UserImageCard.vue b/app/imports/ui/files/UserImageCard.vue index e70141a6..85c5d155 100644 --- a/app/imports/ui/files/UserImageCard.vue +++ b/app/imports/ui/files/UserImageCard.vue @@ -34,7 +34,6 @@ diff --git a/app/imports/ui/layouts/AppLayout.vue b/app/imports/ui/layouts/AppLayout.vue index 8d958e86..99469969 100644 --- a/app/imports/ui/layouts/AppLayout.vue +++ b/app/imports/ui/layouts/AppLayout.vue @@ -6,9 +6,7 @@ > - + - +
{{ $store.state.pageTitle }}
- +
- +
- +
- + - + diff --git a/app/imports/ui/library/LibraryCollectionEditDialog.vue b/app/imports/ui/library/LibraryCollectionEditDialog.vue index 8cf2e70c..cccce61a 100644 --- a/app/imports/ui/library/LibraryCollectionEditDialog.vue +++ b/app/imports/ui/library/LibraryCollectionEditDialog.vue @@ -63,62 +63,62 @@ import { snackbar } from '/imports/ui/components/snackbars/SnackbarQueue.js'; import Libraries from '/imports/api/library/Libraries.js'; export default { - components: { - DialogBase, - }, - props: { - _id: String, - }, - methods: { - updateLibraryCollection(update, ack){ - updateLibraryCollection.call({_id: this._id, update}, (error) =>{ - ack(error && error.reason || error); - }); + components: { + DialogBase, + }, + props: { + _id: String, + }, + methods: { + updateLibraryCollection(update, ack) { + updateLibraryCollection.call({ _id: this._id, update }, (error) => { + ack(error && error.reason || error); + }); }, - remove(){ - let that = this; - this.$store.commit('pushDialogStack', { - component: 'delete-confirmation-dialog', - elementId: 'delete-library-button', - data: { - name: this.model.name, - typeName: 'Collection' - }, - callback(confirmation){ - if(!confirmation) return; - removeLibraryCollection.call({_id: that._id}, (error) => { + remove() { + let that = this; + this.$store.commit('pushDialogStack', { + component: 'delete-confirmation-dialog', + elementId: 'delete-library-button', + data: { + name: this.model.name, + typeName: 'Collection' + }, + callback(confirmation) { + if (!confirmation) return; + removeLibraryCollection.call({ _id: that._id }, (error) => { if (error) { console.error(error); snackbar({ text: error.reason, }); - } else { + } else { that.$router.push({ name: 'library', replace: true }); that.$store.dispatch('popDialogStack'); - } - }); - } - }); - }, - share(){ - this.$store.commit('pushDialogStack', { - component: 'share-dialog', - elementId: 'share-library-button', - data: { - docRef: { + } + }); + } + }); + }, + share() { + this.$store.commit('pushDialogStack', { + component: 'share-dialog', + elementId: 'share-library-button', + data: { + docRef: { id: this._id, collection: 'libraryCollections', } - }, - }); - }, - }, - meteor: { - '$subscribe':{ + }, + }); + }, + }, + meteor: { + '$subscribe': { libraries: [], }, - model(){ - return LibraryCollections.findOne(this._id); + model() { + return LibraryCollections.findOne(this._id); }, libraryOptions() { const userId = Meteor.userId(); @@ -131,7 +131,7 @@ export default { { public: true }, ] }, - {sort: {name: 1}} + { sort: { name: 1 } } ).map(library => { return { text: library.name, @@ -139,9 +139,10 @@ export default { }; }); }, - } + } } diff --git a/app/imports/ui/library/LibraryCollectionToolbar.vue b/app/imports/ui/library/LibraryCollectionToolbar.vue index 4147836e..0a128b90 100644 --- a/app/imports/ui/library/LibraryCollectionToolbar.vue +++ b/app/imports/ui/library/LibraryCollectionToolbar.vue @@ -43,24 +43,26 @@ import { assertDocEditPermission } from '/imports/api/sharing/sharingPermissions import { mapMutations } from 'vuex'; export default { - data(){ return { - loading: false, - }}, + data() { + return { + loading: false, + } + }, meteor: { - libraryCollection(){ + libraryCollection() { return LibraryCollections.findOne(this.$route.params.id); }, - subscribed(){ + subscribed() { const libraryCollectionId = this.$route.params.id; const user = Meteor.user(); return user?.subscribedLibraryCollections?.includes(libraryCollectionId); }, - showSubscribeButton(){ + showSubscribeButton() { let user = Meteor.user(); let libraryCollection = this.libraryCollection; if (!user || !libraryCollection) return; let userId = user._id; - if (user.subscribedLibraryCollections?.includes(libraryCollection._id)){ + if (user.subscribedLibraryCollections?.includes(libraryCollection._id)) { return true } else if ( libraryCollection.readers.includes(userId) || @@ -72,7 +74,7 @@ export default { return true; } }, - canEdit(){ + canEdit() { try { assertDocEditPermission(this.libraryCollection, Meteor.userId()); return true @@ -85,7 +87,7 @@ export default { ...mapMutations([ 'toggleDrawer', ]), - subscribe(value){ + subscribe(value) { this.loading = true; Meteor.users.subscribeToLibraryCollection.call({ libraryCollectionId: this.$route.params.id, @@ -94,16 +96,17 @@ export default { this.loading = false; }); }, - editLibraryCollection(){ - this.$store.commit('pushDialogStack', { - component: 'library-collection-edit-dialog', - elementId: 'library-collection-edit-button', - data: {_id: this.$route.params.id}, - }); - }, + editLibraryCollection() { + this.$store.commit('pushDialogStack', { + component: 'library-collection-edit-dialog', + elementId: 'library-collection-edit-button', + data: { _id: this.$route.params.id }, + }); + }, }, } diff --git a/app/imports/ui/library/LibraryContentsContainer.vue b/app/imports/ui/library/LibraryContentsContainer.vue index f30e30fb..1e70fa07 100644 --- a/app/imports/ui/library/LibraryContentsContainer.vue +++ b/app/imports/ui/library/LibraryContentsContainer.vue @@ -1,7 +1,5 @@ - + + diff --git a/app/imports/ui/library/SingleLibraryToolbar.vue b/app/imports/ui/library/SingleLibraryToolbar.vue index fc953903..c3404d8f 100644 --- a/app/imports/ui/library/SingleLibraryToolbar.vue +++ b/app/imports/ui/library/SingleLibraryToolbar.vue @@ -43,24 +43,26 @@ import { assertDocEditPermission } from '/imports/api/sharing/sharingPermissions import { mapMutations } from 'vuex'; export default { - data(){ return { - loading: false, - }}, + data() { + return { + loading: false, + } + }, meteor: { - library(){ + library() { return Libraries.findOne(this.$route.params.id); }, - subscribed(){ + subscribed() { let libraryId = this.$route.params.id; let user = Meteor.user(); return user?.subscribedLibraries?.includes(libraryId); }, - showSubscribeButton(){ + showSubscribeButton() { let user = Meteor.user(); let library = this.library; if (!user || !library) return; let userId = user._id; - if (user.subscribedLibraries.includes(library._id)){ + if (user.subscribedLibraries.includes(library._id)) { return true } else if ( library.readers.includes(userId) || @@ -72,7 +74,7 @@ export default { return true; } }, - canEdit(){ + canEdit() { try { assertDocEditPermission(this.library, Meteor.userId()); return true @@ -85,7 +87,7 @@ export default { ...mapMutations([ 'toggleDrawer', ]), - subscribe(value){ + subscribe(value) { this.loading = true; Meteor.users.subscribeToLibrary.call({ libraryId: this.$route.params.id, @@ -94,16 +96,17 @@ export default { this.loading = false; }); }, - editLibrary(){ - this.$store.commit('pushDialogStack', { - component: 'library-edit-dialog', - elementId: 'library-edit-button', - data: {_id: this.$route.params.id}, - }); - }, + editLibrary() { + this.$store.commit('pushDialogStack', { + component: 'library-edit-dialog', + elementId: 'library-edit-button', + data: { _id: this.$route.params.id }, + }); + }, }, } diff --git a/app/imports/ui/pages/CharacterSheetPage.vue b/app/imports/ui/pages/CharacterSheetPage.vue index c21908db..a2346e36 100644 --- a/app/imports/ui/pages/CharacterSheetPage.vue +++ b/app/imports/ui/pages/CharacterSheetPage.vue @@ -8,8 +8,8 @@ diff --git a/app/imports/ui/pages/Documentation.vue b/app/imports/ui/pages/Documentation.vue index 8a0acb10..c94fe41f 100644 --- a/app/imports/ui/pages/Documentation.vue +++ b/app/imports/ui/pages/Documentation.vue @@ -37,9 +37,9 @@ import Docs from '/imports/api/docs/Docs.js'; import { propsByDocsPath } from '/imports/constants/PROPERTIES.js'; export default { - components: { + components: { MarkdownText, - }, + }, data() { return { action: undefined, @@ -54,21 +54,21 @@ export default { }, title() { if (this.prop) { - return this.prop.name + ' Docs'; - } else { - const titleCase = this.path.replace( - /(\w*)(\W+)/g, - function(txt, word) { - return word.charAt(0).toUpperCase() + word.substr(1).toLowerCase() + ' '; - } - ); - return titleCase || 'DiceCloud Docs'; - } + return this.prop.name + ' Docs'; + } else { + const titleCase = this.path.replace( + /(\w*)(\W+)/g, + function (txt, word) { + return word.charAt(0).toUpperCase() + word.substr(1).toLowerCase() + ' '; + } + ); + return titleCase || 'DiceCloud Docs'; + } } }, meteor: { $subscribe: { - 'docs'(){ + 'docs'() { return [this.path]; }, }, diff --git a/app/imports/ui/pages/Home.vue b/app/imports/ui/pages/Home.vue index 171ade01..72642172 100644 --- a/app/imports/ui/pages/Home.vue +++ b/app/imports/ui/pages/Home.vue @@ -132,17 +132,17 @@ diff --git a/app/imports/ui/pages/SignIn.vue b/app/imports/ui/pages/SignIn.vue index 87f83356..a1a0ba2e 100644 --- a/app/imports/ui/pages/SignIn.vue +++ b/app/imports/ui/pages/SignIn.vue @@ -91,53 +91,53 @@ diff --git a/app/imports/ui/properties/components/actions/ActionCard.vue b/app/imports/ui/properties/components/actions/ActionCard.vue index 50f22c18..cf204d71 100644 --- a/app/imports/ui/properties/components/actions/ActionCard.vue +++ b/app/imports/ui/properties/components/actions/ActionCard.vue @@ -38,9 +38,7 @@ :disabled="model.insufficientResources || !context.editPermission" @click.stop="doAction" > - +
-
+
{{ model.name || propertyName }}
@@ -89,9 +85,7 @@ /> 3; }, - propertyName(){ + propertyName() { return getPropertyName(this.model.type); }, cardClasses() { @@ -173,12 +169,12 @@ export default { actionTypeIcon() { return `$vuetify.icons.${this.model.actionType}`; }, - }, + }, methods: { - click(e){ - this.$emit('click', e); - }, - doAction({advantage}){ + click(e) { + this.$emit('click', e); + }, + doAction({ advantage }) { this.doActionLoading = true; this.shwing(); doAction.call({ @@ -188,13 +184,13 @@ export default { } }, error => { this.doActionLoading = false; - if (error){ + if (error) { console.error(error); - snackbar({text: error.reason}); + snackbar({ text: error.reason }); } }); }, - shwing(){ + shwing() { this.activated = true; setTimeout(() => { this.activated = undefined; @@ -209,9 +205,11 @@ export default { transition: box-shadow .4s cubic-bezier(0.25, 0.8, 0.25, 1), transform 0.075s ease; } + .action-card.active { transform: scale(0.92); } + .action-title { font-size: 16px; font-weight: 400; @@ -222,9 +220,10 @@ export default { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; - transition: .3s cubic-bezier(.25,.8,.5,1); + transition: .3s cubic-bezier(.25, .8, .5, 1); width: 100%; } + .action-sub-title { color: #9e9e9e; flex-grow: 0; @@ -236,15 +235,19 @@ export default { text-overflow: ellipsis; width: 100%; } + .action-child { height: 32px; } + .theme--light.muted-text { - color: rgba(0,0,0,.3) !important; + color: rgba(0, 0, 0, .3) !important; } + .theme--dark.muted-text { - color: hsla(0,0%,100%,.3) !important; + color: hsla(0, 0%, 100%, .3) !important; } + .action-card { transition: transform 0.15s cubic; } @@ -252,12 +255,14 @@ export default { diff --git a/app/imports/ui/properties/components/attributes/AbilityListTile.vue b/app/imports/ui/properties/components/attributes/AbilityListTile.vue index c4d748da..46d36bb8 100644 --- a/app/imports/ui/properties/components/attributes/AbilityListTile.vue +++ b/app/imports/ui/properties/components/attributes/AbilityListTile.vue @@ -55,7 +55,7 @@ import numberToSignedString from '/imports/ui/utility/numberToSignedString.js'; import RollPopup from '/imports/ui/components/RollPopup.vue'; import doCheck from '/imports/api/engine/actions/doCheck.js'; -import {snackbar} from '/imports/ui/components/snackbars/SnackbarQueue.js'; +import { snackbar } from '/imports/ui/components/snackbars/SnackbarQueue.js'; export default { components: { @@ -66,23 +66,25 @@ export default { default: {}, }, }, - props: { - model: {type: Object, required: true}, - }, - data(){return { - checkLoading: false, - }}, - computed: { - hasClickListener(){ + props: { + model: { type: Object, required: true }, + }, + data() { + return { + checkLoading: false, + } + }, + computed: { + hasClickListener() { return this.$listeners && this.$listeners.click - }, - }, - methods: { - numberToSignedString, - click(e){ - this.$emit('click', e); - }, - check({advantage}){ + }, + }, + methods: { + numberToSignedString, + click(e) { + this.$emit('click', e); + }, + check({ advantage }) { this.checkLoading = true; doCheck.call({ propId: this.model._id, @@ -91,15 +93,15 @@ export default { }, }, error => { this.checkLoading = false; - if (error){ + if (error) { console.error(error); - snackbar({text: error.reason}); + snackbar({ text: error.reason }); } }); }, - }, + }, meteor: { - swapScoresAndMods(){ + swapScoresAndMods() { let user = Meteor.user(); return user && user.preferences && @@ -110,26 +112,32 @@ export default { diff --git a/app/imports/ui/properties/components/attributes/HealthBar.vue b/app/imports/ui/properties/components/attributes/HealthBar.vue index ae9b3bce..da4bbb7d 100644 --- a/app/imports/ui/properties/components/attributes/HealthBar.vue +++ b/app/imports/ui/properties/components/attributes/HealthBar.vue @@ -104,9 +104,18 @@ export default { }, }, props: { - value: Number, - maxValue: Number, - name: String, + value: { + type: Number, + default: undefined, + }, + maxValue: { + type: Number, + default: undefined, + }, + name: { + type: String, + default: undefined, + }, color: { type: String, default() { @@ -139,22 +148,22 @@ export default { barColor() { const fraction = this.value / this.maxValue; if (!Number.isFinite(fraction)) return this.color; - if (fraction > 0.5){ + if (fraction > 0.5) { return this.color; } else if (this.midColor && this.lowColor) { return chroma.mix(this.lowColor, this.midColor, fraction * 2).hex(); - } else if (this.midColor){ + } else if (this.midColor) { return this.midColor; } return this.color; }, - barBackgroundColor(){ + barBackgroundColor() { return chroma(this.barColor) - .darken(1.5) - .desaturate(1.5) - .hex(); + .darken(1.5) + .desaturate(1.5) + .hex(); }, - isTextLight(){ + isTextLight() { return isDarkColor(this.barBackgroundColor); /* Change color at the halfway mark const fraction = this.value / this.maxValue; @@ -173,7 +182,7 @@ export default { cancelEdit() { this.editing = false; }, - changeIncrementMenu(e){ + changeIncrementMenu(e) { this.$emit('change', e); this.editing = false; } @@ -193,70 +202,85 @@ export default { diff --git a/app/imports/ui/properties/components/attributes/HealthBarCard.vue b/app/imports/ui/properties/components/attributes/HealthBarCard.vue index f05c7978..c3409ee0 100644 --- a/app/imports/ui/properties/components/attributes/HealthBarCard.vue +++ b/app/imports/ui/properties/components/attributes/HealthBarCard.vue @@ -17,17 +17,17 @@ diff --git a/app/imports/ui/properties/components/attributes/HealthBarCardContainer.vue b/app/imports/ui/properties/components/attributes/HealthBarCardContainer.vue index 9b8440e7..639beee0 100644 --- a/app/imports/ui/properties/components/attributes/HealthBarCardContainer.vue +++ b/app/imports/ui/properties/components/attributes/HealthBarCardContainer.vue @@ -12,60 +12,60 @@ diff --git a/app/imports/ui/properties/components/attributes/HitDiceListTile.vue b/app/imports/ui/properties/components/attributes/HitDiceListTile.vue index e26f1abd..7cfff667 100644 --- a/app/imports/ui/properties/components/attributes/HitDiceListTile.vue +++ b/app/imports/ui/properties/components/attributes/HitDiceListTile.vue @@ -31,9 +31,7 @@ - +
{{ model.value }}
@@ -63,60 +61,71 @@ export default { inject: { context: { default: {} } }, - props: { + props: { model: { type: Object, required: true, } - }, - data(){ return{ - hover: false, - }}, + }, + data() { + return { + hover: false, + } + }, computed: { - signedConMod(){ + signedConMod() { return numberToSignedString(this.model.constitutionMod); }, }, - methods: { - click(e){ - this.$emit('click', e); - }, - increment(value){ - this.$emit('change', {type: 'increment', value}) - }, - }, + methods: { + click(e) { + this.$emit('click', e); + }, + increment(value) { + this.$emit('change', { type: 'increment', value }) + }, + }, }; diff --git a/app/imports/ui/properties/components/attributes/ResourceCard.vue b/app/imports/ui/properties/components/attributes/ResourceCard.vue index 44390756..c5759892 100644 --- a/app/imports/ui/properties/components/attributes/ResourceCard.vue +++ b/app/imports/ui/properties/components/attributes/ResourceCard.vue @@ -22,9 +22,7 @@ mdi-chevron-down
-
+
{{ model.value }}
@@ -53,55 +51,64 @@ diff --git a/app/imports/ui/properties/components/attributes/SpellSlotListTile.vue b/app/imports/ui/properties/components/attributes/SpellSlotListTile.vue index 02afc030..584fc765 100644 --- a/app/imports/ui/properties/components/attributes/SpellSlotListTile.vue +++ b/app/imports/ui/properties/components/attributes/SpellSlotListTile.vue @@ -7,9 +7,7 @@ v-on="hasClickListener ? {click} : {}" > - +
import numberToSignedString from '/imports/ui/utility/numberToSignedString.js'; export default { - props: { - model: { + props: { + model: { type: Object, required: true, }, dark: Boolean, hideCastButton: Boolean, disabled: Boolean, - }, - computed: { - hasClickListener(){ + }, + computed: { + hasClickListener() { return this.$listeners && !!this.$listeners.click; }, - }, - methods: { - signed: numberToSignedString, - click(e){ - this.$emit('click', e); - }, - }, + }, + methods: { + signed: numberToSignedString, + click(e) { + this.$emit('click', e); + }, + }, }; diff --git a/app/imports/ui/properties/components/inventory/ContainerCard.vue b/app/imports/ui/properties/components/inventory/ContainerCard.vue index 6d862b60..0843dcfd 100644 --- a/app/imports/ui/properties/components/inventory/ContainerCard.vue +++ b/app/imports/ui/properties/components/inventory/ContainerCard.vue @@ -30,9 +30,7 @@ > $vuetify.icons.two_coins - + @@ -52,57 +50,57 @@ import CoinValue from '/imports/ui/components/CoinValue.vue'; import stripFloatingPointOddities from '/imports/api/engine/computation/utility/stripFloatingPointOddities.js'; export default { - components: { - ToolbarCard, + components: { + ToolbarCard, ItemList, CoinValue, - }, - props: { - model: { + }, + props: { + model: { type: Object, required: true, }, - }, + }, computed: { - weight(){ + weight() { const contentWeight = this.model.contentsWeightless ? 0 : this.model.contentsWeight || 0; const ownWeight = this.model.weight || 0; return stripFloatingPointOddities(contentWeight + ownWeight); }, - value(){ + value() { const contentValue = this.model.contentsValue || 0; const ownValue = this.model.value || 0; return contentValue + ownValue; } }, - methods: { - clickContainer(_id){ - this.$store.commit('pushDialogStack', { - component: 'creature-property-dialog', - elementId: `${_id}`, - data: {_id}, - }); - }, - clickProperty(_id){ - this.$store.commit('pushDialogStack', { - component: 'creature-property-dialog', - elementId: `tree-node-${_id}`, - data: {_id}, - }); - }, - }, + methods: { + clickContainer(_id) { + this.$store.commit('pushDialogStack', { + component: 'creature-property-dialog', + elementId: `${_id}`, + data: { _id }, + }); + }, + clickProperty(_id) { + this.$store.commit('pushDialogStack', { + component: 'creature-property-dialog', + elementId: `tree-node-${_id}`, + data: { _id }, + }); + }, + }, meteor: { - items(){ + items() { return CreatureProperties.find({ 'parent.id': this.model._id, - type: {$in: ['item', 'container']}, - removed: {$ne: true}, - equipped: {$ne: true}, - deactivatedByAncestor: {$ne: true}, + type: { $in: ['item', 'container'] }, + removed: { $ne: true }, + equipped: { $ne: true }, + deactivatedByAncestor: { $ne: true }, }, { - sort: {order: 1}, + sort: { order: 1 }, }); }, } @@ -110,4 +108,5 @@ export default { diff --git a/app/imports/ui/properties/components/inventory/ItemList.vue b/app/imports/ui/properties/components/inventory/ItemList.vue index 6696ee12..28a07995 100644 --- a/app/imports/ui/properties/components/inventory/ItemList.vue +++ b/app/imports/ui/properties/components/inventory/ItemList.vue @@ -52,40 +52,42 @@ export default { preparingSpells: Boolean, equipment: Boolean, }, - data(){ return { - dataItems: [], - }}, + data() { + return { + dataItems: [], + } + }, computed: { - levels(){ + levels() { let levels = new Set(); this.items.forEach(item => levels.add(item.level)); return levels; }, }, watch: { - items(value){ + items(value) { this.dataItems = value; } }, - mounted(){ + mounted() { this.dataItems = this.items; }, methods: { - clickProperty(_id){ - this.$store.commit('pushDialogStack', { - component: 'creature-property-dialog', - elementId: _id, - data: {_id}, - }); - }, - change({added, moved}){ + clickProperty(_id) { + this.$store.commit('pushDialogStack', { + component: 'creature-property-dialog', + elementId: _id, + data: { _id }, + }); + }, + change({ added, moved }) { let event = added || moved; - if (event){ + if (event) { // If this item is now adjacent to another, set the order accordingly let order; let before = this.dataItems[event.newIndex - 1]; let after = this.dataItems[event.newIndex + 1]; - if (before && before._id){ + if (before && before._id) { order = before.order + 0.5; } else if (after && after._id) { order = after.order - 0.5; @@ -101,7 +103,7 @@ export default { parentRef: this.parentRef, order, }); - if (doc.type === 'item' && doc.equipped != this.equipment){ + if (doc.type === 'item' && doc.equipped != this.equipment) { updateCreatureProperty.call({ _id: doc._id, path: ['equipped'], @@ -111,6 +113,6 @@ export default { } setTimeout(() => this.dataItems = this.items, 0); }, - } + } } diff --git a/app/imports/ui/properties/components/inventory/ItemListTile.vue b/app/imports/ui/properties/components/inventory/ItemListTile.vue index d6e64249..5dfa1b8e 100644 --- a/app/imports/ui/properties/components/inventory/ItemListTile.vue +++ b/app/imports/ui/properties/components/inventory/ItemListTile.vue @@ -49,10 +49,10 @@ import treeNodeViewMixin from '/imports/ui/properties/treeNodeViews/treeNodeView import PROPERTIES from '/imports/constants/PROPERTIES.js'; import adjustQuantity from '/imports/api/creature/creatureProperties/methods/adjustQuantity.js'; import IncrementButton from '/imports/ui/components/IncrementButton.vue'; -import {snackbar} from '/imports/ui/components/snackbars/SnackbarQueue.js'; +import { snackbar } from '/imports/ui/components/snackbars/SnackbarQueue.js'; export default { - components:{ + components: { IncrementButton, }, mixins: [treeNodeViewMixin], @@ -62,20 +62,22 @@ export default { props: { preparingSpells: Boolean, }, - data(){return { - incrementLoading: false, - }}, + data() { + return { + incrementLoading: false, + } + }, computed: { - hasClickListener(){ + hasClickListener() { return this.$listeners && !!this.$listeners.click; }, - title(){ + title() { let model = this.model; if (!model) return; - if (model.quantity !== 1){ - if (model.plural){ + if (model.quantity !== 1) { + if (model.plural) { return `${model.quantity} ${model.plural}`; - } else if (model.name){ + } else if (model.name) { return `${model.quantity} ${model.name}`; } } else if (model.name) { @@ -86,10 +88,10 @@ export default { } }, methods: { - click(e){ - this.$emit('click', e); - }, - changeQuantity({type, value}) { + click(e) { + this.$emit('click', e); + }, + changeQuantity({ type, value }) { this.incrementLoading = true; adjustQuantity.call({ _id: this.model._id, @@ -97,8 +99,8 @@ export default { value: value }, error => { this.incrementLoading = false; - if (error){ - snackbar({text: error.reason}); + if (error) { + snackbar({ text: error.reason }); console.error(error); } }); @@ -111,6 +113,7 @@ export default { .item-avatar { min-width: 32px; } + .item { background-color: inherit; } diff --git a/app/imports/ui/properties/components/persona/NoteCard.vue b/app/imports/ui/properties/components/persona/NoteCard.vue index f3be2a0e..d07afa1e 100644 --- a/app/imports/ui/properties/components/persona/NoteCard.vue +++ b/app/imports/ui/properties/components/persona/NoteCard.vue @@ -31,10 +31,10 @@ import isDarkColor from '/imports/ui/utility/isDarkColor.js'; import CardHighlight from '/imports/ui/components/CardHighlight.vue'; export default { - components: { - PropertyDescription, + components: { + PropertyDescription, CardHighlight, - }, + }, inject: { theme: { default: { @@ -42,31 +42,34 @@ export default { }, }, }, - props: { - model: { + props: { + model: { type: Object, required: true, }, - }, - data(){ return{ - hover: false, - }}, + }, + data() { + return { + hover: false, + } + }, computed: { - isDark(){ + isDark() { return isDarkColor(this.model.color); }, }, - methods: { - clickProperty(_id){ - this.$store.commit('pushDialogStack', { - component: 'creature-property-dialog', - elementId: `${_id}`, - data: {_id}, - }); - }, - }, + methods: { + clickProperty(_id) { + this.$store.commit('pushDialogStack', { + component: 'creature-property-dialog', + elementId: `${_id}`, + data: { _id }, + }); + }, + }, }; diff --git a/app/imports/ui/properties/components/skills/SkillListTile.vue b/app/imports/ui/properties/components/skills/SkillListTile.vue index e24ece45..bbcb773e 100644 --- a/app/imports/ui/properties/components/skills/SkillListTile.vue +++ b/app/imports/ui/properties/components/skills/SkillListTile.vue @@ -62,7 +62,7 @@ import numberToSignedString from '/imports/ui/utility/numberToSignedString.js'; import ProficiencyIcon from '/imports/ui/properties/shared/ProficiencyIcon.vue'; import RollPopup from '/imports/ui/components/RollPopup.vue'; import doCheck from '/imports/api/engine/actions/doCheck.js'; -import {snackbar} from '/imports/ui/components/snackbars/SnackbarQueue.js'; +import { snackbar } from '/imports/ui/components/snackbars/SnackbarQueue.js'; export default { components: { @@ -74,37 +74,39 @@ export default { default: {}, }, }, - props: { + props: { model: { type: Object, required: true, }, hideModifier: Boolean, - }, - data(){return { - checkLoading: false, - }}, - computed: { - displayedModifier(){ - let mod = this.model.value; - if (this.model.fail){ - return 'fail'; - } else { - return numberToSignedString(mod); - } - }, - hasClickListener(){ + }, + data() { + return { + checkLoading: false, + } + }, + computed: { + displayedModifier() { + let mod = this.model.value; + if (this.model.fail) { + return 'fail'; + } else { + return numberToSignedString(mod); + } + }, + hasClickListener() { return this.$listeners && this.$listeners.click - }, - passiveScore(){ + }, + passiveScore() { return 10 + this.model.value + this.model.passiveBonus; } - }, - methods: { - click(e){ - this.$emit('click', e); - }, - check({advantage}){ + }, + methods: { + click(e) { + this.$emit('click', e); + }, + check({ advantage }) { this.checkLoading = true; doCheck.call({ propId: this.model._id, @@ -113,24 +115,26 @@ export default { }, }, error => { this.checkLoading = false; - if (error){ + if (error) { console.error(error); - snackbar({text: error.reason}); + snackbar({ text: error.reason }); } }); }, - } + } } diff --git a/app/imports/ui/properties/components/spells/CastSpellWithSlotDialog.vue b/app/imports/ui/properties/components/spells/CastSpellWithSlotDialog.vue index c3ec9a92..cf9cf288 100644 --- a/app/imports/ui/properties/components/spells/CastSpellWithSlotDialog.vue +++ b/app/imports/ui/properties/components/spells/CastSpellWithSlotDialog.vue @@ -184,10 +184,10 @@ import { find } from 'lodash'; const slotFilter = { type: 'attribute', attributeType: 'spellSlot', - removed: {$ne: true}, - inactive: {$ne: true}, - overridden: {$ne: true}, - 'spellSlotLevel.value': {$gte: 1}, + removed: { $ne: true }, + inactive: { $ne: true }, + overridden: { $ne: true }, + 'spellSlotLevel.value': { $gte: 1 }, }; export default { @@ -212,36 +212,38 @@ export default { default: undefined, }, }, - data(){ return { - searchString: undefined, - selectedSlotId: this.slotId, - selectedSpellId: this.spellId, - selectedSlot: undefined, - selectedSpell: undefined, - searchValue: undefined, - searchError: undefined, - filterMenuOpen: false, - booleanFilters: { - verbal: {name: 'Verbal', enabled: false, value: false}, - somatic: {name: 'Somatic', enabled: false, value: false}, - material: {name: 'Material', enabled: false, value: false}, - concentration: {name: 'Concentration', enabled: false, value: false}, - ritual: {name: 'Ritual', enabled: false, value: false}, - }, - }}, + data() { + return { + searchString: undefined, + selectedSlotId: this.slotId, + selectedSpellId: this.spellId, + selectedSlot: undefined, + selectedSpell: undefined, + searchValue: undefined, + searchError: undefined, + filterMenuOpen: false, + booleanFilters: { + verbal: { name: 'Verbal', enabled: false, value: false }, + somatic: { name: 'Somatic', enabled: false, value: false }, + material: { name: 'Material', enabled: false, value: false }, + concentration: { name: 'Concentration', enabled: false, value: false }, + ritual: { name: 'Ritual', enabled: false, value: false }, + }, + } + }, computed: { - computedSpells(){ + computedSpells() { return spellsWithSubheaders(this.spells); }, - canCast(){ + canCast() { if (!this.selectedSpell || !this.selectedSlotId) return false; return this.canCastSpellWithSlot( this.selectedSpell, this.selectedSlotId, this.selectedSlot ); }, - filtersApplied(){ - for (let key in this.booleanFilters){ - if (this.booleanFilters[key].enabled){ + filtersApplied() { + for (let key in this.booleanFilters) { + if (this.booleanFilters[key].enabled) { return true; } } @@ -250,15 +252,15 @@ export default { }, watch: { selectedSpellId: { - handler(spellId){ + handler(spellId) { this.selectedSpell = CreatureProperties.findOne(spellId) }, immediate: true }, selectedSpell: { - handler(spell){ + handler(spell) { if (!spell) return; - if(spell.level === 0 || spell.castWithoutSpellSlots){ + if (spell.level === 0 || spell.castWithoutSpellSlots) { this.selectedSlotId = 'no-slot'; } else if ( !this.selectedSlotId || @@ -270,13 +272,13 @@ export default { 'ancestors.id': this.creatureId, ...slotFilter }, { - sort: {'spellSlotLevel.value': 1, order: 1}, + sort: { 'spellSlotLevel.value': 1, order: 1 }, }).fetch(), slot => { return this.canCastSpellWithSlot(spell, slot._id, slot) } ); - if (newSlot){ + if (newSlot) { this.selectedSlotId = newSlot._id; } } @@ -284,45 +286,45 @@ export default { immediate: true, }, selectedSlotId: { - handler(slotId){ + handler(slotId) { this.selectedSlot = CreatureProperties.findOne(slotId); }, immediate: true }, - selectedSlot:{ - handler(slot){ + selectedSlot: { + handler(slot) { if (!slot) return; if (!this.selectedSpell) return; - if(this.selectedSpell.level > slot.spellSlotLevel.value){ + if (this.selectedSpell.level > slot.spellSlotLevel.value) { this.selectedSpellId = undefined; } }, immediate: true, }, }, - mounted(){ - if (this.selectedSpellId){ - this.$vuetify.goTo('.spell.v-list-item--active', {container: '.right'}); + mounted() { + if (this.selectedSpellId) { + this.$vuetify.goTo('.spell.v-list-item--active', { container: '.right' }); } }, methods: { - clearBooleanFilters(){ - for (let key in this.booleanFilters){ + clearBooleanFilters() { + for (let key in this.booleanFilters) { this.booleanFilters[key].enabled = false; } }, - spellDialog(_id){ + spellDialog(_id) { this.$store.commit('pushDialogStack', { - component: 'creature-property-dialog', - elementId: `spell-info-btn-${_id}`, - data: {_id}, - }); + component: 'creature-property-dialog', + elementId: `spell-info-btn-${_id}`, + data: { _id }, + }); }, - searchChanged(val, ack){ + searchChanged(val, ack) { this.searchValue = val; setTimeout(ack, 200); }, - canCastSpellWithSlot(spell, slotId, slot){ + canCastSpellWithSlot(spell, slotId, slot) { if (slot && !slot.value) return false; if (!spell) return true; if (!slotId) return true; @@ -341,7 +343,7 @@ export default { ) ) }, - cast({advantage}){ + cast({ advantage }) { let selectedSlotId = this.selectedSlotId; if (selectedSlotId === 'no-slot') selectedSlotId = undefined; this.$store.dispatch('popDialogStack', { @@ -352,45 +354,45 @@ export default { } }, meteor: { - spells(){ + spells() { let filter = { 'ancestors.id': this.creatureId, - removed: {$ne: true}, - inactive: {$ne: true}, + removed: { $ne: true }, + inactive: { $ne: true }, $or: [ - {prepared: true}, - {alwaysPrepared: true}, + { prepared: true }, + { alwaysPrepared: true }, ], }; // Apply the filters from the filter menu - for (let key in this.booleanFilters){ - if (this.booleanFilters[key].enabled){ + for (let key in this.booleanFilters) { + if (this.booleanFilters[key].enabled) { let value = this.booleanFilters[key].value; - if (key === 'material'){ - filter[key] = {$exists: this.booleanFilters[key].value}; + if (key === 'material') { + filter[key] = { $exists: this.booleanFilters[key].value }; } else { - filter[key] = value ? true: {$ne: true}; + filter[key] = value ? true : { $ne: true }; } } } // Apply the search string to the name field - if (this.searchValue){ + if (this.searchValue) { filter.name = { $regex: this.searchValue.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&'), $options: 'i' }; } return CreatureProperties.find(filter, { - sort: {order: 1} + sort: { order: 1 } }); }, - spellSlots(){ + spellSlots() { return CreatureProperties.find({ 'ancestors.id': this.creatureId, ...slotFilter }, { - sort: {'spellSlotLevel.value': 1, order: 1}, + sort: { 'spellSlotLevel.value': 1, order: 1 }, }); }, }, @@ -398,10 +400,11 @@ export default { diff --git a/app/imports/ui/properties/components/spells/SpellList.vue b/app/imports/ui/properties/components/spells/SpellList.vue index 3d1d6333..50f70bfc 100644 --- a/app/imports/ui/properties/components/spells/SpellList.vue +++ b/app/imports/ui/properties/components/spells/SpellList.vue @@ -62,48 +62,50 @@ export default { }, preparingSpells: Boolean, }, - data(){ return { - dataSpells: [], - }}, + data() { + return { + dataSpells: [], + } + }, computed: { - levels(){ + levels() { let levels = new Set(); this.spells.forEach(spell => levels.add(spell.level)); return levels; }, computedSpells: { - get(){ + get() { return spellsWithSubheaders(this.dataSpells); }, - set(value){ + set(value) { this.dataSpells = value; }, } }, watch: { - spells(value){ + spells(value) { this.dataSpells = spellsWithSubheaders(value); } }, - mounted(){ + mounted() { this.dataSpells = spellsWithSubheaders(this.spells); }, methods: { - clickProperty(_id){ - this.$store.commit('pushDialogStack', { - component: 'creature-property-dialog', - elementId: `spell-list-tile-${_id}`, - data: {_id}, - }); - }, - change({added, moved}){ + clickProperty(_id) { + this.$store.commit('pushDialogStack', { + component: 'creature-property-dialog', + elementId: `spell-list-tile-${_id}`, + data: { _id }, + }); + }, + change({ added, moved }) { let event = added || moved; - if (event){ + if (event) { // If this spell is now adjacent to another, set the order accordingly let order; let before = this.dataSpells[event.newIndex - 1]; let after = this.dataSpells[event.newIndex + 1]; - if (before && before._id){ + if (before && before._id) { order = before.order + 0.5; } else if (after && after._id) { order = after.order - 0.5; @@ -121,9 +123,10 @@ export default { }); } }, - } + } } diff --git a/app/imports/ui/properties/components/spells/SpellListCard.vue b/app/imports/ui/properties/components/spells/SpellListCard.vue index c45c7cc3..4dd5edc4 100644 --- a/app/imports/ui/properties/components/spells/SpellListCard.vue +++ b/app/imports/ui/properties/components/spells/SpellListCard.vue @@ -63,32 +63,34 @@ import SpellList from '/imports/ui/properties/components/spells/SpellList.vue'; import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js'; export default { - components: { - ToolbarCard, + components: { + ToolbarCard, SpellList, - }, - props: { - model: { + }, + props: { + model: { type: Object, required: true, }, - organize: Boolean, - }, - data(){ return { - preparingSpells: false, - }}, + organize: Boolean, + }, + data() { + return { + preparingSpells: false, + } + }, meteor: { - spells(){ + spells() { let filter = { 'ancestors.id': this.model._id, type: 'spell', - removed: {$ne: true}, + removed: { $ne: true }, }; - if (this.preparingSpells){ - filter.deactivatedByAncestor = {$ne: true}; - filter.deactivatedByToggle = {$ne: true}; + if (this.preparingSpells) { + filter.deactivatedByAncestor = { $ne: true }; + filter.deactivatedByToggle = { $ne: true }; } else { - filter.inactive = {$ne: true}; + filter.inactive = { $ne: true }; } return CreatureProperties.find(filter, { sort: { @@ -97,35 +99,36 @@ export default { } }); }, - numPrepared(){ + numPrepared() { return CreatureProperties.find({ 'ancestors.id': this.model._id, type: 'spell', - removed: {$ne: true}, + removed: { $ne: true }, prepared: true, - alwaysPrepared: {$ne: true}, - deactivatedByAncestor: {$ne: true}, - deactivatedByToggle: {$ne: true}, + alwaysPrepared: { $ne: true }, + deactivatedByAncestor: { $ne: true }, + deactivatedByToggle: { $ne: true }, }).count(); }, - preparedError(){ + preparedError() { if (!this.model.maxPrepared) return; let numPrepared = this.numPrepared; let maxPrepared = this.model.maxPrepared.value || 0; return numPrepared !== maxPrepared }, }, - methods: { - clickSpellList(_id){ - this.$store.commit('pushDialogStack', { - component: 'creature-property-dialog', - elementId: `${_id}`, - data: {_id}, - }); - }, - } + methods: { + clickSpellList(_id) { + this.$store.commit('pushDialogStack', { + component: 'creature-property-dialog', + elementId: `${_id}`, + data: { _id }, + }); + }, + } }; diff --git a/app/imports/ui/properties/components/spells/SpellListTile.vue b/app/imports/ui/properties/components/spells/SpellListTile.vue index 1e6af252..20122630 100644 --- a/app/imports/ui/properties/components/spells/SpellListTile.vue +++ b/app/imports/ui/properties/components/spells/SpellListTile.vue @@ -67,10 +67,10 @@ export default { disabled: Boolean, }, computed: { - hasClickListener(){ + hasClickListener() { return this.$listeners && !!this.$listeners.click; }, - spellComponents(){ + spellComponents() { let components = []; if (this.model.ritual) components.push('R'); if (this.model.concentration) components.push('C'); @@ -81,10 +81,10 @@ export default { }, }, methods: { - click(e){ - this.$emit('click', e); - }, - setPrepared(val, ack){ + click(e) { + this.$emit('click', e); + }, + setPrepared(val, ack) { updateCreatureProperty.call({ _id: this.model._id, path: ['prepared'], @@ -99,13 +99,17 @@ export default { .spell-avatar { min-width: 32px; } + .spell { background-color: inherit; } -.primary--text .v-icon, .primary--text .v-list__tile__sub-title { + +.primary--text .v-icon, +.primary--text .v-list__tile__sub-title { color: #b71c1c } -.theme--light.info-icon{ - color: rgba(0,0,0,.54) !important; + +.theme--light.info-icon { + color: rgba(0, 0, 0, .54) !important; } diff --git a/app/imports/ui/properties/forms/AdjustmentForm.vue b/app/imports/ui/properties/forms/AdjustmentForm.vue index 94d419c4..31712722 100644 --- a/app/imports/ui/properties/forms/AdjustmentForm.vue +++ b/app/imports/ui/properties/forms/AdjustmentForm.vue @@ -81,60 +81,63 @@ import propertyFormMixin from '/imports/ui/properties/forms/shared/propertyFormM export default { mixins: [propertyFormMixin, attributeListMixin], - props: { - parentTarget: { - type: String, + props: { + parentTarget: { + type: String, default: undefined, - }, - }, - data(){return { - adjustmentOps: [ - {text: 'Damage', value: 'increment'}, - {text: 'Set', value: 'set'}, - ], - damageHint: 'The amount of damage to apply to the selected stat, can be a calculation or roll. Negative values will restore the selected from previous damage. If the operation is set, this is the final value of the stat instead.', - setHint: 'The value of the stat after applying this adjustment. The stat\'s value can\'t exceed its total', - }}, - computed: { - targetOptions(){ - if (this.parentTarget === 'singleTarget') { - return [ - { - text: 'Self', - value: 'self', - }, { - text: 'Target', - value: 'every', - }, - ]; - } else { - return [ - { - text: 'Self', - value: 'self', - }, { - text: 'Target', - value: 'target', - }, - ]; - } - }, - targetOptionHint(){ - let hints = { - self: 'The damage will be applied to the character\'s own attribute when taking the action', - target: 'The damage will be applied to the target of the action', - each: 'The damage will be rolled separately for each of the targets of the action', - every: 'The damage will be rolled once and applied to each of the targets of the action', - }; - if (this.parentTarget === 'singleTarget'){ - hints.each = hints.target; - hints.every = hints.target; - } - return hints[this.model.target]; - } - }, + }, + }, + data() { + return { + adjustmentOps: [ + { text: 'Damage', value: 'increment' }, + { text: 'Set', value: 'set' }, + ], + damageHint: 'The amount of damage to apply to the selected stat, can be a calculation or roll. Negative values will restore the selected from previous damage. If the operation is set, this is the final value of the stat instead.', + setHint: 'The value of the stat after applying this adjustment. The stat\'s value can\'t exceed its total', + } + }, + computed: { + targetOptions() { + if (this.parentTarget === 'singleTarget') { + return [ + { + text: 'Self', + value: 'self', + }, { + text: 'Target', + value: 'every', + }, + ]; + } else { + return [ + { + text: 'Self', + value: 'self', + }, { + text: 'Target', + value: 'target', + }, + ]; + } + }, + targetOptionHint() { + let hints = { + self: 'The damage will be applied to the character\'s own attribute when taking the action', + target: 'The damage will be applied to the target of the action', + each: 'The damage will be rolled separately for each of the targets of the action', + every: 'The damage will be rolled once and applied to each of the targets of the action', + }; + if (this.parentTarget === 'singleTarget') { + hints.each = hints.target; + hints.every = hints.target; + } + return hints[this.model.target]; + } + }, } diff --git a/app/imports/ui/properties/forms/AttributeForm.vue b/app/imports/ui/properties/forms/AttributeForm.vue index 0b23d1df..42814d86 100644 --- a/app/imports/ui/properties/forms/AttributeForm.vue +++ b/app/imports/ui/properties/forms/AttributeForm.vue @@ -140,9 +140,7 @@ - + diff --git a/app/imports/ui/properties/forms/AttributesConsumedListForm.vue b/app/imports/ui/properties/forms/AttributesConsumedListForm.vue index b0a847de..97ff2d81 100644 --- a/app/imports/ui/properties/forms/AttributesConsumedListForm.vue +++ b/app/imports/ui/properties/forms/AttributesConsumedListForm.vue @@ -29,13 +29,13 @@ diff --git a/app/imports/ui/properties/forms/BranchForm.vue b/app/imports/ui/properties/forms/BranchForm.vue index d1aa6ec9..1ad49fb4 100644 --- a/app/imports/ui/properties/forms/BranchForm.vue +++ b/app/imports/ui/properties/forms/BranchForm.vue @@ -54,45 +54,48 @@ diff --git a/app/imports/ui/properties/forms/BuffForm.vue b/app/imports/ui/properties/forms/BuffForm.vue index a7c3b0dd..ade759e4 100644 --- a/app/imports/ui/properties/forms/BuffForm.vue +++ b/app/imports/ui/properties/forms/BuffForm.vue @@ -44,9 +44,7 @@ > - + diff --git a/app/imports/ui/properties/forms/ClassForm.vue b/app/imports/ui/properties/forms/ClassForm.vue index d301b750..f4bd101e 100644 --- a/app/imports/ui/properties/forms/ClassForm.vue +++ b/app/imports/ui/properties/forms/ClassForm.vue @@ -1,8 +1,6 @@ diff --git a/app/imports/ui/properties/forms/ContainerForm.vue b/app/imports/ui/properties/forms/ContainerForm.vue index 78990b4d..58f127e0 100644 --- a/app/imports/ui/properties/forms/ContainerForm.vue +++ b/app/imports/ui/properties/forms/ContainerForm.vue @@ -63,9 +63,7 @@ - + diff --git a/app/imports/ui/properties/forms/DamageForm.vue b/app/imports/ui/properties/forms/DamageForm.vue index 98d92e3d..1faa66f3 100644 --- a/app/imports/ui/properties/forms/DamageForm.vue +++ b/app/imports/ui/properties/forms/DamageForm.vue @@ -76,45 +76,48 @@ import VARIABLE_NAME_REGEX from '/imports/constants/VARIABLE_NAME_REGEX.js'; export default { mixins: [propertyFormMixin], - props: { - parentTarget: { - type: String, + props: { + parentTarget: { + type: String, default: undefined, - }, - }, - data(){return{ - DAMAGE_TYPES, - damageTypeRules: [ - value => { - if (!value) return 'Damage type is required'; - if (!VARIABLE_NAME_REGEX.test(value)){ - return `${value} is not a valid damage name` + }, + }, + data() { + return { + DAMAGE_TYPES, + damageTypeRules: [ + value => { + if (!value) return 'Damage type is required'; + if (!VARIABLE_NAME_REGEX.test(value)) { + return `${value} is not a valid damage name` + } } - } - ], - }}, - computed: { - targetOptions(){ - return [ - { - text: 'Self', - value: 'self', - }, { - text: 'Target', - value: 'target', - }, - ]; - }, - targetOptionHint(){ - let hints = { - self: 'The damage will be applied to the character taking the action', - target: 'The damage will be applied to the target of the action', - }; - return hints[this.model.target]; - } - }, + ], + } + }, + computed: { + targetOptions() { + return [ + { + text: 'Self', + value: 'self', + }, { + text: 'Target', + value: 'target', + }, + ]; + }, + targetOptionHint() { + let hints = { + self: 'The damage will be applied to the character taking the action', + target: 'The damage will be applied to the target of the action', + }; + return hints[this.model.target]; + } + }, } diff --git a/app/imports/ui/properties/forms/DamageMultiplierForm.vue b/app/imports/ui/properties/forms/DamageMultiplierForm.vue index 7705196b..551f1514 100644 --- a/app/imports/ui/properties/forms/DamageMultiplierForm.vue +++ b/app/imports/ui/properties/forms/DamageMultiplierForm.vue @@ -52,9 +52,7 @@ > - + diff --git a/app/imports/ui/properties/forms/EffectForm.vue b/app/imports/ui/properties/forms/EffectForm.vue index 9ef23c9a..694db21d 100644 --- a/app/imports/ui/properties/forms/EffectForm.vue +++ b/app/imports/ui/properties/forms/EffectForm.vue @@ -29,9 +29,7 @@ slot="item" slot-scope="item" > - + {{ getEffectIcon(item.item.value, 1) }} {{ item.item.text }} @@ -157,9 +155,7 @@ - + diff --git a/app/imports/ui/properties/forms/FeatureForm.vue b/app/imports/ui/properties/forms/FeatureForm.vue index cb900962..c3f85735 100644 --- a/app/imports/ui/properties/forms/FeatureForm.vue +++ b/app/imports/ui/properties/forms/FeatureForm.vue @@ -46,49 +46,52 @@ diff --git a/app/imports/ui/properties/forms/FolderForm.vue b/app/imports/ui/properties/forms/FolderForm.vue index 84f29b84..77c96aec 100644 --- a/app/imports/ui/properties/forms/FolderForm.vue +++ b/app/imports/ui/properties/forms/FolderForm.vue @@ -17,9 +17,7 @@ - + diff --git a/app/imports/ui/properties/forms/ItemForm.vue b/app/imports/ui/properties/forms/ItemForm.vue index ced109d9..d8097daa 100644 --- a/app/imports/ui/properties/forms/ItemForm.vue +++ b/app/imports/ui/properties/forms/ItemForm.vue @@ -158,9 +158,9 @@ import FormSection from '/imports/ui/properties/forms/shared/FormSection.vue'; import propertyFormMixin from '/imports/ui/properties/forms/shared/propertyFormMixin.js'; export default { - components: { - FormSection, - }, + components: { + FormSection, + }, mixins: [propertyFormMixin], - } +} diff --git a/app/imports/ui/properties/forms/ItemsConsumedListForm.vue b/app/imports/ui/properties/forms/ItemsConsumedListForm.vue index 80069155..97531ec6 100644 --- a/app/imports/ui/properties/forms/ItemsConsumedListForm.vue +++ b/app/imports/ui/properties/forms/ItemsConsumedListForm.vue @@ -29,13 +29,13 @@ diff --git a/app/imports/ui/properties/forms/ProficiencyForm.vue b/app/imports/ui/properties/forms/ProficiencyForm.vue index 87b35b4a..4fcf2825 100644 --- a/app/imports/ui/properties/forms/ProficiencyForm.vue +++ b/app/imports/ui/properties/forms/ProficiencyForm.vue @@ -48,17 +48,18 @@ diff --git a/app/imports/ui/properties/forms/ResourcesForm.vue b/app/imports/ui/properties/forms/ResourcesForm.vue index 5c3e92d9..e3fc0dcb 100644 --- a/app/imports/ui/properties/forms/ResourcesForm.vue +++ b/app/imports/ui/properties/forms/ResourcesForm.vue @@ -55,54 +55,57 @@ diff --git a/app/imports/ui/properties/forms/RollForm.vue b/app/imports/ui/properties/forms/RollForm.vue index 26432790..c06ceaa6 100644 --- a/app/imports/ui/properties/forms/RollForm.vue +++ b/app/imports/ui/properties/forms/RollForm.vue @@ -41,7 +41,7 @@ > - + diff --git a/app/imports/ui/properties/forms/SavingThrowForm.vue b/app/imports/ui/properties/forms/SavingThrowForm.vue index 1fc03b68..8e0c5540 100644 --- a/app/imports/ui/properties/forms/SavingThrowForm.vue +++ b/app/imports/ui/properties/forms/SavingThrowForm.vue @@ -88,24 +88,24 @@ import propertyFormMixin from '/imports/ui/properties/forms/shared/propertyFormM export default { mixins: [saveListMixin, propertyFormMixin], computed: { - targetOptions(){ - return [ - { - text: 'Self', - value: 'self', - }, { - text: 'Target', - value: 'target', - }, - ]; - }, - targetOptionHint(){ - let hints = { - self: 'The save will be applied to the character taking the action', - target: 'The save will be applied to the targets of the action', - }; - return hints[this.model.target]; - } - }, + targetOptions() { + return [ + { + text: 'Self', + value: 'self', + }, { + text: 'Target', + value: 'target', + }, + ]; + }, + targetOptionHint() { + let hints = { + self: 'The save will be applied to the character taking the action', + target: 'The save will be applied to the targets of the action', + }; + return hints[this.model.target]; + } + }, }; diff --git a/app/imports/ui/properties/forms/SkillForm.vue b/app/imports/ui/properties/forms/SkillForm.vue index 2854013e..91d30970 100644 --- a/app/imports/ui/properties/forms/SkillForm.vue +++ b/app/imports/ui/properties/forms/SkillForm.vue @@ -53,9 +53,7 @@ - + diff --git a/app/imports/ui/properties/forms/SlotFillerForm.vue b/app/imports/ui/properties/forms/SlotFillerForm.vue index 64ce1c51..28890a30 100644 --- a/app/imports/ui/properties/forms/SlotFillerForm.vue +++ b/app/imports/ui/properties/forms/SlotFillerForm.vue @@ -95,20 +95,20 @@ diff --git a/app/imports/ui/properties/forms/SlotForm.vue b/app/imports/ui/properties/forms/SlotForm.vue index 6c001c2c..f2dc7c9b 100644 --- a/app/imports/ui/properties/forms/SlotForm.vue +++ b/app/imports/ui/properties/forms/SlotForm.vue @@ -72,13 +72,11 @@ @change="change('slotTags', ...arguments)" /> - +
- +
diff --git a/app/imports/ui/properties/forms/SpellForm.vue b/app/imports/ui/properties/forms/SpellForm.vue index db36d5b0..4544857c 100644 --- a/app/imports/ui/properties/forms/SpellForm.vue +++ b/app/imports/ui/properties/forms/SpellForm.vue @@ -247,9 +247,7 @@ /> - + diff --git a/app/imports/ui/properties/forms/ToggleForm.vue b/app/imports/ui/properties/forms/ToggleForm.vue index f3d01b0f..5f98729c 100644 --- a/app/imports/ui/properties/forms/ToggleForm.vue +++ b/app/imports/ui/properties/forms/ToggleForm.vue @@ -41,9 +41,7 @@ cols="12" md="6" > - + - + diff --git a/app/imports/ui/properties/forms/shared/FormSection.vue b/app/imports/ui/properties/forms/shared/FormSection.vue index 96946f77..88c8c279 100644 --- a/app/imports/ui/properties/forms/shared/FormSection.vue +++ b/app/imports/ui/properties/forms/shared/FormSection.vue @@ -22,16 +22,16 @@ diff --git a/app/imports/ui/properties/forms/shared/FormSections.vue b/app/imports/ui/properties/forms/shared/FormSections.vue index e7e8a738..70701ed4 100644 --- a/app/imports/ui/properties/forms/shared/FormSections.vue +++ b/app/imports/ui/properties/forms/shared/FormSections.vue @@ -8,5 +8,5 @@ diff --git a/app/imports/ui/properties/forms/shared/ProficiencySelect.vue b/app/imports/ui/properties/forms/shared/ProficiencySelect.vue index 51e1dc4c..1af7095a 100644 --- a/app/imports/ui/properties/forms/shared/ProficiencySelect.vue +++ b/app/imports/ui/properties/forms/shared/ProficiencySelect.vue @@ -22,11 +22,11 @@ diff --git a/app/imports/ui/properties/forms/shared/schemaFormMixin.js b/app/imports/ui/properties/forms/shared/schemaFormMixin.js index e1d3a349..5db234da 100644 --- a/app/imports/ui/properties/forms/shared/schemaFormMixin.js +++ b/app/imports/ui/properties/forms/shared/schemaFormMixin.js @@ -4,9 +4,9 @@ */ import { get, toPath } from 'lodash'; -function resolvePath(model, path, set){ +function resolvePath(model, path, set) { let arrayPath = toPath(path); - if (arrayPath.length === 1){ + if (arrayPath.length === 1) { return { object: model, key: arrayPath[0] }; } let key = arrayPath.slice(-1); @@ -15,67 +15,69 @@ function resolvePath(model, path, set){ // Ensure that nested objects exist before navigating them objectPath.forEach(pathKey => { let newObject = object[pathKey]; - if (!newObject){ + if (!newObject) { newObject = {}; set(object, pathKey, newObject); } object = newObject; }); - return {object, key}; + return { object, key }; } const schemaFormMixin = { - data(){ return { - valid: true, - };}, - computed: { - errors(){ - this.valid = true; - if (!this.model){ - throw new Error('this.model must be set'); - } - if (!this.validationContext) return {}; - let cleanModel = this.validationContext.clean(this.model, { - getAutoValues: false, - }); - this.validationContext.validate(cleanModel); - let errors = {}; - this.validationContext.validationErrors().forEach(error => { - if (this.valid) this.valid = false; - errors[error.name] = this.schema.messageForError(error); - }); - return errors; - }, - }, - methods: { + data() { + return { + valid: true, + }; + }, + computed: { + errors() { + this.valid = true; + if (!this.model) { + throw new Error('this.model must be set'); + } + if (!this.validationContext) return {}; + let cleanModel = this.validationContext.clean(this.model, { + getAutoValues: false, + }); + this.validationContext.validate(cleanModel); + let errors = {}; + this.validationContext.validationErrors().forEach(error => { + if (this.valid) this.valid = false; + errors[error.name] = this.schema.messageForError(error); + }); + return errors; + }, + }, + methods: { // Sets the value at the given path - change({path, value, ack}){ - let {object, key} = resolvePath(this.model, path, this.$set); + change({ path, value, ack }) { + let { object, key } = resolvePath(this.model, path, this.$set); - this.$set(object, key, value); - if (ack) ack(); - }, - push({path, value, ack}){ + this.$set(object, key, value); + if (ack) ack(); + }, + push({ path, value, ack }) { let array = get(this.model, path); - if (array === undefined){ - let {object, key} = resolvePath(this.model, path, this.$set); + if (array === undefined) { + let { object, key } = resolvePath(this.model, path, this.$set); this.$set(object, key, [value]); - } else if (!array.push){ + } else if (!array.push) { throw `${path.join('.')} is ${array}, doesn't have "push"` } else { array.push(value); } - if (ack) ack(); + if (ack) ack(); }, - pull({path, ack}){ - let {object, key} = resolvePath(this.model, path, this.$set); - if (!object || !object.splice){ + pull({ path, ack }) { + let { object, key } = resolvePath(this.model, path, this.$set); + if (!object || !object.splice) { throw `${path.join('.')} is ${object}, doesnt have "splice"` } object.splice(key, 1); if (ack) ack(); }, - }, + }, }; export default schemaFormMixin; diff --git a/app/imports/ui/properties/shared/ProficiencyIcon.vue b/app/imports/ui/properties/shared/ProficiencyIcon.vue index 91249fc3..223ef725 100644 --- a/app/imports/ui/properties/shared/ProficiencyIcon.vue +++ b/app/imports/ui/properties/shared/ProficiencyIcon.vue @@ -8,16 +8,16 @@ import getProficiencyIcon from '/imports/ui/utility/getProficiencyIcon.js'; export default { - props: { - value: { + props: { + value: { type: Number, default: undefined, }, - }, - computed: { - displayedIcon(){ + }, + computed: { + displayedIcon(){ return getProficiencyIcon(this.value); - } - } + } + } } diff --git a/app/imports/ui/properties/shared/PropertyIcon.vue b/app/imports/ui/properties/shared/PropertyIcon.vue index bf173aef..73a241db 100644 --- a/app/imports/ui/properties/shared/PropertyIcon.vue +++ b/app/imports/ui/properties/shared/PropertyIcon.vue @@ -18,8 +18,8 @@ import { getPropertyIcon } from '/imports/constants/PROPERTIES.js'; export default { - props: { - model: { + props: { + model: { type: Object, default: () => ({}), }, @@ -28,17 +28,18 @@ export default { default: undefined, }, disabled: Boolean, - }, - computed: { - icon(){ - return getPropertyIcon(this.model && this.model.type); - }, - }, + }, + computed: { + icon() { + return getPropertyIcon(this.model && this.model.type); + }, + }, } diff --git a/app/imports/ui/properties/shared/PropertySelector.vue b/app/imports/ui/properties/shared/PropertySelector.vue index 36670fe7..8fc32b7e 100644 --- a/app/imports/ui/properties/shared/PropertySelector.vue +++ b/app/imports/ui/properties/shared/PropertySelector.vue @@ -1,8 +1,6 @@ diff --git a/app/imports/ui/properties/viewers/EffectViewer.vue b/app/imports/ui/properties/viewers/EffectViewer.vue index ef72d816..5880fc10 100644 --- a/app/imports/ui/properties/viewers/EffectViewer.vue +++ b/app/imports/ui/properties/viewers/EffectViewer.vue @@ -6,9 +6,7 @@ class="layout" style="overflow: hidden;" > - + {{ effectIcon }} {{ operation }} @@ -77,71 +75,75 @@ diff --git a/app/imports/ui/properties/viewers/FeatureViewer.vue b/app/imports/ui/properties/viewers/FeatureViewer.vue index 4727df07..ae5c405b 100644 --- a/app/imports/ui/properties/viewers/FeatureViewer.vue +++ b/app/imports/ui/properties/viewers/FeatureViewer.vue @@ -16,9 +16,10 @@ diff --git a/app/imports/ui/properties/viewers/FolderViewer.vue b/app/imports/ui/properties/viewers/FolderViewer.vue index 352db109..51a51a76 100644 --- a/app/imports/ui/properties/viewers/FolderViewer.vue +++ b/app/imports/ui/properties/viewers/FolderViewer.vue @@ -5,9 +5,10 @@ diff --git a/app/imports/ui/properties/viewers/ItemViewer.vue b/app/imports/ui/properties/viewers/ItemViewer.vue index d5f07aab..0db65ce7 100644 --- a/app/imports/ui/properties/viewers/ItemViewer.vue +++ b/app/imports/ui/properties/viewers/ItemViewer.vue @@ -24,9 +24,7 @@ v-if="model.value !== undefined" name="value" > -
+
- + -
+
- + Equipped - + diff --git a/app/imports/ui/properties/viewers/RollViewer.vue b/app/imports/ui/properties/viewers/RollViewer.vue index d00aeed1..31e87bbf 100644 --- a/app/imports/ui/properties/viewers/RollViewer.vue +++ b/app/imports/ui/properties/viewers/RollViewer.vue @@ -17,9 +17,9 @@ diff --git a/app/imports/ui/properties/viewers/SavingThrowViewer.vue b/app/imports/ui/properties/viewers/SavingThrowViewer.vue index 02b4fbcb..59feea7a 100644 --- a/app/imports/ui/properties/viewers/SavingThrowViewer.vue +++ b/app/imports/ui/properties/viewers/SavingThrowViewer.vue @@ -22,9 +22,9 @@ diff --git a/app/imports/ui/properties/viewers/SkillViewer.vue b/app/imports/ui/properties/viewers/SkillViewer.vue index 651fd9c5..db6609c5 100644 --- a/app/imports/ui/properties/viewers/SkillViewer.vue +++ b/app/imports/ui/properties/viewers/SkillViewer.vue @@ -133,42 +133,44 @@ export default { AttributeEffect, SkillProficiency, }, - mixins: [propertyViewerMixin], + mixins: [propertyViewerMixin], inject: { context: { default: {} } }, - data(){return { - proficiencyText: { - 0: 'Not proficient', - 1: 'Proficient', - 0.49: 'Half proficiency bonus rounded down', - 0.5: 'Half proficiency bonus rounded up', - 2: 'Double proficiency bonus', - }, - skillTypes: { - skill: 'Skill', - save: 'Save', - check: 'Check', - tool: 'Tool', - weapon: 'Weapon', - armor: 'Armor', - language: 'Language', - utility: 'Utility', - }, - }}, + data() { + return { + proficiencyText: { + 0: 'Not proficient', + 1: 'Proficient', + 0.49: 'Half proficiency bonus rounded down', + 0.5: 'Half proficiency bonus rounded up', + 2: 'Double proficiency bonus', + }, + skillTypes: { + skill: 'Skill', + save: 'Save', + check: 'Check', + tool: 'Tool', + weapon: 'Weapon', + armor: 'Armor', + language: 'Language', + utility: 'Utility', + }, + } + }, computed: { - displayedModifier(){ - let mod = this.model.value; - if (this.model.fail){ - return 'fail'; - } else { - return numberToSignedString(mod); - } - }, - icon(){ + displayedModifier() { + let mod = this.model.value; + if (this.model.fail) { + return 'fail'; + } else { + return numberToSignedString(mod); + } + }, + icon() { return getProficiencyIcon(this.model.proficiency); - }, - passiveScore(){ + }, + passiveScore() { return 10 + this.model.value + this.model.passiveBonus; }, effects() { @@ -178,53 +180,53 @@ export default { methods: { numberToSignedString, isFinite: Number.isFinite, - clickEffect(id){ + clickEffect(id) { this.$store.commit('pushDialogStack', { component: 'creature-property-dialog', elementId: `${id}`, - data: {_id: id}, + data: { _id: id }, }); }, }, meteor: { - variables(){ - return CreatureVariables.findOne({_creatureId: this.context.creatureId}) || {}; + variables() { + return CreatureVariables.findOne({ _creatureId: this.context.creatureId }) || {}; }, - baseProficiencies(){ - if (this.context.creatureId){ + baseProficiencies() { + if (this.context.creatureId) { let creatureId = this.context.creatureId; return CreatureProperties.find({ 'ancestors.id': creatureId, type: 'skill', variableName: this.model.variableName, - removed: {$ne: true}, - inactive: {$ne: true}, - }).map( prop => ({ + removed: { $ne: true }, + inactive: { $ne: true }, + }).map(prop => ({ _id: prop._id, name: 'Skill base proficiency', value: prop.baseProficiency, stats: [prop.variableName], ancestors: prop.ancestors, - }) ).filter(prof => prof.value); + })).filter(prof => prof.value); } else { return []; } }, - proficiencies(){ + proficiencies() { let creatureId = this.context.creatureId; - if (creatureId){ + if (creatureId) { return CreatureProperties.find({ 'ancestors.id': creatureId, stats: this.model.variableName, type: 'proficiency', - removed: {$ne: true}, - inactive: {$ne: true}, + removed: { $ne: true }, + inactive: { $ne: true }, }).fetch(); } else { return []; } }, - ability(){ + ability() { let creatureId = this.context.creatureId; let ability = this.model.ability; if (!creatureId || !ability) return; @@ -232,21 +234,21 @@ export default { 'ancestors.id': creatureId, variableName: ability, type: 'attribute', - removed: {$ne: true}, - inactive: {$ne: true}, - overridden: {$ne: true}, + removed: { $ne: true }, + inactive: { $ne: true }, + overridden: { $ne: true }, }); if (!abilityProp) return; return { _id: abilityProp._id, name: abilityProp.name, operation: 'add', - amount: {value: abilityProp.modifier}, + amount: { value: abilityProp.modifier }, stats: [this.model.variableName], ancestors: abilityProp.ancestors, } }, - proficiencyBonus(){ + proficiencyBonus() { let creatureId = this.context.creatureId; if (!creatureId) return; return this.variables.proficiencyBonus && @@ -257,4 +259,5 @@ export default { diff --git a/app/imports/ui/properties/viewers/SlotFillerViewer.vue b/app/imports/ui/properties/viewers/SlotFillerViewer.vue index 6ebf8a36..d3cb60c5 100644 --- a/app/imports/ui/properties/viewers/SlotFillerViewer.vue +++ b/app/imports/ui/properties/viewers/SlotFillerViewer.vue @@ -32,32 +32,30 @@ name="Description" :cols="{cols: 12}" > - +
diff --git a/app/imports/ui/properties/viewers/SlotViewer.vue b/app/imports/ui/properties/viewers/SlotViewer.vue index 36e432f1..55ceeb8d 100644 --- a/app/imports/ui/properties/viewers/SlotViewer.vue +++ b/app/imports/ui/properties/viewers/SlotViewer.vue @@ -60,34 +60,34 @@ diff --git a/app/imports/ui/properties/viewers/SpellListViewer.vue b/app/imports/ui/properties/viewers/SpellListViewer.vue index 9b4c9d49..f7c35b57 100644 --- a/app/imports/ui/properties/viewers/SpellListViewer.vue +++ b/app/imports/ui/properties/viewers/SpellListViewer.vue @@ -37,6 +37,6 @@ diff --git a/app/imports/ui/properties/viewers/SpellViewer.vue b/app/imports/ui/properties/viewers/SpellViewer.vue index 35b31e7b..ee8085b2 100644 --- a/app/imports/ui/properties/viewers/SpellViewer.vue +++ b/app/imports/ui/properties/viewers/SpellViewer.vue @@ -43,12 +43,12 @@ export default { components: { ActionViewer, }, - mixins: [propertyViewerMixin], - computed:{ - levelText(){ + mixins: [propertyViewerMixin], + computed: { + levelText() { return levelText[this.model.level] }, - spellComponents(){ + spellComponents() { let components = []; if (this.model.ritual) components.push('Ritual'); if (this.model.concentration) components.push('Concentration'); @@ -62,4 +62,5 @@ export default { diff --git a/app/imports/ui/properties/viewers/ToggleViewer.vue b/app/imports/ui/properties/viewers/ToggleViewer.vue index 666c184f..f2b9037a 100644 --- a/app/imports/ui/properties/viewers/ToggleViewer.vue +++ b/app/imports/ui/properties/viewers/ToggleViewer.vue @@ -11,9 +11,7 @@ name="Status" :value="model.enabled ? 'Enabled' : 'Disabled'" /> -