thelounge/client/js/render.js

223 lines
5.6 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");
module.exports = {
2017-06-22 20:08:36 +00:00
appendMessage,
2017-05-18 20:08:54 +00:00
buildChannelMessages,
buildChatMessage,
renderChannel,
renderChannelMessages,
renderChannelUsers,
renderNetworks,
2017-05-18 20:08:54 +00:00
};
2017-06-22 20:08:36 +00:00
function buildChannelMessages(data) {
return data.messages.reduce(function(docFragment, message) {
appendMessage(docFragment, data.id, data.type, message.type, buildChatMessage({
chan: data.id,
2017-05-18 20:08:54 +00:00
msg: message
}));
return docFragment;
}, $(document.createDocumentFragment()));
}
2017-06-22 20:08:36 +00:00
function appendMessage(container, chan, chanType, messageType, msg) {
2017-08-19 18:47:23 +00:00
// 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);
return;
}
const lastChild = container.children("div.msg").last();
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: []
}
});
condensed.updateText(newCondensed, [messageType, lastChild.attr("data-type")]);
container.append(newCondensed);
newCondensed.append(lastChild);
newCondensed.append(msg);
2017-06-22 20:08:36 +00:00
} else {
container.append(msg);
}
}
2017-05-18 20:08:54 +00:00
function buildChatMessage(data) {
const type = data.msg.type;
let target = "#chan-" + data.chan;
if (type === "error") {
target = "#chan-" + chat.find(".active").data("id");
}
const chan = chat.find(target);
let template = "msg";
// See if any of the custom highlight regexes match
if (!data.msg.highlight && !data.msg.self
&& options.highlightsRE
&& (type === "message" || type === "notice")
&& options.highlightsRE.exec(data.msg.text)) {
2017-05-18 20:08:54 +00:00
data.msg.highlight = true;
}
2017-06-22 20:08:36 +00:00
if (constants.actionTypes.indexOf(type) !== -1) {
data.msg.template = "actions/" + type;
2017-05-18 20:08:54 +00:00
template = "msg_action";
} else if (type === "unhandled") {
template = "msg_unhandled";
2017-06-22 20:08:36 +00:00
} else if (type === "condensed") {
template = "msg_condensed";
2017-05-18 20:08:54 +00:00
}
const msg = $(templates[template](data.msg));
const content = msg.find(".content");
2017-05-18 20:08:54 +00:00
if (template === "msg_action") {
content.html(templates.actions[type](data.msg));
2017-05-18 20:08:54 +00:00
}
data.msg.previews.forEach((preview) => {
renderPreview(preview, msg);
});
if ((type === "message" || type === "action" || type === "notice") && chan.hasClass("channel")) {
2017-05-18 20:08:54 +00:00
const nicks = chan.find(".users").data("nicks");
if (nicks) {
const find = nicks.indexOf(data.msg.from);
if (find !== -1) {
nicks.splice(find, 1);
nicks.unshift(data.msg.from);
}
}
}
return msg;
}
function renderChannel(data) {
renderChannelMessages(data);
if (data.type === "channel") {
renderChannelUsers(data);
}
}
function renderChannelMessages(data) {
2017-06-22 20:08:36 +00:00
const documentFragment = buildChannelMessages(data);
2017-05-18 20:08:54 +00:00
const channel = chat.find("#chan-" + data.id + " .messages").append(documentFragment);
if (data.firstUnread > 0) {
const first = channel.find("#msg-" + data.firstUnread);
// TODO: If the message is far off in the history, we still need to append the marker into DOM
if (!first.length) {
channel.prepend(templates.unread_marker());
2017-06-22 20:08:36 +00:00
} else if (first.parent().hasClass("condensed")) {
first.parent().before(templates.unread_marker());
2017-05-18 20:08:54 +00:00
} else {
first.before(templates.unread_marker());
}
} 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()) {
2017-06-22 20:08:36 +00:00
var parent = msg.parent();
if (parent.hasClass("condensed")) {
msg.insertAfter(parent);
}
2017-05-18 20:08:54 +00:00
msg.before(templates.date_marker({msgDate: msgDate}));
}
lastDate = msgDate;
});
}
}
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);
}
}