Skip to content

Commit 6cccc6e

Browse files
authored
fix: APP-410 save buy flow on last user interaction (#2574)
1 parent 2d4fdcb commit 6cccc6e

20 files changed

+582
-184
lines changed

web-marketplace/src/components/molecules/CreditsAmount/CreditsAmount.constants.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ export const CURRENCY_AMOUNT = 'currencyAmount';
55
export const CURRENCY = 'currency';
66
export const CREDIT_VINTAGE_OPTIONS = 'creditVintageOptions';
77
export const SELL_ORDERS = 'sellOrders';
8+
export const MIN_USD_CURRENCY_AMOUNT = 0.5;
89

910
export const cryptoOptions = [
1011
{

web-marketplace/src/components/molecules/CreditsAmount/CreditsAmount.tsx

Lines changed: 88 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,13 @@ import {
1212
spendingCapAtom,
1313
} from 'pages/BuyCredits/BuyCredits.atoms';
1414
import { PAYMENT_OPTIONS } from 'pages/BuyCredits/BuyCredits.constants';
15+
import { BuyCreditsSchemaTypes } from 'pages/BuyCredits/BuyCredits.types';
1516
import {
1617
getCreditsAvailableBannerText,
1718
getOrderedSellOrders,
19+
resetCurrencyAndCredits,
1820
} from 'pages/BuyCredits/BuyCredits.utils';
21+
import { useMultiStep } from 'components/templates/MultiStepTemplate';
1922

2023
import { findDisplayDenom } from '../DenomLabel/DenomLabel.utils';
2124
import {
@@ -54,6 +57,11 @@ export const CreditsAmount = ({
5457
const setWarningBannerTextAtom = useSetAtom(warningBannerTextAtom);
5558
const [spendingCap, setSpendingCap] = useAtom(spendingCapAtom);
5659
const paymentOption = useAtomValue(paymentOptionAtom);
60+
const {
61+
data,
62+
handleSave: updateMultiStepData,
63+
activeStep,
64+
} = useMultiStep<BuyCreditsSchemaTypes>();
5765

5866
const card = useMemo(
5967
() => paymentOption === PAYMENT_OPTIONS.CARD,
@@ -142,34 +150,38 @@ export const CreditsAmount = ({
142150
// This can happen when the user switches payment option, currency,
143151
// or to only buy tradable credits,
144152
// but the amount set is above the amount of newly available credits
145-
const currentCreditsAmount = getValues(CREDITS_AMOUNT);
153+
let currentCreditsAmount = getValues(CREDITS_AMOUNT);
146154
if (currentCreditsAmount > _creditsAvailable) {
147-
setValue(CREDITS_AMOUNT, _creditsAvailable);
148-
setValue(CURRENCY_AMOUNT, _spendingCap);
149-
setValue(
150-
SELL_ORDERS,
151-
orderedSellOrders.map(order => {
152-
const price = getSellOrderPrice({ order, card });
153-
return formatSellOrder({ order, card, price });
154-
}),
155-
);
156155
setWarningBannerTextAtom(
157156
getCreditsAvailableBannerText(_creditsAvailable, displayDenom),
158157
);
158+
resetCurrencyAndCredits(
159+
paymentOption,
160+
orderedSellOrders,
161+
creditTypePrecision,
162+
setValue,
163+
updateMultiStepData,
164+
data,
165+
activeStep,
166+
_creditsAvailable,
167+
);
159168
} else {
160169
// Else we keep the same amount of credits
161-
// but we still need to update currency amount and sell orders
162-
// (because pricing and sell orders can be different)
163-
const { currencyAmount, sellOrders } = getCurrencyAmount({
164-
currentCreditsAmount,
165-
card,
170+
resetCurrencyAndCredits(
171+
paymentOption,
166172
orderedSellOrders,
167173
creditTypePrecision,
168-
});
169-
setValue(CURRENCY_AMOUNT, currencyAmount);
170-
setValue(SELL_ORDERS, sellOrders);
174+
setValue,
175+
updateMultiStepData,
176+
data,
177+
activeStep,
178+
currentCreditsAmount,
179+
);
171180
}
172181
}
182+
// Intentionally omit `updateMultiStepData` and `data` from the dependency array
183+
// because including them trigger unnecessary renders.
184+
// eslint-disable-next-line react-hooks/exhaustive-deps
173185
}, [
174186
cardSellOrders,
175187
filteredCryptoSellOrders,
@@ -183,6 +195,7 @@ export const CreditsAmount = ({
183195
card,
184196
setWarningBannerTextAtom,
185197
_,
198+
activeStep,
186199
displayDenom,
187200
]);
188201

@@ -193,26 +206,35 @@ export const CreditsAmount = ({
193206
setValue(CURRENCY_AMOUNT, spendingCap, {
194207
shouldValidate: true,
195208
});
196-
setValue(
197-
SELL_ORDERS,
198-
orderedSellOrders.map(order => {
199-
const price = getSellOrderPrice({ order, card });
200-
return formatSellOrder({ order, card, price });
201-
}),
209+
const sellOrders = orderedSellOrders.map(order => {
210+
const price = getSellOrderPrice({ order, card });
211+
return formatSellOrder({ order, card, price });
212+
});
213+
setValue(SELL_ORDERS, sellOrders);
214+
updateMultiStepData(
215+
{
216+
...data,
217+
creditsAmount: creditsAvailable,
218+
currencyAmount: spendingCap,
219+
sellOrders: sellOrders,
220+
},
221+
activeStep,
202222
);
203223
setMaxCreditsSelected(false);
204224
}
205225
}, [
226+
activeStep,
206227
card,
207228
creditsAvailable,
229+
data,
208230
maxCreditsSelected,
209231
orderedSellOrders,
210232
paymentOption,
211233
setValue,
212234
spendingCap,
235+
updateMultiStepData,
213236
]);
214237

215-
// Credits amount change
216238
const handleCreditsAmountChange = useCallback(
217239
(e: ChangeEvent<HTMLInputElement>) => {
218240
// Set currency amount according to credits quantity,
@@ -226,11 +248,27 @@ export const CreditsAmount = ({
226248
});
227249
setValue(CURRENCY_AMOUNT, currencyAmount, { shouldValidate: true });
228250
setValue(SELL_ORDERS, sellOrders);
251+
updateMultiStepData(
252+
{
253+
...data,
254+
creditsAmount: currentCreditsAmount,
255+
currencyAmount: currencyAmount,
256+
sellOrders,
257+
},
258+
activeStep,
259+
);
229260
},
230-
[card, orderedSellOrders, setValue, creditTypePrecision],
261+
[
262+
card,
263+
orderedSellOrders,
264+
creditTypePrecision,
265+
setValue,
266+
updateMultiStepData,
267+
data,
268+
activeStep,
269+
],
231270
);
232271

233-
// Currency amount change
234272
const handleCurrencyAmountChange = useCallback(
235273
(e: ChangeEvent<HTMLInputElement>) => {
236274
// Set credits quantity according to currency amount,
@@ -242,10 +280,28 @@ export const CreditsAmount = ({
242280
orderedSellOrders,
243281
creditTypePrecision,
244282
});
283+
284+
updateMultiStepData(
285+
{
286+
...data,
287+
creditsAmount: currentCreditsAmount,
288+
currencyAmount: isNaN(value) ? 0 : value,
289+
sellOrders,
290+
},
291+
activeStep,
292+
);
245293
setValue(CREDITS_AMOUNT, currentCreditsAmount, { shouldValidate: true });
246294
setValue(SELL_ORDERS, sellOrders);
247295
},
248-
[card, orderedSellOrders, setValue, creditTypePrecision],
296+
[
297+
card,
298+
orderedSellOrders,
299+
creditTypePrecision,
300+
updateMultiStepData,
301+
data,
302+
activeStep,
303+
setValue,
304+
],
249305
);
250306

251307
return (
@@ -266,12 +322,16 @@ export const CreditsAmount = ({
266322
cryptoCurrencies={cryptoCurrencies}
267323
displayDenom={displayDenom}
268324
allowedDenoms={allowedDenoms}
325+
orderedSellOrders={orderedSellOrders}
326+
creditTypePrecision={creditTypePrecision}
269327
/>
270328
)}
271329
<span className="p-10 sm:p-20 text-xl">=</span>
272330
<CreditsInput
273331
creditsAvailable={creditsAvailable}
274332
handleCreditsAmountChange={handleCreditsAmountChange}
333+
orderedSellOrders={orderedSellOrders}
334+
creditTypePrecision={creditTypePrecision}
275335
/>
276336
</div>
277337
{paymentOption === PAYMENT_OPTIONS.CRYPTO && (

web-marketplace/src/components/molecules/CreditsAmount/CreditsAmount.types.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ export interface CreditsAmountProps {
2727
export interface CreditsInputProps {
2828
creditsAvailable: number;
2929
handleCreditsAmountChange: (e: ChangeEvent<HTMLInputElement>) => void;
30+
orderedSellOrders: UISellOrderInfo[];
31+
creditTypePrecision?: number | null;
3032
}
3133

3234
export interface CurrencyInputProps {
@@ -37,4 +39,6 @@ export interface CurrencyInputProps {
3739
cryptoCurrencies: Currency[];
3840
allowedDenoms?: AllowedDenoms;
3941
displayDenom: string;
42+
orderedSellOrders: UISellOrderInfo[];
43+
creditTypePrecision?: number | null;
4044
}

web-marketplace/src/components/molecules/CreditsAmount/CreditsInput.tsx

Lines changed: 34 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,25 @@ import { ChangeEvent, FocusEvent, useCallback } from 'react';
22
import { useFormContext } from 'react-hook-form';
33
import { msg, Trans } from '@lingui/macro';
44
import { useLingui } from '@lingui/react';
5+
import { useAtomValue } from 'jotai';
56
import { ChooseCreditsFormSchemaType } from 'web-marketplace/src/components/organisms/ChooseCreditsForm/ChooseCreditsForm.schema';
67

78
import { LeafIcon } from 'web-components/src/components/icons/LeafIcon';
89
import TextField from 'web-components/src/components/inputs/new/TextField/TextField';
910

11+
import { paymentOptionAtom } from 'pages/BuyCredits/BuyCredits.atoms';
12+
import { BuyCreditsSchemaTypes } from 'pages/BuyCredits/BuyCredits.types';
13+
import { resetCurrencyAndCredits } from 'pages/BuyCredits/BuyCredits.utils';
14+
import { useMultiStep } from 'components/templates/MultiStepTemplate';
15+
1016
import { CREDITS_AMOUNT } from './CreditsAmount.constants';
1117
import { CreditsInputProps } from './CreditsAmount.types';
1218

1319
export const CreditsInput = ({
1420
creditsAvailable,
1521
handleCreditsAmountChange,
22+
orderedSellOrders,
23+
creditTypePrecision,
1624
}: CreditsInputProps) => {
1725
const { _ } = useLingui();
1826

@@ -22,6 +30,12 @@ export const CreditsInput = ({
2230
setValue,
2331
} = useFormContext<ChooseCreditsFormSchemaType>();
2432
const { onChange } = register(CREDITS_AMOUNT);
33+
const paymentOption = useAtomValue(paymentOptionAtom);
34+
const {
35+
data,
36+
handleSave: updateMultiStepData,
37+
activeStep,
38+
} = useMultiStep<BuyCreditsSchemaTypes>();
2539

2640
const onHandleChange = (event: ChangeEvent<HTMLInputElement>) => {
2741
onChange(event);
@@ -51,22 +65,29 @@ export const CreditsInput = ({
5165

5266
const handleOnBlur = useCallback(
5367
(event: FocusEvent<HTMLInputElement>): void => {
54-
// If the value is empty, set it to 0
68+
// If the value is empty or 0 reset the currency and credit fields.
5569
const value = event.target.value;
56-
if (value === '') {
57-
setValue(CREDITS_AMOUNT, 0, { shouldValidate: true });
70+
if (value === '' || parseFloat(value) === 0) {
71+
resetCurrencyAndCredits(
72+
paymentOption,
73+
orderedSellOrders,
74+
creditTypePrecision,
75+
setValue,
76+
updateMultiStepData,
77+
data,
78+
activeStep,
79+
);
5880
}
5981
},
60-
[setValue],
61-
);
62-
const handleOnFocus = useCallback(
63-
(event: FocusEvent<HTMLInputElement>): void => {
64-
const value = event.target.value;
65-
if (value === '0') {
66-
setValue(CREDITS_AMOUNT, 0, { shouldValidate: true });
67-
}
68-
},
69-
[setValue],
82+
[
83+
activeStep,
84+
creditTypePrecision,
85+
data,
86+
orderedSellOrders,
87+
paymentOption,
88+
setValue,
89+
updateMultiStepData,
90+
],
7091
);
7192

7293
return (
@@ -84,7 +105,6 @@ export const CreditsInput = ({
84105
onChange={onHandleChange}
85106
onInput={handleInput}
86107
onBlur={handleOnBlur}
87-
onFocus={handleOnFocus}
88108
placeholder="0"
89109
sx={{
90110
'& .MuiInputBase-root': {

0 commit comments

Comments
 (0)