diff --git a/module/Apps/StatSidebar.mjs b/module/Apps/StatSidebar.mjs index e6fbe05..4c8f9aa 100644 --- a/module/Apps/StatSidebar.mjs +++ b/module/Apps/StatSidebar.mjs @@ -35,7 +35,7 @@ export class StatSidebar extends HandlebarsApplicationMixin(AbstractSidebarTab) const ctx = await super._prepareContext(options); const db = CONFIG.stats.db; - ctx.tableCount = db.getTables().length; + ctx.tableCount = (await db.getTables()).length; const controls = { openStats: { label: `View Stats`, action: `openStats` }, diff --git a/module/Apps/StatsViewer.mjs b/module/Apps/StatsViewer.mjs index 5568d7f..2f96c0f 100644 --- a/module/Apps/StatsViewer.mjs +++ b/module/Apps/StatsViewer.mjs @@ -131,7 +131,7 @@ export class StatsViewer extends HandlebarsApplicationMixin(ApplicationV2) { const tables = new Set(); const subtables = {}; - for (const tableConfig of CONFIG.stats.db.getTables()) { + for (const tableConfig of await CONFIG.stats.db.getTables()) { const [ table, subtable ] = tableConfig.name.split(`/`); tables.add(table); if (subtable?.length > 0) { @@ -190,8 +190,8 @@ export class StatsViewer extends HandlebarsApplicationMixin(ApplicationV2) { _graphData = {}; _privacySetting = `my`; async #prepareGraphContext(_ctx) { - const table = CONFIG.stats.db.getTable(this.activeTableID); - const userData = CONFIG.stats.db.getRows( + const table = await CONFIG.stats.db.getTable(this.activeTableID); + const userData = await CONFIG.stats.db.getRows( this.activeTableID, this._selectedUsers, this._privacySetting, diff --git a/module/Apps/TableCreator.mjs b/module/Apps/TableCreator.mjs index 5be09e3..5d9f511 100644 --- a/module/Apps/TableCreator.mjs +++ b/module/Apps/TableCreator.mjs @@ -116,7 +116,7 @@ export class TableCreator extends HandlebarsApplicationMixin(ApplicationV2) { ui.notifications.error(`Cannot create a table without a name`); }; - const existing = CONFIG.stats.db.getTable(name); + const existing = await CONFIG.stats.db.getTable(name); if (existing) { ui.notifications.error(`A table with the name "${name}" already exists`); return; @@ -128,11 +128,11 @@ export class TableCreator extends HandlebarsApplicationMixin(ApplicationV2) { return; }; const size = Number(name.replace(`Dice/d`, ``)); - CONFIG.stats.db.createTable(createDiceTable(size)); + await CONFIG.stats.db.createTable(createDiceTable(size)); return; }; - CONFIG.stats.db.createTable({ + await CONFIG.stats.db.createTable({ name, buckets: { type: this._type, diff --git a/module/Apps/TableManager.mjs b/module/Apps/TableManager.mjs index 9df5b90..75c4d00 100644 --- a/module/Apps/TableManager.mjs +++ b/module/Apps/TableManager.mjs @@ -1,3 +1,4 @@ +import { BucketTypes } from "../utils/buckets.mjs"; import { diceSizeSorter } from "../utils/sorters/diceSize.mjs"; import { filePath } from "../consts.mjs"; import { Logger } from "../utils/Logger.mjs"; @@ -42,23 +43,18 @@ export class TableManager extends HandlebarsApplicationMixin(ApplicationV2) { template: filePath(`templates/Apps/common/tableSelect.hbs`), }, buckets: { - template: filePath(`templates/Apps/TableManager/buckets/empty.hbs`), + template: filePath(`templates/Apps/TableManager/buckets.hbs`), + templates: [ + filePath(`templates/Apps/TableManager/buckets/empty.hbs`), + ...Object.values(BucketTypes).map( + (bucketType) => filePath(`templates/Apps/TableManager/buckets/${bucketType}.hbs`), + ), + ], }, submit: { template: filePath(`templates/Apps/TableManager/submit.hbs`), }, }; - - _configureRenderOptions(options) { - const table = CONFIG.stats.db.getTable(this.activeTableID); - - let bucketType = table?.buckets?.type ?? `empty`; - this.constructor.PARTS.buckets = { - template: filePath(`templates/Apps/TableManager/buckets/${bucketType}.hbs`), - }; - - super._configureRenderOptions(options); - }; // #endregion Options // #region Selected Table @@ -84,7 +80,7 @@ export class TableManager extends HandlebarsApplicationMixin(ApplicationV2) { async render({ userUpdated, ...opts } = {}) { if (userUpdated) { return; - } + }; await super.render(opts); }; @@ -140,7 +136,7 @@ export class TableManager extends HandlebarsApplicationMixin(ApplicationV2) { const tables = new Set(); const subtables = {}; - for (const tableConfig of CONFIG.stats.db.getTables()) { + for (const tableConfig of await CONFIG.stats.db.getTables()) { const [ table, subtable ] = tableConfig.name.split(`/`); tables.add(table); if (subtable?.length > 0) { @@ -167,35 +163,46 @@ export class TableManager extends HandlebarsApplicationMixin(ApplicationV2) { }; async #prepareBucketContext(ctx) { - const table = CONFIG.stats.db.getTable(this.activeTableID); - if (!table) { return }; - const type = table.buckets.type; + const table = await CONFIG.stats.db.getTable(this.activeTableID); + const type = table?.buckets?.type ?? `empty`; + + const template = filePath(`templates/Apps/TableManager/buckets/${type}.hbs`); + ctx.buckets = { + locked: false, + template, + classes: ``, + }; + + if (!table) { + ctx.buckets.classes = `alert-box warning center`; + return; + }; + + const locked = this._selectedTable === `Dice` || table.buckets.locked; + ctx.buckets.locked = locked; + if (locked) { + ctx.buckets.classes = `alert-box locked`; + }; + const capitalizedType = type[0].toUpperCase() + type.slice(1); if (!this[`_prepare${capitalizedType}Context`]) { return }; this[`_prepare${capitalizedType}Context`](ctx, table); }; async _prepareNumberContext(ctx, table) { - ctx.buckets = { - min: table.buckets.min, - max: table.buckets.max, - step: table.buckets.step, - }; + ctx.buckets.min = table.buckets.min; + ctx.buckets.max = table.buckets.max; + ctx.buckets.step = table.buckets.step; }; async _prepareRangeContext(ctx, table) { - ctx.buckets = { - locked: this._selectedTable === `Dice` || table.buckets.locked, - min: table.buckets.min, - max: table.buckets.max, - step: table.buckets.step, - }; + ctx.buckets.min = table.buckets.min; + ctx.buckets.max = table.buckets.max; + ctx.buckets.step = table.buckets.step; }; async _prepareStringContext(ctx, table) { - ctx.buckets = { - choices: [...table.buckets.choices], - }; + ctx.buckets.choices = [...table.buckets.choices]; }; // #endregion Data Prep @@ -230,7 +237,7 @@ export class TableManager extends HandlebarsApplicationMixin(ApplicationV2) { ui.notifications.info(`Nothing to save`); return; } - CONFIG.stats.db.updateTable(this.activeTableID, formData.object); + await CONFIG.stats.db.updateTable(this.activeTableID, formData.object); }; // #endregion Actions }; diff --git a/module/utils/databases/Database.mjs b/module/utils/databases/Database.mjs index 72f1ca1..5f32069 100644 --- a/module/utils/databases/Database.mjs +++ b/module/utils/databases/Database.mjs @@ -1,4 +1,5 @@ /* eslint-disable no-unused-vars */ +import { validateBucketConfig } from "../buckets.mjs"; /* NOTE: @@ -24,9 +25,11 @@ Default Subtables: tables that are parents to other tables. */ +const { deleteProperty, diffObject, expandObject, mergeObject } = foundry.utils; + export class Database { // MARK: Table Ops - static createTable(tableConfig) { + static async createTable(tableConfig) { if (!game.user.isGM) { ui.notifications.error(`You do not have the required permission to create a new table`); return false; @@ -52,23 +55,61 @@ export class Database { tables[name] = tableConfig; game.settings.set(__ID__, `tables`, tables); - this.render(); + this.render({ tags: [`table`] }); return true; }; /** @returns {Array} */ - static getTables() { + static async getTables() { const tables = game.settings.get(__ID__, `tables`); return Object.values(tables) ?? []; }; - static getTable(tableID) { + static async getTable(tableID) { const tables = game.settings.get(__ID__, `tables`); if (!tables[tableID]) { return }; return tables[tableID]; }; - static deleteTable(tableID) { + static async updateTable(tableID, changes) { + const table = this.getTable(tableID); + if (!tables[tableID]) { + ui.notifications.error(`Cannot update table that doesn't exist`); + return false; + }; + + // Bucket coercion in case called via the API + deleteProperty(changes, `name`); + deleteProperty(changes, `buckets.type`); + + const diff = diffObject( + table, + expandObject(changes), + { inner: true, deletionKeys: true }, + ); + if (Object.keys(diff).length === 0) { return false }; + + const updated = mergeObject( + table, + diff, + { inplace: false, performDeletions: true }, + ); + + try { + updated.buckets = validateBucketConfig(updated.buckets); + } catch (e) { + ui.notifications.error(e); + return false; + }; + + const tables = game.settings.get(__ID__, `tables`); + tables[tableID] = updated; + game.settings.set(__ID__, `tables`, tables); + this.render({ tags: [`table`] }); + return true; + }; + + static async deleteTable(tableID) { if (!game.user.isGM) { ui.notifications.error(`You do not have the required permission to delete a table`); return false; @@ -86,23 +127,23 @@ export class Database { }; // MARK: Row Ops - static createRow(table, userID, row, opts) { + static async createRow(table, userID, row, opts) { throw new Error(`createRow() must be implemented`); }; - static createRows(table, userID, rows, opts) { + static async createRows(table, userID, rows, opts) { throw new Error(`createRows() must be implemented`); }; - static getRows(tableID, userIDs, privacy = `none`) { + static async getRows(tableID, userIDs, privacy = `none`) { throw new Error(`getRows() must be implemented`); }; - static updateRow(table, userID, rowID, changes) { + static async updateRow(table, userID, rowID, changes) { throw new Error(`updateRow() must be implemented`); }; - static deleteRow(table, userID, rowID) { + static async deleteRow(table, userID, rowID) { throw new Error(`deleteRow() must be implemented`); }; @@ -137,7 +178,7 @@ export class Database { * Rerenders all of the applications that are displaying data from * this database */ - static render(opts) { + static async render(opts) { for (const app of this._apps.values()) { app.render(opts); }; @@ -148,9 +189,9 @@ export class Database { * Used to listen for changes from other clients and rerender the apps * as required in order to keep the data as up-to-date as possible. */ - static registerListeners() {}; + static async registerListeners() {}; - static unregisterListeners() {}; + static async unregisterListeners() {}; }; /* eslint-enable no-unused-vars */ diff --git a/module/utils/databases/Memory.mjs b/module/utils/databases/Memory.mjs index 7ee598a..2987bab 100644 --- a/module/utils/databases/Memory.mjs +++ b/module/utils/databases/Memory.mjs @@ -1,3 +1,4 @@ +import { createDiceTable } from "./utils.mjs"; import { Database } from "./Database.mjs"; import { filterPrivateRows } from "../privacy.mjs"; import { Logger } from "../Logger.mjs"; @@ -7,48 +8,9 @@ const { deleteProperty, diffObject, expandObject, mergeObject, randomID } = foun export class MemoryDatabase extends Database { static #tables = { - "Dice/d10": { - name: `Dice/d10`, - buckets: { - type: `range`, - locked: true, - min: 1, - max: 10, - step: 1, - }, - graph: { - type: `bar`, - stacked: true, - }, - }, - "Dice/d20": { - name: `Dice/d20`, - buckets: { - type: `range`, - locked: true, - min: 1, - max: 20, - step: 1, - }, - graph: { - type: `bar`, - stacked: true, - }, - }, - "Dice/d100": { - name: `Dice/d100`, - buckets: { - type: `range`, - locked: true, - min: 1, - max: 100, - step: 1, - }, - graph: { - type: `bar`, - stacked: true, - }, - }, + "Dice/d10": createDiceTable(10), + "Dice/d20": createDiceTable(20), + "Dice/d100": createDiceTable(100), "Successes Number": { name: `Successes Number`, buckets: { @@ -88,13 +50,13 @@ export class MemoryDatabase extends Database { static #rows = {}; - static createTable(tableConfig) { + static async createTable(tableConfig) { this.#tables[tableConfig.name] = tableConfig; this.render(); return true; }; - static getTableNames() { + static async getTableNames() { const tables = new Set(); for (const tableID of Object.keys(this.#tables)) { const [ targetTable ] = tableID.split(`/`, 2); @@ -103,7 +65,7 @@ export class MemoryDatabase extends Database { return Array.from(tables); }; - static getSubtableNames(table) { + static async getSubtableNames(table) { const subtables = new Set(); for (const tableID of Object.keys(this.#tables)) { const [ targetTable, targetSubtable ] = tableID.split(`/`, 2); @@ -115,16 +77,15 @@ export class MemoryDatabase extends Database { }; /** @returns {Array
} */ - static getTables() { + static async getTables() { return Object.values(this.#tables); }; - static getTable(tableID) { + static async getTable(tableID) { return this.#tables[tableID]; }; static async updateTable(tableID, changes) { - Logger.debug({tableID, changes}); const table = this.getTable(tableID); if (!table) { return false }; @@ -156,7 +117,7 @@ export class MemoryDatabase extends Database { return true; }; - static createRow(table, userID, row, { rerender = true } = {}) { + static async createRow(table, userID, row, { rerender = true } = {}) { if (!this.#tables[table]) { return }; this.#rows[userID] ??= {}; this.#rows[userID][table] ??= []; @@ -172,7 +133,7 @@ export class MemoryDatabase extends Database { }; }; - static createRows(table, userID, rows, { rerender = true } = {}) { + static async createRows(table, userID, rows, { rerender = true } = {}) { if (!this.#tables[table]) { return }; this.#rows[userID] ??= {}; this.#rows[userID][table] ??= []; @@ -187,7 +148,7 @@ export class MemoryDatabase extends Database { }; }; - static getRows(tableID, userIDs, privacy = `none`) { + static async getRows(tableID, userIDs, privacy = `none`) { if (userIDs.length === 0) { return {}; }; @@ -207,7 +168,7 @@ export class MemoryDatabase extends Database { return datasets; }; - static updateRow(table, userID, rowID, changes) { + static async updateRow(table, userID, rowID, changes) { if (!this.#tables[table] || !this.#rows[userID]?.[table]) { return }; let row = this.#rows[userID][table].find(row => row._id === rowID); if (!row) { return }; @@ -215,7 +176,7 @@ export class MemoryDatabase extends Database { this.render({ userUpdated: userID }); }; - static deleteRow(table, userID, rowID) { + static async deleteRow(table, userID, rowID) { if (!this.#tables[table] || !this.#rows[userID]?.[table]) { return }; let rowIndex = this.#rows[userID][table].findIndex(row => row._id === rowID); if (rowIndex === -1) { return }; @@ -227,7 +188,7 @@ export class MemoryDatabase extends Database { * Used to listen for changes from other clients and refresh the local DB as * required, so that the StatsTracker stays up to date. */ - static registerListeners() {}; + static async registerListeners() {}; - static unregisterListeners() {}; + static async unregisterListeners() {}; }; diff --git a/public/templates/Apps/TableManager/buckets.hbs b/public/templates/Apps/TableManager/buckets.hbs new file mode 100644 index 0000000..38301a0 --- /dev/null +++ b/public/templates/Apps/TableManager/buckets.hbs @@ -0,0 +1,9 @@ +
+ {{#if buckets.locked}} +

+ This bucket configuration has been locked, preventing editing + of the settings. +

+ {{/if}} + {{> (lookup buckets "template") }} +
diff --git a/public/templates/Apps/TableManager/buckets/empty.hbs b/public/templates/Apps/TableManager/buckets/empty.hbs index e4819fd..af96295 100644 --- a/public/templates/Apps/TableManager/buckets/empty.hbs +++ b/public/templates/Apps/TableManager/buckets/empty.hbs @@ -1,7 +1,5 @@ -
- {{#if table}} - Select a Subtable - {{else}} - Select a Table - {{/if}} -
+{{#if table}} + Select a Subtable +{{else}} + Select a Table +{{/if}} diff --git a/public/templates/Apps/TableManager/buckets/number.hbs b/public/templates/Apps/TableManager/buckets/number.hbs index 455595d..91ce2ec 100644 --- a/public/templates/Apps/TableManager/buckets/number.hbs +++ b/public/templates/Apps/TableManager/buckets/number.hbs @@ -1,4 +1,7 @@ -
+
{{#if buckets.locked}}

This bucket configuration has been locked, preventing editing diff --git a/public/templates/Apps/TableManager/buckets/range.hbs b/public/templates/Apps/TableManager/buckets/range.hbs index e54d552..0462a0e 100644 --- a/public/templates/Apps/TableManager/buckets/range.hbs +++ b/public/templates/Apps/TableManager/buckets/range.hbs @@ -1,50 +1,42 @@ -

- {{#if buckets.locked}} -

- This bucket configuration has been locked, preventing editing - of the settings. -

- {{/if}} -
- - -
-
- - -
-
- - -

- The size of the step between values within the range. -

-
+
+ + +
+
+ + +
+
+ + +

+ The size of the step between values within the range. +

diff --git a/public/templates/Apps/TableManager/buckets/string.hbs b/public/templates/Apps/TableManager/buckets/string.hbs index 3f77240..866665e 100644 --- a/public/templates/Apps/TableManager/buckets/string.hbs +++ b/public/templates/Apps/TableManager/buckets/string.hbs @@ -1,4 +1,7 @@ -
+