+ tooltips

+ sort transactions by date correctly
+ confirmed transactions indicator
This commit is contained in:
Taegus
2018-12-20 18:29:23 +01:00
parent f80c9123f1
commit 62f8be866e
15 changed files with 228 additions and 13 deletions

View File

@@ -0,0 +1,74 @@
/**
* This plug-in for DataTables represents the ultimate option in extensibility
* for sorting date / time strings correctly. It uses
* [Moment.js](http://momentjs.com) to create automatic type detection and
* sorting plug-ins for DataTables based on a given format. This way, DataTables
* will automatically detect your temporal information and sort it correctly.
*
* For usage instructions, please see the DataTables blog
* post that [introduces it](//datatables.net/blog/2014-12-18).
*
* @name Ultimate Date / Time sorting
* @summary Sort date and time in any format using Moment.js
* @author [Allan Jardine](//datatables.net)
* @depends DataTables 1.10+, Moment.js 1.7+
*
* @example
* $.fn.dataTable.moment( 'HH:mm MMM D, YY' );
* $.fn.dataTable.moment( 'dddd, MMMM Do, YYYY' );
*
* $('#example').DataTable();
*/
(function (factory) {
if (typeof define === "function" && define.amd) {
define(["jquery", "moment", "datatables.net"], factory);
} else {
factory(jQuery, moment);
}
}(function ($, moment) {
$.fn.dataTable.moment = function ( format, locale ) {
var types = $.fn.dataTable.ext.type;
// Add type detection
types.detect.unshift( function ( d ) {
if ( d ) {
// Strip HTML tags and newline characters if possible
if ( d.replace ) {
d = d.replace(/(<.*?>)|(\r?\n|\r)/g, '');
}
// Strip out surrounding white space
d = $.trim( d );
}
// Null and empty values are acceptable
if ( d === '' || d === null ) {
return 'moment-'+format;
}
return moment( d, format, locale, true ).isValid() ?
'moment-'+format :
null;
} );
// Add sorting method - use an integer for the sorting
types.order[ 'moment-'+format+'-pre' ] = function ( d ) {
if ( d ) {
// Strip HTML tags and newline characters if possible
if ( d.replace ) {
d = d.replace(/(<.*?>)|(\r?\n|\r)/g, '');
}
// Strip out surrounding white space
d = $.trim( d );
}
return !moment(d, format, locale, true).isValid() ?
Infinity :
parseInt( moment( d, format, locale, true ).format( 'x' ), 10 );
};
};
}));

View File

@@ -34,3 +34,11 @@
#tableTransactionsForAll .fa-arrows-alt-h { #tableTransactionsForAll .fa-arrows-alt-h {
color: #DCDCDC; color: #DCDCDC;
} }
#tableTransactionsForAll .fa-question {
color: #DCDCDC;
}
#tableTransactionsForAll .fa-check {
color: #228B22;
}

View File

@@ -8,6 +8,7 @@
<th>From</th> <th>From</th>
<th>To</th> <th>To</th>
<th>Value</th> <th>Value</th>
<th></th>
</tr> </tr>
</thead> </thead>
<tfoot> <tfoot>
@@ -18,6 +19,7 @@
<th>From</th> <th>From</th>
<th>To</th> <th>To</th>
<th>Value</th> <th>Value</th>
<th></th>
</tr> </tr>
</tfoot> </tfoot>
</table> </table>

View File

