/ src / modules / governance / RepresentativesInfoPanel.tsx
RepresentativesInfoPanel.tsx
  1  import { Representative, Rpresented } from '@aave/contract-helpers';
  2  import { PlusIcon } from '@heroicons/react/outline';
  3  import { ExternalLinkIcon } from '@heroicons/react/solid';
  4  import { Trans } from '@lingui/macro';
  5  import { Box, Button, IconButton, Paper, Stack, SvgIcon, Typography } from '@mui/material';
  6  import { CompactableTypography, CompactMode } from 'src/components/CompactableTypography';
  7  import { Link } from 'src/components/primitives/Link';
  8  import { useRepresentatives } from 'src/hooks/governance/useRepresentatives';
  9  // import { useIsContractAddress } from 'src/hooks/useIsContractAddress';
 10  import { useModalContext } from 'src/hooks/useModal';
 11  import { useRootStore } from 'src/store/root';
 12  import { networkConfigs } from 'src/ui-config/networksConfig';
 13  
 14  import { ZERO_ADDRESS } from './utils/formatProposal';
 15  
 16  // Setting up a representative is only useful for smart contract wallets.
 17  // If connected account is an EOA, this section is hidden.
 18  
 19  // If an account has representatives, then we assume that there is no need to set up a representative,
 20  // and it will show the addresses that have selected the current account to represent them.
 21  export const RepresentativesInfoPanel = () => {
 22    const { openGovRepresentatives } = useModalContext();
 23    const account = useRootStore((state) => state.account);
 24  
 25    const { data } = useRepresentatives(account);
 26  
 27    {
 28      //   const { data: isContractAddress, isFetching: fetchingIsContractAddress } =
 29      // useIsContractAddress(account);
 30    }
 31  
 32    const isAddressSelectedAsRepresentative = data?.Represented.some(
 33      (r) => r.votersRepresented.length > 0
 34    );
 35  
 36    // if (!isContractAddress) {
 37    //   return null;
 38    // }
 39  
 40    return (
 41      <Paper sx={{ mt: 2 }}>
 42        <Box sx={{ px: 6, pb: 6, pt: 4 }}>
 43          <Stack direction="row" alignItems="center" justifyContent="space-between">
 44            <Typography typography="h3">
 45              <Trans>Linked addresses</Trans>
 46            </Typography>
 47            {isAddressSelectedAsRepresentative ? null : (
 48              <Button onClick={() => openGovRepresentatives(data?.Representatives || [])}>
 49                <Typography typography="subheader1">
 50                  <Trans>Edit</Trans>
 51                </Typography>
 52              </Button>
 53            )}
 54          </Stack>
 55          <Stack gap={8} sx={{ mt: 2 }}>
 56            <Stack direction="column">
 57              <Typography variant="description" color="text.secondary">
 58                {isAddressSelectedAsRepresentative ? (
 59                  <Trans>
 60                    Representing smart contract wallet (ie. Safe) addresses on other chains.
 61                  </Trans>
 62                ) : (
 63                  <Trans>
 64                    Representative smart contract wallet (ie. Safe) addresses on other chains.
 65                  </Trans>
 66                )}
 67              </Typography>
 68            </Stack>
 69            <Stack alignItems="start" gap={6}>
 70              {isAddressSelectedAsRepresentative ? (
 71                <Representing representing={data?.Represented || []} />
 72              ) : (
 73                <Representatives
 74                  representatives={data?.Representatives || []}
 75                  onOpenRepresentatives={() => openGovRepresentatives(data?.Representatives || [])}
 76                />
 77              )}
 78            </Stack>
 79          </Stack>
 80        </Box>
 81      </Paper>
 82    );
 83  };
 84  
 85  const Representatives = ({
 86    representatives,
 87    onOpenRepresentatives,
 88  }: {
 89    representatives: Representative[];
 90    onOpenRepresentatives: () => void;
 91  }) => {
 92    return (
 93      <>
 94        {representatives.map((representative, i) => (
 95          <Stack gap={4} key={i} direction="column" alignItems="self-start">
 96            <Network
 97              networkLogoPath={networkConfigs[representative.chainId].networkLogoPath}
 98              networkName={networkConfigs[representative.chainId].name}
 99            />
100            {representative.representative === ZERO_ADDRESS ? (
101              <Stack direction="row" gap={1} alignItems="center">
102                <IconButton
103                  sx={(theme) => ({
104                    height: '24px',
105                    width: '24px',
106                    background: theme.palette.background.disabled,
107                  })}
108                  onClick={onOpenRepresentatives}
109                >
110                  <SvgIcon sx={{ p: 1 }}>
111                    <PlusIcon />
112                  </SvgIcon>
113                </IconButton>
114                <Typography variant="subheader1" color="text.muted">
115                  <Trans>Connect</Trans>
116                </Typography>
117              </Stack>
118            ) : (
119              <AddressLink
120                explorerLink={networkConfigs[representative.chainId].explorerLink}
121                address={representative.representative}
122              />
123            )}
124          </Stack>
125        ))}
126      </>
127    );
128  };
129  
130  const Representing = ({ representing }: { representing: Rpresented[] }) => {
131    return (
132      <>
133        {representing.map((representing, i) => (
134          <Stack gap={4} key={i} direction="column" alignItems="self-start">
135            <Network
136              networkLogoPath={networkConfigs[representing.chainId].networkLogoPath}
137              networkName={networkConfigs[representing.chainId].name}
138            />
139            {representing.votersRepresented.length === 0 ? (
140              <Typography sx={{ ml: 4 }} color="text.secondary">
141                <Trans>None</Trans>
142              </Typography>
143            ) : (
144              representing.votersRepresented.map((voter, i) => (
145                <AddressLink
146                  key={i}
147                  explorerLink={networkConfigs[representing.chainId].explorerLink}
148                  address={voter}
149                />
150              ))
151            )}
152          </Stack>
153        ))}
154      </>
155    );
156  };
157  
158  const Network = ({
159    networkLogoPath,
160    networkName,
161  }: {
162    networkLogoPath: string;
163    networkName: string;
164  }) => {
165    return (
166      <Stack direction="row" alignItems="center" gap={2}>
167        <img src={networkLogoPath} height="16px" width="16px" alt="network logo" />
168        <Typography variant="subheader1">{networkName}</Typography>
169      </Stack>
170    );
171  };
172  
173  const AddressLink = ({ explorerLink, address }: { explorerLink: string; address: string }) => {
174    return (
175      <Link href={`${explorerLink}/address/${address}`}>
176        <Stack direction="row" alignItems="center" gap={1}>
177          <CompactableTypography
178            variant="subheader1"
179            compactMode={CompactMode.MD}
180            compact
181            sx={{ ml: 4 }}
182          >
183            {address}
184          </CompactableTypography>
185          <SvgIcon
186            sx={(theme) => ({
187              width: 14,
188              height: 14,
189              ml: 0.5,
190              color: theme.palette.text.muted,
191            })}
192          >
193            <ExternalLinkIcon />
194          </SvgIcon>
195        </Stack>
196      </Link>
197    );
198  };