Skip to content

Commit

Permalink
feat: use refresher for oeth redeems
Browse files Browse the repository at this point in the history
  • Loading branch information
toniocodo committed Jul 25, 2024
1 parent c9f9f67 commit 7774e1e
Show file tree
Hide file tree
Showing 3 changed files with 160 additions and 112 deletions.
89 changes: 61 additions & 28 deletions libs/defi/oeth/src/redeem/components/ClaimForm.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useMemo, useState } from 'react';
import { useEffect, useMemo, useState } from 'react';

import {
Button,
Expand All @@ -18,7 +18,7 @@ import {
FaCircleCheckRegular,
FaClockRegular,
} from '@origin/shared/icons';
import { TxButton } from '@origin/shared/providers';
import { TxButton, useRefresher } from '@origin/shared/providers';
import {
getFormatPrecision,
isNilOrEmpty,
Expand All @@ -28,10 +28,9 @@ import { useQueryClient } from '@tanstack/react-query';
import { add, eq, format, from } from 'dnum';
import { remove } from 'ramda';
import { useIntl } from 'react-intl';
import { useAccount } from 'wagmi';
import { useAccount, useConfig } from 'wagmi';

import { useWithdrawalRequests } from '../hooks';
import { useWithdrawalRequestsQuery } from '../queries.generated';

import type { StackProps } from '@mui/material';
import type { Dnum } from 'dnum';
Expand All @@ -40,13 +39,21 @@ import type { WithdrawalRequest } from '../types';

export const ClaimForm = (props: StackProps) => {
const intl = useIntl();
const queryClient = useQueryClient();
const { address } = useAccount();
const config = useConfig();
const queryClient = useQueryClient();
const [selectedClaimIds, setSelectedClaimIds] = useState<bigint[]>([]);
const { data: requests, isLoading: isRequestsLoading } =
useWithdrawalRequests({
select: (data) => data?.filter((r) => !r.claimed),
});
const { startRefresh, status } = useRefresher<WithdrawalRequest[]>({
queryKey: useWithdrawalRequests.getKey(address ?? ZERO_ADDRESS),
queryFn: useWithdrawalRequests.fetcher(config, queryClient),
isResultProcessed: (prev, next) =>
prev.filter((r) => r.claimed).length <
next.filter((r) => r.claimed).length,
});
const args =
selectedClaimIds.length === 1
? {
Expand All @@ -59,7 +66,6 @@ export const ClaimForm = (props: StackProps) => {
functionName: 'claimWithdrawals',
args: [selectedClaimIds],
};

const selectedAmount = useMemo(
() =>
selectedClaimIds.reduce((acc, curr) => {
Expand All @@ -73,13 +79,7 @@ export const ClaimForm = (props: StackProps) => {
params: args as any,
callbacks: {
onWriteSuccess: () => {
queryClient.invalidateQueries({
queryKey: [
useWithdrawalRequestsQuery.getKey({
address: address ?? ZERO_ADDRESS,
}),
],
});
startRefresh();
},
},
activity: {
Expand All @@ -91,6 +91,13 @@ export const ClaimForm = (props: StackProps) => {
enableGas: true,
});

useEffect(() => {
if (['timeout', 'processed', 'error'].includes(status)) {
setSelectedClaimIds([]);
queryClient.invalidateQueries();
}
}, [address, queryClient, status]);

const handleClaimClick = (requestId: bigint) => () => {
const idx = selectedClaimIds.findIndex((id) => id === requestId);
if (idx === -1) {
Expand Down Expand Up @@ -133,6 +140,9 @@ export const ClaimForm = (props: StackProps) => {
request={r}
selected={selectedClaimIds.includes(r.requestId)}
onSelect={handleClaimClick(r.requestId)}
isProcessing={
selectedClaimIds.includes(r.requestId) && status === 'polling'
}
/>
))
)}
Expand All @@ -159,7 +169,7 @@ export const ClaimForm = (props: StackProps) => {
params={params}
callbacks={callbacks}
variant="action"
disabled={isNilOrEmpty(selectedClaimIds)}
disabled={isNilOrEmpty(selectedClaimIds) || status === 'polling'}
label={intl.formatMessage(
{ defaultMessage: 'Claim{amount}' },
{
Expand All @@ -177,10 +187,18 @@ type ClaimRowProps = {
request: WithdrawalRequest;
selected: boolean;
onSelect: () => void;
isProcessing: boolean;
} & StackProps;

const ClaimRow = ({ request, selected, onSelect, ...rest }: ClaimRowProps) => {
const ClaimRow = ({
request,
selected,
onSelect,
isProcessing,
...rest
}: ClaimRowProps) => {
const amt = [request?.amount ?? 0n, tokens.mainnet.WETH.decimals] as Dnum;
const disabled = !request.claimable || isProcessing;

return (
<Stack
Expand Down Expand Up @@ -209,11 +227,11 @@ const ClaimRow = ({ request, selected, onSelect, ...rest }: ClaimRowProps) => {
</Stack>
}
onChange={onSelect}
disabled={!request.claimable}
disabled={disabled}
disableTypography
/>
<Stack direction="row" alignItems="center" spacing={1}>
<ClaimChip claimable={request.claimable} />
<ClaimChip claimable={request.claimable} isProcessing={isProcessing} />
<Button
variant="outlined"
color="secondary"
Expand All @@ -228,36 +246,51 @@ const ClaimRow = ({ request, selected, onSelect, ...rest }: ClaimRowProps) => {
);
};

type ClaimChipProps = { claimable: boolean } & StackProps;
type ClaimChipProps = {
claimable: boolean;
isProcessing: boolean;
} & StackProps;

const ClaimChip = ({ claimable, ...rest }: ClaimChipProps) => {
const ClaimChip = ({ claimable, isProcessing, ...rest }: ClaimChipProps) => {
const intl = useIntl();

const icon = claimable ? (
const icon = isProcessing ? (
<CircularProgress size={16} />
) : claimable ? (
<FaCircleCheckRegular sx={{ color: 'success.dark' }} />
) : (
<FaClockRegular sx={{ color: 'warning.dark' }} />
);
const label = claimable
? intl.formatMessage({ defaultMessage: 'Ready' })
: intl.formatMessage({ defaultMessage: 'Pending' });
const label = isProcessing
? intl.formatMessage({ defaultMessage: 'Processing' })
: claimable
? intl.formatMessage({ defaultMessage: 'Ready' })
: intl.formatMessage({ defaultMessage: 'Pending' });
const color = isProcessing
? 'primary.main'
: claimable
? 'success.dark'
: 'warning.dark';
const bgColor = isProcessing
? 'primary.faded'
: claimable
? 'success.faded'
: 'warning.faded';

return (
<Stack
direction="row"
alignItems="center"
spacing={1}
color={claimable ? 'success.dark' : 'warning.dark'}
bgcolor={claimable ? 'success.faded' : 'warning.faded'}
color={color}
bgcolor={bgColor}
px={2}
py={1}
borderRadius={2}
{...rest}
>
{icon}
<Typography color={claimable ? 'success.dark' : 'warning.dark'}>
{label}
</Typography>
<Typography color={color}>{label}</Typography>
</Stack>
);
};
80 changes: 38 additions & 42 deletions libs/defi/oeth/src/redeem/components/WithdrawalRequestModal.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useState } from 'react';
import { useEffect } from 'react';

import {
alpha,
Expand All @@ -19,9 +19,8 @@ import {
FaExclamationRegular,
FaXmarkRegular,
} from '@origin/shared/icons';
import { BlockExplorerLink } from '@origin/shared/providers';
import { BlockExplorerLink, useRefresher } from '@origin/shared/providers';
import { getFormatPrecision, ZERO_ADDRESS } from '@origin/shared/utils';
import { useIntervalEffect, usePrevious } from '@react-hookz/web';
import { useQueryClient } from '@tanstack/react-query';
import { format } from 'dnum';
import { useIntl } from 'react-intl';
Expand All @@ -35,8 +34,7 @@ import type { Token } from '@origin/shared/contracts';
import type { Dnum } from 'dnum';
import type { TransactionReceipt } from 'viem';

const INTERVAL = 2000; // ms
const MAX_RETRY = 10; // 10 * 2s = 20s
import type { WithdrawalRequestsQuery } from '../queries.generated';

export type WithdrawalRequestModalProps = {
amountOut?: bigint;
Expand All @@ -45,13 +43,12 @@ export type WithdrawalRequestModalProps = {
txReceipt?: TransactionReceipt;
} & DialogProps;

type Status = 'processing' | 'processed' | 'timeout';

export const WithdrawalRequestModal = ({
amountOut,
tokenIn,
tokenOut,
txReceipt,
open,
onClose,
...rest
}: WithdrawalRequestModalProps) => {
Expand All @@ -61,35 +58,22 @@ export const WithdrawalRequestModal = ({
const { update } = useViewSelect();
const queryClient = useQueryClient();
const { address } = useAccount();
const [status, setStatus] = useState<Status>('processing');
const [retries, setRetries] = useState(0);
const { data: count } = useWithdrawalRequestsQuery(
{ address: address ?? ZERO_ADDRESS },
{
enabled: !!address,
refetchInterval: status === 'processing' ? INTERVAL : undefined,
select: (data) => data?.oethWithdrawalRequests?.length ?? 0,
},
);
const prevCount = usePrevious(count);
const { status, startRefresh } = useRefresher<WithdrawalRequestsQuery>({
queryKey: useWithdrawalRequestsQuery.getKey({
address: address ?? ZERO_ADDRESS,
}),
queryFn: useWithdrawalRequestsQuery.fetcher({
address: address ?? ZERO_ADDRESS,
}),
isResultProcessed: (prev, next) =>
prev.oethWithdrawalRequests.length < next.oethWithdrawalRequests.length,
});

useIntervalEffect(
() => {
if (status === 'processing') {
setRetries((prev) => prev + 1);
if (
count !== undefined &&
prevCount !== undefined &&
count > prevCount
) {
setStatus('processed');
} else if (retries > MAX_RETRY) {
setStatus('timeout');
}
}
},
status === 'processing' ? INTERVAL : undefined,
);
useEffect(() => {
if (open) {
startRefresh();
}
}, [open, startRefresh]);

const handleClaimClick = () => {
update('claim');
Expand All @@ -98,7 +82,9 @@ export const WithdrawalRequestModal = ({

const amt = [amountOut ?? 0n, tokenOut?.decimals ?? 18] as Dnum;
const icon = {
processing: (
idle: null,
error: null,
polling: (
<Box sx={{ position: 'relative', width: 85, height: 85 }}>
<CircularProgress
variant="determinate"
Expand Down Expand Up @@ -154,6 +140,8 @@ export const WithdrawalRequestModal = ({
),
}[status];
const label = {
idle: null,
error: null,
processed: {
title: intl.formatMessage({
defaultMessage: 'Withdrawal request successfully sent',
Expand All @@ -179,7 +167,7 @@ export const WithdrawalRequestModal = ({
'Try to refresh the page and go to the Claim tab to see your withdrawal request',
}),
},
processing: {
polling: {
title: intl.formatMessage({
defaultMessage: 'Your withdrawal is being processed',
}),
Expand All @@ -189,6 +177,8 @@ export const WithdrawalRequestModal = ({
},
}[status];
const button = {
idle: null,
error: null,
timeout: (
<Button
fullWidth
Expand All @@ -197,7 +187,7 @@ export const WithdrawalRequestModal = ({
window.location.reload();
}}
size="large"
disabled={status === 'processing'}
disabled={status === 'polling'}
>
{intl.formatMessage({
defaultMessage: 'Refresh the page',
Expand All @@ -221,7 +211,7 @@ export const WithdrawalRequestModal = ({
})}
</Button>
),
processing: (
polling: (
<Button fullWidth onClick={handleClaimClick} size="large" disabled>
{intl.formatMessage({
defaultMessage: 'Processing withdrawal request',
Expand All @@ -231,7 +221,13 @@ export const WithdrawalRequestModal = ({
}[status];

return (
<Dialog {...rest} maxWidth="xs" fullWidth fullScreen={fullScreen}>
<Dialog
{...rest}
open={open}
maxWidth="xs"
fullWidth
fullScreen={fullScreen}
>
<DialogTitle display="flex" justifyContent="flex-end" alignItems="center">
<IconButton
onClick={(evt) => {
Expand All @@ -250,15 +246,15 @@ export const WithdrawalRequestModal = ({
textAlign="center"
mb={2}
>
{label.title}
{label?.title}
</Typography>
<Typography
variant="mono"
textAlign="center"
mb={3}
color="text.secondary"
>
{label.subtitle}
{label?.subtitle}
</Typography>
{button}
{txReceipt && (
Expand Down
Loading

0 comments on commit 7774e1e

Please sign in to comment.