[IMP] mail: add rtc information to the member list

This commit adds call icons to the channel member list and
sorts this list as well as the call participant cards of the call view
based on call state (in call, raising hand, invited).

task-3251025

closes odoo/odoo#117995

Signed-off-by: Alexandre Kühn (aku) <aku@odoo.com>
This commit is contained in:
Thanh Dodeur
2023-04-13 15:26:27 +02:00
parent de7d7a71ea
commit 945f3cce9c
8 changed files with 67 additions and 20 deletions
+19 -1
View File
@@ -378,7 +378,25 @@ export class Thread {
orderedOnlineMembers.push(member);
}
}
return orderedOnlineMembers.sort((m1, m2) => (m1.persona.name < m2.persona.name ? -1 : 1));
return orderedOnlineMembers.sort((m1, m2) => {
const m1HasRtc = Boolean(m1.rtcSession);
const m2HasRtc = Boolean(m2.rtcSession);
if (m1HasRtc === m2HasRtc) {
/**
* If raisingHand is falsy, it gets an Infinity value so that when
* we sort by [oldest/lowest-value]-first, falsy values end up last.
*/
const m1RaisingValue = m1.rtcSession?.raisingHand || Infinity;
const m2RaisingValue = m2.rtcSession?.raisingHand || Infinity;
if (m1HasRtc && m1RaisingValue !== m2RaisingValue) {
return m1RaisingValue - m2RaisingValue;
} else {
return m1.persona.name?.localeCompare(m2.persona.name) ?? 1;
}
} else {
return m2HasRtc - m1HasRtc;
}
});
}
get unknownMembersCount() {
@@ -33,7 +33,13 @@
<ImStatus className="'position-absolute bottom-0 end-0'" persona="member.persona"/>
</div>
<span class="ms-2 text-truncate" t-esc="member.persona.name"/>
<span t-if="member.rtcSession?.isRaisingHand" class="p-2 fa ms-auto fa-hand-paper-o"/>
<span class="ms-auto">
<span t-if="props.thread.invitedMemberIds.has(member.id)" class="p-1 fa fa-user-plus"/>
<span t-if="member.rtcSession?.isSelfMuted and !member.rtcSession?.isDeaf" class="p-1 fa fa-microphone-slash"/>
<span t-elif="member.rtcSession?.isDeaf" class="p-1 fa fa-deaf"/>
<span t-if="member.rtcSession?.raisingHand" class="p-1 fa fa-hand-paper-o"/>
</span>
</div>
</t>
+25 -5
View File
@@ -72,25 +72,45 @@ export class Call extends Component {
}
get visibleCards() {
const cards = [];
const raisingHandCards = [];
const sessionCards = [];
const invitationCards = [];
const filterVideos = this.userSettings.showOnlyVideo && this.props.thread.videoCount > 0;
for (const session of Object.values(this.props.thread.rtcSessions)) {
if (!filterVideos || session.videoStream) {
cards.push({
const data = {
key: "session_" + session.id,
session,
});
};
if (session.raisingHand) {
raisingHandCards.push(data);
} else {
sessionCards.push(data);
}
}
}
if (!filterVideos) {
for (const memberId of this.props.thread.invitedMemberIds) {
cards.push({
invitationCards.push({
key: "member_" + memberId,
member: this.store.channelMembers[memberId],
});
}
}
return cards;
raisingHandCards.sort((c1, c2) => {
return c1.session.raisingHand - c2.session.raisingHand;
});
sessionCards.sort((c1, c2) => {
return (
c1.session.channelMember?.persona?.name?.localeCompare(
c2.session.channelMember?.persona?.name
) ?? 1
);
});
invitationCards.sort((c1, c2) => {
return c1.member.persona?.name?.localeCompare(c2.member.persona?.name) ?? 1;
});
return raisingHandCards.concat(sessionCards, invitationCards);
}
get visibleMainCards() {
@@ -36,7 +36,7 @@ export class CallActionList extends Component {
}
async onClickRaiseHand(ev) {
this.rtc.raiseHand(!this.rtc.state.selfSession.isRaisingHand);
this.rtc.raiseHand(!this.rtc.state.selfSession.raisingHand);
}
/**
@@ -37,10 +37,10 @@
}"/>
</div>
</button>
<t t-if="rtc.state?.selfSession.isRaisingHand" t-set="handText">Lower hand</t>
<t t-if="rtc.state?.selfSession.raisingHand" t-set="handText">Lower hand</t>
<t t-else="" t-set="handText">Raise hand</t>
<button class="btn d-flex m-1 border-0 rounded-circle shadow-none opacity-100 opacity-75-hover"
t-att-class="{ 'p-2': isSmall, 'p-3': !isSmall, 'bg-500' : rtc.state?.selfSession.isRaisingHand }"
t-att-class="{ 'p-2': isSmall, 'p-3': !isSmall, 'bg-500' : rtc.state?.selfSession.raisingHand }"
t-att-aria-label="handText"
t-att-title="handText"
t-on-click="onClickRaiseHand">
@@ -36,7 +36,7 @@
</small>
</span>
<div class="o-mail-CallParticipantCard-overlay position-absolute top-0 end-0 d-flex flex-row-reverse">
<span t-if="rtcSession.isRaisingHand" class="d-flex flex-column justify-content-center me-1 rounded-circle bg-500" t-att-class="{'o-minimized p-1': props.minimized, 'p-2': !props.minimized }" title="raising hand" aria-label="raising hand">
<span t-if="rtcSession.raisingHand" class="d-flex flex-column justify-content-center me-1 rounded-circle bg-500" t-att-class="{'o-minimized p-1': props.minimized, 'p-2': !props.minimized }" title="raising hand" aria-label="raising hand">
<i class="fa fa-hand-paper-o"/>
</span>
<span t-if="rtcSession.isSelfMuted and !rtcSession.isDeaf" class="d-flex flex-column justify-content-center me-1 rounded-circle bg-900" t-att-class="{'o-minimized p-1': props.minimized, 'p-2': !props.minimized }" title="muted" aria-label="muted">
+10 -8
View File
@@ -440,10 +440,12 @@ export class Rtc {
this.disconnect(session);
break;
case "raise_hand":
Object.assign(session, { isRaisingHand: payload.active });
Object.assign(session, {
raisingHand: payload.active ? new Date() : undefined,
});
// eslint-disable-next-line no-case-declarations
const notificationId = "raise_hand_" + session.id;
if (session.isRaisingHand) {
if (session.raisingHand) {
this.addCallNotification({
id: notificationId,
text: sprintf(_t("%s raised a hand"), session.name),
@@ -691,7 +693,7 @@ export class Rtc {
},
});
await this.notify([session], "raise_hand", {
active: this.state.selfSession.isRaisingHand,
active: Boolean(this.state.selfSession.raisingHand),
});
} catch (e) {
if (!(e instanceof DOMException) || e.name !== "OperationError") {
@@ -929,10 +931,10 @@ export class Rtc {
delete session.packetsSent;
delete session.dtlsState;
delete session.iceState;
delete session.raisingHand;
delete session.logStep;
delete session.audioError;
delete session.videoError;
session.isRaisingHand = false;
session.isTalking = false;
this.removeVideoFromSession(session);
session.dataChannel?.close();
@@ -1057,15 +1059,15 @@ export class Rtc {
}
/**
* @param {Boolean} isRaisingHand
* @param {Boolean} raise
*/
async raiseHand(isRaisingHand) {
async raiseHand(raise) {
if (!this.state.selfSession || !this.state.channel) {
return;
}
this.state.selfSession.isRaisingHand = isRaisingHand;
this.state.selfSession.raisingHand = raise ? new Date() : undefined;
await this.notify(Object.values(this.state.channel.rtcSessions), "raise_hand", {
active: this.state.selfSession.isRaisingHand,
active: this.state.selfSession.raisingHand,
});
}
@@ -20,11 +20,12 @@ export class RtcSession {
dataChannel;
audioError;
videoError;
isRaisingHand;
isTalking;
localVolume;
/** @type {RTCPeerConnection} */
peerConnection;
/** @type {Date|undefined} */
raisingHand;
videoComponentCount = 0;
/** @type {MediaStream} */
videoStream;