fix chrome on ios blue screen after selecting share webcam / lint issues
This commit is contained in:
parent
d8f891408d
commit
dbf8938d24
@ -37,6 +37,13 @@ const propTypes = {
|
||||
joinFullAudioImmediately: PropTypes.bool.isRequired,
|
||||
joinFullAudioEchoTest: PropTypes.bool.isRequired,
|
||||
forceListenOnlyAttendee: PropTypes.bool.isRequired,
|
||||
audioLocked: PropTypes.bool.isRequired,
|
||||
resolve: PropTypes.func.isRequired,
|
||||
isMobileNative: PropTypes.bool.isRequired,
|
||||
isIOSChrome: PropTypes.bool.isRequired,
|
||||
isIEOrEdge: PropTypes.bool.isRequired,
|
||||
hasMediaDevices: PropTypes.bool.isRequired,
|
||||
formattedTelVoice: PropTypes.string.isRequired,
|
||||
};
|
||||
|
||||
const defaultProps = {
|
||||
@ -341,12 +348,12 @@ class AudioModal extends Component {
|
||||
const {
|
||||
isEchoTest,
|
||||
intl,
|
||||
isIOSChrome,
|
||||
hasMediaDevices,
|
||||
} = this.props;
|
||||
|
||||
const { content } = this.state;
|
||||
|
||||
if (isIOSChrome) {
|
||||
if (!hasMediaDevices) {
|
||||
return (
|
||||
<div>
|
||||
<div className={styles.warning}>!</div>
|
||||
|
@ -6,6 +6,7 @@ import getFromUserSettings from '/imports/ui/services/users-settings';
|
||||
import AudioModal from './component';
|
||||
import Meetings from '/imports/api/meetings';
|
||||
import Auth from '/imports/ui/services/auth';
|
||||
import deviceInfo from '/imports/utils/deviceInfo';
|
||||
import lockContextContainer from '/imports/ui/components/lock-viewers/context/container';
|
||||
import Service from '../service';
|
||||
|
||||
@ -83,5 +84,6 @@ export default lockContextContainer(withModalMounter(withTracker(({ mountModal,
|
||||
isIOSChrome: browser().name === 'crios',
|
||||
isMobileNative: navigator.userAgent.toLowerCase().includes('bbbnative'),
|
||||
isIEOrEdge: browser().name === 'edge' || browser().name === 'ie',
|
||||
hasMediaDevices: deviceInfo.hasMediaDevices,
|
||||
});
|
||||
})(AudioModalContainer)));
|
||||
|
@ -18,6 +18,10 @@ const propTypes = {
|
||||
startSharing: PropTypes.func.isRequired,
|
||||
changeWebcam: PropTypes.func.isRequired,
|
||||
changeProfile: PropTypes.func.isRequired,
|
||||
joinVideo: PropTypes.func.isRequired,
|
||||
resolve: PropTypes.func.isRequired,
|
||||
hasMediaDevices: PropTypes.bool.isRequired,
|
||||
webcamDeviceId: PropTypes.string.isRequired,
|
||||
};
|
||||
|
||||
const intlMessages = defineMessages({
|
||||
@ -81,8 +85,56 @@ const intlMessages = defineMessages({
|
||||
id: 'app.video.notReadableError',
|
||||
description: 'error message When the webcam is being used by other software',
|
||||
},
|
||||
iOSError: {
|
||||
id: 'app.audioModal.iOSBrowser',
|
||||
description: 'Audio/Video Not supported warning',
|
||||
},
|
||||
iOSErrorDescription: {
|
||||
id: 'app.audioModal.iOSErrorDescription',
|
||||
description: 'Audio/Video not supported description',
|
||||
},
|
||||
iOSErrorRecommendation: {
|
||||
id: 'app.audioModal.iOSErrorRecommendation',
|
||||
description: 'Audio/Video recommended action',
|
||||
},
|
||||
});
|
||||
|
||||
const handleGUMError = (error) => {
|
||||
// logger.error(error);
|
||||
// logger.error(error.id);
|
||||
// logger.error(error.name);
|
||||
// console.log(error);
|
||||
|
||||
let convertedError;
|
||||
|
||||
switch (error.name) {
|
||||
case 'SourceUnavailableError':
|
||||
case 'NotReadableError':
|
||||
// hardware failure with the device
|
||||
break;
|
||||
case 'NotAllowedError':
|
||||
// media was disallowed
|
||||
convertedError = intlMessages.NotAllowedError;
|
||||
break;
|
||||
case 'AbortError':
|
||||
// generic error occured
|
||||
break;
|
||||
case 'NotFoundError':
|
||||
// no webcam found
|
||||
convertedError = intlMessages.NotFoundError;
|
||||
break;
|
||||
case 'SecurityError':
|
||||
// user media support is disabled on the document
|
||||
break;
|
||||
case 'TypeError':
|
||||
// issue with constraints or maybe Chrome with HTTP
|
||||
break;
|
||||
default:
|
||||
// default error message handling
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
class VideoPreview extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
@ -98,6 +150,8 @@ class VideoPreview extends Component {
|
||||
this.scanProfiles = this.scanProfiles.bind(this);
|
||||
this.doGUM = this.doGUM.bind(this);
|
||||
this.displayPreview = this.displayPreview.bind(this);
|
||||
this.supportWarning = this.supportWarning.bind(this);
|
||||
this.renderModalContent = this.renderModalContent.bind(this);
|
||||
|
||||
this.deviceStream = null;
|
||||
|
||||
@ -112,6 +166,70 @@ class VideoPreview extends Component {
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const { webcamDeviceId, hasMediaDevices } = this.props;
|
||||
|
||||
this._isMounted = true;
|
||||
|
||||
// Have to request any device to get past checks before finding devices. If this is
|
||||
// skipped then we get devices with no labels
|
||||
if (hasMediaDevices) {
|
||||
try {
|
||||
navigator.mediaDevices.getUserMedia({ audio: false, video: true }).then(() => {
|
||||
if (!this._isMounted) return;
|
||||
|
||||
navigator.mediaDevices.enumerateDevices().then(async (devices) => {
|
||||
const webcams = [];
|
||||
let initialDeviceId;
|
||||
|
||||
if (!this._isMounted) return;
|
||||
|
||||
// set webcam
|
||||
devices.forEach((device) => {
|
||||
if (device.kind === 'videoinput') {
|
||||
webcams.push(device);
|
||||
if (!initialDeviceId || (webcamDeviceId && webcamDeviceId === device.deviceId)) {
|
||||
initialDeviceId = device.deviceId;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
logger.debug(`Enumerate devices came back. There are ${devices.length} devices and ${webcams.length} are video inputs`);
|
||||
|
||||
if (initialDeviceId) {
|
||||
this.setState({
|
||||
availableWebcams: webcams,
|
||||
});
|
||||
|
||||
this.scanProfiles(initialDeviceId);
|
||||
}
|
||||
}).catch((error) => {
|
||||
// CHANGE THIS TO SOMETHING USEFUL
|
||||
logger.warning(`Error enumerating devices. name: [${error.name}] message: [${error.message}]`);
|
||||
handleGUMError(error);
|
||||
});
|
||||
});
|
||||
} catch (error) {
|
||||
// CHANGE THIS TO SOMETHING USEFUL
|
||||
logger.warning(`Error grabbing initial video stream. name: [${error.name}] message: [${error.message}]`);
|
||||
handleGUMError(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
// console.log("unmounting video preview");
|
||||
this.stopTracks();
|
||||
this.deviceStream = null;
|
||||
if (this.video) {
|
||||
// console.log("clear video srcObject");
|
||||
this.video.srcObject = null;
|
||||
}
|
||||
|
||||
this._isMounted = false;
|
||||
}
|
||||
|
||||
|
||||
stopTracks() {
|
||||
// console.log("in stop tracks");
|
||||
if (this.deviceStream) {
|
||||
@ -123,42 +241,6 @@ class VideoPreview extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
handleGUMError(error) {
|
||||
// logger.error(error);
|
||||
// logger.error(error.id);
|
||||
// logger.error(error.name);
|
||||
// console.log(error);
|
||||
|
||||
let convertedError;
|
||||
|
||||
switch (error.name) {
|
||||
case 'SourceUnavailableError':
|
||||
case 'NotReadableError':
|
||||
// hardware failure with the device
|
||||
break;
|
||||
case 'NotAllowedError':
|
||||
// media was disallowed
|
||||
convertedError = intlMessages.NotAllowedError;
|
||||
break;
|
||||
case 'AbortError':
|
||||
// generic error occured
|
||||
break;
|
||||
case 'NotFoundError':
|
||||
// no webcam found
|
||||
convertedError = intlMessages.NotFoundError;
|
||||
break;
|
||||
case 'SecurityError':
|
||||
// user media support is disabled on the document
|
||||
break;
|
||||
case 'TypeError':
|
||||
// issue with constraints or maybe Chrome with HTTP
|
||||
break;
|
||||
default:
|
||||
// default error message handling
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
handleSelectWebcam(event) {
|
||||
const webcamValue = event.target.value;
|
||||
|
||||
@ -208,23 +290,20 @@ class VideoPreview extends Component {
|
||||
|
||||
// logger.debug('starting scan');
|
||||
|
||||
const checkWebcamExists = () => {
|
||||
// logger.debug('initial webcam check');
|
||||
// we call gUM with no constraints so we know if any stream is available
|
||||
this.doGUM(deviceId, {}).then((stream) => {
|
||||
if (!this._isMounted) return;
|
||||
const scanningCleanup = () => {
|
||||
this.video.onloadedmetadata = undefined;
|
||||
|
||||
// We don't need to do anything with the returned stream
|
||||
nextProfile();
|
||||
}).catch((error) => {
|
||||
if (!this._isMounted) return;
|
||||
if (availableProfiles.length > 0) {
|
||||
const defaultProfile = availableProfiles.find(profile => profile.default)
|
||||
|| availableProfiles[0];
|
||||
logger.debug(`Found default profile: ${JSON.stringify(defaultProfile)}`);
|
||||
|
||||
// webcam might no longer exist or be available
|
||||
logger.debug(`Error with profile: ${CAMERA_PROFILES[currNum].name}`);
|
||||
this.displayPreview(deviceId, defaultProfile);
|
||||
}
|
||||
|
||||
this.handleGUMError(error);
|
||||
|
||||
scanningCleanup();
|
||||
this.setState({
|
||||
scanning: false,
|
||||
availableProfiles,
|
||||
});
|
||||
};
|
||||
|
||||
@ -241,7 +320,7 @@ class VideoPreview extends Component {
|
||||
if (!this._isMounted) return;
|
||||
|
||||
logger.debug(`Error with fetching profile {${CAMERA_PROFILES[currNum].name}} skipping to next profile. Error is {${error.name}}`);
|
||||
currNum++;
|
||||
currNum += 1;
|
||||
nextProfile();
|
||||
});
|
||||
} else {
|
||||
@ -250,6 +329,26 @@ class VideoPreview extends Component {
|
||||
}
|
||||
};
|
||||
|
||||
const checkWebcamExists = () => {
|
||||
// logger.debug('initial webcam check');
|
||||
// we call gUM with no constraints so we know if any stream is available
|
||||
this.doGUM(deviceId, {}).then(() => {
|
||||
if (!this._isMounted) return;
|
||||
|
||||
// We don't need to do anything with the returned stream
|
||||
nextProfile();
|
||||
}).catch((error) => {
|
||||
if (!this._isMounted) return;
|
||||
|
||||
// webcam might no longer exist or be available
|
||||
logger.debug(`Error with profile: ${CAMERA_PROFILES[currNum].name}`);
|
||||
|
||||
handleGUMError(error);
|
||||
|
||||
scanningCleanup();
|
||||
});
|
||||
};
|
||||
|
||||
const getVideoDimensions = () => {
|
||||
// logger.debug('loaded metadata');
|
||||
if (!this.video.videoWidth) {
|
||||
@ -266,27 +365,10 @@ class VideoPreview extends Component {
|
||||
logger.debug(`Not including profile ${CAMERA_PROFILES[currNum].name}`);
|
||||
}
|
||||
|
||||
currNum++;
|
||||
currNum += 1;
|
||||
nextProfile();
|
||||
};
|
||||
|
||||
const scanningCleanup = () => {
|
||||
this.video.onloadedmetadata = undefined;
|
||||
|
||||
if (availableProfiles.length > 0) {
|
||||
const defaultProfile = availableProfiles.find(profile => profile.default)
|
||||
|| availableProfiles[0];
|
||||
logger.debug(`Found default profile: ${JSON.stringify(defaultProfile)}`);
|
||||
|
||||
this.displayPreview(deviceId, defaultProfile);
|
||||
}
|
||||
|
||||
this.setState({
|
||||
scanning: false,
|
||||
availableProfiles,
|
||||
});
|
||||
};
|
||||
|
||||
this.video.onloadedmetadata = getVideoDimensions;
|
||||
|
||||
checkWebcamExists();
|
||||
@ -326,67 +408,6 @@ class VideoPreview extends Component {
|
||||
});
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const { webcamDeviceId } = this.props;
|
||||
|
||||
this._isMounted = true;
|
||||
|
||||
// Have to request any device to get past checks before finding devices. If this is
|
||||
// skipped then we get devices with no labels
|
||||
try {
|
||||
navigator.mediaDevices.getUserMedia({ audio: false, video: true }).then((stream) => {
|
||||
if (!this._isMounted) return;
|
||||
|
||||
navigator.mediaDevices.enumerateDevices().then(async (devices) => {
|
||||
const webcams = [];
|
||||
let initialDeviceId;
|
||||
|
||||
if (!this._isMounted) return;
|
||||
|
||||
// set webcam
|
||||
devices.forEach((device) => {
|
||||
if (device.kind === 'videoinput') {
|
||||
webcams.push(device);
|
||||
if (!initialDeviceId || (webcamDeviceId && webcamDeviceId === device.deviceId)) {
|
||||
initialDeviceId = device.deviceId;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
logger.debug(`Enumerate devices came back. There are ${devices.length} devices and ${webcams.length} are video inputs`);
|
||||
|
||||
if (initialDeviceId) {
|
||||
this.setState({
|
||||
availableWebcams: webcams,
|
||||
});
|
||||
|
||||
this.scanProfiles(initialDeviceId);
|
||||
}
|
||||
}).catch((error) => {
|
||||
// CHANGE THIS TO SOMETHING USEFUL
|
||||
logger.warning(`Error enumerating devices. name: [${error.name}] message: [${error.message}]`);
|
||||
this.handleGUMError(error);
|
||||
});
|
||||
});
|
||||
} catch (error) {
|
||||
// CHANGE THIS TO SOMETHING USEFUL
|
||||
logger.warning(`Error grabbing initial video stream. name: [${error.name}] message: [${error.message}]`);
|
||||
this.handleGUMError(error);
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
// console.log("unmounting video preview");
|
||||
this.stopTracks();
|
||||
this.deviceStream = null;
|
||||
if (this.video) {
|
||||
// console.log("clear video srcObject");
|
||||
this.video.srcObject = null;
|
||||
}
|
||||
|
||||
this._isMounted = false;
|
||||
}
|
||||
|
||||
handleJoinVideo() {
|
||||
const {
|
||||
joinVideo,
|
||||
@ -395,7 +416,22 @@ class VideoPreview extends Component {
|
||||
joinVideo();
|
||||
}
|
||||
|
||||
render() {
|
||||
supportWarning() {
|
||||
const { intl } = this.props;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className={styles.warning}>!</div>
|
||||
<h4 className={styles.main}>{intl.formatMessage(intlMessages.iOSError)}</h4>
|
||||
<div className={styles.text}>{intl.formatMessage(intlMessages.iOSErrorDescription)}</div>
|
||||
<div className={styles.text}>
|
||||
{intl.formatMessage(intlMessages.iOSErrorRecommendation)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderModalContent() {
|
||||
const {
|
||||
intl,
|
||||
} = this.props;
|
||||
@ -409,13 +445,7 @@ class VideoPreview extends Component {
|
||||
} = this.state;
|
||||
|
||||
return (
|
||||
<Modal
|
||||
overlayClassName={styles.overlay}
|
||||
className={styles.modal}
|
||||
onRequestClose={this.handleProceed}
|
||||
hideBorder
|
||||
contentLabel={intl.formatMessage(intlMessages.webcamSettingsTitle)}
|
||||
>
|
||||
<div>
|
||||
{browser().name === 'edge' || browser().name === 'ie' ? (
|
||||
<p className={styles.browserWarning}>
|
||||
<FormattedMessage
|
||||
@ -503,6 +533,28 @@ class VideoPreview extends Component {
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
intl,
|
||||
hasMediaDevices,
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
<Modal
|
||||
overlayClassName={styles.overlay}
|
||||
className={styles.modal}
|
||||
onRequestClose={this.handleProceed}
|
||||
hideBorder
|
||||
contentLabel={intl.formatMessage(intlMessages.webcamSettingsTitle)}
|
||||
>
|
||||
{ hasMediaDevices
|
||||
? this.renderModalContent()
|
||||
: this.supportWarning()
|
||||
}
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
import React from 'react';
|
||||
import { withModalMounter } from '/imports/ui/components/modal/service';
|
||||
import { withTracker } from 'meteor/react-meteor-data';
|
||||
import deviceInfo from '/imports/utils/deviceInfo';
|
||||
import Service from './service';
|
||||
import VideoPreview from './component';
|
||||
import VideoService from '../video-provider/service';
|
||||
@ -8,14 +9,13 @@ import VideoService from '../video-provider/service';
|
||||
const VideoPreviewContainer = props => <VideoPreview {...props} />;
|
||||
|
||||
export default withModalMounter(withTracker(({ mountModal }) => ({
|
||||
closeModal: () => {
|
||||
mountModal(null);
|
||||
},
|
||||
startSharing: () => {
|
||||
mountModal(null);
|
||||
VideoService.joinVideo();
|
||||
},
|
||||
closeModal: () => mountModal(null),
|
||||
changeWebcam: deviceId => Service.changeWebcam(deviceId),
|
||||
webcamDeviceId: Service.webcamDeviceId(),
|
||||
changeProfile: profileId => Service.changeProfile(profileId),
|
||||
hasMediaDevices: deviceInfo.hasMediaDevices,
|
||||
}))(VideoPreviewContainer));
|
||||
|
@ -2,6 +2,23 @@
|
||||
@import '/imports/ui/stylesheets/mixins/focus';
|
||||
@import "/imports/ui/components/modal/simple/styles";
|
||||
|
||||
.warning {
|
||||
text-align: center;
|
||||
font-weight: var(--headings-font-weight);
|
||||
font-size: 5rem;
|
||||
white-space: normal;
|
||||
}
|
||||
.text {
|
||||
margin: var(--line-height-computed);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.main {
|
||||
margin: var(--line-height-computed);
|
||||
text-align: center;
|
||||
font-size: var(--font-size-large);
|
||||
}
|
||||
|
||||
.actions {
|
||||
margin-left: auto;
|
||||
margin-right: 3px;
|
||||
|
@ -12,8 +12,8 @@ const deviceInfo = {
|
||||
isPhone: smallSide <= MAX_PHONE_SHORT_SIDE,
|
||||
};
|
||||
},
|
||||
hasMediaDevices: !!navigator.mediaDevices,
|
||||
};
|
||||
|
||||
|
||||
export default deviceInfo;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user