Untitled
unknown
plain_text
10 months ago
13 kB
6
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