feat(governance): new market details improvements (#4106)
This commit is contained in:
parent
597e07608f
commit
cabd99d3ef
@ -596,6 +596,8 @@
|
|||||||
"noPercentage": "No percentage",
|
"noPercentage": "No percentage",
|
||||||
"proposalJson": "Full proposal JSON",
|
"proposalJson": "Full proposal JSON",
|
||||||
"proposalDetails": "Proposal details",
|
"proposalDetails": "Proposal details",
|
||||||
|
"marketSpecification": "Market specification",
|
||||||
|
"viewMarketJson": "View market JSON",
|
||||||
"proposalDescription": "Description",
|
"proposalDescription": "Description",
|
||||||
"currentlySetTo": "Currently expected to ",
|
"currentlySetTo": "Currently expected to ",
|
||||||
"currently": "currently",
|
"currently": "currently",
|
||||||
|
6
apps/governance/src/lib/collapsible-toggle-styles.ts
Normal file
6
apps/governance/src/lib/collapsible-toggle-styles.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import classnames from 'classnames';
|
||||||
|
|
||||||
|
export const collapsibleToggleStyles = (toggleState: boolean) =>
|
||||||
|
classnames('mb-4 transition-transform ease-in-out duration-300', {
|
||||||
|
'rotate-180': toggleState,
|
||||||
|
});
|
@ -1,9 +1,9 @@
|
|||||||
import ReactMarkdown from 'react-markdown';
|
import ReactMarkdown from 'react-markdown';
|
||||||
import classnames from 'classnames';
|
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { Icon, RoundedWrapper } from '@vegaprotocol/ui-toolkit';
|
import { Icon, RoundedWrapper } from '@vegaprotocol/ui-toolkit';
|
||||||
import { SubHeading } from '../../../../components/heading';
|
import { SubHeading } from '../../../../components/heading';
|
||||||
|
import { collapsibleToggleStyles } from '../../../../lib/collapsible-toggle-styles';
|
||||||
|
|
||||||
export const ProposalDescription = ({
|
export const ProposalDescription = ({
|
||||||
description,
|
description,
|
||||||
@ -12,9 +12,6 @@ export const ProposalDescription = ({
|
|||||||
}) => {
|
}) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [showDescription, setShowDescription] = useState(false);
|
const [showDescription, setShowDescription] = useState(false);
|
||||||
const showDescriptionIconClasses = classnames('mb-4', {
|
|
||||||
'rotate-180': showDescription,
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section data-testid="proposal-description">
|
<section data-testid="proposal-description">
|
||||||
@ -24,7 +21,7 @@ export const ProposalDescription = ({
|
|||||||
>
|
>
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
<SubHeading title={t('proposalDescription')} />
|
<SubHeading title={t('proposalDescription')} />
|
||||||
<div className={showDescriptionIconClasses}>
|
<div className={collapsibleToggleStyles(showDescription)}>
|
||||||
<Icon name="chevron-down" size={8} />
|
<Icon name="chevron-down" size={8} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import classnames from 'classnames';
|
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { Icon, SyntaxHighlighter } from '@vegaprotocol/ui-toolkit';
|
import { Icon, SyntaxHighlighter } from '@vegaprotocol/ui-toolkit';
|
||||||
import { SubHeading } from '../../../../components/heading';
|
import { SubHeading } from '../../../../components/heading';
|
||||||
|
import { collapsibleToggleStyles } from '../../../../lib/collapsible-toggle-styles';
|
||||||
import type { ProposalFieldsFragment } from '../../proposals/__generated__/Proposals';
|
import type { ProposalFieldsFragment } from '../../proposals/__generated__/Proposals';
|
||||||
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||||
|
|
||||||
@ -13,9 +13,6 @@ export const ProposalJson = ({
|
|||||||
}) => {
|
}) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const [showDetails, setShowDetails] = useState(false);
|
const [showDetails, setShowDetails] = useState(false);
|
||||||
const showDetailsIconClasses = classnames('mb-4', {
|
|
||||||
'rotate-180': showDetails,
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section data-testid="proposal-json">
|
<section data-testid="proposal-json">
|
||||||
@ -25,7 +22,7 @@ export const ProposalJson = ({
|
|||||||
>
|
>
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
<SubHeading title={t('proposalJson')} />
|
<SubHeading title={t('proposalJson')} />
|
||||||
<div className={showDetailsIconClasses}>
|
<div className={collapsibleToggleStyles(showDetails)}>
|
||||||
<Icon name="chevron-down" size={8} />
|
<Icon name="chevron-down" size={8} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -0,0 +1 @@
|
|||||||
|
export * from './proposal-market-data';
|
@ -0,0 +1,222 @@
|
|||||||
|
import isEqual from 'lodash/isEqual';
|
||||||
|
import { useState } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import {
|
||||||
|
InstrumentInfoPanel,
|
||||||
|
KeyDetailsInfoPanel,
|
||||||
|
LiquidityMonitoringParametersInfoPanel,
|
||||||
|
LiquidityPriceRangeInfoPanel,
|
||||||
|
MetadataInfoPanel,
|
||||||
|
OracleInfoPanel,
|
||||||
|
PriceMonitoringBoundsInfoPanel,
|
||||||
|
RiskFactorsInfoPanel,
|
||||||
|
RiskModelInfoPanel,
|
||||||
|
RiskParametersInfoPanel,
|
||||||
|
SettlementAssetInfoPanel,
|
||||||
|
} from '@vegaprotocol/markets';
|
||||||
|
import {
|
||||||
|
Accordion,
|
||||||
|
AccordionItem,
|
||||||
|
Button,
|
||||||
|
CopyWithTooltip,
|
||||||
|
Dialog,
|
||||||
|
Icon,
|
||||||
|
SyntaxHighlighter,
|
||||||
|
} from '@vegaprotocol/ui-toolkit';
|
||||||
|
import { SubHeading } from '../../../../components/heading';
|
||||||
|
import { collapsibleToggleStyles } from '../../../../lib/collapsible-toggle-styles';
|
||||||
|
import type { MarketInfoWithData } from '@vegaprotocol/markets';
|
||||||
|
import type { DataSourceDefinition } from '@vegaprotocol/types';
|
||||||
|
import { create } from 'zustand';
|
||||||
|
|
||||||
|
type MarketDataDialogState = {
|
||||||
|
isOpen: boolean;
|
||||||
|
open: () => void;
|
||||||
|
close: () => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useMarketDataDialogStore = create<MarketDataDialogState>(
|
||||||
|
(set) => ({
|
||||||
|
isOpen: false,
|
||||||
|
open: () => set({ isOpen: true }),
|
||||||
|
close: () => set({ isOpen: false }),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
export const ProposalMarketData = ({
|
||||||
|
marketData,
|
||||||
|
}: {
|
||||||
|
marketData: MarketInfoWithData;
|
||||||
|
}) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const { isOpen, open, close } = useMarketDataDialogStore();
|
||||||
|
const [showDetails, setShowDetails] = useState(false);
|
||||||
|
|
||||||
|
if (!marketData) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const settlementData = marketData.tradableInstrument.instrument.product
|
||||||
|
.dataSourceSpecForSettlementData.data as DataSourceDefinition;
|
||||||
|
const terminationData = marketData.tradableInstrument.instrument.product
|
||||||
|
.dataSourceSpecForTradingTermination.data as DataSourceDefinition;
|
||||||
|
|
||||||
|
const getSigners = (data: DataSourceDefinition) => {
|
||||||
|
if (data.sourceType.__typename === 'DataSourceDefinitionExternal') {
|
||||||
|
const signers = data.sourceType.sourceType.signers || [];
|
||||||
|
|
||||||
|
return signers.map(({ signer }) => {
|
||||||
|
return (
|
||||||
|
(signer.__typename === 'ETHAddress' && signer.address) ||
|
||||||
|
(signer.__typename === 'PubKey' && signer.key)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section className="relative" data-testid="proposal-market-data">
|
||||||
|
<button
|
||||||
|
onClick={() => setShowDetails(!showDetails)}
|
||||||
|
data-testid="proposal-market-data-toggle"
|
||||||
|
>
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<SubHeading title={t('marketSpecification')} />
|
||||||
|
<div className={collapsibleToggleStyles(showDetails)}>
|
||||||
|
<Icon name="chevron-down" size={8} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
{showDetails && (
|
||||||
|
<>
|
||||||
|
<div className="float-right">
|
||||||
|
<Button onClick={open} data-testid="view-market-json">
|
||||||
|
{t('viewMarketJson')}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
<div className="mb-10">
|
||||||
|
<Accordion>
|
||||||
|
<AccordionItem
|
||||||
|
itemId="key-details"
|
||||||
|
title={t('Key details')}
|
||||||
|
content={<KeyDetailsInfoPanel market={marketData} />}
|
||||||
|
/>
|
||||||
|
<AccordionItem
|
||||||
|
itemId="instrument"
|
||||||
|
title={t('Instrument')}
|
||||||
|
content={<InstrumentInfoPanel market={marketData} />}
|
||||||
|
/>
|
||||||
|
{isEqual(
|
||||||
|
getSigners(settlementData),
|
||||||
|
getSigners(terminationData)
|
||||||
|
) ? (
|
||||||
|
<AccordionItem
|
||||||
|
itemId="oracles"
|
||||||
|
title={t('Oracle')}
|
||||||
|
content={
|
||||||
|
<OracleInfoPanel
|
||||||
|
market={marketData}
|
||||||
|
type="settlementData"
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<AccordionItem
|
||||||
|
itemId="settlement-oracle"
|
||||||
|
title={t('Settlement Oracle')}
|
||||||
|
content={
|
||||||
|
<OracleInfoPanel
|
||||||
|
market={marketData}
|
||||||
|
type="settlementData"
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<AccordionItem
|
||||||
|
itemId="termination-oracle"
|
||||||
|
title={t('Termination Oracle')}
|
||||||
|
content={
|
||||||
|
<OracleInfoPanel market={marketData} type="termination" />
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
<AccordionItem
|
||||||
|
itemId="settlement-asset"
|
||||||
|
title={t('Settlement asset')}
|
||||||
|
content={<SettlementAssetInfoPanel market={marketData} />}
|
||||||
|
/>
|
||||||
|
<AccordionItem
|
||||||
|
itemId="metadata"
|
||||||
|
title={t('Metadata')}
|
||||||
|
content={<MetadataInfoPanel market={marketData} />}
|
||||||
|
/>
|
||||||
|
<AccordionItem
|
||||||
|
itemId="risk-model"
|
||||||
|
title={t('Risk model')}
|
||||||
|
content={<RiskModelInfoPanel market={marketData} />}
|
||||||
|
/>
|
||||||
|
<AccordionItem
|
||||||
|
itemId="risk-parameters"
|
||||||
|
title={t('Risk parameters')}
|
||||||
|
content={<RiskParametersInfoPanel market={marketData} />}
|
||||||
|
/>
|
||||||
|
<AccordionItem
|
||||||
|
itemId="risk-factors"
|
||||||
|
title={t('Risk factors')}
|
||||||
|
content={<RiskFactorsInfoPanel market={marketData} />}
|
||||||
|
/>
|
||||||
|
{(
|
||||||
|
marketData.priceMonitoringSettings?.parameters?.triggers || []
|
||||||
|
).map((_, triggerIndex) => (
|
||||||
|
<AccordionItem
|
||||||
|
itemId={`trigger-${triggerIndex}`}
|
||||||
|
title={t(`Price monitoring bounds ${triggerIndex + 1}`)}
|
||||||
|
content={
|
||||||
|
<PriceMonitoringBoundsInfoPanel
|
||||||
|
market={marketData}
|
||||||
|
triggerIndex={triggerIndex}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
<AccordionItem
|
||||||
|
itemId="liqudity-monitoring-parameters"
|
||||||
|
title={t('Liquidity monitoring parameters')}
|
||||||
|
content={
|
||||||
|
<LiquidityMonitoringParametersInfoPanel market={marketData} />
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<AccordionItem
|
||||||
|
itemId="liquidity-price-range"
|
||||||
|
title={t('Liquidity price range')}
|
||||||
|
content={<LiquidityPriceRangeInfoPanel market={marketData} />}
|
||||||
|
/>
|
||||||
|
</Accordion>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<Dialog
|
||||||
|
title={marketData.tradableInstrument.instrument.code}
|
||||||
|
open={isOpen}
|
||||||
|
onChange={(isOpen) => (isOpen ? open() : close())}
|
||||||
|
size="medium"
|
||||||
|
dataTestId="market-json-dialog"
|
||||||
|
>
|
||||||
|
<CopyWithTooltip text={JSON.stringify(marketData)}>
|
||||||
|
<button className="bg-vega-dark-100 rounded-sm py-2 px-3 mb-4 text-white">
|
||||||
|
<span>
|
||||||
|
<Icon name="duplicate" />
|
||||||
|
</span>
|
||||||
|
<span className="ml-2">Copy</span>
|
||||||
|
</button>
|
||||||
|
</CopyWithTooltip>
|
||||||
|
<SyntaxHighlighter data={marketData} />
|
||||||
|
</Dialog>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
};
|
@ -1,4 +1,3 @@
|
|||||||
import classnames from 'classnames';
|
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import {
|
import {
|
||||||
@ -13,6 +12,7 @@ import { SubHeading } from '../../../../components/heading';
|
|||||||
import { useVoteInformation } from '../../hooks';
|
import { useVoteInformation } from '../../hooks';
|
||||||
import { useAppState } from '../../../../contexts/app-state/app-state-context';
|
import { useAppState } from '../../../../contexts/app-state/app-state-context';
|
||||||
import { ProposalType } from '../proposal/proposal';
|
import { ProposalType } from '../proposal/proposal';
|
||||||
|
import { collapsibleToggleStyles } from '../../../../lib/collapsible-toggle-styles';
|
||||||
import type { ProposalFieldsFragment } from '../../proposals/__generated__/Proposals';
|
import type { ProposalFieldsFragment } from '../../proposals/__generated__/Proposals';
|
||||||
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||||
|
|
||||||
@ -57,10 +57,6 @@ export const ProposalVotesTable = ({
|
|||||||
? t('byTokenVote')
|
? t('byTokenVote')
|
||||||
: t('byLiquidityVote');
|
: t('byLiquidityVote');
|
||||||
|
|
||||||
const showDetailsIconClasses = classnames('mb-4', {
|
|
||||||
'rotate-180': showDetails,
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<button
|
<button
|
||||||
@ -69,7 +65,7 @@ export const ProposalVotesTable = ({
|
|||||||
>
|
>
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
<SubHeading title={t('voteBreakdown')} />
|
<SubHeading title={t('voteBreakdown')} />
|
||||||
<div className={showDetailsIconClasses}>
|
<div className={collapsibleToggleStyles(showDetails)}>
|
||||||
<Icon name="chevron-down" size={8} />
|
<Icon name="chevron-down" size={8} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -29,9 +29,6 @@ jest.mock('../proposal-change-table', () => ({
|
|||||||
jest.mock('../proposal-json', () => ({
|
jest.mock('../proposal-json', () => ({
|
||||||
ProposalJson: () => <div data-testid="proposal-json"></div>,
|
ProposalJson: () => <div data-testid="proposal-json"></div>,
|
||||||
}));
|
}));
|
||||||
jest.mock('../proposal-terms/proposal-terms', () => ({
|
|
||||||
ProposalTerms: () => <div data-testid="proposal-terms"></div>,
|
|
||||||
}));
|
|
||||||
jest.mock('../proposal-votes-table', () => ({
|
jest.mock('../proposal-votes-table', () => ({
|
||||||
ProposalVotesTable: () => <div data-testid="proposal-votes-table"></div>,
|
ProposalVotesTable: () => <div data-testid="proposal-votes-table"></div>,
|
||||||
}));
|
}));
|
||||||
@ -74,7 +71,6 @@ it('renders each section', async () => {
|
|||||||
expect(await screen.findByTestId('proposal-header')).toBeInTheDocument();
|
expect(await screen.findByTestId('proposal-header')).toBeInTheDocument();
|
||||||
expect(screen.getByTestId('proposal-change-table')).toBeInTheDocument();
|
expect(screen.getByTestId('proposal-change-table')).toBeInTheDocument();
|
||||||
expect(screen.getByTestId('proposal-json')).toBeInTheDocument();
|
expect(screen.getByTestId('proposal-json')).toBeInTheDocument();
|
||||||
expect(screen.getByTestId('proposal-terms')).toBeInTheDocument();
|
|
||||||
expect(screen.getByTestId('proposal-votes-table')).toBeInTheDocument();
|
expect(screen.getByTestId('proposal-votes-table')).toBeInTheDocument();
|
||||||
expect(screen.getByTestId('proposal-vote-details')).toBeInTheDocument();
|
expect(screen.getByTestId('proposal-vote-details')).toBeInTheDocument();
|
||||||
expect(screen.queryByTestId('proposal-list-asset')).not.toBeInTheDocument();
|
expect(screen.queryByTestId('proposal-list-asset')).not.toBeInTheDocument();
|
||||||
|
@ -1,22 +1,22 @@
|
|||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { Link } from 'react-router-dom';
|
||||||
import {
|
import {
|
||||||
NetworkParams,
|
NetworkParams,
|
||||||
useNetworkParams,
|
useNetworkParams,
|
||||||
} from '@vegaprotocol/network-parameters';
|
} from '@vegaprotocol/network-parameters';
|
||||||
import { AsyncRenderer, Icon, RoundedWrapper } from '@vegaprotocol/ui-toolkit';
|
import { AsyncRenderer, Icon, RoundedWrapper } from '@vegaprotocol/ui-toolkit';
|
||||||
import { ProposalHeader } from '../proposal-detail-header/proposal-header';
|
import { ProposalHeader } from '../proposal-detail-header/proposal-header';
|
||||||
import type { ProposalFieldsFragment } from '../../proposals/__generated__/Proposals';
|
|
||||||
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
|
||||||
import { ProposalDescription } from '../proposal-description';
|
import { ProposalDescription } from '../proposal-description';
|
||||||
import { ProposalChangeTable } from '../proposal-change-table';
|
import { ProposalChangeTable } from '../proposal-change-table';
|
||||||
import { ProposalJson } from '../proposal-json';
|
import { ProposalJson } from '../proposal-json';
|
||||||
import { ProposalTerms } from '../proposal-terms';
|
|
||||||
import { ProposalVotesTable } from '../proposal-votes-table';
|
import { ProposalVotesTable } from '../proposal-votes-table';
|
||||||
import { VoteDetails } from '../vote-details';
|
import { VoteDetails } from '../vote-details';
|
||||||
import { ListAsset } from '../list-asset';
|
import { ListAsset } from '../list-asset';
|
||||||
import { Link } from 'react-router-dom';
|
|
||||||
import Routes from '../../../routes';
|
import Routes from '../../../routes';
|
||||||
import React from 'react';
|
import { ProposalMarketData } from '../proposal-market-data';
|
||||||
import { useTranslation } from 'react-i18next';
|
import type { ProposalFieldsFragment } from '../../proposals/__generated__/Proposals';
|
||||||
|
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||||
|
import type { MarketInfoWithData } from '@vegaprotocol/markets';
|
||||||
|
|
||||||
export enum ProposalType {
|
export enum ProposalType {
|
||||||
PROPOSAL_NEW_MARKET = 'PROPOSAL_NEW_MARKET',
|
PROPOSAL_NEW_MARKET = 'PROPOSAL_NEW_MARKET',
|
||||||
@ -28,11 +28,16 @@ export enum ProposalType {
|
|||||||
}
|
}
|
||||||
export interface ProposalProps {
|
export interface ProposalProps {
|
||||||
proposal: ProposalFieldsFragment | ProposalQuery['proposal'];
|
proposal: ProposalFieldsFragment | ProposalQuery['proposal'];
|
||||||
|
newMarketData?: MarketInfoWithData | null;
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
restData: any;
|
restData: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Proposal = ({ proposal, restData }: ProposalProps) => {
|
export const Proposal = ({
|
||||||
|
proposal,
|
||||||
|
restData,
|
||||||
|
newMarketData,
|
||||||
|
}: ProposalProps) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { params, loading, error } = useNetworkParams([
|
const { params, loading, error } = useNetworkParams([
|
||||||
NetworkParams.governance_proposal_market_minVoterBalance,
|
NetworkParams.governance_proposal_market_minVoterBalance,
|
||||||
@ -97,51 +102,58 @@ export const Proposal = ({ proposal, restData }: ProposalProps) => {
|
|||||||
</div>
|
</div>
|
||||||
<ProposalHeader proposal={proposal} isListItem={false} />
|
<ProposalHeader proposal={proposal} isListItem={false} />
|
||||||
|
|
||||||
<div className="my-10">
|
<div id="details">
|
||||||
<ProposalChangeTable proposal={proposal} />
|
<div className="my-10">
|
||||||
</div>
|
<ProposalChangeTable proposal={proposal} />
|
||||||
|
</div>
|
||||||
|
|
||||||
{proposal.terms.change.__typename === 'NewAsset' &&
|
{proposal.terms.change.__typename === 'NewAsset' &&
|
||||||
proposal.terms.change.source.__typename === 'ERC20' &&
|
proposal.terms.change.source.__typename === 'ERC20' &&
|
||||||
proposal.id ? (
|
proposal.id ? (
|
||||||
<ListAsset
|
<ListAsset
|
||||||
assetId={proposal.id}
|
assetId={proposal.id}
|
||||||
withdrawalThreshold={proposal.terms.change.source.withdrawThreshold}
|
withdrawalThreshold={
|
||||||
lifetimeLimit={proposal.terms.change.source.lifetimeLimit}
|
proposal.terms.change.source.withdrawThreshold
|
||||||
/>
|
}
|
||||||
) : null}
|
lifetimeLimit={proposal.terms.change.source.lifetimeLimit}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
|
|
||||||
<div className="mb-4">
|
<div className="mb-4">
|
||||||
<ProposalDescription description={proposal.rationale.description} />
|
<ProposalDescription description={proposal.rationale.description} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{proposal.terms.change.__typename !== 'NewMarket' &&
|
{newMarketData && (
|
||||||
proposal.terms.change.__typename !== 'UpdateMarket' &&
|
|
||||||
proposal.terms.change.__typename !== 'NewFreeform' && (
|
|
||||||
<div className="mb-4">
|
<div className="mb-4">
|
||||||
<ProposalTerms data={proposal.terms} />
|
<ProposalMarketData marketData={newMarketData} />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className="mb-6">
|
<div className="mb-6">
|
||||||
<ProposalJson proposal={restData?.data?.proposal} />
|
<ProposalJson proposal={restData?.data?.proposal} />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="mb-10">
|
<div id="voting">
|
||||||
<RoundedWrapper paddingBottom={true}>
|
<div className="mb-10">
|
||||||
<VoteDetails
|
<RoundedWrapper paddingBottom={true}>
|
||||||
|
<VoteDetails
|
||||||
|
proposal={proposal}
|
||||||
|
proposalType={proposalType}
|
||||||
|
minVoterBalance={minVoterBalance}
|
||||||
|
spamProtectionMinTokens={
|
||||||
|
params?.spam_protection_voting_min_tokens
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</RoundedWrapper>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="mb-4">
|
||||||
|
<ProposalVotesTable
|
||||||
proposal={proposal}
|
proposal={proposal}
|
||||||
proposalType={proposalType}
|
proposalType={proposalType}
|
||||||
minVoterBalance={minVoterBalance}
|
|
||||||
spamProtectionMinTokens={
|
|
||||||
params?.spam_protection_voting_min_tokens
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
</RoundedWrapper>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="mb-4">
|
|
||||||
<ProposalVotesTable proposal={proposal} proposalType={proposalType} />
|
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</AsyncRenderer>
|
</AsyncRenderer>
|
||||||
|
@ -7,6 +7,8 @@ import { ProposalNotFound } from '../components/proposal-not-found';
|
|||||||
import { useProposalQuery } from './__generated__/Proposal';
|
import { useProposalQuery } from './__generated__/Proposal';
|
||||||
import { useFetch } from '@vegaprotocol/react-helpers';
|
import { useFetch } from '@vegaprotocol/react-helpers';
|
||||||
import { ENV } from '../../../config';
|
import { ENV } from '../../../config';
|
||||||
|
import { useDataProvider } from '@vegaprotocol/data-provider';
|
||||||
|
import { marketInfoWithDataProvider } from '@vegaprotocol/markets';
|
||||||
|
|
||||||
export const ProposalContainer = () => {
|
export const ProposalContainer = () => {
|
||||||
const params = useParams<{ proposalId: string }>();
|
const params = useParams<{ proposalId: string }>();
|
||||||
@ -20,15 +22,36 @@ export const ProposalContainer = () => {
|
|||||||
skip: !params.proposalId,
|
skip: !params.proposalId,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const {
|
||||||
|
data: newMarketData,
|
||||||
|
loading: newMarketLoading,
|
||||||
|
error: newMarketError,
|
||||||
|
} = useDataProvider({
|
||||||
|
dataProvider: marketInfoWithDataProvider,
|
||||||
|
skipUpdates: true,
|
||||||
|
variables: {
|
||||||
|
marketId: data?.proposal?.id || '',
|
||||||
|
skip: !data?.proposal?.id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const interval = setInterval(refetch, 1000);
|
const interval = setInterval(refetch, 2000);
|
||||||
return () => clearInterval(interval);
|
return () => clearInterval(interval);
|
||||||
}, [refetch]);
|
}, [refetch]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AsyncRenderer loading={loading} error={error} data={data}>
|
<AsyncRenderer
|
||||||
|
loading={loading || newMarketLoading}
|
||||||
|
error={error || newMarketError}
|
||||||
|
data={newMarketData ? { newMarketData, data } : data}
|
||||||
|
>
|
||||||
{data?.proposal ? (
|
{data?.proposal ? (
|
||||||
<Proposal proposal={data.proposal} restData={restData} />
|
<Proposal
|
||||||
|
proposal={data.proposal}
|
||||||
|
restData={restData}
|
||||||
|
newMarketData={newMarketData}
|
||||||
|
/>
|
||||||
) : (
|
) : (
|
||||||
<ProposalNotFound />
|
<ProposalNotFound />
|
||||||
)}
|
)}
|
||||||
|
Loading…
Reference in New Issue
Block a user