Iterate on action engine and tree data store migration
This commit is contained in:
@@ -145,15 +145,6 @@
|
||||
</v-list-item-group>
|
||||
</template>
|
||||
</split-list-layout>
|
||||
<v-btn
|
||||
text
|
||||
:disabled="!canCast"
|
||||
class="mx-2 px-4"
|
||||
color="primary"
|
||||
@click="cast"
|
||||
>
|
||||
Cast
|
||||
</v-btn>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -186,18 +177,10 @@ export default {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
slotId: {
|
||||
type: String,
|
||||
default: undefined,
|
||||
},
|
||||
value: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
spellId: {
|
||||
type: String,
|
||||
default: undefined,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@@ -258,7 +241,7 @@ export default {
|
||||
} else {
|
||||
const newSlot = find(
|
||||
CreatureProperties.find({
|
||||
'ancestors.id': this.creatureId,
|
||||
...getFilter.descendantsOfRoot(this.creatureId),
|
||||
...slotFilter
|
||||
}, {
|
||||
sort: { 'spellSlotLevel.value': 1, order: 1 },
|
||||
@@ -339,9 +322,6 @@ export default {
|
||||
);
|
||||
}
|
||||
},
|
||||
cast() {
|
||||
this.$emit('continue');
|
||||
}
|
||||
},
|
||||
meteor: {
|
||||
spells() {
|
||||
@@ -379,7 +359,7 @@ export default {
|
||||
},
|
||||
spellSlots() {
|
||||
return CreatureProperties.find({
|
||||
'ancestors.id': this.creatureId,
|
||||
...getFilter.descendantsOfRoot(this.creatureId),
|
||||
...slotFilter
|
||||
}, {
|
||||
sort: { 'spellSlotLevel.value': 1, order: 1 },
|
||||
|
||||
@@ -87,7 +87,7 @@ import { getPropertyName } from '/imports/constants/PROPERTIES';
|
||||
import PropertyForm from '/imports/client/ui/properties/PropertyForm.vue';
|
||||
import getPropertyTitle from '/imports/client/ui/properties/shared/getPropertyTitle';
|
||||
import { assertEditPermission } from '/imports/api/creature/creatures/creaturePermissions';
|
||||
import { get, findLast } from 'lodash';
|
||||
import { get } from 'lodash';
|
||||
import equipItem from '/imports/api/creature/creatureProperties/methods/equipItem';
|
||||
import { snackbar } from '/imports/client/ui/components/snackbars/SnackbarQueue';
|
||||
import insertProperty from '/imports/api/creature/creatureProperties/methods/insertProperty';
|
||||
|
||||
@@ -86,7 +86,7 @@
|
||||
</div>
|
||||
</v-layout>
|
||||
<div class="text-caption text-no-wrap text-truncate">
|
||||
{{ libraryNames[libraryNode.ancestors[0].id ] }}
|
||||
{{ libraryNames[libraryNode.root.id ] }}
|
||||
</div>
|
||||
</v-layout>
|
||||
<div
|
||||
@@ -187,14 +187,13 @@ import LibraryNodes from '/imports/api/library/LibraryNodes';
|
||||
import DialogBase from '/imports/client/ui/dialogStack/DialogBase.vue';
|
||||
import TreeNodeView from '/imports/client/ui/properties/treeNodeViews/TreeNodeView.vue';
|
||||
import PropertyDescription from '/imports/client/ui/properties/viewers/shared/PropertyDescription.vue'
|
||||
import resolve, { toString } from '/imports/parser/resolve';
|
||||
import resolve from '/imports/parser/resolve';
|
||||
import { prettifyParseError, parse } from '/imports/parser/parser';
|
||||
// import evaluateString from '/imports/api/creature/computation/afterComputation/evaluateString';
|
||||
import getSlotFillFilter from '/imports/api/creature/creatureProperties/methods/getSlotFillFilter'
|
||||
import Libraries from '/imports/api/library/Libraries';
|
||||
import LibraryNodeExpansionContent from '/imports/client/ui/library/LibraryNodeExpansionContent.vue';
|
||||
import PropertyTags from '/imports/client/ui/properties/viewers/shared/PropertyTags.vue';
|
||||
import { clone, difference, isEqual } from 'lodash';
|
||||
import { getFilter } from '/imports/api/parenting/parentingFunctions';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
@@ -378,7 +377,7 @@ export default {
|
||||
ancestorId = this.creatureId;
|
||||
}
|
||||
CreatureProperties.find({
|
||||
'ancestors.id': ancestorId,
|
||||
...getFilter.descendants(ancestorId),
|
||||
libraryNodeId: { $exists: true },
|
||||
removed: { $ne: true },
|
||||
}, {
|
||||
|
||||
@@ -107,6 +107,7 @@ import Libraries, { updateLibraryName, updateLibraryDescription, updateLibrarySh
|
||||
import LibraryNodes, { restoreLibraryNode } from '/imports/api/library/LibraryNodes';
|
||||
import TreeNodeView from '/imports/client/ui/properties/treeNodeViews/TreeNodeView.vue';
|
||||
import { snackbar } from '/imports/client/ui/components/snackbars/SnackbarQueue';
|
||||
import { getFilter } from '/imports/api/parenting/parentingFunctions';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
@@ -186,7 +187,7 @@ export default {
|
||||
},
|
||||
removedDocs() {
|
||||
return LibraryNodes.find({
|
||||
'ancestors.0.id': this._id,
|
||||
...getFilter.descendantsOfRoot(this._id),
|
||||
removed: true,
|
||||
removedWith: { $exists: false },
|
||||
}, {
|
||||
|
||||
@@ -1,31 +1,59 @@
|
||||
<template lang="html">
|
||||
<div class="log-content">
|
||||
<div
|
||||
v-for="(content, index) in model"
|
||||
v-for="(contentGroup, index) in contentByTargetId"
|
||||
:key="index"
|
||||
class="content-line"
|
||||
>
|
||||
<h4
|
||||
class="content-name"
|
||||
style="min-height: 12px;"
|
||||
<h3
|
||||
v-if="contentGroup.targetIds.length"
|
||||
class="content-target-ids"
|
||||
>
|
||||
{{ content.name }}
|
||||
</h4>
|
||||
<markdown-text
|
||||
v-if="content.value"
|
||||
class="content-value"
|
||||
:markdown="content.value"
|
||||
/>
|
||||
<v-icon>mdi-chevron-right</v-icon>
|
||||
<v-list-item-avatar
|
||||
v-for="creature in contentGroup.targetCreatures"
|
||||
:key="creature._id"
|
||||
:color="model.color || 'grey'"
|
||||
size="32"
|
||||
>
|
||||
<img
|
||||
v-if="creature.avatarPicture"
|
||||
:src="creature.avatarPicture"
|
||||
:alt="creature.name"
|
||||
>
|
||||
<span v-else>
|
||||
{{ creature.name && creature.name[0] || '?' }}
|
||||
</span>
|
||||
</v-list-item-avatar>
|
||||
</h3>
|
||||
<div
|
||||
v-else
|
||||
style="min-height: 12px;"
|
||||
/>
|
||||
v-for="(content, contentIndex) in contentGroup.content"
|
||||
:key="contentIndex"
|
||||
class="content-line"
|
||||
>
|
||||
<h4
|
||||
class="content-name"
|
||||
style="min-height: 12px;"
|
||||
>
|
||||
{{ content.name }}
|
||||
</h4>
|
||||
<markdown-text
|
||||
v-if="content.value"
|
||||
class="content-value"
|
||||
:markdown="content.value"
|
||||
/>
|
||||
<div
|
||||
v-else
|
||||
style="min-height: 12px;"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="js">
|
||||
import { isEqual } from 'lodash';
|
||||
import MarkdownText from '/imports/client/ui/components/MarkdownText.vue';
|
||||
import Creatures from '/imports/api/creature/creatures/Creatures';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
@@ -37,6 +65,35 @@ export default {
|
||||
default: () => [],
|
||||
},
|
||||
},
|
||||
meteor: {
|
||||
contentByTargetId() {
|
||||
const content = [];
|
||||
const creaturesById = {};
|
||||
const getCreature = creatureId => {
|
||||
if (creaturesById[creatureId]) return creaturesById[creatureId];
|
||||
return creaturesById[creatureId] = Creatures.findOne(creatureId, {
|
||||
fields: { _id: 1, avatarPicture: 1, name: 1 },
|
||||
});
|
||||
};
|
||||
let currentContent = undefined;
|
||||
for (const contentItem of this.model) {
|
||||
if (!currentContent || !isEqual(currentContent.targetIds, contentItem.targetIds)) {
|
||||
if (currentContent) {
|
||||
content.push(currentContent);
|
||||
}
|
||||
currentContent = {
|
||||
targetIds: contentItem.targetIds,
|
||||
targetCreatures: contentItem.targetIds.map(getCreature),
|
||||
content: [contentItem],
|
||||
};
|
||||
} else {
|
||||
currentContent.content.push(contentItem);
|
||||
}
|
||||
}
|
||||
content.push(currentContent);
|
||||
return content;
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -206,7 +206,6 @@ import getThemeColor from '/imports/client/ui/utility/getThemeColor';
|
||||
import PropertySelector from '/imports/client/ui/properties/shared/PropertySelector.vue';
|
||||
import {snackbar} from '/imports/client/ui/components/snackbars/SnackbarQueue';
|
||||
import PropertyForm from '/imports/client/ui/properties/PropertyForm.vue';
|
||||
import SimpleSchema from 'simpl-schema';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
@@ -372,9 +371,9 @@ export default {
|
||||
_searchResult: true
|
||||
},{
|
||||
sort: {
|
||||
'ancestors.0.id': 1,
|
||||
name: 1,
|
||||
order: 1,
|
||||
type: 1,
|
||||
left: 1,
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
@@ -123,6 +123,7 @@ import SkillProficiency from '/imports/client/ui/properties/components/skills/Sk
|
||||
import getProficiencyIcon from '/imports/client/ui/utility/getProficiencyIcon';
|
||||
import sortEffects from '/imports/client/ui/utility/sortEffects';
|
||||
import PropertyTargetTags from '/imports/client/ui/properties/viewers/shared/PropertyTargetTags.vue';
|
||||
import { getFilter } from '/imports/api/parenting/parentingFunctions';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
@@ -201,12 +202,12 @@ export default {
|
||||
let ability = this.model.ability;
|
||||
if (!creatureId || !ability) return;
|
||||
let abilityProp = CreatureProperties.findOne({
|
||||
...getFilter.descendantsOfRoot(creatureId),
|
||||
variableName: ability,
|
||||
type: 'attribute',
|
||||
removed: { $ne: true },
|
||||
inactive: { $ne: true },
|
||||
overridden: { $ne: true },
|
||||
'ancestors.id': creatureId,
|
||||
});
|
||||
if (!abilityProp) return;
|
||||
return {
|
||||
@@ -215,16 +216,16 @@ export default {
|
||||
operation: 'base',
|
||||
amount: { value: abilityProp.modifier },
|
||||
stats: [this.model.variableName],
|
||||
ancestors: abilityProp.ancestors,
|
||||
root: abilityProp.root,
|
||||
}
|
||||
},
|
||||
proficiencyBonus() {
|
||||
return CreatureProperties.findOne({
|
||||
...getFilter.descendantsOfRoot(this.context.creatureId),
|
||||
variableName: 'proficiencyBonus',
|
||||
overridden: { $ne: true },
|
||||
removed: { $ne: true },
|
||||
inactive: { $ne: true },
|
||||
'ancestors.id': this.context.creatureId,
|
||||
})?.value;
|
||||
},
|
||||
},
|
||||
|
||||
@@ -109,6 +109,7 @@ import TreeNodeList from '/imports/client/ui/components/tree/TreeNodeList.vue';
|
||||
import { docsToForest } from '/imports/api/parenting/parentingFunctions';
|
||||
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties';
|
||||
import { some } from 'lodash';
|
||||
import { getFilter } from '/imports/api/parenting/parentingFunctions';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
@@ -184,9 +185,9 @@ export default {
|
||||
},
|
||||
meteor: {
|
||||
children() {
|
||||
const indicesOfTerminatingProps = [];
|
||||
const excludedRanges = [];
|
||||
const decendants = CreatureProperties.find({
|
||||
'ancestors.id': this.model._id,
|
||||
...getFilter.descendants(this.model),
|
||||
'removed': { $ne: true },
|
||||
}, {
|
||||
sort: {left: 1}
|
||||
@@ -194,9 +195,9 @@ export default {
|
||||
// Get all the props we don't want to show the decendants of and
|
||||
// where they might appear in the ancestor list
|
||||
if (prop.type === 'buff' || prop.type === 'folder') {
|
||||
indicesOfTerminatingProps.push({
|
||||
id: prop._id,
|
||||
ancestorIndex: prop.ancestors.length,
|
||||
excludedRanges.push({
|
||||
left: prop.left,
|
||||
right: prop.right,
|
||||
});
|
||||
}
|
||||
return prop;
|
||||
@@ -204,8 +205,8 @@ export default {
|
||||
// Filter out folders entirely
|
||||
if (prop.type === 'folder') return false;
|
||||
// Filter out decendants of terminating props
|
||||
return !some(indicesOfTerminatingProps, buffIndex => {
|
||||
return prop.ancestors[buffIndex.ancestorIndex]?.id === buffIndex.id;
|
||||
return !some(excludedRanges, range => {
|
||||
return prop.left > range.left && prop.right < range.right;
|
||||
});
|
||||
});
|
||||
return docsToForest(decendants);
|
||||
|
||||
@@ -64,6 +64,7 @@ import TreeNodeList from '/imports/client/ui/components/tree/TreeNodeList.vue';
|
||||
import { docsToForest } from '/imports/api/parenting/parentingFunctions';
|
||||
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties';
|
||||
import { some } from 'lodash';
|
||||
import { getFilter } from '/imports/api/parenting/parentingFunctions';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
@@ -137,31 +138,29 @@ export default {
|
||||
},
|
||||
meteor: {
|
||||
children() {
|
||||
const indicesOfTerminatingProps = [];
|
||||
const decendants = CreatureProperties.find({
|
||||
'ancestors.id': this.model._id,
|
||||
const excludedRanges = [];
|
||||
const descendants = CreatureProperties.find({
|
||||
...getFilter.descendants(this.model),
|
||||
'removed': { $ne: true },
|
||||
}, {
|
||||
sort: {left: 1}
|
||||
}).map(prop => {
|
||||
// Get all the props we don't want to show the decendants of and
|
||||
// where they might appear in the ancestor list
|
||||
if (prop.type === 'buff' || prop.type === 'folder') {
|
||||
indicesOfTerminatingProps.push({
|
||||
id: prop._id,
|
||||
ancestorIndex: prop.ancestors.length,
|
||||
});
|
||||
}
|
||||
// Get all the props we don't want to show the descendants of and what range they cover in
|
||||
// the tree
|
||||
excludedRanges.push({
|
||||
left: prop.left,
|
||||
right: prop.right,
|
||||
});
|
||||
return prop;
|
||||
}).filter(prop => {
|
||||
// Filter out folders entirely
|
||||
if (prop.type === 'folder') return false;
|
||||
// Filter out decendants of terminating props
|
||||
return !some(indicesOfTerminatingProps, buffIndex => {
|
||||
return prop.ancestors[buffIndex.ancestorIndex]?.id === buffIndex.id;
|
||||
// Filter out descendants of terminating props
|
||||
return !some(excludedRanges, range => {
|
||||
return prop.left > range.left && prop.right < range.right;
|
||||
});
|
||||
});
|
||||
return docsToForest(decendants);
|
||||
return docsToForest(descendants);
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
|
||||
@@ -109,12 +109,11 @@ import { assertEditPermission } from '/imports/api/creature/creatures/creaturePe
|
||||
import SelectedCreatureBar from '/imports/client/ui/tabletop/selectedCreatureBar/SelectedCreatureBar.vue';
|
||||
import addCreaturesFromLibraryToTabletop from '/imports/api/tabletop/methods/addCreaturesFromLibraryToTabletop';
|
||||
import removeCreatureFromTabletop from '/imports/api/tabletop/methods/removeCreatureFromTabletop';
|
||||
import { getFilter } from '/imports/api/parenting/parentingFunctions';
|
||||
|
||||
const getProperties = function (creatureId, selector = {}) {
|
||||
return CreatureProperties.find({
|
||||
'ancestors.id': {
|
||||
$eq: creatureId,
|
||||
},
|
||||
...getFilter.descendantsOfRoot(creatureId),
|
||||
inactive: { $ne: true },
|
||||
removed: { $ne: true },
|
||||
overridden: { $ne: true },
|
||||
|
||||
@@ -4,6 +4,7 @@ import LibraryCollections from '/imports/api/library/LibraryCollections';
|
||||
import LibraryNodes from '/imports/api/library/LibraryNodes';
|
||||
import { assertViewPermission, assertDocViewPermission } from '/imports/api/sharing/sharingPermissions';
|
||||
import { union } from 'lodash';
|
||||
import { getFilter } from '/imports/api/parenting/parentingFunctions';
|
||||
|
||||
const LIBRARY_NODE_TREE_FIELDS = {
|
||||
_id: 1,
|
||||
@@ -284,7 +285,7 @@ Meteor.publish('softRemovedLibraryNodes', function (libraryId) {
|
||||
}
|
||||
return [
|
||||
LibraryNodes.find({
|
||||
'ancestors.0.id': libraryId,
|
||||
...getFilter.descendantsOfRoot(libraryId),
|
||||
removed: true,
|
||||
removedWith: { $exists: false },
|
||||
}, {
|
||||
@@ -296,8 +297,8 @@ Meteor.publish('softRemovedLibraryNodes', function (libraryId) {
|
||||
|
||||
Meteor.publish('descendantLibraryNodes', function (nodeId) {
|
||||
let node = LibraryNodes.findOne(nodeId);
|
||||
let libraryId = node?.ancestors[0]?.id;
|
||||
if (!libraryId) return [];
|
||||
let libraryId = node?.root.id;
|
||||
if (!libraryId || !node) return [];
|
||||
this.autorun(function () {
|
||||
let userId = this.userId;
|
||||
let library = Libraries.findOne(libraryId);
|
||||
@@ -307,7 +308,7 @@ Meteor.publish('descendantLibraryNodes', function (nodeId) {
|
||||
}
|
||||
return [
|
||||
LibraryNodes.find({
|
||||
'ancestors.id': nodeId,
|
||||
...getFilter.descendants(node),
|
||||
}, {
|
||||
sort: { left: 1 },
|
||||
}),
|
||||
|
||||
@@ -5,6 +5,7 @@ import getCreatureLibraryIds from '/imports/api/library/getCreatureLibraryIds';
|
||||
import getUserLibraryIds from '/imports/api/library/getUserLibraryIds';
|
||||
import { assertViewPermission } from '/imports/api/sharing/sharingPermissions';
|
||||
import escapeRegex from '/imports/api/utility/escapeRegex';
|
||||
import { getFilter } from '/imports/api/parenting/parentingFunctions';
|
||||
|
||||
Meteor.publish('selectedLibraryNodes', function (selectedNodeIds) {
|
||||
check(selectedNodeIds, Array);
|
||||
@@ -13,10 +14,12 @@ Meteor.publish('selectedLibraryNodes', function (selectedNodeIds) {
|
||||
selectedNodeIds = selectedNodeIds.slice(0, 20);
|
||||
}
|
||||
let libraryViewPermissions = {};
|
||||
const nodes = [];
|
||||
// Check view permissions of all libraries
|
||||
for (let id of selectedNodeIds) {
|
||||
let node = LibraryNodes.findOne(id);
|
||||
if (!node) continue;
|
||||
nodes.push(node);
|
||||
let libraryId = node.ancestors[0].id;
|
||||
if (libraryViewPermissions[id]) {
|
||||
continue;
|
||||
@@ -27,6 +30,9 @@ Meteor.publish('selectedLibraryNodes', function (selectedNodeIds) {
|
||||
readers: 1,
|
||||
writers: 1,
|
||||
public: 1,
|
||||
root: 1,
|
||||
left: 1,
|
||||
right: 1,
|
||||
}
|
||||
});
|
||||
assertViewPermission(library, this.userId);
|
||||
@@ -37,7 +43,7 @@ Meteor.publish('selectedLibraryNodes', function (selectedNodeIds) {
|
||||
return [LibraryNodes.find({
|
||||
$or: [
|
||||
{ _id: { $in: selectedNodeIds } },
|
||||
{ 'ancestors.id': { $in: selectedNodeIds } },
|
||||
{ ...getFilter.descendantsOfAll(nodes) },
|
||||
],
|
||||
})];
|
||||
});
|
||||
@@ -63,7 +69,7 @@ Meteor.publish('searchLibraryNodes', function (creatureId) {
|
||||
|
||||
// Build a filter for nodes in those libraries that match the type
|
||||
let filter = {
|
||||
'ancestors.id': { $in: libraryIds },
|
||||
...getFilter.descendantsOfAllRoots(libraryIds),
|
||||
removed: { $ne: true },
|
||||
searchable: true //library nodes must opt-in
|
||||
};
|
||||
|
||||
@@ -53,50 +53,3 @@ JsonRoutes.add('get', 'api/creature/:id', function (req, res) {
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
/*
|
||||
Meteor.publish('api-creature', function (creatureId) {
|
||||
try {
|
||||
new SimpleSchema({
|
||||
creatureId: {
|
||||
type: String,
|
||||
regEx: SimpleSchema.RegEx.Id,
|
||||
},
|
||||
}).validate({ creatureId });
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
this.error(e);
|
||||
return;
|
||||
}
|
||||
const userId = this.userId;
|
||||
const creatureCursor = Creatures.find({
|
||||
_id: creatureId,
|
||||
});
|
||||
const creature = creatureCursor.fetch()[0];
|
||||
try {
|
||||
assertViewPermission(creature, userId)
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
this.error(e);
|
||||
return;
|
||||
}
|
||||
if (creature.computeVersion !== VERSION) {
|
||||
try {
|
||||
computeCreature(creatureId)
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
}
|
||||
}
|
||||
return [
|
||||
creatureCursor,
|
||||
CreatureProperties.find({
|
||||
'ancestors.id': creatureId,
|
||||
}),
|
||||
CreatureVariables.find({
|
||||
_creatureId: creatureId,
|
||||
}),
|
||||
];
|
||||
}, {
|
||||
url: 'api/creature/:0'
|
||||
});
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user