diff --git a/.npmignore b/.npmignore index 9528bb80..21306630 100644 --- a/.npmignore +++ b/.npmignore @@ -7,9 +7,10 @@ !.lounge_home # Ignore client folder as it's being built into public/ folder -# except for the findLinks.js file which is used by the server +# except for the specified files which are used by the server client/** !client/js/libs/handlebars/ircmessageparser/findLinks.js +!client/js/libs/handlebars/ircmessageparser/cleanIrcMessage.js public/js/bundle.vendor.js.map coverage/ diff --git a/client/js/libs/handlebars/ircmessageparser/cleanIrcMessage.js b/client/js/libs/handlebars/ircmessageparser/cleanIrcMessage.js new file mode 100644 index 00000000..d5bacde9 --- /dev/null +++ b/client/js/libs/handlebars/ircmessageparser/cleanIrcMessage.js @@ -0,0 +1,4 @@ +"use strict"; + +// TODO: This does not strip hex based colours - issue #1413 +module.exports = (message) => message.replace(/\x02|\x1D|\x1F|\x16|\x0F|\x03(?:[0-9]{1,2}(?:,[0-9]{1,2})?)?/g, "").trim(); diff --git a/client/js/socket-events/msg.js b/client/js/socket-events/msg.js index ccb17c3d..585dc7ef 100644 --- a/client/js/socket-events/msg.js +++ b/client/js/socket-events/msg.js @@ -6,6 +6,8 @@ const render = require("../render"); const utils = require("../utils"); const options = require("../options"); const helpers_roundBadgeNumber = require("../libs/handlebars/roundBadgeNumber"); +const cleanIrcMessage = require("../libs/handlebars/ircmessageparser/cleanIrcMessage"); +const webpush = require("../webpush"); const chat = $("#chat"); const sidebar = $("#sidebar"); @@ -127,20 +129,34 @@ function notifyMessage(targetId, channel, msg) { if (msg.type === "message") { title += " says:"; } - body = msg.text.replace(/\x03(?:[0-9]{1,2}(?:,[0-9]{1,2})?)?|[\x00-\x1F]|\x7F/g, "").trim(); + body = cleanIrcMessage(msg.text); } try { - const notify = new Notification(title, { - body: body, - icon: "img/logo-64.png", - tag: `lounge-${targetId}`, - }); - notify.addEventListener("click", function() { - window.focus(); - button.click(); - this.close(); - }); + if (webpush.hasServiceWorker) { + navigator.serviceWorker.ready.then((registration) => { + registration.active.postMessage({ + type: "notification", + chanId: targetId, + timestamp: msg.time, + title: title, + body: body, + }); + }); + } else { + const notify = new Notification(title, { + tag: `chan-${targetId}`, + badge: "img/logo-64.png", + icon: "img/touch-icon-192x192.png", + body: body, + timestamp: msg.time, + }); + notify.addEventListener("click", function() { + window.focus(); + button.click(); + this.close(); + }); + } } catch (exception) { // `new Notification(...)` is not supported and should be silenced. } diff --git a/client/js/webpush.js b/client/js/webpush.js index 56d5f0c6..16eb9dd8 100644 --- a/client/js/webpush.js +++ b/client/js/webpush.js @@ -8,6 +8,8 @@ const pushNotificationsButton = $("#pushNotifications"); let clientSubscribed = null; let applicationServerKey; +module.exports.hasServiceWorker = false; + module.exports.configurePushNotifications = (subscribedOnServer, key) => { applicationServerKey = key; @@ -16,7 +18,7 @@ module.exports.configurePushNotifications = (subscribedOnServer, key) => { if (clientSubscribed === true && subscribedOnServer === false) { pushNotificationsButton.attr("disabled", true); - navigator.serviceWorker.register("service-worker.js") + navigator.serviceWorker.ready .then((registration) => registration.pushManager.getSubscription()) .then((subscription) => subscription && subscription.unsubscribe()) .then((successful) => { @@ -32,6 +34,8 @@ if (isAllowedServiceWorkersHost()) { if ("serviceWorker" in navigator) { navigator.serviceWorker.register("service-worker.js").then((registration) => { + module.exports.hasServiceWorker = true; + if (!registration.pushManager) { return; } @@ -58,7 +62,7 @@ if (isAllowedServiceWorkersHost()) { function onPushButton() { pushNotificationsButton.attr("disabled", true); - navigator.serviceWorker.register("service-worker.js").then((registration) => { + navigator.serviceWorker.ready.then((registration) => { registration.pushManager.getSubscription().then((existingSubscription) => { if (existingSubscription) { socket.emit("push:unregister"); diff --git a/client/service-worker.js b/client/service-worker.js index e0600045..77882983 100644 --- a/client/service-worker.js +++ b/client/service-worker.js @@ -2,13 +2,19 @@ /* global clients */ "use strict"; +self.addEventListener("message", function(event) { + showNotification(event, event.data); +}); + self.addEventListener("push", function(event) { if (!event.data) { return; } - const payload = event.data.json(); + showNotification(event, event.data.json()); +}); +function showNotification(event, payload) { if (payload.type !== "notification") { return; } @@ -33,7 +39,7 @@ self.addEventListener("push", function(event) { }); }) ); -}); +} self.addEventListener("notificationclick", function(event) { event.notification.close(); diff --git a/src/helper.js b/src/helper.js index a9ba455a..1f44d81e 100644 --- a/src/helper.js +++ b/src/helper.js @@ -21,7 +21,6 @@ var Helper = { getVersion: getVersion, getGitCommit: getGitCommit, ip2hex: ip2hex, - cleanIrcMessage: cleanIrcMessage, password: { hash: passwordHash, @@ -140,11 +139,6 @@ function expandHome(shortenedPath) { return path.resolve(shortenedPath.replace(/^~($|\/|\\)/, home + "$1")); } -function cleanIrcMessage(message) { - // TODO: This does not strip hex based colours - return message.replace(/\x02|\x1D|\x1F|\x16|\x0F|\x03(?:[0-9]{1,2}(?:,[0-9]{1,2})?)?/g, ""); -} - function passwordRequiresUpdate(password) { return bcrypt.getRounds(password) !== 11; } diff --git a/src/plugins/irc-events/link.js b/src/plugins/irc-events/link.js index 1eba9dde..d82a425a 100644 --- a/src/plugins/irc-events/link.js +++ b/src/plugins/irc-events/link.js @@ -4,6 +4,7 @@ const cheerio = require("cheerio"); const request = require("request"); const url = require("url"); const Helper = require("../../helper"); +const cleanIrcMessage = require("../../../client/js/libs/handlebars/ircmessageparser/cleanIrcMessage"); const findLinks = require("../../../client/js/libs/handlebars/ircmessageparser/findLinks"); const storage = require("../storage"); @@ -15,7 +16,7 @@ module.exports = function(client, chan, msg) { } // Remove all IRC formatting characters before searching for links - const cleanText = Helper.cleanIrcMessage(msg.text); + const cleanText = cleanIrcMessage(msg.text); // We will only try to prefetch http(s) links const links = findLinks(cleanText).filter((w) => /^https?:\/\//.test(w.link)); diff --git a/src/plugins/irc-events/message.js b/src/plugins/irc-events/message.js index a078e28b..3fed76d0 100644 --- a/src/plugins/irc-events/message.js +++ b/src/plugins/irc-events/message.js @@ -3,7 +3,7 @@ const Chan = require("../../models/chan"); const Msg = require("../../models/msg"); const LinkPrefetch = require("./link"); -const Helper = require("../../helper"); +const cleanIrcMessage = require("../../../client/js/libs/handlebars/ircmessageparser/cleanIrcMessage"); module.exports = function(irc, network) { const client = this; @@ -107,7 +107,7 @@ module.exports = function(irc, network) { // Do not send notifications for messages older than 15 minutes (znc buffer for example) if (highlight && (!data.time || data.time > Date.now() - 900000)) { let title = chan.name; - let body = Helper.cleanIrcMessage(data.message); + let body = cleanIrcMessage(data.message); // In channels, prepend sender nickname to the message if (chan.type !== Chan.Type.QUERY) { diff --git a/test/tests/cleanircmessages.js b/test/client/js/libs/handlebars/ircmessageparser/cleanIrcMessage.js similarity index 81% rename from test/tests/cleanircmessages.js rename to test/client/js/libs/handlebars/ircmessageparser/cleanIrcMessage.js index 8a3d0dcb..4bca4132 100644 --- a/test/tests/cleanircmessages.js +++ b/test/client/js/libs/handlebars/ircmessageparser/cleanIrcMessage.js @@ -1,9 +1,9 @@ "use strict"; const expect = require("chai").expect; -const Helper = require("../../src/helper"); +const cleanIrcMessage = require("../../../../../../client/js/libs/handlebars/ircmessageparser/cleanIrcMessage"); -describe("Clean IRC messages", function() { +describe("cleanIrcMessage", function() { it("should remove all formatting", function() { const testCases = [{ input: "\x0303", @@ -40,7 +40,7 @@ describe("Clean IRC messages", function() { expected: "#thelounge", }]; - const actual = testCases.map((testCase) => Helper.cleanIrcMessage(testCase.input)); + const actual = testCases.map((testCase) => cleanIrcMessage(testCase.input)); const expected = testCases.map((testCase) => testCase.expected); expect(actual).to.deep.equal(expected);