diff --git a/addons/mail/static/src/components/call_participant_card/call_participant_card.xml b/addons/mail/static/src/components/call_participant_card/call_participant_card.xml
index 4886d9c8620..01d7544511e 100644
--- a/addons/mail/static/src/components/call_participant_card/call_participant_card.xml
+++ b/addons/mail/static/src/components/call_participant_card/call_participant_card.xml
@@ -6,10 +6,10 @@
@@ -37,7 +37,7 @@
-
+
diff --git a/addons/mail/static/src/models/call_participant_card.js b/addons/mail/static/src/models/call_participant_card.js
index 54054dda97d..0863d89e03e 100644
--- a/addons/mail/static/src/models/call_participant_card.js
+++ b/addons/mail/static/src/models/call_participant_card.js
@@ -9,7 +9,7 @@ import { sprintf } from '@web/core/utils/strings';
registerModel({
name: 'CallParticipantCard',
- identifyingFields: [['rtcSession', 'invitedMember'], ['callViewAsMainCard', 'callViewAsTile']],
+ identifyingFields: ['channelMember', ['callViewAsMainCard', 'callViewAsTile']],
recordMethods: {
/**
* @param {Event} ev
@@ -32,12 +32,12 @@ registerModel({
}
return;
}
- const channel = this.channel;
+ const channel = this.channelMember.channel.thread;
const channelData = await this.messaging.rpc(({
route: '/mail/rtc/channel/cancel_call_invitation',
params: {
- channel_id: this.channel.id,
- member_ids: this.invitedMember && [this.invitedMember.id],
+ channel_id: channel.id,
+ member_ids: [this.channelMember.id],
},
}));
if (!channel.exists()) {
@@ -66,21 +66,6 @@ registerModel({
}
this.volumeMenuAnchorRef.el.click();
},
- /**
- * @private
- * @returns {string}
- */
- _computeAvatarUrl() {
- if (!this.channel) {
- return;
- }
- if (this.rtcSession) {
- return this.rtcSession.channelMember.avatarUrl;
- }
- if (this.invitedMember) {
- return this.invitedMember.avatarUrl;
- }
- },
/**
* @private
* @returns {mail.callView}
@@ -98,7 +83,7 @@ registerModel({
* @returns {boolean}
*/
_computeHasConnectionInfo() {
- return Boolean(this.rtcSession && this.env.debug && this.channel.rtc);
+ return Boolean(this.rtcSession && this.env.debug && this.channelMember.channel.thread.rtc);
},
/**
* @private
@@ -106,12 +91,12 @@ registerModel({
*/
_computeInboundConnectionTypeText() {
if (!this.rtcSession || !this.rtcSession.remoteCandidateType) {
- return sprintf(this.env._t('From %s: no connection'), this.name);
+ return sprintf(this.env._t('From %s: no connection'), this.channelMember.persona.name);
}
return sprintf(
this.env._t('From %(name)s: %(candidateType)s (%(protocol)s)'), {
candidateType: this.rtcSession.remoteCandidateType,
- name: this.name,
+ name: this.channelMember.persona.name,
protocol: this.messaging.rtc.protocolsByCandidateTypes[this.rtcSession.remoteCandidateType],
},
);
@@ -130,30 +115,18 @@ registerModel({
_computeIsTalking() {
return Boolean(this.rtcSession && this.rtcSession.isTalking && !this.rtcSession.isMute);
},
- /**
- * @private
- * @returns {string}
- */
- _computeName() {
- if (this.rtcSession) {
- return this.rtcSession.channelMember.persona.name;
- }
- if (this.invitedMember) {
- return this.invitedMember.name;
- }
- },
/**
* @private
* @returns {string}
*/
_computeOutboundConnectionTypeText() {
if (!this.rtcSession || !this.rtcSession.localCandidateType) {
- return sprintf(this.env._t('To %s: no connection'), this.name);
+ return sprintf(this.env._t('To %s: no connection'), this.channelMember.persona.name);
}
return sprintf(
this.env._t('To %(name)s: %(candidateType)s (%(protocol)s)'), {
candidateType: this.rtcSession.localCandidateType,
- name: this.name,
+ name: this.channelMember.persona.name,
protocol: this.messaging.rtc.protocolsByCandidateTypes[this.rtcSession.localCandidateType],
},
);
@@ -170,17 +143,10 @@ registerModel({
},
},
fields: {
- /**
- * The relative url of the image that represents the card.
- */
- avatarUrl: attr({
- compute: '_computeAvatarUrl',
- }),
- /**
- * The channel of the call.
- */
- channel: one('Thread', {
+ channelMember: one('ChannelMember', {
+ inverse: 'callParticipantCards',
required: true,
+ readonly: true,
}),
/**
* Determines whether or not we show the connection info.
@@ -194,9 +160,6 @@ registerModel({
inboundConnectionTypeText: attr({
compute: '_computeInboundConnectionTypeText',
}),
- invitedMember: one('ChannelMember', {
- readonly: true,
- }),
/**
* Determines if this card has to be displayed in a minimized form.
*/
@@ -211,13 +174,6 @@ registerModel({
default: false,
compute: '_computeIsTalking',
}),
- /**
- * The name of the rtcSession or the invited partner.
- */
- name: attr({
- default: 'Anonymous',
- compute: '_computeName',
- }),
/**
* The text describing the outbound ice connection candidate type.
*/
@@ -245,12 +201,9 @@ registerModel({
inverse: 'tileParticipantCards',
readonly: true,
}),
- /**
- * If set, this card represents a rtcSession.
- */
rtcSession: one('RtcSession', {
+ related: 'channelMember.rtcSession',
inverse: 'callParticipantCards',
- readonly: true,
}),
callParticipantVideoView: one('CallParticipantVideoView', {
compute: '_computeCallParticipantVideoView',
diff --git a/addons/mail/static/src/models/call_view.js b/addons/mail/static/src/models/call_view.js
index 9e6d48904a1..4c6936fe888 100644
--- a/addons/mail/static/src/models/call_view.js
+++ b/addons/mail/static/src/models/call_view.js
@@ -115,8 +115,7 @@ registerModel({
return clear();
}
return insert({
- rtcSession: replace(this.activeRtcSession),
- channel: replace(this.channel),
+ channelMember: replace(this.activeRtcSession.channelMember),
});
},
/**
@@ -128,6 +127,7 @@ registerModel({
},
/**
* @private
+ * @returns {FieldCommand}
*/
_computeTileParticipantCards() {
if (!this.threadView) {
@@ -137,19 +137,12 @@ registerModel({
return clear();
}
const tileCards = [];
- for (const rtcSession of this.channel.rtcSessions) {
- if (this.filterVideoGrid && !rtcSession.videoStream) {
+ for (const channelMember of this.channel.channel.callParticipants) {
+ if (this.filterVideoGrid && !channelMember.isStreaming) {
continue;
}
tileCards.push({
- rtcSession: replace(rtcSession),
- channel: replace(this.channel),
- });
- }
- for (const member of this.channel.invitedMembers) {
- tileCards.push({
- invitedMember: replace(member),
- channel: replace(this.channel),
+ channelMember: replace(channelMember),
});
}
return insertAndReplace(tileCards);
diff --git a/addons/mail/static/src/models/channel.js b/addons/mail/static/src/models/channel.js
index 198ed402af1..a4d39bed99e 100644
--- a/addons/mail/static/src/models/channel.js
+++ b/addons/mail/static/src/models/channel.js
@@ -2,7 +2,7 @@
import { registerModel } from '@mail/model/model_core';
import { attr, one, many } from '@mail/model/model_field';
-import { insertAndReplace } from '@mail/model/model_field_command';
+import { insertAndReplace, replace } from '@mail/model/model_field_command';
registerModel({
name: 'Channel',
@@ -29,6 +29,17 @@ registerModel({
_computeAreAllMembersLoaded() {
return this.memberCount === this.channelMembers.length;
},
+ /**
+ * @private
+ * @returns {FieldCommand}
+ */
+ _computeCallParticipants() {
+ const callParticipants = this.thread.invitedMembers;
+ for (const rtcSession of this.thread.rtcSessions) {
+ callParticipants.push(rtcSession.channelMember);
+ }
+ return replace(callParticipants);
+ },
/**
* @private
* @returns {FieldCommand}
@@ -46,6 +57,16 @@ registerModel({
_computeUnknownMemberCount() {
return this.memberCount - this.channelMembers.length;
},
+ /**
+ * @private
+ * @returns {Array[]}
+ */
+ _sortCallParticipants() {
+ return [
+ ['truthy-first', 'rtcSession'],
+ ['smaller-first', 'rtcSession.id'],
+ ];
+ },
/**
* @private
* @returns {Array[]}
@@ -61,6 +82,10 @@ registerModel({
areAllMembersLoaded: attr({
compute: '_computeAreAllMembersLoaded',
}),
+ callParticipants: many('ChannelMember', {
+ compute: '_computeCallParticipants',
+ sort: '_sortCallParticipants',
+ }),
channelMembers: many('ChannelMember', {
inverse: 'channel',
isCausal: true,
diff --git a/addons/mail/static/src/models/channel_member.js b/addons/mail/static/src/models/channel_member.js
index 63f79e0ee91..e76c9497ece 100644
--- a/addons/mail/static/src/models/channel_member.js
+++ b/addons/mail/static/src/models/channel_member.js
@@ -47,11 +47,22 @@ registerModel({
}
return clear();
},
+ /**
+ * @private
+ * @returns {boolean}
+ */
+ _computeIsStreaming() {
+ return Boolean(this.rtcSession && this.rtcSession.videoStream);
+ },
},
fields: {
avatarUrl: attr({
compute: '_computeAvatarUrl',
}),
+ callParticipantCards: many('CallParticipantCard', {
+ inverse: 'channelMember',
+ isCausal: true,
+ }),
channel: one('Channel', {
inverse: 'channelMembers',
readonly: true,
@@ -73,10 +84,16 @@ registerModel({
readonly: true,
required: true,
}),
+ isStreaming: attr({
+ compute: '_computeIsStreaming',
+ }),
persona: one('Persona', {
inverse: 'channelMembers',
readonly: true,
required: true,
}),
+ rtcSession: one('RtcSession', {
+ inverse: 'channelMember',
+ }),
},
});
diff --git a/addons/mail/static/src/models/rtc_session.js b/addons/mail/static/src/models/rtc_session.js
index 7cfaa1bfc05..e453885806b 100644
--- a/addons/mail/static/src/models/rtc_session.js
+++ b/addons/mail/static/src/models/rtc_session.js
@@ -365,7 +365,9 @@ registerModel({
channel: one('Thread', {
inverse: 'rtcSessions',
}),
- channelMember: one('ChannelMember'),
+ channelMember: one('ChannelMember', {
+ inverse: 'rtcSession',
+ }),
connectionRecoveryTimeout: attr(),
/**
* State of the connection with this session, uses RTCPeerConnection.iceConnectionState