summaryrefslogtreecommitdiff
path: root/addons/mail/static/src/components
diff options
context:
space:
mode:
Diffstat (limited to 'addons/mail/static/src/components')
-rw-r--r--addons/mail/static/src/components/chatter/chatter.xml1
-rw-r--r--addons/mail/static/src/components/composer/composer.js2
-rw-r--r--addons/mail/static/src/components/composer/composer.scss2
-rw-r--r--addons/mail/static/src/components/composer/composer.xml3
-rw-r--r--addons/mail/static/src/components/composer_suggested_recipient/composer_suggested_recipient.js163
-rw-r--r--addons/mail/static/src/components/composer_suggested_recipient/composer_suggested_recipient.scss5
-rw-r--r--addons/mail/static/src/components/composer_suggested_recipient/composer_suggested_recipient.xml41
-rw-r--r--addons/mail/static/src/components/composer_suggested_recipient_list/composer_suggested_recipient_list.js72
-rw-r--r--addons/mail/static/src/components/composer_suggested_recipient_list/composer_suggested_recipient_list.scss3
-rw-r--r--addons/mail/static/src/components/composer_suggested_recipient_list/composer_suggested_recipient_list.xml26
10 files changed, 316 insertions, 2 deletions
diff --git a/addons/mail/static/src/components/chatter/chatter.xml b/addons/mail/static/src/components/chatter/chatter.xml
index 53550da516d..911851106ad 100644
--- a/addons/mail/static/src/components/chatter/chatter.xml
+++ b/addons/mail/static/src/components/chatter/chatter.xml
@@ -13,6 +13,7 @@
class="o_Chatter_composer"
t-att-class="{ 'o-bordered': chatter.hasExternalBorder }"
composerLocalId="chatter.thread.composer.localId"
+ hasFollowers="true"
hasMentionSuggestionsBelowPosition="true"
isCompact="false"
isExpandable="true"
diff --git a/addons/mail/static/src/components/composer/composer.js b/addons/mail/static/src/components/composer/composer.js
index 510a5ac6038..bc46cd72d1d 100644
--- a/addons/mail/static/src/components/composer/composer.js
+++ b/addons/mail/static/src/components/composer/composer.js
@@ -3,6 +3,7 @@ odoo.define('mail/static/src/components/composer/composer.js', function (require
const components = {
AttachmentList: require('mail/static/src/components/attachment_list/attachment_list.js'),
+ ComposerSuggestedRecipientList: require('mail/static/src/components/composer_suggested_recipient_list/composer_suggested_recipient_list.js'),
DropZone: require('mail/static/src/components/drop_zone/drop_zone.js'),
EmojisPopover: require('mail/static/src/components/emojis_popover/emojis_popover.js'),
FileUploader: require('mail/static/src/components/file_uploader/file_uploader.js'),
@@ -190,7 +191,6 @@ class Composer extends Component {
}
return;
}
- // TODO: take suggested recipients into account (task-2283356)
await this.composer.postMessage();
// TODO: we might need to remove trigger and use the store to wait for the post rpc to be done
// task-2252858
diff --git a/addons/mail/static/src/components/composer/composer.scss b/addons/mail/static/src/components/composer/composer.scss
index 7ba50371577..37250a2ffd7 100644
--- a/addons/mail/static/src/components/composer/composer.scss
+++ b/addons/mail/static/src/components/composer/composer.scss
@@ -13,7 +13,7 @@
&.o-has-current-partner-avatar {
grid-template-columns: 50px 1fr;
- padding: map-get($spacers, 0) map-get($spacers, 3) map-get($spacers, 1) map-get($spacers, 1);
+ padding: map-get($spacers, 3) map-get($spacers, 3) map-get($spacers, 4) map-get($spacers, 1);
&:not(.o-has-footer) {
padding-bottom: 20px;
diff --git a/addons/mail/static/src/components/composer/composer.xml b/addons/mail/static/src/components/composer/composer.xml
index 61694e5aaf1..be947b72b3f 100644
--- a/addons/mail/static/src/components/composer/composer.xml
+++ b/addons/mail/static/src/components/composer/composer.xml
@@ -46,6 +46,9 @@
</t>
</b>
</small>
+ <ComposerSuggestedRecipientList
+ threadLocalId="composer.thread.localId"
+ />
</t>
</div>
</t>
diff --git a/addons/mail/static/src/components/composer_suggested_recipient/composer_suggested_recipient.js b/addons/mail/static/src/components/composer_suggested_recipient/composer_suggested_recipient.js
new file mode 100644
index 00000000000..cbc215a3b7e
--- /dev/null
+++ b/addons/mail/static/src/components/composer_suggested_recipient/composer_suggested_recipient.js
@@ -0,0 +1,163 @@
+odoo.define('mail/static/src/components/composer_suggested_recipient/composer_suggested_recipient.js', function (require) {
+'use strict';
+
+const useStore = require('mail/static/src/component_hooks/use_store/use_store.js');
+
+const { FormViewDialog } = require('web.view_dialogs');
+const { ComponentAdapter } = require('web.OwlCompatibility');
+
+const { Component } = owl;
+const { useRef } = owl.hooks;
+
+class FormViewDialogComponentAdapter extends ComponentAdapter {
+
+ renderWidget() {
+ // Ensure the dialog is properly reconstructed. Without this line, it is
+ // impossible to open the dialog again after having it closed a first
+ // time, because the DOM of the dialog has disappeared.
+ return this.willStart();
+ }
+
+}
+
+const components = {
+ FormViewDialogComponentAdapter,
+};
+
+class ComposerSuggestedRecipient extends Component {
+
+ constructor(...args) {
+ super(...args);
+ this.id = _.uniqueId('o_ComposerSuggestedRecipient_');
+
+ useStore(props => {
+ const suggestedRecipientInfo = this.env.models['mail.suggested_recipient_info'].get(props.suggestedRecipientLocalId);
+ const partner = suggestedRecipientInfo && suggestedRecipientInfo.partner;
+ return {
+ partner: partner && partner.__state,
+ suggestedRecipientInfo: suggestedRecipientInfo && suggestedRecipientInfo.__state,
+ };
+ });
+ /**
+ * Form view dialog class. Useful to reference it in the template.
+ */
+ this.FormViewDialog = FormViewDialog;
+ /**
+ * Reference of the checkbox. Useful to know whether it was checked or
+ * not, to properly update the corresponding state in the record or to
+ * prompt the user with the partner creation dialog.
+ */
+ this._checkboxRef = useRef('checkbox');
+ /**
+ * Reference of the partner creation dialog. Useful to open it, for
+ * compatibility with old code.
+ */
+ this._dialogRef = useRef('dialog');
+ /**
+ * Whether the dialog is currently open. `_dialogRef` cannot be trusted
+ * to know if the dialog is open due to manually calling `open` and
+ * potential out of sync with component adapter.
+ */
+ this._isDialogOpen = false;
+ this._onDialogSaved = this._onDialogSaved.bind(this);
+ }
+
+ mounted() {
+ this._update();
+ }
+
+ patched() {
+ this._update();
+ }
+
+ //--------------------------------------------------------------------------
+ // Public
+ //--------------------------------------------------------------------------
+
+ /**
+ * @returns {string|undefined}
+ */
+ get ADD_AS_RECIPIENT_AND_FOLLOWER_REASON() {
+ if (!this.suggestedRecipientInfo) {
+ return undefined;
+ }
+ return this.env._t(_.str.sprintf(
+ "Add as recipient and follower (reason: %s)",
+ this.suggestedRecipientInfo.reason
+ ));
+ }
+
+ /**
+ * @returns {string}
+ */
+ get PLEASE_COMPLETE_CUSTOMER_S_INFORMATION() {
+ return this.env._t("Please complete customer's information");
+ }
+
+ /**
+ * @returns {mail.suggested_recipient_info}
+ */
+ get suggestedRecipientInfo() {
+ return this.env.models['mail.suggested_recipient_info'].get(this.props.suggestedRecipientInfoLocalId);
+ }
+
+ //--------------------------------------------------------------------------
+ // Private
+ //--------------------------------------------------------------------------
+
+ /**
+ * @private
+ */
+ _update() {
+ if (this._checkboxRef.el && this.suggestedRecipientInfo) {
+ this._checkboxRef.el.checked = this.suggestedRecipientInfo.isSelected;
+ }
+ }
+
+ //--------------------------------------------------------------------------
+ // Handler
+ //--------------------------------------------------------------------------
+
+ /**
+ * @private
+ */
+ _onChangeCheckbox() {
+ const isChecked = this._checkboxRef.el.checked;
+ this.suggestedRecipientInfo.update({ isSelected: isChecked });
+ if (!this.suggestedRecipientInfo.partner) {
+ // Recipients must always be partners. On selecting a suggested
+ // recipient that does not have a partner, the partner creation form
+ // should be opened.
+ if (isChecked && this._dialogRef && !this._isDialogOpen) {
+ this._isDialogOpen = true;
+ this._dialogRef.comp.widget.on('closed', this, () => {
+ this._isDialogOpen = false;
+ });
+ this._dialogRef.comp.widget.open();
+ }
+ }
+ }
+
+ /**
+ * @private
+ */
+ _onDialogSaved() {
+ const thread = this.suggestedRecipientInfo && this.suggestedRecipientInfo.thread;
+ if (!thread) {
+ return;
+ }
+ thread.fetchAndUpdateSuggestedRecipients();
+ }
+}
+
+Object.assign(ComposerSuggestedRecipient, {
+ components,
+ props: {
+ suggestedRecipientInfoLocalId: String,
+ },
+ template: 'mail.ComposerSuggestedRecipient',
+});
+
+return ComposerSuggestedRecipient;
+
+});
diff --git a/addons/mail/static/src/components/composer_suggested_recipient/composer_suggested_recipient.scss b/addons/mail/static/src/components/composer_suggested_recipient/composer_suggested_recipient.scss
new file mode 100644
index 00000000000..c8b6b5adaba
--- /dev/null
+++ b/addons/mail/static/src/components/composer_suggested_recipient/composer_suggested_recipient.scss
@@ -0,0 +1,5 @@
+// Dirty fix: clear modal-body padding, otherwise it create space inside the
+// suggested_recipient list.
+.o_ComposerSuggestedRecipient .modal-body {
+ padding: 0;
+}
diff --git a/addons/mail/static/src/components/composer_suggested_recipient/composer_suggested_recipient.xml b/addons/mail/static/src/components/composer_suggested_recipient/composer_suggested_recipient.xml
new file mode 100644
index 00000000000..ae65b2bb77c
--- /dev/null
+++ b/addons/mail/static/src/components/composer_suggested_recipient/composer_suggested_recipient.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<templates xml:space="preserve">
+ <t t-name="mail.ComposerSuggestedRecipient" owl="1">
+ <div class="o_ComposerSuggestedRecipient" t-att-title="ADD_AS_RECIPIENT_AND_FOLLOWER_REASON">
+ <t t-if="suggestedRecipientInfo">
+ <div class="custom-control custom-checkbox">
+ <input t-attf-id="{{ id }}_checkbox" class="custom-control-input" type="checkbox" t-att-checked="suggestedRecipientInfo.isSelected ? 'checked' : undefined" t-on-change="_onChangeCheckbox" t-ref="checkbox" />
+ <label class="custom-control-label" t-attf-for="{{ id }}_checkbox">
+ <t t-if="suggestedRecipientInfo.name">
+ <t t-esc="suggestedRecipientInfo.name"/>
+ </t>
+ <t t-if="suggestedRecipientInfo.email">
+ (<t t-esc="suggestedRecipientInfo.email"/>)
+ </t>
+ </label>
+ </div>
+ <t t-if="!suggestedRecipientInfo.partner">
+ <FormViewDialogComponentAdapter
+ Component="FormViewDialog"
+ params="{
+ context: {
+ active_id: suggestedRecipientInfo.thread.id,
+ active_model: 'mail.compose.message',
+ default_email: suggestedRecipientInfo.email,
+ default_name: suggestedRecipientInfo.name,
+ force_email: true,
+ ref: 'compound_context',
+ },
+ disable_multiple_selection: true,
+ on_saved: _onDialogSaved,
+ res_id: false,
+ res_model: 'res.partner',
+ title: PLEASE_COMPLETE_CUSTOMER_S_INFORMATION,
+ }"
+ t-ref="dialog"
+ />
+ </t>
+ </t>
+ </div>
+ </t>
+</templates>
diff --git a/addons/mail/static/src/components/composer_suggested_recipient_list/composer_suggested_recipient_list.js b/addons/mail/static/src/components/composer_suggested_recipient_list/composer_suggested_recipient_list.js
new file mode 100644
index 00000000000..330072960bd
--- /dev/null
+++ b/addons/mail/static/src/components/composer_suggested_recipient_list/composer_suggested_recipient_list.js
@@ -0,0 +1,72 @@
+odoo.define('mail/static/src/components/composer_suggested_recipient_list/composer_suggested_recipient_list.js', function (require) {
+'use strict';
+
+const useStore = require('mail/static/src/component_hooks/use_store/use_store.js');
+
+const { Component } = owl;
+const { useState } = owl.hooks;
+
+const components = {
+ ComposerSuggestedRecipient: require('mail/static/src/components/composer_suggested_recipient/composer_suggested_recipient.js'),
+};
+
+class ComposerSuggestedRecipientList extends Component {
+
+ /**
+ * @override
+ */
+ constructor(...args) {
+ super(...args);
+ this.state = useState({
+ hasShowMoreButton: false,
+ });
+
+ useStore(props => {
+ const thread = this.env.models['mail.thread'].get(props.threadLocalId);
+ return {
+ thread: thread ? thread.__state : undefined,
+ };
+ });
+ }
+
+ //--------------------------------------------------------------------------
+ // Public
+ //--------------------------------------------------------------------------
+
+ /**
+ * @returns {mail.thread}
+ */
+ get thread() {
+ return this.env.models['mail.thread'].get(this.props.threadLocalId);
+ }
+
+ //--------------------------------------------------------------------------
+ // Handlers
+ //--------------------------------------------------------------------------
+
+ /**
+ * @private
+ */
+ _onClickShowLess(ev) {
+ this.state.hasShowMoreButton = false;
+ }
+
+ /**
+ * @private
+ */
+ _onClickShowMore(ev) {
+ this.state.hasShowMoreButton = true;
+ }
+
+}
+
+Object.assign(ComposerSuggestedRecipientList, {
+ components,
+ props: {
+ threadLocalId: String,
+ },
+ template: 'mail.ComposerSuggestedRecipientList',
+});
+
+return ComposerSuggestedRecipientList;
+});
diff --git a/addons/mail/static/src/components/composer_suggested_recipient_list/composer_suggested_recipient_list.scss b/addons/mail/static/src/components/composer_suggested_recipient_list/composer_suggested_recipient_list.scss
new file mode 100644
index 00000000000..4e1b4dd76ea
--- /dev/null
+++ b/addons/mail/static/src/components/composer_suggested_recipient_list/composer_suggested_recipient_list.scss
@@ -0,0 +1,3 @@
+.o_ComposerSuggestedRecipientList {
+ margin-bottom: map-get($spacers, 2);
+}
diff --git a/addons/mail/static/src/components/composer_suggested_recipient_list/composer_suggested_recipient_list.xml b/addons/mail/static/src/components/composer_suggested_recipient_list/composer_suggested_recipient_list.xml
new file mode 100644
index 00000000000..c78570dbe24
--- /dev/null
+++ b/addons/mail/static/src/components/composer_suggested_recipient_list/composer_suggested_recipient_list.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<templates xml:space="preserve">
+ <t t-name="mail.ComposerSuggestedRecipientList" owl="1">
+ <div class="o_ComposerSuggestedRecipientList">
+ <t t-if="thread">
+ <t t-foreach="state.hasShowMoreButton ? thread.suggestedRecipientInfoList : thread.suggestedRecipientInfoList.slice(0,3)" t-as="recipientInfo" t-key="recipientInfo.localId">
+ <ComposerSuggestedRecipient
+ suggestedRecipientInfoLocalId="recipientInfo.localId"
+ />
+ </t>
+ <t t-if="thread.suggestedRecipientInfoList.length > 3">
+ <t t-if="!state.hasShowMoreButton" >
+ <button class="o_ComposerSuggestedRecipientList_showMore btn btn-sm btn-link" t-on-click="_onClickShowMore">
+ Show more
+ </button>
+ </t>
+ <t t-else="">
+ <button class="o_ComposerSuggestedRecipientList_showLess btn btn-sm btn-link" t-on-click="_onClickShowLess">
+ Show less
+ </button>
+ </t>
+ </t>
+ </t>
+ </div>
+ </t>
+</templates>