Skip to content

Commit e8e48f4

Browse files
authored
Merge pull request #1783 from kleros/refactor/stake-abort-controller
Refactor/stake abort controller
2 parents 3c9e792 + e0cf5e6 commit e8e48f4

File tree

2 files changed

+46
-18
lines changed

2 files changed

+46
-18
lines changed

web/src/pages/Courts/CourtDetails/StakePanel/StakeWithdrawButton.tsx

+39-15
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { useCallback, useEffect, useMemo, useState } from "react";
1+
import React, { useCallback, useEffect, useMemo, useRef, useState } from "react";
22
import styled, { DefaultTheme, useTheme } from "styled-components";
33

44
import { useParams } from "react-router-dom";
@@ -39,6 +39,7 @@ const Container = styled.div`
3939
flex-direction: column;
4040
`;
4141

42+
type Steps = [_TimelineItem1, ..._TimelineItem1[]];
4243
interface IActionButton {
4344
amount: string;
4445
parsedAmount: bigint;
@@ -52,7 +53,8 @@ const StakeWithdrawButton: React.FC<IActionButton> = ({ amount, parsedAmount, ac
5253
const theme = useTheme();
5354
const [isPopupOpen, setIsPopupOpen] = useState(false);
5455
const [isSuccess, setIsSuccess] = useState(false);
55-
const [popupStepsState, setPopupStepsState] = useState<[_TimelineItem1, ..._TimelineItem1[]]>();
56+
const [popupStepsState, setPopupStepsState] = useState<Steps>();
57+
const controllerRef = useRef<AbortController | null>(null);
5658

5759
const { data: courtDetails } = useCourtDetails(id);
5860
const { balance, jurorBalance, allowance, refetchAllowance } = usePnkData({ courtId: id });
@@ -107,26 +109,37 @@ const StakeWithdrawButton: React.FC<IActionButton> = ({ amount, parsedAmount, ac
107109
});
108110
const { writeContractAsync: setStake } = useWriteKlerosCoreSetStake();
109111

112+
const updatePopupState = (signal: AbortSignal, state: Steps) => {
113+
if (signal.aborted) return;
114+
setPopupStepsState(state);
115+
};
116+
110117
const handleStake = useCallback(
111-
(config?: typeof setStakeConfig, approvalHash?: `0x${string}`) => {
118+
(signal: AbortSignal, config?: typeof setStakeConfig, approvalHash?: `0x${string}`) => {
119+
if (signal.aborted) return;
112120
const isWithdraw = action === ActionType.withdraw;
113121
const requestData = config?.request ?? setStakeConfig?.request;
114122
const commonArgs: [string, DefaultTheme, `0x${string}` | undefined] = [amount, theme, approvalHash];
115123

116124
if (requestData && publicClient) {
117-
setPopupStepsState(
125+
updatePopupState(
126+
signal,
118127
getStakeSteps(isWithdraw ? StakeSteps.WithdrawInitiate : StakeSteps.StakeInitiate, ...commonArgs)
119128
);
120129

121130
setStake(requestData)
122131
.then(async (hash) => {
123-
setPopupStepsState(
132+
if (signal.aborted) return;
133+
updatePopupState(
134+
signal,
124135
getStakeSteps(isWithdraw ? StakeSteps.WithdrawPending : StakeSteps.StakePending, ...commonArgs, hash)
125136
);
126137
await publicClient.waitForTransactionReceipt({ hash, confirmations: 2 }).then((res: TransactionReceipt) => {
138+
if (signal.aborted) return;
127139
const status = res.status === "success";
128140
if (status) {
129-
setPopupStepsState(
141+
updatePopupState(
142+
signal,
130143
getStakeSteps(
131144
isWithdraw ? StakeSteps.WithdrawConfirmed : StakeSteps.StakeConfirmed,
132145
...commonArgs,
@@ -135,13 +148,15 @@ const StakeWithdrawButton: React.FC<IActionButton> = ({ amount, parsedAmount, ac
135148
);
136149
setIsSuccess(true);
137150
} else
138-
setPopupStepsState(
151+
updatePopupState(
152+
signal,
139153
getStakeSteps(isWithdraw ? StakeSteps.WithdrawFailed : StakeSteps.StakeFailed, ...commonArgs, hash)
140154
);
141155
});
142156
})
143157
.catch((err) => {
144-
setPopupStepsState(
158+
updatePopupState(
159+
signal,
145160
getStakeSteps(
146161
isWithdraw ? StakeSteps.WithdrawFailed : StakeSteps.StakeFailed,
147162
...commonArgs,
@@ -157,25 +172,31 @@ const StakeWithdrawButton: React.FC<IActionButton> = ({ amount, parsedAmount, ac
157172

158173
const handleClick = useCallback(() => {
159174
setIsPopupOpen(true);
175+
controllerRef.current = new AbortController();
176+
const signal = controllerRef.current.signal;
177+
160178
if (isAllowance && increaseAllowanceConfig && publicClient) {
161179
const commonArgs: [string, DefaultTheme] = [amount, theme];
162-
setPopupStepsState(getStakeSteps(StakeSteps.ApproveInitiate, ...commonArgs));
180+
updatePopupState(signal, getStakeSteps(StakeSteps.ApproveInitiate, ...commonArgs));
163181

164182
increaseAllowance(increaseAllowanceConfig.request)
165183
.then(async (hash) => {
166-
setPopupStepsState(getStakeSteps(StakeSteps.ApprovePending, ...commonArgs, hash));
184+
if (signal.aborted) return;
185+
updatePopupState(signal, getStakeSteps(StakeSteps.ApprovePending, ...commonArgs, hash));
167186

168187
await publicClient
169188
.waitForTransactionReceipt({ hash, confirmations: 2 })
170189
.then(async (res: TransactionReceipt) => {
190+
if (signal.aborted) return;
171191
const status = res.status === "success";
172192
if (status) {
173193
await refetchAllowance();
174194
const refetchData = await refetchWithRetry(refetchSetStake);
175195
// check for a relatively new error with react/tanstack-query:
176196
// https://github.com/TanStack/query/issues/8209
177197
if (!refetchData?.data)
178-
setPopupStepsState(
198+
updatePopupState(
199+
signal,
179200
getStakeSteps(
180201
StakeSteps.ApproveFailed,
181202
...commonArgs,
@@ -185,16 +206,16 @@ const StakeWithdrawButton: React.FC<IActionButton> = ({ amount, parsedAmount, ac
185206
)
186207
);
187208
else {
188-
handleStake(refetchData.data, hash);
209+
handleStake(signal, refetchData.data, hash);
189210
}
190-
} else setPopupStepsState(getStakeSteps(StakeSteps.ApproveFailed, ...commonArgs, hash));
211+
} else updatePopupState(signal, getStakeSteps(StakeSteps.ApproveFailed, ...commonArgs, hash));
191212
});
192213
})
193214
.catch((err) => {
194-
setPopupStepsState(getStakeSteps(StakeSteps.ApproveFailed, ...commonArgs, undefined, undefined, err));
215+
updatePopupState(signal, getStakeSteps(StakeSteps.ApproveFailed, ...commonArgs, undefined, undefined, err));
195216
});
196217
} else {
197-
handleStake();
218+
handleStake(signal);
198219
}
199220
}, [
200221
increaseAllowance,
@@ -248,6 +269,9 @@ const StakeWithdrawButton: React.FC<IActionButton> = ({ amount, parsedAmount, ac
248269
setIsSuccess(false);
249270
setAmount("");
250271
setPopupStepsState(undefined);
272+
if (controllerRef.current) {
273+
controllerRef.current.abort();
274+
}
251275
};
252276

253277
return (

web/src/pages/Courts/CourtDetails/StakePanel/StakeWithdrawPopup/index.tsx

+7-3
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ const Container = styled.div`
4040
transform: translate(-50%, -50%);
4141
max-height: 80vh;
4242
overflow-y: auto;
43+
position: relative;
4344
4445
z-index: 10;
4546
flex-direction: column;
@@ -73,16 +74,19 @@ const InnerContainer = styled.div`
7374
flex-direction: column;
7475
align-self: center;
7576
gap: 24px;
76-
padding: 0 24px 24px;
77+
padding: 16px 24px 24px;
7778
`;
7879

7980
const StyledButton = styled(LightButton)`
81+
position: absolute;
82+
top: 8px;
83+
right: 8px;
8084
border: none !important;
81-
padding: 8px !important;
85+
padding: 4px !important;
8286
border-radius: 7px !important;
8387
height: fit-content !important;
84-
align-self: end;
8588
.button-svg {
89+
margin: 0;
8690
path {
8791
fill: ${({ theme }) => theme.stroke};
8892
}

0 commit comments

Comments
 (0)