diff --git a/client/js/render.js b/client/js/render.js index 03b73158..df9628cf 100644 --- a/client/js/render.js +++ b/client/js/render.js @@ -15,57 +15,85 @@ const sidebar = $("#sidebar"); module.exports = { appendMessage, buildChannelMessages, - buildChatMessage, renderChannel, - renderChannelMessages, renderChannelUsers, renderNetworks, }; -function buildChannelMessages(data) { - return data.messages.reduce(function(docFragment, message) { - appendMessage(docFragment, data.id, data.type, message.type, buildChatMessage({ - chan: data.id, - msg: message - })); +function buildChannelMessages(chanId, chanType, messages) { + return messages.reduce((docFragment, message) => { + appendMessage(docFragment, chanId, chanType, message); return docFragment; }, $(document.createDocumentFragment())); } -function appendMessage(container, chan, chanType, messageType, msg) { - // TODO: To fix #1432, statusMessage option should entirely be implemented in CSS - if (constants.condensedTypes.indexOf(messageType) === -1 || chanType !== "channel" || options.statusMessages !== "condensed") { - container.append(msg); +function appendMessage(container, chanId, chanType, msg) { + const renderedMessage = buildChatMessage(chanId, msg); + + // Check if date changed + let lastChild = container.find(".msg").last(); + const msgTime = new Date(msg.time); + + // It's the first message in a window, + // then just append the message and do nothing else + if (lastChild.length === 0) { + container + .append(templates.date_marker({msgDate: msgTime})) + .append(renderedMessage); + return; } - const lastChild = container.children("div.msg").last(); + const prevMsgTime = new Date(lastChild.attr("data-time")); + const parent = lastChild.parent(); - if (lastChild && $(lastChild).hasClass("condensed")) { - lastChild.append(msg); - condensed.updateText(lastChild, [messageType]); - } else if (lastChild && $(lastChild).is(constants.condensedTypesQuery)) { - const newCondensed = buildChatMessage({ - chan: chan, - msg: { - type: "condensed", - time: msg.attr("data-time"), - previews: [] - } + // If this message is condensed, we have to work on the wrapper + if (parent.hasClass("condensed")) { + lastChild = parent; + } + + // Insert date marker if date changed compared to previous message + if (prevMsgTime.toDateString() !== msgTime.toDateString()) { + lastChild.after(templates.date_marker({msgDate: msgTime})); + + // If date changed, we don't need to do condensed logic + container.append(renderedMessage); + return; + } + + // TODO: To fix #1432, statusMessage option should entirely be implemented in CSS + // If current window is not a channel or this message is not condensable, + // then just append the message to container and be done with it + if (constants.condensedTypes.indexOf(msg.type) === -1 || chanType !== "channel" || options.statusMessages !== "condensed") { + container.append(renderedMessage); + return; + } + + // If the previous message is already condensed, + // we just append to it and update text + if (lastChild.hasClass("condensed")) { + lastChild.append(renderedMessage); + condensed.updateText(lastChild, [msg.type]); + // If the previous message can be condensed, we create a new condensed wrapper + } else if (lastChild.is(constants.condensedTypesQuery)) { + const newCondensed = buildChatMessage(chanId, { + type: "condensed", + time: msg.time, + previews: [] }); - condensed.updateText(newCondensed, [messageType, lastChild.attr("data-type")]); + condensed.updateText(newCondensed, [msg.type, lastChild.attr("data-type")]); container.append(newCondensed); newCondensed.append(lastChild); - newCondensed.append(msg); + newCondensed.append(renderedMessage); } else { - container.append(msg); + container.append(renderedMessage); } } -function buildChatMessage(data) { - const type = data.msg.type; - let target = "#chan-" + data.chan; +function buildChatMessage(chanId, msg) { + const type = msg.type; + let target = "#chan-" + chanId; if (type === "error") { target = "#chan-" + chat.find(".active").data("id"); } @@ -74,15 +102,15 @@ function buildChatMessage(data) { let template = "msg"; // See if any of the custom highlight regexes match - if (!data.msg.highlight && !data.msg.self + if (!msg.highlight && !msg.self && options.highlightsRE && (type === "message" || type === "notice") - && options.highlightsRE.exec(data.msg.text)) { - data.msg.highlight = true; + && options.highlightsRE.exec(msg.text)) { + msg.highlight = true; } if (constants.actionTypes.indexOf(type) !== -1) { - data.msg.template = "actions/" + type; + msg.template = "actions/" + type; template = "msg_action"; } else if (type === "unhandled") { template = "msg_unhandled"; @@ -90,29 +118,29 @@ function buildChatMessage(data) { template = "msg_condensed"; } - const msg = $(templates[template](data.msg)); - const content = msg.find(".content"); + const renderedMessage = $(templates[template](msg)); + const content = renderedMessage.find(".content"); if (template === "msg_action") { - content.html(templates.actions[type](data.msg)); + content.html(templates.actions[type](msg)); } - data.msg.previews.forEach((preview) => { - renderPreview(preview, msg); + msg.previews.forEach((preview) => { + renderPreview(preview, renderedMessage); }); if ((type === "message" || type === "action" || type === "notice") && chan.hasClass("channel")) { const nicks = chan.find(".users").data("nicks"); if (nicks) { - const find = nicks.indexOf(data.msg.from); + const find = nicks.indexOf(msg.from); if (find !== -1) { nicks.splice(find, 1); - nicks.unshift(data.msg.from); + nicks.unshift(msg.from); } } } - return msg; + return renderedMessage; } function renderChannel(data) { @@ -124,7 +152,7 @@ function renderChannel(data) { } function renderChannelMessages(data) { - const documentFragment = buildChannelMessages(data); + const documentFragment = buildChannelMessages(data.id, data.type, data.messages); const channel = chat.find("#chan-" + data.id + " .messages").append(documentFragment); if (data.firstUnread > 0) { @@ -141,30 +169,6 @@ function renderChannelMessages(data) { } else { channel.append(templates.unread_marker()); } - - if (data.type !== "lobby") { - let lastDate; - $(chat.find("#chan-" + data.id + " .messages .msg[data-time]")).each(function() { - const msg = $(this); - const msgDate = new Date(msg.attr("data-time")); - - // Top-most message in a channel - if (!lastDate) { - lastDate = msgDate; - msg.before(templates.date_marker({msgDate: msgDate})); - } - - if (lastDate.toDateString() !== msgDate.toDateString()) { - var parent = msg.parent(); - if (parent.hasClass("condensed")) { - msg.insertAfter(parent); - } - msg.before(templates.date_marker({msgDate: msgDate})); - } - - lastDate = msgDate; - }); - } } function renderChannelUsers(data) { diff --git a/client/js/socket-events/more.js b/client/js/socket-events/more.js index 6383a83a..02ef52f0 100644 --- a/client/js/socket-events/more.js +++ b/client/js/socket-events/more.js @@ -4,12 +4,11 @@ const $ = require("jquery"); const socket = require("../socket"); const render = require("../render"); const chat = $("#chat"); -const templates = require("../../views"); socket.on("more", function(data) { - const chan = chat - .find("#chan-" + data.chan) - .find(".messages"); + let chan = chat.find("#chan-" + data.chan); + const type = chan.data("type"); + chan = chan.find(".messages"); // get the scrollable wrapper around messages const scrollable = chan.closest(".chat"); @@ -34,7 +33,7 @@ socket.on("more", function(data) { } // Add the older messages - const documentFragment = render.buildChannelMessages(data); + const documentFragment = render.buildChannelMessages(data.chan, type, data.messages); chan.prepend(documentFragment).end(); // restore scroll position @@ -45,31 +44,6 @@ socket.on("more", function(data) { scrollable.find(".show-more").removeClass("show"); } - // Date change detect - // Have to use data instead of the documentFragment because it's being weird - let lastDate; - $(data.messages).each(function() { - const msgData = this; - const msgDate = new Date(msgData.time); - const msg = $(chat.find("#chan-" + data.chan + " .messages #msg-" + msgData.id)); - - // Top-most message in a channel - if (!lastDate) { - lastDate = msgDate; - msg.before(templates.date_marker({msgDate: msgDate})); - } - - if (lastDate.toDateString() !== msgDate.toDateString()) { - var parent = msg.parent(); - if (parent.hasClass("condensed")) { - msg.insertAfter(parent); - } - msg.before(templates.date_marker({msgDate: msgDate})); - } - - lastDate = msgDate; - }); - scrollable.find(".show-more-button") .text("Show older messages") .prop("disabled", false); diff --git a/client/js/socket-events/msg.js b/client/js/socket-events/msg.js index f2b88498..6549c66c 100644 --- a/client/js/socket-events/msg.js +++ b/client/js/socket-events/msg.js @@ -4,7 +4,6 @@ const $ = require("jquery"); const socket = require("../socket"); const render = require("../render"); const chat = $("#chat"); -const templates = require("../../views"); socket.on("msg", function(data) { if (window.requestIdleCallback) { @@ -18,7 +17,6 @@ socket.on("msg", function(data) { }); function processReceivedMessage(data) { - const msg = render.buildChatMessage(data); const targetId = data.chan; const target = "#chan-" + targetId; const channel = chat.find(target); @@ -30,31 +28,12 @@ function processReceivedMessage(data) { $(container).empty(); } - // Check if date changed - let prevMsg = $(container.find(".msg")).last(); - const prevMsgTime = new Date(prevMsg.attr("data-time")); - const msgTime = new Date(msg.attr("data-time")); - - // It's the first message in a channel/query - if (prevMsg.length === 0) { - container.append(templates.date_marker({msgDate: msgTime})); - } - - if (prevMsgTime.toDateString() !== msgTime.toDateString()) { - var parent = prevMsg.parent(); - if (parent.hasClass("condensed")) { - prevMsg = parent; - } - prevMsg.after(templates.date_marker({msgDate: msgTime})); - } - // Add message to the container render.appendMessage( container, - data.chan, + targetId, $(target).attr("data-type"), - data.msg.type, - msg + data.msg ); container.trigger("msg", [