// Copyright 2017-2020 @polkadot/react-query authors & contributors // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. import { DeriveAccountInfo } from '@polkadot/api-derive/types'; import { BareProps } from '@polkadot/react-api/types'; import { AccountId, AccountIndex, Address, RegistrarInfo } from '@polkadot/types/interfaces'; import React, { useEffect, useMemo, useState } from 'react'; import styled from 'styled-components'; 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; defaultName?: string; label?: React.ReactNode; onClick?: () => void; override?: React.ReactNode; toggle?: any; value?: AccountId | AccountIndex | Address | string | null | Uint8Array; withShort?: boolean; } const nameCache: Map = new Map(); function defaultOrAddr (defaultName = '', _address: AccountId | AccountIndex | Address | string | Uint8Array, _accountIndex?: AccountIndex | null): [React.ReactNode, boolean, boolean] { const accountId = _address.toString(); const accountIndex = (_accountIndex || '').toString(); const [isAddressExtracted,, extracted] = getAddressName(accountId, null, defaultName); const [isAddressCached, nameCached] = nameCache.get(accountId) || [false, null]; if (extracted && isAddressCached && !isAddressExtracted) { // skip, default return } else if (nameCached) { return [nameCached, false, isAddressCached]; } else if (isAddressExtracted && accountIndex) { nameCache.set(accountId, [true, accountIndex]); return [accountIndex, false, true]; } return [extracted, !isAddressExtracted, isAddressExtracted]; } function AccountName ({ children, className, defaultName, label, onClick, override, style, toggle, value, withShort }: Props): React.ReactElement { const { t } = useTranslation(); const { api } = useApi(); const { allAccounts } = useAccounts(); const [isJudgementOpen, toggleJudgement] = useToggle(); const registrars = useCall[]>(api.query.identity?.registrars, []); const info = useCall(api.derive.accounts.info as any, [value]); const [accountId, setAccountId] = useState(null); const [isRegistrar, setIsRegistrar] = useState(false); const [judgementAccountId, setJudgementAccountId] = useState(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 => { const [name, isLocal, isAddress] = defaultOrAddr(defaultName, accountId || address, withShort ? null : accountIndex); return (
{name}
); }; const [name, setName] = useState((): React.ReactNode => _extractName()); // determine if we have a registrar or not - registrars are allowed to approve 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]); // find the id of our registrar in the list 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]); // set the actual nickname, localname, accountIndex, accountId 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); const isBad = identity.judgements.some(([, judgement]): boolean => judgement.isErroneous || judgement.isLowQuality); // FIXME This needs to be i18n, with plurals const hover = (
{`${identity.judgements.length ? identity.judgements.length : 'no'} judgement${identity.judgements.length === 1 ? '' : 's'}${identity.judgements.length ? ': ' : ''}${identity.judgements.map(([, judgement]): string => judgement.toString()).join(', ')}`}
{identity.legal && ( )} {identity.email && ( )} {identity.web && ( )} {identity.riot && ( )}
{t('display')} {identity.display}
{t('legal')} {identity.legal}
{t('email')} {identity.email}
{t('www')} {identity.web}
{t('riot')} {identity.riot}
); const displayName = isGood ? identity.display : identity.display.replace(/[^\x20-\x7E]/g, ''); const name = (
} isInline isSmall isTooltip onClick={isRegistrar ? toggleJudgement : undefined} type={ isGood ? 'green' : isBad ? 'brown' : 'gray' } /> {displayName}
); nameCache.set(address, [false, displayName]); setName((): React.ReactNode => name); } else { setName((): React.ReactNode => _extractName(accountId, accountIndex)); } } else if (nickname) { nameCache.set(address, [false, nickname]); setName(nickname); } else { setName(defaultOrAddr(defaultName, accountId || address, withShort ? null : accountIndex)); } }, [info, toggle]); return ( <> {isJudgementOpen && (