Dialog: Provide hints for the sender name

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.
This commit is contained in:
trotFunky 2025-06-11 18:48:44 +01:00
parent 30c0324799
commit d2b14dbe94
3 changed files with 64 additions and 0 deletions

View file

@ -31,6 +31,8 @@ each page containing the messages from one specific recipient.
Both the journals and pages will be created automatically.
Players only have observer permissions on their own journal, which will take the name of their
character, or their own if they don't control one yet.
If the history is enabled, the message form will go look for previous senders' names and provide
a list for autocompletion.
The GM can send a message to an offline player, which will add it to its history if it is enabled,
but won't show up as a pop-up when they next log in.

View file

@ -3,6 +3,10 @@
* 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
@ -16,6 +20,23 @@
* @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.
@ -88,6 +109,22 @@ export function prepare_send_message_html(existing_values = null, chain = false)
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,

View file

@ -50,3 +50,28 @@ export async function create_history_journal(user) {
return user.setFlag(module_id, history_flag, journal.id).then(() => journal);
});
}
/**
* Go through all the user's histories to build a list of existing senders,
* so that they can be displayed or re-used.
* The final list is sorted and does not contain duplicates, as one sender
* can send messages to multiple players at once.
* @returns {String[]} A sorted array of the sender names, if any
*/
export function get_existing_senders() {
/** @type {String[]} */
let senders = []
for (let user of game.users.players) {
/** @type {JournalEntry} */
let history = game.journal.get(user.getFlag(module_id, history_flag))
if (!history) {
continue;
}
for (let page of history.pages) {
if (!senders.includes(page.name)) {
senders.push(page.name);
}
}
}
return senders.sort();
}