From 019584512286fa8e7c26ff34fca1f3dd9fc8f559 Mon Sep 17 00:00:00 2001
From: Sharon Sheah <37326128+sharonsheah@users.noreply.github.com>
Date: Thu, 1 Feb 2024 15:27:05 +1100
Subject: [PATCH] feat: WT-2054 Add Loading, Success and Failure screens for
transaction (#1429)
---
.../src/widgets/definitions/events/bridge.ts | 26 +++++++
.../sdk/src/widgets/definitions/types.ts | 4 +
.../ClaimWithdrawalInProgress.tsx | 74 ++++++++++++++++++
.../components/Transactions/Transactions.tsx | 7 +-
.../view-context/BridgeViewContextTypes.ts | 25 +++++-
.../checkout/widgets-lib/src/locales/en.json | 13 ++++
.../checkout/widgets-lib/src/locales/ja.json | 13 ++++
.../checkout/widgets-lib/src/locales/ko.json | 13 ++++
.../checkout/widgets-lib/src/locales/zh.json | 13 ++++
.../src/widgets/bridge/BridgeWidget.tsx | 78 ++++++++++++++++++-
.../src/widgets/bridge/BridgeWidgetEvents.ts | 40 ++++++++++
.../widgets/bridge/views/ClaimWithdrawal.tsx | 20 +++--
.../widgets/bridge/views/MoveInProgress.tsx | 4 +-
13 files changed, 317 insertions(+), 13 deletions(-)
create mode 100644 packages/checkout/widgets-lib/src/components/Transactions/ClaimWithdrawalInProgress.tsx
diff --git a/packages/checkout/sdk/src/widgets/definitions/events/bridge.ts b/packages/checkout/sdk/src/widgets/definitions/events/bridge.ts
index 60dc3aa9b1..e1ae2c7506 100644
--- a/packages/checkout/sdk/src/widgets/definitions/events/bridge.ts
+++ b/packages/checkout/sdk/src/widgets/definitions/events/bridge.ts
@@ -6,6 +6,8 @@ export enum BridgeEventType {
FAILURE = 'failure',
TRANSACTION_SENT = 'transaction-sent',
LANGUAGE_CHANGED = 'language-changed',
+ CLAIM_WITHDRAWAL_SUCCESS = 'claim-withdrawal-success',
+ CLAIM_WITHDRAWAL_FAILURE = 'claim-withdrawal-failure',
}
/**
@@ -28,3 +30,27 @@ export type BridgeFailed = {
/** The timestamp of the failed transaction. */
timestamp: number;
};
+
+/**
+ * Represents a successful bridge claim withdrawal.
+ * @property {string} transactionHash
+ */
+export type BridgeClaimWithdrawalSuccess = {
+ /** The hash of the successful transaction. */
+ transactionHash: string;
+};
+
+/**
+ * Represents a failed bridge claim withdrawal.
+ * @property {string} transactionHash
+ * @property {string} reason
+ * @property {number} timestamp
+ */
+export type BridgeClaimWithdrawalFailed = {
+ /** The hash of the failed transaction. */
+ transactionHash: string;
+ /** The reason for the failed transaction. */
+ reason: string;
+ /** The timestamp of the failed transaction. */
+ timestamp: number;
+};
diff --git a/packages/checkout/sdk/src/widgets/definitions/types.ts b/packages/checkout/sdk/src/widgets/definitions/types.ts
index 2a0ce2deca..d2d8345e0e 100644
--- a/packages/checkout/sdk/src/widgets/definitions/types.ts
+++ b/packages/checkout/sdk/src/widgets/definitions/types.ts
@@ -1,6 +1,8 @@
import { Environment } from '@imtbl/config';
import { Web3Provider } from '@ethersproject/providers';
import {
+ BridgeClaimWithdrawalFailed,
+ BridgeClaimWithdrawalSuccess,
BridgeEventType,
BridgeFailed,
BridgeTransactionSent,
@@ -142,6 +144,8 @@ export type WidgetEventData = {
[BridgeEventType.TRANSACTION_SENT]: BridgeTransactionSent,
[BridgeEventType.FAILURE]: BridgeFailed,
[BridgeEventType.CLOSE_WIDGET]: {}
+ [BridgeEventType.CLAIM_WITHDRAWAL_SUCCESS]: BridgeClaimWithdrawalSuccess
+ [BridgeEventType.CLAIM_WITHDRAWAL_FAILURE]: BridgeClaimWithdrawalFailed
} & OrchestrationMapping & ProviderEventMapping,
[WidgetType.ONRAMP]: {
diff --git a/packages/checkout/widgets-lib/src/components/Transactions/ClaimWithdrawalInProgress.tsx b/packages/checkout/widgets-lib/src/components/Transactions/ClaimWithdrawalInProgress.tsx
new file mode 100644
index 0000000000..695cb4ca83
--- /dev/null
+++ b/packages/checkout/widgets-lib/src/components/Transactions/ClaimWithdrawalInProgress.tsx
@@ -0,0 +1,74 @@
+import { TransactionResponse } from '@ethersproject/providers';
+import { useContext, useEffect } from 'react';
+import { useTranslation } from 'react-i18next';
+import { UserJourney, useAnalytics } from 'context/analytics-provider/SegmentAnalyticsProvider';
+import { ViewActions, ViewContext } from 'context/view-context/ViewContext';
+import { BridgeWidgetViews } from 'context/view-context/BridgeViewContextTypes';
+import { LoadingView } from 'views/loading/LoadingView';
+
+interface ClaimWithdrawalInProgressProps {
+ transactionResponse: TransactionResponse;
+}
+
+export function ClaimWithdrawalInProgress({ transactionResponse }: ClaimWithdrawalInProgressProps) {
+ const { t } = useTranslation();
+ const { viewDispatch } = useContext(ViewContext);
+
+ const { page } = useAnalytics();
+
+ useEffect(() => {
+ page({
+ userJourney: UserJourney.BRIDGE,
+ screen: 'ClaimWithdrawalInProgress',
+ });
+ }, []);
+
+ useEffect(() => {
+ if (!transactionResponse) return;
+
+ (async () => {
+ try {
+ const receipt = await transactionResponse.wait();
+
+ if (receipt.status === 1) {
+ viewDispatch({
+ payload: {
+ type: ViewActions.UPDATE_VIEW,
+ view: {
+ type: BridgeWidgetViews.CLAIM_WITHDRAWAL_SUCCESS,
+ transactionHash: receipt.transactionHash,
+ },
+ },
+ });
+ return;
+ }
+
+ viewDispatch({
+ payload: {
+ type: ViewActions.UPDATE_VIEW,
+ view: {
+ type: BridgeWidgetViews.CLAIM_WITHDRAWAL_FAILURE,
+ transactionHash: receipt.transactionHash,
+ reason: 'Transaction failed',
+ },
+ },
+ });
+ } catch (error) {
+ // eslint-disable-next-line no-console
+ console.error(error);
+ viewDispatch({
+ payload: {
+ type: ViewActions.UPDATE_VIEW,
+ view: {
+ type: BridgeWidgetViews.CLAIM_WITHDRAWAL_FAILURE,
+ transactionHash: '',
+ reason: 'Transaction failed',
+ },
+ },
+ });
+ }
+ })();
+ }, [transactionResponse]);
+
+ return ;
+}
diff --git a/packages/checkout/widgets-lib/src/components/Transactions/Transactions.tsx b/packages/checkout/widgets-lib/src/components/Transactions/Transactions.tsx
index 37e20c03fa..2bd3b59dbe 100644
--- a/packages/checkout/widgets-lib/src/components/Transactions/Transactions.tsx
+++ b/packages/checkout/widgets-lib/src/components/Transactions/Transactions.tsx
@@ -38,7 +38,11 @@ import { KnownNetworkMap } from './transactionsType';
import { TransactionList } from './TransactionList';
import { NoTransactions } from './NoTransactions';
-export function Transactions() {
+type TransactionsProps = {
+ onBackButtonClick: () => void;
+};
+
+export function Transactions({ onBackButtonClick }: TransactionsProps) {
const { eventTargetState: { eventTarget } } = useContext(EventTargetContext);
const { cryptoFiatDispatch } = useContext(CryptoFiatContext);
@@ -292,6 +296,7 @@ export function Transactions() {
header={(
sendBridgeWidgetCloseEvent(eventTarget)}
/>
diff --git a/packages/checkout/widgets-lib/src/context/view-context/BridgeViewContextTypes.ts b/packages/checkout/widgets-lib/src/context/view-context/BridgeViewContextTypes.ts
index 452b013940..9550b1097a 100644
--- a/packages/checkout/widgets-lib/src/context/view-context/BridgeViewContextTypes.ts
+++ b/packages/checkout/widgets-lib/src/context/view-context/BridgeViewContextTypes.ts
@@ -1,4 +1,5 @@
import { ApproveBridgeResponse, BridgeTxResponse } from '@imtbl/bridge-sdk';
+import { TransactionResponse } from '@ethersproject/providers';
import { Transaction } from 'lib/clients';
import { ViewType } from './ViewType';
@@ -11,6 +12,9 @@ export enum BridgeWidgetViews {
APPROVE_TRANSACTION = 'APPROVE_TRANSACTION',
TRANSACTIONS = 'TRANSACTIONS',
CLAIM_WITHDRAWAL = 'CLAIM_WITHDRAWAL',
+ CLAIM_WITHDRAWAL_IN_PROGRESS = 'CLAIM_WITHDRAWAL_IN_PROGRESS',
+ CLAIM_WITHDRAWAL_SUCCESS = 'CLAIM_WITHDRAWAL_SUCCESS',
+ CLAIM_WITHDRAWAL_FAILURE = 'CLAIM_WITHDRAWAL_FAILURE',
}
export type BridgeWidgetView =
@@ -21,7 +25,10 @@ export type BridgeWidgetView =
| BridgeFailure
| BridgeApproveTransaction
| BridgeTransactions
- | BridgeClaimWithdrawal;
+ | BridgeClaimWithdrawal
+ | BridgeClaimWithdrawalInProgress
+ | BridgeClaimWithdrawalSuccess
+ | BridgeClaimWithdrawalFailure;
interface BridgeCrossWalletSelection extends ViewType {
type: BridgeWidgetViews.WALLET_NETWORK_SELECTION,
@@ -59,3 +66,19 @@ interface BridgeClaimWithdrawal extends ViewType {
type: BridgeWidgetViews.CLAIM_WITHDRAWAL,
transaction: Transaction
}
+
+interface BridgeClaimWithdrawalInProgress extends ViewType {
+ type: BridgeWidgetViews.CLAIM_WITHDRAWAL_IN_PROGRESS,
+ transactionResponse: TransactionResponse;
+}
+
+export interface BridgeClaimWithdrawalSuccess extends ViewType {
+ type: BridgeWidgetViews.CLAIM_WITHDRAWAL_SUCCESS,
+ transactionHash: string;
+}
+
+export interface BridgeClaimWithdrawalFailure extends ViewType {
+ type: BridgeWidgetViews.CLAIM_WITHDRAWAL_FAILURE,
+ transactionHash: string;
+ reason: string;
+}
diff --git a/packages/checkout/widgets-lib/src/locales/en.json b/packages/checkout/widgets-lib/src/locales/en.json
index 0ae3abb3fc..9c6c1127d4 100644
--- a/packages/checkout/widgets-lib/src/locales/en.json
+++ b/packages/checkout/widgets-lib/src/locales/en.json
@@ -503,6 +503,19 @@
"footer": {
"buttonText": "Continue",
"retryText": "Try again"
+ },
+ "IN_PROGRESS": {
+ "loading": {
+ "text": "Processing"
+ },
+ "success": {
+ "text": "Success",
+ "actionText": "Done"
+ },
+ "failure": {
+ "text": "Transaction failed",
+ "actionText": "Try again"
+ }
}
}
},
diff --git a/packages/checkout/widgets-lib/src/locales/ja.json b/packages/checkout/widgets-lib/src/locales/ja.json
index 8abd3dd74f..e6f252fc1a 100644
--- a/packages/checkout/widgets-lib/src/locales/ja.json
+++ b/packages/checkout/widgets-lib/src/locales/ja.json
@@ -506,6 +506,19 @@
"footer": {
"buttonText": "続行",
"retryText": "もう一度試す"
+ },
+ "IN_PROGRESS": {
+ "loading": {
+ "text": "処理中"
+ },
+ "success": {
+ "text": "成功",
+ "actionText": "完了"
+ },
+ "failure": {
+ "text": "取引失敗",
+ "actionText": "再試行"
+ }
}
}
},
diff --git a/packages/checkout/widgets-lib/src/locales/ko.json b/packages/checkout/widgets-lib/src/locales/ko.json
index 6335b6f3b2..2408b7fb49 100644
--- a/packages/checkout/widgets-lib/src/locales/ko.json
+++ b/packages/checkout/widgets-lib/src/locales/ko.json
@@ -503,6 +503,19 @@
"footer": {
"buttonText": "계속",
"retryText": "다시 시도"
+ },
+ "IN_PROGRESS": {
+ "loading": {
+ "text": "처리 중"
+ },
+ "success": {
+ "text": "성공",
+ "actionText": "완료"
+ },
+ "failure": {
+ "text": "거래 실패",
+ "actionText": "다시 시도"
+ }
}
}
},
diff --git a/packages/checkout/widgets-lib/src/locales/zh.json b/packages/checkout/widgets-lib/src/locales/zh.json
index 4a7d656c24..5270ceab42 100644
--- a/packages/checkout/widgets-lib/src/locales/zh.json
+++ b/packages/checkout/widgets-lib/src/locales/zh.json
@@ -503,6 +503,19 @@
"footer": {
"buttonText": "继续",
"retryText": "重试"
+ },
+ "IN_PROGRESS": {
+ "loading": {
+ "text": "处理中"
+ },
+ "success": {
+ "text": "成功",
+ "actionText": "完成"
+ },
+ "failure": {
+ "text": "交易失败",
+ "actionText": "请重试"
+ }
}
}
},
diff --git a/packages/checkout/widgets-lib/src/widgets/bridge/BridgeWidget.tsx b/packages/checkout/widgets-lib/src/widgets/bridge/BridgeWidget.tsx
index a084fc82d4..8f11e467b2 100644
--- a/packages/checkout/widgets-lib/src/widgets/bridge/BridgeWidget.tsx
+++ b/packages/checkout/widgets-lib/src/widgets/bridge/BridgeWidget.tsx
@@ -13,7 +13,7 @@ import {
import { StrongCheckoutWidgetsConfig } from 'lib/withDefaultWidgetConfig';
import { CryptoFiatProvider } from 'context/crypto-fiat-context/CryptoFiatProvider';
import { JsonRpcProvider, Web3Provider } from '@ethersproject/providers';
-import { BridgeWidgetViews } from 'context/view-context/BridgeViewContextTypes';
+import { BridgeClaimWithdrawalFailure, BridgeWidgetViews } from 'context/view-context/BridgeViewContextTypes';
import { StatusView } from 'components/Status/StatusView';
import { StatusType } from 'components/Status/StatusType';
import { ImmutableConfiguration } from '@imtbl/config';
@@ -29,6 +29,7 @@ import { Transactions } from 'components/Transactions/Transactions';
import { UserJourney, useAnalytics } from 'context/analytics-provider/SegmentAnalyticsProvider';
import { TopUpView } from 'views/top-up/TopUpView';
import { useTranslation } from 'react-i18next';
+import { ClaimWithdrawalInProgress } from 'components/Transactions/ClaimWithdrawalInProgress';
import {
ViewActions,
ViewContext,
@@ -49,7 +50,15 @@ import { MoveInProgress } from './views/MoveInProgress';
import { ApproveTransaction } from './views/ApproveTransaction';
import { ErrorView } from '../../views/error/ErrorView';
import { EventTargetContext } from '../../context/event-target-context/EventTargetContext';
-import { sendBridgeFailedEvent, sendBridgeWidgetCloseEvent } from './BridgeWidgetEvents';
+import {
+ sendBridgeClaimWithdrawalFailedEvent,
+ sendBridgeClaimWithdrawalSuccessEvent,
+ sendBridgeFailedEvent,
+ sendBridgeWidgetCloseEvent,
+} from './BridgeWidgetEvents';
+import {
+ BridgeClaimWithdrawalSuccess,
+} from '../../context/view-context/BridgeViewContextTypes';
import { ClaimWithdrawal } from './views/ClaimWithdrawal';
export type BridgeWidgetInputs = BridgeWidgetParams & {
@@ -215,7 +224,7 @@ export function BridgeWidget({
/>
)}
{viewState.view.type === BridgeWidgetViews.TRANSACTIONS && (
-
+
)}
{viewState.view.type === BridgeWidgetViews.CLAIM_WITHDRAWAL && (
@@ -245,6 +254,69 @@ export function BridgeWidget({
onCloseButtonClick={() => sendBridgeWidgetCloseEvent(eventTarget)}
/>
)}
+ {viewState.view.type === BridgeWidgetViews.CLAIM_WITHDRAWAL_IN_PROGRESS && (
+
+ )}
+ {viewState.view.type === BridgeWidgetViews.CLAIM_WITHDRAWAL_SUCCESS && (
+ {
+ page({
+ userJourney: UserJourney.BRIDGE,
+ screen: 'ClaimWithdrawalSuccess',
+ });
+ sendBridgeClaimWithdrawalSuccessEvent(
+ eventTarget,
+ (viewState.view as BridgeClaimWithdrawalSuccess).transactionHash,
+ );
+ }}
+ onActionClick={() => sendBridgeWidgetCloseEvent(eventTarget)}
+ statusType={StatusType.SUCCESS}
+ testId="claim-withdrawal-success-view"
+ />
+ )}
+ {viewState.view.type === BridgeWidgetViews.CLAIM_WITHDRAWAL_FAILURE && (
+ {
+ let reason = '';
+ if (viewState.view.type === BridgeWidgetViews.CLAIM_WITHDRAWAL_FAILURE) {
+ reason = viewState.view.reason;
+ }
+ page({
+ userJourney: UserJourney.BRIDGE,
+ screen: 'ClaimWithdrawalFailure',
+ extras: {
+ reason,
+ },
+ });
+ sendBridgeClaimWithdrawalFailedEvent(
+ eventTarget,
+ (viewState.view as BridgeClaimWithdrawalFailure).transactionHash,
+ 'Transaction failed',
+ );
+ }}
+ onActionClick={() => {
+ if (viewState.view.type === BridgeWidgetViews.CLAIM_WITHDRAWAL_FAILURE) {
+ viewDispatch({
+ payload: {
+ type: ViewActions.UPDATE_VIEW,
+ view: {
+ type: BridgeWidgetViews.TRANSACTIONS,
+ },
+ },
+ });
+ }
+ }}
+ statusType={StatusType.FAILURE}
+ onCloseClick={() => sendBridgeWidgetCloseEvent(eventTarget)}
+ testId="claim-withdrawal-fail-view"
+ />
+ )}
diff --git a/packages/checkout/widgets-lib/src/widgets/bridge/BridgeWidgetEvents.ts b/packages/checkout/widgets-lib/src/widgets/bridge/BridgeWidgetEvents.ts
index 116df50ec8..1fde897b0a 100644
--- a/packages/checkout/widgets-lib/src/widgets/bridge/BridgeWidgetEvents.ts
+++ b/packages/checkout/widgets-lib/src/widgets/bridge/BridgeWidgetEvents.ts
@@ -54,3 +54,43 @@ export function sendBridgeWidgetCloseEvent(eventTarget: Window | EventTarget) {
console.log('bridge close ', eventTarget, closeWidgetEvent);
if (eventTarget !== undefined) eventTarget.dispatchEvent(closeWidgetEvent);
}
+
+export const sendBridgeClaimWithdrawalSuccessEvent = (eventTarget: Window | EventTarget, transactionHash: string) => {
+ const successEvent = new CustomEvent>(
+ IMTBLWidgetEvents.IMTBL_BRIDGE_WIDGET_EVENT,
+ {
+ detail: {
+ type: BridgeEventType.CLAIM_WITHDRAWAL_SUCCESS,
+ data: {
+ transactionHash,
+ },
+ },
+ },
+ );
+ // eslint-disable-next-line no-console
+ console.log('bridge claim withdrawal success event:', eventTarget, successEvent);
+ if (eventTarget !== undefined) eventTarget.dispatchEvent(successEvent);
+};
+
+export const sendBridgeClaimWithdrawalFailedEvent = (
+ eventTarget: Window | EventTarget,
+ transactionHash: string,
+ reason: string,
+) => {
+ const failedEvent = new CustomEvent>(
+ IMTBLWidgetEvents.IMTBL_BRIDGE_WIDGET_EVENT,
+ {
+ detail: {
+ type: BridgeEventType.CLAIM_WITHDRAWAL_FAILURE,
+ data: {
+ transactionHash,
+ reason,
+ timestamp: new Date().getTime(),
+ },
+ },
+ },
+ );
+ // eslint-disable-next-line no-console
+ console.log('bridge claim withdrawal failed event:', eventTarget, failedEvent);
+ if (eventTarget !== undefined) eventTarget.dispatchEvent(failedEvent);
+};
diff --git a/packages/checkout/widgets-lib/src/widgets/bridge/views/ClaimWithdrawal.tsx b/packages/checkout/widgets-lib/src/widgets/bridge/views/ClaimWithdrawal.tsx
index 5ac20d5d41..8f2fc93493 100644
--- a/packages/checkout/widgets-lib/src/widgets/bridge/views/ClaimWithdrawal.tsx
+++ b/packages/checkout/widgets-lib/src/widgets/bridge/views/ClaimWithdrawal.tsx
@@ -15,6 +15,7 @@ import { WalletProviderName } from '@imtbl/checkout-sdk';
import { isNativeToken } from 'lib/utils';
import { BigNumber } from 'ethers';
import { FlowRateWithdrawResponse } from '@imtbl/bridge-sdk';
+import { BridgeWidgetViews } from 'context/view-context/BridgeViewContextTypes';
import { SimpleLayout } from '../../../components/SimpleLayout/SimpleLayout';
import { HeaderNavigation } from '../../../components/Header/HeaderNavigation';
import { sendBridgeWidgetCloseEvent } from '../BridgeWidgetEvents';
@@ -169,13 +170,20 @@ export function ClaimWithdrawal({ transaction }: ClaimWithdrawalProps) {
// send transaction to wallet for signing
try {
- // TODO: WT-2054 Update view to go to in progress screens and pass through sendTransaction response
-
- // const response = await checkout.sendTransaction({
- // provider: providerToUse,
- // transaction: withdrawalResponse.unsignedTx,
- // });
+ const response = await checkout.sendTransaction({
+ provider: providerToUse,
+ transaction: withdrawalResponse.unsignedTx,
+ });
+ viewDispatch({
+ payload: {
+ type: ViewActions.UPDATE_VIEW,
+ view: {
+ type: BridgeWidgetViews.CLAIM_WITHDRAWAL_IN_PROGRESS,
+ transactionResponse: response.transactionResponse,
+ },
+ },
+ });
} catch (err) {
// eslint-disable-next-line no-console
console.log(err);
diff --git a/packages/checkout/widgets-lib/src/widgets/bridge/views/MoveInProgress.tsx b/packages/checkout/widgets-lib/src/widgets/bridge/views/MoveInProgress.tsx
index de5205b909..25a2dba436 100644
--- a/packages/checkout/widgets-lib/src/widgets/bridge/views/MoveInProgress.tsx
+++ b/packages/checkout/widgets-lib/src/widgets/bridge/views/MoveInProgress.tsx
@@ -84,8 +84,8 @@ export function MoveInProgress({ transactionHash }: MoveInProgressProps) {
variant="guidance"
sx={{
position: 'absolute',
- right: 'base.spacing.x16',
- top: 'base.spacing.x2',
+ right: 'base.spacing.x14',
+ top: 'base.spacing.x1',
}}
/>
>