All user data for FoundryVTT. Includes worlds, systems, modules, and any asset in the "foundryuserdata" directory. Does NOT include the FoundryVTT installation itself.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

183 lines
7.9 KiB

// import libWrapperShared from "./libWrapperShared.js";
export default class ChatMerge {
static get _enabled() {
return true;
}
static get _epoch() {
return 10;
}
static get _allowRolls() {
return "rolls";
}
static get _separateWithBorder() {
return false;
}
static get _showHover() {
return false;
}
static get _showHeader() {
return false;
}
static init() {
// libWrapperShared.register("ChatLog.prototype.deleteMessage", this._deleteMessage.bind(this));
Hooks.on("renderChatMessage", this._renderChatMessage);
Hooks.on("deleteChatMessage", this.deleteMessage);
}
static ready() {
const style = document.querySelector(":root").style;
// style.setProperty("--dfce-cm-separation", this._separateWithBorder ? "" : "0");
// this._showHover
// ? style.removeProperty("--dfce-cm-hover-shadow")
// : style.setProperty("--dfce-cm-hover-shadow", "0px");
// style.setProperty("--dfce-cm-header", this._showHeader ? "" : "none");
// if (game.user.isGM) {
// style.setProperty("--dfce-cm-header-delete", this._showHeader ? "" : "0");
// style.setProperty("--dfce-cm-header-delete-pad", this._showHeader ? "" : "16px");
// }
this._processAllMessage(ui.chat.element);
Hooks.on("renderChatLog", (_, html) => this._processAllMessage(html));
}
static deleteMessage(message, flags) {
if (flags.deleteAll === true) return;
const messageId = message._id;
const element = document.querySelector(`li[data-message-id="${messageId}"`);
// If we were a TOP
if (element?.classList?.contains("dfce-cm-top")) {
element.classList.remove("dfce-cm-top");
// If the next element was a middle, make it a top
if (element.nextElementSibling.classList.contains("dfce-cm-middle")) {
element.nextElementSibling.classList.remove("dfce-cm-middle");
element.nextElementSibling.classList.add("dfce-cm-top");
}
// Otherwise, it was a bottom and should now become a normal message again
else element.nextElementSibling.classList.remove("dfce-cm-bottom");
}
// If we were a BOTTOM
else if (element?.classList?.contains("dfce-cm-bottom")) {
element.classList.remove("dfce-cm-bottom");
// If the previous element was a middle, make it a bottom
if (element.previousElementSibling.classList.contains("dfce-cm-middle")) {
element.previousElementSibling.classList.remove("dfce-cm-middle");
element.previousElementSibling.classList.add("dfce-cm-bottom");
}
// Otherwise, it was a top and should now become a normal message again
else element.previousElementSibling.classList.remove("dfce-cm-top");
}
// If we were a MIDDLE, let the above and below snug and they'll be fine
else if (element?.classList?.contains("dfce-cm-middle")) element.classList.remove("dfce-cm-middle");
}
// static _deleteMessage(wrapper, messageId, { deleteAll = false } = {}) {
// // Ignore the Delete All process. Everything is being obliterated, who cares about the styling
// if (!deleteAll && this._enabled) {
// const element = document.querySelector(`li[data-message-id="${messageId}"`);
// // If we were a TOP
// if (element?.classList?.contains("dfce-cm-top")) {
// element.classList.remove("dfce-cm-top");
// // If the next element was a middle, make it a top
// if (element.nextElementSibling.classList.contains("dfce-cm-middle")) {
// element.nextElementSibling.classList.remove("dfce-cm-middle");
// element.nextElementSibling.classList.add("dfce-cm-top");
// }
// // Otherwise, it was a bottom and should now become a normal message again
// else element.nextElementSibling.classList.remove("dfce-cm-bottom");
// }
// // If we were a BOTTOM
// else if (element?.classList?.contains("dfce-cm-bottom")) {
// element.classList.remove("dfce-cm-bottom");
// // If the previous element was a middle, make it a bottom
// if (element.previousElementSibling.classList.contains("dfce-cm-middle")) {
// element.previousElementSibling.classList.remove("dfce-cm-middle");
// element.previousElementSibling.classList.add("dfce-cm-bottom");
// }
// // Otherwise, it was a top and should now become a normal message again
// else element.previousElementSibling.classList.remove("dfce-cm-top");
// }
// // If we were a MIDDLE, let the above and below snug and they'll be fine
// else if (element?.classList?.contains("dfce-cm-middle")) element.classList.remove("dfce-cm-middle");
// }
// return wrapper(messageId, { deleteAll });
// }
static _processAllMessage(element) {
element = element ?? $(document.body);
// Remove the old CSS class designations
element.find(".dfce-cm-top").removeClass("dfce-cm-top");
element.find(".dfce-cm-middle").removeClass("dfce-cm-middle");
element.find(".dfce-cm-bottom").removeClass("dfce-cm-bottom");
// If we are disabled, return
if (!ChatMerge._enabled) return;
// Collect all rendered chat messages
const messages = element.find("li.chat-message");
// Return if there are no messages rendered
if (messages.length === 0) return;
// Process each message after the first
for (let c = 1; c < messages.length; c++) {
// Update styling of the chat messages
this._styleChatMessages(
game.messages.get(messages[c].getAttribute("data-message-id")),
messages[c],
game.messages.get(messages[c - 1].getAttribute("data-message-id")),
messages[c - 1]
);
}
}
static _renderChatMessage(message, html, _cmd) {
if (!ChatMerge._enabled) return;
// Find the most recent message in the chat log
const partnerElem = $(`li.chat-message`).last()[0];
// If there is no message, return
if (partnerElem === null || partnerElem === undefined) return;
// get the ChatMessage document associated with the html
const partner = game.messages.get(partnerElem.getAttribute("data-message-id"));
if (!message || !partner) return;
// Update styling of the chat messages
ChatMerge._styleChatMessages(message, html[0], partner, partnerElem);
}
static _inTimeFrame(current, previous) {
return current > previous && current - previous < this._epoch * 1000;
}
static _isValidMessage(current, previous) {
const rolls = this._allowRolls;
// const splitSpeaker = SETTINGS.get < boolean > this.PREF_SPLIT_SPEAKER;
const splitSpeaker = true;
let userCompare = false;
const currData = current ?? current;
const prevData = previous ?? previous;
if (splitSpeaker) {
// this is a bit complex, basically we want to group by actors, but if you're not using an actor, group by user instead
userCompare =
// If actors are equal and NOT null
(currData.speaker.actor === prevData.speaker.actor && !!currData.speaker.actor) || // If BOTH actors are null and users are equal
(!currData.speaker.actor && !prevData.speaker.actor && currData.user === prevData.user);
} else {
// If we are not splitting by speaker, just do the simple option of comparing the users
userCompare = currData.user === prevData.user;
}
return (
userCompare &&
this._inTimeFrame(currData.timestamp, prevData.timestamp) &&
// Check for merging with roll types
(rolls === "all" ||
(rolls === "rolls" && current.isRoll === previous.isRoll) ||
(rolls === "none" && !current.isRoll && !previous.isRoll))
);
}
static _styleChatMessages(curr, currElem, prev, prevElem) {
if (!ChatMerge._isValidMessage(curr, prev)) return;
if (prevElem.classList.contains("dfce-cm-bottom")) {
prevElem.classList.remove("dfce-cm-bottom");
prevElem.classList.add("dfce-cm-middle");
} else prevElem.classList.add("dfce-cm-top");
currElem.classList.add("dfce-cm-bottom");
}
}