+ initial import

This commit is contained in:
Taegus
2018-12-16 13:33:09 +01:00
commit 554f96387b
47 changed files with 14962 additions and 0 deletions

45
README.md Normal file
View File

@@ -0,0 +1,45 @@
# electron-quick-start
**Clone and run for a quick way to see Electron in action.**
This is a minimal Electron application based on the [Quick Start Guide](https://electronjs.org/docs/tutorial/quick-start) within the Electron documentation.
**Use this app along with the [Electron API Demos](https://electronjs.org/#get-started) app for API code examples to help you get started.**
A basic Electron application needs just these files:
- `package.json` - Points to the app's main file and lists its details and dependencies.
- `main.js` - Starts the app and creates a browser window to render HTML. This is the app's **main process**.
- `index.html` - A web page to render. This is the app's **renderer process**.
You can learn more about each of these components within the [Quick Start Guide](https://electronjs.org/docs/tutorial/quick-start).
## To Use
To clone and run this repository you'll need [Git](https://git-scm.com) and [Node.js](https://nodejs.org/en/download/) (which comes with [npm](http://npmjs.com)) installed on your computer. From your command line:
```bash
# Clone this repository
git clone https://github.com/electron/electron-quick-start
# Go into the repository
cd electron-quick-start
# Install dependencies
npm install
# Run the app
npm start
```
Note: If you're using Linux Bash for Windows, [see this guide](https://www.howtogeek.com/261575/how-to-run-graphical-linux-desktop-applications-from-windows-10s-bash-shell/) or use `node` from the command prompt.
## Resources for Learning Electron
- [electronjs.org/docs](https://electronjs.org/docs) - all of Electron's documentation
- [electronjs.org/community#boilerplates](https://electronjs.org/community#boilerplates) - sample starter apps created by the community
- [electron/electron-quick-start](https://github.com/electron/electron-quick-start) - a very basic starter Electron app
- [electron/simple-samples](https://github.com/electron/simple-samples) - small applications with ideas for taking them further
- [electron/electron-api-demos](https://github.com/electron/electron-api-demos) - an Electron app that teaches you how to use Electron
- [hokein/electron-sample-apps](https://github.com/hokein/electron-sample-apps) - small demo apps for the various Electron APIs
## License
[CC0 1.0 (Public Domain)](LICENSE.md)

BIN
assets/images/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

1434
assets/images/sort_asc.png Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

1434
assets/images/sort_both.png Normal file

File diff suppressed because it is too large Load Diff

1434
assets/images/sort_desc.png Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

5
assets/scripts/all.min.js vendored Normal file

File diff suppressed because one or more lines are too long

438
assets/scripts/datatables.min.js vendored Normal file

File diff suppressed because one or more lines are too long

4838
assets/scripts/handlebars.js Normal file

File diff suppressed because one or more lines are too long

6
assets/scripts/iziModal.min.js vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

2
assets/scripts/jquery.min.js vendored Normal file

File diff suppressed because one or more lines are too long

6
assets/scripts/materialize.min.js vendored Normal file

File diff suppressed because one or more lines are too long

1
assets/scripts/moment.min.js vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

2
assets/scripts/progressbar.min.js vendored Normal file

File diff suppressed because one or more lines are too long

2
assets/scripts/sprintf.min.js vendored Normal file
View File

@@ -0,0 +1,2 @@
/*! sprintf-js | Alexandru Marasteanu <hello@alexei.ro> (http://alexei.ro/) | BSD-3-Clause */
!function(a){function b(){var a=arguments[0],c=b.cache;return c[a]&&c.hasOwnProperty(a)||(c[a]=b.parse(a)),b.format.call(null,c[a],arguments)}function c(a){return"number"==typeof a?"number":"string"==typeof a?"string":Object.prototype.toString.call(a).slice(8,-1).toLowerCase()}function d(a,b){return b>=0&&7>=b&&g[a]?g[a][b]:Array(b+1).join(a)}var e={not_string:/[^s]/,not_bool:/[^t]/,not_type:/[^T]/,not_primitive:/[^v]/,number:/[diefg]/,numeric_arg:/bcdiefguxX/,json:/[j]/,not_json:/[^j]/,text:/^[^\x25]+/,modulo:/^\x25{2}/,placeholder:/^\x25(?:([1-9]\d*)\$|\(([^\)]+)\))?(\+)?(0|'[^$])?(-)?(\d+)?(?:\.(\d+))?([b-gijostTuvxX])/,key:/^([a-z_][a-z_\d]*)/i,key_access:/^\.([a-z_][a-z_\d]*)/i,index_access:/^\[(\d+)\]/,sign:/^[\+\-]/};b.format=function(a,f){var g,h,i,j,k,l,m,n=1,o=a.length,p="",q=[],r=!0,s="";for(h=0;o>h;h++)if(p=c(a[h]),"string"===p)q[q.length]=a[h];else if("array"===p){if(j=a[h],j[2])for(g=f[n],i=0;i<j[2].length;i++){if(!g.hasOwnProperty(j[2][i]))throw new Error(b('[sprintf] property "%s" does not exist',j[2][i]));g=g[j[2][i]]}else g=j[1]?f[j[1]]:f[n++];if(e.not_type.test(j[8])&&e.not_primitive.test(j[8])&&"function"==c(g)&&(g=g()),e.numeric_arg.test(j[8])&&"number"!=c(g)&&isNaN(g))throw new TypeError(b("[sprintf] expecting number but found %s",c(g)));switch(e.number.test(j[8])&&(r=g>=0),j[8]){case"b":g=parseInt(g,10).toString(2);break;case"c":g=String.fromCharCode(parseInt(g,10));break;case"d":case"i":g=parseInt(g,10);break;case"j":g=JSON.stringify(g,null,j[6]?parseInt(j[6]):0);break;case"e":g=j[7]?parseFloat(g).toExponential(j[7]):parseFloat(g).toExponential();break;case"f":g=j[7]?parseFloat(g).toFixed(j[7]):parseFloat(g);break;case"g":g=j[7]?parseFloat(g).toPrecision(j[7]):parseFloat(g);break;case"o":g=g.toString(8);break;case"s":g=String(g),g=j[7]?g.substring(0,j[7]):g;break;case"t":g=String(!!g),g=j[7]?g.substring(0,j[7]):g;break;case"T":g=c(g),g=j[7]?g.substring(0,j[7]):g;break;case"u":g=parseInt(g,10)>>>0;break;case"v":g=g.valueOf(),g=j[7]?g.substring(0,j[7]):g;break;case"x":g=parseInt(g,10).toString(16);break;case"X":g=parseInt(g,10).toString(16).toUpperCase()}e.json.test(j[8])?q[q.length]=g:(!e.number.test(j[8])||r&&!j[3]?s="":(s=r?"+":"-",g=g.toString().replace(e.sign,"")),l=j[4]?"0"===j[4]?"0":j[4].charAt(1):" ",m=j[6]-(s+g).length,k=j[6]&&m>0?d(l,m):"",q[q.length]=j[5]?s+g+k:"0"===l?s+k+g:k+s+g)}return q.join("")},b.cache={},b.parse=function(a){for(var b=a,c=[],d=[],f=0;b;){if(null!==(c=e.text.exec(b)))d[d.length]=c[0];else if(null!==(c=e.modulo.exec(b)))d[d.length]="%";else{if(null===(c=e.placeholder.exec(b)))throw new SyntaxError("[sprintf] unexpected placeholder");if(c[2]){f|=1;var g=[],h=c[2],i=[];if(null===(i=e.key.exec(h)))throw new SyntaxError("[sprintf] failed to parse named argument key");for(g[g.length]=i[1];""!==(h=h.substring(i[0].length));)if(null!==(i=e.key_access.exec(h)))g[g.length]=i[1];else{if(null===(i=e.index_access.exec(h)))throw new SyntaxError("[sprintf] failed to parse named argument key");g[g.length]=i[1]}c[2]=g}else f|=2;if(3===f)throw new Error("[sprintf] mixing positional and named placeholders is not (yet) supported");d[d.length]=c}b=b.substring(c[0].length)}return d};var f=function(a,c,d){return d=(c||[]).slice(0),d.splice(0,0,a),b.apply(null,d)},g={0:["","0","00","000","0000","00000","000000","0000000"]," ":[""," "," "," "," "," "," "," "],_:["","_","__","___","____","_____","______","_______"]};"undefined"!=typeof exports?(exports.sprintf=b,exports.vsprintf=f):(a.sprintf=b,a.vsprintf=f,"function"==typeof define&&define.amd&&define(function(){return{sprintf:b,vsprintf:f}}))}("undefined"==typeof window?this:window);

5
assets/styles/all.min.css vendored Normal file

File diff suppressed because one or more lines are too long

45
assets/styles/buttons.css Normal file
View File

@@ -0,0 +1,45 @@
.btn-etho {
color: #ffffff;
background-color: #7A1336;
border-color: #450118;
}
.btn-etho:hover,
.btn-etho:focus,
.btn-etho:active,
.btn-etho.active,
.open .dropdown-toggle.btn-etho {
color: #ffffff;
background-color: #B01549;
border-color: #450118;
}
.btn-etho:active,
.btn-etho.active,
.open .dropdown-toggle.btn-etho {
background-image: none;
}
.btn-etho.disabled,
.btn-etho[disabled],
fieldset[disabled] .btn-etho,
.btn-etho.disabled:hover,
.btn-etho[disabled]:hover,
fieldset[disabled] .btn-etho:hover,
.btn-etho.disabled:focus,
.btn-etho[disabled]:focus,
fieldset[disabled] .btn-etho:focus,
.btn-etho.disabled:active,
.btn-etho[disabled]:active,
fieldset[disabled] .btn-etho:active,
.btn-etho.disabled.active,
.btn-etho[disabled].active,
fieldset[disabled] .btn-etho.active {
background-color: #7A1336;
border-color: #450118;
}
.btn-etho .badge {
color: #7A1336;
background-color: #ffffff;
}

33
assets/styles/datatables.min.css vendored Normal file

File diff suppressed because one or more lines are too long

24
assets/styles/forms.css Normal file
View File

@@ -0,0 +1,24 @@
.select-wrapper .caret {
fill: #aaa;
}
.select-dropdown
{
color: #aaa;
}
.dropdown-content li>a, .dropdown-content li>span {
color: rgb(36, 13, 21);
}
#sendFeeRange {
margin-top: 40px;
}
.card-panel {
background-color: #333
}
.sendWrapper input {
color: #aaa;
}

6
assets/styles/iziModal.min.css vendored Normal file

File diff suppressed because one or more lines are too long

13
assets/styles/materialize.min.css vendored Normal file

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,161 @@
/* line 17, ../src/please-wait.scss */
body.pg-loading {
overflow: hidden;
}
/* line 21, ../src/please-wait.scss */
.pg-loading-screen {
position: fixed;
bottom: 0;
left: 0;
right: 0;
top: 0;
z-index: 1000000;
opacity: 1;
background-color: #FFF;
-webkit-transition: background-color 0.4s ease-in-out 0s;
-moz-transition: background-color 0.4s ease-in-out 0s;
-ms-transition: background-color 0.4s ease-in-out 0s;
-o-transition: background-color 0.4s ease-in-out 0s;
transition: background-color 0.4s ease-in-out 0s;
}
/* line 32, ../src/please-wait.scss */
.pg-loading-screen.pg-loaded {
opacity: 0;
-webkit-animation: pgAnimLoaded 0.5s cubic-bezier(0.7, 0, 0.3, 1) both;
-moz-animation: pgAnimLoaded 0.5s cubic-bezier(0.7, 0, 0.3, 1) both;
-ms-animation: pgAnimLoaded 0.5s cubic-bezier(0.7, 0, 0.3, 1) both;
-o-animation: pgAnimLoaded 0.5s cubic-bezier(0.7, 0, 0.3, 1) both;
animation: pgAnimLoaded 0.5s cubic-bezier(0.7, 0, 0.3, 1) both;
}
/* line 38, ../src/please-wait.scss */
.pg-loading-screen.pg-loading .pg-loading-logo-header, .pg-loading-screen.pg-loading .pg-loading-html {
opacity: 1;
}
/* line 42, ../src/please-wait.scss */
.pg-loading-screen.pg-loading .pg-loading-logo-header, .pg-loading-screen.pg-loading .pg-loading-html:not(.pg-loaded) {
-webkit-animation: pgAnimLoading 1s cubic-bezier(0.7, 0, 0.3, 1) both;
-moz-animation: pgAnimLoading 1s cubic-bezier(0.7, 0, 0.3, 1) both;
-ms-animation: pgAnimLoading 1s cubic-bezier(0.7, 0, 0.3, 1) both;
-o-animation: pgAnimLoading 1s cubic-bezier(0.7, 0, 0.3, 1) both;
animation: pgAnimLoading 1s cubic-bezier(0.7, 0, 0.3, 1) both;
}
/* line 46, ../src/please-wait.scss */
.pg-loading-screen.pg-loading .pg-loading-html:not(.pg-loaded) {
-webkit-animation-delay: 0.3s;
-moz-animation-delay: 0.3s;
-ms-animation-delay: 0.3s;
-o-animation-delay: 0.3s;
animation-delay: 0.3s;
}
/* line 51, ../src/please-wait.scss */
.pg-loading-screen .pg-loading-inner {
height: 100%;
width: 100%;
margin: 0;
padding: 0;
position: static;
}
/* line 59, ../src/please-wait.scss */
.pg-loading-screen .pg-loading-center-outer {
width: 100%;
padding: 0;
display: table !important;
height: 100%;
position: absolute;
top: 0;
left: 0;
margin: 0;
}
/* line 70, ../src/please-wait.scss */
.pg-loading-screen .pg-loading-center-middle {
padding: 0;
vertical-align: middle;
display: table-cell !important;
margin: 0;
text-align: center;
}
/* line 78, ../src/please-wait.scss */
.pg-loading-screen .pg-loading-logo-header, .pg-loading-screen .pg-loading-html {
width: 100%;
opacity: 0;
}
/* line 83, ../src/please-wait.scss */
.pg-loading-screen .pg-loading-logo-header {
text-align: center;
}
/* line 86, ../src/please-wait.scss */
.pg-loading-screen .pg-loading-logo-header img {
display: inline-block !important;
}
/* line 91, ../src/please-wait.scss */
.pg-loading-screen .pg-loading-html {
margin-top: 90px;
}
/* line 94, ../src/please-wait.scss */
.pg-loading-screen .pg-loading-html.pg-loaded {
-webkit-transition: opacity 0.5s cubic-bezier(0.7, 0, 0.3, 1);
-moz-transition: opacity 0.5s cubic-bezier(0.7, 0, 0.3, 1);
-ms-transition: opacity 0.5s cubic-bezier(0.7, 0, 0.3, 1);
-o-transition: opacity 0.5s cubic-bezier(0.7, 0, 0.3, 1);
transition: opacity 0.5s cubic-bezier(0.7, 0, 0.3, 1);
}
/* line 97, ../src/please-wait.scss */
.pg-loading-screen .pg-loading-html.pg-loaded.pg-removing {
opacity: 0;
}
/* line 101, ../src/please-wait.scss */
.pg-loading-screen .pg-loading-html.pg-loaded.pg-loading {
opacity: 1;
}
@-webkit-keyframes pgAnimLoading {
from {
opacity: 0;
}
}
@-moz-keyframes pgAnimLoading {
from {
opacity: 0;
}
}
@-o-keyframes pgAnimLoading {
from {
opacity: 0;
}
}
@-ms-keyframes pgAnimLoading {
from {
opacity: 0;
}
}
@keyframes pgAnimLoading {
from {
opacity: 0;
}
}
@-webkit-keyframes pgAnimLoaded {
from {
opacity: 1;
}
}
@-moz-keyframes pgAnimLoaded {
from {
opacity: 1;
}
}
@-o-keyframes pgAnimLoaded {
from {
opacity: 1;
}
}
@-ms-keyframes pgAnimLoaded {
from {
opacity: 1;
}
}
@keyframes pgAnimLoaded {
from {
opacity: 1;
}
}

41
assets/styles/spinner.css Normal file
View File

@@ -0,0 +1,41 @@
.spinner {
margin: 0px auto 0;
width: 70px;
text-align: center;
}
.spinner > div {
width: 18px;
height: 18px;
background-color: #333;
border-radius: 100%;
display: inline-block;
-webkit-animation: sk-bouncedelay 1.4s infinite ease-in-out both;
animation: sk-bouncedelay 1.4s infinite ease-in-out both;
}
.spinner .bounce1 {
-webkit-animation-delay: -0.32s;
animation-delay: -0.32s;
}
.spinner .bounce2 {
-webkit-animation-delay: -0.16s;
animation-delay: -0.16s;
}
@-webkit-keyframes sk-bouncedelay {
0%, 80%, 100% { -webkit-transform: scale(0) }
40% { -webkit-transform: scale(1.0) }
}
@keyframes sk-bouncedelay {
0%, 80%, 100% {
-webkit-transform: scale(0);
transform: scale(0);
} 40% {
-webkit-transform: scale(1.0);
transform: scale(1.0);
}
}

317
assets/styles/style.css Normal file
View File

@@ -0,0 +1,317 @@
html {
color: #aaa;
}
/* hide / show the body when the loader is finished */
body > .inner {
display: none;
}
body.pg-loaded > .inner {
display: block;
}
/* Style the sidebar - fixed full height */
.sidebar {
height: 100%;
width: 120px;
position: fixed;
z-index: 1;
top: 0;
left: 0;
background-color: #111;
overflow-x: hidden;
padding-top: 16px;
}
/* Style sidebar links */
.sidebar a {
padding: 6px 8px 6px 16px;
text-decoration: none;
font-size: 20px;
color: #818181;
display: block;
}
/* Style links on mouse-over */
.sidebar a:hover {
color: #f1f1f1;
}
#peerCount {
bottom: 0;
height: 20px;
color: #aaa;
position: absolute;
font-size: 0.8em;
padding-left: 5px;
}
#syncProgress
{
height: 20px;
position: fixed;
z-index: 1;
left: 120px;
right: 0;
bottom: 0;
background-color: #111;
border-top: 1px solid #111;
}
#mainContent
{
position: fixed;
z-index: 1;
left: 120px;
top: 0;
right: 0;
bottom: 20px;
background-color: #212529;
}
/* Style the main content */
.main {
margin-left: 160px; /* Same as the width of the sidenav */
padding: 0px 10px;
}
/* Add media queries for small screens (when the height of the screen is less than 450px, add a smaller padding and font-size) */
@media screen and (max-height: 450px) {
.sidebar {padding-top: 15px;}
.sidebar a {font-size: 18px;}
}
.sidebarIcon {
width: 100%;
height: 80px;
line-height: 80px;
}
.sidebarIconWrapper {
width: 100%;
height: 80px;
display: inline-block;
text-align: center;
vertical-align: middle;
}
#sumBalance {
float: right;
font-weight: bold;
line-height: 38px;
margin-right: 10px;
}
.modalBodyPassword {
margin: 20px;
}
#dlgGeneralError .modalBodyPassword {
height: 100px;
}
#dlgCreateWalletPassword .modalBodyPassword,
#dlgCreateWalletPassword .modalBodyPassword {
height: 200px;
}
#dlgSendWalletPassword .modalBodyPassword {
height: 300px;
}
#dlgChangeWalletName .modalBodyPassword {
height: 150px;
}
.btn-dialog-confirm {
position: absolute;
bottom: 10px;
right: 20px;
}
.modalDialog {
display: none;
border-bottom: 3px solid #7A1336 !important;
border-radius: 0px !important;
}
.iziModal-header {
background: #7A1336 !important;
}
#walletsToolbar {
padding: 10px;
border-bottom: 1px solid #111;
border-radius: 0px;
height: 60px;
}
#transactionsWrapper {
color: #aaa;
padding-top: 10px;
}
div.iconSelected {
background-color: ivory;
border: 1px solid slategray;
}
div.loadingText {
color: #aaa;
margin-top: 50px;
}
#tableTransactionsForAll_wrapper .dataTables_scroll {
font-size: 0.9em;
}
div.sidebar svg {
color: #7A1336;
}
#tableTransactionsForAll_filter {
color: #aaa;
padding-right: 10px;
}
#tableTransactionsForAll_filter label {
color: #aaa;
font-size: 1em;
}
#tableTransactionsForAll_filter input {
width: 400px;
color: #aaa;
padding: 12px 10px;
display: inline-block;
border: 1px solid #ccc;
border-radius: 4px;
height: 2.5em;
box-sizing: border-box;
}
.floatThead-container {
background-color: #212529;
}
.dataTables_scrollBody .transactionsBlockNum {
color: #f92472;
}
.sendWrapper,
.settingsWrapper
{
padding: 30px;
}
#sendMaxAmmount {
margin-right: 5px;
}
#btnSendAll {
margin-left: 10px;
}
#addressList {
overflow-y: auto;
height: calc(100vh - 80px);
}
#addressList .colEdit {
padding-left: 20px;
}
.sendTXInfo {
height: 30px;
}
.sendTXInfo label {
line-height: 30px;
font-size: 1.2em;
}
.sendTXdivider {
margin-top: 30px;
margin-bottom: 20px;
}
#fromAddressInfo,
#toAddressInfo,
#valueToSendInfo,
#feeToPayInfo {
color: #7A1336;
margin-left: 5px;
margin-right: 5px;
}
.accountName {
color: #7A1336;
}
.accountAddr {
font-size: 0.8em;
}
.pg-loading-screen .pg-loading-html {
margin-top: 0px;
}
.fromAddressSelect .dropdown-content {
width: 600px !important;
}
.loadingOverlay {
background-color: #212529;
display: none;
position: fixed;
left: 120px;
right: 0;
bottom: 20px;
top: 0;
}
.loadingOverlay .loadingWrapper {
height: 100%;
width: 100%;
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
}
.loadingTextTransactions {
font-size: 1.3em;
margin-bottom: 30px;
}
.spinner .bounce {
background-color: #aaa;
}
.sumBalance {
color: white;
background-color: #7A1336;
border: 2px solid #991643;
border-radius: 10px;
padding: 5px;
}
.cleanText {
margin-left: 10px;
}
.cleanWrapper {
margin: 10px;
}
#noWalletsPresent {
font-size: 1.3em;
}
div.noWalletsWrapper {
display: flex;
align-items: center;
justify-content: center;
}

