eagle-id
Version:
A React based component to perform identity verification
409 lines (376 loc) • 13.7 kB
JSX
import React, { Component } from 'react';
import RadioButtonUncheckedIcon from '@material-ui/icons/RadioButtonUnchecked';
import LoopIcon from '@material-ui/icons/Loop';
import Fab from '@material-ui/core/Fab';
import CircularProgress from '@material-ui/core/CircularProgress';
import Dialog from '@material-ui/core/Dialog';
import DialogContent from '@material-ui/core/DialogContent';
import Webcam from 'react-webcam';
import Lottie from 'react-lottie';
import { isMobile } from 'react-device-detect';
import axios from 'axios'
const { detect } = require('detect-browser');
const browser = detect();
import * as constants from './constants'
import { defaultConfig, defaultStyles } from './config';
var webcamRef = React.createRef()
class WebcamCapture extends Component {
constructor(props) {
super(props)
var animationData = {}
var label = ""
var info = ""
var number = ""
var overlay = ""
var mirrored = false
var cameraPosition = constants.camera.FRONT
if (this.props.documentType === constants.SELFIE) {
cameraPosition = constants.camera.FRONT
}
if (browser.name === "chrome" && browser.os === "Android OS") {
cameraPosition = constants.camera.FRONT
}
this.state = {
config: this.props.config,
cameraPosition: cameraPosition,
deviceIdFront: "",
deviceIdBack: "",
loading: this.props.loading,
disableButtons: false,
showAnimation: false
};
this.getDeviceId()
}
componentWillMount() {
axios.get(this.state.config.resources.animations.passport)
.then(response => {
this.setState({
animationPassport: response.data
}, this.forceUpdate())
})
.catch((error) => {
console.log(error)
})
axios.get(this.state.config.resources.animations.idfront)
.then(response => {
this.setState({
animationIdFront: response.data
}, this.forceUpdate())
})
.catch((error) => {
console.log(error)
})
axios.get(this.state.config.resources.animations.idback)
.then(response => {
this.setState({
animationIdBack: response.data
}, this.forceUpdate())
})
.catch((error) => {
console.log(error)
})
var showAnimation = false
switch(this.props.documentType) {
case constants.ID_FRONT:
showAnimation = true
break;
case constants.ID_BACK:
showAnimation = true
break;
case constants.VISA:
showAnimation = true
break;
case constants.PASSPORT:
showAnimation = true
break;
case constants.OTHER:
showAnimation = false
break;
case constants.SELFIE:
showAnimation = false
break;
default:
}
this.setState({
showAnimation: showAnimation
}, this.forceUpdate())
}
componentDidUpdate(prevProps, prevState) {
if (prevState.disableButtons) {
this.setState({ disableButtons: false })
}
if (prevProps.config != this.props.config) {
this.setState({
config: this.props.config
})
}
}
capture = () => {
if (this.state.disableButtons) {
return
}
this.setState({
disableButtons: true
})
const image = webcamRef.current.getScreenshot()
this.props.processImage(image)
}
getDeviceId = async () => {
if (browser.name === "chrome" && browser.os === "Android OS") {
navigator.mediaDevices.enumerateDevices().then((devices) => {
devices.forEach((device) => {
if(device.kind === "videoinput" && device.label.includes("facing front")) {
this.setState({
deviceIdFront: device.deviceId
})
}
if(device.kind === "videoinput" && device.label.includes("facing back")) {
this.setState({
deviceIdBack: device.deviceId
})
}
})
})
}
}
getFacingMode = (cameraPosition) => {
if (cameraPosition === constants.camera.FRONT) {
return "user"
} else {
return "environment"
}
}
changeCameraPosition = () => {
if (browser.name === "chrome" && browser.os === "Android OS") {
if (webcamRef.current && webcamRef.current.stream && webcamRef.current.stream.getVideoTracks) {
webcamRef.current.stream.getVideoTracks().map(track => track.stop());
}
webcamRef = React.createRef()
}
if (cameraPosition === constants.camera.FRONT) {
this.setState({ cameraPosition: constants.camera.BACK })
} else if (cameraPosition === constants.camera.BACK) {
this.setState({ cameraPosition: constants.camera.FRONT })
}
}
render() {
/* configure camera label descriptions */
switch(this.props.documentType) {
case constants.ID_FRONT:
this.label = this.props.config.camera.ui.idFront.scanLabel
this.info = this.props.config.camera.ui.idFront.infoLabel
this.number = this.props.config.camera.ui.idFront.scanNumber
this.overlay = constants.OVERLAY_DOCUMENT
this.animationData = this.state.animationIdFront
this.mirrored = false
break;
case constants.ID_BACK:
this.label = this.props.config.camera.ui.idBack.scanLabel
this.info = this.props.config.camera.ui.idBack.infoLabel
this.number = this.props.config.camera.ui.idBack.scanNumber
this.overlay = constants.OVERLAY_DOCUMENT
this.animationData = this.state.animationIdBack
this.mirrored = false
break;
case constants.VISA:
this.label = this.props.config.camera.ui.visa.scanLabel
this.info = this.props.config.camera.ui.visa.infoLabel
this.number = this.props.config.camera.ui.visa.scanNumber
this.overlay = constants.OVERLAY_DOCUMENT
this.animationData = this.state.animationPassport
this.mirrored = false
break;
case constants.PASSPORT:
this.label = this.props.config.camera.ui.passport.scanLabel
this.info = this.props.config.camera.ui.passport.infoLabel
this.number = this.props.config.camera.ui.passport.scanNumber
this.overlay = constants.OVERLAY_DOCUMENT
this.animationData = this.state.animationPassport
this.mirrored = false
break;
case constants.OTHER:
this.label = this.props.config.camera.ui.other.scanLabel
this.info = this.props.config.camera.ui.other.infoLabel
this.number = this.props.config.camera.ui.other.scanNumber
this.overlay = constants.OVERLAY_NONE
this.mirrored = false
break;
case constants.SELFIE:
this.label = this.props.config.camera.ui.selfie.scanLabel
this.info = this.props.config.camera.ui.selfie.infoLabel
this.number = this.props.config.camera.ui.selfie.scanNumber
this.overlay = constants.OVERLAY_SELFIE
this.mirrored = true
break;
default:
}
/* configure videoConstraints for webcam */
var width = 800
var height = 600
var fullScreen = false
var videoConstraints = {
facingMode: this.getFacingMode(this.state.cameraPosition),
width: width,
height: height,
}
/* configure animation settings */
var animationWidth = width
if (isMobile) {
width = window.innerWidth
height = window.innerHeight
fullScreen = true
animationWidth = width - 100
if (this.props.documentType === constants.PASSPORT ||
this.props.documentType === constants.VISA) {
animationWidth = width - 150
}
} else {
animationWidth = 350
if (this.props.documentType === constants.PASSPORT ||
this.props.documentType === constants.VISA) {
animationWidth = 300
}
}
/* configure camera position for webcam */
if (browser.name === "chrome" && browser.os === "Android OS") {
if (this.state.cameraPosition === constants.camera.BACK) {
videoConstraints = {
deviceId: this.state.deviceIdBack,
width: width,
height: height,
}
}
}
/* configure animation settings */
const defaultOptions = {
loop: false,
autoplay: true,
animationData: this.animationData,
rendererSettings: {
preserveAspectRatio: 'xMidYMid slice'
}
}
const eventListeners = [{
eventName: 'complete',
callback: () => {
this.setState({
showAnimation: false
}, this.forceUpdate())
}
}]
return (
<Dialog
open={this.props.open}
onClose={() => { this.props.close() }}
fullScreen = {true}
fullWidth={false}
maxWidth={false}
>
{ /* Video Feed */ }
<Webcam
ref={webcamRef}
width={width}
height={height}
audio={false}
mirrored={this.mirrored}
screenshotFormat={this.props.config.camera.imageFormat}
videoConstraints={videoConstraints}
forceScreenshotSourceSize={true}
screenshotQuality={1}
onUserMediaError={(error) => {
this.props.showSnackMessage("error", "Loading the camera failed with: " + error)
this.setState({ disableButtons: true })
}}
style={defaultStyles.videoStream}
/>
{ /* Animation */ }
{ this.state.showAnimation?
<center>
<Lottie
options={defaultOptions}
width={animationWidth}
eventListeners={eventListeners}
style={defaultStyles.lottie}
/>
</center> : <div></div>
}
{ /* Loading */ }
{ this.state.loading?
<center>
<CircularProgress
color="secondary"
style={{ position: 'absolute', marginTop: (window.innerHeight / 2) }} />
</center>
: <div></div>
}
{ /* Camera Labels */ }
<div style={defaultStyles.cameraLabels}>
<center>
<table>
<tbody>
<tr>
<td>
<center>
<h1 style={defaultStyles.label} > { this.label } </h1>
</center>
</td>
</tr>
<tr>
<td>
<center>
<p style={defaultStyles.info} > { this.info } </p>
</center>
</td>
</tr>
<tr>
<td>
<center>
<p style={defaultStyles.scan} > { this.number } </p>
</center>
</td>
</tr>
</tbody>
</table>
</center>
</div>
{ /* Camera and Rotate Camera Buttons */ }
<div>
<center>
<table>
<tbody>
<tr>
<td>
<Fab
color="primary"
style={defaultStyles.camera}
onClick={() => { this.capture() }}>
{ this.state.disableButtons ?
<CircularProgress
style={defaultStyles.progress}
disableShrink={true}
variant="indeterminate"
/> :
<RadioButtonUncheckedIcon
style={defaultStyles.cameraIcon}
/>
}
</Fab>
</td>
<td>
<Fab
color="primary"
style={defaultStyles.refresh}
onClick={ () => { this.changeCameraPosition() } } >
<LoopIcon />
</Fab>
</td>
</tr>
</tbody>
</table>
</center>
</div>
</Dialog>
);
}
}
export default WebcamCapture;