diff --git a/css/modals/device-selection/_device-selection.scss b/css/modals/device-selection/_device-selection.scss
index ab1428856..4ac49d92e 100644
--- a/css/modals/device-selection/_device-selection.scss
+++ b/css/modals/device-selection/_device-selection.scss
@@ -79,7 +79,7 @@
border-radius: 3px;
}
- .video-input-preview-muted {
+ .video-input-preview-error {
color: $participantNameColor;
display: none;
left: 0;
@@ -89,12 +89,10 @@
top: 50%;
}
- &.video-muted {
- /* TOFIX: to be removed when we move out from muted preview */
+ &.video-preview-has-error {
background: black;
- /* TOFIX-END */
- .video-input-preview-muted {
+ .video-input-preview-error {
display: block;
}
}
diff --git a/lang/main.json b/lang/main.json
index 22bc14cb8..4ce44012b 100644
--- a/lang/main.json
+++ b/lang/main.json
@@ -429,9 +429,9 @@
"speakerTime": "Speaker Time"
},
"deviceSelection": {
- "currentlyVideoMuted": "Video is currently muted",
"deviceSettings": "Device settings",
"noPermission": "Permission not granted",
+ "previewUnavailable": "Preview unavailable",
"selectADevice": "Select a device",
"testAudio": "Test sound"
},
diff --git a/react/features/device-selection/actions.js b/react/features/device-selection/actions.js
index dd0b0f6f4..f08a6025c 100644
--- a/react/features/device-selection/actions.js
+++ b/react/features/device-selection/actions.js
@@ -12,16 +12,13 @@ import { DeviceSelectionDialog } from './components';
* @returns {Function}
*/
export function openDeviceSelectionDialog() {
- return (dispatch, getState) => {
+ return dispatch => {
JitsiMeetJS.mediaDevices.isDeviceListAvailable()
.then(isDeviceListAvailable => {
- const state = getState();
- const conference = state['features/base/conference'].conference;
-
dispatch(openDialog(DeviceSelectionDialog, {
+ currentAudioInputId: APP.settings.getMicDeviceId(),
currentAudioOutputId: APP.settings.getAudioOutputDeviceId(),
- currentAudioTrack: conference.getLocalAudioTrack(),
- currentVideoTrack: conference.getLocalVideoTrack(),
+ currentVideoInputId: APP.settings.getCameraDeviceId(),
disableAudioInputChange:
!JitsiMeetJS.isMultipleAudioInputSupported(),
disableDeviceChange: !isDeviceListAvailable
diff --git a/react/features/device-selection/components/DeviceSelectionDialog.js b/react/features/device-selection/components/DeviceSelectionDialog.js
index 4eca98476..abb8198b2 100644
--- a/react/features/device-selection/components/DeviceSelectionDialog.js
+++ b/react/features/device-selection/components/DeviceSelectionDialog.js
@@ -34,29 +34,25 @@ class DeviceSelectionDialog extends Component {
* All known audio and video devices split by type. This prop comes from
* the app state.
*/
- _devices: React.PropTypes.object,
+ _availableDevices: React.PropTypes.object,
/**
- * Device id for the current audio output device.
+ * Device id for the current audio input device. This device will be set
+ * as the default audio input device to preview.
+ */
+ currentAudioInputId: React.PropTypes.string,
+
+ /**
+ * Device id for the current audio output device. This device will be
+ * set as the default audio output device to preview.
*/
currentAudioOutputId: React.PropTypes.string,
/**
- * JitsiLocalTrack for the current local audio.
- *
- * JitsiLocalTracks for the current audio and video, if any, should be
- * passed in for re-use in the previews. This is needed for Internet
- * Explorer, which cannot get multiple tracks from the same device, even
- * across tabs.
+ * Device id for the current video input device. This device will be set
+ * as the default video input device to preview.
*/
- currentAudioTrack: React.PropTypes.object,
-
- /**
- * JitsiLocalTrack for the current local video.
- *
- * Needed for reuse. See comment for propTypes.currentAudioTrack.
- */
- currentVideoTrack: React.PropTypes.object,
+ currentVideoInputId: React.PropTypes.string,
/**
* Whether or not the audio selector can be interacted with. If true,
@@ -78,12 +74,12 @@ class DeviceSelectionDialog extends Component {
dispatch: React.PropTypes.func,
/**
- * Whether or not new audio input source can be selected.
+ * Whether or not a new audio input source can be selected.
*/
hasAudioPermission: React.PropTypes.bool,
/**
- * Whether or not new video input sources can be selected.
+ * Whether or not a new video input sources can be selected.
*/
hasVideoPermission: React.PropTypes.bool,
@@ -117,15 +113,40 @@ class DeviceSelectionDialog extends Component {
constructor(props) {
super(props);
+ const { _availableDevices } = this.props;
+
this.state = {
- // JitsiLocalTracks to use for live previewing.
+ // JitsiLocalTrack to use for live previewing of audio input.
previewAudioTrack: null,
+
+ // JitsiLocalTrack to use for live previewing of video input.
previewVideoTrack: null,
- // Device ids to keep track of new selections.
- videInput: null,
- audioInput: null,
- audioOutput: null
+ // An message describing a problem with obtaining a video preview.
+ previewVideoTrackError: null,
+
+ // The audio input device id to show as selected by default.
+ selectedAudioInputId: this.props.currentAudioInputId || '',
+
+ // The audio output device id to show as selected by default.
+ selectedAudioOutputId: this.props.currentAudioOutputId || '',
+
+ // The video input device id to show as selected by default.
+ // FIXME: On temasys, without a device selected and put into local
+ // storage as the default device to use, the current video device id
+ // is a blank string. This is because the library gets a local video
+ // track and then maps the track's device id by matching the track's
+ // label to the MediaDeviceInfos returned from enumerateDevices. In
+ // WebRTC, the track label is expected to return the camera device
+ // label. However, temasys video track labels refer to track id, not
+ // device label, so the library cannot match the track to a device.
+ // The workaround of defaulting to the first videoInput available
+ // is re-used from the previous device settings implementation.
+ selectedVideoInputId: this.props.currentVideoInputId
+ || (_availableDevices.videoInput
+ && _availableDevices.videoInput[0]
+ && _availableDevices.videoInput[0].deviceId)
+ || ''
};
// Preventing closing while cleaning up previews is important for
@@ -134,16 +155,29 @@ class DeviceSelectionDialog extends Component {
// closure until cleanup is complete ensures no errors in the process.
this._isClosing = false;
+ // Bind event handlers so they are only bound once for every instance.
this._closeModal = this._closeModal.bind(this);
- this._getAndSetAudioOutput = this._getAndSetAudioOutput.bind(this);
- this._getAndSetAudioTrack = this._getAndSetAudioTrack.bind(this);
- this._getAndSetVideoTrack = this._getAndSetVideoTrack.bind(this);
this._onCancel = this._onCancel.bind(this);
this._onSubmit = this._onSubmit.bind(this);
+ this._updateAudioOutput = this._updateAudioOutput.bind(this);
+ this._updateAudioInput = this._updateAudioInput.bind(this);
+ this._updateVideoInput = this._updateVideoInput.bind(this);
}
/**
- * Clean up any preview tracks that might not have been cleaned up already.
+ * Sets default device choices so a choice is pre-selected in the dropdowns
+ * and live previews are created.
+ *
+ * @inheritdoc
+ */
+ componentDidMount() {
+ this._updateAudioOutput(this.state.selectedAudioOutputId);
+ this._updateAudioInput(this.state.selectedAudioInputId);
+ this._updateVideoInput(this.state.selectedVideoInputId);
+ }
+
+ /**
+ * Disposes preview tracks that might not already be disposed.
*
* @inheritdoc
*/
@@ -173,8 +207,8 @@ class DeviceSelectionDialog extends Component {
+ error = { this.state.previewVideoTrackError }
+ track = { this.state.previewVideoTrack } />
{ this._renderAudioInputPreview() }
@@ -197,17 +231,10 @@ class DeviceSelectionDialog extends Component {
* promise can be for video cleanup and another for audio cleanup.
*/
_attemptPreviewTrackCleanup() {
- const cleanupPromises = [];
-
- if (!this._isPreviewingCurrentVideoTrack()) {
- cleanupPromises.push(this._disposeVideoPreview());
- }
-
- if (!this._isPreviewingCurrentAudioTrack()) {
- cleanupPromises.push(this._disposeAudioPreview());
- }
-
- return cleanupPromises;
+ return Promise.all([
+ this._disposeVideoPreview(),
+ this._disposeAudioPreview()
+ ]);
}
/**
@@ -243,147 +270,7 @@ class DeviceSelectionDialog extends Component {
}
/**
- * Callback invoked when a new audio output device has been selected.
- * Updates the internal state of the user's selection.
- *
- * @param {string} deviceId - The id of the chosen audio output device.
- * @private
- * @returns {void}
- */
- _getAndSetAudioOutput(deviceId) {
- this.setState({
- audioOutput: deviceId
- });
- }
-
- /**
- * Callback invoked when a new audio input device has been selected.
- * Updates the internal state of the user's selection as well as the audio
- * track that should display in the preview. Will reuse the current local
- * audio track if it has been selected.
- *
- * @param {string} deviceId - The id of the chosen audio input device.
- * @private
- * @returns {void}
- */
- _getAndSetAudioTrack(deviceId) {
- this.setState({
- audioInput: deviceId
- }, () => {
- const cleanupPromise = this._isPreviewingCurrentAudioTrack()
- ? Promise.resolve() : this._disposeAudioPreview();
-
- if (this._isCurrentAudioTrack(deviceId)) {
- cleanupPromise
- .then(() => {
- this.setState({
- previewAudioTrack: this.props.currentAudioTrack
- });
- });
- } else {
- cleanupPromise
- .then(() => createLocalTrack('audio', deviceId))
- .then(jitsiLocalTrack => {
- this.setState({
- previewAudioTrack: jitsiLocalTrack
- });
- });
- }
- });
- }
-
- /**
- * Callback invoked when a new video input device has been selected. Updates
- * the internal state of the user's selection as well as the video track
- * that should display in the preview. Will reuse the current local video
- * track if it has been selected.
- *
- * @param {string} deviceId - The id of the chosen video input device.
- * @private
- * @returns {void}
- */
- _getAndSetVideoTrack(deviceId) {
- this.setState({
- videoInput: deviceId
- }, () => {
- const cleanupPromise = this._isPreviewingCurrentVideoTrack()
- ? Promise.resolve() : this._disposeVideoPreview();
-
- if (this._isCurrentVideoTrack(deviceId)) {
- cleanupPromise
- .then(() => {
- this.setState({
- previewVideoTrack: this.props.currentVideoTrack
- });
- });
- } else {
- cleanupPromise
- .then(() => createLocalTrack('video', deviceId))
- .then(jitsiLocalTrack => {
- this.setState({
- previewVideoTrack: jitsiLocalTrack
- });
- });
- }
- });
- }
-
- /**
- * Utility function for determining if the current local audio track has the
- * passed in device id.
- *
- * @param {string} deviceId - The device id to match against.
- * @private
- * @returns {boolean} True if the device id is being used by the local audio
- * track.
- */
- _isCurrentAudioTrack(deviceId) {
- return this.props.currentAudioTrack
- && this.props.currentAudioTrack.getDeviceId() === deviceId;
- }
-
- /**
- * Utility function for determining if the current local video track has the
- * passed in device id.
- *
- * @param {string} deviceId - The device id to match against.
- * @private
- * @returns {boolean} True if the device id is being used by the local
- * video track.
- */
- _isCurrentVideoTrack(deviceId) {
- return this.props.currentVideoTrack
- && this.props.currentVideoTrack.getDeviceId() === deviceId;
- }
-
- /**
- * Utility function for detecting if the current audio preview track is not
- * the currently used audio track.
- *
- * @private
- * @returns {boolean} True if the current audio track is being used for
- * the preview.
- */
- _isPreviewingCurrentAudioTrack() {
- return !this.state.previewAudioTrack
- || this.state.previewAudioTrack === this.props.currentAudioTrack;
- }
-
- /**
- * Utility function for detecting if the current video preview track is not
- * the currently used video track.
- *
- * @private
- * @returns {boolean} True if the current video track is being used as the
- * preview.
- */
- _isPreviewingCurrentVideoTrack() {
- return !this.state.previewVideoTrack
- || this.state.previewVideoTrack === this.props.currentVideoTrack;
- }
-
- /**
- * Cleans existing preview tracks and signal to closeDeviceSelectionDialog.
+ * Disposes preview tracks and signals to close DeviceSelectionDialog.
*
* @private
* @returns {boolean} Returns false to prevent closure until cleanup is
@@ -406,7 +293,7 @@ class DeviceSelectionDialog extends Component {
}
/**
- * Identify changes to the preferred input/output devices and perform
+ * Identifies changes to the preferred input/output devices and perform
* necessary cleanup and requests to use those devices. Closes the modal
* after cleanup and device change requests complete.
*
@@ -421,32 +308,26 @@ class DeviceSelectionDialog extends Component {
this._isClosing = true;
- const deviceChangePromises = [];
+ const deviceChangePromises = this._attemptPreviewTrackCleanup()
+ .then(() => {
+ if (this.state.selectedVideoInputId
+ !== this.props.currentVideoInputId) {
+ this.props.dispatch(
+ setVideoInputDevice(this.state.selectedVideoInputId));
+ }
- if (this.state.videoInput && !this._isPreviewingCurrentVideoTrack()) {
- const changeVideoPromise = this._disposeVideoPreview()
- .then(() => {
- this.props.dispatch(setVideoInputDevice(
- this.state.videoInput));
- });
+ if (this.state.selectedAudioInputId
+ !== this.props.currentAudioInputId) {
+ this.props.dispatch(
+ setAudioInputDevice(this.state.selectedAudioInputId));
+ }
- deviceChangePromises.push(changeVideoPromise);
- }
-
- if (this.state.audioInput && !this._isPreviewingCurrentAudioTrack()) {
- const changeAudioPromise = this._disposeAudioPreview()
- .then(() => {
- this.props.dispatch(setAudioInputDevice(
- this.state.audioInput));
- });
-
- deviceChangePromises.push(changeAudioPromise);
- }
-
- if (this.state.audioOutput
- && this.state.audioOutput !== this.props.currentAudioOutputId) {
- this.props.dispatch(setAudioOutputDevice(this.state.audioOutput));
- }
+ if (this.state.selectedAudioOutputId
+ !== this.props.currentAudioOutputId) {
+ this.props.dispatch(
+ setAudioOutputDevice(this.state.selectedAudioOutputId));
+ }
+ });
Promise.all(deviceChangePromises)
.then(this._closeModal)
@@ -470,8 +351,7 @@ class DeviceSelectionDialog extends Component {
return (
+ track = { this.state.previewAudioTrack } />
);
}
@@ -489,8 +369,7 @@ class DeviceSelectionDialog extends Component {
return (
+ deviceId = { this.state.selectedAudioOutputId } />
);
}
@@ -515,70 +394,120 @@ class DeviceSelectionDialog extends Component {
* @returns {Array} DeviceSelector instances.
*/
_renderSelectors() {
- const availableDevices = this.props._devices;
- const currentAudioId = this.state.audioInput
- || (this.props.currentAudioTrack
- && this.props.currentAudioTrack.getDeviceId());
- const currentAudioOutId = this.state.audioOutput
- || this.props.currentAudioOutputId;
-
- // FIXME: On temasys, without a device selected and put into local
- // storage as the default device to use, the current video device id is
- // a blank string. This is because the library gets a local video track
- // and then maps the track's device id by matching the track's label to
- // the MediaDeviceInfos returned from enumerateDevices. In WebRTC, the
- // track label is expected to return the camera device label. However,
- // temasys video track labels refer to track id, not device label, so
- // the library cannot match the track to a device. The workaround of
- // defaulting to the first videoInput available has been re-used from
- // the previous device settings implementation.
- const currentVideoId = this.state.videoInput
- || (this.props.currentVideoTrack
- && this.props.currentVideoTrack.getDeviceId())
- || (availableDevices.videoInput[0]
- && availableDevices.videoInput[0].deviceId)
- || ''; // DeviceSelector expects a string for prop selectedDeviceId.
-
+ const { _availableDevices } = this.props;
const configurations = [
{
- devices: availableDevices.videoInput,
+ devices: _availableDevices.videoInput,
hasPermission: this.props.hasVideoPermission,
icon: 'icon-camera',
isDisabled: this.props.disableDeviceChange,
key: 'videoInput',
label: 'settings.selectCamera',
- onSelect: this._getAndSetVideoTrack,
- selectedDeviceId: currentVideoId
+ onSelect: this._updateVideoInput,
+ selectedDeviceId: this.state.selectedVideoInputId
},
{
- devices: availableDevices.audioInput,
+ devices: _availableDevices.audioInput,
hasPermission: this.props.hasAudioPermission,
icon: 'icon-microphone',
isDisabled: this.props.disableAudioInputChange
|| this.props.disableDeviceChange,
key: 'audioInput',
label: 'settings.selectMic',
- onSelect: this._getAndSetAudioTrack,
- selectedDeviceId: currentAudioId
+ onSelect: this._updateAudioInput,
+ selectedDeviceId: this.state.selectedAudioInputId
}
];
if (!this.props.hideAudioOutputSelect) {
configurations.push({
- devices: availableDevices.audioOutput,
+ devices: _availableDevices.audioOutput,
hasPermission: this.props.hasAudioPermission
|| this.props.hasVideoPermission,
icon: 'icon-volume',
isDisabled: this.props.disableDeviceChange,
key: 'audioOutput',
label: 'settings.selectAudioOutput',
- onSelect: this._getAndSetAudioOutput,
- selectedDeviceId: currentAudioOutId
+ onSelect: this._updateAudioOutput,
+ selectedDeviceId: this.state.selectedAudioOutputId
});
}
return configurations.map(this._renderSelector);
}
+
+ /**
+ * Callback invoked when a new audio input device has been selected. Updates
+ * the internal state of the user's selection as well as the audio track
+ * that should display in the preview.
+ *
+ * @param {string} deviceId - The id of the chosen audio input device.
+ * @private
+ * @returns {void}
+ */
+ _updateAudioInput(deviceId) {
+ this.setState({
+ selectedAudioInputId: deviceId
+ }, () => {
+ this._disposeAudioPreview()
+ .then(() => createLocalTrack('audio', deviceId))
+ .then(jitsiLocalTrack => {
+ this.setState({
+ previewAudioTrack: jitsiLocalTrack
+ });
+ })
+ .catch(() => {
+ this.setState({
+ previewAudioTrack: null
+ });
+ });
+ });
+ }
+
+ /**
+ * Callback invoked when a new audio output device has been selected.
+ * Updates the internal state of the user's selection.
+ *
+ * @param {string} deviceId - The id of the chosen audio output device.
+ * @private
+ * @returns {void}
+ */
+ _updateAudioOutput(deviceId) {
+ this.setState({
+ selectedAudioOutputId: deviceId
+ });
+ }
+
+ /**
+ * Callback invoked when a new video input device has been selected. Updates
+ * the internal state of the user's selection as well as the video track
+ * that should display in the preview.
+ *
+ * @param {string} deviceId - The id of the chosen video input device.
+ * @private
+ * @returns {void}
+ */
+ _updateVideoInput(deviceId) {
+ this.setState({
+ selectedVideoInputId: deviceId
+ }, () => {
+ this._disposeVideoPreview()
+ .then(() => createLocalTrack('video', deviceId))
+ .then(jitsiLocalTrack => {
+ this.setState({
+ previewVideoTrack: jitsiLocalTrack,
+ previewVideoTrackError: null
+ });
+ })
+ .catch(() => {
+ this.setState({
+ previewVideoTrack: null,
+ previewVideoTrackError:
+ this.props.t('deviceSelection.previewUnavailable')
+ });
+ });
+ });
+ }
}
/**
@@ -588,12 +517,12 @@ class DeviceSelectionDialog extends Component {
* @param {Object} state - The Redux state.
* @private
* @returns {{
- * _devices: Object
+ * _availableDevices: Object
* }}
*/
function _mapStateToProps(state) {
return {
- _devices: state['features/base/devices']
+ _availableDevices: state['features/base/devices']
};
}
diff --git a/react/features/device-selection/components/VideoInputPreview.js b/react/features/device-selection/components/VideoInputPreview.js
index 2b7eb5c14..23d74640d 100644
--- a/react/features/device-selection/components/VideoInputPreview.js
+++ b/react/features/device-selection/components/VideoInputPreview.js
@@ -2,7 +2,7 @@ import React, { Component } from 'react';
import { translate } from '../../base/i18n';
-const VIDEO_MUTE_CLASS = 'video-muted';
+const VIDEO_ERROR_CLASS = 'video-preview-has-error';
/**
* React component for displaying video. This component defers to lib-jitsi-meet
@@ -17,12 +17,18 @@ class VideoInputPreview extends Component {
* @static
*/
static propTypes = {
+ /**
+ * An error message to display instead of a preview. Displaying an error
+ * will take priority over displaying a video preview.
+ */
+ error: React.PropTypes.string,
+
/**
* Invoked to obtain translated strings.
*/
t: React.PropTypes.func,
- /*
+ /**
* The JitsiLocalTrack to display.
*/
track: React.PropTypes.object
@@ -37,9 +43,37 @@ class VideoInputPreview extends Component {
constructor(props) {
super(props);
+ /**
+ * The internal reference to the DOM/HTML element intended for showing
+ * error messages.
+ *
+ * @private
+ * @type {HTMLDivElement}
+ */
+ this._errorElement = null;
+
+ /**
+ * The internal reference to topmost DOM/HTML element backing the React
+ * {@code Component}. Accessed directly for toggling a classname to
+ * indicate an error is present so styling can be changed to display it.
+ *
+ * @private
+ * @type {HTMLDivElement}
+ */
this._rootElement = null;
+
+ /**
+ * The internal reference to the DOM/HTML element intended for
+ * displaying a video. This element may be an HTML video element or a
+ * temasys video object.
+ *
+ * @private
+ * @type {HTMLVideoElement|Object}
+ */
this._videoElement = null;
+ // Bind event handlers so they are only bound once for every instance.
+ this._setErrorElement = this._setErrorElement.bind(this);
this._setRootElement = this._setRootElement.bind(this);
this._setVideoElement = this._setVideoElement.bind(this);
}
@@ -51,7 +85,11 @@ class VideoInputPreview extends Component {
* @returns {void}
*/
componentDidMount() {
- this._attachTrack(this.props.track);
+ if (this.props.error) {
+ this._updateErrorView(this.props.error);
+ } else {
+ this._attachTrack(this.props.track);
+ }
}
/**
@@ -80,9 +118,9 @@ class VideoInputPreview extends Component {
autoPlay = { true }
className = 'video-input-preview-display flipVideoX'
ref = { this._setVideoElement } />
-
- { this.props.t('deviceSelection.currentlyVideoMuted') }
-
+
);
}
@@ -99,8 +137,15 @@ class VideoInputPreview extends Component {
* @returns {void}
*/
shouldComponentUpdate(nextProps) {
- if (nextProps.track !== this.props.track) {
+ const hasNewTrack = nextProps.track !== this.props.track;
+
+ if (hasNewTrack || nextProps.error) {
this._detachTrack(this.props.track);
+ this._updateErrorView(nextProps.error);
+ }
+
+ // Never attempt to show the new track if there is an error present.
+ if (hasNewTrack && !nextProps.error) {
this._attachTrack(nextProps.track);
}
@@ -123,17 +168,9 @@ class VideoInputPreview extends Component {
return;
}
- // Do not attempt to display a preview if the track is muted, as the
- // library will simply return a falsy value for the element anyway.
- if (track.isMuted()) {
- this._showMuteOverlay(true);
- } else {
- this._showMuteOverlay(false);
+ const updatedVideoElement = track.attach(this._videoElement);
- const updatedVideoElement = track.attach(this._videoElement);
-
- this._setVideoElement(updatedVideoElement);
- }
+ this._setVideoElement(updatedVideoElement);
}
/**
@@ -159,6 +196,19 @@ class VideoInputPreview extends Component {
}
}
+ /**
+ * Sets an instance variable for the component's element intended for
+ * displaying error messages. The element will be accessed directly to
+ * display an error message.
+ *
+ * @param {Object} element - DOM element intended for displaying errors.
+ * @private
+ * @returns {void}
+ */
+ _setErrorElement(element) {
+ this._errorElement = element;
+ }
+
/**
* Sets the component's root element.
*
@@ -183,20 +233,22 @@ class VideoInputPreview extends Component {
}
/**
- * Adds or removes a class to the component's parent node to indicate mute
- * status.
+ * Adds or removes a class to the component's parent node to indicate an
+ * error has occurred. Also sets the error text.
*
- * @param {boolean} shouldShow - True if the mute class should be added and
- * false if the class should be removed.
+ * @param {string} error - The error message to display. If falsy, error
+ * message display will be hidden.
* @private
* @returns {void}
*/
- _showMuteOverlay(shouldShow) {
- if (shouldShow) {
- this._rootElement.classList.add(VIDEO_MUTE_CLASS);
+ _updateErrorView(error) {
+ if (error) {
+ this._rootElement.classList.add(VIDEO_ERROR_CLASS);
} else {
- this._rootElement.classList.remove(VIDEO_MUTE_CLASS);
+ this._rootElement.classList.remove(VIDEO_ERROR_CLASS);
}
+
+ this._errorElement.innerText = error || '';
}
}