Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .env
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ VITE_FEATURE_NEW_LIMIT_FLOW=true
VITE_FEATURE_THORCHAIN_SWAPPER_ACK=false
VITE_FEATURE_ACTION_CENTER=true
VITE_FEATURE_QUICK_BUY=false
VITE_FEATURE_NEW_WALLET_MANAGER=false
VITE_FEATURE_CHATWOOT=true

# absolute URL prefix
Expand Down
1 change: 1 addition & 0 deletions .env.development
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
# feature flags
VITE_FEATURE_THORCHAIN_TCY_ACTIVITY=true
VITE_FEATURE_QUICK_BUY=true
VITE_FEATURE_NEW_WALLET_MANAGER=true

# mixpanel
VITE_MIXPANEL_TOKEN=a867ce40912a6b7d01d088cf62b0e1ff
Expand Down
1 change: 1 addition & 0 deletions .env.production
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ VITE_FEATURE_MIXPANEL=true

VITE_FEATURE_THORCHAIN_TCY_ACTIVITY=false
VITE_FEATURE_QUICK_BUY=false
VITE_FEATURE_NEW_WALLET_MANAGER=false

# mixpanel
VITE_MIXPANEL_TOKEN=9d304465fc72224aead9e027e7c24356
Expand Down
4 changes: 3 additions & 1 deletion src/components/Layout/Header/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { GlobalSeachButton } from './GlobalSearch/GlobalSearchButton'
import { ChainMenu } from './NavBar/ChainMenu'
import { MobileNavBar } from './NavBar/MobileNavBar'
import { UserMenu } from './NavBar/UserMenu'
import { WalletManagerPopover } from './NavBar/WalletManagerPopover'
import { TxWindow } from './TxWindow/TxWindow'