@@ -1,5 +1,7 @@
<div id="walletsToolbar"> <div id="walletsToolbar">
<button type="button" class="btn btn-etho" id="btnNewAddress">New Address</button> <button type="button" class="btn btn-etho" id="btnNewAddress"><i class="fas fa-plus"></i></button>
<button type="button" class="btn btn-etho" id="btnExportAccounts"><i class="fas fa-file-export"></i></button>
<button type="button" class="btn btn-etho" id="btnImportAccounts"><i class="fas fa-file-import"></i></button>
<div id="sumBalance">Total ETHO: <span class="sumBalance">{{sumBalance}}</span></div> <div id="sumBalance">Total ETHO: <span class="sumBalance">{{sumBalance}}</span></div>
</div> </div>
<div id="addressList" class="{{#if addressData.length}}walletsWrapper{{else}}noWalletsWrapper{{/if}}"> <div id="addressList" class="{{#if addressData.length}}walletsWrapper{{else}}noWalletsWrapper{{/if}}">

View File

@@ -30,6 +30,7 @@
<script src="./assets/scripts/sprintf.min.js"></script> <script src="./assets/scripts/sprintf.min.js"></script>
<script src="./assets/scripts/please-wait.js "></script> <script src="./assets/scripts/please-wait.js "></script>
<script src="./assets/scripts/moment.min.js"></script> <script src="./assets/scripts/moment.min.js"></script>
<script src="./assets/scripts/datetime-moment.js"></script>
<script src="./assets/scripts/jquery.floatThead.min.js"></script> <script src="./assets/scripts/jquery.floatThead.min.js"></script>
<script src="./assets/scripts/all.min.js"></script> <script src="./assets/scripts/all.min.js"></script>

View File

@@ -64,4 +64,5 @@ ipcMain.on('getTemplateContent', (event, arg) => {
}); });
require('./modules/geth.js'); require('./modules/geth.js');
require('./modules/accounts.js');
require('./modules/database.js'); require('./modules/database.js');

54
modules/accounts.js Normal file
View File

@@ -0,0 +1,54 @@
const {app, dialog, ipcMain} = require('electron');
const admZip = require('adm-zip');
const path = require('path');
const fs = require('fs');
class Accounts {
constructor() {
}
exportAccounts() {
var savePath = dialog.showSaveDialog({
defaultPath: path.join(app.getPath('documents'), 'accounts.zip')
});
if (savePath) {
const accPath = path.join(path.join(process.env.APPDATA, 'Ether1'), 'keystore');
fs.readdir(accPath, function(err, files) {
var zip = new admZip();
for(let filePath of files) {
zip.addFile(filePath, fs.readFileSync(path.join(accPath, filePath)));
}
// store zip to path
zip.writeZip(savePath);
});
}
}
importAccounts() {
const accPath = path.join(path.join(process.env.APPDATA, 'Ether1'), 'keystore');
var openPath = dialog.showOpenDialog({
defaultPath: app.getPath('documents')
});
if (openPath) {
var zip = new admZip(openPath[0]);
zip.extractAllTo(accPath, true);
}
}
}
ipcMain.on('exportAccounts', (event, arg) => {
EthoAccounts.exportAccounts();
});
ipcMain.on('importAccounts', (event, arg) => {
EthoAccounts.importAccounts();
event.returnValue = true;
});
EthoAccounts = new Accounts();

View File

@@ -29,7 +29,8 @@ ipcMain.on('getTransactions', (event, arg) => {
docs[i].timestamp, docs[i].timestamp,
docs[i].fromaddr, docs[i].fromaddr,
docs[i].toaddr, docs[i].toaddr,
docs[i].value docs[i].value,
docs[i].confirmed || 1
]); ]);
} }

View File

