Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: handle account icon's tooltip translations #1607

Merged
merged 5 commits into from
Oct 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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> {
Nick-1979 marked this conversation as resolved.
Show resolved Hide resolved
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);
Nick-1979 marked this conversation as resolved.
Show resolved Hide resolved

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) {
Nick-1979 marked this conversation as resolved.
Show resolved Hide resolved
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]);
Nick-1979 marked this conversation as resolved.
Show resolved Hide resolved

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
Loading