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, + }; + }, +});