diff --git a/README.md b/README.md
index 93ae9aa..8a1871b 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,7 @@
-# Safehold - Ellcrys Desktop Client
+# SafeHold - Ellcrys Desktop Client
-Safehold is the official desktop client that allows users to join the Ellcrys network, manage their Ellcrys accounts, send and receive
-the native cryptocurrency and more. Safehold provides a beautiful graphic user interface that allows all categories of users experience and interact with the network easily.
+SafeHold is the official desktop client that allows users to join the Ellcrys network, manage their Ellcrys accounts, send and receive
+the native cryptocurrency and more. SafeHold provides a beautiful graphic user interface that allows all categories of users experience and interact with the network easily.
## Installation
@@ -9,7 +9,7 @@ the native cryptocurrency and more. Safehold provides a beautiful graphic user i
## Development
-Safehold is actively being developed by the Ellcrys team. It will continue to mature as the protocol itself gains more features and improvements.
+SafeHold is actively being developed by the Ellcrys team. It will continue to mature as the protocol itself gains more features and improvements.
### Install Dependencies
@@ -21,16 +21,17 @@ npm install
### Embedded ELLD
-Internally, Safehold embeds and execute a copy of [ELLD](https://github.com/ellcrys/elld) (our official CLI network client) to gain access to all features supported by the protocol. During development, you will need to add pre-built binaries into the [binaries](https://github.com/ellcrys/safehold/tree/master/binaries) directory where it will be picked up by the build script and embedded into the build.
+Internally, SafeHold embeds and execute a copy of [ELLD](https://github.com/ellcrys/elld) (our official CLI network client) to gain access to all features supported by the protocol. During development, you will need to add pre-built binaries into the [binaries](https://github.com/ellcrys/safehold/tree/master/binaries) directory where it will be picked up by the build script and embedded into the build.
```bash
-env TARGET=darwin npm run dev
-# TARGET = darwin, linux or windows
+env npm run dev
+#env npm run dev-linux
+#env npm run dev-win
```
## SetUp Development Environment
-You will need to start up two terminals to run the build processes for vue-electron and typescript compilation.
+You will need to start up two terminals for vue-electron and typescript compilation.
**Build vue-electron**
@@ -45,37 +46,11 @@ npm run compile // On terminal 2
```
## Build
-
To create a production build, run the following command:
```bash
+npm install cpy-cli -g
npm run build
-```
-
-## Troubleshooting
-
-```
-sh: cpy: command not found
-```
-
-#### Solution
-
-To solve the above error, you need to install `cpy-cli` globally with nom.
-
-- code `npm install cpy-cli -g`
-
-```
-An unhandled error occurred inside electron-rebuild
- CXX(target) Release/obj.target/bignum/bignum.o
-../bignum.cc:9:10: fatal error: 'openssl/bn.h' file not found
-#include
-```
-
-#### Solution :
-
-(Mac)
-
-```
-brew update
-brew upgrade openssl
+# npm run build-win
+# npm run build-linux
```
diff --git a/build/icons/icon.ico b/build/icons/icon.ico
index ddf3101..c042e4a 100644
Binary files a/build/icons/icon.ico and b/build/icons/icon.ico differ
diff --git a/dev/core/analytics.ts b/dev/core/analytics.ts
new file mode 100644
index 0000000..8e5b31d
--- /dev/null
+++ b/dev/core/analytics.ts
@@ -0,0 +1,37 @@
+import { app } from "electron";
+const ua = require("universal-analytics");
+const uuid = require("uuid/v4");
+const { JSONStorage } = require("node-localstorage");
+const nodeStorage = new JSONStorage(app.getPath("userData"));
+
+// Retrieve the userId value, and if it's not there, assign it a new uuid.
+const userId = nodeStorage.getItem("userId") || uuid();
+nodeStorage.setItem("userId", userId);
+
+const usr = ua("UA-101346362-5", userId);
+
+export default function trackEvent(
+ category: string,
+ action: string,
+ label?: string,
+ value?: any,
+) {
+ usr.event({
+ ec: category,
+ ea: action,
+ el: label,
+ ev: value,
+ }).send();
+}
+
+export function exceptionEvent(desc: string, fatal?: boolean) {
+ usr.exception(desc, fatal);
+}
+
+export function timingEvent(category: string, variable: string, time: number) {
+ usr.timing(category, variable, time);
+}
+
+export function trackPage(path: string) {
+ usr.pageview(path);
+}
diff --git a/dev/core/app.ts b/dev/core/app.ts
index 2154db9..23f9033 100644
--- a/dev/core/app.ts
+++ b/dev/core/app.ts
@@ -30,6 +30,11 @@ import {
} from "../..";
import { kdf } from "../utilities/crypto";
import Account from "./account";
+import trackEvent, {
+ exceptionEvent,
+ timingEvent,
+ trackPage,
+} from "./analytics";
import AverageBlockTime from "./average_block_time";
import { Base } from "./base";
import ChannelCodes from "./channel_codes";
@@ -43,6 +48,9 @@ import Transactions from "./transactions";
import Wallet from "./wallet";
const moment = require("moment");
+(global as any).trackEvent = trackEvent;
+(global as any).trackPage = trackPage;
+
/**
* Returns the file path of the wallet
* @returns {string}
@@ -152,6 +160,8 @@ export default class App extends Base {
win.webContents.send(ChannelCodes.AppLaunched, {
hasWallet: mHasWallet,
});
+
+ trackEvent("App", "run");
}
/**
@@ -195,6 +205,7 @@ export default class App extends Base {
public stop() {
if (this.elld) {
this.elld.stop();
+ trackEvent("App", "stopped");
}
}
@@ -257,6 +268,7 @@ export default class App extends Base {
}
i++;
}
+ trackEvent("Wallet", "restore");
return resolve();
});
}
@@ -373,9 +385,7 @@ export default class App extends Base {
Interval.clear("runUnconfirmedTx");
const dbOps = DBOps.fromDB(this.db);
-
const txCheck = await dbOps.find({ _type: "txPool" });
-
const Spells = this.elld.getSpell();
for (const t of txCheck) {
@@ -505,6 +515,7 @@ export default class App extends Base {
await this.makeWallet(secInfo);
this.kdfPass = secInfo.kdfPass;
if (this.win) {
+ trackEvent("Wallet", "new");
return this.send(
this.win,
ChannelCodes.WalletCreated,
@@ -529,7 +540,11 @@ export default class App extends Base {
if (this.win) {
try {
+ const now = moment().unix();
await this.execELLD();
+ // prettier-ignore
+ timingEvent("App", "elld:started:timed", now - moment().unix() * 1000);
+ trackEvent("App", "elld:started");
Menu.setApplicationMenu(
makeMenu(app, {
afterAuth: true,
@@ -540,14 +555,17 @@ export default class App extends Base {
await this.startBgProcesses();
this.applyPreferences();
this.normalizeWindow();
+ trackEvent("Wallet", "loaded");
// prettier-ignore
return this.send(this.win, ChannelCodes.WalletLoaded, null);
} catch (error) {
+ exceptionEvent("WalletLoad failed");
log.error("Failed to load wallet", error.message);
}
}
} catch (error) {
if (this.win) {
+ log.error("Failed to load wallet", error.message);
return this.sendError(this.win, {
code: ErrCodes.FailedToLoadWallet.code,
msg: ErrCodes.FailedToLoadWallet.msg,
@@ -593,12 +611,14 @@ export default class App extends Base {
// Request to start the miner
ipcMain.on(ChannelCodes.MinerStart, () => {
+ trackEvent("App", "miner:started");
this.preference.set(PrefMinerOn, true);
this.elld.getSpell().miner.start();
});
// Request to stop the miner
ipcMain.on(ChannelCodes.MinerStop, async () => {
+ trackEvent("App", "miner:stopped");
this.preference.set(PrefMinerOn, false);
this.elld.getSpell().miner.stop();
});
@@ -647,6 +667,7 @@ export default class App extends Base {
ChannelCodes.AccountRedirect,
newAcct.getAddress().toString(),
);
+ trackEvent("Account", "created");
} catch (err) {
this.wallet.removeAccount(newAcct);
return this.sendError(this.win, {
@@ -711,6 +732,7 @@ export default class App extends Base {
hash: txHash.id.toString(),
});
+ trackEvent("App:Tx", "sent");
return this.send(
this.win,
ChannelCodes.TransactionSend,
@@ -718,6 +740,8 @@ export default class App extends Base {
);
} catch (error) {
const jsonErr = JSON.parse(error.data);
+ trackEvent("App:Tx", "failed", "error", error.data);
+ exceptionEvent("App:Tx:Send");
return this.send(
this.win,
@@ -955,6 +979,7 @@ export default class App extends Base {
// Request to force account resynchronization
ipcMain.on(ChannelCodes.AccountsReSync, async (e) => {
+ trackEvent("Account", "resync");
await this.transactions.clearCursors();
});
}
@@ -995,6 +1020,7 @@ export default class App extends Base {
.then(resolve)
.catch(reject);
} catch (error) {
+ trackEvent("App", "elld:exec:failed");
if (this.win) {
this.sendError(this.win, {
code: ErrCodes.FailedToLoadElldObject.code,
@@ -1027,6 +1053,7 @@ export default class App extends Base {
});
});
} catch (error) {
+ trackEvent("Wallet", "persist:failed");
return reject(error);
}
});
diff --git a/dev/core/elld.ts b/dev/core/elld.ts
index 9917a83..a22e9c2 100644
--- a/dev/core/elld.ts
+++ b/dev/core/elld.ts
@@ -126,9 +126,8 @@ export default class Elld {
ELLD_RPC_PASSWORD: rpcPass,
};
- console.log(env);
-
- const elld = spawn("elld", args, { shell: true, cwd: this.execPath, env });
+ const command = (process.platform === "win32") ? "elld" : "./elld";
+ const elld = spawn(command, args, { cwd: this.execPath, env });
this.elld = elld;
// hook a callback to stdout
diff --git a/dev/core/menu.ts b/dev/core/menu.ts
index 94f06bd..286a306 100644
--- a/dev/core/menu.ts
+++ b/dev/core/menu.ts
@@ -26,10 +26,6 @@ export function makeBaseMenuTemplate(app: Electron.App, opts: IMenuOpts) {
{ label: "Application data", click: showAppDir(app) },
],
},
- {
- role: "quit",
- click: opts.onQuit,
- },
],
},
{
@@ -135,11 +131,20 @@ interface IMenuOpts {
// prettier-ignore
export const makeMenu = (app: Electron.App, opts: IMenuOpts) => {
const template = makeBaseMenuTemplate(app, opts);
+
if (opts.afterAuth) {
- template.splice(3, 0, { label: "View", submenu: [{ role: "togglefullscreen" }]});
(template[1].submenu as MenuItemConstructorOptions[]).push({ type: "separator" });
+ template.splice(3, 0, { label: "View", submenu: [{ role: "togglefullscreen" }]});
(template[1].submenu as MenuItemConstructorOptions[])
- .push({ label: "New Account", click: opts.onNewAccount });
+ .push({ label: "New Account", click: opts.onNewAccount });
}
+
+ const t = (process.platform === "darwin") ? 1 : 0;
+ (template[t].submenu as MenuItemConstructorOptions[]).push({ type: "separator" });
+ (template[t].submenu as MenuItemConstructorOptions[]).push({
+ role: "quit",
+ click: opts.onQuit,
+ });
+
return Menu.buildFromTemplate(template as any);
};
diff --git a/dev/index.ejs b/dev/index.ejs
index dcdf871..8b1078f 100644
--- a/dev/index.ejs
+++ b/dev/index.ejs
@@ -3,7 +3,7 @@
- Safehold <%= process.env.APP_VERSION %>
+ SafeHold <%= process.env.APP_VERSION %>
<% if (htmlWebpackPlugin.options.nodeModules) { %>
diff --git a/dev/renderer/assets/css/app.scss b/dev/renderer/assets/css/app.scss
index d3b3b4f..c54e1e0 100755
--- a/dev/renderer/assets/css/app.scss
+++ b/dev/renderer/assets/css/app.scss
@@ -309,7 +309,8 @@ body {
#password-strength-bar-list {
clear: both;
list-style: none;
- margin-top: -5px;
+ position: relative;
+ top: -8px;
margin-bottom: 0;
li {
@@ -437,6 +438,9 @@ body {
background: rgba(0, 0, 0, .9);
position: fixed;
z-index: 10000;
+ display: flex;
+ align-items: center;
+ justify-content: center;
.modal-pane.big {
width: 800px;
@@ -444,12 +448,15 @@ body {
margin-top: 100px;
}
+ .modal-pane.h10pct {
+ margin-top: 10%;
+ }
+
.modal-pane {
- width: 500px;
+ width: auto;
height: 300px;
border-radius: 6px;
margin: auto;
- margin-top: 100px;
background: #fbfcff;
position: relative;
@@ -512,7 +519,7 @@ body {
.modal-flex {
display: table;
- margin-top: 30px;
+ // margin-top: 30px;
}
.flex-layout {
@@ -528,6 +535,11 @@ body {
flex: 1;
}
+
+}
+
+.modal-overlay .modal-override-warning {
+ width: 500px;
}
.modal-overlay.loader {
@@ -615,7 +627,7 @@ body {
font-size: 14px;
margin-left: 50px;
margin-top: 20px;
- min-width: 150px;
+ min-width: 120px;
padding: 4px 15px;
cursor: pointer;
padding-top: 5px;
@@ -762,10 +774,9 @@ body {
}
+
#main-split .split-right.index-bg {
height: 100vh;
- background: url("../img/image.png");
- background-size: cover;
.about {
background: #0D1422;
@@ -819,6 +830,24 @@ body {
}
}
+#main-split .split-right .step1 {
+ background: url("../img/image.png");
+ background-size: cover;
+ height: 100vh;
+}
+
+#main-split .split-right .step2 {
+ background: url("../img/image2.png");
+ background-size: cover;
+ height: 100vh;
+}
+
+#main-split .split-right .step3 {
+ background: url("../img/image3.png");
+ background-size: cover;
+ height: 100vh;
+}
+
#main-split .split-right-nav {
border: 0px solid #09f;
border-spacing: 20px 0;
diff --git a/dev/renderer/assets/css/dashboard.scss b/dev/renderer/assets/css/dashboard.scss
index f5c7cda..3246b4e 100644
--- a/dev/renderer/assets/css/dashboard.scss
+++ b/dev/renderer/assets/css/dashboard.scss
@@ -603,7 +603,6 @@ body {
border: 0px solid #09f;
display: table;
margin: auto;
- margin-top: 40px;
width: 661px;
}
@@ -902,6 +901,25 @@ body {
+#learn-safehold .small {
+ display: none;
+ font-size: inherit;
+ font-weight: inherit;
+}
+
+@media screen and (max-width: 1132px) {
+
+ #learn-safehold .large {
+ display: none;
+ }
+
+ #learn-safehold .small {
+ display: block;
+ }
+}
+
+
+
#learn-safehold p,
#learn-safehold hr,
#learn-safehold a {
@@ -958,24 +976,24 @@ body {
@media screen and (max-width: 1480px) {
- #learn-safehold {
- display: none;
- }
+ // #learn-safehold {
+ // display: none;
+ // }
}
@media screen and (max-width: 1366px) {
- #learn-safehold {
- display: block;
- }
+ // #learn-safehold {
+ // display: block;
+ // }
}
@media screen and (max-width: 1296px) {
- #learn-safehold {
- display: none;
- }
+ // #learn-safehold {
+ // display: none;
+ // }
}
#top-icons .hide,
@@ -1769,6 +1787,28 @@ body {
}
+#side-navigation-wrapper .side-nav .nav .icon:nth-child(1) {
+ display: block;
+}
+
+
+
+#side-navigation-wrapper .side-nav .nav .icon:nth-child(2) {
+ display: none;
+}
+
+
+#side-navigation-wrapper .side-nav .nav:hover .icon:nth-child(1) {
+ display: none;
+}
+
+
+
+#side-navigation-wrapper .side-nav .nav:hover .icon:nth-child(2) {
+ display: block;
+}
+
+
#side-navigation-wrapper .side-nav strong {
margin-left: 15px;
}
@@ -1809,6 +1849,17 @@ body {
+#side-navigation-wrapper .side-nav .active .icon:nth-child(1) {
+ display: none;
+}
+
+
+
+#side-navigation-wrapper .side-nav .active .icon:nth-child(2) {
+ display: block;
+}
+
+
#side-navigation-wrapper .sub-nav-wrapper {
diff --git a/dev/renderer/assets/css/onboarding.scss b/dev/renderer/assets/css/onboarding.scss
index e1a38a4..adc1fd2 100644
--- a/dev/renderer/assets/css/onboarding.scss
+++ b/dev/renderer/assets/css/onboarding.scss
@@ -17,11 +17,7 @@
}
.onboarding-main-split .split-left,
-.onboarding-main-split .split-right {
-
- position: relative;
-
-}
+.onboarding-main-split .split-right {}
.onboarding-main-split .split-left {
background-clip: content-box;
@@ -61,25 +57,42 @@
.onboarding-main-split .split-right {
- border: 0px solid red;
- padding: 100px 90px;
-}
+ display: flex;
+ flex-direction: column;
+ padding: 0 30px;
+ .item {
+ flex-grow: 1;
+ flex-basis: 45px;
+ }
+ .item.header {
+ padding-top: 50px;
+ padding-bottom: 40px;
+ padding-left: 25px;
+ }
-.split-right-header,
-.split-right-main,
-.split-right-footer {
- float: left;
- width: 100%;
+ .item.content {
+ flex-grow: 15;
+ overflow: auto;
+ padding: 25px;
+ line-height: 25px;
+ }
+
+ .item.footer {
+ padding: 15px;
+ }
}
+
+.split-right-content {}
+
+
.split-right-header {
font-size: 28px;
font-weight: bold;
}
-
.split-right-outter-article {
color: #323c47;
display: block;
@@ -163,12 +176,8 @@
}
.split-right-footer {
- border: 0px solid red;
- bottom: 60px;
- clear: both;
height: 38px;
- position: absolute;
- width: 80%;
+ width: 100%;
}
@@ -201,7 +210,7 @@
.split-right-footer .page-switcher {
border: 0px solid red;
display: table;
- margin-left: 30%;
+ margin-left: 20%;
}
.split-right-footer .page-switcher li {
diff --git a/dev/renderer/assets/icon/Accounts.svg b/dev/renderer/assets/icon/Accounts.svg
new file mode 100644
index 0000000..49927a3
--- /dev/null
+++ b/dev/renderer/assets/icon/Accounts.svg
@@ -0,0 +1,33 @@
+
+
\ No newline at end of file
diff --git a/dev/renderer/assets/icon/Log.svg b/dev/renderer/assets/icon/Log.svg
new file mode 100644
index 0000000..86d4b1d
--- /dev/null
+++ b/dev/renderer/assets/icon/Log.svg
@@ -0,0 +1,53 @@
+
+
\ No newline at end of file
diff --git a/dev/renderer/assets/icon/Mining.svg b/dev/renderer/assets/icon/Mining.svg
new file mode 100644
index 0000000..06dd69f
--- /dev/null
+++ b/dev/renderer/assets/icon/Mining.svg
@@ -0,0 +1,38 @@
+
+
\ No newline at end of file
diff --git a/dev/renderer/assets/icon/Overview.svg b/dev/renderer/assets/icon/Overview.svg
new file mode 100644
index 0000000..b91928c
--- /dev/null
+++ b/dev/renderer/assets/icon/Overview.svg
@@ -0,0 +1,46 @@
+
+
\ No newline at end of file
diff --git a/dev/renderer/assets/icon/export_wallet.svg b/dev/renderer/assets/icon/export_wallet.svg
new file mode 100644
index 0000000..7a4c059
--- /dev/null
+++ b/dev/renderer/assets/icon/export_wallet.svg
@@ -0,0 +1,33 @@
+
+
\ No newline at end of file
diff --git a/dev/renderer/assets/img/image.png b/dev/renderer/assets/img/image.png
index e002670..88fa2d8 100644
Binary files a/dev/renderer/assets/img/image.png and b/dev/renderer/assets/img/image.png differ
diff --git a/dev/renderer/assets/img/image2.png b/dev/renderer/assets/img/image2.png
new file mode 100644
index 0000000..4f1a61d
Binary files /dev/null and b/dev/renderer/assets/img/image2.png differ
diff --git a/dev/renderer/assets/img/image3.png b/dev/renderer/assets/img/image3.png
new file mode 100644
index 0000000..380863f
Binary files /dev/null and b/dev/renderer/assets/img/image3.png differ
diff --git a/dev/renderer/components/Index.vue b/dev/renderer/components/Index.vue
index be367c2..f19cece 100644
--- a/dev/renderer/components/Index.vue
+++ b/dev/renderer/components/Index.vue
@@ -6,6 +6,7 @@
import ChannelCodes from '../../core/channel_codes';
import { ipcRenderer, remote } from 'electron';
import { ModalLoaderOpen } from './constants/events';
+import log from 'electron-log';
export default {
data() {
@@ -28,7 +29,7 @@ export default {
// to the index page.
// prettier-ignore
onAppLaunched(event, msg) {
- console.log("AppLaunched")
+ log.info("AppLaunched")
if (msg.hasWallet) { return this.$router.replace('login') }
return this.$router.replace("signup")
},
diff --git a/dev/renderer/components/Login.vue b/dev/renderer/components/Login.vue
index 1855275..7b295ba 100644
--- a/dev/renderer/components/Login.vue
+++ b/dev/renderer/components/Login.vue
@@ -15,21 +15,16 @@
remember your passphrase, but you know your 12-Word phrase, click the "Restore" button
to recover your wallet.
-