diff options
Diffstat (limited to 'ui/app/components')
-rw-r--r-- | ui/app/components/ens-input.js | 1 | ||||
-rw-r--r-- | ui/app/components/qr-scanner/index.js | 2 | ||||
-rw-r--r-- | ui/app/components/qr-scanner/qr-scanner.component.js | 185 | ||||
-rw-r--r-- | ui/app/components/send/send-content/send-content.component.js | 6 | ||||
-rw-r--r-- | ui/app/components/send/send-content/send-to-row/send-to-row.component.js | 3 | ||||
-rw-r--r-- | ui/app/components/send/send.component.js | 22 | ||||
-rw-r--r-- | ui/app/components/send/send.container.js | 6 | ||||
-rw-r--r-- | ui/app/components/send/send.selectors.js | 5 | ||||
-rw-r--r-- | ui/app/components/send/to-autocomplete/to-autocomplete.js | 8 |
9 files changed, 234 insertions, 4 deletions
diff --git a/ui/app/components/ens-input.js b/ui/app/components/ens-input.js index b9f99b3d1..cfdf663a5 100644 --- a/ui/app/components/ens-input.js +++ b/ui/app/components/ens-input.js @@ -54,6 +54,7 @@ EnsInput.prototype.render = function () { const opts = extend(props, { list: 'addresses', onChange: this.onChange.bind(this), + qrScanner: true, }) return h('div', { style: { width: '100%', position: 'relative' }, diff --git a/ui/app/components/qr-scanner/index.js b/ui/app/components/qr-scanner/index.js new file mode 100644 index 000000000..f459f6702 --- /dev/null +++ b/ui/app/components/qr-scanner/index.js @@ -0,0 +1,2 @@ +import QrScanner from './qr-scanner.component' +module.exports = QrScanner diff --git a/ui/app/components/qr-scanner/qr-scanner.component.js b/ui/app/components/qr-scanner/qr-scanner.component.js new file mode 100644 index 000000000..e0b2c40d6 --- /dev/null +++ b/ui/app/components/qr-scanner/qr-scanner.component.js @@ -0,0 +1,185 @@ +import React, { Component } from 'react' +import PropTypes from 'prop-types' +import {connect} from 'react-redux' +import { hideQrScanner, qrCodeDetected} from '../../actions' +import Spinner from '../spinner' +import { BrowserQRCodeReader } from '@zxing/library' + +class QrScanner extends Component { + static propTypes = { + visible: PropTypes.bool, + hideQrScanner: PropTypes.func, + qrCodeDetected: PropTypes.func, + } + constructor (props) { + super(props) + this.state = { + ready: false, + msg: 'Accesing your camera...', + } + this.scanning = false + this.codeReader = null + } + + componentDidUpdate () { + if (this.props.visible && this.camera && !this.scanning) { + this.scanning = true + this.initCamera() + } + } + + initCamera () { + console.log('QR-SCANNER: initCamera ') + this.codeReader = new BrowserQRCodeReader() + this.codeReader.getVideoInputDevices() + .then(videoInputDevices => { + console.log('QR-SCANNER: getVideoInputDevices ', videoInputDevices) + setTimeout(_ => { + this.setState({ + ready: true, + msg: 'Place the QR code in front of your camera so we can read it...'}) + console.log('QR-SCANNER: this.state.ready = true') + }, 2000) + + console.log('QR-SCANNER: started scanning...') + this.codeReader.decodeFromInputVideoDevice(videoInputDevices[0].deviceId, 'video') + .then(content => { + console.log('QR-SCANNER: content found!', content) + this.codeReader.reset() + console.log('QR-SCANNER: stopped scanning...') + const result = this.parseContent(content.text) + if (result.type !== 'unknown') { + console.log('QR-SCANNER: CODE DETECTED', result) + this.props.qrCodeDetected(result) + this.props.hideQrScanner() + this.setState({ ready: false }) + } else { + this.setState({msg: 'Error: We couldn\'t identify that QR code'}) + console.log('QR-SCANNER: Unknown code') + } + }) + .catch(err => { + console.log('QR-SCANNER: decodeFromInputVideoDevice threw an exception: ', err) + }) + }).catch(err => { + console.log('QR-SCANNER: getVideoInputDevices threw an exception: ', err) + }) + } + + parseContent (content) { + let type = 'unknown' + let values = {} + + // Here we could add more cases + // To parse other codes (transactions for ex.) + + if (content.split('ethereum:').length > 1) { + type = 'address' + values = {'address': content.split('ethereum:')[1] } + } + return {type, values} + } + + + stopAndClose = () => { + console.log('QR-SCANNER: stopping scanner...') + this.codeReader.reset() + this.scanning = false + this.props.hideQrScanner() + this.setState({ ready: false }) + } + + render () { + const { visible } = this.props + + if (!visible) { + return null + } + + return ( + <div className={'qr-code-modal-wrapper'}> + <div className={'qr-scanner'} + style={{ + position: 'fixed', + top: '50%', + left: '50%', + zIndex: 1050, + minWidth: '320px', + minHeight: '400px', + maxWidth: '300px', + maxHeight: '300px', + transform: 'translate(-50%, -50%)', + backgroundColor: '#ffffff', + padding: '15px', + }} + > + <h3 style={{ + textAlign: 'center', + marginBottom: '20px', + fontSize: '1.5rem', + fontWeight: '500', + }}> + Scan QR code + </h3> + <div + className={'qr-code-video-wrapper'} + style={{ + overflow: 'hidden', + width: '100%', + height: '275px', + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + }}> + <video + id="video" + style={{ + width: 'auto', + height: '275px', + marginLeft: '-15%', + display: this.state.ready ? 'block' : 'none', + transform: 'scaleX(-1)', + }} + ref={(cam) => { + this.camera = cam + }} + /> + { !this.state.ready ? <Spinner color={'#F7C06C'} /> : null} + </div> + <div className={'qr-code-help'} style={{textAlign: 'center', fontSize: '12px', padding: '15px'}}> + {this.state.msg} + </div> + </div> + <div + className={'qr-code-modal-overlay'} + style={{ + position: 'fixed', + top: '0', + right: '0', + bottom: '0', + left: '0', + zIndex: '1040', + backgroundColor: 'rgba(0, 0, 0, 0.5)', + animationFillMode: 'forwards', + animationDuration: '0.3s', + animationName: 'anim_171532470906313', + animationTimingFunction: 'ease-out', + }} + onClick={this.stopAndClose} + /> + </div> + ) + } +} + +function mapDispatchToProps (dispatch) { + return { + hideQrScanner: () => dispatch(hideQrScanner()), + qrCodeDetected: (data) => dispatch(qrCodeDetected(data)), + } +} +function mapStateToProps (state) { + return {} +} + +export default connect(mapStateToProps, mapDispatchToProps)(QrScanner) diff --git a/ui/app/components/send/send-content/send-content.component.js b/ui/app/components/send/send-content/send-content.component.js index 7a0b1a18e..df7bcb7cc 100644 --- a/ui/app/components/send/send-content/send-content.component.js +++ b/ui/app/components/send/send-content/send-content.component.js @@ -11,6 +11,7 @@ export default class SendContent extends Component { static propTypes = { updateGas: PropTypes.func, + scanQrCode: PropTypes.func, }; render () { @@ -18,7 +19,10 @@ export default class SendContent extends Component { <PageContainerContent> <div className="send-v2__form"> <SendFromRow /> - <SendToRow updateGas={(updateData) => this.props.updateGas(updateData)} /> + <SendToRow + updateGas={(updateData) => this.props.updateGas(updateData)} + scanQrCode={ _ => this.props.scanQrCode()} + /> <SendAmountRow updateGas={(updateData) => this.props.updateGas(updateData)} /> <SendGasRow /> <SendHexDataRow /> diff --git a/ui/app/components/send/send-content/send-to-row/send-to-row.component.js b/ui/app/components/send/send-content/send-to-row/send-to-row.component.js index 892ad5d67..6d8ec8d89 100644 --- a/ui/app/components/send/send-content/send-to-row/send-to-row.component.js +++ b/ui/app/components/send/send-content/send-to-row/send-to-row.component.js @@ -3,6 +3,7 @@ import PropTypes from 'prop-types' import SendRowWrapper from '../send-row-wrapper/' import EnsInput from '../../../ens-input' import { getToErrorObject } from './send-to-row.utils.js' +import adapter from 'webrtc-adapter' export default class SendToRow extends Component { @@ -17,6 +18,7 @@ export default class SendToRow extends Component { updateGas: PropTypes.func, updateSendTo: PropTypes.func, updateSendToError: PropTypes.func, + scanQrCode: PropTypes.func, }; static contextTypes = { @@ -51,6 +53,7 @@ export default class SendToRow extends Component { showError={inError} > <EnsInput + scanQrCode={_ => this.props.scanQrCode()} accounts={toAccounts} closeDropdown={() => closeToDropdown()} dropdownOpen={toDropdownOpen} diff --git a/ui/app/components/send/send.component.js b/ui/app/components/send/send.component.js index 6f1b20c55..fb7eef329 100644 --- a/ui/app/components/send/send.component.js +++ b/ui/app/components/send/send.component.js @@ -38,12 +38,29 @@ export default class SendTransactionScreen extends PersistentForm { updateAndSetGasTotal: PropTypes.func, updateSendErrors: PropTypes.func, updateSendTokenBalance: PropTypes.func, + scanQrCode: PropTypes.func, + qrCodeData: PropTypes.object, }; static contextTypes = { t: PropTypes.func, }; + componentWillReceiveProps (nextProps) { + if (nextProps.qrCodeData) { + if (nextProps.qrCodeData.type === 'address') { + const scannedAddress = nextProps.qrCodeData.values.address.toLowerCase() + const currentAddress = this.props.to && this.props.to.toLowerCase() + if (currentAddress !== scannedAddress) { + this.props.updateSendTo(scannedAddress) + this.updateGas({ to: scannedAddress }) + + // Here we should clear props.qrCodeData + } + } + } + } + updateGas ({ to: updatedToAddress, amount: value } = {}) { const { amount, @@ -170,7 +187,10 @@ export default class SendTransactionScreen extends PersistentForm { return ( <div className="page-container"> <SendHeader history={history}/> - <SendContent updateGas={(updateData) => this.updateGas(updateData)}/> + <SendContent + updateGas={(updateData) => this.updateGas(updateData)} + scanQrCode={_ => this.props.scanQrCode()} + /> <SendFooter history={history}/> </div> ) diff --git a/ui/app/components/send/send.container.js b/ui/app/components/send/send.container.js index 44ebd2792..67a441a9d 100644 --- a/ui/app/components/send/send.container.js +++ b/ui/app/components/send/send.container.js @@ -21,11 +21,14 @@ import { getSendFromObject, getSendTo, getTokenBalance, + getQrCodeData, } from './send.selectors' import { + updateSendTo, updateSendTokenBalance, updateGasData, setGasTotal, + scanQrCode, } from '../../actions' import { resetSendState, @@ -60,6 +63,7 @@ function mapStateToProps (state) { tokenBalance: getTokenBalance(state), tokenContract: getSelectedTokenContract(state), tokenToFiatRate: getSelectedTokenToFiatRate(state), + qrCodeData: getQrCodeData(state), } } @@ -89,5 +93,7 @@ function mapDispatchToProps (dispatch) { }, updateSendErrors: newError => dispatch(updateSendErrors(newError)), resetSendState: () => dispatch(resetSendState()), + scanQrCode: () => dispatch(scanQrCode()), + updateSendTo: (to, nickname) => dispatch(updateSendTo(to, nickname)), } } diff --git a/ui/app/components/send/send.selectors.js b/ui/app/components/send/send.selectors.js index cf07eafe1..ab3f6d34b 100644 --- a/ui/app/components/send/send.selectors.js +++ b/ui/app/components/send/send.selectors.js @@ -46,6 +46,7 @@ const selectors = { getTokenExchangeRate, getUnapprovedTxs, transactionsSelector, + getQrCodeData, } module.exports = selectors @@ -282,3 +283,7 @@ function transactionsSelector (state) { : txsToRender .sort((a, b) => b.time - a.time) } + +function getQrCodeData (state) { + return state.appState.qrCodeData +} diff --git a/ui/app/components/send/to-autocomplete/to-autocomplete.js b/ui/app/components/send/to-autocomplete/to-autocomplete.js index 80cfa7a85..14e30e84c 100644 --- a/ui/app/components/send/to-autocomplete/to-autocomplete.js +++ b/ui/app/components/send/to-autocomplete/to-autocomplete.js @@ -94,11 +94,12 @@ ToAutoComplete.prototype.render = function () { dropdownOpen, onChange, inError, + qrScanner, } = this.props return h('div.send-v2__to-autocomplete', {}, [ - h('input.send-v2__to-autocomplete__input', { + h(`input.send-v2__to-autocomplete__input${qrScanner ? '.with-qr' : ''}`, { placeholder: this.context.t('recipientAddress'), className: inError ? `send-v2__error-border` : '', value: to, @@ -108,7 +109,10 @@ ToAutoComplete.prototype.render = function () { borderColor: inError ? 'red' : null, }, }), - + qrScanner && h(`i.fa.fa-qrcode.fa-lg.send-v2__to-autocomplete__qr-code`, { + style: { color: '#33333' }, + onClick: () => this.props.scanQrCode(), + }), !to && h(`i.fa.fa-caret-down.fa-lg.send-v2__to-autocomplete__down-caret`, { style: { color: '#dedede' }, onClick: () => this.handleInputEvent(), |