diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 6f309cf..af6f565 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -109,6 +109,7 @@ jobs: README.md \ templates/ \ scripts/ \ + sounds/ \ styles/ \ languages/ # Don't forget to add a backslash at the end of the line for any diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..aeff6f5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +.idea/ +notes/ +tmp/ +*.code-workspace diff --git a/languages/en.json b/languages/en.json index e69de29..5856e7d 100644 --- a/languages/en.json +++ b/languages/en.json @@ -0,0 +1,6 @@ +{ + "LAMM": { + "HistoryHint": "This is your message history. Sent and received messages will appear here while you're logged in and until you refresh your browser.", + "WhisperHint": "Type your whisper here. Send message by pressing Ctrl-Enter." + } +} diff --git a/module.json b/module.json index 10a85d6..4e06dbd 100644 --- a/module.json +++ b/module.json @@ -21,7 +21,7 @@ "requires": [] }, "esmodules": [ - "scripts/module.js" + "scripts/module.mjs" ], "styles": [ "styles/messenger.css" diff --git a/scripts/config.mjs b/scripts/config.mjs new file mode 100644 index 0000000..4220be8 --- /dev/null +++ b/scripts/config.mjs @@ -0,0 +1,10 @@ +export const MODULE_ID = "lucas-messenger", + TITLE = "Lucas's Almost Magnificent Messenger", // or Lucas's Almost Awesome Messenger; or Lucas's Awesome Messenger Extension + TITLE_ABBREVIATION = "LAMM"; + +const PATH = `modules/${MODULE_ID}`; + +export const TEMPLATE_PATH = `${PATH}/templates`, + TEMPLATE_PARTS = { + history: `${TEMPLATE_PATH}/history.hbs`, + }; diff --git a/scripts/helpers/log.mjs b/scripts/helpers/log.mjs new file mode 100644 index 0000000..6ca260b --- /dev/null +++ b/scripts/helpers/log.mjs @@ -0,0 +1,7 @@ +import { TITLE_ABBREVIATION } from "../config.mjs"; + +const CONSOLE_MESSAGE_PRESET = [`%c${TITLE_ABBREVIATION}%c |`, 'background: #8000ff; color: #fff', 'color: #fff']; // see chat-images\scripts\utils.js + +export function log(...args) { + console.log(...CONSOLE_MESSAGE_PRESET, ...args); +} diff --git a/scripts/module.js b/scripts/module.mjs similarity index 68% rename from scripts/module.js rename to scripts/module.mjs index ef6dd74..ae68cd1 100644 --- a/scripts/module.js +++ b/scripts/module.mjs @@ -1,36 +1,138 @@ -const MODULE_ID = "lucas-messenger", - TITLE = "Lucas's Almost Magnificent Messenger", // or Lucas's Almost Awesome Messenger; or Lucas's Awesome Messenger Extension - TITLE_ABBREVIATION = "LAMM", - PATH = `modules/${MODULE_ID}`, - TEMPLATE_PATH = `${PATH}/templates`, - TEMPLATES = { - history: `${TEMPLATE_PATH}/history.hbs`, - whispers: `${TEMPLATE_PATH}/whispers.html` - }, - CONSOLE_MESSAGE_PRESET = [`%c${TITLE_ABBREVIATION}%c |`, 'background: #8000ff; color: #fff', 'color: #fff']; // see chat-images\scripts\utils.js - - -function log(...args) { - console.log(...CONSOLE_MESSAGE_PRESET, ...args); -} +const { ApplicationV2, HandlebarsApplicationMixin } = foundry.applications.api; + +import { MODULE_ID, TITLE, TEMPLATE_PATH, TEMPLATE_PARTS } from "./config.mjs"; +import { log } from "./helpers/log.mjs"; + +class LAMM extends HandlebarsApplicationMixin(ApplicationV2) { + + // ----- Application v2 BEGIN ----- + /** @inheritDoc */ + static DEFAULT_OPTIONS = { + // https://foundryvtt.com/api/v12/interfaces/foundry.applications.types.ApplicationConfiguration.html + + id: MODULE_ID, + form: { + handler: LAMM.onSubmit, + closeOnSubmit: false + }, + position: { + width: 640, + height: "auto", + }, + tag: "form", // The default is "div" + window: { + icon: "fas fa-comment-dots", // You can now add an icon to the header + title: TITLE // TODO: move this to i18n: "FOO.form.title" + }, + classes: ['messenger'] // options.classes.concat('messenger'), + } +/* + static get defaultOptions() { + return foundry.utils.mergeObject(super.defaultOptions, { + popout: true, + resizable: false, + minimizable: true, + submitOnClose: false, + submitOnUnfocus: false + }); + } +*/ + + /** @override */ + static PARTS = { + // I don't have any idea how to access these later-on + history: { + // template: "./modules/foo/templates/form.hbs" + // id: "history", + template: `${TEMPLATE_PATH}/history.hbs` + // template: TEMPLATE_PARTS.history + }, + form: { // "main window" + template: `${TEMPLATE_PATH}/whispers.html` + } + } + + /** @inheritDoc */ + get title() { + // TODO: add i18n: return `My Module: ${game.i18n.localize(this.options.window.title)}`; + log("this.options", this.options) + return this.options.window.title; + } + + // Provides template with dynamic data: + /** @override */ + async _prepareContext() { + return { + users: this.users, + history: this.beautifyHistory() + }; + } + + _onRender(context, options) { + // super.activateListeners(html); // DEBUG: this might not be used anymore + + const html = $(this.element); + // TODO: try to avoid using jQuery, e.g.: + // this.element.querySelector("input[name=something]").addEventListener("click", /* ... */); + + // Submit/Send button: + html.find('input[type="submit"]').click(event => { + this.sendMessage(html); + }); + + html.find('#message').on("keypress", event => this._onKeyPressEvent(event, html)); + } + + /* + async _updateObject() { // `event` uninteresting, `formData` empty + // log('_updateObject'); + // TODO: check if msg has been sent or if no user was selected. if the latter, this would clear the message unintentionally + this.render(); + } + // replaced by: */ + static async onSubmit(event, form, formData) { + /*const settings = foundry.utils.expandObject(formData.object); + await Promise.all( + Object.entries(settings) + .map(([key, value]) => game.settings.set("foo", key, value)) + );*/ + } + + + + // DEBUG: this might be needed as all template parts are concat'ed, but we only want the main one itself. + /** @override */ + _configureRenderOptions(options) { + // This fills in `options.parts` with an array of ALL part keys by default + // So we need to call `super` first + super._configureRenderOptions(options); + // Completely overriding the parts + options.parts = ['form'] + } + + // ----- Application v2 END ----- + + + + + + -class LAMM extends FormApplication { /* TODO: A subclass of the FormApplication must implement the _updateObject method. */ constructor(app) { super(app); // this.users = this.computeUsersData(); // DEBUG: deactivated for .setup() test this.history = []; + this.pstSound = new Sound("modules/lucas-messenger/sounds/pst-pst.ogg"); + // TODO: deprecated to foundry.audio.Sound + // this.window = new LAMMwindow(); } static async init() { - let templates = []; - for (const key in TEMPLATES) { - templates.push(TEMPLATES[key]); - } - loadTemplates(templates); + loadTemplates([TEMPLATE_PARTS.history]); // TODO: figure out how to do this nicely with Application v2's PARTS log('initialised'); } @@ -48,19 +150,15 @@ class LAMM extends FormApplication { /* TODO: A subclass of the FormApplication log('ready') } - static get defaultOptions() { - const options = super.defaultOptions; - options.title = TITLE; - options.classes = options.classes.concat('messenger'); - options.template = TEMPLATES.whispers; - options.popout = true; - options.resizable = false; - options.minimizable = true; - options.closeOnSubmit = false; - options.submitOnClose = false; - options.submitOnUnfocus = false; - return options; - } + + + + + + + + + beautifyHistory() { // TODO: I think this is called too often and the output should be cached if it isn't already. @@ -83,18 +181,14 @@ class LAMM extends FormApplication { /* TODO: A subclass of the FormApplication // await loadTemplates([TEMPLATES.history]); // let historyHtml = $(await renderTemplate(TEMPLATES.history, data)); + + // log("renderHistoryPartial > window.LAMM", window.LAMM) // does not contain PARTS + // this.PARTS is not defined as `this` does not refer to the class instance const data = { history: this.beautifyHistory() }, - history = await renderTemplate(TEMPLATES.history, data); + history = await renderTemplate(TEMPLATE_PARTS.history, data); $('#history').val(history); } - // Provides template with dynamic data: - getData() { - return { - users: this.users, - history: this.beautifyHistory() - }; - } render(...args) { if (!this.rendered) return super.render(true, ...args); @@ -141,17 +235,6 @@ class LAMM extends FormApplication { /* TODO: A subclass of the FormApplication } } - activateListeners(html) { - super.activateListeners(html); - - // Submit/Send button: - html.find('input[type="submit"]').click(event => { - this.sendMessage(html); - }); - - html.find('#message').on("keypress", event => this._onKeyPressEvent(event, html)); - } - _onKeyPressEvent(event, html) { if ((event.keyCode === 10) && event.ctrlKey) { // for `#on("keyup")` it's 13 when combined with Ctrl this.sendMessage(html); @@ -216,11 +299,6 @@ class LAMM extends FormApplication { /* TODO: A subclass of the FormApplication } } - async _updateObject() { // `event` uninteresting, `formData` empty - // log('_updateObject'); - // TODO: check if msg has been sent or if no user was selected. if the latter, this would clear the message unintentionally - this.render(); - } } /* class LAMMwindow { @@ -238,7 +316,7 @@ class LAMM extends FormApplication { /* TODO: A subclass of the FormApplication Hooks.on('renderSceneControls', (controls, html) => { const messengerBtn = $( `