From db744b5f50d808481bf25a8bb1947bae4cb861a1 Mon Sep 17 00:00:00 2001 From: "G. Kami Ekbatanifard" <46442452+Nick-1979@users.noreply.github.com> Date: Sat, 26 Oct 2024 15:35:17 +0330 Subject: [PATCH] refactor: handle account icon's tooltip translations (#1607) --- .../src/components/AccountIcons.tsx | 37 ++++--- .../components/AccountIconsFs.tsx | 97 +++---------------- .../extension-polkagate/src/hooks/index.ts | 3 + .../src/hooks/useHasIdentityTooltipText.ts | 27 ++++++ .../src/hooks/useHasProxyTooltipText.ts | 35 +++++++ .../src/hooks/useIsRecoverableTooltipText.ts | 53 ++++++++++ .../src/hooks/useProxies.ts | 25 ++--- .../src/popup/home/AccountPreview.tsx | 17 +--- 8 files changed, 166 insertions(+), 128 deletions(-) create mode 100644 packages/extension-polkagate/src/hooks/useHasIdentityTooltipText.ts create mode 100644 packages/extension-polkagate/src/hooks/useHasProxyTooltipText.ts create mode 100644 packages/extension-polkagate/src/hooks/useIsRecoverableTooltipText.ts diff --git a/packages/extension-polkagate/src/components/AccountIcons.tsx b/packages/extension-polkagate/src/components/AccountIcons.tsx index 2f89d5722..6e4a004ae 100644 --- a/packages/extension-polkagate/src/components/AccountIcons.tsx +++ b/packages/extension-polkagate/src/components/AccountIcons.tsx @@ -3,43 +3,39 @@ /* eslint-disable react/jsx-max-props-per-line */ -import type { Chain } from '@polkadot/extension-chains/types'; import type { IconTheme } from '@polkadot/react-identicon/types'; -import type { Proxy } from '../util/types'; import { faShieldHalved, faSitemap } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { Grid, IconButton, useTheme } from '@mui/material'; import React, { useCallback, useContext } from 'react'; -import { useAnimateOnce, useTranslation } from '../hooks'; +import { useAnimateOnce, useHasProxyTooltipText, useInfo, useIsRecoverableTooltipText } from '../hooks'; import { windowOpen } from '../messaging'; import { PROXY_CHAINS } from '../util/constants'; -import { getSubstrateAddress } from '../util/utils'; import { ActionContext } from './contexts'; import Identicon from './Identicon'; import { Infotip } from '.'; interface Props { - chain: Chain | null | undefined; - formatted: string | undefined; + address: string | undefined; identiconTheme: IconTheme; isSubId: boolean; judgements?: RegExpMatchArray | null | undefined; prefix?: number; - proxies: Proxy[] | undefined; - recoverable?: boolean; } -export default function AccountIcons ({ chain, formatted, identiconTheme, isSubId, judgements, prefix, proxies, recoverable = false }: Props): React.ReactElement { +function AccountIcons ({ address, identiconTheme, isSubId, judgements, prefix }: Props): React.ReactElement { const theme = useTheme(); - const { t } = useTranslation(); const onAction = useContext(ActionContext); - const address = getSubstrateAddress(formatted); + const { chain, formatted } = useInfo(address); - const shakeProxy = useAnimateOnce(!!proxies?.length); - const shakeShield = useAnimateOnce(recoverable); + const { hasProxy, proxyTooltipTxt } = useHasProxyTooltipText(address); + const { isRecoverable, recoverableToolTipTxt } = useIsRecoverableTooltipText(address); + + const shakeProxy = useAnimateOnce(hasProxy); + const shakeShield = useAnimateOnce(isRecoverable); const openManageProxy = useCallback(() => { address && chain && PROXY_CHAINS.includes(chain.genesisHash ?? '') && onAction(`/manageProxies/${address}`); @@ -50,7 +46,7 @@ export default function AccountIcons ({ chain, formatted, identiconTheme, isSubI }, [address]); return ( - + - + + sx={{ height: '15px', width: '15px' }} + > - + ); } + +export default React.memo(AccountIcons); diff --git a/packages/extension-polkagate/src/fullscreen/accountDetails/components/AccountIconsFs.tsx b/packages/extension-polkagate/src/fullscreen/accountDetails/components/AccountIconsFs.tsx index fc2fd394c..9316dfdc4 100644 --- a/packages/extension-polkagate/src/fullscreen/accountDetails/components/AccountIconsFs.tsx +++ b/packages/extension-polkagate/src/fullscreen/accountDetails/components/AccountIconsFs.tsx @@ -4,20 +4,18 @@ /* eslint-disable react/jsx-max-props-per-line */ import type { DeriveAccountInfo } from '@polkadot/api-derive/types'; -import type { Option, u128, Vec } from '@polkadot/types'; //@ts-ignore import type { PalletProxyAnnouncement, PalletRecoveryActiveRecovery } from '@polkadot/types/lookup'; -import type { Proxy } from '../../../util/types'; import { faChain, faCheckCircle, faCircleInfo, faShieldHalved, faSitemap } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { Grid, IconButton, useTheme } from '@mui/material'; -import React, { useCallback, useContext, useEffect, useMemo, useState } from 'react'; +import React, { useCallback, useContext, useEffect, useState } from 'react'; import { ActionContext, Infotip } from '../../../components'; -import { useAnimateOnce, useInfo, useTranslation } from '../../../hooks'; +import { useAnimateOnce, useHasIdentityTooltipText, useHasProxyTooltipText, useInfo, useIsRecoverableTooltipText } from '../../../hooks'; import { windowOpen } from '../../../messaging'; -import { IDENTITY_CHAINS, PROXY_CHAINS, SOCIAL_RECOVERY_CHAINS } from '../../../util/constants'; +import { IDENTITY_CHAINS } from '../../../util/constants'; interface AddressDetailsProps { address: string | undefined; @@ -25,71 +23,23 @@ interface AddressDetailsProps { } function AccountIconsFs ({ accountInfo, address }: AddressDetailsProps): React.ReactElement { - const { t } = useTranslation(); const theme = useTheme(); const onAction = useContext(ActionContext); const { account, api, chain, formatted } = useInfo(address); const [hasID, setHasID] = useState(); - const [isRecoverable, setIsRecoverable] = useState(); - const [hasProxy, setHasProxy] = useState(); + + const { isRecoverable, recoverableToolTipTxt } = useIsRecoverableTooltipText(address); + const { hasProxy, proxyTooltipTxt } = useHasProxyTooltipText(address); + const identityToolTipTxt = useHasIdentityTooltipText(address, hasID); const shakeProxy = useAnimateOnce(hasProxy); - const shakeIdentity = useAnimateOnce(hasID); const shakeShield = useAnimateOnce(isRecoverable); - - const identityToolTipTxt = useMemo(() => { - if (!chain) { - return 'Account is in Any Chain mode'; - } - - switch (hasID) { - case true: - return 'Has Identity'; - case false: - return 'No Identity'; - default: - return 'Checking'; - } - }, [chain, hasID]); - - const recoverableToolTipTxt = useMemo(() => { - if (!chain) { - return 'Account is in Any Chain mode'; - } - - switch (isRecoverable) { - case true: - return 'Recoverable'; - case false: - return 'Not Recoverable'; - default: - return 'Checking'; - } - }, [chain, isRecoverable]); - - const proxyTooltipTxt = useMemo(() => { - if (!chain) { - return 'Account is in Any Chain mode'; - } - - switch (hasProxy) { - case true: - return 'Has Proxy'; - case false: - return 'No Proxy'; - default: - return 'Checking'; - } - }, [chain, hasProxy]); + const shakeIdentity = useAnimateOnce(hasID); useEffect((): void => { - setHasID(undefined); - setIsRecoverable(undefined); - setHasProxy(undefined); - - if (!api || !address || !account?.genesisHash || api.genesisHash.toHex() !== account.genesisHash) { + if (!api || !formatted || !account?.genesisHash || api.genesisHash.toHex() !== account.genesisHash) { return; } @@ -98,28 +48,7 @@ function AccountIconsFs ({ accountInfo, address }: AddressDetailsProps): React.R } else { setHasID(false); } - - if (api.query?.['recovery'] && SOCIAL_RECOVERY_CHAINS.includes(account.genesisHash)) { - api.query['recovery']['recoverable'](formatted) - .then((r) => - setIsRecoverable((r as Option).isSome)) - .catch(console.error); - } else { - setIsRecoverable(false); - } - - if (api.query?.['proxy'] && PROXY_CHAINS.includes(account.genesisHash)) { - api.query['proxy']['proxies'](formatted) - .then((p) => { - const _p = p as unknown as [Vec, u128]; - const fetchedProxies = JSON.parse(JSON.stringify(_p[0])) as unknown as Proxy[]; - - setHasProxy(fetchedProxies.length > 0); - }).catch(console.error); - } else { - setHasProxy(false); - } - }, [api, address, formatted, account?.genesisHash, accountInfo]); + }, [api, formatted, account?.genesisHash, accountInfo]); const openIdentity = useCallback(() => { address && chain && windowOpen(`/manageIdentity/${address}`).catch(console.error); @@ -136,7 +65,7 @@ function AccountIconsFs ({ accountInfo, address }: AddressDetailsProps): React.R return ( - + {hasID ? accountInfo?.identity?.displayParent ? - + - + { + if (!chain) { + return t('Account is in Any Chain mode'); + } + + switch (hasID) { + case true: + return t('Has identity'); + case false: + return t('No identity'); + default: + return t('Checking'); + } + }, [chain, hasID, t]); +} diff --git a/packages/extension-polkagate/src/hooks/useHasProxyTooltipText.ts b/packages/extension-polkagate/src/hooks/useHasProxyTooltipText.ts new file mode 100644 index 000000000..f401fa490 --- /dev/null +++ b/packages/extension-polkagate/src/hooks/useHasProxyTooltipText.ts @@ -0,0 +1,35 @@ +// Copyright 2019-2024 @polkadot/extension-polkagate authors & contributors +// SPDX-License-Identifier: Apache-2.0 + +import { useMemo } from 'react'; + +import { useInfo, useProxies, useTranslation } from '.'; + +export default function useHasProxyTooltipText (address: string | undefined): { hasProxy: boolean | undefined; proxyTooltipTxt: string; } { + const { t } = useTranslation(); + + const { api, chain, formatted } = useInfo(address); + + const proxies = useProxies(api, formatted); + const hasProxy = proxies ? !!proxies.length : undefined; + + const proxyTooltipTxt = useMemo(() => { + if (!chain) { + return t('Account is in Any Chain mode'); + } + + switch (hasProxy) { + case true: + return t('Has proxy'); + case false: + return t('No proxy'); + default: + return t('Checking'); + } + }, [chain, hasProxy, t]); + + return { + hasProxy, + proxyTooltipTxt + }; +} diff --git a/packages/extension-polkagate/src/hooks/useIsRecoverableTooltipText.ts b/packages/extension-polkagate/src/hooks/useIsRecoverableTooltipText.ts new file mode 100644 index 000000000..52d74fac6 --- /dev/null +++ b/packages/extension-polkagate/src/hooks/useIsRecoverableTooltipText.ts @@ -0,0 +1,53 @@ +// Copyright 2019-2024 @polkadot/extension-polkagate authors & contributors +// SPDX-License-Identifier: Apache-2.0 + +import type { Option } from '@polkadot/types'; +//@ts-ignore +import type { PalletRecoveryRecoveryConfig } from '@polkadot/types/lookup'; + +import { useEffect, useMemo, useState } from 'react'; + +import { useInfo, useTranslation } from '.'; + +export default function useIsRecoverableTooltipText (address: string | undefined): { isRecoverable: boolean | undefined; recoverableToolTipTxt: string; } { + const { t } = useTranslation(); + + const { api, chain, formatted } = useInfo(address); + + const [isRecoverable, setRecoverable] = useState(); + + useEffect((): void => { + if (!api || !formatted) { + return; + } + + api.query?.['recovery']?.['recoverable'](formatted) + .then((result) => { + const recoveryOpt = result as Option + + setRecoverable(!!recoveryOpt.isSome); + }) + .catch(console.error); + }, [api, formatted]); + + const recoverableToolTipTxt = useMemo(() => { + if (!chain) { + return t('Account is in Any Chain mode'); + ; + } + + switch (isRecoverable) { + case true: + return t('Recoverable'); + case false: + return t('Not recoverable'); + default: + return t('Checking'); + } + }, [chain, isRecoverable, t]); + + return { + isRecoverable, + recoverableToolTipTxt + }; +} diff --git a/packages/extension-polkagate/src/hooks/useProxies.ts b/packages/extension-polkagate/src/hooks/useProxies.ts index 18d7451e2..97245cc0f 100644 --- a/packages/extension-polkagate/src/hooks/useProxies.ts +++ b/packages/extension-polkagate/src/hooks/useProxies.ts @@ -1,31 +1,34 @@ // Copyright 2019-2024 @polkadot/extension-polkagate authors & contributors // SPDX-License-Identifier: Apache-2.0 -// @ts-nocheck -/** +/** * @description - * this hook returns a proxied proxies in non formatted style + * this hook returns a proxied proxies in non formatted style * */ -import { useCallback, useContext, useEffect, useState } from 'react'; - -import { ApiPromise } from '@polkadot/api'; +import type { ApiPromise } from '@polkadot/api'; import type { AccountId } from '@polkadot/types/interfaces/runtime'; +//@ts-ignore +import type { PalletProxyProxyDefinition } from '@polkadot/types/lookup'; +import type { u128, Vec } from '@polkadot/types-codec'; +import type { Proxy, ProxyTypes } from '../util/types'; + +import { useCallback, useContext, useEffect, useState } from 'react'; import { AccountContext } from '../components'; -import type { Proxy, ProxyTypes } from '../util/types'; import { getSubstrateAddress } from '../util/utils'; -export default function useProxies(api: ApiPromise | undefined, proxiedAddress: string | AccountId | undefined | null, onlyAvailableWithTypes?: ProxyTypes[]): Proxy[] | undefined { +export default function useProxies (api: ApiPromise | undefined, proxiedAddress: string | AccountId | undefined | null, onlyAvailableWithTypes?: ProxyTypes[]): Proxy[] | undefined { const [proxies, setProxies] = useState(); const [proxiesWithAvailability, setProxiesWithAvailability] = useState(); const { accounts } = useContext(AccountContext); const getProxies = useCallback(() => { if (!proxies && api) { - api.query.proxy?.proxies(proxiedAddress) - .then((p) => { - const fetchedProxies = JSON.parse(JSON.stringify(p[0])) as unknown as Proxy[]; + api.query['proxy']?.['proxies'](proxiedAddress) + .then((result) => { + const typedResult = result as unknown as [Vec, u128]; + const fetchedProxies = JSON.parse(JSON.stringify(typedResult[0])) as unknown as Proxy[]; if (onlyAvailableWithTypes?.length && fetchedProxies) { return setProxies(fetchedProxies.filter((p) => onlyAvailableWithTypes.includes(p.proxyType))); diff --git a/packages/extension-polkagate/src/popup/home/AccountPreview.tsx b/packages/extension-polkagate/src/popup/home/AccountPreview.tsx index a47b2ef4b..00d0916d7 100644 --- a/packages/extension-polkagate/src/popup/home/AccountPreview.tsx +++ b/packages/extension-polkagate/src/popup/home/AccountPreview.tsx @@ -12,7 +12,7 @@ import React, { useCallback, useContext, useEffect, useState } from 'react'; import { ActionContext } from '../../components'; import AccountFeatures from '../../components/AccountFeatures'; import AccountIcons from '../../components/AccountIcons'; -import { useInfo, useMyAccountIdentity, useProxies } from '../../hooks'; +import { useInfo, useMyAccountIdentity } from '../../hooks'; import useIsExtensionPopup from '../../hooks/useIsExtensionPopup'; import { showAccount } from '../../messaging'; import { AccountMenu } from '../../partials'; @@ -22,7 +22,6 @@ import AccountDetail from './AccountDetail'; export interface Props { actions?: React.ReactNode; address: string; - // children?: React.ReactNode; isExternal?: boolean | null; isHardware?: boolean | null; isHidden?: boolean; @@ -38,21 +37,14 @@ export interface Props { export default function AccountPreview ({ address, hideNumbers, isHidden, name, quickActionOpen, setQuickActionOpen, toggleActions, type }: Props): React.ReactElement { const onExtension = useIsExtensionPopup(); - const { api, chain, formatted } = useInfo(address); + const { chain, formatted } = useInfo(address); const onAction = useContext(ActionContext); - const proxies = useProxies(api, formatted); const identity = useMyAccountIdentity(address); const [showAccountMenu, setShowAccountMenu] = useState(false); - const [recoverable, setRecoverable] = useState(); const _judgement = identity && JSON.stringify(identity.judgements).match(/reasonable|knownGood/gi); - useEffect((): void => { - api?.query?.['recovery']?.['recoverable'](formatted) - .then((r: any) => setRecoverable(!!r.isSome)).catch(console.error); - }, [api, formatted]); - useEffect((): void => { setShowAccountMenu(false); }, [toggleActions]); @@ -83,14 +75,11 @@ export default function AccountPreview ({ address, hideNumbers, isHidden, name, return (