import React, { memo, useState } from 'react'; import PropTypes from 'prop-types'; import { defineMessages, injectIntl } from 'react-intl'; import deviceInfo from '/imports/utils/deviceInfo'; import browserInfo from '/imports/utils/browserInfo'; import logger from '/imports/startup/client/logger'; import { notify } from '/imports/ui/services/notification'; import { useMutation } from '@apollo/client'; import Styled from './styles'; import ScreenshareBridgeService from '/imports/api/screenshare/client/bridge/service'; import { shareScreen, screenshareHasEnded, useIsCameraAsContentBroadcasting, useIsSharing, } from '/imports/ui/components/screenshare/service'; import { SCREENSHARING_ERRORS } from '/imports/api/screenshare/client/bridge/errors'; import Button from '/imports/ui/components/common/button/component'; import { EXTERNAL_VIDEO_STOP } from '../../external-video-player/mutations'; const { isMobile } = deviceInfo; const { isSafari, isTabletApp } = browserInfo; const propTypes = { intl: PropTypes.objectOf(Object).isRequired, enabled: PropTypes.bool.isRequired, amIPresenter: PropTypes.bool, isScreenBroadcasting: PropTypes.bool.isRequired, isMeteorConnected: PropTypes.bool.isRequired, screenshareDataSavingSetting: PropTypes.bool.isRequired, }; const intlMessages = defineMessages({ desktopShareLabel: { id: 'app.actionsBar.actionsDropdown.desktopShareLabel', description: 'Desktop Share option label', }, stopDesktopShareLabel: { id: 'app.actionsBar.actionsDropdown.stopDesktopShareLabel', description: 'Stop Desktop Share option label', }, desktopShareDesc: { id: 'app.actionsBar.actionsDropdown.desktopShareDesc', description: 'adds context to desktop share option', }, stopDesktopShareDesc: { id: 'app.actionsBar.actionsDropdown.stopDesktopShareDesc', description: 'adds context to stop desktop share option', }, screenShareNotSupported: { id: 'app.media.screenshare.notSupported', descriptions: 'error message when trying share screen on unsupported browsers', }, screenShareUnavailable: { id: 'app.media.screenshare.unavailable', descriptions: 'title for unavailable screen share modal', }, finalError: { id: 'app.screenshare.screenshareFinalError', description: 'Screen sharing failures with no recovery procedure', }, retryError: { id: 'app.screenshare.screenshareRetryError', description: 'Screen sharing failures where a retry is recommended', }, retryOtherEnvError: { id: 'app.screenshare.screenshareRetryOtherEnvError', description: 'Screen sharing failures where a retry in another environment is recommended', }, unsupportedEnvError: { id: 'app.screenshare.screenshareUnsupportedEnv', description: 'Screen sharing is not supported, changing browser or device is recommended', }, permissionError: { id: 'app.screenshare.screensharePermissionError', description: 'Screen sharing failure due to lack of permission', }, }); const getErrorLocale = (errorCode) => { switch (errorCode) { // Denied getDisplayMedia permission error case SCREENSHARING_ERRORS.NotAllowedError.errorCode: return intlMessages.permissionError; // Browser is supposed to be supported, but a browser-related error happening. // Suggest retrying in another device/browser/env case SCREENSHARING_ERRORS.AbortError.errorCode: case SCREENSHARING_ERRORS.InvalidStateError.errorCode: case SCREENSHARING_ERRORS.OverconstrainedError.errorCode: case SCREENSHARING_ERRORS.TypeError.errorCode: case SCREENSHARING_ERRORS.NotFoundError.errorCode: case SCREENSHARING_ERRORS.NotReadableError.errorCode: case SCREENSHARING_ERRORS.PEER_NEGOTIATION_FAILED.errorCode: case SCREENSHARING_ERRORS.SCREENSHARE_PLAY_FAILED.errorCode: case SCREENSHARING_ERRORS.MEDIA_NO_AVAILABLE_CODEC.errorCode: case SCREENSHARING_ERRORS.MEDIA_INVALID_SDP.errorCode: return intlMessages.retryOtherEnvError; // Fatal errors where a retry isn't warranted. This probably means the server // is misconfigured somehow or the provider is utterly botched, so nothing // the end user can do besides requesting support case SCREENSHARING_ERRORS.SIGNALLING_TRANSPORT_CONNECTION_FAILED.errorCode: case SCREENSHARING_ERRORS.MEDIA_SERVER_CONNECTION_ERROR.errorCode: case SCREENSHARING_ERRORS.SFU_INVALID_REQUEST.errorCode: return intlMessages.finalError; // Unsupported errors case SCREENSHARING_ERRORS.NotSupportedError.errorCode: return intlMessages.unsupportedEnvError; // Errors that should be silent/ignored. They WILL NOT be LOGGED nor NOTIFIED via toasts. case SCREENSHARING_ERRORS.ENDED_WHILE_STARTING.errorCode: return null; // Fall through: everything else is an error which might be solved with a retry default: return intlMessages.retryError; } }; const ScreenshareButton = ({ intl, enabled, isScreenBroadcasting, amIPresenter = false, isMeteorConnected, }) => { const [stopExternalVideoShare] = useMutation(EXTERNAL_VIDEO_STOP); const isCameraAsContentBroadcasting = useIsCameraAsContentBroadcasting(); const [isScreenshareUnavailableModalOpen, setScreenshareUnavailableModalIsOpen] = useState(false); // This is the failure callback that will be passed to the /api/screenshare/kurento.js // script on the presenter's call const handleFailure = (error) => { const { errorCode = SCREENSHARING_ERRORS.UNKNOWN_ERROR.errorCode, errorMessage = error.message, } = error; const localizedError = getErrorLocale(errorCode); if (localizedError) { notify(intl.formatMessage(localizedError, { 0: errorCode }), 'error', 'desktop'); logger.error({ logCode: 'screenshare_failed', extraInfo: { errorCode, errorMessage }, }, `Screenshare failed: ${errorMessage} (code=${errorCode})`); } screenshareHasEnded(); }; const RenderScreenshareUnavailableModal = (otherProps) => ( {intl.formatMessage(intlMessages.screenShareUnavailable)}

{intl.formatMessage(intlMessages.screenShareNotSupported)}

); const screenshareLabel = intlMessages.desktopShareLabel; const vLabel = isScreenBroadcasting ? intlMessages.stopDesktopShareLabel : screenshareLabel; const vDescr = isScreenBroadcasting ? intlMessages.stopDesktopShareDesc : intlMessages.desktopShareDesc; const amIBroadcasting = isScreenBroadcasting && amIPresenter; const shouldAllowScreensharing = enabled && (!isMobile || isTabletApp) && amIPresenter; const dataTest = isScreenBroadcasting ? 'stopScreenShare' : 'startScreenShare'; const isSharing = useIsSharing(); const loading = isSharing && !isScreenBroadcasting; return ( <> { shouldAllowScreensharing ? (