diff options
| author | Dharmraj Jhala <dja@openerp.com> | 2017-05-11 17:15:15 +0530 |
|---|---|---|
| committer | Géry Debongnie <ged@odoo.com> | 2017-07-12 13:43:08 +0200 |
| commit | 4330ea02676a4afd88707bb463a007e7de3c8686 (patch) | |
| tree | f547124a18fd30e09433a65cd9aa7b5a6645131a /addons/web/static/src/js | |
| parent | 578edf73b9f637b0d27708c511b542a27bd7a3b8 (diff) | |
[ADD] web, *: add rainbowification
This commit introduces the 'rainbowification' feature to the web client
(and to some affected addons). This feature is essentially a way to
display a nice friendly message when some business event happens. For
example, a encouraging message is displayed when the user clear her/his
inbox, or when a salesman closes a deal.
The mechanism currently only displays a 'rainbowman', but could be
extended later to add other kind of animation.
There are a few different ways to display a 'rainbowman':
- any JS code can simply trigger_up an event ('show_effect') with some
options: for example
this.trigger_up('show_effect', {
type: 'rainbow_man',
fadeout: 'no',
message: $done,
click_close: false,
});
- an action returned by the server can have an 'effect' key, with some
options
- the do_execute_action method accepts a 'effect' option. This is
useful when one wants to specify a rainbow on an action button in a
form view
Joint work with: dbh <dbh@odoo.com>, ged <ged@odoo.com>
Diffstat (limited to 'addons/web/static/src/js')
6 files changed, 121 insertions, 25 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 481e9a7fc54..2a5bb5310ce 100644 --- a/addons/web/static/src/js/chrome/abstract_web_client.js +++ b/addons/web/static/src/js/chrome/abstract_web_client.js @@ -21,6 +21,7 @@ var Dialog = require('web.Dialog'); var Loading = require('web.Loading'); var mixins = require('web.mixins'); var NotificationManager = require('web.notification').NotificationManager; +var RainbowMan = require('web.rainbow_man'); var session = require('web.session'); var Widget = require('web.Widget'); @@ -43,13 +44,13 @@ var AbstractWebClient = Widget.extend(mixins.ServiceProvider, { // downstream widgets. Mainly side effects, such as rpcs, notifications // or cache. - // notifications and warnings + // 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: '_displayWarning', + warning: '_onDisplayWarning', load_views: function (event) { var params = { model: event.data.modelName, @@ -65,6 +66,7 @@ var AbstractWebClient = Widget.extend(mixins.ServiceProvider, { .load_filters(event.data.dataset, event.data.action_id) .then(event.data.on_success); }, + show_effect: '_onShowEffect', // session get_session: function (event) { if (event.data.callback) { @@ -82,7 +84,6 @@ var AbstractWebClient = Widget.extend(mixins.ServiceProvider, { } }); }, - show_wow: '_onShowWow', }, init: function (parent) { this.client_options = {}; @@ -300,7 +301,8 @@ var AbstractWebClient = Widget.extend(mixins.ServiceProvider, { /** * Displays a warning in a dialog of with the NotificationManager - + * + * @private * @param {OdooEvent} e * @param {string} e.data.message the warning's message * @param {string} e.data.title the warning's title @@ -308,7 +310,7 @@ var AbstractWebClient = Widget.extend(mixins.ServiceProvider, { * @param {boolean} [e.data.sticky] whether or not the warning should be * sticky (if displayed with the NotificationManager) */ - _displayWarning: function (e) { + _onDisplayWarning: function (e) { var data = e.data; if (data.type === 'dialog') { new Dialog(this, { @@ -321,20 +323,21 @@ var AbstractWebClient = Widget.extend(mixins.ServiceProvider, { } }, /** - * Displays a thumb up, heart or peace image (randomly) for a moment (e.g. - * used when an opportunity is won) + * Displays a visual effect (for example, a rainbowman0 * * @private + * @param {OdooEvent} e + * @param {Object} [e.data] - key-value options to decide rainbowman + * behavior / appearance */ - _onShowWow: function () { - var className = 'o_wow_thumbs'; - if (Math.random() > 0.9) { - var otherClasses = ['o_wow_peace', 'o_wow_heart']; - className = otherClasses[Math.floor(Math.random()*otherClasses.length)]; + _onShowEffect: function (e) { + var data = e.data || {}; + var type = data.type || 'rainbow_man'; + if (type === 'rainbow_man') { + new RainbowMan(data).appendTo(this.$el); + } else { + throw new Error('Unknown effect type: ' + type); } - var $body = $('body'); - $body.addClass(className); - setTimeout($body.removeClass.bind($body, className), 1000); }, }); diff --git a/addons/web/static/src/js/chrome/action_manager.js b/addons/web/static/src/js/chrome/action_manager.js index c81479ec1e0..280a90cec8d 100644 --- a/addons/web/static/src/js/chrome/action_manager.js +++ b/addons/web/static/src/js/chrome/action_manager.js @@ -862,6 +862,11 @@ var ActionManager = Widget.extend({ options.on_close(); } this.dialog_stop(); + // Display rainbowman on appropriate actions + if (action.effect) { + this.trigger_up('show_effect', action.effect); + } + return $.when(); }, ir_actions_server: function (action, options) { diff --git a/addons/web/static/src/js/chrome/view_manager.js b/addons/web/static/src/js/chrome/view_manager.js index 11be6fa4e6e..14ed4bdebc7 100644 --- a/addons/web/static/src/js/chrome/view_manager.js +++ b/addons/web/static/src/js/chrome/view_manager.js @@ -550,6 +550,9 @@ var ViewManager = Widget.extend(ControlPanelMixin, { * @param {String} [action_data.special=null] special action handlers, closes the dialog if set * @param {String} [action_data.type='workflow'] the action type, if present, one of ``'object'``, ``'action'`` or ``'workflow'`` * @param {Object} [action_data.context=null] additional action context, to add to the current context + * @param {string} [action_data.effect] if given, a visual effect (a + * rainbowman by default) will be displayed when the action is complete, + * with the string (evaluated) given as options. * @param {DataSet} dataset a dataset object used to communicate with the server * @param {integer[]} [res_ids] the res_ids of the objects on which the action is to be applied (fallback on env.ids if not set) * @param {Function} on_closed callback to execute when dialog is closed or when the action does not generate any result (no new action) @@ -562,6 +565,14 @@ var ViewManager = Widget.extend(ControlPanelMixin, { // response handler var handler = function (action) { + // show effect if button have effect attribute + // Rainbowman can be displayed from two places : from attribute on a button, or from python. + // Code below handles the first case i.e 'effect' attribute on button. + var effect = false; + if (action_data.effect) { + effect = pyeval.py_eval(action_data.effect); + }; + if (action && action.constructor === Object) { // filter out context keys that are specific to the current action. // Wrong default_* and search_default_* values will no give the expected result @@ -580,11 +591,16 @@ var ViewManager = Widget.extend(ControlPanelMixin, { } ncontext.add(action.context || {}); action.context = ncontext; + // In case effect data is returned from python and also there is rainbow + // attribute on button, priority is given to button attribute + action.effect = effect || action.effect; return self.do_action(action, { on_close: result_handler, }); } else { - self.do_action({"type":"ir.actions.act_window_close"}); + // If action doesn't return anything, but have effect + // attribute on button, display rainbowman + self.do_action({"type":"ir.actions.act_window_close", 'effect': effect}); return result_handler(); } }; diff --git a/addons/web/static/src/js/views/form/form_controller.js b/addons/web/static/src/js/views/form/form_controller.js index f741e98bf4d..c95278aa255 100644 --- a/addons/web/static/src/js/views/form/form_controller.js +++ b/addons/web/static/src/js/views/form/form_controller.js @@ -244,7 +244,7 @@ var FormController = BasicController.extend({ * @override * @private */ - _disableButtons: function () { + _disableButtons: function () { this._super.apply(this, arguments); this.renderer.disableButtons(); }, @@ -254,7 +254,7 @@ var FormController = BasicController.extend({ * @override * @private */ - _enableButtons: function () { + _enableButtons: function () { this._super.apply(this, arguments); this.renderer.enableButtons(); }, @@ -380,10 +380,6 @@ var FormController = BasicController.extend({ }); } - if (event.data.showWow) { - def.then(this.trigger_up.bind(this, 'show_wow')); - } - def.always(this._enableButtons.bind(this)); }, /** diff --git a/addons/web/static/src/js/views/form/form_renderer.js b/addons/web/static/src/js/views/form/form_renderer.js index 25b72e4fb5a..f7c4861dd2a 100644 --- a/addons/web/static/src/js/views/form/form_renderer.js +++ b/addons/web/static/src/js/views/form/form_renderer.js @@ -117,14 +117,14 @@ var FormRenderer = BasicRenderer.extend({ * Disable stat buttons so that they can't be clicked anymore * */ - disableButtons: function() { + disableButtons: function() { this.$('.oe_button_box button').attr('disabled', true); }, /** * Enable stat buttons so they can be clicked again * */ - enableButtons: function() { + enableButtons: function() { this.$('.oe_button_box button').removeAttr('disabled'); }, /** @@ -206,7 +206,6 @@ var FormRenderer = BasicRenderer.extend({ self.trigger_up('button_clicked', { attrs: node.attrs, record: self.state, - showWow: $el.hasClass('o_wow'), }); }); }, diff --git a/addons/web/static/src/js/widgets/rainbow_man.js b/addons/web/static/src/js/widgets/rainbow_man.js new file mode 100644 index 00000000000..53624b6322d --- /dev/null +++ b/addons/web/static/src/js/widgets/rainbow_man.js @@ -0,0 +1,77 @@ +odoo.define('web.rainbow_man', function (require) { +"use strict"; + +var ajax = require("web.ajax"); +var Widget = require('web.Widget'); +var core = require('web.core'); +var qweb = core.qweb; +var _t = core._t; + +var RainbowMan = Widget.extend({ + template: 'rainbow_man.notification', + /** + * @override + * @constructor + * @param {Object} [options] - key-value options to decide rainbowman behavior / appearance + * @param {string} [options.message] - Message to be displayed on rainbowman card + * @param {string} [options.fadeout] + * Delay for rainbowman to disappear - [options.fadeout='fast'] will make rainbowman + * dissapear quickly, [options.fadeout='medium'] and [options.fadeout='slow'] will + * wait little longer before disappearing (can be used when [options.message] + * is longer), [options.fadeout='no'] will keep rainbowman on screen until + * user clicks anywhere outside rainbowman + * @param {string} [options.img_url] - URL of the image to be displayed + * @param {Boolean} [options.click_close] - If true, destroys rainbowman on click outside + */ + init: function (options) { + this._super.apply(this, arguments); + var rainbowDelay = {slow: 4500, medium: 3500, fast:2000, no: false }; + this.options = _.defaults(options || {}, { + fadeout: 'medium', + img_url: '/web/static/src/img/smile.svg', + message: _t('Well Done!'), + click_close: true, + }); + this.delay = rainbowDelay[this.options.fadeout]; + core.bus.on('clear_uncommitted_changes', this, this.destroy); + }, + /** + * @override + */ + willStart: function () { + var def; + if (!qweb.has_template(this.template)) { + // we need to manually load the templates when the rainbon_man is used in + // the frontend, for example, it may be displayed at the end of a tour. + def = ajax.loadXML("/web/static/src/xml/rainbow_man.xml", qweb); + } + return $.when(this._super.apply(this, arguments), def); + }, + /** + * @override + */ + start: function () { + var self = this; + if (this.options.click_close) { + core.bus.on('click', this, function (ev) { + if (ev.originalEvent && ev.target.className.indexOf('o_reward') === -1) { + this.destroy(); + } + }); + } + if (this.delay) { + setTimeout(function () { + self.$el.addClass('o_reward_fading'); + setTimeout(function () { + self.destroy(); + }, 600); // destroy only after fadeout animation is completed + }, this.delay); + } + this.$('.o_reward_msg_content').append(this.options.message); + return this._super.apply(this, arguments); + } +}); + +return RainbowMan; + +}); |
