Skip to content

Commit

Permalink
feat: add useToken and useTokenBalance hooks, update SwapInput
Browse files Browse the repository at this point in the history
  • Loading branch information
chybisov committed Feb 17, 2022
1 parent e71ad88 commit be17b6d
Show file tree
Hide file tree
Showing 16 changed files with 178 additions and 98 deletions.
2 changes: 1 addition & 1 deletion packages/widget/src/components/ContainerDrawer/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,6 @@ export type ContainerDrawerProps = DrawerProps & {
};

export interface ContainerDrawerBase {
openDrawer(type: SwapFormDirection): void;
openDrawer(formType: SwapFormDirection): void;
closeDrawer(): void;
}
2 changes: 1 addition & 1 deletion packages/widget/src/components/SelectTokenDrawer/types.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { SwapFormDirection } from '../../providers/SwapFormProvider';

export interface SelectTokenDrawerBase {
openDrawer(type: SwapFormDirection): void;
openDrawer(formType: SwapFormDirection): void;
closeDrawer(): void;
}

Expand Down
23 changes: 11 additions & 12 deletions packages/widget/src/components/SwapChainButton/SwapChainButton.tsx
Original file line number Diff line number Diff line change
@@ -1,39 +1,38 @@
import { getChainByKey } from '@lifinance/sdk';
import { useToken } from '@lifinance/widget/hooks/useToken';
import { KeyboardArrowDown as KeyboardArrowDownIcon } from '@mui/icons-material';
import { useFormContext, useWatch } from 'react-hook-form';
import { useFormState, useWatch } from 'react-hook-form';
import { useTranslation } from 'react-i18next';
import { SwapFormKeyHelper } from '../../providers/SwapFormProvider';
import { Button } from './SwapChainButton.style';
import { SwapChainButtonProps } from './types';

