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
4 changes: 2 additions & 2 deletions packages/mask/src/components/DataSource/useNextID.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,9 @@ const verifyPersona = (personaIdentifier?: PersonaIdentifier, username?: string)
})
}

export const useNextIDBoundByPlatform = (platform: NextIDPlatform, identity?: string) => {
export const useNextIDBoundByPlatform = (platform?: NextIDPlatform, identity?: string) => {
const res = useAsyncRetry(() => {
if (!identity) return Promise.resolve([])
if (!platform || !identity) return Promise.resolve([])
return NextIDProof.queryExistedBindingByPlatform(platform, identity)
}, [platform, identity])
useEffect(() => MaskMessages.events.ownProofChanged.on(res.retry), [res.retry])
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
import { useEffect, useMemo, useState } from 'react'
import { useUpdateEffect } from 'react-use'
import { first } from 'lodash-unified'
import {
createInjectHooksRenderer,
PluginId,
useActivatedPluginsSNSAdaptor,
usePluginI18NField,
} from '@masknet/plugin-infra/content-script'
import { useAddressNames, useAvailablePlugins } from '@masknet/plugin-infra/web3'
import { useSocialAddressListAll, useAvailablePlugins } from '@masknet/plugin-infra/web3'
import { ConcealableTabs } from '@masknet/shared'
import { EMPTY_LIST, NextIDPlatform } from '@masknet/shared-base'
import { makeStyles, useStylesExtends } from '@masknet/theme'
import { NetworkPluginID } from '@masknet/web3-shared-base'
import { Box, CircularProgress, Typography } from '@mui/material'
import { first } from 'lodash-unified'
import { useEffect, useMemo, useState } from 'react'
import { useUpdateEffect } from 'react-use'
import { activatedSocialNetworkUI } from '../../social-network'
import { isTwitter } from '../../social-network-adaptor/twitter.com/base'
import { MaskMessages, useI18N } from '../../utils'
Expand All @@ -21,11 +20,12 @@ import { useCurrentVisitingIdentity, useLastRecognizedIdentity } from '../DataSo
import { useNextIDBoundByPlatform } from '../DataSource/useNextID'
import { usePersonaConnectStatus } from '../DataSource/usePersonaConnectStatus'

function getTabContent(tabId: string) {
const platform = activatedSocialNetworkUI.configuration.nextIDConfig?.platform as NextIDPlatform | undefined

function getTabContent(tabId?: string) {
return createInjectHooksRenderer(useActivatedPluginsSNSAdaptor.visibility.useAnyMode, (x) => {
const tab = x.ProfileTabs?.find((x) => x.ID === tabId)
if (!tab) return
return tab.UI?.TabContent
return tab?.UI?.TabContent
})
}

Expand All @@ -43,19 +43,19 @@ export function ProfileTabContent(props: ProfileTabContentProps) {
const classes = useStylesExtends(useStyles(), props)

const { t } = useI18N()
const translate = usePluginI18NField()

const [hidden, setHidden] = useState(true)
const [selectedTab, setSelectedTab] = useState<string | undefined>()

const currentIdentity = useLastRecognizedIdentity()
const identity = useCurrentVisitingIdentity()
const { currentConnectedPersona } = usePersonaConnectStatus()
const platform = activatedSocialNetworkUI.configuration.nextIDConfig?.platform as NextIDPlatform
const { value: addressNames = EMPTY_LIST, loading: loadingAddressNames } = useAddressNames(
NetworkPluginID.PLUGIN_EVM,
identity,
)

const { value: socialAddressList = EMPTY_LIST, loading: loadingSocialAddressList } =
useSocialAddressListAll(identity)
const { value: personaList = EMPTY_LIST, loading: loadingPersonaList } = useNextIDBoundByPlatform(
platform as NextIDPlatform,
platform,
identity.identifier?.userId,
)

Expand All @@ -64,14 +64,14 @@ export function ProfileTabContent(props: ProfileTabContentProps) {
personaList.findIndex((persona) => persona?.persona === currentConnectedPersona?.identifier.publicKeyAsHex) ===
-1

const translate = usePluginI18NField()
const activatedPlugins = useActivatedPluginsSNSAdaptor('any')
const availablePlugins = useAvailablePlugins(activatedPlugins)
const displayPlugins = useMemo(() => {
return availablePlugins
.flatMap((x) => x.ProfileTabs?.map((y) => ({ ...y, pluginID: x.ID })) ?? [])
.filter((z) => z.Utils?.shouldDisplay?.(identity, addressNames) ?? true)
}, [availablePlugins, identity, addressNames])
.filter((z) => z.Utils?.shouldDisplay?.(identity, socialAddressList) ?? true)
}, [availablePlugins])

const tabs = useMemo(() => {
return displayPlugins
.sort((a, z) => {
Expand Down Expand Up @@ -121,17 +121,31 @@ export function ProfileTabContent(props: ProfileTabContentProps) {
})
}, [identity.identifier?.userId])

const ContentComponent = useMemo(() => {
const tabId =
const content = useMemo(() => {
const Component = getTabContent(
isTwitter(activatedSocialNetworkUI) && currentAccountNotConnectPersona
? displayPlugins?.find((tab) => tab?.pluginID === PluginId.NextID)?.ID
: selectedTabId
return getTabContent(tabId ?? '')
}, [selectedTabId, identity.identifier?.userId, currentAccountNotConnectPersona])
: selectedTabId,
)
const Utils = displayPlugins.find((x) => x.ID === selectedTabId)?.Utils

return (
<Component
identity={identity}
personaList={personaList?.map((x) => x.persona)}
socialAddressList={socialAddressList.filter((x) => Utils?.filter?.(x) ?? true).sort(Utils?.sorter)}
/>
)
}, [
selectedTabId,
displayPlugins.map((x) => x.ID).join(),
identity.identifier?.userId,
currentAccountNotConnectPersona,
])

if (hidden) return null

if (loadingAddressNames || loadingPersonaList)
if (!identity.identifier?.userId || loadingSocialAddressList || loadingPersonaList)
return (
<div className={classes.root}>
<Box
Expand All @@ -155,13 +169,7 @@ export function ProfileTabContent(props: ProfileTabContentProps) {
</Typography>
)}
</div>
<div className={classes.content}>
<ContentComponent
addressNames={addressNames}
identity={identity}
personaList={personaList?.map((persona) => persona.persona)}
/>
</div>
<div className={classes.content}>{content}</div>
</div>
)
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { createContext } from 'react'
import type { IdentityAddress, NonFungibleAsset } from '@masknet/web3-shared-base'
import type { SocialAddress, NonFungibleAsset, NetworkPluginID } from '@masknet/web3-shared-base'
import type { ChainId, NonFungibleAssetProvider, SchemaType, Wallet } from '@masknet/web3-shared-evm'
import { Button, styled, Typography } from '@mui/material'
import { makeStyles } from '@masknet/theme'
Expand Down Expand Up @@ -228,7 +228,7 @@ export function CollectionList({
addressName,
onSelectAddress,
}: {
addressName: IdentityAddress
addressName: SocialAddress<NetworkPluginID>
onSelectAddress: (event: React.MouseEvent<HTMLButtonElement>) => void
}) {
return null
Expand Down
14 changes: 8 additions & 6 deletions packages/mask/src/plugins/Avatar/Services/bind.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,12 @@ import { getGunData } from '@masknet/gun-utils'

const READ_GUN_TIMEOUT = 15 * 1000

const NFTAvatarDB = (network: string) => KeyValue.createJSON_Storage(NFT_AVATAR_DB_NAME + '_' + network)
const NFTAvatarDBStorage = (network: string) => KeyValue.createJSON_Storage(NFT_AVATAR_DB_NAME_STORAGE + '_' + network)
const NFTAvatarDB = (network: string) =>
KeyValue.createJSON_Storage<{ networkPluginId: string; chainId: number; address: string }>(
NFT_AVATAR_DB_NAME + '_' + network,
)
const NFTAvatarDBStorage = (network: string) =>
KeyValue.createJSON_Storage<Record<string, string>>(NFT_AVATAR_DB_NAME_STORAGE + '_' + network)

const cache = new Map<string, [Promise<string | undefined>, number]>()

Expand Down Expand Up @@ -41,11 +45,9 @@ function getCacheKey(

async function _getUserAddress(userId: string, network: string, networkPluginId?: NetworkPluginID, chainId?: number) {
try {
const result = await NFTAvatarDB(network).get<{ networkPluginId: string; chainId: number; address: string }>(
userId,
)
const result = await NFTAvatarDB(network).get(userId)
if (!result || !result?.address) {
const result = await NFTAvatarDBStorage(network).get<Record<string, string>>(userId)
const result = await NFTAvatarDBStorage(network).get(userId)
const address = result?.[getKey(networkPluginId, chainId)]
if (address) return address
return getUserAddressFromGUN(userId)
Expand Down
40 changes: 40 additions & 0 deletions packages/mask/src/plugins/Avatar/Services/kv.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import type { EnhanceableSite } from '@masknet/shared-base'
import { KeyValue } from '@masknet/web3-providers'
import { isSameAddress, NetworkPluginID } from '@masknet/web3-shared-base'
import { NFT_AVATAR_DB_NAME } from '../constants'

type AddressStorageV1 = { address: string; networkId: NetworkPluginID }
type AddressStorageV2 = Record<NetworkPluginID, string>

// The DB contains data that an SNS identity handle maps to a Web3 address.
const createAddressDB = (site: EnhanceableSite) =>
KeyValue.createJSON_Storage<AddressStorageV1 | AddressStorageV2>(`${NFT_AVATAR_DB_NAME}_${site}`)

export async function getAddress(site: EnhanceableSite, userId: string, pluginID: NetworkPluginID): Promise<string> {
if (userId === '$unknown') return ''

const storage = await createAddressDB(site).get(userId)
const storageV1 = storage as AddressStorageV1
const storageV2 = storage as AddressStorageV2

if (storageV2[pluginID]) return storageV2[pluginID]

// V1 only supports EVM
if (storageV1.address && pluginID === NetworkPluginID.PLUGIN_EVM) return storageV1.address

return ''
}

export async function setAddress(site: EnhanceableSite, userId: string, pluginID: NetworkPluginID, address: string) {
if (userId === '$unknown') return

const storage = await createAddressDB(site).get(userId)
const storageV2 = storage as AddressStorageV2

if (!isSameAddress(address, storageV2[pluginID])) {
await createAddressDB(site).set(userId, {
...(storageV2 as AddressStorageV2),
[pluginID]: address,
})
}
}
21 changes: 11 additions & 10 deletions packages/mask/src/plugins/Collectible/SNSAdaptor/NFTPage.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { useState } from 'react'
import { first, uniqBy } from 'lodash-unified'
import { ReversedAddress } from '@masknet/shared'
import { getMaskColor, makeStyles, ShadowRootMenu } from '@masknet/theme'
import { MenuItem } from '@mui/material'
import type { SocialAddress, NetworkPluginID, SocialIdentity } from '@masknet/web3-shared-base'
import { CollectionList } from '../../../extension/options-page/DashboardComponents/CollectibleList'
import { first, uniqBy } from 'lodash-unified'
import { ReversedAddress } from '@masknet/shared'
import type { IdentityAddress } from '@masknet/web3-shared-base'
import { EMPTY_LIST } from '@masknet/shared-base'

const useStyles = makeStyles()((theme) => ({
root: {
Expand Down Expand Up @@ -43,18 +44,18 @@ const useStyles = makeStyles()((theme) => ({
}))

export interface NFTPageProps {
addressNames?: IdentityAddress[]
identity?: SocialIdentity
socialAddressList?: Array<SocialAddress<NetworkPluginID>>
}

export function NFTPage(props: NFTPageProps) {
const { addressNames } = props
export function NFTPage({ socialAddressList }: NFTPageProps) {
const { classes } = useStyles()
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null)

const [selectedAddress, setSelectedAddress] = useState(first(addressNames))
const [selectedAddress, setSelectedAddress] = useState(first(socialAddressList))
const onOpen = (event: React.MouseEvent<HTMLButtonElement>) => setAnchorEl(event.currentTarget)
const onClose = () => setAnchorEl(null)
const onSelect = (option: IdentityAddress) => {
const onSelect = (option: SocialAddress<NetworkPluginID>) => {
setSelectedAddress(option)
onClose()
}
Expand All @@ -68,15 +69,15 @@ export function NFTPage(props: NFTPageProps) {
onClose={onClose}
anchorEl={anchorEl}
PaperProps={{ style: { maxHeight: 192 } }}>
{uniqBy(addressNames ?? [], (x) => x.address.toLowerCase()).map((x) => {
{uniqBy(socialAddressList ?? EMPTY_LIST, (x) => x.address.toLowerCase()).map((x) => {
return (
<MenuItem key={x.address} value={x.address} onClick={() => onSelect(x)}>
<ReversedAddress address={x.address} />
</MenuItem>
)
})}
</ShadowRootMenu>
<CollectionList addressName={selectedAddress ?? undefined} onSelectAddress={onOpen} />
<CollectionList addressName={selectedAddress} onSelectAddress={onOpen} />
</div>
)
}
44 changes: 22 additions & 22 deletions packages/mask/src/plugins/Collectible/SNSAdaptor/index.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import { uniq } from 'lodash-unified'
import { Trans } from 'react-i18next'
import { CollectiblesIcon } from '@masknet/icons'
import { type Plugin, usePostInfoDetails, usePluginWrapper } from '@masknet/plugin-infra/content-script'
import { PostInspector } from './PostInspector'
import { base } from '../base'
import { checkUrl, getAssetInfoFromURL, getRelevantUrl } from '../utils'
import { PLUGIN_ID } from '../constants'
import { extractTextFromTypedMessage } from '@masknet/typed-message'
import { NFTPage } from './NFTPage'
import { Trans } from 'react-i18next'
import { CollectiblesIcon } from '@masknet/icons'
import { IdentityAddress, IdentityAddressType } from '@masknet/web3-shared-base'
import { SocialAddressType } from '@masknet/web3-shared-base'

const sns: Plugin.SNSAdaptor.Definition = {
...base,
Expand All @@ -34,36 +34,36 @@ const sns: Plugin.SNSAdaptor.Definition = {
label: 'NFTs',
priority: 1,
UI: {
TabContent: ({ addressNames = [] }) => <NFTPage addressNames={addressNames as IdentityAddress[]} />,
TabContent: NFTPage,
},
Utils: {
addressNameSorter: (a, z) => {
if (a.type === IdentityAddressType.ENS) return -1
if (z.type === IdentityAddressType.ENS) return 1
shouldDisplay: (identity, socialAddressList) => {
return !!socialAddressList?.length
},
sorter: (a, z) => {
if (a.type === SocialAddressType.ENS) return -1
if (z.type === SocialAddressType.ENS) return 1

if (a.type === IdentityAddressType.UNS) return -1
if (z.type === IdentityAddressType.UNS) return 1
if (a.type === SocialAddressType.UNS) return -1
if (z.type === SocialAddressType.UNS) return 1

if (a.type === IdentityAddressType.DNS) return -1
if (z.type === IdentityAddressType.DNS) return 1
if (a.type === SocialAddressType.DNS) return -1
if (z.type === SocialAddressType.DNS) return 1

if (a.type === IdentityAddressType.RSS3) return -1
if (z.type === IdentityAddressType.RSS3) return 1
if (a.type === SocialAddressType.RSS3) return -1
if (z.type === SocialAddressType.RSS3) return 1

if (a.type === IdentityAddressType.ADDRESS) return -1
if (z.type === IdentityAddressType.ADDRESS) return 1
if (a.type === SocialAddressType.ADDRESS) return -1
if (z.type === SocialAddressType.ADDRESS) return 1

if (a.type === IdentityAddressType.GUN) return -1
if (z.type === IdentityAddressType.GUN) return 1
if (a.type === SocialAddressType.GUN) return -1
if (z.type === SocialAddressType.GUN) return 1

if (a.type === IdentityAddressType.THE_GRAPH) return -1
if (z.type === IdentityAddressType.THE_GRAPH) return 1
if (a.type === SocialAddressType.THE_GRAPH) return -1
if (z.type === SocialAddressType.THE_GRAPH) return 1

return 0
},
shouldDisplay: (identity, addressNames) => {
return !!addressNames?.length
},
},
},
],
Expand Down
Loading