Reworked log data format, overhauled snackbar
This commit is contained in:
@@ -4,7 +4,7 @@ import embedInlineCalculations from '/imports/api/creature/computation/afterComp
|
|||||||
export default function applyAction({prop, log}){
|
export default function applyAction({prop, log}){
|
||||||
let content = { name: prop.name };
|
let content = { name: prop.name };
|
||||||
if (prop.summary){
|
if (prop.summary){
|
||||||
content.description = embedInlineCalculations(
|
content.value = embedInlineCalculations(
|
||||||
prop.summary, prop.summaryCalculations
|
prop.summary, prop.summaryCalculations
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ export default function applyAdjustment({
|
|||||||
context.errors.forEach(e => {
|
context.errors.forEach(e => {
|
||||||
log.content.push({
|
log.content.push({
|
||||||
name: 'Attribute damage error',
|
name: 'Attribute damage error',
|
||||||
error: e.message || e.toString(),
|
value: e.message || e.toString(),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
if (damageTargets) {
|
if (damageTargets) {
|
||||||
@@ -41,15 +41,15 @@ export default function applyAdjustment({
|
|||||||
});
|
});
|
||||||
log.content.push({
|
log.content.push({
|
||||||
name: 'Attribute damage',
|
name: 'Attribute damage',
|
||||||
resultPrefix: `${prop.stat} ${prop.operation === 'set' ? 'set to' : ''}`,
|
value: `${prop.stat}${prop.operation === 'set' ? ' set to' : ''}` +
|
||||||
result: `${result.isNumber ? -result.value : result.toString()}`,
|
` ${result.isNumber ? -result.value : result.toString()}`,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
log.content.push({
|
log.content.push({
|
||||||
name: 'Attribute damage',
|
name: 'Attribute damage',
|
||||||
resultPrefix: `${prop.stat} ${prop.operation === 'set' ? 'set to' : ''}`,
|
value: `${prop.stat}${prop.operation === 'set' ? ' set to' : ''}` +
|
||||||
result: `${result.isNumber ? -result.value : result.toString()}`,
|
` ${result.isNumber ? -result.value : result.toString()}`,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ export default function applyAttack({
|
|||||||
|
|
||||||
log.content.push({
|
log.content.push({
|
||||||
name: criticalHit ? 'Critical Hit!' : 'To Hit',
|
name: criticalHit ? 'Critical Hit!' : 'To Hit',
|
||||||
resultPrefix: `1d20 [${value}] + ${prop.rollBonusResult} = `,
|
value: `1d20 [${value}] + ${prop.rollBonusResult} = ` + result,
|
||||||
result,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,15 +42,15 @@ export default function applyDamage({
|
|||||||
if (result.constructor.name === 'ErrorNode'){
|
if (result.constructor.name === 'ErrorNode'){
|
||||||
log.content.push({
|
log.content.push({
|
||||||
name: 'Damage error',
|
name: 'Damage error',
|
||||||
error: result.toString(),
|
value: result.toString(),
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Memoise the damage suffix for the log
|
// Memoise the damage suffix for the log
|
||||||
let suffix = (criticalHit ? ' critical ' : '') +
|
let suffix = (criticalHit ? ' critical ' : ' ') +
|
||||||
prop.damageType +
|
prop.damageType +
|
||||||
(prop.damageType !== 'healing' ? ' damage': '');
|
(prop.damageType !== ' healing ' ? ' damage ': '');
|
||||||
|
|
||||||
if (damageTargets && damageTargets.length) {
|
if (damageTargets && damageTargets.length) {
|
||||||
// Iterate through all the targets
|
// Iterate through all the targets
|
||||||
@@ -69,7 +69,7 @@ export default function applyDamage({
|
|||||||
if (result.constructor.name === 'ErrorNode' || !result.isNumber){
|
if (result.constructor.name === 'ErrorNode' || !result.isNumber){
|
||||||
log.content.push({
|
log.content.push({
|
||||||
name: 'Damage error',
|
name: 'Damage error',
|
||||||
error: result.toString(),
|
value: result.toString(),
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -86,26 +86,21 @@ export default function applyDamage({
|
|||||||
// Target is same as self, log damage as such
|
// Target is same as self, log damage as such
|
||||||
log.content.push({
|
log.content.push({
|
||||||
name,
|
name,
|
||||||
result: damageDealt,
|
value: damageDealt + suffix + ' to self',
|
||||||
details: suffix + ' to self',
|
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
log.content.push({
|
log.content.push({
|
||||||
name,
|
name,
|
||||||
resultPrefix: 'Dealt ',
|
value: 'Dealt ' + damageDealt + suffix + ` ${target.name && ' to '}${target.name}`,
|
||||||
result: damageDealt,
|
|
||||||
details: suffix + `${target.name && ' to '}${target.name}`,
|
|
||||||
});
|
});
|
||||||
// Log the damage received on that creature's log as well
|
// Log the damage received on that creature's log as well
|
||||||
insertCreatureLog.call({
|
insertCreatureLog.call({
|
||||||
log: {
|
log: {
|
||||||
|
creatureId: target._id,
|
||||||
content: [{
|
content: [{
|
||||||
name,
|
name,
|
||||||
resultPrefix: 'Recieved ',
|
value: 'Recieved ' + damageDealt + suffix,
|
||||||
result: damageDealt,
|
|
||||||
details: suffix,
|
|
||||||
}],
|
}],
|
||||||
creatureId: target._id,
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -114,8 +109,7 @@ export default function applyDamage({
|
|||||||
// There are no targets, just log the result
|
// There are no targets, just log the result
|
||||||
log.content.push({
|
log.content.push({
|
||||||
name: prop.damageType === 'healing' ? 'Healing' : 'Damage',
|
name: prop.damageType === 'healing' ? 'Healing' : 'Damage',
|
||||||
result: result.toString(),
|
value: result.toString() + suffix,
|
||||||
details: suffix,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ export default function applyRoll({
|
|||||||
}
|
}
|
||||||
log.content.push({
|
log.content.push({
|
||||||
name: prop.name,
|
name: prop.name,
|
||||||
resultPrefix: prop.variableName + ' = ' + prop.roll + ' = ',
|
value: prop.variableName + ' = ' + prop.roll + ' = ' + result.toString(),
|
||||||
result: result.toString(),
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,8 +22,7 @@ export default function applySave({
|
|||||||
let dc = result.value;
|
let dc = result.value;
|
||||||
log.content.push({
|
log.content.push({
|
||||||
name: prop.name,
|
name: prop.name,
|
||||||
resultPrefix: ' DC ',
|
value: ' DC ' + result.toString(),
|
||||||
result: result.toString(),
|
|
||||||
});
|
});
|
||||||
if (prop.target === 'self'){
|
if (prop.target === 'self'){
|
||||||
let save = CreaturesProperties.findOne({
|
let save = CreaturesProperties.findOne({
|
||||||
@@ -36,7 +35,8 @@ export default function applySave({
|
|||||||
});
|
});
|
||||||
if (!save){
|
if (!save){
|
||||||
log.content.push({
|
log.content.push({
|
||||||
error: 'No saving throw found: ' + prop.stat,
|
name: 'Saving throw error',
|
||||||
|
value: 'No saving throw found: ' + prop.stat,
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -60,9 +60,7 @@ export default function applySave({
|
|||||||
let saveSuccess = result >= dc;
|
let saveSuccess = result >= dc;
|
||||||
log.content.push({
|
log.content.push({
|
||||||
name: 'Save',
|
name: 'Save',
|
||||||
resultPrefix,
|
value: resultPrefix + result + (saveSuccess ? 'Passed' : 'Failed')
|
||||||
result,
|
|
||||||
details: saveSuccess ? 'Passed' : 'Failed'
|
|
||||||
});
|
});
|
||||||
return !saveSuccess;
|
return !saveSuccess;
|
||||||
} else {
|
} else {
|
||||||
@@ -71,7 +69,8 @@ export default function applySave({
|
|||||||
}
|
}
|
||||||
} catch (e){
|
} catch (e){
|
||||||
log.content.push({
|
log.content.push({
|
||||||
error: e.toString(),
|
name: 'Save error',
|
||||||
|
value: e.toString(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,14 +21,13 @@ export default function applyToggle({
|
|||||||
if (result.constructor.name === 'ErrorNode') {
|
if (result.constructor.name === 'ErrorNode') {
|
||||||
log.content.push({
|
log.content.push({
|
||||||
name: 'Toggle error',
|
name: 'Toggle error',
|
||||||
error: result.toString(),
|
value: result.toString(),
|
||||||
});
|
});
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
log.content.push({
|
log.content.push({
|
||||||
name: prop.name || 'Toggle',
|
name: prop.name || 'Toggle',
|
||||||
resultPrefix: prop.condition + ' = ',
|
value: prop.condition + ' = ' + result.toString(),
|
||||||
result: result.toString(),
|
|
||||||
});
|
});
|
||||||
return !!result.value;
|
return !!result.value;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ export default function spendResources({prop, log}){
|
|||||||
});
|
});
|
||||||
log.content.push({
|
log.content.push({
|
||||||
name: 'Uses left',
|
name: 'Uses left',
|
||||||
result: prop.usesResult - (prop.usesUsed || 0) - 1,
|
value: prop.usesResult - (prop.usesUsed || 0) - 1,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -84,10 +84,10 @@ export default function spendResources({prop, log}){
|
|||||||
// Log all the spending
|
// Log all the spending
|
||||||
if (gainLog.length) log.content.push({
|
if (gainLog.length) log.content.push({
|
||||||
name: 'Gained',
|
name: 'Gained',
|
||||||
description: gainLog.join('\n'),
|
value: gainLog.join('\n'),
|
||||||
});
|
});
|
||||||
if (spendLog.length) log.content.push({
|
if (spendLog.length) log.content.push({
|
||||||
name: 'Spent',
|
name: 'Spent',
|
||||||
description: spendLog.join('\n'),
|
value: spendLog.join('\n'),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ function removeOldLogs(creatureId){
|
|||||||
sort: {date: -1},
|
sort: {date: -1},
|
||||||
skip: PER_CREATURE_LOG_LIMIT,
|
skip: PER_CREATURE_LOG_LIMIT,
|
||||||
});
|
});
|
||||||
|
if (!firstExpiredLog) return;
|
||||||
// Remove all logs older than the one over the limit
|
// Remove all logs older than the one over the limit
|
||||||
CreatureLogs.remove({
|
CreatureLogs.remove({
|
||||||
creatureId,
|
creatureId,
|
||||||
@@ -69,32 +70,10 @@ function logToMessageData(log){
|
|||||||
let embed = {
|
let embed = {
|
||||||
fields: [],
|
fields: [],
|
||||||
};
|
};
|
||||||
log.content.forEach(c => {
|
log.content.forEach(field => {
|
||||||
let field = {};
|
if (!field.name) field.name = '\u200b';
|
||||||
let descriptionField = {};
|
if (!field.value) field.value = '\u200b';
|
||||||
if (c.name) field.name = c.name;
|
embed.fields.push(field);
|
||||||
let valueArray = [];
|
|
||||||
if (c.error) valueArray.push(`*${c.error}*`);
|
|
||||||
if (c.resultPrefix) valueArray.push(`${c.resultPrefix}`);
|
|
||||||
if (c.result) valueArray.push(`\`${c.result}\``);
|
|
||||||
if (c.details) valueArray.push(c.details);
|
|
||||||
if (valueArray.length) field.value = valueArray.join(' ');
|
|
||||||
if (c.description){
|
|
||||||
if (!field.value){
|
|
||||||
field.value = c.description;
|
|
||||||
} else {
|
|
||||||
descriptionField.value = c.description;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (field.name || field.value){
|
|
||||||
if (!field.name) field.name = '\u200b';
|
|
||||||
if (!field.value) field.value = '\u200b';
|
|
||||||
embed.fields.push(field);
|
|
||||||
}
|
|
||||||
if (descriptionField.value){
|
|
||||||
descriptionField.name = '\u200b';
|
|
||||||
embed.fields.push(descriptionField);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
return { embeds: [embed] };
|
return { embeds: [embed] };
|
||||||
}
|
}
|
||||||
@@ -109,7 +88,7 @@ function logWebhook({log, creature}){
|
|||||||
}
|
}
|
||||||
|
|
||||||
const insertCreatureLog = new ValidatedMethod({
|
const insertCreatureLog = new ValidatedMethod({
|
||||||
name: 'creatureLogs.methods.insertCreatureLog',
|
name: 'creatureLogs.methods.insert',
|
||||||
mixins: [RateLimiterMixin],
|
mixins: [RateLimiterMixin],
|
||||||
rateLimit: {
|
rateLimit: {
|
||||||
numRequests: 5,
|
numRequests: 5,
|
||||||
@@ -138,7 +117,7 @@ const insertCreatureLog = new ValidatedMethod({
|
|||||||
export function insertCreatureLogWork({log, creature, method}){
|
export function insertCreatureLogWork({log, creature, method}){
|
||||||
// Build the new log
|
// Build the new log
|
||||||
if (typeof log === 'string'){
|
if (typeof log === 'string'){
|
||||||
log = {text: log};
|
log = {content: [{value: log}]};
|
||||||
}
|
}
|
||||||
log.date = new Date();
|
log.date = new Date();
|
||||||
// Insert it
|
// Insert it
|
||||||
@@ -190,30 +169,30 @@ const logRoll = new ValidatedMethod({
|
|||||||
parsedResult = parse(roll);
|
parsedResult = parse(roll);
|
||||||
} catch (e){
|
} catch (e){
|
||||||
let error = prettifyParseError(e);
|
let error = prettifyParseError(e);
|
||||||
logContent.push({error});
|
logContent.push({name: 'Parse Error', value: error});
|
||||||
}
|
}
|
||||||
if (parsedResult) try {
|
if (parsedResult) try {
|
||||||
let rollContext = new CompilationContext();
|
let rollContext = new CompilationContext();
|
||||||
let compiled = parsedResult.compile(creature.variables, rollContext);
|
let compiled = parsedResult.compile(creature.variables, rollContext);
|
||||||
let compiledString = compiled.toString();
|
let compiledString = compiled.toString();
|
||||||
if (!equalIgnoringWhitespace(compiledString, roll)) logContent.push({
|
if (!equalIgnoringWhitespace(compiledString, roll)) logContent.push({
|
||||||
details: roll
|
value: roll
|
||||||
});
|
});
|
||||||
logContent.push({
|
logContent.push({
|
||||||
details: compiledString
|
value: compiledString
|
||||||
});
|
});
|
||||||
let rolled = compiled.roll(creature.variables, rollContext);
|
let rolled = compiled.roll(creature.variables, rollContext);
|
||||||
let rolledString = rolled.toString();
|
let rolledString = rolled.toString();
|
||||||
if (rolledString !== compiledString) logContent.push({
|
if (rolledString !== compiledString) logContent.push({
|
||||||
result: rolled.toString()
|
value: rolled.toString()
|
||||||
});
|
});
|
||||||
let result = rolled.reduce(creature.variables, rollContext);
|
let result = rolled.reduce(creature.variables, rollContext);
|
||||||
let resultString = result.toString();
|
let resultString = result.toString();
|
||||||
if (resultString !== rolledString) logContent.push({
|
if (resultString !== rolledString) logContent.push({
|
||||||
result: resultString
|
value: resultString
|
||||||
});
|
});
|
||||||
} catch (e){
|
} catch (e){
|
||||||
logContent = [{error: 'Calculation error'}];
|
logContent = [{name: 'Calculation error'}];
|
||||||
}
|
}
|
||||||
const log = {
|
const log = {
|
||||||
content: logContent,
|
content: logContent,
|
||||||
|
|||||||
@@ -3,31 +3,14 @@ import ErrorSchema from '/imports/api/properties/subSchemas/ErrorSchema.js';
|
|||||||
import RollDetailsSchema from '/imports/api/properties/subSchemas/RollDetailsSchema.js';
|
import RollDetailsSchema from '/imports/api/properties/subSchemas/RollDetailsSchema.js';
|
||||||
|
|
||||||
let LogContentSchema = new SimpleSchema({
|
let LogContentSchema = new SimpleSchema({
|
||||||
|
// The name of the field, included in discord webhook message
|
||||||
name: {
|
name: {
|
||||||
type: String,
|
type: String,
|
||||||
optional: true,
|
optional: true,
|
||||||
},
|
},
|
||||||
error: {
|
// The details of the field, included in discord webhook message
|
||||||
type: String,
|
// Markdown support
|
||||||
optional: true,
|
value: {
|
||||||
},
|
|
||||||
resultPrefix: {
|
|
||||||
type: String,
|
|
||||||
optional: true,
|
|
||||||
},
|
|
||||||
result: {
|
|
||||||
type: String,
|
|
||||||
optional: true,
|
|
||||||
},
|
|
||||||
expandedResult: {
|
|
||||||
type: String,
|
|
||||||
optional: true,
|
|
||||||
},
|
|
||||||
details: {
|
|
||||||
type: String,
|
|
||||||
optional: true,
|
|
||||||
},
|
|
||||||
description: {
|
|
||||||
type: String,
|
type: String,
|
||||||
optional: true,
|
optional: true,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -53,6 +53,10 @@ export default class OperatorNode extends ParseNode {
|
|||||||
}
|
}
|
||||||
toString(){
|
toString(){
|
||||||
let {left, right, operator} = this;
|
let {left, right, operator} = this;
|
||||||
|
// special case of adding a negative number
|
||||||
|
if (operator === '+' && right.isNumber && right.value < 0){
|
||||||
|
return `${left.toString()} - ${-right.value}`
|
||||||
|
}
|
||||||
return `${left.toString()} ${operator} ${right.toString()}`;
|
return `${left.toString()} ${operator} ${right.toString()}`;
|
||||||
}
|
}
|
||||||
traverse(fn){
|
traverse(fn){
|
||||||
|
|||||||
6
app/imports/server/config/SyncedCronConfig.js
Normal file
6
app/imports/server/config/SyncedCronConfig.js
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
import { SyncedCron } from 'meteor/percolate:synced-cron';
|
||||||
|
|
||||||
|
SyncedCron.config({
|
||||||
|
// Log job run details to console
|
||||||
|
log: false,
|
||||||
|
});
|
||||||
16
app/imports/ui/components/snackbars/SnackbarQueue.js
Normal file
16
app/imports/ui/components/snackbars/SnackbarQueue.js
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
// Modified from https://gitlab.com/tozd/vue/snackbar-que
|
||||||
|
import Vue from 'vue';
|
||||||
|
|
||||||
|
const globalState = Vue.observable({queue: []});
|
||||||
|
let lastSnackbarId = 0;
|
||||||
|
|
||||||
|
function snackbar(data) {
|
||||||
|
globalState.queue.push({
|
||||||
|
data,
|
||||||
|
id: ++lastSnackbarId,
|
||||||
|
enqueuedAt: new Date(),
|
||||||
|
shown: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export {snackbar, globalState}
|
||||||
128
app/imports/ui/components/snackbars/SnackbarQueue.vue
Normal file
128
app/imports/ui/components/snackbars/SnackbarQueue.vue
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
<template lang="html">
|
||||||
|
<v-snackbar
|
||||||
|
bottom
|
||||||
|
left
|
||||||
|
v-bind="$attrs"
|
||||||
|
:value="isShown"
|
||||||
|
:timeout="timeout"
|
||||||
|
@input="value => isShown = value"
|
||||||
|
>
|
||||||
|
<div class="layout align-center">
|
||||||
|
<template v-if="snackbar && snackbar.data">
|
||||||
|
<div v-if="snackbar.data.text">
|
||||||
|
{{ snackbar.data.text }}
|
||||||
|
</div>
|
||||||
|
<template v-else-if="snackbar.data.content">
|
||||||
|
<log-content :model="snackbar.data.content" />
|
||||||
|
</template>
|
||||||
|
<v-spacer />
|
||||||
|
<v-btn
|
||||||
|
v-if="snackbar.data.callback"
|
||||||
|
color="primary"
|
||||||
|
text
|
||||||
|
@click="closeSnackbar(); snackbar.data.callback()"
|
||||||
|
>
|
||||||
|
{{ snackbar.data.callbackName }}
|
||||||
|
</v-btn>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
<template #action="{ attrs }">
|
||||||
|
<v-btn
|
||||||
|
icon
|
||||||
|
v-bind="attrs"
|
||||||
|
@click="closeSnackbar"
|
||||||
|
>
|
||||||
|
<v-icon>close</v-icon>
|
||||||
|
</v-btn>
|
||||||
|
</template>
|
||||||
|
</v-snackbar>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="js">
|
||||||
|
// Modified from https://gitlab.com/tozd/vue/snackbar-queue
|
||||||
|
import { globalState } from '/imports/ui/components/snackbars/SnackbarQueue.js';
|
||||||
|
import LogContent from '/imports/ui/log/LogContent.vue';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
LogContent,
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
timeout: {
|
||||||
|
type: Number,
|
||||||
|
default: 6000,
|
||||||
|
},
|
||||||
|
pause: {
|
||||||
|
type: Number,
|
||||||
|
default: 300,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
isShown: false,
|
||||||
|
snackbar: null,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
isShown(newValue) {
|
||||||
|
if (newValue === false && this.snackbar) {
|
||||||
|
const snackbarIndex = globalState.queue.findIndex((element) => element.id === this.snackbar.id);
|
||||||
|
if (snackbarIndex > -1) {
|
||||||
|
globalState.queue.splice(snackbarIndex, 1);
|
||||||
|
}
|
||||||
|
this.snackbar = null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.handle = null;
|
||||||
|
this.unwait = null;
|
||||||
|
this.showNextSnackbar();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
clearSnackbarState() {
|
||||||
|
if (this.handle) {
|
||||||
|
clearTimeout(this.handle);
|
||||||
|
this.handle = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.unwait) {
|
||||||
|
this.unwait();
|
||||||
|
this.unwait = null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
showNextSnackbar() {
|
||||||
|
this.clearSnackbarState();
|
||||||
|
|
||||||
|
// Wait for the first next snackbar to be available.
|
||||||
|
this.unwait = this.$wait(function () {
|
||||||
|
// Snackbars are enqueued from oldest to newest and "find" searches array elements in
|
||||||
|
// same order as well, so the first one which matches is also the oldest one.
|
||||||
|
return globalState.queue.find((element) => element.shown === false);
|
||||||
|
}, function (snackbar) {
|
||||||
|
this.unwait = null;
|
||||||
|
|
||||||
|
snackbar.shown = true;
|
||||||
|
|
||||||
|
this.snackbar = snackbar;
|
||||||
|
this.isShown = true;
|
||||||
|
|
||||||
|
this.handle = setTimeout(() => {
|
||||||
|
this.handle = null;
|
||||||
|
|
||||||
|
this.showNextSnackbar();
|
||||||
|
}, this.timeout + this.pause);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
closeSnackbar() {
|
||||||
|
this.clearSnackbarState();
|
||||||
|
|
||||||
|
this.isShown = false;
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
this.showNextSnackbar();
|
||||||
|
}, this.pause);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
<template lang="html">
|
|
||||||
<v-snackbar
|
|
||||||
v-if="snackbar"
|
|
||||||
:key="snackbar.text"
|
|
||||||
auto-height
|
|
||||||
bottom
|
|
||||||
:value="true"
|
|
||||||
:timeout="0"
|
|
||||||
>
|
|
||||||
{{ snackbar.text }}
|
|
||||||
<v-btn
|
|
||||||
v-if="snackbar.callback"
|
|
||||||
class="primary--text"
|
|
||||||
icon
|
|
||||||
@click="doCallback"
|
|
||||||
>
|
|
||||||
{{ snackbar.callbackName }}
|
|
||||||
</v-btn>
|
|
||||||
<v-btn
|
|
||||||
v-if="snackbar.showCloseButton"
|
|
||||||
icon
|
|
||||||
@click="$store.dispatch('closeSnackbar')"
|
|
||||||
>
|
|
||||||
<v-icon>close</v-icon>
|
|
||||||
</v-btn>
|
|
||||||
</v-snackbar>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="js">
|
|
||||||
export default {
|
|
||||||
computed: {
|
|
||||||
snackbar(){
|
|
||||||
return this.$store.state.snackbars.snackbars[0];
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
doCallback(){
|
|
||||||
this.snackbar.callback();
|
|
||||||
this.$store.dispatch('closeSnackbar')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="css" scoped>
|
|
||||||
</style>
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
const snackbarStore = {
|
|
||||||
state: {
|
|
||||||
snackbars: [],
|
|
||||||
snackbarTimout: undefined,
|
|
||||||
},
|
|
||||||
mutations: {
|
|
||||||
addSnackbar(state, value){
|
|
||||||
state.snackbars.push(value)
|
|
||||||
},
|
|
||||||
closeCurrentSnackbar (state){
|
|
||||||
state.snackbars.shift();
|
|
||||||
},
|
|
||||||
cancelSnackbarTimeout (state){
|
|
||||||
if(state.snackbarTimout){
|
|
||||||
clearTimeout(state.snackbarTimout);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
setSnackbarTimout(state, value){
|
|
||||||
state.snackbarTimout = value;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
actions: {
|
|
||||||
snackbar({dispatch, commit}, value){
|
|
||||||
// value = {
|
|
||||||
// text,
|
|
||||||
// showCloseButton,
|
|
||||||
// callback,
|
|
||||||
// callbackName
|
|
||||||
// }
|
|
||||||
commit('addSnackbar', value);
|
|
||||||
commit('setSnackbarTimout', setTimeout(() => {
|
|
||||||
dispatch('closeSnackbar');
|
|
||||||
}, 5000));
|
|
||||||
},
|
|
||||||
closeSnackbar({dispatch, commit, state}){
|
|
||||||
commit('closeCurrentSnackbar');
|
|
||||||
commit('cancelSnackbarTimeout');
|
|
||||||
if (state.snackbars.length){
|
|
||||||
commit('setSnackbarTimout', setTimeout(() => {
|
|
||||||
dispatch('closeSnackbar');
|
|
||||||
}, 5000));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export default snackbarStore;
|
|
||||||
@@ -78,6 +78,7 @@
|
|||||||
import TreeTab from '/imports/ui/creature/character/characterSheetTabs/TreeTab.vue';
|
import TreeTab from '/imports/ui/creature/character/characterSheetTabs/TreeTab.vue';
|
||||||
import { assertEditPermission } from '/imports/api/creature/creaturePermissions.js';
|
import { assertEditPermission } from '/imports/api/creature/creaturePermissions.js';
|
||||||
import CreatureLogs from '/imports/api/creature/log/CreatureLogs.js';
|
import CreatureLogs from '/imports/api/creature/log/CreatureLogs.js';
|
||||||
|
import { snackbar } from '/imports/ui/components/snackbars/SnackbarQueue.js';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
@@ -119,13 +120,10 @@
|
|||||||
this.logObserver = CreatureLogs.find({
|
this.logObserver = CreatureLogs.find({
|
||||||
creatureId: this.creatureId,
|
creatureId: this.creatureId,
|
||||||
}).observe({
|
}).observe({
|
||||||
added(doc){
|
added({content}){
|
||||||
if (!that.$subReady.singleCharacter) return;
|
if (!that.$subReady.singleCharacter) return;
|
||||||
if (that.$store.state.rightDrawer) return;
|
if (that.$store.state.rightDrawer) return;
|
||||||
that.$store.dispatch('snackbar', {
|
snackbar({content});
|
||||||
text: doc.name,
|
|
||||||
showCloseButton: true,
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -80,6 +80,7 @@ import getPropertyTitle from '/imports/ui/properties/shared/getPropertyTitle.js'
|
|||||||
import { assertEditPermission } from '/imports/api/creature/creaturePermissions.js';
|
import { assertEditPermission } from '/imports/api/creature/creaturePermissions.js';
|
||||||
import { get, findLast } from 'lodash';
|
import { get, findLast } from 'lodash';
|
||||||
import equipItem from '/imports/api/creature/creatureProperties/methods/equipItem.js';
|
import equipItem from '/imports/api/creature/creatureProperties/methods/equipItem.js';
|
||||||
|
import { snackbar } from '/imports/ui/components/snackbars/SnackbarQueue.js';
|
||||||
|
|
||||||
let formIndex = {};
|
let formIndex = {};
|
||||||
for (let key in propertyFormIndex){
|
for (let key in propertyFormIndex){
|
||||||
@@ -194,7 +195,7 @@ export default {
|
|||||||
} else {
|
} else {
|
||||||
this.$store.dispatch('popDialogStack');
|
this.$store.dispatch('popDialogStack');
|
||||||
}
|
}
|
||||||
this.$store.dispatch('snackbar', {
|
snackbar({
|
||||||
text: `Deleted ${getPropertyTitle(this.model)}`,
|
text: `Deleted ${getPropertyTitle(this.model)}`,
|
||||||
callbackName: 'undo',
|
callbackName: 'undo',
|
||||||
callback(){
|
callback(){
|
||||||
|
|||||||
@@ -57,6 +57,7 @@ import softRemoveProperty from '/imports/api/creature/creatureProperties/methods
|
|||||||
import restoreProperty from '/imports/api/creature/creatureProperties/methods/restoreProperty.js';
|
import restoreProperty from '/imports/api/creature/creatureProperties/methods/restoreProperty.js';
|
||||||
import getPropertyTitle from '/imports/ui/properties/shared/getPropertyTitle.js';
|
import getPropertyTitle from '/imports/ui/properties/shared/getPropertyTitle.js';
|
||||||
import insertPropertyFromLibraryNode from '/imports/api/creature/creatureProperties/methods/insertPropertyFromLibraryNode.js';
|
import insertPropertyFromLibraryNode from '/imports/api/creature/creatureProperties/methods/insertPropertyFromLibraryNode.js';
|
||||||
|
import { snackbar } from '/imports/ui/components/snackbars/SnackbarQueue.js';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
@@ -104,7 +105,7 @@ export default {
|
|||||||
},
|
},
|
||||||
remove(model){
|
remove(model){
|
||||||
softRemoveProperty.call({_id: model._id});
|
softRemoveProperty.call({_id: model._id});
|
||||||
this.$store.dispatch('snackbar', {
|
snackbar({
|
||||||
text: `Deleted ${getPropertyTitle(model)}`,
|
text: `Deleted ${getPropertyTitle(model)}`,
|
||||||
callbackName: 'undo',
|
callbackName: 'undo',
|
||||||
callback(){
|
callback(){
|
||||||
|
|||||||
@@ -65,7 +65,7 @@
|
|||||||
name="rightDrawer"
|
name="rightDrawer"
|
||||||
/>
|
/>
|
||||||
<dialog-stack />
|
<dialog-stack />
|
||||||
<snackbars />
|
<snackbar-queue />
|
||||||
</v-app>
|
</v-app>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -74,13 +74,13 @@
|
|||||||
import Sidebar from '/imports/ui/layouts/Sidebar.vue';
|
import Sidebar from '/imports/ui/layouts/Sidebar.vue';
|
||||||
import DialogStack from '/imports/ui/dialogStack/DialogStack.vue';
|
import DialogStack from '/imports/ui/dialogStack/DialogStack.vue';
|
||||||
import { mapMutations } from 'vuex';
|
import { mapMutations } from 'vuex';
|
||||||
import Snackbars from '/imports/ui/components/snackbars/Snackbars.vue';
|
import SnackbarQueue from '/imports/ui/components/snackbars/SnackbarQueue.vue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
Sidebar,
|
Sidebar,
|
||||||
DialogStack,
|
DialogStack,
|
||||||
Snackbars,
|
SnackbarQueue,
|
||||||
},
|
},
|
||||||
data(){return {
|
data(){return {
|
||||||
name: 'Home',
|
name: 'Home',
|
||||||
|
|||||||
51
app/imports/ui/log/LogContent.vue
Normal file
51
app/imports/ui/log/LogContent.vue
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
<template lang="html">
|
||||||
|
<div class="log-content">
|
||||||
|
<div
|
||||||
|
v-for="(content, index) in model"
|
||||||
|
:key="index"
|
||||||
|
class="content-line"
|
||||||
|
>
|
||||||
|
<h4 class="content-name">
|
||||||
|
{{ content.name }}
|
||||||
|
</h4>
|
||||||
|
<markdown-text
|
||||||
|
v-if="content.value"
|
||||||
|
class="content-value"
|
||||||
|
:markdown="content.value"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="js">
|
||||||
|
import MarkdownText from '/imports/ui/components/MarkdownText.vue';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
MarkdownText,
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
model: {
|
||||||
|
type: Array,
|
||||||
|
default: () => [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="css" scoped>
|
||||||
|
.content-line {
|
||||||
|
min-height: 24px;
|
||||||
|
margin-top: 2px;
|
||||||
|
margin-bottom: 2px;
|
||||||
|
}
|
||||||
|
.content-line .details {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style lang="css">
|
||||||
|
.log-content .content-value > p:last-of-type{
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -6,42 +6,17 @@
|
|||||||
v-if="model.text || (model.content && model.content.length)"
|
v-if="model.text || (model.content && model.content.length)"
|
||||||
class="pa-2"
|
class="pa-2"
|
||||||
>
|
>
|
||||||
<div
|
<log-content :model="model.content" />
|
||||||
v-for="(content, index) in model.content"
|
|
||||||
:key="index"
|
|
||||||
class="content-line"
|
|
||||||
>
|
|
||||||
<h4>
|
|
||||||
{{ content.name }}
|
|
||||||
</h4>
|
|
||||||
<span
|
|
||||||
v-if="content.error"
|
|
||||||
class="error"
|
|
||||||
>{{ content.error }}</span>
|
|
||||||
{{ content.resultPrefix }}
|
|
||||||
<span
|
|
||||||
v-if="content.result"
|
|
||||||
class="subheading font-weight-bold mx-1"
|
|
||||||
>{{ content.result }}</span>
|
|
||||||
<span
|
|
||||||
v-if="content.details"
|
|
||||||
>{{ content.details }}</span>
|
|
||||||
<markdown-text
|
|
||||||
v-if="content.description"
|
|
||||||
class="description"
|
|
||||||
:markdown="content.description"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</v-card-text>
|
</v-card-text>
|
||||||
</v-card>
|
</v-card>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="js">
|
<script lang="js">
|
||||||
import MarkdownText from '/imports/ui/components/MarkdownText.vue';
|
import LogContent from '/imports/ui/log/LogContent.vue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
MarkdownText,
|
LogContent,
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
model: {
|
model: {
|
||||||
@@ -51,20 +26,3 @@ export default {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="css" scoped>
|
|
||||||
.content-line {
|
|
||||||
min-height: 24px;
|
|
||||||
margin-top: 2px;
|
|
||||||
margin-bottom: 2px;
|
|
||||||
}
|
|
||||||
.content-line .details {
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<style lang="css">
|
|
||||||
.log-entry .description > p:last-of-type{
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import store from '/imports/ui/vuexStore.js';
|
|||||||
import VueMeteorTracker from 'vue-meteor-tracker';
|
import VueMeteorTracker from 'vue-meteor-tracker';
|
||||||
import AppLayout from '/imports/ui/layouts/AppLayout.vue';
|
import AppLayout from '/imports/ui/layouts/AppLayout.vue';
|
||||||
import ReactiveProvide from 'vue-reactive-provide';
|
import ReactiveProvide from 'vue-reactive-provide';
|
||||||
|
import VueObserverUtils from '@tozd/vue-observer-utils';
|
||||||
import router from '/imports/ui/router.js';
|
import router from '/imports/ui/router.js';
|
||||||
import themes from '/imports/ui/themes.js';
|
import themes from '/imports/ui/themes.js';
|
||||||
import '/imports/ui/components/global/globalIndex.js';
|
import '/imports/ui/components/global/globalIndex.js';
|
||||||
@@ -35,7 +36,8 @@ Vue.use(Vuetify, {
|
|||||||
});
|
});
|
||||||
Vue.use(ReactiveProvide, {
|
Vue.use(ReactiveProvide, {
|
||||||
name: 'reactiveProvide', // default value
|
name: 'reactiveProvide', // default value
|
||||||
})
|
});
|
||||||
|
Vue.use(VueObserverUtils);
|
||||||
|
|
||||||
// App start
|
// App start
|
||||||
Meteor.startup(() => {
|
Meteor.startup(() => {
|
||||||
|
|||||||
@@ -1,14 +1,12 @@
|
|||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
import Vuex from 'vuex';
|
import Vuex from 'vuex';
|
||||||
import dialogStackStore from '/imports/ui/dialogStack/dialogStackStore.js';
|
import dialogStackStore from '/imports/ui/dialogStack/dialogStackStore.js';
|
||||||
import snackbarStore from '/imports/ui/components/snackbars/snackboxStore.js';
|
|
||||||
|
|
||||||
Vue.use(Vuex);
|
Vue.use(Vuex);
|
||||||
const store = new Vuex.Store({
|
const store = new Vuex.Store({
|
||||||
strict: process.env.NODE_ENV !== 'production',
|
strict: process.env.NODE_ENV !== 'production',
|
||||||
modules: {
|
modules: {
|
||||||
dialogStack: dialogStackStore,
|
dialogStack: dialogStackStore,
|
||||||
snackbars: snackbarStore,
|
|
||||||
},
|
},
|
||||||
state: {
|
state: {
|
||||||
drawer: undefined,
|
drawer: undefined,
|
||||||
|
|||||||
5
app/package-lock.json
generated
5
app/package-lock.json
generated
@@ -79,6 +79,11 @@
|
|||||||
"resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz",
|
||||||
"integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw=="
|
"integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw=="
|
||||||
},
|
},
|
||||||
|
"@tozd/vue-observer-utils": {
|
||||||
|
"version": "0.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@tozd/vue-observer-utils/-/vue-observer-utils-0.5.0.tgz",
|
||||||
|
"integrity": "sha512-HeRxWFJB7FXcQigH2LvauiR0l7hA4qqBC6hK9rBeKf076Ew08C4lx3eo7/YmvADt3b8ZP1j+TN0pGCEhKYOhEA=="
|
||||||
|
},
|
||||||
"@types/color-name": {
|
"@types/color-name": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz",
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "^7.9.2",
|
"@babel/runtime": "^7.9.2",
|
||||||
"@chenfengyuan/vue-countdown": "^1.1.5",
|
"@chenfengyuan/vue-countdown": "^1.1.5",
|
||||||
|
"@tozd/vue-observer-utils": "^0.5.0",
|
||||||
"animejs": "^2.2.0",
|
"animejs": "^2.2.0",
|
||||||
"bcrypt": "^5.0.0",
|
"bcrypt": "^5.0.0",
|
||||||
"core-js": "^2.6.11",
|
"core-js": "^2.6.11",
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import '/imports/server/config/SimpleRestConfig.js';
|
import '/imports/server/config/SimpleRestConfig.js';
|
||||||
|
import '/imports/server/config/SyncedCronConfig.js';
|
||||||
import '/imports/server/publications/index.js';
|
import '/imports/server/publications/index.js';
|
||||||
import '/imports/server/config/simpleSchemaDebug.js';
|
import '/imports/server/config/simpleSchemaDebug.js';
|
||||||
import '/imports/server/cron/deleteSoftRemovedDocuments.js';
|
import '/imports/server/cron/deleteSoftRemovedDocuments.js';
|
||||||
|
|||||||
Reference in New Issue
Block a user