Fixing UI for 2.1 data changes
This commit is contained in:
1
.vscode/settings.json
vendored
1
.vscode/settings.json
vendored
@@ -11,6 +11,7 @@
|
||||
"meteortesting",
|
||||
"nearley",
|
||||
"ngraph",
|
||||
"ostrio",
|
||||
"uncomputed",
|
||||
"walkdown"
|
||||
]
|
||||
|
||||
244
app/imports/@types/meteor-ostrio-files.d.ts
vendored
Normal file
244
app/imports/@types/meteor-ostrio-files.d.ts
vendored
Normal file
@@ -0,0 +1,244 @@
|
||||
declare module 'meteor/ostrio:files' {
|
||||
import { Meteor } from 'meteor/meteor';
|
||||
import { Mongo } from 'meteor/mongo';
|
||||
import { ReactiveVar } from 'meteor/reactive-var';
|
||||
import { SimpleSchemaDefinition } from 'simpl-schema';
|
||||
import * as http from 'http';
|
||||
import { IncomingMessage } from 'connect';
|
||||
|
||||
interface Params {
|
||||
_id: string;
|
||||
query: { [key: string]: string };
|
||||
name: string;
|
||||
version: string;
|
||||
}
|
||||
|
||||
interface ContextHTTP {
|
||||
request: IncomingMessage;
|
||||
response: http.ServerResponse;
|
||||
params: Params;
|
||||
}
|
||||
|
||||
interface ContextUser {
|
||||
userId: string;
|
||||
user: () => Meteor.User;
|
||||
}
|
||||
|
||||
interface ContextUpload {
|
||||
file: object;
|
||||
/** On server only. */
|
||||
chunkId?: number;
|
||||
/** On server only. */
|
||||
eof?: boolean;
|
||||
}
|
||||
|
||||
interface Version<MetadataType> {
|
||||
extension: string;
|
||||
meta: MetadataType;
|
||||
path: string;
|
||||
size: number;
|
||||
type: string;
|
||||
}
|
||||
|
||||
class FileObj<MetadataType> {
|
||||
_id: string;
|
||||
size: number;
|
||||
name: string;
|
||||
type: string;
|
||||
path: string;
|
||||
isVideo: boolean;
|
||||
isAudio: boolean;
|
||||
isImage: boolean;
|
||||
isText: boolean;
|
||||
isJSON: boolean;
|
||||
isPDF: boolean;
|
||||
ext?: string;
|
||||
extension?: string;
|
||||
extensionWithDot: string;
|
||||
_storagePath: string;
|
||||
_downloadRoute: string;
|
||||
_collectionName: string;
|
||||
public?: boolean;
|
||||
meta?: MetadataType;
|
||||
userId?: string;
|
||||
updatedAt?: Date;
|
||||
versions: {
|
||||
[propName: string]: Version<MetadataType>;
|
||||
};
|
||||
mime: string;
|
||||
'mime-type': string;
|
||||
}
|
||||
|
||||
class FileRef<MetadataType> extends FileObj<MetadataType> {
|
||||
remove: (callback?: (error: Meteor.Error) => void) => void;
|
||||
link: (version?: string, location?: string) => string;
|
||||
get: (property?: string) => any;
|
||||
fetch: () => Array<FileObj<MetadataType>>;
|
||||
with: () => FileCursor<MetadataType>;
|
||||
}
|
||||
|
||||
interface FileData<MetadataType> {
|
||||
size: number;
|
||||
type: string;
|
||||
mime: string;
|
||||
'mime-type': string;
|
||||
ext: string;
|
||||
extension: string;
|
||||
name: string;
|
||||
meta: MetadataType;
|
||||
}
|
||||
|
||||
interface FilesCollectionConfig<MetadataType> {
|
||||
storagePath?: string | ((fileObj: FileObj<MetadataType>) => string);
|
||||
collection?: Mongo.Collection<FileObj<MetadataType>>;
|
||||
collectionName?: string;
|
||||
continueUploadTTL?: string;
|
||||
ddp?: object;
|
||||
cacheControl?: string;
|
||||
responseHeaders?: { [x: string]: string } | ((responseCode?: string, fileRef?: FileRef<MetadataType>, versionRef?: Version<MetadataType>, version?: string) => { [x: string]: string });
|
||||
throttle?: number | boolean;
|
||||
downloadRoute?: string;
|
||||
schema?: SimpleSchemaDefinition;
|
||||
chunkSize?: number;
|
||||
namingFunction?: (fileObj: FileObj<MetadataType>) => string;
|
||||
permissions?: number;
|
||||
parentDirPermissions?: number;
|
||||
integrityCheck?: boolean;
|
||||
strict?: boolean;
|
||||
downloadCallback?: (this: ContextHTTP & ContextUser, fileObj: FileObj<MetadataType>) => boolean;
|
||||
protected?: boolean | ((this: ContextHTTP & ContextUser, fileObj: FileObj<MetadataType>) => boolean | number);
|
||||
public?: boolean;
|
||||
onBeforeUpload?: (this: ContextUpload & ContextUser, fileData: FileData<MetadataType>) => boolean | string;
|
||||
onBeforeRemove?: (this: ContextUser, cursor: Mongo.Cursor<FileObj<MetadataType>>) => boolean;
|
||||
onInitiateUpload?: (this: ContextUpload & ContextUser, fileData: FileData<MetadataType>) => void;
|
||||
onAfterUpload?: (fileRef: FileRef<MetadataType>) => any;
|
||||
onAfterRemove?: (files: ReadonlyArray<FileObj<MetadataType>>) => any;
|
||||
onbeforeunloadMessage?: string | (() => string);
|
||||
allowClientCode?: boolean;
|
||||
debug?: boolean;
|
||||
interceptDownload?: (http: object, fileRef: FileRef<MetadataType>, version: string) => boolean;
|
||||
}
|
||||
|
||||
interface SearchOptions<MetadataType, TransformAdditions> {
|
||||
sort?: Mongo.SortSpecifier;
|
||||
skip?: number;
|
||||
limit?: number;
|
||||
fields?: Mongo.FieldSpecifier;
|
||||
reactive?: boolean;
|
||||
transform?: (fileObj: FileObj<MetadataType>) => FileObj<MetadataType> & TransformAdditions;
|
||||
}
|
||||
|
||||
interface InsertOptions<MetadataType> {
|
||||
file: File | object | string;
|
||||
fileId?: string;
|
||||
fileName?: string;
|
||||
isBase64?: boolean;
|
||||
meta?: MetadataType;
|
||||
transport?: 'ddp' | 'http';
|
||||
ddp?: object;
|
||||
onStart?: (error: Meteor.Error, fileData: FileData<MetadataType>) => any;
|
||||
onUploaded?: (error: Meteor.Error, fileRef: FileRef<MetadataType>) => any;
|
||||
onAbort?: (fileData: FileData<MetadataType>) => any;
|
||||
onError?: (error: Meteor.Error, fileData: FileData<MetadataType>) => any;
|
||||
onProgress?: (progress: number, fileData: FileData<MetadataType>) => any;
|
||||
onBeforeUpload?: (fileData: FileData<MetadataType>) => any;
|
||||
chunkSize?: number | 'dynamic';
|
||||
allowWebWorkers?: boolean;
|
||||
type?: string;
|
||||
}
|
||||
|
||||
interface LoadOptions<MetadataType> {
|
||||
fileName: string;
|
||||
meta?: MetadataType;
|
||||
type?: string;
|
||||
size?: number;
|
||||
userId?: string;
|
||||
fileId?: string;
|
||||
}
|
||||
|
||||
class FileUpload {
|
||||
file: File;
|
||||
onPause: ReactiveVar<boolean>;
|
||||
progress: ReactiveVar<number>;
|
||||
estimateTime: ReactiveVar<number>;
|
||||
estimateSpeed: ReactiveVar<number>;
|
||||
state: ReactiveVar<'active' | 'paused' | 'aborted' | 'completed'>;
|
||||
pause(): void;
|
||||
continue(): void;
|
||||
toggle(): void;
|
||||
pipe(): void;
|
||||
start(): void;
|
||||
on(event: string, callback: () => void): void;
|
||||
}
|
||||
|
||||
class FileCursor<MetadataType> extends FileRef<MetadataType> { }
|
||||
|
||||
class FilesCursor<MetadataType, TransformAdditions> extends Mongo.Cursor<FileObj<MetadataType>> {
|
||||
cursor: Mongo.Cursor<FileObj<MetadataType>>; // Refers to base cursor? Why is this existing?
|
||||
|
||||
get(): Array<FileCursor<MetadataType> & TransformAdditions>;
|
||||
hasNext(): boolean;
|
||||
next(): FileCursor<MetadataType> & TransformAdditions;
|
||||
hasPrevious(): boolean;
|
||||
previous(): FileCursor<MetadataType> & TransformAdditions;
|
||||
first(): FileCursor<MetadataType> & TransformAdditions;
|
||||
last(): FileCursor<MetadataType> & TransformAdditions;
|
||||
remove(callback?: (err: object) => void): void;
|
||||
each(callback: (cursor: FileCursor<MetadataType> & TransformAdditions) => void): void;
|
||||
current(): object | undefined;
|
||||
}
|
||||
|
||||
class FilesCollection<MetadataType = { [x: string]: any }> {
|
||||
collection: Mongo.Collection<FileObj<MetadataType>>;
|
||||
schema: SimpleSchemaDefinition;
|
||||
|
||||
constructor(config: FilesCollectionConfig<MetadataType>)
|
||||
|
||||
/**
|
||||
* Find and return Cursor for matching documents.
|
||||
*
|
||||
* @param selector [[http://docs.meteor.com/api/collections.html#selectors | Mongo-Style selector]]
|
||||
* @param options [[http://docs.meteor.com/api/collections.html#sortspecifiers | Mongo-Style selector Options]]
|
||||
*
|
||||
* @template TransformAdditions Additional properties provided by transforming a document with options.tranform().
|
||||
* Note that removing fields with a transform function is not currently supported as this may break
|
||||
* functions defined on a FileRef or FileCursor.
|
||||
*/
|
||||
find<TransformAdditions = {}>(
|
||||
selector?: Mongo.Selector<Partial<FileObj<MetadataType>>>,
|
||||
options?: SearchOptions<MetadataType, TransformAdditions>
|
||||
): FilesCursor<MetadataType, TransformAdditions>;
|
||||
|
||||
/**
|
||||
* Finds the first document that matches the selector, as ordered by sort and skip options.
|
||||
*
|
||||
* @param selector [[http://docs.meteor.com/api/collections.html#selectors | Mongo-Style selector]]
|
||||
* @param options [[http://docs.meteor.com/api/collections.html#sortspecifiers | Mongo-Style selector Options]]
|
||||
*
|
||||
* @template TransformAdditions Additional properties provided by transforming a document with options.tranform().
|
||||
* Note that removing fields with a transform function is not currently supported as this may break
|
||||
* functions defined on a FileRef or FileCursor.
|
||||
*/
|
||||
findOne<TransformAdditions = {}>(
|
||||
selector?: Mongo.Selector<Partial<FileObj<MetadataType>>> | string,
|
||||
options?: SearchOptions<MetadataType, TransformAdditions>
|
||||
): FileCursor<MetadataType> & TransformAdditions;
|
||||
|
||||
insert(settings: InsertOptions<MetadataType>, autoStart?: boolean): FileUpload;
|
||||
remove(select: Mongo.Selector<FileObj<MetadataType>> | string, callback?: (error: Meteor.Error) => void): FilesCollection<MetadataType>;
|
||||
update(select: Mongo.Selector<FileObj<MetadataType>> | string, modifier: Mongo.Modifier<FileObj<MetadataType>>, options?: {
|
||||
multi?: boolean;
|
||||
upsert?: boolean;
|
||||
arrayFilters?: Array<{ [identifier: string]: any }>;
|
||||
}, callback?: (error: Meteor.Error, insertedCount: number) => void): FilesCollection<MetadataType>;
|
||||
link(fileRef: FileRef<MetadataType>, version?: string): string;
|
||||
allow(options: Mongo.AllowDenyOptions): void;
|
||||
deny(options: Mongo.AllowDenyOptions): void;
|
||||
denyClient(): void;
|
||||
on(event: string, callback: (fileRef: FileRef<MetadataType>) => void): void;
|
||||
unlink(fileRef: FileRef<MetadataType>, version?: string): FilesCollection<MetadataType>;
|
||||
addFile(path: string, opts: LoadOptions<MetadataType>, callback?: (err: any, fileRef: FileRef<MetadataType>) => any, proceedAfterUpload?: boolean): FilesCollection<MetadataType>;
|
||||
load(url: string, opts: LoadOptions<MetadataType>, callback?: (err: object, fileRef: FileRef<MetadataType>) => any, proceedAfterUpload?: boolean): FilesCollection<MetadataType>;
|
||||
write(buffer: Buffer, opts: LoadOptions<MetadataType>, callback?: (err: object, fileRef: FileRef<MetadataType>) => any, proceedAfterUpload?: boolean): FilesCollection<MetadataType>;
|
||||
}
|
||||
}
|
||||
15
app/imports/@types/vue-meteor.d.ts
vendored
Normal file
15
app/imports/@types/vue-meteor.d.ts
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
import Vue from 'vue';
|
||||
|
||||
declare module 'vue/types/options' {
|
||||
interface ComponentOptions<V extends Vue> {
|
||||
meteor?: any;
|
||||
}
|
||||
}
|
||||
|
||||
declare module 'vue/types/vue' {
|
||||
interface Vue {
|
||||
$subscribe: (name: string, params: any[]) => void;
|
||||
$autorun: (fn: () => void) => number;
|
||||
$subReady: Record<string, boolean>;
|
||||
}
|
||||
}
|
||||
@@ -21,14 +21,8 @@ export default function verifyArchiveSafety({ meta, creature, properties, experi
|
||||
}
|
||||
});
|
||||
properties.forEach(prop => {
|
||||
if (meta.schemaVersion.schemaVersion >= 3) {
|
||||
if (prop.root?.id !== creatureId) {
|
||||
throw new Meteor.Error('Malicious prop', 'Properties contains an entry for the wrong creature');
|
||||
}
|
||||
} else {
|
||||
if (prop.ancestors?.[0]?.id !== creatureId) {
|
||||
throw new Meteor.Error('Malicious prop', 'Properties contains an entry for the wrong creature');
|
||||
}
|
||||
if (prop.root?.id !== creatureId) {
|
||||
throw new Meteor.Error('Malicious prop', 'Properties contains an entry for the wrong creature');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -10,10 +10,11 @@ import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS';
|
||||
// TODO make this a union type of all CreatureProperty types
|
||||
const CreatureProperties: Mongo.Collection<any> = new Mongo.Collection('creatureProperties');
|
||||
|
||||
export interface CreatureProperty {
|
||||
export interface CreatureProperty extends TreeDoc {
|
||||
_id: string
|
||||
_migrationError?: string
|
||||
tags: string[]
|
||||
type: string
|
||||
disabled?: boolean
|
||||
icon?: {
|
||||
name: string
|
||||
|
||||
@@ -79,6 +79,8 @@ if (Meteor.isClient) {
|
||||
} else if (Meteor.isServer) {
|
||||
Meteor.startup(() => {
|
||||
if (!Docs.findOne()) {
|
||||
console.warn('Default documents must be updated to new parenting format');
|
||||
return;
|
||||
Assets.getText('docs/defaultDocs.json', (error, string) => {
|
||||
const docs = JSON.parse(string)
|
||||
docs.forEach(doc => Docs.insert(doc));
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import SimpleSchema from 'simpl-schema';
|
||||
import TaskResult from './tasks/TaskResult';
|
||||
import LogContentSchema from '/imports/api/creature/log/LogContentSchema';
|
||||
import { Mongo } from 'meteor/mongo';
|
||||
|
||||
const EngineActions = new Mongo.Collection<EngineAction>('actions');
|
||||
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
import { EngineAction } from '/imports/api/engine/action/EngineActions';
|
||||
|
||||
export async function writeChangedAction(originalAction: EngineAction, action: EngineAction) {
|
||||
console.warn('writeChangedAction not implemented.');
|
||||
}
|
||||
@@ -8,8 +8,6 @@ import {
|
||||
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties';
|
||||
import { assertEditPermission } from '/imports/api/creature/creatures/creaturePermissions';
|
||||
import { damagePropertyWork } from '/imports/api/creature/creatureProperties/methods/damageProperty';
|
||||
import { doActionWork } from '/imports/api/engine/actions/doAction';
|
||||
import ActionContext from '/imports/api/engine/actions/ActionContext';
|
||||
|
||||
// TODO Migrate this to the new action engine
|
||||
|
||||
@@ -48,6 +46,8 @@ const doAction = new ValidatedMethod({
|
||||
timeInterval: 5000,
|
||||
},
|
||||
run({ spellId, slotId, ritual, targetIds = [], scope = {} }) {
|
||||
console.warn('Do cast spell not implemented');
|
||||
return;
|
||||
// Get action context
|
||||
let spell = CreatureProperties.findOne(spellId);
|
||||
const creatureId = spell.root.id;
|
||||
|
||||
@@ -24,6 +24,8 @@ const doCheck = new ValidatedMethod({
|
||||
timeInterval: 5000,
|
||||
},
|
||||
run({ propId, scope }) {
|
||||
console.warn('do check not implemented');
|
||||
return;
|
||||
const prop = CreatureProperties.findOne(propId);
|
||||
if (!prop) throw new Meteor.Error('not-found', 'The property was not found');
|
||||
const creatureId = prop.root.id;
|
||||
|
||||
@@ -3,6 +3,9 @@ import SimpleSchema from 'simpl-schema';
|
||||
import EngineActions from '/imports/api/engine/action/EngineActions';
|
||||
import { assertEditPermission } from '/imports/api/sharing/sharingPermissions';
|
||||
import { getCreature } from '/imports/api/engine/loadCreatures';
|
||||
import { EJSON } from 'meteor/ejson';
|
||||
import { applyAction } from '/imports/api/engine/action/functions/applyAction';
|
||||
import { writeChangedAction } from '../functions/writeChangedAction';
|
||||
|
||||
export const runAction = new ValidatedMethod({
|
||||
name: 'actions.runAction',
|
||||
|
||||
@@ -8,12 +8,12 @@ interface CreatureProperty {
|
||||
}
|
||||
|
||||
export default class CreatureComputation {
|
||||
originalPropsById: object;
|
||||
propsById: object;
|
||||
propsWithTag: object;
|
||||
scope: object;
|
||||
originalPropsById: Record<string, CreatureProperty>;
|
||||
propsById: Record<string, CreatureProperty>;
|
||||
propsWithTag: Record<string, string[]>;
|
||||
scope: Record<string, any>;
|
||||
props: Array<CreatureProperty>;
|
||||
dependencyGraph: Graph;
|
||||
dependencyGraph: Graph<any, string>;
|
||||
errors: Array<object>;
|
||||
creature: object;
|
||||
variables: object;
|
||||
|
||||
@@ -27,7 +27,7 @@ function isActive(prop: CreatureProperty): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
function childrenActive(prop): boolean {
|
||||
function childrenActive(prop: CreatureProperty): boolean {
|
||||
// Children of disabled properties are always inactive
|
||||
if (prop.disabled) return false;
|
||||
switch (prop.type) {
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
import walkDown from '/imports/api/engine/computation/utility/walkdown';
|
||||
import { getEffectTagTargets } from '/imports/api/engine/computation/buildComputation/linkTypeDependencies';
|
||||
import { Forest, TreeNode } from '/imports/api/parenting/parentingFunctions';
|
||||
import { CreatureProperty } from '/imports/api/creature/creatureProperties/CreatureProperties';
|
||||
import CreatureComputation from '/imports/api/engine/computation/CreatureComputation';
|
||||
|
||||
export default function computeToggleDependencies(node, dependencyGraph, computation, forest) {
|
||||
export default function computeToggleDependencies(
|
||||
node: TreeNode<CreatureProperty>, computation: CreatureComputation, forest: Forest<CreatureProperty>
|
||||
) {
|
||||
const prop = node.doc
|
||||
// Only for toggles
|
||||
if (prop.type !== 'toggle') return;
|
||||
@@ -12,11 +17,11 @@ export default function computeToggleDependencies(node, dependencyGraph, computa
|
||||
const target = forest.nodeIndex[targetId];
|
||||
if (!target) return;
|
||||
target.doc._computationDetails.toggleAncestors.push(prop);
|
||||
dependencyGraph.addLink(target.doc._id, prop._id, 'toggle');
|
||||
computation.dependencyGraph.addLink(target.doc._id, prop._id, 'toggle');
|
||||
walkDown(target.children, child => {
|
||||
// The child nodes depend on the toggle
|
||||
child.doc._computationDetails.toggleAncestors.push(prop);
|
||||
dependencyGraph.addLink(child.doc._id, prop._id, 'toggle');
|
||||
computation.dependencyGraph.addLink(child.doc._id, prop._id, 'toggle');
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -27,6 +32,6 @@ export default function computeToggleDependencies(node, dependencyGraph, computa
|
||||
walkDown(node.children, child => {
|
||||
// The child nodes depend on the toggle
|
||||
child.doc._computationDetails.toggleAncestors.push(prop);
|
||||
dependencyGraph.addLink(child.doc._id, prop._id, 'toggle');
|
||||
computation.dependencyGraph.addLink(child.doc._id, prop._id, 'toggle');
|
||||
});
|
||||
}
|
||||
@@ -4,7 +4,7 @@
|
||||
*/
|
||||
export default function linkInventory(forest, dependencyGraph) {
|
||||
// The stack of properties to still navigate
|
||||
const stack = [...forest];
|
||||
const stack = [...forest.trees];
|
||||
// The current containers we are inside of
|
||||
const containerStack = [];
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { applyNestedSetProperties, calculateNestedSetOperations } from '/imports/api/parenting/parentingFunctions';
|
||||
import { DenormalisedOnlyCreaturePropertySchema as denormSchema }
|
||||
import { applyNestedSetProperties } from '/imports/api/parenting/parentingFunctions';
|
||||
import { CreatureProperty, DenormalisedOnlyCreaturePropertySchema as denormSchema }
|
||||
from '/imports/api/creature/creatureProperties/CreatureProperties';
|
||||
import { getProperties, getCreature, getVariables } from '/imports/api/engine/loadCreatures';
|
||||
import computedOnlySchemas from '/imports/api/properties/computedOnlyPropertySchemasIndex';
|
||||
@@ -29,7 +29,7 @@ import removeSchemaFields from './buildComputation/removeSchemaFields';
|
||||
* computed toggles
|
||||
*/
|
||||
|
||||
export default function buildCreatureComputation(creatureId) {
|
||||
export default function buildCreatureComputation(creatureId: string) {
|
||||
const creature = getCreature(creatureId);
|
||||
const variables = getVariables(creatureId);
|
||||
const properties = getProperties(creatureId);
|
||||
@@ -37,7 +37,9 @@ export default function buildCreatureComputation(creatureId) {
|
||||
return computation;
|
||||
}
|
||||
|
||||
export function buildComputationFromProps(properties, creature, variables) {
|
||||
export function buildComputationFromProps(
|
||||
properties: CreatureProperty[], creature, variables
|
||||
) {
|
||||
|
||||
const computation = new CreatureComputation(properties, creature, variables);
|
||||
// Dependency graph where edge(a, b) means a depends on b
|
||||
@@ -89,13 +91,13 @@ export function buildComputationFromProps(properties, creature, variables) {
|
||||
const forest = applyNestedSetProperties(properties);
|
||||
|
||||
// Walk the property trees computing things that need to be inherited
|
||||
walkDown(forest, node => {
|
||||
walkDown(forest.trees, node => {
|
||||
computeInactiveStatus(node);
|
||||
});
|
||||
// Inactive status must be complete for the whole tree before toggle deps
|
||||
// are calculated
|
||||
walkDown(forest, node => {
|
||||
computeToggleDependencies(node, dependencyGraph, computation, forest);
|
||||
walkDown(forest.trees, node => {
|
||||
computeToggleDependencies(node, computation, forest);
|
||||
computeSlotQuantityFilled(node, dependencyGraph);
|
||||
});
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { buildComputationFromProps } from '/imports/api/engine/computation/buildCreatureComputation.js';
|
||||
import { buildComputationFromProps } from '/imports/api/engine/computation/buildCreatureComputation';
|
||||
import { assert } from 'chai';
|
||||
import computeCreatureComputation from '../../computeCreatureComputation.js';
|
||||
import clean from '../../utility/cleanProp.testFn.js';
|
||||
import computeCreatureComputation from '../../computeCreatureComputation';
|
||||
import clean from '../../utility/cleanProp.testFn';
|
||||
|
||||
export default async function () {
|
||||
const computation = buildComputationFromProps(testProperties);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { buildComputationFromProps } from '/imports/api/engine/computation/buildCreatureComputation.js';
|
||||
import { buildComputationFromProps } from '/imports/api/engine/computation/buildCreatureComputation';
|
||||
import { assert } from 'chai';
|
||||
import computeCreatureComputation from '../../computeCreatureComputation.js';
|
||||
import { propsFromForest } from '/imports/api/properties/tests/propTestBuilder.testFn.js';
|
||||
import computeCreatureComputation from '../../computeCreatureComputation';
|
||||
import { propsFromForest } from '/imports/api/properties/tests/propTestBuilder.testFn';
|
||||
|
||||
export default async function () {
|
||||
const computation = buildComputationFromProps(testProperties);
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
export default function walkDown(tree, callback){
|
||||
let stack = [...tree];
|
||||
while(stack.length){
|
||||
let node = stack.pop();
|
||||
callback(node, stack);
|
||||
stack.push(...node.children);
|
||||
}
|
||||
}
|
||||
14
app/imports/api/engine/computation/utility/walkdown.ts
Normal file
14
app/imports/api/engine/computation/utility/walkdown.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { TreeDoc } from '/imports/api/parenting/ChildSchema';
|
||||
import { TreeNode } from '/imports/api/parenting/parentingFunctions';
|
||||
|
||||
export default function walkDown<T extends TreeDoc>(
|
||||
trees: TreeNode<T>[], callback: (node: TreeNode<T>, stack: TreeNode<T>[]) => any
|
||||
) {
|
||||
const stack = [...trees];
|
||||
while (stack.length) {
|
||||
const node = stack.pop();
|
||||
if (!node) return;
|
||||
callback(node, stack);
|
||||
stack.push(...node.children);
|
||||
}
|
||||
}
|
||||
@@ -30,7 +30,6 @@ async function computeComputation(computation, creatureId) {
|
||||
logError.location = e.stack.split('\n')[1];
|
||||
}
|
||||
console.error(logError);
|
||||
throw e;
|
||||
} finally {
|
||||
checkPropertyCount(computation)
|
||||
writeErrors(creatureId, computation.errors);
|
||||
|
||||
@@ -52,7 +52,7 @@ export function getSingleProperty(creatureId: string, propertyId: string) {
|
||||
return prop;
|
||||
}
|
||||
|
||||
export function getProperties(creatureId) {
|
||||
export function getProperties(creatureId: string): CreatureProperty[] {
|
||||
const creature = loadedCreatures.get(creatureId);
|
||||
if (creature) {
|
||||
const props = Array.from(creature.properties.values());
|
||||
@@ -94,7 +94,7 @@ export function getPropertiesOfType(creatureId, propType) {
|
||||
return props;
|
||||
}
|
||||
|
||||
export function getCreature(creatureId) {
|
||||
export function getCreature(creatureId: string) {
|
||||
const loadedCreature = loadedCreatures.get(creatureId);
|
||||
const loadedCreatureDoc = loadedCreature?.creature;
|
||||
if (loadedCreatureDoc) {
|
||||
@@ -106,7 +106,7 @@ export function getCreature(creatureId) {
|
||||
return creature;
|
||||
}
|
||||
|
||||
export function getVariables(creatureId) {
|
||||
export function getVariables(creatureId: string) {
|
||||
const loadedCreature = loadedCreatures.get(creatureId);
|
||||
const loadedVariables = loadedCreature?.variables;
|
||||
if (loadedVariables) {
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
import { Meteor } from 'meteor/meteor';
|
||||
import { each, clone } from 'lodash';
|
||||
import { Random } from 'meteor/random';
|
||||
import { FilesCollection } from 'meteor/ostrio:files';
|
||||
import { FileObj, FileRef, FilesCollection, FilesCollectionConfig } from 'meteor/ostrio:files';
|
||||
import stream from 'stream';
|
||||
import S3 from 'aws-sdk/clients/s3';
|
||||
import { S3 } from '@aws-sdk/client-s3';
|
||||
|
||||
/* See fs-extra and graceful-fs NPM packages */
|
||||
/* For better i/o performance */
|
||||
@@ -21,26 +21,32 @@ Meteor.settings.useS3 = !!(
|
||||
s3Conf && s3Conf.key && s3Conf.secret && s3Conf.bucket && s3Conf.endpoint
|
||||
);
|
||||
|
||||
const bound = Meteor.bindEnvironment((callback) => {
|
||||
const bound = Meteor.bindEnvironment((callback: () => any) => {
|
||||
return callback();
|
||||
});
|
||||
|
||||
let createS3FilesCollection;
|
||||
|
||||
type S3Metadata = {
|
||||
pipePath: string,
|
||||
}
|
||||
|
||||
type S3FilesCollection = FilesCollection<S3Metadata> & {
|
||||
readJSONFile?: (file: FileObj<S3Metadata>) => Promise<any>
|
||||
};
|
||||
|
||||
/* Check settings existence in `Meteor.settings` */
|
||||
/* This is the best practice for app security */
|
||||
if (Meteor.settings.useS3) {
|
||||
// Create a new S3 object
|
||||
const s3 = new S3({
|
||||
accessKeyId: s3Conf.key,
|
||||
secretAccessKey: s3Conf.secret,
|
||||
credentials: {
|
||||
accessKeyId: s3Conf.key,
|
||||
secretAccessKey: s3Conf.secret,
|
||||
},
|
||||
endpoint: s3Conf.endpoint,
|
||||
sslEnabled: true, // optional
|
||||
maxRetries: 10,
|
||||
httpOptions: {
|
||||
timeout: 12000,
|
||||
agent: false
|
||||
}
|
||||
tls: true,
|
||||
maxAttempts: 10,
|
||||
});
|
||||
|
||||
createS3FilesCollection = function ({
|
||||
@@ -50,8 +56,15 @@ if (Meteor.settings.useS3) {
|
||||
onAfterUpload,
|
||||
debug,// = !Meteor.isProduction,
|
||||
allowClientCode = false,
|
||||
}: {
|
||||
collectionName: string,
|
||||
storagePath: string,
|
||||
onBeforeUpload: (...args: any[]) => any,
|
||||
onAfterUpload: (...args: any[]) => any,
|
||||
debug: boolean,
|
||||
allowClientCode?: boolean,
|
||||
}) {
|
||||
const collection = new FilesCollection({
|
||||
const filesCollection: S3FilesCollection = new FilesCollection<S3Metadata>({
|
||||
collectionName,
|
||||
storagePath,
|
||||
onBeforeUpload,
|
||||
@@ -80,31 +93,35 @@ if (Meteor.settings.useS3) {
|
||||
Key: filePath,
|
||||
Body: fs.createReadStream(vRef.path),
|
||||
ContentType: vRef.type,
|
||||
}, (error) => {
|
||||
}, (error: Error) => {
|
||||
bound(() => {
|
||||
if (error) {
|
||||
console.error(error);
|
||||
} else {
|
||||
// Update FilesCollection with link to the file at AWS
|
||||
const upd = { $set: {} };
|
||||
upd['$set']['versions.' + version + '.meta.pipePath'] = filePath;
|
||||
|
||||
this.collection.update({
|
||||
_id: fileRef._id
|
||||
}, upd, (updError) => {
|
||||
if (updError) {
|
||||
console.error(updError);
|
||||
} else {
|
||||
// Unlink original files from FS after successful upload to AWS:S3
|
||||
this.unlink(this.collection.findOne(fileRef._id), version);
|
||||
}
|
||||
});
|
||||
return console.error(error);
|
||||
}
|
||||
// Update FilesCollection with link to the file at AWS
|
||||
// any should actually be Mongo.Modifier<FileObj<S3Metadata>>, but the types aren't quite set up
|
||||
// Right for mongo modifiers on version.meta
|
||||
const upd: any = {
|
||||
$set: {
|
||||
[`versions.${version}.meta.pipePath`]: filePath
|
||||
}
|
||||
};
|
||||
|
||||
filesCollection.collection.update({
|
||||
_id: fileRef._id
|
||||
}, upd, undefined, (updError: any) => {
|
||||
if (updError) {
|
||||
console.error(updError);
|
||||
} else {
|
||||
// Unlink original files from FS after successful upload to AWS:S3
|
||||
filesCollection.unlink(filesCollection.findOne(fileRef._id), version);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
interceptDownload(http, fileRef, version) {
|
||||
interceptDownload(http: any, fileRef: FileRef<S3Metadata>, version: string) {
|
||||
// Intercept access to the file
|
||||
// And redirect request to AWS:S3
|
||||
let path;
|
||||
@@ -122,20 +139,20 @@ if (Meteor.settings.useS3) {
|
||||
// and to keep original file name, content-type,
|
||||
// content-disposition, chunked "streaming" and cache-control
|
||||
// we're using low-level .serve() method
|
||||
const opts = {
|
||||
const opts: Parameters<typeof s3.getObject>[0] = {
|
||||
Bucket: s3Conf.bucket,
|
||||
Key: path
|
||||
};
|
||||
|
||||
if (http.request.headers.range) {
|
||||
const vRef = fileRef.versions[version];
|
||||
let range = clone(http.request.headers.range);
|
||||
const range = clone(http.request.headers.range);
|
||||
const array = range.split(/bytes=([0-9]*)-([0-9]*)/);
|
||||
const start = parseInt(array[1]);
|
||||
let end = parseInt(array[2]);
|
||||
if (isNaN(end)) {
|
||||
// Request data from AWS:S3 by small chunks
|
||||
end = (start + this.chunkSize) - 1;
|
||||
end = (start + (this.chunkSize || 0)) - 1;
|
||||
if (end >= vRef.size) {
|
||||
end = vRef.size - 1;
|
||||
}
|
||||
@@ -173,8 +190,8 @@ if (Meteor.settings.useS3) {
|
||||
allowClientCode,
|
||||
});
|
||||
// Intercept FilesCollection's remove method to remove file from AWS:S3
|
||||
const _origRemove = collection.remove;
|
||||
collection.remove = function (search) {
|
||||
const _origRemove = filesCollection.remove;
|
||||
filesCollection.remove = function (search) {
|
||||
const cursor = this.collection.find(search);
|
||||
cursor.forEach((fileRef) => {
|
||||
each(fileRef.versions, (vRef) => {
|
||||
@@ -183,7 +200,7 @@ if (Meteor.settings.useS3) {
|
||||
s3.deleteObject({
|
||||
Bucket: s3Conf.bucket,
|
||||
Key: vRef.meta.pipePath,
|
||||
}, (error) => {
|
||||
}, (error: any) => {
|
||||
bound(() => {
|
||||
if (error) {
|
||||
console.error(error);
|
||||
@@ -195,18 +212,19 @@ if (Meteor.settings.useS3) {
|
||||
});
|
||||
|
||||
//remove original file from database
|
||||
_origRemove.call(this, search);
|
||||
return _origRemove.call(this, search);
|
||||
};
|
||||
|
||||
collection.readJSONFile = async function (file) {
|
||||
filesCollection.readJSONFile = async function (file: FileObj<S3Metadata>) {
|
||||
// If there is the pipepath, use s3 to get the file
|
||||
if (file?.versions?.original?.meta?.pipePath) {
|
||||
const path = file.versions.original.meta.pipePath;
|
||||
const data = await s3.getObject({
|
||||
Bucket: s3Conf.bucket,
|
||||
Key: path
|
||||
}).promise();
|
||||
return JSON.parse(data.Body.toString('utf-8'));
|
||||
});
|
||||
if (!data.Body) return;
|
||||
return JSON.parse(data.Body.toString());
|
||||
} else {
|
||||
// Otherwise use the normal filesystem
|
||||
const fileString = await fsp.readFile(file.path, 'utf8');
|
||||
@@ -214,7 +232,7 @@ if (Meteor.settings.useS3) {
|
||||
}
|
||||
};
|
||||
|
||||
return collection;
|
||||
return filesCollection;
|
||||
}
|
||||
} else {
|
||||
createS3FilesCollection = function ({
|
||||
@@ -224,8 +242,8 @@ if (Meteor.settings.useS3) {
|
||||
onAfterUpload,
|
||||
debug,// = !Meteor.isProduction,
|
||||
allowClientCode = false,
|
||||
}) {
|
||||
const collection = new FilesCollection({
|
||||
}: FilesCollectionConfig<S3Metadata>) {
|
||||
const collection: S3FilesCollection = new FilesCollection<S3Metadata>({
|
||||
collectionName,
|
||||
storagePath,
|
||||
onBeforeUpload,
|
||||
@@ -2,6 +2,7 @@ import { chain, reverse, set } from 'lodash';
|
||||
import { TreeDoc, treeDocFields, Reference } from '/imports/api/parenting/ChildSchema';
|
||||
import { getProperties } from '/imports/api/engine/loadCreatures';
|
||||
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties';
|
||||
import { Mongo } from 'meteor/mongo';
|
||||
|
||||
export function getCollectionByName(name: string): Mongo.Collection<TreeDoc> {
|
||||
const collection = Mongo.Collection.get(name)
|
||||
@@ -140,9 +141,9 @@ export function filterToForest(
|
||||
});
|
||||
|
||||
// Get the doc ancestors
|
||||
let ancestors: object[] = [];
|
||||
let ancestors: FilteredDoc[] = [];
|
||||
if (filter && includeFilteredDocAncestors) {
|
||||
ancestors = collection.find(getFilter.ancestorsOfAll(docs), collectionOptions).map(doc => {
|
||||
ancestors = collection.find(getFilter.ancestorsOfAll(docs), collectionOptions).map((doc: FilteredDoc) => {
|
||||
// Mark that the nodes are ancestors of the found nodes
|
||||
doc._ancestorOfMatchedDocument = true;
|
||||
return doc;
|
||||
@@ -172,7 +173,12 @@ export function filterToForest(
|
||||
return docsToForest(nodes);
|
||||
}
|
||||
|
||||
type ForestAndOrphans = { forest: TreeNode<TreeDoc>[], orphanIds: string[] }
|
||||
export type Forest<T extends TreeDoc> = {
|
||||
trees: TreeNode<T>[],
|
||||
nodeIndex: { [_id: string]: TreeNode<T> },
|
||||
orphanIds: string[],
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a complete set of documents and builds a forest using just their `.parentIds`
|
||||
* Uses `.left` for sibling order within a parent only.
|
||||
@@ -182,31 +188,35 @@ type ForestAndOrphans = { forest: TreeNode<TreeDoc>[], orphanIds: string[] }
|
||||
* @returns forest: An array of tree nodes that each contain a document and its children.
|
||||
* orphans: an array of the same, but their parents weren't in the input array
|
||||
*/
|
||||
export function docsToForestByParentId(docs: TreeDoc[]): ForestAndOrphans {
|
||||
export function docsToForestByParentId<T extends TreeDoc>(docs: T[]): Forest<T> {
|
||||
// Collect all the docs in a dict by id
|
||||
const nodesById = <{ [_id: string]: TreeNode<TreeDoc> }>{};
|
||||
const nodeIndex = <{ [_id: string]: TreeNode<T> }>{};
|
||||
docs.forEach(doc => {
|
||||
nodesById[doc._id] = { doc, children: [] };
|
||||
nodeIndex[doc._id] = { doc, children: [] };
|
||||
});
|
||||
// Assign the docs to their parent or the forest or orphanage
|
||||
const forest: TreeNode<TreeDoc>[] = [];
|
||||
const trees: TreeNode<T>[] = [];
|
||||
const orphanIds: string[] = [];
|
||||
docs.forEach(doc => {
|
||||
const node = nodesById[doc._id];
|
||||
const node = nodeIndex[doc._id];
|
||||
if (!doc.parentId) {
|
||||
// Root is parent
|
||||
forest.push(node);
|
||||
} else if (nodesById[doc.parentId]) {
|
||||
trees.push(node);
|
||||
} else if (nodeIndex[doc.parentId]) {
|
||||
// Parent is found
|
||||
nodesById[doc.parentId].children.push(node);
|
||||
nodeIndex[doc.parentId].children.push(node);
|
||||
} else {
|
||||
// Parent is missing, unset it, and store orphan
|
||||
node.doc.parentId = undefined;
|
||||
orphanIds.push(node.doc._id);
|
||||
forest.push(node);
|
||||
trees.push(node);
|
||||
}
|
||||
});
|
||||
return { forest, orphanIds };
|
||||
return {
|
||||
trees,
|
||||
orphanIds,
|
||||
nodeIndex
|
||||
};
|
||||
}
|
||||
|
||||
export const getFilter = {
|
||||
@@ -700,7 +710,7 @@ export async function rebuildCreatureNestedSets(creatureId) {
|
||||
* @returns
|
||||
*/
|
||||
export function calculateNestedSetOperations(docs: TreeDoc[]) {
|
||||
const { forest: stack, orphanIds } = docsToForestByParentId(reverse(docs));
|
||||
const { trees: stack, orphanIds } = docsToForestByParentId(reverse(docs));
|
||||
const removeMissingParentsOp = orphanIds.length ? {
|
||||
updateMany: {
|
||||
filter: { _id: { $in: orphanIds } },
|
||||
@@ -763,11 +773,12 @@ export function calculateNestedSetOperations(docs: TreeDoc[]) {
|
||||
* @param docs An array of documents that share a common root. Must already be sorted by `.left` in ascending order
|
||||
* @returns The documents as a forest of tree nodes
|
||||
*/
|
||||
export function applyNestedSetProperties(docs: TreeDoc[]) {
|
||||
export function applyNestedSetProperties<T extends TreeDoc>(docs: T[]): Forest<T> {
|
||||
// Walk around the tree numbering left on the way down and right on the way up like so:
|
||||
const { forest, orphanIds } = docsToForestByParentId(reverse([...docs]));
|
||||
const forest = docsToForestByParentId<T>(reverse([...docs]));
|
||||
const { trees, orphanIds } = forest;
|
||||
|
||||
const stack = [...forest];
|
||||
const stack = [...trees];
|
||||
const visitedNodes = new Set();
|
||||
const visitedChildren = new Set();
|
||||
let count = 1;
|
||||
|
||||
@@ -42,7 +42,6 @@
|
||||
|
||||
<script lang="js">
|
||||
import DialogBase from '/imports/client/ui/dialogStack/DialogBase.vue';
|
||||
import Actions, { runAction } from '/imports/api/engine/actions/ActionEngine';
|
||||
import CreatureProperties from '/imports/api/creature/creatureProperties/CreatureProperties';
|
||||
import TreeNodeView from '/imports/client/ui/properties/treeNodeViews/TreeNodeView.vue';
|
||||
|
||||
@@ -80,10 +79,7 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
async apply(stepThrough) {
|
||||
await runAction.callAsync({
|
||||
actionId: this.actionId,
|
||||
stepThrough
|
||||
});
|
||||
throw new Error('Not implemented')
|
||||
},
|
||||
cancel() {
|
||||
this.$store.dispatch('popDialogStack');
|
||||
|
||||
@@ -151,7 +151,7 @@
|
||||
creatureId: this.creatureId,
|
||||
noBackdropClose: true,
|
||||
},
|
||||
callback(result){
|
||||
async callback(result){
|
||||
if (!result){
|
||||
return 'insert-creature-property-fab';
|
||||
}
|
||||
@@ -164,7 +164,7 @@
|
||||
revealFab(fab);
|
||||
let creatureProperty = result;
|
||||
// Insert the property
|
||||
let id = insertProperty.call({creatureProperty, parentRef});
|
||||
let id = await insertProperty.callAsync({creatureProperty, parentRef});
|
||||
return forcedType ? id : `tree-node-${id}`;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
</v-icon>
|
||||
<span
|
||||
v-if="noLinks"
|
||||
:key="prop._id"
|
||||
:key="prop._id + '-no-links'"
|
||||
>
|
||||
<tree-node-view
|
||||
:model="prop"
|
||||
@@ -53,8 +53,9 @@
|
||||
</template>
|
||||
|
||||
<script lang="js">
|
||||
import { fetchDocByRef } from '/imports/api/parenting/parentingFunctions';
|
||||
import { getFilter } from '/imports/api/parenting/parentingFunctions';
|
||||
import TreeNodeView from '/imports/client/ui/properties/treeNodeViews/TreeNodeView.vue';
|
||||
import { Mongo } from 'meteor/mongo';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
@@ -75,11 +76,11 @@
|
||||
embedded: Boolean,
|
||||
},
|
||||
computed:{
|
||||
props(){
|
||||
return this.model.ancestors
|
||||
.slice(1)
|
||||
.map(ref => fetchDocByRef(ref))
|
||||
.filter(prop => (this.collection !== 'creatureProperties' || prop.type !== 'propertySlot'));
|
||||
props() {
|
||||
return Mongo.Collection.get(this.collection).find({
|
||||
...getFilter.ancestors(this.model),
|
||||
...this.collection === 'creatureProperties' && { type: { $ne: 'propertySlot' } }
|
||||
});
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
|
||||
@@ -52,20 +52,21 @@
|
||||
/>
|
||||
</v-fade-transition>
|
||||
</template>
|
||||
<div
|
||||
v-if="!embedded"
|
||||
slot="actions"
|
||||
class="layout"
|
||||
>
|
||||
<v-spacer />
|
||||
<v-btn
|
||||
text
|
||||
color="accent"
|
||||
@click="$store.dispatch('popDialogStack')"
|
||||
<template #actions>
|
||||
<div
|
||||
v-if="!embedded"
|
||||
class="layout"
|
||||
>
|
||||
Close
|
||||
</v-btn>
|
||||
</div>
|
||||
<v-spacer />
|
||||
<v-btn
|
||||
text
|
||||
color="accent"
|
||||
@click="$store.dispatch('popDialogStack')"
|
||||
>
|
||||
Close
|
||||
</v-btn>
|
||||
</div>
|
||||
</template>
|
||||
</dialog-base>
|
||||
</template>
|
||||
|
||||
@@ -129,12 +130,7 @@ export default {
|
||||
computed: {
|
||||
creature(){
|
||||
if (!this.model) return;
|
||||
let nearestCreatureAncestor = findLast(
|
||||
this.model.ancestors,
|
||||
ref => ref.collection === 'creatures'
|
||||
);
|
||||
if (!nearestCreatureAncestor) return;
|
||||
return Creatures.findOne(nearestCreatureAncestor.id);
|
||||
return Creatures.findOne(this.model.root.id);
|
||||
},
|
||||
creatureId(){
|
||||
return this.creature && this.creature._id;
|
||||
|
||||
@@ -6,27 +6,27 @@
|
||||
<slot
|
||||
name="replace-toolbar"
|
||||
:flat="!offsetTop"
|
||||
/>
|
||||
<v-toolbar
|
||||
v-if="!$scopedSlots['replace-toolbar']"
|
||||
:color="computedColor"
|
||||
:dark="isDark"
|
||||
:light="!isDark"
|
||||
class="base-dialog-toolbar"
|
||||
:flat="!offsetTop"
|
||||
>
|
||||
<v-toolbar
|
||||
:color="computedColor"
|
||||
:dark="isDark"
|
||||
:light="!isDark"
|
||||
class="base-dialog-toolbar"
|
||||
:flat="!offsetTop"
|
||||
<v-btn
|
||||
icon
|
||||
@click="back"
|
||||
>
|
||||
<v-btn
|
||||
icon
|
||||
@click="back"
|
||||
>
|
||||
<v-icon>mdi-arrow-left</v-icon>
|
||||
</v-btn>
|
||||
<slot name="toolbar" />
|
||||
<slot
|
||||
slot="extension"
|
||||
name="toolbar-extension"
|
||||
/>
|
||||
</v-toolbar>
|
||||
</slot>
|
||||
<v-icon>mdi-arrow-left</v-icon>
|
||||
</v-btn>
|
||||
<slot name="toolbar" />
|
||||
<slot
|
||||
slot="extension"
|
||||
name="toolbar-extension"
|
||||
/>
|
||||
</v-toolbar>
|
||||
<div
|
||||
v-if="$slots['unwrapped-content']"
|
||||
id="base-dialog-body"
|
||||
|
||||
@@ -64,9 +64,9 @@
|
||||
</template>
|
||||
|
||||
<script lang="js">
|
||||
import numberToSignedString from '../../../../../api/utility/numberToSignedString';
|
||||
import doCheck from '/imports/api/engine/action/methods/doCheck';
|
||||
import numberToSignedString from '/imports/api/utility/numberToSignedString';
|
||||
import RollPopup from '/imports/client/ui/components/RollPopup.vue';
|
||||
import doCheck from '/imports/api/engine/actions/doCheck';
|
||||
import { snackbar } from '/imports/client/ui/components/snackbars/SnackbarQueue';
|
||||
|
||||
export default {
|
||||
|
||||
@@ -47,10 +47,10 @@
|
||||
</template>
|
||||
|
||||
<script lang="js">
|
||||
import numberToSignedString from '../../../../../api/utility/numberToSignedString';
|
||||
import RollPopup from '/imports/client/ui/components/RollPopup.vue';
|
||||
import doCheck from '/imports/api/engine/actions/doCheck';
|
||||
import doCheck from '/imports/api/engine/action/methods/doCheck.js';
|
||||
import {snackbar} from '/imports/client/ui/components/snackbars/SnackbarQueue';
|
||||
import numberToSignedString from '/imports/api/utility/numberToSignedString';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
|
||||
@@ -32,8 +32,8 @@
|
||||
|
||||
<script lang="js">
|
||||
import SpellSlotListTile from '/imports/client/ui/properties/components/attributes/SpellSlotListTile.vue';
|
||||
import doCastSpell from '/imports/api/engine/actions/doCastSpell';
|
||||
import { snackbar } from '/imports/client/ui/components/snackbars/SnackbarQueue';
|
||||
import doCastSpell from '/imports/api/engine/action/methods/doCastSpell';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
|
||||
@@ -58,11 +58,11 @@
|
||||
</template>
|
||||
|
||||
<script lang="js">
|
||||
import numberToSignedString from '../../../../../api/utility/numberToSignedString';
|
||||
import ProficiencyIcon from '/imports/client/ui/properties/shared/ProficiencyIcon.vue';
|
||||
import RollPopup from '/imports/client/ui/components/RollPopup.vue';
|
||||
import doCheck from '/imports/api/engine/actions/doCheck';
|
||||
import { snackbar } from '/imports/client/ui/components/snackbars/SnackbarQueue';
|
||||
import numberToSignedString from '/imports/api/utility/numberToSignedString';
|
||||
import doCheck from '/imports/api/engine/action/methods/doCheck';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
|
||||
@@ -125,7 +125,7 @@ import AttributeConsumedView from '/imports/client/ui/properties/components/acti
|
||||
import ItemConsumedView from '/imports/client/ui/properties/components/actions/ItemConsumedView.vue';
|
||||
import PropertyIcon from '/imports/client/ui/properties/shared/PropertyIcon.vue';
|
||||
import updateCreatureProperty from '/imports/api/creature/creatureProperties/methods/updateCreatureProperty';
|
||||
import doCastSpell from '/imports/api/engine/actions/doCastSpell';
|
||||
import doCastSpell from '/imports/api/engine/action/methods/doCastSpell.js';
|
||||
import { snackbar } from '/imports/client/ui/components/snackbars/SnackbarQueue';
|
||||
|
||||
export default {
|
||||
@@ -254,4 +254,3 @@ export default {
|
||||
height: 40px;
|
||||
}
|
||||
</style>
|
||||
../../../../api/engine/action/methods/doCastSpell
|
||||
@@ -40,6 +40,7 @@
|
||||
v-bind="$attrs"
|
||||
>
|
||||
<slot>
|
||||
default slot content
|
||||
<template v-if="value !== undefined">
|
||||
{{ valueText }}
|
||||
</template>
|
||||
|
||||
@@ -8,6 +8,8 @@ export default function migrate2To3(archive) {
|
||||
if (prop.parent?.collection === 'creatureProperties') {
|
||||
prop.parentId = prop.parent.id;
|
||||
}
|
||||
prop.left = prop.order;
|
||||
prop.right = prop.order;
|
||||
} catch (e) {
|
||||
console.warn('Property migration 2 -> 3 failed: ', { propId: prop._id, error: e.message || e.reason || e.toString() });
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ Meteor.publish('singleCharacter', function (creatureId) {
|
||||
_creatureId: creatureId,
|
||||
}),
|
||||
CreatureProperties.find({
|
||||
'ancestors.id': creatureId,
|
||||
'root.id': creatureId,
|
||||
}),
|
||||
CreatureLogs.find({
|
||||
creatureId,
|
||||
|
||||
1984
app/package-lock.json
generated
1984
app/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "dicecloud",
|
||||
"version": "2.1.0",
|
||||
"description": "Unofficial Online Realtime D&D 5e App",
|
||||
"description": "Online Realtime TTRPG Engine",
|
||||
"license": "GPL-3.0",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -22,12 +22,12 @@
|
||||
"npm": "6.13.x"
|
||||
},
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-s3": "^3.523.0",
|
||||
"@babel/runtime": "^7.23.9",
|
||||
"@chenfengyuan/vue-countdown": "^1.1.5",
|
||||
"@tozd/vue-observer-utils": "^0.5.0",
|
||||
"@types/meteor": "^2.9.8",
|
||||
"alea": "^1.0.1",
|
||||
"aws-sdk": "^2.1561.0",
|
||||
"bcrypt": "^5.1.1",
|
||||
"chroma-js": "^2.4.2",
|
||||
"css-box-shadow": "^1.0.0-3",
|
||||
@@ -58,17 +58,18 @@
|
||||
"speakingurl": "^14.0.1",
|
||||
"three": "^0.156.1",
|
||||
"vivagraphjs": "^0.12.0",
|
||||
"vue": "2.6.10",
|
||||
"vue": "2.6.14",
|
||||
"vue-meteor-tracker": "^2.0.0",
|
||||
"vue-reactive-provide": "^0.3.0",
|
||||
"vue-router": "^3.6.5",
|
||||
"vuedraggable": "^2.23.2",
|
||||
"vuetify": "^2.7.2",
|
||||
"vuetify": "2.6.15",
|
||||
"vuetify-upload-button": "^2.0.2",
|
||||
"vuex": "^3.1.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/chai": "^4.3.11",
|
||||
"@types/lodash": "^4.14.202",
|
||||
"@types/mocha": "^10.0.6",
|
||||
"@types/simpl-schema": "^1.12.7",
|
||||
"@typescript-eslint/eslint-plugin": "^5.62.0",
|
||||
|
||||
17
package-lock.json
generated
17
package-lock.json
generated
@@ -1,17 +0,0 @@
|
||||
{
|
||||
"requires": true,
|
||||
"lockfileVersion": 1,
|
||||
"dependencies": {
|
||||
"@types/lodash": {
|
||||
"version": "4.14.202",
|
||||
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.202.tgz",
|
||||
"integrity": "sha512-OvlIYQK9tNneDlS0VN54LLd5uiPCBOp7gS5Z0f1mjoJYBrtStzgmJBxONW3U6OZqdtNzZPmn9BS/7WI7BFFcFQ==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/nearley": {
|
||||
"version": "2.11.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/nearley/-/nearley-2.11.5.tgz",
|
||||
"integrity": "sha512-dM7TrN0bVxGGXTYGx4YhGear8ysLO5SOuouAWM9oltjQ3m9oYa13qi8Z1DJp5zxVMPukvQdsrnZmgzpeuTSEQA=="
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user