diff --git a/addons/mail/static/src/components/call_main_view/call_main_view.scss b/addons/mail/static/src/components/call_main_view/call_main_view.scss index 8d66edee7e2..af0df47921a 100644 --- a/addons/mail/static/src/components/call_main_view/call_main_view.scss +++ b/addons/mail/static/src/components/call_main_view/call_main_view.scss @@ -6,10 +6,6 @@ min-width: 0; } -.o_CallMainView_mainParticipantContainer { - min-width: 0; -} - .o_CallMainView_gridParticipantCard { width: var(--width); height: var(--height); @@ -30,10 +26,6 @@ // Style // ------------------------------------------------------------------ -.o_CallMainView_mainParticipantContainer { - background-color: black; -} - .o_CallMainView_sidebarButton { color: white; border-radius: 10px 0px 0px 10px; diff --git a/addons/mail/static/src/components/call_main_view/call_main_view.xml b/addons/mail/static/src/components/call_main_view/call_main_view.xml index 3e9521c5fc6..9c1ff878ead 100644 --- a/addons/mail/static/src/components/call_main_view/call_main_view.xml +++ b/addons/mail/static/src/components/call_main_view/call_main_view.xml @@ -3,36 +3,28 @@ -
+
- -
- -
- - - - - -
-
- -
- - - -
+
+ +
+ + + +
diff --git a/addons/mail/static/src/components/call_sidebar_view/call_sidebar_view.xml b/addons/mail/static/src/components/call_sidebar_view/call_sidebar_view.xml index f2ff5d9e424..12e0ca20f73 100644 --- a/addons/mail/static/src/components/call_sidebar_view/call_sidebar_view.xml +++ b/addons/mail/static/src/components/call_sidebar_view/call_sidebar_view.xml @@ -4,10 +4,10 @@
- +
diff --git a/addons/mail/static/src/models/call_main_view.js b/addons/mail/static/src/models/call_main_view.js index 82a85c703ac..1b88381e635 100644 --- a/addons/mail/static/src/models/call_main_view.js +++ b/addons/mail/static/src/models/call_main_view.js @@ -1,8 +1,8 @@ /** @odoo-module **/ import { registerModel } from '@mail/model/model_core'; -import { attr, one } from '@mail/model/model_field'; -import { clear, insertAndReplace } from '@mail/model/model_field_command'; +import { attr, many, one } from '@mail/model/model_field'; +import { clear, insertAndReplace, replace } from '@mail/model/model_field_command'; import { isEventHandled, markEventHandled } from '@mail/utils/utils'; @@ -63,13 +63,13 @@ registerModel({ * @param {MouseEvent} ev */ onClickHideSidebar(ev) { - this.callView.update({ hasSidebar: false }); + this.callView.update({ isSidebarOpen: false }); }, /** * @param {MouseEvent} ev */ onClickShowSidebar(ev) { - this.callView.update({ hasSidebar: true }); + this.callView.update({ isSidebarOpen: true }); }, /** * @param {MouseEvent} ev @@ -117,12 +117,29 @@ registerModel({ // Private //---------------------------------------------------------------------- + /** + * @private + * @returns {boolean} + */ + _computeHasSidebarButton() { + return Boolean(this.callView.activeRtcSession && this.showOverlay && !this.callView.threadView.compact); + }, /** * @private */ _computeIsControllerFloating() { return Boolean(this.callView.isFullScreen || this.callView.activeRtcSession && !this.callView.threadView.compact); }, + /** + * @private + * @returns {FieldCommand} + */ + _computeMainTiles() { + if (this.callView.activeRtcSession) { + return insertAndReplace([{ channelMember: replace(this.callView.activeRtcSession.channelMember) }]); + } + return insertAndReplace(this.callView.filteredChannelMembers.map(channelMember => ({ channelMember: replace(channelMember) }))); + }, /** * Shows the overlay (buttons) for a set a mount of time. * @@ -154,6 +171,9 @@ registerModel({ related: 'callView.channel', required: true, }), + hasSidebarButton: attr({ + compute: '_computeHasSidebarButton', + }), /** * Determines if the controller is an overlay or a bottom bar. */ @@ -161,6 +181,11 @@ registerModel({ default: false, compute: '_computeIsControllerFloating', }), + mainTiles: many('CallMainViewTile', { + compute: '_computeMainTiles', + inverse: 'callMainViewOwner', + isCausal: true, + }), /** * Determines if we show the overlay with the control buttons. */ diff --git a/addons/mail/static/src/models/call_main_view_tile.js b/addons/mail/static/src/models/call_main_view_tile.js new file mode 100644 index 00000000000..72370d3369a --- /dev/null +++ b/addons/mail/static/src/models/call_main_view_tile.js @@ -0,0 +1,26 @@ +/** @odoo-module **/ + +import { registerModel } from '@mail/model/model_core'; +import { insertAndReplace } from '@mail/model/model_field_command'; +import { one } from '@mail/model/model_field'; + +registerModel({ + name: 'CallMainViewTile', + identifyingFields: ['callMainViewOwner', 'channelMember'], + fields: { + callMainViewOwner: one('CallMainView', { + inverse: 'mainTiles', + readonly: true, + required: true, + }), + channelMember: one('ChannelMember', { + readonly: true, + required: true, + }), + participantCard: one('CallParticipantCard', { + default: insertAndReplace(), + inverse: 'mainViewTileOwner', + isCausal: true, + }), + }, +}); diff --git a/addons/mail/static/src/models/call_participant_card.js b/addons/mail/static/src/models/call_participant_card.js index 0863d89e03e..d1d55f0af5b 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: ['channelMember', ['callViewAsMainCard', 'callViewAsTile']], + identifyingFields: [['sidebarViewTileOwner', 'mainViewTileOwner']], recordMethods: { /** * @param {Event} ev @@ -25,7 +25,7 @@ registerModel({ return; } if (this.rtcSession) { - if (this.callView.activeRtcSession === this.rtcSession && this.callViewAsMainCard) { + if (this.callView.activeRtcSession === this.rtcSession && this.mainViewTileOwner) { this.callView.update({ activeRtcSession: clear() }); } else { this.callView.update({ activeRtcSession: replace(this.rtcSession) }); @@ -68,16 +68,24 @@ registerModel({ }, /** * @private - * @returns {mail.callView} + * @returns {FieldCommand} */ _computeCallView() { - const callView = this.callViewAsMainCard || this.callViewAsTile; - if (callView) { - return replace(callView); - } else { - return clear(); + if (this.sidebarViewTileOwner) { + return replace(this.sidebarViewTileOwner.callSidebarViewOwner.callView); } + return replace(this.mainViewTileOwner.callMainViewOwner.callView); }, + /** + * @private + * @returns {FieldCommand} + */ + _computeChannelMember() { + if (this.sidebarViewTileOwner) { + return replace(this.sidebarViewTileOwner.channelMember); + } + return replace(this.mainViewTileOwner.channelMember); + }, /** * @private * @returns {boolean} @@ -144,9 +152,8 @@ registerModel({ }, fields: { channelMember: one('ChannelMember', { + compute: '_computeChannelMember', inverse: 'callParticipantCards', - required: true, - readonly: true, }), /** * Determines whether or not we show the connection info. @@ -160,6 +167,10 @@ registerModel({ inboundConnectionTypeText: attr({ compute: '_computeInboundConnectionTypeText', }), + mainViewTileOwner: one('CallMainViewTile', { + inverse: 'participantCard', + readonly: true, + }), /** * Determines if this card has to be displayed in a minimized form. */ @@ -187,24 +198,14 @@ registerModel({ compute: '_computeCallView', inverse: 'participantCards', }), - /** - * The call view for which this card is the main card. - */ - callViewAsMainCard: one('CallView', { - inverse: 'mainParticipantCard', - readonly: true, - }), - /** - * The call view for which this card is one of the tiles. - */ - callViewAsTile: one('CallView', { - inverse: 'tileParticipantCards', - readonly: true, - }), rtcSession: one('RtcSession', { related: 'channelMember.rtcSession', inverse: 'callParticipantCards', }), + sidebarViewTileOwner: one('CallSidebarViewTile', { + inverse: 'participantCard', + readonly: true, + }), callParticipantVideoView: one('CallParticipantVideoView', { compute: '_computeCallParticipantVideoView', inverse: 'callParticipantCardOwner', diff --git a/addons/mail/static/src/models/call_sidebar_view.js b/addons/mail/static/src/models/call_sidebar_view.js index 5b5385c3db3..65b296a91ab 100644 --- a/addons/mail/static/src/models/call_sidebar_view.js +++ b/addons/mail/static/src/models/call_sidebar_view.js @@ -1,16 +1,31 @@ /** @odoo-module **/ import { registerModel } from '@mail/model/model_core'; -import { one } from '@mail/model/model_field'; +import { many, one } from '@mail/model/model_field'; +import { insertAndReplace, replace } from '@mail/model/model_field_command'; registerModel({ name: 'CallSidebarView', identifyingFields: ['callView'], + recordMethods: { + /** + * @private + * @returns {FieldCommand} + */ + _computeSidebarTiles() { + return insertAndReplace(this.callView.filteredChannelMembers.map(channelMember => ({ channelMember: replace(channelMember) }))); + }, + }, fields: { callView: one('CallView', { inverse: 'callSidebarView', - required: true, readonly: true, - }) + required: true, + }), + sidebarTiles: many('CallSidebarViewTile', { + compute: '_computeSidebarTiles', + inverse: 'callSidebarViewOwner', + isCausal: true, + }), }, }); diff --git a/addons/mail/static/src/models/call_sidebar_view_tile.js b/addons/mail/static/src/models/call_sidebar_view_tile.js new file mode 100644 index 00000000000..f031ee4e624 --- /dev/null +++ b/addons/mail/static/src/models/call_sidebar_view_tile.js @@ -0,0 +1,26 @@ +/** @odoo-module **/ + +import { registerModel } from '@mail/model/model_core'; +import { insertAndReplace } from '@mail/model/model_field_command'; +import { one } from '@mail/model/model_field'; + +registerModel({ + name: 'CallSidebarViewTile', + identifyingFields: ['callSidebarViewOwner', 'channelMember'], + fields: { + callSidebarViewOwner: one('CallSidebarView', { + inverse: 'sidebarTiles', + readonly: true, + required: true, + }), + channelMember: one('ChannelMember', { + readonly: true, + required: true, + }), + participantCard: one('CallParticipantCard', { + default: insertAndReplace(), + inverse: 'sidebarViewTileOwner', + isCausal: true, + }), + }, +}); diff --git a/addons/mail/static/src/models/call_view.js b/addons/mail/static/src/models/call_view.js index 4c6936fe888..a17e3ccac5f 100644 --- a/addons/mail/static/src/models/call_view.js +++ b/addons/mail/static/src/models/call_view.js @@ -5,7 +5,7 @@ import { browser } from "@web/core/browser/browser"; import { registerModel } from '@mail/model/model_core'; import { attr, many, one } from '@mail/model/model_field'; import { OnChange } from '@mail/model/model_onchange'; -import { clear, insert, insertAndReplace, replace } from '@mail/model/model_field_command'; +import { clear, insertAndReplace, replace } from '@mail/model/model_field_command'; registerModel({ name: 'CallView', @@ -80,11 +80,21 @@ registerModel({ * @private */ _computeCallSideBarView() { - if (this.mainParticipantCard && this.hasSidebar && !this.threadView.compact) { + if (this.activeRtcSession && this.isSidebarOpen && !this.threadView.compact) { return insertAndReplace(); } return clear(); }, + _computeFilteredChannelMembers() { + const channelMembers = []; + for (const channelMember of this.channel.channel.callParticipants) { + if (this.filterVideoGrid && !channelMember.isStreaming) { + continue; + } + channelMembers.push(channelMember); + } + return replace(channelMembers); + }, /** * @private */ @@ -95,7 +105,7 @@ registerModel({ if (this.isFullScreen || this.threadView.compact) { return false; } - if (this.mainParticipantCard) { + if (this.activeRtcSession) { return false; } return !this.channel.rtc || this.channel.videoCount === 0; @@ -107,17 +117,6 @@ registerModel({ _computeLayoutSettingsTitle() { return this.env._t("Change Layout"); }, - /** - * @private - */ - _computeMainParticipantCard() { - if (!this.messaging || !this.activeRtcSession || !this.threadView || !this.activeRtcSession) { - return clear(); - } - return insert({ - channelMember: replace(this.activeRtcSession.channelMember), - }); - }, /** * @private * @returns {string} @@ -125,28 +124,6 @@ registerModel({ _computeSettingsTitle() { return this.env._t("Settings"); }, - /** - * @private - * @returns {FieldCommand} - */ - _computeTileParticipantCards() { - if (!this.threadView) { - return clear(); - } - if (this.activeRtcSession && !this.hasSidebar) { - return clear(); - } - const tileCards = []; - for (const channelMember of this.channel.channel.callParticipants) { - if (this.filterVideoGrid && !channelMember.isStreaming) { - continue; - } - tileCards.push({ - channelMember: replace(channelMember), - }); - } - return insertAndReplace(tileCards); - }, /** * @private */ @@ -202,18 +179,15 @@ registerModel({ related: 'threadView.thread', required: true, }), + filteredChannelMembers: many('ChannelMember', { + compute: '_computeFilteredChannelMembers', + }), /** * Determines whether we only display the videos or all the participants */ filterVideoGrid: attr({ default: false, }), - /** - * Determines if the viewer should have a sidebar. - */ - hasSidebar: attr({ - default: true, - }), /** * Determines if the viewer should be displayed fullScreen. */ @@ -228,20 +202,15 @@ registerModel({ default: false, compute: '_computeIsMinimized', }), + isSidebarOpen: attr({ + default: true, + }), /** * Text content that is displayed on title of the layout settings dialog. */ layoutSettingsTitle: attr({ compute: '_computeLayoutSettingsTitle', }), - /** - * If set, the card to be displayed as the main card. - */ - mainParticipantCard: one('CallParticipantCard', { - compute: '_computeMainParticipantCard', - inverse: 'callViewAsMainCard', - isCausal: true, - }), /** * All the participant cards of the call viewer (main card and tile cards). * this is a technical inverse to distinguish from the other relation 'tileParticipantCards'. @@ -264,14 +233,6 @@ registerModel({ readonly: true, required: true, }), - /** - * List of all participant cards (can either be invitations or rtcSessions). - */ - tileParticipantCards: many('CallParticipantCard', { - compute: '_computeTileParticipantCards', - inverse: 'callViewAsTile', - isCausal: true, - }), }, onChanges: [ new OnChange({