From 52c76fc861b358f40f25884c77e962a7ce0c2a5f Mon Sep 17 00:00:00 2001 From: Nick Date: Wed, 19 Jun 2024 00:55:50 +0330 Subject: [PATCH 01/36] init --- .../src/fullscreen/accountDetails/index.tsx | 16 +-- .../src/fullscreen/homeFullScreen/index.tsx | 25 ++-- .../partials/AccountInformationForHome.tsx | 19 ++- .../partials/FullScreenAccountMenu.tsx | 6 +- .../homeFullScreen/partials/ProfileMenu.tsx | 117 ++++++++++++++++++ .../homeFullScreen/partials/ProfileTabs.tsx | 101 +++++++++++++++ .../manageProfiles/ManageProfileModal.tsx | 85 +++++++++++++ .../manageProfiles/SimpleModalTitle.tsx | 53 ++++++++ .../extension-polkagate/src/hooks/index.ts | 1 + .../src/hooks/useProfileAccounts.tsx | 44 +++++++ packages/extension-polkagate/src/messaging.ts | 4 +- 11 files changed, 445 insertions(+), 26 deletions(-) create mode 100644 packages/extension-polkagate/src/fullscreen/homeFullScreen/partials/ProfileMenu.tsx create mode 100644 packages/extension-polkagate/src/fullscreen/homeFullScreen/partials/ProfileTabs.tsx create mode 100644 packages/extension-polkagate/src/fullscreen/manageProfiles/ManageProfileModal.tsx create mode 100644 packages/extension-polkagate/src/fullscreen/manageProfiles/SimpleModalTitle.tsx create mode 100644 packages/extension-polkagate/src/hooks/useProfileAccounts.tsx diff --git a/packages/extension-polkagate/src/fullscreen/accountDetails/index.tsx b/packages/extension-polkagate/src/fullscreen/accountDetails/index.tsx index 30579dfd4..804651dd3 100644 --- a/packages/extension-polkagate/src/fullscreen/accountDetails/index.tsx +++ b/packages/extension-polkagate/src/fullscreen/accountDetails/index.tsx @@ -32,14 +32,14 @@ import ReservedDisplayBalance from './components/ReservedDisplayBalance'; import LockedInReferenda from './unlock/Review'; import { AccountInformationForDetails, AccountSetting, AssetSelect, CommonTasks, DisplayBalance, ExternalLinks, LockedBalanceDisplay, TotalChart } from './components'; -export const popupNumbers = { - LOCKED_IN_REFERENDA: 1, - FORGET_ACCOUNT: 2, - RENAME: 3, - EXPORT_ACCOUNT: 4, - DERIVE_ACCOUNT: 5, - RECEIVE: 6, - HISTORY: 7 +export enum popupNumbers { + LOCKED_IN_REFERENDA, + FORGET_ACCOUNT, + RENAME, + EXPORT_ACCOUNT, + DERIVE_ACCOUNT, + RECEIVE, + HISTORY }; export interface UnlockInformationType { diff --git a/packages/extension-polkagate/src/fullscreen/homeFullScreen/index.tsx b/packages/extension-polkagate/src/fullscreen/homeFullScreen/index.tsx index 1c913b7bc..543ce173e 100644 --- a/packages/extension-polkagate/src/fullscreen/homeFullScreen/index.tsx +++ b/packages/extension-polkagate/src/fullscreen/homeFullScreen/index.tsx @@ -1,23 +1,23 @@ // 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 type { AccountWithChildren } from '@polkadot/extension-base/background/types'; + import { Grid } from '@mui/material'; import React, { useContext, useEffect, useState } from 'react'; -import { AccountWithChildren } from '@polkadot/extension-base/background/types'; - import { AccountContext, ActionContext } from '../../components'; -import { useAccountsOrder, useFullscreen } from '../../hooks'; +import { useAccountsOrder, useFullscreen, useProfileAccounts } from '../../hooks'; import { AddNewAccountButton } from '../../partials'; import { FullScreenHeader } from '../governance/FullScreenHeader'; import HeaderComponents from './components/HeaderComponents'; import DraggableAccountsList from './partials/DraggableAccountList'; import HomeMenu from './partials/HomeMenu'; import TotalBalancePieChart from './partials/TotalBalancePieChart'; -import WatchList, { AssetsWithUiAndPrice } from './partials/WatchList'; +import WatchList, { type AssetsWithUiAndPrice } from './partials/WatchList'; +import ProfileTabs from './partials/ProfileTabs'; export interface AccountsOrder { id: number, @@ -26,13 +26,17 @@ export interface AccountsOrder { export default function HomePageFullScreen(): React.ReactElement { useFullscreen(); - const initialAccountList = useAccountsOrder(true) as AccountsOrder[]; + const initialAccountList = useAccountsOrder(true) as AccountsOrder[] | undefined; const onAction = useContext(ActionContext); const { accounts: accountsInExtension } = useContext(AccountContext); const [hideNumbers, setHideNumbers] = useState(); const [groupedAssets, setGroupedAssets] = useState(); + const profileAccounts = useProfileAccounts(initialAccountList) as AccountsOrder[] | undefined; + + console.log('profileAccounts', profileAccounts) + useEffect(() => { if (accountsInExtension && accountsInExtension?.length === 0) { onAction('/onboarding'); @@ -51,12 +55,15 @@ export default function HomePageFullScreen(): React.ReactElement { noAccountDropDown noChainSwitch /> - + + - {initialAccountList && + {profileAccounts && } {initialAccountList && initialAccountList?.length <= 2 && diff --git a/packages/extension-polkagate/src/fullscreen/homeFullScreen/partials/AccountInformationForHome.tsx b/packages/extension-polkagate/src/fullscreen/homeFullScreen/partials/AccountInformationForHome.tsx index c9c837a97..ad6c1d291 100644 --- a/packages/extension-polkagate/src/fullscreen/homeFullScreen/partials/AccountInformationForHome.tsx +++ b/packages/extension-polkagate/src/fullscreen/homeFullScreen/partials/AccountInformationForHome.tsx @@ -1,6 +1,5 @@ // Copyright 2019-2024 @polkadot/extension-polkagate authors & contributors // SPDX-License-Identifier: Apache-2.0 -// @ts-nocheck /* eslint-disable react/jsx-first-prop-new-line */ /* eslint-disable react/jsx-max-props-per-line */ @@ -27,6 +26,7 @@ import AccountIconsFs from '../../accountDetails/components/AccountIconsFs'; import AOC from '../../accountDetails/components/AOC'; import { openOrFocusTab } from '../../accountDetails/components/CommonTasks'; import FullScreenAccountMenu from './FullScreenAccountMenu'; +import ManageProfileModal from '../../manageProfiles/ManageProfileModal'; interface AddressDetailsProps { accountAssets: FetchedBalance[] | null | undefined; @@ -39,11 +39,12 @@ interface AddressDetailsProps { type AccountButtonType = { text: string, onClick: () => void, icon: React.ReactNode }; -export const POPUPS_NUMBER = { - DERIVE_ACCOUNT: 4, - EXPORT_ACCOUNT: 3, - FORGET_ACCOUNT: 1, - RENAME: 2 +export enum POPUPS_NUMBER { + DERIVE_ACCOUNT, + EXPORT_ACCOUNT, + FORGET_ACCOUNT, + RENAME, + MANAGE_PROFILE }; export default function AccountInformationForHome({ accountAssets, address, hideNumbers, isChild, selectedAsset, setSelectedAsset }: AddressDetailsProps): React.ReactElement { @@ -242,6 +243,12 @@ export default function AccountInformationForHome({ accountAssets, address, hide setDisplayPopup={setDisplayPopup} /> } + {displayPopup === POPUPS_NUMBER.MANAGE_PROFILE && address && + + } ); } diff --git a/packages/extension-polkagate/src/fullscreen/homeFullScreen/partials/FullScreenAccountMenu.tsx b/packages/extension-polkagate/src/fullscreen/homeFullScreen/partials/FullScreenAccountMenu.tsx index 617417001..c1c79b082 100644 --- a/packages/extension-polkagate/src/fullscreen/homeFullScreen/partials/FullScreenAccountMenu.tsx +++ b/packages/extension-polkagate/src/fullscreen/homeFullScreen/partials/FullScreenAccountMenu.tsx @@ -1,6 +1,5 @@ // Copyright 2019-2024 @polkadot/extension-polkagate authors & contributors // SPDX-License-Identifier: Apache-2.0 -// @ts-nocheck /* eslint-disable react/jsx-max-props-per-line */ @@ -13,6 +12,7 @@ import { ActionContext, MenuItem, SocialRecoveryIcon, VaadinIcon } from '../../. import { useInfo, useTranslation } from '../../../hooks'; import { IDENTITY_CHAINS, PROXY_CHAINS, SOCIAL_RECOVERY_CHAINS } from '../../../util/constants'; import { POPUPS_NUMBER } from './AccountInformationForHome'; +import ProfileMenu from './ProfileMenu'; interface Props { address: string | undefined; @@ -119,6 +119,10 @@ function FullScreenAccountMenu({ address, baseButton, setDisplayPopup }: Props): withHoverEffect /> + {hasPrivateKey && >; +} + +function ProfileMenu({ address, setDisplayPopup }: Props): React.ReactElement { + const theme = useTheme(); + const { t } = useTranslation(); + const { chain } = useInfo(address); + const onAction = useContext(ActionContext); + + const [anchorEl, setAnchorEl] = useState(); + + const handleClose = useCallback(() => { + setAnchorEl(null); + }, []); + + const onClick = useCallback((event: React.MouseEvent) => { + setAnchorEl(event.currentTarget); + }, []); + + const onManageProfiles = useCallback(() => { + setDisplayPopup(POPUPS_NUMBER.MANAGE_PROFILE); + }, [address, chain, onAction]); + + const isDisable = useCallback((supportedChains: string[]) => { + if (!chain) { + return true; + } else { + return !supportedChains.includes(chain.genesisHash ?? ''); + } + }, [chain]); + + const Menus = () => ( + + + } + onClick={onManageProfiles} + text={t('New profile')} + withHoverEffect + /> + + + } + onClick={onManageProfiles} + text={t('Others')} + withHoverEffect + /> + + ); + + const open = Boolean(anchorEl); + const id = open ? 'simple-popover' : undefined; + + return ( + <> + + + } + text={t('Add account to profile')} + withHoverEffect + /> + + + + + + ); +} + +export default React.memo(ProfileMenu); diff --git a/packages/extension-polkagate/src/fullscreen/homeFullScreen/partials/ProfileTabs.tsx b/packages/extension-polkagate/src/fullscreen/homeFullScreen/partials/ProfileTabs.tsx new file mode 100644 index 000000000..afe0c67aa --- /dev/null +++ b/packages/extension-polkagate/src/fullscreen/homeFullScreen/partials/ProfileTabs.tsx @@ -0,0 +1,101 @@ +// Copyright 2019-2024 @polkadot/extension-polkagate authors & contributors +// SPDX-License-Identifier: Apache-2.0 + +/* eslint-disable react/jsx-max-props-per-line */ + +import { Grid, Typography, useTheme } from '@mui/material'; +import React, { useCallback, useMemo, useEffect, useState } from 'react'; +import { useTranslation } from '../../../hooks'; +import type { AccountsOrder } from '..'; +import { getStorage, setStorage, watchStorage } from '../../../components/Loading'; + +interface Props { + orderedAccounts: AccountsOrder[] | undefined; +} + +interface TabProps { + text: string; + onClick: (event: any) => void; +} + +function Tab({ text, onClick }: TabProps): React.ReactElement { + const { t } = useTranslation(); + const theme = useTheme(); + + const [profile, setProfile] = useState(); + + useEffect(() => { + getStorage('profile').then((res) => { + setProfile(res as string || t('All')); + }).catch(console.error); + + watchStorage('profile', setProfile).catch(console.error); + }, []); + + return ( + + + {text} + + + ); +} + +export default function ProfileTabs({ orderedAccounts }: Props): React.ReactElement { + const { t } = useTranslation(); + + const hasLocal = useMemo(() => + orderedAccounts?.find(({ account }) => !account.isExternal) + , [orderedAccounts]); + + const hasWatchOnly = useMemo(() => + orderedAccounts?.find(({ account }) => account.isExternal && !account.isQR && !account.isHardware) + , [orderedAccounts]); + + const hasQrAttached = useMemo(() => + orderedAccounts?.find(({ account: { isQR } }) => isQR) + , [orderedAccounts]); + + const onTabClick = useCallback((event: any) => { + setStorage('profile', event.target.innerText); + }, []); + + return ( + + + {hasLocal && + + } + {hasWatchOnly && + + } + {hasQrAttached && + + } + + ); +} + diff --git a/packages/extension-polkagate/src/fullscreen/manageProfiles/ManageProfileModal.tsx b/packages/extension-polkagate/src/fullscreen/manageProfiles/ManageProfileModal.tsx new file mode 100644 index 000000000..dc4515e34 --- /dev/null +++ b/packages/extension-polkagate/src/fullscreen/manageProfiles/ManageProfileModal.tsx @@ -0,0 +1,85 @@ +// Copyright 2019-2024 @polkadot/extension-polkagate authors & contributors +// SPDX-License-Identifier: Apache-2.0 + +/* eslint-disable react/jsx-max-props-per-line */ + + +import { Grid } from '@mui/material'; +import React, { useCallback, useState } from 'react'; + +import { ButtonWithCancel, NewAddress } from '../../components'; +import { useTranslation } from 'react-i18next'; +import { DraggableModal } from '../governance/components/DraggableModal'; +import { useInfo } from '../../hooks'; +import { SimpleModalTitle } from './SimpleModalTitle'; +import { Name } from '../../partials'; +import { updateMeta } from '../../messaging'; + +interface Props { + address: string; + setDisplayPopup: React.Dispatch>; +} + +export default function ManageProfileModal({ address, setDisplayPopup }: Props): React.ReactElement { + const { t } = useTranslation(); + + const { account } = useInfo(address); + + const [isBusy, setIsBusy] = useState(false); + const [newName, setNewName] = useState(); + + const editName = useCallback((newName: string | null) => { + setNewName(newName ?? ''); + }, []); + console.log('account', account) + + + const addToNewProfile = useCallback(() => { + if (!newName) { + return; + } + + setIsBusy(true); + const metaData = JSON.stringify({ profile: newName }); + + updateMeta(String(address), metaData) + .then(() => { + setIsBusy(false); + onClose(); + }).catch(console.error); + }, [address, newName]); + + const onClose = useCallback(() => setDisplayPopup(undefined), [setDisplayPopup]); + + return ( + + <> + + + + div': { ml: 'auto', width: '87.5%' }, bottom: 0, height: '36px', position: 'absolute' }}> + + + + + ); +} diff --git a/packages/extension-polkagate/src/fullscreen/manageProfiles/SimpleModalTitle.tsx b/packages/extension-polkagate/src/fullscreen/manageProfiles/SimpleModalTitle.tsx new file mode 100644 index 000000000..53dcad7df --- /dev/null +++ b/packages/extension-polkagate/src/fullscreen/manageProfiles/SimpleModalTitle.tsx @@ -0,0 +1,53 @@ +// Copyright 2019-2024 @polkadot/extension-polkagate authors & contributors +// SPDX-License-Identifier: Apache-2.0 + +/* eslint-disable react/jsx-max-props-per-line */ + +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { Close as CloseIcon } from '@mui/icons-material'; +import { Grid, Typography, useTheme } from '@mui/material'; +import React, { } from 'react'; + +interface Props { + text: string; + onClose: () => void; + icon?: any +} + +export const SimpleModalTitle = ({ icon, onClose, text }: Props): React.ReactElement => { + const theme = useTheme(); + const isIconVaadin = icon?.startsWith('vaadin') + const isIconFontAwesome = icon?.startsWith('fa') + + return ( + + + + {isIconVaadin + // @ts-ignore + ? + : isIconFontAwesome + ? + : <> + } + + + + {text} + + + + + + + + ); +}; + diff --git a/packages/extension-polkagate/src/hooks/index.ts b/packages/extension-polkagate/src/hooks/index.ts index c201d7a29..36b9314b6 100644 --- a/packages/extension-polkagate/src/hooks/index.ts +++ b/packages/extension-polkagate/src/hooks/index.ts @@ -102,3 +102,4 @@ export { default as useApiWithChain2 } from './useApiWithChain2'; export { default as usePeopleChain } from './usePeopleChain'; export { default as useIdentity } from './useIdentity'; export { default as useReservedDetails } from './useReservedDetails'; +export { default as useProfileAccounts } from './useProfileAccounts'; diff --git a/packages/extension-polkagate/src/hooks/useProfileAccounts.tsx b/packages/extension-polkagate/src/hooks/useProfileAccounts.tsx new file mode 100644 index 000000000..1868afe3e --- /dev/null +++ b/packages/extension-polkagate/src/hooks/useProfileAccounts.tsx @@ -0,0 +1,44 @@ +// Copyright 2019-2024 @polkadot/extension-polkagate authors & contributors +// SPDX-License-Identifier: Apache-2.0 + +import { useEffect, useLayoutEffect, useState } from 'react'; +import type { AccountsOrder } from '../fullscreen/homeFullScreen'; +import { getStorage, watchStorage } from '../components/Loading'; +import { useTranslation } from '.'; + +export default function useProfileAccounts(initialAccountList: AccountsOrder[] | undefined) { + const { t } = useTranslation(); + + const [profile, setProfile] = useState(); + const [profileAccounts, setProfileAccounts] = useState(); + + useEffect(() => { + getStorage('profile').then((res) => { + setProfile(res as string || t('All')); + }).catch(console.error); + + watchStorage('profile', setProfile).catch(console.error); + }, []); + + useLayoutEffect(() => { + if (!initialAccountList) { + return; + } + + switch (profile) { + case t('Local'): + const localAccounts = initialAccountList.filter(({ account: { isExternal } }) => !isExternal); + return setProfileAccounts(localAccounts); + case t('Watch Only'): + const watchOnlyAccounts = initialAccountList.filter(({ account: { isExternal, isQR, isHardware } }) => isExternal && !isQR && !isHardware); + return setProfileAccounts(watchOnlyAccounts); + case t('QR-attached'): + const qrAttachedAccounts = initialAccountList.filter(({ account: { isQR } }) => isQR); + return setProfileAccounts(qrAttachedAccounts); + default: + setProfileAccounts(initialAccountList); + } + }, [profile]); + + return profileAccounts; +} diff --git a/packages/extension-polkagate/src/messaging.ts b/packages/extension-polkagate/src/messaging.ts index ff17fefcb..4acc9b92e 100644 --- a/packages/extension-polkagate/src/messaging.ts +++ b/packages/extension-polkagate/src/messaging.ts @@ -72,7 +72,7 @@ export async function editAccount(address: string, name: string): Promise { return sendMessage('pri(accounts.updateMeta)', { address, meta }); } @@ -80,7 +80,7 @@ export async function updateMeta(address: string, meta: string): Promise { return sendMessage('pri(extension.lock)'); } - +// ------------------------------------- export async function showAccount(address: string, isShowing: boolean): Promise { return sendMessage('pri(accounts.show)', { address, isShowing }); } From 371d2c4ac0286ca7b3146b060e7c8f95d243464f Mon Sep 17 00:00:00 2001 From: Nick Date: Wed, 19 Jun 2024 01:08:59 +0330 Subject: [PATCH 02/36] add userDefinedProfiles --- packages/extension-base/src/background/types.ts | 1 + .../src/fullscreen/homeFullScreen/index.tsx | 2 -- .../homeFullScreen/partials/ProfileTabs.tsx | 14 +++++++++++++- .../src/hooks/useProfileAccounts.tsx | 5 ++++- 4 files changed, 18 insertions(+), 4 deletions(-) diff --git a/packages/extension-base/src/background/types.ts b/packages/extension-base/src/background/types.ts index 44d50d3cc..c233f8b4c 100644 --- a/packages/extension-base/src/background/types.ts +++ b/packages/extension-base/src/background/types.ts @@ -54,6 +54,7 @@ export interface AccountJson extends KeyringPair$Meta { balances?: string; identities?: string; isQR?: boolean; + profile?: string; stakingAccount?: string; } diff --git a/packages/extension-polkagate/src/fullscreen/homeFullScreen/index.tsx b/packages/extension-polkagate/src/fullscreen/homeFullScreen/index.tsx index 543ce173e..6896e5281 100644 --- a/packages/extension-polkagate/src/fullscreen/homeFullScreen/index.tsx +++ b/packages/extension-polkagate/src/fullscreen/homeFullScreen/index.tsx @@ -35,8 +35,6 @@ export default function HomePageFullScreen(): React.ReactElement { const profileAccounts = useProfileAccounts(initialAccountList) as AccountsOrder[] | undefined; - console.log('profileAccounts', profileAccounts) - useEffect(() => { if (accountsInExtension && accountsInExtension?.length === 0) { onAction('/onboarding'); diff --git a/packages/extension-polkagate/src/fullscreen/homeFullScreen/partials/ProfileTabs.tsx b/packages/extension-polkagate/src/fullscreen/homeFullScreen/partials/ProfileTabs.tsx index afe0c67aa..8107a15e1 100644 --- a/packages/extension-polkagate/src/fullscreen/homeFullScreen/partials/ProfileTabs.tsx +++ b/packages/extension-polkagate/src/fullscreen/homeFullScreen/partials/ProfileTabs.tsx @@ -43,7 +43,7 @@ function Tab({ text, onClick }: TabProps): React.ReactElement { borderBottomLeftRadius: '12px', WebkitBorderBottomRightRadius: '12px', minWidth: '100px', - borderBottom: profile === text ?`1.5px solid ${theme.palette.secondary.light}`: undefined + borderBottom: profile === text ? `1.5px solid ${theme.palette.secondary.light}` : undefined }}> {text} @@ -55,6 +55,11 @@ function Tab({ text, onClick }: TabProps): React.ReactElement { export default function ProfileTabs({ orderedAccounts }: Props): React.ReactElement { const { t } = useTranslation(); + const userDefinedProfiles = useMemo(() => { + const profiles = orderedAccounts?.map(({ account: { profile } }) => profile)?.filter((item) => !!item); + return [...new Set(profiles)]; + }, [orderedAccounts]); + const hasLocal = useMemo(() => orderedAccounts?.find(({ account }) => !account.isExternal) , [orderedAccounts]); @@ -95,6 +100,13 @@ export default function ProfileTabs({ orderedAccounts }: Props): React.ReactElem onClick={onTabClick} /> } + {userDefinedProfiles?.map((profile) => ( + + )) + } ); } diff --git a/packages/extension-polkagate/src/hooks/useProfileAccounts.tsx b/packages/extension-polkagate/src/hooks/useProfileAccounts.tsx index 1868afe3e..54e17f0d0 100644 --- a/packages/extension-polkagate/src/hooks/useProfileAccounts.tsx +++ b/packages/extension-polkagate/src/hooks/useProfileAccounts.tsx @@ -26,6 +26,8 @@ export default function useProfileAccounts(initialAccountList: AccountsOrder[] | } switch (profile) { + case t('All'): + return setProfileAccounts(initialAccountList); case t('Local'): const localAccounts = initialAccountList.filter(({ account: { isExternal } }) => !isExternal); return setProfileAccounts(localAccounts); @@ -36,7 +38,8 @@ export default function useProfileAccounts(initialAccountList: AccountsOrder[] | const qrAttachedAccounts = initialAccountList.filter(({ account: { isQR } }) => isQR); return setProfileAccounts(qrAttachedAccounts); default: - setProfileAccounts(initialAccountList); + const useDefinedProfile = initialAccountList.filter(({ account }) => account?.profile && account.profile === profile); + return setProfileAccounts(useDefinedProfile); } }, [profile]); From 02e5c93306ef1c6ae7fdb15b3d8fdb0f52f951f2 Mon Sep 17 00:00:00 2001 From: Nick Date: Wed, 19 Jun 2024 01:22:14 +0330 Subject: [PATCH 03/36] add user profile to menu --- .../homeFullScreen/partials/ProfileMenu.tsx | 46 +++++++++++++------ .../manageProfiles/ManageProfileModal.tsx | 2 - 2 files changed, 31 insertions(+), 17 deletions(-) diff --git a/packages/extension-polkagate/src/fullscreen/homeFullScreen/partials/ProfileMenu.tsx b/packages/extension-polkagate/src/fullscreen/homeFullScreen/partials/ProfileMenu.tsx index cf4c6ddd7..04460a706 100644 --- a/packages/extension-polkagate/src/fullscreen/homeFullScreen/partials/ProfileMenu.tsx +++ b/packages/extension-polkagate/src/fullscreen/homeFullScreen/partials/ProfileMenu.tsx @@ -3,15 +3,14 @@ /* eslint-disable react/jsx-max-props-per-line */ -import '@vaadin/icons'; - import { Divider, Grid, Popover, useTheme } from '@mui/material'; -import React, { useCallback, useContext, useState } from 'react'; +import React, { useCallback, useContext, useState, useMemo } from 'react'; -import { ActionContext, MenuItem } from '../../../components'; +import { AccountContext, ActionContext, MenuItem, VaadinIcon } from '../../../components'; import { useInfo, useTranslation } from '../../../hooks'; import { PROXY_CHAINS } from '../../../util/constants'; import { POPUPS_NUMBER } from './AccountInformationForHome'; +import { updateMeta } from '../../../messaging'; interface Props { address: string | undefined; @@ -22,10 +21,17 @@ function ProfileMenu({ address, setDisplayPopup }: Props): React.ReactElement(); + const userDefinedProfiles = useMemo(() => { + const profiles = accounts?.map(({ profile }) => profile)?.filter((item) => !!item); + return [...new Set(profiles)]; + }, [accounts]); + const handleClose = useCallback(() => { setAnchorEl(null); }, []); @@ -38,6 +44,15 @@ function ProfileMenu({ address, setDisplayPopup }: Props): React.ReactElement { + const metaData = JSON.stringify({ profile: profile }); + + updateMeta(String(address), metaData) + .then(() => { + handleClose(); + }).catch(console.error); + }, [address]); + const isDisable = useCallback((supportedChains: string[]) => { if (!chain) { return true; @@ -50,21 +65,23 @@ function ProfileMenu({ address, setDisplayPopup }: Props): React.ReactElement + } onClick={onManageProfiles} text={t('New profile')} withHoverEffect /> - - } - onClick={onManageProfiles} - text={t('Others')} - withHoverEffect - /> + {userDefinedProfiles?.map((profile) => ( + + } + onClick={() => addToNewProfile(profile as string)} + text={profile as string} + withHoverEffect + /> + ))} ); @@ -76,8 +93,7 @@ function ProfileMenu({ address, setDisplayPopup }: Props): React.ReactElement + } text={t('Add account to profile')} withHoverEffect diff --git a/packages/extension-polkagate/src/fullscreen/manageProfiles/ManageProfileModal.tsx b/packages/extension-polkagate/src/fullscreen/manageProfiles/ManageProfileModal.tsx index dc4515e34..39ae7de46 100644 --- a/packages/extension-polkagate/src/fullscreen/manageProfiles/ManageProfileModal.tsx +++ b/packages/extension-polkagate/src/fullscreen/manageProfiles/ManageProfileModal.tsx @@ -31,8 +31,6 @@ export default function ManageProfileModal({ address, setDisplayPopup }: Props): const editName = useCallback((newName: string | null) => { setNewName(newName ?? ''); }, []); - console.log('account', account) - const addToNewProfile = useCallback(() => { if (!newName) { From 2b8b03b042cce958947aea6353830d9ae4753055 Mon Sep 17 00:00:00 2001 From: Nick Date: Wed, 19 Jun 2024 01:32:33 +0330 Subject: [PATCH 04/36] add No user profile text --- .../homeFullScreen/partials/ProfileMenu.tsx | 24 +++++++++++++------ 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/packages/extension-polkagate/src/fullscreen/homeFullScreen/partials/ProfileMenu.tsx b/packages/extension-polkagate/src/fullscreen/homeFullScreen/partials/ProfileMenu.tsx index 04460a706..63b17d240 100644 --- a/packages/extension-polkagate/src/fullscreen/homeFullScreen/partials/ProfileMenu.tsx +++ b/packages/extension-polkagate/src/fullscreen/homeFullScreen/partials/ProfileMenu.tsx @@ -72,16 +72,26 @@ function ProfileMenu({ address, setDisplayPopup }: Props): React.ReactElement - {userDefinedProfiles?.map((profile) => ( - ( + + } + onClick={() => addToNewProfile(profile as string)} + text={profile as string} + withHoverEffect + /> + )) + : + } - onClick={() => addToNewProfile(profile as string)} - text={profile as string} + text={t('No user profile')} withHoverEffect /> - ))} + } ); @@ -95,7 +105,7 @@ function ProfileMenu({ address, setDisplayPopup }: Props): React.ReactElement } - text={t('Add account to profile')} + text={t('Add to profile')} withHoverEffect /> From c8c1f0000727b053fd3d14cb27a67a892a25a453 Mon Sep 17 00:00:00 2001 From: Nick Date: Wed, 19 Jun 2024 02:18:38 +0330 Subject: [PATCH 05/36] add new profile text box in place and sort user defined profiles --- .../partials/AccountInformationForHome.tsx | 13 +-- .../partials/FullScreenAccountMenu.tsx | 2 +- .../homeFullScreen/partials/ProfileMenu.tsx | 59 +++++++++---- .../homeFullScreen/partials/ProfileTabs.tsx | 2 +- .../partials}/SimpleModalTitle.tsx | 0 .../manageProfiles/ManageProfileModal.tsx | 83 ------------------- .../public/locales/en/translation.json | 10 ++- 7 files changed, 57 insertions(+), 112 deletions(-) rename packages/extension-polkagate/src/fullscreen/{manageProfiles => homeFullScreen/partials}/SimpleModalTitle.tsx (100%) delete mode 100644 packages/extension-polkagate/src/fullscreen/manageProfiles/ManageProfileModal.tsx diff --git a/packages/extension-polkagate/src/fullscreen/homeFullScreen/partials/AccountInformationForHome.tsx b/packages/extension-polkagate/src/fullscreen/homeFullScreen/partials/AccountInformationForHome.tsx index ad6c1d291..87f34f83d 100644 --- a/packages/extension-polkagate/src/fullscreen/homeFullScreen/partials/AccountInformationForHome.tsx +++ b/packages/extension-polkagate/src/fullscreen/homeFullScreen/partials/AccountInformationForHome.tsx @@ -4,6 +4,8 @@ /* eslint-disable react/jsx-first-prop-new-line */ /* eslint-disable react/jsx-max-props-per-line */ +import type { FetchedBalance } from '../../../hooks/useAssetsBalances'; + import { ArrowForwardIos as ArrowForwardIosIcon, MoreVert as MoreVertIcon } from '@mui/icons-material'; import { Box, Button, Divider, Grid, IconButton, Skeleton, Typography, useTheme } from '@mui/material'; import React, { useCallback, useContext, useMemo, useState } from 'react'; @@ -15,7 +17,6 @@ import { stars6Black, stars6White } from '../../../assets/icons'; import { ActionContext, Identicon, Identity, Infotip, OptionalCopyButton, ShortAddress2, VaadinIcon } from '../../../components'; import { nFormatter } from '../../../components/FormatPrice'; import { useCurrency, useIdentity, useInfo, usePrices, useTranslation } from '../../../hooks'; -import { FetchedBalance } from '../../../hooks/useAssetsBalances'; import { showAccount, tieAccount } from '../../../messaging'; import ExportAccountModal from '../../../popup/export/ExportAccountModal'; import ForgetAccountModal from '../../../popup/forgetAccount/ForgetAccountModal'; @@ -26,7 +27,7 @@ import AccountIconsFs from '../../accountDetails/components/AccountIconsFs'; import AOC from '../../accountDetails/components/AOC'; import { openOrFocusTab } from '../../accountDetails/components/CommonTasks'; import FullScreenAccountMenu from './FullScreenAccountMenu'; -import ManageProfileModal from '../../manageProfiles/ManageProfileModal'; +import type { BalancesInfo } from '@polkadot/extension-polkagate/util/types'; interface AddressDetailsProps { accountAssets: FetchedBalance[] | null | undefined; @@ -68,7 +69,7 @@ export default function AccountInformationForHome({ accountAssets, address, hide } else { const sortedAssets = accountAssets.sort((a, b) => calculatePrice(b.totalBalance, b.decimal, pricesInCurrencies.prices?.[b.priceId]?.value ?? 0) - calculatePrice(a.totalBalance, a.decimal, pricesInCurrencies.prices?.[a.priceId]?.value ?? 0)); - return sortedAssets.filter((_asset) => !getValue('total', _asset)?.isZero()); + return sortedAssets.filter((_asset) => !getValue('total', _asset as unknown as BalancesInfo)?.isZero()); } }, [accountAssets, calculatePrice, pricesInCurrencies]); @@ -243,12 +244,6 @@ export default function AccountInformationForHome({ accountAssets, address, hide setDisplayPopup={setDisplayPopup} /> } - {displayPopup === POPUPS_NUMBER.MANAGE_PROFILE && address && - - } ); } diff --git a/packages/extension-polkagate/src/fullscreen/homeFullScreen/partials/FullScreenAccountMenu.tsx b/packages/extension-polkagate/src/fullscreen/homeFullScreen/partials/FullScreenAccountMenu.tsx index c1c79b082..1b853b989 100644 --- a/packages/extension-polkagate/src/fullscreen/homeFullScreen/partials/FullScreenAccountMenu.tsx +++ b/packages/extension-polkagate/src/fullscreen/homeFullScreen/partials/FullScreenAccountMenu.tsx @@ -121,7 +121,7 @@ function FullScreenAccountMenu({ address, baseButton, setDisplayPopup }: Props): {hasPrivateKey && >; + setUpperAnchorEl: React.Dispatch> } -function ProfileMenu({ address, setDisplayPopup }: Props): React.ReactElement { +function ProfileMenu({ address, setUpperAnchorEl }: Props): React.ReactElement { const theme = useTheme(); const { t } = useTranslation(); const { chain } = useInfo(address); @@ -26,25 +25,37 @@ function ProfileMenu({ address, setDisplayPopup }: Props): React.ReactElement(); + const [showName, setShowName] = useState(); + const [newName, setNewName] = useState(); + + const editName = useCallback((newName: string | null) => { + setNewName(newName ?? ''); + }, []); const userDefinedProfiles = useMemo(() => { const profiles = accounts?.map(({ profile }) => profile)?.filter((item) => !!item); - return [...new Set(profiles)]; + return [...new Set(profiles)].sort(); }, [accounts]); const handleClose = useCallback(() => { setAnchorEl(null); + setShowName(false); + setUpperAnchorEl(null); }, []); const onClick = useCallback((event: React.MouseEvent) => { setAnchorEl(event.currentTarget); }, []); - const onManageProfiles = useCallback(() => { - setDisplayPopup(POPUPS_NUMBER.MANAGE_PROFILE); + const onNewProfile = useCallback(() => { + setShowName(true); }, [address, chain, onAction]); - const addToNewProfile = useCallback((profile: string) => { + const addToNewProfile = useCallback((profile?: string) => { + if (!profile) { + return; + } + const metaData = JSON.stringify({ profile: profile }); updateMeta(String(address), metaData) @@ -63,14 +74,28 @@ function ProfileMenu({ address, setDisplayPopup }: Props): React.ReactElement ( - - } - onClick={onManageProfiles} - text={t('New profile')} - withHoverEffect - /> + {showName + ? addToNewProfile(newName as string)} + placeholder={t('Profile Name')} + value={newName} + /> + : + } + onClick={onNewProfile} + text={t('New profile')} + withHoverEffect + /> + } {userDefinedProfiles?.length ? userDefinedProfiles?.map((profile) => ( @@ -96,7 +121,7 @@ function ProfileMenu({ address, setDisplayPopup }: Props): React.ReactElement diff --git a/packages/extension-polkagate/src/fullscreen/homeFullScreen/partials/ProfileTabs.tsx b/packages/extension-polkagate/src/fullscreen/homeFullScreen/partials/ProfileTabs.tsx index 8107a15e1..8a9b98970 100644 --- a/packages/extension-polkagate/src/fullscreen/homeFullScreen/partials/ProfileTabs.tsx +++ b/packages/extension-polkagate/src/fullscreen/homeFullScreen/partials/ProfileTabs.tsx @@ -57,7 +57,7 @@ export default function ProfileTabs({ orderedAccounts }: Props): React.ReactElem const userDefinedProfiles = useMemo(() => { const profiles = orderedAccounts?.map(({ account: { profile } }) => profile)?.filter((item) => !!item); - return [...new Set(profiles)]; + return [...new Set(profiles)].sort(); }, [orderedAccounts]); const hasLocal = useMemo(() => diff --git a/packages/extension-polkagate/src/fullscreen/manageProfiles/SimpleModalTitle.tsx b/packages/extension-polkagate/src/fullscreen/homeFullScreen/partials/SimpleModalTitle.tsx similarity index 100% rename from packages/extension-polkagate/src/fullscreen/manageProfiles/SimpleModalTitle.tsx rename to packages/extension-polkagate/src/fullscreen/homeFullScreen/partials/SimpleModalTitle.tsx diff --git a/packages/extension-polkagate/src/fullscreen/manageProfiles/ManageProfileModal.tsx b/packages/extension-polkagate/src/fullscreen/manageProfiles/ManageProfileModal.tsx deleted file mode 100644 index 39ae7de46..000000000 --- a/packages/extension-polkagate/src/fullscreen/manageProfiles/ManageProfileModal.tsx +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright 2019-2024 @polkadot/extension-polkagate authors & contributors -// SPDX-License-Identifier: Apache-2.0 - -/* eslint-disable react/jsx-max-props-per-line */ - - -import { Grid } from '@mui/material'; -import React, { useCallback, useState } from 'react'; - -import { ButtonWithCancel, NewAddress } from '../../components'; -import { useTranslation } from 'react-i18next'; -import { DraggableModal } from '../governance/components/DraggableModal'; -import { useInfo } from '../../hooks'; -import { SimpleModalTitle } from './SimpleModalTitle'; -import { Name } from '../../partials'; -import { updateMeta } from '../../messaging'; - -interface Props { - address: string; - setDisplayPopup: React.Dispatch>; -} - -export default function ManageProfileModal({ address, setDisplayPopup }: Props): React.ReactElement { - const { t } = useTranslation(); - - const { account } = useInfo(address); - - const [isBusy, setIsBusy] = useState(false); - const [newName, setNewName] = useState(); - - const editName = useCallback((newName: string | null) => { - setNewName(newName ?? ''); - }, []); - - const addToNewProfile = useCallback(() => { - if (!newName) { - return; - } - - setIsBusy(true); - const metaData = JSON.stringify({ profile: newName }); - - updateMeta(String(address), metaData) - .then(() => { - setIsBusy(false); - onClose(); - }).catch(console.error); - }, [address, newName]); - - const onClose = useCallback(() => setDisplayPopup(undefined), [setDisplayPopup]); - - return ( - - <> - - - - div': { ml: 'auto', width: '87.5%' }, bottom: 0, height: '36px', position: 'absolute' }}> - - - - - ); -} diff --git a/packages/extension/public/locales/en/translation.json b/packages/extension/public/locales/en/translation.json index b2ce1daae..df71f65bc 100644 --- a/packages/extension/public/locales/en/translation.json +++ b/packages/extension/public/locales/en/translation.json @@ -1288,5 +1288,13 @@ "Comm": "", "Reasons": "", "Please wait a few seconds and don't close the window.": "", - "Reserved Reasons": "" + "Reserved Reasons": "", + "Choose a name for your new profile.": "", + "Profile Name": "", + "New profile": "", + "No user profile": "", + "Add to profile": "", + "Local": "", + "Watch Only": "", + "Manage Profiles": "" } From 72f9000b2c27083d76e9f4e12daf90aeeead9b7e Mon Sep 17 00:00:00 2001 From: Nick Date: Wed, 19 Jun 2024 14:15:08 +0330 Subject: [PATCH 06/36] add "Remove from {{profileName}} profile" Update useProfileAccounts.tsx --- .../partials/FullScreenAccountMenu.tsx | 5 ++- .../homeFullScreen/partials/ProfileMenu.tsx | 41 ++++++++++++++++--- .../homeFullScreen/partials/ProfileTabs.tsx | 13 +++++- .../src/hooks/useProfileAccounts.tsx | 4 +- 4 files changed, 52 insertions(+), 11 deletions(-) diff --git a/packages/extension-polkagate/src/fullscreen/homeFullScreen/partials/FullScreenAccountMenu.tsx b/packages/extension-polkagate/src/fullscreen/homeFullScreen/partials/FullScreenAccountMenu.tsx index 1b853b989..042e4232f 100644 --- a/packages/extension-polkagate/src/fullscreen/homeFullScreen/partials/FullScreenAccountMenu.tsx +++ b/packages/extension-polkagate/src/fullscreen/homeFullScreen/partials/FullScreenAccountMenu.tsx @@ -23,11 +23,14 @@ interface Props { function FullScreenAccountMenu({ address, baseButton, setDisplayPopup }: Props): React.ReactElement { const theme = useTheme(); const { t } = useTranslation(); + const { account, chain } = useInfo(address); const onAction = useContext(ActionContext); const [anchorEl, setAnchorEl] = React.useState(null); + const hasPrivateKey = !(account?.isExternal || account?.isHardware); + const handleClose = useCallback(() => { setAnchorEl(null); }, []); @@ -36,8 +39,6 @@ function FullScreenAccountMenu({ address, baseButton, setDisplayPopup }: Props): setAnchorEl(event.currentTarget); }, []); - const hasPrivateKey = !(account?.isExternal || account?.isHardware); - const onForgetAccount = useCallback(() => { account && setDisplayPopup(POPUPS_NUMBER.FORGET_ACCOUNT); handleClose(); diff --git a/packages/extension-polkagate/src/fullscreen/homeFullScreen/partials/ProfileMenu.tsx b/packages/extension-polkagate/src/fullscreen/homeFullScreen/partials/ProfileMenu.tsx index 90d047157..a1d5dc606 100644 --- a/packages/extension-polkagate/src/fullscreen/homeFullScreen/partials/ProfileMenu.tsx +++ b/packages/extension-polkagate/src/fullscreen/homeFullScreen/partials/ProfileMenu.tsx @@ -19,7 +19,7 @@ interface Props { function ProfileMenu({ address, setUpperAnchorEl }: Props): React.ReactElement { const theme = useTheme(); const { t } = useTranslation(); - const { chain } = useInfo(address); + const { account, chain } = useInfo(address); const onAction = useContext(ActionContext); const { accounts } = useContext(AccountContext); @@ -32,6 +32,8 @@ function ProfileMenu({ address, setUpperAnchorEl }: Props): React.ReactElement

{ const profiles = accounts?.map(({ profile }) => profile)?.filter((item) => !!item); return [...new Set(profiles)].sort(); @@ -43,7 +45,7 @@ function ProfileMenu({ address, setUpperAnchorEl }: Props): React.ReactElement

) => { + const onAddClick = useCallback((event: React.MouseEvent) => { setAnchorEl(event.currentTarget); }, []); @@ -64,6 +66,19 @@ function ProfileMenu({ address, setUpperAnchorEl }: Props): React.ReactElement

{ + if (!account) { + return; + } + + const metaData = JSON.stringify({ profile: null }); + + updateMeta(String(address), metaData) + .then(() => { + handleClose(); + }).catch(console.error); + }, [address, account]); + const isDisable = useCallback((supportedChains: string[]) => { if (!chain) { return true; @@ -80,7 +95,7 @@ function ProfileMenu({ address, setUpperAnchorEl }: Props): React.ReactElement

addToNewProfile(newName as string)} @@ -101,7 +116,7 @@ function ProfileMenu({ address, setUpperAnchorEl }: Props): React.ReactElement

( + } onClick={() => addToNewProfile(profile as string)} text={profile as string} @@ -125,15 +140,29 @@ function ProfileMenu({ address, setUpperAnchorEl }: Props): React.ReactElement

- + + } text={t('Add to profile')} withHoverEffect /> + {!!profileName && + <> + + + } + text={t('Remove from {{profileName}} profile', { replace: { profileName } })} + withHoverEffect + /> + + + + } {text} @@ -72,6 +82,7 @@ export default function ProfileTabs({ orderedAccounts }: Props): React.ReactElem orderedAccounts?.find(({ account: { isQR } }) => isQR) , [orderedAccounts]); + /** Save the current selected tab in local storage on tab click */ const onTabClick = useCallback((event: any) => { setStorage('profile', event.target.innerText); }, []); diff --git a/packages/extension-polkagate/src/hooks/useProfileAccounts.tsx b/packages/extension-polkagate/src/hooks/useProfileAccounts.tsx index 54e17f0d0..0efe625ba 100644 --- a/packages/extension-polkagate/src/hooks/useProfileAccounts.tsx +++ b/packages/extension-polkagate/src/hooks/useProfileAccounts.tsx @@ -41,7 +41,7 @@ export default function useProfileAccounts(initialAccountList: AccountsOrder[] | const useDefinedProfile = initialAccountList.filter(({ account }) => account?.profile && account.profile === profile); return setProfileAccounts(useDefinedProfile); } - }, [profile]); + }, [profile, initialAccountList]); - return profileAccounts; + return profileAccounts && (profileAccounts?.length ? profileAccounts : initialAccountList); } From ae068a18efb17331dd32598e1a25c517ce06271d Mon Sep 17 00:00:00 2001 From: Nick Date: Wed, 19 Jun 2024 16:49:30 +0330 Subject: [PATCH 07/36] add EyeIconFullScreen --- .../AccountInformationForDetails.tsx | 30 ++++++++++++------- .../partials/AccountInformationForHome.tsx | 14 ++++----- 2 files changed, 27 insertions(+), 17 deletions(-) diff --git a/packages/extension-polkagate/src/fullscreen/accountDetails/components/AccountInformationForDetails.tsx b/packages/extension-polkagate/src/fullscreen/accountDetails/components/AccountInformationForDetails.tsx index 58b9cb630..ac8a2ff68 100644 --- a/packages/extension-polkagate/src/fullscreen/accountDetails/components/AccountInformationForDetails.tsx +++ b/packages/extension-polkagate/src/fullscreen/accountDetails/components/AccountInformationForDetails.tsx @@ -1,6 +1,5 @@ // Copyright 2019-2024 @polkadot/extension-polkagate authors & contributors // SPDX-License-Identifier: Apache-2.0 -// @ts-nocheck /* eslint-disable react/jsx-max-props-per-line */ @@ -11,7 +10,7 @@ import { BN } from '@polkadot/util'; import { DisplayLogo, FormatBalance2, FormatPrice, Identicon, Identity, Infotip, Infotip2, OptionalCopyButton, ShortAddress2, VaadinIcon } from '../../../components'; import { useIdentity, useInfo, useTranslation } from '../../../hooks'; -import { FetchedBalance } from '../../../hooks/useAssetsBalances'; +import type { FetchedBalance } from '../../../hooks/useAssetsBalances'; import { showAccount, tieAccount } from '../../../messaging'; import { getValue } from '../../../popup/account/util'; import { BALANCES_VALIDITY_PERIOD } from '../../../util/constants'; @@ -129,9 +128,21 @@ interface AddressDetailsProps { setAssetIdOnAssetHub: React.Dispatch>; } -export default function AccountInformationForDetails({ accountAssets, address, label, price, pricesInCurrency, selectedAsset, setAssetIdOnAssetHub, setSelectedAsset }: AddressDetailsProps): React.ReactElement { +export const EyeIconFullScreen = ({ isHidden, onClick }: { isHidden: boolean | undefined, onClick?: React.MouseEventHandler | undefined }) => { const { t } = useTranslation(); const theme = useTheme(); + + return ( + + ) +} + +export default function AccountInformationForDetails({ accountAssets, address, label, price, pricesInCurrency, selectedAsset, setAssetIdOnAssetHub, setSelectedAsset }: AddressDetailsProps): React.ReactElement { + const theme = useTheme(); const { account, api, chain, formatted, genesisHash, token } = useInfo(address); const accountInfo = useIdentity(genesisHash, formatted); @@ -160,7 +171,7 @@ export default function AccountInformationForDetails({ accountAssets, address, l if (!sortedAccountAssets) { return sortedAccountAssets; // null or undefined! } else { - return sortedAccountAssets.filter((_asset) => !getValue('total', _asset)?.isZero()); + return sortedAccountAssets.filter((_asset) => !getValue('total', _asset as unknown as BalancesInfo)?.isZero()); } }, [sortedAccountAssets]); @@ -217,11 +228,10 @@ export default function AccountInformationForDetails({ accountAssets, address, l // subIdOnly /> - + @@ -234,7 +244,7 @@ export default function AccountInformationForDetails({ accountAssets, address, l - + From 0a862d722a3aff4e7118d00f119a42f92f717dd6 Mon Sep 17 00:00:00 2001 From: Nick Date: Wed, 19 Jun 2024 16:49:58 +0330 Subject: [PATCH 08/36] add hasLedger and refactor --- .../homeFullScreen/partials/ProfileTabs.tsx | 28 +++++++++++-------- 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/packages/extension-polkagate/src/fullscreen/homeFullScreen/partials/ProfileTabs.tsx b/packages/extension-polkagate/src/fullscreen/homeFullScreen/partials/ProfileTabs.tsx index f0389d687..683c7f85d 100644 --- a/packages/extension-polkagate/src/fullscreen/homeFullScreen/partials/ProfileTabs.tsx +++ b/packages/extension-polkagate/src/fullscreen/homeFullScreen/partials/ProfileTabs.tsx @@ -16,15 +16,19 @@ interface Props { interface TabProps { text: string; - onClick: (event: any) => void; } -function Tab({ text, onClick }: TabProps): React.ReactElement { +function Tab({ text }: TabProps): React.ReactElement { const { t } = useTranslation(); const theme = useTheme(); const [profile, setProfile] = useState(); + /** Save the current selected tab in local storage on tab click */ + const onClick = useCallback((event: any) => { + setStorage('profile', event.target.innerText); + }, []); + useEffect(() => { getStorage('profile').then((res) => { setProfile(res as string || t('All')); @@ -74,6 +78,10 @@ export default function ProfileTabs({ orderedAccounts }: Props): React.ReactElem orderedAccounts?.find(({ account }) => !account.isExternal) , [orderedAccounts]); + const hasLedger = useMemo(() => + orderedAccounts?.find(({ account }) => account.isHardware) + , [orderedAccounts]); + const hasWatchOnly = useMemo(() => orderedAccounts?.find(({ account }) => account.isExternal && !account.isQR && !account.isHardware) , [orderedAccounts]); @@ -82,39 +90,35 @@ export default function ProfileTabs({ orderedAccounts }: Props): React.ReactElem orderedAccounts?.find(({ account: { isQR } }) => isQR) , [orderedAccounts]); - /** Save the current selected tab in local storage on tab click */ - const onTabClick = useCallback((event: any) => { - setStorage('profile', event.target.innerText); - }, []); - + // TODO: can put all texts in an array return ( {hasLocal && + } + {hasLedger && + } {hasWatchOnly && } {hasQrAttached && } {userDefinedProfiles?.map((profile) => ( )) } From 5c8161124517230efd332c64283d2463f7fce2c4 Mon Sep 17 00:00:00 2001 From: Nick Date: Wed, 19 Jun 2024 16:51:54 +0330 Subject: [PATCH 09/36] add extra text text: This account is visible to websites --- packages/extension-polkagate/src/popup/home/AccountDetail.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/extension-polkagate/src/popup/home/AccountDetail.tsx b/packages/extension-polkagate/src/popup/home/AccountDetail.tsx index 13546ca09..91e8cccda 100644 --- a/packages/extension-polkagate/src/popup/home/AccountDetail.tsx +++ b/packages/extension-polkagate/src/popup/home/AccountDetail.tsx @@ -44,7 +44,7 @@ const EyeButton = ({ isHidden, toggleVisibility }: EyeProps) => { const theme = useTheme(); return ( -

