diff --git a/react/src/actions/actionCreators.js b/react/src/actions/actionCreators.js index 2086541..f6e8629 100644 --- a/react/src/actions/actionCreators.js +++ b/react/src/actions/actionCreators.js @@ -29,6 +29,7 @@ import { DISPLAY_CLAIM_INTEREST_MODAL, START_INTERVAL, STOP_INTERVAL, + GET_PIN_LIST, DASHBOARD_SYNC_ONLY_UPDATE, DISPLAY_IMPORT_KEY_MODAL, } from './storeType'; @@ -345,6 +346,13 @@ export function toggleClaimInterestModal(display) { } } +export function getPinList(pinList) { + return { + type: GET_PIN_LIST, + pinList: pinList, + } +} + export function skipFullDashboardUpdate(skip) { return { type: DASHBOARD_SYNC_ONLY_UPDATE, diff --git a/react/src/actions/actions/pin.js b/react/src/actions/actions/pin.js new file mode 100644 index 0000000..0d19c80 --- /dev/null +++ b/react/src/actions/actions/pin.js @@ -0,0 +1,110 @@ +import Config from '../../config'; +import { + getDecryptedPassphrase, + getPinList, + triggerToaster +} from '../actionCreators'; +import { iguanaWalletPassphrase } from './walletAuth'; + +export function encryptPassphrase(passphrase, key, pubKey) { + const payload = { + string: passphrase, + key: key, + pubkey: pubKey, + }; + + return dispatch => { + return fetch(`http://127.0.0.1:${Config.agamaPort}/shepherd/encryptkey`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(payload), + }) + .catch(function(error) { + console.log(error); + dispatch( + triggerToaster( + 'encryptKey', + 'Error', + 'error' + ) + ); + }) + .then(response => response.json()) + .then(json => { + dispatch( + triggerToaster( + 'Passphrase successfully encrypted', + 'Success', + 'success' + ) + ); + }) + } +} + +export function loginWithPin(key, pubKey) { + const payload = { + key: key, + pubkey: pubKey, + }; + + return dispatch => { + return fetch(`http://127.0.0.1:${Config.agamaPort}/shepherd/decryptkey`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(payload), + }) + .catch(function(error) { + console.log(error); + dispatch( + triggerToaster( + 'decryptKey', + 'Error', + 'error' + ) + ); + }) + .then(response => response.json()) + .then(json => { + dispatch(iguanaWalletPassphrase(json.result)); + }) + } +} + +export function loadPinList() { + return dispatch => { + return fetch(`http://127.0.0.1:${Config.agamaPort}/shepherd/getpinlist`, { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + }, + }) + .catch(function(error) { + console.log(error); + dispatch( + triggerToaster( + 'getPinList', + 'Error', + 'error' + ) + ); + }) + .then(response => response.json()) + .then(json => { + dispatch( + triggerToaster( + 'getPinList', + 'Success', + 'success' + ) + ); + dispatch( + getPinList(json.result) + ); + }) + } +} \ No newline at end of file diff --git a/react/src/actions/storeType.js b/react/src/actions/storeType.js index 7a7bdfb..e7e1444 100644 --- a/react/src/actions/storeType.js +++ b/react/src/actions/storeType.js @@ -46,4 +46,5 @@ export const LOGOUT = 'LOGOUT'; export const DISPLAY_COIND_DOWN_MODAL = 'DISPLAY_COIND_DOWN_MODAL'; export const DISPLAY_LOGIN_SETTINGS_MODAL = 'DISPLAY_LOGIN_SETTINGS_MODAL'; export const DISPLAY_CLAIM_INTEREST_MODAL = 'DISPLAY_CLAIM_INTEREST_MODAL'; +export const GET_PIN_LIST = 'GET_PIN_LIST'; export const DISPLAY_IMPORT_KEY_MODAL = 'DISPLAY_IMPORT_KEY_MODAL'; \ No newline at end of file diff --git a/react/src/components/app/app.js b/react/src/components/app/app.js index 75b3992..5fa20d0 100644 --- a/react/src/components/app/app.js +++ b/react/src/components/app/app.js @@ -5,6 +5,7 @@ import Main from '../main/main'; function mapStateToProps(state) { return { + login: state.login, toaster: state.toaster, AddCoin: state.AddCoin, Main: state.Main, diff --git a/react/src/components/dashboard/bodyBottom/bodyBottom.js b/react/src/components/dashboard/bodyBottom/bodyBottom.js new file mode 100644 index 0000000..f8b741e --- /dev/null +++ b/react/src/components/dashboard/bodyBottom/bodyBottom.js @@ -0,0 +1,32 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; +import PropTypes from 'prop-types'; + +export default class BodyEnd extends React.PureComponent { + static propTypes = { + children: PropTypes.node, + }; + + componentDidMount() { + this._popup = document.createElement('div'); + document.body.appendChild(this._popup); + this._render(); + } + + componentDidUpdate() { + this._render(); + } + + componentWillUnmount() { + ReactDOM.unmountComponentAtNode(this._popup); + document.body.removeChild(this._popup); + } + + _render() { + ReactDOM.render(this.props.children, this._popup); + } + + render() { + return null; + } +} \ No newline at end of file diff --git a/react/src/components/dashboard/invoiceModal/invoiceModal.js b/react/src/components/dashboard/invoiceModal/invoiceModal.js new file mode 100755 index 0000000..73602ab --- /dev/null +++ b/react/src/components/dashboard/invoiceModal/invoiceModal.js @@ -0,0 +1,140 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; +import { connect } from 'react-redux'; +import Store from '../../../store'; +import { translate } from '../../../translate/translate'; +import BodyEnd from '../bodyBottom/bodyBottom'; +import { + InvoiceModalRender, + InvoiceModalButtonRender, + AddressItemRender, +} from './invoiceModal.render'; + +class InvoiceModal extends React.Component { + constructor() { + super(); + this.state = { + modalIsOpen: false, + content: '', + qrAddress: '', + qrAmount: 0, + }; + this.openModal = this.openModal.bind(this); + this.closeModal = this.closeModal.bind(this); + this.updateInput = this.updateInput.bind(this); + this.renderAddressList = this.renderAddressList.bind(this); + this.updateQRContent = this.updateQRContent.bind(this); + } + + openModal() { + this.setState({ + modalIsOpen: true + }); + } + + updateInput(e) { + this.setState({ + [e.target.name]: e.target.value + }, this.updateQRContent); + } + + updateQRContent() { + this.setState({ + content: JSON.stringify({ + address: this.state.qrAddress, + amount: this.state.qrAmount, + coin: this.props.ActiveCoin.coin, + }), + }); + } + + closeModal() { + this.setState({ + modalIsOpen: false, + }); + } + + hasNoAmount(address) { + return address.amount === 'N/A' || address.amount === 0; + } + + hasNoInterest(address) { + return address.interest === 'N/A' || address.interest === 0 || !address.interest; + } + + isBasiliskMode() { + return this.props.ActiveCoin.mode === 'basilisk'; + } + + isNativeMode() { + return this.props.ActiveCoin.mode == 'native'; + } + + renderAddressList(type) { + const _addresses = this.props.ActiveCoin.addresses; + const _cache = this.props.ActiveCoin.cache; + const _coin = this.props.ActiveCoin.coin; + + if (_addresses && + _addresses[type] && + _addresses[type].length) { + let items = []; + + for (let i = 0; i < _addresses[type].length; i++) { + let address = _addresses[type][i]; + + if (this.isBasiliskMode() && + this.hasNoAmount(address)) { + address.amount = _cache && _cache[_coin][address.address] && + _cache[_coin][address.address].getbalance && + _cache[_coin][address.address].getbalance.data && + _cache[_coin][address.address].getbalance.data.balance ? _cache[_coin][address.address].getbalance.data.balance : 'N/A'; + } + if (this.isBasiliskMode() && + this.hasNoInterest(address)) { + address.interest = _cache && _cache[_coin][address.address] && + _cache[_coin][address.address].getbalance && + _cache[_coin][address.address].getbalance.data && + _cache[_coin][address.address].getbalance.data.interest ? _cache[_coin][address.address].getbalance.data.interest : 'N/A'; + } + + items.push( + AddressItemRender.call(this, address, type) + ); + } + + return items; + } else { + return null; + } + } + + render() { + if (this.state.modalIsOpen) { + return { InvoiceModalRender.call(this) } + } else { + return InvoiceModalButtonRender.call(this); + } + } +} + +const mapStateToProps = (state) => { + return { + ActiveCoin: { + coin: state.ActiveCoin.coin, + mode: state.ActiveCoin.mode, + send: state.ActiveCoin.send, + receive: state.ActiveCoin.receive, + balance: state.ActiveCoin.balance, + cache: state.ActiveCoin.cache, + activeAddress: state.ActiveCoin.activeAddress, + lastSendToResponse: state.ActiveCoin.lastSendToResponse, + addresses: state.ActiveCoin.addresses, + }, + Dashboard: { + activeHandle: state.Dashboard.activeHandle, + }, + }; +}; + +export default connect(mapStateToProps)(InvoiceModal); diff --git a/react/src/components/dashboard/invoiceModal/invoiceModal.render.js b/react/src/components/dashboard/invoiceModal/invoiceModal.render.js new file mode 100644 index 0000000..25b59b9 --- /dev/null +++ b/react/src/components/dashboard/invoiceModal/invoiceModal.render.js @@ -0,0 +1,107 @@ +import React from 'react'; +import { translate } from '../../../translate/translate'; +import QRCode from 'qrcode.react'; + +export const InvoiceModalRender = function () { + return ( + +
+
+
+
+ +

{ translate('INDEX.CREATE_INVOICE_QR') }

+
+
+
+
+
+
+
+ + + + +
+
+
+ +
+
+
+
+

+ { translate('INDEX.QR_CONTENT') }:
+ { this.state.content } +

+
+
+
+
+
+
+
+
+
+
+ ); +}; + +export const InvoiceModalButtonRender = function () { + return ( + + + + ); +}; + +export const AddressItemRender = function(address, type) { + return ( + + ); +}; diff --git a/react/src/components/dashboard/qrModal/qrModal.js b/react/src/components/dashboard/qrModal/qrModal.js index c1a5757..e152ba9 100755 --- a/react/src/components/dashboard/qrModal/qrModal.js +++ b/react/src/components/dashboard/qrModal/qrModal.js @@ -33,15 +33,9 @@ class QRModal extends React.Component { } handleError(err) { - if (err.name === 'NoVideoInputDevicesError') { - this.setState({ - error: translate('DASHBOARD.QR_ERR_NO_VIDEO_DEVICE'), - }); - } else { - this.setState({ - error: translate('DASHBOARD.QR_ERR_UNKNOWN'), - }); - } + this.setState({ + error: err.name === 'NoVideoInputDevicesError' ? translate('DASHBOARD.QR_ERR_NO_VIDEO_DEVICE') : translate('DASHBOARD.QR_ERR_UNKNOWN'), + }); } openModal() { @@ -68,6 +62,8 @@ class QRModal extends React.Component { modalIsOpen: false, errorShown: this.state.error ? true : false, }); + + ReactDOM.unmountComponentAtNode(document.getElementById('webcam')); } render() { diff --git a/react/src/components/dashboard/receiveCoin/receiveCoin.render.js b/react/src/components/dashboard/receiveCoin/receiveCoin.render.js index 13dc485..8ad55fe 100644 --- a/react/src/components/dashboard/receiveCoin/receiveCoin.render.js +++ b/react/src/components/dashboard/receiveCoin/receiveCoin.render.js @@ -1,6 +1,7 @@ import React from 'react'; import { translate } from '../../../translate/translate'; import QRModal from '../qrModal/qrModal'; +import InvoiceModal from '../invoiceModal/invoiceModal'; export const AddressActionsBasiliskModeRender = function(address) { return ( @@ -69,6 +70,7 @@ export const _ReceiveCoinTableRender = function() {