MigrationListItem.tsx
1 import { ExclamationCircleIcon } from '@heroicons/react/outline'; 2 import { ArrowNarrowRightIcon, CheckIcon } from '@heroicons/react/solid'; 3 import CheckRoundedIcon from '@mui/icons-material/CheckRounded'; 4 import { Box, Button, SvgIcon, Typography, useMediaQuery, useTheme } from '@mui/material'; 5 import { IncentivesCard } from 'src/components/incentives/IncentivesCard'; 6 import { MigrationDisabledTooltip } from 'src/components/infoTooltips/MigrationDisabledTooltip'; 7 import { IsolatedEnabledBadge } from 'src/components/isolationMode/IsolatedBadge'; 8 import { ListColumn } from 'src/components/lists/ListColumn'; 9 import { ListItem } from 'src/components/lists/ListItem'; 10 import { FormattedNumber } from 'src/components/primitives/FormattedNumber'; 11 import { ROUTES } from 'src/components/primitives/Link'; 12 import { NoData } from 'src/components/primitives/NoData'; 13 import { TokenIcon } from 'src/components/primitives/TokenIcon'; 14 import { ComputedUserReserveData } from 'src/hooks/app-data-provider/useAppDataProvider'; 15 import { useRootStore } from 'src/store/root'; 16 import { MigrationDisabled, V3Rates } from 'src/store/v3MigrationSelectors'; 17 import { useShallow } from 'zustand/shallow'; 18 19 import { MigrationListItemToggler } from './MigrationListItemToggler'; 20 import { MigrationListMobileItem } from './MigrationListMobileItem'; 21 import { StETHMigrationWarning } from './StETHMigrationWarning'; 22 23 interface MigrationListItemProps { 24 checked: boolean; 25 amount: string; 26 amountInUSD: string; 27 onCheckboxClick: () => void; 28 disabled?: MigrationDisabled; 29 enabledAsCollateral?: boolean; 30 canBeEnforced?: boolean; 31 enableAsCollateral?: () => void; 32 isIsolated?: boolean; 33 borrowApyType?: string; 34 userReserve: ComputedUserReserveData; 35 v3Rates?: V3Rates; 36 enteringIsolation: boolean; 37 userControlledCollateral?: boolean; 38 isSupplyList: boolean; 39 } 40 41 export const MigrationListItem = ({ 42 checked, 43 amount, 44 amountInUSD, 45 onCheckboxClick, 46 enabledAsCollateral, 47 disabled, 48 enableAsCollateral, 49 isIsolated, 50 enteringIsolation, 51 borrowApyType, 52 userReserve, 53 v3Rates, 54 userControlledCollateral, 55 canBeEnforced, 56 isSupplyList, 57 }: MigrationListItemProps) => { 58 const theme = useTheme(); 59 const [currentMarket, currentMarketData] = useRootStore( 60 useShallow((store) => [store.currentMarket, store.currentMarketData]) 61 ); 62 const isMobile = useMediaQuery(theme.breakpoints.down(1125)); 63 64 const baseColor = disabled === undefined ? 'text.primary' : 'text.muted'; 65 const baseColorSecondary = disabled === undefined ? 'text.secondary' : 'text.muted'; 66 67 const loadingRates = v3Rates?.ltv === undefined && v3Rates?.liquidationThreshold === undefined; 68 69 const v2APY = borrowApyType 70 ? userReserve.reserve.variableBorrowAPY 71 : userReserve.reserve.supplyAPY; 72 const v2Incentives = borrowApyType 73 ? userReserve.reserve.vIncentivesData 74 : userReserve.reserve.aIncentivesData; 75 const v3APY = borrowApyType ? v3Rates?.variableBorrowAPY || '-1' : v3Rates?.supplyAPY || '-1'; 76 const v3Incentives = borrowApyType 77 ? v3Rates?.vIncentivesData || [] 78 : v3Rates?.aIncentivesData || []; 79 80 const showCollateralToggle = userControlledCollateral && isIsolated && canBeEnforced; 81 82 if (isMobile) 83 return ( 84 <MigrationListMobileItem 85 checked={checked} 86 amount={amount} 87 amountInUSD={amountInUSD} 88 onCheckboxClick={onCheckboxClick} 89 enabledAsCollateral={enabledAsCollateral} 90 disabled={disabled} 91 enableAsCollateral={enableAsCollateral} 92 isIsolated={isIsolated} 93 enteringIsolation={enteringIsolation} 94 borrowApyType={borrowApyType} 95 userReserve={userReserve} 96 v3Rates={v3Rates} 97 showCollateralToggle={showCollateralToggle} 98 isSupplyList={isSupplyList} 99 /> 100 ); 101 return ( 102 <ListItem 103 sx={{ flexDirection: 'column', pl: 0 }} 104 data-cy={`migration-${borrowApyType !== undefined ? 'borrow-' + borrowApyType : 'supply'}-${ 105 userReserve.reserve.symbol 106 }`} 107 > 108 <Box 109 sx={{ display: 'flex', flexDirection: 'row', width: '100%', alignItems: 'center', my: 4 }} 110 > 111 <ListColumn align="center" maxWidth={64} minWidth={64}> 112 <Box 113 sx={(theme) => ({ 114 border: `2px solid ${ 115 disabled !== undefined 116 ? theme.palette.action.disabled 117 : theme.palette.text.secondary 118 }`, 119 background: 120 disabled !== undefined 121 ? theme.palette.background.disabled 122 : checked 123 ? theme.palette.text.secondary 124 : theme.palette.background.paper, 125 width: 16, 126 height: 16, 127 borderRadius: '2px', 128 '&:hover': { 129 cursor: disabled !== undefined ? 'not-allowed' : 'pointer', 130 }, 131 display: 'flex', 132 alignItems: 'center', 133 justifyContent: 'center', 134 })} 135 onClick={disabled !== undefined ? undefined : onCheckboxClick} 136 data-cy={`migration-checkbox`} 137 > 138 {disabled === undefined && ( 139 <SvgIcon sx={{ fontSize: '14px', color: 'background.paper' }}> 140 <CheckIcon /> 141 </SvgIcon> 142 )} 143 </Box> 144 </ListColumn> 145 146 <ListColumn isRow maxWidth={250} minWidth={170}> 147 <TokenIcon symbol={userReserve.reserve.iconSymbol} fontSize="large" /> 148 149 <Box sx={{ pl: '12px', overflow: 'hidden', display: 'flex' }}> 150 <Typography variant="subheader1" color={baseColor} noWrap sx={{ pr: 1 }}> 151 {userReserve.reserve.symbol} 152 </Typography> 153 {disabled !== undefined && ( 154 <MigrationDisabledTooltip 155 dashboardLink={ROUTES.dashboard + '/?marketName=' + currentMarket + '_v3'} 156 marketName={currentMarketData.marketTitle} 157 warningType={disabled} 158 isolatedV3={!enteringIsolation} 159 /> 160 )} 161 </Box> 162 </ListColumn> 163 164 <ListColumn> 165 <Box sx={{ display: 'flex', alignItems: 'center' }}> 166 <IncentivesCard 167 value={v2APY} 168 symbol={userReserve.reserve.symbol} 169 incentives={v2Incentives} 170 variant="main14" 171 color={baseColor} 172 market={currentMarket} 173 /> 174 <SvgIcon sx={{ px: 1.5 }}> 175 <ArrowNarrowRightIcon 176 fontSize="14px" 177 color={ 178 disabled === undefined ? theme.palette.text.primary : theme.palette.text.muted 179 } 180 /> 181 </SvgIcon> 182 <IncentivesCard 183 value={v3APY} 184 symbol={userReserve.reserve.symbol} 185 incentives={v3Incentives} 186 variant="main14" 187 color={baseColor} 188 market={currentMarket} 189 /> 190 </Box> 191 </ListColumn> 192 193 {!!enableAsCollateral && ( 194 <ListColumn> 195 <Box sx={{ display: 'flex', alignItems: 'center' }}> 196 {userReserve.usageAsCollateralEnabledOnUser && 197 userReserve.reserve.reserveLiquidationThreshold !== '0' ? ( 198 <CheckRoundedIcon fontSize="small" color="success" /> 199 ) : ( 200 <NoData variant="main14" color={baseColorSecondary} /> 201 )} 202 203 <SvgIcon sx={{ px: 1.5 }}> 204 <ArrowNarrowRightIcon 205 fontSize="14px" 206 color={ 207 disabled === undefined ? theme.palette.text.primary : theme.palette.text.muted 208 } 209 /> 210 </SvgIcon> 211 212 {showCollateralToggle ? ( 213 <MigrationListItemToggler 214 enableAsCollateral={enableAsCollateral} 215 enabledAsCollateral={enabledAsCollateral} 216 /> 217 ) : !enabledAsCollateral ? ( 218 <NoData variant="main14" color={baseColorSecondary} /> 219 ) : isIsolated ? ( 220 <Box 221 sx={{ 222 display: 'flex', 223 flexDirection: 'column', 224 justifyContent: 'center', 225 alignItems: 'center', 226 }} 227 > 228 <SvgIcon sx={{ color: 'warning.main', fontSize: '20px' }}> 229 <ExclamationCircleIcon /> 230 </SvgIcon> 231 <IsolatedEnabledBadge /> 232 </Box> 233 ) : ( 234 <CheckRoundedIcon fontSize="small" color="success" /> 235 )} 236 </Box> 237 </ListColumn> 238 )} 239 {isSupplyList && 240 (loadingRates ? ( 241 <ListColumn> 242 <NoData variant="main14" color="text.secondary" /> 243 </ListColumn> 244 ) : ( 245 <ListColumn> 246 <Box sx={{ display: 'flex' }}> 247 <FormattedNumber 248 value={userReserve.reserve.formattedBaseLTVasCollateral} 249 percent 250 variant="secondary14" 251 color={baseColor} 252 /> 253 <SvgIcon sx={{ px: 1.5 }}> 254 <ArrowNarrowRightIcon 255 fontSize="14px" 256 color={ 257 disabled === undefined ? theme.palette.text.primary : theme.palette.text.muted 258 } 259 /> 260 </SvgIcon> 261 <FormattedNumber 262 value={v3Rates?.ltv || 0} 263 percent 264 variant="secondary14" 265 color={baseColor} 266 /> 267 </Box> 268 </ListColumn> 269 ))} 270 271 {!!borrowApyType && ( 272 <ListColumn> 273 <Box sx={{ display: 'flex' }}> 274 <Button 275 variant="outlined" 276 size="small" 277 sx={{ width: '50px', background: theme.palette.background.paper }} 278 disabled 279 > 280 <Typography variant="buttonS" color={baseColor}> 281 {borrowApyType} 282 </Typography> 283 </Button> 284 <SvgIcon sx={{ px: 1.5 }}> 285 <ArrowNarrowRightIcon 286 fontSize="14px" 287 color={ 288 disabled === undefined ? theme.palette.text.primary : theme.palette.text.muted 289 } 290 /> 291 </SvgIcon> 292 <Button 293 variant="outlined" 294 size="small" 295 sx={{ width: '50px', background: theme.palette.background.paper }} 296 disabled 297 > 298 <Typography variant="buttonS" color={baseColor}> 299 Variable 300 </Typography> 301 </Button> 302 </Box> 303 </ListColumn> 304 )} 305 306 {!isSupplyList && 307 (loadingRates ? ( 308 <ListColumn> 309 <NoData variant="main14" color="text.secondary" /> 310 </ListColumn> 311 ) : ( 312 <ListColumn> 313 <Box sx={{ display: 'flex' }}> 314 <FormattedNumber 315 value={userReserve.reserve.formattedReserveLiquidationThreshold} 316 percent 317 variant="secondary14" 318 color={baseColor} 319 /> 320 <SvgIcon sx={{ px: 1.5 }}> 321 <ArrowNarrowRightIcon 322 fontSize="14px" 323 color={ 324 disabled === undefined ? theme.palette.text.primary : theme.palette.text.muted 325 } 326 /> 327 </SvgIcon> 328 <FormattedNumber 329 value={v3Rates?.liquidationThreshold ?? -1} 330 percent 331 variant="secondary14" 332 color={baseColor} 333 /> 334 </Box> 335 </ListColumn> 336 ))} 337 338 <ListColumn> 339 <FormattedNumber value={amount} variant="secondary14" color={baseColor} /> 340 <FormattedNumber 341 value={amountInUSD} 342 variant="secondary12" 343 color={baseColor} 344 symbol="USD" 345 symbolsColor={baseColor} 346 /> 347 </ListColumn> 348 </Box> 349 350 {userReserve.reserve.symbol === 'stETH' && ( 351 <Box sx={{ pl: '16px', width: '100%' }}> 352 <StETHMigrationWarning 353 v2Price={userReserve.reserve.priceInUSD} 354 v2Amount={amount} 355 v3Price={v3Rates?.priceInUSD} 356 /> 357 </Box> 358 )} 359 </ListItem> 360 ); 361 };