Skip to content

Commit 5ef14cf

Browse files
committed
Disable/dim all actionable components when tx in progress
Signed-off-by: Emre Bogazliyanlioglu <emre@wormholelabs.xyz>
1 parent 1959ff6 commit 5ef14cf

File tree

7 files changed

+145
-86
lines changed

7 files changed

+145
-86
lines changed

wormhole-connect/src/views/v2/Bridge/AmountInput/index.tsx

+22-11
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ const DebouncedTextField = memo(
5757
setInnerValue(e.target.value);
5858
deferredOnChange(e.target.value);
5959
},
60-
[],
60+
[deferredOnChange],
6161
);
6262

6363
// Propagate any outside changes to the inner TextField value
@@ -133,9 +133,11 @@ const AmountInput = (props: Props) => {
133133
amount ? sdkAmount.display(amount) : '',
134134
);
135135

136-
const { fromChain: sourceChain, token: sourceToken } = useSelector(
137-
(state: RootState) => state.transferInput,
138-
);
136+
const {
137+
fromChain: sourceChain,
138+
token: sourceToken,
139+
isTransactionInProgress,
140+
} = useSelector((state: RootState) => state.transferInput);
139141

140142
const { balances, isFetching } = useGetTokenBalances(
141143
sendingWallet?.address || '',
@@ -159,8 +161,8 @@ const AmountInput = (props: Props) => {
159161
);
160162

161163
const isInputDisabled = useMemo(
162-
() => !sourceChain || !sourceToken,
163-
[sourceChain, sourceToken],
164+
() => isTransactionInProgress || !sourceChain || !sourceToken,
165+
[isTransactionInProgress, sourceChain, sourceToken],
164166
);
165167

166168
const balance = useMemo(() => {
@@ -193,12 +195,21 @@ const AmountInput = (props: Props) => {
193195
)}
194196
</Stack>
195197
);
196-
}, [isInputDisabled, balances, tokenBalance, sendingWallet.address]);
198+
}, [
199+
isInputDisabled,
200+
sendingWallet.address,
201+
classes.balance,
202+
isFetching,
203+
tokenBalance,
204+
]);
197205

198-
const handleChange = useCallback((newValue: string): void => {
199-
dispatch(setAmount(newValue));
200-
setAmountInput(newValue);
201-
}, []);
206+
const handleChange = useCallback(
207+
(newValue: string): void => {
208+
dispatch(setAmount(newValue));
209+
setAmountInput(newValue);
210+
},
211+
[dispatch],
212+
);
202213

