[FIX] mail: allow focus on any rtc session

Before this commit, in the rtc call viewer, it was only possible to
focus videos, this commit changes that by allowing to focus any rtc
session.

This is a first step in improving the focusing and layout system.

This commit also changes the call viewer and call participant card
templates to ensure that they remain consistent when focusing non-video
sessions, which also fixes a bug which caused avatars to not be round
when the displayed image had an aspect ratio other than 1.

closes odoo/odoo#79862

X-original-commit: d49407423164c44e86558cd2fc4f6c3ae47aed25
Signed-off-by: Sébastien Theys (seb) <seb@odoo.com>
This commit is contained in:
Thanh Dodeur
2021-11-16 14:34:25 +00:00
parent ebf8c735cf
commit 7b298178e3
9 changed files with 59 additions and 70 deletions
@@ -8,16 +8,21 @@
position: relative;
}
.o_RtcCallParticipantCard_avatar {
.o_RtcCallParticipantCard_avatarFrame {
display: flex;
justify-content: center;
max-height: 100%;
user-select: none;
aspect-ratio: 16/9;
&.o-isMinimized {
aspect-ratio: 1;
}
}
.o_RtcCallParticipantCard_avatarImage {
max-height: 100px;
max-width: 100px;
max-height: #{"min(100%, 100px)"}; // interpolated as not supported by Sass
max-width: #{"min(100%, 100px)"};
aspect-ratio: 1;
object-fit: cover;
border: 5px solid gray('700');
&.o-isTalking {
@@ -74,6 +79,7 @@
max-width: 50%;
overflow: hidden;
bottom: 0;
left: 0;
}
.o_RtcCallParticipantCard_overlayTop {
@@ -110,8 +116,12 @@
.o_RtcCallParticipantCard {
border-radius: $o-mail-rounded-rectangle-border-radius-sm;
&.o-isClickable {
cursor: pointer;
}
&.o-isTalking {
box-shadow: inset 0 0 0 map-get($spacers, 2) darken($o-enterprise-primary-color, 5%);
box-shadow: inset 0 0 0 map-get($spacers, 1) darken($o-enterprise-primary-color, 5%);
}
&.o-isInvitation {
@@ -119,20 +129,19 @@
}
}
.o_RtcCallParticipantCard_avatarFrame {
border-radius: $o-mail-rounded-rectangle-border-radius-sm;
&:not(.o-isMinimized) {
background-color: $o-brand-secondary;
}
}
.o_RtcCallParticipantCard_connectionState {
color: theme-color('warning');
}
.o_RtcCallParticipantCard_container {
border-radius: $o-mail-rounded-rectangle-border-radius-sm;
&.o-canClick {
cursor: pointer;
}
&:not(.o-isMinimized) {
background-color: $o-brand-secondary;
}
}
.o_RtcCallParticipantCard_liveIndicator {
@@ -4,33 +4,30 @@
<t t-name="mail.RtcCallParticipantCard" owl="1">
<div class="o_RtcCallParticipantCard"
t-att-class="{
'o-isClickable': callParticipantCard.invitedGuest or callParticipantCard.invitedPartner or !callParticipantCard.isMinimized,
'o-isTalking': callParticipantCard and !callParticipantCard.isMinimized and callParticipantCard.isTalking,
'o-isInvitation': callParticipantCard and callParticipantCard.isInvitation,
}"
t-on-contextmenu="_onContextMenu"
>
<t t-if="callParticipantCard">
<div class="o_RtcCallParticipantCard_container"
t-att-class="{
'o-isMinimized': callParticipantCard.isMinimized,
'o-canClick': !callParticipantCard.isInvitation,
}"
<div class="o_RtcCallParticipantCard_container align-items-center"
t-att-title="callParticipantCard.name"
t-att-aria-label="callParticipantCard.name"
t-on-click="callParticipantCard.onClick"
>
<!-- card -->
<t t-if="callParticipantCard.rtcSession and callParticipantCard.rtcSession.videoStream">
<RtcVideo t-on-click="callParticipantCard.onClickVideo" rtcSessionLocalId="callParticipantCard.rtcSession.localId"/>
<RtcVideo rtcSessionLocalId="callParticipantCard.rtcSession.localId"/>
</t>
<t t-else="">
<div class="o_RtcCallParticipantCard_avatar" draggable="false">
<div class="o_RtcCallParticipantCard_avatarFrame mh-100 mw-100 h-100 align-items-center justify-content-center" t-att-class="{ 'o-isMinimized': callParticipantCard.isMinimized }" draggable="false">
<img alt="Avatar"
t-att-class="{
'o-isTalking': callParticipantCard.isTalking,
'o-isInvitation': callParticipantCard.isInvitation,
}"
class="o_RtcCallParticipantCard_avatarImage rounded-circle"
class="o_RtcCallParticipantCard_avatarImage h-100 rounded-circle"
t-att-src="callParticipantCard.avatarUrl"
draggable="false"
/>
@@ -79,14 +76,12 @@
<!-- volume popover -->
<t t-if="!callParticipantCard.rtcSession.isOwnSession">
<i class="o_RtcCallParticipantCard_volumeMenuAnchor">
<Popover>
<i t-ref="volumeMenuAnchor"/>
<t t-set="opened">
<input type="range" min="0.0" max="1" step="0.01" t-att-value="callParticipantCard.rtcSession.volume" t-on-change="callParticipantCard.onChangeVolume"/>
</t>
</Popover>
</i>
<Popover>
<i class="o_RtcCallParticipantCard_volumeMenuAnchor" t-on-click="callParticipantCard.onClickVolumeAnchor" t-ref="volumeMenuAnchor"/>
<t t-set="opened">
<input type="range" min="0.0" max="1" step="0.01" t-att-value="callParticipantCard.rtcSession.volume" t-on-change="callParticipantCard.onChangeVolume"/>
</t>
</Popover>
</t>
</t>
@@ -77,12 +77,7 @@
aspect-ratio: 16/9;
}
.o_RtcCallViewer_mainParticipant {
width: 100%;
}
.o_RtcCallViewer_mainParticipantContainer {
max-height: 100%;
display: flex;
flex-grow: 1;
}
@@ -11,9 +11,9 @@
<!-- Call members display -->
<div class="o_RtcCallViewer_participantContainer" t-on-click="rtcCallViewer.onClick" t-on-mousemove="rtcCallViewer.onMouseMove">
<t t-if="rtcCallViewer.layout !== 'tiled'">
<div class="o_RtcCallViewer_mainParticipantContainer">
<div class="o_RtcCallViewer_mainParticipantContainer justify-content-center mw-100 mh-100">
<t t-if="rtcCallViewer.mainParticipantCard">
<RtcCallParticipantCard class="o_RtcCallViewer_participantCard o_RtcCallViewer_mainParticipant" callParticipantCardLocalId="rtcCallViewer.mainParticipantCard.localId"/>
<RtcCallParticipantCard class="o_RtcCallViewer_participantCard w-100" callParticipantCardLocalId="rtcCallViewer.mainParticipantCard.localId"/>
</t>
</div>
</t>
@@ -163,7 +163,7 @@ function factory(dependencies) {
id: sessionId,
});
const focusedSessionId = this.focusedRtcSession && this.focusedRtcSession.id;
if (!sessionId || focusedSessionId === sessionId || !rtcSession.videoStream) {
if (!sessionId || focusedSessionId === sessionId) {
this.update({ focusedRtcSession: unlink() });
return;
}
@@ -304,9 +304,7 @@ function factory(dependencies) {
isCausal: true,
readonly: true,
}),
focusedRtcSession: one2one('mail.rtc_session', {
inverse: 'focusingMessaging',
}),
focusedRtcSession: one2one('mail.rtc_session'),
/**
* Mailbox History.
*/
@@ -2,6 +2,7 @@
import { registerNewModel } from '@mail/model/model_core';
import { attr, many2one, one2one } from '@mail/model/model_field';
import { isEventHandled, markEventHandled } from '@mail/utils/utils';
function factory(dependencies) {
@@ -14,7 +15,7 @@ function factory(dependencies) {
super._created();
this.onChangeVolume = this.onChangeVolume.bind(this);
this.onClick = this.onClick.bind(this);
this.onClickVideo = this.onClickVideo.bind(this);
this.onClickVolumeAnchor = this.onClickVolumeAnchor.bind(this);
}
//----------------------------------------------------------------------
@@ -32,7 +33,13 @@ function factory(dependencies) {
* @param {MouseEvent} ev
*/
async onClick(ev) {
if (isEventHandled(ev, 'CallParticipantCard.clickVolumeAnchor')) {
return;
}
if (!this.invitedPartner && !this.invitedGuest) {
if (!this.isMinimized) {
this.messaging.toggleFocusedRtcSession(this.rtcSession.id);
}
return;
}
const channel = this.channel;
@@ -51,11 +58,12 @@ function factory(dependencies) {
}
/**
* Handled by the popover component.
*
* @param {MouseEvent} ev
*/
async onClickVideo(ev) {
ev.stopPropagation();
this.messaging.toggleFocusedRtcSession(this.rtcSession.id);
async onClickVolumeAnchor(ev) {
markEventHandled(ev, 'CallParticipantCard.clickVolumeAnchor');
}
//----------------------------------------------------------------------
@@ -181,10 +181,13 @@ function factory(dependencies) {
if (!this.threadView) {
return 'tiled';
}
if (this.isMinimized) {
return 'tiled';
}
if (!this.threadView.thread.rtc) {
return 'tiled';
}
if (!this.threadView.thread.videoCount || !this.mainParticipantCard) {
if (!this.mainParticipantCard) {
return 'tiled';
}
if (
@@ -2,6 +2,7 @@
import { registerNewModel } from '@mail/model/model_core';
import { attr, one2one } from '@mail/model/model_field';
import { clear } from '@mail/model/model_field_command';
function factory(dependencies) {
@@ -35,6 +36,9 @@ function factory(dependencies) {
this.callViewer.update({
filterVideoGrid: true,
});
if (this.messaging.focusedRtcSession && !this.messaging.focusedRtcSession.videoStream) {
this.messaging.update({ focusedRtcSession: clear() });
}
break;
}
}
@@ -4,8 +4,7 @@ import { browser } from "@web/core/browser/browser";
import { registerNewModel } from '@mail/model/model_core';
import { attr, many2one, one2one, one2many } from '@mail/model/model_field';
import { clear, unlink } from '@mail/model/model_field_command';
import { OnChange } from '@mail/model/model_onchange';
import { clear } from '@mail/model/model_field_command';
function factory(dependencies) {
@@ -283,15 +282,6 @@ function factory(dependencies) {
}, delay);
}
/**
* @private
*/
_onChangeVideoStream() {
if (!this.videoStream) {
this.update({ focusingMessaging: unlink() });
}
}
/**
* cleanly removes the audio stream of the session
*
@@ -349,13 +339,6 @@ function factory(dependencies) {
connectionState: attr({
default: 'Waiting for the peer to send a RTC offer',
}),
/**
* If this field is set, this session is 'focused' which means that it will
* be displayed as the main session.
*/
focusingMessaging: one2one('mail.messaging', {
inverse: 'focusedRtcSession',
}),
guest: many2one('mail.guest', {
inverse: 'rtcSessions',
}),
@@ -470,12 +453,6 @@ function factory(dependencies) {
compute: '_computeVolume',
}),
};
RtcSession.onChanges = [
new OnChange({
dependencies: ['videoStream'],
methodName: '_onChangeVideoStream',
}),
];
RtcSession.identifyingFields = ['id'];
RtcSession.modelName = 'mail.rtc_session';