@@ -1,5 +1,5 @@
const {app, dialog, BrowserWindow} = require('electron')
const child_process = require('child_process'); const child_process = require('child_process');
const {app, dialog} = require('electron');
const path = require('path'); const path = require('path');
class Geth { class Geth {
@@ -14,6 +14,7 @@ class Geth {
this.gethProcess = child_process.spawn(gethPath, ['--ws', '--wsorigins', '*', '--wsaddr', '127.0.0.1', '--wsport', '8546', '--wsapi', 'admin,db,eth,net,miner,personal,web3']); this.gethProcess = child_process.spawn(gethPath, ['--ws', '--wsorigins', '*', '--wsaddr', '127.0.0.1', '--wsport', '8546', '--wsapi', 'admin,db,eth,net,miner,personal,web3']);
this.gethProcess.on('error', function(err) { this.gethProcess.on('error', function(err) {
dialog.showErrorBox("Error starting application", "Geth failed to start!"); dialog.showErrorBox("Error starting application", "Geth failed to start!");
app.quit();
}); });
this.gethProcess.stderr.on('data', function(data) { this.gethProcess.stderr.on('data', function(data) {
console.log(data.toString()); console.log(data.toString());

View File

@@ -25,9 +25,11 @@
"author": "Ether1", "author": "Ether1",
"license": "CC0-1.0", "license": "CC0-1.0",
"dependencies": { "dependencies": {
"adm-zip": "^0.4.13",
"electron-storage": "^1.0.7", "electron-storage": "^1.0.7",
"handlebars": "^4.0.12", "handlebars": "^4.0.12",
"nedb": "^1.8.0" "nedb": "^1.8.0",
"typescript": "^3.2.2"
}, },
"devDependencies": { "devDependencies": {
"electron": "^3.0.12", "electron": "^3.0.12",

View File

@@ -28,6 +28,16 @@ class Blockchain {
return web3Local.utils.isAddress(address); return web3Local.utils.isAddress(address);
} }
getTransaction(thxid, clbError, clbSuccess) {
web3Local.eth.getTransaction(thxid, function( error, result ) {
if (error) {
clbError(error);
} else {
clbSuccess(result);
}
});
}
getTranasctionFee(fromAddress, toAddress, value, clbError, clbSuccess) { getTranasctionFee(fromAddress, toAddress, value, clbError, clbSuccess) {
web3Local.eth.getTransactionCount(fromAddress, function( error, result ) { web3Local.eth.getTransactionCount(fromAddress, function( error, result ) {
if (error) { if (error) {
@@ -65,7 +75,7 @@ class Blockchain {
clbError("Wrong password for the selected address!"); clbError("Wrong password for the selected address!");
} else } else
{ {
web3Local.eth.getTransactionCount(fromAddress, function( error, result ) { web3Local.eth.getTransactionCount(fromAddress, 'pending', function( error, result ) {
if (error) { if (error) {
clbError(error); clbError(error);
} else { } else {

View File

@@ -139,6 +139,24 @@ $(document).on("render_send", function() {
position: 'topRight', position: 'topRight',
timeout: 5000 timeout: 5000
}); });
EthoBlockchain.getTransaction(data,
function(error) {
EthoMainGUI.showGeneralError(error);
},
function(transaction) {
/*
ipcRenderer.send('storeTransaction', {
block: element.block,
fromaddr: element.fromaddr,
timestamp: element.timestamp,
toaddr: element.toaddr,
value: element.value,
confirmed: "1",
});
*/
}
);
} }
); );
} }

View File

@@ -1,4 +1,3 @@
// In renderer process (web page).
const {ipcRenderer} = require('electron'); const {ipcRenderer} = require('electron');
class Transactions { class Transactions {
@@ -23,14 +22,15 @@ class Transactions {
$.getJSON("https://richlist.ether1.org/transactions_list.php" + params, function( result ) { $.getJSON("https://richlist.ether1.org/transactions_list.php" + params, function( result ) {
result.data.forEach(element => { result.data.forEach(element => {
var Transaction = { ipcRenderer.send('storeTransaction', {
block: element.block, block: element.block,
txhash: element.hash,
fromaddr: element.fromaddr, fromaddr: element.fromaddr,
timestamp: element.timestamp, timestamp: element.timestamp,
toaddr: element.toaddr, toaddr: element.toaddr,
value: element.value value: element.value,
} confirmed: "1"
ipcRenderer.send('storeTransaction', Transaction); });
}); });
// call the transaction sync for the next address // call the transaction sync for the next address
@@ -93,6 +93,9 @@ class Transactions {
} }
}); });
// register the sort datetime format
$.fn.dataTable.moment('MMM Do YYYY');
// render the transactions // render the transactions
$('#tableTransactionsForAll').DataTable({ $('#tableTransactionsForAll').DataTable({
"paging": false, "paging": false,
@@ -129,6 +132,16 @@ class Transactions {
"render": function ( data, type, row ) { "render": function ( data, type, row ) {
return parseFloat(web3Local.utils.fromWei(EthoUtils.toFixed(parseFloat(data)).toString(), 'ether')).toFixed(2); return parseFloat(web3Local.utils.fromWei(EthoUtils.toFixed(parseFloat(data)).toString(), 'ether')).toFixed(2);
} }
},
{
"targets": 6,
"render": function ( data, type, row ) {
if (data == 0) {
return '<i class="fas fa-question"></i>';
} else if (data == 1) {
return '<i class="fas fa-check"></i>';
}
}
} }
], ],
"drawCallback": function( settings ) { "drawCallback": function( settings ) {
@@ -172,7 +185,8 @@ $(document).on("onSyncInterval", function() {
fromaddr: element.from.toLowerCase(), fromaddr: element.from.toLowerCase(),
timestamp: moment().format('YYYY-MM-DD HH:mm:ss'), timestamp: moment().format('YYYY-MM-DD HH:mm:ss'),
toaddr: element.to.toLowerCase(), toaddr: element.to.toLowerCase(),
value: Number(element.value).toExponential(5).toString().replace('+','') value: Number(element.value).toExponential(5).toString().replace('+',''),
confirmed: "0",
} }
// store transaction and notify about new transactions // store transaction and notify about new transactions

View File

@@ -39,6 +39,19 @@ class Utils {
} }
} }
} }
createToolTip(element, text) {
tippy(element, {
content: text,
delay: 500,
arrow: true,
arrowType: 'round',
size: 'large',
duration: 500,
animation: 'scale'
});
}
} }
EthoUtils = new Utils(); EthoUtils = new Utils();

View File

@@ -1,4 +1,3 @@
// In renderer process (web page).
const {ipcRenderer} = require('electron'); const {ipcRenderer} = require('electron');
class Wallets { class Wallets {
@@ -22,6 +21,12 @@ class Wallets {
this.addressList.push(address.toLowerCase()); this.addressList.push(address.toLowerCase());
} }
enableButtonTooltips() {
EthoUtils.createToolTip("#btnNewAddress", "Create New Address");
EthoUtils.createToolTip("#btnExportAccounts", "Export Accounts");
EthoUtils.createToolTip("#btnImportAccounts", "Import Accounts");
}
validateNewAccountForm() { validateNewAccountForm() {
if (EthoMainGUI.getAppState() == "account") { if (EthoMainGUI.getAppState() == "account") {
if (!$("#walletPasswordFirst").val()) { if (!$("#walletPasswordFirst").val()) {
@@ -61,6 +66,7 @@ renderWalletsState() {
// render the wallets current state // render the wallets current state
EthoMainGUI.renderTemplate("wallets.html", data); EthoMainGUI.renderTemplate("wallets.html", data);
$(document).trigger("render_wallets"); $(document).trigger("render_wallets");
EthoWallets.enableButtonTooltips();
} }
); );
} }
@@ -148,6 +154,14 @@ $(document).on("render_wallets", function() {
}); });
}); });
$("#btnExportAccounts").off('click').on('click', function() {
ipcRenderer.send('exportAccounts', {});
});
$("#btnImportAccounts").off('click').on('click', function() {
ipcRenderer.sendSync('importAccounts', {});
});
$(".textAddress").off('click').on('click', function() { $(".textAddress").off('click').on('click', function() {
EthoMainGUI.copyToClipboard($(this).html()); EthoMainGUI.copyToClipboard($(this).html());