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. */