[FIX] mail: add the ability to invite on mobile

Before this commit, it was impossible for a user to invite people to a channel
or a group DM.
This commit add the invite button on the chat windows header only on mobile.

Part of task-2634175

closes odoo/odoo#78267

X-original-commit: 56ab9ffea28dd5f22a74ba269cbd5552c42953e3
Signed-off-by: Alexandre Kühn (aku) <aku@odoo.com>
This commit is contained in:
Didier (did)
2021-10-12 19:17:43 +00:00
committed by Sébastien Theys
parent b57b884430
commit 2e2b49931f
9 changed files with 118 additions and 16 deletions
@@ -2,11 +2,6 @@
// Layout
// ------------------------------------------------------------------
.o_ChannelInvitationForm {
width: Min(95vw, 400px);
max-height: Min(calc(100vh - 140px), 530px);
}
.o_ChannelInvitationForm_selectablePartnerAvatarContainer {
width: 32px;
height: 32px;
@@ -7,6 +7,7 @@
bottom: 0;
display: flex;
flex-flow: column;
overflow: auto;
&:not(.o-mobile) {
max-width: 100%;
@@ -32,6 +33,10 @@
}
}
.o_ChatWindow_channelInvitationForm {
min-height: 450px; // allow flex shrink smaller than content (but not too small)
}
.o_ChatWindow_header {
flex: 0 0 auto;
}
@@ -23,7 +23,10 @@
<t t-if="chatWindow.thread and chatWindow.thread.hasMemberListFeature and chatWindow.isMemberListOpened">
<ChannelMemberList channelLocalId="chatWindow.thread.localId" class="bg-white"/>
</t>
<t t-if="chatWindow.threadView and !chatWindow.isMemberListOpened">
<t t-if="chatWindow.channelInvitationForm">
<ChannelInvitationForm class="o_ChatWindow_channelInvitationForm" localId="chatWindow.channelInvitationForm.localId"/>
</t>
<t t-if="chatWindow.threadView">
<ThreadView
class="o_ChatWindow_thread"
hasComposerCurrentPartnerAvatar="false"
@@ -51,6 +51,7 @@ export class ChatWindowHeader extends Component {
*/
_onClick(ev) {
if (
isEventHandled(ev, 'ChatWindow.onClickCommand') ||
isEventHandled(ev, 'ChatWindowHeader.ClickShiftNext') ||
isEventHandled(ev, 'ChatWindowHeader.ClickShiftPrev') ||
isEventHandled(ev, 'ChatWindow.onClickHideMemberList') ||
@@ -33,6 +33,18 @@
<i class="fa fa-phone"/>
</div>
</t>
<t t-if="chatWindow.hasInviteFeature">
<t t-if="!chatWindow.channelInvitationForm">
<div class="o_ChatWindowHeader_command" title="Add users" t-on-click="chatWindow.onClickShowInviteForm">
<i class="fa fa-lg fa-user-plus"/>
</div>
</t>
<t t-if="chatWindow.channelInvitationForm">
<div class="o_ChatWindowHeader_command" title="Stop adding users" t-on-click="chatWindow.onClickHideInviteForm">
<i class="fa fa-lg fa-user-plus"/>
</div>
</t>
</t>
<t t-if="chatWindow.thread and chatWindow.thread.hasMemberListFeature and !chatWindow.isMemberListOpened">
<div class="o_ChatWindowHeader_command o_ChatWindowHeader_commandShowMemberList" t-att-class="{ 'o-mobile': messaging.device.isMobile }" title="Show Member List" t-on-click="chatWindow.onClickShowMemberList">
<i class="fa fa-users"/>
@@ -7,6 +7,11 @@
border: $border-width solid;
}
.o_PopoverView_channelInvitationForm {
width: Min(95vw, 400px);
max-height: Min(calc(100vh - 140px), 530px);
}
// ------------------------------------------------------------------
// Style
// ------------------------------------------------------------------
@@ -4,7 +4,7 @@
<t t-name="mail.PopoverView" owl="1">
<div class="o_PopoverView">
<t t-if="popoverView">
<ChannelInvitationForm localId="popoverView.channelInvitationForm.localId"/>
<ChannelInvitationForm class="o_PopoverView_channelInvitationForm" localId="popoverView.channelInvitationForm.localId"/>
</t>
</div>
</t>
@@ -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;
@@ -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.
*/