From a3525aedbea2b46ab7e128934405f90390a49dd3 Mon Sep 17 00:00:00 2001 From: tsm-odoo Date: Tue, 9 May 2023 10:46:39 +0000 Subject: [PATCH] [IMP] bus, *: add single notification subscription MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit *: calendar, iap_mail, im_livechat, mail. Until now, one had to subscribe to the global "notification" event of the bus service and to loop through each notification to handle the one that was targeted. This PR introduces the `subscribe` method on the bus service in order to subscribe to a single notification type. closes odoo/odoo#120893 Related: odoo/enterprise#40848 Signed-off-by: Sébastien Theys (seb) --- .../src/services/assets_watchdog_service.js | 23 +- addons/bus/static/src/services/bus_service.js | 15 ++ .../static/src/simple_notification_service.js | 12 +- addons/bus/static/tests/bus_tests.js | 16 ++ .../services/calendar_notification_service.js | 8 +- .../js/services/iap_notification_service.js | 14 +- ...ic_livechat_global_notification_handler.js | 226 ++++++++---------- addons/mail/static/src/rtc/rtc_service.js | 36 +-- .../src/core/messaging_service_patch.js | 24 -- .../website_livechat_notification_handler.js | 30 +++ .../tests/helpers/setup_manager_patch.js | 14 ++ 11 files changed, 206 insertions(+), 212 deletions(-) delete mode 100644 addons/website_livechat/static/src/core/messaging_service_patch.js create mode 100644 addons/website_livechat/static/src/core/website_livechat_notification_handler.js create mode 100644 addons/website_livechat/static/tests/helpers/setup_manager_patch.js diff --git a/addons/bus/static/src/services/assets_watchdog_service.js b/addons/bus/static/src/services/assets_watchdog_service.js index e3c18986810..8ac2fda7152 100644 --- a/addons/bus/static/src/services/assets_watchdog_service.js +++ b/addons/bus/static/src/services/assets_watchdog_service.js @@ -11,7 +11,11 @@ export const assetsWatchdogService = { let isNotificationDisplayed = false; let bundleNotifTimerID = null; - bus_service.addEventListener("notification", onNotification.bind(this)); + bus_service.subscribe("bundle_changed", ({ server_version }) => { + if (server_version !== session.server_version) { + displayBundleChangedNotification(); + } + }); bus_service.start(); /** @@ -56,23 +60,6 @@ export const assetsWatchdogService = { function getBundleNotificationDelay() { return 10000 + Math.floor(Math.random() * 50) * 1000; } - - /** - * Reacts to bus's notification - * - * @param {CustomEvent} ev - * @param {Array} [ev.detail] list of received notifications - */ - function onNotification({ detail: notifications }) { - for (const { payload, type } of notifications) { - if (type === "bundle_changed") { - if (payload.server_version !== session.server_version) { - displayBundleChangedNotification(); - break; - } - } - } - } }, }; diff --git a/addons/bus/static/src/services/bus_service.js b/addons/bus/static/src/services/bus_service.js index 469646e61af..33f24d0b180 100644 --- a/addons/bus/static/src/services/bus_service.js +++ b/addons/bus/static/src/services/bus_service.js @@ -25,6 +25,7 @@ export const busService = { async start(env, { multi_tab: multiTab, "bus.parameters": params }) { const bus = new EventBus(); + const notificationBus = new EventBus(); let worker; let isActive = false; let isInitialized = false; @@ -65,6 +66,9 @@ export const busService = { if (type === "notification") { multiTab.setSharedValue("last_notification_id", data[data.length - 1].id); data = data.map((notification) => notification.message); + for (const { type, payload } of data) { + notificationBus.trigger(type, payload); + } } else if (type === "initialized") { isInitialized = true; connectionInitializedDeferred.resolve(); @@ -182,6 +186,17 @@ export const busService = { send("leave"); isActive = false; }, + /** + * Subscribe to a single notification type. + * + * @param {string} notificationType + * @param {function} callback + */ + subscribe(notificationType, callback) { + notificationBus.addEventListener(notificationType, ({ detail }) => + callback(detail) + ); + }, }; }, }; diff --git a/addons/bus/static/src/simple_notification_service.js b/addons/bus/static/src/simple_notification_service.js index b34420b64c8..7d3ffe7c4a4 100644 --- a/addons/bus/static/src/simple_notification_service.js +++ b/addons/bus/static/src/simple_notification_service.js @@ -5,16 +5,8 @@ import { registry } from "@web/core/registry"; export const simpleNotificationService = { dependencies: ["bus_service", "notification"], start(env, { bus_service, notification: notificationService }) { - bus_service.addEventListener("notification", ({ detail: notifications }) => { - for (const { payload, type } of notifications) { - if (type === "simple_notification") { - notificationService.add(payload.message, { - sticky: payload.sticky, - title: payload.title, - type: payload.type, - }); - } - } + bus_service.subscribe("simple_notification", ({ message, sticky, title, type }) => { + notificationService.add(message, { sticky, title, type }); }); bus_service.start(); }, diff --git a/addons/bus/static/tests/bus_tests.js b/addons/bus/static/tests/bus_tests.js index 1da0b8274c0..13d5e231b0e 100644 --- a/addons/bus/static/tests/bus_tests.js +++ b/addons/bus/static/tests/bus_tests.js @@ -676,5 +676,21 @@ QUnit.module( ]); } ); + + QUnit.test("subscribe to single notification", async (assert) => { + const pyEnv = await startServer(); + const env = await makeTestEnv({ activateMockServer: true }); + env.services["bus_service"].start(); + env.services["bus_service"].subscribe("message", (payload) => { + assert.deepEqual({ body: "hello", id: 1 }, payload); + assert.step("message"); + }); + pyEnv["bus.bus"]._sendone("channel1", "message", { + body: "hello", + id: 1, + }); + await nextTick(); + assert.verifySteps(["message"]); + }); } ); diff --git a/addons/calendar/static/src/js/services/calendar_notification_service.js b/addons/calendar/static/src/js/services/calendar_notification_service.js index f8e2ecedd4c..11594cf4aa6 100644 --- a/addons/calendar/static/src/js/services/calendar_notification_service.js +++ b/addons/calendar/static/src/js/services/calendar_notification_service.js @@ -12,12 +12,8 @@ export const calendarNotificationService = { let nextCalendarNotifTimeout = null; const displayedNotifications = new Set(); - bus_service.addEventListener('notification', ({ detail: notifications }) => { - for (const { payload, type } of notifications) { - if (type === "calendar.alarm") { - displayCalendarNotification(payload); - } - } + bus_service.subscribe("calendar.alarm", (payload) => { + displayCalendarNotification(payload); }); bus_service.start(); diff --git a/addons/iap_mail/static/src/js/services/iap_notification_service.js b/addons/iap_mail/static/src/js/services/iap_notification_service.js index 93e99a08157..03c78a86aa9 100644 --- a/addons/iap_mail/static/src/js/services/iap_notification_service.js +++ b/addons/iap_mail/static/src/js/services/iap_notification_service.js @@ -8,15 +8,11 @@ export const iapNotificationService = { dependencies: ["bus_service", "notification"], start(env, { bus_service, notification }) { - bus_service.addEventListener('notification', ({ detail: notifications }) => { - for (const { payload, type } of notifications) { - if (type === 'iap_notification') { - if (payload.error_type == 'success') { - displaySuccessIapNotification(payload); - } else if (payload.error_type == 'danger') { - displayFailureIapNotification(payload); - } - } + bus_service.subscribe("iap_notification", (payload) => { + if (payload.error_type == "success") { + displaySuccessIapNotification(payload); + } else if (payload.error_type == "danger") { + displayFailureIapNotification(payload); } }); bus_service.start(); diff --git a/addons/im_livechat/static/src/legacy/public_models/public_livechat_global_notification_handler.js b/addons/im_livechat/static/src/legacy/public_models/public_livechat_global_notification_handler.js index 15a7453d806..8576e698ac4 100644 --- a/addons/im_livechat/static/src/legacy/public_models/public_livechat_global_notification_handler.js +++ b/addons/im_livechat/static/src/legacy/public_models/public_livechat_global_notification_handler.js @@ -13,134 +13,116 @@ Model({ this.env.services["bus_service"].addChannel( this.messaging.publicLivechatGlobal.publicLivechat.uuid ); - this.env.services["bus_service"].addEventListener("notification", this._onNotification); + this.env.services["bus_service"].subscribe( + "im_livechat.history_command", + this._handleLivechatHistoryCommand + ); + this.env.services["bus_service"].subscribe( + "discuss.channel.member/typing_status", + this._handleTypingStatus + ); + this.env.services["bus_service"].subscribe( + "discuss.channel/new_message", + this._handleNewMessage + ); }, }, recordMethods: { - /** - * @private - * @param {Object} notification - * @param {Object} notification.payload - * @param {string} notification.type - */ - _handleNotification({ payload: notifPayload, type }) { - switch (type) { - case "im_livechat.history_command": { - if (notifPayload.id !== this.messaging.publicLivechatGlobal.publicLivechat.id) { - return; - } - const cookie = getCookie( - this.messaging.publicLivechatGlobal.LIVECHAT_COOKIE_HISTORY - ); - const history = cookie ? JSON.parse(cookie) : []; - session.rpc("/im_livechat/history", { - pid: this.messaging.publicLivechatGlobal.publicLivechat.operator.id, - channel_uuid: this.messaging.publicLivechatGlobal.publicLivechat.uuid, - page_history: history, - }); - return; - } - case "discuss.channel.member/typing_status": { - if ( - !this.messaging.publicLivechatGlobal.chatWindow || - !this.messaging.publicLivechatGlobal.chatWindow.exists() - ) { - return; - } - const channelMemberData = notifPayload; - if ( - channelMemberData.channel.id !== - this.messaging.publicLivechatGlobal.publicLivechat.id - ) { - return; - } - if (!channelMemberData.persona.partner) { - return; - } - if ( - channelMemberData.persona.partner.id === - this.messaging.publicLivechatGlobal.livechatButtonView.currentPartnerId - ) { - // ignore typing display of current partner. - return; - } - if (channelMemberData.isTyping) { - this.messaging.publicLivechatGlobal.publicLivechat.widget.registerTyping({ - partnerID: channelMemberData.persona.partner.id, - }); - } else { - this.messaging.publicLivechatGlobal.publicLivechat.widget.unregisterTyping({ - partnerID: channelMemberData.persona.partner.id, - }); - } - return; - } - case "discuss.channel/new_message": { - if ( - !this.messaging.publicLivechatGlobal.chatWindow || - !this.messaging.publicLivechatGlobal.chatWindow.exists() - ) { - return; - } - if (notifPayload.id !== this.messaging.publicLivechatGlobal.publicLivechat.id) { - return; - } - const notificationData = notifPayload.message; - // If message from notif is already in chatter messages, stop handling - if ( - this.messaging.publicLivechatGlobal.messages.some( - (message) => message.id === notificationData.id - ) - ) { - return; - } - notificationData.body = utils.Markup(notificationData.body); - this.messaging.publicLivechatGlobal.livechatButtonView.addMessage( - notificationData - ); - if ( - this.messaging.publicLivechatGlobal.publicLivechat.isFolded || - !this.messaging.publicLivechatGlobal.chatWindow.publicLivechatView.widget.isAtBottom() - ) { - this.messaging.publicLivechatGlobal.publicLivechat.update({ - unreadCounter: increment(), - }); - } - this.messaging.publicLivechatGlobal.chatWindow.renderMessages(); - return; - } - case "mail.record/insert": { - const { Message: payload } = notifPayload; - if (!payload) { - return; - } - if ( - !this.messaging.publicLivechatGlobal.chatWindow || - !this.messaging.publicLivechatGlobal.chatWindow.exists() - ) { - return; - } - const message = this.messaging.publicLivechatGlobal.messages.find( - (message) => message.id === payload.id - ); - if (!message) { - return; - } - message.widget._body = utils.Markup(payload.body); - this.messaging.publicLivechatGlobal.chatWindow.renderMessages(); - return; - } + _handleLivechatHistoryCommand(notifPayload) { + if (notifPayload.id !== this.messaging.publicLivechatGlobal.publicLivechat.id) { + return; + } + const cookie = getCookie(this.messaging.publicLivechatGlobal.LIVECHAT_COOKIE_HISTORY); + const history = cookie ? JSON.parse(cookie) : []; + session.rpc("/im_livechat/history", { + pid: this.messaging.publicLivechatGlobal.publicLivechat.operator.id, + channel_uuid: this.messaging.publicLivechatGlobal.publicLivechat.uuid, + page_history: history, + }); + }, + _handleTypingStatus(channelMemberData) { + if ( + !this.messaging.publicLivechatGlobal.chatWindow || + !this.messaging.publicLivechatGlobal.chatWindow.exists() + ) { + return; + } + if ( + channelMemberData.channel.id !== + this.messaging.publicLivechatGlobal.publicLivechat.id + ) { + return; + } + if (!channelMemberData.persona.partner) { + return; + } + if ( + channelMemberData.persona.partner.id === + this.messaging.publicLivechatGlobal.livechatButtonView.currentPartnerId + ) { + // ignore typing display of current partner. + return; + } + if (channelMemberData.isTyping) { + this.messaging.publicLivechatGlobal.publicLivechat.widget.registerTyping({ + partnerID: channelMemberData.persona.partner.id, + }); + } else { + this.messaging.publicLivechatGlobal.publicLivechat.widget.unregisterTyping({ + partnerID: channelMemberData.persona.partner.id, + }); } }, - /** - * @private - * @param {CustomEvent} ev - * @param {Array[]} [ev.detail] Notifications coming from the bus. - */ - _onNotification({ detail: notifications }) { - for (const notification of notifications) { - this._handleNotification(notification); + _handleNewMessage(notifPayload) { + if ( + !this.messaging.publicLivechatGlobal.chatWindow || + !this.messaging.publicLivechatGlobal.chatWindow.exists() + ) { + return; } + if (notifPayload.id !== this.messaging.publicLivechatGlobal.publicLivechat.id) { + return; + } + const notificationData = notifPayload.message; + // If message from notif is already in chatter messages, stop handling + if ( + this.messaging.publicLivechatGlobal.messages.some( + (message) => message.id === notificationData.id + ) + ) { + return; + } + notificationData.body = utils.Markup(notificationData.body); + this.messaging.publicLivechatGlobal.livechatButtonView.addMessage(notificationData); + if ( + this.messaging.publicLivechatGlobal.publicLivechat.isFolded || + !this.messaging.publicLivechatGlobal.chatWindow.publicLivechatView.widget.isAtBottom() + ) { + this.messaging.publicLivechatGlobal.publicLivechat.update({ + unreadCounter: increment(), + }); + } + this.messaging.publicLivechatGlobal.chatWindow.renderMessages(); + }, + _handleRecordInsert(notifPayload) { + const { Message: payload } = notifPayload; + if (!payload) { + return; + } + if ( + !this.messaging.publicLivechatGlobal.chatWindow || + !this.messaging.publicLivechatGlobal.chatWindow.exists() + ) { + return; + } + const message = this.messaging.publicLivechatGlobal.messages.find( + (message) => message.id === payload.id + ); + if (!message) { + return; + } + message.widget._body = utils.Markup(payload.body); + this.messaging.publicLivechatGlobal.chatWindow.renderMessages(); }, }, fields: { diff --git a/addons/mail/static/src/rtc/rtc_service.js b/addons/mail/static/src/rtc/rtc_service.js index cd9164c2dc2..fc67dc1207e 100644 --- a/addons/mail/static/src/rtc/rtc_service.js +++ b/addons/mail/static/src/rtc/rtc_service.js @@ -1486,31 +1486,21 @@ export const rtcService = { ], start(env, services) { const rtc = new Rtc(env, services); - services["bus_service"].addEventListener("notification", (notifEvent) => { - for (const notif of notifEvent.detail) { - switch (notif.type) { - case "discuss.channel.rtc.session/peer_notification": - { - const { sender, notifications } = notif.payload; - for (const content of notifications) { - rtc.handleNotification(sender, content); - } - } - break; - case "discuss.channel.rtc.session/ended": - { - const { sessionId } = notif.payload; - if (rtc.state.selfSession?.id === sessionId) { - rtc.endCall(); - services.notification.add( - _t("Disconnected from the RTC call by the server"), - { type: "warning" } - ); - } - } - break; + services["bus_service"].subscribe( + "discuss.channel.rtc.session/peer_notification", + ({ sender, notifications }) => { + for (const content of notifications) { + rtc.handleNotification(sender, content); } } + ); + services["bus_service"].subscribe("discuss.channel.rtc.session/ended", ({ sessionId }) => { + if (rtc.state.selfSession?.id === sessionId) { + rtc.endCall(); + services.notification.add(_t("Disconnected from the RTC call by the server"), { + type: "warning", + }); + } }); return rtc; }, diff --git a/addons/website_livechat/static/src/core/messaging_service_patch.js b/addons/website_livechat/static/src/core/messaging_service_patch.js deleted file mode 100644 index 7f26f04a42f..00000000000 --- a/addons/website_livechat/static/src/core/messaging_service_patch.js +++ /dev/null @@ -1,24 +0,0 @@ -/** @odoo-module */ - -import { Messaging } from "@mail/core/messaging_service"; -import { patch } from "@web/core/utils/patch"; - -patch(Messaging.prototype, "website_livechat", { - handleNotification(notifications) { - this._super(notifications); - for (const notification of notifications) { - if (notification.type === "website_livechat.send_chat_request") { - const channel = this.threadService.insert({ - ...notification.payload, - id: notification.payload.id, - model: "discuss.channel", - serverData: notification.payload, - type: notification.payload.channel.channel_type, - }); - const chatWindow = this.chatWindowService.insert({ thread: channel }); - this.chatWindowService.makeVisible(chatWindow); - this.chatWindowService.focus(chatWindow); - } - } - }, -}); diff --git a/addons/website_livechat/static/src/core/website_livechat_notification_handler.js b/addons/website_livechat/static/src/core/website_livechat_notification_handler.js new file mode 100644 index 00000000000..3407a183ee0 --- /dev/null +++ b/addons/website_livechat/static/src/core/website_livechat_notification_handler.js @@ -0,0 +1,30 @@ +/* @odoo-module */ + +import { registry } from "@web/core/registry"; + +export const websiteLivechatNotifications = { + dependencies: ["bus_service", "mail.chat_window", "mail.thread"], + start( + env, + { + bus_service: busService, + "mail.chat_window": chatWindowService, + "mail.thread": threadService, + } + ) { + busService.subscribe("website_livechat.send_chat_request", (payload) => { + const channel = threadService.insert({ + ...payload, + id: payload.id, + model: "discuss.channel", + serverData: payload, + type: payload.channel.channel_type, + }); + const chatWindow = chatWindowService.insert({ thread: channel }); + chatWindowService.makeVisible(chatWindow); + chatWindowService.focus(chatWindow); + }); + }, +}; + +registry.category("services").add("website_livechat.notifications", websiteLivechatNotifications); diff --git a/addons/website_livechat/static/tests/helpers/setup_manager_patch.js b/addons/website_livechat/static/tests/helpers/setup_manager_patch.js new file mode 100644 index 00000000000..d8aab1ab85f --- /dev/null +++ b/addons/website_livechat/static/tests/helpers/setup_manager_patch.js @@ -0,0 +1,14 @@ +/* @odoo-module */ + +import { setupManager } from "@mail/../tests/helpers/webclient_setup"; +import { patch } from "@web/core/utils/patch"; +import { websiteLivechatNotifications } from "@website_livechat/core/website_livechat_notification_handler"; + +patch(setupManager, "website_livechat", { + setupServices() { + return { + ...this._super(...arguments), + "website_livechat.notifications": websiteLivechatNotifications, + }; + }, +});