Merge pull request #1580 from thelounge/xpaw/mobile-active-notifications

Use service worker to display notifications if available
This commit is contained in:
Pavel Djundik 2017-11-19 18:40:45 +02:00 committed by GitHub
commit 055bd5dca5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 54 additions and 28 deletions

View File

@ -7,9 +7,10 @@
!.lounge_home !.lounge_home
# Ignore client folder as it's being built into public/ folder # 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/**
!client/js/libs/handlebars/ircmessageparser/findLinks.js !client/js/libs/handlebars/ircmessageparser/findLinks.js
!client/js/libs/handlebars/ircmessageparser/cleanIrcMessage.js
public/js/bundle.vendor.js.map public/js/bundle.vendor.js.map
coverage/ coverage/

View File

@ -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();

View File

@ -6,6 +6,8 @@ const render = require("../render");
const utils = require("../utils"); const utils = require("../utils");
const options = require("../options"); const options = require("../options");
const helpers_roundBadgeNumber = require("../libs/handlebars/roundBadgeNumber"); const helpers_roundBadgeNumber = require("../libs/handlebars/roundBadgeNumber");
const cleanIrcMessage = require("../libs/handlebars/ircmessageparser/cleanIrcMessage");
const webpush = require("../webpush");
const chat = $("#chat"); const chat = $("#chat");
const sidebar = $("#sidebar"); const sidebar = $("#sidebar");
@ -127,20 +129,34 @@ function notifyMessage(targetId, channel, msg) {
if (msg.type === "message") { if (msg.type === "message") {
title += " says:"; 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 { try {
const notify = new Notification(title, { if (webpush.hasServiceWorker) {
body: body, navigator.serviceWorker.ready.then((registration) => {
icon: "img/logo-64.png", registration.active.postMessage({
tag: `lounge-${targetId}`, type: "notification",
}); chanId: targetId,
notify.addEventListener("click", function() { timestamp: msg.time,
window.focus(); title: title,
button.click(); body: body,
this.close(); });
}); });
} 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) { } catch (exception) {
// `new Notification(...)` is not supported and should be silenced. // `new Notification(...)` is not supported and should be silenced.
} }

View File

@ -8,6 +8,8 @@ const pushNotificationsButton = $("#pushNotifications");
let clientSubscribed = null; let clientSubscribed = null;
let applicationServerKey; let applicationServerKey;
module.exports.hasServiceWorker = false;
module.exports.configurePushNotifications = (subscribedOnServer, key) => { module.exports.configurePushNotifications = (subscribedOnServer, key) => {
applicationServerKey = key; applicationServerKey = key;
@ -16,7 +18,7 @@ module.exports.configurePushNotifications = (subscribedOnServer, key) => {
if (clientSubscribed === true && subscribedOnServer === false) { if (clientSubscribed === true && subscribedOnServer === false) {
pushNotificationsButton.attr("disabled", true); pushNotificationsButton.attr("disabled", true);
navigator.serviceWorker.register("service-worker.js") navigator.serviceWorker.ready
.then((registration) => registration.pushManager.getSubscription()) .then((registration) => registration.pushManager.getSubscription())
.then((subscription) => subscription && subscription.unsubscribe()) .then((subscription) => subscription && subscription.unsubscribe())
.then((successful) => { .then((successful) => {
@ -32,6 +34,8 @@ if (isAllowedServiceWorkersHost()) {
if ("serviceWorker" in navigator) { if ("serviceWorker" in navigator) {
navigator.serviceWorker.register("service-worker.js").then((registration) => { navigator.serviceWorker.register("service-worker.js").then((registration) => {
module.exports.hasServiceWorker = true;
if (!registration.pushManager) { if (!registration.pushManager) {
return; return;
} }
@ -58,7 +62,7 @@ if (isAllowedServiceWorkersHost()) {
function onPushButton() { function onPushButton() {
pushNotificationsButton.attr("disabled", true); pushNotificationsButton.attr("disabled", true);
navigator.serviceWorker.register("service-worker.js").then((registration) => { navigator.serviceWorker.ready.then((registration) => {
registration.pushManager.getSubscription().then((existingSubscription) => { registration.pushManager.getSubscription().then((existingSubscription) => {
if (existingSubscription) { if (existingSubscription) {
socket.emit("push:unregister"); socket.emit("push:unregister");

View File

@ -2,13 +2,19 @@
/* global clients */ /* global clients */
"use strict"; "use strict";
self.addEventListener("message", function(event) {
showNotification(event, event.data);
});
self.addEventListener("push", function(event) { self.addEventListener("push", function(event) {
if (!event.data) { if (!event.data) {
return; return;
} }
const payload = event.data.json(); showNotification(event, event.data.json());
});
function showNotification(event, payload) {
if (payload.type !== "notification") { if (payload.type !== "notification") {
return; return;
} }
@ -33,7 +39,7 @@ self.addEventListener("push", function(event) {
}); });
}) })
); );
}); }
self.addEventListener("notificationclick", function(event) { self.addEventListener("notificationclick", function(event) {
event.notification.close(); event.notification.close();

View File

@ -21,7 +21,6 @@ var Helper = {
getVersion: getVersion, getVersion: getVersion,
getGitCommit: getGitCommit, getGitCommit: getGitCommit,
ip2hex: ip2hex, ip2hex: ip2hex,
cleanIrcMessage: cleanIrcMessage,
password: { password: {
hash: passwordHash, hash: passwordHash,
@ -140,11 +139,6 @@ function expandHome(shortenedPath) {
return path.resolve(shortenedPath.replace(/^~($|\/|\\)/, home + "$1")); 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) { function passwordRequiresUpdate(password) {
return bcrypt.getRounds(password) !== 11; return bcrypt.getRounds(password) !== 11;
} }

View File

@ -4,6 +4,7 @@ const cheerio = require("cheerio");
const request = require("request"); const request = require("request");
const url = require("url"); const url = require("url");
const Helper = require("../../helper"); const Helper = require("../../helper");
const cleanIrcMessage = require("../../../client/js/libs/handlebars/ircmessageparser/cleanIrcMessage");
const findLinks = require("../../../client/js/libs/handlebars/ircmessageparser/findLinks"); const findLinks = require("../../../client/js/libs/handlebars/ircmessageparser/findLinks");
const storage = require("../storage"); const storage = require("../storage");
@ -15,7 +16,7 @@ module.exports = function(client, chan, msg) {
} }
// Remove all IRC formatting characters before searching for links // 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 // We will only try to prefetch http(s) links
const links = findLinks(cleanText).filter((w) => /^https?:\/\//.test(w.link)); const links = findLinks(cleanText).filter((w) => /^https?:\/\//.test(w.link));

View File

@ -3,7 +3,7 @@
const Chan = require("../../models/chan"); const Chan = require("../../models/chan");
const Msg = require("../../models/msg"); const Msg = require("../../models/msg");
const LinkPrefetch = require("./link"); const LinkPrefetch = require("./link");
const Helper = require("../../helper"); const cleanIrcMessage = require("../../../client/js/libs/handlebars/ircmessageparser/cleanIrcMessage");
module.exports = function(irc, network) { module.exports = function(irc, network) {
const client = this; 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) // Do not send notifications for messages older than 15 minutes (znc buffer for example)
if (highlight && (!data.time || data.time > Date.now() - 900000)) { if (highlight && (!data.time || data.time > Date.now() - 900000)) {
let title = chan.name; let title = chan.name;
let body = Helper.cleanIrcMessage(data.message); let body = cleanIrcMessage(data.message);
// In channels, prepend sender nickname to the message // In channels, prepend sender nickname to the message
if (chan.type !== Chan.Type.QUERY) { if (chan.type !== Chan.Type.QUERY) {

View File

@ -1,9 +1,9 @@
"use strict"; "use strict";
const expect = require("chai").expect; 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() { it("should remove all formatting", function() {
const testCases = [{ const testCases = [{
input: "\x0303", input: "\x0303",
@ -40,7 +40,7 @@ describe("Clean IRC messages", function() {
expected: "#thelounge", 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); const expected = testCases.map((testCase) => testCase.expected);
expect(actual).to.deep.equal(expected); expect(actual).to.deep.equal(expected);