/*
* This file is part of the warpgate module (https://github.com/trioderegion/warpgate)
* Copyright (c) 2021 Matthew Haentschke.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
import { logger } from './logger.js'
import { Gateway } from './gateway.js'
import { MODULE } from './module.js'
import {queueUpdate} from './update-queue.js'
import { Mutator } from './mutator.js'
export class UserInterface {
static register() {
this.hooks();
this.settings();
}
static hooks() {
Hooks.on("renderActorSheet", UserInterface._renderActorSheet);
}
static settings() {
const config = true;
const settingsData = {
showDismissLabel : {
scope: "client", config, default: true, type: Boolean,
},
showRevertLabel : {
scope: "client", config, default: true, type: Boolean,
},
dismissButtonScope : {
scope: "client", config, default: 'spawned', type: String, choices: {
disabled: MODULE.localize('setting.option.disabled'),
spawned: MODULE.localize('setting.option.spawnedOnly'),
all: MODULE.localize('setting.option.all')
}
},
revertButtonBehavior : {
scope: 'client', config, default: 'pop', type: String, choices: {
disabled: MODULE.localize('setting.option.disabled'),
pop: MODULE.localize('setting.option.popLatestMutation'),
menu: MODULE.localize('setting.option.showMutationList')
}
}
};
MODULE.applySettings(settingsData);
}
static _renderActorSheet(app, html, data) {
logger.debug("app |", app);
logger.debug("html |", html);
logger.debug("data |", data);
UserInterface.addDismissButton(app, html, data);
UserInterface.addRevertMutation(app, html, data);
}
static _shouldAddDismiss(token) {
if ( !(token instanceof TokenDocument) ) return false;
switch (MODULE.setting('dismissButtonScope')){
case 'disabled':
return false;
case 'spawned':
const controlData = token?.actor.getFlag(MODULE.data.name, 'control');
/** do not add the button if we are not the controlling actor AND we aren't the GM */
if ( !(controlData?.user === game.user.id) &&
!game.user.isGM) return false;
return !!controlData;
case 'all':
return true;
}
}
static addDismissButton(app, html/*, data*/) {
const token = app.token;
/** this is not a warpgate spawned actor */
if (!UserInterface._shouldAddDismiss(token)) return;
/* do not add duplicate buttons! */
if(html.closest('.app').find('.dismiss-warpgate').length !== 0) {
logger.debug(MODULE.localize('debug.dismissPresent'));
return;
}
const label = MODULE.setting('showDismissLabel') ? MODULE.localize("display.dismiss") : ""
let dismissButton = $(`${label}`);
dismissButton.click( (/*event*/) => {
if (!token) {
logger.error(MODULE.localize('error.sheetNoToken'));
return;
}
const {id, parent} = token;
Gateway.dismissSpawn(id, parent?.id);
/** close the actor sheet if provided */
app?.close({submit: false});
});
let title = html.closest('.app').find('.window-title');
dismissButton.insertAfter(title);
}
static _shouldAddRevert(token) {
if ( !(token instanceof TokenDocument) ) return false;
const mutateStack = warpgate.mutationStack(token).stack;
/* this is not a warpgate mutated actor,
* or there are no remaining stacks to peel */
if (mutateStack.length == 0) return false;
return MODULE.setting('revertButtonBehavior') !== 'disabled';
}
static _getTokenFromApp(app) {
const {token, actor} = app;
const hasToken = token instanceof TokenDocument;
if( !hasToken ) {
/* check if linked and has an active token on scene */
const candidates = actor?.getActiveTokens() ?? [];
const linkedToken = candidates.find( t => (MODULE.isV10?t.document:t.data).actorLink )?.document ?? null;
return linkedToken;
}
return token;
}
static addRevertMutation(app, html, data) {
/* do not add duplicate buttons! */
let foundButton = html.closest('.app').find('.revert-warpgate')
/* we remove the current button on each render
* in case the render was triggered by a mutation
* event and we need to update the tool tip
* on the revert stack
*/
if (foundButton) {
foundButton.remove();
}
const token = UserInterface._getTokenFromApp(app);
if(!UserInterface._shouldAddRevert(token)) return;
const mutateStack = token?.actor?.getFlag(MODULE.data.name, 'mutate');
/* construct the revert button */
const label = MODULE.setting('showRevertLabel') ? MODULE.localize("display.revert") : ""
const stackCount = mutateStack.length > 1 ? ` 1/${mutateStack.length}` : '';
let revertButton = $(`${label}`);
revertButton.click( async (event) => {
const shouldShow = (shiftKey) => {
const mode = MODULE.setting('revertButtonBehavior')
const show = mode == 'menu' ? !shiftKey : shiftKey;
return show;
}
let name = undefined;
const showMenu = shouldShow(event.shiftKey);
if (showMenu) {
const buttons = mutateStack.map( mutation => {return {label: mutation.name, value: mutation.name}} )
name = await warpgate.buttonDialog({buttons, title: MODULE.localize('display.revertDialogTitle')}, 'column');
if (name === false) return;
}
/* need to queue this since 'click' could
* happen at any time.
* Do not need to remove the button here
* as it will be refreshed on the render call
*/
queueUpdate( async () => {
await Mutator.revertMutation(token, name);
app?.render(false);
});
});
let title = html.closest('.app').find('.window-title');
revertButton.insertAfter(title);
}
}