ghost-portal/packages/app-example/src/comp-007.tsx
Jaco Greeff 46a48539b7
Additional block production overview on explorer (#212)
* Cleanup Card layouts

* Additional block production overview on explorer

* Adjust Card padding for accounts

* Observable calculations into ApiObservable

* DRY-er combineLatest + .pipe(map(...))

* .pipe(map(...))

* Progress takes value + total, auto-colors

* CardSummary with Progress

* Small style updates

* Cleanup withApiObservable (optional value name)

* Cleanup progress props

* Cleanup rpc method use

* Auto-set the prop from withStorage

* Cleanups in observable use

* Cleanups to layout

* Initial bestNumber

* Poll for changes in ApiObservable

* WIP (debug)

* Initial conversion (untested) or subscribeStorage

* s/apiObservable/observable/

* Starting to show stuff (after storage subscriptions). WIP.

* Staking UI works, still WIP on rest.

* Readability

* Storage withObservableDiv, Closes https://github.com/polkadot-js/apps/issues/215

* Add encoding FIXMEs

* 'nominee' on set click

* Align withObservable with others (single line)

* Retrieve ss58 encoded AccountId from storage

* Update with latest api (null/undefined storage distinction)
2018-08-13 12:05:11 +02:00

181 lines
4.5 KiB
TypeScript

// Copyright 2017-2018 @polkadot/app-example authors & contributors
// This software may be modified and distributed under the terms
// of the ISC license. See the LICENSE file for details.
import { ApiProps } from '@polkadot/ui-react-rx/types';
import BN from 'bn.js';
import React from 'react';
import storage from '@polkadot/storage';
import IdentityIcon from '@polkadot/ui-react/IdentityIcon';
import withApi from '@polkadot/ui-react-rx/with/api';
type StorageProposal = [BN, any, string];
type StorageIntentions = Array<string>;
type StorageValidators = Array<string>;
type StateBalances = {
[index: string]: BN
};
type StateProposals = {
[index: string]: number[]
};
type State = {
balances: StateBalances,
intentions: Array<string>,
proposals: StateProposals,
subscriptions: Array<any>,
validators: Array<string>
};
const ZERO = new BN(0);
class Comp extends React.PureComponent<ApiProps, State> {
constructor (props: ApiProps) {
super(props);
this.state = {
balances: {},
intentions: [],
proposals: {},
subscriptions: [],
validators: []
};
}
componentDidMount () {
this.setState({
subscriptions: [
this.subscribeIntentions(),
this.subscribeProposals(),
this.subscribeValidators()
]
});
}
subscribeIntentions () {
const { api } = this.props;
return api.state
.getStorage(storage.staking.public.intentions)
.subscribe((intentions: StorageIntentions) => {
this.setState({ intentions }, () => {
this.subscribeBalances(intentions);
});
});
}
subscribeProposals () {
const { api } = this.props;
return api.state
.getStorage(storage.democracy.public.proposals)
.subscribe((value: Array<StorageProposal>) => {
this.setState({
proposals: value.reduce((proposals: StateProposals, [propIdx, proposal, accountId]) => {
if (!proposals[accountId]) {
proposals[accountId] = [propIdx.toNumber()];
} else {
proposals[accountId].push(propIdx.toNumber());
}
return proposals;
}, {} as StateProposals)
});
});
}
subscribeValidators () {
const { api } = this.props;
return api.state
.getStorage(storage.session.public.validators)
.subscribe((validators: StorageValidators) => {
this.setState({ validators });
});
}
subscribeBalances (accounts: string[]) {
const { api } = this.props;
const { balances, subscriptions } = this.state;
const newBalances = { ...balances };
accounts.forEach((account) => {
if (newBalances[account]) {
return;
} else {
newBalances[account] = ZERO;
}
subscriptions.push(
api.state
// Here we pass a parameter to the key generator, so it points to the correct storage entry
.getStorage(storage.staking.public.freeBalanceOf, account)
.subscribe((balance: BN) => {
this.setState(({ balances }: State) => {
const newBalances = { ...balances };
newBalances[account] = balance;
return {
balances: newBalances
};
});
})
);
});
this.setState({
balances: newBalances,
subscriptions
});
}
componentWillUnmount () {
const { subscriptions } = this.state;
subscriptions.forEach((sub) => sub.unsubscribe());
}
render () {
const { balances, intentions, proposals, validators } = this.state;
const sortedIntentions = intentions.sort((a, b) =>
(balances[b] || ZERO).cmp(balances[a] || ZERO)
);
return (
<table className='accounts'>
<thead>
<tr>
<th />
<th>Address</th>
<th>Balance</th>
<th>Proposals</th>
</tr>
</thead>
<tbody>
{sortedIntentions.map((address) => (
this.renderAccount(address, balances[address], proposals[address], validators.includes(address))
))}
</tbody>
</table>
);
}
renderAccount = (address: string, balance: BN = ZERO, proposals: number[] = [], isValidator: boolean = false) => {
return (
<tr className={isValidator ? 'validator' : ''} key={address}>
<td><IdentityIcon size={24} value={address} /></td>
<td>{address}</td>
<td>{balance.toString(10)}</td>
<td>{proposals.length}</td>
</tr>
);
}
}
export default withApi(Comp);