View File

@@ -0,0 +1,357 @@
/*
* Table styles
*/
table.dataTable {
width: 100%;
margin: 0 auto;
clear: both;
border-collapse: collapse;
border-spacing: 0;
/*
* Header and footer styles
*/
/*
* Body styles
*/ }
table.dataTable thead th,
table.dataTable tfoot th {
font-weight: bold; }
table.dataTable thead th,
table.dataTable thead td {
padding: 10px 18px;
border-bottom: 1px solid white; }
table.dataTable thead th:active,
table.dataTable thead td:active {
outline: none; }
table.dataTable tfoot th,
table.dataTable tfoot td {
padding: 10px 18px 6px 18px;
border-top: 1px solid white; }
table.dataTable thead .sorting,
table.dataTable thead .sorting_asc,
table.dataTable thead .sorting_desc,
table.dataTable thead .sorting_asc_disabled,
table.dataTable thead .sorting_desc_disabled {
cursor: pointer;
*cursor: hand;
background-repeat: no-repeat;
background-position: center right; }
table.dataTable thead .sorting {
background-image: url("../images/sort_both.png"); }
table.dataTable thead .sorting_asc {
background-image: url("../images/sort_asc.png"); }
table.dataTable thead .sorting_desc {
background-image: url("../images/sort_desc.png"); }
table.dataTable thead .sorting_asc_disabled {
background-image: url("../images/sort_asc_disabled.png"); }
table.dataTable thead .sorting_desc_disabled {
background-image: url("../images/sort_desc_disabled.png"); }
table.dataTable tbody tr {
background-color: #212529; }
table.dataTable tbody tr.selected {
background-color: #b0bed9; }
table.dataTable tbody th,
table.dataTable tbody td {
padding: 8px 10px; }
table.dataTable.row-border tbody th, table.dataTable.row-border tbody td, table.dataTable.display tbody th, table.dataTable.display tbody td {
border-top: 1px solid #666 }
table.dataTable.row-border tbody tr:first-child th,
table.dataTable.row-border tbody tr:first-child td, table.dataTable.display tbody tr:first-child th,
table.dataTable.display tbody tr:first-child td {
border-top: none; }
table.dataTable.cell-border tbody th, table.dataTable.cell-border tbody td {
border-top: 1px solid #bbb;
border-right: 1px solid #bbb; }
table.dataTable.cell-border tbody tr th:first-child,
table.dataTable.cell-border tbody tr td:first-child {
border-left: 1px solid #bbb; }
table.dataTable.cell-border tbody tr:first-child th,
table.dataTable.cell-border tbody tr:first-child td {
border-top: none; }
table.dataTable.stripe tbody tr.odd, table.dataTable.display tbody tr.odd {
background-color: #202428; }
table.dataTable.stripe tbody tr.odd.selected, table.dataTable.display tbody tr.odd.selected {
background-color: #abb9d3; }
table.dataTable.hover tbody tr:hover, table.dataTable.display tbody tr:hover {
background-color: #1f2327; }
table.dataTable.hover tbody tr:hover.selected, table.dataTable.display tbody tr:hover.selected {
background-color: #a9b7d1; }
table.dataTable.order-column tbody tr > .sorting_1,
table.dataTable.order-column tbody tr > .sorting_2,
table.dataTable.order-column tbody tr > .sorting_3, table.dataTable.display tbody tr > .sorting_1,
table.dataTable.display tbody tr > .sorting_2,
table.dataTable.display tbody tr > .sorting_3 {
background-color: #202428; }
table.dataTable.order-column tbody tr.selected > .sorting_1,
table.dataTable.order-column tbody tr.selected > .sorting_2,
table.dataTable.order-column tbody tr.selected > .sorting_3, table.dataTable.display tbody tr.selected > .sorting_1,
table.dataTable.display tbody tr.selected > .sorting_2,
table.dataTable.display tbody tr.selected > .sorting_3 {
background-color: #acbad4; }
table.dataTable.display tbody tr.odd > .sorting_1, table.dataTable.order-column.stripe tbody tr.odd > .sorting_1 {
background-color: #1f2326; }
table.dataTable.display tbody tr.odd > .sorting_2, table.dataTable.order-column.stripe tbody tr.odd > .sorting_2 {
background-color: #1f2327; }
table.dataTable.display tbody tr.odd > .sorting_3, table.dataTable.order-column.stripe tbody tr.odd > .sorting_3 {
background-color: #1f2327; }
table.dataTable.display tbody tr.odd.selected > .sorting_1, table.dataTable.order-column.stripe tbody tr.odd.selected > .sorting_1 {
background-color: #a6b3cd; }
table.dataTable.display tbody tr.odd.selected > .sorting_2, table.dataTable.order-column.stripe tbody tr.odd.selected > .sorting_2 {
background-color: #a7b5ce; }
table.dataTable.display tbody tr.odd.selected > .sorting_3, table.dataTable.order-column.stripe tbody tr.odd.selected > .sorting_3 {
background-color: #a9b6d0; }
table.dataTable.display tbody tr.even > .sorting_1, table.dataTable.order-column.stripe tbody tr.even > .sorting_1 {
background-color: #202428; }
table.dataTable.display tbody tr.even > .sorting_2, table.dataTable.order-column.stripe tbody tr.even > .sorting_2 {
background-color: #202428; }
table.dataTable.display tbody tr.even > .sorting_3, table.dataTable.order-column.stripe tbody tr.even > .sorting_3 {
background-color: #202428; }
table.dataTable.display tbody tr.even.selected > .sorting_1, table.dataTable.order-column.stripe tbody tr.even.selected > .sorting_1 {
background-color: #acbad4; }
table.dataTable.display tbody tr.even.selected > .sorting_2, table.dataTable.order-column.stripe tbody tr.even.selected > .sorting_2 {
background-color: #adbbd6; }
table.dataTable.display tbody tr.even.selected > .sorting_3, table.dataTable.order-column.stripe tbody tr.even.selected > .sorting_3 {
background-color: #afbdd8; }
table.dataTable.display tbody tr:hover > .sorting_1, table.dataTable.order-column.hover tbody tr:hover > .sorting_1 {
background-color: #1e2125; }
table.dataTable.display tbody tr:hover > .sorting_2, table.dataTable.order-column.hover tbody tr:hover > .sorting_2 {
background-color: #1e2225; }
table.dataTable.display tbody tr:hover > .sorting_3, table.dataTable.order-column.hover tbody tr:hover > .sorting_3 {
background-color: #1e2226; }
table.dataTable.display tbody tr:hover.selected > .sorting_1, table.dataTable.order-column.hover tbody tr:hover.selected > .sorting_1 {
background-color: #a1aec7; }
table.dataTable.display tbody tr:hover.selected > .sorting_2, table.dataTable.order-column.hover tbody tr:hover.selected > .sorting_2 {
background-color: #a2afc8; }
table.dataTable.display tbody tr:hover.selected > .sorting_3, table.dataTable.order-column.hover tbody tr:hover.selected > .sorting_3 {
background-color: #a4b2cb; }
table.dataTable.no-footer {
border-bottom: 1px solid white; }
table.dataTable.nowrap th, table.dataTable.nowrap td {
white-space: nowrap; }
table.dataTable.compact thead th,
table.dataTable.compact thead td {
padding: 4px 17px 4px 4px; }
table.dataTable.compact tfoot th,
table.dataTable.compact tfoot td {
padding: 4px; }
table.dataTable.compact tbody th,
table.dataTable.compact tbody td {
padding: 4px; }
table.dataTable th.dt-left,
table.dataTable td.dt-left {
text-align: left; }
table.dataTable th.dt-center,
table.dataTable td.dt-center,
table.dataTable td.dataTables_empty {
text-align: center; }
table.dataTable th.dt-right,
table.dataTable td.dt-right {
text-align: right; }
table.dataTable th.dt-justify,
table.dataTable td.dt-justify {
text-align: justify; }
table.dataTable th.dt-nowrap,
table.dataTable td.dt-nowrap {
white-space: nowrap; }
table.dataTable thead th.dt-head-left,
table.dataTable thead td.dt-head-left,
table.dataTable tfoot th.dt-head-left,
table.dataTable tfoot td.dt-head-left {
text-align: left; }
table.dataTable thead th.dt-head-center,
table.dataTable thead td.dt-head-center,
table.dataTable tfoot th.dt-head-center,
table.dataTable tfoot td.dt-head-center {
text-align: center; }
table.dataTable thead th.dt-head-right,
table.dataTable thead td.dt-head-right,
table.dataTable tfoot th.dt-head-right,
table.dataTable tfoot td.dt-head-right {
text-align: right; }
table.dataTable thead th.dt-head-justify,
table.dataTable thead td.dt-head-justify,
table.dataTable tfoot th.dt-head-justify,
table.dataTable tfoot td.dt-head-justify {
text-align: justify; }
table.dataTable thead th.dt-head-nowrap,
table.dataTable thead td.dt-head-nowrap,
table.dataTable tfoot th.dt-head-nowrap,
table.dataTable tfoot td.dt-head-nowrap {
white-space: nowrap; }
table.dataTable tbody th.dt-body-left,
table.dataTable tbody td.dt-body-left {
text-align: left; }
table.dataTable tbody th.dt-body-center,
table.dataTable tbody td.dt-body-center {
text-align: center; }
table.dataTable tbody th.dt-body-right,
table.dataTable tbody td.dt-body-right {
text-align: right; }
table.dataTable tbody th.dt-body-justify,
table.dataTable tbody td.dt-body-justify {
text-align: justify; }
table.dataTable tbody th.dt-body-nowrap,
table.dataTable tbody td.dt-body-nowrap {
white-space: nowrap; }
table.dataTable,
table.dataTable th,
table.dataTable td {
box-sizing: content-box; }
/*
* Control feature layout
*/
.dataTables_wrapper {
position: relative;
clear: both;
*zoom: 1;
zoom: 1; }
.dataTables_wrapper .dataTables_length {
float: left; }
.dataTables_wrapper .dataTables_filter {
float: right;
text-align: right; }
.dataTables_wrapper .dataTables_filter input {
margin-left: 0.5em; }
.dataTables_wrapper .dataTables_info {
clear: both;
float: left;
padding-top: 0.755em; }
.dataTables_wrapper .dataTables_paginate {
float: right;
text-align: right;
padding-top: 0.25em; }
.dataTables_wrapper .dataTables_paginate .paginate_button {
box-sizing: border-box;
display: inline-block;
min-width: 1.5em;
padding: 0.5em 1em;
margin-left: 2px;
text-align: center;
text-decoration: none !important;
cursor: pointer;
*cursor: hand;
color: #333333 !important;
border: 1px solid transparent;
border-radius: 2px; }
.dataTables_wrapper .dataTables_paginate .paginate_button.current, .dataTables_wrapper .dataTables_paginate .paginate_button.current:hover {
color: #333333 !important;
border: 1px solid black;
background-color: #616c78;
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #616c78), color-stop(100%, #212529));
/* Chrome,Safari4+ */
background: -webkit-linear-gradient(top, #616c78 0%, #212529 100%);
/* Chrome10+,Safari5.1+ */
background: -moz-linear-gradient(top, #616c78 0%, #212529 100%);
/* FF3.6+ */
background: -ms-linear-gradient(top, #616c78 0%, #212529 100%);
/* IE10+ */
background: -o-linear-gradient(top, #616c78 0%, #212529 100%);
/* Opera 11.10+ */
background: linear-gradient(to bottom, #616c78 0%, #212529 100%);
/* W3C */ }
.dataTables_wrapper .dataTables_paginate .paginate_button.disabled, .dataTables_wrapper .dataTables_paginate .paginate_button.disabled:hover, .dataTables_wrapper .dataTables_paginate .paginate_button.disabled:active {
cursor: default;
color: #666 !important;
border: 1px solid transparent;
background: transparent;
box-shadow: none; }
.dataTables_wrapper .dataTables_paginate .paginate_button:hover {
color: white !important;
border: 1px solid #111111;
background-color: #585858;
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #585858), color-stop(100%, #111111));
/* Chrome,Safari4+ */
background: -webkit-linear-gradient(top, #585858 0%, #111111 100%);
/* Chrome10+,Safari5.1+ */
background: -moz-linear-gradient(top, #585858 0%, #111111 100%);
/* FF3.6+ */
background: -ms-linear-gradient(top, #585858 0%, #111111 100%);
/* IE10+ */
background: -o-linear-gradient(top, #585858 0%, #111111 100%);
/* Opera 11.10+ */
background: linear-gradient(to bottom, #585858 0%, #111111 100%);
/* W3C */ }
.dataTables_wrapper .dataTables_paginate .paginate_button:active {
outline: none;
background-color: #2b2b2b;
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #2b2b2b), color-stop(100%, #0c0c0c));
/* Chrome,Safari4+ */
background: -webkit-linear-gradient(top, #2b2b2b 0%, #0c0c0c 100%);
/* Chrome10+,Safari5.1+ */
background: -moz-linear-gradient(top, #2b2b2b 0%, #0c0c0c 100%);
/* FF3.6+ */
background: -ms-linear-gradient(top, #2b2b2b 0%, #0c0c0c 100%);
/* IE10+ */
background: -o-linear-gradient(top, #2b2b2b 0%, #0c0c0c 100%);
/* Opera 11.10+ */
background: linear-gradient(to bottom, #2b2b2b 0%, #0c0c0c 100%);
/* W3C */
box-shadow: inset 0 0 3px #111; }
.dataTables_wrapper .dataTables_paginate .ellipsis {
padding: 0 1em; }
.dataTables_wrapper .dataTables_processing {
position: absolute;
top: 50%;
left: 50%;
width: 100%;
height: 40px;
margin-left: -50%;
margin-top: -25px;
padding-top: 20px;
text-align: center;
font-size: 1.2em;
background-color: white;
background: -webkit-gradient(linear, left top, right top, color-stop(0%, rgba(33, 37, 41, 0)), color-stop(25%, rgba(33, 37, 41, 0.9)), color-stop(75%, rgba(33, 37, 41, 0.9)), color-stop(100%, rgba(255, 255, 255, 0)));
background: -webkit-linear-gradient(left, rgba(33, 37, 41, 0) 0%, rgba(33, 37, 41, 0.9) 25%, rgba(33, 37, 41, 0.9) 75%, rgba(33, 37, 41, 0) 100%);
background: -moz-linear-gradient(left, rgba(33, 37, 41, 0) 0%, rgba(33, 37, 41, 0.9) 25%, rgba(33, 37, 41, 0.9) 75%, rgba(33, 37, 41, 0) 100%);
background: -ms-linear-gradient(left, rgba(33, 37, 41, 0) 0%, rgba(33, 37, 41, 0.9) 25%, rgba(33, 37, 41, 0.9) 75%, rgba(33, 37, 41, 0) 100%);
background: -o-linear-gradient(left, rgba(33, 37, 41, 0) 0%, rgba(33, 37, 41, 0.9) 25%, rgba(33, 37, 41, 0.9) 75%, rgba(33, 37, 41, 0) 100%);
background: linear-gradient(to right, rgba(33, 37, 41, 0) 0%, rgba(33, 37, 41, 0.9) 25%, rgba(33, 37, 41, 0.9) 75%, rgba(33, 37, 41, 0) 100%); }
.dataTables_wrapper .dataTables_length,
.dataTables_wrapper .dataTables_filter,
.dataTables_wrapper .dataTables_info,
.dataTables_wrapper .dataTables_processing,
.dataTables_wrapper .dataTables_paginate {
color: #333333; }
.dataTables_wrapper .dataTables_scroll {
clear: both; }
.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody {
*margin-top: -1px;
-webkit-overflow-scrolling: touch; }
.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody > table > thead > tr > th, .dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody > table > thead > tr > td, .dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody > table > tbody > tr > th, .dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody > table > tbody > tr > td {
vertical-align: middle; }
.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody > table > thead > tr > th > div.dataTables_sizing,
.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody > table > thead > tr > td > div.dataTables_sizing, .dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody > table > tbody > tr > th > div.dataTables_sizing,
.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody > table > tbody > tr > td > div.dataTables_sizing {
height: 0;
overflow: hidden;
margin: 0 !important;
padding: 0 !important; }
.dataTables_wrapper.no-footer .dataTables_scrollBody {
border-bottom: 1px solid white; }
.dataTables_wrapper.no-footer div.dataTables_scrollHead table.dataTable,
.dataTables_wrapper.no-footer div.dataTables_scrollBody > table {
border-bottom: none; }
.dataTables_wrapper:after {
visibility: hidden;
display: block;
content: "";
clear: both;
height: 0; }
@media screen and (max-width: 767px) {
.dataTables_wrapper .dataTables_info,
.dataTables_wrapper .dataTables_paginate {
float: none;
text-align: center; }
.dataTables_wrapper .dataTables_paginate {
margin-top: 0.5em; } }
@media screen and (max-width: 640px) {
.dataTables_wrapper .dataTables_length,
.dataTables_wrapper .dataTables_filter {
float: none;
text-align: center; }
.dataTables_wrapper .dataTables_filter {
margin-top: 0.5em; } }

View File

@@ -0,0 +1,86 @@
<div class="sendWrapper">
<div class="card-panel">
<form class="col s12">
<div class="row">
<div class="input-field col s6">
<select id="sendFromAddress">
<option value="" disabled selected>Choose your address</option>
{{#addressData}}
<option value="{{address}}">{{name}} - {{address}}</option>
{{/addressData}}
</select>
<label for="sendFromAddress">From address:</label>
</div>
<div class="input-field col s6">
<input id="sendToAddress" placeholder="recipient address" type="text">
<label for="sendToAddress" class="active">To address:</label>
</div>
</div>
<div class="row">
<div class="input-field col s6">
<input id="sendAmmount" placeholder="0" type="number" value="0">
<label for="sendAmmount" class="active">Ammount:</label>
</div>
<div class="input-field col s6">
<div class="input-field col s12">
<span id="sendMaxAmmount">0</span><span>ETHO</span>
<button type="button" class="btn btn-etho" id="btnSendAll">ALL</button>
</div>
</div>
</div>
<div class="row">
<div class="input-field col s12">
<button type="button" class="btn btn-etho" id="btnSendTransaction">Send</button>
</div>
</div>
</form>
</div>
<div id="cardTransactionsForAddress" class="card-panel" style="display: none;">
<table id="tableTransactionsForAddress" class="display" style="width:100%">
<thead>
<tr>
<th>Block</th>
<th>Timestamp</th>
<th>To</th>
<th>Value</th>
</tr>
</thead>
<tfoot>
<tr>
<th>Block</th>
<th>Timestamp</th>
<th>To</th>
<th>Value</th>
</tr>
</tfoot>
</table>
</div>
</div>
<!-- The modal for wallet password -->
<div id="dlgSendWalletPassword" class="modalDialog" data-iziModal-title="Wallet Password" data-iziModal-subtitle="To send from this wallet, please enter the wallet password" data-iziModal-icon="icon-home">
<div class="modalBodyPassword">
<div class="form-group sendTXInfo">
<i class="fas fa-wallet"></i>
<label>From address:</label><label id="fromAddressInfo"></label>
</div>
<div class="form-group sendTXInfo">
<i class="fas fa-wallet"></i>
<label>To address:</label><label id="toAddressInfo"></label>
</div>
<div class="form-group sendTXInfo">
<i class="fas fa-dollar-sign"></i>
<label>Value to send:</label><label id="valueToSendInfo"></label><label class="currencyTicker">ETHO</label>
</div>
<div class="form-group sendTXInfo">
<i class="fas fa-dollar-sign"></i>
<label>Fee to pay:</label><label id="feeToPayInfo"></label><label class="currencyTicker">ETHO</label>
</div>
<hr class="sendTXdivider">
<div class="form-group sendTXPass">
<label for="usr">Type Password:</label>
<input type="password" class="form-control" id="walletPassword">
</div>
<button type="button" class="btn btn-etho btn-dialog-confirm" id="btnSendWalletPasswordConfirm">Confirm</button>
</div>
</div>

View File

@@ -0,0 +1,17 @@
<div class="settingsWrapper">
<div class="card-panel">
<div class="cleanWrapper">
<button type="button" class="btn btn-etho btnSettingsClean" id="btnSettingsCleanTransactions"><i class="far fa-trash-alt"></i></button>
<span class="cleanText">Clean transactions data</span>
</div>
<div class="cleanWrapper">
<button type="button" class="btn btn-etho btnSettingsClean" id="btnSettingsCleanWallets"><i class="far fa-trash-alt"></i></button>
<span class="cleanText">Clean wallets data</span>
</div>
<div class="cleanWrapper">
<button type="button" class="btn btn-etho btnSettingsClean" id="btnSettingsCleanBlockchain"><i class="far fa-trash-alt"></i></button>
<span class="cleanText">Clean blockchain data</span>
</div>
</div>
</div>
1

View File

@@ -0,0 +1,33 @@
<div id="transactionsWrapper">
<table id="tableTransactionsForAll" class="display tableTransactions" style="width:100%">
<thead>
<tr>
<th>Block</th>
<th>Timestamp</th>
<th>From</th>
<th>To</th>
<th>Value</th>
</tr>
</thead>
<tfoot>
<tr>
<th>Block</th>
<th>Timestamp</th>
<th>From</th>
<th>To</th>
<th>Value</th>
</tr>
</tfoot>
</table>
</div>
<div class="loadingOverlay" id="loadingTransactionsOverlay">
<div class="loadingWrapper">
<div class="loadingTextTransactions">Transaction are loading, please wait...</div>
<div class='spinner'>
<div class='bounce bounce1'></div>
<div class='bounce bounce2'></div>
<div class='bounce bounce3'></div>
</div>
<div>
</div>

View File

@@ -0,0 +1,59 @@
<div id="walletsToolbar">
<button type="button" class="btn btn-etho" id="btnNewAddress">New Address</button>
<div id="sumBalance">Total ETHO: <span class="sumBalance">{{sumBalance}}</span></div>
</div>
<div id="addressList" class="{{#if addressData.length}}walletsWrapper{{else}}noWalletsWrapper{{/if}}">
{{#if addressData.length}}
<table class="bordered" id="addressTable">
<thead>
<tr>
<th scope="col"></th>
<th scope="col">Name</th>
<th scope="col">Address</th>
<th scope="col">Balance</th>
</tr>
</thead>
<tbody>
{{#addressData}}
<tr>
<th scope="row" class="colEdit"><button type="button" class="btn btn-etho btnChangWalletName" data-wallet="{{address}}" data-name="{{name}}">Edit</button></th>
<td>{{name}}</td>
<td>{{address}}</td>
<td>{{balance}}</td>
</tr>
{{/addressData}}
</tbody>
</table>
{{else}}
<div id="noWalletsPresent">You don't have any wallets, please import them, or create a new one</div>
{{/if}}
</div>
<div id="transactionList">
</div>
<!-- The modal for new wallet -->
<div id="dlgCreateWalletPassword" class="modalDialog" data-iziModal-title="Wallet Password" data-iziModal-subtitle="Make sure to write it down or remember it!!!" data-iziModal-icon="icon-home">
<div class="modalBodyPassword">
<div class="form-group">
<label for="usr">Type Password:</label>
<input type="password" class="form-control" id="walletPasswordFirst">
</div>
<div class="form-group">
<label for="pwd">Confirm Password:</label>
<input type="password" class="form-control" id="walletPasswordSecond">
</div>
<button type="button" class="btn btn-etho btn-dialog-confirm" id="btnCreateWalletConfirm">Confirm</button>
</div>
</div>
</div>
<!-- The modal to change wallet name -->
<div id="dlgChangeWalletName" class="modalDialog" data-iziModal-title="Wallet Name" data-iziModal-subtitle="Enter the name for this address" data-iziModal-icon="icon-home">
<div class="modalBodyPassword">
<div class="form-group">
<label for="usr">Type Name:</label>
<input type="text" class="form-control" id="inputWalletName">
</div>
<button type="button" class="btn btn-etho btn-dialog-confirm" id="btnChangeWalletNameConfirm">Confirm</button>
</div>
</div>

BIN
bin/geth.exe Normal file

Binary file not shown.

BIN
build/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

96
index.html Normal file
View File

@@ -0,0 +1,96 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Ether-1 Desktop Wallet</title>
<link rel="stylesheet" href="./assets/styles/materialize.min.css">
<link rel="stylesheet" href="./assets/styles/datatables.min.css">
<link rel="stylesheet" href="./assets/styles/iziModal.min.css">
<link rel="stylesheet" href="./assets/styles/transactions.css">
<link rel="stylesheet" href="./assets/styles/please-wait.css">
<link rel="stylesheet" href="./assets/styles/all.min.css">
<link rel="stylesheet" href="./assets/styles/spinner.css">
<link rel="stylesheet" href="./assets/styles/buttons.css">
<link rel="stylesheet" href="./assets/styles/style.css">
<link rel="stylesheet" href="./assets/styles/forms.css">
<!-- Insert this line above script imports -->
<script>if (typeof module === 'object') {window.module = module; module = undefined;}</script>
<!-- normal script imports etc -->
<script src="https://cdn.jsdelivr.net/gh/ethereum/web3.js@1.0.0-beta.36/dist/web3.min.js" integrity="sha256-nWBTbvxhJgjslRyuAKJHK+XcZPlCnmIAAMixz6EefVk=" crossorigin="anonymous"></script>
<script src="./assets/scripts/jquery.min.js"></script>
<script src="./assets/scripts/handlebars.js"></script>
<script src="./assets/scripts/materialize.min.js"></script>
<script src="./assets/scripts/datatables.min.js"></script>
<script src="./assets/scripts/iziModal.min.js"></script>
<script src="./assets/scripts/progressbar.min.js"></script>
<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/jquery.floatThead.min.js"></script>
<script src="./assets/scripts/all.min.js"></script>
<!-- Insert this line after script imports -->
<script>if (window.module) module = window.module;</script>
</head>
<body>
<script>
var loading_screen = pleaseWait({
logo: "assets/images/logo.png",
backgroundColor: '#000000',
loadingHtml: "<div class='spinner'><div class='bounce bounce1'></div><div class='bounce bounce2'></div><div class='bounce bounce3'></div></div><div class='loadingText'>Starting the node and loading app, please wait...</div>"
});
$(document).on("onGethReady", function() {
loading_screen.finish();
});
$(window).on("beforeunload", function() {
EthoBlockchain.closeConnection();
})
</script>
<div class="inner">
<!-- The sidebar -->
<div class="sidebar">
<div class="sidebarIconWrapper iconSelected" id="mainNavBtnWalletsWrapper">
<a class="sidebarIcon" id="mainNavBtnWallets" href="#"><i class="fas fa-wallet fa-2x"></i></a>
</div>
<div class="sidebarIconWrapper" id="mainNavBtnSendWrapper">
<a class="sidebarIcon" id="mainNavBtnSend" href="#"><i class="fas fas fa-share-square fa-2x"></i></a>
</div>
<div class="sidebarIconWrapper" id="mainNavBtnTransactionsWrapper">
<a class="sidebarIcon" id="mainNavBtnTransactions" href="#"><i class="fas fa-exchange-alt fa-2x"></i></a>
</div>
<div class="sidebarIconWrapper" id="mainNavBtnSettingsWrapper">
<a class="sidebarIcon" id="mainNavBtnSettings" href="#"><i class="fas fa-cog fa-2x"></i></a>
</div>
<div id="peerCount">
Peer Count: 0
</div>
</div>
<div id="mainContent"></div>
<div id="syncProgress"></div>
<script>
// You can also require other files to run in this process
require('./renderer/send.js');
require('./renderer/utils.js');
require('./renderer/maingui.js');
require('./renderer/syncing.js');
require('./renderer/settings.js');
require('./renderer/wallets.js');
require('./renderer/blockchain.js');
require('./renderer/transactions.js');
</script>
</div>
<!-- The modal for general error -->
<div id="dlgGeneralError" class="modalDialog" data-iziModal-title="Application Error" data-iziModal-subtitle="Something went wrong, don't kill the fish..." data-iziModal-icon="icon-home">
<div class="modalBodyPassword">
<div class="form-group">
<span id="txtGeneralError"></span>
</div>
<button type="button" class="btn btn-etho btn-dialog-confirm" id="btnGeneralErrorOK">OK</button>
</div>
</div>
</body>
</html>

67
main.js Normal file
View File

@@ -0,0 +1,67 @@
// Modules to control application life and create native browser window
const {app, ipcMain, BrowserWindow} = require('electron');
const path = require('path');
const fs = require('fs');
// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is garbage collected.
let mainWindow
function createWindow () {
// Create the browser window.
mainWindow = new BrowserWindow({
width: 1200,
height: 800,
minWidth: 1000,
minHeight: 800,
backgroundColor: "#000000"
});
// and load the index.html of the app.
mainWindow.loadFile('index.html');
EthoGeth.startGeth();
// Open the DevTools.
// mainWindow.webContents.openDevTools()
// Emitted when the window is closed.
mainWindow.on('closed', function () {
// Dereference the window object, usually you would store windows
// in an array if your app supports multi windows, this is the time
// when you should delete the corresponding element.
mainWindow = null
})
}
// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.on('ready', createWindow)
// Quit when all windows are closed.
app.on('window-all-closed', function () {
// On OS X it is common for applications and their menu bar
// to stay active until the user quits explicitly with Cmd + Q
if (process.platform !== 'darwin') {
EthoGeth.stopGeth();
app.quit()
}
})
app.on('activate', function () {
// On OS X it's common to re-create a window in the app when the
// dock icon is clicked and there are no other windows open.
if (mainWindow === null) {
createWindow()
}
})
// In this file you can include the rest of your app's specific main process
// code. You can also put them in separate files and require them here.
// listen for request to get template
ipcMain.on('getTemplateContent', (event, arg) => {
event.returnValue = fs.readFileSync(path.join(app.getAppPath(), "assets/templates/") + arg, 'utf8');
});
require('./modules/geth.js');
require('./modules/database.js');

63
modules/database.js Normal file
View File

@@ -0,0 +1,63 @@
const {app, ipcMain} = require('electron');
const storage = require('electron-storage');
const datastore = require('nedb');
const path = require('path');
const dbPath = path.join(app.getPath('userData'), 'storage.db');
const db = new datastore({ filename: dbPath });
db.loadDatabase(function (err) {
// Now commands will be executed
});
db.ensureIndex({ fieldName: 'block' }, function (err) {
// If there was an error, err is not null
});
ipcMain.on('storeTransaction', (event, arg) => {
db.find({ block: arg.block, fromaddr: arg.fromaddr, toaddr: arg.toaddr }, function (err, docs) {
if (docs.length == 0) {
db.insert(arg, function (err, newDoc) {
// do nothing
});
}
});
});
ipcMain.on('getTransactions', (event, arg) => {
db.find({}).sort({ block: 1 }).exec(function (err, docs) {
ResultData = [];
for (i = 0; i < 500; i++) {
ResultData.push([
docs[i].block,
docs[i].timestamp,
docs[i].fromaddr,
docs[i].toaddr,
docs[i].value
]);
}
// return the transactions data
event.returnValue = ResultData;
});
});
ipcMain.on('getJSONFile', (event, arg) => {
storage.get(arg, (err, data) => {
if (err) {
event.returnValue = null;
} else {
event.returnValue = data;
}
});
});
ipcMain.on('setJSONFile', (event, arg) => {
storage.set(arg.file, arg.data, (err) => {
if (err) {
event.returnValue = { success: false, error: err };
} else {
event.returnValue = { success: true, error: null };
}
});
});

35
modules/geth.js Normal file
View File

@@ -0,0 +1,35 @@
const {app, dialog, BrowserWindow} = require('electron')
const child_process = require('child_process');
const path = require('path');
class Geth {
constructor() {
this.gethProcess = null;
}
startGeth() {
// get the path of get and execute the child process
try {
const gethPath = path.join(app.getPath('userData'), '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!");
});
this.gethProcess.stderr.on('data', function(data) {
console.log(data.toString());
});
this.gethProcess.stdout.on('data', function(data) {
console.log(data.toString());
});
} catch (e) {
dialog.showErrorBox("Error starting application", e);
app.quit();
}
}
stopGeth() {
this.gethProcess.kill('SIGINT');
}
}
EthoGeth = new Geth();

38
package.json Normal file
View File

@@ -0,0 +1,38 @@
{
"name": "Ether1Wallet",
"version": "0.1.0",
"description": "Desktop wallet for Ether1 currency",
"main": "main.js",
"scripts": {
"start": "electron .",
"postinstall": "electron-builder install-app-deps",
"pack": "build --dir",
"dist": "build"
},
"build": {
"appId": "Ether1DesktopWallet",
"win": {
"icon": "build/icon.png",
"target": "portable"
}
},
"repository": "https://github.com/electron/electron-quick-start",
"keywords": [
"Electron",
"quick",
"start",
"tutorial",
"demo"
],
"author": "Ether1",
"license": "CC0-1.0",
"dependencies": {
"electron-storage": "^1.0.7",
"handlebars": "^4.0.12",
"nedb": "^1.8.0"
},
"devDependencies": {
"electron": "^3.0.12",
"electron-builder": "^20.38.3"
}
}

183
renderer/blockchain.js Normal file
View File

@@ -0,0 +1,183 @@
// In renderer process (web page).
const {ipcRenderer} = require('electron');
class Blockchain {
constructor() {}
getBlock(blockToGet, includeData, clbError, clbSuccess) {
web3Local.eth.getBlock(blockToGet, includeData, function(error, block) {
if (error) {
clbError(error);
} else {
clbSuccess(block);
}
});
}
getAccounts(clbError, clbSuccess) {
web3Local.eth.getAccounts(function(err, res) {
if (err) {
clbError(err);
} else {
clbSuccess(res);
}
});
}
isAddress(address) {
return web3Local.utils.isAddress(address);
}
getTranasctionFee(fromAddress, toAddress, value, clbError, clbSuccess) {
web3Local.eth.getTransactionCount(fromAddress, function( error, result ) {
if (error) {
clbError(error);
} else {
var amountToSend = web3Local.utils.toWei(value, "ether"); //convert to wei value
var RawTransaction = {
from: fromAddress,
to: toAddress,
value: amountToSend,
nonce: result
};
web3Local.eth.estimateGas(RawTransaction, function( error, result ) {
if (error) {
clbError(error);
} else {
var usedGas = result;
web3Local.eth.getGasPrice(function( error, result ) {
if (error) {
clbError(error);
} else {
clbSuccess(result * usedGas);
}
});
}
});
}
});
}
prepareTransaction(password, fromAddress, toAddress, value, clbError, clbSuccess) {
web3Local.eth.personal.unlockAccount(fromAddress, password, function( error, result ) {
if (error) {
clbError("Wrong password for the selected address!");
} else
{
web3Local.eth.getTransactionCount(fromAddress, function( error, result ) {
if (error) {
clbError(error);
} else {
var amountToSend = web3Local.utils.toWei(value, "ether"); //convert to wei value
var RawTransaction = {
from: fromAddress,
to: toAddress,
value: amountToSend,
nonce: result
};
web3Local.eth.estimateGas(RawTransaction, function( error, result ) {
if (error) {
clbError(error);
} else {
RawTransaction.gas = result;
web3Local.eth.getGasPrice(function( error, result ) {
if (error) {
clbError(error);
} else {
RawTransaction.gasPrice = result;
web3Local.eth.signTransaction(RawTransaction, fromAddress, function( error, result ) {
if (error) {
clbError(error);
} else {
clbSuccess(result);
}
});
}
});
}
});
}
});
}
});
}
sendTransaction(rawTransaction, clbError, clbSuccess) {
web3Local.eth.sendSignedTransaction(rawTransaction, function( error, result ) {
if (error) {
clbError(error);
} else {
clbSuccess(result);
}
});
}
getAccountsData(clbError, clbSuccess) {
var rendererData = {};
rendererData.sumBalance = 0;
rendererData.addressData = [];
var wallets = ipcRenderer.sendSync('getJSONFile', 'wallets.json');
var counter = 0;
web3Local.eth.getAccounts(function(err, res) {
if (err) {
clbError(err);
} else {
for (var i = 0; i < res.length; i++) {
var walletName = vsprintf("Account %d", [i + 1]);
if (wallets) {
walletName = wallets.names[res[i]] || walletName;
}
var addressInfo = {};
addressInfo.balance = 0;
addressInfo.address = res[i];
addressInfo.name = walletName;
rendererData.addressData.push(addressInfo);
}
if (rendererData.addressData.length > 0) {
updateBalance(counter);
} else {
clbSuccess(rendererData);
}
}
});
function updateBalance(index)
{
web3Local.eth.getBalance(rendererData.addressData[index].address, function(error, balance) {
rendererData.addressData[index].balance = parseFloat(web3Local.utils.fromWei(balance, 'ether')).toFixed(2);
rendererData.sumBalance = rendererData.sumBalance + parseFloat(web3Local.utils.fromWei(balance, 'ether'));
if (counter < rendererData.addressData.length - 1) {
counter++;
updateBalance(counter);
} else {
rendererData.sumBalance = parseFloat(rendererData.sumBalance).toFixed(2);
clbSuccess(rendererData);
}
});
}
}
createNewAccount(password, clbError, clbSuccess) {
web3Local.eth.personal.newAccount(password, function(error, account) {
if (error) {
clbError(error);
} else {
clbSuccess(account);
}
});
}
closeConnection() {
web3Local.currentProvider.connection.close();
}
}
// create new blockchain variable
EthoBlockchain = new Blockchain();

74
renderer/maingui.js Normal file
View File

@@ -0,0 +1,74 @@
// In renderer process (web page).
const {ipcRenderer} = require('electron');
class MainGUI {
constructor() {
this.appState = "account";
}
changeAppState(newState) {
this.appState = newState;
$(".sidebarIconWrapper").removeClass("iconSelected");
switch(this.appState) {
case "account":
$("#mainNavBtnWalletsWrapper").addClass("iconSelected");
break;
case "send":
$("#mainNavBtnSendWrapper").addClass("iconSelected");
break;
case "transactions":
$("#mainNavBtnTransactionsWrapper").addClass("iconSelected");
break;
case "settings":
$("#mainNavBtnSettingsWrapper").addClass("iconSelected");
break;
default: // do nothing for now
}
}
getAppState() {
return this.appState;
}
showGeneralError(errorText) {
$("#txtGeneralError").html(errorText);
// create and open the dialog
$("#dlgGeneralError").iziModal();
$('#dlgGeneralError').iziModal('open');
$("#btnGeneralErrorOK").click(function() {
$('#dlgGeneralError').iziModal('close');
});
}
renderTemplate(template, data) {
var template = Handlebars.compile(ipcRenderer.sendSync('getTemplateContent', template));
$("#mainContent").empty();
$("#mainContent").html(template(data));
}
}
$("#mainNavBtnTransactions").click(function() {
EthoMainGUI.changeAppState("transactions");
EthoTransactions.renderTransactions();
});
$("#mainNavBtnSend").click(function() {
EthoMainGUI.changeAppState("send");
EthoSend.renderSendState();
});
$("#mainNavBtnWallets").click(function() {
EthoMainGUI.changeAppState("account");
EthoWallets.renderWalletsState();
});
$("#mainNavBtnSettings").click(function() {
EthoMainGUI.changeAppState("settings");
EthoSettings.renderSettingsState();
});
EthoMainGUI = new MainGUI();

174
renderer/send.js Normal file
View File

@@ -0,0 +1,174 @@
// In renderer process (web page).
const {ipcRenderer} = require('electron');
class SendTransaction {
constructor() {}
renderSendState() {
EthoBlockchain.getAccountsData(
function(error) {
EthoMainGUI.showGeneralError(error);
},
function(data) {
EthoMainGUI.renderTemplate("send.html", data);
$(document).trigger("render_send");
}
);
}
validateSendForm() {
if (EthoMainGUI.getAppState() == "send") {
if (!$("#sendFromAddress").val()) {
EthoMainGUI.showGeneralError("Sender address must be specified!");
return false;
}
if (!EthoBlockchain.isAddress($("#sendFromAddress").val())) {
EthoMainGUI.showGeneralError("Sender address must be a valid address!");
return false;
}
if (!$("#sendToAddress").val()) {
EthoMainGUI.showGeneralError("Recipient address must be specified!");
return false;
}
if (!EthoBlockchain.isAddress($("#sendToAddress").val())) {
EthoMainGUI.showGeneralError("Recipient address must be a valid address!");
return false;
}
if (Number($("#sendAmmount").val()) <= 0) {
EthoMainGUI.showGeneralError("Send ammount must be greater then zero!");
return false;
}
return true;
} else {
return false;
}
}
resetSendForm() {
if (EthoMainGUI.getAppState() == "send") {
$("#sendFromAddress").val("");
$("#sendToAddress").val("");
$("#sendAmmount").val(0);
}
}
}
$(document).on("render_send", function() {
$('select').formSelect( {classes: "fromAddressSelect"});
$("#sendFromAddress").on("change", function() {
web3Local.eth.getBalance(this.value, function(error, balance) {
$("#sendMaxAmmount").html(parseFloat(web3Local.utils.fromWei(balance, 'ether')));
});
/*
// list all transactions for this address
if (this.value) {
$("#cardTransactionsForAddress").css("display", "block");
setTimeout(() => {
// render the transactions
$('#tableTransactionsForAddress').DataTable({
"paging": false,
"scrollY": "calc(100vh - 115px)",
"responsive": true,
"processing": true,
"order": [[ 0, "desc" ]],
"data": ipcRenderer.sendSync('getTransactions', this.value),
"columnDefs": [
{
"className": "transactionsBlockNum",
"targets": 0
},
{
"targets": 1,
"render": function ( data, type, row ) {
return moment(data).format("MMM Do YYYY");
}
},
{
"targets": 4,
"render": function ( data, type, row ) {
return parseFloat(web3Local.utils.fromWei(EthoUtils.toFixed(parseFloat(data)).toString(), 'ether')).toFixed(2);
}
}
],
"drawCallback": function( settings ) {
$("#loadingTransactionsOverlay").css("display", "none");
}
});
}, 200);
} else {
$("#cardTransactionsForAddress").css("display", "none");
}
*/
});
$("#btnSendAll").off('click').on('click', function() {
$("#sendAmmount").focus();
$("#sendAmmount").val($("#sendMaxAmmount").html());
});
$("#btnSendTransaction").off('click').on('click', function() {
if (EthoSend.validateSendForm()) {
EthoBlockchain.getTranasctionFee($("#sendFromAddress").val(), $("#sendToAddress").val(), $("#sendAmmount").val(),
function(error) {
EthoMainGUI.showGeneralError(error);
},
function(data) {
$("#dlgSendWalletPassword").iziModal();
$("#walletPassword").val("");
$("#fromAddressInfo").html($("#sendFromAddress").val());
$("#toAddressInfo").html($("#sendToAddress").val());
$("#valueToSendInfo").html($("#sendAmmount").val());
$("#feeToPayInfo").html(parseFloat(web3Local.utils.fromWei(data.toString(), 'ether')));
$('#dlgSendWalletPassword').iziModal('open');
function doSendTransaction() {
$('#dlgSendWalletPassword').iziModal('close');
EthoBlockchain.prepareTransaction(
$("#walletPassword").val(),
$("#sendFromAddress").val(),
$("#sendToAddress").val(),
$("#sendAmmount").val(),
function(error) {
EthoMainGUI.showGeneralError(error);
},
function(data) {
EthoBlockchain.sendTransaction(data.raw,
function(error) {
EthoMainGUI.showGeneralError(error);
},
function(data) {
EthoSend.resetSendForm();
// use the transaction hash
}
);
}
);
}
$("#btnSendWalletPasswordConfirm").off('click').on('click', function() {
doSendTransaction();
});
$("#dlgSendWalletPassword").off('keypress').on('keypress', function(e) {
if(e.which == 13) {
doSendTransaction();
}
});
}
);
}
});
});
// create new account variable
EthoSend = new SendTransaction();

28
renderer/settings.js Normal file
View File

@@ -0,0 +1,28 @@
// In renderer process (web page).
const {ipcRenderer} = require('electron');
class Settings {
constructor() {}
renderSettingsState() {
EthoMainGUI.renderTemplate("settings.html", {});
$(document).trigger("render_settings");
}
}
$(document).on("render_settings", function() {
$("#btnSettingsCleanTransactions").off('click').on('click', function() {
EthoMainGUI.showGeneralError("Not implemented yet!");
});
$("#btnSettingsCleanWallets").off('click').on('click', function() {
EthoMainGUI.showGeneralError("Not implemented yet!");
});
$("#btnSettingsCleanBlockchain").off('click').on('click', function() {
EthoMainGUI.showGeneralError("Not implemented yet!");
});
});
// create new account variable
EthoSettings = new Settings();

109
renderer/syncing.js Normal file
View File

@@ -0,0 +1,109 @@
// In renderer process (web page).
const {ipcRenderer} = require('electron');
// Set the provider you want from Web3.providers
SyncProgress = new ProgressBar.Line('#syncProgress',
{
strokeWidth: 6,
easing: 'easeInOut',
duration: 1400,
color: "#7A1336",
trailColor: '#eee',
trailWidth: 1,
text: {
style: {
color: '#bbb',
position: "absolute",
left: "50%",
top: "-1px",
transform: "translateX(-50%)",
fontSize: "0.9em",
LineHeight: "24px",
padding: 0
},
autoStyleContainer: false
},
from: {color: '#FFEA82'},
to: {color: '#ED6A5A'}
});
// set initial value for the progress text
SyncProgress.setText("initializing, please wait...");
var peerCountInterval = setInterval(function()
{
web3Local.eth.net.getPeerCount(function(error, count) {
$("#peerCount").html(vsprintf("Peer Count: %d", [count]));
});
}, 5000);
function StartSyncProcess() {
var alreadyCatchedUp = false;
function keepTheNodeInSync(interval) {
var nodeSyncInterval = setInterval(function()
{
web3Local.eth.isSyncing(function(error, sync)
{
if(!error) {
if(sync == true) {
console.log("start the sync");
} else if(sync) {
SyncProgress.animate(sync.currentBlock / sync.highestBlock);
SyncProgress.setText(vsprintf('%d/%d (%d%%)', [sync.currentBlock, sync.highestBlock, Math.round(sync.currentBlock / sync.highestBlock * 100)]));
} else {
web3Local.eth.getBlock("latest", function(error, localBlock) {
if (localBlock.number > 0) {
web3Remote.eth.getBlock("latest", function(error, remoteBlock) {
if (!EthoTransactions.getIsSyncing()) {
SyncProgress.animate(localBlock.number / remoteBlock.number);
SyncProgress.setText(vsprintf('%d/%d (%d%%)', [localBlock.number, remoteBlock.number, Math.round(localBlock.number / remoteBlock.number * 100)]));
}
if (remoteBlock.number == localBlock.number) {
if (alreadyCatchedUp == false)
{
// clear the repeat interval and render wallets
$(document).trigger("onNewAccountTransaction");
clearInterval(nodeSyncInterval);
alreadyCatchedUp = true;
// sync all the transactions to the current block
EthoTransactions.syncTransactionsForAllAddresses(localBlock.number);
$(document).trigger("onSyncInterval");
// restart with less intensity
keepTheNodeInSync(10000);
}
}
});
}
});
}
}
});
}, interval);
}
// initial fast syncing
keepTheNodeInSync(2000);
}
var InitWeb3 = setInterval(function()
{
try {
web3Local = new Web3(new Web3.providers.WebsocketProvider('ws://localhost:8546'));
web3Remote = new Web3(new Web3.providers.HttpProvider("https://rpc.ether1.org"));
web3Local.eth.net.isListening(function(error, success) {
if (!error) {
$(document).trigger("onGethReady");
clearInterval(InitWeb3);
StartSyncProcess();
}
});
}
catch(err) {
console.log(err);
}
}, 2000);

188
renderer/transactions.js Normal file
View File

@@ -0,0 +1,188 @@
// In renderer process (web page).
const {ipcRenderer} = require('electron');
class Transactions {
constructor() {
this.isSyncing = false;
}
setIsSyncing(value) {
this.isSyncing = value;
}
getIsSyncing() {
return this.isSyncing;
}
syncTransactionsForSingleAddress(addressList, counters, lastBlock, counter) {
if (counter < addressList.length - 1) {
SyncProgress.setText(vsprintf("Syncing address transactions %d/%d, please wait...", [counter, addressList.length]));
var startBlock = parseInt(counters.transactions) || 0;
var params = vsprintf('?address=%s&fromBlock=%d&toBlock=%d', [addressList[counter], startBlock, lastBlock]);
$.getJSON("https://richlist.ether1.org/transactions_list.php" + params, function( result ) {
result.data.forEach(element => {
var Transaction = {
block: element.block,
fromaddr: element.fromaddr,
timestamp: element.timestamp,
toaddr: element.toaddr,
value: element.value
}
ipcRenderer.send('storeTransaction', Transaction);
});
// update the counter and store it back to file system
counters.transactions = lastBlock;
ipcRenderer.sendSync('setJSONFile',
{
file: 'counters.json',
data: counters
});
// call the transaction sync for the next address
EthoTransactions.syncTransactionsForSingleAddress(addressList, counters, lastBlock, counter + 1);
});
} else {
SyncProgress.setText("Syncing transactions is complete.");
EthoTransactions.setIsSyncing(false);
}
}
syncTransactionsForAllAddresses(lastBlock) {
var counters = ipcRenderer.sendSync('getJSONFile', 'counters.json');
var counter = 0;
if (counters == null) {
counters = {};
}
EthoBlockchain.getAccounts(
function(error) {
EthoMainGUI.showGeneralError(error);
},
function(data) {
EthoTransactions.setIsSyncing(true);
EthoTransactions.syncTransactionsForSingleAddress(data, counters, lastBlock, counter);
}
);
}
renderTransactions() {
EthoMainGUI.renderTemplate("transactions.html", {});
$(document).trigger("render_transactions");
// show the loading overlay for transactions
$("#loadingTransactionsOverlay").css("display", "block");
setTimeout(() => {
// render the transactions
$('#tableTransactionsForAll').DataTable({
"paging": false,
"scrollY": "calc(100vh - 115px)",
"responsive": true,
"processing": true,
"order": [[ 0, "desc" ]],
"data": ipcRenderer.sendSync('getTransactions'),
"columnDefs": [
{
"className": "transactionsBlockNum",
"targets": 0
},
{
"targets": 1,
"render": function ( data, type, row ) {
return moment(data).format("MMM Do YYYY");
}
},
{
"targets": 4,
"render": function ( data, type, row ) {
return parseFloat(web3Local.utils.fromWei(EthoUtils.toFixed(parseFloat(data)).toString(), 'ether')).toFixed(2);
}
}
],
"drawCallback": function( settings ) {
$("#loadingTransactionsOverlay").css("display", "none");
}
});
}, 200);
}
}
// event that tells us that geth is ready and up
$(document).on("onSyncInterval", function() {
var counters = ipcRenderer.sendSync('getJSONFile', 'counters.json');
if (counters == null) {
counters = {};
}
function doSyncRemainingBlocks() {
EthoBlockchain.getBlock("latest", false,
function(error) {
EthoMainGUI.showGeneralError(error);
},
function(block) {
var lastBlock = counters.transactions || 0;
if (lastBlock < block.number) {
function getNextBlockTransactions(blockNumber, maxBlock) {
EthoBlockchain.getBlock(blockNumber, true,
function(error) {
EthoMainGUI.showGeneralError(error);
},
function(data) {
if (blockNumber < maxBlock) {
if (data.transactions) {
data.transactions.forEach(element => {
if ((EthoWallets.getAddressExists(element.from)) || (EthoWallets.getAddressExists(element.to))) {
var Transaction = {
block: element.blockNumber.toString(),
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('+','')
}
// 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)
}
}
);
}
// call initial call of function
getNextBlockTransactions(lastBlock, block.number);
} else {
counters.transactions = block.number;
ipcRenderer.sendSync('setJSONFile',
{
file: 'counters.json',
data: counters
});
setTimeout(function() {
doSyncRemainingBlocks();
}, 10000);
}
}
);
}
// do the initial sync
doSyncRemainingBlocks();
});
// create new transactions variable
EthoTransactions = new Transactions();

23
renderer/utils.js Normal file
View File

@@ -0,0 +1,23 @@
class Utils {
constructor() {}
toFixed(x) {
if (Math.abs(x) < 1.0) {
var e = parseInt(x.toString().split('e-')[1]);
if (e) {
x *= Math.pow(10,e-1);
x = '0.' + (new Array(e)).join('0') + x.toString().substring(2);
}
} else {
var e = parseInt(x.toString().split('+')[1]);
if (e > 20) {
e -= 20;
x /= Math.pow(10,e);
x += (new Array(e+1)).join('0');
}
}
return x;
}
}
EthoUtils = new Utils();

158
renderer/wallets.js Normal file
View File

@@ -0,0 +1,158 @@
// In renderer process (web page).
const {ipcRenderer} = require('electron');
class Wallets {
constructor() {
this.addressList = [];
}
getAddressList() {
return this.addressList;
}
clearAddressList() {
this.addressList = [];
}
getAddressExists(address) {
return this.addressList.indexOf(address.toLowerCase()) > -1;
}
addAddressToList(address) {
this.addressList.push(address.toLowerCase());
}
validateNewAccountForm() {
if (EthoMainGUI.getAppState() == "account") {
if (!$("#walletPasswordFirst").val()) {
EthoMainGUI.showGeneralError("Password cannot be empty!");
return false;
}
if (!$("#walletPasswordSecond").val()) {
EthoMainGUI.showGeneralError("Password cannot be empty!");
return false;
}
if ($("#walletPasswordFirst").val() !== $("#walletPasswordSecond").val()) {
EthoMainGUI.showGeneralError("Passwords do not match!");
return false;
}
return true;
} else {
return false;
}
}
renderWalletsState() {
// clear the list of addresses
EthoWallets.clearAddressList();
EthoBlockchain.getAccountsData(
function(error) {
EthoMainGUI.showGeneralError(error);
},
function(data) {
data.addressData.forEach(element => {
EthoWallets.addAddressToList(element.address);
});
// render the wallets current state
EthoMainGUI.renderTemplate("wallets.html", data);
$(document).trigger("render_wallets");
}
);
}
}
// the event to tell us that the wallets are rendered
$(document).on("render_wallets", function() {
$('#addressTable').floatThead();
$("#btnNewAddress").off('click').on('click', function() {
$("#dlgCreateWalletPassword").iziModal();
$("#walletPasswordFirst").val("");
$("#walletPasswordSecond").val("");
$('#dlgCreateWalletPassword').iziModal('open');
function doCreateNewWallet() {
$('#dlgCreateWalletPassword').iziModal('close');
if (EthoWallets.validateNewAccountForm()) {
EthoBlockchain.createNewAccount($("#walletPasswordFirst").val(),
function(error) {
EthoMainGUI.showGeneralError(error);
},
function(account) {
EthoWallets.addAddressToList(account);
EthoWallets.renderWalletsState();
}
);
}
}
$("#btnCreateWalletConfirm").off('click').on('click', function() {
doCreateNewWallet();
});
$("dlgCreateWalletPassword").off('keypress').on('keypress', function(e) {
if(e.which == 13) {
doCreateNewWallet();
}
});
});
$(".btnChangWalletName").off('click').on('click', function() {
var walletAddress = $(this).attr('data-wallet');
var walletName = $(this).attr('data-name');
$("#dlgChangeWalletName").iziModal();
$("#inputWalletName").val(walletName);
$('#dlgChangeWalletName').iziModal('open');
function doChangeWalletName() {
var wallets = ipcRenderer.sendSync('getJSONFile', 'wallets.json');
if (!wallets) {
wallets = { names: {} };
}
// set the wallet name from the dialog box
wallets.names[walletAddress] = $("#inputWalletName").val();
ipcRenderer.sendSync('setJSONFile',
{
file: 'wallets.json',
data: wallets
});
$('#dlgChangeWalletName').iziModal('close');
EthoWallets.renderWalletsState();
}
$("#btnChangeWalletNameConfirm").off('click').on('click', function() {
doChangeWalletName();
});
$("#dlgChangeWalletName").off('keypress').on('keypress', function(e) {
if(e.which == 13) {
doChangeWalletName();
}
});
});
});
// event that tells us that geth is ready and up
$(document).on("onGethReady", function() {
EthoMainGUI.changeAppState("account");
EthoWallets.renderWalletsState();
});
$(document).on("onNewAccountTransaction", function() {
if (EthoMainGUI.getAppState() == "account") {
EthoWallets.renderWalletsState();
}
});
EthoWallets = new Wallets();