From ee7c4fa45b66f0804bd92734816ddfd2241d9145 Mon Sep 17 00:00:00 2001 From: Jaco Greeff Date: Tue, 28 Apr 2020 09:18:41 +0200 Subject: [PATCH] Allow pre-select option when nominating (#2644) * Allow pre-select option when nominating * Pre-select as default (faster initial operation) --- .../src/Actions/Account/Nominate.tsx | 111 +++++++++++++++--- .../src/Actions/Account/index.tsx | 5 +- packages/page-staking/src/Actions/index.tsx | 5 +- .../page-staking/src/Targets/Nominate.tsx | 7 +- packages/page-staking/src/Targets/index.tsx | 2 +- packages/page-staking/src/index.tsx | 1 + .../src/InputAddressMulti/index.tsx | 1 + 7 files changed, 112 insertions(+), 20 deletions(-) diff --git a/packages/page-staking/src/Actions/Account/Nominate.tsx b/packages/page-staking/src/Actions/Account/Nominate.tsx index df513c78b8..16adff456c 100644 --- a/packages/page-staking/src/Actions/Account/Nominate.tsx +++ b/packages/page-staking/src/Actions/Account/Nominate.tsx @@ -2,12 +2,14 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. -import React, { useState } from 'react'; +import { SortedTargets } from '../../types'; + +import React, { useCallback, useState } from 'react'; import styled from 'styled-components'; -import { InputAddressMulti, InputAddress, Modal, TxButton } from '@polkadot/react-components'; +import { AddressMini, InputAddressMulti, InputAddress, Modal, Static, Toggle, TxButton } from '@polkadot/react-components'; import { useFavorites } from '@polkadot/react-hooks'; -import { STORE_FAVS_BASE } from '../../constants'; +import { MAX_NOMINATIONS, STORE_FAVS_BASE } from '../../constants'; import { useTranslation } from '../../translate'; interface Props { @@ -17,15 +19,29 @@ interface Props { nominating?: string[]; onClose: () => void; stashId: string; + targets: SortedTargets; validators: string[]; } -const MAX_NOMINEES = 16; +interface Selected { + isAutoSelect: boolean; + selected: string[]; +} -function Nominate ({ className, controllerId, next, nominating, onClose, stashId, validators }: Props): React.ReactElement | null { +function autoPick (targets: SortedTargets): string[] { + return (targets.validators || []).filter((_, index) => index < MAX_NOMINATIONS).map(({ key }) => key); +} + +function initialPick (targets: SortedTargets): Selected { + return targets.validators?.length + ? { isAutoSelect: true, selected: autoPick(targets) } + : { isAutoSelect: false, selected: [] }; +} + +function Nominate ({ className, controllerId, next, nominating, onClose, stashId, targets, validators }: Props): React.ReactElement | null { const { t } = useTranslation(); const [favorites] = useFavorites(STORE_FAVS_BASE); - const [selection, setSelection] = useState([]); + const [{ isAutoSelect, selected }, setSelected] = useState(initialPick(targets)); const [available] = useState((): string[] => { const shortlist = [ // ensure that the favorite is included in the list of stashes @@ -39,6 +55,24 @@ function Nominate ({ className, controllerId, next, nominating, onClose, stashId .concat(...(next || []).filter((acc) => !shortlist.includes(acc))); }); + const _setSelected = useCallback( + (selected: string[]) => setSelected(({ isAutoSelect }: Selected) => ({ + isAutoSelect, + selected + })), + [] + ); + + const _toggleAutoSelect = useCallback( + (isAutoSelect: boolean) => setSelected(() => ({ + isAutoSelect, + selected: isAutoSelect + ? autoPick(targets) + : [] + })), + [targets] + ); + return ( - + ( + + )) + } + /> +
{t('The auto-selection is done on the current profitability of the validators taking your favorites into account. It is adjusted based on the commission and current range of backing for teh validator. The calculation may and will change over time, so it is rather a selection based on the current state of the network, not a predictor of future profitability.')}
+ + ) + : ( + + ) + } + @@ -82,6 +147,20 @@ function Nominate ({ className, controllerId, next, nominating, onClose, stashId } export default React.memo(styled(Nominate)` + article.warning { + margin-top: 0; + } + + .auto--toggle { + margin: 0.5rem 0 0; + text-align: right; + width: 100%; + } + + .ui--Static .ui--AddressMini.padded { + padding-top: 0.5rem; + } + .shortlist { display: flex; flex-wrap: wrap; diff --git a/packages/page-staking/src/Actions/Account/index.tsx b/packages/page-staking/src/Actions/Account/index.tsx index 6f97f439b1..8928ba0e68 100644 --- a/packages/page-staking/src/Actions/Account/index.tsx +++ b/packages/page-staking/src/Actions/Account/index.tsx @@ -5,6 +5,7 @@ import { DeriveBalancesAll, DeriveStakingAccount } from '@polkadot/api-derive/types'; import { EraIndex } from '@polkadot/types/interfaces'; import { StakerState } from '@polkadot/react-hooks/types'; +import { SortedTargets } from '../../types'; import React, { useEffect, useState } from 'react'; import styled from 'styled-components'; @@ -29,10 +30,11 @@ interface Props { info: StakerState; next?: string[]; stashId: string; + targets: SortedTargets; validators?: string[]; } -function Account ({ className, info: { controllerId, destination, destinationId, hexSessionIdNext, hexSessionIdQueue, isLoading, isOwnController, isOwnStash, isStashNominating, isStashValidating, nominating, sessionIds, stakingLedger, stashId }, isDisabled, next, validators }: Props): React.ReactElement { +function Account ({ className, info: { controllerId, destination, destinationId, hexSessionIdNext, hexSessionIdQueue, isLoading, isOwnController, isOwnStash, isStashNominating, isStashValidating, nominating, sessionIds, stakingLedger, stashId }, isDisabled, next, targets, validators }: Props): React.ReactElement { const { t } = useTranslation(); const { api } = useApi(); const balancesAll = useCall(api.derive.balances.all, [stashId]); @@ -76,6 +78,7 @@ function Account ({ className, info: { controllerId, destination, destinationId, nominating={nominating} onClose={toggleNominate} stashId={stashId} + targets={targets} validators={validators} /> )} diff --git a/packages/page-staking/src/Actions/index.tsx b/packages/page-staking/src/Actions/index.tsx index 03d930c305..f091ad2f78 100644 --- a/packages/page-staking/src/Actions/index.tsx +++ b/packages/page-staking/src/Actions/index.tsx @@ -4,6 +4,7 @@ import { ActiveEraInfo, EraIndex } from '@polkadot/types/interfaces'; import { StakerState } from '@polkadot/react-hooks/types'; +import { SortedTargets } from '../types'; import BN from 'bn.js'; import React, { useEffect, useMemo, useState } from 'react'; @@ -23,6 +24,7 @@ interface Props { ownStashes?: StakerState[]; next?: string[]; validators?: string[]; + targets: SortedTargets; } interface State { @@ -30,7 +32,7 @@ interface State { foundStashes?: StakerState[]; } -function Actions ({ className, isInElection, next, ownStashes, validators }: Props): React.ReactElement { +function Actions ({ className, isInElection, next, ownStashes, targets, validators }: Props): React.ReactElement { const { t } = useTranslation(); const { api } = useApi(); const activeEra = useCall(api.query.staking?.activeEra, [], { @@ -85,6 +87,7 @@ function Actions ({ className, isInElection, next, ownStashes, validators }: Pro isDisabled={isInElection} key={info.stashId} next={next} + targets={targets} validators={validators} /> ))} diff --git a/packages/page-staking/src/Targets/Nominate.tsx b/packages/page-staking/src/Targets/Nominate.tsx index 41500cafab..acdfa1dd3f 100644 --- a/packages/page-staking/src/Targets/Nominate.tsx +++ b/packages/page-staking/src/Targets/Nominate.tsx @@ -5,6 +5,7 @@ import { StakerState } from '@polkadot/react-hooks/types'; import React, { useCallback, useEffect, useState } from 'react'; +import styled from 'styled-components'; import { AddressMini, Button, InputAddress, Modal, Static, TxButton } from '@polkadot/react-components'; import { useToggle } from '@polkadot/react-hooks'; @@ -92,4 +93,8 @@ function Nominate ({ ownNominators, targets }: Props): React.ReactElement ); } -export default React.memo(Nominate); +export default React.memo(styled(Nominate)` + .ui--AddressMini.padded { + padding-top: 0.5rem; + } +`); diff --git a/packages/page-staking/src/Targets/index.tsx b/packages/page-staking/src/Targets/index.tsx index b065563198..3b012b9dfe 100644 --- a/packages/page-staking/src/Targets/index.tsx +++ b/packages/page-staking/src/Targets/index.tsx @@ -136,7 +136,7 @@ function Targets ({ className, ownStashes, targets: { calcWith, lastReward, nomi filter={filter} header={header} > - {validators && sorted?.map((index): React.ReactNode => + {validators && sorted && (validators.length === sorted.length) && sorted.map((index): React.ReactNode => isInElection={isInElection} next={next} ownStashes={ownStashes} + targets={targets} validators={validators} />