- {status === STATUS.GOOD ? (
-
- {/* Change homepage on the next line */}
-
-
-
-
-
-
-
-
-
- ) : (
-
- )}
-
+
{this.renderScreen()}
);
}
+
+ /**
+ * Decide which screen to render.
+ */
+ renderScreen () {
+ const {
+ onboardingStore: { isOnboarding },
+ healthStore: {
+ health: { status }
+ }
+ } = this.props;
+
+ // If we are onboarding, then never show the Overlay. On the other hand, if
+ // we're not onboarding, show the Overlay whenever we have an issue.
+ if (!isOnboarding && status !== STATUS.GOOD) {
+ return ;
+ }
+
+ return (
+
+ {/* We redirect to Onboarding if necessary, or by default to our
+ homepage which is Tokens */}
+
+
+
+
+
+
+
+
+
+
+ );
+ }
}
export default App;
diff --git a/src/Onboarding/Onboarding.js b/src/Onboarding/Onboarding.js
new file mode 100644
index 0000000000000000000000000000000000000000..40b8d2cc7d5a29eda888d60b0b0d246c4a5258bf
--- /dev/null
+++ b/src/Onboarding/Onboarding.js
@@ -0,0 +1,40 @@
+// Copyright 2015-2018 Parity Technologies (UK) Ltd.
+// This file is part of Parity.
+//
+// SPDX-License-Identifier: MIT
+
+import React, { Component } from 'react';
+import { inject, observer } from 'mobx-react';
+import { Link } from 'react-router-dom';
+
+import Health from '../Health';
+
+@inject('onboardingStore')
+@observer
+class Onboarding extends Component {
+ handleFirstRun = () => {
+ // Not first run anymore after clicking Start
+ this.props.onboardingStore.setIsFirstRun(false);
+ };
+
+ render () {
+ const {
+ onboardingStore: { hasAccounts }
+ } = this.props;
+ return (
+
+ This is the onboarding page.
+ {hasAccounts ? (
+
+ Start
+
+ ) : (
+ Create account
+ )}
+
+
+ );
+ }
+}
+
+export default Onboarding;
diff --git a/src/Onboarding/index.js b/src/Onboarding/index.js
new file mode 100644
index 0000000000000000000000000000000000000000..ac178853b60dadff3f452a70b3874cae914d2da7
--- /dev/null
+++ b/src/Onboarding/index.js
@@ -0,0 +1,8 @@
+// Copyright 2015-2018 Parity Technologies (UK) Ltd.
+// This file is part of Parity.
+//
+// SPDX-License-Identifier: MIT
+
+import Onboarding from './Onboarding';
+
+export default Onboarding;
diff --git a/src/stores/index.js b/src/stores/index.js
index d2346d8aa89dccc2693799b4812c4127dc15620c..16f1178c62a0fb1744e24266637f17bfcc52f865 100644
--- a/src/stores/index.js
+++ b/src/stores/index.js
@@ -5,6 +5,7 @@
import createAccountStore from './createAccountStore';
import healthStore from './healthStore';
+import onboardingStore from './onboardingStore';
import parityStore from './parityStore';
import signerStore from './signerStore';
import tokensStore from './tokensStore';
@@ -12,6 +13,7 @@ import tokensStore from './tokensStore';
export default {
createAccountStore,
healthStore,
+ onboardingStore,
parityStore,
signerStore,
tokensStore
diff --git a/src/stores/onboardingStore.js b/src/stores/onboardingStore.js
new file mode 100644
index 0000000000000000000000000000000000000000..fc878ecd0776eba963fff0271b9ded565c7e2b8b
--- /dev/null
+++ b/src/stores/onboardingStore.js
@@ -0,0 +1,62 @@
+// Copyright 2015-2018 Parity Technologies (UK) Ltd.
+// This file is part of Parity.
+//
+// SPDX-License-Identifier: MIT
+
+import { accountsInfo$ } from '@parity/light.js';
+import { action, computed, observable } from 'mobx';
+import { map } from 'rxjs/operators';
+import store from 'store';
+
+import LS_PREFIX from './utils/lsPrefix';
+
+const LS_KEY = `${LS_PREFIX}::firstRun`;
+
+class OnboardingStore {
+ @observable hasAccounts; // If the user has at least 1 account or not
+ @observable isFirstRun; // If it's the 1st time the user is running the app
+
+ constructor () {
+ const isFirstRun = store.get(LS_KEY);
+
+ if (isFirstRun === undefined) {
+ // Set store property to true.
+ this.setIsFirstRun(true);
+ } else {
+ this.setIsFirstRun(isFirstRun);
+ }
+
+ accountsInfo$()
+ .pipe(map(accounts => Object.keys(accounts).length > 0))
+ .subscribe(this.setHasAccounts);
+ }
+
+ /**
+ * We show the onboarding process if:
+ * - either it's the 1st time the user runs this app
+ * - or the user has 0 account
+ */
+ @computed
+ get isOnboarding () {
+ // If either of the two is undefined, then it means we're still fetching.
+ // This doesn't count as onboarding.
+ return this.hasAccounts === undefined || this.isFirstRun === undefined
+ ? false
+ : !this.hasAccounts || this.isFirstRun;
+ }
+
+ @action
+ setHasAccounts = hasAccounts => {
+ this.hasAccounts = hasAccounts;
+ };
+
+ @action
+ setIsFirstRun = isFirstRun => {
+ this.isFirstRun = isFirstRun;
+ this.updateLS();
+ };
+
+ updateLS = () => store.set(LS_KEY, this.isFirstRun);
+}
+
+export default new OnboardingStore();
diff --git a/src/stores/parityStore.js b/src/stores/parityStore.js
index daf392d429a6b6ec48af081c6e891f34df901c83..b5c7c9e5457e48a49cab6ef060815091128325d0 100644
--- a/src/stores/parityStore.js
+++ b/src/stores/parityStore.js
@@ -9,10 +9,11 @@ import isElectron from 'is-electron';
import light from '@parity/light.js';
import store from 'store';
+import LS_PREFIX from './utils/lsPrefix';
+
const electron = isElectron() ? window.require('electron') : null;
-const LS_PREFIX = '__paritylight::';
-const LS_KEY = `${LS_PREFIX}secureToken`;
+const LS_KEY = `${LS_PREFIX}::secureToken`;
class ParityStore {
@observable downloadProgress = 0;
diff --git a/src/stores/signerStore.js b/src/stores/signerStore.js
index 785cca7a3ffc5a04b8e18a34325f3d2b37c6406e..dcf8bc39bb34c91d7d0c413e6bd7408c3e457ef0 100644
--- a/src/stores/signerStore.js
+++ b/src/stores/signerStore.js
@@ -19,7 +19,7 @@ class SignerStore {
// TODO This .on() is not working, so we poll every second
// this.api.on('connected', this.subscribePending);
this.interval = setInterval(() => {
- if (parityStore.isApiConnected) {
+ if (parityStore.isApiConnected && this.api) {
this.subscribePending();
clearInterval(this.interval);
}
diff --git a/src/stores/tokensStore.js b/src/stores/tokensStore.js
index 6778956a6db31e7cf306dc7e4f248fdf947a946c..562b0bcc90b6dc8057c36e2c268cb6add9e7a8c1 100644
--- a/src/stores/tokensStore.js
+++ b/src/stores/tokensStore.js
@@ -9,20 +9,19 @@ import { combineLatest } from 'rxjs';
import store from 'store';
import ethereumIcon from '../assets/img/tokens/ethereum.png';
+import LS_PREFIX from './utils/lsPrefix';
-const LS_PREFIX = '__paritylight::tokens';
+const LS_KEY = `${LS_PREFIX}::tokens`;
class TokensStore {
@observable tokens = new Map();
constructor () {
- combineLatest(
- chainName$(),
- defaultAccount$()
- ).subscribe(([chainName, defaultAccount]) =>
- // Refetch token from localStorage everytime we have a new chainName
- // (shouldn't happen) or the user selects a new account
- this.fetchTokensFromDb(chainName, defaultAccount)
+ combineLatest(chainName$(), defaultAccount$()).subscribe(
+ ([chainName, defaultAccount]) =>
+ // Refetch token from localStorage everytime we have a new chainName
+ // (shouldn't happen) or the user selects a new account
+ this.fetchTokensFromDb(chainName, defaultAccount)
);
}
@@ -36,7 +35,7 @@ class TokensStore {
fetchTokensFromDb = async (chainName, defaultAccount) => {
// Set the localStorage key, we have one key per chain per account, in this
// format: __paritylight::tokens::0x123::kovan
- this.lsKey = `${LS_PREFIX}::${defaultAccount}::${chainName}`;
+ this.lsKey = `${LS_KEY}::${defaultAccount}::${chainName}`;
// Now we fetch the tokens from the localStorage
const tokens = store.get(this.lsKey);
diff --git a/src/stores/utils/lsPrefix.js b/src/stores/utils/lsPrefix.js
new file mode 100644
index 0000000000000000000000000000000000000000..52c6efcfb6a209c12e87112c56fd231dcc0ce0c5
--- /dev/null
+++ b/src/stores/utils/lsPrefix.js
@@ -0,0 +1,7 @@
+// Copyright 2015-2018 Parity Technologies (UK) Ltd.
+// This file is part of Parity.
+//
+// SPDX-License-Identifier: MIT
+
+// All keys in localStorage are prefixed with:
+export default '__paritylight';