From 358ae4662710c1bededfd714ae1b0b3368812573 Mon Sep 17 00:00:00 2001 From: Stefan Zermatten Date: Thu, 3 Nov 2022 19:08:44 +0200 Subject: [PATCH 01/46] Began work on copy to for library nodes --- .../api/library/methods/copyLibraryNodeTo.js | 100 ++++++++++++++++++ .../library/methods/duplicateLibraryNode.js | 2 +- 2 files changed, 101 insertions(+), 1 deletion(-) create mode 100644 app/imports/api/library/methods/copyLibraryNodeTo.js diff --git a/app/imports/api/library/methods/copyLibraryNodeTo.js b/app/imports/api/library/methods/copyLibraryNodeTo.js new file mode 100644 index 00000000..e87eb5e2 --- /dev/null +++ b/app/imports/api/library/methods/copyLibraryNodeTo.js @@ -0,0 +1,100 @@ +import { ValidatedMethod } from 'meteor/mdg:validated-method'; +import SimpleSchema from 'simpl-schema'; +i +import { RateLimiterMixin } from 'ddp-rate-limiter-mixin'; +import { RefSchema } from '/imports/api/parenting/ChildSchema.js'; +import LibraryNodes from '/imports/api/library/LibraryNodes.js'; +import { assertDocEditPermission } from '/imports/api/sharing/sharingPermissions.js'; +import { + setLineageOfDocs, + renewDocIds +} from '/imports/api/parenting/parenting.js'; +import { reorderDocs } from '/imports/api/parenting/order.js'; +import fetchDocByRef from '/imports/api/parenting/fetchDocByRef.js'; + +var snackbar; +if (Meteor.isClient) { + snackbar = require( + '/imports/ui/components/snackbars/SnackbarQueue.js' + ).snackbar +} + +const DUPLICATE_CHILDREN_LIMIT = 100; + +const copyLibraryNodeTo = new ValidatedMethod({ + name: 'libraryNodes.copyTo', + validate: new SimpleSchema({ + _id: { + type: String, + regEx: SimpleSchema.RegEx.Id, + }, + parent: { + type: RefSchema, + }, + }).validator(), + mixins: [RateLimiterMixin], + rateLimit: { + numRequests: 1, + timeInterval: 5000, + }, + run({ _id, parent }) { + if (parent.collection !== 'libraryNodes' && parent.collection !== 'libraries') { + throw new Meteor.Error('Invalid destination', + 'Library documents can only be copied to destinations inside other libraries' + ); + } + const libraryNode = LibraryNodes.findOne(_id); + const parentDoc = fetchDocByRef(parent); + assertDocEditPermission(libraryNode, this.userId); + + let randomSrc = DDP.randomStream('copyLibraryNodeTo'); + let libraryNodeId = randomSrc.id(); + libraryNode._id = libraryNodeId; + + let decendants = LibraryNodes.find({ + 'ancestors.id': _id, + removed: { $ne: true }, + }, { + limit: DUPLICATE_CHILDREN_LIMIT + 1, + sort: { order: 1 }, + }).fetch(); + + if (decendants.length > DUPLICATE_CHILDREN_LIMIT) { + decendants.pop(); + if (Meteor.isClient) { + snackbar({ + text: `Only the first ${DUPLICATE_CHILDREN_LIMIT} children were duplicated`, + }); + } + } + + const nodes = [libraryNode, decendants]; + + const newAncestry = parentDoc.ancestors || []; + newAncestry.push(parent); + // re-map all the ancestors + setLineageOfDocs({ + docArray: nodes, + newAncestry, + oldParent: libraryNode.parent, + }); + + // Give the docs new IDs without breaking internal references + renewDocIds({ docArray: nodes }); + + // Order the root node + libraryNode.order = (parentDoc.order || 0) + 0.5; + + LibraryNodes.batchInsert(nodes); + + // Tree structure changed by inserts, reorder the tree + reorderDocs({ + collection: LibraryNodes, + ancestorId: parent.collection === 'libraries' ? parent.id : parentDoc.ancestors[0].id, + }); + + return libraryNodeId; + }, +}); + +export default duplicateLibraryNode; diff --git a/app/imports/api/library/methods/duplicateLibraryNode.js b/app/imports/api/library/methods/duplicateLibraryNode.js index 107f3c6b..59b54c8a 100644 --- a/app/imports/api/library/methods/duplicateLibraryNode.js +++ b/app/imports/api/library/methods/duplicateLibraryNode.js @@ -16,7 +16,7 @@ if (Meteor.isClient) { ).snackbar } -const DUPLICATE_CHILDREN_LIMIT = 50; +const DUPLICATE_CHILDREN_LIMIT = 100; const duplicateLibraryNode = new ValidatedMethod({ name: 'libraryNodes.duplicate', From 8f56a60fb142a9583525c6a2e9df2aa48f49854b Mon Sep 17 00:00:00 2001 From: Stefan Zermatten Date: Thu, 3 Nov 2022 20:18:59 +0200 Subject: [PATCH 02/46] Added copy-to and related sharing permissions --- .../api/library/methods/copyLibraryNodeTo.js | 23 +++-- .../library/methods/duplicateLibraryNode.js | 4 +- app/imports/api/library/methods/index.js | 1 + app/imports/api/sharing/SharingSchema.js | 4 + app/imports/api/sharing/sharing.js | 22 ++++- app/imports/api/sharing/sharingPermissions.js | 88 ++++++++++++++----- app/imports/ui/components/ColorPicker.vue | 4 + app/imports/ui/components/propertyToolbar.vue | 20 +++++ app/imports/ui/library/LibraryNodeDialog.vue | 47 +++++++++- .../ui/library/MoveLibraryNodeDialog.vue | 8 +- app/imports/ui/sharing/ShareDialog.vue | 20 +++++ 11 files changed, 202 insertions(+), 39 deletions(-) diff --git a/app/imports/api/library/methods/copyLibraryNodeTo.js b/app/imports/api/library/methods/copyLibraryNodeTo.js index e87eb5e2..f99b184f 100644 --- a/app/imports/api/library/methods/copyLibraryNodeTo.js +++ b/app/imports/api/library/methods/copyLibraryNodeTo.js @@ -1,10 +1,12 @@ import { ValidatedMethod } from 'meteor/mdg:validated-method'; import SimpleSchema from 'simpl-schema'; -i import { RateLimiterMixin } from 'ddp-rate-limiter-mixin'; import { RefSchema } from '/imports/api/parenting/ChildSchema.js'; import LibraryNodes from '/imports/api/library/LibraryNodes.js'; -import { assertDocEditPermission } from '/imports/api/sharing/sharingPermissions.js'; +import { + assertDocCopyPermission, + assertDocEditPermission +} from '/imports/api/sharing/sharingPermissions.js'; import { setLineageOfDocs, renewDocIds @@ -19,7 +21,7 @@ if (Meteor.isClient) { ).snackbar } -const DUPLICATE_CHILDREN_LIMIT = 100; +const DUPLICATE_CHILDREN_LIMIT = 500; const copyLibraryNodeTo = new ValidatedMethod({ name: 'libraryNodes.copyTo', @@ -35,7 +37,7 @@ const copyLibraryNodeTo = new ValidatedMethod({ mixins: [RateLimiterMixin], rateLimit: { numRequests: 1, - timeInterval: 5000, + timeInterval: 10000, }, run({ _id, parent }) { if (parent.collection !== 'libraryNodes' && parent.collection !== 'libraries') { @@ -45,11 +47,8 @@ const copyLibraryNodeTo = new ValidatedMethod({ } const libraryNode = LibraryNodes.findOne(_id); const parentDoc = fetchDocByRef(parent); - assertDocEditPermission(libraryNode, this.userId); - - let randomSrc = DDP.randomStream('copyLibraryNodeTo'); - let libraryNodeId = randomSrc.id(); - libraryNode._id = libraryNodeId; + assertDocCopyPermission(libraryNode, this.userId); + assertDocEditPermission(parentDoc, this.userId); let decendants = LibraryNodes.find({ 'ancestors.id': _id, @@ -68,7 +67,7 @@ const copyLibraryNodeTo = new ValidatedMethod({ } } - const nodes = [libraryNode, decendants]; + const nodes = [libraryNode, ...decendants]; const newAncestry = parentDoc.ancestors || []; newAncestry.push(parent); @@ -92,9 +91,7 @@ const copyLibraryNodeTo = new ValidatedMethod({ collection: LibraryNodes, ancestorId: parent.collection === 'libraries' ? parent.id : parentDoc.ancestors[0].id, }); - - return libraryNodeId; }, }); -export default duplicateLibraryNode; +export default copyLibraryNodeTo; diff --git a/app/imports/api/library/methods/duplicateLibraryNode.js b/app/imports/api/library/methods/duplicateLibraryNode.js index 59b54c8a..ff35b8fb 100644 --- a/app/imports/api/library/methods/duplicateLibraryNode.js +++ b/app/imports/api/library/methods/duplicateLibraryNode.js @@ -16,7 +16,7 @@ if (Meteor.isClient) { ).snackbar } -const DUPLICATE_CHILDREN_LIMIT = 100; +const DUPLICATE_CHILDREN_LIMIT = 500; const duplicateLibraryNode = new ValidatedMethod({ name: 'libraryNodes.duplicate', @@ -28,7 +28,7 @@ const duplicateLibraryNode = new ValidatedMethod({ }).validator(), mixins: [RateLimiterMixin], rateLimit: { - numRequests: 5, + numRequests: 1, timeInterval: 5000, }, run({ _id }) { diff --git a/app/imports/api/library/methods/index.js b/app/imports/api/library/methods/index.js index 1b566bc5..e67eec8a 100644 --- a/app/imports/api/library/methods/index.js +++ b/app/imports/api/library/methods/index.js @@ -1,2 +1,3 @@ +import '/imports/api/library/methods/copyLibraryNodeTo.js'; import '/imports/api/library/methods/duplicateLibraryNode.js'; import '/imports/api/library/methods/updateReferenceNode.js'; diff --git a/app/imports/api/sharing/SharingSchema.js b/app/imports/api/sharing/SharingSchema.js index 2a4f87c3..629f90bb 100644 --- a/app/imports/api/sharing/SharingSchema.js +++ b/app/imports/api/sharing/SharingSchema.js @@ -33,6 +33,10 @@ let SharingSchema = new SimpleSchema({ defaultValue: false, index: 1, }, + readersCanCopy: { + type: Boolean, + optional: true, + }, }); export default SharingSchema; diff --git a/app/imports/api/sharing/sharing.js b/app/imports/api/sharing/sharing.js index f6c8d8b7..7062ff40 100644 --- a/app/imports/api/sharing/sharing.js +++ b/app/imports/api/sharing/sharing.js @@ -27,6 +27,26 @@ const setPublic = new ValidatedMethod({ }, }); +const setReadersCanCopy = new ValidatedMethod({ + name: 'sharing.setReadersCanCopy', + validate: new SimpleSchema({ + docRef: RefSchema, + readersCanCopy: { type: Boolean }, + }).validator(), + mixins: [RateLimiterMixin], + rateLimit: { + numRequests: 5, + timeInterval: 5000, + }, + run({ docRef, readersCanCopy }) { + let doc = fetchDocByRef(docRef); + assertOwnership(doc, this.userId); + return getCollectionByName(docRef.collection).update(docRef.id, { + $set: { readersCanCopy }, + }); + }, +}); + const updateUserSharePermissions = new ValidatedMethod({ name: 'sharing.updateUserSharePermissions', validate: new SimpleSchema({ @@ -129,4 +149,4 @@ const transferOwnership = new ValidatedMethod({ }, }); -export { setPublic, updateUserSharePermissions, transferOwnership }; +export { setPublic, setReadersCanCopy, updateUserSharePermissions, transferOwnership }; diff --git a/app/imports/api/sharing/sharingPermissions.js b/app/imports/api/sharing/sharingPermissions.js index 58629c62..5591c59c 100644 --- a/app/imports/api/sharing/sharingPermissions.js +++ b/app/imports/api/sharing/sharingPermissions.js @@ -1,24 +1,25 @@ import { _ } from 'meteor/underscore'; import fetchDocByRef from '/imports/api/parenting/fetchDocByRef.js'; -function assertIdValid(userId){ - if (!userId || typeof userId !== 'string'){ +function assertIdValid(userId) { + if (!userId || typeof userId !== 'string') { throw new Meteor.Error('Permission denied', 'No user ID. Are you logged in?'); } } -function assertdocExists(doc){ - if (!doc){ +function assertdocExists(doc) { + if (!doc) { throw new Meteor.Error('Permission denied', 'Permission denied: No such document exists'); } } -export function assertOwnership(doc, userId){ +export function assertOwnership(doc, userId) { assertIdValid(userId); assertdocExists(doc); - if (doc.owner === userId ){ + + if (doc.owner === userId) { return true; } else { throw new Meteor.Error('Permission denied', @@ -37,13 +38,12 @@ export function assertEditPermission(doc, userId) { assertdocExists(doc); const user = Meteor.users.findOne(userId, { fields: { - 'services.patreon': 1, 'roles': 1, } }); // Admin override - if (user.roles && user.roles.includes('admin')){ + if (user.roles && user.roles.includes('admin')) { return true; } @@ -51,7 +51,7 @@ export function assertEditPermission(doc, userId) { if ( doc.owner === userId || _.contains(doc.writers, userId) - ){ + ) { return true; } else { throw new Meteor.Error('Edit permission denied', @@ -59,9 +59,46 @@ export function assertEditPermission(doc, userId) { } } -function getRoot(doc){ +/** + * Assert that the user can edit the root document which manages its own sharing + * permissions. + * + * Warning: the doc and userId must be set by a trusted source + */ +export function assertCopyPermission(doc, userId) { + assertIdValid(userId); assertdocExists(doc); - if (doc.ancestors && doc.ancestors.length && doc.ancestors[0]){ + const user = Meteor.users.findOne(userId, { + fields: { + 'roles': 1, + } + }); + + // Admin override + if (user.roles && user.roles.includes('admin')) { + return true; + } + + // Ensure the user is authorized for this specific document + if ( + doc.owner === userId || + _.contains(doc.writers, userId) + ) { + return true; + } else if ( + (_.contains(doc.readers, userId) || doc.public) && + doc.readersCanCopy + ) { + return true; + } else { + throw new Meteor.Error('Copy permission denied', + 'You do not have permission to copy this document'); + } +} + +function getRoot(doc) { + assertdocExists(doc); + if (doc.ancestors && doc.ancestors.length && doc.ancestors[0]) { return fetchDocByRef(doc.ancestors[0]); } else { return doc; @@ -74,11 +111,22 @@ function getRoot(doc){ * * Warning: the doc and userId must be set by a trusted source */ -export function assertDocEditPermission(doc, userId){ +export function assertDocEditPermission(doc, userId) { let root = getRoot(doc); assertEditPermission(root, userId); } +/** + * Assert that the user can copy a descendant document whose root ancestor + * implements sharing permissions. + * + * Warning: the doc and userId must be set by a trusted source + */ +export function assertDocCopyPermission(doc, userId) { + let root = getRoot(doc); + assertCopyPermission(root, userId); +} + export function assertViewPermission(doc, userId) { assertdocExists(doc); if (doc.public) return true; @@ -88,17 +136,17 @@ export function assertViewPermission(doc, userId) { doc.owner === userId || _.contains(doc.readers, userId) || _.contains(doc.writers, userId) - ){ + ) { return true; } else { - + // Admin override const user = Meteor.users.findOne(userId, { fields: { 'roles': 1, } }); - if (user.roles && user.roles.includes('admin')){ + if (user.roles && user.roles.includes('admin')) { return true; } @@ -113,20 +161,20 @@ export function assertViewPermission(doc, userId) { * * Warning: the doc and userId must be set by a trusted source */ -export function assertDocViewPermission(doc, userId){ +export function assertDocViewPermission(doc, userId) { let root = getRoot(doc); assertViewPermission(root, userId); } -export function assertAdmin(userId){ +export function assertAdmin(userId) { assertIdValid(userId); - let user = Meteor.users.findOne(userId, {fields: {roles: 1}}); - if (!user){ + let user = Meteor.users.findOne(userId, { fields: { roles: 1 } }); + if (!user) { throw new Meteor.Error('Permission denied', 'UserId does not match any existing user'); } let isAdmin = user.roles && user.roles.includes('admin') - if (!isAdmin){ + if (!isAdmin) { throw new Meteor.Error('Permission denied', 'User does not have the admin role'); } diff --git a/app/imports/ui/components/ColorPicker.vue b/app/imports/ui/components/ColorPicker.vue index bb1115f6..cc1423bd 100644 --- a/app/imports/ui/components/ColorPicker.vue +++ b/app/imports/ui/components/ColorPicker.vue @@ -10,6 +10,7 @@ :outlined="!!label" :icon="!label" :min-width="label && 108" + :disabled="context.editPermission === false" v-on="on" > {{ label }} @@ -124,6 +125,9 @@ } export default { + inject: { + context: { default: {} } + }, props: { //hex string value: { diff --git a/app/imports/ui/components/propertyToolbar.vue b/app/imports/ui/components/propertyToolbar.vue index 28b454d7..af6ba016 100644 --- a/app/imports/ui/components/propertyToolbar.vue +++ b/app/imports/ui/components/propertyToolbar.vue @@ -69,6 +69,7 @@ @@ -80,8 +81,23 @@ mdi-content-copy + + + + Copy To + + + + mdi-content-duplicate + + @@ -95,6 +111,7 @@ @@ -157,6 +174,9 @@ export default { PropertyIcon, ColorPicker, }, + inject: { + context: { default: {} } + }, props: { model: { type: Object, diff --git a/app/imports/ui/library/LibraryNodeDialog.vue b/app/imports/ui/library/LibraryNodeDialog.vue index e67c0cc2..977f3906 100644 --- a/app/imports/ui/library/LibraryNodeDialog.vue +++ b/app/imports/ui/library/LibraryNodeDialog.vue @@ -8,6 +8,7 @@ :embedded="embedded" @duplicate="duplicate" @move="move" + @copy="copy" @remove="remove" @toggle-editing="editing = !editing" @color-changed="value => change({path: ['color'], value})" @@ -95,10 +96,13 @@ import propertyFormIndex from '/imports/ui/properties/forms/shared/propertyFormIndex.js'; import propertyViewerIndex from '/imports/ui/properties/viewers/shared/propertyViewerIndex.js'; import { get } from 'lodash'; - import { assertDocEditPermission } from '/imports/api/sharing/sharingPermissions.js'; + import { + assertDocEditPermission, assertDocCopyPermission + } from '/imports/api/sharing/sharingPermissions.js'; import { organizeDoc } from '/imports/api/parenting/organizeMethods.js'; import { snackbar } from '/imports/ui/components/snackbars/SnackbarQueue.js'; import getPropertyTitle from '/imports/ui/properties/shared/getPropertyTitle.js'; + import copyLibraryNodeTo from '/imports/api/library/methods/copyLibraryNodeTo.js'; let formIndex = {}; for (let key in propertyFormIndex){ @@ -126,7 +130,7 @@ }, reactiveProvide: { name: 'context', - include: ['editPermission', 'isLibraryForm'], + include: ['editPermission', 'copyPermission', 'isLibraryForm'], }, data(){return { editing: !!this.startInEditTab, @@ -162,6 +166,14 @@ return false; } }, + copyPermission(){ + try { + assertDocCopyPermission(this.model, Meteor.userId()); + return true; + } catch (e) { + return false; + } + }, }, methods: { getPropertyName, @@ -200,6 +212,37 @@ } }); }, + copy(){ + const thisId = this._id; + this.$store.commit('pushDialogStack', { + component: 'move-library-node-dialog', + elementId: 'property-toolbar-menu-button', + data: { + action: 'Copy', + }, + callback(parentId){ + if (!parentId) return; + copyLibraryNodeTo.call({ + _id: thisId, + parent: { + collection: 'libraryNodes', + id: parentId + }, + }, (error) => { + if (error) { + console.error(error); + snackbar({ + text: error.reason || error.message || error.toString(), + }); + } else { + snackbar({ + text: 'Copied successfully', + }); + } + }); + } + }); + }, change({path, value, ack}){ updateLibraryNode.call({_id: this.currentId, path, value}, (error) =>{ if (ack){ diff --git a/app/imports/ui/library/MoveLibraryNodeDialog.vue b/app/imports/ui/library/MoveLibraryNodeDialog.vue index ce412813..f0d22101 100644 --- a/app/imports/ui/library/MoveLibraryNodeDialog.vue +++ b/app/imports/ui/library/MoveLibraryNodeDialog.vue @@ -16,7 +16,7 @@ color="primary" @click="$store.dispatch('popDialogStack', node._id)" > - Move + {{ action || 'Move' }} @@ -30,6 +30,12 @@ export default { DialogBase, LibraryAndNode, }, + props: { + action: { + type: String, + default: undefined, + }, + }, data() { return { node: undefined, diff --git a/app/imports/ui/sharing/ShareDialog.vue b/app/imports/ui/sharing/ShareDialog.vue index 8cd82873..27d8318f 100644 --- a/app/imports/ui/sharing/ShareDialog.vue +++ b/app/imports/ui/sharing/ShareDialog.vue @@ -13,6 +13,16 @@ :value="!!model.public + ''" @change="(value, ack) => setSheetPublic({value, ack})" /> + @@ -126,6 +137,7 @@ + + diff --git a/app/imports/ui/creature/character/printedCharacterSheet/PrintedCharacter.vue b/app/imports/ui/creature/character/printedCharacterSheet/PrintedCharacter.vue new file mode 100644 index 00000000..f37e7b5b --- /dev/null +++ b/app/imports/ui/creature/character/printedCharacterSheet/PrintedCharacter.vue @@ -0,0 +1,58 @@ + + + + + \ No newline at end of file diff --git a/app/imports/ui/creature/character/printedCharacterSheet/PrintedFeatures.vue b/app/imports/ui/creature/character/printedCharacterSheet/PrintedFeatures.vue new file mode 100644 index 00000000..c1bfc215 --- /dev/null +++ b/app/imports/ui/creature/character/printedCharacterSheet/PrintedFeatures.vue @@ -0,0 +1,60 @@ + + + + + diff --git a/app/imports/ui/creature/character/printedCharacterSheet/PrintedInventory.vue b/app/imports/ui/creature/character/printedCharacterSheet/PrintedInventory.vue new file mode 100644 index 00000000..ea77fddd --- /dev/null +++ b/app/imports/ui/creature/character/printedCharacterSheet/PrintedInventory.vue @@ -0,0 +1,233 @@ + + + + + diff --git a/app/imports/ui/creature/character/printedCharacterSheet/PrintedSpells.vue b/app/imports/ui/creature/character/printedCharacterSheet/PrintedSpells.vue new file mode 100644 index 00000000..a5e307cc --- /dev/null +++ b/app/imports/ui/creature/character/printedCharacterSheet/PrintedSpells.vue @@ -0,0 +1,109 @@ + + + + + diff --git a/app/imports/ui/creature/character/printedCharacterSheet/PrintedStats.vue b/app/imports/ui/creature/character/printedCharacterSheet/PrintedStats.vue new file mode 100644 index 00000000..b4717b20 --- /dev/null +++ b/app/imports/ui/creature/character/printedCharacterSheet/PrintedStats.vue @@ -0,0 +1,572 @@ + + + + + diff --git a/app/imports/ui/router.js b/app/imports/ui/router.js index 6fd075d9..bb8dc307 100644 --- a/app/imports/ui/router.js +++ b/app/imports/ui/router.js @@ -12,6 +12,7 @@ const LibraryCollectionToolbar = () => import('/imports/ui/library/LibraryCollec const CharacterSheetPage = () => import('/imports/ui/pages/CharacterSheetPage.vue'); const CharacterSheetToolbar = () => import('/imports/ui/creature/character/CharacterSheetToolbar.vue'); const CharacterSheetRightDrawer = () => import('/imports/ui/creature/character/CharacterSheetRightDrawer.vue'); +const CharacterSheetPrinted = () => import('/imports/ui/creature/character/printedCharacterSheet/CharacterSheetPrinted.vue'); const SignIn = () => import('/imports/ui/pages/SignIn.vue'); const Register = () => import('/imports/ui/pages/Register.vue'); const IconAdmin = () => import('/imports/ui/icons/IconAdmin.vue'); @@ -177,6 +178,16 @@ RouterFactory.configure(router => { meta: { title: 'Character Sheet', }, + }, { + name: 'printCharacterSheet', + path: '/print-character/:id', + alias: '/print-character/:id/:urlName', + components: { + default: CharacterSheetPrinted, + }, + meta: { + title: 'Print Character Sheet', + }, }, { path: '/tabletops', name: 'tabletops', From 008ef625175b3f63421722714540182c25d034b2 Mon Sep 17 00:00:00 2001 From: Stefan Zermatten Date: Mon, 7 Nov 2022 16:18:35 +0200 Subject: [PATCH 09/46] Printing implemented, needs print button on sheet --- .../CharacterSheetPrinted.vue | 239 ++++++- .../PrintedCharacter.vue | 58 -- .../printedCharacterSheet/PrintedFeatures.vue | 60 -- .../PrintedInventory.vue | 209 +++--- .../printedCharacterSheet/PrintedSpells.vue | 75 ++- .../printedCharacterSheet/PrintedStats.vue | 619 ++++++++++-------- .../components/PrintedAction.vue | 247 +++++++ .../components/PrintedContainer.vue | 135 ++++ .../components/PrintedDamageMultipliers.vue | 71 ++ .../components/PrintedItem.vue | 173 +++++ .../components/PrintedSkill.vue | 105 +++ .../components/PrintedSpell.vue | 82 +++ .../components/PrintedSpellList.vue | 39 ++ app/package-lock.json | 45 +- app/package.json | 3 +- .../{octogonBorder.png => octagonBorder.png} | Bin 16 files changed, 1618 insertions(+), 542 deletions(-) delete mode 100644 app/imports/ui/creature/character/printedCharacterSheet/PrintedCharacter.vue delete mode 100644 app/imports/ui/creature/character/printedCharacterSheet/PrintedFeatures.vue create mode 100644 app/imports/ui/creature/character/printedCharacterSheet/components/PrintedAction.vue create mode 100644 app/imports/ui/creature/character/printedCharacterSheet/components/PrintedContainer.vue create mode 100644 app/imports/ui/creature/character/printedCharacterSheet/components/PrintedDamageMultipliers.vue create mode 100644 app/imports/ui/creature/character/printedCharacterSheet/components/PrintedItem.vue create mode 100644 app/imports/ui/creature/character/printedCharacterSheet/components/PrintedSkill.vue create mode 100644 app/imports/ui/creature/character/printedCharacterSheet/components/PrintedSpell.vue create mode 100644 app/imports/ui/creature/character/printedCharacterSheet/components/PrintedSpellList.vue rename app/public/images/print/{octogonBorder.png => octagonBorder.png} (100%) diff --git a/app/imports/ui/creature/character/printedCharacterSheet/CharacterSheetPrinted.vue b/app/imports/ui/creature/character/printedCharacterSheet/CharacterSheetPrinted.vue index cffd8bc5..cba250c3 100644 --- a/app/imports/ui/creature/character/printedCharacterSheet/CharacterSheetPrinted.vue +++ b/app/imports/ui/creature/character/printedCharacterSheet/CharacterSheetPrinted.vue @@ -31,14 +31,45 @@ v-else light > - - - - - +
+
+
+
+ {{ creature.name }} +
+
+
+ {{ creature.alignment }} {{ background }} +
+ + {{ race }} {{ creature.gender }} + +
+ Level {{ level }} {{ classes[0].name }} +
+
+ Level {{ level }} ({{ classes.map(c => `${c.name} ${c.level}`).join(', ') }}) +
+
+ +
+
+ {{ creatureUrl }} +
+ + + +
@@ -46,25 +77,59 @@ - - \ No newline at end of file diff --git a/app/imports/ui/creature/character/printedCharacterSheet/PrintedFeatures.vue b/app/imports/ui/creature/character/printedCharacterSheet/PrintedFeatures.vue deleted file mode 100644 index c1bfc215..00000000 --- a/app/imports/ui/creature/character/printedCharacterSheet/PrintedFeatures.vue +++ /dev/null @@ -1,60 +0,0 @@ - - - - - diff --git a/app/imports/ui/creature/character/printedCharacterSheet/PrintedInventory.vue b/app/imports/ui/creature/character/printedCharacterSheet/PrintedInventory.vue index ea77fddd..53694a81 100644 --- a/app/imports/ui/creature/character/printedCharacterSheet/PrintedInventory.vue +++ b/app/imports/ui/creature/character/printedCharacterSheet/PrintedInventory.vue @@ -1,90 +1,84 @@ @@ -93,22 +87,20 @@ import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js'; import Creatures from '/imports/api/creature/creatures/Creatures.js'; import ColumnLayout from '/imports/ui/components/ColumnLayout.vue'; -import ContainerCard from '/imports/ui/properties/components/inventory/ContainerCard.vue'; -import ToolbarCard from '/imports/ui/components/ToolbarCard.vue'; -import ItemList from '/imports/ui/properties/components/inventory/ItemList.vue'; import getParentRefByTag from '/imports/api/creature/creatureProperties/methods/getParentRefByTag.js'; import BUILT_IN_TAGS from '/imports/constants/BUILT_IN_TAGS.js'; import CoinValue from '/imports/ui/components/CoinValue.vue'; import stripFloatingPointOddities from '/imports/api/engine/computation/utility/stripFloatingPointOddities.js'; -import CreatureVariables from '../../../../api/creature/creatures/CreatureVariables'; +import PrintedItem from '/imports/ui/creature/character/printedCharacterSheet/components/PrintedItem.vue'; +import PrintedContainer from '/imports/ui/creature/character/printedCharacterSheet/components/PrintedContainer.vue'; +import CreatureVariables from '/imports/api/creature/creatures/CreatureVariables.js'; export default { components: { ColumnLayout, - ContainerCard, - ToolbarCard, - ItemList, CoinValue, + PrintedItem, + PrintedContainer, }, props: { creatureId: { @@ -154,6 +146,17 @@ export default { inactive: { $ne: true }, }, { sort: { order: 1 }, + }).map(c => { + c.items = CreatureProperties.find({ + 'parent.id': c._id, + type: { $in: ['item', 'container'] }, + removed: { $ne: true }, + equipped: { $ne: true }, + deactivatedByAncestor: { $ne: true }, + }, { + sort: { order: 1 }, + }).fetch(); + return c; }); }, carriedItems() { @@ -229,5 +232,39 @@ export default { diff --git a/app/imports/ui/creature/character/printedCharacterSheet/PrintedSpells.vue b/app/imports/ui/creature/character/printedCharacterSheet/PrintedSpells.vue index a5e307cc..1d60374b 100644 --- a/app/imports/ui/creature/character/printedCharacterSheet/PrintedSpells.vue +++ b/app/imports/ui/creature/character/printedCharacterSheet/PrintedSpells.vue @@ -1,23 +1,38 @@ @@ -25,14 +40,14 @@ diff --git a/app/imports/ui/creature/character/printedCharacterSheet/components/PrintedAction.vue b/app/imports/ui/creature/character/printedCharacterSheet/components/PrintedAction.vue new file mode 100644 index 00000000..5f1adf07 --- /dev/null +++ b/app/imports/ui/creature/character/printedCharacterSheet/components/PrintedAction.vue @@ -0,0 +1,247 @@ + + + + + + + diff --git a/app/imports/ui/creature/character/printedCharacterSheet/components/PrintedContainer.vue b/app/imports/ui/creature/character/printedCharacterSheet/components/PrintedContainer.vue new file mode 100644 index 00000000..a15ed8c8 --- /dev/null +++ b/app/imports/ui/creature/character/printedCharacterSheet/components/PrintedContainer.vue @@ -0,0 +1,135 @@ + + + + + diff --git a/app/imports/ui/creature/character/printedCharacterSheet/components/PrintedDamageMultipliers.vue b/app/imports/ui/creature/character/printedCharacterSheet/components/PrintedDamageMultipliers.vue new file mode 100644 index 00000000..386aabcd --- /dev/null +++ b/app/imports/ui/creature/character/printedCharacterSheet/components/PrintedDamageMultipliers.vue @@ -0,0 +1,71 @@ + + + + + diff --git a/app/imports/ui/creature/character/printedCharacterSheet/components/PrintedItem.vue b/app/imports/ui/creature/character/printedCharacterSheet/components/PrintedItem.vue new file mode 100644 index 00000000..ccc5b85d --- /dev/null +++ b/app/imports/ui/creature/character/printedCharacterSheet/components/PrintedItem.vue @@ -0,0 +1,173 @@ + + + + + diff --git a/app/imports/ui/creature/character/printedCharacterSheet/components/PrintedSkill.vue b/app/imports/ui/creature/character/printedCharacterSheet/components/PrintedSkill.vue new file mode 100644 index 00000000..2920634c --- /dev/null +++ b/app/imports/ui/creature/character/printedCharacterSheet/components/PrintedSkill.vue @@ -0,0 +1,105 @@ + + + + + diff --git a/app/imports/ui/creature/character/printedCharacterSheet/components/PrintedSpell.vue b/app/imports/ui/creature/character/printedCharacterSheet/components/PrintedSpell.vue new file mode 100644 index 00000000..8367c2a0 --- /dev/null +++ b/app/imports/ui/creature/character/printedCharacterSheet/components/PrintedSpell.vue @@ -0,0 +1,82 @@ + + + + + diff --git a/app/imports/ui/creature/character/printedCharacterSheet/components/PrintedSpellList.vue b/app/imports/ui/creature/character/printedCharacterSheet/components/PrintedSpellList.vue new file mode 100644 index 00000000..0ee80376 --- /dev/null +++ b/app/imports/ui/creature/character/printedCharacterSheet/components/PrintedSpellList.vue @@ -0,0 +1,39 @@ + + + \ No newline at end of file diff --git a/app/package-lock.json b/app/package-lock.json index fcedb023..04d47295 100644 --- a/app/package-lock.json +++ b/app/package-lock.json @@ -1,6 +1,6 @@ { "name": "dicecloud", - "version": "2.0.42", + "version": "2.0.43", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -594,9 +594,9 @@ } }, "balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, "base64-js": { "version": "1.5.1", @@ -893,12 +893,12 @@ "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" }, "console-control-strings": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==" + "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=" }, "core-js": { "version": "2.6.12", @@ -958,7 +958,7 @@ "decamelize": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==" + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" }, "decompress-response": { "version": "6.0.0", @@ -1005,7 +1005,7 @@ "delegates": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==" + "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=" }, "detect-libc": { "version": "2.0.1", @@ -1715,7 +1715,7 @@ "has-unicode": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==" + "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=" }, "http-signature": { "version": "1.2.0", @@ -2090,7 +2090,7 @@ "lodash.omit": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.omit/-/lodash.omit-4.5.0.tgz", - "integrity": "sha512-XeqSp49hNGmlkj2EJlfrQFIzQ6lXdNro9sddtQzcJY8QaoC2GO0DT7xaIokHeyM+mIT0mPMlPvkYzg2xCuHdZg==" + "integrity": "sha1-brGa5aHuHdnfC5aeZs4Lf6MLXmA=" }, "lodash.template": { "version": "4.5.0", @@ -3008,9 +3008,9 @@ "integrity": "sha1-IBvZSSceGfbgrwodwMzFg95HxjA=" }, "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "requires": { "brace-expansion": "^1.1.7" } @@ -3156,7 +3156,7 @@ "object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==" + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" }, "object-inspect": { "version": "1.12.2", @@ -3352,6 +3352,11 @@ "yargs": "^15.3.1" } }, + "qrcode.vue": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/qrcode.vue/-/qrcode.vue-1.7.0.tgz", + "integrity": "sha512-R7t6Y3fDDtcU7L4rtqwGUDP9xD64gJhIwpfjhRCTKmBoYF6SS49PIJHRJ048cse6OI7iwTwgyy2C46N9Ygoc6g==" + }, "qs": { "version": "6.5.2", "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", @@ -3396,7 +3401,7 @@ "strip-json-comments": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==" + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=" } } }, @@ -3470,7 +3475,7 @@ "require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==" + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=" }, "require-from-string": { "version": "2.0.2", @@ -3570,7 +3575,7 @@ "set-blocking": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==" + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" }, "setimmediate": { "version": "1.0.5", @@ -3660,7 +3665,7 @@ "simple-swizzle": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", - "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", + "integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=", "requires": { "is-arrayish": "^0.3.1" } @@ -4096,7 +4101,7 @@ "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" }, "uuid": { "version": "3.4.0", @@ -4243,7 +4248,7 @@ "which-module": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q==" + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=" }, "which-typed-array": { "version": "1.1.8", diff --git a/app/package.json b/app/package.json index 23f1e79f..52342baf 100644 --- a/app/package.json +++ b/app/package.json @@ -44,6 +44,7 @@ "ngraph.path": "^1.4.0", "pretty-bytes": "^6.0.0", "qrcode": "^1.5.1", + "qrcode.vue": "^1.7.0", "request": "^2.88.2", "sharp": "^0.30.7", "simpl-schema": "^1.13.1", @@ -124,4 +125,4 @@ "vuetify/no-deprecated-classes": "error" } } -} \ No newline at end of file +} diff --git a/app/public/images/print/octogonBorder.png b/app/public/images/print/octagonBorder.png similarity index 100% rename from app/public/images/print/octogonBorder.png rename to app/public/images/print/octagonBorder.png From a1d992ec8de6a172db925c8c7d9aefb6092d6be0 Mon Sep 17 00:00:00 2001 From: Stefan Zermatten Date: Mon, 7 Nov 2022 16:38:54 +0200 Subject: [PATCH 10/46] Fixed blank multipliers box --- .../creature/character/printedCharacterSheet/PrintedStats.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/imports/ui/creature/character/printedCharacterSheet/PrintedStats.vue b/app/imports/ui/creature/character/printedCharacterSheet/PrintedStats.vue index 4d9e0a29..e3bad2a4 100644 --- a/app/imports/ui/creature/character/printedCharacterSheet/PrintedStats.vue +++ b/app/imports/ui/creature/character/printedCharacterSheet/PrintedStats.vue @@ -107,7 +107,7 @@ -
+
Date: Tue, 8 Nov 2022 16:59:52 +0200 Subject: [PATCH 11/46] Fixed logo not showing --- .../CharacterSheetPrinted.vue | 2 ++ app/package-lock.json | 26 +++++++++---------- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/app/imports/ui/creature/character/printedCharacterSheet/CharacterSheetPrinted.vue b/app/imports/ui/creature/character/printedCharacterSheet/CharacterSheetPrinted.vue index cba250c3..5a4c5b53 100644 --- a/app/imports/ui/creature/character/printedCharacterSheet/CharacterSheetPrinted.vue +++ b/app/imports/ui/creature/character/printedCharacterSheet/CharacterSheetPrinted.vue @@ -253,6 +253,8 @@ export default { background-image: url(/crown-dice-logo-cropped-transparent.png); background-size: contain; background-position: 0 center; + print-color-adjust: exact; + -webkit-print-color-adjust: exact; } .character-sheet-printed .v-divider { diff --git a/app/package-lock.json b/app/package-lock.json index 04d47295..74b38a56 100644 --- a/app/package-lock.json +++ b/app/package-lock.json @@ -893,12 +893,12 @@ "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" }, "console-control-strings": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=" + "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==" }, "core-js": { "version": "2.6.12", @@ -958,7 +958,7 @@ "decamelize": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==" }, "decompress-response": { "version": "6.0.0", @@ -1005,7 +1005,7 @@ "delegates": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=" + "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==" }, "detect-libc": { "version": "2.0.1", @@ -1715,7 +1715,7 @@ "has-unicode": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=" + "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==" }, "http-signature": { "version": "1.2.0", @@ -2090,7 +2090,7 @@ "lodash.omit": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.omit/-/lodash.omit-4.5.0.tgz", - "integrity": "sha1-brGa5aHuHdnfC5aeZs4Lf6MLXmA=" + "integrity": "sha512-XeqSp49hNGmlkj2EJlfrQFIzQ6lXdNro9sddtQzcJY8QaoC2GO0DT7xaIokHeyM+mIT0mPMlPvkYzg2xCuHdZg==" }, "lodash.template": { "version": "4.5.0", @@ -3156,7 +3156,7 @@ "object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==" }, "object-inspect": { "version": "1.12.2", @@ -3401,7 +3401,7 @@ "strip-json-comments": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=" + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==" } } }, @@ -3475,7 +3475,7 @@ "require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=" + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==" }, "require-from-string": { "version": "2.0.2", @@ -3575,7 +3575,7 @@ "set-blocking": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==" }, "setimmediate": { "version": "1.0.5", @@ -3665,7 +3665,7 @@ "simple-swizzle": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", - "integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=", + "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", "requires": { "is-arrayish": "^0.3.1" } @@ -4101,7 +4101,7 @@ "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" }, "uuid": { "version": "3.4.0", @@ -4248,7 +4248,7 @@ "which-module": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=" + "integrity": "sha512-B+enWhmw6cjfVC7kS8Pj9pCrKSc5txArRyaYGe088shv/FGWH+0Rjx/xPgtsWfsUtS27FkP697E4DDhgrgoc0Q==" }, "which-typed-array": { "version": "1.1.8", From 48291d2c8f59bb8516db8a1827d3a0fe5b69b487 Mon Sep 17 00:00:00 2001 From: Stefan Zermatten Date: Tue, 8 Nov 2022 17:17:26 +0200 Subject: [PATCH 12/46] Added help to property creation forms --- .../AddCreaturePropertyDialog.vue | 33 +++++++++++++++++-- .../ui/library/LibraryNodeInsertForm.vue | 25 ++++++++++++++ 2 files changed, 56 insertions(+), 2 deletions(-) diff --git a/app/imports/ui/creature/creatureProperties/AddCreaturePropertyDialog.vue b/app/imports/ui/creature/creatureProperties/AddCreaturePropertyDialog.vue index 944c7642..720b4c45 100644 --- a/app/imports/ui/creature/creatureProperties/AddCreaturePropertyDialog.vue +++ b/app/imports/ui/creature/creatureProperties/AddCreaturePropertyDialog.vue @@ -8,6 +8,22 @@ + + + mdi-help + import LibraryNodes from '/imports/api/library/LibraryNodes.js'; import DialogBase from '/imports/ui/dialogStack/DialogBase.vue'; -import { getPropertyName } from '/imports/constants/PROPERTIES.js'; +import PROPERTIES, { getPropertyName } from '/imports/constants/PROPERTIES.js'; import TreeNodeView from '/imports/ui/properties/treeNodeViews/TreeNodeView.vue'; import LibraryNodeExpansionContent from '/imports/ui/library/LibraryNodeExpansionContent.vue'; import schemaFormMixin from '/imports/ui/properties/forms/shared/schemaFormMixin.js'; @@ -235,7 +251,11 @@ export default { }, toolbarColor(){ return getThemeColor('secondary'); - } + }, + docsPath() { + const propDef = PROPERTIES[this.type]; + return propDef && propDef.docsPath; + }, }, watch: { type(newType){ @@ -259,6 +279,15 @@ export default { }); }); }, + helpDialog() { + this.$store.commit('pushDialogStack', { + component: 'help-dialog', + elementId: 'help-button', + data: { + path: this.docsPath, + }, + }); + }, searchChanged(val, ack){ this._subs.searchLibraryNodes.setData('searchTerm', val); this._subs.searchLibraryNodes.setData('limit', undefined); diff --git a/app/imports/ui/library/LibraryNodeInsertForm.vue b/app/imports/ui/library/LibraryNodeInsertForm.vue index ebd94d49..c006dde6 100644 --- a/app/imports/ui/library/LibraryNodeInsertForm.vue +++ b/app/imports/ui/library/LibraryNodeInsertForm.vue @@ -12,6 +12,13 @@ :value="model.color" @input="value => change({path: ['color'], value})" /> + + mdi-help + From 03f87b0afa0d584c3d8682b37cd0355930a57077 Mon Sep 17 00:00:00 2001 From: Stefan Zermatten Date: Tue, 8 Nov 2022 18:09:00 +0200 Subject: [PATCH 13/46] Added spellcasting ability to spell lists --- .../computeComputation/computeByType.js | 2 + .../computeByType/computeSpellList.js | 6 +++ app/imports/api/properties/SpellLists.js | 12 +++++ .../components/PrintedSpellList.vue | 6 +++ .../ui/properties/forms/SpellListForm.vue | 46 +++++++++++++++++++ .../ui/properties/viewers/SpellListViewer.vue | 12 +++++ .../viewers/shared/PropertyField.vue | 9 +++- app/private/docs/property/spell-list.md | 4 ++ 8 files changed, 96 insertions(+), 1 deletion(-) create mode 100644 app/imports/api/engine/computation/computeComputation/computeByType/computeSpellList.js diff --git a/app/imports/api/engine/computation/computeComputation/computeByType.js b/app/imports/api/engine/computation/computeComputation/computeByType.js index 7597b976..0b5633e3 100644 --- a/app/imports/api/engine/computation/computeComputation/computeByType.js +++ b/app/imports/api/engine/computation/computeComputation/computeByType.js @@ -5,6 +5,7 @@ import skill from './computeByType/computeSkill.js'; import pointBuy from './computeByType/computePointBuy.js'; import propertySlot from './computeByType/computeSlot.js'; import container from './computeByType/computeContainer.js'; +import spellList from './computeByType/computeSpellList.js'; import _calculation from './computeByType/computeCalculation.js'; export default Object.freeze({ @@ -17,4 +18,5 @@ export default Object.freeze({ pointBuy, propertySlot, spell: action, + spellList, }); diff --git a/app/imports/api/engine/computation/computeComputation/computeByType/computeSpellList.js b/app/imports/api/engine/computation/computeComputation/computeByType/computeSpellList.js new file mode 100644 index 00000000..63dc6003 --- /dev/null +++ b/app/imports/api/engine/computation/computeComputation/computeByType/computeSpellList.js @@ -0,0 +1,6 @@ +export default function computeSpelllist(computation, node) { + const prop = node.data; + + const ability = computation.scope[prop.ability]; + prop.abilityMod = ability?.modifier || 0; +} \ No newline at end of file diff --git a/app/imports/api/properties/SpellLists.js b/app/imports/api/properties/SpellLists.js index 01434ea2..285832eb 100644 --- a/app/imports/api/properties/SpellLists.js +++ b/app/imports/api/properties/SpellLists.js @@ -17,6 +17,12 @@ let SpellListSchema = createPropertySchema({ type: 'fieldToCompute', optional: true, }, + // The variable name of the ability this spell relies on + ability: { + type: String, + optional: true, + max: STORAGE_LIMITS.variableName, + }, // Calculation of The attack roll bonus used by spell attacks in this list attackRollBonus: { type: 'fieldToCompute', @@ -38,6 +44,12 @@ const ComputedOnlySpellListSchema = createPropertySchema({ type: 'computedOnlyField', optional: true, }, + // Computed value determined by the ability + abilityMod: { + type: SimpleSchema.Integer, + optional: true, + removeBeforeCompute: true, + }, attackRollBonus: { type: 'computedOnlyField', optional: true, diff --git a/app/imports/ui/creature/character/printedCharacterSheet/components/PrintedSpellList.vue b/app/imports/ui/creature/character/printedCharacterSheet/components/PrintedSpellList.vue index 0ee80376..e158000f 100644 --- a/app/imports/ui/creature/character/printedCharacterSheet/components/PrintedSpellList.vue +++ b/app/imports/ui/creature/character/printedCharacterSheet/components/PrintedSpellList.vue @@ -6,6 +6,12 @@
Spell Save DC: {{ model.dc && model.dc.value }}
+
+ Spell casting ability: {{ model.ability }} +
+
+ Spell casting ability modifier: {{ model.abilityMod }} +
Spell Attack Bonus: {{ model.attackRollBonus && model.attackRollBonus.value }}
diff --git a/app/imports/ui/properties/forms/SpellListForm.vue b/app/imports/ui/properties/forms/SpellListForm.vue index b44ea60c..3855b78a 100644 --- a/app/imports/ui/properties/forms/SpellListForm.vue +++ b/app/imports/ui/properties/forms/SpellListForm.vue @@ -27,6 +27,15 @@ $emit('change', {path: ['maxPrepared', ...path], value, ack})" /> + + import propertyFormMixin from '/imports/ui/properties/forms/shared/propertyFormMixin.js'; +import createListOfProperties from '/imports/ui/properties/forms/shared/lists/createListOfProperties.js'; export default { mixins: [propertyFormMixin], + meteor: { + abilityScoreList() { + return createListOfProperties({ + type: 'attribute', + attributeType: 'ability', + }); + }, + }, + methods: { + changeAbility(value, ack) { + this.$emit('change', { path: ['ability'], value, ack }) + const oldValue = this.model.ability; + + const attackRollBonus = this.model.attackRollBonus?.calculation; + if ( + !attackRollBonus || + attackRollBonus === `proficiencyBonus + ${oldValue}.modifier` + ) { + this.$emit('change', { + path: ['attackRollBonus', 'calculation'], + value: `proficiencyBonus + ${value}.modifier` + }); + } + + const dc = this.model.dc?.calculation; + if ( + !dc || + dc === `8 + proficiencyBonus + ${oldValue}.modifier` + ) { + this.$emit('change', { + path: ['dc', 'calculation'], + value: `8 + proficiencyBonus + ${value}.modifier` + }); + } + } + } }; diff --git a/app/imports/ui/properties/viewers/SpellListViewer.vue b/app/imports/ui/properties/viewers/SpellListViewer.vue index f7c35b57..6aae147e 100644 --- a/app/imports/ui/properties/viewers/SpellListViewer.vue +++ b/app/imports/ui/properties/viewers/SpellListViewer.vue @@ -13,6 +13,18 @@ center :calculation="model.maxPrepared" /> + + @@ -104,31 +99,9 @@ export default { }, }, props: { - value: { - type: Number, - default: undefined, - }, - maxValue: { - type: Number, - default: undefined, - }, - name: { - type: String, - default: undefined, - }, - color: { - type: String, - default() { - return this.$vuetify.theme.currentTheme.primary - }, - }, - midColor: { - type: String, - default: undefined, - }, - lowColor: { - type: String, - default: undefined, + model: { + type: Object, + required: true, }, _id: String, }, @@ -136,24 +109,29 @@ export default { return { editing: false, hover: false, + x: 0, + y: 0, }; }, computed: { fillFraction() { - let fraction = this.value / this.maxValue; + let fraction = this.model.value / this.model.total; if (fraction < 0) fraction = 0; if (fraction > 1) fraction = 1; return fraction; }, + color() { + return this.model.color || this.$vuetify.theme.currentTheme.primary + }, barColor() { - const fraction = this.value / this.maxValue; + const fraction = this.model.value / this.model.total; if (!Number.isFinite(fraction)) return this.color; 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) { - return this.midColor; + } else if (this.model.healthBarColorMid && this.model.healthBarColorLow) { + return chroma.mix(this.model.healthBarColorLow, this.model.healthBarColorMid, fraction * 2).hex(); + } else if (this.model.healthBarColorMid) { + return this.model.healthBarColorMid; } return this.color; }, @@ -166,7 +144,7 @@ export default { isTextLight() { return isDarkColor(this.barBackgroundColor); /* Change color at the halfway mark - const fraction = this.value / this.maxValue; + const fraction = this.model.value / this.model.total; if (fraction >= 0.5){ return isDarkColor(this.barColor); } else { @@ -176,8 +154,14 @@ export default { } }, methods: { - edit() { - this.editing = true; + edit(e) { + e.preventDefault() + this.editing = false; + this.x = e.clientX - 165; + this.y = e.clientY - 24; + this.$nextTick(() => { + this.editing = true + }); }, cancelEdit() { this.editing = false; @@ -199,6 +183,10 @@ export default { z-index: 7; position: relative; } + +.no-menu-shadow { + box-shadow: none !important; +} diff --git a/app/imports/ui/properties/components/attributes/ResourceCardContent.vue b/app/imports/ui/properties/components/attributes/ResourceCardContent.vue new file mode 100644 index 00000000..1f9de91f --- /dev/null +++ b/app/imports/ui/properties/components/attributes/ResourceCardContent.vue @@ -0,0 +1,89 @@ + + + + + \ No newline at end of file diff --git a/app/imports/ui/properties/components/attributes/SpellSlotListTile.vue b/app/imports/ui/properties/components/attributes/SpellSlotListTile.vue index 584fc765..f4a00384 100644 --- a/app/imports/ui/properties/components/attributes/SpellSlotListTile.vue +++ b/app/imports/ui/properties/components/attributes/SpellSlotListTile.vue @@ -61,7 +61,6 @@ export default { required: true, }, dark: Boolean, - hideCastButton: Boolean, disabled: Boolean, }, computed: { diff --git a/app/imports/ui/properties/components/buffs/BuffListItem.vue b/app/imports/ui/properties/components/buffs/BuffListItem.vue new file mode 100644 index 00000000..b122f33c --- /dev/null +++ b/app/imports/ui/properties/components/buffs/BuffListItem.vue @@ -0,0 +1,30 @@ + + + \ No newline at end of file diff --git a/app/imports/ui/properties/components/folders/FolderGroupCard.vue b/app/imports/ui/properties/components/folders/FolderGroupCard.vue index 34662396..a216c8a6 100644 --- a/app/imports/ui/properties/components/folders/FolderGroupCard.vue +++ b/app/imports/ui/properties/components/folders/FolderGroupCard.vue @@ -1,15 +1,25 @@ \ No newline at end of file + + + \ No newline at end of file diff --git a/app/imports/ui/properties/components/folders/folderGroupComponents/ActionGroupComponent.vue b/app/imports/ui/properties/components/folders/folderGroupComponents/ActionGroupComponent.vue new file mode 100644 index 00000000..7bcb81da --- /dev/null +++ b/app/imports/ui/properties/components/folders/folderGroupComponents/ActionGroupComponent.vue @@ -0,0 +1,35 @@ + + + \ No newline at end of file diff --git a/app/imports/ui/properties/components/folders/folderGroupComponents/AttributeGroupComponent.vue b/app/imports/ui/properties/components/folders/folderGroupComponents/AttributeGroupComponent.vue new file mode 100644 index 00000000..30677f39 --- /dev/null +++ b/app/imports/ui/properties/components/folders/folderGroupComponents/AttributeGroupComponent.vue @@ -0,0 +1,95 @@ + + + + + \ No newline at end of file diff --git a/app/imports/ui/properties/components/folders/propertyComponentIndex.js b/app/imports/ui/properties/components/folders/propertyComponentIndex.js index 4b8ed64a..b61d0181 100644 --- a/app/imports/ui/properties/components/folders/propertyComponentIndex.js +++ b/app/imports/ui/properties/components/folders/propertyComponentIndex.js @@ -1,27 +1,27 @@ -import action from '/imports/ui/properties/components/actions/EventButton.vue'; +import action from '/imports/ui/properties/components/folders/folderGroupComponents/ActionGroupComponent.vue'; //import adjustment from ''; -import attribute from '/imports/ui/properties/components/attributes/SpellSlotListTile.vue'; -//import buff from ''; +import attribute from './folderGroupComponents/AttributeGroupComponent.vue'; +import buff from '/imports/ui/properties/components/buffs/BuffListItem.vue'; //import buffRemover from ''; //import branch from ''; //import constant from ''; -//import container from ''; +import container from '/imports/ui/properties/components/inventory/ContainerCard.vue'; //import classComponent from ''; //import classLevel from ''; //import damage from ''; //import damageMultiplier from ''; //import effect from ''; -//import feature from ''; -//import folder from ''; -//import item from ''; -//import note from ''; +import feature from '/imports/ui/properties/components/features/FeatureCard.vue'; +// import folder from ''; +import item from '/imports/ui/properties/components/inventory/ItemListTile.vue'; +import note from '/imports/ui/properties/components/persona/NoteCard.vue'; //import pointBuy from ''; //import proficiency from ''; //import propertySlot from ''; //import reference from ''; //import roll from ''; //import savingThrow from ''; -//import skill from ''; +import skill from '/imports/ui/properties/components/skills/SkillListTile.vue'; //import slotFiller from ''; //import spellList from ''; //import spell from ''; @@ -32,27 +32,27 @@ export default { action, //adjustment, attribute, - //buff, + buff, //buffRemover, //branch, //constant, - //container, + container, //class: classComponent, //classLevel, //damage, //damageMultiplier, //effect, - //feature, + feature, //folder, - //item, - //note, + item, + note, //pointBuy, //proficiency, //propertySlot, //reference, //roll, //savingThrow, - //skill, + skill, //slotFiller, //spellList, //spell, diff --git a/app/imports/ui/properties/components/inventory/ItemListTile.vue b/app/imports/ui/properties/components/inventory/ItemListTile.vue index 5dfa1b8e..72fb09c6 100644 --- a/app/imports/ui/properties/components/inventory/ItemListTile.vue +++ b/app/imports/ui/properties/components/inventory/ItemListTile.vue @@ -32,7 +32,7 @@ @change="changeQuantity" /> - + Date: Wed, 9 Nov 2022 15:02:41 +0200 Subject: [PATCH 18/46] Fixed log of recovering HD not having names --- app/imports/api/creature/creatures/methods/restCreature.js | 1 + 1 file changed, 1 insertion(+) diff --git a/app/imports/api/creature/creatures/methods/restCreature.js b/app/imports/api/creature/creatures/methods/restCreature.js index b943a584..7ca7c16c 100644 --- a/app/imports/api/creature/creatures/methods/restCreature.js +++ b/app/imports/api/creature/creatures/methods/restCreature.js @@ -137,6 +137,7 @@ function resetHitDice(creatureId, actionContext) { inactive: { $ne: true }, }, { fields: { + name: 1, hitDiceSize: 1, damage: 1, total: 1, From c55d572134faeef25a1ead797820eb5b9060b4fa Mon Sep 17 00:00:00 2001 From: Stefan Zermatten Date: Wed, 16 Nov 2022 23:52:08 +0200 Subject: [PATCH 19/46] Bumped version --- app/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/package.json b/app/package.json index e4cc506b..ba3a017b 100644 --- a/app/package.json +++ b/app/package.json @@ -1,6 +1,6 @@ { "name": "dicecloud", - "version": "2.0.43", + "version": "2.0.44", "description": "Unofficial Online Realtime D&D 5e App", "license": "GPL-3.0", "repository": { From 0f3a96da174b63bce330bfc6782f1577bfee3ad8 Mon Sep 17 00:00:00 2001 From: Stefan Zermatten Date: Fri, 18 Nov 2022 14:21:22 +0200 Subject: [PATCH 20/46] Spell list ability modifier can take non-abilities defaults to .value if .modifier is undefined is now undefined for no .modifier or .value --- .../computeComputation/computeByType/computeSpellList.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/app/imports/api/engine/computation/computeComputation/computeByType/computeSpellList.js b/app/imports/api/engine/computation/computeComputation/computeByType/computeSpellList.js index 63dc6003..499ddb97 100644 --- a/app/imports/api/engine/computation/computeComputation/computeByType/computeSpellList.js +++ b/app/imports/api/engine/computation/computeComputation/computeByType/computeSpellList.js @@ -2,5 +2,9 @@ export default function computeSpelllist(computation, node) { const prop = node.data; const ability = computation.scope[prop.ability]; - prop.abilityMod = ability?.modifier || 0; -} \ No newline at end of file + if (Number.isFinite(ability?.modifier)) { + prop.abilityMod = ability.modifier; + } else if (Number.isFinite(ability?.value)) { + prop.abilityMod = ability.value; + } +} From 060b5f93cab4e35a0798e85ea198c4051d0b6c47 Mon Sep 17 00:00:00 2001 From: Stefan Zermatten Date: Sat, 19 Nov 2022 17:19:07 +0200 Subject: [PATCH 21/46] Reduced bundle size and updates packages --- app/.meteor/packages | 24 +- app/.meteor/release | 2 +- app/.meteor/versions | 56 +- app/imports/api/files/s3FileStorage.js | 34 +- app/imports/api/files/server/s3.js | 2 + app/imports/api/sharing/sharingPermissions.js | 12 +- .../character/characterSheetTabs/StatsTab.vue | 1 + app/imports/ui/dialogStack/DialogStack.vue | 2 - app/imports/ui/vuetify.js | 3 +- app/package-lock.json | 512 +++--------------- app/package.json | 26 +- 11 files changed, 166 insertions(+), 508 deletions(-) create mode 100644 app/imports/api/files/server/s3.js diff --git a/app/.meteor/packages b/app/.meteor/packages index dc62173e..ff36c66b 100644 --- a/app/.meteor/packages +++ b/app/.meteor/packages @@ -4,27 +4,27 @@ # but you can also edit it by hand. accounts-password@2.3.1 -random@1.2.0 -underscore@1.0.10 +random@1.2.1 +underscore@1.0.11 dburles:mongo-collection-instances accounts-google@1.4.0 -email@2.2.1 +email@2.2.2 meteor-base@1.5.1 mobile-experience@1.1.0 -mongo@1.16.0 -session@1.2.0 -tracker@1.2.0 +mongo@1.16.1 +session@1.2.1 +tracker@1.2.1 logging@1.3.1 reload@1.3.1 -ejson@1.1.2 -check@1.3.1 +ejson@1.1.3 +check@1.3.2 standard-minifier-js@2.8.1 shell-server@0.5.0 -ecmascript@0.16.2 +ecmascript@0.16.3 es5-shim@4.8.0 -service-configuration@1.3.0 +service-configuration@1.3.1 dynamic-import@0.7.2 -ddp-rate-limiter@1.1.0 +ddp-rate-limiter@1.1.1 rate-limit@1.0.9 mdg:validated-method static-html@1.3.2 @@ -37,7 +37,6 @@ simple:rest simple:rest-method-mixin mikowals:batch-insert peerlibrary:subscription-data -seba:minifiers-autoprefixer zer0th:meteor-vuetify-loader akryum:vue-component akryum:vue-router2 @@ -49,3 +48,4 @@ simple:rest-json-error-handler littledata:synced-cron mdg:meteor-apm-agent typescript@4.5.4 +seba:minifiers-autoprefixer diff --git a/app/.meteor/release b/app/.meteor/release index 1d2a6d0f..42890181 100644 --- a/app/.meteor/release +++ b/app/.meteor/release @@ -1 +1 @@ -METEOR@2.8.0 +METEOR@2.8.1 diff --git a/app/.meteor/versions b/app/.meteor/versions index b9f87f06..3f632f0b 100644 --- a/app/.meteor/versions +++ b/app/.meteor/versions @@ -1,4 +1,4 @@ -accounts-base@2.2.4 +accounts-base@2.2.5 accounts-google@1.4.0 accounts-oauth@1.4.1 accounts-password@2.3.1 @@ -22,26 +22,26 @@ bozhao:link-accounts@2.6.1 caching-compiler@1.2.2 caching-html-compiler@1.2.1 callback-hook@1.4.0 -check@1.3.1 +check@1.3.2 coffeescript@2.4.1 coffeescript-compiler@2.4.1 dburles:mongo-collection-instances@0.3.6 -ddp@1.4.0 -ddp-client@2.6.0 +ddp@1.4.1 +ddp-client@2.6.1 ddp-common@1.4.0 -ddp-rate-limiter@1.1.0 +ddp-rate-limiter@1.1.1 ddp-server@2.6.0 -diff-sequence@1.1.1 +diff-sequence@1.1.2 dynamic-import@0.7.2 -ecmascript@0.16.2 +ecmascript@0.16.3 ecmascript-runtime@0.8.0 ecmascript-runtime-client@0.12.1 ecmascript-runtime-server@0.11.0 -ejson@1.1.2 -email@2.2.1 +ejson@1.1.3 +email@2.2.2 es5-shim@4.8.0 -fetch@0.1.1 -geojson-utils@1.0.10 +fetch@0.1.2 +geojson-utils@1.0.11 google-oauth@1.4.2 hot-code-push@1.0.4 html-tools@1.1.3 @@ -57,7 +57,7 @@ localstorage@1.2.0 logging@1.3.1 mdg:meteor-apm-agent@3.5.1 mdg:validated-method@1.2.0 -meteor@1.10.1 +meteor@1.10.2 meteor-base@1.5.1 meteortesting:browser-tests@1.3.5 meteortesting:mocha@2.0.3 @@ -68,20 +68,20 @@ minifier-js@2.7.5 minimongo@1.9.0 mobile-experience@1.1.0 mobile-status-bar@1.1.0 -modern-browsers@0.1.8 +modern-browsers@0.1.9 modules@0.19.0 -modules-runtime@0.13.0 -mongo@1.16.0 +modules-runtime@0.13.1 +mongo@1.16.1 mongo-decimal@0.1.3 mongo-dev-server@1.1.0 mongo-id@1.0.8 mongo-livedata@1.0.12 -npm-mongo@4.9.0 +npm-mongo@4.11.0 oauth@2.1.2 oauth2@1.3.1 ordered-dict@1.1.0 ostrio:cookies@2.7.2 -ostrio:files@2.3.0 +ostrio:files@2.3.2 patreon-oauth@0.1.0 peerlibrary:assert@0.3.0 peerlibrary:check-extension@0.7.0 @@ -93,20 +93,20 @@ peerlibrary:reactive-mongo@0.4.1 peerlibrary:reactive-publish@0.10.0 peerlibrary:server-autorun@0.8.0 peerlibrary:subscription-data@0.8.0 -percolate:migrations@1.0.3 -promise@0.12.0 +percolate:migrations@1.1.0 +promise@0.12.1 raix:eventemitter@1.0.0 -random@1.2.0 +random@1.2.1 rate-limit@1.0.9 react-fast-refresh@0.2.3 -reactive-dict@1.3.0 -reactive-var@1.0.11 +reactive-dict@1.3.1 +reactive-var@1.0.12 reload@1.3.1 retry@1.1.0 routepolicy@1.1.1 seba:minifiers-autoprefixer@2.0.1 -service-configuration@1.3.0 -session@1.2.0 +service-configuration@1.3.1 +session@1.2.1 sha@1.0.9 shell-server@0.5.0 simple:json-routes@2.3.1 @@ -120,10 +120,10 @@ standard-minifier-js@2.8.1 static-html@1.3.2 templating-tools@1.2.2 tmeasday:check-npm-versions@1.0.2 -tracker@1.2.0 +tracker@1.2.1 typescript@4.5.4 -underscore@1.0.10 +underscore@1.0.11 url@1.3.2 -webapp@1.13.1 -webapp-hashing@1.1.0 +webapp@1.13.2 +webapp-hashing@1.1.1 zer0th:meteor-vuetify-loader@0.1.41 diff --git a/app/imports/api/files/s3FileStorage.js b/app/imports/api/files/s3FileStorage.js index 3c3c540e..7103d044 100644 --- a/app/imports/api/files/s3FileStorage.js +++ b/app/imports/api/files/s3FileStorage.js @@ -4,7 +4,9 @@ import { each, clone } from 'lodash'; import { Random } from 'meteor/random'; import { FilesCollection } from 'meteor/ostrio:files'; import stream from 'stream'; -import S3 from 'aws-sdk/clients/s3'; +if (Meteor.isServer) { + import S3 from '/imports/api/files/server/s3.js'; +} /* See fs-extra and graceful-fs NPM packages */ /* For better i/o performance */ @@ -21,7 +23,7 @@ Meteor.settings.useS3 = !!( s3Conf && s3Conf.key && s3Conf.secret && s3Conf.bucket && s3Conf.endpoint ); -const bound = Meteor.bindEnvironment((callback) => { +const bound = Meteor.bindEnvironment((callback) => { return callback(); }); @@ -43,14 +45,14 @@ if (Meteor.isServer && Meteor.settings.useS3) { } }); - createS3FilesCollection = function({ + createS3FilesCollection = function ({ collectionName, storagePath, onBeforeUpload, onAfterUpload, debug = !Meteor.isProduction, allowClientCode = false, - }){ + }) { const collection = new FilesCollection({ collectionName, storagePath, @@ -58,7 +60,7 @@ if (Meteor.isServer && Meteor.settings.useS3) { onAfterUpload(fileRef) { // Call the provided afterUpload hook first onAfterUpload?.(fileRef); - + // Start moving files to AWS:S3 // after fully received by the Meteor server @@ -128,19 +130,19 @@ if (Meteor.isServer && Meteor.settings.useS3) { }; if (http.request.headers.range) { - const vRef = fileRef.versions[version]; - let range = clone(http.request.headers.range); + const vRef = fileRef.versions[version]; + let range = clone(http.request.headers.range); const array = range.split(/bytes=([0-9]*)-([0-9]*)/); const start = parseInt(array[1]); - let end = parseInt(array[2]); + let end = parseInt(array[2]); if (isNaN(end)) { // Request data from AWS:S3 by small chunks - end = (start + this.chunkSize) - 1; + end = (start + this.chunkSize) - 1; if (end >= vRef.size) { - end = vRef.size - 1; + end = vRef.size - 1; } } - opts.Range = `bytes=${start}-${end}`; + opts.Range = `bytes=${start}-${end}`; http.request.headers.range = `bytes=${start}-${end}`; } @@ -198,9 +200,9 @@ if (Meteor.isServer && Meteor.settings.useS3) { _origRemove.call(this, search); }; - collection.readJSONFile = async function(file){ + collection.readJSONFile = async function (file) { // If there is the pipepath, use s3 to get the file - if (file?.versions?.original?.meta?.pipePath){ + if (file?.versions?.original?.meta?.pipePath) { const path = file.versions.original.meta.pipePath; const data = await s3.getObject({ Bucket: s3Conf.bucket, @@ -217,14 +219,14 @@ if (Meteor.isServer && Meteor.settings.useS3) { return collection; } } else { - createS3FilesCollection = function({ + createS3FilesCollection = function ({ collectionName, storagePath, onBeforeUpload, onAfterUpload, debug = !Meteor.isProduction, allowClientCode = false, - }){ + }) { const collection = new FilesCollection({ collectionName, storagePath, @@ -236,7 +238,7 @@ if (Meteor.isServer && Meteor.settings.useS3) { if (Meteor.isServer) { // Use the normal file system to read files - collection.readJSONFile = async function(file){ + collection.readJSONFile = async function (file) { const fileString = await fsp.readFile(file.path, 'utf8'); return JSON.parse(fileString); }; diff --git a/app/imports/api/files/server/s3.js b/app/imports/api/files/server/s3.js new file mode 100644 index 00000000..83719fa6 --- /dev/null +++ b/app/imports/api/files/server/s3.js @@ -0,0 +1,2 @@ +import S3 from 'aws-sdk/clients/s3'; +export default S3; diff --git a/app/imports/api/sharing/sharingPermissions.js b/app/imports/api/sharing/sharingPermissions.js index 5591c59c..c01675f4 100644 --- a/app/imports/api/sharing/sharingPermissions.js +++ b/app/imports/api/sharing/sharingPermissions.js @@ -1,4 +1,4 @@ -import { _ } from 'meteor/underscore'; +import { includes } from 'lodash'; import fetchDocByRef from '/imports/api/parenting/fetchDocByRef.js'; function assertIdValid(userId) { @@ -50,7 +50,7 @@ export function assertEditPermission(doc, userId) { // Ensure the user is authorized for this specific document if ( doc.owner === userId || - _.contains(doc.writers, userId) + includes(doc.writers, userId) ) { return true; } else { @@ -82,11 +82,11 @@ export function assertCopyPermission(doc, userId) { // Ensure the user is authorized for this specific document if ( doc.owner === userId || - _.contains(doc.writers, userId) + includes(doc.writers, userId) ) { return true; } else if ( - (_.contains(doc.readers, userId) || doc.public) && + (includes(doc.readers, userId) || doc.public) && doc.readersCanCopy ) { return true; @@ -134,8 +134,8 @@ export function assertViewPermission(doc, userId) { if ( doc.owner === userId || - _.contains(doc.readers, userId) || - _.contains(doc.writers, userId) + includes(doc.readers, userId) || + includes(doc.writers, userId) ) { return true; } else { diff --git a/app/imports/ui/creature/character/characterSheetTabs/StatsTab.vue b/app/imports/ui/creature/character/characterSheetTabs/StatsTab.vue index 78847e45..ac9b3711 100644 --- a/app/imports/ui/creature/character/characterSheetTabs/StatsTab.vue +++ b/app/imports/ui/creature/character/characterSheetTabs/StatsTab.vue @@ -1,5 +1,6 @@ diff --git a/app/imports/ui/layouts/SingleCardLayout.vue b/app/imports/client/ui/layouts/SingleCardLayout.vue similarity index 100% rename from app/imports/ui/layouts/SingleCardLayout.vue rename to app/imports/client/ui/layouts/SingleCardLayout.vue diff --git a/app/imports/ui/library/InsertLibraryNodeButton.vue b/app/imports/client/ui/library/InsertLibraryNodeButton.vue similarity index 100% rename from app/imports/ui/library/InsertLibraryNodeButton.vue rename to app/imports/client/ui/library/InsertLibraryNodeButton.vue diff --git a/app/imports/ui/library/LibraryAndNode.vue b/app/imports/client/ui/library/LibraryAndNode.vue similarity index 87% rename from app/imports/ui/library/LibraryAndNode.vue rename to app/imports/client/ui/library/LibraryAndNode.vue index aa6601db..781fcac8 100644 --- a/app/imports/ui/library/LibraryAndNode.vue +++ b/app/imports/client/ui/library/LibraryAndNode.vue @@ -80,18 +80,18 @@ diff --git a/app/imports/ui/properties/treeNodeViews/SavingThrowTreeNode.vue b/app/imports/client/ui/properties/treeNodeViews/SavingThrowTreeNode.vue similarity index 89% rename from app/imports/ui/properties/treeNodeViews/SavingThrowTreeNode.vue rename to app/imports/client/ui/properties/treeNodeViews/SavingThrowTreeNode.vue index 10767f99..63bbdbe5 100644 --- a/app/imports/ui/properties/treeNodeViews/SavingThrowTreeNode.vue +++ b/app/imports/client/ui/properties/treeNodeViews/SavingThrowTreeNode.vue @@ -17,7 +17,7 @@ diff --git a/app/imports/ui/properties/forms/shared/propertyFormIndex.js b/app/imports/ui/properties/forms/shared/propertyFormIndex.js deleted file mode 100644 index ef29ea2d..00000000 --- a/app/imports/ui/properties/forms/shared/propertyFormIndex.js +++ /dev/null @@ -1,61 +0,0 @@ -import ActionForm from '/imports/ui/properties/forms/ActionForm.vue'; -import AdjustmentForm from '/imports/ui/properties/forms/AdjustmentForm.vue'; -import AttributeForm from '/imports/ui/properties/forms/AttributeForm.vue'; -import BuffForm from '/imports/ui/properties/forms/BuffForm.vue'; -import BuffRemoverForm from '/imports/ui/properties/forms/BuffRemoverForm.vue'; -import BranchForm from '/imports/ui/properties/forms/BranchForm.vue'; -import ClassForm from '/imports/ui/properties/forms/ClassForm.vue'; -import ClassLevelForm from '/imports/ui/properties/forms/ClassLevelForm.vue'; -import ConstantForm from '/imports/ui/properties/forms/ConstantForm.vue'; -import ContainerForm from '/imports/ui/properties/forms/ContainerForm.vue'; -import DamageForm from '/imports/ui/properties/forms/DamageForm.vue'; -import DamageMultiplierForm from '/imports/ui/properties/forms/DamageMultiplierForm.vue'; -import EffectForm from '/imports/ui/properties/forms/EffectForm.vue'; -import FeatureForm from '/imports/ui/properties/forms/FeatureForm.vue'; -import FolderForm from '/imports/ui/properties/forms/FolderForm.vue'; -import ItemForm from '/imports/ui/properties/forms/ItemForm.vue'; -import NoteForm from '/imports/ui/properties/forms/NoteForm.vue'; -import PointBuyForm from '/imports/ui/properties/forms/PointBuyForm.vue'; -import ProficiencyForm from '/imports/ui/properties/forms/ProficiencyForm.vue'; -import ReferenceForm from '/imports/ui/properties/forms/ReferenceForm.vue'; -import RollForm from '/imports/ui/properties/forms/RollForm.vue'; -import SavingThrowForm from '/imports/ui/properties/forms/SavingThrowForm.vue'; -import SkillForm from '/imports/ui/properties/forms/SkillForm.vue'; -import SlotForm from '/imports/ui/properties/forms/SlotForm.vue'; -import SlotFillerForm from '/imports/ui/properties/forms/SlotFillerForm.vue'; -import SpellListForm from '/imports/ui/properties/forms/SpellListForm.vue'; -import SpellForm from '/imports/ui/properties/forms/SpellForm.vue'; -import ToggleForm from '/imports/ui/properties/forms/ToggleForm.vue'; -import TriggerForm from '/imports/ui/properties/forms/TriggerForm.vue'; - -export default { - action: ActionForm, - adjustment: AdjustmentForm, - attribute: AttributeForm, - buff: BuffForm, - buffRemover: BuffRemoverForm, - branch: BranchForm, - constant: ConstantForm, - container: ContainerForm, - class: ClassForm, - classLevel: ClassLevelForm, - damage: DamageForm, - damageMultiplier: DamageMultiplierForm, - effect: EffectForm, - feature: FeatureForm, - folder: FolderForm, - item: ItemForm, - note: NoteForm, - pointBuy: PointBuyForm, - proficiency: ProficiencyForm, - propertySlot: SlotForm, - reference: ReferenceForm, - roll: RollForm, - savingThrow: SavingThrowForm, - skill: SkillForm, - slotFiller: SlotFillerForm, - spellList: SpellListForm, - spell: SpellForm, - toggle: ToggleForm, - trigger: TriggerForm, -}; diff --git a/app/imports/ui/properties/treeNodeViews/treeNodeViewIndex.js b/app/imports/ui/properties/treeNodeViews/treeNodeViewIndex.js deleted file mode 100644 index 1f9772e8..00000000 --- a/app/imports/ui/properties/treeNodeViews/treeNodeViewIndex.js +++ /dev/null @@ -1,23 +0,0 @@ -import DefaultTreeNode from '/imports/ui/properties/treeNodeViews/DefaultTreeNode.vue'; -import AdjustmentTreeNode from '/imports/ui/properties/treeNodeViews/AdjustmentTreeNode.vue'; -import BranchTreeNode from '/imports/ui/properties/treeNodeViews/BranchTreeNode.vue'; -import ItemTreeNode from '/imports/ui/properties/treeNodeViews/ItemTreeNode.vue'; -import DamageTreeNode from '/imports/ui/properties/treeNodeViews/DamageTreeNode.vue'; -import EffectTreeNode from '/imports/ui/properties/treeNodeViews/EffectTreeNode.vue'; -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, - adjustment: AdjustmentTreeNode, - branch: BranchTreeNode, - classLevel: ClassLevelTreeNode, - damage: DamageTreeNode, - effect: EffectTreeNode, - item: ItemTreeNode, - proficiency: ProficiencyTreeNode, - reference: ReferenceTreeNode, - savingThrow: SavingThrowTreeNode, -} diff --git a/app/imports/ui/properties/viewers/shared/propertyViewerIndex.js b/app/imports/ui/properties/viewers/shared/propertyViewerIndex.js deleted file mode 100644 index 70151b48..00000000 --- a/app/imports/ui/properties/viewers/shared/propertyViewerIndex.js +++ /dev/null @@ -1,61 +0,0 @@ -import ActionViewer from '/imports/ui/properties/viewers/ActionViewer.vue'; -import AdjustmentViewer from '/imports/ui/properties/viewers/AdjustmentViewer.vue'; -import AttributeViewer from '/imports/ui/properties/viewers/AttributeViewer.vue'; -import BuffViewer from '/imports/ui/properties/viewers/BuffViewer.vue'; -import BuffRemoverViewer from '/imports/ui/properties/viewers/BuffRemoverViewer.vue'; -import BranchViewer from '/imports/ui/properties/viewers/BranchViewer.vue'; -import ContainerViewer from '/imports/ui/properties/viewers/ContainerViewer.vue'; -import ClassViewer from '/imports/ui/properties/viewers/ClassViewer.vue'; -import ClassLevelViewer from '/imports/ui/properties/viewers/ClassLevelViewer.vue'; -import ConstantViewer from '/imports/ui/properties/viewers/ConstantViewer.vue'; -import DamageViewer from '/imports/ui/properties/viewers/DamageViewer.vue'; -import DamageMultiplierViewer from '/imports/ui/properties/viewers/DamageMultiplierViewer.vue'; -import EffectViewer from '/imports/ui/properties/viewers/EffectViewer.vue'; -import FeatureViewer from '/imports/ui/properties/viewers/FeatureViewer.vue'; -import FolderViewer from '/imports/ui/properties/viewers/FolderViewer.vue'; -import ItemViewer from '/imports/ui/properties/viewers/ItemViewer.vue'; -import NoteViewer from '/imports/ui/properties/viewers/NoteViewer.vue'; -import PointBuyViewer from '/imports/ui/properties/viewers/PointBuyViewer.vue'; -import ProficiencyViewer from '/imports/ui/properties/viewers/ProficiencyViewer.vue'; -import ReferenceViewer from '/imports/ui/properties/viewers/ReferenceViewer.vue'; -import RollViewer from '/imports/ui/properties/viewers/RollViewer.vue'; -import SkillViewer from '/imports/ui/properties/viewers/SkillViewer.vue'; -import SavingThrowViewer from '/imports/ui/properties/viewers/SavingThrowViewer.vue'; -import SlotViewer from '/imports/ui/properties/viewers/SlotViewer.vue'; -import SlotFillerViewer from '/imports/ui/properties/viewers/SlotFillerViewer.vue'; -import SpellListViewer from '/imports/ui/properties/viewers/SpellListViewer.vue'; -import SpellViewer from '/imports/ui/properties/viewers/SpellViewer.vue'; -import ToggleViewer from '/imports/ui/properties/viewers/ToggleViewer.vue'; -import TriggerViewer from '/imports/ui/properties/viewers/TriggerViewer.vue'; - -export default { - action: ActionViewer, - adjustment: AdjustmentViewer, - attribute: AttributeViewer, - buff: BuffViewer, - buffRemover: BuffRemoverViewer, - branch: BranchViewer, - container: ContainerViewer, - class: ClassViewer, - classLevel: ClassLevelViewer, - constant: ConstantViewer, - damage: DamageViewer, - damageMultiplier: DamageMultiplierViewer, - effect: EffectViewer, - feature: FeatureViewer, - folder: FolderViewer, - item: ItemViewer, - note: NoteViewer, - pointBuy: PointBuyViewer, - proficiency: ProficiencyViewer, - propertySlot: SlotViewer, - roll: RollViewer, - reference: ReferenceViewer, - savingThrow: SavingThrowViewer, - slotFiller: SlotFillerViewer, - skill: SkillViewer, - spellList: SpellListViewer, - spell: SpellViewer, - toggle: ToggleViewer, - trigger: TriggerViewer, -}; diff --git a/app/imports/ui/properties/viewers/shared/propertyViewerMixin.js b/app/imports/ui/properties/viewers/shared/propertyViewerMixin.js deleted file mode 100644 index de4278f2..00000000 --- a/app/imports/ui/properties/viewers/shared/propertyViewerMixin.js +++ /dev/null @@ -1,23 +0,0 @@ -import PropertyName from '/imports/ui/properties/viewers/shared/PropertyName.vue'; -import PropertyVariableName from '/imports/ui/properties/viewers/shared/PropertyVariableName.vue'; -import PropertyField from '/imports/ui/properties/viewers/shared/PropertyField.vue'; -import PropertyDescription from '/imports/ui/properties/viewers/shared/PropertyDescription.vue'; -import PropertyTags from '/imports/ui/properties/viewers/shared/PropertyTags.vue'; - -const propertyViewerMixin = { - components: { - PropertyName, - PropertyVariableName, - PropertyField, - PropertyDescription, - PropertyTags, - }, - props: { - model: { - type: Object, - required: true, - }, - }, -}; - -export default propertyViewerMixin; From 428aeef6352351259854dacd5e9d539d192dcf32 Mon Sep 17 00:00:00 2001 From: Stefan Zermatten Date: Sat, 19 Nov 2022 17:56:36 +0200 Subject: [PATCH 23/46] Removed HMR test text --- .../client/ui/creature/character/characterSheetTabs/StatsTab.vue | 1 - 1 file changed, 1 deletion(-) diff --git a/app/imports/client/ui/creature/character/characterSheetTabs/StatsTab.vue b/app/imports/client/ui/creature/character/characterSheetTabs/StatsTab.vue index 259001b7..50b880ee 100644 --- a/app/imports/client/ui/creature/character/characterSheetTabs/StatsTab.vue +++ b/app/imports/client/ui/creature/character/characterSheetTabs/StatsTab.vue @@ -1,6 +1,5 @@ @@ -190,6 +210,7 @@ import CharacterErrors from '/imports/client/ui/creature/character/errors/Charac import { snackbar } from '/imports/client/ui/components/snackbars/SnackbarQueue.js'; import updateCreatureProperty from '/imports/api/creature/creatureProperties/methods/updateCreatureProperty.js'; import getPropertyTitle from '/imports/client/ui/properties/shared/getPropertyTitle.js'; +import tabFoldersMixin from '/imports/client/ui/properties/components/folders/tabFoldersMixin.js'; function traverse(tree, callback, parents = []){ tree.forEach(node => { @@ -204,12 +225,23 @@ export default { BuildTreeNodeList, SlotCardsToFill, }, + mixins: [tabFoldersMixin], props: { creatureId: { type: String, required: true, }, }, + data() { + return { + tabName: 'build', + cols: { + cols: '12', + md: '6', + xl: '4', + } + }; + }, computed: { highestLevels(){ let highestLevels = {}; diff --git a/app/imports/client/ui/creature/character/characterSheetTabs/FeaturesTab.vue b/app/imports/client/ui/creature/character/characterSheetTabs/FeaturesTab.vue index 8630a938..11144684 100644 --- a/app/imports/client/ui/creature/character/characterSheetTabs/FeaturesTab.vue +++ b/app/imports/client/ui/creature/character/characterSheetTabs/FeaturesTab.vue @@ -1,6 +1,14 @@ @@ -19,18 +35,25 @@ import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js'; import ColumnLayout from '/imports/client/ui/components/ColumnLayout.vue'; import FeatureCard from '/imports/client/ui/properties/components/features/FeatureCard.vue'; +import tabFoldersMixin from '/imports/client/ui/properties/components/folders/tabFoldersMixin.js'; export default { components: { ColumnLayout, FeatureCard, }, + mixins: [tabFoldersMixin], props: { creatureId: { type: String, required: true, }, }, + data() { + return { + tabName: 'features', + }; + }, meteor: { features() { return CreatureProperties.find({ diff --git a/app/imports/client/ui/creature/character/characterSheetTabs/InventoryTab.vue b/app/imports/client/ui/creature/character/characterSheetTabs/InventoryTab.vue index b78e0d73..89c4d59b 100644 --- a/app/imports/client/ui/creature/character/characterSheetTabs/InventoryTab.vue +++ b/app/imports/client/ui/creature/character/characterSheetTabs/InventoryTab.vue @@ -1,6 +1,14 @@ @@ -101,6 +117,7 @@ import BUILT_IN_TAGS from '/imports/constants/BUILT_IN_TAGS.js'; import CoinValue from '/imports/client/ui/components/CoinValue.vue'; import stripFloatingPointOddities from '/imports/api/engine/computation/utility/stripFloatingPointOddities.js'; import CreatureVariables from '/imports/api/creature/creatures/CreatureVariables.js'; +import tabFoldersMixin from '/imports/client/ui/properties/components/folders/tabFoldersMixin.js'; export default { components: { @@ -110,6 +127,7 @@ export default { ItemList, CoinValue, }, + mixins: [tabFoldersMixin], props: { creatureId: { type: String, @@ -119,7 +137,8 @@ export default { data() { return { organize: false, - } + tabName: 'inventory', + }; }, meteor: { containers() { @@ -216,15 +235,6 @@ export default { ); }, }, - methods: { - clickProperty(_id) { - this.$store.commit('pushDialogStack', { - component: 'creature-property-dialog', - elementId: `tree-node-${_id}`, - data: { _id }, - }); - }, - }, } diff --git a/app/imports/client/ui/creature/character/characterSheetTabs/JournalTab.vue b/app/imports/client/ui/creature/character/characterSheetTabs/JournalTab.vue index 4bc52058..2679265a 100644 --- a/app/imports/client/ui/creature/character/characterSheetTabs/JournalTab.vue +++ b/app/imports/client/ui/creature/character/characterSheetTabs/JournalTab.vue @@ -1,6 +1,14 @@ @@ -22,6 +38,7 @@ import Creatures from '/imports/api/creature/creatures/Creatures.js'; import NoteCard from '/imports/client/ui/properties/components/persona/NoteCard.vue'; import CreatureSummary from '/imports/client/ui/creature/character/CreatureSummary.vue'; import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js'; +import tabFoldersMixin from '/imports/client/ui/properties/components/folders/tabFoldersMixin.js'; export default { components: { @@ -29,12 +46,18 @@ export default { CreatureSummary, NoteCard, }, + mixins: [tabFoldersMixin], props: { creatureId: { type: String, required: true, }, }, + data() { + return { + tabName: 'journal', + }; + }, meteor: { notes(){ return CreatureProperties.find({ diff --git a/app/imports/client/ui/creature/character/characterSheetTabs/SpellsTab.vue b/app/imports/client/ui/creature/character/characterSheetTabs/SpellsTab.vue index 14a5ff5c..12c65775 100644 --- a/app/imports/client/ui/creature/character/characterSheetTabs/SpellsTab.vue +++ b/app/imports/client/ui/creature/character/characterSheetTabs/SpellsTab.vue @@ -1,6 +1,14 @@ @@ -27,6 +43,7 @@ import ColumnLayout from '/imports/client/ui/components/ColumnLayout.vue'; import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties.js'; import SpellListCard from '/imports/client/ui/properties/components/spells/SpellListCard.vue'; import SpellList from '/imports/client/ui/properties/components/spells/SpellList.vue'; +import tabFoldersMixin from '/imports/client/ui/properties/components/folders/tabFoldersMixin.js'; export default { components: { @@ -34,6 +51,7 @@ export default { SpellList, SpellListCard, }, + mixins: [tabFoldersMixin], props: { creatureId: { type: String, @@ -43,6 +61,7 @@ export default { data() { return { organize: false, + tabName: 'spells', } }, meteor: { @@ -92,15 +111,6 @@ export default { return this.spellLists.map(spellList => spellList._id); }, }, - methods: { - clickProperty(_id) { - this.$store.commit('pushDialogStack', { - component: 'creature-property-dialog', - elementId: `spell-list-tile-${_id}`, - data: { _id }, - }); - }, - }, } diff --git a/app/imports/client/ui/creature/character/characterSheetTabs/StatsTab.vue b/app/imports/client/ui/creature/character/characterSheetTabs/StatsTab.vue index 50b880ee..2da6dfb5 100644 --- a/app/imports/client/ui/creature/character/characterSheetTabs/StatsTab.vue +++ b/app/imports/client/ui/creature/character/characterSheetTabs/StatsTab.vue @@ -1,12 +1,15 @@ @@ -85,16 +142,20 @@ import TreeTab from '/imports/client/ui/creature/character/characterSheetTabs/Tr import { assertEditPermission } from '/imports/api/creature/creatures/creaturePermissions.js'; import CreatureLogs from '/imports/api/creature/log/CreatureLogs.js'; import { snackbar } from '/imports/client/ui/components/snackbars/SnackbarQueue.js'; +import CharacterSheetFab from '/imports/client/ui/creature/character/CharacterSheetFab.vue'; +import ActionsTab from '/imports/client/ui/creature/character/characterSheetTabs/ActionsTab.vue'; export default { components: { StatsTab, FeaturesTab, - InventoryTab, + ActionsTab, SpellsTab, + InventoryTab, CharacterTab, BuildTab, TreeTab, + CharacterSheetFab, }, props: { creatureId: { @@ -171,6 +232,19 @@ export default { } + + + + diff --git a/app/imports/client/ui/components/global/globalIndex.js b/app/imports/client/ui/components/global/globalIndex.js index bb0898d5..f7e48b3e 100644 --- a/app/imports/client/ui/components/global/globalIndex.js +++ b/app/imports/client/ui/components/global/globalIndex.js @@ -1,6 +1,7 @@ import Vue from 'vue'; // Global components import DatePicker from '/imports/client/ui/components/global/DatePicker.vue'; +import DragHandle from '/imports/client/ui/components/global/DragHandle.vue'; import IconPicker from '/imports/client/ui/components/global/IconPicker.vue'; import TextField from '/imports/client/ui/components/global/TextField.vue'; import TextArea from '/imports/client/ui/components/global/TextArea.vue'; @@ -13,6 +14,7 @@ import SvgIcon from '/imports/client/ui/components/global/SvgIcon.vue'; import SmartSlider from '/imports/client/ui/components/global/SmartSlider.vue'; Vue.component('DatePicker', DatePicker); +Vue.component('DragHandle', DragHandle); Vue.component('IconPicker', IconPicker); Vue.component('TextField', TextField); Vue.component('TextArea', TextArea); diff --git a/app/imports/client/ui/components/tree/TreeNode.vue b/app/imports/client/ui/components/tree/TreeNode.vue index 2a8ee46e..9ef46561 100644 --- a/app/imports/client/ui/components/tree/TreeNode.vue +++ b/app/imports/client/ui/components/tree/TreeNode.vue @@ -30,14 +30,12 @@ :class="{'ml-4': startExpanded}" style="flex-grow: 0;" > - - mdi-drag - + /> diff --git a/app/imports/client/ui/creature/creatureList/CreatureListTile.vue b/app/imports/client/ui/creature/creatureList/CreatureListTile.vue index ee9a7d95..2685ff13 100644 --- a/app/imports/client/ui/creature/creatureList/CreatureListTile.vue +++ b/app/imports/client/ui/creature/creatureList/CreatureListTile.vue @@ -40,12 +40,9 @@ - - mdi-drag - + diff --git a/app/imports/client/ui/properties/components/inventory/ItemListTile.vue b/app/imports/client/ui/properties/components/inventory/ItemListTile.vue index cf252ddc..839db61d 100644 --- a/app/imports/client/ui/properties/components/inventory/ItemListTile.vue +++ b/app/imports/client/ui/properties/components/inventory/ItemListTile.vue @@ -33,13 +33,10 @@ /> - - mdi-drag - + /> diff --git a/app/imports/client/ui/properties/components/spells/SpellListTile.vue b/app/imports/client/ui/properties/components/spells/SpellListTile.vue index 6c47536f..2b20c73f 100644 --- a/app/imports/client/ui/properties/components/spells/SpellListTile.vue +++ b/app/imports/client/ui/properties/components/spells/SpellListTile.vue @@ -29,14 +29,11 @@ @click.native.stop="() => {}" @change="setPrepared" /> - - mdi-drag - + />