summaryrefslogtreecommitdiff
path: root/addons/mail/static/src/js/services/mail_notification_manager.js
diff options
context:
space:
mode:
authorSébastien Theys <seb@odoo.com>2020-06-15 18:12:21 +0000
committerSébastien Theys <seb@odoo.com>2020-06-15 18:12:21 +0000
commit3fea5b2136627c1d7e83b2a5e111952fe3eb10a1 (patch)
tree5eefad07972a87ce4689b83260f981bc87aadad4 /addons/mail/static/src/js/services/mail_notification_manager.js
parent0a82848606151ef60eb6523e9c4c161171f214e7 (diff)
[REF] mail, *: refactor messaging with OWL
This commit is a significant rewriting of client-side discuss, chatter, chat window, and messaging menu using OWL. The behavior should be broadly the same, with some slight functional changes here and there. From a technical standpoint, the code of messaging is mainly organized in 2 main groups of modules: - models, which are logical entities that depict the client-side state of messaging as a whole. - components, which are in charge of displaying information from models. This refactoring also introduces new JS guidelines regarding folder structure (/static) and naming rules for JS modules. Community PR: https://github.com/odoo/odoo/pull/39023 Enterprise PR: https://github.com/odoo/enterprise/pull/6249 Task-1914207 This PR is a collaborative work by Alexandre, Julien, Sébastien and Xavier, with the precious help of Lucas to speed it up towards the end. closes odoo/odoo#39023 Related: odoo/enterprise#6249 Signed-off-by: Alexandre Kühn (aku) <aku@odoo.com> Co-authored-by: Alexandre Kühn <aku@odoo.com> Co-authored-by: Julien Giannone <jgi@odoo.com> Co-authored-by: Lucas Perais <lpe@odoo.com> Co-authored-by: Sébastien Theys <seb@odoo.com> Co-authored-by: Xavier Dubuc <xdu@odoo.com>
Diffstat (limited to 'addons/mail/static/src/js/services/mail_notification_manager.js')
-rw-r--r--addons/mail/static/src/js/services/mail_notification_manager.js597
1 files changed, 0 insertions, 597 deletions
diff --git a/addons/mail/static/src/js/services/mail_notification_manager.js b/addons/mail/static/src/js/services/mail_notification_manager.js
deleted file mode 100644
index c7c5a03e6bf..00000000000
--- a/addons/mail/static/src/js/services/mail_notification_manager.js
+++ /dev/null
@@ -1,597 +0,0 @@
-odoo.define('mail.Manager.Notification', function (require) {
-"use strict";
-
-/**
- * Mail Notification Manager
- *
- * This part of the mail manager is responsible for receiving notifications on
- * the longpoll bus, which are data received from the server.
- */
-var MailManager = require('mail.Manager');
-var MailFailure = require('mail.model.MailFailure');
-
-const config = require('web.config');
-var core = require('web.core');
-var session = require('web.session');
-
-var _t = core._t;
-
-MailManager.include({
-
- //--------------------------------------------------------------------------
- // Private
- //--------------------------------------------------------------------------
-
- /**
- * Remove channel notifications if there is an unsubscribe notification
- * on this channel.
- *
- * @private
- * @param {Object[]} notifications
- * @returns {Object[]} notifications filtered of channel notifications
- * matching unsubscribe notifs
- */
- _filterNotificationsOnUnsubscribe: function (notifications) {
- var unsubscribedNotif = _.find(notifications, function (notif) {
- return notif[1].info === 'unsubscribe';
- });
- if (unsubscribedNotif) {
- notifications = _.reject(notifications, function (notif) {
- return notif[0][1] === 'mail.channel' &&
- notif[0][2] === unsubscribedNotif[1].id;
- });
- }
- return notifications;
- },
- /**
- * Called when receiving a notification on a channel (all members of a
- * channel receive this notification)
- *
- * @private
- * @param {Object} params
- * @param {integer} [params.channelID]
- * @param {Object} params.data
- * @param {string} [params.data.info] if set, specify the type of data on
- * this channel notification
- */
- _handleChannelNotification: function (params) {
- if (params.data && params.data.info === 'typing_status') {
- this._handleChannelTypingNotification(params.channelID, params.data);
- } else if (params.data && params.data.info === 'channel_fetched') {
- this._handleChannelFetchedNotification(params.channelID, params.data);
- } else if (params.data && params.data.info === 'channel_seen') {
- this._handleChannelSeenNotification(params.channelID, params.data);
- } else {
- this._handleChannelMessageNotification(params.data);
- }
- },
- /**
- * Called when a channel has been fetched, and the server responses with the
- * last message fetched. Useful in order to track last message fetched.
- *
- * @private
- * @param {integer} channelID
- * @param {Object} data
- * @param {string} data.info 'channel_fetched'
- * @param {integer} data.last_message_id
- * @param {integer} data.partner_id
- */
- _handleChannelFetchedNotification: function (channelID, data) {
- var channel = this.getChannel(channelID);
- if (!channel) {
- return;
- }
- if (channel.hasSeenFeature()) {
- channel.updateSeenPartnersInfo(data);
- }
- },
- /**
- * Called when a new or updated message is received on a channel
- *
- * @private
- * @param {Object} messageData
- * @param {integer[]} messageData.channel_ids channel IDs of this message
- * (note that 'pending moderation' messages in moderated channels do not
- * have the moderated channels in this array).
- */
- _handleChannelMessageNotification: function (messageData) {
- var self = this;
- var def;
- let notify = true;
- if (messageData.channel_ids.length === 1) {
- const channel = this.getChannel(messageData.channel_ids[0]);
- // Message from mailing channel should not make a notification in Odoo for users
- // with notification "Handled by Email", but web client should receive the message.
- // Channel has been marked as read server-side in this case, so it should not display
- // a notification by incrementing the unread counter.
- notify = channel && (!channel.isMassMailing() || session.notification_type !== 'email');
- def = this.joinChannel(messageData.channel_ids[0], { autoswitch: false });
- } else {
- def = Promise.resolve();
- }
- def.then(function () {
- // don't increment unread if channel wasn't in cache yet as
- // its unread counter has just been fetched
- return self.addMessage(messageData, {
- showNotification: true,
- incrementUnread: notify,
- });
- });
- },
- /**
- * Called when a channel has been seen, and the server responses with the
- * last message seen. Useful in order to track last message seen.
- *
- * @private
- * @param {integer} channelID
- * @param {Object} data
- * @param {string} data.info 'channel_seen'
- * @param {integer} data.last_message_id
- * @param {integer} data.partner_id
- */
- _handleChannelSeenNotification: function (channelID, data) {
- var channel = this.getChannel(channelID);
- if (!channel) {
- return;
- }
- if (channel.hasSeenFeature()) {
- channel.updateSeenPartnersInfo(data);
- }
- if (session.partner_id !== data.partner_id) {
- return;
- }
- channel.setLastSeenMessageID(data.last_message_id);
- if (channel.hasUnreadMessages()) {
- channel.resetUnreadCounter();
- }
- },
- /**
- * Called when someone starts or stops typing a message in a channel
- *
- * @private
- * @param {integer} channelID
- * @param {Object} typingData
- * @param {boolean} typingData.is_typing
- * @param {boolean} typingData.is_website_user
- * @param {integer} typingData.partner_id
- */
- _handleChannelTypingNotification: function (channelID, typingData) {
- var isWebsiteUser = typingData.is_website_user;
- var partnerID = typingData.partner_id;
- if (partnerID === session.partner_id && !isWebsiteUser) {
- return; // ignore typing notification of myself
- }
- var channel = this.getChannel(channelID);
- if (!channel) {
- return;
- }
- var typingID = {
- isWebsiteUser: typingData.is_website_user,
- partnerID: typingData.partner_id,
- };
- if (typingData.is_typing) {
- channel.registerTyping(typingID);
- } else {
- channel.unregisterTyping(typingID);
- }
- },
- /**
- * On message becoming a need action (pinned to inbox)
- *
- * @private
- * @param {Object} messageData
- * @param {integer[]} messageData.channel_ids
- */
- _handleNeedactionNotification: function (messageData) {
- var self = this;
- var inbox = this.getMailbox('inbox');
- this.addMessage(messageData, {
- incrementUnread: true,
- showNotification: true,
- }).then(function (message) {
- inbox.incrementMailboxCounter();
- _.each(message.getThreadIDs(), function (threadID) {
- var channel = self.getChannel(threadID);
- if (channel) {
- channel.incrementNeedactionCounter();
- }
- });
- self._mailBus.trigger('update_needaction', inbox.getMailboxCounter());
- });
- },
- /**
- * Called when an activity record has been updated on the server
- *
- * @private
- * @param {Object} data key, value to decide activity created or deleted
- */
- _handlePartnerActivityUpdateNotification: function (data) {
- this._mailBus.trigger('activity_updated', data);
- },
- /**
- * Called to open the channel in detach mode (minimized) even if no new message:
- *
- * @private
- * @param {Object} channelData
- * @param {integer} channelData.id
- * @param {string} [channelData.info]
- * @param {boolean} channelData.is_minimized
- * @param {string} channelData.state
- */
- _handlePartnerChannelMinimizeNotification: function (channelData) {
- var self = this;
- this._addChannel(channelData).then(function (channelID){
- self.getChannel(channelID).detach()
- });
- },
- /**
- * Called when receiving a channel state as a partner notification:
- *
- * - if it is a new channel, it means we have been invited to this channel
- * - if it is an existing channel, it means the window state of the channel
- * may have changed (as this state is stored server-side)
- *
- * @private
- * @param {Object} channelData
- * @param {integer} channelData.id
- * @param {string} [channelData.info]
- * @param {boolean} channelData.is_minimized
- * @param {string} channelData.state
- */
- _handlePartnerChannelNotification: function (channelData) {
- if (
- (channelData.channel_type === 'channel') &&
- (channelData.state === 'open')
- ) {
- // invited to a new channel
- this._addChannel(channelData, { autoswitch: false });
- if (
- !channelData.is_minimized &&
- channelData.info !== 'creation'
- ) {
- this.do_notify(
- false,
- _t("You have been invited to: ") + channelData.name);
- }
- }
- // Prevent to open/close a channel on mobile when you open/close it on desktop.
- if (!config.device.isMobile) {
- const channel = this.getChannel(channelData.id);
- if (channel && channelData.info !== 'join') {
- channel.updateWindowState({
- folded: channelData.state === 'folded' ? true : false,
- detached: channelData.is_minimized,
- });
- }
- }
- },
- /**
- * Called when receiving a multi_user_channel seen notification. Only
- * the current user is notified. This must be handled as if this is a
- * channel seen notification.
- *
- * Note that this is a 'res.partner' notification because only the current
- * user is notified on channel seen. This is a consequence from disabling
- * the seen feature on multi_user_channel, because we still need to get
- * the last seen message ID in order to display the "New Messages" separator
- * in Discuss.
- *
- * @private
- * @param {Object} data
- * @param {integer} data.channel_id
- * @param {string} data.info 'channel_seen'
- * @param {integer} data.last_message_id
- * @param {integer} data.partner_id
- */
- _handlePartnerChannnelSeenNotification: function (data) {
- this._handleChannelSeenNotification(data.channel_id, data);
- },
- /**
- * Add or remove failure when receiving a failure update message
- *
- * @private
- * @param {Object} datas
- * @param {Object[]} datas.elements list of mail failure data
- * @param {string} datas.elements[].message_id ID of related message that
- * has a mail failure.
- * @param {Array} datas.elements[].notifications list of notifications
- * that is related to a mail failure.
- * @param {string} datas.elements[].notifications[0] sending state of a mail
- * failure (e.g. 'exception').
- */
- _handlePartnerMessageNotificationUpdateNotification(datas) {
- var self = this;
- _.each(datas.elements, function (data) {
- var isNewFailure = _.some(data.notifications, function (notif) {
- return notif[0] === 'exception' || notif[0] === 'bounce';
- });
- var matchedFailure = _.find(self._mailFailures, function (failure) {
- return failure.getMessageID() === data.message_id;
- });
- if (matchedFailure) {
- var index = _.findIndex(self._mailFailures, matchedFailure);
- if (isNewFailure) {
- self._mailFailures[index] = new MailFailure(self, data);
- } else {
- self._mailFailures.splice(index, 1);
- }
- } else if (isNewFailure) {
- self._mailFailures.push(new MailFailure(self, data));
- }
- var message = _.find(self._messages, function (msg) {
- return msg.getID() === data.message_id;
- });
- if (message) {
- message.setNotifications(data.notifications);
- self._mailBus.trigger('update_message', message);
- }
- });
- this._mailBus.trigger('update_needaction', this.needactionCounter);
- },
- /**
- * Updates mailbox_inbox when a message has marked as read.
- *
- * @private
- * @param {Object} data
- * @param {integer[]} [data.channel_ids]
- * @param {integer[]} [data.message_ids]
- * @param {string} [data.type]
- */
- _handlePartnerMarkAsReadNotification: function (data) {
- var self = this;
- var history = this.getMailbox('history');
- _.each(data.message_ids, function (messageID) {
- var message = _.find(self._messages, function (msg) {
- return msg.getID() === messageID;
- });
- if (message) {
- self._removeMessageFromThread('mailbox_inbox', message);
- history.addMessage(message);
- self._mailBus.trigger('update_message', message, data.type);
- }
- });
- if (data.channel_ids) {
- _.each(data.channel_ids, function (channelID) {
- var channel = self.getChannel(channelID);
- if (channel) {
- channel.decrementNeedactionCounter(data.message_ids.length, 0);
- }
- });
- } else {
- // if no channel_ids specified, this is a 'mark all read' in inbox
- _.each(this.getChannels(), function (channel) {
- channel.resetNeedactionCounter();
- });
- }
- var inbox = this.getMailbox('inbox');
- inbox.decrementMailboxCounter(data.message_ids.length);
- this._mailBus.trigger('update_needaction', inbox.getMailboxCounter());
- },
- /**
- * On receiving a message made by the current user, on a moderated channel,
- * which is pending moderation.
- *
- * @private
- * @param {Object} data
- * @param {Object} data.message server-side data of the message
- */
- _handlePartnerMessageAuthorNotification: function (data) {
- this.addMessage(data.message);
- },
- /**
- * Notification to delete several messages locally
- * Useful when a pending moderation message has been rejected, so that
- * this message should not be displayed anymore.
- *
- * @private
- * @param {Object} data
- * @param {Object[]} [data.message_ids] IDs of messages to delete locally.
- */
- _handlePartnerMessageDeletionNotification: function (data) {
- var self = this;
- _.each(data.message_ids, function (messageID) {
- var message = self.getMessage(messageID);
- if (message) {
- message.setModerationStatus('rejected');
- }
- });
- },
- /**
- * On receiving a message pending moderation, and current user is moderator
- * of such message.
- *
- * @param {Object} data notification data
- * @param {Object} data.message data of message
- */
- _handlePartnerMessageModeratorNotification: function (data) {
- var self = this;
- this.addMessage(data.message).then(function () {
- self._mailBus.trigger('update_moderation_counter');
- });
- },
- /**
- * On receiving a notification that is specific to a user
- *
- * @private
- * @param {Object} data structure depending on the type
- * @param {integer} data.id
- */
- _handlePartnerNotification: function (data) {
- if (data.info === 'unsubscribe') {
- this._handlePartnerUnsubscribeNotification(data);
- } else if (data.type === 'toggle_star') {
- this._handlePartnerToggleStarNotification(data);
- } else if (data.type === 'mark_as_read') {
- this._handlePartnerMarkAsReadNotification(data);
- } else if (data.type === 'moderator') {
- this._handlePartnerMessageModeratorNotification(data);
- } else if (data.type === 'author') {
- this._handlePartnerMessageAuthorNotification(data);
- } else if (data.type === 'deletion') {
- this._handlePartnerMessageDeletionNotification(data);
- } else if (data.info === 'transient_message') {
- this._handlePartnerTransientMessageNotification(data);
- } else if (data.type === 'activity_updated') {
- this._handlePartnerActivityUpdateNotification(data);
- } else if (data.type === 'message_notification_update') {
- this._handlePartnerMessageNotificationUpdateNotification(data);
- } else if (data.type === 'user_connection') {
- this._handlePartnerUserConnectionNotification(data);
- } else if (data.info === 'channel_seen') {
- this._handlePartnerChannnelSeenNotification(data);
- } else if (data.type === 'simple_notification') {
- var title = _.escape(data.title), message = _.escape(data.message);
- data.warning ? this.do_warn(title, message, data.sticky) : this.do_notify(title, message, data.sticky);
- } else if (data.info === 'channel_minimize') {
- this._handlePartnerChannelMinimizeNotification(data);
- } else {
- this._handlePartnerChannelNotification(data);
- }
- },
- /**
- * On toggling on or off the star status of one or several messages.
- * As the information is stored server-side, the web client must adapt
- * itself from server's data on the messages.
- *
- * @private
- * @param {Object} data
- * @param {integer[]} data.message_ids IDs of messages that have a change
- * of their starred status.
- * @param {boolean} data.starred states whether the messages with id in
- * `data.message_ids` have become either starred or unstarred
- */
- _handlePartnerToggleStarNotification: function (data) {
- var self = this;
- var starred = this.getMailbox('starred');
- _.each(data.message_ids, function (messageID) {
- var message = _.find(self._messages, function (msg) {
- return msg.getID() === messageID;
- });
- if (message) {
- message.setStarred(data.starred);
- if (!message.isStarred()) {
- self._removeMessageFromThread('mailbox_starred', message);
- } else {
- self._addMessageToThreads(message, []);
- var channelStarred = self.getMailbox('starred');
- channelStarred.invalidateCaches();
- }
- self._mailBus.trigger('update_message', message);
- }
- });
-
- if (data.starred) {
- // increase starred counter if message is marked as star
- starred.incrementMailboxCounter(data.message_ids.length);
- } else {
- // decrease starred counter if message is remove from starred
- // if unstar_all then it will set to 0.
- starred.decrementMailboxCounter(data.message_ids.length);
- }
-
- this._mailBus.trigger('update_starred', starred.getMailboxCounter());
- },
- /**
- * On receiving a transient message, i.e. a message which does not come from
- * a member of the channel. Usually a log message, such as one generated
- * from a command with ('/').
- *
- * @private
- * @param {Object} data
- */
- _handlePartnerTransientMessageNotification: function (data) {
- var lastMessage = _.last(this._messages);
- data.id = (lastMessage ? lastMessage.getID() : 0) + 0.01;
- data.author_id = this.getOdoobotID();
- this.addMessage(data);
- },
- /**
- * On receiving a unsubscribe from channel notification, confirm
- * unsubscription from channel and adapt screen accordingly.
- *
- * @private
- * @param {Object} data
- * @param {Object} data.id ID of the unsubscribed channel
- */
- _handlePartnerUnsubscribeNotification: function (data) {
- var channel = this.getChannel(data.id);
- if (channel) {
- var message;
- if (_.contains(['public', 'private'], channel.getType())) {
- message = _.str.sprintf(
- _t("You unsubscribed from <b>%s</b>."),
- channel.getName()
- );
- } else {
- message = _.str.sprintf(
- _t("You unpinned your conversation with <b>%s</b>."),
- channel.getName()
- );
- }
- this._removeChannel(channel);
- this._mailBus.trigger('unsubscribe_from_channel', data.id);
- this.do_notify(false, message);
- }
- },
- /**
- * Shows a popup to notify a user connection
- *
- * @private
- * @param {Object} data
- * @param {Object[]} data.partner_id id of the connected partner
- * @param {string} data.title title to display on notification
- * @param {Array} data.messages message to display on notification
- */
- _handlePartnerUserConnectionNotification: function (data) {
- var self = this;
- var partnerID = data.partner_id;
- this.call('bus_service', 'sendNotification', data.title, data.message, function ( ){
- self.call('mail_service', 'openDMChatWindowFromBlankThreadWindow', partnerID);
- });
- },
- /**
- * @override
- * @private
- */
- _listenOnBuses: function () {
- this._super.apply(this, arguments);
- this.call('bus_service', 'onNotification', this, this._onNotification);
- },
-
- //--------------------------------------------------------------------------
- // Handlers
- //--------------------------------------------------------------------------
-
- /**
- * Notification handlers
- * Sometimes, the web client receives unsubscribe notification and an extra
- * notification on that channel. This is then followed by an attempt to
- * rejoin the channel that we just left. The next few lines remove the
- * extra notification to prevent that situation to occur.
- *
- * @private
- * @param {Array} notifs
- */
- _onNotification: function (notifs) {
- var self = this;
- notifs = this._filterNotificationsOnUnsubscribe(notifs);
- _.each(notifs, function (notif) {
- var model = notif[0][1];
- if (model === 'ir.needaction') {
- self._handleNeedactionNotification(notif[1]);
- } else if (model === 'mail.channel') {
- // new message in a channel
- self._handleChannelNotification({
- channelID: notif[0][2],
- data: notif[1],
- });
- } else if (model === 'res.partner') {
- self._handlePartnerNotification(notif[1]);
- }
- });
- },
-});
-
-return MailManager;
-
-});