From d33f90b73875284f5b496d5a9fa7395c7e8c4f57 Mon Sep 17 00:00:00 2001 From: Taegus Date: Fri, 28 Dec 2018 08:56:20 +0100 Subject: [PATCH] * moved JSON read writes to database.js + show commutative price and price per etho + use Roboto as the default font + use subscribe for new blocks to sync transactions better --- assets/styles/style.css | 30 +++-- assets/styles/transactions.css | 4 +- assets/templates/addressBook.html | 2 +- assets/templates/wallets.html | 5 +- index.html | 15 ++- renderer/addressBook.js | 71 ++++-------- renderer/blockchain.js | 57 +++++++++- renderer/database.js | 63 +++++++++++ renderer/maingui.js | 18 +++ renderer/send.js | 15 ++- renderer/settings.js | 10 +- renderer/syncing.js | 10 +- renderer/transactions.js | 175 +++++++++++++++--------------- renderer/wallets.js | 39 ++++--- 14 files changed, 343 insertions(+), 171 deletions(-) create mode 100644 renderer/database.js diff --git a/assets/styles/style.css b/assets/styles/style.css index d72408a..ce2a70d 100644 --- a/assets/styles/style.css +++ b/assets/styles/style.css @@ -1,5 +1,7 @@ html { color: #aaa; + font-family: 'Roboto', sans-serif; + -webkit-font-smoothing: antialiased; } /* hide / show the body when the loader is finished */ @@ -98,19 +100,22 @@ body.pg-loaded > .inner { #sumBalance { float: right; - font-weight: bold; line-height: 38px; margin-right: 10px; } .modalBody { margin: 20px; + color: #333; } #dlgGeneralError .modalBody { - height: 100px; + height: 100px; } +#dlgGeneralConfirm .modalBody { + height: 100px; +} #dlgDeleteAddressConfirm .modalBody { height: 100px; } @@ -135,6 +140,10 @@ body.pg-loaded > .inner { background-color: #333; } +#dlgAddressList .modalBody { + color: #aaa; +} + .btn-dialog-confirm { position: absolute; bottom: 10px; @@ -240,6 +249,12 @@ div.sidebar svg { padding-left: 20px; } +#addressList th { + color: #ccc; + font-size: 1.1em; + font-weight: normal; +} + .sendTXInfo { height: 30px; } @@ -307,12 +322,11 @@ div.sidebar svg { background-color: #aaa; } -.sumBalance { - color: white; - background-color: #7A1336; - border: 2px solid #991643; - border-radius: 10px; - padding: 5px; +.sumDollars, +.sumBalance, +.sumCurrency { + color: #ccc; + margin-right: 5px; } .cleanText { diff --git a/assets/styles/transactions.css b/assets/styles/transactions.css index 6af2955..05cefad 100644 --- a/assets/styles/transactions.css +++ b/assets/styles/transactions.css @@ -66,7 +66,9 @@ table.dataTable { */ } table.dataTable thead th, table.dataTable tfoot th { - font-weight: bold; } + color: #ccc; + font-size: 1.1em; + font-weight: normal; } table.dataTable thead th, table.dataTable thead td { padding: 10px 18px; diff --git a/assets/templates/addressBook.html b/assets/templates/addressBook.html index 9aecc91..50157b8 100644 --- a/assets/templates/addressBook.html +++ b/assets/templates/addressBook.html @@ -18,7 +18,7 @@ {{name}} {{address}} - + {{/addressData}} diff --git a/assets/templates/wallets.html b/assets/templates/wallets.html index ee576be..fde703c 100644 --- a/assets/templates/wallets.html +++ b/assets/templates/wallets.html @@ -2,7 +2,10 @@ -
Total ETHO: {{sumBalance}}
+
+ {{sumBalance}} + ETHO + / 0.00 $ / 0.00 $ per ETHO
{{#if addressData.length}} diff --git a/index.html b/index.html index 0231138..e94f26a 100644 --- a/index.html +++ b/index.html @@ -3,6 +3,7 @@ Ether-1 Desktop Wallet + @@ -86,6 +87,7 @@ require('./renderer/syncing.js'); require('./renderer/settings.js'); require('./renderer/wallets.js'); + require('./renderer/database.js'); require('./renderer/blockchain.js'); require('./renderer/addressBook.js'); require('./renderer/transactions.js'); @@ -102,5 +104,16 @@
- + + +
+
+
+ +
+ + +
+
+ diff --git a/renderer/addressBook.js b/renderer/addressBook.js index 52b057a..265286a 100644 --- a/renderer/addressBook.js +++ b/renderer/addressBook.js @@ -5,58 +5,28 @@ class AddressBook { } setAddressName(address, name) { - console.log(address); - console.log(name); - var addressBook = ipcRenderer.sendSync('getJSONFile', 'addresses.json'); - - if (!addressBook) { - addressBook = { names: {} }; - } + var addressBook = EthoDatatabse.getAddresses(); // set the wallet name from the dialog box addressBook.names[address] = name; - ipcRenderer.sendSync('setJSONFile', - { - file: 'addresses.json', - data: addressBook - }); + EthoDatatabse.setAddresses(addressBook); } getAddressName(address) { - var addressBook = ipcRenderer.sendSync('getJSONFile', 'addresses.json'); - - if (!addressBook) { - addressBook = { names: {} }; - } - + var addressBook = EthoDatatabse.getAddresses(); // set the wallet name from the dialog box return addressBook.names[address] || ""; } getAddressList() { - var addressBook = ipcRenderer.sendSync('getJSONFile', 'addresses.json'); - - if (!addressBook) { - addressBook = { names: {} }; - } - + var addressBook = EthoDatatabse.getAddresses(); return addressBook.names; } deleteAddress(address) { - var addressBook = ipcRenderer.sendSync('getJSONFile', 'addresses.json'); - - if (!addressBook) { - addressBook = { names: {} }; - } else { - delete addressBook.names[address]; - } - - ipcRenderer.sendSync('setJSONFile', - { - file: 'addresses.json', - data: addressBook - }); + var addressBook = EthoDatatabse.getAddresses(); + delete addressBook.names[address]; + EthoDatatabse.setAddresses(addressBook); } enableButtonTooltips() { @@ -93,15 +63,20 @@ $(document).on("render_addressBook", function() { function doCreateNewWallet() { $('#dlgCreateAddressAndName').iziModal('close'); - EthoAddressBook.setAddressName($("#addressHash").val(), $("#addressName").val()); - EthoAddressBook.renderAddressBook(); - - iziToast.success({ - title: 'Created', - message: 'New address was successfully created', - position: 'topRight', - timeout: 5000 - }); + if (!EthoBlockchain.isAddress($("#addressHash").val())) { + EthoMainGUI.showGeneralError("Address must be a valid address!"); + } else { + EthoAddressBook.setAddressName($("#addressHash").val(), $("#addressName").val()); + EthoAddressBook.renderAddressBook(); + + iziToast.success({ + title: 'Created', + message: 'New address was successfully created', + position: 'topRight', + timeout: 5000 + }); + + } } $("#btnCreateAddressConfirm").off('click').on('click', function() { @@ -141,6 +116,8 @@ $(document).on("render_addressBook", function() { }); $(".btnDeleteAddress").off('click').on('click', function() { + var deleteAddress = $(this).attr('data-address'); + $("#dlgDeleteAddressConfirm").iziModal(); $('#dlgDeleteAddressConfirm').iziModal('open'); @@ -149,8 +126,8 @@ $(document).on("render_addressBook", function() { }); $("#btnDeleteAddressConfirm").off('click').on('click', function() { - EthoAddressBook.deleteAddress($(this).attr('data-address')); $('#dlgDeleteAddressConfirm').iziModal('close'); + EthoAddressBook.deleteAddress(deleteAddress); EthoAddressBook.renderAddressBook(); }); }); diff --git a/renderer/blockchain.js b/renderer/blockchain.js index b88bda6..4f27211 100644 --- a/renderer/blockchain.js +++ b/renderer/blockchain.js @@ -2,7 +2,10 @@ const {ipcRenderer} = require('electron'); class Blockchain { - constructor() {} + constructor() { + this.txSubscribe = null; + this.bhSubscribe = null; + } getBlock(blockToGet, includeData, clbError, clbSuccess) { web3Local.eth.getBlock(blockToGet, includeData, function(error, block) { @@ -129,7 +132,7 @@ class Blockchain { rendererData.sumBalance = 0; rendererData.addressData = []; - var wallets = ipcRenderer.sendSync('getJSONFile', 'wallets.json'); + var wallets = EthoDatatabse.getWallets(); var counter = 0; web3Local.eth.getAccounts(function(err, res) { @@ -178,7 +181,7 @@ class Blockchain { var rendererData = {}; rendererData.addressData = []; - var wallets = ipcRenderer.sendSync('getJSONFile', 'wallets.json'); + var wallets = EthoDatatabse.getWallets(); var counter = 0; web3Local.eth.getAccounts(function(err, res) { @@ -212,6 +215,54 @@ class Blockchain { }); } + subsribePendingTransactions(clbError, clbSuccess, clbData) { + this.txSubscribe = web3Local.eth.subscribe('pendingTransactions', function(error, result){ + if (error) { + clbError(error); + } else { + clbSuccess(result); + } + }).on("data", function(transaction){ + if (clbData) { + clbData(transaction); + } + }); + } + + unsubsribePendingTransactions(clbError, clbSuccess) { + this.txSubscribe.unsubscribe(function(error, success){ + if(error) { + clbError(error); + } else { + clbSuccess(success) ; + } + }); + } + + subsribeNewBlockHeaders(clbError, clbSuccess, clbData) { + this.bhSubscribe = web3Local.eth.subscribe('newBlockHeaders', function(error, result){ + if (error) { + clbError(error); + } else { + clbSuccess(result); + } + }).on("data", function(blockHeader){ + if (clbData) { + clbData(blockHeader); + } + }); + } + + unsubsribeNewBlockHeaders(clbError, clbSuccess) { + this.bhSubscribe.unsubscribe(function(error, success){ + if(error) { + clbError(error); + } else { + clbSuccess(success) ; + } + }); + } + closeConnection() { web3Local.currentProvider.connection.close(); } diff --git a/renderer/database.js b/renderer/database.js new file mode 100644 index 0000000..f952e50 --- /dev/null +++ b/renderer/database.js @@ -0,0 +1,63 @@ +// In renderer process (web page). +const {ipcRenderer} = require('electron'); + +class Datatabse { + constructor() {} + + getCounters() { + var counters = ipcRenderer.sendSync('getJSONFile', 'counters.json'); + + if (counters == null) { + counters = {}; + } + + return counters; + } + + setCounters(counters) { + ipcRenderer.sendSync('setJSONFile', + { + file: 'counters.json', + data: counters + }); + } + + getWallets() { + var wallets = ipcRenderer.sendSync('getJSONFile', 'wallets.json'); + + if (!wallets) { + wallets = { names: {} }; + } + + return wallets; + } + + setWallets(wallets) { + ipcRenderer.sendSync('setJSONFile', + { + file: 'wallets.json', + data: wallets + }); + } + + getAddresses() { + var addressBook = ipcRenderer.sendSync('getJSONFile', 'addresses.json'); + + if (!addressBook) { + addressBook = { names: {} }; + } + + return addressBook; + } + + setAddresses(addresses) { + ipcRenderer.sendSync('setJSONFile', + { + file: 'addresses.json', + data: addresses + }); + } +} + +// create new account variable +EthoDatatabse = new Datatabse(); \ No newline at end of file diff --git a/renderer/maingui.js b/renderer/maingui.js index 1ce0efa..765d9b5 100644 --- a/renderer/maingui.js +++ b/renderer/maingui.js @@ -46,6 +46,24 @@ class MainGUI { }); } + showGeneralConfirmation(confirmText, callback) { + $("#txtGeneralConfirm").html(confirmText); + + // create and open the dialog + $("#dlgGeneralConfirm").iziModal(); + $('#dlgGeneralConfirm').iziModal('open'); + + $("#btnGeneralConfirmYes").click(function() { + $('#dlgGeneralConfirm').iziModal('close'); + callback(true); + }); + + $("#btnGeneralConfirmNo").click(function() { + $('#dlgGeneralConfirm').iziModal('close'); + callback(false); + }); + } + renderTemplate(template, data, container) { var template = Handlebars.compile(ipcRenderer.sendSync('getTemplateContent', template)); diff --git a/renderer/send.js b/renderer/send.js index 5b700c0..324ae82 100644 --- a/renderer/send.js +++ b/renderer/send.js @@ -77,9 +77,20 @@ $(document).on("render_send", function() { function(error) { EthoMainGUI.showGeneralError(error); }, - function(data) { + function(addressList) { + var addressBook = EthoAddressBook.getAddressList(); + + for (var key in addressBook) { + if (addressBook.hasOwnProperty(key)) { + var adddressObject = {}; + adddressObject.address = key + adddressObject.name = addressBook[key]; + addressList.addressData.push(adddressObject); + } + } + $("#dlgAddressList").iziModal({ width: "800px" }); - EthoMainGUI.renderTemplate("addressList.html", data, $("#dlgAddressListBody")); + EthoMainGUI.renderTemplate("addressList.html", addressList, $("#dlgAddressListBody")); $('#dlgAddressList').iziModal('open'); $(".btnSelectToAddress").off('click').on('click', function() { diff --git a/renderer/settings.js b/renderer/settings.js index 5f198eb..e94c2ff 100644 --- a/renderer/settings.js +++ b/renderer/settings.js @@ -12,7 +12,15 @@ class Settings { $(document).on("render_settings", function() { $("#btnSettingsCleanTransactions").off('click').on('click', function() { - EthoMainGUI.showGeneralError("Not implemented yet!"); + EthoMainGUI.showGeneralConfirmation("Do you really want to resync transactions?", function(result) { + if (result) { + if (EthoTransactions.getIsSyncing()) { + EthoMainGUI.showGeneralError("Transactions sync is currently in progress"); + } else { + EthoTransactions.disableKeepInSync(); + } + } + }); }); $("#btnSettingsCleanWallets").off('click').on('click', function() { diff --git a/renderer/syncing.js b/renderer/syncing.js index ee83e67..252f062 100644 --- a/renderer/syncing.js +++ b/renderer/syncing.js @@ -63,9 +63,13 @@ function StartSyncProcess() { $(document).trigger("onNewAccountTransaction"); alreadyCatchedUp = true; + // enable the keep in sync feature + EthoTransactions.enableKeepInSync(); // sync all the transactions to the current block EthoTransactions.syncTransactionsForAllAddresses(localBlock.number); - $(document).trigger("onSyncInterval"); + + // signal that the sync is complete + $(document).trigger("onSyncComplete"); } } }); @@ -75,7 +79,7 @@ function StartSyncProcess() { }).on("data", function(sync){ if ((sync) && (sync.HighestBlock > 0)) { SyncProgress.animate(sync.CurrentBlock / sync.HighestBlock); - SyncProgress.setText(vsprintf('%d/%d (%d%%)', [sync.CurrentBlock, sync.HighestBlock, Math.round(sync.CurrentBlock / sync.HighestBlock * 100)])); + SyncProgress.setText(vsprintf('%d/%d (%d%%)', [sync.CurrentBlock, sync.HighestBlock, Math.floor(sync.CurrentBlock / sync.HighestBlock * 100)])); } }).on("changed", function(isSyncing){ if(isSyncing) { @@ -84,7 +88,7 @@ function StartSyncProcess() { web3Local.eth.isSyncing(function(error, sync){ if ((!error) && (sync)) { SyncProgress.animate(sync.currentBlock / sync.highestBlock); - SyncProgress.setText(vsprintf('%d/%d (%d%%)', [sync.currentBlock, sync.highestBlock, Math.round(sync.currentBlock / sync.highestBlock * 100)])); + SyncProgress.setText(vsprintf('%d/%d (%d%%)', [sync.currentBlock, sync.highestBlock, Math.floor(sync.currentBlock / sync.highestBlock * 100)])); } }); }, 2000); diff --git a/renderer/transactions.js b/renderer/transactions.js index 1e10884..d5b4721 100644 --- a/renderer/transactions.js +++ b/renderer/transactions.js @@ -51,11 +51,7 @@ class Transactions { } else { // update the counter and store it back to file system counters.transactions = lastBlock; - ipcRenderer.sendSync('setJSONFile', - { - file: 'counters.json', - data: counters - }); + EthoDatatabse.setCounters(counters); SyncProgress.setText("Syncing transactions is complete."); EthoTransactions.setIsSyncing(false); @@ -63,13 +59,9 @@ class Transactions { } syncTransactionsForAllAddresses(lastBlock) { - var counters = ipcRenderer.sendSync('getJSONFile', 'counters.json'); + var counters = EthoDatatabse.getCounters(); var counter = 0; - if (counters == null) { - counters = {}; - } - EthoBlockchain.getAccounts( function(error) { EthoMainGUI.showGeneralError(error); @@ -109,99 +101,102 @@ class Transactions { }, 200); } -} + enableKeepInSync() { + function processTransaction(data) { -// event that tells us that geth is ready and up -$(document).on("onSyncInterval", function() { - var counters = ipcRenderer.sendSync('getJSONFile', 'counters.json'); + if ((EthoWallets.getAddressExists(data.from)) || (EthoWallets.getAddressExists(data.to))) { + if (data.blockNumber) { + console.log(data.blockNumber); + } - if (counters == null) { - counters = {}; - } + var Transaction = { + block: null, + txhash: data.hash.toLowerCase(), + fromaddr: data.from.toLowerCase(), + timestamp: moment.unix(data.timestamp).format('YYYY-MM-DD HH:mm:ss'), + toaddr: data.to.toLowerCase(), + value: Number(data.value).toExponential(5).toString().replace('+','') + } + + // store transaction and notify about new transactions + ipcRenderer.send('storeTransaction', Transaction); + $(document).trigger("onNewAccountTransaction"); - function finalizeCounters(lastBlock) { - counters.transactions = lastBlock; + iziToast.info({ + title: 'New Transaction', + message: vsprintf('Transaction from address %s to address %s was just processed', [Transaction.fromaddr, Transaction.toaddr]), + position: 'topRight', + timeout: 10000 + }); + + // render transactions again to show latest + if (EthoMainGUI.getAppState() == "transactions") { + setTimeout(function() { + EthoTransactions.renderTransactions(); + }, 500); + } + } + } - ipcRenderer.sendSync('setJSONFile', - { - file: 'counters.json', - data: counters - }); - } - - function doSyncRemainingBlocks() { - EthoBlockchain.getBlock("latest", false, + EthoBlockchain.subsribeNewBlockHeaders( function(error) { EthoMainGUI.showGeneralError(error); }, - function(block) { - var lastBlock = counters.transactions || 1; - - if (lastBlock < block.number) { - function getNextBlockTransactions(blockNumber, maxBlock) { - if (blockNumber < maxBlock) { - EthoBlockchain.getBlock(blockNumber, true, - function(error) { - EthoMainGUI.showGeneralError(error); - getNextBlockTransactions(blockNumber + 1 , maxBlock); - }, - function(data) { - if (data.transactions) { - data.transactions.forEach(element => { - if ((EthoWallets.getAddressExists(element.from)) || (EthoWallets.getAddressExists(element.to))) { - var Transaction = { - block: element.blockNumber.toString(), - txhash: element.hash.toLowerCase(), - fromaddr: element.from.toLowerCase(), - timestamp: moment.unix(data.timestamp).format('YYYY-MM-DD HH:mm:ss'), - toaddr: element.to.toLowerCase(), - value: Number(element.value).toExponential(5).toString().replace('+','') - } - - // store transaction and notify about new transactions - ipcRenderer.send('storeTransaction', Transaction); - $(document).trigger("onNewAccountTransaction"); - - iziToast.info({ - title: 'New Transaction', - message: vsprintf('Transaction from address %s to address %s was just processed', [Transaction.fromaddr, Transaction.toaddr]), - position: 'topRight', - timeout: 10000 - }); - } - }); + function(data) + { + EthoBlockchain.getBlock(data.number, true, + function(error) { + EthoMainGUI.showGeneralError(error); + }, + function(data) { + if (data.transactions) { + data.transactions.forEach(element => { + if ((EthoWallets.getAddressExists(element.from)) || (EthoWallets.getAddressExists(element.to))) { + var Transaction = { + block: element.blockNumber.toString(), + txhash: element.hash.toLowerCase(), + fromaddr: element.from.toLowerCase(), + timestamp: moment.unix(data.timestamp).format('YYYY-MM-DD HH:mm:ss'), + toaddr: element.to.toLowerCase(), + value: Number(element.value).toExponential(5).toString().replace('+','') } + + // store transaction and notify about new transactions + ipcRenderer.send('storeTransaction', Transaction); + $(document).trigger("onNewAccountTransaction"); - // call the next iteration for the next block - getNextBlockTransactions(blockNumber + 1 , maxBlock); + iziToast.info({ + title: 'New Transaction', + message: vsprintf('Transaction from address %s to address %s was just processed', [Transaction.fromaddr, Transaction.toaddr]), + position: 'topRight', + timeout: 10000 + }); + + if (EthoMainGUI.getAppState() == "transactions") { + setTimeout(function() { + EthoTransactions.renderTransactions(); + }, 500); + } } - ); - } else { - finalizeCounters(blockNumber); - - setTimeout(function() { - doSyncRemainingBlocks(); - }, 10000); - } + }); + } } + ); + } + ); + } - // call initial call of function - getNextBlockTransactions(lastBlock, block.number); - } else { - finalizeCounters(lastBlock); - - setTimeout(function() { - doSyncRemainingBlocks(); - }, 10000); - } - } - ); + disableKeepInSync() { + EthoBlockchain.unsubsribeNewBlockHeaders( + function(error) { + EthoMainGUI.showGeneralError(error); + }, + function(data) { + // success + } + ); } - - // do the initial sync - doSyncRemainingBlocks(); -}); - +} // create new transactions variable EthoTransactions = new Transactions(); \ No newline at end of file diff --git a/renderer/wallets.js b/renderer/wallets.js index 68773d7..c50da66 100644 --- a/renderer/wallets.js +++ b/renderer/wallets.js @@ -3,8 +3,21 @@ const {ipcRenderer} = require('electron'); class Wallets { constructor() { this.addressList = []; + + $.getJSON("https://min-api.cryptocompare.com/data/price?fsym=ETHO&tsyms=USD", function( price ) + { + EthoWallets._setPrice(price.USD); + }); } - + + _getPrice() { + return this.price; + } + + _setPrice(price) { + this.price = price; + } + getAddressList() { return this.addressList; } @@ -14,11 +27,17 @@ class Wallets { } getAddressExists(address) { - return this.addressList.indexOf(address.toLowerCase()) > -1; + if (address) { + return this.addressList.indexOf(address.toLowerCase()) > -1; + } else { + return false; + } } addAddressToList(address) { - this.addressList.push(address.toLowerCase()); + if (address) { + this.addressList.push(address.toLowerCase()); + } } enableButtonTooltips() { @@ -67,6 +86,8 @@ renderWalletsState() { EthoMainGUI.renderTemplate("wallets.html", data); $(document).trigger("render_wallets"); EthoWallets.enableButtonTooltips(); + + $("#labelSumDollars").html(vsprintf("/ %.2f $ / %.4f $ per ETHO", [data.sumBalance * EthoWallets._getPrice(), EthoWallets._getPrice()])); } ); } @@ -131,19 +152,11 @@ $(document).on("render_wallets", function() { $('#dlgChangeWalletName').iziModal('open'); function doChangeWalletName() { - var wallets = ipcRenderer.sendSync('getJSONFile', 'wallets.json'); - - if (!wallets) { - wallets = { names: {} }; - } + var wallets = EthoDatatabse.getWallets(); // set the wallet name from the dialog box wallets.names[walletAddress] = $("#inputWalletName").val(); - ipcRenderer.sendSync('setJSONFile', - { - file: 'wallets.json', - data: wallets - }); + EthoDatatabse.setWallets(wallets); $('#dlgChangeWalletName').iziModal('close'); EthoWallets.renderWalletsState();