Compare commits

...
Author SHA1 Message Date
Dexter cde6577356 chore(explorer,trading,governance): change explorer api nodes 2023-05-09 13:07:15 +01:00
Dexter b41de05cfc chore(explorer,trading,governance): change data node URLs to new ones 2023-05-09 12:59:58 +01:00
m.ray 00679c75f2 fix(toasts): proposal toasts timeout (#3645) 2023-05-09 10:03:17 +00:00
Art 7fe722dad0 fix(explorer): use data provider import (#3648) 2023-05-09 09:46:24 +00:00
Maciek 0d8a74dfd0 fix(trading): wrong hover effect on datagrid (#3647) 2023-05-09 09:26:59 +00:00
Edd 928b899c46 fix(explorer): improve party page loading (#3274) 2023-05-09 09:43:12 +01:00
Bartłomiej Głownia 72d93bb568 feat(trading): update fills queries, fix empty fills list issue (#3636) 2023-05-09 08:40:06 +00:00
Matthew Russell db75261cd5 chore(react-helpers): remove ag-grid deps (#3628) 2023-05-09 09:04:33 +01:00
Joe Tsang 66fbe28cf0 test(governance): e2e test to include withdrawal toasts (#3624) 2023-05-09 09:03:45 +01:00
Edd 9f3b8ecade fix(explorer): set explorer url env var in explorer (#3622) 2023-05-09 09:03:35 +01:00
Matthew Russell cd13c61e90 fix(datagrid): remove default balham borders (#3642) 2023-05-09 09:02:39 +01:00
Maciek 246577f401 feat(trading): persist table column sizes (#3595) 2023-05-09 08:09:53 +02:00
dexturr 503ca26f68 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-05-09 06:07:05 +00:00
dexturr 13e374a6bb chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-05-09 00:15:01 +00:00
Art bed411040c chore(trading): make limit default order type (#3619) 2023-05-08 15:48:43 -07:00
Joe Tsang d0173f15b2 chore(governance): assert proposal dates (#3610) 2023-05-08 15:48:14 -07:00
Matthew Russell 106b040977 chore(utils): remove ethers deps from ui-toolkit (#3627) 2023-05-08 14:40:31 -07:00
dexturr bf84b04a30 chore: update tranches
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2023-05-08 18:07:04 +00:00
120 changed files with 1149 additions and 782 deletions
+1 -1
View File
@@ -2,7 +2,7 @@
NX_CHAIN_EXPLORER_URL=https://explorer.vega.trading/.netlify/functions/chain-explorer-api
NX_TENDERMINT_URL=https://mainnet-observer-proxy01.ops.vega.xyz/
NX_TENDERMINT_WEBSOCKET_URL=wss://mainnet-observer-proxy01.ops.vega.xyz/websocket
NX_VEGA_URL=https://api.vega.xyz/query
NX_VEGA_URL=https://api.vega.community/graphql
NX_VEGA_ENV=MAINNET
NX_BLOCK_EXPLORER=https://be.explorer.vega.xyz/rest
+1
View File
@@ -4,6 +4,7 @@ NX_TENDERMINT_WEBSOCKET_URL=wss://localhost:26617/websocket
NX_VEGA_ENV=CUSTOM
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/test/announcements.json
NX_VEGA_EXPLORER_URL=/
# App flags
NX_EXPLORER_TXS_LIST=0
+2 -1
View File
@@ -8,4 +8,5 @@ NX_ETHERSCAN_URL=https://sepolia.etherscan.io
NX_VEGA_GOVERNANCE_URL=https://dev.token.vega.xyz
NX_VEGA_URL=https://api.devnet1.vega.xyz/graphql
NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks-internal/main/devnet1/vegawallet-devnet1.toml
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
NX_VEGA_EXPLORER_URL=/
+5 -4
View File
@@ -1,9 +1,10 @@
# App configuration variables
NX_TENDERMINT_URL=https://be.explorer.vega.xyz
NX_TENDERMINT_WEBSOCKET_URL=wss://be.explorer.vega.xyz/websocket
NX_TENDERMINT_URL=https://be.vega.community
NX_TENDERMINT_WEBSOCKET_URL=wss://be.vega.community/websocket
NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks/master/mainnet1/mainnet1.toml
NX_VEGA_ENV=MAINNET
NX_BLOCK_EXPLORER=https://be.explorer.vega.xyz/rest/
NX_BLOCK_EXPLORER=https://be.vega.community/rest/
NX_ETHERSCAN_URL=https://etherscan.io
NX_VEGA_GOVERNANCE_URL=https://token.vega.xyz
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/mainnet/announcements.json
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/mainnet/announcements.json
NX_VEGA_EXPLORER_URL=https://explorer.vega.xyz/
+2 -1
View File
@@ -8,4 +8,5 @@ NX_VEGA_URL=https://api.n07.testnet.vega.xyz/graphql
NX_HOSTED_WALLET_URL=https://wallet.testnet.vega.xyz
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
NX_VEGA_GOVERNANCE_URL=https://token.fairground.wtf
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/fairground/announcements.json
NX_VEGA_EXPLORER_URL=https://explorer.fairground.wtf
+1
View File
@@ -11,3 +11,4 @@ NX_VEGA_GOVERNANCE_URL=https://validator-testnet.governance.vega.xyz
NX_TENDERMINT_URL=https://tm-be.validators-testnet.vega.rocks
NX_BLOCK_EXPLORER=https://be.validators-testnet.vega.rocks/rest
NX_TENDERMINT_WEBSOCKET_URL=wss://be.validators-testnet.vega.xyz/websocket
NX_VEGA_EXPLORER_URL=https://validator-testnet.explorer.vega.xyz/
+1
View File
@@ -5,3 +5,4 @@ NX_VEGA_ENV=CUSTOM
NX_BLOCK_EXPLORER=
NX_ETHERSCAN_URL=https://sepolia.etherscan.io
NX_ANNOUNCEMENTS_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/announcements/test/announcements.json
NX_VEGA_EXPLORER_URL=/
@@ -6,6 +6,7 @@ export type AssetBalanceProps = {
assetId: string;
price: string;
showAssetLink?: boolean;
showAssetSymbol?: boolean;
};
/**
@@ -16,18 +17,21 @@ const AssetBalance = ({
assetId,
price,
showAssetLink = true,
showAssetSymbol = false,
}: AssetBalanceProps) => {
const { data: asset } = useAssetDataProvider(assetId);
const { data: asset, loading } = useAssetDataProvider(assetId);
const label =
asset && asset.decimals
!loading && asset && asset.decimals
? addDecimalsFormatNumber(price, asset.decimals)
: price;
return (
<div className="inline-block">
<span>{label}</span>{' '}
{showAssetLink && asset?.id ? <AssetLink assetId={assetId} /> : null}
{showAssetLink && asset?.id ? (
<AssetLink showAssetSymbol={showAssetSymbol} assetId={assetId} />
) : null}
</div>
);
};
@@ -13,11 +13,17 @@ const DEFAULT_DECIMALS = 18;
* the governance asset first, which is set by a network parameter
*/
const GovernanceAssetBalance = ({ price }: GovernanceAssetBalanceProps) => {
const { data } = useExplorerGovernanceAssetQuery();
const { data, loading } = useExplorerGovernanceAssetQuery();
if (data && data.networkParameter?.value) {
if (!loading && data && data.networkParameter?.value) {
const governanceAssetId = data.networkParameter.value;
return <AssetBalance price={price} assetId={governanceAssetId} />;
return (
<AssetBalance
price={price}
showAssetSymbol={true}
assetId={governanceAssetId}
/>
);
} else {
return (
<div className="inline-block">
@@ -20,7 +20,7 @@ export const PageHeader = ({
copy = false,
className,
}: PageHeaderProps) => {
const titleClasses = 'text-4xl xl:text-5xl uppercase font-alpha calt';
const titleClasses = 'text-xl uppercase font-alpha calt';
return (
<header className={className}>
<span className={`${titleClasses} block`}>{prefix}</span>
@@ -30,7 +30,7 @@ export const BundleExists = ({
// Note if this is wrong, the wrong decoder will be used which will give incorrect data
return (
<div className="w-auto max-w-lg border-2 border-solid border-vega-light-100 dark:border-vega-dark-200 p-5 mt-5">
<div className="w-auto h-10 max-w-lg border-2 border-solid border-vega-light-100 dark:border-vega-dark-200 p-5 mt-5">
<IconForBundleStatus status={status} />
<h1 className="text-xl pb-1">
{status === 'STATUS_ENABLED'
@@ -1,4 +1,3 @@
export { TxList } from './tx-list';
export { TxOrderType } from './tx-order-type';
export { TxsInfiniteList } from './txs-infinite-list';
export { TxsInfiniteListItem } from './txs-infinite-list-item';
@@ -1,14 +0,0 @@
import { t } from '@vegaprotocol/i18n';
import type { TendermintUnconfirmedTransactionsResponse } from '../../routes/txs/tendermint-unconfirmed-transactions-response.d';
interface TxsProps {
data: TendermintUnconfirmedTransactionsResponse | undefined;
}
export const TxList = ({ data }: TxsProps) => {
if (!data) {
return <div>{t('Awaiting transactions')}</div>;
}
return <div>{JSON.stringify(data, null, ' ')}</div>;
};
@@ -7,7 +7,7 @@ import { toHex } from '../search/detect-search';
import { ChainResponseCode } from './details/chain-response-code/chain-reponse.code';
import isNumber from 'lodash/isNumber';
const TRUNCATE_LENGTH = 5;
const TRUNCATE_LENGTH = 10;
export const TxsInfiniteListItem = ({
hash,
@@ -34,10 +34,10 @@ export const TxsInfiniteListItem = ({
className="flex items-center h-full border-t border-neutral-600 dark:border-neutral-800 txs-infinite-list-item grid grid-cols-10 py-2"
>
<div
className="text-sm col-span-10 xl:col-span-3 leading-none"
className="text-sm col-span-10 md:col-span-3 leading-none"
data-testid="tx-hash"
>
<span className="xl:hidden uppercase text-vega-dark-300">
<span className="md:hidden uppercase text-vega-dark-300">
ID:&nbsp;
</span>
<TruncatedLink
@@ -48,10 +48,10 @@ export const TxsInfiniteListItem = ({
/>
</div>
<div
className="text-sm col-span-10 xl:col-span-3 leading-none"
className="text-sm col-span-10 md:col-span-3 leading-none"
data-testid="pub-key"
>
<span className="xl:hidden uppercase text-vega-dark-300">
<span className="md:hidden uppercase text-vega-dark-300">
By:&nbsp;
</span>
<TruncatedLink
@@ -61,14 +61,14 @@ export const TxsInfiniteListItem = ({
endChars={TRUNCATE_LENGTH}
/>
</div>
<div className="text-sm col-span-5 xl:col-span-2 leading-none flex items-center">
<div className="text-sm col-span-5 md:col-span-2 leading-none flex items-center">
<TxOrderType orderType={type} command={command} />
</div>
<div
className="text-sm col-span-3 xl:col-span-1 leading-none flex items-center"
className="text-sm col-span-3 md:col-span-1 leading-none flex items-center"
data-testid="tx-block"
>
<span className="xl:hidden uppercase text-vega-dark-300">
<span className="md:hidden uppercase text-vega-dark-300">
Block:&nbsp;
</span>
<TruncatedLink
@@ -79,10 +79,10 @@ export const TxsInfiniteListItem = ({
/>
</div>
<div
className="text-sm col-span-2 xl:col-span-1 leading-none flex items-center"
className="text-sm col-span-2 md:col-span-1 leading-none flex items-center"
data-testid="tx-success"
>
<span className="xl:hidden uppercase text-vega-dark-300">
<span className="md:hidden uppercase text-vega-dark-300">
Success:&nbsp;
</span>
{isNumber(code) ? (
@@ -64,9 +64,7 @@ describe('Txs infinite list', () => {
error={Error('test error!')}
/>
);
expect(
screen.getByText('Cannot fetch transaction: Error: test error!')
).toBeInTheDocument();
expect(screen.getByText('Cannot fetch transaction')).toBeInTheDocument();
});
it('item renders data of n length into list of n length', () => {
@@ -30,7 +30,7 @@ const NOOP = () => {};
const Item = ({ index, style, isLoading, error }: ItemProps) => {
let content;
if (error) {
content = t(`Cannot fetch transaction: ${error}`);
content = t(`Cannot fetch transaction`);
} else if (isLoading) {
content = <Loader />;
} else {
@@ -68,7 +68,7 @@ export const TxsInfiniteList = ({
className,
}: TxsInfiniteListProps) => {
const { screenSize } = useScreenDimensions();
const isStacked = ['xs', 'sm', 'md', 'lg'].includes(screenSize);
const isStacked = ['xs', 'sm'].includes(screenSize);
if (!txs) {
if (!areTxsLoading) {
@@ -95,15 +95,15 @@ export const TxsInfiniteList = ({
return (
<div className={className} data-testid="transactions-list">
<div className="xl:grid grid-cols-10 w-full mb-3 hidden text-vega-dark-300 uppercase">
<div className="lg:grid grid-cols-10 w-full mb-3 hidden text-vega-dark-300 uppercase">
<div className="col-span-3">
<span className="hidden xl:inline">Transaction &nbsp;</span>
<span className="hidden xl:inline">{t('Transaction')} &nbsp;</span>
<span>ID</span>
</div>
<div className="col-span-3">Submitted By</div>
<div className="col-span-2">Type</div>
<div className="col-span-1">Block</div>
<div className="col-span-1">Success</div>
<div className="col-span-3">{t('Submitted By')}</div>
<div className="col-span-2">{t('Type')}</div>
<div className="col-span-1">{t('Block')}</div>
<div className="col-span-1">{t('Success')}</div>
</div>
<div data-testid="infinite-scroll-wrapper">
<InfiniteLoader
@@ -1,5 +1,4 @@
import { Routes } from '../../routes/route-names';
import { RenderFetched } from '../render-fetched';
import { TruncatedLink } from '../truncate/truncated-link';
import { TxOrderType } from './tx-order-type';
import { Table, TableRow, TableCell } from '../table';
@@ -9,7 +8,7 @@ import type { BlockExplorerTransactions } from '../../routes/types/block-explore
import isNumber from 'lodash/isNumber';
import { ChainResponseCode } from './details/chain-response-code/chain-reponse.code';
import { getTxsDataUrl } from '../../hooks/use-txs-data';
import { Loader } from '@vegaprotocol/ui-toolkit';
import { AsyncRenderer, Loader } from '@vegaprotocol/ui-toolkit';
import EmptyList from '../empty-list/empty-list';
interface TxsPerBlockProps {
@@ -27,7 +26,7 @@ export const TxsPerBlock = ({ blockHeight, txCount }: TxsPerBlockProps) => {
} = useFetch<BlockExplorerTransactions>(url);
return (
<RenderFetched error={error} loading={loading} className="text-body-large">
<AsyncRenderer data={data} error={error} loading={!!loading}>
{data && data.transactions.length > 0 ? (
<div className="overflow-x-auto whitespace-nowrap mb-28">
<Table>
@@ -95,6 +94,6 @@ export const TxsPerBlock = ({ blockHeight, txCount }: TxsPerBlockProps) => {
label={t('0 transactions')}
/>
)}
</RenderFetched>
</AsyncRenderer>
);
};
@@ -144,12 +144,12 @@ afterEach(() => {
describe('Block', () => {
it('renders error state if error is present', async () => {
(useFetch as jest.Mock).mockReturnValue({
state: { data: null, loading: false, error: 'asd' },
state: { data: null, loading: false, error: new Error('asd') },
});
render(renderComponent());
expect(screen.getByText(`BLOCK ${blockId}`)).toBeInTheDocument();
expect(screen.getByText('Error retrieving data')).toBeInTheDocument();
expect(screen.getByText('Something went wrong: asd')).toBeInTheDocument();
});
it('renders loading state if present', async () => {
@@ -1,4 +1,3 @@
import React from 'react';
import { Link, useParams } from 'react-router-dom';
import { DATA_SOURCES } from '../../../config';
import { getDateTimeFormat } from '@vegaprotocol/utils';
@@ -12,9 +11,8 @@ import {
TableCell,
} from '../../../components/table';
import { TxsPerBlock } from '../../../components/txs/txs-per-block';
import { Button } from '@vegaprotocol/ui-toolkit';
import { AsyncRenderer, Button } from '@vegaprotocol/ui-toolkit';
import { Routes } from '../../route-names';
import { RenderFetched } from '../../../components/render-fetched';
import { t } from '@vegaprotocol/i18n';
import { useFetch } from '@vegaprotocol/react-helpers';
import { NodeLink } from '../../../components/links';
@@ -34,7 +32,7 @@ const Block = () => {
return (
<section>
<RouteTitle data-testid="block-header">{t(`BLOCK ${block}`)}</RouteTitle>
<RenderFetched error={error} loading={loading}>
<AsyncRenderer data={blockData} error={error} loading={!!loading}>
<>
<div className="grid grid-cols-2 gap-2 mb-8">
<Link
@@ -123,7 +121,7 @@ const Block = () => {
</>
)}
</>
</RenderFetched>
</AsyncRenderer>
</section>
);
};
+16 -11
View File
@@ -1,7 +1,7 @@
import { t } from '@vegaprotocol/i18n';
import { useFetch } from '@vegaprotocol/react-helpers';
import { RouteTitle } from '../../components/route-title';
import { Loader, SyntaxHighlighter } from '@vegaprotocol/ui-toolkit';
import { AsyncRenderer, SyntaxHighlighter } from '@vegaprotocol/ui-toolkit';
import { DATA_SOURCES } from '../../config';
import type { TendermintGenesisResponse } from './tendermint-genesis-response';
import { useDocumentTitle } from '../../hooks/use-document-title';
@@ -10,21 +10,26 @@ const Genesis = () => {
useDocumentTitle(['Genesis']);
const {
state: { data: genesis, loading },
state: { data, loading, error },
} = useFetch<TendermintGenesisResponse>(
`${DATA_SOURCES.tendermintUrl}/genesis`
);
if (!genesis?.result.genesis) {
if (loading) {
return <Loader />;
}
return null;
}
return (
<section>
<>
<RouteTitle data-testid="genesis-header">{t('Genesis')}</RouteTitle>
<SyntaxHighlighter data={genesis?.result.genesis} />
</section>
<AsyncRenderer
data={data}
error={error}
loading={!!loading}
loadingMessage={t('Loading genesis information...')}
errorMessage={t('Could not fetch genesis data')}
>
<section>
<SyntaxHighlighter data={data?.result.genesis} />
</section>
</AsyncRenderer>
</>
);
};
@@ -49,6 +49,7 @@ export const MarketPage = () => {
/>
<AsyncRenderer
noDataMessage={t('This chain has no markets')}
errorMessage={t('Could not fetch market') + ' ' + marketId}
data={data}
loading={loading}
error={error}
@@ -1,4 +1,4 @@
import { Loader, SyntaxHighlighter } from '@vegaprotocol/ui-toolkit';
import { AsyncRenderer, SyntaxHighlighter } from '@vegaprotocol/ui-toolkit';
import { RouteTitle } from '../../../components/route-title';
import { t } from '@vegaprotocol/i18n';
import { useExplorerOracleSpecsQuery } from '../__generated__/Oracles';
@@ -8,7 +8,7 @@ import { useScrollToLocation } from '../../../hooks/scroll-to-location';
import filter from 'recursive-key-filter';
const Oracles = () => {
const { data, loading } = useExplorerOracleSpecsQuery();
const { data, loading, error } = useExplorerOracleSpecsQuery();
useDocumentTitle(['Oracles']);
useScrollToLocation();
@@ -16,28 +16,40 @@ const Oracles = () => {
return (
<section>
<RouteTitle data-testid="oracle-specs-heading">{t('Oracles')}</RouteTitle>
{loading ? <Loader /> : null}
{data?.oracleSpecsConnection?.edges
? data.oracleSpecsConnection.edges.map((o) => {
const id = o?.node.dataSourceSpec.spec.id;
if (!id) {
return null;
}
return (
<div id={id} key={id} className="mb-10">
<OracleDetails
id={id}
dataSource={o?.node}
showBroadcasts={false}
/>
<details>
<summary className="pointer">JSON</summary>
<SyntaxHighlighter data={filter(o, ['__typename'])} />
</details>
</div>
);
})
: null}
<AsyncRenderer
data={data}
loading={loading}
error={error}
loadingMessage={t('Loading oracle data...')}
errorMessage={t('Oracle data could not be loaded')}
noDataMessage={t('No oracles found')}
noDataCondition={(data) =>
!data?.oracleSpecsConnection?.edges ||
data.oracleSpecsConnection.edges?.length === 0
}
>
{data?.oracleSpecsConnection?.edges
? data.oracleSpecsConnection.edges.map((o) => {
const id = o?.node.dataSourceSpec.spec.id;
if (!id) {
return null;
}
return (
<div id={id} key={id} className="mb-10">
<OracleDetails
id={id}
dataSource={o?.node}
showBroadcasts={false}
/>
<details>
<summary className="pointer">JSON</summary>
<SyntaxHighlighter data={filter(o, ['__typename'])} />
</details>
</div>
);
})
: null}
</AsyncRenderer>
</section>
);
};
@@ -1,12 +1,11 @@
import { RouteTitle } from '../../../components/route-title';
import { RenderFetched } from '../../../components/render-fetched';
import { truncateByChars } from '@vegaprotocol/utils';
import { t } from '@vegaprotocol/i18n';
import { useDocumentTitle } from '../../../hooks/use-document-title';
import { useParams } from 'react-router-dom';
import { useExplorerOracleSpecByIdQuery } from '../__generated__/Oracles';
import { OracleDetails } from '../components/oracle';
import { SyntaxHighlighter } from '@vegaprotocol/ui-toolkit';
import { AsyncRenderer, SyntaxHighlighter } from '@vegaprotocol/ui-toolkit';
import filter from 'recursive-key-filter';
import { TruncateInline } from '../../../components/truncate/truncate';
@@ -27,7 +26,14 @@ export const Oracle = () => {
{t(`Oracle `)}
<TruncateInline startChars={5} endChars={5} text={id || '1'} />
</RouteTitle>
<RenderFetched error={error} loading={loading}>
<AsyncRenderer
data={data}
error={error}
loading={loading}
noDataCondition={(data) => !data?.oracleSpec}
errorMessage={t('Could not load oracle data')}
loadingMessage={t('Loading oracle data...')}
>
{data?.oracleSpec ? (
<div id={id} key={id} className="mb-10">
<OracleDetails
@@ -44,7 +50,7 @@ export const Oracle = () => {
) : (
<span></span>
)}
</RenderFetched>
</AsyncRenderer>
</section>
);
};
@@ -48,6 +48,13 @@ query ExplorerPartyAssets($partyId: ID!) {
}
stakingSummary {
currentStakeAvailable
linkings(pagination: { first: 100 }) {
edges {
node {
amount
}
}
}
}
accountsConnection {
edges {
@@ -10,7 +10,7 @@ export type ExplorerPartyAssetsQueryVariables = Types.Exact<{
}>;
export type ExplorerPartyAssetsQuery = { __typename?: 'Query', partiesConnection?: { __typename?: 'PartyConnection', edges: Array<{ __typename?: 'PartyEdge', node: { __typename?: 'Party', id: string, delegationsConnection?: { __typename?: 'DelegationsConnection', edges?: Array<{ __typename?: 'DelegationEdge', node: { __typename?: 'Delegation', amount: string, epoch: number, node: { __typename?: 'Node', id: string, name: string } } } | null> | null } | null, stakingSummary: { __typename?: 'StakingSummary', currentStakeAvailable: string }, accountsConnection?: { __typename?: 'AccountsConnection', edges?: Array<{ __typename?: 'AccountEdge', node: { __typename?: 'AccountBalance', type: Types.AccountType, balance: string, asset: { __typename?: 'Asset', name: string, id: string, decimals: number, symbol: string, source: { __typename: 'BuiltinAsset' } | { __typename: 'ERC20', contractAddress: string } }, market?: { __typename?: 'Market', id: string, decimalPlaces: number, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', name: string, product: { __typename?: 'Future', quoteName: string } } } } | null } } | null> | null } | null } }> } | null };
export type ExplorerPartyAssetsQuery = { __typename?: 'Query', partiesConnection?: { __typename?: 'PartyConnection', edges: Array<{ __typename?: 'PartyEdge', node: { __typename?: 'Party', id: string, delegationsConnection?: { __typename?: 'DelegationsConnection', edges?: Array<{ __typename?: 'DelegationEdge', node: { __typename?: 'Delegation', amount: string, epoch: number, node: { __typename?: 'Node', id: string, name: string } } } | null> | null } | null, stakingSummary: { __typename?: 'StakingSummary', currentStakeAvailable: string, linkings: { __typename?: 'StakesConnection', edges?: Array<{ __typename?: 'StakeLinkingEdge', node: { __typename?: 'StakeLinking', amount: string } } | null> | null } }, accountsConnection?: { __typename?: 'AccountsConnection', edges?: Array<{ __typename?: 'AccountEdge', node: { __typename?: 'AccountBalance', type: Types.AccountType, balance: string, asset: { __typename?: 'Asset', name: string, id: string, decimals: number, symbol: string, source: { __typename: 'BuiltinAsset' } | { __typename: 'ERC20', contractAddress: string } }, market?: { __typename?: 'Market', id: string, decimalPlaces: number, tradableInstrument: { __typename?: 'TradableInstrument', instrument: { __typename?: 'Instrument', name: string, product: { __typename?: 'Future', quoteName: string } } } } | null } } | null> | null } | null } }> } | null };
export const ExplorerPartyAssetsAccountsFragmentDoc = gql`
fragment ExplorerPartyAssetsAccounts on AccountBalance {
@@ -64,6 +64,13 @@ export const ExplorerPartyAssetsDocument = gql`
}
stakingSummary {
currentStakeAvailable
linkings(pagination: {first: 100}) {
edges {
node {
amount
}
}
}
}
accountsConnection {
edges {
@@ -0,0 +1,24 @@
import { t } from '@vegaprotocol/i18n';
import { useParams } from 'react-router-dom';
import { toNonHex } from '../../../../components/search/detect-search';
import { PageHeader } from '../../../../components/page-header';
import { useDocumentTitle } from '../../../../hooks/use-document-title';
import { PartyAccounts } from '../components/party-accounts';
const PartyAccountsByAsset = () => {
const { party } = useParams<{ party: string }>();
useDocumentTitle(['Public keys', party || '-']);
const partyId = toNonHex(party ? party : '');
return (
<section>
<PageHeader title={t('Balances by asset')} />
<PartyAccounts partyId={partyId} />
</section>
);
};
export { PartyAccountsByAsset };
@@ -1,32 +1,9 @@
import { t } from '@vegaprotocol/i18n';
import get from 'lodash/get';
import AssetBalance from '../../../../components/asset-balance/asset-balance';
import { AssetLink, MarketLink } from '../../../../components/links';
import { Table, TableRow } from '../../../../components/table';
import type * as Schema from '@vegaprotocol/types';
import type { ExplorerPartyAssetsAccountsFragment } from '../__generated__/Party-assets';
const accountTypeString: Record<Schema.AccountType, string> = {
ACCOUNT_TYPE_BOND: t('Bond'),
ACCOUNT_TYPE_EXTERNAL: t('External'),
ACCOUNT_TYPE_FEES_INFRASTRUCTURE: t('Fees (Infrastructure)'),
ACCOUNT_TYPE_FEES_LIQUIDITY: t('Fees (Liquidity)'),
ACCOUNT_TYPE_FEES_MAKER: t('Fees (Maker)'),
ACCOUNT_TYPE_GENERAL: t('General'),
ACCOUNT_TYPE_GLOBAL_INSURANCE: t('Global Insurance Pool'),
ACCOUNT_TYPE_GLOBAL_REWARD: t('Global Reward Pool'),
ACCOUNT_TYPE_INSURANCE: t('Insurance'),
ACCOUNT_TYPE_MARGIN: t('Margin'),
ACCOUNT_TYPE_PENDING_TRANSFERS: t('Pending Transfers'),
ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES: t('Reward - LP Fees received'),
ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES: t('Reward - Maker fees paid'),
ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES: t('Reward - Maker fees received'),
ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS: t('Reward - Market proposers'),
ACCOUNT_TYPE_SETTLEMENT: t('Settlement'),
};
import { AccountManager } from '@vegaprotocol/accounts';
import { useCallback } from 'react';
import { useAssetDetailsDialogStore } from '@vegaprotocol/assets';
interface PartyAccountsProps {
accounts: ExplorerPartyAssetsAccountsFragment[];
partyId: string;
}
/**
@@ -34,49 +11,22 @@ interface PartyAccountsProps {
* probably do with sorting by asset, and then within asset, by type with general
* appearing first and... tbd
*/
export const PartyAccounts = ({ accounts }: PartyAccountsProps) => {
return (
<Table className="max-w-5xl min-w-fit">
<thead>
<TableRow modifier="bordered" className="font-mono">
<td>{t('Type')}</td>
<td>{t('Market')}</td>
<td className="text-right pr-2">{t('Balance')}</td>
<td>{t('Asset')}</td>
</TableRow>
</thead>
<tbody>
{accounts.map((account) => {
const m = get(account, 'market.tradableInstrument.instrument.name');
export const PartyAccounts = ({ partyId }: PartyAccountsProps) => {
const { open: openAssetDetailsDialog } = useAssetDetailsDialogStore();
const onClickAsset = useCallback(
(assetId?: string) => {
assetId && openAssetDetailsDialog(assetId);
},
[openAssetDetailsDialog]
);
return (
<TableRow
key={`pa-${account.asset.id}-${account.type}`}
title={account.asset.name}
id={`${accountTypeString[account.type]} ${m ? ` - ${m}` : ''}`}
>
<td className="text-md">{accountTypeString[account.type]}</td>
<td className="text-md">
{account.market?.id ? (
<MarketLink id={account.market?.id} />
) : (
<p>-</p>
)}
</td>
<td className="text-md text-right pr-2">
<AssetBalance
assetId={account.asset.id}
price={account.balance}
showAssetLink={false}
/>
</td>
<td className="text-md">
<AssetLink assetId={account.asset.id} asDialog={true} />
</td>
</TableRow>
);
})}
</tbody>
</Table>
return (
<div className="block min-h-44 h-60 4 w-full border-red-800 relative">
<AccountManager
partyId={partyId}
onClickAsset={onClickAsset}
isReadOnly={true}
/>
</div>
);
};
@@ -0,0 +1,63 @@
import { t } from '@vegaprotocol/i18n';
import { useNavigate } from 'react-router-dom';
import { Routes } from '../../../../routes/route-names';
import { Button, Icon, Loader } from '@vegaprotocol/ui-toolkit';
import { PartyBlock } from './party-block';
import type { AccountFields } from '@vegaprotocol/accounts';
export interface PartyBlockAccountProps {
partyId: string;
accountData: AccountFields[] | null;
accountLoading: boolean;
accountError?: Error;
}
/**
* Displays an overview of a party's assets. This uses existing data
* providers to structure the details by asset, rather than looking at
* it by account. The assumption is that this is a more natural way to
* get an idea of the assets and activity of a party.
*/
export const PartyBlockAccounts = ({
partyId,
accountData,
accountLoading,
accountError,
}: PartyBlockAccountProps) => {
const navigate = useNavigate();
const shouldShowActionButton =
accountData && accountData.length > 0 && !accountLoading && !accountError;
const action = shouldShowActionButton ? (
<Button
size="sm"
onClick={() => navigate(`/${Routes.PARTIES}/${partyId}/assets`)}
>
{t('Show all')}
</Button>
) : null;
return (
<PartyBlock title={t('Assets')} action={action}>
{accountData && accountData.length > 0 ? (
<p>
{accountData.length} {t('assets, including')}{' '}
{accountData
.map((a) => a.asset.symbol)
.slice(0, 3)
.join(', ')}
</p>
) : accountLoading && !accountError ? (
<Loader size="small" />
) : accountData && accountData.length === 0 ? (
<p>{t('No accounts found')}</p>
) : (
<p>
<Icon className="mr-1" name="error" />
<span className="text-sm">{t('Could not load assets')}</span>
</p>
)}
</PartyBlock>
);
};
@@ -0,0 +1,83 @@
import { t } from '@vegaprotocol/i18n';
import { useExplorerPartyAssetsQuery } from '../__generated__/Party-assets';
import GovernanceAssetBalance from '../../../../components/asset-balance/governance-asset-balance';
import {
Icon,
KeyValueTable,
KeyValueTableRow,
Loader,
} from '@vegaprotocol/ui-toolkit';
import { PartyBlock } from './party-block';
import BigNumber from 'bignumber.js';
export interface PartyBlockStakeProps {
partyId: string;
accountLoading: boolean;
accountError?: Error;
}
/**
* Displays an overview of a single party's staking balance, importantly maintaining'
* the same height before and after the details are loaded in.
*
* Unlike PartyBlockAccounts there is not action button in the title of this block as
* there is no page for it to link to currently. That's a future task.
*/
export const PartyBlockStake = ({
partyId,
accountLoading,
accountError,
}: PartyBlockStakeProps) => {
const partyRes = useExplorerPartyAssetsQuery({
// Don't cache data for this query, party information can move quite quickly
fetchPolicy: 'network-only',
variables: { partyId: partyId },
skip: !partyId,
});
const p = partyRes.data?.partiesConnection?.edges[0].node;
const linkedLength = p?.stakingSummary?.linkings?.edges?.length;
const linkedStake =
linkedLength && linkedLength > 0
? p?.stakingSummary?.linkings?.edges
?.reduce((total, e) => {
return new BigNumber(total).plus(
new BigNumber(e?.node.amount || 0)
);
}, new BigNumber(0))
.toString()
: '0';
return (
<PartyBlock title={t('Staking')}>
{p?.stakingSummary.currentStakeAvailable ? (
<KeyValueTable>
<KeyValueTableRow noBorder={true}>
<div>{t('Available stake')}</div>
<div>
<GovernanceAssetBalance
price={p.stakingSummary.currentStakeAvailable}
/>
</div>
</KeyValueTableRow>
<KeyValueTableRow noBorder={true}>
<div>{t('Active stake')}</div>
<div>
<GovernanceAssetBalance price={linkedStake || '0'} />
</div>
</KeyValueTableRow>
</KeyValueTable>
) : accountLoading && !accountError ? (
<Loader size="small" />
) : !accountError ? (
<p>{t('No staking balance')}</p>
) : (
<p>
<Icon className="mr-1" name="error" />
<span className="text-sm">{t('Could not load stake details')}</span>
</p>
)}
</PartyBlock>
);
};
@@ -0,0 +1,24 @@
import type { ReactNode } from 'react';
export interface PartyBlockProps {
children: ReactNode;
title: string;
action?: ReactNode;
}
export function PartyBlock({ children, title, action }: PartyBlockProps) {
return (
<div className="border-2 min-h-[138px] border-solid border-vega-light-100 dark:border-vega-dark-200 p-5 mt-5">
<div
className="flex flex-col md:flex-row gap-1 justify-between content-start mb-2"
data-testid="page-title"
>
<h3 className="font-semibold text-lg">{title}</h3>
{action ? action : null}
</div>
{children}
</div>
);
}
@@ -1,24 +1,26 @@
import { getNodes } from '@vegaprotocol/utils';
import { t } from '@vegaprotocol/i18n';
import { useScreenDimensions } from '@vegaprotocol/react-helpers';
import { useMemo } from 'react';
import { useParams } from 'react-router-dom';
import { useNavigate, useParams } from 'react-router-dom';
import { SubHeading } from '../../../components/sub-heading';
import { Panel } from '../../../components/panel';
import { toNonHex } from '../../../components/search/detect-search';
import { useTxsData } from '../../../hooks/use-txs-data';
import { TxsInfiniteList } from '../../../components/txs';
import { PageHeader } from '../../../components/page-header';
import { useExplorerPartyAssetsQuery } from './__generated__/Party-assets';
import type { ExplorerPartyAssetsAccountsFragment } from './__generated__/Party-assets';
import { useDocumentTitle } from '../../../hooks/use-document-title';
import GovernanceAssetBalance from '../../../components/asset-balance/governance-asset-balance';
import { PartyAccounts } from './components/party-accounts';
import { Icon, Intent, Notification, Splash } from '@vegaprotocol/ui-toolkit';
import { aggregatedAccountsDataProvider } from '@vegaprotocol/accounts';
import { PartyBlockStake } from './components/party-block-stake';
import { PartyBlockAccounts } from './components/party-block-accounts';
import { isValidPartyId } from './components/party-id-error';
import { useDataProvider } from '@vegaprotocol/data-provider';
const Party = () => {
const { party } = useParams<{ party: string }>();
useDocumentTitle(['Public keys', party || '-']);
const navigate = useNavigate();
const partyId = toNonHex(party ? party : '');
const { isMobile } = useScreenDimensions();
const visibleChars = useMemo(() => (isMobile ? 10 : 14), [isMobile]);
@@ -28,71 +30,72 @@ const Party = () => {
filters,
});
const partyRes = useExplorerPartyAssetsQuery({
// Don't cache data for this query, party information can move quite quickly
fetchPolicy: 'network-only',
variables: { partyId: partyId },
skip: !party,
const variables = useMemo(() => ({ partyId }), [partyId]);
const {
data: AccountData,
loading: AccountLoading,
error: AccountError,
} = useDataProvider({
dataProvider: aggregatedAccountsDataProvider,
variables,
});
const p = partyRes.data?.partiesConnection?.edges[0].node;
const header = p?.id ? (
<PageHeader
title={p.id}
copy
truncateStart={visibleChars}
truncateEnd={visibleChars}
/>
) : (
<Panel>
<p>No data found for public key {party}</p>
</Panel>
);
const staking = (
<section>
{p?.stakingSummary?.currentStakeAvailable ? (
<div className="mt-4 leading-3">
<strong className="font-semibold">{t('Staking Balance: ')}</strong>
<GovernanceAssetBalance
price={p.stakingSummary.currentStakeAvailable}
/>
</div>
) : null}
</section>
);
const accounts = getNodes<ExplorerPartyAssetsAccountsFragment>(
p?.accountsConnection
);
if (!isValidPartyId(partyId)) {
return (
<div className="max-w-sm mx-auto">
<Notification
message={t('Invalid party ID')}
intent={Intent.Danger}
buttonProps={{
text: t('Go back'),
action: () => navigate(-1),
className: 'py-1',
size: 'sm',
}}
/>
</div>
);
}
return (
<section>
<h1
className="font-alpha calt uppercase font-xl mb-4 text-vega-dark-100 dark:text-vega-light-100"
data-testid="parties-header"
>
{t('Public key')}
</h1>
{partyRes.data ? (
<>
{header}
<SubHeading>{t('Asset data')}</SubHeading>
{accounts ? <PartyAccounts accounts={accounts} /> : null}
{staking}
<PageHeader
title={partyId}
copy
truncateStart={visibleChars}
truncateEnd={visibleChars}
/>
<SubHeading>{t('Transactions')}</SubHeading>
<TxsInfiniteList
hasMoreTxs={hasMoreTxs}
areTxsLoading={loading}
txs={txsData}
loadMoreTxs={loadTxs}
error={error}
className="mb-28"
/>
</>
) : null}
<div className="grid md:grid-flow-col grid-flow-row md:space-x-4 grid-cols-1 md:grid-cols-2 w-full">
<PartyBlockAccounts
accountError={AccountError}
accountLoading={AccountLoading}
accountData={AccountData}
partyId={partyId}
/>
<PartyBlockStake
accountError={AccountError}
accountLoading={AccountLoading}
partyId={partyId}
/>
</div>
<SubHeading>{t('Transactions')}</SubHeading>
{!error && txsData ? (
<TxsInfiniteList
hasMoreTxs={hasMoreTxs}
areTxsLoading={loading}
txs={txsData}
loadMoreTxs={loadTxs}
error={error}
className="mb-28"
/>
) : (
<Splash>
<Icon name="error" className="mr-1" />
&nbsp;{t('Could not load transaction list for party')}
</Splash>
)}
</section>
);
};
@@ -0,0 +1,7 @@
import { Outlet } from 'react-router-dom';
const PartiesSubPage = () => {
return <Outlet />;
};
export default PartiesSubPage;
@@ -1,35 +0,0 @@
import React from 'react';
import { DATA_SOURCES } from '../../config';
import type { TendermintUnconfirmedTransactionsResponse } from '../txs/tendermint-unconfirmed-transactions-response.d';
import { TxList } from '../../components/txs';
import { RouteTitle } from '../../components/route-title';
import { t } from '@vegaprotocol/i18n';
import { useFetch } from '@vegaprotocol/react-helpers';
import { useDocumentTitle } from '../../hooks/use-document-title';
const PendingTxs = () => {
const {
state: { data: unconfirmedTransactions },
} = useFetch<TendermintUnconfirmedTransactionsResponse>(
`${DATA_SOURCES.tendermintUrl}/unconfirmed_txs`
);
useDocumentTitle(['Pending transactions']);
return (
<section>
<RouteTitle data-testid="unconfirmed-transactions-header">
{t('Unconfirmed transactions')}
</RouteTitle>
<br />
<div>{t(`Number: ${unconfirmedTransactions?.result?.n_txs || 0}`)}</div>
<br />
<div>
<br />
<TxList data={unconfirmedTransactions} />
</div>
</section>
);
};
export { PendingTxs };
+38 -20
View File
@@ -14,7 +14,6 @@ import { Block } from './blocks/id';
import { Blocks } from './blocks/home';
import { Tx } from './txs/id';
import { TxsList } from './txs/home';
import { PendingTxs } from './pending';
import flags from '../config/flags';
import { t } from '@vegaprotocol/i18n';
import { Routes } from './route-names';
@@ -29,6 +28,7 @@ import compact from 'lodash/compact';
import { AssetLink, MarketLink } from '../components/links';
import { truncateMiddle } from '@vegaprotocol/ui-toolkit';
import { remove0x } from '@vegaprotocol/utils';
import { PartyAccountsByAsset } from './parties/id/accounts';
export type Navigable = {
path: string;
@@ -75,14 +75,43 @@ const partiesRoutes: Route[] = flags.parties
},
{
path: ':party',
element: <PartySingle />,
handle: {
breadcrumb: (params: Params<string>) => (
<Link to={linkTo(Routes.PARTIES, params.party)}>
{truncateMiddle(params.party as string)}
</Link>
),
},
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>
),
},
},
{
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');
},
},
},
],
},
],
},
],
},
@@ -237,17 +266,6 @@ export const routerConfig: Route[] = [
breadcrumb: () => <Link to={Routes.TX}>{t('Transactions')}</Link>,
},
children: [
{
path: 'pending',
element: <PendingTxs />,
handle: {
breadcrumb: () => (
<Link to={linkTo(Routes.TX, 'pending')}>
{t('Pending transactions')}
</Link>
),
},
},
{
path: ':txHash',
element: <Tx />,
@@ -10,7 +10,7 @@ interface TxDetailsProps {
export const txDetailsTruncateLength = 30;
export const TxDetails = ({ txData, pubKey, className }: TxDetailsProps) => {
export const TxDetails = ({ txData, pubKey }: TxDetailsProps) => {
if (!txData) {
return <>{t('Awaiting Block Explorer transaction details')}</>;
}
@@ -28,6 +28,7 @@ import {
} from '@vegaprotocol/environment';
import classNames from 'classnames';
import { NodeStatus, NodeStatusMapping } from '@vegaprotocol/types';
import { PartyLink } from '../../components/links';
type RateProps = {
value: BigNumber | number | undefined;
@@ -191,7 +192,9 @@ export const ValidatorsPage = () => {
<KeyValueTable>
<KeyValueTableRow>
<div>{t('ID')}</div>
<div className="break-all text-xs">{v.id}</div>
<div className="break-all text-xs font-mono">
{v.id}
</div>
</KeyValueTableRow>
<KeyValueTableRow>
<div>{t('Status')}</div>
@@ -218,12 +221,14 @@ export const ValidatorsPage = () => {
</div>
</KeyValueTableRow>
<KeyValueTableRow>
<div>{t('Public key')}</div>
<div className="break-all text-xs">{v.pubkey}</div>
<div>{t('Key')}</div>
<div className="break-all text-xs">
<PartyLink id={v.pubkey} />
</div>
</KeyValueTableRow>
<KeyValueTableRow>
<div>{t('Ethereum address')}</div>
<div className="break-all text-xs">
<div className="break-all text-xs font-mono">
<EtherscanLink address={v.ethereumAddress} />{' '}
<CopyWithTooltip text={v.ethereumAddress}>
<button title={t('Copy address to clipboard')}>
@@ -234,7 +239,9 @@ export const ValidatorsPage = () => {
</KeyValueTableRow>
<KeyValueTableRow>
<div>{t('Tendermint public key')}</div>
<div className="break-all text-xs">{v.tmPubkey}</div>
<div className="break-all text-xs font-mono">
{v.tmPubkey}
</div>
</KeyValueTableRow>
<KeyValueTableRow>
+5
View File
@@ -32,6 +32,11 @@
line-height: calc(min(var(--ag-line-height, 26px), 26px) - 4px);
}
.vega-ag-grid .ag-row {
border-width: 1px 0;
border-bottom: 1px solid transparent;
}
/* Light variables */
.ag-theme-balham {
--ag-background-color: theme(colors.white);
+1 -1
View File
@@ -1,5 +1,5 @@
# App configuration variables
NX_VEGA_ENV=MAINNET
NX_VEGA_URL=https://api.vega.xyz/query
NX_VEGA_URL=https://api.vega.community/graphql
NX_ETHEREUM_PROVIDER_URL=https://mainnet.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
NX_ETHERSCAN_URL=https://etherscan.io
@@ -4,12 +4,11 @@ import {
navigation,
} from '../../support/common.functions';
import {
convertUnixTimestampToDateformat,
createRawProposal,
createTenDigitUnixTimeStampForSpecifiedDays,
enterUniqueFreeFormProposalBody,
generateFreeFormProposalTitle,
getGovernanceProposalDateFormatForSpecifiedDays,
getDateFormatForSpecifiedDays,
getProposalIdFromList,
getProposalInformationFromTable,
getSubmittedProposalFromProposalList,
@@ -23,6 +22,7 @@ import { ensureSpecifiedUnstakedTokensAreAssociated } from '../../../../governan
import { ethereumWalletConnect } from '../../../../governance-e2e/src/support/wallet-eth.functions';
import { vegaWalletSetSpecifiedApprovalAmount } from '../../../../governance-e2e/src/support/wallet-teardown.functions';
import type { testFreeformProposal } from '../../support/common-interfaces';
import { formatDateWithLocalTimezone } from '@vegaprotocol/utils';
const proposalVoteProgressForPercentage =
'[data-testid="vote-progress-indicator-percentage-for"]';
@@ -99,20 +99,22 @@ describe(
getSubmittedProposalFromProposalList(proposalTitle).within(() =>
cy.get(viewProposalButton).click()
);
convertUnixTimestampToDateformat(proposalTimeStamp).then(
(closingDate) => {
getProposalInformationFromTable('Closes on')
.contains(closingDate)
.should('be.visible');
}
);
getGovernanceProposalDateFormatForSpecifiedDays(0).then(
(proposalDate) => {
getProposalInformationFromTable('Proposed on')
.contains(proposalDate)
.should('be.visible');
}
);
cy.wrap(
formatDateWithLocalTimezone(new Date(proposalTimeStamp * 1000))
).then((closingDate) => {
getProposalInformationFromTable('Closes on')
.contains(closingDate)
.should('be.visible');
});
cy.wrap(
formatDateWithLocalTimezone(
new Date(createTenDigitUnixTimeStampForSpecifiedDays(0) * 1000)
)
).then((proposalDate) => {
getProposalInformationFromTable('Proposed on')
.contains(proposalDate)
.should('be.visible');
});
});
it('Newly created proposal details - shows default status set to fail', function () {
@@ -155,18 +157,16 @@ describe(
cy.getByTestId('vote-buttons').contains('against').should('be.visible');
cy.getByTestId('vote-buttons').contains('for').should('be.visible');
voteForProposal('for');
getGovernanceProposalDateFormatForSpecifiedDays(0, 'shortMonth').then(
(votedDate) => {
// 3001-VOTE-051
// 3001-VOTE-093
cy.contains('You voted:')
.siblings()
.contains('For')
.siblings()
.contains(votedDate)
.should('be.visible');
}
);
getDateFormatForSpecifiedDays(0).then((votedDate) => {
// 3001-VOTE-051
// 3001-VOTE-093
cy.contains('You voted:')
.siblings()
.contains('For')
.siblings()
.contains(votedDate)
.should('be.visible');
});
cy.get(proposalVoteProgressForPercentage) // 3001-VOTE-072
.contains('100.00%')
.and('be.visible');
@@ -29,6 +29,8 @@ const tableReceiverAddress = '[col-id="details.receiverAddress"]';
const tableWithdrawnTimeStamp = '[col-id="withdrawnTimestamp"]';
const tableWithdrawnStatus = '[col-id="status"]';
const tableCreatedTimeStamp = '[col-id="createdTimestamp"]';
const toastContent = 'toast-content';
const toastPanel = 'toast-panel';
const usdtName = 'USDC (local)';
const usdcEthAddress = '0x1b8a1B6CBE5c93609b46D1829Cc7f3Cb8eeE23a0';
const usdcSymbol = 'tUSDC';
@@ -149,7 +151,6 @@ context(
});
});
// Skipping because of bug #1857
it('Able to withdraw asset: -eth wallet not connected', function () {
const ethWalletAddress = Cypress.env('ethWalletPublicKey');
cy.reload();
@@ -187,8 +188,26 @@ context(
.should('have.attr', 'href')
.and('contain', 'https://sepolia.etherscan.io/address/');
cy.get(tableCreatedTimeStamp).should('not.be.empty');
cy.getByTestId(completeWithdrawalButton).click();
// Unable to complete withdrawal in Capsule
});
ethereumWalletConnect();
cy.getByTestId(completeWithdrawalButton).click();
cy.getByTestId(toastContent)
.last()
.should('contain.text', 'Awaiting confirmation')
.within(() => {
cy.getByTestId('external-link').should('exist');
});
cy.getByTestId(toastContent)
.first()
.should('contain.text', 'The withdrawal has been approved.')
.within(() => {
cy.getByTestId(toastPanel).should('contain.text', '110.00', 'tUSDC');
});
cy.getByTestId(toastContent)
.last()
.should('contain.text', 'Transaction confirmed')
.within(() => {
cy.getByTestId('external-link').should('exist');
});
});
@@ -218,7 +237,10 @@ context(
});
function waitForAssetsDisplayed(expectedAsset: string) {
cy.getByTestId('currency-title').should('contain.text', expectedAsset);
cy.getByTestId('currency-title', txTimeout).should(
'contain.text',
expectedAsset
);
}
}
);
@@ -1,3 +1,4 @@
import { format } from 'date-fns';
import { closeDialog, navigateTo, navigation } from './common.functions';
import { ensureSpecifiedUnstakedTokensAreAssociated } from './staking.functions';
@@ -15,35 +16,6 @@ const newProposalSubmitButton = '[data-testid="proposal-submit"]';
const epochTimeout = Cypress.env('epochTimeout');
const proposalTimeout = { timeout: 14000 };
export function convertUnixTimestampToDateformat(
unixTimestamp: number,
monthTextLength = 'longMonth'
) {
const dateSupplied = new Date(unixTimestamp * 1000);
const year = dateSupplied.getFullYear();
const months = [
'January',
'February',
'March',
'April',
'May',
'June',
'July',
'August',
'September',
'October',
'November',
'December',
];
const month = months[dateSupplied.getMonth()];
const shortMonth = months[dateSupplied.getMonth()].substring(0, 3),
date = dateSupplied.getDate();
if (monthTextLength === 'longMonth') {
return cy.wrap(`${date} ${month} ${year}`);
} else return cy.wrap(`${date} ${shortMonth} ${year}`);
}
export function createTenDigitUnixTimeStampForSpecifiedDays(
durationDays: number
) {
@@ -52,6 +24,13 @@ export function createTenDigitUnixTimeStampForSpecifiedDays(
return (timestamp = Math.floor(timestamp / 1000));
}
export function getDateFormatForSpecifiedDays(days: number) {
const date = new Date(
createTenDigitUnixTimeStampForSpecifiedDays(days) * 1000
);
return cy.wrap(format(date, 'dd MMM yyyy'));
}
export function enterRawProposalBody(timestamp: number) {
cy.fixture('/proposals/raw.json').then((rawProposal) => {
rawProposal.terms.closingTimestamp = timestamp;
@@ -104,16 +83,6 @@ export function getProposalIdFromList(proposalTitle: string) {
});
}
export function getGovernanceProposalDateFormatForSpecifiedDays(
days: number,
shortOrLong?: string
) {
return convertUnixTimestampToDateformat(
createTenDigitUnixTimeStampForSpecifiedDays(days),
shortOrLong
);
}
export function getProposalInformationFromTable(heading: string) {
return cy.get(proposalInformationTableRows).contains(heading).siblings();
}
+1 -1
View File
@@ -1,7 +1,7 @@
# App configuration variables
NX_VEGA_ENV=MAINNET
NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks/master/mainnet1/mainnet1.toml
NX_VEGA_URL=https://api.vega.xyz/query
NX_VEGA_URL=https://api.vega.community/graphql
NX_VEGA_NETWORKS='{"DEVNET":"https://dev.token.vega.xyz","TESTNET":"https://token.fairground.wtf","MAINNET":"https://token.vega.xyz"}'
NX_ETHEREUM_PROVIDER_URL=https://mainnet.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
NX_ETHERSCAN_URL=https://etherscan.io
@@ -21,10 +21,10 @@ export const useGetUserBalances = (account: string | undefined) => {
token.allowance(account, config.staking_bridge_contract.address),
]);
const balance = toBigNum(b, decimals);
const walletBalance = toBigNum(w, decimals);
const lien = toBigNum(stats.lien, decimals);
const allowance = toBigNum(a, decimals);
const balance = toBigNum(b.toString(), decimals);
const walletBalance = toBigNum(w.toString(), decimals);
const lien = toBigNum(stats.lien.toString(), decimals);
const allowance = toBigNum(a.toString(), decimals);
return {
balanceFormatted: balance,
@@ -21,8 +21,14 @@ export function useRefreshAssociatedBalances() {
]);
updateBalances({
walletAssociatedBalance: toBigNum(walletAssociatedBalance, decimals),
vestingAssociatedBalance: toBigNum(vestingAssociatedBalance, decimals),
walletAssociatedBalance: toBigNum(
walletAssociatedBalance.toString(),
decimals
),
vestingAssociatedBalance: toBigNum(
vestingAssociatedBalance.toString(),
decimals
),
});
},
[staking, vesting, updateBalances, decimals]
@@ -31,12 +31,18 @@ export const useRefreshBalances = (address: string) => {
pubKey ? vesting.stake_balance(address, pubKey) : null,
]);
const balance = toBigNum(b, decimals);
const walletBalance = toBigNum(w, decimals);
const lien = toBigNum(stats.lien, decimals);
const allowance = toBigNum(a, decimals);
const walletAssociatedBalance = toBigNum(walletStakeBalance, decimals);
const vestingAssociatedBalance = toBigNum(vestingStakeBalance, decimals);
const balance = toBigNum(b.toString(), decimals);
const walletBalance = toBigNum(w.toString(), decimals);
const lien = toBigNum(stats.lien.toString(), decimals);
const allowance = toBigNum(a.toString(), decimals);
const walletAssociatedBalance = toBigNum(
walletStakeBalance ? walletStakeBalance.toString() : 0,
decimals
);
const vestingAssociatedBalance = toBigNum(
vestingStakeBalance ? vestingStakeBalance.toString() : 0,
decimals
);
updateBalances({
balanceFormatted: balance,
@@ -34,8 +34,9 @@ export const useUserTrancheBalances = (address: string | undefined) => {
vesting.get_vested_for_tranche(address, tId),
]);
const total = toBigNum(t, decimals);
const vested = toBigNum(v, decimals);
// Convert t and v EthersBigNumbers to regular BigNumbers
const total = toBigNum(t.toString(), decimals);
const vested = toBigNum(v.toString(), decimals);
return {
id: tId,
@@ -54,7 +54,7 @@ export const WalletAssociate = ({
address,
ethereumConfig.staking_bridge_contract.address
);
const allowance = toBigNum(a, decimals);
const allowance = toBigNum(a.toString(), decimals);
setAllowance(allowance);
}
};
+3
View File
@@ -6,6 +6,7 @@ import '@testing-library/jest-dom';
import dev from './i18n/translations/dev.json';
import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';
import ResizeObserver from 'resize-observer-polyfill';
// Set up i18n instance so that components have the correct default
// en translations
@@ -22,3 +23,5 @@ i18n.use(initReactI18next).init({
ns: ['translations'],
defaultNS: 'translations',
});
global.ResizeObserver = ResizeObserver;
+5
View File
@@ -40,6 +40,11 @@
line-height: calc(min(var(--ag-line-height, 26px), 26px) - 4px);
}
.vega-ag-grid .ag-row {
border-width: 1px 0;
border-bottom: 1px solid transparent;
}
/* Dark variables */
.ag-theme-balham-dark {
--ag-background-color: theme(colors.black);
@@ -1,6 +1,6 @@
# App configuration variables
NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks/master/mainnet1/mainnet1.toml
NX_VEGA_URL=https://api.vega.xyz/query
NX_VEGA_URL=https://api.vega.community/graphql
NX_VEGA_ENV=MAINNET
NX_VEGA_NETWORKS={\"MAINNET\":\"https://alpha.console.vega.xyz\",\"TESTNET\":\"https://console.fairground.wtf\"}
NX_ETHEREUM_PROVIDER_URL=https://mainnet.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8
+1 -1
View File
@@ -1,5 +1,5 @@
# App configuration variables
NX_VEGA_CONFIG_URL=https://raw.githubusercontent.com/vegaprotocol/networks/master/mainnet1/mainnet1.toml
NX_VEGA_URL=https://api.vega.xyz/query
NX_VEGA_URL=https://api.vega.community/graphql
NX_VEGA_NETWORKS='{"TESTNET":"https://multisig-signer.fairground.wtf","MAINNET":"https://multisig-signer.vega.xyz"}'
NX_VEGA_ENV=MAINNET
+14 -14
View File
@@ -49,7 +49,7 @@
"tranche_end": "2023-05-20T00:00:00.000Z",
"total_added": "19242.125",
"total_removed": "1523.8177488329475",
"locked_amount": "7371.31511443865825548625",
"locked_amount": "6891.9471601080244395475",
"deposits": [
{
"amount": "188",
@@ -4861,7 +4861,7 @@
"tranche_end": "2023-12-05T00:00:00.000Z",
"total_added": "86666.297",
"total_removed": "0",
"locked_amount": "49979.7329096604869903443",
"locked_amount": "49802.2753121154213651374",
"deposits": [
{
"amount": "86666.297",
@@ -4927,7 +4927,7 @@
"tranche_end": "2023-06-01T00:00:00.000Z",
"total_added": "2500",
"total_removed": "0",
"locked_amount": "322.698698870573775",
"locked_amount": "312.4325905575906",
"deposits": [
{
"amount": "2500",
@@ -4960,7 +4960,7 @@
"tranche_end": "2023-11-01T00:00:00.000Z",
"total_added": "15000.000000000000015",
"total_removed": "0",
"locked_amount": "14387.9727128623185143879727128623185",
"locked_amount": "14327.0455917874395143270455917874395",
"deposits": [
{
"amount": "1.5e-14",
@@ -5048,7 +5048,7 @@
"tranche_end": "2023-09-01T00:00:00.000Z",
"total_added": "17500",
"total_removed": "0",
"locked_amount": "10984.33773022342925",
"locked_amount": "10913.25608896940375",
"deposits": [
{
"amount": "12500",
@@ -5315,7 +5315,7 @@
"tranche_end": "2023-08-01T00:00:00.000Z",
"total_added": "37500",
"total_removed": "18077.0118744",
"locked_amount": "17505.34501611418125",
"locked_amount": "17350.5026089625535",
"deposits": [
{
"amount": "7500",
@@ -5734,7 +5734,7 @@
"tranche_end": "2023-12-05T00:00:00.000Z",
"total_added": "129999.45",
"total_removed": "0",
"locked_amount": "49934.132692071551825937",
"locked_amount": "49756.837002255673748325",
"deposits": [
{
"amount": "129999.45",
@@ -5767,7 +5767,7 @@
"tranche_end": "2024-04-01T00:00:00.000Z",
"total_added": "54144.7663",
"total_removed": "0",
"locked_amount": "48596.0321414648663214997",
"locked_amount": "48485.46842731635829207292",
"deposits": [
{
"amount": "54144.7663",
@@ -5800,7 +5800,7 @@
"tranche_end": "2023-09-03T00:00:00.000Z",
"total_added": "62600",
"total_removed": "0",
"locked_amount": "20150.76253805175029",
"locked_amount": "20022.5830035514954",
"deposits": [
{
"amount": "10000",
@@ -5993,7 +5993,7 @@
"tranche_end": "2023-09-17T00:00:00.000Z",
"total_added": "5000",
"total_removed": "0",
"locked_amount": "1801.26664764079125",
"locked_amount": "1791.028665651953",
"deposits": [
{
"amount": "5000",
@@ -7062,7 +7062,7 @@
"tranche_end": "2023-06-02T00:00:00.000Z",
"total_added": "1939928.38",
"total_removed": "1709370.7872515768348",
"locked_amount": "130174.32462609789781957",
"locked_amount": "126202.1342632826843379468",
"deposits": [
{
"amount": "1852091.69",
@@ -40934,7 +40934,7 @@
"tranche_end": "2023-06-05T00:00:00.000Z",
"total_added": "3732368.4671",
"total_removed": "715655.108029600523393",
"locked_amount": "224533.9397934779571663606186",
"locked_amount": "218430.066175628697078272948",
"deposits": [
{
"amount": "1998.95815",
@@ -42327,7 +42327,7 @@
"tranche_end": "2023-12-05T00:00:00.000Z",
"total_added": "15870102.715470999700000001",
"total_removed": "871680.07831804700259352",
"locked_amount": "6095870.51969169065811272227952994009813466",
"locked_amount": "6074226.5757489411253544394273819693295485",
"deposits": [
{
"amount": "16249.93",
@@ -59070,7 +59070,7 @@
"tranche_end": "2023-06-05T00:00:00.000Z",
"total_added": "472355.6199999996",
"total_removed": "44479.0535455583416",
"locked_amount": "35578.671272696564429696212379508",
"locked_amount": "34611.49258501772999518148756976",
"deposits": [
{
"amount": "3000",
@@ -12,7 +12,7 @@ import {
formatNumberPercentage,
} from '@vegaprotocol/utils';
import { t } from '@vegaprotocol/i18n';
import { updateGridData } from '@vegaprotocol/react-helpers';
import { updateGridData } from '@vegaprotocol/datagrid';
import {
NetworkParams,
useNetworkParams,
@@ -155,6 +155,7 @@ const MarketBottomPanel = memo(
onMarketClick={onMarketClick}
onOrderTypeClick={onOrderTypeClick}
enforceBottomPlaceholder
storeKey="marketOpenOrders"
/>
</VegaWalletContainer>
</Tab>
@@ -166,6 +167,7 @@ const MarketBottomPanel = memo(
onMarketClick={onMarketClick}
onOrderTypeClick={onOrderTypeClick}
enforceBottomPlaceholder
storeKey="marketClosedOrders"
/>
</VegaWalletContainer>
</Tab>
@@ -177,6 +179,7 @@ const MarketBottomPanel = memo(
onMarketClick={onMarketClick}
onOrderTypeClick={onOrderTypeClick}
enforceBottomPlaceholder
storeKey="marketRejectOrders"
/>
</VegaWalletContainer>
</Tab>
@@ -187,6 +190,7 @@ const MarketBottomPanel = memo(
onMarketClick={onMarketClick}
onOrderTypeClick={onOrderTypeClick}
enforceBottomPlaceholder
storeKey="marketAllOrders"
/>
</VegaWalletContainer>
</Tab>
@@ -195,6 +199,7 @@ const MarketBottomPanel = memo(
<TradingViews.fills.component
marketId={marketId}
onMarketClick={onMarketClick}
storeKey="marketFills"
/>
</VegaWalletContainer>
</Tab>
@@ -213,6 +218,7 @@ const MarketBottomPanel = memo(
<TradingViews.positions.component
onMarketClick={onMarketClick}
noBottomPlaceholder
storeKey="marketPositions"
/>
</VegaWalletContainer>
</Tab>
@@ -222,6 +228,7 @@ const MarketBottomPanel = memo(
pinnedAsset={pinnedAsset}
noBottomPlaceholder
hideButtons
storeKey="marketCollateral"
/>
</VegaWalletContainer>
</Tab>
@@ -234,7 +241,10 @@ const MarketBottomPanel = memo(
<Tabs storageKey="console-trade-grid-bottom">
<Tab id="positions" name={t('Positions')}>
<VegaWalletContainer>
<TradingViews.positions.component onMarketClick={onMarketClick} />
<TradingViews.positions.component
onMarketClick={onMarketClick}
storeKey="marketPositions"
/>
</VegaWalletContainer>
</Tab>
<Tab id="open-orders" name={t('Open')}>
@@ -245,6 +255,7 @@ const MarketBottomPanel = memo(
onMarketClick={onMarketClick}
onOrderTypeClick={onOrderTypeClick}
enforceBottomPlaceholder
storeKey="marketOpenOrders"
/>
</VegaWalletContainer>
</Tab>
@@ -256,6 +267,7 @@ const MarketBottomPanel = memo(
onMarketClick={onMarketClick}
onOrderTypeClick={onOrderTypeClick}
enforceBottomPlaceholder
storeKey="marketClosedOrders"
/>
</VegaWalletContainer>
</Tab>
@@ -267,6 +279,7 @@ const MarketBottomPanel = memo(
onMarketClick={onMarketClick}
onOrderTypeClick={onOrderTypeClick}
enforceBottomPlaceholder
storeKey="marketRejectedOrders"
/>
</VegaWalletContainer>
</Tab>
@@ -277,6 +290,7 @@ const MarketBottomPanel = memo(
onMarketClick={onMarketClick}
onOrderTypeClick={onOrderTypeClick}
enforceBottomPlaceholder
storeKey="marketAllOrders"
/>
</VegaWalletContainer>
</Tab>
@@ -285,6 +299,7 @@ const MarketBottomPanel = memo(
<TradingViews.fills.component
marketId={marketId}
onMarketClick={onMarketClick}
storeKey="marketFills"
/>
</VegaWalletContainer>
</Tab>
@@ -293,6 +308,7 @@ const MarketBottomPanel = memo(
<TradingViews.collateral.component
pinnedAsset={pinnedAsset}
hideButtons
storeKey="marketCollateral"
/>
</VegaWalletContainer>
</Tab>
+2 -1
View File
@@ -264,6 +264,7 @@ const ClosedMarketsDataGrid = ({ rowData }: { rowData: Row[] }) => {
{
headerName: t('Market ID'),
field: 'id',
flex: 1,
},
];
return cols;
@@ -276,10 +277,10 @@ const ClosedMarketsDataGrid = ({ rowData }: { rowData: Row[] }) => {
columnDefs={colDefs}
getRowId={({ data }) => data.id}
defaultColDef={{
flex: 1,
resizable: true,
}}
overlayNoRowsTemplate="No data"
storeKey="closedMarkets"
/>
);
};
@@ -2,7 +2,7 @@ import { AsyncRenderer, Button } from '@vegaprotocol/ui-toolkit';
import { useDepositDialog, DepositsTable } from '@vegaprotocol/deposits';
import { depositsProvider } from '@vegaprotocol/deposits';
import { t } from '@vegaprotocol/i18n';
import { useBottomPlaceholder } from '@vegaprotocol/react-helpers';
import { useBottomPlaceholder } from '@vegaprotocol/datagrid';
import { useDataProvider } from '@vegaprotocol/data-provider';
import { useVegaWallet } from '@vegaprotocol/wallet';
import { useRef } from 'react';
@@ -53,6 +53,7 @@ export const Portfolio = () => {
<PositionsContainer
onMarketClick={onMarketClick}
noBottomPlaceholder
storeKey="portfolioPositions"
/>
</VegaWalletContainer>
</Tab>
@@ -61,12 +62,16 @@ export const Portfolio = () => {
<OrderListContainer
onMarketClick={onMarketClick}
onOrderTypeClick={onOrderTypeClick}
storeKey="portfolioOrders"
/>
</VegaWalletContainer>
</Tab>
<Tab id="fills" name={t('Fills')}>
<VegaWalletContainer>
<FillsContainer onMarketClick={onMarketClick} />
<FillsContainer
onMarketClick={onMarketClick}
storeKey="portfolioFills"
/>
</VegaWalletContainer>
</Tab>
<Tab id="ledger-entries" name={t('Ledger entries')}>
@@ -86,7 +91,7 @@ export const Portfolio = () => {
<Tabs storageKey="console-portfolio-bottom">
<Tab id="collateral" name={t('Collateral')}>
<VegaWalletContainer>
<AccountsContainer />
<AccountsContainer storeKey="portfolioCollateral" />
</VegaWalletContainer>
</Tab>
<Tab id="deposits" name={t('Deposits')}>
@@ -13,10 +13,12 @@ export const AccountsContainer = ({
pinnedAsset,
hideButtons,
noBottomPlaceholder,
storeKey,
}: {
pinnedAsset?: PinnedAsset;
hideButtons?: boolean;
noBottomPlaceholder?: boolean;
storeKey?: string;
}) => {
const { pubKey, isReadOnly } = useVegaWallet();
const { open: openAssetDetailsDialog } = useAssetDetailsDialogStore();
@@ -49,6 +51,7 @@ export const AccountsContainer = ({
isReadOnly={isReadOnly}
pinnedAsset={pinnedAsset}
noBottomPlaceholder={noBottomPlaceholder}
storeKey={storeKey}
/>
{!isReadOnly && !hideButtons && (
<div className="flex gap-2 justify-end p-2 px-[11px] absolute lg:fixed bottom-0 right-3 dark:bg-black/75 bg-white/75 rounded">
+11
View File
@@ -93,6 +93,11 @@ html [data-theme='light'] {
line-height: calc(min(var(--ag-line-height, 26px), 26px) - 4px);
}
.vega-ag-grid .ag-row {
border-width: 1px 0;
border-bottom: 1px solid transparent;
}
/* Light variables */
.ag-theme-balham {
--ag-background-color: theme(colors.white);
@@ -116,3 +121,9 @@ html [data-theme='light'] {
--ag-row-hover-color: theme(colors.neutral[800]);
--ag-font-size: 12px;
}
.ag-theme-balham-dark .ag-row.no-hover,
.ag-theme-balham-dark .ag-row.no-hover:hover,
.ag-theme-balham .ag-row.no-hover,
.ag-theme-balham .ag-row.no-hover:hover {
background: var(--ag-background-color);
}
+4 -1
View File
@@ -1,6 +1,6 @@
import { useRef, useMemo, memo, useCallback } from 'react';
import { t } from '@vegaprotocol/i18n';
import { useBottomPlaceholder } from '@vegaprotocol/react-helpers';
import { useBottomPlaceholder } from '@vegaprotocol/datagrid';
import { useDataProvider } from '@vegaprotocol/data-provider';
import { AsyncRenderer } from '@vegaprotocol/ui-toolkit';
import type { AgGridReact } from 'ag-grid-react';
@@ -17,6 +17,7 @@ interface AccountManagerProps {
isReadOnly: boolean;
pinnedAsset?: PinnedAsset;
noBottomPlaceholder?: boolean;
storeKey?: string;
}
export const AccountManager = ({
@@ -27,6 +28,7 @@ export const AccountManager = ({
isReadOnly,
pinnedAsset,
noBottomPlaceholder,
storeKey,
}: AccountManagerProps) => {
const gridRef = useRef<AgGridReact | null>(null);
const variables = useMemo(() => ({ partyId }), [partyId]);
@@ -59,6 +61,7 @@ export const AccountManager = ({
suppressLoadingOverlay
suppressNoRowsOverlay
pinnedAsset={pinnedAsset}
storeKey={storeKey}
{...bottomPlaceholderProps}
/>
<div className="pointer-events-none absolute inset-0">
+2 -2
View File
@@ -100,6 +100,7 @@ export interface AccountTableProps extends AgGridReactProps {
onClickDeposit?: (assetId: string) => void;
isReadOnly: boolean;
pinnedAsset?: PinnedAsset;
storeKey?: string;
}
export const AccountTable = forwardRef<AgGridReact, AccountTableProps>(
@@ -155,7 +156,6 @@ export const AccountTable = forwardRef<AgGridReact, AccountTableProps>(
(data) => data.asset.id !== pinnedAssetId
)}
defaultColDef={{
flex: 1,
resizable: true,
tooltipComponent: TooltipCellComponent,
sortable: true,
@@ -187,7 +187,6 @@ export const AccountTable = forwardRef<AgGridReact, AccountTableProps>(
</ButtonLink>
);
}}
maxWidth={300}
/>
<AgGridColumn
headerName={t('Used')}
@@ -362,6 +361,7 @@ export const AccountTable = forwardRef<AgGridReact, AccountTableProps>(
);
}
}}
flex={1}
/>
}
</AgGrid>
+5
View File
@@ -1,4 +1,5 @@
export * from './lib/ag-grid/ag-grid-lazy';
export * from './lib/ag-grid/use-column-sizes';
export * from './lib/cells/cumulative-vol-cell';
export * from './lib/cells/flash-cell';
@@ -10,6 +11,7 @@ export * from './lib/cells/vol-cell';
export * from './lib/cells/centered-grid-cell';
export * from './lib/cells/market-name-cell';
export * from './lib/cells/order-type-cell';
export * from './lib/cells/size';
export * from './lib/filters/date-range-filter';
export * from './lib/filters/set-filter';
@@ -18,3 +20,6 @@ export * from './lib/cell-class-rules';
export * from './lib/type-helpers';
export * from './lib/cells/grid-progress-bar';
export * from './lib/ag-grid-update';
export * from './lib/use-bottom-placeholder';
@@ -1,7 +1,8 @@
import type { MutableRefObject, RefObject } from 'react';
import type { AgGridReact } from 'ag-grid-react';
import type { IGetRowsParams } from 'ag-grid-community';
type AnyArray = Array<any> | null; // eslint-disable-line @typescript-eslint/no-explicit-any
export const isXOrWasEmpty = (prev?: AnyArray, curr?: AnyArray) =>
Boolean(Number(!!prev?.length) ^ Number(!!curr?.length));
@@ -17,14 +18,3 @@ export const updateGridData = (
}
return !rerender;
};
export const makeGetRows =
(dataRef: MutableRefObject<AnyArray>) =>
() =>
async ({ successCallback, startRow, endRow }: IGetRowsParams) => {
const rowsThisBlock = dataRef.current
? dataRef.current.slice(startRow, endRow)
: [];
const lastRow = dataRef.current ? dataRef.current.length : 0;
successCallback(rowsThisBlock, lastRow);
};
@@ -1,22 +1,30 @@
import type { AgGridReactProps, AgReactUiProps } from 'ag-grid-react';
import { AgGridReact } from 'ag-grid-react';
import { useThemeSwitcher } from '@vegaprotocol/react-helpers';
import { useColumnSizes } from './use-column-sizes';
import classNames from 'classnames';
export const AgGridThemed = ({
style,
gridRef,
storeKey,
...props
}: (AgGridReactProps | AgReactUiProps) & {
style?: React.CSSProperties;
gridRef?: React.ForwardedRef<AgGridReact>;
storeKey?: string;
}) => {
const commonColumnCallbacks = useColumnSizes({
storeKey,
props,
});
const { theme } = useThemeSwitcher();
const defaultProps = {
rowHeight: 22,
headerHeight: 22,
enableCellTextSelection: true,
};
const wrapperClasses = classNames('vega-ag-grid', {
'ag-theme-balham': theme === 'light',
'ag-theme-balham-dark': theme === 'dark',
@@ -24,7 +32,12 @@ export const AgGridThemed = ({
return (
<div className={wrapperClasses} style={style}>
<AgGridReact {...defaultProps} {...props} ref={gridRef} />
<AgGridReact
{...defaultProps}
{...props}
{...commonColumnCallbacks}
ref={gridRef}
/>
</div>
);
};
@@ -4,6 +4,7 @@ import type { AgGridReactProps, AgGridReact } from 'ag-grid-react';
type Props = AgGridReactProps & {
style?: React.CSSProperties;
gridRef?: React.Ref<AgGridReact>;
storeKey?: string;
};
export const AgGridLazyInternal = lazy(() =>
@@ -0,0 +1,117 @@
import type {
Column,
ColumnResizedEvent,
GridSizeChangedEvent,
GridReadyEvent,
} from 'ag-grid-community';
import { renderHook, act, waitFor } from '@testing-library/react';
import { useColumnSizes } from './use-column-sizes';
const mockApis = {
api: {
sizeColumnsToFit: jest.fn(),
},
columnApi: {
setColumnWidths: jest.fn(),
},
};
const mockValueSetter = jest.fn();
const mockStore = {
sizes: { testid: { col1: 100 } },
valueSetter: mockValueSetter,
};
jest.mock('zustand', () => ({
...jest.requireActual('zustand'),
create: () =>
jest.fn(() =>
jest.fn().mockImplementation((creator) => {
return creator(mockStore);
})
),
}));
describe('UseColumnSizes hook', () => {
const storeKey = 'testid';
beforeEach(() => {
jest.clearAllMocks();
});
it('should return proper methods', () => {
const { result } = renderHook(() =>
useColumnSizes({ storeKey, props: {} })
);
expect(Object.keys(result.current)).toHaveLength(3);
expect(result.current).toStrictEqual({
onColumnResized: expect.any(Function),
onGridReady: expect.any(Function),
onGridSizeChanged: expect.any(Function),
});
});
it('onGridSizeChanged should call setSize', async () => {
const { result } = renderHook(() =>
useColumnSizes({ storeKey, props: {} })
);
await act(() => {
result.current.onGridSizeChanged?.({
clientWidth: 1000,
...mockApis,
} as GridSizeChangedEvent);
});
await waitFor(() => {
expect(mockApis.columnApi.setColumnWidths).toHaveBeenCalledWith([
{ key: 'col1', newWidth: 100 },
]);
});
});
it('onColumnResized should fill up store', async () => {
const columns: Column[] = [
{ getColId: () => 'col1', getActualWidth: () => 100 },
{ getColId: () => 'col2', getActualWidth: () => 200 },
] as Column[];
const sizeObj = { col1: 100, col2: 200, clientWidth: 1000 };
const { result } = renderHook(() =>
useColumnSizes({ storeKey, props: {} })
);
await act(() => {
result.current.onGridSizeChanged?.({
clientWidth: 1000,
...mockApis,
} as GridSizeChangedEvent);
});
await act(() => {
result.current.onColumnResized?.({
columns,
finished: true,
source: 'uiColumnDragged',
...mockApis,
} as ColumnResizedEvent);
});
await waitFor(() => {
expect(mockValueSetter).toHaveBeenCalledWith(storeKey, sizeObj);
});
});
it('onGridReady should call setSizes', async () => {
const props = { onGridReady: jest.fn() };
const { result } = renderHook(() => useColumnSizes({ storeKey, props }));
const obTest = { cool: 1, ...mockApis };
await act(() => {
result.current.onGridReady?.(obTest as GridReadyEvent);
});
expect(props.onGridReady).toHaveBeenCalledWith(obTest);
expect(mockApis.api.sizeColumnsToFit).toHaveBeenCalledWith();
});
it('if no storeKey should be transparent', () => {
const { result } = renderHook(() =>
useColumnSizes({ storeKey: '', props: {} })
);
expect(result.current).toStrictEqual({
onColumnResized: undefined,
onGridReady: undefined,
onGridSizeChanged: undefined,
});
});
});
@@ -0,0 +1,143 @@
import { useCallback, useRef } from 'react';
import type {
GridSizeChangedEvent,
GridReadyEvent,
ColumnResizedEvent,
} from 'ag-grid-community';
import type { AgGridReactProps, AgReactUiProps } from 'ag-grid-react';
import { create } from 'zustand';
import { persist } from 'zustand/middleware';
import { immer } from 'zustand/middleware/immer';
const STORAGE_KEY = 'vega_columns_sizes_store';
export const useColumnSizesStore = create<{
sizes: Record<string, Record<string, number>>;
valueSetter: (storeKey: string, value: Record<string, number>) => void;
}>()(
persist(
immer((set) => ({
sizes: {},
valueSetter: (storeKey, value) =>
set((state) => {
state.sizes[storeKey] = {
...(state.sizes[storeKey] || {}),
...value,
};
return state;
}),
})),
{ name: STORAGE_KEY }
)
);
interface UseColumnSizesProps {
props: AgGridReactProps | AgReactUiProps;
storeKey?: string;
}
export const useColumnSizes = ({
storeKey = '',
props,
}: UseColumnSizesProps): {
onColumnResized?: (event: ColumnResizedEvent) => void;
onGridReady?: (event: GridReadyEvent) => void;
onGridSizeChanged?: (event: GridSizeChangedEvent) => void;
} => {
const sizes = useColumnSizesStore((store) => store.sizes[storeKey] || {});
const valueSetter = useColumnSizesStore((store) => store.valueSetter);
const widthRef = useRef(sizes['clientWidth'] || 0);
const {
onColumnResized: parentOnColumnResized,
onGridReady: parentOnGridReady,
onGridSizeChanged: parentOnGridSizeChanged,
} = props;
const recalculateSizes = useCallback((sizes: Record<string, number>) => {
if (
widthRef.current &&
sizes['clientWidth'] &&
widthRef.current !== sizes['clientWidth']
) {
const oldWidth = sizes['clientWidth'];
const ratio = widthRef.current / oldWidth;
return {
...Object.entries(sizes).reduce((agg, [key, value]) => {
agg[key] = value * ratio;
return agg;
}, {} as Record<string, number>),
width: widthRef.current,
} as Record<string, number>;
}
return sizes;
}, []);
const onColumnResized = useCallback(
(event: ColumnResizedEvent) => {
parentOnColumnResized?.(event);
if (
storeKey &&
event.source === 'uiColumnDragged' &&
event.finished &&
widthRef.current
) {
const { columns } = event;
if (columns?.length) {
const sizesObj = columns.reduce((aggr, column) => {
aggr[column.getColId()] = column.getActualWidth();
return aggr;
}, {} as Record<string, number>);
sizesObj['clientWidth'] = widthRef.current;
valueSetter(storeKey, sizesObj);
}
}
},
[valueSetter, storeKey, parentOnColumnResized]
);
const setSizes = useCallback(
(apiEvent: GridReadyEvent | GridSizeChangedEvent) => {
if (!storeKey || !Object.keys(sizes).length || !widthRef.current) {
apiEvent.api.sizeColumnsToFit();
} else {
const recalculatedSizes = recalculateSizes(sizes);
const newSizes = Object.entries(recalculatedSizes).map(
([key, size]) => ({
key,
newWidth: size,
})
);
apiEvent.columnApi.setColumnWidths(newSizes);
}
},
[storeKey, recalculateSizes, sizes]
);
const onGridReady = useCallback(
(event: GridReadyEvent) => {
parentOnGridReady?.(event);
setSizes(event);
},
[setSizes, parentOnGridReady]
);
const onGridSizeChanged = useCallback(
(event: GridSizeChangedEvent) => {
parentOnGridSizeChanged?.(event);
widthRef.current = event.clientWidth;
setSizes(event);
},
[parentOnGridSizeChanged, setSizes]
);
if (storeKey) {
return {
onGridReady,
onGridSizeChanged,
onColumnResized,
};
}
return {
onGridReady: parentOnGridReady,
onGridSizeChanged: parentOnGridSizeChanged,
onColumnResized: parentOnColumnResized,
};
};
+3
View File
@@ -1 +1,4 @@
import '@testing-library/jest-dom';
import ResizeObserver from 'resize-observer-polyfill';
global.ResizeObserver = ResizeObserver;
@@ -1,6 +1,6 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import { VegaWalletContext } from '@vegaprotocol/wallet';
import { render, screen } from '@testing-library/react';
import { act, render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { generateMarket, generateMarketData } from '../../test-helpers';
import { DealTicket } from './deal-ticket';
@@ -40,12 +40,21 @@ describe('DealTicket', () => {
});
it('should display ticket defaults', () => {
render(generateJsx());
const { container } = render(generateJsx());
// Assert defaults are used
expect(
screen.getByTestId(`order-type-${Schema.OrderType.TYPE_MARKET}`)
).toBeInTheDocument();
expect(
screen.getByTestId(`order-type-${Schema.OrderType.TYPE_LIMIT}`)
).toBeInTheDocument();
const oderTypeLimitToggle = container.querySelector(
`[data-testid="order-type-${Schema.OrderType.TYPE_LIMIT}"] input[type="radio"]`
);
expect(oderTypeLimitToggle).toBeChecked();
expect(
screen.queryByTestId('order-side-SIDE_BUY')?.querySelector('input')
).toBeChecked();
@@ -54,8 +63,15 @@ describe('DealTicket', () => {
).not.toBeChecked();
expect(screen.getByTestId('order-size')).toHaveDisplayValue('0');
expect(screen.getByTestId('order-tif')).toHaveValue(
Schema.OrderTimeInForce.TIME_IN_FORCE_IOC
Schema.OrderTimeInForce.TIME_IN_FORCE_GTC
);
});
it('should display last price for market type order', () => {
render(generateJsx());
act(() => {
screen.getByTestId(`order-type-${Schema.OrderType.TYPE_MARKET}`).click();
});
// Assert last price is shown
expect(screen.getByTestId('last-price')).toHaveTextContent(
// eslint-disable-next-line
@@ -206,7 +222,11 @@ describe('DealTicket', () => {
it('handles TIF select box dependent on order type', async () => {
render(generateJsx());
// Only FOK and IOC should be present by default (type market order)
act(() => {
screen.getByTestId(`order-type-${Schema.OrderType.TYPE_MARKET}`).click();
});
// Only FOK and IOC should be present for type market order
expect(
Array.from(screen.getByTestId('order-tif').children).map(
(o) => o.textContent
@@ -20,8 +20,8 @@ interface TypeSelectorProps {
}
const toggles = [
{ label: t('Market'), value: Schema.OrderType.TYPE_MARKET },
{ label: t('Limit'), value: Schema.OrderType.TYPE_LIMIT },
{ label: t('Market'), value: Schema.OrderType.TYPE_MARKET },
];
export const TypeSelector = ({
+3 -1
View File
@@ -26,9 +26,10 @@ export const DepositsTable = forwardRef<
<AgGrid
ref={ref}
overlayNoRowsTemplate={t('No deposits')}
defaultColDef={{ flex: 1, resizable: true }}
defaultColDef={{ resizable: true }}
style={{ width: '100%', height: '100%' }}
suppressCellFocus={true}
storeKey="depositTable"
{...props}
>
<AgGridColumn headerName="Asset" field="asset.symbol" />
@@ -80,6 +81,7 @@ export const DepositsTable = forwardRef<
</EtherscanLink>
);
}}
flex={1}
/>
</AgGrid>
);
+12 -15
View File
@@ -34,25 +34,22 @@ fragment FillEdge on TradeEdge {
cursor
}
query Fills($partyId: ID!, $marketId: ID, $pagination: Pagination) {
party(id: $partyId) {
id
tradesConnection(marketId: $marketId, pagination: $pagination) {
edges {
...FillEdge
}
pageInfo {
startCursor
endCursor
hasNextPage
hasPreviousPage
}
query Fills($filter: TradesFilter, $pagination: Pagination) {
trades(filter: $filter, pagination: $pagination) {
edges {
...FillEdge
}
pageInfo {
startCursor
endCursor
hasNextPage
hasPreviousPage
}
}
}
subscription FillsEvent($partyId: ID!) {
trades(partyId: $partyId) {
subscription FillsEvent($filter: TradesSubscriptionFilter!) {
tradesStream(filter: $filter) {
id
marketId
buyOrder
+19 -24
View File
@@ -8,20 +8,19 @@ export type FillFieldsFragment = { __typename?: 'Trade', id: string, createdAt:
export type FillEdgeFragment = { __typename?: 'TradeEdge', cursor: string, node: { __typename?: 'Trade', id: string, createdAt: any, price: string, size: string, buyOrder: string, sellOrder: string, aggressor: Types.Side, market: { __typename?: 'Market', id: string }, buyer: { __typename?: 'Party', id: string }, seller: { __typename?: 'Party', id: string }, buyerFee: { __typename?: 'TradeFee', makerFee: string, infrastructureFee: string, liquidityFee: string }, sellerFee: { __typename?: 'TradeFee', makerFee: string, infrastructureFee: string, liquidityFee: string } } };
export type FillsQueryVariables = Types.Exact<{
partyId: Types.Scalars['ID'];
marketId?: Types.InputMaybe<Types.Scalars['ID']>;
filter?: Types.InputMaybe<Types.TradesFilter>;
pagination?: Types.InputMaybe<Types.Pagination>;
}>;
export type FillsQuery = { __typename?: 'Query', party?: { __typename?: 'Party', id: string, tradesConnection?: { __typename?: 'TradeConnection', edges: Array<{ __typename?: 'TradeEdge', cursor: string, node: { __typename?: 'Trade', id: string, createdAt: any, price: string, size: string, buyOrder: string, sellOrder: string, aggressor: Types.Side, market: { __typename?: 'Market', id: string }, buyer: { __typename?: 'Party', id: string }, seller: { __typename?: 'Party', id: string }, buyerFee: { __typename?: 'TradeFee', makerFee: string, infrastructureFee: string, liquidityFee: string }, sellerFee: { __typename?: 'TradeFee', makerFee: string, infrastructureFee: string, liquidityFee: string } } }>, pageInfo: { __typename?: 'PageInfo', startCursor: string, endCursor: string, hasNextPage: boolean, hasPreviousPage: boolean } } | null } | null };
export type FillsQuery = { __typename?: 'Query', trades?: { __typename?: 'TradeConnection', edges: Array<{ __typename?: 'TradeEdge', cursor: string, node: { __typename?: 'Trade', id: string, createdAt: any, price: string, size: string, buyOrder: string, sellOrder: string, aggressor: Types.Side, market: { __typename?: 'Market', id: string }, buyer: { __typename?: 'Party', id: string }, seller: { __typename?: 'Party', id: string }, buyerFee: { __typename?: 'TradeFee', makerFee: string, infrastructureFee: string, liquidityFee: string }, sellerFee: { __typename?: 'TradeFee', makerFee: string, infrastructureFee: string, liquidityFee: string } } }>, pageInfo: { __typename?: 'PageInfo', startCursor: string, endCursor: string, hasNextPage: boolean, hasPreviousPage: boolean } } | null };
export type FillsEventSubscriptionVariables = Types.Exact<{
partyId: Types.Scalars['ID'];
filter: Types.TradesSubscriptionFilter;
}>;
export type FillsEventSubscription = { __typename?: 'Subscription', trades?: Array<{ __typename?: 'TradeUpdate', id: string, marketId: string, buyOrder: string, sellOrder: string, buyerId: string, sellerId: string, aggressor: Types.Side, price: string, size: string, createdAt: any, type: Types.TradeType, buyerFee: { __typename?: 'TradeFee', makerFee: string, infrastructureFee: string, liquidityFee: string }, sellerFee: { __typename?: 'TradeFee', makerFee: string, infrastructureFee: string, liquidityFee: string } }> | null };
export type FillsEventSubscription = { __typename?: 'Subscription', tradesStream?: Array<{ __typename?: 'TradeUpdate', id: string, marketId: string, buyOrder: string, sellOrder: string, buyerId: string, sellerId: string, aggressor: Types.Side, price: string, size: string, createdAt: any, type: Types.TradeType, buyerFee: { __typename?: 'TradeFee', makerFee: string, infrastructureFee: string, liquidityFee: string }, sellerFee: { __typename?: 'TradeFee', makerFee: string, infrastructureFee: string, liquidityFee: string } }> | null };
export const FillFieldsFragmentDoc = gql`
fragment FillFields on Trade {
@@ -62,19 +61,16 @@ export const FillEdgeFragmentDoc = gql`
}
${FillFieldsFragmentDoc}`;
export const FillsDocument = gql`
query Fills($partyId: ID!, $marketId: ID, $pagination: Pagination) {
party(id: $partyId) {
id
tradesConnection(marketId: $marketId, pagination: $pagination) {
edges {
...FillEdge
}
pageInfo {
startCursor
endCursor
hasNextPage
hasPreviousPage
}
query Fills($filter: TradesFilter, $pagination: Pagination) {
trades(filter: $filter, pagination: $pagination) {
edges {
...FillEdge
}
pageInfo {
startCursor
endCursor
hasNextPage
hasPreviousPage
}
}
}
@@ -92,13 +88,12 @@ export const FillsDocument = gql`
* @example
* const { data, loading, error } = useFillsQuery({
* variables: {
* partyId: // value for 'partyId'
* marketId: // value for 'marketId'
* filter: // value for 'filter'
* pagination: // value for 'pagination'
* },
* });
*/
export function useFillsQuery(baseOptions: Apollo.QueryHookOptions<FillsQuery, FillsQueryVariables>) {
export function useFillsQuery(baseOptions?: Apollo.QueryHookOptions<FillsQuery, FillsQueryVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useQuery<FillsQuery, FillsQueryVariables>(FillsDocument, options);
}
@@ -110,8 +105,8 @@ export type FillsQueryHookResult = ReturnType<typeof useFillsQuery>;
export type FillsLazyQueryHookResult = ReturnType<typeof useFillsLazyQuery>;
export type FillsQueryResult = Apollo.QueryResult<FillsQuery, FillsQueryVariables>;
export const FillsEventDocument = gql`
subscription FillsEvent($partyId: ID!) {
trades(partyId: $partyId) {
subscription FillsEvent($filter: TradesSubscriptionFilter!) {
tradesStream(filter: $filter) {
id
marketId
buyOrder
@@ -149,7 +144,7 @@ export const FillsEventDocument = gql`
* @example
* const { data, loading, error } = useFillsEventSubscription({
* variables: {
* partyId: // value for 'partyId'
* filter: // value for 'filter'
* },
* });
*/
+3
View File
@@ -6,9 +6,11 @@ import { FillsManager } from './fills-manager';
export const FillsContainer = ({
marketId,
onMarketClick,
storeKey,
}: {
marketId?: string;
onMarketClick?: (marketId: string, metaKey?: boolean) => void;
storeKey?: string;
}) => {
const { pubKey } = useVegaWallet();
@@ -25,6 +27,7 @@ export const FillsContainer = ({
partyId={pubKey}
marketId={marketId}
onMarketClick={onMarketClick}
storeKey={storeKey}
/>
);
};
+8 -5
View File
@@ -22,7 +22,7 @@ import type {
const update = (
data: FillEdgeFragment[] | null,
delta: FillsEventSubscription['trades']
delta: FillsEventSubscription['tradesStream']
) => {
return produce(data, (draft) => {
orderBy(delta, 'createdAt').forEach((node) => {
@@ -36,7 +36,10 @@ const update = (
}
} else {
const firstNode = draft[0]?.node;
if (firstNode && node.createdAt >= firstNode.createdAt) {
if (
(firstNode && node.createdAt >= firstNode.createdAt) ||
!firstNode
) {
const { buyerId, sellerId, marketId, ...trade } = node;
draft.unshift({
node: {
@@ -65,13 +68,13 @@ export type Trade = Omit<FillFieldsFragment, 'market'> & {
export type TradeEdge = Edge<Trade>;
const getData = (responseData: FillsQuery | null): FillEdgeFragment[] =>
responseData?.party?.tradesConnection?.edges || [];
responseData?.trades?.edges || [];
const getPageInfo = (responseData: FillsQuery | null): PageInfo | null =>
responseData?.party?.tradesConnection?.pageInfo || null;
responseData?.trades?.pageInfo || null;
const getDelta = (subscriptionData: FillsEventSubscription) =>
subscriptionData.trades || [];
subscriptionData.tradesStream || [];
export const fillsProvider = makeDataProvider<
Parameters<typeof getData>['0'],
+4 -1
View File
@@ -6,18 +6,20 @@ import { FillsTable } from './fills-table';
import type { BodyScrollEvent, BodyScrollEndEvent } from 'ag-grid-community';
import { useFillsList } from './use-fills-list';
import type { Trade } from './fills-data-provider';
import { useBottomPlaceholder } from '@vegaprotocol/react-helpers';
import { useBottomPlaceholder } from '@vegaprotocol/datagrid';
interface FillsManagerProps {
partyId: string;
marketId?: string;
onMarketClick?: (marketId: string, metaKey?: boolean) => void;
storeKey?: string;
}
export const FillsManager = ({
partyId,
marketId,
onMarketClick,
storeKey,
}: FillsManagerProps) => {
const gridRef = useRef<AgGridReact | null>(null);
const scrolledToTop = useRef(true);
@@ -83,6 +85,7 @@ export const FillsManager = ({
fullWidthCellRenderer={fullWidthCellRenderer}
rowClassRules={rowClassRules}
getRowHeight={getRowHeight}
storeKey={storeKey}
/>
<div className="pointer-events-none absolute inset-0">
<AsyncRenderer
+2 -1
View File
@@ -34,6 +34,7 @@ export type Role = typeof TAKER | typeof MAKER | '-';
export type Props = (AgGridReactProps | AgReactUiProps) & {
partyId: string;
onMarketClick?: (marketId: string, metaKey?: boolean) => void;
storeKey?: string;
};
export const FillsTable = forwardRef<AgGridReact, Props>(
@@ -42,7 +43,7 @@ export const FillsTable = forwardRef<AgGridReact, Props>(
<AgGrid
ref={ref}
overlayNoRowsTemplate={t('No fills')}
defaultColDef={{ flex: 1, resizable: true }}
defaultColDef={{ resizable: true }}
style={{ width: '100%', height: '100%' }}
getRowId={({ data }) => data?.id}
tooltipShowDelay={0}
+14 -18
View File
@@ -12,24 +12,20 @@ export const fillsQuery = (
vegaPublicKey?: string
): FillsQuery => {
const defaultResult: FillsQuery = {
party: {
id: vegaPublicKey || 'vega-0',
tradesConnection: {
__typename: 'TradeConnection',
edges: fills(vegaPublicKey).map((node) => ({
__typename: 'TradeEdge',
cursor: '3',
node,
})),
pageInfo: {
__typename: 'PageInfo',
startCursor: '1',
endCursor: '2',
hasNextPage: false,
hasPreviousPage: false,
},
trades: {
__typename: 'TradeConnection',
edges: fills(vegaPublicKey).map((node) => ({
__typename: 'TradeEdge',
cursor: '3',
node,
})),
pageInfo: {
__typename: 'PageInfo',
startCursor: '1',
endCursor: '2',
hasNextPage: false,
hasPreviousPage: false,
},
__typename: 'Party',
},
};
@@ -169,7 +165,7 @@ export const fillsEventSubscription = (
): FillsEventSubscription => {
const defaultResult: FillsEventSubscription = {
__typename: 'Subscription',
trades: [
tradesStream: [
{
__typename: 'TradeUpdate',
id: '0',
+10 -2
View File
@@ -2,7 +2,8 @@ import type { RefObject } from 'react';
import type { AgGridReact } from 'ag-grid-react';
import { useCallback, useRef } from 'react';
import { makeInfiniteScrollGetRows } from '@vegaprotocol/data-provider';
import { updateGridData } from '@vegaprotocol/react-helpers';
import type * as Types from '@vegaprotocol/types';
import { updateGridData } from '@vegaprotocol/datagrid';
import { useDataProvider } from '@vegaprotocol/data-provider';
import type { Trade, TradeEdge } from './fills-data-provider';
import { fillsWithMarketProvider } from './fills-data-provider';
@@ -89,11 +90,18 @@ export const useFillsList = ({
[gridRef]
);
const filter: Types.TradesFilter & Types.TradesSubscriptionFilter = {
partyIds: [partyId],
};
if (marketId) {
filter.marketIds = [marketId];
}
const { data, error, loading, load, totalCount, reload } = useDataProvider({
dataProvider: fillsWithMarketProvider,
update,
insert,
variables: { partyId, marketId: marketId || '' },
variables: { filter },
});
totalCountRef.current = totalCount;
+3
View File
@@ -1 +1,4 @@
import '@testing-library/jest-dom';
import ResizeObserver from 'resize-observer-polyfill';
global.ResizeObserver = ResizeObserver;
@@ -3,7 +3,7 @@ import { assetsProvider } from '@vegaprotocol/assets';
import type { Market } from '@vegaprotocol/market-list';
import { marketsProvider } from '@vegaprotocol/market-list';
import { makeInfiniteScrollGetRows } from '@vegaprotocol/data-provider';
import { updateGridData } from '@vegaprotocol/react-helpers';
import { updateGridData } from '@vegaprotocol/datagrid';
import {
makeDataProvider,
makeDerivedDataProvider,
+2 -1
View File
@@ -53,7 +53,6 @@ export const LedgerTable = forwardRef<AgGridReact, LedgerEntryProps>(
ref={ref}
tooltipShowDelay={500}
defaultColDef={{
flex: 1,
resizable: true,
sortable: true,
tooltipComponent: TransferTooltipCellComponent,
@@ -62,6 +61,7 @@ export const LedgerTable = forwardRef<AgGridReact, LedgerEntryProps>(
buttons: ['reset'],
},
}}
storeKey="ledgerTable"
suppressLoadingOverlay
suppressNoRowsOverlay
{...props}
@@ -203,6 +203,7 @@ export const LedgerTable = forwardRef<AgGridReact, LedgerEntryProps>(
}
filterParams={dateRangeFilterParams}
filter={DateRangeFilter}
flex={1}
/>
</AgGrid>
);
+1 -1
View File
@@ -59,12 +59,12 @@ export const LiquidityTable = forwardRef<AgGridReact, LiquidityTableProps>(
ref={ref}
tooltipShowDelay={500}
defaultColDef={{
flex: 1,
resizable: true,
minWidth: 100,
tooltipComponent: TooltipCellComponent,
sortable: true,
}}
storeKey="liquidityProvisionTable"
{...props}
>
<AgGridColumn
+3
View File
@@ -1 +1,4 @@
import '@testing-library/jest-dom';
import ResizeObserver from 'resize-observer-polyfill';
global.ResizeObserver = ResizeObserver;
@@ -37,7 +37,6 @@ export const MarketListTable = forwardRef<
getRowId={getRowId}
ref={ref}
defaultColDef={{
flex: 1,
resizable: true,
sortable: true,
filter: true,
@@ -45,6 +44,7 @@ export const MarketListTable = forwardRef<
}}
suppressCellFocus
components={{ PriceFlashCell, MarketNameCell }}
storeKey="allMarkets"
{...props}
>
<AgGridColumn
@@ -198,7 +198,7 @@ export const MarketListTable = forwardRef<
);
}}
/>
<AgGridColumn headerName={t('Market ID')} field="id" />
<AgGridColumn headerName={t('Market ID')} field="id" flex={1} />
</AgGrid>
);
});
@@ -10,6 +10,7 @@ export interface OrderListContainerProps {
onOrderTypeClick?: (marketId: string, metaKey?: boolean) => void;
enforceBottomPlaceholder?: boolean;
filter?: Filter;
storeKey?: string;
}
export const OrderListContainer = ({
@@ -18,6 +19,7 @@ export const OrderListContainer = ({
onOrderTypeClick,
enforceBottomPlaceholder,
filter,
storeKey,
}: OrderListContainerProps) => {
const { pubKey, isReadOnly } = useVegaWallet();
@@ -34,6 +36,7 @@ export const OrderListContainer = ({
onOrderTypeClick={onOrderTypeClick}
isReadOnly={isReadOnly}
enforceBottomPlaceholder={enforceBottomPlaceholder}
storeKey={storeKey}
/>
);
};
@@ -7,7 +7,7 @@ import type { GridReadyEvent, FilterChangedEvent } from 'ag-grid-community';
import { OrderListTable } from '../order-list/order-list';
import { useHasAmendableOrder } from '../../order-hooks/use-has-amendable-order';
import { useBottomPlaceholder } from '@vegaprotocol/react-helpers';
import { useBottomPlaceholder } from '@vegaprotocol/datagrid';
import { useDataProvider } from '@vegaprotocol/data-provider';
import { ordersWithMarketProvider } from '../order-data-provider/order-data-provider';
import {
@@ -45,6 +45,7 @@ export interface OrderListManagerProps {
isReadOnly: boolean;
enforceBottomPlaceholder?: boolean;
filter?: Filter;
storeKey?: string;
}
const CancelAllOrdersButton = ({ onClick }: { onClick: () => void }) => (
@@ -68,6 +69,7 @@ export const OrderListManager = ({
isReadOnly,
enforceBottomPlaceholder,
filter,
storeKey,
}: OrderListManagerProps) => {
const gridRef = useRef<AgGridReact | null>(null);
const [hasData, setHasData] = useState(false);
@@ -152,8 +154,10 @@ export const OrderListManager = ({
onFilterChanged={onFilterChanged}
isReadOnly={isReadOnly}
blockLoadDebounceMillis={100}
storeKey={storeKey}
suppressLoadingOverlay
suppressNoRowsOverlay
suppressAutoSize
{...bottomPlaceholderProps}
/>
<div className="pointer-events-none absolute inset-0">
@@ -6,7 +6,7 @@ import {
validateAmount,
} from '@vegaprotocol/utils';
import { t } from '@vegaprotocol/i18n';
import { Size } from '@vegaprotocol/react-helpers';
import { Size } from '@vegaprotocol/datagrid';
import * as Schema from '@vegaprotocol/types';
import {
FormGroup,
@@ -8,6 +8,7 @@ import * as Schema from '@vegaprotocol/types';
import { ButtonLink } from '@vegaprotocol/ui-toolkit';
import { AgGridColumn } from 'ag-grid-react';
import BigNumber from 'bignumber.js';
import type { ForwardedRef } from 'react';
import { memo, forwardRef } from 'react';
import {
AgGridLazy as AgGrid,
@@ -25,19 +26,22 @@ import type {
} from '@vegaprotocol/datagrid';
import type { AgGridReact } from 'ag-grid-react';
import type { Order } from '../order-data-provider';
import * as React from 'react';
type OrderListProps = TypedDataAgGrid<Order> & { marketId?: string };
export type OrderListTableProps = OrderListProps & {
export type OrderListTableProps = TypedDataAgGrid<Order> & {
marketId?: string;
cancel: (order: Order) => void;
setEditOrder: (order: Order) => void;
onMarketClick?: (marketId: string, metaKey?: boolean) => void;
onOrderTypeClick?: (marketId: string, metaKey?: boolean) => void;
readonlyStatusFilter?: boolean;
isReadOnly: boolean;
storeKey?: string;
};
export const OrderListTable = memo(
export const OrderListTable = memo<
OrderListTableProps & { ref?: ForwardedRef<AgGridReact> }
>(
forwardRef<AgGridReact, OrderListTableProps>(
(
{
@@ -54,7 +58,6 @@ export const OrderListTable = memo(
<AgGrid
ref={ref}
defaultColDef={{
flex: 1,
resizable: true,
sortable: true,
filterParams: { buttons: ['reset'] },
@@ -163,7 +166,6 @@ export const OrderListTable = memo(
valueFormatter={({
data,
value,
node,
}: VegaValueFormatterParams<Order, 'remaining'>) => {
if (!data) {
return undefined;
@@ -189,7 +191,6 @@ export const OrderListTable = memo(
valueFormatter={({
value,
data,
node,
}: VegaValueFormatterParams<Order, 'price'>) => {
if (!data) {
return undefined;
@@ -240,7 +241,6 @@ export const OrderListTable = memo(
field="createdAt"
filter={DateRangeFilter}
cellRenderer={({
data,
value,
}: VegaICellRendererParams<Order, 'createdAt'>) => {
return (
@@ -291,6 +291,7 @@ export const OrderListTable = memo(
) : null;
}}
sortable={false}
flex={1}
/>
</AgGrid>
);
@@ -109,9 +109,9 @@ export const useOrder = (marketId: string) => {
export const getDefaultOrder = (marketId: string): OrderObj => ({
marketId,
type: OrderType.TYPE_MARKET,
type: OrderType.TYPE_LIMIT,
side: Side.SIDE_BUY,
timeInForce: OrderTimeInForce.TIME_IN_FORCE_IOC,
timeInForce: OrderTimeInForce.TIME_IN_FORCE_GTC,
size: '0',
price: '0',
expiresAt: undefined,
+3
View File
@@ -1 +1,4 @@
import '@testing-library/jest-dom';
import ResizeObserver from 'resize-observer-polyfill';
global.ResizeObserver = ResizeObserver;
@@ -6,9 +6,11 @@ import { PositionsManager } from './positions-manager';
export const PositionsContainer = ({
onMarketClick,
noBottomPlaceholder,
storeKey,
}: {
onMarketClick?: (marketId: string) => void;
noBottomPlaceholder?: boolean;
storeKey?: string;
}) => {
const { pubKey, isReadOnly } = useVegaWallet();
@@ -25,6 +27,7 @@ export const PositionsContainer = ({
onMarketClick={onMarketClick}
isReadOnly={isReadOnly}
noBottomPlaceholder={noBottomPlaceholder}
storeKey={storeKey}
/>
);
};
+4 -1
View File
@@ -6,13 +6,14 @@ import type { AgGridReact } from 'ag-grid-react';
import * as Schema from '@vegaprotocol/types';
import { useVegaTransactionStore } from '@vegaprotocol/wallet';
import { t } from '@vegaprotocol/i18n';
import { useBottomPlaceholder } from '@vegaprotocol/react-helpers';
import { useBottomPlaceholder } from '@vegaprotocol/datagrid';
interface PositionsManagerProps {
partyId: string;
onMarketClick?: (marketId: string) => void;
isReadOnly: boolean;
noBottomPlaceholder?: boolean;
storeKey?: string;
}
export const PositionsManager = ({
@@ -20,6 +21,7 @@ export const PositionsManager = ({
onMarketClick,
isReadOnly,
noBottomPlaceholder,
storeKey,
}: PositionsManagerProps) => {
const gridRef = useRef<AgGridReact | null>(null);
const { data, error, loading, reload } = usePositionsData(partyId, gridRef);
@@ -82,6 +84,7 @@ export const PositionsManager = ({
onFilterChanged={updateRowCount}
onRowDataUpdated={updateRowCount}
{...bottomPlaceholderProps}
storeKey={storeKey}
/>
<div className="pointer-events-none absolute inset-0">
<AsyncRenderer
+2 -1
View File
@@ -46,6 +46,7 @@ interface Props extends TypedDataAgGrid<Position> {
onMarketClick?: (id: string, metaKey?: boolean) => void;
style?: CSSProperties;
isReadOnly: boolean;
storeKey?: string;
}
export interface AmountCellProps {
@@ -89,7 +90,6 @@ export const PositionsTable = forwardRef<AgGridReact, Props>(
ref={ref}
tooltipShowDelay={500}
defaultColDef={{
flex: 1,
resizable: true,
sortable: true,
filter: true,
@@ -383,6 +383,7 @@ export const PositionsTable = forwardRef<AgGridReact, Props>(
) : null
}
minWidth={80}
flex={1}
/>
) : null}
</AgGrid>
@@ -4,7 +4,7 @@ import type { AgGridReact } from 'ag-grid-react';
import type { Position } from './positions-data-providers';
import { positionsMetricsProvider } from './positions-data-providers';
import type { PositionsQueryVariables } from './__generated__/Positions';
import { updateGridData } from '@vegaprotocol/react-helpers';
import { updateGridData } from '@vegaprotocol/datagrid';
import { useDataProvider } from '@vegaprotocol/data-provider';
import type { GetRowsParams } from '@vegaprotocol/datagrid';
import isEqual from 'lodash/isEqual';
@@ -18,7 +18,7 @@ export const PROPOSAL_STATES_TO_TOAST = [
ProposalState.STATE_OPEN,
ProposalState.STATE_PASSED,
];
const CLOSE_AFTER = 5000;
const CLOSE_AFTER = 0;
type Proposal = UpdateNetworkParameterProposalFragment;
const UpdateNetworkParameterToastContent = ({
@@ -21,9 +21,6 @@ export const getNewMarketProposals = (data: ProposalListFieldsFragment[]) =>
export const ProposalsList = () => {
const gridRef = useRef<AgGridReact | null>(null);
const [dataCount, setDataCount] = useState(0);
const handleOnGridReady = useCallback(() => {
gridRef.current?.api?.sizeColumnsToFit();
}, [gridRef]);
const { data, loading, error, reload } = useDataProvider({
dataProvider: proposalsDataProvider,
variables: {
@@ -47,10 +44,10 @@ export const ProposalsList = () => {
columnDefs={columnDefs}
rowData={filteredData}
defaultColDef={defaultColDef}
onGridReady={handleOnGridReady}
suppressLoadingOverlay
suppressNoRowsOverlay
onFilterChanged={onFilterChanged}
storeKey="proposedMarkets"
/>
<div className="pointer-events-none absolute inset-0">
<AsyncRenderer
@@ -132,6 +132,7 @@ export const useColumnDefs = () => {
'terms.enactmentDatetime'
>) => (value ? getDateTimeFormat().format(new Date(value)) : '-'),
filter: DateRangeFilter,
flex: 1,
},
];
}, [VEGA_TOKEN_URL, requiredMajorityPercentage]);
@@ -139,7 +140,6 @@ export const useColumnDefs = () => {
return {
sortable: true,
cellClass: cellCss,
flex: 1,
resizable: true,
filter: true,
filterParams: { buttons: ['reset'] },

Some files were not shown because too many files have changed in this diff Show More