thelounge/client/js/render.js

214 lines
5.1 KiB
JavaScript
Raw Normal View History

2017-05-18 20:08:54 +00:00
"use strict";
const $ = require("jquery");
const templates = require("../views");
const options = require("./options");
const renderPreview = require("./renderPreview");
2017-05-18 20:08:54 +00:00
const utils = require("./utils");
const sorting = require("./sorting");
2017-06-22 20:08:36 +00:00
const constants = require("./constants");
2017-08-19 18:47:23 +00:00
const condensed = require("./condensed");
2017-05-18 20:08:54 +00:00
const chat = $("#chat");
const sidebar = $("#sidebar");
2017-08-22 21:04:55 +00:00
require("intersection-observer");
const historyObserver = window.IntersectionObserver ?
new window.IntersectionObserver(loadMoreHistory, {
root: chat.get(0)
}) : null;
2017-05-18 20:08:54 +00:00
module.exports = {
2017-06-22 20:08:36 +00:00
appendMessage,
2017-05-18 20:08:54 +00:00
buildChannelMessages,
renderChannel,
renderChannelUsers,
renderNetworks,
2017-05-18 20:08:54 +00:00
};
function buildChannelMessages(chanId, chanType, messages) {
return messages.reduce((docFragment, message) => {
appendMessage(docFragment, chanId, chanType, message);
2017-05-18 20:08:54 +00:00
return docFragment;
}, $(document.createDocumentFragment()));
}
function appendMessage(container, chanId, chanType, msg) {
2017-09-01 11:43:24 +00:00
let lastChild = container.children(".msg, .date-marker-container").last();
const renderedMessage = buildChatMessage(msg);
// Check if date changed
const msgTime = new Date(msg.time);
2017-09-01 11:43:24 +00:00
const prevMsgTime = new Date(lastChild.data("time"));
// Insert date marker if date changed compared to previous message
if (prevMsgTime.toDateString() !== msgTime.toDateString()) {
lastChild = $(templates.date_marker({time: msg.time}));
2017-09-01 11:43:24 +00:00
container.append(lastChild);
}
// 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") {
container.append(renderedMessage);
2017-08-19 18:47:23 +00:00
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]);
return;
2017-06-22 20:08:36 +00:00
}
2017-09-01 11:43:24 +00:00
// Always create a condensed container
const newCondensed = $(templates.msg_condensed({time: msg.time}));
condensed.updateText(newCondensed, [msg.type]);
newCondensed.append(renderedMessage);
container.append(newCondensed);
2017-06-22 20:08:36 +00:00
}
function buildChatMessage(msg) {
const type = msg.type;
2017-05-18 20:08:54 +00:00
let template = "msg";
// See if any of the custom highlight regexes match
if (!msg.highlight && !msg.self
&& options.highlightsRE
&& (type === "message" || type === "notice")
&& options.highlightsRE.exec(msg.text)) {
msg.highlight = true;
2017-05-18 20:08:54 +00:00
}
2017-06-22 20:08:36 +00:00
if (constants.actionTypes.indexOf(type) !== -1) {
msg.template = "actions/" + type;
2017-05-18 20:08:54 +00:00
template = "msg_action";
} else if (type === "unhandled") {
template = "msg_unhandled";
}
const renderedMessage = $(templates[template](msg));
const content = renderedMessage.find(".content");
2017-05-18 20:08:54 +00:00
if (template === "msg_action") {
content.html(templates.actions[type](msg));
2017-05-18 20:08:54 +00:00
}
msg.previews.forEach((preview) => {
renderPreview(preview, renderedMessage);
});
return renderedMessage;
2017-05-18 20:08:54 +00:00
}
function renderChannel(data) {
renderChannelMessages(data);
if (data.type === "channel") {
renderChannelUsers(data);
}
if (historyObserver) {
historyObserver.observe(chat.find("#chan-" + data.id + " .show-more").get(0));
}
2017-05-18 20:08:54 +00:00
}
function renderChannelMessages(data) {
const documentFragment = buildChannelMessages(data.id, data.type, data.messages);
2017-05-18 20:08:54 +00:00
const channel = chat.find("#chan-" + data.id + " .messages").append(documentFragment);
const template = $(templates.unread_marker());
2017-05-18 20:08:54 +00:00
if (data.firstUnread > 0) {
let first = channel.find("#msg-" + data.firstUnread);
2017-05-18 20:08:54 +00:00
if (!first.length) {
template.data("unread-id", data.firstUnread);
channel.prepend(template);
2017-05-18 20:08:54 +00:00
} else {
const parent = first.parent();
if (parent.hasClass("condensed")) {
first = parent;
}
first.before(template);
2017-05-18 20:08:54 +00:00
}
} else {
channel.append(template);
2017-05-18 20:08:54 +00:00
}
}
function renderChannelUsers(data) {
const users = chat.find("#chan-" + data.id).find(".users");
const nicks = data.users
2017-07-10 10:56:58 +00:00
.concat() // Make a copy of the user list, sort is applied in-place
.sort((a, b) => b.lastMessage - a.lastMessage)
.map((a) => a.nick);
2017-05-18 20:08:54 +00:00
const search = users
.find(".search")
.attr("placeholder", nicks.length + " " + (nicks.length === 1 ? "user" : "users"));
users
.data("nicks", nicks)
.find(".names-original")
.html(templates.user(data));
// Refresh user search
if (search.val().length) {
search.trigger("input");
}
}
function renderNetworks(data) {
sidebar.find(".empty").hide();
sidebar.find(".networks").append(
templates.network({
networks: data.networks
})
);
const channels = $.map(data.networks, function(n) {
return n.channels;
});
chat.append(
templates.chat({
channels: channels
})
);
2017-07-10 10:56:58 +00:00
channels.forEach((channel) => {
renderChannel(channel);
if (channel.type === "channel") {
chat.find("#chan-" + channel.id).data("needsNamesRefresh", true);
}
});
2017-05-18 20:08:54 +00:00
utils.confirmExit();
sorting();
if (sidebar.find(".highlight").length) {
utils.toggleNotificationMarkers(true);
}
}
function loadMoreHistory(entries) {
entries.forEach((entry) => {
if (!entry.isIntersecting) {
return;
}
var target = $(entry.target).find(".show-more-button");
if (target.attr("disabled")) {
return;
}
target.click();
});
}