[IMP] mail: move activeSuggestionView to ComposerSuggestionListView

Part of task-2741982

Part-of: odoo/odoo#95341
This commit is contained in:
Sébastien Theys
2022-07-08 18:44:58 +02:00
parent 4573b4e46e
commit 3aa248b2e0
5 changed files with 61 additions and 68 deletions
@@ -36,13 +36,13 @@ export class ComposerSuggestionView extends Component {
_update() {
if (
this.root.el &&
this.composerSuggestionView.composerSuggestionListViewOwner.composerViewOwner.hasToScrollToActiveSuggestionView &&
this.composerSuggestionView.composerViewOwnerAsActiveSuggestionView
this.composerSuggestionView.composerSuggestionListViewOwner.hasToScrollToActiveSuggestionView &&
this.composerSuggestionView.composerSuggestionListViewOwnerAsActiveSuggestionView
) {
this.root.el.scrollIntoView({
block: 'center',
});
this.composerSuggestionView.composerSuggestionListViewOwner.composerViewOwner.update({ hasToScrollToActiveSuggestionView: false });
this.composerSuggestionView.composerSuggestionListViewOwner.update({ hasToScrollToActiveSuggestionView: false });
}
}
@@ -3,7 +3,7 @@
<t t-name="mail.ComposerSuggestionView" owl="1">
<t t-if="composerSuggestionView">
<a class="o_ComposerSuggestionView dropdown-item d-flex w-100 py-2 px-4" t-att-class="{ 'active bg-300': composerSuggestionView.composerViewOwnerAsActiveSuggestionView }" t-attf-class="{{ className }}" href="#" t-att-title="composerSuggestionView.title" role="menuitem" t-on-click="composerSuggestionView.onClick" t-ref="root">
<a class="o_ComposerSuggestionView dropdown-item d-flex w-100 py-2 px-4" t-att-class="{ 'active bg-300': composerSuggestionView.composerSuggestionListViewOwnerAsActiveSuggestionView }" t-attf-class="{{ className }}" href="#" t-att-title="composerSuggestionView.title" role="menuitem" t-on-click="composerSuggestionView.onClick" t-ref="root">
<t t-if="composerSuggestionView.suggestable.cannedResponse">
<strong class="o_ComposerSuggestionView_part1 flex-shrink-0 mw-100 pe-2 text-truncate"><t t-esc="composerSuggestionView.suggestable.cannedResponse.source"/></strong>
<em class="o_ComposerSuggestionView_part2 text-600 text-truncate"><t t-esc="composerSuggestionView.suggestable.cannedResponse.substitution"/></em>
@@ -1,13 +1,31 @@
/** @odoo-module **/
import { registerModel } from '@mail/model/model_core';
import { many, one } from '@mail/model/model_field';
import { attr, many, one } from '@mail/model/model_field';
import { insertAndReplace, replace } from '@mail/model/model_field_command';
registerModel({
name: 'ComposerSuggestionListView',
identifyingFields: ['composerViewOwner'],
recordMethods: {
/**
* Adapts the active suggestion it if the active suggestion is no longer
* part of the suggestions.
*
* @private
* @returns {FieldCommand}
*/
_computeActiveSuggestionView() {
if (
this.mainSuggestionViews.includes(this.activeSuggestionView) ||
this.extraSuggestionViews.includes(this.activeSuggestionView)
) {
return;
}
const suggestionViews = this.mainSuggestionViews.concat(this.extraSuggestionViews);
const firstSuggestionView = suggestionViews[0];
return replace(firstSuggestionView);
},
/**
* @returns {FieldCommand}
*/
@@ -30,6 +48,15 @@ registerModel({
},
},
fields: {
/**
* Determines the suggestion that is currently active. This suggestion
* is highlighted in the UI and it will be selected when the
* suggestion is confirmed by the user.
*/
activeSuggestionView: one('ComposerSuggestionView', {
compute: '_computeActiveSuggestionView',
inverse: 'composerSuggestionListViewOwnerAsActiveSuggestionView',
}),
composerViewOwner: one('ComposerView', {
inverse: 'composerSuggestionListView',
readonly: true,
@@ -40,6 +67,13 @@ registerModel({
inverse: 'composerSuggestionListViewOwnerAsExtraSuggestion',
isCausal: true,
}),
/**
* Determines whether the currently active suggestion should be scrolled
* into view.
*/
hasToScrollToActiveSuggestionView: attr({
default: false,
}),
mainSuggestionViews: many('ComposerSuggestionView', {
compute: '_computeMainSuggestionViews',
inverse: 'composerSuggestionListViewOwnerAsMainSuggestion',
@@ -20,7 +20,7 @@ registerModel({
*/
onClick(ev) {
ev.preventDefault();
this.composerSuggestionListViewOwner.composerViewOwner.update({ activeSuggestionView: replace(this) });
this.composerSuggestionListViewOwner.update({ activeSuggestionView: replace(this) });
const composerViewOwner = this.composerSuggestionListViewOwner.composerViewOwner;
composerViewOwner.insertSuggestion();
composerViewOwner.closeSuggestions();
@@ -92,7 +92,7 @@ registerModel({
compute: '_computeComposerSuggestionListViewOwner',
required: true,
}),
composerViewOwnerAsActiveSuggestionView: one('ComposerView', {
composerSuggestionListViewOwnerAsActiveSuggestionView: one('ComposerSuggestionListView', {
inverse: 'activeSuggestionView',
}),
composerSuggestionListViewOwnerAsExtraSuggestion: one('ComposerSuggestionListView', {
+20 -61
View File
@@ -134,7 +134,7 @@ registerModel({
this.composer.textInputContent.length
);
}
const recordReplacement = this.activeSuggestionView.mentionText;
const recordReplacement = this.composerSuggestionListView.activeSuggestionView.mentionText;
const updateData = {
textInputContent: textLeft + recordReplacement + ' ' + textRight,
textInputCursorEnd: textLeft.length + recordReplacement.length + 1,
@@ -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.activeSuggestionView.suggestable.thread) {
Object.assign(updateData, { mentionedChannels: link(this.activeSuggestionView.suggestable.thread) });
if (this.composerSuggestionListView.activeSuggestionView.suggestable.thread) {
Object.assign(updateData, { mentionedChannels: link(this.composerSuggestionListView.activeSuggestionView.suggestable.thread) });
}
if (this.activeSuggestionView.suggestable.partner) {
Object.assign(updateData, { mentionedPartners: link(this.activeSuggestionView.suggestable.partner) });
if (this.composerSuggestionListView.activeSuggestionView.suggestable.partner) {
Object.assign(updateData, { mentionedPartners: link(this.composerSuggestionListView.activeSuggestionView.suggestable.partner) });
}
this.composer.update(updateData);
for (const composerView of this.composer.composerViews) {
@@ -440,7 +440,7 @@ registerModel({
}
if (this.hasSuggestions) {
this.setPreviousSuggestionViewActive();
this.update({ hasToScrollToActiveSuggestionView: true });
this.composerSuggestionListView.update({ hasToScrollToActiveSuggestionView: true });
}
break;
case 'ArrowDown':
@@ -451,29 +451,29 @@ registerModel({
}
if (this.hasSuggestions) {
this.setNextSuggestionViewActive();
this.update({ hasToScrollToActiveSuggestionView: true });
this.composerSuggestionListView.update({ hasToScrollToActiveSuggestionView: true });
}
break;
case 'Home':
if (this.hasSuggestions) {
this.setFirstSuggestionViewActive();
this.update({ hasToScrollToActiveSuggestionView: true });
this.composerSuggestionListView.update({ hasToScrollToActiveSuggestionView: true });
}
break;
case 'End':
if (this.hasSuggestions) {
this.setLastSuggestionViewActive();
this.update({ hasToScrollToActiveSuggestionView: true });
this.composerSuggestionListView.update({ hasToScrollToActiveSuggestionView: true });
}
break;
case 'Tab':
if (this.hasSuggestions) {
if (ev.shiftKey) {
this.setPreviousSuggestionViewActive();
this.update({ hasToScrollToActiveSuggestionView: true });
this.composerSuggestionListView.update({ hasToScrollToActiveSuggestionView: true });
} else {
this.setNextSuggestionViewActive();
this.update({ hasToScrollToActiveSuggestionView: true });
this.composerSuggestionListView.update({ hasToScrollToActiveSuggestionView: true });
}
}
break;
@@ -676,7 +676,7 @@ registerModel({
setFirstSuggestionViewActive() {
const suggestionViews = this.composerSuggestionListView.mainSuggestionViews.concat(this.composerSuggestionListView.extraSuggestionViews);
const firstSuggestionView = suggestionViews[0];
this.update({ activeSuggestionView: replace(firstSuggestionView) });
this.composerSuggestionListView.update({ activeSuggestionView: replace(firstSuggestionView) });
},
/**
* Sets the last suggestion as active. Main and extra records are
@@ -685,7 +685,7 @@ registerModel({
setLastSuggestionViewActive() {
const suggestionViews = this.composerSuggestionListView.mainSuggestionViews.concat(this.composerSuggestionListView.extraSuggestionViews);
const { length, [length - 1]: lastSuggestionView } = suggestionViews;
this.update({ activeSuggestionView: replace(lastSuggestionView) });
this.composerSuggestionListView.update({ activeSuggestionView: replace(lastSuggestionView) });
},
/**
* Sets the next suggestion as active. Main and extra records are
@@ -694,7 +694,7 @@ registerModel({
setNextSuggestionViewActive() {
const suggestionViews = this.composerSuggestionListView.mainSuggestionViews.concat(this.composerSuggestionListView.extraSuggestionViews);
const activeElementIndex = suggestionViews.findIndex(
suggestion => suggestion === this.activeSuggestionView
suggestion => suggestion === this.composerSuggestionListView.activeSuggestionView
);
if (activeElementIndex === suggestionViews.length - 1) {
// loop when reaching the end of the list
@@ -702,7 +702,7 @@ registerModel({
return;
}
const nextSuggestionView = suggestionViews[activeElementIndex + 1];
this.update({ activeSuggestionView: replace(nextSuggestionView) });
this.composerSuggestionListView.update({ activeSuggestionView: replace(nextSuggestionView) });
},
/**
* Sets the previous suggestion as active. Main and extra records are
@@ -711,7 +711,7 @@ registerModel({
setPreviousSuggestionViewActive() {
const suggestionViews = this.composerSuggestionListView.mainSuggestionViews.concat(this.composerSuggestionListView.extraSuggestionViews);
const activeElementIndex = suggestionViews.findIndex(
suggestion => suggestion === this.activeSuggestionView
suggestion => suggestion === this.composerSuggestionListView.activeSuggestionView
);
if (activeElementIndex === 0) {
// loop when reaching the start of the list
@@ -719,7 +719,7 @@ registerModel({
return;
}
const previousSuggestionView = suggestionViews[activeElementIndex - 1];
this.update({ activeSuggestionView: replace(previousSuggestionView) });
this.composerSuggestionListView.update({ activeSuggestionView: replace(previousSuggestionView) });
},
/**
* Updates the textarea height of text input.
@@ -759,33 +759,6 @@ registerModel({
}
}
},
/**
* Clears the active suggestion on closing mentions or adapts it if
* the active suggestion is no longer part of the suggestions.
*
* @private
* @returns {FieldCommand}
*/
_computeActiveSuggestionView() {
if (!this.composerSuggestionListView) {
return clear();
}
if (
this.composerSuggestionListView.mainSuggestionViews.length === 0 &&
this.composerSuggestionListView.extraSuggestionViews.length === 0
) {
return clear();
}
if (
this.composerSuggestionListView.mainSuggestionViews.includes(this.activeSuggestionView) ||
this.composerSuggestionListView.extraSuggestionViews.includes(this.activeSuggestionView)
) {
return;
}
const suggestionViews = this.composerSuggestionListView.mainSuggestionViews.concat(this.composerSuggestionListView.extraSuggestionViews);
const firstSuggestionView = suggestionViews[0];
return replace(firstSuggestionView);
},
/**
* @private
* @returns {boolean}
@@ -1384,9 +1357,11 @@ registerModel({
extraSuggestedRecords.length = Math.min(extraSuggestedRecords.length, limit - mainSuggestedRecords.length);
this.update({
extraSuggestions: replace(extraSuggestedRecords.map(record => record.suggestable)),
hasToScrollToActiveSuggestionView: true,
mainSuggestions: replace(mainSuggestedRecords.map(record => record.suggestable)),
});
if (this.composerSuggestionListView) {
this.composerSuggestionListView.update({ hasToScrollToActiveSuggestionView: true });
}
},
/**
* @private
@@ -1400,15 +1375,6 @@ registerModel({
}
},
fields: {
/**
* Determines the suggestion that is currently active. This suggestion
* is highlighted in the UI and it will be selected when the
* suggestion is confirmed by the user.
*/
activeSuggestionView: one('ComposerSuggestionView', {
compute: '_computeActiveSuggestionView',
inverse: 'composerViewOwnerAsActiveSuggestionView',
}),
/**
* Determines the attachment list that will be used to display the attachments.
*/
@@ -1548,13 +1514,6 @@ registerModel({
hasToRestoreContent: attr({
default: false,
}),
/**
* Determines whether the currently active suggestion should be scrolled
* into view.
*/
hasToScrollToActiveSuggestionView: attr({
default: false,
}),
isCompact: attr({
compute: '_computeIsCompact',
default: true,