diff --git a/module/Apps/StatsViewer.mjs b/module/Apps/StatsViewer.mjs index 2f96c0f..d65da35 100644 --- a/module/Apps/StatsViewer.mjs +++ b/module/Apps/StatsViewer.mjs @@ -2,6 +2,7 @@ import { Chart } from "chart.js"; import { diceSizeSorter } from "../utils/sorters/diceSize.mjs"; import { filePath } from "../consts.mjs"; import { Logger } from "../utils/Logger.mjs"; +import { PrivacyMode } from "../utils/privacy.mjs"; import { smallToLarge } from "../utils/sorters/smallToLarge.mjs"; const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api; @@ -48,12 +49,30 @@ export class StatsViewer extends HandlebarsApplicationMixin(ApplicationV2) { }; // #endregion - constructor({ users, ...opts } = {}) { + // #region Instance Data + constructor({ users, privacy, ...opts } = {}) { super(opts); if (users != null) { this._selectedUsers = users; }; + if (privacy != null && Array.isArray(privacy)) { + this._privacySetting = privacy; + }; + }; + + _selectedUsers = [game.user.id]; + _graphData = null; + _privacySetting = [PrivacyMode.PUBLIC, PrivacyMode.PRIVATE]; + + #_selectedTable = ``; + _selectedSubtable = ``; + get _selectedTable() { + return this.#_selectedTable; + }; + set _selectedTable(val) { + this.#_selectedTable = val; + this._selectedSubtable = ``; }; get activeTableID() { @@ -62,7 +81,9 @@ export class StatsViewer extends HandlebarsApplicationMixin(ApplicationV2) { } return this._selectedTable; }; + // #endregion Instance Data + // #region Lifecycle async render({ userUpdated, ...opts } = {}) { if (userUpdated && !this._selectedUsers.includes(userUpdated)) { return; @@ -84,14 +105,19 @@ export class StatsViewer extends HandlebarsApplicationMixin(ApplicationV2) { input.addEventListener(`change`, this.#bindListener.bind(this)); }; - if (options.parts.includes(`graph`)) { + if (options.parts.includes(`graph`) && this._graphData) { const canvas = this.element.querySelector(`canvas`); new Chart( canvas, this._graphData ); }; }; + // #endregion Lifecycle + // #region Data Prep async _preparePartContext(partId) { - const ctx = {}; + const ctx = { + table: this._selectedTable, + subtable: this._selectedSubtable, + }; ctx.meta = { idp: this.id, }; @@ -117,16 +143,6 @@ export class StatsViewer extends HandlebarsApplicationMixin(ApplicationV2) { return ctx; }; - #_selectedTable = ``; - _selectedSubtable = ``; - get _selectedTable() { - return this.#_selectedTable; - }; - set _selectedTable(val) { - this.#_selectedTable = val; - this._selectedSubtable = ``; - }; - async #prepareTableSelectContext(ctx) { const tables = new Set(); const subtables = {}; @@ -141,8 +157,6 @@ export class StatsViewer extends HandlebarsApplicationMixin(ApplicationV2) { }; const tableList = Array.from(tables); - this._selectedTable ??= tableList[0]; - ctx.table = this._selectedTable; ctx.tables = tableList; @@ -153,18 +167,12 @@ export class StatsViewer extends HandlebarsApplicationMixin(ApplicationV2) { subtableList?.sort(diceSizeSorter); } else { subtableList?.sort(smallToLarge); - } - - if (!subtableList) { - this._selectedSubtable = undefined; - } else if (!subtableList.includes(this._selectedSubtable)) { - this._selectedSubtable = subtableList?.[0]; }; + ctx.subtable = this._selectedSubtable; ctx.subtables = subtableList; }; - _selectedUsers = [game.user.id]; async #prepareDataFiltersContext(ctx) { ctx.users = []; ctx.selectedUsers = this._selectedUsers; @@ -177,26 +185,36 @@ export class StatsViewer extends HandlebarsApplicationMixin(ApplicationV2) { ctx.privacySetting = this._privacySetting; ctx.privacyOptions = [ - { label: `Only Public Data`, value: `none` }, - { label: `Only Your Private Data`, value: `my` }, + { label: `Self`, value: PrivacyMode.SELF }, + { label: `Private`, value: PrivacyMode.PRIVATE }, + { label: `Public`, value: PrivacyMode.PUBLIC }, ]; if (game.user.isGM) { ctx.privacyOptions.push( - { label: `All Private Data`, value: `all` }, + { label: `Blind`, value: PrivacyMode.GM }, ); }; }; - _graphData = {}; - _privacySetting = `my`; - async #prepareGraphContext(_ctx) { + async #prepareGraphContext(ctx) { const table = await CONFIG.stats.db.getTable(this.activeTableID); + if (!table) { + this._graphData = null; + ctx.showGraph = false; + ctx.classes = `alert-box warning`; + return; + }; + ctx.classes = ``; + ctx.showGraph = true; + const userData = await CONFIG.stats.db.getRows( this.activeTableID, this._selectedUsers, this._privacySetting, ); + Logger.log(userData); + const data = {}; const allBuckets = new Set(); @@ -265,7 +283,9 @@ export class StatsViewer extends HandlebarsApplicationMixin(ApplicationV2) { }; console.log(`graphData`, this._graphData); }; + // #endregion Data Prep + // #region Actions /** * @param {Event} event */ @@ -283,4 +303,5 @@ export class StatsViewer extends HandlebarsApplicationMixin(ApplicationV2) { /** @this {StatsViewer} */ static async #addAllUsers() {}; + // #endregion Actions }; diff --git a/module/api.mjs b/module/api.mjs index a54c23c..ff0f5b2 100644 --- a/module/api.mjs +++ b/module/api.mjs @@ -5,8 +5,8 @@ import { TableManager } from "./Apps/TableManager.mjs"; import { TestApp } from "./Apps/TestApp.mjs"; // Utils +import { filterPrivateRows, PrivacyMode } from "./utils/privacy.mjs"; import { validateBucketConfig, validateValue } from "./utils/buckets.mjs"; -import { filterPrivateRows } from "./utils/privacy.mjs"; const { deepFreeze } = foundry.utils; @@ -26,6 +26,9 @@ Object.defineProperty( validateValue, validateBucketConfig, }, + enums: { + PrivacyMode, + }, }), writable: false, }, diff --git a/module/hooks/preCreateChatMessage.mjs b/module/hooks/preCreateChatMessage.mjs index 44cb4ac..05a4d41 100644 --- a/module/hooks/preCreateChatMessage.mjs +++ b/module/hooks/preCreateChatMessage.mjs @@ -9,13 +9,13 @@ Hooks.on(`preCreateChatMessage`, (_message, context, options, author) => { /** An object of dice denomination to rows to add */ const rows = {}; - const mode = determinePrivacyFromRollMode(options.rollMode); + const privacy = determinePrivacyFromRollMode(options.rollMode); for (const roll of context.rolls) { for (const die of roll.dice) { const size = die.denomination; rows[size] ??= []; for (const result of die.results) { - rows[size].push({ mode, value: result.result }); + rows[size].push({ privacy, value: result.result }); }; }; }; diff --git a/module/utils/databases/UserFlag.mjs b/module/utils/databases/UserFlag.mjs index c448762..430dbf5 100644 --- a/module/utils/databases/UserFlag.mjs +++ b/module/utils/databases/UserFlag.mjs @@ -1,6 +1,4 @@ /* eslint-disable no-unused-vars */ -import { Table } from "./model.mjs"; - const tablesFlag = `tables`; export class UserFlagDatabase { @@ -22,8 +20,6 @@ export class UserFlagDatabase { datasets[user.id] = null; continue; }; - - const table = new Table(tables[tableId]); } return datasets; }; diff --git a/module/utils/privacy.mjs b/module/utils/privacy.mjs index 12aefd1..20ed3ff 100644 --- a/module/utils/privacy.mjs +++ b/module/utils/privacy.mjs @@ -1,7 +1,11 @@ export const PrivacyMode = Object.freeze({ + /** Only the GM is able to the see the result of the row */ GM: `gm`, + /** Both the GM and the logged in user are able to see the row */ PRIVATE: `private`, + /** Only the logged in user is able to see the row */ SELF: `self`, + /** Everyone is able to see the row */ PUBLIC: `public`, }); @@ -23,22 +27,37 @@ export function determinePrivacyFromRollMode(rollMode) { * * @param {Array} rows The rows to filter * @param {string} userID The user's ID who the rows belong to - * @param {"all"|"me"|"none"} privacy The privacy level we're filtering for + * @param {(PrivacyMode[keyof PrivacyMode])[]} privacies The privacy level we're filtering for * @returns The filtered rows */ -export function filterPrivateRows(rows, userID, privacy) { - console.log(rows, userID, privacy); +export function filterPrivateRows(rows, userID, privacies) { + console.log({rows, userID, privacies}); const filtered = []; const isMe = userID === game.user.id; - // TODO: make this use a permission rather than just isGM - const canSeeAll = game.user.isGM; + const isGM = game.user.isGM; for (const row of rows) { + let allowed = privacies.includes(row.privacy); - let allowed = !row.isPrivate; - allowed ||= privacy === `all` && canSeeAll; - allowed ||= privacy === `my` && isMe; + /* + Assert that the user is actually allowed to see the privacy level, even if + they provide through the param that they want that privacy level. + */ + switch (row.privacy) { + case PrivacyMode.SELF: { + allowed &&= isMe; + break; + }; + case PrivacyMode.GM: { + allowed &&= isGM; + break; + }; + case PrivacyMode.PRIVATE: { + allowed &&= (isMe || isGM); + break; + }; + }; if (allowed) { filtered.push(row); diff --git a/public/langs/en-ca.json b/public/langs/en-ca.json index d68d01e..4cf777c 100644 --- a/public/langs/en-ca.json +++ b/public/langs/en-ca.json @@ -3,7 +3,8 @@ "common": { "Table": "Table", "Subtable": "Subtable", - "Users": "Users" + "Users": "Users", + "DataVisibility": "Data Visibility" } } } diff --git a/public/styles/elements/custom-multi-select.css b/public/styles/elements/custom-multi-select.css index c5b2e0e..c9f2e0e 100644 --- a/public/styles/elements/custom-multi-select.css +++ b/public/styles/elements/custom-multi-select.css @@ -1,4 +1,7 @@ stats-tracker-multi-select { + /* + TODO: Improve styling when this isn't the tallest element in a row + */ display: grid; grid-template-columns: auto minmax(0, 1fr); gap: 2px; diff --git a/public/templates/Apps/StatsViewer/dataFilters.hbs b/public/templates/Apps/StatsViewer/dataFilters.hbs index bbcc52f..8e2f5f0 100644 --- a/public/templates/Apps/StatsViewer/dataFilters.hbs +++ b/public/templates/Apps/StatsViewer/dataFilters.hbs @@ -1,15 +1,10 @@
-
- - -
+ + {{ st-options privacySetting privacyOptions }} + - +
+ {{#if showGraph}} + + {{else}} + {{#if table}} + Select a Subtable + {{else}} + Select a Table + {{/if}} + {{/if}}