+
+
+
Upload a Wallet File
+
+
+ Upload a wallet file to add the login information to your browser.
+ We provide the file on New Account Creation.
+
+
+
- )}
- {isUploading && (
-
-
-
{ }
+
+
+ Proceed to login
+
+ or
+
+ Back
- )}
-
- {isUploaded && (
-
-
attachment
-
{fileName}
-
delete
-
-
- )}
-
-
this.walletFileUploadInput = input}
- type='file'
- styleName='hide'
- />
-
-
-
}
- type='password'
- value={password}
- onChange={this.handlePasswordChange}
- required
- fullWidth
- {...styles.textField}
- />
-
- {isLoading && (
-
- { }
-
- )}
-
-
- }
- fullWidth
- disabled={isLoading}
- onClick={this.props.onGenerate}
- />
-
-
- : }
- disabled={isLoading || !isUploaded || !password || password === ''}
- onClick={this.handleEnterPassword}
- />
-
+
)
}
}
-
-export default LoginWithWallet
diff --git a/packages/login-ui/components/LoginWithWallet/LoginWithWallet.scss b/packages/login-ui/components/LoginWithWallet/LoginWithWallet.scss
index 106d35416..6eea2ae81 100644
--- a/packages/login-ui/components/LoginWithWallet/LoginWithWallet.scss
+++ b/packages/login-ui/components/LoginWithWallet/LoginWithWallet.scss
@@ -4,108 +4,105 @@
*/
@import "~styles/partials/variables";
-@import '../LoginPageShared';
+@import "~styles/partials/mixins";
-$gutter: 16px;
-
-// TODO @dkchv: will be moved to mui lib
-%mui-button-secondary {
- border-radius: $border-radius;
- background-color: $color-white;
-}
-
-%mui-button-secondary-content {
- padding: 10px;
- display: flex;
- justify-content: center;
- color: $color-blue;
- text-transform: uppercase;
- font-weight: 500;
- font-size: 14px;
-}
-
-.root {
- margin-top: 24px;
-}
+.wrapper {
+ text-align: center;
+ max-width: 600px;
+ margin: 0 auto;
+ color: #fff;
-.buttonIcon {
- @extend %buttonIcon;
- font-size: 16px;
- color: $color-primary-1;
+ @include md-only {
+ padding: 0 20px;
+ }
}
-.hide {
- display: none;
+.page-title {
+ font-size: 30px;
+ margin-bottom: 10px;
+ font-weight: 700;
}
-.back {
- margin-bottom: 16px;
-}
+.description {
+ margin-bottom: 50px;
+ color: $additionalData-color-1;
+ line-height: 22px;
+ font-weight: 400;
-.upload {
- @extend %mui-button-secondary;
- cursor: pointer;
+ @include md-only {
+ margin-bottom: 20px;
+ }
}
-.uploadContent {
- @extend %mui-button-secondary-content;
+.link {
+ color: $border-color;
+ margin: 0 auto 45px;
+ text-decoration: none;
+ font: 700 16px $font-proxima;
&:hover {
- background-color: rgba($color-blue, 0.4);
+ color: #FFB54E;
}
-}
-.progress {
- @extend %mui-button-secondary;
- @extend %mui-button-secondary-content;
+ @include md-only {
+ margin: 0 0 10px;
+ }
}
-.progressText {
- margin-left: 10px;
+.submit {
+ margin-bottom: 25px;
}
-.uploaded {
- @extend %mui-button-secondary;
- @extend %mui-button-secondary-content;
- align-items: center;
+.row {
+ margin-bottom: 50px;
}
-.walletIcon {
- flex-grow: 0;
- margin-right: 5px;
-}
+.button {
+ button {
+ display: flex;
+ align-items: center;
+ padding: 0 20px;
+ line-height: 45px;
+ min-width: 280px;
+ margin: 0 auto 20px;
+ text-transform: none;
+
+ &:disabled {
+ background: $background-color-1;
+ }
+ }
-.walletName {
- flex-grow: 1;
-}
+ &.button-warning button {
+ background: $color-red-2;
+ }
+ .button-text {
+ flex: 1;
+ text-align: left;
+ }
-.walletRemove {
- flex-grow: 0;
- margin-left: 10px;
- cursor: pointer;
-}
+ .before-img {
+ width: 24px;
+ display: inline-block;
+ margin-right: 5px;
+ }
-.tip {
- margin-top: 10px;
- text-align: center;
- color: white;
- font-weight: 300;
+ .after-img {
+ width: 24px;
+ display: inline-block;
+ }
}
.actions {
- display: flex;
- margin: 24px (-$gutter / 2) 0;
+ text-align: center;
+ margin-bottom: 45px;
+ color: $additionalData-color-1;
+ line-height: 30px;
}
-.action {
- margin: 0 ($gutter / 2);
- width: 50%;
- display: flex;
- justify-content: flex-end;
-}
+.submit {
+ margin-bottom: 25px;
-.whiteButton {
- button {
- color: $color-white;
+ button:disabled {
+ background: $background-color-1;
}
-}
\ No newline at end of file
+}
diff --git a/packages/login-ui/components/NetworkSelector/NetworkSelector.js b/packages/login-ui/components/NetworkSelector/NetworkSelector.js
index fc2263b27..0f32607a9 100644
--- a/packages/login-ui/components/NetworkSelector/NetworkSelector.js
+++ b/packages/login-ui/components/NetworkSelector/NetworkSelector.js
@@ -57,6 +57,7 @@ export default class NetworkSelector extends PureComponent {
resolveNetwork = () => {
const web3 = new Web3()
+ console.log('resolve network', this.props.getProviderURL())
web3Provider.reinit(web3, web3Utils.createStatusEngine(this.props.getProviderURL()))
web3Provider.resolve()
}
diff --git a/packages/login-ui/components/RecoverAccount/RecoverAccount.js b/packages/login-ui/components/RecoverAccount/RecoverAccount.js
new file mode 100644
index 000000000..a346cb752
--- /dev/null
+++ b/packages/login-ui/components/RecoverAccount/RecoverAccount.js
@@ -0,0 +1,128 @@
+/**
+ * Copyright 2017–2018, LaborX PTY
+ * Licensed under the AGPL Version 3 license.
+ */
+
+import { MuiThemeProvider } from 'material-ui'
+import React, { PureComponent } from 'react'
+import { connect } from 'react-redux'
+import PropTypes from 'prop-types'
+import { Link } from 'react-router'
+import { reduxForm, Field } from 'redux-form/immutable'
+import { TextField } from 'redux-form-material-ui'
+import {
+ WalletEntryModel,
+} from 'models/persistAccount'
+import {
+ onSubmitRecoverAccountForm,
+ onSubmitRecoverAccountFormSuccess,
+ onSubmitRecoverAccountFormFail,
+ initRecoverAccountPage,
+} from '@chronobank/login/redux/network/actions'
+
+import { Button, UserRow } from 'components'
+
+import styles from 'layouts/Splash/styles'
+import './RecoverAccount.scss'
+
+export const FORM_RECOVER_ACCOUNT = 'RecoverAccountPage'
+
+function mapStateToProps (state, ownProps) {
+ const selectedWallet = state.get('persistAccount').selectedWallet
+ return {
+ selectedWallet: new WalletEntryModel({...selectedWallet}),
+ }
+}
+
+function mapDispatchToProps (dispatch, ownProps) {
+ return {
+ onSubmit: async (values) => {
+ let words = [], mnemonic = ''
+
+ for (let i = 1; i <= 12; i++) {
+ const word = values.get(`word-${i}`)
+ word && words.push(word)
+ }
+
+ mnemonic = words.join(' ')
+
+ await dispatch(onSubmitRecoverAccountForm(mnemonic))
+ },
+ onSubmitSuccess: () => dispatch(onSubmitRecoverAccountFormSuccess()),
+ onSubmitFail: (errors, dispatch, submitErrors) => dispatch(onSubmitRecoverAccountFormFail(errors, dispatch, submitErrors)),
+ initRecoverAccountPage: () => dispatch(initRecoverAccountPage()),
+ }
+}
+
+class RecoverAccountPage extends PureComponent {
+ static propTypes = {
+ selectedWallet: PropTypes.instanceOf(WalletEntryModel),
+ initRecoverAccountPage: PropTypes.func,
+ }
+
+ componentWillMount(){
+ this.props.initRecoverAccountPage()
+ }
+
+ get getSelectedWalletName(){
+ const { selectedWallet } = this.props
+ return selectedWallet && selectedWallet.name || ''
+ }
+
+ render () {
+ const { handleSubmit, selectedWallet } = this.props
+
+ const wordsArray = new Array(12).fill()
+
+ return (
+
+
+
+
+ )
+ }
+}
+
+const form = reduxForm({ form: FORM_RECOVER_ACCOUNT })(RecoverAccountPage)
+export default connect(mapStateToProps, mapDispatchToProps)(form)
diff --git a/packages/login-ui/components/RecoverAccount/RecoverAccount.scss b/packages/login-ui/components/RecoverAccount/RecoverAccount.scss
new file mode 100644
index 000000000..ceaa1309e
--- /dev/null
+++ b/packages/login-ui/components/RecoverAccount/RecoverAccount.scss
@@ -0,0 +1,77 @@
+/**
+ * Copyright 2017–2018, LaborX PTY
+ * Licensed under the AGPL Version 3 license.
+ */
+
+@import "~styles/partials/variables";
+@import "~styles/partials/mixins";
+
+.form {
+ max-width: 380px;
+ margin:0 auto;
+
+ @include xs-only {
+ padding: 0 20px;
+ }
+}
+
+.title {
+ color: $color-white;
+ font-weight: 700;
+ font-size: 30px;
+ line-height: 42px;
+ margin-bottom: 40px;
+ text-align: center;
+}
+
+.actions {
+ text-align: center;
+ margin-bottom: 45px;
+ line-height: 30px;
+ color: #fff;
+}
+
+.link {
+ color: $border-color;
+ margin: 0 auto 45px;
+ text-decoration: none;
+ font: 700 16px $font-proxima;
+
+ &:hover {
+ color: #FFB54E;
+ }
+
+ @include md-only {
+ margin: 0 0 10px;
+ }
+}
+
+.button {
+ text-align: center;
+ margin-bottom: 22px;
+
+ button {
+ padding: 0 30px;
+ }
+}
+
+.actionIcon {
+ transform: matrix(-1,0,0,-1,0,0);
+}
+
+.user-row {
+ border-top: 1px solid $color-purpule;
+ margin-bottom: 10px;
+ color: #fff;
+}
+
+.fields-block {
+ margin-bottom: 50px;
+ display: flex;
+ justify-content: space-between;
+ flex-wrap: wrap;
+}
+
+.field {
+ width: 23% !important;
+}
diff --git a/packages/login-ui/components/ResetPassword/ResetPassword.js b/packages/login-ui/components/ResetPassword/ResetPassword.js
new file mode 100644
index 000000000..8c0d513ab
--- /dev/null
+++ b/packages/login-ui/components/ResetPassword/ResetPassword.js
@@ -0,0 +1,116 @@
+/**
+ * Copyright 2017–2018, LaborX PTY
+ * Licensed under the AGPL Version 3 license.
+ */
+
+import { MuiThemeProvider } from 'material-ui'
+import React, { PureComponent } from 'react'
+import { connect } from 'react-redux'
+import PropTypes from 'prop-types'
+import {
+ WalletEntryModel,
+} from 'models/persistAccount'
+import {
+ onSubmitResetAccountPasswordForm,
+ onSubmitResetAccountPasswordSuccess,
+ onSubmitResetAccountPasswordFail,
+ initResetPasswordPage,
+} from '@chronobank/login/redux/network/actions'
+import { reduxForm, Field } from 'redux-form/immutable'
+import { TextField } from 'redux-form-material-ui'
+import { UserRow, Button } from 'components'
+
+import styles from 'layouts/Splash/styles'
+import validate from './validate'
+import './ResetPassword.scss'
+
+export const FORM_RESET_PASSWORD = 'ResetPasswordPage'
+
+function mapStateToProps (state, ownProps) {
+ const selectedWallet = state.get('persistAccount').selectedWallet
+ return {
+ selectedWallet: new WalletEntryModel({...selectedWallet}),
+ }
+}
+
+function mapDispatchToProps (dispatch, ownProps) {
+ return {
+ onSubmit: async (values) => {
+ const password = values.get('password')
+
+ await dispatch(onSubmitResetAccountPasswordForm(password))
+ },
+ onSubmitSuccess: () => dispatch(onSubmitResetAccountPasswordSuccess()),
+ onSubmitFail: (errors, dispatch, submitErrors) => dispatch(onSubmitResetAccountPasswordFail(errors, dispatch, submitErrors)),
+ initResetPasswordPage: () => dispatch(initResetPasswordPage()),
+ }
+}
+
+class ResetPasswordPage extends PureComponent {
+ static propTypes = {
+ selectedWallet: PropTypes.instanceOf(WalletEntryModel),
+ initResetPasswordPage: PropTypes.func,
+ }
+
+ componentWillMount(){
+ this.props.initResetPasswordPage()
+ }
+
+ get getSelectedWalletName(){
+ const { selectedWallet } = this.props
+ return selectedWallet && selectedWallet.name || ''
+ }
+ render () {
+ const { handleSubmit, selectedWallet } = this.props
+
+ return (
+
+
+
+ )
+ }
+}
+
+const form = reduxForm({ form: FORM_RESET_PASSWORD, validate })(ResetPasswordPage)
+export default connect(mapStateToProps, mapDispatchToProps)(form)
diff --git a/packages/login-ui/components/ResetPassword/ResetPassword.scss b/packages/login-ui/components/ResetPassword/ResetPassword.scss
new file mode 100644
index 000000000..d1ce6b9bf
--- /dev/null
+++ b/packages/login-ui/components/ResetPassword/ResetPassword.scss
@@ -0,0 +1,92 @@
+/**
+ * Copyright 2017–2018, LaborX PTY
+ * Licensed under the AGPL Version 3 license.
+ */
+
+@import "~styles/partials/variables";
+@import "~styles/partials/mixins";
+
+ul.actions {
+ position: absolute;
+ top: 30px;
+ right: 80px;
+ display: flex;
+ justify-content: flex-end;
+ flex: 0 0 auto;
+ padding: 0;
+ list-style: none;
+ font-size: 18px;
+ line-height: 48px;
+
+ @include md-only {
+ top: 10px;
+ right: 10px;
+ flex: 1 1 auto;
+ }
+
+ li {
+
+ flex: 0 0 auto;
+
+ a {
+ padding: 10px 20px;
+ text-decoration: none;
+ color: $color-white;
+
+ &:hover {
+ text-decoration: underline;
+ }
+ }
+ }
+}
+
+.form {
+ max-width: 380px;
+ margin:0 auto;
+
+ @include xs-only {
+ padding: 0 20px;
+ }
+}
+
+.page-title {
+ color: $color-white;
+ font-weight: 700;
+ font-size: 30px;
+ line-height: 42px;
+ margin-bottom: 40px;
+ text-align: center;
+}
+
+.user-row {
+ border-top: 1px solid $color-purpule;
+}
+
+.field {
+ margin-bottom: 50px;
+}
+
+.button {
+ text-align: center;
+ margin-bottom: 30px;
+}
+
+.actions {
+ text-align: center;
+ margin-bottom: 45px;
+}
+
+.link {
+ color: $border-color;
+ margin: 0 auto 45px;
+ text-decoration: none;
+ font: 700 16px $font-proxima;
+
+ &:hover {
+ color: #FFB54E;
+ }
+
+ @include md-only {
+ margin: 0 0 10px;
+ }
+}
diff --git a/packages/login-ui/components/ResetPassword/validate.js b/packages/login-ui/components/ResetPassword/validate.js
new file mode 100644
index 000000000..a23dc2614
--- /dev/null
+++ b/packages/login-ui/components/ResetPassword/validate.js
@@ -0,0 +1,27 @@
+/**
+ * Copyright 2017–2018, LaborX PTY
+ * Licensed under the AGPL Version 3 license.
+ */
+
+import ErrorList from 'platform/ErrorList'
+import * as validator from 'models/validator'
+
+const validateEqualPasswords = (password, confirmPassword) => password === confirmPassword ? null : 'Wrong password'
+
+export default (values) => {
+
+ const password = values.get('password')
+
+ let passwordErrors = new ErrorList()
+ passwordErrors.add(validator.required(password))
+
+ const confirmPassword = values.get('confirmPassword')
+ let confirmPasswordErrors = new ErrorList()
+ confirmPasswordErrors.add(validator.required(confirmPassword))
+ confirmPasswordErrors.add(validateEqualPasswords(password, confirmPassword))
+
+ return {
+ password: passwordErrors.getErrors(),
+ confirmPassword: confirmPasswordErrors.getErrors(),
+ }
+}
diff --git a/packages/login-ui/components/SelectWallet/SelectWallet.js b/packages/login-ui/components/SelectWallet/SelectWallet.js
new file mode 100644
index 000000000..0929db635
--- /dev/null
+++ b/packages/login-ui/components/SelectWallet/SelectWallet.js
@@ -0,0 +1,111 @@
+/**
+ * Copyright 2017–2018, LaborX PTY
+ * Licensed under the AGPL Version 3 license.
+ */
+
+import PropTypes from 'prop-types'
+import { MuiThemeProvider } from 'material-ui'
+import { connect } from 'react-redux'
+import React, { PureComponent } from 'react'
+import { Link } from 'react-router'
+import { UserRow, Button } from 'components'
+import { navigateToSelectImportMethod, onWalletSelect } from '@chronobank/login/redux/network/actions'
+import {
+ WalletEntryModel,
+} from 'models/persistAccount'
+
+import arrow from 'assets/img/icons/prev-white.svg'
+import './SelectWallet.scss'
+
+function mapDispatchToProps (dispatch) {
+ return {
+ navigateToSelectImportMethod: () => dispatch(navigateToSelectImportMethod()),
+ onWalletSelect: (wallet) => dispatch(onWalletSelect(wallet)),
+ }
+}
+
+function mapStateToProps (state) {
+ return {
+ walletsList: state.get('persistAccount').walletsList.map(
+ (wallet) => new WalletEntryModel({...wallet})
+ ),
+ }
+}
+
+@connect(mapStateToProps, mapDispatchToProps)
+export default class SelectWalletPage extends PureComponent {
+ static propTypes = {
+ onWalletSelect: PropTypes.func,
+ walletsList: PropTypes.arrayOf(
+ PropTypes.instanceOf(WalletEntryModel)
+ ),
+ navigateToSelectImportMethod: PropTypes.func,
+ }
+
+ static defaultProps = {
+ onWalletSelect: () => {},
+ walletsList: [],
+ }
+
+ renderWalletsList (){
+ const { onWalletSelect, walletsList } = this.props
+
+ if (!walletsList || !walletsList.length){
+ return (
+
+ Sorry, there are no accounts to display
+
+ )
+ }
+
+ return (
+
+ {
+ walletsList ? walletsList.map((w, i) => (
+ onWalletSelect(w)}
+ />
+ )) : null
+ }
+
+ )
+ }
+
+ render () {
+ return (
+
+
+
+
My Accounts
+
+
+ Browse account stored on your device.
+
+ If you have created an account before you may use Add an Existing Account option below.
+
+
+
+ { this.renderWalletsList() }
+
+
+
+ Add an existing account
+
+ or
+ Create New Account
+
+
+
+
+
+ )
+ }
+}
diff --git a/packages/login-ui/components/SelectWallet/SelectWallet.scss b/packages/login-ui/components/SelectWallet/SelectWallet.scss
new file mode 100644
index 000000000..168f3cf01
--- /dev/null
+++ b/packages/login-ui/components/SelectWallet/SelectWallet.scss
@@ -0,0 +1,93 @@
+/**
+ * Copyright 2017–2018, LaborX PTY
+ * Licensed under the AGPL Version 3 license.
+ */
+
+@import "~styles/partials/variables";
+@import "~styles/partials/mixins";
+
+.wrapper {
+ margin:0 auto;
+
+ @include xs-only {
+ padding: 0 20px;
+ }
+}
+
+.content {
+ margin: 0 auto;
+ max-width: 380px;
+}
+
+.page-title {
+ color: $color-white;
+ font-weight: 700;
+ font-size: 30px;
+ line-height: 42px;
+ margin-bottom: 40px;
+ text-align: center;
+}
+
+.wallets-list {
+ border-top: 1px solid $color-purpule;
+ margin-bottom: 45px;
+ color: #fff;
+}
+
+.empty-list {
+ border-top: 1px solid $color-purpule;
+ border-bottom: 1px solid $color-purpule;
+ padding: 18px 0;
+ margin-bottom: 30px;
+ text-align: center;
+ color: #fff;
+ width: 380px;
+
+ @include xs-only {
+ width: 100%;
+ }
+}
+
+.actions {
+ text-align: center;
+ margin-bottom: 45px;
+ line-height: 30px;
+ color: #fff;
+}
+
+.link {
+ color: $border-color;
+ margin: 0 auto 45px;
+ text-decoration: none;
+ font: 700 16px $font-proxima;
+
+ &:hover {
+ color: #FFB54E;
+ }
+
+ @include md-only {
+ margin: 0 0 10px;
+ }
+}
+
+.button {
+ text-align: center;
+ margin-bottom: 22px;
+
+ button {
+ padding: 0 30px;
+ }
+}
+
+.actionIcon {
+ transform: matrix(-1,0,0,-1,0,0);
+}
+
+.description {
+ font-weight: 400;
+ color: $additionalData-color-1;
+ font-size: 16px;
+ line-height: 22px;
+ margin-bottom: 40px;
+ text-align: center;
+}
diff --git a/packages/login-ui/components/index.js b/packages/login-ui/components/index.js
new file mode 100644
index 000000000..fcf046928
--- /dev/null
+++ b/packages/login-ui/components/index.js
@@ -0,0 +1,11 @@
+export { default as GenerateMnemonic } from './GenerateMnemonic/GenerateMnemonic'
+export { default as LoginWithMnemonic } from './LoginWithMnemonic/LoginWithMnemonic'
+export { default as LoginWithPrivateKey } from './LoginWithPrivateKey/LoginWithPrivateKey'
+export { default as LoginForm } from './LoginForm/LoginForm'
+export { default as LoginWithOptions } from './LoginWithOptions/LoginWithOptions'
+export { default as AccountSelector } from './AccountSelector/AccountSelector'
+export { default as RecoverAccount } from './RecoverAccount/RecoverAccount'
+export { default as ResetPassword } from './ResetPassword/ResetPassword'
+export { default as CreateAccount } from './CreateAccount/CreateAccount'
+export { default as ConfirmMnemonic } from './ConfirmMnemonic/ConfirmMnemonic'
+export { default as CommonNetworkSelector } from './CommonNetworkSelector/CommonNetworkSelector'
diff --git a/packages/login/network/HDWalletProvider.js b/packages/login/network/HDWalletProvider.js
index a8cadf0c3..c67192379 100644
--- a/packages/login/network/HDWalletProvider.js
+++ b/packages/login/network/HDWalletProvider.js
@@ -18,6 +18,7 @@ ProviderEngine.prototype.changeProvider = function (source, idx) {
export default class HDWalletProvider {
constructor (wallet, provider_url, address_index = 0, num_addresses = 0) {
+ console.log('HDWALLET', provider_url)
this.hdwallet = hdkey.fromMasterSeed(wallet.getPrivateKey())
this.wallet_hdpath = WALLET_HD_PATH
this.wallets = [wallet]
diff --git a/packages/login/network/NetworkService.js b/packages/login/network/NetworkService.js
index b810833d3..075e5e761 100644
--- a/packages/login/network/NetworkService.js
+++ b/packages/login/network/NetworkService.js
@@ -98,6 +98,7 @@ class NetworkService extends EventEmitter {
}
const web3 = new Web3()
+ console.log('check local session', providerURL)
web3Provider.reinit(web3, new web3.providers.HttpProvider(providerURL || TESTRPC_URL))
const accounts = await web3Provider.getAccounts()
@@ -256,6 +257,7 @@ class NetworkService extends EventEmitter {
async checkTestRPC (providerUrl) {
const web3 = new Web3()
+ console.log('checkTestRPC', providerUrl)
web3.setProvider(new web3.providers.HttpProvider(providerUrl || TESTRPC_URL))
const web3Provider = new Web3Provider(web3)
diff --git a/packages/login/network/settings.js b/packages/login/network/settings.js
index 72d348c8b..3f5314969 100644
--- a/packages/login/network/settings.js
+++ b/packages/login/network/settings.js
@@ -268,6 +268,30 @@ export const getNetworksByProvider = (providerId, withLocal = false) => {
}
}
+export const getNetworksWithProviders = (withLocal = false) => {
+ let networks = []
+
+ Object.keys(providerMap)
+ .filter((key) => !providerMap[key].disabled)
+ .forEach((key) => {
+ const provider = providerMap[key]
+
+ const networksProvider = getNetworksByProvider(provider && provider.id, withLocal)
+ .map((network) => ({
+ provider: provider,
+ network,
+ }))
+
+ networks = networks.concat(networksProvider)
+ })
+
+ return networks
+}
+
+export const getNetworkWithProviderNames = (providerId, networkId, withLocal = false) => {
+ return `${getProviderById(providerId).name} - ${getNetworkById(networkId, providerId, withLocal).name}`
+}
+
export const getProviderById = (providerId) => {
return providerMap[Object.keys(providerMap).find((key) => providerMap[key].id === providerId)] || {}
}
diff --git a/packages/login/redux/network/actions.js b/packages/login/redux/network/actions.js
index 85e60f5eb..084f6619e 100644
--- a/packages/login/redux/network/actions.js
+++ b/packages/login/redux/network/actions.js
@@ -3,9 +3,36 @@
* Licensed under the AGPL Version 3 license.
*/
+import {
+ createAccount,
+ resetPasswordAccount,
+ validateMnemonicForAccount,
+ validateAccountName,
+ decryptAccount,
+ accountAdd,
+ accountSelect,
+} from 'redux/persistAccount/actions'
+import {
+ FORM_CONFIRM_MNEMONIC,
+ FORM_MNEMONIC_LOGIN_PAGE,
+ FORM_PRIVATE_KEY_LOGIN_PAGE,
+ FORM_LOGIN_PAGE,
+ FORM_CREATE_ACCOUNT,
+ FORM_RECOVER_ACCOUNT,
+ FORM_RESET_PASSWORD,
+} from 'pages'
+import Web3 from 'web3'
+import bip39 from 'bip39'
+import Accounts from 'web3-eth-accounts'
+import { login } from 'redux/session/actions'
+import { stopSubmit, SubmissionError } from 'redux-form'
+import { push } from 'react-router-redux'
+import mnemonicProvider from '@chronobank/login/network/mnemonicProvider'
import { ethereumProvider } from '../../network/EthereumProvider'
import { btcProvider, ltcProvider, btgProvider } from '../../network/BitcoinProvider'
import { nemProvider } from '../../network/NemProvider'
+import networkService from '../../network/NetworkService'
+import privateKeyProvider from '../../network/privateKeyProvider'
export const DUCK_NETWORK = 'network'
@@ -20,6 +47,25 @@ export const NETWORK_SET_TEST_RPC = 'network/SET_TEST_RPC'
export const NETWORK_SET_TEST_METAMASK = 'network/SET_TEST_METAMASK'
export const NETWORK_SET_NETWORK = 'network/SET_NETWORK'
export const NETWORK_SET_PROVIDER = 'network/SET_PROVIDER'
+export const NETWORK_SET_NEW_ACCOUNT_CREDENTIALS = 'network/SET_NEW_ACCOUNT_CREDENTIALS'
+export const NETWORK_SET_NEW_MNEMONIC = 'network/SET_NEW_MNEMONIC'
+export const NETWORK_RESET_NEW_MNEMONIC = 'network/RESET_NEW_MNEMONIC'
+
+export const NETWORK_SET_IMPORT_MNEMONIC = 'network/SET_IMPORT_MNEMONIC'
+export const NETWORK_SET_IMPORT_PRIVATE_KEY = 'network/SET_NEW_PRIVATE_KEY'
+export const NETWORK_RESET_IMPORT_PRIVATE_KEY = 'network/RESET_NEW_PRIVATE_KEY'
+export const NETWORK_SET_IMPORT_ACCOUNT_MODE = 'network/SET_IMPORT_ACCOUNT_MODE'
+export const NETWORK_RESET_IMPORT_ACCOUNT_MODE = 'network/RESET_IMPORT_ACCOUNT_MODE'
+export const NETWORK_SET_LOGIN_SUBMITTING = 'network/SET_LOGIN_SUBMITTING'
+export const NETWORK_RESET_LOGIN_SUBMITTING = 'network/RESET_LOGIN_SUBMITTING'
+export const NETWORK_SET_ACCOUNT_RECOVERY_MODE = 'network/SET_ACCOUNT_RECOVERY_MODE'
+export const NETWORK_RESET_ACCOUNT_RECOVERY_MODE = 'network/RESET_ACCOUNT_RECOVERY_MODE'
+
+export const WALLETS_ADD = 'network/WALLETS_ADD'
+export const WALLETS_SELECT = 'network/WALLETS_SELECT'
+export const WALLETS_LOAD = 'network/WALLETS_LOAD'
+export const WALLETS_UPDATE_LIST = 'network/WALLETS_UPDATE_LIST'
+export const WALLETS_REMOVE = 'network/WALLETS_REMOVE'
export const loading = (isLoading = true) => (dispatch) => {
dispatch({ type: NETWORK_LOADING, isLoading })
@@ -33,6 +79,387 @@ export const clearErrors = () => (dispatch) => {
dispatch({ type: NETWORK_CLEAR_ERRORS })
}
+export const navigateToCreateAccountWithoutImport = () => (dispatch) => {
+ dispatch(navigateToCreateAccount())
+ dispatch({ type: NETWORK_RESET_IMPORT_ACCOUNT_MODE })
+}
+
+export const initConfirmMnemonicPage = () => (dispatch, getState) => {
+ const state = getState()
+
+ const { newAccountMnemonic } = state.get('network')
+
+ if (!newAccountMnemonic){
+ dispatch(navigateToCreateAccount())
+ }
+
+}
+
+export const initMnemonicPage = () => (dispatch, getState) => {
+ const state = getState()
+
+ const { newAccountName, newAccountPassword } = state.get('network')
+
+ const emptyAccountCredentials = !newAccountName || !newAccountPassword
+
+ if (emptyAccountCredentials){
+ dispatch(navigateToCreateAccount())
+ }
+}
+
+export const initLoginPage = () => (dispatch, getState) => {
+ const state = getState()
+
+ const { selectedWallet } = state.get('persistAccount')
+
+ dispatch({ type: NETWORK_RESET_LOGIN_SUBMITTING })
+
+ if (!selectedWallet){
+ dispatch(navigateToSelectWallet())
+ }
+
+}
+
+export const resetNewMnemonic = () => (dispatch) => {
+ dispatch({ type: NETWORK_RESET_NEW_MNEMONIC })
+}
+export const generateNewMnemonic = () => (dispatch) => {
+ const mnemonic = mnemonicProvider.generateMnemonic()
+
+ dispatch({ type: NETWORK_SET_NEW_MNEMONIC, mnemonic })
+}
+
+export const resetImportAccountMode = () => (dispatch) => {
+ dispatch({ type: NETWORK_RESET_IMPORT_ACCOUNT_MODE })
+}
+
+export const onSubmitCreateAccountPage = (walletName, walletPassword) => async (dispatch, getState) => {
+ const state = getState()
+
+ const { importAccountMode, newAccountMnemonic, newAccountPrivateKey } = state.get('network')
+
+ const validateName = dispatch(validateAccountName(walletName))
+
+ if (!validateName){
+ throw new SubmissionError({ walletName: 'Wrong wallet name' })
+ }
+
+ dispatch({ type: NETWORK_SET_NEW_ACCOUNT_CREDENTIALS, walletName, walletPassword })
+
+ if (importAccountMode){
+ let wallet = await dispatch(createAccount({
+ name: walletName,
+ password: walletPassword,
+ mnemonic: newAccountMnemonic,
+ privateKey: newAccountPrivateKey,
+ numberOfAccounts: 0,
+ }))
+
+ dispatch(accountAdd(wallet))
+
+ dispatch(accountSelect(wallet))
+
+ dispatch(resetImportAccountMode())
+
+ dispatch(navigateToLoginPage())
+
+ return
+ }
+
+ dispatch(generateNewMnemonic())
+
+ dispatch(navigateToGenerateMnemonicPage())
+
+}
+
+export const onSubmitCreateAccountPageSuccess = () => (dispatch) => {
+
+}
+
+export const onSubmitCreateAccountPageFail = (errors, dispatch, submitErrors) => (dispatch) => {
+ dispatch(stopSubmit(FORM_CREATE_ACCOUNT, submitErrors && submitErrors.errors))
+}
+
+export const initImportMethodsPage = () => (dispatch) => {
+ dispatch({ type: NETWORK_SET_IMPORT_ACCOUNT_MODE })
+}
+
+export const onSubmitConfirmMnemonic = (confirmMnemonic) => (dispatch, getState) => {
+ const state = getState()
+
+ const { newAccountMnemonic } = state.get('network')
+
+ if (confirmMnemonic !== newAccountMnemonic){
+ throw new SubmissionError({ _error: 'Please enter correct mnemonic phrase' })
+ }
+
+}
+
+export const onSubmitConfirmMnemonicSuccess = () => async (dispatch, getState) => {
+ const state = getState()
+
+ const { newAccountMnemonic, newAccountName, newAccountPassword } = state.get('network')
+
+ let wallet = await dispatch(createAccount({
+ name: newAccountName,
+ password: newAccountPassword,
+ mnemonic: newAccountMnemonic,
+ numberOfAccounts: 0,
+ }))
+
+ dispatch(accountAdd(wallet))
+
+ dispatch(accountSelect(wallet))
+
+ dispatch(navigateToDownloadWalletPage())
+}
+
+export const onSubmitConfirmMnemonicFail = (errors, dispatch, submitErrors) => (dispatch) => {
+ dispatch(stopSubmit(FORM_CONFIRM_MNEMONIC, submitErrors && submitErrors.errors))
+}
+
+export const navigateToConfirmMnemonicPage = () => (dispatch) => {
+ dispatch(push('/login/confirm-mnemonic'))
+}
+
+export const navigateToDownloadWalletPage = () => (dispatch) => {
+ dispatch(push('/login/download-wallet'))
+}
+
+export const navigateToCreateAccount = () => (dispatch) => {
+ dispatch(push('/login/create-account'))
+}
+
+export const navigateToSelectImportMethod = () => (dispatch) => {
+ dispatch(push('/login/import-methods'))
+}
+
+export const navigateToMnemonicImportMethod = () => (dispatch) => {
+ dispatch(push('/login/mnemonic-login'))
+}
+
+export const navigateToPrivateKeyImportMethod = () => (dispatch) => {
+ dispatch(push('/login/private-key-login'))
+}
+
+export const navigateToSelectWallet = () => (dispatch) => {
+ dispatch(push('/login/select-account'))
+}
+
+export const navigateToLoginPage = () => (dispatch) => {
+ dispatch(push('/login'))
+}
+
+export const navigateToResetPasswordPage = () => (dispatch) => {
+ dispatch(push('/login/reset-password'))
+}
+
+export const navigateToRecoverAccountPage = () => (dispatch) => {
+ dispatch(push('/login/recover-account'))
+}
+
+export const navigateToGenerateMnemonicPage = () => (dispatch) => {
+ dispatch(push('/login/mnemonic'))
+}
+
+export const onSubmitMnemonicLoginForm = (mnemonic) => async (dispatch) => {
+ if (!bip39.validateMnemonic(mnemonic)){
+ throw new Error('Invalid mnemonic')
+ }
+
+ dispatch({ type: NETWORK_SET_NEW_MNEMONIC, mnemonic })
+
+}
+
+export const onSubmitMnemonicLoginFormSuccess = () => (dispatch) => {
+ dispatch(navigateToCreateAccount())
+}
+
+export const onSubmitMnemonicLoginFormFail = () => (dispatch) => {
+ dispatch(stopSubmit(FORM_MNEMONIC_LOGIN_PAGE, { key: 'Wrong mnemonic' }))
+
+}
+
+export const onSubmitPrivateKeyLoginForm = (privateKey) => (dispatch) => {
+ let pk = privateKey || ''
+
+ if (pk.slice(0, 2) === '0x'){
+ pk = pk.slice(2)
+ }
+
+ dispatch({ type: NETWORK_SET_IMPORT_PRIVATE_KEY, privateKey: pk })
+}
+
+export const onSubmitPrivateKeyLoginFormSuccess = () => (dispatch) => {
+ dispatch(navigateToCreateAccount())
+}
+
+export const onSubmitPrivateKeyLoginFormFail = () => (dispatch) => {
+ dispatch(stopSubmit(FORM_PRIVATE_KEY_LOGIN_PAGE, { pk: 'Wrong private key' }))
+
+}
+
+export const onSubmitLoginForm = (password) => async (dispatch, getState) => {
+ const state = getState()
+
+ dispatch({ type: NETWORK_SET_LOGIN_SUBMITTING })
+
+ const { selectedWallet } = state.get('persistAccount')
+ let wallet = await dispatch(decryptAccount(selectedWallet, password))
+
+ let privateKey = wallet && wallet[0] && wallet[0].privateKey
+
+ if (privateKey){
+ await dispatch(handlePrivateKeyLogin(privateKey))
+ }
+
+}
+
+export const onSubmitLoginFormSuccess = () => () => {
+}
+
+export const onSubmitLoginFormFail = () => (dispatch) => {
+ dispatch(stopSubmit(FORM_LOGIN_PAGE, { password: 'Wrong password' }))
+ dispatch({ type: NETWORK_RESET_LOGIN_SUBMITTING })
+
+}
+
+export const validateRecoveryForm = (mnemonic) => (dispatch, getState) => {
+ const state = getState()
+
+ const { selectedWallet } = state.get('persistAccount')
+
+ return dispatch(validateMnemonicForAccount(selectedWallet, mnemonic))
+
+}
+
+export const initRecoverAccountPage = () => (dispatch) => {
+ dispatch(resetNewMnemonic())
+ dispatch({ type: NETWORK_SET_ACCOUNT_RECOVERY_MODE })
+}
+
+export const onSubmitRecoverAccountForm = (mnemonic) => (dispatch) => {
+ const validForm = dispatch(validateRecoveryForm(mnemonic))
+
+ if (!validForm) {
+ throw new SubmissionError({ _error: 'Mnemonic incorrect for this wallet' })
+ }
+
+ dispatch({ type: NETWORK_SET_NEW_MNEMONIC, mnemonic })
+}
+
+export const onSubmitRecoverAccountFormSuccess = () => (dispatch) => {
+ dispatch(navigateToResetPasswordPage())
+}
+
+export const onSubmitRecoverAccountFormFail = () => (dispatch) => {
+ dispatch(stopSubmit(FORM_RECOVER_ACCOUNT, { pk: 'Wrong private key' }))
+
+}
+
+export const initResetPasswordPage = () => (dispatch, getState) => {
+ const state = getState()
+
+ const { accountRecoveryMode } = state.get('network')
+
+ if (!accountRecoveryMode){
+ dispatch(navigateToRecoverAccountPage())
+ }
+}
+
+export const onSubmitResetAccountPasswordForm = (password) => async (dispatch, getState) => {
+ const state = getState()
+
+ const { newAccountMnemonic } = state.get('network')
+ const { selectedWallet } = state.get('persistAccount')
+
+ await dispatch(resetPasswordAccount(selectedWallet, newAccountMnemonic, password))
+
+}
+
+export const onSubmitResetAccountPasswordSuccess = () => (dispatch) => {
+ dispatch({ type: NETWORK_RESET_ACCOUNT_RECOVERY_MODE })
+ dispatch(navigateToLoginPage())
+
+}
+
+export const onSubmitResetAccountPasswordFail = (error, dispatch, submitError) => (dispatch) => {
+ dispatch(stopSubmit(FORM_RESET_PASSWORD, { _error: 'Error' }))
+
+}
+
+export const onWalletSelect = (wallet) => (dispatch) => {
+ dispatch(accountSelect(wallet))
+
+ dispatch(navigateToLoginPage())
+}
+
+export const handlePrivateKeyLogin = (privateKey) => async (dispatch, getState) => {
+ let state = getState()
+
+ dispatch(loading())
+ dispatch(clearErrors())
+ const provider = privateKeyProvider.getPrivateKeyProvider(privateKey.slice(2), networkService.getProviderSettings(), state.get('multisigWallet'))
+
+ networkService.selectAccount(provider.ethereum.getAddress())
+ await networkService.setup(provider)
+
+ state = getState()
+ const { selectedAccount, selectedProviderId, selectedNetworkId } = state.get(DUCK_NETWORK)
+
+ dispatch(clearErrors())
+
+ const isPassed = await networkService.checkNetwork(
+ selectedAccount,
+ selectedProviderId,
+ selectedNetworkId,
+ )
+
+ if (isPassed) {
+ networkService.createNetworkSession(
+ selectedAccount,
+ selectedProviderId,
+ selectedNetworkId,
+ )
+ dispatch(login(selectedAccount))
+ }
+
+}
+
+export const handleMnemonicLogin = (mnemonic) => async (dispatch, getState) => {
+ const web3 = new Web3()
+ const accounts = new Accounts(new web3.providers.HttpProvider(networkService.getProviderSettings().url))
+ await accounts.wallet.clear()
+
+ dispatch(loading())
+ dispatch(clearErrors())
+ const provider = mnemonicProvider.getMnemonicProvider(mnemonic, networkService.getProviderSettings())
+ networkService.selectAccount(provider.ethereum.getAddress())
+ await networkService.setup(provider)
+
+ const state = getState()
+
+ const { selectedAccount, selectedProviderId, selectedNetworkId } = state.get(DUCK_NETWORK)
+
+ dispatch(clearErrors())
+
+ const isPassed = await networkService.checkNetwork(
+ selectedAccount,
+ selectedProviderId,
+ selectedNetworkId,
+ )
+
+ if (isPassed) {
+ networkService.createNetworkSession(
+ selectedAccount,
+ selectedProviderId,
+ selectedNetworkId,
+ )
+ dispatch(login(selectedAccount))
+ }
+
+}
+
export const getPrivateKeyFromBlockchain = (blockchain: string) => {
switch (blockchain) {
case 'Ethereum':
diff --git a/packages/login/redux/network/reducer.js b/packages/login/redux/network/reducer.js
index 45572a6b9..c88316efa 100644
--- a/packages/login/redux/network/reducer.js
+++ b/packages/login/redux/network/reducer.js
@@ -5,6 +5,8 @@
import { getNetworksByProvider, providerMap } from '../../network/settings'
import * as actions from './actions'
+import { NETWORK_SET_IMPORT_PRIVATE_KEY } from "./actions";
+import { NETWORK_SET_LOGIN_LOADING } from "./actions";
const initialState = {
isLoading: false,
@@ -31,6 +33,12 @@ const initialState = {
selectedProviderId: null,
networks: [],
selectedNetworkId: null,
+ newAccountName: null,
+ newAccountPassword: null,
+ newAccountMnemonic: null,
+ newAccountPrivateKey: null,
+ isLoginSubmitting: false,
+ accountRecoveryMode: false,
}
export default (state = initialState, action) => {
@@ -74,6 +82,63 @@ export default (state = initialState, action) => {
isLoading: false,
errors: [...state.errors, action.error],
}
+ case actions.NETWORK_SET_NEW_ACCOUNT_CREDENTIALS:
+ return {
+ ...state,
+ newAccountName: action.walletName,
+ newAccountPassword: action.walletPassword,
+ }
+ case actions.NETWORK_SET_NEW_MNEMONIC:
+ return {
+ ...state,
+ newAccountMnemonic: action.mnemonic,
+ }
+ case actions.NETWORK_RESET_NEW_MNEMONIC:
+ return {
+ ...state,
+ newAccountMnemonic: null,
+ }
+ case actions.NETWORK_SET_IMPORT_PRIVATE_KEY:
+ return {
+ ...state,
+ newAccountPrivateKey: action.privateKey,
+ }
+ case actions.NETWORK_RESET_IMPORT_PRIVATE_KEY:
+ return {
+ ...state,
+ newAccountPrivateKey: null,
+ }
+ case actions.NETWORK_SET_IMPORT_ACCOUNT_MODE:
+ return {
+ ...state,
+ importAccountMode: true,
+ }
+ case actions.NETWORK_RESET_IMPORT_ACCOUNT_MODE:
+ return {
+ ...state,
+ importAccountMode: false,
+ }
+ case actions.NETWORK_SET_LOGIN_SUBMITTING:
+ return {
+ ...state,
+ isLoginSubmitting: true,
+ }
+ case actions.NETWORK_RESET_LOGIN_SUBMITTING:
+ return {
+ ...state,
+ isLoginSubmitting: false,
+ }
+ case actions.NETWORK_SET_ACCOUNT_RECOVERY_MODE:
+ return {
+ ...state,
+ accountRecoveryMode: true,
+ }
+ case actions.NETWORK_RESET_ACCOUNT_RECOVERY_MODE:
+ return {
+ ...state,
+ accountRecoveryMode: false,
+ }
+
default:
return state
}
diff --git a/src/assets/img/appstore-white.svg b/src/assets/img/appstore-white.svg
new file mode 100644
index 000000000..7bc45022d
--- /dev/null
+++ b/src/assets/img/appstore-white.svg
@@ -0,0 +1,55 @@
+
+
+
+
+
+
+
+ 2d0745e2-571f-4313-890f-7945a689d7ff
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/assets/img/chronowalletlogobright.svg b/src/assets/img/chronowalletlogobright.svg
new file mode 100644
index 000000000..8f3ea19f5
--- /dev/null
+++ b/src/assets/img/chronowalletlogobright.svg
@@ -0,0 +1,28 @@
+
+
+
+
+ 1edbc86b-d425-405d-9bd8-60ee0f49b94a
+
+
+
+
+
+
+
diff --git a/src/assets/img/chronowallettextbright.svg b/src/assets/img/chronowallettextbright.svg
new file mode 100644
index 000000000..8bfe78bd7
--- /dev/null
+++ b/src/assets/img/chronowallettextbright.svg
@@ -0,0 +1,67 @@
+
+
+
+
+ a0f5ff71-36c6-4417-b91e-1ebd618450b1
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/assets/img/facebook.svg b/src/assets/img/facebook.svg
new file mode 100644
index 000000000..2fb8ce2fb
--- /dev/null
+++ b/src/assets/img/facebook.svg
@@ -0,0 +1,3 @@
+
+
+
diff --git a/src/assets/img/github.svg b/src/assets/img/github.svg
new file mode 100644
index 000000000..87609b98e
--- /dev/null
+++ b/src/assets/img/github.svg
@@ -0,0 +1,3 @@
+
+
+
diff --git a/src/assets/img/icons/back.svg b/src/assets/img/icons/back.svg
new file mode 100644
index 000000000..fa7e43a29
--- /dev/null
+++ b/src/assets/img/icons/back.svg
@@ -0,0 +1,18 @@
+
+
+
+
+ 90f1a8dc-9f06-4e17-987d-0d12e8d7cd42
+
+
+
+
+
diff --git a/src/assets/img/icons/check-green.svg b/src/assets/img/icons/check-green.svg
new file mode 100644
index 000000000..e2462aa8a
--- /dev/null
+++ b/src/assets/img/icons/check-green.svg
@@ -0,0 +1,16 @@
+
+
+
+
+ 8220b91f-6a4a-4ebe-98b8-0d701ad8170e
+
+
+
diff --git a/src/assets/img/icons/delete-white.svg b/src/assets/img/icons/delete-white.svg
new file mode 100644
index 000000000..cc63bdfd8
--- /dev/null
+++ b/src/assets/img/icons/delete-white.svg
@@ -0,0 +1,16 @@
+
+
+
+
+ ebffa863-a7aa-455a-9cb6-07c24cf83aed
+
+
+
diff --git a/src/assets/img/icons/file-white.svg b/src/assets/img/icons/file-white.svg
new file mode 100644
index 000000000..ce8746523
--- /dev/null
+++ b/src/assets/img/icons/file-white.svg
@@ -0,0 +1,16 @@
+
+
+
+
+ 5a5fafd5-b2c5-47ea-b2c0-7ef57e280622
+
+
+
diff --git a/src/assets/img/icons/key-white.svg b/src/assets/img/icons/key-white.svg
new file mode 100644
index 000000000..603e73829
--- /dev/null
+++ b/src/assets/img/icons/key-white.svg
@@ -0,0 +1,16 @@
+
+
+
+
+ 8d956c6a-7586-4e0b-a30f-371b3563407a
+
+
+
diff --git a/src/assets/img/icons/ledger-nano-white.svg b/src/assets/img/icons/ledger-nano-white.svg
new file mode 100644
index 000000000..d5e8fae74
--- /dev/null
+++ b/src/assets/img/icons/ledger-nano-white.svg
@@ -0,0 +1,21 @@
+
+
+
+
+ 65a7d0dd-b6fe-4fe5-a4db-e0696a8d7a5f
+
+
+
+
+
+
+
+
diff --git a/src/assets/img/icons/list.svg b/src/assets/img/icons/list.svg
new file mode 100644
index 000000000..462dbe514
--- /dev/null
+++ b/src/assets/img/icons/list.svg
@@ -0,0 +1,16 @@
+
+
+
+
+ 635d99ca-d8c6-4878-b277-83d9fe988a37
+
+
+
diff --git a/src/assets/img/icons/mnemonic-white.svg b/src/assets/img/icons/mnemonic-white.svg
new file mode 100644
index 000000000..716e3632e
--- /dev/null
+++ b/src/assets/img/icons/mnemonic-white.svg
@@ -0,0 +1,16 @@
+
+
+
+
+ 76b6e7c7-b5b0-4710-8f20-506b658dcd6b
+
+
+
diff --git a/src/assets/img/icons/plugin-white.svg b/src/assets/img/icons/plugin-white.svg
new file mode 100644
index 000000000..3c7387d6a
--- /dev/null
+++ b/src/assets/img/icons/plugin-white.svg
@@ -0,0 +1,16 @@
+
+
+
+
+ 4def2330-1322-4f97-bce0-2f128536f1fb
+
+
+
diff --git a/src/assets/img/icons/prev-white.svg b/src/assets/img/icons/prev-white.svg
new file mode 100644
index 000000000..09d794129
--- /dev/null
+++ b/src/assets/img/icons/prev-white.svg
@@ -0,0 +1,16 @@
+
+
+
+
+ 2e847eb1-2f11-470d-8c5e-a3e628575639
+
+
+
diff --git a/src/assets/img/icons/print-white.svg b/src/assets/img/icons/print-white.svg
new file mode 100644
index 000000000..38b5d6d4a
--- /dev/null
+++ b/src/assets/img/icons/print-white.svg
@@ -0,0 +1,16 @@
+
+
+
+
+ 160c1a77-03d7-4834-9669-e304e1093918
+
+
+
diff --git a/src/assets/img/icons/trezor-white.svg b/src/assets/img/icons/trezor-white.svg
new file mode 100644
index 000000000..2a6416ee1
--- /dev/null
+++ b/src/assets/img/icons/trezor-white.svg
@@ -0,0 +1,16 @@
+
+
+
+
+ bea14c40-229c-4277-a27f-d60338d325f5
+
+
+
diff --git a/src/assets/img/icons/uport.svg b/src/assets/img/icons/uport.svg
new file mode 100644
index 000000000..3a17295cd
--- /dev/null
+++ b/src/assets/img/icons/uport.svg
@@ -0,0 +1,16 @@
+
+
+
+
+ 770e3155-7634-4b44-965d-ccb7195c9cb8
+
+
+
diff --git a/src/assets/img/icons/wallet-white.svg b/src/assets/img/icons/wallet-white.svg
new file mode 100644
index 000000000..05c412564
--- /dev/null
+++ b/src/assets/img/icons/wallet-white.svg
@@ -0,0 +1,16 @@
+
+
+
+
+ 405250a0-8aac-4199-8932-61078aa77b6d
+
+
+
diff --git a/src/assets/img/icons/warning.svg b/src/assets/img/icons/warning.svg
new file mode 100644
index 000000000..b97391fc8
--- /dev/null
+++ b/src/assets/img/icons/warning.svg
@@ -0,0 +1,16 @@
+
+
+
+
+ 223a9793-bfa9-41d8-8047-9adb426e17c7
+
+
+
diff --git a/src/assets/img/instagram.svg b/src/assets/img/instagram.svg
new file mode 100644
index 000000000..c52ee4904
--- /dev/null
+++ b/src/assets/img/instagram.svg
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/src/assets/img/logo-chrono-bank-full.svg b/src/assets/img/logo-chrono-bank-full.svg
new file mode 100644
index 000000000..2961dd95c
--- /dev/null
+++ b/src/assets/img/logo-chrono-bank-full.svg
@@ -0,0 +1,34 @@
+
+
+
+
+ a92693b9-efd8-4fb5-92ed-1a8cc052713c
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/assets/img/play-white.svg b/src/assets/img/play-white.svg
new file mode 100644
index 000000000..933f5e26e
--- /dev/null
+++ b/src/assets/img/play-white.svg
@@ -0,0 +1,74 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ dba20148-1f77-467e-8fb4-b06aeb3fe101
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/assets/img/reddit.svg b/src/assets/img/reddit.svg
new file mode 100644
index 000000000..b4f687be9
--- /dev/null
+++ b/src/assets/img/reddit.svg
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/src/assets/img/spinningwheel-1.gif b/src/assets/img/spinningwheel-1.gif
new file mode 100644
index 000000000..a28fc9ffd
Binary files /dev/null and b/src/assets/img/spinningwheel-1.gif differ
diff --git a/src/assets/img/spinningwheel.gif b/src/assets/img/spinningwheel.gif
new file mode 100644
index 000000000..a28fc9ffd
Binary files /dev/null and b/src/assets/img/spinningwheel.gif differ
diff --git a/src/assets/img/stripes-2-crop-footer.jpg b/src/assets/img/stripes-2-crop-footer.jpg
new file mode 100644
index 000000000..12d9b4bfd
Binary files /dev/null and b/src/assets/img/stripes-2-crop-footer.jpg differ
diff --git a/src/assets/img/stripes-2-crop.jpg b/src/assets/img/stripes-2-crop.jpg
new file mode 100644
index 000000000..727e46587
Binary files /dev/null and b/src/assets/img/stripes-2-crop.jpg differ
diff --git a/src/assets/img/telegramm.svg b/src/assets/img/telegramm.svg
new file mode 100644
index 000000000..f268b5332
--- /dev/null
+++ b/src/assets/img/telegramm.svg
@@ -0,0 +1,3 @@
+
+
+
diff --git a/src/assets/img/twitter.svg b/src/assets/img/twitter.svg
new file mode 100644
index 000000000..5213d28ef
--- /dev/null
+++ b/src/assets/img/twitter.svg
@@ -0,0 +1,3 @@
+
+
+
diff --git a/src/assets/img/wallet-title-bg.png b/src/assets/img/wallet-title-bg.png
new file mode 100644
index 000000000..44edb7e56
Binary files /dev/null and b/src/assets/img/wallet-title-bg.png differ
diff --git a/src/components/common/ui/Button/Button.jsx b/src/components/common/ui/Button/Button.jsx
index 97ad6f608..d710a1806 100644
--- a/src/components/common/ui/Button/Button.jsx
+++ b/src/components/common/ui/Button/Button.jsx
@@ -68,12 +68,15 @@ export default class Button extends PureComponent {
if (flat) {
buttonType = 'flat'
}
+
+ const buttonClasses = classnames('button', buttonType)
+
return (
diff --git a/src/components/common/ui/Button/Button.scss b/src/components/common/ui/Button/Button.scss
index 20ff69577..21b833a20 100644
--- a/src/components/common/ui/Button/Button.scss
+++ b/src/components/common/ui/Button/Button.scss
@@ -52,6 +52,25 @@
}
}
+.login {
+ font-weight: 700;
+ font-size: 16px;
+ line-height: 45px;
+ padding: 0 50px;
+ min-width: 280px;
+ border-radius: 50px;
+ white-space: nowrap;
+ color: $color-white;
+ background-color: $button-color-1;
+
+ &:hover, &:active {
+ background-color: $button-color-2
+ }
+ &:disabled {
+ background-color: $button-color-3;
+ }
+}
+
:global {
.ripple {
position: absolute;
diff --git a/src/components/common/ui/UserRow/UserRow.js b/src/components/common/ui/UserRow/UserRow.js
new file mode 100644
index 000000000..d6d54562f
--- /dev/null
+++ b/src/components/common/ui/UserRow/UserRow.js
@@ -0,0 +1,71 @@
+import React from 'react'
+import PropTypes from 'prop-types'
+import classnames from 'classnames'
+import css from './UserRow.scss'
+
+export default class UserRow extends React.Component {
+ static propTypes = {
+ avatar: PropTypes.string,
+ name: PropTypes.string,
+ address: PropTypes.string,
+ onClick: PropTypes.func,
+ hideActionIcon: PropTypes.bool,
+ actionIcon: PropTypes.string,
+ actionIconClass: PropTypes.string,
+ reverseIcon: PropTypes.bool,
+ }
+
+ static defaultProps = {
+ avatar: '/src/assets/img/profile-photo-1.jpg',
+ name: '',
+ address: '',
+ onClick: () => {},
+ hideActionIcon: false,
+ actionIcon: '/src/assets/img/icons/list.svg',
+ actionIconClass: '',
+ reverseIcon: false,
+ }
+
+ render () {
+ const {
+ handleSubmit,
+ error,
+ pristine,
+ invalid,
+ avatar,
+ actionIcon,
+ hideActionIcon,
+ title,
+ subtitle,
+ onClick,
+ reverseIcon,
+ } = this.props
+
+ return (
+ {}}>
+
+
+
+
+
+ { title ? (
+
+ {title}
+
) : null}
+ { subtitle ? (
+
+ {subtitle}
+
) : null}
+
+
+ { !hideActionIcon ? (
+
+
+
+
+
+ ) : null}
+
+ )
+ }
+}
diff --git a/src/components/common/ui/UserRow/UserRow.scss b/src/components/common/ui/UserRow/UserRow.scss
new file mode 100644
index 000000000..5307e9205
--- /dev/null
+++ b/src/components/common/ui/UserRow/UserRow.scss
@@ -0,0 +1,88 @@
+@import "~styles/partials/variables";
+@import "~styles/partials/mixins";
+
+.userBlock {
+ color: $additionalData-color-1;
+ border-bottom: 1px solid $color-purpule;
+ padding: 18px 0;
+ text-align: left;
+ font-weight: 400;
+ display: flex;
+ justify-content: space-between;
+ cursor: pointer;
+
+ &.userBlockSingle {
+ cursor: initial;
+ }
+
+ &:hover .userBlockInfo {
+ color: $color-hover;
+ }
+
+ .userBlockInner {
+ display: flex;
+
+ .userBlockAvatar {
+ .userAvatar {
+ width: 40px;
+ border-radius: 20px;
+ margin-right: 20px;
+ display: block;
+ }
+ }
+
+ .userBlockInfo {
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ min-width: 0;
+
+ .title {
+ font-size: 16px;
+ line-height: 22px;
+ min-width: 0;
+ word-break: break-all;
+ padding-right: 20px;
+ font-weight: 500;
+ }
+
+ .subtitle {
+ font-size: 12px;
+ line-height: 17px;
+ min-width: 0;
+ word-break: break-all;
+ }
+ }
+ }
+
+ .actionWrapper {
+ align-items: center;
+ display: flex;
+ }
+
+ .actionListTrigger {
+ background: transparent;
+ width: 24px;
+ border: none;
+ opacity: 0.25;
+ cursor: pointer;
+ outline: none;
+
+ &.actionListTriggerDisabled {
+ cursor: initial;
+ }
+
+ &:hover {
+ opacity: 1;
+ }
+
+ img {
+ display: block;
+ }
+ }
+
+}
+
+.reverseIcon {
+ transform: matrix(-1,0,0,-1,0,0);
+}
diff --git a/src/components/index.js b/src/components/index.js
index 00f78db81..e9eee11b5 100644
--- a/src/components/index.js
+++ b/src/components/index.js
@@ -48,5 +48,6 @@ export { default as CopyDialog } from './dialogs/CopyDialog/CopyDialog'
export { default as WalletPendingTransfers } from './wallet/WalletPendingTransfers'
export { default as WalletWidget } from './wallet/WalletWidget/WalletWidget'
export { default as Button } from './common/ui/Button/Button'
+export { default as UserRow } from './common/ui/UserRow/UserRow'
export { default as TxConfirmations } from './common/TxConfirmations/TxConfirmations'
export { default as TopButtons } from './common/TopButtons/TopButtons'
diff --git a/src/layouts/Footer/Footer.js b/src/layouts/Footer/Footer.js
new file mode 100644
index 000000000..ab194f4c7
--- /dev/null
+++ b/src/layouts/Footer/Footer.js
@@ -0,0 +1,138 @@
+/**
+ * Copyright 2017–2018, LaborX PTY
+ * Licensed under the AGPL Version 3 license.
+ */
+
+import { Translate } from 'react-redux-i18n'
+import PropTypes from 'prop-types'
+import React, { Component } from 'react'
+import { reduxForm, Field } from 'redux-form/immutable'
+
+import { Button } from 'components'
+import { TextField } from 'redux-form-material-ui'
+import LogoChronobankFull from 'assets/img/logo-chrono-bank-full.svg'
+import PlayWhite from 'assets/img/play-white.svg'
+import AppstoreWhite from 'assets/img/appstore-white.svg'
+import facebook from 'assets/img/facebook.svg'
+import reddit from 'assets/img/reddit.svg'
+import instagram from 'assets/img/instagram.svg'
+import github from 'assets/img/github.svg'
+import twitter from 'assets/img/twitter.svg'
+import telegramm from 'assets/img/telegramm.svg'
+import StripesToCropFooter from 'assets/img/stripes-2-crop-footer.jpg'
+
+import './Footer.scss'
+import styles from './styles'
+
+const FORM_FOOTER_EMAIL_SUBSCRIPTION = 'FooterEmailSubscriptionForm'
+
+@reduxForm({ form: FORM_FOOTER_EMAIL_SUBSCRIPTION })
+export default class Footer extends Component {
+ static propTypes = {
+ children: PropTypes.node,
+ }
+
+ render () {
+ return (
+
+
+
+
+
+
+
+
+ Home
+ LaborX
+ Team
+ FAQ
+ Blog
+ Business Outline
+ Development Plan
+ White Paper
+
+
+
+
+
+
+ Downloads
+
+
+
+
+
+
+
+
+
+
+
+ Desktop App (Windows)
+ Desktop App (MacOS)
+
+
+
+
+
+
+ {'Connect with us'}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ info@chronobank.io
+ support@chronobank.io
+
+
+
+
+
+
+
+
+ { }
+
+
+
+
+
+
+ )
+ }
+}
diff --git a/src/layouts/Footer/Footer.scss b/src/layouts/Footer/Footer.scss
new file mode 100644
index 000000000..7c9eba6b6
--- /dev/null
+++ b/src/layouts/Footer/Footer.scss
@@ -0,0 +1,246 @@
+/**
+ * Copyright 2017–2018, LaborX PTY
+ * Licensed under the AGPL Version 3 license.
+ */
+@import "~styles/partials/mixins";
+@import "~styles/partials/variables";
+
+$copyRightPadding: 200px;
+
+.root {
+ background-image: $bg-gradient-0;
+ position: relative;
+ display: flex;
+ min-height: 100vh;
+ flex-direction: column;
+
+ @include md-only {
+ padding-bottom: 230px;
+ }
+
+ &:after {
+ // hack for preloading material icons and avoid FUOC on render
+ content: '';
+ font-family: 'Material Icons';
+ }
+}
+
+.content {
+ flex: 1;
+ width: 470px;
+ margin: 0 auto;
+ padding-top: 40px;
+
+ @include md-only {
+ padding-top: 80px;
+ }
+ @include xs-only {
+ width: 100vw;
+ }
+}
+
+.footer {
+ position: relative;
+ display: flex;
+ flex-direction: column;
+ bottom: 0;
+ left: 0;
+ right: 0;
+ min-width: 700px;
+ padding: 17px 80px;
+ align-items: center;
+ justify-content: flex-start;
+ overflow: hidden;
+
+ @include md-only {
+ padding: 20px;
+ min-width: 0;
+ }
+
+ .subscription {
+ position: absolute;
+ display: flex;
+ bottom: 0;
+ left: 335px;
+ }
+
+ .subscription-input {
+ width: 280px;
+ display: flex;
+ }
+
+ .subscription-button {
+ width: 280px;
+ margin-left: 30px;
+ display: flex;
+ align-items: flex-end;
+
+ .button {
+ button {
+ background: $color-light-blue;
+ font-size: 14px;
+ border-radius: 50px;
+ padding: 0 17px;
+ min-height: 30px;
+
+ &:hover {
+ background: #E2A864;
+ }
+ }
+ }
+ }
+
+ .copyright {
+ display: flex;
+ margin-top: 30px;
+ width: 870px;
+ z-index: 2;
+
+ @include md-only {
+ width: auto;
+ text-align: center;
+ }
+ }
+
+ .copyright-text {
+ color: #9997B2;
+ font-weight: 400;
+ font-size: 12px;
+ line-height: 14px;
+ text-align: left;
+ }
+
+ .footer-container {
+ display: flex;
+ z-index: 2;
+ position: relative;
+
+ @include md-only {
+ display: none;
+ }
+ }
+
+ .navigation-chrono-logo-container {
+ overflow: hidden;
+ display: flex;
+ }
+
+ .navigation-chrono-logo {
+ width: 250px;
+ display: block;
+ margin-left: -67px;
+ }
+
+ .navigation {
+ display: flex;
+ flex-direction: column;
+ align-items: flex-start;
+ width: 310px;
+ padding-left: 30px;
+ padding-right: 30px;
+ }
+
+
+ .navigation-list {
+
+ display: flex;
+ flex-direction: column;
+ list-style-type: none;
+
+ li {
+ display: flex;
+ color: $color-light-blue;
+ font-weight: 500;
+ font-size: 14px;
+ margin-top: 20px;
+
+ &.first {
+ margin: 0;
+ }
+ }
+
+ }
+
+ .title-container {
+ display: flex;
+ margin: 10px 0 30px;
+ }
+
+ .title {
+ color: #E2A864;
+ font-weight: 700;
+ font-size: 20px;
+ line-height: 24px;
+ }
+
+ .downloads {
+ display: flex;
+ flex-direction: column;
+ width: 310px;
+ padding-left: 30px;
+ padding-right: 30px;
+
+ .market-logo-container {
+ display: block;
+ margin-bottom: 20px;
+ }
+
+ .ios-market-logo {
+ display: block;
+ width: 125px;
+ }
+
+ .android-market-logo {
+ display: block;
+ width: 125px;
+ }
+
+ }
+
+ .connect {
+ display: flex;
+ flex-direction: column;
+ width: 310px;
+
+ padding-left: 30px;
+ padding-right: 30px;
+
+ .logos-container {
+ display: flex;
+ width: 125px;
+ height: 130px;
+ flex-direction: row;
+ justify-content: space-between;
+ flex-wrap: wrap;
+ }
+
+ .logo {
+ display: flex;
+ }
+
+ .img-logo {
+ width: 32px;
+ height: 32px;
+ }
+ }
+}
+
+.link {
+ color: white;
+ margin: auto 45px;
+ text-decoration: none;
+ font-size: 18px;
+
+}
+
+.background {
+ position: absolute;
+ bottom: 0;
+ left: 0;
+ right: -750px;
+ margin: auto;
+
+ @include md-only {
+ display: none;
+ }
+}
diff --git a/src/layouts/Footer/lang.js b/src/layouts/Footer/lang.js
new file mode 100644
index 000000000..efa7f6b92
--- /dev/null
+++ b/src/layouts/Footer/lang.js
@@ -0,0 +1,13 @@
+/**
+ * Copyright 2017–2018, LaborX PTY
+ * Licensed under the AGPL Version 3 license.
+ */
+
+export const prefix = `Splash`
+
+export default {
+ en: {
+ copyright: 'Copyright ©2018 LaborX Australia Pty Ltd. All Rights Reserved.',
+ subscribe: 'Subscribe',
+ },
+}
diff --git a/src/layouts/Footer/styles.js b/src/layouts/Footer/styles.js
new file mode 100644
index 000000000..ccc86af92
--- /dev/null
+++ b/src/layouts/Footer/styles.js
@@ -0,0 +1,30 @@
+export default {
+ textField: {
+ style: {
+ height: 62,
+ },
+ underlineStyle: {
+ borderColor: '#A3A3CC',
+ bottom: 0,
+ },
+ underlineFocusStyle: {
+ borderColor: '#E2A864',
+ bottom: 0,
+ },
+ inputStyle: {
+ color: '#9997B2',
+ textAlign: 'center',
+ marginTop: 0,
+ paddingTop: 18,
+ },
+ floatingLabelStyle: {
+ color: '#A3A3CC',
+ top: 28,
+ left: 0,
+ right: 0,
+ margin: 'auto',
+ textAlign: 'center',
+ transformOrigin: 'center center',
+ },
+ },
+}
diff --git a/src/layouts/Markup.jsx b/src/layouts/Markup.jsx
index c82973551..169a8854c 100644
--- a/src/layouts/Markup.jsx
+++ b/src/layouts/Markup.jsx
@@ -116,7 +116,7 @@ export default class Markup extends PureComponent {
{this.renderPageTitle()}
-
+ {/* */}
@@ -136,7 +136,7 @@ export default class Markup extends PureComponent {
-
+ {/*
*/}
diff --git a/src/layouts/Splash/Splash.js b/src/layouts/Splash/Splash.js
index d44331a4b..f79e16b76 100644
--- a/src/layouts/Splash/Splash.js
+++ b/src/layouts/Splash/Splash.js
@@ -3,38 +3,70 @@
* Licensed under the AGPL Version 3 license.
*/
-import { Translate } from 'react-redux-i18n'
import PropTypes from 'prop-types'
import React, { Component } from 'react'
-import ChronoBankLogo from 'components/common/ChronoBankLogo/ChronoBankLogo'
+
+import WalletTitleBG from 'assets/img/wallet-title-bg.png'
+import StripesToCrop from 'assets/img/stripes-2-crop.jpg'
+import ChronoWalletLogoBright from 'assets/img/chronowalletlogobright.svg'
+import ChronoWalletTextBright from 'assets/img/chronowallettextbright.svg'
+import BackIcon from 'assets/img/icons/back.svg'
+
+import Footer from '../Footer/Footer'
+import PersistWrapper from '../partials/PersistWrapper/PersistWrapper'
import './Splash.scss'
-class Splash extends Component {
+export default class Splash extends Component {
static propTypes = {
children: PropTypes.node,
+ goBack: PropTypes.func,
+ navigatorText: PropTypes.string,
+ }
+
+ static defaultProps = {
+ goBack: null,
+ navigatorText: '',
}
render () {
+ const { children, goBack, navigatorText } = this.props
+
return (
-
-
- {this.props.children}
+
- {!window.isMobile && (
-
-
-
-
-
-
+
+ {
+ goBack ? (
+
+
+
+
+
+ { navigatorText }
+
-
- )}
+ ) : null
+ }
+
+
+ {children ? children: null}
+
+
+ {!window.isMobile && (
)}
)
}
}
-export default Splash
diff --git a/src/layouts/Splash/Splash.scss b/src/layouts/Splash/Splash.scss
index 51dc29f46..07b08b2f5 100644
--- a/src/layouts/Splash/Splash.scss
+++ b/src/layouts/Splash/Splash.scss
@@ -8,15 +8,16 @@
$copyRightPadding: 200px;
.root {
- background-image: $bg-gradient-0;
+ background-color: #242045;
position: relative;
- padding-bottom: 130px; // for footer
display: flex;
min-height: 100vh;
flex-direction: column;
+ font-family: $font-proxima;
@include md-only {
- padding-bottom: 230px;
+ min-height: 100vh;
+ padding: 0;
}
&:after {
@@ -26,20 +27,6 @@ $copyRightPadding: 200px;
}
}
-.content {
- flex: 1;
- width: 470px;
- margin: 0 auto;
- padding-top: 40px;
-
- @include md-only {
- padding-top: 80px;
- }
- @include xs-only {
- width: 100vw;
- }
-}
-
.footer {
display: flex;
background-color: rgba(255, 255, 255, 0.1);
@@ -55,6 +42,94 @@ $copyRightPadding: 200px;
}
}
+.header-container {
+ display: flex;
+ justify-content: center;
+ height: 340px;
+ overflow: hidden;
+ margin-bottom: 20px;
+ position: relative;
+
+}
+
+.header-images {
+
+}
+
+.header-navigator {
+ position: absolute;
+ display: none;
+ align-items: center;
+ top: 0;
+ left: 0;
+ width: 100%;
+ line-height: 70px;
+ height: 70px;
+ padding-left: 20px;
+
+ @include xs-only {
+ display: flex;
+ }
+}
+
+.back-button {
+ background: transparent;
+ border:0;
+ cursor: pointer;
+ margin-right: 20px;
+
+ img {
+ width: 24px;
+ height: 24px;
+ display: block;
+ }
+}
+
+.navigator-text {
+ color: #fff;
+ font-weight: 700;
+}
+
+.header-picture {
+ display: flex;
+ position: absolute;
+ height: 320px;
+ overflow: hidden;
+ flex: 1;
+}
+
+.header-picture-crop {
+ display: flex;
+ position: absolute;
+ height: 400px;
+ top: -115px;
+ flex: 1;
+}
+
+.header-logos {
+ z-index: 100;
+ display: flex;
+ position: relative;
+ flex-direction: column;
+ align-items: center;
+ top: 90px;
+ width: 300px;
+ height: 200px;
+}
+
+.chrono-wallet-logo-bright {
+ display: flex;
+ width: 114px;
+ height: 117px;
+ margin-bottom: 40px;
+}
+
+.chrono-wallet-text-bright {
+ display: flex;
+ height: 59px;
+ width: 282px;
+}
+
.copyright {
max-width: $copyRightPadding;
font-size: 12px;
@@ -62,6 +137,25 @@ $copyRightPadding: 200px;
color: white;
}
+.create-title {
+ color: $color-white;
+ font-weight: 700;
+ font-size: 30px;
+ line-height: 42px;
+ margin-bottom: 10px;
+ text-align: center;
+}
+
+.create-title-description {
+ font-weight: 400;
+ color: $color-description;
+ font-size: 16px;
+ line-height: 22px;
+ margin-bottom: 10px;
+ text-align: center;
+ width: 520px;
+}
+
.links {
padding-right: $copyRightPadding; // compensation for centering
text-align: center;
@@ -79,14 +173,41 @@ $copyRightPadding: 200px;
}
}
-.link {
+.actions {
color: white;
+ text-align: center;
+ line-height: 30px;
+ margin-bottom: 45px;
+}
+
+.link {
+ color: $border-color;
margin: auto 45px;
text-decoration: none;
- font-family: $font-family-main;
- font-size: 18px;
+ font: 700 16px $font-proxima;
+
+ &:hover {
+ color: #FFB54E;
+ }
@include md-only {
margin: 0 0 10px;
}
}
+
+.fields-block {
+ margin: 0 auto 50px;
+ max-width: 300px;
+}
+
+.button {
+ margin-bottom: 25px;
+
+ button {
+ font: 700 16px $font-proxima;
+ line-height: 45px;
+ padding: 0 50px;
+ border-radius: 50px;
+ white-space: nowrap;
+ }
+}
diff --git a/src/layouts/Splash/styles.js b/src/layouts/Splash/styles.js
new file mode 100644
index 000000000..6bd9566ea
--- /dev/null
+++ b/src/layouts/Splash/styles.js
@@ -0,0 +1,35 @@
+export default {
+ textField: {
+ style: {
+ height: 62,
+ },
+ underlineStyle: {
+ borderColor: '#424066',
+ bottom: 0,
+ },
+ underlineFocusStyle: {
+ borderColor: '#FFB54E',
+ bottom: 0,
+ },
+ inputStyle: {
+ color: '#A3A3CC',
+ textAlign: 'center',
+ marginTop: 0,
+ paddingTop: 18,
+ },
+ floatingLabelStyle: {
+ color: '#A3A3CC',
+ top: 28,
+ left: 0,
+ right: 0,
+ margin: 'auto',
+ textAlign: 'center',
+ transformOrigin: 'center center',
+ },
+ errorStyle: {
+ bottom: 0,
+ marginTop: 5,
+ textAlign: 'center',
+ },
+ },
+}
diff --git a/src/layouts/partials/LocaleDropDown/LocaleDropDown.jsx b/src/layouts/partials/LocaleDropDown/LocaleDropDown.jsx
index e3a9c2228..4a695f630 100644
--- a/src/layouts/partials/LocaleDropDown/LocaleDropDown.jsx
+++ b/src/layouts/partials/LocaleDropDown/LocaleDropDown.jsx
@@ -10,6 +10,7 @@ import { connect } from 'react-redux'
import i18n from 'i18n'
import { Button } from 'components'
import { changeMomentLocale } from 'redux/ui/actions'
+import classnames from 'classnames'
import './LocaleDropDown.scss'
@@ -64,6 +65,7 @@ export default class LocaleDropDown extends PureComponent {
}
render () {
+ const { locale } = this.props
const locales = Object.entries(i18n).map(([ name, dictionary ]) => ({
name,
title: dictionary.title,
@@ -75,7 +77,7 @@ export default class LocaleDropDown extends PureComponent {
styleName='langButton'
onClick={this.handleClick}
>
- {this.props.locale}
+ {locale}
-
- {locales.map((item) => (
-
+ {locales.map((item, i) => (
+ this.handleChangeLocale(item.name)}
- value={item.name}
- key={item.name}
- primaryText={item.title}
- />
+ >
+ {item.title}
+
))}
-
+
)
diff --git a/src/layouts/partials/LocaleDropDown/LocaleDropDown.scss b/src/layouts/partials/LocaleDropDown/LocaleDropDown.scss
index 3fe03508c..da6e0f604 100644
--- a/src/layouts/partials/LocaleDropDown/LocaleDropDown.scss
+++ b/src/layouts/partials/LocaleDropDown/LocaleDropDown.scss
@@ -6,25 +6,44 @@
@import "~styles/partials/variables";
.LocaleDropDown {
+ border-radius: 20px;
+ border-top: 3px solid #E2A864;
+ overflow: hidden;
+ background: #fff;
+ margin-top: 10px;
+
@include xs-only {
width: 100%;
top: 10px;
}
}
+.LocaleDropDownItem {
+ padding: 15px 20px;
+ font-size: 14px;
+ font-family: Arial, Helvetica Neue, Helvetica, sans-serif;
+ cursor: pointer;
+
+ &:hover, &.LocaleDropDownItemActive {
+ background: #F2F2F2;
+ }
+}
+
.root {
}
.langButton {
button {
- color: $color-white;
- border-radius: 50%;
- width: 40px;
- height: 40px;
- margin: 5px;
- padding: 0;
- font-size: 20px;
- font-weight: lighter;
+ color: #5DB3ED;
+ border:1px solid #5DB3ED;
+ background: transparent;
+ font-size: 16px;
+ text-transform: uppercase;
+
+ &:hover {
+ color:#fff;
+ border-color: transparent;
+ }
}
}
diff --git a/src/layouts/partials/PersistWrapper/PersistWrapper.js b/src/layouts/partials/PersistWrapper/PersistWrapper.js
new file mode 100644
index 000000000..d0cab5d0e
--- /dev/null
+++ b/src/layouts/partials/PersistWrapper/PersistWrapper.js
@@ -0,0 +1,48 @@
+import PropTypes from 'prop-types'
+import React from 'react'
+import { connect } from 'react-redux'
+import spinner from 'assets/img/spinningwheel-1.gif'
+
+import './PersistWrapper.scss'
+
+const mapStateToProps = (state) => {
+ return {
+ rehydrated: state.get('persistAccount').rehydrated,
+ }
+}
+
+class PersistWrapper extends React.Component {
+ static propTypes = {
+ rehydrated: PropTypes.bool,
+ }
+
+ static contextTypes = {
+ store: PropTypes.object.isRequired,
+ }
+
+ constructor (props, context) {
+ super(props, context)
+ this.store = context.store
+ }
+
+ renderLoader(){
+ return (
+
+ {/*
Log In
*/}
+
+
+ )
+ }
+
+ render () {
+ const { rehydrated, children } = this.props
+ if (!rehydrated){
+ return this.renderLoader()
+ }
+
+ return children
+ }
+
+}
+
+export default connect(mapStateToProps, null)(PersistWrapper)
diff --git a/src/layouts/partials/PersistWrapper/PersistWrapper.scss b/src/layouts/partials/PersistWrapper/PersistWrapper.scss
new file mode 100644
index 000000000..1165ce933
--- /dev/null
+++ b/src/layouts/partials/PersistWrapper/PersistWrapper.scss
@@ -0,0 +1,23 @@
+.loadingMessage {
+ text-align: center;
+ padding: 40px 130px;
+ color: #fff;
+ font-size: 20px;
+ font-weight: bold;
+ line-height: 24px;
+ margin-bottom: 45px;
+
+ @media (max-width: 640px) {
+ padding: 40px 20px;
+ }
+
+
+}
+
+.loadingMessageHeader {
+ line-height: 24px;
+ font-weight: bold;
+ font-size: 20px;
+ color: #fff;
+ margin-bottom: 50px;
+}
diff --git a/src/layouts/partials/index.js b/src/layouts/partials/index.js
index 212fc811b..e0c00fb84 100644
--- a/src/layouts/partials/index.js
+++ b/src/layouts/partials/index.js
@@ -23,6 +23,7 @@ import DrawerMainMenu from './DrawerMainMenu/DrawerMainMenu'
import DepositsContent from './DepositsContent/DepositsContent'
import DepositContent from './DepositContent/DepositContent'
import TwoFAContent from './TwoFAContent/TwoFAContent'
+import PersistWrapper from './PersistWrapper/PersistWrapper'
export {
HeaderPartial,
@@ -45,6 +46,7 @@ export {
DepositContent,
AddWalletContent,
TwoFAContent,
+ PersistWrapper,
}
export default {
@@ -68,4 +70,5 @@ export default {
DepositContent,
AddWalletContent,
TwoFAContent,
+ PersistWrapper,
}
diff --git a/src/models/persistAccount/AbstractWalletModel.js b/src/models/persistAccount/AbstractWalletModel.js
new file mode 100644
index 000000000..92d878628
--- /dev/null
+++ b/src/models/persistAccount/AbstractWalletModel.js
@@ -0,0 +1,8 @@
+import PropTypes from 'prop-types'
+
+export default class AbstractWalletModel {
+ constructor (props, schema) {
+ PropTypes.checkPropTypes(schema, props, 'prop', '' + this.class)
+ Object.assign(this, props)
+ }
+}
diff --git a/src/models/persistAccount/SignerModel.js b/src/models/persistAccount/SignerModel.js
new file mode 100644
index 000000000..628ad2574
--- /dev/null
+++ b/src/models/persistAccount/SignerModel.js
@@ -0,0 +1,16 @@
+import PropTypes from 'prop-types'
+import AbstractWalletModel from './AbstractWalletModel'
+
+const schema = {
+ address: PropTypes.string.isRequired,
+ sign: PropTypes.func.isRequired,
+ signTransaction: PropTypes.func.isRequired,
+}
+
+export default class SignerModel extends AbstractWalletModel {
+ constructor (props) {
+ super(props, schema)
+ Object.assign(this, props)
+ Object.freeze(this)
+ }
+}
diff --git a/src/models/persistAccount/WalletEntryModel.js b/src/models/persistAccount/WalletEntryModel.js
new file mode 100644
index 000000000..a4b758aaf
--- /dev/null
+++ b/src/models/persistAccount/WalletEntryModel.js
@@ -0,0 +1,21 @@
+import PropTypes from 'prop-types'
+import AbstractWalletModel from './AbstractWalletModel'
+
+const schema = {
+ key: PropTypes.string,
+ name: PropTypes.string,
+ types: PropTypes.object,
+ encrypted: PropTypes.array,
+}
+
+export default class WalletEntryModel extends AbstractWalletModel {
+ constructor (props) {
+ super(props, schema)
+ Object.assign(this, {
+ key: '',
+ name: '',
+ types: {},
+ }, props)
+ Object.freeze(this)
+ }
+}
diff --git a/src/models/persistAccount/WalletModel.js b/src/models/persistAccount/WalletModel.js
new file mode 100644
index 000000000..1d8459a2b
--- /dev/null
+++ b/src/models/persistAccount/WalletModel.js
@@ -0,0 +1,26 @@
+import PropTypes from 'prop-types'
+import AbstractWalletModel from './AbstractWalletModel'
+import WalletEntryModel from './WalletEntryModel'
+import SignerModel from './SignerModel'
+
+const schema = {
+ wallet: PropTypes.object,
+ entry: PropTypes.instanceOf(WalletEntryModel),
+}
+
+export default class WalletModel extends AbstractWalletModel {
+ constructor (props) {
+ super(props, schema)
+ Object.assign(this, props)
+ Object.freeze(this)
+ }
+
+ get signer () {
+ // TODO @ipavlenko: Implement custom signers for hardware tokens
+ return new SignerModel({
+ address: this.wallet[0].address.toLowerCase(),
+ sign: this.wallet[0].sign,
+ signTransaction: this.wallet[0].signTransaction,
+ })
+ }
+}
diff --git a/src/models/persistAccount/index.js b/src/models/persistAccount/index.js
new file mode 100644
index 000000000..569753291
--- /dev/null
+++ b/src/models/persistAccount/index.js
@@ -0,0 +1,11 @@
+import AbstractWalletModel from './AbstractWalletModel'
+import SignerModel from './SignerModel'
+import WalletEntryModel from './WalletEntryModel'
+import WalletModel from './WalletModel'
+
+export {
+ AbstractWalletModel,
+ SignerModel,
+ WalletEntryModel,
+ WalletModel,
+}
diff --git a/src/models/persistWallet/AbstractWalletModel.js b/src/models/persistWallet/AbstractWalletModel.js
new file mode 100644
index 000000000..92d878628
--- /dev/null
+++ b/src/models/persistWallet/AbstractWalletModel.js
@@ -0,0 +1,8 @@
+import PropTypes from 'prop-types'
+
+export default class AbstractWalletModel {
+ constructor (props, schema) {
+ PropTypes.checkPropTypes(schema, props, 'prop', '' + this.class)
+ Object.assign(this, props)
+ }
+}
diff --git a/src/models/persistWallet/SignerModel.js b/src/models/persistWallet/SignerModel.js
new file mode 100644
index 000000000..628ad2574
--- /dev/null
+++ b/src/models/persistWallet/SignerModel.js
@@ -0,0 +1,16 @@
+import PropTypes from 'prop-types'
+import AbstractWalletModel from './AbstractWalletModel'
+
+const schema = {
+ address: PropTypes.string.isRequired,
+ sign: PropTypes.func.isRequired,
+ signTransaction: PropTypes.func.isRequired,
+}
+
+export default class SignerModel extends AbstractWalletModel {
+ constructor (props) {
+ super(props, schema)
+ Object.assign(this, props)
+ Object.freeze(this)
+ }
+}
diff --git a/src/models/persistWallet/WalletEntryModel.js b/src/models/persistWallet/WalletEntryModel.js
new file mode 100644
index 000000000..a4b758aaf
--- /dev/null
+++ b/src/models/persistWallet/WalletEntryModel.js
@@ -0,0 +1,21 @@
+import PropTypes from 'prop-types'
+import AbstractWalletModel from './AbstractWalletModel'
+
+const schema = {
+ key: PropTypes.string,
+ name: PropTypes.string,
+ types: PropTypes.object,
+ encrypted: PropTypes.array,
+}
+
+export default class WalletEntryModel extends AbstractWalletModel {
+ constructor (props) {
+ super(props, schema)
+ Object.assign(this, {
+ key: '',
+ name: '',
+ types: {},
+ }, props)
+ Object.freeze(this)
+ }
+}
diff --git a/src/models/persistWallet/WalletModel.js b/src/models/persistWallet/WalletModel.js
new file mode 100644
index 000000000..1d8459a2b
--- /dev/null
+++ b/src/models/persistWallet/WalletModel.js
@@ -0,0 +1,26 @@
+import PropTypes from 'prop-types'
+import AbstractWalletModel from './AbstractWalletModel'
+import WalletEntryModel from './WalletEntryModel'
+import SignerModel from './SignerModel'
+
+const schema = {
+ wallet: PropTypes.object,
+ entry: PropTypes.instanceOf(WalletEntryModel),
+}
+
+export default class WalletModel extends AbstractWalletModel {
+ constructor (props) {
+ super(props, schema)
+ Object.assign(this, props)
+ Object.freeze(this)
+ }
+
+ get signer () {
+ // TODO @ipavlenko: Implement custom signers for hardware tokens
+ return new SignerModel({
+ address: this.wallet[0].address.toLowerCase(),
+ sign: this.wallet[0].sign,
+ signTransaction: this.wallet[0].signTransaction,
+ })
+ }
+}
diff --git a/src/models/persistWallet/index.js b/src/models/persistWallet/index.js
new file mode 100644
index 000000000..569753291
--- /dev/null
+++ b/src/models/persistWallet/index.js
@@ -0,0 +1,11 @@
+import AbstractWalletModel from './AbstractWalletModel'
+import SignerModel from './SignerModel'
+import WalletEntryModel from './WalletEntryModel'
+import WalletModel from './WalletModel'
+
+export {
+ AbstractWalletModel,
+ SignerModel,
+ WalletEntryModel,
+ WalletModel,
+}
diff --git a/src/pages/ConfirmMnemonicPage/ConfirmMnemonicPage.js b/src/pages/ConfirmMnemonicPage/ConfirmMnemonicPage.js
new file mode 100644
index 000000000..37e9ef3ce
--- /dev/null
+++ b/src/pages/ConfirmMnemonicPage/ConfirmMnemonicPage.js
@@ -0,0 +1,184 @@
+/**
+ * Copyright 2017–2018, LaborX PTY
+ * Licensed under the AGPL Version 3 license.
+ */
+
+import PropTypes from 'prop-types'
+import classnames from 'classnames'
+import { connect } from 'react-redux'
+import { push } from 'react-router-redux'
+import { MuiThemeProvider } from 'material-ui'
+import { reduxForm, Field } from 'redux-form/immutable'
+import React, { Component } from 'react'
+import { Link } from 'react-router'
+import { Button } from 'components'
+import {
+ initConfirmMnemonicPage,
+ navigateToConfirmMnemonicPage,
+ onSubmitConfirmMnemonic,
+ onSubmitConfirmMnemonicSuccess,
+ onSubmitConfirmMnemonicFail,
+} from '@chronobank/login/redux/network/actions'
+
+import './ConfirmMnemonicPage.scss'
+import { FORM_CREATE_ACCOUNT } from "../CreateAccountPage/CreateAccountPage";
+
+export const FORM_CONFIRM_MNEMONIC = 'ConfirmMnemonicForm'
+
+function mapStateToProps (state) {
+
+ return {
+ mnemonic: state.get('network').newAccountMnemonic,
+ }
+}
+
+function mapDispatchToProps (dispatch) {
+ return {
+ navigateToConfirmPage: () => dispatch(navigateToConfirmMnemonicPage()),
+ initConfirmMnemonicPage: () => dispatch(initConfirmMnemonicPage()),
+ onSubmit: (values) => {
+ const confirmMnemonic = values.get('mnemonic')
+
+ dispatch(onSubmitConfirmMnemonic(confirmMnemonic))
+ },
+ onSubmitSuccess: () => dispatch(onSubmitConfirmMnemonicSuccess()),
+ onSubmitFail: (errors, dispatch, submitErrors) => dispatch(onSubmitConfirmMnemonicFail(errors, dispatch, submitErrors)),
+ }
+}
+
+class ConfirmMnemonicPage extends Component {
+ static propTypes = {
+ mnemonic: PropTypes.string,
+ initConfirmMnemonicPage: PropTypes.func,
+ }
+
+ static defaultProps = {
+ mnemonic: '',
+ }
+
+ constructor (props){
+ super(props)
+
+ const wordsArray = props.mnemonic ?
+ props.mnemonic.split(' ').map((word, index) => {
+ return { index, word }
+ }) : []
+
+ this.state = {
+ confirmPhrase: [],
+ currentWordsArray: wordsArray.sort((a,b) => a.word < b.word),
+ }
+ }
+
+ componentDidMount(){
+ this.props.initConfirmMnemonicPage()
+ }
+
+ getCurrentMnemonic (){
+ return this.state.confirmPhrase.map((item) => item.word).join(' ')
+ }
+
+ getWordsButtons (){
+ return this.state.currentWordsArray.map((item, index) => {
+ const wordSelected = this.state.confirmPhrase.includes(item)
+
+ return (
+
+ { item.word }
+
+ )}
+ )
+ }
+
+ onClickWord (word, e){
+ const { dispatch, change } = this.props
+
+ if (!this.state.confirmPhrase.includes(word)) {
+ this.setState(
+ { confirmPhrase: this.state.confirmPhrase.concat(word) },
+ () => change('mnemonic', this.getCurrentMnemonic())
+ )
+ }
+ }
+
+ clearMnemonic (){
+ const { dispatch, change } = this.props
+
+ this.setState(
+ { confirmPhrase: [] },
+ () => change('mnemonic', this.getCurrentMnemonic())
+ )
+ }
+
+ clearLastWord (){
+ const { dispatch, change } = this.props
+
+ this.setState(
+ { confirmPhrase: this.state.confirmPhrase.slice(0, -1) },
+ () => change('mnemonic', this.getCurrentMnemonic())
+ )
+ }
+
+ render () {
+ const { handleSubmit, error } = this.props
+ console.log('confirm mnemonic page', this.props)
+
+ return (
+
+
+
+ )
+ }
+}
+
+const form = reduxForm({ form: FORM_CONFIRM_MNEMONIC })(ConfirmMnemonicPage)
+export default connect(mapStateToProps, mapDispatchToProps)(form)
diff --git a/src/pages/ConfirmMnemonicPage/ConfirmMnemonicPage.scss b/src/pages/ConfirmMnemonicPage/ConfirmMnemonicPage.scss
new file mode 100644
index 000000000..50acb912b
--- /dev/null
+++ b/src/pages/ConfirmMnemonicPage/ConfirmMnemonicPage.scss
@@ -0,0 +1,216 @@
+/**
+ * Copyright 2017–2018, LaborX PTY
+ * Licensed under the AGPL Version 3 license.
+ */
+
+@import "~styles/partials/variables";
+@import "~styles/partials/mixins";
+
+
+.page-title {
+ font-size: 30px;
+ margin-bottom: 10px;
+ font-weight: 700;
+}
+
+.description {
+ margin-bottom: 30px;
+ color: $additionalData-color-1;
+
+}
+
+.passPhraseWrapper {
+ position: relative;
+ margin-bottom: 20px;
+ background: $background-color-1;
+ min-height: 100px;
+ border-radius: 2px;
+ padding: 20px 45px 20px 30px;
+}
+
+.passPhrase {
+ font-size: 16px;
+ font-weight: 700;
+ line-height: 24px;
+ color: $button-color-2;
+ text-align: left;
+ min-height: 46px;
+ width: 100%;
+
+ @include md-only {
+ min-height: 70px;
+ color: $synced-color;
+ }
+}
+
+.wordsBlock {
+ display: flex;
+ justify-content: space-between;
+ flex-wrap: wrap;
+}
+
+.word {
+ width: 23%;
+ margin-bottom: 10px;
+
+ button {
+ white-space: nowrap;
+ width: 100%;
+ cursor: pointer;
+ user-select: none;
+ color: #FFFFFF;
+ background-color: #614DBA;
+ text-transform: lowercase;
+ line-height: 62px;
+ border-radius: 2px;
+ font-size: 16px;
+ font-weight: 700;
+ padding: 12px 0;
+ }
+
+ &:hover {
+ background-color: $button-color-2;
+ color: $background-color-1;
+ }
+
+ @include md-only {
+ width: 31%;
+ }
+}
+
+.submitButton {
+ background: #00A0D2;
+ font-size: 14px;
+ font-weight: 500;
+ color: #fff;
+ border-radius: 21px;
+ cursor: pointer;
+ line-height: 40px;
+ height: 40px;
+ min-width: 150px;
+ border: none;
+ margin-bottom: 40px;
+ padding: 0;
+ box-shadow: none;
+
+ &:disabled {
+ cursor: not-allowed;
+ }
+
+ @include md-only {
+ min-width: 180px;
+ }
+
+ &:hover {
+ background: #0088C3;
+ }
+}
+
+.progressBlock {
+ width: 50px;
+ margin: 0 auto 20px;
+ display: flex;
+ justify-content: space-between;
+}
+
+.progressPoint {
+ background: $button-color-2;
+ width: 10px;
+ height: 10px;
+ border-radius: 50%;
+
+ &.progressPointInactive {
+ background: $color-blue-4;
+ }
+}
+
+.clearMnemonic {
+ width: 23px;
+ height: 23px;
+ display: inline-block;
+ position: absolute;
+ right: 0;
+ top: 0;
+ bottom: 0;
+ margin: auto;
+ padding: 11px;
+ cursor: pointer;
+ box-sizing: content-box;
+
+ img {
+ width: 100%;
+ }
+}
+
+.controlsBlock {
+ display: flex;
+ justify-content: space-between;
+ margin-bottom: 35px;
+
+ .control {
+ width: 49%;
+ line-height: 42px;
+ font-size: 16px;
+ border: 1px solid $color-blue-4;
+ border-radius: 2px;
+ color: $color-blue-4;
+ cursor: pointer;
+ font-weight: 700;
+
+ &:hover {
+ background: $color-hover;
+ color: $background-color-1;
+ border: 0;
+ }
+ }
+
+}
+
+.form {
+ text-align: center;
+ width: 600px;
+ margin: 0 auto;
+ color: #fff;
+
+ @include md-only {
+ width: auto;
+ padding: 0 20px;
+ }
+}
+
+.actions {
+ text-align: center;
+ margin-bottom: 45px;
+ color: #fff;
+ line-height: 30px;
+}
+
+.link {
+ color: $border-color;
+ margin: 0 auto 45px;
+ text-decoration: none;
+ font: 700 16px $font-proxima;
+
+ &:hover {
+ color: #FFB54E;
+ }
+
+ @include md-only {
+ margin: 0 0 10px;
+ }
+}
+
+.submit {
+ margin-bottom: 25px;
+}
+
+.error {
+ color: red;
+ margin-bottom: 20px;
+ display: none;
+ text-align: center;
+}
+
+.visible {
+ display: block;
+}
diff --git a/src/pages/CreateAccountPage/CreateAccountPage.js b/src/pages/CreateAccountPage/CreateAccountPage.js
new file mode 100644
index 000000000..f254f2c62
--- /dev/null
+++ b/src/pages/CreateAccountPage/CreateAccountPage.js
@@ -0,0 +1,177 @@
+/**
+ * Copyright 2017–2018, LaborX PTY
+ * Licensed under the AGPL Version 3 license.
+ */
+
+import PropTypes from 'prop-types'
+import { MuiThemeProvider } from 'material-ui'
+import React, { PureComponent } from 'react'
+import { connect } from 'react-redux'
+import { Link } from 'react-router'
+import { reduxForm, Field } from 'redux-form/immutable'
+import { TextField } from 'redux-form-material-ui'
+
+import { Button } from 'components'
+import {
+ onSubmitCreateAccountPage,
+ onSubmitCreateAccountPageSuccess,
+ onSubmitCreateAccountPageFail,
+} from '@chronobank/login/redux/network/actions'
+import AutomaticProviderSelector from '@chronobank/login-ui/components/ProviderSelectorSwitcher/AutomaticProviderSelector'
+import ManualProviderSelector from '@chronobank/login-ui/components/ProviderSelectorSwitcher/ManualProviderSelector'
+import web3Provider from '@chronobank/login/network/Web3Provider'
+
+import validate from './validate'
+
+import styles from 'layouts/Splash/styles'
+import fieldStyles from './styles'
+import './CreateAccountPage.scss'
+
+const STRATEGY_MANUAL = 'manual'
+const STRATEGY_AUTOMATIC = 'automatic'
+
+const nextStrategy = {
+ [STRATEGY_AUTOMATIC]: STRATEGY_MANUAL,
+ [STRATEGY_MANUAL]: STRATEGY_AUTOMATIC,
+}
+
+export const FORM_CREATE_ACCOUNT = 'CreateAccountForm'
+
+function mapStateToProps (state, ownProps) {
+
+ return {
+ isImportMode: state.get('network').importAccountMode,
+ }
+}
+
+function mapDispatchToProps (dispatch, ownProps) {
+ return {
+ onSubmit: async (values) => {
+ const walletName = values.get('walletName')
+ const password = values.get('password')
+
+ await dispatch(onSubmitCreateAccountPage(walletName, password))
+ },
+ onSubmitSuccess: () => dispatch(onSubmitCreateAccountPageSuccess()),
+ onSubmitFail: (errors, dispatch, submitErrors) => dispatch(onSubmitCreateAccountPageFail(errors, dispatch, submitErrors)),
+ }
+}
+
+class CreateAccountPage extends PureComponent {
+ static propTypes = {
+ isImportMode: PropTypes.bool,
+ }
+
+ constructor(){
+ super()
+
+ this.state = {
+ isShowProvider: true,
+ strategy: STRATEGY_AUTOMATIC,
+ }
+ }
+
+ handleToggleProvider = (isShowProvider) => this.setState({ isShowProvider })
+
+ handleSelectorSwitch = (currentStrategy) => this.setState({ strategy: nextStrategy[currentStrategy] })
+
+ renderProviderSelector () {
+ switch (this.state.strategy) {
+ case STRATEGY_MANUAL:
+ return this.renderManualProviderSelector()
+ case STRATEGY_AUTOMATIC:
+ return this.renderAutomaticProviderSelector()
+ default:
+ return null
+ }
+ }
+
+ renderAutomaticProviderSelector () {
+ return (
+
+ )
+ }
+
+ renderManualProviderSelector () {
+ return (
+
+ )
+ }
+
+ render () {
+ const { handleSubmit, pristine, valid, initialValues, isImportMode } = this.props
+
+ return (
+
+
+
+
+ )
+ }
+}
+
+const form = reduxForm({ form: FORM_CREATE_ACCOUNT, validate })(CreateAccountPage)
+export default connect(mapStateToProps, mapDispatchToProps)(form)
+
diff --git a/src/pages/CreateAccountPage/CreateAccountPage.scss b/src/pages/CreateAccountPage/CreateAccountPage.scss
new file mode 100644
index 000000000..3048ae140
--- /dev/null
+++ b/src/pages/CreateAccountPage/CreateAccountPage.scss
@@ -0,0 +1,197 @@
+/**
+ * Copyright 2017–2018, LaborX PTY
+ * Licensed under the AGPL Version 3 license.
+ */
+@import "~styles/partials/mixins";
+@import "~styles/partials/variables";
+
+$copyRightPadding: 200px;
+
+.root {
+ background-color: #242045;
+ position: relative;
+ display: flex;
+ min-height: 100vh;
+ flex-direction: column;
+ font-family: $font-proxima;
+
+ @include md-only {
+ min-height: 100vh;
+ padding: 0;
+ }
+
+ &:after {
+ // hack for preloading material icons and avoid FUOC on render
+ content: '';
+ font-family: 'Material Icons';
+ }
+}
+
+.form {
+ flex: 1;
+ justify-content: center;
+ flex-direction: column;
+ display: flex;
+ margin: 0 auto;
+
+ @include xs-only {
+ padding: 0 20px;
+ }
+}
+
+.content {
+ display: flex;
+ flex: 1;
+ justify-content: center;
+ flex-direction: column;
+ margin: 0 auto;
+
+ @include xs-only {
+ padding: 80px 20px 0;
+ }
+}
+
+.footer {
+ display: flex;
+ background-color: rgba(255, 255, 255, 0.1);
+ bottom: 0;
+ left: 0;
+ right: 0;
+ position: absolute;
+ padding: 17px 80px;
+
+ @include md-only {
+ padding: 20px;
+ flex-direction: column-reverse;
+ }
+}
+
+.header-container {
+ display: flex;
+ justify-content: center;
+ height: 340px;
+ overflow: hidden;
+ margin-bottom: 20px;
+
+ @include md-only {
+ display: none;
+ }
+}
+
+.header-picture {
+ display: flex;
+ position: absolute;
+ height: 320px;
+ overflow: hidden;
+ flex: 1;
+}
+
+.header-picture-crop {
+ display: flex;
+ position: absolute;
+ height: 400px;
+ top: -115px;
+ flex: 1;
+}
+
+.header-logos {
+ z-index: 100;
+ display: flex;
+ position: relative;
+ flex-direction: column;
+ align-items: center;
+ top: 90px;
+ width: 300px;
+ height: 200px;
+}
+
+.chrono-wallet-logo-bright {
+ display: flex;
+ width: 114px;
+ height: 117px;
+ margin-bottom: 40px;
+}
+
+.chrono-wallet-text-bright {
+ display: flex;
+ height: 59px;
+ width: 282px;
+}
+
+.copyright {
+ max-width: $copyRightPadding;
+ font-size: 12px;
+ font-weight: 300;
+ color: white;
+}
+
+.create-title {
+ color: $color-white;
+ font-weight: 700;
+ font-size: 30px;
+ line-height: 42px;
+ margin-bottom: 10px;
+ text-align: center;
+}
+
+.create-title-description {
+ font-weight: 400;
+ color: $color-description;
+ font-size: 16px;
+ line-height: 22px;
+ margin-bottom: 10px;
+ text-align: center;
+ max-width: 520px;
+}
+
+.links {
+ padding-right: $copyRightPadding; // compensation for centering
+ text-align: center;
+ flex-grow: 1;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+
+ @include md-only {
+ padding: 0;
+ display: flex;
+ flex-direction: column;
+ justify-content: flex-start;
+ align-items: flex-start;
+ }
+}
+
+.actions {
+ color: white;
+ text-align: center;
+ line-height: 30px;
+ margin-bottom: 45px;
+}
+
+.link {
+ color: $border-color;
+ margin: auto 45px;
+ text-decoration: none;
+ font: 700 16px $font-proxima;
+
+ &:hover {
+ color: #FFB54E;
+ }
+
+ @include md-only {
+ margin: 0 0 10px;
+ }
+}
+
+.fields-block {
+ margin: 0 auto 50px;
+ max-width: 300px;
+}
+
+.button {
+ margin-bottom: 25px;
+}
+
+.selector {
+ color: #fff;
+}
diff --git a/src/pages/CreateAccountPage/styles.js b/src/pages/CreateAccountPage/styles.js
new file mode 100644
index 000000000..265d41576
--- /dev/null
+++ b/src/pages/CreateAccountPage/styles.js
@@ -0,0 +1,8 @@
+export default {
+ textField: {
+ underlineStyle: {
+ borderColor: '#A3A3CC',
+ bottom: 0,
+ },
+ },
+}
diff --git a/src/pages/CreateAccountPage/validate.js b/src/pages/CreateAccountPage/validate.js
new file mode 100644
index 000000000..183b6eb86
--- /dev/null
+++ b/src/pages/CreateAccountPage/validate.js
@@ -0,0 +1,32 @@
+/**
+ * Copyright 2017–2018, LaborX PTY
+ * Licensed under the AGPL Version 3 license.
+ */
+
+import ErrorList from 'platform/ErrorList'
+import * as validator from 'models/validator'
+
+const validateEqualPasswords = (password, confirmPassword) => password === confirmPassword ? null : 'Wrong password'
+
+export default (values) => {
+ const walletName = values.get('walletName')
+
+ let walletNameErrors = new ErrorList()
+ walletNameErrors.add(validator.required(walletName))
+
+ const password = values.get('password')
+
+ let passwordErrors = new ErrorList()
+ passwordErrors.add(validator.required(password))
+
+ const confirmPassword = values.get('confirmPassword')
+ let confirmPasswordErrors = new ErrorList()
+ confirmPasswordErrors.add(validator.required(confirmPassword))
+ confirmPasswordErrors.add(validateEqualPasswords(password, confirmPassword))
+
+ return {
+ walletName: walletNameErrors.getErrors(),
+ password: passwordErrors.getErrors(),
+ confirmPassword: confirmPasswordErrors.getErrors(),
+ }
+}
diff --git a/src/pages/DownloadWalletFilePage/DownloadWalletFilePage.js b/src/pages/DownloadWalletFilePage/DownloadWalletFilePage.js
new file mode 100644
index 000000000..76195d515
--- /dev/null
+++ b/src/pages/DownloadWalletFilePage/DownloadWalletFilePage.js
@@ -0,0 +1,83 @@
+/**
+ * Copyright 2017–2018, LaborX PTY
+ * Licensed under the AGPL Version 3 license.
+ */
+
+import PropTypes from 'prop-types'
+import classnames from 'classnames'
+import { MuiThemeProvider } from 'material-ui'
+import { reduxForm, Field } from 'redux-form/immutable'
+import { connect } from 'react-redux'
+import React, { Component } from 'react'
+import { Link } from 'react-router'
+import { Button } from 'components'
+import {
+ downloadWallet,
+} from 'redux/persistAccount/actions'
+import {
+ navigateToLoginPage,
+} from '@chronobank/login/redux/network/actions'
+
+import Wallet from 'assets/img/icons/wallet-white.svg'
+
+import './DownloadWalletFilePage.scss'
+
+function mapDispatchToProps (dispatch) {
+ return {
+ downloadWallet: () => dispatch(downloadWallet()),
+ navigateToLoginPage: () => dispatch(navigateToLoginPage()),
+ }
+}
+
+@connect(null, mapDispatchToProps)
+export default class MnemonicPage extends Component {
+ static propTypes = {
+ downloadWallet: PropTypes.func,
+ navigateToLoginPage: PropTypes.func,
+ }
+
+ render () {
+ const { downloadWallet, navigateToLoginPage } = this.props
+
+ return (
+
+
+
+
Download a Wallet File
+
+
+ You can use this wallet file in password recovery option to
+ make your account available in another browser, for example.
+ The file is protected by the same password as your created before.
+
+
+
+
+
+
+ Download
+
+
+
+ Finish
+
+
+
+
+
+
+
+ )
+ }
+}
diff --git a/src/pages/DownloadWalletFilePage/DownloadWalletFilePage.scss b/src/pages/DownloadWalletFilePage/DownloadWalletFilePage.scss
new file mode 100644
index 000000000..ed40e91e3
--- /dev/null
+++ b/src/pages/DownloadWalletFilePage/DownloadWalletFilePage.scss
@@ -0,0 +1,109 @@
+/**
+ * Copyright 2017–2018, LaborX PTY
+ * Licensed under the AGPL Version 3 license.
+ */
+
+@import "~styles/partials/variables";
+@import "~styles/partials/mixins";
+
+.wrapper {
+ text-align: center;
+ max-width: 600px;
+ margin: 0 auto;
+ color: #fff;
+
+ @include md-only {
+ padding: 0 20px;
+ }
+}
+
+.page-title {
+ font-size: 30px;
+ margin-bottom: 10px;
+ font-weight: 700;
+}
+
+.description {
+ margin-bottom: 50px;
+ color: $additionalData-color-1;
+ line-height: 22px;
+ font-weight: 400;
+
+ @include md-only {
+ margin-bottom: 20px;
+ }
+}
+
+.progress-block {
+ width: 50px;
+ margin: 0 auto 20px;
+ display: flex;
+ justify-content: space-between;
+}
+
+.progress-point {
+ background: $button-color-2;
+ width: 10px;
+ height: 10px;
+ border-radius: 50%;
+
+ &.progress-point-inactive {
+ background: $color-blue-4;
+ }
+}
+
+.row {
+ margin: 0 auto 50px;
+}
+
+.actions {
+ text-align: center;
+ margin-bottom: 45px;
+ color: #fff;
+ line-height: 30px;
+}
+
+.link {
+ color: $border-color;
+ margin: 0 auto 45px;
+ text-decoration: none;
+ font: 700 16px $font-proxima;
+
+ &:hover {
+ color: #FFB54E;
+ }
+
+ @include md-only {
+ margin: 0 0 10px;
+ }
+}
+
+.submit {
+ margin-bottom: 25px;
+}
+
+.wallet-img {
+ width: 48px;
+ height: 48px;
+ display: block;
+ margin: 0 auto;
+}
+
+.button {
+ text-align: center;
+ margin-bottom: 50px;
+
+ button {
+ font-size: 14px;
+ text-transform: none;
+ width: 110px;
+ height: 100px;
+ padding: 0;
+ border-radius: 3px;
+ }
+
+ img {
+ width: 50px;
+ margin-bottom: 5px;
+ }
+}
diff --git a/src/pages/ImportMethodsPage/ImportMethodsPage.js b/src/pages/ImportMethodsPage/ImportMethodsPage.js
new file mode 100644
index 000000000..1652d1559
--- /dev/null
+++ b/src/pages/ImportMethodsPage/ImportMethodsPage.js
@@ -0,0 +1,136 @@
+/**
+ * Copyright 2017–2018, LaborX PTY
+ * Licensed under the AGPL Version 3 license.
+ */
+
+import PropTypes from 'prop-types'
+import { MuiThemeProvider } from 'material-ui'
+import React, { PureComponent } from 'react'
+import { Link } from 'react-router'
+import { connect } from 'react-redux'
+import { Button } from 'components'
+
+import {
+ navigateToMnemonicImportMethod,
+ navigateToPrivateKeyImportMethod,
+ navigateToCreateAccount,
+ initImportMethodsPage,
+ navigateToCreateAccountWithoutImport,
+} from '@chronobank/login/redux/network/actions'
+
+import Trezor from 'assets/img/icons/trezor-white.svg'
+import Ledger from 'assets/img/icons/ledger-nano-white.svg'
+import Plugin from 'assets/img/icons/plugin-white.svg'
+import Mnemonic from 'assets/img/icons/mnemonic-white.svg'
+import Key from 'assets/img/icons/key-white.svg'
+import Wallet from 'assets/img/icons/wallet-white.svg'
+import Uport from 'assets/img/icons/uport.svg'
+
+// import styles from 'layouts/Splash/styles'
+import './ImportMethodsPage.scss'
+
+function mapDispatchToProps (dispatch) {
+ return {
+ navigateToMnemonicImportMethod: () => dispatch(navigateToMnemonicImportMethod()),
+ navigateToPrivateKeyImportMethod: () => dispatch(navigateToPrivateKeyImportMethod()),
+ navigateToCreateAccount: () => dispatch(navigateToCreateAccount()),
+ navigateToCreateAccountWithoutImport: () => dispatch(navigateToCreateAccountWithoutImport()),
+ initImportMethodsPage: () => dispatch(initImportMethodsPage()),
+ }
+}
+
+@connect(null, mapDispatchToProps)
+export default class ImportMethodsPage extends PureComponent {
+ static propTypes = {
+ navigateToMnemonicImportMethod: PropTypes.func,
+ navigateToPrivateKeyImportMethod: PropTypes.func,
+ initImportMethodsPage: PropTypes.func,
+ navigateToCreateAccountWithoutImport: PropTypes.func,
+ }
+
+ componentDidMount(){
+ this.props.initImportMethodsPage()
+ }
+
+ handleMnemonicLogin = () => this.props.navigateToMnemonicImportMethod()
+
+ handlePrivateKeyLogin = () => this.props.navigateToPrivateKeyImportMethod()
+
+ handleWalletFileLogin = () => {}
+
+ handleCreateAccount = () => this.props.navigateToCreateAccountWithoutImport()
+
+ render () {
+ return (
+
+
+
+
Add an Existing Account
+
+
+
+
+
+ Trezor
+
+
+
+
+
+ LedgerNano
+
+
+
+
+
+ Browser Plugin
+
+
+
+
+
+ Mnemonic
+
+
+
+
+
+ Private Key
+
+
+
+
+
+ Wallet File
+
+
+
+
+
+ Uport
+
+
+
+
+ or
+ Create New Account
+
+
+
+
+ )
+ }
+}
diff --git a/src/pages/ImportMethodsPage/ImportMethodsPage.scss b/src/pages/ImportMethodsPage/ImportMethodsPage.scss
new file mode 100644
index 000000000..d18a725c9
--- /dev/null
+++ b/src/pages/ImportMethodsPage/ImportMethodsPage.scss
@@ -0,0 +1,124 @@
+/**
+ * Copyright 2017–2018, LaborX PTY
+ * Licensed under the AGPL Version 3 license.
+ */
+
+@import "~styles/partials/variables";
+@import "~styles/partials/mixins";
+
+ul.actions {
+ position: absolute;
+ top: 30px;
+ right: 80px;
+ display: flex;
+ justify-content: flex-end;
+ flex: 0 0 auto;
+ padding: 0;
+ list-style: none;
+ font-size: 18px;
+ line-height: 48px;
+
+ @include md-only {
+ top: 10px;
+ right: 10px;
+ flex: 1 1 auto;
+ }
+
+ li {
+
+ flex: 0 0 auto;
+
+ a {
+ padding: 10px 20px;
+ text-decoration: none;
+ color: $color-white;
+
+ &:hover {
+ text-decoration: underline;
+ }
+ }
+ }
+}
+
+.page {
+ max-width: 350px;
+ margin:0 auto;
+
+}
+
+.page-title {
+ color: $color-white;
+ font-weight: 700;
+ font-size: 30px;
+ line-height: 42px;
+ margin-bottom: 40px;
+ text-align: center;
+}
+
+.user-row {
+ border-top: 1px solid $color-purpule;
+}
+
+.methods {
+ margin-bottom: 15px;
+ display: flex;
+ justify-content: space-around;
+ flex-wrap: wrap;
+}
+
+.button {
+ width: 31%;
+ margin-bottom: 10px;
+
+ &.button-uport {
+ display: none;
+
+ @include xs-only {
+ display: inline-block;
+ }
+ }
+
+ &.button-trezor,
+ &.button-ledger,
+ &.button-plugin {
+ @include xs-only {
+ display: none;
+ }
+ }
+
+ button {
+ width: 100%;
+ height: 100px;
+ padding: 0;
+ text-transform: none;
+ border-radius: 3px;
+ }
+
+ img {
+ width: 50px;
+ margin-bottom: 5px;
+ }
+}
+
+.actions {
+ text-align: center;
+ margin-bottom: 45px;
+ color: #fff;
+ line-height: 30px;
+}
+
+.link {
+ color: $border-color;
+ margin: 0 auto 45px;
+ text-decoration: none;
+ cursor: pointer;
+ font: 700 16px $font-proxima;
+
+ &:hover {
+ color: #FFB54E;
+ }
+
+ @include md-only {
+ margin: 0 0 10px;
+ }
+}
diff --git a/src/pages/LoginMethods/Mnemonic/Mnemonic.js b/src/pages/LoginMethods/Mnemonic/Mnemonic.js
new file mode 100644
index 000000000..4103884c4
--- /dev/null
+++ b/src/pages/LoginMethods/Mnemonic/Mnemonic.js
@@ -0,0 +1,76 @@
+/**
+ * Copyright 2017–2018, LaborX PTY
+ * Licensed under the AGPL Version 3 license.
+ */
+
+import { MuiThemeProvider } from 'material-ui'
+import React, { PureComponent } from 'react'
+import { connect } from 'react-redux'
+import { reduxForm, Field } from 'redux-form/immutable'
+import { Link } from 'react-router'
+import { TextField } from 'redux-form-material-ui'
+import styles from 'layouts/Splash/styles'
+import { Button } from 'components'
+import {
+ onSubmitMnemonicLoginForm,
+ onSubmitMnemonicLoginFormSuccess,
+ onSubmitMnemonicLoginFormFail,
+} from '@chronobank/login/redux/network/actions'
+
+import './Mnemonic.scss'
+import { FORM_LOGIN_PAGE } from "../../LoginPage/LoginPage";
+
+export const FORM_MNEMONIC_LOGIN_PAGE = 'MnemonicLoginPageForm'
+
+function mapDispatchToProps (dispatch) {
+ return {
+ onSubmit: (values) => {
+ const confirmMnemonic = values.get('mnemonic')
+ dispatch(onSubmitMnemonicLoginForm(confirmMnemonic))
+ },
+ onSubmitSuccess: () => dispatch(onSubmitMnemonicLoginFormSuccess()),
+ onSubmitFail: () => dispatch(onSubmitMnemonicLoginFormFail()),
+ }
+}
+
+class MnemonicLoginPage extends PureComponent {
+ render () {
+ const { handleSubmit } = this.props
+
+ return (
+
+
+
+ )
+ }
+}
+
+const form = reduxForm({ form: FORM_MNEMONIC_LOGIN_PAGE })(MnemonicLoginPage)
+export default connect(null, mapDispatchToProps)(form)
diff --git a/src/pages/LoginMethods/Mnemonic/Mnemonic.scss b/src/pages/LoginMethods/Mnemonic/Mnemonic.scss
new file mode 100644
index 000000000..6d524ee18
--- /dev/null
+++ b/src/pages/LoginMethods/Mnemonic/Mnemonic.scss
@@ -0,0 +1,94 @@
+/**
+ * Copyright 2017–2018, LaborX PTY
+ * Licensed under the AGPL Version 3 license.
+ */
+
+@import "~styles/partials/variables";
+@import "~styles/partials/mixins";
+
+ul.actions {
+ position: absolute;
+ top: 30px;
+ right: 80px;
+ display: flex;
+ justify-content: flex-end;
+ flex: 0 0 auto;
+ padding: 0;
+ list-style: none;
+ font-size: 18px;
+ line-height: 48px;
+
+ @include md-only {
+ top: 10px;
+ right: 10px;
+ flex: 1 1 auto;
+ }
+
+ li {
+
+ flex: 0 0 auto;
+
+ a {
+ padding: 10px 20px;
+ text-decoration: none;
+ color: $color-white;
+
+ &:hover {
+ text-decoration: underline;
+ }
+ }
+ }
+}
+
+.form {
+ max-width: 380px;
+ margin:0 auto;
+
+ @include xs-only {
+ padding: 0 20px;
+ }
+}
+
+.page-title {
+ color: $color-white;
+ font-weight: 700;
+ font-size: 30px;
+ line-height: 42px;
+ margin-bottom: 40px;
+ text-align: center;
+}
+
+.user-row {
+ border-top: 1px solid $color-purpule;
+}
+
+.field {
+ margin-bottom: 50px;
+}
+
+.button {
+ text-align: center;
+ margin-bottom: 30px;
+}
+
+.actions {
+ text-align: center;
+ margin-bottom: 45px;
+ color: #fff;
+ line-height: 30px;
+}
+
+.link {
+ color: $border-color;
+ margin: 0 auto 45px;
+ text-decoration: none;
+ font: 700 16px $font-proxima;
+
+ &:hover {
+ color: #FFB54E;
+ }
+
+ @include md-only {
+ margin: 0 0 10px;
+ }
+}
diff --git a/src/pages/LoginMethods/PrivateKey/PrivateKey.js b/src/pages/LoginMethods/PrivateKey/PrivateKey.js
new file mode 100644
index 000000000..f566c002a
--- /dev/null
+++ b/src/pages/LoginMethods/PrivateKey/PrivateKey.js
@@ -0,0 +1,75 @@
+/**
+ * Copyright 2017–2018, LaborX PTY
+ * Licensed under the AGPL Version 3 license.
+ */
+
+import { MuiThemeProvider } from 'material-ui'
+import React, { PureComponent } from 'react'
+import { connect } from 'react-redux'
+import { Link } from 'react-router'
+import { reduxForm, Field } from 'redux-form/immutable'
+import { TextField } from 'redux-form-material-ui'
+import styles from 'layouts/Splash/styles'
+import { Button } from 'components'
+import {
+ onSubmitPrivateKeyLoginForm,
+ onSubmitPrivateKeyLoginFormSuccess,
+ onSubmitPrivateKeyLoginFormFail,
+} from '@chronobank/login/redux/network/actions'
+
+import './PrivateKey.scss'
+
+export const FORM_PRIVATE_KEY_LOGIN_PAGE = 'PrivateKeyLoginPageForm'
+
+function mapDispatchToProps (dispatch) {
+ return {
+ onSubmit: (values) => {
+ const privateKey = values.get('pk')
+ dispatch(onSubmitPrivateKeyLoginForm(privateKey))
+ },
+ onSubmitSuccess: () => dispatch(onSubmitPrivateKeyLoginFormSuccess()),
+ onSubmitFail: (errors, dispatch, submitErrors) => dispatch(onSubmitPrivateKeyLoginFormFail(errors, dispatch, submitErrors)),
+ }
+}
+
+class MnemonicLoginPage extends PureComponent {
+ render () {
+ const { handleSubmit } = this.props
+
+ return (
+
+
+
+ )
+ }
+}
+
+const form = reduxForm({ form: FORM_PRIVATE_KEY_LOGIN_PAGE })(MnemonicLoginPage)
+export default connect(null, mapDispatchToProps)(form)
diff --git a/src/pages/LoginMethods/PrivateKey/PrivateKey.scss b/src/pages/LoginMethods/PrivateKey/PrivateKey.scss
new file mode 100644
index 000000000..6d524ee18
--- /dev/null
+++ b/src/pages/LoginMethods/PrivateKey/PrivateKey.scss
@@ -0,0 +1,94 @@
+/**
+ * Copyright 2017–2018, LaborX PTY
+ * Licensed under the AGPL Version 3 license.
+ */
+
+@import "~styles/partials/variables";
+@import "~styles/partials/mixins";
+
+ul.actions {
+ position: absolute;
+ top: 30px;
+ right: 80px;
+ display: flex;
+ justify-content: flex-end;
+ flex: 0 0 auto;
+ padding: 0;
+ list-style: none;
+ font-size: 18px;
+ line-height: 48px;
+
+ @include md-only {
+ top: 10px;
+ right: 10px;
+ flex: 1 1 auto;
+ }
+
+ li {
+
+ flex: 0 0 auto;
+
+ a {
+ padding: 10px 20px;
+ text-decoration: none;
+ color: $color-white;
+
+ &:hover {
+ text-decoration: underline;
+ }
+ }
+ }
+}
+
+.form {
+ max-width: 380px;
+ margin:0 auto;
+
+ @include xs-only {
+ padding: 0 20px;
+ }
+}
+
+.page-title {
+ color: $color-white;
+ font-weight: 700;
+ font-size: 30px;
+ line-height: 42px;
+ margin-bottom: 40px;
+ text-align: center;
+}
+
+.user-row {
+ border-top: 1px solid $color-purpule;
+}
+
+.field {
+ margin-bottom: 50px;
+}
+
+.button {
+ text-align: center;
+ margin-bottom: 30px;
+}
+
+.actions {
+ text-align: center;
+ margin-bottom: 45px;
+ color: #fff;
+ line-height: 30px;
+}
+
+.link {
+ color: $border-color;
+ margin: 0 auto 45px;
+ text-decoration: none;
+ font: 700 16px $font-proxima;
+
+ &:hover {
+ color: #FFB54E;
+ }
+
+ @include md-only {
+ margin: 0 0 10px;
+ }
+}
diff --git a/src/pages/LoginMethods/WalletFile/UploadWalletPage.js b/src/pages/LoginMethods/WalletFile/UploadWalletPage.js
new file mode 100644
index 000000000..58ad77677
--- /dev/null
+++ b/src/pages/LoginMethods/WalletFile/UploadWalletPage.js
@@ -0,0 +1,82 @@
+/**
+ * Copyright 2017–2018, LaborX PTY
+ * Licensed under the AGPL Version 3 license.
+ */
+
+import PropTypes from 'prop-types'
+import classnames from 'classnames'
+import { MuiThemeProvider } from 'material-ui'
+import { reduxForm, Field } from 'redux-form/immutable'
+import React, { Component } from 'react'
+import { Link } from 'react-router'
+import { UserRow, Button } from 'components'
+
+import FileIcon from 'assets/img/icons/file-white.svg'
+import DeleteIcon from 'assets/img/icons/delete-white.svg'
+import SpinnerGif from 'assets/img/spinningwheel.gif'
+import WarningIcon from 'assets/img/icons/warning.svg'
+import CheckIcon from 'assets/img/icons/check-green.svg'
+
+import './UploadWalletPage.scss'
+
+export default class MnemonicPage extends Component {
+ static propTypes = {
+ mnemonic: PropTypes.string,
+ }
+
+ static defaultProps = {
+ mnemonic: '',
+ }
+
+ render () {
+ return (
+
+
+
+
Upload a Wallet File
+
+
+ Upload a wallet file to add the login information to your browser.
+ We provide the file on New Account Creation.
+
+
+
+
+
+ Browse for a Wallet File
+
+
+
+
+ Uploading
+
+
+
+
+
+ Uploading
+
+
+
+
+
+ Upload Error
+
+
+
+
+
+
+ Proceed to login
+
+ or
+
+ Back
+
+
+
+
+
+ )
+ }
+}
diff --git a/src/pages/LoginMethods/WalletFile/UploadWalletPage.scss b/src/pages/LoginMethods/WalletFile/UploadWalletPage.scss
new file mode 100644
index 000000000..6eea2ae81
--- /dev/null
+++ b/src/pages/LoginMethods/WalletFile/UploadWalletPage.scss
@@ -0,0 +1,108 @@
+/**
+ * Copyright 2017–2018, LaborX PTY
+ * Licensed under the AGPL Version 3 license.
+ */
+
+@import "~styles/partials/variables";
+@import "~styles/partials/mixins";
+
+.wrapper {
+ text-align: center;
+ max-width: 600px;
+ margin: 0 auto;
+ color: #fff;
+
+ @include md-only {
+ padding: 0 20px;
+ }
+}
+
+.page-title {
+ font-size: 30px;
+ margin-bottom: 10px;
+ font-weight: 700;
+}
+
+.description {
+ margin-bottom: 50px;
+ color: $additionalData-color-1;
+ line-height: 22px;
+ font-weight: 400;
+
+ @include md-only {
+ margin-bottom: 20px;
+ }
+}
+
+.link {
+ color: $border-color;
+ margin: 0 auto 45px;
+ text-decoration: none;
+ font: 700 16px $font-proxima;
+
+ &:hover {
+ color: #FFB54E;
+ }
+
+ @include md-only {
+ margin: 0 0 10px;
+ }
+}
+
+.submit {
+ margin-bottom: 25px;
+}
+
+.row {
+ margin-bottom: 50px;
+}
+
+.button {
+ button {
+ display: flex;
+ align-items: center;
+ padding: 0 20px;
+ line-height: 45px;
+ min-width: 280px;
+ margin: 0 auto 20px;
+ text-transform: none;
+
+ &:disabled {
+ background: $background-color-1;
+ }
+ }
+
+ &.button-warning button {
+ background: $color-red-2;
+ }
+ .button-text {
+ flex: 1;
+ text-align: left;
+ }
+
+ .before-img {
+ width: 24px;
+ display: inline-block;
+ margin-right: 5px;
+ }
+
+ .after-img {
+ width: 24px;
+ display: inline-block;
+ }
+}
+
+.actions {
+ text-align: center;
+ margin-bottom: 45px;
+ color: $additionalData-color-1;
+ line-height: 30px;
+}
+
+.submit {
+ margin-bottom: 25px;
+
+ button:disabled {
+ background: $background-color-1;
+ }
+}
diff --git a/src/pages/LoginMethods/index.js b/src/pages/LoginMethods/index.js
new file mode 100644
index 000000000..b287c51ee
--- /dev/null
+++ b/src/pages/LoginMethods/index.js
@@ -0,0 +1,2 @@
+export { default as MnemonicLoginPage, FORM_MNEMONIC_LOGIN_PAGE } from './Mnemonic/Mnemonic'
+export { default as PrivateKeyLoginPage, FORM_PRIVATE_KEY_LOGIN_PAGE } from './PrivateKey/PrivateKey'
diff --git a/src/pages/LoginPage/LoginPage.js b/src/pages/LoginPage/LoginPage.js
index 247fdca19..4197c428f 100644
--- a/src/pages/LoginPage/LoginPage.js
+++ b/src/pages/LoginPage/LoginPage.js
@@ -3,29 +3,167 @@
* Licensed under the AGPL Version 3 license.
*/
-import LocaleDropDown from 'layouts/partials/LocaleDropDown/LocaleDropDown'
-import LoginForm from '@chronobank/login-ui/components/LoginForm/LoginForm'
-import { MuiThemeProvider } from 'material-ui'
+import PropTypes from 'prop-types'
+import { MuiThemeProvider, CircularProgress } from 'material-ui'
import React, { PureComponent } from 'react'
-import { styles } from '@chronobank/login-ui/settings'
+import { Link } from 'react-router'
+import { reduxForm, Field } from 'redux-form/immutable'
+import { TextField } from 'redux-form-material-ui'
+import { connect } from 'react-redux'
+import { Translate } from 'react-redux-i18n'
+import { UserRow, Button } from 'components'
+import {
+ onSubmitLoginForm,
+ onSubmitLoginFormFail,
+ initLoginPage,
+ navigateToSelectWallet,
+} from '@chronobank/login/redux/network/actions'
+import AutomaticProviderSelector from '@chronobank/login-ui/components/ProviderSelectorSwitcher/AutomaticProviderSelector'
+import ManualProviderSelector from '@chronobank/login-ui/components/ProviderSelectorSwitcher/ManualProviderSelector'
+import styles from 'layouts/Splash/styles'
import './LoginPage.scss'
+const STRATEGY_MANUAL = 'manual'
+const STRATEGY_AUTOMATIC = 'automatic'
+
+const nextStrategy = {
+ [STRATEGY_AUTOMATIC]: STRATEGY_MANUAL,
+ [STRATEGY_MANUAL]: STRATEGY_AUTOMATIC,
+}
+
+export const FORM_LOGIN_PAGE = 'FormLoginPage'
+
+function mapStateToProps (state, ownProps) {
+
+ return {
+ selectedWallet: state.get('persistAccount').selectedWallet,
+ isLoginSubmitting: state.get('network').isLoginSubmitting,
+ }
+}
+
+function mapDispatchToProps (dispatch, ownProps) {
+ return {
+ onSubmit: async (values) => {
+ const password = values.get('password')
+
+ await dispatch(onSubmitLoginForm(password))
+ },
+ onSubmitFail: () => dispatch(onSubmitLoginFormFail()),
+ initLoginPage: () => dispatch(initLoginPage()),
+ navigateToSelectWallet: () => dispatch(navigateToSelectWallet()),
+ }
+}
+
class LoginPage extends PureComponent {
+ static propTypes = {
+ initLoginPage: PropTypes.func,
+ navigateToSelectWallet: PropTypes.func,
+ isLoginSubmitting: PropTypes.bool,
+ }
+
+ constructor(props){
+ super(props)
+
+ this.state = {
+ isShowProvider: true,
+ strategy: STRATEGY_AUTOMATIC,
+ }
+ }
+
+ componentWillMount(){
+ this.props.initLoginPage()
+ }
+
+ handleToggleProvider = (isShowProvider) => this.setState({ isShowProvider })
+
+ handleSelectorSwitch = (currentStrategy) => this.setState({ strategy: nextStrategy[currentStrategy] })
+
+ renderProviderSelector () {
+ switch (this.state.strategy) {
+ case STRATEGY_MANUAL:
+ return this.renderManualProviderSelector()
+ case STRATEGY_AUTOMATIC:
+ return this.renderAutomaticProviderSelector()
+ default:
+ return null
+ }
+ }
+
+ renderAutomaticProviderSelector () {
+ return (
+
+ )
+ }
+
+ renderManualProviderSelector () {
+ return (
+
+ )
+ }
+
render () {
+ const { handleSubmit, pristine, valid, initialValues, isImportMode, onSubmit, selectedWallet,
+ navigateToSelectWallet, isLoginSubmitting } = this.props
+
return (
-
+
)
}
}
-export default LoginPage
+const form = reduxForm({ form: FORM_LOGIN_PAGE })(LoginPage)
+export default connect(mapStateToProps, mapDispatchToProps)(form)
diff --git a/src/pages/LoginPage/LoginPage.scss b/src/pages/LoginPage/LoginPage.scss
index 1b58ae2a5..8514b0650 100644
--- a/src/pages/LoginPage/LoginPage.scss
+++ b/src/pages/LoginPage/LoginPage.scss
@@ -40,3 +40,57 @@ ul.actions {
}
}
+.form {
+ max-width: 380px;
+ margin:0 auto;
+
+ @include xs-only {
+ padding: 0 20px;
+ }
+}
+
+.selector {
+ color: white;
+}
+
+.page-title {
+ color: $color-white;
+ font-weight: 700;
+ font-size: 30px;
+ line-height: 42px;
+ margin-bottom: 40px;
+ text-align: center;
+}
+
+.user-row {
+ border-top: 1px solid $color-purpule;
+}
+
+.field {
+ margin-bottom: 50px;
+}
+
+.button {
+ text-align: center;
+ margin-bottom: 30px;
+}
+
+.actions {
+ text-align: center;
+ margin-bottom: 45px;
+}
+
+.link {
+ color: $border-color;
+ margin: 0 auto 45px;
+ text-decoration: none;
+ font: 700 16px $font-proxima;
+
+ &:hover {
+ color: #FFB54E;
+ }
+
+ @include md-only {
+ margin: 0 0 10px;
+ }
+}
diff --git a/src/pages/MnemonicPage/MnemonicPage.js b/src/pages/MnemonicPage/MnemonicPage.js
new file mode 100644
index 000000000..40c560608
--- /dev/null
+++ b/src/pages/MnemonicPage/MnemonicPage.js
@@ -0,0 +1,119 @@
+/**
+ * Copyright 2017–2018, LaborX PTY
+ * Licensed under the AGPL Version 3 license.
+ */
+
+import PropTypes from 'prop-types'
+import { push } from 'react-router-redux'
+import { connect } from 'react-redux'
+import classnames from 'classnames'
+import { MuiThemeProvider } from 'material-ui'
+import { reduxForm, Field } from 'redux-form/immutable'
+import React, { Component } from 'react'
+import { Link } from 'react-router'
+import { UserRow, Button } from 'components'
+import { initMnemonicPage, navigateToConfirmMnemonicPage } from '@chronobank/login/redux/network/actions'
+
+import PrintIcon from 'assets/img/icons/print-white.svg'
+
+import './MnemonicPage.scss'
+
+function mapStateToProps (state, ownProps) {
+
+ return {
+ mnemonic: state.get('network').newAccountMnemonic,
+ }
+}
+
+function mapDispatchToProps (dispatch, ownProps) {
+ return {
+ initMnemonicPage: () => dispatch(initMnemonicPage()),
+ navigateToConfirmPage: () => dispatch(navigateToConfirmMnemonicPage()),
+ }
+}
+
+@connect(mapStateToProps, mapDispatchToProps)
+export default class MnemonicPage extends Component {
+ static propTypes = {
+ mnemonic: PropTypes.string,
+ initMnemonicPage: PropTypes.func,
+ navigateToConfirmPage: PropTypes.func,
+ }
+
+ static defaultProps = {
+ mnemonic: '',
+ }
+
+ componentDidMount(){
+ this.props.initMnemonicPage()
+ }
+
+ navigateToConfirmPage(){
+ this.props.navigateToConfirmPage()
+ }
+
+ render () {
+ return (
+
+
+
+
Write down back-up phrase
+
+
+ You can use this phrase to login and access your wallet,
+ even if you forgot your password. You may also print the key
+ which will be provided with a QR code. Use this QR code to
+ scan on phone on ChronoWallet recover page.
+
+
+
+
{ this.props.mnemonic }
+
+
{}}>
+
+
+
+
+
+
+
Important! Read the security guidelines
+
+
+
+
+ Don't share your back-up phrase (mnemonic key) with someone you don't trust.
+ Double check services you're giving your mnemonic to and don't share your phrase with anyone.
+
+
+
+
+
+ Don't loose your back-up phrase (mnemonic key).
+ We do not store this information and Your account will be lost
+ together with all your funds and history.
+
+
+
+
+
+
+
+ Proceed
+
+
+
+
+
+
+
+ )
+ }
+}
diff --git a/src/pages/MnemonicPage/MnemonicPage.scss b/src/pages/MnemonicPage/MnemonicPage.scss
new file mode 100644
index 000000000..c7d7994be
--- /dev/null
+++ b/src/pages/MnemonicPage/MnemonicPage.scss
@@ -0,0 +1,167 @@
+/**
+ * Copyright 2017–2018, LaborX PTY
+ * Licensed under the AGPL Version 3 license.
+ */
+
+@import "~styles/partials/variables";
+@import "~styles/partials/mixins";
+
+.wrapper {
+ text-align: center;
+ max-width: 600px;
+ margin: 0 auto;
+ color: #fff;
+
+ @include md-only {
+ padding: 0 20px;
+ }
+}
+
+.page-title {
+ font-size: 30px;
+ margin-bottom: 10px;
+ font-weight: 700;
+}
+
+.description {
+ margin-bottom: 30px;
+ color: $additionalData-color-1;
+ line-height: 22px;
+ font-weight: 400;
+
+ @include md-only {
+ margin-bottom: 20px;
+ }
+}
+
+.passPhraseWrapper {
+ position: relative;
+ margin-bottom: 20px;
+ background: $background-color-1;
+ min-height: 100px;
+ border-radius: 2px;
+ padding: 30px 60px 20px 30px;
+
+ @include md-only {
+ margin-bottom: 0;
+ }
+}
+
+.passPhrase {
+ font-size: 16px;
+ font-weight: 700;
+ line-height: 24px;
+ color: $button-color-2;
+ text-align: left;
+ min-height: 46px;
+ width: 100%;
+
+ @include md-only {
+ min-height: 55px;
+ }
+}
+
+
+.progressBlock {
+ width: 50px;
+ margin: 0 auto 20px;
+ display: flex;
+ justify-content: space-between;
+}
+
+.progressPoint {
+ background: $button-color-2;
+ width: 10px;
+ height: 10px;
+ border-radius: 50%;
+
+ &.progressPointInactive {
+ background: $color-blue-4;
+ }
+}
+
+.actions {
+ text-align: center;
+ margin-bottom: 45px;
+ color: #fff;
+ line-height: 30px;
+}
+
+.link {
+ color: $border-color;
+ margin: 0 auto 45px;
+ text-decoration: none;
+ font: 700 16px $font-proxima;
+
+ &:hover {
+ color: #FFB54E;
+ }
+
+ @include md-only {
+ margin: 0 0 10px;
+ }
+}
+
+.submit {
+ margin-bottom: 25px;
+}
+
+.infoBlock {
+ text-align: left;
+ padding: 32px 30px 10px;
+ border-top: 5px solid #FF6D6B;
+ background: $background-color-1;
+ border-radius: 3px;
+ margin-bottom: 40px;
+
+ @include md-only {
+ border-radius: 0;
+ }
+}
+
+.infoBlockList {
+ font-weight: bold;
+ font-size: 16px;
+ margin-left: 15px;
+}
+
+.listItemContent {
+ font-weight: 400;
+ color: #A3A3CC;
+ line-height: 22px;
+ margin-bottom: 20px;
+
+ b {
+ color: #fff;
+ font-weight: bold;
+ }
+}
+
+.infoBlockHeader {
+ color: #FF6D6B;
+ font-weight: 700;
+ font-size: 20px;
+ margin-bottom: 20px;
+}
+
+.printButtonWrapper {
+ position: absolute;
+ right: 0;
+ width: 45px;
+ top: 0;
+ bottom: 0;
+ display: flex;
+ justify-content: flex-start;
+ align-items: center;
+}
+
+.printButton {
+ width: 24px;
+ opacity: 0.25;
+ cursor: pointer;
+
+ &:hover {
+ opacity: 1;
+ }
+}
+
diff --git a/src/pages/RecoverAccountPage/RecoverAccountPage.js b/src/pages/RecoverAccountPage/RecoverAccountPage.js
new file mode 100644
index 000000000..3b9cc4b86
--- /dev/null
+++ b/src/pages/RecoverAccountPage/RecoverAccountPage.js
@@ -0,0 +1,128 @@
+/**
+ * Copyright 2017–2018, LaborX PTY
+ * Licensed under the AGPL Version 3 license.
+ */
+
+import { MuiThemeProvider } from 'material-ui'
+import React, { PureComponent } from 'react'
+import { connect } from 'react-redux'
+import PropTypes from 'prop-types'
+import { Link } from 'react-router'
+import { reduxForm, Field } from 'redux-form/immutable'
+import { TextField } from 'redux-form-material-ui'
+import {
+ WalletEntryModel,
+} from 'models/persistAccount'
+import {
+ onSubmitRecoverAccountForm,
+ onSubmitRecoverAccountFormSuccess,
+ onSubmitRecoverAccountFormFail,
+ initRecoverAccountPage,
+} from '@chronobank/login/redux/network/actions'
+
+import { Button, UserRow } from 'components'
+
+import styles from 'layouts/Splash/styles'
+import './RecoverAccountPage.scss'
+
+export const FORM_RECOVER_ACCOUNT = 'RecoverAccountPage'
+
+function mapStateToProps (state, ownProps) {
+ const selectedWallet = state.get('persistAccount').selectedWallet
+ return {
+ selectedWallet: new WalletEntryModel({...selectedWallet}),
+ }
+}
+
+function mapDispatchToProps (dispatch, ownProps) {
+ return {
+ onSubmit: async (values) => {
+ let words = [], mnemonic = ''
+
+ for (let i = 1; i <= 12; i++) {
+ const word = values.get(`word-${i}`)
+ word && words.push(word)
+ }
+
+ mnemonic = words.join(' ')
+
+ await dispatch(onSubmitRecoverAccountForm(mnemonic))
+ },
+ onSubmitSuccess: () => dispatch(onSubmitRecoverAccountFormSuccess()),
+ onSubmitFail: (errors, dispatch, submitErrors) => dispatch(onSubmitRecoverAccountFormFail(errors, dispatch, submitErrors)),
+ initRecoverAccountPage: () => dispatch(initRecoverAccountPage()),
+ }
+}
+
+class RecoverAccountPage extends PureComponent {
+ static propTypes = {
+ selectedWallet: PropTypes.instanceOf(WalletEntryModel),
+ initRecoverAccountPage: PropTypes.func,
+ }
+
+ componentWillMount(){
+ this.props.initRecoverAccountPage()
+ }
+
+ get getSelectedWalletName(){
+ const { selectedWallet } = this.props
+ return selectedWallet && selectedWallet.name || ''
+ }
+
+ render () {
+ const { handleSubmit, selectedWallet } = this.props
+
+ const wordsArray = new Array(12).fill()
+
+ return (
+
+
+
+
+ )
+ }
+}
+
+const form = reduxForm({ form: FORM_RECOVER_ACCOUNT })(RecoverAccountPage)
+export default connect(mapStateToProps, mapDispatchToProps)(form)
diff --git a/src/pages/RecoverAccountPage/RecoverAccountPage.scss b/src/pages/RecoverAccountPage/RecoverAccountPage.scss
new file mode 100644
index 000000000..ceaa1309e
--- /dev/null
+++ b/src/pages/RecoverAccountPage/RecoverAccountPage.scss
@@ -0,0 +1,77 @@
+/**
+ * Copyright 2017–2018, LaborX PTY
+ * Licensed under the AGPL Version 3 license.
+ */
+
+@import "~styles/partials/variables";
+@import "~styles/partials/mixins";
+
+.form {
+ max-width: 380px;
+ margin:0 auto;
+
+ @include xs-only {
+ padding: 0 20px;
+ }
+}
+
+.title {
+ color: $color-white;
+ font-weight: 700;
+ font-size: 30px;
+ line-height: 42px;
+ margin-bottom: 40px;
+ text-align: center;
+}
+
+.actions {
+ text-align: center;
+ margin-bottom: 45px;
+ line-height: 30px;
+ color: #fff;
+}
+
+.link {
+ color: $border-color;
+ margin: 0 auto 45px;
+ text-decoration: none;
+ font: 700 16px $font-proxima;
+
+ &:hover {
+ color: #FFB54E;
+ }
+
+ @include md-only {
+ margin: 0 0 10px;
+ }
+}
+
+.button {
+ text-align: center;
+ margin-bottom: 22px;
+
+ button {
+ padding: 0 30px;
+ }
+}
+
+.actionIcon {
+ transform: matrix(-1,0,0,-1,0,0);
+}
+
+.user-row {
+ border-top: 1px solid $color-purpule;
+ margin-bottom: 10px;
+ color: #fff;
+}
+
+.fields-block {
+ margin-bottom: 50px;
+ display: flex;
+ justify-content: space-between;
+ flex-wrap: wrap;
+}
+
+.field {
+ width: 23% !important;
+}
diff --git a/src/pages/ResetPasswordPage/ResetPasswordPage.js b/src/pages/ResetPasswordPage/ResetPasswordPage.js
new file mode 100644
index 000000000..71a8962fb
--- /dev/null
+++ b/src/pages/ResetPasswordPage/ResetPasswordPage.js
@@ -0,0 +1,116 @@
+/**
+ * Copyright 2017–2018, LaborX PTY
+ * Licensed under the AGPL Version 3 license.
+ */
+
+import { MuiThemeProvider } from 'material-ui'
+import React, { PureComponent } from 'react'
+import { connect } from 'react-redux'
+import PropTypes from 'prop-types'
+import {
+ WalletEntryModel,
+} from 'models/persistAccount'
+import {
+ onSubmitResetAccountPasswordForm,
+ onSubmitResetAccountPasswordSuccess,
+ onSubmitResetAccountPasswordFail,
+ initResetPasswordPage,
+} from '@chronobank/login/redux/network/actions'
+import { reduxForm, Field } from 'redux-form/immutable'
+import { TextField } from 'redux-form-material-ui'
+import { UserRow, Button } from 'components'
+
+import styles from 'layouts/Splash/styles'
+import validate from './validate'
+import './ResetPasswordPage.scss'
+
+export const FORM_RESET_PASSWORD = 'ResetPasswordPage'
+
+function mapStateToProps (state, ownProps) {
+ const selectedWallet = state.get('persistAccount').selectedWallet
+ return {
+ selectedWallet: new WalletEntryModel({...selectedWallet}),
+ }
+}
+
+function mapDispatchToProps (dispatch, ownProps) {
+ return {
+ onSubmit: async (values) => {
+ const password = values.get('password')
+
+ await dispatch(onSubmitResetAccountPasswordForm(password))
+ },
+ onSubmitSuccess: () => dispatch(onSubmitResetAccountPasswordSuccess()),
+ onSubmitFail: (errors, dispatch, submitErrors) => dispatch(onSubmitResetAccountPasswordFail(errors, dispatch, submitErrors)),
+ initResetPasswordPage: () => dispatch(initResetPasswordPage()),
+ }
+}
+
+class ResetPasswordPage extends PureComponent {
+ static propTypes = {
+ selectedWallet: PropTypes.instanceOf(WalletEntryModel),
+ initResetPasswordPage: PropTypes.func,
+ }
+
+ componentWillMount(){
+ this.props.initResetPasswordPage()
+ }
+
+ get getSelectedWalletName(){
+ const { selectedWallet } = this.props
+ return selectedWallet && selectedWallet.name || ''
+ }
+ render () {
+ const { handleSubmit, selectedWallet } = this.props
+
+ return (
+
+
+
+ )
+ }
+}
+
+const form = reduxForm({ form: FORM_RESET_PASSWORD, validate })(ResetPasswordPage)
+export default connect(mapStateToProps, mapDispatchToProps)(form)
diff --git a/src/pages/ResetPasswordPage/ResetPasswordPage.scss b/src/pages/ResetPasswordPage/ResetPasswordPage.scss
new file mode 100644
index 000000000..d1ce6b9bf
--- /dev/null
+++ b/src/pages/ResetPasswordPage/ResetPasswordPage.scss
@@ -0,0 +1,92 @@
+/**
+ * Copyright 2017–2018, LaborX PTY
+ * Licensed under the AGPL Version 3 license.
+ */
+
+@import "~styles/partials/variables";
+@import "~styles/partials/mixins";
+
+ul.actions {
+ position: absolute;
+ top: 30px;
+ right: 80px;
+ display: flex;
+ justify-content: flex-end;
+ flex: 0 0 auto;
+ padding: 0;
+ list-style: none;
+ font-size: 18px;
+ line-height: 48px;
+
+ @include md-only {
+ top: 10px;
+ right: 10px;
+ flex: 1 1 auto;
+ }
+
+ li {
+
+ flex: 0 0 auto;
+
+ a {
+ padding: 10px 20px;
+ text-decoration: none;
+ color: $color-white;
+
+ &:hover {
+ text-decoration: underline;
+ }
+ }
+ }
+}
+
+.form {
+ max-width: 380px;
+ margin:0 auto;
+
+ @include xs-only {
+ padding: 0 20px;
+ }
+}
+
+.page-title {
+ color: $color-white;
+ font-weight: 700;
+ font-size: 30px;
+ line-height: 42px;
+ margin-bottom: 40px;
+ text-align: center;
+}
+
+.user-row {
+ border-top: 1px solid $color-purpule;
+}
+
+.field {
+ margin-bottom: 50px;
+}
+
+.button {
+ text-align: center;
+ margin-bottom: 30px;
+}
+
+.actions {
+ text-align: center;
+ margin-bottom: 45px;
+}
+
+.link {
+ color: $border-color;
+ margin: 0 auto 45px;
+ text-decoration: none;
+ font: 700 16px $font-proxima;
+
+ &:hover {
+ color: #FFB54E;
+ }
+
+ @include md-only {
+ margin: 0 0 10px;
+ }
+}
diff --git a/src/pages/ResetPasswordPage/validate.js b/src/pages/ResetPasswordPage/validate.js
new file mode 100644
index 000000000..a23dc2614
--- /dev/null
+++ b/src/pages/ResetPasswordPage/validate.js
@@ -0,0 +1,27 @@
+/**
+ * Copyright 2017–2018, LaborX PTY
+ * Licensed under the AGPL Version 3 license.
+ */
+
+import ErrorList from 'platform/ErrorList'
+import * as validator from 'models/validator'
+
+const validateEqualPasswords = (password, confirmPassword) => password === confirmPassword ? null : 'Wrong password'
+
+export default (values) => {
+
+ const password = values.get('password')
+
+ let passwordErrors = new ErrorList()
+ passwordErrors.add(validator.required(password))
+
+ const confirmPassword = values.get('confirmPassword')
+ let confirmPasswordErrors = new ErrorList()
+ confirmPasswordErrors.add(validator.required(confirmPassword))
+ confirmPasswordErrors.add(validateEqualPasswords(password, confirmPassword))
+
+ return {
+ password: passwordErrors.getErrors(),
+ confirmPassword: confirmPasswordErrors.getErrors(),
+ }
+}
diff --git a/src/pages/SelectWalletPage/SelectWalletPage.js b/src/pages/SelectWalletPage/SelectWalletPage.js
new file mode 100644
index 000000000..7a6f258bc
--- /dev/null
+++ b/src/pages/SelectWalletPage/SelectWalletPage.js
@@ -0,0 +1,111 @@
+/**
+ * Copyright 2017–2018, LaborX PTY
+ * Licensed under the AGPL Version 3 license.
+ */
+
+import PropTypes from 'prop-types'
+import { MuiThemeProvider } from 'material-ui'
+import { connect } from 'react-redux'
+import React, { PureComponent } from 'react'
+import { Link } from 'react-router'
+import { UserRow, Button } from 'components'
+import { navigateToSelectImportMethod, onWalletSelect } from '@chronobank/login/redux/network/actions'
+import {
+ WalletEntryModel,
+} from 'models/persistAccount'
+
+import arrow from 'assets/img/icons/prev-white.svg'
+import './SelectWalletPage.scss'
+
+function mapDispatchToProps (dispatch) {
+ return {
+ navigateToSelectImportMethod: () => dispatch(navigateToSelectImportMethod()),
+ onWalletSelect: (wallet) => dispatch(onWalletSelect(wallet)),
+ }
+}
+
+function mapStateToProps (state) {
+ return {
+ walletsList: state.get('persistAccount').walletsList.map(
+ (wallet) => new WalletEntryModel({...wallet})
+ ),
+ }
+}
+
+@connect(mapStateToProps, mapDispatchToProps)
+export default class SelectWalletPage extends PureComponent {
+ static propTypes = {
+ onWalletSelect: PropTypes.func,
+ walletsList: PropTypes.arrayOf(
+ PropTypes.instanceOf(WalletEntryModel)
+ ),
+ navigateToSelectImportMethod: PropTypes.func,
+ }
+
+ static defaultProps = {
+ onWalletSelect: () => {},
+ walletsList: [],
+ }
+
+ renderWalletsList (){
+ const { onWalletSelect, walletsList } = this.props
+
+ if (!walletsList || !walletsList.length){
+ return (
+
+ Sorry, there are no accounts to display
+
+ )
+ }
+
+ return (
+
+ {
+ walletsList ? walletsList.map((w, i) => (
+ onWalletSelect(w)}
+ />
+ )) : null
+ }
+
+ )
+ }
+
+ render () {
+ return (
+
+
+
+
My Accounts
+
+
+ Browse account stored on your device.
+
+ If you have created an account before you may use Add an Existing Account option below.
+
+
+
+ { this.renderWalletsList() }
+
+
+
+ Add an existing account
+
+ or
+ Create New Account
+
+
+
+
+
+ )
+ }
+}
diff --git a/src/pages/SelectWalletPage/SelectWalletPage.scss b/src/pages/SelectWalletPage/SelectWalletPage.scss
new file mode 100644
index 000000000..168f3cf01
--- /dev/null
+++ b/src/pages/SelectWalletPage/SelectWalletPage.scss
@@ -0,0 +1,93 @@
+/**
+ * Copyright 2017–2018, LaborX PTY
+ * Licensed under the AGPL Version 3 license.
+ */
+
+@import "~styles/partials/variables";
+@import "~styles/partials/mixins";
+
+.wrapper {
+ margin:0 auto;
+
+ @include xs-only {
+ padding: 0 20px;
+ }
+}
+
+.content {
+ margin: 0 auto;
+ max-width: 380px;
+}
+
+.page-title {
+ color: $color-white;
+ font-weight: 700;
+ font-size: 30px;
+ line-height: 42px;
+ margin-bottom: 40px;
+ text-align: center;
+}
+
+.wallets-list {
+ border-top: 1px solid $color-purpule;
+ margin-bottom: 45px;
+ color: #fff;
+}
+
+.empty-list {
+ border-top: 1px solid $color-purpule;
+ border-bottom: 1px solid $color-purpule;
+ padding: 18px 0;
+ margin-bottom: 30px;
+ text-align: center;
+ color: #fff;
+ width: 380px;
+
+ @include xs-only {
+ width: 100%;
+ }
+}
+
+.actions {
+ text-align: center;
+ margin-bottom: 45px;
+ line-height: 30px;
+ color: #fff;
+}
+
+.link {
+ color: $border-color;
+ margin: 0 auto 45px;
+ text-decoration: none;
+ font: 700 16px $font-proxima;
+
+ &:hover {
+ color: #FFB54E;
+ }
+
+ @include md-only {
+ margin: 0 0 10px;
+ }
+}
+
+.button {
+ text-align: center;
+ margin-bottom: 22px;
+
+ button {
+ padding: 0 30px;
+ }
+}
+
+.actionIcon {
+ transform: matrix(-1,0,0,-1,0,0);
+}
+
+.description {
+ font-weight: 400;
+ color: $additionalData-color-1;
+ font-size: 16px;
+ line-height: 22px;
+ margin-bottom: 40px;
+ text-align: center;
+}
diff --git a/src/pages/index.js b/src/pages/index.js
new file mode 100644
index 000000000..76af6bc56
--- /dev/null
+++ b/src/pages/index.js
@@ -0,0 +1,18 @@
+export { default as PageBase } from './PageBase'
+export { default as LoginPage, FORM_LOGIN_PAGE } from './LoginPage/LoginPage'
+export { default as NotFoundPage } from './NotFound/NotFound'
+export { default as CreateAccountPage, FORM_CREATE_ACCOUNT } from './CreateAccountPage/CreateAccountPage'
+export { default as SelectWalletPage } from './SelectWalletPage/SelectWalletPage'
+export { default as RecoverAccountPage, FORM_RECOVER_ACCOUNT } from './RecoverAccountPage/RecoverAccountPage'
+export { default as ResetPasswordPage, FORM_RESET_PASSWORD } from './ResetPasswordPage/ResetPasswordPage'
+export { default as ImportMethodsPage } from './ImportMethodsPage/ImportMethodsPage'
+export { default as ConfirmMnemonicPage, FORM_CONFIRM_MNEMONIC } from './ConfirmMnemonicPage/ConfirmMnemonicPage'
+export { default as MnemonicPage } from './MnemonicPage/MnemonicPage'
+export { default as DownloadWalletFilePage } from './DownloadWalletFilePage/DownloadWalletFilePage'
+export { default as UploadWalletPage } from './LoginMethods/WalletFile/UploadWalletPage'
+export {
+ MnemonicLoginPage,
+ PrivateKeyLoginPage,
+ FORM_MNEMONIC_LOGIN_PAGE,
+ FORM_PRIVATE_KEY_LOGIN_PAGE,
+} from './LoginMethods'
diff --git a/src/redux/configureStore.js b/src/redux/configureStore.js
index aa62a9aa2..34f1e6c9d 100644
--- a/src/redux/configureStore.js
+++ b/src/redux/configureStore.js
@@ -84,11 +84,13 @@ const configureStore = () => {
const i18nState = state.get('i18n')
const mainWalletsState = state.get('mainWallet')
const walletsState = state.get('multisigWallet')
+ const persistAccount = state.get('persistAccount')
state = new Immutable.Map()
state = state
.set('i18n', i18nState)
.set('multisigWallet', walletsState)
.set('mainWallet', mainWalletsState)
+ .set('persistAccount', persistAccount)
}
return appReducer(state, action)
}
@@ -117,7 +119,8 @@ export const store = configureStore()
store.dispatch(globalWatcher())
const persistorConfig = {
- whitelist: ['multisigWallet', 'mainWallet'],
+ key: 'root',
+ whitelist: ['multisigWallet', 'mainWallet', 'persistAccount'],
transforms: [transformer()],
}
store.__persistor = persistStore(store, persistorConfig)
diff --git a/src/redux/ducks.js b/src/redux/ducks.js
index 54ee07874..a47f3398c 100644
--- a/src/redux/ducks.js
+++ b/src/redux/ducks.js
@@ -24,6 +24,7 @@ import * as wallet from './wallet'
import * as watcher from './watcher'
import * as tokens from './tokens'
import * as assetsHolder from './assetsHolder'
+import * as persistAccount from './persistAccount'
export default {
ui,
@@ -46,5 +47,6 @@ export default {
assetsManager,
tokens,
assetsHolder,
+ persistAccount,
...Login,
}
diff --git a/src/redux/mainWallet/reducer.js b/src/redux/mainWallet/reducer.js
index c8af0bd48..16e91913a 100644
--- a/src/redux/mainWallet/reducer.js
+++ b/src/redux/mainWallet/reducer.js
@@ -51,6 +51,7 @@ export default (state = initialState, action) => {
))
case a.WALLET_SET_NAME:
return state.names(state.names().set(`${action.blockchain}-${action.address}`, action.name))
+
default:
return state
}
diff --git a/src/redux/persistAccount/actions.js b/src/redux/persistAccount/actions.js
new file mode 100644
index 000000000..5fc78dd8e
--- /dev/null
+++ b/src/redux/persistAccount/actions.js
@@ -0,0 +1,169 @@
+/**
+ * Copyright 2017–2018, LaborX PTY
+ * Licensed under the AGPL Version 3 license.
+ */
+
+import uniqid from 'uniqid'
+import bip39 from 'bip39'
+import Web3 from 'web3'
+import Accounts from 'web3-eth-accounts'
+
+import web3Provider from '@chronobank/login/network/Web3Provider'
+import {
+ WalletModel,
+ WalletEntryModel,
+} from 'models/persistAccount'
+import networkService from '@chronobank/login/network/NetworkService'
+import mnemonicProvider from '@chronobank/login/network/mnemonicProvider'
+import privateKeyProvider from '@chronobank/login/network/privateKeyProvider'
+import { clearErrors, loading } from '@chronobank/login/redux/network/actions'
+import { getSelectedNetwork } from './selectors'
+
+export const WALLETS_ADD = 'persistAccount/WALLETS_ADD'
+export const WALLETS_SELECT = 'persistAccount/WALLETS_SELECT'
+export const WALLETS_LOAD = 'persistAccount/WALLETS_LOAD'
+export const WALLETS_UPDATE_LIST = 'persistAccount/WALLETS_UPDATE_LIST'
+export const WALLETS_REMOVE = 'persistAccount/WALLETS_REMOVE'
+
+export const accountAdd = (wallet) => (dispatch) => {
+ dispatch({ type: WALLETS_ADD, wallet })
+}
+
+export const accountSelect = (wallet) => (dispatch) => {
+ dispatch({ type: WALLETS_SELECT, wallet })
+}
+
+export const accountLoad = (wallet) => (dispatch) => {
+ dispatch({ type: WALLETS_LOAD, wallet })
+}
+
+export const accountUpdateList = (walletList) => (dispatch) => {
+ dispatch({ type: WALLETS_UPDATE_LIST, walletList })
+}
+
+export const accountUpdate = (wallet) => (dispatch, getState) => {
+ const state = getState()
+
+ const { walletsList } = state.get('persistAccount')
+
+ let index = walletsList.findIndex((item) => item.key === wallet.key)
+
+ let copyWalletList = [...walletsList]
+
+ copyWalletList.splice(index, 1, wallet)
+
+ dispatch({ type: WALLETS_UPDATE_LIST, walletsList: copyWalletList })
+
+}
+
+export const decryptAccount = (entry, password) => async (dispatch, getState) => {
+ const state = getState()
+
+ const web3 = new Web3()
+ const accounts = new Accounts(new web3.providers.HttpProvider(networkService.getProviderSettings().url))
+ await accounts.wallet.clear()
+
+ let wallet = await accounts.wallet.decrypt(entry.encrypted, password)
+
+ const model = new WalletModel({
+ entry,
+ wallet,
+ })
+
+ dispatch(accountLoad(model))
+
+ return wallet
+
+}
+
+export const validateAccountName = (name) => (dispatch, getState) => {
+ const state = getState()
+
+ const { walletsList } = state.get('persistAccount')
+
+ return !walletsList.find((item) => item.name === name)
+}
+
+export const validateMnemonicForAccount = (wallet, mnemonic) => async () => {
+ let host = networkService.getProviderSettings().url
+
+ const web3 = new Web3()
+ const accounts = new Accounts(new web3.providers.HttpProvider(host))
+ accounts.wallet.clear()
+
+ const walletAddress = wallet && wallet.encrypted && wallet.encrypted[0] && wallet.encrypted[0].address || ''
+
+ const addressFromWallet = `0x${walletAddress}`
+
+ const account = accounts.privateKeyToAccount(`0x${bip39.mnemonicToSeedHex(mnemonic)}`)
+ const address = account && account.address && account.address.toLowerCase()
+
+ return addressFromWallet === address
+}
+
+export const resetPasswordAccount = (wallet, mnemonic, password) => async (dispatch) => {
+ let host = networkService.getProviderSettings().url
+
+ const web3 = new Web3()
+ const accounts = new Accounts(new web3.providers.HttpProvider(host))
+ accounts.wallet.clear()
+
+ const newCopy = await dispatch(createAccount({ name: wallet.name, mnemonic, password }))
+
+ let newWallet = {
+ ...wallet,
+ encrypted: newCopy.encrypted,
+ }
+
+ dispatch(accountUpdate(newWallet))
+
+ dispatch(accountSelect(newWallet))
+
+}
+
+export const createAccount = ({ name, password, privateKey, mnemonic, numberOfAccounts = 0, types = {} }) => async (dispatch, getState) => {
+ const state = getState()
+
+ let wallet, hex = privateKey || bip39.mnemonicToSeedHex(mnemonic) || ''
+
+ let host = networkService.getProviderSettings().url
+
+ const web3 = new Web3()
+ const accounts = new Accounts(new web3.providers.HttpProvider(host))
+ accounts.wallet.clear()
+
+ wallet = await accounts.wallet.create(numberOfAccounts)
+ const account = accounts.privateKeyToAccount(`0x${hex}`)
+ wallet.add(account)
+
+ return new WalletEntryModel({
+ key: uniqid(),
+ name,
+ types,
+ encrypted: wallet && wallet.encrypt(password),
+ })
+
+}
+
+export const downloadWallet = () => (dispatch, getState) => {
+ const state = getState()
+
+ const { selectedWallet } = state.get('persistAccount')
+
+ if (selectedWallet) {
+ const text = JSON.stringify(selectedWallet.encrypted.length > 1 ? selectedWallet.encrypted : selectedWallet.encrypted[0])
+ const element = document.createElement('a')
+ element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(text))
+ element.setAttribute('download', `Wallet.wlt`)
+ element.style.display = 'none'
+ document.body.appendChild(element)
+ element.click()
+ document.body.removeChild(element)
+ }
+}
+
+export const logout = () => (dispatch) => {
+ dispatch(accountSelect(null))
+ dispatch(accountLoad(null))
+ // Router.pushRoute('/')
+}
diff --git a/src/redux/persistAccount/index.js b/src/redux/persistAccount/index.js
new file mode 100644
index 000000000..30fd4fe33
--- /dev/null
+++ b/src/redux/persistAccount/index.js
@@ -0,0 +1,8 @@
+/**
+ * Copyright 2017–2018, LaborX PTY
+ * Licensed under the AGPL Version 3 license.
+ */
+
+import persistAccount from './reducer'
+
+export { persistAccount }
diff --git a/src/redux/persistAccount/reducer.js b/src/redux/persistAccount/reducer.js
new file mode 100644
index 000000000..813559a68
--- /dev/null
+++ b/src/redux/persistAccount/reducer.js
@@ -0,0 +1,70 @@
+/**
+ * Copyright 2017–2018, LaborX PTY
+ * Licensed under the AGPL Version 3 license.
+ */
+
+import { persistReducer, REHYDRATE } from 'redux-persist'
+import storage from 'redux-persist/lib/storage'
+import * as a from './actions'
+import { removeWallet } from './utils'
+
+const persistConfig = {
+ key: 'wallet',
+ storage: storage,
+ blacklist: ['decryptedWallet'],
+}
+
+const initialState = {
+ walletsList: [],
+ selectedWallet: null,
+ decryptedWallet: null,
+ rehydrated: false,
+}
+
+const persistAccount = (state = initialState, action) => {
+ switch (action.type) {
+ case REHYDRATE:
+ return {
+ ...state,
+ ...action.payload.persistAccount,
+ rehydrated: true,
+ }
+ case a.WALLETS_ADD :
+ return {
+ ...state,
+ walletsList: [
+ ...state.walletsList,
+ action.wallet,
+ ],
+ }
+
+ case a.WALLETS_SELECT :
+ return {
+ ...state,
+ selectedWallet: action.wallet,
+ }
+
+ case a.WALLETS_LOAD :
+ return {
+ ...state,
+ decryptedWallet: action.wallet,
+ }
+
+ case a.WALLETS_UPDATE_LIST :
+ return {
+ ...state,
+ walletsList: action.walletsList,
+ }
+
+ case a.WALLETS_REMOVE :
+ return {
+ ...state,
+ walletsList: removeWallet(state.walletsList, action.name),
+ }
+
+ default:
+ return state
+ }
+}
+
+export default persistReducer(persistConfig, persistAccount)
diff --git a/src/redux/persistAccount/selectors.js b/src/redux/persistAccount/selectors.js
new file mode 100644
index 000000000..0accdd635
--- /dev/null
+++ b/src/redux/persistAccount/selectors.js
@@ -0,0 +1,19 @@
+/**
+ * Copyright 2017–2018, LaborX PTY
+ * Licensed under the AGPL Version 3 license.
+ */
+
+import { createSelector } from 'reselect'
+
+export const getSelectedNetwork = () => createSelector(
+ (state) => state.get('network'),
+ (network) => {
+ if (!network.selectedNetworkId){
+ return null
+ }
+
+ return network.networks && network.networks.find(
+ (item) => item.id === network.selectedNetworkId
+ )
+ },
+)
diff --git a/src/redux/persistAccount/utils.js b/src/redux/persistAccount/utils.js
new file mode 100644
index 000000000..c083c2708
--- /dev/null
+++ b/src/redux/persistAccount/utils.js
@@ -0,0 +1,13 @@
+export const replaceWallet = (wallet, walletList) => {
+ let index = walletList.findIndex((item) => item.key === wallet.key)
+
+ let copyWalletList = [...walletList]
+
+ copyWalletList.splice(index, 1, wallet)
+
+ return copyWalletList
+}
+
+export const removeWallet = (walletsList, name) => {
+ return walletsList.filter((w) => w.name !== name)
+}
diff --git a/src/redux/wallet/reducer.js b/src/redux/wallet/reducer.js
index 3e71c4bd7..218b1abad 100644
--- a/src/redux/wallet/reducer.js
+++ b/src/redux/wallet/reducer.js
@@ -9,6 +9,9 @@ const initialState = {
isMultisig: false,
blockchain: null,
address: null,
+ walletsList: [],
+ selectedWallet: null,
+ decryptedWallet: null,
}
export default (state = initialState, action) => {
@@ -24,6 +27,7 @@ export default (state = initialState, action) => {
blockchain: action.blockchain,
address: action.address,
}
+
default:
return state
}
diff --git a/src/router.js b/src/router.js
index 9cf769d91..2efccb1f0 100644
--- a/src/router.js
+++ b/src/router.js
@@ -6,9 +6,22 @@
import Markup from 'layouts/Markup'
import { Provider } from 'react-redux'
import React from 'react'
-import { Route, Router } from 'react-router'
-import NotFoundPage from 'pages/NotFound/NotFound'
-import LoginPage from 'pages/LoginPage/LoginPage'
+import { Route, Router, IndexRoute, Redirect } from 'react-router'
+import {
+ NotFoundPage,
+ LoginForm,
+ CreateAccount,
+ AccountSelector,
+ RecoverAccount,
+ ResetPassword,
+ LoginWithOptions,
+ ConfirmMnemonic,
+ GenerateMnemonic,
+ GenerateWallet,
+ UploadWalletPage,
+ LoginWithMnemonic,
+ LoginWithPrivateKey,
+} from '@chronobank/login-ui/components'
import Splash from 'layouts/Splash/Splash'
import {
AssetsPage,
@@ -54,6 +67,7 @@ function hashLinkScroll () {
const router = (
+
@@ -72,8 +86,19 @@ const router = (
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/styles/partials/_variables.scss b/src/styles/partials/_variables.scss
index 3c3d8e516..73d53d7de 100644
--- a/src/styles/partials/_variables.scss
+++ b/src/styles/partials/_variables.scss
@@ -13,8 +13,11 @@ $color-primary-light: #e9efff;
$color-primary-light-1: #f5f5ff;
$color-secondary: #2962ff;
+$color-description: #9997B2;
+
$color-white: #FFFFFF;
$color-blue: #2962FF;
+$color-light-blue: #5DB3ED;
$color-blue-2: #758EFF;
$color-blue-2-hover: #6980E5;
$color-blue405: #4058b8;
@@ -27,6 +30,7 @@ $color-red: #FF1744;
$error-color: #F44336;
$color-red-1: #FF1645;
$color-red-1-hover: #E51441;
+$color-orange: #E2A864;
$color-black: #222;
$color-error: $color-red;
@@ -81,8 +85,8 @@ $highlight-green: $color-green;
$highlight-red: #{material-color('red', 'a400')};
// fonts
-$font-family-main: 'Roboto',
-sans-serif;
+$font-family-main: 'Roboto', sans-serif;
+$font-proxima: 'proxima nova', sans-serif;
$font-size-base: 16px;
$font-weight-bold: 600;
diff --git a/yarn.lock b/yarn.lock
index e87a95036..24454a3f7 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2831,7 +2831,7 @@ cryptiles@3.x.x:
dependencies:
boom "5.x.x"
-crypto-browserify@^3.11.0:
+crypto-browserify@3.12.0, crypto-browserify@^3.11.0:
version "3.12.0"
resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.12.0.tgz#396cf9f3137f03e4b8e532c58f698254e00f80ec"
dependencies:
@@ -4146,6 +4146,14 @@ eth-lib@0.1.27:
ws "^3.0.0"
xhr-request-promise "^0.1.2"
+eth-lib@0.2.7:
+ version "0.2.7"
+ resolved "https://registry.yarnpkg.com/eth-lib/-/eth-lib-0.2.7.tgz#2f93f17b1e23aec3759cd4a3fe20c1286a3fc1ca"
+ dependencies:
+ bn.js "^4.11.6"
+ elliptic "^6.4.0"
+ xhr-request-promise "^0.1.2"
+
eth-lib@0.2.8:
version "0.2.8"
resolved "https://registry.yarnpkg.com/eth-lib/-/eth-lib-0.2.8.tgz#b194058bef4b220ad12ea497431d6cb6aa0623c8"
@@ -10943,7 +10951,7 @@ script-ext-html-webpack-plugin@^1.8.8:
dependencies:
debug "^3.1.0"
-scrypt.js@^0.2.0:
+scrypt.js@0.2.0, scrypt.js@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/scrypt.js/-/scrypt.js-0.2.0.tgz#af8d1465b71e9990110bedfc593b9479e03a8ada"
dependencies:
@@ -12467,6 +12475,10 @@ utils-merge@1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713"
+uuid@2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/uuid/-/uuid-2.0.1.tgz#c2a30dedb3e535d72ccf82e343941a50ba8533ac"
+
uuid@3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.0.0.tgz#6728fc0459c450d796a99c31837569bdf672d728"
@@ -12674,6 +12686,21 @@ web3-eth-abi@1.0.0-beta.34:
web3-core-helpers "1.0.0-beta.34"
web3-utils "1.0.0-beta.34"
+web3-eth-accounts@^1.0.0-beta.34:
+ version "1.0.0-beta.34"
+ resolved "https://registry.yarnpkg.com/web3-eth-accounts/-/web3-eth-accounts-1.0.0-beta.34.tgz#e09142eeecc797ac3459b75e9b23946d3695f333"
+ dependencies:
+ any-promise "1.3.0"
+ crypto-browserify "3.12.0"
+ eth-lib "0.2.7"
+ scrypt.js "0.2.0"
+ underscore "1.8.3"
+ uuid "2.0.1"
+ web3-core "1.0.0-beta.34"
+ web3-core-helpers "1.0.0-beta.34"
+ web3-core-method "1.0.0-beta.34"
+ web3-utils "1.0.0-beta.34"
+
web3-eth-contract@1.0.0-beta.34:
version "1.0.0-beta.34"
resolved "https://registry.yarnpkg.com/web3-eth-contract/-/web3-eth-contract-1.0.0-beta.34.tgz#9dbb38fae7643a808427a20180470ec7415c91e6"