diff --git a/react/package.json b/react/package.json
index 81b81d2..656d1fd 100644
--- a/react/package.json
+++ b/react/package.json
@@ -38,10 +38,12 @@
"bluebird": "^3.5.0",
"express": "^4.14.0",
"file-loader": "^0.10.0",
+ "qrcode.react": "^0.7.1",
"rc-tree": "^1.4.6",
"react": "^15.3.1",
"react-dom": "^15.3.1",
"react-hot-loader": "^1.3.0",
+ "react-qr-reader": "^1.1.3",
"react-redux": "^5.0.3",
"react-router": "^3.0.2",
"react-router-redux": "^4.0.4",
diff --git a/react/src/components/dashboard/qrModal/qrModal.js b/react/src/components/dashboard/qrModal/qrModal.js
new file mode 100755
index 0000000..0866cc9
--- /dev/null
+++ b/react/src/components/dashboard/qrModal/qrModal.js
@@ -0,0 +1,78 @@
+import React from 'react';
+import ReactDOM from 'react-dom';
+import Store from '../../../store';
+import { translate } from '../../../translate/translate';
+import QrReader from 'react-qr-reader'
+import {
+ QRModalRender,
+ QRModalReaderRender
+} from './qrModal.render';
+
+class QRModal extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ modalIsOpen: false,
+ error: null,
+ };
+ this.openModal = this.openModal.bind(this);
+ this.closeModal = this.closeModal.bind(this);
+ this.handleScan = this.handleScan.bind(this);
+ this.handleError = this.handleError.bind(this);
+ document.body.addEventListener('click', this.closeModal);
+ }
+
+ handleScan(data) {
+ if (data !== null) {
+ if (this.props.mode === 'scan') {
+ this.props.setRecieverFromScan(data)
+ }
+ this.closeModal();
+ }
+ }
+
+ handleError(err) {
+ this.setState({
+ error: err,
+ });
+ }
+
+ openModal() {
+ this.setState({
+ modalIsOpen: true
+ });
+
+ if (this.props.mode === 'scan') {
+ ReactDOM.render(
+ , document.getElementById('webcam'));
+ }
+ }
+
+ closeModal() {
+ this.setState({
+ modalIsOpen: false,
+ });
+
+ if (this.props.mode === 'scan') {
+ ReactDOM.unmountComponentAtNode(document.getElementById('webcam'));
+ }
+ }
+
+ render() {
+ if (this.props.mode === 'scan') {
+ return QRModalReaderRender.call(this);
+ } else {
+ return QRModalRender.call(this);
+ }
+ }
+}
+
+export default QRModal;
\ No newline at end of file
diff --git a/react/src/components/dashboard/qrModal/qrModal.render.js b/react/src/components/dashboard/qrModal/qrModal.render.js
new file mode 100644
index 0000000..0524407
--- /dev/null
+++ b/react/src/components/dashboard/qrModal/qrModal.render.js
@@ -0,0 +1,82 @@
+import React from 'react';
+import { translate } from '../../../translate/translate';
+import QRCode from 'qrcode.react';
+
+export const QRModalRender = function () {
+ return (
+
+
+
+
+
+
+
+
+
+
{ translate('INDEX.SCAN_QR_CODE') }
+
+
+
+
+
+
+
+ );
+};
+
+export const QRModalReaderRender = function () {
+ return (
+
+
+
+
+
+
+
+
{ translate('INDEX.SCAN_QRCODE_WEBCAM') }
+
+
+
+
+
+ { this.state.error }
+
+
+
+
+
+
+
+
+
+ );
+};
\ No newline at end of file
diff --git a/react/src/components/dashboard/receiveCoin/receiveCoin.render.js b/react/src/components/dashboard/receiveCoin/receiveCoin.render.js
index f5acf16..108fb88 100644
--- a/react/src/components/dashboard/receiveCoin/receiveCoin.render.js
+++ b/react/src/components/dashboard/receiveCoin/receiveCoin.render.js
@@ -1,5 +1,6 @@
import React from 'react';
import { translate } from '../../../translate/translate';
+import QRModal from '../qrModal/qrModal';
export const AddressActionsBasiliskModeRender = function(address) {
return (
@@ -25,6 +26,9 @@ export const AddressActionsBasiliskModeRender = function(address) {
onClick={ () => this._validateAddressBasilisk(address) }>
+
);
};
@@ -41,6 +45,9 @@ export const AddressActionsNonBasiliskModeRender = function(address, type) {
onClick={ () => this._copyCoinAddress(address) }>
{ translate('INDEX.COPY') }
+
);
};
@@ -140,4 +147,5 @@ export const ReceiveCoinRender = function() {
);
-};
\ No newline at end of file
+};
+
diff --git a/react/src/components/dashboard/sendCoin/sendCoin.js b/react/src/components/dashboard/sendCoin/sendCoin.js
index 3d41a4b..39566aa 100644
--- a/react/src/components/dashboard/sendCoin/sendCoin.js
+++ b/react/src/components/dashboard/sendCoin/sendCoin.js
@@ -55,6 +55,7 @@ class SendCoin extends React.Component {
currentStackLength: 0,
totalStackLength: 0,
utxoMethodInProgress: false,
+ isCameraFeatureDetected: false,
};
this.updateInput = this.updateInput.bind(this);
this.handleBasiliskSend = this.handleBasiliskSend.bind(this);
@@ -64,15 +65,44 @@ class SendCoin extends React.Component {
this.toggleSendAPIType = this.toggleSendAPIType.bind(this);
this._fetchNewUTXOData = this._fetchNewUTXOData.bind(this);
this.handleClickOutside = this.handleClickOutside.bind(this);
+ this.setRecieverFromScan = this.setRecieverFromScan.bind(this);
+ this.detectCamera = this.detectCamera.bind(this);
socket.on('messages', msg => this.updateSocketsData(msg));
}
+ // test device camera capabilities
+ detectCamera() {
+ const _getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia;
+
+ _getUserMedia(
+ { 'video': true },
+ function() {
+ this.setState({
+ isCameraFeatureDetected: true,
+ });
+ },
+ function() {
+ console.warn('this device doesn\'t have camera!');
+ }
+ );
+ }
+
+ setRecieverFromScan(receiver) {
+ this.setState({
+ sendTo: receiver
+ });
+
+ document.getElementById('edexcoinSendTo').focus();
+ }
+
componentWillMount() {
document.addEventListener(
'click',
this.handleClickOutside,
false
);
+
+ this.detectCamera();
}
componentWillUnmount() {
@@ -173,7 +203,7 @@ class SendCoin extends React.Component {
this,
refreshCacheData,
isReadyToUpdate,
- waitUntilCallIsFinished,
+ waitUntilCallIsFinished,
timestamp
);
}
@@ -745,7 +775,6 @@ class SendCoin extends React.Component {
return null;
}
-
render() {
if (this.props.ActiveCoin &&
this.props.ActiveCoin.send &&
diff --git a/react/src/components/dashboard/sendCoin/sendCoin.render.js b/react/src/components/dashboard/sendCoin/sendCoin.render.js
index 9e313ee..3745132 100644
--- a/react/src/components/dashboard/sendCoin/sendCoin.render.js
+++ b/react/src/components/dashboard/sendCoin/sendCoin.render.js
@@ -5,6 +5,8 @@ import {
secondsToString
} from '../../../util/time';
+import QRModal from '../qrModal/qrModal';
+
export const UTXOCacheInfoRender = function(refreshCacheData, isReadyToUpdate, waitUntilCallIsFinished, timestamp) {
return (
@@ -137,7 +139,7 @@ export const OASendUIRender = function() {
export const SendApiTypeSelectorRender = function() {
return (
-
+
+ { this.stateisCameraFeatureDetected &&
+
+
+
+ }
);
};
diff --git a/react/src/translate/en.js b/react/src/translate/en.js
index 8a47b93..4d5937c 100644
--- a/react/src/translate/en.js
+++ b/react/src/translate/en.js
@@ -249,7 +249,10 @@ export const _lang = {
'INFO': 'Info',
'ENTER': 'Enter',
'ADDR_SM': 'address',
- 'ACTIVATING': 'Activating'
+ 'ACTIVATING': 'Activating',
+ 'QRCODE': 'Show QR code',
+ 'SCAN_QR_CODE': 'Scan QR Code',
+ 'SCAN_QRCODE_WEBCAM': 'Scan QR Code with webcam'
},
'ATOMIC': {
'RAW_OUTPUT': 'Raw Output',