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

Update profiles #1388

Merged
merged 4 commits into from
Jun 25, 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
8 changes: 4 additions & 4 deletions packages/extension-polkagate/src/components/ProfileInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,14 @@ interface Props {
export default function ProfileInput({ disabled = false, placeHolder = '', setProfileName, profileName, helperText = '', label, style }: Props): React.ReactElement<Props> {
const containerRef = useRef<HTMLDivElement>(null);

const userDefinedProfiles = useProfiles();
const profiles = useProfiles();

const [isPopperOpen, setTogglePopper] = useState<boolean>(false);
const [focus, setFocus] = useState<boolean>(false);
const [enteredProfile, setEnteredProfile] = useState<string | undefined>();
const [dropdownWidth, setDropdownWidth] = useState<string>('0');

const autocompleteOptions = useMemo(() => userDefinedProfiles?.map((profile, index) => ({ index, profile })), [userDefinedProfiles]);
const autocompleteOptions = useMemo(() => profiles?.userDefinedProfiles?.map((profile, index) => ({ index, profile })), [profiles?.userDefinedProfiles]);

useEffect(() => {
profileName && setEnteredProfile(profileName);
Expand All @@ -54,8 +54,8 @@ export default function ProfileInput({ disabled = false, placeHolder = '', setPr
}, [setProfileName]);

const openPopper = useCallback(() =>
userDefinedProfiles && userDefinedProfiles?.length > 0 && !enteredProfile && !isPopperOpen && setTogglePopper(true)
, [userDefinedProfiles?.length, enteredProfile, isPopperOpen]);
profiles?.userDefinedProfiles && profiles.userDefinedProfiles?.length > 0 && !enteredProfile && !isPopperOpen && setTogglePopper(true)
, [profiles?.userDefinedProfiles?.length, enteredProfile, isPopperOpen]);

const closePopper = useCallback(() =>
setTogglePopper(false),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,10 @@ export default function HomePageFullScreen(): React.ReactElement {
noChainSwitch
/>
<Grid container item sx={{ bgcolor: 'backgroundFL.secondary', maxWidth: '1282px' }}>
<ProfileTabs
orderedAccounts={initialAccountList}
/>
<Grid container item justifyContent='space-around' sx={{ bgcolor: 'backgroundFL.secondary', height: 'calc(100vh - 105px)', maxWidth: '1282px', overflow: 'scroll', pb: '40px' }}>
<Grid container item justifyContent='space-around' sx={{ bgcolor: 'backgroundFL.secondary', height: 'calc(100vh - 70px)', maxWidth: '1282px', overflow: 'scroll', pb: '40px' }}>
<ProfileTabs
orderedAccounts={initialAccountList}
/>
<Grid container direction='column' item rowGap='20px' width='760px'>
{profileAccounts &&
<DraggableAccountsList
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@

/* eslint-disable react/jsx-max-props-per-line */

import { Divider, Grid, Popover, useTheme } from '@mui/material';
import { Divider, Grid, Popover, useTheme, IconButton, type Theme } from '@mui/material';
import React, { useCallback, useContext, useState } from 'react';

import { type TFunction } from '@polkagate/apps-config/types';
import DoneIcon from '@mui/icons-material/Done';
import { ActionContext, InputWithLabel, MenuItem, VaadinIcon } from '../../../components';
import { useInfo, useTranslation, useProfiles } from '../../../hooks';
import { updateMeta } from '../../../messaging';
Expand All @@ -15,13 +16,51 @@ interface Props {
setUpperAnchorEl: React.Dispatch<React.SetStateAction<HTMLButtonElement | null>>
}

interface InputBoxProps {
editName: (newName: string | null) => void;
newName: string | undefined;
addToNewProfile: (profile?: string) => void;
t: TFunction;
theme: Theme;
}

const InputBox = ({ addToNewProfile, editName, newName, t, theme }: InputBoxProps) => {
return (
<Grid container item alignItems='flex-end' justifyContent='space-evenly'>
<Grid container item xs>
<InputWithLabel
isFocused
fontSize={16}
fontWeight={400}
height={35}
label={t('Choose a name for the profile')}
labelFontSize='14px'
onChange={editName}
onEnter={() => newName && addToNewProfile(newName as string)}
placeholder={t('Profile Name')}
value={newName}
/>
</Grid>
<Grid container item height='fit-content' ml='10px' width='fit-content'>
<IconButton
disabled={!newName}
onClick={() => addToNewProfile(newName as string)}
sx={{ p: 0 }}
>
<DoneIcon sx={{ color: 'secondary.light', fontSize: '32px', stroke: theme.palette.secondary.light, strokeWidth: 1.5 }} />
</IconButton>
</Grid>
</Grid>
);
}

function ProfileMenu({ address, setUpperAnchorEl }: Props): React.ReactElement<Props> {
const theme = useTheme();
const { t } = useTranslation();

const onAction = useContext(ActionContext);
const { account, chain } = useInfo(address);
const userDefinedProfiles = useProfiles();
const profiles = useProfiles();

const [anchorEl, setAnchorEl] = useState<HTMLButtonElement | HTMLDivElement | null>();
const [showName, setShowName] = useState<boolean>();
Expand Down Expand Up @@ -76,17 +115,12 @@ function ProfileMenu({ address, setUpperAnchorEl }: Props): React.ReactElement<P
const Menus = () => (
<Grid alignItems='flex-start' container display='block' item sx={{ borderRadius: '10px', minWidth: '300px', p: '10px' }}>
{showName
? <InputWithLabel
isFocused
fontSize={16}
fontWeight={400}
height={35}
label={t('Choose a name for the profile')}
labelFontSize='14px'
onChange={editName}
onEnter={() => addToNewProfile(newName as string)}
placeholder={t('Profile Name')}
value={newName}
? <InputBox
addToNewProfile={addToNewProfile}
editName={editName}
newName={newName}
t={t}
theme={theme}
/>
: <MenuItem
iconComponent={
Expand All @@ -98,8 +132,8 @@ function ProfileMenu({ address, setUpperAnchorEl }: Props): React.ReactElement<P
/>
}
<Divider sx={{ bgcolor: 'secondary.light', height: '1px', my: '7px' }} />
{userDefinedProfiles?.length
? userDefinedProfiles?.map((profile) => (
{profiles?.userDefinedProfiles?.length
? profiles.userDefinedProfiles?.map((profile) => (
<MenuItem
iconComponent={
<VaadinIcon icon='vaadin:folder-open-o' style={{ height: '20px', color: theme.palette.text.primary }} />
Expand Down Expand Up @@ -127,7 +161,7 @@ function ProfileMenu({ address, setUpperAnchorEl }: Props): React.ReactElement<P

return (
<>
<Grid aria-describedby={id} component='button' container item onClick={onAddClick} sx={{ bgcolor: 'transparent', border: 'none', color:theme.palette.text.primary, height: 'fit-content', p: 0, width: 'inherit' }}>
<Grid aria-describedby={id} component='button' container item onClick={onAddClick} sx={{ bgcolor: 'transparent', border: 'none', color: theme.palette.text.primary, height: 'fit-content', p: 0, width: 'inherit' }}>
<MenuItem
iconComponent={
<VaadinIcon icon='vaadin:folder-add' style={{ height: '20px', color: `${theme.palette.text.primary}` }} />
Expand All @@ -139,12 +173,12 @@ function ProfileMenu({ address, setUpperAnchorEl }: Props): React.ReactElement<P
</Grid>
{!!profileName &&
<>
<Grid component='button' container item onClick={onRemove} sx={{ bgcolor: 'transparent', border: 'none', color: theme.palette.text.primary, height: 'fit-content', p: 0, width: 'inherit' }}>
<Grid component='button' container item onClick={onRemove} sx={{ '> div div:last-child p': { maxWidth: '220px', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }, bgcolor: 'transparent', border: 'none', color: theme.palette.text.primary, height: 'fit-content', p: 0, width: 'inherit' }}>
<MenuItem
iconComponent={
<VaadinIcon icon='vaadin:folder-remove' style={{ height: '20px', color: `${theme.palette.text.primary}` }} />
}
text={t('Remove from {{profileName}} profile', { replace: { profileName } })}
text={t('Remove from {{profileName}}', { replace: { profileName } })}
withHoverEffect
/>
</Grid>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,8 @@ import { Grid, Typography, useTheme } from '@mui/material';
import React, { useCallback, useMemo, useEffect, useState } from 'react';
import { useProfileAccounts, useTranslation } from '../../../hooks';
import { getStorage, setStorage, watchStorage } from '../../../components/Loading';
import { pgBoxShadow } from '../../../util/utils';
import { VaadinIcon } from '../../../components/index';
import { showAccount } from '../../../messaging';
import { keyframes } from '@emotion/react';
import { HIDDEN_PERCENT } from './ProfileTabs';

interface Props {
Expand All @@ -18,41 +16,38 @@ interface Props {
setSelectedProfile: React.Dispatch<React.SetStateAction<string | undefined>>;
isHovered: boolean | undefined;
text: string;
index: number;
}

// Define keyframes for the swinging animation around the x-axis
const swingAnimation = keyframes`
0% { transform: rotateX(0deg); }
20% { transform: rotateX(30deg); }
40% { transform: rotateX(-20deg); }
60% { transform: rotateX(10deg); }
80% { transform: rotateX(-10deg); }
100% { transform: rotateX(0deg); }
`;

export default function ProfileTab({ isHovered, text, selectedProfile, setSelectedProfile, orderedAccounts }: Props): React.ReactElement {
export default function ProfileTab({ isHovered, text, selectedProfile, setSelectedProfile, orderedAccounts, index }: Props): React.ReactElement {
const { t } = useTranslation();
const theme = useTheme();

const PREDEFINED_TAB_COLORS = useMemo(() => {
return [
{ text: t('All'), colorLight: '#D1C4E9', colorDark: '#5E35B1' },
{ text: t('Local'), colorLight: '#C8E6C9', colorDark: '#388E3C' },
{ text: t('Ledger'), colorLight: '#FFCCBC', colorDark: '#D84315' },
{ text: t('Watch-only'), colorLight: '#B3E5FC', colorDark: '#0288D1' },
{ text: t('QR-attached'), colorLight: '#F8BBD0', colorDark: '#D81B60' },
{ lightColor: '#D1C4E9', darkColor: '#99004F' },
{ lightColor: '#C8E6C9', darkColor: '#468189' },
{ lightColor: '#B3E5FC', darkColor: '#846C5B' },
{ lightColor: '#F8BBD0', darkColor: '#A63C06' },
{ lightColor: '#ACE894', darkColor: '#D81B60' },
{ lightColor: '#F5D5ED', darkColor: '#2B4162' },
{ lightColor: '#EBCFB2', darkColor: '#9D8189' },
{ lightColor: '#FCF0CC', darkColor: '#5F4842' },
]
}, [t, theme]);

const profileAccounts = useProfileAccounts(orderedAccounts, text);

const [animate, setAnimate] = useState<boolean>(true);
/** set by user click on a profile tab */
const [toHiddenAll, setToHiddenAll] = useState<boolean>();

const getColor = useCallback((_text: string) => {
const selectedProfile = PREDEFINED_TAB_COLORS.find(({ text }) => text === _text);
const color = theme.palette.mode === 'dark' ? selectedProfile?.colorDark : selectedProfile?.colorLight;
const isDarkMode = useMemo(() => theme.palette.mode === 'dark', [theme.palette.mode]);
const shadow = useMemo(() => isDarkMode ? '0px 2px 5px 1px rgba(255, 255, 255, 0.10)' : '0px 2px 3px 1px rgba(000, 000, 000, 0.13)', [isDarkMode]);
const shadowOnHover = useMemo(() => isDarkMode ? '0px 2px 5px 1px rgba(255, 255, 255, 0.20)' : '0px 2px 3px 1px rgba(000, 000, 000, 0.20)', [isDarkMode]);

const getColor = useCallback((index: number) => {
const selectedProfile = PREDEFINED_TAB_COLORS[index];
const color = isDarkMode ? selectedProfile?.darkColor : selectedProfile?.lightColor;

return color;
}, [PREDEFINED_TAB_COLORS]);
Expand Down Expand Up @@ -83,6 +78,8 @@ export default function ProfileTab({ isHovered, text, selectedProfile, setSelect

const isHiddenAll = isAllProfileAccountsHidden !== undefined ? isAllProfileAccountsHidden : toHiddenAll;

const hideCard = useMemo(() => !Boolean(isSelected || isHovered || visibleContent), [isSelected, isHovered, visibleContent]);

useEffect(() => {
if (profileAccounts && toHiddenAll !== undefined) {
hideAccounts(profileAccounts);
Expand All @@ -96,58 +93,40 @@ export default function ProfileTab({ isHovered, text, selectedProfile, setSelect
}).catch(console.error);

watchStorage('profile', setSelectedProfile).catch(console.error);

// Disable animation after initial render
const timer = setTimeout(() => {
setAnimate(false);
}, 1000);

return () => clearTimeout(timer);
}, [t]);

return (
<Grid item container onClick={onClick}
justifyContent='center'
alignItems='center'
columnGap='5px'
px='8px'
sx={{
cursor: 'pointer',
flexShrink: 0,
mx: '1px',
bgcolor: getColor(text) || 'background.paper',
borderBottomLeftRadius: '12px',
WebkitBorderBottomRightRadius: '12px',
bgcolor: getColor(index) || 'background.paper',
borderRadius: '0 0 12px 12px',
minWidth: '100px',
borderBottom: isSelected
? `1.5px solid ${theme.palette.secondary.light}`
: `1.5px solid ${theme.palette.divider}`,
transition: 'transform 0.3s, box-shadow 0.3s',
transition: 'transform 0.2s, box-shadow 0.2s',
boxShadow: shadow,
'&:hover': {
transform: 'perspective(1000px) translateZ(10px)',
boxShadow: pgBoxShadow(theme),
boxShadow: shadowOnHover,
},
animation: animate ? `${swingAnimation} 1s ease-in-out` : 'none',
perspective: '1000px',
width: 'fit-content',
transformOrigin: 'top',
position: 'relative',
transform: isSelected && !isHovered ? `translateY(${HIDDEN_PERCENT})` : undefined
transform: hideCard ? `translateY(-${HIDDEN_PERCENT})` : undefined
}}>
<VaadinIcon icon={'vaadin:check'} style={{ height: '13px', marginRight: '-20px', visibility: isSelected ? 'visible' : 'hidden' }} />
<Typography color={'text.primary'} display='block' fontSize='15px' fontWeight={400} textAlign='center'
sx={{
userSelect: 'none',
px: '20px',
visibility: visibleContent ? 'visible' : 'hidden',
transition: 'visibility 0.3s ease-in-out'
}}>
{text}
<VaadinIcon icon={'vaadin:check'} style={{ height: '13px', visibility: isSelected ? 'visible' : 'hidden', width: '15px' }} />
<Typography color={'text.primary'} display='block' fontSize='16px' fontWeight={isSelected ? 500 : 400} textAlign='center' sx={{ visibility: visibleContent ? 'visible' : 'hidden', transition: isSelected ? 'none' : 'visibility 0.1s ease-in-out', maxWidth: '100px', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
{t(text)}
</Typography>
<VaadinIcon icon={isHiddenAll ? 'vaadin:eye-slash' : ''}
style={{
height: '13px',
marginLeft: '-20px',
visibility: visibleContent ? 'visible' : 'hidden',
transition: 'visibility 0.3s ease-in-out'
transition: isSelected ? 'none' : 'visibility 0.1s ease-in-out',
width: '15px'
}} />
</Grid>
);
Expand Down
Loading
Loading