diff --git a/packages/plugins/Web3Profile/src/SiteAdaptor/components/Lens/FollowLensDialog.tsx b/packages/plugins/Web3Profile/src/SiteAdaptor/components/Lens/FollowLensDialog.tsx index 9a40f4a66c78..f042f99dbb8c 100644 --- a/packages/plugins/Web3Profile/src/SiteAdaptor/components/Lens/FollowLensDialog.tsx +++ b/packages/plugins/Web3Profile/src/SiteAdaptor/components/Lens/FollowLensDialog.tsx @@ -119,7 +119,7 @@ export function FollowLensDialog({ handle, onClose }: Props) { const { showSnackbar } = useCustomSnackbar() const lensClient = useLensClient() - const myLensAccount = useMyLensAccountAddress() + const myLensAccount = useMyLensAccountAddress(true) const { data: lensAccount, isLoading } = useQuery({ enabled: !!handle && !!open, @@ -134,17 +134,14 @@ export function FollowLensDialog({ handle, onClose }: Props) { const isSelf = isSameAddress(lensAccount?.username?.ownedBy as string, walletAccount) const currentAccount = accounts?.find((p) => isSameAddress(p.account.address, myLensAccount)) || first(accounts) - const currentAccountAddress: EvmAddress | undefined = currentAccount?.account.address const targetAccountAddress: EvmAddress | undefined = lensAccount?.address const { isPending, data: isFollowing } = useQuery({ - queryKey: ['lens', 'following-status', currentAccountAddress, targetAccountAddress, !lensClient], + queryKey: ['lens', 'following-status', myLensAccount, targetAccountAddress, !lensClient], queryFn: async () => { - if (!targetAccountAddress || !currentAccountAddress || !lensClient) return false - const res = await lensClient.getFollowStatus([ - { account: targetAccountAddress, follower: currentAccountAddress }, - ]) + if (!targetAccountAddress || !myLensAccount || !lensClient) return false + const res = await lensClient.getFollowStatus([{ account: targetAccountAddress, follower: myLensAccount }]) const status = res[0].isFollowing - return status.onChain || status.optimistic + return status.onChain }, refetchOnWindowFocus: false, staleTime: 0, @@ -157,31 +154,34 @@ export function FollowLensDialog({ handle, onClose }: Props) { accountAddress: lensAccount?.address, onSuccess: (width: number, height: number) => { showConfettiExplosion(width, height) - updateFollowingStatus(currentAccountAddress, targetAccountAddress, true) + updateFollowingStatus(myLensAccount, targetAccountAddress, true) }, - onFailed: () => updateFollowingStatus(currentAccountAddress, handle, false), + onFailed: () => updateFollowingStatus(myLensAccount, targetAccountAddress, false), }) const { loading: unfollowLoading, handleUnfollow } = useUnfollow({ accountAddress: lensAccount?.address as string, - onSuccess: () => updateFollowingStatus(currentAccountAddress, targetAccountAddress, false), - onFailed: () => updateFollowingStatus(currentAccountAddress, targetAccountAddress, true), + onSuccess: () => updateFollowingStatus(myLensAccount, targetAccountAddress, false), + onFailed: () => updateFollowingStatus(myLensAccount, targetAccountAddress, true), }) // #endregion - const handleClick = useCallback(() => { - if (task) { - showSnackbar(isFollowing ? Lens Unfollow : Lens Follow, { - processing: true, - message: - isFollowing ? - Previous unfollow transaction is in processing, please wait and try again. - : Previous follow transaction is in processing, please wait and try again., - autoHideDuration: 2000, - }) - return - } - task = (isFollowing ? handleUnfollow() : handleFollow()).finally(() => (task = undefined)) - }, [handleFollow, handleUnfollow, isFollowing, showSnackbar]) + const handleClick = useCallback( + (event: React.MouseEvent) => { + if (task) { + showSnackbar(isFollowing ? Lens Unfollow : Lens Follow, { + processing: true, + message: + isFollowing ? + Previous unfollow transaction is in processing, please wait and try again. + : Previous follow transaction is in processing, please wait and try again., + autoHideDuration: 2000, + }) + return + } + task = (isFollowing ? handleUnfollow() : handleFollow(event)).finally(() => (task = undefined)) + }, + [handleFollow, handleUnfollow, isFollowing, showSnackbar], + ) const accountConditions = !walletAccount || !currentAccount || !!wallet?.owner || pluginID !== NetworkPluginID.PLUGIN_EVM diff --git a/packages/plugins/Web3Profile/src/SiteAdaptor/components/SocialBadges/LensList.tsx b/packages/plugins/Web3Profile/src/SiteAdaptor/components/SocialBadges/LensList.tsx index 9c7c623784fb..9cb0ef0e76fd 100644 --- a/packages/plugins/Web3Profile/src/SiteAdaptor/components/SocialBadges/LensList.tsx +++ b/packages/plugins/Web3Profile/src/SiteAdaptor/components/SocialBadges/LensList.tsx @@ -98,6 +98,7 @@ export const LensList = memo(function LensList({ accounts }: Props) { if (!target) return return { ...target, + address: nativeAccount.address, ownedBy: nativeAccount.username?.ownedBy as string, } }), @@ -107,7 +108,7 @@ export const LensList = memo(function LensList({ accounts }: Props) { const followStatus = await lensV3.getFollowStatus( (myLensAccount ? nativeAccounts || [] : []).map((x) => ({ follower: evmAddress(myLensAccount!), - account: evmAddress(x.username?.ownedBy), + account: evmAddress(x.address), })), ) return compact( @@ -121,8 +122,9 @@ export const LensList = memo(function LensList({ accounts }: Props) { )?.isFollowing return { ...target, + address: nativeAccount.address, ownedBy: nativeAccount.username?.ownedBy as string, - isFollowing: status?.onChain || status?.optimistic, + isFollowing: status?.optimistic || status?.onChain, } }), ) diff --git a/packages/plugins/Web3Profile/src/SiteAdaptor/hooks/Lens/useFollow.tsx b/packages/plugins/Web3Profile/src/SiteAdaptor/hooks/Lens/useFollow.tsx index 16884efc244e..0f59233c147a 100644 --- a/packages/plugins/Web3Profile/src/SiteAdaptor/hooks/Lens/useFollow.tsx +++ b/packages/plugins/Web3Profile/src/SiteAdaptor/hooks/Lens/useFollow.tsx @@ -13,7 +13,7 @@ export type FollowOptions = { onFailed?: () => void } -export function useFollow({ accountAddress, onFailed }: FollowOptions) { +export function useFollow({ accountAddress, onSuccess, onFailed }: FollowOptions) { const { t } = useLingui() const [loading, setLoading] = useState(false) const { chainId } = useChainContext() @@ -35,43 +35,48 @@ export function useFollow({ accountAddress, onFailed }: FollowOptions) { const myLensAccount = useMyLensAccountAddress() const lensClient = useLensClient() - const handleFollow = useCallback<() => Promise>(async () => { - try { - setLoading(true) - if (!accountAddress || chainId !== ChainId.Polygon) return - if (!lensClient || !myLensAccount) return - await lensClient.login(myLensAccount) - const res = await lensClient.follow(accountAddress) - if (res.isErr()) { - throw res.error + const handleFollow = useCallback( + async (event: React.MouseEvent) => { + try { + setLoading(true) + if (!accountAddress || chainId !== ChainId.Polygon) return + if (!lensClient || !myLensAccount) return + await lensClient.login(myLensAccount) + const res = await lensClient.follow(accountAddress) + if (res.isErr()) { + throw res.error + } + const target = event.target as HTMLButtonElement + onSuccess?.(target.offsetWidth, target.offsetHeight) + } catch (error) { + if (!(error instanceof Error)) return + const message = error.message + if (message.match(/Bad user input .* is already following/)) { + showSingletonSnackbar(t`Follow Lens handle`, { + processing: false, + variant: 'warning', + message: Already following, + }) + } else if ( + !message.includes('Transaction was rejected') && + !message.includes('Signature canceled') && + !message.includes('User rejected the request') && + !message.includes('User rejected transaction') && + !message.includes('RPC Error') + ) { + onFailed?.() + showSingletonSnackbar(t`Follow Lens handle`, { + processing: false, + variant: 'error', + message: Network error, try again: {error.message}, + }) + } + } finally { + setLoading(false) } - } catch (error) { - if (!(error instanceof Error)) return - const message = error.message - if (message.match(/Bad user input .* is already following/)) { - showSingletonSnackbar(t`Follow Lens handle`, { - processing: false, - variant: 'warning', - message: Already following, - }) - } else if ( - !message.includes('Transaction was rejected') && - !message.includes('Signature canceled') && - !message.includes('User rejected the request') && - !message.includes('User rejected transaction') && - !message.includes('RPC Error') - ) { - onFailed?.() - showSingletonSnackbar(t`Follow Lens handle`, { - processing: false, - variant: 'error', - message: Network error, try again: {error.message}, - }) - } - } finally { - setLoading(false) - } - }, [accountAddress, chainId, lensClient, myLensAccount, onFailed, showSingletonSnackbar, t]) + }, + [accountAddress, chainId, lensClient, myLensAccount, onFailed, showSingletonSnackbar, t], + ) return { loading, handleFollow } } diff --git a/packages/plugins/Web3Profile/src/SiteAdaptor/hooks/Lens/useUpdateFollowingStatus.ts b/packages/plugins/Web3Profile/src/SiteAdaptor/hooks/Lens/useUpdateFollowingStatus.ts index 9d3b51ac2cd2..f4a4c54f1ad2 100644 --- a/packages/plugins/Web3Profile/src/SiteAdaptor/hooks/Lens/useUpdateFollowingStatus.ts +++ b/packages/plugins/Web3Profile/src/SiteAdaptor/hooks/Lens/useUpdateFollowingStatus.ts @@ -1,4 +1,5 @@ import type { FireflyConfigAPI } from '@masknet/web3-providers/types' +import { isSameAddress } from '@masknet/web3-shared-base' import { useQueryClient } from '@tanstack/react-query' import { useCallback } from 'react' @@ -12,7 +13,7 @@ export function useUpdateFollowingStatus() { (data) => { if (!data) return data return data.map((x) => { - return x.handle === targetLensAccount ? { ...x, isFollowing } : x + return isSameAddress(x.address, targetLensAccount) ? { ...x, isFollowing } : x }) }, ) diff --git a/packages/shared/src/hooks/useAvailableLensAccounts.ts b/packages/shared/src/hooks/useAvailableLensAccounts.ts index 2afc2bf766ec..ef18326a75db 100644 --- a/packages/shared/src/hooks/useAvailableLensAccounts.ts +++ b/packages/shared/src/hooks/useAvailableLensAccounts.ts @@ -4,7 +4,7 @@ import { useChainContext } from '@masknet/web3-hooks-base' import { useQuery } from '@tanstack/react-query' import { useLensClient } from './useLensClient.js' -export function useAvailableLensAccounts() { +export function useAvailableLensAccounts(isManaged?: boolean) { const { account: walletAccount } = useChainContext() const lensClient = useLensClient() @@ -15,5 +15,11 @@ export function useAvailableLensAccounts() { const accounts = await lensClient.getAvailableAccounts(evmAddress(walletAccount)) return accounts }, + select(data) { + if (isManaged === undefined || !data) return data + return data.filter((account) => + isManaged ? account.__typename === 'AccountManaged' : account.__typename === 'AccountOwned', + ) + }, }) } diff --git a/packages/shared/src/hooks/useMyLensAccountAddress.ts b/packages/shared/src/hooks/useMyLensAccountAddress.ts index 4fda29b5e2aa..fef15e8fcfed 100644 --- a/packages/shared/src/hooks/useMyLensAccountAddress.ts +++ b/packages/shared/src/hooks/useMyLensAccountAddress.ts @@ -1,21 +1,24 @@ import type { EvmAddress } from '@lens-protocol/client' -import { PersistentStorages, type NetworkPluginID } from '@masknet/shared-base' -import { useChainContext } from '@masknet/web3-hooks-base' +import { PersistentStorages } from '@masknet/shared-base' import { isSameAddress } from '@masknet/web3-shared-base' import { useMemo } from 'react' import { useSubscription } from 'use-subscription' import { useAvailableLensAccounts } from './useAvailableLensAccounts.js' +import { first } from 'lodash-es' -export function useMyLensAccountAddress(): EvmAddress | undefined { - const { account: walletAccount } = useChainContext() +export function useMyLensAccountAddress(isManaged?: boolean): EvmAddress | undefined { const lastLensAccount = useSubscription(PersistentStorages.Settings.storage.lastLensAccount.subscription) - const { data: lensAccounts } = useAvailableLensAccounts() + const { data: lensAccounts } = useAvailableLensAccounts(isManaged) return useMemo(() => { - if (!walletAccount) return + if (!lensAccounts?.length) return // Make sure lastLensAccount is in lensAccounts - return lensAccounts?.find((x) => isSameAddress(x.account.address, lastLensAccount))?.account.address - }, [walletAccount, lastLensAccount, lensAccounts]) + const lensAccount = + lastLensAccount ? + lensAccounts.find((x) => isSameAddress(x.account.address, lastLensAccount)) + : first(lensAccounts) + return lensAccount?.account.address + }, [lastLensAccount, lensAccounts]) } export function setMyLensAccountAddress(address: string) { diff --git a/packages/web3-providers/src/LensV3/helpers.ts b/packages/web3-providers/src/LensV3/helpers.ts index cc3694198a65..c26c6840abf3 100644 --- a/packages/web3-providers/src/LensV3/helpers.ts +++ b/packages/web3-providers/src/LensV3/helpers.ts @@ -104,7 +104,7 @@ export function formatLensPost(result: AnyPost): Social.Post { canMirror: result.repostOf.operations?.canRepost.__typename === 'PostOperationValidationPassed', hasMirrored: result.repostOf.operations?.hasReported, hasQuoted: - result.repostOf.operations?.hasQuoted.onChain || result.repostOf.operations?.hasQuoted.optimistic, + result.repostOf.operations?.hasQuoted.optimistic || result.repostOf.operations?.hasQuoted.onChain, hasLiked: result.repostOf.operations?.hasUpvoted, hasBookmarked: result.repostOf.operations?.hasBookmarked, mentions: result.repostOf.mentions.map((x) => { @@ -159,7 +159,7 @@ export function formatLensPost(result: AnyPost): Social.Post { canComment: result.operations?.canComment.__typename === 'PostOperationValidationPassed', canMirror: result.operations?.canRepost.__typename === 'PostOperationValidationPassed', hasMirrored: result.operations?.hasReported, - hasQuoted: result.operations?.hasQuoted.onChain || result.operations?.hasQuoted.optimistic, + hasQuoted: result.operations?.hasQuoted.optimistic || result.operations?.hasQuoted.onChain, hasLiked: result.operations?.hasUpvoted, hasBookmarked: result.operations?.hasBookmarked, quoteOn: formatLensQuoteOrComment(result.quoteOf), @@ -199,8 +199,8 @@ export function formatLensPost(result: AnyPost): Social.Post { commentOn: formatLensQuoteOrComment(result.commentOn), canComment: result.operations?.canComment.__typename === 'PostOperationValidationPassed', canMirror: result.operations?.canRepost.__typename === 'PostOperationValidationPassed', - hasMirrored: result.operations?.hasReposted.onChain || result.operations?.hasReposted.optimistic, - hasQuoted: result.operations?.hasQuoted.onChain || result.operations?.hasQuoted.optimistic, + hasMirrored: result.operations?.hasReposted.optimistic || result.operations?.hasReposted.onChain, + hasQuoted: result.operations?.hasQuoted.optimistic || result.operations?.hasQuoted.onChain, hasLiked: result.operations?.hasUpvoted, hasBookmarked: result.operations?.hasBookmarked, mentions: result.mentions.map((x) => { @@ -237,8 +237,8 @@ export function formatLensPost(result: AnyPost): Social.Post { }, canComment: result.operations?.canComment.__typename === 'PostOperationValidationPassed', canMirror: result.operations?.canRepost.__typename === 'PostOperationValidationPassed', - hasMirrored: result.operations?.hasReposted.onChain || result.operations?.hasReposted.optimistic, - hasQuoted: result.operations?.hasQuoted.onChain || result.operations?.hasQuoted.optimistic, + hasMirrored: result.operations?.hasReposted.optimistic || result.operations?.hasReposted.onChain, + hasQuoted: result.operations?.hasQuoted.optimistic || result.operations?.hasQuoted.onChain, hasLiked: result.operations?.hasUpvoted, hasBookmarked: result.operations?.hasBookmarked, mentions: result.mentions.map((x) => { @@ -356,8 +356,8 @@ function formatLensQuoteOrComment(result: ReferencedPost): Social.Post { }, canComment: result.operations?.canComment.__typename === 'PostOperationValidationPassed', canMirror: result.operations?.canRepost.__typename === 'PostOperationValidationPassed', - hasMirrored: result.operations?.hasReposted.onChain || result.operations?.hasReposted.optimistic, - hasQuoted: result.operations?.hasQuoted.onChain || result.operations?.hasQuoted.optimistic, + hasMirrored: result.operations?.hasReposted.optimistic || result.operations?.hasReposted.onChain, + hasQuoted: result.operations?.hasQuoted.optimistic || result.operations?.hasQuoted.onChain, hasLiked: result.operations?.hasUpvoted, hasBookmarked: result.operations?.hasBookmarked, stats, diff --git a/packages/web3-providers/src/LensV3/index.ts b/packages/web3-providers/src/LensV3/index.ts index 23e8a64c2f2c..89e3edcc3511 100644 --- a/packages/web3-providers/src/LensV3/index.ts +++ b/packages/web3-providers/src/LensV3/index.ts @@ -139,7 +139,7 @@ export class LensV3 { }) if (result.isErr()) return [] const list = uniqBy( - sortBy(result.value.items, (x) => (x.__typename === 'AccountOwned' ? -1 : 0)), + sortBy(result.value.items, (x) => (x.__typename === 'AccountManaged' ? -1 : 0)), (x) => x.account.address, ) return list as AccountAvailable[] diff --git a/packages/web3-providers/src/NextID/proof.ts b/packages/web3-providers/src/NextID/proof.ts index 47d12dca4c80..48e76cbf7fc2 100644 --- a/packages/web3-providers/src/NextID/proof.ts +++ b/packages/web3-providers/src/NextID/proof.ts @@ -161,7 +161,7 @@ export class NextIDProof { const nextIDPersonaBindings: NextIDPersonaBindings[] = [] let page = 1 do { - const bindings = await fetchFromProofService( + const bindings = await fetchFromProofService( urlcat(BASE_URL, '/v1/proof', { platform, identity, @@ -170,8 +170,12 @@ export class NextIDProof { order: 'desc', }), ) + if ('message' in bindings) { + console.error('NextIDProof.queryAllExistedBindingsByPlatform', bindings.message) + return nextIDPersonaBindings + } const personaBindings = bindings.ids - if (personaBindings.length === 0) return nextIDPersonaBindings + if (!personaBindings?.length) return nextIDPersonaBindings nextIDPersonaBindings.push(...personaBindings) // next is `0` if current page is the last one.