diff --git a/lang/en.json b/lang/en.json index dae24038..ea2f5e92 100755 --- a/lang/en.json +++ b/lang/en.json @@ -1087,6 +1087,7 @@ "RemoveCountdownText": "Are you sure you want to remove the countdown: {name}?", "OpenOwnership": "Edit Player Ownership", "Title": "{type} Countdowns", + "ToggleSimple": "Toggle Simple View", "Types": { "narrative": "Narrative", "encounter": "Encounter" diff --git a/module/applications/countdowns.mjs b/module/applications/countdowns.mjs index 0eac145f..955a7d8a 100644 --- a/module/applications/countdowns.mjs +++ b/module/applications/countdowns.mjs @@ -1,5 +1,6 @@ import { countdownTypes } from '../config/generalConfig.mjs'; import { GMUpdateEvent, RefreshType, socketEvent } from '../helpers/socket.mjs'; +import constructHTMLButton from '../helpers/utils.mjs'; import OwnershipSelection from './ownershipSelection.mjs'; const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api; @@ -25,14 +26,15 @@ class Countdowns extends HandlebarsApplicationMixin(ApplicationV2) { frame: true, title: 'Countdowns', resizable: true, - minimizable: true + minimizable: false }, actions: { addCountdown: this.addCountdown, removeCountdown: this.removeCountdown, editImage: this.onEditImage, openOwnership: this.openOwnership, - openCountdownOwnership: this.openCountdownOwnership + openCountdownOwnership: this.openCountdownOwnership, + toggleSimpleView: this.toggleSimpleView }, form: { handler: this.updateData, submitOnChange: true } }; @@ -53,11 +55,44 @@ class Countdowns extends HandlebarsApplicationMixin(ApplicationV2) { }); } - async _onFirstRender(context, options) { - super._onFirstRender(context, options); + async _preFirstRender(context, options) { + options.position = + game.user.getFlag(SYSTEM.id, SYSTEM.FLAGS.countdown.position) ?? Countdowns.DEFAULT_OPTIONS.position; - this.element.querySelector('.expanded-view').classList.toggle('hidden'); - this.element.querySelector('.minimized-view').classList.toggle('hidden'); + const viewSetting = game.user.getFlag(SYSTEM.id, SYSTEM.FLAGS.countdown.simple) ?? !game.user.isGM; + this.simpleView = + game.user.isGM || !this.testUserPermission(CONST.DOCUMENT_OWNERSHIP_LEVELS.OBSERVER) ? viewSetting : true; + } + + _onPosition(position) { + game.user.setFlag(SYSTEM.id, SYSTEM.FLAGS.countdown.position, position); + } + + async _renderFrame(options) { + const frame = await super._renderFrame(options); + + if (this.testUserPermission(CONST.DOCUMENT_OWNERSHIP_LEVELS.OBSERVER)) { + const button = constructHTMLButton({ + label: '', + classes: ['header-control', 'icon', 'fa-solid', 'fa-wrench'], + dataset: { action: 'toggleSimpleView', tooltip: 'DAGGERHEART.Countdown.ToggleSimple' } + }); + this.window.controls.after(button); + } + + return frame; + } + + testUserPermission(level, exact, altSettings) { + if (game.user.isGM) return true; + + const settings = + altSettings ?? game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Countdowns)[this.basePath]; + const defaultAllowed = exact ? settings.ownership.default === level : settings.ownership.default >= level; + const userAllowed = exact + ? settings.playerOwnership[game.user.id]?.value === level + : settings.playerOwnership[game.user.id]?.value >= level; + return defaultAllowed || userAllowed; } async _prepareContext(_options) { @@ -67,15 +102,17 @@ class Countdowns extends HandlebarsApplicationMixin(ApplicationV2) { context.isGM = game.user.isGM; context.base = this.basePath; - context.canCreate = countdownData.playerOwnership[game.user.id].value === CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER; + context.canCreate = this.testUserPermission(CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER, true); context.source = { ...countdownData, countdowns: Object.keys(countdownData.countdowns).reduce((acc, key) => { const countdown = countdownData.countdowns[key]; - const ownershipValue = countdown.playerOwnership[game.user.id].value; - if (ownershipValue > CONST.DOCUMENT_OWNERSHIP_LEVELS.NONE) { - acc[key] = { ...countdown, canEdit: ownershipValue === CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER }; + if (this.testUserPermission(CONST.DOCUMENT_OWNERSHIP_LEVELS.LIMITED, false, countdown)) { + acc[key] = { + ...countdown, + canEdit: this.testUserPermission(CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER, true, countdown) + }; } return acc; @@ -83,7 +120,7 @@ class Countdowns extends HandlebarsApplicationMixin(ApplicationV2) { }; context.systemFields = countdownData.schema.fields; context.countdownFields = context.systemFields.countdowns.element.fields; - context.minimized = this.minimized || _options.isFirstRender; + context.simple = this.simpleView; return context; } @@ -110,28 +147,6 @@ class Countdowns extends HandlebarsApplicationMixin(ApplicationV2) { } } - async minimize() { - await super.minimize(); - - this.element.querySelector('.expanded-view').classList.toggle('hidden'); - this.element.querySelector('.minimized-view').classList.toggle('hidden'); - } - - async maximize() { - if (this.minimized) { - const settings = game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Countdowns)[this.basePath]; - if (settings.playerOwnership[game.user.id].value <= CONST.DOCUMENT_OWNERSHIP_LEVELS.LIMITED) { - ui.notifications.info(game.i18n.localize('DAGGERHEART.Countdown.Notifications.LimitedOwnership')); - return; - } - - this.element.querySelector('.expanded-view').classList.toggle('hidden'); - this.element.querySelector('.minimized-view').classList.toggle('hidden'); - } - - await super.maximize(); - } - async updateSetting(update) { if (game.user.isGM) { await game.settings.set(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Countdowns, update); @@ -213,11 +228,17 @@ class Countdowns extends HandlebarsApplicationMixin(ApplicationV2) { }); } + static toggleSimpleView() { + this.simpleView = !this.simpleView; + game.user.setFlag(SYSTEM.id, SYSTEM.FLAGS.countdown.simple, this.simpleView); + this.render(); + } + async updateCountdownValue(event, increase) { const countdownSetting = game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Countdowns); const countdown = countdownSetting[this.basePath].countdowns[event.currentTarget.dataset.countdown]; - if (countdown.playerOwnership[game.user.id] < CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER) { + if (!this.testUserPermission(CONST.DOCUMENT_OWNERSHIP_LEVELS.OWNER)) { return; } diff --git a/module/config/flagsConfig.mjs b/module/config/flagsConfig.mjs index b06a36e1..a737b7e8 100644 --- a/module/config/flagsConfig.mjs +++ b/module/config/flagsConfig.mjs @@ -1 +1,5 @@ export const displayDomainCardsAsList = 'displayDomainCardsAsList'; +export const countdown = { + simple: 'countdown-simple', + position: 'countdown-position' +}; diff --git a/module/data/countdowns.mjs b/module/data/countdowns.mjs index e71cda55..a61a1aab 100644 --- a/module/data/countdowns.mjs +++ b/module/data/countdowns.mjs @@ -36,7 +36,8 @@ class DhCountdownData extends foundry.abstract.DataModel { }) }) ) - }) + }), + window: new fields.SchemaField({}) }; } diff --git a/module/helpers/utils.mjs b/module/helpers/utils.mjs index d812f043..bc551711 100644 --- a/module/helpers/utils.mjs +++ b/module/helpers/utils.mjs @@ -225,10 +225,10 @@ export const getDeleteKeys = (property, innerProperty, innerPropertyDefaultValue // Fix on Foundry native formula replacement for DH const nativeReplaceFormulaData = Roll.replaceFormulaData; -Roll.replaceFormulaData = function (formula, data={}, { missing, warn = false } = {}) { +Roll.replaceFormulaData = function (formula, data = {}, { missing, warn = false } = {}) { const terms = Object.keys(SYSTEM.GENERAL.multiplierTypes).map(type => { - return { term: type, default: 1} - }) + return { term: type, default: 1 }; + }); formula = terms.reduce((a, c) => a.replaceAll(`@${c.term}`, data[c.term] ?? c.default), formula); return nativeReplaceFormulaData(formula, data, { missing, warn }); }; @@ -258,3 +258,25 @@ export const damageKeyToNumber = key => { return 0; } }; + +export default function constructHTMLButton({ + label, + dataset = {}, + classes = [], + icon = '', + type = 'button', + disabled = false +}) { + const button = document.createElement('button'); + button.type = type; + + for (const [key, value] of Object.entries(dataset)) { + button.dataset[key] = value; + } + button.classList.add(...classes); + if (icon) icon = ` `; + if (disabled) button.disabled = true; + button.innerHTML = `${icon}${label}`; + + return button; +} diff --git a/styles/countdown.less b/styles/countdown.less index dd80acc4..336805a9 100644 --- a/styles/countdown.less +++ b/styles/countdown.less @@ -1,6 +1,6 @@ .theme-light { .daggerheart.dh-style.countdown { - &.minimized .minimized-view .mini-countdown-container { + .minimized-view .mini-countdown-container { background-image: url('../assets/parchments/dh-parchment-dark.png'); } } @@ -26,51 +26,38 @@ } } - &.minimized { - height: auto !important; - max-height: unset !important; - max-width: 740px !important; - width: auto !important; + .minimized-view { + display: flex; + gap: 8px; + flex-wrap: wrap; - .window-content { - display: flex; - padding: 4px 8px; - justify-content: center; - } - - .minimized-view { + .mini-countdown-container { + width: fit-content; display: flex; + align-items: center; gap: 8px; - flex-wrap: wrap; + border: 2px solid light-dark(@dark-blue, @golden); + border-radius: 6px; + padding: 0 4px 0 0; + background-image: url('../assets/parchments/dh-parchment-light.png'); + color: light-dark(@beige, @dark); + cursor: pointer; - .mini-countdown-container { - width: fit-content; - display: flex; - align-items: center; - gap: 8px; - border: 2px solid light-dark(@dark-blue, @golden); - border-radius: 6px; - padding: 0 4px 0 0; - background-image: url('../assets/parchments/dh-parchment-light.png'); - color: light-dark(@beige, @dark); - cursor: pointer; + &.disabled { + cursor: initial; + } - &.disabled { - cursor: initial; - } + img { + width: 30px; + height: 30px; + border-radius: 6px 0 0 6px; + } - img { - width: 30px; - height: 30px; - border-radius: 6px 0 0 6px; - } + .mini-countdown-name { + white-space: nowrap; + } - .mini-countdown-name { - white-space: nowrap; - } - - .mini-countdown-value { - } + .mini-countdown-value { } } } diff --git a/styles/daggerheart.css b/styles/daggerheart.css index d620cb5b..58b354e6 100755 --- a/styles/daggerheart.css +++ b/styles/daggerheart.css @@ -3386,7 +3386,7 @@ div.daggerheart.views.multiclass { #resources:has(.fear-bar) { min-width: 200px; } -.theme-light .daggerheart.dh-style.countdown.minimized .minimized-view .mini-countdown-container { +.theme-light .daggerheart.dh-style.countdown .minimized-view .mini-countdown-container { background-image: url('../assets/parchments/dh-parchment-dark.png'); } .daggerheart.dh-style.countdown { @@ -3406,23 +3406,12 @@ div.daggerheart.views.multiclass { .daggerheart.dh-style.countdown fieldset legend a { text-shadow: none; } -.daggerheart.dh-style.countdown.minimized { - height: auto !important; - max-height: unset !important; - max-width: 740px !important; - width: auto !important; -} -.daggerheart.dh-style.countdown.minimized .window-content { - display: flex; - padding: 4px 8px; - justify-content: center; -} -.daggerheart.dh-style.countdown.minimized .minimized-view { +.daggerheart.dh-style.countdown .minimized-view { display: flex; gap: 8px; flex-wrap: wrap; } -.daggerheart.dh-style.countdown.minimized .minimized-view .mini-countdown-container { +.daggerheart.dh-style.countdown .minimized-view .mini-countdown-container { width: fit-content; display: flex; align-items: center; @@ -3434,15 +3423,15 @@ div.daggerheart.views.multiclass { color: light-dark(#efe6d8, #222); cursor: pointer; } -.daggerheart.dh-style.countdown.minimized .minimized-view .mini-countdown-container.disabled { +.daggerheart.dh-style.countdown .minimized-view .mini-countdown-container.disabled { cursor: initial; } -.daggerheart.dh-style.countdown.minimized .minimized-view .mini-countdown-container img { +.daggerheart.dh-style.countdown .minimized-view .mini-countdown-container img { width: 30px; height: 30px; border-radius: 6px 0 0 6px; } -.daggerheart.dh-style.countdown.minimized .minimized-view .mini-countdown-container .mini-countdown-name { +.daggerheart.dh-style.countdown .minimized-view .mini-countdown-container .mini-countdown-name { white-space: nowrap; } .daggerheart.dh-style.countdown .hidden { diff --git a/templates/views/countdowns.hbs b/templates/views/countdowns.hbs index 293b84b9..f06aa4db 100644 --- a/templates/views/countdowns.hbs +++ b/templates/views/countdowns.hbs @@ -1,42 +1,45 @@