[FIX] mail, web: properly add messaging menu to the systray
This commit intends to fix two strange bugs encountered while trying to push a new component into the systray menu: - MessagingMenuWidget and RtcActivityNoticeWidget were removing their parent node via DOM manipulations when attached in the DOM. This caused a crash when adding other items to the systray menu since the deleted nodes were actually managed by OWL. - The t-foreach directive in the navbar used indexes as the t-key, which led mapping items subsequently added to wrong templates. closes odoo/odoo#78784 X-original-commit: 5f0f80da4fc951954d253c9e45defd591605f556 Related: odoo/enterprise#21829 Signed-off-by: Sébastien Theys (seb) <seb@odoo.com> Signed-off-by: Ooming <oomsveta@users.noreply.github.com>
This commit is contained in:
+6
-3
@@ -15,15 +15,17 @@ QUnit.module('chat_window_manager_tests.js', {
|
||||
beforeEach(this);
|
||||
|
||||
this.start = async params => {
|
||||
const { afterEvent, env, widget } = await start(Object.assign(
|
||||
{ hasChatWindow: true, hasMessagingMenu: true },
|
||||
const res = await start(Object.assign(
|
||||
{ hasChatWindow: true },
|
||||
params,
|
||||
{ data: this.data }
|
||||
));
|
||||
const { afterEvent, env, widget } = res;
|
||||
this.debug = params && params.debug;
|
||||
this.afterEvent = afterEvent;
|
||||
this.env = env;
|
||||
this.widget = widget;
|
||||
return res;
|
||||
};
|
||||
},
|
||||
afterEach() {
|
||||
@@ -48,7 +50,8 @@ QUnit.test('closing a chat window with no message from admin side unpins it', as
|
||||
uuid: 'channel-10-uuid',
|
||||
},
|
||||
);
|
||||
await this.start();
|
||||
const { createMessagingMenuComponent } = await this.start();
|
||||
await createMessagingMenuComponent();
|
||||
|
||||
await afterNextRender(() => document.querySelector(`.o_MessagingMenu_toggler`).click());
|
||||
await afterNextRender(() => document.querySelector(`.o_NotificationList_preview`).click());
|
||||
|
||||
+5
-3
@@ -15,12 +15,13 @@ QUnit.module('messaging_menu_tests.js', {
|
||||
beforeEach(this);
|
||||
|
||||
this.start = async params => {
|
||||
let { env, widget } = await start(Object.assign({}, params, {
|
||||
const res = await start(Object.assign({}, params, {
|
||||
data: this.data,
|
||||
hasMessagingMenu: true,
|
||||
}));
|
||||
const { env, widget } = res;
|
||||
this.env = env;
|
||||
this.widget = widget;
|
||||
return res;
|
||||
};
|
||||
},
|
||||
afterEach() {
|
||||
@@ -38,7 +39,8 @@ QUnit.test('livechats should be in "chat" filter', async function (assert) {
|
||||
livechat_operator_id: this.data.currentPartnerId,
|
||||
members: [this.data.currentPartnerId, this.data.publicPartnerId],
|
||||
});
|
||||
await this.start();
|
||||
const { createMessagingMenuComponent } = await this.start();
|
||||
await createMessagingMenuComponent();
|
||||
assert.containsOnce(
|
||||
document.body,
|
||||
'.o_MessagingMenu',
|
||||
|
||||
+48
-24
@@ -21,15 +21,17 @@ QUnit.module('chat_window_manager_tests.js', {
|
||||
beforeEach(this);
|
||||
|
||||
this.start = async params => {
|
||||
const { afterEvent, env, widget } = await start(Object.assign(
|
||||
{ hasChatWindow: true, hasMessagingMenu: true },
|
||||
const res = await start(Object.assign(
|
||||
{ hasChatWindow: true },
|
||||
params,
|
||||
{ data: this.data }
|
||||
));
|
||||
const { afterEvent, env, widget } = res;
|
||||
this.debug = params && params.debug;
|
||||
this.afterEvent = afterEvent;
|
||||
this.env = env;
|
||||
this.widget = widget;
|
||||
return res;
|
||||
};
|
||||
},
|
||||
afterEach() {
|
||||
@@ -84,7 +86,8 @@ QUnit.test('initial mount', async function (assert) {
|
||||
QUnit.test('chat window new message: basic rendering', async function (assert) {
|
||||
assert.expect(10);
|
||||
|
||||
await this.start();
|
||||
const { createMessagingMenuComponent } = await this.start();
|
||||
await createMessagingMenuComponent();
|
||||
await afterNextRender(() =>
|
||||
document.querySelector(`.o_MessagingMenu_toggler`).click()
|
||||
);
|
||||
@@ -146,7 +149,8 @@ QUnit.test('chat window new message: basic rendering', async function (assert) {
|
||||
QUnit.test('chat window new message: focused on open [REQUIRE FOCUS]', async function (assert) {
|
||||
assert.expect(2);
|
||||
|
||||
await this.start();
|
||||
const { createMessagingMenuComponent } = await this.start();
|
||||
await createMessagingMenuComponent();
|
||||
await afterNextRender(() =>
|
||||
document.querySelector(`.o_MessagingMenu_toggler`).click()
|
||||
);
|
||||
@@ -167,7 +171,8 @@ QUnit.test('chat window new message: focused on open [REQUIRE FOCUS]', async fun
|
||||
QUnit.test('chat window new message: close', async function (assert) {
|
||||
assert.expect(1);
|
||||
|
||||
await this.start();
|
||||
const { createMessagingMenuComponent } = await this.start();
|
||||
await createMessagingMenuComponent();
|
||||
await afterNextRender(() =>
|
||||
document.querySelector(`.o_MessagingMenu_toggler`).click()
|
||||
);
|
||||
@@ -187,7 +192,8 @@ QUnit.test('chat window new message: close', async function (assert) {
|
||||
QUnit.test('chat window new message: fold', async function (assert) {
|
||||
assert.expect(6);
|
||||
|
||||
await this.start();
|
||||
const { createMessagingMenuComponent } = await this.start();
|
||||
await createMessagingMenuComponent();
|
||||
await afterNextRender(() => document.querySelector(`.o_MessagingMenu_toggler`).click());
|
||||
await afterNextRender(() =>
|
||||
document.querySelector(`.o_MessagingMenu_newMessageButton`).click()
|
||||
@@ -251,7 +257,7 @@ QUnit.test('open chat from "new message" chat window should open chat in place o
|
||||
{ is_minimized: true },
|
||||
);
|
||||
const imSearchDef = makeDeferred();
|
||||
await this.start({
|
||||
const { createMessagingMenuComponent } = await this.start({
|
||||
env: {
|
||||
browser: {
|
||||
innerWidth: 1920,
|
||||
@@ -265,6 +271,7 @@ QUnit.test('open chat from "new message" chat window should open chat in place o
|
||||
return res;
|
||||
}
|
||||
});
|
||||
await createMessagingMenuComponent();
|
||||
assert.containsN(
|
||||
document.body,
|
||||
'.o_ChatWindow',
|
||||
@@ -365,7 +372,7 @@ QUnit.test('new message chat window should close on selecting the user if chat w
|
||||
state: 'open',
|
||||
});
|
||||
const imSearchDef = makeDeferred();
|
||||
await this.start({
|
||||
const { createMessagingMenuComponent } = await this.start({
|
||||
async mockRPC(route, args) {
|
||||
const res = await this._super(...arguments);
|
||||
if (args.method === 'im_search') {
|
||||
@@ -374,6 +381,7 @@ QUnit.test('new message chat window should close on selecting the user if chat w
|
||||
return res;
|
||||
},
|
||||
});
|
||||
await createMessagingMenuComponent();
|
||||
|
||||
// open "new message" chat window
|
||||
await afterNextRender(() => document.querySelector(`.o_MessagingMenu_toggler`).click());
|
||||
@@ -411,7 +419,7 @@ QUnit.test('new message autocomplete should automatically select first result',
|
||||
this.data['res.partner'].records.push({ id: 131, name: "Partner 131" });
|
||||
this.data['res.users'].records.push({ partner_id: 131 });
|
||||
const imSearchDef = makeDeferred();
|
||||
await this.start({
|
||||
const { createMessagingMenuComponent } = await this.start({
|
||||
async mockRPC(route, args) {
|
||||
const res = await this._super(...arguments);
|
||||
if (args.method === 'im_search') {
|
||||
@@ -420,6 +428,7 @@ QUnit.test('new message autocomplete should automatically select first result',
|
||||
return res;
|
||||
},
|
||||
});
|
||||
await createMessagingMenuComponent();
|
||||
|
||||
// open "new message" chat window
|
||||
await afterNextRender(() =>
|
||||
@@ -453,7 +462,8 @@ QUnit.test('chat window: basic rendering', async function (assert) {
|
||||
// channel that is expected to be found in the messaging menu
|
||||
// with random unique id and name that will be asserted during the test
|
||||
this.data['mail.channel'].records.push({ id: 20, name: "General" });
|
||||
await this.start();
|
||||
const { createMessagingMenuComponent } = await this.start();
|
||||
await createMessagingMenuComponent();
|
||||
|
||||
await afterNextRender(() =>
|
||||
document.querySelector(`.o_MessagingMenu_toggler`).click()
|
||||
@@ -543,7 +553,7 @@ QUnit.test('chat window: fold', async function (assert) {
|
||||
// channel that is expected to be found in the messaging menu
|
||||
// with random UUID, will be asserted during the test
|
||||
this.data['mail.channel'].records.push({ uuid: 'channel-uuid' });
|
||||
await this.start({
|
||||
const { createMessagingMenuComponent } = await this.start({
|
||||
mockRPC(route, args) {
|
||||
if (args.method === 'channel_fold') {
|
||||
assert.step(`rpc:${args.method}/${args.kwargs.state}`);
|
||||
@@ -551,6 +561,7 @@ QUnit.test('chat window: fold', async function (assert) {
|
||||
return this._super(...arguments);
|
||||
},
|
||||
});
|
||||
await createMessagingMenuComponent();
|
||||
// Open Thread
|
||||
await afterNextRender(() => document.querySelector(`.o_MessagingMenu_toggler`).click());
|
||||
await afterNextRender(() =>
|
||||
@@ -597,7 +608,7 @@ QUnit.test('chat window: open / close', async function (assert) {
|
||||
// channel that is expected to be found in the messaging menu
|
||||
// with random UUID, will be asserted during the test
|
||||
this.data['mail.channel'].records.push({ uuid: 'channel-uuid' });
|
||||
await this.start({
|
||||
const { createMessagingMenuComponent } = await this.start({
|
||||
mockRPC(route, args) {
|
||||
if (args.method === 'channel_fold') {
|
||||
assert.step(`rpc:channel_fold/${args.kwargs.state}`);
|
||||
@@ -605,6 +616,7 @@ QUnit.test('chat window: open / close', async function (assert) {
|
||||
return this._super(...arguments);
|
||||
},
|
||||
});
|
||||
await createMessagingMenuComponent();
|
||||
assert.containsNone(
|
||||
document.body,
|
||||
'.o_ChatWindow',
|
||||
@@ -659,13 +671,14 @@ QUnit.test('Mobile: opening a chat window should not update channel state on the
|
||||
id: 20,
|
||||
state: 'closed',
|
||||
});
|
||||
await this.start({
|
||||
const { createMessagingMenuComponent } = await this.start({
|
||||
env: {
|
||||
device: {
|
||||
isMobile: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
await createMessagingMenuComponent();
|
||||
await afterNextRender(() => document.querySelector(`.o_MessagingMenu_toggler`).click());
|
||||
await afterNextRender(() => document.querySelector(`.o_NotificationList_preview`).click());
|
||||
assert.containsOnce(
|
||||
@@ -692,13 +705,14 @@ QUnit.test('Mobile: closing a chat window should not update channel state on the
|
||||
id: 20,
|
||||
state: 'open',
|
||||
});
|
||||
await this.start({
|
||||
const { createMessagingMenuComponent } = await this.start({
|
||||
env: {
|
||||
device: {
|
||||
isMobile: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
await createMessagingMenuComponent();
|
||||
await afterNextRender(() => document.querySelector(`.o_MessagingMenu_toggler`).click());
|
||||
await afterNextRender(() => document.querySelector(`.o_NotificationList_preview`).click());
|
||||
assert.containsOnce(
|
||||
@@ -916,7 +930,8 @@ QUnit.test('chat window: composer state conservation on toggle discuss', async f
|
||||
// channel that is expected to be found in the messaging menu
|
||||
// with random unique id that is needed to link messages
|
||||
this.data['mail.channel'].records.push({ id: 20 });
|
||||
await this.start();
|
||||
const { createMessagingMenuComponent } = await this.start();
|
||||
await createMessagingMenuComponent();
|
||||
await afterNextRender(() => document.querySelector(`.o_MessagingMenu_toggler`).click());
|
||||
await afterNextRender(() =>
|
||||
document.querySelector(`.o_MessagingMenu_dropdownMenu .o_NotificationList_preview`).click()
|
||||
@@ -990,7 +1005,8 @@ QUnit.test('chat window: scroll conservation on toggle discuss', async function
|
||||
res_id: 20,
|
||||
});
|
||||
}
|
||||
await this.start();
|
||||
const { createMessagingMenuComponent } = await this.start();
|
||||
await createMessagingMenuComponent();
|
||||
await afterNextRender(() => document.querySelector(`.o_MessagingMenu_toggler`).click());
|
||||
await this.afterEvent({
|
||||
eventName: 'o-component-message-list-scrolled',
|
||||
@@ -1065,13 +1081,14 @@ QUnit.test('open 2 different chat windows: enough screen width [REQUIRE FOCUS]',
|
||||
// 2 channels are expected to be found in the messaging menu, each with a
|
||||
// random unique id that will be referenced in the test
|
||||
this.data['mail.channel'].records.push({ id: 10 }, { id: 20 });
|
||||
await this.start({
|
||||
const { createMessagingMenuComponent } = await this.start({
|
||||
env: {
|
||||
browser: {
|
||||
innerWidth: 1920, // enough to fit at least 2 chat windows
|
||||
},
|
||||
},
|
||||
});
|
||||
await createMessagingMenuComponent();
|
||||
await afterNextRender(() => document.querySelector(`.o_MessagingMenu_toggler`).click());
|
||||
await afterNextRender(() =>
|
||||
document.querySelector(`
|
||||
@@ -1184,7 +1201,8 @@ QUnit.test('open 2 chat windows: check shift operations are available', async fu
|
||||
// 2 channels are expected to be found in the messaging menu
|
||||
// only their existence matters, data are irrelevant
|
||||
this.data['mail.channel'].records.push({}, {});
|
||||
await this.start();
|
||||
const { createMessagingMenuComponent } = await this.start();
|
||||
await createMessagingMenuComponent();
|
||||
|
||||
await afterNextRender(() => {
|
||||
document.querySelector('.o_MessagingMenu_toggler').click();
|
||||
@@ -1410,13 +1428,14 @@ QUnit.test('open 3 different chat windows: not enough screen width', async funct
|
||||
// 3 channels are expected to be found in the messaging menu, each with a
|
||||
// random unique id that will be referenced in the test
|
||||
this.data['mail.channel'].records.push({ id: 1 }, { id: 2 }, { id: 3 });
|
||||
await this.start({
|
||||
const { createMessagingMenuComponent } = await this.start({
|
||||
env: {
|
||||
browser: {
|
||||
innerWidth: 900, // enough to fit 2 chat windows but not 3
|
||||
},
|
||||
},
|
||||
});
|
||||
await createMessagingMenuComponent();
|
||||
|
||||
// open, from systray menu, chat windows of channels with Id 1, 2, then 3
|
||||
await afterNextRender(() =>
|
||||
@@ -1554,7 +1573,8 @@ QUnit.test('chat window: switch on TAB', async function (assert) {
|
||||
{ id: 1, name: "channel1" },
|
||||
{ id: 2, name: "channel2" }
|
||||
);
|
||||
await this.start();
|
||||
const { createMessagingMenuComponent } = await this.start();
|
||||
await createMessagingMenuComponent();
|
||||
|
||||
await afterNextRender(() =>
|
||||
document.querySelector(`.o_MessagingMenu_toggler`).click()
|
||||
@@ -1759,7 +1779,8 @@ QUnit.test('chat window with a thread: keep scroll position in message list on f
|
||||
res_id: 20,
|
||||
});
|
||||
}
|
||||
await this.start();
|
||||
const { createMessagingMenuComponent } = await this.start();
|
||||
await createMessagingMenuComponent();
|
||||
await afterNextRender(() => document.querySelector(`.o_MessagingMenu_toggler`).click());
|
||||
await this.afterEvent({
|
||||
eventName: 'o-component-message-list-scrolled',
|
||||
@@ -1872,13 +1893,14 @@ QUnit.test('chat window: post message on non-mailing channel with "CTRL-Enter" k
|
||||
id: 20,
|
||||
is_minimized: true,
|
||||
});
|
||||
await this.start({
|
||||
const { createMessagingMenuComponent } = await this.start({
|
||||
env: {
|
||||
device: {
|
||||
isMobile: true, // here isMobile is used for the small screen size, not actually for the mobile devices
|
||||
},
|
||||
},
|
||||
});
|
||||
await createMessagingMenuComponent();
|
||||
|
||||
await afterNextRender(() => document.querySelector(`.o_MessagingMenu_toggler`).click());
|
||||
await afterNextRender(() =>
|
||||
@@ -1913,7 +1935,8 @@ QUnit.test('chat window with a thread: keep scroll position in message list on t
|
||||
res_id: 20,
|
||||
});
|
||||
}
|
||||
await this.start();
|
||||
const { createMessagingMenuComponent } = await this.start();
|
||||
await createMessagingMenuComponent();
|
||||
await afterNextRender(() => document.querySelector(`.o_MessagingMenu_toggler`).click());
|
||||
await this.afterEvent({
|
||||
eventName: 'o-component-message-list-scrolled',
|
||||
@@ -1983,7 +2006,8 @@ QUnit.test('chat window with a thread: keep scroll position in message list on t
|
||||
res_id: 20,
|
||||
});
|
||||
}
|
||||
await this.start();
|
||||
const { createMessagingMenuComponent } = await this.start();
|
||||
await createMessagingMenuComponent();
|
||||
await afterNextRender(() => document.querySelector(`.o_MessagingMenu_toggler`).click());
|
||||
await this.afterEvent({
|
||||
eventName: 'o-component-message-list-scrolled',
|
||||
|
||||
@@ -30,6 +30,16 @@ export class MessagingMenu extends Component {
|
||||
*/
|
||||
_constructor() {}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
setup() {
|
||||
// for now, the legacy env is needed for internal functions such as
|
||||
// `useModels` to work
|
||||
this.env = owl.Component.env;
|
||||
super.setup();
|
||||
}
|
||||
|
||||
mounted() {
|
||||
document.addEventListener('click', this._onClickCaptureGlobal, true);
|
||||
}
|
||||
|
||||
@@ -18,13 +18,14 @@ QUnit.module('messaging_menu_tests.js', {
|
||||
beforeEach(this);
|
||||
|
||||
this.start = async params => {
|
||||
let { discussWidget, env, widget } = await start(Object.assign({}, params, {
|
||||
const res = await start(Object.assign({}, params, {
|
||||
data: this.data,
|
||||
hasMessagingMenu: true,
|
||||
}));
|
||||
const { discussWidget, env, widget } = res;
|
||||
this.discussWidget = discussWidget;
|
||||
this.env = env;
|
||||
this.widget = widget;
|
||||
return res;
|
||||
};
|
||||
},
|
||||
afterEach() {
|
||||
@@ -44,10 +45,11 @@ QUnit.test('[technical] messaging not created then becomes created', async funct
|
||||
assert.expect(2);
|
||||
|
||||
const messagingBeforeCreationDeferred = makeTestPromise();
|
||||
await this.start({
|
||||
const { createMessagingMenuComponent } = await this.start({
|
||||
messagingBeforeCreationDeferred,
|
||||
waitUntilMessagingCondition: 'none',
|
||||
});
|
||||
await createMessagingMenuComponent();
|
||||
assert.containsOnce(
|
||||
document.body,
|
||||
'.o_MessagingMenu',
|
||||
@@ -78,10 +80,11 @@ QUnit.test('[technical] no crash on attempting opening messaging menu when messa
|
||||
*/
|
||||
assert.expect(2);
|
||||
|
||||
await this.start({
|
||||
const { createMessagingMenuComponent } = await this.start({
|
||||
messagingBeforeCreationDeferred: new Promise(() => {}), // keep messaging not created
|
||||
waitUntilMessagingCondition: 'none',
|
||||
});
|
||||
await createMessagingMenuComponent();
|
||||
assert.containsOnce(
|
||||
document.body,
|
||||
'.o_MessagingMenu',
|
||||
@@ -107,7 +110,7 @@ QUnit.test('[technical] no crash on attempting opening messaging menu when messa
|
||||
QUnit.test('messaging not initialized', async function (assert) {
|
||||
assert.expect(2);
|
||||
|
||||
await this.start({
|
||||
const { createMessagingMenuComponent } = await this.start({
|
||||
async mockRPC(route) {
|
||||
if (route === '/mail/init_messaging') {
|
||||
// simulate messaging never initialized
|
||||
@@ -117,6 +120,7 @@ QUnit.test('messaging not initialized', async function (assert) {
|
||||
},
|
||||
waitUntilMessagingCondition: 'created',
|
||||
});
|
||||
await createMessagingMenuComponent();
|
||||
assert.strictEqual(
|
||||
document.querySelectorAll('.o_MessagingMenu_loading').length,
|
||||
1,
|
||||
@@ -136,7 +140,7 @@ QUnit.test('messaging becomes initialized', async function (assert) {
|
||||
|
||||
const messagingInitializedProm = makeTestPromise();
|
||||
|
||||
await this.start({
|
||||
const { createMessagingMenuComponent } = await this.start({
|
||||
async mockRPC(route) {
|
||||
const _super = this._super.bind(this, ...arguments); // limitation of class.js
|
||||
if (route === '/mail/init_messaging') {
|
||||
@@ -146,6 +150,7 @@ QUnit.test('messaging becomes initialized', async function (assert) {
|
||||
},
|
||||
waitUntilMessagingCondition: 'created',
|
||||
});
|
||||
await createMessagingMenuComponent();
|
||||
await afterNextRender(() => document.querySelector(`.o_MessagingMenu_toggler`).click());
|
||||
|
||||
// simulate messaging becomes initialized
|
||||
@@ -164,7 +169,8 @@ QUnit.test('messaging becomes initialized', async function (assert) {
|
||||
QUnit.test('basic rendering', async function (assert) {
|
||||
assert.expect(21);
|
||||
|
||||
await this.start();
|
||||
const { createMessagingMenuComponent } = await this.start();
|
||||
await createMessagingMenuComponent();
|
||||
assert.strictEqual(
|
||||
document.querySelectorAll('.o_MessagingMenu').length,
|
||||
1,
|
||||
@@ -303,7 +309,8 @@ QUnit.test('counter is taking into account failure notification', async function
|
||||
mail_message_id: 11, // id of the related message
|
||||
notification_status: 'exception', // necessary value to have a failure
|
||||
});
|
||||
await this.start();
|
||||
const { createMessagingMenuComponent } = await this.start();
|
||||
await createMessagingMenuComponent();
|
||||
|
||||
assert.containsOnce(
|
||||
document.body,
|
||||
@@ -320,7 +327,8 @@ QUnit.test('counter is taking into account failure notification', async function
|
||||
QUnit.test('switch tab', async function (assert) {
|
||||
assert.expect(15);
|
||||
|
||||
await this.start();
|
||||
const { createMessagingMenuComponent } = await this.start();
|
||||
await createMessagingMenuComponent();
|
||||
|
||||
await afterNextRender(() => document.querySelector(`.o_MessagingMenu_toggler`).click());
|
||||
assert.strictEqual(
|
||||
@@ -427,9 +435,10 @@ QUnit.test('switch tab', async function (assert) {
|
||||
QUnit.test('new message', async function (assert) {
|
||||
assert.expect(3);
|
||||
|
||||
await this.start({
|
||||
const { createMessagingMenuComponent } = await this.start({
|
||||
hasChatWindow: true,
|
||||
});
|
||||
await createMessagingMenuComponent();
|
||||
|
||||
await afterNextRender(() =>
|
||||
document.querySelector(`.o_MessagingMenu_toggler`).click()
|
||||
@@ -456,10 +465,11 @@ QUnit.test('new message', async function (assert) {
|
||||
QUnit.test('no new message when discuss is open', async function (assert) {
|
||||
assert.expect(3);
|
||||
|
||||
await this.start({
|
||||
const { createMessagingMenuComponent } = await this.start({
|
||||
autoOpenDiscuss: true,
|
||||
hasDiscuss: true,
|
||||
});
|
||||
await createMessagingMenuComponent();
|
||||
|
||||
await afterNextRender(() =>
|
||||
document.querySelector(`.o_MessagingMenu_toggler`).click()
|
||||
@@ -506,7 +516,8 @@ QUnit.test('channel preview: basic rendering', async function (assert) {
|
||||
model: 'mail.channel', // necessary to link message to channel
|
||||
res_id: 20, // id of related channel
|
||||
});
|
||||
await this.start();
|
||||
const { createMessagingMenuComponent } = await this.start();
|
||||
await createMessagingMenuComponent();
|
||||
|
||||
await afterNextRender(() => document.querySelector(`.o_MessagingMenu_toggler`).click());
|
||||
assert.strictEqual(
|
||||
@@ -603,7 +614,8 @@ QUnit.test('filtered previews', async function (assert) {
|
||||
res_id: 20, // id of related channel
|
||||
},
|
||||
);
|
||||
await this.start();
|
||||
const { createMessagingMenuComponent } = await this.start();
|
||||
await createMessagingMenuComponent();
|
||||
|
||||
await afterNextRender(() =>
|
||||
document.querySelector(`.o_MessagingMenu_toggler`).click()
|
||||
@@ -754,9 +766,10 @@ QUnit.test('open chat window from preview', async function (assert) {
|
||||
|
||||
// channel expected to be found in the menu, only its existence matters, data are irrelevant
|
||||
this.data['mail.channel'].records.push({});
|
||||
await this.start({
|
||||
const { createMessagingMenuComponent } = await this.start({
|
||||
hasChatWindow: true,
|
||||
});
|
||||
await createMessagingMenuComponent();
|
||||
|
||||
await afterNextRender(() =>
|
||||
document.querySelector(`.o_MessagingMenu_toggler`).click()
|
||||
@@ -780,7 +793,8 @@ QUnit.test('no code injection in message body preview', async function (assert)
|
||||
model: "mail.channel",
|
||||
res_id: 11,
|
||||
});
|
||||
await this.start();
|
||||
const { createMessagingMenuComponent } = await this.start();
|
||||
await createMessagingMenuComponent();
|
||||
|
||||
await afterNextRender(() => {
|
||||
document.querySelector(`.o_MessagingMenu_toggler`).click();
|
||||
@@ -822,7 +836,8 @@ QUnit.test('no code injection in message body preview from sanitized message', a
|
||||
model: "mail.channel",
|
||||
res_id: 11,
|
||||
});
|
||||
await this.start();
|
||||
const { createMessagingMenuComponent } = await this.start();
|
||||
await createMessagingMenuComponent();
|
||||
|
||||
await afterNextRender(() => {
|
||||
document.querySelector(`.o_MessagingMenu_toggler`).click();
|
||||
@@ -864,7 +879,8 @@ QUnit.test('<br/> tags in message body preview are transformed in spaces', async
|
||||
model: "mail.channel",
|
||||
res_id: 11,
|
||||
});
|
||||
await this.start();
|
||||
const { createMessagingMenuComponent } = await this.start();
|
||||
await createMessagingMenuComponent();
|
||||
|
||||
await afterNextRender(() => {
|
||||
document.querySelector(`.o_MessagingMenu_toggler`).click();
|
||||
@@ -894,7 +910,7 @@ QUnit.test('<br/> tags in message body preview are transformed in spaces', async
|
||||
QUnit.test('rendering with OdooBot has a request (default)', async function (assert) {
|
||||
assert.expect(4);
|
||||
|
||||
await this.start({
|
||||
const { createMessagingMenuComponent } = await this.start({
|
||||
env: {
|
||||
browser: {
|
||||
Notification: {
|
||||
@@ -903,6 +919,7 @@ QUnit.test('rendering with OdooBot has a request (default)', async function (ass
|
||||
},
|
||||
},
|
||||
});
|
||||
await createMessagingMenuComponent();
|
||||
|
||||
assert.ok(
|
||||
document.querySelector('.o_MessagingMenu_counter'),
|
||||
@@ -932,7 +949,7 @@ QUnit.test('rendering with OdooBot has a request (default)', async function (ass
|
||||
QUnit.test('rendering without OdooBot has a request (denied)', async function (assert) {
|
||||
assert.expect(2);
|
||||
|
||||
await this.start({
|
||||
const { createMessagingMenuComponent } = await this.start({
|
||||
env: {
|
||||
browser: {
|
||||
Notification: {
|
||||
@@ -941,6 +958,7 @@ QUnit.test('rendering without OdooBot has a request (denied)', async function (a
|
||||
},
|
||||
},
|
||||
});
|
||||
await createMessagingMenuComponent();
|
||||
|
||||
assert.containsNone(
|
||||
document.body,
|
||||
@@ -961,7 +979,7 @@ QUnit.test('rendering without OdooBot has a request (denied)', async function (a
|
||||
QUnit.test('rendering without OdooBot has a request (accepted)', async function (assert) {
|
||||
assert.expect(2);
|
||||
|
||||
await this.start({
|
||||
const { createMessagingMenuComponent } = await this.start({
|
||||
env: {
|
||||
browser: {
|
||||
Notification: {
|
||||
@@ -970,6 +988,7 @@ QUnit.test('rendering without OdooBot has a request (accepted)', async function
|
||||
},
|
||||
},
|
||||
});
|
||||
await createMessagingMenuComponent();
|
||||
|
||||
assert.containsNone(
|
||||
document.body,
|
||||
@@ -990,7 +1009,7 @@ QUnit.test('rendering without OdooBot has a request (accepted)', async function
|
||||
QUnit.test('respond to notification prompt (denied)', async function (assert) {
|
||||
assert.expect(4);
|
||||
|
||||
await this.start({
|
||||
const { createMessagingMenuComponent } = await this.start({
|
||||
env: {
|
||||
browser: {
|
||||
Notification: {
|
||||
@@ -1012,6 +1031,7 @@ QUnit.test('respond to notification prompt (denied)', async function (assert) {
|
||||
}
|
||||
},
|
||||
});
|
||||
await createMessagingMenuComponent();
|
||||
|
||||
await afterNextRender(() =>
|
||||
document.querySelector('.o_MessagingMenu_toggler').click()
|
||||
@@ -1047,7 +1067,8 @@ QUnit.test('Group chat should be displayed inside the chat section of the messag
|
||||
channel_type: 'group',
|
||||
is_pinned: true,
|
||||
});
|
||||
await this.start();
|
||||
const { createMessagingMenuComponent } = await this.start();
|
||||
await createMessagingMenuComponent();
|
||||
|
||||
await afterNextRender(() =>
|
||||
document.querySelector('.o_MessagingMenu_toggler').click()
|
||||
|
||||
@@ -3,28 +3,30 @@
|
||||
|
||||
<t t-name="mail.NotificationRequest" owl="1">
|
||||
<div class="o_NotificationRequest" t-on-click="_onClick">
|
||||
<div class="o_NotificationRequest_sidebar">
|
||||
<div class="o_NotificationRequest_imageContainer o_NotificationRequest_sidebarItem">
|
||||
<img class="o_NotificationRequest_image rounded-circle" src="/mail/static/src/img/odoobot.png" alt="Avatar of OdooBot"/>
|
||||
<PartnerImStatusIcon
|
||||
class="o_NotificationRequest_partnerImStatusIcon"
|
||||
t-att-class="{ 'o-mobile': messaging.device.isMobile }"
|
||||
partnerLocalId="messaging.partnerRoot.localId"
|
||||
/>
|
||||
<t t-if="messaging">
|
||||
<div class="o_NotificationRequest_sidebar">
|
||||
<div class="o_NotificationRequest_imageContainer o_NotificationRequest_sidebarItem">
|
||||
<img class="o_NotificationRequest_image rounded-circle" src="/mail/static/src/img/odoobot.png" alt="Avatar of OdooBot"/>
|
||||
<PartnerImStatusIcon
|
||||
class="o_NotificationRequest_partnerImStatusIcon"
|
||||
t-att-class="{ 'o-mobile': messaging.device.isMobile }"
|
||||
partnerLocalId="messaging.partnerRoot.localId"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="o_NotificationRequest_content">
|
||||
<div class="o_NotificationRequest_header">
|
||||
<span class="o_NotificationRequest_name text-truncate" t-att-class="{ 'o-mobile': messaging.device.isMobile }">
|
||||
<t t-esc="getHeaderText()"/>
|
||||
</span>
|
||||
<div class="o_NotificationRequest_content">
|
||||
<div class="o_NotificationRequest_header">
|
||||
<span class="o_NotificationRequest_name text-truncate" t-att-class="{ 'o-mobile': messaging.device.isMobile }">
|
||||
<t t-esc="getHeaderText()"/>
|
||||
</span>
|
||||
</div>
|
||||
<div class="o_NotificationRequest_core">
|
||||
<span class="o_NotificationRequest_coreItem o_NotificationRequest_inlineText text-truncate">
|
||||
Enable desktop notifications to chat.
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="o_NotificationRequest_core">
|
||||
<span class="o_NotificationRequest_coreItem o_NotificationRequest_inlineText text-truncate">
|
||||
Enable desktop notifications to chat.
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
|
||||
@@ -6,6 +6,16 @@ const { Component } = owl;
|
||||
|
||||
export class RtcActivityNotice extends Component {
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
setup() {
|
||||
// for now, the legacy env is needed for internal functions such as
|
||||
// `useModels` to work
|
||||
this.env = owl.Component.env;
|
||||
super.setup();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Handlers
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
+15
-17
@@ -25,12 +25,14 @@ QUnit.module('thread_needaction_preview_tests.js', {
|
||||
};
|
||||
|
||||
this.start = async params => {
|
||||
const { afterEvent, env, widget } = await start(Object.assign({}, params, {
|
||||
const res = await start(Object.assign({}, params, {
|
||||
data: this.data,
|
||||
}));
|
||||
const { afterEvent, env, widget } = res;
|
||||
this.afterEvent = afterEvent;
|
||||
this.env = env;
|
||||
this.widget = widget;
|
||||
return res;
|
||||
};
|
||||
},
|
||||
afterEach() {
|
||||
@@ -54,9 +56,8 @@ QUnit.test('mark as read', async function (assert) {
|
||||
notification_type: 'inbox',
|
||||
res_partner_id: this.data.currentPartnerId,
|
||||
});
|
||||
await this.start({
|
||||
const { createMessagingMenuComponent } = await this.start({
|
||||
hasChatWindow: true,
|
||||
hasMessagingMenu: true,
|
||||
async mockRPC(route, args) {
|
||||
if (route.includes('mark_all_as_read')) {
|
||||
assert.step('mark_all_as_read');
|
||||
@@ -72,6 +73,7 @@ QUnit.test('mark as read', async function (assert) {
|
||||
return this._super(...arguments);
|
||||
},
|
||||
});
|
||||
await createMessagingMenuComponent();
|
||||
await afterNextRender(() => this.afterEvent({
|
||||
eventName: 'o-thread-cache-loaded-messages',
|
||||
func: () => document.querySelector('.o_MessagingMenu_toggler').click(),
|
||||
@@ -116,9 +118,8 @@ QUnit.test('click on preview should mark as read and open the thread', async fun
|
||||
notification_type: 'inbox',
|
||||
res_partner_id: this.data.currentPartnerId,
|
||||
});
|
||||
await this.start({
|
||||
const { createMessagingMenuComponent } = await this.start({
|
||||
hasChatWindow: true,
|
||||
hasMessagingMenu: true,
|
||||
async mockRPC(route, args) {
|
||||
if (route.includes('mark_all_as_read')) {
|
||||
assert.step('mark_all_as_read');
|
||||
@@ -134,6 +135,7 @@ QUnit.test('click on preview should mark as read and open the thread', async fun
|
||||
return this._super(...arguments);
|
||||
},
|
||||
});
|
||||
await createMessagingMenuComponent();
|
||||
await afterNextRender(() => this.afterEvent({
|
||||
eventName: 'o-thread-cache-loaded-messages',
|
||||
func: () => document.querySelector('.o_MessagingMenu_toggler').click(),
|
||||
@@ -197,11 +199,11 @@ QUnit.test('click on expand from chat window should close the chat window and op
|
||||
notification_type: 'inbox',
|
||||
res_partner_id: this.data.currentPartnerId,
|
||||
});
|
||||
await this.start({
|
||||
const { createMessagingMenuComponent } = await this.start({
|
||||
env: { bus },
|
||||
hasChatWindow: true,
|
||||
hasMessagingMenu: true,
|
||||
});
|
||||
await createMessagingMenuComponent();
|
||||
await afterNextRender(() => this.afterEvent({
|
||||
eventName: 'o-thread-cache-loaded-messages',
|
||||
func: () => document.querySelector('.o_MessagingMenu_toggler').click(),
|
||||
@@ -261,9 +263,8 @@ QUnit.test('[technical] opening a non-channel chat window should not call channe
|
||||
notification_type: 'inbox',
|
||||
res_partner_id: this.data.currentPartnerId,
|
||||
});
|
||||
await this.start({
|
||||
const { createMessagingMenuComponent } = await this.start({
|
||||
hasChatWindow: true,
|
||||
hasMessagingMenu: true,
|
||||
async mockRPC(route, args) {
|
||||
if (route.includes('channel_fold')) {
|
||||
const message = "should not call channel_fold when opening a non-channel chat window";
|
||||
@@ -274,6 +275,7 @@ QUnit.test('[technical] opening a non-channel chat window should not call channe
|
||||
return this._super(...arguments);
|
||||
},
|
||||
});
|
||||
await createMessagingMenuComponent();
|
||||
await afterNextRender(() => this.afterEvent({
|
||||
eventName: 'o-thread-cache-loaded-messages',
|
||||
func: () => document.querySelector('.o_MessagingMenu_toggler').click(),
|
||||
@@ -332,10 +334,8 @@ QUnit.test('preview should display last needaction message preview even if there
|
||||
notification_type: 'inbox',
|
||||
res_partner_id: this.data.currentPartnerId,
|
||||
});
|
||||
await this.start({
|
||||
hasChatWindow: true,
|
||||
hasMessagingMenu: true,
|
||||
});
|
||||
const { createMessagingMenuComponent } = await this.start({ hasChatWindow: true });
|
||||
await createMessagingMenuComponent();
|
||||
await afterNextRender(() => this.afterEvent({
|
||||
eventName: 'o-thread-cache-loaded-messages',
|
||||
func: () => document.querySelector('.o_MessagingMenu_toggler').click(),
|
||||
@@ -375,10 +375,8 @@ QUnit.test('chat window header should not have unread counter for non-channel th
|
||||
notification_type: 'inbox',
|
||||
res_partner_id: this.data.currentPartnerId,
|
||||
});
|
||||
await this.start({
|
||||
hasChatWindow: true,
|
||||
hasMessagingMenu: true,
|
||||
});
|
||||
const { createMessagingMenuComponent } = await this.start({ hasChatWindow: true });
|
||||
await createMessagingMenuComponent();
|
||||
await afterNextRender(() => this.afterEvent({
|
||||
eventName: 'o-thread-cache-loaded-messages',
|
||||
func: () => document.querySelector('.o_MessagingMenu_toggler').click(),
|
||||
|
||||
@@ -3,27 +3,15 @@
|
||||
import { ChatWindowService } from '@mail/services/chat_window_service/chat_window_service';
|
||||
import { DialogService } from '@mail/services/dialog_service/dialog_service';
|
||||
import { MessagingService } from '@mail/services/messaging/messaging';
|
||||
import { SystrayService } from '@mail/services/systray_service/systray_service';
|
||||
import { DiscussWidget } from '@mail/widgets/discuss/discuss';
|
||||
import { MessagingMenuWidget } from '@mail/widgets/messaging_menu/messaging_menu';
|
||||
|
||||
import { action_registry } from 'web.core';
|
||||
import { serviceRegistry } from 'web.core';
|
||||
import SystrayMenu from 'web.SystrayMenu';
|
||||
|
||||
serviceRegistry.add('chat_window', ChatWindowService);
|
||||
serviceRegistry.add('dialog', DialogService);
|
||||
serviceRegistry.add('messaging', MessagingService);
|
||||
serviceRegistry.add('systray_service', SystrayService);
|
||||
|
||||
action_registry.add('mail.widgets.discuss', DiscussWidget);
|
||||
|
||||
// Systray menu items display order matches order in the list
|
||||
// lower index comes first, and display is from right to left.
|
||||
// For messagin menu, it should come before activity menu, if any
|
||||
// otherwise, it is the next systray item.
|
||||
const activityMenuIndex = SystrayMenu.Items.findIndex(SystrayMenuItem =>
|
||||
SystrayMenuItem.prototype.name === 'activity_menu');
|
||||
if (activityMenuIndex > 0) {
|
||||
SystrayMenu.Items.splice(activityMenuIndex, 0, MessagingMenuWidget);
|
||||
} else {
|
||||
SystrayMenu.Items.push(MessagingMenuWidget);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import { getMessagingComponent } from '@mail/utils/messaging_component';
|
||||
|
||||
import AbstractService from 'web.AbstractService';
|
||||
import { registry } from '@web/core/registry';
|
||||
|
||||
const systrayRegistry = registry.category('systray');
|
||||
|
||||
export const SystrayService = AbstractService.extend({
|
||||
dependencies: ['messaging'],
|
||||
/**
|
||||
* @override {web.AbstractService}
|
||||
*/
|
||||
async start() {
|
||||
await owl.Component.env.services.messaging.modelManager.messagingCreatedPromise;
|
||||
systrayRegistry.add('mail.MessagingMenu', {
|
||||
Component: getMessagingComponent('MessagingMenu'),
|
||||
});
|
||||
systrayRegistry.add('mail.RtcActivityNotice', {
|
||||
Component: getMessagingComponent('RtcActivityNotice'),
|
||||
});
|
||||
},
|
||||
});
|
||||
@@ -14,7 +14,6 @@ import { DialogService } from '@mail/services/dialog_service/dialog_service';
|
||||
import { getMessagingComponent } from '@mail/utils/messaging_component';
|
||||
import { nextTick } from '@mail/utils/utils';
|
||||
import { DiscussWidget } from '@mail/widgets/discuss/discuss';
|
||||
import { MessagingMenuWidget } from '@mail/widgets/messaging_menu/messaging_menu';
|
||||
import { MockModels } from '@mail/../tests/helpers/mock_models';
|
||||
|
||||
import AbstractStorageService from 'web.AbstractStorageService';
|
||||
@@ -154,33 +153,6 @@ function _useDiscuss(callbacks) {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {Object} callbacks
|
||||
* @param {function[]} callbacks.init
|
||||
* @param {function[]} callbacks.mount
|
||||
* @param {function[]} callbacks.destroy
|
||||
* @param {function[]} callbacks.return
|
||||
* @returns {Object} update callbacks
|
||||
*/
|
||||
function _useMessagingMenu(callbacks) {
|
||||
const {
|
||||
mount: prevMount,
|
||||
return: prevReturn,
|
||||
} = callbacks;
|
||||
let messagingMenuWidget;
|
||||
return Object.assign({}, callbacks, {
|
||||
mount: prevMount.concat(async ({ selector, widget }) => {
|
||||
messagingMenuWidget = new MessagingMenuWidget(widget, {});
|
||||
await messagingMenuWidget.appendTo($(selector));
|
||||
await messagingMenuWidget.on_attach_callback();
|
||||
}),
|
||||
return: prevReturn.concat(result => {
|
||||
Object.assign(result, { messagingMenuWidget });
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Public: rendering timers
|
||||
//------------------------------------------------------------------------------
|
||||
@@ -463,6 +435,15 @@ function getCreateMessageComponent({ components, env, modelManager, widget }) {
|
||||
};
|
||||
}
|
||||
|
||||
function getCreateMessagingMenuComponent({ components, env, widget }) {
|
||||
return async function createMessagingMenuComponent() {
|
||||
await createRootMessagingComponent({ components, env }, 'MessagingMenu', {
|
||||
props: {},
|
||||
target: widget.el,
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
function getCreateThreadViewComponent({ afterEvent, components, env, widget }) {
|
||||
return async function createThreadViewComponent(threadView, otherProps = {}, { isFixedSize = false, waitUntilMessagesLoaded = true } = {}) {
|
||||
let target;
|
||||
@@ -524,8 +505,6 @@ function getCreateThreadViewComponent({ afterEvent, components, env, widget }) {
|
||||
* @param {boolean} [param0.hasChatWindow=false] if set, mount chat window
|
||||
* service.
|
||||
* @param {boolean} [param0.hasDiscuss=false] if set, mount discuss app.
|
||||
* @param {boolean} [param0.hasMessagingMenu=false] if set, mount messaging
|
||||
* menu.
|
||||
* @param {boolean} [param0.hasTimeControl=false] if set, all flow of time
|
||||
* with `env.browser.setTimeout` are fully controlled by test itself.
|
||||
* @see addTimeControlToEnv that adds `advanceTime` function in
|
||||
@@ -581,7 +560,6 @@ async function start(param0 = {}) {
|
||||
hasChatWindow = false,
|
||||
hasDialog = false,
|
||||
hasDiscuss = false,
|
||||
hasMessagingMenu = false,
|
||||
hasTimeControl = false,
|
||||
hasView = false,
|
||||
loadingBaseDelayDuration = 0,
|
||||
@@ -596,7 +574,6 @@ async function start(param0 = {}) {
|
||||
delete param0.hasWebClient;
|
||||
delete param0.hasChatWindow;
|
||||
delete param0.hasDiscuss;
|
||||
delete param0.hasMessagingMenu;
|
||||
delete param0.hasTimeControl;
|
||||
delete param0.hasView;
|
||||
if (hasChatWindow) {
|
||||
@@ -608,9 +585,6 @@ async function start(param0 = {}) {
|
||||
if (hasDiscuss) {
|
||||
callbacks = _useDiscuss(callbacks);
|
||||
}
|
||||
if (hasMessagingMenu) {
|
||||
callbacks = _useMessagingMenu(callbacks);
|
||||
}
|
||||
const messagingBus = new EventBus();
|
||||
const {
|
||||
init: initCallbacks,
|
||||
@@ -814,6 +788,7 @@ async function start(param0 = {}) {
|
||||
createComposerComponent: getCreateComposerComponent({ components, env: testEnv, modelManager, widget }),
|
||||
createComposerSuggestionComponent: getCreateComposerSuggestionComponent({ components, env: testEnv, modelManager, widget }),
|
||||
createMessageComponent: getCreateMessageComponent({ components, env: testEnv, modelManager, widget }),
|
||||
createMessagingMenuComponent: getCreateMessagingMenuComponent({ components, env: testEnv, widget }),
|
||||
createThreadViewComponent: getCreateThreadViewComponent({ afterEvent, components, env: testEnv, widget }),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import { getMessagingComponent } from "@mail/utils/messaging_component";
|
||||
|
||||
import Widget from 'web.Widget';
|
||||
|
||||
/**
|
||||
* Odoo Widget, necessary to instantiate component.
|
||||
*/
|
||||
export const MessagingMenuWidget = Widget.extend({
|
||||
template: 'mail.widgets.MessagingMenu',
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
init() {
|
||||
this._super(...arguments);
|
||||
this.component = undefined;
|
||||
},
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
destroy() {
|
||||
if (this.component) {
|
||||
this.component.destroy();
|
||||
}
|
||||
this._super(...arguments);
|
||||
},
|
||||
async on_attach_callback() {
|
||||
const MessagingMenu = getMessagingComponent("MessagingMenu");
|
||||
this.component = new MessagingMenu(null);
|
||||
await this.component.mount(this.el);
|
||||
// unwrap
|
||||
this.el.parentNode.insertBefore(this.component.el, this.el);
|
||||
this.el.parentNode.removeChild(this.el);
|
||||
},
|
||||
});
|
||||
@@ -1,8 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<templates xml:space="preserve">
|
||||
|
||||
<t t-name="mail.widgets.MessagingMenu">
|
||||
<li class="o_widget_SystrayMessagingItem"/>
|
||||
</t>
|
||||
|
||||
</templates>
|
||||
@@ -1,40 +0,0 @@
|
||||
/** @odoo-module **/
|
||||
|
||||
import { getMessagingComponent } from '@mail/utils/messaging_component';
|
||||
|
||||
import SystrayMenu from 'web.SystrayMenu';
|
||||
import Widget from 'web.Widget';
|
||||
|
||||
/**
|
||||
* Odoo Widget, necessary to instantiate component.
|
||||
*/
|
||||
export const RtcActivityNoticeWidget = Widget.extend({
|
||||
template: 'mail.widgets.RtcActivityNotice',
|
||||
sequence: 99, // position in the systray, right to left.
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
init() {
|
||||
this._super(...arguments);
|
||||
this.component = undefined;
|
||||
},
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
destroy() {
|
||||
if (this.component) {
|
||||
this.component.destroy();
|
||||
}
|
||||
this._super(...arguments);
|
||||
},
|
||||
async on_attach_callback() {
|
||||
const RtcActivityNotice = getMessagingComponent('RtcActivityNotice');
|
||||
this.component = new RtcActivityNotice(null);
|
||||
await this.component.mount(this.el);
|
||||
// unwrap
|
||||
this.el.parentNode.insertBefore(this.component.el, this.el);
|
||||
this.el.parentNode.removeChild(this.el);
|
||||
},
|
||||
});
|
||||
|
||||
SystrayMenu.Items.push(RtcActivityNoticeWidget);
|
||||
@@ -1,8 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<templates xml:space="preserve">
|
||||
|
||||
<t t-name="mail.widgets.RtcActivityNotice">
|
||||
<div class="o_widget_RtcActivityNotice"/>
|
||||
</t>
|
||||
|
||||
</templates>
|
||||
@@ -234,9 +234,8 @@ QUnit.test('activity menu widget: activity view icon', async function (assert) {
|
||||
QUnit.test('activity menu widget: close on messaging menu click', async function (assert) {
|
||||
assert.expect(2);
|
||||
|
||||
const { widget } = await start({
|
||||
const { createMessagingMenuComponent, widget } = await start({
|
||||
data: this.data,
|
||||
hasMessagingMenu: true,
|
||||
async mockRPC(route, args) {
|
||||
if (args.method === 'systray_get_activities') {
|
||||
return [];
|
||||
@@ -244,6 +243,7 @@ QUnit.test('activity menu widget: close on messaging menu click', async function
|
||||
return this._super(route, args);
|
||||
},
|
||||
});
|
||||
await createMessagingMenuComponent();
|
||||
const activityMenu = new ActivityMenu(widget);
|
||||
await activityMenu.appendTo($('#qunit-fixture'));
|
||||
await testUtils.nextTick();
|
||||
|
||||
@@ -92,7 +92,8 @@ export class NavBar extends Component {
|
||||
|
||||
get systrayItems() {
|
||||
return systrayRegistry
|
||||
.getAll()
|
||||
.getEntries()
|
||||
.map(([key, value]) => ({ key, ...value }))
|
||||
.filter((item) => ("isDisplayed" in item ? item.isDisplayed(this.env) : true))
|
||||
.reverse();
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
|
||||
<!-- Systray -->
|
||||
<div class="o_menu_systray" role="menu">
|
||||
<t t-foreach="systrayItems" t-as="item" t-key="item_index">
|
||||
<t t-foreach="systrayItems" t-as="item" t-key="item.key">
|
||||
<NotUpdatable>
|
||||
<ErrorHandler onError="error => handleItemError(error, item)">
|
||||
<t t-component="item.Component" t-props="item.props" />
|
||||
|
||||
Reference in New Issue
Block a user