Use empty props from CardGrid (#1304)

* empty for account, addressbook

* staking empty screen

* use multi only when values changed

* lint

* withCall paramPick transform

* use paramPick and log things

* swap logging

* Display getMethod errors in withCall

* Remove nested try

* Use updateDb to trigger an update

* fix bonding

* Revert ifs

* Update call.tsx

* lint

* stray logs
This commit is contained in:
Thibaut Sardan 2019-06-29 10:02:58 +02:00 committed by GitHub
parent b2bc58b444
commit b2f6d87de1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 145 additions and 60 deletions

View File

@ -38,8 +38,9 @@ class Overview extends React.PureComponent<Props, State> {
}
render () {
const { accounts, onStatusChange, t } = this.props;
const { accounts, t } = this.props;
const { isCreateOpen, isImportOpen } = this.state;
const emptyScreen = !isCreateOpen && !isImportOpen && (!accounts || Object.keys(accounts).length === 0);
return (
<CardGrid
@ -58,19 +59,11 @@ class Overview extends React.PureComponent<Props, State> {
/>
</Button.Group>
}
isEmpty={emptyScreen}
emptyText={t('No account yet?')}
>
{isCreateOpen && (
<CreateModal
onClose={this.toggleCreate}
onStatusChange={onStatusChange}
/>
)}
{isImportOpen && (
<ImportModal
onClose={this.toggleImport}
onStatusChange={onStatusChange}
/>
)}
{this.renderCreate()}
{this.renderImport()}
{accounts && Object.keys(accounts).map((address) => (
<Account
address={address}
@ -81,6 +74,38 @@ class Overview extends React.PureComponent<Props, State> {
);
}
private renderCreate () {
const { isCreateOpen } = this.state;
const { onStatusChange } = this.props;
if (!isCreateOpen) {
return null;
}
return (
<CreateModal
onClose={this.toggleCreate}
onStatusChange={onStatusChange}
/>
);
}
private renderImport () {
const { isImportOpen } = this.state;
const { onStatusChange } = this.props;
if (!isImportOpen) {
return null;
}
return (
<ImportModal
onClose={this.toggleImport}
onStatusChange={onStatusChange}
/>
);
}
private toggleCreate = (): void => {
this.setState(({ isCreateOpen }) => ({
isCreateOpen: !isCreateOpen

View File

@ -31,6 +31,7 @@ class Overview extends React.PureComponent<Props, State> {
render () {
const { addresses, onStatusChange, t } = this.props;
const { isCreateOpen } = this.state;
const emptyScreen = !isCreateOpen && (!addresses || Object.keys(addresses).length === 0);
return (
<CardGrid
@ -43,6 +44,8 @@ class Overview extends React.PureComponent<Props, State> {
/>
</Button.Group>
}
isEmpty={emptyScreen}
emptyText={t('No contact found.')}
>
{isCreateOpen && (
<CreateModal

View File

@ -97,8 +97,8 @@ const BOB = '5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty';
console.log('Tracking balances for:', [ALICE, BOB])
// Subscribe and listen to several balance changes
api.query.balances.freeBalance.multi([ALICE, BOB], (balance) => {
console.log('Change detected, new balances: ', balance)
api.query.balances.freeBalance.multi([ALICE, BOB], (balances) => {
console.log('Change detected, new balances: ', balances)
});`
};

View File

@ -80,19 +80,20 @@ class Account extends React.PureComponent<Props, State> {
stashId: null
};
static getDerivedStateFromProps ({ staking_info }: Props): State | null {
static getDerivedStateFromProps ({ accountId, staking_info }: Props): State | null {
if (!staking_info) {
return null;
}
const { accountId, controllerId, nextSessionId, nominators, rewardDestination, stakers, stakingLedger, stashId, validatorPrefs } = staking_info;
const { controllerId, nextSessionId, nominators, rewardDestination, stakers, stakingLedger, stashId, validatorPrefs } = staking_info;
const isStashNominating = nominators && nominators.length !== 0;
const isStashValidating = !!validatorPrefs && !validatorPrefs.isEmpty && !isStashNominating;
return {
controllerId: toIdString(controllerId),
destination: rewardDestination && rewardDestination.toNumber(),
isActiveStash: accountId.eq(stashId),
isActiveStash: accountId === toIdString(stashId),
isStashNominating,
isStashValidating,
nominees: nominators && nominators.map(toIdString),

View File

@ -2,22 +2,25 @@
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
import { I18nProps } from '@polkadot/ui-app/types';
import { ApiProps } from '@polkadot/ui-api/types';
import { ComponentProps } from '../types';
import { I18nProps } from '@polkadot/ui-app/types';
import { KeyringSectionOption } from '@polkadot/ui-keyring/options/types';
import { withCalls, withMulti } from '@polkadot/ui-api/with';
import React from 'react';
import styled from 'styled-components';
import { Button, CardGrid, Icon } from '@polkadot/ui-app';
import createOption from '@polkadot/ui-keyring/options/item';
import { getAddressName } from '@polkadot/ui-app/util';
import keyring from '@polkadot/ui-keyring';
import styled from 'styled-components';
import Account from './Account';
import { KeyringSectionOption } from '@polkadot/ui-keyring/options/types';
import StartStaking from './NewStake';
import translate from '../translate';
type Props = I18nProps & ComponentProps;
type Props = I18nProps & ComponentProps & ApiProps & {
myControllers?: Array<string>
};
type State = {
isNewStakeOpen: boolean
@ -30,8 +33,10 @@ class Accounts extends React.PureComponent<Props,State> {
render () {
const { className, recentlyOffline, t } = this.props;
const accounts = keyring.getAccounts();
const { isNewStakeOpen } = this.state;
const stashOptions = this.getStashOptions();
const myStashes = this.getMyStashes();
const isEmpty = !isNewStakeOpen && (!myStashes || myStashes.length === 0);
return (
<CardGrid
@ -49,9 +54,12 @@ class Accounts extends React.PureComponent<Props,State> {
/>
}
className={className}
emptyText={t('No funds staked yet.')}
isEmpty={isEmpty}
>
{this.renderNewStake()}
{accounts.map(({ address }, index) => (
{myStashes && myStashes.map((address, index) => (
address &&
<Account
accountId={address}
key={index}
@ -63,6 +71,31 @@ class Accounts extends React.PureComponent<Props,State> {
);
}
private getMyStashes () {
const { myControllers, allAccounts } = this.props;
const result: Array<string> = [];
if (!myControllers) {
return null;
}
myControllers.forEach((value,index) => {
if (value.toString() !== '') {
allAccounts && result.push(Object.keys(allAccounts)[index]);
}
});
return result;
}
private getStashOptions (): Array<KeyringSectionOption> {
const { stashes } = this.props;
return stashes.map((stashId) =>
createOption(stashId, getAddressName(stashId, 'account'))
);
}
private renderNewStake () {
const { isNewStakeOpen } = this.state;
@ -82,18 +115,22 @@ class Accounts extends React.PureComponent<Props,State> {
isNewStakeOpen: !isNewStakeOpen
}));
}
private getStashOptions (): Array<KeyringSectionOption> {
const { stashes } = this.props;
return stashes.map((stashId) =>
createOption(stashId, getAddressName(stashId, 'account'))
);
}
}
export default translate(styled(Accounts)`
.ui--CardGrid-buttons {
text-align: right;
}
`);
export default withMulti(
styled(Accounts)`
.ui--CardGrid-buttons {
text-align: right;
}
`,
translate,
withCalls<Props>(
['query.staking.bonded', {
isMulti: true,
paramPick: ({ allAccounts }: Props) => {
return allAccounts && Object.keys(allAccounts);
},
propName: 'myControllers'
}]
)
);

View File

@ -18,8 +18,8 @@ import accountObservable from '@polkadot/ui-keyring/observable/accounts';
import './index.css';
import basicMd from './md/basic.md';
import Accounts from './Actions/Accounts';
import basicMd from './md/basic.md';
import Overview from './Overview';
import translate from './translate';
@ -121,10 +121,11 @@ class App extends React.PureComponent<Props, State> {
private renderComponent (Component: React.ComponentType<ComponentProps>) {
return (): React.ReactNode => {
const { controllers, recentlyOffline, stashes, validators } = this.state;
const { balances = {} } = this.props;
const { balances = {}, allAccounts } = this.props;
return (
<Component
allAccounts={allAccounts}
balances={balances}
controllers={controllers}
recentlyOffline={recentlyOffline}

View File

@ -2,9 +2,10 @@
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
import { AccountId, BlockNumber } from '@polkadot/types';
import BN from 'bn.js';
import { DerivedFees, DerivedBalances, DerivedBalancesMap } from '@polkadot/api-derive/types';
import { AccountId, BlockNumber } from '@polkadot/types';
import { SubjectInfo } from '@polkadot/ui-keyring/observable/types';
export type Nominators = {
// stash account and who is being nominated
@ -12,6 +13,7 @@ export type Nominators = {
};
export type ComponentProps = {
allAccounts?: SubjectInfo,
balances?: DerivedBalancesMap,
controllers: Array<string>,
recentlyOffline: RecentlyOfflineMap,

View File

@ -18,13 +18,15 @@ interface Method {
multi: (params: Array<any>, cb: (value?: any) => void) => Promise<any>;
}
type ApiMethodInfo = [Method, Array<any>, boolean];
type State = CallState;
const NOOP = () => {
// ignore
};
export default function withCall<P extends ApiProps> (endpoint: string, { at, atProp, callOnResult, isMulti = false, params = [], paramName, paramValid = false, propName, transform = echoTransform }: Options = {}): (Inner: React.ComponentType<ApiProps>) => React.ComponentType<any> {
export default function withCall<P extends ApiProps> (endpoint: string, { at, atProp, callOnResult, isMulti = false, params = [], paramName, paramPick, paramValid = false, propName, transform = echoTransform }: Options = {}): (Inner: React.ComponentType<ApiProps>) => React.ComponentType<any> {
return (Inner: React.ComponentType<ApiProps>): React.ComponentType<SubtractProps<P, ApiProps>> => {
class WithPromise extends React.Component<P, State> {
state: State = {
@ -46,8 +48,8 @@ export default function withCall<P extends ApiProps> (endpoint: string, { at, at
}
componentDidUpdate (prevProps: any) {
const newParams = this.getParams(this.props);
const oldParams = this.getParams(prevProps);
const newParams = this.getParams(this.props);
if (this.isActive && !isEqual(newParams, oldParams)) {
this
@ -93,9 +95,11 @@ export default function withCall<P extends ApiProps> (endpoint: string, { at, at
}
private getParams (props: any): [boolean, Array<any>] {
const paramValue = paramName
? props[paramName]
: undefined;
const paramValue = paramPick
? paramPick(props)
: paramName
? props[paramName]
: undefined;
if (atProp) {
at = props[atProp];
@ -118,7 +122,7 @@ export default function withCall<P extends ApiProps> (endpoint: string, { at, at
return [true, values];
}
private getApiMethod (newParams: Array<any>): [Method, Array<any>, boolean] {
private getApiMethod (newParams: Array<any>): ApiMethodInfo {
const { api } = this.props;
if (endpoint === 'subscribe') {
@ -162,29 +166,39 @@ export default function withCall<P extends ApiProps> (endpoint: string, { at, at
}
const { api } = this.props;
let info: ApiMethodInfo | undefined;
await api.isReady;
try {
const [apiMethod, params, isSubscription] = this.getApiMethod(newParams);
assert(at || !atProp, 'Unable to perform query on non-existent at hash');
await this.unsubscribe();
info = this.getApiMethod(newParams);
} catch (error) {
console.error(endpoint, '::', error);
}
if (!info) {
return;
}
const [apiMethod, params, isSubscription] = info;
const updateCb = (value?: any) =>
this.triggerUpdate(this.props, value);
await this.unsubscribe();
try {
if (isSubscription) {
const updateCb = (value?: any) =>
this.triggerUpdate(this.props, value);
this.destroy = isMulti
? await apiMethod.multi(params, updateCb)
: await apiMethod(...params, updateCb);
} else {
const value: any = at
? await apiMethod.at(at, ...params)
: await apiMethod(...params);
this.triggerUpdate(this.props, value);
updateCb(
at
? await apiMethod.at(at, ...params)
: await apiMethod(...params)
);
}
} catch (error) {
// console.error(endpoint, '::', error);

View File

@ -20,6 +20,7 @@ export type Options = {
isMulti?: boolean,
params?: Array<any>,
paramName?: string,
paramPick?: (props: any) => any,
paramValid?: boolean,
propName?: string,
transform?: Transform

View File

@ -14,8 +14,8 @@ type Props = CollectionProps;
type State = CollectionState;
class CardGrid extends Collection<Props, State> {
static getDerivedStateFromProps ({ children }: Props) {
if (!children || (children as Array<any>).length <= 0) {
static getDerivedStateFromProps ({ children, isEmpty }: Props) {
if (isEmpty || !children || (children as Array<any>).length <= 0) {
return { isEmpty: true, showHeader: false };
}
return { isEmpty: false, showHeader: true };

View File

@ -11,6 +11,7 @@ export type CollectionProps = I18nProps & {
children: React.ReactNode,
className?: string,
headerText?: React.ReactNode,
isEmpty?: boolean,
emptyText?: React.ReactNode
};