diff --git a/packages/app-staking/package.json b/packages/app-staking/package.json index 45ddf9663a..0381e5ca58 100644 --- a/packages/app-staking/package.json +++ b/packages/app-staking/package.json @@ -11,6 +11,7 @@ "license": "ISC", "dependencies": { "@babel/runtime": "^7.0.0-beta.51", + "@polkadot/app-extrinsics": "^0.19.15", "@polkadot/storage": "^0.26.36", "@polkadot/ui-app": "^0.19.15", "@polkadot/ui-keyring": "^0.19.15", diff --git a/packages/app-staking/src/Account.tsx b/packages/app-staking/src/Account.tsx index 74c67d77b8..99dc6af31b 100644 --- a/packages/app-staking/src/Account.tsx +++ b/packages/app-staking/src/Account.tsx @@ -11,28 +11,40 @@ import { QueueTx$ExtrinsicAdd } from '@polkadot/ui-signer/types'; import BN from 'bn.js'; import React from 'react'; import extrinsics from '@polkadot/extrinsics'; +import storage from '@polkadot/storage'; import Button from '@polkadot/ui-app/Button'; import Icon from '@polkadot/ui-app/Icon'; +import Input from '@polkadot/ui-app/Input'; import classes from '@polkadot/ui-app/util/classes'; import IdentityIcon from '@polkadot/ui-react/IdentityIcon'; import RxBalance from '@polkadot/ui-react-rx/Balance'; import RxNonce from '@polkadot/ui-react-rx/Nonce'; +import withMulti from '@polkadot/ui-react-rx/with/multi'; +import withStorage from '@polkadot/ui-react-rx/with/storage'; import decodeAddress from '@polkadot/util-keyring/address/decode'; +import encodeAddress from '@polkadot/util-keyring/address/encode'; +import UnnominateButton from './UnnominateButton'; import translate from './translate'; type Props = I18nProps & { address: string, name: string, nonce?: BN, + nominating?: string, + nominatorsFor?: Array, intentionPosition: number, isIntending: boolean, isValidator: boolean, - queueExtrinsic: QueueTx$ExtrinsicAdd + queueExtrinsic: QueueTx$ExtrinsicAdd, + validators: Array }; type State = { - nonce: BN; + isInNominate: boolean, + isNomineeValid: boolean, + nominee: string, + nonce: BN }; class Account extends React.PureComponent { @@ -40,11 +52,15 @@ class Account extends React.PureComponent { super(props); this.state = { - nonce: new BN(0) + isInNominate: false, + isNomineeValid: false, + nonce: new BN(0), + nominee: '' }; } + render () { - const { className, isIntending, style, t } = this.props; + const { className, style } = this.props; return (
{ style={style} > {this.renderAccount()} -
); } private renderAccount () { const { address, isValidator, name, t } = this.props; - const addrShort = `${address.slice(0, 7)}…${address.slice(-7)}`; + // TODO: Still now sure what the best layout is, so just keeping the shortened around + // const addrShort = `${address.slice(0, 7)}…${address.slice(-7)}`; return (
@@ -91,7 +94,7 @@ class Account extends React.PureComponent { />
{name}
-
{addrShort}
+
{address}
{ ); } + private renderButtons () { + const { isInNominate } = this.state; + + if (isInNominate) { + return null; + } + + const { address, isIntending, nominating, t } = this.props; + const isNominating = !!nominating; + const canStake = !isIntending && !isNominating; + + if (canStake) { + return ( +
+ +
+ ); + } + + if (isNominating) { + return ( +
+ + + +
+ ); + } + + return ( +
+ +
+ ); + } + + private renderNominate () { + const { isInNominate, isNomineeValid, nominee } = this.state; + + if (!isInNominate) { + return null; + } + + const { t } = this.props; + + return ( +
+
+ +
+
+
+ +
+
+
+ ); + } + private send (extrinsic: SectionItem, values: Array) { const { address, queueExtrinsic } = this.props; const { nonce } = this.state; @@ -127,6 +240,35 @@ class Account extends React.PureComponent { }); } + private toggleNominate = () => { + this.setState( + ({ isInNominate }: State) => ({ + isInNominate: !isInNominate + }) + ); + } + + private onChangeNominee = (nominee: string) => { + const { validators } = this.props; + + this.setState({ + isNomineeValid: validators.includes(nominee), + nominee + }); + } + + private nominate = () => { + const { nominee } = this.state; + + this.send(extrinsics.staking.public.nominate, [nominee]); + + this.toggleNominate(); + } + + private unnominate = (index: number) => { + this.send(extrinsics.staking.public.unnominate, [index]); + } + private stake = () => { this.send(extrinsics.staking.public.stake, []); } @@ -142,4 +284,24 @@ class Account extends React.PureComponent { } } -export default translate(Account); +export default withMulti( + Account, + translate, + withStorage( + storage.staking.public.nominatorsFor, + { + propName: 'nominatorsFor', + paramProp: 'address', + transform: (publicKeys: Array) => + publicKeys.map(encodeAddress) + } + ), + withStorage( + storage.staking.public.nominating, + { + propName: 'nominating', + paramProp: 'address', + transform: encodeAddress + } + ) +); diff --git a/packages/app-staking/src/StakeList.tsx b/packages/app-staking/src/StakeList.tsx index a65aa80330..e5d3411c7d 100644 --- a/packages/app-staking/src/StakeList.tsx +++ b/packages/app-staking/src/StakeList.tsx @@ -42,6 +42,7 @@ class StakeList extends React.PureComponent { key={address} name={name} queueExtrinsic={queueExtrinsic} + validators={validators} /> ); })} diff --git a/packages/app-staking/src/UnnominateButton.tsx b/packages/app-staking/src/UnnominateButton.tsx new file mode 100644 index 0000000000..6d8ab8f1a7 --- /dev/null +++ b/packages/app-staking/src/UnnominateButton.tsx @@ -0,0 +1,64 @@ +// Copyright 2017-2018 @polkadot/app-staking authors & contributors +// This software may be modified and distributed under the terms +// of the ISC license. See the LICENSE file for details. + +import { I18nProps } from '@polkadot/ui-app/types'; + +import React from 'react'; +import storage from '@polkadot/storage'; +import Button from '@polkadot/ui-app/Button'; +import classes from '@polkadot/ui-app/util/classes'; +import withMulti from '@polkadot/ui-react-rx/with/multi'; +import withStorage from '@polkadot/ui-react-rx/with/storage'; +import encodeAddress from '@polkadot/util-keyring/address/encode'; + +import translate from './translate'; + +type Props = I18nProps & { + address: string, + nominating: string, + nominatorsFor: Array, + onClick: (index: number) => void +}; + +class UnnominateButton extends React.Component { + render () { + const { className, nominating, nominatorsFor, style, t } = this.props; + + if (!nominating || !nominatorsFor || nominatorsFor.length === 0) { + return null; + } + + return ( +