fix chrome on ios blue screen after selecting share webcam / lint issues

This commit is contained in:
KDSBrowne 2019-06-27 16:02:42 +00:00
parent d8f891408d
commit dbf8938d24
6 changed files with 222 additions and 144 deletions

View File

@ -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>

View File

@ -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)));

View File

@ -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>
);
}

View File

@ -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));

View File

@ -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;

View File

@ -12,8 +12,8 @@ const deviceInfo = {
isPhone: smallSide <= MAX_PHONE_SHORT_SIDE,
};
},
hasMediaDevices: !!navigator.mediaDevices,
};
export default deviceInfo;