[IMP] mail: introduce ComposerSuggestable model

This will allow to remove AND/OR mix in identifying fields of composer
suggestion view.

Part of task-2741982

closes odoo/odoo#94999

Signed-off-by: Alexandre Kühn (aku) <aku@odoo.com>
This commit is contained in:
Sébastien Theys
2022-07-01 16:35:59 +02:00
parent 3a5f94bff5
commit d5ed1069f0
9 changed files with 109 additions and 91 deletions
@@ -4,27 +4,27 @@
<t t-name="mail.ComposerSuggestion" owl="1">
<t t-if="composerSuggestion">
<a class="o_ComposerSuggestion dropdown-item d-flex w-100 py-2 px-4" t-att-class="{ 'active bg-300': composerSuggestion.isActive }" t-attf-class="{{ className }}" href="#" t-att-title="composerSuggestion.title" role="menuitem" t-on-click="composerSuggestion.onClick" t-ref="root">
<t t-if="composerSuggestion.cannedResponse">
<strong class="o_ComposerSuggestion_part1 flex-shrink-0 mw-100 pr-2 text-truncate"><t t-esc="composerSuggestion.record.source"/></strong>
<em class="o_ComposerSuggestion_part2 text-600 text-truncate"><t t-esc="composerSuggestion.record.substitution"/></em>
<t t-if="composerSuggestion.suggestable.cannedResponse">
<strong class="o_ComposerSuggestion_part1 flex-shrink-0 mw-100 pr-2 text-truncate"><t t-esc="composerSuggestion.suggestable.cannedResponse.source"/></strong>
<em class="o_ComposerSuggestion_part2 text-600 text-truncate"><t t-esc="composerSuggestion.suggestable.cannedResponse.substitution"/></em>
</t>
<t t-if="composerSuggestion.thread">
<strong class="o_ComposerSuggestion_part1 flex-shrink-0 mw-100 pr-2 text-truncate"><t t-esc="composerSuggestion.record.name"/></strong>
<t t-if="composerSuggestion.suggestable.thread">
<strong class="o_ComposerSuggestion_part1 flex-shrink-0 mw-100 pr-2 text-truncate"><t t-esc="composerSuggestion.suggestable.thread.name"/></strong>
</t>
<t t-if="composerSuggestion.channelCommand">
<strong class="o_ComposerSuggestion_part1 flex-shrink-0 mw-100 pr-2 text-truncate"><t t-esc="composerSuggestion.record.name"/></strong>
<em class="o_ComposerSuggestion_part2 text-600 text-truncate"><t t-esc="composerSuggestion.record.help"/></em>
<t t-if="composerSuggestion.suggestable.channelCommand">
<strong class="o_ComposerSuggestion_part1 flex-shrink-0 mw-100 pr-2 text-truncate"><t t-esc="composerSuggestion.suggestable.channelCommand.name"/></strong>
<em class="o_ComposerSuggestion_part2 text-600 text-truncate"><t t-esc="composerSuggestion.suggestable.channelCommand.help"/></em>
</t>
<t t-if="composerSuggestion.partner">
<t t-if="composerSuggestion.suggestable.partner">
<PersonaImStatusIcon
t-if="composerSuggestion.personaImStatusIconView"
className="'o_ComposerSuggestion_personaImStatusIcon flex-shrink-0 mr-1'"
hasBackground="false"
record="composerSuggestion.personaImStatusIconView"
/>
<strong class="o_ComposerSuggestion_part1 flex-shrink-0 mw-100 pr-2 text-truncate"><t t-esc="composerSuggestion.record.nameOrDisplayName"/></strong>
<t t-if="composerSuggestion.record.email">
<em class="o_ComposerSuggestion_part2 text-600 text-truncate">(<t t-esc="composerSuggestion.record.email"/>)</em>
<strong class="o_ComposerSuggestion_part1 flex-shrink-0 mw-100 pr-2 text-truncate"><t t-esc="composerSuggestion.suggestable.partner.nameOrDisplayName"/></strong>
<t t-if="composerSuggestion.suggestable.partner.email">
<em class="o_ComposerSuggestion_part2 text-600 text-truncate">(<t t-esc="composerSuggestion.suggestable.partner.email"/>)</em>
</t>
</t>
</a>
@@ -1,7 +1,8 @@
/** @odoo-module **/
import { registerModel } from '@mail/model/model_core';
import { attr } from '@mail/model/model_field';
import { attr, one } from '@mail/model/model_field';
import { insertAndReplace } from '@mail/model/model_field_command';
import { cleanSearchTerm } from '@mail/utils/utils';
registerModel({
@@ -82,5 +83,12 @@ registerModel({
* entered.
*/
substitution: attr(),
suggestable: one('ComposerSuggestable', {
default: insertAndReplace(),
inverse: 'cannedResponse',
isCausal: true,
readonly: true,
required: true,
}),
},
});
@@ -1,7 +1,8 @@
/** @odoo-module **/
import { registerModel } from '@mail/model/model_core';
import { attr } from '@mail/model/model_field';
import { attr, one } from '@mail/model/model_field';
import { insertAndReplace } from '@mail/model/model_field_command';
import { cleanSearchTerm } from '@mail/utils/utils';
registerModel({
@@ -130,5 +131,12 @@ registerModel({
readonly: true,
required: true,
}),
suggestable: one('ComposerSuggestable', {
default: insertAndReplace(),
inverse: 'channelCommand',
isCausal: true,
readonly: true,
required: true,
}),
},
});
@@ -0,0 +1,31 @@
/** @odoo-module **/
import { registerModel } from '@mail/model/model_core';
import { many, one } from '@mail/model/model_field';
registerModel({
name: 'ComposerSuggestable',
identifyingFields: [['cannedResponse', 'channelCommand', 'partner', 'thread']],
fields: {
cannedResponse: one('CannedResponse', {
inverse: 'suggestable',
readonly: true,
}),
channelCommand: one('ChannelCommand', {
inverse: 'suggestable',
readonly: true,
}),
composerSuggestions: many('ComposerSuggestion', {
inverse: 'suggestable',
isCausal: true,
}),
partner: one('Partner', {
inverse: 'suggestable',
readonly: true,
}),
thread: one('Thread', {
inverse: 'suggestable',
readonly: true,
}),
},
});
@@ -13,7 +13,7 @@ import { sprintf } from '@web/core/utils/strings';
*/
registerModel({
name: 'ComposerSuggestion',
identifyingFields: [['composerViewOwnerAsExtraSuggestion', 'composerViewOwnerAsMainSuggestion'], ['cannedResponse', 'channelCommand', 'partner', 'thread']],
identifyingFields: [['composerViewOwnerAsExtraSuggestion', 'composerViewOwnerAsMainSuggestion'], 'suggestable'],
recordMethods: {
/**
* @param {Event} ev
@@ -53,17 +53,17 @@ registerModel({
* @returns {string}
*/
_computeMentionText() {
if (this.cannedResponse) {
return this.cannedResponse.substitution;
if (this.suggestable.cannedResponse) {
return this.suggestable.cannedResponse.substitution;
}
if (this.channelCommand) {
return this.channelCommand.name;
if (this.suggestable.channelCommand) {
return this.suggestable.channelCommand.name;
}
if (this.partner) {
return this.partner.name;
if (this.suggestable.partner) {
return this.suggestable.partner.name;
}
if (this.thread) {
return this.thread.name;
if (this.suggestable.thread) {
return this.suggestable.thread.name;
}
},
/**
@@ -71,57 +71,32 @@ registerModel({
* @returns {FieldCommand}
*/
_computePersonaImStatusIconView() {
return this.partner && this.partner.isImStatusSet ? insertAndReplace() : clear();
},
/**
* @private
* @returns {FieldCommand}
*/
_computeRecord() {
if (this.cannedResponse) {
return replace(this.cannedResponse);
}
if (this.channelCommand) {
return replace(this.channelCommand);
}
if (this.partner) {
return replace(this.partner);
}
if (this.thread) {
return replace(this.thread);
}
return clear();
return this.suggestable.partner && this.suggestable.partner.isImStatusSet ? insertAndReplace() : clear();
},
/**
* @private
* @returns {string|FieldCommand}
*/
_computeTitle() {
if (this.cannedResponse) {
return sprintf("%s: %s", this.record.source, this.record.substitution);
if (this.suggestable.cannedResponse) {
return sprintf("%s: %s", this.suggestable.cannedResponse.source, this.suggestable.cannedResponse.substitution);
}
if (this.thread) {
return this.record.name;
if (this.suggestable.thread) {
return this.suggestable.thread.name;
}
if (this.channelCommand) {
return sprintf("%s: %s", this.record.name, this.record.help);
if (this.suggestable.channelCommand) {
return sprintf("%s: %s", this.suggestable.channelCommand.name, this.suggestable.channelCommand.help);
}
if (this.partner) {
if (this.record.email) {
return sprintf("%s (%s)", this.record.nameOrDisplayName, this.record.email);
if (this.suggestable.partner) {
if (this.suggestable.partner.email) {
return sprintf("%s (%s)", this.suggestable.partner.nameOrDisplayName, this.suggestable.partner.email);
}
return this.record.nameOrDisplayName;
return this.suggestable.partner.nameOrDisplayName;
}
return clear();
},
},
fields: {
cannedResponse: one('CannedResponse', {
readonly: true,
}),
channelCommand: one('ChannelCommand', {
readonly: true,
}),
composerViewOwner: one('ComposerView', {
compute: '_computeComposerViewOwner',
required: true,
@@ -144,20 +119,16 @@ registerModel({
mentionText: attr({
compute: '_computeMentionText',
}),
record: one('Record', {
compute: '_computeRecord',
}),
partner: one('Partner', {
readonly: true,
}),
personaImStatusIconView: one('PersonaImStatusIconView', {
compute: '_computePersonaImStatusIconView',
inverse: 'composerSuggestionViewOwner',
isCausal: true,
readonly: true,
}),
thread: one('Thread', {
suggestable: one('ComposerSuggestable', {
inverse: 'composerSuggestions',
readonly: true,
required: true,
}),
/**
* Descriptive title for this suggestion. Useful to be able to
+10 -24
View File
@@ -143,11 +143,11 @@ registerModel({
// Specific cases for channel and partner mentions: the message with
// the mention will appear in the target channel, or be notified to
// the target partner.
if (this.activeSuggestion.thread) {
Object.assign(updateData, { mentionedChannels: link(this.activeSuggestion.thread) });
if (this.activeSuggestion.suggestable.thread) {
Object.assign(updateData, { mentionedChannels: link(this.activeSuggestion.suggestable.thread) });
}
if (this.activeSuggestion.partner) {
Object.assign(updateData, { mentionedPartners: link(this.activeSuggestion.partner) });
if (this.activeSuggestion.suggestable.partner) {
Object.assign(updateData, { mentionedPartners: link(this.activeSuggestion.suggestable.partner) });
}
this.composer.update(updateData);
for (const composerView of this.composer.composerViews) {
@@ -1389,31 +1389,17 @@ registerModel({
this.update({
extraSuggestions: insertAndReplace(
extraSuggestedRecords.map(record => {
switch (record.constructor.name) {
case 'CannedResponse':
return { cannedResponse: replace(record) };
case 'ChannelCommand':
return { channelCommand: replace(record) };
case 'Partner':
return { partner: replace(record) };
case 'Thread':
return { thread: replace(record) };
}
return {
suggestable: replace(record.suggestable),
};
}),
),
hasToScrollToActiveSuggestion: true,
mainSuggestions: insertAndReplace(
mainSuggestedRecords.map(record => {
switch (record.constructor.name) {
case 'CannedResponse':
return { cannedResponse: replace(record) };
case 'ChannelCommand':
return { channelCommand: replace(record) };
case 'Partner':
return { partner: replace(record) };
case 'Thread':
return { thread: replace(record) };
}
return {
suggestable: replace(record.suggestable),
};
}),
),
});
+7
View File
@@ -451,6 +451,13 @@ registerModel({
rtcSessions: many('RtcSession', {
inverse: 'partner',
}),
suggestable: one('ComposerSuggestable', {
default: insertAndReplace(),
inverse: 'partner',
isCausal: true,
readonly: true,
required: true,
}),
user: one('User', {
inverse: 'partner',
}),
@@ -28,7 +28,7 @@ registerModel({
return replace(this.channelMemberViewOwner.channelMember.partner.persona);
}
if (this.composerSuggestionViewOwner) {
return replace(this.composerSuggestionViewOwner.partner.persona);
return replace(this.composerSuggestionViewOwner.suggestable.partner.persona);
}
if (this.messageViewOwner) {
return replace(this.messageViewOwner.message.author.persona);
+7
View File
@@ -2257,6 +2257,13 @@ registerModel({
serverMessageUnreadCounter: attr({
default: 0,
}),
suggestable: one('ComposerSuggestable', {
default: insertAndReplace(),
inverse: 'thread',
isCausal: true,
readonly: true,
required: true,
}),
/**
* Determines the `SuggestedRecipientInfo` concerning `this`.
*/