Browse Source

Merge pull request #173 from SuperNETorg/feature/settings-refactor

Feature/settings refactor
all-modes
pbca26 7 years ago
committed by GitHub
parent
commit
32c86f59e0
  1. 4
      react/src/actions/actions/settings.js
  2. 131
      react/src/components/dashboard/coinTile/coinTileItem.js
  3. 2
      react/src/components/dashboard/main/dashboard.render.js
  4. 180
      react/src/components/dashboard/settings/settings.addNodePanel.js
  5. 90
      react/src/components/dashboard/settings/settings.appInfoPanel.js
  6. 321
      react/src/components/dashboard/settings/settings.appSettingsPanel.js
  7. 151
      react/src/components/dashboard/settings/settings.appUpdatePanel.js
  8. 206
      react/src/components/dashboard/settings/settings.cliPanel.js
  9. 180
      react/src/components/dashboard/settings/settings.debugLogPanel.js
  10. 221
      react/src/components/dashboard/settings/settings.exportKeysPanel.js
  11. 23
      react/src/components/dashboard/settings/settings.fiatCurrencyPanel.js
  12. 67
      react/src/components/dashboard/settings/settings.importKeysPanel.js
  13. 870
      react/src/components/dashboard/settings/settings.js
  14. 596
      react/src/components/dashboard/settings/settings.render.js
  15. 82
      react/src/components/dashboard/settings/settings.supportPanel.js
  16. 23
      react/src/components/dashboard/settings/settings.walletBackupPanel.js
  17. 60
      react/src/components/dashboard/settings/settings.walletInfoPanel.js

4
react/src/actions/actions/settings.js

