Browse Source

spv wallet ui

v0.25
pbca26 8 years ago
parent
commit
c619d8ba8d
  1. 2
      react/src/actions/actionCreators.js
  2. 113
      react/src/actions/actions/addCoin.js
  3. 148
      react/src/actions/actions/electrum.js
  4. 6
      react/src/actions/storeType.js
  5. 51
      react/src/components/addcoin/addcoin.js
  6. 4
      react/src/components/addcoin/addcoinOptionsAC.js
  7. 4
      react/src/components/addcoin/addcoinOptionsCrypto.js
  8. 46
      react/src/components/addcoin/coin-selectors.render.js
  9. 1
      react/src/components/dashboard/coinTile/coinTile.js
  10. 20
      react/src/components/dashboard/coinTile/coinTileItem.js
  11. 16
      react/src/components/dashboard/main/dashboard.js
  12. 2
      react/src/components/dashboard/main/dashboard.render.js
  13. 4
      react/src/components/dashboard/navbar/navbar.render.js
  14. 14
      react/src/components/dashboard/receiveCoin/receiveCoin.js
  15. 34
      react/src/components/dashboard/receiveCoin/receiveCoin.render.js
  16. 70
      react/src/components/dashboard/sendCoin/sendCoin.js
  17. 6
      react/src/components/dashboard/sendCoin/sendCoin.render.js
  18. 17
      react/src/components/dashboard/walletsBalance/walletsBalance.js
  19. 14
      react/src/components/dashboard/walletsBalance/walletsBalance.render.js
  20. 12
      react/src/components/dashboard/walletsData/walletsData.js
  21. 9
      react/src/components/dashboard/walletsInfo/walletsInfo.js
  22. 356
      react/src/components/dashboard/walletsInfo/walletsInfo.render.js
  23. 6
      react/src/components/dashboard/walletsNative/walletsNative.js
  24. 4
      react/src/components/dashboard/walletsNative/walletsNative.render.js
  25. 17
      react/src/components/dashboard/walletsNav/walletsNav.js
  26. 6
      react/src/components/dashboard/walletsTxInfo/walletsTxInfo.render.js
  27. 428
      react/src/components/login/login.js
  28. 280
      react/src/components/login/login.render.js
  29. 98
      react/src/components/login/login.scss
  30. 24
      react/src/components/login/swall-modal.render.js
  31. 4
      react/src/components/main/main.js
  32. 12
      react/src/reducers/activeCoin.js
  33. 7
      react/src/reducers/dashboard.js
  34. 2
      react/src/reducers/index.js
  35. 18
      react/src/reducers/login.js
  36. 18
      react/src/reducers/main.js
  37. 1
      react/src/translate/en.js
  38. 6
      react/src/util/coinHelper.js
  39. 6
      react/src/util/crypto/passphrasegenerator.js

2
react/src/actions/actionCreators.js

@ -54,6 +54,7 @@ export * from './actions/update';
export * from './actions/jumblr';
export * from './actions/interest';
export * from './actions/nativeDashboardUpdate';
export * from './actions/electrum';
export function changeActiveAddress(address) {
return {
@ -138,6 +139,7 @@ export function dashboardCoinsState(json) {
type: GET_ACTIVE_COINS,
coins: json,
activeCoins: Object.keys(json.native).length ? true : false,
total: json.total,
}
}

113
react/src/actions/actions/addCoin.js

@ -1,3 +1,4 @@
import { ACTIVE_HANDLE } from '../storeType';
import { translate } from '../../translate/translate';
import Config from '../../config';
import {
@ -13,9 +14,107 @@ import {
checkAC
} from '../../components/addcoin/payload';
export function addCoin(coin, mode, syncOnly, port, startupParams) {
function iguanaActiveHandleState(json) {
return {
type: ACTIVE_HANDLE,
isLoggedIn: json.status === 'unlocked' ? true : false,
handle: json,
}
}
export function activeHandle() {
return dispatch => {
dispatch(shepherdGetConfig(coin, '-1', startupParams));
return fetch(`http://127.0.0.1:${Config.agamaPort}/shepherd/auth/status`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
}
})
.catch(function(error) {
console.log(error);
dispatch(
triggerToaster(
'activeHandle',
'Error',
'error'
)
)
})
.then(response => response.json())
.then(json => {
console.log(json);
dispatch(
iguanaActiveHandleState(json)
);
});
}
}
export function shepherdElectrumAuth(seed) {
return dispatch => {
return fetch(`http://127.0.0.1:${Config.agamaPort}/shepherd/electrum/login?seed=${seed}&iguana=true`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
}
})
.catch(function(error) {
console.log(error);
dispatch(
triggerToaster(
'shepherdElectrumAuth',
'Error',
'error'
)
)
})
.then(response => response.json())
.then(json => {
console.warn(json);
dispatch(activeHandle());
});
}
}
export function shepherdElectrumAddCoin(coin) {
return dispatch => {
return fetch(`http://127.0.0.1:${Config.agamaPort}/shepherd/electrum/coins/add?coin=${coin}`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
}
})
.catch(function(error) {
console.log(error);
dispatch(
triggerToaster(
'shepherdElectrumAddCoin',
'Error',
'error'
)
)
})
.then(response => response.json())
.then(json => {
console.log(json);
dispatch(
addCoinResult(coin, '0')
);
});
}
}
export function addCoin(coin, mode, startupParams) {
console.warn(mode);
if (Number(mode) === 0) {
console.warn('spv');
return dispatch => {
dispatch(shepherdElectrumAddCoin(coin));
}
} else {
return dispatch => {
dispatch(shepherdGetConfig(coin, '-1', startupParams));
}
}
}
@ -118,8 +217,8 @@ export function shepherdHerd(coin, mode, path, startupParams) {
'Content-Type': 'application/json',
},
body: JSON.stringify({
'herd': _herd,
'options': herdData,
herd: _herd,
options: herdData,
}),
})
.catch(function(error) {
@ -155,18 +254,22 @@ export function shepherdHerd(coin, mode, path, startupParams) {
export function addCoinResult(coin, mode) {
const modeToValue = {
'0': 'spv',
'-1': 'native',
};
return dispatch => {
dispatch(
triggerToaster(
`${coin} ${translate('TOASTR.STARTED_IN')} ${modeToValue[mode]} ${translate('TOASTR.MODE')}`,
`${coin} ${translate('TOASTR.STARTED_IN')} ${modeToValue[mode].toUpperCase()} ${translate('TOASTR.MODE')}`,
translate('TOASTR.COIN_NOTIFICATION'),
'success'
)
);
dispatch(toggleAddcoinModal(false, false));
if (Number(mode) === 0) {
dispatch(activeHandle());
}
dispatch(getDexCoins());
}
}

148
react/src/actions/actions/electrum.js

@ -0,0 +1,148 @@
import {
DASHBOARD_ELECTRUM_BALANCE,
DASHBOARD_ELECTRUM_TRANSACTIONS,
DASHBOARD_ELECTRUM_COINS,
} from '../storeType';
import { translate } from '../../translate/translate';
import Config from '../../config';
import {
triggerToaster,
sendToAddressState,
} from '../actionCreators';
export function shepherdElectrumBalance(coin, address) {
return dispatch => {
return fetch(`http://127.0.0.1:${Config.agamaPort}/shepherd/electrum/getbalance?coin=${coin}&address=${address}`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
}
})
.catch(function(error) {
console.log(error);
dispatch(
triggerToaster(
'shepherdElectrumBalance',
'Error',
'error'
)
)
})
.then(response => response.json())
.then(json => {
console.warn(json);
dispatch(shepherdElectrumBalanceState(json));
});
}
}
export function shepherdElectrumBalanceState(json) {
return {
type: DASHBOARD_ELECTRUM_BALANCE,
balance: json.result,
}
}
export function shepherdElectrumTransactions(coin, address) {
return dispatch => {
return fetch(`http://127.0.0.1:${Config.agamaPort}/shepherd/electrum/listtransactions?coin=${coin}&address=${address}&full=true`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
}
})
.catch(function(error) {
console.log(error);
dispatch(
triggerToaster(
'shepherdElectrumTransactions',
'Error',
'error'
)
)
})
.then(response => response.json())
.then(json => {
console.warn(json);
dispatch(shepherdElectrumTransactionsState(json));
});
}
}
export function shepherdElectrumTransactionsState(json) {
json = json.result.listtransactions;
if (json &&
json.error) {
json = null;
} else if (!json || !json.length) {
json = 'no data';
}
return {
type: DASHBOARD_ELECTRUM_TRANSACTIONS,
txhistory: json,
}
}
export function shepherdElectrumCoins() {
return dispatch => {
return fetch(`http://127.0.0.1:${Config.agamaPort}/shepherd/electrum/coins`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
}
})
.catch(function(error) {
console.log(error);
dispatch(
triggerToaster(
'shepherdElectrumCoins',
'Error',
'error'
)
)
})
.then(response => response.json())
.then(json => {
console.warn(json);
dispatch(shepherdElectrumCoinsState(json));
});
}
}
export function shepherdElectrumCoinsState(json) {
return {
type: DASHBOARD_ELECTRUM_COINS,
electrumCoins: json.result,
}
}
// value in sats
export function shepherdElectrumSend(coin, value, sendToAddress, changeAddress) {
// console.log(`http://127.0.0.1:${Config.agamaPort}/shepherd/electrum/createrawtx?coin=${coin}&address=${sendToAddress}&value=${value}&change=${changeAddress}&gui=true`);
return dispatch => {
return fetch(`http://127.0.0.1:${Config.agamaPort}/shepherd/electrum/createrawtx?coin=${coin}&address=${sendToAddress}&value=${value}&change=${changeAddress}&gui=true&push=true`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
}
})
.catch(function(error) {
console.log(error);
dispatch(
triggerToaster(
'shepherdElectrumSend',
'Error',
'error'
)
)
})
.then(response => response.json())
.then(json => {
console.warn(json);
dispatch(sendToAddressState(json.result));
});
}
}

6
react/src/actions/storeType.js

@ -40,4 +40,8 @@ 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';
export const DISPLAY_IMPORT_KEY_MODAL = 'DISPLAY_IMPORT_KEY_MODAL';
export const LOGIN = 'LOGIN';
export const DASHBOARD_ELECTRUM_BALANCE = 'DASHBOARD_ELECTRUM_BALANCE';
export const DASHBOARD_ELECTRUM_TRANSACTIONS = 'DASHBOARD_ELECTRUM_TRANSACTIONS';
export const DASHBOARD_ELECTRUM_COINS = 'DASHBOARD_ELECTRUM_COINS';

