diff --git a/packages/page-accounts/src/modals/ProxyOverview.tsx b/packages/page-accounts/src/modals/ProxyOverview.tsx index c8fb801eb6..0aabf423b6 100644 --- a/packages/page-accounts/src/modals/ProxyOverview.tsx +++ b/packages/page-accounts/src/modals/ProxyOverview.tsx @@ -6,12 +6,12 @@ import type { SubmittableExtrinsic } from '@polkadot/api/types'; import type { AccountId, ProxyDefinition, ProxyType } from '@polkadot/types/interfaces'; import BN from 'bn.js'; -import React, { useCallback, useEffect, useMemo, useState } from 'react'; +import React, { useCallback, useEffect, useRef, useState } from 'react'; import styled from 'styled-components'; import { BatchWarning, Button, Dropdown, InputAddress, Modal, TxButton } from '@polkadot/react-components'; import { useApi } from '@polkadot/react-hooks'; -import { isFunction } from '@polkadot/util'; +import { BN_ZERO, isFunction } from '@polkadot/util'; import { useTranslation } from '../translate'; @@ -34,12 +34,15 @@ interface NewProxyProps extends ValueProps { onChangeAccount: (index: number, value: string | null) => void; onChangeType: (index: number, value: number | undefined) => void; onRemove: (index: number) => void; + proxiedAccount: string; } interface PrevProxyProps extends ValueProps { onRemove: (accountId: AccountId, type: ProxyType, index: number) => void; } +const EMPTY_EXISTING: [ProxyDefinition[], BN] = [[], BN_ZERO]; + function createExtrinsic (api: ApiPromise, batchPrevious: SubmittableExtrinsic<'promise'>[], batchAdded: SubmittableExtrinsic<'promise'>[]): SubmittableExtrinsic<'promise'> | null { if (batchPrevious.length + batchAdded.length === 1) { return batchPrevious.length @@ -102,7 +105,7 @@ function PrevProxy ({ index, onRemove, typeOpts, value: [accountId, type] }: Pre ); } -function NewProxy ({ index, onChangeAccount, onChangeType, onRemove, typeOpts, value: [accountId, type] }: NewProxyProps): React.ReactElement { +function NewProxy ({ index, onChangeAccount, onChangeType, onRemove, proxiedAccount, typeOpts, value: [accountId, type] }: NewProxyProps): React.ReactElement { const { t } = useTranslation(); const _onChangeAccount = useCallback( @@ -132,6 +135,11 @@ function NewProxy ({ index, onChangeAccount, onChangeType, onRemove, typeOpts, v type='account' value={accountId} /> + {accountId.eq(proxiedAccount) && ( +
+ {t('You should not setup proxies to act as a self-proxy.')} +
+ )} { +function ProxyOverview ({ className, onClose, previousProxy: [existing] = EMPTY_EXISTING, proxiedAccount }: Props): React.ReactElement { const { t } = useTranslation(); const { api } = useApi(); - const [batchStackPrevious, setBatchStackPrevious] = useState[]>([]); - const [batchStackAdded, setBatchStackAdded] = useState[]>([]); + const [batchPrevious, setBatchPrevious] = useState[]>([]); + const [batchAdded, setBatchAdded] = useState[]>([]); const [extrinsic, setExtrinsic] = useState | null>(null); - const [previousProxyDisplay, setPreviousProxyDisplay] = useState( - previousProxy?.[0].map(({ delegate, proxyType }): [AccountId, ProxyType] => [delegate, proxyType]) || undefined + const [previous, setPrevious] = useState( + existing.map(({ delegate, proxyType }): [AccountId, ProxyType] => [delegate, proxyType]) ); - const [addedProxies, setAddedProxies] = useState([]); + const [added, setAdded] = useState([]); - useEffect(() => { - if (batchStackPrevious.length || batchStackAdded.length) { - setExtrinsic(() => createExtrinsic(api, batchStackPrevious, batchStackAdded)); - } - }, [api, batchStackPrevious, batchStackAdded]); - - const typeOpts = useMemo( - () => api.createType('ProxyType').defKeys.map((text, value) => ({ text, value })), - [api] + const typeOpts = useRef( + api.createType('ProxyType').defKeys.map((text, value) => ({ text, value })) ); + useEffect((): void => { + setBatchAdded( + added.map(([newAccount, newType]) => createAddProxy(api, newAccount, newType)) + ); + }, [api, added]); + + useEffect((): void => { + (batchPrevious.length || batchAdded.length) && + setExtrinsic(() => createExtrinsic(api, batchPrevious, batchAdded)); + }, [api, batchPrevious, batchAdded]); + const _addProxy = useCallback( - () => { - const newAccount = api.createType('AccountId', proxiedAccount); - const newType = api.createType('ProxyType', 0); - - setAddedProxies((prevState) => { - const newProxy: [AccountId, ProxyType] = [newAccount, newType]; - - return !prevState - ? [newProxy] - : [...prevState, newProxy]; - }); - - setBatchStackAdded((prev) => prev.concat(createAddProxy(api, newAccount, newType))); - }, - [api, proxiedAccount] + () => setAdded((added) => + [...added, [ + added.length + ? added[added.length - 1][0] + : previous.length + ? previous[previous.length - 1][0] + : api.createType('AccountId', proxiedAccount), + api.createType('ProxyType', 0) + ]] + ), + [api, previous, proxiedAccount] ); const _delProxy = useCallback( - (index: number) => { - setAddedProxies((addedProxies) => addedProxies.filter((_, i) => i !== index)); - setBatchStackAdded((batchStackAdded) => batchStackAdded.filter((_, i) => i !== index)); - }, + (index: number) => setAdded((added) => added.filter((_, i) => i !== index)), [] ); const _delPrev = useCallback( - (accountId: AccountId, type: ProxyType, index: number) => { - setPreviousProxyDisplay((previousProxyDisplay) => previousProxyDisplay?.filter((_, i) => i !== index)); - setBatchStackPrevious((batchStackPrevious) => [...batchStackPrevious, createRmProxy(api, accountId, type)]); + (accountId: AccountId, type: ProxyType, index: number): void => { + setPrevious((previous) => previous.filter((_, i) => i !== index)); + setBatchPrevious((previous) => [...previous, createRmProxy(api, accountId, type)]); }, [api] ); const _changeProxyAccount = useCallback( - (index: number, address: string | null) => { - const accountId = api.createType('AccountId', address); - const oldType = addedProxies[index][1]; + (index: number, address: string | null) => setAdded((prevState) => { + const newState = [...prevState]; - // update the UI with the new selected account - setAddedProxies((prevState) => { - const newState = [...prevState]; + newState[index][0] = api.createType('AccountId', address); - newState[index][0] = accountId; - - return newState; - }); - - // Then add the new extrinsic to the batch stack with the new account - setBatchStackAdded((batchStackAdded): SubmittableExtrinsic<'promise'>[] => { - const newBatchAdded = batchStackAdded.filter((_, i) => i !== index); - - newBatchAdded.push(createAddProxy(api, accountId, oldType)); - - return newBatchAdded; - }); - }, - [addedProxies, api] + return newState; + }), + [api] ); const _changeProxyType = useCallback( - (index: number, newTypeNumber: number | undefined) => { - const newType = api.createType('ProxyType', newTypeNumber); - const oldAccount = addedProxies[index][0]; + (index: number, newTypeNumber: number | undefined): void => + setAdded((added) => { + const newState = [...added]; - // update the UI with the new selected type - setAddedProxies((prevState) => { - const newState = [...prevState]; - - newState[index][1] = newType; + newState[index][1] = api.createType('ProxyType', newTypeNumber); return newState; - }); - - // The Batch stack needs to be updated wit the new type - // First, delete the corresponding extrinsic in the batch stack - const newBatchStackAdded = batchStackAdded.filter((_, i) => i !== index); - - // Then add the new extrinsic to the batch stack with the new account - newBatchStackAdded.push(createAddProxy(api, oldAccount, newType)); - setBatchStackAdded(newBatchStackAdded); - }, - [addedProxies, api, batchStackAdded] + }), + [api] ); + const isSameAdd = added.some(([accountId]) => accountId.eq(proxiedAccount)); + return ( - {previousProxyDisplay?.map((value, index) => ( + {previous.map((value, index) => ( ))} - {addedProxies.map((value, index) => ( + {added.map((value, index) => ( ))} @@ -318,7 +298,7 @@ function ProxyOverview ({ className, onClose, previousProxy, proxiedAccount }: P - {previousProxy && previousProxy[0].length !== 0 && ( + {existing.length !== 0 && (