Update the privacy handling for the rows to be more dynamic and support more than just private or public rolls
This commit is contained in:
parent
f66510c811
commit
6dbc0a817f
9 changed files with 103 additions and 57 deletions
|
|
@ -2,6 +2,7 @@ import { Chart } from "chart.js";
|
||||||
import { diceSizeSorter } from "../utils/sorters/diceSize.mjs";
|
import { diceSizeSorter } from "../utils/sorters/diceSize.mjs";
|
||||||
import { filePath } from "../consts.mjs";
|
import { filePath } from "../consts.mjs";
|
||||||
import { Logger } from "../utils/Logger.mjs";
|
import { Logger } from "../utils/Logger.mjs";
|
||||||
|
import { PrivacyMode } from "../utils/privacy.mjs";
|
||||||
import { smallToLarge } from "../utils/sorters/smallToLarge.mjs";
|
import { smallToLarge } from "../utils/sorters/smallToLarge.mjs";
|
||||||
|
|
||||||
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
|
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
|
||||||
|
|
@ -48,12 +49,30 @@ export class StatsViewer extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||||
};
|
};
|
||||||
// #endregion
|
// #endregion
|
||||||
|
|
||||||
constructor({ users, ...opts } = {}) {
|
// #region Instance Data
|
||||||
|
constructor({ users, privacy, ...opts } = {}) {
|
||||||
super(opts);
|
super(opts);
|
||||||
|
|
||||||
if (users != null) {
|
if (users != null) {
|
||||||
this._selectedUsers = users;
|
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() {
|
get activeTableID() {
|
||||||
|
|
@ -62,7 +81,9 @@ export class StatsViewer extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||||
}
|
}
|
||||||
return this._selectedTable;
|
return this._selectedTable;
|
||||||
};
|
};
|
||||||
|
// #endregion Instance Data
|
||||||
|
|
||||||
|
// #region Lifecycle
|
||||||
async render({ userUpdated, ...opts } = {}) {
|
async render({ userUpdated, ...opts } = {}) {
|
||||||
if (userUpdated && !this._selectedUsers.includes(userUpdated)) {
|
if (userUpdated && !this._selectedUsers.includes(userUpdated)) {
|
||||||
return;
|
return;
|
||||||
|
|
@ -84,14 +105,19 @@ export class StatsViewer extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||||
input.addEventListener(`change`, this.#bindListener.bind(this));
|
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`);
|
const canvas = this.element.querySelector(`canvas`);
|
||||||
new Chart( canvas, this._graphData );
|
new Chart( canvas, this._graphData );
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
// #endregion Lifecycle
|
||||||
|
|
||||||
|
// #region Data Prep
|
||||||
async _preparePartContext(partId) {
|
async _preparePartContext(partId) {
|
||||||
const ctx = {};
|
const ctx = {
|
||||||
|
table: this._selectedTable,
|
||||||
|
subtable: this._selectedSubtable,
|
||||||
|
};
|
||||||
ctx.meta = {
|
ctx.meta = {
|
||||||
idp: this.id,
|
idp: this.id,
|
||||||
};
|
};
|
||||||
|
|
@ -117,16 +143,6 @@ export class StatsViewer extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||||
return ctx;
|
return ctx;
|
||||||
};
|
};
|
||||||
|
|
||||||
#_selectedTable = ``;
|
|
||||||
_selectedSubtable = ``;
|
|
||||||
get _selectedTable() {
|
|
||||||
return this.#_selectedTable;
|
|
||||||
};
|
|
||||||
set _selectedTable(val) {
|
|
||||||
this.#_selectedTable = val;
|
|
||||||
this._selectedSubtable = ``;
|
|
||||||
};
|
|
||||||
|
|
||||||
async #prepareTableSelectContext(ctx) {
|
async #prepareTableSelectContext(ctx) {
|
||||||
const tables = new Set();
|
const tables = new Set();
|
||||||
const subtables = {};
|
const subtables = {};
|
||||||
|
|
@ -141,8 +157,6 @@ export class StatsViewer extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||||
};
|
};
|
||||||
|
|
||||||
const tableList = Array.from(tables);
|
const tableList = Array.from(tables);
|
||||||
this._selectedTable ??= tableList[0];
|
|
||||||
|
|
||||||
ctx.table = this._selectedTable;
|
ctx.table = this._selectedTable;
|
||||||
ctx.tables = tableList;
|
ctx.tables = tableList;
|
||||||
|
|
||||||
|
|
@ -153,18 +167,12 @@ export class StatsViewer extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||||
subtableList?.sort(diceSizeSorter);
|
subtableList?.sort(diceSizeSorter);
|
||||||
} else {
|
} else {
|
||||||
subtableList?.sort(smallToLarge);
|
subtableList?.sort(smallToLarge);
|
||||||
}
|
|
||||||
|
|
||||||
if (!subtableList) {
|
|
||||||
this._selectedSubtable = undefined;
|
|
||||||
} else if (!subtableList.includes(this._selectedSubtable)) {
|
|
||||||
this._selectedSubtable = subtableList?.[0];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
ctx.subtable = this._selectedSubtable;
|
ctx.subtable = this._selectedSubtable;
|
||||||
ctx.subtables = subtableList;
|
ctx.subtables = subtableList;
|
||||||
};
|
};
|
||||||
|
|
||||||
_selectedUsers = [game.user.id];
|
|
||||||
async #prepareDataFiltersContext(ctx) {
|
async #prepareDataFiltersContext(ctx) {
|
||||||
ctx.users = [];
|
ctx.users = [];
|
||||||
ctx.selectedUsers = this._selectedUsers;
|
ctx.selectedUsers = this._selectedUsers;
|
||||||
|
|
@ -177,26 +185,36 @@ export class StatsViewer extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||||
|
|
||||||
ctx.privacySetting = this._privacySetting;
|
ctx.privacySetting = this._privacySetting;
|
||||||
ctx.privacyOptions = [
|
ctx.privacyOptions = [
|
||||||
{ label: `Only Public Data`, value: `none` },
|
{ label: `Self`, value: PrivacyMode.SELF },
|
||||||
{ label: `Only Your Private Data`, value: `my` },
|
{ label: `Private`, value: PrivacyMode.PRIVATE },
|
||||||
|
{ label: `Public`, value: PrivacyMode.PUBLIC },
|
||||||
];
|
];
|
||||||
if (game.user.isGM) {
|
if (game.user.isGM) {
|
||||||
ctx.privacyOptions.push(
|
ctx.privacyOptions.push(
|
||||||
{ label: `All Private Data`, value: `all` },
|
{ label: `Blind`, value: PrivacyMode.GM },
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
_graphData = {};
|
async #prepareGraphContext(ctx) {
|
||||||
_privacySetting = `my`;
|
|
||||||
async #prepareGraphContext(_ctx) {
|
|
||||||
const table = await CONFIG.stats.db.getTable(this.activeTableID);
|
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(
|
const userData = await CONFIG.stats.db.getRows(
|
||||||
this.activeTableID,
|
this.activeTableID,
|
||||||
this._selectedUsers,
|
this._selectedUsers,
|
||||||
this._privacySetting,
|
this._privacySetting,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
Logger.log(userData);
|
||||||
|
|
||||||
const data = {};
|
const data = {};
|
||||||
const allBuckets = new Set();
|
const allBuckets = new Set();
|
||||||
|
|
||||||
|
|
@ -265,7 +283,9 @@ export class StatsViewer extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||||
};
|
};
|
||||||
console.log(`graphData`, this._graphData);
|
console.log(`graphData`, this._graphData);
|
||||||
};
|
};
|
||||||
|
// #endregion Data Prep
|
||||||
|
|
||||||
|
// #region Actions
|
||||||
/**
|
/**
|
||||||
* @param {Event} event
|
* @param {Event} event
|
||||||
*/
|
*/
|
||||||
|
|
@ -283,4 +303,5 @@ export class StatsViewer extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||||
|
|
||||||
/** @this {StatsViewer} */
|
/** @this {StatsViewer} */
|
||||||
static async #addAllUsers() {};
|
static async #addAllUsers() {};
|
||||||
|
// #endregion Actions
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,8 @@ import { TableManager } from "./Apps/TableManager.mjs";
|
||||||
import { TestApp } from "./Apps/TestApp.mjs";
|
import { TestApp } from "./Apps/TestApp.mjs";
|
||||||
|
|
||||||
// Utils
|
// Utils
|
||||||
|
import { filterPrivateRows, PrivacyMode } from "./utils/privacy.mjs";
|
||||||
import { validateBucketConfig, validateValue } from "./utils/buckets.mjs";
|
import { validateBucketConfig, validateValue } from "./utils/buckets.mjs";
|
||||||
import { filterPrivateRows } from "./utils/privacy.mjs";
|
|
||||||
|
|
||||||
const { deepFreeze } = foundry.utils;
|
const { deepFreeze } = foundry.utils;
|
||||||
|
|
||||||
|
|
@ -26,6 +26,9 @@ Object.defineProperty(
|
||||||
validateValue,
|
validateValue,
|
||||||
validateBucketConfig,
|
validateBucketConfig,
|
||||||
},
|
},
|
||||||
|
enums: {
|
||||||
|
PrivacyMode,
|
||||||
|
},
|
||||||
}),
|
}),
|
||||||
writable: false,
|
writable: false,
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -9,13 +9,13 @@ Hooks.on(`preCreateChatMessage`, (_message, context, options, author) => {
|
||||||
/** An object of dice denomination to rows to add */
|
/** An object of dice denomination to rows to add */
|
||||||
const rows = {};
|
const rows = {};
|
||||||
|
|
||||||
const mode = determinePrivacyFromRollMode(options.rollMode);
|
const privacy = determinePrivacyFromRollMode(options.rollMode);
|
||||||
for (const roll of context.rolls) {
|
for (const roll of context.rolls) {
|
||||||
for (const die of roll.dice) {
|
for (const die of roll.dice) {
|
||||||
const size = die.denomination;
|
const size = die.denomination;
|
||||||
rows[size] ??= [];
|
rows[size] ??= [];
|
||||||
for (const result of die.results) {
|
for (const result of die.results) {
|
||||||
rows[size].push({ mode, value: result.result });
|
rows[size].push({ privacy, value: result.result });
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,4 @@
|
||||||
/* eslint-disable no-unused-vars */
|
/* eslint-disable no-unused-vars */
|
||||||
import { Table } from "./model.mjs";
|
|
||||||
|
|
||||||
const tablesFlag = `tables`;
|
const tablesFlag = `tables`;
|
||||||
|
|
||||||
export class UserFlagDatabase {
|
export class UserFlagDatabase {
|
||||||
|
|
@ -22,8 +20,6 @@ export class UserFlagDatabase {
|
||||||
datasets[user.id] = null;
|
datasets[user.id] = null;
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
|
||||||
const table = new Table(tables[tableId]);
|
|
||||||
}
|
}
|
||||||
return datasets;
|
return datasets;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,11 @@
|
||||||
export const PrivacyMode = Object.freeze({
|
export const PrivacyMode = Object.freeze({
|
||||||
|
/** Only the GM is able to the see the result of the row */
|
||||||
GM: `gm`,
|
GM: `gm`,
|
||||||
|
/** Both the GM and the logged in user are able to see the row */
|
||||||
PRIVATE: `private`,
|
PRIVATE: `private`,
|
||||||
|
/** Only the logged in user is able to see the row */
|
||||||
SELF: `self`,
|
SELF: `self`,
|
||||||
|
/** Everyone is able to see the row */
|
||||||
PUBLIC: `public`,
|
PUBLIC: `public`,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -23,22 +27,37 @@ export function determinePrivacyFromRollMode(rollMode) {
|
||||||
*
|
*
|
||||||
* @param {Array<any>} rows The rows to filter
|
* @param {Array<any>} rows The rows to filter
|
||||||
* @param {string} userID The user's ID who the rows belong to
|
* @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
|
* @returns The filtered rows
|
||||||
*/
|
*/
|
||||||
export function filterPrivateRows(rows, userID, privacy) {
|
export function filterPrivateRows(rows, userID, privacies) {
|
||||||
console.log(rows, userID, privacy);
|
console.log({rows, userID, privacies});
|
||||||
const filtered = [];
|
const filtered = [];
|
||||||
|
|
||||||
const isMe = userID === game.user.id;
|
const isMe = userID === game.user.id;
|
||||||
// TODO: make this use a permission rather than just isGM
|
const isGM = game.user.isGM;
|
||||||
const canSeeAll = game.user.isGM;
|
|
||||||
|
|
||||||
for (const row of rows) {
|
for (const row of rows) {
|
||||||
|
let allowed = privacies.includes(row.privacy);
|
||||||
|
|
||||||
let allowed = !row.isPrivate;
|
/*
|
||||||
allowed ||= privacy === `all` && canSeeAll;
|
Assert that the user is actually allowed to see the privacy level, even if
|
||||||
allowed ||= privacy === `my` && isMe;
|
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) {
|
if (allowed) {
|
||||||
filtered.push(row);
|
filtered.push(row);
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,8 @@
|
||||||
"common": {
|
"common": {
|
||||||
"Table": "Table",
|
"Table": "Table",
|
||||||
"Subtable": "Subtable",
|
"Subtable": "Subtable",
|
||||||
"Users": "Users"
|
"Users": "Users",
|
||||||
|
"DataVisibility": "Data Visibility"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,7 @@
|
||||||
stats-tracker-multi-select {
|
stats-tracker-multi-select {
|
||||||
|
/*
|
||||||
|
TODO: Improve styling when this isn't the tallest element in a row
|
||||||
|
*/
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: auto minmax(0, 1fr);
|
grid-template-columns: auto minmax(0, 1fr);
|
||||||
gap: 2px;
|
gap: 2px;
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,10 @@
|
||||||
<div>
|
<div>
|
||||||
<div class="control-group">
|
<stats-tracker-multi-select
|
||||||
<label for="{{meta.idp}}-private-data">
|
|
||||||
Private Data Visibility
|
|
||||||
</label>
|
|
||||||
<select
|
|
||||||
id="{{meta.idp}}-private-data"
|
|
||||||
data-bind="_privacySetting"
|
data-bind="_privacySetting"
|
||||||
|
placeholder="STAT_TRACKER.common.DataVisibility"
|
||||||
>
|
>
|
||||||
{{ st-options privacySetting privacyOptions }}
|
{{ st-options privacySetting privacyOptions }}
|
||||||
</select>
|
</stats-tracker-multi-select>
|
||||||
</div>
|
|
||||||
<stats-tracker-multi-select
|
<stats-tracker-multi-select
|
||||||
data-bind="_selectedUsers"
|
data-bind="_selectedUsers"
|
||||||
placeholder="STAT_TRACKER.common.Users"
|
placeholder="STAT_TRACKER.common.Users"
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,11 @@
|
||||||
<div>
|
<div class="{{classes}} center">
|
||||||
|
{{#if showGraph}}
|
||||||
<canvas></canvas>
|
<canvas></canvas>
|
||||||
|
{{else}}
|
||||||
|
{{#if table}}
|
||||||
|
<span class="large">Select a Subtable</span>
|
||||||
|
{{else}}
|
||||||
|
<span class="large">Select a Table</span>
|
||||||
|
{{/if}}
|
||||||
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue