[IMP] mail: introduce MessageSeenIndicatorView
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) <aku@odoo.com>
This commit is contained in:
@@ -49,8 +49,8 @@
|
||||
<t t-esc="messageView.message.shortTime"/>
|
||||
</div>
|
||||
</t>
|
||||
<t t-if="!messageView.isActive and messageView.message.isCurrentUserOrGuestAuthor and messageView.threadView and messageView.threadView.thread and messageView.threadView.thread.hasSeenIndicators">
|
||||
<MessageSeenIndicator className="'o_Message_seenIndicator'" messageLocalId="messageView.message.localId" threadLocalId="messageView.threadView.thread.localId"/>
|
||||
<t t-if="!messageView.isActive and messageView.messageSeenIndicatorView">
|
||||
<MessageSeenIndicator className="'o_Message_seenIndicator'" localId="messageView.messageSeenIndicatorView.localId"/>
|
||||
</t>
|
||||
</t>
|
||||
</div>
|
||||
@@ -87,8 +87,8 @@
|
||||
- <t t-esc="messageView.message.dateFromNow"/>
|
||||
</div>
|
||||
</t>
|
||||
<t t-if="messageView.message.isCurrentUserOrGuestAuthor and messageView.threadView and messageView.threadView.thread and messageView.threadView.thread.hasSeenIndicators">
|
||||
<MessageSeenIndicator className="'o_Message_seenIndicator'" messageLocalId="messageView.message.localId" threadLocalId="messageView.threadView.thread.localId"/>
|
||||
<t t-if="messageView.messageSeenIndicatorView">
|
||||
<MessageSeenIndicator className="'o_Message_seenIndicator'" localId="messageView.messageSeenIndicatorView.localId"/>
|
||||
</t>
|
||||
<t t-if="messageView.threadView and messageView.message.originThread and messageView.message.originThread !== messageView.threadView.thread">
|
||||
<div class="o_Message_originThread" t-att-class="{ 'o-message-selected': messageView.isSelected }">
|
||||
|
||||
@@ -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',
|
||||
});
|
||||
|
||||
|
||||
@@ -2,13 +2,13 @@
|
||||
<templates xml:space="preserve">
|
||||
|
||||
<t t-name="mail.MessageSeenIndicator" owl="1">
|
||||
<t t-if="messageSeenIndicator">
|
||||
<span class="o_MessageSeenIndicator" t-att-class="{ 'o-all-seen': messageSeenIndicator.hasEveryoneSeen }" t-attf-class="{{ className }}" t-att-title="messageSeenIndicator.text" t-ref="root">
|
||||
<t t-if="!messageSeenIndicator.isMessagePreviousToLastCurrentPartnerMessageSeenByEveryone">
|
||||
<t t-if="messageSeenIndicator.hasSomeoneFetched or messageSeenIndicator.hasSomeoneSeen">
|
||||
<t t-if="messageSeenIndicatorView">
|
||||
<span class="o_MessageSeenIndicator" t-att-class="{ 'o-all-seen': messageSeenIndicatorView.messageSeenIndicator.hasEveryoneSeen }" t-attf-class="{{ className }}" t-att-title="messageSeenIndicatorView.messageSeenIndicator.text" t-ref="root">
|
||||
<t t-if="!messageSeenIndicatorView.messageSeenIndicator.isMessagePreviousToLastCurrentPartnerMessageSeenByEveryone">
|
||||
<t t-if="messageSeenIndicatorView.messageSeenIndicator.hasSomeoneFetched or messageSeenIndicatorView.messageSeenIndicator.hasSomeoneSeen">
|
||||
<i class="o_MessageSeenIndicator_icon o-first fa fa-check"/>
|
||||
</t>
|
||||
<t t-if="messageSeenIndicator.hasSomeoneSeen">
|
||||
<t t-if="messageSeenIndicatorView.messageSeenIndicator.hasSomeoneSeen">
|
||||
<i class="o_MessageSeenIndicator_icon o-second fa fa-check"/>
|
||||
</t>
|
||||
</t>
|
||||
|
||||
@@ -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',
|
||||
}),
|
||||
},
|
||||
});
|
||||
@@ -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).
|
||||
*/
|
||||
|
||||
+146
-113
@@ -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: "<p>Test</p>",
|
||||
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: "<p>Test</p>",
|
||||
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: "<p>Test</p>",
|
||||
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: "<p>Test</p>",
|
||||
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: "<p>Test</p>",
|
||||
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',
|
||||
|
||||
Reference in New Issue
Block a user