Untitled
unknown
plain_text
2 months ago
13 kB
3
Indexable
import { GQLStatusAirdrop, GQLStatusCheckAirdrop } from '@/app-clients/airdrop/schemas'; import { PageContainer } from '@/app-components/layout/PageContainer'; import { ProtocolType } from '@/app-constants'; import { compareAddress, compareTonAddress, get, isTonAddress, parseErrorMessage, } from '@/app-helpers'; import { useQueryAirdropDetail, useQueryVerifyClaimed } from '@/app-hooks/airdrop'; import { useI18n, useOnEventCallback, useSearchWithUrlParams } from '@/app-hooks/common'; import { useIsMainWallet, useWalletAddressByProtocol } from '@/app-hooks/wallet'; import { useIsAwaitingHash, useIsClaimAirdrop, useSetClaimAirdrop, useSetIsAwaitingHash, useSetStatusClaimAll, useSetTimeDelayClaim, useStatusClaimAll, useTimeDelayClaim, } from '@/app-stores/reward'; import classNames from 'classnames'; import { isNil } from 'lodash-es'; import React, { useState } from 'react'; import { Button, Spinner } from 'react-bootstrap'; import { toast } from 'sonner'; import { CheckEligibility } from '../../components/CheckEligibility'; import { ClaimSuccessScreen } from '../../components/ClaimSuccessScreen'; import { CountdownAirdrop } from '../../components/CountdownAirdrop'; import { InfomationAirdropDetail } from '../../components/InfomationAirdropDetail'; import { useAirdropClaimReward } from '../../hooks'; import { SwitchToMainWalletSheet } from '../../components/SwitchToMainWalletSheet'; import { AppRoutes } from '@/app-routes'; export const NEXT_SHOW_CLAIM_BUTTON_TIMEOUT = 45; const AirdropDetailScreen: React.FC = () => { const [isShow, setIsShow] = useState(false); const [transactionHash, setTransactionHash] = useState<string>(''); const [status, setStatus] = useState< 'claim' | 'claimed' | 'processing' | 'dismiss' | undefined >(undefined); const [checkTimeOut, setCheckTimeOut] = useState<boolean>(true); const [isShowSwitchWallet, setIsShowSwitchWallet] = useState<boolean>(true); const navigate = useNavigate(); const { t: t_airdrop } = useI18n('airdrop'); const { searchQuery } = useSearchWithUrlParams({ paramName: 'id' }); const { onChange: setAmountClaim } = useSearchWithUrlParams({ paramName: 'amountClaim', }); const isCurrentMainWallet = useIsMainWallet(); const claimReward = useAirdropClaimReward(); const isShowClaimScreen = useIsClaimAirdrop(); const setIsShowClaimScreen = useSetClaimAirdrop(); const statusClaimAll = useStatusClaimAll(); const setStatusClaimAll = useSetStatusClaimAll(); const timeDelay = useTimeDelayClaim(); const setTimeDelay = useSetTimeDelayClaim(); const isAwaitingHash = useIsAwaitingHash(); const setIsAwaitingHash = useSetIsAwaitingHash(); const [, forceRender] = useReducer((x) => x + 1, 0); const { data: airdropDetail, loading, refetch, } = useQueryAirdropDetail({ id: searchQuery }); const { isClaim = false, tokenCanClaim = 0, timeCountDown = 0, tokenCanClaimBigNumber = '0', signature = '', receiver = '', claimId = 0, } = get(airdropDetail, (d) => d.allocationInfo.pools[0]) || {}; const isMultiClaim = airdropDetail.allocationInfo?.pools && airdropDetail.allocationInfo?.pools.length > 1; const isDelayProcessing = useOnEventCallback(() => { const delay = timeDelay.find((item) => item.claimId === claimId); if (!delay) return false; const now = Math.floor(Date.now() / 1000); return now < delay.time; }); useEffect(() => { if (isDelayProcessing()) { const interval = setInterval(() => forceRender(), 3000); return () => { reCheckClaimed(); clearInterval(interval); refetch(); }; } }, [isDelayProcessing]); const { data: isCheckClaimed, refetch: reCheckClaimed, loading: loadingCheck, } = useQueryVerifyClaimed( { input: { chainId: airdropDetail.tokenInfo?.chainId?.toString()!, protocol: airdropDetail.tokenInfo?.protocol! as ProtocolType, address: receiver!, rewardPoolAddress: airdropDetail.contractAddress!, claimId: String(claimId), }, }, { fetchPolicy: 'network-only', skip: !airdropDetail.tokenInfo?.chainId || !airdropDetail.tokenInfo.protocol || !airdropDetail.contractAddress || airdropDetail.allocationInfo?.isCheck === false, onError: (error) => { console.error('Fail to check claimed:', error); setTimeout(() => { reCheckClaimed(); }, 2000); }, }, ); useEffect(() => { if (isMultiClaim) return; const statusClaimItem = statusClaimAll.find( (item) => item.claimId === claimId, )?.status; if (!isNil(statusClaimItem)) { setStatus(statusClaimItem); } else { setStatus('claim'); setStatusClaimAll('claim', claimId); } }, [statusClaimAll]); useEffect(() => { if (loadingCheck || isNil(isCheckClaimed.data)) { airdropDetail.allocationInfo?.isCheck === false ? setCheckTimeOut(false) : setCheckTimeOut(true); } else { setCheckTimeOut(false); if (!isNil(status) || !isMultiClaim) { if (status === 'processing' && isCheckClaimed.data === true) { // CASE: 1 // Đầu tiên vào thì lấy statusClaimAirdrop nếu là processing và check đã claimed thành công // ==> thì set button về claimed setStatusClaimAll('claimed', claimId); } if (status === 'dismiss') { // CASE: 2 // Nếu statusClaimAirdrop là dismiss thì kiểm tra xem đã claimed chưa if (isCheckClaimed.data === true) { // Trong case này check đã claimed thành công thì set statusClaimAirdrop về claimed setStatusClaimAll('claimed', claimId); } else { // Nếu chưa claim thì set về claim setStatusClaimAll('claim', claimId); } } if (isCheckClaimed.data === true) { // CASE: 2 // Trong case này check đã claimed thành công thì set statusClaimAirdrop về claimed setStatusClaimAll('claimed', claimId); } else { // CASE: 3 // Nếu chưa claim hoặc đang trong tiến trình claim thì rơi vào case này if (status !== 'processing' && status !== 'dismiss') { // Nếu không phải processing thì set về claim setStatusClaimAll('claim', claimId); } } } } }, [isCheckClaimed, loadingCheck, status]); const isCheckEligible = airdropDetail.allocationInfo?.isCheck; const statusCheck = airdropDetail.allocationInfo?.statusCheck; const isShowCountdown = !isMultiClaim && Number(timeCountDown!) > 0 && isCheckEligible && statusCheck === GQLStatusCheckAirdrop.ELIGIBLE; const walletAddress = useWalletAddressByProtocol({ protocol: airdropDetail.tokenInfo?.protocol! as ProtocolType, }); const isWalletAllowedToClaim = () => { if (!walletAddress || !receiver) { return false; } if (isTonAddress(receiver)) { return compareTonAddress(walletAddress, receiver); } return compareAddress(walletAddress, receiver); }; const onEnterClaimAirdrop = useOnEventCallback(async () => { setStatusClaimAll('processing', claimId); try { if (isCheckClaimed.data === true) return; if (isMultiClaim) return; if (isNil(airdropDetail)) return; setAmountClaim(tokenCanClaim.toString()); const result = await claimReward({ chainId: airdropDetail.tokenInfo?.chainId?.toString()!, rewardPoolAddress: airdropDetail.contractAddress!, amount: tokenCanClaimBigNumber!, to: receiver!, signature: signature, claimId: BigInt(claimId), }); if (result) { setStatusClaimAll('claimed', claimId); setIsShowClaimScreen(true); setTransactionHash(result); const now = Math.floor(Date.now() / 1000); setTimeDelay(now + NEXT_SHOW_CLAIM_BUTTON_TIMEOUT, claimId!); } } catch (error) { toast.error(parseErrorMessage(error)); setStatusClaimAll('claim', claimId); } finally { reCheckClaimed(); refetch(); } }); useEffect(() => { const now = Math.floor(Date.now() / 1000); if (!loading) { if ( (signature === '' || typeof signature !== 'string') && !isNil(airdropDetail.allocationInfo?.pools) && timeCountDown === 0 && now < airdropDetail.endDatePeriod! && statusCheck === GQLStatusCheckAirdrop.ELIGIBLE ) { setIsAwaitingHash(true); } else { setIsAwaitingHash(false); } } }, [airdropDetail, loading]); const renderFooterButton = useOnEventCallback( ( text: { content: string; textColor?: string }, variant: string, disabled = false, disableAndLoading = false, onClick?: () => void, ) => ( <div className={classNames('viewFooter', loading ? 'd-none' : 'block')}> <div className="inner-footer"> <Button disabled={disabled} variant={variant ?? 'primary'} onClick={onClick} className={classNames(text.textColor ? text.textColor : '')} > {!disableAndLoading ? ( <Spinner size="sm" style={{ borderWidth: '1px' }} animation="border" role="status" /> ) : null} {text.content} </Button> </div> </div> ), ); return ( <PageContainer id="AirdropDetailScreen" backable transition={false} onBack={() => { navigate(AppRoutes.Reward.path); setIsShowClaimScreen(false); }} > <InfomationAirdropDetail loading={loading} airdropDetail={airdropDetail} isDelayProcessing={isDelayProcessing() && !isMultiClaim} refetch={refetch} /> {/* Countdown */} {isShowCountdown && !isMultiClaim && ( <CountdownAirdrop style={{ paddingLeft: '24px', paddingRight: '24px', paddingTop: '10px', position: 'fixed', bottom: '0', left: '0', right: '0', }} airdropName={airdropDetail.tokenInfo?.tokenName!} airdropEndTime={timeCountDown} allocationCanClaim={tokenCanClaim ?? 0} isCoundown={true} onCountdownEnd={() => refetch && refetch()} /> )} {/* Footer Buttons */} {/* Check */} {!isDelayProcessing() && isCurrentMainWallet && ( <> {!isCheckEligible && airdropDetail.status !== GQLStatusAirdrop.COMPLETED && renderFooterButton( { content: airdropDetail.status === GQLStatusAirdrop.COMING_SOON ? t_airdrop('comingSoon') : t_airdrop('checkEligibility'), }, 'primary', airdropDetail.status === GQLStatusAirdrop.COMING_SOON, true, () => setIsShow(!isShow), )} {/* Không đủ điều kiện để claim */} {isCheckEligible && airdropDetail.status !== GQLStatusAirdrop.COMPLETED && statusCheck === GQLStatusCheckAirdrop.NOT_ELIGIBLE && renderFooterButton( { content: t_airdrop('notEligible'), textColor: 'text-danger' }, 'secondary', true, true, )} {/* Claim - Bấm vào để nhận */} {timeCountDown <= 0 && !isMultiClaim && isWalletAllowedToClaim() && airdropDetail.status !== GQLStatusAirdrop.COMPLETED && checkTimeOut === false && renderFooterButton( { content: status === 'claimed' ? t_airdrop('claimed') : status === 'processing' || status === 'dismiss' ? t_airdrop('processing') : t_airdrop('claim'), textColor: status === 'claimed' ? 'text-success' : '', }, status === 'claimed' ? 'secondary' : 'success', status !== 'claim' || isAwaitingHash, status !== 'processing' && status !== 'dismiss', () => onEnterClaimAirdrop(), )} {checkTimeOut === true && !isShowCountdown && !isMultiClaim && statusCheck !== GQLStatusCheckAirdrop.NOT_ELIGIBLE && renderFooterButton( { content: 'Checking', }, 'success', true, false, () => onEnterClaimAirdrop(), )} </> )} {/* Check Eligibility Modal */} {isShow && ( <CheckEligibility show={isShow} onDismiss={() => setIsShow(false)} airdropItem={airdropDetail} refetch={refetch} /> )} {/* Claim Successul */} {isShowClaimScreen && !isMultiClaim && ( <ClaimSuccessScreen onDismiss={() => setIsShowClaimScreen(false)} network={airdropDetail.tokenInfo?.chainName!} amount={tokenCanClaim ?? 0} symbol={airdropDetail.tokenInfo?.tokenName!} urlGoback={airdropDetail.metaData?.urlCallBack} refetch={reCheckClaimed} transactionHash={transactionHash} airdropDetail={airdropDetail} claimId={claimId} receiver={receiver} /> )} {!isCurrentMainWallet && ( <SwitchToMainWalletSheet show={isShowSwitchWallet} onDismiss={() => setIsShowSwitchWallet(false)} /> )} </PageContainer> ); }; export default AirdropDetailScreen;
Editor is loading...
Leave a Comment