Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bf391bd5ef | ||
|
|
d77a8792fc | ||
|
|
64adba4eec |
@@ -77,7 +77,6 @@
|
||||
"fixStyle": "inline-type-imports"
|
||||
}
|
||||
],
|
||||
"@typescript-eslint/no-useless-constructor": 0,
|
||||
"curly": ["error", "multi-line"]
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
* text eol=lf
|
||||
*.png binary
|
||||
*.ico binary
|
||||
*.woff2 binary
|
||||
|
||||
+2
-1
@@ -59,4 +59,5 @@ apps/trading/e2e/logs/
|
||||
apps/trading/e2e/.pytest_cache/
|
||||
apps/trading/e2e/traces/
|
||||
|
||||
.nx/
|
||||
.nx/cache
|
||||
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
#!/bin/sh
|
||||
. "$(dirname "$0")/_/husky.sh"
|
||||
|
||||
# Auto-format all files
|
||||
yarn nx format:write
|
||||
|
||||
# Lint all staged files
|
||||
yarn lint-staged
|
||||
|
||||
+3
-3
@@ -1,8 +1,8 @@
|
||||
#!/bin/sh
|
||||
. "$(dirname "$0")/_/husky.sh"
|
||||
|
||||
# Lint all staged files - this brings more value as pre-commit
|
||||
# yarn nx format:check
|
||||
# Lint all staged files
|
||||
yarn nx format:check
|
||||
|
||||
# Test all projects with changes
|
||||
# yarn nx affected -t test --exclude trading
|
||||
yarn nx affected -t test --exclude trading
|
||||
|
||||
@@ -12,8 +12,7 @@ import { TendermintWebsocketProvider } from './contexts/websocket/tendermint-web
|
||||
import { Loader, Splash } from '@vegaprotocol/ui-toolkit';
|
||||
import { DEFAULT_CACHE_CONFIG } from '@vegaprotocol/apollo-client';
|
||||
import { RouterProvider } from 'react-router-dom';
|
||||
import { useRouterConfig } from './routes/router-config';
|
||||
import { createBrowserRouter } from 'react-router-dom';
|
||||
import { router } from './routes/router-config';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { Suspense } from 'react';
|
||||
|
||||
@@ -39,10 +38,7 @@ function App() {
|
||||
}
|
||||
>
|
||||
<Suspense fallback={splashLoading}>
|
||||
<RouterProvider
|
||||
router={createBrowserRouter(useRouterConfig())}
|
||||
fallbackElement={splashLoading}
|
||||
/>
|
||||
<RouterProvider router={router} fallbackElement={splashLoading} />
|
||||
</Suspense>
|
||||
</NodeGuard>
|
||||
<NodeSwitcherDialog
|
||||
|
||||
@@ -12,8 +12,7 @@ import { type VegaICellRendererParams } from '@vegaprotocol/datagrid';
|
||||
import { useRef, useLayoutEffect } from 'react';
|
||||
import { BREAKPOINT_MD } from '../../config/breakpoints';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { type ColDef } from 'ag-grid-community';
|
||||
import type { RowClickedEvent } from 'ag-grid-community';
|
||||
import { type RowClickedEvent, ColDef } from 'ag-grid-community';
|
||||
|
||||
type AssetsTableProps = {
|
||||
data: AssetFieldsFragment[] | null;
|
||||
@@ -24,7 +23,7 @@ export const AssetsTable = ({ data }: AssetsTableProps) => {
|
||||
const navigate = useNavigate();
|
||||
const ref = useRef<AgGridReact>(null);
|
||||
const showColumnsOnDesktop = () => {
|
||||
ref.current?.api.setColumnsVisible(
|
||||
ref.current?.columnApi.setColumnsVisible(
|
||||
['id', 'type', 'status'],
|
||||
window.innerWidth > BREAKPOINT_MD
|
||||
);
|
||||
|
||||
@@ -14,7 +14,7 @@ import { Routes } from '../../routes/route-names';
|
||||
import { NetworkSwitcher } from '@vegaprotocol/environment';
|
||||
import type { Navigable } from '../../routes/router-config';
|
||||
import { isNavigable } from '../../routes/router-config';
|
||||
import { useRouterConfig } from '../../routes/router-config';
|
||||
import { routerConfig } from '../../routes/router-config';
|
||||
import { useMemo } from 'react';
|
||||
import compact from 'lodash/compact';
|
||||
import { Search } from '../search';
|
||||
@@ -26,7 +26,6 @@ const routeToNavigationItem = (r: Navigable) => (
|
||||
);
|
||||
|
||||
export const Header = () => {
|
||||
const routerConfig = useRouterConfig();
|
||||
const isHome = Boolean(useMatch(Routes.HOME));
|
||||
const pages = routerConfig[0].children || [];
|
||||
const mainItems = compact(
|
||||
|
||||
@@ -60,7 +60,7 @@ const PartyLink = ({ id, truncate = false, ...props }: PartyLinkProps) => {
|
||||
}
|
||||
|
||||
return (
|
||||
<span>
|
||||
<span className="whitespace-nowrap">
|
||||
{useName && <Icon size={4} name="cube" className="mr-2" />}
|
||||
<Link
|
||||
className="underline font-mono"
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
query ExplorerProposal($id: ID!) {
|
||||
proposal(id: $id) {
|
||||
... on Proposal {
|
||||
id
|
||||
rationale {
|
||||
title
|
||||
description
|
||||
}
|
||||
id
|
||||
rationale {
|
||||
title
|
||||
description
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+5
-7
@@ -8,18 +8,16 @@ export type ExplorerProposalQueryVariables = Types.Exact<{
|
||||
}>;
|
||||
|
||||
|
||||
export type ExplorerProposalQuery = { __typename?: 'Query', proposal?: { __typename?: 'BatchProposal' } | { __typename?: 'Proposal', id?: string | null, rationale: { __typename?: 'ProposalRationale', title: string, description: string } } | null };
|
||||
export type ExplorerProposalQuery = { __typename?: 'Query', proposal?: { __typename?: 'Proposal', id?: string | null, rationale: { __typename?: 'ProposalRationale', title: string, description: string } } | null };
|
||||
|
||||
|
||||
export const ExplorerProposalDocument = gql`
|
||||
query ExplorerProposal($id: ID!) {
|
||||
proposal(id: $id) {
|
||||
... on Proposal {
|
||||
id
|
||||
rationale {
|
||||
title
|
||||
description
|
||||
}
|
||||
id
|
||||
rationale {
|
||||
title
|
||||
description
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
import {
|
||||
useExplorerProposalQuery,
|
||||
type ExplorerProposalQuery,
|
||||
} from './__generated__/Proposal';
|
||||
import { useExplorerProposalQuery } from './__generated__/Proposal';
|
||||
import { ExternalLink } from '@vegaprotocol/ui-toolkit';
|
||||
import { ENV } from '../../../config/env';
|
||||
import Hash from '../hash';
|
||||
|
||||
export type ProposalLinkProps = {
|
||||
id: string;
|
||||
text?: string;
|
||||
@@ -20,13 +16,8 @@ const ProposalLink = ({ id, text }: ProposalLinkProps) => {
|
||||
variables: { id },
|
||||
});
|
||||
|
||||
const proposal = data?.proposal as Extract<
|
||||
ExplorerProposalQuery['proposal'],
|
||||
{ __typename?: 'Proposal' }
|
||||
>;
|
||||
|
||||
const base = ENV.dataSources.governanceUrl;
|
||||
const label = proposal?.rationale.title || id;
|
||||
const label = data?.proposal?.rationale.title || id;
|
||||
|
||||
return (
|
||||
<ExternalLink href={`${base}/proposals/${id}`}>
|
||||
|
||||
@@ -29,7 +29,7 @@ export const MarketsTable = ({ data }: MarketsTableProps) => {
|
||||
const gridRef = useRef<AgGridReact>(null);
|
||||
useLayoutEffect(() => {
|
||||
const showColumnsOnDesktop = () => {
|
||||
gridRef.current?.api.setColumnsVisible(
|
||||
gridRef.current?.columnApi.setColumnsVisible(
|
||||
['id', 'state', 'asset'],
|
||||
window.innerWidth > BREAKPOINT_MD
|
||||
);
|
||||
|
||||
@@ -6,7 +6,6 @@ import { Time } from '../time';
|
||||
import { sideText, statusText, tifFull, tifShort } from './lib/order-labels';
|
||||
import SizeInMarket from '../size-in-market/size-in-market';
|
||||
import { TxOrderPeggedReference } from '../txs/details/order/tx-order-peg';
|
||||
import { OrderTypeMapping } from '@vegaprotocol/types';
|
||||
|
||||
export interface DeterministicOrderDetailsProps {
|
||||
id: string;
|
||||
@@ -60,7 +59,7 @@ const DeterministicOrderDetails = ({
|
||||
const o = data.orderByID;
|
||||
return (
|
||||
<div className={wrapperClasses}>
|
||||
<div className="mb-0">
|
||||
<div className="mb-12 lg:mb-0">
|
||||
<div className="relative block px-3 py-6 md:px-6 lg:-mr-7">
|
||||
<h2 className="text-3xl font-bold mb-4 display-5">
|
||||
<abbr title={tifFull[o.timeInForce]} className="bb-dotted mr-2">
|
||||
@@ -70,7 +69,7 @@ const DeterministicOrderDetails = ({
|
||||
<span className="mx-5 text-base">@</span>
|
||||
<PriceInMarket price={o.price} marketId={o.market.id} />
|
||||
</h2>
|
||||
<p className="text-gray-400 dark:text-gray-600">
|
||||
<p className="text-gray-200">
|
||||
In <MarketLink id={o.market.id} /> at <Time date={o.createdAt} />.
|
||||
</p>
|
||||
{o.peggedOrder ? (
|
||||
@@ -84,14 +83,15 @@ const DeterministicOrderDetails = ({
|
||||
/>
|
||||
</p>
|
||||
) : null}
|
||||
|
||||
{o.reference ? (
|
||||
<p className="text-gray-500 mt-4">
|
||||
<span>{t('Reference')}</span>: {o.reference}
|
||||
</p>
|
||||
) : null}
|
||||
<div className="grid grid-cols-2 md:grid-cols-5 gap-x-6 mt-4">
|
||||
<div className="mb-6 md:mb-0">
|
||||
<h2 className="text-2xl font-bold text-dark mb-0 md:mb-4">
|
||||
<div className="grid md:grid-cols-4 gap-x-6 mt-4">
|
||||
<div className="mb-12 md:mb-0">
|
||||
<h2 className="text-2xl font-bold text-dark mb-4">
|
||||
{t('Status')}
|
||||
</h2>
|
||||
<h5 className="text-lg font-medium text-gray-500 mb-0 capitalize">
|
||||
@@ -99,33 +99,21 @@ const DeterministicOrderDetails = ({
|
||||
</h5>
|
||||
</div>
|
||||
|
||||
<div className="mb-6 md:mb-0">
|
||||
<h2 className="text-2xl font-bold text-dark mb-0 md:mb-4">
|
||||
{t('Size')}
|
||||
</h2>
|
||||
<div className="mb-12 md:mb-0">
|
||||
<h2 className="text-2xl font-bold text-dark mb-4">{t('Size')}</h2>
|
||||
<h5 className="text-lg font-medium text-gray-500 mb-0">
|
||||
<SizeInMarket size={o.size} marketId={o.market.id} />
|
||||
</h5>
|
||||
</div>
|
||||
|
||||
<div className="mb-6 md:mb-0">
|
||||
<h2 className="text-2xl font-bold text-dark mb-0 md:mb-4">
|
||||
<div className="">
|
||||
<h2 className="text-2xl font-bold text-dark mb-4">
|
||||
{t('Version')}
|
||||
</h2>
|
||||
<h5 className="text-lg font-medium text-gray-500 mb-0">
|
||||
{o.version}
|
||||
</h5>
|
||||
</div>
|
||||
{o.type ? (
|
||||
<div className="mb-6 md:mb-0">
|
||||
<h2 className="text-2xl font-bold text-dark mb-0 md:mb-4">
|
||||
{t('Type')}
|
||||
</h2>
|
||||
<h5 className="text-lg font-medium text-gray-500 mb-0">
|
||||
{OrderTypeMapping[o.type]}
|
||||
</h5>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -8,8 +8,7 @@ import {
|
||||
type VegaValueFormatterParams,
|
||||
} from '@vegaprotocol/datagrid';
|
||||
import { useLayoutEffect, useMemo, useRef, useState } from 'react';
|
||||
import { type ColDef } from 'ag-grid-community';
|
||||
import type { RowClickedEvent } from 'ag-grid-community';
|
||||
import { type RowClickedEvent, ColDef } from 'ag-grid-community';
|
||||
import { getDateTimeFormat } from '@vegaprotocol/utils';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import {
|
||||
@@ -44,11 +43,11 @@ export const ProposalsTable = ({ data }: ProposalsTableProps) => {
|
||||
const gridRef = useRef<AgGridReact>(null);
|
||||
useLayoutEffect(() => {
|
||||
const showColumnsOnDesktop = () => {
|
||||
gridRef.current?.api.setColumnsVisible(
|
||||
gridRef.current?.columnApi.setColumnsVisible(
|
||||
['voting', 'cDate', 'eDate', 'type'],
|
||||
window.innerWidth > BREAKPOINT_MD
|
||||
);
|
||||
gridRef.current?.api.setColumnWidth(
|
||||
gridRef.current?.columnApi.setColumnWidth(
|
||||
'actions',
|
||||
window.innerWidth > BREAKPOINT_MD ? 221 : 80
|
||||
);
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
import { useState } from 'react';
|
||||
import type { BlockExplorerTransactionResult } from '../../routes/types/block-explorer-response';
|
||||
import {
|
||||
CopyWithTooltip,
|
||||
VegaIcon,
|
||||
VegaIconNames,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
|
||||
interface SignatureProps {
|
||||
signature: BlockExplorerTransactionResult['signature'];
|
||||
}
|
||||
|
||||
const valueClass =
|
||||
'font-mono px-2.5 py-0.5 text-xs max-w-[200px] cursor-pointer';
|
||||
const valueClassClosed = 'text-ellipsis overflow-hidden';
|
||||
const valueClassOpen = 'break-words text-left';
|
||||
|
||||
/**
|
||||
* Viewer component for a vega signature. Featuers copy and pasting, truncation
|
||||
*
|
||||
* @param signature
|
||||
*/
|
||||
export const Signature = ({ signature }: SignatureProps) => {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
|
||||
if (!signature || !signature.value || !signature.version || !signature.algo) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="inline-flex border rounded signature-component relative pr-[20px]">
|
||||
<div
|
||||
className="bg-gray-100 px-2.5 py-0.5 text-xs text-gray-500 select-none cursor-default"
|
||||
title={`${signature.algo}`}
|
||||
>
|
||||
<span>v{signature.version}</span>
|
||||
</div>
|
||||
<div
|
||||
className={
|
||||
isOpen
|
||||
? `${valueClass} ${valueClassOpen}`
|
||||
: `${valueClass} ${valueClassClosed}`
|
||||
}
|
||||
>
|
||||
<CopyWithTooltip text={signature.value}>
|
||||
<span title={signature.value}>{signature.value}</span>
|
||||
</CopyWithTooltip>
|
||||
</div>
|
||||
<button
|
||||
onClick={() => setIsOpen(!isOpen)}
|
||||
className="absolute top-[-3px] right-0 pr-2"
|
||||
title={t('Show full signature')}
|
||||
>
|
||||
<VegaIcon name={isOpen ? VegaIconNames.EYE_OFF : VegaIconNames.EYE} />
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -1,9 +1,7 @@
|
||||
query ExplorerProposalStatus($id: ID!) {
|
||||
proposal(id: $id) {
|
||||
... on Proposal {
|
||||
id
|
||||
state
|
||||
rejectionReason
|
||||
}
|
||||
id
|
||||
state
|
||||
rejectionReason
|
||||
}
|
||||
}
|
||||
|
||||
+4
-6
@@ -8,17 +8,15 @@ export type ExplorerProposalStatusQueryVariables = Types.Exact<{
|
||||
}>;
|
||||
|
||||
|
||||
export type ExplorerProposalStatusQuery = { __typename?: 'Query', proposal?: { __typename?: 'BatchProposal' } | { __typename?: 'Proposal', id?: string | null, state: Types.ProposalState, rejectionReason?: Types.ProposalRejectionReason | null } | null };
|
||||
export type ExplorerProposalStatusQuery = { __typename?: 'Query', proposal?: { __typename?: 'Proposal', id?: string | null, state: Types.ProposalState, rejectionReason?: Types.ProposalRejectionReason | null } | null };
|
||||
|
||||
|
||||
export const ExplorerProposalStatusDocument = gql`
|
||||
query ExplorerProposalStatus($id: ID!) {
|
||||
proposal(id: $id) {
|
||||
... on Proposal {
|
||||
id
|
||||
state
|
||||
rejectionReason
|
||||
}
|
||||
id
|
||||
state
|
||||
rejectionReason
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
@@ -14,18 +14,16 @@ export function format(date: string | undefined, def: string) {
|
||||
return new Date().toLocaleDateString() || def;
|
||||
}
|
||||
|
||||
type Proposal = Extract<
|
||||
ExplorerProposalStatusQuery['proposal'],
|
||||
{ __typename?: 'Proposal' }
|
||||
>;
|
||||
|
||||
export function getDate(proposal: Proposal | undefined, terms: Terms): string {
|
||||
export function getDate(
|
||||
data: ExplorerProposalStatusQuery | undefined,
|
||||
terms: Terms
|
||||
): string {
|
||||
const DEFAULT = t('Unknown');
|
||||
if (!proposal?.state) {
|
||||
if (!data?.proposal?.state) {
|
||||
return DEFAULT;
|
||||
}
|
||||
|
||||
switch (proposal.state) {
|
||||
switch (data.proposal.state) {
|
||||
case 'STATE_DECLINED':
|
||||
return `${t('Rejected on')}: ${format(terms.closingTimestamp, DEFAULT)}`;
|
||||
case 'STATE_ENACTED':
|
||||
@@ -64,11 +62,9 @@ export const ProposalDate = ({ terms, id }: ProposalDateProps) => {
|
||||
},
|
||||
});
|
||||
|
||||
const proposal = data?.proposal as Proposal;
|
||||
|
||||
return (
|
||||
<Lozenge className="font-sans text-xs float-right">
|
||||
{getDate(proposal, terms)}
|
||||
{getDate(data, terms)}
|
||||
</Lozenge>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -2,8 +2,17 @@ import { Icon, Tooltip } from '@vegaprotocol/ui-toolkit';
|
||||
import type { IconProps } from '@vegaprotocol/ui-toolkit';
|
||||
import { useExplorerProposalStatusQuery } from './__generated__/Proposal';
|
||||
import type { ExplorerProposalStatusQuery } from './__generated__/Proposal';
|
||||
import type * as Apollo from '@apollo/client';
|
||||
import type * as Types from '@vegaprotocol/types';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
|
||||
type ProposalQueryResult = Apollo.QueryResult<
|
||||
ExplorerProposalStatusQuery,
|
||||
Types.Exact<{
|
||||
id: string;
|
||||
}>
|
||||
>;
|
||||
|
||||
interface ProposalStatusIconProps {
|
||||
id: string;
|
||||
}
|
||||
@@ -20,38 +29,29 @@ type IconAndLabel = {
|
||||
* @param data a data result from useExplorerProposalStatusQuery
|
||||
* @returns Icon name
|
||||
*/
|
||||
export function useIconAndLabelForStatus(id: string): IconAndLabel {
|
||||
const { data, loading, error } = useExplorerProposalStatusQuery({
|
||||
variables: {
|
||||
id,
|
||||
},
|
||||
});
|
||||
|
||||
const proposal = data?.proposal as Extract<
|
||||
ExplorerProposalStatusQuery['proposal'],
|
||||
{ __typename?: 'Proposal' }
|
||||
>;
|
||||
|
||||
export function getIconAndLabelForStatus(
|
||||
res: ProposalQueryResult
|
||||
): IconAndLabel {
|
||||
const DEFAULT: IconAndLabel = {
|
||||
icon: 'error',
|
||||
label: t('Proposal state unknown'),
|
||||
};
|
||||
|
||||
if (loading) {
|
||||
if (res.loading) {
|
||||
return {
|
||||
icon: 'more',
|
||||
label: t('Loading data'),
|
||||
};
|
||||
}
|
||||
|
||||
if (!data?.proposal || error) {
|
||||
if (!res?.data?.proposal || res.error) {
|
||||
return {
|
||||
icon: 'error',
|
||||
label: error?.message || DEFAULT.label,
|
||||
label: res.error?.message || DEFAULT.label,
|
||||
};
|
||||
}
|
||||
|
||||
switch (proposal.state) {
|
||||
switch (res.data.proposal.state) {
|
||||
case 'STATE_DECLINED':
|
||||
return {
|
||||
icon: 'stop',
|
||||
@@ -99,7 +99,13 @@ export function useIconAndLabelForStatus(id: string): IconAndLabel {
|
||||
/**
|
||||
*/
|
||||
export const ProposalStatusIcon = ({ id }: ProposalStatusIconProps) => {
|
||||
const { icon, label } = useIconAndLabelForStatus(id);
|
||||
const { icon, label } = getIconAndLabelForStatus(
|
||||
useExplorerProposalStatusQuery({
|
||||
variables: {
|
||||
id,
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="float-left mr-3">
|
||||
|
||||
@@ -9,7 +9,6 @@ import { Time } from '../../../time';
|
||||
import { ChainResponseCode } from '../chain-response-code/chain-reponse.code';
|
||||
import { TxDataView } from '../../tx-data-view';
|
||||
import Hash from '../../../links/hash';
|
||||
import { Signature } from '../../../signature/signature';
|
||||
|
||||
interface TxDetailsSharedProps {
|
||||
txData: BlockExplorerTransactionResult | undefined;
|
||||
@@ -76,12 +75,6 @@ export const TxDetailsShared = ({
|
||||
<BlockLink height={height} />
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow modifier="bordered">
|
||||
<TableCell {...sharedHeaderProps}>{t('Signature')}</TableCell>
|
||||
<TableCell>
|
||||
<Signature signature={txData.signature} />
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow modifier="bordered">
|
||||
<TableCell {...sharedHeaderProps}>{t('Time')}</TableCell>
|
||||
<TableCell>
|
||||
|
||||
@@ -80,12 +80,6 @@ export function getLabelForOrderType(
|
||||
if (command.orderSubmission.icebergOpts) {
|
||||
return 'Iceberg';
|
||||
}
|
||||
if (command.orderSubmission.type === 'TYPE_MARKET') {
|
||||
return 'Market order';
|
||||
}
|
||||
if (command.orderSubmission.type === 'TYPE_LIMIT') {
|
||||
return 'Limit order';
|
||||
}
|
||||
}
|
||||
return 'Order';
|
||||
}
|
||||
|
||||
@@ -98,8 +98,6 @@ describe('TxDetailsTransfer', () => {
|
||||
},
|
||||
},
|
||||
signature: {
|
||||
version: '1',
|
||||
algo: 'vega/ed25519',
|
||||
value:
|
||||
'610c2e196a7d4fed4413b9e82af267b1ff3e30e943df3a3d28096fd60604d430d752fbaf6dd4f84d496be78885bb6118f40560bff7832c06bd7a3d67b718b700',
|
||||
},
|
||||
|
||||
@@ -20,8 +20,6 @@ const generateTxs = (number: number): BlockExplorerTransactionResult[] => {
|
||||
'4b782482f587d291e8614219eb9a5ee9280fa2c58982dee71d976782a9be1964',
|
||||
type: 'Submit Order',
|
||||
signature: {
|
||||
version: '1',
|
||||
algo: 'vega/ed25519',
|
||||
value: '123',
|
||||
},
|
||||
code: 0,
|
||||
|
||||
@@ -54,7 +54,7 @@ const Block = () => {
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
{blockData && 'result' in blockData && (
|
||||
{blockData && (
|
||||
<>
|
||||
<TableWithTbody className="mb-8">
|
||||
<TableRow modifier="bordered">
|
||||
|
||||
@@ -73,7 +73,7 @@ export const Layout = () => {
|
||||
<ProtocolUpgradeInProgressNotification />
|
||||
</div>
|
||||
<div className={fixedWidthClasses}>
|
||||
<main className="md:p-4">
|
||||
<main className="p-4">
|
||||
{!isHome && <BreadcrumbsContainer className="mb-4" />}
|
||||
<Outlet />
|
||||
</main>
|
||||
|
||||
@@ -18,6 +18,7 @@ import { t } from '@vegaprotocol/i18n';
|
||||
import { Routes } from './route-names';
|
||||
import { NetworkParameters } from './network-parameters';
|
||||
import type { Params, RouteObject } from 'react-router-dom';
|
||||
import { createBrowserRouter } from 'react-router-dom';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { MarketPage, MarketsPage } from './markets';
|
||||
import type { ReactNode } from 'react';
|
||||
@@ -28,7 +29,7 @@ import { truncateMiddle } from '@vegaprotocol/ui-toolkit';
|
||||
import { remove0x } from '@vegaprotocol/utils';
|
||||
import { PartyAccountsByAsset } from './parties/id/accounts';
|
||||
import { Disclaimer } from './pages/disclaimer';
|
||||
import { useFeatureFlags } from '@vegaprotocol/environment';
|
||||
import { FLAGS } from '@vegaprotocol/environment';
|
||||
import RestrictedPage from './restricted';
|
||||
|
||||
export type Navigable = {
|
||||
@@ -59,315 +60,311 @@ type Route = RouteItem & {
|
||||
children?: RouteItem[];
|
||||
};
|
||||
|
||||
export const useRouterConfig = () => {
|
||||
const featureFlags = useFeatureFlags((state) => state.flags);
|
||||
|
||||
const partiesRoutes: Route[] = featureFlags.EXPLORER_PARTIES
|
||||
? [
|
||||
{
|
||||
path: Routes.PARTIES,
|
||||
element: <Party />,
|
||||
handle: {
|
||||
name: t('Parties'),
|
||||
text: t('Parties'),
|
||||
breadcrumb: () => <Link to={Routes.PARTIES}>{t('Parties')}</Link>,
|
||||
const partiesRoutes: Route[] = FLAGS.EXPLORER_PARTIES
|
||||
? [
|
||||
{
|
||||
path: Routes.PARTIES,
|
||||
element: <Party />,
|
||||
handle: {
|
||||
name: t('Parties'),
|
||||
text: t('Parties'),
|
||||
breadcrumb: () => <Link to={Routes.PARTIES}>{t('Parties')}</Link>,
|
||||
},
|
||||
children: [
|
||||
{
|
||||
index: true,
|
||||
element: <Parties />,
|
||||
},
|
||||
children: [
|
||||
{
|
||||
index: true,
|
||||
element: <Parties />,
|
||||
},
|
||||
{
|
||||
path: ':party',
|
||||
element: <Party />,
|
||||
{
|
||||
path: ':party',
|
||||
element: <Party />,
|
||||
|
||||
children: [
|
||||
{
|
||||
index: true,
|
||||
element: <PartySingle />,
|
||||
handle: {
|
||||
breadcrumb: (params: Params<string>) => (
|
||||
<Link to={linkTo(Routes.PARTIES, params.party)}>
|
||||
{truncateMiddle(params.party as string)}
|
||||
</Link>
|
||||
),
|
||||
},
|
||||
children: [
|
||||
{
|
||||
index: true,
|
||||
element: <PartySingle />,
|
||||
handle: {
|
||||
breadcrumb: (params: Params<string>) => (
|
||||
<Link to={linkTo(Routes.PARTIES, params.party)}>
|
||||
{truncateMiddle(params.party as string)}
|
||||
</Link>
|
||||
),
|
||||
},
|
||||
{
|
||||
path: 'assets',
|
||||
element: <Party />,
|
||||
handle: {
|
||||
breadcrumb: (params: Params<string>) => (
|
||||
<Link to={linkTo(Routes.PARTIES, params.party)}>
|
||||
{truncateMiddle(params.party as string)}
|
||||
</Link>
|
||||
),
|
||||
},
|
||||
children: [
|
||||
{
|
||||
index: true,
|
||||
element: <PartyAccountsByAsset />,
|
||||
handle: {
|
||||
breadcrumb: () => {
|
||||
return t('Assets');
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'assets',
|
||||
element: <Party />,
|
||||
handle: {
|
||||
breadcrumb: (params: Params<string>) => (
|
||||
<Link to={linkTo(Routes.PARTIES, params.party)}>
|
||||
{truncateMiddle(params.party as string)}
|
||||
</Link>
|
||||
),
|
||||
},
|
||||
children: [
|
||||
{
|
||||
index: true,
|
||||
element: <PartyAccountsByAsset />,
|
||||
handle: {
|
||||
breadcrumb: () => {
|
||||
return t('Assets');
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
: [];
|
||||
|
||||
const assetsRoutes: Route[] = featureFlags.EXPLORER_ASSETS
|
||||
? [
|
||||
{
|
||||
path: Routes.ASSETS,
|
||||
handle: {
|
||||
name: t('Assets'),
|
||||
text: t('Assets'),
|
||||
breadcrumb: () => <Link to={Routes.ASSETS}>{t('Assets')}</Link>,
|
||||
},
|
||||
children: [
|
||||
{
|
||||
index: true,
|
||||
element: <AssetsPage />,
|
||||
},
|
||||
{
|
||||
path: ':assetId',
|
||||
element: <AssetPage />,
|
||||
handle: {
|
||||
breadcrumb: (params: Params<string>) => (
|
||||
<AssetLink assetId={params.assetId as string} />
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
: [];
|
||||
|
||||
const genesisRoutes: Route[] = featureFlags.EXPLORER_GENESIS
|
||||
? [
|
||||
{
|
||||
path: Routes.GENESIS,
|
||||
handle: {
|
||||
name: t('Genesis'),
|
||||
text: t('Genesis Parameters'),
|
||||
breadcrumb: () => (
|
||||
<Link to={Routes.GENESIS}>{t('Genesis Parameters')}</Link>
|
||||
),
|
||||
],
|
||||
},
|
||||
element: <Genesis />,
|
||||
},
|
||||
]
|
||||
: [];
|
||||
],
|
||||
},
|
||||
]
|
||||
: [];
|
||||
|
||||
const governanceRoutes: Route[] = featureFlags.EXPLORER_GOVERNANCE
|
||||
? [
|
||||
{
|
||||
path: Routes.GOVERNANCE,
|
||||
handle: {
|
||||
name: t('Governance proposals'),
|
||||
text: t('Governance Proposals'),
|
||||
breadcrumb: () => (
|
||||
<Link to={Routes.GOVERNANCE}>{t('Governance Proposals')}</Link>
|
||||
),
|
||||
},
|
||||
element: <Proposals />,
|
||||
const assetsRoutes: Route[] = FLAGS.EXPLORER_ASSETS
|
||||
? [
|
||||
{
|
||||
path: Routes.ASSETS,
|
||||
handle: {
|
||||
name: t('Assets'),
|
||||
text: t('Assets'),
|
||||
breadcrumb: () => <Link to={Routes.ASSETS}>{t('Assets')}</Link>,
|
||||
},
|
||||
]
|
||||
: [];
|
||||
|
||||
const marketsRoutes: Route[] = featureFlags.EXPLORER_MARKETS
|
||||
? [
|
||||
{
|
||||
path: Routes.MARKETS,
|
||||
handle: {
|
||||
name: t('Markets'),
|
||||
text: t('Markets'),
|
||||
breadcrumb: () => <Link to={Routes.MARKETS}>{t('Markets')}</Link>,
|
||||
},
|
||||
children: [
|
||||
{
|
||||
index: true,
|
||||
element: <MarketsPage />,
|
||||
},
|
||||
{
|
||||
path: ':marketId',
|
||||
element: <MarketPage />,
|
||||
handle: {
|
||||
breadcrumb: (params: Params<string>) => (
|
||||
<MarketLink id={params.marketId as string} />
|
||||
),
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
: [];
|
||||
|
||||
const networkParametersRoutes: Route[] =
|
||||
featureFlags.EXPLORER_NETWORK_PARAMETERS
|
||||
? [
|
||||
children: [
|
||||
{
|
||||
path: Routes.NETWORK_PARAMETERS,
|
||||
index: true,
|
||||
element: <AssetsPage />,
|
||||
},
|
||||
{
|
||||
path: ':assetId',
|
||||
element: <AssetPage />,
|
||||
handle: {
|
||||
name: t('NetworkParameters'),
|
||||
text: t('Network Parameters'),
|
||||
breadcrumb: () => (
|
||||
<Link to={Routes.NETWORK_PARAMETERS}>
|
||||
{t('Network Parameters')}
|
||||
breadcrumb: (params: Params<string>) => (
|
||||
<AssetLink assetId={params.assetId as string} />
|
||||
),
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
: [];
|
||||
|
||||
const genesisRoutes: Route[] = FLAGS.EXPLORER_GENESIS
|
||||
? [
|
||||
{
|
||||
path: Routes.GENESIS,
|
||||
handle: {
|
||||
name: t('Genesis'),
|
||||
text: t('Genesis Parameters'),
|
||||
breadcrumb: () => (
|
||||
<Link to={Routes.GENESIS}>{t('Genesis Parameters')}</Link>
|
||||
),
|
||||
},
|
||||
element: <Genesis />,
|
||||
},
|
||||
]
|
||||
: [];
|
||||
|
||||
const governanceRoutes: Route[] = FLAGS.EXPLORER_GOVERNANCE
|
||||
? [
|
||||
{
|
||||
path: Routes.GOVERNANCE,
|
||||
handle: {
|
||||
name: t('Governance proposals'),
|
||||
text: t('Governance Proposals'),
|
||||
breadcrumb: () => (
|
||||
<Link to={Routes.GOVERNANCE}>{t('Governance Proposals')}</Link>
|
||||
),
|
||||
},
|
||||
element: <Proposals />,
|
||||
},
|
||||
]
|
||||
: [];
|
||||
|
||||
const marketsRoutes: Route[] = FLAGS.EXPLORER_MARKETS
|
||||
? [
|
||||
{
|
||||
path: Routes.MARKETS,
|
||||
handle: {
|
||||
name: t('Markets'),
|
||||
text: t('Markets'),
|
||||
breadcrumb: () => <Link to={Routes.MARKETS}>{t('Markets')}</Link>,
|
||||
},
|
||||
children: [
|
||||
{
|
||||
index: true,
|
||||
element: <MarketsPage />,
|
||||
},
|
||||
{
|
||||
path: ':marketId',
|
||||
element: <MarketPage />,
|
||||
handle: {
|
||||
breadcrumb: (params: Params<string>) => (
|
||||
<MarketLink id={params.marketId as string} />
|
||||
),
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
: [];
|
||||
|
||||
const networkParametersRoutes: Route[] = FLAGS.EXPLORER_NETWORK_PARAMETERS
|
||||
? [
|
||||
{
|
||||
path: Routes.NETWORK_PARAMETERS,
|
||||
handle: {
|
||||
name: t('NetworkParameters'),
|
||||
text: t('Network Parameters'),
|
||||
breadcrumb: () => (
|
||||
<Link to={Routes.NETWORK_PARAMETERS}>
|
||||
{t('Network Parameters')}
|
||||
</Link>
|
||||
),
|
||||
},
|
||||
element: <NetworkParameters />,
|
||||
},
|
||||
]
|
||||
: [];
|
||||
|
||||
const validators: Route[] = FLAGS.EXPLORER_VALIDATORS
|
||||
? [
|
||||
{
|
||||
path: Routes.VALIDATORS,
|
||||
handle: {
|
||||
name: t('Validators'),
|
||||
text: t('Validators'),
|
||||
breadcrumb: () => (
|
||||
<Link to={Routes.VALIDATORS}>{t('Validators')}</Link>
|
||||
),
|
||||
},
|
||||
element: <ValidatorsPage />,
|
||||
},
|
||||
]
|
||||
: [];
|
||||
|
||||
const linkTo = (...segments: (string | undefined)[]) =>
|
||||
compact(segments).join('/');
|
||||
|
||||
export const routerConfig: Route[] = [
|
||||
{
|
||||
path: Routes.HOME,
|
||||
element: <Layout />,
|
||||
handle: {
|
||||
name: t('Home'),
|
||||
text: t('Home'),
|
||||
breadcrumb: () => <Link to={Routes.HOME}>{t('Home')}</Link>,
|
||||
},
|
||||
errorElement: <ErrorBoundary />,
|
||||
children: [
|
||||
{
|
||||
index: true,
|
||||
element: <Home />,
|
||||
},
|
||||
{
|
||||
path: Routes.TX,
|
||||
handle: {
|
||||
name: t('Txs'),
|
||||
text: t('Transactions'),
|
||||
breadcrumb: () => <Link to={Routes.TX}>{t('Transactions')}</Link>,
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: ':txHash',
|
||||
element: <Tx />,
|
||||
handle: {
|
||||
breadcrumb: (params: Params<string>) => (
|
||||
<Link to={linkTo(Routes.TX, params.txHash)}>
|
||||
{truncateMiddle(remove0x(params.txHash as string))}
|
||||
</Link>
|
||||
),
|
||||
},
|
||||
element: <NetworkParameters />,
|
||||
},
|
||||
]
|
||||
: [];
|
||||
|
||||
const validators: Route[] = featureFlags.EXPLORER_VALIDATORS
|
||||
? [
|
||||
{
|
||||
path: Routes.VALIDATORS,
|
||||
handle: {
|
||||
name: t('Validators'),
|
||||
text: t('Validators'),
|
||||
breadcrumb: () => (
|
||||
<Link to={Routes.VALIDATORS}>{t('Validators')}</Link>
|
||||
),
|
||||
{
|
||||
index: true,
|
||||
element: <TxsList />,
|
||||
},
|
||||
element: <ValidatorsPage />,
|
||||
},
|
||||
]
|
||||
: [];
|
||||
|
||||
const linkTo = (...segments: (string | undefined)[]) =>
|
||||
compact(segments).join('/');
|
||||
|
||||
const routerConfig: Route[] = [
|
||||
{
|
||||
path: Routes.HOME,
|
||||
element: <Layout />,
|
||||
handle: {
|
||||
name: t('Home'),
|
||||
text: t('Home'),
|
||||
breadcrumb: () => <Link to={Routes.HOME}>{t('Home')}</Link>,
|
||||
],
|
||||
},
|
||||
errorElement: <ErrorBoundary />,
|
||||
children: [
|
||||
{
|
||||
index: true,
|
||||
element: <Home />,
|
||||
{
|
||||
path: Routes.BLOCKS,
|
||||
handle: {
|
||||
name: t('Blocks'),
|
||||
text: t('Blocks'),
|
||||
breadcrumb: () => <Link to={Routes.BLOCKS}>{t('Blocks')}</Link>,
|
||||
},
|
||||
{
|
||||
path: Routes.TX,
|
||||
handle: {
|
||||
name: t('Txs'),
|
||||
text: t('Transactions'),
|
||||
breadcrumb: () => <Link to={Routes.TX}>{t('Transactions')}</Link>,
|
||||
element: <BlockPage />,
|
||||
children: [
|
||||
{
|
||||
index: true,
|
||||
element: <Blocks />,
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: ':txHash',
|
||||
element: <Tx />,
|
||||
handle: {
|
||||
breadcrumb: (params: Params<string>) => (
|
||||
<Link to={linkTo(Routes.TX, params.txHash)}>
|
||||
{truncateMiddle(remove0x(params.txHash as string))}
|
||||
</Link>
|
||||
),
|
||||
},
|
||||
{
|
||||
path: ':block',
|
||||
element: <Block />,
|
||||
handle: {
|
||||
breadcrumb: (params: Params<string>) => (
|
||||
<Link to={linkTo(Routes.BLOCKS, params.block)}>
|
||||
{params.block}
|
||||
</Link>
|
||||
),
|
||||
},
|
||||
{
|
||||
index: true,
|
||||
element: <TxsList />,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: Routes.BLOCKS,
|
||||
handle: {
|
||||
name: t('Blocks'),
|
||||
text: t('Blocks'),
|
||||
breadcrumb: () => <Link to={Routes.BLOCKS}>{t('Blocks')}</Link>,
|
||||
},
|
||||
element: <BlockPage />,
|
||||
children: [
|
||||
{
|
||||
index: true,
|
||||
element: <Blocks />,
|
||||
},
|
||||
{
|
||||
path: ':block',
|
||||
element: <Block />,
|
||||
handle: {
|
||||
breadcrumb: (params: Params<string>) => (
|
||||
<Link to={linkTo(Routes.BLOCKS, params.block)}>
|
||||
{params.block}
|
||||
</Link>
|
||||
),
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: Routes.ORACLES,
|
||||
handle: {
|
||||
name: t('Oracles'),
|
||||
text: t('Oracles'),
|
||||
breadcrumb: () => <Link to={Routes.ORACLES}>{t('Oracles')}</Link>,
|
||||
},
|
||||
element: <OraclePage />,
|
||||
children: [
|
||||
{
|
||||
index: true,
|
||||
element: <Oracles />,
|
||||
},
|
||||
{
|
||||
path: ':id',
|
||||
element: <Oracle />,
|
||||
handle: {
|
||||
breadcrumb: (params: Params<string>) => (
|
||||
<Link to={linkTo(Routes.ORACLES, params.id)}>
|
||||
{truncateMiddle(params.id as string)}
|
||||
</Link>
|
||||
),
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: Routes.DISCLAIMER,
|
||||
element: <Disclaimer />,
|
||||
handle: {
|
||||
name: t('Disclaimer'),
|
||||
text: t('Disclaimer'),
|
||||
breadcrumb: () => (
|
||||
<Link to={Routes.DISCLAIMER}>{t('Disclaimer')}</Link>
|
||||
),
|
||||
},
|
||||
},
|
||||
...partiesRoutes,
|
||||
...assetsRoutes,
|
||||
...genesisRoutes,
|
||||
...governanceRoutes,
|
||||
...marketsRoutes,
|
||||
...networkParametersRoutes,
|
||||
...validators,
|
||||
],
|
||||
},
|
||||
{
|
||||
path: Routes.RESTRICTED,
|
||||
element: <RestrictedPage />,
|
||||
handle: {
|
||||
name: t('Restricted'),
|
||||
text: t('Restricted'),
|
||||
],
|
||||
},
|
||||
{
|
||||
path: Routes.ORACLES,
|
||||
handle: {
|
||||
name: t('Oracles'),
|
||||
text: t('Oracles'),
|
||||
breadcrumb: () => <Link to={Routes.ORACLES}>{t('Oracles')}</Link>,
|
||||
},
|
||||
element: <OraclePage />,
|
||||
children: [
|
||||
{
|
||||
index: true,
|
||||
element: <Oracles />,
|
||||
},
|
||||
{
|
||||
path: ':id',
|
||||
element: <Oracle />,
|
||||
handle: {
|
||||
breadcrumb: (params: Params<string>) => (
|
||||
<Link to={linkTo(Routes.ORACLES, params.id)}>
|
||||
{truncateMiddle(params.id as string)}
|
||||
</Link>
|
||||
),
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: Routes.DISCLAIMER,
|
||||
element: <Disclaimer />,
|
||||
handle: {
|
||||
name: t('Disclaimer'),
|
||||
text: t('Disclaimer'),
|
||||
breadcrumb: () => (
|
||||
<Link to={Routes.DISCLAIMER}>{t('Disclaimer')}</Link>
|
||||
),
|
||||
},
|
||||
},
|
||||
...partiesRoutes,
|
||||
...assetsRoutes,
|
||||
...genesisRoutes,
|
||||
...governanceRoutes,
|
||||
...marketsRoutes,
|
||||
...networkParametersRoutes,
|
||||
...validators,
|
||||
],
|
||||
},
|
||||
{
|
||||
path: Routes.RESTRICTED,
|
||||
element: <RestrictedPage />,
|
||||
handle: {
|
||||
name: t('Restricted'),
|
||||
text: t('Restricted'),
|
||||
},
|
||||
];
|
||||
return routerConfig;
|
||||
};
|
||||
},
|
||||
];
|
||||
|
||||
export const router = createBrowserRouter(routerConfig);
|
||||
|
||||
@@ -23,8 +23,6 @@ const txData: BlockExplorerTransactionResult = {
|
||||
type: 'type',
|
||||
command: {} as ValidatorHeartbeat,
|
||||
signature: {
|
||||
version: '1',
|
||||
algo: 'vega/ed25519',
|
||||
value: '123',
|
||||
},
|
||||
};
|
||||
|
||||
@@ -11,8 +11,6 @@ export interface BlockExplorerTransactionResult {
|
||||
cursor: string;
|
||||
command: components['schemas']['blockexplorerv1transaction'];
|
||||
signature: {
|
||||
version: string;
|
||||
algo: string;
|
||||
value: string;
|
||||
};
|
||||
error?: string;
|
||||
|
||||
@@ -32,7 +32,7 @@ CYPRESS_FAIRGROUND=false
|
||||
|
||||
# Cosmic elevator flags
|
||||
NX_SUCCESSOR_MARKETS=true
|
||||
NX_METAMASK_SNAPS=true
|
||||
NX_METAMASK_SNAPS=false
|
||||
NX_PRODUCT_PERPETUALS=true
|
||||
NX_UPDATE_MARKET_STATE=true
|
||||
NX_REFERRALS=true
|
||||
|
||||
@@ -23,7 +23,7 @@ NX_TENDERMINT_WEBSOCKET_URL=wss://be.vega.community/websocket
|
||||
|
||||
# Cosmic elevator flags
|
||||
NX_SUCCESSOR_MARKETS=true
|
||||
NX_METAMASK_SNAPS=true
|
||||
NX_METAMASK_SNAPS=false
|
||||
NX_PRODUCT_PERPETUALS=true
|
||||
NX_UPDATE_MARKET_STATE=true
|
||||
NX_REFERRALS=true
|
||||
|
||||
@@ -22,7 +22,7 @@ NX_TENDERMINT_WEBSOCKET_URL=wss://be.mainnet-mirror.vega.rocks/websocket
|
||||
|
||||
# Cosmic elevator flags
|
||||
NX_SUCCESSOR_MARKETS=true
|
||||
NX_METAMASK_SNAPS=true
|
||||
NX_METAMASK_SNAPS=false
|
||||
NX_PRODUCT_PERPETUALS=true
|
||||
NX_UPDATE_MARKET_STATE=true
|
||||
NX_REFERRALS=true
|
||||
|
||||
@@ -21,7 +21,7 @@ NX_TENDERMINT_WEBSOCKET_URL=wss://be.validators-testnet.vega.
|
||||
|
||||
# Cosmic elevator flags
|
||||
NX_SUCCESSOR_MARKETS=true
|
||||
NX_METAMASK_SNAPS=true
|
||||
NX_METAMASK_SNAPS=false
|
||||
NX_PRODUCT_PERPETUALS=true
|
||||
NX_UPDATE_MARKET_STATE=true
|
||||
NX_REFERRALS=true
|
||||
|
||||
@@ -2,7 +2,7 @@ import * as Sentry from '@sentry/react';
|
||||
import { toBigNum } from '@vegaprotocol/utils';
|
||||
import { Splash } from '@vegaprotocol/ui-toolkit';
|
||||
import { useVegaWallet, useEagerConnect } from '@vegaprotocol/wallet';
|
||||
import { useFeatureFlags, useEnvironment } from '@vegaprotocol/environment';
|
||||
import { FLAGS, useEnvironment } from '@vegaprotocol/environment';
|
||||
import { useWeb3React } from '@web3-react/core';
|
||||
import React, { Suspense } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
@@ -15,23 +15,21 @@ import {
|
||||
} from './contexts/app-state/app-state-context';
|
||||
import { useContracts } from './contexts/contracts/contracts-context';
|
||||
import { useRefreshAssociatedBalances } from './hooks/use-refresh-associated-balances';
|
||||
import { useConnectors } from './lib/vega-connectors';
|
||||
import { Connectors } from './lib/vega-connectors';
|
||||
import { useSearchParams } from 'react-router-dom';
|
||||
|
||||
const useVegaWalletEagerConnect = () => {
|
||||
const connectors = useConnectors();
|
||||
const vegaConnecting = useEagerConnect(connectors);
|
||||
const vegaConnecting = useEagerConnect(Connectors);
|
||||
const { pubKey, connect } = useVegaWallet();
|
||||
const [searchParams] = useSearchParams();
|
||||
const [query] = React.useState(searchParams.get('address'));
|
||||
if (query && !pubKey) {
|
||||
connect(connectors.view);
|
||||
connect(Connectors['view']);
|
||||
}
|
||||
return vegaConnecting;
|
||||
};
|
||||
|
||||
export const AppLoader = ({ children }: { children: React.ReactElement }) => {
|
||||
const featureFlags = useFeatureFlags((state) => state.flags);
|
||||
const { t } = useTranslation();
|
||||
const { account } = useWeb3React();
|
||||
const { VEGA_URL } = useEnvironment();
|
||||
@@ -81,16 +79,10 @@ export const AppLoader = ({ children }: { children: React.ReactElement }) => {
|
||||
}
|
||||
};
|
||||
|
||||
if (!featureFlags.GOVERNANCE_NETWORK_DOWN) {
|
||||
if (!FLAGS.GOVERNANCE_NETWORK_DOWN) {
|
||||
run();
|
||||
}
|
||||
}, [
|
||||
token,
|
||||
appDispatch,
|
||||
staking,
|
||||
vesting,
|
||||
featureFlags.GOVERNANCE_NETWORK_DOWN,
|
||||
]);
|
||||
}, [token, appDispatch, staking, vesting]);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (account && pubKey) {
|
||||
@@ -155,16 +147,16 @@ export const AppLoader = ({ children }: { children: React.ReactElement }) => {
|
||||
};
|
||||
|
||||
// Only begin polling if network limits flag is set, as this is a new API not yet on mainnet 7/3/22
|
||||
if (featureFlags.GOVERNANCE_NETWORK_LIMITS) {
|
||||
if (FLAGS.GOVERNANCE_NETWORK_LIMITS) {
|
||||
getNetworkLimits();
|
||||
}
|
||||
|
||||
return () => {
|
||||
stopPoll();
|
||||
};
|
||||
}, [appDispatch, VEGA_URL, t, featureFlags.GOVERNANCE_NETWORK_LIMITS]);
|
||||
}, [appDispatch, VEGA_URL, t]);
|
||||
|
||||
if (featureFlags.GOVERNANCE_NETWORK_DOWN) {
|
||||
if (FLAGS.GOVERNANCE_NETWORK_DOWN) {
|
||||
return (
|
||||
<Splash>
|
||||
<SplashError />
|
||||
|
||||
@@ -23,13 +23,10 @@ export const Heading = ({
|
||||
})}
|
||||
>
|
||||
<h1
|
||||
className={classNames(
|
||||
'font-alpha calt text-5xl [word-break:break-word]',
|
||||
{
|
||||
'mt-0': !marginTop,
|
||||
'mb-0': !marginBottom,
|
||||
}
|
||||
)}
|
||||
className={classNames('font-alpha calt text-5xl break-words', {
|
||||
'mt-0': !marginTop,
|
||||
'mb-0': !marginBottom,
|
||||
})}
|
||||
>
|
||||
{title}
|
||||
</h1>
|
||||
|
||||
@@ -7,16 +7,16 @@ import {
|
||||
AppStateActionType,
|
||||
useAppState,
|
||||
} from '../../contexts/app-state/app-state-context';
|
||||
import { useConnectors } from '../../lib/vega-connectors';
|
||||
import { Connectors } from '../../lib/vega-connectors';
|
||||
import { RiskMessage } from './risk-message';
|
||||
|
||||
export const VegaWalletDialogs = () => {
|
||||
const { appState, appDispatch } = useAppState();
|
||||
const connectors = useConnectors();
|
||||
|
||||
return (
|
||||
<>
|
||||
<VegaConnectDialog
|
||||
connectors={connectors}
|
||||
connectors={Connectors}
|
||||
riskMessage={<RiskMessage />}
|
||||
/>
|
||||
|
||||
@@ -30,7 +30,7 @@ export const VegaWalletDialogs = () => {
|
||||
}
|
||||
/>
|
||||
|
||||
<ViewAsDialog connector={connectors.view} />
|
||||
<ViewAsDialog connector={Connectors.view} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -49,8 +49,12 @@ export const ContractsProvider = ({ children }: { children: JSX.Element }) => {
|
||||
? activeProvider
|
||||
: defaultProvider;
|
||||
|
||||
if (account && provider && typeof provider.getSigner === 'function') {
|
||||
signer = provider.getSigner(account);
|
||||
if (
|
||||
account &&
|
||||
activeProvider &&
|
||||
typeof activeProvider.getSigner === 'function'
|
||||
) {
|
||||
signer = provider.getSigner();
|
||||
}
|
||||
|
||||
const tokenVestingAddress =
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { useFeatureFlags } from '@vegaprotocol/environment';
|
||||
import { useMemo } from 'react';
|
||||
import { FLAGS } from '@vegaprotocol/environment';
|
||||
import {
|
||||
JsonRpcConnector,
|
||||
ViewConnector,
|
||||
@@ -14,17 +13,13 @@ export const jsonRpc = new JsonRpcConnector();
|
||||
export const injected = new InjectedConnector();
|
||||
export const view = new ViewConnector(urlParams.get('address'));
|
||||
|
||||
export const snap = new SnapConnector(DEFAULT_SNAP_ID);
|
||||
export const snap = FLAGS.METAMASK_SNAPS
|
||||
? new SnapConnector(DEFAULT_SNAP_ID)
|
||||
: undefined;
|
||||
|
||||
export const useConnectors = () => {
|
||||
const featureFlags = useFeatureFlags((state) => state.flags);
|
||||
return useMemo(
|
||||
() => ({
|
||||
injected,
|
||||
jsonRpc,
|
||||
view,
|
||||
snap: featureFlags.METAMASK_SNAPS ? snap : undefined,
|
||||
}),
|
||||
[featureFlags.METAMASK_SNAPS]
|
||||
);
|
||||
export const Connectors = {
|
||||
injected,
|
||||
jsonRpc,
|
||||
view,
|
||||
snap,
|
||||
};
|
||||
|
||||
@@ -12,7 +12,7 @@ import { useRefreshAfterEpoch } from '../../hooks/use-refresh-after-epoch';
|
||||
import { ProposalsListItem } from '../proposals/components/proposals-list-item';
|
||||
import { ProtocolUpgradeProposalsListItem } from '../proposals/components/protocol-upgrade-proposals-list-item/protocol-upgrade-proposals-list-item';
|
||||
import Routes from '../routes';
|
||||
import { ExternalLinks, useFeatureFlags } from '@vegaprotocol/environment';
|
||||
import { ExternalLinks, FLAGS } from '@vegaprotocol/environment';
|
||||
import { removePaginationWrapper } from '@vegaprotocol/utils';
|
||||
import { useNodesQuery } from '../staking/home/__generated__/Nodes';
|
||||
import { useProposalsQuery } from '../proposals/proposals/__generated__/Proposals';
|
||||
@@ -31,7 +31,7 @@ import {
|
||||
orderByUpgradeBlockHeight,
|
||||
} from '../proposals/components/proposals-list/proposals-list';
|
||||
import { BigNumber } from '../../lib/bignumber';
|
||||
import { type Proposal } from '../proposals/types';
|
||||
import type { ProposalQuery } from '../proposals/proposal/__generated__/Proposal';
|
||||
|
||||
const nodesToShow = 6;
|
||||
|
||||
@@ -39,7 +39,7 @@ const HomeProposals = ({
|
||||
proposals,
|
||||
protocolUpgradeProposals,
|
||||
}: {
|
||||
proposals: Proposal[];
|
||||
proposals: ProposalQuery['proposal'][];
|
||||
protocolUpgradeProposals: ProtocolUpgradeProposalFieldsFragment[];
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
@@ -175,7 +175,6 @@ export const ValidatorDetailsLink = ({
|
||||
};
|
||||
|
||||
const GovernanceHome = ({ name }: RouteChildProps) => {
|
||||
const featureFlags = useFeatureFlags((state) => state.flags);
|
||||
useDocumentTitle(name);
|
||||
const { t } = useTranslation();
|
||||
const {
|
||||
@@ -187,9 +186,9 @@ const GovernanceHome = ({ name }: RouteChildProps) => {
|
||||
fetchPolicy: 'network-only',
|
||||
errorPolicy: 'ignore',
|
||||
variables: {
|
||||
includeNewMarketProductFields: !!featureFlags.PRODUCT_PERPETUALS,
|
||||
includeUpdateMarketStates: !!featureFlags.UPDATE_MARKET_STATE,
|
||||
includeUpdateReferralPrograms: !!featureFlags.REFERRALS,
|
||||
includeNewMarketProductFields: !!FLAGS.PRODUCT_PERPETUALS,
|
||||
includeUpdateMarketStates: !!FLAGS.UPDATE_MARKET_STATE,
|
||||
includeUpdateReferralPrograms: !!FLAGS.REFERRALS,
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
+9
-4
@@ -1,11 +1,16 @@
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import type { ProposalFieldsFragment } from '../../proposals/__generated__/Proposals';
|
||||
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||
import { ProposalState } from '@vegaprotocol/types';
|
||||
import { ProposalInfoLabel } from '../proposal-info-label';
|
||||
import { type ReactNode } from 'react';
|
||||
import { type ProposalInfoLabelVariant } from '../proposal-info-label';
|
||||
import { type Proposal } from '../../types';
|
||||
import type { ReactNode } from 'react';
|
||||
import type { ProposalInfoLabelVariant } from '../proposal-info-label';
|
||||
|
||||
export const CurrentProposalState = ({ proposal }: { proposal: Proposal }) => {
|
||||
export const CurrentProposalState = ({
|
||||
proposal,
|
||||
}: {
|
||||
proposal: ProposalFieldsFragment | ProposalQuery['proposal'];
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
let proposalStatus: ReactNode;
|
||||
let variant = 'tertiary' as ProposalInfoLabelVariant;
|
||||
|
||||
+272
@@ -0,0 +1,272 @@
|
||||
import type { MockedResponse } from '@apollo/client/testing';
|
||||
import { MockedProvider } from '@apollo/client/testing';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { ProposalRejectionReason, ProposalState } from '@vegaprotocol/types';
|
||||
import type { NetworkParamsQuery } from '@vegaprotocol/network-parameters';
|
||||
import { NetworkParamsDocument } from '@vegaprotocol/network-parameters';
|
||||
import { AppStateProvider } from '../../../../contexts/app-state/app-state-provider';
|
||||
import { generateProposal } from '../../test-helpers/generate-proposals';
|
||||
import { CurrentProposalStatus } from './current-proposal-status';
|
||||
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||
|
||||
const networkParamsQueryMock: MockedResponse<NetworkParamsQuery> = {
|
||||
request: {
|
||||
query: NetworkParamsDocument,
|
||||
},
|
||||
result: {
|
||||
data: {
|
||||
networkParametersConnection: {
|
||||
edges: [
|
||||
{
|
||||
node: {
|
||||
__typename: 'NetworkParameter',
|
||||
key: 'governance.proposal.updateNetParam.requiredMajority',
|
||||
value: '0.00000001',
|
||||
},
|
||||
},
|
||||
{
|
||||
node: {
|
||||
__typename: 'NetworkParameter',
|
||||
key: 'governance.proposal.updateNetParam.requiredParticipation',
|
||||
value: '0.000000001',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const renderComponent = ({
|
||||
proposal,
|
||||
}: {
|
||||
proposal: ProposalQuery['proposal'];
|
||||
}) => {
|
||||
render(
|
||||
<AppStateProvider>
|
||||
<MockedProvider mocks={[networkParamsQueryMock]}>
|
||||
<CurrentProposalStatus proposal={proposal} />
|
||||
</MockedProvider>
|
||||
</AppStateProvider>
|
||||
);
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
jest.useFakeTimers();
|
||||
jest.setSystemTime(60 * 60 * 1000);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.useRealTimers();
|
||||
});
|
||||
|
||||
it('Proposal open - renders will fail state if the proposal will fail', async () => {
|
||||
const failedProposal = generateProposal({
|
||||
votes: {
|
||||
__typename: 'ProposalVotes',
|
||||
yes: {
|
||||
__typename: 'ProposalVoteSide',
|
||||
totalNumber: '0',
|
||||
totalTokens: '0',
|
||||
totalEquityLikeShareWeight: '0',
|
||||
},
|
||||
no: {
|
||||
__typename: 'ProposalVoteSide',
|
||||
totalNumber: '0',
|
||||
totalTokens: '0',
|
||||
totalEquityLikeShareWeight: '0',
|
||||
},
|
||||
},
|
||||
});
|
||||
renderComponent({ proposal: failedProposal });
|
||||
expect(await screen.findByText('Currently expected to')).toBeInTheDocument();
|
||||
expect(await screen.findByText('fail.')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Proposal open - renders will pass state if the proposal will pass', async () => {
|
||||
const proposal = generateProposal();
|
||||
|
||||
renderComponent({ proposal });
|
||||
expect(await screen.findByText('Currently expected to')).toBeInTheDocument();
|
||||
expect(await screen.findByText('pass.')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Proposal enacted - renders vote passed and time since enactment', async () => {
|
||||
const proposal = generateProposal({
|
||||
state: ProposalState.STATE_ENACTED,
|
||||
terms: {
|
||||
enactmentDatetime: new Date(0).toISOString(),
|
||||
},
|
||||
});
|
||||
|
||||
renderComponent({ proposal });
|
||||
expect(await screen.findByText('Vote passed.')).toBeInTheDocument();
|
||||
expect(await screen.findByText('about 1 hour ago')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Proposal passed - renders vote passed and time since vote closed', async () => {
|
||||
const proposal = generateProposal({
|
||||
state: ProposalState.STATE_PASSED,
|
||||
terms: {
|
||||
closingDatetime: new Date(0).toISOString(),
|
||||
},
|
||||
});
|
||||
|
||||
renderComponent({ proposal });
|
||||
expect(await screen.findByText('Vote passed.')).toBeInTheDocument();
|
||||
expect(await screen.findByText('about 1 hour ago')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Proposal waiting for node vote - will pass - renders if the vote will pass and status', async () => {
|
||||
const failedProposal = generateProposal({
|
||||
state: ProposalState.STATE_WAITING_FOR_NODE_VOTE,
|
||||
votes: {
|
||||
__typename: 'ProposalVotes',
|
||||
yes: {
|
||||
__typename: 'ProposalVoteSide',
|
||||
totalNumber: '0',
|
||||
totalTokens: '0',
|
||||
totalEquityLikeShareWeight: '0',
|
||||
},
|
||||
no: {
|
||||
__typename: 'ProposalVoteSide',
|
||||
totalNumber: '0',
|
||||
totalTokens: '0',
|
||||
totalEquityLikeShareWeight: '0',
|
||||
},
|
||||
},
|
||||
});
|
||||
renderComponent({ proposal: failedProposal });
|
||||
expect(
|
||||
await screen.findByText('Waiting for nodes to validate asset.')
|
||||
).toBeInTheDocument();
|
||||
expect(await screen.findByText('Currently expected to')).toBeInTheDocument();
|
||||
expect(await screen.findByText('fail.')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Proposal waiting for node vote - will fail - renders if the vote will pass and status', async () => {
|
||||
const proposal = generateProposal({
|
||||
state: ProposalState.STATE_WAITING_FOR_NODE_VOTE,
|
||||
});
|
||||
|
||||
renderComponent({ proposal });
|
||||
expect(
|
||||
await screen.findByText('Waiting for nodes to validate asset.')
|
||||
).toBeInTheDocument();
|
||||
expect(await screen.findByText('Currently expected to')).toBeInTheDocument();
|
||||
expect(await screen.findByText('pass.')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Proposal failed - renders vote failed reason and vote closed ago', async () => {
|
||||
const proposal = generateProposal({
|
||||
state: ProposalState.STATE_FAILED,
|
||||
errorDetails: 'foo',
|
||||
terms: {
|
||||
closingDatetime: new Date(0).toISOString(),
|
||||
},
|
||||
});
|
||||
|
||||
renderComponent({ proposal });
|
||||
expect(
|
||||
await screen.findByText('Vote closed. Failed due to:')
|
||||
).toBeInTheDocument();
|
||||
expect(await screen.findByText('foo')).toBeInTheDocument();
|
||||
expect(await screen.findByText('about 1 hour ago')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Proposal failed - renders rejection reason there are no error details', async () => {
|
||||
const proposal = generateProposal({
|
||||
state: ProposalState.STATE_FAILED,
|
||||
rejectionReason: ProposalRejectionReason.PROPOSAL_ERROR_CLOSE_TIME_TOO_LATE,
|
||||
terms: {
|
||||
closingDatetime: new Date(0).toISOString(),
|
||||
},
|
||||
});
|
||||
|
||||
renderComponent({ proposal });
|
||||
expect(
|
||||
await screen.findByText('Vote closed. Failed due to:')
|
||||
).toBeInTheDocument();
|
||||
expect(
|
||||
await screen.findByText('PROPOSAL_ERROR_CLOSE_TIME_TOO_LATE')
|
||||
).toBeInTheDocument();
|
||||
expect(await screen.findByText('about 1 hour ago')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Proposal failed - renders unknown reason if there are no error details or rejection reason', async () => {
|
||||
const proposal = generateProposal({
|
||||
state: ProposalState.STATE_FAILED,
|
||||
terms: {
|
||||
closingDatetime: new Date(0).toISOString(),
|
||||
},
|
||||
});
|
||||
|
||||
renderComponent({ proposal });
|
||||
expect(
|
||||
await screen.findByText('Vote closed. Failed due to:')
|
||||
).toBeInTheDocument();
|
||||
expect(await screen.findByText('unknown reason')).toBeInTheDocument();
|
||||
expect(await screen.findByText('about 1 hour ago')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Proposal failed - renders participation not met if participation is not met', async () => {
|
||||
const proposal = generateProposal({
|
||||
state: ProposalState.STATE_FAILED,
|
||||
terms: {
|
||||
closingDatetime: new Date(0).toISOString(),
|
||||
},
|
||||
votes: {
|
||||
__typename: 'ProposalVotes',
|
||||
yes: {
|
||||
__typename: 'ProposalVoteSide',
|
||||
totalNumber: '0',
|
||||
totalTokens: '0',
|
||||
totalEquityLikeShareWeight: '0',
|
||||
},
|
||||
no: {
|
||||
__typename: 'ProposalVoteSide',
|
||||
totalNumber: '0',
|
||||
totalTokens: '0',
|
||||
totalEquityLikeShareWeight: '0',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
renderComponent({ proposal });
|
||||
expect(
|
||||
await screen.findByText('Vote closed. Failed due to:')
|
||||
).toBeInTheDocument();
|
||||
expect(await screen.findByText('Participation not met')).toBeInTheDocument();
|
||||
expect(await screen.findByText('about 1 hour ago')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('Proposal failed - renders majority not met if majority is not met', async () => {
|
||||
const proposal = generateProposal({
|
||||
state: ProposalState.STATE_FAILED,
|
||||
terms: {
|
||||
closingDatetime: new Date(0).toISOString(),
|
||||
},
|
||||
votes: {
|
||||
__typename: 'ProposalVotes',
|
||||
yes: {
|
||||
__typename: 'ProposalVoteSide',
|
||||
totalNumber: '0',
|
||||
totalTokens: '0',
|
||||
totalEquityLikeShareWeight: '0',
|
||||
},
|
||||
no: {
|
||||
__typename: 'ProposalVoteSide',
|
||||
totalNumber: '1',
|
||||
totalTokens: '25242474195500835440000',
|
||||
totalEquityLikeShareWeight: '0',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
renderComponent({ proposal });
|
||||
expect(
|
||||
await screen.findByText('Vote closed. Failed due to:')
|
||||
).toBeInTheDocument();
|
||||
expect(await screen.findByText('Majority not met')).toBeInTheDocument();
|
||||
expect(await screen.findByText('about 1 hour ago')).toBeInTheDocument();
|
||||
});
|
||||
+143
@@ -0,0 +1,143 @@
|
||||
import type { ReactNode } from 'react';
|
||||
import { formatDistanceToNow } from 'date-fns';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { ProposalState } from '@vegaprotocol/types';
|
||||
import { useVoteInformation } from '../../hooks';
|
||||
import type { ProposalFieldsFragment } from '../../proposals/__generated__/Proposals';
|
||||
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||
|
||||
export const StatusPass = ({ children }: { children: ReactNode }) => (
|
||||
<span className="text-vega-green">{children}</span>
|
||||
);
|
||||
|
||||
export const StatusFail = ({ children }: { children: ReactNode }) => (
|
||||
<span className="text-danger">{children}</span>
|
||||
);
|
||||
|
||||
const WillPass = ({
|
||||
willPass,
|
||||
children,
|
||||
}: {
|
||||
willPass: boolean;
|
||||
children?: ReactNode;
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
if (willPass) {
|
||||
return (
|
||||
<>
|
||||
{children}
|
||||
<StatusPass>{t('pass')}.</StatusPass>
|
||||
<span className="ml-2">{t('finalOutcomeMayDiffer')}</span>
|
||||
</>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<>
|
||||
{children}
|
||||
<StatusFail>{t('fail')}.</StatusFail>
|
||||
<span className="ml-2">{t('finalOutcomeMayDiffer')}</span>
|
||||
</>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export const CurrentProposalStatus = ({
|
||||
proposal,
|
||||
}: {
|
||||
proposal: ProposalFieldsFragment | ProposalQuery['proposal'];
|
||||
}) => {
|
||||
const { willPassByTokenVote, majorityMet, participationMet } =
|
||||
useVoteInformation({
|
||||
proposal,
|
||||
});
|
||||
const { t } = useTranslation();
|
||||
|
||||
const daysClosedAgo = formatDistanceToNow(
|
||||
new Date(proposal?.terms.closingDatetime),
|
||||
{ addSuffix: true }
|
||||
);
|
||||
|
||||
const daysEnactedAgo =
|
||||
proposal?.terms.enactmentDatetime &&
|
||||
formatDistanceToNow(new Date(proposal.terms.enactmentDatetime), {
|
||||
addSuffix: true,
|
||||
});
|
||||
|
||||
if (proposal?.state === ProposalState.STATE_OPEN) {
|
||||
return (
|
||||
<WillPass willPass={willPassByTokenVote}>{t('currentlySetTo')}</WillPass>
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
proposal?.state === ProposalState.STATE_FAILED ||
|
||||
proposal?.state === ProposalState.STATE_DECLINED ||
|
||||
proposal?.state === ProposalState.STATE_REJECTED
|
||||
) {
|
||||
if (!participationMet) {
|
||||
return (
|
||||
<>
|
||||
<span>{t('voteFailedReason')}</span>
|
||||
<StatusFail>{t('participationNotMet')}</StatusFail>
|
||||
<span> {daysClosedAgo}</span>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
if (!majorityMet) {
|
||||
return (
|
||||
<>
|
||||
<span>{t('voteFailedReason')}</span>
|
||||
<StatusFail>{t('majorityNotMet')}</StatusFail>
|
||||
<span> {daysClosedAgo}</span>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<span>{t('voteFailedReason')}</span>
|
||||
<StatusFail>
|
||||
{proposal?.errorDetails ||
|
||||
proposal?.rejectionReason ||
|
||||
t('unknownReason')}
|
||||
</StatusFail>
|
||||
<span> {daysClosedAgo}</span>
|
||||
</>
|
||||
);
|
||||
}
|
||||
if (
|
||||
proposal?.state === ProposalState.STATE_ENACTED ||
|
||||
proposal?.state === ProposalState.STATE_PASSED
|
||||
) {
|
||||
return (
|
||||
<>
|
||||
<span>{t('votePassed')}</span>
|
||||
<StatusPass>
|
||||
|
||||
{proposal?.state === ProposalState.STATE_ENACTED
|
||||
? t('Enacted')
|
||||
: t('Passed')}
|
||||
</StatusPass>
|
||||
<span>
|
||||
|
||||
{proposal?.state === ProposalState.STATE_ENACTED
|
||||
? daysEnactedAgo
|
||||
: daysClosedAgo}
|
||||
</span>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
if (proposal?.state === ProposalState.STATE_WAITING_FOR_NODE_VOTE) {
|
||||
return (
|
||||
<WillPass willPass={willPassByTokenVote}>
|
||||
<span>{t('WaitingForNodeVote')}</span>{' '}
|
||||
<span>{t('currentlySetTo')}</span>
|
||||
</WillPass>
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
@@ -0,0 +1 @@
|
||||
export { CurrentProposalStatus } from './current-proposal-status';
|
||||
-3
@@ -7,10 +7,8 @@ import type { AssetFieldsFragment } from '@vegaprotocol/assets';
|
||||
|
||||
export const ProposalAssetDetails = ({
|
||||
asset,
|
||||
originalAsset,
|
||||
}: {
|
||||
asset: AssetFieldsFragment;
|
||||
originalAsset?: AssetFieldsFragment;
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const [showAssetDetails, setShowAssetDetails] = useState(false);
|
||||
@@ -29,7 +27,6 @@ export const ProposalAssetDetails = ({
|
||||
<div className="mb-10 pb-4">
|
||||
<AssetDetailsTable
|
||||
asset={asset}
|
||||
originalAsset={originalAsset}
|
||||
omitRows={[
|
||||
AssetDetail.STATUS,
|
||||
AssetDetail.INFRASTRUCTURE_FEE_ACCOUNT_BALANCE,
|
||||
|
||||
+3
-2
@@ -6,10 +6,11 @@ import {
|
||||
KeyValueTableRow,
|
||||
RoundedWrapper,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import { type Proposal } from '../../types';
|
||||
import type { ProposalFieldsFragment } from '../../proposals/__generated__/Proposals';
|
||||
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||
|
||||
interface ProposalChangeTableProps {
|
||||
proposal: Proposal;
|
||||
proposal: ProposalFieldsFragment | ProposalQuery['proposal'];
|
||||
}
|
||||
|
||||
export const ProposalChangeTable = ({ proposal }: ProposalChangeTableProps) => {
|
||||
|
||||
+6
-19
@@ -19,12 +19,12 @@ import {
|
||||
mockWalletContext,
|
||||
createUserVoteQueryMock,
|
||||
} from '../../test-helpers/mocks';
|
||||
import { useFeatureFlags } from '@vegaprotocol/environment';
|
||||
import { FLAGS } from '@vegaprotocol/environment';
|
||||
import { BrowserRouter } from 'react-router-dom';
|
||||
import { VoteState } from '../vote-details/use-user-vote';
|
||||
import { useNewTransferProposalDetails } from '@vegaprotocol/proposals';
|
||||
import { type MockedResponse } from '@apollo/client/testing';
|
||||
import { type Proposal } from '../../types';
|
||||
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||
import type { MockedResponse } from '@apollo/client/testing';
|
||||
|
||||
jest.mock('@vegaprotocol/proposals', () => ({
|
||||
...jest.requireActual('@vegaprotocol/proposals'),
|
||||
@@ -36,7 +36,7 @@ jest.mock('@vegaprotocol/proposals', () => ({
|
||||
}));
|
||||
|
||||
const renderComponent = (
|
||||
proposal: Proposal,
|
||||
proposal: ProposalQuery['proposal'],
|
||||
isListItem = true,
|
||||
mocks: MockedResponse[] = [],
|
||||
voteState?: VoteState
|
||||
@@ -62,9 +62,9 @@ describe('Proposal header', () => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
it('Renders New market proposal', () => {
|
||||
useFeatureFlags.setState({ flags: { SUCCESSOR_MARKETS: true } });
|
||||
const mockedFlags = jest.mocked(FLAGS);
|
||||
mockedFlags.SUCCESSOR_MARKETS = true;
|
||||
renderComponent(
|
||||
// @ts-ignore we aren't using batch yet
|
||||
generateProposal({
|
||||
rationale: {
|
||||
title: 'New some market',
|
||||
@@ -103,7 +103,6 @@ describe('Proposal header', () => {
|
||||
|
||||
it('Renders Update market proposal', () => {
|
||||
renderComponent(
|
||||
// @ts-ignore we aren't using batch yet
|
||||
generateProposal({
|
||||
rationale: {
|
||||
title: 'New market id',
|
||||
@@ -132,7 +131,6 @@ describe('Proposal header', () => {
|
||||
|
||||
it('Renders New asset proposal - ERC20', () => {
|
||||
renderComponent(
|
||||
// @ts-ignore we aren't using batch yet
|
||||
generateProposal({
|
||||
rationale: {
|
||||
title: 'New asset: Fake currency',
|
||||
@@ -162,7 +160,6 @@ describe('Proposal header', () => {
|
||||
|
||||
it('Renders New asset proposal - BuiltInAsset', () => {
|
||||
renderComponent(
|
||||
// @ts-ignore we aren't using batch yet
|
||||
generateProposal({
|
||||
terms: {
|
||||
change: {
|
||||
@@ -188,7 +185,6 @@ describe('Proposal header', () => {
|
||||
|
||||
it('Renders Update network', () => {
|
||||
renderComponent(
|
||||
// @ts-ignore we aren't using batch yet
|
||||
generateProposal({
|
||||
rationale: {
|
||||
title: 'Network parameter',
|
||||
@@ -218,7 +214,6 @@ describe('Proposal header', () => {
|
||||
|
||||
it('Renders Freeform proposal - short rationale', () => {
|
||||
renderComponent(
|
||||
// @ts-ignore we aren't using batch yet
|
||||
generateProposal({
|
||||
id: 'short',
|
||||
rationale: {
|
||||
@@ -240,7 +235,6 @@ describe('Proposal header', () => {
|
||||
|
||||
it('Renders Freeform proposal - long rationale (105 chars) - listing', () => {
|
||||
renderComponent(
|
||||
// @ts-ignore we aren't using batch yet
|
||||
generateProposal({
|
||||
id: 'long',
|
||||
rationale: {
|
||||
@@ -266,7 +260,6 @@ describe('Proposal header', () => {
|
||||
// Remove once proposals have rationale and re-enable above tests
|
||||
it('Renders Freeform proposal - id for title', () => {
|
||||
renderComponent(
|
||||
// @ts-ignore we aren't using batch yet
|
||||
generateProposal({
|
||||
id: 'freeform id',
|
||||
rationale: {
|
||||
@@ -288,7 +281,6 @@ describe('Proposal header', () => {
|
||||
|
||||
it('Renders asset change proposal header', () => {
|
||||
renderComponent(
|
||||
// @ts-ignore we aren't using batch yet
|
||||
generateProposal({
|
||||
terms: {
|
||||
change: {
|
||||
@@ -306,7 +298,6 @@ describe('Proposal header', () => {
|
||||
|
||||
it("Renders unknown proposal if it's a different proposal type", () => {
|
||||
renderComponent(
|
||||
// @ts-ignore we aren't using batch yet
|
||||
generateProposal({
|
||||
terms: {
|
||||
change: {
|
||||
@@ -323,7 +314,6 @@ describe('Proposal header', () => {
|
||||
|
||||
it('Renders proposal state: Enacted', () => {
|
||||
renderComponent(
|
||||
// @ts-ignore we aren't using batch yet
|
||||
generateProposal({
|
||||
state: ProposalState.STATE_ENACTED,
|
||||
terms: {
|
||||
@@ -336,7 +326,6 @@ describe('Proposal header', () => {
|
||||
|
||||
it('Renders proposal state: Passed', () => {
|
||||
renderComponent(
|
||||
// @ts-ignore we aren't using batch yet
|
||||
generateProposal({
|
||||
state: ProposalState.STATE_PASSED,
|
||||
terms: {
|
||||
@@ -350,7 +339,6 @@ describe('Proposal header', () => {
|
||||
|
||||
it('Renders proposal state: Waiting for node vote', () => {
|
||||
renderComponent(
|
||||
// @ts-ignore we aren't using batch yet
|
||||
generateProposal({
|
||||
state: ProposalState.STATE_WAITING_FOR_NODE_VOTE,
|
||||
terms: {
|
||||
@@ -365,7 +353,6 @@ describe('Proposal header', () => {
|
||||
|
||||
it('Renders proposal state: Open', () => {
|
||||
renderComponent(
|
||||
// @ts-ignore we aren't using batch yet
|
||||
generateProposal({
|
||||
state: ProposalState.STATE_OPEN,
|
||||
votes: {
|
||||
|
||||
+12
-14
@@ -2,7 +2,8 @@ import { useTranslation } from 'react-i18next';
|
||||
import { Lozenge, VegaIcon, VegaIconNames } from '@vegaprotocol/ui-toolkit';
|
||||
import { shorten } from '@vegaprotocol/utils';
|
||||
import { Heading, SubHeading } from '../../../../components/heading';
|
||||
import { type ReactNode } from 'react';
|
||||
import type { ReactNode } from 'react';
|
||||
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||
import { truncateMiddle } from '../../../../lib/truncate-middle';
|
||||
import { CurrentProposalState } from '../current-proposal-state';
|
||||
import { ProposalInfoLabel } from '../proposal-info-label';
|
||||
@@ -11,24 +12,22 @@ import {
|
||||
useNewTransferProposalDetails,
|
||||
useSuccessorMarketProposalDetails,
|
||||
} from '@vegaprotocol/proposals';
|
||||
import { useFeatureFlags } from '@vegaprotocol/environment';
|
||||
import { FLAGS } from '@vegaprotocol/environment';
|
||||
import Routes from '../../../routes';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { type VoteState } from '../vote-details/use-user-vote';
|
||||
import type { VoteState } from '../vote-details/use-user-vote';
|
||||
import { VoteBreakdown } from '../vote-breakdown';
|
||||
import { GovernanceTransferKindMapping } from '@vegaprotocol/types';
|
||||
import { type Proposal } from '../../types';
|
||||
|
||||
export const ProposalHeader = ({
|
||||
proposal,
|
||||
isListItem = true,
|
||||
voteState,
|
||||
}: {
|
||||
proposal: Proposal;
|
||||
proposal: ProposalQuery['proposal'];
|
||||
isListItem?: boolean;
|
||||
voteState?: VoteState | null;
|
||||
}) => {
|
||||
const featureFlags = useFeatureFlags((state) => state.flags);
|
||||
const { t } = useTranslation();
|
||||
const change = proposal?.terms.change;
|
||||
|
||||
@@ -40,7 +39,7 @@ export const ProposalHeader = ({
|
||||
|
||||
const titleContent = shorten(title ?? '', 100);
|
||||
|
||||
const getAsset = (proposal: Proposal) => {
|
||||
const getAsset = (proposal: ProposalQuery['proposal']) => {
|
||||
const terms = proposal?.terms;
|
||||
if (
|
||||
terms?.change.__typename === 'NewMarket' &&
|
||||
@@ -55,14 +54,13 @@ export const ProposalHeader = ({
|
||||
switch (change?.__typename) {
|
||||
case 'NewMarket': {
|
||||
proposalType =
|
||||
featureFlags.PRODUCT_PERPETUALS &&
|
||||
change?.instrument?.product?.__typename
|
||||
FLAGS.PRODUCT_PERPETUALS && change?.instrument?.product?.__typename
|
||||
? `NewMarket${change?.instrument?.product?.__typename}`
|
||||
: 'NewMarket';
|
||||
fallbackTitle = t('NewMarketProposal');
|
||||
details = (
|
||||
<>
|
||||
{featureFlags.SUCCESSOR_MARKETS && (
|
||||
{FLAGS.SUCCESSOR_MARKETS && (
|
||||
<SuccessorCode proposalId={proposal?.id} />
|
||||
)}
|
||||
<span>
|
||||
@@ -84,13 +82,13 @@ export const ProposalHeader = ({
|
||||
}
|
||||
case 'UpdateMarketState': {
|
||||
proposalType =
|
||||
featureFlags.UPDATE_MARKET_STATE && change?.updateType
|
||||
FLAGS.UPDATE_MARKET_STATE && change?.updateType
|
||||
? t(change.updateType)
|
||||
: 'UpdateMarketState';
|
||||
fallbackTitle = t('UpdateMarketStateProposal');
|
||||
details = (
|
||||
<span>
|
||||
{featureFlags.UPDATE_MARKET_STATE &&
|
||||
{FLAGS.UPDATE_MARKET_STATE &&
|
||||
change?.market?.id &&
|
||||
change.updateType ? (
|
||||
<>
|
||||
@@ -179,14 +177,14 @@ export const ProposalHeader = ({
|
||||
case 'NewTransfer':
|
||||
proposalType = 'NewTransfer';
|
||||
fallbackTitle = t('NewTransferProposal');
|
||||
details = featureFlags.GOVERNANCE_TRANSFERS ? (
|
||||
details = FLAGS.GOVERNANCE_TRANSFERS ? (
|
||||
<NewTransferSummary proposalId={proposal?.id} />
|
||||
) : null;
|
||||
break;
|
||||
case 'CancelTransfer':
|
||||
proposalType = 'CancelTransfer';
|
||||
fallbackTitle = t('CancelTransferProposal');
|
||||
details = featureFlags.GOVERNANCE_TRANSFERS ? (
|
||||
details = FLAGS.GOVERNANCE_TRANSFERS ? (
|
||||
<CancelTransferSummary proposalId={proposal?.id} />
|
||||
) : null;
|
||||
break;
|
||||
|
||||
+4
-4
@@ -1,4 +1,5 @@
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||
import {
|
||||
KeyValueTable,
|
||||
KeyValueTableRow,
|
||||
@@ -13,10 +14,9 @@ import {
|
||||
} from '@vegaprotocol/utils';
|
||||
import BigNumber from 'bignumber.js';
|
||||
import { useAppState } from '../../../../contexts/app-state/app-state-context';
|
||||
import { type Proposal } from '../../types';
|
||||
|
||||
interface ProposalReferralProgramDetailsProps {
|
||||
proposal: Proposal | null;
|
||||
proposal: ProposalQuery['proposal'];
|
||||
}
|
||||
|
||||
export const formatEndOfProgramTimestamp = (value: string) => {
|
||||
@@ -54,8 +54,8 @@ export const ProposalReferralProgramDetails = ({
|
||||
return null;
|
||||
}
|
||||
|
||||
const benefitTiers = proposal?.terms?.change?.benefitTiers.slice();
|
||||
const stakingTiers = proposal?.terms?.change?.stakingTiers.slice();
|
||||
const benefitTiers = proposal?.terms?.change?.benefitTiers;
|
||||
const stakingTiers = proposal?.terms?.change?.stakingTiers;
|
||||
const windowLength = proposal?.terms?.change?.windowLength;
|
||||
const endOfProgramTimestamp = proposal?.terms?.change?.endOfProgram;
|
||||
|
||||
|
||||
+3
-2
@@ -1,4 +1,6 @@
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||
import type { ProposalFieldsFragment } from '../../proposals/__generated__/Proposals';
|
||||
import { useCancelTransferProposalDetails } from '@vegaprotocol/proposals';
|
||||
import {
|
||||
KeyValueTable,
|
||||
@@ -6,12 +8,11 @@ import {
|
||||
RoundedWrapper,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import { SubHeading } from '../../../../components/heading';
|
||||
import { type Proposal } from '../../types';
|
||||
|
||||
export const ProposalCancelTransferDetails = ({
|
||||
proposal,
|
||||
}: {
|
||||
proposal: Proposal;
|
||||
proposal: ProposalFieldsFragment | ProposalQuery['proposal'];
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const details = useCancelTransferProposalDetails(proposal?.id);
|
||||
|
||||
+3
-2
@@ -1,4 +1,6 @@
|
||||
import { useState } from 'react';
|
||||
import type { ProposalFieldsFragment } from '../../proposals/__generated__/Proposals';
|
||||
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||
import { CollapsibleToggle } from '../../../../components/collapsible-toggle';
|
||||
import { SubHeading } from '../../../../components/heading';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
@@ -19,12 +21,11 @@ import {
|
||||
addDecimalsFormatNumberQuantum,
|
||||
formatDateWithLocalTimezone,
|
||||
} from '@vegaprotocol/utils';
|
||||
import { type Proposal } from '../../types';
|
||||
|
||||
export const ProposalTransferDetails = ({
|
||||
proposal,
|
||||
}: {
|
||||
proposal: Proposal;
|
||||
proposal: ProposalFieldsFragment | ProposalQuery['proposal'];
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const [show, setShow] = useState(false);
|
||||
|
||||
+2
-2
@@ -1,4 +1,5 @@
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||
import {
|
||||
KeyValueTable,
|
||||
KeyValueTableRow,
|
||||
@@ -11,7 +12,6 @@ import {
|
||||
} from '../proposal-referral-program-details';
|
||||
import { formatNumberPercentage } from '@vegaprotocol/utils';
|
||||
import BigNumber from 'bignumber.js';
|
||||
import { type Proposal } from '../../types';
|
||||
|
||||
// These types are not generated as it's not known how dynamic these are
|
||||
type VestingBenefitTier = {
|
||||
@@ -43,7 +43,7 @@ export const formatVolumeDiscountFactor = (value: string) => {
|
||||
};
|
||||
|
||||
interface ProposalReferralProgramDetailsProps {
|
||||
proposal: Proposal | null;
|
||||
proposal: ProposalQuery['proposal'];
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+2
-2
@@ -5,13 +5,13 @@ import {
|
||||
RoundedWrapper,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import { Row } from '@vegaprotocol/markets';
|
||||
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||
import { useState } from 'react';
|
||||
import { CollapsibleToggle } from '../../../../components/collapsible-toggle';
|
||||
import { SubHeading } from '../../../../components/heading';
|
||||
import { type Proposal } from '../../types';
|
||||
|
||||
interface ProposalUpdateMarketStateProps {
|
||||
proposal: Proposal | null;
|
||||
proposal: ProposalQuery['proposal'];
|
||||
}
|
||||
|
||||
export const ProposalUpdateMarketState = ({
|
||||
|
||||
+2
-2
@@ -1,4 +1,5 @@
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||
import {
|
||||
KeyValueTable,
|
||||
KeyValueTableRow,
|
||||
@@ -11,10 +12,9 @@ import {
|
||||
} from '../proposal-referral-program-details';
|
||||
import { formatNumberPercentage } from '@vegaprotocol/utils';
|
||||
import BigNumber from 'bignumber.js';
|
||||
import { type Proposal } from '../../types';
|
||||
|
||||
interface ProposalReferralProgramDetailsProps {
|
||||
proposal: Proposal | null;
|
||||
proposal: ProposalQuery['proposal'];
|
||||
}
|
||||
|
||||
export const formatVolumeDiscountFactor = (value: string) => {
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
import { MockedProvider } from '@apollo/client/testing';
|
||||
import { VegaWalletProvider } from '@vegaprotocol/wallet';
|
||||
import { type VegaWalletConfig } from '@vegaprotocol/wallet';
|
||||
import type { VegaWalletConfig } from '@vegaprotocol/wallet';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { generateProposal } from '../../test-helpers/generate-proposals';
|
||||
import { Proposal } from './proposal';
|
||||
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||
import { ProposalState } from '@vegaprotocol/types';
|
||||
import { mockNetworkParams } from '../../test-helpers/mocks';
|
||||
import { type Proposal as IProposal } from '../../types';
|
||||
|
||||
jest.mock('@vegaprotocol/network-parameters', () => ({
|
||||
...jest.requireActual('@vegaprotocol/network-parameters'),
|
||||
@@ -50,14 +50,14 @@ const vegaWalletConfig: VegaWalletConfig = {
|
||||
},
|
||||
};
|
||||
|
||||
const renderComponent = (proposal: IProposal) => {
|
||||
const renderComponent = (proposal: ProposalQuery['proposal']) => {
|
||||
render(
|
||||
<MemoryRouter>
|
||||
<MockedProvider>
|
||||
<VegaWalletProvider config={vegaWalletConfig}>
|
||||
<Proposal
|
||||
restData={{}}
|
||||
proposal={proposal}
|
||||
proposal={proposal as ProposalQuery['proposal']}
|
||||
networkParams={mockNetworkParams}
|
||||
/>
|
||||
</VegaWalletProvider>
|
||||
|
||||
@@ -12,25 +12,25 @@ import { UserVote } from '../vote-details';
|
||||
import { ListAsset } from '../list-asset';
|
||||
import Routes from '../../../routes';
|
||||
import { ProposalMarketData } from '../proposal-market-data';
|
||||
import { type MarketInfo } from '@vegaprotocol/markets';
|
||||
import { type AssetQuery } from '@vegaprotocol/assets';
|
||||
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||
import type { MarketInfo } from '@vegaprotocol/markets';
|
||||
import type { AssetQuery } from '@vegaprotocol/assets';
|
||||
import { removePaginationWrapper } from '@vegaprotocol/utils';
|
||||
import { ProposalState } from '@vegaprotocol/types';
|
||||
import { ProposalMarketChanges } from '../proposal-market-changes';
|
||||
import { ProposalUpdateMarketState } from '../proposal-update-market-state';
|
||||
import { type NetworkParamsResult } from '@vegaprotocol/network-parameters';
|
||||
import type { NetworkParamsResult } from '@vegaprotocol/network-parameters';
|
||||
import { useVoteSubmit } from '@vegaprotocol/proposals';
|
||||
import { useUserVote } from '../vote-details/use-user-vote';
|
||||
import {
|
||||
ProposalCancelTransferDetails,
|
||||
ProposalTransferDetails,
|
||||
} from '../proposal-transfer';
|
||||
import { useFeatureFlags } from '@vegaprotocol/environment';
|
||||
import { FLAGS } from '@vegaprotocol/environment';
|
||||
import { ProposalUpdateBenefitTiers } from '../proposal-update-benefit-tiers';
|
||||
import { type Proposal as IProposal } from '../../types';
|
||||
|
||||
export interface ProposalProps {
|
||||
proposal: IProposal;
|
||||
proposal: ProposalQuery['proposal'];
|
||||
networkParams: Partial<NetworkParamsResult>;
|
||||
marketData?: MarketInfo | null;
|
||||
parentMarketData?: MarketInfo | null;
|
||||
@@ -53,7 +53,6 @@ export const Proposal = ({
|
||||
originalMarketProposalRestData,
|
||||
mostRecentlyEnactedAssociatedMarketProposal,
|
||||
}: ProposalProps) => {
|
||||
const featureFlags = useFeatureFlags((state) => state.flags);
|
||||
const { t } = useTranslation();
|
||||
const { submit, Dialog, finalizedVote, transaction } = useVoteSubmit();
|
||||
const { voteState, voteDatetime } = useUserVote(proposal?.id, finalizedVote);
|
||||
@@ -66,13 +65,10 @@ export const Proposal = ({
|
||||
? removePaginationWrapper(assetData.assetsConnection?.edges)[0]
|
||||
: undefined;
|
||||
|
||||
const originalAsset = asset;
|
||||
|
||||
if (proposal.terms.change.__typename === 'UpdateAsset' && asset) {
|
||||
asset = {
|
||||
...asset,
|
||||
quantum: proposal.terms.change.quantum,
|
||||
source: { ...asset.source },
|
||||
};
|
||||
|
||||
if (asset.source.__typename === 'ERC20') {
|
||||
@@ -133,7 +129,7 @@ export const Proposal = ({
|
||||
}
|
||||
|
||||
// Show governance transfer details only if the GOVERNANCE_TRANSFERS flag is on.
|
||||
const governanceTransferDetails = featureFlags.GOVERNANCE_TRANSFERS && (
|
||||
const governanceTransferDetails = FLAGS.GOVERNANCE_TRANSFERS && (
|
||||
<>
|
||||
{proposal.terms.change.__typename === 'NewTransfer' && (
|
||||
/** Governance New Transfer Details */
|
||||
@@ -232,7 +228,7 @@ export const Proposal = ({
|
||||
proposal.terms.change.__typename === 'UpdateAsset') &&
|
||||
asset && (
|
||||
<div className="mb-4">
|
||||
<ProposalAssetDetails asset={asset} originalAsset={originalAsset} />
|
||||
<ProposalAssetDetails asset={asset} />
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
+3
-3
@@ -1,7 +1,7 @@
|
||||
import { BrowserRouter as Router } from 'react-router-dom';
|
||||
import { AppStateProvider } from '../../../../contexts/app-state/app-state-provider';
|
||||
import { VegaWalletContext } from '@vegaprotocol/wallet';
|
||||
import { type MockedResponse } from '@apollo/client/testing';
|
||||
import type { MockedResponse } from '@apollo/client/testing';
|
||||
import { MockedProvider } from '@apollo/client/testing';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { format } from 'date-fns';
|
||||
@@ -18,10 +18,10 @@ import {
|
||||
lastWeek,
|
||||
nextWeek,
|
||||
} from '../../test-helpers/mocks';
|
||||
import { type Proposal } from '../../types';
|
||||
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||
|
||||
const renderComponent = (
|
||||
proposal: Proposal,
|
||||
proposal: ProposalQuery['proposal'],
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
mocks: MockedResponse<any>[] = [networkParamsQueryMock]
|
||||
) =>
|
||||
|
||||
+4
-3
@@ -1,20 +1,21 @@
|
||||
import { type ReactNode } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { Button } from '@vegaprotocol/ui-toolkit';
|
||||
import { differenceInHours, format, formatDistanceToNowStrict } from 'date-fns';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { DATE_FORMAT_DETAILED } from '../../../../lib/date-formats';
|
||||
import type { ReactNode } from 'react';
|
||||
import {
|
||||
ProposalRejectionReasonMapping,
|
||||
ProposalState,
|
||||
} from '@vegaprotocol/types';
|
||||
import Routes from '../../../routes';
|
||||
import { type Proposal } from '../../types';
|
||||
import type { ProposalFieldsFragment } from '../../proposals/__generated__/Proposals';
|
||||
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||
|
||||
export const ProposalsListItemDetails = ({
|
||||
proposal,
|
||||
}: {
|
||||
proposal: Proposal;
|
||||
proposal: ProposalFieldsFragment | ProposalQuery['proposal'];
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const state = proposal?.state;
|
||||
|
||||
+2
-2
@@ -2,10 +2,10 @@ import { RoundedWrapper } from '@vegaprotocol/ui-toolkit';
|
||||
import { ProposalHeader } from '../proposal-detail-header/proposal-header';
|
||||
import { ProposalsListItemDetails } from './proposals-list-item-details';
|
||||
import { useUserVote } from '../vote-details/use-user-vote';
|
||||
import { type Proposal } from '../../types';
|
||||
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||
|
||||
interface ProposalsListItemProps {
|
||||
proposal?: Proposal | null;
|
||||
proposal?: ProposalQuery['proposal'] | null;
|
||||
}
|
||||
|
||||
export const ProposalsListItem = ({ proposal }: ProposalsListItemProps) => {
|
||||
|
||||
+3
-3
@@ -17,8 +17,8 @@ import {
|
||||
lastMonth,
|
||||
nextMonth,
|
||||
} from '../../test-helpers/mocks';
|
||||
import { type ProtocolUpgradeProposalFieldsFragment } from '@vegaprotocol/proposals';
|
||||
import { type Proposal } from '../../types';
|
||||
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||
import type { ProtocolUpgradeProposalFieldsFragment } from '@vegaprotocol/proposals';
|
||||
|
||||
const openProposalClosesNextMonth = generateProposal({
|
||||
id: 'proposal1',
|
||||
@@ -63,7 +63,7 @@ const closedProtocolUpgradeProposal = generateProtocolUpgradeProposal({
|
||||
});
|
||||
|
||||
const renderComponent = (
|
||||
proposals: Proposal,
|
||||
proposals: ProposalQuery['proposal'][],
|
||||
protocolUpgradeProposals?: ProtocolUpgradeProposalFieldsFragment[]
|
||||
) => (
|
||||
<Router>
|
||||
|
||||
@@ -10,20 +10,20 @@ import Routes from '../../../routes';
|
||||
import { Button, Toggle } from '@vegaprotocol/ui-toolkit';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { ExternalLink } from '@vegaprotocol/ui-toolkit';
|
||||
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||
import type { ProposalFieldsFragment } from '../../proposals/__generated__/Proposals';
|
||||
import type { ProtocolUpgradeProposalFieldsFragment } from '@vegaprotocol/proposals';
|
||||
import { ExternalLinks } from '@vegaprotocol/environment';
|
||||
import { type ProposalFieldsFragment } from '../../proposals/__generated__/Proposals';
|
||||
import { type ProtocolUpgradeProposalFieldsFragment } from '@vegaprotocol/proposals';
|
||||
import { type Proposal } from '../../types';
|
||||
|
||||
interface ProposalsListProps {
|
||||
proposals: Proposal[];
|
||||
proposals: Array<ProposalQuery['proposal']>;
|
||||
protocolUpgradeProposals: ProtocolUpgradeProposalFieldsFragment[];
|
||||
lastBlockHeight?: string;
|
||||
}
|
||||
|
||||
interface SortedProposalsProps {
|
||||
open: Proposal[];
|
||||
closed: Proposal[];
|
||||
open: ProposalQuery['proposal'][];
|
||||
closed: ProposalQuery['proposal'][];
|
||||
}
|
||||
|
||||
interface SortedProtocolUpgradeProposalsProps {
|
||||
@@ -31,7 +31,7 @@ interface SortedProtocolUpgradeProposalsProps {
|
||||
closed: ProtocolUpgradeProposalFieldsFragment[];
|
||||
}
|
||||
|
||||
export const orderByDate = (arr: Proposal[]) =>
|
||||
export const orderByDate = (arr: ProposalQuery['proposal'][]) =>
|
||||
orderBy(
|
||||
arr,
|
||||
[
|
||||
@@ -91,10 +91,14 @@ export const ProposalsList = ({
|
||||
);
|
||||
return {
|
||||
open:
|
||||
initialSorting.open.length > 0 ? orderByDate(initialSorting.open) : [],
|
||||
initialSorting.open.length > 0
|
||||
? orderByDate(initialSorting.open as ProposalQuery['proposal'][])
|
||||
: [],
|
||||
closed:
|
||||
initialSorting.closed.length > 0
|
||||
? orderByDate(initialSorting.closed).reverse()
|
||||
? orderByDate(
|
||||
initialSorting.closed as ProposalQuery['proposal'][]
|
||||
).reverse()
|
||||
: [],
|
||||
};
|
||||
}, [proposals]);
|
||||
@@ -121,7 +125,9 @@ export const ProposalsList = ({
|
||||
};
|
||||
}, [protocolUpgradeProposals, lastBlockHeight]);
|
||||
|
||||
const filterPredicate = (p: ProposalFieldsFragment | Proposal) =>
|
||||
const filterPredicate = (
|
||||
p: ProposalFieldsFragment | ProposalQuery['proposal']
|
||||
) =>
|
||||
p?.id?.includes(filterString) ||
|
||||
p?.party?.id?.toString().includes(filterString);
|
||||
|
||||
|
||||
+2
-2
@@ -12,7 +12,7 @@ import {
|
||||
nextWeek,
|
||||
lastMonth,
|
||||
} from '../../test-helpers/mocks';
|
||||
import { type Proposal } from '../../types';
|
||||
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||
|
||||
const rejectedProposalClosesNextWeek = generateProposal({
|
||||
id: 'rejected1',
|
||||
@@ -35,7 +35,7 @@ const rejectedProposalClosedLastMonth = generateProposal({
|
||||
},
|
||||
});
|
||||
|
||||
const renderComponent = (proposals: Proposal[]) => (
|
||||
const renderComponent = (proposals: ProposalQuery['proposal'][]) => (
|
||||
<Router>
|
||||
<MockedProvider mocks={[networkParamsQueryMock]}>
|
||||
<AppStateProvider>
|
||||
|
||||
+3
-3
@@ -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 Proposal } from '../../types';
|
||||
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||
|
||||
interface ProposalsListProps {
|
||||
proposals: Proposal[];
|
||||
proposals: ProposalQuery['proposal'][];
|
||||
}
|
||||
|
||||
export const RejectedProposalsList = ({ proposals }: ProposalsListProps) => {
|
||||
const { t } = useTranslation();
|
||||
const [filterString, setFilterString] = useState('');
|
||||
|
||||
const filterPredicate = (p: Proposal) =>
|
||||
const filterPredicate = (p: ProposalQuery['proposal']) =>
|
||||
p?.id?.includes(filterString) ||
|
||||
p?.party?.id?.toString().includes(filterString);
|
||||
|
||||
|
||||
+6
-6
@@ -9,7 +9,8 @@ import {
|
||||
nextWeek,
|
||||
} from '../../test-helpers/mocks';
|
||||
import { CompactVotes, VoteBreakdown } from './vote-breakdown';
|
||||
import { type MockedResponse } from '@apollo/client/testing';
|
||||
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||
import type { MockedResponse } from '@apollo/client/testing';
|
||||
import {
|
||||
generateNoVotes,
|
||||
generateProposal,
|
||||
@@ -17,8 +18,7 @@ import {
|
||||
} from '../../test-helpers/generate-proposals';
|
||||
import { ProposalState } from '@vegaprotocol/types';
|
||||
import { BigNumber } from '../../../../lib/bignumber';
|
||||
import { type AppState } from '../../../../contexts/app-state/app-state-context';
|
||||
import { type Proposal } from '../../types';
|
||||
import type { AppState } from '../../../../contexts/app-state/app-state-context';
|
||||
|
||||
const mockTotalSupply = new BigNumber(100);
|
||||
// Note - giving a fixedTokenValue of 1 means a ratio of 1:1 votes to tokens, making sums easier :)
|
||||
@@ -41,7 +41,7 @@ jest.mock('../../../../contexts/app-state/app-state-context', () => ({
|
||||
}));
|
||||
|
||||
const renderComponent = (
|
||||
proposal: Proposal,
|
||||
proposal: ProposalQuery['proposal'],
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
mocks: MockedResponse<any>[] = [networkParamsQueryMock]
|
||||
) =>
|
||||
@@ -281,8 +281,8 @@ describe('VoteBreakdown', () => {
|
||||
});
|
||||
|
||||
it('Progress bar displays status - LP majority', () => {
|
||||
const yesVotesLP = 0.8;
|
||||
const noVotesLP = 0.2;
|
||||
const yesVotesLP = 800;
|
||||
const noVotesLP = 200;
|
||||
const expectedProgress = (yesVotesLP / (yesVotesLP + noVotesLP)) * 100; // 80%
|
||||
|
||||
renderComponent(
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { type ReactNode } from 'react';
|
||||
import classNames from 'classnames';
|
||||
import BigNumber from 'bignumber.js';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
@@ -6,8 +5,10 @@ import { useVoteInformation } from '../../hooks';
|
||||
import { Icon, Tooltip } from '@vegaprotocol/ui-toolkit';
|
||||
import { formatNumber } from '@vegaprotocol/utils';
|
||||
import { ProposalState } from '@vegaprotocol/types';
|
||||
import type { ReactNode } from 'react';
|
||||
import type { ProposalFieldsFragment } from '../../proposals/__generated__/Proposals';
|
||||
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||
import { CompactNumber } from '@vegaprotocol/react-helpers';
|
||||
import { type Proposal } from '../../types';
|
||||
|
||||
export const CompactVotes = ({ number }: { number: BigNumber }) => (
|
||||
<CompactNumber
|
||||
@@ -19,7 +20,7 @@ export const CompactVotes = ({ number }: { number: BigNumber }) => (
|
||||
);
|
||||
|
||||
interface VoteBreakdownProps {
|
||||
proposal: Proposal;
|
||||
proposal: ProposalFieldsFragment | ProposalQuery['proposal'];
|
||||
}
|
||||
|
||||
interface VoteProgressProps {
|
||||
@@ -104,6 +105,8 @@ export const VoteBreakdown = ({ proposal }: VoteBreakdownProps) => {
|
||||
yesLPPercentage,
|
||||
yesTokens,
|
||||
noTokens,
|
||||
yesEquityLikeShareWeight,
|
||||
noEquityLikeShareWeight,
|
||||
totalEquityLikeShareWeight,
|
||||
requiredMajorityPercentage,
|
||||
requiredMajorityLPPercentage,
|
||||
@@ -132,7 +135,6 @@ export const VoteBreakdown = ({ proposal }: VoteBreakdownProps) => {
|
||||
.multipliedBy(100),
|
||||
new BigNumber(100)
|
||||
);
|
||||
|
||||
const willPass = willPassByTokenVote || willPassByLPVote;
|
||||
const updateMarketVotePassMethod = willPassByTokenVote
|
||||
? t('byTokenVote')
|
||||
@@ -200,24 +202,50 @@ export const VoteBreakdown = ({ proposal }: VoteBreakdownProps) => {
|
||||
<div className="flex items-center gap-1">
|
||||
<span>{t('liquidityProviderVotesFor')}:</span>
|
||||
<Tooltip
|
||||
description={
|
||||
<span>{yesLPPercentage.toFixed(defaultDP)}%</span>
|
||||
}
|
||||
description={formatNumber(
|
||||
yesEquityLikeShareWeight,
|
||||
defaultDP
|
||||
)}
|
||||
>
|
||||
<button>{yesLPPercentage.toFixed(1)}%</button>
|
||||
<button>
|
||||
<CompactVotes number={yesEquityLikeShareWeight} />
|
||||
</button>
|
||||
</Tooltip>
|
||||
<span>
|
||||
(
|
||||
<Tooltip
|
||||
description={
|
||||
<span>{yesLPPercentage.toFixed(defaultDP)}%</span>
|
||||
}
|
||||
>
|
||||
<button>{yesLPPercentage.toFixed(0)}%</button>
|
||||
</Tooltip>
|
||||
)
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-1">
|
||||
<span>{t('liquidityProviderVotesAgainst')}:</span>
|
||||
<Tooltip
|
||||
description={formatNumber(
|
||||
noEquityLikeShareWeight,
|
||||
defaultDP
|
||||
)}
|
||||
>
|
||||
<button>
|
||||
<CompactVotes number={noEquityLikeShareWeight} />
|
||||
</button>
|
||||
</Tooltip>
|
||||
<span>
|
||||
(
|
||||
<Tooltip
|
||||
description={
|
||||
<span>{noLPPercentage.toFixed(defaultDP)}%</span>
|
||||
}
|
||||
>
|
||||
<button>{noLPPercentage.toFixed(1)}%</button>
|
||||
<button>{noLPPercentage.toFixed(0)}%</button>
|
||||
</Tooltip>
|
||||
)
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -254,8 +282,13 @@ export const VoteBreakdown = ({ proposal }: VoteBreakdownProps) => {
|
||||
defaultDP
|
||||
)}
|
||||
>
|
||||
<span>{totalEquityLikeShareWeight.toFixed(1)}%</span>
|
||||
<button>
|
||||
<CompactVotes number={totalEquityLikeShareWeight} />
|
||||
</button>
|
||||
</Tooltip>
|
||||
<span>
|
||||
({totalEquityLikeShareWeight.toFixed(defaultDP)}%)
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -5,13 +5,14 @@ import { ProposalState } from '@vegaprotocol/types';
|
||||
import { ConnectToVega } from '../../../../components/connect-to-vega';
|
||||
import { VoteButtonsContainer } from './vote-buttons';
|
||||
import { SubHeading } from '../../../../components/heading';
|
||||
import { type VoteValue } from '@vegaprotocol/types';
|
||||
import { type DialogProps, type VegaTxState } from '@vegaprotocol/proposals';
|
||||
import { type VoteState } from './use-user-vote';
|
||||
import { type Proposal } from '../../types';
|
||||
import type { VoteValue } from '@vegaprotocol/types';
|
||||
import type { DialogProps, VegaTxState } from '@vegaprotocol/proposals';
|
||||
import type { ProposalFieldsFragment } from '../../proposals/__generated__/Proposals';
|
||||
import type { ProposalQuery } from '../../proposal/__generated__/Proposal';
|
||||
import type { VoteState } from './use-user-vote';
|
||||
|
||||
interface UserVoteProps {
|
||||
proposal: Proposal;
|
||||
proposal: ProposalFieldsFragment | ProposalQuery['proposal'];
|
||||
minVoterBalance: string | null | undefined;
|
||||
spamProtectionMinTokens: string | null | undefined;
|
||||
transaction: VegaTxState | null;
|
||||
|
||||
@@ -3,12 +3,13 @@ import {
|
||||
useNetworkParams,
|
||||
} from '@vegaprotocol/network-parameters';
|
||||
import { BigNumber } from '../../../lib/bignumber';
|
||||
import { type Proposal } from '../types';
|
||||
import type { ProposalFieldsFragment } from '../proposals/__generated__/Proposals';
|
||||
import type { ProposalQuery } from '../proposal/__generated__/Proposal';
|
||||
|
||||
export const useProposalNetworkParams = ({
|
||||
proposal,
|
||||
}: {
|
||||
proposal: Proposal;
|
||||
proposal: ProposalFieldsFragment | ProposalQuery['proposal'];
|
||||
}) => {
|
||||
const { params } = useNetworkParams([
|
||||
NetworkParams.governance_proposal_updateMarket_requiredMajority,
|
||||
|
||||
@@ -54,8 +54,8 @@ describe('use-vote-information', () => {
|
||||
it('returns all required vote information', () => {
|
||||
const yesVotes = 40;
|
||||
const noVotes = 60;
|
||||
const yesEquityLikeShareWeight = '0.30';
|
||||
const noEquityLikeShareWeight = '0.70';
|
||||
const yesEquityLikeShareWeight = '30';
|
||||
const noEquityLikeShareWeight = '70';
|
||||
// Note - giving a fixedTokenValue of 1 means a ratio of 1:1 votes to tokens, making sums easier :)
|
||||
const fixedTokenValue = 1000000000000000000;
|
||||
|
||||
@@ -195,10 +195,10 @@ describe('use-vote-information', () => {
|
||||
});
|
||||
|
||||
it('correctly shows whether an update market proposal will pass by token or LP vote - both failing', () => {
|
||||
const yesVotes = 0.2;
|
||||
const noVotes = 0.7;
|
||||
const yesEquityLikeShareWeight = '0.30';
|
||||
const noEquityLikeShareWeight = '0.60';
|
||||
const yesVotes = 20;
|
||||
const noVotes = 70;
|
||||
const yesEquityLikeShareWeight = '30';
|
||||
const noEquityLikeShareWeight = '60';
|
||||
const fixedTokenValue = 1000000000000000000;
|
||||
|
||||
const proposal = generateProposal({
|
||||
|
||||
@@ -2,10 +2,15 @@ import { useMemo } from 'react';
|
||||
import { useAppState } from '../../../contexts/app-state/app-state-context';
|
||||
import { BigNumber } from '../../../lib/bignumber';
|
||||
import { useProposalNetworkParams } from './use-proposal-network-params';
|
||||
import type { ProposalFieldsFragment } from '../proposals/__generated__/Proposals';
|
||||
import type { ProposalQuery } from '../proposal/__generated__/Proposal';
|
||||
import { addDecimal } from '@vegaprotocol/utils';
|
||||
import { type Proposal } from '../types';
|
||||
|
||||
export const useVoteInformation = ({ proposal }: { proposal: Proposal }) => {
|
||||
export const useVoteInformation = ({
|
||||
proposal,
|
||||
}: {
|
||||
proposal: ProposalFieldsFragment | ProposalQuery['proposal'];
|
||||
}) => {
|
||||
const {
|
||||
appState: { totalSupply, decimals },
|
||||
} = useAppState();
|
||||
@@ -56,7 +61,7 @@ export const useVoteInformation = ({ proposal }: { proposal: Proposal }) => {
|
||||
const noEquityLikeShareWeight = !proposal?.votes.no
|
||||
.totalEquityLikeShareWeight
|
||||
? new BigNumber(0)
|
||||
: new BigNumber(proposal.votes.no.totalEquityLikeShareWeight).times(100);
|
||||
: new BigNumber(proposal.votes.no.totalEquityLikeShareWeight);
|
||||
|
||||
const yesTokens = new BigNumber(
|
||||
addDecimal(proposal?.votes.yes.totalTokens ?? 0, decimals)
|
||||
@@ -65,7 +70,7 @@ export const useVoteInformation = ({ proposal }: { proposal: Proposal }) => {
|
||||
const yesEquityLikeShareWeight = !proposal?.votes.yes
|
||||
.totalEquityLikeShareWeight
|
||||
? new BigNumber(0)
|
||||
: new BigNumber(proposal.votes.yes.totalEquityLikeShareWeight).times(100);
|
||||
: new BigNumber(proposal.votes.yes.totalEquityLikeShareWeight);
|
||||
|
||||
const totalTokensVoted = yesTokens.plus(noTokens);
|
||||
|
||||
@@ -76,7 +81,12 @@ export const useVoteInformation = ({ proposal }: { proposal: Proposal }) => {
|
||||
const yesPercentage = totalTokensVoted.isZero()
|
||||
? new BigNumber(0)
|
||||
: yesTokens.multipliedBy(100).dividedBy(totalTokensVoted);
|
||||
const yesLPPercentage = yesEquityLikeShareWeight;
|
||||
|
||||
const yesLPPercentage = totalEquityLikeShareWeight.isZero()
|
||||
? new BigNumber(0)
|
||||
: yesEquityLikeShareWeight
|
||||
.multipliedBy(100)
|
||||
.dividedBy(totalEquityLikeShareWeight);
|
||||
|
||||
const noPercentage = totalTokensVoted.isZero()
|
||||
? new BigNumber(0)
|
||||
@@ -93,7 +103,9 @@ export const useVoteInformation = ({ proposal }: { proposal: Proposal }) => {
|
||||
);
|
||||
|
||||
const participationLPMet = requiredParticipationLP
|
||||
? totalEquityLikeShareWeight.isGreaterThan(requiredParticipationLP)
|
||||
? totalEquityLikeShareWeight.isGreaterThan(
|
||||
totalSupply.multipliedBy(requiredParticipationLP)
|
||||
)
|
||||
: false;
|
||||
|
||||
const majorityMet = yesPercentage.isGreaterThanOrEqualTo(
|
||||
@@ -108,7 +120,9 @@ export const useVoteInformation = ({ proposal }: { proposal: Proposal }) => {
|
||||
.multipliedBy(100)
|
||||
.dividedBy(totalSupply);
|
||||
|
||||
const totalLPTokensPercentage = totalEquityLikeShareWeight;
|
||||
const totalLPTokensPercentage = totalEquityLikeShareWeight
|
||||
.multipliedBy(100)
|
||||
.dividedBy(totalSupply);
|
||||
|
||||
const willPassByTokenVote =
|
||||
participationMet &&
|
||||
|
||||
@@ -86,65 +86,143 @@ query Proposal(
|
||||
$includeUpdateReferralProgram: Boolean!
|
||||
) {
|
||||
proposal(id: $proposalId) {
|
||||
... on Proposal {
|
||||
id
|
||||
rationale {
|
||||
title
|
||||
description
|
||||
}
|
||||
reference
|
||||
state
|
||||
datetime
|
||||
rejectionReason
|
||||
party {
|
||||
id
|
||||
rationale {
|
||||
title
|
||||
description
|
||||
}
|
||||
reference
|
||||
state
|
||||
datetime
|
||||
rejectionReason
|
||||
party {
|
||||
id
|
||||
}
|
||||
errorDetails
|
||||
...NewMarketProductField @include(if: $includeNewMarketProductField)
|
||||
...UpdateMarketState @include(if: $includeUpdateMarketState)
|
||||
...UpdateReferralProgram @include(if: $includeUpdateReferralProgram)
|
||||
...UpdateVolumeDiscountProgram
|
||||
terms {
|
||||
closingDatetime
|
||||
enactmentDatetime
|
||||
change {
|
||||
... on NewMarket {
|
||||
decimalPlaces
|
||||
metadata
|
||||
riskParameters {
|
||||
... on LogNormalRiskModel {
|
||||
riskAversionParameter
|
||||
tau
|
||||
params {
|
||||
mu
|
||||
r
|
||||
sigma
|
||||
}
|
||||
}
|
||||
... on SimpleRiskModel {
|
||||
params {
|
||||
factorLong
|
||||
factorShort
|
||||
}
|
||||
}
|
||||
errorDetails
|
||||
...NewMarketProductField @include(if: $includeNewMarketProductField)
|
||||
...UpdateMarketState @include(if: $includeUpdateMarketState)
|
||||
...UpdateReferralProgram @include(if: $includeUpdateReferralProgram)
|
||||
...UpdateVolumeDiscountProgram
|
||||
terms {
|
||||
closingDatetime
|
||||
enactmentDatetime
|
||||
change {
|
||||
... on NewMarket {
|
||||
decimalPlaces
|
||||
metadata
|
||||
riskParameters {
|
||||
... on LogNormalRiskModel {
|
||||
riskAversionParameter
|
||||
tau
|
||||
params {
|
||||
mu
|
||||
r
|
||||
sigma
|
||||
}
|
||||
}
|
||||
... on SimpleRiskModel {
|
||||
params {
|
||||
factorLong
|
||||
factorShort
|
||||
}
|
||||
}
|
||||
}
|
||||
instrument {
|
||||
name
|
||||
code
|
||||
product {
|
||||
... on FutureProduct {
|
||||
settlementAsset {
|
||||
id
|
||||
name
|
||||
symbol
|
||||
decimals
|
||||
quantum
|
||||
}
|
||||
quoteName
|
||||
dataSourceSpecBinding {
|
||||
settlementDataProperty
|
||||
tradingTerminationProperty
|
||||
}
|
||||
dataSourceSpecForSettlementData {
|
||||
sourceType {
|
||||
... on DataSourceDefinitionInternal {
|
||||
sourceType {
|
||||
... on DataSourceSpecConfigurationTime {
|
||||
conditions {
|
||||
operator
|
||||
value
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
... on DataSourceDefinitionExternal {
|
||||
sourceType {
|
||||
... on DataSourceSpecConfiguration {
|
||||
signers {
|
||||
signer {
|
||||
... on PubKey {
|
||||
key
|
||||
}
|
||||
... on ETHAddress {
|
||||
address
|
||||
}
|
||||
}
|
||||
}
|
||||
filters {
|
||||
key {
|
||||
name
|
||||
type
|
||||
}
|
||||
conditions {
|
||||
operator
|
||||
value
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
... on PerpetualProduct {
|
||||
settlementAsset {
|
||||
id
|
||||
name
|
||||
symbol
|
||||
decimals
|
||||
quantum
|
||||
}
|
||||
quoteName
|
||||
}
|
||||
}
|
||||
}
|
||||
priceMonitoringParameters {
|
||||
triggers {
|
||||
horizonSecs
|
||||
probability
|
||||
auctionExtensionSecs
|
||||
}
|
||||
}
|
||||
liquidityMonitoringParameters {
|
||||
triggeringRatio
|
||||
targetStakeParameters {
|
||||
timeWindow
|
||||
scalingFactor
|
||||
}
|
||||
}
|
||||
positionDecimalPlaces
|
||||
linearSlippageFactor
|
||||
quadraticSlippageFactor
|
||||
}
|
||||
... on UpdateMarket {
|
||||
marketId
|
||||
updateMarketConfiguration {
|
||||
instrument {
|
||||
name
|
||||
code
|
||||
product {
|
||||
... on FutureProduct {
|
||||
settlementAsset {
|
||||
id
|
||||
name
|
||||
symbol
|
||||
decimals
|
||||
quantum
|
||||
}
|
||||
... on UpdateFutureProduct {
|
||||
quoteName
|
||||
dataSourceSpecBinding {
|
||||
settlementDataProperty
|
||||
tradingTerminationProperty
|
||||
}
|
||||
dataSourceSpecForSettlementData {
|
||||
sourceType {
|
||||
... on DataSourceDefinitionInternal {
|
||||
@@ -185,19 +263,101 @@ query Proposal(
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
... on PerpetualProduct {
|
||||
settlementAsset {
|
||||
id
|
||||
name
|
||||
symbol
|
||||
decimals
|
||||
quantum
|
||||
# dataSourceSpecForTradingTermination {
|
||||
# sourceType {
|
||||
# ... on DataSourceDefinitionInternal {
|
||||
# sourceType {
|
||||
# ... on DataSourceSpecConfigurationTime {
|
||||
# conditions {
|
||||
# operator
|
||||
# value
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
# ... on DataSourceDefinitionExternal {
|
||||
# sourceType {
|
||||
# ... on DataSourceSpecConfiguration {
|
||||
# signers {
|
||||
# signer {
|
||||
# ... on PubKey {
|
||||
# key
|
||||
# }
|
||||
# ... on ETHAddress {
|
||||
# address
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
# filters {
|
||||
# key {
|
||||
# name
|
||||
# type
|
||||
# }
|
||||
# conditions {
|
||||
# operator
|
||||
# value
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
dataSourceSpecBinding {
|
||||
settlementDataProperty
|
||||
tradingTerminationProperty
|
||||
}
|
||||
}
|
||||
... on UpdatePerpetualProduct {
|
||||
quoteName
|
||||
dataSourceSpecForSettlementData {
|
||||
sourceType {
|
||||
... on DataSourceDefinitionInternal {
|
||||
sourceType {
|
||||
... on DataSourceSpecConfigurationTime {
|
||||
conditions {
|
||||
operator
|
||||
value
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
... on DataSourceDefinitionExternal {
|
||||
sourceType {
|
||||
... on DataSourceSpecConfiguration {
|
||||
signers {
|
||||
signer {
|
||||
... on PubKey {
|
||||
key
|
||||
}
|
||||
... on ETHAddress {
|
||||
address
|
||||
}
|
||||
}
|
||||
}
|
||||
filters {
|
||||
key {
|
||||
name
|
||||
type
|
||||
}
|
||||
conditions {
|
||||
operator
|
||||
value
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
dataSourceSpecBinding {
|
||||
settlementDataProperty
|
||||
settlementScheduleProperty
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
metadata
|
||||
priceMonitoringParameters {
|
||||
triggers {
|
||||
horizonSecs
|
||||
@@ -212,233 +372,71 @@ query Proposal(
|
||||
scalingFactor
|
||||
}
|
||||
}
|
||||
positionDecimalPlaces
|
||||
linearSlippageFactor
|
||||
quadraticSlippageFactor
|
||||
}
|
||||
... on UpdateMarket {
|
||||
marketId
|
||||
updateMarketConfiguration {
|
||||
instrument {
|
||||
code
|
||||
product {
|
||||
... on UpdateFutureProduct {
|
||||
quoteName
|
||||
dataSourceSpecForSettlementData {
|
||||
sourceType {
|
||||
... on DataSourceDefinitionInternal {
|
||||
sourceType {
|
||||
... on DataSourceSpecConfigurationTime {
|
||||
conditions {
|
||||
operator
|
||||
value
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
... on DataSourceDefinitionExternal {
|
||||
sourceType {
|
||||
... on DataSourceSpecConfiguration {
|
||||
signers {
|
||||
signer {
|
||||
... on PubKey {
|
||||
key
|
||||
}
|
||||
... on ETHAddress {
|
||||
address
|
||||
}
|
||||
}
|
||||
}
|
||||
filters {
|
||||
key {
|
||||
name
|
||||
type
|
||||
}
|
||||
conditions {
|
||||
operator
|
||||
value
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
# dataSourceSpecForTradingTermination {
|
||||
# sourceType {
|
||||
# ... on DataSourceDefinitionInternal {
|
||||
# sourceType {
|
||||
# ... on DataSourceSpecConfigurationTime {
|
||||
# conditions {
|
||||
# operator
|
||||
# value
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
# ... on DataSourceDefinitionExternal {
|
||||
# sourceType {
|
||||
# ... on DataSourceSpecConfiguration {
|
||||
# signers {
|
||||
# signer {
|
||||
# ... on PubKey {
|
||||
# key
|
||||
# }
|
||||
# ... on ETHAddress {
|
||||
# address
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
# filters {
|
||||
# key {
|
||||
# name
|
||||
# type
|
||||
# }
|
||||
# conditions {
|
||||
# operator
|
||||
# value
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
dataSourceSpecBinding {
|
||||
settlementDataProperty
|
||||
tradingTerminationProperty
|
||||
}
|
||||
}
|
||||
... on UpdatePerpetualProduct {
|
||||
quoteName
|
||||
dataSourceSpecForSettlementData {
|
||||
sourceType {
|
||||
... on DataSourceDefinitionInternal {
|
||||
sourceType {
|
||||
... on DataSourceSpecConfigurationTime {
|
||||
conditions {
|
||||
operator
|
||||
value
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
... on DataSourceDefinitionExternal {
|
||||
sourceType {
|
||||
... on DataSourceSpecConfiguration {
|
||||
signers {
|
||||
signer {
|
||||
... on PubKey {
|
||||
key
|
||||
}
|
||||
... on ETHAddress {
|
||||
address
|
||||
}
|
||||
}
|
||||
}
|
||||
filters {
|
||||
key {
|
||||
name
|
||||
type
|
||||
}
|
||||
conditions {
|
||||
operator
|
||||
value
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
dataSourceSpecBinding {
|
||||
settlementDataProperty
|
||||
settlementScheduleProperty
|
||||
}
|
||||
}
|
||||
riskParameters {
|
||||
... on UpdateMarketSimpleRiskModel {
|
||||
simple {
|
||||
factorLong
|
||||
factorShort
|
||||
}
|
||||
}
|
||||
metadata
|
||||
priceMonitoringParameters {
|
||||
triggers {
|
||||
horizonSecs
|
||||
probability
|
||||
auctionExtensionSecs
|
||||
}
|
||||
}
|
||||
liquidityMonitoringParameters {
|
||||
triggeringRatio
|
||||
targetStakeParameters {
|
||||
timeWindow
|
||||
scalingFactor
|
||||
}
|
||||
}
|
||||
riskParameters {
|
||||
... on UpdateMarketSimpleRiskModel {
|
||||
simple {
|
||||
factorLong
|
||||
factorShort
|
||||
}
|
||||
}
|
||||
... on UpdateMarketLogNormalRiskModel {
|
||||
logNormal {
|
||||
riskAversionParameter
|
||||
tau
|
||||
params {
|
||||
r
|
||||
sigma
|
||||
mu
|
||||
}
|
||||
... on UpdateMarketLogNormalRiskModel {
|
||||
logNormal {
|
||||
riskAversionParameter
|
||||
tau
|
||||
params {
|
||||
r
|
||||
sigma
|
||||
mu
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
... on NewAsset {
|
||||
name
|
||||
symbol
|
||||
decimals
|
||||
quantum
|
||||
source {
|
||||
... on BuiltinAsset {
|
||||
maxFaucetAmountMint
|
||||
}
|
||||
... on ERC20 {
|
||||
contractAddress
|
||||
lifetimeLimit
|
||||
withdrawThreshold
|
||||
}
|
||||
}
|
||||
... on NewAsset {
|
||||
name
|
||||
symbol
|
||||
decimals
|
||||
quantum
|
||||
source {
|
||||
... on BuiltinAsset {
|
||||
maxFaucetAmountMint
|
||||
}
|
||||
... on ERC20 {
|
||||
contractAddress
|
||||
lifetimeLimit
|
||||
withdrawThreshold
|
||||
}
|
||||
}
|
||||
... on UpdateNetworkParameter {
|
||||
networkParameter {
|
||||
key
|
||||
value
|
||||
}
|
||||
}
|
||||
... on UpdateNetworkParameter {
|
||||
networkParameter {
|
||||
key
|
||||
value
|
||||
}
|
||||
... on UpdateAsset {
|
||||
quantum
|
||||
assetId
|
||||
source {
|
||||
... on UpdateERC20 {
|
||||
lifetimeLimit
|
||||
withdrawThreshold
|
||||
}
|
||||
}
|
||||
... on UpdateAsset {
|
||||
quantum
|
||||
assetId
|
||||
source {
|
||||
... on UpdateERC20 {
|
||||
lifetimeLimit
|
||||
withdrawThreshold
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
votes {
|
||||
yes {
|
||||
totalTokens
|
||||
totalNumber
|
||||
totalEquityLikeShareWeight
|
||||
}
|
||||
no {
|
||||
totalTokens
|
||||
totalNumber
|
||||
totalEquityLikeShareWeight
|
||||
}
|
||||
}
|
||||
votes {
|
||||
yes {
|
||||
totalTokens
|
||||
totalNumber
|
||||
totalEquityLikeShareWeight
|
||||
}
|
||||
no {
|
||||
totalTokens
|
||||
totalNumber
|
||||
totalEquityLikeShareWeight
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+232
-234
File diff suppressed because one or more lines are too long
@@ -15,12 +15,10 @@ import {
|
||||
useNetworkParams,
|
||||
} from '@vegaprotocol/network-parameters';
|
||||
import { useParentMarketIdQuery } from '@vegaprotocol/markets';
|
||||
import { useFeatureFlags } from '@vegaprotocol/environment';
|
||||
import { FLAGS } from '@vegaprotocol/environment';
|
||||
import { useSuccessorMarketProposalDetails } from '@vegaprotocol/proposals';
|
||||
import { type Proposal as IProposal } from '../types';
|
||||
|
||||
export const ProposalContainer = () => {
|
||||
const featureFlags = useFeatureFlags((state) => state.flags);
|
||||
const [
|
||||
mostRecentlyEnactedAssociatedMarketProposal,
|
||||
setMostRecentlyEnactedAssociatedMarketProposal,
|
||||
@@ -61,15 +59,13 @@ export const ProposalContainer = () => {
|
||||
errorPolicy: 'ignore',
|
||||
variables: {
|
||||
proposalId: params.proposalId || '',
|
||||
includeNewMarketProductField: !!featureFlags.PRODUCT_PERPETUALS,
|
||||
includeUpdateMarketState: !!featureFlags.UPDATE_MARKET_STATE,
|
||||
includeUpdateReferralProgram: !!featureFlags.REFERRALS,
|
||||
includeNewMarketProductField: !!FLAGS.PRODUCT_PERPETUALS,
|
||||
includeUpdateMarketState: !!FLAGS.UPDATE_MARKET_STATE,
|
||||
includeUpdateReferralProgram: !!FLAGS.REFERRALS,
|
||||
},
|
||||
skip: !params.proposalId,
|
||||
});
|
||||
|
||||
const proposal = data?.proposal as IProposal;
|
||||
|
||||
const successor = useSuccessorMarketProposalDetails(params.proposalId);
|
||||
|
||||
const isSuccessor = !!successor?.parentMarketId || !!successor.code;
|
||||
@@ -82,12 +78,12 @@ export const ProposalContainer = () => {
|
||||
},
|
||||
} = useFetch(
|
||||
`${ENV.rest}governance?proposalId=${
|
||||
proposal?.terms.change.__typename === 'UpdateMarket' &&
|
||||
proposal.terms.change.marketId
|
||||
data?.proposal?.terms.change.__typename === 'UpdateMarket' &&
|
||||
data?.proposal.terms.change.marketId
|
||||
}`,
|
||||
undefined,
|
||||
true,
|
||||
proposal?.terms.change.__typename !== 'UpdateMarket'
|
||||
data?.proposal?.terms.change.__typename !== 'UpdateMarket'
|
||||
);
|
||||
|
||||
const {
|
||||
@@ -100,7 +96,7 @@ export const ProposalContainer = () => {
|
||||
`${ENV.rest}governances?proposalState=STATE_ENACTED&proposalType=TYPE_UPDATE_MARKET`,
|
||||
undefined,
|
||||
true,
|
||||
proposal?.terms.change.__typename !== 'UpdateMarket'
|
||||
data?.proposal?.terms.change.__typename !== 'UpdateMarket'
|
||||
);
|
||||
|
||||
const {
|
||||
@@ -111,8 +107,8 @@ export const ProposalContainer = () => {
|
||||
dataProvider: marketInfoProvider,
|
||||
skipUpdates: true,
|
||||
variables: {
|
||||
marketId: proposal?.id || '',
|
||||
skip: !proposal?.id,
|
||||
marketId: data?.proposal?.id || '',
|
||||
skip: !data?.proposal?.id,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -124,7 +120,7 @@ export const ProposalContainer = () => {
|
||||
variables: {
|
||||
marketId: marketData?.id || '',
|
||||
},
|
||||
skip: !featureFlags.SUCCESSOR_MARKETS || !isSuccessor || !marketData?.id,
|
||||
skip: !FLAGS.SUCCESSOR_MARKETS || !isSuccessor || !marketData?.id,
|
||||
});
|
||||
|
||||
const {
|
||||
@@ -137,7 +133,7 @@ export const ProposalContainer = () => {
|
||||
variables: {
|
||||
marketId: parentMarketId?.market?.parentMarketID || '',
|
||||
skip:
|
||||
!featureFlags.SUCCESSOR_MARKETS ||
|
||||
!FLAGS.SUCCESSOR_MARKETS ||
|
||||
!isSuccessor ||
|
||||
!parentMarketId?.market?.parentMarketID,
|
||||
},
|
||||
@@ -151,22 +147,23 @@ export const ProposalContainer = () => {
|
||||
fetchPolicy: 'network-only',
|
||||
variables: {
|
||||
assetId:
|
||||
(proposal?.terms.change.__typename === 'NewAsset' && proposal?.id) ||
|
||||
(proposal?.terms.change.__typename === 'UpdateAsset' &&
|
||||
proposal.terms.change.assetId) ||
|
||||
(data?.proposal?.terms.change.__typename === 'NewAsset' &&
|
||||
data?.proposal?.id) ||
|
||||
(data?.proposal?.terms.change.__typename === 'UpdateAsset' &&
|
||||
data.proposal.terms.change.assetId) ||
|
||||
'',
|
||||
},
|
||||
skip: !['NewAsset', 'UpdateAsset'].includes(
|
||||
proposal?.terms?.change?.__typename || ''
|
||||
data?.proposal?.terms?.change?.__typename || ''
|
||||
),
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
previouslyEnactedMarketProposalsRestData &&
|
||||
proposal?.terms.change.__typename === 'UpdateMarket'
|
||||
data?.proposal?.terms.change.__typename === 'UpdateMarket'
|
||||
) {
|
||||
const change = proposal?.terms?.change as { marketId: string };
|
||||
const change = data?.proposal?.terms?.change as { marketId: string };
|
||||
|
||||
const filteredProposals =
|
||||
// @ts-ignore rest data is not typed
|
||||
@@ -190,8 +187,8 @@ export const ProposalContainer = () => {
|
||||
}, [
|
||||
previouslyEnactedMarketProposalsRestData,
|
||||
params.proposalId,
|
||||
proposal?.terms.change.__typename,
|
||||
proposal?.terms.change,
|
||||
data?.proposal?.terms.change.__typename,
|
||||
data?.proposal?.terms.change,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -244,7 +241,7 @@ export const ProposalContainer = () => {
|
||||
>
|
||||
{data?.proposal ? (
|
||||
<Proposal
|
||||
proposal={proposal}
|
||||
proposal={data.proposal}
|
||||
networkParams={networkParams}
|
||||
restData={restData}
|
||||
marketData={marketData}
|
||||
|
||||
@@ -17,7 +17,7 @@ import {
|
||||
} from './__generated__/Proposals';
|
||||
import { type ProtocolUpgradeProposalFieldsFragment } from '@vegaprotocol/proposals';
|
||||
import { useProtocolUpgradeProposalsQuery } from '@vegaprotocol/proposals';
|
||||
import { useFeatureFlags } from '@vegaprotocol/environment';
|
||||
import { FLAGS } from '@vegaprotocol/environment';
|
||||
|
||||
export function getNotRejectedProposals(data?: ProposalFieldsFragment[]) {
|
||||
return flow([
|
||||
@@ -43,16 +43,15 @@ export function getNotRejectedProtocolUpgradeProposals<
|
||||
}
|
||||
|
||||
export const ProposalsContainer = () => {
|
||||
const featureFlags = useFeatureFlags((state) => state.flags);
|
||||
const { t } = useTranslation();
|
||||
const { data, loading, error } = useProposalsQuery({
|
||||
pollInterval: 5000,
|
||||
fetchPolicy: 'network-only',
|
||||
errorPolicy: 'ignore',
|
||||
variables: {
|
||||
includeNewMarketProductFields: !!featureFlags.PRODUCT_PERPETUALS,
|
||||
includeUpdateMarketStates: !!featureFlags.UPDATE_MARKET_STATE,
|
||||
includeUpdateReferralPrograms: !!featureFlags.REFERRALS,
|
||||
includeNewMarketProductFields: !!FLAGS.PRODUCT_PERPETUALS,
|
||||
includeUpdateMarketStates: !!FLAGS.UPDATE_MARKET_STATE,
|
||||
includeUpdateReferralPrograms: !!FLAGS.REFERRALS,
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
+1
-1
@@ -95,7 +95,7 @@ export const ProtocolUpgradeProposalContainer = () => {
|
||||
time={
|
||||
pending && time ? (
|
||||
convertToCountdownString(time, '0:00:00:00')
|
||||
) : blockInfo && 'result' in blockInfo && blockInfo?.result ? (
|
||||
) : blockInfo?.result ? (
|
||||
<span title={blockInfo.result.block.header.time}>
|
||||
{formatDateWithLocalTimezone(
|
||||
new Date(blockInfo.result.block.header.time)
|
||||
|
||||
@@ -10,7 +10,7 @@ import { removePaginationWrapper } from '@vegaprotocol/utils';
|
||||
import flow from 'lodash/flow';
|
||||
import orderBy from 'lodash/orderBy';
|
||||
import { ProposalState } from '@vegaprotocol/types';
|
||||
import { useFeatureFlags } from '@vegaprotocol/environment';
|
||||
import { FLAGS } from '@vegaprotocol/environment';
|
||||
|
||||
const orderByDate = (arr: ProposalFieldsFragment[]) =>
|
||||
orderBy(
|
||||
@@ -33,16 +33,15 @@ export function getRejectedProposals(data?: ProposalFieldsFragment[] | null) {
|
||||
}
|
||||
|
||||
export const RejectedProposalsContainer = () => {
|
||||
const featureFlags = useFeatureFlags((state) => state.flags);
|
||||
const { t } = useTranslation();
|
||||
const { data, loading, error } = useProposalsQuery({
|
||||
pollInterval: 5000,
|
||||
fetchPolicy: 'network-only',
|
||||
errorPolicy: 'ignore',
|
||||
variables: {
|
||||
includeNewMarketProductFields: !!featureFlags.PRODUCT_PERPETUALS,
|
||||
includeUpdateMarketStates: !!featureFlags.UPDATE_MARKET_STATE,
|
||||
includeUpdateReferralPrograms: !!featureFlags.REFERRALS,
|
||||
includeNewMarketProductFields: !!FLAGS.PRODUCT_PERPETUALS,
|
||||
includeUpdateMarketStates: !!FLAGS.UPDATE_MARKET_STATE,
|
||||
includeUpdateReferralPrograms: !!FLAGS.REFERRALS,
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -8,7 +8,6 @@ import mergeWith from 'lodash/mergeWith';
|
||||
import { type PartialDeep } from 'type-fest';
|
||||
import { type ProposalQuery } from '../proposal/__generated__/Proposal';
|
||||
import { type ProtocolUpgradeProposalFieldsFragment } from '@vegaprotocol/proposals';
|
||||
import { type Proposal } from '../types';
|
||||
|
||||
export function generateProtocolUpgradeProposal(
|
||||
override: PartialDeep<ProtocolUpgradeProposalFieldsFragment> = {}
|
||||
@@ -44,8 +43,8 @@ export function generateProtocolUpgradeProposal(
|
||||
}
|
||||
|
||||
export function generateProposal(
|
||||
override: PartialDeep<Proposal> = {}
|
||||
): Proposal {
|
||||
override: PartialDeep<ProposalQuery['proposal']> = {}
|
||||
): ProposalQuery['proposal'] {
|
||||
const defaultProposal: ProposalQuery['proposal'] = {
|
||||
__typename: 'Proposal',
|
||||
id: faker.datatype.uuid(),
|
||||
@@ -93,16 +92,15 @@ export function generateProposal(
|
||||
},
|
||||
};
|
||||
|
||||
return mergeWith<Proposal, PartialDeep<Proposal>>(
|
||||
defaultProposal,
|
||||
override,
|
||||
(objValue, srcValue) => {
|
||||
if (!isArray(objValue)) {
|
||||
return;
|
||||
}
|
||||
return srcValue;
|
||||
return mergeWith<
|
||||
ProposalQuery['proposal'],
|
||||
PartialDeep<ProposalQuery['proposal']>
|
||||
>(defaultProposal, override, (objValue, srcValue) => {
|
||||
if (!isArray(objValue)) {
|
||||
return;
|
||||
}
|
||||
);
|
||||
return srcValue;
|
||||
});
|
||||
}
|
||||
|
||||
type Vote = Pick<Schema.Vote, '__typename' | 'value' | 'party' | 'datetime'>;
|
||||
@@ -118,8 +116,7 @@ export const generateYesVotes = (
|
||||
fixedTokenValue?: number,
|
||||
totalEquityLikeShareWeight?: string
|
||||
): Votes => {
|
||||
const votes = [];
|
||||
for (let i = 0; i < numberOfVotes; i++) {
|
||||
const votes = Array.from(Array(numberOfVotes)).map(() => {
|
||||
const vote: Vote = {
|
||||
__typename: 'Vote',
|
||||
value: Schema.VoteValue.VALUE_YES,
|
||||
@@ -155,9 +152,8 @@ export const generateYesVotes = (
|
||||
datetime: faker.date.past().toISOString(),
|
||||
};
|
||||
|
||||
votes.push(vote);
|
||||
}
|
||||
|
||||
return vote;
|
||||
});
|
||||
return {
|
||||
__typename: 'ProposalVoteSide',
|
||||
totalNumber: votes.length.toString(),
|
||||
@@ -176,8 +172,7 @@ export const generateNoVotes = (
|
||||
fixedTokenValue?: number,
|
||||
totalEquityLikeShareWeight?: string
|
||||
): Votes => {
|
||||
const votes = [];
|
||||
for (let i = 0; i < numberOfVotes; i++) {
|
||||
const votes = Array.from(Array(numberOfVotes)).map(() => {
|
||||
const vote: Vote = {
|
||||
__typename: 'Vote',
|
||||
value: Schema.VoteValue.VALUE_NO,
|
||||
@@ -212,9 +207,8 @@ export const generateNoVotes = (
|
||||
},
|
||||
datetime: faker.date.past().toISOString(),
|
||||
};
|
||||
votes.push(vote);
|
||||
}
|
||||
|
||||
return vote;
|
||||
});
|
||||
return {
|
||||
__typename: 'ProposalVoteSide',
|
||||
totalNumber: votes.length.toString(),
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
import type { ProposalQuery } from './proposal/__generated__/Proposal';
|
||||
|
||||
/**
|
||||
* The default Proposal type needs extracting from the ProposalNode union type
|
||||
* as lots of fields on the original type don't exist on BatchProposal. Eventually
|
||||
* we will support BatchProposal but for now we don't
|
||||
*/
|
||||
export type Proposal = Extract<
|
||||
ProposalQuery['proposal'],
|
||||
{ __typename?: 'Proposal' }
|
||||
>;
|
||||
@@ -104,10 +104,6 @@
|
||||
list-style: circle;
|
||||
}
|
||||
|
||||
.react-markdown-container a {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.jsondiffpatch-delta,
|
||||
.jsondiffpatch-delta pre {
|
||||
font-family: 'Roboto Mono', monospace !important;
|
||||
|
||||
@@ -0,0 +1,182 @@
|
||||
import * as Schema from '@vegaprotocol/types';
|
||||
|
||||
const expirtyTooltip = 'expiry-tooltip';
|
||||
const externalLink = 'external-link';
|
||||
const itemHeader = 'item-header';
|
||||
const itemValue = 'item-value';
|
||||
const link = 'link';
|
||||
const liquidityLink = 'view-liquidity-link';
|
||||
const liquiditySupplied = 'liquidity-supplied';
|
||||
const liquiditySuppliedTooltip = 'liquidity-supplied-tooltip';
|
||||
const marketChange = 'market-change';
|
||||
const marketExpiry = 'market-expiry';
|
||||
const marketMode = 'market-trading-mode';
|
||||
const marketName = 'header-title';
|
||||
const marketPrice = 'market-price';
|
||||
const marketSettlement = 'market-settlement-asset';
|
||||
const marketSummaryBlock = 'header-summary';
|
||||
const marketVolume = 'market-volume';
|
||||
const percentageValue = 'price-change-percentage';
|
||||
const priceChangeValue = 'price-change';
|
||||
const tradingModeTooltip = 'trading-mode-tooltip';
|
||||
|
||||
describe('Market trading page', () => {
|
||||
before(() => {
|
||||
cy.clearAllLocalStorage();
|
||||
cy.mockTradingPage(
|
||||
Schema.MarketState.STATE_ACTIVE,
|
||||
Schema.MarketTradingMode.TRADING_MODE_MONITORING_AUCTION,
|
||||
Schema.AuctionTrigger.AUCTION_TRIGGER_LIQUIDITY_TARGET_NOT_MET
|
||||
);
|
||||
cy.mockSubscription();
|
||||
cy.setOnBoardingViewed();
|
||||
cy.visit('/#/markets/market-0');
|
||||
cy.wait('@MarketData');
|
||||
cy.getByTestId(marketSummaryBlock).should('be.visible');
|
||||
});
|
||||
|
||||
describe('Market summary', { tags: '@smoke' }, () => {
|
||||
// 7002-SORD-001
|
||||
// 7002-SORD-002
|
||||
it('must display market name', () => {
|
||||
// 6002-MDET-001
|
||||
cy.getByTestId(marketName).should('not.be.empty');
|
||||
});
|
||||
|
||||
it('must see market expiry', () => {
|
||||
// 6002-MDET-002
|
||||
cy.getByTestId(marketSummaryBlock).within(() => {
|
||||
cy.getByTestId(marketExpiry).within(() => {
|
||||
cy.getByTestId(itemHeader).should('have.text', 'Expiry');
|
||||
cy.getByTestId(itemValue).should('not.be.empty');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('must see market price', () => {
|
||||
// 6002-MDET-003
|
||||
cy.getByTestId(marketSummaryBlock).within(() => {
|
||||
cy.getByTestId(marketPrice).within(() => {
|
||||
cy.getByTestId(itemHeader).should('have.text', 'Mark Price');
|
||||
cy.getByTestId(itemValue).should('not.be.empty');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('must see market change', () => {
|
||||
// 6002-MDET-004
|
||||
cy.getByTestId(marketSummaryBlock).within(() => {
|
||||
cy.getByTestId(marketChange).within(() => {
|
||||
cy.getByTestId(itemHeader).should('have.text', 'Change (24h)');
|
||||
cy.getByTestId(percentageValue).should('not.be.empty');
|
||||
cy.getByTestId(priceChangeValue).should('not.be.empty');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('must see market volume', () => {
|
||||
// 6002-MDET-005
|
||||
cy.getByTestId(marketSummaryBlock).within(() => {
|
||||
cy.getByTestId(marketVolume).within(() => {
|
||||
cy.getByTestId(itemHeader).should('have.text', 'Volume (24h)');
|
||||
cy.getByTestId(itemValue).should('not.be.empty');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('must see market settlement', () => {
|
||||
// 6002-MDET-008
|
||||
cy.getByTestId(marketSummaryBlock).within(() => {
|
||||
cy.getByTestId(marketSettlement).within(() => {
|
||||
cy.getByTestId(itemHeader).should('have.text', 'Settlement asset');
|
||||
cy.getByTestId(itemValue).should('not.be.empty');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Market tooltips', { tags: '@smoke' }, () => {
|
||||
it('should see expiry tooltip', () => {
|
||||
cy.getByTestId(marketSummaryBlock).within(() => {
|
||||
cy.getByTestId(marketExpiry).within(() => {
|
||||
cy.getByTestId(itemValue)
|
||||
.should('have.text', 'Not time-based')
|
||||
.realHover();
|
||||
});
|
||||
});
|
||||
cy.getByTestId(expirtyTooltip)
|
||||
.eq(0)
|
||||
.should(
|
||||
'contain.text',
|
||||
'This market expires when triggered by its oracle, not on a set date.'
|
||||
)
|
||||
.within(() => {
|
||||
cy.getByTestId(link)
|
||||
.should('have.attr', 'href')
|
||||
.and('include', Cypress.env('EXPLORER_URL'));
|
||||
});
|
||||
});
|
||||
|
||||
it('should see trading conditions tooltip', () => {
|
||||
const toolTipLabel = 'tooltip-label';
|
||||
const toolTipValue = 'tooltip-value';
|
||||
const auctionToolTipLabels = [
|
||||
'Auction start',
|
||||
'Est. auction end',
|
||||
'Target liquidity',
|
||||
'Current liquidity',
|
||||
'Est. uncrossing price',
|
||||
'Est. uncrossing vol',
|
||||
];
|
||||
|
||||
cy.getByTestId(marketSummaryBlock).within(() => {
|
||||
cy.getByTestId(marketMode).within(() => {
|
||||
cy.getByTestId(itemValue)
|
||||
.should('contain.text', 'Monitoring auction')
|
||||
.and('contain.text', 'liquidity')
|
||||
.realHover();
|
||||
});
|
||||
});
|
||||
cy.getByTestId(tradingModeTooltip)
|
||||
.should(
|
||||
'contain.text',
|
||||
'This market is in auction until it reaches sufficient liquidity.'
|
||||
)
|
||||
.eq(0)
|
||||
.within(() => {
|
||||
cy.getByTestId(externalLink)
|
||||
.should('have.attr', 'href')
|
||||
.and('include', Cypress.env('TRADING_MODE_LINK'));
|
||||
|
||||
for (let i = 0; i < 6; i++) {
|
||||
cy.getByTestId(toolTipLabel)
|
||||
.eq(i)
|
||||
.should('have.text', auctionToolTipLabels[i]);
|
||||
cy.getByTestId(toolTipValue).eq(i).should('not.be.empty');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('should see liquidity supplied tooltip', () => {
|
||||
cy.getByTestId(marketSummaryBlock).within(() => {
|
||||
cy.getByTestId(liquiditySupplied).within(() => {
|
||||
cy.getByTestId(itemValue).realHover();
|
||||
});
|
||||
});
|
||||
cy.getByTestId(liquiditySuppliedTooltip)
|
||||
.should('contain.text', 'Supplied stake')
|
||||
.and('contain.text', 'Target stake')
|
||||
.first()
|
||||
.within(() => {
|
||||
cy.getByTestId(liquidityLink).should(
|
||||
'have.text',
|
||||
'View liquidity provision table'
|
||||
);
|
||||
cy.getByTestId(externalLink).should(
|
||||
'have.text',
|
||||
'Learn about providing liquidity'
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,103 @@
|
||||
import * as Schema from '@vegaprotocol/types';
|
||||
import type { OrderSubmission } from '@vegaprotocol/wallet';
|
||||
import { createOrder } from '../support/create-order';
|
||||
|
||||
describe(
|
||||
'vega wallet - prompt',
|
||||
{ tags: '@regression', testIsolation: true },
|
||||
() => {
|
||||
describe('must submit order', { tags: '@smoke' }, () => {
|
||||
// 7002-SORD-039
|
||||
beforeEach(() => {
|
||||
cy.setVegaWallet();
|
||||
cy.mockTradingPage();
|
||||
cy.mockSubscription();
|
||||
cy.visit('/#/markets/market-0');
|
||||
cy.wait('@Markets');
|
||||
});
|
||||
|
||||
it('must see a prompt to check connected vega wallet to approve transaction', () => {
|
||||
// 0003-WTXN-002
|
||||
cy.mockVegaWalletTransaction(1000);
|
||||
const order: OrderSubmission = {
|
||||
marketId: 'market-0',
|
||||
type: Schema.OrderType.TYPE_MARKET,
|
||||
side: Schema.Side.SIDE_BUY,
|
||||
timeInForce: Schema.OrderTimeInForce.TIME_IN_FORCE_FOK,
|
||||
size: '100',
|
||||
};
|
||||
createOrder(order);
|
||||
cy.getByTestId('toast-content').should(
|
||||
'contain.text',
|
||||
'Please go to your Vega wallet application and approve or reject the transaction.'
|
||||
);
|
||||
});
|
||||
|
||||
it('must show error returned by wallet ', () => {
|
||||
// 0003-WTXN-009
|
||||
// 0003-WTXN-011
|
||||
// 0002-WCON-016
|
||||
// 0003-WTXN-008
|
||||
|
||||
//trigger error from the wallet
|
||||
cy.intercept('POST', 'http://localhost:1789/api/v2/requests', (req) => {
|
||||
req.on('response', (res) => {
|
||||
res.send({
|
||||
jsonrpc: '2.0',
|
||||
id: '1',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
const order: OrderSubmission = {
|
||||
marketId: 'market-0',
|
||||
type: Schema.OrderType.TYPE_MARKET,
|
||||
side: Schema.Side.SIDE_BUY,
|
||||
timeInForce: Schema.OrderTimeInForce.TIME_IN_FORCE_FOK,
|
||||
size: '100',
|
||||
};
|
||||
createOrder(order);
|
||||
cy.getByTestId('toast-content').should(
|
||||
'contain.text',
|
||||
'The connection to your Vega Wallet has been lost.'
|
||||
);
|
||||
cy.getByTestId('connect-vega-wallet').click();
|
||||
cy.getByTestId('dialog-content').should('be.visible');
|
||||
});
|
||||
|
||||
it('must see that the order was rejected by the connected wallet', () => {
|
||||
// 0003-WTXN-007
|
||||
|
||||
//trigger rejection error from the wallet
|
||||
cy.intercept('POST', 'http://localhost:1789/api/v2/requests', (req) => {
|
||||
req.alias = 'client.send_transaction';
|
||||
req.reply({
|
||||
statusCode: 400,
|
||||
body: {
|
||||
jsonrpc: '2.0',
|
||||
error: {
|
||||
code: 3001,
|
||||
data: 'the user rejected the wallet connection',
|
||||
message: 'User error',
|
||||
},
|
||||
id: '0',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
const order: OrderSubmission = {
|
||||
marketId: 'market-0',
|
||||
type: Schema.OrderType.TYPE_MARKET,
|
||||
side: Schema.Side.SIDE_BUY,
|
||||
timeInForce: Schema.OrderTimeInForce.TIME_IN_FORCE_FOK,
|
||||
size: '100',
|
||||
};
|
||||
createOrder(order);
|
||||
cy.getByTestId('toast-content').should(
|
||||
'contain.text',
|
||||
'Error occurredthe user rejected the wallet connection'
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
@@ -26,6 +26,18 @@ describe('ethereum wallet', { tags: '@smoke', testIsolation: true }, () => {
|
||||
cy.getByTestId('tab-deposits').should('not.be.empty');
|
||||
});
|
||||
|
||||
it.skip('should see QR code modal for WalletConnect', () => {
|
||||
// 0004-EWAL-003
|
||||
|
||||
cy.getByTestId('Deposits').click();
|
||||
cy.getByTestId('deposit-button').click();
|
||||
cy.getByTestId('connect-eth-wallet-btn').click();
|
||||
cy.getByTestId('web3-connector-list').should('exist');
|
||||
cy.getByTestId('web3-connector-WalletConnect').click();
|
||||
// testing if exists rather than visible because of the long loading time
|
||||
cy.get('#w3m-modal').should('exist');
|
||||
});
|
||||
|
||||
it('able to disconnect eth wallet', () => {
|
||||
// 0004-EWAL-004
|
||||
// 0004-EWAL-005
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
import {
|
||||
mockConnectWallet,
|
||||
mockConnectWalletWithUserError,
|
||||
} from '@vegaprotocol/cypress';
|
||||
|
||||
const connectVegaBtn = 'connect-vega-wallet';
|
||||
const manageVegaBtn = 'manage-vega-wallet';
|
||||
const dialogContent = 'dialog-content';
|
||||
|
||||
describe('connect vega wallet', { tags: '@smoke', testIsolation: true }, () => {
|
||||
beforeEach(() => {
|
||||
// Using portfolio page as it requires vega wallet connection
|
||||
cy.mockTradingPage();
|
||||
cy.mockSubscription();
|
||||
cy.setOnBoardingViewed();
|
||||
cy.visit('/#/portfolio');
|
||||
cy.get('[data-testid="pathname-/portfolio"]').should('exist');
|
||||
});
|
||||
|
||||
it('can connect', () => {
|
||||
// 0002-WCON-002
|
||||
// 0002-WCON-005
|
||||
// 0002-WCON-007
|
||||
// 0002-WCON-009
|
||||
|
||||
mockConnectWallet();
|
||||
cy.getByTestId(connectVegaBtn).click();
|
||||
cy.getByTestId('connectors-list')
|
||||
.find('[data-testid="connector-jsonRpc"]')
|
||||
.click();
|
||||
cy.wait('@walletReq');
|
||||
cy.getByTestId(dialogContent).should(
|
||||
'contain.text',
|
||||
'Approve the connection from your Vega wallet app.'
|
||||
);
|
||||
cy.getByTestId(dialogContent).should('not.exist');
|
||||
cy.getByTestId(manageVegaBtn).should('exist');
|
||||
});
|
||||
|
||||
it('can not connect', () => {
|
||||
// 0002-WCON-002
|
||||
// 0002-WCON-005
|
||||
// 0002-WCON-007
|
||||
// 0002-WCON-015
|
||||
|
||||
mockConnectWalletWithUserError();
|
||||
cy.getByTestId(connectVegaBtn).click();
|
||||
cy.getByTestId('connectors-list')
|
||||
.find('[data-testid="connector-jsonRpc"]')
|
||||
.click();
|
||||
cy.getByTestId('dialog-content')
|
||||
.should('contain.text', 'User error')
|
||||
.and('contain.text', 'the user rejected the wallet connection');
|
||||
});
|
||||
|
||||
it('can change selected public key and disconnect', () => {
|
||||
// 0002-WCON-022
|
||||
// 0002-WCON-023
|
||||
// 0002-WCON-025
|
||||
// 0002-WCON-026
|
||||
// 0002-WCON-021
|
||||
// 0002-WCON-027
|
||||
// 0002-WCON-030
|
||||
// 0002-WCON-029
|
||||
// 0002-WCON-008
|
||||
// 0002-WCON-035
|
||||
// 0002-WCON-014
|
||||
// 0002-WCON-010
|
||||
// 0003-WTXN-004
|
||||
|
||||
mockConnectWallet();
|
||||
const key2 = Cypress.env('VEGA_PUBLIC_KEY2');
|
||||
const truncatedKey2 = Cypress.env('TRUNCATED_VEGA_PUBLIC_KEY2');
|
||||
cy.connectVegaWallet();
|
||||
cy.getByTestId('manage-vega-wallet').click();
|
||||
cy.getByTestId('keypair-list').should('exist');
|
||||
cy.getByTestId(`key-${key2}`).should('contain.text', truncatedKey2);
|
||||
cy.getByTestId(`key-${key2}`)
|
||||
.find('[data-testid="copy-vega-public-key"]')
|
||||
.should('be.visible');
|
||||
cy.get(`[data-testid="key-${key2}"] > .mr-2`).click();
|
||||
cy.getByTestId('keypair-list')
|
||||
.find('[data-state="checked"]')
|
||||
.should('be.visible');
|
||||
cy.getByTestId('disconnect').click();
|
||||
cy.getByTestId('connect-vega-wallet').should('exist');
|
||||
cy.getByTestId('manage-vega-wallet').should('not.exist');
|
||||
cy.getByTestId('connect-vega-wallet').click();
|
||||
cy.contains('Enter a custom wallet location');
|
||||
});
|
||||
});
|
||||
@@ -25,4 +25,3 @@ NX_ICEBERG_ORDERS=true
|
||||
# NX_PRODUCT_PERPETUALS
|
||||
NX_METAMASK_SNAPS=true
|
||||
NX_REFERRALS=true
|
||||
NX_TEAM_COMPETITION=true
|
||||
|
||||
@@ -22,9 +22,9 @@ NX_WALLETCONNECT_PROJECT_ID=fe8091dc35738863e509fc4947525c72
|
||||
NX_SUCCESSOR_MARKETS=true
|
||||
NX_STOP_ORDERS=true
|
||||
NX_ICEBERG_ORDERS=true
|
||||
# NX_PRODUCT_PERPETUALS
|
||||
NX_METAMASK_SNAPS=true
|
||||
NX_REFERRALS=true
|
||||
# NX_DISABLE_CLOSE_POSITION=false
|
||||
NX_REFERRALS=false
|
||||
|
||||
NX_TENDERMINT_URL=https://be.vega.community
|
||||
NX_TENDERMINT_WEBSOCKET_URL=wss://be.vega.community/websocket
|
||||
|
||||
@@ -24,7 +24,4 @@ NX_STOP_ORDERS=true
|
||||
NX_ICEBERG_ORDERS=true
|
||||
# NX_PRODUCT_PERPETUALS
|
||||
NX_METAMASK_SNAPS=true
|
||||
NX_REFERRALS=true
|
||||
|
||||
NX_CHARTING_LIBRARY_PATH=https://assets.vega.community/trading-view-bundle/v0.0.1/
|
||||
NX_CHARTING_LIBRARY_HASH=PDjWaqPFndDp+LCvqbKvntWriaqNzNpZ5i9R/BULzCg=
|
||||
NX_REFERRALS=true
|
||||
@@ -23,11 +23,9 @@ NX_WALLETCONNECT_PROJECT_ID=fe8091dc35738863e509fc4947525c72
|
||||
NX_SUCCESSOR_MARKETS=true
|
||||
NX_STOP_ORDERS=true
|
||||
NX_ICEBERG_ORDERS=true
|
||||
# NX_PRODUCT_PERPETUALS
|
||||
NX_METAMASK_SNAPS=true
|
||||
NX_REFERRALS=true
|
||||
|
||||
NX_TENDERMINT_URL=https://tm.be.testnet.vega.xyz
|
||||
NX_TENDERMINT_WEBSOCKET_URL=wss://be.testnet.vega.xyz/websocket
|
||||
|
||||
NX_CHARTING_LIBRARY_PATH=https://assets.vega.community/trading-view-bundle/v0.0.1/
|
||||
NX_CHARTING_LIBRARY_HASH=PDjWaqPFndDp+LCvqbKvntWriaqNzNpZ5i9R/BULzCg=
|
||||
|
||||
@@ -54,5 +54,3 @@ To run the UI automation tests with a mocked API, run:
|
||||
```bash
|
||||
yarn nx run trading-e2e:e2e
|
||||
```
|
||||
|
||||
To run tests with market sim please read [the readme](e2e/README.md).
|
||||
|
||||
@@ -1,318 +0,0 @@
|
||||
import { useSearchParams } from 'react-router-dom';
|
||||
import { Controller, useForm } from 'react-hook-form';
|
||||
import {
|
||||
Intent,
|
||||
TextArea,
|
||||
TradingAnchorButton,
|
||||
TradingButton,
|
||||
TradingCheckbox,
|
||||
TradingFormGroup,
|
||||
TradingInput,
|
||||
TradingInputError,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import {
|
||||
useVegaWallet,
|
||||
type CreateReferralSet,
|
||||
useVegaWalletDialogStore,
|
||||
} from '@vegaprotocol/wallet';
|
||||
import {
|
||||
addDecimalsFormatNumber,
|
||||
isValidVegaPublicKey,
|
||||
URL_REGEX,
|
||||
} from '@vegaprotocol/utils';
|
||||
import { useT } from '../../lib/use-t';
|
||||
import { useCreateReferralSet } from '../../lib/hooks/use-create-referral-set';
|
||||
import { DApp, TokenStaticLinks, useLinks } from '@vegaprotocol/environment';
|
||||
import { RainbowButton } from '../../components/rainbow-button';
|
||||
import { usePageTitle } from '../../lib/hooks/use-page-title';
|
||||
import { ErrorBoundary } from '../../components/error-boundary';
|
||||
import { Box } from '../../components/competitions/box';
|
||||
import { Links } from '../../lib/links';
|
||||
|
||||
interface FormFields {
|
||||
name: string;
|
||||
url: string;
|
||||
avatarUrl: string;
|
||||
private: boolean;
|
||||
allowList: string;
|
||||
}
|
||||
|
||||
export const CompetitionsCreateTeam = () => {
|
||||
const [searchParams] = useSearchParams();
|
||||
const isSolo = Boolean(searchParams.get('solo'));
|
||||
const t = useT();
|
||||
usePageTitle(t('Create a team'));
|
||||
|
||||
const { isReadOnly, pubKey } = useVegaWallet();
|
||||
const openWalletDialog = useVegaWalletDialogStore(
|
||||
(store) => store.openVegaWalletDialog
|
||||
);
|
||||
|
||||
return (
|
||||
<ErrorBoundary feature="create-team">
|
||||
<div className="relative h-full pt-5 overflow-y-auto">
|
||||
<div className="absolute top-0 left-0 w-full h-[40%] -z-10 bg-[40%_0px] bg-cover bg-no-repeat bg-local bg-[url(/cover.png)]">
|
||||
<div className="absolute top-o left-0 w-full h-full bg-gradient-to-t from-white dark:from-vega-cdark-900 to-transparent from-20% to-60%" />
|
||||
</div>
|
||||
<div className="lg:gap-6 container p-4 mx-auto">
|
||||
<div className="mx-auto md:w-2/3 max-w-xl">
|
||||
<Box className="flex flex-col gap-4">
|
||||
<h1 className="calt text-2xl lg:text-3xl xl:text-5xl">
|
||||
{t('Create a team')}
|
||||
</h1>
|
||||
{pubKey && !isReadOnly ? (
|
||||
<CreateTeamFormContainer isSolo={isSolo} />
|
||||
) : (
|
||||
<>
|
||||
<p>
|
||||
{t(
|
||||
'Create a team to participate in team based rewards as well as access the discount benefits of the current referral program.'
|
||||
)}
|
||||
</p>
|
||||
<RainbowButton variant="border" onClick={openWalletDialog}>
|
||||
{t('Connect wallet')}
|
||||
</RainbowButton>
|
||||
</>
|
||||
)}
|
||||
</Box>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ErrorBoundary>
|
||||
);
|
||||
};
|
||||
|
||||
const CreateTeamFormContainer = ({ isSolo }: { isSolo: boolean }) => {
|
||||
const t = useT();
|
||||
const createLink = useLinks(DApp.Governance);
|
||||
|
||||
const { err, status, code, isEligible, requiredStake, onSubmit } =
|
||||
useCreateReferralSet({
|
||||
onSuccess: (code) => {
|
||||
// For some reason team creation takes a long time, too long even to make
|
||||
// polling viable, so its not feasible to navigate to the team page
|
||||
// after creation
|
||||
//
|
||||
// navigate(Links.COMPETITIONS_TEAM(code));
|
||||
},
|
||||
});
|
||||
|
||||
if (status === 'success') {
|
||||
return (
|
||||
<div className="flex flex-col items-start gap-2">
|
||||
<p className="text-sm">{t('Team creation transaction successful')}</p>
|
||||
{code && (
|
||||
<>
|
||||
<p className="text-sm">
|
||||
Your team ID is:{' '}
|
||||
<span className="font-mono break-all">{code}</span>
|
||||
</p>
|
||||
<TradingAnchorButton
|
||||
href={Links.COMPETITIONS_TEAM(code)}
|
||||
intent={Intent.Info}
|
||||
size="small"
|
||||
>
|
||||
{t('View team')}
|
||||
</TradingAnchorButton>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (!isEligible) {
|
||||
return (
|
||||
<div className="flex flex-col gap-4">
|
||||
{requiredStake !== undefined && (
|
||||
<p>
|
||||
{t(
|
||||
'You need at least {{requiredStake}} VEGA staked to generate a referral code and participate in the referral program.',
|
||||
{
|
||||
requiredStake: addDecimalsFormatNumber(
|
||||
requiredStake.toString(),
|
||||
18
|
||||
),
|
||||
}
|
||||
)}
|
||||
</p>
|
||||
)}
|
||||
<TradingAnchorButton
|
||||
href={createLink(TokenStaticLinks.ASSOCIATE)}
|
||||
intent={Intent.Primary}
|
||||
target="_blank"
|
||||
>
|
||||
{t('Stake some $VEGA now')}
|
||||
</TradingAnchorButton>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<CreateTeamForm
|
||||
onSubmit={onSubmit}
|
||||
status={status}
|
||||
err={err}
|
||||
isSolo={isSolo}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const CreateTeamForm = ({
|
||||
status,
|
||||
err,
|
||||
isSolo,
|
||||
onSubmit,
|
||||
}: {
|
||||
status: ReturnType<typeof useCreateReferralSet>['status'];
|
||||
err: ReturnType<typeof useCreateReferralSet>['err'];
|
||||
isSolo: boolean;
|
||||
onSubmit: (tx: CreateReferralSet) => void;
|
||||
}) => {
|
||||
const t = useT();
|
||||
|
||||
const {
|
||||
register,
|
||||
handleSubmit,
|
||||
control,
|
||||
watch,
|
||||
formState: { errors },
|
||||
} = useForm<FormFields>({
|
||||
defaultValues: {
|
||||
private: isSolo,
|
||||
},
|
||||
});
|
||||
|
||||
const isPrivate = watch('private');
|
||||
|
||||
const createTeam = (fields: FormFields) => {
|
||||
onSubmit({
|
||||
createReferralSet: {
|
||||
isTeam: true,
|
||||
team: {
|
||||
name: fields.name,
|
||||
teamUrl: fields.url,
|
||||
avatarUrl: fields.avatarUrl,
|
||||
closed: fields.private,
|
||||
allowList: fields.private ? parseAllowListText(fields.allowList) : [],
|
||||
},
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<form onSubmit={handleSubmit(createTeam)}>
|
||||
<TradingFormGroup label={t('Team name')} labelFor="name">
|
||||
<TradingInput {...register('name', { required: t('Required') })} />
|
||||
{errors.name?.message && (
|
||||
<TradingInputError forInput="name">
|
||||
{errors.name.message}
|
||||
</TradingInputError>
|
||||
)}
|
||||
</TradingFormGroup>
|
||||
<TradingFormGroup
|
||||
label={t('URL')}
|
||||
labelFor="url"
|
||||
labelDescription={t(
|
||||
'Provide a link so users can learn more about your team'
|
||||
)}
|
||||
>
|
||||
<TradingInput
|
||||
{...register('url', {
|
||||
pattern: { value: URL_REGEX, message: t('Invalid URL') },
|
||||
})}
|
||||
/>
|
||||
{errors.url?.message && (
|
||||
<TradingInputError forInput="url">
|
||||
{errors.url.message}
|
||||
</TradingInputError>
|
||||
)}
|
||||
</TradingFormGroup>
|
||||
<TradingFormGroup
|
||||
label={t('Avatar URL')}
|
||||
labelFor="avatarUrl"
|
||||
labelDescription={t('Provide a URL to a hosted image')}
|
||||
>
|
||||
<TradingInput
|
||||
{...register('avatarUrl', {
|
||||
pattern: {
|
||||
value: URL_REGEX,
|
||||
message: t('Invalid image URL'),
|
||||
},
|
||||
})}
|
||||
/>
|
||||
{errors.avatarUrl?.message && (
|
||||
<TradingInputError forInput="avatarUrl">
|
||||
{errors.avatarUrl.message}
|
||||
</TradingInputError>
|
||||
)}
|
||||
</TradingFormGroup>
|
||||
<TradingFormGroup
|
||||
label={t('Make team private')}
|
||||
labelFor="private"
|
||||
hideLabel={true}
|
||||
>
|
||||
<Controller
|
||||
name="private"
|
||||
control={control}
|
||||
render={({ field }) => {
|
||||
return (
|
||||
<TradingCheckbox
|
||||
label={t('Make team private')}
|
||||
checked={field.value}
|
||||
onCheckedChange={(value) => {
|
||||
field.onChange(value);
|
||||
}}
|
||||
disabled={isSolo}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</TradingFormGroup>
|
||||
{isPrivate && (
|
||||
<TradingFormGroup
|
||||
label={t('Public key allow list')}
|
||||
labelFor="allowList"
|
||||
labelDescription={t(
|
||||
'Use a comma separated list to allow only specific public keys to join the team'
|
||||
)}
|
||||
>
|
||||
<TextArea
|
||||
{...register('allowList', {
|
||||
required: t('Required'),
|
||||
disabled: isSolo,
|
||||
validate: {
|
||||
allowList: (value) => {
|
||||
const publicKeys = parseAllowListText(value);
|
||||
if (publicKeys.every((pk) => isValidVegaPublicKey(pk))) {
|
||||
return true;
|
||||
}
|
||||
return t('Invalid public key found in allow list');
|
||||
},
|
||||
},
|
||||
})}
|
||||
/>
|
||||
{errors.allowList?.message && (
|
||||
<TradingInputError forInput="avatarUrl">
|
||||
{errors.allowList.message}
|
||||
</TradingInputError>
|
||||
)}
|
||||
</TradingFormGroup>
|
||||
)}
|
||||
{err && <p className="text-danger text-xs mb-4 capitalize">{err}</p>}
|
||||
<TradingButton
|
||||
type="submit"
|
||||
intent={Intent.Info}
|
||||
disabled={status === 'loading'}
|
||||
>
|
||||
{status === 'loading' ? t('Confirm in wallet...') : t('Create')}
|
||||
</TradingButton>
|
||||
</form>
|
||||
);
|
||||
};
|
||||
|
||||
const parseAllowListText = (str: string) => {
|
||||
return str
|
||||
.split(',')
|
||||
.map((v) => v.trim())
|
||||
.filter(Boolean);
|
||||
};
|
||||
@@ -1,133 +0,0 @@
|
||||
import { useT } from '../../lib/use-t';
|
||||
import { ErrorBoundary } from '@sentry/react';
|
||||
import { CompetitionsHeader } from '../../components/competitions/competitions-header';
|
||||
import { Intent, Loader, TradingButton } from '@vegaprotocol/ui-toolkit';
|
||||
|
||||
import { useGames } from './hooks/use-games';
|
||||
import { useCurrentEpochInfoQuery } from '../referrals/hooks/__generated__/Epoch';
|
||||
import { Link, useNavigate } from 'react-router-dom';
|
||||
import { Links } from '../../lib/links';
|
||||
import {
|
||||
CompetitionsAction,
|
||||
CompetitionsActionsContainer,
|
||||
} from '../../components/competitions/competitions-cta';
|
||||
import { GamesContainer } from '../../components/competitions/games-container';
|
||||
import { CompetitionsLeaderboard } from '../../components/competitions/competitions-leaderboard';
|
||||
import { useTeams } from './hooks/use-teams';
|
||||
import take from 'lodash/take';
|
||||
import { usePageTitle } from '../../lib/hooks/use-page-title';
|
||||
|
||||
export const CompetitionsHome = () => {
|
||||
const t = useT();
|
||||
const navigate = useNavigate();
|
||||
|
||||
usePageTitle(t('Competitions'));
|
||||
|
||||
const { data: epochData } = useCurrentEpochInfoQuery();
|
||||
const currentEpoch = Number(epochData?.epoch.id);
|
||||
|
||||
const { data: gamesData, loading: gamesLoading } = useGames({
|
||||
onlyActive: true,
|
||||
currentEpoch,
|
||||
});
|
||||
|
||||
const { data: teamsData, loading: teamsLoading } = useTeams({
|
||||
sortByField: ['totalQuantumRewards'],
|
||||
order: 'desc',
|
||||
});
|
||||
|
||||
return (
|
||||
<ErrorBoundary>
|
||||
<CompetitionsHeader title={t('Competitions')}>
|
||||
<p className="text-lg mb-1">
|
||||
{t(
|
||||
'Be a team player! Participate in games and work together to rake in as much profit to win.'
|
||||
)}
|
||||
</p>
|
||||
</CompetitionsHeader>
|
||||
|
||||
{/** Get started */}
|
||||
<h2 className="text-2xl mb-6">{t('Get started')}</h2>
|
||||
|
||||
<CompetitionsActionsContainer>
|
||||
<CompetitionsAction
|
||||
variant="A"
|
||||
title={t('Create a team')}
|
||||
description={t(
|
||||
'Lorem ipsum dolor sit amet, consectetur adipisicing elit placeat ipsum minus nemo error dicta.'
|
||||
)}
|
||||
actionElement={
|
||||
<TradingButton
|
||||
intent={Intent.Primary}
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
navigate(Links.COMPETITIONS_CREATE_TEAM());
|
||||
}}
|
||||
>
|
||||
{t('Create a public team')}
|
||||
</TradingButton>
|
||||
}
|
||||
/>
|
||||
<CompetitionsAction
|
||||
variant="B"
|
||||
title={t('Solo team / lone wolf')}
|
||||
description={t(
|
||||
'Lorem ipsum dolor sit amet, consectetur adipisicing elit placeat ipsum minus nemo error dicta.'
|
||||
)}
|
||||
actionElement={
|
||||
<TradingButton
|
||||
intent={Intent.Primary}
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
navigate(Links.COMPETITIONS_CREATE_TEAM_SOLO());
|
||||
}}
|
||||
>
|
||||
{t('Create a private team')}
|
||||
</TradingButton>
|
||||
}
|
||||
/>
|
||||
<CompetitionsAction
|
||||
variant="C"
|
||||
title={t('Join a team')}
|
||||
description={t(
|
||||
'Lorem ipsum dolor sit amet, consectetur adipisicing elit placeat ipsum minus nemo error dicta.'
|
||||
)}
|
||||
actionElement={
|
||||
<TradingButton
|
||||
intent={Intent.Primary}
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
navigate(Links.COMPETITIONS_TEAMS());
|
||||
}}
|
||||
>
|
||||
{t('Choose a team')}
|
||||
</TradingButton>
|
||||
}
|
||||
/>
|
||||
</CompetitionsActionsContainer>
|
||||
|
||||
{/** List of available games */}
|
||||
<h2 className="text-2xl mb-6">{t('Games')}</h2>
|
||||
|
||||
{gamesLoading ? (
|
||||
<Loader size="small" />
|
||||
) : (
|
||||
<GamesContainer data={gamesData} currentEpoch={currentEpoch} />
|
||||
)}
|
||||
|
||||
{/** The teams ranking */}
|
||||
<div className="mb-6 flex flex-row items-baseline justify-between">
|
||||
<h2 className="text-2xl">{t('Leaderboard')}</h2>
|
||||
<Link to={Links.COMPETITIONS_TEAMS()} className="text-sm underline">
|
||||
{t('View all teams')}
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
{teamsLoading ? (
|
||||
<Loader size="small" />
|
||||
) : (
|
||||
<CompetitionsLeaderboard data={take(teamsData, 10)} />
|
||||
)}
|
||||
</ErrorBoundary>
|
||||
);
|
||||
};
|
||||
@@ -1,404 +0,0 @@
|
||||
import { useState, type ReactNode, type ButtonHTMLAttributes } from 'react';
|
||||
import { Link, useParams } from 'react-router-dom';
|
||||
import orderBy from 'lodash/orderBy';
|
||||
import countBy from 'lodash/countBy';
|
||||
import {
|
||||
TradingButton as Button,
|
||||
Intent,
|
||||
Pill,
|
||||
VegaIcon,
|
||||
VegaIconNames,
|
||||
Tooltip,
|
||||
Splash,
|
||||
truncateMiddle,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import classNames from 'classnames';
|
||||
import { useT } from '../../lib/use-t';
|
||||
import { Table } from '../../components/table';
|
||||
import { formatNumberRounded, getDateTimeFormat } from '@vegaprotocol/utils';
|
||||
import {
|
||||
useTeam,
|
||||
type Team as TeamType,
|
||||
type TeamStats,
|
||||
type Member,
|
||||
type TeamGame,
|
||||
} from './hooks/use-team';
|
||||
import { DApp, EXPLORER_PARTIES, useLinks } from '@vegaprotocol/environment';
|
||||
import BigNumber from 'bignumber.js';
|
||||
import { TeamAvatar } from '../../components/competitions/team-avatar';
|
||||
import { usePageTitle } from '../../lib/hooks/use-page-title';
|
||||
import { ErrorBoundary } from '../../components/error-boundary';
|
||||
|
||||
export const CompetitionsTeam = () => {
|
||||
const t = useT();
|
||||
const { teamId } = useParams<{ teamId: string }>();
|
||||
usePageTitle([t('Competitions'), t('Team')]);
|
||||
return (
|
||||
<ErrorBoundary feature="team">
|
||||
<TeamPageContainer teamId={teamId} />
|
||||
</ErrorBoundary>
|
||||
);
|
||||
};
|
||||
|
||||
const TeamPageContainer = ({ teamId }: { teamId: string | undefined }) => {
|
||||
const t = useT();
|
||||
const { team, stats, partyInTeam, members, games, loading } = useTeam(teamId);
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<Splash>
|
||||
<p>Loading...</p>
|
||||
</Splash>
|
||||
);
|
||||
}
|
||||
|
||||
if (!team) {
|
||||
return (
|
||||
<Splash>
|
||||
<p>{t('Page not found')}</p>
|
||||
</Splash>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<TeamPage
|
||||
team={team}
|
||||
stats={stats}
|
||||
partyInTeam={partyInTeam}
|
||||
members={members}
|
||||
games={games}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const TeamPage = ({
|
||||
team,
|
||||
stats,
|
||||
partyInTeam,
|
||||
members,
|
||||
games,
|
||||
}: {
|
||||
team: TeamType;
|
||||
stats?: TeamStats;
|
||||
partyInTeam: boolean;
|
||||
members?: Member[];
|
||||
games?: TeamGame[];
|
||||
}) => {
|
||||
const t = useT();
|
||||
const [showGames, setShowGames] = useState(true);
|
||||
|
||||
return (
|
||||
<div className="relative h-full overflow-y-auto pt-5">
|
||||
<div className="absolute top-0 left-0 w-full h-[40%] -z-10 bg-[40%_0px] bg-cover bg-no-repeat bg-local bg-[url(/cover.png)]">
|
||||
<div className="absolute top-o left-0 w-full h-full bg-gradient-to-t from-white dark:from-vega-cdark-900 to-transparent from-20% to-60%" />
|
||||
</div>
|
||||
<div className="flex flex-col gap-4 lg:gap-6 container p-4 mx-auto">
|
||||
<header className="flex gap-3 lg:gap-4 pt-5 lg:pt-10">
|
||||
<TeamAvatar teamId={team.teamId} imgUrl={team.avatarUrl} />
|
||||
<div className="flex flex-col items-start gap-1 lg:gap-3">
|
||||
<h1 className="calt text-2xl lg:text-3xl xl:text-5xl">
|
||||
{team.name}
|
||||
</h1>
|
||||
<JoinButton joined={partyInTeam} />
|
||||
</div>
|
||||
</header>
|
||||
<StatSection>
|
||||
<StatList>
|
||||
<Stat value={members ? members.length : 0} label={t('Members')} />
|
||||
<Stat
|
||||
value={stats ? stats.totalGamesPlayed : 0}
|
||||
label={t('Total games')}
|
||||
tooltip={t('Total number of games this team has participated in')}
|
||||
/>
|
||||
<StatSectionSeparator />
|
||||
<Stat
|
||||
value={
|
||||
stats
|
||||
? formatNumberRounded(
|
||||
new BigNumber(stats.totalQuantumVolume),
|
||||
'1e3'
|
||||
)
|
||||
: 0
|
||||
}
|
||||
label={t('Total volume')}
|
||||
/>
|
||||
<Stat
|
||||
value={
|
||||
stats
|
||||
? formatNumberRounded(
|
||||
new BigNumber(stats.totalQuantumRewards),
|
||||
'1e3'
|
||||
)
|
||||
: 0
|
||||
}
|
||||
label={t('Rewards paid')}
|
||||
tooltip={'Total amount of rewards paid out to this team in qUSD'}
|
||||
/>
|
||||
</StatList>
|
||||
</StatSection>
|
||||
{games && games.length ? (
|
||||
<StatSection>
|
||||
<FavoriteGame games={games} />
|
||||
<StatSectionSeparator />
|
||||
<LatestResults games={games} />
|
||||
</StatSection>
|
||||
) : null}
|
||||
<section>
|
||||
<div className="flex gap-4 lg:gap-8 mb-4 border-b border-default">
|
||||
<ToggleButton active={showGames} onClick={() => setShowGames(true)}>
|
||||
{t('Games ({{count}})', { count: games ? games.length : 0 })}
|
||||
</ToggleButton>
|
||||
<ToggleButton
|
||||
active={!showGames}
|
||||
onClick={() => setShowGames(false)}
|
||||
>
|
||||
{t('Members ({{count}})', {
|
||||
count: members ? members.length : 0,
|
||||
})}
|
||||
</ToggleButton>
|
||||
</div>
|
||||
{showGames ? <Games games={games} /> : <Members members={members} />}
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const Games = ({ games }: { games?: TeamGame[] }) => {
|
||||
const t = useT();
|
||||
|
||||
if (!games?.length) {
|
||||
return <p>{t('No games')}</p>;
|
||||
}
|
||||
|
||||
return (
|
||||
<Table
|
||||
columns={[
|
||||
{ name: 'rank', displayName: t('Rank') },
|
||||
{
|
||||
name: 'epoch',
|
||||
displayName: t('Epoch'),
|
||||
headerClassName: 'hidden md:block',
|
||||
className: 'hidden md:block',
|
||||
},
|
||||
{ name: 'type', displayName: t('Type') },
|
||||
{ name: 'amount', displayName: t('Amount earned') },
|
||||
{
|
||||
name: 'teams',
|
||||
displayName: t('No. of participating teams'),
|
||||
headerClassName: 'hidden md:block',
|
||||
className: 'hidden md:block',
|
||||
},
|
||||
{ name: 'status', displayName: t('Status') },
|
||||
]}
|
||||
data={games.map((game) => ({
|
||||
rank: game.team.rank,
|
||||
epoch: game.epoch,
|
||||
type: game.team.rewardMetric,
|
||||
amount: game.team.totalRewardsEarned,
|
||||
teams: game.numberOfParticipants,
|
||||
}))}
|
||||
noCollapse={true}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const Members = ({ members }: { members?: Member[] }) => {
|
||||
const t = useT();
|
||||
|
||||
if (!members?.length) {
|
||||
return <p>{t('No members')}</p>;
|
||||
}
|
||||
|
||||
const data = orderBy(
|
||||
members.map((m) => ({
|
||||
referee: <RefereeCell pubkey={m.referee} />,
|
||||
joinedAt: getDateTimeFormat().format(new Date(m.joinedAt)),
|
||||
joinedAtEpoch: Number(m.joinedAtEpoch),
|
||||
explorerLink: <RefereeLink pubkey={m.referee} />,
|
||||
})),
|
||||
'joinedAtEpoch',
|
||||
'desc'
|
||||
);
|
||||
|
||||
return (
|
||||
<Table
|
||||
columns={[
|
||||
{ name: 'referee', displayName: t('Referee') },
|
||||
{
|
||||
name: 'joinedAt',
|
||||
displayName: t('Joined at'),
|
||||
},
|
||||
{
|
||||
name: 'joinedAtEpoch',
|
||||
displayName: t('Joined epoch'),
|
||||
headerClassName: 'text-right',
|
||||
className: 'text-right',
|
||||
},
|
||||
{
|
||||
name: 'explorerLink',
|
||||
displayName: '',
|
||||
headerClassName: 'hidden md:block',
|
||||
className: 'hidden md:block text-right',
|
||||
},
|
||||
]}
|
||||
data={data}
|
||||
noCollapse={true}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const RefereeCell = ({ pubkey }: { pubkey: string }) => {
|
||||
return <span title={pubkey}>{truncateMiddle(pubkey)}</span>;
|
||||
};
|
||||
|
||||
const RefereeLink = ({ pubkey }: { pubkey: string }) => {
|
||||
const t = useT();
|
||||
|
||||
const linkCreator = useLinks(DApp.Explorer);
|
||||
const link = linkCreator(EXPLORER_PARTIES.replace(':id', pubkey));
|
||||
|
||||
return (
|
||||
<Link to={link} className="underline underline-offset-4">
|
||||
{t('View on explorer')}
|
||||
</Link>
|
||||
);
|
||||
};
|
||||
|
||||
const JoinButton = ({ joined }: { joined: boolean }) => {
|
||||
const t = useT();
|
||||
|
||||
if (joined) {
|
||||
return (
|
||||
<Button intent={Intent.None} disabled={true}>
|
||||
<span className="flex items-center gap-2">
|
||||
{t('Joined')}{' '}
|
||||
<span className="text-vega-green-600 dark:text-vega-green">
|
||||
<VegaIcon name={VegaIconNames.TICK} />
|
||||
</span>
|
||||
</span>
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
|
||||
return <Button intent={Intent.Primary}>{t('Join this team')}</Button>;
|
||||
};
|
||||
|
||||
const LatestResults = ({ games }: { games: TeamGame[] }) => {
|
||||
const t = useT();
|
||||
const latestGames = games.slice(0, 5);
|
||||
|
||||
return (
|
||||
<dl>
|
||||
<dt className="text-muted text-sm">
|
||||
{t('Last {{count}} game results', { count: latestGames.length })}
|
||||
</dt>
|
||||
<dd className="flex gap-1">
|
||||
{latestGames.map((game) => {
|
||||
return (
|
||||
<Pill key={game.id} className="text-sm">
|
||||
{t('place', { count: game.team.rank, ordinal: true })}
|
||||
</Pill>
|
||||
);
|
||||
})}
|
||||
</dd>
|
||||
</dl>
|
||||
);
|
||||
};
|
||||
|
||||
const FavoriteGame = ({ games }: { games: TeamGame[] }) => {
|
||||
const t = useT();
|
||||
|
||||
const rewardMetrics = games.map((game) => game.team.rewardMetric);
|
||||
const count = countBy(rewardMetrics);
|
||||
|
||||
let favoriteMetric = '';
|
||||
let mostOccurances = 0;
|
||||
|
||||
for (const key in count) {
|
||||
if (count[key] > mostOccurances) {
|
||||
favoriteMetric = key;
|
||||
mostOccurances = count[key];
|
||||
}
|
||||
}
|
||||
|
||||
if (!favoriteMetric) return null;
|
||||
|
||||
return (
|
||||
<dl>
|
||||
<dt className="text-muted text-sm">{t('Favorite game')}</dt>
|
||||
<dd>
|
||||
<Pill className="flex-inline items-center gap-2 bg-transparent text-sm">
|
||||
<VegaIcon
|
||||
name={VegaIconNames.STAR}
|
||||
className="text-vega-yellow-400"
|
||||
/>{' '}
|
||||
{favoriteMetric}
|
||||
</Pill>
|
||||
</dd>
|
||||
</dl>
|
||||
);
|
||||
};
|
||||
|
||||
const ToggleButton = ({
|
||||
active,
|
||||
...props
|
||||
}: ButtonHTMLAttributes<HTMLButtonElement> & { active: boolean }) => {
|
||||
return (
|
||||
<button
|
||||
{...props}
|
||||
className={classNames('relative top-px uppercase border-b-2 py-4', {
|
||||
'text-muted border-transparent': !active,
|
||||
'border-vega-yellow': active,
|
||||
})}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const StatSection = ({ children }: { children: ReactNode }) => {
|
||||
return (
|
||||
<section className="flex flex-col lg:flex-row gap-2 lg:gap-8">
|
||||
{children}
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
const StatSectionSeparator = () => {
|
||||
return <div className="hidden md:block border-r border-default" />;
|
||||
};
|
||||
|
||||
const StatList = ({ children }: { children: ReactNode }) => {
|
||||
return (
|
||||
<dl className="grid grid-cols-[min-content_min-content] md:flex gap-4 md:gap-6 lg:gap-8 whitespace-nowrap">
|
||||
{children}
|
||||
</dl>
|
||||
);
|
||||
};
|
||||
|
||||
const Stat = ({
|
||||
value,
|
||||
label,
|
||||
tooltip,
|
||||
}: {
|
||||
value: ReactNode;
|
||||
label: ReactNode;
|
||||
tooltip?: string;
|
||||
}) => {
|
||||
return (
|
||||
<div>
|
||||
<dd className="text-3xl lg:text-4xl">{value}</dd>
|
||||
<dt className="text-sm text-muted">
|
||||
{tooltip ? (
|
||||
<Tooltip description={tooltip} underline={false}>
|
||||
<span className="flex items-center gap-2">
|
||||
{label}
|
||||
<VegaIcon name={VegaIconNames.INFO} size={12} />
|
||||
</span>
|
||||
</Tooltip>
|
||||
) : (
|
||||
label
|
||||
)}
|
||||
</dt>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -1,236 +0,0 @@
|
||||
import { ErrorBoundary } from '@sentry/react';
|
||||
import { CompetitionsHeader } from '../../components/competitions/competitions-header';
|
||||
import { useRef, useState } from 'react';
|
||||
import { useT } from '../../lib/use-t';
|
||||
import { useTeams } from './hooks/use-teams';
|
||||
import { CompetitionsLeaderboard } from '../../components/competitions/competitions-leaderboard';
|
||||
import {
|
||||
Input,
|
||||
Loader,
|
||||
VegaIcon,
|
||||
VegaIconNames,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import { usePageTitle } from '../../lib/hooks/use-page-title';
|
||||
|
||||
export const CompetitionsTeams = () => {
|
||||
const t = useT();
|
||||
|
||||
usePageTitle([t('Competitions'), t('Teams')]);
|
||||
|
||||
const { data: teamsData, loading: teamsLoading } = useTeams({
|
||||
sortByField: ['totalQuantumRewards'],
|
||||
order: 'desc',
|
||||
});
|
||||
|
||||
const inputRef = useRef<HTMLInputElement>(null);
|
||||
|
||||
// const teamsData = [
|
||||
// {
|
||||
// referrer: '12345678909876543212345678765432345676543234567',
|
||||
// avatarUrl: '',
|
||||
// teamUrl: 'https://vega.xyz',
|
||||
// closed: true,
|
||||
// teamId:
|
||||
// '8d81a2ba54c21cbb891e46a2a8debac508b33ee83e2d7c3b8a5bcd420753515e',
|
||||
// name: 'cat lovers 2000',
|
||||
// totalQuantumRewards: '1234567890',
|
||||
// totalGamesPlayed: 12,
|
||||
// totalQuantumVolume: '1234567890',
|
||||
// gamesPlayed: [],
|
||||
// createdAt: 123,
|
||||
// createdAtEpoch: 123,
|
||||
// },
|
||||
// {
|
||||
// referrer: '12345678909876543212345678765432345676543234567',
|
||||
// avatarUrl: '',
|
||||
// teamUrl: 'https://vega.xyz',
|
||||
// closed: true,
|
||||
// teamId:
|
||||
// 'afe0f77322c9bc9ebe36faa8b0d846617018b86db78f33cadbe09dc15c95f408',
|
||||
// name: 'dog lovers 2000',
|
||||
// totalQuantumRewards: '1234567890',
|
||||
// totalGamesPlayed: 12,
|
||||
// totalQuantumVolume: '1234567890',
|
||||
// gamesPlayed: [],
|
||||
// createdAt: 123,
|
||||
// createdAtEpoch: 123,
|
||||
// },
|
||||
// {
|
||||
// referrer: '12345678909876543212345678765432345676543234567',
|
||||
// avatarUrl: '',
|
||||
// teamUrl: 'https://vega.xyz',
|
||||
// closed: true,
|
||||
// teamId:
|
||||
// '580e52a0ef7f706898c6e0a3a34a6f870ef2a49e191408a3e1ed2c0a92e6e588',
|
||||
// name: 'we like vega',
|
||||
// totalQuantumRewards: '1234567890',
|
||||
// totalGamesPlayed: 12,
|
||||
// totalQuantumVolume: '1234567890',
|
||||
// gamesPlayed: [],
|
||||
// createdAt: 123,
|
||||
// createdAtEpoch: 123,
|
||||
// },
|
||||
// {
|
||||
// referrer: '12345678909876543212345678765432345676543234567',
|
||||
// avatarUrl: '',
|
||||
// teamUrl: 'https://vega.xyz',
|
||||
// closed: true,
|
||||
// teamId:
|
||||
// '38c49f91670f536ce5ab6609899780cfb6bb9dcfc6c79842cbc348325e10c10f',
|
||||
// name: 'pure gold',
|
||||
// totalQuantumRewards: '1234567890',
|
||||
// totalGamesPlayed: 12,
|
||||
// totalQuantumVolume: '1234567890',
|
||||
// gamesPlayed: [],
|
||||
// createdAt: 123,
|
||||
// createdAtEpoch: 123,
|
||||
// },
|
||||
// {
|
||||
// referrer: '12345678909876543212345678765432345676543234567',
|
||||
// avatarUrl: '',
|
||||
// teamUrl: 'https://vega.xyz',
|
||||
// closed: true,
|
||||
// teamId:
|
||||
// 'dfaaace4de83cab2443ebd7f75dc0d9a22a4f4a38f757b3adb5269aa930e0bdc',
|
||||
// name: 'diamond hands',
|
||||
// totalQuantumRewards: '1234567890',
|
||||
// totalGamesPlayed: 12,
|
||||
// totalQuantumVolume: '1234567890',
|
||||
// gamesPlayed: [],
|
||||
// createdAt: 123,
|
||||
// createdAtEpoch: 123,
|
||||
// },
|
||||
// {
|
||||
// referrer: '12345678909876543212345678765432345676543234567',
|
||||
// avatarUrl: '',
|
||||
// teamUrl: 'https://vega.xyz',
|
||||
// closed: true,
|
||||
// teamId:
|
||||
// '1627a2ea10b07ed9cf08bfc7818bd76716493f5e6a5b5ffe4ed856ef34602a14',
|
||||
// name: 'to the moon',
|
||||
// totalQuantumRewards: '1234567890',
|
||||
// totalGamesPlayed: 12,
|
||||
// totalQuantumVolume: '1234567890',
|
||||
// gamesPlayed: [],
|
||||
// createdAt: 123,
|
||||
// createdAtEpoch: 123,
|
||||
// },
|
||||
// {
|
||||
// referrer: '12345678909876543212345678765432345676543234567',
|
||||
// avatarUrl: '',
|
||||
// teamUrl: 'https://vega.xyz',
|
||||
// closed: true,
|
||||
// teamId:
|
||||
// 'd21f7b04985e5b5f510a53ee61c4d77c100374eafeffd754b5296d386b7454da',
|
||||
// name: 'beyond cats',
|
||||
// totalQuantumRewards: '1234567890',
|
||||
// totalGamesPlayed: 12,
|
||||
// totalQuantumVolume: '1234567890',
|
||||
// gamesPlayed: [],
|
||||
// createdAt: 123,
|
||||
// createdAtEpoch: 123,
|
||||
// },
|
||||
// {
|
||||
// referrer: '12345678909876543212345678765432345676543234567',
|
||||
// avatarUrl: '',
|
||||
// teamUrl: 'https://vega.xyz',
|
||||
// closed: true,
|
||||
// teamId:
|
||||
// 'a40339f4cf31f4496e4d85ac7f56d5bbc9e257a337070fa361205c1f2fdaa0eb',
|
||||
// name: 'cat lovers 2000',
|
||||
// totalQuantumRewards: '1234567890',
|
||||
// totalGamesPlayed: 12,
|
||||
// totalQuantumVolume: '1234567890',
|
||||
// gamesPlayed: [],
|
||||
// createdAt: 123,
|
||||
// createdAtEpoch: 123,
|
||||
// },
|
||||
// {
|
||||
// referrer: '12345678909876543212345678765432345676543234567',
|
||||
// avatarUrl: '',
|
||||
// teamUrl: 'https://vega.xyz',
|
||||
// closed: true,
|
||||
// teamId:
|
||||
// 'af059e7508a302cce9523edcbde52bf7f2e876f34854c6274a83aec4045b9657',
|
||||
// name: 'cat lovers 2000',
|
||||
// totalQuantumRewards: '1234567890',
|
||||
// totalGamesPlayed: 12,
|
||||
// totalQuantumVolume: '1234567890',
|
||||
// gamesPlayed: [],
|
||||
// createdAt: 123,
|
||||
// createdAtEpoch: 123,
|
||||
// },
|
||||
// {
|
||||
// referrer: '12345678909876543212345678765432345676543234567',
|
||||
// avatarUrl: '',
|
||||
// teamUrl: 'https://vega.xyz',
|
||||
// closed: true,
|
||||
// teamId:
|
||||
// '3f06f0e0ec863e686212d6b100f03122c9997f890d304f15f55215ac32334e6f',
|
||||
// name: 'cat lovers 2000',
|
||||
// totalQuantumRewards: '1234567890',
|
||||
// totalGamesPlayed: 12,
|
||||
// totalQuantumVolume: '1234567890',
|
||||
// gamesPlayed: [],
|
||||
// createdAt: 123,
|
||||
// createdAtEpoch: 123,
|
||||
// },
|
||||
// {
|
||||
// referrer: '12345678909876543212345678765432345676543234567',
|
||||
// avatarUrl: '',
|
||||
// teamUrl: 'https://vega.xyz',
|
||||
// closed: true,
|
||||
// teamId:
|
||||
// '0fdc948a548ccd29001c2411629065bc5e248b6535b59b4b089f07a577074bf9',
|
||||
// name: 'cat lovers 2000',
|
||||
// totalQuantumRewards: '1234567890',
|
||||
// totalGamesPlayed: 12,
|
||||
// totalQuantumVolume: '1234567890',
|
||||
// gamesPlayed: [],
|
||||
// createdAt: 123,
|
||||
// createdAtEpoch: 123,
|
||||
// },
|
||||
// ];
|
||||
|
||||
const [filter, setFilter] = useState<string | null | undefined>(undefined);
|
||||
|
||||
return (
|
||||
<ErrorBoundary>
|
||||
<CompetitionsHeader title={t('Join a team')}>
|
||||
<p className="text-lg mb-1">{t('Choose a team to get involved')}</p>
|
||||
</CompetitionsHeader>
|
||||
|
||||
<div className="mb-6 flex justify-end">
|
||||
<div className="w-40 h-10 relative">
|
||||
<span className="absolute z-10 pointer-events-none opacity-90 top-[5px] left-[5px]">
|
||||
<VegaIcon name={VegaIconNames.SEARCH} size={18} />
|
||||
</span>
|
||||
<Input
|
||||
ref={inputRef}
|
||||
className="opacity-90 text-right"
|
||||
placeholder={t('Name')}
|
||||
onKeyUp={() => {
|
||||
const value = inputRef.current?.value;
|
||||
if (value != filter) setFilter(value);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
{teamsLoading ? (
|
||||
<Loader size="small" />
|
||||
) : (
|
||||
<CompetitionsLeaderboard
|
||||
data={teamsData.filter((td) => {
|
||||
if (filter && filter.length > 0) {
|
||||
const re = new RegExp(filter, 'i');
|
||||
return re.test(td.name);
|
||||
}
|
||||
return true;
|
||||
})}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</ErrorBoundary>
|
||||
);
|
||||
};
|
||||
@@ -1,89 +0,0 @@
|
||||
fragment TeamFields on Team {
|
||||
teamId
|
||||
referrer
|
||||
name
|
||||
teamUrl
|
||||
avatarUrl
|
||||
createdAt
|
||||
createdAtEpoch
|
||||
closed
|
||||
}
|
||||
|
||||
fragment TeamStatsFields on TeamStatistics {
|
||||
teamId
|
||||
totalQuantumVolume
|
||||
totalQuantumRewards
|
||||
totalGamesPlayed
|
||||
quantumRewards {
|
||||
epoch
|
||||
total_quantum_rewards
|
||||
}
|
||||
gamesPlayed
|
||||
}
|
||||
|
||||
fragment TeamRefereeFields on TeamReferee {
|
||||
teamId
|
||||
referee
|
||||
joinedAt
|
||||
joinedAtEpoch
|
||||
}
|
||||
|
||||
fragment TeamEntity on TeamGameEntity {
|
||||
rank
|
||||
volume
|
||||
rewardMetric
|
||||
rewardEarned
|
||||
totalRewardsEarned
|
||||
team {
|
||||
teamId
|
||||
}
|
||||
}
|
||||
|
||||
fragment TeamGameFields on Game {
|
||||
id
|
||||
epoch
|
||||
numberOfParticipants
|
||||
entities {
|
||||
... on TeamGameEntity {
|
||||
...TeamEntity
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
query Team($teamId: ID!, $partyId: ID) {
|
||||
teams(teamId: $teamId) {
|
||||
edges {
|
||||
node {
|
||||
...TeamFields
|
||||
}
|
||||
}
|
||||
}
|
||||
partyTeams: teams(partyId: $partyId) {
|
||||
edges {
|
||||
node {
|
||||
...TeamFields
|
||||
}
|
||||
}
|
||||
}
|
||||
teamsStatistics(teamId: $teamId) {
|
||||
edges {
|
||||
node {
|
||||
...TeamStatsFields
|
||||
}
|
||||
}
|
||||
}
|
||||
teamReferees(teamId: $teamId) {
|
||||
edges {
|
||||
node {
|
||||
...TeamRefereeFields
|
||||
}
|
||||
}
|
||||
}
|
||||
games(entityScope: ENTITY_SCOPE_TEAMS) {
|
||||
edges {
|
||||
node {
|
||||
...TeamGameFields
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
query TeamReferees($teamId: ID!) {
|
||||
teamReferees(teamId: $teamId) {
|
||||
edges {
|
||||
node {
|
||||
teamId
|
||||
referee
|
||||
joinedAt
|
||||
joinedAtEpoch
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
query Teams($teamId: ID, $partyId: ID) {
|
||||
teams(teamId: $teamId, partyId: $partyId) {
|
||||
edges {
|
||||
node {
|
||||
teamId
|
||||
referrer
|
||||
name
|
||||
teamUrl
|
||||
avatarUrl
|
||||
createdAt
|
||||
createdAtEpoch
|
||||
closed
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
query TeamsStatistics($teamId: ID, $aggregationEpochs: Int) {
|
||||
teamsStatistics(teamId: $teamId, aggregationEpochs: $aggregationEpochs) {
|
||||
edges {
|
||||
node {
|
||||
teamId
|
||||
totalQuantumVolume
|
||||
totalQuantumRewards
|
||||
totalGamesPlayed
|
||||
gamesPlayed
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,151 +0,0 @@
|
||||
import * as Types from '@vegaprotocol/types';
|
||||
|
||||
import { gql } from '@apollo/client';
|
||||
import * as Apollo from '@apollo/client';
|
||||
const defaultOptions = {} as const;
|
||||
export type TeamFieldsFragment = { __typename?: 'Team', teamId: string, referrer: string, name: string, teamUrl: string, avatarUrl: string, createdAt: any, createdAtEpoch: number, closed: boolean };
|
||||
|
||||
export type TeamStatsFieldsFragment = { __typename?: 'TeamStatistics', teamId: string, totalQuantumVolume: string, totalQuantumRewards: string, totalGamesPlayed: number, gamesPlayed: Array<string>, quantumRewards: Array<{ __typename?: 'QuantumRewardsPerEpoch', epoch: number, total_quantum_rewards: string }> };
|
||||
|
||||
export type TeamRefereeFieldsFragment = { __typename?: 'TeamReferee', teamId: string, referee: string, joinedAt: any, joinedAtEpoch: number };
|
||||
|
||||
export type TeamEntityFragment = { __typename?: 'TeamGameEntity', rank: number, volume: string, rewardMetric: string, rewardEarned: string, totalRewardsEarned: string, team: { __typename?: 'TeamParticipation', teamId: string } };
|
||||
|
||||
export type TeamGameFieldsFragment = { __typename?: 'Game', id: string, epoch: number, numberOfParticipants: number, entities: Array<{ __typename?: 'IndividualGameEntity' } | { __typename?: 'TeamGameEntity', rank: number, volume: string, rewardMetric: string, rewardEarned: string, totalRewardsEarned: string, team: { __typename?: 'TeamParticipation', teamId: string } }> };
|
||||
|
||||
export type TeamQueryVariables = Types.Exact<{
|
||||
teamId: Types.Scalars['ID'];
|
||||
partyId?: Types.InputMaybe<Types.Scalars['ID']>;
|
||||
}>;
|
||||
|
||||
|
||||
export type TeamQuery = { __typename?: 'Query', teams?: { __typename?: 'TeamConnection', edges: Array<{ __typename?: 'TeamEdge', node: { __typename?: 'Team', teamId: string, referrer: string, name: string, teamUrl: string, avatarUrl: string, createdAt: any, createdAtEpoch: number, closed: boolean } }> } | null, partyTeams?: { __typename?: 'TeamConnection', edges: Array<{ __typename?: 'TeamEdge', node: { __typename?: 'Team', teamId: string, referrer: string, name: string, teamUrl: string, avatarUrl: string, createdAt: any, createdAtEpoch: number, closed: boolean } }> } | null, teamsStatistics?: { __typename?: 'TeamsStatisticsConnection', edges: Array<{ __typename?: 'TeamStatisticsEdge', node: { __typename?: 'TeamStatistics', teamId: string, totalQuantumVolume: string, totalQuantumRewards: string, totalGamesPlayed: number, gamesPlayed: Array<string>, quantumRewards: Array<{ __typename?: 'QuantumRewardsPerEpoch', epoch: number, total_quantum_rewards: string }> } }> } | null, teamReferees?: { __typename?: 'TeamRefereeConnection', edges: Array<{ __typename?: 'TeamRefereeEdge', node: { __typename?: 'TeamReferee', teamId: string, referee: string, joinedAt: any, joinedAtEpoch: number } }> } | null, games: { __typename?: 'GamesConnection', edges?: Array<{ __typename?: 'GameEdge', node: { __typename?: 'Game', id: string, epoch: number, numberOfParticipants: number, entities: Array<{ __typename?: 'IndividualGameEntity' } | { __typename?: 'TeamGameEntity', rank: number, volume: string, rewardMetric: string, rewardEarned: string, totalRewardsEarned: string, team: { __typename?: 'TeamParticipation', teamId: string } }> } } | null> | null } };
|
||||
|
||||
export const TeamFieldsFragmentDoc = gql`
|
||||
fragment TeamFields on Team {
|
||||
teamId
|
||||
referrer
|
||||
name
|
||||
teamUrl
|
||||
avatarUrl
|
||||
createdAt
|
||||
createdAtEpoch
|
||||
closed
|
||||
}
|
||||
`;
|
||||
export const TeamStatsFieldsFragmentDoc = gql`
|
||||
fragment TeamStatsFields on TeamStatistics {
|
||||
teamId
|
||||
totalQuantumVolume
|
||||
totalQuantumRewards
|
||||
totalGamesPlayed
|
||||
quantumRewards {
|
||||
epoch
|
||||
total_quantum_rewards
|
||||
}
|
||||
gamesPlayed
|
||||
}
|
||||
`;
|
||||
export const TeamRefereeFieldsFragmentDoc = gql`
|
||||
fragment TeamRefereeFields on TeamReferee {
|
||||
teamId
|
||||
referee
|
||||
joinedAt
|
||||
joinedAtEpoch
|
||||
}
|
||||
`;
|
||||
export const TeamEntityFragmentDoc = gql`
|
||||
fragment TeamEntity on TeamGameEntity {
|
||||
rank
|
||||
volume
|
||||
rewardMetric
|
||||
rewardEarned
|
||||
totalRewardsEarned
|
||||
team {
|
||||
teamId
|
||||
}
|
||||
}
|
||||
`;
|
||||
export const TeamGameFieldsFragmentDoc = gql`
|
||||
fragment TeamGameFields on Game {
|
||||
id
|
||||
epoch
|
||||
numberOfParticipants
|
||||
entities {
|
||||
... on TeamGameEntity {
|
||||
...TeamEntity
|
||||
}
|
||||
}
|
||||
}
|
||||
${TeamEntityFragmentDoc}`;
|
||||
export const TeamDocument = gql`
|
||||
query Team($teamId: ID!, $partyId: ID) {
|
||||
teams(teamId: $teamId) {
|
||||
edges {
|
||||
node {
|
||||
...TeamFields
|
||||
}
|
||||
}
|
||||
}
|
||||
partyTeams: teams(partyId: $partyId) {
|
||||
edges {
|
||||
node {
|
||||
...TeamFields
|
||||
}
|
||||
}
|
||||
}
|
||||
teamsStatistics(teamId: $teamId) {
|
||||
edges {
|
||||
node {
|
||||
...TeamStatsFields
|
||||
}
|
||||
}
|
||||
}
|
||||
teamReferees(teamId: $teamId) {
|
||||
edges {
|
||||
node {
|
||||
...TeamRefereeFields
|
||||
}
|
||||
}
|
||||
}
|
||||
games(entityScope: ENTITY_SCOPE_TEAMS) {
|
||||
edges {
|
||||
node {
|
||||
...TeamGameFields
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
${TeamFieldsFragmentDoc}
|
||||
${TeamStatsFieldsFragmentDoc}
|
||||
${TeamRefereeFieldsFragmentDoc}
|
||||
${TeamGameFieldsFragmentDoc}`;
|
||||
|
||||
/**
|
||||
* __useTeamQuery__
|
||||
*
|
||||
* To run a query within a React component, call `useTeamQuery` and pass it any options that fit your needs.
|
||||
* When your component renders, `useTeamQuery` returns an object from Apollo Client that contains loading, error, and data properties
|
||||
* you can use to render your UI.
|
||||
*
|
||||
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
|
||||
*
|
||||
* @example
|
||||
* const { data, loading, error } = useTeamQuery({
|
||||
* variables: {
|
||||
* teamId: // value for 'teamId'
|
||||
* partyId: // value for 'partyId'
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useTeamQuery(baseOptions: Apollo.QueryHookOptions<TeamQuery, TeamQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useQuery<TeamQuery, TeamQueryVariables>(TeamDocument, options);
|
||||
}
|
||||
export function useTeamLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<TeamQuery, TeamQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useLazyQuery<TeamQuery, TeamQueryVariables>(TeamDocument, options);
|
||||
}
|
||||
export type TeamQueryHookResult = ReturnType<typeof useTeamQuery>;
|
||||
export type TeamLazyQueryHookResult = ReturnType<typeof useTeamLazyQuery>;
|
||||
export type TeamQueryResult = Apollo.QueryResult<TeamQuery, TeamQueryVariables>;
|
||||
@@ -1,55 +0,0 @@
|
||||
import * as Types from '@vegaprotocol/types';
|
||||
|
||||
import { gql } from '@apollo/client';
|
||||
import * as Apollo from '@apollo/client';
|
||||
const defaultOptions = {} as const;
|
||||
export type TeamRefereesQueryVariables = Types.Exact<{
|
||||
teamId: Types.Scalars['ID'];
|
||||
}>;
|
||||
|
||||
|
||||
export type TeamRefereesQuery = { __typename?: 'Query', teamReferees?: { __typename?: 'TeamRefereeConnection', edges: Array<{ __typename?: 'TeamRefereeEdge', node: { __typename?: 'TeamReferee', teamId: string, referee: string, joinedAt: any, joinedAtEpoch: number } }> } | null };
|
||||
|
||||
|
||||
export const TeamRefereesDocument = gql`
|
||||
query TeamReferees($teamId: ID!) {
|
||||
teamReferees(teamId: $teamId) {
|
||||
edges {
|
||||
node {
|
||||
teamId
|
||||
referee
|
||||
joinedAt
|
||||
joinedAtEpoch
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
/**
|
||||
* __useTeamRefereesQuery__
|
||||
*
|
||||
* To run a query within a React component, call `useTeamRefereesQuery` and pass it any options that fit your needs.
|
||||
* When your component renders, `useTeamRefereesQuery` returns an object from Apollo Client that contains loading, error, and data properties
|
||||
* you can use to render your UI.
|
||||
*
|
||||
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
|
||||
*
|
||||
* @example
|
||||
* const { data, loading, error } = useTeamRefereesQuery({
|
||||
* variables: {
|
||||
* teamId: // value for 'teamId'
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useTeamRefereesQuery(baseOptions: Apollo.QueryHookOptions<TeamRefereesQuery, TeamRefereesQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useQuery<TeamRefereesQuery, TeamRefereesQueryVariables>(TeamRefereesDocument, options);
|
||||
}
|
||||
export function useTeamRefereesLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<TeamRefereesQuery, TeamRefereesQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useLazyQuery<TeamRefereesQuery, TeamRefereesQueryVariables>(TeamRefereesDocument, options);
|
||||
}
|
||||
export type TeamRefereesQueryHookResult = ReturnType<typeof useTeamRefereesQuery>;
|
||||
export type TeamRefereesLazyQueryHookResult = ReturnType<typeof useTeamRefereesLazyQuery>;
|
||||
export type TeamRefereesQueryResult = Apollo.QueryResult<TeamRefereesQuery, TeamRefereesQueryVariables>;
|
||||
@@ -1,61 +0,0 @@
|
||||
import * as Types from '@vegaprotocol/types';
|
||||
|
||||
import { gql } from '@apollo/client';
|
||||
import * as Apollo from '@apollo/client';
|
||||
const defaultOptions = {} as const;
|
||||
export type TeamsQueryVariables = Types.Exact<{
|
||||
teamId?: Types.InputMaybe<Types.Scalars['ID']>;
|
||||
partyId?: Types.InputMaybe<Types.Scalars['ID']>;
|
||||
}>;
|
||||
|
||||
|
||||
export type TeamsQuery = { __typename?: 'Query', teams?: { __typename?: 'TeamConnection', edges: Array<{ __typename?: 'TeamEdge', node: { __typename?: 'Team', teamId: string, referrer: string, name: string, teamUrl: string, avatarUrl: string, createdAt: any, createdAtEpoch: number, closed: boolean } }> } | null };
|
||||
|
||||
|
||||
export const TeamsDocument = gql`
|
||||
query Teams($teamId: ID, $partyId: ID) {
|
||||
teams(teamId: $teamId, partyId: $partyId) {
|
||||
edges {
|
||||
node {
|
||||
teamId
|
||||
referrer
|
||||
name
|
||||
teamUrl
|
||||
avatarUrl
|
||||
createdAt
|
||||
createdAtEpoch
|
||||
closed
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
/**
|
||||
* __useTeamsQuery__
|
||||
*
|
||||
* To run a query within a React component, call `useTeamsQuery` and pass it any options that fit your needs.
|
||||
* When your component renders, `useTeamsQuery` returns an object from Apollo Client that contains loading, error, and data properties
|
||||
* you can use to render your UI.
|
||||
*
|
||||
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
|
||||
*
|
||||
* @example
|
||||
* const { data, loading, error } = useTeamsQuery({
|
||||
* variables: {
|
||||
* teamId: // value for 'teamId'
|
||||
* partyId: // value for 'partyId'
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useTeamsQuery(baseOptions?: Apollo.QueryHookOptions<TeamsQuery, TeamsQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useQuery<TeamsQuery, TeamsQueryVariables>(TeamsDocument, options);
|
||||
}
|
||||
export function useTeamsLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<TeamsQuery, TeamsQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useLazyQuery<TeamsQuery, TeamsQueryVariables>(TeamsDocument, options);
|
||||
}
|
||||
export type TeamsQueryHookResult = ReturnType<typeof useTeamsQuery>;
|
||||
export type TeamsLazyQueryHookResult = ReturnType<typeof useTeamsLazyQuery>;
|
||||
export type TeamsQueryResult = Apollo.QueryResult<TeamsQuery, TeamsQueryVariables>;
|
||||
-58
@@ -1,58 +0,0 @@
|
||||
import * as Types from '@vegaprotocol/types';
|
||||
|
||||
import { gql } from '@apollo/client';
|
||||
import * as Apollo from '@apollo/client';
|
||||
const defaultOptions = {} as const;
|
||||
export type TeamsStatisticsQueryVariables = Types.Exact<{
|
||||
teamId?: Types.InputMaybe<Types.Scalars['ID']>;
|
||||
aggregationEpochs?: Types.InputMaybe<Types.Scalars['Int']>;
|
||||
}>;
|
||||
|
||||
|
||||
export type TeamsStatisticsQuery = { __typename?: 'Query', teamsStatistics?: { __typename?: 'TeamsStatisticsConnection', edges: Array<{ __typename?: 'TeamStatisticsEdge', node: { __typename?: 'TeamStatistics', teamId: string, totalQuantumVolume: string, totalQuantumRewards: string, totalGamesPlayed: number, gamesPlayed: Array<string> } }> } | null };
|
||||
|
||||
|
||||
export const TeamsStatisticsDocument = gql`
|
||||
query TeamsStatistics($teamId: ID, $aggregationEpochs: Int) {
|
||||
teamsStatistics(teamId: $teamId, aggregationEpochs: $aggregationEpochs) {
|
||||
edges {
|
||||
node {
|
||||
teamId
|
||||
totalQuantumVolume
|
||||
totalQuantumRewards
|
||||
totalGamesPlayed
|
||||
gamesPlayed
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
/**
|
||||
* __useTeamsStatisticsQuery__
|
||||
*
|
||||
* To run a query within a React component, call `useTeamsStatisticsQuery` and pass it any options that fit your needs.
|
||||
* When your component renders, `useTeamsStatisticsQuery` returns an object from Apollo Client that contains loading, error, and data properties
|
||||
* you can use to render your UI.
|
||||
*
|
||||
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
|
||||
*
|
||||
* @example
|
||||
* const { data, loading, error } = useTeamsStatisticsQuery({
|
||||
* variables: {
|
||||
* teamId: // value for 'teamId'
|
||||
* aggregationEpochs: // value for 'aggregationEpochs'
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useTeamsStatisticsQuery(baseOptions?: Apollo.QueryHookOptions<TeamsStatisticsQuery, TeamsStatisticsQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useQuery<TeamsStatisticsQuery, TeamsStatisticsQueryVariables>(TeamsStatisticsDocument, options);
|
||||
}
|
||||
export function useTeamsStatisticsLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<TeamsStatisticsQuery, TeamsStatisticsQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useLazyQuery<TeamsStatisticsQuery, TeamsStatisticsQueryVariables>(TeamsStatisticsDocument, options);
|
||||
}
|
||||
export type TeamsStatisticsQueryHookResult = ReturnType<typeof useTeamsStatisticsQuery>;
|
||||
export type TeamsStatisticsLazyQueryHookResult = ReturnType<typeof useTeamsStatisticsLazyQuery>;
|
||||
export type TeamsStatisticsQueryResult = Apollo.QueryResult<TeamsStatisticsQuery, TeamsStatisticsQueryVariables>;
|
||||
@@ -1,37 +0,0 @@
|
||||
import compact from 'lodash/compact';
|
||||
import { useActiveRewardsQuery } from '../../../components/rewards-container/__generated__/Rewards';
|
||||
import { isActiveReward } from '../../../components/rewards-container/active-rewards';
|
||||
import { EntityScope, type TransferNode } from '@vegaprotocol/types';
|
||||
|
||||
const isScopedToTeams = (node: TransferNode) =>
|
||||
node.transfer.kind.__typename === 'RecurringTransfer' &&
|
||||
node.transfer.kind.dispatchStrategy?.entityScope ===
|
||||
EntityScope.ENTITY_SCOPE_TEAMS;
|
||||
|
||||
export const useGames = ({
|
||||
currentEpoch,
|
||||
onlyActive,
|
||||
}: {
|
||||
currentEpoch: number;
|
||||
onlyActive: boolean;
|
||||
}) => {
|
||||
const { data, loading, error } = useActiveRewardsQuery({
|
||||
variables: {
|
||||
isReward: true,
|
||||
},
|
||||
});
|
||||
|
||||
const games = compact(data?.transfersConnection?.edges?.map((n) => n?.node))
|
||||
.map((n) => n as TransferNode)
|
||||
.filter((node) => {
|
||||
const recurring = node.transfer.kind.__typename !== 'RecurringTransfer';
|
||||
const active = onlyActive ? isActiveReward(node, currentEpoch) : true;
|
||||
return active && recurring && isScopedToTeams(node);
|
||||
});
|
||||
|
||||
return {
|
||||
data: games,
|
||||
loading,
|
||||
error,
|
||||
};
|
||||
};
|
||||
@@ -1,64 +0,0 @@
|
||||
import compact from 'lodash/compact';
|
||||
import orderBy from 'lodash/orderBy';
|
||||
import {
|
||||
useTeamQuery,
|
||||
type TeamFieldsFragment,
|
||||
type TeamStatsFieldsFragment,
|
||||
type TeamRefereeFieldsFragment,
|
||||
type TeamEntityFragment,
|
||||
} from './__generated__/Team';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||
|
||||
export type Team = TeamFieldsFragment;
|
||||
export type TeamStats = TeamStatsFieldsFragment;
|
||||
export type Member = TeamRefereeFieldsFragment;
|
||||
export type TeamEntity = TeamEntityFragment;
|
||||
export type TeamGame = ReturnType<typeof useTeam>['games'][number];
|
||||
|
||||
export const useTeam = (teamId?: string) => {
|
||||
const { pubKey } = useVegaWallet();
|
||||
|
||||
const { data, loading, error } = useTeamQuery({
|
||||
variables: { teamId: teamId || '', partyId: pubKey },
|
||||
skip: !teamId,
|
||||
});
|
||||
|
||||
const teamEdge = data?.teams?.edges.find((e) => e.node.teamId === teamId);
|
||||
const partyTeamEdge = data?.partyTeams?.edges[0];
|
||||
const teamStatsEdge = data?.teamsStatistics?.edges.find(
|
||||
(e) => e.node.teamId === teamId
|
||||
);
|
||||
const members = data?.teamReferees?.edges
|
||||
.filter((e) => e.node.teamId === teamId)
|
||||
.map((e) => e.node);
|
||||
|
||||
// Find games where the current team participated in
|
||||
const gamesWithTeam = compact(data?.games.edges).map((edge) => {
|
||||
const team = edge.node.entities.find((e) => {
|
||||
if (e.__typename !== 'TeamGameEntity') return false;
|
||||
if (e.team.teamId !== teamId) return false;
|
||||
return true;
|
||||
});
|
||||
|
||||
if (!team) return null;
|
||||
|
||||
return {
|
||||
id: edge.node.id,
|
||||
epoch: edge.node.epoch,
|
||||
numberOfParticipants: edge.node.numberOfParticipants,
|
||||
team: team as TeamEntity, // TS can't infer that all the game entities are teams
|
||||
};
|
||||
});
|
||||
const games = orderBy(compact(gamesWithTeam), 'epoch', 'desc');
|
||||
|
||||
return {
|
||||
data,
|
||||
loading,
|
||||
error,
|
||||
stats: teamStatsEdge?.node,
|
||||
team: teamEdge?.node,
|
||||
members,
|
||||
games,
|
||||
partyInTeam: Boolean(partyTeamEdge),
|
||||
};
|
||||
};
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user