Skip to content

Commit

Permalink
feat: add account icon theme options (#1612)
Browse files Browse the repository at this point in the history
  • Loading branch information
Nick-1979 authored Oct 29, 2024
1 parent a496f1d commit e1b3570
Show file tree
Hide file tree
Showing 12 changed files with 256 additions and 142 deletions.
8 changes: 6 additions & 2 deletions packages/extension-polkagate/src/components/Identicon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@ import type { IconTheme } from '@polkadot/react-identicon/types';
import type { AccountId } from '@polkadot/types/interfaces/runtime';

import { CheckCircleOutline as CheckIcon, InsertLinkRounded as LinkIcon } from '@mui/icons-material';
import React from 'react';
import React, { useContext } from 'react';
import styled from 'styled-components';

import Icon from '@polkadot/react-identicon';

import { AccountIconThemeContext } from '.';

interface Props {
className?: string;
iconTheme?: IconTheme;
Expand All @@ -22,6 +24,8 @@ interface Props {
}

function Identicon ({ className, iconTheme, isSubId, judgement, onCopy, prefix, size, value }: Props): React.ReactElement<Props> {
const { accountIconTheme } = useContext(AccountIconThemeContext);

return (
<div style={{ position: 'relative' }}>
<div className={className}>
Expand All @@ -30,7 +34,7 @@ function Identicon ({ className, iconTheme, isSubId, judgement, onCopy, prefix,
onCopy={onCopy}
prefix={prefix}
size={size}
theme={iconTheme}
theme={ accountIconTheme || iconTheme}
value={value}
/>
</div>
Expand Down
94 changes: 60 additions & 34 deletions packages/extension-polkagate/src/components/Select2.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,16 @@
/* eslint-disable react/jsx-max-props-per-line */

import type { SelectChangeEvent } from '@mui/material';
import type { IconTheme } from '@polkadot/react-identicon/types';
import type { DropdownOption } from '../util/types';

import { CircularProgress, FormControl, Grid, InputBase, MenuItem, Select, Typography } from '@mui/material';
import { styled, useTheme } from '@mui/material/styles';
import React, { useCallback, useLayoutEffect, useState } from 'react';

import Icon from '@polkadot/react-identicon';

import { DEMO_ACCOUNT } from '../util/constants';
import { sanitizeChainName } from '../util/utils';
import ChainLogo from './ChainLogo';

Expand All @@ -19,7 +23,13 @@ interface Props {
onChange?: (v: number | string) => void;
options: DropdownOption[];
label: string;
labelFontSize?: string;
labelPaddingLeft?: string;
textFontSize?: string;
labelAlignment?: string;
isDisabled?: boolean;
isIdenticon?: boolean;
rounded?: boolean;
showLogo?: boolean;
showIcons?: boolean;
_mt?: string | number;
Expand Down Expand Up @@ -49,7 +59,7 @@ const BootstrapInput = styled(InputBase)<{ isDisabled?: boolean }>(({ isDisabled
}
}));

function CustomizedSelect ({ _mt = 0, defaultValue, disabledItems, isDisabled = false, isItemsLoading, label, onChange, options, showIcons = true, showLogo = false, value }: Props) {
function CustomizedSelect ({ _mt = 0, defaultValue, disabledItems, isDisabled = false, isIdenticon, isItemsLoading, label, labelAlignment, labelFontSize = '10px', labelPaddingLeft = '5px', onChange, options, rounded = true, showIcons = true, showLogo = false, textFontSize = '14px', value }: Props) {
const theme = useTheme();
const [showMenu, setShowMenu] = useState<boolean>(false);
const [selectedValue, setSelectedValue] = useState<string>();
Expand All @@ -70,15 +80,15 @@ function CustomizedSelect ({ _mt = 0, defaultValue, disabledItems, isDisabled =

return (
<FormControl disabled={isDisabled} sx={{ mt: `${_mt}`, width: '100%' }} variant='standard'>
<Typography sx={{ fontSize: '10px', paddingLeft: '5px' }}>
<Typography sx={{ alignSelf: labelAlignment, fontSize: labelFontSize, paddingLeft: labelPaddingLeft }}>
{label}
</Typography>
{selectedValue &&
<Select
// eslint-disable-next-line react/jsx-no-bind
IconComponent={
isItemsLoading
? () => <CircularProgress size={20} sx={{ color: `${theme.palette.secondary.light}`, position: 'absolute', right: '5px' }} />
? () => <CircularProgress size={20} sx={{ color: `${theme.palette.secondary.light}`, position: 'absolute', right: '5px' }} />
: undefined
}
MenuProps={{
Expand Down Expand Up @@ -120,11 +130,45 @@ function CustomizedSelect ({ _mt = 0, defaultValue, disabledItems, isDisabled =
onChange={_onChange}
onClick={toggleMenu}
open={options?.length !== 1 && showMenu} // do not open select when page is loading , or options has just one item
// eslint-disable-next-line react/jsx-no-bind
renderValue={(v) => {
let textToShow = options.find((option) => v === option.value || v === option.text || String(v) === String(option.value))?.text?.split(/\s*\(/)[0];

if (textToShow?.split(':')?.[1]) {
textToShow = textToShow?.split(':')[1]?.trim();
}

return (
<Grid container height={'30px'} justifyContent='flex-start'>
{showIcons && textToShow && textToShow !== 'Allow use on any chain' &&
<Grid alignItems='center' container item width='fit-content'>
{isIdenticon
? <Icon
className='icon'
size={20}
theme={v as IconTheme}
value={DEMO_ACCOUNT}
/>
: <ChainLogo chainName={chainName(textToShow)} genesisHash={v} size={19.8} />
}
</Grid>
}
<Grid alignItems='center' container item justifyContent='flex-start' pl='6px' width='fit-content'>
<Typography fontSize={textFontSize} fontWeight={300}>
{textToShow}
</Typography>
</Grid>
</Grid>
);
}}
sx={{
'.MuiSelect-icon': {
display: options?.length && options.length === 1 ? 'none' : 'block'
},
'> #selectChain': {
border: '1px solid',
borderColor: 'secondary.light',
borderRadius: '20px',
borderRadius: rounded ? '20px' : '5px',
fontSize: '14px',
height: '29px',
lineHeight: '30px',
Expand All @@ -141,35 +185,9 @@ function CustomizedSelect ({ _mt = 0, defaultValue, disabledItems, isDisabled =
fontSize: '30px'
},
bgcolor: isDisabled ? 'primary.contrastText' : 'transparent',
width: '100%',
'.MuiSelect-icon': {
display: options?.length && options.length === 1 ? 'none' : 'block'
}
width: '100%'
}}
value={selectedValue} // Assuming selectedValue is a state variable
// eslint-disable-next-line react/jsx-no-bind
renderValue={(v) => {
let textToShow = options.find((option) => v === option.value || v === option.text || String(v) === String(option.value))?.text?.split(/\s*\(/)[0];

if (textToShow?.split(':')?.[1]) {
textToShow = textToShow?.split(':')[1]?.trim();
}

return (
<Grid container height={'30px'} justifyContent='flex-start'>
{showIcons && textToShow && textToShow !== 'Allow use on any chain' &&
<Grid alignItems='center' container item width='fit-content'>
<ChainLogo chainName={chainName(textToShow)} genesisHash={v} size={19.8} />
</Grid>
}
<Grid alignItems='center' container item justifyContent='flex-start' pl='6px' width='fit-content'>
<Typography fontSize='14px' fontWeight={300}>
{textToShow}
</Typography>
</Grid>
</Grid>
);
}}
>
{options.map(({ text, value }): React.ReactNode => (
<MenuItem
Expand All @@ -181,11 +199,19 @@ function CustomizedSelect ({ _mt = 0, defaultValue, disabledItems, isDisabled =
<Grid container height={'30px'} justifyContent='flex-start'>
{showIcons && text !== 'Allow use on any chain' &&
<Grid alignItems='center' container item pr='6px' width='fit-content'>
<ChainLogo chainName={chainName(text)} genesisHash={value as string} size={19.8} />
{isIdenticon
? <Icon
className='icon'
size={25}
theme={value as IconTheme}
value={DEMO_ACCOUNT}
/>
: <ChainLogo chainName={chainName(text)} genesisHash={value as string} size={19.8} />
}
</Grid>
}
<Grid alignItems='center' container item justifyContent='flex-start' pl='6px' width='fit-content' sx={{ overflowX: 'scroll' }}>
<Typography fontSize='14px' fontWeight={300}>
<Grid alignItems='center' container item justifyContent='flex-start' pl='6px' sx={{ overflowX: 'scroll' }} width='fit-content'>
<Typography fontSize={textFontSize} fontWeight={300}>
{text}
</Typography>
</Grid>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// Copyright 2019-2024 @polkadot/extension-polkagate authors & contributors
// SPDX-License-Identifier: Apache-2.0

import type { IconTheme } from '@polkadot/react-identicon/types';

import { Grid, type SxProps, type Theme } from '@mui/material';
import { t } from 'i18next';
import React, { useCallback, useContext } from 'react';

import { AccountIconThemeContext } from './contexts';
import { setStorage } from './Loading';
import Select2 from './Select2';

interface Props {
style?: SxProps<Theme> | undefined;
}

const THEME_OPTIONS = [
{ text: 'Dot', value: 'polkadot' },
{ text: 'Ball', value: 'beachball' },
{ text: 'Cube', value: 'ethereum' }
];

function SelectIdenticonTheme ({ style = {} }: Props) {
const { accountIconTheme, setAccountIconTheme } = useContext(AccountIconThemeContext);

const onChangeTheme = useCallback((iconTheme: string | number) => {
setStorage('iconTheme', iconTheme).catch(console.error);
setAccountIconTheme(iconTheme as IconTheme);
}, [setAccountIconTheme]);

return (
<Grid
container
item
sx={{ ...style }}
>
<Select2
defaultValue={accountIconTheme}
isIdenticon
label={t('Account Icon')}
labelAlignment='flex-start'
labelFontSize='14px'
labelPaddingLeft='0px'
onChange={onChangeTheme}
options={THEME_OPTIONS}
rounded={false}
textFontSize='18px'
/>
</Grid>
);
}

export default React.memo(SelectIdenticonTheme);
4 changes: 3 additions & 1 deletion packages/extension-polkagate/src/components/contexts.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

import type { AccountsContext, AuthorizeRequest, MetadataRequest, SigningRequest } from '@polkadot/extension-base/background/types';
import type { SettingsStruct } from '@polkadot/ui-settings/types';
import type { AccountsAssetsContextType, AlertContextType, APIsContext, CurrencyContextType, DropdownOption, FetchingRequests, ReferendaContextType, UserAddedChains } from '../util/types';
import type { AccountIconThemeContextType, AccountsAssetsContextType, AlertContextType, APIsContext, CurrencyContextType, DropdownOption, FetchingRequests, ReferendaContextType, UserAddedChains } from '../util/types';

import React from 'react';

Expand All @@ -26,8 +26,10 @@ const SigningReqContext = React.createContext<SigningRequest[]>([]);
const ToastContext = React.createContext<({ show: (message: string) => void })>({ show: noop });
const UserAddedChainContext = React.createContext<UserAddedChains>({});
const GenesisHashOptionsContext = React.createContext<DropdownOption[]>([]);
const AccountIconThemeContext = React.createContext<AccountIconThemeContextType>({ accountIconTheme: undefined, setAccountIconTheme: noop });

export { AccountContext,
AccountIconThemeContext,
AccountsAssetsContext,
ActionContext,
AlertContext,
Expand Down
1 change: 1 addition & 0 deletions packages/extension-polkagate/src/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ export { default as RemoveAuth } from './RemoveAuth';
export { default as Select } from './Select';
export { default as Select2 } from './Select2';
export { default as SelectChain } from './SelectChain';
export { default as SelectIdenticonTheme } from './SelectIdenticonTheme';
export { default as ShortAddress } from './ShortAddress';
export { default as ShortAddress2 } from './ShortAddress2';
export { default as ShowBalance } from './ShowBalance';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
// Copyright 2019-2024 @polkadot/extension-polkagate authors & contributors
// SPDX-License-Identifier: Apache-2.0

// @ts-nocheck
/* eslint-disable react/jsx-max-props-per-line */

import { Box, Collapse, Divider, Grid, useTheme } from '@mui/material';
Expand All @@ -10,7 +9,7 @@ import React, { useCallback, useContext, useEffect, useMemo, useState } from 're
import settings from '@polkadot/ui-settings';

import { checkBox, checkedBox } from '../../../assets/icons';
import { AccountContext, Select, VaadinIcon } from '../../../components';
import { AccountContext, Select, SelectIdenticonTheme, VaadinIcon } from '../../../components';
import { getStorage, setStorage } from '../../../components/Loading';
import { useIsTestnetEnabled, useTranslation } from '../../../hooks';
import { setNotification, tieAccount } from '../../../messaging';
Expand All @@ -25,7 +24,7 @@ interface Props {
show: boolean;
}

export default function SettingSubMenuFullScreen({ show }: Props): React.ReactElement {
export default function SettingSubMenuFullScreen ({ show }: Props): React.ReactElement {
const { t } = useTranslation();
const theme = useTheme();
const { accounts } = useContext(AccountContext);
Expand Down Expand Up @@ -85,7 +84,7 @@ export default function SettingSubMenuFullScreen({ show }: Props): React.ReactEl

useEffect(() => {
getStorage('testnet_enabled').then((res) => {
setIsTestnetEnabledChecked(res as boolean);
setIsTestnetEnabledChecked(res as unknown as boolean);
}).catch(console.error);
}, [setIsTestnetEnabledChecked]);

Expand Down Expand Up @@ -135,9 +134,13 @@ export default function SettingSubMenuFullScreen({ show }: Props): React.ReactEl
onClick={onManageLoginPassword}
text={t('Manage login password')}
/>
<Grid item pt='12px'>
<SelectIdenticonTheme
style={{ pt: '18px', width: '100%' }}
/>
<Grid item pt='10px'>
<Select
label={t('Language')}
//@ts-ignore
onChange={onChangeLang}
options={languageOptions}
value={settings.i18nLang !== 'default' ? settings.i18nLang : languageOptions[0].value}
Expand All @@ -146,6 +149,7 @@ export default function SettingSubMenuFullScreen({ show }: Props): React.ReactEl
<Grid item pt='10px'>
<Select
label={t('Notification')}
//@ts-ignore
onChange={onChangeNotification}
options={notificationOptions}
value={notification ?? notificationOptions[1].value}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { Grid, Typography } from '@mui/material';
import { POLKADOT_GENESIS } from '@polkagate/apps-config';
import React, { useCallback, useContext, useEffect, useState } from 'react';

import { FULLSCREEN_WIDTH } from '@polkadot/extension-polkagate/src/util/constants';
import { DEMO_ACCOUNT, FULLSCREEN_WIDTH } from '@polkadot/extension-polkagate/src/util/constants';

import { AccountContext, ActionContext } from '../../components';
import { useFullscreen, useTranslation } from '../../hooks';
Expand All @@ -20,8 +20,6 @@ import Privacy from '../../popup/welcome/Privacy';
import FullScreenHeader from '../governance/FullScreenHeader';
import IconBox from './IconBox';

const demoAccount = '1ChFWeNRLarAPRCTM3bfJmncJbSAbSS9yqjueWz7jX7iTVZ';

export const ICON_BOX_WIDTH = '300px';

function Onboarding (): React.ReactElement {
Expand Down Expand Up @@ -53,7 +51,7 @@ function Onboarding (): React.ReactElement {
);

const onExploreDemo = useCallback((): void => {
createAccountExternal('Demo Account ☔️', demoAccount, POLKADOT_GENESIS)
createAccountExternal('Demo Account ☔️', DEMO_ACCOUNT, POLKADOT_GENESIS)
.then(() => onAction('/'))
.catch((error: Error) => {
console.error(error);
Expand Down
9 changes: 6 additions & 3 deletions packages/extension-polkagate/src/partials/SettingSubMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import React, { useCallback, useContext, useEffect, useMemo, useState } from 're

import settings from '@polkadot/ui-settings';

import { ActionContext, Checkbox2, FullScreenIcon, Infotip2, MenuItem, Select, VaadinIcon } from '../components';
import { ActionContext, Checkbox2, FullScreenIcon, Infotip2, MenuItem, Select, SelectIdenticonTheme, VaadinIcon } from '../components';
import { getStorage, updateStorage } from '../components/Loading';
import { useExtensionLockContext } from '../context/ExtensionLockContext';
import ThemeChanger from '../fullscreen/governance/partials/ThemeChanger';
Expand Down Expand Up @@ -171,7 +171,10 @@ export default function SettingSubMenu ({ isTestnetEnabledChecked, onChange, set
text={t('Manage login password')}
/>
</Grid>
<Grid item pt='12px'>
<SelectIdenticonTheme
style={{ pt: '12px', width: '100%' }}
/>
<Grid item pt='10px'>
<Select
defaultValue={languageOptions[0].value}
label={t('Language')}
Expand All @@ -180,7 +183,7 @@ export default function SettingSubMenu ({ isTestnetEnabledChecked, onChange, set
value={settings.i18nLang !== 'default' ? settings.i18nLang : languageOptions[0].value}
/>
</Grid>
<Grid item pt='10px'>
<Grid item pt='10px' sx={{ visibility: 'hidden' }}>
<Select
defaultValue={notificationOptions[1].value}
label={t('Notification')}
Expand Down
Loading

0 comments on commit e1b3570

Please sign in to comment.