Skip to content

Commit

Permalink
feat: deposit/withdraw UX improvements (#857)
Browse files Browse the repository at this point in the history
  • Loading branch information
rosepuppy authored Jul 30, 2024
1 parent a26bb23 commit f0b484e
Show file tree
Hide file tree
Showing 11 changed files with 228 additions and 133 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@
"@cosmjs/tendermint-rpc": "^0.32.1",
"@dydxprotocol/v4-abacus": "1.8.63",
"@dydxprotocol/v4-client-js": "^1.1.27",
"@dydxprotocol/v4-localization": "^1.1.164",
"@dydxprotocol/v4-localization": "^1.1.165",
"@ethersproject/providers": "^5.7.2",
"@hugocxl/react-to-image": "^0.0.9",
"@js-joda/core": "^5.5.3",
Expand Down
8 changes: 4 additions & 4 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 12 additions & 1 deletion src/components/Button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ type ElementProps = {
slotLeft?: React.ReactNode;
slotRight?: React.ReactNode;
state?: ButtonState | ButtonStateConfig;
withContentOnLoading?: boolean;
};

type StyleProps = {
Expand All @@ -48,6 +49,7 @@ export const Button = forwardRef<HTMLButtonElement | HTMLAnchorElement, ButtonPr
children,
slotLeft = null,
slotRight = null,
withContentOnLoading = false,

...otherProps
},
Expand All @@ -67,7 +69,12 @@ export const Button = forwardRef<HTMLButtonElement | HTMLAnchorElement, ButtonPr
{...{ ref, action, size, shape, state, ...otherProps }}
>
{state[ButtonState.Loading] ? (
<LoadingDots size={3} />
<>
{withContentOnLoading && slotLeft}
{withContentOnLoading && children}
{withContentOnLoading && slotRight}
<$LoadingDots size={3} />
</>
) : (
<>
{slotLeft}
Expand Down Expand Up @@ -170,3 +177,7 @@ const StyledBaseButton = styled(BaseButton)<StyleProps>`
${state[ButtonState.Disabled] && buttonStateVariants(action)[ButtonState.Disabled]}
`}
`;

const $LoadingDots = styled(LoadingDots)`
padding-top: 0.5em;
`;
8 changes: 6 additions & 2 deletions src/components/Loading/LoadingDots.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,15 @@ import styled, { css } from 'styled-components';
// Types/constants
export type LoadingDotsProps = {
size?: number;
className?: string;
};

// Component
export const LoadingDots: React.FC<LoadingDotsProps> = ({ size = 1 }: LoadingDotsProps) => (
<LoadingDotsContainer size={size}>
export const LoadingDots: React.FC<LoadingDotsProps> = ({
size = 1,
className,
}: LoadingDotsProps) => (
<LoadingDotsContainer size={size} className={className}>
<i />
<i />
<i />
Expand Down
2 changes: 2 additions & 0 deletions src/constants/notifications.ts
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,7 @@ export enum TransferNotificationTypes {
}

export type TransferNotifcation = {
id?: string;
txHash: string;
type?: TransferNotificationTypes;
toChainId?: string;
Expand All @@ -203,6 +204,7 @@ export type TransferNotifcation = {
isExchange?: boolean;
requestId?: string;
tracked?: boolean;
isDummy?: boolean;
};

export enum ReleaseUpdateNotificationIds {
Expand Down
195 changes: 110 additions & 85 deletions src/hooks/useLocalNotifications.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -83,10 +83,33 @@ const useLocalNotificationsContext = () => {
[setAllTransferNotifications, dydxAddress, allTransferNotifications]
);

const addTransferNotification = useCallback(
useEffect(() => {
// whip out all dummy notifications on startup
if (!dydxAddress) return;
setAllTransferNotifications((currentAllNotifications) => {
const updatedNotifications = { ...currentAllNotifications };
updatedNotifications[dydxAddress] = (updatedNotifications[dydxAddress] ?? []).filter(
(n) => !n.isDummy
);
return updatedNotifications;
});
}, [dydxAddress]);

const addOrUpdateTransferNotification = useCallback(
(notification: TransferNotifcation) => {
const { txHash, triggeredAt, toAmount, type, fromChainId } = notification;
setTransferNotifications([...transferNotifications, notification]);
const { id, txHash, triggeredAt, toAmount, type, fromChainId } = notification;
// replace notification if id or txhash already exists
const existingNotificationIndex = transferNotifications.findIndex(
(n) => n.id === id || n.txHash === txHash
);
if (existingNotificationIndex > -1) {
const updatedNotifications = [...transferNotifications];
updatedNotifications[existingNotificationIndex] = notification;
setTransferNotifications(updatedNotifications);
} else {
setTransferNotifications([...transferNotifications, notification]);
}

trackSkipTxWithTenacity({
transactionHash: txHash,
chainId: fromChainId,
Expand All @@ -104,7 +127,7 @@ const useLocalNotificationsContext = () => {
})
);
},
[transferNotifications]
[transferNotifications, setTransferNotifications, skip]
);

useQuery({
Expand All @@ -114,91 +137,93 @@ const useLocalNotificationsContext = () => {
transferNotificationsInner: TransferNotifcation[]
) => {
const newTransferNotifications = await Promise.all(
transferNotificationsInner.map(async (transferNotification) => {
const {
txHash,
toChainId,
fromChainId,
triggeredAt,
isCctp,
errorCount,
status: currentStatus,
isExchange,
requestId,
tracked,
} = transferNotification;

const hasErrors =
// @ts-ignore status.errors is not in the type definition but can be returned
// also error can some time come back as an empty object so we need to ignore for that
!!currentStatus?.errors ||
(currentStatus?.error && Object.keys(currentStatus.error).length !== 0);

if (
!isExchange &&
!hasErrors &&
(!currentStatus?.squidTransactionStatus ||
currentStatus?.squidTransactionStatus === 'ongoing')
) {
try {
const skipParams = {
transactionHash: txHash,
chainId: fromChainId,
baseUrl: skip,
};
if (!tracked && useSkip) {
const { tx_hash: trackedTxHash } = await trackSkipTx(skipParams);
// if no tx hash was returned, transfer has not yet been tracked
if (!trackedTxHash) return transferNotification;
transferNotification.tracked = true;
}
const status = await fetchTransferStatus({
transactionId: txHash,
toChainId,
fromChainId,
isCctp,
requestId,
baseUrl: skip,
useSkip,
});
if (status) {
transferNotification.status = status;
if (status.squidTransactionStatus === 'success') {
track(
AnalyticsEvents.TransferNotification({
triggeredAt,
timeSpent: triggeredAt ? Date.now() - triggeredAt : undefined,
toAmount: transferNotification.toAmount,
status: 'success',
type: transferNotification.type,
txHash,
})
);
transferNotificationsInner
.filter((n) => !n.isDummy)
.map(async (transferNotification) => {
const {
txHash,
toChainId,
fromChainId,
triggeredAt,
isCctp,
errorCount,
status: currentStatus,
isExchange,
requestId,
tracked,
} = transferNotification;

const hasErrors =
// @ts-ignore status.errors is not in the type definition but can be returned
// also error can some time come back as an empty object so we need to ignore for that
!!currentStatus?.errors ||
(currentStatus?.error && Object.keys(currentStatus.error).length !== 0);

if (
!isExchange &&
!hasErrors &&
(!currentStatus?.squidTransactionStatus ||
currentStatus?.squidTransactionStatus === 'ongoing')
) {
try {
const skipParams = {
transactionHash: txHash,
chainId: fromChainId,
baseUrl: skip,
};
if (!tracked && useSkip) {
const { tx_hash: trackedTxHash } = await trackSkipTx(skipParams);
// if no tx hash was returned, transfer has not yet been tracked
if (!trackedTxHash) return transferNotification;
transferNotification.tracked = true;
}
}
} catch (error) {
if (!triggeredAt || Date.now() - triggeredAt > STATUS_ERROR_GRACE_PERIOD) {
if (errorCount && errorCount > ERROR_COUNT_THRESHOLD) {
transferNotification.status = error;
track(
AnalyticsEvents.TransferNotification({
triggeredAt,
timeSpent: triggeredAt ? Date.now() - triggeredAt : undefined,
toAmount: transferNotification.toAmount,
status: 'error',
type: transferNotification.type,
txHash,
})
);
} else {
transferNotification.errorCount = errorCount ? errorCount + 1 : 1;
const status = await fetchTransferStatus({
transactionId: txHash,
toChainId,
fromChainId,
isCctp,
requestId,
baseUrl: skip,
useSkip,
});
if (status) {
transferNotification.status = status;
if (status.squidTransactionStatus === 'success') {
track(
AnalyticsEvents.TransferNotification({
triggeredAt,
timeSpent: triggeredAt ? Date.now() - triggeredAt : undefined,
toAmount: transferNotification.toAmount,
status: 'success',
type: transferNotification.type,
txHash,
})
);
}
}
} catch (error) {
if (!triggeredAt || Date.now() - triggeredAt > STATUS_ERROR_GRACE_PERIOD) {
if (errorCount && errorCount > ERROR_COUNT_THRESHOLD) {
transferNotification.status = error;
track(
AnalyticsEvents.TransferNotification({
triggeredAt,
timeSpent: triggeredAt ? Date.now() - triggeredAt : undefined,
toAmount: transferNotification.toAmount,
status: 'error',
type: transferNotification.type,
txHash,
})
);
} else {
transferNotification.errorCount = errorCount ? errorCount + 1 : 1;
}
}
}
}
}

return transferNotification;
})
return transferNotification;
})
);

return newTransferNotifications;
Expand All @@ -212,6 +237,6 @@ const useLocalNotificationsContext = () => {
return {
// Transfer notifications
transferNotifications,
addTransferNotification,
addOrUpdateTransferNotification,
};
};
4 changes: 2 additions & 2 deletions src/hooks/useNotificationTypes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ export const notificationTypes: NotificationTypeConfig[] = [
useEffect(() => {
// eslint-disable-next-line no-restricted-syntax
for (const transfer of transferNotifications) {
const { fromChainId, status, txHash, toAmount, type, isExchange } = transfer;
const { id, fromChainId, status, txHash, toAmount, type, isExchange } = transfer;
const isFinished =
(Boolean(status) && status?.squidTransactionStatus !== 'ongoing') || isExchange;
const icon = <Icon iconName={isFinished ? IconName.Transfer : IconName.Clock} />;
Expand Down Expand Up @@ -241,7 +241,7 @@ export const notificationTypes: NotificationTypeConfig[] = [
});

trigger(
txHash,
id ?? txHash,
{
icon,
title,
Expand Down
Loading

0 comments on commit f0b484e

Please sign in to comment.