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

  1. // import libWrapperShared from "./libWrapperShared.js";
  2. export default class ChatMerge {
  3. static get _enabled() {
  4. return true;
  5. }
  6. static get _epoch() {
  7. return 10;
  8. }
  9. static get _allowRolls() {
  10. return "rolls";
  11. }
  12. static get _separateWithBorder() {
  13. return false;
  14. }
  15. static get _showHover() {
  16. return false;
  17. }
  18. static get _showHeader() {
  19. return false;
  20. }
  21. static init() {
  22. // libWrapperShared.register("ChatLog.prototype.deleteMessage", this._deleteMessage.bind(this));
  23. Hooks.on("renderChatMessage", this._renderChatMessage);
  24. Hooks.on("deleteChatMessage", this.deleteMessage);
  25. }
  26. static ready() {
  27. const style = document.querySelector(":root").style;
  28. // style.setProperty("--dfce-cm-separation", this._separateWithBorder ? "" : "0");
  29. // this._showHover
  30. // ? style.removeProperty("--dfce-cm-hover-shadow")
  31. // : style.setProperty("--dfce-cm-hover-shadow", "0px");
  32. // style.setProperty("--dfce-cm-header", this._showHeader ? "" : "none");
  33. // if (game.user.isGM) {
  34. // style.setProperty("--dfce-cm-header-delete", this._showHeader ? "" : "0");
  35. // style.setProperty("--dfce-cm-header-delete-pad", this._showHeader ? "" : "16px");
  36. // }
  37. this._processAllMessage(ui.chat.element);
  38. Hooks.on("renderChatLog", (_, html) => this._processAllMessage(html));
  39. }
  40. static deleteMessage(message, flags) {
  41. if (flags.deleteAll === true) return;
  42. const messageId = message._id;
  43. const element = document.querySelector(`li[data-message-id="${messageId}"`);
  44. // If we were a TOP
  45. if (element?.classList?.contains("dfce-cm-top")) {
  46. element.classList.remove("dfce-cm-top");
  47. // If the next element was a middle, make it a top
  48. if (element.nextElementSibling.classList.contains("dfce-cm-middle")) {
  49. element.nextElementSibling.classList.remove("dfce-cm-middle");
  50. element.nextElementSibling.classList.add("dfce-cm-top");
  51. }
  52. // Otherwise, it was a bottom and should now become a normal message again
  53. else element.nextElementSibling.classList.remove("dfce-cm-bottom");
  54. }
  55. // If we were a BOTTOM
  56. else if (element?.classList?.contains("dfce-cm-bottom")) {
  57. element.classList.remove("dfce-cm-bottom");
  58. // If the previous element was a middle, make it a bottom
  59. if (element.previousElementSibling.classList.contains("dfce-cm-middle")) {
  60. element.previousElementSibling.classList.remove("dfce-cm-middle");
  61. element.previousElementSibling.classList.add("dfce-cm-bottom");
  62. }
  63. // Otherwise, it was a top and should now become a normal message again
  64. else element.previousElementSibling.classList.remove("dfce-cm-top");
  65. }
  66. // If we were a MIDDLE, let the above and below snug and they'll be fine
  67. else if (element?.classList?.contains("dfce-cm-middle")) element.classList.remove("dfce-cm-middle");
  68. }
  69. // static _deleteMessage(wrapper, messageId, { deleteAll = false } = {}) {
  70. // // Ignore the Delete All process. Everything is being obliterated, who cares about the styling
  71. // if (!deleteAll && this._enabled) {
  72. // const element = document.querySelector(`li[data-message-id="${messageId}"`);
  73. // // If we were a TOP
  74. // if (element?.classList?.contains("dfce-cm-top")) {
  75. // element.classList.remove("dfce-cm-top");
  76. // // If the next element was a middle, make it a top
  77. // if (element.nextElementSibling.classList.contains("dfce-cm-middle")) {
  78. // element.nextElementSibling.classList.remove("dfce-cm-middle");
  79. // element.nextElementSibling.classList.add("dfce-cm-top");
  80. // }
  81. // // Otherwise, it was a bottom and should now become a normal message again
  82. // else element.nextElementSibling.classList.remove("dfce-cm-bottom");
  83. // }
  84. // // If we were a BOTTOM
  85. // else if (element?.classList?.contains("dfce-cm-bottom")) {
  86. // element.classList.remove("dfce-cm-bottom");
  87. // // If the previous element was a middle, make it a bottom
  88. // if (element.previousElementSibling.classList.contains("dfce-cm-middle")) {
  89. // element.previousElementSibling.classList.remove("dfce-cm-middle");
  90. // element.previousElementSibling.classList.add("dfce-cm-bottom");
  91. // }
  92. // // Otherwise, it was a top and should now become a normal message again
  93. // else element.previousElementSibling.classList.remove("dfce-cm-top");
  94. // }
  95. // // If we were a MIDDLE, let the above and below snug and they'll be fine
  96. // else if (element?.classList?.contains("dfce-cm-middle")) element.classList.remove("dfce-cm-middle");
  97. // }
  98. // return wrapper(messageId, { deleteAll });
  99. // }
  100. static _processAllMessage(element) {
  101. element = element ?? $(document.body);
  102. // Remove the old CSS class designations
  103. element.find(".dfce-cm-top").removeClass("dfce-cm-top");
  104. element.find(".dfce-cm-middle").removeClass("dfce-cm-middle");
  105. element.find(".dfce-cm-bottom").removeClass("dfce-cm-bottom");
  106. // If we are disabled, return
  107. if (!ChatMerge._enabled) return;
  108. // Collect all rendered chat messages
  109. const messages = element.find("li.chat-message");
  110. // Return if there are no messages rendered
  111. if (messages.length === 0) return;
  112. // Process each message after the first
  113. for (let c = 1; c < messages.length; c++) {
  114. // Update styling of the chat messages
  115. this._styleChatMessages(
  116. game.messages.get(messages[c].getAttribute("data-message-id")),
  117. messages[c],
  118. game.messages.get(messages[c - 1].getAttribute("data-message-id")),
  119. messages[c - 1]
  120. );
  121. }
  122. }
  123. static _renderChatMessage(message, html, _cmd) {
  124. if (!ChatMerge._enabled) return;
  125. // Find the most recent message in the chat log
  126. const partnerElem = $(`li.chat-message`).last()[0];
  127. // If there is no message, return
  128. if (partnerElem === null || partnerElem === undefined) return;
  129. // get the ChatMessage document associated with the html
  130. const partner = game.messages.get(partnerElem.getAttribute("data-message-id"));
  131. if (!message || !partner) return;
  132. // Update styling of the chat messages
  133. ChatMerge._styleChatMessages(message, html[0], partner, partnerElem);
  134. }
  135. static _inTimeFrame(current, previous) {
  136. return current > previous && current - previous < this._epoch * 1000;
  137. }
  138. static _isValidMessage(current, previous) {
  139. const rolls = this._allowRolls;
  140. // const splitSpeaker = SETTINGS.get < boolean > this.PREF_SPLIT_SPEAKER;
  141. const splitSpeaker = true;
  142. let userCompare = false;
  143. const currData = current ?? current;
  144. const prevData = previous ?? previous;
  145. if (splitSpeaker) {
  146. // this is a bit complex, basically we want to group by actors, but if you're not using an actor, group by user instead
  147. userCompare =
  148. // If actors are equal and NOT null
  149. (currData.speaker.actor === prevData.speaker.actor && !!currData.speaker.actor) || // If BOTH actors are null and users are equal
  150. (!currData.speaker.actor && !prevData.speaker.actor && currData.user === prevData.user);
  151. } else {
  152. // If we are not splitting by speaker, just do the simple option of comparing the users
  153. userCompare = currData.user === prevData.user;
  154. }
  155. return (
  156. userCompare &&
  157. this._inTimeFrame(currData.timestamp, prevData.timestamp) &&
  158. // Check for merging with roll types
  159. (rolls === "all" ||
  160. (rolls === "rolls" && current.isRoll === previous.isRoll) ||
  161. (rolls === "none" && !current.isRoll && !previous.isRoll))
  162. );
  163. }
  164. static _styleChatMessages(curr, currElem, prev, prevElem) {
  165. if (!ChatMerge._isValidMessage(curr, prev)) return;
  166. if (prevElem.classList.contains("dfce-cm-bottom")) {
  167. prevElem.classList.remove("dfce-cm-bottom");
  168. prevElem.classList.add("dfce-cm-middle");
  169. } else prevElem.classList.add("dfce-cm-top");
  170. currElem.classList.add("dfce-cm-bottom");
  171. }
  172. }