From 157289258a60c48e18b7de64c10ca1d776e83942 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9mie=20Astori?= Date: Mon, 24 Jul 2017 02:01:25 -0400 Subject: [PATCH] Keep track of preview visibility on the server so it persists at page reload --- client/js/render.js | 4 ---- client/js/renderPreview.js | 13 +++++++++- src/models/chan.js | 4 ++++ src/models/msg.js | 44 +++++++++++++++++++--------------- src/plugins/irc-events/link.js | 1 + src/server.js | 20 ++++++++++++++++ test/models/chan.js | 21 ++++++++++++++++ test/models/msg.js | 37 ++++++++++++++++++++++++++++ test/plugins/link.js | 9 ++++--- 9 files changed, 126 insertions(+), 27 deletions(-) create mode 100644 test/models/msg.js diff --git a/client/js/render.js b/client/js/render.js index b076e230..e3cca373 100644 --- a/client/js/render.js +++ b/client/js/render.js @@ -36,10 +36,6 @@ function buildChatMessage(data) { 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"; diff --git a/client/js/renderPreview.js b/client/js/renderPreview.js index 563ab99a..3f09ff44 100644 --- a/client/js/renderPreview.js +++ b/client/js/renderPreview.js @@ -2,6 +2,7 @@ const $ = require("jquery"); const options = require("./options"); +const socket = require("./socket"); const templates = require("../views"); const input = $("#input"); @@ -24,7 +25,7 @@ function renderPreview(preview, msg) { return; } - preview.shown = options.shouldOpenMessagePreview(preview.type); + preview.shown = preview.shown && options.shouldOpenMessagePreview(preview.type); const container = msg.closest(".chat"); let bottom = false; @@ -60,6 +61,16 @@ $("#chat").on("click", ".toggle-button", function() { self.toggleClass("opened"); content.toggleClass("show"); + // Tell the server we're toggling so it remembers at page reload + // TODO Avoid sending many single events when using `/collapse` or `/expand` + // See https://github.com/thelounge/lounge/issues/1377 + socket.emit("msg:preview:toggle", { + target: parseInt(self.closest(".chan").data("id"), 10), + msgId: parseInt(self.closest(".msg").attr("id").replace("msg-", ""), 10), + link: self.data("url"), + shown: content.hasClass("show"), + }); + // If scrollbar was at the bottom before toggling the preview, keep it at the bottom if (bottom) { container.scrollBottom(); diff --git a/src/models/chan.js b/src/models/chan.js index af4607a8..07a2e229 100644 --- a/src/models/chan.js +++ b/src/models/chan.js @@ -93,6 +93,10 @@ Chan.prototype.sortUsers = function(irc) { }); }; +Chan.prototype.findMessage = function(msgId) { + return this.messages.find((message) => message.id === msgId); +}; + Chan.prototype.findUser = function(nick) { return _.find(this.users, {nick: nick}); }; diff --git a/src/models/msg.js b/src/models/msg.js index 4421b5de..019a7198 100644 --- a/src/models/msg.js +++ b/src/models/msg.js @@ -2,6 +2,31 @@ var _ = require("lodash"); +var id = 0; + +class Msg { + constructor(attr) { + _.defaults(this, attr, { + from: "", + id: id++, + previews: [], + text: "", + type: Msg.Type.MESSAGE, + self: false + }); + + if (this.time > 0) { + this.time = new Date(this.time); + } else { + this.time = new Date(); + } + } + + findPreview(link) { + return this.previews.find((preview) => preview.link === link); + } +} + Msg.Type = { UNHANDLED: "unhandled", ACTION: "action", @@ -24,22 +49,3 @@ Msg.Type = { }; module.exports = Msg; - -var id = 0; - -function Msg(attr) { - _.defaults(this, attr, { - from: "", - id: id++, - previews: [], - text: "", - type: Msg.Type.MESSAGE, - self: false - }); - - if (this.time > 0) { - this.time = new Date(this.time); - } else { - this.time = new Date(); - } -} diff --git a/src/plugins/irc-events/link.js b/src/plugins/irc-events/link.js index e15cf7a8..67e2db89 100644 --- a/src/plugins/irc-events/link.js +++ b/src/plugins/irc-events/link.js @@ -32,6 +32,7 @@ module.exports = function(client, chan, msg) { body: "", thumb: "", link: link, + shown: true, })).slice(0, 5); // Only preview the first 5 URLs in message to avoid abuse msg.previews.forEach((preview) => { diff --git a/src/server.js b/src/server.js index 3bde88ee..2d78192a 100644 --- a/src/server.js +++ b/src/server.js @@ -280,6 +280,26 @@ function init(socket, client) { client.names(data); } ); + + socket.on("msg:preview:toggle", function(data) { + const networkAndChan = client.find(data.target); + if (!networkAndChan) { + return; + } + + const message = networkAndChan.chan.findMessage(data.msgId); + + if (!message) { + return; + } + + const preview = message.findPreview(data.link); + + if (preview) { + preview.shown = data.shown; + } + }); + socket.join(client.id); socket.emit("init", { active: client.lastActiveChannel, diff --git a/test/models/chan.js b/test/models/chan.js index 0201a076..fa0482ae 100644 --- a/test/models/chan.js +++ b/test/models/chan.js @@ -3,9 +3,30 @@ var expect = require("chai").expect; var Chan = require("../../src/models/chan"); +var Msg = require("../../src/models/msg"); var User = require("../../src/models/user"); describe("Chan", function() { + describe("#findMessage(id)", function() { + const chan = new Chan({ + messages: [ + new Msg(), + new Msg({ + text: "Message to be found" + }), + new Msg() + ] + }); + + it("should find a message in the list of messages", function() { + expect(chan.findMessage(1).text).to.equal("Message to be found"); + }); + + it("should not find a message that does not exist", function() { + expect(chan.findMessage(42)).to.be.undefined; + }); + }); + describe("#sortUsers(irc)", function() { var network = { network: { diff --git a/test/models/msg.js b/test/models/msg.js new file mode 100644 index 00000000..5a9a8ad8 --- /dev/null +++ b/test/models/msg.js @@ -0,0 +1,37 @@ +"use strict"; + +const expect = require("chai").expect; + +const Msg = require("../../src/models/msg"); + +describe("Msg", function() { + describe("#findPreview(link)", function() { + const msg = new Msg({ + previews: [{ + body: "", + head: "Example Domain", + link: "https://example.org/", + thumb: "", + type: "link", + shown: true, + }, { + body: "", + head: "The Lounge", + link: "https://thelounge.github.io/", + thumb: "", + type: "link", + shown: true, + }] + }); + + it("should find a preview given an existing link", function() { + expect(msg.findPreview("https://thelounge.github.io/").head) + .to.equal("The Lounge"); + }); + + it("should not find a preview that does not exist", function() { + expect(msg.findPreview("https://github.com/thelounge/lounge")) + .to.be.undefined; + }); + }); +}); diff --git a/test/plugins/link.js b/test/plugins/link.js index a0d9aff8..912d3de1 100644 --- a/test/plugins/link.js +++ b/test/plugins/link.js @@ -39,7 +39,8 @@ describe("Link plugin", function() { head: "", link: url, thumb: "", - type: "loading" + type: "loading", + shown: true, }]); this.app.get("/basic", function(req, res) { @@ -193,13 +194,15 @@ describe("Link plugin", function() { head: "", link: "http://localhost:9002/one", thumb: "", - type: "loading" + type: "loading", + shown: true, }, { body: "", head: "", link: "http://localhost:9002/two", thumb: "", - type: "loading" + type: "loading", + shown: true, }]); this.app.get("/one", function(req, res) {