1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-09-20 01:08:50 +02:00

Improve the implementation of Notifications

Summary:
Currently, you can't change a notification that's already shown. There's no reason for this.

(I'm planning to put file upload progress/errors in notifications.)

  - Make `setContent()` and `setDuration()` immediately affect the notification.
  - When there are more than 5 notifications, queue them up instead of dropping them.
  - Allow arbitrarily many classes to be added/removed.
  - Make the examples in the UIExamples tests more rich.

Test Plan:
  - Verified normal notifications continue to function as expected.
  - Played with the UIExamples notifications:
    - Verified the "update every second" notification udpated every second.
    - Verified the permanent alert notification was yellow and requires a click to dismiss.
    - Verified the interactive notification responds correctly to "OK" / "Cancel".
    - Verified the "click every 2 seconds" notification doesn't vanish until not clicked for 2 seconds.

Reviewers: btrahan, vrana

Reviewed By: btrahan

CC: aran

Differential Revision: https://secure.phabricator.com/D3653
This commit is contained in:
epriestley 2012-10-08 15:22:29 -07:00
parent 3440839c99
commit e00d3b72fe
4 changed files with 125 additions and 74 deletions

View file

@ -888,7 +888,7 @@ celerity_register_resource_map(array(
), ),
'javelin-behavior-aphlict-listen' => 'javelin-behavior-aphlict-listen' =>
array( array(
'uri' => '/res/0743d3f3/rsrc/js/application/aphlict/behavior-aphlict-listen.js', 'uri' => '/res/6dde3f43/rsrc/js/application/aphlict/behavior-aphlict-listen.js',
'type' => 'js', 'type' => 'js',
'requires' => 'requires' =>
array( array(
@ -1522,7 +1522,7 @@ celerity_register_resource_map(array(
), ),
'javelin-behavior-phabricator-notification-example' => 'javelin-behavior-phabricator-notification-example' =>
array( array(
'uri' => '/res/df97e4b3/rsrc/js/application/uiexample/notification-example.js', 'uri' => '/res/a6d51998/rsrc/js/application/uiexample/notification-example.js',
'type' => 'js', 'type' => 'js',
'requires' => 'requires' =>
array( array(
@ -2499,7 +2499,7 @@ celerity_register_resource_map(array(
), ),
'phabricator-notification' => 'phabricator-notification' =>
array( array(
'uri' => '/res/cacd79f1/rsrc/js/application/core/Notification.js', 'uri' => '/res/c604fbbe/rsrc/js/application/core/Notification.js',
'type' => 'js', 'type' => 'js',
'requires' => 'requires' =>
array( array(

View file

@ -35,7 +35,7 @@ JX.behavior('aphlict-listen', function(config) {
new JX.Notification() new JX.Notification()
.setContent('(Aphlict) [' + type + '] ' + details) .setContent('(Aphlict) [' + type + '] ' + details)
.setClassName('jx-notification-debug') .alterClassName('jx-notification-debug', true)
.setDuration(0) .setDuration(0)
.show(); .show();
} }
@ -63,7 +63,7 @@ JX.behavior('aphlict-listen', function(config) {
!showing_reload) { !showing_reload) {
var reload = new JX.Notification() var reload = new JX.Notification()
.setContent('Page updated, click to reload.') .setContent('Page updated, click to reload.')
.setClassName('jx-notification-alert') .alterClassName('jx-notification-alert', true)
.setDuration(0); .setDuration(0);
reload.listen('activate', function(e) { JX.$U().go(); }) reload.listen('activate', function(e) { JX.$U().go(); })
reload.show(); reload.show();

View file

@ -8,7 +8,7 @@
*/ */
/** /**
* Show a notification. Usage: * Show a notification popup on screen. Usage:
* *
* var n = new JX.Notification() * var n = new JX.Notification()
* .setContent('click me!'); * .setContent('click me!');
@ -21,49 +21,78 @@ JX.install('Notification', {
events : ['activate', 'close'], events : ['activate', 'close'],
members : { members : {
_container : null,
_visible : false,
_hideTimer : null,
_duration : 12000,
show : function() { show : function() {
var self = JX.Notification; if (!this._visible) {
self._show(this); this._visible = true;
if (this.getDuration()) { var self = JX.Notification;
setTimeout(JX.bind(self, self._hide, this), this.getDuration()); self._show(this);
this._updateTimer();
} }
return this;
}, },
_render : function() {
return JX.$N(
'div',
{
className: 'jx-notification ' + this.getClassName(),
sigil: 'jx-notification'
},
this.getContent());
}
},
properties : { 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;
},
/** /**
* Optional class name(s) to add to the rendered notification. * Set duration before the notification fades away, in milliseconds. If set
* * to 0, the notification persists until dismissed.
* @param string Class name(s).
*/
className : null,
/**
* Notification content.
*
* @param mixed Content.
*/
content : null,
/**
* Duration before the notification fades away, in milliseconds. If set to
* 0, the notification persists until dismissed.
* *
* @param int Notification duration, in milliseconds. * @param int Notification duration, in milliseconds.
* @return this
*/ */
duration : 12000 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 : { statics : {
@ -74,23 +103,14 @@ JX.install('Notification', {
var self = JX.Notification; var self = JX.Notification;
self._installListener(); self._installListener();
self._active.push({ self._active.push(notification);
object: notification,
render: notification._render()
});
// Don't show more than a few notifications at once because it's silly.
while (self._active.length > 5) {
self._hide(self._active[0].object);
}
self._redraw(); self._redraw();
}, },
_hide : function(notification) { _hide : function(notification) {
var self = JX.Notification; var self = JX.Notification;
for (var ii = 0; ii < self._active.length; ii++) { for (var ii = 0; ii < self._active.length; ii++) {
if (self._active[ii].object === notification) { if (self._active[ii] === notification) {
notification.invoke('close'); notification.invoke('close');
self._active.splice(ii, 1); self._active.splice(ii, 1);
break; break;
@ -113,17 +133,17 @@ JX.install('Notification', {
'jx-notification', 'jx-notification',
function(e) { function(e) {
// NOTE: Don't kill the event since the user might have clicked a // NOTE: Don't kill the event since the user might have clicked a
// link, and we want to follow the link if they did. Istead, invoke // 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 // the activate event for the active notification and dismiss it if it
// isn't handled. // isn't handled.
var target = e.getNode('jx-notification'); var target = e.getNode('jx-notification');
for (var ii = 0; ii < self._active.length; ii++) { for (var ii = 0; ii < self._active.length; ii++) {
var n = self._active[ii]; var n = self._active[ii];
if (n.render === target) { if (n._getContainer() === target) {
var activation = n.object.invoke('activate'); var activation = n.invoke('activate');
if (!activation.getPrevented()) { if (!activation.getPrevented()) {
self._hide(n.object); n.hide();
} }
return; return;
} }
@ -151,9 +171,15 @@ JX.install('Notification', {
document.body.appendChild(self._container); document.body.appendChild(self._container);
} }
// Show only a limited number of notifications at once.
var limit = 5;
var notifications = []; var notifications = [];
for (var ii = 0; ii < self._active.length; ii++) { for (var ii = 0; ii < self._active.length; ii++) {
notifications.push(self._active[ii].render); notifications.push(self._active[ii]._getContainer());
if (!(--limit)) {
break;
}
} }
JX.DOM.setContent(self._container, notifications); JX.DOM.setContent(self._container, notifications);

View file

@ -7,6 +7,9 @@
*/ */
JX.behavior('phabricator-notification-example', function(config) { JX.behavior('phabricator-notification-example', function(config) {
var sequence = 0;
JX.Stratcom.listen( JX.Stratcom.listen(
'click', 'click',
'notification-example', 'notification-example',
@ -14,29 +17,51 @@ JX.behavior('phabricator-notification-example', function(config) {
e.kill(); e.kill();
var notification = new JX.Notification(); var notification = new JX.Notification();
if (Math.random() > 0.1) { switch (sequence % 4) {
notification.setContent('It is ' + new Date().toString()); case 0:
var update = function() {
notification.setContent('It is ' + new Date().toString());
};
notification.listen( update();
'activate', setInterval(update, 1000);
function(e) {
if (!confirm("Close notification?")) {
e.kill();
}
});
} else {
notification
.setContent('Alert! Click to reload!')
.setDuration(0)
.setClassName('jx-notification-alert');
notification.listen( break;
'activate', case 1:
function(e) { notification
new JX.$U().go(); .setContent('Permanent alert notification (until clicked).')
}); .setDuration(0)
.alterClassName('jx-notification-alert', true);
break;
case 2:
notification
.setContent('This notification reacts when you click it.');
notification.listen(
'activate',
function() {
if (!confirm("Close notification?")) {
JX.Stratcom.context().kill();
}
});
break;
case 3:
notification
.setDuration(2000)
.setContent('This notification will close after 2 seconds ' +
'unless you keep clicking it!');
notification.listen(
'activate',
function() {
notification.setDuration(2000);
JX.Stratcom.context().kill();
});
break;
} }
notification.show()
notification.show();
sequence++;
}); });
}); });