@ -52,7 +52,7 @@ function parseImportPrivKeyResponse(json, dispatch) {
return dispatch => {
dispatch(
triggerToaster(
transalte('API.ILLEGAL_PRIVKEY'),
translate('API.ILLEGAL_PRIVKEY'),
translate('TOASTR.SETTINGS_NOTIFICATION'),
'error'
)
@ -63,7 +63,7 @@ function parseImportPrivKeyResponse(json, dispatch) {
return dispatch => {
dispatch(
triggerToaster(
transalte('API.PRIVKEY_IN_WALLET'),
translate('API.PRIVKEY_IN_WALLET'),
translate('TOASTR.SETTINGS_NOTIFICATION'),
'warning'
)

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

@ -79,86 +79,89 @@ class CoinTileItem extends React.Component {
}
dispatchCoinActions(coin, mode) {
if (mode === 'native') {
Store.dispatch(iguanaActiveHandle(true));
const _propsDashboard = this.props.ActiveCoin;
const syncPercentage = _propsDashboard && _propsDashboard.progress && (parseFloat(parseInt(_propsDashboard.progress.blocks, 10) * 100 / parseInt(_propsDashboard.progress.longestchain, 10)).toFixed(2)).replace('NaN', 0);
if (this.props.Dashboard &&
this.props.Dashboard.activeSection === 'wallets') {
if (mode === 'native') {
Store.dispatch(iguanaActiveHandle(true));
const _propsDashboard = this.props.ActiveCoin;
const syncPercentage = _propsDashboard && _propsDashboard.progress && (parseFloat(parseInt(_propsDashboard.progress.blocks, 10) * 100 / parseInt(_propsDashboard.progress.longestchain, 10)).toFixed(2)).replace('NaN', 0);
if ((syncPercentage < 100 &&
!this.props.Dashboard.displayCoindDownModal) ||
this.props.ActiveCoin.rescanInProgress) {
if (coin === 'KMD') {
Store.dispatch(getDebugLog('komodo', 50));
} else {
Store.dispatch(getDebugLog('komodo', 50, coin));
}
}
if (!this.props.Dashboard.displayCoindDownModal &&
_propsDashboard.progress &&
_propsDashboard.progress.blocks &&
_propsDashboard.progress.longestchain &&
syncPercentage &&
(Config.iguanaLessMode || syncPercentage >= NATIVE_MIN_SYNC_PERCENTAGE_THRESHOLD)) {
Store.dispatch(
getSyncInfoNative(
coin,
true,
this.props.Dashboard.skipFullDashboardUpdate,
this.props.ActiveCoin.rescanInProgress
)
);
if ((syncPercentage < 100 &&
!this.props.Dashboard.displayCoindDownModal) ||
this.props.ActiveCoin.rescanInProgress) {
if (coin === 'KMD') {
Store.dispatch(getDebugLog('komodo', 50));
if (!this.props.Dashboard.skipFullDashboardUpdate) {
Store.dispatch(getDashboardUpdate(coin, _propsDashboard));
}
} else {
Store.dispatch(getDebugLog('komodo', 50, coin));
Store.dispatch(
getSyncInfoNative(
coin,
null,
this.props.Dashboard.skipFullDashboardUpdate,
this.props.ActiveCoin.rescanInProgress
)
);
}
}
if (mode === 'full') {
Store.dispatch(iguanaActiveHandle(true));
Store.dispatch(getSyncInfo(coin));
Store.dispatch(iguanaEdexBalance(coin, mode));
Store.dispatch(getAddressesByAccount(coin, mode));
Store.dispatch(getFullTransactionsList(coin));
}
if (mode === 'basilisk') {
const useAddress = this.props.ActiveCoin.mainBasiliskAddress ? this.props.ActiveCoin.mainBasiliskAddress : this.props.Dashboard.activeHandle[coin];
Store.dispatch(iguanaActiveHandle(true));
if (!this.props.Dashboard.displayCoindDownModal &&
_propsDashboard.progress &&
_propsDashboard.progress.blocks &&
_propsDashboard.progress.longestchain &&
syncPercentage &&
(Config.iguanaLessMode || syncPercentage >= NATIVE_MIN_SYNC_PERCENTAGE_THRESHOLD)) {
Store.dispatch(
getSyncInfoNative(
getKMDAddressesNative(
coin,
true,
this.props.Dashboard.skipFullDashboardUpdate,
this.props.ActiveCoin.rescanInProgress
mode,
useAddress
)
);
if (!this.props.Dashboard.skipFullDashboardUpdate) {
Store.dispatch(getDashboardUpdate(coin, _propsDashboard));
}
} else {
Store.dispatch(
getSyncInfoNative(
coin,
null,
this.props.Dashboard.skipFullDashboardUpdate,
this.props.ActiveCoin.rescanInProgress
getShepherdCache(
JSON.parse(sessionStorage.getItem('IguanaActiveAccount')).pubkey,
coin
)
);
}
}
if (mode === 'full') {
Store.dispatch(iguanaActiveHandle(true));
Store.dispatch(getSyncInfo(coin));
Store.dispatch(iguanaEdexBalance(coin, mode));
Store.dispatch(getAddressesByAccount(coin, mode));
Store.dispatch(getFullTransactionsList(coin));
}
if (mode === 'basilisk') {
const useAddress = this.props.ActiveCoin.mainBasiliskAddress ? this.props.ActiveCoin.mainBasiliskAddress : this.props.Dashboard.activeHandle[coin];
Store.dispatch(iguanaActiveHandle(true));
Store.dispatch(
getKMDAddressesNative(
coin,
mode,
useAddress
)
);
Store.dispatch(
getShepherdCache(
JSON.parse(sessionStorage.getItem('IguanaActiveAccount')).pubkey,
coin
)
);
if (this.props &&
this.props.Dashboard &&
this.props.Dashboard.activeHandle &&
this.props.Dashboard.activeHandle[coin]) {
if (!this.props.ActiveCoin.addresses) {
Store.dispatch(getAddressesByAccount(coin, mode));
}
if (this.props &&
this.props.Dashboard &&
this.props.Dashboard.activeHandle &&
this.props.Dashboard.activeHandle[coin]) {
if (!this.props.ActiveCoin.addresses) {
Store.dispatch(getAddressesByAccount(coin, mode));
Store.dispatch(getBasiliskTransactionsList(coin, useAddress));
}
Store.dispatch(getBasiliskTransactionsList(coin, useAddress));
}
}
}

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

@ -50,7 +50,7 @@ const DashboardRender = function() {
<Jumblr />
}
{ this.isSectionActive('settings') &&
<Settings />
<Settings disableWalletSpecificUI={false} />
}
{ this.isSectionActive('about') &&
<About />

180
react/src/components/dashboard/settings/settings.addNodePanel.js

@ -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);

90
react/src/components/dashboard/settings/settings.appInfoPanel.js

@ -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);

321
react/src/components/dashboard/settings/settings.appSettingsPanel.js

@ -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);

151
react/src/components/dashboard/settings/settings.appUpdatePanel.js

@ -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);

206
react/src/components/dashboard/settings/settings.cliPanel.js

@ -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);

180
react/src/components/dashboard/settings/settings.debugLogPanel.js

@ -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, '').replace('\\n', ' ') }</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);

221
react/src/components/dashboard/settings/settings.exportKeysPanel.js

@ -0,0 +1,221 @@
import React from 'react';
import { translate } from '../../../translate/translate';
import { connect } from 'react-redux';
import {
encryptWallet,
settingsWifkeyState,
} 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.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;
}
}
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>{ _key }</td>
<td className="padding-left-15">{ _wifKeys[_key] }</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);

23
react/src/components/dashboard/settings/settings.fiatCurrencyPanel.js

@ -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);

67
react/src/components/dashboard/settings/settings.importKeysPanel.js

@ -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;

870
react/src/components/dashboard/settings/settings.js

@ -4,37 +4,31 @@ import { translate } from '../../../translate/translate';
import Config from '../../../config';
import {
iguanaActiveHandle,
encryptWallet,
settingsWifkeyState,
importPrivKey,
getDebugLog,
getAppConfig,
getPeersList,
addPeerNode,
getAppConfig,
saveAppConfig,
resetAppConfig,
getAppInfo,
shepherdCli,
checkForUpdateUIPromise,
updateUIPromise,
triggerToaster,
skipFullDashboardUpdate,
} from '../../../actions/actionCreators';
import Store from '../../../store';
import { secondsToString } from '../../../util/time';
import {
AppInfoTabRender,
SettingsRender,
AppUpdateTabRender,
} from './settings.render';
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,
};
import AppUpdatePanel from './settings.appUpdatePanel';
import AppInfoPanel from './settings.appInfoPanel';
import AddNodePanel from './settings.addNodePanel';
import AppSettingsPanel from './settings.appSettingsPanel';
import CliPanel from './settings.cliPanel';
import DebugLogPanel from './settings.debugLogPanel';
import FiatCurrencyPanel from './settings.fiatCurrencyPanel';
import ExportKeysPanel from './settings.exportKeysPanel';
import ImportKeysPanel from './settings.importKeysPanel';
import SupportPanel from './settings.supportPanel';
import WalletInfoPanel from './settings.walletInfoPanel';
import WalletBackupPanel from './settings.walletBackupPanel';
/*
TODO:
@ -48,125 +42,12 @@ class Settings extends React.Component {
super();
this.state = {
activeTab: 0,
debugLinesCount: 10,
debugTarget: 'iguana',
activeTabHeight: '0',
appSettings: {},
appConfigSchema: {},
tabElId: null,
cliCmdString: '',
cliCoin: null,
cliResponse: null,
exportWifKeysRaw: false,
seedInputVisibility: false,
nativeOnly: Config.iguanaLessMode,
updatePatch: null,
updateBins: null,
updateLog: [],
updateProgressPatch: null,
wifkeysPassphrase: '',
trimPassphraseTimer: null,
disableWalletSpecificUI: null,
appRuntimeLog: [],
toggleAppRuntimeLog: false,
disableWalletSpecificUI: false,
};
this.exportWifKeys = this.exportWifKeys.bind(this);
this.updateInput = this.updateInput.bind(this);
this.importWifKey = this.importWifKey.bind(this);
this.readDebugLog = this.readDebugLog.bind(this);
this.checkNodes = this.checkNodes.bind(this);
this.addNode = this.addNode.bind(this);
this.renderPeersList = this.renderPeersList.bind(this);
this.renderSNPeersList = this.renderSNPeersList.bind(this);
this._saveAppConfig = this._saveAppConfig.bind(this);
this._resetAppConfig = this._resetAppConfig.bind(this);
this.exportWifKeysRaw = this.exportWifKeysRaw.bind(this);
this.toggleSeedInputVisibility = this.toggleSeedInputVisibility.bind(this);
this._checkForUpdateUIPromise = this._checkForUpdateUIPromise.bind(this);
this._updateUIPromise = this._updateUIPromise.bind(this);
this.updateTabDimensions = this.updateTabDimensions.bind(this);
this._skipFullDashboardUpdate = this._skipFullDashboardUpdate.bind(this);
this.getAppRuntimeLog = this.getAppRuntimeLog.bind(this);
this.toggleAppRuntimeLog = this.toggleAppRuntimeLog.bind(this);
this.renderAppRuntimeLog = this.renderAppRuntimeLog.bind(this);
}
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, '').replace('\\n', ' ') }</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,
}));
});
}
_skipFullDashboardUpdate() {
Store.dispatch(skipFullDashboardUpdate(!this.props.Dashboard.skipFullDashboardUpdate));
}
updateTabDimensions() {
setTimeout(() => {
const _height = document.querySelector(`#${this.state.tabElId} .panel-collapse .panel-body`).offsetHeight;
this.setState(Object.assign({}, this.state, {
activeTabHeight: _height,
}));
}, 100);
}
componentWillMount() {
socket.on('patch', msg => this.updateSocketsData(msg));
window.addEventListener('resize', this.updateTabDimensions);
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) {}
}
componentWillUnmount() {
socket.removeAllListeners('patch', msg => this.updateSocketsData(msg));
window.removeEventListener('resize', this.updateTabDimensions);
if (!this.state.disableWalletSpecificUI) {
document.documentElement.style.height = '100%';
document.body.style.height = '100%';
}
}
componentDidMount(props) {
@ -176,743 +57,84 @@ class Settings extends React.Component {
Store.dispatch(getAppConfig());
Store.dispatch(getAppInfo());
document.getElementById('section-iguana-wallet-settings').setAttribute('style', 'height:auto; min-height: 100%');
}
componentWillReceiveProps(props) {
if (this.state.tabElId) {
const _height = document.querySelector(`#${this.state.tabElId} .panel-collapse .panel-body`).offsetHeight;
this.setState(Object.assign({}, this.state, {
activeTab: this.state.activeTab,
activeTabHeight: _height,
tabElId: this.state.tabElId,
disableWalletSpecificUI: props.disableWalletSpecificUI,
disableWalletSpecificUI: this.props.disableWalletSpecificUI,
}));
}
}
openExternalWindow(url) {
const remote = window.require('electron').remote;
const BrowserWindow = remote.BrowserWindow;
const externalWindow = new BrowserWindow({
width: 1280,
height: 800,
title: `${translate('INDEX.LOADING')}...`,
icon: remote.getCurrentWindow().iguanaIcon,
});
externalWindow.loadURL(url);
externalWindow.webContents.on('did-finish-load', function() {
setTimeout(function() {
externalWindow.show();
}, 40);
});
}
_resetAppConfig() {
Store.dispatch(resetAppConfig());
}
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);
}
updateSocketsData(data) {
if (data &&
data.msg &&
data.msg.type === 'ui') {
if (data.msg.status === 'progress' &&
data.msg.progress &&
data.msg.progress < 100) {
this.setState(Object.assign({}, this.state, {
updateProgressPatch: data.msg.progress,
}));
updateProgressBar.patch = data.msg.progress;
} else {
if (data.msg.status === 'progress' &&
data.msg.progress &&
data.msg.progress === 100) {
let _updateLog = [];
_updateLog.push(`${translate('INDEX.UI_UPDATE_DOWNLOADED')}...`);
this.setState(Object.assign({}, this.state, {
updateLog: _updateLog,
}));
updateProgressBar.patch = 100;
}
if (data.msg.status === 'done') {
let _updateLog = [];
_updateLog.push(translate('INDEX.UI_UPDATED'));
this.setState(Object.assign({}, this.state, {
updateLog: _updateLog,
updatePatch: null,
}));
updateProgressBar.patch = -1;
}
if (data.msg.status === 'error') {
let _updateLog = [];
_updateLog.push(translate('INDEX.UI_UPDATE_ERROR'));
this.setState(Object.assign({}, this.state, {
updateLog: _updateLog,
}));
updateProgressBar.patch = -1;
}
}
} else {
if (data &&
data.msg) {
let _updateLog = this.state.updateLog;
_updateLog.push(data.msg);
this.setState(Object.assign({}, this.state, {
updateLog: _updateLog,
}));
}
}
}
_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,
}));
});
}
_updateUIPromise() {
updateProgressBar.patch = 0;
let _updateLog = [];
_updateLog.push(`${translate('INDEX.DOWNLOADING_UI_UPDATE')}...`);
this.setState(Object.assign({}, this.state, {
updateLog: _updateLog,
openTab(elemId, tab) {
this.setState(Object.assign({}, this.state, {
activeTab: tab,
tabElId: elemId,
}));
updateUIPromise();
}
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;
}
}
toggleSeedInputVisibility() {
updateInput(e) {
this.setState({
seedInputVisibility: !this.state.seedInputVisibility,
[e.target.name]: e.target.value,
});
}
execCliCmd() {
Store.dispatch(
shepherdCli(
'passthru',
this.state.cliCoin,
this.state.cliCmdString
)
);
}
openTab(elemId, tab) {
setTimeout(() => {
const _height = document.querySelector(`#${elemId} .panel-collapse .panel-body`).offsetHeight;
this.setState(Object.assign({}, this.state, {
activeTab: tab,
activeTabHeight: _height,
tabElId: elemId,
}));
// body size hack
if (!this.state.disableWalletSpecificUI) {
document.documentElement.style.height = '100%';
document.body.style.height = '100%';
setTimeout(() => {
document.documentElement.style.height = _height <= 200 ? '100%' : 'inherit';
document.body.style.height = _height <= 200 ? '100%' : 'inherit';
}, 100);
}
}, 100);
}
exportWifKeys() {
Store.dispatch(
encryptWallet(
this.state.wifkeysPassphrase,
settingsWifkeyState,
this.props.ActiveCoin.coin
)
);
}
importWifKey() {
Store.dispatch(importPrivKey(this.state.importWifKey));
}
readDebugLog() {
Store.dispatch(
getDebugLog(
this.state.debugTarget,
this.state.debugLinesCount
)
);
}
checkNodes() {
if (this.state.getPeersCoin) {
Store.dispatch(getPeersList(this.state.getPeersCoin.split('|')[0]));
}
}
addNode() {
if (this.state.addNodeCoin) {
Store.dispatch(
addPeerNode(
this.state.addNodeCoin.split('|')[0],
this.state.addPeerIP
)
);
}
}
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;
}
}
renderAppInfoTab() {
const releaseInfo = this.props.Settings.appInfo && this.props.Settings.appInfo.releaseInfo;
if (releaseInfo) {
return AppInfoTabRender.call(this);
return <AppInfoPanel />
}
return null;
}
renderAppUpdateTab() {
return AppUpdateTabRender.call(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;
}
return <AppUpdatePanel />
}
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,
});
}
_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].type === 'number' ? Number(_appSettings[key]) : _appSettings[key];
if (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));
}
renderWalletInfo() {
return <WalletInfoPanel />
}
renderConfigEditForm() {
let items = [];
const _appConfig = this.state.appSettings;
for (let key in _appConfig) {
if (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].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;
renderAddNode() {
return <AddNodePanel />
}
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,
});
}
renderWalletBackup() {
return <WalletBackupPanel />
}
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;
}
renderFiatCurrency() {
return <FiatCurrencyPanel />
}
renderLB(_translationID) {
const _translationComponents = translate(_translationID).split('<br>');
return _translationComponents.map((_translation) =>
<span key={ `settings-label-${Math.random(0, 9) * 10}` }>
{ _translation }
<br />
</span>
);
renderExportKeys() {
return <ExportKeysPanel />
}
// 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;
}
renderImportKeys() {
return <ImportKeysPanel />
}
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;
}
renderDebugLog() {
return <DebugLogPanel />
}
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;
}
renderAppSettings() {
return <AppSettingsPanel />
}
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>{ _key }</td>
<td className="padding-left-15">{ _wifKeys[_key] }</td>
</tr>
);
}
}
}
return items;
} else {
return null;
}
renderCliPanel() {
return <CliPanel />
}
exportWifKeysRaw() {
this.setState(Object.assign({}, this.state, {
exportWifKeysRaw: !this.state.exportWifKeysRaw,
}));
renderSupportPanel() {
return <SupportPanel />
}
render() {

596
react/src/components/dashboard/settings/settings.render.js

@ -1,146 +1,5 @@
import React from 'react';
import { translate } from '../../../translate/translate';
import AddCoinOptionsCrypto from '../../addcoin/addcoinOptionsCrypto';
import AddCoinOptionsAC from '../../addcoin/addcoinOptionsAC';
import AddCoinOptionsACFiat from '../../addcoin/addcoinOptionsACFiat';
export const AppUpdateTabRender = function() {
return (
<div
className="panel"
id="AppUpdate"
onClick={ () => this.openTab('AppUpdate', 10) }>
<div className="panel-heading">
<a className={ 'panel-title' + (this.state.activeTab === 10 ? '' : ' collapsed') }>
<i className="icon fa fa-cloud-download"></i> { translate('INDEX.UPDATE') }
</a>
</div>
<div
className={ 'panel-collapse collapse' + (this.state.activeTab === 10 ? ' in' : '') }
style={{ height: this.state.activeTab === 10 ? `${this.state.activeTabHeight}px` : '0' }}>
<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>
</div>
</div>
);
};
export const AppInfoTabRender = function() {
return (
<div
className="panel"
id="AppInfo"
onClick={ () => this.openTab('AppInfo', 8) }>
<div className="panel-heading">
<a className={ 'panel-title' + (this.state.activeTab === 8 ? '' : ' collapsed') }>
<i className="icon md-info"></i>{ translate('SETTINGS.APP_INFO') }
</a>
</div>
<div
className={ 'panel-collapse collapse' + (this.state.activeTab === 8 ? ' in' : '') }
style={{ height: this.state.activeTab === 8 ? `${this.state.activeTabHeight}px` : '0' }}>
<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>
</div>
</div>
);
};
export const SettingsRender = function() {
return (
@ -156,6 +15,7 @@ export const SettingsRender = function() {
<div
className="panel-group"
id="SettingsAccordion">
{ !this.props.disableWalletSpecificUI &&
<div
id="WalletInfo"
@ -168,43 +28,8 @@ export const SettingsRender = function() {
</div>
<div
className={ 'panel-collapse collapse' + (this.state.activeTab === 0 ? ' in' : '') }
style={{ height: this.state.activeTab === 0 ? `${this.state.activeTabHeight}px` : '0' }}>
<div className="panel-body">
<table className="table">
<thead>
<tr>
<th width="10%">{ translate('INDEX.KEY') }</th>
<th>{ translate('INDEX.VALUE') }</th>
</tr>
</thead>
<tbody>
<tr>
<td className="wallet-info-key">pubkey</td>
<td>{ this.props.Main.activeHandle.pubkey }</td>
</tr>
<tr>
<td className="wallet-info-key">btcpubkey</td>
<td>{ this.props.Main.activeHandle.btcpubkey }</td>
</tr>
<tr>
<td className="wallet-info-key">rmd160</td>
<td>{ this.props.Main.activeHandle.rmd160 }</td>
</tr>
<tr>
<td className="wallet-info-key">NXT</td>
<td>{ this.props.Main.activeHandle.NXT }</td>
</tr>
<tr>
<td className="wallet-info-key">notary</td>
<td>{ this.props.Main.activeHandle.notary }</td>
</tr>
<tr>
<td className="wallet-info-key">status</td>
<td>{ this.props.Main.activeHandle.status }</td>
</tr>
</tbody>
</table>
</div>
style={{ height: this.state.activeTab === 0 ? `auto` : '0' }}>
{ this.renderWalletInfo() }
</div>
</div>
}
@ -220,78 +45,8 @@ export const SettingsRender = function() {
</div>
<div
className={ 'panel-collapse collapse' + (this.state.activeTab === 1 ? ' in' : '') }
style={{ height: this.state.activeTab === 1 ? `${this.state.activeTabHeight}px` : '0' }}>
<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>
<p>{ this.renderSNPeersList() }</p>
<h5>
Raw Peers:
</h5>
<p>{ this.renderPeersList() }</p>
</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>
style={{ height: this.state.activeTab === 1 ? `auto` : '0' }}>
{ this.renderAddNode() }
</div>
</div>
}
@ -299,7 +54,7 @@ export const SettingsRender = function() {
<div
id="DumpWallet"
onClick={ () => this.openTab('DumpWallet', 2) }
className={ 'panel' + (this.state.nativeOnly ? ' hide' : '') }>
className={ 'hide panel' + (this.state.nativeOnly ? ' hide' : '') }>
<div className="panel-heading">
<a className={ 'panel-title' + (this.state.activeTab === 2 ? '' : ' collapsed') }>
<i className="icon wb-briefcase"></i>{ translate('INDEX.WALLET_BACKUP') }
@ -307,8 +62,8 @@ export const SettingsRender = function() {
</div>
<div
className={ 'panel-collapse collapse' + (this.state.activeTab === 2 ? ' in' : '') }
style={{ height: this.state.activeTab === 2 ? `${this.state.activeTabHeight}px` : '0' }}>
<div className="panel-body">Wallet Backup section to be updated soon.</div>
style={{ height: this.state.activeTab === 2 ? `auto` : '0' }}>
{ this.renderWalletBackup() }
</div>
</div>
}
@ -316,7 +71,7 @@ export const SettingsRender = function() {
<div
id="FiatCurrencySettings"
onClick={ () => this.openTab('FiatCurrencySettings', 3) }
className={ 'panel' + (this.state.nativeOnly ? ' hide' : '') }>
className={ 'hide panel' + (this.state.nativeOnly ? ' hide' : '') }>
<div className="panel-heading">
<a className={ 'panel-title' + (this.state.activeTab === 3 ? '' : ' collapsed') }>
<i className="icon fa-money"></i>{ translate('INDEX.FIAT_CURRENCY') }
@ -324,8 +79,8 @@ export const SettingsRender = function() {
</div>
<div
className={ 'panel-collapse collapse' + (this.state.activeTab === 3 ? ' in' : '') }
style={{ height: this.state.activeTab === 3 ? `${this.state.activeTabHeight}px` : '0' }}>
<div className="panel-body">Fiat currency settings section to be updated soon.</div>
style={{ height: this.state.activeTab === 3 ? `auto` : '0' }}>
{ this.renderFiatCurrency() }
</div>
</div>
}
@ -341,69 +96,8 @@ export const SettingsRender = function() {
</div>
<div
className={ 'panel-collapse collapse' + (this.state.activeTab === 4 ? ' in' : '') }
style={{ height: this.state.activeTab === 4 ? `${this.state.activeTabHeight}px` : '0' }}>
<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.Settings.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>
style={{ height: this.state.activeTab === 4 ? `auto` : '0' }}>
{ this.renderExportKeys() }
</div>
</div>
}
@ -419,41 +113,8 @@ export const SettingsRender = function() {
</div>
<div
className={ 'panel-collapse collapse' + (this.state.activeTab === 5 ? ' in' : '') }
style={{ height: this.state.activeTab === 5 ? `${this.state.activeTabHeight}px` : '0' }}>
<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>
style={{ height: this.state.activeTab === 5 ? `auto` : '0' }}>
{ this.renderImportKeys() }
</div>
</div>
}
@ -469,72 +130,8 @@ export const SettingsRender = function() {
</div>
<div
className={ 'panel-collapse collapse' + (this.state.activeTab === 6 ? ' in' : '') }
style={{ height: this.state.activeTab === 6 ? `${this.state.activeTabHeight}px` : '0' }}>
<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>
style={{ height: this.state.activeTab === 6 ? `auto` : '0' }}>
{ this.renderDebugLog() }
</div>
</div>
@ -549,32 +146,26 @@ export const SettingsRender = function() {
</div>
<div
className={ 'panel-collapse collapse' + (this.state.activeTab === 7 ? ' in' : '') }
style={{ height: this.state.activeTab === 7 ? `${this.state.activeTabHeight}px` : '0' }}>
<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>
style={{ height: this.state.activeTab === 7 ? `auto` : '0' }}>
{ this.renderAppSettings() }
</div>
</div>
<div
className="panel"
id="AppInfo"
onClick={ () => this.openTab('AppInfo', 8) }>
<div className="panel-heading">
<a className={ 'panel-title' + (this.state.activeTab === 8 ? '' : ' collapsed') }>
<i className="icon md-info"></i>{ translate('SETTINGS.APP_INFO') }
</a>
</div>
<div
className={ 'panel-collapse collapse' + (this.state.activeTab === 8 ? ' in' : '') }
style={{ height: this.state.activeTab === 8 ? `auto` : '0' }}>
{ this.renderAppInfoTab() }
</div>
</div>
{ this.renderAppInfoTab() }
{ this.props.Main && this.props.Main.coins.native &&
<div
@ -588,59 +179,27 @@ export const SettingsRender = function() {
</div>
<div
className={ 'panel-collapse collapse' + (this.state.activeTab === 9 ? ' in' : '') }
style={{ height: this.state.activeTab === 9 ? `${this.state.activeTabHeight}px` : '0' }}>
<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>
style={{ height: this.state.activeTab === 9 ? `auto` : '0' }}>
{ this.renderCliPanel() }
</div>
</div>
}
{ this.renderAppUpdateTab() }
<div
className="panel"
id="AppUpdate"
onClick={ () => this.openTab('AppUpdate', 10) }>
<div className="panel-heading">
<a className={ 'panel-title' + (this.state.activeTab === 10 ? '' : ' collapsed') }>
<i className="icon fa fa-cloud-download"></i> { translate('INDEX.UPDATE') }
</a>
</div>
<div
className={ 'panel-collapse collapse' + (this.state.activeTab === 10 ? ' in' : '') }
style={{ height: this.state.activeTab === 10 ? `auto` : '0' }}>
{ this.renderAppUpdateTab() }
</div>
</div>
<div
className="panel"
@ -653,55 +212,8 @@ export const SettingsRender = function() {
</div>
<div
className={ 'panel-collapse collapse' + (this.state.activeTab === 11 ? ' in' : '') }
style={{ height: this.state.activeTab === 11 ? `${this.state.activeTabHeight}px` : '0' }}>
<div className="panel-body">
<div className="col-sm-12 no-padding-left">
<div className="support-box-wrapper">
<div
className="support-box"
onClick={ () => this.openExternalWindow('http://support.supernet.org') }>
<img
src="assets/images/cryptologo/supernet.png"
alt="Support tickets" />
<div className="support-box-title">{ translate('SETTINGS.SUPPORT_TICKETS') }</div>
<div className="support-box-link">support.supernet.org</div>
</div>
</div>
<div className="support-box-wrapper">
<div
className="support-box"
onClick={ () => this.openExternalWindow('https://sprnt.slack.com') }>
<img
src="assets/images/support/slack-icon.png"
alt="Slack" />
<div className="support-box-title">Slack</div>
<div className="support-box-link">sprnt.slack.com</div>
</div>
</div>
<div className="support-box-wrapper">
<div
className="support-box"
onClick={ () => this.openExternalWindow('http://slackinvite.supernet.org') }>
<img
src="assets/images/support/slack-invite-icon.png"
alt="Slack invite" />
<div className="support-box-title">{ translate('SETTINGS.GET_SLACK_INVITE') }</div>
<div className="support-box-link">slackinvite.supernet.org</div>
</div>
</div>
<div className="support-box-wrapper">
<div
className="support-box"
onClick={ () => this.openExternalWindow('https://github.com/SuperNETorg/Agama') }>
<img
src="assets/images/support/github-icon.png"
alt="Github" />
<div className="support-box-title">Github</div>
<div className="support-box-link">github.com/SuperNETorg/Agama</div>
</div>
</div>
</div>
</div>
style={{ height: this.state.activeTab === 11 ? `auto` : '0' }}>
{ this.renderSupportPanel() }
</div>
</div>
</div>

82
react/src/components/dashboard/settings/settings.supportPanel.js

@ -0,0 +1,82 @@
import React from 'react';
import { translate } from '../../../translate/translate';
class SupportPanel extends React.Component {
constructor() {
super();
}
openExternalWindow(url) {
const remote = window.require('electron').remote;
const BrowserWindow = remote.BrowserWindow;
const externalWindow = new BrowserWindow({
width: 1280,
height: 800,
title: `${translate('INDEX.LOADING')}...`,
icon: remote.getCurrentWindow().iguanaIcon,
});
externalWindow.loadURL(url);
externalWindow.webContents.on('did-finish-load', function() {
setTimeout(function() {
externalWindow.show();
}, 40);
});
}
render() {
return (
<div className="panel-body">
<div className="col-sm-12 no-padding-left">
<div className="support-box-wrapper">
<div
className="support-box"
onClick={ () => this.openExternalWindow('http://support.supernet.org') }>
<img
src="assets/images/cryptologo/supernet.png"
alt="Support tickets" />
<div className="support-box-title">{ translate('SETTINGS.SUPPORT_TICKETS') }</div>
<div className="support-box-link">support.supernet.org</div>
</div>
</div>
<div className="support-box-wrapper">
<div
className="support-box"
onClick={ () => this.openExternalWindow('https://sprnt.slack.com') }>
<img
src="assets/images/support/slack-icon.png"
alt="Slack" />
<div className="support-box-title">Slack</div>
<div className="support-box-link">sprnt.slack.com</div>
</div>
</div>
<div className="support-box-wrapper">
<div
className="support-box"
onClick={ () => this.openExternalWindow('http://slackinvite.supernet.org') }>
<img
src="assets/images/support/slack-invite-icon.png"
alt="Slack invite" />
<div className="support-box-title">{ translate('SETTINGS.GET_SLACK_INVITE') }</div>
<div className="support-box-link">slackinvite.supernet.org</div>
</div>
</div>
<div className="support-box-wrapper">
<div
className="support-box"
onClick={ () => this.openExternalWindow('https://github.com/SuperNETorg/Agama') }>
<img
src="assets/images/support/github-icon.png"
alt="Github" />
<div className="support-box-title">Github</div>
<div className="support-box-link">github.com/SuperNETorg/Agama</div>
</div>
</div>
</div>
</div>
);
};
}
export default SupportPanel;

23
react/src/components/dashboard/settings/settings.walletBackupPanel.js

@ -0,0 +1,23 @@
import React from 'react';
import { translate } from '../../../translate/translate';
import { connect } from 'react-redux';
class WalletBackupPanel extends React.Component {
constructor() {
super();
}
render() {
return (
<div className="panel-body">Wallet Backup section to be updated soon.</div>
);
};
}
const mapStateToProps = (state) => {
return {
Settings: state.Settings,
};
};
export default connect(mapStateToProps)(WalletBackupPanel);

60
react/src/components/dashboard/settings/settings.walletInfoPanel.js

@ -0,0 +1,60 @@
import React from 'react';
import { translate } from '../../../translate/translate';
import { connect } from 'react-redux';
class WalletInfoPanel extends React.Component {
constructor() {
super();
}
render() {
return (
<div className="panel-body">
<table className="table">
<thead>
<tr>
<th width="10%">{ translate('INDEX.KEY') }</th>
<th>{ translate('INDEX.VALUE') }</th>
</tr>
</thead>
<tbody>
<tr>
<td className="wallet-info-key">pubkey</td>
<td>{ this.props.Main.activeHandle.pubkey }</td>
</tr>
<tr>
<td className="wallet-info-key">btcpubkey</td>
<td>{ this.props.Main.activeHandle.btcpubkey }</td>
</tr>
<tr>
<td className="wallet-info-key">rmd160</td>
<td>{ this.props.Main.activeHandle.rmd160 }</td>
</tr>
<tr>
<td className="wallet-info-key">NXT</td>
<td>{ this.props.Main.activeHandle.NXT }</td>
</tr>
<tr>
<td className="wallet-info-key">notary</td>
<td>{ this.props.Main.activeHandle.notary }</td>
</tr>
<tr>
<td className="wallet-info-key">status</td>
<td>{ this.props.Main.activeHandle.status }</td>
</tr>
</tbody>
</table>
</div>
);
};
}
const mapStateToProps = (state) => {
return {
Main: {
activeHandle: state.Main.activeHandle,
},
};
};
export default connect(mapStateToProps)(WalletInfoPanel);
Loading…
Cancel
Save