Skip to content

Commit

Permalink
add transfer support
Browse files Browse the repository at this point in the history
  • Loading branch information
backslash47 committed Apr 18, 2018
1 parent 21454f7 commit 2cbf383
Show file tree
Hide file tree
Showing 16 changed files with 573 additions and 44 deletions.
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"license": "LGPL",
"private": true,
"dependencies": {
"@types/promise-timeout": "^1.3.0",
"aws-sdk": "^2.222.1",
"axios": "^0.18.0",
"date-fns": "^1.29.0",
Expand All @@ -18,6 +19,7 @@
"make-runnable": "^1.3.6",
"numeral": "^2.0.6",
"ont-sdk-ts": "backslash47/ontology-ts-sdk#exported",
"promise-timeout": "^1.3.0",
"query-string": "^6.0.0",
"react": "^16.2.0",
"react-copy-to-clipboard": "^5.0.1",
Expand Down
7 changes: 6 additions & 1 deletion src/accounts/accountDetail.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import { StateSetter } from '~/utils';
import { getAccount } from '~/shared/accountsApi';
import { Account } from '~/shared/ont/model';
import View from './accountDetailView';
import { isOwnAccount } from '~/shared/walletApi';

interface PropsOuter {
match: match<{id: string}>;
Expand All @@ -35,6 +36,7 @@ interface PropsOwn {

interface State {
account: Account;
own: boolean;
loaded: boolean;
}

Expand All @@ -46,14 +48,17 @@ export default compose<PropsInner, PropsOuter>(
accountId: props.match.params.id
})),
withState<null, Partial<State>, 'state', 'setState'>('state', 'setState', {
loaded: false
loaded: false,
own: false
}),
lifecycle<PropsOwn & StateSetter<State>, null>({
async componentDidMount() {
const account = await getAccount(this.props.accountId);
const own = isOwnAccount(account.address);

this.props.setState({
account,
own,
loaded: true
});
}
Expand Down
33 changes: 27 additions & 6 deletions src/accounts/accountDetailView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

import * as React from 'react';
import { Link } from 'react-router-dom';
import { Breadcrumb, Segment, Table, Header, Popup, Loader } from 'semantic-ui-react';
import { Breadcrumb, Segment, Table, Header, Popup, Loader, Button } from 'semantic-ui-react';
import { distanceInWordsToNow, format } from 'date-fns';
import { PropsInner as Props } from './accountDetail';
import AccountTransfers from './accountTransfers';
Expand All @@ -31,6 +31,9 @@ const Transaction: React.SFC<Props> = (props) => (
<Breadcrumb.Section as={Link} to="/accounts">Accounts</Breadcrumb.Section>
<Breadcrumb.Divider icon="right chevron" />
<Breadcrumb.Section active={true}>{props.accountId}</Breadcrumb.Section>
{props.own ? (
<Breadcrumb.Section active={true} className="bold">&nbsp;(Own)</Breadcrumb.Section>
) : null}
</Breadcrumb>
</Header>
</Segment>
Expand All @@ -49,21 +52,30 @@ const Transaction: React.SFC<Props> = (props) => (
<Table.Row>
<Table.Cell width={1}>Created</Table.Cell>
<Table.Cell width={1}>
<Link to={`/transactions/${props.account.firstTx}`}>
<Popup trigger={<span>{distanceInWordsToNow(props.account.firstTime)}</span>}>
{format(props.account.firstTime, 'MMM Do YYYY HH:mm:ss')}
</Popup>
</Link>
{props.account.firstTime !== undefined ? (
<Link to={`/transactions/${props.account.firstTx}`}>
<Popup trigger={<span>{distanceInWordsToNow(props.account.firstTime)}</span>}>
{format(props.account.firstTime, 'MMM Do YYYY HH:mm:ss')}
</Popup>
</Link>
) : (
<>new</>
)}

</Table.Cell>
</Table.Row>
<Table.Row>
<Table.Cell width={1}>Last transaction</Table.Cell>
<Table.Cell width={1}>
{props.account.lastTime !== undefined ? (
<Link to={`/transactions/${props.account.lastTx}`}>
<Popup trigger={<span>{distanceInWordsToNow(props.account.lastTime)}</span>}>
{format(props.account.lastTime, 'MMM Do YYYY HH:mm:ss')}
</Popup>
</Link>
) : (
<>new</>
)}
</Table.Cell>
</Table.Row>
<Table.Row>
Expand All @@ -77,6 +89,15 @@ const Transaction: React.SFC<Props> = (props) => (
{props.loaded ? (
<Segment>
<Header as="h2">Assets</Header>
{props.own ? (
<Button
as={Link}
to={`/accounts/${props.account.address}/transfer`}
size="large"
>
Transfer
</Button>
) : null}
<Table celled={false} basic="very" selectable={true} fixed={true}>
<Table.Body>
<Table.Row>
Expand Down
73 changes: 43 additions & 30 deletions src/accounts/accountsGridView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -91,36 +91,49 @@ const Accounts: React.SFC<Props> = (props) => (
) : null}
{props.items.map(account => (
<Table.Row key={account.address}>
<Table.Cell selectable={true}>
<Link to={`/accounts/${account.address}`}>{account.address}</Link>
</Table.Cell>
<Table.Cell selectable={true}>
<Link to={`/accounts/${account.address}`}>
<Popup trigger={<span>{distanceInWordsToNow(account.firstTime)}</span>}>
{format(account.firstTime, 'MMM Do YYYY HH:mm:ss')}
</Popup>
</Link>
</Table.Cell>
<Table.Cell selectable={true}>
<Link to={`/accounts/${account.address}`}>
<Popup trigger={<span>{distanceInWordsToNow(account.lastTime)}</span>}>
{format(account.lastTime, 'MMM Do YYYY HH:mm:ss')}
</Popup>
</Link>
</Table.Cell>
<Table.Cell selectable={true}>
<Link to={`/accounts/${account.address}`}>{account.transactionsCount}</Link>
</Table.Cell>
<Table.Cell selectable={true}>
<Link to={`/accounts/${account.address}`}>
{account.ontBalance}
</Link>
</Table.Cell>
<Table.Cell selectable={true}>
<Link to={`/accounts/${account.address}`}>
{account.ongBalance}
</Link>
</Table.Cell>
{account.firstTime !== undefined && account.lastTime !== undefined ? (
<>
<Table.Cell selectable={true}>
<Link to={`/accounts/${account.address}`}>{account.address}</Link>
</Table.Cell>
<Table.Cell selectable={true}>
<Link to={`/accounts/${account.address}`}>
<Popup trigger={<span>{distanceInWordsToNow(account.firstTime)}</span>}>
{format(account.firstTime, 'MMM Do YYYY HH:mm:ss')}
</Popup>
</Link>
</Table.Cell>
<Table.Cell selectable={true}>
<Link to={`/accounts/${account.address}`}>
<Popup trigger={<span>{distanceInWordsToNow(account.lastTime)}</span>}>
{format(account.lastTime, 'MMM Do YYYY HH:mm:ss')}
</Popup>
</Link>
</Table.Cell>
<Table.Cell selectable={true}>
<Link to={`/accounts/${account.address}`}>{account.transactionsCount}</Link>
</Table.Cell>
<Table.Cell selectable={true}>
<Link to={`/accounts/${account.address}`}>
{account.ontBalance}
</Link>
</Table.Cell>
<Table.Cell selectable={true}>
<Link to={`/accounts/${account.address}`}>
{account.ongBalance}
</Link>
</Table.Cell>
</>
) : (
<>
<Table.Cell>{account.address}</Table.Cell>
<Table.Cell/>
<Table.Cell/>
<Table.Cell>{account.transactionsCount}</Table.Cell>
<Table.Cell>{account.ontBalance}</Table.Cell>
<Table.Cell>{account.ongBalance}</Table.Cell>
</>
)}
</Table.Row>
))}
</Table.Body>
Expand Down
94 changes: 94 additions & 0 deletions src/accounts/transfer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
/*
* Copyright (C) 2018 Matus Zamborsky
* This file is part of The ONT Detective.
*
* The ONT Detective is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* The ONT Detective is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with The ONT Detective. If not, see <http://www.gnu.org/licenses/>.
*/

import { compose, withHandlers, flattenProp, withProps, withState } from 'recompose';
import { get } from 'lodash';
import { RouterProps, match } from 'react-router';
import { withRouter } from 'react-router-dom';
import { timeout, TimeoutError } from 'promise-timeout';
import { transferAsset } from '~/shared/walletApi';
import View from './transferView';
import { StateSetter } from '~/utils';

interface PropsOuter {
match: match<{id: string}>;
}

interface PropsOwn {
id: string;
}

interface State {
sending: boolean;
}

interface Handlers {
handleSend: (values: object) => void;
handleValidateNotEmpty: (value: string) => boolean;
handleValidateAddress: (value: string) => boolean;
}

export interface PropsInner extends Handlers, State, PropsOwn, PropsOuter {
}

export default compose<PropsInner, PropsOuter>(
withRouter,
withProps<PropsOwn, PropsOuter>(props => ({
id: props.match.params.id
})),
withState<null, Partial<State>, 'state', 'setState'>('state', 'setState', {
sending: false
}),
withHandlers<RouterProps & PropsOwn & StateSetter<State>, Handlers>({
handleSend: (props) => async (values) => {
props.setState({
...props.state,
sending: true
});

const destination = get(values, 'destination', '');
const amount = get(values, 'amount', '0');
const password = get(values, 'password', '');

try {
await timeout(transferAsset(props.id, destination, password, amount), 15000);
props.setState({
...props.state,
sending: false
});

props.history.push(`/accounts/${props.id}`);
return Promise.resolve({});
} catch (e) {
props.setState({
...props.state,
sending: false,
});

if (e instanceof TimeoutError) {
return Promise.resolve({ FORM_ERROR: 'Failed to transfer.'});
} else {
return Promise.resolve({ password: 'Invalid password.'});
}
}
},
handleValidateNotEmpty: (props) => (value) => (value === undefined || value.trim().length === 0),
handleValidateAddress: (props) => (value) => (value === undefined || value.trim().length !== 34)
}),
flattenProp('state'),
) (View);
Loading

0 comments on commit 2cbf383

Please sign in to comment.