Browse Source

Merge pull request #15 from SuperNETorg/v0.25

V0.25
v0.25
pbca26 7 years ago
committed by GitHub
parent
commit
43c345350e
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      react/src/actions/actionCreators.js
  2. 2
      react/src/actions/actions/nativeSend.js
  3. 225
      react/src/actions/actions/tools.js
  4. 4
      react/src/components/dashboard/main/dashboard.render.js
  5. 8
      react/src/components/dashboard/navbar/navbar.render.js
  6. 10
      react/src/components/dashboard/qrModal/qrModal.js
  7. 8
      react/src/components/dashboard/qrModal/qrModal.render.js
  8. 22
      react/src/components/dashboard/sendCoin/sendCoin.js
  9. 18
      react/src/components/dashboard/sendCoin/sendCoin.render.js
  10. 7
      react/src/components/dashboard/sendCoin/sendCoin.scss
  11. 769
      react/src/components/dashboard/tools/tools.js
  12. 39
      react/src/components/dashboard/tools/tools.scss
  13. 19
      react/src/components/login/login.js
  14. 23
      react/src/components/login/login.render.js
  15. 23
      react/src/components/login/login.scss
  16. 1
      react/src/styles/index.scss
  17. 2
      react/src/translate/en.js

1
react/src/actions/actionCreators.js

@ -50,6 +50,7 @@ export * from './actions/getTxDetails';
export * from './actions/electrum';
export * from './actions/mm';
export * from './actions/nativeNetwork';
export * from './actions/tools';
export function changeActiveAddress(address) {
return {

2
react/src/actions/actions/nativeSend.js

@ -27,7 +27,7 @@ export function sendNativeTx(coin, _payload) {
token: Config.token,
params:
(_payload.addressType === 'public' && _payload.sendTo.length !== 95) || !_payload.sendFrom ?
(_payload.substractFee ?
(_payload.subtractFee ?
[
_payload.sendTo,
_payload.amount,

225
react/src/actions/actions/tools.js

@ -0,0 +1,225 @@
import { translate } from '../../translate/translate';
import Config from '../../config';
import {
triggerToaster,
} from '../actionCreators';
import Store from '../../store';
export function shepherdToolsSeedKeys(seed) {
return new Promise((resolve, reject) => {
fetch(`http://127.0.0.1:${Config.agamaPort}/shepherd/electrum/keys`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
seed,
active: true,
iguana: true,
token: Config.token,
}),
})
.catch((error) => {
console.log(error);
Store.dispatch(
triggerToaster(
'shepherdToolsSeedKeys',
'Error',
'error'
)
);
})
.then(response => response.json())
.then(json => {
resolve(!json.result ? 'error' : json);
});
});
}
export function shepherdToolsBalance(coin, address) {
return new Promise((resolve, reject) => {
fetch(`http://127.0.0.1:${Config.agamaPort}/shepherd/electrum/getbalance?coin=${coin}&address=${address}&token=${Config.token}`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
})
.catch((error) => {
console.log(error);
dispatch(
triggerToaster(
'shepherdToolsBalance',
'Error',
'error'
)
);
})
.then(response => response.json())
.then(json => {
resolve(!json.result ? 'error' : json);
});
});
}
export function shepherdToolsTransactions(coin, address) {
return new Promise((resolve, reject) => {
fetch(`http://127.0.0.1:${Config.agamaPort}/shepherd/electrum/listtransactions?coin=${coin}&address=${address}&full=true&maxlength=20&token=${Config.token}`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
})
.catch((error) => {
console.log(error);
dispatch(
triggerToaster(
'shepherdToolsTransactions',
'Error',
'error'
)
);
})
.then(response => response.json())
.then(json => {
resolve(!json.result ? 'error' : json);
});
});
}
export function shepherdToolsBuildUnsigned(coin, value, sendToAddress, changeAddress) {
value = Math.floor(value);
return new Promise((resolve, reject) => {
return fetch(`http://127.0.0.1:${Config.agamaPort}/shepherd/electrum/createrawtx?coin=${coin}&address=${sendToAddress}&value=${value}&change=${changeAddress}&verify=false&push=false&offline=true&token=${Config.token}`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
})
.catch((error) => {
console.log(error);
Store.dispatch(
triggerToaster(
'shepherdToolsBuildUnsigned',
'Error',
'error'
)
);
})
.then(response => response.json())
.then(json => {
resolve(json);
});
});
}
export function shepherdToolsListunspent(coin, address) {
return new Promise((resolve, reject) => {
fetch(`http://127.0.0.1:${Config.agamaPort}/shepherd/electrum/listunspent?coin=${coin}&address=${address}&full=true&token=${Config.token}`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
})
.catch((error) => {
console.log(error);
Store.dispatch(
triggerToaster(
'shepherdToolsListunspent',
'Error',
'error'
)
);
})
.then(response => response.json())
.then(json => {
resolve(!json.result ? 'error' : json);
});
});
}
export function shepherdToolsPushTx(network, rawtx) {
return new Promise((resolve, reject) => {
fetch(`http://127.0.0.1:${Config.agamaPort}/shepherd/electrum/pushtx`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
network,
rawtx,
token: Config.token,
}),
})
.catch((error) => {
console.log(error);
Store.dispatch(
triggerToaster(
'shepherdToolsPushTx',
'Error',
'error'
)
);
})
.then(response => response.json())
.then(json => {
resolve(!json.result ? 'error' : json);
});
});
}
export function shepherdToolsWifToKP(coin, wif) {
return new Promise((resolve, reject) => {
fetch(`http://127.0.0.1:${Config.agamaPort}/shepherd/electrum/wiftopub?coin=${coin}&wif=${wif}&token=${Config.token}`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
})
.catch((error) => {
console.log(error);
Store.dispatch(
triggerToaster(
'shepherdToolsWifToKP',
'Error',
'error'
)
);
})
.then(response => response.json())
.then(json => {
resolve(!json.result ? 'error' : json);
});
});
}
export function shepherdToolsSeedToWif(seed, network, iguana) {
return new Promise((resolve, reject) => {
fetch(`http://127.0.0.1:${Config.agamaPort}/shepherd/electrum/seedtowif`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
seed,
network,
iguana,
token: Config.token,
}),
})
.catch((error) => {
console.log(error);
Store.dispatch(
triggerToaster(
'shepherdToolsSeedToWif',
'Error',
'error'
)
);
})
.then(response => response.json())
.then(json => {
resolve(!json.result ? 'error' : json);
});
});
}

