Provide judgement via account hover (#2078)

* Provide judgement via account hover

* Click to provide judgement
This commit is contained in:
Jaco Greeff 2019-12-19 13:08:21 +01:00 committed by GitHub
parent 364e50f6c5
commit 08adfbc43f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 141 additions and 23 deletions

View File

@ -32,11 +32,12 @@ function WarmUp (): React.ReactElement {
const { api, isApiReady } = useApi();
const fees = useCall<any>(isApiReady ? api.derive.balances.fees : undefined, []);
const indexes = useCall<any>(isApiReady ? api.derive.accounts.indexes : undefined, []);
const registrars = useCall<any>(isApiReady ? api.query.identity?.registrars : undefined, []);
const staking = useCall<any>(isApiReady ? api.derive.staking.overview : undefined, []);
const [hasValues, setHasValues] = useState(false);
useEffect((): void => {
setHasValues(!!fees || !!indexes || !!staking);
setHasValues(!!fees || !!indexes || !!registrars || !!staking);
}, []);
return (

View File

@ -4,15 +4,23 @@
import { DeriveAccountInfo } from '@polkadot/api-derive/types';
import { BareProps } from '@polkadot/react-api/types';
import { AccountId, AccountIndex, Address } from '@polkadot/types/interfaces';
import { AccountId, AccountIndex, Address, RegistrarInfo } from '@polkadot/types/interfaces';
import React, { useEffect, useMemo, useState } from 'react';
import styled from 'styled-components';
import { Badge, Icon } from '@polkadot/react-components';
import { getAddressName } from '@polkadot/react-components/util';
import { useCall, useApi } from '@polkadot/react-hooks';
import { useCall, useAccounts, useApi, useToggle } from '@polkadot/react-hooks';
import { Option } from '@polkadot/types';
import { useTranslation } from './translate';
import { getAddressName } from './util';
import Badge from './Badge';
import Button from './Button';
import Dropdown from './Dropdown';
import Icon from './Icon';
import Input from './Input';
import InputAddress from './InputAddress';
import Modal from './Modal';
import TxButton from './TxButton';
interface Props extends BareProps {
children?: React.ReactNode;
@ -50,7 +58,15 @@ function defaultOrAddr (defaultName = '', _address: AccountId | AccountIndex | A
function AccountName ({ children, className, defaultName, label, onClick, override, style, toggle, value, withShort }: Props): React.ReactElement<Props> {
const { t } = useTranslation();
const { api } = useApi();
const { allAccounts } = useAccounts();
const [isJudgementOpen, toggleJudgement] = useToggle();
const registrars = useCall<Option<RegistrarInfo>[]>(api.query.identity?.registrars, []);
const info = useCall<DeriveAccountInfo>(api.derive.accounts.info as any, [value]);
const [accountId, setAccountId] = useState<string | null>(null);
const [isRegistrar, setIsRegistrar] = useState(false);
const [judgementAccountId, setJudgementAccountId] = useState<string | null>(null);
const [judgementEnum, setJudgementEnum] = useState(0);
const [registrarIndex, setRegistrarIndex] = useState(-1);
const address = useMemo((): string => (value || '').toString(), [value]);
const _extractName = (accountId?: AccountId, accountIndex?: AccountIndex): React.ReactNode => {
@ -65,9 +81,43 @@ function AccountName ({ children, className, defaultName, label, onClick, overri
const [name, setName] = useState<React.ReactNode>((): React.ReactNode => _extractName());
useEffect((): void => {
if (allAccounts && registrars) {
setIsRegistrar(
registrars
.map((registrar): string| null =>
registrar.isSome
? registrar.unwrap().account.toString()
: null
)
.some((regId): boolean => !!regId && allAccounts.includes(regId))
);
}
}, [allAccounts, registrars]);
useEffect((): void => {
if (registrars && judgementAccountId) {
setRegistrarIndex(
registrars
.map((registrar): string| null =>
registrar.isSome
? registrar.unwrap().account.toString()
: null
)
.indexOf(judgementAccountId)
);
} else {
setRegistrarIndex(-1);
}
}, [judgementAccountId, registrars]);
useEffect((): void => {
const { accountId, accountIndex, identity, nickname } = info || {};
if (accountId) {
setAccountId(accountId.toString());
}
if (api.query.identity?.identityOf) {
if (identity?.display) {
const isGood = identity.judgements.some(([, judgement]): boolean => judgement.isKnownGood || judgement.isReasonable);
@ -120,6 +170,7 @@ function AccountName ({ children, className, defaultName, label, onClick, overri
isInline
isSmall
isTooltip
onClick={isRegistrar ? toggleJudgement : undefined}
type={
isGood
? 'green'
@ -148,17 +199,71 @@ function AccountName ({ children, className, defaultName, label, onClick, overri
}, [info, toggle]);
return (
<div
className={className}
onClick={
override
? undefined
: onClick
}
style={style}
>
{label || ''}{override || name}{children}
</div>
<>
{isJudgementOpen && (
<Modal
header={t('Provide judgement')}
open
size='small'
>
<Modal.Content>
<InputAddress
label={t('registrar account')}
onChange={setJudgementAccountId}
/>
<Input
isDisabled
label={t('registrar index')}
value={registrarIndex === -1 ? t('invalid/unknown registrar account') : registrarIndex}
/>
<Dropdown
label={t('judgement')}
onChange={setJudgementEnum}
options={[
{ value: 0, text: 'Unknown' },
{ value: 1, text: 'Fee paid' },
{ value: 2, text: 'Reasonable' },
{ value: 3, text: 'Known good' },
{ value: 4, text: 'Out of date' },
{ value: 5, text: 'Low quality' }
]}
value={judgementEnum}
/>
</Modal.Content>
<Modal.Actions>
<Button.Group>
<Button
icon='cancel'
isNegative
label={t('Cancel')}
onClick={toggleJudgement}
/>
<Button.Or />
<TxButton
accountId={judgementAccountId}
icon='check'
isDisabled={registrarIndex === -1}
label={t('Judge')}
onClick={toggleJudgement}
params={[registrarIndex, accountId, judgementEnum]}
tx='identity.provideJudgement'
/>
</Button.Group>
</Modal.Actions>
</Modal>
)}
<div
className={className}
onClick={
override
? undefined
: onClick
}
style={style}
>
{label || ''}{override || name}{children}
</div>
</>
);
}

View File

@ -8,10 +8,10 @@ import { BareProps } from './types';
import BN from 'bn.js';
import React from 'react';
import styled from 'styled-components';
import { AccountName } from '@polkadot/react-query';
import { KeyringItemType } from '@polkadot/ui-keyring/types';
import { classes, toShortAddress } from './util';
import AccountName from './AccountName';
import BalanceDisplay from './Balance';
import BondedDisplay from './Bonded';
import IdentityIcon from './IdentityIcon';

View File

@ -6,8 +6,9 @@ import { Address, AccountId } from '@polkadot/types/interfaces';
import React from 'react';
import styled from 'styled-components';
import { AccountIndex, AccountName } from '@polkadot/react-query';
import { AccountIndex } from '@polkadot/react-query';
import AccountName from './AccountName';
import IdentityIcon from './IdentityIcon';
interface Props {

View File

@ -14,20 +14,22 @@ interface Props {
isInline?: boolean;
isSmall?: boolean;
isTooltip?: boolean;
onClick?: () => void;
type: 'counter' | 'online' | 'offline' | 'next' | 'runnerup' | 'selected' | 'green' | 'blue' | 'brown' | 'gray';
}
let badgeId = 0;
function Badge ({ className, hover, info, isInline, isSmall, isTooltip, type }: Props): React.ReactElement<Props> | null {
function Badge ({ className, hover, info, isInline, isSmall, isTooltip, onClick, type }: Props): React.ReactElement<Props> | null {
const [key] = useState(`${Date.now()}-${badgeId++}`);
return (
<div
className={`ui--Badge ${isInline && 'isInline'} ${isTooltip && 'isTooltip'} ${isSmall && 'isSmall'} ${type} ${className}`}
className={`ui--Badge ${isInline && 'isInline'} ${isTooltip && 'isTooltip'} ${isSmall && 'isSmall'} ${onClick && 'isClickable'} ${type} ${className}`}
data-for={`badge-status-${key}`}
data-tip={true}
data-tip-disable={!isTooltip}
onClick={onClick}
>
<div className='badge'>
{info}
@ -57,10 +59,15 @@ export default styled(Badge)`
width: 22px;
i.icon {
cursor: inherit !important;
margin: 0;
width: 1em;
}
&.isClickable {
cursor: pointer;
}
&.isSmall {
box-shadow: none;
font-size: 10px;

View File

@ -4,8 +4,8 @@
import React from 'react';
import styled from 'styled-components';
import { AccountName } from '@polkadot/react-query';
import AccountName from '../AccountName';
import IdentityIcon from '../IdentityIcon';
interface Props {

View File

@ -59,7 +59,6 @@ function Tooltip ({ className, effect = 'solid', offset, place = 'bottom', text,
export default styled(Tooltip)`
table {
border: 0;
margin-top: 0.75rem;
td {
text-align: left;
@ -72,4 +71,9 @@ export default styled(Tooltip)`
white-space: nowrap;
}
}
div+table,
table+div {
margin-top: 0.75rem;
}
`;

View File

@ -2,6 +2,7 @@
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
export { default as AccountName } from './AccountName';
export { default as ActionItem } from './ActionItem';
export { default as AddressInfo } from './AddressInfo';
export { default as AddressMini } from './AddressMini';

View File

@ -3,7 +3,6 @@
// of the Apache-2.0 license. See the LICENSE file for details.
export { default as AccountIndex } from './AccountIndex';
export { default as AccountName } from './AccountName';
export { default as Available } from './Available';
export { default as Balance } from './Balance';
export { default as BalanceFree } from './BalanceFree';