Skip to content

Commit

Permalink
refactor: handle account icon's tooltip translations (#1607)
Browse files Browse the repository at this point in the history
  • Loading branch information
Nick-1979 authored Oct 26, 2024
1 parent b45343f commit db744b5
Show file tree
Hide file tree
Showing 8 changed files with 166 additions and 128 deletions.
37 changes: 18 additions & 19 deletions packages/extension-polkagate/src/components/AccountIcons.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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<Props> {
function AccountIcons ({ address, identiconTheme, isSubId, judgements, prefix }: Props): React.ReactElement<Props> {
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}`);
Expand All @@ -50,7 +46,7 @@ export default function AccountIcons ({ chain, formatted, identiconTheme, isSubI
}, [address]);

return (
<Grid container direction='column' sx={{ width: '17%', ml: '8px' }}>
<Grid container direction='column' sx={{ ml: '8px', width: '17%' }}>
<Grid item m='auto' width='fit-content'>
<Identicon
iconTheme={identiconTheme}
Expand All @@ -63,12 +59,13 @@ export default function AccountIcons ({ chain, formatted, identiconTheme, isSubI
</Grid>
<Grid container direction='row' item justifyContent='center'>
<Grid item>
<Infotip placement='bottom-start' text={t('Is recoverable')}>
<Infotip placement='bottom-start' text={recoverableToolTipTxt}>
<IconButton
onClick={openSocialRecovery}
sx={{ height: '15px', width: '15px' }}>
sx={{ height: '15px', width: '15px' }}
>
<FontAwesomeIcon
color={recoverable ? theme.palette.success.main : theme.palette.action.disabledBackground}
color={isRecoverable ? theme.palette.success.main : theme.palette.action.disabledBackground}
fontSize='13px'
icon={faShieldHalved}
shake={shakeShield}
Expand All @@ -77,10 +74,10 @@ export default function AccountIcons ({ chain, formatted, identiconTheme, isSubI
</Infotip>
</Grid>
<Grid item>
<Infotip placement='bottom-end' text={t('Has proxy')}>
<Infotip placement='bottom-end' text={proxyTooltipTxt}>
<IconButton onClick={openManageProxy} sx={{ height: '15px', width: '15px' }}>
<FontAwesomeIcon
color={proxies?.length ? theme.palette.success.main : theme.palette.action.disabledBackground}
color={hasProxy ? theme.palette.success.main : theme.palette.action.disabledBackground}
fontSize='13px'
icon={faSitemap}
shake={shakeProxy}
Expand All @@ -92,3 +89,5 @@ export default function AccountIcons ({ chain, formatted, identiconTheme, isSubI
</Grid>
);
}

export default React.memo(AccountIcons);
Original file line number Diff line number Diff line change
Expand Up @@ -4,92 +4,42 @@
/* 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;
accountInfo: DeriveAccountInfo | undefined | null
}

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<boolean | undefined>();
const [isRecoverable, setIsRecoverable] = useState<boolean | undefined>();
const [hasProxy, setHasProxy] = useState<boolean | undefined>();

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;
}

Expand All @@ -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<PalletRecoveryActiveRecovery>).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<PalletProxyAnnouncement>, 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);
Expand All @@ -136,7 +65,7 @@ function AccountIconsFs ({ accountInfo, address }: AddressDetailsProps): React.R
return (
<Grid alignItems='center' container direction='column' display='grid' height='72px' item justifyContent='center' justifyItems='center' width='fit-content'>
<Grid item onClick={openIdentity} sx={{ cursor: 'pointer', height: '24px', m: 'auto', p: '2px', width: 'fit-content' }}>
<Infotip placement='right' text={t(identityToolTipTxt)}>
<Infotip placement='right' text={identityToolTipTxt}>
{hasID
? accountInfo?.identity?.displayParent
? <FontAwesomeIcon
Expand All @@ -157,7 +86,7 @@ function AccountIconsFs ({ accountInfo, address }: AddressDetailsProps): React.R
</Infotip>
</Grid>
<Grid height='24px' item my='1px' width='24px'>
<Infotip placement='right' text={t(recoverableToolTipTxt)}>
<Infotip placement='right' text={recoverableToolTipTxt}>
<IconButton
onClick={openSocialRecovery}
sx={{ height: '24px', width: '24px' }}
Expand All @@ -171,7 +100,7 @@ function AccountIconsFs ({ accountInfo, address }: AddressDetailsProps): React.R
</Infotip>
</Grid>
<Grid height='24px' item width='fit-content'>
<Infotip placement='right' text={t(proxyTooltipTxt)}>
<Infotip placement='right' text={proxyTooltipTxt}>
<IconButton onClick={openManageProxy} sx={{ height: '16px', width: '16px' }}>
<FontAwesomeIcon
icon={faSitemap}
Expand Down
3 changes: 3 additions & 0 deletions packages/extension-polkagate/src/hooks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,12 +49,15 @@ export { useGenericLedger } from './useGenericLedger';
export { default as useGenesisHash } from './useGenesisHash';
export { default as useGenesisHashOptions } from './useGenesisHashOptions';
export { default as useHasDelegated } from './useHasDelegated';
export { default as useHasIdentityTooltipText } from './useHasIdentityTooltipText';
export { default as useHasProxyTooltipText } from './useHasProxyTooltipText';
export { default as useIdentity } from './useIdentity';
export { default as useInfo } from './useInfo';
export { default as useIsExposed } from './useIsExposed';
export { default as useIsExtensionPopup } from './useIsExtensionPopup';
export { default as useIsLoginEnabled } from './useIsLoginEnabled';
export { default as useIsMounted } from './useIsMounted';
export { default as useIsRecoverableTooltipText } from './useIsRecoverableTooltipText';
export { default as useIsTestnetEnabled } from './useIsTestnetEnabled';
export { default as useIsValidator } from './useIsValidator';
export { useLedger } from './useLedger';
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Copyright 2019-2024 @polkadot/extension-polkagate authors & contributors
// SPDX-License-Identifier: Apache-2.0

import { useMemo } from 'react';

import { useChain, useTranslation } from '.';

export default function useHasIdentityTooltipText (address: string | undefined, hasID: boolean | undefined): string {
const { t } = useTranslation();

const chain = useChain(address);

return useMemo(() => {
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]);
}
35 changes: 35 additions & 0 deletions packages/extension-polkagate/src/hooks/useHasProxyTooltipText.ts
Original file line number Diff line number Diff line change
@@ -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
};
}
Original file line number Diff line number Diff line change
@@ -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<boolean | undefined>();

useEffect((): void => {
if (!api || !formatted) {
return;
}

api.query?.['recovery']?.['recoverable'](formatted)
.then((result) => {
const recoveryOpt = result as Option<PalletRecoveryRecoveryConfig>

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
};
}
Loading

0 comments on commit db744b5

Please sign in to comment.