After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 2.5 KiB |
After Width: | Height: | Size: 4.7 KiB |
After Width: | Height: | Size: 3.4 KiB |
After Width: | Height: | Size: 3.6 KiB |
After Width: | Height: | Size: 2.5 KiB |
@ -1,61 +0,0 @@ |
|||
import { LOG_GUI_HTTP } from '../storeType'; |
|||
import { triggerToaster } from '../actionCreators'; |
|||
import Config from '../../config'; |
|||
|
|||
export function logGuiHttp(payload) { |
|||
return dispatch => { |
|||
dispatch(guiLogState(payload)); |
|||
|
|||
// disabled for now
|
|||
/*return fetch(`http://127.0.0.1:${Config.agamaPort}/shepherd/guilog`, {
|
|||
method: 'POST', |
|||
headers: { |
|||
'Content-Type': 'application/json', |
|||
}, |
|||
body: JSON.stringify(payload), |
|||
}) |
|||
.catch(function(error) { |
|||
console.log(error); |
|||
dispatch(triggerToaster('logGuiHttp', 'Error', 'error')); |
|||
}) |
|||
.then(response => response.json())*/ |
|||
} |
|||
} |
|||
|
|||
export function getAgamaLog(type) { |
|||
return dispatch => { |
|||
return fetch(`http://127.0.0.1:${Config.agamaPort}/shepherd/getlog?type=${type}`, { |
|||
method: 'GET', |
|||
headers: { |
|||
'Content-Type': 'application/json', |
|||
}, |
|||
}) |
|||
.catch(function(error) { |
|||
console.log(error); |
|||
dispatch( |
|||
triggerToaster( |
|||
'getAgamaLog', |
|||
'Error', |
|||
'error' |
|||
) |
|||
); |
|||
}) |
|||
.then(response => response.json()) |
|||
} |
|||
} |
|||
|
|||
export function guiLogState(logData) { |
|||
return { |
|||
type: LOG_GUI_HTTP, |
|||
timestamp: logData.timestamp, |
|||
log: { |
|||
timestamp: logData.timestamp, |
|||
function: logData.function, |
|||
httpMethod: logData.type, |
|||
url: logData.url, |
|||
payload: logData.payload, |
|||
status: logData.status, |
|||
response: logData.response, |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,86 @@ |
|||
import { |
|||
triggerToaster, |
|||
} from '../actionCreators'; |
|||
import Config from '../../config'; |
|||
import { DASHBOARD_UPDATE } from '../storeType'; |
|||
|
|||
export function getDashboardUpdate(coin, activeCoinProps) { |
|||
return dispatch => { |
|||
const _fetchConfig = { |
|||
method: 'POST', |
|||
headers: { |
|||
'Content-Type': 'application/json', |
|||
}, |
|||
body: JSON.stringify({ coin: coin }), |
|||
}; |
|||
|
|||
return fetch( |
|||
`http://127.0.0.1:${Config.agamaPort}/shepherd/native/dashboard/update`, |
|||
_fetchConfig |
|||
) |
|||
.catch(function(error) { |
|||
console.log(error); |
|||
dispatch( |
|||
triggerToaster( |
|||
'getDashboardUpdate', |
|||
'Error', |
|||
'error' |
|||
) |
|||
); |
|||
}) |
|||
.then(response => response.json()) |
|||
.then(json => { |
|||
dispatch(getDashboardUpdateState(json, coin)); |
|||
|
|||
// dirty hack to trigger dashboard render
|
|||
if (!activeCoinProps.balance && |
|||
!activeCoinProps.addresses) { |
|||
setTimeout(() => { |
|||
dispatch(getDashboardUpdateState(json, coin)); |
|||
}, 100); |
|||
} |
|||
}) |
|||
} |
|||
} |
|||
|
|||
export function getDashboardUpdateState(json, coin, fakeResponse) { |
|||
// rescan or similar resource heavy process
|
|||
if (fakeResponse || |
|||
((json.result['getinfo'].error && json.result['getinfo'].error === 'daemon is busy') && |
|||
(json.result['z_getoperationstatus'].error && json.result['z_getoperationstatus'].error === 'daemon is busy') && |
|||
(json.result['listtransactions'].error && json.result['listtransactions'].error === 'daemon is busy') && |
|||
(json.result['listtransactions'].error && json.result['listtransactions'].error === 'daemon is busy'))) { |
|||
return { |
|||
type: DASHBOARD_UPDATE, |
|||
progress: null, |
|||
opids: null, |
|||
txhistory: null, |
|||
balance: null, |
|||
addresses: null, |
|||
coin: coin, |
|||
rescanInProgress: true, |
|||
}; |
|||
} else { |
|||
let _listtransactions = json.result['listtransactions']; |
|||
|
|||
if (_listtransactions && |
|||
_listtransactions.error) { |
|||
_listtransactions = null; |
|||
} else if (_listtransactions && _listtransactions.result && _listtransactions.result.length) { |
|||
_listtransactions = _listtransactions.result; |
|||
} else if (!_listtransactions || (!_listtransactions.result || !_listtransactions.result.length)) { |
|||
_listtransactions = 'no data'; |
|||
} |
|||
|
|||
return { |
|||
type: DASHBOARD_UPDATE, |
|||
progress: json.result['getinfo'].result, |
|||
opids: json.result['z_getoperationstatus'].result, |
|||
txhistory: _listtransactions, |
|||
balance: json.result['z_gettotalbalance'].result, |
|||
addresses: json.result['addresses'], |
|||
coin: coin, |
|||
rescanInProgress: false, |
|||
}; |
|||
} |
|||
} |
@ -1,223 +0,0 @@ |
|||
import { |
|||
DASHBOARD_CONNECT_NOTARIES, |
|||
DASHBOARD_GET_NOTARIES_LIST |
|||
} from '../storeType'; |
|||
import { translate } from '../../translate/translate'; |
|||
import { triggerToaster } from '../actionCreators'; |
|||
import Config from '../../config'; |
|||
import { |
|||
logGuiHttp, |
|||
guiLogState |
|||
} from './log'; |
|||
|
|||
function initNotaryNodesConSequence(nodes) { |
|||
return dispatch => { |
|||
Promise.all(nodes.map((node, index) => { |
|||
const payload = { |
|||
userpass: `tmpIgRPCUser@${sessionStorage.getItem('IguanaRPCAuth')}`, |
|||
agent: 'dex', |
|||
method: 'getinfo', |
|||
symbol: node, |
|||
timeout: 10000, |
|||
}; |
|||
|
|||
return new Promise((resolve, reject) => { |
|||
const _timestamp = Date.now(); |
|||
if (Config.debug) { |
|||
dispatch(logGuiHttp({ |
|||
timestamp: _timestamp, |
|||
function: `initNotaryNodesConSequence+${node}`, |
|||
type: 'post', |
|||
url: `http://127.0.0.1:${Config.iguanaCorePort}`, |
|||
payload: payload, |
|||
status: 'pending', |
|||
})); |
|||
} |
|||
|
|||
fetch(`http://127.0.0.1:${(Config.useBasiliskInstance ? Config.iguanaCorePort + 1 : Config.iguanaCorePort)}/api/dex/getinfo?userpass=${('tmpIgRPCUser@' + sessionStorage.getItem('IguanaRPCAuth'))}&symbol=${node}`, { |
|||
method: 'GET', |
|||
}) |
|||
.catch(function(error) { |
|||
console.log(error); |
|||
if (Config.debug) { |
|||
dispatch(logGuiHttp({ |
|||
timestamp: _timestamp, |
|||
status: 'error', |
|||
response: error, |
|||
})); |
|||
} |
|||
dispatch( |
|||
triggerToaster( |
|||
`getInfoDexNode+${node}`, |
|||
'Error', |
|||
'error' |
|||
) |
|||
); |
|||
}) |
|||
.then(response => response.json()) |
|||
.then(json => { |
|||
if (Config.debug) { |
|||
dispatch(logGuiHttp({ |
|||
timestamp: _timestamp, |
|||
status: 'success', |
|||
response: json, |
|||
})); |
|||
} |
|||
dispatch( |
|||
updateNotaryNodeConState( |
|||
json, |
|||
nodes.length, |
|||
index, |
|||
node |
|||
) |
|||
); |
|||
}) |
|||
}); |
|||
})); |
|||
} |
|||
} |
|||
|
|||
function updateNotaryNodeConState(json, totalNodes, currentNodeIndex, currentNodeName) { |
|||
if (currentNodeIndex === totalNodes - 1) { |
|||
return dispatch => { |
|||
dispatch(basiliskConnectionState(false)); |
|||
}; |
|||
} else { |
|||
if (json && |
|||
json.error === 'less than required responses') { |
|||
return { |
|||
type: DASHBOARD_CONNECT_NOTARIES, |
|||
total: totalNodes - 1, |
|||
current: currentNodeIndex, |
|||
name: currentNodeName, |
|||
failedNode: currentNodeName, |
|||
} |
|||
} else { |
|||
return { |
|||
type: DASHBOARD_CONNECT_NOTARIES, |
|||
total: totalNodes - 1, |
|||
current: currentNodeIndex, |
|||
name: currentNodeName, |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
function connectAllNotaryNodes(json, dispatch) { |
|||
if (json && |
|||
json.length) { |
|||
dispatch(initNotaryNodesConSequence(json)); |
|||
|
|||
return { |
|||
type: DASHBOARD_CONNECT_NOTARIES, |
|||
total: json.length - 1, |
|||
current: 0, |
|||
name: json[0], |
|||
} |
|||
} |
|||
} |
|||
|
|||
export function connectNotaries() { |
|||
const payload = { |
|||
userpass: `tmpIgRPCUser@${sessionStorage.getItem('IguanaRPCAuth')}`, |
|||
agent: 'dpow', |
|||
method: 'notarychains', |
|||
}; |
|||
|
|||
return dispatch => { |
|||
return fetch(`http://127.0.0.1:${Config.iguanaCorePort}`, { |
|||
method: 'POST', |
|||
body: JSON.stringify(payload), |
|||
}) |
|||
.catch(function(error) { |
|||
console.log(error); |
|||
dispatch( |
|||
triggerToaster( |
|||
'connectNotaries', |
|||
'Error', |
|||
'error' |
|||
) |
|||
); |
|||
}) |
|||
.then(response => response.json()) |
|||
.then( |
|||
json => dispatch( |
|||
connectAllNotaryNodes(json, dispatch) |
|||
) |
|||
) |
|||
} |
|||
} |
|||
|
|||
function getDexNotariesState(json) { |
|||
if (json.error === 'less than required responses') { |
|||
return dispatch => { |
|||
dispatch( |
|||
triggerToaster( |
|||
translate('TOASTR.LESS_RESPONSES_REQ'), |
|||
translate('TOASTR.BASILISK_NOTIFICATION'), |
|||
'error' |
|||
) |
|||
); |
|||
} |
|||
} else { |
|||
return { |
|||
type: DASHBOARD_GET_NOTARIES_LIST, |
|||
notaries: json, |
|||
} |
|||
} |
|||
} |
|||
|
|||
export function getDexNotaries(coin) { |
|||
const payload = { |
|||
userpass: `tmpIgRPCUser@${sessionStorage.getItem('IguanaRPCAuth')}`, |
|||
agent: 'dex', |
|||
method: 'getnotaries', |
|||
symbol: coin, |
|||
}; |
|||
|
|||
return dispatch => { |
|||
const _timestamp = Date.now(); |
|||
if (Config.debug) { |
|||
dispatch(logGuiHttp({ |
|||
timestamp: _timestamp, |
|||
function: 'getDexNotaries', |
|||
type: 'post', |
|||
url: `http://127.0.0.1:${Config.useBasiliskInstance ? Config.iguanaCorePort + 1 : Config.iguanaCorePort}`, |
|||
payload: payload, |
|||
status: 'pending', |
|||
})); |
|||
} |
|||
return fetch(`http://127.0.0.1:${Config.useBasiliskInstance ? Config.iguanaCorePort + 1 : Config.iguanaCorePort}`, { |
|||
method: 'POST', |
|||
body: JSON.stringify(payload), |
|||
}) |
|||
.catch(function(error) { |
|||
console.log(error); |
|||
if (Config.debug) { |
|||
dispatch(logGuiHttp({ |
|||
timestamp: _timestamp, |
|||
status: 'error', |
|||
response: error, |
|||
})); |
|||
} |
|||
dispatch( |
|||
triggerToaster( |
|||
'getDexNotaries', |
|||
'Error', |
|||
'error' |
|||
) |
|||
); |
|||
}) |
|||
.then(response => response.json()) |
|||
.then(json => { |
|||
if (Config.debug) { |
|||
dispatch(logGuiHttp({ |
|||
timestamp: _timestamp, |
|||
status: 'success', |
|||
response: json, |
|||
})); |
|||
} |
|||
dispatch(getDexNotariesState(json)); |
|||
}) |
|||
} |
|||
} |
@ -0,0 +1,47 @@ |
|||
.modal-claim-interest { |
|||
.modal-dialog { |
|||
width: 70%; |
|||
|
|||
.table > tbody > tr > td, |
|||
.table > tbody > tr > th, |
|||
.table > tfoot > tr > td, |
|||
.table > tfoot > tr > th, |
|||
.table > thead > tr > td, |
|||
.table > thead > tr > th { |
|||
padding: 8px 30px 8px 0; |
|||
} |
|||
|
|||
.claim-btn { |
|||
float: right; |
|||
margin-bottom: 30px; |
|||
} |
|||
.table-scroll { |
|||
height: 366px; |
|||
overflow-y: auto; |
|||
overflow-x: hidden; |
|||
width: 100%; |
|||
} |
|||
.bold { |
|||
font-weight: bold; |
|||
} |
|||
.green { |
|||
color: #66bb6a; |
|||
} |
|||
.red { |
|||
color: #f96868; |
|||
} |
|||
.locktime { |
|||
i { |
|||
font-size: 20px; |
|||
line-height: 1.1; |
|||
} |
|||
} |
|||
|
|||
.refresh-icon { |
|||
position: absolute; |
|||
right: 20px; |
|||
font-size: 20px; |
|||
z-index: 100; |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,24 @@ |
|||
.coind-down-modal { |
|||
.modal-body { |
|||
height: 60vh; |
|||
|
|||
> div { |
|||
height: 100%; |
|||
} |
|||
.form-group { |
|||
&.form-material { |
|||
&.floating { |
|||
height: 80%; |
|||
} |
|||
} |
|||
} |
|||
.page-content { |
|||
width: 90%; |
|||
height: 100%; |
|||
|
|||
textarea { |
|||
height: 100%; |
|||
} |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,243 @@ |
|||
import React from 'react'; |
|||
import { connect } from 'react-redux'; |
|||
import ReactDOM from 'react-dom'; |
|||
import Store from '../../../store'; |
|||
import { |
|||
displayImportKeyModal, |
|||
importPrivkey, |
|||
triggerToaster, |
|||
copyCoinAddress, |
|||
getDebugLog, |
|||
getDashboardUpdateState |
|||
} from '../../../actions/actionCreators'; |
|||
import { translate } from '../../../translate/translate'; |
|||
import { |
|||
ImportKeyModalRender, |
|||
} from './importKeyModal.render'; |
|||
|
|||
// import gen komodo keys utils
|
|||
import '../../../util/crypto/gen/array.map.js'; |
|||
import '../../../util/crypto/gen/cryptojs.js'; |
|||
import '../../../util/crypto/gen/cryptojs.sha256.js'; |
|||
import '../../../util/crypto/gen/cryptojs.pbkdf2.js'; |
|||
import '../../../util/crypto/gen/cryptojs.hmac.js'; |
|||
import '../../../util/crypto/gen/cryptojs.aes.js'; |
|||
import '../../../util/crypto/gen/cryptojs.blockmodes.js'; |
|||
import '../../../util/crypto/gen/cryptojs.ripemd160.js'; |
|||
import '../../../util/crypto/gen/securerandom.js'; |
|||
import '../../../util/crypto/gen/ellipticcurve.js'; |
|||
import '../../../util/crypto/gen/biginteger.js'; |
|||
import '../../../util/crypto/gen/crypto-scrypt.js'; |
|||
import { Bitcoin } from '../../../util/crypto/gen/bitcoin.js'; |
|||
|
|||
class ImportKeyModal extends React.Component { |
|||
constructor() { |
|||
super(); |
|||
this.state = { |
|||
open: false, |
|||
wif: '', |
|||
wifkeysPassphrase: '', |
|||
passphraseWif: null, |
|||
passphraseAddress: null, |
|||
keyImportResult: null, |
|||
importWithRescan: false, |
|||
seedInputVisibility: false, |
|||
toggleSeedInputVisibility: false, |
|||
trimPassphraseTimer: null, |
|||
}; |
|||
this.generateKeysFromPassphrase = this.generateKeysFromPassphrase.bind(this); |
|||
this.toggleImportWithRescan = this.toggleImportWithRescan.bind(this); |
|||
this.toggleSeedInputVisibility = this.toggleSeedInputVisibility.bind(this); |
|||
this._copyCoinAddress = this._copyCoinAddress.bind(this); |
|||
this.showPassphraseAddress = this.showPassphraseAddress.bind(this); |
|||
this.importWifAddress = this.importWifAddress.bind(this); |
|||
this.updateInput = this.updateInput.bind(this); |
|||
this.importFromPassphrase = this.importFromPassphrase.bind(this); |
|||
this.importFromWif = this.importFromWif.bind(this); |
|||
} |
|||
|
|||
_copyCoinAddress(address) { |
|||
Store.dispatch(copyCoinAddress(address)); |
|||
} |
|||
|
|||
updateInput(e) { |
|||
if (e.target.name === 'wifkeysPassphrase') { |
|||
// 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({ |
|||
wifkeysPassphrase: newValue ? newValue.trim() : '', // hardcoded field name
|
|||
}); |
|||
}, 2000); |
|||
|
|||
this.resizeLoginTextarea(); |
|||
|
|||
this.setState({ |
|||
trimPassphraseTimer: _trimPassphraseTimer, |
|||
[e.target.name]: newValue, |
|||
}); |
|||
} else { |
|||
this.setState({ |
|||
[e.target.name]: e.target.value, |
|||
}); |
|||
} |
|||
} |
|||
|
|||
resizeLoginTextarea() { |
|||
// auto-size textarea
|
|||
setTimeout(() => { |
|||
if (this.state.seedInputVisibility) { |
|||
document.querySelector('#wifkeysPassphraseTextarea').style.height = '1px'; |
|||
document.querySelector('#wifkeysPassphraseTextarea').style.height = `${(15 + document.querySelector('#wifkeysPassphraseTextarea').scrollHeight)}px`; |
|||
} |
|||
}, 100); |
|||
} |
|||
|
|||
toggleSeedInputVisibility() { |
|||
this.setState({ |
|||
seedInputVisibility: !this.state.seedInputVisibility, |
|||
}); |
|||
} |
|||
|
|||
showPassphraseAddress() { |
|||
const _address = this.generateKeysFromPassphrase(); |
|||
|
|||
if (_address) { |
|||
this.setState({ |
|||
passphraseAddress: _address.address, |
|||
passphraseWif: _address.wif, |
|||
}); |
|||
} |
|||
} |
|||
|
|||
importFromPassphrase() { |
|||
const _address = this.generateKeysFromPassphrase(); |
|||
|
|||
if (_address) { |
|||
this.importWifAddress(_address.wif, true); |
|||
} |
|||
} |
|||
|
|||
importFromWif() { |
|||
this.importWifAddress(this.state.wif, this.state.importWithRescan); |
|||
} |
|||
|
|||
importWifAddress(wif, rescan) { |
|||
let _rescanInProgress = true; |
|||
|
|||
if (rescan) { |
|||
setTimeout(() => { |
|||
if (_rescanInProgress) { |
|||
setTimeout(() => { |
|||
if (this.props.ActiveCoin.coin === 'KMD') { |
|||
Store.dispatch(getDebugLog('komodo', 100)); |
|||
} else { |
|||
Store.dispatch(getDebugLog('komodo', 100, this.props.ActiveCoin.coin)); |
|||
} |
|||
}, 2000); |
|||
|
|||
Store.dispatch(getDashboardUpdateState(null, this.props.ActiveCoin.coin, true)); |
|||
Store.dispatch( |
|||
triggerToaster( |
|||
'Address imported. Wallet rescan is in progress. Please wait until it is finished.', |
|||
translate('TOASTR.WALLET_NOTIFICATION'), |
|||
'info', |
|||
false |
|||
) |
|||
); |
|||
this.closeModal(); |
|||
} |
|||
}, 2000); |
|||
} |
|||
|
|||
importPrivkey( |
|||
this.props.ActiveCoin.coin, |
|||
wif, |
|||
rescan |
|||
).then((json) => { |
|||
_rescanInProgress = false; |
|||
|
|||
if (!json.id && |
|||
!json.result && |
|||
!json.error) { |
|||
// console.warn('importPrivkey', json);
|
|||
Store.dispatch( |
|||
triggerToaster( |
|||
rescan ? 'Wallet rescan finished' : 'Address imported', |
|||
translate('TOASTR.WALLET_NOTIFICATION'), |
|||
'success', |
|||
false |
|||
) |
|||
); |
|||
} else { |
|||
Store.dispatch( |
|||
triggerToaster( |
|||
json.error.message, |
|||
'Error', |
|||
'error' |
|||
) |
|||
); |
|||
} |
|||
}); |
|||
} |
|||
|
|||
generateKeysFromPassphrase() { |
|||
if (this.state.wifkeysPassphrase && |
|||
this.state.wifkeysPassphrase.length) { |
|||
const bytes = Crypto.SHA256(this.state.wifkeysPassphrase, { asBytes: true }); |
|||
// byte flipping to make it compat with iguana core
|
|||
bytes[0] &= 248; |
|||
bytes[31] &= 127; |
|||
bytes[31] |= 64; |
|||
const btcKey = new Bitcoin.ECKey(bytes).setCompressed(true); |
|||
const kmdAddress = btcKey.getBitcoinAddress(); |
|||
const wifAddress = btcKey.getBitcoinWalletImportFormat(); |
|||
|
|||
return { |
|||
address: kmdAddress, |
|||
wif: wifAddress, |
|||
}; |
|||
} else { |
|||
Store.dispatch( |
|||
triggerToaster( |
|||
'Empty passphrase field', |
|||
'Error', |
|||
'error' |
|||
) |
|||
); |
|||
|
|||
return null; |
|||
} |
|||
} |
|||
|
|||
toggleImportWithRescan() { |
|||
this.setState({ |
|||
importWithRescan: !this.state.importWithRescan, |
|||
}); |
|||
} |
|||
|
|||
closeModal() { |
|||
Store.dispatch(displayImportKeyModal(false)); |
|||
} |
|||
|
|||
render() { |
|||
return ImportKeyModalRender.call(this); |
|||
} |
|||
} |
|||
|
|||
const mapStateToProps = (state) => { |
|||
return { |
|||
ActiveCoin: { |
|||
coin: state.ActiveCoin.coin, |
|||
balance: state.ActiveCoin.balance, |
|||
}, |
|||
Dashboard: { |
|||
displayImportKeyModal: state.Dashboard.displayImportKeyModal, |
|||
}, |
|||
}; |
|||
}; |
|||
|
|||
export default connect(mapStateToProps)(ImportKeyModal); |
@ -0,0 +1,136 @@ |
|||
import React from 'react'; |
|||
import { translate } from '../../../translate/translate'; |
|||
|
|||
/*export const _ClaimInterestTableRender = function() { |
|||
};*/ |
|||
|
|||
export const ImportKeyModalRender = function() { |
|||
return ( |
|||
<span> |
|||
<div className={ 'modal modal-import-key modal-3d-sign ' + (this.props.Dashboard.displayImportKeyModal ? 'show in' : 'fade hide') }> |
|||
<div className="modal-dialog modal-center modal-sm"> |
|||
<div className="modal-content"> |
|||
<div className="modal-header bg-orange-a400 wallet-send-header"> |
|||
<button |
|||
type="button" |
|||
className="close white" |
|||
onClick={ this.closeModal }> |
|||
<span>×</span> |
|||
</button> |
|||
<h4 className="modal-title white text-left">Import key</h4> |
|||
</div> |
|||
<div className="modal-body"> |
|||
<div className="padding-bottom-40"> |
|||
Two forms below allow you to import either <strong>Iguana Core / ICO</strong> passphrase (seed) or <strong>WIF (Wallet Import Format)</strong> key. |
|||
</div> |
|||
<div> |
|||
<strong>Passphrase / seed</strong> |
|||
<p className="margin-top-10"> |
|||
<strong>Notice:</strong> importing a passphrase will trigger a full wallet rescan. |
|||
<span className={ this.props.ActiveCoin.coin === 'KMD' ? '' : 'hide' }>This process can take hours to rescan the whole blockchain.</span> |
|||
</p> |
|||
<form |
|||
className="wifkeys-form" |
|||
method="post" |
|||
action="javascript:" |
|||
autoComplete="off"> |
|||
<div className="form-group form-material floating"> |
|||
<input |
|||
type="password" |
|||
className={ !this.state.seedInputVisibility ? 'form-control' : 'hide' } |
|||
name="wifkeysPassphrase" |
|||
id="wifkeysPassphrase" |
|||
onChange={ this.updateInput } |
|||
value={ this.state.wifkeysPassphrase } /> |
|||
<textarea |
|||
className={ this.state.seedInputVisibility ? 'form-control' : 'hide' } |
|||
id="wifkeysPassphraseTextarea" |
|||
name="wifkeysPassphrase" |
|||
onChange={ this.updateInput } |
|||
value={ this.state.wifkeysPassphrase }></textarea> |
|||
<i |
|||
className={ 'seed-toggle fa fa-eye' + (!this.state.seedInputVisibility ? '-slash' : '') } |
|||
onClick={ this.toggleSeedInputVisibility }></i> |
|||
<label |
|||
className="floating-label" |
|||
htmlFor="wifkeysPassphrase">{ translate('INDEX.PASSPHRASE') }</label> |
|||
</div> |
|||
<div className="text-align-center"> |
|||
<button |
|||
type="button" |
|||
className="btn btn-primary waves-effect waves-light margin-right-20" |
|||
onClick={ this.importFromPassphrase }>Import</button> |
|||
<button |
|||
type="button" |
|||
className="btn btn-primary waves-effect waves-light" |
|||
onClick={ this.showPassphraseAddress }>Show address and WIF</button> |
|||
</div> |
|||
</form> |
|||
{ this.state.passphraseAddress && this.state.passphraseWif && |
|||
<div className="margin-top-60"> |
|||
<p> |
|||
<strong>Address: </strong> { this.state.passphraseAddress } |
|||
<button |
|||
className="btn btn-default btn-xs clipboard-edexaddr copy-string-btn" |
|||
title={ translate('INDEX.COPY_TO_CLIPBOARD') } |
|||
onClick={ () => this._copyCoinAddress(this.state.passphraseAddress) }> |
|||
<i className="icon wb-copy"></i> { translate('INDEX.COPY') } |
|||
</button> |
|||
</p> |
|||
<p> |
|||
<strong>WIF: </strong> { this.state.passphraseWif } |
|||
<button |
|||
className="btn btn-default btn-xs clipboard-edexaddr copy-string-btn" |
|||
title={ translate('INDEX.COPY_TO_CLIPBOARD') } |
|||
onClick={ () => this._copyCoinAddress(this.state.passphraseWif) }> |
|||
<i className="icon wb-copy"></i> { translate('INDEX.COPY') } |
|||
</button> |
|||
</p> |
|||
</div> |
|||
} |
|||
</div> |
|||
<div className="line">or</div> |
|||
<div> |
|||
<strong>WIF (Wallet Import Format)</strong> |
|||
<div className="toggle-box padding-top-20"> |
|||
<span className="pointer"> |
|||
<label className="switch"> |
|||
<input |
|||
type="checkbox" |
|||
checked={ this.state.importWithRescan } /> |
|||
<div |
|||
className="slider" |
|||
onClick={ this.toggleImportWithRescan }></div> |
|||
</label> |
|||
<div |
|||
className="toggle-label" |
|||
onClick={ this.toggleImportWithRescan }> |
|||
Trigger rescan |
|||
<i |
|||
className="icon fa-question-circle settings-help" |
|||
title="Use this option if you want to trigger rescan after WIF is imported. If you have several addresses that you want to import add them one by one and toggle this option on the last address import."></i> |
|||
</div> |
|||
</span> |
|||
</div> |
|||
<div className="margin-top-20"> |
|||
<label htmlFor="wif" className="bold">Wif key</label> |
|||
<input |
|||
type="text" |
|||
className="form-control" |
|||
name="wif" |
|||
onChange={ this.updateInput } |
|||
value={ this.state.wif } /> |
|||
</div> |
|||
<button |
|||
type="button" |
|||
className="btn btn-primary waves-effect waves-light margin-top-10" |
|||
onClick={ this.importFromWif }>{ this.state.importWithRescan ? 'Import and rescan' : 'Import' }</button> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div className={ 'modal-backdrop ' + (this.props.Dashboard.displayImportKeyModal ? 'show in' : 'fade hide') }></div> |
|||
</span> |
|||
); |
|||
}; |
@ -0,0 +1,33 @@ |
|||
.modal-import-key { |
|||
.modal-dialog { |
|||
width: 70%; |
|||
max-height: 90vh; |
|||
overflow-y: auto; |
|||
overflow-x: hidden; |
|||
} |
|||
.line { |
|||
padding: 50px 0 40px 0; |
|||
text-transform: uppercase; |
|||
font-weight: bold; |
|||
} |
|||
.line:before { |
|||
content: ''; |
|||
display: inline-block; |
|||
height: 1px; |
|||
width: 47%; |
|||
background: #ccc; |
|||
margin-right: 10px; |
|||
position: relative; |
|||
top: -4px; |
|||
} |
|||
.line:after { |
|||
content: ''; |
|||
display: inline-block; |
|||
height: 1px; |
|||
width: 47%; |
|||
background: #ccc; |
|||
margin-left: 10px; |
|||
position: relative; |
|||
top: -4px; |
|||
} |
|||
} |
@ -0,0 +1,92 @@ |
|||
.jumblr { |
|||
.alert-info { |
|||
width: 100%; |
|||
} |
|||
p { |
|||
width: calc(100% - 100px); |
|||
} |
|||
.breadcrumb { |
|||
padding: 8px 30px; |
|||
position: relative; |
|||
top: 0; |
|||
} |
|||
.img-responsive { |
|||
position: absolute; |
|||
top: -28px; |
|||
right: 18px; |
|||
|
|||
.coin { |
|||
font-size: 30px; |
|||
position: relative; |
|||
left: -18px; |
|||
top: 4px; |
|||
} |
|||
.image { |
|||
width: 60px; |
|||
} |
|||
} |
|||
.header-easydex-section { |
|||
img { |
|||
max-width: inherit; |
|||
} |
|||
} |
|||
.copy-string-btn { |
|||
position: absolute; |
|||
right: 36px; |
|||
margin-top: -68px; |
|||
} |
|||
.btn-next { |
|||
position: absolute; |
|||
top: 60px; |
|||
right: 32px; |
|||
} |
|||
input.labelauty+label, |
|||
input.labelauty+label { |
|||
background: #d6d5d5; |
|||
color: #504e4e; |
|||
} |
|||
input.labelauty:checked+label { |
|||
color: #fff; |
|||
background-color: #3949ab; |
|||
} |
|||
input.labelauty + label:hover .labelauty-unchecked, |
|||
input.labelauty + label .labelauty-unchecked { |
|||
color: #504e4e; |
|||
} |
|||
.nofloat { |
|||
float: none; |
|||
display: inline-block; |
|||
padding-left: 0; |
|||
} |
|||
} |
|||
|
|||
.jumblr-mode-selector { |
|||
.nav-tabs { |
|||
li { |
|||
cursor: pointer; |
|||
|
|||
&.active { |
|||
> a { |
|||
cursor: pointer; |
|||
color: #fff; |
|||
background-color: #62a8ea; |
|||
border-color: transparent; |
|||
border-bottom-color: #62a8ea; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
.panel-heading { |
|||
background: #f3f3f3; |
|||
cursor: pointer; |
|||
} |
|||
.panel-title { |
|||
color: #676767; |
|||
} |
|||
.jumblr-addresses-list { |
|||
.col-xs-3 { |
|||
padding: 0; |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,7 @@ |
|||
.login-settings-modal { |
|||
form { |
|||
width: 100%; |
|||
margin: 0; |
|||
margin-top: 25px; |
|||
} |
|||
} |
@ -1,122 +0,0 @@ |
|||
import React from 'react'; |
|||
import { connect } from 'react-redux'; |
|||
import { sortByDate } from '../../../util/sort'; |
|||
import Config from '../../../config'; |
|||
import { |
|||
NotificationsByTypeRender, |
|||
NotificationsModalRender, |
|||
NotificationsRender |
|||
} from './notifications.render'; |
|||
|
|||
class Notifications extends React.Component { |
|||
constructor() { |
|||
super(); |
|||
this.state = { |
|||
displayModal: false, |
|||
calls: { |
|||
total: 0, |
|||
error: 0, |
|||
success: 0, |
|||
pending: 0, |
|||
}, |
|||
activeTab: 2, |
|||
guiLog: null, |
|||
debug: Config.debug, |
|||
}; |
|||
this.toggleNotificationsModal = this.toggleNotificationsModal.bind(this); |
|||
} |
|||
|
|||
openTab(tab) { |
|||
this.setState(Object.assign({}, this.state, { |
|||
activeTab: tab, |
|||
})); |
|||
} |
|||
|
|||
componentWillReceiveProps(props) { |
|||
// get total number of calls per type
|
|||
if (this.props.Dashboard && |
|||
this.props.Dashboard.guiLog) { |
|||
const _guiLog = this.props.Dashboard.guiLog; |
|||
let _callsLength = { |
|||
total: Object.keys(_guiLog).length, |
|||
error: 0, |
|||
success: 0, |
|||
pending: 0, |
|||
} |
|||
let guiLogToArray = []; |
|||
|
|||
for (let timestamp in _guiLog) { |
|||
guiLogToArray.push(_guiLog[timestamp]); |
|||
_callsLength[_guiLog[timestamp].status]++; |
|||
} |
|||
|
|||
this.setState(Object.assign({}, this.state, { |
|||
calls: { |
|||
total: _callsLength.total, |
|||
error: _callsLength.error, |
|||
success: _callsLength.success, |
|||
pending: _callsLength.pending, |
|||
}, |
|||
guiLog: guiLogToArray, |
|||
})); |
|||
} |
|||
} |
|||
|
|||
renderNotificationsByType(type) { |
|||
// get total number of calls per type
|
|||
if (this.state.guiLog && |
|||
this.state.guiLog.length) { |
|||
let _guiLog = this.state.guiLog; |
|||
let items = []; |
|||
_guiLog = sortByDate(_guiLog); |
|||
|
|||
for (let i = 0; i < _guiLog.length; i++) { |
|||
if (_guiLog[i].status === type) { |
|||
const _logItem = _guiLog[i]; |
|||
|
|||
items.push( |
|||
NotificationsByTypeRender.call( |
|||
this, |
|||
_logItem, |
|||
type, |
|||
i |
|||
) |
|||
); |
|||
} |
|||
} |
|||
|
|||
return items; |
|||
} |
|||
} |
|||
|
|||
renderNotificationsModal() { |
|||
if (this.state.displayModal) { |
|||
return NotificationsModalRender.call(this); |
|||
} |
|||
|
|||
return null; |
|||
} |
|||
|
|||
toggleNotificationsModal() { |
|||
this.setState(Object.assign({}, this.state, { |
|||
displayModal: !this.state.displayModal, |
|||
})); |
|||
} |
|||
|
|||
render() { |
|||
return NotificationsRender.call(this); |
|||
} |
|||
} |
|||
|
|||
const mapStateToProps = (state) => { |
|||
return { |
|||
Dashboard: { |
|||
guiLog: state.Dashboard.guiLog, |
|||
activeHandle: state.Dashboard.activeHandle, |
|||
} |
|||
}; |
|||
|
|||
}; |
|||
|
|||
export default connect(mapStateToProps)(Notifications); |
|||
|
@ -1,116 +0,0 @@ |
|||
import React from 'react'; |
|||
import { |
|||
secondsToString |
|||
} from '../../../util/time'; |
|||
import { translate } from '../../../translate/translate'; |
|||
|
|||
export const NotificationsByTypeRender = function(logItem, type, index) { |
|||
return ( |
|||
<div key={ logItem.timestamp }> |
|||
<div>{ index + 1 }.</div> |
|||
<div> |
|||
<strong>Time:</strong> { secondsToString(logItem.timestamp, true, true) } |
|||
</div> |
|||
<div> |
|||
<strong>GUI Function:</strong> { logItem.function } |
|||
</div> |
|||
<div> |
|||
<strong>HTTP:</strong> { logItem.httpMethod.toUpperCase() } |
|||
</div> |
|||
<div> |
|||
<strong>URL:</strong> { logItem.url } |
|||
</div> |
|||
<div> |
|||
<strong>Payload:</strong> { JSON.stringify(logItem.payload, null, '\t') } |
|||
</div> |
|||
<div className={ type !== 'pending' ? '' : 'hide' }> |
|||
<strong>Response:</strong> { JSON.stringify(logItem.response, null, '\t') } |
|||
</div> |
|||
<hr /> |
|||
</div> |
|||
); |
|||
} |
|||
|
|||
export const NotificationsModalRender = function() { |
|||
return ( |
|||
<div onKeyDown={ (event) => this.handleKeydown(event) }> |
|||
<div className="modal show notifications-modal"> |
|||
<div className="modal-dialog modal-center modal-lg"> |
|||
<div className="modal-content"> |
|||
<div className="modal-body modal-body-container"> |
|||
<div className="panel nav-tabs-horizontal"> |
|||
<ul className="nav nav-tabs nav-tabs-line"> |
|||
<li className={ this.state.activeTab === 0 ? 'active' : 'pointer' }> |
|||
<a onClick={ () => this.openTab(0) }> |
|||
<i className="icon fa fa-thumbs-o-up"></i> Success ({ this.state.calls.success }) |
|||
</a> |
|||
</li> |
|||
<li className={ this.state.activeTab === 1 ? 'active' : 'pointer' }> |
|||
<a onClick={ () => this.openTab(1) }> |
|||
<i className="icon fa fa-exclamation-triangle"></i> Error ({ this.state.calls.error }) |
|||
</a> |
|||
</li> |
|||
<li className={ this.state.activeTab === 2 ? 'active' : 'pointer' }> |
|||
<a onClick={ () => this.openTab(2) }> |
|||
<i className="icon fa fa-clock-o"></i> Pending ({ this.state.calls.pending }) |
|||
</a> |
|||
</li> |
|||
</ul> |
|||
<div className="panel-body panel-body-container"> |
|||
<div className="tab-content"> |
|||
<div className={ 'tab-pane' + (this.state.activeTab === 0 ? ' active' : '') }> |
|||
{ this.renderNotificationsByType('success') } |
|||
</div> |
|||
<div className={ 'tab-pane' + (this.state.activeTab === 1 ? ' active' : '') }> |
|||
{ this.renderNotificationsByType('error') } |
|||
</div> |
|||
<div className={ 'tab-pane' + (this.state.activeTab === 2 ? ' active' : '') }> |
|||
{ this.renderNotificationsByType('pending') } |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div className="modal-footer"> |
|||
<button |
|||
type="button" |
|||
className="btn btn-default" |
|||
onClick={ this.toggleNotificationsModal }> |
|||
{ translate('INDEX.CLOSE') } |
|||
</button> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div className="modal-backdrop show in"></div> |
|||
</div> |
|||
); |
|||
}; |
|||
|
|||
export const NotificationsRender = function() { |
|||
return ( |
|||
<div> |
|||
<div |
|||
className={ 'notifications-badge' + (this.props.Dashboard.activeHandle && this.props.Dashboard.activeHandle.status === 'unlocked' ? ' stick-to-top' : '') } |
|||
onClick={ this.state.debug ? this.toggleNotificationsModal : null }> |
|||
<span className={ this.state.debug ? 'badge success' : 'hide' }> |
|||
{ this.state.calls.success } |
|||
</span> |
|||
<span className={ this.state.debug ? 'badge error' : 'hide' }> |
|||
{ this.state.calls.error } |
|||
</span> |
|||
<span className={ this.state.debug ? 'badge pending' : 'hide' }> |
|||
{ this.state.calls.pending } |
|||
</span> |
|||
<div className={ 'spinner' + (this.state.calls.pending === 0 ? ' spinner-hide' : '') }> |
|||
<div className="rect1"></div> |
|||
<div className="rect2"></div> |
|||
<div className="rect3"></div> |
|||
<div className="rect4"></div> |
|||
<div className="rect5"></div> |
|||
</div> |
|||
</div> |
|||
{ this.renderNotificationsModal() } |
|||
</div> |
|||
); |
|||
}; |
@ -0,0 +1,11 @@ |
|||
.qr-modal-send-block { |
|||
position: absolute; |
|||
top: 15px; |
|||
right: 30px; |
|||
} |
|||
|
|||
.webcam-frame { |
|||
width: 100%; |
|||
text-align: center; |
|||
font-size: 16px; |
|||
} |
@ -0,0 +1,180 @@ |
|||
import React from 'react'; |
|||
import { translate } from '../../../translate/translate'; |
|||
import { connect } from 'react-redux'; |
|||
|
|||
import { |
|||
addPeerNode, |
|||
getPeersList, |
|||
getPeersListState, |
|||
} from '../../../actions/actionCreators'; |
|||
import Store from '../../../store'; |
|||
|
|||
import AddCoinOptionsCrypto from '../../addcoin/addcoinOptionsCrypto'; |
|||
import AddCoinOptionsAC from '../../addcoin/addcoinOptionsAC'; |
|||
import AddCoinOptionsACFiat from '../../addcoin/addcoinOptionsACFiat'; |
|||
|
|||
class AddNodePanel extends React.Component { |
|||
constructor() { |
|||
super(); |
|||
this.state = { |
|||
addNodeCoin: null, |
|||
addPeerIP: null, |
|||
getPeersCoin: null, |
|||
trimPassphraseTimer: null, |
|||
wifkeysPassphrase:'', |
|||
}; |
|||
this.renderSNPeersList = this.renderSNPeersList.bind(this); |
|||
this.renderPeersList = this.renderPeersList.bind(this); |
|||
this.checkNodes = this.checkNodes.bind(this); |
|||
this.addNode = this.addNode.bind(this); |
|||
this.updateInput = this.updateInput.bind(this); |
|||
} |
|||
|
|||
renderSNPeersList() { |
|||
if (this.state.getPeersCoin) { |
|||
const _getPeersCoin = this.state.getPeersCoin; |
|||
const _supernetPeers = this.props.Settings.supernetPeers; |
|||
const coin = _getPeersCoin.split('|')[0]; |
|||
|
|||
if (_supernetPeers && |
|||
_getPeersCoin && |
|||
_supernetPeers[coin]) { |
|||
return _supernetPeers[coin].map((ip) => |
|||
<div key={ ip }>{ ip }</div> |
|||
); |
|||
} else { |
|||
return null; |
|||
} |
|||
} else { |
|||
return null; |
|||
} |
|||
} |
|||
|
|||
renderPeersList() { |
|||
if (this.state.getPeersCoin) { |
|||
const _getPeersCoin = this.state.getPeersCoin; |
|||
const _rawPeers = this.props.Settings.rawPeers; |
|||
const coin = _getPeersCoin.split('|')[0]; |
|||
|
|||
if (_rawPeers && |
|||
_getPeersCoin && |
|||
_rawPeers[coin]) { |
|||
return _rawPeers[coin].map((ip) => |
|||
<div key={ ip }>{ ip }</div> |
|||
); |
|||
} else { |
|||
return null; |
|||
} |
|||
} else { |
|||
return null; |
|||
} |
|||
} |
|||
|
|||
checkNodes() { |
|||
if (this.state.getPeersCoin) { |
|||
console.warn(this.state.getPeersCoin.split('|')[0]); |
|||
Store.dispatch(getPeersList(this.state.getPeersCoin.split('|')[0])); |
|||
} |
|||
} |
|||
|
|||
addNode() { |
|||
if (this.state.addNodeCoin && this.state.addPeerIP) { |
|||
Store.dispatch( |
|||
addPeerNode( |
|||
this.state.addNodeCoin.split('|')[0], |
|||
this.state.addPeerIP |
|||
) |
|||
); |
|||
} |
|||
} |
|||
|
|||
updateInput(e) { |
|||
this.setState({ |
|||
[e.target.name]: e.target.value, |
|||
}); |
|||
} |
|||
|
|||
render() { |
|||
return ( |
|||
<div className="panel-body"> |
|||
<div className="row"> |
|||
<div className="col-sm-6"> |
|||
<div className="col-sm-12"> |
|||
<p>{ translate('INDEX.USE_THIS_SECTION') }</p> |
|||
</div> |
|||
<div className="col-sm-8 col-xs-12"> |
|||
<div className="form-group"> |
|||
<select |
|||
className="form-control form-material" |
|||
name="getPeersCoin" |
|||
onChange={ this.updateInput }> |
|||
<option>{ translate('INDEX.SELECT_COIN') }</option> |
|||
<AddCoinOptionsCrypto /> |
|||
<AddCoinOptionsAC /> |
|||
<AddCoinOptionsACFiat /> |
|||
</select> |
|||
</div> |
|||
</div> |
|||
<div className="col-sm-4 col-xs-12 text-align-center"> |
|||
<button |
|||
type="button" |
|||
className="btn btn-primary waves-effect waves-light" |
|||
onClick={ this.checkNodes }>{ translate('INDEX.CHECK_NODES') }</button> |
|||
</div> |
|||
<div className="col-sm-12"> |
|||
<h5> |
|||
SuperNET Peers: |
|||
</h5> |
|||
<div>{ this.renderSNPeersList() }</div> |
|||
<h5> |
|||
Raw Peers: |
|||
</h5> |
|||
<div>{ this.renderPeersList() }</div> |
|||
</div> |
|||
</div> |
|||
|
|||
<div className="col-sm-6"> |
|||
<div className="col-sm-12"> |
|||
<p>{ translate('INDEX.USE_THIS_SECTION_PEER') }</p> |
|||
</div> |
|||
<div className="col-sm-8 col-xs-12"> |
|||
<div className="form-group"> |
|||
<select |
|||
className="form-control form-material" |
|||
name="addNodeCoin" |
|||
onChange={ this.updateInput }> |
|||
<option>{ translate('INDEX.SELECT_COIN') }</option> |
|||
<AddCoinOptionsCrypto /> |
|||
<AddCoinOptionsAC /> |
|||
<AddCoinOptionsACFiat /> |
|||
</select> |
|||
</div> |
|||
<div className="form-group"> |
|||
<input |
|||
type="text" |
|||
className="form-control" |
|||
name="addPeerIP" |
|||
placeholder={ translate('SETTINGS.ADD_PEER_IP') } |
|||
onChange={ this.updateInput } /> |
|||
</div> |
|||
</div> |
|||
<div className="col-sm-4 col-xs-12 text-align-center"> |
|||
<button |
|||
type="button" |
|||
className="btn btn-primary waves-effect waves-light" |
|||
onClick={ this.addNode }>{ translate('INDEX.ADD_NODE') }</button> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
); |
|||
}; |
|||
} |
|||
|
|||
const mapStateToProps = (state) => { |
|||
return { |
|||
Settings: state.Settings, |
|||
}; |
|||
}; |
|||
|
|||
export default connect(mapStateToProps)(AddNodePanel); |
@ -0,0 +1,90 @@ |
|||
import React from 'react'; |
|||
import { translate } from '../../../translate/translate'; |
|||
import { connect } from 'react-redux'; |
|||
|
|||
class AppInfoPanel extends React.Component { |
|||
constructor() { |
|||
super(); |
|||
} |
|||
|
|||
render() { |
|||
return ( |
|||
<div className="panel-body"> |
|||
<div className="col-sm-12 padding-top-15"> |
|||
<div className="row"> |
|||
<h5>{ translate('SETTINGS.APP_RELEASE') }</h5> |
|||
<div> |
|||
{ translate('SETTINGS.NAME') }: { this.props.Settings.appInfo.releaseInfo.name } |
|||
</div> |
|||
<div> |
|||
{ translate('SETTINGS.VERSION') }: { `${this.props.Settings.appInfo.releaseInfo.version.replace('version=', '')}-beta` } |
|||
</div> |
|||
<div> |
|||
{ translate('SETTINGS.APP_SESSION') }: { this.props.Settings.appInfo.appSession } |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div className="col-sm-12 padding-top-20"> |
|||
<div className="row"> |
|||
<h5>{ translate('SETTINGS.SYS_INFO') }</h5> |
|||
<div> |
|||
{ translate('SETTINGS.ARCH') }: { this.props.Settings.appInfo.sysInfo.arch } |
|||
</div> |
|||
<div> |
|||
{ translate('SETTINGS.OS_TYPE') }: { this.props.Settings.appInfo.sysInfo.os_type } |
|||
</div> |
|||
<div> |
|||
{ translate('SETTINGS.OS_PLATFORM') }: { this.props.Settings.appInfo.sysInfo.platform } |
|||
</div> |
|||
<div> |
|||
{ translate('SETTINGS.OS_RELEASE') }: { this.props.Settings.appInfo.sysInfo.os_release } |
|||
</div> |
|||
<div> |
|||
{ translate('SETTINGS.CPU') }: { this.props.Settings.appInfo.sysInfo.cpu } |
|||
</div> |
|||
<div> |
|||
{ translate('SETTINGS.CPU_CORES') }: { this.props.Settings.appInfo.sysInfo.cpu_cores } |
|||
</div> |
|||
<div> |
|||
{ translate('SETTINGS.MEM') }: { this.props.Settings.appInfo.sysInfo.totalmem_readable } |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div className="col-sm-12 padding-top-20"> |
|||
<div className="row"> |
|||
<h5>{ translate('SETTINGS.LOCATIONS') }</h5> |
|||
<div> |
|||
{ translate('SETTINGS.CACHE') }: { this.props.Settings.appInfo.dirs.cacheLocation } |
|||
</div> |
|||
<div> |
|||
{ translate('SETTINGS.CONFIG') }: { this.props.Settings.appInfo.dirs.configLocation } |
|||
</div> |
|||
<div> |
|||
Iguana { translate('SETTINGS.BIN') }: { this.props.Settings.appInfo.dirs.iguanaBin } |
|||
</div> |
|||
<div> |
|||
Iguana { translate('SETTINGS.DIR') }: { this.props.Settings.appInfo.dirs.iguanaDir } |
|||
</div> |
|||
<div> |
|||
Komodo { translate('SETTINGS.BIN') }: { this.props.Settings.appInfo.dirs.komododBin } |
|||
</div> |
|||
<div> |
|||
Komodo { translate('SETTINGS.DIR') }: { this.props.Settings.appInfo.dirs.komodoDir } |
|||
</div> |
|||
<div> |
|||
Komodo wallet.dat: { this.props.Settings.appInfo.dirs.komodoDir } |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
); |
|||
}; |
|||
} |
|||
|
|||
const mapStateToProps = (state) => { |
|||
return { |
|||
Settings: state.Settings, |
|||
}; |
|||
}; |
|||
|
|||
export default connect(mapStateToProps)(AppInfoPanel); |
@ -0,0 +1,321 @@ |
|||
import React from 'react'; |
|||
import { connect } from 'react-redux'; |
|||
import { translate } from '../../../translate/translate'; |
|||
import Config from '../../../config'; |
|||
import { |
|||
iguanaActiveHandle, |
|||
getAppConfig, |
|||
getAppInfo, |
|||
resetAppConfig, |
|||
saveAppConfig, |
|||
skipFullDashboardUpdate, |
|||
triggerToaster, |
|||
} from '../../../actions/actionCreators'; |
|||
import Store from '../../../store'; |
|||
|
|||
class AppSettingsPanel extends React.Component { |
|||
constructor() { |
|||
super(); |
|||
this.state = { |
|||
appSettings: {}, |
|||
appConfigSchema: {}, |
|||
}; |
|||
this._saveAppConfig = this._saveAppConfig.bind(this); |
|||
this._resetAppConfig = this._resetAppConfig.bind(this); |
|||
this._skipFullDashboardUpdate = this._skipFullDashboardUpdate.bind(this); |
|||
this.updateInput = this.updateInput.bind(this); |
|||
} |
|||
|
|||
componentWillMount() { |
|||
try { |
|||
const _appConfigSchema = window.require('electron').remote.getCurrentWindow().appConfigSchema; |
|||
const _appSettings = this.props.Settings.appSettings ? this.props.Settings.appSettings : Object.assign({}, window.require('electron').remote.getCurrentWindow().appConfig); |
|||
|
|||
this.setState(Object.assign({}, this.state, { |
|||
appConfigSchema: _appConfigSchema, |
|||
appSettings: _appSettings, |
|||
})); |
|||
} catch(e) {} |
|||
} |
|||
|
|||
componentDidMount(props) { |
|||
if (!this.props.disableWalletSpecificUI) { |
|||
Store.dispatch(iguanaActiveHandle()); |
|||
} |
|||
|
|||
Store.dispatch(getAppConfig()); |
|||
Store.dispatch(getAppInfo()); |
|||
} |
|||
|
|||
_skipFullDashboardUpdate() { |
|||
Store.dispatch(skipFullDashboardUpdate(!this.props.Dashboard.skipFullDashboardUpdate)); |
|||
} |
|||
|
|||
_resetAppConfig() { |
|||
Store.dispatch(resetAppConfig()); |
|||
} |
|||
|
|||
_saveAppConfig() { |
|||
const _appSettings = this.state.appSettings; |
|||
let _appSettingsPristine = Object.assign({}, this.props.Settings.appSettings); |
|||
let isError = false; |
|||
let saveAfterPathCheck = false; |
|||
|
|||
for (let key in _appSettings) { |
|||
if (key.indexOf('__') === -1) { |
|||
_appSettingsPristine[key] = this.state.appConfigSchema[key] && this.state.appConfigSchema[key].type === 'number' ? Number(_appSettings[key]) : _appSettings[key]; |
|||
|
|||
if (this.state.appConfigSchema[key] && this.state.appConfigSchema[key].type === 'folder' && |
|||
_appSettings[key] && |
|||
_appSettings[key].length) { |
|||
const _testLocation = window.require('electron').remote.getCurrentWindow().testLocation; |
|||
saveAfterPathCheck = true; |
|||
|
|||
_testLocation(_appSettings[key]) |
|||
.then((res) => { |
|||
if (res === -1) { |
|||
isError = true; |
|||
Store.dispatch( |
|||
triggerToaster( |
|||
translate('TOASTR.KOMODO_DATADIR_INVALID'), |
|||
translate('INDEX.SETTINGS'), |
|||
'error' |
|||
) |
|||
); |
|||
} else if (res === false) { |
|||
isError = true; |
|||
Store.dispatch( |
|||
triggerToaster( |
|||
translate('TOASTR.KOMODO_DATADIR_NOT_DIR'), |
|||
translate('INDEX.SETTINGS'), |
|||
'error' |
|||
) |
|||
); |
|||
} else { |
|||
Store.dispatch(saveAppConfig(_appSettingsPristine)); |
|||
} |
|||
}); |
|||
} |
|||
} else { |
|||
const _nestedKey = key.split('__'); |
|||
_appSettingsPristine[_nestedKey[0]][_nestedKey[1]] = this.state.appConfigSchema[_nestedKey[0]][_nestedKey[1]].type === 'number' ? Number(_appSettings[key]) : _appSettings[key]; |
|||
} |
|||
} |
|||
|
|||
if (!saveAfterPathCheck) { |
|||
Store.dispatch(saveAppConfig(_appSettingsPristine)); |
|||
} |
|||
} |
|||
|
|||
renderConfigEditForm() { |
|||
let items = []; |
|||
const _appConfig = this.state.appSettings; |
|||
for (let key in _appConfig) { |
|||
if (this.state.appConfigSchema[key] && typeof _appConfig[key] === 'object') { |
|||
if (this.state.appConfigSchema[key].display) { |
|||
items.push( |
|||
<tr key={ `app-settings-${key}` }> |
|||
<td className="padding-15"> |
|||
{ this.state.appConfigSchema[key].displayName ? this.state.appConfigSchema[key].displayName : key } |
|||
{ this.state.appConfigSchema[key].info && |
|||
<i |
|||
className="icon fa-question-circle settings-help" |
|||
title={ this.state.appConfigSchema[key].info }></i> |
|||
} |
|||
</td> |
|||
<td className="padding-15"></td> |
|||
</tr> |
|||
); |
|||
|
|||
for (let _key in _appConfig[key]) { |
|||
items.push( |
|||
<tr key={ `app-settings-${key}-${_key}` }> |
|||
<td className="padding-15 padding-left-30"> |
|||
{ this.state.appConfigSchema[key][_key].displayName ? this.state.appConfigSchema[key][_key].displayName : _key } |
|||
{ this.state.appConfigSchema[key][_key].info && |
|||
<i |
|||
className="icon fa-question-circle settings-help" |
|||
title={ this.state.appConfigSchema[key][_key].info }></i> |
|||
} |
|||
</td> |
|||
<td className="padding-15"> |
|||
{ this.state.appConfigSchema[key][_key].type === 'number' && |
|||
<input |
|||
type="number" |
|||
pattern="[0-9]*" |
|||
name={ `${key}__${_key}` } |
|||
value={ _appConfig[key][_key] } |
|||
onChange={ (event) => this.updateInputSettings(event, key, _key) } /> |
|||
} |
|||
{ (this.state.appConfigSchema[key][_key].type === 'string' || this.state.appConfigSchema[key][_key].type === 'folder') && |
|||
<input |
|||
type="text" |
|||
name={ `${key}__${_key}` } |
|||
value={ _appConfig[key][_key] } |
|||
className={ this.state.appConfigSchema[key][_key].type === 'folder' ? 'full-width': '' } |
|||
onChange={ (event) => this.updateInputSettings(event, key, _key) } /> |
|||
} |
|||
{ this.state.appConfigSchema[key][_key].type === 'boolean' && |
|||
<span className="pointer toggle"> |
|||
<label className="switch"> |
|||
<input |
|||
type="checkbox" |
|||
name={ `${key}__${_key}` } |
|||
value={ _appConfig[key] } |
|||
checked={ _appConfig[key][_key] } /> |
|||
<div |
|||
className="slider" |
|||
onClick={ (event) => this.updateInputSettings(event, key, _key) }></div> |
|||
</label> |
|||
</span> |
|||
} |
|||
</td> |
|||
</tr> |
|||
); |
|||
} |
|||
} |
|||
} else { |
|||
if (this.state.appConfigSchema[key] && this.state.appConfigSchema[key].display) { |
|||
items.push( |
|||
<tr key={ `app-settings-${key}` }> |
|||
<td className="padding-15"> |
|||
{ this.state.appConfigSchema[key].displayName ? this.state.appConfigSchema[key].displayName : key } |
|||
{ this.state.appConfigSchema[key].info && |
|||
<i |
|||
className="icon fa-question-circle settings-help" |
|||
title={ this.state.appConfigSchema[key].info }></i> |
|||
} |
|||
</td> |
|||
<td className="padding-15"> |
|||
{ this.state.appConfigSchema[key].type === 'number' && |
|||
<input |
|||
type="number" |
|||
pattern="[0-9]*" |
|||
name={ `${key}` } |
|||
value={ _appConfig[key] } |
|||
onChange={ (event) => this.updateInputSettings(event, key) } /> |
|||
} |
|||
{ (this.state.appConfigSchema[key].type === 'string' || this.state.appConfigSchema[key].type === 'folder') && |
|||
<input |
|||
type="text" |
|||
name={ `${key}` } |
|||
value={ _appConfig[key] } |
|||
className={ this.state.appConfigSchema[key].type === 'folder' ? 'full-width': '' } |
|||
onChange={ (event) => this.updateInputSettings(event, key) } /> |
|||
} |
|||
{ this.state.appConfigSchema[key].type === 'boolean' && |
|||
<span className="pointer toggle"> |
|||
<label className="switch"> |
|||
<input |
|||
type="checkbox" |
|||
name={ `${key}` } |
|||
value={ _appConfig[key] } |
|||
checked={ _appConfig[key] } /> |
|||
<div |
|||
className="slider" |
|||
onClick={ (event) => this.updateInputSettings(event, key) }></div> |
|||
</label> |
|||
</span> |
|||
} |
|||
</td> |
|||
</tr> |
|||
); |
|||
} |
|||
} |
|||
} |
|||
|
|||
items.push( |
|||
<tr key={ `kmd-main-sync-only` }> |
|||
<td className="padding-15"> |
|||
KMD main sync only |
|||
<i |
|||
className="icon fa-question-circle settings-help" |
|||
title="Fetch block synchronization data only. Skip any other requests that can deteriorate sync speed."></i> |
|||
</td> |
|||
<td className="padding-15"> |
|||
<span className="pointer toggle"> |
|||
<label className="switch"> |
|||
<input |
|||
type="checkbox" |
|||
name={ `kmd-main-sync-only` } |
|||
value={ this.props.Dashboard.skipFullDashboardUpdate } |
|||
checked={ this.props.Dashboard.skipFullDashboardUpdate } /> |
|||
<div |
|||
className="slider" |
|||
onClick={ this._skipFullDashboardUpdate }></div> |
|||
</label> |
|||
</span> |
|||
</td> |
|||
</tr> |
|||
); |
|||
|
|||
return items; |
|||
} |
|||
|
|||
updateInput(e) { |
|||
this.setState({ |
|||
[e.target.name]: e.target.value, |
|||
}); |
|||
} |
|||
|
|||
updateInputSettings(e, parentKey, childKey) { |
|||
let _appSettings = this.state.appSettings; |
|||
let _appSettingsPrev = Object.assign({}, _appSettings); |
|||
|
|||
if (!childKey && this.state.appConfigSchema[parentKey].type === 'boolean') { |
|||
_appSettings[parentKey] = typeof _appSettings[parentKey] !== undefined ? !_appSettings[parentKey] : !this.state.appSettings[parentKey]; |
|||
} else if (childKey && this.state.appConfigSchema[parentKey][childKey].type === 'boolean') { |
|||
_appSettings[parentKey][childKey] = typeof _appSettings[parentKey][childKey] !== undefined ? !_appSettings[parentKey][childKey] : !this.state.appSettings[parentKey][childKey]; |
|||
} else if ((!childKey && this.state.appConfigSchema[parentKey].type === 'number') || (childKey && this.state.appConfigSchema[parentKey][childKey].type === 'number')) { |
|||
if (e.target.value === '') { |
|||
_appSettings[e.target.name] = _appSettingsPrev[e.target.name]; |
|||
} else { |
|||
_appSettings[e.target.name] = e.target.value.replace(/[^0-9]+/g, ''); |
|||
} |
|||
} else { |
|||
_appSettings[e.target.name] = e.target.value; |
|||
} |
|||
|
|||
this.setState({ |
|||
appSettings: _appSettings, |
|||
}); |
|||
} |
|||
|
|||
render() { |
|||
return ( |
|||
<div className="panel-body"> |
|||
<p> |
|||
<strong>{ translate('SETTINGS.CONFIG_RESTART_REQUIRED') }</strong> |
|||
</p> |
|||
<div className="col-sm-12 padding-top-15"> |
|||
<table> |
|||
<tbody> |
|||
{ this.renderConfigEditForm() } |
|||
</tbody> |
|||
</table> |
|||
</div> |
|||
<div className="col-sm-12 col-xs-12 text-align-center padding-top-35 padding-bottom-30"> |
|||
<button |
|||
type="button" |
|||
className="btn btn-primary waves-effect waves-light" |
|||
onClick={ this._saveAppConfig }>{ translate('SETTINGS.SAVE_APP_CONFIG') }</button> |
|||
<button |
|||
type="button" |
|||
className="btn btn-primary waves-effect waves-light margin-left-30" |
|||
onClick={ this._resetAppConfig }>Reset to default</button> |
|||
</div> |
|||
</div> |
|||
); |
|||
}; |
|||
} |
|||
|
|||
const mapStateToProps = (state) => { |
|||
return { |
|||
Settings: state.Settings, |
|||
Dashboard: { |
|||
skipFullDashboardUpdate: state.Dashboard.skipFullDashboardUpdate, |
|||
}, |
|||
}; |
|||
}; |
|||
|
|||
export default connect(mapStateToProps)(AppSettingsPanel); |
@ -0,0 +1,151 @@ |
|||
import React from 'react'; |
|||
import { translate } from '../../../translate/translate'; |
|||
import { connect } from 'react-redux'; |
|||
import Config from '../../../config'; |
|||
import { |
|||
getPeersList, |
|||
checkForUpdateUIPromise, |
|||
updateUIPromise, |
|||
} from '../../../actions/actionCreators'; |
|||
|
|||
import { SocketProvider } from 'socket.io-react'; |
|||
import io from 'socket.io-client'; |
|||
|
|||
const socket = io.connect(`http://127.0.0.1:${Config.agamaPort}`); |
|||
|
|||
let updateProgressBar = { |
|||
patch: -1, |
|||
}; |
|||
|
|||
class AppUpdatePanel extends React.Component { |
|||
constructor() { |
|||
super(); |
|||
this.state = { |
|||
updatePatch: null, |
|||
updateLog: [], |
|||
updateProgressPatch: null, |
|||
updatePatch: null, |
|||
updateBins: null, |
|||
}; |
|||
this._checkForUpdateUIPromise = this._checkForUpdateUIPromise.bind(this); |
|||
this._updateUIPromise = this._updateUIPromise.bind(this); |
|||
this.checkNodes = this.checkNodes.bind(this); |
|||
} |
|||
|
|||
componentWillMount() { |
|||
socket.on('patch', msg => this.updateSocketsData = (msg) => {}); |
|||
} |
|||
|
|||
componentWillUnmount() { |
|||
socket.removeAllListeners('patch', msg => this.updateSocketsData(msg)); |
|||
} |
|||
|
|||
checkNodes() { |
|||
if (this.state.getPeersCoin) { |
|||
Store.dispatch(getPeersList(this.state.getPeersCoin.split('|')[0])); |
|||
} |
|||
} |
|||
|
|||
_updateUIPromise() { |
|||
updateProgressBar.patch = 0; |
|||
let _updateLog = []; |
|||
_updateLog.push(`${translate('INDEX.DOWNLOADING_UI_UPDATE')}...`); |
|||
this.setState(Object.assign({}, this.state, { |
|||
updateLog: _updateLog, |
|||
})); |
|||
|
|||
updateUIPromise(); |
|||
} |
|||
|
|||
_checkForUpdateUIPromise() { |
|||
let _updateLog = []; |
|||
_updateLog.push(translate('INDEX.CHECKING_UI_UPDATE')); |
|||
this.setState(Object.assign({}, this.state, { |
|||
updateLog: _updateLog, |
|||
})); |
|||
|
|||
checkForUpdateUIPromise() |
|||
.then((res) => { |
|||
let _updateLog = this.state.updateLog; |
|||
_updateLog.push(res.result === 'update' ? (`${translate('INDEX.NEW_UI_UPDATE')} ${res.version.remote}`) : translate('INDEX.YOU_HAVE_LATEST_UI')); |
|||
this.setState(Object.assign({}, this.state, { |
|||
updatePatch: res.result === 'update' ? true : false, |
|||
updateLog: _updateLog, |
|||
})); |
|||
}); |
|||
} |
|||
|
|||
renderUpdateStatus() { |
|||
let items = []; |
|||
let patchProgressBar = null; |
|||
const _updateLogLength = this.state.updateLog.length; |
|||
|
|||
for (let i = 0; i < _updateLogLength; i++) { |
|||
items.push( |
|||
<div key={ `settings-update-log-${i}` }>{ this.state.updateLog[i] }</div> |
|||
); |
|||
} |
|||
|
|||
if (_updateLogLength) { |
|||
return ( |
|||
<div style={{ minHeight: '200px' }}> |
|||
<hr /> |
|||
<h5>{ translate('SETTINGS.PROGRESS') }:</h5> |
|||
<div className="padding-bottom-15">{ items }</div> |
|||
<div className={ updateProgressBar.patch > -1 ? 'progress progress-sm' : 'hide' }> |
|||
<div |
|||
className="progress-bar progress-bar-striped active progress-bar-indicating progress-bar-success font-size-80-percent" |
|||
style={{ width: `${updateProgressBar.patch}%` }}> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
); |
|||
} else { |
|||
return null; |
|||
} |
|||
} |
|||
|
|||
render() { |
|||
return ( |
|||
<div className="panel-body"> |
|||
<div className="col-sm-4 padding-top-15"> |
|||
<h5>{ translate('INDEX.UI_UPDATE') }</h5> |
|||
<div className="padding-top-15"> |
|||
<button |
|||
type="button" |
|||
className="btn btn-info waves-effect waves-light" |
|||
onClick={ this._checkForUpdateUIPromise }>{ translate('INDEX.CHECK_FOR_UPDATE') }</button> |
|||
<button |
|||
type="button" |
|||
className="btn btn-primary waves-effect waves-light margin-left-20" |
|||
onClick={ this._updateUIPromise } |
|||
disabled={ !this.state.updatePatch }>{ translate('INDEX.UPDATE_UI_NOW') }</button> |
|||
</div> |
|||
</div> |
|||
<div className="col-sm-4 padding-top-15 hide"> |
|||
<h5>{ translate('INDEX.BINS_UPDATE') }</h5> |
|||
<div className="padding-top-15"> |
|||
<button |
|||
type="button" |
|||
className="btn btn-info waves-effect waves-light" |
|||
onClick={ this._checkForUpdateUIPromise }>{ translate('INDEX.CHECK_FOR_UPDATE') }</button> |
|||
<button |
|||
type="button" |
|||
className="btn btn-primary waves-effect waves-light margin-left-20" |
|||
onClick={ this.checkNodes }>{ translate('INDEX.UPDATE_BINS_NOW') }</button> |
|||
</div> |
|||
</div> |
|||
<div className="col-sm-12 padding-top-15"> |
|||
{ this.renderUpdateStatus() } |
|||
</div> |
|||
</div> |
|||
); |
|||
}; |
|||
} |
|||
const mapStateToProps = (state) => { |
|||
return { |
|||
Settings: state.Settings, |
|||
}; |
|||
}; |
|||
|
|||
export default connect(mapStateToProps)(AppUpdatePanel); |
@ -0,0 +1,206 @@ |
|||
import React from 'react'; |
|||
import { connect } from 'react-redux'; |
|||
import { translate } from '../../../translate/translate'; |
|||
import { |
|||
shepherdCli, |
|||
} from '../../../actions/actionCreators'; |
|||
import Store from '../../../store'; |
|||
|
|||
class CliPanel extends React.Component { |
|||
constructor() { |
|||
super(); |
|||
this.state = { |
|||
cliCmdString: '', |
|||
cliCoin: null, |
|||
cliResponse: null, |
|||
}; |
|||
} |
|||
|
|||
renderActiveCoinsList(mode) { |
|||
const modes = [ |
|||
'native', |
|||
'basilisk', |
|||
'full' |
|||
]; |
|||
|
|||
const allCoins = this.props.Main.coins; |
|||
let items = []; |
|||
|
|||
if (allCoins) { |
|||
if (mode === 'all') { |
|||
modes.map(function(mode) { |
|||
allCoins[mode].map(function(coin) { |
|||
items.push( |
|||
<option |
|||
value={ coin } |
|||
key={ coin }> |
|||
{ coin } ({ mode }) |
|||
</option> |
|||
); |
|||
}); |
|||
}); |
|||
} else { |
|||
allCoins[mode].map(function(coin) { |
|||
items.push( |
|||
<option |
|||
value={ coin } |
|||
key={ coin }> |
|||
{ coin } ({ mode }) |
|||
</option> |
|||
); |
|||
}); |
|||
} |
|||
|
|||
return items; |
|||
} else { |
|||
return null; |
|||
} |
|||
} |
|||
|
|||
// TODO: rerender only if prop is changed
|
|||
renderCliResponse() { |
|||
const _cliResponse = this.props.Settings.cli; |
|||
let _items = []; |
|||
|
|||
if (_cliResponse) { |
|||
let _cliResponseParsed; |
|||
let responseType; |
|||
|
|||
try { |
|||
_cliResponseParsed = JSON.parse(_cliResponse.result); |
|||
} catch(e) { |
|||
_cliResponseParsed = _cliResponse.result; |
|||
} |
|||
|
|||
if (Object.prototype.toString.call(_cliResponseParsed) === '[object Array]') { |
|||
responseType = 'array'; |
|||
|
|||
for (let i = 0; i < _cliResponseParsed.length; i++) { |
|||
_items.push( |
|||
<div key={ `cli-response-${Math.random(0, 9) * 10}` }>{ JSON.stringify(_cliResponseParsed[i], null, '\t') }</div> |
|||
); |
|||
} |
|||
} |
|||
if (Object.prototype.toString.call(_cliResponseParsed) === '[object]' || |
|||
typeof _cliResponseParsed === 'object') { |
|||
responseType = 'object'; |
|||
|
|||
_items.push( |
|||
<div key={ `cli-response-${Math.random(0, 9) * 10}` }>{ JSON.stringify(_cliResponseParsed, null, '\t') }</div> |
|||
); |
|||
} |
|||
if (Object.prototype.toString.call(_cliResponseParsed) === 'number' || |
|||
typeof _cliResponseParsed === 'boolean' || |
|||
_cliResponseParsed === 'wrong cli string format') { |
|||
responseType = 'number'; |
|||
|
|||
_items.push( |
|||
<div key={ `cli-response-${Math.random(0, 9) * 10}` }>{ _cliResponseParsed.toString() }</div> |
|||
); |
|||
} |
|||
|
|||
if (responseType !== 'number' && |
|||
responseType !== 'array' && |
|||
responseType !== 'object' && |
|||
_cliResponseParsed.indexOf('\n') > -1) { |
|||
_cliResponseParsed = _cliResponseParsed.split('\n'); |
|||
|
|||
for (let i = 0; i < _cliResponseParsed.length; i++) { |
|||
_items.push( |
|||
<div key={ `cli-response-${Math.random(0, 9) * 10}` }>{ _cliResponseParsed[i] }</div> |
|||
); |
|||
} |
|||
} |
|||
|
|||
return ( |
|||
<div> |
|||
<div> |
|||
<strong>{ translate('SETTINGS.CLI_RESPONSE') }:</strong> |
|||
</div> |
|||
{ _items } |
|||
</div> |
|||
); |
|||
} else { |
|||
return null; |
|||
} |
|||
} |
|||
|
|||
execCliCmd() { |
|||
Store.dispatch( |
|||
shepherdCli( |
|||
'passthru', |
|||
this.state.cliCoin, |
|||
this.state.cliCmdString |
|||
) |
|||
); |
|||
} |
|||
|
|||
updateInput = (e) => { |
|||
this.setState({ |
|||
[e.target.name]: e.target.value, |
|||
}); |
|||
} |
|||
|
|||
render() { |
|||
return ( |
|||
<div className="panel-body"> |
|||
<p>{ translate('INDEX.CLI_SELECT_A_COIN') }</p> |
|||
<div className="col-sm-12"></div> |
|||
<form |
|||
className="execute-cli-cmd-form" |
|||
method="post" |
|||
action="javascript:" |
|||
autoComplete="off"> |
|||
<div className="form-group form-material floating"> |
|||
<select |
|||
className="form-control form-material" |
|||
name="cliCoin" |
|||
id="settingsCliOptions" |
|||
onChange={ this.updateInput }> |
|||
<option>{ translate('INDEX.CLI_NATIVE_COIN') }</option> |
|||
{ this.renderActiveCoinsList('native') } |
|||
</select> |
|||
<label |
|||
className="floating-label" |
|||
htmlFor="settingsDelectDebugLogOptions">{ translate('INDEX.COIN') }</label> |
|||
</div> |
|||
<div className="form-group form-material floating"> |
|||
<textarea |
|||
type="text" |
|||
className="form-control" |
|||
name="cliCmdString" |
|||
id="cliCmd" |
|||
value={ this.state.cliCmdString } |
|||
onChange={ this.updateInput }></textarea> |
|||
<label |
|||
className="floating-label" |
|||
htmlFor="readDebugLogLines">{ translate('INDEX.TYPE_CLI_CMD') }</label> |
|||
</div> |
|||
<div className="col-sm-12 col-xs-12 text-align-center"> |
|||
<button |
|||
type="button" |
|||
className="btn btn-primary waves-effect waves-light" |
|||
disabled={ !this.state.cliCoin || !this.state.cliCmdString } |
|||
onClick={ () => this.execCliCmd() }>{ translate('INDEX.EXECUTE') }</button> |
|||
</div> |
|||
<div className="col-sm-12 col-xs-12 text-align-left"> |
|||
<div className="padding-top-40 padding-bottom-20 horizontal-padding-0"> |
|||
{ this.renderCliResponse() } |
|||
</div> |
|||
</div> |
|||
</form> |
|||
</div> |
|||
); |
|||
}; |
|||
} |
|||
|
|||
const mapStateToProps = (state) => { |
|||
return { |
|||
Main: { |
|||
coins: state.Main.coins, |
|||
}, |
|||
Settings: state.Settings, |
|||
}; |
|||
}; |
|||
|
|||
export default connect(mapStateToProps)(CliPanel); |
@ -0,0 +1,180 @@ |
|||
import React from 'react'; |
|||
import { connect } from 'react-redux'; |
|||
import { translate } from '../../../translate/translate'; |
|||
import Config from '../../../config'; |
|||
import { secondsToString } from '../../../util/time'; |
|||
import { |
|||
getDebugLog, |
|||
} from '../../../actions/actionCreators'; |
|||
import Store from '../../../store'; |
|||
|
|||
class DebugLogPanel extends React.Component { |
|||
constructor() { |
|||
super(); |
|||
this.state = { |
|||
appRuntimeLog: [], |
|||
debugLinesCount: 10, |
|||
debugTarget: 'iguana', |
|||
nativeOnly: Config.iguanaLessMode, |
|||
toggleAppRuntimeLog: false, |
|||
}; |
|||
this.readDebugLog = this.readDebugLog.bind(this); |
|||
this.updateInput = this.updateInput.bind(this); |
|||
this.getAppRuntimeLog = this.getAppRuntimeLog.bind(this); |
|||
this.toggleAppRuntimeLog = this.toggleAppRuntimeLog.bind(this); |
|||
this.renderAppRuntimeLog = this.renderAppRuntimeLog.bind(this); |
|||
} |
|||
|
|||
readDebugLog() { |
|||
Store.dispatch( |
|||
getDebugLog( |
|||
this.state.debugTarget, |
|||
this.state.debugLinesCount |
|||
) |
|||
); |
|||
} |
|||
|
|||
renderAppRuntimeLog() { |
|||
let _items = []; |
|||
const _appRuntimeLog = this.state.appRuntimeLog; |
|||
|
|||
for (let i = 0; i < _appRuntimeLog.length; i++) { |
|||
_items.push( |
|||
<p key={ `app-runtime-log-entry-${i}` }> |
|||
<span>{ secondsToString(_appRuntimeLog[i].time, true) }</span> |
|||
<span className="padding-left-30">{ JSON.stringify(_appRuntimeLog[i].msg, null, '') }</span> |
|||
</p> |
|||
); |
|||
} |
|||
|
|||
return _items; |
|||
} |
|||
|
|||
toggleAppRuntimeLog() { |
|||
this.setState(Object.assign({}, this.state, { |
|||
toggleAppRuntimeLog: !this.state.toggleAppRuntimeLog, |
|||
})); |
|||
|
|||
this.getAppRuntimeLog(); |
|||
} |
|||
|
|||
getAppRuntimeLog() { |
|||
let _appRuntimeLog; |
|||
|
|||
try { |
|||
_appRuntimeLog = window.require('electron').remote.getCurrentWindow().getAppRuntimeLog; |
|||
} catch (e) {} |
|||
|
|||
_appRuntimeLog() |
|||
.then((json) => { |
|||
this.setState(Object.assign({}, this.state, { |
|||
appRuntimeLog: json, |
|||
})); |
|||
}); |
|||
} |
|||
|
|||
renderDebugLogData() { |
|||
if (this.props.Settings.debugLog) { |
|||
const _debugLogDataRows = this.props.Settings.debugLog.split('\n'); |
|||
|
|||
if (_debugLogDataRows && |
|||
_debugLogDataRows.length) { |
|||
return _debugLogDataRows.map((_row) => |
|||
<div |
|||
key={ `settings-debuglog-${Math.random(0, 9) * 10}` } |
|||
className="padding-bottom-5">{ _row }</div> |
|||
); |
|||
} else { |
|||
return ( |
|||
<span>{ translate('INDEX.EMPTY_DEBUG_LOG') }</span> |
|||
); |
|||
} |
|||
} else { |
|||
return null; |
|||
} |
|||
} |
|||
|
|||
updateInput(e) { |
|||
this.setState({ |
|||
[e.target.name]: e.target.value, |
|||
}); |
|||
} |
|||
|
|||
render() { |
|||
return ( |
|||
<div className="panel-body"> |
|||
<p>{ translate('INDEX.DEBUG_LOG_DESC') }</p> |
|||
<div className="margin-top-30"> |
|||
<span className="pointer toggle"> |
|||
<label className="switch"> |
|||
<input |
|||
type="checkbox" |
|||
name="settings-app-debug-toggle" |
|||
value={ this.state.toggleAppRuntimeLog } |
|||
checked={ this.state.toggleAppRuntimeLog } /> |
|||
<div |
|||
className="slider" |
|||
onClick={ this.toggleAppRuntimeLog }></div> |
|||
</label> |
|||
<span |
|||
className="title" |
|||
onClick={ this.toggleAppRuntimeLog }>Show app runtime log</span> |
|||
</span> |
|||
</div> |
|||
{ !this.state.toggleAppRuntimeLog && |
|||
<form |
|||
className="read-debug-log-import-form" |
|||
method="post" |
|||
action="javascript:" |
|||
autoComplete="off"> |
|||
<div className="form-group form-material floating"> |
|||
<input |
|||
type="text" |
|||
className="form-control" |
|||
name="debugLinesCount" |
|||
id="readDebugLogLines" |
|||
value={ this.state.debugLinesCount } |
|||
onChange={ this.updateInput } /> |
|||
<label |
|||
className="floating-label" |
|||
htmlFor="readDebugLogLines">{ translate('INDEX.DEBUG_LOG_LINES') }</label> |
|||
</div> |
|||
<div className="form-group form-material floating"> |
|||
<select |
|||
className="form-control form-material" |
|||
name="debugTarget" |
|||
id="settingsDelectDebugLogOptions" |
|||
onChange={ this.updateInput }> |
|||
<option value="iguana" className={ this.state.nativeOnly ? 'hide' : '' }>Iguana</option> |
|||
<option value="komodo">Komodo</option> |
|||
</select> |
|||
<label |
|||
className="floating-label" |
|||
htmlFor="settingsDelectDebugLogOptions">{ translate('INDEX.TARGET') }</label> |
|||
</div> |
|||
<div className="col-sm-12 col-xs-12 text-align-center"> |
|||
<button |
|||
type="button" |
|||
className="btn btn-primary waves-effect waves-light" |
|||
onClick={ this.readDebugLog }>{ translate('INDEX.LOAD_DEBUG_LOG') }</button> |
|||
</div> |
|||
<div className="col-sm-12 col-xs-12 text-align-left"> |
|||
<div className="padding-top-40 padding-bottom-20 horizontal-padding-0">{ this.renderDebugLogData() }</div> |
|||
</div> |
|||
</form> |
|||
} |
|||
{ this.state.toggleAppRuntimeLog && |
|||
<div className="margin-top-20">{ this.renderAppRuntimeLog() }</div> |
|||
} |
|||
</div> |
|||
); |
|||
}; |
|||
} |
|||
|
|||
const mapStateToProps = (state) => { |
|||
return { |
|||
Settings: state.Settings, |
|||
}; |
|||
}; |
|||
|
|||
export default connect(mapStateToProps)(DebugLogPanel); |
@ -0,0 +1,235 @@ |
|||
import React from 'react'; |
|||
import { translate } from '../../../translate/translate'; |
|||
import { connect } from 'react-redux'; |
|||
import { |
|||
encryptWallet, |
|||
settingsWifkeyState, |
|||
copyCoinAddress, |
|||
} from '../../../actions/actionCreators'; |
|||
import Store from '../../../store'; |
|||
|
|||
class ExportKeysPanel extends React.Component { |
|||
constructor() { |
|||
super(); |
|||
this.state = { |
|||
exportWifKeysRaw: false, |
|||
seedInputVisibility: false, |
|||
trimPassphraseTimer: null, |
|||
wifkeysPassphrase: '', |
|||
}; |
|||
this.exportWifKeys = this.exportWifKeys.bind(this); |
|||
this.exportWifKeysRaw = this.exportWifKeysRaw.bind(this); |
|||
this.toggleSeedInputVisibility = this.toggleSeedInputVisibility.bind(this); |
|||
this._copyCoinAddress = this._copyCoinAddress.bind(this); |
|||
this.updateInput = this.updateInput.bind(this); |
|||
} |
|||
|
|||
exportWifKeys() { |
|||
Store.dispatch( |
|||
encryptWallet( |
|||
this.state.wifkeysPassphrase, |
|||
settingsWifkeyState, |
|||
this.props.ActiveCoin.coin |
|||
) |
|||
); |
|||
} |
|||
|
|||
exportWifKeysRaw() { |
|||
this.setState(Object.assign({}, this.state, { |
|||
exportWifKeysRaw: !this.state.exportWifKeysRaw, |
|||
})); |
|||
} |
|||
|
|||
toggleSeedInputVisibility() { |
|||
this.setState({ |
|||
seedInputVisibility: !this.state.seedInputVisibility, |
|||
}); |
|||
} |
|||
|
|||
renderExportWifKeysRaw() { |
|||
const _wifKeysResponse = this.props.Settings.wifkey; |
|||
|
|||
if (_wifKeysResponse && |
|||
this.state.exportWifKeysRaw) { |
|||
return ( |
|||
<div className="padding-bottom-30 padding-top-30"> |
|||
{ JSON.stringify(_wifKeysResponse, null, '\t') } |
|||
</div> |
|||
); |
|||
} else { |
|||
return null; |
|||
} |
|||
} |
|||
|
|||
_copyCoinAddress(address) { |
|||
Store.dispatch(copyCoinAddress(address)); |
|||
} |
|||
|
|||
renderWifKeys() { |
|||
let items = []; |
|||
|
|||
if (this.props.Settings.wifkey) { |
|||
const _wifKeys = this.props.Settings.wifkey; |
|||
|
|||
for (let i = 0; i < 2; i++) { |
|||
items.push( |
|||
<tr key={ `wif-export-table-header-${i}` }> |
|||
<td className="padding-bottom-10 padding-top-10"> |
|||
<strong>{ i === 0 ? translate('SETTINGS.ADDRESS_LIST') : translate('SETTINGS.WIF_KEY_LIST') }</strong> |
|||
</td> |
|||
<td className="padding-bottom-10 padding-top-10"></td> |
|||
</tr> |
|||
); |
|||
|
|||
for (let _key in _wifKeys) { |
|||
if ((i === 0 && _key.length === 3 && _key !== 'tag') || |
|||
(i === 1 && _key.indexOf('wif') > -1)) { |
|||
items.push( |
|||
<tr key={ _key }> |
|||
<td className="padding-bottom-20">{ _key.replace('wif', ' WIF') }</td> |
|||
<td className="padding-bottom-20 padding-left-15"> |
|||
{ _wifKeys[_key] } |
|||
<button |
|||
className="btn btn-default btn-xs clipboard-edexaddr margin-left-10" |
|||
title={ translate('INDEX.COPY_TO_CLIPBOARD') } |
|||
onClick={ () => this._copyCoinAddress(_wifKeys[_key]) }> |
|||
<i className="icon wb-copy"></i> { translate('INDEX.COPY') } |
|||
</button> |
|||
</td> |
|||
</tr> |
|||
); |
|||
} |
|||
} |
|||
} |
|||
|
|||
return items; |
|||
} else { |
|||
return null; |
|||
} |
|||
} |
|||
|
|||
updateInput(e) { |
|||
if (e.target.name === 'wifkeysPassphrase') { |
|||
// 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({ |
|||
wifkeysPassphrase: newValue ? newValue.trim() : '', // hardcoded field name
|
|||
}); |
|||
}, 2000); |
|||
|
|||
this.resizeLoginTextarea(); |
|||
|
|||
this.setState({ |
|||
trimPassphraseTimer: _trimPassphraseTimer, |
|||
[e.target.name]: newValue, |
|||
}); |
|||
} else { |
|||
this.setState({ |
|||
[e.target.name]: e.target.value, |
|||
}); |
|||
} |
|||
} |
|||
|
|||
resizeLoginTextarea() { |
|||
// auto-size textarea
|
|||
setTimeout(() => { |
|||
if (this.state.seedInputVisibility) { |
|||
document.querySelector('#wifkeysPassphraseTextarea').style.height = '1px'; |
|||
document.querySelector('#wifkeysPassphraseTextarea').style.height = `${(15 + document.querySelector('#wifkeysPassphraseTextarea').scrollHeight)}px`; |
|||
} |
|||
}, 100); |
|||
} |
|||
|
|||
renderLB(_translationID) { |
|||
const _translationComponents = translate(_translationID).split('<br>'); |
|||
|
|||
return _translationComponents.map((_translation) => |
|||
<span key={ `settings-label-${Math.random(0, 9) * 10}` }> |
|||
{ _translation } |
|||
<br /> |
|||
</span> |
|||
); |
|||
} |
|||
|
|||
render() { |
|||
return ( |
|||
<div className="panel-body"> |
|||
<div> |
|||
<div className="padding-bottom-20">{ this.renderLB('INDEX.ONLY_ACTIVE_WIF_KEYS') }</div> |
|||
<div className="padding-bottom-20"> |
|||
<i>{ this.renderLB('SETTINGS.EXPORT_KEYS_NOTE') }</i> |
|||
</div> |
|||
<strong> |
|||
<i>{ translate('INDEX.PLEASE_KEEP_KEYS_SAFE') }</i> |
|||
</strong> |
|||
</div> |
|||
<div className="col-sm-12"></div> |
|||
<form |
|||
className="wifkeys-form" |
|||
method="post" |
|||
action="javascript:" |
|||
autoComplete="off"> |
|||
<div className="form-group form-material floating"> |
|||
<input |
|||
type="password" |
|||
className={ !this.state.seedInputVisibility ? 'form-control' : 'hide' } |
|||
name="wifkeysPassphrase" |
|||
id="wifkeysPassphrase" |
|||
onChange={ this.updateInput } |
|||
value={ this.state.wifkeysPassphrase } /> |
|||
<textarea |
|||
className={ this.state.seedInputVisibility ? 'form-control' : 'hide' } |
|||
id="wifkeysPassphraseTextarea" |
|||
name="wifkeysPassphrase" |
|||
onChange={ this.updateInput } |
|||
value={ this.state.wifkeysPassphrase }></textarea> |
|||
<i |
|||
className={ 'seed-toggle fa fa-eye' + (!this.state.seedInputVisibility ? '-slash' : '') } |
|||
onClick={ this.toggleSeedInputVisibility }></i> |
|||
<label |
|||
className="floating-label" |
|||
htmlFor="wifkeysPassphrase">{ translate('INDEX.PASSPHRASE') }</label> |
|||
</div> |
|||
<div className="col-sm-12 col-xs-12 text-align-center"> |
|||
<button |
|||
type="button" |
|||
className="btn btn-primary waves-effect waves-light" |
|||
onClick={ this.exportWifKeys }>{ translate('INDEX.GET_WIF_KEYS') }</button> |
|||
</div> |
|||
</form> |
|||
|
|||
<div className="col-sm-12 padding-top-15"> |
|||
<div className="row"> |
|||
<table className="table"> |
|||
{ this.renderWifKeys() } |
|||
</table> |
|||
<div className={ this.props.wifkey ? 'col-sm-12 col-xs-12 text-align-center' : 'hide' }> |
|||
<button |
|||
type="button" |
|||
className="btn btn-primary waves-effect waves-light" |
|||
onClick={ this.exportWifKeysRaw }>{ this.state.exportWifKeysRaw ? 'Hide' : 'Show' } raw data</button> |
|||
</div> |
|||
<div className={ this.state.exportWifKeysRaw ? 'col-sm-12 col-xs-12 text-align-center' : 'hide' }> |
|||
{ this.renderExportWifKeysRaw() } |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
); |
|||
}; |
|||
} |
|||
|
|||
const mapStateToProps = (state) => { |
|||
return { |
|||
ActiveCoin: { |
|||
coin: state.ActiveCoin.coin, |
|||
}, |
|||
Settings: state.Settings, |
|||
}; |
|||
}; |
|||
|
|||
export default connect(mapStateToProps)(ExportKeysPanel); |
@ -0,0 +1,23 @@ |
|||
import React from 'react'; |
|||
import { translate } from '../../../translate/translate'; |
|||
import { connect } from 'react-redux'; |
|||
|
|||
class FiatCurrencyPanel extends React.Component { |
|||
constructor() { |
|||
super(); |
|||
} |
|||
|
|||
render() { |
|||
return ( |
|||
<div className="panel-body">Fiat currency settings section to be updated soon.</div> |
|||
); |
|||
}; |
|||
} |
|||
|
|||
const mapStateToProps = (state) => { |
|||
return { |
|||
Settings: state.Settings, |
|||
}; |
|||
}; |
|||
|
|||
export default connect(mapStateToProps)(FiatCurrencyPanel); |
@ -0,0 +1,67 @@ |
|||
import React from 'react'; |
|||
import { translate } from '../../../translate/translate'; |
|||
import { |
|||
importPrivKey, |
|||
} from '../../../actions/actionCreators'; |
|||
import Store from '../../../store'; |
|||
|
|||
class ImportKeysPanel extends React.Component { |
|||
constructor() { |
|||
super(); |
|||
this.state = { |
|||
importWifKey: '', |
|||
}; |
|||
this.importWifKey = this.importWifKey.bind(this); |
|||
} |
|||
|
|||
importWifKey() { |
|||
Store.dispatch(importPrivKey(this.state.importWifKey)); |
|||
} |
|||
|
|||
updateInput = (e) => { |
|||
this.setState({ |
|||
[e.target.name]: e.target.value, |
|||
}); |
|||
} |
|||
|
|||
render() { |
|||
return ( |
|||
<div className="panel-body"> |
|||
<div>{ translate('INDEX.IMPORT_KEYS_DESC_P1') }</div><br/> |
|||
<div>{ translate('INDEX.IMPORT_KEYS_DESC_P2') }</div><br/> |
|||
<div>{ translate('INDEX.IMPORT_KEYS_DESC_P3') }</div><br/> |
|||
<div> |
|||
<strong> |
|||
<i>{ translate('INDEX.PLEASE_KEEP_KEYS_SAFE') }</i> |
|||
</strong> |
|||
</div> |
|||
<div className="col-sm-12"></div> |
|||
<form |
|||
className="wifkeys-import-form" |
|||
method="post" |
|||
action="javascript:" |
|||
autoComplete="off"> |
|||
<div className="form-group form-material floating"> |
|||
<input |
|||
type="text" |
|||
className="form-control" |
|||
name="importWifKey" |
|||
id="importWifkey" |
|||
onChange={ this.updateInput } /> |
|||
<label |
|||
className="floating-label" |
|||
htmlFor="importWifkey">{ translate('INDEX.INPUT_PRIV_KEY') }</label> |
|||
</div> |
|||
<div className="col-sm-12 col-xs-12 text-align-center"> |
|||
<button |
|||
type="button" |
|||
className="btn btn-primary waves-effect waves-light" |
|||
onClick={ this.importWifKey }>{ translate('INDEX.IMPORT_PRIV_KEY') }</button> |
|||
</div> |
|||
</form> |
|||
</div> |
|||
); |
|||
} |
|||
} |
|||
|
|||
export default ImportKeysPanel; |
@ -0,0 +1,121 @@ |
|||
.support-box { |
|||
padding: 15px 20px; |
|||
width: 220px; |
|||
display: inline-block; |
|||
cursor: pointer; |
|||
|
|||
&-title { |
|||
font-weight: bold; |
|||
padding-top: 12px; |
|||
padding-bottom: 3px; |
|||
} |
|||
img { |
|||
height: 50px; |
|||
} |
|||
} |
|||
|
|||
.support-box-wrapper { |
|||
display: inline-block; |
|||
margin-right: 50px; |
|||
|
|||
&:last-child, { |
|||
margin-right: 0; |
|||
} |
|||
} |
|||
|
|||
.support-box:hover { |
|||
.support-box-link { |
|||
color: #5683ad; |
|||
font-weight: 500; |
|||
} |
|||
} |
|||
|
|||
.login-settings-modal { |
|||
#AppUpdate { |
|||
.col-sm-4 { |
|||
width: 100%; |
|||
} |
|||
} |
|||
.modal-dialog { |
|||
width: 80%; |
|||
} |
|||
.modal-body { |
|||
background: #f3f4f5; |
|||
border-radius: 4px; |
|||
} |
|||
.modal-footer { |
|||
margin-top: 15px; |
|||
} |
|||
.page-content { |
|||
padding-top: 0; |
|||
} |
|||
.support-box-wrapper { |
|||
.support-box { |
|||
margin: 0; |
|||
margin-bottom: 20px; |
|||
} |
|||
} |
|||
} |
|||
|
|||
.settings-help { |
|||
font-size: 20px; |
|||
position: relative; |
|||
top: 2px; |
|||
left: 10px; |
|||
color: #5683ad; |
|||
} |
|||
|
|||
#SettingsAccordion { |
|||
.toggle { |
|||
position: relative; |
|||
top: 4px; |
|||
|
|||
.title { |
|||
position: relative; |
|||
top: -12px; |
|||
left: 12px; |
|||
} |
|||
} |
|||
table { |
|||
width: 100%; |
|||
|
|||
td { |
|||
&:first-child { |
|||
width: 40%; |
|||
} |
|||
&:last-child { |
|||
width: 60%; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
#SettingsAccordion { |
|||
.panel { |
|||
.panel-collapse { |
|||
transition: all .3s; |
|||
|
|||
&.collapse { |
|||
height: 0; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
#section-iguana-wallet-settings { |
|||
background: #f3f4f5; |
|||
|
|||
.panel-title { |
|||
cursor: pointer; |
|||
cursor: hand; |
|||
|
|||
&:before { |
|||
content: '\F273'; |
|||
} |
|||
&.collapsed { |
|||
&:before { |
|||
content: '\F278'; |
|||
} |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,82 @@ |
|||
import React from 'react'; |
|||
import { translate } from '../../../translate/translate'; |
|||
|
|||
class SupportPanel extends React.Component { |
|||
constructor() { |
|||
super(); |
|||
} |
|||
|
|||
openExternalWindow(url) { |
|||
const remote = window.require('electron').remote; |
|||
const BrowserWindow = remote.BrowserWindow; |
|||
|
|||
const externalWindow = new BrowserWindow({ |
|||
width: 1280, |
|||
height: 800, |
|||
title: `${translate('INDEX.LOADING')}...`, |
|||
icon: remote.getCurrentWindow().iguanaIcon, |
|||
}); |
|||
|
|||
externalWindow.loadURL(url); |
|||
externalWindow.webContents.on('did-finish-load', function() { |
|||
setTimeout(function() { |
|||
externalWindow.show(); |
|||
}, 40); |
|||
}); |
|||
} |
|||
|
|||
render() { |
|||
return ( |
|||
<div className="panel-body"> |
|||
<div className="col-sm-12 no-padding-left"> |
|||
<div className="support-box-wrapper"> |
|||
<div |
|||
className="support-box" |
|||
onClick={ () => this.openExternalWindow('http://support.supernet.org') }> |
|||
<img |
|||
src="assets/images/cryptologo/supernet.png" |
|||
alt="Support tickets" /> |
|||
<div className="support-box-title">{ translate('SETTINGS.SUPPORT_TICKETS') }</div> |
|||
<div className="support-box-link">support.supernet.org</div> |
|||
</div> |
|||
</div> |
|||
<div className="support-box-wrapper"> |
|||
<div |
|||
className="support-box" |
|||
onClick={ () => this.openExternalWindow('https://sprnt.slack.com') }> |
|||
<img |
|||
src="assets/images/support/slack-icon.png" |
|||
alt="Slack" /> |
|||
<div className="support-box-title">Slack</div> |
|||
<div className="support-box-link">sprnt.slack.com</div> |
|||
</div> |
|||
</div> |
|||
<div className="support-box-wrapper"> |
|||
<div |
|||
className="support-box" |
|||
onClick={ () => this.openExternalWindow('http://slackinvite.supernet.org') }> |
|||
<img |
|||
src="assets/images/support/slack-invite-icon.png" |
|||
alt="Slack invite" /> |
|||
<div className="support-box-title">{ translate('SETTINGS.GET_SLACK_INVITE') }</div> |
|||
<div className="support-box-link">slackinvite.supernet.org</div> |
|||
</div> |
|||
</div> |
|||
<div className="support-box-wrapper"> |
|||
<div |
|||
className="support-box" |
|||
onClick={ () => this.openExternalWindow('https://github.com/SuperNETorg/Agama') }> |
|||
<img |
|||
src="assets/images/support/github-icon.png" |
|||
alt="Github" /> |
|||
<div className="support-box-title">Github</div> |
|||
<div className="support-box-link">github.com/SuperNETorg/Agama</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
); |
|||
}; |
|||
} |
|||
|
|||
export default SupportPanel; |
@ -0,0 +1,23 @@ |
|||
import React from 'react'; |
|||
import { translate } from '../../../translate/translate'; |
|||
import { connect } from 'react-redux'; |
|||
|
|||
class WalletBackupPanel extends React.Component { |
|||
constructor() { |
|||
super(); |
|||
} |
|||
|
|||
render() { |
|||
return ( |
|||
<div className="panel-body">Wallet Backup section to be updated soon.</div> |
|||
); |
|||
}; |
|||
} |
|||
|
|||
const mapStateToProps = (state) => { |
|||
return { |
|||
Settings: state.Settings, |
|||
}; |
|||
}; |
|||
|
|||
export default connect(mapStateToProps)(WalletBackupPanel); |
@ -0,0 +1,60 @@ |
|||
import React from 'react'; |
|||
import { translate } from '../../../translate/translate'; |
|||
import { connect } from 'react-redux'; |
|||
|
|||
class WalletInfoPanel extends React.Component { |
|||
constructor() { |
|||
super(); |
|||
} |
|||
|
|||
render() { |
|||
return ( |
|||
<div className="panel-body"> |
|||
<table className="table"> |
|||
<thead> |
|||
<tr> |
|||
<th width="10%">{ translate('INDEX.KEY') }</th> |
|||
<th>{ translate('INDEX.VALUE') }</th> |
|||
</tr> |
|||
</thead> |
|||
<tbody> |
|||
<tr> |
|||
<td className="wallet-info-key">pubkey</td> |
|||
<td>{ this.props.Main.activeHandle.pubkey }</td> |
|||
</tr> |
|||
<tr> |
|||
<td className="wallet-info-key">btcpubkey</td> |
|||
<td>{ this.props.Main.activeHandle.btcpubkey }</td> |
|||
</tr> |
|||
<tr> |
|||
<td className="wallet-info-key">rmd160</td> |
|||
<td>{ this.props.Main.activeHandle.rmd160 }</td> |
|||
</tr> |
|||
<tr> |
|||
<td className="wallet-info-key">NXT</td> |
|||
<td>{ this.props.Main.activeHandle.NXT }</td> |
|||
</tr> |
|||
<tr> |
|||
<td className="wallet-info-key">notary</td> |
|||
<td>{ this.props.Main.activeHandle.notary }</td> |
|||
</tr> |
|||
<tr> |
|||
<td className="wallet-info-key">status</td> |
|||
<td>{ this.props.Main.activeHandle.status }</td> |
|||
</tr> |
|||
</tbody> |
|||
</table> |
|||
</div> |
|||
); |
|||
}; |
|||
} |
|||
|
|||
const mapStateToProps = (state) => { |
|||
return { |
|||
Main: { |
|||
activeHandle: state.Main.activeHandle, |
|||
}, |
|||
}; |
|||
}; |
|||
|
|||
export default connect(mapStateToProps)(WalletInfoPanel); |