import React, { memo, useEffect, useState } from 'react'; import PropTypes from 'prop-types'; import ButtonEmoji from '/imports/ui/components/common/button/button-emoji/ButtonEmoji'; import VideoService from '../service'; import { defineMessages, injectIntl } from 'react-intl'; import Styled from './styles'; import deviceInfo from '/imports/utils/deviceInfo'; import { debounce } from '/imports/utils/debounce'; import BBBMenu from '/imports/ui/components/common/menu/component'; import { isVirtualBackgroundsEnabled } from '/imports/ui/services/features'; import Button from '/imports/ui/components/common/button/component'; import VideoPreviewContainer from '/imports/ui/components/video-preview/container'; import { CameraSettingsDropdownItemType } from 'bigbluebutton-html-plugin-sdk/dist/cjs/extensible-areas/camera-settings-dropdown-item/enums'; import Settings from '/imports/ui/services/settings'; const ENABLE_WEBCAM_SELECTOR_BUTTON = Meteor.settings.public.app.enableWebcamSelectorButton; const ENABLE_CAMERA_BRIGHTNESS = Meteor.settings.public.app.enableCameraBrightness; const intlMessages = defineMessages({ videoSettings: { id: 'app.video.videoSettings', description: 'Open video settings', }, visualEffects: { id: 'app.video.visualEffects', description: 'Visual effects label', }, joinVideo: { id: 'app.video.joinVideo', description: 'Join video button label', }, leaveVideo: { id: 'app.video.leaveVideo', description: 'Leave video button label', }, advancedVideo: { id: 'app.video.advancedVideo', description: 'Open advanced video label', }, videoLocked: { id: 'app.video.videoLocked', description: 'video disabled label', }, videoConnecting: { id: 'app.video.connecting', description: 'video connecting label', }, camCapReached: { id: 'app.video.meetingCamCapReached', description: 'meeting camera cap label', }, meteorDisconnected: { id: 'app.video.clientDisconnected', description: 'Meteor disconnected label', }, }); const JOIN_VIDEO_DELAY_MILLISECONDS = 500; const propTypes = { intl: PropTypes.object.isRequired, hasVideoStream: PropTypes.bool.isRequired, status: PropTypes.string.isRequired, cameraSettingsDropdownItems: PropTypes.arrayOf(PropTypes.shape({ id: PropTypes.string, type: PropTypes.string, })).isRequired, sendUserUnshareWebcam: PropTypes.func.isRequired, }; const JoinVideoButton = ({ intl, hasVideoStream, status, disableReason, updateSettings, cameraSettingsDropdownItems, sendUserUnshareWebcam, }) => { const { isMobile } = deviceInfo; const isMobileSharingCamera = hasVideoStream && isMobile; const isDesktopSharingCamera = hasVideoStream && !isMobile; const shouldEnableWebcamSelectorButton = ENABLE_WEBCAM_SELECTOR_BUTTON && isDesktopSharingCamera; const shouldEnableWebcamVisualEffectsButton = (isVirtualBackgroundsEnabled() || ENABLE_CAMERA_BRIGHTNESS) && hasVideoStream && !isMobile; const exitVideo = () => isDesktopSharingCamera && (!VideoService.isMultipleCamerasEnabled() || shouldEnableWebcamSelectorButton); const [propsToPassModal, setPropsToPassModal] = useState({}); const [forceOpen, setForceOpen] = useState(false); const [isVideoPreviewModalOpen, setVideoPreviewModalIsOpen] = useState(false); const [wasSelfViewDisabled, setWasSelfViewDisabled] = useState(false); useEffect(() => { const isSelfViewDisabled = Settings.application.selfViewDisable; if (isVideoPreviewModalOpen && isSelfViewDisabled) { setWasSelfViewDisabled(true); const obj = { application: { ...Settings.application, selfViewDisable: false }, }; updateSettings(obj); } }, [isVideoPreviewModalOpen]); const handleOnClick = debounce(() => { switch (status) { case 'videoConnecting': VideoService.stopVideo(undefined, sendUserUnshareWebcam); break; case 'connected': default: if (exitVideo()) { VideoService.exitVideo(sendUserUnshareWebcam); } else { setForceOpen(isMobileSharingCamera); setVideoPreviewModalIsOpen(true); } } }, JOIN_VIDEO_DELAY_MILLISECONDS); const handleOpenAdvancedOptions = (callback) => { if (callback) callback(); setForceOpen(isDesktopSharingCamera); setVideoPreviewModalIsOpen(true); }; const getMessageFromStatus = () => { let statusMessage = status; if (status !== 'videoConnecting') { statusMessage = exitVideo() ? 'leaveVideo' : 'joinVideo'; } return statusMessage; }; const label = disableReason ? intl.formatMessage(intlMessages[disableReason]) : intl.formatMessage(intlMessages[getMessageFromStatus()]); const isSharing = hasVideoStream || status === 'videoConnecting'; const renderUserActions = () => { const actions = []; if (shouldEnableWebcamSelectorButton) { actions.push( { key: 'advancedVideo', label: intl.formatMessage(intlMessages.advancedVideo), onClick: () => handleOpenAdvancedOptions(), dataTest: 'advancedVideoSettingsButton', }, ); } if (shouldEnableWebcamVisualEffectsButton) { actions.push( { key: 'virtualBgSelection', label: intl.formatMessage(intlMessages.visualEffects), onClick: () => handleOpenAdvancedOptions(( ) => setPropsToPassModal({ isVisualEffects: true })), }, ); } if (actions.length === 0) return null; const customStyles = { top: '-3.6rem' }; cameraSettingsDropdownItems.forEach((plugin) => { switch (plugin.type) { case CameraSettingsDropdownItemType.OPTION: actions.push({ key: plugin.id, label: plugin.label, onClick: plugin.onClick, icon: plugin.icon, }); break; case CameraSettingsDropdownItemType.SEPARATOR: actions.push({ key: plugin.id, isSeparator: true, }); break; default: break; } }); return ( )} actions={actions} opts={{ id: 'video-dropdown-menu', keepMounted: true, transitionDuration: 0, elevation: 3, getcontentanchorel: null, fullwidth: 'true', anchorOrigin: { vertical: 'top', horizontal: 'center' }, transformOrigin: { vertical: 'top', horizontal: 'center' }, }} /> ); }; return ( <>