/ src / components / MarketSwitcher.tsx
MarketSwitcher.tsx
  1  import { ChevronDownIcon } from '@heroicons/react/outline';
  2  import { Trans } from '@lingui/macro';
  3  import {
  4    Box,
  5    BoxProps,
  6    ListItemText,
  7    MenuItem,
  8    SvgIcon,
  9    TextField,
 10    Tooltip,
 11    Typography,
 12    useMediaQuery,
 13    useTheme,
 14  } from '@mui/material';
 15  import React, { useState } from 'react';
 16  import { useRootStore } from 'src/store/root';
 17  import { BaseNetworkConfig } from 'src/ui-config/networksConfig';
 18  import { DASHBOARD } from 'src/utils/mixPanelEvents';
 19  import { useShallow } from 'zustand/shallow';
 20  
 21  import {
 22    availableMarkets,
 23    CustomMarket,
 24    ENABLE_TESTNET,
 25    MarketDataType,
 26    marketsData,
 27    networkConfigs,
 28    STAGING_ENV,
 29  } from '../utils/marketsAndNetworksConfig';
 30  import StyledToggleButton from './StyledToggleButton';
 31  import StyledToggleButtonGroup from './StyledToggleButtonGroup';
 32  
 33  export const getMarketInfoById = (marketId: CustomMarket) => {
 34    const market: MarketDataType = marketsData[marketId as CustomMarket];
 35    const network: BaseNetworkConfig = networkConfigs[market.chainId];
 36    const logo = market.logo || network.networkLogoPath;
 37  
 38    return { market, logo };
 39  };
 40  
 41  export const getMarketHelpData = (marketName: string) => {
 42    const testChains = [
 43      'Görli',
 44      'Ropsten',
 45      'Mumbai',
 46      'Sepolia',
 47      'Fuji',
 48      'Testnet',
 49      'Kovan',
 50      'Rinkeby',
 51    ];
 52    const arrayName = marketName.split(' ');
 53    const testChainName = arrayName.filter((el) => testChains.indexOf(el) > -1);
 54    const marketTitle = arrayName.filter((el) => !testChainName.includes(el)).join(' ');
 55  
 56    return {
 57      name: marketTitle,
 58      testChainName: testChainName[0],
 59    };
 60  };
 61  
 62  export type Market = {
 63    marketTitle: string;
 64    networkName: string;
 65    networkLogo: string;
 66    selected?: boolean;
 67  };
 68  
 69  type MarketLogoProps = {
 70    size: number;
 71    logo: string;
 72    testChainName?: string;
 73    sx?: BoxProps;
 74  };
 75  
 76  export const MarketLogo = ({ size, logo, testChainName, sx }: MarketLogoProps) => {
 77    return (
 78      <Box sx={{ mr: 2, width: size, height: size, position: 'relative', ...sx }}>
 79        <img src={logo} alt="" width="100%" height="100%" />
 80  
 81        {testChainName && (
 82          <Tooltip title={testChainName} arrow>
 83            <Box
 84              sx={{
 85                bgcolor: '#29B6F6',
 86                width: '16px',
 87                height: '16px',
 88                borderRadius: '50%',
 89                color: 'common.white',
 90                fontSize: '12px',
 91                lineHeight: '16px',
 92                display: 'flex',
 93                alignItems: 'center',
 94                justifyContent: 'center',
 95                position: 'absolute',
 96                right: '-2px',
 97                bottom: '-2px',
 98              }}
 99            >
100              {testChainName.split('')[0]}
101            </Box>
102          </Tooltip>
103        )}
104      </Box>
105    );
106  };
107  
108  enum SelectedMarketVersion {
109    V2,
110    V3,
111  }
112  
113  export const MarketSwitcher = () => {
114    const [selectedMarketVersion, setSelectedMarketVersion] = useState<SelectedMarketVersion>(
115      SelectedMarketVersion.V3
116    );
117    const theme = useTheme();
118    const upToLG = useMediaQuery(theme.breakpoints.up('lg'));
119    const downToXSM = useMediaQuery(theme.breakpoints.down('xsm'));
120    const [trackEvent, currentMarket, setCurrentMarket] = useRootStore(
121      useShallow((store) => [store.trackEvent, store.currentMarket, store.setCurrentMarket])
122    );
123  
124    const isV3MarketsAvailable = availableMarkets
125      .map((marketId: CustomMarket) => {
126        const { market } = getMarketInfoById(marketId);
127  
128        return market.v3;
129      })
130      .some((item) => !!item);
131  
132    const handleMarketSelect = (e: React.ChangeEvent<HTMLInputElement>) => {
133      trackEvent(DASHBOARD.CHANGE_MARKET, { market: e.target.value });
134      setCurrentMarket(e.target.value as unknown as CustomMarket);
135    };
136  
137    const marketBlurbs: { [key: string]: JSX.Element } = {
138      proto_mainnet_v3: (
139        <Trans>Main Ethereum market with the largest selection of assets and yield options</Trans>
140      ),
141      proto_lido_v3: (
142        <Trans>Optimized for efficiency and risk by supporting blue-chip collateral assets</Trans>
143      ),
144    };
145  
146    return (
147      <TextField
148        select
149        aria-label="select market"
150        data-cy="marketSelector"
151        value={currentMarket}
152        onChange={handleMarketSelect}
153        sx={{
154          mr: 2,
155          '& .MuiOutlinedInput-notchedOutline': {
156            border: 'none',
157          },
158        }}
159        SelectProps={{
160          native: false,
161          className: 'MarketSwitcher__select',
162          IconComponent: () => null,
163          renderValue: (marketId) => {
164            const { market, logo } = getMarketInfoById(marketId as CustomMarket);
165  
166            return (
167              <Box>
168                {/* Main Row with Market Name */}
169                <Box sx={{ display: 'flex', alignItems: 'center' }}>
170                  <MarketLogo
171                    size={upToLG ? 32 : 28}
172                    logo={logo}
173                    testChainName={getMarketHelpData(market.marketTitle).testChainName}
174                  />
175                  <Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
176                    <Typography
177                      variant={upToLG ? 'display1' : 'h1'}
178                      sx={{
179                        fontSize: downToXSM ? '1.55rem' : undefined,
180                        color: 'common.white',
181                        mr: 1,
182                      }}
183                    >
184                      {getMarketHelpData(market.marketTitle).name} {market.isFork ? 'Fork' : ''}
185                      {upToLG &&
186                      (currentMarket === 'proto_mainnet_v3' || currentMarket === 'proto_lido_v3')
187                        ? 'Instance'
188                        : ' Market'}
189                    </Typography>
190                    {market.v3 ? (
191                      <Box sx={{ display: 'flex', alignItems: 'center' }}>
192                        <Box
193                          sx={{
194                            color: '#fff',
195                            px: 2,
196                            borderRadius: '12px',
197                            background: (theme) => theme.palette.gradients.aaveGradient,
198                            display: 'flex',
199                            alignItems: 'center',
200                          }}
201                        >
202                          <Typography variant="subheader2">V3</Typography>
203                        </Box>
204                        <SvgIcon
205                          fontSize="medium"
206                          sx={{
207                            ml: 1,
208                            color: '#F1F1F3',
209                          }}
210                        >
211                          <ChevronDownIcon />
212                        </SvgIcon>
213                      </Box>
214                    ) : (
215                      <Box sx={{ display: 'flex', alignItems: 'center' }}>
216                        <Box
217                          sx={{
218                            color: '#A5A8B6',
219                            px: 2,
220                            borderRadius: '12px',
221                            backgroundColor: '#383D51',
222                            display: 'flex',
223                            alignItems: 'center',
224                          }}
225                        >
226                          <Typography variant="subheader2">V2</Typography>
227                        </Box>
228                        <SvgIcon
229                          fontSize="medium"
230                          sx={{
231                            ml: 1,
232                            color: '#F1F1F3',
233                          }}
234                        >
235                          <ChevronDownIcon />
236                        </SvgIcon>
237                      </Box>
238                    )}
239                  </Box>
240                </Box>
241  
242                {marketBlurbs[currentMarket] && (
243                  <Typography
244                    sx={{
245                      color: 'common.white',
246                      mt: 0.5,
247                      fontSize: '0.85rem',
248                      wordWrap: 'break-word',
249                      whiteSpace: 'normal',
250                      lineHeight: 1.3,
251                      maxWidth: '100%',
252                    }}
253                  >
254                    {marketBlurbs[currentMarket]}
255                  </Typography>
256                )}
257              </Box>
258            );
259          },
260  
261          sx: {
262            '&.MarketSwitcher__select .MuiSelect-outlined': {
263              pl: 0,
264              py: 0,
265              backgroundColor: 'transparent !important',
266            },
267            '.MuiSelect-icon': { color: '#F1F1F3' },
268          },
269          MenuProps: {
270            anchorOrigin: {
271              vertical: 'bottom',
272              horizontal: 'right',
273            },
274            transformOrigin: {
275              vertical: 'top',
276              horizontal: 'right',
277            },
278            PaperProps: {
279              style: {
280                minWidth: 240,
281              },
282              variant: 'outlined',
283              elevation: 0,
284            },
285          },
286        }}
287      >
288        <Box>
289          <Typography variant="subheader2" color="text.secondary" sx={{ px: 4, pt: 2 }}>
290            <Trans>
291              {ENABLE_TESTNET || STAGING_ENV ? 'Select Aave Testnet Market' : 'Select Aave Market'}
292            </Trans>
293          </Typography>
294        </Box>
295        {isV3MarketsAvailable && (
296          <Box sx={{ mx: '18px', display: 'flex', justifyContent: 'center' }}>
297            <StyledToggleButtonGroup
298              value={selectedMarketVersion}
299              exclusive
300              onChange={(_, value) => {
301                if (value !== null) {
302                  setSelectedMarketVersion(value);
303                }
304              }}
305              sx={{
306                width: '100%',
307                height: '36px',
308                background: theme.palette.primary.main,
309                border: `1px solid ${
310                  theme.palette.mode === 'dark' ? 'rgba(235, 235, 237, 0.12)' : '#1B2030'
311                }`,
312                borderRadius: '6px',
313                marginTop: '16px',
314                marginBottom: '12px',
315                padding: '2px',
316              }}
317            >
318              <StyledToggleButton
319                value={SelectedMarketVersion.V3}
320                data-cy={`markets_switch_button_v3`}
321                sx={{
322                  backgroundColor: theme.palette.mode === 'dark' ? '#EAEBEF' : '#383D51',
323                  '&.Mui-selected, &.Mui-selected:hover': {
324                    backgroundColor: theme.palette.mode === 'dark' ? '#292E41' : '#FFFFFF',
325                    boxShadow: '0px 1px 0px rgba(0, 0, 0, 0.05)',
326                  },
327                  borderRadius: '4px',
328                }}
329              >
330                <Typography
331                  variant="buttonM"
332                  sx={
333                    selectedMarketVersion === SelectedMarketVersion.V3
334                      ? {
335                          backgroundImage: (theme) => theme.palette.gradients.aaveGradient,
336                          backgroundClip: 'text',
337                          color: 'transparent',
338                        }
339                      : {
340                          color: theme.palette.mode === 'dark' ? '#0F121D' : '#FFFFFF',
341                        }
342                  }
343                >
344                  <Trans>Version 3</Trans>
345                </Typography>
346              </StyledToggleButton>
347              <StyledToggleButton
348                value={SelectedMarketVersion.V2}
349                data-cy={`markets_switch_button_v2`}
350                sx={{
351                  backgroundColor: theme.palette.mode === 'dark' ? '#EAEBEF' : '#383D51',
352                  '&.Mui-selected, &.Mui-selected:hover': {
353                    backgroundColor: theme.palette.mode === 'dark' ? '#292E41' : '#FFFFFF',
354                    boxShadow: '0px 1px 0px rgba(0, 0, 0, 0.05)',
355                  },
356                  borderRadius: '4px',
357                }}
358              >
359                <Typography
360                  variant="buttonM"
361                  sx={
362                    selectedMarketVersion === SelectedMarketVersion.V2
363                      ? {
364                          backgroundImage: (theme) => theme.palette.gradients.aaveGradient,
365                          backgroundClip: 'text',
366                          color: 'transparent',
367                        }
368                      : {
369                          color: theme.palette.mode === 'dark' ? '#0F121D' : '#FFFFFF',
370                        }
371                  }
372                >
373                  <Trans>Version 2</Trans>
374                </Typography>
375              </StyledToggleButton>
376            </StyledToggleButtonGroup>
377          </Box>
378        )}
379        {availableMarkets.map((marketId: CustomMarket) => {
380          const { market, logo } = getMarketInfoById(marketId);
381          const marketNaming = getMarketHelpData(market.marketTitle);
382          return (
383            <MenuItem
384              key={marketId}
385              data-cy={`marketSelector_${marketId}`}
386              value={marketId}
387              sx={{
388                '.MuiListItemIcon-root': { minWidth: 'unset' },
389                display:
390                  (market.v3 && selectedMarketVersion === SelectedMarketVersion.V2) ||
391                  (!market.v3 && selectedMarketVersion === SelectedMarketVersion.V3)
392                    ? 'none'
393                    : 'flex',
394              }}
395            >
396              <MarketLogo size={32} logo={logo} testChainName={marketNaming.testChainName} />
397              <ListItemText sx={{ mr: 0 }}>
398                {marketNaming.name} {market.isFork ? 'Fork' : ''}
399              </ListItemText>
400              <ListItemText sx={{ textAlign: 'right' }}>
401                <Typography color="text.muted" variant="description">
402                  {marketNaming.testChainName}
403                </Typography>
404              </ListItemText>
405            </MenuItem>
406          );
407        })}
408      </TextField>
409    );
410  };