import { useDiscoverAccounts } from '@/context/AppProvider/hooks/useDiscoverAccounts'
Expand Down Expand Up @@ -53,6 +54,7 @@ export const Header = memo(() => {

const isWalletConnectToDappsV2Enabled = useFeatureFlag('WalletConnectToDappsV2')
const isActionCenterEnabled = useFeatureFlag('ActionCenter')
const isNewWalletManagerEnabled = useFeatureFlag('NewWalletManager')
const { degradedChainIds } = useDiscoverAccounts()

/**
Expand Down Expand Up @@ -119,7 +121,7 @@ export const Header = memo(() => {
{isConnected && isActionCenterEnabled && <ActionCenter />}
{isLargerThanMd && (
<Box display={displayProp2}>
<UserMenu />
{isNewWalletManagerEnabled ? <WalletManagerPopover /> : <UserMenu />}
</Box>
)}
</Flex>
Expand Down
192 changes: 192 additions & 0 deletions src/components/Layout/Header/NavBar/PopoverWallet.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
import { ArrowDownIcon } from '@chakra-ui/icons'
import {
Box,
Button,
Flex,
Spinner,
Tab,
TabList,
TabPanel,
TabPanels,
Tabs,
Text,
useColorModeValue,
VStack,
} from '@chakra-ui/react'
import type { FC } from 'react'
import { memo, useCallback, useMemo, useState } from 'react'
import { useTranslate } from 'react-polyglot'

import { PopoverWalletHeader } from './PopoverWalletHeader'

import { Amount } from '@/components/Amount/Amount'
import { SendIcon } from '@/components/Icons/SendIcon'
import { useDiscoverAccounts } from '@/context/AppProvider/hooks/useDiscoverAccounts'
import { WalletActions } from '@/context/WalletProvider/actions'
import { useModal } from '@/hooks/useModal/useModal'
import { useWallet } from '@/hooks/useWallet/useWallet'
import { AccountTable } from '@/pages/Dashboard/components/AccountList/AccountTable'
import { WatchlistTable } from '@/pages/Home/WatchlistTable'
import {
selectIsAnyPortfolioGetAccountLoading,
selectPortfolioTotalUserCurrencyBalance,
} from '@/state/slices/selectors'
import { useAppSelector } from '@/state/store'

type ActionButtonProps = {
icon: React.ReactNode
label: string
onClick: () => void
isDisabled?: boolean
}

const ActionButton: FC<ActionButtonProps> = memo(({ icon, label, onClick, isDisabled }) => {
return (
<Button
flex='1'
height='80px'
borderRadius='xl'
alignItems='center'
onClick={onClick}
isDisabled={isDisabled}
>
<VStack spacing={2} justify='center' align='center'>
{icon}
<Text fontSize='sm' fontWeight='medium' color='text.subtle'>
{label}
</Text>
</VStack>
</Button>
)
})

const sendIcon = <SendIcon boxSize='6' color='blue.500' />
const receiveIcon = <ArrowDownIcon boxSize={6} color='green.500' />

const unselectedTabStyle = { color: 'text.base' }

type PopoverWalletProps = {
onClose?: () => void
}

export const PopoverWallet: FC<PopoverWalletProps> = memo(({ onClose }) => {
const translate = useTranslate()
const send = useModal('send')
const receive = useModal('receive')
const [activeTabIndex, setActiveTabIndex] = useState(0)
const selectedTabBg = useColorModeValue('black', 'white')
const selectedTabColor = useColorModeValue('white', 'black')
const selectedTabStyle = useMemo(
() => ({ bg: selectedTabBg, color: selectedTabColor }),
[selectedTabBg, selectedTabColor],
)

const { isFetching: isDiscoveringAccounts } = useDiscoverAccounts()

const portfolioTotalUserCurrencyBalance = useAppSelector(selectPortfolioTotalUserCurrencyBalance)
const isAnyPortfolioGetAccountLoading = useAppSelector(selectIsAnyPortfolioGetAccountLoading)

const {
state: { isConnected, walletInfo, connectedType },
dispatch,
disconnect,
} = useWallet()

const handleSendClick = useCallback(() => {
send.open({})
}, [send])

const handleReceiveClick = useCallback(() => {
receive.open({})
}, [receive])

const handleTabChange = useCallback((index: number) => {
setActiveTabIndex(index)
}, [])

const handleSwitchProvider = useCallback(() => {
dispatch({ type: WalletActions.SET_WALLET_MODAL, payload: true })
}, [dispatch])

return (
<Box width='100%' height='100%' display='flex' flexDirection='column'>
<PopoverWalletHeader
walletInfo={walletInfo}
isConnected={isConnected}
connectedType={connectedType}
onDisconnect={disconnect}
onSwitchProvider={handleSwitchProvider}
/>
<VStack pt={6} pb={8} spacing={1} align='center'>
<Box position='relative'>
<Amount.Fiat
value={portfolioTotalUserCurrencyBalance}
fontSize={'4xl'}
fontWeight='bold'
lineHeight='shorter'
/>

{(isDiscoveringAccounts || isAnyPortfolioGetAccountLoading) && (
<Spinner
position='absolute'
right={0}
top={0}
mr={-4}
transform='translateY(-50%)'
size='xs'
/>
)}
</Box>

<Text color='text.subtle' whiteSpace='nowrap'>
{translate('defi.netWorth')}
</Text>
</VStack>

<Flex width='100%' pb={4} gap={2}>
<ActionButton
icon={sendIcon}
label={translate('common.send')}
onClick={handleSendClick}
isDisabled={!isConnected}
/>
<ActionButton
icon={receiveIcon}
label={translate('common.receive')}
onClick={handleReceiveClick}
isDisabled={!isConnected}
/>
</Flex>

<Box flex='1' overflow='hidden' display='flex' flexDirection='column'>
<Tabs
index={activeTabIndex}
onChange={handleTabChange}
variant='soft-rounded'
size='sm'
isLazy
display='flex'
flexDirection='column'
height='100%'
>
<TabList bg='transparent' borderWidth={0} pt={2} pb={4} px={0} gap={2} flexShrink={0}>
<Tab _selected={selectedTabStyle} sx={unselectedTabStyle}>
{translate('dashboard.portfolio.myCrypto')}{' '}
</Tab>
<Tab _selected={selectedTabStyle} sx={unselectedTabStyle}>
{translate('watchlist.title')}
</Tab>
</TabList>
<TabPanels flex='1' overflow='auto' maxHeight={'35vh'} className='scroll-container'>
<TabPanel p={0} pt={2}>
<AccountTable forceCompactView onRowClick={onClose} />
</TabPanel>
<TabPanel p={0} pt={2}>
<WatchlistTable forceCompactView onRowClick={onClose} hideExploreMore />
</TabPanel>
</TabPanels>
</Tabs>
</Box>
</Box>
)
})
125 changes: 125 additions & 0 deletions src/components/Layout/Header/NavBar/PopoverWalletHeader.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
import { CloseIcon, RepeatIcon, SettingsIcon } from '@chakra-ui/icons'
import {
Flex,
Icon,
IconButton,
Menu,
MenuButton,
MenuDivider,
MenuGroup,
MenuItem,
MenuList,
Text,
} from '@chakra-ui/react'
import type { FC } from 'react'
import { memo, useCallback, useMemo } from 'react'
import { TbDots } from 'react-icons/tb'
import { useTranslate } from 'react-polyglot'

import { WalletImage } from './WalletImage'

import { SUPPORTED_WALLETS } from '@/context/WalletProvider/config'
import type { InitialState } from '@/context/WalletProvider/WalletProvider'
import { useModal } from '@/hooks/useModal/useModal'
import { useMipdProviders } from '@/lib/mipd'
import { ProfileAvatar } from '@/pages/Dashboard/components/ProfileAvatar/ProfileAvatar'
import { selectWalletRdns } from '@/state/slices/localWalletSlice/selectors'
import { useAppSelector } from '@/state/store'

const settingsIcon = <SettingsIcon />
const dotsIcon = <Icon as={TbDots} />

type PopoverHeaderProps = {
walletInfo: InitialState['walletInfo']
isConnected: boolean
connectedType: InitialState['connectedType']
onDisconnect: () => void
onSwitchProvider: () => void
}

export const PopoverWalletHeader: FC<PopoverHeaderProps> = memo(
({ walletInfo, isConnected, connectedType, onDisconnect, onSwitchProvider }) => {
const translate = useTranslate()
const settings = useModal('settings')

const maybeRdns = useAppSelector(selectWalletRdns)
const mipdProviders = useMipdProviders()
const maybeMipdProvider = useMemo(
() => mipdProviders.find(provider => provider.info.rdns === maybeRdns),
[mipdProviders, maybeRdns],
)

const label = useMemo(
() => maybeMipdProvider?.info?.name || walletInfo?.meta?.label || walletInfo?.name,
[walletInfo, maybeMipdProvider?.info?.name],
)

const handleSettingsClick = useCallback(() => {
settings.open({})
}, [settings])

const repeatIcon = useMemo(() => <RepeatIcon />, [])
const closeIcon = useMemo(() => <CloseIcon />, [])

const ConnectMenuComponent = useMemo(
() => connectedType && SUPPORTED_WALLETS[connectedType]?.connectedMenuComponent,
[connectedType],
)

const walletImageIcon = useMemo(() => <WalletImage walletInfo={walletInfo} />, [walletInfo])

if (!isConnected || !walletInfo) return null

return (
<Flex align='center' justify='space-between'>
<Flex align='center' gap={2}>
<ProfileAvatar size='sm' borderRadius='full' />
<Text fontWeight='medium'>{label}</Text>
</Flex>
<Flex gap={2}>
<IconButton
aria-label='Settings'
rounded='full'
icon={settingsIcon}
size='sm'
onClick={handleSettingsClick}
/>
<Menu>
<MenuButton
as={IconButton}
rounded='full'
aria-label='Menu'
icon={dotsIcon}
size='sm'
/>
<MenuList zIndex={2}>
<MenuGroup title={translate('common.connectedWallet')} color='text.subtle'>
<MenuItem icon={walletImageIcon} isDisabled closeOnSelect={false}>
<Flex flexDir='row' justifyContent='space-between' alignItems='center'>
<Text>{walletInfo?.name}</Text>
</Flex>
</MenuItem>
</MenuGroup>
<MenuDivider />
<MenuGroup title={translate('common.walletActions')} color='text.subtle'>
{ConnectMenuComponent && <ConnectMenuComponent />}
<MenuDivider />
<MenuItem icon={repeatIcon} onClick={onSwitchProvider}>
{translate('connectWallet.menu.switchWallet')}
</MenuItem>
<MenuItem
fontWeight='medium'
icon={closeIcon}
onClick={onDisconnect}
color='red.500'
>
{translate('connectWallet.menu.disconnect')}
</MenuItem>
</MenuGroup>
</MenuList>
</Menu>
</Flex>
</Flex>
)
},
)
Loading