diff --git a/packages/ui/cypress/fixtures/landingData.ts b/packages/ui/cypress/fixtures/landingData.ts index 32a39c26..d9d1ca02 100644 --- a/packages/ui/cypress/fixtures/landingData.ts +++ b/packages/ui/cypress/fixtures/landingData.ts @@ -3,6 +3,8 @@ export const defaultNetwork = 'rococo' const WATCH_ACCOUNT_ANCHOR = 'watched-accounts' export const landingPageNetwork = (networkName: string) => `${baseUrl}?network=${networkName}` export const landingPageUrl = landingPageNetwork('rococo') -export const settingsPageUrl = `${baseUrl}/settings?network=${defaultNetwork}` -export const settingsPageWatchAccountUrl = `${settingsPageUrl}#${WATCH_ACCOUNT_ANCHOR}` +export const getSettingsPageUrl = (network = defaultNetwork) => + `${baseUrl}/settings?network=${network}` +export const getSettingsPageWatchAccountUrl = (network = defaultNetwork) => + `${getSettingsPageUrl(network)}#${WATCH_ACCOUNT_ANCHOR}` export const landingPageAddressUrl = (address: string) => `${landingPageUrl}&address=${address}` diff --git a/packages/ui/cypress/fixtures/nameDisplay.ts b/packages/ui/cypress/fixtures/nameDisplay.ts new file mode 100644 index 00000000..9038b72f --- /dev/null +++ b/packages/ui/cypress/fixtures/nameDisplay.ts @@ -0,0 +1,6 @@ +export const multisigWithKusamaIdentity = { + publicKey: '0x905f923a67cec79db9e1415567822f2c440e794c4a38b43144bfb1a044b2a2f2', + address: 'Fqcoa6z2T8QJkNWMr8M7LcVmPonv7wwARsvciiL7HyUnJc4', + identityMain: 'ChainSafe', + subIdentity: 'ChainSafe Validator 0' +} diff --git a/packages/ui/cypress/support/page-objects/components/accountDisplay.ts b/packages/ui/cypress/support/page-objects/components/accountDisplay.ts index 320409fb..9c049650 100644 --- a/packages/ui/cypress/support/page-objects/components/accountDisplay.ts +++ b/packages/ui/cypress/support/page-objects/components/accountDisplay.ts @@ -3,6 +3,13 @@ export const accountDisplay = { pureBadge: () => cy.get('[data-cy=badge-pure]'), multisigBadge: () => cy.get('[data-cy=badge-multi]'), nameLabel: () => cy.get('[data-cy=label-account-name]'), + noNameLabel: () => cy.get('[data-cy=label-no-name]'), addressLabel: () => cy.get('[data-cy=label-account-address]'), - watchedIcon: () => cy.get('[data-cy=icon-watched]') + watchedIcon: () => cy.get('[data-cy=icon-watched]'), + nameEditButton: () => cy.get('[data-cy=button-name-edit]'), + validateEditButton: () => cy.get('[data-cy=button-edition-submit]'), + cancelEditButton: () => cy.get('[data-cy=button-edition-cancel]'), + nameEditionInput: () => cy.get('[data-cy=input-name-edition]'), + identityIcon: () => cy.get('[data-cy=icon-identity]'), + subIdentityLabel: () => cy.get('[data-cy=label-sub-identity]') } diff --git a/packages/ui/cypress/tests/address-bar.cy.ts b/packages/ui/cypress/tests/address-bar.cy.ts index 252a8c0b..19fa9741 100644 --- a/packages/ui/cypress/tests/address-bar.cy.ts +++ b/packages/ui/cypress/tests/address-bar.cy.ts @@ -236,6 +236,7 @@ describe('Account address in the address bar', () => { // check that there is the pure address in the address bar cy.url().should('include', expectedPureAddress) + topMenuItems.multiproxySelectorInputDesktop().should('have.value', expectedPureAddress) topMenuItems .desktopMenu() .within(() => diff --git a/packages/ui/cypress/tests/name-edition-display.cy.ts b/packages/ui/cypress/tests/name-edition-display.cy.ts new file mode 100644 index 00000000..9f4c05eb --- /dev/null +++ b/packages/ui/cypress/tests/name-edition-display.cy.ts @@ -0,0 +1,171 @@ +import { accountDisplay } from '../support/page-objects/components/accountDisplay' +import { getSettingsPageWatchAccountUrl, landingPageNetwork } from '../fixtures/landingData' +import { settingsPage } from '../support/page-objects/settingsPage' +import { topMenuItems } from '../support/page-objects/topMenuItems' +import { watchMultisigs } from '../fixtures/watchAccounts/watchMultisigs' +import { multisigPage } from '../support/page-objects/multisigPage' +import { multisigWithKusamaIdentity } from '../fixtures/nameDisplay' + +describe('Name Edition and Display', () => { + it('can see the edit button with no name', () => { + const { purePublicKey } = watchMultisigs['multisig-with-pure'] + + cy.setupAndVisit({ + url: getSettingsPageWatchAccountUrl('polkadot'), + watchedAccounts: [purePublicKey] + }) + + settingsPage.accountContainer().within(() => { + accountDisplay.identicon().should('be.visible') + accountDisplay.addressLabel().should('be.visible') + accountDisplay.noNameLabel().should('have.text', 'No Name') + accountDisplay.nameEditButton().should('be.visible') + }) + }) + + it('can edit with no name before', () => { + const { purePublicKey } = watchMultisigs['multisig-with-pure'] + const newName = 'some name' + const newName2 = 'new name 2' + cy.setupAndVisit({ + url: getSettingsPageWatchAccountUrl('polkadot'), + watchedAccounts: [purePublicKey] + }) + + settingsPage.accountContainer().within(() => { + accountDisplay.nameEditButton().click() + accountDisplay.nameEditionInput().should('be.focused') + // editing with enter + accountDisplay.nameEditionInput().type(`${newName}{Enter}`) + accountDisplay.nameEditionInput().should('not.exist') + accountDisplay.nameLabel().should('have.text', newName) + accountDisplay.noNameLabel().should('not.exist') + + //editing with validate button + accountDisplay.nameEditButton().click() + accountDisplay.nameEditionInput().should('have.value', newName) + accountDisplay.nameEditionInput().type(`{selectAll}${newName2}`) + accountDisplay.validateEditButton().click() + accountDisplay.nameEditionInput().should('not.exist') + accountDisplay.nameLabel().should('have.text', newName2) + accountDisplay.noNameLabel().should('not.exist') + }) + }) + + it('can edit and cancel with a name before', () => { + const { purePublicKey } = watchMultisigs['multisig-with-pure'] + const originalName = 'some name' + const newName = 'new name' + cy.setupAndVisit({ + url: getSettingsPageWatchAccountUrl('polkadot'), + watchedAccounts: [purePublicKey], + accountNames: { [purePublicKey]: originalName } + }) + + settingsPage.accountContainer().within(() => { + //edit and cancel with Escape + accountDisplay.nameEditButton().click() + accountDisplay.nameEditionInput().should('have.value', originalName) + accountDisplay.nameEditionInput().type(`{selectAll}${newName}{esc}`) + accountDisplay.nameEditionInput().should('not.exist') + accountDisplay.nameLabel().should('have.text', originalName) + accountDisplay.noNameLabel().should('not.exist') + + //edit and cancel with cancel button + accountDisplay.nameEditButton().click() + accountDisplay.nameEditionInput().should('have.value', originalName) + accountDisplay.nameEditionInput().type(`{selectAll}${newName}`) + accountDisplay.cancelEditButton().click() + accountDisplay.nameEditionInput().should('not.exist') + accountDisplay.nameLabel().should('have.text', originalName) + accountDisplay.noNameLabel().should('not.exist') + + //back to no name + accountDisplay.nameEditButton().click() + accountDisplay.nameEditionInput().should('have.value', originalName) + accountDisplay.nameEditionInput().type(`{selectAll}{del}{enter}`) + accountDisplay.nameEditionInput().should('not.exist') + accountDisplay.nameLabel().should('be.empty') + accountDisplay.noNameLabel().should('have.text', 'No Name') + }) + }) + + it('can display a local name with identity', () => { + const { publicKey, address } = multisigWithKusamaIdentity + const originalName = 'some name' + + cy.setupAndVisit({ + url: landingPageNetwork('kusama'), + watchedAccounts: [publicKey], + accountNames: { [publicKey]: originalName } + }) + + // show the local name + topMenuItems.multiproxySelectorInputDesktop().should('have.value', originalName) + + multisigPage + .accountHeader() + .should('be.visible') + .within(() => { + accountDisplay.addressLabel().should('contain.text', address.slice(0, 6)) + accountDisplay.nameLabel().should('have.text', originalName) + accountDisplay.identityIcon().should('be.visible') + accountDisplay.subIdentityLabel().should('not.exist') + }) + }) + + it('can edit a local name with identity', () => { + const { publicKey } = multisigWithKusamaIdentity + const originalName = 'some name' + const newName = 'new name' + + cy.setupAndVisit({ + url: landingPageNetwork('kusama'), + watchedAccounts: [publicKey], + accountNames: { [publicKey]: originalName } + }) + + // show the local name + topMenuItems.multiproxySelectorInputDesktop().should('have.value', originalName) + + multisigPage + .accountHeader() + .should('be.visible') + .within(() => { + accountDisplay.nameEditButton().click() + accountDisplay.nameEditionInput().should('have.value', originalName) + accountDisplay.nameEditionInput().type(`{selectAll}${newName}{Enter}`) + }) + + topMenuItems.multiproxySelectorInputDesktop().should('have.value', newName) + }) + + it('can delete local name and see identity and sub', () => { + const { publicKey, identityMain, subIdentity } = multisigWithKusamaIdentity + const originalName = 'some name' + + cy.setupAndVisit({ + url: landingPageNetwork('kusama'), + watchedAccounts: [publicKey], + accountNames: { [publicKey]: originalName } + }) + + // show the local name + topMenuItems.multiproxySelectorInputDesktop().should('have.value', originalName) + + multisigPage + .accountHeader() + .should('be.visible') + .within(() => { + accountDisplay.nameEditButton().click() + accountDisplay.nameEditionInput().should('have.value', originalName) + + // remove the local name shows the identity name and sub + accountDisplay.nameEditionInput().type(`{selectAll}{del}{Enter}`) + accountDisplay.identityIcon().should('be.visible') + accountDisplay.nameLabel().should('have.text', `${identityMain}/${subIdentity}`) + }) + + topMenuItems.multiproxySelectorInputDesktop().should('have.value', identityMain) + }) +}) diff --git a/packages/ui/cypress/tests/network-switch.cy.ts b/packages/ui/cypress/tests/network-switch.cy.ts index 295612d3..13879d0d 100644 --- a/packages/ui/cypress/tests/network-switch.cy.ts +++ b/packages/ui/cypress/tests/network-switch.cy.ts @@ -1,5 +1,5 @@ import { accountDisplay } from '../support/page-objects/components/accountDisplay' -import { settingsPageWatchAccountUrl } from '../fixtures/landingData' +import { getSettingsPageWatchAccountUrl } from '../fixtures/landingData' import { settingsPage } from '../support/page-objects/settingsPage' import { topMenuItems } from '../support/page-objects/topMenuItems' import { watchMultisigs } from '../fixtures/watchAccounts/watchMultisigs' @@ -18,7 +18,7 @@ const kusamaAddress = encodeAddress(multisigAddress, KUSAMA_S58_PREFIX) describe('Network can be switched', () => { it('should switch network using selector', () => { cy.setupAndVisit({ - url: settingsPageWatchAccountUrl, + url: getSettingsPageWatchAccountUrl(), accountNames: { [multisigPublicKey]: multisigName }, watchedAccounts: [multisigPublicKey] }) diff --git a/packages/ui/cypress/tests/walletconnect.cy.ts b/packages/ui/cypress/tests/walletconnect.cy.ts index 6ee987b7..feb1ff60 100644 --- a/packages/ui/cypress/tests/walletconnect.cy.ts +++ b/packages/ui/cypress/tests/walletconnect.cy.ts @@ -1,11 +1,11 @@ -import { settingsPageUrl } from '../fixtures/landingData' +import { getSettingsPageUrl } from '../fixtures/landingData' import { settingsPage } from '../support/page-objects/settingsPage' import { topMenuItems } from '../support/page-objects/topMenuItems' import { testAccounts } from '../fixtures/testAccounts' describe('WalletConnect', () => { it('can see expected wc state when a wallet is not connected', () => { - cy.visit(settingsPageUrl) + cy.visit(getSettingsPageUrl()) topMenuItems.connectButton().should('be.visible') settingsPage.wallectConnectAccordion().should('be.visible').click() // ensure elements are disabled when not connected @@ -22,7 +22,7 @@ describe('WalletConnect', () => { it('can see expected wc state when wallet is connected', () => { cy.setupAndVisit({ - url: settingsPageUrl, + url: getSettingsPageUrl(), extensionConnectionAllowed: true, injectExtensionWithAccounts: [testAccounts['Multisig Member Account 1']] }) @@ -38,7 +38,7 @@ describe('WalletConnect', () => { }) it('can see the wallet connect accordion is collapsed by default', () => { - cy.visit(settingsPageUrl) + cy.visit(getSettingsPageUrl()) topMenuItems.connectButton().should('be.visible') topMenuItems.settingsButton().click() // ensure wallet connect accordion is closed by default diff --git a/packages/ui/cypress/tests/watched-accounts.cy.ts b/packages/ui/cypress/tests/watched-accounts.cy.ts index 67ab8ee0..7f81c065 100644 --- a/packages/ui/cypress/tests/watched-accounts.cy.ts +++ b/packages/ui/cypress/tests/watched-accounts.cy.ts @@ -1,8 +1,8 @@ import { accountDisplay } from '../support/page-objects/components/accountDisplay' import { landingPageUrl, - settingsPageUrl, - settingsPageWatchAccountUrl + getSettingsPageUrl, + getSettingsPageWatchAccountUrl } from '../fixtures/landingData' import { landingPage } from '../support/page-objects/landingPage' import { settingsPage } from '../support/page-objects/settingsPage' @@ -41,7 +41,7 @@ describe('Watched Accounts', () => { it('can remove an account from the watch list', () => { // add an account first - cy.visit(settingsPageWatchAccountUrl) + cy.visit(getSettingsPageWatchAccountUrl()) addWatchAccount(testAccountAddress) // now remove it settingsPage.accountContainer().within(() => { @@ -54,7 +54,7 @@ describe('Watched Accounts', () => { it('can see error when attempting to add same address more than once', () => { // add an account first - cy.visit(settingsPageWatchAccountUrl) + cy.visit(getSettingsPageWatchAccountUrl()) addWatchAccount(testAccountAddress) settingsPage.accountContainer().should('have.length', 1) // attempt to add the same account again @@ -65,7 +65,7 @@ describe('Watched Accounts', () => { }) it('can see error when attempting to add an invalid address', () => { - cy.visit(settingsPageWatchAccountUrl) + cy.visit(getSettingsPageWatchAccountUrl()) addWatchAccount('123') settingsPage.errorLabel().should('be.visible').should('have.text', 'Invalid address') settingsPage.accountContainer().should('have.length', 0) @@ -77,7 +77,7 @@ describe('Watched Accounts', () => { watchMultisigs['multisig-without-pure'] cy.setupAndVisit({ - url: settingsPageWatchAccountUrl, + url: getSettingsPageWatchAccountUrl(), accountNames: { [multisigPublicKey]: multisigName }, @@ -112,7 +112,7 @@ describe('Watched Accounts', () => { const { name: pureName, purePublicKey } = watchMultisigs['multisig-with-pure'] cy.setupAndVisit({ - url: settingsPageWatchAccountUrl, + url: getSettingsPageWatchAccountUrl(), accountNames: { [purePublicKey]: pureName }, @@ -212,7 +212,7 @@ describe('Watched Accounts', () => { const { purePublicKey } = watchMultisigs['multisig-with-pure'] cy.setupAndVisit({ - url: settingsPageUrl, + url: getSettingsPageUrl(), watchedAccounts: [purePublicKey] }) settingsPage.wallectConnectAccordion().click() diff --git a/packages/ui/package.json b/packages/ui/package.json index 7fb347a7..a34db7fd 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -28,7 +28,7 @@ "reactflow": "^11.10.3", "rxjs": "^7.8.1", "typescript": "5.3.3", - "vite": "^5.1.1" + "vite": "^5.1.7" }, "devDependencies": { "@chainsafe/cypress-polkadot-wallet": "^2.1.0", diff --git a/packages/ui/src/components/AccountDisplay/AccountDisplay.tsx b/packages/ui/src/components/AccountDisplay/AccountDisplay.tsx index 87d66e15..fcbeb51b 100644 --- a/packages/ui/src/components/AccountDisplay/AccountDisplay.tsx +++ b/packages/ui/src/components/AccountDisplay/AccountDisplay.tsx @@ -1,15 +1,12 @@ import { Box, Tooltip } from '@mui/material' -import { useCallback, useEffect, useMemo, useState } from 'react' +import { useCallback, useMemo, useState } from 'react' import { styled } from '@mui/material/styles' -import { useAccountNames } from '../../contexts/AccountNamesContext' import { AccountBadge, IconSizeVariant } from '../../types' import { getDisplayAddress } from '../../utils' import IdenticonBadge from '../IdenticonBadge' -import { useApi } from '../../contexts/ApiContext' import IdentityIcon from '../IdentityIcon' import Balance from '../library/Balance' import { useGetEncodedAddress } from '../../hooks/useGetEncodedAddress' -import { useIdentity } from '../../hooks/useIdentity' import { IconButton } from '@mui/material' import { HiOutlinePencilSquare as PencilIcon, @@ -18,6 +15,7 @@ import { import { EditInput } from './EditInput' import { useAccounts } from '../../contexts/AccountsContext' import { copyTextToClipboard } from '../../utils/copyToClipboard' +import { useAccountDisplayInfo } from '../../hooks/useAccountDisplayInfo' const DEFAULT_PLACEMENT = 'top' const DEFAULT_TITLE = 'Address copied!' @@ -45,34 +43,17 @@ const AccountDisplay = ({ canCopy = false }: Props) => { const [isCopyTooltipOpen, setIsCopyTooltipOpen] = useState(false) - const { getNamesWithExtension } = useAccountNames() const { ownAddressList } = useAccounts() - const localName = useMemo(() => getNamesWithExtension(address), [address, getNamesWithExtension]) - const { api } = useApi() - const [identityName, setIdentityName] = useState('') - const [sub, setSub] = useState(null) const getEncodedAddress = useGetEncodedAddress() const encodedAddress = useMemo(() => getEncodedAddress(address), [address, getEncodedAddress]) - const identity = useIdentity(address) - const nameDisplay = useMemo(() => localName || identityName, [identityName, localName]) + const [isEditing, setIsEditing] = useState(false) const isOwnAccount = useMemo(() => ownAddressList.includes(address), [address, ownAddressList]) - useEffect(() => { - if (!identity) return - - if (identity.displayParent && identity.display) { - // when an identity is a sub identity `displayParent` is set - // and `display` get the sub identity - setIdentityName(identity.displayParent) - setSub(identity.display) - } else { - // There should not be a `displayParent` without a `display` - // but we can't be too sure. - setSub('') - setIdentityName(identity.displayParent || identity.display || '') - } - }, [address, api, identity]) + const { displayName, subIdentity, isLocalNameDisplayed, identity, localName, identityName } = + useAccountDisplayInfo({ + address + }) const handleTooltipClose = useCallback(() => setIsCopyTooltipOpen(false), []) const handleCopyAddress = useCallback(() => { @@ -97,7 +78,7 @@ const AccountDisplay = ({ <> {!isEditing && ( - {!!identity && identityName && ( + {!!identityName && !!identity && ( - {nameDisplay} - {!!sub && /{sub}} + {displayName} + {!isLocalNameDisplayed && !!subIdentity && ( + + /{subIdentity} + + )} - {canEdit && !nameDisplay && No Name} + {canEdit && !displayName && ( + No Name + )} {canEdit && !isOwnAccount && ( @@ -178,9 +169,9 @@ const CopyIconWrapperStyled = styled(Box)` justify-content: center; align-items: center; cursor: pointer; - display: none; - opacity: 90%; + opacity: 0; color: ${({ theme }) => theme.custom.gray[800]}; + transition: opacity 250ms; ` const EditIconButtonStyled = styled(IconButton)` @@ -241,7 +232,7 @@ const AddressStyled = styled('div')` position: relative; &:hover > .copyIcon { - display: flex; + opacity: 90%; } ` diff --git a/packages/ui/src/components/AccountDisplay/EditInput.tsx b/packages/ui/src/components/AccountDisplay/EditInput.tsx index 1246098a..2d556de9 100644 --- a/packages/ui/src/components/AccountDisplay/EditInput.tsx +++ b/packages/ui/src/components/AccountDisplay/EditInput.tsx @@ -57,11 +57,13 @@ export const EditInput = ({ onChange={onNameChange} value={name} inputRef={inputRef} + data-cy="input-name-edition" /> @@ -69,6 +71,7 @@ export const EditInput = ({ aria-label="close" onClick={onClose} className={buttonSize !== 'large' ? 'small' : ''} + data-cy="button-edition-cancel" > diff --git a/packages/ui/src/components/IdentityIcon.tsx b/packages/ui/src/components/IdentityIcon.tsx index a0d5cedb..decb1854 100644 --- a/packages/ui/src/components/IdentityIcon.tsx +++ b/packages/ui/src/components/IdentityIcon.tsx @@ -90,6 +90,7 @@ const IdentityIcon = ({ className, identity }: Props) => { {isGood ? ( diff --git a/packages/ui/src/components/select/MultiProxySelection.tsx b/packages/ui/src/components/select/MultiProxySelection.tsx index 2af417bf..cf12c302 100644 --- a/packages/ui/src/components/select/MultiProxySelection.tsx +++ b/packages/ui/src/components/select/MultiProxySelection.tsx @@ -1,8 +1,8 @@ import { Box, InputAdornment } from '@mui/material' -import React, { useCallback, useMemo, useRef } from 'react' +import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react' import { styled } from '@mui/material/styles' import { createFilterOptions } from '@mui/material/Autocomplete' -import { isMultiProxy, MultiProxy, useMultiProxy } from '../../contexts/MultiProxyContext' +import { useMultiProxy } from '../../contexts/MultiProxyContext' import AccountDisplay from '../AccountDisplay/AccountDisplay' import IdenticonBadge from '../IdenticonBadge' import { useAccountNames } from '../../contexts/AccountNamesContext' @@ -10,22 +10,28 @@ import { AccountBadge } from '../../types' import { Autocomplete } from '../library' import { AutocompleteRenderInputParams } from '@mui/material/Autocomplete/Autocomplete' import TextFieldLargeStyled from '../library/TextFieldLargeStyled' +import { useGetIdentity } from '../../hooks/useGetIdentity' +import { getIdentityName } from '../../utils/getIdentityName' +import { getDisplayName } from '../../utils/getDisplayName' interface Props { className?: string testId?: string } -const getDisplayAddress = (option?: MultiProxy) => - option?.proxy ? option?.proxy : option?.multisigs[0].address +interface Option { + address: string + isPure: boolean + localName: string + identityName: string +} + +const emptyOption = { address: '', isPure: false, localName: '', identityName: '' } as Option -const isOptionEqualToValue = (option?: MultiProxy, value?: MultiProxy) => { +const isOptionEqualToValue = (option?: Option, value?: Option) => { if (!option || !value) return false - if (!!option.proxy || !!value.proxy) { - return option.proxy === value.proxy - } - return option.multisigs[0].address === value.multisigs[0].address + return option.address === value.address } const MultiProxySelection = ({ className, testId = '' }: Props) => { @@ -37,44 +43,97 @@ const MultiProxySelection = ({ className, testId = '' }: Props) => { isLoading, canFindMultiProxyFromUrl } = useMultiProxy() + const [options, setOptions] = useState([]) const isSelectedProxy = useMemo(() => !!selectedMultiProxy?.proxy, [selectedMultiProxy]) - // We only support one multisigs if they have no proxy const addressToShow = useMemo( + // We only support one multisigs if they have no proxy () => selectedMultiProxy?.proxy || selectedMultiProxy?.multisigs[0].address, [selectedMultiProxy] ) + + const selectedOption = useMemo(() => { + let res: Option = emptyOption + + if (!canFindMultiProxyFromUrl) return res + + if (!selectedMultiProxy) return options[0] + + if (selectedMultiProxy?.proxy) { + res = options.find((option) => option.address === selectedMultiProxy.proxy) || emptyOption + } else { + res = + options.find((option) => option.address === selectedMultiProxy?.multisigs[0].address) || + emptyOption + } + + return res + }, [canFindMultiProxyFromUrl, options, selectedMultiProxy]) const { accountNames } = useAccountNames() const filterOptions = createFilterOptions({ ignoreCase: true, - stringify: (option: typeof selectedMultiProxy) => { - const displayAddress = getDisplayAddress(option) || '' - return `${option?.proxy}${option?.multisigs[0].address}${accountNames[displayAddress]}` || '' + stringify: (option: Option) => { + const { localName, identityName, address } = option + return `${localName}${identityName}${address}` || '' } }) + const getIdentity = useGetIdentity() - const getOptionLabel = useCallback( - (option?: NonNullable): string => { - // We only support one multisigs if they have no proxy + useEffect(() => { + const addressList = multiProxyList.map((mp) => { + const isPure = !!mp.proxy - if (isMultiProxy(option)) { - const addressToSearch = option?.proxy || option?.multisigs[0].address - const name = !!addressToSearch && accountNames[addressToSearch] - return name || (addressToSearch as string) + return { + isPure, + address: isPure ? mp.proxy : mp.multisigs[0].address } + }) - return '' - }, - [accountNames] - ) + const allIdentityNames = Promise.all( + addressList.map(({ address }) => { + return getIdentity(address!) + }) + ) + + allIdentityNames + .then((allNames) => { + const optionList = addressList.map( + (info, index) => + ({ + ...info, + localName: accountNames[info.address!], + identityName: getIdentityName(allNames[index]).identityName + }) as Option + ) + + setOptions(optionList) + }) + .catch(console.error) + }, [accountNames, getIdentity, multiProxyList]) + + const getOptionLabel = useCallback((option?: NonNullable