+ tooltips
+ sort transactions by date correctly + confirmed transactions indicator
This commit is contained in:
74
assets/scripts/datetime-moment.js
Normal file
74
assets/scripts/datetime-moment.js
Normal 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 );
|
||||
};
|
||||
};
|
||||
|
||||
}));
|
||||
@@ -33,4 +33,12 @@
|
||||
|
||||
#tableTransactionsForAll .fa-arrows-alt-h {
|
||||
color: #DCDCDC;
|
||||
}
|
||||
|
||||
#tableTransactionsForAll .fa-question {
|
||||
color: #DCDCDC;
|
||||
}
|
||||
|
||||
#tableTransactionsForAll .fa-check {
|
||||
color: #228B22;
|
||||
}
|
||||
@@ -8,6 +8,7 @@
|
||||
<th>From</th>
|
||||
<th>To</th>
|
||||
<th>Value</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tfoot>
|
||||
@@ -18,6 +19,7 @@
|
||||
<th>From</th>
|
||||
<th>To</th>
|
||||
<th>Value</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
<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>
|
||||
<div id="addressList" class="{{#if addressData.length}}walletsWrapper{{else}}noWalletsWrapper{{/if}}">
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
<script src="./assets/scripts/sprintf.min.js"></script>
|
||||
<script src="./assets/scripts/please-wait.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/all.min.js"></script>
|
||||
|
||||
|
||||
1
main.js
1
main.js
@@ -64,4 +64,5 @@ ipcMain.on('getTemplateContent', (event, arg) => {
|
||||
});
|
||||
|
||||
require('./modules/geth.js');
|
||||
require('./modules/accounts.js');
|
||||
require('./modules/database.js');
|
||||
54
modules/accounts.js
Normal file
54
modules/accounts.js
Normal 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();
|
||||
@@ -29,7 +29,8 @@ ipcMain.on('getTransactions', (event, arg) => {
|
||||
docs[i].timestamp,
|
||||
docs[i].fromaddr,
|
||||
docs[i].toaddr,
|
||||
docs[i].value
|
||||
docs[i].value,
|
||||
docs[i].confirmed || 1
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
const {app, dialog, BrowserWindow} = require('electron')
|
||||
const child_process = require('child_process');
|
||||
const {app, dialog} = require('electron');
|
||||
const path = require('path');
|
||||
|
||||
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.on('error', function(err) {
|
||||
dialog.showErrorBox("Error starting application", "Geth failed to start!");
|
||||
app.quit();
|
||||
});
|
||||
this.gethProcess.stderr.on('data', function(data) {
|
||||
console.log(data.toString());
|
||||
|
||||
@@ -25,9 +25,11 @@
|
||||
"author": "Ether1",
|
||||
"license": "CC0-1.0",
|
||||
"dependencies": {
|
||||
"adm-zip": "^0.4.13",
|
||||
"electron-storage": "^1.0.7",
|
||||
"handlebars": "^4.0.12",
|
||||
"nedb": "^1.8.0"
|
||||
"nedb": "^1.8.0",
|
||||
"typescript": "^3.2.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"electron": "^3.0.12",
|
||||
|
||||
@@ -28,6 +28,16 @@ class Blockchain {
|
||||
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) {
|
||||
web3Local.eth.getTransactionCount(fromAddress, function( error, result ) {
|
||||
if (error) {
|
||||
@@ -65,7 +75,7 @@ class Blockchain {
|
||||
clbError("Wrong password for the selected address!");
|
||||
} else
|
||||
{
|
||||
web3Local.eth.getTransactionCount(fromAddress, function( error, result ) {
|
||||
web3Local.eth.getTransactionCount(fromAddress, 'pending', function( error, result ) {
|
||||
if (error) {
|
||||
clbError(error);
|
||||
} else {
|
||||
|
||||
@@ -139,6 +139,24 @@ $(document).on("render_send", function() {
|
||||
position: 'topRight',
|
||||
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",
|
||||
});
|
||||
*/
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// In renderer process (web page).
|
||||
const {ipcRenderer} = require('electron');
|
||||
|
||||
class Transactions {
|
||||
@@ -23,14 +22,15 @@ class Transactions {
|
||||
|
||||
$.getJSON("https://richlist.ether1.org/transactions_list.php" + params, function( result ) {
|
||||
result.data.forEach(element => {
|
||||
var Transaction = {
|
||||
ipcRenderer.send('storeTransaction', {
|
||||
block: element.block,
|
||||
txhash: element.hash,
|
||||
fromaddr: element.fromaddr,
|
||||
timestamp: element.timestamp,
|
||||
toaddr: element.toaddr,
|
||||
value: element.value
|
||||
}
|
||||
ipcRenderer.send('storeTransaction', Transaction);
|
||||
value: element.value,
|
||||
confirmed: "1"
|
||||
});
|
||||
});
|
||||
|
||||
// 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
|
||||
$('#tableTransactionsForAll').DataTable({
|
||||
"paging": false,
|
||||
@@ -129,6 +132,16 @@ class Transactions {
|
||||
"render": function ( data, type, row ) {
|
||||
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 ) {
|
||||
@@ -172,7 +185,8 @@ $(document).on("onSyncInterval", function() {
|
||||
fromaddr: element.from.toLowerCase(),
|
||||
timestamp: moment().format('YYYY-MM-DD HH:mm:ss'),
|
||||
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
|
||||
|
||||
@@ -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();
|
||||
@@ -1,4 +1,3 @@
|
||||
// In renderer process (web page).
|
||||
const {ipcRenderer} = require('electron');
|
||||
|
||||
class Wallets {
|
||||
@@ -22,6 +21,12 @@ class Wallets {
|
||||
this.addressList.push(address.toLowerCase());
|
||||
}
|
||||
|
||||
enableButtonTooltips() {
|
||||
EthoUtils.createToolTip("#btnNewAddress", "Create New Address");
|
||||
EthoUtils.createToolTip("#btnExportAccounts", "Export Accounts");
|
||||
EthoUtils.createToolTip("#btnImportAccounts", "Import Accounts");
|
||||
}
|
||||
|
||||
validateNewAccountForm() {
|
||||
if (EthoMainGUI.getAppState() == "account") {
|
||||
if (!$("#walletPasswordFirst").val()) {
|
||||
@@ -61,6 +66,7 @@ renderWalletsState() {
|
||||
// render the wallets current state
|
||||
EthoMainGUI.renderTemplate("wallets.html", data);
|
||||
$(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() {
|
||||
EthoMainGUI.copyToClipboard($(this).html());
|
||||
|
||||
@@ -168,7 +182,7 @@ $(document).on("onGethReady", function() {
|
||||
|
||||
$(document).on("onNewAccountTransaction", function() {
|
||||
if (EthoMainGUI.getAppState() == "account") {
|
||||
EthoWallets.renderWalletsState();
|
||||
EthoWallets.renderWalletsState();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user