Skip to content

Commit

Permalink
refactor: lazy reverse wallet ens/names (#10671)
Browse files Browse the repository at this point in the history
  • Loading branch information
UncleBill committed Aug 30, 2023
1 parent 1b5c72f commit c2214f0
Show file tree
Hide file tree
Showing 6 changed files with 108 additions and 132 deletions.
Original file line number Diff line number Diff line change
@@ -1,19 +1,21 @@
import { memo, useCallback } from 'react'
import { Trans } from 'react-i18next'
import { makeStyles, usePopupCustomSnackbar } from '@masknet/theme'
import { Box, Link, Typography, useTheme } from '@mui/material'
import type { ConnectedWalletInfo } from '../../pages/Personas/type.js'
import { MaskMessages, NetworkPluginID, NextIDAction, PopupModalRoutes, SignType } from '@masknet/shared-base'
import { useChainContext, useNetworkDescriptor } from '@masknet/web3-hooks-base'
import { FormattedAddress, ImageIcon, PersonaContext } from '@masknet/shared'
import { ChainId, formatDomainName, formatEthereumAddress } from '@masknet/web3-shared-evm'
import { Icons } from '@masknet/icons'
import { FormattedAddress, ImageIcon, PersonaContext, ProgressiveText } from '@masknet/shared'
import { MaskMessages, NetworkPluginID, NextIDAction, PopupModalRoutes, SignType } from '@masknet/shared-base'
import { makeStyles, usePopupCustomSnackbar } from '@masknet/theme'
import { useChainContext, useNetworkDescriptor, useWallets, useWeb3State } from '@masknet/web3-hooks-base'
import { ExplorerResolver, NextIDProof } from '@masknet/web3-providers'
import { isSameAddress, resolveNextIDPlatformWalletName } from '@masknet/web3-shared-base'
import { ChainId, formatDomainName, formatEthereumAddress } from '@masknet/web3-shared-evm'
import { Box, Link, Typography, useTheme } from '@mui/material'
import { useQueries } from '@tanstack/react-query'
import { memo, useCallback } from 'react'
import { Trans } from 'react-i18next'
import { useI18N } from '../../../../utils/i18n-next-ui.js'
import { DisconnectModal } from '../../modals/modals.js'
import Services from '../../../service.js'
import { useModalNavigate } from '../index.js'
import { SelectProvider } from '../SelectProvider/index.js'
import { DisconnectModal } from '../../modals/modals.js'
import type { ConnectedWalletInfo } from '../../pages/Personas/type.js'
import { useModalNavigate } from '../ActionModal/index.js'
import { useVerifiedWallets } from '../../hooks/index.js'

const useStyles = makeStyles()((theme) => ({
walletList: {
Expand Down Expand Up @@ -72,19 +74,31 @@ const useStyles = makeStyles()((theme) => ({
},
}))

export interface ConnectedWalletProps {
wallets?: ConnectedWalletInfo[]
}

export const ConnectedWallet = memo<ConnectedWalletProps>(function ConnectedWallet({ wallets }) {
export const ConnectedWallet = memo(function ConnectedWallet() {
const { t } = useI18N()
const theme = useTheme()
const { classes } = useStyles()

const { chainId } = useChainContext<NetworkPluginID.PLUGIN_EVM>()
const localWallets = useWallets()
const { showSnackbar } = usePopupCustomSnackbar()
const { currentPersona } = PersonaContext.useContainer()
const { currentPersona, proofs } = PersonaContext.useContainer()
const modalNavigate = useModalNavigate()
const { NameService } = useWeb3State(NetworkPluginID.PLUGIN_EVM)
const wallets = useVerifiedWallets(proofs)

const queries = useQueries({
queries: wallets.map((wallet, index) => ({
enabled: !!NameService,
queryKey: ['persona-connected-wallet', wallet.identity, index],
queryFn: async () => {
const domain = await NameService?.reverse?.(wallet.identity)
if (domain) return domain
const localWallet = localWallets.find((x) => isSameAddress(x.address, wallet.identity))?.name
return localWallet || null
},
})),
})

// TODO: remove this after next dot id support multiple chain
const networkDescriptor = useNetworkDescriptor(NetworkPluginID.PLUGIN_EVM, ChainId.Mainnet)
Expand Down Expand Up @@ -133,60 +147,68 @@ export const ConnectedWallet = memo<ConnectedWalletProps>(function ConnectedWall
[currentPersona],
)

if (!wallets?.length) {
return <SelectProvider />
}

return (
<Box className={classes.walletList}>
{wallets.map((wallet, index) => (
<Box className={classes.wallet} key={index}>
<Box display="flex" alignItems="center">
<ImageIcon size={24} icon={networkDescriptor?.icon} className={classes.walletIcon} />
<Typography className={classes.walletInfo} component="div">
<Typography className={classes.walletName} component="span">
{formatDomainName(wallet.name, 13)}
</Typography>
{wallets.map((wallet, index) => {
const query = queries[index]
let walletName = query.data || ''
if (!walletName && !query.isLoading) {
walletName = `${resolveNextIDPlatformWalletName(wallet.platform)} ${wallets.length - index}`
}
return (
<Box className={classes.wallet} key={index}>
<Box display="flex" alignItems="center">
<ImageIcon size={24} icon={networkDescriptor?.icon} className={classes.walletIcon} />
<Typography className={classes.walletInfo} component="div">
<ProgressiveText
className={classes.walletName}
component="span"
skeletonWidth={60}
skeletonHeight={16}
loading={query.isLoading}>
{formatDomainName(walletName, 13)}
</ProgressiveText>

<Typography component="span" className={classes.address}>
<FormattedAddress
address={wallet.identity}
size={4}
formatter={formatEthereumAddress}
/>
<Link
style={{ width: 14, height: 14, color: theme.palette.maskColor.main }}
href={ExplorerResolver.addressLink(chainId, wallet.identity ?? '')}
target="_blank"
rel="noopener noreferrer">
<Icons.LinkOut size={14} sx={{ ml: 0.25 }} />
</Link>
<Typography component="span" className={classes.address}>
<FormattedAddress
address={wallet.identity}
size={4}
formatter={formatEthereumAddress}
/>
<Link
style={{ width: 14, height: 14, color: theme.palette.maskColor.main }}
href={ExplorerResolver.addressLink(chainId, wallet.identity ?? '')}
target="_blank"
rel="noopener noreferrer">
<Icons.LinkOut size={14} sx={{ ml: 0.25 }} />
</Link>
</Typography>
</Typography>
</Typography>
</Box>
<Icons.Disconnect
size={16}
onClick={async () => {
if (!currentPersona) return
const confirmed = await DisconnectModal.openAndWaitForClose({
title: t('popups_release_bind_wallet_title'),
tips: (
<Trans
i18nKey="popups_wallet_disconnect_tips"
components={{
strong: <strong style={{ color: theme.palette.maskColor.main }} />,
}}
values={{
wallet: formatEthereumAddress(wallet.identity, 4),
}}
/>
),
})
if (confirmed) return handleConfirmRelease(wallet)
}}
/>
</Box>
<Icons.Disconnect
size={16}
onClick={async () => {
if (!currentPersona) return
const confirmed = await DisconnectModal.openAndWaitForClose({
title: t('popups_release_bind_wallet_title'),
tips: (
<Trans
i18nKey="popups_wallet_disconnect_tips"
components={{
strong: <strong style={{ color: theme.palette.maskColor.main }} />,
}}
values={{
wallet: formatEthereumAddress(wallet.identity, 4),
}}
/>
),
})
if (confirmed) return handleConfirmRelease(wallet)
}}
/>
</Box>
))}
)
})}
<Box className={classes.connect} onClick={() => modalNavigate(PopupModalRoutes.SelectProvider)}>
<Icons.Connect size={16} />
<Typography fontSize={12} fontWeight={700} lineHeight="16px">
Expand Down
66 changes: 9 additions & 57 deletions packages/mask/src/extension/popups/hooks/useVerifiedWallets.ts
Original file line number Diff line number Diff line change
@@ -1,60 +1,12 @@
import { compact } from 'lodash-es'
import { useQuery } from '@tanstack/react-query'
import { NetworkPluginID, type BindingProof, NextIDPlatform } from '@masknet/shared-base'
import { useWallets, useWeb3State } from '@masknet/web3-hooks-base'
import { isGreaterThan, isSameAddress, resolveNextIDPlatformWalletName } from '@masknet/web3-shared-base'
import { type BindingProof, NextIDPlatform, EMPTY_LIST } from '@masknet/shared-base'
import { isGreaterThan } from '@masknet/web3-shared-base'
import { useMemo } from 'react'

export function useVerifiedWallets(proofs?: BindingProof[]) {
const wallets = useWallets()
const { NameService } = useWeb3State(NetworkPluginID.PLUGIN_EVM)

return useQuery({
enabled: !!NameService && !!proofs?.length,
queryKey: ['popups', 'verified', 'wallets', proofs ? JSON.stringify(proofs) : undefined],
queryFn: async () => {
if (!NameService || !proofs) return

const results = await Promise.all(
proofs.map(async (x) => {
if (x.platform === NextIDPlatform.Ethereum) {
const domain = await NameService?.reverse?.(x.identity)

if (domain)
return {
...x,
name: domain,
}

const wallet = wallets.find((wallet) => isSameAddress(wallet.address, x.identity))

if (wallet)
return {
...x,
name: wallet.name,
}

return {
...x,
name: '',
}
}
return null
}),
)

return compact(results)
.sort((a, z) => (isGreaterThan(a.last_checked_at, z.last_checked_at) ? -1 : 1))
.map((x, index, list) => {
if (!x.name) {
const name = resolveNextIDPlatformWalletName(x.platform)
return {
...x,
name: `${name} ${list.length - index}`,
}
}

return x
})
},
})
return useMemo(() => {
if (!proofs?.length) return EMPTY_LIST
return proofs
.filter((x) => x.platform === NextIDPlatform.Ethereum)
.sort((a, z) => (isGreaterThan(a.last_checked_at, z.last_checked_at) ? -1 : 1))
}, [proofs])
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import { PersonaAvatar } from '../../../components/PersonaAvatar/index.js'
import { useParamTab } from '../../../hooks/index.js'
import { useNavigate } from 'react-router-dom'
import { PopupHomeTabType } from '@masknet/shared'
import { SelectProvider } from '../../../components/SelectProvider/index.js'

const useStyles = makeStyles()((theme) => ({
container: {
Expand Down Expand Up @@ -278,7 +279,7 @@ export const PersonaHomeUI = memo<PersonaHomeUIProps>(
className={classes.panel}
value={PopupHomeTabType.ConnectedWallets}
data-hide-scrollbar>
<ConnectedWallet wallets={bindingWallets} />
{bindingWallets?.length ? <ConnectedWallet /> : <SelectProvider />}
</TabPanel>
</TabContext>
) : (
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { memo, useCallback } from 'react'
import { memo, useCallback, useMemo } from 'react'
import { useNavigate } from 'react-router-dom'
import { PersonaContext } from '@masknet/shared'
import {
Expand All @@ -7,17 +7,18 @@ import {
PopupRoutes,
type EnhanceableSite,
type ProfileAccount,
NextIDPlatform,
} from '@masknet/shared-base'
import { PersonaHomeUI } from './UI.js'
import Services from '../../../../service.js'
import { useSupportSocialNetworks, useVerifiedWallets, useHasPassword } from '../../../hooks/index.js'
import { useSupportSocialNetworks, useHasPassword } from '../../../hooks/index.js'

const PersonaHome = memo(() => {
const navigate = useNavigate()
const { avatar, currentPersona, setSelectedAccount, personas, accounts, proofs } = PersonaContext.useContainer()

const { value: definedSocialNetworks = EMPTY_LIST } = useSupportSocialNetworks()
const { data: bindingWallets } = useVerifiedWallets(proofs)
const bindingWallets = useMemo(() => proofs?.filter((x) => x.platform === NextIDPlatform.Ethereum), [proofs])
const { hasPassword } = useHasPassword()

const onCreatePersona = useCallback(() => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ const PersonaAvatarSetting = memo(function PersonaAvatar() {

const { proofs, currentPersona } = PersonaContext.useContainer()

const { data: bindingWallets } = useVerifiedWallets(proofs)
const bindingWallets = useVerifiedWallets(proofs)

const inputRef = useRef<HTMLInputElement>(null)
const [file, setFile] = useState<File | string | null>()
Expand All @@ -108,7 +108,7 @@ const PersonaAvatarSetting = memo(function PersonaAvatar() {

const handleChangeTab = useCallback(
(event: unknown, value: any) => {
if (value === ProfilePhotoType.NFT && !bindingWallets?.length && !account) {
if (value === ProfilePhotoType.NFT && !bindingWallets.length && !account) {
modalNavigate(PopupModalRoutes.SelectProvider, { onlyMask: true })
return
}
Expand All @@ -127,7 +127,7 @@ const PersonaAvatarSetting = memo(function PersonaAvatar() {
updateAt: Date.now(),
}
// Verify Wallet sign with persona
if (bindingWallets?.some((x) => isSameAddress(x.identity, account))) {
if (bindingWallets.some((x) => isSameAddress(x.identity, account))) {
sign = await Services.Identity.signWithPersona(
SignType.Message,
JSON.stringify(data),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ const SelectWallet = memo(function SelectWallet() {
const { Network } = useWeb3State(NetworkPluginID.PLUGIN_EVM)
const { proofs } = PersonaContext.useContainer()

const { data: bindingWallets } = useVerifiedWallets(proofs)
const bindingWallets = useVerifiedWallets(proofs)

const { account, chainId } = useChainContext<NetworkPluginID.PLUGIN_EVM>({
chainId: chainIdSearched ? (Number.parseInt(chainIdSearched, 10) as ChainId) : undefined,
Expand Down

0 comments on commit c2214f0

Please sign in to comment.