Browse Source

notary nodes elections

v0.25
pbca26 7 years ago
parent
commit
9211094ee7
  1. 11
      react/src/actions/actionCreators.js
  2. 168
      react/src/actions/actions/elections.js
  3. 1
      react/src/actions/storeType.js
  4. BIN
      react/src/assets/images/world-map.png
  5. 2
      react/src/components/dashboard/coinTile/coinTileItem.js
  6. 6
      react/src/components/dashboard/navbar/navbar.js
  7. 7
      react/src/components/dashboard/navbar/navbar.render.js
  8. 543
      react/src/components/dashboard/notaryElectionsModal/notaryElectionsModal.js
  9. 142
      react/src/components/dashboard/notaryElectionsModal/notaryElectionsModal.scss
  10. 2
      react/src/components/dashboard/tools/toolsSplitUtxo.js
  11. 16
      react/src/components/login/login.js
  12. 8
      react/src/components/login/login.render.js
  13. 6
      react/src/components/main/walletMain.js
  14. 9
      react/src/reducers/main.js
  15. 1
      react/src/styles/index.scss

11
react/src/actions/actionCreators.js

@ -30,6 +30,7 @@ import {
ELECTRUM_SERVER_CHANGED,
DISPLAY_ZCASH_PARAMS_FETCH,
DASHBOARD_REMOVE_COIN,
DISPLAY_NOTARY_ELECTIONS_MODAL,
} from './storeType';
export * from './actions/nativeSyncInfo';
@ -52,6 +53,7 @@ export * from './actions/mm';
export * from './actions/nativeNetwork';
export * from './actions/tools';
export * from './actions/prices';
export * from './actions/elections';
export function changeActiveAddress(address) {
return {
@ -201,7 +203,7 @@ export function toggleAddcoinModal(display, isLogin) {
export function dismissToasterMessage(toastId) {
return dispatch => {
dispatch(dismissToaster(toastId))
dispatch(dismissToaster(toastId));
}
}
@ -299,4 +301,11 @@ export function dashboardRemoveCoin(coin) {
type: DASHBOARD_REMOVE_COIN,
coin,
}
}
export function toggleNotaryElectionsModal(display) {
return {
type: DISPLAY_NOTARY_ELECTIONS_MODAL,
displayNotaryElectionsModal: display,
}
}

168
react/src/actions/actions/elections.js

@ -0,0 +1,168 @@
import { translate } from '../../translate/translate';
import Config from '../../config';
import { triggerToaster } from '../actionCreators';
import Store from '../../store';
export function shepherdElectionsBalance(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(
'shepherdElectionsBalance',
'Error',
'error'
)
);
})
.then(response => response.json())
.then(json => {
resolve(!json.result ? 'error' : json);
});
});
}
export function shepherdElectionsTransactions(coin, address, type) {
return new Promise((resolve, reject) => {
fetch(`http://127.0.0.1:${Config.agamaPort}/shepherd/elections/listtransactions?coin=${coin}&address=${address}&full=true&type=${type}&maxlength=20&token=${Config.token}`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
})
.catch((error) => {
console.log(error);
dispatch(
triggerToaster(
'shepherdElectionsTransactions',
'Error',
'error'
)
);
})
.then(response => response.json())
.then(json => {
resolve(!json.result ? 'error' : json);
});
});
}
export function shepherdElectionsStatus() {
return new Promise((resolve, reject) => {
return fetch(`http://127.0.0.1:${Config.agamaPort}/shepherd/elections/status`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
token: Config.token,
}),
})
.catch((error) => {
console.log(error);
Store.dispatch(
triggerToaster(
'shepherdElectionsStatus',
'Error',
'error'
)
);
})
.then(response => response.json())
.then(json => {
resolve(json);
});
});
}
export function shepherdElectionsLogin(seed, network) {
return new Promise((resolve, reject) => {
return fetch(`http://127.0.0.1:${Config.agamaPort}/shepherd/elections/login`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
seed,
network,
iguana: true,
token: Config.token,
}),
})
.catch((error) => {
console.log(error);
Store.dispatch(
triggerToaster(
'shepherdElectionsLogin',
'Error',
'error'
)
);
})
.then(response => response.json())
.then(json => {
resolve(json);
});
});
}
export function shepherdElectionsLogout() {
return new Promise((resolve, reject) => {
return fetch(`http://127.0.0.1:${Config.agamaPort}/shepherd/elections/logout`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
token: Config.token,
}),
})
.catch((error) => {
console.log(error);
Store.dispatch(
triggerToaster(
'shepherdElectionsLogout',
'Error',
'error'
)
);
})
.then(response => response.json())
.then(json => {
resolve(json);
});
});
}
export function shepherdElectionsSend(coin, value, sendToAddress, changeAddress, opreturn) {
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}&vote=true&push=true&verify=false&opreturn=${opreturn}&token=${Config.token}`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
})
.catch((error) => {
console.log(error);
Store.dispatch(
triggerToaster(
'shepherdElectionsSend',
'Error',
'error'
)
);
})
.then(response => response.json())
.then(json => {
resolve(json);
});
});
}

1
react/src/actions/storeType.js

@ -51,6 +51,7 @@ export const DASHBOARD_REMOVE_COIN = 'DASHBOARD_REMOVE_COIN';
export const DASHBOARD_ACTIVE_COIN_NET_PEERS = 'DASHBOARD_ACTIVE_COIN_NET_PEERS';
export const DASHBOARD_ACTIVE_COIN_NET_TOTALS = 'DASHBOARD_ACTIVE_COIN_NET_TOTALS';
export const PRICES = 'PRICES';
export const DISPLAY_NOTARY_ELECTIONS_MODAL = 'DISPLAY_NOTARY_ELECTIONS_MODAL';
/* dex */
export const DEX_LOGIN = 'DEX_LOGIN';

BIN
react/src/assets/images/world-map.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

2
react/src/components/dashboard/coinTile/coinTileItem.js

@ -279,7 +279,7 @@ class CoinTileItem extends React.Component {
)
);
}
} else if (mode === 'spv' && this.props.Dashboard.electrumCoins[coin].pub) {
} else if (mode === 'spv' && this.props.Dashboard.electrumCoins && this.props.Dashboard.electrumCoins[coin] && this.props.Dashboard.electrumCoins[coin].pub) {
Store.dispatch(shepherdElectrumBalance(coin, this.props.Dashboard.electrumCoins[coin].pub));
if (this.props.ActiveCoin.activeSection === 'default') {

6
react/src/components/dashboard/navbar/navbar.js

@ -12,6 +12,7 @@ import {
activeHandle,
dashboardRemoveCoin,
dashboardChangeActiveCoin,
toggleNotaryElectionsModal,
} from '../../../actions/actionCreators';
import Store from '../../../store';
import Config from '../../../config';
@ -29,6 +30,7 @@ class Navbar extends React.Component {
};
this.openDropMenu = this.openDropMenu.bind(this);
this.handleClickOutside = this.handleClickOutside.bind(this);
this._toggleNotaryElectionsModal = this._toggleNotaryElectionsModal.bind(this);
this._checkAC = this._checkAC.bind(this);
this.spvLock = this.spvLock.bind(this);
this.spvLogout = this.spvLogout.bind(this);
@ -122,6 +124,10 @@ class Navbar extends React.Component {
}));
}
_toggleNotaryElectionsModal() {
Store.dispatch(toggleNotaryElectionsModal(true));
}
toggleAddCoinModal() {
Store.dispatch(toggleAddcoinModal(true, false));
}

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

@ -89,6 +89,13 @@ const NavbarRender = function() {
</a>
</li>
}
{ mainWindow.nnVoteChain &&
<li className="nav-top-menu">
<a onClick={ this._toggleNotaryElectionsModal }>
<i className="site-menu-icon"></i> Notary Elections
</a>
</li>
}
{ !navigator.onLine &&
<li
className="nav-top-menu offline"

543
react/src/components/dashboard/notaryElectionsModal/notaryElectionsModal.js

@ -0,0 +1,543 @@
import React from 'react';
import { connect } from 'react-redux';
import { translate } from '../../../translate/translate';
import {
toggleNotaryElectionsModal,
shepherdElectionsSend,
shepherdElectionsLogout,
shepherdElectionsLogin,
shepherdElectionsStatus,
triggerToaster,
shepherdElectionsBalance,
shepherdElectionsTransactions,
} from '../../../actions/actionCreators';
import Store from '../../../store';
import { secondsToString } from '../../../util/time';
import { isPositiveNumber } from '../../../util/number';
import mainWindow from '../../../util/mainWindow';
import Spinner from '../spinner/spinner';
const ELECTIONS_SYNC_UPDATE_INTERVAL = 120000; // every 2 min
class NotaryElectionsModal extends React.Component {
constructor(props) {
super(props);
this.state = {
loginPassphrase: '',
seedInputVisibility: false,
userType: 'voter',
region: null,
trimPassphraseTimer: null,
isAuth: false,
balance: 0,
transactions: null,
coin: mainWindow.nnVoteChain,
pub: null,
amount: 0,
address: '',
};
this.defaultState = JSON.parse(JSON.stringify(this.state));
this.closeModal = this.closeModal.bind(this);
this.toggleSeedInputVisibility = this.toggleSeedInputVisibility.bind(this);
this.resizeLoginTextarea = this.resizeLoginTextarea.bind(this);
this.updateLoginPassPhraseInput = this.updateLoginPassPhraseInput.bind(this);
this.setUserType = this.setUserType.bind(this);
this.setRegion = this.setRegion.bind(this);
this.loginSeed = this.loginSeed.bind(this);
this.logout = this.logout.bind(this);
this.sync = this.sync.bind(this);
this.send = this.send.bind(this);
this.updateInput = this.updateInput.bind(this);
this.sendValidate = this.sendValidate.bind(this);
this.electionsDataInterval = null;
}
sendValidate() {
let valid = true;
const _amount = this.state.amount;
const _balance = this.state.balance;
const _fee = 0.0001;
if (Number(_amount) + _fee > _balance) {
Store.dispatch(
triggerToaster(
`${translate('SEND.INSUFFICIENT_FUNDS')} ${translate('SEND.MAX_AVAIL_BALANCE')} ${Number(_balance - _fee)} ${this.state.coin}`,
translate('TOASTR.WALLET_NOTIFICATION'),
'error'
)
);
valid = false;
} else if (Number(_amount) < _fee) {
Store.dispatch(
triggerToaster(
`${translate('SEND.AMOUNT_IS_TOO_SMALL', this.state.amount)}, ${translate('SEND.MIN_AMOUNT_IS', this.state.coin)} ${_fee}`,
translate('TOASTR.WALLET_NOTIFICATION'),
'error'
)
);
valid = false;
}
if (!this.state.address ||
this.state.address.length < 34) {
Store.dispatch(
triggerToaster(
'Wrong address format',
translate('TOASTR.WALLET_NOTIFICATION'),
'error'
)
);
valid = false;
}
if (!isPositiveNumber(this.state.amount)) {
Store.dispatch(
triggerToaster(
translate('SEND.AMOUNT_POSITIVE_NUMBER'),
translate('TOASTR.WALLET_NOTIFICATION'),
'error'
)
);
valid = false;
}
return valid;
}
send() {
if (this.sendValidate()) {
shepherdElectionsSend(
this.state.coin,
(this.state.amount * 100000000) - 10000,
this.state.address,
this.state.pub,
'ne2k18-' + this.state.region,
)
.then((res) => {
if (res.msg === 'success') {
Store.dispatch(
triggerToaster(
`You succesfully voted ${this.state.amount} for ${this.state.address}`,
'Notary voting 2018',
'success',
false
)
);
let _transactions = this.state.transactions;
_transactions.unshift({
address: this.state.address,
amount: this.state.amount - 0.0001,
region: 'ne2k18-' + this.state.region,
timestamp: Math.floor(Date.now() / 1000),
});
this.setState({
transactions: _transactions,
});
} else {
Store.dispatch(
triggerToaster(
res.result.txid || res.result,
'Notary voting 2018',
'error'
)
);
}
});
}
}
sync() {
shepherdElectionsStatus()
.then((res) => {
if (res.result !== 'unauth') {
shepherdElectionsBalance(this.state.coin, res.result)
.then((res) => {
if (res.msg === 'success') {
this.setState({
balance: res.result.balance,
});
}
});
shepherdElectionsTransactions(this.state.coin, res.result, this.state.userType)
.then((res) => {
this.setState({
transactions: res.result,
});
});
this.setState({
isAuth: true,
pub: res.result,
});
}
});
}
componentWillReceiveProps(props) {
if (props &&
props.Main &&
props.Main.displayNotaryElectionsModal &&
!this.electionsDataInterval) {
this.sync();
this.electionsDataInterval = setInterval(() => {
this.sync();
}, ELECTIONS_SYNC_UPDATE_INTERVAL);
} else {
clearInterval(this.electionsDataInterval);
this.electionsDataInterval = null;
}
}
updateInput(e) {
this.setState({
[e.target.name]: e.target.value,
});
}
handleKeydown(e) {
this.updateLoginPassPhraseInput(e);
if (e.key === 'Enter') {
this.loginSeed();
}
}
setRegion(region) {
this.setState({
region,
});
}
setUserType(type) {
this.setState({
userType: type,
});
}
toggleSeedInputVisibility() {
this.setState({
seedInputVisibility: !this.state.seedInputVisibility,
});
this.resizeLoginTextarea();
}
resizeLoginTextarea() {
// auto-size textarea
setTimeout(() => {
if (this.state.seedInputVisibility) {
document.querySelector('#loginPassphrase').style.height = '1px';
document.querySelector('#loginPassphrase').style.height = `${(15 + document.querySelector('#loginPassphrase').scrollHeight)}px`;
}
}, 100);
}
updateLoginPassPhraseInput(e) {
// remove any empty chars from the start/end of the string
const newValue = e.target.value;
clearTimeout(this.state.trimPassphraseTimer);
const _trimPassphraseTimer = setTimeout(() => {
this.setState({
loginPassphrase: newValue ? newValue.trim() : '', // hardcoded field name
});
}, 2000);
this.resizeLoginTextarea();
this.setState({
trimPassphraseTimer: _trimPassphraseTimer,
[e.target.name === 'loginPassphraseTextarea' ? 'loginPassphrase' : e.target.name]: newValue,
});
}
loginSeed() {
shepherdElectionsLogin(this.state.loginPassphrase, this.state.coin)
.then((res) => {
if (res.msg === 'success') {
this.setState({
isAuth: true,
pub: res.result,
});
} else {
Store.dispatch(
triggerToaster(
'loginSeed',
'Error',
'error'
)
);
}
});
this.setState({
loginPassphrase: '',
loginPassPhraseSeedType: null,
});
// reset login input vals
this.refs.loginPassphrase.value = '';
this.refs.loginPassphraseTextarea.value = '';
this.sync();
}
logout() {
shepherdElectionsLogout()
.then((res) => {
this.setState(this.defaultState);
});
}
closeModal() {
clearInterval(this.electionsDataInterval);
this.electionsDataInterval = null;
Store.dispatch(toggleNotaryElectionsModal(false));
}
displayTxHistoryRender() {
if (this.state.transactions &&
this.state.transactions.length) {
return true;
}
}
renderHistoryRegion(region) {
let _region;
switch (region) {
case 'ne2k18-sh':
_region = 'SH';
break;
case 'ne2k18-na':
_region = 'NA';
break;
case 'ne2k18-ae':
_region = 'AE';
break;
case 'ne2k18-eu':
_region = 'EU';
break;
}
return _region;
}
renderHistory() {
const _history = this.state.transactions;
let _items = [];
for (let i = 0; i < _history.length; i++) {
_items.push(
<tr key={ `notary-elections-history-${i}` }>
<td>{ _history[i].address }</td>
<td>{ Number(_history[i].amount) }</td>
<td>{ this.renderHistoryRegion(_history[i].region) }</td>
<td>{ secondsToString(_history[i].timestamp) }</td>
</tr>
);
}
return (
<table className="table table-hover dataTable table-striped">
<thead>
<tr>
<th>{ this.state.userType === 'voter' ? 'To' : 'From' }</th>
<th>{ translate('INDEX.AMOUNT') }</th>
<th>Region</th>
<th>Time</th>
</tr>
</thead>
<tbody>
{ _items }
</tbody>
<tfoot>
<tr>
<th>{ this.state.userType === 'voter' ? 'To' : 'From' }</th>
<th>{ translate('INDEX.AMOUNT') }</th>
<th>Region</th>
<th>Time</th>
</tr>
</tfoot>
</table>
);
}
render() {
if (this.props &&
this.props.Main &&
this.props.Main.displayNotaryElectionsModal) {
return (
<div>
<div className="modal show notary-elections-modal ff">
<div
onClick={ this.closeModal }
className="modal-close-overlay"></div>
<div className="modal-dialog modal-center modal-lg">
<div
onClick={ this.closeModal }
className="modal-close-overlay"></div>
<div className="modal-content">
<div className="modal-body modal-body-container">
<div className="modal-resizable">
<div className="elections-title-bar">
<img src={ `assets/images/native/kmd_header_title_logo.png` } />
<div className="elections-title">Notary Elections 2018</div>
</div>
{ this.state.isAuth &&
<button
onClick={ this.logout }
className="btn btn-md btn-info btn-block ladda-button elections-logout-btn">
<i className="fa fa-power-off margin-right-5"></i>Logout
</button>
}
<div className="elections-user-type">
<a
className={ this.state.userType === 'voter' ? 'active' : '' }
onClick={ () => this.setUserType('voter') }><i className="fa fa-file margin-right-10"></i>I'm a voter</a>
<span className="margin-left-30 margin-right-30">|</span>
<a
className={ this.state.userType === 'candidate' ? 'active' : '' }
onClick={ () => this.setUserType('candidate') }><i className="fa fa-user margin-right-10"></i>I'm a candidate</a>
</div>
{ !this.state.isAuth &&
<div className="elections-login">
<label
className="floating-label"
htmlFor="inputPassword">{ translate('INDEX.WALLET_SEED') }</label>
<input
type="password"
name="loginPassphrase"
ref="loginPassphrase"
className={ !this.state.seedInputVisibility ? 'form-control' : 'hide' }
onChange={ this.updateLoginPassPhraseInput }
onKeyDown={ (event) => this.handleKeydown(event) }
autoComplete="off"
value={ this.state.loginPassphrase || '' } />
<textarea
className={ this.state.seedInputVisibility ? 'form-control' : 'hide' }
id="loginPassphrase"
ref="loginPassphraseTextarea"
name="loginPassphraseTextarea"
autoComplete="off"
onChange={ this.updateLoginPassPhraseInput }
onKeyDown={ (event) => this.handleKeydown(event) }
value={ this.state.loginPassphrase || '' }></textarea>
<i
className={ 'seed-toggle fa fa-eye' + (!this.state.seedInputVisibility ? '-slash' : '') }
onClick={ this.toggleSeedInputVisibility }></i>
<button
onClick={ this.loginSeed }
disabled={ !this.state.loginPassphrase || !this.state.loginPassphrase.length }
className="btn btn-md btn-primary btn-block ladda-button elections-login-btn">
Login
</button>
</div>
}
{ this.state.isAuth &&
!this.state.transactions &&
!this.state.balance &&
<Spinner />
}
{ this.state.isAuth &&
this.state.userType === 'voter' &&
<div className="elections-voter-ui">
<div className="elections-map">
<img src={ `assets/images/world-map.png` } />
<div className={ 'elections-map-node elections-map-node--na' + (this.state.region === 'na' ? ' active' : '') }>
<label className="notary-elections-node-title">NA</label>
<img
onClick={ () => this.setRegion('na') }
src={ `assets/images/cryptologo/kmd.png` } />
</div>
<div className={ 'elections-map-node elections-map-node--sh' + (this.state.region === 'sh' ? ' active' : '') }>
<label className="notary-elections-node-title">SH</label>
<img
onClick={ () => this.setRegion('sh') }
src={ `assets/images/cryptologo/kmd.png` } />
</div>
<div className={ 'elections-map-node elections-map-node--ae' + (this.state.region === 'ae' ? ' active' : '') }>
<label className="notary-elections-node-title">AE</label>
<img
onClick={ () => this.setRegion('ae') }
src={ `assets/images/cryptologo/kmd.png` } />
</div>
<div className={ 'elections-map-node elections-map-node--eu' + (this.state.region === 'eu' ? ' active' : '') }>
<label className="notary-elections-node-title">EU</label>
<img
onClick={ () => this.setRegion('eu') }
src={ `assets/images/cryptologo/kmd.png` } />
</div>
</div>
</div>
}
{ this.state.isAuth &&
<div className={ `elections-balance` + (this.state.userType === 'candidate' ? ' margin-top-25' : '') }>
You have <strong>{ this.state.balance }</strong> VOTE
</div>
}
{ this.state.isAuth &&
this.state.userType === 'voter' &&
<div className="elections-send margin-top-10">
<input
type="text"
className="form-control block margin-bottom-10"
name="address"
value={ this.state.address !== 0 ? this.state.address : '' }
onChange={ this.updateInput }
placeholder="Enter an address"
autoComplete="off" />
<input
type="text"
className="form-control inline"
name="amount"
value={ this.state.amount !== 0 ? this.state.amount : '' }
onChange={ this.updateInput }
placeholder="Enter an amout, e.g. 1"
autoComplete="off" />
<button
onClick={ this.send }
disabled={ !this.state.amount || !this.state.address || !this.state.address.length || !this.state.region || this.state.address === this.state.pub }
className="btn btn-md btn-primary btn-block ladda-button elections-login-btn">
Vote
</button>
</div>
}
{ this.displayTxHistoryRender() &&
<div>
<div className={ `elections-history` + (this.state.userType === 'voter' ? ' margin-top-20' : '') }>
History
</div>
{ this.renderHistory() }
</div>
}
</div>
</div>
<div className="modal-footer">
<button
type="button"
className="btn btn-default"
onClick={ this.closeModal }>{ translate('INDEX.CLOSE') }</button>
</div>
</div>
</div>
</div>
<div className="modal-backdrop show in"></div>
</div>
);
} else {
return null;
}
}
}
const mapStateToProps = (state) => {
return {
Main: state.Main,
};
};
export default connect(mapStateToProps)(NotaryElectionsModal);

142
react/src/components/dashboard/notaryElectionsModal/notaryElectionsModal.scss

@ -0,0 +1,142 @@
.notary-elections-modal {
color: #757575 !important;
label,
input {
color: #757575 !important;
}
.loader {
float: none;
}
.modal-body-container {
height: inherit;
max-height: 90vh;
overflow-y: auto;
}
.elections-title {
font-size: 18px;
margin-left: 83px;
}
.elections-user-type {
text-align: center;
margin-top: 40px;
a {
font-size: 18px;
color: #2f6363;
cursor: pointer;
&.active {
text-decoration: underline;
}
}
}
.elections-logout-btn {
width: 100px;
position: absolute;
top: 20px;
right: 20px;
}
.elections-login {
margin-top: 15px;
}
.elections-login-btn {
width: 100px;
margin-top: 10px;
}
.seed-toggle {
right: 20px;
margin-top: -30px;
top: inherit;
}
.elections-map {
text-align: center;
}
.elections-map-node {
position: absolute;
.notary-elections-node-title {
color: #fff !important;
font-weight: bold;
position: absolute;
margin-left: 40px;
margin-top: -6px;
}
img {
width: 35px;
background: #fff;
border-radius: 50%;
cursor: pointer;
border: none;
box-shadow: 1px 2px 2px rgba(0, 0, 0, 0.2);
transition: width 0.2s ease-in-out;
}
&.active {
img {
cursor: default;
width: 38px !important;
border: solid 5px #fff;
}
}
&:hover {
img {
width: 40px;
}
}
&.elections-map-node--na {
left: 210px;
top: 258px;
}
&.elections-map-node--sh {
left: 420px;
top: 344px;
}
&.elections-map-node--ae {
left: 560px;
top: 284px;
}
&.elections-map-node--eu {
left: 440px;
top: 250px;
}
}
.elections-balance,
.elections-history {
font-size: 16px;
}
.elections-history {
margin-bottom: 10px;
text-align: center;
}
.elections-send {
.form-control {
display: inline-block;
width: 250px;
}
.btn {
position: relative;
top: -6px;
left: 15px;
display: inline-block;
}
.block {
width: 365px;
display: block;
}
}
}

2
react/src/components/dashboard/tools/toolsSplitUtxo.js

@ -121,7 +121,7 @@ class ToolsSplitUTXO extends React.Component {
for (let i = 0; i < pairsCount; i++) {
for (let j = 0; j < targetSizes.length; j++) {
devlog(`vout ${_targets.length} ${targetSizes[j]}`);
_targets.push(Number(targetSizes[j]) * 100000000);
_targets.push(parseInt(Number(targetSizes[j]) * 100000000));
totalOutSize += Number(targetSizes[j]);
}
}

16
react/src/components/login/login.js

@ -11,6 +11,7 @@ import {
stopInterval,
dashboardChangeActiveCoin,
toggleZcparamsFetchModal,
toggleNotaryElectionsModal,
activeHandle,
} from '../../actions/actionCreators';
import Config from '../../config';
@ -83,6 +84,13 @@ class Login extends React.Component {
this.setRecieverFromScan = this.setRecieverFromScan.bind(this);
}
_toggleNotaryElectionsModal() {
this.setState({
displayLoginSettingsDropdown: false,
});
Store.dispatch(toggleNotaryElectionsModal(true));
}
// the setInterval handler for 'activeCoins'
_iguanaActiveCoins = null;
@ -337,12 +345,8 @@ class Login extends React.Component {
if (this.state.selectedPin) {
Store.dispatch(loginWithPin(this.state.decryptKey, this.state.selectedPin));
} else {
Store.dispatch(
shepherdElectrumAuth(this.state.loginPassphrase)
);
Store.dispatch(
shepherdElectrumCoins()
);
Store.dispatch(shepherdElectrumAuth(this.state.loginPassphrase));
Store.dispatch(shepherdElectrumCoins());
}
}

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

@ -5,6 +5,7 @@ import ZcparamsFetchModal from '../dashboard/zcparamsFetchModal/zcparamsFetchMod
import QRModal from '../dashboard/qrModal/qrModal';
import Select from 'react-select';
import ReactTooltip from 'react-tooltip';
import mainWindow from '../../util/mainWindow';
const LoginRender = function() {
return (
@ -45,6 +46,13 @@ const LoginRender = function() {
<i className="icon fa-users"></i> { translate('ABOUT.ABOUT_AGAMA') }
</a>
</li>
{ mainWindow.nnVoteChain &&
<li>
<a onClick={ () => this._toggleNotaryElectionsModal() }>
<i className="icon fa-thumbs-up"></i> Notary Elections 2018
</a>
</li>
}
</ul>
</div>
</div>

6
react/src/components/main/walletMain.js

@ -5,6 +5,7 @@ import AddCoin from '../addcoin/addcoin';
import Login from '../login/login';
import Dashboard from '../dashboard/main/dashboard';
import DexMain from '../dex/dexMain';
import NotaryElectionsModal from '../dashboard/notaryElectionsModal/notaryElectionsModal';
import mainWindow from '../../util/mainWindow';
import Store from '../../store';
import {
@ -14,6 +15,7 @@ import {
toggleClaimInterestModal,
toggleCoindDownModal,
displayImportKeyModal,
toggleNotaryElectionsModal,
} from '../../actions/actionCreators';
class WalletMain extends React.Component {
@ -46,6 +48,8 @@ class WalletMain extends React.Component {
Store.dispatch(toggleAddcoinModal(false, false));
} else if (this.props.activeModals.displayLoginSettingsModal) {
Store.dispatch(toggleLoginSettingsModal(false));
} else if (this.props.activeModals.displayNotaryElectionsModal) {
Store.dispatch(toggleNotaryElectionsModal(false));
}
}
};
@ -70,6 +74,7 @@ class WalletMain extends React.Component {
<Dashboard />
<AddCoin />
<Login />
<NotaryElectionsModal />
<Toaster {...this.props.toaster} />
</div>
);
@ -88,6 +93,7 @@ const mapStateToProps = (state) => {
displayZcparamsModal: state.Dashboard.displayZcparamsModal,
displayAddCoinModal: state.AddCoin.display,
displayLoginSettingsModal: state.Main.displayLoginSettingsModal,
displayNotaryElectionsModal: state.Main.displayNotaryElectionsModal,
},
};
};

9
react/src/reducers/main.js

@ -2,12 +2,14 @@ import {
GET_ACTIVE_COINS,
LOGIN,
ACTIVE_HANDLE,
DISPLAY_LOGIN_SETTINGS_MODAL
DISPLAY_LOGIN_SETTINGS_MODAL,
DISPLAY_NOTARY_ELECTIONS_MODAL,
} from '../actions/storeType';
export function Main(state = {
isLoggedIn: false,
displayLoginSettingsModal: false,
displayNotaryElectionsModal: false,
total: 0,
}, action) {
switch (action.type) {
@ -33,6 +35,11 @@ export function Main(state = {
...state,
displayLoginSettingsModal: action.displayLoginSettingsModal,
};
case DISPLAY_NOTARY_ELECTIONS_MODAL:
return {
...state,
displayNotaryElectionsModal: action.displayNotaryElectionsModal,
};
default:
return state;
}

1
react/src/styles/index.scss

@ -52,6 +52,7 @@
@import '../components/dashboard/coindDownModal/coindDownModal.scss';
@import '../components/dashboard/loginSettingsModal/loginSettingsModal.scss';
@import '../components/dashboard/zcparamsFetchModal/zcparamsFetchModal.scss';
@import '../components/dashboard/notaryElectionsModal/notaryElectionsModal.scss';
@import '../components/dashboard/spinner/spinner.scss';
@import '../components/dashboard/tools/tools.scss';
@import '../components/toaster/toaster.scss';

Loading…
Cancel
Save