Skip to content

Commit

Permalink
fix: mf-6200 nft avatar decoration (#11598)
Browse files Browse the repository at this point in the history
  • Loading branch information
swkatmask authored Apr 28, 2024
1 parent c58e53d commit b4b3375
Show file tree
Hide file tree
Showing 24 changed files with 97 additions and 109 deletions.
5 changes: 4 additions & 1 deletion .github/workflows/compile.yml
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,10 @@ jobs:
# See https://github.com/getsentry/action-release/issues/187
- name: Change git configuration
shell: bash
run: git config --unset-all extensions.worktreeconfig
run: |
if [ -n "$(git config --get extensions.worktreeconfig)" ]; then
git config --unset-all extensions.worktreeconfig
fi
- name: Create Sentry release
uses: getsentry/action-release@v1
if: github.ref == 'refs/heads/develop' || github.ref == 'refs/heads/released'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export function useSaveAvatarInFacebook(identity: IdentityResolved) {
const { account } = useChainContext()

const [NFTEvent, setNFTEvent] = useState<NFTAvatarEvent | null>(null)
const [, saveNFTAvatar] = useSaveStringStorage(NetworkPluginID.PLUGIN_EVM)
const saveNFTAvatar = useSaveStringStorage(NetworkPluginID.PLUGIN_EVM)

const onSave = useCallback(async () => {
if (!account || !identity.identifier) return
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export function NFTAvatarSettingDialog() {
const { classes } = useStyles()
const { account } = useChainContext()
const identity = useCurrentVisitingIdentity()
const [, saveNFTAvatar] = useSaveStringStorage(NetworkPluginID.PLUGIN_EVM)
const saveNFTAvatar = useSaveStringStorage(NetworkPluginID.PLUGIN_EVM)

const onChange = useCallback(
async (info: SelectTokenInfo) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ function resolveCurrentVisitingIdentityInner(

const handle = user.screenName
const ownerHandle = ownerRef.value.identifier?.userId
const isOwner = !!(ownerHandle && handle.toLowerCase() === ownerHandle.toLowerCase())
const isOwner = !!ownerHandle && handle.toLowerCase() === ownerHandle.toLowerCase()
const avatar = user.avatarURL
const bio = user.bio
const homepage = user.homepage
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import { useLocation, useWindowSize } from 'react-use'
import { AvatarStore, Twitter } from '@masknet/web3-providers'
import { useInjectedCSS } from './useInjectedCSS.js'
import { useUpdatedAvatar } from './useUpdatedAvatar.js'
import { useSaveAvatarInTwitter } from './useSaveAvatarInTwitter.js'
import { searchAvatarMetaSelector, searchAvatarSelector, searchTwitterAvatarSelector } from '../../utils/selector.js'
import { useCurrentVisitingIdentity } from '../../../../components/DataSource/useActivatedUI.js'

Expand Down Expand Up @@ -89,21 +88,21 @@ export function NFTAvatarInTwitter() {

function useNFTCircleAvatar(size: number) {
const identity = useCurrentVisitingIdentity()
const savedAvatar = useSaveAvatarInTwitter(identity)

const userId = identity.identifier?.userId || ''
const identityAvatarId = Twitter.getAvatarId(identity.avatar)
const store = useSyncExternalStore(AvatarStore.subscribe, AvatarStore.getSnapshot)
const avatar =
savedAvatar ?? store.retrieveAvatar(identity.identifier?.userId, Twitter.getAvatarId(identity.avatar))
const token = store.retrieveToken(identity.identifier?.userId, Twitter.getAvatarId(identity.avatar))
const avatar = store.retrieveAvatar(userId, identityAvatarId)
const token = store.retrieveToken(userId, identityAvatarId)

const showAvatar = useMemo(() => {
const avatarUrl = searchAvatarSelector().evaluate()?.getAttribute('src')
if (!avatarUrl || !avatar?.avatarId) return false
return Twitter.getAvatarId(avatarUrl ?? '') === avatar.avatarId
}, [avatar?.avatarId, identity.avatar])
useEffect(() => {
AvatarStore.dispatch(userId, identityAvatarId)
}, [userId, identityAvatarId])

const showAvatar = avatar?.avatarId ? identityAvatarId === avatar.avatarId : false

return {
showAvatar: Boolean(size && avatar && showAvatar && token),
showAvatar: Boolean(size && showAvatar && token),
avatar,
token,
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { useAsync } from 'react-use'
export function useSaveAvatarInTwitter(identity: IdentityResolved) {
const { account } = useChainContext()

const [, saveNFTAvatar] = useSaveStringStorage(NetworkPluginID.PLUGIN_EVM)
const saveNFTAvatar = useSaveStringStorage(NetworkPluginID.PLUGIN_EVM)

const onSave = useCallback(async () => {
if (!account || !identity.identifier) return
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,7 @@ async function uploadAvatar(blob: Blob, userId: string): Promise<AvatarInfo | un
const media = await Twitter.uploadMedia(blob)
const data = await Twitter.updateProfileImage(userId, media.media_id_string)
if (!data) return
const avatarId = Twitter.getAvatarId(data?.imageUrl ?? '')
return { ...data, avatarId }
return { ...data, avatarId: media.media_id_string }
} catch (err) {
return
}
Expand All @@ -68,7 +67,7 @@ export function UploadAvatarDialog() {
const [disabled, setDisabled] = useState(false)
const { currentPersona } = usePersonaConnectStatus()
const identity = useLastRecognizedIdentity()
const [, saveAvatar] = useSave(currentPluginID)
const saveAvatar = useSave(currentPluginID)
const navigate = useNavigate()

const onSave = useCallback(async () => {
Expand Down
4 changes: 1 addition & 3 deletions packages/plugins/Avatar/src/constants.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { PluginID, NetworkPluginID, EnhanceableSite, getSiteType } from '@masknet/shared-base'
import { NetworkPluginID, PluginID } from '@masknet/shared-base'
import { ChainId } from '@masknet/web3-shared-evm'

export const NFT_AVATAR_DB_NAME = 'com.maskbook.user'
Expand All @@ -11,5 +11,3 @@ export const PLUGIN_DESCRIPTION = 'NFT Avatar'
export const SUPPORTED_CHAIN_IDS: ChainId[] = [ChainId.Mainnet, ChainId.Matic, ChainId.BSC]

export const supportPluginIds = [NetworkPluginID.PLUGIN_EVM, NetworkPluginID.PLUGIN_FLOW, NetworkPluginID.PLUGIN_SOLANA]

export const StorageKey = `${PLUGIN_NAME}-${(getSiteType() || EnhanceableSite.Twitter).replace('.com', '')}`
21 changes: 10 additions & 11 deletions packages/plugins/Avatar/src/hooks/useSave.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
import { useAsyncFn } from 'react-use'
import { NetworkPluginID, type BindingProof, type ECKeyIdentifier } from '@masknet/shared-base'
import { type ECKeyIdentifier, NetworkPluginID, type BindingProof } from '@masknet/shared-base'
import type { AvatarNextID, TwitterBaseAPI } from '@masknet/web3-providers/types'
import { ChainId, SchemaType } from '@masknet/web3-shared-evm'
import type { AllChainsNonFungibleToken } from '../types.js'
import { useSaveKV } from './useSaveKV.js'
import { useSaveToNextID } from './useSaveToNextID.js'
import { useSaveStringStorage } from './useSaveStringStorage.js'
import { useCallback } from 'react'

export type AvatarInfo = TwitterBaseAPI.AvatarInfo & {
avatarId: string
}

export function useSave(pluginID: NetworkPluginID) {
const [, saveToNextID] = useSaveToNextID()
const [, saveToStringStorage] = useSaveStringStorage(pluginID)
const [, saveToKV] = useSaveKV(pluginID)
const saveToNextID = useSaveToNextID()
const saveToStringStorage = useSaveStringStorage(pluginID)
const saveToKV = useSaveKV(pluginID)

return useAsyncFn(
return useCallback(
async (
account: string,
isBindAccount: boolean,
Expand All @@ -32,11 +32,11 @@ export function useSave(pluginID: NetworkPluginID) {
userId: data.userId,
imageUrl: data.imageUrl,
avatarId: data.avatarId,
address: token.contract.address ?? '',
address: token.contract.address,
ownerAddress: account,
tokenId: token.tokenId || token.id,
chainId: (token.contract.chainId ?? ChainId.Mainnet) as ChainId,
schema: (token.contract.schema ?? SchemaType.ERC721) as SchemaType,
chainId: (token.contract.chainId || ChainId.Mainnet) as ChainId,
schema: (token.contract.schema || SchemaType.ERC721) as SchemaType,
}

try {
Expand All @@ -46,8 +46,7 @@ export function useSave(pluginID: NetworkPluginID) {
return await saveToStringStorage(data.userId, account, info)
}
default:
if (!proof) return
return await saveToKV(info, account, persona, proof)
return await saveToKV(info, account)
}
} catch {
return
Expand Down
33 changes: 18 additions & 15 deletions packages/plugins/Avatar/src/hooks/useSaveAddress.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,27 @@
import { useAsyncFn } from 'react-use'
import { Web3Storage } from '@masknet/web3-providers'
import type { AddressStorageV2 } from '@masknet/web3-providers/types'
import { type NetworkPluginID, EnhanceableSite } from '@masknet/shared-base'
import { NFT_AVATAR_DB_NAME } from '../constants.js'
import { useCallback } from 'react'

export function useSaveAddress() {
return useAsyncFn(async (userId: string, pluginID: NetworkPluginID, account: string, network?: EnhanceableSite) => {
const addressStorage = Web3Storage.createKVStorage(
`${NFT_AVATAR_DB_NAME}_${network ?? EnhanceableSite.Twitter}`,
)
if (!addressStorage) return
return useCallback(
async (userId: string, pluginID: NetworkPluginID, account: string, network?: EnhanceableSite) => {
const addressStorage = Web3Storage.createKVStorage(
`${NFT_AVATAR_DB_NAME}_${network || EnhanceableSite.Twitter}`,
)
if (!addressStorage) return

const prevData = (await addressStorage.get<AddressStorageV2>(userId).catch(() => ({}))) as
| AddressStorageV2
| undefined
const prevData = (await addressStorage.get<AddressStorageV2>(userId).catch(() => ({}))) as
| AddressStorageV2
| undefined

await addressStorage.set<AddressStorageV2>(userId, {
...prevData,
[pluginID]: account,
[userId]: { address: account, networkPluginID: pluginID },
} as AddressStorageV2)
}, [])
await addressStorage.set<AddressStorageV2>(userId, {
...prevData,
[pluginID]: account,
[userId]: { address: account, networkPluginID: pluginID },
} as AddressStorageV2)
},
[],
)
}
10 changes: 5 additions & 5 deletions packages/plugins/Avatar/src/hooks/useSaveAvatar.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import { useAsyncFn } from 'react-use'
import { NetworkPluginID, type EnhanceableSite } from '@masknet/shared-base'
import { Web3Storage } from '@masknet/web3-providers'
import { type AvatarNextID } from '@masknet/web3-providers/types'
import { NetworkPluginID, type EnhanceableSite } from '@masknet/shared-base'
import { useSaveAddress } from './useSaveAddress.js'
import { useCallback } from 'react'
import { NFT_AVATAR_METADATA_STORAGE } from '../constants.js'
import { useSaveAddress } from './useSaveAddress.js'

export function useSaveAvatar(pluginID?: NetworkPluginID) {
const [, saveAddress] = useSaveAddress()
const saveAddress = useSaveAddress()

return useAsyncFn(
return useCallback(
async (siteType: EnhanceableSite, account: string, avatar: AvatarNextID<NetworkPluginID>, sign: string) => {
if (avatar.userId === '$unknown') return
await saveAddress(avatar.userId, avatar.pluginId ?? NetworkPluginID.PLUGIN_EVM, account, siteType)
Expand Down
15 changes: 5 additions & 10 deletions packages/plugins/Avatar/src/hooks/useSaveKV.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,15 @@
import { useAsyncFn } from 'react-use'
import { getEnhanceableSiteType, type NetworkPluginID } from '@masknet/shared-base'
import { useWeb3Connection } from '@masknet/web3-hooks-base'
import {
type BindingProof,
type ECKeyIdentifier,
type NetworkPluginID,
getEnhanceableSiteType,
} from '@masknet/shared-base'
import type { AvatarNextID } from '@masknet/web3-providers/types'
import { useSaveAvatar } from './useSaveAvatar.js'
import { useCallback } from 'react'

export function useSaveKV(pluginID: NetworkPluginID) {
const Web3 = useWeb3Connection(pluginID)
const [, saveAvatar] = useSaveAvatar(pluginID)
const saveAvatar = useSaveAvatar(pluginID)

return useAsyncFn(
async (info: AvatarNextID<NetworkPluginID>, account: string, persona: ECKeyIdentifier, proof: BindingProof) => {
return useCallback(
async (info: AvatarNextID<NetworkPluginID>, account: string) => {
const siteType = getEnhanceableSiteType()
if (!siteType) return

Expand Down
17 changes: 6 additions & 11 deletions packages/plugins/Avatar/src/hooks/useSaveStringStorage.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,15 @@
import { useAsyncFn } from 'react-use'
import { Web3Storage } from '@masknet/web3-providers'
import { getEnhanceableSiteType, type NetworkPluginID } from '@masknet/shared-base'
import { setAvatarToStorage } from '@masknet/web3-providers'
import type { AvatarNextID } from '@masknet/web3-providers/types'
import { type NetworkPluginID, getEnhanceableSiteType, EnhanceableSite } from '@masknet/shared-base'
import { useSaveAddress } from './useSaveAddress.js'
import { PLUGIN_NAME } from '../constants.js'
import { useCallback } from 'react'

export function useSaveStringStorage(pluginID: NetworkPluginID) {
const [, saveAddress] = useSaveAddress()
const saveAddress = useSaveAddress()

return useAsyncFn(
return useCallback(
async (userId: string, address: string, avatar: AvatarNextID<NetworkPluginID>) => {
const stringStorage = Web3Storage.createFireflyStorage(
`${PLUGIN_NAME}-${(getEnhanceableSiteType() || EnhanceableSite.Twitter).replace('.com', '')}`,
address,
)
await stringStorage.set?.(userId, avatar)
await setAvatarToStorage(userId, address, avatar)
await saveAddress(avatar.userId, pluginID, address, getEnhanceableSiteType())

return avatar
Expand Down
8 changes: 4 additions & 4 deletions packages/plugins/Avatar/src/hooks/useSaveToNextID.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import { useAsyncFn } from 'react-use'
import { type BindingProof, type ECKeyIdentifier, NetworkPluginID, getEnhanceableSiteType } from '@masknet/shared-base'
import { Web3Storage } from '@masknet/web3-providers'
import type { AvatarNextID } from '@masknet/web3-providers/types'
import { signWithPersona } from '@masknet/plugin-infra/dom/context'
import { useSaveAddress } from './useSaveAddress.js'
import { PLUGIN_ID } from '../constants.js'
import { useCallback } from 'react'

export function useSaveToNextID() {
const [, saveAddress] = useSaveAddress()
return useAsyncFn(
const saveAddress = useSaveAddress()
return useCallback(
async (
info: AvatarNextID<NetworkPluginID>,
account: string,
Expand All @@ -23,7 +23,7 @@ export function useSaveToNextID() {
const storage = Web3Storage.createNextIDStorage(proof.identity, proof.platform, persona, signWithPersona)
await storage.set(PLUGIN_ID, info)

await saveAddress(info.userId, info.pluginId ?? NetworkPluginID.PLUGIN_EVM, account, siteType)
await saveAddress(info.userId, info.pluginId || NetworkPluginID.PLUGIN_EVM, account, siteType)

return info
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { EnhanceableSite, NetworkPluginID, createLookupTableResolver } from '@ma
import { getAddress } from './getAddress.js'
import { Web3Storage } from '../../Storage/apis/Storage.js'
import { RSS3_KEY_SITE, type AvatarNextID } from '../types.js'
import { getAvatarFromStorage } from './getAvatarFromStorage.js'
import { getAvatarFromStorage } from './storage.js'
import { getAvatarFromRSS3 } from './getAvatarFromRSS3.js'

const NFT_AVATAR_METADATA_STORAGE = 'com.maskbook.avatar.metadata.storage'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,15 +41,15 @@ export async function getAvatarFromNextID<T extends NetworkPluginID>(
siteType: EnhanceableSite,
userId: string,
avatarId: string,
persona: string,
persona?: string,
): Promise<AvatarNextID<T> | undefined> {
const platform = siteType === EnhanceableSite.Twitter ? NextIDPlatform.Twitter : undefined
if (!platform) return

const bindings = await NextIDProof.queryAllExistedBindingsByPlatform(platform, userId)

if (persona) {
const binding = bindings.filter((x) => x.persona.toLowerCase() === persona.toLowerCase())?.[0]
const binding = bindings.find((x) => x.persona.toLowerCase() === persona.toLowerCase())
if (binding) return getAvatarFromNextIDStorage<T>(platform, userId, avatarId, binding.persona)
}
for (const binding of bindings.sort((a, b) => sortPersonaBindings(a, b, userId))) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export async function getPersonaAvatar<T extends NetworkPluginID>(
siteType: EnhanceableSite,
userId: string,
avatarId: string,
persona: string,
persona?: string,
): Promise<AvatarNextID<T> | null> {
// only twitter is supported
if (siteType !== EnhanceableSite.Twitter) return null
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,12 @@ export function getAvatarFromStorage(userId: string, address: string) {
const stringStorage = Web3Storage.createFireflyStorage(KEY, address)
return stringStorage.get?.<AvatarNextID<NetworkPluginID.PLUGIN_EVM>>(userId)
}

export function setAvatarToStorage<T extends NetworkPluginID = NetworkPluginID.PLUGIN_EVM>(
userId: string,
address: string,
avatar: AvatarNextID<T>,
) {
const stringStorage = Web3Storage.createFireflyStorage(KEY, address)
return stringStorage.set?.<AvatarNextID<T>>(userId, avatar)
}
3 changes: 2 additions & 1 deletion packages/web3-providers/src/AvatarStore/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ class AvatarStoreProvider extends StoreProvider<Store> implements StoreAPI.Provi
if (!address) return null

const avatar =
siteType === EnhanceableSite.Twitter && avatarId && publicKey ?
siteType === EnhanceableSite.Twitter && avatarId ?
await getPersonaAvatar(siteType, userId, avatarId, publicKey)
: await getAvatar(siteType, userId)
if (!avatar) return null
Expand Down Expand Up @@ -75,3 +75,4 @@ class AvatarStoreProvider extends StoreProvider<Store> implements StoreAPI.Provi
}

export const AvatarStore = new AvatarStoreProvider()
export { getAvatarFromStorage, setAvatarToStorage } from './helpers/storage.js'
2 changes: 1 addition & 1 deletion packages/web3-providers/src/Storage/apis/Storage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export class Web3Storage {
}

static createFireflyStorage(namespace: string, address: string) {
return new FireflyStorage.FireflyStorage(namespace, address || '')
return new FireflyStorage.FireflyStorage(namespace, address)
}

static createRSS3Storage(namespace: string) {
Expand Down
Loading

0 comments on commit b4b3375

Please sign in to comment.