Skip to content

Commit e51457e

Browse files
authored
GPR | Add fns to help route errors to error views (#969)
1 parent 675f4c8 commit e51457e

File tree

6 files changed

+156
-61
lines changed

6 files changed

+156
-61
lines changed

packages/checkout/widgets-lib/src/context/view-context/SaleViewContextTypes.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ export enum SaleWidgetViews {
55
PAY_WITH_COINS = 'PAY_WITH_COINS',
66
PAY_WITH_CARD = 'PAY_WITH_CARD',
77
FUND_WITH_SMART_CHECKOUT = 'FUND_WITH_SMART_CHECKOUT',
8-
MINT_SUCCESS = 'MINT_SUCCESS',
9-
MINT_FAIL = 'MINT_FAIL',
8+
SALE_SUCCESS = 'SALE_SUCCESS',
9+
SALE_FAIL = 'SALE_FAIL',
1010
}
1111

1212
export type SaleWidgetView =
@@ -31,10 +31,10 @@ interface SaleSmartCheckoutView extends ViewType {
3131
subView: FundWithSmartCheckoutSubViews;
3232
}
3333
interface SaleSuccessView extends ViewType {
34-
type: SaleWidgetViews.MINT_SUCCESS;
34+
type: SaleWidgetViews.SALE_SUCCESS;
3535
}
3636
interface SaleFailView extends ViewType {
37-
type: SaleWidgetViews.MINT_FAIL;
37+
type: SaleWidgetViews.SALE_FAIL;
3838
reason?: string;
3939
}
4040

packages/checkout/widgets-lib/src/resources/text/textConfig.ts

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { WalletWidgetViews } from '../../context/view-context/WalletViewContextT
77
import { BridgeWidgetViews } from '../../context/view-context/BridgeViewContextTypes';
88
import { OnRampWidgetViews } from '../../context/view-context/OnRampViewContextTypes';
99
import { SaleWidgetViews } from '../../context/view-context/SaleViewContextTypes';
10-
import { MintErrorTypes, PaymentTypes } from '../../widgets/sale/types';
10+
import { SaleErrorTypes, PaymentTypes } from '../../widgets/sale/types';
1111
import { ServiceType } from '../../views/error/serviceTypes';
1212

1313
export const text = {
@@ -349,47 +349,47 @@ export const text = {
349349
screenTitle: 'Pay with card',
350350
loading: 'Taking you to Transak',
351351
},
352-
[SaleWidgetViews.MINT_FAIL]: {
352+
[SaleWidgetViews.SALE_FAIL]: {
353353
errors: {
354-
[MintErrorTypes.TRANSACTION_FAILED]: {
354+
[SaleErrorTypes.TRANSACTION_FAILED]: {
355355
description: 'Transaction failed',
356356
primaryAction: 'Try again',
357357
secondaryAction: 'View details',
358358
},
359-
[MintErrorTypes.SERVICE_BREAKDOWN]: {
359+
[SaleErrorTypes.SERVICE_BREAKDOWN]: {
360360
description:
361361
"Sorry, we're unable to process your payment right now. Please try again in a few minutes.",
362362
secondaryAction: 'Dismiss',
363363
},
364-
[MintErrorTypes.TRANSAK_FAILED]: {
364+
[SaleErrorTypes.TRANSAK_FAILED]: {
365365
description: 'Sorry, something went wrong. Please try again.',
366366
primaryAction: 'Try again',
367367
secondaryAction: 'Dismiss',
368368
},
369-
[MintErrorTypes.PASSPORT_FAILED]: {
369+
[SaleErrorTypes.WALLET_FAILED]: {
370370
description: "Sorry, we're unable to process this right now.",
371371
primaryAction: 'Go back',
372372
secondaryAction: 'Dismiss',
373373
},
374-
[MintErrorTypes.PASSPORT_REJECTED_NO_FUNDS]: {
374+
[SaleErrorTypes.WALLET_REJECTED_NO_FUNDS]: {
375375
description: 'Sorry, something went wrong. Plese try again.',
376376
primaryAction: 'Go back',
377377
secondaryAction: 'Dismiss',
378378
},
379-
[MintErrorTypes.PASSPORT_REJECTED]: {
379+
[SaleErrorTypes.WALLET_REJECTED]: {
380380
description:
381-
"You'll need to approve the transaction in Passport to proceed.",
381+
"You'll need to approve the transaction in your wallet to proceed.",
382382
primaryAction: 'Try again',
383383
secondaryAction: 'Cancel',
384384
},
385-
[MintErrorTypes.DEFAULT]: {
385+
[SaleErrorTypes.DEFAULT]: {
386386
description: 'Sorry, something went wrong. Please try again.',
387387
primaryAction: 'Try again',
388388
secondaryAction: 'Dismiss',
389389
},
390390
},
391391
},
392-
[SaleWidgetViews.MINT_SUCCESS]: {
392+
[SaleWidgetViews.SALE_SUCCESS]: {
393393
text: 'Order completed',
394394
actionText: 'Continue',
395395
},

packages/checkout/widgets-lib/src/widgets/sale/SaleWidget.tsx

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import { StatusType } from '../../components/Status/StatusType';
2222
import { StatusView, StatusViewProps } from '../../components/Status/StatusView';
2323
import { EventTargetContext } from '../../context/event-target-context/EventTargetContext';
2424
import { SaleWidgetViews } from '../../context/view-context/SaleViewContextTypes';
25-
import { Item, MintErrorTypes } from './types';
25+
import { Item, SaleErrorTypes } from './types';
2626
import { widgetTheme } from '../../lib/theme';
2727
import { SaleContextProvider } from './context/SaleContextProvider';
2828
import { FundWithSmartCheckout } from './views/FundWithSmartCheckout';
@@ -44,7 +44,7 @@ interface ErrorTextConfig {
4444
secondaryAction?: string;
4545
}
4646

47-
type AllErrorTextConfigs = Record<MintErrorTypes, ErrorTextConfig>;
47+
type AllErrorTextConfigs = Record<SaleErrorTypes, ErrorTextConfig>;
4848

4949
export interface SaleWidgetProps {
5050
config: StrongCheckoutWidgetsConfig;
@@ -127,8 +127,8 @@ export function SaleWidget(props: SaleWidgetProps) {
127127
sendSaleWidgetCloseEvent(eventTarget);
128128
};
129129

130-
const errorHandlersConfig: Record<MintErrorTypes, ErrorHandlerConfig> = {
131-
[MintErrorTypes.TRANSACTION_FAILED]: {
130+
const errorHandlersConfig: Record<SaleErrorTypes, ErrorHandlerConfig> = {
131+
[SaleErrorTypes.TRANSACTION_FAILED]: {
132132
onActionClick: updateToPaymentMethods,
133133
onSecondaryActionClick: () => {
134134
/* TODO: redirects to Immutascan to check the transaction */
@@ -138,50 +138,50 @@ export function SaleWidget(props: SaleWidgetProps) {
138138
fill: biomeTheme.color.status.destructive.dim,
139139
},
140140
},
141-
[MintErrorTypes.SERVICE_BREAKDOWN]: {
141+
[SaleErrorTypes.SERVICE_BREAKDOWN]: {
142142
onSecondaryActionClick: closeWidget,
143143
statusType: StatusType.INFORMATION,
144144
statusIconStyles: {
145145
fill: biomeTheme.color.status.fatal.dim,
146146
},
147147
},
148-
[MintErrorTypes.TRANSAK_FAILED]: {
148+
[SaleErrorTypes.TRANSAK_FAILED]: {
149149
onActionClick: () => {
150150
/* TODO: start over the transak flow */
151151
},
152152
onSecondaryActionClick: closeWidget,
153153
statusType: StatusType.INFORMATION,
154154
},
155-
[MintErrorTypes.PASSPORT_FAILED]: {
155+
[SaleErrorTypes.WALLET_FAILED]: {
156156
onActionClick: updateToPaymentMethods,
157157
onSecondaryActionClick: closeWidget,
158158
statusType: StatusType.INFORMATION,
159159
statusIconStyles: {
160160
fill: biomeTheme.color.status.fatal.dim,
161161
},
162162
},
163-
[MintErrorTypes.PASSPORT_REJECTED_NO_FUNDS]: {
163+
[SaleErrorTypes.WALLET_REJECTED_NO_FUNDS]: {
164164
onActionClick: updateToPaymentMethods,
165165
onSecondaryActionClick: closeWidget,
166166
statusType: StatusType.INFORMATION,
167167
},
168-
[MintErrorTypes.PASSPORT_REJECTED]: {
168+
[SaleErrorTypes.WALLET_REJECTED]: {
169169
onActionClick: () => {
170170
/* TODO: trigger the approve and execute flow pop up flow again */
171171
},
172172
onSecondaryActionClick: closeWidget,
173173
statusType: StatusType.INFORMATION,
174174
},
175-
[MintErrorTypes.DEFAULT]: {
175+
[SaleErrorTypes.DEFAULT]: {
176176
onActionClick: updateToPaymentMethods,
177177
onSecondaryActionClick: closeWidget,
178178
statusType: StatusType.INFORMATION,
179179
},
180180
};
181181

182182
const errorViewProps = useMemo<StatusViewProps>(() => {
183-
const errorTextConfig: AllErrorTextConfigs = text.views[SaleWidgetViews.MINT_FAIL].errors;
184-
const errorType = viewState.view.data?.error || MintErrorTypes.DEFAULT;
183+
const errorTextConfig: AllErrorTextConfigs = text.views[SaleWidgetViews.SALE_FAIL].errors;
184+
const errorType = viewState.view.data?.error || SaleErrorTypes.DEFAULT;
185185
const handlers = errorHandlersConfig[errorType] || {};
186186
return {
187187
testId: 'fail-view',
@@ -230,17 +230,17 @@ export function SaleWidget(props: SaleWidgetProps) {
230230
{viewState.view.type === SaleWidgetViews.PAY_WITH_COINS && (
231231
<PayWithCoins />
232232
)}
233-
{viewState.view.type === SaleWidgetViews.MINT_FAIL && (
233+
{viewState.view.type === SaleWidgetViews.SALE_FAIL && (
234234
<StatusView {...errorViewProps} />
235235
)}
236-
{viewState.view.type === SaleWidgetViews.MINT_SUCCESS
236+
{viewState.view.type === SaleWidgetViews.SALE_SUCCESS
237237
&& provider && (
238238
<StatusView
239239
statusText={
240-
text.views[SaleWidgetViews.MINT_SUCCESS].text
240+
text.views[SaleWidgetViews.SALE_SUCCESS].text
241241
}
242242
actionText={
243-
text.views[SaleWidgetViews.MINT_SUCCESS].actionText
243+
text.views[SaleWidgetViews.SALE_SUCCESS].actionText
244244
}
245245
onActionClick={() => closeWidget()}
246246
statusType={StatusType.SUCCESS}

packages/checkout/widgets-lib/src/widgets/sale/context/SaleContextProvider.tsx

Lines changed: 67 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,23 @@ import {
66
useEffect,
77
useState,
88
useCallback,
9+
useRef,
910
} from 'react';
1011
import { Passport } from '@imtbl/passport';
1112

1213
import { SaleSuccess } from '@imtbl/checkout-widgets';
1314

14-
import { Item, PaymentTypes, SignResponse } from '../types';
15+
import {
16+
Item, PaymentTypes, SignResponse, SaleErrorTypes,
17+
} from '../types';
1518
import { useSignOrder } from '../hooks/useSignOrder';
1619
import { ConnectLoaderState } from '../../../context/connect-loader-context/ConnectLoaderContext';
1720
import { StrongCheckoutWidgetsConfig } from '../../../lib/withDefaultWidgetConfig';
21+
import {
22+
ViewActions,
23+
ViewContext,
24+
} from '../../../context/view-context/ViewContext';
25+
import { SaleWidgetViews } from '../../../context/view-context/SaleViewContextTypes';
1826

1927
type SaleContextProps = {
2028
config: StrongCheckoutWidgetsConfig;
@@ -37,6 +45,8 @@ type SaleContextValues = SaleContextProps & {
3745
isPassportWallet: boolean;
3846
paymentMethod: PaymentTypes | undefined;
3947
setPaymentMethod: (paymentMethod: PaymentTypes) => void;
48+
goBackToPaymentMethods: (paymentMethod?: PaymentTypes | undefined) => void;
49+
goToErrorView: (type: SaleErrorTypes, data?: Record<string, unknown>) => void;
4050
};
4151

4252
// eslint-disable-next-line @typescript-eslint/naming-convention
@@ -57,11 +67,16 @@ const SaleContext = createContext<SaleContextValues>({
5767
isPassportWallet: false,
5868
paymentMethod: undefined,
5969
setPaymentMethod: () => {},
70+
goBackToPaymentMethods: () => {},
71+
goToErrorView: () => {},
6072
config: {} as StrongCheckoutWidgetsConfig,
6173
});
6274

6375
SaleContext.displayName = 'SaleSaleContext';
6476

77+
/** Max attemps to retry with same payment method */
78+
const MAX_ERROR_RETRIES = 1;
79+
6580
export function SaleContextProvider(props: {
6681
children: ReactNode;
6782
value: SaleContextProps;
@@ -81,6 +96,8 @@ export function SaleContextProvider(props: {
8196
},
8297
} = props;
8398

99+
const errorRetries = useRef(0);
100+
const { viewDispatch } = useContext(ViewContext);
84101
const [{ recipientEmail, recipientAddress }, setUserInfo] = useState<{
85102
recipientEmail: string;
86103
recipientAddress: string;
@@ -91,11 +108,41 @@ export function SaleContextProvider(props: {
91108

92109
const [paymentMethod, setPaymentMethod] = useState<PaymentTypes | undefined>(undefined);
93110

94-
// Get user info
111+
const goBackToPaymentMethods = useCallback((type?: PaymentTypes | undefined) => {
112+
setPaymentMethod(type);
113+
viewDispatch({
114+
payload: {
115+
type: ViewActions.UPDATE_VIEW,
116+
view: { type: SaleWidgetViews.PAYMENT_METHODS },
117+
},
118+
});
119+
}, []);
120+
121+
const goToErrorView = useCallback(
122+
(errorType: SaleErrorTypes, data: Record<string, unknown> = {}) => {
123+
errorRetries.current += 1;
124+
if (errorRetries.current > MAX_ERROR_RETRIES) {
125+
errorRetries.current = 0;
126+
setPaymentMethod(undefined);
127+
}
128+
129+
viewDispatch({
130+
payload: {
131+
type: ViewActions.UPDATE_VIEW,
132+
view: {
133+
type: SaleWidgetViews.SALE_FAIL,
134+
data: { errorType, ...data },
135+
},
136+
},
137+
});
138+
},
139+
[],
140+
);
141+
95142
useEffect(() => {
96143
const getUserInfo = async () => {
97144
const signer = provider?.getSigner();
98-
const address = await signer?.getAddress() || '';
145+
const address = (await signer?.getAddress()) || '';
99146
const email = (await passport?.getUserInfo())?.email || '';
100147

101148
setUserInfo({ recipientEmail: email, recipientAddress: address });
@@ -113,11 +160,19 @@ export function SaleContextProvider(props: {
113160
env,
114161
});
115162

116-
const sign = useCallback(async (type: PaymentTypes, callback?: (r?: SignResponse) => void) => {
117-
const response = await signOrder(type);
118-
callback?.(response);
119-
return response;
120-
}, [signOrder]);
163+
const sign = useCallback(
164+
async (
165+
type: PaymentTypes,
166+
callback?: (r?: SignResponse) => void,
167+
): Promise<SignResponse | undefined> => {
168+
const response = await signOrder(type);
169+
if (!response) return undefined;
170+
171+
callback?.(response);
172+
return response;
173+
},
174+
[signOrder],
175+
);
121176

122177
const values = useMemo(
123178
() => ({
@@ -136,6 +191,8 @@ export function SaleContextProvider(props: {
136191
recipientEmail,
137192
paymentMethod,
138193
setPaymentMethod,
194+
goBackToPaymentMethods,
195+
goToErrorView,
139196
isPassportWallet: !!(provider?.provider as any)?.isPassport,
140197
}),
141198
[
@@ -152,6 +209,8 @@ export function SaleContextProvider(props: {
152209
signResponse,
153210
paymentMethod,
154211
signResponse,
212+
goBackToPaymentMethods,
213+
goToErrorView,
155214
],
156215
);
157216

0 commit comments

Comments
 (0)