203214
const maxButton = useMemo(() => {
204215
const maxButtonDisabled =

wormhole-connect/src/views/v2/Bridge/AssetPicker/index.tsx

+13-4
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import ChainList from './ChainList';
2222
import TokenList from './TokenList';
2323
import { Chain } from '@wormhole-foundation/sdk';
2424
import AssetBadge from 'components/AssetBadge';
25+
import { joinClass } from 'utils/style';
2526

2627
const useStyles = makeStyles()((theme: any) => ({
2728
card: {
@@ -46,8 +47,8 @@ const useStyles = makeStyles()((theme: any) => ({
4647
justifyContent: 'space-between',
4748
},
4849
disabled: {
49-
opacity: '0.4',
50-
cursor: 'not-allowed',
50+
opacity: '0.6',
51+
cursor: 'default',
5152
clickEvent: 'none',
5253
},
5354
popover: {
@@ -70,6 +71,7 @@ type Props = {
7071
setChain: (value: Chain) => void;
7172
wallet: WalletData;
7273
isSource: boolean;
74+
isTransactionInProgress: boolean;
7375
};
7476

7577
const AssetPicker = (props: Props) => {
@@ -138,12 +140,19 @@ const AssetPicker = (props: Props) => {
138140
);
139141
}, [chainConfig, tokenConfig]);
140142

143+
const triggerProps = props.isTransactionInProgress
144+
? {}
145+
: bindTrigger(popupState);
146+
141147
return (
142148
<>
143149
<Card
144-
className={classes.card}
150+
className={joinClass([
151+
classes.card,
152+
props.isTransactionInProgress && classes.disabled,
153+
])}
145154
variant="elevation"
146-
{...bindTrigger(popupState)}
155+
{...triggerProps}
147156
>
148157
<CardContent className={classes.cardContent}>
149158
<Typography

wormhole-connect/src/views/v2/Bridge/Routes/SingleRoute.tsx

+72-63
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import {
2525
millisToHumanString,
2626
formatDuration,
2727
} from 'utils';
28+
import { joinClass } from 'utils/style';
2829

2930
import type { RouteData } from 'config/routes';
3031
import type { RootState } from 'store';
@@ -35,56 +36,65 @@ import GasSlider from 'views/v2/Bridge/GasSlider';
3536

3637
const HIGH_FEE_THRESHOLD = 20; // dollhairs
3738

38-
const useStyles = makeStyles()((theme: any) => ({
39-
container: {
40-
width: '100%',
41-
maxWidth: '420px',
42-
marginBottom: '8px',
43-
},
44-
card: {
45-
borderRadius: '8px',
46-
width: '100%',
47-
maxWidth: '420px',
48-
},
49-
cardHeader: {
50-
padding: '20px 20px 0px',
51-
},
52-
cardContent: {
53-
marginTop: '18px',
54-
padding: '0px 20px 20px',
55-
},
56-
errorIcon: {
57-
color: theme.palette.error.main,
58-
height: '34px',
59-
width: '34px',
60-
marginRight: '24px',
61-
},
62-
fastestBadge: {
63-
width: '14px',
64-
height: '14px',
65-
position: 'relative',
66-
top: '2px',
67-
marginRight: '4px',
68-
fill: theme.palette.primary.main,
69-
},
70-
cheapestBadge: {
71-
width: '12px',
72-
height: '12px',
73-
position: 'relative',
74-
top: '1px',
75-
marginRight: '3px',
76-
fill: theme.palette.primary.main,
77-
},
78-
messageContainer: {
79-
padding: '12px 0px 0px',
80-
},
81-
warningIcon: {
82-
color: theme.palette.warning.main,
83-
height: '34px',
84-
width: '34px',
85-
marginRight: '12px',
86-
},
87-
}));
39+
const useStyles = makeStyles<{ isSelected: boolean }>()(
40+
(theme: any, { isSelected }) => ({
41+
container: {
42+
width: '100%',
43+
maxWidth: '420px',
44+
marginBottom: '8px',
45+
},
46+
card: {
47+
border: '1px solid',
48+
borderColor: isSelected ? theme.palette.primary.main : 'transparent',
49+
borderRadius: '8px',
50+
width: '100%',
51+
maxWidth: '420px',
52+
},
53+
cardHeader: {
54+
padding: '20px 20px 0px',
55+
},
56+
cardContent: {
57+
marginTop: '18px',
58+
padding: '0px 20px 20px',
59+
},
60+
disabled: {
61+
opacity: '0.6',
62+
cursor: 'default',
63+
clickEvent: 'none',
64+
},
65+
errorIcon: {
66+
color: theme.palette.error.main,
67+
height: '34px',
68+
width: '34px',
69+
marginRight: '24px',
70+
},
71+
fastestBadge: {
72+
width: '14px',
73+
height: '14px',
74+
position: 'relative',
75+
top: '2px',
76+
marginRight: '4px',
77+
fill: theme.palette.primary.main,
78+
},
79+
cheapestBadge: {
80+
width: '12px',
81+
height: '12px',
82+
position: 'relative',
83+
top: '1px',
84+
marginRight: '3px',
85+
fill: theme.palette.primary.main,
86+
},
87+
messageContainer: {
88+
padding: '12px 0px 0px',
89+
},
90+
warningIcon: {
91+
color: theme.palette.warning.main,
92+
height: '34px',
93+
width: '34px',
94+
marginRight: '12px',
95+
},
96+
}),
97+
);
8898

8999
type Props = {
90100
route: RouteData;
@@ -99,7 +109,6 @@ type Props = {
99109
};
100110

101111
const SingleRoute = (props: Props) => {
102-
const { classes } = useStyles();
103112
const theme = useTheme();
104113
const routeConfig = config.routes.get(props.route.name);
105114

@@ -115,10 +124,12 @@ const SingleRoute = (props: Props) => {
115124
(state: RootState) => state.tokenPrices,
116125
);
117126

127+
const { quote, isSelected } = props;
118128
const { name } = props.route;
119-
const { quote } = props;
120129
const receiveNativeAmount = quote?.destinationNativeGas;
121130

131+
const { classes } = useStyles({ isSelected });
132+
122133
const destTokenConfig = useMemo(
123134
() => config.tokens[destToken] as TokenConfig | undefined,
124135
[destToken],
@@ -566,7 +577,7 @@ const SingleRoute = (props: Props) => {
566577
// 1- If no action handler provided, fall back to default
567578
// 2- Otherwise there is an action handler, "pointer"
568579
const cursor = useMemo(() => {
569-
if (props.isSelected || typeof props.onSelect !== 'function') {
580+
if (isSelected || typeof props.onSelect !== 'function') {
570581
return 'default';
571582
}
572583

@@ -575,7 +586,7 @@ const SingleRoute = (props: Props) => {
575586
}
576587

577588
return 'pointer';
578-
}, [props.error, props.isSelected, props.onSelect]);
589+
}, [props.error, isSelected, props.onSelect]);
579590

580591
const routeCardBadge = useMemo(() => {
581592
if (props.isFastest) {
@@ -609,18 +620,16 @@ const SingleRoute = (props: Props) => {
609620
return (
610621
<div key={name} className={classes.container}>
611622
<Card
612-
className={classes.card}
613-
sx={{
614-
border: '1px solid',
615-
borderColor: props.isSelected
616-
? theme.palette.primary.main
617-
: 'transparent',
618-
opacity: 1,
619-
}}
623+
className={joinClass([
624+
classes.card,
625+
isTransactionInProgress && classes.disabled,
626+
])}
620627
>
621628
<CardActionArea
622629
disabled={
623-
typeof props.onSelect !== 'function' || props.error !== undefined
630+
isTransactionInProgress ||
631+
typeof props.onSelect !== 'function' ||
632+
props.error !== undefined
624633
}
625634
disableTouchRipple
626635
sx={{ cursor }}

wormhole-connect/src/views/v2/Bridge/Routes/index.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ const Routes = ({ ...props }: Props) => {
5050
const selectedRoute = routes.find((route) => route === props.selectedRoute);
5151

5252
return selectedRoute ? [selectedRoute] : routes.slice(0, 1);
53-
}, [showAll, routes]);
53+
}, [showAll, routes, props.selectedRoute]);
5454

5555
const fastestRoute = useMemo(() => {
5656
return routes.reduce(

wormhole-connect/src/views/v2/Bridge/SwapInputs/index.tsx

+1
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ function SwapInputs() {
3434
} = useSelector((state: RootState) => state.transferInput);
3535

3636
const canSwap =
37+
!isTransactionInProgress &&
3738
fromChain &&
3839
!config.chains[fromChain]?.disabledAsDestination &&
3940
toChain &&

wormhole-connect/src/views/v2/Bridge/WalletConnector/Controller.tsx

+22-4
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import { RootState } from 'store';
1717
import { disconnectWallet as disconnectFromStore } from 'store/wallet';
1818
import { TransferWallet } from 'utils/wallet';
1919
import { copyTextToClipboard, displayWalletAddress } from 'utils';
20+
import { joinClass } from 'utils/style';
2021

2122
import DownIcon from 'icons/Down';
2223
import WalletIcons from 'icons/WalletIcons';
@@ -47,6 +48,11 @@ const useStyles = makeStyles()((theme: any) => ({
4748
up: {
4849
transform: 'scaleY(-1)',
4950
},
51+
disabled: {
52+
opacity: '0.6',
53+
cursor: 'default',
54+
clickEvent: 'none',
55+
},
5056
dropdown: {
5157
backgroundColor: theme.palette.popover.background,
5258
display: 'flex',
@@ -76,6 +82,10 @@ const ConnectedWallet = (props: Props) => {
7682

7783
const { classes } = useStyles();
7884

85+
const { isTransactionInProgress } = useSelector(
86+
(state: RootState) => state.transferInput,
87+
);
88+
7989
const wallet = useSelector((state: RootState) => state.wallet[props.type]);
8090

8191
const [isOpen, setIsOpen] = useState(false);
@@ -89,18 +99,18 @@ const ConnectedWallet = (props: Props) => {
8999
const connectWallet = useCallback(() => {
90100
popupState?.close();
91101
setIsOpen(true);
92-
}, []);
102+
}, [popupState]);
93103

94104
const copyAddress = useCallback(() => {
95105
copyTextToClipboard(wallet.address);
96106
popupState?.close();
97107
setIsCopied(true);
98-
}, [wallet.address]);
108+
}, [popupState, wallet.address]);
99109

100110
const disconnectWallet = useCallback(() => {
101111
dispatch(disconnectFromStore(props.type));
102112
popupState?.close();
103-
}, [props.type]);
113+
}, [dispatch, popupState, props.type]);
104114

105115
useEffect(() => {
106116
if (isCopied) {
@@ -110,13 +120,21 @@ const ConnectedWallet = (props: Props) => {
110120
}
111121
}, [isCopied]);
112122

123+
const popupTrigger = isTransactionInProgress ? {} : bindTrigger(popupState);
124+
113125
if (!wallet?.address) {
114126
return <></>;
115127
}
116128

117129
return (
118130
<>
119-
<div className={classes.connectWallet} {...bindTrigger(popupState)}>
131+
<div
132+
className={joinClass([
133+
classes.connectWallet,
134+
isTransactionInProgress && classes.disabled,
135+
])}
136+
{...popupTrigger}
137+
>
120138
<WalletIcons name={wallet.name} icon={wallet.icon} size={20} />
121139
<Tooltip title="Copied" open={isCopied} placement="top" arrow>
122140
<Typography

0 commit comments

Comments
 (0)