diff --git a/module/settings/meta.mjs b/module/settings/meta.mjs
index 78f6ad1..83ea057 100644
--- a/module/settings/meta.mjs
+++ b/module/settings/meta.mjs
@@ -1,9 +1,34 @@
+function createDiceTable(size) {
+ return {
+ name: `Dice/d${size}`,
+ buckets: {
+ type: `range`,
+ min: 1,
+ max: size,
+ step: 1,
+ },
+ graph: {
+ type: `bar`,
+ stacked: true,
+ },
+ };
+};
+
export function registerMetaSettings() {
game.settings.register(__ID__, `tables`, {
scope: `world`,
- type: Array,
+ type: Object,
config: false,
requiresReload: false,
+ default: {
+ "Dice/d4": createDiceTable(4),
+ "Dice/d6": createDiceTable(6),
+ "Dice/d8": createDiceTable(8),
+ "Dice/d10": createDiceTable(10),
+ "Dice/d12": createDiceTable(12),
+ "Dice/d20": createDiceTable(20),
+ "Dice/d100": createDiceTable(100),
+ },
});
game.settings.register(__ID__, `data`, {
diff --git a/module/utils/databases/Database.mjs b/module/utils/databases/Database.mjs
new file mode 100644
index 0000000..e9400d7
--- /dev/null
+++ b/module/utils/databases/Database.mjs
@@ -0,0 +1,89 @@
+/* eslint-disable no-unused-vars */
+
+export class Database {
+ // MARK: Table Ops
+ static createTable(tableConfig) {
+ throw new Error(`createTable() must be defined`);
+ };
+
+ /** @returns {Array
} */
+ static getTables() {
+ const tables = game.settings.get(__ID__, `tables`);
+ return Object.values(tables) ?? [];
+ };
+
+ static getTable(tableID) {
+ const tables = game.settings.get(__ID__, `tables`);
+ if (!tables[tableID]) { return };
+ return tables[tableID];
+ };
+
+ static deleteTable(tableID) {
+ throw new Error(`deleteTable() must be defined`);
+ };
+
+ // MARK: Row Ops
+ static createRow(table, userID, row) {
+ throw new Error(`createRow() must be implemented`);
+ };
+
+ static getRows(tableID, userIDs, privacy = `none`) {
+ throw new Error(`getRows() must be implemented`);
+ };
+
+ static updateRow(table, userID, rowID, changes) {
+ throw new Error(`updateRow() must be implemented`);
+ };
+
+ static deleteRow(table, userID, rowID) {
+ throw new Error(`deleteRow() must be implemented`);
+ };
+
+ // MARK: Applications
+ static _apps = new Map();
+
+ /**
+ * Adds an application into the registry so that when a data update
+ * is received, we can re-render the sheets.
+ *
+ * @param app an ApplicationV2 instance
+ */
+ static addApp(app) {
+ this._apps.set(app.id, app);
+ this.registerListeners();
+ };
+
+ /**
+ * Adds an application into the registry so that when a data update
+ * is received, we can re-render the sheets.
+ *
+ * @param app an ApplicationV2 instance
+ */
+ static removeApp(app) {
+ this._apps.delete(app.id);
+ if (this._apps.size === 0) {
+ this.unregisterListeners();
+ };
+ };
+
+ /**
+ * Rerenders all of the applications that are displaying data from
+ * this database
+ */
+ static render() {
+ for (const app of Object.values(this.apps)) {
+ app.render();
+ };
+ };
+
+ // MARK: Listeners
+ /**
+ * 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 unregisterListeners() {};
+};
+
+/* eslint-enable no-unused-vars */
diff --git a/module/utils/databases/Memory.mjs b/module/utils/databases/Memory.mjs
index 70af985..f3698ae 100644
--- a/module/utils/databases/Memory.mjs
+++ b/module/utils/databases/Memory.mjs
@@ -1,9 +1,9 @@
-/* eslint-disable no-unused-vars */
+import { Database } from "./Database.mjs";
import { filterPrivateRows } from "../filterPrivateRows.mjs";
const { randomID, mergeObject } = foundry.utils;
-export class MemoryDatabase {
+export class MemoryDatabase extends Database {
static #tables = {
"Dice/d10": {
name: `Dice/d10`,
@@ -14,7 +14,8 @@ export class MemoryDatabase {
max: 10,
step: 1,
},
- config: {
+ graph: {
+ type: `bar`,
stacked: true,
},
},
@@ -74,8 +75,8 @@ export class MemoryDatabase {
static #rows = {};
+ /** @returns {Array} */
static getTables() {
- /** @type {Array<{ name: string; }>} */
return Object.values(this.#tables);
};
@@ -146,5 +147,3 @@ export class MemoryDatabase {
static unregisterListeners() {};
};
-
-/* eslint-enable no-unused-vars */
diff --git a/module/utils/validateValue.mjs b/module/utils/validateValue.mjs
index d141083..4440d96 100644
--- a/module/utils/validateValue.mjs
+++ b/module/utils/validateValue.mjs
@@ -3,7 +3,13 @@ import { Logger } from "./Logger.mjs";
const { deepClone } = foundry.utils;
const { StringField, NumberField } = foundry.data.fields;
+/**
+ * @param {unknown} value The value to validate
+ * @param {BucketConfig} options The bucket config for the table
+ * @returns Whether or not the value is valid for the table
+ */
export function validateValue(value, options) {
+ /** @type {BucketConfig} */
let opts = deepClone(options);
if (validatorTypes[opts.type] == null) {
Logger.error(`Failed to find type validator for: ${opts.type}`);