Summary: Fixes T4139. Adds a "Desktop Notifications" panel to settings. For now, we start with "Send Desktop Notifications Too" functionality. We can try to be fancy later and only send desktop notifications if the web app doesn't have focus, etc. Test Plan: Made some comments as a test user on a task and got purdy desktop notifications using Chrome. Then did it again with Firefox. Played around with permissions form with Chrome and got helpful information about what was up. Played around with Firefox and got similar results, except canceling the dialogue didn't invoke my handler code somehow. Oh Firefox! Reviewers: epriestley Reviewed By: epriestley Subscribers: rbalik, tycho.tatitscheff, joshuaspence, epriestley, Korvin Maniphest Tasks: T4139 Differential Revision: https://secure.phabricator.com/D13219
255 lines
6 KiB
JavaScript
255 lines
6 KiB
JavaScript
/**
|
|
* @requires javelin-install
|
|
* javelin-dom
|
|
* javelin-stratcom
|
|
* javelin-util
|
|
* phabricator-notification-css
|
|
* @provides phabricator-notification
|
|
* @javelin
|
|
*/
|
|
|
|
/**
|
|
* Show a notification popup on screen. Usage:
|
|
*
|
|
* var n = new JX.Notification()
|
|
* .setContent('click me!');
|
|
* n.listen('activate', function(e) { alert('you clicked!'); });
|
|
* n.show();
|
|
*
|
|
*/
|
|
JX.install('Notification', {
|
|
|
|
events : ['activate', 'close'],
|
|
|
|
members : {
|
|
_container : null,
|
|
_visible : false,
|
|
_hideTimer : null,
|
|
_duration : 12000,
|
|
_desktopReady : false,
|
|
_key : null,
|
|
_title : null,
|
|
_body : null,
|
|
_href : null,
|
|
_icon : null,
|
|
|
|
show : function() {
|
|
var self = JX.Notification;
|
|
if (!this._visible) {
|
|
this._visible = true;
|
|
|
|
self._show(this);
|
|
this._updateTimer();
|
|
}
|
|
|
|
if (self.supportsDesktopNotifications() &&
|
|
self.desktopNotificationsEnabled() &&
|
|
this._desktopReady) {
|
|
// Note: specifying "tag" means that notifications with matching
|
|
// keys will aggregate.
|
|
var n = new window.Notification(this._title, {
|
|
icon: this._icon,
|
|
body: this._body,
|
|
tag: this._key,
|
|
});
|
|
n.onclick = JX.bind(n, function (href) {
|
|
this.close();
|
|
window.focus();
|
|
if (href) {
|
|
JX.$U(href).go();
|
|
}
|
|
}, this._href);
|
|
// Note: some OS / browsers do this automagically; make the behavior
|
|
// happen everywhere.
|
|
setTimeout(n.close.bind(n), this._duration);
|
|
}
|
|
return this;
|
|
},
|
|
|
|
hide : function() {
|
|
if (this._visible) {
|
|
this._visible = false;
|
|
|
|
var self = JX.Notification;
|
|
self._hide(this);
|
|
this._updateTimer();
|
|
}
|
|
return this;
|
|
},
|
|
|
|
alterClassName : function(name, enable) {
|
|
JX.DOM.alterClass(this._getContainer(), name, enable);
|
|
return this;
|
|
},
|
|
|
|
setContent : function(content) {
|
|
JX.DOM.setContent(this._getContainer(), content);
|
|
return this;
|
|
},
|
|
|
|
setDesktopReady : function(ready) {
|
|
this._desktopReady = ready;
|
|
return this;
|
|
},
|
|
|
|
setTitle : function(title) {
|
|
this._title = title;
|
|
return this;
|
|
},
|
|
|
|
setBody : function(body) {
|
|
this._body = body;
|
|
return this;
|
|
},
|
|
|
|
setHref : function(href) {
|
|
this._href = href;
|
|
return this;
|
|
},
|
|
|
|
setKey : function(key) {
|
|
this._key = key;
|
|
return this;
|
|
},
|
|
|
|
setIcon : function(icon) {
|
|
this._icon = icon;
|
|
return this;
|
|
},
|
|
|
|
/**
|
|
* Set duration before the notification fades away, in milliseconds. If set
|
|
* to 0, the notification persists until dismissed.
|
|
*
|
|
* @param int Notification duration, in milliseconds.
|
|
* @return this
|
|
*/
|
|
setDuration : function(milliseconds) {
|
|
this._duration = milliseconds;
|
|
this._updateTimer(false);
|
|
return this;
|
|
},
|
|
|
|
_updateTimer : function() {
|
|
if (this._hideTimer) {
|
|
clearTimeout(this._hideTimer);
|
|
this._hideTimer = null;
|
|
}
|
|
|
|
if (this._visible && this._duration) {
|
|
this._hideTimer = setTimeout(JX.bind(this, this.hide), this._duration);
|
|
}
|
|
},
|
|
|
|
_getContainer : function() {
|
|
if (!this._container) {
|
|
this._container = JX.$N(
|
|
'div',
|
|
{
|
|
className: 'jx-notification',
|
|
sigil: 'jx-notification'
|
|
});
|
|
}
|
|
return this._container;
|
|
}
|
|
},
|
|
|
|
statics : {
|
|
supportsDesktopNotifications : function () {
|
|
return 'Notification' in window;
|
|
},
|
|
desktopNotificationsEnabled : function () {
|
|
return window.Notification.permission === 'granted';
|
|
},
|
|
_container : null,
|
|
_listening : false,
|
|
_active : [],
|
|
_show : function(notification) {
|
|
var self = JX.Notification;
|
|
|
|
self._installListener();
|
|
self._active.push(notification);
|
|
self._redraw();
|
|
},
|
|
_hide : function(notification) {
|
|
var self = JX.Notification;
|
|
|
|
for (var ii = 0; ii < self._active.length; ii++) {
|
|
if (self._active[ii] === notification) {
|
|
notification.invoke('close');
|
|
self._active.splice(ii, 1);
|
|
break;
|
|
}
|
|
}
|
|
|
|
self._redraw();
|
|
},
|
|
_installListener : function() {
|
|
var self = JX.Notification;
|
|
|
|
if (self._listening) {
|
|
return;
|
|
} else {
|
|
self._listening = true;
|
|
}
|
|
|
|
JX.Stratcom.listen(
|
|
'click',
|
|
'jx-notification',
|
|
function(e) {
|
|
// NOTE: Don't kill the event since the user might have clicked a
|
|
// link, and we want to follow the link if they did. Instead, invoke
|
|
// the activate event for the active notification and dismiss it if it
|
|
// isn't handled.
|
|
|
|
var target = e.getNode('jx-notification');
|
|
for (var ii = 0; ii < self._active.length; ii++) {
|
|
var n = self._active[ii];
|
|
if (n._getContainer() === target) {
|
|
var activation = n.invoke('activate');
|
|
if (!activation.getPrevented()) {
|
|
n.hide();
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
|
|
});
|
|
},
|
|
_redraw : function() {
|
|
var self = JX.Notification;
|
|
|
|
if (!self._active.length) {
|
|
if (self._container) {
|
|
JX.DOM.remove(self._container);
|
|
self._container = null;
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (!self._container) {
|
|
self._container = JX.$N(
|
|
'div',
|
|
{
|
|
className: 'jx-notification-container'
|
|
});
|
|
document.body.appendChild(self._container);
|
|
}
|
|
|
|
// Show only a limited number of notifications at once.
|
|
var limit = 5;
|
|
|
|
var notifications = [];
|
|
for (var ii = 0; ii < self._active.length; ii++) {
|
|
notifications.push(self._active[ii]._getContainer());
|
|
if (!(--limit)) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
JX.DOM.setContent(self._container, notifications);
|
|
}
|
|
}
|
|
|
|
});
|