/ src / modules / markets / MarketAssetsListContainer.tsx
MarketAssetsListContainer.tsx
  1  import { API_ETH_MOCK_ADDRESS } from '@aave/contract-helpers';
  2  import { Trans } from '@lingui/macro';
  3  import { Box, Switch, Typography, useMediaQuery, useTheme } from '@mui/material';
  4  import { useState } from 'react';
  5  import { ListWrapper } from 'src/components/lists/ListWrapper';
  6  import { NoSearchResults } from 'src/components/NoSearchResults';
  7  import { Link } from 'src/components/primitives/Link';
  8  import { Warning } from 'src/components/primitives/Warning';
  9  import { TitleWithSearchBar } from 'src/components/TitleWithSearchBar';
 10  import { useAppDataContext } from 'src/hooks/app-data-provider/useAppDataProvider';
 11  import MarketAssetsList from 'src/modules/markets/MarketAssetsList';
 12  import { useRootStore } from 'src/store/root';
 13  import { fetchIconSymbolAndName } from 'src/ui-config/reservePatches';
 14  import { getGhoReserve, GHO_MINTING_MARKETS, GHO_SYMBOL } from 'src/utils/ghoUtilities';
 15  import { useShallow } from 'zustand/shallow';
 16  
 17  import { GENERAL } from '../../utils/mixPanelEvents';
 18  import { GhoBanner } from './Gho/GhoBanner';
 19  
 20  function shouldDisplayGhoBanner(marketTitle: string, searchTerm: string): boolean {
 21    // GHO banner is only displayed on markets where new GHO is mintable (i.e. Ethereum)
 22    // If GHO is listed as a reserve, then it will be displayed in the normal market asset list
 23    if (!GHO_MINTING_MARKETS.includes(marketTitle)) {
 24      return false;
 25    }
 26  
 27    if (!searchTerm) {
 28      return true;
 29    }
 30  
 31    const normalizedSearchTerm = searchTerm.toLowerCase().trim();
 32    return (
 33      normalizedSearchTerm.length <= 3 && GHO_SYMBOL.toLowerCase().includes(normalizedSearchTerm)
 34    );
 35  }
 36  
 37  export const MarketAssetsListContainer = () => {
 38    const { reserves, loading } = useAppDataContext();
 39    const [trackEvent, currentMarket, currentMarketData, currentNetworkConfig] = useRootStore(
 40      useShallow((store) => [
 41        store.trackEvent,
 42        store.currentMarket,
 43        store.currentMarketData,
 44        store.currentNetworkConfig,
 45      ])
 46    );
 47    const [searchTerm, setSearchTerm] = useState('');
 48    const { breakpoints } = useTheme();
 49    const sm = useMediaQuery(breakpoints.down('sm'));
 50  
 51    const ghoReserve = getGhoReserve(reserves);
 52    const displayGhoBanner = shouldDisplayGhoBanner(currentMarket, searchTerm);
 53  
 54    const filteredData = reserves
 55      // Filter out any non-active reserves
 56      .filter((res) => res.isActive)
 57      // Filter out GHO if the banner is being displayed
 58      .filter((res) => (displayGhoBanner ? res !== ghoReserve : true))
 59      // filter out any that don't meet search term criteria
 60      .filter((res) => {
 61        if (!searchTerm) return true;
 62        const term = searchTerm.toLowerCase().trim();
 63        return (
 64          res.symbol.toLowerCase().includes(term) ||
 65          res.name.toLowerCase().includes(term) ||
 66          res.underlyingAsset.toLowerCase().includes(term)
 67        );
 68      })
 69      // Transform the object for list to consume it
 70      .map((reserve) => ({
 71        ...reserve,
 72        ...(reserve.isWrappedBaseAsset
 73          ? fetchIconSymbolAndName({
 74              symbol: currentNetworkConfig.baseAssetSymbol,
 75              underlyingAsset: API_ETH_MOCK_ADDRESS.toLowerCase(),
 76            })
 77          : {}),
 78      }));
 79    // const marketFrozen = !reserves.some((reserve) => !reserve.isFrozen);
 80    // const showFrozenMarketWarning =
 81    //   marketFrozen && ['Fantom', 'Ethereum AMM'].includes(currentMarketData.marketTitle);
 82    const unfrozenReserves = filteredData.filter((r) => !r.isFrozen && !r.isPaused);
 83    const [showFrozenMarketsToggle, setShowFrozenMarketsToggle] = useState(false);
 84  
 85    const handleChange = () => {
 86      setShowFrozenMarketsToggle((prevState) => !prevState);
 87    };
 88  
 89    const frozenOrPausedReserves = filteredData.filter((r) => r.isFrozen || r.isPaused);
 90  
 91    return (
 92      <ListWrapper
 93        titleComponent={
 94          <TitleWithSearchBar
 95            onSearchTermChange={setSearchTerm}
 96            title={
 97              <>
 98                {currentMarketData.marketTitle} <Trans>assets</Trans>
 99              </>
100            }
101            searchPlaceholder={sm ? 'Search asset' : 'Search asset name, symbol, or address'}
102          />
103        }
104      >
105        {displayGhoBanner && (
106          <Box mb={4}>
107            <GhoBanner reserve={ghoReserve} />
108          </Box>
109        )}
110  
111        {/* Unfrozen assets list */}
112        <MarketAssetsList reserves={unfrozenReserves} loading={loading} />
113  
114        {/* Frozen or paused assets list */}
115        {frozenOrPausedReserves.length > 0 && (
116          <Box sx={{ mt: 10, px: { xs: 4, xsm: 6 } }}>
117            <Typography variant="h4" mb={4}>
118              <Trans>Show Frozen or paused assets</Trans>
119  
120              <Switch
121                checked={showFrozenMarketsToggle}
122                onChange={handleChange}
123                inputProps={{ 'aria-label': 'controlled' }}
124              />
125            </Typography>
126            {showFrozenMarketsToggle && (
127              <Warning severity="info">
128                <Trans>
129                  These assets are temporarily frozen or paused by Aave community decisions, meaning
130                  that further supply / borrow, or rate swap of these assets are unavailable.
131                  Withdrawals and debt repayments are allowed. Follow the{' '}
132                  <Link
133                    onClick={() => {
134                      trackEvent(GENERAL.EXTERNAL_LINK, {
135                        link: 'Frozen Market Markets Page',
136                        frozenMarket: currentNetworkConfig.name,
137                      });
138                    }}
139                    href="https://governance.aave.com"
140                    underline="always"
141                  >
142                    Aave governance forum
143                  </Link>{' '}
144                  for further updates.
145                </Trans>
146              </Warning>
147            )}
148          </Box>
149        )}
150        {showFrozenMarketsToggle && (
151          <MarketAssetsList reserves={frozenOrPausedReserves} loading={loading} />
152        )}
153  
154        {/* Show no search results message if nothing hits in either list */}
155        {!loading && filteredData.length === 0 && !displayGhoBanner && (
156          <NoSearchResults
157            searchTerm={searchTerm}
158            subtitle={
159              <Trans>
160                We couldn&apos;t find any assets related to your search. Try again with a different
161                asset name, symbol, or address.
162              </Trans>
163            }
164          />
165        )}
166      </ListWrapper>
167    );
168  };