[IMP] bus, *: add single notification subscription
*: 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) <seb@odoo.com>
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -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)
|
||||
);
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
@@ -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();
|
||||
},
|
||||
|
||||
@@ -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"]);
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
+104
-122
@@ -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: {
|
||||
|
||||
@@ -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;
|
||||
},
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
@@ -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);
|
||||
@@ -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,
|
||||
};
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user