Iterate on action engine and tree data store migration

This commit is contained in:
Thaum Rystra
2024-10-28 14:26:48 +02:00
parent e887daf543
commit e8158ba531
13 changed files with 124 additions and 128 deletions

View File

@@ -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 },

View File

@@ -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';

View File

@@ -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 },
}, {

View File

@@ -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 },
}, {

View File

@@ -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>

View File

@@ -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,
},
});
},

View File

@@ -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;
},
},

View File

@@ -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);

View File

@@ -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: {

View File

@@ -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 },

View File

@@ -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 },
}),

View File

@@ -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
};

View File

@@ -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'
});
*/