Skip to content

Commit

Permalink
Ability to show transaction toast on demand (#1304)
Browse files Browse the repository at this point in the history
* Ability to show transaction toast on demand

* Refactor

* Added transaction received toast

* CHANGELOG.md

* Refactor

* Refactor
  • Loading branch information
razvantomegea authored Nov 11, 2024
1 parent 8ff54ea commit f5e9d9d
Show file tree
Hide file tree
Showing 16 changed files with 148 additions and 51 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

- [Added ability to show transaction toast on demand](https://github.com/multiversx/mx-sdk-dapp/pull/1304)

## [[v3.0.9](https://github.com/multiversx/mx-sdk-dapp/pull/1299)] - 2024-11-06

- [Fix clear initiated login](https://github.com/multiversx/mx-sdk-dapp/pull/1301)
Expand Down
27 changes: 21 additions & 6 deletions src/UI/TransactionDetails/TransactionDetails.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import React, { useMemo, ReactNode } from 'react';
import React, { ReactNode, useMemo } from 'react';
import { withStyles, WithStylesImportType } from 'hocs/withStyles';
import { SignedTransactionType } from 'types/index';
import { useGetAccount } from 'hooks/account/useGetAccount';
import {
SignedTransactionType,
TransactionServerStatusesEnum
} from 'types/index';
import { isServerTransactionPending } from 'utils/transactions/transactionStateByStatus';
import {
TransactionDetailsBody,
Expand All @@ -25,32 +29,43 @@ const TransactionDetailsComponent = ({
return null;
}

const { address } = useGetAccount();

const processedTransactionsStatus = useMemo(() => {
const processedTransactions = transactions.filter(
(tx) => !isServerTransactionPending(tx?.status)
(tx) =>
!isServerTransactionPending(TransactionServerStatusesEnum[tx?.status])
).length;

const totalTransactions = transactions.length;

if (totalTransactions === 1 && processedTransactions === 1) {
return isServerTransactionPending(transactions[0].status)
return isServerTransactionPending(
TransactionServerStatusesEnum[transactions[0].status]
)
? 'Processing transaction'
: 'Transaction processed';
}

return `${processedTransactions} / ${totalTransactions} transactions processed`;
}, [transactions]);

const hideProcessedTransactionsStatus =
transactions.length === 1 && transactions[0].sender !== address;

return (
<>
{title && <div className={styles?.title}>{title}</div>}

<div className={styles?.status}>{processedTransactionsStatus}</div>
{!hideProcessedTransactionsStatus && (
<div className={styles?.status}>{processedTransactionsStatus}</div>
)}

{transactions.map(({ hash, status }) => {
const transactionDetailsBodyProps: TransactionDetailsBodyPropsType = {
className,
hash,
status,
status: TransactionServerStatusesEnum[status],
isTimedOut
};

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
import React from 'react';
import {
ServerTransactionType,
SignedTransactionType,
TransactionBatchStatusesEnum
} from 'types';
import { TransactionToast } from '../TransactionToast';
import { IconToast, SimpleToast, CustomComponentToast } from './components';
import { CustomToastPropsType } from './customToast.types';
import { useRemoveCustomToast } from './helpers';
Expand All @@ -11,6 +17,22 @@ export const CustomToast = (props: CustomToastPropsType) => {
return <CustomComponentToast {...props} />;
}

if (props.transaction) {
const serverTransaction = props.transaction as ServerTransactionType;
const signedTransaction =
props.transaction as unknown as SignedTransactionType;

const transactionHash = serverTransaction.txHash || signedTransaction.hash;

return (
<TransactionToast
{...props}
status={TransactionBatchStatusesEnum[props.transaction.status]}
transactions={[{ ...signedTransaction, hash: transactionHash }]}
/>
);
}

if (props.icon) {
return <IconToast {...props} />;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,16 @@ type SharedCustomToastPropsType = WithClassnameType & {

export type MessageCustomToastPropsType = SharedCustomToastPropsType &
MessageCustomToastType;

export type MessageIconToastPropsType = SharedCustomToastPropsType &
MessageIconToastType;

export type TransactionIconToastPropsType = SharedCustomToastPropsType &
TransactionIconToastType;

export type ComponentIconToastPropsType = SharedCustomToastPropsType &
ComponentIconToastType;

export type CustomToastPropsType =
| MessageCustomToastPropsType
| MessageIconToastPropsType
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,17 @@ export interface TransactionToastPropsType
}

const TransactionToastComponent = ({
toastId,
title = '',
className = 'dapp-transaction-toast',
onDelete,
startTimestamp,
customization,
endTimeProgress,
lifetimeAfterSuccess,
onDelete,
startTimestamp,
status,
transactions,
customization,
styles
styles,
title = '',
toastId,
transactions
}: TransactionToastPropsType & WithStylesImportType) => {
const {
isCrossShard,
Expand Down Expand Up @@ -62,14 +62,14 @@ const TransactionToastComponent = ({
isCrossShard={isCrossShard}
>
<TransactionToastContentComponent
customElements={customization?.TransactionToastContentCustomElements}
isTimedOut={isTimedOut}
onDeleteToast={handleDeleteToast}
showCloseButton={!isPending}
style={styles ?? {}}
toastDataState={toastDataState}
transactions={transactions ?? []}
toastTitle={title}
isTimedOut={isTimedOut}
showCloseButton={!isPending}
onDeleteToast={handleDeleteToast}
customElements={customization?.TransactionToastContentCustomElements}
transactions={transactions ?? []}
/>
</ProgressComponent>
</TransactionToastWrapper>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { useEffect, useMemo, useRef } from 'react';
import { AVERAGE_TX_DURATION_MS, CROSS_SHARD_ROUNDS } from 'constants/index';
import { useStyles } from 'hocs/useStyles';
import { useGetTransactionDisplayInfo } from 'hooks';
import { useGetAccount, useGetTransactionDisplayInfo } from 'hooks';
import { useSelector } from 'reduxStore/DappProviderContext';
import { shardSelector } from 'reduxStore/selectors';
import { getUnixTimestamp } from 'utils/dateTime/getUnixTimestamp';
Expand Down Expand Up @@ -37,6 +37,7 @@ export const useTransactionToast = ({

const transactionDisplayInfo = useGetTransactionDisplayInfo(toastId);
const accountShard = useSelector(shardSelector);
const { address } = useGetAccount();
const timeoutRef = useRef<NodeJS.Timeout>();
const areSameShardTransactions = useMemo(
() => getAreTransactionsOnSameShard(transactions, accountShard),
Expand Down Expand Up @@ -71,10 +72,12 @@ export const useTransactionToast = ({
const isCompleted = isFailed || isSuccess || isTimedOut;

const toastDataState = getToastDataStateByStatus({
address,
classes: styles ?? {},
sender: transactions?.[0].sender || address,
status,
toastId,
transactionDisplayInfo,
classes: styles ?? {}
transactionDisplayInfo
});

const handleDeleteToast = () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
import { FontAwesomeIconProps } from '@fortawesome/react-fontawesome';
import { SignedTransactionType, TransactionBatchStatusesEnum } from 'types';
import {
SignedTransactionType,
TransactionBatchStatusesEnum,
TransactionServerStatusesEnum
} from 'types';
import { ProgressProps } from 'UI/Progress';
import { TransactionDetailsType } from 'UI/TransactionDetails';
import { ComponentTypeWithChildren } from '../types';
import { TransactionToastContentProps } from './components/TransactionToastContent';
import { TransactionToastContentProps } from './components';

export interface TransactionToastDefaultProps {
toastId: string;
transactions?: SignedTransactionType[];
status?: TransactionBatchStatusesEnum;
status?: TransactionBatchStatusesEnum | TransactionServerStatusesEnum;
classes?: Record<string, string>;
lifetimeAfterSuccess?: number;
endTimeProgress?: number;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ import {
import {
TransactionBatchStatusesEnum,
TransactionsDefaultTitles,
TransactionsDisplayInfoType
TransactionsDisplayInfoType,
TransactionServerStatusesEnum
} from 'types';

export interface ToastDataState {
Expand All @@ -21,23 +22,27 @@ export interface ToastDataState {
}

interface GetToastsOptionsDataPropsType {
status?: TransactionBatchStatusesEnum;
toastId: string;
address: string;
classes?: Record<
'success' | 'warning' | 'danger' | string,
'success' | 'warning' | 'danger' | string
>;
sender: string;
status?: TransactionBatchStatusesEnum | TransactionServerStatusesEnum;
toastId: string;
transactionDisplayInfo: TransactionsDisplayInfoType;
}

export const getToastDataStateByStatus = ({
status,
toastId,
address,
classes = {
success: 'success',
danger: 'danger',
warning: 'warning'
},
sender,
status,
toastId,
transactionDisplayInfo
}: GetToastsOptionsDataPropsType) => {
const successToastData: ToastDataState = {
Expand All @@ -51,6 +56,15 @@ export const getToastDataStateByStatus = ({
iconClassName: classes.success
};

const receivedToastData: ToastDataState = {
id: toastId,
icon: faCheck,
expires: 30000,
hasCloseButton: true,
title: TransactionsDefaultTitles.received,
iconClassName: classes.success
};

const pendingToastData: ToastDataState = {
id: toastId,
expires: false,
Expand Down Expand Up @@ -96,7 +110,7 @@ export const getToastDataStateByStatus = ({
case TransactionBatchStatusesEnum.sent:
return pendingToastData;
case TransactionBatchStatusesEnum.success:
return successToastData;
return sender !== address ? receivedToastData : successToastData;
case TransactionBatchStatusesEnum.cancelled:
case TransactionBatchStatusesEnum.fail:
return failToastData;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export const TransactionToastGuard = ({

const invalidCurrentTx =
currentTx?.transactions == null || currentTx?.status == null;

if (invalidCurrentTx) {
return null;
}
Expand Down
2 changes: 2 additions & 0 deletions src/hooks/login/useLoginService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ export const useLoginService = (config?: OnProviderLoginType['nativeAuth']) => {
...(apiAddress ? { nativeAuthConfig: configuration } : {})
})
);

return nativeAuthToken;
};

Expand All @@ -119,6 +120,7 @@ export const useLoginService = (config?: OnProviderLoginType['nativeAuth']) => {
});

tokenRef.current = loginToken;

if (!loginToken) {
return;
}
Expand Down
11 changes: 10 additions & 1 deletion src/hooks/transactions/useSignMultipleTransactions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,7 @@ export const useSignMultipleTransactions = ({
setWaitingForDevice(isLedger);

let signedTx: Nullable<Transaction | undefined>;

try {
signedTx = await onSignTransaction(currentTransaction.transaction);
} catch (err) {
Expand All @@ -205,6 +206,7 @@ export const useSignMultipleTransactions = ({
const newSignedTransactions = signedTransactions
? { ...signedTransactions, ...newSignedTx }
: newSignedTx;

setSignedTransactions(newSignedTransactions);

if (!isLastTransaction) {
Expand Down Expand Up @@ -237,6 +239,7 @@ export const useSignMultipleTransactions = ({
if (currentTransaction == null) {
return;
}

const signature = currentTransaction.transaction.getSignature();

if (signature.toString('hex') && !isLastTransaction) {
Expand All @@ -245,7 +248,8 @@ export const useSignMultipleTransactions = ({
}

await sign();
} catch {
} catch (e) {
console.error('Error during signing transaction');
// the only way to check if tx has signature is with try catch
await sign();
}
Expand Down Expand Up @@ -274,25 +278,30 @@ export const useSignMultipleTransactions = ({
setCurrentStep((exising) => exising + 1);
return;
}

await signTx();
};

const onNext = () => {
setCurrentStep((current) => {
const nextStep = current + 1;

if (nextStep > allTransactions?.length) {
return current;
}

return nextStep;
});
};

const onPrev = () => {
setCurrentStep((current) => {
const nextStep = current - 1;

if (nextStep < 0) {
return current;
}

return nextStep;
});
};
Expand Down
7 changes: 6 additions & 1 deletion src/hooks/transactions/useSignTransactionsWithDevice.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,12 @@ export function useSignTransactionsWithDevice(
if (!transaction) {
return null;
}
return await connectedProvider.signTransaction(transaction);

const signedTransaction = await connectedProvider.signTransaction(
transaction
);

return signedTransaction;
}

const signMultipleTxReturnValues = useSignMultipleTransactions({
Expand Down
Loading

0 comments on commit f5e9d9d

Please sign in to comment.