chore: new designs
This commit is contained in:
@@ -3,6 +3,7 @@ import { useAssetsDataProvider } from '@vegaprotocol/assets';
|
||||
import { ETHERSCAN_TX, useEtherscanLink } from '@vegaprotocol/environment';
|
||||
import { formatNumber, t, toBigNum } from '@vegaprotocol/react-helpers';
|
||||
import type { Toast, ToastContent } from '@vegaprotocol/ui-toolkit';
|
||||
import { Panel } from '@vegaprotocol/ui-toolkit';
|
||||
import { CLOSE_AFTER } from '@vegaprotocol/ui-toolkit';
|
||||
import { useToasts } from '@vegaprotocol/ui-toolkit';
|
||||
import { ExternalLink, Intent, ProgressBar } from '@vegaprotocol/ui-toolkit';
|
||||
@@ -46,34 +47,35 @@ const EthTransactionDetails = ({ tx }: { tx: EthStoredTxState }) => {
|
||||
if (isWithdraw) label = t('Withdraw');
|
||||
if (isDeposit) label = t('Deposit');
|
||||
assetInfo = (
|
||||
<div className="mt-[5px]">
|
||||
<span className="font-mono text-xs p-1 bg-gray-100 rounded">
|
||||
{label}{' '}
|
||||
{formatNumber(toBigNum(tx.args[1], asset.decimals), asset.decimals)}{' '}
|
||||
{asset.symbol}
|
||||
</span>
|
||||
</div>
|
||||
<b>
|
||||
{label}{' '}
|
||||
{formatNumber(toBigNum(tx.args[1], asset.decimals), asset.decimals)}{' '}
|
||||
{asset.symbol}
|
||||
</b>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{assetInfo}
|
||||
{tx.status === EthTxStatus.Pending && (
|
||||
<div className="mt-[10px]">
|
||||
<span className="font-mono text-xs">
|
||||
{t('Awaiting confirmations')}{' '}
|
||||
{`(${tx.confirmations}/${tx.requiredConfirmations})`}
|
||||
</span>
|
||||
<ProgressBar
|
||||
value={(tx.confirmations / tx.requiredConfirmations) * 100}
|
||||
intent={Intent.Warning}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
if (assetInfo || tx.requiresConfirmation) {
|
||||
return (
|
||||
<Panel>
|
||||
{assetInfo}
|
||||
{tx.status === EthTxStatus.Pending && (
|
||||
<>
|
||||
<p className="mt-[2px]">
|
||||
{t('Awaiting confirmations')}{' '}
|
||||
{`(${tx.confirmations}/${tx.requiredConfirmations})`}
|
||||
</p>
|
||||
<ProgressBar
|
||||
value={(tx.confirmations / tx.requiredConfirmations) * 100}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</Panel>
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
type EthTxToastContentProps = {
|
||||
@@ -82,26 +84,26 @@ type EthTxToastContentProps = {
|
||||
|
||||
const EthTxRequestedToastContent = ({ tx }: EthTxToastContentProps) => {
|
||||
return (
|
||||
<div>
|
||||
<h3 className="font-bold">{t('Action required')}</h3>
|
||||
<>
|
||||
<h3>{t('Action required')}</h3>
|
||||
<p>
|
||||
{t(
|
||||
'Please go to your wallet application and approve or reject the transaction.'
|
||||
)}
|
||||
</p>
|
||||
<EthTransactionDetails tx={tx} />
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const EthTxPendingToastContent = ({ tx }: EthTxToastContentProps) => {
|
||||
return (
|
||||
<div>
|
||||
<h3 className="font-bold">{t('Awaiting confirmation')}</h3>
|
||||
<>
|
||||
<h3>{t('Awaiting confirmation')}</h3>
|
||||
<p>{t('Please wait for your transaction to be confirmed.')}</p>
|
||||
<EtherscanLink tx={tx} />
|
||||
<EthTransactionDetails tx={tx} />
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -114,11 +116,11 @@ const EthTxErrorToastContent = ({ tx }: EthTxToastContentProps) => {
|
||||
errorMessage = tx.error.message;
|
||||
}
|
||||
return (
|
||||
<div>
|
||||
<h3 className="font-bold">{t('Error occurred')}</h3>
|
||||
<p>{errorMessage}</p>
|
||||
<>
|
||||
<h3>{t('Error occurred')}</h3>
|
||||
<p className="first-letter:uppercase">{errorMessage}</p>
|
||||
<EthTransactionDetails tx={tx} />
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -138,20 +140,20 @@ const EtherscanLink = ({ tx }: EthTxToastContentProps) => {
|
||||
|
||||
const EthTxConfirmedToastContent = ({ tx }: EthTxToastContentProps) => {
|
||||
return (
|
||||
<div>
|
||||
<h3 className="font-bold">{t('Transaction confirmed')}</h3>
|
||||
<>
|
||||
<h3>{t('Transaction confirmed')}</h3>
|
||||
<p>{t('Your transaction has been confirmed.')}</p>
|
||||
<EtherscanLink tx={tx} />
|
||||
<EthTransactionDetails tx={tx} />
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const EthTxCompletedToastContent = ({ tx }: EthTxToastContentProps) => {
|
||||
const isDeposit = isDepositTransaction(tx);
|
||||
return (
|
||||
<div>
|
||||
<h3 className="font-bold">
|
||||
<>
|
||||
<h3>
|
||||
{t('Processing')} {isDeposit && t('deposit')}
|
||||
</h3>
|
||||
<p>
|
||||
@@ -160,7 +162,7 @@ const EthTxCompletedToastContent = ({ tx }: EthTxToastContentProps) => {
|
||||
</p>
|
||||
<EtherscanLink tx={tx} />
|
||||
<EthTransactionDetails tx={tx} />
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { formatNumber, t, toBigNum } from '@vegaprotocol/react-helpers';
|
||||
import type { Toast } from '@vegaprotocol/ui-toolkit';
|
||||
import { Panel } from '@vegaprotocol/ui-toolkit';
|
||||
import { CLOSE_AFTER } from '@vegaprotocol/ui-toolkit';
|
||||
import { useToasts } from '@vegaprotocol/ui-toolkit';
|
||||
import { Intent } from '@vegaprotocol/ui-toolkit';
|
||||
@@ -32,23 +33,26 @@ const EthWithdrawalApprovalToastContent = ({
|
||||
if (tx.status === ApprovalStatus.Delayed) {
|
||||
title = t('Delayed');
|
||||
}
|
||||
if (tx.status === ApprovalStatus.Ready) {
|
||||
title = t('Approved');
|
||||
}
|
||||
const num = formatNumber(
|
||||
toBigNum(tx.withdrawal.amount, tx.withdrawal.asset.decimals),
|
||||
tx.withdrawal.asset.decimals
|
||||
);
|
||||
const details = (
|
||||
<div className="mt-[5px]">
|
||||
<span className="font-mono text-xs p-1 bg-gray-100 rounded">
|
||||
<Panel>
|
||||
<b>
|
||||
{t('Withdraw')} {num} {tx.withdrawal.asset.symbol}
|
||||
</span>
|
||||
</div>
|
||||
</b>
|
||||
</Panel>
|
||||
);
|
||||
return (
|
||||
<div>
|
||||
<>
|
||||
{title.length > 0 && <h3 className="font-bold">{title}</h3>}
|
||||
<VerificationStatus state={tx} />
|
||||
{details}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -60,21 +64,28 @@ export const useEthereumWithdrawApprovalsToasts = () => {
|
||||
state.setToast,
|
||||
state.remove,
|
||||
]);
|
||||
const dismissTx = useEthWithdrawApprovalsStore((state) => state.dismiss);
|
||||
const [dismissTx, deleteTx] = useEthWithdrawApprovalsStore((state) => [
|
||||
state.dismiss,
|
||||
state.delete,
|
||||
]);
|
||||
|
||||
const fromWithdrawalApproval = useCallback(
|
||||
(tx: EthWithdrawalApprovalState): Toast => ({
|
||||
id: `withdrawal-${tx.id}`,
|
||||
intent: intentMap[tx.status],
|
||||
onClose: () => {
|
||||
dismissTx(tx.id);
|
||||
if ([ApprovalStatus.Error, ApprovalStatus.Ready].includes(tx.status)) {
|
||||
deleteTx(tx.id);
|
||||
} else {
|
||||
dismissTx(tx.id);
|
||||
}
|
||||
remove(`withdrawal-${tx.id}`);
|
||||
},
|
||||
loader: tx.status === ApprovalStatus.Pending,
|
||||
content: <EthWithdrawalApprovalToastContent tx={tx} />,
|
||||
closeAfter: isFinal(tx) ? CLOSE_AFTER : undefined,
|
||||
}),
|
||||
[dismissTx, remove]
|
||||
[deleteTx, dismissTx, remove]
|
||||
);
|
||||
|
||||
useEthWithdrawApprovalsStore.subscribe(
|
||||
|
||||
@@ -260,7 +260,7 @@ describe('VegaTransactionDetails', () => {
|
||||
const { queryByTestId } = render(
|
||||
<VegaTransactionDetails tx={unsupportedTransaction} />
|
||||
);
|
||||
expect(queryByTestId('vega-tx-details')).toBeNull();
|
||||
expect(queryByTestId('toast-panel')).toBeNull();
|
||||
});
|
||||
it.each([
|
||||
{ tx: withdraw, details: 'Withdraw 12.34 $A' },
|
||||
@@ -275,6 +275,6 @@ describe('VegaTransactionDetails', () => {
|
||||
{ tx: batch, details: 'Batch market instruction' },
|
||||
])('display details for transaction', ({ tx, details }) => {
|
||||
const { queryByTestId } = render(<VegaTransactionDetails tx={tx} />);
|
||||
expect(queryByTestId('vega-tx-details')?.textContent).toEqual(details);
|
||||
expect(queryByTestId('toast-panel')?.textContent).toEqual(details);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import type { ReactNode } from 'react';
|
||||
import { useCallback } from 'react';
|
||||
import first from 'lodash/first';
|
||||
import compact from 'lodash/compact';
|
||||
@@ -25,6 +24,7 @@ import {
|
||||
VegaTxStatus,
|
||||
} from '@vegaprotocol/wallet';
|
||||
import type { Toast, ToastContent } from '@vegaprotocol/ui-toolkit';
|
||||
import { Panel } from '@vegaprotocol/ui-toolkit';
|
||||
import { CLOSE_AFTER } from '@vegaprotocol/ui-toolkit';
|
||||
import { useToasts } from '@vegaprotocol/ui-toolkit';
|
||||
import { Button, ExternalLink, Intent } from '@vegaprotocol/ui-toolkit';
|
||||
@@ -42,6 +42,7 @@ import { DApp, EXPLORER_TX, useLinks } from '@vegaprotocol/environment';
|
||||
import { getRejectionReason, useOrderByIdQuery } from '@vegaprotocol/orders';
|
||||
import { useMarketList } from '@vegaprotocol/market-list';
|
||||
import type { Side } from '@vegaprotocol/types';
|
||||
import { OrderRejectionReasonMapping, OrderStatus } from '@vegaprotocol/types';
|
||||
import { OrderStatusMapping } from '@vegaprotocol/types';
|
||||
|
||||
const intentMap: { [s in VegaTxStatus]: Intent } = {
|
||||
@@ -100,20 +101,6 @@ const isTransactionTypeSupported = (tx: VegaStoredTxState) => {
|
||||
);
|
||||
};
|
||||
|
||||
const Details = ({
|
||||
children,
|
||||
title = '',
|
||||
}: {
|
||||
children: ReactNode;
|
||||
title?: string;
|
||||
}) => (
|
||||
<div className="pt-[5px]" data-testid="vega-tx-details" title={title}>
|
||||
<div className="font-mono text-xs p-2 bg-neutral-100 rounded dark:bg-neutral-700 dark:text-white">
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
type SizeAtPriceProps = {
|
||||
side: Side;
|
||||
size: string;
|
||||
@@ -154,8 +141,8 @@ const SubmitOrderDetails = ({
|
||||
const side = order ? order.side : data.side;
|
||||
|
||||
return (
|
||||
<Details>
|
||||
<h4 className="font-bold">
|
||||
<Panel>
|
||||
<h4>
|
||||
{order
|
||||
? t(
|
||||
`Submit order - ${OrderStatusMapping[order.status].toLowerCase()}`
|
||||
@@ -177,10 +164,7 @@ const SubmitOrderDetails = ({
|
||||
price={price}
|
||||
/>
|
||||
</p>
|
||||
{order && order.rejectionReason && (
|
||||
<p className="italic">{getRejectionReason(order)}</p>
|
||||
)}
|
||||
</Details>
|
||||
</Panel>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -196,7 +180,7 @@ const EditOrderDetails = ({
|
||||
});
|
||||
const { data: markets } = useMarketList();
|
||||
|
||||
const originalOrder = orderById?.orderByID;
|
||||
const originalOrder = order || orderById?.orderByID;
|
||||
if (!originalOrder) return null;
|
||||
const market = markets?.find((m) => m.id === originalOrder.market.id);
|
||||
if (!market) return null;
|
||||
@@ -230,8 +214,8 @@ const EditOrderDetails = ({
|
||||
);
|
||||
|
||||
return (
|
||||
<Details title={data.orderId}>
|
||||
<h4 className="font-bold">
|
||||
<Panel title={data.orderId}>
|
||||
<h4>
|
||||
{order
|
||||
? t(`Edit order - ${OrderStatusMapping[order.status].toLowerCase()}`)
|
||||
: t('Edit order')}
|
||||
@@ -241,10 +225,7 @@ const EditOrderDetails = ({
|
||||
<s>{original}</s>
|
||||
</p>
|
||||
<p>{edited}</p>
|
||||
{order && order.rejectionReason && (
|
||||
<p className="italic">{getRejectionReason(order)}</p>
|
||||
)}
|
||||
</Details>
|
||||
</Panel>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -279,8 +260,8 @@ const CancelOrderDetails = ({
|
||||
/>
|
||||
);
|
||||
return (
|
||||
<Details title={orderId}>
|
||||
<h4 className="font-bold">
|
||||
<Panel title={orderId}>
|
||||
<h4>
|
||||
{order
|
||||
? t(
|
||||
`Cancel order - ${OrderStatusMapping[order.status].toLowerCase()}`
|
||||
@@ -291,10 +272,7 @@ const CancelOrderDetails = ({
|
||||
<p>
|
||||
<s>{original}</s>
|
||||
</p>
|
||||
{order && order.rejectionReason && (
|
||||
<p className="italic">{getRejectionReason(order)}</p>
|
||||
)}
|
||||
</Details>
|
||||
</Panel>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -313,9 +291,11 @@ export const VegaTransactionDetails = ({ tx }: { tx: VegaStoredTxState }) => {
|
||||
asset.decimals
|
||||
);
|
||||
return (
|
||||
<Details>
|
||||
{t('Withdraw')} {num} {asset.symbol}
|
||||
</Details>
|
||||
<Panel>
|
||||
<b>
|
||||
{t('Withdraw')} {num} {asset.symbol}
|
||||
</b>
|
||||
</Panel>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -332,7 +312,7 @@ export const VegaTransactionDetails = ({ tx }: { tx: VegaStoredTxState }) => {
|
||||
tx.body.orderCancellation.marketId === undefined &&
|
||||
tx.body.orderCancellation.orderId === undefined
|
||||
) {
|
||||
return <Details>{t('Cancel all orders')}</Details>;
|
||||
return <Panel>{t('Cancel all orders')}</Panel>;
|
||||
}
|
||||
|
||||
// CANCEL
|
||||
@@ -355,11 +335,15 @@ export const VegaTransactionDetails = ({ tx }: { tx: VegaStoredTxState }) => {
|
||||
m.id === (tx.body as OrderCancellationBody).orderCancellation.marketId
|
||||
)?.tradableInstrument.instrument.code;
|
||||
return (
|
||||
<Details>
|
||||
{marketName
|
||||
? `${t('Cancel all orders for')} ${marketName}`
|
||||
: t('Cancel all orders')}
|
||||
</Details>
|
||||
<Panel>
|
||||
{marketName ? (
|
||||
<>
|
||||
{t('Cancel all orders for')} <b>{marketName}</b>
|
||||
</>
|
||||
) : (
|
||||
t('Cancel all orders')
|
||||
)}
|
||||
</Panel>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -381,15 +365,16 @@ export const VegaTransactionDetails = ({ tx }: { tx: VegaStoredTxState }) => {
|
||||
const market = marketId && markets?.find((m) => m.id === marketId);
|
||||
if (market) {
|
||||
return (
|
||||
<Details>
|
||||
{t('Close position for')} {market.tradableInstrument.instrument.code}
|
||||
</Details>
|
||||
<Panel>
|
||||
{t('Close position for')}{' '}
|
||||
<b>{market.tradableInstrument.instrument.code}</b>
|
||||
</Panel>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (isBatchMarketInstructionsTransaction(tx.body)) {
|
||||
return <Details>{t('Batch market instruction')}</Details>;
|
||||
return <Panel>{t('Batch market instruction')}</Panel>;
|
||||
}
|
||||
|
||||
if (isTransferTransaction(tx.body)) {
|
||||
@@ -418,22 +403,22 @@ export const VegaTransactionDetails = ({ tx }: { tx: VegaStoredTxState }) => {
|
||||
type VegaTxToastContentProps = { tx: VegaStoredTxState };
|
||||
|
||||
const VegaTxRequestedToastContent = ({ tx }: VegaTxToastContentProps) => (
|
||||
<div>
|
||||
<h3 className="font-bold">{t('Action required')}</h3>
|
||||
<>
|
||||
<h3>{t('Action required')}</h3>
|
||||
<p>
|
||||
{t(
|
||||
'Please go to your Vega wallet application and approve or reject the transaction.'
|
||||
)}
|
||||
</p>
|
||||
<VegaTransactionDetails tx={tx} />
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
||||
const VegaTxPendingToastContentProps = ({ tx }: VegaTxToastContentProps) => {
|
||||
const explorerLink = useLinks(DApp.Explorer);
|
||||
return (
|
||||
<div>
|
||||
<h3 className="font-bold">{t('Awaiting confirmation')}</h3>
|
||||
<>
|
||||
<h3>{t('Awaiting confirmation')}</h3>
|
||||
<p>{t('Please wait for your transaction to be confirmed')}</p>
|
||||
{tx.txHash && (
|
||||
<p className="break-all">
|
||||
@@ -446,7 +431,7 @@ const VegaTxPendingToastContentProps = ({ tx }: VegaTxToastContentProps) => {
|
||||
</p>
|
||||
)}
|
||||
<VegaTransactionDetails tx={tx} />
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -460,7 +445,7 @@ const VegaTxCompleteToastsContent = ({ tx }: VegaTxToastContentProps) => {
|
||||
|
||||
if (isWithdrawTransaction(tx.body)) {
|
||||
const completeWithdrawalButton = tx.withdrawal && (
|
||||
<div className="mt-[10px]">
|
||||
<p className="mt-1">
|
||||
<Button
|
||||
data-testid="toast-complete-withdrawal"
|
||||
size="xs"
|
||||
@@ -473,11 +458,11 @@ const VegaTxCompleteToastsContent = ({ tx }: VegaTxToastContentProps) => {
|
||||
>
|
||||
{t('Complete withdrawal')}
|
||||
</Button>
|
||||
</div>
|
||||
</p>
|
||||
);
|
||||
return (
|
||||
<div>
|
||||
<h3 className="font-bold">{t('Funds unlocked')}</h3>
|
||||
<>
|
||||
<h3>{t('Funds unlocked')}</h3>
|
||||
<p>{t('Your funds have been unlocked for withdrawal')}</p>
|
||||
{tx.txHash && (
|
||||
<p className="break-all">
|
||||
@@ -491,7 +476,32 @@ const VegaTxCompleteToastsContent = ({ tx }: VegaTxToastContentProps) => {
|
||||
)}
|
||||
<VegaTransactionDetails tx={tx} />
|
||||
{completeWithdrawalButton}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
if (tx.order && tx.order.rejectionReason) {
|
||||
return (
|
||||
<>
|
||||
<h3>{t('Rejected')}</h3>
|
||||
<p>
|
||||
{t(
|
||||
'Your order has been rejected because: %s',
|
||||
OrderRejectionReasonMapping[tx.order.rejectionReason]
|
||||
)}
|
||||
</p>
|
||||
{tx.txHash && (
|
||||
<p className="break-all">
|
||||
<ExternalLink
|
||||
href={explorerLink(EXPLORER_TX.replace(':hash', tx.txHash))}
|
||||
rel="noreferrer"
|
||||
>
|
||||
{t('View in block explorer')}
|
||||
</ExternalLink>
|
||||
</p>
|
||||
)}
|
||||
<VegaTransactionDetails tx={tx} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -526,8 +536,8 @@ const VegaTxCompleteToastsContent = ({ tx }: VegaTxToastContentProps) => {
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h3 className="font-bold">{t('Confirmed')}</h3>
|
||||
<>
|
||||
<h3>{t('Confirmed')}</h3>
|
||||
<p>{t('Your transaction has been confirmed ')}</p>
|
||||
{tx.txHash && (
|
||||
<p className="break-all">
|
||||
@@ -540,7 +550,7 @@ const VegaTxCompleteToastsContent = ({ tx }: VegaTxToastContentProps) => {
|
||||
</p>
|
||||
)}
|
||||
<VegaTransactionDetails tx={tx} />
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -571,16 +581,16 @@ const VegaTxErrorToastContent = ({ tx }: VegaTxToastContentProps) => {
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h3 className="font-bold">{label}</h3>
|
||||
<p>{errorMessage}</p>
|
||||
<>
|
||||
<h3>{label}</h3>
|
||||
<p className="first-letter:uppercase">{errorMessage}</p>
|
||||
{walletError && (
|
||||
<Button size="xs" onClick={reconnectVegaWallet}>
|
||||
{t('Connect vega wallet')}
|
||||
</Button>
|
||||
)}
|
||||
<VegaTransactionDetails tx={tx} />
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -626,9 +636,15 @@ export const useVegaTransactionToasts = () => {
|
||||
if (tx.status === VegaTxStatus.Error) {
|
||||
content = <VegaTxErrorToastContent tx={tx} />;
|
||||
}
|
||||
|
||||
const intent =
|
||||
tx.order && [OrderStatus.STATUS_REJECTED].includes(tx.order.status)
|
||||
? Intent.Danger
|
||||
: intentMap[tx.status];
|
||||
|
||||
return {
|
||||
id: `vega-${tx.id}`,
|
||||
intent: intentMap[tx.status],
|
||||
intent,
|
||||
onClose: onClose(tx),
|
||||
loader: tx.status === VegaTxStatus.Pending,
|
||||
content,
|
||||
|
||||
@@ -174,16 +174,16 @@ module.exports = {
|
||||
'60%': { transform: 'rotate( 0.0deg)' },
|
||||
'100%': { transform: 'rotate( 0.0deg)' },
|
||||
},
|
||||
'vertical-progress': {
|
||||
from: { height: '0' },
|
||||
to: { height: '100%' },
|
||||
'progress': {
|
||||
from: { width: '0' },
|
||||
to: { width: '100%' },
|
||||
},
|
||||
},
|
||||
animation: {
|
||||
rotate: 'rotate 2s linear alternate infinite',
|
||||
'rotate-back': 'rotate 2s linear reverse infinite',
|
||||
wave: 'wave 2s linear infinite',
|
||||
'vertical-progress': 'vertical-progress 5s linear 1',
|
||||
'progress': 'progress 5s cubic-bezier(.39,.58,.57,1) 1',
|
||||
},
|
||||
data: {
|
||||
selected: 'state~="checked"',
|
||||
|
||||
@@ -11,6 +11,7 @@ interface ProgressBarProps {
|
||||
export const ProgressBar = ({ className, intent, value }: ProgressBarProps) => {
|
||||
return (
|
||||
<div
|
||||
data-progress-bar
|
||||
style={{ height: '6px' }}
|
||||
className={classNames(
|
||||
'bg-neutral-300 dark:bg-neutral-700 relative',
|
||||
@@ -18,6 +19,7 @@ export const ProgressBar = ({ className, intent, value }: ProgressBarProps) => {
|
||||
)}
|
||||
>
|
||||
<div
|
||||
data-progress-bar-value
|
||||
className={classNames(
|
||||
'absolute left-0 top-0 bottom-0',
|
||||
intent === undefined || intent === Intent.None
|
||||
|
||||
@@ -1,21 +1,17 @@
|
||||
.initial {
|
||||
top: 20px;
|
||||
opacity: 0;
|
||||
max-height: 0;
|
||||
border: 0;
|
||||
transition: all 0.3s;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.showing {
|
||||
right: 0;
|
||||
opacity: 1;
|
||||
transition: all 0.3s;
|
||||
max-height: 100vw;
|
||||
}
|
||||
|
||||
.expired {
|
||||
right: -375px;
|
||||
opacity: 0;
|
||||
transition: all 0.5s;
|
||||
max-height: 0;
|
||||
transition: all 0.75s;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
/* eslint-disable jsx-a11y/accessible-emoji */
|
||||
import { Toast } from './toast';
|
||||
import { Panel, Toast } from './toast';
|
||||
import type { ComponentStory, ComponentMeta } from '@storybook/react';
|
||||
import { Intent } from '../../utils/intent';
|
||||
import { ExternalLink } from '../link';
|
||||
import { ProgressBar } from '../progress-bar';
|
||||
|
||||
export default {
|
||||
title: 'Toast',
|
||||
@@ -9,14 +11,7 @@ export default {
|
||||
} as ComponentMeta<typeof Toast>;
|
||||
|
||||
const Template: ComponentStory<typeof Toast> = (args) => {
|
||||
const toastContent = (
|
||||
<>
|
||||
<p>Lorem ipsum dolor sit amet consectetur adipisicing elit.</p>
|
||||
<p>Eaque exercitationem saepe cupiditate sunt impedit.</p>
|
||||
<p>I really like 🥪🥪🥪!</p>
|
||||
</>
|
||||
);
|
||||
return <Toast {...args} content={toastContent} />;
|
||||
return <Toast {...args} />;
|
||||
};
|
||||
|
||||
export const Default = Template.bind({});
|
||||
@@ -24,6 +19,16 @@ Default.args = {
|
||||
id: 'def',
|
||||
intent: Intent.None,
|
||||
state: 'showing',
|
||||
content: (
|
||||
<>
|
||||
<h3>Optional heading</h3>
|
||||
<p>This is a message that can return over multiple lines.</p>
|
||||
<p>
|
||||
<ExternalLink>Optional link</ExternalLink>
|
||||
</p>
|
||||
</>
|
||||
),
|
||||
onClose: () => undefined,
|
||||
};
|
||||
|
||||
export const Primary = Template.bind({});
|
||||
@@ -31,6 +36,17 @@ Primary.args = {
|
||||
id: 'pri',
|
||||
intent: Intent.Primary,
|
||||
state: 'showing',
|
||||
content: (
|
||||
<>
|
||||
<h3>Optional heading</h3>
|
||||
<p>This is a message that can return over multiple lines.</p>
|
||||
<p>
|
||||
<ExternalLink>Optional link</ExternalLink>
|
||||
</p>
|
||||
<Panel>Lorem ipsum dolor sit amet consectetur adipisicing elit</Panel>
|
||||
</>
|
||||
),
|
||||
onClose: () => undefined,
|
||||
};
|
||||
|
||||
export const Danger = Template.bind({});
|
||||
@@ -38,6 +54,17 @@ Danger.args = {
|
||||
id: 'dan',
|
||||
intent: Intent.Danger,
|
||||
state: 'showing',
|
||||
content: (
|
||||
<>
|
||||
<h3>Optional heading</h3>
|
||||
<p>This is a message that can return over multiple lines.</p>
|
||||
<p>
|
||||
<ExternalLink>Optional link</ExternalLink>
|
||||
</p>
|
||||
<Panel>Lorem ipsum dolor sit amet consectetur adipisicing elit</Panel>
|
||||
</>
|
||||
),
|
||||
onClose: () => undefined,
|
||||
};
|
||||
|
||||
export const Warning = Template.bind({});
|
||||
@@ -45,6 +72,21 @@ Warning.args = {
|
||||
id: 'war',
|
||||
intent: Intent.Warning,
|
||||
state: 'showing',
|
||||
content: (
|
||||
<>
|
||||
<h3>Optional heading</h3>
|
||||
<p>This is a message that can return over multiple lines.</p>
|
||||
<p>
|
||||
<ExternalLink>Optional link</ExternalLink>
|
||||
</p>
|
||||
<Panel>
|
||||
<b>Deposit 10.00 tUSDX</b>
|
||||
<p className="mt-[2px]">Awaiting confirmations (1/3)</p>
|
||||
<ProgressBar value={33.33} />
|
||||
</Panel>
|
||||
</>
|
||||
),
|
||||
onClose: () => undefined,
|
||||
};
|
||||
|
||||
export const Success = Template.bind({});
|
||||
@@ -52,4 +94,15 @@ Success.args = {
|
||||
id: 'suc',
|
||||
intent: Intent.Success,
|
||||
state: 'showing',
|
||||
content: (
|
||||
<>
|
||||
<h3>Optional heading</h3>
|
||||
<p>This is a message that can return over multiple lines.</p>
|
||||
<p>
|
||||
<ExternalLink>Optional link</ExternalLink>
|
||||
</p>
|
||||
<Panel>Lorem ipsum dolor sit amet consectetur adipisicing elit</Panel>
|
||||
</>
|
||||
),
|
||||
onClose: () => undefined,
|
||||
};
|
||||
|
||||
@@ -3,7 +3,8 @@ import styles from './toast.module.css';
|
||||
import type { IconName } from '@blueprintjs/icons';
|
||||
import { IconNames } from '@blueprintjs/icons';
|
||||
import classNames from 'classnames';
|
||||
import { useEffect } from 'react';
|
||||
import type { ForwardedRef, HTMLAttributes, ReactNode } from 'react';
|
||||
import { forwardRef, useEffect } from 'react';
|
||||
import { useCallback } from 'react';
|
||||
import { useLayoutEffect } from 'react';
|
||||
import { useRef } from 'react';
|
||||
@@ -33,23 +34,38 @@ const toastIconMapping: { [i in Intent]: IconName } = {
|
||||
[Intent.None]: IconNames.HELP,
|
||||
[Intent.Primary]: IconNames.INFO_SIGN,
|
||||
[Intent.Success]: IconNames.TICK_CIRCLE,
|
||||
[Intent.Warning]: IconNames.ERROR,
|
||||
[Intent.Warning]: IconNames.WARNING_SIGN,
|
||||
[Intent.Danger]: IconNames.ERROR,
|
||||
};
|
||||
|
||||
const getToastAccent = (intent: Intent) => ({
|
||||
// strip
|
||||
'bg-gray-200 text-black text-opacity-70': intent === Intent.None,
|
||||
'bg-vega-blue text-white text-opacity-70': intent === Intent.Primary,
|
||||
'bg-success text-white text-opacity-70': intent === Intent.Success,
|
||||
'bg-warning text-white text-opacity-70': intent === Intent.Warning,
|
||||
'bg-vega-pink text-white text-opacity-70': intent === Intent.Danger,
|
||||
});
|
||||
|
||||
export const CLOSE_DELAY = 750;
|
||||
export const CLOSE_DELAY = 500;
|
||||
export const TICKER = 100;
|
||||
export const CLOSE_AFTER = 5000;
|
||||
|
||||
export const Panel = forwardRef<
|
||||
HTMLDivElement,
|
||||
{
|
||||
ref: ForwardedRef<HTMLDivElement>;
|
||||
} & HTMLAttributes<HTMLDivElement>
|
||||
>(({ children, ref, className, ...props }) => {
|
||||
return (
|
||||
<div
|
||||
data-panel
|
||||
ref={ref}
|
||||
data-testid="toast-panel"
|
||||
className={classNames(
|
||||
'p-2 rounded mt-[10px]',
|
||||
'font-mono text-[12px] leading-[16px] font-normal',
|
||||
'[&>h4]:font-bold',
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
export const Toast = ({
|
||||
id,
|
||||
intent,
|
||||
@@ -85,7 +101,7 @@ export const Toast = ({
|
||||
}
|
||||
});
|
||||
return () => cancelAnimationFrame(req);
|
||||
}, [id]);
|
||||
}, [id, intent, content]); // DO NOT REMOVE DEPS: intent, content
|
||||
|
||||
useEffect(() => {
|
||||
const i = setInterval(() => {
|
||||
@@ -130,8 +146,54 @@ export const Toast = ({
|
||||
}
|
||||
}}
|
||||
className={classNames(
|
||||
'relative w-[300px] top-0 rounded-md border overflow-hidden mb-2',
|
||||
'text-black bg-white dark:border-zinc-700',
|
||||
'w-[320px] rounded-md overflow-hidden',
|
||||
'shadow-[8px_8px_16px_0_rgba(0,0,0,0.4)]',
|
||||
'text-black dark:text-white',
|
||||
'font-alpha liga-0-calt-0 text-[14px] leading-[19px]',
|
||||
// background
|
||||
{
|
||||
'bg-vega-light-100 dark:bg-vega-dark-100 ': intent === Intent.None,
|
||||
'bg-vega-blue-300 dark:bg-vega-blue-700': intent === Intent.Primary,
|
||||
'bg-vega-green-300 dark:bg-vega-green-700': intent === Intent.Success,
|
||||
'bg-vega-orange-300 dark:bg-vega-orange-700':
|
||||
intent === Intent.Warning,
|
||||
'bg-vega-pink-300 dark:bg-vega-pink-700': intent === Intent.Danger,
|
||||
},
|
||||
// panel's colours
|
||||
{
|
||||
'[&_[data-panel]]:bg-vega-light-150 [&_[data-panel]]:dark:bg-vega-dark-150 ':
|
||||
intent === Intent.None,
|
||||
'[&_[data-panel]]:bg-vega-blue-350 [&_[data-panel]]:dark:bg-vega-blue-650':
|
||||
intent === Intent.Primary,
|
||||
'[&_[data-panel]]:bg-vega-green-350 [&_[data-panel]]:dark:bg-vega-green-650':
|
||||
intent === Intent.Success,
|
||||
'[&_[data-panel]]:bg-vega-orange-350 [&_[data-panel]]:dark:bg-vega-orange-650':
|
||||
intent === Intent.Warning,
|
||||
'[&_[data-panel]]:bg-vega-pink-350 [&_[data-panel]]:dark:bg-vega-pink-650':
|
||||
intent === Intent.Danger,
|
||||
},
|
||||
// panels's progress bar colours
|
||||
'[&_[data-progress-bar]]:mt-[10px] [&_[data-progress-bar]]:mb-[4px]',
|
||||
{
|
||||
'[&_[data-progress-bar]]:bg-vega-light-200 [&_[data-progress-bar]]:dark:bg-vega-dark-200 ':
|
||||
intent === Intent.None,
|
||||
'[&_[data-progress-bar]]:bg-vega-blue-400 [&_[data-progress-bar]]:dark:bg-vega-blue-600':
|
||||
intent === Intent.Primary,
|
||||
'[&_[data-progress-bar-value]]:bg-vega-blue-500 [&_[data-progress-bar-value]]:dark:bg-vega-blue-500':
|
||||
intent === Intent.Primary,
|
||||
'[&_[data-progress-bar]]:bg-vega-green-400 [&_[data-progress-bar]]:dark:bg-vega-green-600':
|
||||
intent === Intent.Success,
|
||||
'[&_[data-progress-bar-value]]:bg-vega-green-600 [&_[data-progress-bar-value]]:dark:bg-vega-green-500':
|
||||
intent === Intent.Success,
|
||||
'[&_[data-progress-bar]]:bg-vega-orange-400 [&_[data-progress-bar]]:dark:bg-vega-orange-600':
|
||||
intent === Intent.Warning,
|
||||
'[&_[data-progress-bar-value]]:bg-vega-orange-500 [&_[data-progress-bar-value]]:dark:bg-vega-orange-500':
|
||||
intent === Intent.Warning,
|
||||
'[&_[data-progress-bar]]:bg-vega-pink-400 [&_[data-progress-bar]]:dark:bg-vega-pink-600':
|
||||
intent === Intent.Danger,
|
||||
'[&_[data-progress-bar-value]]:bg-vega-pink-500 [&_[data-progress-bar-value]]:dark:bg-vega-pink-500':
|
||||
intent === Intent.Danger,
|
||||
},
|
||||
{
|
||||
[styles['initial']]: state === 'initial',
|
||||
[styles['showing']]: state === 'showing',
|
||||
@@ -144,49 +206,83 @@ export const Toast = ({
|
||||
type="button"
|
||||
data-testid="toast-close"
|
||||
onClick={closeToast}
|
||||
className="absolute p-2 top-0 right-0"
|
||||
className="absolute p-[8px] top-[3px] right-[3px] z-20"
|
||||
>
|
||||
<Icon name="cross" size={3} className="!block dark:text-white" />
|
||||
<Icon
|
||||
name="cross"
|
||||
size={3}
|
||||
className="!block dark:text-white !w-[11px] !h-[11px]"
|
||||
/>
|
||||
</button>
|
||||
<div
|
||||
data-testid="toast-accent"
|
||||
className={classNames(
|
||||
getToastAccent(intent),
|
||||
'w-8',
|
||||
'p-2 pt-3 text-center relative'
|
||||
{
|
||||
// gray
|
||||
'bg-vega-light-200 dark:bg-vega-dark-200 text-vega-light-400 dark:text-vega-dark-100':
|
||||
intent === Intent.None,
|
||||
// blue
|
||||
'bg-vega-blue-500 text-vega-blue-600': intent === Intent.Primary,
|
||||
// green
|
||||
'bg-vega-green-500 text-vega-green-600':
|
||||
intent === Intent.Success,
|
||||
// orange
|
||||
'bg-vega-orange-500 text-vega-orange-600':
|
||||
intent === Intent.Warning,
|
||||
// pink
|
||||
'bg-vega-pink-500 text-vega-pink-600': intent === Intent.Danger,
|
||||
},
|
||||
'w-8 p-[9px]',
|
||||
'flex justify-center'
|
||||
)}
|
||||
>
|
||||
{loader ? (
|
||||
<div className="w-[15px] h-[15px]">
|
||||
<Loader size="small" forceTheme="dark" />
|
||||
</div>
|
||||
) : (
|
||||
<Icon
|
||||
name={toastIconMapping[intent]}
|
||||
size={4}
|
||||
className="!block !w-[14px] !h-[14px]"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<div
|
||||
className={classNames(
|
||||
'relative',
|
||||
'overflow-auto flex-1 p-4 pr-[40px] [&>p]:mb-[2.5px]',
|
||||
// toast heading
|
||||
'[&>h3]:text-[14px] [&>h3]:leading-[13px] [&>h3]:uppercase [&>h3]:mb-[8px]'
|
||||
)}
|
||||
data-testid="toast-content"
|
||||
>
|
||||
{content}
|
||||
{withProgress && (
|
||||
<div
|
||||
ref={progressRef}
|
||||
data-testid="toast-progress-bar"
|
||||
className={classNames(
|
||||
'absolute top-0 left-0 w-full h-full',
|
||||
'animate-vertical-progress',
|
||||
'bg-white/30 backdrop-saturate-125'
|
||||
{
|
||||
'bg-vega-light-200 dark:bg-vega-dark-200 ':
|
||||
intent === Intent.None,
|
||||
'bg-vega-blue-400 dark:bg-vega-blue-600':
|
||||
intent === Intent.Primary,
|
||||
'bg-vega-green-400 dark:bg-vega-green-600':
|
||||
intent === Intent.Success,
|
||||
'bg-vega-orange-400 dark:bg-vega-orange-600':
|
||||
intent === Intent.Warning,
|
||||
'bg-vega-pink-400 dark:bg-vega-pink-600':
|
||||
intent === Intent.Danger,
|
||||
},
|
||||
'absolute bottom-0 left-0 w-full h-[4px]',
|
||||
'animate-progress'
|
||||
)}
|
||||
style={{
|
||||
animationDuration: `${closeAfter}ms`,
|
||||
}}
|
||||
></div>
|
||||
)}
|
||||
<div className="absolute">
|
||||
{loader ? (
|
||||
<div className="w-4 h-4">
|
||||
<Loader size="small" forceTheme="dark" />
|
||||
</div>
|
||||
) : (
|
||||
<Icon
|
||||
name={toastIconMapping[intent]}
|
||||
size={4}
|
||||
className="!block"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="flex-1 p-2 pr-6 text-sm overflow-auto dark:bg-black dark:text-white"
|
||||
data-testid="toast-content"
|
||||
>
|
||||
{content}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -84,7 +84,7 @@ const usePrice = create<PriceStore>((set) => ({
|
||||
const Template: ComponentStory<typeof ToastsContainer> = (args) => {
|
||||
const setPrice = usePrice((state) => state.setPrice);
|
||||
|
||||
const { add, close, closeAll, update, remove, toasts } = useToasts(
|
||||
const { add, close, closeAll, update, remove, toasts, setToast } = useToasts(
|
||||
(state) => ({
|
||||
add: state.add,
|
||||
close: state.close,
|
||||
@@ -92,6 +92,7 @@ const Template: ComponentStory<typeof ToastsContainer> = (args) => {
|
||||
update: state.update,
|
||||
remove: state.remove,
|
||||
toasts: state.toasts,
|
||||
setToast: state.setToast,
|
||||
})
|
||||
);
|
||||
|
||||
@@ -196,6 +197,26 @@ const Template: ComponentStory<typeof ToastsContainer> = (args) => {
|
||||
>
|
||||
🧽
|
||||
</button>
|
||||
<button
|
||||
onClick={() => {
|
||||
const toasts = Object.values(useToasts.getState().toasts);
|
||||
if (toasts.length > 0) {
|
||||
const t = toasts[toasts.length - 1];
|
||||
setToast({
|
||||
...t,
|
||||
intent: Intent.Danger,
|
||||
content: (
|
||||
<>
|
||||
<h3>Error occurred</h3>
|
||||
<p>Something went terribly wrong</p>
|
||||
</>
|
||||
),
|
||||
});
|
||||
}
|
||||
}}
|
||||
>
|
||||
Set first as Error
|
||||
</button>
|
||||
<ToastsContainer {...args} toasts={toasts} />
|
||||
</div>
|
||||
);
|
||||
@@ -203,5 +224,5 @@ const Template: ComponentStory<typeof ToastsContainer> = (args) => {
|
||||
|
||||
export const Default = Template.bind({});
|
||||
Default.args = {
|
||||
order: 'asc',
|
||||
order: 'desc',
|
||||
};
|
||||
|
||||
@@ -7,6 +7,8 @@ import { Toast } from './toast';
|
||||
import type { Toasts } from './use-toasts';
|
||||
import { useToasts } from './use-toasts';
|
||||
|
||||
import { Portal } from '@radix-ui/react-portal';
|
||||
|
||||
type ToastsContainerProps = {
|
||||
toasts: Toasts;
|
||||
order: 'asc' | 'desc';
|
||||
@@ -39,19 +41,21 @@ export const ToastsContainer = ({
|
||||
}, [count, order, toasts]);
|
||||
|
||||
return (
|
||||
<div
|
||||
<Portal
|
||||
ref={ref as Ref<HTMLDivElement>}
|
||||
className={classNames(
|
||||
'group',
|
||||
'absolute top-0 right-0 pt-2 pr-2 max-w-full z-20 max-h-full overflow-x-hidden overflow-y-auto',
|
||||
'absolute bottom-0 right-0 z-20 ',
|
||||
'p-[8px_16px_16px_16px]',
|
||||
'max-w-full max-h-full overflow-x-hidden overflow-y-auto',
|
||||
{
|
||||
hidden: Object.keys(toasts).length === 0,
|
||||
}
|
||||
)}
|
||||
>
|
||||
<ul
|
||||
className={classNames('relative mt-[26px]', {
|
||||
'flex flex-col-reverse': order === 'desc',
|
||||
className={classNames('relative mt-[38px]', 'flex flex-col gap-[8px]', {
|
||||
'flex-col-reverse': order === 'desc',
|
||||
})}
|
||||
>
|
||||
{toasts &&
|
||||
@@ -63,23 +67,26 @@ export const ToastsContainer = ({
|
||||
);
|
||||
})}
|
||||
<Button
|
||||
title={t('Close all toasts')}
|
||||
size={'xs'}
|
||||
fill={false}
|
||||
title={t('Dismiss all toasts')}
|
||||
size="sm"
|
||||
fill={true}
|
||||
className={classNames(
|
||||
'absolute top-[-26px] right-0 z-20',
|
||||
'absolute top-[-38px] right-0 z-20',
|
||||
'transition-opacity',
|
||||
'opacity-0 group-hover:opacity-50 hover:!opacity-100',
|
||||
'text-xs'
|
||||
'text-sm text-black dark:text-white bg-white dark:bg-black hover:bg-white hover:dark:bg-black',
|
||||
{
|
||||
hidden: Object.keys(toasts).length === 0,
|
||||
}
|
||||
)}
|
||||
onClick={() => {
|
||||
closeAll();
|
||||
}}
|
||||
variant={'default'}
|
||||
>
|
||||
{t('Close all')}
|
||||
{t('Dismiss all')}
|
||||
</Button>
|
||||
</ul>
|
||||
</div>
|
||||
</Portal>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -71,7 +71,6 @@ export const useToasts = create(
|
||||
const found = state.toasts[toast.id];
|
||||
if (found) {
|
||||
if (!isEqual(found, toast)) {
|
||||
console.log('updating', toast.id);
|
||||
Object.assign(found, toast);
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -103,7 +103,7 @@ export const useEthWithdrawApprovalsManager = () => {
|
||||
update(transaction.id, {
|
||||
status: ApprovalStatus.Ready,
|
||||
approval,
|
||||
dialogOpen: false,
|
||||
dialogOpen: true,
|
||||
});
|
||||
const signer = provider.getSigner();
|
||||
createEthTransaction(
|
||||
|
||||
@@ -46,6 +46,7 @@ export interface EthWithdrawApprovalStore {
|
||||
>
|
||||
) => void;
|
||||
dismiss: (index: number) => void;
|
||||
delete: (index: number) => void;
|
||||
}
|
||||
|
||||
export const useEthWithdrawApprovalsStore = create(
|
||||
@@ -108,5 +109,12 @@ export const useEthWithdrawApprovalsStore = create(
|
||||
})
|
||||
);
|
||||
},
|
||||
delete: (index: number) => {
|
||||
set(
|
||||
produce((state: EthWithdrawApprovalStore) => {
|
||||
delete state.transactions[index];
|
||||
})
|
||||
);
|
||||
},
|
||||
}))
|
||||
);
|
||||
|
||||
@@ -229,13 +229,15 @@ export const VerificationStatus = ({ state }: { state: VerifyState }) => {
|
||||
);
|
||||
return (
|
||||
<>
|
||||
<p className="mb-2">
|
||||
{t("The amount you're withdrawing has triggered a time delay")}
|
||||
</p>
|
||||
<p>{t("The amount you're withdrawing has triggered a time delay")}</p>
|
||||
<p>{t(`Cannot be completed until ${formattedTime}`)}</p>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
if (state.status === ApprovalStatus.Ready) {
|
||||
return <p>{t('The withdrawal has been approved.')}</p>;
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user