diff --git a/TODO b/TODO
index 9a8b20b..f1736dc 100644
--- a/TODO
+++ b/TODO
@@ -1,6 +1,13 @@
+
resume statistic dashboard with money values
+pagination
+pre-anchors
Toaster
+buy/offer untrusted asset
+
+credit backstream from last timestamp
+ground
- desktop app link and page
diff --git a/app/js/actions-creators/account.js b/app/js/actions-creators/account.js
index 6e0ec31..a691bdc 100644
--- a/app/js/actions-creators/account.js
+++ b/app/js/actions-creators/account.js
@@ -1,15 +1,48 @@
import { push } from 'react-router-redux';
import { StellarServer, StellarTools } from 'stellar-toolkit';
+import { Keypair } from 'stellar-sdk';
import { AsyncActions } from '../helpers/asyncActions';
import * as AccountActions from '../actions/account';
import { ASYNC_FETCH_ACCOUNT, ASYNC_CREATE_TEST_ACCOUNT } from '../constants/asyncActions';
+import * as routes from '../constants/routes';
+import { getLocalAccounts } from '../helpers/storage';
+import { getAccounts, getCurrentAccount } from '../selectors/account';
import { getNetwork } from '../selectors/stellarData';
const { getAccount, switchNetwork: switchNetworkInstance, generateTestPair } = StellarServer;
const { KeypairInstance } = StellarTools;
+const PROD = process.env.NODE_ENV === 'production';
+
+export const resetAccount = () => (dispatch) => {
+ dispatch(push({ query: {} }));
+ dispatch(AccountActions.resetAccount());
+};
+
+export const switchNetwork = network => (dispatch, getState) => {
+ const currentNetwork = getNetwork(getState());
+ if (network === currentNetwork) return;
+
+ dispatch(resetAccount());
+
+ switchNetworkInstance(network);
+ dispatch(AccountActions.switchNetwork(network));
+};
+
+export const addAccount = keypair => (dispatch) => {
+ const newAccount = {
+ id: keypair.publicKey(),
+ keypair,
+ };
+
+ // TODO update existing : add seed, edit other fields ...
+ // TODO add network into account
+ dispatch(AccountActions.addAccount(newAccount));
+};
+
+// TODO handle invalid keys
export const setAccount = keys => (dispatch, getState) => {
dispatch(AsyncActions.startFetch(ASYNC_FETCH_ACCOUNT));
@@ -17,36 +50,75 @@ export const setAccount = keys => (dispatch, getState) => {
const network = getNetwork(getState());
return getAccount(keypair.publicKey())
- .then((account) => {
- dispatch(AsyncActions.successFetch(ASYNC_FETCH_ACCOUNT, account));
- dispatch(AccountActions.setKeypair(keypair));
-
- const putSecret = (keypair.canSign() && process.env.NODE_ENV === 'development');
- const query = {
- accountId: putSecret ? undefined : keypair.publicKey(),
- secretSeed: putSecret ? keypair.secret() : undefined,
- network,
+ .then((stellarAccount) => {
+ dispatch(AsyncActions.successFetch(ASYNC_FETCH_ACCOUNT, stellarAccount));
+ dispatch(resetAccount());
+ dispatch(addAccount(keypair));
+ dispatch(AccountActions.setCurrentAccountId(keypair.publicKey()));
+
+ const putSecret = (keypair.canSign() && !PROD);
+ const routeUpdate = {
+ pathname: routes.Account_G(keypair.publicKey()),
+ query: {
+ secretSeed: putSecret ? keypair.secret() : undefined,
+ network,
+ },
};
- dispatch(push({ query }));
+ dispatch(push(routeUpdate));
- return account;
+ return stellarAccount;
})
- .catch(error => dispatch(AsyncActions.errorFetch(ASYNC_FETCH_ACCOUNT, error)));
+ .catch((error) => {
+ dispatch(AsyncActions.errorFetch(ASYNC_FETCH_ACCOUNT, error));
+ dispatch(push(routes.Root));
+ throw error;
+ });
};
-export const resetAccount = () => (dispatch) => {
- dispatch(push({ query: {} }));
- dispatch(AccountActions.resetAccount());
+export const openAccountId = id => (dispatch, getState) => {
+ const state = getState();
+ const localAccounts = getAccounts(state);
+ const currentAccount = getCurrentAccount(state);
+
+ if (!id) return Promise.reject();
+ if (id === 'null') { // TODO store constant or direct call reset
+ return dispatch(resetAccount());
+ }
+ if (currentAccount && currentAccount.id === id)
+ return Promise.resolve();
+
+ const localAccount = localAccounts.find(a => (a.id === id));
+
+ let keypair = null;
+ if (localAccount) {
+ keypair = localAccount.keypair;
+ } else {
+ keypair = Keypair.fromPublicKey(id);
+ }
+ return dispatch(setAccount(keypair));
};
-export const switchNetwork = network => (dispatch, getState) => {
- const currentNetwork = getNetwork(getState());
- if (network === currentNetwork) return;
+export const onPageLoad = nextState => (dispatch) => {
+ // Retrieve stored accounts
+ const localAccounts = getLocalAccounts();
+ dispatch(AccountActions.addAccounts(localAccounts));
- dispatch(resetAccount());
+ const { location: { query } } = nextState;
+ if (query.network) {
+ dispatch(switchNetwork(query.network));
+ }
+};
- switchNetworkInstance(network);
- dispatch(AccountActions.switchNetwork(network));
+export const onChangeAccountRoute = nextState => (dispatch) => {
+ const { params: { id }, location: { action, query } } = nextState;
+ if(action === 'PUSH') return; // Disable self-sent actions
+
+ if (query.secretSeed) {
+ const keypair = Keypair.fromSecret(query.secretSeed);
+ dispatch(setAccount(keypair));
+ } else {
+ dispatch(openAccountId(id));
+ }
};
export const createTestAccount = () => (dispatch) => {
diff --git a/app/js/actions/account.js b/app/js/actions/account.js
index 63abee0..e92a237 100644
--- a/app/js/actions/account.js
+++ b/app/js/actions/account.js
@@ -1,8 +1,8 @@
export const SWITCH_NETWORK = 'network:switch';
export const RESET_ACCOUNT = 'account:reset';
-export const SET_KEYPAIR = 'keypair:set';
-
+export const ADD_ACCOUNT = 'account:add';
+export const SET_CURRENT_ACCOUNT_ID = 'account:set-id';
export function resetAccount() {
return {
@@ -10,10 +10,10 @@ export function resetAccount() {
};
}
-export function setKeypair(keypair) {
+export function setCurrentAccountId(id) {
return {
- type: SET_KEYPAIR,
- keypair,
+ type: SET_CURRENT_ACCOUNT_ID,
+ id,
};
}
@@ -23,3 +23,15 @@ export function switchNetwork(network) {
network,
};
}
+
+export function addAccount(account, accounts) {
+ return {
+ type: ADD_ACCOUNT,
+ account,
+ accounts,
+ };
+}
+
+export function addAccounts(accounts) {
+ return addAccount(null, accounts);
+}
diff --git a/app/js/bootstrap/router.js b/app/js/bootstrap/router.js
index 21e8543..3312f1b 100644
--- a/app/js/bootstrap/router.js
+++ b/app/js/bootstrap/router.js
@@ -1,35 +1,52 @@
-import React from 'react';
-import { Router, Route, Redirect, browserHistory } from 'react-router';
+import React, { PropTypes } from 'react';
+import { Router, Route, IndexRoute, Redirect, browserHistory } from 'react-router';
+import { connect } from 'react-redux';
import { syncHistoryWithStore } from 'react-router-redux';
import store from './store';
import Layout from '../components/Layout';
+import Welcome from '../components/views/WelcomeView';
import * as routes from '../constants/routes';
import AppMode from '../elements/AppMode';
// import NotFound from '../components/views/NotFound';
import Desktop from '../components/views/Desktop';
-
-import * as AccountManager from '../helpers/AccountManager';
+import { onPageLoad as onPageLoadAction, onChangeAccountRoute as onChangeAccountRouteAction } from '../actions-creators/account';
const history = syncHistoryWithStore(browserHistory, store);
-const RouterContainer = () =>
+const RouterContainer = ({ onPageLoad, onChangeAccountRoute }) =>
-
+
+
-
+
+
;
-export default RouterContainer;
+RouterContainer.propTypes = {
+ onPageLoad: PropTypes.func.isRequired,
+ onChangeAccountRoute: PropTypes.func.isRequired,
+};
+
+const mapDispatchToProps = {
+ onPageLoad: onPageLoadAction,
+ onChangeAccountRoute: onChangeAccountRouteAction,
+};
+
+export default connect(null, mapDispatchToProps)(RouterContainer);
diff --git a/app/js/bootstrap/store.js b/app/js/bootstrap/store.js
index 8f39192..3eca652 100644
--- a/app/js/bootstrap/store.js
+++ b/app/js/bootstrap/store.js
@@ -6,8 +6,6 @@ import { browserHistory } from 'react-router';
import { routerMiddleware } from 'react-router-redux';
import reducers from '../reducers';
-import { setStore } from '../helpers/AccountManager';
-
import stellarStreamerMiddleware from '../middlewares/StellarStreamer';
import asyncActionsMiddleware from '../helpers/asyncActions/middleware';
@@ -26,6 +24,4 @@ const store = createStore(
enhancer,
);
-setStore(store);
-
export default store;
diff --git a/app/js/components/views/WelcomeView.js b/app/js/components/views/WelcomeView.js
index 6456ae6..e3d4782 100644
--- a/app/js/components/views/WelcomeView.js
+++ b/app/js/components/views/WelcomeView.js
@@ -51,6 +51,8 @@ const styles = {
},
};
+// TODO reshow loader on account loading
+
class WelcomeScreen extends Component {
render() {
return (
diff --git a/app/js/constants/routes.js b/app/js/constants/routes.js
index 676e50e..208f1fb 100644
--- a/app/js/constants/routes.js
+++ b/app/js/constants/routes.js
@@ -1,2 +1,5 @@
export const Root = '/';
+export const Account = '/account/:id';
export const Desktop = '/desktop';
+
+export const Account_G = id => Account.replace(':id', id);
diff --git a/app/js/elements/AppMode/component.js b/app/js/elements/AppMode/component.js
index 8387af8..c44bdc3 100644
--- a/app/js/elements/AppMode/component.js
+++ b/app/js/elements/AppMode/component.js
@@ -1,7 +1,6 @@
import React, { PropTypes } from 'react';
import { Dimmer, Loader } from 'semantic-ui-react';
-import Welcome from '../../components/views/WelcomeView';
import PrivateView from '../../components/views/PrivateView';
import PublicView from '../../components/views/PublicView';
@@ -15,9 +14,6 @@ function AppMode({ isAccountLoading, accountSet, canSign }) {
accountSet &&
(canSign ? : )
}
- {
- !accountSet &&
- }
);
}
diff --git a/app/js/elements/StellarContainers/AccountSelector/component.js b/app/js/elements/StellarContainers/AccountSelector/component.js
index e85d9c9..e12e917 100644
--- a/app/js/elements/StellarContainers/AccountSelector/component.js
+++ b/app/js/elements/StellarContainers/AccountSelector/component.js
@@ -24,26 +24,6 @@ class AccountSelector extends Component {
showSeed: false,
resolving: false,
};
- if (this.props.keypair) {
- this.state.accountId = this.props.keypair.publicKey();
- this.state.secretSeed = this.props.keypair.canSign() ? this.props.keypair.secret() : '';
- }
- }
-
- componentWillReceiveProps(newProps) {
- if (this.props.keypair !== newProps.keypair) {
- if (newProps.keypair) {
- this.setState({
- accountId: newProps.keypair.publicKey(),
- secretSeed: newProps.keypair.canSign() ? newProps.keypair.secret() : '',
- });
- } else {
- this.setState({
- accountId: '',
- secretSeed: '',
- });
- }
- }
}
componentDidMount() {
@@ -89,7 +69,8 @@ class AccountSelector extends Component {
}
newForm() {
- const { values: { address } } = this.props;
+ const { values } = this.props;
+ const address = values.address || null;
let buttonLabel = 'Invalid address';
const buttonContent = 'Go';
@@ -172,7 +153,6 @@ AccountSelector.propTypes = {
isAccountLoading: PropTypes.bool,
isCreatingTestAccount: PropTypes.bool,
error: PropTypes.object,
- keypair: PropTypes.object,
setAccount: PropTypes.func.isRequired,
createTestAccount: PropTypes.func.isRequired,
network: PropTypes.string.isRequired,
diff --git a/app/js/elements/StellarContainers/AccountSelector/container.js b/app/js/elements/StellarContainers/AccountSelector/container.js
index c99ba0b..a711512 100644
--- a/app/js/elements/StellarContainers/AccountSelector/container.js
+++ b/app/js/elements/StellarContainers/AccountSelector/container.js
@@ -8,7 +8,6 @@ import {
isAccountLoading,
isCreatingTestAccount,
getAccountError,
- getKeypair,
canSign,
} from '../../../selectors/account';
import {
@@ -22,7 +21,6 @@ const mapStateToProps = state => ({
isCreatingTestAccount: isCreatingTestAccount(state),
canSign: canSign(state),
error: getAccountError(state),
- keypair: getKeypair(state),
network: getNetwork(state),
values: getFormValues(FORM_NAME)(state),
diff --git a/app/js/elements/StellarContainers/CurrentAccount/component.js b/app/js/elements/StellarContainers/CurrentAccount/component.js
index e038ad5..1bbff66 100644
--- a/app/js/elements/StellarContainers/CurrentAccount/component.js
+++ b/app/js/elements/StellarContainers/CurrentAccount/component.js
@@ -2,6 +2,8 @@ import React, { Component, PropTypes } from 'react';
import { Table, Container, Button, Header } from 'semantic-ui-react';
import Clipboard from 'clipboard';
+import * as routes from '../../../constants/routes';
+
class CurrentAccount extends Component {
constructor(props) {
@@ -17,16 +19,38 @@ class CurrentAccount extends Component {
}
openOnNewTab() {
- let url = '/?';
- url += `network=${this.props.network}`;
- if (this.props.keypair.canSign()) {
- url += `&secretSeed=${this.props.keypair.secret()}`;
- } else {
- url += `&accountId=${this.props.keypair.publicKey()}`;
- }
+ let url = routes.Account_G(this.props.keypair.publicKey());
window.open(url);
}
+ renderSeedBtn() {
+ const { keypair } = this.props;
+ const canSign = keypair.canSign();
+
+ if(canSign) {
+ return (
+ this.state.showSeed ?
+