[IMP] mail: 'ComposerSuggestion' + 'ComposerView/mainSuggestions'

Task-2793280

closes odoo/odoo#87092

Signed-off-by: Sébastien Theys (seb) <seb@odoo.com>
This commit is contained in:
Alexandre Kühn
2022-03-24 13:57:46 +01:00
parent e27fcdf42e
commit 1b5fa72056
3 changed files with 96 additions and 25 deletions
@@ -6,15 +6,15 @@
<div class="o_ComposerSuggestionList" t-att-class="{ 'o-lowPosition': props.isBelow }" t-attf-class="{{ className }}" t-ref="root">
<div class="o_ComposerSuggestionList_drop" t-att-class="{ 'dropdown': props.isBelow, 'dropup': !props.isBelow }">
<div class="o_ComposerSuggestionList_list dropdown-menu show">
<t t-foreach="composerView.mainSuggestedRecords" t-as="record" t-key="record.localId">
<t t-foreach="composerView.mainSuggestions" t-as="suggestion" t-key="suggestion.localId">
<ComposerSuggestion
composerViewLocalId="composerView.localId"
isActive="record === composerView.activeSuggestedRecord"
isActive="suggestion.record === composerView.activeSuggestedRecord"
modelName="composerView.suggestionModelName"
recordLocalId="record.localId"
recordLocalId="suggestion.record.localId"
/>
</t>
<t t-if="composerView.mainSuggestedRecords.length > 0 and composerView.extraSuggestedRecords.length > 0">
<t t-if="composerView.mainSuggestions.length > 0 and composerView.extraSuggestedRecords.length > 0">
<div role="separator" class="dropdown-divider"/>
</t>
<t t-foreach="composerView.extraSuggestedRecords" t-as="record" t-key="record.localId">
@@ -0,0 +1,59 @@
/** @odoo-module **/
import { registerModel } from '@mail/model/model_core';
import { one } from '@mail/model/model_field';
import { clear, replace } from '@mail/model/model_field_command';
/**
* Models a suggestion in the composer suggestion.
*
* For instance, to mention a partner, can type "@" and some keyword,
* and display suggested partners to mention.
*/
registerModel({
name: 'ComposerSuggestion',
identifyingFields: ['composerViewOwnerAsMainSuggestion', ['cannedResponse', 'channelCommand', 'partner', 'thread']],
recordMethods: {
/**
* @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();
},
},
fields: {
cannedResponse: one('CannedResponse', {
readonly: true,
}),
channelCommand: one('ChannelCommand', {
readonly: true,
}),
composerViewOwnerAsMainSuggestion: one('ComposerView', {
inverse: 'mainSuggestions',
readonly: true,
required: true,
}),
record: one('Record', {
compute: '_computeRecord',
}),
partner: one('Partner', {
readonly: true,
}),
thread: one('Thread', {
readonly: true,
}),
},
});
+33 -21
View File
@@ -3,7 +3,7 @@
import { emojis } from '@mail/js/emojis';
import { registerModel } from '@mail/model/model_core';
import { attr, many, one } from '@mail/model/model_field';
import { clear, insertAndReplace, link, replace, unlink, unlinkAll } from '@mail/model/model_field_command';
import { clear, insertAndReplace, link, replace, unlink } from '@mail/model/model_field_command';
import { OnChange } from '@mail/model/model_onchange';
import { addLink, escapeAndCompactTextContent, parseAndTransform } from '@mail/js/utils';
import { isEventHandled, markEventHandled } from '@mail/utils/utils';
@@ -408,7 +408,7 @@ registerModel({
* considered together.
*/
setFirstSuggestionActive() {
const suggestedRecords = this.mainSuggestedRecords.concat(this.extraSuggestedRecords);
const suggestedRecords = this.mainSuggestions.map(suggestion => suggestion.record).concat(this.extraSuggestedRecords);
const firstRecord = suggestedRecords[0];
this.update({ activeSuggestedRecord: link(firstRecord) });
},
@@ -417,7 +417,7 @@ registerModel({
* considered together.
*/
setLastSuggestionActive() {
const suggestedRecords = this.mainSuggestedRecords.concat(this.extraSuggestedRecords);
const suggestedRecords = this.mainSuggestions.map(suggestion => suggestion.record).concat(this.extraSuggestedRecords);
const { length, [length - 1]: lastRecord } = suggestedRecords;
this.update({ activeSuggestedRecord: link(lastRecord) });
},
@@ -426,7 +426,7 @@ registerModel({
* considered together.
*/
setNextSuggestionActive() {
const suggestedRecords = this.mainSuggestedRecords.concat(this.extraSuggestedRecords);
const suggestedRecords = this.mainSuggestions.map(suggestion => suggestion.record).concat(this.extraSuggestedRecords);
const activeElementIndex = suggestedRecords.findIndex(
suggestion => suggestion === this.activeSuggestedRecord
);
@@ -443,7 +443,7 @@ registerModel({
* considered together.
*/
setPreviousSuggestionActive() {
const suggestedRecords = this.mainSuggestedRecords.concat(this.extraSuggestedRecords);
const suggestedRecords = this.mainSuggestions.map(suggestion => suggestion.record).concat(this.extraSuggestedRecords);
const activeElementIndex = suggestedRecords.findIndex(
suggestion => suggestion === this.activeSuggestedRecord
);
@@ -495,18 +495,18 @@ registerModel({
*/
_computeActiveSuggestedRecord() {
if (
this.mainSuggestedRecords.length === 0 &&
this.mainSuggestions.length === 0 &&
this.extraSuggestedRecords.length === 0
) {
return unlink();
}
if (
this.mainSuggestedRecords.includes(this.activeSuggestedRecord) ||
this.mainSuggestions.map(suggestion => suggestion.record).includes(this.activeSuggestedRecord) ||
this.extraSuggestedRecords.includes(this.activeSuggestedRecord)
) {
return;
}
const suggestedRecords = this.mainSuggestedRecords.concat(this.extraSuggestedRecords);
const suggestedRecords = this.mainSuggestions.map(suggestion => suggestion.record).concat(this.extraSuggestedRecords);
const firstRecord = suggestedRecords[0];
return link(firstRecord);
},
@@ -565,9 +565,9 @@ registerModel({
*/
_computeExtraSuggestedRecords() {
if (this.suggestionDelimiterPosition === undefined) {
return unlinkAll();
return clear();
}
return unlink(this.mainSuggestedRecords);
return unlink(this.mainSuggestions.map(suggestion => suggestion.record));
},
/**
* @private
@@ -607,7 +607,7 @@ registerModel({
* @return {boolean}
*/
_computeHasSuggestions() {
return this.mainSuggestedRecords.length > 0 || this.extraSuggestedRecords.length > 0;
return this.mainSuggestions.length > 0 || this.extraSuggestedRecords.length > 0;
},
/**
* @private
@@ -643,14 +643,14 @@ registerModel({
return clear();
},
/**
* Clears the main suggested record on closing mentions.
* Clears the main suggestions on closing mentions.
*
* @private
* @returns {Record[]}
*/
_computeMainSuggestedRecords() {
_computeMainSuggestions() {
if (this.suggestionDelimiterPosition === undefined) {
return unlinkAll();
return clear();
}
},
/**
@@ -988,7 +988,20 @@ registerModel({
this.update({
extraSuggestedRecords: replace(extraSuggestedRecords),
hasToScrollToActiveSuggestion: true,
mainSuggestedRecords: replace(mainSuggestedRecords),
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) };
}
}),
),
});
},
},
@@ -1113,13 +1126,12 @@ registerModel({
default: false,
}),
/**
* Determines the main records that are currently suggested.
* Allows to have different model types of mentions through a dynamic
* process. 2 arbitrary lists can be provided and the first is defined
* as "main".
* Determines the main suggestions.
*/
mainSuggestedRecords: many('Record', {
compute: '_computeMainSuggestedRecords',
mainSuggestions: many('ComposerSuggestion', {
compute: '_computeMainSuggestions',
inverse: 'composerViewOwnerAsMainSuggestion',
isCausal: true,
}),
/**
* States the message view on which this composer allows editing (if any).