64ebe0f437
- Load up to 5 previews per message (to avoid abuse) - Do not load multiple times the same URL - Prepare preview containers per message instead of appending (to maintain correct order) - Store an array of previews instead of a single preview in `Msg` objects - Consolidate preview rendering for new messages and upon refresh/load history (when rendering entire channels) - Update `parse` tests to reflect previous point - Add test for multiple URLs - Switch preview tests from `assert` API to `expect` API
205 lines
4.5 KiB
JavaScript
205 lines
4.5 KiB
JavaScript
"use strict";
|
|
|
|
const $ = require("jquery");
|
|
const templates = require("../views");
|
|
const options = require("./options");
|
|
const renderPreview = require("./renderPreview");
|
|
const utils = require("./utils");
|
|
const sorting = require("./sorting");
|
|
|
|
const chat = $("#chat");
|
|
const sidebar = $("#sidebar");
|
|
|
|
module.exports = {
|
|
buildChannelMessages,
|
|
buildChatMessage,
|
|
renderChannel,
|
|
renderChannelMessages,
|
|
renderChannelUsers,
|
|
renderNetworks,
|
|
};
|
|
|
|
function buildChannelMessages(channel, messages) {
|
|
return messages.reduce(function(docFragment, message) {
|
|
docFragment.append(buildChatMessage({
|
|
chan: channel,
|
|
msg: message
|
|
}));
|
|
return docFragment;
|
|
}, $(document.createDocumentFragment()));
|
|
}
|
|
|
|
function buildChatMessage(data) {
|
|
const type = data.msg.type;
|
|
let target = "#chan-" + data.chan;
|
|
if (type === "error") {
|
|
target = "#chan-" + chat.find(".active").data("id");
|
|
}
|
|
|
|
data.msg.previews.forEach((preview) => {
|
|
preview.shown = options.shouldOpenMessagePreview(preview.type);
|
|
});
|
|
|
|
const chan = chat.find(target);
|
|
let template = "msg";
|
|
|
|
if (!data.msg.highlight && !data.msg.self && (type === "message" || type === "notice") && options.highlights.some(function(h) {
|
|
return data.msg.text.toLocaleLowerCase().indexOf(h.toLocaleLowerCase()) > -1;
|
|
})) {
|
|
data.msg.highlight = true;
|
|
}
|
|
|
|
if ([
|
|
"invite",
|
|
"join",
|
|
"mode",
|
|
"kick",
|
|
"nick",
|
|
"part",
|
|
"quit",
|
|
"topic",
|
|
"topic_set_by",
|
|
"action",
|
|
"whois",
|
|
"ctcp",
|
|
"channel_list",
|
|
"ban_list",
|
|
].indexOf(type) !== -1) {
|
|
template = "msg_action";
|
|
} else if (type === "unhandled") {
|
|
template = "msg_unhandled";
|
|
}
|
|
|
|
const msg = $(templates[template](data.msg));
|
|
const text = msg.find(".text");
|
|
|
|
if (data.msg.previews.length) {
|
|
data.msg.previews.forEach((preview) => {
|
|
renderPreview(preview, msg);
|
|
});
|
|
}
|
|
|
|
if (template === "msg_action") {
|
|
text.html(templates.actions[type](data.msg));
|
|
}
|
|
|
|
if ((type === "message" || type === "action") && chan.hasClass("channel")) {
|
|
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) {
|
|
const documentFragment = buildChannelMessages(data.id, data.messages);
|
|
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());
|
|
} 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()) {
|
|
msg.before(templates.date_marker({msgDate: msgDate}));
|
|
}
|
|
|
|
lastDate = msgDate;
|
|
});
|
|
}
|
|
}
|
|
|
|
function renderChannelUsers(data) {
|
|
const users = chat.find("#chan-" + data.id).find(".users");
|
|
let nicks = users.data("nicks") || [];
|
|
const oldSortOrder = {};
|
|
|
|
for (const i in nicks) {
|
|
oldSortOrder[nicks[i]] = i;
|
|
}
|
|
|
|
nicks = [];
|
|
|
|
for (const i in data.users) {
|
|
nicks.push(data.users[i].nick);
|
|
}
|
|
|
|
nicks = nicks.sort(function(a, b) {
|
|
return (oldSortOrder[a] || Number.MAX_VALUE) - (oldSortOrder[b] || Number.MAX_VALUE);
|
|
});
|
|
|
|
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
|
|
})
|
|
);
|
|
channels.forEach(renderChannel);
|
|
|
|
utils.confirmExit();
|
|
sorting();
|
|
|
|
if (sidebar.find(".highlight").length) {
|
|
utils.toggleNotificationMarkers(true);
|
|
}
|
|
}
|