51
react/src/components/addcoin/addcoin.js

@ -22,6 +22,10 @@ class AddCoin extends React.Component {
coins: [],
defaultCoinState: {
selectedCoin: null,
spvMode: {
disabled: true,
checked: false,
},
nativeMode: {
disabled: true,
checked: false,
@ -39,6 +43,7 @@ class AddCoin extends React.Component {
this.dismiss = this.dismiss.bind(this);
this.addNewItem = this.addNewItem.bind(this);
this.activateAllCoins = this.activateAllCoins.bind(this);
this.toggleActionsMenu = this.toggleActionsMenu.bind(this);
this.saveCoinSelection = this.saveCoinSelection.bind(this);
this.loadCoinSelection = this.loadCoinSelection.bind(this);
}
@ -70,6 +75,18 @@ class AddCoin extends React.Component {
});
}
toggleSyncOnlyMode(index) {
let _coins = this.state.coins;
_coins[index] = Object.assign({}, _coins[index], {
syncOnly: !this.state.coins[index].syncOnly,
});
this.setState(Object.assign({}, this.state, {
coins: _coins,
}));
}
updateDaemonParam(e, index) {
let _coins = this.state.coins;
@ -90,6 +107,16 @@ class AddCoin extends React.Component {
componentWillMount() {
this.addNewItem();
let appConfig;
try {
appConfig = window.require('electron').remote.getCurrentWindow().appConfig;
} catch (e) {}
this.setState({
isExperimentalOn: appConfig.experimentalFeatures,
});
}
componentWillReceiveProps(props) {
@ -115,15 +142,20 @@ class AddCoin extends React.Component {
updateSelectedCoin(e, index) {
const coin = e.target.value.split('|');
const defaultMode = 'native';
const defaultMode = Config.iguanaLessMode ? 'native' : coin[1];
const modeToValue = { // TODO: move to utils
native: -1,
'spv': 0,
'native': -1,
};
let _coins = this.state.coins;
const _value = e.target.value;
_coins[index] = {
[e.target.name]: _value,
spvMode: {
disabled: _value.indexOf('spv') > -1 ? false : true,
checked: defaultMode === 'spv' ? true : false,
},
nativeMode: {
disabled: _value.indexOf('native') > -1 ? false : true,
checked: defaultMode === 'native' ? true : false,
@ -142,6 +174,11 @@ class AddCoin extends React.Component {
_coins[index] = {
selectedCoin: _selectedCoin,
spvMode: {
disabled: _selectedCoin.indexOf('spv') > -1 ? false : true,
checked: _value === '0' ? true : false,
},
nativeMode: {
disabled: _selectedCoin.indexOf('native') > -1 ? false : true,
checked: _value === '-1' ? true : false,
@ -172,12 +209,13 @@ class AddCoin extends React.Component {
if (!_coin.daemonParam) {
Store.dispatch(addCoin(
coin,
_coin.mode,
));
} else {
Store.dispatch(addCoin(
coin,
null,
{ type: _coin.daemonParam } // TODO: custom param value
_coin.mode,
{ type: _coin.daemonParam }
));
}
@ -221,7 +259,6 @@ class AddCoin extends React.Component {
addCoin(
coin,
this.state.coins[0].mode,
this.state.coins[0].syncOnly
)
);
}
@ -236,7 +273,6 @@ class AddCoin extends React.Component {
addCoin(
itemCoin,
_item.mode,
_item.syncOnly
)
);
}
@ -284,6 +320,7 @@ class AddCoin extends React.Component {
isCoinAlreadyAdded(coin) {
const modes = [
'spv',
'native'
];
@ -318,4 +355,4 @@ const mapStateToProps = (state) => {
};
};
export default connect(mapStateToProps)(AddCoin);
export default connect(mapStateToProps)(AddCoin);

4
react/src/components/addcoin/addcoinOptionsAC.js

@ -24,10 +24,12 @@ class AddCoinOptionsAC extends React.Component {
let _items = [];
for (let i = 0; i < _assetChains.length; i++) {
const availableModes = _assetChains[i] === 'revs' ? 'native|spv' : 'native';
_items.push(
<option
key={ _assetChains[i] }
value={ `${_assetChains[i].toUpperCase()}|native` }>{ translate(`ASSETCHAINS.${_assetChains[i].toUpperCase()}`) }</option>
value={ `${_assetChains[i].toUpperCase()}|${availableModes}` }>{ translate(`ASSETCHAINS.${_assetChains[i].toUpperCase()}`) }</option>
);
}

4
react/src/components/addcoin/addcoinOptionsCrypto.js

@ -10,8 +10,8 @@ class AddCoinOptionsCrypto extends React.Component {
render() {
return (
<optgroup label={ translate('ADD_COIN.CRYPTO_CURRENCIES') }>
<option value="KMD|native">Komodo (KMD)</option>
<option value="CHIPS|native">Chips (CHIPS)</option>
<option value="KMD|native|spv">Komodo (KMD)</option>
<option value="CHIPS|native|spv">Chips (CHIPS)</option>
</optgroup>
);
}

46
react/src/components/addcoin/coin-selectors.render.js

@ -13,7 +13,7 @@ const CoinSelectorsRender = function(item, coin, i) {
className={ this.hasMoreThanOneCoin() ? 'col-sm-10' : 'col-sm-8' }
style={{ paddingLeft: !this.hasMoreThanOneCoin() ? '0' : '15px' }}>
<div
className={ this.hasMoreThanOneCoin() ? 'col-sm-6 form-group' : 'form-group' }
className={ this.hasMoreThanOneCoin() && (item.mode === '-1' || item.mode === -1) ? 'col-sm-6 form-group' : 'form-group' }
style={{ paddingLeft: this.hasMoreThanOneCoin() ? '0' : '15px' }}>
<select
className="form-control form-material"
@ -22,12 +22,12 @@ const CoinSelectorsRender = function(item, coin, i) {
onChange={ (event) => this.updateSelectedCoin(event, i) }
autoFocus>
<option>{ translate('INDEX.SELECT') }</option>
<AddCoinOptionsCrypto />
<AddCoinOptionsAC />
<AddCoinOptionsACFiat />
<AddCoinOptionsCrypto appSettings={ this.props.Settings } />
<AddCoinOptionsAC appSettings={ this.props.Settings } />
<AddCoinOptionsACFiat appSettings={ this.props.Settings } />
</select>
</div>
<div className={ this.hasMoreThanOneCoin() ? 'col-sm-6' : 'hide' }>
<div className={ this.hasMoreThanOneCoin() && (item.mode === '-1' || item.mode === -1) ? 'col-sm-6' : 'hide' }>
<div className="toggle-box padding-bottom-10">
<select
className="form-control form-material"
@ -52,7 +52,39 @@ const CoinSelectorsRender = function(item, coin, i) {
</button>
</div>
<div className="col-sm-11 text-center add-coin-modes">
<div className="form-group col-lg-4 col-md-4 col-sm-6 col-xs-6">
<div className="form-group col-lg-4 col-md-4 col-sm-6 col-xs-6 style-addcoin-lbl-mdl-login">
<input
type="radio"
className="to-labelauty labelauty"
name={ `mode-${i}` }
id={ `addcoin_mdl_basilisk_mode_login-${i}` }
disabled={ item.spvMode.disabled }
checked={ item.spvMode.checked } />
<label
htmlFor={ `addcoin_mdl_basilisk_mode_login-${i}` }
onClick={ () => this.updateSelectedMode('0', i) }
style={{ pointerEvents: item.spvMode.disabled ? 'none' : 'all' }}>
<span
className="labelauty-unchecked-image"
style={{ display: item.spvMode.checked ? 'none' : 'inline-block' }}></span>
<span
className="labelauty-unchecked"
style={{ display: item.spvMode.checked ? 'none' : 'inline-block' }}>
{ translate('INDEX.SPV_MODE') }
</span>
<span
className="labelauty-checked-image"
style={{ display: item.spvMode.checked ? 'inline-block' : 'none' }}></span>
<span
className="labelauty-checked"
style={{ display: item.spvMode.checked ? 'inline-block' : 'none' }}>
{ translate('INDEX.SPV_MODE') }
</span>
</label>
</div>
<div
className="form-group col-lg-4 col-md-4 col-sm-6 col-xs-6"
style={{ paddingLeft: '0' }}>
<input
type="radio"
className="to-labelauty labelauty"
@ -91,7 +123,7 @@ const CoinSelectorsRender = function(item, coin, i) {
<i className="fa fa-trash-o"></i>
</button>
</div>
<div className={ !this.hasMoreThanOneCoin() ? 'col-sm-5 padding-bottom-30' : 'hide' }>
<div className={ !this.hasMoreThanOneCoin() && (item.mode === '-1' || item.mode === -1) ? 'col-sm-5 padding-bottom-30' : 'hide' }>
<div className="toggle-box padding-top-3 padding-bottom-10">
<select
className="form-control form-material"

1
react/src/components/dashboard/coinTile/coinTile.js

@ -17,6 +17,7 @@ class CoinTile extends React.Component {
renderTiles() {
const modes = [
'native',
'spv',
];
const allCoins = this.props.allCoins;
let items = [];

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

@ -14,13 +14,17 @@ import {
getKMDBalanceTotal,
getSyncInfoNative,
getDebugLog,
getDashboardUpdate
getDashboardUpdate,
shepherdElectrumBalance,
shepherdElectrumTransactions,
shepherdElectrumCoins,
} from '../../../actions/actionCreators';
import Store from '../../../store';
import Config from '../../../config';
import CoinTileItemRender from './coinTileItem.render';
const SPV_DASHBOARD_UPDATE_TIMEOUT = 60000;
const IGUNA_ACTIVE_HANDLE_TIMEOUT_COIND_NATIVE = 15000;
const COIND_DOWN_MODAL_FETCH_FAILURES_THRESHOLD = 5;
@ -41,6 +45,7 @@ class CoinTileItem extends React.Component {
let _coinMode = {};
const modes = [
'native',
'spv',
];
const allCoins = this.props.Main.coins;
@ -115,6 +120,11 @@ class CoinTileItem extends React.Component {
);
}
}
if (mode === 'spv') {
Store.dispatch(shepherdElectrumBalance(coin, this.props.Dashboard.electrumCoins[coin].pub));
Store.dispatch(shepherdElectrumTransactions(coin, this.props.Dashboard.electrumCoins[coin].pub));
}
}
}
@ -124,7 +134,7 @@ class CoinTileItem extends React.Component {
setTimeout(() => {
this.dispatchCoinActions(coin, mode);
}, 100);
if (mode === 'native') { // faster coin data load if fully synced
if (mode === 'native' || mode === 'spv') { // faster coin data load if fully synced
setTimeout(() => {
this.dispatchCoinActions(coin, mode);
}, 1000);
@ -144,6 +154,12 @@ class CoinTileItem extends React.Component {
this.dispatchCoinActions(coin, mode);
}, IGUNA_ACTIVE_HANDLE_TIMEOUT_COIND_NATIVE);
Store.dispatch(startInterval('sync', _iguanaActiveHandle));
} else if (mode === 'spv') {
const _iguanaActiveHandle = setInterval(() => {
this.dispatchCoinActions(coin, mode);
}, SPV_DASHBOARD_UPDATE_TIMEOUT);
Store.dispatch(startInterval('sync', _iguanaActiveHandle));
}
}

16
react/src/components/dashboard/main/dashboard.js

@ -22,10 +22,22 @@ class Dashboard extends React.Component {
displayDashboard() {
return this.props &&
this.props.Main &&
(this.props.Main &&
this.props.Main.coins &&
this.props.Main.coins.native &&
this.props.Main.coins.native.length;
this.props.Main.coins.native.length &&
!this.props.Main.coins.spv.length) ||
(this.props.Main &&
this.props.Main.coins &&
this.props.Main.coins.spv &&
this.props.Main.coins.spv.length &&
this.props.Main.isLoggedIn) ||
(this.props.Main &&
this.props.Main.coins &&
this.props.Main.coins.native &&
this.props.Main.coins.native.length &&
!this.props.Main.coins.spv.length &&
this.props.Main.isLoggedIn);
}
render() {

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

@ -6,7 +6,7 @@ import EDEX from '../edex/edex';
import WalletsBalance from '../walletsBalance/walletsBalance';
import WalletsProgress from '../walletsProgress/walletsProgress';
import WalletsNav from '../walletsNav/walletsNav';
// import SendCoin from '../sendCoin/sendCoin';
import SendCoin from '../sendCoin/sendCoin';
import WalletsData from '../walletsData/walletsData';
import Jumblr from '../jumblr/jumblr';
import Settings from '../settings/settings';

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

@ -57,14 +57,14 @@ const NavbarRender = function() {
<i className="site-menu-icon"></i> BarterDEX
</a>
</li>
{ this.props.ActiveCoin && (/*this._checkAC() || */this.props.ActiveCoin.coin === 'KMD') &&
{ this.props.ActiveCoin && (/*this._checkAC() || */this.props.ActiveCoin.coin === 'KMD' && this.props.ActiveCoin.mode === 'native') &&
<li className={ this.isSectionActive('jumblr') ? 'active nav-top-menu' : 'nav-top-menu' }>
<a onClick={ () => this.dashboardChangeSection('jumblr') }>
<i className="site-menu-icon"></i> Jumblr
</a>
</li>
}
{ this.props.ActiveCoin.coin !== 'CHIPS' &&
{ this.props.ActiveCoin.coin !== 'CHIPS' && this.props.ActiveCoin.mode !== 'spv' &&
<li className="nav-top-menu">
<a onClick={ this.openImportKeyModal }>
<i className="site-menu-icon"></i> { translate('IMPORT_KEY.IMPORT_KEY') }

14
react/src/components/dashboard/receiveCoin/receiveCoin.js

@ -126,7 +126,18 @@ class ReceiveCoin extends React.Component {
return items;
} else {
return null;
if (this.props.electrumCoins &&
type === 'public') {
let items = [];
items.push(
AddressItemRender.call(this, { address: this.props.electrumCoins[this.props.coin].pub, amount: this.props.balance.balance }, 'public')
);
return items;
} else {
return null;
}
}
}
@ -152,6 +163,7 @@ const mapStateToProps = (state, props) => {
activeSection: state.ActiveCoin.activeSection,
activeAddress: state.ActiveCoin.activeAddress,
addresses: state.ActiveCoin.addresses,
electrumCoins: state.Dashboard.electrumCoins,
};
if (props &&

34
react/src/components/dashboard/receiveCoin/receiveCoin.render.js

@ -35,20 +35,24 @@ export const _ReceiveCoinTableRender = function() {
<span>
{ this.checkTotalBalance() !== 0 &&
<div className="text-left padding-top-10 padding-bottom-10">
<label className="switch">
<input
type="checkbox"
value="on"
checked={ this.state.hideZeroAddresses } />
<div
className="slider"
onClick={ this.toggleVisibleAddress }></div>
</label>
<div
className="toggle-label margin-right-15 pointer"
onClick={ this.toggleVisibleAddress }>
{ translate('INDEX.TOGGLE_ZERO_ADDRESSES') }
</div>
{ this.props.mode !== 'spv' &&
<div>
<label className="switch">
<input
type="checkbox"
value="on"
checked={ this.state.hideZeroAddresses } />
<div
className="slider"
onClick={ this.toggleVisibleAddress }></div>
</label>
<div
className="toggle-label margin-right-15 pointer"
onClick={ this.toggleVisibleAddress }>
{ translate('INDEX.TOGGLE_ZERO_ADDRESSES') }
</div>
</div>
}
</div>
}
<table className="table table-hover dataTable table-striped">
@ -91,6 +95,7 @@ export const ReceiveCoinRender = function() {
<header className="panel-heading">
<div className="panel-actions">
<InvoiceModal />
{ this.props.mode !== 'spv' &&
<div
className={ 'dropdown' + (this.state.openDropMenu ? ' open' : '') }
onClick={ this.openDropMenu }>
@ -111,6 +116,7 @@ export const ReceiveCoinRender = function() {
</li>
</ul>
</div>
}
</div>
<h4 className="panel-title">{ translate('INDEX.RECEIVING_ADDRESS') }</h4>
</header>

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

@ -7,7 +7,8 @@ import {
triggerToaster,
sendNativeTx,
getKMDOPID,
clearLastSendToResponseState
clearLastSendToResponseState,
shepherdElectrumSend
} from '../../../actions/actionCreators';
import Store from '../../../store';
import {
@ -224,7 +225,7 @@ class SendCoin extends React.Component {
);
} else {
return (
<span>{ translate('INDEX.T_FUNDS') }</span>
<span>{ this.props.ActiveCoin.mode === 'spv' ? `[ ${this.props.ActiveCoin.balance.balance} ${this.props.ActiveCoin.coin} ] ${this.props.Dashboard.electrumCoins[this.props.ActiveCoin.coin].pub}` : translate('INDEX.T_FUNDS') }</span>
);
}
}
@ -388,22 +389,38 @@ class SendCoin extends React.Component {
return;
}
Store.dispatch(
sendNativeTx(
this.props.ActiveCoin.coin,
this.state
)
);
if (this.props.ActiveCoin.mode === 'native') {
Store.dispatch(
sendNativeTx(
this.props.ActiveCoin.coin,
this.state
)
);
if (this.state.addressType === 'private') {
setTimeout(() => {
if (this.state.addressType === 'private') {
setTimeout(() => {
Store.dispatch(
getKMDOPID(
null,
this.props.ActiveCoin.coin
)
);
}, 1000);
}
} else if (this.props.ActiveCoin.mode === 'spv') {
console.warn('spv send');
console.warn(this.props.Dashboard.electrumCoins[this.props.ActiveCoin.coin].pub);
// no op
if (this.props.Dashboard.electrumCoins[this.props.ActiveCoin.coin].pub) {
Store.dispatch(
getKMDOPID(
null,
this.props.ActiveCoin.coin
shepherdElectrumSend(
this.props.ActiveCoin.coin,
this.state.amount * 100000000,
this.state.sendTo,
this.props.Dashboard.electrumCoins[this.props.ActiveCoin.coin].pub
)
);
}, 1000);
}
}
}
@ -411,6 +428,30 @@ class SendCoin extends React.Component {
validateSendFormData() {
let valid = true;
if (this.props.ActiveCoin.mode === 'spv') {
const _amount = this.state.amount;
const _amountSats = this.state.amount * 100000000;
const _balanceSats = this.props.ActiveCoin.balance.sats;
const _fee = this.props.Dashboard.electrumCoins[this.props.ActiveCoin.coin].txfee;
if (_amountSats > (Number(_balanceSats) + Number(_fee))) {
Store.dispatch(
triggerToaster(
translate('SEND.INSUFFICIENT_FUNDS'),
translate('TOASTR.WALLET_NOTIFICATION'),
'error'
)
);
valid = false;
}
console.warn('send val ' + this.state.amount);
console.warn('send val sats ' + (this.state.amount * 100000000));
console.warn(this.props.ActiveCoin.balance.sats);
console.warn(this.props.Dashboard.electrumCoins[this.props.ActiveCoin.coin].txfee);
}
if (!this.state.sendTo ||
this.state.sendTo.length < 34) {
Store.dispatch(
@ -504,6 +545,7 @@ const mapStateToProps = (state, props) => {
activeSection: state.ActiveCoin.activeSection,
lastSendToResponse: state.ActiveCoin.lastSendToResponse,
},
Dashboard: state.Dashboard,
};
if (props &&

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

@ -21,7 +21,7 @@ export const AddressListRender = function() {
className="selected"
onClick={ () => this.updateAddressSelection(null, 'public', null) }>
<a>
<span className="text">{ translate('INDEX.T_FUNDS') }</span>
<span className="text">{ this.props.ActiveCoin.mode === 'spv' ? `[ ${this.props.ActiveCoin.balance.balance} ${this.props.ActiveCoin.coin} ] ${this.props.Dashboard.electrumCoins[this.props.ActiveCoin.coin].pub}` : translate('INDEX.T_FUNDS') }</span>
<span
className="glyphicon glyphicon-ok check-mark pull-right"
style={{ display: this.state.sendFrom === null ? 'inline-block' : 'none' }}></span>
@ -81,7 +81,7 @@ export const _SendFormRender = function() {
placeholder="0.000"
autoComplete="off" />
</div>
<div className={ 'col-lg-6 form-group form-material' + (this.isTransparentTx() ? '' : ' hide') }>
<div className={ 'col-lg-6 form-group form-material' + (this.isTransparentTx() && this.props.ActiveCoin.mode === 'native' ? '' : ' hide') }>
<span className="pointer">
<label className="switch">
<input
@ -258,7 +258,7 @@ export const SendRender = function() {
</tr>
<tr>
<td>Transaction ID</td>
<td>{ this.state.lastSendToResponse }</td>
<td>{ this.props.ActiveCoin.mode === 'spv' ? (this.state.lastSendToResponse && this.state.lastSendToResponse.txid ? this.state.lastSendToResponse.txid : 'error') : this.state.lastSendToResponse }</td>
</tr>
</tbody>
</table>

17
react/src/components/dashboard/walletsBalance/walletsBalance.js

@ -1,7 +1,10 @@
import React from 'react';
import { connect } from 'react-redux';
import { translate } from '../../../translate/translate';
import { getDashboardUpdate } from '../../../actions/actionCreators';
import {
getDashboardUpdate,
shepherdElectrumBalance,
} from '../../../actions/actionCreators';
import Store from '../../../store';
import WalletsBalanceRender from './walletsBalance.render';
@ -39,7 +42,11 @@ class WalletsBalance extends React.Component {
}
refreshBalance() {
Store.dispatch(getDashboardUpdate(this.props.ActiveCoin.coin));
if (this.props.ActiveCoin.mode === 'native') {
Store.dispatch(getDashboardUpdate(this.props.ActiveCoin.coin));
} else if (this.props.ActiveCoin.mode === 'spv') {
Store.dispatch(shepherdElectrumBalance(this.props.ActiveCoin.coin, this.props.Dashboard.electrumCoins[this.props.ActiveCoin.coin].pub));
}
}
renderBalance(type) {
@ -70,6 +77,8 @@ class WalletsBalance extends React.Component {
this.props.ActiveCoin.balance.transparent) {
_balance = this.props.ActiveCoin.balance.transparent;
}
} else if (_mode === 'spv' && this.props.ActiveCoin.balance.balance) {
_balance = this.props.ActiveCoin.balance.balance;
}
return _balance;
@ -118,9 +127,7 @@ const mapStateToProps = (state) => {
activeAddress: state.ActiveCoin.activeAddress,
progress: state.ActiveCoin.progress,
},
Dashboard: {
activeHandle: state.Dashboard.activeHandle,
},
Dashboard: state.Dashboard,
};
};

14
react/src/components/dashboard/walletsBalance/walletsBalance.render.js

@ -9,7 +9,7 @@ const WalletsBalanceRender = function() {
id="wallet-widgets"
className="wallet-widgets">
<div className="col-xs-12 flex">
<div className={ this.props.ActiveCoin.coin === 'CHIPS' ? 'col-lg-12 col-xs-12' : 'col-lg-3 col-xs-12' }>
<div className={ this.props.ActiveCoin.coin === 'CHIPS' || this.props.ActiveCoin.mode === 'spv' ? 'col-lg-12 col-xs-12' : 'col-lg-3 col-xs-12' }>
<div className="widget widget-shadow">
<div className="widget-content">
<i
@ -18,8 +18,10 @@ const WalletsBalanceRender = function() {
<div className="padding-20 padding-top-10">
<div className="clearfix">
<div className="pull-left padding-vertical-10">
<i className="icon fa-eye font-size-24 vertical-align-bottom margin-right-5"></i>
{ this.props.ActiveCoin.coin === 'CHIPS' ? translate('INDEX.BALANCE') : translate('INDEX.TRANSPARENT_BALANCE') }
{ this.props.ActiveCoin.coin !== 'CHIPS' && this.props.ActiveCoin.mode !== 'spv' &&
<i className="icon fa-eye font-size-24 vertical-align-bottom margin-right-5"></i>
}
{ this.props.ActiveCoin.coin === 'CHIPS' || this.props.ActiveCoin.mode === 'spv' ? translate('INDEX.BALANCE') : translate('INDEX.TRANSPARENT_BALANCE') }
</div>
<span
className="pull-right padding-top-10 font-size-22"
@ -32,7 +34,7 @@ const WalletsBalanceRender = function() {
</div>
</div>
<div className={ this.props.ActiveCoin.coin !== 'CHIPS' ? 'col-lg-3 col-xs-12' : 'hide' }>
<div className={ this.props.ActiveCoin.coin !== 'CHIPS' && this.props.ActiveCoin.mode !== 'spv' ? 'col-lg-3 col-xs-12' : 'hide' }>
<div className="widget widget-shadow">
<div className="padding-20 padding-top-10">
<div className="clearfix">
@ -50,7 +52,7 @@ const WalletsBalanceRender = function() {
</div>
</div>
<div className={ this.props.ActiveCoin.coin !== 'CHIPS' ? 'col-lg-3 col-xs-12' : 'hide' }>
<div className={ this.props.ActiveCoin.coin !== 'CHIPS' && this.props.ActiveCoin.mode !== 'spv' ? 'col-lg-3 col-xs-12' : 'hide' }>
<div className="widget widget-shadow">
<div className="widget-content">
<div className="padding-20 padding-top-10">
@ -70,7 +72,7 @@ const WalletsBalanceRender = function() {
</div>
</div>
<div className={ this.props.ActiveCoin.coin !== 'CHIPS' ? 'col-lg-3 col-xs-12' : 'hide' }>
<div className={ this.props.ActiveCoin.coin !== 'CHIPS' && this.props.ActiveCoin.mode !== 'spv' ? 'col-lg-3 col-xs-12' : 'hide' }>
<div className="widget widget-shadow">
<div className="widget-content">
<div className="padding-20 padding-top-10">

12
react/src/components/dashboard/walletsData/walletsData.js

@ -8,6 +8,7 @@ import {
toggleDashboardTxInfoModal,
changeActiveAddress,
getDashboardUpdate,
shepherdElectrumTransactions,
} from '../../../actions/actionCreators';
import Store from '../../../store';
import {
@ -182,7 +183,11 @@ class WalletsData extends React.Component {
}
refreshTxHistory() {
Store.dispatch(getDashboardUpdate(this.props.ActiveCoin.coin));
if (this.props.ActiveCoin.mode === 'native') {
Store.dispatch(getDashboardUpdate(this.props.ActiveCoin.coin));
} else if (this.props.ActiveCoin.mode === 'spv') {
Store.dispatch(shepherdElectrumTransactions(this.props.ActiveCoin.coin, this.props.Dashboard.electrumCoins[this.props.ActiveCoin.coin].pub));
}
}
toggleTxInfoModal(display, txIndex) {
@ -208,9 +213,7 @@ class WalletsData extends React.Component {
this.props.ActiveCoin.txhistory !== 'loading' &&
this.props.ActiveCoin.txhistory !== 'no data' &&
this.props.ActiveCoin.txhistory.length) {
_stateChange = Object.assign({}, _stateChange, {
isExplorerData: this.props.ActiveCoin.txhistory[0].source ? true : false,
itemsList: this.props.ActiveCoin.txhistory,
filteredItemsList: this.filterTransactions(this.props.ActiveCoin.txhistory, this.state.searchTerm),
txhistory: this.props.ActiveCoin.txhistory,
@ -419,7 +422,7 @@ class WalletsData extends React.Component {
if (this.props &&
this.props.ActiveCoin &&
this.props.ActiveCoin.coin &&
this.props.ActiveCoin.mode === 'native' &&
//this.props.ActiveCoin.mode === 'native' &&
this.props.ActiveCoin.activeSection === 'default'
) {
return WalletsDataRender.call(this);
@ -449,6 +452,7 @@ const mapStateToProps = (state) => {
Main: {
coins: state.Main.coins,
},
Dashboard: state.Dashboard,
};
};

9
react/src/components/dashboard/walletsInfo/walletsInfo.js

@ -17,7 +17,7 @@ class WalletsInfo extends React.Component {
render() {
if (this.props &&
this.props.ActiveCoin &&
this.props.ActiveCoin.progress &&
(this.props.ActiveCoin.progress || this.props.Dashboard.electrumCoins) &&
this.props.ActiveCoin.activeSection === 'settings') {
return WalletsNativeInfoRender.call(this);
}
@ -28,11 +28,8 @@ class WalletsInfo extends React.Component {
const mapStateToProps = (state) => {
return {
ActiveCoin: {
coin: state.ActiveCoin.coin,
activeSection: state.ActiveCoin.activeSection,
progress: state.ActiveCoin.progress,
},
ActiveCoin: state.ActiveCoin,
Dashboard: state.Dashboard,
};
};

356
react/src/components/dashboard/walletsInfo/walletsInfo.render.js

@ -3,159 +3,227 @@ import { translate } from '../../../translate/translate';
import ClaimInterestModal from '../claimInterestModal/claimInterestModal';
const WalletsInfoRender = function() {
const _progress = this.props.ActiveCoin.progress;
if (this.props.ActiveCoin.mode === 'native') {
const _progress = this.props.ActiveCoin.progress;
return (
<div>
<div className="col-xlg-6 col-md-4">
<div className="panel">
<div className="panel-heading">
<h3 className="panel-title">{ translate('INDEX.WALLET_INFO') }</h3>
</div>
<div className="table-responsive">
<table className="table table-striped">
<tbody>
<tr>
<td>{ translate('INDEX.WALLET_VERSION') }</td>
<td>
{ _progress.walletversion }
</td>
</tr>
<tr>
<td>{ translate('INDEX.BALANCE') }</td>
<td>
{ _progress.balance }
</td>
</tr>
<tr>
<td>{ translate('INDEX.UNCONFIRMED_BALANCE') }</td>
<td></td>
</tr>
<tr>
<td>{ translate('INDEX.IMMATURE_BALANCE') }</td>
<td></td>
</tr>
<tr>
<td>{ translate('INDEX.TOTAL_TX_COUNT') }</td>
<td></td>
</tr>
</tbody>
</table>
return (
<div>
<div className="col-xlg-6 col-md-4">
<div className="panel">
<div className="panel-heading">
<h3 className="panel-title">{ translate('INDEX.WALLET_INFO') }</h3>
</div>
<div className="table-responsive">
<table className="table table-striped">
<tbody>
<tr>
<td>{ translate('INDEX.WALLET_VERSION') }</td>
<td>
{ _progress.walletversion }
</td>
</tr>
<tr>
<td>{ translate('INDEX.BALANCE') }</td>
<td>
{ _progress.balance }
</td>
</tr>
<tr>
<td>{ translate('INDEX.UNCONFIRMED_BALANCE') }</td>
<td></td>
</tr>
<tr>
<td>{ translate('INDEX.IMMATURE_BALANCE') }</td>
<td></td>
</tr>
<tr>
<td>{ translate('INDEX.TOTAL_TX_COUNT') }</td>
<td></td>
</tr>
</tbody>
</table>
</div>
</div>
{ this.props.ActiveCoin.coin === 'KMD' &&
<div>
<button
type="button"
className="btn btn-success waves-effect waves-light margin-top-20 btn-next"
onClick={ () => this.openClaimInterestModal() }>{ translate('CLAIM_INTEREST.CLAIM_INTEREST', ' ') }</button>
<ClaimInterestModal />
</div>
}
</div>
{ this.props.ActiveCoin.coin === 'KMD' &&
<div>
<button
type="button"
className="btn btn-success waves-effect waves-light margin-top-20 btn-next"
onClick={ () => this.openClaimInterestModal() }>{ translate('CLAIM_INTEREST.CLAIM_INTEREST', ' ') }</button>
<ClaimInterestModal />
<div className="col-xlg-6 col-md-8">
<div className="panel">
<div className="panel-heading">
<h3 className="panel-title">
Komodo { translate('INDEX.INFO') }
</h3>
</div>
<div className="table-responsive">
<table className="table table-striped">
<tbody>
<tr>
<td>{ translate('INDEX.VERSION') }</td>
<td>
{ _progress.KMDversion }
</td>
</tr>
<tr>
<td>{ translate('INDEX.PROTOCOL_VERSION') }</td>
<td>
{ _progress.protocolversion }
</td>
</tr>
<tr>
<td>{ translate('INDEX.NOTARIZED') }</td>
<td>
{ _progress.notarized }
</td>
</tr>
<tr>
<td>
{ translate('INDEX.NOTARIZED') } { translate('INDEX.HASH') }
</td>
<td>
{ _progress.notarizedhash ?
_progress.notarizedhash.substring(
0,
Math.floor(_progress.notarizedhash.length / 2)
) +
'\t' +
_progress.notarizedhash.substring(
Math.floor(_progress.notarizedhash.length / 2),
_progress.notarizedhash.length
)
: ''
}
</td>
</tr>
<tr>
<td>
{ translate('INDEX.NOTARIZED') } BTC
</td>
<td></td>
</tr>
<tr>
<td>{ translate('INDEX.BLOCKS') }</td>
<td>
{ _progress.blocks }
</td>
</tr>
<tr>
<td>{ translate('INDEX.CONNECTIONS') }</td>
<td>
{ _progress.connections }
</td>
</tr>
<tr>
<td>{ translate('INDEX.DIFFICULTY') }</td>
<td>
{ _progress.difficulty }
</td>
</tr>
<tr>
<td>Testnet</td>
<td>
{ _progress.testnet }
</td>
</tr>
<tr>
<td>{ translate('INDEX.PAY_TX_FEE') }</td>
<td>
{ _progress.paytxfee }
</td>
</tr>
<tr>
<td>{ translate('INDEX.RELAY_FEE') }</td>
<td>
{ _progress.relayfee }
</td>
</tr>
<tr>
<td>{ translate('INDEX.ERRORS') }</td>
<td>
{ _progress.errors }
</td>
</tr>
</tbody>
</table>
</div>
</div>
}
</div>
</div>
);
} else if (this.props.ActiveCoin.mode === 'spv') {
const _balance = this.props.ActiveCoin.balance;
const _server = this.props.Dashboard.electrumCoins[this.props.ActiveCoin.coin];
<div className="col-xlg-6 col-md-8">
<div className="panel">
<div className="panel-heading">
<h3 className="panel-title">
Komodo { translate('INDEX.INFO') }
</h3>
</div>
<div className="table-responsive">
<table className="table table-striped">
<tbody>
<tr>
<td>{ translate('INDEX.VERSION') }</td>
<td>
{ _progress.KMDversion }
</td>
</tr>
<tr>
<td>{ translate('INDEX.PROTOCOL_VERSION') }</td>
<td>
{ _progress.protocolversion }
</td>
</tr>
<tr>
<td>{ translate('INDEX.NOTARIZED') }</td>
<td>
{ _progress.notarized }
</td>
</tr>
<tr>
<td>
{ translate('INDEX.NOTARIZED') } { translate('INDEX.HASH') }
</td>
<td>
{ _progress.notarizedhash ?
_progress.notarizedhash.substring(
0,
Math.floor(_progress.notarizedhash.length / 2)
) +
'\t' +
_progress.notarizedhash.substring(
Math.floor(_progress.notarizedhash.length / 2),
_progress.notarizedhash.length
)
: ''
}
</td>
</tr>
<tr>
<td>
{ translate('INDEX.NOTARIZED') } BTC
</td>
<td></td>
</tr>
<tr>
<td>{ translate('INDEX.BLOCKS') }</td>
<td>
{ _progress.blocks }
</td>
</tr>
<tr>
<td>{ translate('INDEX.CONNECTIONS') }</td>
<td>
{ _progress.connections }
</td>
</tr>
<tr>
<td>{ translate('INDEX.DIFFICULTY') }</td>
<td>
{ _progress.difficulty }
</td>
</tr>
<tr>
<td>Testnet</td>
<td>
{ _progress.testnet }
</td>
</tr>
<tr>
<td>{ translate('INDEX.PAY_TX_FEE') }</td>
<td>
{ _progress.paytxfee }
</td>
</tr>
<tr>
<td>{ translate('INDEX.RELAY_FEE') }</td>
<td>
{ _progress.relayfee }
</td>
</tr>
<tr>
<td>{ translate('INDEX.ERRORS') }</td>
<td>
{ _progress.errors }
</td>
</tr>
</tbody>
</table>
return (
<div>
<div className="col-xlg-6 col-md-4">
<div className="panel">
<div className="panel-heading">
<h3 className="panel-title">{ translate('INDEX.WALLET_INFO') }</h3>
</div>
<div className="table-responsive">
<table className="table table-striped">
<tbody>
<tr>
<td>Server IP</td>
<td>
{ _server.server.ip }
</td>
</tr>
<tr>
<td>Server Port</td>
<td>
{ _server.server.port }
</td>
</tr>
<tr>
<td>Connection type</td>
<td>
TCP
</td>
</tr>
<tr>
<td>{ translate('INDEX.PAY_TX_FEE') }</td>
<td>
{ _server.txfee }
</td>
</tr>
<tr>
<td>{ translate('INDEX.BALANCE') }</td>
<td>
{ _balance.balance }
</td>
</tr>
<tr>
<td>{ translate('INDEX.UNCONFIRMED_BALANCE') }</td>
<td>
{ _balance.uncomfirmed }
</td>
</tr>
</tbody>
</table>
</div>
</div>
{ this.props.ActiveCoin.coin === 'KMD' && this.props.ActiveCoin.mode !== 'spv' &&
<div>
<button
type="button"
className="btn btn-success waves-effect waves-light margin-top-20 btn-next"
onClick={ () => this.openClaimInterestModal() }>{ translate('CLAIM_INTEREST.CLAIM_INTEREST', ' ') }</button>
<ClaimInterestModal />
</div>
}
</div>
</div>
</div>
);
);
}
};
export default WalletsInfoRender;

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

@ -66,11 +66,15 @@ class WalletsNative extends React.Component {
}
render() {
if (this.isActiveCoinModeNative()) {
//if (this.isActiveCoinModeNative()) {
if (this.props.ActiveCoin && this.props.ActiveCoin.mode) {
return WalletsNativeRender.call(this);
} else {
return null;
}
/*} else {
return null;
}*/
}
}

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

@ -28,7 +28,9 @@ const WalletsNativeRender = function() {
</ol>
</div>
<div className="page-content page-content-native">
<WalletsProgress />
{ this.props.ActiveCoin.mode !== 'spv' &&
<WalletsProgress />
}
<div className="row">
<WalletsBalance />
<ReceiveCoin />

17
react/src/components/dashboard/walletsNav/walletsNav.js

@ -32,8 +32,11 @@ class WalletsNav extends React.Component {
const _mode = this.props.ActiveCoin.mode;
if (this.props.ActiveCoin.balance &&
this.props.ActiveCoin.balance.total) {
this.props.ActiveCoin.balance.total &&
_mode === 'native') {
_balance = this.props.ActiveCoin.balance.total;
} else if (_mode === 'spv' && this.props.ActiveCoin.balance.balance) {
_balance = this.props.ActiveCoin.balance.balance;
}
return _balance;
@ -49,33 +52,33 @@ class WalletsNav extends React.Component {
// TODO: merge toggle func into one
toggleSendReceiveCoinForms() {
if (this.props.ActiveCoin.mode === 'native') {
//if (this.props.ActiveCoin.mode === 'native') {
Store.dispatch(
toggleDashboardActiveSection(
this.props.ActiveCoin.activeSection === 'settings' ? 'default' : 'settings'
)
);
}
//}
}
toggleSendCoinForm(display) {
if (this.props.ActiveCoin.mode === 'native') {
//if (this.props.ActiveCoin.mode === 'native') {
Store.dispatch(
toggleDashboardActiveSection(
this.props.ActiveCoin.activeSection === 'send' ? 'default' : 'send'
)
);
}
//}
}
toggleReceiveCoinForm(display) {
if (this.props.ActiveCoin.mode === 'native') {
//if (this.props.ActiveCoin.mode === 'native') {
Store.dispatch(
toggleDashboardActiveSection(
this.props.ActiveCoin.activeSection === 'receive' ? 'default' : 'receive'
)
);
}
//}
}
render() {

6
react/src/components/dashboard/walletsTxInfo/walletsTxInfo.render.js

@ -90,19 +90,19 @@ const WalletsTxInfoRender = function(txInfo) {
<tr>
<td>walletconflicts</td>
<td>
{ txInfo.walletconflicts.length }
{ txInfo.walletconflicts ? txInfo.walletconflicts.length : '' }
</td>
</tr>
<tr>
<td>time</td>
<td>
{ secondsToString(txInfo.time) }
{ txInfo.time ? secondsToString(txInfo.time) : '' }
</td>
</tr>
<tr>
<td>timereceived</td>
<td>
{ secondsToString(txInfo.timereceived) }
{ txInfo.timereceived ? secondsToString(txInfo.timereceived) : '' }
</td>
</tr>
</tbody>

428
react/src/components/login/login.js

@ -2,16 +2,32 @@ import React from 'react';
import { connect } from 'react-redux';
import {
toggleAddcoinModal,
iguanaWalletPassphrase,
shepherdElectrumAuth,
shepherdElectrumCoins,
//iguanaActiveHandle,
startInterval,
getDexCoins,
createNewWallet,
triggerToaster,
toggleLoginSettingsModal
} from '../../actions/actionCreators';
import Config from '../../config';
import Store from '../../store';
import { PassPhraseGenerator } from '../../util/crypto/passphrasegenerator';
import SwallModalRender from './swall-modal.render';
import LoginRender from './login.render';
import { translate } from '../../translate/translate';
import {
encryptPassphrase,
loadPinList,
loginWithPin
} from '../../actions/actions/pin';
const IGUNA_ACTIVE_HANDLE_TIMEOUT = 3000;
const IGUNA_ACTIVE_COINS_TIMEOUT = 10000;
// TODO: remove duplicate activehandle and activecoins calls
class Login extends React.Component {
constructor() {
@ -19,11 +35,44 @@ class Login extends React.Component {
this.state = {
display: false,
activeLoginSection: 'activateCoin',
loginPassphrase: '',
seedInputVisibility: false,
loginPassPhraseSeedType: null,
bitsOption: 256,
randomSeed: PassPhraseGenerator.generatePassPhrase(256),
randomSeedConfirm: '',
isSeedConfirmError: false,
isSeedBlank: false,
displaySeedBackupModal: false,
customWalletSeed: false,
isCustomSeedWeak: false,
nativeOnly: Config.iguanaLessMode,
trimPassphraseTimer: null,
displayLoginSettingsDropdown: false,
displayLoginSettingsDropdownSection: null,
shouldEncryptSeed: false,
encryptKey: '',
pubKey: '',
decryptKey: '',
selectedPin: '',
isExperimentalOn: false,
};
this.toggleActivateCoinForm = this.toggleActivateCoinForm.bind(this);
this.updateRegisterConfirmPassPhraseInput = this.updateRegisterConfirmPassPhraseInput.bind(this);
this.updateLoginPassPhraseInput = this.updateLoginPassPhraseInput.bind(this);
this.loginSeed = this.loginSeed.bind(this);
this.toggleSeedInputVisibility = this.toggleSeedInputVisibility.bind(this);
this.handleRegisterWallet = this.handleRegisterWallet.bind(this);
this.openSyncOnlyModal = this.openSyncOnlyModal.bind(this);
this.toggleSeedBackupModal = this.toggleSeedBackupModal.bind(this);
this.copyPassPhraseToClipboard = this.copyPassPhraseToClipboard.bind(this);
this.execWalletCreate = this.execWalletCreate.bind(this);
this.resizeLoginTextarea = this.resizeLoginTextarea.bind(this);
this.toggleLoginSettingsDropdown = this.toggleLoginSettingsDropdown.bind(this);
this.updateEncryptKey = this.updateEncryptKey.bind(this);
this.updatePubKey = this.updatePubKey.bind(this);
this.updateDecryptKey = this.updateDecryptKey.bind(this);
this.loadPinList = this.loadPinList.bind(this);
}
// the setInterval handler for 'activeCoins'
@ -38,38 +87,391 @@ class Login extends React.Component {
});
}
isCustomWalletSeed() {
return this.state.customWalletSeed;
}
toggleCustomWalletSeed() {
this.setState({
customWalletSeed: !this.state.customWalletSeed
}, () => {
// if customWalletSeed is set to false, regenerate the seed
if (!this.state.customWalletSeed) {
this.setState({
randomSeed: PassPhraseGenerator.generatePassPhrase(this.state.bitsOption),
isSeedConfirmError: false,
isSeedBlank: false,
});
} else {
// if customWalletSeed is set to true, reset to seed to an empty string
this.setState({
randomSeed: '',
randomSeedConfirm: '',
});
}
});
}
shouldEncryptSeed() {
return this.state.shouldEncryptSeed;
}
toggleShouldEncryptSeed() {
this.setState({
shouldEncryptSeed: !this.state.shouldEncryptSeed
});
}
updateEncryptKey(e) {
this.setState({
encryptKey: e.target.value
});
}
updatePubKey(e) {
this.setState({
pubKey: e.target.value
});
}
updateDecryptKey(e) {
this.setState({
decryptKey: e.target.value
});
}
openSyncOnlyModal() {
Store.dispatch(getSyncOnlyForks());
const _iguanaActiveHandle = setInterval(() => {
Store.dispatch(getSyncOnlyForks());
}, IGUNA_ACTIVE_HANDLE_TIMEOUT);
Store.dispatch(
startInterval(
'syncOnly',
_iguanaActiveHandle
)
);
Store.dispatch(toggleSyncOnlyModal(true));
this.setState({
displayLoginSettingsDropdown: false,
});
}
componentDidMount() {
// Store.dispatch(iguanaActiveHandle(true));
// this.loadPinList();
let appConfig;
try {
appConfig = window.require('electron').remote.getCurrentWindow().appConfig;
} catch (e) {}
this.setState({
isExperimentalOn: appConfig.experimentalFeatures,
});
}
toggleSeedInputVisibility() {
this.setState({
seedInputVisibility: !this.state.seedInputVisibility,
});
this.resizeLoginTextarea();
}
generateNewSeed(bits) {
this.setState(Object.assign({}, this.state, {
randomSeed: PassPhraseGenerator.generatePassPhrase(bits),
bitsOption: bits,
isSeedBlank: false,
}));
}
toggleLoginSettingsDropdown() {
this.setState(Object.assign({}, this.state, {
displayLoginSettingsDropdown: !this.state.displayLoginSettingsDropdown,
}));
}
componentWillReceiveProps(props) {
if (props.Login.pinList === 'no pins') {
props.Login.pinList = [];
}
if (props &&
props.Main &&
props.Main.coins &&
props.Main.coins.native &&
props.Main.coins.native.length) {
this.setState({
display: false,
});
} else {
props.Main.isLoggedIn) {
if (props.Main.total === 0) {
this.setState({
activeLoginSection: 'activateCoin',
display: true,
});
} else {
this.setState({
display: false,
});
}
}
if (props &&
props.Main &&
!props.Main.isLoggedIn) {
console.warn('display login');
this.setState({
display: true,
activeLoginSection: 'login',
});
/*if (!this.props.Interval.interval.activeCoins) {
// only start a new 'activeCoins' interval if a previous one doesn't exist
if (!this._iguanaActiveCoins) {
this._iguanaActiveCoins = setInterval(() => {
Store.dispatch(getDexCoins());
}, IGUNA_ACTIVE_COINS_TIMEOUT);
Store.dispatch(startInterval('activeCoins', this._iguanaActiveCoins));
}
}*/
document.body.className = 'page-login layout-full page-dark';
}
}
toggleLoginSettingsDropdown() {
this.setState(Object.assign({}, this.state, {
displayLoginSettingsDropdown: !this.state.displayLoginSettingsDropdown,
}));
if (this.state.activeLoginSection !== 'signup' &&
props &&
props.Main &&
props.Main.isLoggedIn) {
/*if (props &&
props.Main &&
props.Main.activeCoins) {
this.setState({
activeLoginSection: 'login',
});
} else {*/
this.setState({
activeLoginSection: 'activateCoin',
});
//}
}
console.warn(this.state);
}
toggleActivateCoinForm() {
Store.dispatch(toggleAddcoinModal(true, false));
}
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
loginPassPhraseSeedType: this.getLoginPassPhraseSeedType(newValue),
});
}, 2000);
this.resizeLoginTextarea();
this.setState({
trimPassphraseTimer: _trimPassphraseTimer,
[e.target.name]: newValue,
loginPassPhraseSeedType: this.getLoginPassPhraseSeedType(newValue),
});
}
updateRegisterConfirmPassPhraseInput(e) {
this.setState({
[e.target.name]: e.target.value,
isSeedConfirmError: false,
isSeedBlank: this.isBlank(e.target.value),
});
}
updateWalletSeed(e) {
this.setState({
randomSeed: e.target.value,
isSeedConfirmError: false,
isSeedBlank: this.isBlank(e.target.value),
});
}
loginSeed() {
// reset the login pass phrase values so that when the user logs out, the values are clear
this.setState({
loginPassphrase: null,
loginPassPhraseSeedType: null,
});
if (this.state.shouldEncryptSeed) {
Store.dispatch(encryptPassphrase(this.state.loginPassphrase, this.state.encryptKey, this.state.pubKey));
}
if (this.state.selectedPin) {
Store.dispatch(loginWithPin(this.state.decryptKey, this.state.selectedPin));
} else {
/*Store.dispatch(
iguanaWalletPassphrase(this.state.loginPassphrase)
);*/
Store.dispatch(
shepherdElectrumAuth(this.state.loginPassphrase)
);
Store.dispatch(
shepherdElectrumCoins()
);
}
}
loadPinList() {
Store.dispatch(loadPinList());
}
updateSelectedPin(e) {
this.setState({
selectedPin: e.target.value
});
}
getLoginPassPhraseSeedType(passPhrase) {
if (!passPhrase) {
return null;
}
const passPhraseWords = passPhrase.split(' ');
if (!PassPhraseGenerator.arePassPhraseWordsValid(passPhraseWords)) {
return null;
}
if (PassPhraseGenerator.isPassPhraseValid(passPhraseWords, 256)) {
return translate('LOGIN.IGUANA_SEED');
}
if (PassPhraseGenerator.isPassPhraseValid(passPhraseWords, 160)) {
return translate('LOGIN.WAVES_SEED');
}
if (PassPhraseGenerator.isPassPhraseValid(passPhraseWords, 128)) {
return translate('LOGIN.NXT_SEED');
}
return null;
}
updateActiveLoginSection(name) {
// reset login/create form
this.setState({
activeLoginSection: name,
loginPassphrase: null,
loginPassPhraseSeedType: null,
seedInputVisibility: false,
bitsOption: 256,
randomSeed: PassPhraseGenerator.generatePassPhrase(256),
randomSeedConfirm: '',
isSeedConfirmError: false,
isSeedBlank: false,
displaySeedBackupModal: false,
customWalletSeed: false,
isCustomSeedWeak: false,
});
}
execWalletCreate() {
Store.dispatch(
createNewWallet(
this.state.randomSeedConfirm,
this.props.Dashboard.activeHandle
)
);
this.setState({
activeLoginSection: 'activateCoin',
displaySeedBackupModal: false,
isSeedConfirmError: false,
});
}
// TODO: disable register btn if seed or seed conf is incorrect
handleRegisterWallet() {
const enteredSeedsMatch = this.state.randomSeed === this.state.randomSeedConfirm;
const isSeedBlank = this.isBlank(this.state.randomSeed);
// if custom seed check for string strength
// at least 1 letter in upper case
// at least 1 digit
// at least one special char
// min length 10 chars
const _customSeed = this.state.customWalletSeed ? this.state.randomSeed.match('^(?=.*[A-Z])(?=.*[^<>{}\"/|;:.,~!?@#$%^=&*\\]\\\\()\\[_+]*$)(?=.*[0-9])(?=.*[a-z]).{10,99}$') : false;
this.setState({
isCustomSeedWeak: _customSeed === null ? true : false,
isSeedConfirmError: !enteredSeedsMatch ? true : false,
isSeedBlank: isSeedBlank ? true : false,
});
if (enteredSeedsMatch && !isSeedBlank && _customSeed !== null) {
this.toggleSeedBackupModal();
}
}
isBlank(str) {
return (!str || /^\s*$/.test(str));
}
handleKeydown(e) {
this.updateLoginPassPhraseInput(e);
if (e.key === 'Enter') {
this.loginSeed();
}
}
toggleSeedBackupModal() {
this.setState(Object.assign({}, this.state, {
displaySeedBackupModal: !this.state.displaySeedBackupModal,
}));
}
copyPassPhraseToClipboard() {
const passPhrase = this.state.randomSeed;
const textField = document.createElement('textarea');
textField.innerText = passPhrase;
document.body.appendChild(textField);
textField.select();
document.execCommand('copy');
textField.remove();
Store.dispatch(
triggerToaster(
translate('LOGIN.SEED_SUCCESSFULLY_COPIED'),
translate('LOGIN.SEED_COPIED'),
'success'
)
);
}
renderSwallModal() {
if (this.state.displaySeedBackupModal) {
return SwallModalRender.call(this);
}
return null;
}
render() {
if ((this.state && this.state.display) ||
!this.props.Main) {
@ -93,4 +495,4 @@ const mapStateToProps = (state) => {
};
};
export default connect(mapStateToProps)(Login);
export default connect(mapStateToProps)(Login);

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

@ -6,6 +6,7 @@ const LoginRender = function () {
return (
<div>
<LoginSettingsModal section={ this.state.displayLoginSettingsDropdownSection } />
{ this.renderSwallModal() }
<div className="page animsition vertical-align text-center fade-in">
<div className="page-content vertical-align-middle col-xs-12 col-sm-6 col-sm-offset-3">
<div className="brand">
@ -33,6 +34,11 @@ const LoginRender = function () {
<i className="icon md-settings"></i> { translate('INDEX.SETTINGS') }
</a>
</li>
<li className={ this.state.nativeOnly || !this.state.isExperimentalOn ? 'hide' : '' }>
<a onClick={ this.openSyncOnlyModal }>
<i className="icon fa-cubes"></i> { translate('ADD_COIN.SYNC_ONLY') }
</a>
</li>
<li>
<a onClick={ () => this.toggleLoginSettingsDropdownSection('about') }>
<i className="icon fa-users"></i> { translate('ABOUT.ABOUT_AGAMA') }
@ -42,6 +48,155 @@ const LoginRender = function () {
</div>
</div>
<div className={ this.state.activeLoginSection === 'login' && !this.state.nativeOnly ? 'show' : 'hide' }>
<h4 className="color-white">
{ translate('INDEX.WELCOME_LOGIN') }
</h4>
{ this.props.Login.pinList.length > 0 &&
<span>You can login be entering a login seed or by selecting a pin</span>
}
<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"
onChange={ this.updateLoginPassPhraseInput }
onKeyDown={ (event) => this.handleKeydown(event) }
value={ this.state.loginPassphrase || '' } />
<textarea
className={ this.state.seedInputVisibility ? 'form-control' : 'hide' }
id="loginPassphrase"
name="loginPassphrase"
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>
<label
className="floating-label"
htmlFor="inputPassword">{ translate('INDEX.WALLET_SEED') }</label>
</div>
{ this.state.loginPassPhraseSeedType &&
<div
className="form-group form-material floating horizontal-padding-0 margin-top-20 seed-type-block"
style={{ width: `${this.state.loginPassPhraseSeedType.length * 8}px` }}>
<div className="placeholder-label">{ this.state.loginPassPhraseSeedType }</div>
</div>
}
{ this.state.loginPassphrase &&
<div className="row">
<div className="toggle-box padding-top-30 col-sm-3">
<span className="pointer">
<label className="switch">
<input
type="checkbox"
checked={ this.shouldEncryptSeed() } />
<div
className="slider"
onClick={ () => this.toggleShouldEncryptSeed() }></div>
</label>
<div
className="toggle-label white"
onClick={ () => this.toggleShouldEncryptSeed() }>
{ translate('LOGIN.ENCRYPT_SEED') }
</div>
</span>
</div>
<div className="col-sm-9">
<div className="form-group form-material floating horizontal-padding-0 margin-5 margin-right-0">
<input
type="text"
className="form-control"
name="encryptKey"
placeholder={ translate('LOGIN.ENCRYPT_KEY') }
onChange={ this.updateEncryptKey }
value={ this.state.encryptKey }
disabled={ !this.shouldEncryptSeed() } />
</div>
<div className="form-group form-material floating horizontal-padding-0 margin-5 margin-right">
<input
type="text"
className="form-control"
name="pubKey"
placeholder={ translate('LOGIN.PUBKEY') }
onChange={ this.updatePubKey }
value={ this.state.pubKey }
disabled={ !this.shouldEncryptSeed() } />
</div>
</div>
</div>
}
{ this.props.Login.pinList.length > 0 &&
<div className="row margin-top-30">
<div className="col-xs-12">
<div style={{width: "10%", float: "left", marginLeft: "38%"}}>
<hr/>
</div>
<div style={{width: "4%", float: "left", marginTop: "10px"}}><span>OR</span></div>
<div style={{width: "10%", float: "left"}}>
<hr/>
</div>
</div>
</div>
}
{ this.props.Login.pinList.length > 0 &&
<div className="row">
<div className="form-group form-material floating col-sm-8 padding-left-10 horizontal-padding-0">
<select
className="form-control form-material"
name="storedPins"
value={ this.state.selectedPin }
onChange={ (event) => this.updateSelectedPin(event) }
autoFocus>
<option className="login-option" value="">{ translate('INDEX.SELECT') }</option>
{this.props.Login.pinList.map(function(pin) {
return <option className="login-option" value={pin} key={pin}>{ pin }</option>
})}
</select>
</div>
<div className="form-group form-material floating col-sm-4 padding-left-10 margin-top-20">
<input
type="text"
className="form-control"
name="decryptKey"
placeholder={ translate('LOGIN.DECRYPT_KEY') }
disabled={ false }
onChange={ this.updateDecryptKey }
value={ this.state.decryptKey } />
</div>
</div>
}
<button
type="button"
className="btn btn-primary btn-block margin-top-20"
onClick={ this.loginSeed }
disabled={ !this.state.loginPassphrase || !this.state.loginPassphrase.length }>{ translate('INDEX.SIGN_IN') }</button>
<div className="form-group form-material floating">
<button
className="btn btn-lg btn-flat btn-block waves-effect"
id="register-btn"
onClick={ () => this.updateActiveLoginSection('signup') }>{ translate('INDEX.CREATE_WALLET') }</button>
<button
className="btn btn-lg btn-flat btn-block waves-effect hide"
id="logint-another-wallet">{ translate('INDEX.LOGIN_ANOTHER_WALLET') }</button>
<button
className="btn btn-lg btn-flat btn-block waves-effect margin-top-20"
id="register-btn"
onClick={ this.toggleActivateCoinForm }
disabled={ !this.props.Main }>
<span className="ladda-label">
{ translate('ADD_COIN.ADD_ANOTHER_COIN') }
</span>
</button>
</div>
</div>
<div className={ this.state.activeLoginSection === 'activateCoin' ? 'show' : 'hide' }>
<h4 className="color-white">
{ translate('INDEX.WELCOME_PLEASE_ADD') }
@ -57,6 +212,131 @@ const LoginRender = function () {
</button>
</div>
</div>
<div className={ this.state.activeLoginSection === 'signup' ? 'show' : 'hide' }>
<div className="register-form">
<h4 className="hint color-white">
{ translate('INDEX.SELECT_SEED_TYPE') }:
</h4>
<div className="row">
<div className="col-sm-5 horizontal-padding-0">
<div className="toggle-box vertical-padding-20">
<span className="pointer">
<label className="switch">
<input
type="checkbox"
checked={ this.isCustomWalletSeed() } />
<div
className="slider"
onClick={ () => this.toggleCustomWalletSeed() }></div>
</label>
<div
className="toggle-label white"
onClick={ () => this.toggleCustomWalletSeed() }>
{ translate('LOGIN.CUSTOM_WALLET_SEED') }
</div>
</span>
</div>
</div>
<div className="col-sm-7 horizontal-padding-0">
{ !this.isCustomWalletSeed() &&
<div>
<div className="form-group form-material floating">
<div
className="radio-custom radio-default radio-inline"
onClick={ () =>this.state.bitsOption !== 256 && this.generateNewSeed(256) }>
<input
type="radio"
name="PassPhraseOptions"
checked={ this.state.bitsOption === 256 }
readOnly />
<label htmlFor="PassPhraseOptionsIguana">
{ translate('LOGIN.IGUANA_SEED') }
</label>
</div>
<div
className="radio-custom radio-default radio-inline"
onClick={ () => this.state.bitsOption !== 160 && this.generateNewSeed(160) }>
<input
type="radio"
name="PassPhraseOptions"
checked={ this.state.bitsOption === 160 }
readOnly />
<label htmlFor="PassPhraseOptionsWaves">
{ translate('LOGIN.WAVES_SEED') }
</label>
</div>
<div
className="radio-custom radio-default radio-inline"
onClick={ () => this.state.bitsOption !== 128 && this.generateNewSeed(128) }>
<input
type="radio"
name="PassPhraseOptions"
checked={ this.state.bitsOption === 128 }
readOnly />
<label htmlFor="PassPhraseOptionsNXT">
{ translate('LOGIN.NXT_SEED') }
</label>
</div>
</div>
</div>
}
</div>
</div>
<div className="form-group form-material floating seed-tooltip">
<textarea
className="form-control placeholder-no-fix height-100"
type="text"
id="walletseed"
value={ this.state.randomSeed }
onChange={ (e) => this.updateWalletSeed(e) }
readOnly={ !this.isCustomWalletSeed() }></textarea>
<button className="copy-floating-label"
htmlFor="walletseed"
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 />
{ translate('INDEX.YOUR_SEED_MUST_CONTAIN1') }<br />
{ translate('INDEX.YOUR_SEED_MUST_CONTAIN2') }<br />
{ translate('INDEX.YOUR_SEED_MUST_CONTAIN3') }<br />
{ translate('INDEX.YOUR_SEED_MUST_CONTAIN4') }
</span>
<label
className="floating-label"
htmlFor="walletseed">{ translate('INDEX.WALLET_SEED') }</label>
</div>
<div className="form-group form-material floating">
<textarea
className="form-control placeholder-no-fix height-100"
type="text"
name="randomSeedConfirm"
value={ this.state.randomSeedConfirm }
onChange={ this.updateRegisterConfirmPassPhraseInput }
id="rwalletseed"></textarea>
<span className={ this.state.isSeedBlank ? 'help-block' : 'hide' }>
{ translate('LOGIN.MUST_ENTER_SEED') }.
</span>
<span className={ this.state.isSeedConfirmError ? 'help-block' : 'hide' }>
{ translate('LOGIN.ENTER_VALUE_AGAIN') }.
</span>
<label
className="floating-label"
htmlFor="rwalletseed">{ translate('INDEX.CONFIRM_SEED') }</label>
</div>
<button
type="button"
className="btn btn-primary btn-block"
onClick={ this.handleRegisterWallet }>{ translate('INDEX.REGISTER') }</button>
<div className="form-group form-material floating">
<button
className="btn btn-lg btn-flat btn-block waves-effect"
id="register-back-btn"
onClick={ () => this.updateActiveLoginSection('login') }>{ translate('INDEX.BACK_TO_LOGIN') }</button>
</div>
</div>
</div>
</div>
</div>
</div>

98
react/src/components/login/login.scss

@ -6,6 +6,38 @@
display: block;
}
.swal2-modal-container {
display: block;
width: 500px;
padding: 20px;
margin-left: -250px;
background: rgb(255, 255, 255);
margin-top: -230px;
}
button {
&.copy-floating-label {
position: absolute;
font-weight: 500;
font-size: 14px;
top: -22px;
right: 0;
background-color: #757575;
border: 0;
cursor: pointer;
z-index: 1000;
}
&.swal2-confirm-container {
background-color: rgb(48, 133, 214);
border-left-color: rgb(48, 133, 214);
border-right-color: rgb(48, 133, 214);
}
&.swal2-cancel-container {
display: inline-block;
background-color: rgb(221, 51, 51);
}
}
.vertical-padding-20 {
padding-top: 20px;
padding-bottom: 20px;
@ -16,6 +48,10 @@
padding-right: 0;
}
.browser-usage-container {
color: #424242;
}
.width-540 {
width: 540px;
}
@ -25,11 +61,73 @@
margin-bottom: 30px;
}
textarea {
&.height-100 {
height: 100px;
}
}
.seed-type-block {
position: absolute;
left: 100%;
min-width: 50px;
.placeholder-label {
border-radius: 4px;
padding: 3px;
background: #61BD4F;
color: #fff;
}
}
#loginPassphrase,
#wifkeysPassphraseTextarea,
input[type="password"] {
width: calc(100% - 35px);
}
#wifkeysPassphraseTextarea,
#loginPassphrase {
overflow: hidden;
margin-top: 5px;
}
.seed-tooltip {
.tooltiptext {
width: 230px;
padding: 10px 20px;
background-color: black;
color: #fff;
text-align: left;
border-radius: 6px;
position: absolute;
z-index: 1;
top: -5px;
left: 105%;
&::after {
content: '';
position: absolute;
top: 50%;
right: 100%;
margin-top: -5px;
border-width: 5px;
border-style: solid;
border-color: transparent black transparent transparent;
}
}
}
.auto-side-margin {
margin-left: auto;
margin-right: auto;
}
.register-form {
.floating-label {
font-size: 13px;
}
}
.login-settings-dropdown {
font-size: 16px;

24
react/src/components/login/swall-modal.render.js

@ -0,0 +1,24 @@
import React from 'react';
import { translate } from '../../translate/translate';
const SwallModalRender = function() {
return (
<div className="swal2-container">
<div className="swal2-overlay full-opacity display-block"></div>
<div className="swal2-modal show-swal2 visible swal2-modal-container">
<div className="swal2-icon swal2-warning pulse-warning display-block">!</div>
<h2>{ translate('LOGIN.SAVED_WALLET_SEED') }</h2>
<div className="swal2-content display-block">{ translate('LOGIN.SEED_MAKE_SURE_BACKUP') }</div>
<hr className="swal2-spacer display-block" />
<button
className="swal2-confirm styled swal2-confirm-container"
onClick={ this.execWalletCreate }>{ translate('LOGIN.YES_I_BACKUP') }</button>
<button
className="swal2-cancel styled swal2-cancel-container"
onClick={ this.toggleSeedBackupModal }>{ translate('LOGIN.CANCEL') }</button>
</div>
</div>
);
};
export default SwallModalRender;

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

@ -4,7 +4,9 @@ import Store from '../../store';
import { translate } from '../../translate/translate';
import {
getDexCoins,
activeHandle,
triggerToaster,
shepherdElectrumCoins,
} from '../../actions/actionCreators';
class Main extends React.Component {
@ -71,6 +73,8 @@ class Main extends React.Component {
componentWillMount() {
Store.dispatch(getDexCoins());
Store.dispatch(activeHandle());
Store.dispatch(shepherdElectrumCoins());
}
render() {

12
react/src/reducers/activeCoin.js

@ -15,6 +15,8 @@ import {
DASHBOARD_ACTIVE_COIN_GETINFO_FAILURE,
SYNCING_NATIVE_MODE,
DASHBOARD_UPDATE,
DASHBOARD_ELECTRUM_BALANCE,
DASHBOARD_ELECTRUM_TRANSACTIONS,
} from '../actions/storeType';
// TODO: refactor current coin props copy on change
@ -134,6 +136,16 @@ export function ActiveCoin(state = {
};
}
}
case DASHBOARD_ELECTRUM_BALANCE:
return {
...state,
balance: action.balance,
};
case DASHBOARD_ELECTRUM_TRANSACTIONS:
return {
...state,
txhistory: action.txhistory,
};
case DASHBOARD_ACTIVE_COIN_BALANCE:
return {
...state,

7
react/src/reducers/dashboard.js

@ -5,6 +5,7 @@ import {
DISPLAY_CLAIM_INTEREST_MODAL,
DASHBOARD_SYNC_ONLY_UPDATE,
DISPLAY_IMPORT_KEY_MODAL,
DASHBOARD_ELECTRUM_COINS,
} from '../actions/storeType';
export function Dashboard(state = {
@ -14,8 +15,14 @@ export function Dashboard(state = {
displayClaimInterestModal: false,
skipFullDashboardUpdate: false,
displayImportKeyModal: false,
electrumCoins: {},
}, action) {
switch (action.type) {
case DASHBOARD_ELECTRUM_COINS:
return {
...state,
electrumCoins: action.electrumCoins,
};
case DASHBOARD_SECTION_CHANGE:
return {
...state,

2
react/src/reducers/index.js

@ -8,6 +8,7 @@ import { Dashboard } from './dashboard';
import { ActiveCoin } from './activeCoin';
import { Settings } from './settings';
import { Interval } from './interval';
import { Login } from './login';
const appReducer = combineReducers({
AddCoin,
@ -17,6 +18,7 @@ const appReducer = combineReducers({
ActiveCoin,
Settings,
Interval,
Login,
routing: routerReducer,
});

18
react/src/reducers/login.js

@ -0,0 +1,18 @@
import { GET_PIN_LIST } from '../actions/storeType';
export function Login(state = {
pinList: [],
}, action) {
if (state === null) state = { pinList: [] };
switch (action.type) {
case GET_PIN_LIST:
return Object.assign({}, state, {
pinList: action.pinList,
});
default:
return state;
}
}
export default Login;

18
react/src/reducers/main.js

@ -1,11 +1,15 @@
import {
GET_ACTIVE_COINS,
LOGIN,
ACTIVE_HANDLE,
DISPLAY_LOGIN_SETTINGS_MODAL
} from '../actions/storeType';
export function Main(state = {
isLoggedIn: false,
activeCoins: [],
displayLoginSettingsModal: false,
total: 0,
}, action) {
switch (action.type) {
case GET_ACTIVE_COINS:
@ -13,6 +17,18 @@ export function Main(state = {
...state,
activeCoins: action.activeCoins,
coins: action.coins,
total: action.total,
};
case LOGIN:
return {
...state,
isLoggedIn: action.isLoggedIn,
};
case ACTIVE_HANDLE:
return {
...state,
isLoggedIn: action.isLoggedIn,
activeHandle: action.handle,
};
case DISPLAY_LOGIN_SETTINGS_MODAL:
return {
@ -24,4 +40,4 @@ export function Main(state = {
}
}
export default Main;
export default Main;

1
react/src/translate/en.js

@ -63,6 +63,7 @@ export const _lang = {
'NO_ACTIVE_COIN': 'No active coin',
},
'INDEX': {
'SPV_MODE': 'SPV Mode',
'PROGRESS_RESCANNING_BLOCKS': '(rescanning blocks)',
'BLOCKS': 'Blocks',
'PLEASE_WAIT_UNTIL_RESCAN_FINISHED': 'Please wait until rescan process is finished',

6
react/src/util/coinHelper.js

@ -371,9 +371,9 @@ export function getModeInfo(mode) {
modetip = 'Native';
modecolor = 'primary';
break;
case 'basilisk':
modecode = 'Basilisk';
modetip = 'Basilisk';
case 'spv':
modecode = 'SPV';
modetip = 'SPV';
modecolor = 'info';
break;
case 'full':

6
react/src/util/crypto/passphrasegenerator.js

@ -1,5 +1,5 @@
/******************************************************************************
* Copyright © 2016 The Waves Core Developers. *
* Copyright © 2016 The Waves Core Developers. *
* *
* See the LICENSE files at *
* the top-level directory of this distribution for the individual copyright *
@ -14,10 +14,6 @@
* *
******************************************************************************/
/**
* @depends {../3rdparty/jquery-2.1.0.js}
*/
import { ClientWordList } from './wordlist.js';
export const PassPhraseGenerator = {

Loading…
Cancel
Save