Unverified Commit 5700d189 authored by zeroleak's avatar zeroleak Committed by GitHub
Browse files

Merge pull request #21 from pajasevi/webcam-scanning

Webcam scanning
parents ddd0ccb3 f1eee507
// @flow
import React from 'react';
import AbstractModal from './AbstractModal';
import Webcam from 'react-webcam';
import QrcodeDecoder from 'qrcode-decoder';
const qr = new QrcodeDecoder();
type Props = {};
export default class WebcamPayloadModal extends AbstractModal {
webcamRef = React.createRef<Webcam>();
captureInterval: IntervalID | null = null;
constructor(props: Props) {
const initialState = {};
super(props, 'modal-webcam-payload', initialState);
}
componentDidMount() {
this.captureInterval = setInterval(this.captureImage, 2000);
}
componentWillUnmount() {
if (this.captureInterval) {
clearInterval(this.captureInterval);
}
}
captureImage = async () => {
if (this.webcamRef.current) {
const imageData = this.webcamRef.current.getScreenshot();
if (imageData) {
const result = await qr.decodeFromImage(imageData).catch(err => {
console.log('QR code not readable: ', err);
});
if (result) {
this.props.onScan(result.data);
this.props.onClose();
}
}
}
};
renderTitle() {
return <span>Pairing Payload</span>;
}
renderButtons() {
return <div />;
}
renderBody() {
return (
<div className="row">
<div className="col-sm-12">
Scan your <strong>pairing payload</strong> from Samourai Wallet, go to{' '}
<strong>Settings/Transactions/Experimental</strong>
<div className="text-center pt-4">
<Webcam
ref={this.webcamRef}
audio={false}
width={300}
height={300}
screenshotQuality={1}
screenshotFormat={'image/png'}
videoConstraints={{
width: 300,
height: 300,
facingMode: 'user'
}}
/>
</div>
</div>
</div>
);
}
}
......@@ -23,6 +23,11 @@ export const IPC_CLILOCAL = {
RELOAD: 'cliLocal.reload',
DELETE_CONFIG: 'cliLocal.deleteConfig'
};
export const IPC_CAMERA = {
REQUEST: 'camera.request',
GRANTED: 'camera.granted',
DENIED: 'camera.denied'
};
export const CLILOCAL_STATUS = {
DOWNLOADING: 'DOWNLOADING',
ERROR: 'ERROR',
......
......@@ -2,10 +2,13 @@
import React, { Component } from 'react';
import { Alert } from 'react-bootstrap';
import { connect } from 'react-redux';
import { ipcRenderer } from "electron";
import * as Icons from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import WebcamPayloadModal from '../components/Modals/WebcamPayloadModal';
import cliService from '../services/cliService';
import { CLI_CONFIG_FILENAME, DEFAULT_CLIPORT } from '../const';
import { CLI_CONFIG_FILENAME, DEFAULT_CLIPORT, IPC_CAMERA } from '../const';
import { cliLocalService } from '../services/cliLocalService';
import utils from '../services/utils';
......@@ -32,13 +35,16 @@ class InitPage extends Component<Props> {
hasPairingDojo: false,
dojo: false,
tor: false,
pairingPayload: '',
pairingError: undefined,
cliInitError: undefined
cliInitError: undefined,
cameraError: null,
pairingModal: false,
cameraAccessGranted: false,
}
// configuration data
this.hasPairingPayload = undefined
this.pairingPayload = undefined
this.inputCliHost = React.createRef()
this.inputCliPort = React.createRef()
......@@ -55,6 +61,24 @@ class InitPage extends Component<Props> {
this.onSubmitInitialize = this.onSubmitInitialize.bind(this)
}
componentDidMount() {
ipcRenderer.on(IPC_CAMERA.GRANTED, () => {
this.setState({ cameraAccessGranted: true })
});
ipcRenderer.on(IPC_CAMERA.DENIED, () => {
this.setState({ cameraAccessGranted: false, pairingModal: false, cameraError: "Camera access was denied or is unavailable." })
});
}
openPairingModal = () => {
ipcRenderer.send(IPC_CAMERA.REQUEST);
this.setState({ pairingModal: true });
};
closePairingModal = () => {
this.setState({ pairingModal: false });
};
goNextStep () {
this.goStep(this.state.step+1)
}
......@@ -111,6 +135,9 @@ class InitPage extends Component<Props> {
{this.state.step === STEP_LAST && this.step2()}
</form>
{this.state.pairingModal && this.state.cameraAccessGranted && (
<WebcamPayloadModal onClose={this.closePairingModal} onScan={(value) => this.onChangePairingPayload(value)} />
)}
</div>
);
}
......@@ -239,8 +266,8 @@ class InitPage extends Component<Props> {
// pairing
resetPairingPayload() {
this.pairingPayload = undefined
this.setState({
pairingPayload: '',
hasPairingPayload: false,
pairingError: undefined,
cliInitError: undefined
......@@ -248,6 +275,8 @@ class InitPage extends Component<Props> {
}
onChangePairingPayload(payloadStr) {
this.setState({ pairingPayload: payloadStr });
let payload = undefined
try {
payload = JSON.parse(payloadStr)
......@@ -256,7 +285,6 @@ class InitPage extends Component<Props> {
const isDojo = payload.dojo && Object.keys(payload.dojo).length > 0
// seems valid
this.pairingPayload = payloadStr
this.setState({
hasPairingPayload: true,
hasPairingDojo: isDojo,
......@@ -290,7 +318,7 @@ class InitPage extends Component<Props> {
}
onSubmitInitialize() {
cliService.initializeCli(this.state.cliUrl, this.state.currentApiKey, this.state.cliLocal, this.pairingPayload, this.state.tor, this.state.dojo).then(() => {
cliService.initializeCli(this.state.cliUrl, this.state.currentApiKey, this.state.cliLocal, this.state.pairingPayload, this.state.tor, this.state.dojo).then(() => {
// success!
this.goNextStep()
}).catch(error => {
......@@ -314,18 +342,24 @@ class InitPage extends Component<Props> {
<div className="col-sm-11">
<div className="row">
<div className="col-sm-12">
Get your <strong>pairing payload</strong> in Samourai Wallet, go to <strong>Settings/Transactions/Experimental</strong><br/><br/>
Get your <strong>pairing payload</strong> in Samourai Wallet, go to <strong>Settings/Transactions/Experimental</strong><br/>
<a onClick={(event) => {
event.preventDefault();
this.openPairingModal();
}} href="#">Scan your pairing payload</a> using webcam.<br /><br />
<input type="text" className='form-control form-control-lg' required autoFocus onChange={e => {
const myValue = e.target.value
this.onChangePairingPayload(myValue)
this.onChangePairingPayload(e.target.value)
}} onClick={e => {
e.target.value = ''
this.resetPairingPayload()
}} defaultValue='' id="pairingPayload" placeholder='Paste your pairing payload here'/>
}} value={this.state.pairingPayload} id="pairingPayload" placeholder='Paste your pairing payload here'/>
</div>
{this.state.pairingError && <div className="col-sm-12"><br/>
<Alert variant='danger'>{this.state.pairingError}</Alert>
</div>}
{this.state.cameraError && <div className="col-sm-12"><br/>
<Alert variant='danger'>{this.state.cameraError}</Alert>
</div>}
</div>
</div>
</div>
......
......@@ -10,13 +10,13 @@
*
* @flow
*/
import { app, BrowserWindow, ipcMain } from 'electron';
import { app, BrowserWindow, ipcMain, systemPreferences } from 'electron';
import { autoUpdater } from 'electron-updater';
import log from 'electron-log';
import MenuBuilder from './menu';
import { CliLocal } from './mainProcess/cliLocal';
import fs from "fs";
import { GUI_LOG_FILE } from './const';
import { GUI_LOG_FILE, IPC_CAMERA } from './const';
export default class AppUpdater {
......@@ -91,6 +91,20 @@ else {
}
});
ipcMain.on(IPC_CAMERA.REQUEST, async (event) => {
if (process.platform !== 'darwin' || systemPreferences.getMediaAccessStatus("camera") !== "granted") {
event.reply(IPC_CAMERA.GRANTED)
} else {
const granted = await systemPreferences.askForMediaAccess("camera");
if (granted) {
event.reply(IPC_CAMERA.GRANTED)
} else {
event.reply(IPC_CAMERA.DENIED)
}
}
});
// init cliLocal
const guiLogStream = fs.createWriteStream(GUI_LOG_FILE, { flags: 'a' })
new CliLocal(ipcMain, mainWindow.webContents, guiLogStream)
......
......@@ -1024,16 +1024,16 @@
ajv-keywords "^3.1.0"
 
"@electron/get@^1.0.1":
version "1.7.1"
resolved "https://registry.yarnpkg.com/@electron/get/-/get-1.7.1.tgz#41aa60871b9d7e225bbe68135012f88a9ef87012"
integrity sha512-+BOIzkmYbe+oOBGqSByq8zXYXCFztccoymR3uNkvX5ckJ/5xU+4peVyEvFyH6+zfv58hCo99RxgIpwuaMfRtRg==
version "1.7.2"
resolved "https://registry.yarnpkg.com/@electron/get/-/get-1.7.2.tgz#286436a9fb56ff1a1fcdf0e80131fd65f4d1e0fd"
integrity sha512-LSE4LZGMjGS9TloDx0yO44D2UTbaeKRk+QjlhWLiQlikV6J4spgDCjb6z4YIcqmPAwNzlNCnWF4dubytwI+ATA==
dependencies:
debug "^4.1.1"
env-paths "^2.2.0"
fs-extra "^8.1.0"
got "^9.6.0"
sanitize-filename "^1.6.2"
sumchecker "^3.0.0"
sumchecker "^3.0.1"
optionalDependencies:
global-agent "^2.0.2"
global-tunnel-ng "^2.7.1"
......@@ -1468,7 +1468,7 @@
resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d"
integrity sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==
 
"@types/node@*", "@types/node@>= 8", "@types/node@^12.0.12":
"@types/node@*", "@types/node@>= 8":
version "12.12.14"
resolved "https://registry.yarnpkg.com/@types/node/-/node-12.12.14.tgz#1c1d6e3c75dba466e0326948d56e8bd72a1903d2"
integrity sha512-u/SJDyXwuihpwjXy7hOOghagLEV1KdAST6syfnOk6QZAMzZuWZqXy5aYYZbh8Jdpd4escVFP0MvftHNDb9pruA==
......@@ -1478,6 +1478,11 @@
resolved "https://registry.yarnpkg.com/@types/node/-/node-10.17.6.tgz#1aaabd6f6470a6ac3824ab1e94d731ca1326d93d"
integrity sha512-0a2X6cgN3RdPBL2MIlR6Lt0KlM7fOFsutuXcdglcOq6WvLnYXgPQSh0Mx6tO1KCAE8MxbHSOSTWDoUxRq+l3DA==
 
"@types/node@^12.0.12":
version "12.12.22"
resolved "https://registry.yarnpkg.com/@types/node/-/node-12.12.22.tgz#b8d9eae3328b96910a373cf06ac8d3c5abe9c200"
integrity sha512-r5i93jqbPWGXYXxianGATOxTelkp6ih/U0WVnvaqAvTqM+0U6J3kw6Xk6uq/dWNRkEVw/0SLcO5ORXbVNz4FMQ==
"@types/normalize-package-data@^2.4.0":
version "2.4.0"
resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz#e486d0d97396d79beedd0a6e33f4534ff6b4973e"
......@@ -2930,6 +2935,15 @@ babel-plugin-transform-strict-mode@^6.24.1:
babel-runtime "^6.22.0"
babel-types "^6.24.1"
 
babel-polyfill@^6.26.0:
version "6.26.0"
resolved "https://registry.yarnpkg.com/babel-polyfill/-/babel-polyfill-6.26.0.tgz#379937abc67d7895970adc621f284cd966cf2153"
integrity sha1-N5k3q8Z9eJWXCtxiHyhM2WbPIVM=
dependencies:
babel-runtime "^6.26.0"
core-js "^2.5.0"
regenerator-runtime "^0.10.5"
babel-preset-env@^1.1.8:
version "1.7.0"
resolved "https://registry.yarnpkg.com/babel-preset-env/-/babel-preset-env-1.7.0.tgz#dea79fa4ebeb883cd35dab07e260c1c9c04df77a"
......@@ -4361,11 +4375,16 @@ core-js@^2.4.0, core-js@^2.4.1, core-js@^2.5.0, core-js@^2.6.5:
resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.10.tgz#8a5b8391f8cc7013da703411ce5b585706300d7f"
integrity sha512-I39t74+4t+zau64EN1fE5v2W31Adtc/REhzWN+gWRRXg6WH5qAsZm62DHpQ1+Yhe4047T55jvzz7MUqF/dBBlA==
 
core-js@^3.1.3, core-js@^3.4.1:
core-js@^3.1.3:
version "3.4.7"
resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.4.7.tgz#57c35937da80fe494fbc3adcf9cf3dc00eb86b34"
integrity sha512-qaPVGw30J1wQ0GR3GvoPqlGf9GZfKKF4kFC7kiHlcsPTqH3txrs9crCp3ZiMAXuSenhz89Jnl4GZs/67S5VOSg==
 
core-js@^3.4.1:
version "3.6.1"
resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.6.1.tgz#39d5e2e346258cc01eb7d44345b1c3c014ca3f05"
integrity sha512-186WjSik2iTGfDjfdCZAxv2ormxtKgemjC3SI6PL31qOA0j5LhTDVjHChccoc7brwLvpvLPiMyRlcO88C4l1QQ==
core-util-is@1.0.2, core-util-is@~1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
......@@ -5449,10 +5468,10 @@ electron-updater@^4.0.0:
pako "^1.0.10"
semver "^6.3.0"
 
electron@^7.1.3:
version "7.1.3"
resolved "https://registry.yarnpkg.com/electron/-/electron-7.1.3.tgz#9f6779846ced5b145b6303f10dcf4bf7f84e7f27"
integrity sha512-FdzGNR/tX0kC53lQnL7K+ZrxwrxKHf9i8KdL+O2LF5yXHJ3M8oZWc0OM1+nfqQUWCBoBV+7UblMF0+6E/tplEg==
electron@^7.1.7:
version "7.1.7"
resolved "https://registry.yarnpkg.com/electron/-/electron-7.1.7.tgz#520e2bc422e3dfd4bae166dd3be62101f2cbdc52"
integrity sha512-aCLJ4BJwnvOckJgovNul22AYlMFDzm4S4KqKCG2iBlFJyMHBxXAKFKMsgYd40LBZWS3hcY6RHpaYjHSAPLS1pw==
dependencies:
"@electron/get" "^1.0.1"
"@types/node" "^12.0.12"
......@@ -7019,13 +7038,11 @@ globals@^9.18.0:
integrity sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==
 
globalthis@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/globalthis/-/globalthis-1.0.0.tgz#c5fb98213a9b4595f59cf3e7074f141b4169daae"
integrity sha512-vcCAZTJ3r5Qcu5l8/2oyVdoFwxKgfYnMTR2vwWeux/NAVZK3PwcMaWkdUIn4GJbmKuRK7xcvDsLuK+CKcXyodg==
version "1.0.1"
resolved "https://registry.yarnpkg.com/globalthis/-/globalthis-1.0.1.tgz#40116f5d9c071f9e8fb0037654df1ab3a83b7ef9"
integrity sha512-mJPRTc/P39NH/iNG4mXa9aIhNymaQikTrnspeCa2ZuJ+mH2QN/rXwtX3XwKrHqWgUQFbNZKtHM105aHzJalElw==
dependencies:
define-properties "^1.1.2"
function-bind "^1.1.1"
object-keys "^1.0.12"
define-properties "^1.1.3"
 
globby@^10.0.1:
version "10.0.1"
......@@ -8934,6 +8951,11 @@ jsprim@^1.2.2:
json-schema "0.2.3"
verror "1.10.0"
 
jsqr@^1.1.1:
version "1.2.0"
resolved "https://registry.yarnpkg.com/jsqr/-/jsqr-1.2.0.tgz#f93fc65fa7d1ded78b1bcb020fa044352b04261a"
integrity sha512-wKcQS9QC2VHGk7aphWCp1RrFyC0CM6fMgC5prZZ2KV/Lk6OKNoCod9IR6bao+yx3KPY0gZFC5dc+h+KFzCI0Wg==
jsx-ast-utils@^2.2.1, jsx-ast-utils@^2.2.3:
version "2.2.3"
resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-2.2.3.tgz#8a9364e402448a3ce7f14d357738310d9248054f"
......@@ -11704,6 +11726,14 @@ qr.js@0.0.0:
resolved "https://registry.yarnpkg.com/qr.js/-/qr.js-0.0.0.tgz#cace86386f59a0db8050fa90d9b6b0e88a1e364f"
integrity sha1-ys6GOG9ZoNuAUPqQ2baw6IoeNk8=
 
qrcode-decoder@^0.1.2:
version "0.1.2"
resolved "https://registry.yarnpkg.com/qrcode-decoder/-/qrcode-decoder-0.1.2.tgz#ad306254e84403ca157b373682b885af6059155c"
integrity sha512-//e4yWO9UUTLxULH3SOXcuNhw95USDhJQHrT5ncjDg9XkoeBFZrYE6Xr4C8TRcLcxkVTfh+BIXhhQohxodw4NQ==
dependencies:
babel-polyfill "^6.26.0"
jsqr "^1.1.1"
qrcode-terminal@^0.10.0:
version "0.10.0"
resolved "https://registry.yarnpkg.com/qrcode-terminal/-/qrcode-terminal-0.10.0.tgz#a76a48e2610a18f97fa3a2bd532b682acff86c53"
......@@ -11999,6 +12029,11 @@ react-transition-group@^4.0.0:
loose-envify "^1.4.0"
prop-types "^15.6.2"
 
react-webcam@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/react-webcam/-/react-webcam-4.0.0.tgz#137493ba35e581ba3ba4b3a520204587b979214b"
integrity sha512-6V7wMX4NhT8bYkjd9G+HcS1EkU2LJgIZ/0oiqLpQJ78LxMX1Oo8ZxbAtR28NuEWnVVh9YlvgNdTKydL8Mjy0Vw==
react@^16.6.3:
version "16.12.0"
resolved "https://registry.yarnpkg.com/react/-/react-16.12.0.tgz#0c0a9c6a142429e3614834d5a778e18aa78a0b83"
......@@ -12223,6 +12258,11 @@ regenerate@^1.2.1, regenerate@^1.4.0:
resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.0.tgz#4a856ec4b56e4077c557589cae85e7a4c8869a11"
integrity sha512-1G6jJVDWrt0rK99kBjvEtziZNCICAuvIPkSiUFIQxVP06RCVpq3dmDo2oi6ABpYaDYaTRr67BEhL8r1wgEZZKg==
 
regenerator-runtime@^0.10.5:
version "0.10.5"
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.10.5.tgz#336c3efc1220adcedda2c9fab67b5a7955a33658"
integrity sha1-M2w+/BIgrc7dosn6tntaeVWjNlg=
regenerator-runtime@^0.11.0:
version "0.11.1"
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9"
......@@ -13735,7 +13775,7 @@ sumchecker@^2.0.2:
dependencies:
debug "^2.2.0"
 
sumchecker@^3.0.0:
sumchecker@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/sumchecker/-/sumchecker-3.0.1.tgz#6377e996795abb0b6d348e9b3e1dfb24345a8e42"
integrity sha512-MvjXzkz/BOfyVDkG0oFOtBxHX2u3gKbMHIF/dXblZsgD3BWOFLmHovIpZY7BykJdAjcqRCBi1WYBNdEC9yI7vg==
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment