chore: proposal schema change (1057) (#1169)
* chore: moved proposal queries to lib/governance * chore: used new rationale title and description * chore: addressed PR comments, refactored * chore: moved proposal queries to lib/governance * chore: used new rationale title and description * chore: addressed PR comments, refactored * fix: dropped s after merge * fix: fixed lodash imports
This commit is contained in:
parent
9de3683bf3
commit
d2791f2e59
@ -9,7 +9,23 @@ import { ProposalState, ProposalRejectionReason, VoteValue } from "@vegaprotocol
|
||||
// GraphQL query operation: ProposalsQuery
|
||||
// ====================================================
|
||||
|
||||
export interface ProposalsQuery_proposals_party {
|
||||
export interface ProposalsQuery_proposalsConnection_edges_node_rationale {
|
||||
__typename: "ProposalRationale";
|
||||
/**
|
||||
* Title to be used to give a short description of the proposal in lists.
|
||||
* This is to be between 0 and 100 unicode characters.
|
||||
* This is mandatory for all proposals.
|
||||
*/
|
||||
title: string;
|
||||
/**
|
||||
* Description to show a short title / something in case the link goes offline.
|
||||
* This is to be between 0 and 20k unicode characters.
|
||||
* This is mandatory for all proposals.
|
||||
*/
|
||||
description: string;
|
||||
}
|
||||
|
||||
export interface ProposalsQuery_proposalsConnection_edges_node_party {
|
||||
__typename: "Party";
|
||||
/**
|
||||
* Party identifier
|
||||
@ -17,11 +33,11 @@ export interface ProposalsQuery_proposals_party {
|
||||
id: string;
|
||||
}
|
||||
|
||||
export interface ProposalsQuery_proposals_terms_change_UpdateAsset {
|
||||
export interface ProposalsQuery_proposalsConnection_edges_node_terms_change_UpdateAsset {
|
||||
__typename: "UpdateAsset" | "NewFreeform";
|
||||
}
|
||||
|
||||
export interface ProposalsQuery_proposals_terms_change_NewMarket_instrument {
|
||||
export interface ProposalsQuery_proposalsConnection_edges_node_terms_change_NewMarket_instrument {
|
||||
__typename: "InstrumentConfiguration";
|
||||
/**
|
||||
* Full and fairly descriptive name for the instrument
|
||||
@ -29,20 +45,20 @@ export interface ProposalsQuery_proposals_terms_change_NewMarket_instrument {
|
||||
name: string;
|
||||
}
|
||||
|
||||
export interface ProposalsQuery_proposals_terms_change_NewMarket {
|
||||
export interface ProposalsQuery_proposalsConnection_edges_node_terms_change_NewMarket {
|
||||
__typename: "NewMarket";
|
||||
/**
|
||||
* New market instrument configuration
|
||||
*/
|
||||
instrument: ProposalsQuery_proposals_terms_change_NewMarket_instrument;
|
||||
instrument: ProposalsQuery_proposalsConnection_edges_node_terms_change_NewMarket_instrument;
|
||||
}
|
||||
|
||||
export interface ProposalsQuery_proposals_terms_change_UpdateMarket {
|
||||
export interface ProposalsQuery_proposalsConnection_edges_node_terms_change_UpdateMarket {
|
||||
__typename: "UpdateMarket";
|
||||
marketId: string;
|
||||
}
|
||||
|
||||
export interface ProposalsQuery_proposals_terms_change_NewAsset_source_BuiltinAsset {
|
||||
export interface ProposalsQuery_proposalsConnection_edges_node_terms_change_NewAsset_source_BuiltinAsset {
|
||||
__typename: "BuiltinAsset";
|
||||
/**
|
||||
* Maximum amount that can be requested by a party through the built-in asset faucet at a time
|
||||
@ -50,7 +66,7 @@ export interface ProposalsQuery_proposals_terms_change_NewAsset_source_BuiltinAs
|
||||
maxFaucetAmountMint: string;
|
||||
}
|
||||
|
||||
export interface ProposalsQuery_proposals_terms_change_NewAsset_source_ERC20 {
|
||||
export interface ProposalsQuery_proposalsConnection_edges_node_terms_change_NewAsset_source_ERC20 {
|
||||
__typename: "ERC20";
|
||||
/**
|
||||
* The address of the ERC20 contract
|
||||
@ -58,9 +74,9 @@ export interface ProposalsQuery_proposals_terms_change_NewAsset_source_ERC20 {
|
||||
contractAddress: string;
|
||||
}
|
||||
|
||||
export type ProposalsQuery_proposals_terms_change_NewAsset_source = ProposalsQuery_proposals_terms_change_NewAsset_source_BuiltinAsset | ProposalsQuery_proposals_terms_change_NewAsset_source_ERC20;
|
||||
export type ProposalsQuery_proposalsConnection_edges_node_terms_change_NewAsset_source = ProposalsQuery_proposalsConnection_edges_node_terms_change_NewAsset_source_BuiltinAsset | ProposalsQuery_proposalsConnection_edges_node_terms_change_NewAsset_source_ERC20;
|
||||
|
||||
export interface ProposalsQuery_proposals_terms_change_NewAsset {
|
||||
export interface ProposalsQuery_proposalsConnection_edges_node_terms_change_NewAsset {
|
||||
__typename: "NewAsset";
|
||||
/**
|
||||
* The symbol of the asset (e.g: GBP)
|
||||
@ -69,10 +85,10 @@ export interface ProposalsQuery_proposals_terms_change_NewAsset {
|
||||
/**
|
||||
* The source of the new asset
|
||||
*/
|
||||
source: ProposalsQuery_proposals_terms_change_NewAsset_source;
|
||||
source: ProposalsQuery_proposalsConnection_edges_node_terms_change_NewAsset_source;
|
||||
}
|
||||
|
||||
export interface ProposalsQuery_proposals_terms_change_UpdateNetworkParameter_networkParameter {
|
||||
export interface ProposalsQuery_proposalsConnection_edges_node_terms_change_UpdateNetworkParameter_networkParameter {
|
||||
__typename: "NetworkParameter";
|
||||
/**
|
||||
* The name of the network parameter
|
||||
@ -84,14 +100,14 @@ export interface ProposalsQuery_proposals_terms_change_UpdateNetworkParameter_ne
|
||||
value: string;
|
||||
}
|
||||
|
||||
export interface ProposalsQuery_proposals_terms_change_UpdateNetworkParameter {
|
||||
export interface ProposalsQuery_proposalsConnection_edges_node_terms_change_UpdateNetworkParameter {
|
||||
__typename: "UpdateNetworkParameter";
|
||||
networkParameter: ProposalsQuery_proposals_terms_change_UpdateNetworkParameter_networkParameter;
|
||||
networkParameter: ProposalsQuery_proposalsConnection_edges_node_terms_change_UpdateNetworkParameter_networkParameter;
|
||||
}
|
||||
|
||||
export type ProposalsQuery_proposals_terms_change = ProposalsQuery_proposals_terms_change_UpdateAsset | ProposalsQuery_proposals_terms_change_NewMarket | ProposalsQuery_proposals_terms_change_UpdateMarket | ProposalsQuery_proposals_terms_change_NewAsset | ProposalsQuery_proposals_terms_change_UpdateNetworkParameter;
|
||||
export type ProposalsQuery_proposalsConnection_edges_node_terms_change = ProposalsQuery_proposalsConnection_edges_node_terms_change_UpdateAsset | ProposalsQuery_proposalsConnection_edges_node_terms_change_NewMarket | ProposalsQuery_proposalsConnection_edges_node_terms_change_UpdateMarket | ProposalsQuery_proposalsConnection_edges_node_terms_change_NewAsset | ProposalsQuery_proposalsConnection_edges_node_terms_change_UpdateNetworkParameter;
|
||||
|
||||
export interface ProposalsQuery_proposals_terms {
|
||||
export interface ProposalsQuery_proposalsConnection_edges_node_terms {
|
||||
__typename: "ProposalTerms";
|
||||
/**
|
||||
* RFC3339Nano time and date when voting closes for this proposal.
|
||||
@ -107,18 +123,18 @@ export interface ProposalsQuery_proposals_terms {
|
||||
/**
|
||||
* Actual change being introduced by the proposal - action the proposal triggers if passed and enacted.
|
||||
*/
|
||||
change: ProposalsQuery_proposals_terms_change;
|
||||
change: ProposalsQuery_proposalsConnection_edges_node_terms_change;
|
||||
}
|
||||
|
||||
export interface ProposalsQuery_proposals_votes_yes_votes_party_stake {
|
||||
__typename: "PartyStake";
|
||||
export interface ProposalsQuery_proposalsConnection_edges_node_votes_yes_votes_party_stakingSummary {
|
||||
__typename: "StakingSummary";
|
||||
/**
|
||||
* The stake currently available for the party
|
||||
*/
|
||||
currentStakeAvailable: string;
|
||||
}
|
||||
|
||||
export interface ProposalsQuery_proposals_votes_yes_votes_party {
|
||||
export interface ProposalsQuery_proposalsConnection_edges_node_votes_yes_votes_party {
|
||||
__typename: "Party";
|
||||
/**
|
||||
* Party identifier
|
||||
@ -127,10 +143,10 @@ export interface ProposalsQuery_proposals_votes_yes_votes_party {
|
||||
/**
|
||||
* The staking information for this Party
|
||||
*/
|
||||
stake: ProposalsQuery_proposals_votes_yes_votes_party_stake;
|
||||
stakingSummary: ProposalsQuery_proposalsConnection_edges_node_votes_yes_votes_party_stakingSummary;
|
||||
}
|
||||
|
||||
export interface ProposalsQuery_proposals_votes_yes_votes {
|
||||
export interface ProposalsQuery_proposalsConnection_edges_node_votes_yes_votes {
|
||||
__typename: "Vote";
|
||||
/**
|
||||
* The vote value cast
|
||||
@ -139,14 +155,14 @@ export interface ProposalsQuery_proposals_votes_yes_votes {
|
||||
/**
|
||||
* The party casting the vote
|
||||
*/
|
||||
party: ProposalsQuery_proposals_votes_yes_votes_party;
|
||||
party: ProposalsQuery_proposalsConnection_edges_node_votes_yes_votes_party;
|
||||
/**
|
||||
* RFC3339Nano time and date when the vote reached Vega network
|
||||
*/
|
||||
datetime: string;
|
||||
}
|
||||
|
||||
export interface ProposalsQuery_proposals_votes_yes {
|
||||
export interface ProposalsQuery_proposalsConnection_edges_node_votes_yes {
|
||||
__typename: "ProposalVoteSide";
|
||||
/**
|
||||
* Total number of governance tokens from the votes cast for this side
|
||||
@ -159,18 +175,18 @@ export interface ProposalsQuery_proposals_votes_yes {
|
||||
/**
|
||||
* All votes cast for this side
|
||||
*/
|
||||
votes: ProposalsQuery_proposals_votes_yes_votes[] | null;
|
||||
votes: ProposalsQuery_proposalsConnection_edges_node_votes_yes_votes[] | null;
|
||||
}
|
||||
|
||||
export interface ProposalsQuery_proposals_votes_no_votes_party_stake {
|
||||
__typename: "PartyStake";
|
||||
export interface ProposalsQuery_proposalsConnection_edges_node_votes_no_votes_party_stakingSummary {
|
||||
__typename: "StakingSummary";
|
||||
/**
|
||||
* The stake currently available for the party
|
||||
*/
|
||||
currentStakeAvailable: string;
|
||||
}
|
||||
|
||||
export interface ProposalsQuery_proposals_votes_no_votes_party {
|
||||
export interface ProposalsQuery_proposalsConnection_edges_node_votes_no_votes_party {
|
||||
__typename: "Party";
|
||||
/**
|
||||
* Party identifier
|
||||
@ -179,10 +195,10 @@ export interface ProposalsQuery_proposals_votes_no_votes_party {
|
||||
/**
|
||||
* The staking information for this Party
|
||||
*/
|
||||
stake: ProposalsQuery_proposals_votes_no_votes_party_stake;
|
||||
stakingSummary: ProposalsQuery_proposalsConnection_edges_node_votes_no_votes_party_stakingSummary;
|
||||
}
|
||||
|
||||
export interface ProposalsQuery_proposals_votes_no_votes {
|
||||
export interface ProposalsQuery_proposalsConnection_edges_node_votes_no_votes {
|
||||
__typename: "Vote";
|
||||
/**
|
||||
* The vote value cast
|
||||
@ -191,14 +207,14 @@ export interface ProposalsQuery_proposals_votes_no_votes {
|
||||
/**
|
||||
* The party casting the vote
|
||||
*/
|
||||
party: ProposalsQuery_proposals_votes_no_votes_party;
|
||||
party: ProposalsQuery_proposalsConnection_edges_node_votes_no_votes_party;
|
||||
/**
|
||||
* RFC3339Nano time and date when the vote reached Vega network
|
||||
*/
|
||||
datetime: string;
|
||||
}
|
||||
|
||||
export interface ProposalsQuery_proposals_votes_no {
|
||||
export interface ProposalsQuery_proposalsConnection_edges_node_votes_no {
|
||||
__typename: "ProposalVoteSide";
|
||||
/**
|
||||
* Total number of governance tokens from the votes cast for this side
|
||||
@ -211,27 +227,31 @@ export interface ProposalsQuery_proposals_votes_no {
|
||||
/**
|
||||
* All votes cast for this side
|
||||
*/
|
||||
votes: ProposalsQuery_proposals_votes_no_votes[] | null;
|
||||
votes: ProposalsQuery_proposalsConnection_edges_node_votes_no_votes[] | null;
|
||||
}
|
||||
|
||||
export interface ProposalsQuery_proposals_votes {
|
||||
export interface ProposalsQuery_proposalsConnection_edges_node_votes {
|
||||
__typename: "ProposalVotes";
|
||||
/**
|
||||
* Yes votes cast for this proposal
|
||||
*/
|
||||
yes: ProposalsQuery_proposals_votes_yes;
|
||||
yes: ProposalsQuery_proposalsConnection_edges_node_votes_yes;
|
||||
/**
|
||||
* No votes cast for this proposal
|
||||
*/
|
||||
no: ProposalsQuery_proposals_votes_no;
|
||||
no: ProposalsQuery_proposalsConnection_edges_node_votes_no;
|
||||
}
|
||||
|
||||
export interface ProposalsQuery_proposals {
|
||||
export interface ProposalsQuery_proposalsConnection_edges_node {
|
||||
__typename: "Proposal";
|
||||
/**
|
||||
* Proposal ID that is filled by Vega once proposal reaches the network
|
||||
*/
|
||||
id: string | null;
|
||||
/**
|
||||
* Rationale behind the proposal
|
||||
*/
|
||||
rationale: ProposalsQuery_proposalsConnection_edges_node_rationale;
|
||||
/**
|
||||
* A UUID reference to aid tracking proposals on Vega
|
||||
*/
|
||||
@ -251,20 +271,36 @@ export interface ProposalsQuery_proposals {
|
||||
/**
|
||||
* Party that prepared the proposal
|
||||
*/
|
||||
party: ProposalsQuery_proposals_party;
|
||||
party: ProposalsQuery_proposalsConnection_edges_node_party;
|
||||
/**
|
||||
* Terms of the proposal
|
||||
*/
|
||||
terms: ProposalsQuery_proposals_terms;
|
||||
terms: ProposalsQuery_proposalsConnection_edges_node_terms;
|
||||
/**
|
||||
* Votes cast for this proposal
|
||||
*/
|
||||
votes: ProposalsQuery_proposals_votes;
|
||||
votes: ProposalsQuery_proposalsConnection_edges_node_votes;
|
||||
}
|
||||
|
||||
export interface ProposalsQuery_proposalsConnection_edges {
|
||||
__typename: "ProposalEdge";
|
||||
/**
|
||||
* The proposal data
|
||||
*/
|
||||
node: ProposalsQuery_proposalsConnection_edges_node;
|
||||
}
|
||||
|
||||
export interface ProposalsQuery_proposalsConnection {
|
||||
__typename: "ProposalsConnection";
|
||||
/**
|
||||
* List of proposals available for the connection
|
||||
*/
|
||||
edges: (ProposalsQuery_proposalsConnection_edges | null)[] | null;
|
||||
}
|
||||
|
||||
export interface ProposalsQuery {
|
||||
/**
|
||||
* All governance proposals in the Vega network
|
||||
*/
|
||||
proposals: ProposalsQuery_proposals[] | null;
|
||||
proposalsConnection: ProposalsQuery_proposalsConnection;
|
||||
}
|
||||
|
@ -4,95 +4,90 @@ import React from 'react';
|
||||
import { RouteTitle } from '../../components/route-title';
|
||||
import { SubHeading } from '../../components/sub-heading';
|
||||
import { SyntaxHighlighter } from '@vegaprotocol/ui-toolkit';
|
||||
import { getProposals } from '@vegaprotocol/governance';
|
||||
import type {
|
||||
ProposalsQuery,
|
||||
ProposalsQuery_proposals_terms_change,
|
||||
ProposalsQuery_proposalsConnection_edges_node,
|
||||
} from './__generated__/ProposalsQuery';
|
||||
|
||||
export function getProposalName(change: ProposalsQuery_proposals_terms_change) {
|
||||
if (change.__typename === 'NewAsset') {
|
||||
return t(`New asset: ${change.symbol}`);
|
||||
} else if (change.__typename === 'NewMarket') {
|
||||
return t(`New market: ${change.instrument.name}`);
|
||||
} else if (change.__typename === 'UpdateMarket') {
|
||||
return t(`Update market: ${change.marketId}`);
|
||||
} else if (change.__typename === 'UpdateNetworkParameter') {
|
||||
return t(`Update network: ${change.networkParameter.key}`);
|
||||
}
|
||||
|
||||
return t('Unknown proposal');
|
||||
}
|
||||
|
||||
const PROPOSAL_QUERY = gql`
|
||||
const PROPOSALS_QUERY = gql`
|
||||
query ProposalsQuery {
|
||||
proposals {
|
||||
id
|
||||
reference
|
||||
state
|
||||
datetime
|
||||
rejectionReason
|
||||
party {
|
||||
id
|
||||
}
|
||||
terms {
|
||||
closingDatetime
|
||||
enactmentDatetime
|
||||
change {
|
||||
... on NewMarket {
|
||||
instrument {
|
||||
name
|
||||
}
|
||||
proposalsConnection {
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
rationale {
|
||||
title
|
||||
description
|
||||
}
|
||||
... on UpdateMarket {
|
||||
marketId
|
||||
reference
|
||||
state
|
||||
datetime
|
||||
rejectionReason
|
||||
party {
|
||||
id
|
||||
}
|
||||
... on NewAsset {
|
||||
__typename
|
||||
symbol
|
||||
source {
|
||||
... on BuiltinAsset {
|
||||
maxFaucetAmountMint
|
||||
terms {
|
||||
closingDatetime
|
||||
enactmentDatetime
|
||||
change {
|
||||
... on NewMarket {
|
||||
instrument {
|
||||
name
|
||||
}
|
||||
}
|
||||
... on ERC20 {
|
||||
contractAddress
|
||||
... on UpdateMarket {
|
||||
marketId
|
||||
}
|
||||
... on NewAsset {
|
||||
__typename
|
||||
symbol
|
||||
source {
|
||||
... on BuiltinAsset {
|
||||
maxFaucetAmountMint
|
||||
}
|
||||
... on ERC20 {
|
||||
contractAddress
|
||||
}
|
||||
}
|
||||
}
|
||||
... on UpdateNetworkParameter {
|
||||
networkParameter {
|
||||
key
|
||||
value
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
... on UpdateNetworkParameter {
|
||||
networkParameter {
|
||||
key
|
||||
value
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
votes {
|
||||
yes {
|
||||
totalTokens
|
||||
totalNumber
|
||||
votes {
|
||||
value
|
||||
party {
|
||||
id
|
||||
stake {
|
||||
currentStakeAvailable
|
||||
yes {
|
||||
totalTokens
|
||||
totalNumber
|
||||
votes {
|
||||
value
|
||||
party {
|
||||
id
|
||||
stakingSummary {
|
||||
currentStakeAvailable
|
||||
}
|
||||
}
|
||||
datetime
|
||||
}
|
||||
}
|
||||
datetime
|
||||
}
|
||||
}
|
||||
no {
|
||||
totalTokens
|
||||
totalNumber
|
||||
votes {
|
||||
value
|
||||
party {
|
||||
id
|
||||
stake {
|
||||
currentStakeAvailable
|
||||
no {
|
||||
totalTokens
|
||||
totalNumber
|
||||
votes {
|
||||
value
|
||||
party {
|
||||
id
|
||||
stakingSummary {
|
||||
currentStakeAvailable
|
||||
}
|
||||
}
|
||||
datetime
|
||||
}
|
||||
}
|
||||
datetime
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -101,9 +96,12 @@ const PROPOSAL_QUERY = gql`
|
||||
`;
|
||||
|
||||
const Governance = () => {
|
||||
const { data } = useQuery<ProposalsQuery>(PROPOSAL_QUERY, {
|
||||
const { data } = useQuery<ProposalsQuery>(PROPOSALS_QUERY, {
|
||||
errorPolicy: 'ignore',
|
||||
});
|
||||
const proposals = getProposals(
|
||||
data
|
||||
) as ProposalsQuery_proposalsConnection_edges_node[];
|
||||
|
||||
if (!data) return null;
|
||||
return (
|
||||
@ -111,9 +109,11 @@ const Governance = () => {
|
||||
<RouteTitle data-testid="governance-header">
|
||||
{t('Governance Proposals')}
|
||||
</RouteTitle>
|
||||
{data.proposals?.map((p) => (
|
||||
{proposals.map((p) => (
|
||||
<React.Fragment key={p.id}>
|
||||
<SubHeading>{getProposalName(p.terms.change)}</SubHeading>
|
||||
<SubHeading>
|
||||
{p.rationale.title || p.rationale.description}
|
||||
</SubHeading>
|
||||
<SyntaxHighlighter data={p} />
|
||||
</React.Fragment>
|
||||
))}
|
||||
|
@ -9,6 +9,22 @@ import { ProposalState, ProposalRejectionReason, VoteValue } from "@vegaprotocol
|
||||
// GraphQL fragment: ProposalFields
|
||||
// ====================================================
|
||||
|
||||
export interface ProposalFields_rationale {
|
||||
__typename: "ProposalRationale";
|
||||
/**
|
||||
* Title to be used to give a short description of the proposal in lists.
|
||||
* This is to be between 0 and 100 unicode characters.
|
||||
* This is mandatory for all proposals.
|
||||
*/
|
||||
title: string;
|
||||
/**
|
||||
* Description to show a short title / something in case the link goes offline.
|
||||
* This is to be between 0 and 20k unicode characters.
|
||||
* This is mandatory for all proposals.
|
||||
*/
|
||||
description: string;
|
||||
}
|
||||
|
||||
export interface ProposalFields_party {
|
||||
__typename: "Party";
|
||||
/**
|
||||
@ -55,14 +71,6 @@ export interface ProposalFields_terms_change_NewMarket_instrument {
|
||||
|
||||
export interface ProposalFields_terms_change_NewMarket {
|
||||
__typename: "NewMarket";
|
||||
/**
|
||||
* Decimal places used for the new market, sets the smallest price increment on the book
|
||||
*/
|
||||
decimalPlaces: number;
|
||||
/**
|
||||
* Metadata for this instrument, tags
|
||||
*/
|
||||
metadata: string[] | null;
|
||||
/**
|
||||
* New market instrument configuration
|
||||
*/
|
||||
@ -146,8 +154,8 @@ export interface ProposalFields_terms {
|
||||
change: ProposalFields_terms_change;
|
||||
}
|
||||
|
||||
export interface ProposalFields_votes_yes_votes_party_stake {
|
||||
__typename: "PartyStake";
|
||||
export interface ProposalFields_votes_yes_votes_party_stakingSummary {
|
||||
__typename: "StakingSummary";
|
||||
/**
|
||||
* The stake currently available for the party
|
||||
*/
|
||||
@ -163,7 +171,7 @@ export interface ProposalFields_votes_yes_votes_party {
|
||||
/**
|
||||
* The staking information for this Party
|
||||
*/
|
||||
stake: ProposalFields_votes_yes_votes_party_stake;
|
||||
stakingSummary: ProposalFields_votes_yes_votes_party_stakingSummary;
|
||||
}
|
||||
|
||||
export interface ProposalFields_votes_yes_votes {
|
||||
@ -198,8 +206,8 @@ export interface ProposalFields_votes_yes {
|
||||
votes: ProposalFields_votes_yes_votes[] | null;
|
||||
}
|
||||
|
||||
export interface ProposalFields_votes_no_votes_party_stake {
|
||||
__typename: "PartyStake";
|
||||
export interface ProposalFields_votes_no_votes_party_stakingSummary {
|
||||
__typename: "StakingSummary";
|
||||
/**
|
||||
* The stake currently available for the party
|
||||
*/
|
||||
@ -215,7 +223,7 @@ export interface ProposalFields_votes_no_votes_party {
|
||||
/**
|
||||
* The staking information for this Party
|
||||
*/
|
||||
stake: ProposalFields_votes_no_votes_party_stake;
|
||||
stakingSummary: ProposalFields_votes_no_votes_party_stakingSummary;
|
||||
}
|
||||
|
||||
export interface ProposalFields_votes_no_votes {
|
||||
@ -268,6 +276,10 @@ export interface ProposalFields {
|
||||
* Proposal ID that is filled by Vega once proposal reaches the network
|
||||
*/
|
||||
id: string | null;
|
||||
/**
|
||||
* Rationale behind the proposal
|
||||
*/
|
||||
rationale: ProposalFields_rationale;
|
||||
/**
|
||||
* A UUID reference to aid tracking proposals on Vega
|
||||
*/
|
||||
@ -284,14 +296,14 @@ export interface ProposalFields {
|
||||
* Reason for the proposal to be rejected by the core
|
||||
*/
|
||||
rejectionReason: ProposalRejectionReason | null;
|
||||
/**
|
||||
* Error details of the rejectionReason
|
||||
*/
|
||||
errorDetails: string | null;
|
||||
/**
|
||||
* Party that prepared the proposal
|
||||
*/
|
||||
party: ProposalFields_party;
|
||||
/**
|
||||
* Error details of the rejectionReason
|
||||
*/
|
||||
errorDetails: string | null;
|
||||
/**
|
||||
* Terms of the proposal
|
||||
*/
|
||||
|
@ -1,10 +1,10 @@
|
||||
import { ProposalState } from '@vegaprotocol/types';
|
||||
import type { Proposals_proposals } from '../../proposals/__generated__/Proposals';
|
||||
import type { ProposalFields } from '../../__generated__/ProposalFields';
|
||||
|
||||
export const CurrentProposalState = ({
|
||||
proposal,
|
||||
}: {
|
||||
proposal: Proposals_proposals;
|
||||
proposal: ProposalFields;
|
||||
}) => {
|
||||
const { state } = proposal;
|
||||
let className = 'text-white';
|
||||
|
@ -4,7 +4,7 @@ import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { ProposalState } from '@vegaprotocol/types';
|
||||
import { useVoteInformation } from '../../hooks';
|
||||
import type { Proposals_proposals } from '../../proposals/__generated__/Proposals';
|
||||
import type { ProposalFields } from '../../__generated__/ProposalFields';
|
||||
|
||||
export const StatusPass = ({ children }: { children: React.ReactNode }) => (
|
||||
<span className="text-vega-green">{children}</span>
|
||||
@ -17,7 +17,7 @@ export const StatusFail = ({ children }: { children: React.ReactNode }) => (
|
||||
export const CurrentProposalStatus = ({
|
||||
proposal,
|
||||
}: {
|
||||
proposal: Proposals_proposals;
|
||||
proposal: ProposalFields;
|
||||
}) => {
|
||||
const { willPass, majorityMet, participationMet } = useVoteInformation({
|
||||
proposal,
|
||||
|
@ -3,11 +3,11 @@ import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { KeyValueTable, KeyValueTableRow } from '@vegaprotocol/ui-toolkit';
|
||||
import { DATE_FORMAT_DETAILED } from '../../../../lib/date-formats';
|
||||
import type { Proposals_proposals } from '../../proposals/__generated__/Proposals';
|
||||
import { CurrentProposalState } from '../current-proposal-state';
|
||||
import type { ProposalFields } from '../../__generated__/ProposalFields';
|
||||
|
||||
interface ProposalChangeTableProps {
|
||||
proposal: Proposals_proposals;
|
||||
proposal: ProposalFields;
|
||||
}
|
||||
|
||||
export const ProposalChangeTable = ({ proposal }: ProposalChangeTableProps) => {
|
||||
|
@ -1,9 +1,9 @@
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { generateProposal } from '../../test-helpers/generate-proposals';
|
||||
import { ProposalHeader } from './proposal-header';
|
||||
import type { Proposals_proposals } from '../../proposals/__generated__/Proposals';
|
||||
import type { Proposal_proposal } from '@vegaprotocol/governance';
|
||||
|
||||
const renderComponent = (proposal: Proposals_proposals) => (
|
||||
const renderComponent = (proposal: Proposal_proposal) => (
|
||||
<ProposalHeader proposal={proposal} />
|
||||
);
|
||||
|
||||
@ -12,10 +12,13 @@ describe('Proposal header', () => {
|
||||
render(
|
||||
renderComponent(
|
||||
generateProposal({
|
||||
rationale: {
|
||||
title: 'New some market',
|
||||
description: 'A new some market',
|
||||
},
|
||||
terms: {
|
||||
change: {
|
||||
__typename: 'NewMarket',
|
||||
decimalPlaces: 1,
|
||||
instrument: {
|
||||
__typename: 'InstrumentConfiguration',
|
||||
name: 'Some market',
|
||||
@ -28,16 +31,18 @@ describe('Proposal header', () => {
|
||||
},
|
||||
},
|
||||
},
|
||||
metadata: [],
|
||||
},
|
||||
},
|
||||
})
|
||||
)
|
||||
);
|
||||
expect(screen.getByTestId('proposal-header')).toHaveTextContent(
|
||||
'New market: Some market'
|
||||
expect(screen.getByTestId('proposal-title')).toHaveTextContent(
|
||||
'New some market'
|
||||
);
|
||||
expect(screen.getByTestId('proposal-details-one')).toHaveTextContent(
|
||||
expect(screen.getByTestId('proposal-description')).toHaveTextContent(
|
||||
'A new some market'
|
||||
);
|
||||
expect(screen.getByTestId('proposal-details')).toHaveTextContent(
|
||||
'tGBP settled future.'
|
||||
);
|
||||
});
|
||||
@ -46,6 +51,9 @@ describe('Proposal header', () => {
|
||||
render(
|
||||
renderComponent(
|
||||
generateProposal({
|
||||
rationale: {
|
||||
title: 'New market id',
|
||||
},
|
||||
terms: {
|
||||
change: {
|
||||
__typename: 'UpdateMarket',
|
||||
@ -55,7 +63,13 @@ describe('Proposal header', () => {
|
||||
})
|
||||
)
|
||||
);
|
||||
expect(screen.getByTestId('proposal-header')).toHaveTextContent(
|
||||
expect(screen.getByTestId('proposal-title')).toHaveTextContent(
|
||||
'New market id'
|
||||
);
|
||||
expect(
|
||||
screen.queryByTestId('proposal-description')
|
||||
).not.toBeInTheDocument();
|
||||
expect(screen.getByTestId('proposal-details')).toHaveTextContent(
|
||||
'Market change: MarketId'
|
||||
);
|
||||
});
|
||||
@ -64,6 +78,10 @@ describe('Proposal header', () => {
|
||||
render(
|
||||
renderComponent(
|
||||
generateProposal({
|
||||
rationale: {
|
||||
title: 'New asset: Fake currency',
|
||||
description: '',
|
||||
},
|
||||
terms: {
|
||||
change: {
|
||||
__typename: 'NewAsset',
|
||||
@ -78,10 +96,10 @@ describe('Proposal header', () => {
|
||||
})
|
||||
)
|
||||
);
|
||||
expect(screen.getByTestId('proposal-header')).toHaveTextContent(
|
||||
expect(screen.getByTestId('proposal-title')).toHaveTextContent(
|
||||
'New asset: Fake currency'
|
||||
);
|
||||
expect(screen.getByTestId('proposal-details-one')).toHaveTextContent(
|
||||
expect(screen.getByTestId('proposal-details')).toHaveTextContent(
|
||||
'Symbol: FAKE. ERC20 0x0'
|
||||
);
|
||||
});
|
||||
@ -104,10 +122,10 @@ describe('Proposal header', () => {
|
||||
})
|
||||
)
|
||||
);
|
||||
expect(screen.getByTestId('proposal-header')).toHaveTextContent(
|
||||
'New asset: Fake currency'
|
||||
expect(screen.getByTestId('proposal-title')).toHaveTextContent(
|
||||
'Unknown proposal'
|
||||
);
|
||||
expect(screen.getByTestId('proposal-details-one')).toHaveTextContent(
|
||||
expect(screen.getByTestId('proposal-details')).toHaveTextContent(
|
||||
'Symbol: BIA. Max faucet amount mint: 300'
|
||||
);
|
||||
});
|
||||
@ -116,6 +134,9 @@ describe('Proposal header', () => {
|
||||
render(
|
||||
renderComponent(
|
||||
generateProposal({
|
||||
rationale: {
|
||||
title: 'Network parameter',
|
||||
},
|
||||
terms: {
|
||||
change: {
|
||||
__typename: 'UpdateNetworkParameter',
|
||||
@ -129,25 +150,22 @@ describe('Proposal header', () => {
|
||||
})
|
||||
)
|
||||
);
|
||||
expect(screen.getByTestId('proposal-header')).toHaveTextContent(
|
||||
expect(screen.getByTestId('proposal-title')).toHaveTextContent(
|
||||
'Network parameter'
|
||||
);
|
||||
expect(screen.getByTestId('proposal-details-one')).toHaveTextContent(
|
||||
expect(screen.getByTestId('proposal-details')).toHaveTextContent(
|
||||
'Network key to Network value'
|
||||
);
|
||||
});
|
||||
|
||||
// Skipped until proposals have rationale - https://github.com/vegaprotocol/frontend-monorepo/issues/824
|
||||
// eslint-disable-next-line jest/no-disabled-tests
|
||||
it.skip('Renders Freeform network - short rationale', () => {
|
||||
it('Renders Freeform network - short rationale', () => {
|
||||
render(
|
||||
renderComponent(
|
||||
generateProposal({
|
||||
id: 'short',
|
||||
// rationale: {
|
||||
// hash: '0x0',
|
||||
// description: 'freeform description',
|
||||
// },
|
||||
rationale: {
|
||||
title: '0x0',
|
||||
},
|
||||
terms: {
|
||||
change: {
|
||||
__typename: 'NewFreeform',
|
||||
@ -156,27 +174,23 @@ describe('Proposal header', () => {
|
||||
})
|
||||
)
|
||||
);
|
||||
expect(screen.getByTestId('proposal-header')).toHaveTextContent(
|
||||
'freeform description'
|
||||
);
|
||||
expect(screen.getByTestId('proposal-details-one')).toBeEmptyDOMElement();
|
||||
expect(screen.getByTestId('proposal-details-two')).toHaveTextContent(
|
||||
'short'
|
||||
);
|
||||
expect(screen.getByTestId('proposal-title')).toHaveTextContent('0x0');
|
||||
expect(
|
||||
screen.queryByTestId('proposal-description')
|
||||
).not.toBeInTheDocument();
|
||||
expect(screen.getByTestId('proposal-details')).toHaveTextContent('short');
|
||||
});
|
||||
|
||||
// Skipped until proposals have rationale
|
||||
// eslint-disable-next-line jest/no-disabled-tests
|
||||
it.skip('Renders Freeform proposal - long rationale (105 chars)', () => {
|
||||
it('Renders Freeform proposal - long rationale (105 chars)', () => {
|
||||
render(
|
||||
renderComponent(
|
||||
generateProposal({
|
||||
id: 'long',
|
||||
// rationale: {
|
||||
// hash: '0x0',
|
||||
// description:
|
||||
// 'Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Aenean dolor.',
|
||||
// },
|
||||
rationale: {
|
||||
title: '0x0',
|
||||
description:
|
||||
'Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Aenean dolor.',
|
||||
},
|
||||
terms: {
|
||||
change: {
|
||||
__typename: 'NewFreeform',
|
||||
@ -187,29 +201,24 @@ 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-header')).toHaveTextContent(
|
||||
'Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Aenean…'
|
||||
);
|
||||
expect(screen.getByTestId('proposal-details-one')).toHaveTextContent(
|
||||
'dolor'
|
||||
);
|
||||
expect(screen.getByTestId('proposal-details-two')).toHaveTextContent(
|
||||
'long'
|
||||
expect(screen.getByTestId('proposal-title')).toHaveTextContent('0x0');
|
||||
expect(screen.getByTestId('proposal-description')).toHaveTextContent(
|
||||
'Class aptent taciti sociosqu ad litora torquent per conubia'
|
||||
);
|
||||
expect(screen.getByTestId('proposal-details')).toHaveTextContent('long');
|
||||
});
|
||||
|
||||
// Skipped until proposals have rationale
|
||||
// eslint-disable-next-line jest/no-disabled-tests
|
||||
it.skip('Renders Freeform proposal - extra long rationale (165 chars)', () => {
|
||||
it('Renders Freeform proposal - extra long rationale (165 chars)', () => {
|
||||
render(
|
||||
renderComponent(
|
||||
generateProposal({
|
||||
id: 'extraLong',
|
||||
// rationale: {
|
||||
// hash: '0x0',
|
||||
// description:
|
||||
// 'Aenean sem odio, eleifend non sodales vitae, porttitor eu ex. Aliquam erat volutpat. Fusce pharetra libero quis risus lobortis, sed ornare leo efficitur turpis duis.',
|
||||
// },
|
||||
rationale: {
|
||||
title:
|
||||
'Aenean sem odio, eleifend non sodales vitae, porttitor eu ex. Aliquam erat volutpat. Fusce pharetra libero quis risus lobortis, sed ornare leo efficitur turpis duis.',
|
||||
description:
|
||||
'Aenean sem odio, eleifend non sodales vitae, porttitor eu ex. Aliquam erat volutpat. Fusce pharetra libero quis risus lobortis, sed ornare leo efficitur turpis duis.',
|
||||
},
|
||||
terms: {
|
||||
change: {
|
||||
__typename: 'NewFreeform',
|
||||
@ -221,13 +230,13 @@ describe('Proposal header', () => {
|
||||
// For a rationale over 160 chars, we expect the header to be truncated at 100
|
||||
// chars with ellipsis and the details-one element to contain 60 chars and also
|
||||
// be truncated with an ellipsis.
|
||||
expect(screen.getByTestId('proposal-header')).toHaveTextContent(
|
||||
expect(screen.getByTestId('proposal-title')).toHaveTextContent(
|
||||
'Aenean sem odio, eleifend non sodales vitae, porttitor eu ex. Aliquam erat volutpat. Fusce pharetra…'
|
||||
);
|
||||
expect(screen.getByTestId('proposal-details-one')).toHaveTextContent(
|
||||
'libero quis risus lobortis, sed ornare leo efficitur turpis…'
|
||||
expect(screen.getByTestId('proposal-description')).toHaveTextContent(
|
||||
'Aenean sem odio, eleifend non sodales vitae, porttitor eu e…'
|
||||
);
|
||||
expect(screen.getByTestId('proposal-details-two')).toHaveTextContent(
|
||||
expect(screen.getByTestId('proposal-details')).toHaveTextContent(
|
||||
'extraLong'
|
||||
);
|
||||
});
|
||||
@ -238,6 +247,9 @@ describe('Proposal header', () => {
|
||||
renderComponent(
|
||||
generateProposal({
|
||||
id: 'freeform id',
|
||||
rationale: {
|
||||
title: 'freeform',
|
||||
},
|
||||
terms: {
|
||||
change: {
|
||||
__typename: 'NewFreeform',
|
||||
@ -246,15 +258,13 @@ describe('Proposal header', () => {
|
||||
})
|
||||
)
|
||||
);
|
||||
expect(screen.getByTestId('proposal-header')).toHaveTextContent(
|
||||
'Freeform proposal: freeform id'
|
||||
expect(screen.getByTestId('proposal-title')).toHaveTextContent('freeform');
|
||||
expect(
|
||||
screen.queryByTestId('proposal-description')
|
||||
).not.toBeInTheDocument();
|
||||
expect(screen.queryByTestId('proposal-details')).toHaveTextContent(
|
||||
'freeform id'
|
||||
);
|
||||
expect(
|
||||
screen.queryByTestId('proposal-details-one')
|
||||
).not.toBeInTheDocument();
|
||||
expect(
|
||||
screen.queryByTestId('proposal-details-two')
|
||||
).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("Renders unknown proposal if it's a different proposal type", () => {
|
||||
@ -270,7 +280,7 @@ describe('Proposal header', () => {
|
||||
})
|
||||
)
|
||||
);
|
||||
expect(screen.getByTestId('proposal-header')).toHaveTextContent(
|
||||
expect(screen.getByTestId('proposal-title')).toHaveTextContent(
|
||||
'Unknown proposal'
|
||||
);
|
||||
});
|
||||
|
@ -1,24 +1,28 @@
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Lozenge } from '@vegaprotocol/ui-toolkit';
|
||||
import type { ReactNode } from 'react';
|
||||
import type { Proposals_proposals } from '../../proposals/__generated__/Proposals';
|
||||
import { shorten } from '@vegaprotocol/react-helpers';
|
||||
import type { ProposalFields } from '../../__generated__/ProposalFields';
|
||||
|
||||
export const ProposalHeader = ({
|
||||
proposal,
|
||||
}: {
|
||||
proposal: Proposals_proposals;
|
||||
}) => {
|
||||
export const ProposalHeader = ({ proposal }: { proposal: ProposalFields }) => {
|
||||
const { t } = useTranslation();
|
||||
const { change } = proposal.terms;
|
||||
|
||||
let headerText: string;
|
||||
let detailsOne: ReactNode;
|
||||
let detailsTwo: ReactNode;
|
||||
let details: ReactNode;
|
||||
|
||||
let title = proposal.rationale.title.trim();
|
||||
let description = proposal.rationale.description.trim();
|
||||
if (title.length === 0 && description.length > 0) {
|
||||
title = description;
|
||||
description = '';
|
||||
}
|
||||
|
||||
const titleContent = shorten(title, 100);
|
||||
const descriptionContent = shorten(description, 60);
|
||||
|
||||
switch (change.__typename) {
|
||||
case 'NewMarket': {
|
||||
headerText = `${t('New market')}: ${change.instrument.name}`;
|
||||
detailsOne = (
|
||||
details = (
|
||||
<>
|
||||
{t('Code')}: {change.instrument.code}.{' '}
|
||||
{change.instrument.futureProduct?.settlementAsset.symbol ? (
|
||||
@ -36,12 +40,11 @@ export const ProposalHeader = ({
|
||||
break;
|
||||
}
|
||||
case 'UpdateMarket': {
|
||||
headerText = `${t('Market change')}: ${change.marketId}`;
|
||||
details = `${t('Market change')}: ${change.marketId}`;
|
||||
break;
|
||||
}
|
||||
case 'NewAsset': {
|
||||
headerText = `${t('New asset')}: ${change.name}`;
|
||||
detailsOne = (
|
||||
details = (
|
||||
<>
|
||||
{t('Symbol')}: {change.symbol}.{' '}
|
||||
<Lozenge>
|
||||
@ -57,8 +60,7 @@ export const ProposalHeader = ({
|
||||
}
|
||||
case 'UpdateNetworkParameter': {
|
||||
const parametersClasses = 'font-mono leading-none';
|
||||
headerText = `${t('Network parameter')}`;
|
||||
detailsOne = (
|
||||
details = (
|
||||
<>
|
||||
<span className={`${parametersClasses} mr-2`}>
|
||||
{change.networkParameter.key}
|
||||
@ -72,44 +74,34 @@ export const ProposalHeader = ({
|
||||
break;
|
||||
}
|
||||
case 'NewFreeform': {
|
||||
// When rationale exists (https://github.com/vegaprotocol/frontend-monorepo/issues/824):
|
||||
// const description = proposal.rationale.description.trim();
|
||||
// const headerMaxLength = 100;
|
||||
// const descriptionOneMaxLength = 60;
|
||||
// const headerOverflow = description.length > headerMaxLength;
|
||||
// const descriptionOneOverflow =
|
||||
// description.length > headerMaxLength + descriptionOneMaxLength;
|
||||
//
|
||||
// headerText = `${description.substring(0, headerMaxLength - 1).trim()}${
|
||||
// headerOverflow ? '…' : ''
|
||||
// }`;
|
||||
// detailsOne = headerOverflow
|
||||
// ? `${description
|
||||
// .substring(
|
||||
// headerMaxLength - 1,
|
||||
// headerMaxLength + descriptionOneMaxLength - 1
|
||||
// )
|
||||
// .trim()}${descriptionOneOverflow ? '…' : ''}`
|
||||
// : '';
|
||||
// detailsTwo = `${proposal.id}`;
|
||||
headerText = proposal.id
|
||||
? `${t('Freeform proposal')}: ${proposal.id.trim()}`
|
||||
: `${t('Unknown proposal')}`;
|
||||
details = `${proposal.id}`;
|
||||
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
headerText = `${t('Unknown proposal')}`;
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="text-sm mb-2">
|
||||
<header data-testid="proposal-header">
|
||||
<h2 className="text-lg mx-0 mt-0 mb-1 font-semibold">{headerText}</h2>
|
||||
<header data-testid="proposal-title">
|
||||
<h2
|
||||
{...(title && title.length > titleContent.length && { title: title })}
|
||||
className="text-lg mx-0 mt-0 mb-1 font-semibold"
|
||||
>
|
||||
{titleContent || t('Unknown proposal')}
|
||||
</h2>
|
||||
</header>
|
||||
{detailsOne && <div data-testid="proposal-details-one">{detailsOne}</div>}
|
||||
{detailsTwo && <div data-testid="proposal-details-two">{detailsTwo}</div>}
|
||||
{descriptionContent && (
|
||||
<div
|
||||
className="mb-4"
|
||||
{...(description.length > descriptionContent.length && {
|
||||
title: description,
|
||||
})}
|
||||
data-testid="proposal-description"
|
||||
>
|
||||
{descriptionContent}
|
||||
</div>
|
||||
)}
|
||||
{details && <div data-testid="proposal-details">{details}</div>}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
@ -1,11 +1,11 @@
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { SyntaxHighlighter } from '@vegaprotocol/ui-toolkit';
|
||||
import type { Proposal_proposal_terms } from '../../proposal/__generated__/Proposal';
|
||||
import type { ProposalFields_terms } from '../../__generated__/ProposalFields';
|
||||
|
||||
export const ProposalTermsJson = ({
|
||||
terms,
|
||||
}: {
|
||||
terms: Proposal_proposal_terms;
|
||||
terms: ProposalFields_terms;
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
|
@ -6,11 +6,11 @@ import {
|
||||
formatNumberPercentage,
|
||||
} from '@vegaprotocol/react-helpers';
|
||||
import { useVoteInformation } from '../../hooks';
|
||||
import type { Proposals_proposals } from '../../proposals/__generated__/Proposals';
|
||||
import { useAppState } from '../../../../contexts/app-state/app-state-context';
|
||||
import type { ProposalFields } from '../../__generated__/ProposalFields';
|
||||
|
||||
interface ProposalVotesTableProps {
|
||||
proposal: Proposals_proposals;
|
||||
proposal: ProposalFields;
|
||||
}
|
||||
|
||||
export const ProposalVotesTable = ({ proposal }: ProposalVotesTableProps) => {
|
||||
|
@ -26,10 +26,10 @@ import {
|
||||
lastWeek,
|
||||
nextWeek,
|
||||
} from '../../test-helpers/mocks';
|
||||
import type { Proposals_proposals } from '../../proposals/__generated__/Proposals';
|
||||
import type { ProposalsConnection_proposalsConnection_edges_node as ProposalNode } from '@vegaprotocol/governance';
|
||||
|
||||
const renderComponent = (
|
||||
proposal: Proposals_proposals,
|
||||
proposal: ProposalNode,
|
||||
mock = networkParamsQueryMock
|
||||
) => (
|
||||
<Router>
|
||||
@ -173,8 +173,8 @@ describe('Proposals list item details', () => {
|
||||
party: {
|
||||
__typename: 'Party',
|
||||
id: mockPubkey,
|
||||
stake: {
|
||||
__typename: 'PartyStake',
|
||||
stakingSummary: {
|
||||
__typename: 'StakingSummary',
|
||||
currentStakeAvailable: '1000',
|
||||
},
|
||||
},
|
||||
@ -211,8 +211,8 @@ describe('Proposals list item details', () => {
|
||||
party: {
|
||||
__typename: 'Party',
|
||||
id: mockPubkey,
|
||||
stake: {
|
||||
__typename: 'PartyStake',
|
||||
stakingSummary: {
|
||||
__typename: 'StakingSummary',
|
||||
currentStakeAvailable: '1000',
|
||||
},
|
||||
},
|
||||
|
@ -10,12 +10,12 @@ import { format, formatDistanceToNowStrict } from 'date-fns';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { DATE_FORMAT_DETAILED } from '../../../../lib/date-formats';
|
||||
import type { ReactNode } from 'react';
|
||||
import type { Proposals_proposals } from '../../proposals/__generated__/Proposals';
|
||||
import {
|
||||
ProposalRejectionReasonMapping,
|
||||
ProposalState,
|
||||
} from '@vegaprotocol/types';
|
||||
import Routes from '../../../routes';
|
||||
import type { ProposalFields } from '../../__generated__/ProposalFields';
|
||||
|
||||
const MajorityNotReached = () => {
|
||||
const { t } = useTranslation();
|
||||
@ -37,7 +37,7 @@ const ParticipationNotReached = () => {
|
||||
export const ProposalsListItemDetails = ({
|
||||
proposal,
|
||||
}: {
|
||||
proposal: Proposals_proposals;
|
||||
proposal: ProposalFields;
|
||||
}) => {
|
||||
const { state } = proposal;
|
||||
const { willPass, majorityMet, participationMet } = useVoteInformation({
|
||||
|
@ -1,9 +1,9 @@
|
||||
import type { ProposalFields } from '../../__generated__/ProposalFields';
|
||||
import { ProposalHeader } from '../proposal-detail-header/proposal-header';
|
||||
import { ProposalsListItemDetails } from './proposals-list-item-details';
|
||||
import type { Proposals_proposals } from '../../proposals/__generated__/Proposals';
|
||||
|
||||
interface ProposalsListItemProps {
|
||||
proposal: Proposals_proposals;
|
||||
proposal: ProposalFields;
|
||||
}
|
||||
|
||||
export const ProposalsListItem = ({ proposal }: ProposalsListItemProps) => {
|
||||
|
@ -14,7 +14,7 @@ import {
|
||||
lastMonth,
|
||||
nextMonth,
|
||||
} from '../../test-helpers/mocks';
|
||||
import type { Proposals_proposals } from '../../proposals/__generated__/Proposals';
|
||||
import type { ProposalsConnection_proposalsConnection_edges_node as ProposalNode } from '@vegaprotocol/governance';
|
||||
|
||||
const openProposalClosesNextMonth = generateProposal({
|
||||
id: 'proposal1',
|
||||
@ -58,7 +58,7 @@ const failedProposalClosedLastMonth = generateProposal({
|
||||
},
|
||||
});
|
||||
|
||||
const renderComponent = (proposals: Proposals_proposals[]) => (
|
||||
const renderComponent = (proposals: ProposalNode[]) => (
|
||||
<Router>
|
||||
<MockedProvider mocks={[networkParamsQueryMock]}>
|
||||
<AppStateProvider>
|
||||
|
@ -4,20 +4,20 @@ import { useTranslation } from 'react-i18next';
|
||||
import { Heading } from '../../../../components/heading';
|
||||
import { ProposalsListItem } from '../proposals-list-item';
|
||||
import { ProposalsListFilter } from '../proposals-list-filter';
|
||||
import type { Proposals_proposals } from '../../proposals/__generated__/Proposals';
|
||||
import Routes from '../../../routes';
|
||||
import { Button } from '@vegaprotocol/ui-toolkit';
|
||||
import { Link } from 'react-router-dom';
|
||||
import type { ProposalFields } from '../../__generated__/ProposalFields';
|
||||
import { Links } from '../../../../config';
|
||||
import { ExternalLink } from '@vegaprotocol/ui-toolkit';
|
||||
|
||||
interface ProposalsListProps {
|
||||
proposals: Proposals_proposals[];
|
||||
proposals: ProposalFields[];
|
||||
}
|
||||
|
||||
interface SortedProposalsProps {
|
||||
open: Proposals_proposals[];
|
||||
closed: Proposals_proposals[];
|
||||
open: ProposalFields[];
|
||||
closed: ProposalFields[];
|
||||
}
|
||||
|
||||
export const ProposalsList = ({ proposals }: ProposalsListProps) => {
|
||||
@ -39,7 +39,7 @@ export const ProposalsList = ({ proposals }: ProposalsListProps) => {
|
||||
}
|
||||
);
|
||||
|
||||
const filterPredicate = (p: Proposals_proposals) =>
|
||||
const filterPredicate = (p: ProposalFields) =>
|
||||
p.id?.includes(filterString) ||
|
||||
p.party?.id?.toString().includes(filterString);
|
||||
|
||||
|
@ -12,11 +12,11 @@ import {
|
||||
nextWeek,
|
||||
lastMonth,
|
||||
} from '../../test-helpers/mocks';
|
||||
import type { Proposals_proposals } from '../../proposals/__generated__/Proposals';
|
||||
import type { ProposalsConnection_proposalsConnection_edges_node as ProposalNode } from '@vegaprotocol/governance';
|
||||
|
||||
const rejectedProposalClosesNextWeek = generateProposal({
|
||||
id: 'rejected1',
|
||||
state: ProposalState.Open,
|
||||
state: ProposalState.STATE_OPEN,
|
||||
party: {
|
||||
id: 'bvcx',
|
||||
},
|
||||
@ -28,14 +28,14 @@ const rejectedProposalClosesNextWeek = generateProposal({
|
||||
|
||||
const rejectedProposalClosedLastMonth = generateProposal({
|
||||
id: 'rejected2',
|
||||
state: ProposalState.Rejected,
|
||||
state: ProposalState.STATE_REJECTED,
|
||||
terms: {
|
||||
closingDatetime: lastMonth.toString(),
|
||||
enactmentDatetime: lastMonth.toString(),
|
||||
},
|
||||
});
|
||||
|
||||
const renderComponent = (proposals: Proposals_proposals[]) => (
|
||||
const renderComponent = (proposals: ProposalNode[]) => (
|
||||
<Router>
|
||||
<MockedProvider mocks={[networkParamsQueryMock]}>
|
||||
<AppStateProvider>
|
||||
|
@ -3,17 +3,17 @@ import { useTranslation } from 'react-i18next';
|
||||
import { Heading } from '../../../../components/heading';
|
||||
import { ProposalsListItem } from '../proposals-list-item';
|
||||
import { ProposalsListFilter } from '../proposals-list-filter';
|
||||
import type { Proposals_proposals } from '../../proposals/__generated__/Proposals';
|
||||
import type { Proposals_proposalsConnection_edges_node } from '../../proposals/__generated__/Proposals';
|
||||
|
||||
interface ProposalsListProps {
|
||||
proposals: Proposals_proposals[];
|
||||
proposals: Proposals_proposalsConnection_edges_node[];
|
||||
}
|
||||
|
||||
export const RejectedProposalsList = ({ proposals }: ProposalsListProps) => {
|
||||
const { t } = useTranslation();
|
||||
const [filterString, setFilterString] = useState('');
|
||||
|
||||
const filterPredicate = (p: Proposals_proposals) =>
|
||||
const filterPredicate = (p: Proposals_proposalsConnection_edges_node) =>
|
||||
p.id?.includes(filterString) ||
|
||||
p.party?.id?.toString().includes(filterString);
|
||||
|
||||
|
@ -4,13 +4,13 @@ import { useTranslation } from 'react-i18next';
|
||||
import { formatNumber } from '../../../../lib/format-number';
|
||||
import { ConnectToVega } from '../../../staking/connect-to-vega';
|
||||
import { useVoteInformation } from '../../hooks';
|
||||
import type { Proposal_proposal } from '../../proposal/__generated__/Proposal';
|
||||
import { CurrentProposalStatus } from '../current-proposal-status';
|
||||
import { useUserVote } from './use-user-vote';
|
||||
import { VoteButtonsContainer } from './vote-buttons';
|
||||
import { VoteProgress } from './vote-progress';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||
import { ProposalState } from '@vegaprotocol/types';
|
||||
import type { Proposal_proposal } from '../../proposal/__generated__/Proposal';
|
||||
|
||||
interface VoteDetailsProps {
|
||||
proposal: Proposal_proposal;
|
||||
|
@ -6,15 +6,15 @@ import { useAppState } from '../../../contexts/app-state/app-state-context';
|
||||
import { BigNumber } from '../../../lib/bignumber';
|
||||
import { addDecimal } from '../../../lib/decimals';
|
||||
import type {
|
||||
Proposal_proposal_votes_no_votes,
|
||||
Proposal_proposal_votes_yes_votes,
|
||||
} from '../proposal/__generated__/Proposal';
|
||||
import type { Proposals_proposals } from '../proposals/__generated__/Proposals';
|
||||
ProposalFields,
|
||||
ProposalFields_votes_no_votes,
|
||||
ProposalFields_votes_yes_votes,
|
||||
} from '../__generated__/ProposalFields';
|
||||
|
||||
const useProposalNetworkParams = ({
|
||||
proposal,
|
||||
}: {
|
||||
proposal: Proposals_proposals;
|
||||
proposal: ProposalFields;
|
||||
}) => {
|
||||
const { data, loading } = useNetworkParams([
|
||||
NetworkParams.GOV_UPDATE_MARKET_REQUIRED_MAJORITY,
|
||||
@ -82,7 +82,7 @@ const useProposalNetworkParams = ({
|
||||
export const useVoteInformation = ({
|
||||
proposal,
|
||||
}: {
|
||||
proposal: Proposals_proposals;
|
||||
proposal: ProposalFields;
|
||||
}) => {
|
||||
const {
|
||||
appState: { totalSupply },
|
||||
@ -105,10 +105,10 @@ export const useVoteInformation = ({
|
||||
return new BigNumber(0);
|
||||
}
|
||||
const totalNoVotes = proposal.votes.no.votes.reduce(
|
||||
(prevValue: BigNumber, newValue: Proposal_proposal_votes_no_votes) => {
|
||||
return new BigNumber(newValue.party.stake.currentStakeAvailable).plus(
|
||||
prevValue
|
||||
);
|
||||
(prevValue: BigNumber, newValue: ProposalFields_votes_no_votes) => {
|
||||
return new BigNumber(
|
||||
newValue.party.stakingSummary.currentStakeAvailable
|
||||
).plus(prevValue);
|
||||
},
|
||||
new BigNumber(0)
|
||||
);
|
||||
@ -120,10 +120,10 @@ export const useVoteInformation = ({
|
||||
return new BigNumber(0);
|
||||
}
|
||||
const totalYesVotes = proposal.votes.yes.votes.reduce(
|
||||
(prevValue: BigNumber, newValue: Proposal_proposal_votes_yes_votes) => {
|
||||
return new BigNumber(newValue.party.stake.currentStakeAvailable).plus(
|
||||
prevValue
|
||||
);
|
||||
(prevValue: BigNumber, newValue: ProposalFields_votes_yes_votes) => {
|
||||
return new BigNumber(
|
||||
newValue.party.stakingSummary.currentStakeAvailable
|
||||
).plus(prevValue);
|
||||
},
|
||||
new BigNumber(0)
|
||||
);
|
||||
|
@ -1,23 +1,25 @@
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
export const PROPOSALS_FRAGMENT = gql`
|
||||
export const PROPOSAL_FRAGMENT = gql`
|
||||
fragment ProposalFields on Proposal {
|
||||
id
|
||||
rationale {
|
||||
title
|
||||
description
|
||||
}
|
||||
reference
|
||||
state
|
||||
datetime
|
||||
rejectionReason
|
||||
errorDetails
|
||||
party {
|
||||
id
|
||||
}
|
||||
errorDetails
|
||||
terms {
|
||||
closingDatetime
|
||||
enactmentDatetime
|
||||
change {
|
||||
... on NewMarket {
|
||||
decimalPlaces
|
||||
metadata
|
||||
instrument {
|
||||
name
|
||||
code
|
||||
@ -37,11 +39,9 @@ export const PROPOSALS_FRAGMENT = gql`
|
||||
symbol
|
||||
source {
|
||||
... on BuiltinAsset {
|
||||
__typename
|
||||
maxFaucetAmountMint
|
||||
}
|
||||
... on ERC20 {
|
||||
__typename
|
||||
contractAddress
|
||||
}
|
||||
}
|
||||
@ -62,7 +62,7 @@ export const PROPOSALS_FRAGMENT = gql`
|
||||
value
|
||||
party {
|
||||
id
|
||||
stake {
|
||||
stakingSummary {
|
||||
currentStakeAvailable
|
||||
}
|
||||
}
|
||||
@ -76,7 +76,7 @@ export const PROPOSALS_FRAGMENT = gql`
|
||||
value
|
||||
party {
|
||||
id
|
||||
stake {
|
||||
stakingSummary {
|
||||
currentStakeAvailable
|
||||
}
|
||||
}
|
||||
|
@ -9,6 +9,22 @@ import { ProposalState, ProposalRejectionReason, VoteValue } from "@vegaprotocol
|
||||
// GraphQL query operation: Proposal
|
||||
// ====================================================
|
||||
|
||||
export interface Proposal_proposal_rationale {
|
||||
__typename: "ProposalRationale";
|
||||
/**
|
||||
* Title to be used to give a short description of the proposal in lists.
|
||||
* This is to be between 0 and 100 unicode characters.
|
||||
* This is mandatory for all proposals.
|
||||
*/
|
||||
title: string;
|
||||
/**
|
||||
* Description to show a short title / something in case the link goes offline.
|
||||
* This is to be between 0 and 20k unicode characters.
|
||||
* This is mandatory for all proposals.
|
||||
*/
|
||||
description: string;
|
||||
}
|
||||
|
||||
export interface Proposal_proposal_party {
|
||||
__typename: "Party";
|
||||
/**
|
||||
@ -55,14 +71,6 @@ export interface Proposal_proposal_terms_change_NewMarket_instrument {
|
||||
|
||||
export interface Proposal_proposal_terms_change_NewMarket {
|
||||
__typename: "NewMarket";
|
||||
/**
|
||||
* Decimal places used for the new market, sets the smallest price increment on the book
|
||||
*/
|
||||
decimalPlaces: number;
|
||||
/**
|
||||
* Metadata for this instrument, tags
|
||||
*/
|
||||
metadata: string[] | null;
|
||||
/**
|
||||
* New market instrument configuration
|
||||
*/
|
||||
@ -146,8 +154,8 @@ export interface Proposal_proposal_terms {
|
||||
change: Proposal_proposal_terms_change;
|
||||
}
|
||||
|
||||
export interface Proposal_proposal_votes_yes_votes_party_stake {
|
||||
__typename: "PartyStake";
|
||||
export interface Proposal_proposal_votes_yes_votes_party_stakingSummary {
|
||||
__typename: "StakingSummary";
|
||||
/**
|
||||
* The stake currently available for the party
|
||||
*/
|
||||
@ -163,7 +171,7 @@ export interface Proposal_proposal_votes_yes_votes_party {
|
||||
/**
|
||||
* The staking information for this Party
|
||||
*/
|
||||
stake: Proposal_proposal_votes_yes_votes_party_stake;
|
||||
stakingSummary: Proposal_proposal_votes_yes_votes_party_stakingSummary;
|
||||
}
|
||||
|
||||
export interface Proposal_proposal_votes_yes_votes {
|
||||
@ -198,8 +206,8 @@ export interface Proposal_proposal_votes_yes {
|
||||
votes: Proposal_proposal_votes_yes_votes[] | null;
|
||||
}
|
||||
|
||||
export interface Proposal_proposal_votes_no_votes_party_stake {
|
||||
__typename: "PartyStake";
|
||||
export interface Proposal_proposal_votes_no_votes_party_stakingSummary {
|
||||
__typename: "StakingSummary";
|
||||
/**
|
||||
* The stake currently available for the party
|
||||
*/
|
||||
@ -215,7 +223,7 @@ export interface Proposal_proposal_votes_no_votes_party {
|
||||
/**
|
||||
* The staking information for this Party
|
||||
*/
|
||||
stake: Proposal_proposal_votes_no_votes_party_stake;
|
||||
stakingSummary: Proposal_proposal_votes_no_votes_party_stakingSummary;
|
||||
}
|
||||
|
||||
export interface Proposal_proposal_votes_no_votes {
|
||||
@ -268,6 +276,10 @@ export interface Proposal_proposal {
|
||||
* Proposal ID that is filled by Vega once proposal reaches the network
|
||||
*/
|
||||
id: string | null;
|
||||
/**
|
||||
* Rationale behind the proposal
|
||||
*/
|
||||
rationale: Proposal_proposal_rationale;
|
||||
/**
|
||||
* A UUID reference to aid tracking proposals on Vega
|
||||
*/
|
||||
@ -284,14 +296,14 @@ export interface Proposal_proposal {
|
||||
* Reason for the proposal to be rejected by the core
|
||||
*/
|
||||
rejectionReason: ProposalRejectionReason | null;
|
||||
/**
|
||||
* Error details of the rejectionReason
|
||||
*/
|
||||
errorDetails: string | null;
|
||||
/**
|
||||
* Party that prepared the proposal
|
||||
*/
|
||||
party: Proposal_proposal_party;
|
||||
/**
|
||||
* Error details of the rejectionReason
|
||||
*/
|
||||
errorDetails: string | null;
|
||||
/**
|
||||
* Terms of the proposal
|
||||
*/
|
||||
|
@ -1,5 +1,4 @@
|
||||
export {
|
||||
ProposalContainer,
|
||||
PROPOSAL_QUERY,
|
||||
ProposalContainer as default,
|
||||
} from './proposal-container';
|
||||
|
@ -4,14 +4,14 @@ import { useEffect } from 'react';
|
||||
import { useParams } from 'react-router-dom';
|
||||
|
||||
import { Proposal } from '../components/proposal';
|
||||
import { PROPOSALS_FRAGMENT } from '../proposal-fragment';
|
||||
import { PROPOSAL_FRAGMENT } from '../proposal-fragment';
|
||||
import type {
|
||||
Proposal as ProposalQueryResult,
|
||||
ProposalVariables,
|
||||
} from './__generated__/Proposal';
|
||||
|
||||
export const PROPOSAL_QUERY = gql`
|
||||
${PROPOSALS_FRAGMENT}
|
||||
${PROPOSAL_FRAGMENT}
|
||||
query Proposal($proposalId: ID!) {
|
||||
proposal(id: $proposalId) {
|
||||
...ProposalFields
|
||||
|
@ -9,7 +9,23 @@ import { ProposalState, ProposalRejectionReason, VoteValue } from "@vegaprotocol
|
||||
// GraphQL query operation: Proposals
|
||||
// ====================================================
|
||||
|
||||
export interface Proposals_proposals_party {
|
||||
export interface Proposals_proposalsConnection_edges_node_rationale {
|
||||
__typename: "ProposalRationale";
|
||||
/**
|
||||
* Title to be used to give a short description of the proposal in lists.
|
||||
* This is to be between 0 and 100 unicode characters.
|
||||
* This is mandatory for all proposals.
|
||||
*/
|
||||
title: string;
|
||||
/**
|
||||
* Description to show a short title / something in case the link goes offline.
|
||||
* This is to be between 0 and 20k unicode characters.
|
||||
* This is mandatory for all proposals.
|
||||
*/
|
||||
description: string;
|
||||
}
|
||||
|
||||
export interface Proposals_proposalsConnection_edges_node_party {
|
||||
__typename: "Party";
|
||||
/**
|
||||
* Party identifier
|
||||
@ -17,11 +33,11 @@ export interface Proposals_proposals_party {
|
||||
id: string;
|
||||
}
|
||||
|
||||
export interface Proposals_proposals_terms_change_UpdateAsset {
|
||||
export interface Proposals_proposalsConnection_edges_node_terms_change_UpdateAsset {
|
||||
__typename: "UpdateAsset" | "NewFreeform";
|
||||
}
|
||||
|
||||
export interface Proposals_proposals_terms_change_NewMarket_instrument_futureProduct_settlementAsset {
|
||||
export interface Proposals_proposalsConnection_edges_node_terms_change_NewMarket_instrument_futureProduct_settlementAsset {
|
||||
__typename: "Asset";
|
||||
/**
|
||||
* The symbol of the asset (e.g: GBP)
|
||||
@ -29,15 +45,15 @@ export interface Proposals_proposals_terms_change_NewMarket_instrument_futurePro
|
||||
symbol: string;
|
||||
}
|
||||
|
||||
export interface Proposals_proposals_terms_change_NewMarket_instrument_futureProduct {
|
||||
export interface Proposals_proposalsConnection_edges_node_terms_change_NewMarket_instrument_futureProduct {
|
||||
__typename: "FutureProduct";
|
||||
/**
|
||||
* Product asset ID
|
||||
*/
|
||||
settlementAsset: Proposals_proposals_terms_change_NewMarket_instrument_futureProduct_settlementAsset;
|
||||
settlementAsset: Proposals_proposalsConnection_edges_node_terms_change_NewMarket_instrument_futureProduct_settlementAsset;
|
||||
}
|
||||
|
||||
export interface Proposals_proposals_terms_change_NewMarket_instrument {
|
||||
export interface Proposals_proposalsConnection_edges_node_terms_change_NewMarket_instrument {
|
||||
__typename: "InstrumentConfiguration";
|
||||
/**
|
||||
* Full and fairly descriptive name for the instrument
|
||||
@ -50,31 +66,23 @@ export interface Proposals_proposals_terms_change_NewMarket_instrument {
|
||||
/**
|
||||
* Future product specification
|
||||
*/
|
||||
futureProduct: Proposals_proposals_terms_change_NewMarket_instrument_futureProduct | null;
|
||||
futureProduct: Proposals_proposalsConnection_edges_node_terms_change_NewMarket_instrument_futureProduct | null;
|
||||
}
|
||||
|
||||
export interface Proposals_proposals_terms_change_NewMarket {
|
||||
export interface Proposals_proposalsConnection_edges_node_terms_change_NewMarket {
|
||||
__typename: "NewMarket";
|
||||
/**
|
||||
* Decimal places used for the new market, sets the smallest price increment on the book
|
||||
*/
|
||||
decimalPlaces: number;
|
||||
/**
|
||||
* Metadata for this instrument, tags
|
||||
*/
|
||||
metadata: string[] | null;
|
||||
/**
|
||||
* New market instrument configuration
|
||||
*/
|
||||
instrument: Proposals_proposals_terms_change_NewMarket_instrument;
|
||||
instrument: Proposals_proposalsConnection_edges_node_terms_change_NewMarket_instrument;
|
||||
}
|
||||
|
||||
export interface Proposals_proposals_terms_change_UpdateMarket {
|
||||
export interface Proposals_proposalsConnection_edges_node_terms_change_UpdateMarket {
|
||||
__typename: "UpdateMarket";
|
||||
marketId: string;
|
||||
}
|
||||
|
||||
export interface Proposals_proposals_terms_change_NewAsset_source_BuiltinAsset {
|
||||
export interface Proposals_proposalsConnection_edges_node_terms_change_NewAsset_source_BuiltinAsset {
|
||||
__typename: "BuiltinAsset";
|
||||
/**
|
||||
* Maximum amount that can be requested by a party through the built-in asset faucet at a time
|
||||
@ -82,7 +90,7 @@ export interface Proposals_proposals_terms_change_NewAsset_source_BuiltinAsset {
|
||||
maxFaucetAmountMint: string;
|
||||
}
|
||||
|
||||
export interface Proposals_proposals_terms_change_NewAsset_source_ERC20 {
|
||||
export interface Proposals_proposalsConnection_edges_node_terms_change_NewAsset_source_ERC20 {
|
||||
__typename: "ERC20";
|
||||
/**
|
||||
* The address of the ERC20 contract
|
||||
@ -90,9 +98,9 @@ export interface Proposals_proposals_terms_change_NewAsset_source_ERC20 {
|
||||
contractAddress: string;
|
||||
}
|
||||
|
||||
export type Proposals_proposals_terms_change_NewAsset_source = Proposals_proposals_terms_change_NewAsset_source_BuiltinAsset | Proposals_proposals_terms_change_NewAsset_source_ERC20;
|
||||
export type Proposals_proposalsConnection_edges_node_terms_change_NewAsset_source = Proposals_proposalsConnection_edges_node_terms_change_NewAsset_source_BuiltinAsset | Proposals_proposalsConnection_edges_node_terms_change_NewAsset_source_ERC20;
|
||||
|
||||
export interface Proposals_proposals_terms_change_NewAsset {
|
||||
export interface Proposals_proposalsConnection_edges_node_terms_change_NewAsset {
|
||||
__typename: "NewAsset";
|
||||
/**
|
||||
* The full name of the asset (e.g: Great British Pound)
|
||||
@ -105,10 +113,10 @@ export interface Proposals_proposals_terms_change_NewAsset {
|
||||
/**
|
||||
* The source of the new asset
|
||||
*/
|
||||
source: Proposals_proposals_terms_change_NewAsset_source;
|
||||
source: Proposals_proposalsConnection_edges_node_terms_change_NewAsset_source;
|
||||
}
|
||||
|
||||
export interface Proposals_proposals_terms_change_UpdateNetworkParameter_networkParameter {
|
||||
export interface Proposals_proposalsConnection_edges_node_terms_change_UpdateNetworkParameter_networkParameter {
|
||||
__typename: "NetworkParameter";
|
||||
/**
|
||||
* The name of the network parameter
|
||||
@ -120,14 +128,14 @@ export interface Proposals_proposals_terms_change_UpdateNetworkParameter_network
|
||||
value: string;
|
||||
}
|
||||
|
||||
export interface Proposals_proposals_terms_change_UpdateNetworkParameter {
|
||||
export interface Proposals_proposalsConnection_edges_node_terms_change_UpdateNetworkParameter {
|
||||
__typename: "UpdateNetworkParameter";
|
||||
networkParameter: Proposals_proposals_terms_change_UpdateNetworkParameter_networkParameter;
|
||||
networkParameter: Proposals_proposalsConnection_edges_node_terms_change_UpdateNetworkParameter_networkParameter;
|
||||
}
|
||||
|
||||
export type Proposals_proposals_terms_change = Proposals_proposals_terms_change_UpdateAsset | Proposals_proposals_terms_change_NewMarket | Proposals_proposals_terms_change_UpdateMarket | Proposals_proposals_terms_change_NewAsset | Proposals_proposals_terms_change_UpdateNetworkParameter;
|
||||
export type Proposals_proposalsConnection_edges_node_terms_change = Proposals_proposalsConnection_edges_node_terms_change_UpdateAsset | Proposals_proposalsConnection_edges_node_terms_change_NewMarket | Proposals_proposalsConnection_edges_node_terms_change_UpdateMarket | Proposals_proposalsConnection_edges_node_terms_change_NewAsset | Proposals_proposalsConnection_edges_node_terms_change_UpdateNetworkParameter;
|
||||
|
||||
export interface Proposals_proposals_terms {
|
||||
export interface Proposals_proposalsConnection_edges_node_terms {
|
||||
__typename: "ProposalTerms";
|
||||
/**
|
||||
* RFC3339Nano time and date when voting closes for this proposal.
|
||||
@ -143,18 +151,18 @@ export interface Proposals_proposals_terms {
|
||||
/**
|
||||
* Actual change being introduced by the proposal - action the proposal triggers if passed and enacted.
|
||||
*/
|
||||
change: Proposals_proposals_terms_change;
|
||||
change: Proposals_proposalsConnection_edges_node_terms_change;
|
||||
}
|
||||
|
||||
export interface Proposals_proposals_votes_yes_votes_party_stake {
|
||||
__typename: "PartyStake";
|
||||
export interface Proposals_proposalsConnection_edges_node_votes_yes_votes_party_stakingSummary {
|
||||
__typename: "StakingSummary";
|
||||
/**
|
||||
* The stake currently available for the party
|
||||
*/
|
||||
currentStakeAvailable: string;
|
||||
}
|
||||
|
||||
export interface Proposals_proposals_votes_yes_votes_party {
|
||||
export interface Proposals_proposalsConnection_edges_node_votes_yes_votes_party {
|
||||
__typename: "Party";
|
||||
/**
|
||||
* Party identifier
|
||||
@ -163,10 +171,10 @@ export interface Proposals_proposals_votes_yes_votes_party {
|
||||
/**
|
||||
* The staking information for this Party
|
||||
*/
|
||||
stake: Proposals_proposals_votes_yes_votes_party_stake;
|
||||
stakingSummary: Proposals_proposalsConnection_edges_node_votes_yes_votes_party_stakingSummary;
|
||||
}
|
||||
|
||||
export interface Proposals_proposals_votes_yes_votes {
|
||||
export interface Proposals_proposalsConnection_edges_node_votes_yes_votes {
|
||||
__typename: "Vote";
|
||||
/**
|
||||
* The vote value cast
|
||||
@ -175,14 +183,14 @@ export interface Proposals_proposals_votes_yes_votes {
|
||||
/**
|
||||
* The party casting the vote
|
||||
*/
|
||||
party: Proposals_proposals_votes_yes_votes_party;
|
||||
party: Proposals_proposalsConnection_edges_node_votes_yes_votes_party;
|
||||
/**
|
||||
* RFC3339Nano time and date when the vote reached Vega network
|
||||
*/
|
||||
datetime: string;
|
||||
}
|
||||
|
||||
export interface Proposals_proposals_votes_yes {
|
||||
export interface Proposals_proposalsConnection_edges_node_votes_yes {
|
||||
__typename: "ProposalVoteSide";
|
||||
/**
|
||||
* Total number of governance tokens from the votes cast for this side
|
||||
@ -195,18 +203,18 @@ export interface Proposals_proposals_votes_yes {
|
||||
/**
|
||||
* All votes cast for this side
|
||||
*/
|
||||
votes: Proposals_proposals_votes_yes_votes[] | null;
|
||||
votes: Proposals_proposalsConnection_edges_node_votes_yes_votes[] | null;
|
||||
}
|
||||
|
||||
export interface Proposals_proposals_votes_no_votes_party_stake {
|
||||
__typename: "PartyStake";
|
||||
export interface Proposals_proposalsConnection_edges_node_votes_no_votes_party_stakingSummary {
|
||||
__typename: "StakingSummary";
|
||||
/**
|
||||
* The stake currently available for the party
|
||||
*/
|
||||
currentStakeAvailable: string;
|
||||
}
|
||||
|
||||
export interface Proposals_proposals_votes_no_votes_party {
|
||||
export interface Proposals_proposalsConnection_edges_node_votes_no_votes_party {
|
||||
__typename: "Party";
|
||||
/**
|
||||
* Party identifier
|
||||
@ -215,10 +223,10 @@ export interface Proposals_proposals_votes_no_votes_party {
|
||||
/**
|
||||
* The staking information for this Party
|
||||
*/
|
||||
stake: Proposals_proposals_votes_no_votes_party_stake;
|
||||
stakingSummary: Proposals_proposalsConnection_edges_node_votes_no_votes_party_stakingSummary;
|
||||
}
|
||||
|
||||
export interface Proposals_proposals_votes_no_votes {
|
||||
export interface Proposals_proposalsConnection_edges_node_votes_no_votes {
|
||||
__typename: "Vote";
|
||||
/**
|
||||
* The vote value cast
|
||||
@ -227,14 +235,14 @@ export interface Proposals_proposals_votes_no_votes {
|
||||
/**
|
||||
* The party casting the vote
|
||||
*/
|
||||
party: Proposals_proposals_votes_no_votes_party;
|
||||
party: Proposals_proposalsConnection_edges_node_votes_no_votes_party;
|
||||
/**
|
||||
* RFC3339Nano time and date when the vote reached Vega network
|
||||
*/
|
||||
datetime: string;
|
||||
}
|
||||
|
||||
export interface Proposals_proposals_votes_no {
|
||||
export interface Proposals_proposalsConnection_edges_node_votes_no {
|
||||
__typename: "ProposalVoteSide";
|
||||
/**
|
||||
* Total number of governance tokens from the votes cast for this side
|
||||
@ -247,27 +255,31 @@ export interface Proposals_proposals_votes_no {
|
||||
/**
|
||||
* All votes cast for this side
|
||||
*/
|
||||
votes: Proposals_proposals_votes_no_votes[] | null;
|
||||
votes: Proposals_proposalsConnection_edges_node_votes_no_votes[] | null;
|
||||
}
|
||||
|
||||
export interface Proposals_proposals_votes {
|
||||
export interface Proposals_proposalsConnection_edges_node_votes {
|
||||
__typename: "ProposalVotes";
|
||||
/**
|
||||
* Yes votes cast for this proposal
|
||||
*/
|
||||
yes: Proposals_proposals_votes_yes;
|
||||
yes: Proposals_proposalsConnection_edges_node_votes_yes;
|
||||
/**
|
||||
* No votes cast for this proposal
|
||||
*/
|
||||
no: Proposals_proposals_votes_no;
|
||||
no: Proposals_proposalsConnection_edges_node_votes_no;
|
||||
}
|
||||
|
||||
export interface Proposals_proposals {
|
||||
export interface Proposals_proposalsConnection_edges_node {
|
||||
__typename: "Proposal";
|
||||
/**
|
||||
* Proposal ID that is filled by Vega once proposal reaches the network
|
||||
*/
|
||||
id: string | null;
|
||||
/**
|
||||
* Rationale behind the proposal
|
||||
*/
|
||||
rationale: Proposals_proposalsConnection_edges_node_rationale;
|
||||
/**
|
||||
* A UUID reference to aid tracking proposals on Vega
|
||||
*/
|
||||
@ -284,27 +296,43 @@ export interface Proposals_proposals {
|
||||
* Reason for the proposal to be rejected by the core
|
||||
*/
|
||||
rejectionReason: ProposalRejectionReason | null;
|
||||
/**
|
||||
* Party that prepared the proposal
|
||||
*/
|
||||
party: Proposals_proposalsConnection_edges_node_party;
|
||||
/**
|
||||
* Error details of the rejectionReason
|
||||
*/
|
||||
errorDetails: string | null;
|
||||
/**
|
||||
* Party that prepared the proposal
|
||||
*/
|
||||
party: Proposals_proposals_party;
|
||||
/**
|
||||
* Terms of the proposal
|
||||
*/
|
||||
terms: Proposals_proposals_terms;
|
||||
terms: Proposals_proposalsConnection_edges_node_terms;
|
||||
/**
|
||||
* Votes cast for this proposal
|
||||
*/
|
||||
votes: Proposals_proposals_votes;
|
||||
votes: Proposals_proposalsConnection_edges_node_votes;
|
||||
}
|
||||
|
||||
export interface Proposals_proposalsConnection_edges {
|
||||
__typename: "ProposalEdge";
|
||||
/**
|
||||
* The proposal data
|
||||
*/
|
||||
node: Proposals_proposalsConnection_edges_node;
|
||||
}
|
||||
|
||||
export interface Proposals_proposalsConnection {
|
||||
__typename: "ProposalsConnection";
|
||||
/**
|
||||
* List of proposals available for the connection
|
||||
*/
|
||||
edges: (Proposals_proposalsConnection_edges | null)[] | null;
|
||||
}
|
||||
|
||||
export interface Proposals {
|
||||
/**
|
||||
* All governance proposals in the Vega network
|
||||
*/
|
||||
proposals: Proposals_proposals[] | null;
|
||||
proposalsConnection: Proposals_proposalsConnection;
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
export {
|
||||
ProposalsContainer,
|
||||
PROPOSALS_QUERY,
|
||||
ProposalsContainer as default,
|
||||
PROPOSALS_QUERY,
|
||||
} from './proposals-container';
|
||||
|
@ -1,56 +1,36 @@
|
||||
import { gql, useQuery } from '@apollo/client';
|
||||
import { ProposalState } from '@vegaprotocol/types';
|
||||
import { getNotRejectedProposals } from '@vegaprotocol/governance';
|
||||
import { Callout, Intent, Splash } from '@vegaprotocol/ui-toolkit';
|
||||
import compact from 'lodash/compact';
|
||||
import filter from 'lodash/filter';
|
||||
import flow from 'lodash/flow';
|
||||
import orderBy from 'lodash/orderBy';
|
||||
import React from 'react';
|
||||
import { useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { SplashLoader } from '../../../components/splash-loader';
|
||||
import { ProposalsList } from '../components/proposals-list';
|
||||
import { PROPOSALS_FRAGMENT } from '../proposal-fragment';
|
||||
import { PROPOSAL_FRAGMENT } from '../proposal-fragment';
|
||||
import type { Proposals } from './__generated__/Proposals';
|
||||
|
||||
export const PROPOSALS_QUERY = gql`
|
||||
${PROPOSALS_FRAGMENT}
|
||||
${PROPOSAL_FRAGMENT}
|
||||
query Proposals {
|
||||
proposals {
|
||||
...ProposalFields
|
||||
proposalsConnection {
|
||||
edges {
|
||||
node {
|
||||
...ProposalFields
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const ProposalsContainer = () => {
|
||||
const { t } = useTranslation();
|
||||
const { data, loading, error } = useQuery<Proposals, never>(PROPOSALS_QUERY, {
|
||||
const { data, loading, error } = useQuery<Proposals>(PROPOSALS_QUERY, {
|
||||
pollInterval: 5000,
|
||||
fetchPolicy: 'network-only',
|
||||
errorPolicy: 'ignore', // this is to get around some backend issues and should be removed in future
|
||||
errorPolicy: 'ignore',
|
||||
});
|
||||
|
||||
const proposals = React.useMemo(() => {
|
||||
if (!data?.proposals?.length) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return flow([
|
||||
compact,
|
||||
(arr) =>
|
||||
filter(arr, ({ state }) => state !== ProposalState.STATE_REJECTED),
|
||||
(arr) =>
|
||||
orderBy(
|
||||
arr,
|
||||
[
|
||||
(p) => new Date(p.terms.enactmentDatetime).getTime(),
|
||||
(p) => new Date(p.terms.closingDatetime).getTime(),
|
||||
(p) => p.id,
|
||||
],
|
||||
['desc', 'desc', 'desc']
|
||||
),
|
||||
])(data.proposals);
|
||||
}, [data]);
|
||||
const proposals = useMemo(() => getNotRejectedProposals(data), [data]);
|
||||
|
||||
if (error) {
|
||||
return (
|
||||
|
@ -1,46 +1,19 @@
|
||||
import { useQuery } from '@apollo/client';
|
||||
import { Callout, Intent, Splash } from '@vegaprotocol/ui-toolkit';
|
||||
import compact from 'lodash/compact';
|
||||
import filter from 'lodash/filter';
|
||||
import flow from 'lodash/flow';
|
||||
import orderBy from 'lodash/orderBy';
|
||||
import React from 'react';
|
||||
import { useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { PROPOSALS_QUERY } from '../proposals';
|
||||
|
||||
import { SplashLoader } from '../../../components/splash-loader';
|
||||
import { RejectedProposalsList } from '../components/proposals-list';
|
||||
import { getRejectedProposals } from '@vegaprotocol/governance';
|
||||
import { PROPOSALS_QUERY } from '../proposals';
|
||||
import type { Proposals } from '../proposals/__generated__/Proposals';
|
||||
import { ProposalState } from '@vegaprotocol/types';
|
||||
|
||||
export const RejectedProposalsContainer = () => {
|
||||
const { t } = useTranslation();
|
||||
const { data, loading, error } = useQuery<Proposals, never>(PROPOSALS_QUERY, {
|
||||
pollInterval: 5000,
|
||||
errorPolicy: 'ignore', // this is to get around some backend issues and should be removed in future
|
||||
});
|
||||
const { data, loading, error } = useQuery<Proposals>(PROPOSALS_QUERY);
|
||||
|
||||
const proposals = React.useMemo(() => {
|
||||
if (!data?.proposals?.length) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return flow([
|
||||
compact,
|
||||
(arr) =>
|
||||
filter(arr, ({ state }) => state === ProposalState.STATE_REJECTED),
|
||||
(arr) =>
|
||||
orderBy(
|
||||
arr,
|
||||
[
|
||||
(p) => new Date(p.terms.enactmentDatetime).getTime(),
|
||||
(p) => new Date(p.terms.closingDatetime).getTime(),
|
||||
(p) => p.id,
|
||||
],
|
||||
['desc', 'desc', 'desc']
|
||||
),
|
||||
])(data.proposals);
|
||||
}, [data]);
|
||||
const proposals = useMemo(() => getRejectedProposals(data), [data]);
|
||||
|
||||
if (error) {
|
||||
return (
|
||||
|
@ -16,6 +16,11 @@ export function generateProposal(
|
||||
const defaultProposal: ProposalFields = {
|
||||
__typename: 'Proposal',
|
||||
id: faker.datatype.uuid(),
|
||||
rationale: {
|
||||
__typename: 'ProposalRationale',
|
||||
title: '',
|
||||
description: '',
|
||||
},
|
||||
reference: 'ref' + faker.datatype.uuid(),
|
||||
state: ProposalState.STATE_OPEN,
|
||||
datetime: faker.date.past().toISOString(),
|
||||
@ -84,8 +89,8 @@ export const generateYesVotes = (
|
||||
party: {
|
||||
id: faker.datatype.uuid(),
|
||||
__typename: 'Party',
|
||||
stake: {
|
||||
__typename: 'PartyStake',
|
||||
stakingSummary: {
|
||||
__typename: 'StakingSummary',
|
||||
currentStakeAvailable: fixedTokenValue
|
||||
? fixedTokenValue.toString()
|
||||
: faker.datatype
|
||||
@ -119,8 +124,8 @@ export const generateNoVotes = (
|
||||
party: {
|
||||
id: faker.datatype.uuid(),
|
||||
__typename: 'Party',
|
||||
stake: {
|
||||
__typename: 'PartyStake',
|
||||
stakingSummary: {
|
||||
__typename: 'StakingSummary',
|
||||
currentStakeAvailable: fixedTokenValue
|
||||
? fixedTokenValue.toString()
|
||||
: faker.datatype
|
||||
|
@ -1,2 +1,3 @@
|
||||
export * from './proposals-hooks';
|
||||
export * from './proposal-form';
|
||||
export * from './proposals-queries';
|
||||
|
1
libs/governance/src/lib/proposals-queries/index.ts
Normal file
1
libs/governance/src/lib/proposals-queries/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from './proposals-queries';
|
@ -0,0 +1,61 @@
|
||||
import compact from 'lodash/compact';
|
||||
import filter from 'lodash/filter';
|
||||
import flow from 'lodash/flow';
|
||||
import orderBy from 'lodash/orderBy';
|
||||
import { ProposalState } from '@vegaprotocol/types';
|
||||
|
||||
type Proposal = {
|
||||
__typename: 'Proposal';
|
||||
id: string | null;
|
||||
state: ProposalState;
|
||||
terms: {
|
||||
enactmentDatetime: string | null;
|
||||
closingDatetime: string;
|
||||
};
|
||||
};
|
||||
type ProposalEdge = {
|
||||
node: Proposal;
|
||||
};
|
||||
type ProposalsConnection = {
|
||||
proposalsConnection: {
|
||||
edges: (ProposalEdge | null)[] | null;
|
||||
};
|
||||
};
|
||||
|
||||
export const getProposals = (data?: ProposalsConnection) => {
|
||||
const proposals = data?.proposalsConnection.edges
|
||||
?.filter((e) => e?.node)
|
||||
.map((e) => e?.node);
|
||||
return proposals ? (proposals as Proposal[]) : [];
|
||||
};
|
||||
|
||||
const orderByDate = (arr: Proposal[]) =>
|
||||
orderBy(
|
||||
arr,
|
||||
[
|
||||
(p) => new Date(p.terms.enactmentDatetime || 0).getTime(), // has to be defaulted to 0 because new Date(null).getTime() -> NaN which is first when ordered.
|
||||
(p) => new Date(p.terms.closingDatetime).getTime(),
|
||||
(p) => p.id,
|
||||
],
|
||||
['desc', 'desc', 'desc']
|
||||
);
|
||||
|
||||
export const getNotRejectedProposals = (data?: ProposalsConnection) => {
|
||||
const proposals = getProposals(data);
|
||||
return flow([
|
||||
compact,
|
||||
(arr: Proposal[]) =>
|
||||
filter(arr, ({ state }) => state !== ProposalState.STATE_REJECTED),
|
||||
orderByDate,
|
||||
])(proposals);
|
||||
};
|
||||
|
||||
export const getRejectedProposals = (data?: ProposalsConnection) => {
|
||||
const proposals = getProposals(data);
|
||||
return flow([
|
||||
compact,
|
||||
(arr: Proposal[]) =>
|
||||
filter(arr, ({ state }) => state === ProposalState.STATE_REJECTED),
|
||||
orderByDate,
|
||||
])(proposals);
|
||||
};
|
@ -1,5 +1,5 @@
|
||||
export * from './date';
|
||||
export * from './number';
|
||||
export * from './size';
|
||||
export * from './truncate';
|
||||
export * from './strings';
|
||||
export * from './utils';
|
||||
|
29
libs/react-helpers/src/lib/format/strings.spec.ts
Normal file
29
libs/react-helpers/src/lib/format/strings.spec.ts
Normal file
@ -0,0 +1,29 @@
|
||||
import { truncateByChars, ELLIPSIS, shorten } from './strings';
|
||||
|
||||
describe('truncateByChars', () => {
|
||||
it.each([
|
||||
{
|
||||
i: '12345678901234567890',
|
||||
s: undefined,
|
||||
e: undefined,
|
||||
o: `123456${ELLIPSIS}567890`,
|
||||
},
|
||||
{ i: '12345678901234567890', s: 0, e: 10, o: `${ELLIPSIS}1234567890` },
|
||||
{ i: '123', s: 0, e: 4, o: '123' },
|
||||
])('should truncate given string by specific chars', ({ i, s, e, o }) => {
|
||||
expect(truncateByChars(i, s, e)).toStrictEqual(o);
|
||||
});
|
||||
});
|
||||
|
||||
describe('shorten', () => {
|
||||
it.each([
|
||||
{ i: '12345678901234567890', l: undefined, o: '12345678901234567890' },
|
||||
{ i: '12345678901234567890', l: 0, o: `12345678901234567890` },
|
||||
{ i: '12345678901234567890', l: 10, o: `123456789${ELLIPSIS}` },
|
||||
{ i: '12345678901234567890', l: 20, o: `1234567890123456789${ELLIPSIS}` },
|
||||
{ i: '12345678901234567890', l: 30, o: `12345678901234567890` },
|
||||
])('should shorten given string by specific limit', ({ i, l, o }) => {
|
||||
const output = shorten(i, l);
|
||||
expect(output).toStrictEqual(o);
|
||||
});
|
||||
});
|
19
libs/react-helpers/src/lib/format/strings.ts
Normal file
19
libs/react-helpers/src/lib/format/strings.ts
Normal file
@ -0,0 +1,19 @@
|
||||
export const ELLIPSIS = '\u2026';
|
||||
|
||||
export function truncateByChars(input: string, start = 6, end = 6) {
|
||||
// if the text is shorted than the total number of chars to show
|
||||
// no truncation is needed. Plus one is to account for the ellipsis
|
||||
if (input.length <= start + end + 1) {
|
||||
return input;
|
||||
}
|
||||
return input.slice(0, start) + ELLIPSIS + input.slice(-end);
|
||||
}
|
||||
|
||||
export function shorten(input: string, limit?: number) {
|
||||
if (!input || !limit || input.length < limit || limit <= 0) {
|
||||
return input;
|
||||
}
|
||||
const output = input.substring(0, limit - 1);
|
||||
const suffix = output.length < limit ? ELLIPSIS : '';
|
||||
return input.substring(0, limit - 1) + suffix;
|
||||
}
|
@ -1,14 +0,0 @@
|
||||
export function truncateByChars(s: string, startChars = 6, endChars = 6) {
|
||||
const ELLIPSIS = '\u2026';
|
||||
|
||||
// if the text is shorted than the total number of chars to show
|
||||
// no truncation is needed. Plus one is to account for the ellipsis
|
||||
if (s.length <= startChars + endChars + 1) {
|
||||
return s;
|
||||
}
|
||||
|
||||
const start = s.slice(0, startChars);
|
||||
const end = s.slice(-endChars);
|
||||
|
||||
return start + ELLIPSIS + end;
|
||||
}
|
Loading…
Reference in New Issue
Block a user