From df514102a7eec36be156ade7e1e649e4f525c8ac Mon Sep 17 00:00:00 2001 From: Wukong Sun <158803171+swkatmask@users.noreply.github.com> Date: Fri, 22 Mar 2024 11:11:13 +0800 Subject: [PATCH] feat: fw-496 a single account can only claim once from a redpacket (#11495) * feat: fw-496 a single account can only claim once from a redpacket * fixup! feat: fw-496 a single account can only claim once from a redpacket * fix: claim platform --- .../FireflyRedpacketConfirmDialog.tsx | 7 +++- .../src/SiteAdaptor/RedPacket/index.tsx | 35 +++++++++++++--- .../hooks/useAvailabilityComputed.ts | 4 +- .../SiteAdaptor/hooks/useParseRedPacket.ts | 40 +++++++++++++++++++ .../src/SiteAdaptor/hooks/useSignedMessage.ts | 1 + .../shared/src/UI/components/Alert/index.tsx | 4 +- .../web3-providers/src/Firefly/RedPacket.ts | 27 +++++++++++++ .../src/Web3/Base/apis/ChainResolver.ts | 3 +- packages/web3-providers/src/types/Firefly.ts | 36 ++++++++++++++++- 9 files changed, 146 insertions(+), 11 deletions(-) create mode 100644 packages/plugins/RedPacket/src/SiteAdaptor/hooks/useParseRedPacket.ts diff --git a/packages/plugins/RedPacket/src/SiteAdaptor/FireflyRedpacketConfirmDialog.tsx b/packages/plugins/RedPacket/src/SiteAdaptor/FireflyRedpacketConfirmDialog.tsx index 01c32ef43ef6..2f70cc3265fd 100644 --- a/packages/plugins/RedPacket/src/SiteAdaptor/FireflyRedpacketConfirmDialog.tsx +++ b/packages/plugins/RedPacket/src/SiteAdaptor/FireflyRedpacketConfirmDialog.tsx @@ -329,7 +329,12 @@ export function FireflyRedpacketConfirmDialog({ {t.amount()} - + { if (isOnFirefly) { - const platform = source?.toLowerCase() as 'lens' | 'farcaster' const context = hasClaimed ? (`${platform}_claimed` as 'lens_claimed' | 'farcaster_claimed') : platform return t.share_on_firefly({ context, @@ -222,7 +222,7 @@ export const RedPacket = memo(function RedPacket({ payload }: RedPacketProps) { t.share_unclaimed_message_official_account(shareTextOption) : t.share_unclaimed_message_not_twitter(shareTextOption) }, - [payload, link, claimTxHash, t, network?.name, source, isOnFirefly, handle], + [payload, link, claimTxHash, t, network?.name, platform, isOnFirefly, handle], ) const claimedShareText = useMemo(() => getShareText(true), [getShareText]) const shareText = useMemo(() => { @@ -262,6 +262,9 @@ export const RedPacket = memo(function RedPacket({ payload }: RedPacketProps) { }, [token, redPacketContract, payload.rpid, account, claimedShareText, source]) const [showRequirements, setShowRequirements] = useState(false) + const me = useLastRecognizedIdentity() + const myProfileId = me?.profileId + const myHandle = me?.identifier?.userId const onClaimOrRefund = useCallback(async () => { let hash: string | undefined const result = await recheckClaimStatus() @@ -271,6 +274,15 @@ export const RedPacket = memo(function RedPacket({ payload }: RedPacketProps) { } if (canClaim) { hash = await claimCallback() + if (platform && myProfileId && myHandle && hash) { + await FireflyRedPacket.finishClaiming( + payload.rpid, + platform as FireflyRedPacketAPI.PlatformType, + myProfileId, + myHandle, + hash, + ) + } checkResult() } else if (canRefund) { hash = await refundCallback() @@ -278,7 +290,18 @@ export const RedPacket = memo(function RedPacket({ payload }: RedPacketProps) { if (typeof hash === 'string') { checkAvailability() } - }, [canClaim, canRefund, claimCallback, checkResult, recheckClaimStatus, checkAvailability]) + }, [ + canClaim, + canRefund, + platform, + claimCallback, + checkResult, + recheckClaimStatus, + checkAvailability, + payload.rpid, + myProfileId, + myHandle, + ]) const myStatus = useMemo(() => { if (!availability) return '' diff --git a/packages/plugins/RedPacket/src/SiteAdaptor/hooks/useAvailabilityComputed.ts b/packages/plugins/RedPacket/src/SiteAdaptor/hooks/useAvailabilityComputed.ts index 2a282ba0e3c6..50cb5e5c00eb 100644 --- a/packages/plugins/RedPacket/src/SiteAdaptor/hooks/useAvailabilityComputed.ts +++ b/packages/plugins/RedPacket/src/SiteAdaptor/hooks/useAvailabilityComputed.ts @@ -9,6 +9,7 @@ import { useCallback } from 'react' import { useAvailability } from './useAvailability.js' import { useClaimStrategyStatus } from './useClaimStrategyStatus.js' import { useSignedMessage } from './useSignedMessage.js' +import { useParseRedPacket } from './useParseRedPacket.js' /** * Fetch the red packet info from the chain @@ -28,6 +29,7 @@ export function useAvailabilityComputed(account: string, payload: RedPacketJSONP chainId: parsedChainId, }, ) + const parsed = useParseRedPacket(parsedChainId) const checkAvailability = recheckAvailability as ( options?: RefetchOptions, ) => Promise> @@ -57,7 +59,7 @@ export function useAvailabilityComputed(account: string, payload: RedPacketJSONP } const isEmpty = availability.balance === '0' const isExpired = availability.expired - const isClaimed = availability.claimed_amount !== '0' + const isClaimed = parsed?.redpacket.isClaimed || availability.claimed_amount !== '0' const isRefunded = isEmpty && availability.claimed < availability.total const isCreator = isSameAddress(payload?.sender.address ?? '', account) const isPasswordValid = !!(password && password !== 'PASSWORD INVALID') diff --git a/packages/plugins/RedPacket/src/SiteAdaptor/hooks/useParseRedPacket.ts b/packages/plugins/RedPacket/src/SiteAdaptor/hooks/useParseRedPacket.ts new file mode 100644 index 000000000000..011e330d5c2a --- /dev/null +++ b/packages/plugins/RedPacket/src/SiteAdaptor/hooks/useParseRedPacket.ts @@ -0,0 +1,40 @@ +import { useLastRecognizedIdentity, usePostInfoDetails } from '@masknet/plugin-infra/content-script' +import { NetworkPluginID } from '@masknet/shared-base' +import { useChainContext, useNetworkContext } from '@masknet/web3-hooks-base' +import { FireflyRedPacket } from '@masknet/web3-providers' +import type { FireflyRedPacketAPI } from '@masknet/web3-providers/types' +import type { ChainId } from '@masknet/web3-shared-evm' +import { useQuery, type UseQueryResult } from '@tanstack/react-query' + +type T = UseQueryResult +/** + * Parse RedPacket with post info + */ +export function useParseRedPacket(chainId: ChainId) { + const images = usePostInfoDetails.postMetadataImages() + const { pluginID } = useNetworkContext() + const { account } = useChainContext({ + chainId, + account: pluginID === NetworkPluginID.PLUGIN_EVM ? undefined : '', + }) + const source = usePostInfoDetails.source() + const me = useLastRecognizedIdentity() + const myProfileId = me?.profileId + + const query = useQuery({ + enabled: images.length > 0, + queryKey: ['red-packet', 'parse', source, images[0], account], + queryFn: async () => { + const platform = source?.toLowerCase() as FireflyRedPacketAPI.PlatformType + return FireflyRedPacket.parse({ + image: { + imageUrl: images[0], + }, + walletAddress: account, + platform, + profileId: myProfileId, + }) + }, + }) + return query.data +} diff --git a/packages/plugins/RedPacket/src/SiteAdaptor/hooks/useSignedMessage.ts b/packages/plugins/RedPacket/src/SiteAdaptor/hooks/useSignedMessage.ts index ee8888b29a17..9b21596f8019 100644 --- a/packages/plugins/RedPacket/src/SiteAdaptor/hooks/useSignedMessage.ts +++ b/packages/plugins/RedPacket/src/SiteAdaptor/hooks/useSignedMessage.ts @@ -22,6 +22,7 @@ export function useSignedMessage( needLensAndFarcasterHandle: true, platform, profileId: me?.profileId, + handle: me?.identifier?.userId, lensToken: me?.lensToken, farcasterMessage: me?.farcasterMessage as HexString, farcasterSigner: me?.farcasterSigner as HexString, diff --git a/packages/shared/src/UI/components/Alert/index.tsx b/packages/shared/src/UI/components/Alert/index.tsx index 8564af5eef49..322c52a9753e 100644 --- a/packages/shared/src/UI/components/Alert/index.tsx +++ b/packages/shared/src/UI/components/Alert/index.tsx @@ -30,7 +30,9 @@ export const Alert = memo(function Alert({ className, children, open, onClose, . return ( - {children} + + {children} + {onClose ? : null} diff --git a/packages/web3-providers/src/Firefly/RedPacket.ts b/packages/web3-providers/src/Firefly/RedPacket.ts index c21c6f192853..ee2fc0e2cac9 100644 --- a/packages/web3-providers/src/Firefly/RedPacket.ts +++ b/packages/web3-providers/src/Firefly/RedPacket.ts @@ -26,6 +26,14 @@ function fetchFireflyJSON(url: string, init?: RequestInit): Promise { } export class FireflyRedPacket { + static async parse(options: FireflyRedPacketAPI.ParseOptions) { + const url = urlcat(FIREFLY_ROOT_URL, '/v1/misc/redpacket/parse') + const { data } = await fetchFireflyJSON(url, { + method: 'POST', + body: JSON.stringify(options), + }) + return data + } static async getPayloadUrls(from: string, amount?: string, type?: string, symbol?: string, decimals?: number) { const url = urlcat(FIREFLY_ROOT_URL, '/v1/redpacket/themeList') const { data } = await fetchJSON(url) @@ -208,4 +216,23 @@ export class FireflyRedPacket { body: JSON.stringify(options), }) } + static async finishClaiming( + rpid: string, + platform: FireflyRedPacketAPI.PlatformType, + profileId: string, + handle: string, + txHash: string, + ) { + const url = urlcat(FIREFLY_ROOT_URL, '/v1/redpacket/finishClaiming') + return fetchFireflyJSON>(url, { + method: 'POST', + body: JSON.stringify({ + rpid, + claimPlatform: platform, + claimProfileId: profileId, + claimHandle: handle, + txHash, + }), + }) + } } diff --git a/packages/web3-providers/src/Web3/Base/apis/ChainResolver.ts b/packages/web3-providers/src/Web3/Base/apis/ChainResolver.ts index 5346b64f5e2b..60c900fac5f4 100644 --- a/packages/web3-providers/src/Web3/Base/apis/ChainResolver.ts +++ b/packages/web3-providers/src/Web3/Base/apis/ChainResolver.ts @@ -16,11 +16,12 @@ export class ChainResolver { * Guess chain id by name, it's not perfectly accurate */ chainId(name: string) { + if (!name) return return this.descriptors().find((x) => [x.name, x.type as string, x.fullName, x.shortName] .map((x) => x?.toLowerCase()) .filter(Boolean) - .includes(name?.toLowerCase()), + .includes(name.toLowerCase()), )?.chainId } chainName(chainId: ChainId) { diff --git a/packages/web3-providers/src/types/Firefly.ts b/packages/web3-providers/src/types/Firefly.ts index 9d51b71c96ef..7232d702593e 100644 --- a/packages/web3-providers/src/types/Firefly.ts +++ b/packages/web3-providers/src/types/Firefly.ts @@ -287,7 +287,7 @@ export namespace FireflyRedPacketAPI { is_default?: boolean } - interface Response { + export interface Response { code: number data: T } @@ -308,6 +308,40 @@ export namespace FireflyRedPacketAPI { export type ClaimHistoryResponse = Response + export interface ParseOptions { + text?: string + image?: { + imageUrl: string + } + walletAddress?: string + platform?: PlatformType + profileId?: string + } + export interface ParseResult { + content: string + /** only `text` for now */ + type: string + /** only 1 for now */ + version: number + serializable: true + meta: object + redpacket: { + /** the same as meta */ + payload: object + canClaim: boolean + canRefund: boolean + canSend: boolean + isPasswordValid: boolean + isClaimed: boolean + isEmpty: boolean + isExpired: boolean + isRefunded: boolean + claimedNumber: number + claimedAmount: string + } + } + export type ParseResponse = Response + export type CheckClaimStrategyStatusOptions = { rpid: string profile: {