From c65c8f329923128d838c1e94a476b6b7442a581f Mon Sep 17 00:00:00 2001 From: Stefan Zermatten Date: Thu, 29 Apr 2021 16:13:22 +0200 Subject: [PATCH 01/42] Added note to attempt to keep children of reference nodes --- .../methods/insertPropertyFromLibraryNode.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/imports/api/creature/creatureProperties/methods/insertPropertyFromLibraryNode.js b/app/imports/api/creature/creatureProperties/methods/insertPropertyFromLibraryNode.js index 52a858c1..3af576c8 100644 --- a/app/imports/api/creature/creatureProperties/methods/insertPropertyFromLibraryNode.js +++ b/app/imports/api/creature/creatureProperties/methods/insertPropertyFromLibraryNode.js @@ -192,6 +192,9 @@ function reifyNodeReferences(nodes, visitedRefs = new Set(), depth = 0){ return true; }); + // TODO: Force the referencedNode to take the old id of the reference + // such that the reference's children can be kept + // Give the new referenced sub-tree new ids renewDocIds({ docArray: addedNodes, From 1ad1d1f23dfee876e522a6915953aaa962f0c9f3 Mon Sep 17 00:00:00 2001 From: Stefan Zermatten Date: Tue, 1 Jun 2021 12:34:51 +0200 Subject: [PATCH 02/42] Migrated from Google material design icons to vuetify default MDI --- app/client/head.html | 3 ++- app/imports/constants/PROPERTIES.js | 20 ++++++++--------- app/imports/constants/SVG_ICONS.js | 4 ---- app/imports/ui/components/ColorPicker.vue | 6 ++--- app/imports/ui/components/IncrementButton.vue | 2 +- app/imports/ui/components/IncrementMenu.vue | 14 ++++++------ .../ui/components/global/DatePicker.vue | 2 +- .../ui/components/global/IconPicker.vue | 14 ++++++------ app/imports/ui/components/propertyToolbar.vue | 14 ++++++------ .../ui/components/snackbars/SnackbarQueue.vue | 2 +- app/imports/ui/components/tree/TreeNode.vue | 4 ++-- app/imports/ui/creature/RestButton.vue | 2 +- .../creature/character/CharacterSheetFab.vue | 6 ++--- .../character/CharacterSheetToolbar.vue | 10 ++++----- .../characterSheetTabs/CharacterTab.vue | 4 ++-- .../character/characterSheetTabs/StatsTab.vue | 2 +- .../character/characterSheetTabs/TreeTab.vue | 2 +- .../creatureProperties/Breadcrumbs.vue | 2 +- .../CreaturePropertyDialog.vue | 2 +- .../experiences/ExperienceListDialog.vue | 6 ++--- .../ui/creature/slots/SlotFillDialog.vue | 2 +- app/imports/ui/creature/slots/Slots.vue | 4 ++-- app/imports/ui/dialogStack/DialogBase.vue | 2 +- .../ui/documentation/FunctionReference.vue | 2 +- app/imports/ui/layouts/Sidebar.vue | 19 +++++++++------- .../ui/library/InsertLibraryNodeButton.vue | 2 +- app/imports/ui/library/LibraryBrowser.vue | 4 ++-- app/imports/ui/library/LibraryEditDialog.vue | 4 ++-- .../ui/library/SingleLibraryToolbar.vue | 4 ++-- app/imports/ui/log/CharacterLog.vue | 2 +- app/imports/ui/pages/Account.vue | 6 ++--- app/imports/ui/pages/CharacterList.vue | 2 +- app/imports/ui/pages/Friends.vue | 2 +- app/imports/ui/pages/Home.vue | 6 ++--- app/imports/ui/pages/Tabletops.vue | 2 +- .../components/attributes/HitDiceListTile.vue | 4 ++-- .../components/attributes/ResourceCard.vue | 4 ++-- .../attributes/SpellSlotListTile.vue | 4 ++-- .../components/inventory/ItemListTile.vue | 2 +- .../components/skills/SkillListTile.vue | 8 +++---- .../components/skills/SkillProficiency.vue | 8 +++---- .../spells/CastSpellWithSlotDialog.vue | 4 ++-- .../components/spells/SpellListCard.vue | 2 +- .../components/spells/SpellListTile.vue | 4 ++-- .../forms/AttributesConsumedListForm.vue | 2 +- .../ui/properties/forms/EffectForm.vue | 2 +- .../forms/ItemsConsumedListForm.vue | 2 +- .../ui/properties/forms/ReferenceForm.vue | 4 ++-- .../ui/properties/forms/ResourcesForm.vue | 2 +- .../forms/shared/ProficiencySelect.vue | 12 +++++----- .../ui/properties/shared/ProficiencyIcon.vue | 6 ++--- .../treeNodeViews/DamageTreeNode.vue | 2 +- .../ui/properties/viewers/SkillViewer.vue | 8 +++---- app/imports/ui/sharing/ShareDialog.vue | 8 +++---- app/imports/ui/tabletop/TabletopComponent.vue | 2 +- .../ui/user/DeleteUserAccountDialog.vue | 6 ++--- app/imports/ui/utility/getEffectIcon.js | 22 +++++++++---------- app/imports/ui/vuetify.js | 2 +- 58 files changed, 152 insertions(+), 152 deletions(-) diff --git a/app/client/head.html b/app/client/head.html index 4984d688..85ef16df 100644 --- a/app/client/head.html +++ b/app/client/head.html @@ -1,5 +1,6 @@ - + + diff --git a/app/imports/constants/PROPERTIES.js b/app/imports/constants/PROPERTIES.js index bb60d32e..f06cc7b2 100644 --- a/app/imports/constants/PROPERTIES.js +++ b/app/imports/constants/PROPERTIES.js @@ -24,11 +24,11 @@ const PROPERTIES = Object.freeze({ name: 'Class level' }, constant: { - icon: 'anchor', + icon: 'mdi-anchor', name: 'Constant' }, container: { - icon: 'work', + icon: 'mdi-bag-personal-outline', name: 'Container' }, damage: { @@ -44,23 +44,23 @@ const PROPERTIES = Object.freeze({ name: 'Effect' }, feature: { - icon: 'subject', + icon: 'mdi-text-subject', name: 'Feature' }, folder: { - icon: 'folder', + icon: 'mdi-folder-outline', name: 'Folder' }, item: { - icon: '$vuetify.icons.item', + icon: 'mdi-cube-outline', name: 'Item' }, note: { - icon: 'note', + icon: 'mdi-note-outline', name: 'Note' }, proficiency: { - icon: 'radio_button_checked', + icon: 'mdi-brightness-1', name: 'Proficiency' }, roll: { @@ -68,7 +68,7 @@ const PROPERTIES = Object.freeze({ name: 'Roll' }, reference: { - icon: 'link', + icon: 'mdi-vector-link', name: 'Reference', libraryOnly: true, }, @@ -81,11 +81,11 @@ const PROPERTIES = Object.freeze({ name: 'Skill' }, propertySlot: { - icon: 'tab_unselected', + icon: 'mdi-power-socket-eu', name: 'Slot' }, slotFiller: { - icon: 'picture_in_picture', + icon: 'mdi-power-plug-outline', name: 'Slot filler' }, spellList: { diff --git a/app/imports/constants/SVG_ICONS.js b/app/imports/constants/SVG_ICONS.js index d988a2ee..00ab7353 100644 --- a/app/imports/constants/SVG_ICONS.js +++ b/app/imports/constants/SVG_ICONS.js @@ -35,10 +35,6 @@ const SVG_ICONS = Object.freeze({ name: 'cash', shape:'M251.813 20.5c-.78-.008-1.558.003-2.344.03-11.005.39-22.285 5.142-32.376 17.814l-5.156 6.468-7.063-4.343c-13.67-8.436-30.948-11.566-45.5-8.75-14.552 2.814-26.03 10.716-31.344 25.374l-3.624 9.968L115 62.22c-16.243-8.34-24.717-8.468-31.75-5.314-5.606 2.515-11.456 8.555-18.094 17.375L147.594 138c12.92 4.168 31.79 4.086 50.75-.813 18.95-4.895 37.863-14.44 51.625-27.406l31.124-76.905c-6.596-6.452-16.42-11.52-26.938-12.28-.778-.058-1.563-.087-2.344-.095zM265 121.28l-.594.595c-16.8 16.497-39.04 27.636-61.375 33.406-6.883 1.78-13.784 3.075-20.56 3.814-3.682 6.112-6.727 12.262-9.19 18.47l.532-.033c1.4 24.178 11.835 42.98 26.75 60.876l-14.343 11.97c-8.29-9.947-15.738-20.762-21.282-32.814-.92 23.227 4.222 47.218 12.78 72.22l-17.687 6.062c-15.615-45.618-20.942-92.383 1-136.375-7.386-.588-14.426-1.96-20.905-4.25l-.344-.126c-4.26 4.08-8.35 8.368-12.25 12.875-31.64 36.583-49.03 85.3-39.936 123.25 4.87 20.324 13.632 39.513 26.156 55.874 3.653-.298 7.256-.49 10.78-.53 11.585-.138 22.4 1.094 32.032 3.623 3.04.798 5.98 1.776 8.813 2.875 7.484-.797 15.245-1.218 23.22-1.218 28.474 0 54.303 5.297 73.843 14.47 7.586 3.56 14.44 7.848 20 12.81-.047-.742-.063-1.493-.063-2.25 0-4.568.85-8.905 2.438-13-8.938-8.382-14.532-18.546-14.532-30.187 0-16.12 10.75-29.346 26.22-39 .012-4.624.893-9.02 2.53-13.156-10.165-8.73-16.655-19.555-16.655-32.092 0-7.917 2.594-15.142 7.063-21.594-4.47-6.452-7.063-13.677-7.063-21.594 0-3.813.598-7.456 1.72-10.938-9.688-8.603-15.814-19.182-15.814-31.375 0-12.108 6.054-22.594 15.626-31.156-5.94-4.6-12.232-8.505-18.906-11.5zm111.438 2.345c-26.022 0-49.507 5.433-65.688 13.563-16.18 8.13-23.78 18.062-23.78 26.75 0 8.687 7.6 18.65 23.78 26.78 16.18 8.13 39.666 13.532 65.688 13.532 11.222 0 21.978-1.018 31.875-2.813v-17.03c13.862-2.068 25.41-5.9 32.812-10.782v17.563c.328-.16.678-.307 1-.47 16.18-8.13 23.813-18.093 23.813-26.78 0-8.687-7.632-18.62-23.813-26.75-16.18-8.13-39.666-13.563-65.688-13.563zm96.5 67.625c-3.334 3.582-7.283 6.86-11.657 9.844l.25 29.03c12.545-7.523 18.5-16.197 18.5-23.874 0-4.788-2.31-9.965-7.092-15zm-171.844 15.47c.265 8.576 7.887 18.325 23.78 26.31 16.182 8.13 39.667 13.532 65.688 13.532 16.316 0 31.636-2.13 44.782-5.718l.625-27.28c-17.166 6.025-37.58 9.374-59.533 9.374-28.558 0-54.474-5.69-74.062-15.532-.435-.218-.852-.464-1.28-.687zm1.97 34.81c-1.345 2.694-2.002 5.352-2.002 7.907 0 8.688 7.632 18.652 23.813 26.782 16.18 8.13 39.666 13.53 65.688 13.53 13.194 0 25.73-1.384 37-3.813v-24.062c-11.556 2.212-24.002 3.375-37 3.375-28.56 0-54.506-5.69-74.094-15.53-4.828-2.426-9.34-5.17-13.408-8.19zm174.967 0c-4.067 3.02-8.58 5.764-13.405 8.19-1.382.693-2.806 1.346-4.25 2V274c13.335-7.677 19.656-16.654 19.656-24.563 0-2.548-.662-5.218-2-7.906zm7.5 36.75c-2.03 2.028-4.236 3.956-6.624 5.783v32.468c10.314-7.004 15.25-14.77 15.25-21.75 0-5.266-2.816-10.994-8.625-16.5zm-170.124 14.064c-.13.817-.22 1.64-.22 2.437.002 8.69 7.633 18.62 23.814 26.75 16.18 8.13 39.666 13.564 65.688 13.564 14.953 0 29.064-1.775 41.437-4.844v-29.875c-16.304 5.196-35.265 8.063-55.563 8.063-28.558 0-54.505-5.69-74.093-15.532-.36-.18-.708-.378-1.064-.562zm-13.594 21.312c-8.665 6.538-12.843 13.626-12.843 20.03 0 8.69 7.63 18.653 23.81 26.783 16.182 8.13 39.667 13.53 65.69 13.53 9.54 0 18.72-.71 27.342-2.03v-18.19c-.377.003-.747 0-1.125 0-28.558 0-54.505-5.688-74.093-15.53-12.46-6.26-22.86-14.558-28.78-24.594zm162.532 30.688c-7.93 2.796-16.566 5.006-25.72 6.594V363c1.92-.813 3.78-1.65 5.532-2.53 9.82-4.935 16.47-10.537 20.188-16.126zM473.5 362.5c-3.77 3.77-8.21 7.184-13.125 10.25v28.688c13.335-7.678 19.656-16.655 19.656-24.563 0-4.59-2.138-9.537-6.53-14.375zm-348.72 2.813c-10.14.115-21.222 1.413-32.624 4.062-22.803 5.3-42.462 15.006-55.25 25.688-12.788 10.68-17.74 21.388-16.28 29.125 1.46 7.736 9.393 14.833 24.78 18.875 15.388 4.04 36.854 4.204 59.657-1.094 9.19-2.136 17.857-4.97 25.78-8.314V413.03c14.53-6.486 25.467-14.375 30.376-21.905V415.5c12.102-10.422 16.83-20.828 15.405-28.375-1.46-7.735-9.425-14.802-24.813-18.844-7.693-2.02-16.89-3.083-27.03-2.967zm64.657 5.218c2.71 3.895 4.61 8.28 5.532 13.158 1.128 5.983.634 11.85-1.126 17.53 9.822 2.56 18.812 6.073 26.656 10.47 4.39 2.46 8.47 5.268 12.125 8.343 9.022-2.163 16.573-5.152 21.938-8.686v20.53c16.183-7.61 23.5-16.89 23.5-24.312 0-7.43-7.334-16.726-23.563-24.343-16.054-7.537-39.295-12.58-65.063-12.69zm111.72 4.94c-.044.467-.094.944-.094 1.405 0 8.688 7.63 18.65 23.812 26.78 16.18 8.13 39.666 13.533 65.688 13.533 13.194 0 25.73-1.384 37-3.813V386.53c-14.77 4-31.43 6.19-49.094 6.19-28.56 0-54.507-5.72-74.095-15.564-1.086-.546-2.165-1.11-3.22-1.687zM304 412.81c-1.958 3.257-2.938 6.484-2.938 9.563 0 8.688 7.632 18.65 23.813 26.78 16.18 8.13 39.666 13.533 65.688 13.533 13.194 0 25.73-1.384 37-3.813V432.5c-11.556 2.212-24.002 3.375-37 3.375-28.56 0-54.506-5.69-74.094-15.53-4.47-2.246-8.643-4.777-12.47-7.533zm173.125 0c-3.832 2.762-8.025 5.283-12.5 7.532-1.382.694-2.806 1.347-4.25 2v24.594c13.335-7.678 19.656-16.655 19.656-24.563 0-3.077-.953-6.308-2.905-9.563zM184.5 418.22c-3.47 4.383-7.572 8.55-12.188 12.405-15.66 13.082-37.66 23.643-63 29.53-9.14 2.125-18.11 3.472-26.75 4.126 3.2 5.317 8.83 10.542 17.063 15.158 13.698 7.677 33.69 12.843 55.875 12.843 10.077 0 19.68-1.084 28.47-2.967v-20.844c13.858-2.068 25.41-5.903 32.81-10.783v18.407c10.023-6.95 14.845-14.89 14.845-22.375 0-8.723-6.552-18.043-20.25-25.72-7.35-4.12-16.532-7.518-26.875-9.78z', }, - 'cube': { - name: 'item', - shape: 'M256 24.585L51.47 118.989 256 213.394l204.53-94.405zM38.998 133.054v258.353L247 487.415V229.063zm434.004 0L265 229.062v258.353l208.002-96.008z', - }, 'electric': { name: 'action', shape: 'M376 211H256V16L136 301h120v195z', diff --git a/app/imports/ui/components/ColorPicker.vue b/app/imports/ui/components/ColorPicker.vue index eb385581..23b8463b 100644 --- a/app/imports/ui/components/ColorPicker.vue +++ b/app/imports/ui/components/ColorPicker.vue @@ -10,7 +10,7 @@ icon v-on="on" > - format_paint + mdi-format-paint @@ -30,7 +30,7 @@ v-if="kebabColor === colorOption" :class="{dark: isDark(colorOption, shade)}" > - check + mdi-check @@ -58,7 +58,7 @@ v-if="kebabShade === shadeOption" :class="isDark(color, shade) ? 'dark' : 'light'" > - check + mdi-check diff --git a/app/imports/ui/components/IncrementButton.vue b/app/imports/ui/components/IncrementButton.vue index 3189010c..3d7ad623 100644 --- a/app/imports/ui/components/IncrementButton.vue +++ b/app/imports/ui/components/IncrementButton.vue @@ -14,7 +14,7 @@ @click.stop > - add + mdi-plus diff --git a/app/imports/ui/components/IncrementMenu.vue b/app/imports/ui/components/IncrementMenu.vue index 24d46b91..b8f0ccb3 100644 --- a/app/imports/ui/components/IncrementMenu.vue +++ b/app/imports/ui/components/IncrementMenu.vue @@ -15,14 +15,14 @@ class="filled" @click="toggleAdd(); $nextTick(() => $refs.editInput.focus())" > - add + mdi-plus - remove + mdi-minus - done + mdi-check - close + mdi-close @@ -117,11 +117,11 @@ operationIcon(operation) { switch (operation) { case 'set': - return 'forward'; + return 'mdi-forward'; case 'add': - return 'add'; + return 'mdi-plus'; case 'subtract': - return 'remove'; + return 'mdi-minus'; } }, toggleAdd(){ diff --git a/app/imports/ui/components/global/DatePicker.vue b/app/imports/ui/components/global/DatePicker.vue index 808eb998..7b70e44a 100644 --- a/app/imports/ui/components/global/DatePicker.vue +++ b/app/imports/ui/components/global/DatePicker.vue @@ -11,7 +11,7 @@ - highlight_alt + mdi-select-search -
+
- - cancel - + clear
- arrow_back + mdi-arrow-left - more_vert + mdi-dots-vertical @@ -64,7 +64,7 @@ - file_copy + mdi-content-copy - send + mdi-send - delete + mdi-delete @@ -112,13 +112,13 @@ v-if="editing" key="doneIcon" > - done + mdi-check - create + mdi-pencil diff --git a/app/imports/ui/components/snackbars/SnackbarQueue.vue b/app/imports/ui/components/snackbars/SnackbarQueue.vue index 0cd0ecd6..78a6c550 100644 --- a/app/imports/ui/components/snackbars/SnackbarQueue.vue +++ b/app/imports/ui/components/snackbars/SnackbarQueue.vue @@ -32,7 +32,7 @@ v-bind="attrs" @click="closeSnackbar" > - close + mdi-close diff --git a/app/imports/ui/components/tree/TreeNode.vue b/app/imports/ui/components/tree/TreeNode.vue index 321347df..c535eb3e 100644 --- a/app/imports/ui/components/tree/TreeNode.vue +++ b/app/imports/ui/components/tree/TreeNode.vue @@ -18,7 +18,7 @@ @click.stop="expanded = !expanded" > - chevron_right + mdi-chevron-right
- drag_handle + mdi-drag - {{ type === 'shortRest' ? 'snooze' : 'bedtime' }} + {{ type === 'shortRest' ? 'mdi-music-rest-quarter' : 'mdi-bed' }} {{ type === 'shortRest' ? 'Short Rest' : 'Long Rest' }} diff --git a/app/imports/ui/creature/character/CharacterSheetFab.vue b/app/imports/ui/creature/character/CharacterSheetFab.vue index abcd83c4..bd8de830 100644 --- a/app/imports/ui/creature/character/CharacterSheetFab.vue +++ b/app/imports/ui/creature/character/CharacterSheetFab.vue @@ -20,7 +20,7 @@ style="transition: transform 0.2s ease-in-out" :style="fab && 'transform: rotate(45deg)'" > - add + mdi-plus @@ -42,7 +42,7 @@ color="primary" data-id="insert-creature-property-btn" label="New Property" - icon="create" + icon="mdi-pencil" :disabled="!editPermission" @click="insertTreeProperty" /> @@ -51,7 +51,7 @@ color="primary" data-id="insert-creature-property-from-library-btn" label="Property From Library" - icon="book" + icon="mdi-library-shelves" :disabled="!editPermission" @click="propertyFromLibrary" /> diff --git a/app/imports/ui/creature/character/CharacterSheetToolbar.vue b/app/imports/ui/creature/character/CharacterSheetToolbar.vue index 71b9b067..67a18d33 100644 --- a/app/imports/ui/creature/character/CharacterSheetToolbar.vue +++ b/app/imports/ui/creature/character/CharacterSheetToolbar.vue @@ -37,30 +37,30 @@ icon v-on="on" > - more_vert + mdi-dots-vertical - delete Delete + mdi-delete Delete - create Edit details + mdi-pencil Edit details - share Sharing + mdi-share-variant Sharing - delete Unshare with me + mdi-delete Unshare with me diff --git a/app/imports/ui/creature/character/characterSheetTabs/CharacterTab.vue b/app/imports/ui/creature/character/characterSheetTabs/CharacterTab.vue index fcfb2200..f3242ee1 100644 --- a/app/imports/ui/creature/character/characterSheetTabs/CharacterTab.vue +++ b/app/imports/ui/creature/character/characterSheetTabs/CharacterTab.vue @@ -73,7 +73,7 @@ data-id="experience-info-button" @click="showExperienceList" > - info + mdi-information-outline @@ -82,7 +82,7 @@ data-id="experience-add-button" @click="addExperience" > - add + mdi-plus diff --git a/app/imports/ui/creature/character/characterSheetTabs/StatsTab.vue b/app/imports/ui/creature/character/characterSheetTabs/StatsTab.vue index 0add6e64..273a534b 100644 --- a/app/imports/ui/creature/character/characterSheetTabs/StatsTab.vue +++ b/app/imports/ui/creature/character/characterSheetTabs/StatsTab.vue @@ -45,7 +45,7 @@ icon @click.stop="softRemove(buff._id)" > - delete + mdi-delete diff --git a/app/imports/ui/creature/character/characterSheetTabs/TreeTab.vue b/app/imports/ui/creature/character/characterSheetTabs/TreeTab.vue index 3c35ae41..b02a814c 100644 --- a/app/imports/ui/creature/character/characterSheetTabs/TreeTab.vue +++ b/app/imports/ui/creature/character/characterSheetTabs/TreeTab.vue @@ -28,7 +28,7 @@ slot="extension" v-model="filterString" :items="filterOptions" - prepend-inner-icon="search" + prepend-inner-icon="mdi-search" class="mx-4" hide-no-data hide-selected diff --git a/app/imports/ui/creature/creatureProperties/Breadcrumbs.vue b/app/imports/ui/creature/creatureProperties/Breadcrumbs.vue index c7f85f46..2cd94af0 100644 --- a/app/imports/ui/creature/creatureProperties/Breadcrumbs.vue +++ b/app/imports/ui/creature/creatureProperties/Breadcrumbs.vue @@ -8,7 +8,7 @@ v-if="index !== 0" :key="index" > - chevron_right + mdi-chevron-right - add + mdi-plus Property diff --git a/app/imports/ui/creature/experiences/ExperienceListDialog.vue b/app/imports/ui/creature/experiences/ExperienceListDialog.vue index 61e46ef1..8fa8d1e2 100644 --- a/app/imports/ui/creature/experiences/ExperienceListDialog.vue +++ b/app/imports/ui/creature/experiences/ExperienceListDialog.vue @@ -10,13 +10,13 @@ data-id="experience-add-button" @click="addExperience" > - add + mdi-plus - refresh + mdi-refresh
- delete + mdi-delete diff --git a/app/imports/ui/creature/slots/SlotFillDialog.vue b/app/imports/ui/creature/slots/SlotFillDialog.vue index 29c98f91..bda05e19 100644 --- a/app/imports/ui/creature/slots/SlotFillDialog.vue +++ b/app/imports/ui/creature/slots/SlotFillDialog.vue @@ -9,7 +9,7 @@ - delete + mdi-delete @@ -44,7 +44,7 @@ style="background-color: inherit;" @click="fillSlot(slot)" > - add + mdi-plus
diff --git a/app/imports/ui/dialogStack/DialogBase.vue b/app/imports/ui/dialogStack/DialogBase.vue index fda72bdd..426c7a54 100644 --- a/app/imports/ui/dialogStack/DialogBase.vue +++ b/app/imports/ui/dialogStack/DialogBase.vue @@ -18,7 +18,7 @@ icon @click="back" > - arrow_back + mdi-arrow-left diff --git a/app/imports/ui/documentation/FunctionReference.vue b/app/imports/ui/documentation/FunctionReference.vue index 078f526f..b8172fbd 100644 --- a/app/imports/ui/documentation/FunctionReference.vue +++ b/app/imports/ui/documentation/FunctionReference.vue @@ -18,7 +18,7 @@ {{ example.input }} - arrow_right_alt + mdi-arrow-right-thick {{ example.result }} diff --git a/app/imports/ui/layouts/Sidebar.vue b/app/imports/ui/layouts/Sidebar.vue index bab7781d..8a8f7c7b 100644 --- a/app/imports/ui/layouts/Sidebar.vue +++ b/app/imports/ui/layouts/Sidebar.vue @@ -28,7 +28,7 @@ to="/account" v-on="on" > - settings + mdi-cog Account Settings @@ -48,6 +48,9 @@ {{ link.title }} + + mdi-open-in-new + @@ -125,15 +128,15 @@ links(){ let isLoggedIn = !!Meteor.userId(); let links = [ - {title: 'Home', icon: 'home', to: '/'}, - {title: 'Characters', icon: 'portrait', to: '/characterList', requireLogin: true}, - {title: 'Library', icon: 'book', to: '/library', requireLogin: true}, + {title: 'Home', icon: 'mdi-home', to: '/'}, + {title: 'Characters', icon: 'mdi-account-group', to: '/characterList', requireLogin: true}, + {title: 'Library', icon: 'mdi-library-shelves', to: '/library', requireLogin: true}, //{title: 'Tabletops', icon: 'api', to: '/tabletops', requireLogin: true}, //{title: 'Friends', icon: 'people', to: '/friends', requireLogin: true}, - {title: 'Feedback', icon: 'bug_report', to: '/feedback'}, - {title: 'About', icon: 'subject', to: '/about'}, - {title: 'Patreon', icon: '', href: 'https://www.patreon.com/dicecloud'}, - {title: 'Github', icon: '', href: 'https://github.com/ThaumRystra/DiceCloud/tree/version-2'}, + {title: 'Feedback', icon: 'mdi-bug', to: '/feedback'}, + {title: 'About', icon: 'mdi-sign-text', to: '/about'}, + {title: 'Patreon', icon: 'mdi-patreon', href: 'https://www.patreon.com/dicecloud'}, + {title: 'Github', icon: 'mdi-github', href: 'https://github.com/ThaumRystra/DiceCloud/tree/version-2'}, ]; return links.filter(link => !link.requireLogin || isLoggedIn); }, diff --git a/app/imports/ui/library/InsertLibraryNodeButton.vue b/app/imports/ui/library/InsertLibraryNodeButton.vue index a881d367..c9e4ba0f 100644 --- a/app/imports/ui/library/InsertLibraryNodeButton.vue +++ b/app/imports/ui/library/InsertLibraryNodeButton.vue @@ -7,7 +7,7 @@ data-id="insert-library-node-button" @click="insertLibraryNode" > - add + mdi-plus diff --git a/app/imports/ui/library/LibraryBrowser.vue b/app/imports/ui/library/LibraryBrowser.vue index 0af63ee8..1b205920 100644 --- a/app/imports/ui/library/LibraryBrowser.vue +++ b/app/imports/ui/library/LibraryBrowser.vue @@ -38,7 +38,7 @@ small @click="$router.push(`/library/${library._id}`)" > - arrow_forward + mdi-arrow-right
- add + mdi-plus New library
diff --git a/app/imports/ui/library/LibraryEditDialog.vue b/app/imports/ui/library/LibraryEditDialog.vue index 735cedee..1c65dba3 100644 --- a/app/imports/ui/library/LibraryEditDialog.vue +++ b/app/imports/ui/library/LibraryEditDialog.vue @@ -10,14 +10,14 @@ data-id="share-library-button" @click="share" > - share + mdi-share-variant - delete + mdi-delete diff --git a/app/imports/ui/log/CharacterLog.vue b/app/imports/ui/log/CharacterLog.vue index f9d9b622..28c6d8a6 100644 --- a/app/imports/ui/log/CharacterLog.vue +++ b/app/imports/ui/log/CharacterLog.vue @@ -21,7 +21,7 @@ class="mx-2 mb-2" persistent-hint style="flex-grow: 0" - append-outer-icon="send" + append-outer-icon="mdi-send" :hint="inputHint" :error-messages="inputError" :disabled="!editPermission" diff --git a/app/imports/ui/pages/Account.vue b/app/imports/ui/pages/Account.vue index e230c464..f09d1d51 100644 --- a/app/imports/ui/pages/Account.vue +++ b/app/imports/ui/pages/Account.vue @@ -42,7 +42,7 @@ v-on="on" @click="changeUsername" > - create + mdi-pencil Change Username @@ -77,7 +77,7 @@ v-on="on" @click="updatePatreon" > - refresh + mdi-refresh Refresh Patreon status @@ -136,7 +136,7 @@ - mail_outline + mdi-email-outline - add + mdi-plus diff --git a/app/imports/ui/pages/Friends.vue b/app/imports/ui/pages/Friends.vue index ba98ea17..5a7abd9d 100644 --- a/app/imports/ui/pages/Friends.vue +++ b/app/imports/ui/pages/Friends.vue @@ -10,7 +10,7 @@ right fab > - add + mdi-plus diff --git a/app/imports/ui/pages/Home.vue b/app/imports/ui/pages/Home.vue index 3a516202..1b638bc8 100644 --- a/app/imports/ui/pages/Home.vue +++ b/app/imports/ui/pages/Home.vue @@ -37,7 +37,7 @@ x-large class="ma-2" > - money_off + mdi-currency-usd-off

Free, open source, community funded @@ -55,7 +55,7 @@ x-large class="ma-2" > - ballot + mdi-ballot-outline

Character sheets optimised for one ruleset @@ -73,7 +73,7 @@ x-large class="ma-2" > - scatter_plot + mdi-file-tree-outline

Inventory manager diff --git a/app/imports/ui/pages/Tabletops.vue b/app/imports/ui/pages/Tabletops.vue index 8cd5c4ad..24f68157 100644 --- a/app/imports/ui/pages/Tabletops.vue +++ b/app/imports/ui/pages/Tabletops.vue @@ -28,7 +28,7 @@ :loading="addTabletopLoading" @click="addTabletop" > - add + mdi-plus diff --git a/app/imports/ui/properties/components/attributes/HitDiceListTile.vue b/app/imports/ui/properties/components/attributes/HitDiceListTile.vue index c1e7d431..6a0f1644 100644 --- a/app/imports/ui/properties/components/attributes/HitDiceListTile.vue +++ b/app/imports/ui/properties/components/attributes/HitDiceListTile.vue @@ -19,7 +19,7 @@ :disabled="currentValue >= model.value || context.editPermission === false" @click="increment(1)" > - arrow_drop_up + mdi-chevron-up - arrow_drop_down + mdi-chevron-down diff --git a/app/imports/ui/properties/components/attributes/ResourceCard.vue b/app/imports/ui/properties/components/attributes/ResourceCard.vue index c63f77f4..a9546d18 100644 --- a/app/imports/ui/properties/components/attributes/ResourceCard.vue +++ b/app/imports/ui/properties/components/attributes/ResourceCard.vue @@ -11,7 +11,7 @@ :disabled="currentValue >= value || context.editPermission === false" @click="increment(1)" > - arrow_drop_up + mdi-chevron-up - arrow_drop_down + mdi-chevron-down
{{ i > model.currentValue ? - 'radio_button_unchecked' : - 'radio_button_checked' + 'mdi-radiobox-blank' : + 'mdi-radiobox-marked' }}
diff --git a/app/imports/ui/properties/components/inventory/ItemListTile.vue b/app/imports/ui/properties/components/inventory/ItemListTile.vue index 9729ddc9..8a7a599d 100644 --- a/app/imports/ui/properties/components/inventory/ItemListTile.vue +++ b/app/imports/ui/properties/components/inventory/ItemListTile.vue @@ -41,7 +41,7 @@ style="height: 100%; width: 40px; cursor: move;" class="handle" > - drag_indicator + mdi-drag diff --git a/app/imports/ui/properties/components/skills/SkillListTile.vue b/app/imports/ui/properties/components/skills/SkillListTile.vue index b428a073..0ab9be72 100644 --- a/app/imports/ui/properties/components/skills/SkillListTile.vue +++ b/app/imports/ui/properties/components/skills/SkillListTile.vue @@ -53,15 +53,15 @@ export default { computed: { icon(){ if (this.model.proficiency == 0.49){ - return 'brightness_3'; + return 'mdi-brightness-3'; } else if (this.model.proficiency == 0.5){ - return 'brightness_2'; + return 'mdi-brightness-2'; } else if (this.model.proficiency == 1) { - return 'brightness_1' + return 'mdi-brightness-1' } else if (this.model.proficiency == 2){ return 'album' } else { - return 'radio_button_unchecked'; + return 'mdi-radiobox-blank'; } }, displayedModifier(){ diff --git a/app/imports/ui/properties/components/skills/SkillProficiency.vue b/app/imports/ui/properties/components/skills/SkillProficiency.vue index a2782b73..6a504aa8 100644 --- a/app/imports/ui/properties/components/skills/SkillProficiency.vue +++ b/app/imports/ui/properties/components/skills/SkillProficiency.vue @@ -59,15 +59,15 @@ computed: { icon(){ if (this.model.value == 0.49){ - return 'brightness_3'; + return 'mdi-brightness-3'; } else if (this.model.value == 0.5) { - return 'brightness_2' + return 'mdi-brightness-2' } else if (this.model.value == 1) { - return 'brightness_1' + return 'mdi-brightness-1' } else if (this.model.value == 2){ return 'album' } else { - return 'radio_button_unchecked'; + return 'mdi-radiobox-blank'; } }, proficiencyText(){ diff --git a/app/imports/ui/properties/components/spells/CastSpellWithSlotDialog.vue b/app/imports/ui/properties/components/spells/CastSpellWithSlotDialog.vue index 8306bf94..f9a9470e 100644 --- a/app/imports/ui/properties/components/spells/CastSpellWithSlotDialog.vue +++ b/app/imports/ui/properties/components/spells/CastSpellWithSlotDialog.vue @@ -8,7 +8,7 @@ - filter_list + mdi-filter diff --git a/app/imports/ui/properties/components/spells/SpellListCard.vue b/app/imports/ui/properties/components/spells/SpellListCard.vue index 83995482..ff8a7b07 100644 --- a/app/imports/ui/properties/components/spells/SpellListCard.vue +++ b/app/imports/ui/properties/components/spells/SpellListCard.vue @@ -21,7 +21,7 @@ v-on="on" @click.stop > - more_vert + mdi-dots-vertical diff --git a/app/imports/ui/properties/components/spells/SpellListTile.vue b/app/imports/ui/properties/components/spells/SpellListTile.vue index 78da4462..fc04f786 100644 --- a/app/imports/ui/properties/components/spells/SpellListTile.vue +++ b/app/imports/ui/properties/components/spells/SpellListTile.vue @@ -32,7 +32,7 @@ style="height: 100%; width: 40px; cursor: move;" class="handle" > - drag_indicator + mdi-drag - info + mdi-information diff --git a/app/imports/ui/properties/forms/AttributesConsumedListForm.vue b/app/imports/ui/properties/forms/AttributesConsumedListForm.vue index 4c32001a..d86652bc 100644 --- a/app/imports/ui/properties/forms/AttributesConsumedListForm.vue +++ b/app/imports/ui/properties/forms/AttributesConsumedListForm.vue @@ -19,7 +19,7 @@ class="ma-3" @click="$emit('pull', {path: [i]})" > - delete + mdi-delete diff --git a/app/imports/ui/properties/forms/EffectForm.vue b/app/imports/ui/properties/forms/EffectForm.vue index 6157b74e..8200a4a4 100644 --- a/app/imports/ui/properties/forms/EffectForm.vue +++ b/app/imports/ui/properties/forms/EffectForm.vue @@ -9,7 +9,7 @@ /> - delete + mdi-delete diff --git a/app/imports/ui/properties/forms/ReferenceForm.vue b/app/imports/ui/properties/forms/ReferenceForm.vue index 27df3b78..fde536d6 100644 --- a/app/imports/ui/properties/forms/ReferenceForm.vue +++ b/app/imports/ui/properties/forms/ReferenceForm.vue @@ -13,8 +13,8 @@ " :hint="model.cache.library && model.cache.library.name" :error-messages="model.cache.error || errors.ref" - prepend-inner-icon="link" - append-icon="refresh" + prepend-inner-icon="mdi-vector-link" + append-icon="mdi-refresh" data-id="change-ref" @click="changeReference" @click:prepend-inner="changeReference" diff --git a/app/imports/ui/properties/forms/ResourcesForm.vue b/app/imports/ui/properties/forms/ResourcesForm.vue index f56c63d2..201915e4 100644 --- a/app/imports/ui/properties/forms/ResourcesForm.vue +++ b/app/imports/ui/properties/forms/ResourcesForm.vue @@ -34,7 +34,7 @@ outlined v-on="on" > - add + mdi-plus diff --git a/app/imports/ui/properties/forms/shared/ProficiencySelect.vue b/app/imports/ui/properties/forms/shared/ProficiencySelect.vue index 8183c00b..42475c22 100644 --- a/app/imports/ui/properties/forms/shared/ProficiencySelect.vue +++ b/app/imports/ui/properties/forms/shared/ProficiencySelect.vue @@ -1,6 +1,6 @@ @@ -71,7 +71,7 @@ @click="updateSharing(user._id, 'writer')" > - create + mdi-pencil Can edit @@ -80,13 +80,13 @@ @click="updateSharing(user._id, 'reader')" > - remove_red_eye + mdi-eye View only - delete + mdi-delete Remove diff --git a/app/imports/ui/tabletop/TabletopComponent.vue b/app/imports/ui/tabletop/TabletopComponent.vue index 5f00c26b..e74f55eb 100644 --- a/app/imports/ui/tabletop/TabletopComponent.vue +++ b/app/imports/ui/tabletop/TabletopComponent.vue @@ -14,7 +14,7 @@ @click="addCreature" >
- add + mdi-plus
Add creature diff --git a/app/imports/ui/user/DeleteUserAccountDialog.vue b/app/imports/ui/user/DeleteUserAccountDialog.vue index 680cd846..931dc992 100644 --- a/app/imports/ui/user/DeleteUserAccountDialog.vue +++ b/app/imports/ui/user/DeleteUserAccountDialog.vue @@ -7,7 +7,7 @@

Are you sure you want to delete your account?

@@ -55,14 +55,14 @@ label="Type your username or email" style="width: 350px;" :error-messages="usernameInputValid ? undefined : ' '" - :append-icon="usernameInputValid ? 'done' : undefined" + :append-icon="usernameInputValid ? 'mdi-check' : undefined" /> Date: Thu, 10 Jun 2021 12:25:17 +0200 Subject: [PATCH 03/42] Added character slot limitations to tiers; added no-patreon tier for self hosting --- app/imports/api/users/patreon/tiers.js | 29 ++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/app/imports/api/users/patreon/tiers.js b/app/imports/api/users/patreon/tiers.js index 8b16bfec..120b2593 100644 --- a/app/imports/api/users/patreon/tiers.js +++ b/app/imports/api/users/patreon/tiers.js @@ -1,58 +1,78 @@ import { findLast } from 'lodash'; import getEntitledCents from '/imports/api/users/patreon/getEntitledCents.js'; import Invites from '/imports/api/users/Invites.js'; +const patreonDisabled = !!process.env.DISABLE_PATREON; -const TIERS = [ +const TIERS = Object.freeze([ { name: 'Commoner', minimumEntitledCents: 0, invites: 0, + characterSlots: 5, paidBenefits: false, }, { name: 'Dreamer', minimumEntitledCents: 100, invites: 0, + characterSlots: 5, paidBenefits: false, }, { name: 'Wanderer', minimumEntitledCents: 300, invites: 0, + characterSlots: 5, paidBenefits: false, }, { //cost per user $5 name: 'Adventurer', minimumEntitledCents: 500, invites: 0, + characterSlots: 20, paidBenefits: true, }, { //cost per user $3.33 name: 'Hero', minimumEntitledCents: 1000, invites: 2, + characterSlots: 50, paidBenefits: true, }, { //cost per user $3.333 name: 'Legend', minimumEntitledCents: 2000, invites: 5, + characterSlots: 120, paidBenefits: true, }, { //cost per user $3.125 name: 'Paragon', minimumEntitledCents: 5000, invites: 15, + characterSlots: -1, // Unlimited characters paidBenefits: true, }, -]; +]); -const GUEST_TIER = { +// Companion tier should be equivalent to the Adventurer tier +const GUEST_TIER = Object.freeze({ name: 'Companion', guest: true, invites: 0, + characterSlots: 20, paidBenefits: true, -} +}); + +// When patreon features are disabled, give all the users the same tier +// with no limitations +const PATREON_DISABLED_TIER = Object.freeze({ + name: 'Outlander', + invites: 0, + characterSlots: -1, // Can have infinitely many characters + paidBenefits: true, +}); export function getTierByEntitledCents(entitledCents = 0){ + if (patreonDisabled) return PATREON_DISABLED_TIER; return findLast(TIERS, tier => entitledCents >= tier.minimumEntitledCents); } @@ -66,6 +86,7 @@ export function getUserTier(user){ }); if (!user) throw 'User not found'; } + if (patreonDisabled) return PATREON_DISABLED_TIER; const entitledCents = getEntitledCents(user); const tier = getTierByEntitledCents(entitledCents); if (tier.paidBenefits) return tier; From 9994c1f32aaa4241cd2674de36dce679bece2009 Mon Sep 17 00:00:00 2001 From: Stefan Zermatten Date: Thu, 10 Jun 2021 15:18:54 +0200 Subject: [PATCH 04/42] New users now get subscribed to the default libraries as defined by env --- app/imports/api/users/Users.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/imports/api/users/Users.js b/app/imports/api/users/Users.js index 9bd75ac7..7b784909 100644 --- a/app/imports/api/users/Users.js +++ b/app/imports/api/users/Users.js @@ -2,6 +2,7 @@ import SimpleSchema from 'simpl-schema'; import { ValidatedMethod } from 'meteor/mdg:validated-method'; import { RateLimiterMixin } from 'ddp-rate-limiter-mixin'; import '/imports/api/users/deleteMyAccount.js'; +const defaultLibraries = process.env.DEFAULT_LIBRARIES && process.env.DEFAULT_LIBRARIES.split(',') || []; const userSchema = new SimpleSchema({ username: { @@ -63,7 +64,7 @@ const userSchema = new SimpleSchema({ }, subscribedLibraries: { type: Array, - defaultValue: [], + defaultValue: defaultLibraries, max: 100, }, 'subscribedLibraries.$': { From 64fceb9c386ef703ab5c1e64a839204cc4176b56 Mon Sep 17 00:00:00 2001 From: Stefan Zermatten Date: Thu, 10 Jun 2021 15:19:14 +0200 Subject: [PATCH 05/42] Added environmental variables to readme for self-hosting --- README.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/README.md b/README.md index c1dd7f4c..e7d3b9f3 100644 --- a/README.md +++ b/README.md @@ -66,5 +66,20 @@ You should see this: => App running at: http://localhost:3000/ ``` +Environmental Variables +----------------------- + +``` +MAIL_URL=smtp:// +METEOR_SETTINGS={ "public": { "environment": "production", "patreon": { "clientId": "", "campaignId": "" } }, "patreon": { "clientSecret": "", "creatorAccessToken": "" } } +MONGO_OPLOG_URL=mongodb+srv:// +MONGO_URL=mongodb+srv:// +NPM_CONFIG_PRODUCTION=true +PROJECT_DIR=app +ROOT_URL=https:// +DEFAULT_LIBRARIES= +DISABLE_PATREON=<"true" if you want to prevent features being locked behind Patreon tiers> +``` + Now, visiting [](http://localhost:3000/) should show you an empty instance of DiceCloud running. From e3fc56a844bbec093be1c87cdd26794172e63034 Mon Sep 17 00:00:00 2001 From: Stefan Zermatten Date: Fri, 11 Jun 2021 12:03:31 +0200 Subject: [PATCH 06/42] Added backend architecture to archive and restore creatures. --- .../api/creature/archive/ArchivedCreatures.js | 57 ++++++++++++++++ .../archive/methods/archiveCreatures.js | 66 +++++++++++++++++++ .../api/creature/archive/methods/index.js | 2 + .../archive/methods/restoreCreatures.js | 63 ++++++++++++++++++ .../server/publications/archivedCreatures.js | 18 +++++ app/imports/server/publications/index.js | 5 +- 6 files changed, 209 insertions(+), 2 deletions(-) create mode 100644 app/imports/api/creature/archive/ArchivedCreatures.js create mode 100644 app/imports/api/creature/archive/methods/archiveCreatures.js create mode 100644 app/imports/api/creature/archive/methods/index.js create mode 100644 app/imports/api/creature/archive/methods/restoreCreatures.js create mode 100644 app/imports/server/publications/archivedCreatures.js diff --git a/app/imports/api/creature/archive/ArchivedCreatures.js b/app/imports/api/creature/archive/ArchivedCreatures.js new file mode 100644 index 00000000..4a572482 --- /dev/null +++ b/app/imports/api/creature/archive/ArchivedCreatures.js @@ -0,0 +1,57 @@ +import SimpleSchema from 'simpl-schema'; + +// Archived creatures is an immutable collection of creatures that are no longer +// in use and can be safely archived by the mongoDB hosting service. +// It keeps the working datasets like creatureProperties much smaller +// than they would otherwise be. +let ArchivedCreatures = new Mongo.Collection('archivedCreatures'); + +// We use blackbox objects for everything: +// - saves time checking every object against a schema +// - doesn't accidentaly create indices defined in subschemas +// - The objects we are archiving have already been checked against their +// own schemas +let ArchivedCreatureSchema = new SimpleSchema({ + owner: { + type: String, + regEx: SimpleSchema.RegEx.Id, + // The primary index on this collection + index: 1, + }, + archiveDate: { + type: Date, + // Indexed so the archiving system can archive documents when they + // get to a certain age + index: 1, + }, + creature: { + type: Object, + blackbox: true, + }, + properties: { + type: Array, + }, + 'properties.$': { + type: Object, + blackbox: true, + }, + experiences: { + type: Array, + }, + 'experiences.$': { + type: Object, + blackbox: true, + }, + logs: { + type: Array, + }, + 'logs.$': { + type: Object, + blackbox: true, + }, +}); + +ArchivedCreatures.attachSchema(ArchivedCreatureSchema); + +import '/imports/api/creature/archive/methods/index.js'; +export default ArchivedCreatures; diff --git a/app/imports/api/creature/archive/methods/archiveCreatures.js b/app/imports/api/creature/archive/methods/archiveCreatures.js new file mode 100644 index 00000000..a63c6563 --- /dev/null +++ b/app/imports/api/creature/archive/methods/archiveCreatures.js @@ -0,0 +1,66 @@ +import SimpleSchema from 'simpl-schema'; +import { ValidatedMethod } from 'meteor/mdg:validated-method'; +import { RateLimiterMixin } from 'ddp-rate-limiter-mixin'; +import { assertOwnership } from '/imports/api/creature/creaturePermissions.js'; +import Creatures from '/imports/api/creature/Creatures.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'; +import { removeCreatureWork } from '/imports/api/creature/removeCreature.js'; +import ArchivedCreatures from '/imports/api/creature/archive/ArchivedCreatures.js'; + +function archiveCreature(creatureId){ + // Build the archive document + const creature = Creatures.findOne(creatureId); + const properties = CreatureProperties.find({'ancestors.id': creatureId}); + const experiences = Experiences.find({creatureId}); + const logs = CreatureLogs.find({creatureId}); + let archiveCreature = { + owner: creature.owner, + archiveDate: new Date(), + creature, + properties, + experiences, + logs, + }; + + // Insert it + let id = ArchivedCreatures.insert(archiveCreature); + + // Remove the original creature + removeCreatureWork(creatureId); + + return id; +} + +const archiveCreatures = new ValidatedMethod({ + name: 'Creatures.methods.archiveCreatures', + validate: new SimpleSchema({ + creatureIds: { + type: Array, + max: 10, + }, + 'creatureIds.$': { + type: String, + regEx: SimpleSchema.RegEx.Id, + }, + }).validator(), + mixins: [RateLimiterMixin], + rateLimit: { + numRequests: 1, + timeInterval: 5000, + }, + run({creatureIds}) { + for (let id in creatureIds){ + assertOwnership(id, this.userId) + } + let archivedIds = []; + for (let id in creatureIds){ + let archivedId = archiveCreature(id); + archivedIds.push(archivedId); + } + return archivedIds; + }, +}); + +export default archiveCreatures; diff --git a/app/imports/api/creature/archive/methods/index.js b/app/imports/api/creature/archive/methods/index.js new file mode 100644 index 00000000..62d8bd8d --- /dev/null +++ b/app/imports/api/creature/archive/methods/index.js @@ -0,0 +1,2 @@ +import '/imports/api/creature/archive/methods/archiveCreatures.js'; +import '/imports/api/creature/archive/methods/restoreCreatures.js'; diff --git a/app/imports/api/creature/archive/methods/restoreCreatures.js b/app/imports/api/creature/archive/methods/restoreCreatures.js new file mode 100644 index 00000000..6a84ccf8 --- /dev/null +++ b/app/imports/api/creature/archive/methods/restoreCreatures.js @@ -0,0 +1,63 @@ +import SimpleSchema from 'simpl-schema'; +import { ValidatedMethod } from 'meteor/mdg:validated-method'; +import { RateLimiterMixin } from 'ddp-rate-limiter-mixin'; +import { assertOwnership } from '/imports/api/sharing/sharingPermissions.js'; +import Creatures from '/imports/api/creature/Creatures.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'; +import ArchivedCreatures from '/imports/api/creature/archive/ArchivedCreatures.js'; + +function restoreCreature(archiveId){ + // Get the archive + const archivedCreature = ArchivedCreatures.findOne(archiveId); + + // Insert the creature sub documents + // They still have their original _id's + Creatures.insert(archivedCreature.creature); + CreatureProperties.batchInsert(archivedCreature.properties); + Experiences.batchInsert(archivedCreature.experiences); + CreatureLogs.batchInsert(archivedCreature.logs); + // Do not recompute. The creature was in a computed and ordered state when + // we archived it, just restore everything as-is + + // Remove the archived creature + ArchivedCreatures.remove(archiveId); + + return archivedCreature.creature._id; +} + +const restoreCreatures = new ValidatedMethod({ + name: 'Creatures.methods.restoreCreatures', + validate: new SimpleSchema({ + archiveIds: { + type: Array, + max: 10, + }, + 'archiveIds.$': { + type: String, + regEx: SimpleSchema.RegEx.Id, + }, + }).validator(), + mixins: [RateLimiterMixin], + rateLimit: { + numRequests: 1, + timeInterval: 5000, + }, + run({archiveIds}) { + for (let id in archiveIds){ + let archivedCreature = ArchivedCreatures.findOne(id, { + fields: {owner: 1} + }); + assertOwnership(archivedCreature, this.userId) + } + let creatureIds = []; + for (let id in archiveIds){ + let creatureId = restoreCreature(id); + creatureIds.push(creatureId); + } + return creatureIds; + }, +}); + +export default restoreCreatures; diff --git a/app/imports/server/publications/archivedCreatures.js b/app/imports/server/publications/archivedCreatures.js new file mode 100644 index 00000000..155a887b --- /dev/null +++ b/app/imports/server/publications/archivedCreatures.js @@ -0,0 +1,18 @@ +import ArchivedCreatures from '/imports/api/creature/archive/ArchivedCreatures.js'; + +Meteor.publish('archivedCreatures', function(){ + this.autorun(function (){ + var userId = this.userId; + if (!userId) { + return []; + } + return ArchivedCreatures.find({ + owner: userId, + }, { + fields: { + creature: 1, + } + } + ); + }); +}); diff --git a/app/imports/server/publications/index.js b/app/imports/server/publications/index.js index c11cb255..adff4e49 100644 --- a/app/imports/server/publications/index.js +++ b/app/imports/server/publications/index.js @@ -6,5 +6,6 @@ import '/imports/server/publications/experiences.js'; import '/imports/server/publications/users.js'; import '/imports/server/publications/icons.js'; import '/imports/server/publications/tabletops.js'; -import '/imports/server/publications/slotFillers.js' -import '/imports/server/publications/ownedDocuments.js' +import '/imports/server/publications/slotFillers.js'; +import '/imports/server/publications/ownedDocuments.js'; +import '/imports/server/publications/archivedCreatures.js'; From 81d52a18478a20dfaedd1df0acd788d8a00743d1 Mon Sep 17 00:00:00 2001 From: Stefan Zermatten Date: Fri, 18 Jun 2021 10:43:27 +0200 Subject: [PATCH 07/42] Enabled editing of attribute damage in library forms --- app/imports/ui/library/LibraryNodeDialog.vue | 3 ++- app/imports/ui/library/LibraryNodeInsertForm.vue | 3 ++- app/imports/ui/properties/forms/AttributeForm.vue | 15 ++++++++++++--- 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/app/imports/ui/library/LibraryNodeDialog.vue b/app/imports/ui/library/LibraryNodeDialog.vue index 03e88fec..d446ef4d 100644 --- a/app/imports/ui/library/LibraryNodeDialog.vue +++ b/app/imports/ui/library/LibraryNodeDialog.vue @@ -113,13 +113,14 @@ }, reactiveProvide: { name: 'context', - include: ['editPermission'], + include: ['editPermission', 'isLibraryForm'], }, data(){return { editing: !!this.startInEditTab, // CurrentId lags behind Id by one tick so that events fired by destroying // forms keyed to the old ID are applied before the new ID overwrites it currentId: undefined, + isLibraryForm: true, }}, watch: { _id: { diff --git a/app/imports/ui/library/LibraryNodeInsertForm.vue b/app/imports/ui/library/LibraryNodeInsertForm.vue index 86f1b1bf..8e8b8c2f 100644 --- a/app/imports/ui/library/LibraryNodeInsertForm.vue +++ b/app/imports/ui/library/LibraryNodeInsertForm.vue @@ -58,7 +58,7 @@ export default { }, reactiveProvide: { name: 'context', - include: ['debounceTime'], + include: ['debounceTime', 'isLibraryForm'], }, data(){return { model: { @@ -67,6 +67,7 @@ export default { schema: undefined, validationContext: undefined, debounceTime: 0, + isLibraryForm: true, };}, watch: { type(newType){ diff --git a/app/imports/ui/properties/forms/AttributeForm.vue b/app/imports/ui/properties/forms/AttributeForm.vue index a92b7c37..bd639b2d 100644 --- a/app/imports/ui/properties/forms/AttributeForm.vue +++ b/app/imports/ui/properties/forms/AttributeForm.vue @@ -62,7 +62,7 @@ @change="change('description', ...arguments)" /> - + {{ cntxt }} From 1a2d4b22bb2e01ff06e9d30eb3e73d7d279d475b Mon Sep 17 00:00:00 2001 From: Stefan Zermatten Date: Fri, 18 Jun 2021 10:48:12 +0200 Subject: [PATCH 08/42] Renamed parties to creatureFolders --- .../CreatureFolders.js} | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) rename app/imports/api/creature/{Parties.js => creatureFolders/CreatureFolders.js} (56%) diff --git a/app/imports/api/creature/Parties.js b/app/imports/api/creature/creatureFolders/CreatureFolders.js similarity index 56% rename from app/imports/api/creature/Parties.js rename to app/imports/api/creature/creatureFolders/CreatureFolders.js index e0f90c60..7d7071b6 100644 --- a/app/imports/api/creature/Parties.js +++ b/app/imports/api/creature/creatureFolders/CreatureFolders.js @@ -1,8 +1,8 @@ import SimpleSchema from 'simpl-schema'; -let Parties = new Mongo.Collection('parties'); +let CreatureFolders = new Mongo.Collection('parties'); -let partySchema = new SimpleSchema({ +let creatureFolderSchema = new SimpleSchema({ name: { type: String, defaultValue: 'New Party', @@ -21,8 +21,12 @@ let partySchema = new SimpleSchema({ type: String, regEx: SimpleSchema.RegEx.Id, }, + archived: { + type: Boolean, + defaultValue: true, + }, }); -Parties.attachSchema(partySchema); +CreatureFolders.attachSchema(creatureFolderSchema); -export default Parties; +export default CreatureFolders; From cf05aea80a2488e25301f9ba40726d4e59eb8b4c Mon Sep 17 00:00:00 2001 From: Stefan Zermatten Date: Fri, 18 Jun 2021 10:49:01 +0200 Subject: [PATCH 09/42] Fixed Parties references --- app/imports/server/publications/characterList.js | 2 +- app/imports/ui/layouts/Sidebar.vue | 2 +- app/imports/ui/pages/CharacterList.vue | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/imports/server/publications/characterList.js b/app/imports/server/publications/characterList.js index 34bdd0da..5929970c 100644 --- a/app/imports/server/publications/characterList.js +++ b/app/imports/server/publications/characterList.js @@ -1,5 +1,5 @@ import Creatures from '/imports/api/creature/Creatures.js'; -import Parties from '/imports/api/creature/Parties.js'; +import Parties from '/imports/api/creature/creatureFolders/CreatureFolders.js'; Meteor.publish('characterList', function(){ this.autorun(function (){ diff --git a/app/imports/ui/layouts/Sidebar.vue b/app/imports/ui/layouts/Sidebar.vue index 8a8f7c7b..5e4b8e9c 100644 --- a/app/imports/ui/layouts/Sidebar.vue +++ b/app/imports/ui/layouts/Sidebar.vue @@ -111,7 +111,7 @@ From 814e371148cdb19c52ed303b4c8bbcb74e91df67 Mon Sep 17 00:00:00 2001 From: Stefan Zermatten Date: Sun, 20 Jun 2021 13:32:28 +0200 Subject: [PATCH 12/42] Can now move creatures between folders using drag and drop --- .../creatureFolders/CreatureFolders.js | 1 - .../creatureFolders/methods.js/index.js | 3 +- .../methods.js/insertCreatureFolder.js | 1 + .../methods.js/moveCreatureToFolder.js | 45 +++++++++ .../ui/creature/creatureList/CreatureList.vue | 91 +++++++++++++++++++ .../{ => creatureList}/CreatureListTile.vue | 9 +- app/imports/ui/pages/CharacterList.vue | 52 +++++------ .../ui/tabletop/SelectCreaturesDialog.vue | 2 +- .../ui/user/DeleteUserAccountDialog.vue | 2 +- 9 files changed, 172 insertions(+), 34 deletions(-) create mode 100644 app/imports/api/creature/creatureFolders/methods.js/moveCreatureToFolder.js create mode 100644 app/imports/ui/creature/creatureList/CreatureList.vue rename app/imports/ui/creature/{ => creatureList}/CreatureListTile.vue (84%) diff --git a/app/imports/api/creature/creatureFolders/CreatureFolders.js b/app/imports/api/creature/creatureFolders/CreatureFolders.js index a61140c1..3d00b3a9 100644 --- a/app/imports/api/creature/creatureFolders/CreatureFolders.js +++ b/app/imports/api/creature/creatureFolders/CreatureFolders.js @@ -5,7 +5,6 @@ let CreatureFolders = new Mongo.Collection('creatureFolders'); let creatureFolderSchema = new SimpleSchema({ name: { type: String, - defaultValue: 'Folder', trim: false, optional: true, }, diff --git a/app/imports/api/creature/creatureFolders/methods.js/index.js b/app/imports/api/creature/creatureFolders/methods.js/index.js index 687f1d97..26782576 100644 --- a/app/imports/api/creature/creatureFolders/methods.js/index.js +++ b/app/imports/api/creature/creatureFolders/methods.js/index.js @@ -1,3 +1,4 @@ import '/imports/api/creature/creatureFolders/methods.js/insertCreatureFolder.js'; import '/imports/api/creature/creatureFolders/methods.js/updateCreatureFolderName.js'; -import '/imports/api/creature/creatureFolders/methods.js/removeCreatureFolder.js'; \ No newline at end of file +import '/imports/api/creature/creatureFolders/methods.js/removeCreatureFolder.js'; +import '/imports/api/creature/creatureFolders/methods.js/moveCreatureToFolder.js'; diff --git a/app/imports/api/creature/creatureFolders/methods.js/insertCreatureFolder.js b/app/imports/api/creature/creatureFolders/methods.js/insertCreatureFolder.js index 7c5d4e15..8df44d40 100644 --- a/app/imports/api/creature/creatureFolders/methods.js/insertCreatureFolder.js +++ b/app/imports/api/creature/creatureFolders/methods.js/insertCreatureFolder.js @@ -36,6 +36,7 @@ const insertCreatureFolder = new ValidatedMethod({ } // Insert return CreatureFolders.insert({ + name: 'Folder', owner: userId, order, }); diff --git a/app/imports/api/creature/creatureFolders/methods.js/moveCreatureToFolder.js b/app/imports/api/creature/creatureFolders/methods.js/moveCreatureToFolder.js new file mode 100644 index 00000000..46f9b696 --- /dev/null +++ b/app/imports/api/creature/creatureFolders/methods.js/moveCreatureToFolder.js @@ -0,0 +1,45 @@ +import CreatureFolders from '/imports/api/creature/creatureFolders/CreatureFolders.js'; +import { ValidatedMethod } from 'meteor/mdg:validated-method'; +import { RateLimiterMixin } from 'ddp-rate-limiter-mixin'; + +const moveCreatureToFolder = new ValidatedMethod({ + name: 'creatureFolders.methods.moveCreatureToFolder', + validate: null, + mixins: [RateLimiterMixin], + rateLimit: { + numRequests: 5, + timeInterval: 5000, + }, + run({creatureId, folderId}) { + // Ensure logged in + let userId = this.userId; + if (!userId) { + throw new Meteor.Error('creatureFolders.methods.updateName.denied', + 'You need to be logged in to remove a folder'); + } + // Check that this folder is owned by the user + if (folderId){ + let existingFolder = CreatureFolders.findOne(folderId); + if (existingFolder.owner !== userId){ + throw new Meteor.Error('creatureFolders.methods.updateName.denied', + 'This folder does not belong to you'); + } + } + // Remove from other folders + CreatureFolders.update({ + owner: userId + }, { + $pull: {creatures: creatureId}, + }, { + multi: true, + }); + if (folderId){ + // Add to this folder + CreatureFolders.update(folderId, { + $addToSet: {creatures: creatureId}, + }); + } + }, +}); + +export default moveCreatureToFolder; diff --git a/app/imports/ui/creature/creatureList/CreatureList.vue b/app/imports/ui/creature/creatureList/CreatureList.vue new file mode 100644 index 00000000..eaf1476d --- /dev/null +++ b/app/imports/ui/creature/creatureList/CreatureList.vue @@ -0,0 +1,91 @@ + + + + + diff --git a/app/imports/ui/creature/CreatureListTile.vue b/app/imports/ui/creature/creatureList/CreatureListTile.vue similarity index 84% rename from app/imports/ui/creature/CreatureListTile.vue rename to app/imports/ui/creature/creatureList/CreatureListTile.vue index ea2772c4..29c67e97 100644 --- a/app/imports/ui/creature/CreatureListTile.vue +++ b/app/imports/ui/creature/creatureList/CreatureListTile.vue @@ -21,11 +21,18 @@ {{ model.alignment }} {{ model.gender }} {{ model.race }} - + + + mdi-drag + diff --git a/app/imports/ui/pages/CharacterList.vue b/app/imports/ui/pages/CharacterList.vue index c3b595dd..53cc878d 100644 --- a/app/imports/ui/pages/CharacterList.vue +++ b/app/imports/ui/pages/CharacterList.vue @@ -4,16 +4,12 @@ style="height: 100%" > - - - + - + - - - + - - add folder - +
+ + add folder + +
{ - loadingInsertFolder = false; + this.loadingInsertFolder = false; if (!error) return; console.error(error); snackbar({ diff --git a/app/imports/ui/tabletop/SelectCreaturesDialog.vue b/app/imports/ui/tabletop/SelectCreaturesDialog.vue index 851b26a7..2a647119 100644 --- a/app/imports/ui/tabletop/SelectCreaturesDialog.vue +++ b/app/imports/ui/tabletop/SelectCreaturesDialog.vue @@ -32,7 +32,7 @@ + + diff --git a/app/imports/ui/pages/CharacterList.vue b/app/imports/ui/pages/CharacterList.vue index e6509cc3..04bd7720 100644 --- a/app/imports/ui/pages/CharacterList.vue +++ b/app/imports/ui/pages/CharacterList.vue @@ -3,6 +3,13 @@ class="card-background pa-4" style="height: 100%" > + + You have exceeded your maximum number of character slots, archive or delete + some characters. + @@ -76,6 +83,7 @@ bottom right data-id="new-character-button" + :disabled="!hasCharacterSpace" @click="insertCharacter" > mdi-plus @@ -143,6 +151,28 @@ {sort: {name: 1}} ).map(characterTransform); }, + creatureCount(){ + let userId = Meteor.userId(); + return Creatures.find({ + owner: userId, + }, { + fields: {_id: 1}, + }).count(); + }, + tier(){ + let userId = Meteor.userId(); + return getUserTier(userId); + }, + hasCharacterSpace(){ + let tier = this.tier; + let currentCharacterCount = this.creatureCount; + return tier.characterSlots === -1 || currentCharacterCount < tier.characterSlots + }, + exceededCharacterSpace(){ + let tier = this.tier; + let currentCharacterCount = this.creatureCount; + return tier.characterSlots !== -1 && currentCharacterCount > tier.characterSlots + } }, watch:{ renamingFolder(newId){ @@ -156,26 +186,21 @@ }, methods: { insertCharacter(){ - let tier = getUserTier(Meteor.userId()); - if (tier.paidBenefits){ - insertCreature.call((error, result) => { - if (error){ - console.error(error); - } else { - this.$store.commit( - 'setTabForCharacterSheet', - {id: result, tab: 4} - ); - this.$store.commit('setShowDetailsDialog', true); - this.$router.push({ path: `/character/${result}`}); - } - }); - } else { - this.$store.commit('pushDialogStack', { - component: 'tier-too-low-dialog', - elementId: 'new-character-button', - }); - } + insertCreature.call((error, result) => { + if (error){ + console.error(error); + snackbar({ + text: error.reason, + }); + } else { + this.$store.commit( + 'setTabForCharacterSheet', + {id: result, tab: 4} + ); + this.$store.commit('setShowDetailsDialog', true); + this.$router.push({ path: `/character/${result}`}); + } + }); }, insertFolder(){ this.loadingInsertFolder = true; diff --git a/app/imports/ui/router.js b/app/imports/ui/router.js index 591f7045..8590f16c 100644 --- a/app/imports/ui/router.js +++ b/app/imports/ui/router.js @@ -5,6 +5,7 @@ import { acceptInviteToken } from '/imports/api/users/Invites.js'; import Home from '/imports/ui/pages/Home.vue'; import About from '/imports/ui/pages/About.vue'; import CharacterList from '/imports/ui/pages/CharacterList.vue'; +import CharacterListToolbarItems from '/imports/ui/creature/creatureList/CharacterListToolbarItems.vue'; import Library from '/imports/ui/pages/Library.vue'; import SingleLibraryToolbar from '/imports/ui/library/SingleLibraryToolbar.vue'; import CharacterSheetPage from '/imports/ui/pages/CharacterSheetPage.vue'; @@ -104,6 +105,7 @@ RouterFactory.configure(factory => { path: '/characterList', components: { default: CharacterList, + toolbarItems: CharacterListToolbarItems, }, meta: { title: 'Character List', From 5f5fe801f6af3e0017986b83932a8ab61e05a49b Mon Sep 17 00:00:00 2001 From: Stefan Zermatten Date: Mon, 21 Jun 2021 16:06:29 +0200 Subject: [PATCH 16/42] Added method to transfer character ownership --- .../methods/transferCreatureOwnership.js | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 app/imports/api/creature/creatures/methods/transferCreatureOwnership.js diff --git a/app/imports/api/creature/creatures/methods/transferCreatureOwnership.js b/app/imports/api/creature/creatures/methods/transferCreatureOwnership.js new file mode 100644 index 00000000..c7012d03 --- /dev/null +++ b/app/imports/api/creature/creatures/methods/transferCreatureOwnership.js @@ -0,0 +1,55 @@ +import SimpleSchema from 'simpl-schema'; +import { ValidatedMethod } from 'meteor/mdg:validated-method'; +import { RateLimiterMixin } from 'ddp-rate-limiter-mixin'; +import Creatures from '/imports/api/creature/creatures/Creatures.js'; +import { assertOwnership } from '/imports/api/creature/creatures/creaturePermissions.js'; +import { getUserTier } from '/imports/api/users/patreon/tiers.js'; + +const transferCreatureOwnership = new ValidatedMethod({ + + name: 'creatures.methods.transferOwnership', + + validate: new SimpleSchema({ + creatureId: { + type: String, + regEx: SimpleSchema.RegEx.Id, + }, + userId: { + type: String, + regEx: SimpleSchema.RegEx.Id, + }, + }).validator(), + + mixins: [RateLimiterMixin], + rateLimit: { + numRequests: 5, + timeInterval: 5000, + }, + + run({creatureId, userId}) { + assertOwnership(creatureId, this.userId); + + let tier = getUserTier(userId); + let currentCharacterCount = Creatures.find({ + owner: userId, + }, { + fields: {_id: 1}, + }).count(); + + if ( + tier.characterSlots !== -1 && + currentCharacterCount >= tier.characterSlots + ){ + throw new Meteor.Error('Creatures.methods.transferOwnership.denied', + 'The new owner is already at their character limit') + } + + Creatures.update(creatureId, { + $set: {owner: userId}, + }); + + return creatureId; + }, +}); + +export default transferCreatureOwnership; From 54bf21c57c41d3088547292bd8da7a638aff240c Mon Sep 17 00:00:00 2001 From: Stefan Zermatten Date: Mon, 21 Jun 2021 16:32:24 +0200 Subject: [PATCH 17/42] Edit permission is no longer patreon-only --- app/imports/api/sharing/sharingPermissions.js | 8 -------- 1 file changed, 8 deletions(-) diff --git a/app/imports/api/sharing/sharingPermissions.js b/app/imports/api/sharing/sharingPermissions.js index 94b42355..16831209 100644 --- a/app/imports/api/sharing/sharingPermissions.js +++ b/app/imports/api/sharing/sharingPermissions.js @@ -1,6 +1,5 @@ import { _ } from 'meteor/underscore'; import fetchDocByRef from '/imports/api/parenting/fetchDocByRef.js'; -import { getUserTier } from '/imports/api/users/patreon/tiers.js'; function assertIdValid(userId){ if (!userId || typeof userId !== 'string'){ @@ -48,13 +47,6 @@ export function assertEditPermission(doc, userId) { return true; } - // Ensure the user is of a tier with paid benefits - let tier = getUserTier(user); - if (!tier.paidBenefits){ - throw new Meteor.Error('Edit permission denied', - `The ${tier.name} tier does not allow you to edit this document`); - } - // Ensure the user is authorized for this specific document if ( doc.owner === userId || From 66dc0ee34f5279f08412341aa0b67e64a9397c84 Mon Sep 17 00:00:00 2001 From: Stefan Zermatten Date: Mon, 21 Jun 2021 16:37:02 +0200 Subject: [PATCH 18/42] Added icon to indicate sharing status of characters --- app/imports/ui/components/SharedIcon.vue | 47 +++++++++++++++++++ .../character/CharacterSheetToolbar.vue | 3 ++ .../creatureList/CreatureListTile.vue | 8 ++++ 3 files changed, 58 insertions(+) create mode 100644 app/imports/ui/components/SharedIcon.vue diff --git a/app/imports/ui/components/SharedIcon.vue b/app/imports/ui/components/SharedIcon.vue new file mode 100644 index 00000000..568bd91c --- /dev/null +++ b/app/imports/ui/components/SharedIcon.vue @@ -0,0 +1,47 @@ + + + + + diff --git a/app/imports/ui/creature/character/CharacterSheetToolbar.vue b/app/imports/ui/creature/character/CharacterSheetToolbar.vue index 693fcd57..fe6106e2 100644 --- a/app/imports/ui/creature/character/CharacterSheetToolbar.vue +++ b/app/imports/ui/creature/character/CharacterSheetToolbar.vue @@ -26,6 +26,7 @@ >
@@ -23,6 +32,14 @@ export default { return getUserTier(Meteor.userId()).characterSlots; } }, + methods: { + openArchive(){ + this.$store.commit('pushDialogStack', { + component: 'archive-dialog', + elementId: 'open-archive-btn', + }); + } + } } diff --git a/app/imports/ui/dialogStack/DialogComponentIndex.js b/app/imports/ui/dialogStack/DialogComponentIndex.js index 53724f22..69dc4dee 100644 --- a/app/imports/ui/dialogStack/DialogComponentIndex.js +++ b/app/imports/ui/dialogStack/DialogComponentIndex.js @@ -1,3 +1,4 @@ +import ArchiveDialog from '/imports/ui/creature/archive/ArchiveDialog.vue'; import CastSpellWithSlotDialog from '/imports/ui/properties/components/spells/CastSpellWithSlotDialog.vue'; import CreatureFormDialog from '/imports/ui/creature/CreatureFormDialog.vue'; import CreaturePropertyCreationDialog from '/imports/ui/creature/creatureProperties/CreaturePropertyCreationDialog.vue'; @@ -22,6 +23,7 @@ import TierTooLowDialog from '/imports/ui/user/TierTooLowDialog.vue'; import UsernameDialog from '/imports/ui/user/UsernameDialog.vue'; export default { + ArchiveDialog, CastSpellWithSlotDialog, CreatureFormDialog, CreaturePropertyCreationDialog, From 3db589f775196d74d29d11f2f2ae7ffc0ded7127 Mon Sep 17 00:00:00 2001 From: Stefan Zermatten Date: Tue, 22 Jun 2021 14:59:02 +0200 Subject: [PATCH 20/42] Fixed button property for new Vuetify version --- app/imports/ui/creature/experiences/ExperienceListDialog.vue | 1 - 1 file changed, 1 deletion(-) diff --git a/app/imports/ui/creature/experiences/ExperienceListDialog.vue b/app/imports/ui/creature/experiences/ExperienceListDialog.vue index 8fa8d1e2..b90450f7 100644 --- a/app/imports/ui/creature/experiences/ExperienceListDialog.vue +++ b/app/imports/ui/creature/experiences/ExperienceListDialog.vue @@ -72,7 +72,6 @@ From 86d9383af075fdc77d01188d7e8d9aff5f8b36d4 Mon Sep 17 00:00:00 2001 From: Stefan Zermatten Date: Tue, 22 Jun 2021 14:59:18 +0200 Subject: [PATCH 21/42] Archive UI --- .../archive/methods/archiveCreatures.js | 10 +- .../archive/methods/restoreCreatures.js | 30 ++- .../server/publications/archivedCreatures.js | 1 + .../ui/creature/archive/ArchiveDialog.vue | 195 +++++++++++++++++- .../creature/creatureList/ArchiveButton.vue | 38 ++++ .../CharacterListToolbarItems.vue | 22 +- .../creatureList/CreatureFolderHeader.vue | 102 +++++++++ .../creatureList/CreatureFolderList.vue | 64 ++++++ .../ui/creature/creatureList/CreatureList.vue | 55 +++-- .../creatureList/CreatureListTile.vue | 46 +++-- app/imports/ui/pages/CharacterList.vue | 171 ++++++--------- 11 files changed, 547 insertions(+), 187 deletions(-) create mode 100644 app/imports/ui/creature/creatureList/ArchiveButton.vue create mode 100644 app/imports/ui/creature/creatureList/CreatureFolderHeader.vue create mode 100644 app/imports/ui/creature/creatureList/CreatureFolderList.vue diff --git a/app/imports/api/creature/archive/methods/archiveCreatures.js b/app/imports/api/creature/archive/methods/archiveCreatures.js index bc401d61..304cce11 100644 --- a/app/imports/api/creature/archive/methods/archiveCreatures.js +++ b/app/imports/api/creature/archive/methods/archiveCreatures.js @@ -12,9 +12,9 @@ import ArchivedCreatures from '/imports/api/creature/archive/ArchivedCreatures.j function archiveCreature(creatureId){ // Build the archive document const creature = Creatures.findOne(creatureId); - const properties = CreatureProperties.find({'ancestors.id': creatureId}); - const experiences = Experiences.find({creatureId}); - const logs = CreatureLogs.find({creatureId}); + const properties = CreatureProperties.find({'ancestors.id': creatureId}).fetch(); + const experiences = Experiences.find({creatureId}).fetch(); + const logs = CreatureLogs.find({creatureId}).fetch(); let archiveCreature = { owner: creature.owner, archiveDate: new Date(), @@ -51,11 +51,11 @@ const archiveCreatures = new ValidatedMethod({ timeInterval: 5000, }, run({creatureIds}) { - for (let id in creatureIds){ + for (let id of creatureIds){ assertOwnership(id, this.userId) } let archivedIds = []; - for (let id in creatureIds){ + for (let id of creatureIds){ let archivedId = archiveCreature(id); archivedIds.push(archivedId); } diff --git a/app/imports/api/creature/archive/methods/restoreCreatures.js b/app/imports/api/creature/archive/methods/restoreCreatures.js index e27093d8..b4ec2c44 100644 --- a/app/imports/api/creature/archive/methods/restoreCreatures.js +++ b/app/imports/api/creature/archive/methods/restoreCreatures.js @@ -7,6 +7,7 @@ import CreatureProperties from '/imports/api/creature/creatureProperties/Creatur import CreatureLogs from '/imports/api/creature/log/CreatureLogs.js'; import Experiences from '/imports/api/creature/experience/Experiences.js'; import ArchivedCreatures from '/imports/api/creature/archive/ArchivedCreatures.js'; +import { removeCreatureWork } from '/imports/api/creature/creatures/methods/removeCreature.js'; function restoreCreature(archiveId){ // Get the archive @@ -15,15 +16,28 @@ function restoreCreature(archiveId){ // Insert the creature sub documents // They still have their original _id's Creatures.insert(archivedCreature.creature); - CreatureProperties.batchInsert(archivedCreature.properties); - Experiences.batchInsert(archivedCreature.experiences); - CreatureLogs.batchInsert(archivedCreature.logs); + try { + // Add all the properties + if (archivedCreature.properties && archivedCreature.properties.length){ + CreatureProperties.batchInsert(archivedCreature.properties); + } + if (archivedCreature.experiences && archivedCreature.experiences.length){ + Experiences.batchInsert(archivedCreature.experiences); + } + if (archivedCreature.logs && archivedCreature.logs.length){ + CreatureLogs.batchInsert(archivedCreature.logs); + } + // Remove the archived creature + ArchivedCreatures.remove(archiveId); + } catch (e) { + // If the above fails, delete the inserted creature + removeCreatureWork(archivedCreature.creature._id); + throw e; + } + // Do not recompute. The creature was in a computed and ordered state when // we archived it, just restore everything as-is - // Remove the archived creature - ArchivedCreatures.remove(archiveId); - return archivedCreature.creature._id; } @@ -45,14 +59,14 @@ const restoreCreatures = new ValidatedMethod({ timeInterval: 5000, }, run({archiveIds}) { - for (let id in archiveIds){ + for (let id of archiveIds){ let archivedCreature = ArchivedCreatures.findOne(id, { fields: {owner: 1} }); assertOwnership(archivedCreature, this.userId) } let creatureIds = []; - for (let id in archiveIds){ + for (let id of archiveIds){ let creatureId = restoreCreature(id); creatureIds.push(creatureId); } diff --git a/app/imports/server/publications/archivedCreatures.js b/app/imports/server/publications/archivedCreatures.js index 155a887b..93043407 100644 --- a/app/imports/server/publications/archivedCreatures.js +++ b/app/imports/server/publications/archivedCreatures.js @@ -11,6 +11,7 @@ Meteor.publish('archivedCreatures', function(){ }, { fields: { creature: 1, + owner: 1, } } ); diff --git a/app/imports/ui/creature/archive/ArchiveDialog.vue b/app/imports/ui/creature/archive/ArchiveDialog.vue index a9c8e8c5..03b687f2 100644 --- a/app/imports/ui/creature/archive/ArchiveDialog.vue +++ b/app/imports/ui/creature/archive/ArchiveDialog.vue @@ -1,26 +1,213 @@ diff --git a/app/imports/ui/creature/creatureList/ArchiveButton.vue b/app/imports/ui/creature/creatureList/ArchiveButton.vue new file mode 100644 index 00000000..4aa46887 --- /dev/null +++ b/app/imports/ui/creature/creatureList/ArchiveButton.vue @@ -0,0 +1,38 @@ + + + + + diff --git a/app/imports/ui/creature/creatureList/CharacterListToolbarItems.vue b/app/imports/ui/creature/creatureList/CharacterListToolbarItems.vue index c7669f15..5c678946 100644 --- a/app/imports/ui/creature/creatureList/CharacterListToolbarItems.vue +++ b/app/imports/ui/creature/creatureList/CharacterListToolbarItems.vue @@ -7,23 +7,19 @@ - - - mdi-archive - - +
diff --git a/app/imports/ui/creature/creatureList/CreatureFolderHeader.vue b/app/imports/ui/creature/creatureList/CreatureFolderHeader.vue new file mode 100644 index 00000000..e146ec2f --- /dev/null +++ b/app/imports/ui/creature/creatureList/CreatureFolderHeader.vue @@ -0,0 +1,102 @@ + + + + + diff --git a/app/imports/ui/creature/creatureList/CreatureFolderList.vue b/app/imports/ui/creature/creatureList/CreatureFolderList.vue new file mode 100644 index 00000000..1735458d --- /dev/null +++ b/app/imports/ui/creature/creatureList/CreatureFolderList.vue @@ -0,0 +1,64 @@ + + + + + diff --git a/app/imports/ui/creature/creatureList/CreatureList.vue b/app/imports/ui/creature/creatureList/CreatureList.vue index 8b98d8d0..a2f30a33 100644 --- a/app/imports/ui/creature/creatureList/CreatureList.vue +++ b/app/imports/ui/creature/creatureList/CreatureList.vue @@ -1,25 +1,26 @@ diff --git a/app/imports/ui/creature/creatureList/CreatureListTile.vue b/app/imports/ui/creature/creatureList/CreatureListTile.vue index d98825d3..e858a2a8 100644 --- a/app/imports/ui/creature/creatureList/CreatureListTile.vue +++ b/app/imports/ui/creature/creatureList/CreatureListTile.vue @@ -2,16 +2,31 @@ lang="html" functional > - - - - + + + + + mdi-check + + + + @@ -25,12 +40,8 @@
- @@ -53,10 +64,7 @@ export default { required: true, }, selection: Boolean, - selected: { - type: Set, - default: () => new Set(), - }, + isSelected: Boolean, } } diff --git a/app/imports/ui/pages/CharacterList.vue b/app/imports/ui/pages/CharacterList.vue index 04bd7720..69d2484d 100644 --- a/app/imports/ui/pages/CharacterList.vue +++ b/app/imports/ui/pages/CharacterList.vue @@ -1,107 +1,74 @@ From a6fbf71b36a8c3907c98b03c2ed580027cae9616 Mon Sep 17 00:00:00 2001 From: Stefan Zermatten Date: Tue, 22 Jun 2021 15:13:59 +0200 Subject: [PATCH 22/42] Added folders to the sidebar character list --- .../creatureList/CreatureFolderHeader.vue | 54 +++++----- .../creatureList/CreatureFolderList.vue | 8 +- .../ui/creature/creatureList/CreatureList.vue | 2 + .../creatureList/CreatureListTile.vue | 7 +- app/imports/ui/layouts/Sidebar.vue | 101 +++++++----------- 5 files changed, 81 insertions(+), 91 deletions(-) diff --git a/app/imports/ui/creature/creatureList/CreatureFolderHeader.vue b/app/imports/ui/creature/creatureList/CreatureFolderHeader.vue index e146ec2f..1b2d6bd3 100644 --- a/app/imports/ui/creature/creatureList/CreatureFolderHeader.vue +++ b/app/imports/ui/creature/creatureList/CreatureFolderHeader.vue @@ -13,33 +13,38 @@ dense :value="model.name" @change="renameFolder" - @click.native.stop="()=>{}" + @click.native.stop="" + @input.native.stop="" + @keydown.native.stop="" + @keyup.native.stop="" /> - - - - mdi-check - - - mdi-pencil - - - - - - mdi-delete - - + @@ -57,6 +62,7 @@ }, open: Boolean, selection: Boolean, + dense: Boolean, }, data(){return { renaming: false, diff --git a/app/imports/ui/creature/creatureList/CreatureFolderList.vue b/app/imports/ui/creature/creatureList/CreatureFolderList.vue index 1735458d..d98e2103 100644 --- a/app/imports/ui/creature/creatureList/CreatureFolderList.vue +++ b/app/imports/ui/creature/creatureList/CreatureFolderList.vue @@ -1,9 +1,12 @@ @@ -53,6 +58,7 @@ export default { type: String, default: undefined, }, + dense: Boolean, }, data(){return{ openFolders: {}, diff --git a/app/imports/ui/creature/creatureList/CreatureList.vue b/app/imports/ui/creature/creatureList/CreatureList.vue index a2f30a33..1cdcdf96 100644 --- a/app/imports/ui/creature/creatureList/CreatureList.vue +++ b/app/imports/ui/creature/creatureList/CreatureList.vue @@ -18,6 +18,7 @@ :selection="selection" :is-selected="selectedCreature === creature._id" v-bind="selection ? {} : {to: creature.url}" + :dense="dense" @click="$emit('creature-selected', creature._id)" /> @@ -48,6 +49,7 @@ type: String, default: undefined, }, + dense: Boolean, }, data(){return { dataCreatures: [], diff --git a/app/imports/ui/creature/creatureList/CreatureListTile.vue b/app/imports/ui/creature/creatureList/CreatureListTile.vue index e858a2a8..187f3115 100644 --- a/app/imports/ui/creature/creatureList/CreatureListTile.vue +++ b/app/imports/ui/creature/creatureList/CreatureListTile.vue @@ -32,16 +32,16 @@ {{ model.name }} - + {{ model.alignment }} {{ model.gender }} {{ model.race }} - + @@ -65,6 +65,7 @@ export default { }, selection: Boolean, isSelected: Boolean, + dense: Boolean, } } diff --git a/app/imports/ui/layouts/Sidebar.vue b/app/imports/ui/layouts/Sidebar.vue index 6fd514f7..66777361 100644 --- a/app/imports/ui/layouts/Sidebar.vue +++ b/app/imports/ui/layouts/Sidebar.vue @@ -54,68 +54,28 @@
- - - - - - - - {{ character.name }} - - - - + diff --git a/app/imports/ui/properties/treeNodeViews/treeNodeViewIndex.js b/app/imports/ui/properties/treeNodeViews/treeNodeViewIndex.js index 86fe7968..3ce03502 100644 --- a/app/imports/ui/properties/treeNodeViews/treeNodeViewIndex.js +++ b/app/imports/ui/properties/treeNodeViews/treeNodeViewIndex.js @@ -4,6 +4,7 @@ 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'; export default { @@ -13,5 +14,6 @@ export default { damage: DamageTreeNode, effect: EffectTreeNode, item: ItemTreeNode, + proficiency: ProficiencyTreeNode, reference: ReferenceTreeNode, } diff --git a/app/imports/ui/properties/viewers/ProficiencyViewer.vue b/app/imports/ui/properties/viewers/ProficiencyViewer.vue index b5c31f00..a7c44e1b 100644 --- a/app/imports/ui/properties/viewers/ProficiencyViewer.vue +++ b/app/imports/ui/properties/viewers/ProficiencyViewer.vue @@ -1,10 +1,12 @@