Compare commits

..

No commits in common. "main" and "v0.2.0" have entirely different histories.
main ... v0.2.0

6 changed files with 50 additions and 121 deletions

View file

@ -1,4 +1,4 @@
# The Mill's Messages - v0.3.0
# The Mill's Messages - v0.2.0
This little [FoundryVTT](https://foundryvtt.com/) module gives the GMs a way to send messages to PCs with a bit more flair than
the chat, and more spontaneity than journals !
@ -31,8 +31,6 @@ 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

@ -2,7 +2,7 @@
"id": "the-mills-messages",
"title": "The Mill's Messages",
"description": "A little message-sending modules for GMs",
"version": "0.3.0",
"version": "0.2.0",
"compatibility": {
"minimum": "12",
"verified": "12",
@ -49,5 +49,5 @@
"readme": "README.md",
"url": "https://git.tfk-astrodome.net/trotFunky/TheMillsMessages/src/branch/release",
"manifest": "https://git.tfk-astrodome.net/trotFunky/TheMillsMessages/raw/branch/release/module.json",
"download": "https://git.tfk-astrodome.net/trotFunky/TheMillsMessages/archive/v0.3.0.zip"
"download": "https://git.tfk-astrodome.net/trotFunky/TheMillsMessages/archive/v0.2.0.zip"
}

View file

@ -1,13 +1,58 @@
import { module_id, history_flag, str_format, create_history_journal } from "./utils.mjs"
export const module_id = "the-mills-messages";
import { module_settings, register_settings } from "./settings.mjs";
import { prepare_send_message_html, validate_form } from "./send_dialog_form.mjs";
// User flag to point to the ID of the journal to store message history to
const history_flag = "history-journal";
/** @type {SocketlibSocket} */
let socket;
const SocketMessages = Object.freeze({
DisplayMessage: "display-message",
})
/**
* This is a copy of Foundry's internal formatting used by
* localize.format(), so that we can be consistent.
* @param {String} str The string whose "{named_args}" will be formatted
* @param {Object} data An object with named_args members, whose data is replaced
* @returns {String} The formatted string
*/
function str_format(str, data) {
const fmt = /{[^}]+}/g;
str = str.replace(fmt, k => {
return data[k.slice(1, -1)];
});
return str;
}
/**
* Create the journal entry for the history of the specified user,
* update the user's history flag and return the entry for further
* manipulation if needed.
* @param {User} user The user object that we are creating the history for
* @return {Promise<JournalEntry>} The JournalEntry that was created.
*/
async function create_history_journal(user) {
// Use the settings' title for the journal, or revert to the default one if it is empty.
let format_name = {name: user.character ? user.character.name : user.name};
let formated_journal_title = str_format(game.settings.get(module_id, module_settings.HistoryJournalTitle), format_name)
let title = formated_journal_title.length > 0 ?
formated_journal_title :
game.i18n.format("MM.UI.HistoryJournalTitle", format_name)
return JournalEntry.create({
name: title,
ownership: {
default: CONST.DOCUMENT_OWNERSHIP_LEVELS.NONE,
[user.id]: CONST.DOCUMENT_OWNERSHIP_LEVELS.OBSERVER // Make the history private and unmodifiable
},
}).then(journal => {
return user.setFlag(module_id, history_flag, journal.id).then(() => journal);
});
}
/**
* Handle the dialog to send messages to players from NPCs.
* The basic dialog is simple : display a few fields selecting which players

View file

@ -3,10 +3,6 @@
* 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
@ -20,23 +16,6 @@ const sender_hints_id = "sender_hints"
* @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.
@ -109,22 +88,6 @@ 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

@ -1,4 +1,4 @@
import { module_id } from "./utils.mjs";
import {module_id} from "./mills_messages.mjs";
export const module_settings = Object.freeze({
MessageDialogTitle: "messageDialogTitle",

View file

@ -1,77 +0,0 @@
/*
* This file contains some module-wide constants and utility functions
* that do not really fit elsewhere.
*/
import { module_settings } from "./settings.mjs";
export const module_id = "the-mills-messages";
// User flag to point to the ID of the journal to store message history to
export const history_flag = "history-journal";
/**
* This is a copy of Foundry's internal formatting used by
* localize.format(), so that we can be consistent.
* @param {String} str The string whose "{named_args}" will be formatted
* @param {Object} data An object with named_args members, whose data is replaced
* @returns {String} The formatted string
*/
export function str_format(str, data) {
const fmt = /{[^}]+}/g;
str = str.replace(fmt, k => {
return data[k.slice(1, -1)];
});
return str;
}
/**
* Create the journal entry for the history of the specified user,
* update the user's history flag and return the entry for further
* manipulation if needed.
* @param {User} user The user object that we are creating the history for
* @return {Promise<JournalEntry>} The JournalEntry that was created.
*/
export async function create_history_journal(user) {
// Use the settings' title for the journal, or revert to the default one if it is empty.
let format_name = {name: user.character ? user.character.name : user.name};
let formated_journal_title = str_format(game.settings.get(module_id, module_settings.HistoryJournalTitle), format_name)
let title = formated_journal_title.length > 0 ?
formated_journal_title :
game.i18n.format("MM.UI.HistoryJournalTitle", format_name)
return JournalEntry.create({
name: title,
ownership: {
default: CONST.DOCUMENT_OWNERSHIP_LEVELS.NONE,
[user.id]: CONST.DOCUMENT_OWNERSHIP_LEVELS.OBSERVER // Make the history private and unmodifiable
},
}).then(journal => {
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();
}