diff --git a/addons/mail/static/src/components/popover_view/popover_view.scss b/addons/mail/static/src/components/popover_view/popover_view.scss
index e0f99263752..4b6599ce46f 100644
--- a/addons/mail/static/src/components/popover_view/popover_view.scss
+++ b/addons/mail/static/src/components/popover_view/popover_view.scss
@@ -7,6 +7,11 @@
border: $border-width solid;
}
+.o_PopoverView_channelInvitationForm {
+ width: Min(95vw, 400px);
+ max-height: Min(calc(100vh - 140px), 530px);
+}
+
// ------------------------------------------------------------------
// Style
// ------------------------------------------------------------------
diff --git a/addons/mail/static/src/components/popover_view/popover_view.xml b/addons/mail/static/src/components/popover_view/popover_view.xml
index 1f6b81b57db..7be1648da80 100644
--- a/addons/mail/static/src/components/popover_view/popover_view.xml
+++ b/addons/mail/static/src/components/popover_view/popover_view.xml
@@ -4,7 +4,7 @@
-
+
diff --git a/addons/mail/static/src/models/channel_invitation_form/channel_invitation_form.js b/addons/mail/static/src/models/channel_invitation_form/channel_invitation_form.js
index 274913df0cb..37d12545ae3 100644
--- a/addons/mail/static/src/models/channel_invitation_form/channel_invitation_form.js
+++ b/addons/mail/static/src/models/channel_invitation_form/channel_invitation_form.js
@@ -2,7 +2,7 @@
import { registerNewModel } from '@mail/model/model_core';
import { attr, many2many, many2one, one2one } from '@mail/model/model_field';
-import { clear, insertAndReplace, link, unlink, unlinkAll } from '@mail/model/model_field_command';
+import { clear, insertAndReplace, link, replace, unlink, unlinkAll } from '@mail/model/model_field_command';
import { cleanSearchTerm } from '@mail/utils/utils';
function factory(dependencies) {
@@ -142,14 +142,19 @@ function factory(dependencies) {
},
{ shadow: true }
);
+ if (!this.exists()) {
+ return;
+ }
this.update({
searchResultCount: count,
selectablePartners: insertAndReplace(partnersData.map(partnerData => this.messaging.models['mail.partner'].convertData(partnerData))),
});
} finally {
- this.update({ hasSearchRpcInProgress: false });
- if (this.hasPendingSearchRpc) {
- this.searchPartnersToInvite();
+ if (this.exists()) {
+ this.update({ hasSearchRpcInProgress: false });
+ if (this.hasPendingSearchRpc) {
+ this.searchPartnersToInvite();
+ }
}
}
}
@@ -175,9 +180,27 @@ function factory(dependencies) {
return this.env._t("Invite to Channel");
}
+ /**
+ * @private
+ * @returns {FieldCommand}
+ */
+ _computeThread() {
+ if (this.threadView && this.threadView.thread) {
+ return replace(this.threadView.thread);
+ }
+ if (this.chatWindow && this.chatWindow.thread) {
+ return replace(this.chatWindow.thread);
+ }
+ return clear();
+ }
+
}
ChannelInvitationForm.fields = {
+ chatWindow: one2one('mail.chat_window', {
+ inverse: 'channelInvitationForm',
+ readonly: true,
+ }),
/**
* States the OWL component of this channel invitation form.
* Useful to be able to close it with popover trigger, or to know when
@@ -240,8 +263,8 @@ function factory(dependencies) {
* States the thread on which this list operates (if any).
*/
thread: many2one('mail.thread', {
+ compute: '_computeThread',
readonly: true,
- related: 'threadView.thread',
required: true,
}),
/**
@@ -252,7 +275,7 @@ function factory(dependencies) {
readonly: true,
}),
};
- ChannelInvitationForm.identifyingFields = ['threadView'];
+ ChannelInvitationForm.identifyingFields = [['chatWindow', 'threadView']];
ChannelInvitationForm.modelName = 'mail.channel_invitation_form';
return ChannelInvitationForm;
diff --git a/addons/mail/static/src/models/chat_window/chat_window.js b/addons/mail/static/src/models/chat_window/chat_window.js
index 2120bb08292..b0f8647d8c6 100644
--- a/addons/mail/static/src/models/chat_window/chat_window.js
+++ b/addons/mail/static/src/models/chat_window/chat_window.js
@@ -15,7 +15,9 @@ function factory(dependencies) {
_created() {
super._created();
// Bind necessary until OWL supports arrow function in handlers: https://github.com/odoo/owl/issues/876
+ this.onClickHideInviteForm = this.onClickHideInviteForm.bind(this);
this.onClickHideMemberList = this.onClickHideMemberList.bind(this);
+ this.onClickShowInviteForm = this.onClickShowInviteForm.bind(this);
this.onClickShowMemberList = this.onClickShowMemberList.bind(this);
this.onFocusInNewMessageFormInput = this.onFocusInNewMessageFormInput.bind(this);
}
@@ -127,6 +129,16 @@ function factory(dependencies) {
this.manager.swap(this, lastVisible);
}
+ /**
+ * Handles click on the "stop adding users" button.
+ *
+ * @param {MouseEvent} ev
+ */
+ onClickHideInviteForm(ev) {
+ markEventHandled(ev, 'ChatWindow.onClickCommand');
+ this.update({ channelInvitationForm: clear() });
+ }
+
/**
* @param {MouseEvent} ev
*/
@@ -138,12 +150,33 @@ function factory(dependencies) {
}
}
+ /**
+ * Handles click on the "add users" button.
+ *
+ * @param {MouseEvent} ev
+ */
+ onClickShowInviteForm(ev) {
+ markEventHandled(ev, 'ChatWindow.onClickCommand');
+ this.update({
+ channelInvitationForm: insertAndReplace({
+ doFocusOnSearchInput: true,
+ }),
+ isMemberListOpened: false,
+ });
+ if (!this.messaging.isCurrentUserGuest) {
+ this.channelInvitationForm.searchPartnersToInvite();
+ }
+ }
+
/**
* @param {MouseEvent} ev
*/
onClickShowMemberList(ev) {
markEventHandled(ev, 'ChatWindow.onClickShowMemberList');
- this.update({ isMemberListOpened: true });
+ this.update({
+ channelInvitationForm: clear(),
+ isMemberListOpened: true,
+ });
}
/**
@@ -194,7 +227,18 @@ function factory(dependencies) {
* @returns {boolean}
*/
_computeHasCallButtons() {
- return this.thread && this.thread.rtcSessions.length === 0 && ['channel', 'chat', 'group'].includes(this.thread.channel_type);
+ return Boolean(this.thread) && this.thread.rtcSessions.length === 0 && ['channel', 'chat', 'group'].includes(this.thread.channel_type);
+ }
+
+ /**
+ * @private
+ * @returns {boolean}
+ */
+ _computeHasInviteFeature() {
+ return Boolean(
+ this.thread && this.thread.hasInviteFeature &&
+ this.messaging && this.messaging.device && this.messaging.device.isMobile
+ );
}
/**
@@ -241,7 +285,7 @@ function factory(dependencies) {
* @returns {boolean}
*/
_computeHasThreadView() {
- return this.isVisible && !this.isFolded && !!this.thread;
+ return this.isVisible && !this.isFolded && !!this.thread && !this.isMemberListOpened && !this.channelInvitationForm;
}
/**
@@ -368,6 +412,14 @@ function factory(dependencies) {
}
ChatWindow.fields = {
+ /**
+ * Determines the channel invitation form displayed by this chat window
+ * (if any). Only makes sense if hasInviteFeature is true.
+ */
+ channelInvitationForm: one2one('mail.channel_invitation_form', {
+ inverse: 'chatWindow',
+ isCausal: true,
+ }),
/**
* Determines whether the buttons to start a RTC call should be displayed.
*/
@@ -375,6 +427,12 @@ function factory(dependencies) {
default: false,
compute: '_computeHasCallButtons',
}),
+ /**
+ * States whether this chat window has the invite feature.
+ */
+ hasInviteFeature: attr({
+ compute: '_computeHasInviteFeature',
+ }),
/**
* Determines whether "new message form" should be displayed.
*/