diff --git a/modules/API/API.js b/modules/API/API.js index 0b06fa2d9..55d3908b1 100644 --- a/modules/API/API.js +++ b/modules/API/API.js @@ -21,7 +21,7 @@ import { import { isEnabled as isDropboxEnabled } from '../../react/features/dropbox'; import { toggleE2EE } from '../../react/features/e2ee/actions'; import { invite } from '../../react/features/invite'; -import { selectParticipantInLargeVideo } from '../../react/features/large-video/actions'; +import { resizeLargeVideo, selectParticipantInLargeVideo } from '../../react/features/large-video/actions'; import { toggleLobbyMode } from '../../react/features/lobby/actions.web'; import { RECORDING_TYPES } from '../../react/features/recording/constants'; import { getActiveSession } from '../../react/features/recording/functions'; @@ -119,6 +119,11 @@ function initCommands() { 'proxy-connection-event': event => { APP.conference.onProxyConnectionEvent(event); }, + 'resize-large-video': (width, height) => { + logger.debug('Resize large video command received'); + sendAnalytics(createApiEvent('largevideo.resized')); + APP.store.dispatch(resizeLargeVideo(width, height)); + }, 'send-tones': (options = {}) => { const { duration, tones, pause } = options; diff --git a/modules/API/external/external_api.js b/modules/API/external/external_api.js index 5f192f00e..895b8a3a9 100644 --- a/modules/API/external/external_api.js +++ b/modules/API/external/external_api.js @@ -35,6 +35,7 @@ const commands = { hangup: 'video-hangup', muteEveryone: 'mute-everyone', password: 'password', + resizeLargeVideo: 'resize-large-video', sendEndpointTextMessage: 'send-endpoint-text-message', sendTones: 'send-tones', setLargeVideoParticipant: 'set-large-video-participant', @@ -437,10 +438,12 @@ export default class JitsiMeetExternalAPI extends EventEmitter { const parsedWidth = parseSizeParam(width); if (parsedHeight !== undefined) { + this._height = height; this._frame.style.height = parsedHeight; } if (parsedWidth !== undefined) { + this._width = width; this._frame.style.width = parsedWidth; } } @@ -930,6 +933,19 @@ export default class JitsiMeetExternalAPI extends EventEmitter { eventList.forEach(event => this.removeEventListener(event)); } + /** + * Resizes the large video container as per the dimensions provided. + * + * @param {number} width - Width that needs to be applied on the large video container. + * @param {number} height - Height that needs to be applied on the large video container. + * @returns {void} + */ + resizeLargeVideo(width, height) { + if (width <= this._width && height <= this._height) { + this.executeCommand('resizeLargeVideo', width, height); + } + } + /** * Passes an event along to the local conference participant to establish * or update a direct peer connection. This is currently used for developing diff --git a/modules/UI/util/UIUtil.js b/modules/UI/util/UIUtil.js index 30e8b6ede..d70bbe983 100644 --- a/modules/UI/util/UIUtil.js +++ b/modules/UI/util/UIUtil.js @@ -5,13 +5,6 @@ */ const UIUtil = { - /** - * Returns the available video width. - */ - getAvailableVideoWidth() { - return window.innerWidth; - }, - /** * Escapes the given text. */ diff --git a/modules/UI/videolayout/LargeVideoManager.js b/modules/UI/videolayout/LargeVideoManager.js index da16533ea..98f236b39 100644 --- a/modules/UI/videolayout/LargeVideoManager.js +++ b/modules/UI/videolayout/LargeVideoManager.js @@ -21,7 +21,6 @@ import { PresenceLabel } from '../../../react/features/presence-status'; import UIEvents from '../../../service/UI/UIEvents'; import { createDeferred } from '../../util/helpers'; import AudioLevels from '../audio_levels/AudioLevels'; -import UIUtil from '../util/UIUtil'; import { VideoContainer, VIDEO_CONTAINER_TYPE } from './VideoContainer'; @@ -323,20 +322,25 @@ export default class LargeVideoManager { /** * Update container size. */ - updateContainerSize() { - let widthToUse = UIUtil.getAvailableVideoWidth(); + updateContainerSize(width, height) { + let widthToUse = width ?? (this.width > 0 ? this.width : window.innerWidth); const { isOpen } = APP.store.getState()['features/chat']; + /** + * If chat state is open, we re-compute the container width by subtracting the default width of + * the chat. We re-compute the width again after the chat window is closed. This is needed when + * custom styling is configured on the large video container through the iFrame API. + */ if (isOpen) { - /** - * If chat state is open, we re-compute the container width - * by subtracting the default width of the chat. - */ widthToUse -= CHAT_SIZE; + this.resizedForChat = true; + } else if (this.resizedForChat) { + this.resizedForChat = false; + widthToUse += CHAT_SIZE; } this.width = widthToUse; - this.height = window.innerHeight; + this.height = height ?? (this.height > 0 ? this.height : window.innerHeight); } /** diff --git a/react/features/large-video/actions.js b/react/features/large-video/actions.js index eb645b975..9100403c4 100644 --- a/react/features/large-video/actions.js +++ b/react/features/large-video/actions.js @@ -2,6 +2,7 @@ import type { Dispatch } from 'redux'; +import VideoLayout from '../../../modules/UI/videolayout/VideoLayout'; import { createSelectParticipantFailedEvent, sendAnalytics @@ -19,6 +20,27 @@ import { declare var APP: Object; +/** + * Resizes the large video container based on the dimensions provided. + * + * @param {number} width - Width that needs to be applied on the large video container. + * @param {number} height - Height that needs to be applied on the large video container. + * @returns {void} + */ +export function resizeLargeVideo(width: number, height: number) { + return (dispatch: Dispatch, getState: Function) => { + const state = getState(); + const largeVideo = state['features/large-video']; + + if (largeVideo) { + const largeVideoContainer = VideoLayout.getLargeVideo(); + + largeVideoContainer.updateContainerSize(width, height); + largeVideoContainer.resize(); + } + }; +} + /** * Signals conference to select a participant. *