2017-07-25 03:29:34 +08:00
|
|
|
import React from 'react';
|
2023-03-28 05:40:08 +08:00
|
|
|
import { injectIntl } from 'react-intl';
|
2019-02-08 01:47:28 +08:00
|
|
|
import PropTypes from 'prop-types';
|
2023-08-10 00:26:42 +08:00
|
|
|
import { debounce } from '/imports/utils/debounce';
|
2023-07-05 00:14:09 +08:00
|
|
|
import { Session } from 'meteor/session';
|
2022-02-15 22:42:02 +08:00
|
|
|
import FullscreenButtonContainer from '/imports/ui/components/common/fullscreen-button/container';
|
2021-06-03 02:31:20 +08:00
|
|
|
import SwitchButtonContainer from './switch-button/container';
|
2021-11-06 00:36:03 +08:00
|
|
|
import Styled from './styles';
|
2021-11-10 21:38:24 +08:00
|
|
|
import VolumeSlider from '../external-video-player/volume-slider/component';
|
2019-08-03 05:32:42 +08:00
|
|
|
import AutoplayOverlay from '../media/autoplay-overlay/component';
|
2019-09-07 02:58:22 +08:00
|
|
|
import logger from '/imports/startup/client/logger';
|
|
|
|
import playAndRetry from '/imports/utils/mediaElementPlayRetry';
|
2021-07-21 21:38:48 +08:00
|
|
|
import { notify } from '/imports/ui/services/notification';
|
2020-12-10 06:00:54 +08:00
|
|
|
import {
|
|
|
|
SCREENSHARE_MEDIA_ELEMENT_NAME,
|
2022-05-09 09:59:25 +08:00
|
|
|
isMediaFlowing,
|
2020-12-10 06:00:54 +08:00
|
|
|
screenshareHasEnded,
|
|
|
|
screenshareHasStarted,
|
2023-05-13 04:06:23 +08:00
|
|
|
setOutputDeviceId,
|
2020-12-10 06:00:54 +08:00
|
|
|
getMediaElement,
|
2022-07-22 07:25:15 +08:00
|
|
|
getMediaElementDimensions,
|
2020-12-10 06:00:54 +08:00
|
|
|
attachLocalPreviewStream,
|
2021-11-12 01:51:21 +08:00
|
|
|
setVolume,
|
|
|
|
getVolume,
|
2022-05-09 09:59:25 +08:00
|
|
|
getStats,
|
2020-12-10 06:00:54 +08:00
|
|
|
} from '/imports/ui/components/screenshare/service';
|
|
|
|
import {
|
2022-05-09 09:59:25 +08:00
|
|
|
isStreamStateHealthy,
|
2020-12-10 06:00:54 +08:00
|
|
|
subscribeToStreamStateChange,
|
|
|
|
unsubscribeFromStreamStateChange,
|
|
|
|
} from '/imports/ui/services/bbb-webrtc-sfu/stream-state-service';
|
2021-08-12 00:19:24 +08:00
|
|
|
import { ACTIONS } from '/imports/ui/components/layout/enums';
|
2021-11-06 00:36:03 +08:00
|
|
|
import Settings from '/imports/ui/services/settings';
|
2021-12-09 01:59:02 +08:00
|
|
|
import deviceInfo from '/imports/utils/deviceInfo';
|
2023-02-23 22:23:51 +08:00
|
|
|
import { uniqueId } from '/imports/utils/string-utils';
|
2018-04-30 19:55:54 +08:00
|
|
|
|
2019-07-24 03:56:39 +08:00
|
|
|
const ALLOW_FULLSCREEN = Meteor.settings.public.app.allowFullscreen;
|
2021-12-09 01:59:02 +08:00
|
|
|
const MOBILE_HOVER_TIMEOUT = 5000;
|
2022-05-09 09:59:25 +08:00
|
|
|
const MEDIA_FLOW_PROBE_INTERVAL = 500;
|
2022-07-22 07:25:15 +08:00
|
|
|
const SCREEN_SIZE_DISPATCH_INTERVAL = 500;
|
2019-07-24 03:56:39 +08:00
|
|
|
|
2019-02-07 05:12:59 +08:00
|
|
|
class ScreenshareComponent extends React.Component {
|
2021-08-09 22:24:02 +08:00
|
|
|
static renderScreenshareContainerInside(mainText) {
|
|
|
|
return (
|
2021-11-06 00:36:03 +08:00
|
|
|
<Styled.ScreenshareContainerInside>
|
|
|
|
<Styled.MainText>{mainText}</Styled.MainText>
|
|
|
|
</Styled.ScreenshareContainerInside>
|
2021-08-09 22:24:02 +08:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2023-03-28 05:40:08 +08:00
|
|
|
constructor(props) {
|
2018-05-15 21:13:15 +08:00
|
|
|
super();
|
|
|
|
this.state = {
|
2018-05-15 22:24:13 +08:00
|
|
|
loaded: false,
|
2019-08-03 05:32:42 +08:00
|
|
|
autoplayBlocked: false,
|
2022-05-09 09:59:25 +08:00
|
|
|
mediaFlowing: false,
|
2021-06-03 02:31:20 +08:00
|
|
|
switched: false,
|
2021-12-09 01:59:02 +08:00
|
|
|
// Volume control hover toolbar
|
|
|
|
showHoverToolBar: false,
|
2018-05-15 21:13:15 +08:00
|
|
|
};
|
|
|
|
|
2020-12-10 06:00:54 +08:00
|
|
|
this.onLoadedData = this.onLoadedData.bind(this);
|
2022-07-22 07:25:15 +08:00
|
|
|
this.onLoadedMetadata = this.onLoadedMetadata.bind(this);
|
|
|
|
this.onVideoResize = this.onVideoResize.bind(this);
|
2019-08-03 05:32:42 +08:00
|
|
|
this.handleAllowAutoplay = this.handleAllowAutoplay.bind(this);
|
|
|
|
this.handlePlayElementFailed = this.handlePlayElementFailed.bind(this);
|
|
|
|
this.failedMediaElements = [];
|
2020-12-10 06:00:54 +08:00
|
|
|
this.onStreamStateChange = this.onStreamStateChange.bind(this);
|
2021-06-03 02:31:20 +08:00
|
|
|
this.onSwitched = this.onSwitched.bind(this);
|
2021-11-10 21:38:24 +08:00
|
|
|
this.handleOnVolumeChanged = this.handleOnVolumeChanged.bind(this);
|
2023-04-28 00:38:27 +08:00
|
|
|
this.dispatchScreenShareSize = this.dispatchScreenShareSize.bind(this);
|
2021-11-10 21:38:24 +08:00
|
|
|
this.handleOnMuted = this.handleOnMuted.bind(this);
|
2023-05-24 23:05:28 +08:00
|
|
|
this.dispatchScreenShareSize = this.dispatchScreenShareSize.bind(this);
|
2023-03-01 21:39:04 +08:00
|
|
|
this.debouncedDispatchScreenShareSize = debounce(
|
2023-04-28 00:38:27 +08:00
|
|
|
this.dispatchScreenShareSize,
|
2023-08-10 00:26:42 +08:00
|
|
|
SCREEN_SIZE_DISPATCH_INTERVAL,
|
|
|
|
{ leading: false, trailing: true },
|
2022-07-22 07:25:15 +08:00
|
|
|
);
|
2023-04-28 00:38:27 +08:00
|
|
|
|
2023-03-28 05:40:08 +08:00
|
|
|
const { locales, icon } = props;
|
|
|
|
this.locales = locales;
|
|
|
|
this.icon = icon;
|
|
|
|
|
2021-12-09 00:24:26 +08:00
|
|
|
this.volume = getVolume();
|
2021-12-09 01:59:02 +08:00
|
|
|
this.mobileHoverSetTimeout = null;
|
2022-05-09 09:59:25 +08:00
|
|
|
this.mediaFlowMonitor = null;
|
2018-05-15 21:13:15 +08:00
|
|
|
}
|
2019-01-17 00:50:24 +08:00
|
|
|
|
2017-07-25 03:29:34 +08:00
|
|
|
componentDidMount() {
|
2021-08-10 03:06:31 +08:00
|
|
|
const {
|
2022-02-10 03:45:43 +08:00
|
|
|
isLayoutSwapped,
|
2021-08-10 03:06:31 +08:00
|
|
|
layoutContextDispatch,
|
|
|
|
intl,
|
2021-11-24 00:43:49 +08:00
|
|
|
isPresenter,
|
2023-03-28 05:40:08 +08:00
|
|
|
startPreviewSizeBig,
|
2023-05-13 04:06:23 +08:00
|
|
|
outputDeviceId,
|
2023-07-05 00:14:09 +08:00
|
|
|
isSharedNotesPinned,
|
2021-08-10 03:06:31 +08:00
|
|
|
} = this.props;
|
2021-07-21 21:38:48 +08:00
|
|
|
|
2023-05-24 09:07:32 +08:00
|
|
|
screenshareHasStarted(isPresenter, { outputDeviceId });
|
2020-12-10 06:00:54 +08:00
|
|
|
// Autoplay failure handling
|
2019-08-03 05:32:42 +08:00
|
|
|
window.addEventListener('screensharePlayFailed', this.handlePlayElementFailed);
|
2020-12-10 06:00:54 +08:00
|
|
|
// Stream health state tracker to propagate UI changes on reconnections
|
|
|
|
subscribeToStreamStateChange('screenshare', this.onStreamStateChange);
|
|
|
|
// Attaches the local stream if it exists to serve as the local presenter preview
|
|
|
|
attachLocalPreviewStream(getMediaElement());
|
2021-07-21 21:38:48 +08:00
|
|
|
|
2023-03-28 05:40:08 +08:00
|
|
|
this.setState({ switched: startPreviewSizeBig });
|
|
|
|
|
|
|
|
notify(intl.formatMessage(this.locales.started), 'info', this.icon);
|
2021-08-10 03:06:31 +08:00
|
|
|
|
2022-02-08 03:29:27 +08:00
|
|
|
layoutContextDispatch({
|
|
|
|
type: ACTIONS.SET_HAS_SCREEN_SHARE,
|
|
|
|
value: true,
|
|
|
|
});
|
2022-02-26 01:56:03 +08:00
|
|
|
|
|
|
|
if (isLayoutSwapped) {
|
|
|
|
layoutContextDispatch({
|
|
|
|
type: ACTIONS.SET_PRESENTATION_IS_OPEN,
|
|
|
|
value: true,
|
|
|
|
});
|
|
|
|
}
|
2023-07-05 00:14:09 +08:00
|
|
|
Session.set('pinnedNotesLastState', isSharedNotesPinned);
|
2017-07-25 03:29:34 +08:00
|
|
|
}
|
2019-01-17 00:50:24 +08:00
|
|
|
|
2020-08-08 04:32:46 +08:00
|
|
|
componentDidUpdate(prevProps) {
|
2023-05-13 04:06:23 +08:00
|
|
|
const { isPresenter, outputDeviceId } = this.props;
|
2021-11-04 22:35:25 +08:00
|
|
|
if (prevProps.isPresenter && !isPresenter) {
|
2020-12-10 06:00:54 +08:00
|
|
|
screenshareHasEnded();
|
2018-03-15 02:28:28 +08:00
|
|
|
}
|
2023-05-24 09:07:32 +08:00
|
|
|
|
2023-05-13 04:06:23 +08:00
|
|
|
if (prevProps.outputDeviceId !== outputDeviceId && !isPresenter) {
|
2023-05-24 09:07:32 +08:00
|
|
|
setOutputDeviceId(outputDeviceId);
|
2023-05-13 04:06:23 +08:00
|
|
|
}
|
2018-03-15 02:28:28 +08:00
|
|
|
}
|
2019-01-17 00:50:24 +08:00
|
|
|
|
2018-03-15 02:28:28 +08:00
|
|
|
componentWillUnmount() {
|
2021-11-06 00:36:03 +08:00
|
|
|
const {
|
2022-01-14 20:43:48 +08:00
|
|
|
intl,
|
|
|
|
fullscreenContext,
|
|
|
|
layoutContextDispatch,
|
|
|
|
toggleSwapLayout,
|
2023-07-05 00:14:09 +08:00
|
|
|
pinSharedNotes,
|
2024-01-20 02:40:27 +08:00
|
|
|
stopExternalVideoShare,
|
2021-11-06 00:36:03 +08:00
|
|
|
} = this.props;
|
2020-12-10 06:00:54 +08:00
|
|
|
screenshareHasEnded();
|
2019-08-03 05:32:42 +08:00
|
|
|
window.removeEventListener('screensharePlayFailed', this.handlePlayElementFailed);
|
2020-12-10 06:00:54 +08:00
|
|
|
unsubscribeFromStreamStateChange('screenshare', this.onStreamStateChange);
|
2021-07-21 21:38:48 +08:00
|
|
|
|
2022-02-08 01:46:45 +08:00
|
|
|
if (Settings.dataSaving.viewScreenshare) {
|
2023-03-28 05:40:08 +08:00
|
|
|
notify(intl.formatMessage(this.locales.ended), 'info', this.icon);
|
2022-02-08 01:46:45 +08:00
|
|
|
} else {
|
2023-03-28 05:40:08 +08:00
|
|
|
notify(intl.formatMessage(this.locales.endedDueToDataSaving), 'info', this.icon);
|
2022-02-08 01:27:14 +08:00
|
|
|
}
|
2021-08-12 00:19:24 +08:00
|
|
|
|
2022-02-08 03:29:27 +08:00
|
|
|
layoutContextDispatch({
|
|
|
|
type: ACTIONS.SET_HAS_SCREEN_SHARE,
|
|
|
|
value: false,
|
|
|
|
});
|
2021-08-12 00:19:24 +08:00
|
|
|
|
|
|
|
if (fullscreenContext) {
|
|
|
|
layoutContextDispatch({
|
|
|
|
type: ACTIONS.SET_FULLSCREEN_ELEMENT,
|
|
|
|
value: {
|
|
|
|
element: '',
|
|
|
|
group: '',
|
|
|
|
},
|
|
|
|
});
|
|
|
|
}
|
2021-09-28 03:57:02 +08:00
|
|
|
|
2022-05-09 09:59:25 +08:00
|
|
|
this.clearMediaFlowingMonitor();
|
2023-04-11 03:25:19 +08:00
|
|
|
layoutContextDispatch({
|
|
|
|
type: ACTIONS.SET_PRESENTATION_IS_OPEN,
|
|
|
|
value: Session.get('presentationLastState'),
|
|
|
|
});
|
2023-07-05 00:14:09 +08:00
|
|
|
|
2024-01-20 02:40:27 +08:00
|
|
|
pinSharedNotes(Session.get('pinnedNotesLastState'), stopExternalVideoShare);
|
2022-05-09 09:59:25 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
clearMediaFlowingMonitor() {
|
|
|
|
if (this.mediaFlowMonitor) {
|
|
|
|
Meteor.clearInterval(this.mediaFlowMonitor);
|
|
|
|
this.mediaFlowMonitor = null;
|
|
|
|
}
|
2020-12-10 06:00:54 +08:00
|
|
|
}
|
|
|
|
|
2019-08-03 05:32:42 +08:00
|
|
|
handleAllowAutoplay() {
|
|
|
|
const { autoplayBlocked } = this.state;
|
|
|
|
|
2019-09-07 02:58:22 +08:00
|
|
|
logger.info({
|
|
|
|
logCode: 'screenshare_autoplay_allowed',
|
|
|
|
}, 'Screenshare media autoplay allowed by the user');
|
|
|
|
|
2019-08-03 05:32:42 +08:00
|
|
|
window.removeEventListener('screensharePlayFailed', this.handlePlayElementFailed);
|
|
|
|
while (this.failedMediaElements.length) {
|
|
|
|
const mediaElement = this.failedMediaElements.shift();
|
|
|
|
if (mediaElement) {
|
2019-09-07 02:58:22 +08:00
|
|
|
const played = playAndRetry(mediaElement);
|
|
|
|
if (!played) {
|
|
|
|
logger.error({
|
|
|
|
logCode: 'screenshare_autoplay_handling_failed',
|
|
|
|
}, 'Screenshare autoplay handling failed to play media');
|
|
|
|
} else {
|
|
|
|
logger.info({
|
|
|
|
logCode: 'screenshare_viewer_media_play_success',
|
|
|
|
}, 'Screenshare viewer media played successfully');
|
|
|
|
}
|
2019-08-03 05:32:42 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if (autoplayBlocked) { this.setState({ autoplayBlocked: false }); }
|
|
|
|
}
|
|
|
|
|
|
|
|
handlePlayElementFailed(e) {
|
|
|
|
const { mediaElement } = e.detail;
|
|
|
|
const { autoplayBlocked } = this.state;
|
|
|
|
|
|
|
|
e.stopPropagation();
|
|
|
|
this.failedMediaElements.push(mediaElement);
|
|
|
|
if (!autoplayBlocked) {
|
2019-09-07 02:58:22 +08:00
|
|
|
logger.info({
|
|
|
|
logCode: 'screenshare_autoplay_prompt',
|
|
|
|
}, 'Prompting user for action to play screenshare media');
|
|
|
|
|
2019-08-03 05:32:42 +08:00
|
|
|
this.setState({ autoplayBlocked: true });
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-05-09 09:59:25 +08:00
|
|
|
async monitorMediaFlow() {
|
|
|
|
let previousStats = await getStats();
|
|
|
|
this.mediaFlowMonitor = Meteor.setInterval(async () => {
|
|
|
|
const { mediaFlowing: prevMediaFlowing } = this.state;
|
|
|
|
let mediaFlowing;
|
2021-08-09 22:24:02 +08:00
|
|
|
|
2022-05-09 09:59:25 +08:00
|
|
|
const currentStats = await getStats();
|
|
|
|
|
|
|
|
try {
|
|
|
|
mediaFlowing = isMediaFlowing(previousStats, currentStats);
|
2023-02-16 07:01:05 +08:00
|
|
|
} catch (error) {
|
2022-05-09 09:59:25 +08:00
|
|
|
// Stats processing failed for whatever reason - maintain previous state
|
|
|
|
mediaFlowing = prevMediaFlowing;
|
2023-02-16 07:01:05 +08:00
|
|
|
logger.warn({
|
|
|
|
logCode: 'screenshare_media_monitor_stats_failed',
|
|
|
|
extraInfo: {
|
|
|
|
errorName: error.name,
|
|
|
|
errorMessage: error.message,
|
|
|
|
},
|
|
|
|
}, 'Failed to collect screenshare stats, flow monitor');
|
2022-05-09 09:59:25 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
previousStats = currentStats;
|
|
|
|
|
|
|
|
if (prevMediaFlowing !== mediaFlowing) this.setState({ mediaFlowing });
|
|
|
|
}, MEDIA_FLOW_PROBE_INTERVAL);
|
2021-08-09 22:24:02 +08:00
|
|
|
}
|
|
|
|
|
2022-07-22 07:25:15 +08:00
|
|
|
dispatchScreenShareSize() {
|
|
|
|
const {
|
|
|
|
layoutContextDispatch,
|
|
|
|
} = this.props;
|
|
|
|
|
|
|
|
const { width, height } = getMediaElementDimensions();
|
|
|
|
const value = {
|
|
|
|
width,
|
|
|
|
height,
|
|
|
|
browserWidth: window.innerWidth,
|
|
|
|
browserHeight: window.innerHeight,
|
2023-04-28 00:38:27 +08:00
|
|
|
};
|
2022-07-22 07:25:15 +08:00
|
|
|
|
|
|
|
layoutContextDispatch({
|
|
|
|
type: ACTIONS.SET_SCREEN_SHARE_SIZE,
|
|
|
|
value,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
onLoadedMetadata() {
|
|
|
|
const element = getMediaElement();
|
|
|
|
|
|
|
|
// Track HTMLVideo's resize event to propagate stream size changes to the
|
|
|
|
// layout engine. See this.onVideoResize;
|
|
|
|
if (element && typeof element.onresize !== 'function') {
|
|
|
|
element.onresize = this.onVideoResize;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Dispatch the initial stream size to the layout engine
|
|
|
|
this.dispatchScreenShareSize();
|
|
|
|
}
|
|
|
|
|
2021-08-09 22:24:02 +08:00
|
|
|
onLoadedData() {
|
|
|
|
this.setState({ loaded: true });
|
|
|
|
}
|
|
|
|
|
|
|
|
onSwitched() {
|
|
|
|
this.setState((prevState) => ({ switched: !prevState.switched }));
|
|
|
|
}
|
|
|
|
|
2021-11-10 21:38:24 +08:00
|
|
|
handleOnVolumeChanged(volume) {
|
|
|
|
this.volume = volume;
|
2021-11-12 01:51:21 +08:00
|
|
|
setVolume(volume);
|
2021-11-10 21:38:24 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
handleOnMuted(muted) {
|
2021-11-12 01:51:21 +08:00
|
|
|
if (muted) {
|
|
|
|
setVolume(0);
|
|
|
|
} else {
|
|
|
|
setVolume(this.volume);
|
|
|
|
}
|
2021-11-10 21:38:24 +08:00
|
|
|
}
|
|
|
|
|
2023-04-28 00:38:27 +08:00
|
|
|
onVideoResize() {
|
|
|
|
// Debounced version of the dispatcher to pace things out - we don't want
|
|
|
|
// to hog the CPU just for resize recalculations...
|
|
|
|
this.debouncedDispatchScreenShareSize();
|
|
|
|
}
|
|
|
|
|
2022-05-09 09:59:25 +08:00
|
|
|
onStreamStateChange(event) {
|
|
|
|
const { streamState } = event.detail;
|
|
|
|
const { mediaFlowing } = this.state;
|
|
|
|
|
|
|
|
const isStreamHealthy = isStreamStateHealthy(streamState);
|
|
|
|
event.stopPropagation();
|
|
|
|
|
|
|
|
if (isStreamHealthy) {
|
|
|
|
this.clearMediaFlowingMonitor();
|
|
|
|
// Current state is media not flowing - stream is now healthy so flip it
|
|
|
|
if (!mediaFlowing) this.setState({ mediaFlowing: isStreamHealthy });
|
2023-02-16 07:01:05 +08:00
|
|
|
} else if (this.mediaFlowMonitor == null) this.monitorMediaFlow();
|
2022-05-09 09:59:25 +08:00
|
|
|
}
|
|
|
|
|
2019-01-08 02:12:28 +08:00
|
|
|
renderFullscreenButton() {
|
2021-08-12 00:29:09 +08:00
|
|
|
const { intl, fullscreenElementId, fullscreenContext } = this.props;
|
2019-07-24 03:56:39 +08:00
|
|
|
|
|
|
|
if (!ALLOW_FULLSCREEN) return null;
|
2019-02-07 05:12:59 +08:00
|
|
|
|
|
|
|
return (
|
2019-04-24 22:20:53 +08:00
|
|
|
<FullscreenButtonContainer
|
2023-05-31 22:12:21 +08:00
|
|
|
key={uniqueId('fullscreenButton-')}
|
2023-03-28 05:40:08 +08:00
|
|
|
elementName={intl.formatMessage(this.locales.label)}
|
2019-07-24 03:56:39 +08:00
|
|
|
fullscreenRef={this.screenshareContainer}
|
2021-07-07 03:27:28 +08:00
|
|
|
elementId={fullscreenElementId}
|
2021-08-12 00:29:09 +08:00
|
|
|
isFullscreen={fullscreenContext}
|
2019-07-24 03:56:39 +08:00
|
|
|
dark
|
2019-02-07 05:12:59 +08:00
|
|
|
/>
|
|
|
|
);
|
2019-01-08 02:12:28 +08:00
|
|
|
}
|
|
|
|
|
2020-12-10 06:00:54 +08:00
|
|
|
renderAutoplayOverlay() {
|
2019-08-03 05:32:42 +08:00
|
|
|
const { intl } = this.props;
|
2019-01-08 02:12:28 +08:00
|
|
|
|
2017-07-25 03:29:34 +08:00
|
|
|
return (
|
2020-12-10 06:00:54 +08:00
|
|
|
<AutoplayOverlay
|
2023-05-31 22:12:21 +08:00
|
|
|
key={uniqueId('screenshareAutoplayOverlay')}
|
2023-03-28 05:40:08 +08:00
|
|
|
autoplayBlockedDesc={intl.formatMessage(this.locales.autoplayBlockedDesc)}
|
|
|
|
autoplayAllowLabel={intl.formatMessage(this.locales.autoplayAllowLabel)}
|
2020-12-10 06:00:54 +08:00
|
|
|
handleAllowAutoplay={this.handleAllowAutoplay}
|
|
|
|
/>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2021-06-03 02:31:20 +08:00
|
|
|
renderSwitchButton() {
|
2023-03-28 05:40:08 +08:00
|
|
|
const { showSwitchPreviewSizeButton } = this.props;
|
2021-06-03 02:31:20 +08:00
|
|
|
const { switched } = this.state;
|
|
|
|
|
2023-03-28 05:40:08 +08:00
|
|
|
if (!showSwitchPreviewSizeButton) return null;
|
|
|
|
|
2021-06-03 02:31:20 +08:00
|
|
|
return (
|
|
|
|
<SwitchButtonContainer
|
|
|
|
handleSwitch={this.onSwitched}
|
|
|
|
switched={switched}
|
|
|
|
dark
|
|
|
|
/>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2021-12-09 01:59:02 +08:00
|
|
|
renderMobileVolumeControlOverlay () {
|
|
|
|
return (
|
2022-01-13 00:40:45 +08:00
|
|
|
<Styled.MobileControlsOverlay
|
2021-12-09 01:59:02 +08:00
|
|
|
key="mobile-overlay-screenshare"
|
|
|
|
ref={(ref) => { this.overlay = ref; }}
|
|
|
|
onTouchStart={() => {
|
|
|
|
clearTimeout(this.mobileHoverSetTimeout);
|
|
|
|
this.setState({ showHoverToolBar: true });
|
|
|
|
}}
|
|
|
|
onTouchEnd={() => {
|
|
|
|
this.mobileHoverSetTimeout = setTimeout(
|
|
|
|
() => this.setState({ showHoverToolBar: false }),
|
|
|
|
MOBILE_HOVER_TIMEOUT,
|
|
|
|
);
|
|
|
|
}}
|
|
|
|
/>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2021-11-10 21:38:24 +08:00
|
|
|
renderVolumeSlider() {
|
2021-12-09 01:59:02 +08:00
|
|
|
const { showHoverToolBar } = this.state;
|
2022-01-13 00:40:45 +08:00
|
|
|
|
|
|
|
let toolbarStyle = 'hoverToolbar';
|
|
|
|
|
|
|
|
if (deviceInfo.isMobile && !showHoverToolBar) {
|
|
|
|
toolbarStyle = 'dontShowMobileHoverToolbar';
|
|
|
|
}
|
|
|
|
|
|
|
|
if (deviceInfo.isMobile && showHoverToolBar) {
|
|
|
|
toolbarStyle = 'showMobileHoverToolbar';
|
|
|
|
}
|
2021-12-09 01:59:02 +08:00
|
|
|
|
|
|
|
return [(
|
2022-01-13 00:40:45 +08:00
|
|
|
<Styled.HoverToolbar
|
|
|
|
toolbarStyle={toolbarStyle}
|
|
|
|
key='hover-toolbar-screenshare'>
|
2021-11-10 21:38:24 +08:00
|
|
|
<VolumeSlider
|
2021-11-12 01:51:21 +08:00
|
|
|
volume={getVolume()}
|
|
|
|
muted={getVolume() === 0}
|
2021-11-10 21:38:24 +08:00
|
|
|
onVolumeChanged={this.handleOnVolumeChanged}
|
|
|
|
onMuted={this.handleOnMuted}
|
|
|
|
/>
|
2022-01-13 00:40:45 +08:00
|
|
|
</Styled.HoverToolbar>
|
2021-12-09 01:59:02 +08:00
|
|
|
),
|
|
|
|
(deviceInfo.isMobile) && this.renderMobileVolumeControlOverlay(),
|
|
|
|
];
|
2021-11-10 21:38:24 +08:00
|
|
|
}
|
|
|
|
|
2021-06-03 02:31:20 +08:00
|
|
|
renderVideo(switched) {
|
2021-07-10 00:36:23 +08:00
|
|
|
const { isGloballyBroadcasting } = this.props;
|
2022-05-09 09:59:25 +08:00
|
|
|
const { mediaFlowing } = this.state;
|
2021-07-10 00:36:23 +08:00
|
|
|
|
2021-06-03 02:31:20 +08:00
|
|
|
return (
|
2021-11-06 00:36:03 +08:00
|
|
|
<Styled.ScreenshareVideo
|
2021-06-03 02:31:20 +08:00
|
|
|
id={SCREENSHARE_MEDIA_ELEMENT_NAME}
|
|
|
|
key={SCREENSHARE_MEDIA_ELEMENT_NAME}
|
2022-05-09 09:59:25 +08:00
|
|
|
unhealthyStream={!isGloballyBroadcasting || !mediaFlowing}
|
2021-06-03 02:31:20 +08:00
|
|
|
style={switched
|
|
|
|
? { maxHeight: '100%', width: '100%', height: '100%' }
|
|
|
|
: { maxHeight: '25%', width: '25%', height: '25%' }}
|
|
|
|
playsInline
|
|
|
|
onLoadedData={this.onLoadedData}
|
2022-07-22 07:25:15 +08:00
|
|
|
onLoadedMetadata={this.onLoadedMetadata}
|
2021-06-03 02:31:20 +08:00
|
|
|
ref={(ref) => {
|
|
|
|
this.videoTag = ref;
|
|
|
|
}}
|
|
|
|
muted
|
|
|
|
/>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
renderScreensharePresenter() {
|
2021-08-02 22:35:46 +08:00
|
|
|
const { switched } = this.state;
|
2021-06-03 02:31:20 +08:00
|
|
|
const { isGloballyBroadcasting, intl } = this.props;
|
|
|
|
|
|
|
|
return (
|
2021-11-06 00:36:03 +08:00
|
|
|
<Styled.ScreenshareContainer
|
|
|
|
switched={switched}
|
2021-06-03 02:31:20 +08:00
|
|
|
key="screenshareContainer"
|
|
|
|
ref={(ref) => { this.screenshareContainer = ref; }}
|
|
|
|
>
|
2021-08-02 22:35:46 +08:00
|
|
|
{isGloballyBroadcasting && this.renderSwitchButton()}
|
2021-06-03 02:31:20 +08:00
|
|
|
{this.renderVideo(switched)}
|
|
|
|
|
2021-08-09 22:24:02 +08:00
|
|
|
{
|
|
|
|
isGloballyBroadcasting
|
|
|
|
? (
|
2021-09-23 00:51:35 +08:00
|
|
|
<div data-test="isSharingScreen">
|
2021-08-09 22:24:02 +08:00
|
|
|
{!switched
|
|
|
|
&& ScreenshareComponent.renderScreenshareContainerInside(
|
2023-03-28 05:40:08 +08:00
|
|
|
intl.formatMessage(this.locales.presenterSharingLabel),
|
2021-08-09 22:24:02 +08:00
|
|
|
)}
|
|
|
|
</div>
|
|
|
|
)
|
|
|
|
: ScreenshareComponent.renderScreenshareContainerInside(
|
2023-03-28 05:40:08 +08:00
|
|
|
intl.formatMessage(this.locales.presenterLoadingLabel),
|
2021-08-09 22:24:02 +08:00
|
|
|
)
|
2021-06-03 02:31:20 +08:00
|
|
|
}
|
2021-11-06 00:36:03 +08:00
|
|
|
</Styled.ScreenshareContainer>
|
2021-06-03 02:31:20 +08:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
renderScreenshareDefault() {
|
2021-12-09 00:24:26 +08:00
|
|
|
const { intl, enableVolumeControl } = this.props;
|
2021-08-12 00:29:09 +08:00
|
|
|
const { loaded } = this.state;
|
2021-06-03 02:31:20 +08:00
|
|
|
|
|
|
|
return (
|
2021-11-06 00:36:03 +08:00
|
|
|
<Styled.ScreenshareContainer
|
|
|
|
switched
|
2021-06-03 02:31:20 +08:00
|
|
|
key="screenshareContainer"
|
|
|
|
ref={(ref) => {
|
|
|
|
this.screenshareContainer = ref;
|
|
|
|
}}
|
2022-09-02 00:48:59 +08:00
|
|
|
id="screenshareContainer"
|
2021-06-03 02:31:20 +08:00
|
|
|
>
|
|
|
|
{loaded && this.renderFullscreenButton()}
|
|
|
|
{this.renderVideo(true)}
|
2021-12-09 00:24:26 +08:00
|
|
|
{loaded && enableVolumeControl && this.renderVolumeSlider() }
|
2021-07-10 00:36:23 +08:00
|
|
|
|
2021-11-06 00:36:03 +08:00
|
|
|
<Styled.ScreenshareContainerDefault>
|
2021-08-09 22:24:02 +08:00
|
|
|
{
|
|
|
|
!loaded
|
|
|
|
? ScreenshareComponent.renderScreenshareContainerInside(
|
2023-03-28 05:40:08 +08:00
|
|
|
intl.formatMessage(this.locales.viewerLoadingLabel),
|
2021-08-09 22:24:02 +08:00
|
|
|
)
|
|
|
|
: null
|
2021-07-10 00:36:23 +08:00
|
|
|
}
|
2021-11-06 00:36:03 +08:00
|
|
|
</Styled.ScreenshareContainerDefault>
|
|
|
|
</Styled.ScreenshareContainer>
|
2021-06-03 02:31:20 +08:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2017-07-25 03:29:34 +08:00
|
|
|
render() {
|
2022-05-09 09:59:25 +08:00
|
|
|
const { loaded, autoplayBlocked, mediaFlowing} = this.state;
|
2021-07-07 03:27:28 +08:00
|
|
|
const {
|
|
|
|
isPresenter,
|
|
|
|
isGloballyBroadcasting,
|
|
|
|
top,
|
|
|
|
left,
|
2021-07-27 04:28:05 +08:00
|
|
|
right,
|
2021-07-07 03:27:28 +08:00
|
|
|
width,
|
|
|
|
height,
|
|
|
|
zIndex,
|
2021-11-20 02:51:42 +08:00
|
|
|
fullscreenContext,
|
2021-07-07 03:27:28 +08:00
|
|
|
} = this.props;
|
2021-02-06 06:18:39 +08:00
|
|
|
|
2021-07-10 00:36:23 +08:00
|
|
|
// Conditions to render the (re)connecting dots and the unhealthy stream
|
2021-02-06 06:18:39 +08:00
|
|
|
// grayscale:
|
|
|
|
// 1 - The local media tag has not received any stream data yet
|
|
|
|
// 2 - The user is a presenter and the stream wasn't globally broadcasted yet
|
|
|
|
// 3 - The media was loaded, the stream was globally broadcasted BUT the stream
|
|
|
|
// state transitioned to an unhealthy stream. tl;dr: screen sharing reconnection
|
|
|
|
const shouldRenderConnectingState = !loaded
|
|
|
|
|| (isPresenter && !isGloballyBroadcasting)
|
2022-05-09 09:59:25 +08:00
|
|
|
|| (!mediaFlowing && loaded && isGloballyBroadcasting);
|
2019-01-08 02:12:28 +08:00
|
|
|
|
2021-09-01 00:21:31 +08:00
|
|
|
const display = (width > 0 && height > 0) ? 'inherit' : 'none';
|
2021-11-06 00:36:03 +08:00
|
|
|
const { animations } = Settings.application;
|
2021-09-01 00:21:31 +08:00
|
|
|
|
2017-07-25 03:29:34 +08:00
|
|
|
return (
|
2021-06-15 19:51:44 +08:00
|
|
|
<div
|
|
|
|
style={
|
2021-08-05 12:22:07 +08:00
|
|
|
{
|
|
|
|
position: 'absolute',
|
2021-09-01 00:21:31 +08:00
|
|
|
display,
|
2021-08-05 12:22:07 +08:00
|
|
|
top,
|
|
|
|
left,
|
|
|
|
right,
|
|
|
|
height,
|
|
|
|
width,
|
2021-11-20 02:51:42 +08:00
|
|
|
zIndex: fullscreenContext ? zIndex : undefined,
|
2021-08-05 12:22:07 +08:00
|
|
|
backgroundColor: '#06172A',
|
|
|
|
}
|
2021-06-15 19:51:44 +08:00
|
|
|
}
|
|
|
|
>
|
|
|
|
{(shouldRenderConnectingState)
|
|
|
|
&& (
|
2021-11-06 00:36:03 +08:00
|
|
|
<Styled.SpinnerWrapper
|
2023-02-23 22:23:51 +08:00
|
|
|
key={uniqueId('screenshareArea-')}
|
2021-06-15 19:51:44 +08:00
|
|
|
data-test="screenshareConnecting"
|
2021-07-10 00:36:23 +08:00
|
|
|
>
|
2021-11-06 00:36:03 +08:00
|
|
|
<Styled.Spinner animations={animations}>
|
|
|
|
<Styled.Bounce1 animations={animations} />
|
|
|
|
<Styled.Bounce2 animations={animations} />
|
2021-07-10 00:36:23 +08:00
|
|
|
<div />
|
2021-11-06 00:36:03 +08:00
|
|
|
</Styled.Spinner>
|
|
|
|
</Styled.SpinnerWrapper>
|
2021-06-15 19:51:44 +08:00
|
|
|
)}
|
|
|
|
{autoplayBlocked ? this.renderAutoplayOverlay() : null}
|
|
|
|
{isPresenter ? this.renderScreensharePresenter() : this.renderScreenshareDefault()}
|
|
|
|
</div>
|
2017-07-25 03:29:34 +08:00
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
2019-02-07 05:12:59 +08:00
|
|
|
|
2021-08-05 15:26:03 +08:00
|
|
|
export default injectIntl(ScreenshareComponent);
|
2019-02-07 05:12:59 +08:00
|
|
|
|
|
|
|
ScreenshareComponent.propTypes = {
|
2021-08-09 22:24:02 +08:00
|
|
|
intl: PropTypes.shape({
|
|
|
|
formatMessage: PropTypes.func.isRequired,
|
|
|
|
}).isRequired,
|
2019-02-08 01:47:28 +08:00
|
|
|
isPresenter: PropTypes.bool.isRequired,
|
2023-04-28 00:38:27 +08:00
|
|
|
layoutContextDispatch: PropTypes.func.isRequired,
|
2021-12-09 00:24:26 +08:00
|
|
|
enableVolumeControl: PropTypes.bool.isRequired,
|
2023-05-24 09:07:32 +08:00
|
|
|
outputDeviceId: PropTypes.string,
|
2022-02-10 03:45:43 +08:00
|
|
|
};
|