From 8f5bd8fa118dbb64607fa60f2888fc7df5c6e78c Mon Sep 17 00:00:00 2001 From: Rafael Ventura Date: Fri, 26 Nov 2021 10:37:29 +0100 Subject: [PATCH 01/36] feat: improvements on data fetching for delegates, reorganizing delegates info (#251) * improvements on data fetching for delegates, reorganizing delegates info * include api docs --- modules/address/components/AddressDetail.tsx | 41 +++++-- modules/address/types/addressApiResponse.d.ts | 1 - modules/delegates/components/DelegateCard.tsx | 13 +-- .../components/DelegateCredentials.tsx | 7 +- .../delegates/components/DelegateDetail.tsx | 50 ++++++--- .../components/DelegateMKRDelegatedStats.tsx | 4 +- .../delegates/components/DelegatePicture.tsx | 14 ++- .../components/DelegateVoteHistory.tsx | 40 +++---- modules/delegates/components/index.ts | 1 - .../components/LastVoted.tsx} | 13 +-- pages/address/[address]/index.tsx | 10 +- pages/api/address/[address]/index.ts | 8 +- pages/api/address/[address]/stats.ts | 106 ++++++++++++++++++ pages/api/delegates/[address].ts | 20 ---- 14 files changed, 222 insertions(+), 106 deletions(-) rename modules/{delegates/components/DelegateLastVoted.tsx => polling/components/LastVoted.tsx} (89%) create mode 100644 pages/api/address/[address]/stats.ts delete mode 100644 pages/api/delegates/[address].ts diff --git a/modules/address/components/AddressDetail.tsx b/modules/address/components/AddressDetail.tsx index 94d043a05..01e149ea7 100644 --- a/modules/address/components/AddressDetail.tsx +++ b/modules/address/components/AddressDetail.tsx @@ -1,6 +1,5 @@ import React from 'react'; import { Box, Text, Link as ExternalLink, Flex, Divider } from 'theme-ui'; -import { useBreakpointIndex } from '@theme-ui/match-media'; import { Icon } from '@makerdao/dai-ui-icons'; import { getNetwork } from 'lib/maker'; import { getEtherscanLink } from 'lib/utils'; @@ -8,18 +7,27 @@ import AddressIcon from './AddressIcon'; import { PollVoteHistoryList } from 'modules/polling/components/PollVoteHistoryList'; import { AddressAPIStats, VoteProxyInfo } from '../types/addressApiResponse'; import Tooltip from 'modules/app/components/Tooltip'; -import { cutMiddle } from 'lib/string'; import { PollingParticipationOverview } from 'modules/polling/components/PollingParticipationOverview'; import { Address } from './Address'; +import useSWR from 'swr'; +import { fetchJson } from 'lib/fetchJson'; +import LastVoted from 'modules/polling/components/LastVoted'; type PropTypes = { address: string; - stats: AddressAPIStats; voteProxyInfo?: VoteProxyInfo; }; -export function AddressDetail({ address, stats, voteProxyInfo }: PropTypes): React.ReactElement { - const bpi = useBreakpointIndex(); +export function AddressDetail({ address, voteProxyInfo }: PropTypes): React.ReactElement { + const { data: statsData } = useSWR( + `/api/address/${address}/stats?network=${getNetwork()}`, + fetchJson, + { + revalidateOnFocus: false, + refreshInterval: 0, + revalidateOnMount: true + } + ); const tooltipLabel = voteProxyInfo ? ( @@ -36,9 +44,18 @@ export function AddressDetail({ address, stats, voteProxyInfo }: PropTypes): Rea ) : null; return ( - + - + + + - + + + + + @@ -85,9 +106,9 @@ export function AddressDetail({ address, stats, voteProxyInfo }: PropTypes): Rea - + {statsData && } - + {statsData && } ); } diff --git a/modules/address/types/addressApiResponse.d.ts b/modules/address/types/addressApiResponse.d.ts index dd92fa3c8..b7b51ef57 100644 --- a/modules/address/types/addressApiResponse.d.ts +++ b/modules/address/types/addressApiResponse.d.ts @@ -18,5 +18,4 @@ export type AddressApiResponse = { voteProxyInfo?: VoteProxyInfo; delegateInfo?: Delegate; address: string; - stats: AddressAPIStats; }; diff --git a/modules/delegates/components/DelegateCard.tsx b/modules/delegates/components/DelegateCard.tsx index 4cf3d606f..107839c0b 100644 --- a/modules/delegates/components/DelegateCard.tsx +++ b/modules/delegates/components/DelegateCard.tsx @@ -9,12 +9,7 @@ import { ANALYTICS_PAGES } from 'modules/app/client/analytics/analytics.constant import { useAnalytics } from 'modules/app/client/analytics/useAnalytics'; import useAccountsStore from 'stores/accounts'; import { Delegate } from '../types'; -import { - DelegatePicture, - DelegateModal, - UndelegateModal, - DelegateLastVoted -} from 'modules/delegates/components'; +import { DelegatePicture, DelegateModal, UndelegateModal } from 'modules/delegates/components'; import { participationTooltipLabel, communicationTooltipLabel @@ -24,6 +19,8 @@ import { CurrentlySupportingExecutive } from 'modules/executive/components/Curre import useSWR from 'swr'; import { fetchJson } from 'lib/fetchJson'; import SkeletonThemed from 'modules/app/components/SkeletonThemed'; +import { PollVoteHistory } from 'modules/polling/types/pollVoteHistory'; +import LastVoted from 'modules/polling/components/LastVoted'; type PropTypes = { delegate: Delegate; @@ -42,7 +39,7 @@ export function DelegateCard({ delegate }: PropTypes): React.ReactElement { const { trackButtonClick } = useAnalytics(ANALYTICS_PAGES.DELEGATES); - const { data: lastVoteData } = useSWR( + const { data: lastVoteData } = useSWR<{ lastVote: PollVoteHistory }>( `/api/address/${delegate.voteDelegateAddress}/last-vote?network=${getNetwork()}`, fetchJson, { @@ -65,7 +62,7 @@ export function DelegateCard({ delegate }: PropTypes): React.ReactElement { {lastVoteData && ( - + )} {!lastVoteData && } diff --git a/modules/delegates/components/DelegateCredentials.tsx b/modules/delegates/components/DelegateCredentials.tsx index 2e5709818..7aba0cbd8 100644 --- a/modules/delegates/components/DelegateCredentials.tsx +++ b/modules/delegates/components/DelegateCredentials.tsx @@ -1,8 +1,7 @@ -import { Box, Divider, Link as ExternalLink, Text } from 'theme-ui'; +import { Box, Link as ExternalLink, Text } from 'theme-ui'; import { Icon } from '@makerdao/dai-ui-icons'; import { Delegate } from '../types'; -import { DelegateMKRDelegatedStats } from './DelegateMKRDelegatedStats'; export function DelegateCredentials({ delegate }: { delegate: Delegate }): React.ReactElement { return ( @@ -24,10 +23,6 @@ export function DelegateCredentials({ delegate }: { delegate: Delegate }): React )} - - - - ); } diff --git a/modules/delegates/components/DelegateDetail.tsx b/modules/delegates/components/DelegateDetail.tsx index 6d22465f4..d31c5e79e 100644 --- a/modules/delegates/components/DelegateDetail.tsx +++ b/modules/delegates/components/DelegateDetail.tsx @@ -1,32 +1,45 @@ import React from 'react'; -import { Box, Text, Link as ExternalLink, Flex } from 'theme-ui'; +import { Box, Text, Link as ExternalLink, Flex, Divider } from 'theme-ui'; import { Icon } from '@makerdao/dai-ui-icons'; import { getNetwork } from 'lib/maker'; import { getEtherscanLink } from 'lib/utils'; -import { AddressAPIStats } from 'modules/address/types/addressApiResponse'; import Tabs from 'modules/app/components/Tabs'; import { DelegatePicture, DelegateContractExpiration, - DelegateLastVoted, DelegateCredentials, DelegateVoteHistory, DelegateParticipationMetrics } from 'modules/delegates/components'; import { Delegate } from 'modules/delegates/types'; import { DelegateStatusEnum } from 'modules/delegates/delegates.constants'; +import { DelegateMKRDelegatedStats } from './DelegateMKRDelegatedStats'; +import { DelegateMKRChart } from './DelegateMKRChart'; +import useSWR from 'swr'; +import { fetchJson } from 'lib/fetchJson'; +import { PollingParticipationOverview } from 'modules/polling/components/PollingParticipationOverview'; +import { AddressAPIStats } from 'modules/address/types/addressApiResponse'; +import LastVoted from 'modules/polling/components/LastVoted'; type PropTypes = { delegate: Delegate; - stats: AddressAPIStats; }; -export function DelegateDetail({ delegate, stats }: PropTypes): React.ReactElement { +export function DelegateDetail({ delegate }: PropTypes): React.ReactElement { const { voteDelegateAddress } = delegate; + const { data: statsData } = useSWR( + `/api/address/${delegate.voteDelegateAddress}/stats?network=${getNetwork()}`, + fetchJson, + { + revalidateOnFocus: false, + refreshInterval: 0, + revalidateOnMount: true + } + ); const tabTitles = [ delegate.status === DelegateStatusEnum.recognized ? 'Delegate Credentials' : null, - delegate.status === DelegateStatusEnum.recognized ? 'Participation Metrics' : null, + 'Metrics', 'Voting History' ].filter(i => !!i) as string[]; @@ -36,19 +49,25 @@ export function DelegateDetail({ delegate, stats }: PropTypes): React.ReactEleme ) : null, - delegate.status === DelegateStatusEnum.recognized ? ( - + + {delegate.status === DelegateStatusEnum.recognized && ( + )} + {delegate.status === DelegateStatusEnum.recognized && } + + - ) : null, + + {statsData && } + , - + ].filter(i => !!i); return ( - + - + @@ -68,7 +87,7 @@ export function DelegateDetail({ delegate, stats }: PropTypes): React.ReactEleme href={getEtherscanLink(getNetwork(), voteDelegateAddress, 'address')} target="_blank" > - + Delegate contract @@ -77,10 +96,13 @@ export function DelegateDetail({ delegate, stats }: PropTypes): React.ReactEleme - + + + + diff --git a/modules/delegates/components/DelegateMKRDelegatedStats.tsx b/modules/delegates/components/DelegateMKRDelegatedStats.tsx index 455591dd3..a693f193a 100644 --- a/modules/delegates/components/DelegateMKRDelegatedStats.tsx +++ b/modules/delegates/components/DelegateMKRDelegatedStats.tsx @@ -9,6 +9,8 @@ export function DelegateMKRDelegatedStats({ delegate }: { delegate: Delegate }): const account = useAccountsStore(state => state.currentAccount); const address = account?.address; + // TODO: Fetch addresses suporting through API fetching + const { data: mkrStaked } = useMkrDelegated(address, delegate.voteDelegateAddress); return ( @@ -26,7 +28,7 @@ export function DelegateMKRDelegatedStats({ delegate }: { delegate: Delegate }): label={'Total MKR Delegated'} /> {/* TODO add once we have data */} - {/* */} + {/* */} + {delegate.picture ? ( ) : ( - + )} {delegate.status === DelegateStatusEnum.recognized && ( ( + `/api/address/${delegate.voteDelegateAddress}/stats?network=${getNetwork()}`, + fetchJson, + { + revalidateOnMount: true + } + ); -export function DelegateVoteHistory({ - delegate, - stats -}: { - delegate: Delegate; - stats: AddressAPIStats; -}): React.ReactElement { return ( - + - - - - - - - + {statsData && } + {!statsData && + [1, 2, 3, 4, 5].map(i => ( + + + + ))} - - ); } diff --git a/modules/delegates/components/index.ts b/modules/delegates/components/index.ts index fe7b002cd..9773caf48 100644 --- a/modules/delegates/components/index.ts +++ b/modules/delegates/components/index.ts @@ -7,7 +7,6 @@ export * from './modals/UndelegateModal'; export * from './DelegateCard'; export * from './DelegateContractExpiration'; export * from './DelegateDetail'; -export * from './DelegateLastVoted'; export * from './DelegatePicture'; export * from './DelegatesSystemInfo'; export * from './DelegateCredentials'; diff --git a/modules/delegates/components/DelegateLastVoted.tsx b/modules/polling/components/LastVoted.tsx similarity index 89% rename from modules/delegates/components/DelegateLastVoted.tsx rename to modules/polling/components/LastVoted.tsx index 1a9c54cf3..0a3b54978 100644 --- a/modules/delegates/components/DelegateLastVoted.tsx +++ b/modules/polling/components/LastVoted.tsx @@ -1,16 +1,15 @@ -import { Delegate } from '../types'; import { Text, Flex } from 'theme-ui'; import React from 'react'; import { formatDateWithTime, formatTimeAgo } from 'lib/datetime'; import Icon from 'modules/app/components/Icon'; -export function DelegateLastVoted({ - delegate, +export default function LastVoted({ + expired, date, left = false }: { - delegate: Delegate; - date?: string; + expired: boolean; + date?: string | number; left?: boolean; }): React.ReactElement { const styles = { @@ -55,7 +54,7 @@ export function DelegateLastVoted({ > {lastVoteDate} @@ -69,7 +68,7 @@ export function DelegateLastVoted({ { )} - {addressInfo.delegateInfo && ( - - )} + {addressInfo.delegateInfo && } {!addressInfo.delegateInfo && ( - + )} diff --git a/pages/api/address/[address]/index.ts b/pages/api/address/[address]/index.ts index 0f48637d9..c8ee1ca5c 100644 --- a/pages/api/address/[address]/index.ts +++ b/pages/api/address/[address]/index.ts @@ -7,7 +7,6 @@ import { DEFAULT_NETWORK } from 'lib/constants'; import withApiHandler from 'lib/api/withApiHandler'; import { fetchDelegate } from 'modules/delegates/api/fetchDelegates'; import { AddressApiResponse } from 'modules/address/types/addressApiResponse'; -import { fetchAddressPollVoteHistory } from 'modules/polling/api/fetchAddressPollVoteHistory'; import { resolveENS } from 'modules/web3/ens'; export default withApiHandler(async (req: NextApiRequest, res: NextApiResponse) => { @@ -43,18 +42,13 @@ export default withApiHandler(async (req: NextApiRequest, res: NextApiResponse (a.blockTimestamp > b.blockTimestamp ? -1 : 1))[0] - } + address }; res.setHeader('Cache-Control', 's-maxage=15, stale-while-revalidate'); diff --git a/pages/api/address/[address]/stats.ts b/pages/api/address/[address]/stats.ts new file mode 100644 index 000000000..2e52056b1 --- /dev/null +++ b/pages/api/address/[address]/stats.ts @@ -0,0 +1,106 @@ +import invariant from 'tiny-invariant'; +import { NextApiRequest, NextApiResponse } from 'next'; +import getMaker from 'lib/maker'; +import voteProxyFactoryAbi from 'lib/abis/voteProxyAbi.json'; +import { isSupportedNetwork } from 'lib/maker/index'; +import { DEFAULT_NETWORK } from 'lib/constants'; +import withApiHandler from 'lib/api/withApiHandler'; +import { AddressAPIStats } from 'modules/address/types/addressApiResponse'; +import { fetchAddressPollVoteHistory } from 'modules/polling/api/fetchAddressPollVoteHistory'; +import { resolveENS } from 'modules/web3/ens'; + +/** + * @swagger + * definitions: + * ArrayOfVoteHistory: + * type: array + * items: + * $ref: '#/definitions/VoteHistory' + * VoteHistory: + * type: object + * properties: + * pollId: + * type: integer + * blockTimestamp: + * type: string + * option: + * type: number + * optionValue: + * type: string + * rankedChoiceOption: + * type: array + * items: + * type: integer + * poll: + * $ref: '#/definitions/Poll' + * example: + * - pollId: 1 + * blockTimestamp: "2021-11-20T19:25:47+00:00" + * option: 1 + * optionValue: "Yes" + * rankedChoiceOption: [1] + * poll: + * pollId: 1 + * AddressStats: + * type: object + * properties: + * pollVoteHistory: + * $ref: '#/definitions/ArrayOfVoteHistory' + * lastVote: + * $ref: '#/definitions/VoteHistory' + * + * /api/[address]/stats: + * get: + * tags: + * - "address" + * description: Returns stats for address + * produces: + * - "application/json" + * parameters: + * - name: "address" + * in: "query" + * description: "Address" + * required: true + * type: "string" + * responses: + * '200': + * description: "Stats of the address" + * content: + * application/json: + * schema: + * $ref: '#/definitions/AddressStats' + * + */ +export default withApiHandler(async (req: NextApiRequest, res: NextApiResponse) => { + const network = (req.query.network as string) || DEFAULT_NETWORK; + const tempAddress = req.query.address as string; + invariant(isSupportedNetwork(network), `unsupported network ${network}`); + + const address = tempAddress.indexOf('.eth') !== -1 ? await resolveENS(tempAddress) : tempAddress; + const maker = await getMaker(network); + const voteProxyContract = maker + .service('smartContract') + .getContractByAddressAndAbi(address, voteProxyFactoryAbi); + + // TODO: should we check cold for history? + let hot; + let cold; + let voteProxyAddress; + try { + hot = await voteProxyContract.hot(); + cold = await voteProxyContract.cold(); + voteProxyAddress = address; + } catch (err) { + // console.log(err); + } + + const pollVoteHistory = await fetchAddressPollVoteHistory(hot ? hot : address, network); + + const response: AddressAPIStats = { + pollVoteHistory, + lastVote: pollVoteHistory.sort((a, b) => (a.blockTimestamp > b.blockTimestamp ? -1 : 1))[0] + }; + + res.setHeader('Cache-Control', 's-maxage=15, stale-while-revalidate'); + res.status(200).json(response); +}); diff --git a/pages/api/delegates/[address].ts b/pages/api/delegates/[address].ts deleted file mode 100644 index 3a3d88ab9..000000000 --- a/pages/api/delegates/[address].ts +++ /dev/null @@ -1,20 +0,0 @@ -import invariant from 'tiny-invariant'; -import { NextApiRequest, NextApiResponse } from 'next'; - -import { isSupportedNetwork } from 'lib/maker/index'; -import { DEFAULT_NETWORK } from 'lib/constants'; -import withApiHandler from 'lib/api/withApiHandler'; -import { fetchDelegate } from 'modules/delegates/api/fetchDelegates'; -import { resolveENS } from 'modules/web3/ens'; - -export default withApiHandler(async (req: NextApiRequest, res: NextApiResponse) => { - const network = (req.query.network as string) || DEFAULT_NETWORK; - const tempAddress = req.query.address as string; - const address = tempAddress.indexOf('.eth') !== -1 ? await resolveENS(tempAddress) : tempAddress; - - invariant(isSupportedNetwork(network), `unsupported network ${network}`); - - const delegate = await fetchDelegate(address, network); - res.setHeader('Cache-Control', 's-maxage=15, stale-while-revalidate'); - res.status(200).json(delegate); -}); From 953b3e5590c5a92e1a6c2b9081bb9f7e4a12dc2e Mon Sep 17 00:00:00 2001 From: Rafael Ventura Date: Mon, 29 Nov 2021 18:39:03 +0100 Subject: [PATCH 02/36] include hotfixes for empty poll options (#259) * hot fix tally * hotfix: Remove polling bars on empty results/votes and fix NaN issue Co-authored-by: Phil Bain --- .../polling/components/PollOverviewCard.tsx | 13 +++++++++--- .../PollVotePluralityResultsCompact.tsx | 6 +++--- .../components/PollWinningOptionBox.tsx | 2 +- modules/polling/helpers/getPollTaly.ts | 19 +++++++++++++++-- modules/polling/helpers/parseRawTally.ts | 21 +++++++++++-------- pages/polling/[poll-hash].tsx | 13 +++++++----- 6 files changed, 51 insertions(+), 23 deletions(-) diff --git a/modules/polling/components/PollOverviewCard.tsx b/modules/polling/components/PollOverviewCard.tsx index c9aecce60..8e1f8f5e7 100644 --- a/modules/polling/components/PollOverviewCard.tsx +++ b/modules/polling/components/PollOverviewCard.tsx @@ -190,15 +190,22 @@ export default function PollOverviewCard({ {poll.voteType === POLL_VOTE_TYPE.PLURALITY_VOTE && ( 0 ? '265px' : '100%', p: bpi > 0 ? 0 : 2 }}> - {tally && } + {tally && tally.totalMkrParticipation > 0 && ( + + )} {!tally && } )} - - + + {tally && tally.totalMkrParticipation > 0 && ( + + + + + )} ); } diff --git a/modules/polling/components/PollVotePluralityResultsCompact.tsx b/modules/polling/components/PollVotePluralityResultsCompact.tsx index c90655553..9ac568055 100644 --- a/modules/polling/components/PollVotePluralityResultsCompact.tsx +++ b/modules/polling/components/PollVotePluralityResultsCompact.tsx @@ -18,9 +18,9 @@ export function PollVotePluralityResultsCompact({ const yesValue = new BigNumber(voteTallyOptions['1'] ? voteTallyOptions['1'].mkrSupport : 0); const noValue = new BigNumber(voteTallyOptions['2'] ? voteTallyOptions['2'].mkrSupport : 0); - const yesPercent = yesValue.dividedBy(max).multipliedBy(100).toFixed(0); - const abstainPercent = abstainValue.dividedBy(max).multipliedBy(100).toFixed(0); - const noPercent = noValue.dividedBy(max).multipliedBy(100).toFixed(0); + const yesPercent = max.isGreaterThan(0) ? yesValue.dividedBy(max).multipliedBy(100).toFixed(0) : 0; + const abstainPercent = max.isGreaterThan(0) ? abstainValue.dividedBy(max).multipliedBy(100).toFixed(0) : 0; + const noPercent = max.isGreaterThan(0) ? noValue.dividedBy(max).multipliedBy(100).toFixed(0) : 0; return ( diff --git a/modules/polling/components/PollWinningOptionBox.tsx b/modules/polling/components/PollWinningOptionBox.tsx index e1bfbf9ab..8d947b907 100644 --- a/modules/polling/components/PollWinningOptionBox.tsx +++ b/modules/polling/components/PollWinningOptionBox.tsx @@ -18,7 +18,7 @@ export default function PollWinningOptionBox({ return ( - {tally && tally.winningOptionName ? ( + {tally && Object.keys(tally.options).length > 0 && tally.winningOptionName ? ( {textWin}:{' '} diff --git a/modules/polling/helpers/getPollTaly.ts b/modules/polling/helpers/getPollTaly.ts index 80faff0ac..2864bb817 100644 --- a/modules/polling/helpers/getPollTaly.ts +++ b/modules/polling/helpers/getPollTaly.ts @@ -23,7 +23,6 @@ export async function getPollTally(poll: Poll, network?: SupportedNetworks): Pro const tally: RawPollTally = await backoffRetry(3, () => fetchPollTally(poll.pollId, voteType, false, currentNetwork) ); - const maker = await getMaker(currentNetwork); const votesByAddress: PollTallyVote[] = ( await maker.service('govPolling').getMkrAmtVotedByAddress(poll.pollId) @@ -35,7 +34,23 @@ export async function getPollTally(poll: Poll, network?: SupportedNetworks): Pro const parsedTally = { pollVoteType: voteType, - options: tally.options, + options: + Object.keys(tally.options).length > 0 + ? tally.options + : { + '0': { + mkrSupport: new BigNumber(0), + winner: false + }, + '1': { + mkrSupport: new BigNumber(0), + winner: false + }, + '2': { + mkrSupport: new BigNumber(0), + winner: false + } + }, winner, totalMkrParticipation, numVoters, diff --git a/modules/polling/helpers/parseRawTally.ts b/modules/polling/helpers/parseRawTally.ts index f318ed566..26beed32c 100644 --- a/modules/polling/helpers/parseRawTally.ts +++ b/modules/polling/helpers/parseRawTally.ts @@ -18,12 +18,14 @@ export function parseRawPollTally(rawTally: RawPollTally, poll: Poll): PollTally optionName: poll.options[key], firstChoice: new BigNumber(rawTally.options?.[key]?.firstChoice || 0), transfer: new BigNumber(rawTally.options?.[key]?.transfer || 0), - firstPct: rawTally.options?.[key]?.firstChoice - ? new BigNumber(rawTally.options[key].firstChoice).div(totalMkrParticipation).times(100) - : new BigNumber(0), - transferPct: rawTally.options?.[key]?.transfer - ? new BigNumber(rawTally.options[key].transfer).div(totalMkrParticipation).times(100) - : new BigNumber(0), + firstPct: + totalMkrParticipation.isGreaterThan(0) && rawTally.options?.[key]?.firstChoice + ? new BigNumber(rawTally.options[key].firstChoice).div(totalMkrParticipation).times(100) + : new BigNumber(0), + transferPct: + totalMkrParticipation.isGreaterThan(0) && rawTally.options?.[key]?.transfer + ? new BigNumber(rawTally.options[key].transfer).div(totalMkrParticipation).times(100) + : new BigNumber(0), eliminated: rawTally.options?.[key]?.eliminated ?? true, winner: rawTally.options?.[key]?.winner ?? false } as RankedChoiceResult; @@ -41,9 +43,10 @@ export function parseRawPollTally(rawTally: RawPollTally, poll: Poll): PollTally optionId: key, optionName: poll.options[key], mkrSupport: new BigNumber(rawTally.options?.[key]?.mkrSupport || 0), - firstPct: rawTally.options?.[key]?.mkrSupport - ? new BigNumber(rawTally.options[key].mkrSupport).div(totalMkrParticipation).times(100) - : new BigNumber(0), + firstPct: + totalMkrParticipation.isGreaterThan(0) && rawTally.options?.[key]?.mkrSupport + ? new BigNumber(rawTally.options[key].mkrSupport).div(totalMkrParticipation).times(100) + : new BigNumber(0), winner: rawTally.options?.[key]?.winner ?? false } as PluralityResult; }) diff --git a/pages/polling/[poll-hash].tsx b/pages/polling/[poll-hash].tsx index 09e31817a..fff5a4aab 100644 --- a/pages/polling/[poll-hash].tsx +++ b/pages/polling/[poll-hash].tsx @@ -282,11 +282,14 @@ const PollView = ({ poll }: { poll: Poll }) => { ) ]} banner={ - - - - - + tally && + tally.totalMkrParticipation > 0 && ( + + + + + + ) } /> From ffb6ea724a982c325860fef55b1e9d56eb516eb7 Mon Sep 17 00:00:00 2001 From: Rafael Ventura Date: Tue, 30 Nov 2021 11:08:39 +0100 Subject: [PATCH 03/36] Add delegate actions to delegate detail (#255) --- .../delegates/components/ManageDelegation.tsx | 71 +++++++++++++++++++ pages/address/[address]/index.tsx | 4 ++ 2 files changed, 75 insertions(+) create mode 100644 modules/delegates/components/ManageDelegation.tsx diff --git a/modules/delegates/components/ManageDelegation.tsx b/modules/delegates/components/ManageDelegation.tsx new file mode 100644 index 000000000..7af9deee9 --- /dev/null +++ b/modules/delegates/components/ManageDelegation.tsx @@ -0,0 +1,71 @@ +import { Card, Box, Button, Heading } from 'theme-ui'; +import React, { useState } from 'react'; +import { Delegate } from '../types'; +import useAccountsStore from 'stores/accounts'; +import { ANALYTICS_PAGES } from 'modules/app/client/analytics/analytics.constants'; +import { useAnalytics } from 'modules/app/client/analytics/useAnalytics'; +import { DelegateModal } from './modals/DelegateModal'; +import { UndelegateModal } from './modals/UndelegateModal'; +import { useLockedMkr } from 'modules/mkr/hooks/useLockedMkr'; +import { useMkrDelegated } from 'modules/mkr/hooks/useMkrDelegated'; + +export default function ManageDelegation({ delegate }: { delegate: Delegate }): React.ReactElement { + const [account] = useAccountsStore(state => [state.currentAccount]); + const { trackButtonClick } = useAnalytics(ANALYTICS_PAGES.DELEGATE_DETAIL); + const [showDelegateModal, setShowDelegateModal] = useState(false); + const [showUndelegateModal, setShowUndelegateModal] = useState(false); + + const { mutate: mutateTotalStaked } = useLockedMkr(delegate.voteDelegateAddress); + const { mutate: mutateMkrStaked } = useMkrDelegated(account?.address, delegate.voteDelegateAddress); + + return ( + + + Manage Delegation + + + + + + + + + + + setShowDelegateModal(false)} + mutateTotalStaked={mutateTotalStaked} + mutateMkrStaked={mutateMkrStaked} + /> + setShowUndelegateModal(false)} + mutateTotalStaked={mutateTotalStaked} + mutateMkrStaked={mutateMkrStaked} + /> + + ); +} diff --git a/pages/address/[address]/index.tsx b/pages/address/[address]/index.tsx index b99a5c10e..ad9a2c268 100644 --- a/pages/address/[address]/index.tsx +++ b/pages/address/[address]/index.tsx @@ -19,6 +19,7 @@ import { AddressApiResponse } from 'modules/address/types/addressApiResponse'; import { AddressDetail } from 'modules/address/components/AddressDetail'; import { DelegateDetail } from 'modules/delegates/components'; import { HeadComponent } from 'modules/app/components/layout/Head'; +import ManageDelegation from 'modules/delegates/components/ManageDelegation'; const AddressView = ({ addressInfo }: { addressInfo: AddressApiResponse }) => { const network = getNetwork(); @@ -67,6 +68,9 @@ const AddressView = ({ addressInfo }: { addressInfo: AddressApiResponse }) => { + {addressInfo.isDelegate && addressInfo.delegateInfo && ( + + )} From f1175f993fa243a039e82c2cf81f9e7b82552ee1 Mon Sep 17 00:00:00 2001 From: Rafael Ventura Date: Tue, 30 Nov 2021 11:38:49 +0100 Subject: [PATCH 04/36] Show percentage of MKR delegated (#256) * Show percentage of MKR delegated * Add a skeletok while loading percent of delegates --- .../components/DelegatesSystemInfo.tsx | 17 +++++++++++ modules/mkr/hooks/useTotalMkr.ts | 28 +++++++++++++++++++ 2 files changed, 45 insertions(+) create mode 100644 modules/mkr/hooks/useTotalMkr.ts diff --git a/modules/delegates/components/DelegatesSystemInfo.tsx b/modules/delegates/components/DelegatesSystemInfo.tsx index e5ba44f48..b897520da 100644 --- a/modules/delegates/components/DelegatesSystemInfo.tsx +++ b/modules/delegates/components/DelegatesSystemInfo.tsx @@ -6,6 +6,8 @@ import { formatAddress, getEtherscanLink } from 'lib/utils'; import useSWR from 'swr'; import { Box, Card, Flex, Heading, Link as ThemeUILink, Text } from 'theme-ui'; import { DelegatesAPIStats } from '../types'; +import { useEffect } from 'react'; +import { useTotalMKR } from 'modules/mkr/hooks/useTotalMkr'; export function DelegatesSystemInfo({ stats, @@ -24,6 +26,9 @@ export function DelegatesSystemInfo({ refreshInterval: 0 } ); + + const { data: totalMKR } = useTotalMKR(); + const statsItems = [ { title: 'Total delegates', @@ -44,6 +49,18 @@ export function DelegatesSystemInfo({ title: 'Total MKR delegated', id: 'total-mkr-system-info', value: new BigNumber(stats.totalMKRDelegated).toFormat(2) + }, + { + title: 'Percent of MKR delegated', + id: 'percent-mkr-system-info', + value: totalMKR ? ( + `${new BigNumber(stats.totalMKRDelegated) + .dividedBy(totalMKR.toBigNumber()) + .multipliedBy(100) + .toFormat(2)}%` + ) : ( + + ) } ]; diff --git a/modules/mkr/hooks/useTotalMkr.ts b/modules/mkr/hooks/useTotalMkr.ts new file mode 100644 index 000000000..8eca27bdc --- /dev/null +++ b/modules/mkr/hooks/useTotalMkr.ts @@ -0,0 +1,28 @@ +// Returns the total supply of MKR + +import getMaker, { getNetwork } from 'lib/maker'; +import useSWR from 'swr'; +import { CurrencyObject } from 'types/currency'; + +export const useTotalMKR = (): { + data: CurrencyObject; +} => { + const { data } = useSWR( + '/total-mkr-supply', + async () => { + const maker = await getMaker(getNetwork()); + + const total = await maker.service('token').getToken('MKR').totalSupply(); + + return total; + }, + { + revalidateOnMount: true, + revalidateOnFocus: false + } + ); + + return { + data + }; +}; From 65b23c0f77e6571249ea22cfbcc648fa1ad269ff Mon Sep 17 00:00:00 2001 From: Rafael Ventura Date: Tue, 30 Nov 2021 12:31:44 +0100 Subject: [PATCH 05/36] Fix polling date filter issue (#253) * Fix polling date filter issue * Include feedback from review --- modules/executive/components/DateFilter.tsx | 10 +++++----- modules/polling/api/fetchPolls.ts | 4 ++-- modules/polling/components/DateFilter.tsx | 4 ++-- pages/polling.tsx | 4 ++-- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/modules/executive/components/DateFilter.tsx b/modules/executive/components/DateFilter.tsx index 7f6442d12..3009b5a17 100644 --- a/modules/executive/components/DateFilter.tsx +++ b/modules/executive/components/DateFilter.tsx @@ -43,14 +43,14 @@ export default function DateFilter(props): JSX.Element { { if (!startDateDisplay && !endDateDisplay) return 'Date Posted'; - if (!startDateDisplay) return `Date Posted: before ${endDateDisplay}`; - if (!endDateDisplay) return `Date Posted: after ${startDateDisplay}`; - return `Date Posted: ${startDateDisplay} - ${endDateDisplay}`; + if (!startDateDisplay) return `Date Filter: before ${endDateDisplay}`; + if (!endDateDisplay) return `Date Filter: after ${startDateDisplay}`; + return `Date Filter: ${startDateDisplay} - ${endDateDisplay}`; }} {...props} > - After + Posted after: - Before + Posted before: { // check date filters first - if (filters.startDate && new Date(poll.startDate).getTime() < filters.startDate.getTime()) return false; - if (filters.endDate && new Date(poll.startDate).getTime() > filters.endDate.getTime()) return false; + if (filters.startDate && new Date(poll.endDate).getTime() < filters.startDate.getTime()) return false; + if (filters.endDate && new Date(poll.endDate).getTime() > filters.endDate.getTime()) return false; // if no category filters selected, return all, otherwise, check if poll contains category return ( diff --git a/modules/polling/components/DateFilter.tsx b/modules/polling/components/DateFilter.tsx index aec4ffd6a..2fa465eed 100644 --- a/modules/polling/components/DateFilter.tsx +++ b/modules/polling/components/DateFilter.tsx @@ -45,7 +45,7 @@ export default function DateFilter(props): JSX.Element { {...props} > - After + Ended after: - Before + Ended before: setEndDate('poll', new Date(e.target.value))} /> diff --git a/pages/polling.tsx b/pages/polling.tsx index 8ec9859cf..1686677f5 100644 --- a/pages/polling.tsx +++ b/pages/polling.tsx @@ -71,8 +71,8 @@ const PollingOverview = ({ polls, categories }: Props) => { const filteredPolls = useMemo(() => { return polls.filter(poll => { // check date filters first - if (start && new Date(poll.startDate).getTime() < start.getTime()) return false; - if (end && new Date(poll.startDate).getTime() > end.getTime()) return false; + if (start && new Date(poll.endDate).getTime() < start.getTime()) return false; + if (end && new Date(poll.endDate).getTime() > end.getTime()) return false; // if no category filters selected, return all, otherwise, check if poll contains category return noCategoriesSelected || poll.categories.some(c => categoryFilter && categoryFilter[c]); From 6e4a57614da4a3607c6d3ad5ffae103180582eb4 Mon Sep 17 00:00:00 2001 From: Phil Bain Date: Tue, 30 Nov 2021 14:57:43 -0700 Subject: [PATCH 06/36] calculate delegator amounts and add table --- .../delegates/components/DelegateDetail.tsx | 6 +- .../delegates/components/DelegateMKRChart.tsx | 2 +- .../components/DelegateVoteHistory.tsx | 11 +- .../components/DelegatedByAddress.tsx | 101 ++++++++++++++++++ pages/address/[address]/index.tsx | 37 ++++++- 5 files changed, 151 insertions(+), 6 deletions(-) create mode 100644 modules/delegates/components/DelegatedByAddress.tsx diff --git a/modules/delegates/components/DelegateDetail.tsx b/modules/delegates/components/DelegateDetail.tsx index d31c5e79e..f3a591d95 100644 --- a/modules/delegates/components/DelegateDetail.tsx +++ b/modules/delegates/components/DelegateDetail.tsx @@ -20,12 +20,13 @@ import { fetchJson } from 'lib/fetchJson'; import { PollingParticipationOverview } from 'modules/polling/components/PollingParticipationOverview'; import { AddressAPIStats } from 'modules/address/types/addressApiResponse'; import LastVoted from 'modules/polling/components/LastVoted'; +import { useLockedMkr } from 'modules/mkr/hooks/useLockedMkr'; type PropTypes = { delegate: Delegate; }; -export function DelegateDetail({ delegate }: PropTypes): React.ReactElement { +export function DelegateDetail({ delegate, delegatedFrom }: PropTypes): React.ReactElement { const { voteDelegateAddress } = delegate; const { data: statsData } = useSWR( `/api/address/${delegate.voteDelegateAddress}/stats?network=${getNetwork()}`, @@ -36,6 +37,7 @@ export function DelegateDetail({ delegate }: PropTypes): React.ReactElement { revalidateOnMount: true } ); + const { data: totalStaked, mutate: mutateTotalStaked } = useLockedMkr(delegate.voteDelegateAddress); const tabTitles = [ delegate.status === DelegateStatusEnum.recognized ? 'Delegate Credentials' : null, @@ -61,7 +63,7 @@ export function DelegateDetail({ delegate }: PropTypes): React.ReactElement { {statsData && } , - + ].filter(i => !!i); diff --git a/modules/delegates/components/DelegateMKRChart.tsx b/modules/delegates/components/DelegateMKRChart.tsx index 71e6717b5..8149a93a7 100644 --- a/modules/delegates/components/DelegateMKRChart.tsx +++ b/modules/delegates/components/DelegateMKRChart.tsx @@ -16,7 +16,7 @@ import { import FilterButton from 'modules/app/components/FilterButton'; import { useState } from 'react'; import { MKRWeightTimeRanges } from '../delegates.constants'; -import { fetchJson } from '@ethersproject/web'; +import { fetchJson } from 'lib/fetchJson'; import useSWR from 'swr'; import { getNetwork } from 'lib/maker'; import { format } from 'date-fns'; diff --git a/modules/delegates/components/DelegateVoteHistory.tsx b/modules/delegates/components/DelegateVoteHistory.tsx index eddeace2f..298683bd7 100644 --- a/modules/delegates/components/DelegateVoteHistory.tsx +++ b/modules/delegates/components/DelegateVoteHistory.tsx @@ -6,8 +6,15 @@ import useSWR from 'swr'; import { getNetwork } from 'lib/maker'; import { fetchJson } from 'lib/fetchJson'; import SkeletonThemed from 'modules/app/components/SkeletonThemed'; +import DelegatedByAddress from 'modules/delegates/components/DelegatedByAddress'; -export function DelegateVoteHistory({ delegate }: { delegate: Delegate }): React.ReactElement { +export function DelegateVoteHistory({ + delegate, + delegatedFrom, + totalStaked +}: { + delegate: Delegate; +}): React.ReactElement { const { data: statsData } = useSWR( `/api/address/${delegate.voteDelegateAddress}/stats?network=${getNetwork()}`, fetchJson, @@ -20,6 +27,8 @@ export function DelegateVoteHistory({ delegate }: { delegate: Delegate }): React + + { + console.log('delegators from comp', delegators); + console.log('dtotalDelegated', totalDelegated); + const bpi = useBreakpointIndex(); + const network = getNetwork(); + + return ( + + + + + + Address + + + MKR Delegated + + + Voting Power + + {/* + Verify + */} + + + + {delegators ? ( + <> + {delegators.map(({ address, lockAmount }, i) => ( + + + + + {/* {delegateAddresses[v.voter] ? ( + delegateAddresses[v.voter] + ) : ( */} +
+ {/* )} */} + + + + {/* + {v.rankedChoiceOption && v.rankedChoiceOption.length > 1 + ? poll.options[v.rankedChoiceOption[0]] + : poll.options[v.optionId]} + {v.rankedChoiceOption && v.rankedChoiceOption.length > 1 && '*'} + */} + {`${new BigNumber( + lockAmount + ).toFormat(2)}${bpi > 0 ? ' MKR' : ''}`} + + {`${new BigNumber(lockAmount).div(totalDelegated.toBigNumber()).times(100).toFormat(1)}%`} + +
+ ))} + + ) : ( + + + + )} + +
+ + Loading + +
+ {/* {showRankedChoiceInfo && ( + + *First choice in ranked choice vote shown + + )} */} +
+ ); +}; + +export default DelegatedByAddress; diff --git a/pages/address/[address]/index.tsx b/pages/address/[address]/index.tsx index ad9a2c268..1f9c82511 100644 --- a/pages/address/[address]/index.tsx +++ b/pages/address/[address]/index.tsx @@ -4,7 +4,7 @@ import { useBreakpointIndex } from '@theme-ui/match-media'; import ErrorPage from 'next/error'; import Link from 'next/link'; import { Icon } from '@makerdao/dai-ui-icons'; -import { getNetwork } from 'lib/maker'; +import getMaker, { getNetwork } from 'lib/maker'; import { fetchJson } from 'lib/fetchJson'; import { useAnalytics } from 'modules/app/client/analytics/useAnalytics'; import { ANALYTICS_PAGES } from 'modules/app/client/analytics/analytics.constants'; @@ -20,15 +20,46 @@ import { AddressDetail } from 'modules/address/components/AddressDetail'; import { DelegateDetail } from 'modules/delegates/components'; import { HeadComponent } from 'modules/app/components/layout/Head'; import ManageDelegation from 'modules/delegates/components/ManageDelegation'; +import { SupportedNetworks } from 'lib/constants'; +import { utils } from 'ethers'; const AddressView = ({ addressInfo }: { addressInfo: AddressApiResponse }) => { const network = getNetwork(); const bpi = useBreakpointIndex({ defaultIndex: 2 }); + const [delegatedFrom, setDelegatedFrom] = useState(null); + const { trackButtonClick } = useAnalytics( addressInfo.isDelegate ? ANALYTICS_PAGES.DELEGATE_DETAIL : ANALYTICS_PAGES.ADDRESS_DETAIL ); + //Using monetsupply to test + useEffect(() => { + getMaker(network).then(maker => { + maker + .service('voteDelegate') + .getMkrLockedDelegate(addressInfo.address) + .then(data => { + const red = data.reduce((acc, { fromAddress, lockAmount }) => { + // const guy = '0xb088a3bc93f71b4de97b9de773e9647645983688'; + const currSum = acc[fromAddress]?.lockAmount + ? utils.parseEther(acc[fromAddress]?.lockAmount) + : utils.parseEther('0'); + + acc[fromAddress] = { lockAmount: utils.formatEther(currSum.add(utils.parseEther(lockAmount))) }; + return acc; + }, {}); + + //TODO do this in the reducer + const delegators = []; + for (const x in red) { + delegators.push({ address: x, ...red[x] }); + } + + setDelegatedFrom(delegators.sort((a, b) => (a.lockAmount > b.lockAmount ? -1 : 1))); + }); + }); + }, []); return ( { )} - {addressInfo.delegateInfo && } + {addressInfo.delegateInfo && ( + + )} {!addressInfo.delegateInfo && ( )} From 6cb9030023cbdae5c61e49a762f906139479ee32 Mon Sep 17 00:00:00 2001 From: Phil Bain Date: Tue, 30 Nov 2021 17:13:55 -0700 Subject: [PATCH 07/36] fetch delegator data in API --- .../delegates/api/fetchDelegationHistory.ts | 33 +++++++++++++++++ .../delegates/api/fetchMKRWeightHistory.ts | 13 +++---- .../delegates/components/DelegateDetail.tsx | 6 ++-- .../components/DelegateVoteHistory.tsx | 21 ++++++----- .../components/DelegatedByAddress.tsx | 2 -- modules/delegates/types/delegate.d.ts | 5 +++ modules/delegates/types/delegatesAPI.d.ts | 8 +++++ pages/address/[address]/index.tsx | 35 ++----------------- .../delegates/delegation-history/[address].ts | 17 +++++++++ 9 files changed, 85 insertions(+), 55 deletions(-) create mode 100644 modules/delegates/api/fetchDelegationHistory.ts create mode 100644 pages/api/delegates/delegation-history/[address].ts diff --git a/modules/delegates/api/fetchDelegationHistory.ts b/modules/delegates/api/fetchDelegationHistory.ts new file mode 100644 index 000000000..e67648151 --- /dev/null +++ b/modules/delegates/api/fetchDelegationHistory.ts @@ -0,0 +1,33 @@ +import BigNumber from 'bignumber.js'; +import { utils } from 'ethers'; +import { format, parse } from 'date-fns'; +import { SupportedNetworks } from 'lib/constants'; +import { DelegationHistory, MKRLockedDelegateAPIResponse } from '../types'; +import getMaker from 'lib/maker'; + +export async function fetchDelegationHistory( + address: string, + network: SupportedNetworks +): Promise { + const maker = await getMaker(network); + const addressData: MKRLockedDelegateAPIResponse[] = await maker + .service('voteDelegate') + .getMkrLockedDelegate(address); + + const delegatorsR = addressData.reduce((acc, { fromAddress, lockAmount }) => { + const currSum = acc[fromAddress]?.lockAmount + ? utils.parseEther(acc[fromAddress]?.lockAmount) + : utils.parseEther('0'); + + acc[fromAddress] = { lockAmount: utils.formatEther(currSum.add(utils.parseEther(lockAmount))) }; + return acc; + }, {}); + + //TODO do this in the reducer + const delegators: DelegationHistory[] = []; + for (const x in delegatorsR) { + delegators.push({ address: x, ...delegatorsR[x] }); + } + + return delegators.sort((a, b) => (a.lockAmount > b.lockAmount ? -1 : 1)); +} diff --git a/modules/delegates/api/fetchMKRWeightHistory.ts b/modules/delegates/api/fetchMKRWeightHistory.ts index a0606715a..697e3b9ca 100644 --- a/modules/delegates/api/fetchMKRWeightHistory.ts +++ b/modules/delegates/api/fetchMKRWeightHistory.ts @@ -4,14 +4,7 @@ import { MKRWeightHisory } from '../types/mkrWeight'; import getMaker from 'lib/maker'; import { format, parse } from 'date-fns'; import BigNumber from 'bignumber.js'; - -type MKRLockedDelegate = { - fromAddress: string; - lockAmount: string; - blockNumber: number; - blockTimestamp: string; - lockTotal: string; -}; +import { MKRLockedDelegateAPIResponse } from '../types'; export async function fetchDelegatesMKRWeightHistory( address: string, @@ -20,7 +13,9 @@ export async function fetchDelegatesMKRWeightHistory( network: SupportedNetworks ): Promise { const maker = await getMaker(network); - const addressData: MKRLockedDelegate[] = await maker.service('voteDelegate').getMkrLockedDelegate(address); + const addressData: MKRLockedDelegateAPIResponse[] = await maker + .service('voteDelegate') + .getMkrLockedDelegate(address); // We need to fill all the data for the interval // If we get last month, we need to add all the missing days diff --git a/modules/delegates/components/DelegateDetail.tsx b/modules/delegates/components/DelegateDetail.tsx index f3a591d95..d31c5e79e 100644 --- a/modules/delegates/components/DelegateDetail.tsx +++ b/modules/delegates/components/DelegateDetail.tsx @@ -20,13 +20,12 @@ import { fetchJson } from 'lib/fetchJson'; import { PollingParticipationOverview } from 'modules/polling/components/PollingParticipationOverview'; import { AddressAPIStats } from 'modules/address/types/addressApiResponse'; import LastVoted from 'modules/polling/components/LastVoted'; -import { useLockedMkr } from 'modules/mkr/hooks/useLockedMkr'; type PropTypes = { delegate: Delegate; }; -export function DelegateDetail({ delegate, delegatedFrom }: PropTypes): React.ReactElement { +export function DelegateDetail({ delegate }: PropTypes): React.ReactElement { const { voteDelegateAddress } = delegate; const { data: statsData } = useSWR( `/api/address/${delegate.voteDelegateAddress}/stats?network=${getNetwork()}`, @@ -37,7 +36,6 @@ export function DelegateDetail({ delegate, delegatedFrom }: PropTypes): React.Re revalidateOnMount: true } ); - const { data: totalStaked, mutate: mutateTotalStaked } = useLockedMkr(delegate.voteDelegateAddress); const tabTitles = [ delegate.status === DelegateStatusEnum.recognized ? 'Delegate Credentials' : null, @@ -63,7 +61,7 @@ export function DelegateDetail({ delegate, delegatedFrom }: PropTypes): React.Re {statsData && } , - + ].filter(i => !!i); diff --git a/modules/delegates/components/DelegateVoteHistory.tsx b/modules/delegates/components/DelegateVoteHistory.tsx index 298683bd7..18cd24cea 100644 --- a/modules/delegates/components/DelegateVoteHistory.tsx +++ b/modules/delegates/components/DelegateVoteHistory.tsx @@ -6,15 +6,11 @@ import useSWR from 'swr'; import { getNetwork } from 'lib/maker'; import { fetchJson } from 'lib/fetchJson'; import SkeletonThemed from 'modules/app/components/SkeletonThemed'; +import { useLockedMkr } from 'modules/mkr/hooks/useLockedMkr'; import DelegatedByAddress from 'modules/delegates/components/DelegatedByAddress'; +import { DelegationHistory } from 'modules/delegates/types'; -export function DelegateVoteHistory({ - delegate, - delegatedFrom, - totalStaked -}: { - delegate: Delegate; -}): React.ReactElement { +export function DelegateVoteHistory({ delegate }: { delegate: Delegate }): React.ReactElement { const { data: statsData } = useSWR( `/api/address/${delegate.voteDelegateAddress}/stats?network=${getNetwork()}`, fetchJson, @@ -23,11 +19,20 @@ export function DelegateVoteHistory({ } ); + const { data: delegators } = useSWR( + `/api/delegates/delegation-history/${delegate.voteDelegateAddress}?network=${getNetwork()}`, + fetchJson, + { + revalidateOnMount: true + } + ); + const { data: totalStaked } = useLockedMkr(delegate.voteDelegateAddress); + return ( - + { - console.log('delegators from comp', delegators); - console.log('dtotalDelegated', totalDelegated); const bpi = useBreakpointIndex(); const network = getNetwork(); diff --git a/modules/delegates/types/delegate.d.ts b/modules/delegates/types/delegate.d.ts index 37bfcbd07..0b48ebacd 100644 --- a/modules/delegates/types/delegate.d.ts +++ b/modules/delegates/types/delegate.d.ts @@ -37,3 +37,8 @@ export type Delegate = { communication?: string; mkrDelegated: number; }; + +export type DelegationHistory = { + address: string; + lockAmount: string; +}; diff --git a/modules/delegates/types/delegatesAPI.d.ts b/modules/delegates/types/delegatesAPI.d.ts index b30ccbe10..6599f2fd8 100644 --- a/modules/delegates/types/delegatesAPI.d.ts +++ b/modules/delegates/types/delegatesAPI.d.ts @@ -15,3 +15,11 @@ export type DelegatesAPIResponse = { pageSize: number; }; }; + +export type MKRLockedDelegateAPIResponse = { + fromAddress: string; + lockAmount: string; + blockNumber: number; + blockTimestamp: string; + lockTotal: string; +}; diff --git a/pages/address/[address]/index.tsx b/pages/address/[address]/index.tsx index 1f9c82511..ab870f9dc 100644 --- a/pages/address/[address]/index.tsx +++ b/pages/address/[address]/index.tsx @@ -4,6 +4,8 @@ import { useBreakpointIndex } from '@theme-ui/match-media'; import ErrorPage from 'next/error'; import Link from 'next/link'; import { Icon } from '@makerdao/dai-ui-icons'; +import useSWR from 'swr'; + import getMaker, { getNetwork } from 'lib/maker'; import { fetchJson } from 'lib/fetchJson'; import { useAnalytics } from 'modules/app/client/analytics/useAnalytics'; @@ -27,39 +29,10 @@ const AddressView = ({ addressInfo }: { addressInfo: AddressApiResponse }) => { const network = getNetwork(); const bpi = useBreakpointIndex({ defaultIndex: 2 }); - const [delegatedFrom, setDelegatedFrom] = useState(null); - const { trackButtonClick } = useAnalytics( addressInfo.isDelegate ? ANALYTICS_PAGES.DELEGATE_DETAIL : ANALYTICS_PAGES.ADDRESS_DETAIL ); - //Using monetsupply to test - useEffect(() => { - getMaker(network).then(maker => { - maker - .service('voteDelegate') - .getMkrLockedDelegate(addressInfo.address) - .then(data => { - const red = data.reduce((acc, { fromAddress, lockAmount }) => { - // const guy = '0xb088a3bc93f71b4de97b9de773e9647645983688'; - const currSum = acc[fromAddress]?.lockAmount - ? utils.parseEther(acc[fromAddress]?.lockAmount) - : utils.parseEther('0'); - - acc[fromAddress] = { lockAmount: utils.formatEther(currSum.add(utils.parseEther(lockAmount))) }; - return acc; - }, {}); - - //TODO do this in the reducer - const delegators = []; - for (const x in red) { - delegators.push({ address: x, ...red[x] }); - } - - setDelegatedFrom(delegators.sort((a, b) => (a.lockAmount > b.lockAmount ? -1 : 1))); - }); - }); - }, []); return ( { )} - {addressInfo.delegateInfo && ( - - )} + {addressInfo.delegateInfo && } {!addressInfo.delegateInfo && ( )} diff --git a/pages/api/delegates/delegation-history/[address].ts b/pages/api/delegates/delegation-history/[address].ts new file mode 100644 index 000000000..cb515de33 --- /dev/null +++ b/pages/api/delegates/delegation-history/[address].ts @@ -0,0 +1,17 @@ +import invariant from 'tiny-invariant'; +import { NextApiRequest, NextApiResponse } from 'next'; + +import { isSupportedNetwork } from 'lib/maker/index'; +import { DEFAULT_NETWORK } from 'lib/constants'; +import withApiHandler from 'lib/api/withApiHandler'; +import { fetchDelegationHistory } from 'modules/delegates/api/fetchDelegationHistory'; + +export default withApiHandler(async (req: NextApiRequest, res: NextApiResponse) => { + const network = (req.query.network as string) || DEFAULT_NETWORK; + const address = req.query.address as string; + invariant(isSupportedNetwork(network), `unsupported network ${network}`); + + const data = await fetchDelegationHistory(address, network); + res.setHeader('Cache-Control', 's-maxage=15, stale-while-revalidate'); + res.status(200).json(data); +}); From 2b54f3855a29e66146ec5a7a81be87b348c5a12e Mon Sep 17 00:00:00 2001 From: Phil Bain Date: Tue, 30 Nov 2021 17:15:12 -0700 Subject: [PATCH 08/36] Revert "calculate delegator amounts and add table" This reverts commit 6e4a57614da4a3607c6d3ad5ffae103180582eb4. --- .../delegates/components/DelegateDetail.tsx | 6 +- .../delegates/components/DelegateMKRChart.tsx | 2 +- .../components/DelegateVoteHistory.tsx | 11 +- .../components/DelegatedByAddress.tsx | 101 ------------------ pages/address/[address]/index.tsx | 37 +------ 5 files changed, 6 insertions(+), 151 deletions(-) delete mode 100644 modules/delegates/components/DelegatedByAddress.tsx diff --git a/modules/delegates/components/DelegateDetail.tsx b/modules/delegates/components/DelegateDetail.tsx index f3a591d95..d31c5e79e 100644 --- a/modules/delegates/components/DelegateDetail.tsx +++ b/modules/delegates/components/DelegateDetail.tsx @@ -20,13 +20,12 @@ import { fetchJson } from 'lib/fetchJson'; import { PollingParticipationOverview } from 'modules/polling/components/PollingParticipationOverview'; import { AddressAPIStats } from 'modules/address/types/addressApiResponse'; import LastVoted from 'modules/polling/components/LastVoted'; -import { useLockedMkr } from 'modules/mkr/hooks/useLockedMkr'; type PropTypes = { delegate: Delegate; }; -export function DelegateDetail({ delegate, delegatedFrom }: PropTypes): React.ReactElement { +export function DelegateDetail({ delegate }: PropTypes): React.ReactElement { const { voteDelegateAddress } = delegate; const { data: statsData } = useSWR( `/api/address/${delegate.voteDelegateAddress}/stats?network=${getNetwork()}`, @@ -37,7 +36,6 @@ export function DelegateDetail({ delegate, delegatedFrom }: PropTypes): React.Re revalidateOnMount: true } ); - const { data: totalStaked, mutate: mutateTotalStaked } = useLockedMkr(delegate.voteDelegateAddress); const tabTitles = [ delegate.status === DelegateStatusEnum.recognized ? 'Delegate Credentials' : null, @@ -63,7 +61,7 @@ export function DelegateDetail({ delegate, delegatedFrom }: PropTypes): React.Re {statsData && } , - + ].filter(i => !!i); diff --git a/modules/delegates/components/DelegateMKRChart.tsx b/modules/delegates/components/DelegateMKRChart.tsx index 8149a93a7..71e6717b5 100644 --- a/modules/delegates/components/DelegateMKRChart.tsx +++ b/modules/delegates/components/DelegateMKRChart.tsx @@ -16,7 +16,7 @@ import { import FilterButton from 'modules/app/components/FilterButton'; import { useState } from 'react'; import { MKRWeightTimeRanges } from '../delegates.constants'; -import { fetchJson } from 'lib/fetchJson'; +import { fetchJson } from '@ethersproject/web'; import useSWR from 'swr'; import { getNetwork } from 'lib/maker'; import { format } from 'date-fns'; diff --git a/modules/delegates/components/DelegateVoteHistory.tsx b/modules/delegates/components/DelegateVoteHistory.tsx index 298683bd7..eddeace2f 100644 --- a/modules/delegates/components/DelegateVoteHistory.tsx +++ b/modules/delegates/components/DelegateVoteHistory.tsx @@ -6,15 +6,8 @@ import useSWR from 'swr'; import { getNetwork } from 'lib/maker'; import { fetchJson } from 'lib/fetchJson'; import SkeletonThemed from 'modules/app/components/SkeletonThemed'; -import DelegatedByAddress from 'modules/delegates/components/DelegatedByAddress'; -export function DelegateVoteHistory({ - delegate, - delegatedFrom, - totalStaked -}: { - delegate: Delegate; -}): React.ReactElement { +export function DelegateVoteHistory({ delegate }: { delegate: Delegate }): React.ReactElement { const { data: statsData } = useSWR( `/api/address/${delegate.voteDelegateAddress}/stats?network=${getNetwork()}`, fetchJson, @@ -27,8 +20,6 @@ export function DelegateVoteHistory({ - - { - console.log('delegators from comp', delegators); - console.log('dtotalDelegated', totalDelegated); - const bpi = useBreakpointIndex(); - const network = getNetwork(); - - return ( - - - - - - Address - - - MKR Delegated - - - Voting Power - - {/* - Verify - */} - - - - {delegators ? ( - <> - {delegators.map(({ address, lockAmount }, i) => ( - - - - - {/* {delegateAddresses[v.voter] ? ( - delegateAddresses[v.voter] - ) : ( */} -
- {/* )} */} - - - - {/* - {v.rankedChoiceOption && v.rankedChoiceOption.length > 1 - ? poll.options[v.rankedChoiceOption[0]] - : poll.options[v.optionId]} - {v.rankedChoiceOption && v.rankedChoiceOption.length > 1 && '*'} - */} - {`${new BigNumber( - lockAmount - ).toFormat(2)}${bpi > 0 ? ' MKR' : ''}`} - - {`${new BigNumber(lockAmount).div(totalDelegated.toBigNumber()).times(100).toFormat(1)}%`} - -
- ))} - - ) : ( - - - - )} - -
- - Loading - -
- {/* {showRankedChoiceInfo && ( - - *First choice in ranked choice vote shown - - )} */} -
- ); -}; - -export default DelegatedByAddress; diff --git a/pages/address/[address]/index.tsx b/pages/address/[address]/index.tsx index 1f9c82511..ad9a2c268 100644 --- a/pages/address/[address]/index.tsx +++ b/pages/address/[address]/index.tsx @@ -4,7 +4,7 @@ import { useBreakpointIndex } from '@theme-ui/match-media'; import ErrorPage from 'next/error'; import Link from 'next/link'; import { Icon } from '@makerdao/dai-ui-icons'; -import getMaker, { getNetwork } from 'lib/maker'; +import { getNetwork } from 'lib/maker'; import { fetchJson } from 'lib/fetchJson'; import { useAnalytics } from 'modules/app/client/analytics/useAnalytics'; import { ANALYTICS_PAGES } from 'modules/app/client/analytics/analytics.constants'; @@ -20,46 +20,15 @@ import { AddressDetail } from 'modules/address/components/AddressDetail'; import { DelegateDetail } from 'modules/delegates/components'; import { HeadComponent } from 'modules/app/components/layout/Head'; import ManageDelegation from 'modules/delegates/components/ManageDelegation'; -import { SupportedNetworks } from 'lib/constants'; -import { utils } from 'ethers'; const AddressView = ({ addressInfo }: { addressInfo: AddressApiResponse }) => { const network = getNetwork(); const bpi = useBreakpointIndex({ defaultIndex: 2 }); - const [delegatedFrom, setDelegatedFrom] = useState(null); - const { trackButtonClick } = useAnalytics( addressInfo.isDelegate ? ANALYTICS_PAGES.DELEGATE_DETAIL : ANALYTICS_PAGES.ADDRESS_DETAIL ); - //Using monetsupply to test - useEffect(() => { - getMaker(network).then(maker => { - maker - .service('voteDelegate') - .getMkrLockedDelegate(addressInfo.address) - .then(data => { - const red = data.reduce((acc, { fromAddress, lockAmount }) => { - // const guy = '0xb088a3bc93f71b4de97b9de773e9647645983688'; - const currSum = acc[fromAddress]?.lockAmount - ? utils.parseEther(acc[fromAddress]?.lockAmount) - : utils.parseEther('0'); - - acc[fromAddress] = { lockAmount: utils.formatEther(currSum.add(utils.parseEther(lockAmount))) }; - return acc; - }, {}); - - //TODO do this in the reducer - const delegators = []; - for (const x in red) { - delegators.push({ address: x, ...red[x] }); - } - - setDelegatedFrom(delegators.sort((a, b) => (a.lockAmount > b.lockAmount ? -1 : 1))); - }); - }); - }, []); return ( { )} - {addressInfo.delegateInfo && ( - - )} + {addressInfo.delegateInfo && } {!addressInfo.delegateInfo && ( )} From 4820a97b061972c5fbbea4bea506ef523c73b269 Mon Sep 17 00:00:00 2001 From: Phil Bain Date: Tue, 30 Nov 2021 17:26:48 -0700 Subject: [PATCH 09/36] Revert "Revert "calculate delegator amounts and add table"" This reverts commit 2b54f3855a29e66146ec5a7a81be87b348c5a12e. --- .../delegates/components/DelegateDetail.tsx | 6 +- .../delegates/components/DelegateMKRChart.tsx | 2 +- .../components/DelegateVoteHistory.tsx | 11 +- .../components/DelegatedByAddress.tsx | 101 ++++++++++++++++++ pages/address/[address]/index.tsx | 37 ++++++- 5 files changed, 151 insertions(+), 6 deletions(-) create mode 100644 modules/delegates/components/DelegatedByAddress.tsx diff --git a/modules/delegates/components/DelegateDetail.tsx b/modules/delegates/components/DelegateDetail.tsx index d31c5e79e..f3a591d95 100644 --- a/modules/delegates/components/DelegateDetail.tsx +++ b/modules/delegates/components/DelegateDetail.tsx @@ -20,12 +20,13 @@ import { fetchJson } from 'lib/fetchJson'; import { PollingParticipationOverview } from 'modules/polling/components/PollingParticipationOverview'; import { AddressAPIStats } from 'modules/address/types/addressApiResponse'; import LastVoted from 'modules/polling/components/LastVoted'; +import { useLockedMkr } from 'modules/mkr/hooks/useLockedMkr'; type PropTypes = { delegate: Delegate; }; -export function DelegateDetail({ delegate }: PropTypes): React.ReactElement { +export function DelegateDetail({ delegate, delegatedFrom }: PropTypes): React.ReactElement { const { voteDelegateAddress } = delegate; const { data: statsData } = useSWR( `/api/address/${delegate.voteDelegateAddress}/stats?network=${getNetwork()}`, @@ -36,6 +37,7 @@ export function DelegateDetail({ delegate }: PropTypes): React.ReactElement { revalidateOnMount: true } ); + const { data: totalStaked, mutate: mutateTotalStaked } = useLockedMkr(delegate.voteDelegateAddress); const tabTitles = [ delegate.status === DelegateStatusEnum.recognized ? 'Delegate Credentials' : null, @@ -61,7 +63,7 @@ export function DelegateDetail({ delegate }: PropTypes): React.ReactElement { {statsData && } , - + ].filter(i => !!i); diff --git a/modules/delegates/components/DelegateMKRChart.tsx b/modules/delegates/components/DelegateMKRChart.tsx index 71e6717b5..8149a93a7 100644 --- a/modules/delegates/components/DelegateMKRChart.tsx +++ b/modules/delegates/components/DelegateMKRChart.tsx @@ -16,7 +16,7 @@ import { import FilterButton from 'modules/app/components/FilterButton'; import { useState } from 'react'; import { MKRWeightTimeRanges } from '../delegates.constants'; -import { fetchJson } from '@ethersproject/web'; +import { fetchJson } from 'lib/fetchJson'; import useSWR from 'swr'; import { getNetwork } from 'lib/maker'; import { format } from 'date-fns'; diff --git a/modules/delegates/components/DelegateVoteHistory.tsx b/modules/delegates/components/DelegateVoteHistory.tsx index eddeace2f..298683bd7 100644 --- a/modules/delegates/components/DelegateVoteHistory.tsx +++ b/modules/delegates/components/DelegateVoteHistory.tsx @@ -6,8 +6,15 @@ import useSWR from 'swr'; import { getNetwork } from 'lib/maker'; import { fetchJson } from 'lib/fetchJson'; import SkeletonThemed from 'modules/app/components/SkeletonThemed'; +import DelegatedByAddress from 'modules/delegates/components/DelegatedByAddress'; -export function DelegateVoteHistory({ delegate }: { delegate: Delegate }): React.ReactElement { +export function DelegateVoteHistory({ + delegate, + delegatedFrom, + totalStaked +}: { + delegate: Delegate; +}): React.ReactElement { const { data: statsData } = useSWR( `/api/address/${delegate.voteDelegateAddress}/stats?network=${getNetwork()}`, fetchJson, @@ -20,6 +27,8 @@ export function DelegateVoteHistory({ delegate }: { delegate: Delegate }): React + + { + console.log('delegators from comp', delegators); + console.log('dtotalDelegated', totalDelegated); + const bpi = useBreakpointIndex(); + const network = getNetwork(); + + return ( + + + + + + Address + + + MKR Delegated + + + Voting Power + + {/* + Verify + */} + + + + {delegators ? ( + <> + {delegators.map(({ address, lockAmount }, i) => ( + + + + + {/* {delegateAddresses[v.voter] ? ( + delegateAddresses[v.voter] + ) : ( */} +
+ {/* )} */} + + + + {/* + {v.rankedChoiceOption && v.rankedChoiceOption.length > 1 + ? poll.options[v.rankedChoiceOption[0]] + : poll.options[v.optionId]} + {v.rankedChoiceOption && v.rankedChoiceOption.length > 1 && '*'} + */} + {`${new BigNumber( + lockAmount + ).toFormat(2)}${bpi > 0 ? ' MKR' : ''}`} + + {`${new BigNumber(lockAmount).div(totalDelegated.toBigNumber()).times(100).toFormat(1)}%`} + +
+ ))} + + ) : ( + + + + )} + +
+ + Loading + +
+ {/* {showRankedChoiceInfo && ( + + *First choice in ranked choice vote shown + + )} */} +
+ ); +}; + +export default DelegatedByAddress; diff --git a/pages/address/[address]/index.tsx b/pages/address/[address]/index.tsx index ad9a2c268..1f9c82511 100644 --- a/pages/address/[address]/index.tsx +++ b/pages/address/[address]/index.tsx @@ -4,7 +4,7 @@ import { useBreakpointIndex } from '@theme-ui/match-media'; import ErrorPage from 'next/error'; import Link from 'next/link'; import { Icon } from '@makerdao/dai-ui-icons'; -import { getNetwork } from 'lib/maker'; +import getMaker, { getNetwork } from 'lib/maker'; import { fetchJson } from 'lib/fetchJson'; import { useAnalytics } from 'modules/app/client/analytics/useAnalytics'; import { ANALYTICS_PAGES } from 'modules/app/client/analytics/analytics.constants'; @@ -20,15 +20,46 @@ import { AddressDetail } from 'modules/address/components/AddressDetail'; import { DelegateDetail } from 'modules/delegates/components'; import { HeadComponent } from 'modules/app/components/layout/Head'; import ManageDelegation from 'modules/delegates/components/ManageDelegation'; +import { SupportedNetworks } from 'lib/constants'; +import { utils } from 'ethers'; const AddressView = ({ addressInfo }: { addressInfo: AddressApiResponse }) => { const network = getNetwork(); const bpi = useBreakpointIndex({ defaultIndex: 2 }); + const [delegatedFrom, setDelegatedFrom] = useState(null); + const { trackButtonClick } = useAnalytics( addressInfo.isDelegate ? ANALYTICS_PAGES.DELEGATE_DETAIL : ANALYTICS_PAGES.ADDRESS_DETAIL ); + //Using monetsupply to test + useEffect(() => { + getMaker(network).then(maker => { + maker + .service('voteDelegate') + .getMkrLockedDelegate(addressInfo.address) + .then(data => { + const red = data.reduce((acc, { fromAddress, lockAmount }) => { + // const guy = '0xb088a3bc93f71b4de97b9de773e9647645983688'; + const currSum = acc[fromAddress]?.lockAmount + ? utils.parseEther(acc[fromAddress]?.lockAmount) + : utils.parseEther('0'); + + acc[fromAddress] = { lockAmount: utils.formatEther(currSum.add(utils.parseEther(lockAmount))) }; + return acc; + }, {}); + + //TODO do this in the reducer + const delegators = []; + for (const x in red) { + delegators.push({ address: x, ...red[x] }); + } + + setDelegatedFrom(delegators.sort((a, b) => (a.lockAmount > b.lockAmount ? -1 : 1))); + }); + }); + }, []); return ( { )} - {addressInfo.delegateInfo && } + {addressInfo.delegateInfo && ( + + )} {!addressInfo.delegateInfo && ( )} From 85cad51f8897a26cc82bd456f800266180ef393e Mon Sep 17 00:00:00 2001 From: Phil Bain Date: Tue, 30 Nov 2021 18:15:06 -0700 Subject: [PATCH 10/36] delegation events for each delegator --- .../delegates/api/fetchDelegationHistory.ts | 34 +++++++++++-------- .../components/DelegateVoteHistory.tsx | 15 +++++--- .../components/DelegatedByAddress.tsx | 21 +++--------- modules/delegates/types/delegate.d.ts | 6 ++++ 4 files changed, 41 insertions(+), 35 deletions(-) diff --git a/modules/delegates/api/fetchDelegationHistory.ts b/modules/delegates/api/fetchDelegationHistory.ts index e67648151..c14e284a2 100644 --- a/modules/delegates/api/fetchDelegationHistory.ts +++ b/modules/delegates/api/fetchDelegationHistory.ts @@ -1,6 +1,4 @@ -import BigNumber from 'bignumber.js'; import { utils } from 'ethers'; -import { format, parse } from 'date-fns'; import { SupportedNetworks } from 'lib/constants'; import { DelegationHistory, MKRLockedDelegateAPIResponse } from '../types'; import getMaker from 'lib/maker'; @@ -14,20 +12,26 @@ export async function fetchDelegationHistory( .service('voteDelegate') .getMkrLockedDelegate(address); - const delegatorsR = addressData.reduce((acc, { fromAddress, lockAmount }) => { - const currSum = acc[fromAddress]?.lockAmount - ? utils.parseEther(acc[fromAddress]?.lockAmount) - : utils.parseEther('0'); + const delegators = addressData.reduce( + (acc, { fromAddress, lockAmount, blockTimestamp }) => { + const existing = acc.find(({ address }) => address === fromAddress); + if (existing) { + existing.lockAmount = utils.formatEther( + utils.parseEther(existing.lockAmount).add(utils.parseEther(lockAmount)) + ); + existing.events.push({ lockAmount, blockTimestamp }); + } else { + acc.push({ + address: fromAddress, + lockAmount: utils.formatEther(utils.parseEther(lockAmount)), + events: [{ lockAmount, blockTimestamp }] + }); + } - acc[fromAddress] = { lockAmount: utils.formatEther(currSum.add(utils.parseEther(lockAmount))) }; - return acc; - }, {}); - - //TODO do this in the reducer - const delegators: DelegationHistory[] = []; - for (const x in delegatorsR) { - delegators.push({ address: x, ...delegatorsR[x] }); - } + return acc; + }, + [] + ); return delegators.sort((a, b) => (a.lockAmount > b.lockAmount ? -1 : 1)); } diff --git a/modules/delegates/components/DelegateVoteHistory.tsx b/modules/delegates/components/DelegateVoteHistory.tsx index 18cd24cea..cef3dfada 100644 --- a/modules/delegates/components/DelegateVoteHistory.tsx +++ b/modules/delegates/components/DelegateVoteHistory.tsx @@ -1,6 +1,6 @@ import { PollVoteHistoryList } from 'modules/polling/components/PollVoteHistoryList'; import { AddressAPIStats } from 'modules/address/types/addressApiResponse'; -import { Box, Divider, Text } from 'theme-ui'; +import { Box, Divider, Flex, Heading, Text } from 'theme-ui'; import { Delegate } from '../types'; import useSWR from 'swr'; import { getNetwork } from 'lib/maker'; @@ -19,7 +19,7 @@ export function DelegateVoteHistory({ delegate }: { delegate: Delegate }): React } ); - const { data: delegators } = useSWR( + const { data: delegators } = useSWR( `/api/delegates/delegation-history/${delegate.voteDelegateAddress}?network=${getNetwork()}`, fetchJson, { @@ -32,8 +32,15 @@ export function DelegateVoteHistory({ delegate }: { delegate: Delegate }): React - - + {delegators && ( + + + MKR Delegated Per Address + + + + + )} { @@ -35,7 +33,7 @@ const DelegatedByAddress = ({ delegators, totalDelegated }: Props): JSX.Element MKR Delegated - Voting Power + Total Percent {/* Verify @@ -50,11 +48,7 @@ const DelegatedByAddress = ({ delegators, totalDelegated }: Props): JSX.Element - {/* {delegateAddresses[v.voter] ? ( - delegateAddresses[v.voter] - ) : ( */}
- {/* )} */} @@ -87,11 +81,6 @@ const DelegatedByAddress = ({ delegators, totalDelegated }: Props): JSX.Element )} - {/* {showRankedChoiceInfo && ( - - *First choice in ranked choice vote shown - - )} */} ); }; diff --git a/modules/delegates/types/delegate.d.ts b/modules/delegates/types/delegate.d.ts index 0b48ebacd..ba4391e2e 100644 --- a/modules/delegates/types/delegate.d.ts +++ b/modules/delegates/types/delegate.d.ts @@ -41,4 +41,10 @@ export type Delegate = { export type DelegationHistory = { address: string; lockAmount: string; + events: DelegationHistoryEvent[]; +}; + +export type DelegationHistoryEvent = { + lockAmount: string; + blockTimestamp: string; }; From 78e447409326f7489ba7e7461e657db1ab323128 Mon Sep 17 00:00:00 2001 From: Phil Bain Date: Wed, 1 Dec 2021 11:50:28 -0700 Subject: [PATCH 11/36] add expando to delegator event row --- lib/theme.js | 68 ++++++++- .../delegates/api/fetchDelegationHistory.ts | 4 +- .../components/DelegatedByAddress.tsx | 137 +++++++++++++----- 3 files changed, 174 insertions(+), 35 deletions(-) diff --git a/lib/theme.js b/lib/theme.js index c694184d6..09d9a4bbf 100644 --- a/lib/theme.js +++ b/lib/theme.js @@ -60,6 +60,8 @@ export default { tagColorFifteenBg: '#FFFBEF', tagColorSixteen: '#FF8237', tagColorSixteenBg: '#FFFBEF', + bull: '#1AAB9B', + bear: '#F77249', modes: { dark: { primary: '#1DC1AE', @@ -117,7 +119,9 @@ export default { tagColorFifteen: '#FF8237', tagColorFifteenBg: '#121212', tagColorSixteen: '#FF8237', - tagColorSixteenBg: '#121212' + tagColorSixteenBg: '#121212', + bull: '#1AAB9B', + bear: '#F77249' } } }, @@ -285,6 +289,13 @@ export default { color: 'textSecondary', letterSpacing: '0.05em' }, + smallCaps: { + ...theme.text.caps, + fontSize: 1, + fontWeight: 'body', + color: 'textSecondary', + letterSpacing: '0.05em' + }, secondary: { color: 'textSecondary', fontSize: '15px', @@ -1095,6 +1106,61 @@ export default { /> ) + }, + decrease: { + viewBox: '0 0 8 7', + path: ( + + + + + + ) + }, + increase: { + viewBox: '0 0 8 8', + path: ( + + + + + + ) + }, + minus: { + viewBox: '0 0 6 3', + path: + }, + plus: { + viewBox: '0 0 9 9', + path: ( + + ) } } }; diff --git a/modules/delegates/api/fetchDelegationHistory.ts b/modules/delegates/api/fetchDelegationHistory.ts index c14e284a2..4d2c9512c 100644 --- a/modules/delegates/api/fetchDelegationHistory.ts +++ b/modules/delegates/api/fetchDelegationHistory.ts @@ -33,5 +33,7 @@ export async function fetchDelegationHistory( [] ); - return delegators.sort((a, b) => (a.lockAmount > b.lockAmount ? -1 : 1)); + return delegators.sort((a, b) => + utils.parseEther(a.lockAmount).gt(utils.parseEther(b.lockAmount)) ? -1 : 1 + ); } diff --git a/modules/delegates/components/DelegatedByAddress.tsx b/modules/delegates/components/DelegatedByAddress.tsx index b446a6b32..8f9f73628 100644 --- a/modules/delegates/components/DelegatedByAddress.tsx +++ b/modules/delegates/components/DelegatedByAddress.tsx @@ -1,18 +1,108 @@ +import { useState } from 'react'; import Link from 'next/link'; -import { Box, Text, Link as ThemeUILink } from 'theme-ui'; +import { Box, Text, Link as ThemeUILink, Flex, IconButton } from 'theme-ui'; import { useBreakpointIndex } from '@theme-ui/match-media'; +import { Icon } from '@makerdao/dai-ui-icons'; import BigNumber from 'bignumber.js'; +import { format } from 'date-fns'; import { getNetwork } from 'lib/maker'; import { CurrencyObject } from 'types/currency'; import { Address } from 'modules/address/components/Address'; import { DelegationHistory } from '../types'; -type Props = { +type DelegatedByAddressProps = { delegators: DelegationHistory[]; totalDelegated: CurrencyObject; }; -const DelegatedByAddress = ({ delegators, totalDelegated }: Props): JSX.Element => { +type CollapsableRowProps = { + delegator: DelegationHistory; + network: string; + bpi: number; + totalDelegated: CurrencyObject; +}; + +const CollapsableRow = ({ delegator, network, bpi, totalDelegated }: CollapsableRowProps) => { + const dateFormat = 'MMM dd yyyy h:mm'; + const [expanded, setExpanded] = useState(false); + const { address, lockAmount, events } = delegator; + return ( + + + + + +
+ + + + {expanded && ( + + {events.map(({ blockTimestamp }) => { + return ( + + {format(new Date(blockTimestamp), dateFormat)} + + ); + })} + + )} + + + + {`${new BigNumber(lockAmount).toFormat(2)}${bpi > 0 ? ' MKR' : ''}`} + + {expanded && ( + + {events.map(({ blockTimestamp, lockAmount }) => { + return ( + + {lockAmount.indexOf('-') === 0 ? ( + + ) : ( + + )} + + {new BigNumber( + lockAmount.indexOf('-') === 0 ? lockAmount.substring(1) : lockAmount + ).toFormat(2)} + + + ); + })} + + )} + + + {totalDelegated ? ( + + {`${new BigNumber(lockAmount).div(totalDelegated.toBigNumber()).times(100).toFormat(1)}%`} + + ) : ( + -- + )} + + + + setExpanded(!expanded)}> + + + + + + ); +}; + +const DelegatedByAddress = ({ delegators, totalDelegated }: DelegatedByAddressProps): JSX.Element => { const bpi = useBreakpointIndex(); const network = getNetwork(); @@ -35,41 +125,22 @@ const DelegatedByAddress = ({ delegators, totalDelegated }: Props): JSX.Element Total Percent - {/* + Verify - */} + {delegators ? ( - <> - {delegators.map(({ address, lockAmount }, i) => ( - - - - -
- - - - {/* - {v.rankedChoiceOption && v.rankedChoiceOption.length > 1 - ? poll.options[v.rankedChoiceOption[0]] - : poll.options[v.optionId]} - {v.rankedChoiceOption && v.rankedChoiceOption.length > 1 && '*'} - */} - {`${new BigNumber( - lockAmount - ).toFormat(2)}${bpi > 0 ? ' MKR' : ''}`} - - {`${new BigNumber(lockAmount).div(totalDelegated.toBigNumber()).times(100).toFormat(1)}%`} - - - ))} - + delegators.map((delegator, i) => ( + + )) ) : ( From 7819b3f8c92521d3ea380b357c285181bf47f905 Mon Sep 17 00:00:00 2001 From: Phil Bain Date: Wed, 1 Dec 2021 12:29:48 -0700 Subject: [PATCH 12/36] move delegators into stats tab --- .../delegates/components/DelegateDetail.tsx | 19 ++++++++++++++- .../components/DelegateVoteHistory.tsx | 23 +------------------ .../components/DelegatedByAddress.tsx | 15 ++++++++++++ 3 files changed, 34 insertions(+), 23 deletions(-) diff --git a/modules/delegates/components/DelegateDetail.tsx b/modules/delegates/components/DelegateDetail.tsx index d31c5e79e..61a31973e 100644 --- a/modules/delegates/components/DelegateDetail.tsx +++ b/modules/delegates/components/DelegateDetail.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { Box, Text, Link as ExternalLink, Flex, Divider } from 'theme-ui'; +import { Box, Text, Link as ExternalLink, Flex, Divider, Heading } from 'theme-ui'; import { Icon } from '@makerdao/dai-ui-icons'; import { getNetwork } from 'lib/maker'; import { getEtherscanLink } from 'lib/utils'; @@ -20,6 +20,9 @@ import { fetchJson } from 'lib/fetchJson'; import { PollingParticipationOverview } from 'modules/polling/components/PollingParticipationOverview'; import { AddressAPIStats } from 'modules/address/types/addressApiResponse'; import LastVoted from 'modules/polling/components/LastVoted'; +import { useLockedMkr } from 'modules/mkr/hooks/useLockedMkr'; +import DelegatedByAddress from 'modules/delegates/components/DelegatedByAddress'; +import { DelegationHistory } from 'modules/delegates/types'; type PropTypes = { delegate: Delegate; @@ -36,6 +39,14 @@ export function DelegateDetail({ delegate }: PropTypes): React.ReactElement { revalidateOnMount: true } ); + const { data: delegators } = useSWR( + `/api/delegates/delegation-history/${delegate.voteDelegateAddress}?network=${getNetwork()}`, + fetchJson, + { + revalidateOnMount: true + } + ); + const { data: totalStaked } = useLockedMkr(delegate.voteDelegateAddress); const tabTitles = [ delegate.status === DelegateStatusEnum.recognized ? 'Delegate Credentials' : null, @@ -54,6 +65,12 @@ export function DelegateDetail({ delegate }: PropTypes): React.ReactElement { )} {delegate.status === DelegateStatusEnum.recognized && } + {delegators && delegators?.length > 0 && ( + + + + )} + {delegate.status === DelegateStatusEnum.recognized && } diff --git a/modules/delegates/components/DelegateVoteHistory.tsx b/modules/delegates/components/DelegateVoteHistory.tsx index cef3dfada..eddeace2f 100644 --- a/modules/delegates/components/DelegateVoteHistory.tsx +++ b/modules/delegates/components/DelegateVoteHistory.tsx @@ -1,14 +1,11 @@ import { PollVoteHistoryList } from 'modules/polling/components/PollVoteHistoryList'; import { AddressAPIStats } from 'modules/address/types/addressApiResponse'; -import { Box, Divider, Flex, Heading, Text } from 'theme-ui'; +import { Box, Divider, Text } from 'theme-ui'; import { Delegate } from '../types'; import useSWR from 'swr'; import { getNetwork } from 'lib/maker'; import { fetchJson } from 'lib/fetchJson'; import SkeletonThemed from 'modules/app/components/SkeletonThemed'; -import { useLockedMkr } from 'modules/mkr/hooks/useLockedMkr'; -import DelegatedByAddress from 'modules/delegates/components/DelegatedByAddress'; -import { DelegationHistory } from 'modules/delegates/types'; export function DelegateVoteHistory({ delegate }: { delegate: Delegate }): React.ReactElement { const { data: statsData } = useSWR( @@ -19,28 +16,10 @@ export function DelegateVoteHistory({ delegate }: { delegate: Delegate }): React } ); - const { data: delegators } = useSWR( - `/api/delegates/delegation-history/${delegate.voteDelegateAddress}?network=${getNetwork()}`, - fetchJson, - { - revalidateOnMount: true - } - ); - const { data: totalStaked } = useLockedMkr(delegate.voteDelegateAddress); - return ( - {delegators && ( - - - MKR Delegated Per Address - - - - - )} + + + Delegators + + + MKR delegated by address + + Date: Wed, 1 Dec 2021 21:16:59 -0700 Subject: [PATCH 13/36] fix styling --- .../delegates/components/DelegateDetail.tsx | 12 ++++++---- .../components/DelegatedByAddress.tsx | 23 +++++++++++-------- 2 files changed, 20 insertions(+), 15 deletions(-) diff --git a/modules/delegates/components/DelegateDetail.tsx b/modules/delegates/components/DelegateDetail.tsx index 61a31973e..67bee5643 100644 --- a/modules/delegates/components/DelegateDetail.tsx +++ b/modules/delegates/components/DelegateDetail.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { Box, Text, Link as ExternalLink, Flex, Divider, Heading } from 'theme-ui'; +import { Box, Text, Link as ExternalLink, Flex, Divider } from 'theme-ui'; import { Icon } from '@makerdao/dai-ui-icons'; import { getNetwork } from 'lib/maker'; import { getEtherscanLink } from 'lib/utils'; @@ -66,11 +66,13 @@ export function DelegateDetail({ delegate }: PropTypes): React.ReactElement { )} {delegate.status === DelegateStatusEnum.recognized && } {delegators && delegators?.length > 0 && ( - - - + <> + + + + + )} - {delegate.status === DelegateStatusEnum.recognized && } diff --git a/modules/delegates/components/DelegatedByAddress.tsx b/modules/delegates/components/DelegatedByAddress.tsx index 56d795bb1..39e80b329 100644 --- a/modules/delegates/components/DelegatedByAddress.tsx +++ b/modules/delegates/components/DelegatedByAddress.tsx @@ -8,7 +8,8 @@ import { format } from 'date-fns'; import { getNetwork } from 'lib/maker'; import { CurrencyObject } from 'types/currency'; import { Address } from 'modules/address/components/Address'; -import { DelegationHistory } from '../types'; +import Skeleton from 'modules/app/components/SkeletonThemed'; +import { DelegationHistory } from 'modules/delegates/types'; type DelegatedByAddressProps = { delegators: DelegationHistory[]; @@ -37,10 +38,10 @@ const CollapsableRow = ({ delegator, network, bpi, totalDelegated }: Collapsable {expanded && ( - + {events.map(({ blockTimestamp }) => { return ( - + {format(new Date(blockTimestamp), dateFormat)} ); @@ -48,15 +49,15 @@ const CollapsableRow = ({ delegator, network, bpi, totalDelegated }: Collapsable )} - - + + {`${new BigNumber(lockAmount).toFormat(2)}${bpi > 0 ? ' MKR' : ''}`} {expanded && ( - + {events.map(({ blockTimestamp, lockAmount }) => { return ( - + {lockAmount.indexOf('-') === 0 ? ( ) : ( @@ -75,11 +76,13 @@ const CollapsableRow = ({ delegator, network, bpi, totalDelegated }: Collapsable {totalDelegated ? ( - + {`${new BigNumber(lockAmount).div(totalDelegated.toBigNumber()).times(100).toFormat(1)}%`} ) : ( - -- + + + )} @@ -120,7 +123,7 @@ const DelegatedByAddress = ({ delegators, totalDelegated }: DelegatedByAddressPr Delegators - MKR delegated by address + Addresses that have delegated MKR
Date: Thu, 2 Dec 2021 11:18:48 +0100 Subject: [PATCH 14/36] Delegate filters store. Refactor stores into modules (#257) * Add first version of delegate filters store. Refactor stores into modules * stuff * list * Delegates sort * rename to shadow * delegate filters empty state and counter --- __tests__/helpers.tsx | 2 +- __tests__/pages/delegates.spec.tsx | 2 +- __tests__/pages/esmodule.spec.tsx | 4 +- __tests__/pages/executive.spec.tsx | 2 +- lib/theme.js | 4 + lib/utils.ts | 4 +- modules/app/components/SystemStatsSidebar.tsx | 2 +- modules/app/components/TxFinal.tsx | 2 +- modules/app/components/TxInProgress.tsx | 2 +- modules/app/components/layout/Header.tsx | 2 +- .../layout/header/AccountSelect.tsx | 4 +- .../layout/header/TransactionBox.tsx | 2 +- .../components/layout/header/VotingWeight.tsx | 2 +- .../app/stores/__tests__}/accounts.spec.ts | 11 ++- .../stores/__tests__}/transactions.spec.ts | 9 ++- {stores => modules/app/stores}/accounts.ts | 6 +- .../app/stores}/transactions.ts | 2 +- {stores => modules/app/stores}/uiFilters.ts | 0 {types => modules/app/types}/account.d.ts | 0 {types => modules/app/types}/comment.d.ts | 0 {types => modules/app/types}/currency.d.ts | 0 {types => modules/app/types}/global.d.ts | 0 .../app/types}/spellStateDiff.d.ts | 0 {types => modules/app/types}/transaction.d.ts | 0 .../app/types}/voteProxyContract.d.ts | 0 modules/delegates/components/DelegateCard.tsx | 2 +- .../components/DelegateMKRDelegatedStats.tsx | 2 +- .../delegates/components/DelegatesFilter.tsx | 70 ++++++++++++++++ .../delegates/components/DelegatesSort.tsx | 24 ++++++ .../components/modals/DelegateModal.tsx | 4 +- .../components/modals/InputDelegateMkr.tsx | 2 +- .../components/modals/UndelegateModal.tsx | 4 +- modules/delegates/helpers/filterDelegates.ts | 25 ++++++ .../delegates/stores/delegatesFiltersStore.ts | 79 +++++++++++++++++++ .../types}/voteDelegateContract.d.ts | 0 .../api/fetchESModuleStats.ts | 4 +- .../components/BurnModal.tsx | 6 +- .../components/ESMHistory.tsx | 4 +- .../components/ProgressRing.tsx | 2 +- .../components/ShutdownModal.tsx | 6 +- .../components/burnModal/BurnTxSuccess.tsx | 2 +- .../components/burnModal/MKRAmount.tsx | 2 +- .../emergency-shutdown/types}/esmodule.d.ts | 0 .../executive/components/CommentSortBy.tsx | 2 +- modules/executive/components/Comments.tsx | 4 +- modules/executive/components/DateFilter.tsx | 2 +- .../components/ExecutiveOverviewCard.tsx | 4 +- modules/executive/components/OnChainFx.tsx | 2 +- .../executive/components/ProposalsSortBy.tsx | 2 +- modules/executive/components/VoteModal.tsx | 4 +- .../executive/components/WithdrawOldChief.tsx | 6 +- modules/executive/helpers/getStatusText.ts | 4 +- modules/executive/hooks/useMkrOnHat.ts | 2 +- modules/executive/hooks/useVotedProposals.ts | 2 +- modules/home/components/ExecutiveCard.tsx | 4 +- .../home/components/ExecutiveIndicator.tsx | 2 +- modules/home/components/PollingIndicator.tsx | 4 +- modules/home/components/SystemStats.tsx | 2 +- modules/mkr/components/Deposit.tsx | 6 +- modules/mkr/components/Withdraw.tsx | 6 +- modules/mkr/hooks/useLockedMkr.ts | 6 +- modules/mkr/hooks/useMkrBalance.ts | 2 +- modules/mkr/hooks/useMkrDelegated.ts | 2 +- modules/polling/components/BallotBox.tsx | 6 +- modules/polling/components/BallotStatus.tsx | 6 +- modules/polling/components/CategoryFilter.tsx | 2 +- modules/polling/components/DateFilter.tsx | 2 +- .../polling/components/MobileVoteSheet.tsx | 6 +- modules/polling/components/PollBar.tsx | 4 +- .../polling/components/PollCategoryTag.tsx | 2 +- .../polling/components/PollCreateModal.tsx | 4 +- .../polling/components/PollOverviewCard.tsx | 4 +- .../polling/components/PollVotingStatus.tsx | 6 +- modules/polling/components/QuickVote.tsx | 6 +- modules/polling/components/VoteBox.tsx | 2 +- modules/polling/components/VotingWeight.tsx | 2 +- .../components/__tests__/QuickVote.spec.tsx | 2 +- .../__tests__/RankedChoiceSelect.spec.tsx | 2 +- .../__tests__/SingleSelect.spec.tsx | 2 +- .../components/__tests__/polling.spec.tsx | 2 +- .../polling/components/review/ReviewBox.tsx | 6 +- .../polling/stores/ballotStore.ts | 7 +- {types => modules/polling/types}/ballot.d.ts | 0 modules/web3/hooks/useAccountChange.tsx | 2 +- package.json | 4 +- pages/account.tsx | 4 +- pages/delegates/index.tsx | 61 ++++++++++++-- pages/esmodule.tsx | 2 +- pages/executive.tsx | 4 +- pages/executive/[proposal-id].tsx | 6 +- pages/polling.tsx | 7 +- pages/polling/[poll-hash].tsx | 4 +- pages/polling/create.tsx | 2 +- pages/polling/review.tsx | 4 +- tsconfig.json | 1 - 95 files changed, 393 insertions(+), 149 deletions(-) rename {__tests__/stores => modules/app/stores/__tests__}/accounts.spec.ts (89%) rename {__tests__/stores => modules/app/stores/__tests__}/transactions.spec.ts (92%) rename {stores => modules/app/stores}/accounts.ts (92%) rename {stores => modules/app/stores}/transactions.ts (99%) rename {stores => modules/app/stores}/uiFilters.ts (100%) rename {types => modules/app/types}/account.d.ts (100%) rename {types => modules/app/types}/comment.d.ts (100%) rename {types => modules/app/types}/currency.d.ts (100%) rename {types => modules/app/types}/global.d.ts (100%) rename {types => modules/app/types}/spellStateDiff.d.ts (100%) rename {types => modules/app/types}/transaction.d.ts (100%) rename {types => modules/app/types}/voteProxyContract.d.ts (100%) create mode 100644 modules/delegates/components/DelegatesFilter.tsx create mode 100644 modules/delegates/components/DelegatesSort.tsx create mode 100644 modules/delegates/helpers/filterDelegates.ts create mode 100644 modules/delegates/stores/delegatesFiltersStore.ts rename {types => modules/delegates/types}/voteDelegateContract.d.ts (100%) rename {types => modules/emergency-shutdown/types}/esmodule.d.ts (100%) rename stores/ballot.ts => modules/polling/stores/ballotStore.ts (91%) rename {types => modules/polling/types}/ballot.d.ts (100%) diff --git a/__tests__/helpers.tsx b/__tests__/helpers.tsx index fedd8b07d..469b8ec55 100644 --- a/__tests__/helpers.tsx +++ b/__tests__/helpers.tsx @@ -6,7 +6,7 @@ import { ethers } from 'ethers'; import WrappedAccountSelect from 'modules/app/components/layout/header/AccountSelect'; import theme from 'lib/theme'; import React from 'react'; -import { accountsApi } from 'stores/accounts'; +import { accountsApi } from 'modules/app/stores/accounts'; import { createCurrency } from '@makerdao/currency'; import { AnalyticsProvider } from 'modules/app/client/analytics/AnalyticsContext'; import { CookiesProvider } from 'modules/app/client/cookies/CookiesContext'; diff --git a/__tests__/pages/delegates.spec.tsx b/__tests__/pages/delegates.spec.tsx index c2666af7e..bf1fa5947 100644 --- a/__tests__/pages/delegates.spec.tsx +++ b/__tests__/pages/delegates.spec.tsx @@ -86,7 +86,7 @@ describe('Delegates list page', () => { }); test('can delegate MKR to a delegate', async () => { - await screen.findByText('Recognized Delegates'); + await screen.findAllByText('Recognized Delegates'); // Open delegate modal const delegateButton = screen.getByText('Delegate'); diff --git a/__tests__/pages/esmodule.spec.tsx b/__tests__/pages/esmodule.spec.tsx index 5289d6aed..ac6b7f422 100644 --- a/__tests__/pages/esmodule.spec.tsx +++ b/__tests__/pages/esmodule.spec.tsx @@ -1,11 +1,11 @@ // @ts-nocheck import { renderWithTheme } from '../helpers'; -import { cleanup, fireEvent, waitFor, configure, screen } from '@testing-library/react'; +import { fireEvent, waitFor, configure, screen } from '@testing-library/react'; import waitForExpect from 'wait-for-expect'; import { TestAccountProvider } from '@makerdao/test-helpers'; import ESModule from '../../pages/esmodule'; import getMaker from '../../lib/maker'; -import { accountsApi } from '../../stores/accounts'; +import { accountsApi } from '../../modules/app/stores/accounts'; import BigNumber from 'bignumber.js'; import { ethers } from 'ethers'; diff --git a/__tests__/pages/executive.spec.tsx b/__tests__/pages/executive.spec.tsx index edabd5783..31deb38f9 100644 --- a/__tests__/pages/executive.spec.tsx +++ b/__tests__/pages/executive.spec.tsx @@ -10,7 +10,7 @@ import { } from '../helpers'; import { ExecutiveOverview } from '../../pages/executive'; import proposals from 'modules/executive/api/mocks/proposals.json'; -import { accountsApi } from 'stores/accounts'; +import { accountsApi } from 'modules/app/stores/accounts'; jest.mock('@theme-ui/match-media', () => { return { diff --git a/lib/theme.js b/lib/theme.js index c694184d6..75d8b7408 100644 --- a/lib/theme.js +++ b/lib/theme.js @@ -160,6 +160,10 @@ export default { variant: 'cards.primary', p: 3 }, + noPadding: { + variant: 'cards.primary', + p: 0 + }, tight: { variant: 'cards.primary', p: [2, 2] diff --git a/lib/utils.ts b/lib/utils.ts index 19e89e0ec..09725eb36 100644 --- a/lib/utils.ts +++ b/lib/utils.ts @@ -5,8 +5,8 @@ import { cloneElement } from 'react'; import { jsx } from 'theme-ui'; import { css, ThemeUIStyleObject } from '@theme-ui/css'; import BigNumber from 'bignumber.js'; -import { CurrencyObject } from 'types/currency'; -import { SpellStateDiff } from 'types/spellStateDiff'; +import { CurrencyObject } from 'modules/app/types/currency'; +import { SpellStateDiff } from 'modules/app/types/spellStateDiff'; import { SupportedNetworks, ETHERSCAN_PREFIXES } from './constants'; import getMaker from './maker'; import mockPolls from 'modules/polling/api/mocks/polls.json'; diff --git a/modules/app/components/SystemStatsSidebar.tsx b/modules/app/components/SystemStatsSidebar.tsx index 596f1329a..7ad9ac767 100644 --- a/modules/app/components/SystemStatsSidebar.tsx +++ b/modules/app/components/SystemStatsSidebar.tsx @@ -8,7 +8,7 @@ import getMaker, { DAI, getNetwork } from 'lib/maker'; import { useMkrBalance } from 'modules/mkr/hooks/useMkrBalance'; import { bigNumberKFormat, formatAddress, getEtherscanLink } from 'lib/utils'; import BigNumber from 'bignumber.js'; -import { CurrencyObject } from 'types/currency'; +import { CurrencyObject } from 'modules/app/types/currency'; async function getSystemStats(): Promise< [CurrencyObject, BigNumber, CurrencyObject, CurrencyObject, CurrencyObject] diff --git a/modules/app/components/TxFinal.tsx b/modules/app/components/TxFinal.tsx index fbfb08be2..f575bc814 100644 --- a/modules/app/components/TxFinal.tsx +++ b/modules/app/components/TxFinal.tsx @@ -1,7 +1,7 @@ import { Button, Flex, Link, Text } from 'theme-ui'; import { Icon } from '@makerdao/dai-ui-icons'; import TxIndicators from 'modules/app/components/TxIndicators'; -import { TXMined } from 'types/transaction'; +import { TXMined } from 'modules/app/types/transaction'; import { getNetwork } from 'lib/maker'; import { getEtherscanLink } from 'lib/utils'; diff --git a/modules/app/components/TxInProgress.tsx b/modules/app/components/TxInProgress.tsx index 7a1bd41d6..eb48318c6 100644 --- a/modules/app/components/TxInProgress.tsx +++ b/modules/app/components/TxInProgress.tsx @@ -1,7 +1,7 @@ import { Flex, Text, Box, Link } from '@theme-ui/components'; import { Icon } from '@makerdao/dai-ui-icons'; import TxIndicators from 'modules/app/components/TxIndicators'; -import { TXMined } from 'types/transaction'; +import { TXMined } from 'modules/app/types/transaction'; import { getNetwork } from 'lib/maker'; import { getEtherscanLink } from 'lib/utils'; diff --git a/modules/app/components/layout/Header.tsx b/modules/app/components/layout/Header.tsx index 5a3ce0f10..cc2e4c680 100644 --- a/modules/app/components/layout/Header.tsx +++ b/modules/app/components/layout/Header.tsx @@ -8,7 +8,7 @@ import AccountSelect from './header/AccountSelect'; import BallotStatus from 'modules/polling/components/BallotStatus'; import { useState, useEffect } from 'react'; import { useBreakpointIndex } from '@theme-ui/match-media'; -import useAccountsStore from 'stores/accounts'; +import useAccountsStore from 'modules/app/stores/accounts'; import ColorModeToggle from './header/ColorModeToggle'; const Header = (): JSX.Element => { diff --git a/modules/app/components/layout/header/AccountSelect.tsx b/modules/app/components/layout/header/AccountSelect.tsx index 8aff3765a..7f692283f 100644 --- a/modules/app/components/layout/header/AccountSelect.tsx +++ b/modules/app/components/layout/header/AccountSelect.tsx @@ -11,13 +11,13 @@ import getMaker, { getNetwork, chainIdToNetworkName } from 'lib/maker'; import { getLibrary, connectors, ConnectorName } from 'lib/maker/web3react'; import { syncMakerAccount, useEagerConnect } from 'lib/maker/web3react/hooks'; import { formatAddress } from 'lib/utils'; -import useTransactionStore from 'stores/transactions'; +import useTransactionStore from 'modules/app/stores/transactions'; import { fadeIn, slideUp } from 'lib/keyframes'; import AccountBox from './AccountBox'; import TransactionBox from './TransactionBox'; import VotingWeight from './VotingWeight'; import NetworkAlertModal from './NetworkAlertModal'; -import useAccountsStore from 'stores/accounts'; +import useAccountsStore from 'modules/app/stores/accounts'; import { AbstractConnector } from '@web3-react/abstract-connector'; import ConnectWalletButton from 'modules/web3/components/ConnectWalletButton'; import { useContext } from 'react'; diff --git a/modules/app/components/layout/header/TransactionBox.tsx b/modules/app/components/layout/header/TransactionBox.tsx index e9c9554b0..c0db22d0a 100644 --- a/modules/app/components/layout/header/TransactionBox.tsx +++ b/modules/app/components/layout/header/TransactionBox.tsx @@ -3,7 +3,7 @@ import { Icon } from '@makerdao/dai-ui-icons'; import { getEtherscanLink } from 'lib/utils'; import { getNetwork } from 'lib/maker'; -import { Transaction, TXPending } from 'types/transaction'; +import { Transaction, TXPending } from 'modules/app/types/transaction'; type Props = { tx: Transaction; diff --git a/modules/app/components/layout/header/VotingWeight.tsx b/modules/app/components/layout/header/VotingWeight.tsx index f751a0716..b8136a63b 100644 --- a/modules/app/components/layout/header/VotingWeight.tsx +++ b/modules/app/components/layout/header/VotingWeight.tsx @@ -1,6 +1,6 @@ import { Flex, Text } from 'theme-ui'; import useSWR from 'swr'; -import useAccountsStore from 'stores/accounts'; +import useAccountsStore from 'modules/app/stores/accounts'; import getMaker from 'lib/maker'; import { getVotingWeightCopy } from 'modules/polling/helpers/getVotingWeightCopy'; diff --git a/__tests__/stores/accounts.spec.ts b/modules/app/stores/__tests__/accounts.spec.ts similarity index 89% rename from __tests__/stores/accounts.spec.ts rename to modules/app/stores/__tests__/accounts.spec.ts index 8adc5ce44..e2fe35f5d 100644 --- a/__tests__/stores/accounts.spec.ts +++ b/modules/app/stores/__tests__/accounts.spec.ts @@ -1,8 +1,8 @@ import { TestAccountProvider } from '@makerdao/test-helpers'; import waitForExpect from 'wait-for-expect'; -import { accountsApi } from '../../stores/accounts'; -import getMaker from '../../lib/maker'; +import { accountsApi } from '../accounts'; +import getMaker from '../../../../lib/maker'; let maker; describe('Store accounts', () => { @@ -10,10 +10,10 @@ describe('Store accounts', () => { maker = await getMaker(); accountsApi.getState().addAccountsListener(maker); }); - + test('should automatically add an account changed listener to dai.js', async () => { expect(accountsApi.getState().currentAccount).toBeUndefined(); - + const nextAccount = TestAccountProvider.nextAccount(); await maker.service('accounts').addAccount('test-account', { type: 'privateKey', @@ -26,5 +26,4 @@ describe('Store accounts', () => { expect(currentAccount?.name).toBe('test-account'); }); }); - -}); \ No newline at end of file +}); diff --git a/__tests__/stores/transactions.spec.ts b/modules/app/stores/__tests__/transactions.spec.ts similarity index 92% rename from __tests__/stores/transactions.spec.ts rename to modules/app/stores/__tests__/transactions.spec.ts index dbc4d4389..40c99a679 100644 --- a/__tests__/stores/transactions.spec.ts +++ b/modules/app/stores/__tests__/transactions.spec.ts @@ -1,7 +1,8 @@ +/* eslint @typescript-eslint/no-var-requires: "off" */ import { TestAccountProvider } from '@makerdao/test-helpers'; import waitForExpect from 'wait-for-expect'; -import { TX_NOT_ENOUGH_FUNDS } from '../../lib/errors'; +import { TX_NOT_ENOUGH_FUNDS } from '../../../../lib/errors'; waitForExpect.defaults.interval = 1; @@ -13,9 +14,9 @@ describe('Transactions', () => { beforeEach(async () => { jest.resetModules(); - maker = await require('../../lib/maker').default(); - ({ ETH } = require('../../lib/maker')); - ({ transactionsApi, transactionsSelectors } = require('../../stores/transactions')); + maker = await require('../../../../lib/maker').default(); + ({ ETH } = require('../../../../lib/maker')); + ({ transactionsApi, transactionsSelectors } = require('../transactions')); }); test('should call initTx, setPending, and setMined for successful transactions', async () => { diff --git a/stores/accounts.ts b/modules/app/stores/accounts.ts similarity index 92% rename from stores/accounts.ts rename to modules/app/stores/accounts.ts index 5d59e669f..07e621037 100644 --- a/stores/accounts.ts +++ b/modules/app/stores/accounts.ts @@ -3,9 +3,9 @@ import getMaker from 'lib/maker'; import oldVoteProxyFactoryAbi from 'lib/abis/oldVoteProxyFactoryAbi.json'; import { getNetwork } from 'lib/maker'; import { oldVoteProxyFactoryAddress, SupportedNetworks } from 'lib/constants'; -import { Account } from 'types/account'; -import { OldVoteProxyContract, VoteProxyContract } from 'types/voteProxyContract'; -import { VoteDelegateContract } from 'types/voteDelegateContract'; +import { Account } from 'modules/app/types/account'; +import { OldVoteProxyContract, VoteProxyContract } from 'modules/app/types/voteProxyContract'; +import { VoteDelegateContract } from 'modules/delegates/types/voteDelegateContract'; export const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000'; diff --git a/stores/transactions.ts b/modules/app/stores/transactions.ts similarity index 99% rename from stores/transactions.ts rename to modules/app/stores/transactions.ts index 1814acbde..49ee28f31 100644 --- a/stores/transactions.ts +++ b/modules/app/stores/transactions.ts @@ -3,7 +3,7 @@ import invariant from 'tiny-invariant'; import { v4 as uuidv4 } from 'uuid'; import { parseTxError } from 'lib/errors'; import getMaker from 'lib/maker'; -import { Transaction, TXMined, TXPending, TXInitialized, TXError } from 'types/transaction'; +import { Transaction, TXMined, TXPending, TXInitialized, TXError } from 'modules/app/types/transaction'; type Hooks = { pending?: (txHash: string) => void; diff --git a/stores/uiFilters.ts b/modules/app/stores/uiFilters.ts similarity index 100% rename from stores/uiFilters.ts rename to modules/app/stores/uiFilters.ts diff --git a/types/account.d.ts b/modules/app/types/account.d.ts similarity index 100% rename from types/account.d.ts rename to modules/app/types/account.d.ts diff --git a/types/comment.d.ts b/modules/app/types/comment.d.ts similarity index 100% rename from types/comment.d.ts rename to modules/app/types/comment.d.ts diff --git a/types/currency.d.ts b/modules/app/types/currency.d.ts similarity index 100% rename from types/currency.d.ts rename to modules/app/types/currency.d.ts diff --git a/types/global.d.ts b/modules/app/types/global.d.ts similarity index 100% rename from types/global.d.ts rename to modules/app/types/global.d.ts diff --git a/types/spellStateDiff.d.ts b/modules/app/types/spellStateDiff.d.ts similarity index 100% rename from types/spellStateDiff.d.ts rename to modules/app/types/spellStateDiff.d.ts diff --git a/types/transaction.d.ts b/modules/app/types/transaction.d.ts similarity index 100% rename from types/transaction.d.ts rename to modules/app/types/transaction.d.ts diff --git a/types/voteProxyContract.d.ts b/modules/app/types/voteProxyContract.d.ts similarity index 100% rename from types/voteProxyContract.d.ts rename to modules/app/types/voteProxyContract.d.ts diff --git a/modules/delegates/components/DelegateCard.tsx b/modules/delegates/components/DelegateCard.tsx index 107839c0b..1fbae6bb2 100644 --- a/modules/delegates/components/DelegateCard.tsx +++ b/modules/delegates/components/DelegateCard.tsx @@ -7,7 +7,7 @@ import { useLockedMkr } from 'modules/mkr/hooks/useLockedMkr'; import { limitString } from 'lib/string'; import { ANALYTICS_PAGES } from 'modules/app/client/analytics/analytics.constants'; import { useAnalytics } from 'modules/app/client/analytics/useAnalytics'; -import useAccountsStore from 'stores/accounts'; +import useAccountsStore from 'modules/app/stores/accounts'; import { Delegate } from '../types'; import { DelegatePicture, DelegateModal, UndelegateModal } from 'modules/delegates/components'; import { diff --git a/modules/delegates/components/DelegateMKRDelegatedStats.tsx b/modules/delegates/components/DelegateMKRDelegatedStats.tsx index a693f193a..934510647 100644 --- a/modules/delegates/components/DelegateMKRDelegatedStats.tsx +++ b/modules/delegates/components/DelegateMKRDelegatedStats.tsx @@ -3,7 +3,7 @@ import { Flex } from 'theme-ui'; import { useMkrDelegated } from 'modules/mkr/hooks/useMkrDelegated'; import { Delegate } from 'modules/delegates/types'; import { StatBox } from 'modules/app/components/StatBox'; -import useAccountsStore from 'stores/accounts'; +import useAccountsStore from 'modules/app/stores/accounts'; export function DelegateMKRDelegatedStats({ delegate }: { delegate: Delegate }): React.ReactElement { const account = useAccountsStore(state => state.currentAccount); diff --git a/modules/delegates/components/DelegatesFilter.tsx b/modules/delegates/components/DelegatesFilter.tsx new file mode 100644 index 000000000..167848685 --- /dev/null +++ b/modules/delegates/components/DelegatesFilter.tsx @@ -0,0 +1,70 @@ +import { Flex, Checkbox, Label, Text, Box } from 'theme-ui'; +import shallow from 'zustand/shallow'; +import FilterButton from 'modules/app/components/FilterButton'; +import { Delegate } from '../types'; +import useDelegatesFiltersStore from '../stores/delegatesFiltersStore'; +import { DelegateStatusEnum } from '../delegates.constants'; +import { useMemo } from 'react'; +import { filterDelegates } from '../helpers/filterDelegates'; + +export default function DelegatesFilter({ delegates }: { delegates: Delegate[] }): JSX.Element { + const [showRecognized, showShadow, setShowRecognizedFilter, setShowShadowFilter] = useDelegatesFiltersStore( + state => [ + state.filters.showRecognized, + state.filters.showShadow, + state.setShowRecognizedFilter, + state.setShowShadowFilter + ], + shallow + ); + + const itemsSelected = [showRecognized, showShadow].filter(i => !!i).length; + const filteredDelegates = useMemo(() => { + return filterDelegates(delegates, showShadow, showRecognized); + }, [delegates, showRecognized, showShadow]); + + return ( + `Delegate Type ${itemsSelected > 0 ? `(${itemsSelected})` : ''}`} + listVariant="cards.noPadding" + > + + + + + + + + + + + {filteredDelegates.length} Results + + + + ); +} diff --git a/modules/delegates/components/DelegatesSort.tsx b/modules/delegates/components/DelegatesSort.tsx new file mode 100644 index 000000000..728518486 --- /dev/null +++ b/modules/delegates/components/DelegatesSort.tsx @@ -0,0 +1,24 @@ +import shallow from 'zustand/shallow'; +import useDelegatesFiltersStore, { delegatesSortEnum } from '../stores/delegatesFiltersStore'; +import { ListboxInput, ListboxButton, ListboxPopover, ListboxList, ListboxOption } from '@reach/listbox'; +import { Icon } from '@makerdao/dai-ui-icons'; + +export default function DelegatesSort(): JSX.Element { + const [sort, setSort] = useDelegatesFiltersStore(state => [state.sort, state.setSort], shallow); + + return ( + + } + /> + + + Sort by MKR delegated + Sort randomly + Sort by date + + + + ); +} diff --git a/modules/delegates/components/modals/DelegateModal.tsx b/modules/delegates/components/modals/DelegateModal.tsx index 491715f73..954d85b20 100644 --- a/modules/delegates/components/modals/DelegateModal.tsx +++ b/modules/delegates/components/modals/DelegateModal.tsx @@ -8,8 +8,8 @@ import getMaker, { MKR } from 'lib/maker'; import { fadeIn, slideUp } from 'lib/keyframes'; import { useTokenAllowance } from 'lib/hooks'; import { useMkrBalance } from 'modules/mkr/hooks/useMkrBalance'; -import useAccountsStore from 'stores/accounts'; -import useTransactionStore, { transactionsSelectors, transactionsApi } from 'stores/transactions'; +import useAccountsStore from 'modules/app/stores/accounts'; +import useTransactionStore, { transactionsSelectors, transactionsApi } from 'modules/app/stores/transactions'; import { Delegate } from '../../types'; import { BoxWithClose } from 'modules/app/components/BoxWithClose'; import { InputDelegateMkr } from './InputDelegateMkr'; diff --git a/modules/delegates/components/modals/InputDelegateMkr.tsx b/modules/delegates/components/modals/InputDelegateMkr.tsx index 84d22d079..8b1bbfd7f 100644 --- a/modules/delegates/components/modals/InputDelegateMkr.tsx +++ b/modules/delegates/components/modals/InputDelegateMkr.tsx @@ -3,7 +3,7 @@ import { Alert } from 'theme-ui'; import { MKRInput } from 'modules/mkr/components/MKRInput'; import BigNumber from 'bignumber.js'; import { useState } from 'react'; -import useAccountsStore from 'stores/accounts'; +import useAccountsStore from 'modules/app/stores/accounts'; import { useLockedMkr } from 'modules/mkr/hooks/useLockedMkr'; import Withdraw from 'modules/mkr/components/Withdraw'; diff --git a/modules/delegates/components/modals/UndelegateModal.tsx b/modules/delegates/components/modals/UndelegateModal.tsx index 4304ee7cc..cb23e539a 100644 --- a/modules/delegates/components/modals/UndelegateModal.tsx +++ b/modules/delegates/components/modals/UndelegateModal.tsx @@ -8,9 +8,9 @@ import getMaker, { MKR } from 'lib/maker'; import { fadeIn, slideUp } from 'lib/keyframes'; import { useTokenAllowance } from 'lib/hooks'; import { Delegate } from '../../types'; -import useAccountsStore from 'stores/accounts'; +import useAccountsStore from 'modules/app/stores/accounts'; import { useMkrDelegated } from 'modules/mkr/hooks/useMkrDelegated'; -import useTransactionStore, { transactionsSelectors, transactionsApi } from 'stores/transactions'; +import useTransactionStore, { transactionsSelectors, transactionsApi } from 'modules/app/stores/transactions'; import { BoxWithClose } from 'modules/app/components/BoxWithClose'; import { ApprovalContent, InputDelegateMkr, TxDisplay } from 'modules/delegates/components'; import { useAnalytics } from 'modules/app/client/analytics/useAnalytics'; diff --git a/modules/delegates/helpers/filterDelegates.ts b/modules/delegates/helpers/filterDelegates.ts new file mode 100644 index 000000000..cf2340f9c --- /dev/null +++ b/modules/delegates/helpers/filterDelegates.ts @@ -0,0 +1,25 @@ +import { DelegateStatusEnum } from '../delegates.constants'; +import { Delegate } from '../types'; + +export function filterDelegates( + delegates: Delegate[], + showShadow: boolean, + showRecognized: boolean +): Delegate[] { + return delegates.filter(delegate => { + // Return all if unchecked + if (!showShadow && !showRecognized) { + return true; + } + + if (!showShadow && delegate.status === DelegateStatusEnum.shadow) { + return false; + } + + if (!showRecognized && delegate.status === DelegateStatusEnum.recognized) { + return false; + } + // Apply all filters from the store + return true; + }); +} diff --git a/modules/delegates/stores/delegatesFiltersStore.ts b/modules/delegates/stores/delegatesFiltersStore.ts new file mode 100644 index 000000000..e50c5ed29 --- /dev/null +++ b/modules/delegates/stores/delegatesFiltersStore.ts @@ -0,0 +1,79 @@ +import create from 'zustand'; + +export enum delegatesSortEnum { + random = 'random', + mkrDelegated = 'mkrDelegated', + creationDate = 'creationDate' +} + +type StoreDelegates = { + filters: { + creationDate: null | Date; + showShadow: boolean; + showRecognized: boolean; + }; + sort: delegatesSortEnum; + setCreationDateFilter: (creationDate: Date | null) => void; + setShowShadowFilter: (showShadow: boolean) => void; + setShowRecognizedFilter: (showRecognized: boolean) => void; + setSort: (sort: delegatesSortEnum) => void; + resetFilters: () => void; +}; + +const [useDelegatesFiltersStore] = create((set, get) => ({ + filters: { + creationDate: null, + showShadow: false, + showRecognized: false + }, + sort: delegatesSortEnum.random, + + setSort: sort => { + set({ + sort + }); + }, + setCreationDateFilter: creationDate => { + set({ + filters: { + ...get().filters, + creationDate + } + }); + }, + setShowShadowFilter: showShadow => { + set({ + filters: { + ...get().filters, + showShadow + } + }); + }, + + setShowRecognizedFilter: showRecognized => { + set({ + filters: { + ...get().filters, + showRecognized + } + }); + }, + + resetFilters: () => { + set({ + filters: { + creationDate: null, + showShadow: false, + showRecognized: false + } + }); + }, + + resetSort: () => { + set({ + sort: delegatesSortEnum.random + }); + } +})); + +export default useDelegatesFiltersStore; diff --git a/types/voteDelegateContract.d.ts b/modules/delegates/types/voteDelegateContract.d.ts similarity index 100% rename from types/voteDelegateContract.d.ts rename to modules/delegates/types/voteDelegateContract.d.ts diff --git a/modules/emergency-shutdown/api/fetchESModuleStats.ts b/modules/emergency-shutdown/api/fetchESModuleStats.ts index ad9cc1bd0..ea6d8fc74 100644 --- a/modules/emergency-shutdown/api/fetchESModuleStats.ts +++ b/modules/emergency-shutdown/api/fetchESModuleStats.ts @@ -1,7 +1,7 @@ import getMaker from 'lib/maker'; import BigNumber from 'bignumber.js'; -import { CurrencyObject } from 'types/currency'; -import { StakingHistoryRow } from 'types/esmodule'; +import { CurrencyObject } from 'modules/app/types/currency'; +import { StakingHistoryRow } from 'modules/emergency-shutdown/types/esmodule'; export type ESModuleStats = [ CurrencyObject, diff --git a/modules/emergency-shutdown/components/BurnModal.tsx b/modules/emergency-shutdown/components/BurnModal.tsx index 7e406ed27..69637f78a 100644 --- a/modules/emergency-shutdown/components/BurnModal.tsx +++ b/modules/emergency-shutdown/components/BurnModal.tsx @@ -1,15 +1,15 @@ import { useState } from 'react'; import shallow from 'zustand/shallow'; import getMaker, { MKR } from 'lib/maker'; -import useTransactionStore, { transactionsApi, transactionsSelectors } from 'stores/transactions'; -import useAccountsStore from 'stores/accounts'; +import useTransactionStore, { transactionsApi, transactionsSelectors } from 'modules/app/stores/transactions'; +import useAccountsStore from 'modules/app/stores/accounts'; import DefaultScreen from './burnModal/Default'; import MKRAmount from './burnModal/MKRAmount'; import ConfirmBurn from './burnModal/ConfirmBurn'; import BurnSigning from './burnModal/BurnSigning'; import BurnTxSuccess from './burnModal/BurnTxSuccess'; import BurnFailed from './burnModal/BurnFailed'; -import { CurrencyObject } from 'types/currency'; +import { CurrencyObject } from 'modules/app/types/currency'; import { useMkrBalance } from 'modules/mkr/hooks/useMkrBalance'; import { TxInProgress } from 'modules/app/components/TxInProgress'; diff --git a/modules/emergency-shutdown/components/ESMHistory.tsx b/modules/emergency-shutdown/components/ESMHistory.tsx index cec328f11..26b6c877c 100644 --- a/modules/emergency-shutdown/components/ESMHistory.tsx +++ b/modules/emergency-shutdown/components/ESMHistory.tsx @@ -4,8 +4,8 @@ import { getNetwork } from 'lib/maker'; import { getEtherscanLink, formatRound } from 'lib/utils'; import { formatDateWithTime, formatDateWithoutTime } from 'lib/datetime'; import { cutMiddle } from 'lib/string'; -import { CurrencyObject } from 'types/currency'; -import { StakingHistoryRow } from 'types/esmodule'; +import { CurrencyObject } from 'modules/app/types/currency'; +import { StakingHistoryRow } from 'modules/emergency-shutdown/types/esmodule'; type Props = { stakingHistory: StakingHistoryRow[] | undefined; diff --git a/modules/emergency-shutdown/components/ProgressRing.tsx b/modules/emergency-shutdown/components/ProgressRing.tsx index 984c13a64..43219ca30 100644 --- a/modules/emergency-shutdown/components/ProgressRing.tsx +++ b/modules/emergency-shutdown/components/ProgressRing.tsx @@ -1,6 +1,6 @@ import { useRef } from 'react'; import { Box, Flex } from 'theme-ui'; -import { CurrencyObject } from 'types/currency'; +import { CurrencyObject } from 'modules/app/types/currency'; const ProgressRing = ({ progress, diff --git a/modules/emergency-shutdown/components/ShutdownModal.tsx b/modules/emergency-shutdown/components/ShutdownModal.tsx index fffeaa636..f64b8f545 100644 --- a/modules/emergency-shutdown/components/ShutdownModal.tsx +++ b/modules/emergency-shutdown/components/ShutdownModal.tsx @@ -3,10 +3,10 @@ import { useState } from 'react'; import shallow from 'zustand/shallow'; import getMaker, { getNetwork } from 'lib/maker'; import { Icon } from '@makerdao/dai-ui-icons'; -import useTransactionStore, { transactionsApi, transactionsSelectors } from 'stores/transactions'; +import useTransactionStore, { transactionsApi, transactionsSelectors } from 'modules/app/stores/transactions'; import { getEtherscanLink } from 'lib/utils'; -import { TXMined } from 'types/transaction'; -import { CurrencyObject } from 'types/currency'; +import { TXMined } from 'modules/app/types/transaction'; +import { CurrencyObject } from 'modules/app/types/currency'; const ModalContent = ({ setShowDialog, diff --git a/modules/emergency-shutdown/components/burnModal/BurnTxSuccess.tsx b/modules/emergency-shutdown/components/burnModal/BurnTxSuccess.tsx index 9fa7bf94b..c2a0bdd21 100644 --- a/modules/emergency-shutdown/components/burnModal/BurnTxSuccess.tsx +++ b/modules/emergency-shutdown/components/burnModal/BurnTxSuccess.tsx @@ -3,7 +3,7 @@ import { Icon } from '@makerdao/dai-ui-icons'; import { getNetwork } from 'lib/maker'; import { getEtherscanLink } from 'lib/utils'; -import { TXMined } from 'types/transaction'; +import { TXMined } from 'modules/app/types/transaction'; const BurnTxSuccess = ({ tx, close }) => ( diff --git a/modules/emergency-shutdown/components/burnModal/MKRAmount.tsx b/modules/emergency-shutdown/components/burnModal/MKRAmount.tsx index 912d4a63e..9cad7c5a2 100644 --- a/modules/emergency-shutdown/components/burnModal/MKRAmount.tsx +++ b/modules/emergency-shutdown/components/burnModal/MKRAmount.tsx @@ -3,7 +3,7 @@ import { useBreakpointIndex } from '@theme-ui/match-media'; import { MKR } from 'lib/maker'; import { MKRInput } from 'modules/mkr/components/MKRInput'; -import { CurrencyObject } from 'types/currency'; +import { CurrencyObject } from 'modules/app/types/currency'; type Props = { setBurnAmount: (burnAmount: CurrencyObject) => void; diff --git a/types/esmodule.d.ts b/modules/emergency-shutdown/types/esmodule.d.ts similarity index 100% rename from types/esmodule.d.ts rename to modules/emergency-shutdown/types/esmodule.d.ts diff --git a/modules/executive/components/CommentSortBy.tsx b/modules/executive/components/CommentSortBy.tsx index 9a08b35b4..7602ef105 100644 --- a/modules/executive/components/CommentSortBy.tsx +++ b/modules/executive/components/CommentSortBy.tsx @@ -1,7 +1,7 @@ import shallow from 'zustand/shallow'; import { MenuItem } from '@reach/menu-button'; -import useUiFiltersStore from 'stores/uiFilters'; +import useUiFiltersStore from 'modules/app/stores/uiFilters'; import FilterButton from 'modules/app/components/FilterButton'; export default function CommentSortBy(props): JSX.Element { diff --git a/modules/executive/components/Comments.tsx b/modules/executive/components/Comments.tsx index 68ddf80e7..bdc8ff4a0 100644 --- a/modules/executive/components/Comments.tsx +++ b/modules/executive/components/Comments.tsx @@ -4,12 +4,12 @@ import BigNumber from 'bignumber.js'; import Stack from 'modules/app/components/layout/layouts/Stack'; import CommentSortBy from './CommentSortBy'; -import { Comment } from 'types/comment'; +import { Comment } from 'modules/app/types/comment'; import { Proposal } from '../types'; import { getEtherscanLink, formatAddress } from 'lib/utils'; import { formatDateWithTime } from 'lib/datetime'; import { getNetwork } from 'lib/maker'; -import useUiFiltersStore from 'stores/uiFilters'; +import useUiFiltersStore from 'modules/app/stores/uiFilters'; export default function CommentsTab({ proposal, diff --git a/modules/executive/components/DateFilter.tsx b/modules/executive/components/DateFilter.tsx index 3009b5a17..fb96aa43c 100644 --- a/modules/executive/components/DateFilter.tsx +++ b/modules/executive/components/DateFilter.tsx @@ -3,7 +3,7 @@ import { Grid, Flex, Input, Text, Button } from 'theme-ui'; import shallow from 'zustand/shallow'; import FilterButton from 'modules/app/components/FilterButton'; -import useUiFiltersStore from 'stores/uiFilters'; +import useUiFiltersStore from 'modules/app/stores/uiFilters'; const displayDate = date => { try { diff --git a/modules/executive/components/ExecutiveOverviewCard.tsx b/modules/executive/components/ExecutiveOverviewCard.tsx index c360285b1..905dbd341 100644 --- a/modules/executive/components/ExecutiveOverviewCard.tsx +++ b/modules/executive/components/ExecutiveOverviewCard.tsx @@ -9,8 +9,8 @@ import { getNetwork } from 'lib/maker'; import { formatDateWithoutTime } from 'lib/datetime'; import { useVotedProposals } from 'modules/executive/hooks/useVotedProposals'; import { getStatusText } from 'modules/executive/helpers/getStatusText'; -import useAccountsStore from 'stores/accounts'; -import { ZERO_ADDRESS } from 'stores/accounts'; +import useAccountsStore from 'modules/app/stores/accounts'; +import { ZERO_ADDRESS } from 'modules/app/stores/accounts'; import { Proposal, SpellData } from 'modules/executive/types'; import Stack from 'modules/app/components/layout/layouts/Stack'; import VoteModal from './VoteModal'; diff --git a/modules/executive/components/OnChainFx.tsx b/modules/executive/components/OnChainFx.tsx index 930a11886..cce15fc6e 100644 --- a/modules/executive/components/OnChainFx.tsx +++ b/modules/executive/components/OnChainFx.tsx @@ -3,7 +3,7 @@ import { Grid, Text, Box, Link as ExternalLink } from 'theme-ui'; import BigNumber from 'bignumber.js'; import Stack from 'modules/app/components/layout/layouts/Stack'; -import { SpellStateDiff } from 'types/spellStateDiff'; +import { SpellStateDiff } from 'modules/app/types/spellStateDiff'; import { formatAddress } from 'lib/utils'; import { ethers } from 'ethers'; diff --git a/modules/executive/components/ProposalsSortBy.tsx b/modules/executive/components/ProposalsSortBy.tsx index 4237b87db..4f80f9cf0 100644 --- a/modules/executive/components/ProposalsSortBy.tsx +++ b/modules/executive/components/ProposalsSortBy.tsx @@ -1,7 +1,7 @@ import shallow from 'zustand/shallow'; import { MenuItem } from '@reach/menu-button'; -import useUiFiltersStore from 'stores/uiFilters'; +import useUiFiltersStore from 'modules/app/stores/uiFilters'; import FilterButton from 'modules/app/components/FilterButton'; export default function ProposalsSortBy(props): JSX.Element { diff --git a/modules/executive/components/VoteModal.tsx b/modules/executive/components/VoteModal.tsx index f0ae90189..4a608aa8a 100644 --- a/modules/executive/components/VoteModal.tsx +++ b/modules/executive/components/VoteModal.tsx @@ -25,8 +25,8 @@ import { fetchJson } from 'lib/fetchJson'; import { useLockedMkr } from 'modules/mkr/hooks/useLockedMkr'; import { useHat } from 'modules/executive/hooks/useHat'; import { useAllSlates } from 'modules/executive/hooks/useAllSlates'; -import useAccountsStore from 'stores/accounts'; -import useTransactionStore, { transactionsApi, transactionsSelectors } from 'stores/transactions'; +import useAccountsStore from 'modules/app/stores/accounts'; +import useTransactionStore, { transactionsApi, transactionsSelectors } from 'modules/app/stores/transactions'; import { Proposal, CMSProposal } from 'modules/executive/types'; import { useAnalytics } from 'modules/app/client/analytics/useAnalytics'; import { ANALYTICS_PAGES } from 'modules/app/client/analytics/analytics.constants'; diff --git a/modules/executive/components/WithdrawOldChief.tsx b/modules/executive/components/WithdrawOldChief.tsx index 0eb24cfae..9960beb82 100644 --- a/modules/executive/components/WithdrawOldChief.tsx +++ b/modules/executive/components/WithdrawOldChief.tsx @@ -6,11 +6,11 @@ import shallow from 'zustand/shallow'; import useSWR from 'swr'; import Stack from 'modules/app/components/layout/layouts/Stack'; import getMaker, { MKR, getNetwork } from 'lib/maker'; -import useAccountsStore from 'stores/accounts'; -import { CurrencyObject } from 'types/currency'; +import useAccountsStore from 'modules/app/stores/accounts'; +import { CurrencyObject } from 'modules/app/types/currency'; import { fadeIn, slideUp } from 'lib/keyframes'; import TxIndicators from 'modules/app/components/TxIndicators'; -import useTransactionStore, { transactionsSelectors, transactionsApi } from 'stores/transactions'; +import useTransactionStore, { transactionsSelectors, transactionsApi } from 'modules/app/stores/transactions'; import invariant from 'tiny-invariant'; import oldChiefAbi from 'lib/abis/oldChiefAbi.json'; import oldVoteProxyAbi from 'lib/abis/oldVoteProxyAbi.json'; diff --git a/modules/executive/helpers/getStatusText.ts b/modules/executive/helpers/getStatusText.ts index 68a285892..5f03fffa0 100644 --- a/modules/executive/helpers/getStatusText.ts +++ b/modules/executive/helpers/getStatusText.ts @@ -3,8 +3,8 @@ import { formatDateWithTime } from 'lib/datetime'; import { isBefore } from 'date-fns'; import { SPELL_SCHEDULED_DATE_OVERRIDES } from 'lib/constants'; import { SpellData } from '../types/spellData'; -import { ZERO_ADDRESS } from 'stores/accounts'; -import { CurrencyObject } from 'types/currency'; +import { ZERO_ADDRESS } from 'modules/app/stores/accounts'; +import { CurrencyObject } from 'modules/app/types/currency'; export const getStatusText = ({ proposalAddress, diff --git a/modules/executive/hooks/useMkrOnHat.ts b/modules/executive/hooks/useMkrOnHat.ts index 221befd5f..aa1c7fac6 100644 --- a/modules/executive/hooks/useMkrOnHat.ts +++ b/modules/executive/hooks/useMkrOnHat.ts @@ -1,6 +1,6 @@ import getMaker from 'lib/maker'; import useSWR from 'swr'; -import { CurrencyObject } from 'types/currency'; +import { CurrencyObject } from 'modules/app/types/currency'; type MkrOnHatResponse = { data?: CurrencyObject; diff --git a/modules/executive/hooks/useVotedProposals.ts b/modules/executive/hooks/useVotedProposals.ts index 585a0a569..a36505c61 100644 --- a/modules/executive/hooks/useVotedProposals.ts +++ b/modules/executive/hooks/useVotedProposals.ts @@ -1,7 +1,7 @@ import getMaker from 'lib/maker'; import { ZERO_SLATE_HASH } from 'modules/executive/helpers/zeroSlateHash'; import useSWR from 'swr'; -import useAccountsStore from 'stores/accounts'; +import useAccountsStore from 'modules/app/stores/accounts'; type VotedProposalsResponse = { data: any; diff --git a/modules/home/components/ExecutiveCard.tsx b/modules/home/components/ExecutiveCard.tsx index 5a6dc9d9c..6945052d8 100644 --- a/modules/home/components/ExecutiveCard.tsx +++ b/modules/home/components/ExecutiveCard.tsx @@ -2,10 +2,10 @@ import Link from 'next/link'; import useSWR from 'swr'; import { Button, Text, Flex, Badge, Box, Link as InternalLink } from 'theme-ui'; import Skeleton from 'modules/app/components/SkeletonThemed'; -import { ZERO_ADDRESS } from 'stores/accounts'; +import { ZERO_ADDRESS } from 'modules/app/stores/accounts'; import Stack from 'modules/app/components/layout/layouts/Stack'; import getMaker, { getNetwork } from 'lib/maker'; -import { CurrencyObject } from 'types/currency'; +import { CurrencyObject } from 'modules/app/types/currency'; import { CMSProposal } from 'modules/executive/types'; type Props = { diff --git a/modules/home/components/ExecutiveIndicator.tsx b/modules/home/components/ExecutiveIndicator.tsx index 3d545c604..3a603d88a 100644 --- a/modules/home/components/ExecutiveIndicator.tsx +++ b/modules/home/components/ExecutiveIndicator.tsx @@ -7,7 +7,7 @@ import Skeleton from 'modules/app/components/SkeletonThemed'; import { getNetwork } from 'lib/maker'; import { fetchJson } from 'lib/fetchJson'; import { useVotedProposals } from 'modules/executive/hooks/useVotedProposals'; -import useAccountsStore from 'stores/accounts'; +import useAccountsStore from 'modules/app/stores/accounts'; import { CMSProposal, SpellData } from 'modules/executive/types'; type Props = { diff --git a/modules/home/components/PollingIndicator.tsx b/modules/home/components/PollingIndicator.tsx index aade0344b..19c425e18 100644 --- a/modules/home/components/PollingIndicator.tsx +++ b/modules/home/components/PollingIndicator.tsx @@ -7,9 +7,9 @@ import invariant from 'tiny-invariant'; import { getNetwork } from 'lib/maker'; import { isActivePoll } from 'modules/polling/helpers/utils'; import { useAllUserVotes } from 'modules/polling/hooks/useAllUserVotes'; -import useAccountsStore from 'stores/accounts'; +import useAccountsStore from 'modules/app/stores/accounts'; import { Poll } from 'modules/polling/types'; -import { Account } from 'types/account'; +import { Account } from 'modules/app/types/account'; type Props = { account?: Account; diff --git a/modules/home/components/SystemStats.tsx b/modules/home/components/SystemStats.tsx index 0cdc0a162..b7f58dcd4 100644 --- a/modules/home/components/SystemStats.tsx +++ b/modules/home/components/SystemStats.tsx @@ -3,7 +3,7 @@ import { Icon } from '@makerdao/dai-ui-icons'; import useSWR, { mutate } from 'swr'; import Skeleton from 'modules/app/components/SkeletonThemed'; import getMaker, { DAI } from 'lib/maker'; -import { CurrencyObject } from 'types/currency'; +import { CurrencyObject } from 'modules/app/types/currency'; import BigNumber from 'bignumber.js'; async function getSystemStats(): Promise<[BigNumber, CurrencyObject, CurrencyObject, CurrencyObject]> { diff --git a/modules/mkr/components/Deposit.tsx b/modules/mkr/components/Deposit.tsx index aabbb3bca..95066b1fb 100644 --- a/modules/mkr/components/Deposit.tsx +++ b/modules/mkr/components/Deposit.tsx @@ -9,11 +9,11 @@ import { slideUp } from 'lib/keyframes'; import Stack from 'modules/app/components/layout/layouts/Stack'; import { MKRInput } from './MKRInput'; import getMaker, { MKR } from 'lib/maker'; -import useAccountsStore from 'stores/accounts'; -import { CurrencyObject } from 'types/currency'; +import useAccountsStore from 'modules/app/stores/accounts'; +import { CurrencyObject } from 'modules/app/types/currency'; import TxIndicators from 'modules/app/components/TxIndicators'; import { fadeIn } from 'lib/keyframes'; -import useTransactionStore, { transactionsSelectors, transactionsApi } from 'stores/transactions'; +import useTransactionStore, { transactionsSelectors, transactionsApi } from 'modules/app/stores/transactions'; import { BoxWithClose } from 'modules/app/components/BoxWithClose'; import invariant from 'tiny-invariant'; import { useMkrBalance } from 'modules/mkr/hooks/useMkrBalance'; diff --git a/modules/mkr/components/Withdraw.tsx b/modules/mkr/components/Withdraw.tsx index 6d7d5f8a0..a657e3d41 100644 --- a/modules/mkr/components/Withdraw.tsx +++ b/modules/mkr/components/Withdraw.tsx @@ -8,11 +8,11 @@ import useSWR from 'swr'; import Stack from 'modules/app/components/layout/layouts/Stack'; import { MKRInput } from './MKRInput'; import getMaker from 'lib/maker'; -import useAccountsStore from 'stores/accounts'; -import { CurrencyObject } from 'types/currency'; +import useAccountsStore from 'modules/app/stores/accounts'; +import { CurrencyObject } from 'modules/app/types/currency'; import { fadeIn, slideUp } from 'lib/keyframes'; import TxIndicators from 'modules/app/components/TxIndicators'; -import useTransactionStore, { transactionsSelectors, transactionsApi } from 'stores/transactions'; +import useTransactionStore, { transactionsSelectors, transactionsApi } from 'modules/app/stores/transactions'; import invariant from 'tiny-invariant'; import { BoxWithClose } from 'modules/app/components/BoxWithClose'; import { useLockedMkr } from 'modules/mkr/hooks/useLockedMkr'; diff --git a/modules/mkr/hooks/useLockedMkr.ts b/modules/mkr/hooks/useLockedMkr.ts index 068eea5fb..74d6e44dc 100644 --- a/modules/mkr/hooks/useLockedMkr.ts +++ b/modules/mkr/hooks/useLockedMkr.ts @@ -1,8 +1,8 @@ import useSWR from 'swr'; import getMaker from 'lib/maker'; -import { CurrencyObject } from 'types/currency'; -import { VoteDelegateContract } from 'types/voteDelegateContract'; -import { VoteProxyContract } from 'types/voteProxyContract'; +import { CurrencyObject } from 'modules/app/types/currency'; +import { VoteDelegateContract } from 'modules/delegates/types/voteDelegateContract'; +import { VoteProxyContract } from 'modules/app/types/voteProxyContract'; type LockedMkrData = { data: CurrencyObject; diff --git a/modules/mkr/hooks/useMkrBalance.ts b/modules/mkr/hooks/useMkrBalance.ts index 4aae81ea1..dfcf3debe 100644 --- a/modules/mkr/hooks/useMkrBalance.ts +++ b/modules/mkr/hooks/useMkrBalance.ts @@ -1,6 +1,6 @@ import getMaker, { MKR } from 'lib/maker'; import useSWR from 'swr'; -import { CurrencyObject } from 'types/currency'; +import { CurrencyObject } from 'modules/app/types/currency'; type MkrBalanceResponse = { data?: CurrencyObject; diff --git a/modules/mkr/hooks/useMkrDelegated.ts b/modules/mkr/hooks/useMkrDelegated.ts index 37f06e2b5..6ce75223c 100644 --- a/modules/mkr/hooks/useMkrDelegated.ts +++ b/modules/mkr/hooks/useMkrDelegated.ts @@ -1,6 +1,6 @@ import getMaker from 'lib/maker'; import useSWR from 'swr'; -import { CurrencyObject } from 'types/currency'; +import { CurrencyObject } from 'modules/app/types/currency'; type TokenAllowanceResponse = { data: CurrencyObject; diff --git a/modules/polling/components/BallotBox.tsx b/modules/polling/components/BallotBox.tsx index 4d86972c8..c7a21cdec 100644 --- a/modules/polling/components/BallotBox.tsx +++ b/modules/polling/components/BallotBox.tsx @@ -5,9 +5,9 @@ import shallow from 'zustand/shallow'; import { SupportedNetworks } from 'lib/constants'; import { getNetwork } from 'lib/maker'; import { Poll } from 'modules/polling/types'; -import { Ballot } from 'types/ballot'; -import useBallotStore from 'stores/ballot'; -import useTransactionStore, { transactionsSelectors } from 'stores/transactions'; +import { Ballot } from 'modules/polling/types/ballot'; +import useBallotStore from 'modules/polling/stores/ballotStore'; +import useTransactionStore, { transactionsSelectors } from 'modules/app/stores/transactions'; import { getEtherscanLink } from 'lib/utils'; import VotingWeight from './VotingWeight'; import PollBar from './PollBar'; diff --git a/modules/polling/components/BallotStatus.tsx b/modules/polling/components/BallotStatus.tsx index 933c2b35a..e749189a1 100644 --- a/modules/polling/components/BallotStatus.tsx +++ b/modules/polling/components/BallotStatus.tsx @@ -3,9 +3,9 @@ import { Text, Button } from 'theme-ui'; import { Icon } from '@makerdao/dai-ui-icons'; import shallow from 'zustand/shallow'; -import useTransactionStore, { transactionsSelectors } from 'stores/transactions'; -import { Transaction } from 'types/transaction'; -import useBallotStore from 'stores/ballot'; +import useTransactionStore, { transactionsSelectors } from 'modules/app/stores/transactions'; +import { Transaction } from 'modules/app/types/transaction'; +import useBallotStore from 'modules/polling/stores/ballotStore'; import { getNetwork } from 'lib/maker'; const BallotStatus = (props: any): JSX.Element => { diff --git a/modules/polling/components/CategoryFilter.tsx b/modules/polling/components/CategoryFilter.tsx index b1a1fadf1..c6cd4d4bf 100644 --- a/modules/polling/components/CategoryFilter.tsx +++ b/modules/polling/components/CategoryFilter.tsx @@ -2,7 +2,7 @@ import { Flex, Checkbox, Label, Text } from 'theme-ui'; import shallow from 'zustand/shallow'; import { PollCategory } from 'modules/polling/types'; import FilterButton from 'modules/app/components/FilterButton'; -import useUiFiltersStore from 'stores/uiFilters'; +import useUiFiltersStore from 'modules/app/stores/uiFilters'; export default function CategoryFilter({ categories, diff --git a/modules/polling/components/DateFilter.tsx b/modules/polling/components/DateFilter.tsx index 2fa465eed..373307b00 100644 --- a/modules/polling/components/DateFilter.tsx +++ b/modules/polling/components/DateFilter.tsx @@ -3,7 +3,7 @@ import { Grid, Flex, Input, Text, Button } from 'theme-ui'; import shallow from 'zustand/shallow'; import FilterButton from 'modules/app/components/FilterButton'; -import useUiFiltersStore from 'stores/uiFilters'; +import useUiFiltersStore from 'modules/app/stores/uiFilters'; const displayDate = date => { try { diff --git a/modules/polling/components/MobileVoteSheet.tsx b/modules/polling/components/MobileVoteSheet.tsx index 18b27d117..38d39325f 100644 --- a/modules/polling/components/MobileVoteSheet.tsx +++ b/modules/polling/components/MobileVoteSheet.tsx @@ -9,13 +9,13 @@ import isEqual from 'lodash/isEqual'; import shallow from 'zustand/shallow'; import lottie from 'lottie-web'; -import { Account } from 'types/account'; +import { Account } from 'modules/app/types/account'; import { Poll } from 'modules/polling/types'; -import useBallotStore from 'stores/ballot'; +import useBallotStore from 'modules/polling/stores/ballotStore'; import { isRankedChoicePoll, extractCurrentPollVote, isActivePoll } from 'modules/polling/helpers/utils'; import Stack from 'modules/app/components/layout/layouts/Stack'; import { useAllUserVotes } from 'modules/polling/hooks/useAllUserVotes'; -import useAccountsStore from 'stores/accounts'; +import useAccountsStore from 'modules/app/stores/accounts'; import RankedChoiceSelect from './RankedChoiceSelect'; import SingleSelect from './SingleSelect'; diff --git a/modules/polling/components/PollBar.tsx b/modules/polling/components/PollBar.tsx index ac23debe2..320680a76 100644 --- a/modules/polling/components/PollBar.tsx +++ b/modules/polling/components/PollBar.tsx @@ -2,10 +2,10 @@ import { Box, Text, Flex } from 'theme-ui'; import isEqual from 'lodash/isEqual'; import { Poll } from 'modules/polling/types'; -import { Ballot } from 'types/ballot'; +import { Ballot } from 'modules/polling/types/ballot'; import { isActivePoll, findPollById } from 'modules/polling/helpers/utils'; import { useAllUserVotes } from 'modules/polling/hooks/useAllUserVotes'; -import useAccountsStore from 'stores/accounts'; +import useAccountsStore from 'modules/app/stores/accounts'; type Props = { ballot: Ballot; polls: Poll[]; activePolls: Poll[] }; diff --git a/modules/polling/components/PollCategoryTag.tsx b/modules/polling/components/PollCategoryTag.tsx index 880551018..918d8eaff 100644 --- a/modules/polling/components/PollCategoryTag.tsx +++ b/modules/polling/components/PollCategoryTag.tsx @@ -1,5 +1,5 @@ import { Box, Text } from 'theme-ui'; -import useUiFiltersStore from 'stores/uiFilters'; +import useUiFiltersStore from 'modules/app/stores/uiFilters'; import shallow from 'zustand/shallow'; export function PollCategoryTag({ diff --git a/modules/polling/components/PollCreateModal.tsx b/modules/polling/components/PollCreateModal.tsx index 38d35dcfd..7857d4fc3 100644 --- a/modules/polling/components/PollCreateModal.tsx +++ b/modules/polling/components/PollCreateModal.tsx @@ -7,9 +7,9 @@ import { DialogOverlay, DialogContent } from '@reach/dialog'; import { fadeIn, slideUp } from 'lib/keyframes'; import getMaker, { getNetwork } from 'lib/maker'; -import useTransactionStore, { transactionsApi, transactionsSelectors } from 'stores/transactions'; +import useTransactionStore, { transactionsApi, transactionsSelectors } from 'modules/app/stores/transactions'; import { getEtherscanLink } from 'lib/utils'; -import { TXMined } from 'types/transaction'; +import { TXMined } from 'modules/app/types/transaction'; import { Poll } from 'modules/polling/types'; type Props = { diff --git a/modules/polling/components/PollOverviewCard.tsx b/modules/polling/components/PollOverviewCard.tsx index 8e1f8f5e7..2dead9e4a 100644 --- a/modules/polling/components/PollOverviewCard.tsx +++ b/modules/polling/components/PollOverviewCard.tsx @@ -10,8 +10,8 @@ import CountdownTimer from '../../app/components/CountdownTimer'; import VotingStatus from './PollVotingStatus'; import { Poll } from 'modules/polling/types'; import { useBreakpointIndex } from '@theme-ui/match-media'; -import useAccountsStore from 'stores/accounts'; -import useBallotStore from 'stores/ballot'; +import useAccountsStore from 'modules/app/stores/accounts'; +import useBallotStore from 'modules/polling/stores/ballotStore'; import QuickVote from './QuickVote'; import { useAnalytics } from 'modules/app/client/analytics/useAnalytics'; import { ANALYTICS_PAGES } from 'modules/app/client/analytics/analytics.constants'; diff --git a/modules/polling/components/PollVotingStatus.tsx b/modules/polling/components/PollVotingStatus.tsx index ec69cc335..851a3a029 100644 --- a/modules/polling/components/PollVotingStatus.tsx +++ b/modules/polling/components/PollVotingStatus.tsx @@ -4,9 +4,9 @@ import { Icon } from '@makerdao/dai-ui-icons'; import isNil from 'lodash/isNil'; import { isActivePoll } from 'modules/polling/helpers/utils'; import { useAllUserVotes } from 'modules/polling/hooks/useAllUserVotes'; -import useAccountsStore from 'stores/accounts'; -import useBallotStore from 'stores/ballot'; -import useTransactionStore, { transactionsSelectors } from 'stores/transactions'; +import useAccountsStore from 'modules/app/stores/accounts'; +import useBallotStore from 'modules/polling/stores/ballotStore'; +import useTransactionStore, { transactionsSelectors } from 'modules/app/stores/transactions'; import { Poll } from 'modules/polling/types'; const BadgeContents = ({ hasVoted, onBallot, poll, isMined, isPending, option, ...otherProps }) => { diff --git a/modules/polling/components/QuickVote.tsx b/modules/polling/components/QuickVote.tsx index 1334b9b96..811125741 100644 --- a/modules/polling/components/QuickVote.tsx +++ b/modules/polling/components/QuickVote.tsx @@ -10,9 +10,9 @@ import { Poll } from 'modules/polling/types'; import { isRankedChoicePoll, extractCurrentPollVote } from 'modules/polling/helpers/utils'; import { useAllUserVotes } from 'modules/polling/hooks/useAllUserVotes'; import Stack from 'modules/app/components/layout/layouts/Stack'; -import { Account } from 'types/account'; -import useAccountsStore from 'stores/accounts'; -import useBallotStore from 'stores/ballot'; +import { Account } from 'modules/app/types/account'; +import useAccountsStore from 'modules/app/stores/accounts'; +import useBallotStore from 'modules/polling/stores/ballotStore'; import RankedChoiceSelect from './RankedChoiceSelect'; import SingleSelect from './SingleSelect'; import ChoiceSummary from './ChoiceSummary'; diff --git a/modules/polling/components/VoteBox.tsx b/modules/polling/components/VoteBox.tsx index 9c3a407ff..526eff475 100644 --- a/modules/polling/components/VoteBox.tsx +++ b/modules/polling/components/VoteBox.tsx @@ -3,7 +3,7 @@ import { useBreakpointIndex } from '@theme-ui/match-media'; import VotingStatus from './PollVotingStatus'; import QuickVote from './QuickVote'; -import useAccountsStore from 'stores/accounts'; +import useAccountsStore from 'modules/app/stores/accounts'; import { isActivePoll } from 'modules/polling/helpers/utils'; import { Poll } from 'modules/polling/types'; diff --git a/modules/polling/components/VotingWeight.tsx b/modules/polling/components/VotingWeight.tsx index 783f7a851..4b3509570 100644 --- a/modules/polling/components/VotingWeight.tsx +++ b/modules/polling/components/VotingWeight.tsx @@ -2,7 +2,7 @@ import { Box, Flex, Text } from 'theme-ui'; import { Icon } from '@makerdao/dai-ui-icons'; import Tooltip from 'modules/app/components/Tooltip'; import useSWR from 'swr'; -import useAccountsStore from 'stores/accounts'; +import useAccountsStore from 'modules/app/stores/accounts'; import getMaker from 'lib/maker'; import { getVotingWeightCopy } from 'modules/polling/helpers/getVotingWeightCopy'; diff --git a/modules/polling/components/__tests__/QuickVote.spec.tsx b/modules/polling/components/__tests__/QuickVote.spec.tsx index d632849b6..8a5ad9a07 100644 --- a/modules/polling/components/__tests__/QuickVote.spec.tsx +++ b/modules/polling/components/__tests__/QuickVote.spec.tsx @@ -10,7 +10,7 @@ import { } from '../../../../__tests__/helpers'; import { Poll, PollCategory } from 'modules/polling/types'; import getMaker from 'lib/maker'; -import { accountsApi } from 'stores/accounts'; +import { accountsApi } from 'modules/app/stores/accounts'; import PollingOverviewPage from '../../../../pages/polling'; let maker; diff --git a/modules/polling/components/__tests__/RankedChoiceSelect.spec.tsx b/modules/polling/components/__tests__/RankedChoiceSelect.spec.tsx index d8b9bd1f0..ccbba2493 100644 --- a/modules/polling/components/__tests__/RankedChoiceSelect.spec.tsx +++ b/modules/polling/components/__tests__/RankedChoiceSelect.spec.tsx @@ -10,7 +10,7 @@ import { import { Poll, PollCategory } from 'modules/polling/types'; import getMaker from 'lib/maker'; -import { accountsApi } from 'stores/accounts'; +import { accountsApi } from 'modules/app/stores/accounts'; import PollingOverviewPage from '../../../../pages/polling'; let maker; diff --git a/modules/polling/components/__tests__/SingleSelect.spec.tsx b/modules/polling/components/__tests__/SingleSelect.spec.tsx index 726f4db5f..da92088d5 100644 --- a/modules/polling/components/__tests__/SingleSelect.spec.tsx +++ b/modules/polling/components/__tests__/SingleSelect.spec.tsx @@ -8,7 +8,7 @@ import { renderWithAccountSelect as render } from '../../../../__tests__/helpers'; import { Poll, PollCategory } from 'modules/polling/types'; -import { accountsApi } from 'stores/accounts'; +import { accountsApi } from 'modules/app/stores/accounts'; import getMaker from 'lib/maker'; import PollingOverviewPage from '../../../../pages/polling'; diff --git a/modules/polling/components/__tests__/polling.spec.tsx b/modules/polling/components/__tests__/polling.spec.tsx index 2f0347027..57e076943 100644 --- a/modules/polling/components/__tests__/polling.spec.tsx +++ b/modules/polling/components/__tests__/polling.spec.tsx @@ -10,7 +10,7 @@ import PollingOverviewPage from '../../../../pages/polling'; import getMaker from '../../../../lib/maker'; import mockPolls from 'modules/polling/api/mocks/polls.json'; import mockCategories from '../__mocks__/categories.json'; -import { accountsApi } from '../../../../stores/accounts'; +import { accountsApi } from '../../../app/stores/accounts'; import { Poll, PollCategory } from 'modules/polling/types'; let maker; diff --git a/modules/polling/components/review/ReviewBox.tsx b/modules/polling/components/review/ReviewBox.tsx index 29e1cfc06..aa0c37d11 100644 --- a/modules/polling/components/review/ReviewBox.tsx +++ b/modules/polling/components/review/ReviewBox.tsx @@ -8,9 +8,9 @@ import { useBreakpointIndex } from '@theme-ui/match-media'; import { getEtherscanLink } from 'lib/utils'; import { getNetwork } from 'lib/maker'; import { Poll } from 'modules/polling/types'; -import { TXMined } from 'types/transaction'; -import useBallotStore from 'stores/ballot'; -import useTransactionStore, { transactionsSelectors } from 'stores/transactions'; +import { TXMined } from 'modules/app/types/transaction'; +import useBallotStore from 'modules/polling/stores/ballotStore'; +import useTransactionStore, { transactionsSelectors } from 'modules/app/stores/transactions'; import VotingWeight from '../VotingWeight'; import TxIndicators from 'modules/app/components/TxIndicators'; import PollBar from '../PollBar'; diff --git a/stores/ballot.ts b/modules/polling/stores/ballotStore.ts similarity index 91% rename from stores/ballot.ts rename to modules/polling/stores/ballotStore.ts index 67576cb3c..8905447d0 100644 --- a/stores/ballot.ts +++ b/modules/polling/stores/ballotStore.ts @@ -2,10 +2,9 @@ import create from 'zustand'; import isNil from 'lodash/isNil'; import omit from 'lodash/omit'; import getMaker from 'lib/maker'; -import { Ballot } from 'types/ballot'; -import { transactionsApi } from './transactions'; -import { accountsApi } from './accounts'; -import { poll } from 'ethers/lib/utils'; +import { Ballot } from '../types/ballot'; +import { transactionsApi } from 'modules/app/stores/transactions'; +import { accountsApi } from 'modules/app/stores/accounts'; type Store = { ballot: Ballot; diff --git a/types/ballot.d.ts b/modules/polling/types/ballot.d.ts similarity index 100% rename from types/ballot.d.ts rename to modules/polling/types/ballot.d.ts diff --git a/modules/web3/hooks/useAccountChange.tsx b/modules/web3/hooks/useAccountChange.tsx index a380b1606..ae07a8b88 100644 --- a/modules/web3/hooks/useAccountChange.tsx +++ b/modules/web3/hooks/useAccountChange.tsx @@ -1,5 +1,5 @@ import getMaker from 'lib/maker'; -import { accountsApi } from 'stores/accounts'; +import { accountsApi } from 'modules/app/stores/accounts'; export async function useAccountChange() { // if we are on the browser start listening for account changes as soon as possible diff --git a/package.json b/package.json index e103d8b1e..76ee3eb83 100644 --- a/package.json +++ b/package.json @@ -12,8 +12,8 @@ "test": "yarn testchain --ci jest --config=jest.config.js --runInBand --watch", "test:ci": "yarn testchain --ci jest --runInBand --ci --coverage", "testchain": "node_modules/@makerdao/testchain/scripts/launch -s default --fast", - "prettier": "prettier --config .prettierrc --write pages/ modules/ lib/ stores/ types/", - "lint": "eslint --ext .tsx,.ts pages/ modules/ lib/ stores/ types/" + "prettier": "prettier --config .prettierrc --write pages/ modules/ lib/", + "lint": "eslint --ext .tsx,.ts pages/ modules/ lib/" }, "dependencies": { "@makerdao/dai": "^0.42.2", diff --git a/pages/account.tsx b/pages/account.tsx index 6d147918c..06e848111 100644 --- a/pages/account.tsx +++ b/pages/account.tsx @@ -18,8 +18,8 @@ import getMaker from 'lib/maker'; import { fadeIn, slideUp } from 'lib/keyframes'; import { getEtherscanLink } from 'lib/utils'; import { getNetwork } from 'lib/maker'; -import useAccountsStore from 'stores/accounts'; -import useTransactionStore, { transactionsSelectors, transactionsApi } from 'stores/transactions'; +import useAccountsStore from 'modules/app/stores/accounts'; +import useTransactionStore, { transactionsSelectors, transactionsApi } from 'modules/app/stores/transactions'; import { cutMiddle } from 'lib/string'; import { useLockedMkr } from 'modules/mkr/hooks/useLockedMkr'; import { useAnalytics } from 'modules/app/client/analytics/useAnalytics'; diff --git a/pages/delegates/index.tsx b/pages/delegates/index.tsx index bca57b3a7..7e47799e4 100644 --- a/pages/delegates/index.tsx +++ b/pages/delegates/index.tsx @@ -1,5 +1,5 @@ -import { useState, useEffect } from 'react'; -import { Heading, Box, Card, Text, Link as ThemeUILInk } from 'theme-ui'; +import { useState, useEffect, useMemo } from 'react'; +import { Heading, Box, Flex, Card, Text, Link as ThemeUILInk, Button } from 'theme-ui'; import { GetStaticProps } from 'next'; import ErrorPage from 'next/error'; import { isDefaultNetwork } from 'lib/maker'; @@ -17,10 +17,15 @@ import { getNetwork } from 'lib/maker'; import { fetchJson } from 'lib/fetchJson'; import { useAnalytics } from 'modules/app/client/analytics/useAnalytics'; import { ANALYTICS_PAGES } from 'modules/app/client/analytics/analytics.constants'; -import useAccountsStore from 'stores/accounts'; +import useAccountsStore from 'modules/app/stores/accounts'; import Link from 'next/link'; import { DelegatesSystemInfo } from 'modules/delegates/components/DelegatesSystemInfo'; import { HeadComponent } from 'modules/app/components/layout/Head'; +import useDelegatesFiltersStore, { delegatesSortEnum } from 'modules/delegates/stores/delegatesFiltersStore'; +import shallow from 'zustand/shallow'; +import DelegatesFilter from 'modules/delegates/components/DelegatesFilter'; +import DelegatesSort from 'modules/delegates/components/DelegatesSort'; +import { filterDelegates } from 'modules/delegates/helpers/filterDelegates'; type Props = { delegates: Delegate[]; @@ -31,24 +36,47 @@ const Delegates = ({ delegates, stats }: Props) => { const network = getNetwork(); const { trackButtonClick } = useAnalytics(ANALYTICS_PAGES.DELEGATES); + const [showRecognized, showShadow, sort, resetFilters] = useDelegatesFiltersStore( + state => [state.filters.showRecognized, state.filters.showShadow, state.sort, state.resetFilters], + shallow + ); + + const filteredDelegates = useMemo(() => { + return filterDelegates(delegates, showShadow, showRecognized); + }, [delegates, showRecognized, showShadow]); + + const sortedDelegates = useMemo(() => { + return filteredDelegates.sort((prev, next) => { + if (sort === delegatesSortEnum.creationDate) { + return prev.expirationDate > next.expirationDate ? -1 : 1; + } else if (sort === delegatesSortEnum.mkrDelegated) { + return prev.mkrDelegated > next.mkrDelegated ? -1 : 1; + } else if (sort === delegatesSortEnum.random) { + return delegates.indexOf(prev) > delegates.indexOf(next) ? 1 : -1; + } + + return 1; + }); + }, [filteredDelegates, sort]); const styles = { delegateGroup: { marginBottom: 2 } }; + const [voteDelegate] = useAccountsStore(state => [state.voteDelegate]); const isOwner = d => d.voteDelegateAddress.toLowerCase() === voteDelegate?.getVoteDelegateAddress().toLowerCase(); - const expiredDelegates = delegates.filter(delegate => delegate.expired === true); + const expiredDelegates = sortedDelegates.filter(delegate => delegate.expired === true); - const recognizedDelegates = delegates + const recognizedDelegates = sortedDelegates .filter(delegate => delegate.status === DelegateStatusEnum.recognized && !delegate.expired) .sort(d => (isOwner(d) ? -1 : 0)); - const shadowDelegates = delegates + const shadowDelegates = sortedDelegates .filter(delegate => delegate.status === DelegateStatusEnum.shadow && !delegate.expired) .sort(d => (isOwner(d) ? -1 : 0)); @@ -60,9 +88,26 @@ const Delegates = ({ delegates, stats }: Props) => { image={'https://vote.makerdao.com/seo/delegates.png'} /> + + + + Filters + + + + + + + + + + + - {delegates && delegates.length === 0 && No delegates found} + {sortedDelegates && sortedDelegates.length === 0 && No delegates found} {recognizedDelegates.length > 0 && ( @@ -156,7 +201,7 @@ export default function DelegatesPage({ delegates, stats }: Props): JSX.Element if (!isDefaultNetwork()) { fetchJson(`/api/delegates?network=${getNetwork()}`) .then((response: DelegatesAPIResponse) => { - _setDelegates(response.delegates); + _setDelegates(shuffleArray(response.delegates)); _setStats(response.stats); }) .catch(setError); diff --git a/pages/esmodule.tsx b/pages/esmodule.tsx index bf68e0af5..cf2740e49 100644 --- a/pages/esmodule.tsx +++ b/pages/esmodule.tsx @@ -7,7 +7,7 @@ import BurnModal from 'modules/emergency-shutdown/components/BurnModal'; import ShutdownModal from 'modules/emergency-shutdown/components/ShutdownModal'; import ProgressRing from 'modules/emergency-shutdown/components/ProgressRing'; import ESMHistory from 'modules/emergency-shutdown/components/ESMHistory'; -import useAccountsStore from 'stores/accounts'; +import useAccountsStore from 'modules/app/stores/accounts'; import { formatDateWithTime } from 'lib/datetime'; import { useESModuleStats } from 'modules/emergency-shutdown/hooks/useESModuleStats'; import { HeadComponent } from 'modules/app/components/layout/Head'; diff --git a/pages/executive.tsx b/pages/executive.tsx index cb13f7a9c..eb39c21ac 100644 --- a/pages/executive.tsx +++ b/pages/executive.tsx @@ -34,8 +34,8 @@ import PageLoadingPlaceholder from 'modules/app/components/PageLoadingPlaceholde import { ExecutiveBalance } from 'modules/executive/components/ExecutiveBalance'; // stores -import useAccountsStore from 'stores/accounts'; -import useUiFiltersStore from 'stores/uiFilters'; +import useAccountsStore from 'modules/app/stores/accounts'; +import useUiFiltersStore from 'modules/app/stores/uiFilters'; // types import { Proposal, CMSProposal, SpellData } from 'modules/executive/types'; diff --git a/pages/executive/[proposal-id].tsx b/pages/executive/[proposal-id].tsx index cb87b972d..b9e2f0bc8 100644 --- a/pages/executive/[proposal-id].tsx +++ b/pages/executive/[proposal-id].tsx @@ -34,8 +34,8 @@ import { ANALYTICS_PAGES } from 'modules/app/client/analytics/analytics.constant import { getEtherscanLink } from 'lib/utils'; // stores -import useAccountsStore from 'stores/accounts'; -import { ZERO_ADDRESS } from 'stores/accounts'; +import useAccountsStore from 'modules/app/stores/accounts'; +import { ZERO_ADDRESS } from 'modules/app/stores/accounts'; //components import Comments from 'modules/executive/components/Comments'; @@ -51,7 +51,7 @@ import { SpellEffectsTab } from 'modules/executive/components/SpellEffectsTab'; //types import { CMSProposal, Proposal, SpellData } from 'modules/executive/types'; import { HeadComponent } from 'modules/app/components/layout/Head'; -import { CurrencyObject } from 'types/currency'; +import { CurrencyObject } from 'modules/app/types/currency'; import { Address } from 'modules/address/components/Address'; type Props = { diff --git a/pages/polling.tsx b/pages/polling.tsx index 1686677f5..9d640f9ef 100644 --- a/pages/polling.tsx +++ b/pages/polling.tsx @@ -14,7 +14,6 @@ import { isDefaultNetwork, getNetwork } from 'lib/maker'; import { formatDateWithTime } from 'lib/datetime'; import { fetchJson } from 'lib/fetchJson'; import { isActivePoll } from 'modules/polling/helpers/utils'; -import { getCategories } from 'modules/polling/helpers/getCategories'; import PrimaryLayout from 'modules/app/components/layout/layouts/Primary'; import SidebarLayout from 'modules/app/components/layout/layouts/Sidebar'; import Stack from 'modules/app/components/layout/layouts/Stack'; @@ -24,9 +23,9 @@ import CategoryFilter from 'modules/polling/components/CategoryFilter'; import BallotBox from 'modules/polling/components/BallotBox'; import ResourceBox from 'modules/app/components/ResourceBox'; import SystemStatsSidebar from 'modules/app/components/SystemStatsSidebar'; -import useBallotStore from 'stores/ballot'; -import useAccountsStore from 'stores/accounts'; -import useUiFiltersStore from 'stores/uiFilters'; +import useBallotStore from 'modules/polling/stores/ballotStore'; +import useAccountsStore from 'modules/app/stores/accounts'; +import useUiFiltersStore from 'modules/app/stores/uiFilters'; import MobileVoteSheet from 'modules/polling/components/MobileVoteSheet'; import BallotStatus from 'modules/polling/components/BallotStatus'; import PageLoadingPlaceholder from 'modules/app/components/PageLoadingPlaceholder'; diff --git a/pages/polling/[poll-hash].tsx b/pages/polling/[poll-hash].tsx index fff5a4aab..b4c4302ed 100644 --- a/pages/polling/[poll-hash].tsx +++ b/pages/polling/[poll-hash].tsx @@ -20,8 +20,8 @@ import { getPolls, getPoll } from 'modules/polling/api/fetchPolls'; import { Poll, PollTally } from 'modules/polling/types'; // stores -import useAccountsStore from 'stores/accounts'; -import useBallotStore from 'stores/ballot'; +import useAccountsStore from 'modules/app/stores/accounts'; +import useBallotStore from 'modules/polling/stores/ballotStore'; // components import Skeleton from 'modules/app/components/SkeletonThemed'; diff --git a/pages/polling/create.tsx b/pages/polling/create.tsx index 583e0fb5b..96e29c229 100644 --- a/pages/polling/create.tsx +++ b/pages/polling/create.tsx @@ -15,7 +15,7 @@ import ResourceBox from 'modules/app/components/ResourceBox'; import { validateUrl } from 'modules/polling/helpers/validator'; import { Poll } from 'modules/polling/types'; import Hash from 'ipfs-only-hash'; -import useAccountsStore from 'stores/accounts'; +import useAccountsStore from 'modules/app/stores/accounts'; import { formatDateWithTime } from 'lib/datetime'; import { markdownToHtml } from 'lib/utils'; import { HeadComponent } from 'modules/app/components/layout/Head'; diff --git a/pages/polling/review.tsx b/pages/polling/review.tsx index 96244edcd..be722ca1c 100644 --- a/pages/polling/review.tsx +++ b/pages/polling/review.tsx @@ -17,8 +17,8 @@ import Stack from 'modules/app/components/layout/layouts/Stack'; import PollOverviewCard from 'modules/polling/components/PollOverviewCard'; import { Poll } from 'modules/polling/types'; import ReviewBox from 'modules/polling/components/review/ReviewBox'; -import useBallotStore from 'stores/ballot'; -import useAccountsStore from 'stores/accounts'; +import useBallotStore from 'modules/polling/stores/ballotStore'; +import useAccountsStore from 'modules/app/stores/accounts'; import MobileVoteSheet from 'modules/polling/components/MobileVoteSheet'; import PageLoadingPlaceholder from 'modules/app/components/PageLoadingPlaceholder'; import { useAnalytics } from 'modules/app/client/analytics/useAnalytics'; diff --git a/tsconfig.json b/tsconfig.json index 2d14964a1..a3ac39368 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -24,7 +24,6 @@ ], "baseUrl": "./", "typeRoots": [ - "./types", "./node_modules/@types" ], "incremental": true From a79d3e061fddeff21eff1517201835d06b25621b Mon Sep 17 00:00:00 2001 From: Rafael Ventura Date: Thu, 2 Dec 2021 12:33:12 +0100 Subject: [PATCH 15/36] Feature/sc 702/implement improved polling filter (#252) * implement active and ended filters * lint * fix active polls tests * change default behaviour of polling filters * build * Count on filter Co-authored-by: Phil Bain --- modules/app/stores/uiFilters.ts | 20 ++- .../delegates/components/ManageDelegation.tsx | 2 +- modules/mkr/hooks/useTotalMkr.ts | 2 +- modules/polling/components/CategoryFilter.tsx | 124 ++++++++++++++---- .../components/__tests__/QuickVote.spec.tsx | 5 +- .../components/__tests__/polling.spec.tsx | 7 +- modules/polling/helpers/filterPolls.ts | 39 ++++++ pages/polling.tsx | 64 +++++---- 8 files changed, 204 insertions(+), 59 deletions(-) create mode 100644 modules/polling/helpers/filterPolls.ts diff --git a/modules/app/stores/uiFilters.ts b/modules/app/stores/uiFilters.ts index 7103544de..443538b11 100644 --- a/modules/app/stores/uiFilters.ts +++ b/modules/app/stores/uiFilters.ts @@ -6,6 +6,8 @@ type Store = { endDate: null | Date; categoryFilter: null | { [category: string]: boolean }; showHistorical: boolean; + showPollActive: boolean; + showPollEnded: boolean; }; executiveFilters: { startDate: null | Date; @@ -15,6 +17,8 @@ type Store = { setEndDate: (type: 'poll' | 'executive', endDate: Date | null) => void; setCategoryFilter: (categoryFilter: { [category: string]: boolean }) => void; setShowHistorical: (showHistorical: boolean) => void; + setShowPollActive: (showActive: boolean) => void; + setShowPollEnded: (ended: boolean) => void; resetPollFilters: () => void; executiveSortBy: 'Date Posted' | 'MKR Amount'; setExecutiveSortBy: (method: 'Date Posted' | 'MKR Amount') => void; @@ -27,7 +31,9 @@ const [useUiFiltersStore] = create((set, get) => ({ startDate: null, endDate: null, categoryFilter: null, - showHistorical: false + showHistorical: false, + showPollActive: false, + showPollEnded: false }, executiveFilters: { @@ -49,6 +55,14 @@ const [useUiFiltersStore] = create((set, get) => ({ set({ pollFilters: { ...get().pollFilters, categoryFilter } }); }, + setShowPollActive: (active: boolean) => { + set({ pollFilters: { ...get().pollFilters, showPollActive: active } }); + }, + + setShowPollEnded: (ended: boolean) => { + set({ pollFilters: { ...get().pollFilters, showPollEnded: ended } }); + }, + setShowHistorical: showHistorical => { set({ pollFilters: { ...get().pollFilters, showHistorical } }); }, @@ -59,7 +73,9 @@ const [useUiFiltersStore] = create((set, get) => ({ startDate: null, endDate: null, categoryFilter: null, - showHistorical: false + showHistorical: false, + showPollActive: false, + showPollEnded: false } }); }, diff --git a/modules/delegates/components/ManageDelegation.tsx b/modules/delegates/components/ManageDelegation.tsx index 7af9deee9..8c65130a0 100644 --- a/modules/delegates/components/ManageDelegation.tsx +++ b/modules/delegates/components/ManageDelegation.tsx @@ -1,7 +1,7 @@ import { Card, Box, Button, Heading } from 'theme-ui'; import React, { useState } from 'react'; import { Delegate } from '../types'; -import useAccountsStore from 'stores/accounts'; +import useAccountsStore from 'modules/app/stores/accounts'; import { ANALYTICS_PAGES } from 'modules/app/client/analytics/analytics.constants'; import { useAnalytics } from 'modules/app/client/analytics/useAnalytics'; import { DelegateModal } from './modals/DelegateModal'; diff --git a/modules/mkr/hooks/useTotalMkr.ts b/modules/mkr/hooks/useTotalMkr.ts index 8eca27bdc..c8f179933 100644 --- a/modules/mkr/hooks/useTotalMkr.ts +++ b/modules/mkr/hooks/useTotalMkr.ts @@ -2,7 +2,7 @@ import getMaker, { getNetwork } from 'lib/maker'; import useSWR from 'swr'; -import { CurrencyObject } from 'types/currency'; +import { CurrencyObject } from 'modules/app/types/currency'; export const useTotalMKR = (): { data: CurrencyObject; diff --git a/modules/polling/components/CategoryFilter.tsx b/modules/polling/components/CategoryFilter.tsx index c6cd4d4bf..5e9730dd8 100644 --- a/modules/polling/components/CategoryFilter.tsx +++ b/modules/polling/components/CategoryFilter.tsx @@ -1,46 +1,122 @@ -import { Flex, Checkbox, Label, Text } from 'theme-ui'; +import { Flex, Box, Checkbox, Label, Text, Divider } from 'theme-ui'; import shallow from 'zustand/shallow'; -import { PollCategory } from 'modules/polling/types'; +import { Poll, PollCategory } from 'modules/polling/types'; import FilterButton from 'modules/app/components/FilterButton'; import useUiFiltersStore from 'modules/app/stores/uiFilters'; +import { isActivePoll } from 'modules/polling/helpers/utils'; +import { useMemo } from 'react'; +import { filterPolls } from '../helpers/filterPolls'; export default function CategoryFilter({ categories, + polls, ...props }: { categories: PollCategory[]; + polls: Poll[]; }): JSX.Element { - const [categoryFilter, setCategoryFilter] = useUiFiltersStore( - state => [state.pollFilters.categoryFilter, state.setCategoryFilter], + const [ + startDate, + endDate, + categoryFilter, + setCategoryFilter, + showPollActive, + showPollEnded, + setShowPollActive, + setShowPollEnded + ] = useUiFiltersStore( + state => [ + state.pollFilters.startDate, + state.pollFilters.endDate, + state.pollFilters.categoryFilter, + state.setCategoryFilter, + state.pollFilters.showPollActive, + state.pollFilters.showPollEnded, + state.setShowPollActive, + state.setShowPollEnded + ], shallow ); const itemsSelected = Object.values(categoryFilter || {}).filter(i => !!i); + const filteredPollsOnlyCategories = useMemo(() => { + return filterPolls(polls, startDate, endDate, categoryFilter, true, true); + }, [polls, startDate, endDate, categoryFilter]); + const filteredPollsNoCategories = useMemo(() => { + return filterPolls(polls, startDate, endDate, null, showPollActive, showPollEnded); + }, [polls, startDate, endDate, showPollActive, showPollEnded]); + + const filteredPolls = useMemo(() => { + return filterPolls(polls, startDate, endDate, categoryFilter, showPollActive, showPollEnded); + }, [polls, startDate, endDate, categoryFilter, showPollActive, showPollEnded]); + + const filtersSelected = itemsSelected.length + (showPollActive ? 1 : 0) + (showPollEnded ? 1 : 0); return ( `Poll Type ${itemsSelected.length > 0 ? `(${itemsSelected.length})` : ''}`} + name={() => `Poll Type ${filtersSelected > 0 ? `(${filtersSelected})` : ''}`} + listVariant="cards.noPadding" {...props} > - - {categories.map(category => ( - - - - ))} - + + + + + + + + + + {categories.map(category => ( + + + + ))} + + + + + {filteredPolls.length} Results + + ); } diff --git a/modules/polling/components/__tests__/QuickVote.spec.tsx b/modules/polling/components/__tests__/QuickVote.spec.tsx index 8a5ad9a07..0ed8c3a3c 100644 --- a/modules/polling/components/__tests__/QuickVote.spec.tsx +++ b/modules/polling/components/__tests__/QuickVote.spec.tsx @@ -42,7 +42,10 @@ describe('QuickVote', () => { expect((await screen.findAllByText('View Details')).length).toBe(2); expect((await screen.findAllByText('Add vote to ballot')).length).toBe(2); expect((await screen.findAllByTestId('countdown timer')).length).toBe(2); - screen.getByText('Active Polls'); + + // Find a heading and checkbox with the text. + const activePollsText = screen.getAllByText('Active Polls'); + expect(activePollsText.length).toBe(2); screen.getByText(/MIP14:/i); }); }); diff --git a/modules/polling/components/__tests__/polling.spec.tsx b/modules/polling/components/__tests__/polling.spec.tsx index 57e076943..f15d6d160 100644 --- a/modules/polling/components/__tests__/polling.spec.tsx +++ b/modules/polling/components/__tests__/polling.spec.tsx @@ -39,7 +39,12 @@ describe('Polling', () => { describe('renders expected voting options for each poll type', () => { test('allows users to vote when account is connected', async () => { - expect(await screen.findByText('Active Polls', {}, { timeout: 15000 })).toBeInTheDocument(); + // Find a heading and checkbox with the text. + const activePollsText = await screen.findAllByText('Active Polls', {}, { timeout: 15000 }); + + expect(activePollsText.length).toBe(2); + expect(activePollsText[0]).toBeInTheDocument(); + expect(activePollsText[1]).toBeInTheDocument(); expect(await screen.findByText('Your Ballot', {}, { timeout: 15000 })).toBeInTheDocument(); await screen.findByText(formatAddress(DEMO_ACCOUNT_TESTS)); }); diff --git a/modules/polling/helpers/filterPolls.ts b/modules/polling/helpers/filterPolls.ts new file mode 100644 index 000000000..86e39d48c --- /dev/null +++ b/modules/polling/helpers/filterPolls.ts @@ -0,0 +1,39 @@ +import { Poll } from '../types'; +import { isActivePoll } from './utils'; + +// Functions for filtering polls based on the frontend main polling page needs +export function filterPolls( + polls: Poll[], + start, + end, + categoryFilter, + showPollActive: boolean, + showPollEnded: boolean +): Poll[] { + const startDate = start && new Date(start); + const endDate = end && new Date(end); + + const noCategoriesSelected = categoryFilter === null || Object.values(categoryFilter).every(c => !c); + + return polls + .filter(poll => { + // check date filters first + if (startDate && new Date(poll.endDate).getTime() < startDate.getTime()) return false; + if (endDate && new Date(poll.endDate).getTime() > endDate.getTime()) return false; + + // if no category filters selected, return all, otherwise, check if poll contains category + return noCategoriesSelected || poll.categories.some(c => categoryFilter && categoryFilter[c]); + }) + .filter(poll => { + if (!showPollEnded && !showPollActive) { + return true; + } + if (!showPollEnded && !isActivePoll(poll)) { + return false; + } + if (!showPollActive && isActivePoll(poll)) { + return false; + } + return true; + }); +} diff --git a/pages/polling.tsx b/pages/polling.tsx index 9d640f9ef..75ac537fe 100644 --- a/pages/polling.tsx +++ b/pages/polling.tsx @@ -35,6 +35,7 @@ import { getPolls } from 'modules/polling/api/fetchPolls'; import { useAllUserVotes } from 'modules/polling/hooks/useAllUserVotes'; import { HeadComponent } from 'modules/app/components/layout/Head'; import { PollsResponse } from 'modules/polling/types/pollsResponse'; +import { filterPolls } from 'modules/polling/helpers/filterPolls'; type Props = { polls: Poll[]; @@ -43,18 +44,28 @@ type Props = { const PollingOverview = ({ polls, categories }: Props) => { const { trackButtonClick } = useAnalytics(ANALYTICS_PAGES.POLLING_REVIEW); - const [startDate, endDate, categoryFilter, showHistorical, setShowHistorical, resetPollFilters] = - useUiFiltersStore( - state => [ - state.pollFilters.startDate, - state.pollFilters.endDate, - state.pollFilters.categoryFilter, - state.pollFilters.showHistorical, - state.setShowHistorical, - state.resetPollFilters - ], - shallow - ); + const [ + startDate, + endDate, + categoryFilter, + showHistorical, + showPollActive, + showPollEnded, + setShowHistorical, + resetPollFilters + ] = useUiFiltersStore( + state => [ + state.pollFilters.startDate, + state.pollFilters.endDate, + state.pollFilters.categoryFilter, + state.pollFilters.showHistorical, + state.pollFilters.showPollActive, + state.pollFilters.showPollEnded, + state.setShowHistorical, + state.resetPollFilters + ], + shallow + ); const [numHistoricalGroupingsLoaded, setNumHistoricalGroupingsLoaded] = useState(3); const ballot = useBallotStore(state => state.ballot); @@ -63,22 +74,12 @@ const PollingOverview = ({ polls, categories }: Props) => { const loader = useRef(null); const bpi = useBreakpointIndex(); - const noCategoriesSelected = categoryFilter === null || Object.values(categoryFilter).every(c => !c); - const start = startDate && new Date(startDate); - const end = endDate && new Date(endDate); - const filteredPolls = useMemo(() => { - return polls.filter(poll => { - // check date filters first - if (start && new Date(poll.endDate).getTime() < start.getTime()) return false; - if (end && new Date(poll.endDate).getTime() > end.getTime()) return false; - - // if no category filters selected, return all, otherwise, check if poll contains category - return noCategoriesSelected || poll.categories.some(c => categoryFilter && categoryFilter[c]); - }); - }, [polls, startDate, endDate, categoryFilter]); + return filterPolls(polls, startDate, endDate, categoryFilter, showPollActive, showPollEnded); + }, [polls, startDate, endDate, categoryFilter, showPollActive, showPollEnded]); - const [activePolls, historicalPolls] = partition(filteredPolls, isActivePoll); + const [activePolls, setActivePolls] = useState([]); + const [historicalPolls, setHistoricalPolls] = useState([]); const groupedActivePolls = groupBy(activePolls, 'endDate'); const sortedEndDatesActive = sortBy(Object.keys(groupedActivePolls), x => new Date(x)); @@ -87,10 +88,15 @@ const PollingOverview = ({ polls, categories }: Props) => { const sortedEndDatesHistorical = sortBy(Object.keys(groupedHistoricalPolls), x => -new Date(x)); useEffect(() => { - if (activePolls.length === 0) { + const [active, historical] = partition(filteredPolls, isActivePoll); + + if (active.length === 0) { setShowHistorical(true); } - }, []); + + setActivePolls(active); + setHistoricalPolls(historical); + }, [filteredPolls]); const loadMore = entries => { const target = entries.pop(); @@ -155,7 +161,7 @@ const PollingOverview = ({ polls, categories }: Props) => { Filters - + - - + + - +
- + {expanded && ( {events.map(({ blockTimestamp }) => { return ( - + {format(new Date(blockTimestamp), dateFormat)} ); @@ -57,7 +64,14 @@ const CollapsableRow = ({ delegator, network, bpi, totalDelegated }: Collapsable {events.map(({ blockTimestamp, lockAmount }) => { return ( - + {lockAmount.indexOf('-') === 0 ? ( ) : ( @@ -89,7 +103,9 @@ const CollapsableRow = ({ delegator, network, bpi, totalDelegated }: Collapsable - Total Percent + Voting Weight - Verify + Expand
From e47da35c8c23fbc12298c6be30e431218e4051b5 Mon Sep 17 00:00:00 2001 From: Phil Bain Date: Thu, 2 Dec 2021 17:17:40 -0700 Subject: [PATCH 18/36] fetch the delegates an address has delegated to --- .../delegates/api/fetchDelegationHistory.ts | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/modules/delegates/api/fetchDelegationHistory.ts b/modules/delegates/api/fetchDelegationHistory.ts index 4d2c9512c..a6dbcc9aa 100644 --- a/modules/delegates/api/fetchDelegationHistory.ts +++ b/modules/delegates/api/fetchDelegationHistory.ts @@ -33,6 +33,31 @@ export async function fetchDelegationHistory( [] ); + const delegateD: MKRLockedDelegateAPIResponse[] = await maker + .service('voteDelegate') + .getMkrDelegatedTo('0xc0583df0d10c2e87ae1873b728a0bda04d8b660c'); + + const reddelegateD = delegateD.reduce( + (acc, { fromAddress, immediateCaller, lockAmount, blockTimestamp }) => { + const existing = acc.find(({ address }) => address === immediateCaller); + if (existing) { + existing.lockAmount = utils.formatEther( + utils.parseEther(existing.lockAmount).add(utils.parseEther(lockAmount)) + ); + } else { + acc.push({ + address: immediateCaller, + lockAmount: utils.formatEther(utils.parseEther(lockAmount)) + }); + } + + return acc; + }, + [] + ); + + console.log('got reddelegateD:', reddelegateD); + return delegators.sort((a, b) => utils.parseEther(a.lockAmount).gt(utils.parseEther(b.lockAmount)) ? -1 : 1 ); From 703ac3938974ba9aa2e9677c8967c1a8965fb0aa Mon Sep 17 00:00:00 2001 From: Phil Bain Date: Thu, 2 Dec 2021 22:48:36 -0700 Subject: [PATCH 19/36] add table and feed data --- .../address/components/AddressDelegatedTo.tsx | 111 ++++++++++++++++++ modules/address/components/AddressDetail.tsx | 19 +++ modules/delegates/api/fetchDelegatedTo.ts | 38 ++++++ .../delegates/api/fetchDelegationHistory.ts | 25 ---- pages/api/address/[address]/stats.ts | 9 +- 5 files changed, 176 insertions(+), 26 deletions(-) create mode 100644 modules/address/components/AddressDelegatedTo.tsx create mode 100644 modules/delegates/api/fetchDelegatedTo.ts diff --git a/modules/address/components/AddressDelegatedTo.tsx b/modules/address/components/AddressDelegatedTo.tsx new file mode 100644 index 000000000..1da3467fe --- /dev/null +++ b/modules/address/components/AddressDelegatedTo.tsx @@ -0,0 +1,111 @@ +import { useState } from 'react'; +import Link from 'next/link'; +import { Box, Text, Link as ThemeUILink, Flex, IconButton, Heading } from 'theme-ui'; +import { useBreakpointIndex } from '@theme-ui/match-media'; +import { Icon } from '@makerdao/dai-ui-icons'; +import BigNumber from 'bignumber.js'; +import { format } from 'date-fns'; +import { getNetwork } from 'lib/maker'; +import { CurrencyObject } from 'types/currency'; +import { Address } from 'modules/address/components/Address'; +import Skeleton from 'modules/app/components/SkeletonThemed'; +import { DelegationHistory } from 'modules/delegates/types'; + +type DelegatedByAddressProps = { + delegators: DelegationHistory[]; + totalDelegated: CurrencyObject; +}; + +type CollapsableRowProps = { + delegator: DelegationHistory; + network: string; + bpi: number; + totalDelegated: CurrencyObject; +}; + +const CollapsableRow = ({ delegate, network, bpi, totalDelegated }: CollapsableRowProps) => { + const dateFormat = 'MMM dd yyyy h:mm'; + const { address, lockAmount } = delegate; + return ( + + + + + +
+ + + + + + + {`${new BigNumber(lockAmount).toFormat(2)}${bpi > 0 ? ' MKR' : ''}`} + + + + {totalDelegated ? ( + + {`${new BigNumber(lockAmount).div(totalDelegated.toBigNumber()).times(100).toFormat(1)}%`} + + ) : ( + + + + )} + +
+ ); +}; + +const AddressDelegatedTo = ({ delegatedTo, totalDelegated }: DelegatedByAddressProps): JSX.Element => { + const bpi = useBreakpointIndex(); + const network = getNetwork(); + + return ( + +
+ + + + Address + + + MKR Delegated + + + Voting Weight + + + + + {delegatedTo ? ( + delegatedTo.map((delegate, i) => ( + + )) + ) : ( + + + + )} + +
+ + Loading + +
+
+ ); +}; + +export default AddressDelegatedTo; diff --git a/modules/address/components/AddressDetail.tsx b/modules/address/components/AddressDetail.tsx index 01e149ea7..2031fd935 100644 --- a/modules/address/components/AddressDetail.tsx +++ b/modules/address/components/AddressDetail.tsx @@ -12,6 +12,7 @@ import { Address } from './Address'; import useSWR from 'swr'; import { fetchJson } from 'lib/fetchJson'; import LastVoted from 'modules/polling/components/LastVoted'; +import AddressDelegatedTo from './AddressDelegatedTo'; type PropTypes = { address: string; @@ -93,6 +94,24 @@ export function AddressDetail({ address, voteProxyInfo }: PropTypes): React.Reac + + + Delegates + + + + + + + + + { + const maker = await getMaker(network); + let delegatedTox = []; + + try { + const delRes: MKRLockedDelegateAPIResponse[] = await maker + .service('voteDelegate') + .getMkrDelegatedTo(address); + + delegatedTox = delRes.reduce((acc, { fromAddress, immediateCaller, lockAmount, blockTimestamp }) => { + const existing = acc.find(({ address }) => address === immediateCaller); + if (existing) { + existing.lockAmount = utils.formatEther( + utils.parseEther(existing.lockAmount).add(utils.parseEther(lockAmount)) + ); + } else { + acc.push({ + address: immediateCaller, + lockAmount: utils.formatEther(utils.parseEther(lockAmount)) + }); + } + + return acc; + }, []); + } catch (e) { + console.error('delegated to error'); + } + + return delegatedTox; //filter 0 here not in the api +} diff --git a/modules/delegates/api/fetchDelegationHistory.ts b/modules/delegates/api/fetchDelegationHistory.ts index a6dbcc9aa..4d2c9512c 100644 --- a/modules/delegates/api/fetchDelegationHistory.ts +++ b/modules/delegates/api/fetchDelegationHistory.ts @@ -33,31 +33,6 @@ export async function fetchDelegationHistory( [] ); - const delegateD: MKRLockedDelegateAPIResponse[] = await maker - .service('voteDelegate') - .getMkrDelegatedTo('0xc0583df0d10c2e87ae1873b728a0bda04d8b660c'); - - const reddelegateD = delegateD.reduce( - (acc, { fromAddress, immediateCaller, lockAmount, blockTimestamp }) => { - const existing = acc.find(({ address }) => address === immediateCaller); - if (existing) { - existing.lockAmount = utils.formatEther( - utils.parseEther(existing.lockAmount).add(utils.parseEther(lockAmount)) - ); - } else { - acc.push({ - address: immediateCaller, - lockAmount: utils.formatEther(utils.parseEther(lockAmount)) - }); - } - - return acc; - }, - [] - ); - - console.log('got reddelegateD:', reddelegateD); - return delegators.sort((a, b) => utils.parseEther(a.lockAmount).gt(utils.parseEther(b.lockAmount)) ? -1 : 1 ); diff --git a/pages/api/address/[address]/stats.ts b/pages/api/address/[address]/stats.ts index 2e52056b1..3665c11eb 100644 --- a/pages/api/address/[address]/stats.ts +++ b/pages/api/address/[address]/stats.ts @@ -1,4 +1,5 @@ import invariant from 'tiny-invariant'; +import { utils } from 'ethers'; import { NextApiRequest, NextApiResponse } from 'next'; import getMaker from 'lib/maker'; import voteProxyFactoryAbi from 'lib/abis/voteProxyAbi.json'; @@ -8,6 +9,7 @@ import withApiHandler from 'lib/api/withApiHandler'; import { AddressAPIStats } from 'modules/address/types/addressApiResponse'; import { fetchAddressPollVoteHistory } from 'modules/polling/api/fetchAddressPollVoteHistory'; import { resolveENS } from 'modules/web3/ens'; +import { fetchDelegatedTo } from 'modules/delegates/api/fetchDelegatedTo'; /** * @swagger @@ -96,9 +98,14 @@ export default withApiHandler(async (req: NextApiRequest, res: NextApiResponse
(a.blockTimestamp > b.blockTimestamp ? -1 : 1))[0] + lastVote: pollVoteHistory.sort((a, b) => (a.blockTimestamp > b.blockTimestamp ? -1 : 1))[0], + delegatedTo: delegatedTo + .filter(({ lockAmount }) => utils.parseEther(lockAmount).gt(0)) + .sort((a, b) => (utils.parseEther(a.lockAmount).gt(utils.parseEther(b.lockAmount)) ? -1 : 1)) }; res.setHeader('Cache-Control', 's-maxage=15, stale-while-revalidate'); From 2ea354e59398aadaffcea4ea9c7d8d68c812f377 Mon Sep 17 00:00:00 2001 From: Phil Bain Date: Thu, 2 Dec 2021 22:55:46 -0700 Subject: [PATCH 20/36] fix CurrencyObject type import --- modules/delegates/components/DelegatedByAddress.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/delegates/components/DelegatedByAddress.tsx b/modules/delegates/components/DelegatedByAddress.tsx index ea4cd98ca..5c8d834ee 100644 --- a/modules/delegates/components/DelegatedByAddress.tsx +++ b/modules/delegates/components/DelegatedByAddress.tsx @@ -6,7 +6,7 @@ import { Icon } from '@makerdao/dai-ui-icons'; import BigNumber from 'bignumber.js'; import { format } from 'date-fns'; import { getNetwork } from 'lib/maker'; -import { CurrencyObject } from 'types/currency'; +import { CurrencyObject } from 'modules/app/types/currency'; import { Address } from 'modules/address/components/Address'; import Skeleton from 'modules/app/components/SkeletonThemed'; import { DelegationHistory } from 'modules/delegates/types'; From 5f1fda422b2f3a10f42d8e5e4e477b86d0af4e2e Mon Sep 17 00:00:00 2001 From: Tyler Sorensen Date: Thu, 2 Dec 2021 23:05:40 -0800 Subject: [PATCH 21/36] edit title properly in exec/create page --- pages/executive/create.tsx | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/pages/executive/create.tsx b/pages/executive/create.tsx index a681cf1d7..e09c463dc 100644 --- a/pages/executive/create.tsx +++ b/pages/executive/create.tsx @@ -67,12 +67,17 @@ const ExecutiveCreate = () => { } } - //remove `Template - [Executive Vote] ` from title - const split = metadata.title.split('Template - [Executive Vote] '); - const editedTitle = split.length > 1 ? split[1] : title; + //remove `Template - [ ... ] ` from title + const editTitle = title => { + const vStr = 'Template - [Executive Vote] '; + const pStr = 'Template - [Executive Proposal] '; + if (title.indexOf(vStr) === 0) return title.replace(vStr, ''); + if (title.indexOf(pStr) === 0) return title.replace(pStr, ''); + return title; + }; setFetchFinished(true); - setTitle(editedTitle); + setTitle(editTitle(metadata.title)); setSummary(metadata.summary); setDate(metadata.date ? new Date(metadata.date).toUTCString() : ''); setMainnetAddress(metadata.address); From ebd0111b27ecf1b3b12737292eee170562183d16 Mon Sep 17 00:00:00 2001 From: Rafael Ventura Date: Fri, 3 Dec 2021 12:41:15 +0100 Subject: [PATCH 22/36] Add video modal home (#264) * Add video modal home * Add video modal home --- modules/app/components/VideoModal.tsx | 59 ++++++++++++++++++++++++++ modules/app/components/layout/Head.tsx | 2 +- pages/index.tsx | 14 +++++- 3 files changed, 73 insertions(+), 2 deletions(-) create mode 100644 modules/app/components/VideoModal.tsx diff --git a/modules/app/components/VideoModal.tsx b/modules/app/components/VideoModal.tsx new file mode 100644 index 000000000..6900b13f2 --- /dev/null +++ b/modules/app/components/VideoModal.tsx @@ -0,0 +1,59 @@ +import React from 'react'; +import { Box } from 'theme-ui'; +import { DialogOverlay, DialogContent } from '@reach/dialog'; +import { useBreakpointIndex } from '@theme-ui/match-media'; +import { fadeIn, slideUp } from 'lib/keyframes'; + +const VideoModal = ({ + embedId, + isOpen, + onDismiss +}: { + embedId?: string; + isOpen: boolean; + onDismiss: () => void; +}): React.ReactElement => { + const bpi = useBreakpointIndex(); + + return ( + + + +