2023-05-23 03:54:41 +08:00
|
|
|
import React, { memo, useEffect, useState } from 'react';
|
2018-01-08 12:44:42 +08:00
|
|
|
import PropTypes from 'prop-types';
|
2022-02-15 04:20:50 +08:00
|
|
|
import ButtonEmoji from '/imports/ui/components/common/button/button-emoji/ButtonEmoji';
|
2019-11-28 21:13:06 +08:00
|
|
|
import VideoService from '../service';
|
2020-05-26 04:00:13 +08:00
|
|
|
import { defineMessages, injectIntl } from 'react-intl';
|
2021-11-03 21:06:18 +08:00
|
|
|
import Styled from './styles';
|
2021-10-16 06:37:37 +08:00
|
|
|
import deviceInfo from '/imports/utils/deviceInfo';
|
2023-08-10 00:26:42 +08:00
|
|
|
import { debounce } from '/imports/utils/debounce';
|
2022-04-12 01:09:13 +08:00
|
|
|
import BBBMenu from '/imports/ui/components/common/menu/component';
|
2022-08-26 22:50:46 +08:00
|
|
|
import { isVirtualBackgroundsEnabled } from '/imports/ui/services/features';
|
2022-06-17 03:59:01 +08:00
|
|
|
import Button from '/imports/ui/components/common/button/component';
|
2023-03-27 23:36:25 +08:00
|
|
|
import VideoPreviewContainer from '/imports/ui/components/video-preview/container';
|
2023-09-25 22:59:12 +08:00
|
|
|
import * as PluginSdk from 'bigbluebutton-html-plugin-sdk';
|
2023-05-23 03:54:41 +08:00
|
|
|
import Settings from '/imports/ui/services/settings';
|
2017-09-20 11:12:10 +08:00
|
|
|
|
2021-10-18 22:03:21 +08:00
|
|
|
const ENABLE_WEBCAM_SELECTOR_BUTTON = Meteor.settings.public.app.enableWebcamSelectorButton;
|
2022-08-26 22:50:46 +08:00
|
|
|
const ENABLE_CAMERA_BRIGHTNESS = Meteor.settings.public.app.enableCameraBrightness;
|
2021-10-16 06:37:37 +08:00
|
|
|
|
2017-09-20 11:12:10 +08:00
|
|
|
const intlMessages = defineMessages({
|
2022-04-12 01:09:13 +08:00
|
|
|
videoSettings: {
|
|
|
|
id: 'app.video.videoSettings',
|
|
|
|
description: 'Open video settings',
|
|
|
|
},
|
|
|
|
visualEffects: {
|
|
|
|
id: 'app.video.visualEffects',
|
|
|
|
description: 'Visual effects label',
|
|
|
|
},
|
2019-01-28 21:21:29 +08:00
|
|
|
joinVideo: {
|
|
|
|
id: 'app.video.joinVideo',
|
|
|
|
description: 'Join video button label',
|
2017-09-20 11:12:10 +08:00
|
|
|
},
|
2019-12-19 01:44:56 +08:00
|
|
|
leaveVideo: {
|
|
|
|
id: 'app.video.leaveVideo',
|
|
|
|
description: 'Leave video button label',
|
|
|
|
},
|
2021-08-10 23:25:20 +08:00
|
|
|
advancedVideo: {
|
|
|
|
id: 'app.video.advancedVideo',
|
|
|
|
description: 'Open advanced video label',
|
|
|
|
},
|
2019-03-29 22:31:56 +08:00
|
|
|
videoLocked: {
|
|
|
|
id: 'app.video.videoLocked',
|
2019-01-28 21:21:29 +08:00
|
|
|
description: 'video disabled label',
|
2018-03-23 01:02:59 +08:00
|
|
|
},
|
2020-09-10 02:07:32 +08:00
|
|
|
videoConnecting: {
|
|
|
|
id: 'app.video.connecting',
|
|
|
|
description: 'video connecting label',
|
|
|
|
},
|
2022-03-02 23:58:02 +08:00
|
|
|
camCapReached: {
|
|
|
|
id: 'app.video.meetingCamCapReached',
|
|
|
|
description: 'meeting camera cap label',
|
|
|
|
},
|
2020-09-10 02:07:32 +08:00
|
|
|
meteorDisconnected: {
|
2020-09-10 03:31:20 +08:00
|
|
|
id: 'app.video.clientDisconnected',
|
2020-09-10 02:07:32 +08:00
|
|
|
description: 'Meteor disconnected label',
|
|
|
|
},
|
2017-09-20 11:12:10 +08:00
|
|
|
});
|
|
|
|
|
2021-02-10 21:29:26 +08:00
|
|
|
const JOIN_VIDEO_DELAY_MILLISECONDS = 500;
|
|
|
|
|
2018-02-26 20:29:28 +08:00
|
|
|
const propTypes = {
|
2020-05-26 04:00:13 +08:00
|
|
|
intl: PropTypes.object.isRequired,
|
2019-11-28 21:13:06 +08:00
|
|
|
hasVideoStream: PropTypes.bool.isRequired,
|
2022-04-05 01:02:50 +08:00
|
|
|
status: PropTypes.string.isRequired,
|
2023-09-25 22:59:12 +08:00
|
|
|
cameraSettingsDropdownItems: PropTypes.arrayOf(PropTypes.shape({
|
|
|
|
id: PropTypes.string,
|
|
|
|
type: PropTypes.string,
|
|
|
|
})).isRequired,
|
2018-02-26 20:29:28 +08:00
|
|
|
};
|
2018-02-08 03:20:10 +08:00
|
|
|
|
2019-01-28 21:21:29 +08:00
|
|
|
const JoinVideoButton = ({
|
2018-02-26 20:29:28 +08:00
|
|
|
intl,
|
2019-11-28 21:13:06 +08:00
|
|
|
hasVideoStream,
|
2022-04-05 01:02:50 +08:00
|
|
|
status,
|
2020-09-10 02:07:32 +08:00
|
|
|
disableReason,
|
2023-05-23 03:54:41 +08:00
|
|
|
updateSettings,
|
2023-09-25 22:59:12 +08:00
|
|
|
cameraSettingsDropdownItems,
|
2019-04-19 01:14:34 +08:00
|
|
|
}) => {
|
2021-10-16 06:37:37 +08:00
|
|
|
const { isMobile } = deviceInfo;
|
2022-01-14 01:54:57 +08:00
|
|
|
const isMobileSharingCamera = hasVideoStream && isMobile;
|
2022-04-05 01:02:50 +08:00
|
|
|
const isDesktopSharingCamera = hasVideoStream && !isMobile;
|
|
|
|
const shouldEnableWebcamSelectorButton = ENABLE_WEBCAM_SELECTOR_BUTTON
|
|
|
|
&& isDesktopSharingCamera;
|
2023-05-23 03:54:41 +08:00
|
|
|
const shouldEnableWebcamVisualEffectsButton = (isVirtualBackgroundsEnabled()
|
|
|
|
|| ENABLE_CAMERA_BRIGHTNESS)
|
2022-04-12 01:09:13 +08:00
|
|
|
&& hasVideoStream
|
|
|
|
&& !isMobile;
|
2022-04-05 01:02:50 +08:00
|
|
|
const exitVideo = () => isDesktopSharingCamera && (!VideoService.isMultipleCamerasEnabled()
|
|
|
|
|| shouldEnableWebcamSelectorButton);
|
2019-12-19 01:44:56 +08:00
|
|
|
|
2023-03-27 23:36:25 +08:00
|
|
|
const [propsToPassModal, setPropsToPassModal] = useState({});
|
|
|
|
const [forceOpen, setForceOpen] = useState(false);
|
|
|
|
const [isVideoPreviewModalOpen, setVideoPreviewModalIsOpen] = useState(false);
|
2023-05-23 03:54:41 +08:00
|
|
|
const [wasSelfViewDisabled, setWasSelfViewDisabled] = useState(false);
|
|
|
|
|
|
|
|
useEffect(() => {
|
2023-05-30 02:02:29 +08:00
|
|
|
const isSelfViewDisabled = Settings.application.selfViewDisable;
|
|
|
|
|
2023-05-23 03:54:41 +08:00
|
|
|
if (isVideoPreviewModalOpen && isSelfViewDisabled) {
|
|
|
|
setWasSelfViewDisabled(true);
|
|
|
|
const obj = {
|
|
|
|
application:
|
|
|
|
{ ...Settings.application, selfViewDisable: false },
|
|
|
|
};
|
|
|
|
updateSettings(obj);
|
|
|
|
}
|
|
|
|
}, [isVideoPreviewModalOpen]);
|
2023-03-27 23:36:25 +08:00
|
|
|
|
2023-08-10 00:26:42 +08:00
|
|
|
const handleOnClick = debounce(() => {
|
2022-04-05 01:02:50 +08:00
|
|
|
switch (status) {
|
|
|
|
case 'videoConnecting':
|
|
|
|
VideoService.stopVideo();
|
|
|
|
break;
|
|
|
|
case 'connected':
|
|
|
|
default:
|
|
|
|
if (exitVideo()) {
|
|
|
|
VideoService.exitVideo();
|
|
|
|
} else {
|
2023-03-27 23:36:25 +08:00
|
|
|
setForceOpen(isMobileSharingCamera);
|
|
|
|
setVideoPreviewModalIsOpen(true);
|
2022-04-05 01:02:50 +08:00
|
|
|
}
|
2019-12-19 01:44:56 +08:00
|
|
|
}
|
2023-08-10 00:26:42 +08:00
|
|
|
}, JOIN_VIDEO_DELAY_MILLISECONDS);
|
2019-05-29 04:46:29 +08:00
|
|
|
|
2023-03-27 23:36:25 +08:00
|
|
|
const handleOpenAdvancedOptions = (callback) => {
|
|
|
|
if (callback) callback();
|
|
|
|
setForceOpen(isDesktopSharingCamera);
|
|
|
|
setVideoPreviewModalIsOpen(true);
|
2022-04-05 01:02:50 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
const getMessageFromStatus = () => {
|
|
|
|
let statusMessage = status;
|
|
|
|
if (status !== 'videoConnecting') {
|
|
|
|
statusMessage = exitVideo() ? 'leaveVideo' : 'joinVideo';
|
|
|
|
}
|
|
|
|
return statusMessage;
|
2021-08-10 23:25:20 +08:00
|
|
|
};
|
|
|
|
|
2022-04-05 01:02:50 +08:00
|
|
|
const label = disableReason
|
|
|
|
? intl.formatMessage(intlMessages[disableReason])
|
|
|
|
: intl.formatMessage(intlMessages[getMessageFromStatus()]);
|
2019-05-29 04:46:29 +08:00
|
|
|
|
2022-04-05 01:02:50 +08:00
|
|
|
const isSharing = hasVideoStream || status === 'videoConnecting';
|
2021-08-10 23:25:20 +08:00
|
|
|
|
2022-04-12 01:09:13 +08:00
|
|
|
const renderUserActions = () => {
|
|
|
|
const actions = [];
|
|
|
|
|
|
|
|
if (shouldEnableWebcamSelectorButton) {
|
|
|
|
actions.push(
|
|
|
|
{
|
|
|
|
key: 'advancedVideo',
|
|
|
|
label: intl.formatMessage(intlMessages.advancedVideo),
|
|
|
|
onClick: () => handleOpenAdvancedOptions(),
|
2023-03-29 22:16:47 +08:00
|
|
|
dataTest: 'advancedVideoSettingsButton',
|
2022-04-12 01:09:13 +08:00
|
|
|
},
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2022-08-26 22:50:46 +08:00
|
|
|
if (shouldEnableWebcamVisualEffectsButton) {
|
2022-04-12 01:09:13 +08:00
|
|
|
actions.push(
|
|
|
|
{
|
|
|
|
key: 'virtualBgSelection',
|
|
|
|
label: intl.formatMessage(intlMessages.visualEffects),
|
2023-05-23 03:54:41 +08:00
|
|
|
onClick: () => handleOpenAdvancedOptions((
|
|
|
|
) => setPropsToPassModal({ isVisualEffects: true })),
|
2022-04-12 01:09:13 +08:00
|
|
|
},
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (actions.length === 0) return null;
|
2022-10-25 01:20:49 +08:00
|
|
|
const customStyles = { top: '-3.6rem' };
|
2022-04-12 01:09:13 +08:00
|
|
|
|
2023-09-25 22:59:12 +08:00
|
|
|
cameraSettingsDropdownItems.forEach((plugin) => {
|
|
|
|
switch (plugin.type) {
|
|
|
|
case PluginSdk.CameraSettingsDropdownItemType.OPTION:
|
|
|
|
actions.push({
|
|
|
|
key: plugin.id,
|
|
|
|
label: plugin.label,
|
|
|
|
onClick: plugin.onClick,
|
|
|
|
icon: plugin.icon,
|
|
|
|
});
|
|
|
|
break;
|
|
|
|
case PluginSdk.CameraSettingsDropdownItemType.SEPARATOR:
|
|
|
|
actions.push({
|
|
|
|
key: plugin.id,
|
|
|
|
isSeparator: true,
|
|
|
|
});
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
});
|
2022-04-12 01:09:13 +08:00
|
|
|
return (
|
|
|
|
<BBBMenu
|
2022-10-25 01:20:49 +08:00
|
|
|
customStyles={!isMobile ? customStyles : null}
|
2022-04-12 01:09:13 +08:00
|
|
|
trigger={(
|
|
|
|
<ButtonEmoji
|
|
|
|
emoji="device_list_selector"
|
2022-09-13 01:10:57 +08:00
|
|
|
data-test="videoDropdownMenu"
|
2022-04-12 01:09:13 +08:00
|
|
|
hideLabel
|
|
|
|
label={intl.formatMessage(intlMessages.videoSettings)}
|
2022-06-17 00:58:54 +08:00
|
|
|
rotate
|
2022-10-04 22:21:52 +08:00
|
|
|
tabIndex={0}
|
2022-04-12 01:09:13 +08:00
|
|
|
/>
|
|
|
|
)}
|
|
|
|
actions={actions}
|
2022-10-25 01:20:49 +08:00
|
|
|
opts={{
|
2023-05-23 03:54:41 +08:00
|
|
|
id: 'video-dropdown-menu',
|
2022-10-25 01:20:49 +08:00
|
|
|
keepMounted: true,
|
|
|
|
transitionDuration: 0,
|
|
|
|
elevation: 3,
|
2023-05-10 09:31:48 +08:00
|
|
|
getcontentanchorel: null,
|
2023-05-23 03:54:41 +08:00
|
|
|
fullwidth: 'true',
|
2022-10-25 01:20:49 +08:00
|
|
|
anchorOrigin: { vertical: 'top', horizontal: 'center' },
|
2023-05-23 03:54:41 +08:00
|
|
|
transformOrigin: { vertical: 'top', horizontal: 'center' },
|
|
|
|
}}
|
2021-08-10 23:25:20 +08:00
|
|
|
/>
|
2022-04-12 01:09:13 +08:00
|
|
|
);
|
2023-05-23 03:54:41 +08:00
|
|
|
};
|
2019-05-29 04:46:29 +08:00
|
|
|
|
2019-04-19 01:14:34 +08:00
|
|
|
return (
|
2023-03-27 23:36:25 +08:00
|
|
|
<>
|
|
|
|
<Styled.OffsetBottom>
|
|
|
|
<Button
|
|
|
|
label={label}
|
|
|
|
data-test={hasVideoStream ? 'leaveVideo' : 'joinVideo'}
|
|
|
|
onClick={handleOnClick}
|
|
|
|
hideLabel
|
|
|
|
color={isSharing ? 'primary' : 'default'}
|
|
|
|
icon={isSharing ? 'video' : 'video_off'}
|
|
|
|
ghost={!isSharing}
|
|
|
|
size="lg"
|
|
|
|
circle
|
|
|
|
disabled={!!disableReason}
|
|
|
|
/>
|
|
|
|
{renderUserActions()}
|
|
|
|
</Styled.OffsetBottom>
|
2023-05-26 21:19:29 +08:00
|
|
|
{isVideoPreviewModalOpen ? (
|
2023-05-23 03:54:41 +08:00
|
|
|
<VideoPreviewContainer
|
|
|
|
{...{
|
|
|
|
callbackToClose: () => {
|
|
|
|
if (wasSelfViewDisabled) {
|
2023-05-30 02:02:29 +08:00
|
|
|
setTimeout(() => {
|
|
|
|
const obj = {
|
|
|
|
application:
|
|
|
|
{ ...Settings.application, selfViewDisable: true },
|
|
|
|
};
|
|
|
|
updateSettings(obj);
|
|
|
|
setWasSelfViewDisabled(false);
|
|
|
|
}, 100);
|
2023-05-23 03:54:41 +08:00
|
|
|
}
|
|
|
|
setPropsToPassModal({});
|
|
|
|
setForceOpen(false);
|
|
|
|
},
|
|
|
|
forceOpen,
|
2023-05-30 02:02:29 +08:00
|
|
|
priority: 'low',
|
2023-05-23 03:54:41 +08:00
|
|
|
setIsOpen: setVideoPreviewModalIsOpen,
|
2023-05-30 02:02:29 +08:00
|
|
|
isOpen: isVideoPreviewModalOpen,
|
2023-05-23 03:54:41 +08:00
|
|
|
}}
|
|
|
|
{...propsToPassModal}
|
2023-05-26 21:19:29 +08:00
|
|
|
/>
|
|
|
|
) : null}
|
2023-03-27 23:36:25 +08:00
|
|
|
</>
|
2019-04-19 01:14:34 +08:00
|
|
|
);
|
|
|
|
};
|
2019-03-23 06:45:44 +08:00
|
|
|
|
2019-01-28 21:21:29 +08:00
|
|
|
JoinVideoButton.propTypes = propTypes;
|
2019-12-19 01:44:56 +08:00
|
|
|
|
2019-06-13 02:40:58 +08:00
|
|
|
export default injectIntl(memo(JoinVideoButton));
|