diff --git a/apps/token-e2e/src/integration/view/governance.cy.js b/apps/token-e2e/src/integration/view/governance.cy.js index e90fe3a8a..6342673f8 100644 --- a/apps/token-e2e/src/integration/view/governance.cy.js +++ b/apps/token-e2e/src/integration/view/governance.cy.js @@ -18,7 +18,7 @@ context( }); it('should have GOVERNANCE header visible', function () { - cy.verify_page_header('Governance'); + cy.verify_page_header('Proposals'); }); it('should be able to see a working link for - find out more about Vega governance', function () { diff --git a/apps/token/src/components/heading/heading.tsx b/apps/token/src/components/heading/heading.tsx index 850e0b5ce..7d446c4aa 100644 --- a/apps/token/src/components/heading/heading.tsx +++ b/apps/token/src/components/heading/heading.tsx @@ -3,24 +3,28 @@ import classNames from 'classnames'; interface HeadingProps { title?: string; centerContent?: boolean; + marginTop?: boolean; marginBottom?: boolean; } export const Heading = ({ title, centerContent = true, + marginTop = true, marginBottom = true, }: HeadingProps) => { if (!title) return null; return (

diff --git a/apps/token/src/components/progress-bar/progress-bar.tsx b/apps/token/src/components/progress-bar/progress-bar.tsx index 371dab8aa..0ac155196 100644 --- a/apps/token/src/components/progress-bar/progress-bar.tsx +++ b/apps/token/src/components/progress-bar/progress-bar.tsx @@ -16,7 +16,7 @@ export const ProgressBar = ({ value }: ProgressBarProps) => { className="relative h-2 bg-neutral-600 rounded-full overflow-hidden" >
diff --git a/apps/token/src/hooks/use-refresh-validators.ts b/apps/token/src/hooks/use-refresh-validators.ts new file mode 100644 index 000000000..b6fd41570 --- /dev/null +++ b/apps/token/src/hooks/use-refresh-validators.ts @@ -0,0 +1,24 @@ +import type { ObservableQuery } from '@apollo/client'; +import { useEffect } from 'react'; + +export const useRefreshValidators = ( + epochExpiry: string | undefined, + refetch: ObservableQuery['refetch'] +) => { + return useEffect(() => { + const epochInterval = setInterval(() => { + if (!epochExpiry) return; + const now = Date.now(); + const expiry = new Date(epochExpiry).getTime(); + + if (now > expiry) { + refetch(); + clearInterval(epochInterval); + } + }, 10000); + + return () => { + clearInterval(epochInterval); + }; + }, [refetch, epochExpiry]); +}; diff --git a/apps/token/src/i18n/translations/dev.json b/apps/token/src/i18n/translations/dev.json index 4206548d6..176b95cb8 100644 --- a/apps/token/src/i18n/translations/dev.json +++ b/apps/token/src/i18n/translations/dev.json @@ -11,7 +11,7 @@ "pageTitle404": "Page not found", "pageTitleNotPermitted": "Can not proceed!", "pageTitleDisassociate": "Disassociate $VEGA tokens from a Vega key", - "pageTitleGovernance": "Governance", + "pageTitleProposals": "Proposals", "pageTitleDepositLp": "Deposit liquidity token for $VEGA rewards", "pageTitleWithdrawLp": "Withdraw SLP and Rewards", "pageTitleRewards": "Rewards", @@ -530,6 +530,8 @@ "validatorTableIntro": "View the validator profile pitches and discussion", "onTheForum": "on the forum", "readMoreStaking": "Read more about staking on Vega", + "readMoreGovernance": "Read more about Vega governance", + "readMoreValidators": "Read more about validators", "networkDown": "This site is not currently connecting to the network please try again later.", "ethTransactionModalTitle": "Ethereum Transactions", "confirmed": "Confirmed", @@ -719,5 +721,13 @@ "Score": "Score", "performancePenalty": "Performance penalty", "overstaked": "Overstaked", - "overstakedPenalty": "Overstaked penalty" + "overstakedPenalty": "Overstaked penalty", + "homeProposalsIntro": "Decisions on the Vega network are on-chain, with tokenholders creating proposals that other tokenholders vote to approve or reject.", + "homeProposalsButtonText": "Browse, vote, and propose", + "homeValidatorsIntro": "Vega runs on a delegated proof of stake blockchain, where validators earn fees for validating block transactions. Tokenholders can nominate validators by staking tokens to them.", + "homeValidatorsButtonText": "Browse, and stake", + "homeRewardsIntro": "Track rewards you've earned for trading, liquidity provision, market creation, and staking.", + "homeRewardsButtonText": "See rewards", + "homeVegaTokenIntro": "VEGA Token is a governance asset used to make and vote on proposals, and nominate validators.", + "homeVegaTokenButtonText": "Manage tokens" } diff --git a/apps/token/src/routes/home/index.tsx b/apps/token/src/routes/home/index.tsx index 877102236..97e8cd227 100644 --- a/apps/token/src/routes/home/index.tsx +++ b/apps/token/src/routes/home/index.tsx @@ -1,147 +1,219 @@ -import React from 'react'; -import { Trans, useTranslation } from 'react-i18next'; +import { useMemo } from 'react'; +import { useTranslation } from 'react-i18next'; import { Link } from 'react-router-dom'; - -import { Heading, SubHeading } from '../../components/heading'; -import { ExternalLinks } from '@vegaprotocol/react-helpers'; -import { useAppState } from '../../contexts/app-state/app-state-context'; +import { + AsyncRenderer, + Button, + ExternalLink, + RoundedWrapper, +} from '@vegaprotocol/ui-toolkit'; import { useDocumentTitle } from '../../hooks/use-document-title'; -import type { RouteChildProps } from '..'; +import { useRefreshValidators } from '../../hooks/use-refresh-validators'; +import { ProposalsListItem } from '../proposals/components/proposals-list-item'; import Routes from '../routes'; -import { TokenDetails } from './token-details'; -import { Button } from '@vegaprotocol/ui-toolkit'; -import { toBigNum } from '@vegaprotocol/react-helpers'; -import { useNodeDataQuery } from './__generated___/NodeData'; +import { + ExternalLinks, + removePaginationWrapper, +} from '@vegaprotocol/react-helpers'; +import { useNodesQuery } from '../staking/home/__generated___/Nodes'; +import { useProposalsQuery } from '../proposals/proposals/__generated__/Proposals'; +import { getNotRejectedProposals } from '../proposals/proposals/proposals-container'; +import { Heading } from '../../components/heading'; +import * as Schema from '@vegaprotocol/types'; +import type { RouteChildProps } from '..'; +import type { ProposalFieldsFragment } from '../proposals/proposals/__generated__/Proposals'; +import type { NodesFragmentFragment } from '../staking/home/__generated___/Nodes'; -const Home = ({ name }: RouteChildProps) => { - useDocumentTitle(name); +const nodesToShow = 6; + +const HomeProposals = ({ + proposals, +}: { + proposals: ProposalFieldsFragment[]; +}) => { const { t } = useTranslation(); - const { appState } = useAppState(); - const { data } = useNodeDataQuery(); - const totalAssociated = React.useMemo(() => { - return toBigNum(data?.nodeData?.stakedTotal || '0', appState.decimals); - }, [appState.decimals, data?.nodeData?.stakedTotal]); return ( - <> - - - - - - -

- {t( - 'The vesting contract holds VEGA tokens until they have become unlocked.' - )} -

-

- - ), - }} - /> -

-

- {t( - 'Once unlocked they can be redeemed from the contract so that you can transfer them between wallets.' - )} -

- - +
+ +

{t('homeProposalsIntro')}

+
+ + - - - -

- {t( - 'To use your tokens on the Vega network they need to be associated with a Vega wallet/key.' - )} -

-

- {t( - 'This can happen both while held in the vesting contract as well as when redeemed.' - )} -

-

- - {t('Get a Vega wallet')} - -

-

- - {t('Associate VEGA tokens')} - -

-
-
-
- - -

- {t( - 'VEGA token holders can nominate a validator node and receive staking rewards.' - )} -

-

- - - -

-
-
-
- - -

- {t( - 'VEGA token holders can vote on proposed changes to the network and create proposals.' - )} -

-

- - - -

-
-
+ + + {t(`readMoreGovernance`)} +
- +
    + {proposals.map((proposal) => ( + + ))} +
+
); }; -export default Home; +interface HomeNodesProps { + activeNodes: NodesFragmentFragment[]; + consensusNodes: NodesFragmentFragment[]; + trimmedActiveNodes: NodesFragmentFragment[]; +} -export const HomeSection = ({ children }: { children: React.ReactNode }) => { - return
{children}
; +const HomeNodes = ({ + activeNodes, + consensusNodes, + trimmedActiveNodes, +}: HomeNodesProps) => { + const { t } = useTranslation(); + + const highlightedNodeData = [ + { title: t('active nodes'), length: activeNodes.length }, + { title: t('consensus nodes'), length: consensusNodes.length }, + ]; + + return ( +
+ +

{t('homeValidatorsIntro')}

+
+ + + + + + {t(`readMoreValidators`)} + +
+
+ {highlightedNodeData.map(({ title, length }, index) => ( +
+ + +
+ {length} + + {title} + +
+
+ +
+ ))} + + {trimmedActiveNodes.map(({ id, avatarUrl, name }) => ( +
+ + +
+ {avatarUrl && ( + {`Avatar + )} + {name} +
+
+ +
+ ))} +
+ + {activeNodes.length > nodesToShow && ( + + + And {activeNodes.length - nodesToShow} more... + + + )} +
+ ); }; + +const GovernanceHome = ({ name }: RouteChildProps) => { + useDocumentTitle(name); + const { t } = useTranslation(); + const { + data: proposalsData, + loading: proposalsLoading, + error: proposalsError, + } = useProposalsQuery({ + pollInterval: 5000, + fetchPolicy: 'network-only', + errorPolicy: 'ignore', + }); + + const { + data: validatorsData, + error: validatorsError, + loading: validatorsLoading, + refetch, + } = useNodesQuery(); + + useRefreshValidators(validatorsData?.epoch.timestamps.expiry, refetch); + + const proposals = useMemo( + () => + proposalsData + ? getNotRejectedProposals( + proposalsData.proposalsConnection + ).slice(0, 3) + : [], + [proposalsData] + ); + + const activeNodes = removePaginationWrapper( + validatorsData?.nodesConnection.edges + ); + + const trimmedActiveNodes = activeNodes?.slice(0, nodesToShow); + + const consensusNodes = activeNodes.filter( + (node) => + node.rankingScore.status === + Schema.ValidatorStatus.VALIDATOR_NODE_STATUS_TENDERMINT + ); + + return ( + + + + + +
+
+ +

{t('homeRewardsIntro')}

+
+ + + +
+
+ +
+ +

{t('homeVegaTokenIntro')}

+
+ + + +
+
+
+
+ ); +}; + +export default GovernanceHome; diff --git a/apps/token/src/routes/governance/components/current-proposal-state/current-proposal-state.tsx b/apps/token/src/routes/proposals/components/current-proposal-state/current-proposal-state.tsx similarity index 100% rename from apps/token/src/routes/governance/components/current-proposal-state/current-proposal-state.tsx rename to apps/token/src/routes/proposals/components/current-proposal-state/current-proposal-state.tsx diff --git a/apps/token/src/routes/governance/components/current-proposal-state/index.tsx b/apps/token/src/routes/proposals/components/current-proposal-state/index.tsx similarity index 100% rename from apps/token/src/routes/governance/components/current-proposal-state/index.tsx rename to apps/token/src/routes/proposals/components/current-proposal-state/index.tsx diff --git a/apps/token/src/routes/governance/components/current-proposal-status/current-proposal-status.spec.tsx b/apps/token/src/routes/proposals/components/current-proposal-status/current-proposal-status.spec.tsx similarity index 100% rename from apps/token/src/routes/governance/components/current-proposal-status/current-proposal-status.spec.tsx rename to apps/token/src/routes/proposals/components/current-proposal-status/current-proposal-status.spec.tsx diff --git a/apps/token/src/routes/governance/components/current-proposal-status/current-proposal-status.tsx b/apps/token/src/routes/proposals/components/current-proposal-status/current-proposal-status.tsx similarity index 100% rename from apps/token/src/routes/governance/components/current-proposal-status/current-proposal-status.tsx rename to apps/token/src/routes/proposals/components/current-proposal-status/current-proposal-status.tsx diff --git a/apps/token/src/routes/governance/components/current-proposal-status/index.tsx b/apps/token/src/routes/proposals/components/current-proposal-status/index.tsx similarity index 100% rename from apps/token/src/routes/governance/components/current-proposal-status/index.tsx rename to apps/token/src/routes/proposals/components/current-proposal-status/index.tsx diff --git a/apps/token/src/routes/governance/components/list-asset/Asset.graphql b/apps/token/src/routes/proposals/components/list-asset/Asset.graphql similarity index 100% rename from apps/token/src/routes/governance/components/list-asset/Asset.graphql rename to apps/token/src/routes/proposals/components/list-asset/Asset.graphql diff --git a/apps/token/src/routes/governance/components/list-asset/__generated__/Asset.ts b/apps/token/src/routes/proposals/components/list-asset/__generated__/Asset.ts similarity index 100% rename from apps/token/src/routes/governance/components/list-asset/__generated__/Asset.ts rename to apps/token/src/routes/proposals/components/list-asset/__generated__/Asset.ts diff --git a/apps/token/src/routes/governance/components/list-asset/__generated___/Asset.ts b/apps/token/src/routes/proposals/components/list-asset/__generated___/Asset.ts similarity index 100% rename from apps/token/src/routes/governance/components/list-asset/__generated___/Asset.ts rename to apps/token/src/routes/proposals/components/list-asset/__generated___/Asset.ts diff --git a/apps/token/src/routes/governance/components/list-asset/index.tsx b/apps/token/src/routes/proposals/components/list-asset/index.tsx similarity index 100% rename from apps/token/src/routes/governance/components/list-asset/index.tsx rename to apps/token/src/routes/proposals/components/list-asset/index.tsx diff --git a/apps/token/src/routes/governance/components/list-asset/list-asset.spec.tsx b/apps/token/src/routes/proposals/components/list-asset/list-asset.spec.tsx similarity index 100% rename from apps/token/src/routes/governance/components/list-asset/list-asset.spec.tsx rename to apps/token/src/routes/proposals/components/list-asset/list-asset.spec.tsx diff --git a/apps/token/src/routes/governance/components/list-asset/list-asset.tsx b/apps/token/src/routes/proposals/components/list-asset/list-asset.tsx similarity index 100% rename from apps/token/src/routes/governance/components/list-asset/list-asset.tsx rename to apps/token/src/routes/proposals/components/list-asset/list-asset.tsx diff --git a/apps/token/src/routes/governance/components/proposal-change-table/index.tsx b/apps/token/src/routes/proposals/components/proposal-change-table/index.tsx similarity index 100% rename from apps/token/src/routes/governance/components/proposal-change-table/index.tsx rename to apps/token/src/routes/proposals/components/proposal-change-table/index.tsx diff --git a/apps/token/src/routes/governance/components/proposal-change-table/proposal-change-table.spec.tsx b/apps/token/src/routes/proposals/components/proposal-change-table/proposal-change-table.spec.tsx similarity index 100% rename from apps/token/src/routes/governance/components/proposal-change-table/proposal-change-table.spec.tsx rename to apps/token/src/routes/proposals/components/proposal-change-table/proposal-change-table.spec.tsx diff --git a/apps/token/src/routes/governance/components/proposal-change-table/proposal-change-table.tsx b/apps/token/src/routes/proposals/components/proposal-change-table/proposal-change-table.tsx similarity index 100% rename from apps/token/src/routes/governance/components/proposal-change-table/proposal-change-table.tsx rename to apps/token/src/routes/proposals/components/proposal-change-table/proposal-change-table.tsx diff --git a/apps/token/src/routes/governance/components/proposal-detail-header/proposal-header.spec.tsx b/apps/token/src/routes/proposals/components/proposal-detail-header/proposal-header.spec.tsx similarity index 90% rename from apps/token/src/routes/governance/components/proposal-detail-header/proposal-header.spec.tsx rename to apps/token/src/routes/proposals/components/proposal-detail-header/proposal-header.spec.tsx index c6f42f20b..5ecc7c12c 100644 --- a/apps/token/src/routes/governance/components/proposal-detail-header/proposal-header.spec.tsx +++ b/apps/token/src/routes/proposals/components/proposal-detail-header/proposal-header.spec.tsx @@ -39,6 +39,7 @@ describe('Proposal header', () => { expect(screen.getByTestId('proposal-title')).toHaveTextContent( 'New some market' ); + expect(screen.getByTestId('proposal-type')).toHaveTextContent('New market'); expect(screen.getByTestId('proposal-description')).toHaveTextContent( 'A new some market' ); @@ -66,6 +67,9 @@ describe('Proposal header', () => { expect(screen.getByTestId('proposal-title')).toHaveTextContent( 'New market id' ); + expect(screen.getByTestId('proposal-type')).toHaveTextContent( + 'Update market' + ); expect( screen.queryByTestId('proposal-description') ).not.toBeInTheDocument(); @@ -99,6 +103,7 @@ describe('Proposal header', () => { expect(screen.getByTestId('proposal-title')).toHaveTextContent( 'New asset: Fake currency' ); + expect(screen.getByTestId('proposal-type')).toHaveTextContent('New asset'); expect(screen.getByTestId('proposal-details')).toHaveTextContent( 'Symbol: FAKE. ERC20 0x0' ); @@ -125,6 +130,7 @@ describe('Proposal header', () => { expect(screen.getByTestId('proposal-title')).toHaveTextContent( 'Unknown proposal' ); + expect(screen.getByTestId('proposal-type')).toHaveTextContent('New asset'); expect(screen.getByTestId('proposal-details')).toHaveTextContent( 'Symbol: BIA. Max faucet amount mint: 300' ); @@ -153,6 +159,9 @@ describe('Proposal header', () => { expect(screen.getByTestId('proposal-title')).toHaveTextContent( 'Network parameter' ); + expect(screen.getByTestId('proposal-type')).toHaveTextContent( + 'Network parameter' + ); expect(screen.getByTestId('proposal-details')).toHaveTextContent( 'Network key to Network value' ); @@ -175,6 +184,7 @@ describe('Proposal header', () => { ) ); expect(screen.getByTestId('proposal-title')).toHaveTextContent('0x0'); + expect(screen.getByTestId('proposal-type')).toHaveTextContent('Freeform'); expect( screen.queryByTestId('proposal-description') ).not.toBeInTheDocument(); @@ -202,6 +212,7 @@ describe('Proposal header', () => { // For a rationale over 100 chars, we expect the header to be truncated at // 100 chars with ellipsis and the details-one element to contain the rest. expect(screen.getByTestId('proposal-title')).toHaveTextContent('0x0'); + expect(screen.getByTestId('proposal-type')).toHaveTextContent('Freeform'); expect(screen.getByTestId('proposal-description')).toHaveTextContent( 'Class aptent taciti sociosqu ad litora torquent per conubia' ); @@ -226,6 +237,7 @@ describe('Proposal header', () => { ) ); expect(screen.getByTestId('proposal-title')).toHaveTextContent('freeform'); + expect(screen.getByTestId('proposal-type')).toHaveTextContent('Freeform'); expect( screen.queryByTestId('proposal-description') ).not.toBeInTheDocument(); @@ -247,6 +259,9 @@ describe('Proposal header', () => { }) ) ); + expect(screen.getByTestId('proposal-type')).toHaveTextContent( + 'Update asset' + ); expect(screen.getByTestId('proposal-details')).toHaveTextContent( 'Update asset' ); diff --git a/apps/token/src/routes/governance/components/proposal-detail-header/proposal-header.tsx b/apps/token/src/routes/proposals/components/proposal-detail-header/proposal-header.tsx similarity index 71% rename from apps/token/src/routes/governance/components/proposal-detail-header/proposal-header.tsx rename to apps/token/src/routes/proposals/components/proposal-detail-header/proposal-header.tsx index 4871fce3b..ca4292efb 100644 --- a/apps/token/src/routes/governance/components/proposal-detail-header/proposal-header.tsx +++ b/apps/token/src/routes/proposals/components/proposal-detail-header/proposal-header.tsx @@ -1,20 +1,23 @@ import { useTranslation } from 'react-i18next'; -import { Lozenge } from '@vegaprotocol/ui-toolkit'; +import { Intent, Lozenge } from '@vegaprotocol/ui-toolkit'; import { shorten } from '@vegaprotocol/react-helpers'; -import { SubHeading } from '../../../../components/heading'; +import { Heading, SubHeading } from '../../../../components/heading'; import type { ReactNode } from 'react'; import type { ProposalFieldsFragment } from '../../proposals/__generated__/Proposals'; import type { ProposalQuery } from '../../proposal/__generated__/Proposal'; export const ProposalHeader = ({ proposal, + useSubHeading = true, }: { proposal: ProposalFieldsFragment | ProposalQuery['proposal']; + useSubHeading?: boolean; }) => { const { t } = useTranslation(); const change = proposal?.terms.change; let details: ReactNode; + let proposalType: ReactNode; let title = proposal?.rationale.title.trim(); let description = proposal?.rationale.description.trim(); @@ -27,6 +30,7 @@ export const ProposalHeader = ({ switch (change?.__typename) { case 'NewMarket': { + proposalType = t('NewMarket'); details = ( <> {t('Code')}: {change.instrument.code}.{' '} @@ -45,10 +49,12 @@ export const ProposalHeader = ({ break; } case 'UpdateMarket': { + proposalType = t('UpdateMarket'); details = `${t('Market change')}: ${change.marketId}`; break; } case 'NewAsset': { + proposalType = t('NewAsset'); details = ( <> {t('Symbol')}: {change.symbol}.{' '} @@ -65,6 +71,7 @@ export const ProposalHeader = ({ break; } case 'UpdateNetworkParameter': { + proposalType = t('NetworkParameter'); const parametersClasses = 'font-mono leading-none'; details = ( <> @@ -80,10 +87,12 @@ export const ProposalHeader = ({ break; } case 'NewFreeform': { + proposalType = t('Freeform'); details = `${t('FreeformProposal')}: ${proposal?.id}`; break; } case 'UpdateAsset': { + proposalType = t('UpdateAsset'); details = ( <> `${t('Update asset')}`; @@ -96,14 +105,28 @@ export const ProposalHeader = ({ return (
-
- -
- {description && ( -
- {description} -
- )} +
+ {useSubHeading ? ( +
+ +
+ ) : ( + + )} +
+ +
+ {proposalType && ( +
+ {proposalType} +
+ )} + + {description && ( +
{description}
+ )} +
+ {details &&
{details}
}
); diff --git a/apps/token/src/routes/governance/components/proposal-not-found/index.tsx b/apps/token/src/routes/proposals/components/proposal-not-found/index.tsx similarity index 100% rename from apps/token/src/routes/governance/components/proposal-not-found/index.tsx rename to apps/token/src/routes/proposals/components/proposal-not-found/index.tsx diff --git a/apps/token/src/routes/governance/components/proposal-not-found/proposal-not-found.spec.tsx b/apps/token/src/routes/proposals/components/proposal-not-found/proposal-not-found.spec.tsx similarity index 100% rename from apps/token/src/routes/governance/components/proposal-not-found/proposal-not-found.spec.tsx rename to apps/token/src/routes/proposals/components/proposal-not-found/proposal-not-found.spec.tsx diff --git a/apps/token/src/routes/governance/components/proposal-not-found/proposal-not-found.tsx b/apps/token/src/routes/proposals/components/proposal-not-found/proposal-not-found.tsx similarity index 100% rename from apps/token/src/routes/governance/components/proposal-not-found/proposal-not-found.tsx rename to apps/token/src/routes/proposals/components/proposal-not-found/proposal-not-found.tsx diff --git a/apps/token/src/routes/governance/components/proposal-terms-json/index.tsx b/apps/token/src/routes/proposals/components/proposal-terms-json/index.tsx similarity index 100% rename from apps/token/src/routes/governance/components/proposal-terms-json/index.tsx rename to apps/token/src/routes/proposals/components/proposal-terms-json/index.tsx diff --git a/apps/token/src/routes/governance/components/proposal-terms-json/proposal-terms-json.tsx b/apps/token/src/routes/proposals/components/proposal-terms-json/proposal-terms-json.tsx similarity index 100% rename from apps/token/src/routes/governance/components/proposal-terms-json/proposal-terms-json.tsx rename to apps/token/src/routes/proposals/components/proposal-terms-json/proposal-terms-json.tsx diff --git a/apps/token/src/routes/governance/components/proposal-votes-table/index.tsx b/apps/token/src/routes/proposals/components/proposal-votes-table/index.tsx similarity index 100% rename from apps/token/src/routes/governance/components/proposal-votes-table/index.tsx rename to apps/token/src/routes/proposals/components/proposal-votes-table/index.tsx diff --git a/apps/token/src/routes/governance/components/proposal-votes-table/proposal-votes-table.spec.tsx b/apps/token/src/routes/proposals/components/proposal-votes-table/proposal-votes-table.spec.tsx similarity index 100% rename from apps/token/src/routes/governance/components/proposal-votes-table/proposal-votes-table.spec.tsx rename to apps/token/src/routes/proposals/components/proposal-votes-table/proposal-votes-table.spec.tsx diff --git a/apps/token/src/routes/governance/components/proposal-votes-table/proposal-votes-table.tsx b/apps/token/src/routes/proposals/components/proposal-votes-table/proposal-votes-table.tsx similarity index 100% rename from apps/token/src/routes/governance/components/proposal-votes-table/proposal-votes-table.tsx rename to apps/token/src/routes/proposals/components/proposal-votes-table/proposal-votes-table.tsx diff --git a/apps/token/src/routes/governance/components/proposal/index.tsx b/apps/token/src/routes/proposals/components/proposal/index.tsx similarity index 100% rename from apps/token/src/routes/governance/components/proposal/index.tsx rename to apps/token/src/routes/proposals/components/proposal/index.tsx diff --git a/apps/token/src/routes/governance/components/proposal/proposal.spec.tsx b/apps/token/src/routes/proposals/components/proposal/proposal.spec.tsx similarity index 100% rename from apps/token/src/routes/governance/components/proposal/proposal.spec.tsx rename to apps/token/src/routes/proposals/components/proposal/proposal.spec.tsx diff --git a/apps/token/src/routes/governance/components/proposal/proposal.tsx b/apps/token/src/routes/proposals/components/proposal/proposal.tsx similarity index 98% rename from apps/token/src/routes/governance/components/proposal/proposal.tsx rename to apps/token/src/routes/proposals/components/proposal/proposal.tsx index f0e02a8fc..2559ea11d 100644 --- a/apps/token/src/routes/governance/components/proposal/proposal.tsx +++ b/apps/token/src/routes/proposals/components/proposal/proposal.tsx @@ -74,7 +74,7 @@ export const Proposal = ({ proposal }: ProposalProps) => { return (
- +
diff --git a/apps/token/src/routes/governance/components/proposals-list-filter/index.tsx b/apps/token/src/routes/proposals/components/proposals-list-filter/index.tsx similarity index 100% rename from apps/token/src/routes/governance/components/proposals-list-filter/index.tsx rename to apps/token/src/routes/proposals/components/proposals-list-filter/index.tsx diff --git a/apps/token/src/routes/governance/components/proposals-list-filter/proposals-list-filter.tsx b/apps/token/src/routes/proposals/components/proposals-list-filter/proposals-list-filter.tsx similarity index 100% rename from apps/token/src/routes/governance/components/proposals-list-filter/proposals-list-filter.tsx rename to apps/token/src/routes/proposals/components/proposals-list-filter/proposals-list-filter.tsx diff --git a/apps/token/src/routes/governance/components/proposals-list-item/index.tsx b/apps/token/src/routes/proposals/components/proposals-list-item/index.tsx similarity index 100% rename from apps/token/src/routes/governance/components/proposals-list-item/index.tsx rename to apps/token/src/routes/proposals/components/proposals-list-item/index.tsx diff --git a/apps/token/src/routes/governance/components/proposals-list-item/proposals-list-item-details.spec.tsx b/apps/token/src/routes/proposals/components/proposals-list-item/proposals-list-item-details.spec.tsx similarity index 99% rename from apps/token/src/routes/governance/components/proposals-list-item/proposals-list-item-details.spec.tsx rename to apps/token/src/routes/proposals/components/proposals-list-item/proposals-list-item-details.spec.tsx index a3b764a45..81d353ca8 100644 --- a/apps/token/src/routes/governance/components/proposals-list-item/proposals-list-item-details.spec.tsx +++ b/apps/token/src/routes/proposals/components/proposals-list-item/proposals-list-item-details.spec.tsx @@ -192,7 +192,6 @@ describe('Proposals list item details', () => { networkParamsQueryMock, createUserVoteQueryMock(proposal?.id, VoteValue.VALUE_YES), ]); - screen.debug(); expect(screen.getByTestId('proposal-status')).toHaveTextContent('Open'); expect(await screen.findByText('You voted')).toBeInTheDocument(); diff --git a/apps/token/src/routes/governance/components/proposals-list-item/proposals-list-item-details.tsx b/apps/token/src/routes/proposals/components/proposals-list-item/proposals-list-item-details.tsx similarity index 100% rename from apps/token/src/routes/governance/components/proposals-list-item/proposals-list-item-details.tsx rename to apps/token/src/routes/proposals/components/proposals-list-item/proposals-list-item-details.tsx diff --git a/apps/token/src/routes/governance/components/proposals-list-item/proposals-list-item.tsx b/apps/token/src/routes/proposals/components/proposals-list-item/proposals-list-item.tsx similarity index 66% rename from apps/token/src/routes/governance/components/proposals-list-item/proposals-list-item.tsx rename to apps/token/src/routes/proposals/components/proposals-list-item/proposals-list-item.tsx index e205a5523..f7145c82d 100644 --- a/apps/token/src/routes/governance/components/proposals-list-item/proposals-list-item.tsx +++ b/apps/token/src/routes/proposals/components/proposals-list-item/proposals-list-item.tsx @@ -1,3 +1,4 @@ +import { RoundedWrapper } from '@vegaprotocol/ui-toolkit'; import { ProposalHeader } from '../proposal-detail-header/proposal-header'; import { ProposalsListItemDetails } from './proposals-list-item-details'; import type { ProposalFieldsFragment } from '../../proposals/__generated__/Proposals'; @@ -11,13 +12,11 @@ export const ProposalsListItem = ({ proposal }: ProposalsListItemProps) => { if (!proposal || !proposal.id) return null; return ( -
  • - - +
  • + + + +
  • ); }; diff --git a/apps/token/src/routes/governance/components/proposals-list/index.tsx b/apps/token/src/routes/proposals/components/proposals-list/index.tsx similarity index 100% rename from apps/token/src/routes/governance/components/proposals-list/index.tsx rename to apps/token/src/routes/proposals/components/proposals-list/index.tsx diff --git a/apps/token/src/routes/governance/components/proposals-list/proposals-list.spec.tsx b/apps/token/src/routes/proposals/components/proposals-list/proposals-list.spec.tsx similarity index 99% rename from apps/token/src/routes/governance/components/proposals-list/proposals-list.spec.tsx rename to apps/token/src/routes/proposals/components/proposals-list/proposals-list.spec.tsx index f8ddc2156..5a7195818 100644 --- a/apps/token/src/routes/governance/components/proposals-list/proposals-list.spec.tsx +++ b/apps/token/src/routes/proposals/components/proposals-list/proposals-list.spec.tsx @@ -81,7 +81,7 @@ afterAll(() => { describe('Proposals list', () => { it('Render a page title and link to the make proposal form', () => { render(renderComponent([])); - expect(screen.getByText('Governance')).toBeInTheDocument(); + expect(screen.getByText('Proposals')).toBeInTheDocument(); expect(screen.getByTestId('new-proposal-link')).toBeInTheDocument(); }); diff --git a/apps/token/src/routes/governance/components/proposals-list/proposals-list.tsx b/apps/token/src/routes/proposals/components/proposals-list/proposals-list.tsx similarity index 98% rename from apps/token/src/routes/governance/components/proposals-list/proposals-list.tsx rename to apps/token/src/routes/proposals/components/proposals-list/proposals-list.tsx index c66b5a9bf..2051ed593 100644 --- a/apps/token/src/routes/governance/components/proposals-list/proposals-list.tsx +++ b/apps/token/src/routes/proposals/components/proposals-list/proposals-list.tsx @@ -51,7 +51,7 @@ export const ProposalsList = ({ proposals }: ProposalsListProps) => { import( - /* webpackChunkName: "route-governance", webpackPrefetch: true */ './governance' + /* webpackChunkName: "route-governance-home", webpackPrefetch: true */ './home' ) ); -const LazyGovernanceProposal = React.lazy( +const LazyProposals = React.lazy( () => import( - /* webpackChunkName: "route-governance-proposal", webpackPrefetch: true */ './governance/proposal' + /* webpackChunkName: "route-governance", webpackPrefetch: true */ './proposals' ) ); -const LazyGovernanceProposals = React.lazy( +const LazyProposal = React.lazy( () => import( - /* webpackChunkName: "route-governance-proposals", webpackPrefetch: true */ './governance/proposals' + /* webpackChunkName: "route-governance-proposal", webpackPrefetch: true */ './proposals/proposal' ) ); -const LazyRejectedGovernanceProposals = React.lazy( +const LazyProposalsList = React.lazy( () => import( - /* webpackChunkName: "route-governance-proposals", webpackPrefetch: true */ './governance/rejected' + /* webpackChunkName: "route-governance-proposals", webpackPrefetch: true */ './proposals/proposals' ) ); -const LazyGovernancePropose = React.lazy( +const LazyRejectedProposalsList = React.lazy( () => import( - /* webpackChunkName: "route-governance-propose", webpackPrefetch: true */ './governance/propose' + /* webpackChunkName: "route-governance-proposals", webpackPrefetch: true */ './proposals/rejected' ) ); -const LazyGovernanceProposeNetworkParameter = React.lazy( +const LazyPropose = React.lazy( () => import( - /* webpackChunkName: "route-governance-propose-network-parameter", webpackPrefetch: true */ './governance/propose/network-parameter' + /* webpackChunkName: "route-governance-propose", webpackPrefetch: true */ './proposals/propose' ) ); -const LazyGovernanceProposeNewMarket = React.lazy( +const LazyProposeNetworkParameter = React.lazy( () => import( - /* webpackChunkName: "route-governance-propose-new-market", webpackPrefetch: true */ './governance/propose/new-market' + /* webpackChunkName: "route-governance-propose-network-parameter", webpackPrefetch: true */ './proposals/propose/network-parameter' ) ); -const LazyGovernanceProposeUpdateMarket = React.lazy( +const LazyProposeNewMarket = React.lazy( () => import( - /* webpackChunkName: "route-governance-propose-update-market", webpackPrefetch: true */ './governance/propose/update-market' + /* webpackChunkName: "route-governance-propose-new-market", webpackPrefetch: true */ './proposals/propose/new-market' ) ); -const LazyGovernanceProposeNewAsset = React.lazy( +const LazyProposeUpdateMarket = React.lazy( () => import( - /* webpackChunkName: "route-governance-propose-new-asset", webpackPrefetch: true */ './governance/propose/new-asset' + /* webpackChunkName: "route-governance-propose-update-market", webpackPrefetch: true */ './proposals/propose/update-market' ) ); -const LazyGovernanceProposeUpdateAsset = React.lazy( +const LazyProposeNewAsset = React.lazy( () => import( - /* webpackChunkName: "route-governance-propose-update-asset", webpackPrefetch: true */ './governance/propose/update-asset' + /* webpackChunkName: "route-governance-propose-new-asset", webpackPrefetch: true */ './proposals/propose/new-asset' ) ); -const LazyGovernanceProposeFreeform = React.lazy( +const LazyProposeUpdateAsset = React.lazy( () => import( - /* webpackChunkName: "route-governance-propose-freeform", webpackPrefetch: true */ './governance/propose/freeform' + /* webpackChunkName: "route-governance-propose-update-asset", webpackPrefetch: true */ './proposals/propose/update-asset' ) ); -const LazyGovernanceProposeRaw = React.lazy( +const LazyProposeFreeform = React.lazy( () => import( - /* webpackChunkName: "route-governance-propose-raw", webpackPrefetch: true */ './governance/propose/raw' + /* webpackChunkName: "route-governance-propose-freeform", webpackPrefetch: true */ './proposals/propose/freeform' + ) +); + +const LazyProposeRaw = React.lazy( + () => + import( + /* webpackChunkName: "route-governance-propose-raw", webpackPrefetch: true */ './proposals/propose/raw' ) ); @@ -194,10 +201,6 @@ const LazyWithdrawals = React.lazy( ); const redirects = [ - { - path: Routes.HOME, - element: , - }, { path: Routes.STAKING, element: , @@ -217,39 +220,44 @@ const redirects = [ ]; const routerConfig = [ + { + path: Routes.HOME, + element: , + }, { path: Routes.PROPOSALS, - element: , + element: , children: [ - { index: true, element: }, + { index: true, element: }, { path: 'propose', element: , children: [ - { index: true, element: }, + { index: true, element: }, { path: 'network-parameter', - element: , + element: , }, { path: 'new-market', - element: , + element: , }, { path: 'update-market', - element: , + element: , }, - { path: 'new-asset', element: }, + { path: 'new-asset', element: }, { path: 'update-asset', - element: , + element: , }, - { path: 'freeform', element: }, - { path: 'raw', element: }, + { path: 'freeform', element: }, + { path: 'raw', element: }, ], }, - { path: ':proposalId', element: }, - { path: 'rejected', element: }, + { path: 'proposals', element: }, + { path: ':proposalId', element: }, + { path: 'rejected', element: }, ], }, { diff --git a/apps/token/src/routes/staking/home/epoch-data.tsx b/apps/token/src/routes/staking/home/epoch-data.tsx index 90f7e3345..85295750c 100644 --- a/apps/token/src/routes/staking/home/epoch-data.tsx +++ b/apps/token/src/routes/staking/home/epoch-data.tsx @@ -1,44 +1,21 @@ -import { useEffect } from 'react'; import { AsyncRenderer } from '@vegaprotocol/ui-toolkit'; import { EpochCountdown } from '../../../components/epoch-countdown'; import { useNodesQuery } from './__generated___/Nodes'; import { usePreviousEpochQuery } from '../__generated___/PreviousEpoch'; import { ValidatorTables } from './validator-tables'; +import { useRefreshValidators } from '../../../hooks/use-refresh-validators'; export const EpochData = () => { // errorPolicy due to vegaprotocol/vega issue 5898 const { data, error, loading, refetch } = useNodesQuery(); - const { data: previousEpochData, refetch: previousEpochRefetch } = - usePreviousEpochQuery({ - variables: { - epochId: (Number(data?.epoch.id) - 1).toString(), - }, - }); + const { data: previousEpochData } = usePreviousEpochQuery({ + variables: { + epochId: (Number(data?.epoch.id) - 1).toString(), + }, + skip: !data?.epoch.id, + }); - useEffect(() => { - const epochInterval = setInterval(() => { - if (!data?.epoch.timestamps.expiry) return; - const now = Date.now(); - const expiry = new Date(data.epoch.timestamps.expiry).getTime(); - - if (now > expiry) { - refetch(); - previousEpochRefetch({ - epochId: (Number(data?.epoch.id) - 1).toString(), - }); - clearInterval(epochInterval); - } - }, 10000); - - return () => { - clearInterval(epochInterval); - }; - }, [ - data?.epoch.id, - data?.epoch.timestamps.expiry, - previousEpochRefetch, - refetch, - ]); + useRefreshValidators(data?.epoch.timestamps.expiry, refetch); return ( diff --git a/apps/token/src/routes/staking/node/nodes-container.tsx b/apps/token/src/routes/staking/node/nodes-container.tsx index f5cf1b9df..806b7f087 100644 --- a/apps/token/src/routes/staking/node/nodes-container.tsx +++ b/apps/token/src/routes/staking/node/nodes-container.tsx @@ -8,6 +8,7 @@ import { SplashLoader } from '../../../components/splash-loader'; import { usePreviousEpochQuery } from '../__generated___/PreviousEpoch'; import type { StakingQuery } from './__generated___/Staking'; import type { PreviousEpochQuery } from '../__generated___/PreviousEpoch'; +import { useRefreshValidators } from '../../../hooks/use-refresh-validators'; // TODO should only request a single node. When migrating from deprecated APIs we should address this. @@ -36,24 +37,7 @@ export const NodeContainer = ({ skip: !data?.epoch.id, }); - // @todo - this epoch querying needs to be hoisted as the validator tables rely - // on it too - React.useEffect(() => { - const interval = setInterval(() => { - if (!data?.epoch.timestamps.expiry) return; - const now = Date.now(); - const expiry = new Date(data.epoch.timestamps.expiry).getTime(); - - if (now > expiry) { - refetch(); - clearInterval(interval); - } - }, 1000); - - return () => { - clearInterval(interval); - }; - }, [data?.epoch.timestamps.expiry, refetch]); + useRefreshValidators(data?.epoch.timestamps.expiry, refetch); if (error) { return ( diff --git a/apps/token/src/routes/staking/node/validator-table.tsx b/apps/token/src/routes/staking/node/validator-table.tsx index 0893ddece..c096ca506 100644 --- a/apps/token/src/routes/staking/node/validator-table.tsx +++ b/apps/token/src/routes/staking/node/validator-table.tsx @@ -124,7 +124,7 @@ export const ValidatorTable = ({ -
    +
    {t('validatorTableIntro')}{' '} - + {t('VEGA ADDRESS / PUBLIC KEY')} @@ -167,7 +167,7 @@ export const ValidatorTable = ({ - + {t('STAKED BY OPERATOR')} @@ -205,7 +205,7 @@ export const ValidatorTable = ({ - + {t('OVERSTAKED AMOUNT')} @@ -239,7 +239,7 @@ export const ValidatorTable = ({ - + {t('UNNORMALISED VOTING POWER')} diff --git a/apps/token/src/routes/home/NodeData.graphql b/apps/token/src/routes/token/NodeData.graphql similarity index 100% rename from apps/token/src/routes/home/NodeData.graphql rename to apps/token/src/routes/token/NodeData.graphql diff --git a/apps/token/src/routes/home/__generated__/NodeData.ts b/apps/token/src/routes/token/__generated__/NodeData.ts similarity index 100% rename from apps/token/src/routes/home/__generated__/NodeData.ts rename to apps/token/src/routes/token/__generated__/NodeData.ts diff --git a/apps/token/src/routes/home/__generated___/NodeData.ts b/apps/token/src/routes/token/__generated___/NodeData.ts similarity index 100% rename from apps/token/src/routes/home/__generated___/NodeData.ts rename to apps/token/src/routes/token/__generated___/NodeData.ts diff --git a/apps/token/src/routes/token/index.tsx b/apps/token/src/routes/token/index.tsx new file mode 100644 index 000000000..877102236 --- /dev/null +++ b/apps/token/src/routes/token/index.tsx @@ -0,0 +1,147 @@ +import React from 'react'; +import { Trans, useTranslation } from 'react-i18next'; +import { Link } from 'react-router-dom'; + +import { Heading, SubHeading } from '../../components/heading'; +import { ExternalLinks } from '@vegaprotocol/react-helpers'; +import { useAppState } from '../../contexts/app-state/app-state-context'; +import { useDocumentTitle } from '../../hooks/use-document-title'; +import type { RouteChildProps } from '..'; +import Routes from '../routes'; +import { TokenDetails } from './token-details'; +import { Button } from '@vegaprotocol/ui-toolkit'; +import { toBigNum } from '@vegaprotocol/react-helpers'; +import { useNodeDataQuery } from './__generated___/NodeData'; + +const Home = ({ name }: RouteChildProps) => { + useDocumentTitle(name); + const { t } = useTranslation(); + const { appState } = useAppState(); + const { data } = useNodeDataQuery(); + const totalAssociated = React.useMemo(() => { + return toBigNum(data?.nodeData?.stakedTotal || '0', appState.decimals); + }, [appState.decimals, data?.nodeData?.stakedTotal]); + + return ( + <> + + + + + + +

    + {t( + 'The vesting contract holds VEGA tokens until they have become unlocked.' + )} +

    +

    + + ), + }} + /> +

    +

    + {t( + 'Once unlocked they can be redeemed from the contract so that you can transfer them between wallets.' + )} +

    + + + +
    + + +

    + {t( + 'To use your tokens on the Vega network they need to be associated with a Vega wallet/key.' + )} +

    +

    + {t( + 'This can happen both while held in the vesting contract as well as when redeemed.' + )} +

    +

    + + {t('Get a Vega wallet')} + +

    +

    + + {t('Associate VEGA tokens')} + +

    +
    +
    +
    + + +

    + {t( + 'VEGA token holders can nominate a validator node and receive staking rewards.' + )} +

    +

    + + + +

    +
    +
    +
    + + +

    + {t( + 'VEGA token holders can vote on proposed changes to the network and create proposals.' + )} +

    +

    + + + +

    +
    +
    +
    + + ); +}; + +export default Home; + +export const HomeSection = ({ children }: { children: React.ReactNode }) => { + return
    {children}
    ; +}; diff --git a/apps/token/src/routes/home/token-details/index.tsx b/apps/token/src/routes/token/token-details/index.tsx similarity index 100% rename from apps/token/src/routes/home/token-details/index.tsx rename to apps/token/src/routes/token/token-details/index.tsx diff --git a/apps/token/src/routes/home/token-details/token-details-circulating.test.ts b/apps/token/src/routes/token/token-details/token-details-circulating.test.ts similarity index 100% rename from apps/token/src/routes/home/token-details/token-details-circulating.test.ts rename to apps/token/src/routes/token/token-details/token-details-circulating.test.ts diff --git a/apps/token/src/routes/home/token-details/token-details-circulating.tsx b/apps/token/src/routes/token/token-details/token-details-circulating.tsx similarity index 100% rename from apps/token/src/routes/home/token-details/token-details-circulating.tsx rename to apps/token/src/routes/token/token-details/token-details-circulating.tsx diff --git a/apps/token/src/routes/home/token-details/token-details.tsx b/apps/token/src/routes/token/token-details/token-details.tsx similarity index 100% rename from apps/token/src/routes/home/token-details/token-details.tsx rename to apps/token/src/routes/token/token-details/token-details.tsx diff --git a/libs/ui-toolkit/src/components/rounded-wrapper/rounded-wrapper.tsx b/libs/ui-toolkit/src/components/rounded-wrapper/rounded-wrapper.tsx index 125ae353e..168a40598 100644 --- a/libs/ui-toolkit/src/components/rounded-wrapper/rounded-wrapper.tsx +++ b/libs/ui-toolkit/src/components/rounded-wrapper/rounded-wrapper.tsx @@ -3,20 +3,24 @@ import type { ReactNode } from 'react'; export interface RoundedWrapperProps { children?: ReactNode; + border?: boolean; paddingBottom?: boolean; + marginBottomLarge?: boolean; } export const RoundedWrapper = ({ children, + border = true, paddingBottom = false, + marginBottomLarge = false, }: RoundedWrapperProps) => (
    {children}