{!!profileName && From 4950251cc4bb31cd490bb42a3627aa96528efd17 Mon Sep 17 00:00:00 2001 From: Nick Date: Thu, 20 Jun 2024 09:12:23 +0330 Subject: [PATCH 16/36] update translations --- packages/extension/public/locales/en/translation.json | 5 +++-- packages/extension/public/locales/fr/translation.json | 11 ++++++++++- packages/extension/public/locales/hi/translation.json | 11 ++++++++++- packages/extension/public/locales/ru/translation.json | 11 ++++++++++- packages/extension/public/locales/zh/translation.json | 11 ++++++++++- 5 files changed, 43 insertions(+), 6 deletions(-) diff --git a/packages/extension/public/locales/en/translation.json b/packages/extension/public/locales/en/translation.json index df71f65bc..966b4a759 100644 --- a/packages/extension/public/locales/en/translation.json +++ b/packages/extension/public/locales/en/translation.json @@ -1289,12 +1289,13 @@ "Reasons": "", "Please wait a few seconds and don't close the window.": "", "Reserved Reasons": "", - "Choose a name for your new profile.": "", "Profile Name": "", "New profile": "", "No user profile": "", "Add to profile": "", "Local": "", "Watch Only": "", - "Manage Profiles": "" + "This account is visible to websites": "", + "Choose a name for the profile": "", + "Remove from {{profileName}} profile": "" } diff --git a/packages/extension/public/locales/fr/translation.json b/packages/extension/public/locales/fr/translation.json index 3635f2cea..8ddcd6096 100644 --- a/packages/extension/public/locales/fr/translation.json +++ b/packages/extension/public/locales/fr/translation.json @@ -1281,5 +1281,14 @@ "Please transfer some tokens to {{chainName}} People chain.": "Veuillez transférer quelques jetons vers la chaîne des personnes {{chainName}}.", "Reasons": "Raisons", "Please wait a few seconds and don't close the window.": "Veuillez patienter quelques secondes et ne fermez pas la fenêtre.", - "Reserved Reasons": "Raisons réservées" + "Reserved Reasons": "Raisons réservées", + "Profile Name": "Nom de profil", + "New profile": "Nouveau profil", + "No user profile": "Aucun profil utilisateur", + "Add to profile": "Ajouter au profil", + "Local": "Local", + "Watch Only": "Seulement regarder", + "This account is visible to websites": "Ce compte est visible aux sites web", + "Choose a name for the profile": "Choisissez un nom pour le profil", + "Remove from {{profileName}} profile": "Retirer du profil {{profileName}}" } \ No newline at end of file diff --git a/packages/extension/public/locales/hi/translation.json b/packages/extension/public/locales/hi/translation.json index 26b58f5f8..b37041ada 100644 --- a/packages/extension/public/locales/hi/translation.json +++ b/packages/extension/public/locales/hi/translation.json @@ -1278,5 +1278,14 @@ "Please transfer some tokens to {{chainName}} People chain.": "कुछ टोकन {{chainName}} लोगों की श्रृंखला में ट्रांसफर करें.", "Reasons": "कारण", "Please wait a few seconds and don't close the window.": "कृपया कुछ सेकंड प्रतीक्षा करें और विंडो न बंद करें।", - "Reserved Reasons": "आरक्षित कारण" + "Reserved Reasons": "आरक्षित कारण", + "Profile Name": "प्रोफाइल नाम", + "New profile": "नई प्रोफाइल", + "No user profile": "कोई उपयोगकर्ता प्रोफाइल नहीं है", + "Add to profile": "प्रोफाइल में जोड़ें", + "Local": "लोकल", + "Watch Only": "केवल देखें", + "This account is visible to websites": "यह खाता वेबसाइटों के लिए देखा जा सकता है", + "Choose a name for the profile": "प्रोफाइल के लिए नाम चुनें", + "Remove from {{profileName}} profile": "{{profileName}} प्रोफाइल से हटाएं" } \ No newline at end of file diff --git a/packages/extension/public/locales/ru/translation.json b/packages/extension/public/locales/ru/translation.json index 56e2fec14..a9a687ad4 100644 --- a/packages/extension/public/locales/ru/translation.json +++ b/packages/extension/public/locales/ru/translation.json @@ -1276,5 +1276,14 @@ "Please transfer some tokens to {{chainName}} People chain.": "Пожалуйста, переведите некоторые токены в цепь людей {{chainName}}.", "Reasons": "Поводы", "Please wait a few seconds and don't close the window.": "Подождите несколько секунд и не закрывайте окно.", - "Reserved Reasons": "Зарезервированные причины" + "Reserved Reasons": "Зарезервированные причины", + "Profile Name": "Имя профиля", + "New profile": "Новый профиль", + "No user profile": "Нет профиля пользователя", + "Add to profile": "Добавить в профиль", + "Local": "Локальный", + "Watch Only": "Только смотреть", + "This account is visible to websites": "Эта учетная запись доступна веб-сайтам", + "Choose a name for the profile": "Выберите имя для профиля", + "Remove from {{profileName}} profile": "Удалить из профиля {{profileName}}" } \ No newline at end of file diff --git a/packages/extension/public/locales/zh/translation.json b/packages/extension/public/locales/zh/translation.json index c012edc26..92d80aea1 100644 --- a/packages/extension/public/locales/zh/translation.json +++ b/packages/extension/public/locales/zh/translation.json @@ -1285,5 +1285,14 @@ "Please transfer some tokens to {{chainName}} People chain.": "请将一些令牌转移到 {{chainName}} 人链上。", "Reasons": "原因", "Please wait a few seconds and don't close the window.": "请等待几秒钟,不要关闭窗口。", - "Reserved Reasons": "保留原因" + "Reserved Reasons": "保留原因", + "Profile Name": "个人资料名称", + "New profile": "新个人资料", + "No user profile": "无用户个人资料", + "Add to profile": "添加到个人资料", + "Local": "本地", + "Watch Only": "只看", + "This account is visible to websites": "这账户可在网站上查看", + "Choose a name for the profile": "选择个人资料名称", + "Remove from {{profileName}} profile": "从{{profileName}}个人资料中删除" } \ No newline at end of file From 32897dbd6b147d6076bedce32e11444c4093c538 Mon Sep 17 00:00:00 2001 From: Nick Date: Thu, 20 Jun 2024 17:02:14 +0330 Subject: [PATCH 17/36] refactor change Watch Only to Watch-only --- .../src/fullscreen/homeFullScreen/partials/ProfileTabs.tsx | 2 +- packages/extension-polkagate/src/hooks/useProfileAccounts.tsx | 2 +- packages/extension/public/locales/en/translation.json | 1 - packages/extension/public/locales/fr/translation.json | 1 - packages/extension/public/locales/hi/translation.json | 1 - packages/extension/public/locales/ru/translation.json | 1 - packages/extension/public/locales/zh/translation.json | 1 - 7 files changed, 2 insertions(+), 7 deletions(-) diff --git a/packages/extension-polkagate/src/fullscreen/homeFullScreen/partials/ProfileTabs.tsx b/packages/extension-polkagate/src/fullscreen/homeFullScreen/partials/ProfileTabs.tsx index c028f5bde..5e42547cf 100644 --- a/packages/extension-polkagate/src/fullscreen/homeFullScreen/partials/ProfileTabs.tsx +++ b/packages/extension-polkagate/src/fullscreen/homeFullScreen/partials/ProfileTabs.tsx @@ -33,7 +33,7 @@ export default function ProfileTabs({ orderedAccounts }: Props): React.ReactElem const hasWatchOnly = orderedAccounts.find(({ account }) => account.isExternal && !account.isQR && !account.isHardware); if (hasWatchOnly) { - texts.push(t('Watch Only')) + texts.push(t('Watch-only')) } const hasQrAttached = orderedAccounts.find(({ account: { isQR } }) => isQR); diff --git a/packages/extension-polkagate/src/hooks/useProfileAccounts.tsx b/packages/extension-polkagate/src/hooks/useProfileAccounts.tsx index d404baaf2..4af95e504 100644 --- a/packages/extension-polkagate/src/hooks/useProfileAccounts.tsx +++ b/packages/extension-polkagate/src/hooks/useProfileAccounts.tsx @@ -38,7 +38,7 @@ export default function useProfileAccounts(initialAccountList: AccountsOrder[] | case t('Ledger'): const ledgerAccounts = initialAccountList.filter(({ account: { isHardware } }) => isHardware); return setProfileAccounts(ledgerAccounts); - case t('Watch Only'): + case t('Watch-only'): const watchOnlyAccounts = initialAccountList.filter(({ account: { isExternal, isQR, isHardware } }) => isExternal && !isQR && !isHardware); return setProfileAccounts(watchOnlyAccounts); case t('QR-attached'): diff --git a/packages/extension/public/locales/en/translation.json b/packages/extension/public/locales/en/translation.json index 966b4a759..e803ccd55 100644 --- a/packages/extension/public/locales/en/translation.json +++ b/packages/extension/public/locales/en/translation.json @@ -1294,7 +1294,6 @@ "No user profile": "", "Add to profile": "", "Local": "", - "Watch Only": "", "This account is visible to websites": "", "Choose a name for the profile": "", "Remove from {{profileName}} profile": "" diff --git a/packages/extension/public/locales/fr/translation.json b/packages/extension/public/locales/fr/translation.json index 8ddcd6096..0eef57c2b 100644 --- a/packages/extension/public/locales/fr/translation.json +++ b/packages/extension/public/locales/fr/translation.json @@ -1287,7 +1287,6 @@ "No user profile": "Aucun profil utilisateur", "Add to profile": "Ajouter au profil", "Local": "Local", - "Watch Only": "Seulement regarder", "This account is visible to websites": "Ce compte est visible aux sites web", "Choose a name for the profile": "Choisissez un nom pour le profil", "Remove from {{profileName}} profile": "Retirer du profil {{profileName}}" diff --git a/packages/extension/public/locales/hi/translation.json b/packages/extension/public/locales/hi/translation.json index b37041ada..9be91abe1 100644 --- a/packages/extension/public/locales/hi/translation.json +++ b/packages/extension/public/locales/hi/translation.json @@ -1284,7 +1284,6 @@ "No user profile": "कोई उपयोगकर्ता प्रोफाइल नहीं है", "Add to profile": "प्रोफाइल में जोड़ें", "Local": "लोकल", - "Watch Only": "केवल देखें", "This account is visible to websites": "यह खाता वेबसाइटों के लिए देखा जा सकता है", "Choose a name for the profile": "प्रोफाइल के लिए नाम चुनें", "Remove from {{profileName}} profile": "{{profileName}} प्रोफाइल से हटाएं" diff --git a/packages/extension/public/locales/ru/translation.json b/packages/extension/public/locales/ru/translation.json index a9a687ad4..762393408 100644 --- a/packages/extension/public/locales/ru/translation.json +++ b/packages/extension/public/locales/ru/translation.json @@ -1282,7 +1282,6 @@ "No user profile": "Нет профиля пользователя", "Add to profile": "Добавить в профиль", "Local": "Локальный", - "Watch Only": "Только смотреть", "This account is visible to websites": "Эта учетная запись доступна веб-сайтам", "Choose a name for the profile": "Выберите имя для профиля", "Remove from {{profileName}} profile": "Удалить из профиля {{profileName}}" diff --git a/packages/extension/public/locales/zh/translation.json b/packages/extension/public/locales/zh/translation.json index 92d80aea1..700a3b12d 100644 --- a/packages/extension/public/locales/zh/translation.json +++ b/packages/extension/public/locales/zh/translation.json @@ -1291,7 +1291,6 @@ "No user profile": "无用户个人资料", "Add to profile": "添加到个人资料", "Local": "本地", - "Watch Only": "只看", "This account is visible to websites": "这账户可在网站上查看", "Choose a name for the profile": "选择个人资料名称", "Remove from {{profileName}} profile": "从{{profileName}}个人资料中删除" From 597780dcdaa9efb71a049ce65b7f47052c7a3391 Mon Sep 17 00:00:00 2001 From: Nick Date: Thu, 20 Jun 2024 17:04:00 +0330 Subject: [PATCH 18/36] adjust text center --- .../homeFullScreen/partials/ProfileTab.tsx | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/packages/extension-polkagate/src/fullscreen/homeFullScreen/partials/ProfileTab.tsx b/packages/extension-polkagate/src/fullscreen/homeFullScreen/partials/ProfileTab.tsx index fe134e245..b87f0b5b4 100644 --- a/packages/extension-polkagate/src/fullscreen/homeFullScreen/partials/ProfileTab.tsx +++ b/packages/extension-polkagate/src/fullscreen/homeFullScreen/partials/ProfileTab.tsx @@ -84,13 +84,12 @@ export default function ProfileTab({ text, orderedAccounts }: Props): React.Reac return ( - - - {text} - - - - - + + {text} + + ); } From a962e97038711ed3057afb271bf1a567d249459b Mon Sep 17 00:00:00 2001 From: Nick Date: Thu, 20 Jun 2024 17:13:52 +0330 Subject: [PATCH 19/36] fix type issue --- packages/extension-polkagate/src/components/Identity.tsx | 2 +- .../src/fullscreen/accountDetails/components/AccountIconsFs.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/extension-polkagate/src/components/Identity.tsx b/packages/extension-polkagate/src/components/Identity.tsx index 42422c135..90151ae38 100644 --- a/packages/extension-polkagate/src/components/Identity.tsx +++ b/packages/extension-polkagate/src/components/Identity.tsx @@ -21,7 +21,7 @@ import { getSubstrateAddress, isValidAddress } from '../util/utils'; import { ChainLogo, Identicon, Infotip, ShortAddress } from '.'; interface Props { - accountInfo?: DeriveAccountInfo; + accountInfo?: DeriveAccountInfo | null; address?: string | AccountId; api?: ApiPromise; chain?: Chain; diff --git a/packages/extension-polkagate/src/fullscreen/accountDetails/components/AccountIconsFs.tsx b/packages/extension-polkagate/src/fullscreen/accountDetails/components/AccountIconsFs.tsx index bfa2f6c81..95248e95d 100644 --- a/packages/extension-polkagate/src/fullscreen/accountDetails/components/AccountIconsFs.tsx +++ b/packages/extension-polkagate/src/fullscreen/accountDetails/components/AccountIconsFs.tsx @@ -20,7 +20,7 @@ import { IDENTITY_CHAINS, PROXY_CHAINS, SOCIAL_RECOVERY_CHAINS } from '../../../ interface AddressDetailsProps { address: string | undefined; - accountInfo: DeriveAccountInfo | undefined + accountInfo: DeriveAccountInfo | undefined | null } export default function AccountIconsFs({ accountInfo, address }: AddressDetailsProps): React.ReactElement { From f541561ddf13ca8e0a1e79db6db6ff70f4417ca5 Mon Sep 17 00:00:00 2001 From: Nick Date: Thu, 20 Jun 2024 18:39:24 +0330 Subject: [PATCH 20/36] add color to predefined tabs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit and add ✔️ check mark on selected tab --- .../homeFullScreen/partials/ProfileTab.tsx | 31 ++++++++++++++----- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/packages/extension-polkagate/src/fullscreen/homeFullScreen/partials/ProfileTab.tsx b/packages/extension-polkagate/src/fullscreen/homeFullScreen/partials/ProfileTab.tsx index b87f0b5b4..e1b2764af 100644 --- a/packages/extension-polkagate/src/fullscreen/homeFullScreen/partials/ProfileTab.tsx +++ b/packages/extension-polkagate/src/fullscreen/homeFullScreen/partials/ProfileTab.tsx @@ -30,18 +30,35 @@ export default function ProfileTab({ text, orderedAccounts }: Props): React.Reac const { t } = useTranslation(); const theme = useTheme(); + const PREDEFINED_TAB_COLORS = [ + { text: t('All'), colorLight: '#D1C4E9', colorDark: '#5E35B1' }, // Muted Lavender + { text: t('Local'), colorLight: '#C8E6C9', colorDark: '#388E3C' }, // Pastel Green + { text: t('Ledger'), colorLight: '#FFCCBC', colorDark: '#D84315' }, // Soft Peach + { text: t('Watch-only'), colorLight: '#B3E5FC', colorDark: '#0288D1' }, // Light Sky Blue + { text: t('QR-attached'), colorLight: '#F8BBD0', colorDark: '#D81B60' }, // Powder Pink + ]; + const profileAccounts = useProfileAccounts(orderedAccounts, text); const [animate, setAnimate] = useState(true); const [profile, setProfile] = useState(); - /** set by user click on profile tab */ + /** set by user click on a profile tab */ const [toHiddenAll, setToHiddenAll] = useState(); + const getColor = useCallback((_text: string) => { + const selectedProfile = PREDEFINED_TAB_COLORS.find(({ text }) => text === _text); + const color = theme.palette.mode === 'dark' ? selectedProfile?.colorDark : selectedProfile?.colorLight; + + return color; + }, [PREDEFINED_TAB_COLORS]); + + const isSelected = profile === text; + /** Save the current selected tab in local storage on tab click */ const onClick = useCallback(() => { setStorage('profile', text); - profile === text && setToHiddenAll(!toHiddenAll); - }, [profile, toHiddenAll, text]); + isSelected && setToHiddenAll(!toHiddenAll); + }, [profile, toHiddenAll, text, isSelected]); /** check to see if all accounts in a profile is hidden */ const isAllProfileAccountsHidden = useMemo(() => { @@ -89,12 +106,11 @@ export default function ProfileTab({ text, orderedAccounts }: Props): React.Reac sx={{ cursor: 'pointer', mx: '1px', - pb: '2px', - bgcolor: 'background.paper', + bgcolor: getColor(text) || 'background.paper', borderBottomLeftRadius: '12px', WebkitBorderBottomRightRadius: '12px', minWidth: '100px', - borderBottom: profile === text + borderBottom: isSelected ? `1.5px solid ${theme.palette.secondary.light}` : `1.5px solid ${theme.palette.divider}`, transition: 'transform 0.3s, box-shadow 0.3s', @@ -107,10 +123,11 @@ export default function ProfileTab({ text, orderedAccounts }: Props): React.Reac width: 'fit-content', transformOrigin: 'top', }}> + {text} - + ); } From bc5d234d41e60f2ca0c17e62d482d2e46c2de6cb Mon Sep 17 00:00:00 2001 From: Nick Date: Thu, 20 Jun 2024 18:52:54 +0330 Subject: [PATCH 21/36] refactor --- packages/extension-polkagate/src/components/MenuItem.tsx | 5 ++--- .../src/fullscreen/homeFullScreen/partials/ProfileMenu.tsx | 1 + .../src/fullscreen/homeFullScreen/partials/ProfileTabs.tsx | 3 ++- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/extension-polkagate/src/components/MenuItem.tsx b/packages/extension-polkagate/src/components/MenuItem.tsx index c281cfccc..7d4160f75 100644 --- a/packages/extension-polkagate/src/components/MenuItem.tsx +++ b/packages/extension-polkagate/src/components/MenuItem.tsx @@ -1,12 +1,11 @@ // 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 { ArrowForwardIos as ArrowForwardIosIcon } from '@mui/icons-material'; import { Box, Grid, type SxProps, type Theme, Typography } from '@mui/material'; -import React, { MouseEventHandler } from 'react'; +import React, { type MouseEventHandler } from 'react'; import { noop } from '../util/utils'; @@ -59,7 +58,7 @@ export default function MenuItem({ children, disabled = false, fontSize, icon, i - + { diff --git a/packages/extension-polkagate/src/fullscreen/homeFullScreen/partials/ProfileMenu.tsx b/packages/extension-polkagate/src/fullscreen/homeFullScreen/partials/ProfileMenu.tsx index 130182dfb..f38dcf178 100644 --- a/packages/extension-polkagate/src/fullscreen/homeFullScreen/partials/ProfileMenu.tsx +++ b/packages/extension-polkagate/src/fullscreen/homeFullScreen/partials/ProfileMenu.tsx @@ -118,6 +118,7 @@ function ProfileMenu({ address, setUpperAnchorEl }: Props): React.ReactElement

} + key={profile} onClick={() => addToNewProfile(profile as string)} text={profile as string} withHoverEffect diff --git a/packages/extension-polkagate/src/fullscreen/homeFullScreen/partials/ProfileTabs.tsx b/packages/extension-polkagate/src/fullscreen/homeFullScreen/partials/ProfileTabs.tsx index 5e42547cf..677e901a1 100644 --- a/packages/extension-polkagate/src/fullscreen/homeFullScreen/partials/ProfileTabs.tsx +++ b/packages/extension-polkagate/src/fullscreen/homeFullScreen/partials/ProfileTabs.tsx @@ -53,8 +53,9 @@ export default function ProfileTabs({ orderedAccounts }: Props): React.ReactElem return ( { - profiles?.map((profile) => ( + profiles?.map((profile, index) => ( From f7db93f019b571d6849c4594e3c4fb415612f914 Mon Sep 17 00:00:00 2001 From: Nick Date: Sun, 23 Jun 2024 16:38:06 +0330 Subject: [PATCH 22/36] add profile to import proxieds --- .../src/components/ProfileInput.tsx | 109 ++++++++++++++++++ .../src/components/index.ts | 3 +- .../homeFullScreen/partials/ProfileMenu.tsx | 38 ++---- .../homeFullScreen/partials/ProfileTab.tsx | 4 +- .../extension-polkagate/src/hooks/index.ts | 1 + .../src/hooks/useProfiles.ts | 17 +++ .../import/importProxiedFullScreen/index.tsx | 50 +++++--- 7 files changed, 178 insertions(+), 44 deletions(-) create mode 100644 packages/extension-polkagate/src/components/ProfileInput.tsx create mode 100644 packages/extension-polkagate/src/hooks/useProfiles.ts diff --git a/packages/extension-polkagate/src/components/ProfileInput.tsx b/packages/extension-polkagate/src/components/ProfileInput.tsx new file mode 100644 index 000000000..8a95d7495 --- /dev/null +++ b/packages/extension-polkagate/src/components/ProfileInput.tsx @@ -0,0 +1,109 @@ +// Copyright 2019-2024 @polkadot/extension-polkagate authors & contributors +// SPDX-License-Identifier: Apache-2.0 + +import { Autocomplete, Grid, type SxProps, TextField, type Theme, Typography } from '@mui/material'; +import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; + +import Label from './Label'; +import { useProfiles } from '../hooks'; + +interface Props { + disabled?: boolean; + helperText?: string; + label: string; + placeHolder?: string; + profileName: string | undefined; + setProfileName: React.Dispatch>; + style?: SxProps; +} + +export default function ProfileInput({ disabled = false, placeHolder = '', setProfileName, profileName, helperText = '', label, style }: Props): React.ReactElement { + const containerRef = useRef(null); + + const userDefinedProfiles = useProfiles(); + + const [isPopperOpen, setTogglePopper] = useState(false); + const [focus, setFocus] = useState(false); + const [enteredProfile, setEnteredProfile] = useState(); + const [dropdownWidth, setDropdownWidth] = useState('0'); + + const autocompleteOptions = useMemo(() => userDefinedProfiles?.map((profile, index) => ({ index, profile })), [userDefinedProfiles]); + + useEffect(() => { + profileName && setEnteredProfile(profileName); + }, [profileName]); + + useEffect(() => { + if (containerRef) { + setDropdownWidth(`${(containerRef.current?.offsetWidth || 0)}px`); + } + }, [containerRef?.current?.offsetWidth]); + + const handleProfile = useCallback((value?:string): void => { + setTogglePopper(false); + + if (!value) { + setProfileName(undefined); + setEnteredProfile(undefined); + + return; + } + + setEnteredProfile(value); + setProfileName(value) + }, [setProfileName]); + + const openPopper = useCallback(() => + userDefinedProfiles && userDefinedProfiles?.length > 0 && !enteredProfile && !isPopperOpen && setTogglePopper(true) + , [userDefinedProfiles?.length, enteredProfile, isPopperOpen]); + + const closePopper = useCallback(() => + setTogglePopper(false), + []); + + return ( + + + + ); +} diff --git a/packages/extension-polkagate/src/components/index.ts b/packages/extension-polkagate/src/components/index.ts index ba36a7c9e..be00b946e 100644 --- a/packages/extension-polkagate/src/components/index.ts +++ b/packages/extension-polkagate/src/components/index.ts @@ -1,7 +1,5 @@ // Copyright 2019-2024 @polkadot/extension-polkagate authors & contributors // SPDX-License-Identifier: Apache-2.0 -// @ts-nocheck -/* eslint-disable header/header */ export { default as AccountNamePasswordCreation } from './AccountNamePasswordCreation'; export { default as AccountHolderWithProxy } from './AccountHolderWithProxy'; @@ -95,5 +93,6 @@ export { default as FullScreenIcon } from './FullScreenIcon'; export { default as OptionalCopyButton } from './OptionalCopyButton'; export { default as Waiting } from './Waiting'; export { default as VaadinIcon } from './VaadinIcon'; +export { default as ProfileInput } from './ProfileInput'; export * from './contexts'; diff --git a/packages/extension-polkagate/src/fullscreen/homeFullScreen/partials/ProfileMenu.tsx b/packages/extension-polkagate/src/fullscreen/homeFullScreen/partials/ProfileMenu.tsx index f38dcf178..de01880c0 100644 --- a/packages/extension-polkagate/src/fullscreen/homeFullScreen/partials/ProfileMenu.tsx +++ b/packages/extension-polkagate/src/fullscreen/homeFullScreen/partials/ProfileMenu.tsx @@ -4,11 +4,10 @@ /* eslint-disable react/jsx-max-props-per-line */ import { Divider, Grid, Popover, useTheme } from '@mui/material'; -import React, { useCallback, useContext, useState, useMemo } from 'react'; +import React, { useCallback, useContext, useState } from 'react'; -import { AccountContext, ActionContext, InputWithLabel, MenuItem, VaadinIcon } from '../../../components'; -import { useInfo, useTranslation } from '../../../hooks'; -import { PROXY_CHAINS } from '../../../util/constants'; +import { ActionContext, InputWithLabel, MenuItem, VaadinIcon } from '../../../components'; +import { useInfo, useTranslation, useProfiles } from '../../../hooks'; import { updateMeta } from '../../../messaging'; interface Props { @@ -19,26 +18,21 @@ interface Props { function ProfileMenu({ address, setUpperAnchorEl }: Props): React.ReactElement { const theme = useTheme(); const { t } = useTranslation(); - const { account, chain } = useInfo(address); const onAction = useContext(ActionContext); - const { accounts } = useContext(AccountContext); + const { account, chain } = useInfo(address); + const userDefinedProfiles = useProfiles(); const [anchorEl, setAnchorEl] = useState(); const [showName, setShowName] = useState(); const [newName, setNewName] = useState(); + const profileName = account?.profile; + const editName = useCallback((newName: string | null) => { setNewName(newName ?? ''); }, []); - const profileName = account?.profile; - - const userDefinedProfiles = useMemo(() => { - const profiles = accounts?.map(({ profile }) => profile)?.filter((item) => !!item); - return [...new Set(profiles)].sort(); - }, [accounts]); - const handleClose = useCallback(() => { setAnchorEl(null); setShowName(false); @@ -66,7 +60,7 @@ function ProfileMenu({ address, setUpperAnchorEl }: Props): React.ReactElement

{ + const onRemove = useCallback(() => { if (!account) { return; } @@ -79,14 +73,6 @@ function ProfileMenu({ address, setUpperAnchorEl }: Props): React.ReactElement

{ - if (!chain) { - return true; - } else { - return !supportedChains.includes(chain.genesisHash ?? ''); - } - }, [chain]); - const Menus = () => ( {showName @@ -104,7 +90,7 @@ function ProfileMenu({ address, setUpperAnchorEl }: Props): React.ReactElement

: + } onClick={onNewProfile} text={t('New profile')} @@ -116,7 +102,7 @@ function ProfileMenu({ address, setUpperAnchorEl }: Props): React.ReactElement

( + } key={profile} onClick={() => addToNewProfile(profile as string)} @@ -141,7 +127,7 @@ function ProfileMenu({ address, setUpperAnchorEl }: Props): React.ReactElement

- + @@ -153,7 +139,7 @@ function ProfileMenu({ address, setUpperAnchorEl }: Props): React.ReactElement

{!!profileName && <> - + diff --git a/packages/extension-polkagate/src/fullscreen/homeFullScreen/partials/ProfileTab.tsx b/packages/extension-polkagate/src/fullscreen/homeFullScreen/partials/ProfileTab.tsx index e1b2764af..065f9d321 100644 --- a/packages/extension-polkagate/src/fullscreen/homeFullScreen/partials/ProfileTab.tsx +++ b/packages/extension-polkagate/src/fullscreen/homeFullScreen/partials/ProfileTab.tsx @@ -48,11 +48,11 @@ export default function ProfileTab({ text, orderedAccounts }: Props): React.Reac const getColor = useCallback((_text: string) => { const selectedProfile = PREDEFINED_TAB_COLORS.find(({ text }) => text === _text); const color = theme.palette.mode === 'dark' ? selectedProfile?.colorDark : selectedProfile?.colorLight; - + return color; }, [PREDEFINED_TAB_COLORS]); - const isSelected = profile === text; + const isSelected = useMemo(() => profile === text, [profile, text]); /** Save the current selected tab in local storage on tab click */ const onClick = useCallback(() => { diff --git a/packages/extension-polkagate/src/hooks/index.ts b/packages/extension-polkagate/src/hooks/index.ts index 36b9314b6..7f496f1a8 100644 --- a/packages/extension-polkagate/src/hooks/index.ts +++ b/packages/extension-polkagate/src/hooks/index.ts @@ -103,3 +103,4 @@ export { default as usePeopleChain } from './usePeopleChain'; export { default as useIdentity } from './useIdentity'; export { default as useReservedDetails } from './useReservedDetails'; export { default as useProfileAccounts } from './useProfileAccounts'; +export { default as useProfiles } from './useProfiles'; diff --git a/packages/extension-polkagate/src/hooks/useProfiles.ts b/packages/extension-polkagate/src/hooks/useProfiles.ts new file mode 100644 index 000000000..6ca4b6434 --- /dev/null +++ b/packages/extension-polkagate/src/hooks/useProfiles.ts @@ -0,0 +1,17 @@ +// Copyright 2019-2024 @polkadot/extension-polkagate authors & contributors +// SPDX-License-Identifier: Apache-2.0 + +import { useContext, useMemo } from 'react'; +import { AccountContext } from '../components'; + +export default function useProfiles(): string [] | undefined { + const { accounts } = useContext(AccountContext); + + return useMemo(() => { + if(!accounts){ + return; + } + const profiles = accounts.map(({ profile }) => profile).filter(Boolean) as string[]; + return [...new Set(profiles)].sort(); + }, [accounts]); +} diff --git a/packages/extension-polkagate/src/popup/import/importProxiedFullScreen/index.tsx b/packages/extension-polkagate/src/popup/import/importProxiedFullScreen/index.tsx index 99c041a98..3b5b48815 100644 --- a/packages/extension-polkagate/src/popup/import/importProxiedFullScreen/index.tsx +++ b/packages/extension-polkagate/src/popup/import/importProxiedFullScreen/index.tsx @@ -1,6 +1,5 @@ // Copyright 2019-2024 @polkadot/extension-polkagate authors & contributors // SPDX-License-Identifier: Apache-2.0 -// @ts-nocheck /* eslint-disable react/jsx-max-props-per-line */ @@ -13,10 +12,10 @@ import type { Chain } from '@polkadot/extension-chains/types'; import Bread from '@polkadot/extension-polkagate/src/fullscreen/partials/Bread'; import { Title } from '@polkadot/extension-polkagate/src/fullscreen/sendFund/InputPage'; -import { AccountContext, Label, SelectChain, TwoButtons, VaadinIcon } from '../../../components'; +import { AccountContext, ProfileInput, Label, SelectChain, TwoButtons, VaadinIcon } from '../../../components'; import { FullScreenHeader } from '../../../fullscreen/governance/FullScreenHeader'; import { useFullscreen, useGenesisHashOptions, useInfo, useProxiedAccounts, useTranslation } from '../../../hooks'; -import { createAccountExternal, getMetadata, tieAccount } from '../../../messaging'; +import { createAccountExternal, getMetadata, tieAccount, updateMeta } from '../../../messaging'; import { FULLSCREEN_WIDTH, PROXY_CHAINS, WESTEND_GENESIS_HASH } from '../../../util/constants'; import getLogo from '../../../util/getLogo'; import AddressDropdownFullScreen from '../../newAccount/deriveFromAccountsFullscreen/AddressDropdownFullScreen'; @@ -42,6 +41,7 @@ function ImportProxiedFS(): React.ReactElement { const [selectedProxied, setSelectedProxied] = useState([]); const [chain, setChain] = useState(null); const [isBusy, setIsBusy] = useState(false); + const [profileName, setProfileName] = useState(); const proxiedAccounts = useProxiedAccounts(chain ? selectedAddress : undefined); const { api, formatted } = useInfo(chain ? selectedAddress : undefined); @@ -75,19 +75,32 @@ function ImportProxiedFS(): React.ReactElement { setSelectedAddress(address); }, []); - const onImport = useCallback(() => { - setIsBusy(true); - selectedProxied.forEach((address, index) => { - const randomName = (chance?.name() as string)?.split(' ')?.[0] || `Proxied ${index + 1}`; + const createProxids = useCallback(() => { + return new Promise(async (resolve) => { + for (let index = 0; index < selectedProxied.length; index++) { + const address = selectedProxied[index]; + const randomName = (chance?.name() as string)?.split(' ')?.[0] || `Proxied ${index + 1}`; - createAccountExternal(randomName, address, chain?.genesisHash ?? WESTEND_GENESIS_HASH).catch((error: Error) => { - setIsBusy(false); - console.error(error); - }); - }); + await createAccountExternal(randomName, address, chain?.genesisHash ?? WESTEND_GENESIS_HASH) - window.close(); - }, [chain?.genesisHash, chance, selectedProxied]); + /** add the proxied account to the profile if has chosen by user */ + if (profileName) { + const metaData = JSON.stringify({ profile: profileName }); + + await updateMeta(address, metaData); + } + } + resolve(true); + }) + }, [chain?.genesisHash, chance, selectedProxied, profileName]); + + const onImport = useCallback(() => { + setIsBusy(true); + createProxids().then(() => { + setIsBusy(false); + window.close(); + }) + }, [createProxids]); const backHome = useCallback(() => { window.close(); @@ -149,6 +162,15 @@ function ImportProxiedFS(): React.ReactElement { style={{ m: '0 auto' }} /> } + {proxiedAccounts && proxiedAccounts?.proxy === formatted && !!proxiedAccounts.proxied.length && + + } div': { m: 0, width: '64%' }, borderTop: '1px solid', borderTopColor: 'divider', bottom: '40px', height: '50px', justifyContent: 'flex-end', left: '10%', position: 'absolute', width: '80%' }}> Date: Sun, 23 Jun 2024 16:40:47 +0330 Subject: [PATCH 23/36] ignore ts --- packages/extension-polkagate/src/components/ProfileInput.tsx | 1 + .../src/popup/import/importProxiedFullScreen/index.tsx | 1 + 2 files changed, 2 insertions(+) diff --git a/packages/extension-polkagate/src/components/ProfileInput.tsx b/packages/extension-polkagate/src/components/ProfileInput.tsx index 8a95d7495..3863ae9a0 100644 --- a/packages/extension-polkagate/src/components/ProfileInput.tsx +++ b/packages/extension-polkagate/src/components/ProfileInput.tsx @@ -87,6 +87,7 @@ export default function ProfileInput({ disabled = false, placeHolder = '', setPr InputProps={{ ...params.InputProps, }} + // @ts-ignore onChange={() => handleProfile(event?.target?.value)} placeholder={placeHolder} sx={{ '> div.MuiOutlinedInput-root': { '> fieldset': { border: 'none' }, '> input.MuiAutocomplete-input': { border: 'none', lineHeight: '31px', p: 0 }, border: 'none', height: '31px', p: 0, px: '5px' }, bgcolor: 'background.paper', border: `${focus ? '2px' : '1px'} solid`, borderColor: `${focus ? 'action.focus' : 'secondary.light'}`, borderRadius: '5px', height: '32px', lineHeight: '31px' }} diff --git a/packages/extension-polkagate/src/popup/import/importProxiedFullScreen/index.tsx b/packages/extension-polkagate/src/popup/import/importProxiedFullScreen/index.tsx index 3b5b48815..f5fb773a5 100644 --- a/packages/extension-polkagate/src/popup/import/importProxiedFullScreen/index.tsx +++ b/packages/extension-polkagate/src/popup/import/importProxiedFullScreen/index.tsx @@ -4,6 +4,7 @@ /* eslint-disable react/jsx-max-props-per-line */ import { Grid, Typography, useTheme } from '@mui/material'; +// @ts-ignore import Chance from 'chance'; import React, { useCallback, useContext, useMemo, useState } from 'react'; From 974067b1d454265a8786a295eb646d0ce44621dd Mon Sep 17 00:00:00 2001 From: Nick Date: Sun, 23 Jun 2024 16:49:35 +0330 Subject: [PATCH 24/36] update translations --- packages/extension/public/locales/fr/translation.json | 4 +++- packages/extension/public/locales/hi/translation.json | 4 +++- packages/extension/public/locales/ru/translation.json | 4 +++- packages/extension/public/locales/zh/translation.json | 4 +++- 4 files changed, 12 insertions(+), 4 deletions(-) diff --git a/packages/extension/public/locales/fr/translation.json b/packages/extension/public/locales/fr/translation.json index 0eef57c2b..5376dbc3f 100644 --- a/packages/extension/public/locales/fr/translation.json +++ b/packages/extension/public/locales/fr/translation.json @@ -1289,5 +1289,7 @@ "Local": "Local", "This account is visible to websites": "Ce compte est visible aux sites web", "Choose a name for the profile": "Choisissez un nom pour le profil", - "Remove from {{profileName}} profile": "Retirer du profil {{profileName}}" + "Remove from {{profileName}} profile": "Retirer du profil {{profileName}}", + "You can add your imported proxied accounts to a profile if you wish. If not, they'll be visible in 'All' and 'Watch-only' profiles.": "Vous pouvez ajouter vos comptes importés proxy à un profil si vous le souhaitez. Si non, ils seront visibles dans les profils 'Tous' et 'Seulement à regarder'.", + "Choose a profile name:": "Choisissez un nom de profil:" } \ No newline at end of file diff --git a/packages/extension/public/locales/hi/translation.json b/packages/extension/public/locales/hi/translation.json index 9be91abe1..db0215708 100644 --- a/packages/extension/public/locales/hi/translation.json +++ b/packages/extension/public/locales/hi/translation.json @@ -1286,5 +1286,7 @@ "Local": "लोकल", "This account is visible to websites": "यह खाता वेबसाइटों के लिए देखा जा सकता है", "Choose a name for the profile": "प्रोफाइल के लिए नाम चुनें", - "Remove from {{profileName}} profile": "{{profileName}} प्रोफाइल से हटाएं" + "Remove from {{profileName}} profile": "{{profileName}} प्रोफाइल से हटाएं", + "You can add your imported proxied accounts to a profile if you wish. If not, they'll be visible in 'All' and 'Watch-only' profiles.": "आप अपने आयातित प्रॉक्सी खातों को एक प्रोफ़ाइल में जोड़ सकते हैं, यदि आप चाहें तो नहीं है, तो वे 'सभी' और 'देखने के लिए ही' प्रोफ़ाइलों में देखे जाएंगे।", + "Choose a profile name:": "एक प्रोफ़ाइल नाम चुनें:" } \ No newline at end of file diff --git a/packages/extension/public/locales/ru/translation.json b/packages/extension/public/locales/ru/translation.json index 762393408..bff223f30 100644 --- a/packages/extension/public/locales/ru/translation.json +++ b/packages/extension/public/locales/ru/translation.json @@ -1284,5 +1284,7 @@ "Local": "Локальный", "This account is visible to websites": "Эта учетная запись доступна веб-сайтам", "Choose a name for the profile": "Выберите имя для профиля", - "Remove from {{profileName}} profile": "Удалить из профиля {{profileName}}" + "Remove from {{profileName}} profile": "Удалить из профиля {{profileName}}", + "You can add your imported proxied accounts to a profile if you wish. If not, they'll be visible in 'All' and 'Watch-only' profiles.": "Вы можете добавить импортированные проксированные учетные записи в профиль, если вы этого хотите. Если не, они будут видны в профилях 'Все' и 'Только для просмотра'.", + "Choose a profile name:": "Выберите имя профиля:" } \ No newline at end of file diff --git a/packages/extension/public/locales/zh/translation.json b/packages/extension/public/locales/zh/translation.json index 700a3b12d..53f30afe1 100644 --- a/packages/extension/public/locales/zh/translation.json +++ b/packages/extension/public/locales/zh/translation.json @@ -1293,5 +1293,7 @@ "Local": "本地", "This account is visible to websites": "这账户可在网站上查看", "Choose a name for the profile": "选择个人资料名称", - "Remove from {{profileName}} profile": "从{{profileName}}个人资料中删除" + "Remove from {{profileName}} profile": "从{{profileName}}个人资料中删除", + "You can add your imported proxied accounts to a profile if you wish. If not, they'll be visible in 'All' and 'Watch-only' profiles.": "您可以将您的导入代理账户添加到一个-profile中,如果您想的话。如果不是,他们将在‘所有’和‘只读’-profiles中可见。", + "Choose a profile name:": "选择个人资料名称:" } \ No newline at end of file From f27064e16581157a8a3a7b65446c0cea38414e49 Mon Sep 17 00:00:00 2001 From: Nick Date: Sun, 23 Jun 2024 16:57:22 +0330 Subject: [PATCH 25/36] Update translation.json --- packages/extension/public/locales/en/translation.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/extension/public/locales/en/translation.json b/packages/extension/public/locales/en/translation.json index e803ccd55..4b6ba3cbd 100644 --- a/packages/extension/public/locales/en/translation.json +++ b/packages/extension/public/locales/en/translation.json @@ -1296,5 +1296,7 @@ "Local": "", "This account is visible to websites": "", "Choose a name for the profile": "", - "Remove from {{profileName}} profile": "" + "Remove from {{profileName}} profile": "", + "You can add your imported proxied accounts to a profile if you wish. If not, they'll be visible in 'All' and 'Watch-only' profiles.": "", + "Choose a profile name:": "" } From fe4f27964a0280d379284b9be708b299dbba7e15 Mon Sep 17 00:00:00 2001 From: Nick Date: Sun, 23 Jun 2024 16:57:38 +0330 Subject: [PATCH 26/36] fix event type issue --- .../extension-polkagate/src/components/ProfileInput.tsx | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/packages/extension-polkagate/src/components/ProfileInput.tsx b/packages/extension-polkagate/src/components/ProfileInput.tsx index 3863ae9a0..309809477 100644 --- a/packages/extension-polkagate/src/components/ProfileInput.tsx +++ b/packages/extension-polkagate/src/components/ProfileInput.tsx @@ -39,7 +39,7 @@ export default function ProfileInput({ disabled = false, placeHolder = '', setPr } }, [containerRef?.current?.offsetWidth]); - const handleProfile = useCallback((value?:string): void => { + const handleProfile = useCallback((value?: string): void => { setTogglePopper(false); if (!value) { @@ -66,7 +66,7 @@ export default function ProfileInput({ disabled = false, placeHolder = '', setPr

(); const profileName = account?.profile; + const newProfileDisabled = useMemo(() => userDefinedProfiles && userDefinedProfiles?.length >= MAX_USER_DEFINED_PROFILE_LIMIT, [userDefinedProfiles, userDefinedProfiles?.length]); const editName = useCallback((newName: string | null) => { setNewName(newName ?? ''); @@ -76,21 +118,17 @@ function ProfileMenu({ address, setUpperAnchorEl }: Props): React.ReactElement

( {showName - ? addToNewProfile(newName as string)} - placeholder={t('Profile Name')} - value={newName} + ? : + } onClick={onNewProfile} text={t('New profile')} @@ -127,7 +165,7 @@ function ProfileMenu({ address, setUpperAnchorEl }: Props): React.ReactElement

- + @@ -139,12 +177,12 @@ function ProfileMenu({ address, setUpperAnchorEl }: Props): React.ReactElement

{!!profileName && <> - + 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' }}> } - text={t('Remove from {{profileName}} profile', { replace: { profileName } })} + text={t('Remove from {{profileName}}', { replace: { profileName } })} withHoverEffect /> diff --git a/packages/extension-polkagate/src/fullscreen/homeFullScreen/partials/ProfileTab.tsx b/packages/extension-polkagate/src/fullscreen/homeFullScreen/partials/ProfileTab.tsx index 7a2d2c1f1..702763ba3 100644 --- a/packages/extension-polkagate/src/fullscreen/homeFullScreen/partials/ProfileTab.tsx +++ b/packages/extension-polkagate/src/fullscreen/homeFullScreen/partials/ProfileTab.tsx @@ -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 { @@ -18,41 +16,38 @@ interface Props { setSelectedProfile: React.Dispatch>; 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(true); /** set by user click on a profile tab */ const [toHiddenAll, setToHiddenAll] = useState(); - 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]); @@ -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); @@ -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 ( - - - {text} + + + {t(text)} ); diff --git a/packages/extension-polkagate/src/fullscreen/homeFullScreen/partials/ProfileTabs.tsx b/packages/extension-polkagate/src/fullscreen/homeFullScreen/partials/ProfileTabs.tsx index 48f0cc338..68878ce3c 100644 --- a/packages/extension-polkagate/src/fullscreen/homeFullScreen/partials/ProfileTabs.tsx +++ b/packages/extension-polkagate/src/fullscreen/homeFullScreen/partials/ProfileTabs.tsx @@ -16,37 +16,37 @@ export const HIDDEN_PERCENT = '50%'; export default function ProfileTabs({ orderedAccounts }: Props): React.ReactElement { const { t } = useTranslation(); - const [profiles, setProfiles] = useState([t('All')]); + const [profiles, setProfiles] = useState([]); const [selectedProfile, setSelectedProfile] = useState(); const [isHovered, setIsHovered] = useState(); - const onMouseEnter= useCallback(()=>setIsHovered(true),[]) - const onMouseLeave= useCallback(()=>setIsHovered(false),[]) + const onMouseEnter = useCallback(() => setIsHovered(true), []); + const onMouseLeave = useCallback(() => setIsHovered(false), []); useEffect(() => { if (!orderedAccounts) { return } - const texts = [t('All')]; + const texts = ['All']; const hasLocal = orderedAccounts.find(({ account }) => !account.isExternal) if (hasLocal) { - texts.push(t('Local')) + texts.push('Local') } const hasLedger = orderedAccounts.find(({ account }) => account.isHardware) if (hasLedger) { - texts.push(t('Ledger')) + texts.push('Ledger') } const hasWatchOnly = orderedAccounts.find(({ account }) => account.isExternal && !account.isQR && !account.isHardware); if (hasWatchOnly) { - texts.push(t('Watch-only')) + texts.push('Watch-only') } const hasQrAttached = orderedAccounts.find(({ account: { isQR } }) => isQR); if (hasQrAttached) { - texts.push(t('QR-attached')) + texts.push('QR-attached') } const userDefinedProfiles = orderedAccounts.map(({ account: { profile } }) => profile as string).filter((item) => !!item); @@ -59,28 +59,23 @@ export default function ProfileTabs({ orderedAccounts }: Props): React.ReactElem }, [orderedAccounts]); return ( - + { - profiles?.map((profile, index) => ( + profiles.map((profile, index) => ( ); } - From 8e349ac1e7ad4bd6ae65ad364eb284965853eebe Mon Sep 17 00:00:00 2001 From: Amir Ekbatanifard Date: Mon, 24 Jun 2024 18:53:38 +0330 Subject: [PATCH 34/36] Update useProfile --- .../src/components/ProfileInput.tsx | 8 ++-- .../homeFullScreen/partials/ProfileMenu.tsx | 10 ++-- .../homeFullScreen/partials/ProfileTabs.tsx | 47 ++++--------------- .../src/hooks/useProfiles.ts | 34 +++++++++++++- 4 files changed, 50 insertions(+), 49 deletions(-) diff --git a/packages/extension-polkagate/src/components/ProfileInput.tsx b/packages/extension-polkagate/src/components/ProfileInput.tsx index 309809477..7edc18cf1 100644 --- a/packages/extension-polkagate/src/components/ProfileInput.tsx +++ b/packages/extension-polkagate/src/components/ProfileInput.tsx @@ -20,14 +20,14 @@ interface Props { export default function ProfileInput({ disabled = false, placeHolder = '', setProfileName, profileName, helperText = '', label, style }: Props): React.ReactElement { const containerRef = useRef(null); - const userDefinedProfiles = useProfiles(); + const profiles = useProfiles(); const [isPopperOpen, setTogglePopper] = useState(false); const [focus, setFocus] = useState(false); const [enteredProfile, setEnteredProfile] = useState(); const [dropdownWidth, setDropdownWidth] = useState('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); @@ -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), diff --git a/packages/extension-polkagate/src/fullscreen/homeFullScreen/partials/ProfileMenu.tsx b/packages/extension-polkagate/src/fullscreen/homeFullScreen/partials/ProfileMenu.tsx index 95fd8eaa3..ce3e2ed18 100644 --- a/packages/extension-polkagate/src/fullscreen/homeFullScreen/partials/ProfileMenu.tsx +++ b/packages/extension-polkagate/src/fullscreen/homeFullScreen/partials/ProfileMenu.tsx @@ -54,7 +54,7 @@ const InputBox = ({ addToNewProfile, editName, newName, t, theme }: InputBoxProp ); } -const MAX_USER_DEFINED_PROFILE_LIMIT = 4; +const MAX_POSSIBLE_PROFILES = 4; function ProfileMenu({ address, setUpperAnchorEl }: Props): React.ReactElement { const theme = useTheme(); @@ -62,14 +62,14 @@ function ProfileMenu({ address, setUpperAnchorEl }: Props): React.ReactElement

(); const [showName, setShowName] = useState(); const [newName, setNewName] = useState(); const profileName = account?.profile; - const newProfileDisabled = useMemo(() => userDefinedProfiles && userDefinedProfiles?.length >= MAX_USER_DEFINED_PROFILE_LIMIT, [userDefinedProfiles, userDefinedProfiles?.length]); + const newProfileDisabled = useMemo(() => profiles?.userDefinedProfiles && profiles.userDefinedProfiles?.length >= MAX_POSSIBLE_PROFILES, [profiles?.userDefinedProfiles, profiles?.userDefinedProfiles?.length]); const editName = useCallback((newName: string | null) => { setNewName(newName ?? ''); @@ -136,8 +136,8 @@ function ProfileMenu({ address, setUpperAnchorEl }: Props): React.ReactElement

} - {userDefinedProfiles?.length - ? userDefinedProfiles?.map((profile) => ( + {profiles?.userDefinedProfiles?.length + ? profiles.userDefinedProfiles?.map((profile) => ( diff --git a/packages/extension-polkagate/src/fullscreen/homeFullScreen/partials/ProfileTabs.tsx b/packages/extension-polkagate/src/fullscreen/homeFullScreen/partials/ProfileTabs.tsx index 68878ce3c..df0d75d60 100644 --- a/packages/extension-polkagate/src/fullscreen/homeFullScreen/partials/ProfileTabs.tsx +++ b/packages/extension-polkagate/src/fullscreen/homeFullScreen/partials/ProfileTabs.tsx @@ -3,9 +3,9 @@ import type { AccountsOrder } from '..'; import { Grid } from '@mui/material'; -import React, { useEffect, useState, useCallback } from 'react'; -import { useTranslation } from '../../../hooks'; +import React, { useState, useCallback, useMemo } from 'react'; import ProfileTab from './ProfileTab'; +import { useProfiles } from '../../../hooks'; interface Props { orderedAccounts: AccountsOrder[] | undefined; @@ -14,49 +14,20 @@ interface Props { export const HIDDEN_PERCENT = '50%'; export default function ProfileTabs({ orderedAccounts }: Props): React.ReactElement { - const { t } = useTranslation(); - - const [profiles, setProfiles] = useState([]); + const profiles = useProfiles(); const [selectedProfile, setSelectedProfile] = useState(); const [isHovered, setIsHovered] = useState(); const onMouseEnter = useCallback(() => setIsHovered(true), []); const onMouseLeave = useCallback(() => setIsHovered(false), []); - useEffect(() => { - if (!orderedAccounts) { - return - } - - const texts = ['All']; - const hasLocal = orderedAccounts.find(({ account }) => !account.isExternal) - if (hasLocal) { - texts.push('Local') - } - - const hasLedger = orderedAccounts.find(({ account }) => account.isHardware) - if (hasLedger) { - texts.push('Ledger') - } - - const hasWatchOnly = orderedAccounts.find(({ account }) => account.isExternal && !account.isQR && !account.isHardware); - if (hasWatchOnly) { - texts.push('Watch-only') - } - - const hasQrAttached = orderedAccounts.find(({ account: { isQR } }) => isQR); - if (hasQrAttached) { - texts.push('QR-attached') - } - - const userDefinedProfiles = orderedAccounts.map(({ account: { profile } }) => profile as string).filter((item) => !!item); - const sortedUserDefinedProfiles = [...new Set(userDefinedProfiles)].sort(); - if (sortedUserDefinedProfiles) { - texts.push(...sortedUserDefinedProfiles) + const profilesToShow = useMemo(() => { + if (!profiles) { + return undefined; } - setProfiles(texts); - }, [orderedAccounts]); + return profiles.defaultProfiles.concat(profiles.userDefinedProfiles); + }, [profiles, profiles?.defaultProfiles.length, profiles?.userDefinedProfiles.length]); return ( @@ -70,7 +41,7 @@ export default function ProfileTabs({ orderedAccounts }: Props): React.ReactElem position: 'relative' }}> { - profiles.map((profile, index) => ( + profilesToShow?.map((profile, index) => ( { if(!accounts){ return; } + + // default profiles + const texts = ['All']; + const hasLocal = accounts.find(({ isExternal }) => !isExternal) + if (hasLocal) { + texts.push('Local') + } + + const hasLedger = accounts.find(({ isHardware }) => isHardware) + if (hasLedger) { + texts.push('Ledger') + } + + const hasWatchOnly = accounts.find(({ isExternal, isQR, isHardware }) => isExternal && !isQR && !isHardware); + if (hasWatchOnly) { + texts.push('Watch-only') + } + + const hasQrAttached = accounts.find(({ isQR }) => isQR); + if (hasQrAttached) { + texts.push('QR-attached') + } + + // user defined profiles const profiles = accounts.map(({ profile }) => profile).filter(Boolean) as string[]; - return [...new Set(profiles)].sort(); + + return { + userDefinedProfiles: [...new Set(profiles)].sort(), + defaultProfiles: texts + }; }, [accounts]); } From 947af221d95c52c35d8e3ae815c9f8302ff74e6e Mon Sep 17 00:00:00 2001 From: Amir Ekbatanifard Date: Tue, 25 Jun 2024 11:27:07 +0330 Subject: [PATCH 35/36] Add scroll --- .../homeFullScreen/partials/ProfileMenu.tsx | 6 +- .../homeFullScreen/partials/ProfileTabs.tsx | 61 ++++++++++++++++--- 2 files changed, 53 insertions(+), 14 deletions(-) diff --git a/packages/extension-polkagate/src/fullscreen/homeFullScreen/partials/ProfileMenu.tsx b/packages/extension-polkagate/src/fullscreen/homeFullScreen/partials/ProfileMenu.tsx index ce3e2ed18..169532aa8 100644 --- a/packages/extension-polkagate/src/fullscreen/homeFullScreen/partials/ProfileMenu.tsx +++ b/packages/extension-polkagate/src/fullscreen/homeFullScreen/partials/ProfileMenu.tsx @@ -54,8 +54,6 @@ const InputBox = ({ addToNewProfile, editName, newName, t, theme }: InputBoxProp ); } -const MAX_POSSIBLE_PROFILES = 4; - function ProfileMenu({ address, setUpperAnchorEl }: Props): React.ReactElement { const theme = useTheme(); const { t } = useTranslation(); @@ -69,7 +67,6 @@ function ProfileMenu({ address, setUpperAnchorEl }: Props): React.ReactElement

(); const profileName = account?.profile; - const newProfileDisabled = useMemo(() => profiles?.userDefinedProfiles && profiles.userDefinedProfiles?.length >= MAX_POSSIBLE_PROFILES, [profiles?.userDefinedProfiles, profiles?.userDefinedProfiles?.length]); const editName = useCallback((newName: string | null) => { setNewName(newName ?? ''); @@ -126,9 +123,8 @@ function ProfileMenu({ address, setUpperAnchorEl }: Props): React.ReactElement

: + } onClick={onNewProfile} text={t('New profile')} diff --git a/packages/extension-polkagate/src/fullscreen/homeFullScreen/partials/ProfileTabs.tsx b/packages/extension-polkagate/src/fullscreen/homeFullScreen/partials/ProfileTabs.tsx index df0d75d60..e70d611c8 100644 --- a/packages/extension-polkagate/src/fullscreen/homeFullScreen/partials/ProfileTabs.tsx +++ b/packages/extension-polkagate/src/fullscreen/homeFullScreen/partials/ProfileTabs.tsx @@ -1,9 +1,10 @@ // Copyright 2019-2024 @polkadot/extension-polkagate authors & contributors // SPDX-License-Identifier: Apache-2.0 +import { ArrowForwardIosRounded as ArrowForwardIosRoundedIcon } from '@mui/icons-material'; import type { AccountsOrder } from '..'; import { Grid } from '@mui/material'; -import React, { useState, useCallback, useMemo } from 'react'; +import React, { useState, useCallback, useMemo, useEffect, useRef } from 'react'; import ProfileTab from './ProfileTab'; import { useProfiles } from '../../../hooks'; @@ -15,11 +16,13 @@ export const HIDDEN_PERCENT = '50%'; export default function ProfileTabs({ orderedAccounts }: Props): React.ReactElement { const profiles = useProfiles(); + const [selectedProfile, setSelectedProfile] = useState(); const [isHovered, setIsHovered] = useState(); - const onMouseEnter = useCallback(() => setIsHovered(true), []); - const onMouseLeave = useCallback(() => setIsHovered(false), []); + const [showLeftMore, setShowLeftMore] = useState(false); + const [showRightMore, setShowRightMore] = useState(true); + const scrollContainerRef = useRef(null); const profilesToShow = useMemo(() => { if (!profiles) { @@ -29,17 +32,56 @@ export default function ProfileTabs({ orderedAccounts }: Props): React.ReactElem return profiles.defaultProfiles.concat(profiles.userDefinedProfiles); }, [profiles, profiles?.defaultProfiles.length, profiles?.userDefinedProfiles.length]); + const onMouseEnter = useCallback(() => setIsHovered(true), []); + const onMouseLeave = useCallback(() => setIsHovered(false), []); + + const handleScroll = () => { + if (scrollContainerRef.current) { + const { scrollLeft, scrollWidth, clientWidth } = scrollContainerRef.current; + setShowLeftMore(scrollLeft > 0); + setShowRightMore(scrollLeft + clientWidth < scrollWidth); + } + }; + + const handleWheel = (event: WheelEvent) => { + if (scrollContainerRef.current) { + event.preventDefault(); + scrollContainerRef.current.scrollLeft += event.deltaY; + handleScroll(); + } + }; + + useEffect(() => { + handleScroll(); // Set initial shadow states on mount + const ref = scrollContainerRef.current; + if (ref) { + ref.addEventListener('scroll', handleScroll); + ref.addEventListener('wheel', handleWheel, { passive: false }); + return () => { + ref.removeEventListener('scroll', handleScroll); + ref.removeEventListener('wheel', handleWheel); + }; + } + }, []); + return ( - + + {showLeftMore && } + px: '25px', + pb: '5px', + flexFlow: 'nowrap', + overflowX: 'scroll', + whiteSpace: 'nowrap' + }} + ref={scrollContainerRef} + xs + > { profilesToShow?.map((profile, index) => ( )) } - + + {showRightMore && } ); } From db93cff88d314a1babb955e45c3d24520d30abc6 Mon Sep 17 00:00:00 2001 From: Nick Date: Tue, 25 Jun 2024 21:20:39 +0330 Subject: [PATCH 36/36] fix lint issue --- .../src/fullscreen/homeFullScreen/partials/ProfileMenu.tsx | 2 +- .../src/fullscreen/homeFullScreen/partials/ProfileTabs.tsx | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/extension-polkagate/src/fullscreen/homeFullScreen/partials/ProfileMenu.tsx b/packages/extension-polkagate/src/fullscreen/homeFullScreen/partials/ProfileMenu.tsx index 169532aa8..39b8a59f7 100644 --- a/packages/extension-polkagate/src/fullscreen/homeFullScreen/partials/ProfileMenu.tsx +++ b/packages/extension-polkagate/src/fullscreen/homeFullScreen/partials/ProfileMenu.tsx @@ -4,7 +4,7 @@ /* eslint-disable react/jsx-max-props-per-line */ import { Divider, Grid, Popover, useTheme, IconButton, type Theme } from '@mui/material'; -import React, { useCallback, useContext, useMemo, useState } from 'react'; +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'; diff --git a/packages/extension-polkagate/src/fullscreen/homeFullScreen/partials/ProfileTabs.tsx b/packages/extension-polkagate/src/fullscreen/homeFullScreen/partials/ProfileTabs.tsx index e70d611c8..ed5cc51f1 100644 --- a/packages/extension-polkagate/src/fullscreen/homeFullScreen/partials/ProfileTabs.tsx +++ b/packages/extension-polkagate/src/fullscreen/homeFullScreen/partials/ProfileTabs.tsx @@ -62,6 +62,7 @@ export default function ProfileTabs({ orderedAccounts }: Props): React.ReactElem ref.removeEventListener('wheel', handleWheel); }; } + return undefined; }, []); return (