The dialog form has a simple text input for the sender's name. This can lead to typos or mistakes if the GM wants to send a message using the same sender name as a previous message. Add a new function that goes through all the players' histories and compiles a list of all unique senders, which we can then use to build a list of hints that will be used to show suggestions when the GM fills the sender name in the form. This is currently quite ineficcient as it re-does all the work for every single dialog. A better way would be to cache it or store it explicitly somewhere when we send a message, but this would require a way for the GM to edit it.
147 lines
5.6 KiB
JavaScript
147 lines
5.6 KiB
JavaScript
/*
|
|
* This files separates the form-specific validation, creation and types,
|
|
* so they can be used in the main module.
|
|
*/
|
|
|
|
import { get_existing_senders } from "./utils.mjs";
|
|
|
|
const sender_hints_id = "sender_hints"
|
|
|
|
/**
|
|
* @typedef {Object} MessageFormResponse
|
|
* @property {string[]} recipients The list of player IDs the message will be sent to
|
|
* @property {string} sender The name to display as the origin of the message, and in the history
|
|
* @property {string} message The body of the message, a string of HTML
|
|
*/
|
|
|
|
/**
|
|
* @typedef {Object} DialogFormResponse
|
|
* @property {boolean} valid Is the response valid ?
|
|
* @property {MessageFormResponse} form_response The response form the dialog form
|
|
*/
|
|
|
|
/**
|
|
* Generate the HTML for a <datalist> whose options are the provided senders.
|
|
* @param {String} hints_id The ID to use for the <datalist> providing the hints
|
|
* @param {String[]} senders The list of existing senders, or an empty string if invalid.
|
|
*/
|
|
function generate_sender_hints(hints_id, senders) {
|
|
// Both arguments need to be usable to generate something useful.
|
|
if (!hints_id || !senders || senders.length === 0) {
|
|
return "";
|
|
}
|
|
let sender_hints_html = `<datalist id="${hints_id}">`
|
|
for (let sender of senders) {
|
|
sender_hints_html += `<option value="${sender}">`;
|
|
}
|
|
return sender_hints_html + "</datalist>";
|
|
}
|
|
|
|
/**
|
|
* Given that "required" does not seem to have much impact, check that everything
|
|
* has been properly filled.
|
|
* To be able to reopen the dialog with the existing inputs, return the result of the check
|
|
* as well as the input data.
|
|
* @param {MessageFormResponse} form_response The response from the form, pre-processed
|
|
* @return {DialogFormResponse} Pass the response through and a validity flag
|
|
*/
|
|
export function validate_form(form_response) {
|
|
let valid = true;
|
|
if (form_response.message.length === 0 ||
|
|
form_response.recipients.length === 0 ||
|
|
form_response.sender.length === 0) {
|
|
ui.notifications.warn("MM.Errors.MissingFields", {localize:true});
|
|
valid = false;
|
|
}
|
|
return {valid, form_response};
|
|
}
|
|
|
|
/**
|
|
* Prepare the HTML used for the sending message dialog, filling previous values and handling if it is a chained message.
|
|
* @param {MessageFormResponse|null} [existing_values=null] Values from a previously filled form
|
|
* @param {boolean} [chain=false] If this is a chained message or not
|
|
* @returns {string} HTML for the send message Dialog content
|
|
*/
|
|
export function prepare_send_message_html(existing_values = null, chain = false) {
|
|
let players_options = []
|
|
for (let user of game.users.players) {
|
|
players_options.push({
|
|
label: user.character ? user.character.name : user.name,
|
|
value: user.id,
|
|
})
|
|
}
|
|
if (existing_values && existing_values.recipients.length > 0) {
|
|
players_options
|
|
.filter((option) => existing_values.recipients.includes(option.value))
|
|
.forEach((option) => {option.selected = true})
|
|
}
|
|
|
|
const recipient_input = foundry.applications.fields.createMultiSelectInput({
|
|
name: "recipients_IDs",
|
|
required: true,
|
|
blank: false,
|
|
options: players_options,
|
|
/*
|
|
* Send chained messages to the same recipients, disallow changing it in this case.
|
|
* This is an arbitrary decision that simplifies the process of sending the messages.
|
|
*/
|
|
disabled: chain,
|
|
})
|
|
|
|
const recipient_group = foundry.applications.fields.createFormGroup({
|
|
input: recipient_input,
|
|
label: "MM.Dialogs.SendDialog.RecipientsLabel",
|
|
hint: chain ? "MM.Dialogs.SendDialog.RecipientsChainHint" : "MM.Dialogs.SendDialog.RecipientsHint",
|
|
localize: true,
|
|
})
|
|
|
|
const sender_input = foundry.applications.fields.createTextInput({
|
|
name: "sender_name",
|
|
required: true,
|
|
placeholder: game.i18n.localize("MM.Dialogs.SendDialog.SenderPlaceholder"),
|
|
value: existing_values ? existing_values.sender : null,
|
|
})
|
|
|
|
const sender_group = foundry.applications.fields.createFormGroup({
|
|
input: sender_input,
|
|
label: "MM.Dialogs.SendDialog.SenderLabel",
|
|
hint: "MM.Dialogs.SendDialog.SenderHint",
|
|
localize: true
|
|
})
|
|
|
|
/*
|
|
* If we can find previous senders in player's histories, create a datalist
|
|
* with them so that the names can appear as autocompletion when filling the form.
|
|
*/
|
|
/*
|
|
* TODO: This is very silly : it re-computes everything for every form that is opened.
|
|
* Find a way to cache it or store the senders list it explicitly rather than deduce it
|
|
* from the history.
|
|
*/
|
|
let existing_senders = get_existing_senders();
|
|
if (existing_senders.length > 0) {
|
|
sender_input.setAttribute("list", sender_hints_id)
|
|
// This keeps the datalist withing the input group as proper HTML.
|
|
sender_group.insertAdjacentHTML("beforeend", generate_sender_hints(sender_hints_id, existing_senders))
|
|
}
|
|
|
|
const message_input = foundry.applications.elements.HTMLProseMirrorElement.create({
|
|
name: "message",
|
|
required: true,
|
|
value: existing_values && existing_values.message ? existing_values.message : "",
|
|
editable: true,
|
|
compact: true,
|
|
collaborate: false,
|
|
toggled: false,
|
|
height: 350,
|
|
})
|
|
|
|
const message_group = foundry.applications.fields.createFormGroup({
|
|
input: message_input,
|
|
label: "MM.Dialogs.SendDialog.MessageEditorLabel",
|
|
stacked: true,
|
|
localize: true,
|
|
})
|
|
|
|
return recipient_group.outerHTML + sender_group.outerHTML + message_group.outerHTML;
|
|
}
|