diff --git a/addons/mail/static/src/components/call_participant_card/call_participant_card.js b/addons/mail/static/src/components/call_participant_card/call_participant_card.js
index 9b7b376b519..5863c25a73a 100644
--- a/addons/mail/static/src/components/call_participant_card/call_participant_card.js
+++ b/addons/mail/static/src/components/call_participant_card/call_participant_card.js
@@ -3,8 +3,6 @@
import { useRefToModel } from '@mail/component_hooks/use_ref_to_model';
import { registerMessagingComponent } from '@mail/utils/messaging_component';
-import Popover from "web.Popover";
-
const { Component } = owl;
export class CallParticipantCard extends Component {
@@ -33,7 +31,6 @@ export class CallParticipantCard extends Component {
Object.assign(CallParticipantCard, {
props: { record: Object },
template: 'mail.CallParticipantCard',
- components: { Popover },
});
registerMessagingComponent(CallParticipantCard);
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 4fac1320bee..28b78ac1758 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
@@ -75,19 +75,7 @@
-
-
-
-
-
-
+
diff --git a/addons/mail/static/src/components/call_participant_card_popover_content_view/call_participant_card_popover_content_view.js b/addons/mail/static/src/components/call_participant_card_popover_content_view/call_participant_card_popover_content_view.js
new file mode 100644
index 00000000000..9a95b60ee81
--- /dev/null
+++ b/addons/mail/static/src/components/call_participant_card_popover_content_view/call_participant_card_popover_content_view.js
@@ -0,0 +1,23 @@
+/** @odoo-module **/
+
+import { registerMessagingComponent } from '@mail/utils/messaging_component';
+
+const { Component } = owl;
+
+export class CallParticipantCardPopoverContentView extends Component {
+
+ /**
+ * @returns {CallParticipantCardPopoverContentView}
+ */
+ get callParticipantCardPopoverContentView() {
+ return this.props.record;
+ }
+
+}
+
+Object.assign(CallParticipantCardPopoverContentView, {
+ props: { record: Object },
+ template: 'mail.CallParticipantCardPopoverContentView',
+});
+
+registerMessagingComponent(CallParticipantCardPopoverContentView);
diff --git a/addons/mail/static/src/components/call_participant_card_popover_content_view/call_participant_card_popover_content_view.xml b/addons/mail/static/src/components/call_participant_card_popover_content_view/call_participant_card_popover_content_view.xml
new file mode 100644
index 00000000000..ebfba5b27cb
--- /dev/null
+++ b/addons/mail/static/src/components/call_participant_card_popover_content_view/call_participant_card_popover_content_view.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
diff --git a/addons/mail/static/src/models/call_participant_card.js b/addons/mail/static/src/models/call_participant_card.js
index 06456140d4a..5e056d4df0a 100644
--- a/addons/mail/static/src/models/call_participant_card.js
+++ b/addons/mail/static/src/models/call_participant_card.js
@@ -5,18 +5,10 @@ import { attr, one } from '@mail/model/model_field';
import { clear } from '@mail/model/model_field_command';
import { isEventHandled, markEventHandled } from '@mail/utils/utils';
-import { sprintf } from '@web/core/utils/strings';
-
registerModel({
name: 'CallParticipantCard',
identifyingMode: 'xor',
recordMethods: {
- /**
- * @param {Event} ev
- */
- onChangeVolume(ev) {
- this.rtcSession && this.rtcSession.setVolume(parseFloat(ev.target.value));
- },
/**
* @param {MouseEvent} ev
*/
@@ -52,6 +44,7 @@ registerModel({
*/
async onClickVolumeAnchor(ev) {
markEventHandled(ev, 'CallParticipantCard.clickVolumeAnchor');
+ this.update({ callParticipantCardPopoverView: this.callParticipantCardPopoverView ? clear() : {} });
},
/**
* This listens to the right click event, and used to redirect the event
@@ -86,29 +79,6 @@ registerModel({
}
return this.mainViewTileOwner.channelMember;
},
- /**
- * @private
- * @returns {boolean}
- */
- _computeHasConnectionInfo() {
- return Boolean(this.rtcSession && this.env.debug && this.channelMember.channel.thread.rtc);
- },
- /**
- * @private
- * @returns {string}
- */
- _computeInboundConnectionTypeText() {
- if (!this.rtcSession || !this.rtcSession.remoteCandidateType) {
- 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.channelMember.persona.name,
- protocol: this.messaging.rtc.protocolsByCandidateTypes[this.rtcSession.remoteCandidateType],
- },
- );
- },
/**
* @private
* @returns {boolean}
@@ -123,22 +93,6 @@ registerModel({
_computeIsTalking() {
return Boolean(this.rtcSession && this.rtcSession.isTalking && !this.rtcSession.isMute);
},
- /**
- * @private
- * @returns {string}
- */
- _computeOutboundConnectionTypeText() {
- if (!this.rtcSession || !this.rtcSession.localCandidateType) {
- 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.channelMember.persona.name,
- protocol: this.messaging.rtc.protocolsByCandidateTypes[this.rtcSession.localCandidateType],
- },
- );
- },
/**
* @private
* @returns {FieldCommand}
@@ -151,22 +105,14 @@ registerModel({
},
},
fields: {
+ callParticipantCardPopoverView: one('PopoverView', {
+ inverse: 'callParticipantCardOwner',
+ isCausal: true,
+ }),
channelMember: one('ChannelMember', {
compute: '_computeChannelMember',
inverse: 'callParticipantCards',
}),
- /**
- * Determines whether or not we show the connection info.
- */
- hasConnectionInfo: attr({
- compute: '_computeHasConnectionInfo',
- }),
- /**
- * The text describing the inbound ice connection candidate type.
- */
- inboundConnectionTypeText: attr({
- compute: '_computeInboundConnectionTypeText',
- }),
mainViewTileOwner: one('CallMainViewTile', {
identifying: true,
inverse: 'participantCard',
@@ -185,12 +131,6 @@ registerModel({
default: false,
compute: '_computeIsTalking',
}),
- /**
- * The text describing the outbound ice connection candidate type.
- */
- outboundConnectionTypeText: attr({
- compute: '_computeOutboundConnectionTypeText',
- }),
/**
* The callView that displays this card.
*/
diff --git a/addons/mail/static/src/models/call_participant_card_popover_content_view.js b/addons/mail/static/src/models/call_participant_card_popover_content_view.js
new file mode 100644
index 00000000000..625f64fc2b8
--- /dev/null
+++ b/addons/mail/static/src/models/call_participant_card_popover_content_view.js
@@ -0,0 +1,84 @@
+/** @odoo-module **/
+
+import { registerModel } from '@mail/model/model_core';
+import { attr, one } from '@mail/model/model_field';
+
+import { sprintf } from '@web/core/utils/strings';
+
+registerModel({
+ name: 'CallParticipantCardPopoverContentView',
+ recordMethods: {
+ /**
+ * @param {Event} ev
+ */
+ onChangeVolume(ev) {
+ this.callParticipantCard.rtcSession && this.callParticipantCard.rtcSession.setVolume(parseFloat(ev.target.value));
+ },
+ /**
+ * @private
+ * @returns {boolean}
+ */
+ _computeHasConnectionInfo() {
+ return Boolean(this.callParticipantCard.rtcSession && this.env.debug && this.callParticipantCard.channelMember.channel.thread.rtc);
+ },
+ /**
+ * @private
+ * @returns {string}
+ */
+ _computeInboundConnectionTypeText() {
+ if (!this.callParticipantCard.rtcSession || !this.callParticipantCard.rtcSession.remoteCandidateType) {
+ return sprintf(this.env._t('From %s: no connection'), this.callParticipantCard.channelMember.persona.name);
+ }
+ return sprintf(
+ this.env._t('From %(name)s: %(candidateType)s (%(protocol)s)'), {
+ candidateType: this.callParticipantCard.rtcSession.remoteCandidateType,
+ name: this.callParticipantCard.channelMember.persona.name,
+ protocol: this.messaging.rtc.protocolsByCandidateTypes[this.callParticipantCard.rtcSession.remoteCandidateType],
+ },
+ );
+ },
+ /**
+ * @private
+ * @returns {string}
+ */
+ _computeOutboundConnectionTypeText() {
+ if (!this.callParticipantCard.rtcSession || !this.callParticipantCard.rtcSession.localCandidateType) {
+ return sprintf(this.env._t('To %s: no connection'), this.callParticipantCard.channelMember.persona.name);
+ }
+ return sprintf(
+ this.env._t('To %(name)s: %(candidateType)s (%(protocol)s)'), {
+ candidateType: this.callParticipantCard.rtcSession.localCandidateType,
+ name: this.callParticipantCard.channelMember.persona.name,
+ protocol: this.messaging.rtc.protocolsByCandidateTypes[this.callParticipantCard.rtcSession.localCandidateType],
+ },
+ );
+ },
+ },
+ fields: {
+ callParticipantCard: one('CallParticipantCard', {
+ related: 'popoverViewOwner.callParticipantCardOwner',
+ }),
+ /**
+ * Determines whether or not we show the connection info.
+ */
+ hasConnectionInfo: attr({
+ compute: '_computeHasConnectionInfo',
+ }),
+ /**
+ * The text describing the inbound ice connection candidate type.
+ */
+ inboundConnectionTypeText: attr({
+ compute: '_computeInboundConnectionTypeText',
+ }),
+ /**
+ * The text describing the outbound ice connection candidate type.
+ */
+ outboundConnectionTypeText: attr({
+ compute: '_computeOutboundConnectionTypeText',
+ }),
+ popoverViewOwner: one('PopoverView', {
+ identifying: true,
+ inverse: 'callParticipantCardPopoverContentView',
+ })
+ },
+});
diff --git a/addons/mail/static/src/models/popover_view.js b/addons/mail/static/src/models/popover_view.js
index 1c548b491dd..3be63caa797 100644
--- a/addons/mail/static/src/models/popover_view.js
+++ b/addons/mail/static/src/models/popover_view.js
@@ -44,6 +44,9 @@ registerModel({
if (this.activityViewOwnerAsMarkDone) {
return this.activityViewOwnerAsMarkDone.markDoneButtonRef;
}
+ if (this.callParticipantCardOwner) {
+ return this.callParticipantCardOwner.volumeMenuAnchorRef;
+ }
if (this.threadViewTopbarOwnerAsInvite) {
return this.threadViewTopbarOwnerAsInvite.inviteButtonRef;
}
@@ -58,6 +61,12 @@ registerModel({
}
return clear();
},
+ _computeCallParticipantCardPopoverContentView() {
+ if (this.callParticipantCardOwner) {
+ return {};
+ }
+ return clear();
+ },
/**
* @private
* @returns {FieldCommand}
@@ -76,6 +85,9 @@ registerModel({
if (this.activityMarkDonePopoverContentView) {
return this.activityMarkDonePopoverContentView;
}
+ if (this.callParticipantCardPopoverContentView) {
+ return this.callParticipantCardPopoverContentView;
+ }
if (this.channelInvitationForm) {
return this.channelInvitationForm;
}
@@ -108,6 +120,9 @@ registerModel({
if (this.activityMarkDonePopoverContentView) {
return 'ActivityMarkDonePopoverContent';
}
+ if (this.callParticipantCardPopoverContentView) {
+ return 'CallParticipantCardPopoverContentView';
+ }
if (this.channelInvitationForm) {
return 'ChannelInvitationForm';
}
@@ -160,6 +175,9 @@ registerModel({
if (this.activityViewOwnerAsMarkDone) {
return 'right';
}
+ if (this.callParticipantCardOwner) {
+ return 'bottom';
+ }
if (this.threadViewTopbarOwnerAsInvite) {
return 'bottom';
}
@@ -207,6 +225,15 @@ registerModel({
compute: '_computeAnchorRef',
required: true,
}),
+ callParticipantCardOwner: one('CallParticipantCard', {
+ identifying: true,
+ inverse: 'callParticipantCardPopoverView',
+ }),
+ callParticipantCardPopoverContentView: one('CallParticipantCardPopoverContentView', {
+ compute: '_computeCallParticipantCardPopoverContentView',
+ inverse: 'popoverViewOwner',
+ isCausal: true,
+ }),
/**
* The record that represents the content inside the popover view.
*/