summaryrefslogtreecommitdiff
path: root/addons/web/static/src
diff options
context:
space:
mode:
authorChristophe Matthieu <chm@odoo.com>2018-03-29 17:46:24 +0200
committerGéry Debongnie <ged@odoo.com>2018-04-19 17:17:54 +0200
commit07cd24b1ecf3ab8edd3fffbf2fcd3a3e685f357c (patch)
tree3fc6a94ed00c2501e83a102f07438ea87097fca8 /addons/web/static/src
parent67a7d1f2b5ec1cdc2fa6677a359fc6cf10f02b85 (diff)
[IMP] web: convert notification popup in service and add buttons params
this.call('web.Notification', 'notify', params) Display a notification at the appropriate location, and returns the reference id to the same widget. Note that this method does not wait for the appendTo method to complete. @param {Object} params @param {string} params.title notification title @param {string} params.message notification main message @param {string} params.type 'notification' or 'warning' @param {boolean} [params.sticky=false] if true, the notification will stay visible until the user clicks on it. @param {string} [params.className] className to add on the dom @param {function} [params.onClose] callback when the user click on the x or when the notification is auto close (no sticky) @param {Array<Object>} params.buttons @param {function} params.buttons[0].click callback on click @param {Boolean} [params.buttons[0].primary] display the button as primary @param {string} [params.buttons[0].text] button label @param {string} [params.buttons[0].icon] font-awsome className or image src @returns {Number} notification id this.call('web.Notification', 'close', notificationId)
Diffstat (limited to 'addons/web/static/src')
-rw-r--r--addons/web/static/src/js/chrome/abstract_web_client.js72
-rw-r--r--addons/web/static/src/js/core/service_mixins.js4
-rw-r--r--addons/web/static/src/js/services/notification_manager.js90
-rw-r--r--addons/web/static/src/js/services/notification_service.js108
-rw-r--r--addons/web/static/src/js/widgets/notification.js77
-rw-r--r--addons/web/static/src/xml/base.xml20
6 files changed, 216 insertions, 155 deletions
diff --git a/addons/web/static/src/js/chrome/abstract_web_client.js b/addons/web/static/src/js/chrome/abstract_web_client.js
index 3492af748f5..612d04c577a 100644
--- a/addons/web/static/src/js/chrome/abstract_web_client.js
+++ b/addons/web/static/src/js/chrome/abstract_web_client.js
@@ -21,7 +21,6 @@ var Dialog = require('web.Dialog');
var dom = require('web.dom');
var KeyboardNavigationMixin = require('web.KeyboardNavigationMixin');
var Loading = require('web.Loading');
-var NotificationManager = require('web.NotificationManager');
var RainbowMan = require('web.RainbowMan');
var ServiceProviderMixin = require('web.ServiceProviderMixin');
var session = require('web.session');
@@ -31,6 +30,7 @@ var _t = core._t;
var qweb = core.qweb;
var AbstractWebClient = Widget.extend(ServiceProviderMixin, KeyboardNavigationMixin, {
+ dependencies: ['notification'],
events: _.extend(KeyboardNavigationMixin.events, {}),
custom_events: {
clear_uncommitted_changes: function (e) {
@@ -46,13 +46,6 @@ var AbstractWebClient = Widget.extend(ServiceProviderMixin, KeyboardNavigationMi
// the next events are dedicated to generic services required by
// downstream widgets. Mainly side effects, such as rpcs, notifications
// or cache.
-
- // notifications, warnings and effects
- notification: function (e) {
- if (this.notification_manager) {
- this.notification_manager.notify(e.data.title, e.data.message, e.data.sticky);
- }
- },
warning: '_onDisplayWarning',
load_action: '_onLoadAction',
load_views: function (event) {
@@ -120,7 +113,6 @@ var AbstractWebClient = Widget.extend(ServiceProviderMixin, KeyboardNavigationMi
self.bind_events();
return $.when(
self.set_action_manager(),
- self.set_notification_manager(),
self.set_loading()
);
}).then(function () {
@@ -174,8 +166,8 @@ var AbstractWebClient = Widget.extend(ServiceProviderMixin, KeyboardNavigationMi
}
this._hideAccessKeyOverlay();
});
- core.bus.on('connection_lost', this, this.on_connection_lost);
- core.bus.on('connection_restored', this, this.on_connection_restored);
+ core.bus.on('connection_lost', this, this._onConnectionLost);
+ core.bus.on('connection_restored', this, this._onConnectionRestored);
// crash manager integration
session.on('error', crash_manager, crash_manager.rpc_error);
@@ -222,10 +214,6 @@ var AbstractWebClient = Widget.extend(ServiceProviderMixin, KeyboardNavigationMi
});
});
},
- set_notification_manager: function () {
- this.notification_manager = new NotificationManager(this);
- return this.notification_manager.appendTo(this.$el);
- },
set_loading: function () {
this.loading = new Loading(this);
return this.loading.appendTo(this.$el);
@@ -309,24 +297,6 @@ var AbstractWebClient = Widget.extend(ServiceProviderMixin, KeyboardNavigationMi
// --------------------------------------------------------------
// Connection notifications
// --------------------------------------------------------------
- on_connection_lost: function () {
- this.connection_notification = this.notification_manager.notify(
- _t('Connection lost'),
- _t('Trying to reconnect...'),
- true
- );
- },
- on_connection_restored: function () {
- if (this.connection_notification) {
- this.connection_notification.destroy();
- this.notification_manager.notify(
- _t('Connection restored'),
- _t('You are back online'),
- false
- );
- this.connection_notification = false;
- }
- },
/**
* Handler to be overridden, called each time the UI is updated by the
* ActionManager.
@@ -348,6 +318,34 @@ var AbstractWebClient = Widget.extend(ServiceProviderMixin, KeyboardNavigationMi
//--------------------------------------------------------------------------
/**
+ * Whenever the connection is lost, we need to notify the user.
+ *
+ * @private
+ */
+ _onConnectionLost: function () {
+ this.connectionNotificationID = this.call('notification', 'notify', {
+ title: _t('Connection lost'),
+ message: _t('Trying to reconnect...'),
+ sticky: true
+ });
+ },
+ /**
+ * Whenever the connection is restored, we need to notify the user.
+ *
+ * @private
+ */
+ _onConnectionRestored: function () {
+ if (this.connectionNotificationID) {
+ this.call('notification', 'close', this.connectionNotificationID);
+ this.call('notification', 'notify', {
+ title: _t('Connection restored'),
+ message: _t('You are back online'),
+ sticky: false
+ });
+ this.connectionNotificationID = false;
+ }
+ },
+ /**
* @private
* @param {OdooEvent} e
* @param {Object} e.data.filter the filter description
@@ -360,7 +358,7 @@ var AbstractWebClient = Widget.extend(ServiceProviderMixin, KeyboardNavigationMi
.then(e.data.on_success);
},
/**
- * Displays a warning in a dialog of with the NotificationManager
+ * Displays a warning in a dialog or with the notification service
*
* @private
* @param {OdooEvent} e
@@ -368,7 +366,7 @@ var AbstractWebClient = Widget.extend(ServiceProviderMixin, KeyboardNavigationMi
* @param {string} e.data.title the warning's title
* @param {string} [e.data.type] 'dialog' to display in a dialog
* @param {boolean} [e.data.sticky] whether or not the warning should be
- * sticky (if displayed with the NotificationManager)
+ * sticky (if displayed with the Notification)
*/
_onDisplayWarning: function (e) {
var data = e.data;
@@ -378,8 +376,8 @@ var AbstractWebClient = Widget.extend(ServiceProviderMixin, KeyboardNavigationMi
title: data.title,
$content: qweb.render("CrashManager.warning", data),
}).open();
- } else if (this.notification_manager) {
- this.notification_manager.warn(e.data.title, e.data.message, e.data.sticky);
+ } else {
+ this.call('notification', 'notify', e.data);
}
},
/**
diff --git a/addons/web/static/src/js/core/service_mixins.js b/addons/web/static/src/js/core/service_mixins.js
index 4ab0a361988..463b0aef4b1 100644
--- a/addons/web/static/src/js/core/service_mixins.js
+++ b/addons/web/static/src/js/core/service_mixins.js
@@ -185,10 +185,10 @@ var ServicesMixin = {
return def;
},
do_notify: function (title, message, sticky, className) {
- this.trigger_up('notification', {title: title, message: message, sticky: sticky, className: className});
+ return this.call('notification', 'notify', {title: title, message: message, sticky: sticky, className: className});
},
do_warn: function (title, message, sticky, className) {
- this.trigger_up('warning', {title: title, message: message, sticky: sticky, className: className});
+ return this.call('notification', 'notify', {type: 'warning', title: title, message: message, sticky: sticky, className: className});
},
};
diff --git a/addons/web/static/src/js/services/notification_manager.js b/addons/web/static/src/js/services/notification_manager.js
deleted file mode 100644
index b9b128be78d..00000000000
--- a/addons/web/static/src/js/services/notification_manager.js
+++ /dev/null
@@ -1,90 +0,0 @@
-odoo.define('web.NotificationManager', function (require) {
-"use strict";
-
-/**
- * NotificationManager
- *
- * The NotificationManager is simply a widget that will be instantiated by the
- * web client to display notifications in the top/right part of the screen.
- *
- * If you want to display such a notification, you probably do not want to do it
- * by using this file. The proper way is to use the do_warn or do_notify
- * methods on the Widget class.
- *
- * @todo This class should be converted into a proper service. Need the right
- * service API to do it.
- */
-
-var Notification = require('web.Notification');
-var Widget = require('web.Widget');
-
-var NotificationManager = Widget.extend({
- className: 'o_notification_manager',
-
- //--------------------------------------------------------------------------
- // Public
- //--------------------------------------------------------------------------
-
- /**
- * Display a notification at the appropriate location, and returns the
- * reference to the same widget (useful to get back a reference).
- *
- * Note that this method does not wait for the appendTo method to complete.
- *
- * @param {Notification} notification
- * @returns {Notification}
- */
- display: function (notification) {
- notification.appendTo(this.$el);
- return notification;
- },
- /**
- * Will display a notification of type 'notification'
- *
- * @param {string} title
- * @param {string} text
- * @param {boolean} sticky
- * @returns {Notification}
- */
- notify: function (title, text, sticky) {
- return this._display(title, text, sticky, 'notification');
- },
- /**
- * Will display a notification of type 'warning'
- *
- * @param {string} title
- * @param {string} text
- * @param {boolean} sticky
- * @returns {Notification}
- */
- warn: function (title, text, sticky) {
- return this._display(title, text, sticky, 'warning');
- },
-
- //--------------------------------------------------------------------------
- // Private
- //--------------------------------------------------------------------------
-
- /**
- * Display a notification
- *
- * @private
- * @param {string} title
- * @param {string} text
- * @param {boolean} sticky
- * @param {string} type either 'notification' or 'warning'
- * @returns {Notification}
- */
- _display: function (title, text, sticky, type) {
- return this.display(new Notification(this, {
- title: title,
- text: text,
- sticky: sticky,
- type: type,
- }));
- }
-});
-
-return NotificationManager;
-
-});
diff --git a/addons/web/static/src/js/services/notification_service.js b/addons/web/static/src/js/services/notification_service.js
new file mode 100644
index 00000000000..7d6ad473fe4
--- /dev/null
+++ b/addons/web/static/src/js/services/notification_service.js
@@ -0,0 +1,108 @@
+odoo.define('web.NotificationService', function (require) {
+"use strict";
+
+var AbstractService = require('web.AbstractService');
+var Notification = require('web.Notification');
+var core = require('web.core');
+var id = 0;
+
+/**
+ * Notification Service
+ *
+ * The Notification Service is simply a service used to display notifications in
+ * the top/right part of the screen.
+ *
+ * If you want to display such a notification, you probably do not want to do it
+ * by using this file. The proper way is to use the do_warn or do_notify
+ * methods on the Widget class.
+ */
+
+var NotificationService = AbstractService.extend({
+ name: 'notification',
+
+ custom_events: {
+ close: '_onCloseNotification',
+ },
+
+ init: function () {
+ this._super.apply(this, arguments);
+ this.notifications = {};
+ },
+
+ //--------------------------------------------------------------------------
+ // Public
+ //--------------------------------------------------------------------------
+
+ /**
+ * It may sometimes be useful to close programmatically a notification. For
+ * example, when there is a sticky notification that warns the user about
+ * some condition (connection lost), but the condition do not apply anymore.
+ *
+ * @param {number} notificationId
+ * @param {boolean} [silent=false] if true, the notification does not call
+ * onClose callback
+ */
+ close: function (notificationId, silent) {
+ var notification = this.notifications[notificationId];
+ if (!notification) {
+ return;
+ }
+ notification.close(silent);
+ },
+ /**
+ * Display a notification at the appropriate location, and returns the
+ * reference id to the same widget.
+ *
+ * Note that this method does not wait for the appendTo method to complete.
+ *
+ * @param {Object} params
+ * @param {string} params.title notification title
+ * @param {string} params.message notification main message
+ * @param {string} params.type 'notification' or 'warning'
+ * @param {boolean} [params.sticky=false] if true, the notification will stay
+ * visible until the user clicks on it.
+ * @param {string} [params.className] className to add on the dom
+ * @param {function} [params.onClose] callback when the user click on the x
+ * or when the notification is auto close (no sticky)
+ * @param {Object[]} params.buttons
+ * @param {function} params.buttons[0].click callback on click
+ * @param {Boolean} [params.buttons[0].primary] display the button as primary
+ * @param {string} [params.buttons[0].text] button label
+ * @param {string} [params.buttons[0].icon] font-awsome className or image src
+ * @returns {Number} notification id
+ */
+ notify: function (options) {
+ if (!this.$el) {
+ this.$el = $('<div class="o_notification_manager"/>');
+ this.$el.prependTo('body');
+ }
+ var notification = this.notifications[++id] = new Notification(this, options);
+ notification.appendTo(this.$el);
+ return id;
+ },
+
+ //--------------------------------------------------------------------------
+ // Handlers
+ //--------------------------------------------------------------------------
+
+ /**
+ * @private
+ * @param {OdooEvent} ev
+ */
+ _onCloseNotification: function (ev) {
+ ev.stopPropagation();
+ for (var notificationId in this.notifications) {
+ if (this.notifications[notificationId] === ev.target) {
+ delete this.notifications[notificationId];
+ break;
+ }
+ }
+ },
+});
+
+
+core.serviceRegistry.add('notification', NotificationService);
+
+
+return NotificationService;
+});
diff --git a/addons/web/static/src/js/widgets/notification.js b/addons/web/static/src/js/widgets/notification.js
index d05a0be380c..e9425144085 100644
--- a/addons/web/static/src/js/widgets/notification.js
+++ b/addons/web/static/src/js/widgets/notification.js
@@ -17,26 +17,45 @@ var Widget = require('web.Widget');
var Notification = Widget.extend({
template: 'Notification',
events: {
- 'click .o_close': '_onClose',
+ 'click > .o_close': '_onClose',
+ 'click .o_buttons button': '_onClickButton'
},
+ _autoCloseDelay: 2500,
+ _animationDelay: 400,
/**
* @override
* @param {Widget} parent
* @param {Object} params
* @param {string} params.title notification title
- * @param {string} params.text notification main text
+ * @param {string} params.message notification main message
* @param {string} params.type 'notification' or 'warning'
* @param {boolean} [params.sticky=false] if true, the notification will stay
* visible until the user clicks on it.
+ * @param {string} [params.className] className to add on the dom
+ * @param {function} [params.onClose] callback when the user click on the x
+ * or when the notification is auto close (no sticky)
+ * @param {Object[]} params.buttons
+ * @param {function} params.buttons[0].click callback on click
+ * @param {boolean} [params.buttons[0].primary] display the button as primary
+ * @param {string} [params.buttons[0].text] button label
+ * @param {string} [params.buttons[0].icon] font-awsome className or image src
*/
init: function (parent, params) {
this._super.apply(this, arguments);
this.title = params.title;
- this.text = params.text;
- this.sticky = !!params.sticky;
- this.type = params.type;
+ this.message = params.message;
+ this.buttons = params.buttons || [];
+ this.sticky = !!this.buttons.length || !!params.sticky;
+ this.type = params.type || 'notification';
+ this.className = params.className || '';
+ this._closeCallback = params.onClose;
+ this.icon = 'fa-lightbulb-o';
+ if (this.buttons && this.buttons.length) {
+ this.icon = 'fa-question-circle-o';
+ }
if (this.type === 'warning') {
- this.template = 'Warning';
+ this.icon = 'fa-exclamation';
+ this.className += ' o_error';
}
},
/**
@@ -44,33 +63,38 @@ var Notification = Widget.extend({
*/
start: function () {
var self = this;
- if (this.type === 'warning') {
- this.$el.addClass('o_error');
- }
return this._super.apply(this, arguments).then(function () {
- self.$el.animate({opacity: 1.0}, 400, "swing", function () {
+ self.$el.animate({opacity: 1.0}, self._animationDelay, "swing", function () {
if(!self.sticky) {
- setTimeout(self._destroy.bind(self), 2500);
+ setTimeout(function () {
+ self.close();
+ }, self._autoCloseDelay);
}
});
});
},
//--------------------------------------------------------------------------
- // Private
+ // Public
//--------------------------------------------------------------------------
/**
- * This method is used to destroy the widget with a nice animation. We
+ * This method is used to destroy the widget with a nice animation. We
* first perform an animation, then call the real destroy method.
*
* @private
+ * @param {boolean} [silent=false] if true, the notification does not call
+ * _closeCallback method
*/
- _destroy: function () {
+ close: function (silent) {
var self = this;
- this.$el.animate({opacity: 0.0}, 400, "swing", function() {
- self.$el.animate({height: 0}, 400, "swing", self.destroy.bind(self));
- });
+ this.trigger_up('close');
+ if (!silent && !this._buttonClicked) {
+ if (this._closeCallback) {
+ this._closeCallback();
+ }
+ }
+ this.$el.animate({opacity: 0.0, height: 0}, this._animationDelay, "swing", self.destroy.bind(self));
},
//--------------------------------------------------------------------------
@@ -81,9 +105,26 @@ var Notification = Widget.extend({
* @private
* @param {MouseEvent} ev
*/
+ _onClickButton: function (ev) {
+ ev.preventDefault();
+ if (this._buttonClicked) {
+ return;
+ }
+ this._buttonClicked = true;
+ var index = $(ev.currentTarget).index();
+ var button = this.buttons[index];
+ if (button.click) {
+ button.click();
+ }
+ this.close(true);
+ },
+ /**
+ * @private
+ * @param {MouseEvent} ev
+ */
_onClose: function (ev) {
ev.preventDefault();
- this._destroy();
+ this.close();
},
});
diff --git a/addons/web/static/src/xml/base.xml b/addons/web/static/src/xml/base.xml
index 592c41a518c..20fee925a0f 100644
--- a/addons/web/static/src/xml/base.xml
+++ b/addons/web/static/src/xml/base.xml
@@ -115,20 +115,24 @@
</t>
<t t-name="Notification">
- <div t-attf-class="o_notification #{className}" t-translation="off">
+ <div t-attf-class="o_notification #{widget.className}" t-translation="off">
<a t-if="widget.sticky" class="fa fa-times o_close" href="#"/>
<div class="o_notification_title">
- <span class="o_icon fa fa-3x fa-lightbulb-o"/>
+ <span t-attf-class="o_icon fa fa-3x #{widget.icon}"/>
<t t-raw="widget.title"/>
</div>
- <div class="o_notification_content" t-if="widget.text"><t t-raw="widget.text"/></div>
+ <div class="o_notification_content" t-if="widget.message"><t t-raw="widget.message"/></div>
+ <div t-if="widget.buttons.length" class="o_buttons">
+ <button t-foreach="widget.buttons" t-as="button" t-attf-class="btn btn-sm #{button.primary ? 'btn-primary' : 'btn-default'}" type="button">
+ <t t-if="button.icon">
+ <i t-if="button.icon.indexOf('fa-') === 0" t-attf-class="fa fa-fw o_button_icon #{button.icon}"/>
+ <img t-else="" t-att-src="button.icon"/>
+ </t>
+ <span><t t-esc="button.text"/></span>
+ </button>
+ </div>
</div>
</t>
-<t t-name="Warning" t-extend="Notification">
- <t t-jquery=".o_icon" t-operation="replace">
- <span class="o_icon fa fa-3x fa-exclamation"/>
- </t>
-</t>
<div t-name="CrashManager.warning" class="o_dialog_warning">
<t t-js="d">