diff --git a/src/id-verification/Camera.jsx b/src/id-verification/Camera.jsx index 8876f16..e42c386 100644 --- a/src/id-verification/Camera.jsx +++ b/src/id-verification/Camera.jsx @@ -20,14 +20,16 @@ class Camera extends React.Component { videoHasLoaded: false, shouldDetect: false, isFinishedLoadingDetection: true, + shouldGiveFeedback: true, + feedback: '', }; } componentDidMount() { this.cameraPhoto = new CameraPhoto(this.videoRef.current); this.cameraPhoto.startCamera( - this.props.isPortrait ? FACING_MODES.USER : FACING_MODES.ENVIRONMENT, - { width: 640, height: 480 } + this.props.isPortrait ? FACING_MODES.USER : FACING_MODES.ENVIRONMENT, + { width: 640, height: 480 }, ); } @@ -97,11 +99,14 @@ class Camera extends React.Component { const x = features[j][0]; const y = features[j][1]; + let isInRange; if (this.props.isPortrait) { - isInPosition = isInPosition && this.isInRangeForPortrait(x, y); + isInRange = this.isInRangeForPortrait(x, y); } else { - isInPosition = isInPosition && this.isInRangeForID(x, y); + isInRange = this.isInRangeForID(x, y); } + // if it is not in range, give feedback depending on which feature is out of range + isInPosition = isInPosition && isInRange; } // draw a box depending on if all landmarks are in position @@ -109,11 +114,81 @@ class Camera extends React.Component { canvasContext.strokeStyle = '#00ffff'; canvasContext.lineWidth = 6; canvasContext.strokeRect(start[0], start[1], size[0], size[1]); + // give positive feedback here if user is in correct position + this.giveFeedback(predictions.length, [], true); } else { canvasContext.fillStyle = 'rgba(255, 51, 0, 0.75)'; canvasContext.fillRect(start[0], start[1], size[0], size[1]); + this.giveFeedback(predictions.length, features[0], false); } }); + + if (predictions.length === 0) { + this.giveFeedback(predictions.length, [], false); + } + } + + giveFeedback(numFaces, rightEye, isCorrect) { + if (this.state.shouldGiveFeedback) { + const currentFeedback = this.state.feedback; + let newFeedback = ''; + if (numFaces === 1) { + // only give feedback if one face is detected otherwise + // it would be difficult to tell a user which face to move + if (isCorrect) { + newFeedback = this.props.intl.formatMessage(messages['id.verification.photo.feedback.correct']); + } else { + // give feedback based on where user is + newFeedback = this.props.intl.formatMessage(messages[this.getGridPosition(rightEye)]); + } + } else if (numFaces > 1) { + newFeedback = this.props.intl.formatMessage(messages['id.verification.photo.feedback.two.faces']); + } else { + newFeedback = this.props.intl.formatMessage(messages['id.verification.photo.feedback.no.faces']); + } + if (currentFeedback !== newFeedback) { + // only update status if it is different, so we don't overload the user with status updates + this.setState({ feedback: newFeedback }); + } + // turn off feedback for one to ensure that instructions aren't disruptive/interrupting + this.setState({ shouldGiveFeedback: false }); + setTimeout(() => { + this.setState({ shouldGiveFeedback: true }); + }, 1000); + } + } + + getGridPosition(coordinates) { + // Used to determine where a face is (i.e. top-left, center-right, bottom-center, etc.) + + const x = coordinates[0]; + const y = coordinates[1]; + + let messageBase = 'id.verification.photo.feedback'; + + const heightUpperLimit = 320; + const heightMiddleLimit = 160; + + if (y < heightMiddleLimit) { + messageBase += '.top'; + } else if (y < heightUpperLimit && y >= heightMiddleLimit) { + messageBase += '.center'; + } else { + messageBase += '.bottom'; + } + + const widthRightLimit = 213; + const widthMiddleLimit = 427; + + if (x < widthRightLimit) { + messageBase += '.right'; + } else if (x >= widthRightLimit && x < widthMiddleLimit) { + messageBase += '.center'; + } else { + messageBase += '.left'; + } + + return messageBase; } isInRangeForPortrait(x, y) { @@ -207,16 +282,32 @@ class Camera extends React.Component { autoPlay className="camera-video" onLoadedData={() => { this.setVideoHasLoaded(); }} - style={{ display: this.state.dataUri ? 'none' : 'block' }} + style={{ + display: this.state.dataUri ? 'none' : 'block', + WebkitTransform: 'scaleX(-1)', + transform: 'scaleX(-1)', + }} playsInline /> - + imgCamera +
{this.state.feedback}