Skip to content

Commit

Permalink
Polishing routes UI (#2611)
Browse files Browse the repository at this point in the history
* consolidate header; add badges

* ETA < 1 minute is green

* more polish on routes UI

* rm unneeded attr
  • Loading branch information
artursapek committed Sep 16, 2024
1 parent 7d02a52 commit 59e1f32
Show file tree
Hide file tree
Showing 6 changed files with 144 additions and 36 deletions.
8 changes: 4 additions & 4 deletions wormhole-connect/src/config/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,16 +112,16 @@ export const RoutesConfig: Record<string, RouteData> = {
},
MayanSwapMCTP: {
name: 'MayanSwapMCTP',
displayName: 'Mayan Swap (MCTP)',
providedBy: 'Mayan (MCTP)',
displayName: 'Mayan Swap MCTP',
providedBy: 'Mayan MCTP',
link: 'https://mayan.finance/',
icon: XLabsIcon,
pendingMessage: 'Waiting for Wormhole network consensus . . .',
},
MayanSwapSWIFT: {
name: 'MayanSwapSWIFT',
displayName: 'Mayan Swap (Swift)',
providedBy: 'Mayan (Swift)',
displayName: 'Mayan Swap Swift',
providedBy: 'Mayan Swift',
link: 'https://mayan.finance/',
icon: XLabsIcon,
pendingMessage: 'Waiting for Wormhole network consensus . . .',
Expand Down
18 changes: 18 additions & 0 deletions wormhole-connect/src/icons/CheapestRoute.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import React from 'react';
import { createSvgIcon } from '@mui/material';

export default createSvgIcon(
<svg
width="14"
height="13"
viewBox="0 0 14 13"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path d="M5.2,7.5c0-1,.8-1.8,1.7-1.8s1.7.8,1.7,1.8-.8,1.8-1.7,1.8-1.7-.8-1.7-1.8Z" />
<path d="M10.1,7.5c0,1.2-.6,2.2-1.6,2.7l1.9,3.4c2.1-1.2,3.5-3.5,3.5-6.1h-3.9Z" />
<path d="M7,4.3c.6,0,1.1.2,1.6.4l1.9-3.4c-1-.6-2.2-.9-3.5-.9s-2.5.3-3.5.9l1.9,3.4c.5-.3,1-.4,1.6-.4Z" />
<path d="M3.9,7.5H0c0,2.6,1.4,4.9,3.5,6.1l1.9-3.4c-.9-.5-1.6-1.5-1.6-2.7Z" />
</svg>,
'Cheapest',
);
15 changes: 15 additions & 0 deletions wormhole-connect/src/icons/FastestRoute.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import React from 'react';
import { createSvgIcon } from '@mui/material';

export default createSvgIcon(
<svg
width="9"
height="14"
viewBox="0 0 9 14"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path d="M8.9,5.7c0-.1-.2-.2-.4-.2h-4c-.3,0-.5-.2-.5-.4V0L0,7.9c0,.1,0,.3,0,.4,0,.1.2.2.4.2h4c.3,0,.5.2.5.4v5.1l4-7.9c0-.1,0-.3,0-.4Z" />
</svg>,
'Fastest',
);
2 changes: 1 addition & 1 deletion wormhole-connect/src/theme.ts
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ export const dark: WormholeConnectTheme = {
},
text: {
primary: '#ffffff',
secondary: '#667085',
secondary: '#79859e',
},
info: {
50: '#97a5b7',
Expand Down
81 changes: 51 additions & 30 deletions wormhole-connect/src/views/v2/Bridge/Routes/SingleRoute.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ import type { RootState } from 'store';
import { formatBalance } from 'store/transferInput';
import { toFixedDecimals } from 'utils/balance';
import { TokenConfig } from 'config/types';
import FastestRoute from 'icons/FastestRoute';
import CheapestRoute from 'icons/CheapestRoute';

const useStyles = makeStyles()((theme: any) => ({
container: {
Expand All @@ -31,6 +33,20 @@ const useStyles = makeStyles()((theme: any) => ({
width: '100%',
maxWidth: '420px',
},
fastestBadge: {
width: '14px',
height: '14px',
position: 'relative',
top: '2px',
fill: theme.palette.primary.main,
},
cheapestBadge: {
width: '12px',
height: '12px',
position: 'relative',
top: '1px',
fill: theme.palette.primary.main,
},
}));

type Props = {
Expand All @@ -39,6 +55,9 @@ type Props = {
error?: string;
destinationGasDrop?: number;
title?: string;
isFastest?: boolean;
isCheapest?: boolean;
isOnlyChoice?: boolean;
onSelect?: (route: string) => void;
quote?: routes.Quote<routes.Options>;
isFetchingQuote: boolean;
Expand Down Expand Up @@ -118,7 +137,9 @@ const SingleRoute = (props: Props) => {
<Typography color={theme.palette.text.secondary} fontSize={14}>
Network cost
</Typography>
{feeValue}
<Typography color={theme.palette.text.secondary} fontSize={14}>
{feeValue}
</Typography>
</Stack>
);
}, [destToken, isFetchingQuote, quote?.relayFee, tokenPrices]);
Expand Down Expand Up @@ -165,7 +186,15 @@ const SingleRoute = (props: Props) => {
{isFetchingQuote ? (
<CircularProgress size={14} />
) : (
<Typography fontSize={14}>
<Typography
fontSize={14}
sx={{
color:
quote?.eta && quote.eta < 60 * 1000
? theme.palette.success.main
: theme.palette.text.secondary,
}}
>
{quote?.eta ? millisToHumanString(quote.eta) : 'N/A'}
</Typography>
)}
Expand Down Expand Up @@ -226,20 +255,6 @@ const SingleRoute = (props: Props) => {
);
}, [showWarning]);

const isAutomaticRoute = useMemo(() => {
if (!props.route.name) {
return false;
}

const route = config.routes.get(props.route.name);

if (!route) {
return false;
}

return route.AUTOMATIC_DEPOSIT;
}, [props.route.name]);

const providerText = useMemo(() => {
if (!sourceToken) {
return '';
Expand Down Expand Up @@ -274,11 +289,6 @@ const SingleRoute = (props: Props) => {
destChain,
]);

const routeTitle = useMemo(
() => (isAutomaticRoute ? 'Automatic route' : 'Manual route'),
[isAutomaticRoute],
);

const receiveAmount = useMemo(() => {
return quote ? amount.whole(quote?.destinationToken.amount) : undefined;
}, [quote]);
Expand Down Expand Up @@ -358,17 +368,27 @@ const SingleRoute = (props: Props) => {
return <></>;
}

const routeCardBadge = useMemo(() => {
if (props.isFastest) {
return (
<>
<FastestRoute className={classes.fastestBadge} />
{props.isOnlyChoice ? 'Fast' : 'Fastest'}
</>
);
} else if (props.isCheapest && !props.isOnlyChoice) {
return (
<>
<CheapestRoute className={classes.cheapestBadge} /> Cheapest
</>
);
} else {
return null;
}
}, [props.isFastest, props.isCheapest]);

return (
<div key={name} className={classes.container}>
<Typography
fontSize={16}
paddingBottom={0}
marginBottom="8px"
width="100%"
textAlign="left"
>
{props.title || routeTitle}
</Typography>
<Card
className={classes.card}
sx={{
Expand All @@ -393,6 +413,7 @@ const SingleRoute = (props: Props) => {
avatar={<TokenIcon icon={destTokenConfig?.icon} height={36} />}
title={routeCardHeader}
subheader={routeCardSubHeader}
action={routeCardBadge}
/>
<CardContent>
<Stack justifyContent="space-between">
Expand Down
56 changes: 55 additions & 1 deletion wormhole-connect/src/views/v2/Bridge/Routes/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import Tooltip from '@mui/material/Tooltip';
import Link from '@mui/material/Link';
import { makeStyles } from 'tss-react/mui';

import config from 'config';
import { RoutesConfig } from 'config/routes';
import SingleRoute from 'views/v2/Bridge/Routes/SingleRoute';
import AlertBannerV2 from 'components/v2/AlertBanner';
Expand All @@ -12,6 +13,7 @@ import type { RootState } from 'store';
import { RouteState } from 'store/transferInput';

import { routes } from '@wormhole-foundation/sdk';
import { Typography } from '@mui/material';

const useStyles = makeStyles()((theme: any) => ({
connectWallet: {
Expand Down Expand Up @@ -84,6 +86,45 @@ const Routes = ({ sortedSupportedRoutes, ...props }: Props) => {
return selectedRoute ? [selectedRoute] : sortedSupportedRoutes.slice(0, 1);
}, [showAll, sortedSupportedRoutes]);

const fastestRoute = useMemo(() => {
return sortedSupportedRoutes.reduce(
(fastest, route) => {
const quote = props.quotes[route.name];
if (!quote || !quote.success) return fastest;

if (
quote.eta !== undefined &&
quote.eta < fastest.eta &&
quote.eta < 60_000
) {
return { name: route.name, eta: quote.eta };
} else {
return fastest;
}
},
{ name: '', eta: Infinity },
);
}, [sortedSupportedRoutes, props.quotes]);

const cheapestRoute = useMemo(() => {
return sortedSupportedRoutes.reduce(
(cheapest, route) => {
const quote = props.quotes[route.name];
const rc = config.routes.get(route.name);
// TODO put AUTOMATIC_DEPOSIT into RouteState
if (!quote || !quote.success || !rc.AUTOMATIC_DEPOSIT) return cheapest;

const amountOut = BigInt(quote.destinationToken.amount.amount);
if (amountOut > cheapest.amountOut) {
return { name: route.name, amountOut };
} else {
return cheapest;
}
},
{ name: '', amountOut: 0n },
);
}, [sortedSupportedRoutes, props.quotes]);

if (walletsConnected && supportedRoutes.length === 0 && Number(amount) > 0) {
return (
<AlertBannerV2
Expand Down Expand Up @@ -111,7 +152,17 @@ const Routes = ({ sortedSupportedRoutes, ...props }: Props) => {

return (
<>
{renderRoutes.map(({ name }) => {
<Typography
fontSize={16}
paddingBottom={0}
marginTop="8px"
marginBottom={0}
width="100%"
textAlign="left"
>
Routes
</Typography>
{renderRoutes.map(({ name }, index) => {
const routeConfig = RoutesConfig[name];
const isSelected = routeConfig.name === props.selectedRoute;
const quoteResult = props.quotes[name];
Expand All @@ -128,6 +179,9 @@ const Routes = ({ sortedSupportedRoutes, ...props }: Props) => {
route={routeConfig}
error={quoteError}
isSelected={isSelected && !quoteError}
isFastest={name === fastestRoute.name}
isCheapest={name === cheapestRoute.name}
isOnlyChoice={supportedRoutes.length === 1}
onSelect={props.onRouteChange}
quote={quote}
isFetchingQuote={props.isFetchingQuotes}
Expand Down

0 comments on commit 59e1f32

Please sign in to comment.