UNPKG

eagle-id

Version:

A React based component to perform identity verification

409 lines (376 loc) 13.7 kB
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;