4
react/src/components/dashboard/main/dashboard.render.js

@ -13,6 +13,7 @@ import Settings from '../settings/settings';
import ReceiveCoin from '../receiveCoin/receiveCoin';
import About from '../about/about';
import Support from '../support/support';
import Tools from '../tools/tools';
import WalletsMain from '../walletsMain/walletsMain';
import WalletsTxInfo from '../walletsTxInfo/walletsTxInfo';
import CoindDownModal from '../coindDownModal/coindDownModal';
@ -56,6 +57,9 @@ const DashboardRender = function() {
{ this.isSectionActive('support') &&
<Support />
}
{ this.isSectionActive('tools') &&
<Tools />
}
</div>
</div>
);

8
react/src/components/dashboard/navbar/navbar.render.js

@ -2,6 +2,7 @@ import React from 'react';
import { translate } from '../../../translate/translate';
import mainWindow from '../../../util/mainWindow';
import ReactTooltip from 'react-tooltip';
import Config from '../../../config';
const NavbarRender = function() {
return (
@ -81,6 +82,13 @@ const NavbarRender = function() {
<i className="site-menu-icon"></i> Explorer
</a>
</li>*/ }
{ Config.experimentalFeatures &&
<li className={ this.isSectionActive('tools') ? 'active nav-top-menu' : 'nav-top-menu' }>
<a onClick={ () => this.dashboardChangeSection('tools') }>
<i className="site-menu-icon"></i> Tools
</a>
</li>
}
{ !navigator.onLine &&
<li
className="nav-top-menu offline"

10
react/src/components/dashboard/qrModal/qrModal.js

@ -2,7 +2,7 @@ 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 QrReader from 'react-qr-reader';
import {
QRModalRender,
QRModalReaderRender,
@ -72,15 +72,11 @@ class QRModal extends React.Component {
const a = document.getElementById('saveModalImage' + this.props.content);
a.href = dataURL;
a.download = this.props.content;
a.download = this.props.fileName || this.props.content;
}
render() {
if (this.props.mode === 'scan') {
return QRModalReaderRender.call(this);
} else {
return QRModalRender.call(this);
}
return this.props.mode === 'scan' ? QRModalReaderRender.call(this) : QRModalRender.call(this);
}
}

8
react/src/components/dashboard/qrModal/qrModal.render.js

@ -14,7 +14,7 @@ export const QRModalRender = function() {
<div
className={ 'modal modal-3d-sign ' + (this.state.modalIsOpen ? 'show in' : 'fade hide') }
id="QRModal">
<div className="modal-dialog modal-center modal-sm">
<div className={ `modal-dialog modal-center modal-${this.props.modalSize || 'sm' }` }>
<div className="modal-content">
<div className="modal-header bg-orange-a400 wallet-send-header">
<button
@ -23,7 +23,7 @@ export const QRModalRender = function() {
onClick={ this.closeModal }>
<span>×</span>
</button>
<h4 className="modal-title white text-left">{ translate('INDEX.SCAN_QR_CODE') }</h4>
<h4 className="modal-title white text-left">{ this.props.title || translate('INDEX.SCAN_QR_CODE') }</h4>
</div>
<div className="modal-body">
<div className="animsition vertical-align fade-in">
@ -32,7 +32,7 @@ export const QRModalRender = function() {
className="page-content vertical-align-middle text-center">
<QRCode
value={ this.props.content }
size={ 198 } />
size={ Number(this.props.qrSize) || 198 } />
<p className="margin-top-10">
<a href=""
id={ 'saveModalImage' + this.props.content }
@ -61,7 +61,7 @@ export const QRModalReaderRender = function() {
className="btn btn-default"
onClick={ this.openModal }>
<i className="icon fa-qrcode"></i>
{ translate('INDEX.SCAN_QRCODE_WEBCAM') }
{ translate('INDEX.SCAN_QR_CODE') }
</button>
<div
className={ 'modal modal-3d-sign ' + (this.state.modalIsOpen ? 'show in' : 'fade hide') }

22
react/src/components/dashboard/sendCoin/sendCoin.js

@ -56,6 +56,25 @@ class SendCoin extends React.Component {
this.isTransparentTx = this.isTransparentTx.bind(this);
this.toggleSubtractFee = this.toggleSubtractFee.bind(this);
this.isFullySynced = this.isFullySynced.bind(this);
this.setSendAmountAll = this.setSendAmountAll.bind(this);
this.setSendToSelf = this.setSendToSelf.bind(this);
}
setSendAmountAll() {
const _amount = this.state.amount;
const _amountSats = this.state.amount * 100000000;
const _balanceSats = this.props.ActiveCoin.balance.balanceSats;
const _fees = mainWindow.spvFees;
this.setState({
amount: Number((0.00000001 * (_balanceSats - _fees[this.props.ActiveCoin.coin])).toFixed(8)),
});
}
setSendToSelf() {
this.setState({
sendTo: this.props.Dashboard.electrumCoins[this.props.ActiveCoin.coin].pub,
});
}
copyTXID(txid) {
@ -426,7 +445,8 @@ class SendCoin extends React.Component {
this.state.amount * 100000000,
this.state.sendTo,
this.props.Dashboard.electrumCoins[this.props.ActiveCoin.coin].pub
).then((sendPreflight) => {
)
.then((sendPreflight) => {
if (sendPreflight &&
sendPreflight.msg === 'success') {
this.setState(Object.assign({}, this.state, {

18
react/src/components/dashboard/sendCoin/sendCoin.render.js

@ -52,6 +52,13 @@ export const _SendFormRender = function() {
}
<div className="row">
<div className="col-xlg-12 form-group form-material">
{ this.props.ActiveCoin.mode === 'spv' &&
<button type="button"
className="btn btn-default btn-send-self"
onClick={ this.setSendToSelf }>
{ translate('SEND.SELF') }
</button>
}
<label
className="control-label"
htmlFor="kmdWalletSendTo">{ translate('INDEX.SEND_TO') }</label>
@ -67,6 +74,13 @@ export const _SendFormRender = function() {
required />
</div>
<div className="col-lg-12 form-group form-material">
{ this.props.ActiveCoin.mode === 'spv' &&
<button type="button"
className="btn btn-default btn-send-self"
onClick={ this.setSendAmountAll }>
{ translate('SEND.ALL') }
</button>
}
<label
className="control-label"
htmlFor="kmdWalletAmount">
@ -353,7 +367,9 @@ export const SendRender = function() {
this.state.lastSendToResponse.raw.txid &&
<div>{ this.state.lastSendToResponse.raw.txid.replace(/\[.*\]/, '') }</div>
}
{ this.state.lastSendToResponse.raw.txid.indexOf('bad-txns-inputs-spent') > -1 &&
{ this.state.lastSendToResponse.raw &&
this.state.lastSendToResponse.raw.txid &&
this.state.lastSendToResponse.raw.txid.indexOf('bad-txns-inputs-spent') > -1 &&
<div className="margin-top-10">
{ translate('SEND.BAD_TXN_SPENT_ERR1') }
<ul>

7
react/src/components/dashboard/sendCoin/sendCoin.scss

@ -6,4 +6,11 @@
position: relative;
top: -2px;
}
}
.btn-send-self {
position: absolute;
right: 16px;
top: 20px;
padding: 0 11px;
}

769
react/src/components/dashboard/tools/tools.js

@ -0,0 +1,769 @@
import React from 'react';
import { translate } from '../../../translate/translate';
import addCoinOptionsCrypto from '../../addcoin/addcoinOptionsCrypto';
import addCoinOptionsAC from '../../addcoin/addcoinOptionsAC';
import Select from 'react-select';
import {
triggerToaster,
shepherdToolsBalance,
shepherdToolsBuildUnsigned,
shepherdToolsPushTx,
shepherdToolsSeedToWif,
shepherdToolsWifToKP,
shepherdElectrumListunspent,
} from '../../../actions/actionCreators';
import Store from '../../../store';
import QRCode from 'qrcode.react';
import QRModal from '../qrModal/qrModal';
class Tools extends React.Component {
constructor() {
super();
this.state = {
sendFrom: '',
sendTo: '',
amount: 0,
selectedCoin: '',
balance: null,
tx2qr: null,
utxo: null,
rawTx2Push: null,
txPushResult: null,
string2qr: null,
s2wSeed: '',
s2wCoin: '',
s2wisIguana: true,
s2wResult: null,
w2wWif: '',
w2wCoin: '',
w2wResult: null,
utxoAddr: '',
utxoCoin: '',
utxoResult: null,
balanceAddr: '',
balanceCoin: '',
balanceResult: null,
};
this.updateInput = this.updateInput.bind(this);
this.updateSelectedCoin = this.updateSelectedCoin.bind(this);
this.getBalance = this.getBalance.bind(this);
this.getUnsignedTx = this.getUnsignedTx.bind(this);
this.sendTx = this.sendTx.bind(this);
this.closeQr = this.closeQr.bind(this);
this.setActiveSection = this.setActiveSection.bind(this);
this.seed2Wif = this.seed2Wif.bind(this);
this.wif2wif = this.wif2wif.bind(this);
this.getBalanceAlt = this.getBalanceAlt.bind(this);
this.getUtxos = this.getUtxos.bind(this);
this.toggleS2wIsIguana = this.toggleS2wIsIguana.bind(this);
}
getUtxos() {
const _coin = this.state.utxoCoin.split('|');
shepherdElectrumListunspent(_coin[0], this.state.utxoAddr)
.then((res) => {
if (res.msg === 'success') {
this.setState({
utxoResult: res.result,
});
} else {
Store.dispatch(
triggerToaster(
res.result,
'Get UTXO error',
'error'
)
);
}
});
}
getBalanceAlt() {
const _coin = this.state.balanceCoin.split('|');
shepherdToolsBalance(_coin[0], this.state.balanceAddr)
.then((res) => {
if (res.msg === 'success') {
this.setState({
balanceResult: res.result,
});
} else {
Store.dispatch(
triggerToaster(
res.result,
'Get balance error',
'error'
)
);
}
});
}
wif2wif() {
const _coin = this.state.w2wCoin.split('|');
shepherdToolsWifToKP(_coin[0], this.state.w2wWif)
.then((res) => {
// console.warn(res);
if (res.msg === 'success') {
this.setState({
w2wResult: res.result,
});
} else {
Store.dispatch(
triggerToaster(
res.result,
'Seed to wif error',
'error'
)
);
}
});
}
seed2Wif() {
const _coin = this.state.s2wCoin.split('|');
shepherdToolsSeedToWif(
this.state.s2wSeed,
_coin[0],
this.state.s2wisIguana
)
.then((res) => {
// console.warn(res);
if (res.msg === 'success') {
this.setState({
s2wResult: res.result,
});
} else {
Store.dispatch(
triggerToaster(
res.result,
'Seed to wif error',
'error'
)
);
}
});
}
sendTx(rawTx2Push) {
let _txData = rawTx2Push.split(':');
// console.warn(_txData);
shepherdToolsPushTx(_txData[0], _txData[1])
.then((res) => {
// console.warn(res);
this.setState({
txPushResult: res.result,
rawTx2Push,
});
});
}
getBalance() {
const _coin = this.state.selectedCoin.split('|');
shepherdToolsBalance(_coin[0], this.state.sendFrom)
.then((res) => {
if (res.msg === 'success') {
this.setState({
balance: res.result,
});
} else {
Store.dispatch(
triggerToaster(
res.result,
'Offline tx signing',
'error'
)
);
}
});
}
getUnsignedTx() {
const _coin = this.state.selectedCoin.split('|');
shepherdToolsBuildUnsigned(_coin[0], this.state.amount * 100000000, this.state.sendTo, this.state.sendFrom)
.then((res) => {
// console.warn(res);
if (res.msg === 'success') {
let tx2qr = 'agtx:';
res = res.result;
tx2qr += (res.network === 'komodo' ? 'KMD' : res.network) + ':' + res.outputAddress + ':' + res.changeAddress + ':' + res.value + ':' + res.change + ':u:';
for (let i = 0; i < res.utxoSet.length; i++) {
tx2qr += res.utxoSet[i].txid + ':' + res.utxoSet[i].value + ':' + res.utxoSet[i].vout + (i === res.utxoSet.length -1 ? '' : '-');
}
// console.warn(tx2qr);
// console.warn('txqr length', tx2qr.length);
// max 350 chars
this.setState({
tx2qr,
utxo: res.utxoSet,
});
} else {
Store.dispatch(
triggerToaster(
res.result,
'Offline tx signing',
'error'
)
);
}
});
}
closeQr() {
this.setState({
tx2qr: null,
});
}
renderCoinOption(option) {
return (
<div>
<img
src={ `assets/images/cryptologo/${option.icon.toLowerCase()}.png` }
alt={ option.label }
width="30px"
height="30px" />
<span className="margin-left-10">{ option.label }</span>
</div>
);
}
updateSelectedCoin(e, propName) {
if (e &&
e.value &&
e.value.indexOf('|')) {
this.setState({
[propName]: e.value,
});
}
}
updateInput(e) {
this.setState({
[e.target.name]: e.target.value,
});
}
setActiveSection(section) {
this.setState({
activeSection: section,
});
}
toggleS2wIsIguana() {
this.setState({
s2wisIguana: !this.state.s2wisIguana,
});
}
renderUTXOResponse() {
const _utxos = this.state.utxoResult;
const _coin = this.state.utxoCoin.split('|');
let _items = [];
if (_utxos &&
_utxos.length) {
for (let i = 0; i < _utxos.length; i++) {
_items.push(
<tr key={ `tools-utxos-${i}` }>
<td>{ _utxos[i].amount }</td>
<td>{ _utxos[i].confirmations }</td>
<td>{ _utxos[i].vout }</td>
{ _coin[0] === 'KMD' &&
<td>{ _utxos[i].locktime }</td>
}
<td>{ _utxos[i].txid }</td>
</tr>
);
}
}
return (
<table className="table table-hover dataTable table-striped">
<thead>
<tr>
<th>Amount</th>
<th>Confirmations</th>
<th>Vout</th>
{ _coin[0] === 'KMD' &&
<th>Locktime</th>
}
<th>TxID</th>
</tr>
</thead>
<tbody>
{ _items }
</tbody>
<tfoot>
<tr>
<th>Amount</th>
<th>Confirmations</th>
<th>Vout</th>
{ _coin[0] === 'KMD' &&
<th>Locktime</th>
}
<th>TxID</th>
</tr>
</tfoot>
</table>
);
}
render() {
return (
<div className="page margin-left-0">
<div className="page-content tools background--white">
<div className="row">
<div className="col-sm-12 no-padding-left">
<h2>Tools</h2>
<div className="margin-top-20">
<button type="button"
className="btn btn-default"
onClick={ () => this.setActiveSection('offlinesig-create') }>
Offline signing create
</button>
<button type="button"
className="btn btn-default margin-left-20"
onClick={ () => this.setActiveSection('offlinesig-scan') }>
Offline signing scan
</button>
<button type="button"
className="btn btn-default margin-left-20"
onClick={ () => this.setActiveSection('string2qr') }>
String to QR
</button>
<button type="button"
className="btn btn-default margin-left-20"
onClick={ () => this.setActiveSection('seed2kp') }>
Seed to key pair
</button>
<button type="button"
className="btn btn-default margin-left-20"
onClick={ () => this.setActiveSection('wif2wif') }>
WIF to WIF
</button>
<button type="button"
className="btn btn-default margin-left-20"
onClick={ () => this.setActiveSection('balance') }>
Balance *
</button>
<button type="button"
className="btn btn-default margin-left-20"
onClick={ () => this.setActiveSection('utxo') }>
UTXO *
</button>
<div className="margin-top-10">* Electrum</div>
</div>
<hr />
{ this.state.activeSection === 'offlinesig-create' &&
<div className="row margin-left-10">
<h4 className="margin-top-20">Offline Transaction Signing</h4>
<div className="col-xlg-12 form-group form-material no-padding-left padding-top-20 padding-bottom-70">
<label
className="control-label col-sm-1 no-padding-left"
htmlFor="kmdWalletSendTo">Coin</label>
<Select
name="selectedCoin"
className="col-sm-3"
value={ this.state.selectedCoin }
onChange={ (event) => this.updateSelectedCoin(event, 'selectedCoin') }
optionRenderer={ this.renderCoinOption }
valueRenderer={ this.renderCoinOption }
options={ addCoinOptionsCrypto().concat(addCoinOptionsAC()) } />
</div>
<div className="col-sm-12 form-group form-material no-padding-left">
<label
className="control-label col-sm-1 no-padding-left"
htmlFor="kmdWalletSendTo">{ translate('INDEX.SEND_FROM') }</label>
<input
type="text"
className="form-control col-sm-3"
name="sendFrom"
onChange={ this.updateInput }
value={ this.state.sendFrom }
id="kmdWalletSendTo"
placeholder={ translate('SEND.ENTER_ADDRESS') }
autoComplete="off"
required />
</div>
<div className="col-sm-12 form-group form-material no-padding-left margin-top-10 padding-bottom-10">
<button
type="button"
className="btn btn-info col-sm-2"
onClick={ this.getBalance }>
Get balance
</button>
{ this.state.balance &&
<label className="margin-left-20">Balance: { this.state.balance.balance } </label>
}
</div>
<div className="col-sm-12 form-group form-material no-padding-left">
<label
className="control-label col-sm-1 no-padding-left"
htmlFor="kmdWalletSendTo">{ translate('INDEX.SEND_TO') }</label>
<input
type="text"
className="form-control col-sm-3"
name="sendTo"
onChange={ this.updateInput }
value={ this.state.sendTo }
id="kmdWalletSendTo"
placeholder={ translate('SEND.ENTER_ADDRESS') }
autoComplete="off"
required />
</div>
<div className="col-sm-12 form-group form-material no-padding-left">
<label
className="control-label col-sm-1 no-padding-left"
htmlFor="kmdWalletAmount">
{ translate('INDEX.AMOUNT') }
</label>
<input
type="text"
className="form-control col-sm-3"
name="amount"
value={ this.state.amount }
onChange={ this.updateInput }
id="kmdWalletAmount"
placeholder="0.000"
autoComplete="off" />
</div>
<div className="col-sm-12 form-group form-material no-padding-left margin-top-20">
<button
type="button"
className="btn btn-primary col-sm-2"
onClick={ this.getUnsignedTx }>
Generate unsigned tx QR
</button>
</div>
{ this.state.tx2qr &&
<div className="col-sm-12 form-group form-material no-padding-left margin-top-20">
<label className="control-label col-sm-1 no-padding-left">QR payload</label>
<textarea
rows="5"
cols="20"
className="col-sm-7"
value={ this.state.tx2qr }></textarea>
</div>
}
{ this.state.tx2qr &&
<div className="col-sm-12 form-group form-material no-padding-left margin-top-20">
<label className="control-label col-sm-2 no-padding-left">
UTXO count: { this.state.utxo.length }
</label>
{ this.state.utxo.length > 3 &&
<div className="col-red margin-left-20 margin-top-5">cant encode a qr tx larger than 3 utxos!</div>
}
</div>
}
{ this.state.tx2qr &&
this.state.utxo.length < 4 &&
<div className="offlinesig-qr">
<div className="margin-top-50 margin-bottom-70 center">
<div>
<QRCode
value={ this.state.tx2qr }
size={ 560 } />
</div>
<button
type="button"
className="btn btn-primary col-sm-2"
onClick={ this.closeQr }>
Close
</button>
</div>
</div>
}
</div>
}
{ this.state.activeSection === 'offlinesig-scan' &&
<div className="row margin-left-10">
<div className="col-sm-12 form-group form-material no-padding-left">
<h4 className="margin-top-20 no-padding-left">Push QR transaction</h4>
</div>
<div className="col-sm-12 form-group form-material no-padding-left">
<QRModal
mode="scan"
setRecieverFromScan={ this.sendTx } />
</div>
{ this.state.rawTx2Push &&
<div className="col-sm-12 form-group form-material no-padding-left margin-top-20">
<textarea
rows="5"
cols="20"
className="col-sm-7 no-padding-left"
value={ this.state.rawTx2Push }></textarea>
</div>
}
{ this.state.txPushResult &&
<div className="col-sm-12 form-group form-material no-padding-left margin-top-20">
{ this.state.txPushResult.length === 64 &&
<div>
<div className="margin-bottom-15">
{ this.state.rawTx2Push.split(':')[0].toUpperCase() } transaction pushed!
</div>
<div>TxID { this.state.txPushResult }</div>
</div>
}
{ this.state.txPushResult.length !== 64 &&
<div>Error: { this.state.txPushResult }</div>
}
</div>
}
</div>
}
{ this.state.activeSection === 'string2qr' &&
<div className="row margin-left-10">
<div className="col-sm-12 form-group form-material no-padding-left">
<h4 className="margin-top-20 no-padding-left">String to QR</h4>
</div>
<div className="col-sm-12 form-group form-material no-padding-left">
<input
type="text"
className="form-control col-sm-5"
name="string2qr"
value={ this.state.string2qr }
onChange={ this.updateInput }
placeholder="Type a string here"
autoComplete="off" />
</div>
{ this.state.string2qr &&
<div className="col-sm-12 form-group form-material no-padding-left margin-top-50 center">
<QRCode
value={ this.state.string2qr }
size={ 320 } />
</div>
}
</div>
}
{ this.state.activeSection === 'balance' &&
<div className="row margin-left-10">
<div className="col-xlg-12 form-group form-material no-padding-left padding-top-20 padding-bottom-70">
<label
className="control-label col-sm-1 no-padding-left"
htmlFor="kmdWalletSendTo">Coin</label>
<Select
name="balanceCoin"
className="col-sm-3"
value={ this.state.balanceCoin }
onChange={ (event) => this.updateSelectedCoin(event, 'balanceCoin') }
optionRenderer={ this.renderCoinOption }
valueRenderer={ this.renderCoinOption }
options={ addCoinOptionsCrypto().concat(addCoinOptionsAC()) } />
</div>
<div className="col-sm-12 form-group form-material no-padding-left">
<label
className="control-label col-sm-1 no-padding-left"
htmlFor="kmdWalletSendTo">Address</label>
<input
type="text"
className="form-control col-sm-3"
name="balanceAddr"
onChange={ this.updateInput }
value={ this.state.balanceAddr }
placeholder={ translate('SEND.ENTER_ADDRESS') }
autoComplete="off"
required />
</div>
<div className="col-sm-12 form-group form-material no-padding-left margin-top-10 padding-bottom-10">
<button
type="button"
className="btn btn-info col-sm-2"
onClick={ this.getBalanceAlt }>
Get balance
</button>
</div>
{ this.state.balanceResult &&
<div className="col-sm-12 form-group form-material no-padding-left margin-top-10">
<div>
<strong>Balance (confirmed):</strong> { this.state.balanceResult.balance }
</div>
<div className="margin-top-10">
<strong>Balance (unconfirmed):</strong> { this.state.balanceResult.unconfirmed }
</div>
</div>
}
</div>
}
{ this.state.activeSection === 'utxo' &&
<div className="row margin-left-10">
<div className="col-xlg-12 form-group form-material no-padding-left padding-top-20 padding-bottom-70">
<label
className="control-label col-sm-1 no-padding-left"
htmlFor="kmdWalletSendTo">Coin</label>
<Select
name="utxoCoin"
className="col-sm-3"
value={ this.state.utxoCoin }
onChange={ (event) => this.updateSelectedCoin(event, 'utxoCoin') }
optionRenderer={ this.renderCoinOption }
valueRenderer={ this.renderCoinOption }
options={ addCoinOptionsCrypto().concat(addCoinOptionsAC()) } />
</div>
<div className="col-sm-12 form-group form-material no-padding-left">
<label
className="control-label col-sm-1 no-padding-left"
htmlFor="kmdWalletSendTo">Address</label>
<input
type="text"
className="form-control col-sm-3"
name="utxoAddr"
onChange={ this.updateInput }
value={ this.state.utxoAddr }
placeholder={ translate('SEND.ENTER_ADDRESS') }
autoComplete="off"
required />
</div>
<div className="col-sm-12 form-group form-material no-padding-left margin-top-10 padding-bottom-10">
<button
type="button"
className="btn btn-info col-sm-2"
onClick={ this.getUtxos }>
Get UTXO(s)
</button>
</div>
{ this.state.utxoResult &&
<div className="col-sm-12 form-group form-material no-padding-left margin-top-10">
{ this.renderUTXOResponse() }
</div>
}
</div>
}
{ this.state.activeSection === 'wif2wif' &&
<div className="row margin-left-10">
<div className="col-xlg-12 form-group form-material no-padding-left padding-top-20 padding-bottom-70">
<label
className="control-label col-sm-1 no-padding-left"
htmlFor="kmdWalletSendTo">Coin</label>
<Select
name="w2wCoin"
className="col-sm-3"
value={ this.state.w2wCoin }
onChange={ (event) => this.updateSelectedCoin(event, 'w2wCoin') }
optionRenderer={ this.renderCoinOption }
valueRenderer={ this.renderCoinOption }
options={ addCoinOptionsCrypto().concat(addCoinOptionsAC()) } />
</div>
<div className="col-sm-12 form-group form-material no-padding-left">
<label
className="control-label col-sm-1 no-padding-left"
htmlFor="kmdWalletSendTo">Address</label>
<input
type="text"
className="form-control col-sm-3"
name="w2wWif"
onChange={ this.updateInput }
value={ this.state.w2wWif }
placeholder={ translate('SEND.ENTER_ADDRESS') }
autoComplete="off"
required />
</div>
<div className="col-sm-12 form-group form-material no-padding-left margin-top-10 padding-bottom-10">
<button
type="button"
className="btn btn-info col-sm-2"
onClick={ this.wif2wif }>
Get WIF
</button>
</div>
{ this.state.w2wResult &&
<div className="col-sm-12 form-group form-material no-padding-left margin-top-10">
<div>
<strong>WIF:</strong> { this.state.w2wResult.keys.priv }
</div>
<div className="margin-top-10">
<strong>Pub:</strong> { this.state.w2wResult.keys.pub }
</div>
</div>
}
</div>
}
{ this.state.activeSection === 'seed2kp' &&
<div className="row margin-left-10">
<div className="col-xlg-12 form-group form-material no-padding-left padding-top-20 padding-bottom-70">
<label
className="control-label col-sm-1 no-padding-left"
htmlFor="kmdWalletSendTo">Coin</label>
<Select
name="s2wCoin"
className="col-sm-3"
value={ this.state.s2wCoin }
onChange={ (event) => this.updateSelectedCoin(event, 's2wCoin') }
optionRenderer={ this.renderCoinOption }
valueRenderer={ this.renderCoinOption }
options={ addCoinOptionsCrypto().concat(addCoinOptionsAC()) } />
</div>
<div className="col-sm-12 form-group form-material no-padding-left">
<label
className="control-label col-sm-1 no-padding-left"
htmlFor="kmdWalletSendTo">Address</label>
<input
type="text"
className="form-control col-sm-3"
name="s2wSeed"
onChange={ this.updateInput }
value={ this.state.s2wSeed }
placeholder={ translate('SEND.ENTER_ADDRESS') }
autoComplete="off"
required />
</div>
<div className="col-sm-12 form-group form-material no-padding-left">
<label className="switch">
<input
type="checkbox"
checked={ this.state.s2wisIguana } />
<div
className="slider"
onClick={ this.toggleS2wIsIguana }></div>
</label>
<div
className="toggle-label pointer iguana-core-toggle"
onClick={ this.toggleS2wIsIguana }>
Iguana Core compatible
</div>
</div>
<div className="col-sm-12 form-group form-material no-padding-left margin-top-10 padding-bottom-10">
<button
type="button"
className="btn btn-info col-sm-2"
onClick={ this.seed2Wif }>
Get WIF
</button>
</div>
{ this.state.s2wResult &&
<div className="col-sm-12 form-group form-material no-padding-left margin-top-10">
<div>
<strong>WIF:</strong> { this.state.s2wResult.keys.priv }
</div>
<div className="margin-top-10">
<strong>Pub:</strong> { this.state.s2wResult.keys.pub }
</div>
</div>
}
</div>
}
</div>
</div>
</div>
</div>
);
}
}
export default Tools;

39
react/src/components/dashboard/tools/tools.scss

@ -0,0 +1,39 @@
.tools {
padding: 0 30px 0 30px;
&.background--white {
background: #fff;
height: 100vh;
}
.form-control.col-sm-3 {
width: 33.3333%;
}
label {
position: relative;
top: 7px;
}
.offlinesig-qr {
position: fixed;
top: 68px;
left: 0;
background: #fff;
width: 100%;
height: 100%;
z-index: 100;
.btn {
position: fixed;
bottom: 28px;
right: 10px;
}
}
.iguana-core-toggle {
position: relative;
top: -6px;
left: 18px;
}
}

19
react/src/components/login/login.js

@ -79,6 +79,7 @@ class Login extends React.Component {
this.updateDecryptKey = this.updateDecryptKey.bind(this);
this.loadPinList = this.loadPinList.bind(this);
this.updateSelectedShortcut = this.updateSelectedShortcut.bind(this);
this.setRecieverFromScan = this.setRecieverFromScan.bind(this);
}
// the setInterval handler for 'activeCoins'
@ -93,6 +94,22 @@ class Login extends React.Component {
});
}
setRecieverFromScan(receiver) {
if (receiver) {
this.setState({
loginPassphrase: receiver,
});
} else {
Store.dispatch(
triggerToaster(
'Unable to recognize QR code',
'QR scan Error',
'error'
)
);
}
}
isCustomWalletSeed() {
return this.state.customWalletSeed;
}
@ -125,7 +142,7 @@ class Login extends React.Component {
toggleShouldEncryptSeed() {
this.setState({
shouldEncryptSeed: !this.state.shouldEncryptSeed
shouldEncryptSeed: !this.state.shouldEncryptSeed,
});
}

23
react/src/components/login/login.render.js

@ -2,6 +2,7 @@ import React from 'react';
import { translate } from '../../translate/translate';
import LoginSettingsModal from '../dashboard/loginSettingsModal/loginSettingsModal';
import ZcparamsFetchModal from '../dashboard/zcparamsFetchModal/zcparamsFetchModal';
import QRModal from '../dashboard/qrModal/qrModal';
import Select from 'react-select';
import ReactTooltip from 'react-tooltip';
@ -59,9 +60,9 @@ const LoginRender = function() {
<div className="form-group form-material floating col-sm-12 horizontal-padding-0">
<input
type="password"
className={ !this.state.seedInputVisibility ? 'form-control' : 'hide' }
name="loginPassphrase"
ref="loginPassphrase"
className={ !this.state.seedInputVisibility ? 'form-control' : 'hide' }
onChange={ this.updateLoginPassPhraseInput }
onKeyDown={ (event) => this.handleKeydown(event) }
autoComplete="off"
@ -81,6 +82,11 @@ const LoginRender = function() {
<label
className="floating-label"
htmlFor="inputPassword">{ translate('INDEX.WALLET_SEED') }</label>
<div className="qr-modal-login-block">
<QRModal
mode="scan"
setRecieverFromScan={ this.setRecieverFromScan } />
</div>
</div>
{ this.state.loginPassPhraseSeedType &&
<div
@ -89,7 +95,6 @@ const LoginRender = function() {
<div className="placeholder-label">{ this.state.loginPassPhraseSeedType }</div>
</div>
}
{ this.state.loginPassphrase &&
this.state.enableEncryptSeed &&
<div className="row">
@ -369,7 +374,9 @@ const LoginRender = function() {
<button
className="copy-floating-label"
htmlFor="walletseed"
onClick={ () => this.copyPassPhraseToClipboard() }>{ translate('INDEX.COPY') }</button>
onClick={ () => this.copyPassPhraseToClipboard() }>
{ translate('INDEX.COPY') }
</button>
<span className={ this.state.isCustomSeedWeak ? 'tooltiptext' : 'hide' }>
<strong>{ translate('INDEX.WEAK_SEED') }</strong><br /><br />
{ translate('INDEX.YOUR_SEED_MUST_CONTAIN') }<br />
@ -400,6 +407,16 @@ const LoginRender = function() {
<label
className="floating-label"
htmlFor="rwalletseed">{ translate('INDEX.CONFIRM_SEED') }</label>
<button
type="button"
className="btn btn-success btn-block margin-top-20 btn-generate-qr">
<QRModal
qrSize="256"
modalSize="md"
title="Seed QR recovery"
fileName="agama-seed"
content={ this.state.randomSeed } />
</button>
</div>
<button
type="button"

23
react/src/components/login/login.scss

@ -232,4 +232,27 @@ option.login-option {
left: 15px;
color: #fff;
}
}
.qrcode-modal + .modal {
a {
color: #fff;
}
}
.btn-generate-qr {
.qrcode-modal {
display: block;
}
}
.qr-modal-login-block {
position: absolute;
right: 0;
top: -50px;
.btn {
background: transparent;
color: #fff;
}
}

1
react/src/styles/index.scss

@ -52,6 +52,7 @@
@import '../components/dashboard/loginSettingsModal/loginSettingsModal.scss';
@import '../components/dashboard/zcparamsFetchModal/zcparamsFetchModal.scss';
@import '../components/dashboard/spinner/spinner.scss';
@import '../components/dashboard/tools/tools.scss';
@import '../components/toaster/toaster.scss';
@import '~react-table/react-table.css';
@import '~react-select/dist/react-select.css';

2
react/src/translate/en.js

@ -744,6 +744,8 @@ export const LANG_EN = {
'DOWNLOAD': 'Download',
},
'SEND': {
'SELF': 'Self',
'ALL': 'All',
'BAD_TXN_SPENT_ERR1': 'This particular error can happen in the following cases:',
'BAD_TXN_SPENT_ERR2': 'Your machine\'s clock is not set properly',
'BAD_TXN_SPENT_ERR3': 'SPV server is temporary out of sync. Try to switch over to another one.',

Loading…
Cancel
Save