[IMP] mail: move activeSuggestionView to ComposerSuggestionListView
Part of task-2741982 Part-of: odoo/odoo#95341
This commit is contained in:
+3
-3
@@ -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 });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -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', {
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user