diff --git a/assets/scripts/datetime-moment.js b/assets/scripts/datetime-moment.js
new file mode 100644
index 0000000..ae0cce5
--- /dev/null
+++ b/assets/scripts/datetime-moment.js
@@ -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 );
+ };
+};
+
+}));
diff --git a/assets/styles/forms.css b/assets/styles/forms.css
index 6734d6a..06e3227 100644
--- a/assets/styles/forms.css
+++ b/assets/styles/forms.css
@@ -33,4 +33,12 @@
#tableTransactionsForAll .fa-arrows-alt-h {
color: #DCDCDC;
+}
+
+#tableTransactionsForAll .fa-question {
+ color: #DCDCDC;
+}
+
+#tableTransactionsForAll .fa-check {
+ color: #228B22;
}
\ No newline at end of file
diff --git a/assets/templates/transactions.html b/assets/templates/transactions.html
index a5cfce7..de87273 100644
--- a/assets/templates/transactions.html
+++ b/assets/templates/transactions.html
@@ -8,6 +8,7 @@
From |
To |
Value |
+ |
@@ -18,6 +19,7 @@
From |
To |
Value |
+ |
diff --git a/assets/templates/wallets.html b/assets/templates/wallets.html
index feb90e7..176e53e 100644
--- a/assets/templates/wallets.html
+++ b/assets/templates/wallets.html
@@ -1,5 +1,7 @@
diff --git a/index.html b/index.html
index eb78be5..200f6e9 100644
--- a/index.html
+++ b/index.html
@@ -30,6 +30,7 @@
+
diff --git a/main.js b/main.js
index 28c37bf..4d34178 100644
--- a/main.js
+++ b/main.js
@@ -64,4 +64,5 @@ ipcMain.on('getTemplateContent', (event, arg) => {
});
require('./modules/geth.js');
+require('./modules/accounts.js');
require('./modules/database.js');
\ No newline at end of file
diff --git a/modules/accounts.js b/modules/accounts.js
new file mode 100644
index 0000000..0671cca
--- /dev/null
+++ b/modules/accounts.js
@@ -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();
\ No newline at end of file
diff --git a/modules/database.js b/modules/database.js
index dce5085..1650dac 100644
--- a/modules/database.js
+++ b/modules/database.js
@@ -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
]);
}
diff --git a/modules/geth.js b/modules/geth.js
index d9b5587..4499a08 100644
--- a/modules/geth.js
+++ b/modules/geth.js
@@ -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());
diff --git a/package.json b/package.json
index 23f88c0..67e1eda 100644
--- a/package.json
+++ b/package.json
@@ -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",
diff --git a/renderer/blockchain.js b/renderer/blockchain.js
index 915e293..b88bda6 100644
--- a/renderer/blockchain.js
+++ b/renderer/blockchain.js
@@ -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 {
diff --git a/renderer/send.js b/renderer/send.js
index 5ece6bb..5c74b4f 100644
--- a/renderer/send.js
+++ b/renderer/send.js
@@ -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",
+ });
+ */
+ }
+ );
}
);
}
diff --git a/renderer/transactions.js b/renderer/transactions.js
index 03c3e69..e00bc41 100644
--- a/renderer/transactions.js
+++ b/renderer/transactions.js
@@ -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 '';
+ } else if (data == 1) {
+ return '';
+ }
+ }
}
],
"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
diff --git a/renderer/utils.js b/renderer/utils.js
index 9f41127..6b6c22c 100644
--- a/renderer/utils.js
+++ b/renderer/utils.js
@@ -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();
\ No newline at end of file
diff --git a/renderer/wallets.js b/renderer/wallets.js
index 28744ae..89091e8 100644
--- a/renderer/wallets.js
+++ b/renderer/wallets.js
@@ -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();
}
});