Skip to content

Commit 369cac0

Browse files
Merge pull request #1000 from curvefi/feat/llama-breakpoints
feat: llamalend breakpoint improvements
2 parents 3cc1dfc + 8f59730 commit 369cac0

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+494
-459
lines changed

apps/main/src/dao/components/ComboBoxSelectGauge/index.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ import { useUserGaugeWeightVotesQuery } from '@/dao/entities/user-gauge-weight-v
99
import useStore from '@/dao/store/useStore'
1010
import { GaugeFormattedData } from '@/dao/types/dao.types'
1111
import { delayAction } from '@/dao/utils'
12-
import useMediaQuery from '@mui/material/useMediaQuery'
1312
import ModalDialog from '@ui/Dialog'
13+
import { useIsMobile } from '@ui-kit/hooks/useBreakpoints'
1414
import { t } from '@ui-kit/lib/i18n'
1515
import { Chain } from '@ui-kit/utils/network'
1616

@@ -35,7 +35,7 @@ const ComboBoxGauges = ({
3535
const setSelectedGauge = useStore((state) => state.gauges.setSelectedGauge)
3636
const setStateByKey = useStore((state) => state.gauges.setStateByKey)
3737
const gaugeMapper = useStore((state) => state.gauges.gaugeMapper)
38-
const isMobile = useMediaQuery((t) => t.breakpoints.down('tablet'))
38+
const isMobile = useIsMobile()
3939

4040
const { data: userGaugeWeightVotes } = useUserGaugeWeightVotesQuery({
4141
chainId: Chain.Ethereum, // DAO is only used on mainnet

apps/main/src/dex/components/PageCreatePool/ConfirmModal/ModalDialog.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,12 @@ import type { AriaOverlayProps } from 'react-aria'
44
import type { OverlayTriggerState } from 'react-stately'
55
import styled from 'styled-components'
66
import useStore from '@/dex/store/useStore'
7-
import useMediaQuery from '@mui/material/useMediaQuery'
87
import type { AriaDialogProps } from '@react-types/dialog'
98
import Box from '@ui/Box'
109
import Icon from '@ui/Icon'
1110
import IconButton from '@ui/IconButton'
1211
import { breakpoints } from '@ui/utils/responsive'
12+
import { useIsMobile } from '@ui-kit/hooks/useBreakpoints'
1313

1414
interface Props extends AriaOverlayProps, AriaDialogProps {
1515
footerContent?: ReactNode
@@ -38,7 +38,7 @@ const ModalDialog = ({
3838
const { titleProps } = useDialog(props, modalRef)
3939
usePreventScroll({ isDisabled: false }) // prevent scrolling while modal is open
4040

41-
const isMobile = useMediaQuery((t) => t.breakpoints.down('tablet'))
41+
const isMobile = useIsMobile()
4242
const isSmUp = useStore((state) => state.isSmUp)
4343

4444
const { buttonProps: closeButtonProps } = useButton(

apps/main/src/dex/components/PageCreatePool/TokensInPool/SelectTokenButton.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import { CreateToken } from '@/dex/components/PageCreatePool/types'
55
import useStore from '@/dex/store/useStore'
66
import { ChainId, CurveApi } from '@/dex/types/main.types'
77
import { delayAction } from '@/dex/utils'
8-
import useMediaQuery from '@mui/material/useMediaQuery'
98
import { useButton } from '@react-aria/button'
109
import { useFilter } from '@react-aria/i18n'
1110
import { useOverlayTriggerState } from '@react-stately/overlays'
@@ -15,6 +14,7 @@ import Checkbox from '@ui/Checkbox'
1514
import Spinner, { SpinnerWrapper } from '@ui/Spinner'
1615
import { Chip } from '@ui/Typography'
1716
import { TokenSelectorModal } from '@ui-kit/features/select-token/ui/modal/TokenSelectorModal'
17+
import { useIsMobile } from '@ui-kit/hooks/useBreakpoints'
1818
import { t } from '@ui-kit/lib/i18n'
1919
import { TokenIcon } from '@ui-kit/shared/ui/TokenIcon'
2020
import { type Address, filterTokens, shortenAddress } from '@ui-kit/utils'
@@ -46,7 +46,7 @@ const SelectTokenButton = ({
4646
const { buttonProps: openButtonProps } = useButton({ onPress: () => overlayTriggerState.open() }, openButtonRef)
4747
const { endsWith } = useFilter({ sensitivity: 'base' })
4848

49-
const isMobile = useMediaQuery((t) => t.breakpoints.down('tablet'))
49+
const isMobile = useIsMobile()
5050
const nativeToken = useStore((state) => state.networks.nativeToken[chainId])
5151

5252
const userAddedTokens = useStore((state) => state.createPool.userAddedTokens)

apps/main/src/dex/components/PoolLabel.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,11 @@ import usePoolAlert from '@/dex/hooks/usePoolAlert'
77
import useTokenAlert from '@/dex/hooks/useTokenAlert'
88
import useStore from '@/dex/store/useStore'
99
import { PoolData, PoolDataCache } from '@/dex/types/main.types'
10-
import useMediaQuery from '@mui/material/useMediaQuery'
1110
import AlertBox from '@ui/AlertBox'
1211
import Box from '@ui/Box'
1312
import { TooltipAlert as AlertTooltipIcon } from '@ui/Tooltip'
1413
import { Chip } from '@ui/Typography'
14+
import { useIsMobile } from '@ui-kit/hooks/useBreakpoints'
1515
import { TokenIcons } from '@ui-kit/shared/ui/TokenIcons'
1616

1717
type PoolListProps = {
@@ -43,7 +43,7 @@ const PoolLabel = ({ className = '', blockchainId, isVisible = true, poolData, p
4343

4444
const poolAlert = usePoolAlert(poolData?.pool.address, poolData?.hasVyperVulnerability)
4545
const tokenAlert = useTokenAlert(poolData?.tokenAddressesAll ?? [])
46-
const isMobile = useMediaQuery((t) => t.breakpoints.down('tablet'))
46+
const isMobile = useIsMobile()
4747
const searchedTerms = useStore((state) => state.poolList.searchedTerms)
4848

4949
const { quickViewValue, onClick } = poolListProps ?? {}

apps/main/src/loan/components/PageLlamaMarkets/LendingMarketsTable.tsx

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,17 @@ import { LendingMarketsFilters } from '@/loan/components/PageLlamaMarkets/Lendin
1010
import { LlamaMarketExpandedPanel } from '@/loan/components/PageLlamaMarkets/LlamaMarketExpandedPanel'
1111
import { MarketsFilterChips } from '@/loan/components/PageLlamaMarkets/MarketsFilterChips'
1212
import { type LlamaMarketsResult } from '@/loan/entities/llama-markets'
13-
import { useMediaQuery } from '@mui/material'
1413
import Stack from '@mui/material/Stack'
1514
import {
1615
ExpandedState,
1716
getCoreRowModel,
1817
getExpandedRowModel,
1918
getFilteredRowModel,
2019
getSortedRowModel,
20+
SortingState,
2121
useReactTable,
2222
} from '@tanstack/react-table'
23+
import { useIsMobile, useIsTablet } from '@ui-kit/hooks/useBreakpoints'
2324
import { useSortFromQueryString } from '@ui-kit/hooks/useSortFromQueryString'
2425
import { t } from '@ui-kit/lib/i18n'
2526
import { DataTable } from '@ui-kit/shared/ui/DataTable'
@@ -28,19 +29,19 @@ import { TableFilters, useColumnFilters } from '@ui-kit/shared/ui/DataTable/Tabl
2829
import { useVisibilitySettings } from '@ui-kit/shared/ui/DataTable/TableVisibilitySettingsPopover'
2930
import { SizesAndSpaces } from '@ui-kit/themes/design/1_sizes_spaces'
3031

31-
const { Spacing, MaxWidth, Sizing } = SizesAndSpaces
32+
const { Spacing, MaxWidth } = SizesAndSpaces
3233

3334
/**
3435
* Hook to manage the visibility of columns in the Llama Markets table.
3536
* The visibility on mobile is based on the sort field.
3637
* On larger devices, it uses the visibility settings that may be customized by the user.
3738
*/
38-
const useVisibility = (sortField: LlamaMarketColumnId, hasPositions: boolean | undefined) => {
39-
const isMobile = useMediaQuery((t) => t.breakpoints.down('tablet'))
39+
const useVisibility = (sorting: SortingState, hasPositions: boolean | undefined) => {
40+
const sortField = (sorting.length ? sorting : DEFAULT_SORT)[0].id as LlamaMarketColumnId
4041
const groups = useMemo(() => createLlamaMarketsColumnOptions(hasPositions), [hasPositions])
41-
const visibilitySettings = useVisibilitySettings(groups)
42+
const visibilitySettings = useVisibilitySettings(groups, LLAMA_MARKET_COLUMNS)
4243
const columnVisibility = useMemo(() => createLlamaMarketsMobileColumns(sortField), [sortField])
43-
return { ...visibilitySettings, ...(isMobile && { columnVisibility }) }
44+
return { sortField, ...visibilitySettings, ...(useIsMobile() && { columnVisibility }) }
4445
}
4546

4647
// todo: rename to LlamaMarketsTable
@@ -62,8 +63,7 @@ export const LendingMarketsTable = ({
6263
{ id: LlamaMarketColumnId.LiquidityUsd, value: [minLiquidity, undefined] },
6364
])
6465
const [sorting, onSortingChange] = useSortFromQueryString(DEFAULT_SORT)
65-
const sortField = (sorting.length ? sorting : DEFAULT_SORT)[0].id as LlamaMarketColumnId
66-
const { columnSettings, columnVisibility, toggleVisibility } = useVisibility(sortField, result?.hasPositions)
66+
const { columnSettings, columnVisibility, toggleVisibility, sortField } = useVisibility(sorting, result?.hasPositions)
6767
const [expanded, setExpanded] = useState<ExpandedState>({})
6868
const table = useReactTable({
6969
columns: LLAMA_MARKET_COLUMNS,
@@ -89,9 +89,9 @@ export const LendingMarketsTable = ({
8989
<DataTable
9090
table={table}
9191
headerHeight={headerHeight}
92-
rowSx={{ height: { ...Sizing['3xl'], mobile: 77 } }} // the 3xl is too small in mobile (64px)
9392
emptyText={isError ? t`Could not load markets` : t`No markets found`}
9493
expandedPanel={LlamaMarketExpandedPanel}
94+
shouldStickFirstColumn={useIsTablet() && !!hasPositions}
9595
>
9696
<TableFilters<LlamaMarketColumnId>
9797
title={t`Llamalend Markets`}

apps/main/src/loan/components/PageLlamaMarkets/LlamaMarketExpandedPanel.tsx

Lines changed: 83 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,67 +1,113 @@
11
import Link from 'next/link'
2+
import { useMemo } from 'react'
23
import { LineGraphCell } from '@/loan/components/PageLlamaMarkets/cells'
34
import { LlamaMarketColumnId } from '@/loan/components/PageLlamaMarkets/columns.enum'
45
import { FavoriteMarketButton } from '@/loan/components/PageLlamaMarkets/FavoriteMarketButton'
56
import { useUserMarketStats } from '@/loan/entities/llama-market-stats'
7+
import useStore from '@/loan/store/useStore'
8+
import { ArrowRight } from '@carbon/icons-react'
69
import Button from '@mui/material/Button'
10+
import CardHeader from '@mui/material/CardHeader'
11+
import Grid from '@mui/material/Grid2'
712
import Stack from '@mui/material/Stack'
13+
import Typography from '@mui/material/Typography'
14+
import { useIsTiny } from '@ui-kit/hooks/useBreakpoints'
815
import { t } from '@ui-kit/lib/i18n'
916
import { CopyIconButton } from '@ui-kit/shared/ui/CopyIconButton'
10-
import { ExpansionPanelSection } from '@ui-kit/shared/ui/DataTable/ExpansionPanelSection'
1117
import { type ExpandedPanel } from '@ui-kit/shared/ui/DataTable/ExpansionRow'
1218
import { Metric } from '@ui-kit/shared/ui/Metric'
19+
import { SizesAndSpaces } from '@ui-kit/themes/design/1_sizes_spaces'
1320
import type { LlamaMarket } from '../../entities/llama-markets'
1421

22+
const { Spacing } = SizesAndSpaces
23+
24+
function useMobileGraphSize() {
25+
const pageWidth = useStore((state) => state.layout.windowWidth)
26+
const isTiny = useIsTiny()
27+
return useMemo(() => ({ width: pageWidth ? pageWidth - (isTiny ? 20 : 40) : 300, height: 48 }), [pageWidth, isTiny])
28+
}
29+
1530
export const LlamaMarketExpandedPanel: ExpandedPanel<LlamaMarket> = ({ row: { original: market } }) => {
1631
const { data: earnings, error: earningsError } = useUserMarketStats(market, LlamaMarketColumnId.UserEarnings)
1732
const { data: deposited, error: depositedError } = useUserMarketStats(market, LlamaMarketColumnId.UserDeposited)
33+
const { leverage, utilizationPercent, liquidityUsd, userHasPosition, url, address, rates } = market
34+
const graphSize = useMobileGraphSize()
1835
return (
1936
<>
20-
<ExpansionPanelSection
21-
title={
22-
<Stack direction="row" gap={2}>
23-
{t`Market Details`}
24-
<CopyIconButton
25-
label={t`Copy market address`}
26-
copyText={market.address}
27-
confirmationText={t`Market address copied`}
28-
data-testid={`copy-market-address-${market.address}`}
29-
/>
30-
<FavoriteMarketButton address={market.address} />
31-
</Stack>
32-
}
33-
>
34-
<Metric label={t`7D Avg Borrow Rate`} value={market.rates.borrow} unit="percentage" />
35-
<LineGraphCell market={market} type="borrow" />
36-
{market.rates.lend && (
37-
<>
38-
<Metric label={t`7D Avg Supply Rate`} value={market.rates.lend} unit="percentage" />
39-
<LineGraphCell market={market} type="lend" />
40-
</>
37+
<Grid container spacing={Spacing.md}>
38+
<Grid size={12}>
39+
<CardHeader
40+
title={
41+
<Stack direction="row" gap={2} justifyContent="space-between">
42+
{t`Market Details`}
43+
<Stack direction="row">
44+
<CopyIconButton
45+
label={t`Copy market address`}
46+
copyText={address}
47+
confirmationText={t`Market address copied`}
48+
data-testid={`copy-market-address-${address}`}
49+
/>
50+
<FavoriteMarketButton address={address} />
51+
</Stack>
52+
</Stack>
53+
}
54+
sx={{ paddingInline: 0 }}
55+
></CardHeader>
56+
</Grid>
57+
<Grid size={6}>
58+
<Metric label={t`7D Avg Borrow Rate`} value={rates.borrow} unit="percentage" />
59+
</Grid>
60+
{leverage > 0 && (
61+
<Grid size={6}>
62+
<Metric label={t`Leverage 🔥`} value={leverage} unit="multiplier" />
63+
</Grid>
4164
)}
42-
<Metric label={t`Available Liquidity`} value={market.liquidityUsd} unit="dollar" />
43-
<Metric
44-
label={t`Utilization`}
45-
value={market.utilizationPercent}
46-
unit="percentage"
47-
testId="metric-utilizationPercent"
48-
decimals={2}
49-
/>
50-
</ExpansionPanelSection>
51-
{market.userHasPosition && (
52-
<ExpansionPanelSection title={t`Your Position`}>
53-
{earnings?.earnings != null && <Metric label={t`Earnings`} value={earnings.earnings} unit="dollar" />}
65+
<Grid size={6}>
66+
<Metric
67+
label={t`Utilization`}
68+
value={utilizationPercent}
69+
unit="percentage"
70+
testId="metric-utilizationPercent"
71+
decimals={2}
72+
/>
73+
</Grid>
74+
<Grid size={6}>
75+
<Metric label={t`Available Liquidity`} value={liquidityUsd} unit="dollar" />
76+
</Grid>
77+
<Grid size={12} data-testid="llama-market-graph">
78+
<Stack direction="column" alignItems="center">
79+
<Typography variant="bodyXsRegular" color="textTertiary" alignSelf="start">
80+
{t`7D Rate Chart`}
81+
</Typography>
82+
83+
<LineGraphCell market={market} type="borrow" graphSize={graphSize} />
84+
</Stack>
85+
</Grid>
86+
</Grid>
87+
{userHasPosition && (
88+
<Grid container spacing={Spacing.md}>
89+
<Grid size={12}>
90+
<CardHeader title={t`Your Position`} sx={{ paddingInline: 0 }}></CardHeader>
91+
</Grid>
92+
{earnings?.earnings != null && (
93+
<Grid size={6}>
94+
<Metric label={t`Earnings`} value={earnings.earnings} unit="dollar" />
95+
</Grid>
96+
)}
5497
{deposited?.deposited != null && (
55-
<Metric label={t`Supplied Amount`} value={deposited.deposited} unit="dollar" />
98+
<Grid size={6}>
99+
<Metric label={t`Supplied Amount`} value={deposited.deposited} unit="dollar" />
100+
</Grid>
56101
)}
57-
</ExpansionPanelSection>
102+
</Grid>
58103
)}
59104
<Button
60105
sx={{ flexGrow: 1, borderBlock: (t) => `1px solid ${t.design.Layer[1].Outline}` }}
61106
component={Link}
62-
href={market.url}
107+
href={url}
63108
color="ghost"
64109
data-testid="llama-market-go-to-market"
110+
endIcon={<ArrowRight />}
65111
>
66112
{t`Go To Market`}
67113
</Button>

apps/main/src/loan/components/PageLlamaMarkets/MarketsFilterChips.tsx

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import PersonIcon from '@mui/icons-material/Person'
88
import Grid from '@mui/material/Grid2'
99
import Stack from '@mui/material/Stack'
1010
import Typography from '@mui/material/Typography'
11-
import useMediaQuery from '@mui/material/useMediaQuery'
11+
import { useIsMobile } from '@ui-kit/hooks/useBreakpoints'
1212
import { t } from '@ui-kit/lib/i18n'
1313
import { HeartIcon } from '@ui-kit/shared/icons/HeartIcon'
1414
import { PointsIcon } from '@ui-kit/shared/icons/PointsIcon'
@@ -83,8 +83,6 @@ export const MarketsFilterChips = ({
8383
const [rewards, toggleRewards] = useToggleFilter(LlamaMarketColumnId.Rewards, props)
8484
const [marketTypes, toggleMarkets] = useMarketTypeFilter(props)
8585
const { address } = useAccount()
86-
const isMobile = useMediaQuery((t) => t.breakpoints.down('tablet'))
87-
8886
return (
8987
<Grid
9088
container
@@ -133,7 +131,7 @@ export const MarketsFilterChips = ({
133131
{...(address && { size: 12 })}
134132
extraMargin
135133
/>
136-
<GridItem {...(!isMobile && { alignRight: true })}>
134+
<GridItem {...(!useIsMobile() && { alignRight: true })}>
137135
<Stack direction="column">
138136
<Typography variant="bodyXsRegular">{t`Hidden Markets`}</Typography>
139137
<Typography variant="highlightS">{hiddenMarketCount}</Typography>

apps/main/src/loan/components/PageLlamaMarkets/Page.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import Box from '@mui/material/Box'
1818
import Skeleton from '@mui/material/Skeleton'
1919
import { useUserProfileStore } from '@ui-kit/features/user-profile'
2020
import { SMALL_POOL_TVL } from '@ui-kit/features/user-profile/store'
21+
import { useIsTiny } from '@ui-kit/hooks/useBreakpoints'
2122
import { logSuccess } from '@ui-kit/lib'
2223
import { SizesAndSpaces } from '@ui-kit/themes/design/1_sizes_spaces'
2324
import { Address } from '@ui-kit/utils'
@@ -72,7 +73,7 @@ export const LlamaMarketsPage = (props: CrvUsdServerData) => {
7273
const headerHeight = useHeaderHeight(bannerHeight)
7374
const showSkeleton = !data && (!isError || isLoading) // on initial render isLoading is still false
7475
return (
75-
<Box sx={{ marginBlockEnd: Spacing.xxl, marginInline: Spacing.md }}>
76+
<Box sx={{ marginBlockEnd: Spacing.xxl, ...(!useIsTiny() && { marginInline: Spacing.md }) }}>
7677
{showSkeleton ? (
7778
<Skeleton variant="rectangular" width={MaxWidth.table} height={ModalHeight.md.height} />
7879
) : (

apps/main/src/loan/components/PageLlamaMarkets/cells/LineGraphCell.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { t } from '@ui-kit/lib/i18n'
1010
import { DesignSystem } from '@ui-kit/themes/design'
1111
import { RateType, useSnapshots } from '../hooks/useSnapshots'
1212

13-
const graphSize = { width: 172, height: 48 }
13+
const defaultGraphSize = { width: 100, height: 48 }
1414

1515
/**
1616
* Get the color for the line graph. Will be green if the last value is higher than the first, red if lower, and blue if equal.
@@ -33,12 +33,13 @@ const calculateDomain =
3333
type RateCellProps = {
3434
market: LlamaMarket
3535
type: RateType
36+
graphSize?: typeof defaultGraphSize
3637
}
3738

3839
/**
3940
* Line graph cell that displays the average historical APY for a vault and a given type (borrow or lend).
4041
*/
41-
export const LineGraphCell = ({ market, type }: RateCellProps) => {
42+
export const LineGraphCell = ({ market, type, graphSize = defaultGraphSize }: RateCellProps) => {
4243
const ref = useRef<HTMLDivElement>(null)
4344
const entry = useIntersectionObserver(ref, { freezeOnceVisible: true })
4445
const { snapshots, snapshotKey, isLoading, rate, error } = useSnapshots(market, type, entry?.isIntersecting)

0 commit comments

Comments
 (0)