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)); |
|
||||
}) |
|
||||
} |
|
||||
} |
|
@ -1,63 +1,81 @@ |
|||||
import React from 'react'; |
import React from 'react'; |
||||
|
import { connect } from 'react-redux'; |
||||
import { translate } from '../../translate/translate'; |
import { translate } from '../../translate/translate'; |
||||
import Config from '../../config'; |
import Config from '../../config'; |
||||
|
|
||||
class AddCoinOptionsCrypto extends React.Component { |
class AddCoinOptionsCrypto extends React.Component { |
||||
constructor(props) { |
constructor() { |
||||
super(props); |
super(); |
||||
this.state = { |
this.state = { |
||||
nativeOnly: Config.iguanaLessMode, |
nativeOnly: Config.iguanaLessMode, |
||||
} |
} |
||||
} |
} |
||||
|
|
||||
render() { |
render() { |
||||
const isWindows = this.props.appSettings && this.props.appSettings.appInfo && this.props.appSettings.appInfo.sysInfo && this.props.appSettings.appInfo.sysInfo.platform === 'win32'; |
// const isWindows = this.props.appSettings && this.props.appSettings.appInfo && this.props.appSettings.appInfo.sysInfo && this.props.appSettings.appInfo.sysInfo.platform === 'win32';
|
||||
|
let appConfig; |
||||
|
|
||||
|
try { |
||||
|
appConfig = window.require('electron').remote.getCurrentWindow().appConfig; |
||||
|
} catch (e) {} |
||||
|
|
||||
//<option value="ANC|full">AnonCoin (ANC)</option>
|
|
||||
//<option value="MZC|full">MazaCoin (MZC)</option>
|
|
||||
//<option value="SYS|full">SysCoin (SYS)</option>
|
|
||||
return ( |
return ( |
||||
<optgroup label={ translate('ADD_COIN.CRYPTO_CURRENCIES') }> |
<optgroup label={ translate('ADD_COIN.CRYPTO_CURRENCIES') }> |
||||
|
<option |
||||
|
value="ANC|full" |
||||
|
className={ this.state.nativeOnly || !appConfig.experimentalFeatures ? 'hide' : '' }>AnonCoin (ANC)</option> |
||||
<option |
<option |
||||
value="BTCD|full" |
value="BTCD|full" |
||||
className={ this.state.nativeOnly || isWindows ? 'hide' : '' }>BitcoinDark (BTCD)</option> |
className={ this.state.nativeOnly || !appConfig.experimentalFeatures ? 'hide' : '' }>BitcoinDark (BTCD)</option> |
||||
<option |
<option |
||||
value="BTC|full" |
value="BTC|full" |
||||
className={ this.state.nativeOnly || isWindows ? 'hide' : '' }>Bitcoin (BTC)</option> |
className={ this.state.nativeOnly || !appConfig.experimentalFeatures ? 'hide' : '' }>Bitcoin (BTC)</option> |
||||
<option |
<option |
||||
value="BTM|full" |
value="BTM|full" |
||||
className={ this.state.nativeOnly || isWindows ? 'hide' : '' }>Bitmark (BTM)</option> |
className={ this.state.nativeOnly || !appConfig.experimentalFeatures ? 'hide' : '' }>Bitmark (BTM)</option> |
||||
<option |
<option |
||||
value="CARB|full" |
value="CARB|full" |
||||
className={ this.state.nativeOnly || isWindows ? 'hide' : '' }>Carboncoin (CARB)</option> |
className={ this.state.nativeOnly || !appConfig.experimentalFeatures ? 'hide' : '' }>Carboncoin (CARB)</option> |
||||
<option |
<option |
||||
value="DGB|full" |
value="DGB|full" |
||||
className={ this.state.nativeOnly || isWindows ? 'hide' : '' }>Digibyte (DGB)</option> |
className={ this.state.nativeOnly || !appConfig.experimentalFeatures ? 'hide' : '' }>Digibyte (DGB)</option> |
||||
<option |
<option |
||||
value="DOGE|full" |
value="DOGE|full" |
||||
className={ this.state.nativeOnly || isWindows ? 'hide' : '' }>Dogecoin (DOGE)</option> |
className={ this.state.nativeOnly || !appConfig.experimentalFeatures ? 'hide' : '' }>Dogecoin (DOGE)</option> |
||||
<option |
<option |
||||
value="FRK|full" |
value="FRK|full" |
||||
className={ this.state.nativeOnly || isWindows ? 'hide' : '' }>Franko (FRK)</option> |
className={ this.state.nativeOnly || !appConfig.experimentalFeatures ? 'hide' : '' }>Franko (FRK)</option> |
||||
<option |
<option |
||||
value="GAME|full" |
value="GAME|full" |
||||
className={ this.state.nativeOnly || isWindows ? 'hide' : '' }>Gamecredits (GAME)</option> |
className={ this.state.nativeOnly || !appConfig.experimentalFeatures ? 'hide' : '' }>Gamecredits (GAME)</option> |
||||
<option value="KMD|basilisk|native">Komodo (KMD)</option> |
<option value="KMD|basilisk|native">Komodo (KMD)</option> |
||||
|
<option |
||||
|
value="MZC|full" |
||||
|
className={ this.state.nativeOnly || !appConfig.experimentalFeatures ? 'hide' : '' }>MazaCoin (MZC)</option> |
||||
<option |
<option |
||||
value="LTC|full" |
value="LTC|full" |
||||
className={ this.state.nativeOnly || isWindows ? 'hide' : '' }>Litecoin (LTC)</option> |
className={ this.state.nativeOnly || !appConfig.experimentalFeatures ? 'hide' : '' }>Litecoin (LTC)</option> |
||||
|
<option |
||||
|
value="SYS|full" |
||||
|
className={ this.state.nativeOnly || !appConfig.experimentalFeatures ? 'hide' : '' }>SysCoin (SYS)</option> |
||||
<option |
<option |
||||
value="UNO|full" |
value="UNO|full" |
||||
className={ this.state.nativeOnly || isWindows ? 'hide' : '' }>Unobtanium (UNO)</option> |
className={ this.state.nativeOnly || !appConfig.experimentalFeatures ? 'hide' : '' }>Unobtanium (UNO)</option> |
||||
<option |
<option |
||||
value="ZEC|full" |
value="ZEC|full" |
||||
className={ this.state.nativeOnly || isWindows ? 'hide' : '' }>Zcash (ZEC)</option> |
className={ this.state.nativeOnly || !appConfig.experimentalFeatures ? 'hide' : '' }>Zcash (ZEC)</option> |
||||
<option |
<option |
||||
value="ZET|full" |
value="ZET|full" |
||||
className={ this.state.nativeOnly || isWindows ? 'hide' : '' }>Zetacoin (ZET)</option> |
className={ this.state.nativeOnly || !appConfig.experimentalFeatures ? 'hide' : '' }>Zetacoin (ZET)</option> |
||||
</optgroup> |
</optgroup> |
||||
); |
); |
||||
} |
} |
||||
} |
} |
||||
|
|
||||
export default AddCoinOptionsCrypto; |
const mapStateToProps = (state) => { |
||||
|
return { |
||||
|
appSettings: state.appSettings, |
||||
|
}; |
||||
|
}; |
||||
|
|
||||
|
export default connect(mapStateToProps)(AddCoinOptionsCrypto); |
||||
|
@ -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,110 +0,0 @@ |
|||||
import React from 'react'; |
|
||||
import { sortByDate } from '../../../util/sort'; |
|
||||
import Config from '../../../config'; |
|
||||
import { |
|
||||
NotificationsByTypeRender, |
|
||||
NotificationsModalRender, |
|
||||
NotificationsRender |
|
||||
} from './notifications.render'; |
|
||||
|
|
||||
class Notifications extends React.Component { |
|
||||
constructor(props) { |
|
||||
super(props); |
|
||||
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); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
export default 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'; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |