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',