From 85ec1d37c4548a53468cf1f3bef03fcb613e46ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexandre=20K=C3=BChn?= Date: Mon, 28 Mar 2022 12:27:40 +0000 Subject: [PATCH] [IMP] mail: introduce MessageSeenIndicatorView MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit introduce the MessageSeenIndicatorView model, in order to move business code from MessageSeenIndicator component to MessageSeenIndicatorViews. Code that are not in modelling are hacky, hard to maintain, and very prone to bug. That's why it's important to move most code in components to models. Task-2793280 closes odoo/odoo#88950 Signed-off-by: Alexandre Kühn (aku) --- .../static/src/components/message/message.xml | 8 +- .../message_seen_indicator.js | 29 +- .../message_seen_indicator.xml | 10 +- .../src/models/message_seen_indicator_view.js | 35 +++ addons/mail/static/src/models/message_view.js | 20 ++ .../message_seen_indicator_tests.js | 259 ++++++++++-------- 6 files changed, 213 insertions(+), 148 deletions(-) create mode 100644 addons/mail/static/src/models/message_seen_indicator_view.js diff --git a/addons/mail/static/src/components/message/message.xml b/addons/mail/static/src/components/message/message.xml index b193bce9bca..f614d2958f7 100644 --- a/addons/mail/static/src/components/message/message.xml +++ b/addons/mail/static/src/components/message/message.xml @@ -49,8 +49,8 @@ - - + + @@ -87,8 +87,8 @@ - - - + +
diff --git a/addons/mail/static/src/components/message_seen_indicator/message_seen_indicator.js b/addons/mail/static/src/components/message_seen_indicator/message_seen_indicator.js index d90c486d513..9bfdb84c8c1 100644 --- a/addons/mail/static/src/components/message_seen_indicator/message_seen_indicator.js +++ b/addons/mail/static/src/components/message_seen_indicator/message_seen_indicator.js @@ -10,39 +10,16 @@ export class MessageSeenIndicator extends Component { // Public //-------------------------------------------------------------------------- - /** - * @returns {Message} - */ - get message() { - return this.messaging && this.messaging.models['Message'].get(this.props.messageLocalId); - } - /** * @returns {MessageSeenIndicator} */ - get messageSeenIndicator() { - if (!this.thread || this.thread.model !== 'mail.channel') { - return undefined; - } - return this.messaging.models['MessageSeenIndicator'].findFromIdentifyingData({ - message: this.message, - thread: this.thread, - }); - } - - /** - * @returns {Thread} - */ - get thread() { - return this.messaging && this.messaging.models['Thread'].get(this.props.threadLocalId); + get messageSeenIndicatorView() { + return this.messaging && this.messaging.models['MessageSeenIndicatorView'].get(this.props.localId); } } Object.assign(MessageSeenIndicator, { - props: { - messageLocalId: String, - threadLocalId: String, - }, + props: { localId: String }, template: 'mail.MessageSeenIndicator', }); diff --git a/addons/mail/static/src/components/message_seen_indicator/message_seen_indicator.xml b/addons/mail/static/src/components/message_seen_indicator/message_seen_indicator.xml index 2bbd09530e3..23da1743866 100644 --- a/addons/mail/static/src/components/message_seen_indicator/message_seen_indicator.xml +++ b/addons/mail/static/src/components/message_seen_indicator/message_seen_indicator.xml @@ -2,13 +2,13 @@ - - - - + + + + - + diff --git a/addons/mail/static/src/models/message_seen_indicator_view.js b/addons/mail/static/src/models/message_seen_indicator_view.js new file mode 100644 index 00000000000..4191e6733b7 --- /dev/null +++ b/addons/mail/static/src/models/message_seen_indicator_view.js @@ -0,0 +1,35 @@ +/** @odoo-module **/ + +import { registerModel } from '@mail/model/model_core'; +import { one } from '@mail/model/model_field'; +import { clear, insertAndReplace, replace } from '@mail/model/model_field_command'; + +registerModel({ + name: 'MessageSeenIndicatorView', + identifyingFields: ['messageViewOwner'], + recordMethods: { + /** + * @private + * @returns {FieldCommand} + */ + _computeMessageSeenIndicator() { + if (this.messageViewOwner.threadView && this.messageViewOwner.threadView.thread) { + return insertAndReplace({ + message: replace(this.messageViewOwner.message), + thread: replace(this.messageViewOwner.threadView.thread), + }); + } + return clear(); + }, + }, + fields: { + messageViewOwner: one('MessageView', { + inverse: 'messageSeenIndicatorView', + readonly: true, + required: true, + }), + messageSeenIndicator: one('MessageSeenIndicator', { + compute: '_computeMessageSeenIndicator', + }), + }, +}); diff --git a/addons/mail/static/src/models/message_view.js b/addons/mail/static/src/models/message_view.js index c4821f06238..08530e14506 100644 --- a/addons/mail/static/src/models/message_view.js +++ b/addons/mail/static/src/models/message_view.js @@ -258,6 +258,21 @@ registerModel({ this.message.parentMessage ) ? insertAndReplace() : clear(); }, + /** + * @private + * @retuns {FieldCommand} + */ + _computeMessageSeenIndicatorView() { + if ( + this.message.isCurrentUserOrGuestAuthor && + this.threadView && + this.threadView.thread && + this.threadView.thread.hasSeenIndicators + ) { + return insertAndReplace(); + } + return clear(); + }, }, fields: { /** @@ -384,6 +399,11 @@ registerModel({ isCausal: true, readonly: true, }), + messageSeenIndicatorView: one('MessageSeenIndicatorView', { + compute: '_computeMessageSeenIndicatorView', + inverse: 'messageViewOwner', + isCausal: true, + }), /** * States the thread view that is displaying this messages (if any). */ diff --git a/addons/mail/static/tests/qunit_suite_tests/components/message_seen_indicator_tests.js b/addons/mail/static/tests/qunit_suite_tests/components/message_seen_indicator_tests.js index 9ebba8bb4ad..b304227ae0e 100644 --- a/addons/mail/static/tests/qunit_suite_tests/components/message_seen_indicator_tests.js +++ b/addons/mail/static/tests/qunit_suite_tests/components/message_seen_indicator_tests.js @@ -1,9 +1,9 @@ /** @odoo-module **/ -import { insert, insertAndReplace, link } from '@mail/model/model_field_command'; import { createRootMessagingComponent, start, + startServer, } from '@mail/../tests/helpers/test_utils'; QUnit.module('mail', {}, function () { @@ -26,30 +26,38 @@ QUnit.module('message_seen_indicator_tests.js', { QUnit.test('rendering when just one has received the message', async function (assert) { assert.expect(3); - const { messaging, widget } = await start(); - const thread = messaging.models['Thread'].create({ - id: 1000, - model: 'mail.channel', - partnerSeenInfos: insertAndReplace([ - { - lastFetchedMessage: insert({ id: 100 }), - partner: insertAndReplace({ id: 10 }), - }, - { - partner: insertAndReplace({ id: 100 }), - }, - ]), - messageSeenIndicators: insertAndReplace({ - message: insertAndReplace({ id: 100 }), - }), + const pyEnv = await startServer(); + + const resPartnerId1 = pyEnv['res.partner'].create({ name: "Demo User" }); + const resPartnerId2 = pyEnv['res.partner'].create({ name: "Other User" }); + const mailChannelId = pyEnv['mail.channel'].create({ + channel_last_seen_partner_ids: [ + [0, 0, { partner_id: pyEnv.currentPartnerId }], + [0, 0, { partner_id: resPartnerId1 }], + [0, 0, { partner_id: resPartnerId2 }], + ], + channel_type: 'chat', // only chat channel have seen notification }); - const message = messaging.models['Message'].insert({ - author: insert({ id: messaging.currentPartner.id, display_name: "Demo User" }), + const mailMessageId = pyEnv['mail.message'].create({ + author_id: pyEnv.currentPartnerId, body: "

Test

", - id: 100, - originThread: link(thread), + model: 'mail.channel', + res_id: mailChannelId, }); - await this.createMessageSeenIndicatorComponent({ message, target: widget.el, thread }); + const [cpId1] = pyEnv['mail.channel.partner'].search([['channel_id', '=', mailChannelId], ['partner_id', '=', resPartnerId1]]); + pyEnv['mail.channel.partner'].write([cpId1], { + fetched_message_id: mailMessageId, + seen_message_id: false, + }); + const { openDiscuss } = await start({ + discuss: { + params: { + default_active_id: `mail.channel_${mailChannelId}`, + }, + }, + hasDiscuss: true, + }); + await openDiscuss(); assert.containsOnce( document.body, '.o_MessageSeenIndicator', @@ -70,31 +78,37 @@ QUnit.test('rendering when just one has received the message', async function (a QUnit.test('rendering when everyone have received the message', async function (assert) { assert.expect(3); - const { messaging, widget } = await start(); - const thread = messaging.models['Thread'].create({ - id: 1000, - model: 'mail.channel', - partnerSeenInfos: insertAndReplace([ - { - lastFetchedMessage: insert({ id: 100 }), - partner: insertAndReplace({ id: 10 }), - }, - { - lastFetchedMessage: insert({ id: 99 }), - partner: insertAndReplace({ id: 100 }), - }, - ]), - messageSeenIndicators: insertAndReplace({ - message: insertAndReplace({ id: 100 }), - }), + const pyEnv = await startServer(); + const resPartnerId1 = pyEnv['res.partner'].create({ name: "Demo User" }); + const resPartnerId2 = pyEnv['res.partner'].create({ name: "Other User" }); + const mailChannelId = pyEnv['mail.channel'].create({ + channel_last_seen_partner_ids: [ + [0, 0, { partner_id: pyEnv.currentPartnerId }], + [0, 0, { partner_id: resPartnerId1 }], + [0, 0, { partner_id: resPartnerId2 }], + ], + channel_type: 'chat', }); - const message = messaging.models['Message'].insert({ - author: insert({ id: messaging.currentPartner.id, display_name: "Demo User" }), + const mailMessageId = pyEnv['mail.message'].create({ + author_id: pyEnv.currentPartnerId, body: "

Test

", - id: 100, - originThread: link(thread), + model: 'mail.channel', + res_id: mailChannelId, }); - await this.createMessageSeenIndicatorComponent({ message, target: widget.el, thread }); + const cpIds = pyEnv['mail.channel.partner'].search([['channel_id', '=', mailChannelId]]); + pyEnv['mail.channel.partner'].write(cpIds, { + fetched_message_id: mailMessageId, + seen_message_id: false, + }); + const { openDiscuss } = await start({ + discuss: { + params: { + default_active_id: `mail.channel_${mailChannelId}`, + }, + }, + hasDiscuss: true, + }); + await openDiscuss(); assert.containsOnce( document.body, '.o_MessageSeenIndicator', @@ -115,32 +129,41 @@ QUnit.test('rendering when everyone have received the message', async function ( QUnit.test('rendering when just one has seen the message', async function (assert) { assert.expect(3); - const { messaging, widget } = await start(); - const thread = messaging.models['Thread'].create({ - id: 1000, - model: 'mail.channel', - partnerSeenInfos: insertAndReplace([ - { - lastFetchedMessage: insert({ id: 100 }), - lastSeenMessage: insert({ id: 100 }), - partner: insertAndReplace({ id: 10 }), - }, - { - lastFetchedMessage: insert({ id: 99 }), - partner: insertAndReplace({ id: 100 }), - }, - ]), - messageSeenIndicators: insertAndReplace({ - message: insertAndReplace({ id: 100 }), - }), + const pyEnv = await startServer(); + const resPartnerId1 = pyEnv['res.partner'].create({ name: "Demo User" }); + const resPartnerId2 = pyEnv['res.partner'].create({ name: "Other User" }); + const mailChannelId = pyEnv['mail.channel'].create({ + channel_last_seen_partner_ids: [ + [0, 0, { partner_id: pyEnv.currentPartnerId }], + [0, 0, { partner_id: resPartnerId1 }], + [0, 0, { partner_id: resPartnerId2 }], + ], + channel_type: 'chat', }); - const message = messaging.models['Message'].insert({ - author: insert({ id: messaging.currentPartner.id, display_name: "Demo User" }), + const mailMessageId = pyEnv['mail.message'].create({ + author_id: pyEnv.currentPartnerId, body: "

Test

", - id: 100, - originThread: link(thread), + model: 'mail.channel', + res_id: mailChannelId, }); - await this.createMessageSeenIndicatorComponent({ message, target: widget.el, thread }); + const cpIds = pyEnv['mail.channel.partner'].search([['channel_id', '=', mailChannelId]]); + pyEnv['mail.channel.partner'].write(cpIds, { + fetched_message_id: mailMessageId, + seen_message_id: false, + }); + const [seenCpId] = pyEnv['mail.channel.partner'].search([['channel_id', '=', mailChannelId], ['partner_id', '=', resPartnerId1]]); + pyEnv['mail.channel.partner'].write([seenCpId], { + seen_message_id: mailMessageId, + }); + const { openDiscuss } = await start({ + discuss: { + params: { + default_active_id: `mail.channel_${mailChannelId}`, + }, + }, + hasDiscuss: true, + }); + await openDiscuss(); assert.containsOnce( document.body, '.o_MessageSeenIndicator', @@ -162,31 +185,37 @@ QUnit.test('rendering when just one has seen the message', async function (asser QUnit.test('rendering when just one has seen & received the message', async function (assert) { assert.expect(3); - const { messaging, widget } = await start(); - const thread = messaging.models['Thread'].create({ - id: 1000, - model: 'mail.channel', - partnerSeenInfos: insertAndReplace([ - { - lastFetchedMessage: insert({ id: 100 }), - lastSeenMessage: insert({ id: 100 }), - partner: insertAndReplace({ id: 10 }), - }, - { - partner: insertAndReplace({ id: 100 }), - }, - ]), - messageSeenIndicators: insertAndReplace({ - message: insertAndReplace({ id: 100 }), - }), + const pyEnv = await startServer(); + const resPartnerId1 = pyEnv['res.partner'].create({ name: "Demo User" }); + const resPartnerId2 = pyEnv['res.partner'].create({ name: "Other User" }); + const mailChannelId = pyEnv['mail.channel'].create({ + channel_last_seen_partner_ids: [ + [0, 0, { partner_id: pyEnv.currentPartnerId }], + [0, 0, { partner_id: resPartnerId1 }], + [0, 0, { partner_id: resPartnerId2 }], + ], + channel_type: 'chat', }); - const message = messaging.models['Message'].insert({ - author: insert({ id: messaging.currentPartner.id, display_name: "Demo User" }), + const mailMessageId = pyEnv['mail.message'].create({ + author_id: pyEnv.currentPartnerId, body: "

Test

", - id: 100, - originThread: link(thread), + model: 'mail.channel', + res_id: mailChannelId, }); - await this.createMessageSeenIndicatorComponent({ message, target: widget.el, thread }); + const [seenCpId] = pyEnv['mail.channel.partner'].search([['channel_id', '=', mailChannelId], ['partner_id', '=', resPartnerId1]]); + pyEnv['mail.channel.partner'].write([seenCpId], { + seen_message_id: mailMessageId, + fetched_message_id: mailMessageId, + }); + const { openDiscuss } = await start({ + discuss: { + params: { + default_active_id: `mail.channel_${mailChannelId}`, + }, + }, + hasDiscuss: true, + }); + await openDiscuss(); assert.containsOnce( document.body, '.o_MessageSeenIndicator', @@ -208,33 +237,37 @@ QUnit.test('rendering when just one has seen & received the message', async func QUnit.test('rendering when just everyone has seen the message', async function (assert) { assert.expect(3); - const { messaging, widget } = await start(); - const thread = messaging.models['Thread'].create({ - id: 1000, - model: 'mail.channel', - partnerSeenInfos: insertAndReplace([ - { - lastFetchedMessage: insert({ id: 100 }), - lastSeenMessage: insert({ id: 100 }), - partner: insertAndReplace({ id: 10 }), - }, - { - lastFetchedMessage: insert({ id: 100 }), - lastSeenMessage: insert({ id: 100 }), - partner: insertAndReplace({ id: 100 }), - }, - ]), - messageSeenIndicators: insertAndReplace({ - message: insertAndReplace({ id: 100 }), - }), + const pyEnv = await startServer(); + const resPartnerId1 = pyEnv['res.partner'].create({ name: "Demo User" }); + const resPartnerId2 = pyEnv['res.partner'].create({ name: "Other User" }); + const mailChannelId = pyEnv['mail.channel'].create({ + channel_last_seen_partner_ids: [ + [0, 0, { partner_id: pyEnv.currentPartnerId }], + [0, 0, { partner_id: resPartnerId1 }], + [0, 0, { partner_id: resPartnerId2 }], + ], + channel_type: 'chat', }); - const message = messaging.models['Message'].insert({ - author: insert({ id: messaging.currentPartner.id, display_name: "Demo User" }), + const mailMessageId = pyEnv['mail.message'].create({ + author_id: pyEnv.currentPartnerId, body: "

Test

", - id: 100, - originThread: link(thread), + model: 'mail.channel', + res_id: mailChannelId, }); - await this.createMessageSeenIndicatorComponent({ message, target: widget.el, thread }); + const cpIds = pyEnv['mail.channel.partner'].search([['channel_id', '=', mailChannelId]]); + pyEnv['mail.channel.partner'].write(cpIds, { + fetched_message_id: mailMessageId, + seen_message_id: mailMessageId, + }); + const { openDiscuss } = await start({ + discuss: { + params: { + default_active_id: `mail.channel_${mailChannelId}`, + }, + }, + hasDiscuss: true, + }); + await openDiscuss(); assert.containsOnce( document.body, '.o_MessageSeenIndicator',