export const SwapChainButton: React.FC<SwapChainButtonProps> = ({
onClick,
type,
formType,
}) => {
const { t } = useTranslation();
const {
register,
formState: { isSubmitting },
} = useFormContext();
const [chain, token] = useWatch({
const { isSubmitting } = useFormState();
const [chainKey, tokenAddress] = useWatch({
name: [
SwapFormKeyHelper.getChainKey(type),
SwapFormKeyHelper.getTokenKey(type),
SwapFormKeyHelper.getChainKey(formType),
SwapFormKeyHelper.getTokenKey(formType),
],
});

const { chain, token } = useToken(chainKey, tokenAddress);

return (
<Button
variant="outlined"
endIcon={<KeyboardArrowDownIcon />}
onClick={() => onClick?.(type)}
onClick={() => onClick?.(formType)}
disabled={isSubmitting}
disableElevation
disableRipple
fullWidth
>
{chain && token
? `${token} on ${getChainByKey(chain).name}`
? `${token.symbol} on ${chain.name}`
: t(`swap.selectChainAndToken`)}
</Button>
);
Expand Down
10 changes: 6 additions & 4 deletions packages/widget/src/components/SwapChainButton/types.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { SwapFormDirection } from '../../providers/SwapFormProvider';
import {
SwapFormDirection,
SwapFormTypeProps,
} from '../../providers/SwapFormProvider';

export interface SwapChainButtonProps {
onClick?(type: SwapFormDirection): void;
type: SwapFormDirection;
export interface SwapChainButtonProps extends SwapFormTypeProps {
onClick?(formType: SwapFormDirection): void;
}
54 changes: 0 additions & 54 deletions packages/widget/src/components/SwapInputAdornment.tsx

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { Typography } from '@mui/material';
import { styled } from '@mui/material/styles';

export const SwapPriceTypography = styled(Typography)(({ theme }) => ({
borderLeft: '1px solid',
borderColor: theme.palette.grey[300],
paddingLeft: 8,
marginLeft: 8,
}));

export const SwapMaxAmountTypography = styled(Typography)({
textDecoration: 'underline',
marginRight: 8,
'&:hover': {
cursor: 'pointer',
},
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { InputAdornment, Skeleton, Typography } from '@mui/material';
import BigNumber from 'bignumber.js';
import { useMemo } from 'react';
import { useWatch } from 'react-hook-form';
import { useTranslation } from 'react-i18next';
import { useTokenBalance } from '../../hooks/useTokenBalance';
import {
SwapFormKeyHelper,
SwapFormTypeProps,
} from '../../providers/SwapFormProvider';
import { formatTokenAmount } from '../../utils/format';
import {
SwapMaxAmountTypography,
SwapPriceTypography,
} from './SwapInputAdornment.style';

export const SwapInputAdornment: React.FC<SwapFormTypeProps> = ({
formType,
}) => {
const { t } = useTranslation();
const [chainKey, tokenAddress] = useWatch({
name: [
SwapFormKeyHelper.getChainKey(formType),
SwapFormKeyHelper.getTokenKey(formType),
],
});
const { token, tokenWithBalance, isFetching } = useTokenBalance(
chainKey,
tokenAddress,
);

const amount = useMemo(
() =>
token
? formatTokenAmount(token, new BigNumber(tokenWithBalance?.amount ?? 0))
: null,
[token, tokenWithBalance?.amount],
);

return (
<InputAdornment position="end">
{isFetching ? (
<Skeleton
variant="rectangular"
width={formType === 'from' ? 160 : 96}
height={20}
sx={{ borderRadius: '6px' }}
/>
) : (
<>
{formType === 'from' && token && amount ? (
<>
<SwapMaxAmountTypography variant="body2" color="text.primary">
{t(`swap.max`)}
</SwapMaxAmountTypography>
<Typography variant="body2" color="text.secondary">
{t(`swap.maxAmount`, {
amount,
})}
</Typography>
</>
) : null}
<SwapPriceTypography variant="body2" color="text.secondary">
{t(`swap.price`, { price: tokenWithBalance?.priceUSD ?? 0 })}
</SwapPriceTypography>
</>
)}
</InputAdornment>
);
};
1 change: 1 addition & 0 deletions packages/widget/src/components/SwapInputAdornment/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './SwapInputAdornment';
4 changes: 2 additions & 2 deletions packages/widget/src/components/TokenList/TokenList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -95,8 +95,8 @@ export const TokenList: React.FC<TokenListProps> = ({
});

const handleTokenClick = useCallback(
(token: string) => {
setValue(SwapFormKeyHelper.getTokenKey(formType), token);
(tokenAddress: string) => {
setValue(SwapFormKeyHelper.getTokenKey(formType), tokenAddress);
onClick?.();
},
[formType, onClick, setValue],
Expand Down
2 changes: 1 addition & 1 deletion packages/widget/src/components/TokenList/TokenListItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { TokenListItemBaseProps, TokenListItemProps } from './types';

export const MemoizedTokenListItem: React.FC<TokenListItemProps> = memo(
({ onClick, size, start, token, isBalancesLoading }) => {
const handleClick = () => onClick?.(token.symbol);
const handleClick = () => onClick?.(token.address);
return (
<TokenListItem
secondaryAction={
Expand Down
21 changes: 21 additions & 0 deletions packages/widget/src/hooks/useToken.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { ChainKey, getChainByKey } from '@lifinance/sdk';
import { useMemo } from 'react';
import { useSwapPossibilities } from './useSwapPossibilities';

export const useToken = (chainKey: ChainKey, tokenAddress: string) => {
const { data: possibilities, isLoading } = useSwapPossibilities();

const [chain, token] = useMemo(() => {
const chain = getChainByKey(chainKey);
const token = possibilities?.tokens.find(
(token) => token.address === tokenAddress && token.chainId === chain.id,
);
return [chain, token];
}, [chainKey, possibilities?.tokens, tokenAddress]);

return {
chain,
token,
isLoading,
};
};
32 changes: 32 additions & 0 deletions packages/widget/src/hooks/useTokenBalance.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import Lifi, { ChainKey } from '@lifinance/sdk';
import { useQuery } from 'react-query';
import { usePriorityAccount } from './connectorHooks';
import { useToken } from './useToken';

export const useTokenBalance = (chainKey: ChainKey, tokenAddress: string) => {
const account = usePriorityAccount();
const { token } = useToken(chainKey, tokenAddress);

const { data: tokenWithBalance, isFetching } = useQuery(
['token', token?.symbol, account],
async ({ queryKey: [_, tokenSymbol, account] }) => {
if (!account || !token) {
return null;
}
const tokenBalance = await Lifi.getTokenBalance(account, token);
return tokenBalance;
},
{
enabled: Boolean(account) && Boolean(token),
refetchIntervalInBackground: true,
refetchInterval: 60_000,
staleTime: 60_000,
},
);

return {
token,
tokenWithBalance,
isFetching,
};
};
4 changes: 2 additions & 2 deletions packages/widget/src/hooks/useTokens.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,8 @@ export const useTokens = (selectedChain: ChainKey) => {
isLoading: isBalancesLoading,
refetch,
} = useQuery(
['tokens', account, selectedChain],
async ({ queryKey: [_, account, chainKey] }) => {
['tokens', selectedChain, account],
async ({ queryKey: [_, chainKey, account] }) => {
if (!account || !possibilities) {
return [];
}
Expand Down
4 changes: 2 additions & 2 deletions packages/widget/src/i18n/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@
"from": "I'd like to swap",
"to": "And receive to",
"max": "Max",
"maxAmount": "({{value, number}})",
"price": "\u2248 {{value, currency(currency: USD)}}",
"maxAmount": "({{amount}})",
"price": "\u2248 {{price, currency(currency: USD)}}",
"sendToRecipient": "Send to recipient",
"routePriority": {
"title": "Route priority",
Expand Down
22 changes: 7 additions & 15 deletions packages/widget/src/pages/SwapPage/SwapPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,7 @@ import { SettingsDrawer } from '../../components/SettingsDrawer';
import { SwapButton } from '../../components/SwapButton';
import { SwapChainButton } from '../../components/SwapChainButton';
import { SwapInput } from '../../components/SwapInput';
import {
SwapFromInputAdornment,
SwapToInputAdornment,
} from '../../components/SwapInputAdornment';
import { SwapInputAdornment } from '../../components/SwapInputAdornment';
import { SwapStepper } from '../../components/SwapStepper';
import { Switch } from '../../components/Switch';
import {
Expand All @@ -38,8 +35,8 @@ export const SwapPage: React.FC<SwapPageProps> = ({ settingsRef }) => {
} = useFormContext();
const drawerRef = useRef<SelectTokenDrawerBase>(null);

const handleChainButton = (type: SwapFormDirection) =>
drawerRef.current?.openDrawer(type);
const handleChainButton = (formType: SwapFormDirection) =>
drawerRef.current?.openDrawer(formType);

useEffect(() => {
register(SwapFormKey.FromToken);
Expand All @@ -53,19 +50,14 @@ export const SwapPage: React.FC<SwapPageProps> = ({ settingsRef }) => {
{t(`swap.from`)}
</Typography>
<Box>
<SwapChainButton onClick={handleChainButton} type="from" />
<SwapChainButton onClick={handleChainButton} formType="from" />
<FormControl variant="standard" disabled={isSubmitting} fullWidth>
<SwapInput
type="number"
size="small"
defaultValue={0}
autoComplete="off"
endAdornment={
<SwapFromInputAdornment
maxAmount={98700.34021}
price={1300.0}
/>
}
endAdornment={<SwapInputAdornment formType="from" />}
aria-describedby=""
inputProps={{
min: 0,
Expand Down Expand Up @@ -93,14 +85,14 @@ export const SwapPage: React.FC<SwapPageProps> = ({ settingsRef }) => {
<SwapVertIcon sx={{ alignSelf: 'center', padding: '0 16px' }} />
</Box>
<Box>
<SwapChainButton onClick={handleChainButton} type="to" />
<SwapChainButton onClick={handleChainButton} formType="to" />
<FormControl variant="standard" fullWidth disabled={isSubmitting}>
<SwapInput
type="number"
size="small"
defaultValue={0}
autoComplete="off"
endAdornment={<SwapToInputAdornment price={1300.0} />}
endAdornment={<SwapInputAdornment formType="to" />}
aria-describedby=""
inputProps={{
min: 0,
Expand Down
Loading

0 comments on commit be17b6d

Please sign in to comment.