Skip to content

Commit

Permalink
feat: add active swaps page and live updates
Browse files Browse the repository at this point in the history
  • Loading branch information
chybisov committed Aug 29, 2022
1 parent 75c40bd commit 4d470b5
Show file tree
Hide file tree
Showing 24 changed files with 355 additions and 140 deletions.
6 changes: 6 additions & 0 deletions packages/widget/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { Header } from './components/Header';
import { Initializer } from './components/Initializer';
import { NotFound } from './components/NotFound';
import { PoweredBy } from './components/PoweredBy';
import { ActiveSwapsPage } from './pages/ActiveSwapsPage';
import { MainPage } from './pages/MainPage';
import { SelectTokenPage } from './pages/SelectTokenPage';
import { SelectWalletPage } from './pages/SelectWalletPage';
Expand Down Expand Up @@ -59,6 +60,10 @@ const AppRoutes = () => {
path: navigationRoutes.swapRoutes,
element: <SwapRoutesPage />,
},
{
path: navigationRoutes.activeSwaps,
element: <ActiveSwapsPage />,
},
{
path: navigationRoutes.swapHistory,
element: <SwapHistoryPage />,
Expand All @@ -78,6 +83,7 @@ const AppRoutes = () => {
...[
navigationRoutes.swapExecution,
`${navigationRoutes.swapRoutes}/${navigationRoutes.swapExecution}`,
`${navigationRoutes.activeSwaps}/${navigationRoutes.swapExecution}`,
].map((path) => ({
path,
element: <SwapPage />,
Expand Down
106 changes: 106 additions & 0 deletions packages/widget/src/components/ActiveSwaps/ActiveSwapItem.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import {
ArrowForward as ArrowForwardIcon,
Info as InfoIcon,
Warning as WarningIcon
} from '@mui/icons-material';
import { Box, ListItemAvatar, ListItemText, Typography } from '@mui/material';
import { useTranslation } from 'react-i18next';
import { useNavigate } from 'react-router-dom';
import { useProcessMessage, useRouteExecution } from '../../hooks';
import { navigationRoutes } from '../../utils';
import { StepTimer } from '../Step/StepTimer';
import { TokenAvatar, TokenAvatarGroup } from '../TokenAvatar';
import { ListItem, ListItemButton } from './ActiveSwaps.style';

export const ActiveSwapItem: React.FC<{
routeId: string;
dense?: boolean;
}> = ({ routeId, dense }) => {
const { t } = useTranslation();
const navigate = useNavigate();
const { route, status } = useRouteExecution(routeId);

// TODO: replace with ES2023 findLast
const lastActiveStep = route?.steps
.slice()
.reverse()
.find((step) => step.execution);
const lastActiveProcess = lastActiveStep?.execution?.process.at(-1);

const { title } = useProcessMessage(lastActiveStep, lastActiveProcess);

if (!route || !lastActiveStep) {
return null;
}

const handleClick = () => {
navigate(navigationRoutes.swapExecution, { state: { routeId } });
};

const getStatusComponent = () => {
switch (lastActiveProcess?.status) {
case 'ACTION_REQUIRED':
return <InfoIcon color="info" fontSize="small" />;
case 'FAILED':
return <WarningIcon color="error" fontSize="small" />;
default:
return (
<Typography fontSize={14} fontWeight={500}>
<StepTimer step={lastActiveStep} hideInProgress />
</Typography>
);
}
};

const ListItemWrapper = dense ? ListItem : ListItemButton;

return (
<ListItemWrapper
onClick={handleClick}
dense
disableRipple
>
<ListItemAvatar>
<TokenAvatarGroup total={2}>
<TokenAvatar token={route.fromToken} />
<TokenAvatar token={route.toToken} />
</TokenAvatarGroup>
</ListItemAvatar>
<ListItemText
sx={{ margin: 0 }}
disableTypography
primary={
<Typography fontWeight={500} lineHeight={1}>
<Box
sx={{
display: 'flex',
alignItems: 'center',
marginLeft: 2,
height: 16,
}}
>
{route.fromToken.symbol}
<ArrowForwardIcon sx={{ paddingX: 0.5 }} />
{route.toToken.symbol}
</Box>
</Typography>
}
secondary={
status !== 'success' ? (
<Typography
fontWeight={400}
fontSize={12}
color="text.secondary"
lineHeight={1}
mt={0.75}
ml={2}
>
{title}
</Typography>
) : null
}
/>
{getStatusComponent()}
</ListItemWrapper>
);
};
43 changes: 43 additions & 0 deletions packages/widget/src/components/ActiveSwaps/ActiveSwaps.style.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import {
Button,
ListItem as MuiListItem,
ListItemButton as MuiListItemButton
} from '@mui/material';
import { listItemSecondaryActionClasses } from '@mui/material/ListItemSecondaryAction';
import { alpha, styled } from '@mui/material/styles';
import { getContrastAlphaColor } from '../../utils';
import { Card } from '../Card';

export const ProgressCard = styled(Card)(({ theme }) => ({
borderColor: alpha(theme.palette.secondary.main, 0.48),
background: alpha(theme.palette.secondary.main, 0.08),
'&:hover': {
background: alpha(theme.palette.secondary.main, 0.08),
},
}));

export const ListItemButton = styled(MuiListItemButton)(({ theme }) => ({
borderRadius: theme.shape.borderRadiusSecondary,
paddingLeft: theme.spacing(1),
paddingRight: theme.spacing(1),
height: 64,
'&:hover': {
backgroundColor: getContrastAlphaColor(theme, '4%'),
},
}));

export const ListItem = styled(MuiListItem)(({ theme }) => ({
padding: theme.spacing(0, 1.5),
[`.${listItemSecondaryActionClasses.root}`]: {
right: theme.spacing(3),
},
'&:hover': {
cursor: 'pointer',
},
}));

export const ShowAllButton = styled(Button)(({ theme }) => ({
'&:hover': {
background: 'none',
},
}));
50 changes: 50 additions & 0 deletions packages/widget/src/components/ActiveSwaps/ActiveSwaps.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import type { BoxProps } from '@mui/material';
import { Stack } from '@mui/material';
import { useTranslation } from 'react-i18next';
import { useNavigate } from 'react-router-dom';
import { useWallet } from '../../providers';
import { useExecutingRoutesIds } from '../../stores';
import { navigationRoutes } from '../../utils';
import { CardTitle } from '../Card';
import { ActiveSwapItem } from './ActiveSwapItem';
import { ProgressCard, ShowAllButton } from './ActiveSwaps.style';

export const ActiveSwaps: React.FC<BoxProps> = (props) => {
const { t } = useTranslation();
const navigate = useNavigate();
const { account } = useWallet();
const executingRoutes = useExecutingRoutesIds(account.address);

if (!executingRoutes?.length) {
return null;
}

const handleShowAll = () => {
navigate(navigationRoutes.activeSwaps);
};

const hasShowAll = executingRoutes?.length > 2;

return (
<ProgressCard {...props}>
<CardTitle>{t('header.activeSwaps')}</CardTitle>
<Stack spacing={1.5} pt={1.5} pb={hasShowAll ? 0 : 2}>
{executingRoutes.slice(0, 2).map((routeId) => (
<ActiveSwapItem key={routeId} routeId={routeId} dense />
))}
</Stack>
{hasShowAll ? (
<ShowAllButton
disableRipple
fullWidth
onClick={handleShowAll}
sx={(theme) => ({
padding: theme.spacing(0.75, 2),
})}
>
{t('button.showAll')}
</ShowAllButton>
) : null}
</ProgressCard>
);
};
3 changes: 3 additions & 0 deletions packages/widget/src/components/ActiveSwaps/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from './ActiveSwapItem';
export * from './ActiveSwaps';

11 changes: 1 addition & 10 deletions packages/widget/src/components/Header/Header.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,11 @@
import type { FC, PropsWithChildren } from 'react';
import { useLocation } from 'react-router-dom';
import { useWidgetConfig } from '../../providers';
import { ElementId, navigationRoutes } from '../../utils';
import { ElementId, stickyHeaderRoutes } from '../../utils';
import { Container } from './Header.style';
import { NavigationHeader } from './NavigationHeader';
import { WalletHeader } from './WalletHeader';

const stickyHeaderRoutes = [
navigationRoutes.selectWallet,
navigationRoutes.settings,
navigationRoutes.swapRoutes,
navigationRoutes.swapExecution,
navigationRoutes.swapHistory,
navigationRoutes.swapDetails,
];

const HeaderContainer: FC<PropsWithChildren<{}>> = ({ children }) => {
const { pathname } = useLocation();
return (
Expand Down
19 changes: 7 additions & 12 deletions packages/widget/src/components/Header/NavigationHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,14 @@ import { useTranslation } from 'react-i18next';
import { Route, Routes, useLocation } from 'react-router-dom';
import { useNavigateBack } from '../../hooks';
import { useWallet } from '../../providers';
import { navigationRoutes, navigationRoutesValues } from '../../utils';
import {
backButtonRoutes,
navigationRoutes,
navigationRoutesValues,
} from '../../utils';
import { HeaderAppBar } from './Header.style';
import { useHeaderActionStore } from './useHeaderActionStore';

const backButtonRoutes = [
navigationRoutes.selectWallet,
navigationRoutes.settings,
navigationRoutes.swapHistory,
navigationRoutes.fromToken,
navigationRoutes.toToken,
navigationRoutes.swapRoutes,
navigationRoutes.swapExecution,
navigationRoutes.swapDetails,
];

export const NavigationHeader: React.FC = () => {
const { t } = useTranslation();
const { navigate, navigateBack } = useNavigateBack();
Expand All @@ -46,6 +39,8 @@ export const NavigationHeader: React.FC = () => {
return t(`header.to`);
case navigationRoutes.swapRoutes:
return t(`header.routes`);
case navigationRoutes.activeSwaps:
return t(`header.activeSwaps`);
case navigationRoutes.swapExecution:
return t(`header.swap`);
case navigationRoutes.swapDetails:
Expand Down
8 changes: 2 additions & 6 deletions packages/widget/src/components/Step/StepProcess.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,15 @@
import type { Process, Step } from '@lifi/sdk';
import { Link as LinkIcon } from '@mui/icons-material';
import { Box, Link, Typography } from '@mui/material';
import { useTranslation } from 'react-i18next';
import { useChains } from '../../hooks';
import { useProcessMessage } from '../../hooks';
import { CircularProgress } from './CircularProgress';
import { LinkButton } from './StepProcess.style';
import { getProcessMessage } from './utils';

export const StepProcess: React.FC<{
step: Step;
process: Process;
}> = ({ step, process }) => {
const { t } = useTranslation();
const { getChainById } = useChains();
const { title, message } = getProcessMessage(t, getChainById, step, process);
const { title, message } = useProcessMessage(step, process);
return (
<Box px={2} py={1}>
<Box
Expand Down
14 changes: 11 additions & 3 deletions packages/widget/src/components/Step/StepTimer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@ const getExpiryTimestamp = (step: Step) =>
step.estimate.executionDuration * 1000,
);

export const StepTimer: React.FC<{ step: Step }> = ({ step }) => {
export const StepTimer: React.FC<{ step: Step; hideInProgress?: boolean }> = ({
step,
hideInProgress,
}) => {
const { t } = useTranslation();
const [isExpired, setExpired] = useState(false);
const [isExecutionStarted, setExecutionStarted] = useState(!!step.execution);
Expand Down Expand Up @@ -59,13 +62,18 @@ export const StepTimer: React.FC<{ step: Step }> = ({ step }) => {
</>
);
}

const isTimerExpired = isExpired || (!minutes && !seconds);

if (
step.execution?.status === 'DONE' ||
step.execution?.status === 'FAILED'
step.execution?.status === 'FAILED' ||
(isTimerExpired && hideInProgress)
) {
return null;
}
return isExpired ? (

return isTimerExpired ? (
<>{t('swap.inProgress')}</>
) : (
// eslint-disable-next-line react/jsx-no-useless-fragment
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ export const SwapRouteCard: React.FC<SwapRouteCardProps & BoxProps> = ({
{(
route.steps
.map((step) => step.estimate.executionDuration)
.reduce((cumulated, x) => cumulated + x) / 60
.reduce((duration, x) => duration + x) / 60
).toFixed(0)}
</Typography>
<Typography
Expand Down

This file was deleted.

Loading

0 comments on commit 4d470b5

Please sign in to comment.