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)
This commit is contained in:
parent
ec869f6f5b
commit
46a48539b7
@ -11,9 +11,9 @@
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.0.0-beta.51",
|
||||
"@polkadot/storage": "^0.27.12",
|
||||
"@polkadot/storage": "^0.28.9",
|
||||
"@polkadot/ui-react": "^0.19.29",
|
||||
"@polkadot/ui-react-rx": "^0.19.29",
|
||||
"@polkadot/util-keyring": "^0.27.12"
|
||||
"@polkadot/util-keyring": "^0.28.1"
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,19 +4,15 @@
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import storage from '@polkadot/storage';
|
||||
import withStorageDiv from '@polkadot/ui-react-rx/with/storageDiv';
|
||||
import encodeAddress from '@polkadot/util-keyring/address/encode';
|
||||
import withObservableDiv from '@polkadot/ui-react-rx/with/observableDiv';
|
||||
|
||||
const method = storage.staking.public.intentions;
|
||||
|
||||
const Comp: React.ComponentType<any> = withStorageDiv(method)(
|
||||
const Comp: React.ComponentType<any> = withObservableDiv('stakingIntentions')(
|
||||
(value: Uint8Array[]): string => {
|
||||
if (!value || !value.length) {
|
||||
return 'No intentions found';
|
||||
}
|
||||
|
||||
return value.map(encodeAddress).join(', ');
|
||||
return value.join(', ');
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
@ -9,9 +9,8 @@ import React from 'react';
|
||||
|
||||
import storage from '@polkadot/storage';
|
||||
import withApi from '@polkadot/ui-react-rx/with/api';
|
||||
import encodeAddress from '@polkadot/util-keyring/address/encode';
|
||||
|
||||
type StorageProposal = [BN, any, Uint8Array];
|
||||
type StorageProposal = [BN, any, string];
|
||||
|
||||
type StateProposals = {
|
||||
[index: string]: number[]
|
||||
@ -43,12 +42,10 @@ class Comp extends React.PureComponent<ApiProps, State> {
|
||||
.subscribe((value: Array<StorageProposal>) => {
|
||||
this.setState({
|
||||
proposals: value.reduce((proposals: StateProposals, [propIdx, proposal, accountId]) => {
|
||||
const address = encodeAddress(accountId);
|
||||
|
||||
if (!proposals[address]) {
|
||||
proposals[address] = [propIdx.toNumber()];
|
||||
if (!proposals[accountId]) {
|
||||
proposals[accountId] = [propIdx.toNumber()];
|
||||
} else {
|
||||
proposals[address].push(propIdx.toNumber());
|
||||
proposals[accountId].push(propIdx.toNumber());
|
||||
}
|
||||
|
||||
return proposals;
|
||||
|
||||
@ -9,17 +9,16 @@ import React from 'react';
|
||||
|
||||
import storage from '@polkadot/storage';
|
||||
import withApi from '@polkadot/ui-react-rx/with/api';
|
||||
import encodeAddress from '@polkadot/util-keyring/address/encode';
|
||||
|
||||
type StorageProposal = [BN, any, Uint8Array];
|
||||
type StorageIntentions = Array<Uint8Array>;
|
||||
type StorageProposal = [BN, any, string];
|
||||
type StorageIntentions = Array<string>;
|
||||
|
||||
type StateProposals = {
|
||||
[index: string]: number[]
|
||||
};
|
||||
|
||||
type State = {
|
||||
intentions: Array<string>,
|
||||
intentions: StorageIntentions,
|
||||
proposals: StateProposals,
|
||||
subscriptions: Array<any>
|
||||
};
|
||||
@ -50,9 +49,7 @@ class Comp extends React.PureComponent<ApiProps, State> {
|
||||
return api.state
|
||||
.getStorage(storage.staking.public.intentions)
|
||||
.subscribe((intentions: StorageIntentions) => {
|
||||
this.setState({
|
||||
intentions: intentions.map(encodeAddress)
|
||||
});
|
||||
this.setState({ intentions });
|
||||
});
|
||||
}
|
||||
|
||||
@ -64,12 +61,10 @@ class Comp extends React.PureComponent<ApiProps, State> {
|
||||
.subscribe((value: Array<StorageProposal>) => {
|
||||
this.setState({
|
||||
proposals: value.reduce((proposals: StateProposals, [propIdx, proposal, accountId]) => {
|
||||
const address = encodeAddress(accountId);
|
||||
|
||||
if (!proposals[address]) {
|
||||
proposals[address] = [propIdx.toNumber()];
|
||||
if (!proposals[accountId]) {
|
||||
proposals[accountId] = [propIdx.toNumber()];
|
||||
} else {
|
||||
proposals[address].push(propIdx.toNumber());
|
||||
proposals[accountId].push(propIdx.toNumber());
|
||||
}
|
||||
|
||||
return proposals;
|
||||
|
||||
@ -9,12 +9,11 @@ import React from 'react';
|
||||
|
||||
import storage from '@polkadot/storage';
|
||||
import withApi from '@polkadot/ui-react-rx/with/api';
|
||||
import encodeAddress from '@polkadot/util-keyring/address/encode';
|
||||
import IdentityIcon from '@polkadot/ui-react/IdentityIcon';
|
||||
import Balance from '@polkadot/ui-react-rx/Balance';
|
||||
|
||||
type StorageProposal = [BN, any, Uint8Array];
|
||||
type StorageIntentions = Array<Uint8Array>;
|
||||
type StorageProposal = [BN, any, string];
|
||||
type StorageIntentions = Array<string>;
|
||||
|
||||
type StateProposals = {
|
||||
[index: string]: number[]
|
||||
@ -52,9 +51,7 @@ class Comp extends React.PureComponent<ApiProps, State> {
|
||||
return api.state
|
||||
.getStorage(storage.staking.public.intentions)
|
||||
.subscribe((intentions: StorageIntentions) => {
|
||||
this.setState({
|
||||
intentions: intentions.map(encodeAddress)
|
||||
});
|
||||
this.setState({ intentions });
|
||||
});
|
||||
}
|
||||
|
||||
@ -66,12 +63,10 @@ class Comp extends React.PureComponent<ApiProps, State> {
|
||||
.subscribe((value: Array<StorageProposal>) => {
|
||||
this.setState({
|
||||
proposals: value.reduce((proposals: StateProposals, [propIdx, proposal, accountId]) => {
|
||||
const address = encodeAddress(accountId);
|
||||
|
||||
if (!proposals[address]) {
|
||||
proposals[address] = [propIdx.toNumber()];
|
||||
if (!proposals[accountId]) {
|
||||
proposals[accountId] = [propIdx.toNumber()];
|
||||
} else {
|
||||
proposals[address].push(propIdx.toNumber());
|
||||
proposals[accountId].push(propIdx.toNumber());
|
||||
}
|
||||
|
||||
return proposals;
|
||||
|
||||
@ -9,13 +9,12 @@ import React from 'react';
|
||||
|
||||
import storage from '@polkadot/storage';
|
||||
import withApi from '@polkadot/ui-react-rx/with/api';
|
||||
import encodeAddress from '@polkadot/util-keyring/address/encode';
|
||||
import IdentityIcon from '@polkadot/ui-react/IdentityIcon';
|
||||
import Balance from '@polkadot/ui-react-rx/Balance';
|
||||
|
||||
type StorageProposal = [BN, any, Uint8Array];
|
||||
type StorageIntentions = Array<Uint8Array>;
|
||||
type StorageValidators = Array<Uint8Array>;
|
||||
type StorageProposal = [BN, any, string];
|
||||
type StorageIntentions = Array<string>;
|
||||
type StorageValidators = Array<string>;
|
||||
|
||||
type StateProposals = {
|
||||
[index: string]: number[]
|
||||
@ -56,9 +55,7 @@ class Comp extends React.PureComponent<ApiProps, State> {
|
||||
return api.state
|
||||
.getStorage(storage.staking.public.intentions)
|
||||
.subscribe((intentions: StorageIntentions) => {
|
||||
this.setState({
|
||||
intentions: intentions.map(encodeAddress)
|
||||
});
|
||||
this.setState({ intentions });
|
||||
});
|
||||
}
|
||||
|
||||
@ -70,12 +67,10 @@ class Comp extends React.PureComponent<ApiProps, State> {
|
||||
.subscribe((value: Array<StorageProposal>) => {
|
||||
this.setState({
|
||||
proposals: value.reduce((proposals: StateProposals, [propIdx, proposal, accountId]) => {
|
||||
const address = encodeAddress(accountId);
|
||||
|
||||
if (!proposals[address]) {
|
||||
proposals[address] = [propIdx.toNumber()];
|
||||
if (!proposals[accountId]) {
|
||||
proposals[accountId] = [propIdx.toNumber()];
|
||||
} else {
|
||||
proposals[address].push(propIdx.toNumber());
|
||||
proposals[accountId].push(propIdx.toNumber());
|
||||
}
|
||||
|
||||
return proposals;
|
||||
@ -90,9 +85,7 @@ class Comp extends React.PureComponent<ApiProps, State> {
|
||||
return api.state
|
||||
.getStorage(storage.session.public.validators)
|
||||
.subscribe((validators: StorageValidators) => {
|
||||
this.setState({
|
||||
validators: validators.map(encodeAddress)
|
||||
});
|
||||
this.setState({ validators });
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@ -10,11 +10,10 @@ 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';
|
||||
import encodeAddress from '@polkadot/util-keyring/address/encode';
|
||||
|
||||
type StorageProposal = [BN, any, Uint8Array];
|
||||
type StorageIntentions = Array<Uint8Array>;
|
||||
type StorageValidators = Array<Uint8Array>;
|
||||
type StorageProposal = [BN, any, string];
|
||||
type StorageIntentions = Array<string>;
|
||||
type StorageValidators = Array<string>;
|
||||
|
||||
type StateBalances = {
|
||||
[index: string]: BN
|
||||
@ -62,9 +61,7 @@ class Comp extends React.PureComponent<ApiProps, State> {
|
||||
|
||||
return api.state
|
||||
.getStorage(storage.staking.public.intentions)
|
||||
.subscribe((storage: StorageIntentions) => {
|
||||
const intentions = storage.map(encodeAddress);
|
||||
|
||||
.subscribe((intentions: StorageIntentions) => {
|
||||
this.setState({ intentions }, () => {
|
||||
this.subscribeBalances(intentions);
|
||||
});
|
||||
@ -79,12 +76,10 @@ class Comp extends React.PureComponent<ApiProps, State> {
|
||||
.subscribe((value: Array<StorageProposal>) => {
|
||||
this.setState({
|
||||
proposals: value.reduce((proposals: StateProposals, [propIdx, proposal, accountId]) => {
|
||||
const address = encodeAddress(accountId);
|
||||
|
||||
if (!proposals[address]) {
|
||||
proposals[address] = [propIdx.toNumber()];
|
||||
if (!proposals[accountId]) {
|
||||
proposals[accountId] = [propIdx.toNumber()];
|
||||
} else {
|
||||
proposals[address].push(propIdx.toNumber());
|
||||
proposals[accountId].push(propIdx.toNumber());
|
||||
}
|
||||
|
||||
return proposals;
|
||||
@ -99,9 +94,7 @@ class Comp extends React.PureComponent<ApiProps, State> {
|
||||
return api.state
|
||||
.getStorage(storage.session.public.validators)
|
||||
.subscribe((validators: StorageValidators) => {
|
||||
this.setState({
|
||||
validators: validators.map(encodeAddress)
|
||||
});
|
||||
this.setState({ validators });
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@ -16,8 +16,8 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.0.0-beta.51",
|
||||
"@polkadot/primitives": "^0.27.12",
|
||||
"@polkadot/primitives": "^0.28.9",
|
||||
"@polkadot/ui-app": "^0.19.29",
|
||||
"@polkadot/util-crypto": "^0.27.12"
|
||||
"@polkadot/util-crypto": "^0.28.1"
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,10 +5,10 @@
|
||||
import { Header } from '@polkadot/primitives/header';
|
||||
|
||||
import headerHash from '@polkadot/primitives/codec/header/hash';
|
||||
import withApiDiv from '@polkadot/ui-react-rx/with/apiDiv';
|
||||
import withObservableDiv from '@polkadot/ui-react-rx/with/observableDiv';
|
||||
import u8aToHex from '@polkadot/util/u8a/toHex';
|
||||
|
||||
const Component: React.ComponentType<any> = withApiDiv({ name: 'newHead', section: 'chain' })(
|
||||
const Component: React.ComponentType<any> = withObservableDiv('chainNewHead')(
|
||||
(value?: Header): string | undefined =>
|
||||
value
|
||||
? u8aToHex(headerHash(value), 64)
|
||||
|
||||
@ -24,7 +24,7 @@
|
||||
align-items: center;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
flex: 1 25%;
|
||||
flex: 1 33%;
|
||||
font-size: 3rem;
|
||||
font-weight: 100;
|
||||
}
|
||||
@ -36,7 +36,7 @@
|
||||
|
||||
.explorer--BlockHeader > .details {
|
||||
box-sizing: border-box;
|
||||
flex: 1 75%;
|
||||
flex: 1 67%;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
|
||||
@ -6,17 +6,12 @@ import { Header } from '@polkadot/primitives/header';
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import withApiDiv from '@polkadot/ui-react-rx/with/apiDiv';
|
||||
import withObservableDiv from '@polkadot/ui-react-rx/with/observableDiv';
|
||||
|
||||
import BlockHeader from './BlockHeader';
|
||||
|
||||
let blockHeaders: Array<Header> = [];
|
||||
|
||||
const apiMethod = {
|
||||
name: 'newHead',
|
||||
section: 'chain'
|
||||
};
|
||||
|
||||
const apiOptions = {
|
||||
transform: (header: Header): Array<Header> => {
|
||||
if (!header) {
|
||||
@ -40,7 +35,7 @@ const apiOptions = {
|
||||
}
|
||||
};
|
||||
|
||||
export default withApiDiv(apiMethod, apiOptions)(
|
||||
export default withObservableDiv('chainNewHead', apiOptions)(
|
||||
(value: Array<Header> = []) =>
|
||||
value.map((value) => (
|
||||
<BlockHeader
|
||||
|
||||
100
packages/app-explorer/src/Summary.tsx
Normal file
100
packages/app-explorer/src/Summary.tsx
Normal file
@ -0,0 +1,100 @@
|
||||
// Copyright 2017-2018 @polkadot/app-explorer 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 BN from 'bn.js';
|
||||
import React from 'react';
|
||||
import classes from '@polkadot/ui-app/util/classes';
|
||||
import CardSummary from '@polkadot/ui-app/CardSummary';
|
||||
import BestNumber from '@polkadot/ui-react-rx/BestNumber';
|
||||
import TimePeriod from '@polkadot/ui-react-rx/TimePeriod';
|
||||
import TimeNow from '@polkadot/ui-react-rx/TimeNow';
|
||||
import withObservable from '@polkadot/ui-react-rx/with/observable';
|
||||
import withMulti from '@polkadot/ui-react-rx/with/multi';
|
||||
|
||||
import translate from './translate';
|
||||
|
||||
type Props = I18nProps & {
|
||||
eraBlockLength?: BN,
|
||||
eraBlockProgress?: BN,
|
||||
sessionBlockProgress?: BN,
|
||||
sessionBrokenValue?: BN,
|
||||
sessionBrokenPercentLate?: BN,
|
||||
sessionLength?: BN
|
||||
};
|
||||
|
||||
class Summary extends React.PureComponent<Props> {
|
||||
render () {
|
||||
const { className, eraBlockLength, eraBlockProgress, sessionBlockProgress, sessionBrokenValue, sessionBrokenPercentLate, sessionLength, style, t } = this.props;
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classes('explorer--Summary', className)}
|
||||
style={style}
|
||||
>
|
||||
<div className='explorer--Summary-column'>
|
||||
<CardSummary label={t('summary.period', {
|
||||
defaultValue: 'target time'
|
||||
})}>
|
||||
<TimePeriod />
|
||||
</CardSummary>
|
||||
<CardSummary label={t('summary.now', {
|
||||
defaultValue: 'last block'
|
||||
})}>
|
||||
<TimeNow />
|
||||
</CardSummary>
|
||||
</div>
|
||||
<div className='explorer--Summary-column'>
|
||||
<CardSummary
|
||||
label={t('summary.sessionProgress', {
|
||||
defaultValue: 'session'
|
||||
})}
|
||||
progress={{
|
||||
total: sessionLength,
|
||||
value: sessionBlockProgress
|
||||
}}
|
||||
/>
|
||||
<CardSummary
|
||||
label={t('summary.eraProgress', {
|
||||
defaultValue: 'era'
|
||||
})}
|
||||
progress={{
|
||||
total: eraBlockLength,
|
||||
value: eraBlockProgress
|
||||
}}
|
||||
/>
|
||||
<CardSummary
|
||||
label={t('summary.brokenCount', {
|
||||
defaultValue: 'lateness'
|
||||
})}
|
||||
progress={{
|
||||
color: 'autoReverse',
|
||||
total: sessionBrokenPercentLate,
|
||||
value: sessionBrokenValue
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div className='explorer--Summary-column'>
|
||||
<CardSummary label={t('summary.best', {
|
||||
defaultValue: 'best'
|
||||
})}>
|
||||
<BestNumber />
|
||||
</CardSummary>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default withMulti(
|
||||
Summary,
|
||||
translate,
|
||||
withObservable('eraBlockLength'),
|
||||
withObservable('eraBlockProgress'),
|
||||
withObservable('sessionBlockProgress'),
|
||||
withObservable('sessionBrokenValue'),
|
||||
withObservable('sessionLength'),
|
||||
withObservable('sessionBrokenPercentLate')
|
||||
);
|
||||
@ -21,12 +21,30 @@
|
||||
.explorer--BestHash {
|
||||
font-size: 1.5em;
|
||||
line-height: 1.2;
|
||||
margin-bottom: 0.75em;
|
||||
}
|
||||
|
||||
.explorer--BestNumber {
|
||||
font-size: 3rem;
|
||||
line-height: 3rem;
|
||||
position: relative;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.explorer--BlockHeaders {}
|
||||
.explorer--Summary {
|
||||
align-items: stretch;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
flex-wrap: wrap;
|
||||
margin-bottom: 2em;
|
||||
}
|
||||
|
||||
.explorer--Summary-column {
|
||||
display: flex;
|
||||
flex: 0 1 auto;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.explorer--Summary-card {
|
||||
flex: 0 1 auto;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
@ -2,41 +2,31 @@
|
||||
// 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 { BareProps } from '@polkadot/ui-app/types';
|
||||
|
||||
import './index.css';
|
||||
|
||||
import React from 'react';
|
||||
import Page from '@polkadot/ui-app/Page';
|
||||
import classes from '@polkadot/ui-app/util/classes';
|
||||
import BestNumber from '@polkadot/ui-react-rx/BestNumber';
|
||||
|
||||
import BestHash from './BestHash';
|
||||
import Summary from './Summary';
|
||||
import BlockHeaders from './BlockHeaders';
|
||||
import translate from './translate';
|
||||
|
||||
type Props = I18nProps & {};
|
||||
type Props = BareProps & {};
|
||||
|
||||
class ExplorerApp extends React.PureComponent<Props> {
|
||||
export default class ExplorerApp extends React.PureComponent<Props> {
|
||||
render () {
|
||||
const { className, style, t } = this.props;
|
||||
const { className, style } = this.props;
|
||||
|
||||
return (
|
||||
<Page
|
||||
className={classes('explorer--App', className)}
|
||||
style={style}
|
||||
>
|
||||
<BestNumber
|
||||
className='explorer--BestNumber'
|
||||
label={t('app.bestNumber', {
|
||||
defaultValue: 'best #'
|
||||
})}
|
||||
/>
|
||||
<BestHash />
|
||||
<Summary />
|
||||
<BlockHeaders />
|
||||
</Page>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default translate(ExplorerApp);
|
||||
|
||||
@ -16,7 +16,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.0.0-beta.51",
|
||||
"@polkadot/extrinsics": "^0.27.12",
|
||||
"@polkadot/extrinsics": "^0.28.9",
|
||||
"@polkadot/ui-app": "^0.19.29",
|
||||
"@polkadot/ui-signer": "^0.19.29",
|
||||
"react-dropzone": "^4.2.9"
|
||||
|
||||
@ -41,8 +41,7 @@ class Account extends React.PureComponent<Props, State> {
|
||||
}
|
||||
|
||||
render () {
|
||||
const { className, defaultValue, isDisabled, isError, isInput, label, style, t, type, withLabel } = this.props;
|
||||
const { publicKey } = this.state;
|
||||
const { className, defaultValue, isDisabled, isError, isInput, label, style, type, withLabel } = this.props;
|
||||
|
||||
return (
|
||||
<div
|
||||
@ -62,23 +61,36 @@ class Account extends React.PureComponent<Props, State> {
|
||||
withLabel={withLabel}
|
||||
/>
|
||||
</div>
|
||||
<Labelled
|
||||
className='small'
|
||||
label={t('account.balance', {
|
||||
defaultValue: 'with an available balance of'
|
||||
})}
|
||||
withLabel={withLabel}
|
||||
>
|
||||
<Balance
|
||||
className='ui disabled dropdown selection'
|
||||
params={publicKey}
|
||||
/>
|
||||
</Labelled>
|
||||
{this.renderBalance()}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
onChange = (publicKey: Uint8Array): void => {
|
||||
private renderBalance (): React.ReactNode {
|
||||
const { t, withLabel } = this.props;
|
||||
const { publicKey } = this.state;
|
||||
|
||||
if (!publicKey) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Labelled
|
||||
className='small'
|
||||
label={t('account.balance', {
|
||||
defaultValue: 'with an available balance of'
|
||||
})}
|
||||
withLabel={withLabel}
|
||||
>
|
||||
<Balance
|
||||
className='ui disabled dropdown selection'
|
||||
params={publicKey}
|
||||
/>
|
||||
</Labelled>
|
||||
);
|
||||
}
|
||||
|
||||
private onChange = (publicKey: Uint8Array): void => {
|
||||
const { onChange } = this.props;
|
||||
|
||||
this.setState({ publicKey }, () =>
|
||||
|
||||
@ -21,6 +21,10 @@ export default class Nonce extends React.PureComponent<Props> {
|
||||
render () {
|
||||
const { className, label, style, onChange, value } = this.props;
|
||||
|
||||
if (!value) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classes('ui--row', className)}
|
||||
|
||||
@ -12,11 +12,11 @@
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.0.0-beta.51",
|
||||
"@polkadot/app-extrinsics": "^0.19.29",
|
||||
"@polkadot/storage": "^0.27.12",
|
||||
"@polkadot/storage": "^0.28.9",
|
||||
"@polkadot/ui-app": "^0.19.29",
|
||||
"@polkadot/ui-keyring": "^0.19.29",
|
||||
"@polkadot/ui-react": "^0.19.29",
|
||||
"@polkadot/ui-react-rx": "^0.19.29",
|
||||
"@polkadot/util-keyring": "^0.27.12"
|
||||
"@polkadot/util-keyring": "^0.28.1"
|
||||
}
|
||||
}
|
||||
|
||||
@ -12,28 +12,27 @@ import { ExtendedBalanceMap } from '@polkadot/ui-react-rx/types';
|
||||
import BN from 'bn.js';
|
||||
import React from 'react';
|
||||
import extrinsics from '@polkadot/extrinsics';
|
||||
import storage from '@polkadot/storage';
|
||||
import AddressMini from '@polkadot/ui-app/AddressMini';
|
||||
import AddressSummary from '@polkadot/ui-app/AddressSummary';
|
||||
import Button from '@polkadot/ui-app/Button';
|
||||
import Card from '@polkadot/ui-app/Card';
|
||||
import Icon from '@polkadot/ui-app/Icon';
|
||||
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 withObservable from '@polkadot/ui-react-rx/with/observable';
|
||||
import decodeAddress from '@polkadot/util-keyring/address/decode';
|
||||
import encodeAddress from '@polkadot/util-keyring/address/encode';
|
||||
|
||||
import Nominating from './Nominating';
|
||||
import UnnominateButton from './UnnominateButton';
|
||||
import translate from './translate';
|
||||
|
||||
type Props = I18nProps & {
|
||||
accountIndex?: BN,
|
||||
systemAccountIndexOf?: BN,
|
||||
address: string,
|
||||
balances: ExtendedBalanceMap,
|
||||
name: string,
|
||||
nominee?: string,
|
||||
nominatorsFor?: Array<string>,
|
||||
stakingNominating?: string,
|
||||
stakingNominatorsFor?: Array<string>,
|
||||
intentions: Array<string>,
|
||||
isValidator: boolean,
|
||||
queueExtrinsic: QueueTx$ExtrinsicAdd,
|
||||
@ -58,10 +57,15 @@ class Account extends React.PureComponent<Props, State> {
|
||||
const { isNominateOpen } = this.state;
|
||||
|
||||
return (
|
||||
<div
|
||||
<Card
|
||||
className={classes('staking--Account', className)}
|
||||
style={style}
|
||||
>
|
||||
<Icon
|
||||
className={classes('staking--Account-validating', isValidator ? 'isValidator' : '')}
|
||||
name='certificate'
|
||||
size='large'
|
||||
/>
|
||||
<Nominating
|
||||
isOpen={isNominateOpen}
|
||||
onClose={this.toggleNominate}
|
||||
@ -74,17 +78,12 @@ class Account extends React.PureComponent<Props, State> {
|
||||
value={address}
|
||||
>
|
||||
<div className='staking--Account-expand'>
|
||||
<Icon
|
||||
className={classes('staking--Account-validating', isValidator ? 'isValidator' : '')}
|
||||
name='certificate'
|
||||
size='large'
|
||||
/>
|
||||
{this.renderButtons()}
|
||||
{this.renderNominee()}
|
||||
{this.renderNominators()}
|
||||
</div>
|
||||
</AddressSummary>
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
@ -97,29 +96,29 @@ class Account extends React.PureComponent<Props, State> {
|
||||
}
|
||||
|
||||
private renderNominee () {
|
||||
const { nominee } = this.props;
|
||||
const { stakingNominating } = this.props;
|
||||
|
||||
if (!nominee) {
|
||||
if (!stakingNominating) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<AddressMini
|
||||
balance={this.balanceArray(nominee)}
|
||||
value={nominee}
|
||||
balance={this.balanceArray(stakingNominating)}
|
||||
value={stakingNominating}
|
||||
withBalance
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
private renderNominators () {
|
||||
const { nominatorsFor } = this.props;
|
||||
const { stakingNominatorsFor } = this.props;
|
||||
|
||||
if (!nominatorsFor) {
|
||||
if (!stakingNominatorsFor) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return nominatorsFor.map((nominator) => (
|
||||
return stakingNominatorsFor.map((nominator) => (
|
||||
<AddressMini
|
||||
key={nominator}
|
||||
value={nominator}
|
||||
@ -129,71 +128,65 @@ class Account extends React.PureComponent<Props, State> {
|
||||
}
|
||||
|
||||
private renderButtons () {
|
||||
const { address, intentions, nominee, t } = this.props;
|
||||
const { address, intentions, stakingNominating, t } = this.props;
|
||||
const isIntending = intentions.includes(address);
|
||||
const isNominating = !!nominee;
|
||||
const isNominating = !!stakingNominating;
|
||||
const canStake = !isIntending && !isNominating;
|
||||
|
||||
if (canStake) {
|
||||
return (
|
||||
<div className='staking--Account-buttons'>
|
||||
<Button.Group>
|
||||
<Button
|
||||
isPrimary
|
||||
onClick={this.stake}
|
||||
text={t('account.stake', {
|
||||
defaultValue: 'stake'
|
||||
})}
|
||||
/>
|
||||
<Button.Or />
|
||||
<Button
|
||||
isPrimary
|
||||
onClick={this.toggleNominate}
|
||||
text={t('account.nominate', {
|
||||
defaultValue: 'nominate'
|
||||
})}
|
||||
/>
|
||||
</Button.Group>
|
||||
</div>
|
||||
<Button.Group>
|
||||
<Button
|
||||
isPrimary
|
||||
onClick={this.stake}
|
||||
text={t('account.stake', {
|
||||
defaultValue: 'stake'
|
||||
})}
|
||||
/>
|
||||
<Button.Or />
|
||||
<Button
|
||||
isPrimary
|
||||
onClick={this.toggleNominate}
|
||||
text={t('account.nominate', {
|
||||
defaultValue: 'nominate'
|
||||
})}
|
||||
/>
|
||||
</Button.Group>
|
||||
);
|
||||
}
|
||||
|
||||
if (isNominating) {
|
||||
return (
|
||||
<div className='staking--Account-buttons'>
|
||||
<Button.Group>
|
||||
<UnnominateButton
|
||||
address={address || ''}
|
||||
nominating={nominee || ''}
|
||||
onClick={this.unnominate}
|
||||
/>
|
||||
</Button.Group>
|
||||
</div>
|
||||
<Button.Group>
|
||||
<UnnominateButton
|
||||
address={address || ''}
|
||||
nominating={stakingNominating || ''}
|
||||
onClick={this.unnominate}
|
||||
/>
|
||||
</Button.Group>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='staking--Account-buttons'>
|
||||
<Button.Group>
|
||||
<Button
|
||||
isNegative
|
||||
onClick={this.unstake}
|
||||
text={t('account.unstake', {
|
||||
defaultValue: 'unstake'
|
||||
})}
|
||||
/>
|
||||
</Button.Group>
|
||||
</div>
|
||||
<Button.Group>
|
||||
<Button
|
||||
isNegative
|
||||
onClick={this.unstake}
|
||||
text={t('account.unstake', {
|
||||
defaultValue: 'unstake'
|
||||
})}
|
||||
/>
|
||||
</Button.Group>
|
||||
);
|
||||
}
|
||||
|
||||
private send (extrinsic: SectionItem<Extrinsics>, values: Array<RawParam$Value>) {
|
||||
const { accountIndex, address, queueExtrinsic } = this.props;
|
||||
const { systemAccountIndexOf, address, queueExtrinsic } = this.props;
|
||||
const publicKey = decodeAddress(address);
|
||||
|
||||
queueExtrinsic({
|
||||
extrinsic,
|
||||
nonce: accountIndex || new BN(0),
|
||||
nonce: systemAccountIndexOf || new BN(0),
|
||||
publicKey,
|
||||
values
|
||||
});
|
||||
@ -231,28 +224,7 @@ class Account extends React.PureComponent<Props, State> {
|
||||
export default withMulti(
|
||||
Account,
|
||||
translate,
|
||||
withStorage(
|
||||
storage.staking.public.nominatorsFor,
|
||||
{
|
||||
propName: 'nominatorsFor',
|
||||
paramProp: 'address',
|
||||
transform: (publicKeys: Array<Uint8Array>) =>
|
||||
publicKeys.map(encodeAddress)
|
||||
}
|
||||
),
|
||||
withStorage(
|
||||
storage.staking.public.nominating,
|
||||
{
|
||||
propName: 'nominee',
|
||||
paramProp: 'address',
|
||||
transform: encodeAddress
|
||||
}
|
||||
),
|
||||
withStorage(
|
||||
storage.system.public.accountIndexOf,
|
||||
{
|
||||
propName: 'accountIndex',
|
||||
paramProp: 'address'
|
||||
}
|
||||
)
|
||||
withObservable('stakingNominatorsFor', { paramProp: 'address' }),
|
||||
withObservable('stakingNominating', { paramProp: 'address' }),
|
||||
withObservable('systemAccountIndexOf', { paramProp: 'address' })
|
||||
);
|
||||
|
||||
@ -2,41 +2,35 @@
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
|
||||
import { Header } from '@polkadot/primitives/header';
|
||||
import { I18nProps } from '@polkadot/ui-app/types';
|
||||
import { ApiProps, ExtendedBalance, ExtendedBalanceMap } from '@polkadot/ui-react-rx/types';
|
||||
|
||||
import BN from 'bn.js';
|
||||
import React from 'react';
|
||||
import apimethods from '@polkadot/jsonrpc';
|
||||
import storage from '@polkadot/storage';
|
||||
import classes from '@polkadot/ui-app/util/classes';
|
||||
import withApiCall from '@polkadot/ui-react-rx/with/apiCall';
|
||||
import withObservable from '@polkadot/ui-react-rx/with/observable';
|
||||
import withMulti from '@polkadot/ui-react-rx/with/multi';
|
||||
import withStorage from '@polkadot/ui-react-rx/with/storage';
|
||||
import numberFormat from '@polkadot/ui-react-rx/util/numberFormat';
|
||||
|
||||
import translate from './translate';
|
||||
|
||||
type Props = ApiProps & I18nProps & {
|
||||
balances: ExtendedBalanceMap,
|
||||
bestNumber?: BN,
|
||||
intentions: Array<string>,
|
||||
lastBlockHeader?: Header,
|
||||
lastLengthChange?: BN,
|
||||
sessionBlockProgress?: BN,
|
||||
sessionLength?: BN,
|
||||
validators: Array<string>
|
||||
};
|
||||
|
||||
const DEFAULT_BLOCKNUMBER = new BN(0);
|
||||
const DEFAULT_SESSION_CHANGE = new BN(0);
|
||||
const DEFAULT_SESSION_PROGRESS = new BN(0);
|
||||
const DEFAULT_SESSION_LENGTH = new BN(60);
|
||||
|
||||
class Summary extends React.PureComponent<Props> {
|
||||
render () {
|
||||
const { className, intentions, lastBlockHeader, lastLengthChange = DEFAULT_SESSION_CHANGE, style, sessionLength = DEFAULT_SESSION_LENGTH, t, validators } = this.props;
|
||||
const blockNumber = lastBlockHeader
|
||||
? lastBlockHeader.number
|
||||
: DEFAULT_BLOCKNUMBER;
|
||||
const { bestNumber = DEFAULT_BLOCKNUMBER, className, intentions, style, sessionLength = DEFAULT_SESSION_LENGTH, sessionBlockProgress = DEFAULT_SESSION_PROGRESS, t, validators } = this.props;
|
||||
const intentionHigh = this.calcIntentionsHigh();
|
||||
const validatorLow = this.calcValidatorLow();
|
||||
|
||||
@ -71,8 +65,8 @@ class Summary extends React.PureComponent<Props> {
|
||||
<div>{t('summary.countdown', {
|
||||
defaultValue: 'session block {{remainder}} / {{length}} at #{{blockNumber}}',
|
||||
replace: {
|
||||
blockNumber: numberFormat(blockNumber),
|
||||
remainder: Math.max(1, blockNumber.sub(lastLengthChange).mod(sessionLength).addn(1).toNumber()).toString(),
|
||||
blockNumber: numberFormat(bestNumber),
|
||||
remainder: sessionBlockProgress.toString(),
|
||||
length: sessionLength.toString()
|
||||
}
|
||||
})}</div>
|
||||
@ -114,16 +108,7 @@ class Summary extends React.PureComponent<Props> {
|
||||
export default withMulti(
|
||||
Summary,
|
||||
translate,
|
||||
withApiCall(
|
||||
apimethods.chain.public.newHead,
|
||||
{ propName: 'lastBlockHeader' }
|
||||
),
|
||||
withStorage(
|
||||
storage.session.public.length,
|
||||
{ propName: 'sessionLength' }
|
||||
),
|
||||
withStorage(
|
||||
storage.session.public.lastLengthChange,
|
||||
{ propName: 'lastLengthChange' }
|
||||
)
|
||||
withObservable('bestNumber'),
|
||||
withObservable('sessionBlockProgress'),
|
||||
withObservable('sessionLength')
|
||||
);
|
||||
|
||||
@ -5,27 +5,25 @@
|
||||
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 withObservable from '@polkadot/ui-react-rx/with/observable';
|
||||
|
||||
import translate from './translate';
|
||||
|
||||
type Props = I18nProps & {
|
||||
address: string,
|
||||
nominating: string,
|
||||
nominatorsFor: Array<string>,
|
||||
stakingNominatorsFor: Array<string>,
|
||||
onClick: (index: number) => void
|
||||
};
|
||||
|
||||
class UnnominateButton extends React.Component<Props> {
|
||||
render () {
|
||||
const { className, nominating, nominatorsFor, style, t } = this.props;
|
||||
const { className, nominating, stakingNominatorsFor, style, t } = this.props;
|
||||
|
||||
if (!nominating || !nominatorsFor || nominatorsFor.length === 0) {
|
||||
if (!nominating || !stakingNominatorsFor || stakingNominatorsFor.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -43,22 +41,14 @@ class UnnominateButton extends React.Component<Props> {
|
||||
}
|
||||
|
||||
onClick = () => {
|
||||
const { address, nominatorsFor, onClick } = this.props;
|
||||
const { address, stakingNominatorsFor, onClick } = this.props;
|
||||
|
||||
onClick(nominatorsFor.indexOf(address));
|
||||
onClick(stakingNominatorsFor.indexOf(address));
|
||||
}
|
||||
}
|
||||
|
||||
export default withMulti(
|
||||
UnnominateButton,
|
||||
translate,
|
||||
withStorage(
|
||||
storage.staking.public.nominatorsFor,
|
||||
{
|
||||
propName: 'nominatorsFor',
|
||||
paramProp: 'nominating',
|
||||
transform: (publicKeys: Array<Uint8Array>) =>
|
||||
publicKeys.map(encodeAddress)
|
||||
}
|
||||
)
|
||||
withObservable('stakingNominatorsFor', { paramProp: 'nominating' })
|
||||
);
|
||||
|
||||
@ -7,35 +7,32 @@
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.staking--StakeList {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.staking--Account {
|
||||
border: 1px solid #f5f5f5;
|
||||
border-radius: 0.25rem;
|
||||
display: inline-block;
|
||||
margin: 1rem;
|
||||
padding: 1rem 0;
|
||||
/* FIXME: HACK: We need flexbox layout */
|
||||
min-width: 528px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.staking--Account .ui--Button-Group {
|
||||
display: inline-block;
|
||||
margin-left: 1em;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.staking--Account .ui--Button-Group {
|
||||
text-align: right;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
.staking--Account .ui--AddressMini {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.staking--Account-expand {
|
||||
padding-left: 1em;
|
||||
.staking--Account .ui--AddressSummary {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.staking--Account-expand {
|
||||
min-width: 14rem;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.staking--Account-buttons,
|
||||
.staking--Account-details {
|
||||
display: inline-block;
|
||||
text-align: left;
|
||||
@ -73,8 +70,9 @@
|
||||
|
||||
.staking--Account-validating {
|
||||
opacity: 0.25 !important;
|
||||
padding-top: 8px;
|
||||
vertical-align: top !important;
|
||||
position: absolute;
|
||||
top: 1em;
|
||||
left: 1em;
|
||||
}
|
||||
|
||||
.staking--Account-validating.isValidator {
|
||||
|
||||
@ -6,12 +6,9 @@ import { BareProps } from '@polkadot/ui-app/types';
|
||||
import { ExtendedBalanceMap } from '@polkadot/ui-react-rx/types';
|
||||
|
||||
import React from 'react';
|
||||
import storage from '@polkadot/storage';
|
||||
import Page from '@polkadot/ui-app/Page';
|
||||
import encodeAddress from '@polkadot/util-keyring/address/encode';
|
||||
import classes from '@polkadot/ui-app/util/classes';
|
||||
import withApiObservable from '@polkadot/ui-react-rx/with/apiObservable';
|
||||
import withStorage from '@polkadot/ui-react-rx/with/storage';
|
||||
import withObservable from '@polkadot/ui-react-rx/with/observable';
|
||||
import withMulti from '@polkadot/ui-react-rx/with/multi';
|
||||
|
||||
import './index.css';
|
||||
@ -20,17 +17,14 @@ import StakeList from './StakeList';
|
||||
import Summary from './Summary';
|
||||
|
||||
type Props = BareProps & {
|
||||
balances?: ExtendedBalanceMap,
|
||||
intentions?: Array<string>,
|
||||
validators?: Array<string>
|
||||
validatingBalances?: ExtendedBalanceMap,
|
||||
stakingIntentions?: Array<string>,
|
||||
sessionValidators?: Array<string>
|
||||
};
|
||||
|
||||
const transformAddresses = (publicKeys: Array<Uint8Array>) =>
|
||||
publicKeys.map(encodeAddress);
|
||||
|
||||
class App extends React.PureComponent<Props> {
|
||||
render () {
|
||||
const { balances = {}, className, intentions = [], style, validators = [] } = this.props;
|
||||
const { className, sessionValidators = [], stakingIntentions = [], style, validatingBalances = {} } = this.props;
|
||||
|
||||
return (
|
||||
<Page
|
||||
@ -38,14 +32,14 @@ class App extends React.PureComponent<Props> {
|
||||
style={style}
|
||||
>
|
||||
<Summary
|
||||
balances={balances}
|
||||
intentions={intentions}
|
||||
validators={validators}
|
||||
balances={validatingBalances}
|
||||
intentions={stakingIntentions}
|
||||
validators={sessionValidators}
|
||||
/>
|
||||
<StakeList
|
||||
balances={balances}
|
||||
intentions={intentions}
|
||||
validators={validators}
|
||||
balances={validatingBalances}
|
||||
intentions={stakingIntentions}
|
||||
validators={sessionValidators}
|
||||
/>
|
||||
</Page>
|
||||
);
|
||||
@ -54,25 +48,7 @@ class App extends React.PureComponent<Props> {
|
||||
|
||||
export default withMulti(
|
||||
App,
|
||||
withStorage(
|
||||
storage.staking.public.intentions,
|
||||
{
|
||||
propName: 'intentions',
|
||||
transform: transformAddresses
|
||||
}
|
||||
),
|
||||
withStorage(
|
||||
storage.session.public.validators,
|
||||
{
|
||||
propName: 'validators',
|
||||
transform: transformAddresses
|
||||
}
|
||||
),
|
||||
withApiObservable(
|
||||
'validatingBalances',
|
||||
{
|
||||
paramProp: 'intentions',
|
||||
propName: 'balances'
|
||||
}
|
||||
)
|
||||
withObservable('stakingIntentions'),
|
||||
withObservable('sessionValidators'),
|
||||
withObservable('validatingBalances', { paramProp: 'stakingIntentions' })
|
||||
);
|
||||
|
||||
@ -13,7 +13,7 @@ import Button from '@polkadot/ui-app/Button';
|
||||
import Labelled from '@polkadot/ui-app/Labelled';
|
||||
import valueToText from '@polkadot/ui-app/Params/valueToText';
|
||||
import classes from '@polkadot/ui-app/util/classes';
|
||||
import withStorageDiv from '@polkadot/ui-react-rx/with/storageDiv';
|
||||
import withObservableDiv from '@polkadot/ui-react-rx/with/observableDiv';
|
||||
|
||||
import translate from './translate';
|
||||
|
||||
@ -41,7 +41,7 @@ class Query extends React.PureComponent<Props, State> {
|
||||
value as Storage$Key$Value
|
||||
);
|
||||
|
||||
cache[id] = withStorageDiv(key, { params: values })(
|
||||
cache[id] = withObservableDiv('rawStorage', { params: [key, ...values] })(
|
||||
(value: any) =>
|
||||
valueToText(key.type, value),
|
||||
{ className: 'ui--output' }
|
||||
|
||||
@ -16,8 +16,8 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.0.0-beta.51",
|
||||
"@polkadot/extrinsics": "^0.27.12",
|
||||
"@polkadot/storage": "^0.27.12",
|
||||
"@polkadot/extrinsics": "^0.28.9",
|
||||
"@polkadot/storage": "^0.28.9",
|
||||
"@polkadot/ui-keyring": "^0.19.29",
|
||||
"@polkadot/ui-react": "^0.19.29",
|
||||
"@polkadot/ui-react-rx": "^0.19.29",
|
||||
|
||||
28
packages/ui-app/src/Card.tsx
Normal file
28
packages/ui-app/src/Card.tsx
Normal file
@ -0,0 +1,28 @@
|
||||
// Copyright 2017-2018 @polkadot/ui-app authors & contributors
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
|
||||
import { BareProps } from './types';
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import classes from './util/classes';
|
||||
|
||||
type Props = BareProps & {
|
||||
children: React.ReactNode
|
||||
};
|
||||
|
||||
export default class Card extends React.PureComponent<Props> {
|
||||
render () {
|
||||
const { children, className, style } = this.props;
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classes('ui--Card', className)}
|
||||
style={style}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
59
packages/ui-app/src/CardSummary.tsx
Normal file
59
packages/ui-app/src/CardSummary.tsx
Normal file
@ -0,0 +1,59 @@
|
||||
// Copyright 2017-2018 @polkadot/ui-app authors & contributors
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
|
||||
import { BareProps } from './types';
|
||||
|
||||
import BN from 'bn.js';
|
||||
import React from 'react';
|
||||
import isUndefined from '@polkadot/util/is/undefined';
|
||||
|
||||
import Card from './Card';
|
||||
import Progress, { Colors as ProgressColors } from './Progress';
|
||||
import Labelled from './Labelled';
|
||||
import classes from './util/classes';
|
||||
|
||||
type ProgressProps = {
|
||||
color?: ProgressColors,
|
||||
total?: BN,
|
||||
value?: BN
|
||||
};
|
||||
|
||||
type Props = BareProps & {
|
||||
children?: React.ReactNode,
|
||||
label: React.ReactNode,
|
||||
progress?: ProgressProps
|
||||
};
|
||||
|
||||
export default class CardSummary extends React.PureComponent<Props> {
|
||||
render () {
|
||||
const { children, className, progress, label, style } = this.props;
|
||||
|
||||
return (
|
||||
<Card
|
||||
className={classes('ui--CardSummary', className)}
|
||||
style={style}
|
||||
>
|
||||
<Labelled label={label}>
|
||||
<div className='ui--CardSummary-large'>
|
||||
{children}{
|
||||
progress && (
|
||||
(isUndefined(progress.value) || progress.value.ltn(0)) || isUndefined(progress.total)
|
||||
? '-'
|
||||
: `${progress.value.toString()}/${progress.total.toString()}`
|
||||
)
|
||||
}
|
||||
</div>
|
||||
{
|
||||
progress && (
|
||||
<Progress
|
||||
className='ui--CardSummary-progress'
|
||||
{...progress}
|
||||
/>
|
||||
)
|
||||
}
|
||||
</Labelled>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -11,7 +11,7 @@ export default function getInitValueArray (type: Param$Type[]): RawParam$Value |
|
||||
// NOTE special cases for where we have a known override formatter. The only horrible thing with
|
||||
// this atm is that we need to add the same override in ./values.ts - which is actually horrible,
|
||||
// need a single place for both the actual raw values and wrapped values
|
||||
if (type.length === 1 && type[0] === 'KeyValueStorage') {
|
||||
if (type.length === 1 && type[0] === 'StorageKeyValue') {
|
||||
return [];
|
||||
}
|
||||
|
||||
|
||||
@ -9,43 +9,44 @@ import './Params.css';
|
||||
import React from 'react';
|
||||
import IdentityIcon from '@polkadot/ui-react/IdentityIcon';
|
||||
import numberFormat from '@polkadot/ui-react-rx/util/numberFormat';
|
||||
import addressEncode from '@polkadot/util-keyring/address/encode';
|
||||
import u8aToHex from '@polkadot/util/u8a/toHex';
|
||||
import isBn from '@polkadot/util/is/bn';
|
||||
import isNull from '@polkadot/util/is/null';
|
||||
import isU8a from '@polkadot/util/is/u8a';
|
||||
import isUndefined from '@polkadot/util/is/undefined';
|
||||
|
||||
import classes from '../util/classes';
|
||||
import { textMap as thresholdTextMap } from './Param/VoteThreshold';
|
||||
|
||||
const empty = div('<empty>');
|
||||
const unknown = div('<unknown>');
|
||||
const empty = div({}, '<empty>');
|
||||
const unknown = div({}, '<unknown>');
|
||||
|
||||
function divWithClass (className: string, ...values: Array<React.ReactNode>): React.ReactNode {
|
||||
type DivProps = {
|
||||
className?: string,
|
||||
key?: any
|
||||
};
|
||||
|
||||
function div ({ key, className }: DivProps, ...values: Array<React.ReactNode>): React.ReactNode {
|
||||
return (
|
||||
<div className={classes('ui--Param-text', className)}>
|
||||
<div
|
||||
className={classes('ui--Param-text', className)}
|
||||
key={key}
|
||||
>
|
||||
{values}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function div (...values: Array<React.ReactNode>): React.ReactNode {
|
||||
return divWithClass.apply(
|
||||
null, [undefined as React.ReactNode].concat(values)
|
||||
);
|
||||
}
|
||||
|
||||
function accountToText (publicKey: Uint8Array): React.ReactNode {
|
||||
const address = addressEncode(publicKey);
|
||||
|
||||
return divWithClass(
|
||||
'nowrap',
|
||||
function accountToText (address: string): React.ReactNode {
|
||||
return div(
|
||||
{ className: 'nowrap', key: `account_${address}` },
|
||||
<IdentityIcon
|
||||
className='icon'
|
||||
key='icon'
|
||||
size={24}
|
||||
value={address}
|
||||
/>,
|
||||
address
|
||||
div({ key: 'address' }, address)
|
||||
);
|
||||
}
|
||||
|
||||
@ -56,15 +57,17 @@ function proposalToText ({ extrinsic, params }: ExtrinsicDecoded): React.ReactNo
|
||||
|
||||
const inputs = extrinsic.params.map(({ name, type }, index) =>
|
||||
div(
|
||||
divWithClass('name', `${name}=`),
|
||||
{ key: `param_${index}` },
|
||||
div({ className: 'name', key: 'param_name' }, `${name}=`),
|
||||
valueToText(type, params[index])
|
||||
)
|
||||
);
|
||||
|
||||
return div(
|
||||
divWithClass('name', `${extrinsic.section}.${extrinsic.name}(`),
|
||||
{},
|
||||
div({ className: 'name' }, `${extrinsic.section}.${extrinsic.name}(`),
|
||||
inputs,
|
||||
divWithClass('name', ')')
|
||||
div({ className: 'name' }, ')')
|
||||
);
|
||||
}
|
||||
|
||||
@ -75,12 +78,13 @@ function arrayToText (type: Param$Type$Array, value: Array<any>, withBound: bool
|
||||
|
||||
if (type.length === 1) {
|
||||
if (type[0] === 'KeyValueStorage') {
|
||||
return div(value.length);
|
||||
return div({}, value.length);
|
||||
}
|
||||
|
||||
return value.map((value, index) =>
|
||||
div(
|
||||
divWithClass('name', `${index}:`),
|
||||
{ key: `value_${index}` },
|
||||
div({ className: 'name', key: 'name' }, `${index}:`),
|
||||
valueToText(type[0], value, false)
|
||||
)
|
||||
);
|
||||
@ -91,16 +95,17 @@ function arrayToText (type: Param$Type$Array, value: Array<any>, withBound: bool
|
||||
);
|
||||
|
||||
return div(
|
||||
divWithClass('name', '('),
|
||||
{},
|
||||
div({ className: 'name' }, '('),
|
||||
values,
|
||||
divWithClass('name', ')')
|
||||
div({ className: 'name' }, ')')
|
||||
);
|
||||
}
|
||||
|
||||
function valueToText (type: Param$Types, value: any, swallowError: boolean = true): React.ReactNode {
|
||||
try {
|
||||
if (type === 'bool') {
|
||||
return div(value ? 'Yes' : 'No');
|
||||
return div({}, value ? 'Yes' : 'No');
|
||||
}
|
||||
|
||||
if (isUndefined(value)) {
|
||||
@ -113,7 +118,7 @@ function valueToText (type: Param$Types, value: any, swallowError: boolean = tru
|
||||
|
||||
if (type === 'AccountId') {
|
||||
return value && value.length
|
||||
? accountToText(value as Uint8Array)
|
||||
? accountToText(value as string)
|
||||
: unknown;
|
||||
}
|
||||
|
||||
@ -122,15 +127,15 @@ function valueToText (type: Param$Types, value: any, swallowError: boolean = tru
|
||||
}
|
||||
|
||||
if (type === 'VoteThreshold') {
|
||||
return div(thresholdTextMap[value]);
|
||||
return div({}, thresholdTextMap[value]);
|
||||
}
|
||||
|
||||
if (isU8a(value)) {
|
||||
return div(u8aToHex(value, 256));
|
||||
return div({}, u8aToHex(value, 256));
|
||||
}
|
||||
|
||||
if (isBn(value)) {
|
||||
return div(numberFormat(value));
|
||||
return div({}, numberFormat(value));
|
||||
}
|
||||
} catch (error) {
|
||||
if (!swallowError) {
|
||||
@ -140,7 +145,9 @@ function valueToText (type: Param$Types, value: any, swallowError: boolean = tru
|
||||
}
|
||||
}
|
||||
|
||||
return div(value.toString());
|
||||
return isNull(value) || isUndefined(value)
|
||||
? unknown
|
||||
: div({}, value.toString());
|
||||
}
|
||||
|
||||
export default valueToText;
|
||||
|
||||
64
packages/ui-app/src/Progress.tsx
Normal file
64
packages/ui-app/src/Progress.tsx
Normal file
@ -0,0 +1,64 @@
|
||||
// Copyright 2017-2018 @polkadot/ui-app authors & contributors
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
|
||||
import { BareProps } from './types';
|
||||
|
||||
import BN from 'bn.js';
|
||||
import React from 'react';
|
||||
import SUIProgress from 'semantic-ui-react/dist/commonjs/modules/Progress/Progress';
|
||||
import isBn from '@polkadot/util/is/bn';
|
||||
import isUndefined from '@polkadot/util/is/undefined';
|
||||
|
||||
import classes from './util/classes';
|
||||
|
||||
type BaseColors = 'blue' | 'green' | 'red' | 'orange';
|
||||
export type Colors = 'auto' | 'autoReverse' | BaseColors;
|
||||
|
||||
type Props = BareProps & {
|
||||
color?: Colors,
|
||||
percent?: BN | number,
|
||||
total?: BN | number,
|
||||
value?: BN | number
|
||||
};
|
||||
|
||||
export default class Progress extends React.PureComponent<Props> {
|
||||
render () {
|
||||
const { className, color = 'blue', percent, total, style, value } = this.props;
|
||||
let calculated: number | undefined;
|
||||
|
||||
if (!isUndefined(value) && !isUndefined(total) && (isBn(total) ? total.gtn(0) : total > 0)) {
|
||||
calculated = 100.0 * (isBn(value) ? value.toNumber() : value) / (isBn(total) ? total.toNumber() : total);
|
||||
} else {
|
||||
calculated = isBn(percent) ? percent.toNumber() : percent;
|
||||
}
|
||||
|
||||
if (isUndefined(calculated) || calculated < 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let rainbow: BaseColors;
|
||||
|
||||
if (color === 'auto' || color === 'autoReverse') {
|
||||
if (calculated > 66.6) {
|
||||
rainbow = color === 'auto' ? 'green' : 'red';
|
||||
} else if (calculated > 33.3) {
|
||||
rainbow = 'orange';
|
||||
} else {
|
||||
rainbow = color === 'auto' ? 'red' : 'green';
|
||||
}
|
||||
} else {
|
||||
rainbow = color;
|
||||
}
|
||||
|
||||
return (
|
||||
<SUIProgress
|
||||
className={classes('ui--Progress', className)}
|
||||
color={rainbow}
|
||||
percent={calculated}
|
||||
size='tiny'
|
||||
style={style}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -123,6 +123,30 @@
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.ui--Card {
|
||||
background: #fefefe;
|
||||
border: 1px solid #f5f5f5;
|
||||
border-radius: 0.25rem;
|
||||
margin: 0.5rem;
|
||||
padding: 1.5rem;
|
||||
}
|
||||
|
||||
.ui--CardSummary .ui.label {
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
.ui--CardSummary-large {
|
||||
font-size: 3rem;
|
||||
line-height: 3rem;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.ui--CardSummary-progress {
|
||||
margin: 0.25rem 0 -0.5rem !important;
|
||||
}
|
||||
|
||||
.ui--Navigation {
|
||||
margin-bottom: 2em;
|
||||
text-align: center;
|
||||
|
||||
@ -69,6 +69,10 @@
|
||||
display: flex !important;
|
||||
}
|
||||
|
||||
.ui.progress .bar {
|
||||
min-width: 0 !important;
|
||||
}
|
||||
|
||||
.ui[class*="left icon"].input.left.icon > input {
|
||||
padding-left: 4rem !important;
|
||||
}
|
||||
|
||||
@ -22,7 +22,7 @@
|
||||
"color": "^3.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@polkadot/util-crypto": "^0.27.12",
|
||||
"@polkadot/util-crypto": "^0.28.1",
|
||||
"xmlserializer": "^0.6.1"
|
||||
}
|
||||
}
|
||||
|
||||
@ -17,7 +17,7 @@
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.0.0-beta.51",
|
||||
"@polkadot/ui-react": "^0.19.29",
|
||||
"@polkadot/util-keyring": "^0.27.12",
|
||||
"@polkadot/util-keyring": "^0.28.1",
|
||||
"store": "^2.0.12"
|
||||
},
|
||||
"peerDependencies": {
|
||||
|
||||
@ -36,9 +36,9 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.0.0-beta.51",
|
||||
"@polkadot/api-rx": "^0.15.9",
|
||||
"@polkadot/extrinsics": "^0.27.12",
|
||||
"@polkadot/storage": "^0.27.12",
|
||||
"@polkadot/api-rx": "^0.28.9",
|
||||
"@polkadot/extrinsics": "^0.28.9",
|
||||
"@polkadot/storage": "^0.28.9",
|
||||
"rxjs-compat": "^6.2.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@ -2,14 +2,25 @@
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
|
||||
import { RxApiInterface } from '@polkadot/api-rx/types';
|
||||
import { ExtendedBalance, ExtendedBalanceMap, ObservableApiInterface } from './types';
|
||||
import { Header } from '@polkadot/primitives/header';
|
||||
import { RxApiInterface, RxApiInterface$Method } from '@polkadot/api-rx/types';
|
||||
import { Interfaces } from '@polkadot/jsonrpc/types';
|
||||
import { SectionItem } from '@polkadot/params/types';
|
||||
import { Storages } from '@polkadot/storage/types';
|
||||
import { ExtendedBalance, ExtendedBalanceMap, ObservableApiInterface, KeyWithParams } from './types';
|
||||
|
||||
import BN from 'bn.js';
|
||||
import { Observable, combineLatest } from 'rxjs';
|
||||
import { map, switchMap, toArray } from 'rxjs/operators';
|
||||
import { concatMap, defaultIfEmpty, map } from 'rxjs/operators';
|
||||
import storage from '@polkadot/storage';
|
||||
import encodeAddress from '@polkadot/util-keyring/address/encode';
|
||||
import assert from '@polkadot/util/assert';
|
||||
|
||||
type OptBN = BN | undefined;
|
||||
type OptDate = Date | undefined;
|
||||
type MapFn<R, T> = (combined: R) => T;
|
||||
|
||||
const defaultMapFn = (result: any): any =>
|
||||
result;
|
||||
|
||||
export default class ObservableApi implements ObservableApiInterface {
|
||||
private api: RxApiInterface;
|
||||
@ -18,68 +29,318 @@ export default class ObservableApi implements ObservableApiInterface {
|
||||
this.api = api;
|
||||
}
|
||||
|
||||
private combine = <T, R> (observables: Array<Observable<any>>, mapfn: MapFn<R, T> = defaultMapFn): Observable<T> => {
|
||||
return combineLatest(...observables).pipe(
|
||||
defaultIfEmpty([]),
|
||||
map(mapfn)
|
||||
);
|
||||
}
|
||||
|
||||
rawCall = <T> ({ name, section }: SectionItem<Interfaces>, ...params: Array<any>): Observable<T> => {
|
||||
assert(section && this.api[section], `Unable to find 'api.${section}'`);
|
||||
|
||||
const fn: RxApiInterface$Method = section
|
||||
? this.api[section][name]
|
||||
// @ts-ignore This one is done for 'isConnected'
|
||||
: this.api[name];
|
||||
|
||||
assert(fn, `Unable to find 'api${section ? '.' : ''}${section || ''}.${name}'`);
|
||||
|
||||
return fn.apply(null, params);
|
||||
}
|
||||
|
||||
rawStorage = <T> (key: SectionItem<Storages>, ...params: Array<any>): Observable<T> => {
|
||||
return this
|
||||
.rawStorageMulti([key, ...params] as KeyWithParams)
|
||||
.pipe(map(([result]: Array<T>): T =>
|
||||
result
|
||||
));
|
||||
}
|
||||
|
||||
rawStorageMulti = <T> (...keys: Array<KeyWithParams>): Observable<T> => {
|
||||
return this.api.state
|
||||
.subscribeStorage(keys)
|
||||
.pipe(map((result?: any) =>
|
||||
result || []
|
||||
));
|
||||
}
|
||||
|
||||
bestNumber = (): Observable<OptBN> => {
|
||||
return this.chainNewHead().pipe(
|
||||
map((header?: Header): OptBN =>
|
||||
header && header.number
|
||||
? header.number
|
||||
: undefined
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
chainNewHead = (): Observable<Header | undefined> => {
|
||||
return this.api.chain.subscribeNewHead();
|
||||
}
|
||||
|
||||
eraBlockLength = (): Observable<OptBN> => {
|
||||
return this.combine(
|
||||
[
|
||||
this.sessionLength(),
|
||||
this.sessionsPerEra()
|
||||
],
|
||||
([sessionLength, sessionsPerEra]: [OptBN, OptBN]): OptBN =>
|
||||
sessionLength && sessionsPerEra
|
||||
? sessionLength.mul(sessionsPerEra)
|
||||
: undefined
|
||||
);
|
||||
}
|
||||
|
||||
eraBlockProgress = (): Observable<OptBN> => {
|
||||
return this.combine(
|
||||
[
|
||||
this.sessionBlockProgress(),
|
||||
this.sessionLength(),
|
||||
this.sessionCurrentIndex(),
|
||||
this.sessionsPerEra(),
|
||||
this.eraLastLengthChange()
|
||||
],
|
||||
([sessionBlockProgress, sessionLength, sessionCurrentIndex, sessionsPerEra, eraLastLengthChange = new BN(0)]: [OptBN, OptBN, OptBN, OptBN, OptBN]): OptBN =>
|
||||
sessionsPerEra && sessionCurrentIndex && sessionLength && sessionBlockProgress && eraLastLengthChange
|
||||
? sessionCurrentIndex
|
||||
.sub(eraLastLengthChange)
|
||||
.mod(sessionsPerEra)
|
||||
.mul(sessionLength)
|
||||
.add(sessionBlockProgress)
|
||||
: undefined
|
||||
);
|
||||
}
|
||||
|
||||
eraBlockRemaining = (): Observable<OptBN> => {
|
||||
return this.combine(
|
||||
[
|
||||
this.eraBlockLength(),
|
||||
this.eraBlockProgress()
|
||||
],
|
||||
([eraBlockLength, eraBlockProgress]: [OptBN, OptBN]): OptBN =>
|
||||
eraBlockLength && eraBlockProgress
|
||||
? eraBlockLength.sub(eraBlockProgress)
|
||||
: undefined
|
||||
);
|
||||
}
|
||||
|
||||
eraLastLengthChange = (): Observable<OptBN> => {
|
||||
return this.rawStorage(storage.staking.public.lastEraLengthChange);
|
||||
}
|
||||
|
||||
sessionBlockProgress = (): Observable<OptBN> => {
|
||||
return this.combine(
|
||||
[
|
||||
this.bestNumber(),
|
||||
this.sessionLength(),
|
||||
this.sessionLastLengthChange()
|
||||
],
|
||||
([bestNumber, sessionLength, lastSessionLengthChange]: [OptBN, OptBN, OptBN]): OptBN =>
|
||||
bestNumber && sessionLength && lastSessionLengthChange
|
||||
? bestNumber
|
||||
.sub(lastSessionLengthChange)
|
||||
.add(sessionLength)
|
||||
.mod(sessionLength)
|
||||
: undefined
|
||||
);
|
||||
}
|
||||
|
||||
sessionBlockRemaining = (): Observable<OptBN> => {
|
||||
return this.combine(
|
||||
[
|
||||
this.sessionBlockProgress(),
|
||||
this.sessionLength()
|
||||
],
|
||||
([sessionBlockProgress, sessionLength]: [OptBN, OptBN]): OptBN =>
|
||||
sessionBlockProgress && sessionLength
|
||||
? sessionLength.sub(sessionBlockProgress)
|
||||
: undefined
|
||||
);
|
||||
}
|
||||
|
||||
sessionBrokenPercentLate = (): Observable<OptBN> => {
|
||||
return this.rawStorage(storage.session.public.brokenPercentLate);
|
||||
}
|
||||
|
||||
sessionBrokenValue = (): Observable<OptBN> => {
|
||||
return this.combine(
|
||||
[
|
||||
this.timestampNow(),
|
||||
this.sessionTimeExpected(),
|
||||
this.sessionTimeRemaining(),
|
||||
this.sessionCurrentStart()
|
||||
],
|
||||
([now, sessionTimeExpected, sessionTimeRemaining, sessionCurrentStart]: [OptDate, OptBN, OptBN, OptDate]): OptBN =>
|
||||
sessionTimeExpected && sessionTimeRemaining && sessionCurrentStart && now
|
||||
? new BN(
|
||||
Math.round(
|
||||
100 * (now.getTime() + sessionTimeRemaining.toNumber() - sessionCurrentStart.getTime()) / sessionTimeExpected.toNumber() - 100
|
||||
)
|
||||
)
|
||||
: undefined
|
||||
);
|
||||
}
|
||||
|
||||
sessionCurrentIndex = (): Observable<OptBN> => {
|
||||
return this.rawStorage(storage.session.public.currentIndex);
|
||||
}
|
||||
|
||||
sessionCurrentStart = (): Observable<OptBN> => {
|
||||
return this.rawStorage(storage.session.public.currentStart);
|
||||
}
|
||||
|
||||
sessionLastLengthChange = (): Observable<OptBN> => {
|
||||
return this.rawStorage(storage.session.public.lastLengthChange);
|
||||
}
|
||||
|
||||
sessionLength = (): Observable<OptBN> => {
|
||||
return this.rawStorage(storage.session.public.length);
|
||||
}
|
||||
|
||||
sessionsPerEra = (): Observable<OptBN> => {
|
||||
return this.rawStorage(storage.staking.public.sessionsPerEra);
|
||||
}
|
||||
|
||||
sessionTimeExpected = (): Observable<OptBN> => {
|
||||
return this.combine(
|
||||
[
|
||||
this.sessionLength(),
|
||||
this.timestampBlockPeriod()
|
||||
],
|
||||
([sessionLength, blockPeriod]: [OptBN, OptBN]): OptBN =>
|
||||
sessionLength && blockPeriod
|
||||
? blockPeriod.mul(sessionLength).muln(1000)
|
||||
: undefined
|
||||
);
|
||||
}
|
||||
|
||||
sessionTimeRemaining = (): Observable<OptBN> => {
|
||||
return this.combine(
|
||||
[
|
||||
this.sessionBlockRemaining(),
|
||||
this.timestampBlockPeriod()
|
||||
],
|
||||
([sessionBlockRemaining, blockPeriod]: [OptBN, OptBN]): OptBN =>
|
||||
blockPeriod && sessionBlockRemaining
|
||||
? blockPeriod.mul(sessionBlockRemaining).muln(1000)
|
||||
: undefined
|
||||
);
|
||||
}
|
||||
|
||||
sessionValidators = (): Observable<Array<string>> => {
|
||||
return this
|
||||
.rawStorage(storage.session.public.validators)
|
||||
.pipe(map((validators: Array<string> = []) =>
|
||||
validators
|
||||
));
|
||||
}
|
||||
|
||||
stakingIntentions = (): Observable<Array<string>> => {
|
||||
return this
|
||||
.rawStorage(storage.staking.public.intentions)
|
||||
.pipe(map((intentions: Array<string> = []) =>
|
||||
intentions
|
||||
));
|
||||
}
|
||||
|
||||
stakingFreeBalanceOf = (address: string): Observable<OptBN> => {
|
||||
return this.rawStorage(storage.staking.public.freeBalanceOf, address);
|
||||
}
|
||||
|
||||
stakingNominatorsFor = (address: string): Observable<Array<string>> => {
|
||||
return this
|
||||
.rawStorage(storage.staking.public.nominatorsFor, address)
|
||||
.pipe(map((nominators: Array<string> = []) =>
|
||||
nominators
|
||||
));
|
||||
}
|
||||
|
||||
stakingNominating = (address: string): Observable<string | undefined> => {
|
||||
return this.rawStorage(storage.staking.public.nominating, address);
|
||||
}
|
||||
|
||||
stakingReservedBalanceOf = (address: string): Observable<OptBN> => {
|
||||
return this.rawStorage(storage.staking.public.reservedBalanceOf, address);
|
||||
}
|
||||
|
||||
timestampBlockPeriod = (): Observable<OptBN> => {
|
||||
return this.rawStorage(storage.timestamp.public.blockPeriod);
|
||||
}
|
||||
|
||||
timestampNow = (): Observable<OptDate> => {
|
||||
return this.rawStorage(storage.timestamp.public.now);
|
||||
}
|
||||
|
||||
systemAccountIndexOf = (address: string): Observable<OptBN> => {
|
||||
return this.rawStorage(storage.system.public.accountIndexOf, address);
|
||||
}
|
||||
|
||||
validatingBalance = (address: string): Observable<ExtendedBalance> => {
|
||||
return combineLatest(
|
||||
this.votingBalance(address),
|
||||
this.api.state
|
||||
.getStorage(storage.staking.public.nominatorsFor, address)
|
||||
.pipe(
|
||||
switchMap((nominators = []) =>
|
||||
// FIXME: Will not be needed after https://github.com/polkadot-js/apps/issues/128
|
||||
this.votingBalances(nominators.map(encodeAddress))
|
||||
),
|
||||
toArray()
|
||||
)
|
||||
).pipe(
|
||||
map(([balance, [nominators = []]]: [ExtendedBalance, [Array<ExtendedBalance>]]): ExtendedBalance => {
|
||||
return this.combine(
|
||||
[
|
||||
this.votingBalance(address),
|
||||
this.votingBalancesNominatorsFor(address)
|
||||
],
|
||||
([balance, nominators = []]: [ExtendedBalance, Array<ExtendedBalance>]): ExtendedBalance => {
|
||||
const nominatedBalance = nominators.reduce((total, nominator: ExtendedBalance) => {
|
||||
return total.add(nominator.votingBalance);
|
||||
}, new BN(0));
|
||||
|
||||
return {
|
||||
const result = {
|
||||
...balance,
|
||||
nominators,
|
||||
nominatedBalance,
|
||||
stakingBalance: nominatedBalance.add(balance.votingBalance)
|
||||
};
|
||||
})
|
||||
|
||||
return result;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
validatingBalances = (addresses: Array<string>): Observable<ExtendedBalanceMap> => {
|
||||
return combineLatest(
|
||||
validatingBalances = (...addresses: Array<string>): Observable<ExtendedBalanceMap> => {
|
||||
return this.combine(
|
||||
addresses.map((address) =>
|
||||
this.validatingBalance(address)
|
||||
)
|
||||
).pipe(
|
||||
map((result: Array<ExtendedBalance>): ExtendedBalanceMap => {
|
||||
return result.reduce((balances, balance) => {
|
||||
),
|
||||
(result: Array<ExtendedBalance>): ExtendedBalanceMap =>
|
||||
result.reduce((balances, balance) => {
|
||||
balances[balance.address] = balance;
|
||||
|
||||
return balances;
|
||||
}, {} as ExtendedBalanceMap);
|
||||
})
|
||||
}, {} as ExtendedBalanceMap)
|
||||
);
|
||||
}
|
||||
|
||||
votingBalance = (address: string): Observable<ExtendedBalance> => {
|
||||
return combineLatest(
|
||||
this.api.state.getStorage(storage.staking.public.freeBalanceOf, address),
|
||||
this.api.state.getStorage(storage.staking.public.reservedBalanceOf, address)
|
||||
).pipe(
|
||||
map(([freeBalance = new BN(0), reservedBalance = new BN(0)]): ExtendedBalance => ({
|
||||
return this.combine(
|
||||
[
|
||||
this.stakingFreeBalanceOf(address),
|
||||
this.stakingReservedBalanceOf(address)
|
||||
],
|
||||
([freeBalance = new BN(0), reservedBalance = new BN(0)]: [OptBN, OptBN]): ExtendedBalance => ({
|
||||
address,
|
||||
freeBalance,
|
||||
nominatedBalance: new BN(0),
|
||||
reservedBalance,
|
||||
stakingBalance: new BN(0),
|
||||
votingBalance: freeBalance.add(reservedBalance)
|
||||
}))
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
votingBalances = (addresses: Array<string>): Observable<ExtendedBalance[]> => {
|
||||
return combineLatest(
|
||||
votingBalancesNominatorsFor = (address: string) => {
|
||||
return this.stakingNominatorsFor(address).pipe(
|
||||
concatMap((nominators: Array<string>) =>
|
||||
this.votingBalances(...nominators)
|
||||
),
|
||||
defaultIfEmpty([])
|
||||
);
|
||||
}
|
||||
|
||||
votingBalances = (...addresses: Array<string>): Observable<ExtendedBalance[]> => {
|
||||
return this.combine(
|
||||
addresses.map((address) =>
|
||||
this.votingBalance(address)
|
||||
)
|
||||
|
||||
@ -2,14 +2,10 @@
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
|
||||
import storage from '@polkadot/storage';
|
||||
|
||||
import numberFormat from './util/numberFormat';
|
||||
import withStorageDiv from './with/storageDiv';
|
||||
import withObservableDiv from './with/observableDiv';
|
||||
|
||||
const method = storage.staking.public.freeBalanceOf;
|
||||
|
||||
const Component: React.ComponentType<any> = withStorageDiv(method)(
|
||||
const Component: React.ComponentType<any> = withObservableDiv('stakingFreeBalanceOf')(
|
||||
numberFormat,
|
||||
{ className: 'rx--Balance' }
|
||||
);
|
||||
|
||||
@ -2,19 +2,15 @@
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
|
||||
import { Header } from '@polkadot/primitives/header';
|
||||
|
||||
import rpcs from '@polkadot/jsonrpc';
|
||||
import BN from 'bn.js';
|
||||
|
||||
import numberFormat from './util/numberFormat';
|
||||
import withApiDiv from './with/apiDiv';
|
||||
import withObservableDiv from './with/observableDiv';
|
||||
|
||||
const method = rpcs.chain.public.newHead;
|
||||
|
||||
const Component: React.ComponentType<any> = withApiDiv(method)(
|
||||
(value?: Header): string => {
|
||||
return value && value.number
|
||||
? numberFormat(value.number)
|
||||
const Component: React.ComponentType<any> = withObservableDiv('bestNumber')(
|
||||
(value?: BN): string => {
|
||||
return value
|
||||
? numberFormat(value)
|
||||
: 'unknown';
|
||||
},
|
||||
{ className: 'rx--BestNumber' }
|
||||
|
||||
@ -1,22 +0,0 @@
|
||||
// Copyright 2017-2018 @polkadot/ui-react-rx authors & contributors
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
|
||||
import storage from '@polkadot/storage';
|
||||
|
||||
import withStorageDiv from './with/storageDiv';
|
||||
|
||||
const method = storage.timestamp.public.current;
|
||||
|
||||
const Component: React.ComponentType<any> = withStorageDiv(method)(
|
||||
(value?: Date): string => {
|
||||
if (!value || value.getTime() === 0) {
|
||||
return 'unknown';
|
||||
}
|
||||
|
||||
return value.toString();
|
||||
},
|
||||
{ className: 'rx--NodeTime' }
|
||||
);
|
||||
|
||||
export default Component;
|
||||
@ -2,14 +2,10 @@
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
|
||||
import storage from '@polkadot/storage';
|
||||
|
||||
import numberFormat from './util/numberFormat';
|
||||
import withStorageDiv from './with/storageDiv';
|
||||
import withObservableDiv from './with/observableDiv';
|
||||
|
||||
const method = storage.system.public.accountIndexOf;
|
||||
|
||||
const Component: React.ComponentType<any> = withStorageDiv(method)(
|
||||
const Component: React.ComponentType<any> = withObservableDiv('systemAccountIndexOf')(
|
||||
numberFormat,
|
||||
{ className: 'rx--Nonce' }
|
||||
);
|
||||
|
||||
13
packages/ui-react-rx/src/TimeNow.ts
Normal file
13
packages/ui-react-rx/src/TimeNow.ts
Normal file
@ -0,0 +1,13 @@
|
||||
// Copyright 2017-2018 @polkadot/ui-react-rx authors & contributors
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
|
||||
import Elapsed from '@polkadot/ui-react/Elapsed';
|
||||
import withApiObservble from './with/observable';
|
||||
|
||||
const Component: React.ComponentType<any> = withApiObservble('timestampNow', { propName: 'value' })(
|
||||
Elapsed,
|
||||
{ className: 'rx--TimeNow' }
|
||||
);
|
||||
|
||||
export default Component;
|
||||
16
packages/ui-react-rx/src/TimePeriod.ts
Normal file
16
packages/ui-react-rx/src/TimePeriod.ts
Normal file
@ -0,0 +1,16 @@
|
||||
// Copyright 2017-2018 @polkadot/ui-react-rx authors & contributors
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
|
||||
import BN from 'bn.js';
|
||||
|
||||
import numberFormat from './util/numberFormat';
|
||||
import withObservableDiv from './with/observableDiv';
|
||||
|
||||
const Component: React.ComponentType<any> = withObservableDiv('timestampBlockPeriod')(
|
||||
(value?: BN): string =>
|
||||
`${numberFormat(value)}.0s`,
|
||||
{ className: 'rx--TimePeriod' }
|
||||
);
|
||||
|
||||
export default Component;
|
||||
@ -12,7 +12,6 @@ import BestNumber from './BestNumber';
|
||||
import Chain from './Chain';
|
||||
import Connected from './Connected';
|
||||
import NodeName from './NodeName';
|
||||
import NodeTime from './NodeTime';
|
||||
import NodeVersion from './NodeVersion';
|
||||
import Nonce from './Nonce';
|
||||
import withApi from './with/api';
|
||||
@ -46,7 +45,6 @@ ReactDOM.render(
|
||||
<BestNumber label='best block #' />
|
||||
<Balance label='balance =' params={[ALICE_PUBLIC]} />
|
||||
<Nonce label='nonce =' params={ALICE_ADDR} />
|
||||
<NodeTime label='node time =' />
|
||||
<br />
|
||||
<WithApiDebug />
|
||||
</Api>,
|
||||
|
||||
@ -9,12 +9,9 @@ import Chain from './Chain';
|
||||
import Connected from './Connected';
|
||||
import Div from './Div';
|
||||
import NodeName from './NodeName';
|
||||
import NodeTime from './NodeTime';
|
||||
import NodeVersion from './NodeVersion';
|
||||
import Nonce from './Nonce';
|
||||
|
||||
export * from './with';
|
||||
|
||||
export {
|
||||
Api,
|
||||
Balance,
|
||||
@ -23,7 +20,6 @@ export {
|
||||
Connected,
|
||||
Div,
|
||||
NodeName,
|
||||
NodeTime,
|
||||
NodeVersion,
|
||||
Nonce
|
||||
};
|
||||
|
||||
40
packages/ui-react-rx/src/types.d.ts
vendored
40
packages/ui-react-rx/src/types.d.ts
vendored
@ -5,9 +5,11 @@
|
||||
import BN from 'bn.js';
|
||||
import { Observable } from 'rxjs';
|
||||
import { ProviderInterface } from '@polkadot/api-provider/types';
|
||||
import { RxApiInterface } from '@polkadot/api-rx/types';
|
||||
import { EncodingVersions } from '@polkadot/params/types';
|
||||
import { Storage$Key$Value } from '@polkadot/storage/types';
|
||||
import { RxApiInterface, RxApiInterface$Method } from '@polkadot/api-rx/types';
|
||||
import { Interfaces } from '@polkadot/jsonrpc/types';
|
||||
import { EncodingVersions, SectionItem } from '@polkadot/params/types';
|
||||
import { Header } from '@polkadot/primitives/header';
|
||||
import { Storages, Storage$Key$Value } from '@polkadot/storage/types';
|
||||
|
||||
export type BareProps = {
|
||||
className?: string,
|
||||
@ -28,13 +30,41 @@ export type ExtendedBalanceMap = {
|
||||
[index: string]: ExtendedBalance
|
||||
}
|
||||
|
||||
export type KeyWithParams = [SectionItem<Storages>, any];
|
||||
|
||||
export interface ObservableApiInterface {
|
||||
rawCall: <T> ({ name, section }: SectionItem<Interfaces>, ...params: Array<any>) => Observable<T>,
|
||||
rawStorage: <T> (key: SectionItem<Storages>, ...params: Array<any>) => Observable<T>,
|
||||
rawStorageMulti: <T> (...keys: Array<[SectionItem<Storages>, any]>) => Observable<T>,
|
||||
bestNumber: () => Observable<BN | undefined>,
|
||||
chainNewHead: () => Observable<Header | undefined>,
|
||||
eraBlockLength: () => Observable<BN | undefined>,
|
||||
eraBlockProgress: () => Observable<BN | undefined>,
|
||||
eraBlockRemaining: () => Observable<BN | undefined>,
|
||||
sessionBlockProgress: () => Observable<BN | undefined>,
|
||||
sessionBlockRemaining: () => Observable<BN | undefined>,
|
||||
sessionBrokenPercentLate: () => Observable<BN | undefined>,
|
||||
sessionBrokenValue: () => Observable<BN | undefined>,
|
||||
sessionLength: () => Observable<BN | undefined>,
|
||||
sessionTimeExpected: () => Observable<BN | undefined>,
|
||||
sessionTimeRemaining: () => Observable<BN | undefined>,
|
||||
sessionValidators: () => Observable<Array<string>>,
|
||||
stakingFreeBalanceOf: (address: string) => Observable<BN | undefined>,
|
||||
stakingIntentions: () => Observable<Array<string>>,
|
||||
stakingNominatorsFor: (address: string) => Observable<Array<string>>,
|
||||
stakingNominating: (address: string) => Observable<string | undefined>,
|
||||
stakingReservedBalanceOf: (address: string) => Observable<BN | undefined>,
|
||||
systemAccountIndexOf: (address: string) => Observable<BN | undefined>,
|
||||
timestampBlockPeriod: () => Observable<BN | undefined>,
|
||||
timestampNow: () => Observable<Date | undefined>,
|
||||
validatingBalance: (address: string) => Observable<ExtendedBalance>,
|
||||
validatingBalances: (addresses: Array<string>) => Observable<ExtendedBalanceMap>,
|
||||
validatingBalances: (...addresses: Array<string>) => Observable<ExtendedBalanceMap>,
|
||||
votingBalance: (address: string) => Observable<ExtendedBalance>,
|
||||
votingBalances: (addresses: Array<string>) => Observable<ExtendedBalance[]>
|
||||
votingBalances: (...addresses: Array<string>) => Observable<ExtendedBalance[]>
|
||||
}
|
||||
|
||||
export type ObservableApiNames = keyof ObservableApiInterface;
|
||||
|
||||
export type ApiProps = {
|
||||
api: RxApiInterface,
|
||||
apiConnected: boolean,
|
||||
|
||||
@ -1,66 +0,0 @@
|
||||
// Copyright 2017-2018 @polkadot/ui-react-rx authors & contributors
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
|
||||
import { RxApiInterface$Method } from '@polkadot/api-rx/types';
|
||||
import { ParamProps } from '../types';
|
||||
import { ApiMethod, HOC, Options, DefaultProps } from './types';
|
||||
|
||||
import React from 'react';
|
||||
import assert from '@polkadot/util/assert';
|
||||
|
||||
import isEqual from '../util/isEqual';
|
||||
import withObservable from './observable';
|
||||
import withApi from './api';
|
||||
|
||||
type State = ParamProps & {
|
||||
Component: React.ComponentType<any>,
|
||||
fn: RxApiInterface$Method
|
||||
};
|
||||
|
||||
// FIXME properly type input and agumented props, e.g. P extends object (in) & P & AugProps (out)
|
||||
|
||||
export default function withApiCall<T> ({ name, section }: ApiMethod, options?: Options<T>): HOC<T> {
|
||||
return (Component: React.ComponentType<any>, defaultProps: DefaultProps<T> = {}): React.ComponentType<any> => {
|
||||
class WithApiCall extends React.Component<any, State> {
|
||||
state: State;
|
||||
|
||||
constructor (props: any) {
|
||||
super(props);
|
||||
|
||||
assert(section && props.api[section], `Unable to find 'api.${section}'`);
|
||||
|
||||
const fn: RxApiInterface$Method = section
|
||||
? props.api[section][name]
|
||||
: ((props.api[name]) as RxApiInterface$Method);
|
||||
|
||||
assert(fn, `Unable to find 'api${section ? '.' : ''}${section || ''}.${name}'`);
|
||||
|
||||
this.state = { fn } as State;
|
||||
}
|
||||
|
||||
static getDerivedStateFromProps ({ params = [] }: any, prevState: State): State | null {
|
||||
if (isEqual(params, prevState.params)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const observable = prevState.fn.apply(null, params);
|
||||
|
||||
return {
|
||||
Component: withObservable(observable, options)(Component, defaultProps),
|
||||
params
|
||||
} as State;
|
||||
}
|
||||
|
||||
render () {
|
||||
const { Component } = this.state;
|
||||
|
||||
return (
|
||||
<Component {...this.props} />
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return withApi(WithApiCall);
|
||||
};
|
||||
}
|
||||
@ -5,13 +5,9 @@
|
||||
import { BaseProps } from '../types';
|
||||
import { ApiMethod, ComponentRenderer, DefaultProps, RenderFn, Options } from './types';
|
||||
|
||||
import Div from '../Div';
|
||||
import withApiCall from './apiCall';
|
||||
import withObservableDiv from './observableDiv';
|
||||
|
||||
export default function withApiDiv<T, Props extends BaseProps<T>> (call: ApiMethod, options?: Options<T>): ComponentRenderer<T> {
|
||||
return (render: RenderFn, defaultProps: DefaultProps<T> = {}): React.ComponentType<Props> =>
|
||||
withApiCall(call, options)(Div, {
|
||||
...defaultProps,
|
||||
render
|
||||
});
|
||||
withObservableDiv('rawCall', { ...options, params: [call] })(render, defaultProps);
|
||||
}
|
||||
|
||||
@ -1,120 +0,0 @@
|
||||
// Copyright 2017-2018 @polkadot/ui-react-rx authors & contributors
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
|
||||
// TODO: Lots of duplicated code between this and withObservable, surely there ois a better way of doing this?
|
||||
|
||||
import { RxProps, ObservableApiInterface } from '../types';
|
||||
import { HOC, StorageOptions, DefaultProps } from './types';
|
||||
|
||||
import React from 'react';
|
||||
import { map } from 'rxjs/operators/map';
|
||||
import isUndefined from '@polkadot/util/is/undefined';
|
||||
|
||||
import intervalSubscribe from '../util/intervalSubscribe';
|
||||
import isEqual from '../util/isEqual';
|
||||
import triggerChange from '../util/triggerChange';
|
||||
import echoTransform from './transform/echo';
|
||||
import withApi from './api';
|
||||
|
||||
type State<T> = RxProps<T> & {
|
||||
subscriptions: Array<any>; // FIXME subscriptions
|
||||
};
|
||||
|
||||
type ObservableNames = keyof ObservableApiInterface;
|
||||
|
||||
// FIXME proper types for attributes
|
||||
|
||||
export default function withApiObservable<T> (observable: ObservableNames, { onChange, params, paramProp = 'params', propName = 'value', transform = echoTransform }: StorageOptions<T> = {}): HOC<T> {
|
||||
return (Inner: React.ComponentType<any>, defaultProps: DefaultProps<T> = {}): React.ComponentType<any> => {
|
||||
class WithStorage extends React.Component<any, State<T>> {
|
||||
state: State<T>;
|
||||
|
||||
constructor (props: any) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
rxUpdated: false,
|
||||
rxUpdatedAt: 0,
|
||||
subscriptions: [],
|
||||
value: void 0
|
||||
};
|
||||
}
|
||||
|
||||
componentDidUpdate (prevProps: any) {
|
||||
if (!isEqual(this.props[paramProp], prevProps[paramProp])) {
|
||||
this.subscribe();
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount () {
|
||||
this.subscribe();
|
||||
}
|
||||
|
||||
private subscribe () {
|
||||
const { apiObservable } = this.props;
|
||||
const propValue = params || this.props[paramProp];
|
||||
|
||||
this.unsubscribe();
|
||||
|
||||
if (isUndefined(propValue)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.setState({
|
||||
subscriptions: [
|
||||
apiObservable[observable](propValue)
|
||||
.pipe(map(transform))
|
||||
.subscribe((value: any) =>
|
||||
this.triggerUpdate(this.props, value)
|
||||
),
|
||||
intervalSubscribe(this)
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
private unsubscribe () {
|
||||
this.state.subscriptions.forEach((subscription) =>
|
||||
subscription.unsubscribe()
|
||||
);
|
||||
}
|
||||
|
||||
componentWillUnmount () {
|
||||
this.unsubscribe();
|
||||
}
|
||||
|
||||
triggerUpdate = (props: any, value?: T): void => {
|
||||
if (isEqual(value, this.state.value)) {
|
||||
return;
|
||||
}
|
||||
|
||||
triggerChange(value, onChange, props.onChange || defaultProps.onChange);
|
||||
|
||||
this.setState({
|
||||
rxUpdated: true,
|
||||
rxUpdatedAt: Date.now(),
|
||||
value
|
||||
});
|
||||
}
|
||||
|
||||
render () {
|
||||
const { rxUpdated, rxUpdatedAt, value } = this.state;
|
||||
const _props = {
|
||||
...defaultProps,
|
||||
...this.props,
|
||||
rxUpdated,
|
||||
rxUpdatedAt,
|
||||
[propName]: value
|
||||
};
|
||||
|
||||
delete _props.onChange;
|
||||
|
||||
return (
|
||||
<Inner {..._props} />
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return withApi(WithStorage);
|
||||
};
|
||||
}
|
||||
@ -1,17 +0,0 @@
|
||||
// Copyright 2017-2018 @polkadot/ui-react-rx authors & contributors
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
|
||||
import withApi from './api';
|
||||
import withApiCall from './apiCall';
|
||||
import withObservable from './observable';
|
||||
import withObservableParams from './observableParams';
|
||||
import withStorage from './storage';
|
||||
|
||||
export {
|
||||
withApi,
|
||||
withApiCall,
|
||||
withObservable,
|
||||
withObservableParams,
|
||||
withStorage
|
||||
};
|
||||
@ -2,41 +2,37 @@
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
|
||||
// TODO: This is now way more messy and way longer than it should be. Maintainability is lacking - apply some effort and split it into managable parts
|
||||
// TODO: Lots of duplicated code between this and withObservable, surely there ois a better way of doing this?
|
||||
|
||||
import { RxProps } from '../types';
|
||||
import { HOC, Options, DefaultProps } from './types';
|
||||
import { RxProps, ObservableApiNames } from '../types';
|
||||
import { HOC, Options, DefaultProps, RenderFn } from './types';
|
||||
|
||||
import React from 'react';
|
||||
import { map } from 'rxjs/operators/map';
|
||||
|
||||
import assert from '@polkadot/util/assert';
|
||||
import isUndefined from '@polkadot/util/is/undefined';
|
||||
|
||||
import intervalSubscribe from '../util/intervalSubscribe';
|
||||
import isEqual from '../util/isEqual';
|
||||
import triggerChange from '../util/triggerChange';
|
||||
import echoTransform from './transform/echo';
|
||||
import withApi from './api';
|
||||
|
||||
type State<T> = RxProps<T> & {
|
||||
subscriptions: Array<any>;
|
||||
subscriptions: Array<any>; // FIXME subscriptions
|
||||
};
|
||||
|
||||
// FIXME Observables here are NOT any, horribly defined (leave until rx-lite decicion is made)
|
||||
// FIXME proper props augmentation
|
||||
// FIXME proper types for attributes
|
||||
|
||||
export default function withObservable<T> (observable: any, options: Options<T> = {}): HOC<T> {
|
||||
const { onChange, propName = 'value', transform = echoTransform } = options;
|
||||
export default function withObservable<T> (observable: ObservableApiNames, { onChange, params = [], paramProp = 'params', propName = observable, transform = echoTransform }: Options<T> = {}): HOC<T> {
|
||||
console.log('observable', observable, paramProp);
|
||||
|
||||
return (Inner: React.ComponentType<any>, defaultProps: DefaultProps<T> = {}): React.ComponentType<any> => {
|
||||
return class WithObservable extends React.Component<any, State<T>> {
|
||||
return (Inner: React.ComponentType<any>, defaultProps: DefaultProps<T> = {}, render?: RenderFn): React.ComponentType<any> => {
|
||||
class WithObservable extends React.Component<any, State<T>> {
|
||||
state: State<T>;
|
||||
|
||||
constructor (props: any) {
|
||||
super(props);
|
||||
|
||||
assert(observable, `Component should have Observable to wrap`);
|
||||
assert(Inner, `Expected 'with*' to wrap a React Component`);
|
||||
|
||||
this.state = {
|
||||
rxUpdated: false,
|
||||
rxUpdatedAt: 0,
|
||||
@ -45,25 +41,54 @@ export default function withObservable<T> (observable: any, options: Options<T>
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount () {
|
||||
const subscriptions = [
|
||||
observable
|
||||
.pipe(map(transform))
|
||||
.subscribe((value: any) => {
|
||||
this.triggerUpdate(this.props, value);
|
||||
}),
|
||||
intervalSubscribe(this)
|
||||
];
|
||||
private getParams (props: any): Array<any> {
|
||||
const paramValue = props[paramProp];
|
||||
|
||||
return isUndefined(paramValue)
|
||||
? params
|
||||
: params.concat(
|
||||
Array.isArray(paramValue)
|
||||
? paramValue
|
||||
: [paramValue]
|
||||
);
|
||||
}
|
||||
|
||||
componentDidUpdate (prevProps: any) {
|
||||
const newParams = this.getParams(this.props);
|
||||
|
||||
if (!isEqual(newParams, this.getParams(prevProps))) {
|
||||
this.subscribe(newParams);
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount () {
|
||||
this.subscribe(this.getParams(this.props));
|
||||
}
|
||||
|
||||
private subscribe (newParams: Array<any>) {
|
||||
const { apiObservable } = this.props;
|
||||
|
||||
this.unsubscribe();
|
||||
this.setState({
|
||||
subscriptions
|
||||
subscriptions: [
|
||||
apiObservable[observable](...newParams)
|
||||
.pipe(map(transform))
|
||||
.subscribe((value: any) =>
|
||||
this.triggerUpdate(this.props, value)
|
||||
),
|
||||
intervalSubscribe(this)
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
private unsubscribe () {
|
||||
this.state.subscriptions.forEach((subscription) =>
|
||||
subscription.unsubscribe()
|
||||
);
|
||||
}
|
||||
|
||||
componentWillUnmount () {
|
||||
this.state.subscriptions.forEach((subscription) => {
|
||||
subscription.unsubscribe();
|
||||
});
|
||||
this.unsubscribe();
|
||||
}
|
||||
|
||||
triggerUpdate = (props: any, value?: T): void => {
|
||||
@ -81,10 +106,10 @@ export default function withObservable<T> (observable: any, options: Options<T>
|
||||
}
|
||||
|
||||
render () {
|
||||
const { children } = this.props;
|
||||
const { rxUpdated, rxUpdatedAt, value } = this.state;
|
||||
const _props = {
|
||||
...defaultProps,
|
||||
// @ts-ignore umpf
|
||||
...this.props,
|
||||
rxUpdated,
|
||||
rxUpdatedAt,
|
||||
@ -94,9 +119,13 @@ export default function withObservable<T> (observable: any, options: Options<T>
|
||||
delete _props.onChange;
|
||||
|
||||
return (
|
||||
<Inner {..._props} />
|
||||
<Inner {..._props}>
|
||||
{render && render(value)}{children}
|
||||
</Inner>
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return withApi(WithObservable);
|
||||
};
|
||||
}
|
||||
|
||||
14
packages/ui-react-rx/src/with/observableDiv.ts
Normal file
14
packages/ui-react-rx/src/with/observableDiv.ts
Normal file
@ -0,0 +1,14 @@
|
||||
// Copyright 2017-2018 @polkadot/ui-react-rx authors & contributors
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
|
||||
import { BaseProps, ObservableApiNames } from '../types';
|
||||
import { ComponentRenderer, DefaultProps, RenderFn, Options } from './types';
|
||||
|
||||
import Div from '../Div';
|
||||
import withObservable from './observable';
|
||||
|
||||
export default function withObservableDiv<T, Props extends BaseProps<T>> (call: ObservableApiNames, options?: Options<T>): ComponentRenderer<T> {
|
||||
return (render: RenderFn, defaultProps: DefaultProps<T> = {}): React.ComponentType<Props> =>
|
||||
withObservable(call, options)(Div, defaultProps, render);
|
||||
}
|
||||
@ -1,16 +0,0 @@
|
||||
// Copyright 2017-2018 @polkadot/ui-react-rx authors & contributors
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
|
||||
import { HOC, Options } from './types';
|
||||
|
||||
import withObservable from './observable';
|
||||
|
||||
// FIXME Observable/Subject types
|
||||
|
||||
export default function withObservableParams<T> (observable: any, options: Options<T> = {}): HOC<T> {
|
||||
return withObservable(observable, {
|
||||
propName: 'params',
|
||||
...options
|
||||
});
|
||||
}
|
||||
@ -1,148 +0,0 @@
|
||||
// Copyright 2017-2018 @polkadot/ui-react-rx authors & contributors
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
|
||||
// TODO: Lots of duplicated code between this and withObservable, surely there ois a better way of doing this?
|
||||
|
||||
import { SectionItem } from '@polkadot/params/types';
|
||||
import { Storages, Storage$Key$Value } from '@polkadot/storage/types';
|
||||
import { RxProps } from '../types';
|
||||
import { HOC, StorageOptions, DefaultProps, Transform } from './types';
|
||||
|
||||
import React from 'react';
|
||||
import { map } from 'rxjs/operators/map';
|
||||
|
||||
import storageKey from '@polkadot/storage/key';
|
||||
|
||||
import intervalSubscribe from '../util/intervalSubscribe';
|
||||
import isEqual from '../util/isEqual';
|
||||
import triggerChange from '../util/triggerChange';
|
||||
import echoTransform from './transform/echo';
|
||||
import createTransform from './transform/storage';
|
||||
import withApi from './api';
|
||||
|
||||
type State<T> = RxProps<T> & {
|
||||
subscriptions: Array<any>; // FIXME subscriptions
|
||||
};
|
||||
|
||||
// FIXME proper types for attributes
|
||||
|
||||
export default function withStorage<T> (key: SectionItem<Storages>, { onChange, params, paramProp = 'params', propName = 'value', transform }: StorageOptions<T> = {}): HOC<T> {
|
||||
const keyCreator = storageKey(key);
|
||||
const storageTransform = createTransform(key);
|
||||
const createKey = (propParams?: Array<Storage$Key$Value>): Uint8Array => {
|
||||
const values = [params, propParams].reduce((result, input = []) => {
|
||||
if (Array.isArray(input)) {
|
||||
return result.concat(input);
|
||||
}
|
||||
|
||||
result.push(input);
|
||||
return result;
|
||||
}, ([] as any[]));
|
||||
|
||||
return keyCreator.apply(null, values);
|
||||
};
|
||||
|
||||
return (Inner: React.ComponentType<any>, defaultProps: DefaultProps<T> = {}): React.ComponentType<any> => {
|
||||
class WithStorage extends React.Component<any, State<T>> {
|
||||
state: State<T>;
|
||||
|
||||
constructor (props: any) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
rxUpdated: false,
|
||||
rxUpdatedAt: 0,
|
||||
subscriptions: [],
|
||||
value: void 0
|
||||
};
|
||||
}
|
||||
|
||||
async componentDidUpdate (prevProps: any) {
|
||||
if (!isEqual(this.props[paramProp], prevProps[paramProp])) {
|
||||
await this.triggerUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount () {
|
||||
const subscriptions = [
|
||||
this.props.api.chain
|
||||
.newHead()
|
||||
.subscribe(async (value: any) => {
|
||||
await this.triggerUpdate();
|
||||
}),
|
||||
intervalSubscribe(this)
|
||||
];
|
||||
|
||||
this.setState({
|
||||
subscriptions
|
||||
});
|
||||
}
|
||||
|
||||
componentWillUnmount () {
|
||||
this.state.subscriptions.forEach((subscription) => {
|
||||
subscription.unsubscribe();
|
||||
});
|
||||
}
|
||||
|
||||
triggerUpdate = async (): Promise<void> => {
|
||||
const mapping = (value: Uint8Array, index: number): any => {
|
||||
const outer = this.props.transform || echoTransform;
|
||||
const inner = transform || echoTransform;
|
||||
|
||||
return outer(
|
||||
inner(
|
||||
storageTransform(value, index),
|
||||
index
|
||||
),
|
||||
index
|
||||
);
|
||||
};
|
||||
|
||||
const key = createKey(this.props[paramProp]);
|
||||
let value;
|
||||
|
||||
try {
|
||||
value = await this.props.api.state
|
||||
.getStorage(key)
|
||||
.pipe(map(mapping))
|
||||
.toPromise();
|
||||
} catch (error) {
|
||||
// swallow?
|
||||
// console.error(error);
|
||||
}
|
||||
|
||||
if (isEqual(value, this.state.value)) {
|
||||
return;
|
||||
}
|
||||
|
||||
triggerChange(value, onChange, this.props.onChange || defaultProps.onChange);
|
||||
|
||||
this.setState({
|
||||
rxUpdated: true,
|
||||
rxUpdatedAt: Date.now(),
|
||||
value
|
||||
});
|
||||
}
|
||||
|
||||
render () {
|
||||
const { rxUpdated, rxUpdatedAt, value } = this.state;
|
||||
const _props = {
|
||||
...defaultProps,
|
||||
...this.props,
|
||||
rxUpdated,
|
||||
rxUpdatedAt,
|
||||
[propName]: value
|
||||
};
|
||||
|
||||
delete _props.onChange;
|
||||
|
||||
return (
|
||||
<Inner {..._props} />
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return withApi(WithStorage);
|
||||
};
|
||||
}
|
||||
@ -1,19 +0,0 @@
|
||||
// Copyright 2017-2018 @polkadot/ui-react-rx authors & contributors
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
|
||||
import { SectionItem } from '@polkadot/params/types';
|
||||
import { Storages } from '@polkadot/storage/types';
|
||||
import { BaseProps } from '../types';
|
||||
import { ComponentRenderer, DefaultProps, RenderFn, StorageOptions } from './types';
|
||||
|
||||
import Div from '../Div';
|
||||
import withStorage from './storage';
|
||||
|
||||
export default function withStorageDiv<T, Props extends BaseProps<T>> (key: SectionItem<Storages>, options?: StorageOptions<T>): ComponentRenderer<T> {
|
||||
return (render: RenderFn, defaultProps: DefaultProps<T> = {}): React.ComponentType<Props> =>
|
||||
withStorage(key, options)(Div, {
|
||||
...defaultProps,
|
||||
render
|
||||
});
|
||||
}
|
||||
13
packages/ui-react-rx/src/with/types.d.ts
vendored
13
packages/ui-react-rx/src/with/types.d.ts
vendored
@ -17,24 +17,21 @@ export type DefaultProps<T> = {
|
||||
|
||||
export type Options<T> = {
|
||||
onChange?: OnChangeCb<T>,
|
||||
params?: Array<any>,
|
||||
paramProp?: string,
|
||||
propName?: string,
|
||||
transform?: Transform
|
||||
};
|
||||
|
||||
export type RenderFn = (value?: any) => React.ReactNode;
|
||||
|
||||
export type StorageTransform = (input: any, index: number) => Param$Values | null;
|
||||
|
||||
export type StorageOptions<T> = Options<T> & {
|
||||
params?: Array<Storage$Key$Value | undefined>;
|
||||
paramProp?: string,
|
||||
};
|
||||
|
||||
export type HOC<T> = (Component: React.ComponentType<any>, defaultProps?: DefaultProps<T>) => React.ComponentType<any>;
|
||||
export type HOC<T> = (Component: React.ComponentType<any>, defaultProps?: DefaultProps<T>, render?: RenderFn) => React.ComponentType<any>;
|
||||
|
||||
export type ApiMethod = {
|
||||
name: string,
|
||||
section?: string
|
||||
};
|
||||
|
||||
export type RenderFn = (value?: any) => any;
|
||||
|
||||
export type ComponentRenderer<T> = (render: RenderFn, defaultProps?: DefaultProps<T>) => React.ComponentType<any>;
|
||||
|
||||
@ -20,7 +20,7 @@
|
||||
"polkadot-identicon": "^1.0.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@polkadot/util-keyring": "^0.27.12",
|
||||
"@polkadot/util-keyring": "^0.28.1",
|
||||
"babel-loader": "^8.0.0-beta.2",
|
||||
"css-loader": "0.28.9",
|
||||
"empty": "^0.10.1",
|
||||
|
||||
80
packages/ui-react/src/Elapsed.tsx
Normal file
80
packages/ui-react/src/Elapsed.tsx
Normal file
@ -0,0 +1,80 @@
|
||||
// Copyright 2017-2018 @polkadot/ui-react authors & contributors
|
||||
// This software may be modified and distributed under the terms
|
||||
// of the ISC license. See the LICENSE file for details.
|
||||
|
||||
import { BaseProps } from './types';
|
||||
|
||||
import React from 'react';
|
||||
|
||||
type Props = BaseProps & {
|
||||
value?: Date
|
||||
};
|
||||
|
||||
type State = {
|
||||
now?: Date
|
||||
};
|
||||
|
||||
const TICK_TIMEOUT = 100;
|
||||
const tickers = new Map<Elapsed, (now: Date) => void>();
|
||||
|
||||
function tick () {
|
||||
const now = new Date();
|
||||
|
||||
for (const ticker of tickers.values()) {
|
||||
ticker(now);
|
||||
}
|
||||
|
||||
setTimeout(tick, TICK_TIMEOUT);
|
||||
}
|
||||
|
||||
tick();
|
||||
|
||||
export default class Elapsed extends React.PureComponent<Props, State> {
|
||||
public state: State = {};
|
||||
|
||||
public componentWillMount () {
|
||||
tickers.set(this, (now: Date): void => {
|
||||
this.setState({
|
||||
now
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public componentWillUnmount () {
|
||||
tickers.delete(this);
|
||||
}
|
||||
|
||||
public render () {
|
||||
const { className, style, value } = this.props;
|
||||
const { now } = this.state;
|
||||
|
||||
return (
|
||||
<div
|
||||
className={['ui--Elapsed', className].join(' ')}
|
||||
style={style}
|
||||
>
|
||||
{this.getDisplayValue(now, value)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
private getDisplayValue (now?: Date, value?: Date): string {
|
||||
const tsNow = (now && now.getTime()) || 0;
|
||||
const tsValue = (value && value.getTime()) || 0;
|
||||
let display = '-';
|
||||
|
||||
if (tsNow && tsValue) {
|
||||
const elapsed = Math.max(tsNow - tsValue, 0) / 1000;
|
||||
|
||||
if (elapsed < 15) {
|
||||
display = `${elapsed.toFixed(1)}s`;
|
||||
} else if (elapsed < 60) {
|
||||
display = `${elapsed | 0}s`;
|
||||
} else {
|
||||
display = `${ elapsed / 60 | 0}m`;
|
||||
}
|
||||
}
|
||||
|
||||
return display;
|
||||
}
|
||||
}
|
||||
@ -16,7 +16,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.0.0-beta.51",
|
||||
"@polkadot/extrinsics": "^0.27.12",
|
||||
"@polkadot/extrinsics": "^0.28.9",
|
||||
"@polkadot/ui-app": "^0.19.29",
|
||||
"@polkadot/ui-keyring": "^0.19.29"
|
||||
},
|
||||
|
||||
158
yarn.lock
158
yarn.lock
@ -765,50 +765,50 @@
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-1.1.0.tgz#50c1e2260ac0ed9439a181de3725a0168d59c48a"
|
||||
|
||||
"@polkadot/api-format@^0.15.9":
|
||||
version "0.15.9"
|
||||
resolved "https://registry.yarnpkg.com/@polkadot/api-format/-/api-format-0.15.9.tgz#4433ea678980c4912aa1b96369667044a7f8e882"
|
||||
"@polkadot/api-format@^0.28.9":
|
||||
version "0.28.9"
|
||||
resolved "https://registry.yarnpkg.com/@polkadot/api-format/-/api-format-0.28.9.tgz#1360e37534d21e871b1bee0d58a0a2ebb99ad753"
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.0.0-beta.51"
|
||||
"@polkadot/jsonrpc" "^0.27.12"
|
||||
"@polkadot/primitives" "^0.27.12"
|
||||
"@polkadot/util" "^0.27.12"
|
||||
"@polkadot/util-keyring" "^0.27.12"
|
||||
"@polkadot/jsonrpc" "^0.28.9"
|
||||
"@polkadot/primitives" "^0.28.9"
|
||||
"@polkadot/util" "^0.28.1"
|
||||
"@polkadot/util-keyring" "^0.28.1"
|
||||
|
||||
"@polkadot/api-provider@^0.15.9":
|
||||
version "0.15.9"
|
||||
resolved "https://registry.yarnpkg.com/@polkadot/api-provider/-/api-provider-0.15.9.tgz#82d2fd835456d247d4dca2d5962493cfe292d73f"
|
||||
"@polkadot/api-provider@^0.28.9":
|
||||
version "0.28.9"
|
||||
resolved "https://registry.yarnpkg.com/@polkadot/api-provider/-/api-provider-0.28.9.tgz#642cc45d9248a61c7f80969290e19461c8f67047"
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.0.0-beta.51"
|
||||
"@polkadot/storage" "^0.27.12"
|
||||
"@polkadot/util" "^0.27.12"
|
||||
"@polkadot/util-crypto" "^0.27.12"
|
||||
"@polkadot/util-keyring" "^0.27.12"
|
||||
"@polkadot/storage" "^0.28.9"
|
||||
"@polkadot/util" "^0.28.1"
|
||||
"@polkadot/util-crypto" "^0.28.1"
|
||||
"@polkadot/util-keyring" "^0.28.1"
|
||||
"@types/nock" "^9.1.3"
|
||||
eventemitter3 "^2.0.3"
|
||||
isomorphic-fetch "^2.2.1"
|
||||
websocket "^1.0.25"
|
||||
|
||||
"@polkadot/api-rx@^0.15.9":
|
||||
version "0.15.9"
|
||||
resolved "https://registry.yarnpkg.com/@polkadot/api-rx/-/api-rx-0.15.9.tgz#9f3997a837d3635f56823a1b5d3acc68b3c20bfa"
|
||||
"@polkadot/api-rx@^0.28.9":
|
||||
version "0.28.9"
|
||||
resolved "https://registry.yarnpkg.com/@polkadot/api-rx/-/api-rx-0.28.9.tgz#5ad6352ce42df7888c37baef40c2acf24e5a7d76"
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.0.0-beta.51"
|
||||
"@polkadot/api" "^0.15.9"
|
||||
"@polkadot/api-provider" "^0.15.9"
|
||||
"@polkadot/api" "^0.28.9"
|
||||
"@polkadot/api-provider" "^0.28.9"
|
||||
"@types/rx" "^4.1.1"
|
||||
rxjs "^6.2.2"
|
||||
|
||||
"@polkadot/api@^0.15.9":
|
||||
version "0.15.9"
|
||||
resolved "https://registry.yarnpkg.com/@polkadot/api/-/api-0.15.9.tgz#fcf041f612a44c956080c6b3b0dd3f26d9133a6e"
|
||||
"@polkadot/api@^0.28.9":
|
||||
version "0.28.9"
|
||||
resolved "https://registry.yarnpkg.com/@polkadot/api/-/api-0.28.9.tgz#86c2f2db81256ba4d101d37ca18c3fcaa076e0c7"
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.0.0-beta.51"
|
||||
"@polkadot/api-format" "^0.15.9"
|
||||
"@polkadot/api-provider" "^0.15.9"
|
||||
"@polkadot/jsonrpc" "^0.27.12"
|
||||
"@polkadot/params" "^0.27.12"
|
||||
"@polkadot/util" "^0.27.12"
|
||||
"@polkadot/api-format" "^0.28.9"
|
||||
"@polkadot/api-provider" "^0.28.9"
|
||||
"@polkadot/jsonrpc" "^0.28.9"
|
||||
"@polkadot/params" "^0.28.9"
|
||||
"@polkadot/util" "^0.28.1"
|
||||
|
||||
"@polkadot/dev-react@^0.20.12":
|
||||
version "0.20.12"
|
||||
@ -877,94 +877,96 @@
|
||||
typedoc "^0.11.1"
|
||||
typescript "^2.9.2"
|
||||
|
||||
"@polkadot/extrinsics@^0.27.12":
|
||||
version "0.27.12"
|
||||
resolved "https://registry.yarnpkg.com/@polkadot/extrinsics/-/extrinsics-0.27.12.tgz#0fab6c4015f6617da6c9305c22e24e4456b0399e"
|
||||
"@polkadot/extrinsics@^0.28.9":
|
||||
version "0.28.9"
|
||||
resolved "https://registry.yarnpkg.com/@polkadot/extrinsics/-/extrinsics-0.28.9.tgz#b7804179153c29e793a9d549b566f23a13f98558"
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.0.0-beta.51"
|
||||
"@polkadot/params" "^0.27.12"
|
||||
"@polkadot/primitives" "^0.27.12"
|
||||
"@polkadot/util" "^0.27.12"
|
||||
"@polkadot/params" "^0.28.9"
|
||||
"@polkadot/primitives" "^0.28.9"
|
||||
"@polkadot/util" "^0.28.1"
|
||||
|
||||
"@polkadot/jsonrpc@^0.27.12":
|
||||
version "0.27.12"
|
||||
resolved "https://registry.yarnpkg.com/@polkadot/jsonrpc/-/jsonrpc-0.27.12.tgz#717f15fe9386cd3660755b936773a29d9c82ceaf"
|
||||
"@polkadot/jsonrpc@^0.28.9":
|
||||
version "0.28.9"
|
||||
resolved "https://registry.yarnpkg.com/@polkadot/jsonrpc/-/jsonrpc-0.28.9.tgz#564116660f703084048139c3bda7f01c979e55b4"
|
||||
dependencies:
|
||||
"@polkadot/params" "^0.27.12"
|
||||
"@polkadot/params" "^0.28.9"
|
||||
babel-runtime "^6.26.0"
|
||||
|
||||
"@polkadot/params@^0.27.12":
|
||||
version "0.27.12"
|
||||
resolved "https://registry.yarnpkg.com/@polkadot/params/-/params-0.27.12.tgz#446e7a9c23ebaf13da9ccd4d78bcaac287426ff0"
|
||||
"@polkadot/params@^0.28.9":
|
||||
version "0.28.9"
|
||||
resolved "https://registry.yarnpkg.com/@polkadot/params/-/params-0.28.9.tgz#220888b70d147c71f195d06b1e38525c520e04aa"
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.0.0-beta.51"
|
||||
"@polkadot/extrinsics" "^0.27.12"
|
||||
"@polkadot/primitives" "^0.27.12"
|
||||
"@polkadot/util" "^0.27.12"
|
||||
"@polkadot/extrinsics" "^0.28.9"
|
||||
"@polkadot/params" "^0.28.9"
|
||||
"@polkadot/primitives" "^0.28.9"
|
||||
"@polkadot/util" "^0.28.1"
|
||||
"@polkadot/util-keyring" "^0.28.1"
|
||||
|
||||
"@polkadot/primitives@^0.27.12":
|
||||
version "0.27.12"
|
||||
resolved "https://registry.yarnpkg.com/@polkadot/primitives/-/primitives-0.27.12.tgz#e4dba94c1b05567131b80ebb42a95923dc5e62fd"
|
||||
"@polkadot/primitives@^0.28.9":
|
||||
version "0.28.9"
|
||||
resolved "https://registry.yarnpkg.com/@polkadot/primitives/-/primitives-0.28.9.tgz#e451adc1faf78b397f7020f0f036d8542e48aab0"
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.0.0-beta.51"
|
||||
"@polkadot/trie-hash" "^0.27.12"
|
||||
"@polkadot/util" "^0.27.12"
|
||||
"@polkadot/trie-hash" "^0.28.1"
|
||||
"@polkadot/util" "^0.28.1"
|
||||
|
||||
"@polkadot/storage@^0.27.12":
|
||||
version "0.27.12"
|
||||
resolved "https://registry.yarnpkg.com/@polkadot/storage/-/storage-0.27.12.tgz#2994dc47bf444d93615604e6ab924aeef4723873"
|
||||
"@polkadot/storage@^0.28.9":
|
||||
version "0.28.9"
|
||||
resolved "https://registry.yarnpkg.com/@polkadot/storage/-/storage-0.28.9.tgz#2ac372d64ba1f92310da88476a1be16f92a30443"
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.0.0-beta.51"
|
||||
"@polkadot/params" "^0.27.12"
|
||||
"@polkadot/primitives" "^0.27.12"
|
||||
"@polkadot/util" "^0.27.12"
|
||||
"@polkadot/util-crypto" "^0.27.12"
|
||||
"@polkadot/util-keyring" "^0.27.12"
|
||||
"@polkadot/params" "^0.28.9"
|
||||
"@polkadot/primitives" "^0.28.9"
|
||||
"@polkadot/util" "^0.28.1"
|
||||
"@polkadot/util-crypto" "^0.28.1"
|
||||
"@polkadot/util-keyring" "^0.28.1"
|
||||
|
||||
"@polkadot/trie-hash@^0.27.12":
|
||||
version "0.27.12"
|
||||
resolved "https://registry.yarnpkg.com/@polkadot/trie-hash/-/trie-hash-0.27.12.tgz#01f73e2dd4967237a581d2a118b14b754e1b8d03"
|
||||
"@polkadot/trie-hash@^0.28.1":
|
||||
version "0.28.1"
|
||||
resolved "https://registry.yarnpkg.com/@polkadot/trie-hash/-/trie-hash-0.28.1.tgz#8e220bd2280813818d6d90e4d7d826edfb0d407d"
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.0.0-beta.51"
|
||||
"@polkadot/util" "^0.27.12"
|
||||
"@polkadot/util-crypto" "^0.27.12"
|
||||
"@polkadot/util-rlp" "^0.27.12"
|
||||
"@polkadot/util" "^0.28.1"
|
||||
"@polkadot/util-crypto" "^0.28.1"
|
||||
"@polkadot/util-rlp" "^0.28.1"
|
||||
|
||||
"@polkadot/ts@^0.1.10":
|
||||
version "0.1.10"
|
||||
resolved "https://registry.yarnpkg.com/@polkadot/ts/-/ts-0.1.10.tgz#784d460cf4b2e9b38bb1c77f9366dd01425f220d"
|
||||
|
||||
"@polkadot/util-crypto@^0.27.12":
|
||||
version "0.27.12"
|
||||
resolved "https://registry.yarnpkg.com/@polkadot/util-crypto/-/util-crypto-0.27.12.tgz#a58fc4668d0fef05cae3760bd47ac9bdf042f09c"
|
||||
"@polkadot/util-crypto@^0.28.1":
|
||||
version "0.28.1"
|
||||
resolved "https://registry.yarnpkg.com/@polkadot/util-crypto/-/util-crypto-0.28.1.tgz#07bef16d5b68889f96ff9b9c034b92e22bd6f63d"
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.0.0-beta.51"
|
||||
"@polkadot/util" "^0.27.12"
|
||||
"@polkadot/util" "^0.28.1"
|
||||
blakejs "^1.1.0"
|
||||
js-sha3 "^0.7.0"
|
||||
tweetnacl "^1.0.0"
|
||||
xxhashjs "^0.2.2"
|
||||
|
||||
"@polkadot/util-keyring@^0.27.12":
|
||||
version "0.27.12"
|
||||
resolved "https://registry.yarnpkg.com/@polkadot/util-keyring/-/util-keyring-0.27.12.tgz#849fcb46987ed733b94c014b65ff4c42772fddd2"
|
||||
"@polkadot/util-keyring@^0.28.1":
|
||||
version "0.28.1"
|
||||
resolved "https://registry.yarnpkg.com/@polkadot/util-keyring/-/util-keyring-0.28.1.tgz#68fac78bc7154226d7e6d06acfcfaebc6232814c"
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.0.0-beta.51"
|
||||
"@polkadot/util" "^0.27.12"
|
||||
"@polkadot/util-crypto" "^0.27.12"
|
||||
"@polkadot/util" "^0.28.1"
|
||||
"@polkadot/util-crypto" "^0.28.1"
|
||||
"@types/bs58" "^3.0.30"
|
||||
bs58 "^4.0.1"
|
||||
|
||||
"@polkadot/util-rlp@^0.27.12":
|
||||
version "0.27.12"
|
||||
resolved "https://registry.yarnpkg.com/@polkadot/util-rlp/-/util-rlp-0.27.12.tgz#6c2d33051a4d54b714872a88bb5ef92754d6b9cd"
|
||||
"@polkadot/util-rlp@^0.28.1":
|
||||
version "0.28.1"
|
||||
resolved "https://registry.yarnpkg.com/@polkadot/util-rlp/-/util-rlp-0.28.1.tgz#101cecbb30223fc3b66bd3448371eb5404d46ac6"
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.0.0-beta.51"
|
||||
"@polkadot/util" "^0.27.12"
|
||||
"@polkadot/util" "^0.28.1"
|
||||
|
||||
"@polkadot/util@^0.27.12":
|
||||
version "0.27.12"
|
||||
resolved "https://registry.yarnpkg.com/@polkadot/util/-/util-0.27.12.tgz#fc1155e8099a5fea93d978307531ca48094e8565"
|
||||
"@polkadot/util@^0.28.1":
|
||||
version "0.28.1"
|
||||
resolved "https://registry.yarnpkg.com/@polkadot/util/-/util-0.28.1.tgz#7c566cc46741c422d93f535fbd27db4e6eff5f80"
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.0.0-beta.51"
|
||||
"@types/bn.js" "^4.11.1"
|
||||
|
||||
Loading…
Reference in New Issue
Block a user