diff --git a/packages/page-staking/src/Filtering.tsx b/packages/page-staking/src/Filtering.tsx new file mode 100644 index 0000000000..aa75a35381 --- /dev/null +++ b/packages/page-staking/src/Filtering.tsx @@ -0,0 +1,59 @@ +// Copyright 2017-2020 @polkadot/app-staking 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 React from 'react'; +import styled from 'styled-components'; +import { Input, Toggle } from '@polkadot/react-components'; +import { useApi } from '@polkadot/react-hooks'; + +import { useTranslation } from './translate'; + +interface Props { + children?: React.ReactNode; + className?: string; + nameFilter: string; + setNameFilter: (value: string) => void; + setWithIdentity: (value: boolean) => void; + withIdentity: boolean; +} + +function Filtering ({ children, className, nameFilter, setNameFilter, setWithIdentity, withIdentity }: Props): React.ReactElement | null { + const { t } = useTranslation(); + const { api } = useApi(); + + return ( +
+ ('filter by name, address or index')} + onChange={setNameFilter} + value={nameFilter} + /> +
+ {children} + {api.query.identity && ( + ('only with an identity')} + onChange={setWithIdentity} + value={withIdentity} + /> + )} +
+
+ ); +} + +export default React.memo(styled(Filtering)` + .staking--optionsBar { + text-align: right; + + .staking--buttonToggle { + display: inline-block; + margin-right: 1rem; + margin-top: 0.5rem; + } + } +`); diff --git a/packages/page-staking/src/Overview/Address/index.tsx b/packages/page-staking/src/Overview/Address/index.tsx index a88c216067..6840fd6095 100644 --- a/packages/page-staking/src/Overview/Address/index.tsx +++ b/packages/page-staking/src/Overview/Address/index.tsx @@ -7,12 +7,11 @@ import { DeriveAccountInfo, DeriveStakingQuery } from '@polkadot/api-derive/type import BN from 'bn.js'; import React, { useCallback, useEffect, useState } from 'react'; -import ApiPromise from '@polkadot/api/promise'; import { AddressSmall, Icon } from '@polkadot/react-components'; import { useAccounts, useApi, useCall } from '@polkadot/react-hooks'; import { FormatBalance } from '@polkadot/react-query'; -import keyring from '@polkadot/ui-keyring'; +import { checkVisibility } from '../../util'; import Favorite from './Favorite'; import NominatedBy from './NominatedBy'; import Status from './Status'; @@ -33,7 +32,7 @@ interface Props { onlineMessage?: boolean; points?: string; toggleFavorite: (accountId: string) => void; - withoutName: boolean; + withIdentity: boolean; } interface StakingState { @@ -72,39 +71,7 @@ function expandInfo ({ exposure, validatorPrefs }: DeriveStakingQuery): StakingS }; } -function checkVisibility (api: ApiPromise, address: string, filterName: string, withoutName: boolean, accountInfo?: DeriveAccountInfo): boolean { - let isVisible = false; - const filterLower = filterName.toLowerCase(); - - if (filterLower || !withoutName) { - if (accountInfo) { - const { accountId, accountIndex, identity, nickname } = accountInfo; - - if (withoutName && (accountId?.toString().includes(filterName) || accountIndex?.toString().includes(filterName))) { - isVisible = true; - } else if (api.query.identity && api.query.identity.identityOf) { - isVisible = (!!identity?.display && identity.display.toLowerCase().includes(filterLower)) || - (!!identity?.displayParent && identity.displayParent.toLowerCase().includes(filterLower)); - } else if (nickname) { - isVisible = nickname.toLowerCase().includes(filterLower); - } - } - - if (!isVisible) { - const account = keyring.getAddress(address); - - isVisible = account?.meta?.name - ? account.meta.name.toLowerCase().includes(filterLower) - : false; - } - } else { - isVisible = true; - } - - return isVisible; -} - -function Address ({ address, className = '', filterName, hasQueries, isAuthor, isElected, isFavorite, isMain, lastBlock, nominatedBy, onlineCount, onlineMessage, points, toggleFavorite, withoutName }: Props): React.ReactElement | null { +function Address ({ address, className = '', filterName, hasQueries, isAuthor, isElected, isFavorite, isMain, lastBlock, nominatedBy, onlineCount, onlineMessage, points, toggleFavorite, withIdentity }: Props): React.ReactElement | null { const { api } = useApi(); const { allAccounts } = useAccounts(); const accountInfo = useCall(api.derive.accounts.info, [address]); @@ -119,9 +86,9 @@ function Address ({ address, className = '', filterName, hasQueries, isAuthor, i useEffect((): void => { setIsVisible( - checkVisibility(api, address, filterName, withoutName, accountInfo) + checkVisibility(api, address, filterName, withIdentity, accountInfo) ); - }, [api, accountInfo, address, filterName, withoutName]); + }, [api, accountInfo, address, filterName, withIdentity]); useEffect((): void => { !isMain && setIsNominating( diff --git a/packages/page-staking/src/Overview/CurrentList.tsx b/packages/page-staking/src/Overview/CurrentList.tsx index cc6d27167d..cd699de4f8 100644 --- a/packages/page-staking/src/Overview/CurrentList.tsx +++ b/packages/page-staking/src/Overview/CurrentList.tsx @@ -7,11 +7,12 @@ import { AccountId, Nominations } from '@polkadot/types/interfaces'; import { Authors } from '@polkadot/react-query/BlockAuthors'; import React, { useCallback, useContext, useEffect, useMemo, useState } from 'react'; -import { Input, Table, Toggle } from '@polkadot/react-components'; +import { Table } from '@polkadot/react-components'; import { useApi, useCall, useFavorites } from '@polkadot/react-hooks'; import { BlockAuthorsContext } from '@polkadot/react-query'; import { Option, StorageKey } from '@polkadot/types'; +import Filtering from '../Filtering'; import { STORE_FAVS_BASE } from '../constants'; import { useTranslation } from '../translate'; import Address from './Address'; @@ -98,7 +99,7 @@ function CurrentList ({ hasQueries, isIntentions, next, stakingOverview }: Props const [{ elected, validators, waiting }, setFiltered] = useState({}); const [nameFilter, setNameFilter] = useState(''); const [nominatedBy, setNominatedBy] = useState | null>(); - const [withoutName, setWithoutName] = useState(true); + const [withIdentity, setWithIdentity] = useState(false); useEffect((): void => { stakingOverview && setFiltered( @@ -129,32 +130,6 @@ function CurrentList ({ hasQueries, isIntentions, next, stakingOverview }: Props [] ], [t]); - const filter = useMemo(() => ( -
- ('filter by name, address or index')} - onChange={setNameFilter} - value={nameFilter} - /> -
- {api.query.identity && ( - ('with/without identity') - : t('only with an identity') - } - onChange={setWithoutName} - value={withoutName} - /> - )} -
-
- ), [api, nameFilter, t, withoutName]); - const _renderRows = useCallback( (addresses?: AccountExtend[], isMain?: boolean): React.ReactNode[] => (addresses || []).map(([address, isElected, isFavorite]): React.ReactNode => ( @@ -173,17 +148,24 @@ function CurrentList ({ hasQueries, isIntentions, next, stakingOverview }: Props onlineMessage={recentlyOnline?.[address]?.hasMessage} points={eraPoints[address]} toggleFavorite={toggleFavorite} - withoutName={withoutName} + withIdentity={withIdentity} /> )), - [byAuthor, eraPoints, hasQueries, lastBlockAuthors, nameFilter, nominatedBy, recentlyOnline, toggleFavorite, withoutName] + [byAuthor, eraPoints, hasQueries, lastBlockAuthors, nameFilter, nominatedBy, recentlyOnline, toggleFavorite, withIdentity] ); return isIntentions ? ( ('No waiting validators found')} - filter={filter} + filter={ + + } header={headerActive} > {_renderRows(elected, false).concat(..._renderRows(waiting, false))} @@ -192,7 +174,14 @@ function CurrentList ({ hasQueries, isIntentions, next, stakingOverview }: Props : (
('No active validators found')} - filter={filter} + filter={ + + } header={headerWaiting} > {_renderRows(validators, true)} diff --git a/packages/page-staking/src/Targets/Validator.tsx b/packages/page-staking/src/Targets/Validator.tsx index e3e4a86606..a12cbccea5 100644 --- a/packages/page-staking/src/Targets/Validator.tsx +++ b/packages/page-staking/src/Targets/Validator.tsx @@ -15,9 +15,11 @@ import keyring from '@polkadot/ui-keyring'; import MaxBadge from '../MaxBadge'; import Favorite from '../Overview/Address/Favorite'; +import { checkVisibility } from '../util'; interface Props { canSelect: boolean; + filterName: string; info: ValidatorInfo; isSelected: boolean; toggleFavorite: (accountId: string) => void; @@ -26,39 +28,37 @@ interface Props { withIdentity: boolean; } -function checkVisibility (api: ApiPromise, accountInfo: DeriveAccountInfo): boolean { - let isVisible = false; +function checkIdentity (api: ApiPromise, accountInfo: DeriveAccountInfo): boolean { + let hasIdentity = false; const { accountId, identity, nickname } = accountInfo; if (api.query.identity && api.query.identity.identityOf) { - isVisible = !!(identity?.display && identity.display.toString()); + hasIdentity = !!(identity?.display && identity.display.toString()); } else if (nickname) { - isVisible = !!nickname.toString(); + hasIdentity = !!nickname.toString(); } - if (!isVisible && accountId) { + if (!hasIdentity && accountId) { const account = keyring.getAddress(accountId.toString()); - isVisible = !!account?.meta?.name; + hasIdentity = !!account?.meta?.name; } - return isVisible; + return hasIdentity; } -function Validator ({ canSelect, info, isSelected, toggleFavorite, toggleSelected, withElected, withIdentity }: Props): React.ReactElement | null { +function Validator ({ canSelect, filterName, info, isSelected, toggleFavorite, toggleSelected, withElected, withIdentity }: Props): React.ReactElement | null { const { api } = useApi(); const accountInfo = useCall(api.derive.accounts.info, [info.accountId]); - const [hasName, setHasName] = useState(true); + const [isVisible, setVisibility] = useState(true); useEffect((): void => { if (accountInfo) { - const hasIdentity = checkVisibility(api, accountInfo); - - info.hasIdentity = hasIdentity; - setHasName(hasIdentity); + info.hasIdentity = checkIdentity(api, accountInfo); + setVisibility(checkVisibility(api, info.key, filterName, withIdentity, accountInfo)); } - }, [accountInfo, api, info]); + }, [accountInfo, api, filterName, info, withIdentity]); const _onQueryStats = useCallback( (): void => { @@ -72,7 +72,7 @@ function Validator ({ canSelect, info, isSelected, toggleFavorite, toggleSelecte [info.key, toggleSelected] ); - if ((withIdentity && !hasName) || (withElected && !info.isElected)) { + if (!isVisible || (withElected && !info.isElected)) { return null; } diff --git a/packages/page-staking/src/Targets/index.tsx b/packages/page-staking/src/Targets/index.tsx index 3bf1c2c890..881a12e211 100644 --- a/packages/page-staking/src/Targets/index.tsx +++ b/packages/page-staking/src/Targets/index.tsx @@ -9,8 +9,8 @@ import { SortedTargets, TargetSortBy, ValidatorInfo } from '../types'; import React, { useCallback, useEffect, useMemo, useState } from 'react'; import styled from 'styled-components'; import { Button, Icon, InputBalance, Table, Toggle } from '@polkadot/react-components'; -import { useApi } from '@polkadot/react-hooks'; +import Filtering from '../Filtering'; import { MAX_NOMINATIONS } from '../constants'; import { useTranslation } from '../translate'; import Nominate from './Nominate'; @@ -47,10 +47,10 @@ function sort (sortBy: TargetSortBy, sortFromMax: boolean, validators: Validator function Targets ({ className = '', ownStashes, targets: { calcWith, lastReward, nominators, setCalcWith, toggleFavorite, totalStaked, validators } }: Props): React.ReactElement { const { t } = useTranslation(); - const { api } = useApi(); const ownNominators = useOwnNominators(ownStashes); const [selected, setSelected] = useState([]); const [sorted, setSorted] = useState(); + const [nameFilter, setNameFilter] = useState(''); const [withElected, setWithElected] = useState(false); const [withIdentity, setWithIdentity] = useState(false); const [{ sortBy, sortFromMax }, setSortBy] = useState({ sortBy: 'rankOverall', sortFromMax: true }); @@ -128,9 +128,22 @@ function Targets ({ className = '', ownStashes, targets: { calcWith, lastReward, onChange={setCalcWith} value={calcWith} /> + + ('limit to elected')} + onChange={setWithElected} + value={withElected} + /> + ) - ), [calcWith, setCalcWith, sorted, t]); + ), [calcWith, setCalcWith, nameFilter, sorted, t, withElected, withIdentity]); return (
@@ -141,22 +154,6 @@ function Targets ({ className = '', ownStashes, targets: { calcWith, lastReward, totalStaked={totalStaked} /> - - ('limit to elected')} - onChange={setWithElected} - value={withElected} - /> - {api.query.identity && ( - ('only with an identity')} - onChange={setWithIdentity} - value={withIdentity} - /> - )} -