Compare commits
21
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
33c2be2e59 | ||
|
|
d82c045a99 | ||
|
|
c8bc2ac9c6 | ||
|
|
4dcb183c71 | ||
|
|
bfb778b58b | ||
|
|
231fc7ddd3 | ||
|
|
8b94a75ba4 | ||
|
|
08c57b6759 | ||
|
|
bb2184498f | ||
|
|
8b07ce3024 | ||
|
|
8a110584dd | ||
|
|
b82615a3d8 | ||
|
|
f178b85846 | ||
|
|
0796f2b31f | ||
|
|
67be224138 | ||
|
|
51c426ef4b | ||
|
|
fa28d31ef3 | ||
|
|
201a586b05 | ||
|
|
7d96d9bcd1 | ||
|
|
345be81142 | ||
|
|
2221e9faca |
@@ -1,5 +1,8 @@
|
||||
#!/bin/sh
|
||||
. "$(dirname "$0")/_/husky.sh"
|
||||
|
||||
# Auto-format all files
|
||||
yarn nx format:write
|
||||
|
||||
# Lint all staged files
|
||||
yarn lint-staged
|
||||
|
||||
+3
-3
@@ -1,8 +1,8 @@
|
||||
#!/bin/sh
|
||||
. "$(dirname "$0")/_/husky.sh"
|
||||
|
||||
# Lint all staged files
|
||||
yarn nx format:check
|
||||
# Lint all staged files - this brings more value as pre-commit
|
||||
# yarn nx format:check
|
||||
|
||||
# Test all projects with changes
|
||||
yarn nx affected -t test --exclude trading
|
||||
# yarn nx affected -t test --exclude trading
|
||||
|
||||
@@ -12,7 +12,8 @@ import { TendermintWebsocketProvider } from './contexts/websocket/tendermint-web
|
||||
import { Loader, Splash } from '@vegaprotocol/ui-toolkit';
|
||||
import { DEFAULT_CACHE_CONFIG } from '@vegaprotocol/apollo-client';
|
||||
import { RouterProvider } from 'react-router-dom';
|
||||
import { router } from './routes/router-config';
|
||||
import { useRouterConfig } from './routes/router-config';
|
||||
import { createBrowserRouter } from 'react-router-dom';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { Suspense } from 'react';
|
||||
|
||||
@@ -38,7 +39,10 @@ function App() {
|
||||
}
|
||||
>
|
||||
<Suspense fallback={splashLoading}>
|
||||
<RouterProvider router={router} fallbackElement={splashLoading} />
|
||||
<RouterProvider
|
||||
router={createBrowserRouter(useRouterConfig())}
|
||||
fallbackElement={splashLoading}
|
||||
/>
|
||||
</Suspense>
|
||||
</NodeGuard>
|
||||
<NodeSwitcherDialog
|
||||
|
||||
@@ -24,7 +24,7 @@ export const AssetsTable = ({ data }: AssetsTableProps) => {
|
||||
const navigate = useNavigate();
|
||||
const ref = useRef<AgGridReact>(null);
|
||||
const showColumnsOnDesktop = () => {
|
||||
ref.current?.columnApi.setColumnsVisible(
|
||||
ref.current?.api.setColumnsVisible(
|
||||
['id', 'type', 'status'],
|
||||
window.innerWidth > BREAKPOINT_MD
|
||||
);
|
||||
|
||||
@@ -14,7 +14,7 @@ import { Routes } from '../../routes/route-names';
|
||||
import { NetworkSwitcher } from '@vegaprotocol/environment';
|
||||
import type { Navigable } from '../../routes/router-config';
|
||||
import { isNavigable } from '../../routes/router-config';
|
||||
import { routerConfig } from '../../routes/router-config';
|
||||
import { useRouterConfig } from '../../routes/router-config';
|
||||
import { useMemo } from 'react';
|
||||
import compact from 'lodash/compact';
|
||||
import { Search } from '../search';
|
||||
@@ -26,6 +26,7 @@ const routeToNavigationItem = (r: Navigable) => (
|
||||
);
|
||||
|
||||
export const Header = () => {
|
||||
const routerConfig = useRouterConfig();
|
||||
const isHome = Boolean(useMatch(Routes.HOME));
|
||||
const pages = routerConfig[0].children || [];
|
||||
const mainItems = compact(
|
||||
|
||||
@@ -29,7 +29,7 @@ export const MarketsTable = ({ data }: MarketsTableProps) => {
|
||||
const gridRef = useRef<AgGridReact>(null);
|
||||
useLayoutEffect(() => {
|
||||
const showColumnsOnDesktop = () => {
|
||||
gridRef.current?.columnApi.setColumnsVisible(
|
||||
gridRef.current?.api.setColumnsVisible(
|
||||
['id', 'state', 'asset'],
|
||||
window.innerWidth > BREAKPOINT_MD
|
||||
);
|
||||
|
||||
@@ -44,11 +44,11 @@ export const ProposalsTable = ({ data }: ProposalsTableProps) => {
|
||||
const gridRef = useRef<AgGridReact>(null);
|
||||
useLayoutEffect(() => {
|
||||
const showColumnsOnDesktop = () => {
|
||||
gridRef.current?.columnApi.setColumnsVisible(
|
||||
gridRef.current?.api.setColumnsVisible(
|
||||
['voting', 'cDate', 'eDate', 'type'],
|
||||
window.innerWidth > BREAKPOINT_MD
|
||||
);
|
||||
gridRef.current?.columnApi.setColumnWidth(
|
||||
gridRef.current?.api.setColumnWidth(
|
||||
'actions',
|
||||
window.innerWidth > BREAKPOINT_MD ? 221 : 80
|
||||
);
|
||||
|
||||
@@ -18,7 +18,6 @@ import { t } from '@vegaprotocol/i18n';
|
||||
import { Routes } from './route-names';
|
||||
import { NetworkParameters } from './network-parameters';
|
||||
import type { Params, RouteObject } from 'react-router-dom';
|
||||
import { createBrowserRouter } from 'react-router-dom';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { MarketPage, MarketsPage } from './markets';
|
||||
import type { ReactNode } from 'react';
|
||||
@@ -29,7 +28,7 @@ import { truncateMiddle } from '@vegaprotocol/ui-toolkit';
|
||||
import { remove0x } from '@vegaprotocol/utils';
|
||||
import { PartyAccountsByAsset } from './parties/id/accounts';
|
||||
import { Disclaimer } from './pages/disclaimer';
|
||||
import { FLAGS } from '@vegaprotocol/environment';
|
||||
import { useFeatureFlags } from '@vegaprotocol/environment';
|
||||
import RestrictedPage from './restricted';
|
||||
|
||||
export type Navigable = {
|
||||
@@ -60,311 +59,315 @@ type Route = RouteItem & {
|
||||
children?: RouteItem[];
|
||||
};
|
||||
|
||||
const partiesRoutes: Route[] = FLAGS.EXPLORER_PARTIES
|
||||
? [
|
||||
{
|
||||
path: Routes.PARTIES,
|
||||
element: <Party />,
|
||||
handle: {
|
||||
name: t('Parties'),
|
||||
text: t('Parties'),
|
||||
breadcrumb: () => <Link to={Routes.PARTIES}>{t('Parties')}</Link>,
|
||||
},
|
||||
children: [
|
||||
{
|
||||
index: true,
|
||||
element: <Parties />,
|
||||
},
|
||||
{
|
||||
path: ':party',
|
||||
element: <Party />,
|
||||
export const useRouterConfig = () => {
|
||||
const featureFlags = useFeatureFlags((state) => state.flags);
|
||||
|
||||
children: [
|
||||
{
|
||||
index: true,
|
||||
element: <PartySingle />,
|
||||
handle: {
|
||||
breadcrumb: (params: Params<string>) => (
|
||||
<Link to={linkTo(Routes.PARTIES, params.party)}>
|
||||
{truncateMiddle(params.party as string)}
|
||||
</Link>
|
||||
),
|
||||
const partiesRoutes: Route[] = featureFlags.EXPLORER_PARTIES
|
||||
? [
|
||||
{
|
||||
path: Routes.PARTIES,
|
||||
element: <Party />,
|
||||
handle: {
|
||||
name: t('Parties'),
|
||||
text: t('Parties'),
|
||||
breadcrumb: () => <Link to={Routes.PARTIES}>{t('Parties')}</Link>,
|
||||
},
|
||||
children: [
|
||||
{
|
||||
index: true,
|
||||
element: <Parties />,
|
||||
},
|
||||
{
|
||||
path: ':party',
|
||||
element: <Party />,
|
||||
|
||||
children: [
|
||||
{
|
||||
index: true,
|
||||
element: <PartySingle />,
|
||||
handle: {
|
||||
breadcrumb: (params: Params<string>) => (
|
||||
<Link to={linkTo(Routes.PARTIES, params.party)}>
|
||||
{truncateMiddle(params.party as string)}
|
||||
</Link>
|
||||
),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'assets',
|
||||
element: <Party />,
|
||||
handle: {
|
||||
breadcrumb: (params: Params<string>) => (
|
||||
<Link to={linkTo(Routes.PARTIES, params.party)}>
|
||||
{truncateMiddle(params.party as string)}
|
||||
</Link>
|
||||
),
|
||||
},
|
||||
children: [
|
||||
{
|
||||
index: true,
|
||||
element: <PartyAccountsByAsset />,
|
||||
handle: {
|
||||
breadcrumb: () => {
|
||||
return t('Assets');
|
||||
{
|
||||
path: 'assets',
|
||||
element: <Party />,
|
||||
handle: {
|
||||
breadcrumb: (params: Params<string>) => (
|
||||
<Link to={linkTo(Routes.PARTIES, params.party)}>
|
||||
{truncateMiddle(params.party as string)}
|
||||
</Link>
|
||||
),
|
||||
},
|
||||
children: [
|
||||
{
|
||||
index: true,
|
||||
element: <PartyAccountsByAsset />,
|
||||
handle: {
|
||||
breadcrumb: () => {
|
||||
return t('Assets');
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
: [];
|
||||
|
||||
const assetsRoutes: Route[] = featureFlags.EXPLORER_ASSETS
|
||||
? [
|
||||
{
|
||||
path: Routes.ASSETS,
|
||||
handle: {
|
||||
name: t('Assets'),
|
||||
text: t('Assets'),
|
||||
breadcrumb: () => <Link to={Routes.ASSETS}>{t('Assets')}</Link>,
|
||||
},
|
||||
children: [
|
||||
{
|
||||
index: true,
|
||||
element: <AssetsPage />,
|
||||
},
|
||||
{
|
||||
path: ':assetId',
|
||||
element: <AssetPage />,
|
||||
handle: {
|
||||
breadcrumb: (params: Params<string>) => (
|
||||
<AssetLink assetId={params.assetId as string} />
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
: [];
|
||||
|
||||
const assetsRoutes: Route[] = FLAGS.EXPLORER_ASSETS
|
||||
? [
|
||||
{
|
||||
path: Routes.ASSETS,
|
||||
handle: {
|
||||
name: t('Assets'),
|
||||
text: t('Assets'),
|
||||
breadcrumb: () => <Link to={Routes.ASSETS}>{t('Assets')}</Link>,
|
||||
},
|
||||
],
|
||||
},
|
||||
children: [
|
||||
{
|
||||
index: true,
|
||||
element: <AssetsPage />,
|
||||
]
|
||||
: [];
|
||||
|
||||
const genesisRoutes: Route[] = featureFlags.EXPLORER_GENESIS
|
||||
? [
|
||||
{
|
||||
path: Routes.GENESIS,
|
||||
handle: {
|
||||
name: t('Genesis'),
|
||||
text: t('Genesis Parameters'),
|
||||
breadcrumb: () => (
|
||||
<Link to={Routes.GENESIS}>{t('Genesis Parameters')}</Link>
|
||||
),
|
||||
},
|
||||
element: <Genesis />,
|
||||
},
|
||||
]
|
||||
: [];
|
||||
|
||||
const governanceRoutes: Route[] = featureFlags.EXPLORER_GOVERNANCE
|
||||
? [
|
||||
{
|
||||
path: Routes.GOVERNANCE,
|
||||
handle: {
|
||||
name: t('Governance proposals'),
|
||||
text: t('Governance Proposals'),
|
||||
breadcrumb: () => (
|
||||
<Link to={Routes.GOVERNANCE}>{t('Governance Proposals')}</Link>
|
||||
),
|
||||
},
|
||||
element: <Proposals />,
|
||||
},
|
||||
]
|
||||
: [];
|
||||
|
||||
const marketsRoutes: Route[] = featureFlags.EXPLORER_MARKETS
|
||||
? [
|
||||
{
|
||||
path: Routes.MARKETS,
|
||||
handle: {
|
||||
name: t('Markets'),
|
||||
text: t('Markets'),
|
||||
breadcrumb: () => <Link to={Routes.MARKETS}>{t('Markets')}</Link>,
|
||||
},
|
||||
children: [
|
||||
{
|
||||
index: true,
|
||||
element: <MarketsPage />,
|
||||
},
|
||||
{
|
||||
path: ':marketId',
|
||||
element: <MarketPage />,
|
||||
handle: {
|
||||
breadcrumb: (params: Params<string>) => (
|
||||
<MarketLink id={params.marketId as string} />
|
||||
),
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
: [];
|
||||
|
||||
const networkParametersRoutes: Route[] =
|
||||
featureFlags.EXPLORER_NETWORK_PARAMETERS
|
||||
? [
|
||||
{
|
||||
path: ':assetId',
|
||||
element: <AssetPage />,
|
||||
path: Routes.NETWORK_PARAMETERS,
|
||||
handle: {
|
||||
breadcrumb: (params: Params<string>) => (
|
||||
<AssetLink assetId={params.assetId as string} />
|
||||
name: t('NetworkParameters'),
|
||||
text: t('Network Parameters'),
|
||||
breadcrumb: () => (
|
||||
<Link to={Routes.NETWORK_PARAMETERS}>
|
||||
{t('Network Parameters')}
|
||||
</Link>
|
||||
),
|
||||
},
|
||||
element: <NetworkParameters />,
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
: [];
|
||||
]
|
||||
: [];
|
||||
|
||||
const genesisRoutes: Route[] = FLAGS.EXPLORER_GENESIS
|
||||
? [
|
||||
{
|
||||
path: Routes.GENESIS,
|
||||
handle: {
|
||||
name: t('Genesis'),
|
||||
text: t('Genesis Parameters'),
|
||||
breadcrumb: () => (
|
||||
<Link to={Routes.GENESIS}>{t('Genesis Parameters')}</Link>
|
||||
),
|
||||
},
|
||||
element: <Genesis />,
|
||||
},
|
||||
]
|
||||
: [];
|
||||
|
||||
const governanceRoutes: Route[] = FLAGS.EXPLORER_GOVERNANCE
|
||||
? [
|
||||
{
|
||||
path: Routes.GOVERNANCE,
|
||||
handle: {
|
||||
name: t('Governance proposals'),
|
||||
text: t('Governance Proposals'),
|
||||
breadcrumb: () => (
|
||||
<Link to={Routes.GOVERNANCE}>{t('Governance Proposals')}</Link>
|
||||
),
|
||||
},
|
||||
element: <Proposals />,
|
||||
},
|
||||
]
|
||||
: [];
|
||||
|
||||
const marketsRoutes: Route[] = FLAGS.EXPLORER_MARKETS
|
||||
? [
|
||||
{
|
||||
path: Routes.MARKETS,
|
||||
handle: {
|
||||
name: t('Markets'),
|
||||
text: t('Markets'),
|
||||
breadcrumb: () => <Link to={Routes.MARKETS}>{t('Markets')}</Link>,
|
||||
},
|
||||
children: [
|
||||
{
|
||||
index: true,
|
||||
element: <MarketsPage />,
|
||||
const validators: Route[] = featureFlags.EXPLORER_VALIDATORS
|
||||
? [
|
||||
{
|
||||
path: Routes.VALIDATORS,
|
||||
handle: {
|
||||
name: t('Validators'),
|
||||
text: t('Validators'),
|
||||
breadcrumb: () => (
|
||||
<Link to={Routes.VALIDATORS}>{t('Validators')}</Link>
|
||||
),
|
||||
},
|
||||
{
|
||||
path: ':marketId',
|
||||
element: <MarketPage />,
|
||||
handle: {
|
||||
breadcrumb: (params: Params<string>) => (
|
||||
<MarketLink id={params.marketId as string} />
|
||||
),
|
||||
element: <ValidatorsPage />,
|
||||
},
|
||||
]
|
||||
: [];
|
||||
|
||||
const linkTo = (...segments: (string | undefined)[]) =>
|
||||
compact(segments).join('/');
|
||||
|
||||
const routerConfig: Route[] = [
|
||||
{
|
||||
path: Routes.HOME,
|
||||
element: <Layout />,
|
||||
handle: {
|
||||
name: t('Home'),
|
||||
text: t('Home'),
|
||||
breadcrumb: () => <Link to={Routes.HOME}>{t('Home')}</Link>,
|
||||
},
|
||||
errorElement: <ErrorBoundary />,
|
||||
children: [
|
||||
{
|
||||
index: true,
|
||||
element: <Home />,
|
||||
},
|
||||
{
|
||||
path: Routes.TX,
|
||||
handle: {
|
||||
name: t('Txs'),
|
||||
text: t('Transactions'),
|
||||
breadcrumb: () => <Link to={Routes.TX}>{t('Transactions')}</Link>,
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: ':txHash',
|
||||
element: <Tx />,
|
||||
handle: {
|
||||
breadcrumb: (params: Params<string>) => (
|
||||
<Link to={linkTo(Routes.TX, params.txHash)}>
|
||||
{truncateMiddle(remove0x(params.txHash as string))}
|
||||
</Link>
|
||||
),
|
||||
},
|
||||
},
|
||||
{
|
||||
index: true,
|
||||
element: <TxsList />,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: Routes.BLOCKS,
|
||||
handle: {
|
||||
name: t('Blocks'),
|
||||
text: t('Blocks'),
|
||||
breadcrumb: () => <Link to={Routes.BLOCKS}>{t('Blocks')}</Link>,
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
: [];
|
||||
|
||||
const networkParametersRoutes: Route[] = FLAGS.EXPLORER_NETWORK_PARAMETERS
|
||||
? [
|
||||
{
|
||||
path: Routes.NETWORK_PARAMETERS,
|
||||
handle: {
|
||||
name: t('NetworkParameters'),
|
||||
text: t('Network Parameters'),
|
||||
breadcrumb: () => (
|
||||
<Link to={Routes.NETWORK_PARAMETERS}>
|
||||
{t('Network Parameters')}
|
||||
</Link>
|
||||
),
|
||||
element: <BlockPage />,
|
||||
children: [
|
||||
{
|
||||
index: true,
|
||||
element: <Blocks />,
|
||||
},
|
||||
{
|
||||
path: ':block',
|
||||
element: <Block />,
|
||||
handle: {
|
||||
breadcrumb: (params: Params<string>) => (
|
||||
<Link to={linkTo(Routes.BLOCKS, params.block)}>
|
||||
{params.block}
|
||||
</Link>
|
||||
),
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
element: <NetworkParameters />,
|
||||
},
|
||||
]
|
||||
: [];
|
||||
|
||||
const validators: Route[] = FLAGS.EXPLORER_VALIDATORS
|
||||
? [
|
||||
{
|
||||
path: Routes.VALIDATORS,
|
||||
handle: {
|
||||
name: t('Validators'),
|
||||
text: t('Validators'),
|
||||
breadcrumb: () => (
|
||||
<Link to={Routes.VALIDATORS}>{t('Validators')}</Link>
|
||||
),
|
||||
{
|
||||
path: Routes.ORACLES,
|
||||
handle: {
|
||||
name: t('Oracles'),
|
||||
text: t('Oracles'),
|
||||
breadcrumb: () => <Link to={Routes.ORACLES}>{t('Oracles')}</Link>,
|
||||
},
|
||||
element: <OraclePage />,
|
||||
children: [
|
||||
{
|
||||
index: true,
|
||||
element: <Oracles />,
|
||||
},
|
||||
{
|
||||
path: ':id',
|
||||
element: <Oracle />,
|
||||
handle: {
|
||||
breadcrumb: (params: Params<string>) => (
|
||||
<Link to={linkTo(Routes.ORACLES, params.id)}>
|
||||
{truncateMiddle(params.id as string)}
|
||||
</Link>
|
||||
),
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
element: <ValidatorsPage />,
|
||||
},
|
||||
]
|
||||
: [];
|
||||
|
||||
const linkTo = (...segments: (string | undefined)[]) =>
|
||||
compact(segments).join('/');
|
||||
|
||||
export const routerConfig: Route[] = [
|
||||
{
|
||||
path: Routes.HOME,
|
||||
element: <Layout />,
|
||||
handle: {
|
||||
name: t('Home'),
|
||||
text: t('Home'),
|
||||
breadcrumb: () => <Link to={Routes.HOME}>{t('Home')}</Link>,
|
||||
{
|
||||
path: Routes.DISCLAIMER,
|
||||
element: <Disclaimer />,
|
||||
handle: {
|
||||
name: t('Disclaimer'),
|
||||
text: t('Disclaimer'),
|
||||
breadcrumb: () => (
|
||||
<Link to={Routes.DISCLAIMER}>{t('Disclaimer')}</Link>
|
||||
),
|
||||
},
|
||||
},
|
||||
...partiesRoutes,
|
||||
...assetsRoutes,
|
||||
...genesisRoutes,
|
||||
...governanceRoutes,
|
||||
...marketsRoutes,
|
||||
...networkParametersRoutes,
|
||||
...validators,
|
||||
],
|
||||
},
|
||||
errorElement: <ErrorBoundary />,
|
||||
children: [
|
||||
{
|
||||
index: true,
|
||||
element: <Home />,
|
||||
{
|
||||
path: Routes.RESTRICTED,
|
||||
element: <RestrictedPage />,
|
||||
handle: {
|
||||
name: t('Restricted'),
|
||||
text: t('Restricted'),
|
||||
},
|
||||
{
|
||||
path: Routes.TX,
|
||||
handle: {
|
||||
name: t('Txs'),
|
||||
text: t('Transactions'),
|
||||
breadcrumb: () => <Link to={Routes.TX}>{t('Transactions')}</Link>,
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: ':txHash',
|
||||
element: <Tx />,
|
||||
handle: {
|
||||
breadcrumb: (params: Params<string>) => (
|
||||
<Link to={linkTo(Routes.TX, params.txHash)}>
|
||||
{truncateMiddle(remove0x(params.txHash as string))}
|
||||
</Link>
|
||||
),
|
||||
},
|
||||
},
|
||||
{
|
||||
index: true,
|
||||
element: <TxsList />,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: Routes.BLOCKS,
|
||||
handle: {
|
||||
name: t('Blocks'),
|
||||
text: t('Blocks'),
|
||||
breadcrumb: () => <Link to={Routes.BLOCKS}>{t('Blocks')}</Link>,
|
||||
},
|
||||
element: <BlockPage />,
|
||||
children: [
|
||||
{
|
||||
index: true,
|
||||
element: <Blocks />,
|
||||
},
|
||||
{
|
||||
path: ':block',
|
||||
element: <Block />,
|
||||
handle: {
|
||||
breadcrumb: (params: Params<string>) => (
|
||||
<Link to={linkTo(Routes.BLOCKS, params.block)}>
|
||||
{params.block}
|
||||
</Link>
|
||||
),
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: Routes.ORACLES,
|
||||
handle: {
|
||||
name: t('Oracles'),
|
||||
text: t('Oracles'),
|
||||
breadcrumb: () => <Link to={Routes.ORACLES}>{t('Oracles')}</Link>,
|
||||
},
|
||||
element: <OraclePage />,
|
||||
children: [
|
||||
{
|
||||
index: true,
|
||||
element: <Oracles />,
|
||||
},
|
||||
{
|
||||
path: ':id',
|
||||
element: <Oracle />,
|
||||
handle: {
|
||||
breadcrumb: (params: Params<string>) => (
|
||||
<Link to={linkTo(Routes.ORACLES, params.id)}>
|
||||
{truncateMiddle(params.id as string)}
|
||||
</Link>
|
||||
),
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: Routes.DISCLAIMER,
|
||||
element: <Disclaimer />,
|
||||
handle: {
|
||||
name: t('Disclaimer'),
|
||||
text: t('Disclaimer'),
|
||||
breadcrumb: () => (
|
||||
<Link to={Routes.DISCLAIMER}>{t('Disclaimer')}</Link>
|
||||
),
|
||||
},
|
||||
},
|
||||
...partiesRoutes,
|
||||
...assetsRoutes,
|
||||
...genesisRoutes,
|
||||
...governanceRoutes,
|
||||
...marketsRoutes,
|
||||
...networkParametersRoutes,
|
||||
...validators,
|
||||
],
|
||||
},
|
||||
{
|
||||
path: Routes.RESTRICTED,
|
||||
element: <RestrictedPage />,
|
||||
handle: {
|
||||
name: t('Restricted'),
|
||||
text: t('Restricted'),
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
export const router = createBrowserRouter(routerConfig);
|
||||
];
|
||||
return routerConfig;
|
||||
};
|
||||
|
||||
@@ -2,7 +2,7 @@ import * as Sentry from '@sentry/react';
|
||||
import { toBigNum } from '@vegaprotocol/utils';
|
||||
import { Splash } from '@vegaprotocol/ui-toolkit';
|
||||
import { useVegaWallet, useEagerConnect } from '@vegaprotocol/wallet';
|
||||
import { FLAGS, useEnvironment } from '@vegaprotocol/environment';
|
||||
import { useFeatureFlags, useEnvironment } from '@vegaprotocol/environment';
|
||||
import { useWeb3React } from '@web3-react/core';
|
||||
import React, { Suspense } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
@@ -15,21 +15,23 @@ import {
|
||||
} from './contexts/app-state/app-state-context';
|
||||
import { useContracts } from './contexts/contracts/contracts-context';
|
||||
import { useRefreshAssociatedBalances } from './hooks/use-refresh-associated-balances';
|
||||
import { Connectors } from './lib/vega-connectors';
|
||||
import { useConnectors } from './lib/vega-connectors';
|
||||
import { useSearchParams } from 'react-router-dom';
|
||||
|
||||
const useVegaWalletEagerConnect = () => {
|
||||
const vegaConnecting = useEagerConnect(Connectors);
|
||||
const connectors = useConnectors();
|
||||
const vegaConnecting = useEagerConnect(connectors);
|
||||
const { pubKey, connect } = useVegaWallet();
|
||||
const [searchParams] = useSearchParams();
|
||||
const [query] = React.useState(searchParams.get('address'));
|
||||
if (query && !pubKey) {
|
||||
connect(Connectors['view']);
|
||||
connect(connectors.view);
|
||||
}
|
||||
return vegaConnecting;
|
||||
};
|
||||
|
||||
export const AppLoader = ({ children }: { children: React.ReactElement }) => {
|
||||
const featureFlags = useFeatureFlags((state) => state.flags);
|
||||
const { t } = useTranslation();
|
||||
const { account } = useWeb3React();
|
||||
const { VEGA_URL } = useEnvironment();
|
||||
@@ -79,10 +81,16 @@ export const AppLoader = ({ children }: { children: React.ReactElement }) => {
|
||||
}
|
||||
};
|
||||
|
||||
if (!FLAGS.GOVERNANCE_NETWORK_DOWN) {
|
||||
if (!featureFlags.GOVERNANCE_NETWORK_DOWN) {
|
||||
run();
|
||||
}
|
||||
}, [token, appDispatch, staking, vesting]);
|
||||
}, [
|
||||
token,
|
||||
appDispatch,
|
||||
staking,
|
||||
vesting,
|
||||
featureFlags.GOVERNANCE_NETWORK_DOWN,
|
||||
]);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (account && pubKey) {
|
||||
@@ -147,16 +155,16 @@ export const AppLoader = ({ children }: { children: React.ReactElement }) => {
|
||||
};
|
||||
|
||||
// Only begin polling if network limits flag is set, as this is a new API not yet on mainnet 7/3/22
|
||||
if (FLAGS.GOVERNANCE_NETWORK_LIMITS) {
|
||||
if (featureFlags.GOVERNANCE_NETWORK_LIMITS) {
|
||||
getNetworkLimits();
|
||||
}
|
||||
|
||||
return () => {
|
||||
stopPoll();
|
||||
};
|
||||
}, [appDispatch, VEGA_URL, t]);
|
||||
}, [appDispatch, VEGA_URL, t, featureFlags.GOVERNANCE_NETWORK_LIMITS]);
|
||||
|
||||
if (FLAGS.GOVERNANCE_NETWORK_DOWN) {
|
||||
if (featureFlags.GOVERNANCE_NETWORK_DOWN) {
|
||||
return (
|
||||
<Splash>
|
||||
<SplashError />
|
||||
|
||||
@@ -7,16 +7,16 @@ import {
|
||||
AppStateActionType,
|
||||
useAppState,
|
||||
} from '../../contexts/app-state/app-state-context';
|
||||
import { Connectors } from '../../lib/vega-connectors';
|
||||
import { useConnectors } from '../../lib/vega-connectors';
|
||||
import { RiskMessage } from './risk-message';
|
||||
|
||||
export const VegaWalletDialogs = () => {
|
||||
const { appState, appDispatch } = useAppState();
|
||||
|
||||
const connectors = useConnectors();
|
||||
return (
|
||||
<>
|
||||
<VegaConnectDialog
|
||||
connectors={Connectors}
|
||||
connectors={connectors}
|
||||
riskMessage={<RiskMessage />}
|
||||
/>
|
||||
|
||||
@@ -30,7 +30,7 @@ export const VegaWalletDialogs = () => {
|
||||
}
|
||||
/>
|
||||
|
||||
<ViewAsDialog connector={Connectors.view} />
|
||||
<ViewAsDialog connector={connectors.view} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { FLAGS } from '@vegaprotocol/environment';
|
||||
import { useFeatureFlags } from '@vegaprotocol/environment';
|
||||
import { useMemo } from 'react';
|
||||
import {
|
||||
JsonRpcConnector,
|
||||
ViewConnector,
|
||||
@@ -13,13 +14,17 @@ export const jsonRpc = new JsonRpcConnector();
|
||||
export const injected = new InjectedConnector();
|
||||
export const view = new ViewConnector(urlParams.get('address'));
|
||||
|
||||
export const snap = FLAGS.METAMASK_SNAPS
|
||||
? new SnapConnector(DEFAULT_SNAP_ID)
|
||||
: undefined;
|
||||
export const snap = new SnapConnector(DEFAULT_SNAP_ID);
|
||||
|
||||
export const Connectors = {
|
||||
injected,
|
||||
jsonRpc,
|
||||
view,
|
||||
snap,
|
||||
export const useConnectors = () => {
|
||||
const featureFlags = useFeatureFlags((state) => state.flags);
|
||||
return useMemo(
|
||||
() => ({
|
||||
injected,
|
||||
jsonRpc,
|
||||
view,
|
||||
snap: featureFlags.METAMASK_SNAPS ? snap : undefined,
|
||||
}),
|
||||
[featureFlags.METAMASK_SNAPS]
|
||||
);
|
||||
};
|
||||
|
||||
@@ -12,7 +12,7 @@ import { useRefreshAfterEpoch } from '../../hooks/use-refresh-after-epoch';
|
||||
import { ProposalsListItem } from '../proposals/components/proposals-list-item';
|
||||
import { ProtocolUpgradeProposalsListItem } from '../proposals/components/protocol-upgrade-proposals-list-item/protocol-upgrade-proposals-list-item';
|
||||
import Routes from '../routes';
|
||||
import { ExternalLinks, FLAGS } from '@vegaprotocol/environment';
|
||||
import { ExternalLinks, useFeatureFlags } from '@vegaprotocol/environment';
|
||||
import { removePaginationWrapper } from '@vegaprotocol/utils';
|
||||
import { useNodesQuery } from '../staking/home/__generated__/Nodes';
|
||||
import { useProposalsQuery } from '../proposals/proposals/__generated__/Proposals';
|
||||
@@ -175,6 +175,7 @@ export const ValidatorDetailsLink = ({
|
||||
};
|
||||
|
||||
const GovernanceHome = ({ name }: RouteChildProps) => {
|
||||
const featureFlags = useFeatureFlags((state) => state.flags);
|
||||
useDocumentTitle(name);
|
||||
const { t } = useTranslation();
|
||||
const {
|
||||
@@ -186,9 +187,9 @@ const GovernanceHome = ({ name }: RouteChildProps) => {
|
||||
fetchPolicy: 'network-only',
|
||||
errorPolicy: 'ignore',
|
||||
variables: {
|
||||
includeNewMarketProductFields: !!FLAGS.PRODUCT_PERPETUALS,
|
||||
includeUpdateMarketStates: !!FLAGS.UPDATE_MARKET_STATE,
|
||||
includeUpdateReferralPrograms: !!FLAGS.REFERRALS,
|
||||
includeNewMarketProductFields: !!featureFlags.PRODUCT_PERPETUALS,
|
||||
includeUpdateMarketStates: !!featureFlags.UPDATE_MARKET_STATE,
|
||||
includeUpdateReferralPrograms: !!featureFlags.REFERRALS,
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
+2
-3
@@ -19,7 +19,7 @@ import {
|
||||
mockWalletContext,
|
||||
createUserVoteQueryMock,
|
||||
} from '../../test-helpers/mocks';
|
||||
import { FLAGS } from '@vegaprotocol/environment';
|
||||
import { useFeatureFlags } from '@vegaprotocol/environment';
|
||||
import { BrowserRouter } from 'react-router-dom';
|
||||
import { VoteState } from '../vote-details/use-user-vote';
|
||||
import { useNewTransferProposalDetails } from '@vegaprotocol/proposals';
|
||||
@@ -62,8 +62,7 @@ describe('Proposal header', () => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
it('Renders New market proposal', () => {
|
||||
const mockedFlags = jest.mocked(FLAGS);
|
||||
mockedFlags.SUCCESSOR_MARKETS = true;
|
||||
useFeatureFlags.setState({ flags: { SUCCESSOR_MARKETS: true } });
|
||||
renderComponent(
|
||||
generateProposal({
|
||||
rationale: {
|
||||
|
||||
+9
-7
@@ -12,7 +12,7 @@ import {
|
||||
useNewTransferProposalDetails,
|
||||
useSuccessorMarketProposalDetails,
|
||||
} from '@vegaprotocol/proposals';
|
||||
import { FLAGS } from '@vegaprotocol/environment';
|
||||
import { useFeatureFlags } from '@vegaprotocol/environment';
|
||||
import Routes from '../../../routes';
|
||||
import { Link } from 'react-router-dom';
|
||||
import type { VoteState } from '../vote-details/use-user-vote';
|
||||
@@ -28,6 +28,7 @@ export const ProposalHeader = ({
|
||||
isListItem?: boolean;
|
||||
voteState?: VoteState | null;
|
||||
}) => {
|
||||
const featureFlags = useFeatureFlags((state) => state.flags);
|
||||
const { t } = useTranslation();
|
||||
const change = proposal?.terms.change;
|
||||
|
||||
@@ -54,13 +55,14 @@ export const ProposalHeader = ({
|
||||
switch (change?.__typename) {
|
||||
case 'NewMarket': {
|
||||
proposalType =
|
||||
FLAGS.PRODUCT_PERPETUALS && change?.instrument?.product?.__typename
|
||||
featureFlags.PRODUCT_PERPETUALS &&
|
||||
change?.instrument?.product?.__typename
|
||||
? `NewMarket${change?.instrument?.product?.__typename}`
|
||||
: 'NewMarket';
|
||||
fallbackTitle = t('NewMarketProposal');
|
||||
details = (
|
||||
<>
|
||||
{FLAGS.SUCCESSOR_MARKETS && (
|
||||
{featureFlags.SUCCESSOR_MARKETS && (
|
||||
<SuccessorCode proposalId={proposal?.id} />
|
||||
)}
|
||||
<span>
|
||||
@@ -82,13 +84,13 @@ export const ProposalHeader = ({
|
||||
}
|
||||
case 'UpdateMarketState': {
|
||||
proposalType =
|
||||
FLAGS.UPDATE_MARKET_STATE && change?.updateType
|
||||
featureFlags.UPDATE_MARKET_STATE && change?.updateType
|
||||
? t(change.updateType)
|
||||
: 'UpdateMarketState';
|
||||
fallbackTitle = t('UpdateMarketStateProposal');
|
||||
details = (
|
||||
<span>
|
||||
{FLAGS.UPDATE_MARKET_STATE &&
|
||||
{featureFlags.UPDATE_MARKET_STATE &&
|
||||
change?.market?.id &&
|
||||
change.updateType ? (
|
||||
<>
|
||||
@@ -177,14 +179,14 @@ export const ProposalHeader = ({
|
||||
case 'NewTransfer':
|
||||
proposalType = 'NewTransfer';
|
||||
fallbackTitle = t('NewTransferProposal');
|
||||
details = FLAGS.GOVERNANCE_TRANSFERS ? (
|
||||
details = featureFlags.GOVERNANCE_TRANSFERS ? (
|
||||
<NewTransferSummary proposalId={proposal?.id} />
|
||||
) : null;
|
||||
break;
|
||||
case 'CancelTransfer':
|
||||
proposalType = 'CancelTransfer';
|
||||
fallbackTitle = t('CancelTransferProposal');
|
||||
details = FLAGS.GOVERNANCE_TRANSFERS ? (
|
||||
details = featureFlags.GOVERNANCE_TRANSFERS ? (
|
||||
<CancelTransferSummary proposalId={proposal?.id} />
|
||||
) : null;
|
||||
break;
|
||||
|
||||
@@ -26,7 +26,7 @@ import {
|
||||
ProposalCancelTransferDetails,
|
||||
ProposalTransferDetails,
|
||||
} from '../proposal-transfer';
|
||||
import { FLAGS } from '@vegaprotocol/environment';
|
||||
import { useFeatureFlags } from '@vegaprotocol/environment';
|
||||
import { ProposalUpdateBenefitTiers } from '../proposal-update-benefit-tiers';
|
||||
|
||||
export interface ProposalProps {
|
||||
@@ -53,6 +53,7 @@ export const Proposal = ({
|
||||
originalMarketProposalRestData,
|
||||
mostRecentlyEnactedAssociatedMarketProposal,
|
||||
}: ProposalProps) => {
|
||||
const featureFlags = useFeatureFlags((state) => state.flags);
|
||||
const { t } = useTranslation();
|
||||
const { submit, Dialog, finalizedVote, transaction } = useVoteSubmit();
|
||||
const { voteState, voteDatetime } = useUserVote(proposal?.id, finalizedVote);
|
||||
@@ -132,7 +133,7 @@ export const Proposal = ({
|
||||
}
|
||||
|
||||
// Show governance transfer details only if the GOVERNANCE_TRANSFERS flag is on.
|
||||
const governanceTransferDetails = FLAGS.GOVERNANCE_TRANSFERS && (
|
||||
const governanceTransferDetails = featureFlags.GOVERNANCE_TRANSFERS && (
|
||||
<>
|
||||
{proposal.terms.change.__typename === 'NewTransfer' && (
|
||||
/** Governance New Transfer Details */
|
||||
|
||||
@@ -15,10 +15,11 @@ import {
|
||||
useNetworkParams,
|
||||
} from '@vegaprotocol/network-parameters';
|
||||
import { useParentMarketIdQuery } from '@vegaprotocol/markets';
|
||||
import { FLAGS } from '@vegaprotocol/environment';
|
||||
import { useFeatureFlags } from '@vegaprotocol/environment';
|
||||
import { useSuccessorMarketProposalDetails } from '@vegaprotocol/proposals';
|
||||
|
||||
export const ProposalContainer = () => {
|
||||
const featureFlags = useFeatureFlags((state) => state.flags);
|
||||
const [
|
||||
mostRecentlyEnactedAssociatedMarketProposal,
|
||||
setMostRecentlyEnactedAssociatedMarketProposal,
|
||||
@@ -59,9 +60,9 @@ export const ProposalContainer = () => {
|
||||
errorPolicy: 'ignore',
|
||||
variables: {
|
||||
proposalId: params.proposalId || '',
|
||||
includeNewMarketProductField: !!FLAGS.PRODUCT_PERPETUALS,
|
||||
includeUpdateMarketState: !!FLAGS.UPDATE_MARKET_STATE,
|
||||
includeUpdateReferralProgram: !!FLAGS.REFERRALS,
|
||||
includeNewMarketProductField: !!featureFlags.PRODUCT_PERPETUALS,
|
||||
includeUpdateMarketState: !!featureFlags.UPDATE_MARKET_STATE,
|
||||
includeUpdateReferralProgram: !!featureFlags.REFERRALS,
|
||||
},
|
||||
skip: !params.proposalId,
|
||||
});
|
||||
@@ -120,7 +121,7 @@ export const ProposalContainer = () => {
|
||||
variables: {
|
||||
marketId: marketData?.id || '',
|
||||
},
|
||||
skip: !FLAGS.SUCCESSOR_MARKETS || !isSuccessor || !marketData?.id,
|
||||
skip: !featureFlags.SUCCESSOR_MARKETS || !isSuccessor || !marketData?.id,
|
||||
});
|
||||
|
||||
const {
|
||||
@@ -133,7 +134,7 @@ export const ProposalContainer = () => {
|
||||
variables: {
|
||||
marketId: parentMarketId?.market?.parentMarketID || '',
|
||||
skip:
|
||||
!FLAGS.SUCCESSOR_MARKETS ||
|
||||
!featureFlags.SUCCESSOR_MARKETS ||
|
||||
!isSuccessor ||
|
||||
!parentMarketId?.market?.parentMarketID,
|
||||
},
|
||||
|
||||
@@ -17,7 +17,7 @@ import {
|
||||
} from './__generated__/Proposals';
|
||||
import { type ProtocolUpgradeProposalFieldsFragment } from '@vegaprotocol/proposals';
|
||||
import { useProtocolUpgradeProposalsQuery } from '@vegaprotocol/proposals';
|
||||
import { FLAGS } from '@vegaprotocol/environment';
|
||||
import { useFeatureFlags } from '@vegaprotocol/environment';
|
||||
|
||||
export function getNotRejectedProposals(data?: ProposalFieldsFragment[]) {
|
||||
return flow([
|
||||
@@ -43,15 +43,16 @@ export function getNotRejectedProtocolUpgradeProposals<
|
||||
}
|
||||
|
||||
export const ProposalsContainer = () => {
|
||||
const featureFlags = useFeatureFlags((state) => state.flags);
|
||||
const { t } = useTranslation();
|
||||
const { data, loading, error } = useProposalsQuery({
|
||||
pollInterval: 5000,
|
||||
fetchPolicy: 'network-only',
|
||||
errorPolicy: 'ignore',
|
||||
variables: {
|
||||
includeNewMarketProductFields: !!FLAGS.PRODUCT_PERPETUALS,
|
||||
includeUpdateMarketStates: !!FLAGS.UPDATE_MARKET_STATE,
|
||||
includeUpdateReferralPrograms: !!FLAGS.REFERRALS,
|
||||
includeNewMarketProductFields: !!featureFlags.PRODUCT_PERPETUALS,
|
||||
includeUpdateMarketStates: !!featureFlags.UPDATE_MARKET_STATE,
|
||||
includeUpdateReferralPrograms: !!featureFlags.REFERRALS,
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ import { removePaginationWrapper } from '@vegaprotocol/utils';
|
||||
import flow from 'lodash/flow';
|
||||
import orderBy from 'lodash/orderBy';
|
||||
import { ProposalState } from '@vegaprotocol/types';
|
||||
import { FLAGS } from '@vegaprotocol/environment';
|
||||
import { useFeatureFlags } from '@vegaprotocol/environment';
|
||||
|
||||
const orderByDate = (arr: ProposalFieldsFragment[]) =>
|
||||
orderBy(
|
||||
@@ -33,15 +33,16 @@ export function getRejectedProposals(data?: ProposalFieldsFragment[] | null) {
|
||||
}
|
||||
|
||||
export const RejectedProposalsContainer = () => {
|
||||
const featureFlags = useFeatureFlags((state) => state.flags);
|
||||
const { t } = useTranslation();
|
||||
const { data, loading, error } = useProposalsQuery({
|
||||
pollInterval: 5000,
|
||||
fetchPolicy: 'network-only',
|
||||
errorPolicy: 'ignore',
|
||||
variables: {
|
||||
includeNewMarketProductFields: !!FLAGS.PRODUCT_PERPETUALS,
|
||||
includeUpdateMarketStates: !!FLAGS.UPDATE_MARKET_STATE,
|
||||
includeUpdateReferralPrograms: !!FLAGS.REFERRALS,
|
||||
includeNewMarketProductFields: !!featureFlags.PRODUCT_PERPETUALS,
|
||||
includeUpdateMarketStates: !!featureFlags.UPDATE_MARKET_STATE,
|
||||
includeUpdateReferralPrograms: !!featureFlags.REFERRALS,
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -22,10 +22,9 @@ NX_WALLETCONNECT_PROJECT_ID=fe8091dc35738863e509fc4947525c72
|
||||
NX_SUCCESSOR_MARKETS=true
|
||||
NX_STOP_ORDERS=true
|
||||
NX_ICEBERG_ORDERS=true
|
||||
# NX_PRODUCT_PERPETUALS
|
||||
NX_METAMASK_SNAPS=true
|
||||
NX_REFERRALS=false
|
||||
NX_REFERRALS=true
|
||||
# NX_DISABLE_CLOSE_POSITION=false
|
||||
|
||||
NX_TENDERMINT_URL=https://be.vega.community
|
||||
NX_TENDERMINT_WEBSOCKET_URL=wss://be.vega.community/websocket
|
||||
NX_DISABLE_CLOSE_POSITION=true
|
||||
|
||||
@@ -24,4 +24,7 @@ NX_STOP_ORDERS=true
|
||||
NX_ICEBERG_ORDERS=true
|
||||
# NX_PRODUCT_PERPETUALS
|
||||
NX_METAMASK_SNAPS=true
|
||||
NX_REFERRALS=true
|
||||
NX_REFERRALS=true
|
||||
|
||||
NX_CHARTING_LIBRARY_PATH=https://assets.vega.community/trading-view-bundle/v0.0.1/
|
||||
NX_CHARTING_LIBRARY_HASH=PDjWaqPFndDp+LCvqbKvntWriaqNzNpZ5i9R/BULzCg=
|
||||
|
||||
@@ -23,9 +23,11 @@ NX_WALLETCONNECT_PROJECT_ID=fe8091dc35738863e509fc4947525c72
|
||||
NX_SUCCESSOR_MARKETS=true
|
||||
NX_STOP_ORDERS=true
|
||||
NX_ICEBERG_ORDERS=true
|
||||
# NX_PRODUCT_PERPETUALS
|
||||
NX_METAMASK_SNAPS=true
|
||||
NX_REFERRALS=true
|
||||
|
||||
NX_TENDERMINT_URL=https://tm.be.testnet.vega.xyz
|
||||
NX_TENDERMINT_WEBSOCKET_URL=wss://be.testnet.vega.xyz/websocket
|
||||
|
||||
NX_CHARTING_LIBRARY_PATH=https://assets.vega.community/trading-view-bundle/v0.0.1/
|
||||
NX_CHARTING_LIBRARY_HASH=PDjWaqPFndDp+LCvqbKvntWriaqNzNpZ5i9R/BULzCg=
|
||||
|
||||
@@ -72,17 +72,14 @@ export const MarketPage = () => {
|
||||
const { data, loading } = useMarket(marketId);
|
||||
|
||||
useEffect(() => {
|
||||
if (data?.id && data.id !== lastMarketId && !closed) {
|
||||
if (data?.id && data.id !== lastMarketId) {
|
||||
update({ marketId: data.id });
|
||||
}
|
||||
}, [update, lastMarketId, data?.id]);
|
||||
|
||||
useEffect(() => {
|
||||
if (largeScreen && view === undefined) {
|
||||
setViews(
|
||||
{ type: closed ? ViewType.Info : ViewType.Order },
|
||||
currentRouteId
|
||||
);
|
||||
setViews({ type: ViewType.Order }, currentRouteId);
|
||||
}
|
||||
}, [setViews, view, currentRouteId, largeScreen]);
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ import {
|
||||
MarketSuccessorProposalBanner,
|
||||
MarketTerminationBanner,
|
||||
} from '../../components/market-banner';
|
||||
import { FLAGS } from '@vegaprotocol/environment';
|
||||
import { useFeatureFlags } from '@vegaprotocol/environment';
|
||||
import { useT } from '../../lib/use-t';
|
||||
import { ErrorBoundary } from '../../components/error-boundary';
|
||||
|
||||
@@ -35,6 +35,7 @@ const MainGrid = memo(
|
||||
marketId: string;
|
||||
pinnedAsset?: PinnedAsset;
|
||||
}) => {
|
||||
const featureFlags = useFeatureFlags((state) => state.flags);
|
||||
const t = useT();
|
||||
const { data: market } = useMarket(marketId);
|
||||
const [sizes, handleOnLayoutChange] = usePaneLayout({ id: 'top' });
|
||||
@@ -61,10 +62,10 @@ const MainGrid = memo(
|
||||
id="chart"
|
||||
overflowHidden
|
||||
name={t('Chart')}
|
||||
menu={<TradingViews.candles.menu />}
|
||||
menu={<TradingViews.chart.menu />}
|
||||
>
|
||||
<ErrorBoundary feature="chart">
|
||||
<TradingViews.candles.component marketId={marketId} />
|
||||
<TradingViews.chart.component marketId={marketId} />
|
||||
</ErrorBoundary>
|
||||
</Tab>
|
||||
<Tab id="depth" name={t('Depth')}>
|
||||
@@ -89,7 +90,11 @@ const MainGrid = memo(
|
||||
{market &&
|
||||
market.tradableInstrument.instrument.product.__typename ===
|
||||
'Perpetual' ? (
|
||||
<Tab id="funding-payments" name={t('Funding payments')}>
|
||||
<Tab
|
||||
id="funding-payments"
|
||||
name={t('Funding payments')}
|
||||
settings={<TradingViews.fundingPayments.settings />}
|
||||
>
|
||||
<ErrorBoundary feature="funding-payments">
|
||||
<TradingViews.fundingPayments.component
|
||||
marketId={marketId}
|
||||
@@ -111,7 +116,11 @@ const MainGrid = memo(
|
||||
<TradingViews.orderbook.component marketId={marketId} />
|
||||
</ErrorBoundary>
|
||||
</Tab>
|
||||
<Tab id="trades" name={t('Trades')}>
|
||||
<Tab
|
||||
id="trades"
|
||||
name={t('Trades')}
|
||||
settings={<TradingViews.trades.settings />}
|
||||
>
|
||||
<ErrorBoundary feature="trades">
|
||||
<TradingViews.trades.component marketId={marketId} />
|
||||
</ErrorBoundary>
|
||||
@@ -132,6 +141,7 @@ const MainGrid = memo(
|
||||
id="positions"
|
||||
name={t('Positions')}
|
||||
menu={<TradingViews.positions.menu />}
|
||||
settings={<TradingViews.positions.settings />}
|
||||
>
|
||||
<ErrorBoundary feature="positions">
|
||||
<TradingViews.positions.component />
|
||||
@@ -141,17 +151,26 @@ const MainGrid = memo(
|
||||
id="open-orders"
|
||||
name={t('Open')}
|
||||
menu={<TradingViews.activeOrders.menu />}
|
||||
settings={<TradingViews.activeOrders.settings />}
|
||||
>
|
||||
<ErrorBoundary feature="activeOrders">
|
||||
<TradingViews.activeOrders.component />
|
||||
</ErrorBoundary>
|
||||
</Tab>
|
||||
<Tab id="closed-orders" name={t('Closed')}>
|
||||
<Tab
|
||||
id="closed-orders"
|
||||
name={t('Closed')}
|
||||
settings={<TradingViews.closedOrders.settings />}
|
||||
>
|
||||
<ErrorBoundary feature="closedOrders">
|
||||
<TradingViews.closedOrders.component />
|
||||
</ErrorBoundary>
|
||||
</Tab>
|
||||
<Tab id="rejected-orders" name={t('Rejected')}>
|
||||
<Tab
|
||||
id="rejected-orders"
|
||||
name={t('Rejected')}
|
||||
settings={<TradingViews.rejectedOrders.settings />}
|
||||
>
|
||||
<ErrorBoundary feature="rejectedOrders">
|
||||
<TradingViews.rejectedOrders.component />
|
||||
</ErrorBoundary>
|
||||
@@ -160,25 +179,35 @@ const MainGrid = memo(
|
||||
id="orders"
|
||||
name={t('All')}
|
||||
menu={<TradingViews.orders.menu />}
|
||||
settings={<TradingViews.orders.settings />}
|
||||
>
|
||||
<ErrorBoundary feature="orders">
|
||||
<TradingViews.orders.component />
|
||||
</ErrorBoundary>
|
||||
</Tab>
|
||||
{FLAGS.STOP_ORDERS ? (
|
||||
<Tab id="stop-orders" name={t('Stop orders')}>
|
||||
{featureFlags.STOP_ORDERS ? (
|
||||
<Tab
|
||||
id="stop-orders"
|
||||
name={t('Stop orders')}
|
||||
settings={<TradingViews.stopOrders.settings />}
|
||||
>
|
||||
<ErrorBoundary feature="stop-orders">
|
||||
<TradingViews.stopOrders.component />
|
||||
</ErrorBoundary>
|
||||
</Tab>
|
||||
) : null}
|
||||
<Tab id="fills" name={t('Fills')}>
|
||||
<Tab
|
||||
id="fills"
|
||||
name={t('Fills')}
|
||||
settings={<TradingViews.fills.settings />}
|
||||
>
|
||||
<TradingViews.fills.component />
|
||||
</Tab>
|
||||
<Tab
|
||||
id="accounts"
|
||||
name={t('Collateral')}
|
||||
menu={<TradingViews.collateral.menu />}
|
||||
settings={<TradingViews.collateral.settings />}
|
||||
>
|
||||
<ErrorBoundary feature="collateral">
|
||||
<TradingViews.collateral.component
|
||||
@@ -196,6 +225,7 @@ const MainGrid = memo(
|
||||
MainGrid.displayName = 'MainGrid';
|
||||
|
||||
export const TradeGrid = ({ market, pinnedAsset }: TradeGridProps) => {
|
||||
const featureFlags = useFeatureFlags((state) => state.flags);
|
||||
const wrapperClasses = classNames(
|
||||
'h-full grid',
|
||||
'grid-rows-[min-content_1fr]'
|
||||
@@ -204,7 +234,7 @@ export const TradeGrid = ({ market, pinnedAsset }: TradeGridProps) => {
|
||||
return (
|
||||
<div className={wrapperClasses}>
|
||||
<div>
|
||||
{FLAGS.SUCCESSOR_MARKETS && (
|
||||
{featureFlags.SUCCESSOR_MARKETS && (
|
||||
<>
|
||||
<MarketSuccessorBanner market={market} />
|
||||
<MarketSuccessorProposalBanner marketId={market?.id} />
|
||||
|
||||
@@ -4,8 +4,7 @@ import { OracleBanner } from '@vegaprotocol/markets';
|
||||
import { useState } from 'react';
|
||||
import AutoSizer from 'react-virtualized-auto-sizer';
|
||||
import classNames from 'classnames';
|
||||
import { FLAGS } from '@vegaprotocol/environment';
|
||||
import { Splash } from '@vegaprotocol/ui-toolkit';
|
||||
import { Splash, VegaIcon, VegaIconNames } from '@vegaprotocol/ui-toolkit';
|
||||
import { useT } from '../../lib/use-t';
|
||||
import {
|
||||
MarketSuccessorBanner,
|
||||
@@ -15,6 +14,7 @@ import {
|
||||
import { ErrorBoundary } from '../../components/error-boundary';
|
||||
import { type TradingView } from './trade-views';
|
||||
import { TradingViews } from './trade-views';
|
||||
import { useFeatureFlags } from '@vegaprotocol/environment';
|
||||
|
||||
interface TradePanelsProps {
|
||||
market: Market | null;
|
||||
@@ -22,7 +22,10 @@ interface TradePanelsProps {
|
||||
}
|
||||
|
||||
export const TradePanels = ({ market, pinnedAsset }: TradePanelsProps) => {
|
||||
const [view, setView] = useState<TradingView>('candles');
|
||||
const featureFlags = useFeatureFlags((state) => state.flags);
|
||||
const [view, setView] = useState<TradingView>('chart');
|
||||
const [settingsOpened, setSettingsOpened] = useState(false);
|
||||
const viewCfg = TradingViews[view];
|
||||
|
||||
const renderView = () => {
|
||||
const Component = TradingViews[view].component;
|
||||
@@ -43,25 +46,27 @@ export const TradePanels = ({ market, pinnedAsset }: TradePanelsProps) => {
|
||||
};
|
||||
|
||||
const renderMenu = () => {
|
||||
const viewCfg = TradingViews[view];
|
||||
|
||||
if ('menu' in viewCfg) {
|
||||
const Menu = viewCfg.menu;
|
||||
|
||||
if ('menu' in viewCfg || 'settings' in viewCfg) {
|
||||
return (
|
||||
<div className="flex gap-1 p-1 bg-vega-clight-800 dark:bg-vega-cdark-800 border-b border-default">
|
||||
<Menu />
|
||||
<div className="flex items-center justify-end gap-1 p-1 bg-vega-clight-800 dark:bg-vega-cdark-800 border-b border-default">
|
||||
{'menu' in viewCfg ? <viewCfg.menu /> : null}
|
||||
{'settings' in viewCfg ? (
|
||||
<button
|
||||
className="ml-1 flex items-center justify-center h-6 w-6"
|
||||
onClick={() => setSettingsOpened((v) => !v)}
|
||||
>
|
||||
<VegaIcon name={VegaIconNames.COG} size={16} />
|
||||
</button>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="h-full grid grid-rows-[min-content_min-content_1fr_min-content]">
|
||||
<div className="h-full grid grid-rows-[min-content_1fr_min-content]">
|
||||
<div>
|
||||
{FLAGS.SUCCESSOR_MARKETS && (
|
||||
{featureFlags.SUCCESSOR_MARKETS && (
|
||||
<>
|
||||
<MarketSuccessorBanner market={market} />
|
||||
<MarketSuccessorProposalBanner marketId={market?.id} />
|
||||
@@ -70,15 +75,36 @@ export const TradePanels = ({ market, pinnedAsset }: TradePanelsProps) => {
|
||||
<MarketTerminationBanner market={market} />
|
||||
<OracleBanner marketId={market?.id || ''} />
|
||||
</div>
|
||||
<div>{renderMenu()}</div>
|
||||
<div className="h-full">
|
||||
<AutoSizer>
|
||||
{({ width, height }) => (
|
||||
<div style={{ width, height }} className="overflow-auto">
|
||||
{renderView()}
|
||||
<div className="h-full grid grid-rows-[min-content_1fr] relative">
|
||||
<div>{renderMenu()}</div>
|
||||
<div className="h-full">
|
||||
<AutoSizer>
|
||||
{({ width, height }) => (
|
||||
<div style={{ width, height }} className="overflow-auto">
|
||||
{renderView()}
|
||||
</div>
|
||||
)}
|
||||
</AutoSizer>
|
||||
</div>
|
||||
{settingsOpened && 'settings' in viewCfg ? (
|
||||
<div className="absolute right-0 top-0 bottom-0 w-[280px] max-w-full bg-vega-clight-700 dark:bg-vega-cdark-700 z-10">
|
||||
<div className="grid grid-rows-[32px_1fr]">
|
||||
<div className="flex justify-end p-1">
|
||||
<button
|
||||
type="button"
|
||||
data-testid="settings-close"
|
||||
onClick={() => setSettingsOpened(false)}
|
||||
className="flex items-center justify-center h-6 w-6"
|
||||
>
|
||||
<VegaIcon name={VegaIconNames.CROSS} size={12} />
|
||||
</button>
|
||||
</div>
|
||||
<div className="relative h-full overflow-auto p-2">
|
||||
<viewCfg.settings />
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</AutoSizer>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
<div className="flex flex-nowrap overflow-x-auto max-w-full border-t border-default">
|
||||
{Object.keys(TradingViews)
|
||||
@@ -109,7 +135,10 @@ export const TradePanels = ({ market, pinnedAsset }: TradePanelsProps) => {
|
||||
key={key}
|
||||
view={key}
|
||||
isActive={isActive}
|
||||
onClick={() => setView(key)}
|
||||
onClick={() => {
|
||||
setSettingsOpened(false);
|
||||
setView(key);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
@@ -148,7 +177,7 @@ const useViewLabel = (view: TradingView) => {
|
||||
const t = useT();
|
||||
|
||||
const labels = {
|
||||
candles: t('Candles'),
|
||||
chart: t('Chart'),
|
||||
depth: t('Depth'),
|
||||
liquidity: t('Liquidity'),
|
||||
funding: t('Funding'),
|
||||
|
||||
@@ -1,28 +1,46 @@
|
||||
import { DepthChartContainer } from '@vegaprotocol/market-depth';
|
||||
import {
|
||||
CandlesChartContainer,
|
||||
CandlesMenu,
|
||||
} from '@vegaprotocol/candles-chart';
|
||||
import { Filter, OpenOrdersMenu } from '@vegaprotocol/orders';
|
||||
import { TradesContainer } from '../../components/trades-container';
|
||||
import {
|
||||
TradesContainer,
|
||||
TradesSettings,
|
||||
} from '../../components/trades-container';
|
||||
import { OrderbookContainer } from '../../components/orderbook-container';
|
||||
import { FillsContainer } from '../../components/fills-container';
|
||||
import { PositionsContainer } from '../../components/positions-container';
|
||||
import { AccountsContainer } from '../../components/accounts-container';
|
||||
import {
|
||||
FillsContainer,
|
||||
FillsSettings,
|
||||
} from '../../components/fills-container';
|
||||
import {
|
||||
PositionsContainer,
|
||||
PositionsSettings,
|
||||
} from '../../components/positions-container';
|
||||
import {
|
||||
AccountsContainer,
|
||||
AccountsSettings,
|
||||
} from '../../components/accounts-container';
|
||||
import { LiquidityContainer } from '../../components/liquidity-container';
|
||||
import { FundingContainer } from '../../components/funding-container';
|
||||
import { FundingPaymentsContainer } from '../../components/funding-payments-container';
|
||||
import { OrdersContainer } from '../../components/orders-container';
|
||||
import { StopOrdersContainer } from '../../components/stop-orders-container';
|
||||
import {
|
||||
FundingPaymentsContainer,
|
||||
FundingPaymentsSettings,
|
||||
} from '../../components/funding-payments-container';
|
||||
import {
|
||||
OrdersContainer,
|
||||
OrdersSettings,
|
||||
} from '../../components/orders-container';
|
||||
import {
|
||||
StopOrdersContainer,
|
||||
StopOrdersSettings,
|
||||
} from '../../components/stop-orders-container';
|
||||
import { AccountsMenu } from '../../components/accounts-menu';
|
||||
import { PositionsMenu } from '../../components/positions-menu';
|
||||
import { ChartContainer, ChartMenu } from '../../components/chart-container';
|
||||
|
||||
export type TradingView = keyof typeof TradingViews;
|
||||
|
||||
export const TradingViews = {
|
||||
candles: {
|
||||
component: CandlesChartContainer,
|
||||
menu: CandlesMenu,
|
||||
chart: {
|
||||
component: ChartContainer,
|
||||
menu: ChartMenu,
|
||||
},
|
||||
depth: {
|
||||
component: DepthChartContainer,
|
||||
@@ -35,37 +53,46 @@ export const TradingViews = {
|
||||
},
|
||||
fundingPayments: {
|
||||
component: FundingPaymentsContainer,
|
||||
settings: FundingPaymentsSettings,
|
||||
},
|
||||
orderbook: {
|
||||
component: OrderbookContainer,
|
||||
},
|
||||
trades: {
|
||||
component: TradesContainer,
|
||||
settings: TradesSettings,
|
||||
},
|
||||
positions: {
|
||||
component: PositionsContainer,
|
||||
menu: PositionsMenu,
|
||||
settings: PositionsSettings,
|
||||
},
|
||||
activeOrders: {
|
||||
component: () => <OrdersContainer filter={Filter.Open} />,
|
||||
menu: OpenOrdersMenu,
|
||||
settings: () => <OrdersSettings filter={Filter.Open} />,
|
||||
},
|
||||
closedOrders: {
|
||||
component: () => <OrdersContainer filter={Filter.Closed} />,
|
||||
settings: () => <OrdersSettings filter={Filter.Closed} />,
|
||||
},
|
||||
rejectedOrders: {
|
||||
component: () => <OrdersContainer filter={Filter.Rejected} />,
|
||||
settings: () => <OrdersSettings filter={Filter.Rejected} />,
|
||||
},
|
||||
orders: {
|
||||
component: OrdersContainer,
|
||||
menu: OpenOrdersMenu,
|
||||
settings: OrdersSettings,
|
||||
},
|
||||
stopOrders: {
|
||||
component: StopOrdersContainer,
|
||||
settings: StopOrdersSettings,
|
||||
},
|
||||
collateral: {
|
||||
component: AccountsContainer,
|
||||
menu: AccountsMenu,
|
||||
settings: AccountsSettings,
|
||||
},
|
||||
fills: { component: FillsContainer },
|
||||
fills: { component: FillsContainer, settings: FillsSettings },
|
||||
} as const;
|
||||
|
||||
@@ -5,14 +5,29 @@ import { titlefy } from '@vegaprotocol/utils';
|
||||
import { useIncompleteWithdrawals } from '@vegaprotocol/withdraws';
|
||||
import { Tab, LocalStoragePersistTabs as Tabs } from '@vegaprotocol/ui-toolkit';
|
||||
import { usePageTitleStore } from '../../stores';
|
||||
import { AccountsContainer } from '../../components/accounts-container';
|
||||
import {
|
||||
AccountsContainer,
|
||||
AccountsSettings,
|
||||
} from '../../components/accounts-container';
|
||||
import { DepositsContainer } from '../../components/deposits-container';
|
||||
import { FillsContainer } from '../../components/fills-container';
|
||||
import { FundingPaymentsContainer } from '../../components/funding-payments-container';
|
||||
import { PositionsContainer } from '../../components/positions-container';
|
||||
import {
|
||||
FillsContainer,
|
||||
FillsSettings,
|
||||
} from '../../components/fills-container';
|
||||
import {
|
||||
FundingPaymentsContainer,
|
||||
FundingPaymentsSettings,
|
||||
} from '../../components/funding-payments-container';
|
||||
import {
|
||||
PositionsContainer,
|
||||
PositionsSettings,
|
||||
} from '../../components/positions-container';
|
||||
import { PositionsMenu } from '../../components/positions-menu';
|
||||
import { WithdrawalsContainer } from '../../components/withdrawals-container';
|
||||
import { OrdersContainer } from '../../components/orders-container';
|
||||
import {
|
||||
OrdersContainer,
|
||||
OrdersSettings,
|
||||
} from '../../components/orders-container';
|
||||
import { LedgerContainer } from '../../components/ledger-container';
|
||||
import {
|
||||
ResizableGrid,
|
||||
@@ -72,22 +87,27 @@ export const Portfolio = () => {
|
||||
id="positions"
|
||||
name={t('Positions')}
|
||||
menu={<PositionsMenu />}
|
||||
settings={<PositionsSettings />}
|
||||
>
|
||||
<ErrorBoundary feature="portfolio-positions">
|
||||
<PositionsContainer allKeys />
|
||||
</ErrorBoundary>
|
||||
</Tab>
|
||||
<Tab id="orders" name={t('Orders')}>
|
||||
<Tab id="orders" name={t('Orders')} settings={<OrdersSettings />}>
|
||||
<ErrorBoundary feature="portfolio-orders">
|
||||
<OrdersContainer />
|
||||
</ErrorBoundary>
|
||||
</Tab>
|
||||
<Tab id="fills" name={t('Fills')}>
|
||||
<Tab id="fills" name={t('Fills')} settings={<FillsSettings />}>
|
||||
<ErrorBoundary feature="portfolio-fills">
|
||||
<FillsContainer />
|
||||
</ErrorBoundary>
|
||||
</Tab>
|
||||
<Tab id="funding-payments" name={t('Funding payments')}>
|
||||
<Tab
|
||||
id="funding-payments"
|
||||
name={t('Funding payments')}
|
||||
settings={<FundingPaymentsSettings />}
|
||||
>
|
||||
<ErrorBoundary feature="portfolio-funding-payments">
|
||||
<FundingPaymentsContainer />
|
||||
</ErrorBoundary>
|
||||
@@ -110,6 +130,7 @@ export const Portfolio = () => {
|
||||
<Tab
|
||||
id="collateral"
|
||||
name={t('Collateral')}
|
||||
settings={<AccountsSettings />}
|
||||
menu={<AccountsMenu />}
|
||||
>
|
||||
<ErrorBoundary feature="portfolio-accounts">
|
||||
|
||||
@@ -18,13 +18,35 @@ import { Routes } from '../../lib/links';
|
||||
import { useTransactionEventSubscription } from '@vegaprotocol/web3';
|
||||
import { Statistics, useStats } from './referral-statistics';
|
||||
import { useReferralProgram } from './hooks/use-referral-program';
|
||||
import { useT } from '../../lib/use-t';
|
||||
import { ns, useT } from '../../lib/use-t';
|
||||
import { useFundsAvailable } from './hooks/use-funds-available';
|
||||
import { ViewType, useSidebar } from '../../components/sidebar';
|
||||
import { useGetCurrentRouteId } from '../../lib/hooks/use-get-current-route-id';
|
||||
import { QUSDTooltip } from './qusd-tooltip';
|
||||
import { Trans } from 'react-i18next';
|
||||
|
||||
const RELOAD_DELAY = 3000;
|
||||
|
||||
const SPAM_PROTECTION_ERR = 'SPAM_PROTECTION_ERR';
|
||||
const SpamProtectionErr = ({
|
||||
requiredFunds,
|
||||
}: {
|
||||
requiredFunds?: string | number | bigint;
|
||||
}) => {
|
||||
if (!requiredFunds) return null;
|
||||
// eslint-disable-next-line react/jsx-no-undef
|
||||
return (
|
||||
<Trans
|
||||
defaults="To protect the network from spam, you must have at least {{requiredFunds}} <0>qUSD</0> of any asset on the network to proceed."
|
||||
values={{
|
||||
requiredFunds,
|
||||
}}
|
||||
components={[<QUSDTooltip key="qusd" />]}
|
||||
ns={ns}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const validateCode = (value: string, t: ReturnType<typeof useT>) => {
|
||||
const number = +`0x${value}`;
|
||||
if (!value || value.length !== 64) {
|
||||
@@ -76,7 +98,6 @@ export const ApplyCodeForm = ({ onSuccess }: { onSuccess?: () => void }) => {
|
||||
setValue,
|
||||
setError,
|
||||
watch,
|
||||
clearErrors,
|
||||
} = useForm();
|
||||
const [params] = useSearchParams();
|
||||
|
||||
@@ -90,30 +111,11 @@ export const ApplyCodeForm = ({ onSuccess }: { onSuccess?: () => void }) => {
|
||||
*/
|
||||
const validateFundsAvailable = useCallback(() => {
|
||||
if (requiredFunds && !isEligible) {
|
||||
const err = t(
|
||||
'Require minimum of {{requiredFunds}} to join a referral set to protect the network from spam.',
|
||||
{ replace: { requiredFunds } }
|
||||
);
|
||||
const err = SPAM_PROTECTION_ERR;
|
||||
return err;
|
||||
}
|
||||
return true;
|
||||
}, [isEligible, requiredFunds, t]);
|
||||
|
||||
useEffect(() => {
|
||||
if (codeField) {
|
||||
const err = validateFundsAvailable();
|
||||
if (err !== true) {
|
||||
setStatus('no-funds');
|
||||
setError('code', {
|
||||
type: 'required',
|
||||
message: err,
|
||||
});
|
||||
} else {
|
||||
setStatus(null);
|
||||
clearErrors('code');
|
||||
}
|
||||
}
|
||||
}, [clearErrors, codeField, isEligible, setError, validateFundsAvailable]);
|
||||
}, [isEligible, requiredFunds]);
|
||||
|
||||
/**
|
||||
* Validates the set a user tries to apply to.
|
||||
@@ -138,6 +140,15 @@ export const ApplyCodeForm = ({ onSuccess }: { onSuccess?: () => void }) => {
|
||||
if (code) setValue('code', code);
|
||||
}, [params, setValue]);
|
||||
|
||||
useEffect(() => {
|
||||
const err = validateFundsAvailable();
|
||||
if (err !== true) {
|
||||
setStatus('no-funds');
|
||||
} else {
|
||||
setStatus(null);
|
||||
}
|
||||
}, [isEligible, validateFundsAvailable]);
|
||||
|
||||
const onSubmit = ({ code }: FieldValues) => {
|
||||
if (isReadOnly || !pubKey || !code || code.length === 0) {
|
||||
return;
|
||||
@@ -321,10 +332,26 @@ export const ApplyCodeForm = ({ onSuccess }: { onSuccess?: () => void }) => {
|
||||
</label>
|
||||
<RainbowButton variant="border" {...getButtonProps()} />
|
||||
</form>
|
||||
{errors.code && (
|
||||
<InputError className="overflow-auto break-words">
|
||||
{errors.code.message?.toString()}
|
||||
{status === 'no-funds' ? (
|
||||
<InputError intent="warning" className="overflow-auto break-words">
|
||||
<span>
|
||||
<SpamProtectionErr requiredFunds={requiredFunds?.toString()} />
|
||||
</span>
|
||||
</InputError>
|
||||
) : (
|
||||
errors.code && (
|
||||
<InputError intent="warning" className="overflow-auto break-words">
|
||||
{errors.code.message === SPAM_PROTECTION_ERR ? (
|
||||
<span>
|
||||
<SpamProtectionErr
|
||||
requiredFunds={requiredFunds?.toString()}
|
||||
/>
|
||||
</span>
|
||||
) : (
|
||||
errors.code.message?.toString()
|
||||
)}
|
||||
</InputError>
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
{validateCode(codeField, t) === true && previewLoading && !previewData ? (
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||
import { useFundsAvailableQuery } from './__generated__/FundsAvailable';
|
||||
import compact from 'lodash/compact';
|
||||
import sum from 'lodash/sum';
|
||||
import BigNumber from 'bignumber.js';
|
||||
|
||||
/**
|
||||
* Gets the funds for given public key and required min for
|
||||
@@ -24,14 +24,16 @@ export const useFundsAvailable = (pubKey?: string) => {
|
||||
? compact(data.party?.accountsConnection?.edges?.map((e) => e?.node))
|
||||
: undefined;
|
||||
const requiredFunds = data
|
||||
? BigInt(data.networkParameter?.value || '0')
|
||||
? BigNumber(data.networkParameter?.value || '0')
|
||||
: undefined;
|
||||
|
||||
const sumOfFunds = sum(
|
||||
fundsAvailable?.filter((fa) => fa.balance).map((fa) => BigInt(fa.balance))
|
||||
);
|
||||
const sumOfFunds =
|
||||
fundsAvailable
|
||||
?.filter((fa) => fa.balance)
|
||||
.reduce((sum, fa) => sum.plus(BigNumber(fa.balance)), BigNumber(0)) ||
|
||||
BigNumber(0);
|
||||
|
||||
if (requiredFunds && sumOfFunds >= requiredFunds) {
|
||||
if (requiredFunds && sumOfFunds.isGreaterThanOrEqualTo(requiredFunds)) {
|
||||
stopPolling();
|
||||
}
|
||||
|
||||
@@ -41,6 +43,6 @@ export const useFundsAvailable = (pubKey?: string) => {
|
||||
isEligible:
|
||||
fundsAvailable != null &&
|
||||
requiredFunds != null &&
|
||||
sumOfFunds >= requiredFunds,
|
||||
sumOfFunds.isGreaterThanOrEqualTo(requiredFunds),
|
||||
};
|
||||
};
|
||||
|
||||
@@ -15,7 +15,7 @@ export const LandingBanner = () => {
|
||||
</div>
|
||||
<div className="pt-20 sm:w-[50%]">
|
||||
<h1 className="text-6xl font-alpha calt mb-10">
|
||||
{t('Vega community referral program')}
|
||||
{t('Vega community referrals')}
|
||||
</h1>
|
||||
<p className="text-lg mb-1">
|
||||
{t(
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
import { DocsLinks } from '@vegaprotocol/environment';
|
||||
import { ExternalLink, Tooltip } from '@vegaprotocol/ui-toolkit';
|
||||
import { useT } from '../../lib/use-t';
|
||||
|
||||
export const QUSDTooltip = () => {
|
||||
const t = useT();
|
||||
return (
|
||||
<Tooltip
|
||||
description={
|
||||
<>
|
||||
<p className="mb-1">
|
||||
{t(
|
||||
'qUSD provides a rough USD equivalent of balances across all assets using the value of "Quantum" for that asset'
|
||||
)}
|
||||
</p>
|
||||
{DocsLinks && (
|
||||
<ExternalLink href={DocsLinks.QUANTUM}>
|
||||
{t('Find out more')}
|
||||
</ExternalLink>
|
||||
)}
|
||||
</>
|
||||
}
|
||||
underline={true}
|
||||
>
|
||||
<span>{t('qUSD')}</span>
|
||||
</Tooltip>
|
||||
);
|
||||
};
|
||||
@@ -4,8 +4,6 @@ import {
|
||||
VegaIcon,
|
||||
VegaIconNames,
|
||||
truncateMiddle,
|
||||
ExternalLink,
|
||||
Tooltip,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||
@@ -28,10 +26,10 @@ import sortBy from 'lodash/sortBy';
|
||||
import { useCallback, useLayoutEffect, useMemo, useRef, useState } from 'react';
|
||||
import { useCurrentEpochInfoQuery } from './hooks/__generated__/Epoch';
|
||||
import BigNumber from 'bignumber.js';
|
||||
import { DocsLinks } from '@vegaprotocol/environment';
|
||||
import { useT, ns } from '../../lib/use-t';
|
||||
import { Trans } from 'react-i18next';
|
||||
import { ApplyCodeForm, ApplyCodeFormContainer } from './apply-code-form';
|
||||
import { QUSDTooltip } from './qusd-tooltip';
|
||||
|
||||
export const ReferralStatistics = () => {
|
||||
const { pubKey } = useVegaWallet();
|
||||
@@ -121,7 +119,7 @@ export const useStats = ({
|
||||
: 1;
|
||||
const finalCommissionValue = isNaN(multiplier)
|
||||
? baseCommissionValue
|
||||
: multiplier * baseCommissionValue;
|
||||
: new BigNumber(multiplier).times(baseCommissionValue).toNumber();
|
||||
|
||||
const discountFactorValue = refereeStats?.discountFactor
|
||||
? Number(refereeStats.discountFactor)
|
||||
@@ -214,6 +212,7 @@ export const Statistics = ({
|
||||
).toString(),
|
||||
}
|
||||
)}
|
||||
testId="base-commission-rate"
|
||||
overrideWithNoProgram={!details}
|
||||
>
|
||||
{baseCommissionValue * 100}%
|
||||
@@ -223,6 +222,7 @@ export const Statistics = ({
|
||||
const stakingMultiplierTile = (
|
||||
<StatTile
|
||||
title={t('Staking multiplier')}
|
||||
testId="staking-multiplier"
|
||||
description={
|
||||
<span
|
||||
className={classNames({
|
||||
@@ -242,24 +242,31 @@ export const Statistics = ({
|
||||
{multiplier || t('None')}
|
||||
</StatTile>
|
||||
);
|
||||
const baseCommissionFormatted = BigNumber(baseCommissionValue)
|
||||
.times(100)
|
||||
.toString();
|
||||
const finalCommissionFormatted = new BigNumber(finalCommissionValue)
|
||||
.times(100)
|
||||
.toString();
|
||||
const finalCommissionTile = (
|
||||
<StatTile
|
||||
title={t('Final commission rate')}
|
||||
description={
|
||||
!isNaN(multiplier)
|
||||
? `(${baseCommissionValue * 100}% ⨉ ${multiplier} = ${
|
||||
finalCommissionValue * 100
|
||||
}%)`
|
||||
? `(${baseCommissionFormatted}% ⨉ ${multiplier} = ${finalCommissionFormatted}%)`
|
||||
: undefined
|
||||
}
|
||||
testId="final-commission-rate"
|
||||
overrideWithNoProgram={!details}
|
||||
>
|
||||
{finalCommissionValue * 100}%
|
||||
{finalCommissionFormatted}%
|
||||
</StatTile>
|
||||
);
|
||||
const numberOfTradersValue = data.referees.length;
|
||||
const numberOfTradersTile = (
|
||||
<StatTile title={t('Number of traders')}>{numberOfTradersValue}</StatTile>
|
||||
<StatTile title={t('Number of traders')} testId="number-of-traders">
|
||||
{numberOfTradersValue}
|
||||
</StatTile>
|
||||
);
|
||||
|
||||
const codeTile = (
|
||||
@@ -274,6 +281,7 @@ export const Statistics = ({
|
||||
title={t('myVolume', 'My volume (last {{count}} epochs)', {
|
||||
count: details?.windowLength || DEFAULT_AGGREGATION_DAYS,
|
||||
})}
|
||||
testId="my-volume"
|
||||
overrideWithNoProgram={!details}
|
||||
>
|
||||
{compactNumFormat.format(referrerVolumeValue)}
|
||||
@@ -289,6 +297,7 @@ export const Statistics = ({
|
||||
count: details?.windowLength || DEFAULT_AGGREGATION_DAYS,
|
||||
})}
|
||||
description={<QUSDTooltip />}
|
||||
testId="total-commission"
|
||||
>
|
||||
{getNumberFormat(0).format(Number(totalCommissionValue))}
|
||||
</StatTile>
|
||||
@@ -314,6 +323,7 @@ export const Statistics = ({
|
||||
const currentBenefitTierTile = (
|
||||
<StatTile
|
||||
title={t('Current tier')}
|
||||
testId="current-tier"
|
||||
description={
|
||||
nextBenefitTierValue?.tier
|
||||
? t('(Next tier: {{nextTier}})', {
|
||||
@@ -329,7 +339,11 @@ export const Statistics = ({
|
||||
</StatTile>
|
||||
);
|
||||
const discountFactorTile = (
|
||||
<StatTile title={t('Discount')} overrideWithNoProgram={!details}>
|
||||
<StatTile
|
||||
title={t('Discount')}
|
||||
testId="discount"
|
||||
overrideWithNoProgram={!details}
|
||||
>
|
||||
{isApplyCodePreview && benefitTiers.length >= 1
|
||||
? benefitTiers[0].discountFactor * 100
|
||||
: discountFactorValue * 100}
|
||||
@@ -345,23 +359,34 @@ export const Statistics = ({
|
||||
count: details?.windowLength,
|
||||
}
|
||||
)}
|
||||
testId="combined-volume"
|
||||
overrideWithNoProgram={!details}
|
||||
>
|
||||
{compactNumFormat.format(runningVolumeValue)}
|
||||
</StatTile>
|
||||
);
|
||||
const epochsTile = (
|
||||
<StatTile title={t('Epochs in set')}>{epochsValue}</StatTile>
|
||||
<StatTile title={t('Epochs in set')} testId="epochs-in-set">
|
||||
{epochsValue}
|
||||
</StatTile>
|
||||
);
|
||||
const nextTierVolumeTile = (
|
||||
<StatTile title={t('Volume to next tier')} overrideWithNoProgram={!details}>
|
||||
<StatTile
|
||||
title={t('Volume to next tier')}
|
||||
testId="vol-to-next-tier"
|
||||
overrideWithNoProgram={!details}
|
||||
>
|
||||
{nextBenefitTierVolumeValue <= 0
|
||||
? '0'
|
||||
: compactNumFormat.format(nextBenefitTierVolumeValue)}
|
||||
</StatTile>
|
||||
);
|
||||
const nextTierEpochsTile = (
|
||||
<StatTile title={t('Epochs to next tier')} overrideWithNoProgram={!details}>
|
||||
<StatTile
|
||||
title={t('Epochs to next tier')}
|
||||
testId="epochs-to-next-tier"
|
||||
overrideWithNoProgram={!details}
|
||||
>
|
||||
{nextBenefitTierEpochsValue <= 0 ? '0' : nextBenefitTierEpochsValue}
|
||||
</StatTile>
|
||||
);
|
||||
@@ -515,28 +540,3 @@ export const RefereesTable = ({
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export const QUSDTooltip = () => {
|
||||
const t = useT();
|
||||
return (
|
||||
<Tooltip
|
||||
description={
|
||||
<>
|
||||
<p className="mb-1">
|
||||
{t(
|
||||
'qUSD provides a rough USD equivalent of balances across all assets using the value of "Quantum" for that asset'
|
||||
)}
|
||||
</p>
|
||||
{DocsLinks && (
|
||||
<ExternalLink href={DocsLinks.QUANTUM}>
|
||||
{t('Find out more')}
|
||||
</ExternalLink>
|
||||
)}
|
||||
</>
|
||||
}
|
||||
underline={true}
|
||||
>
|
||||
<span>{t('qUSD')}</span>
|
||||
</Tooltip>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -159,11 +159,11 @@ export const TiersContainer = () => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<h2 className="text-3xl mt-10">{t('Current Program Details')}</h2>
|
||||
<h2 className="text-3xl mt-10">{t('Current program details')}</h2>
|
||||
{details?.id && (
|
||||
<p>
|
||||
<Trans
|
||||
defaults="As a result of <0>{{proposal}}</0> the program below is currently active on the Vega network."
|
||||
defaults="As a result of governance proposal <0>{{proposal}}</0> the program below is currently active on the Vega network."
|
||||
values={{ proposal: truncateMiddle(details.id) }}
|
||||
components={[
|
||||
<ExternalLink
|
||||
|
||||
@@ -32,6 +32,7 @@ export const Tile = ({
|
||||
|
||||
type StatTileProps = {
|
||||
title: string;
|
||||
testId?: string;
|
||||
description?: ReactNode;
|
||||
children?: ReactNode;
|
||||
overrideWithNoProgram?: boolean;
|
||||
@@ -40,6 +41,7 @@ export const StatTile = ({
|
||||
title,
|
||||
description,
|
||||
children,
|
||||
testId,
|
||||
overrideWithNoProgram = false,
|
||||
}: StatTileProps) => {
|
||||
if (overrideWithNoProgram) {
|
||||
@@ -47,10 +49,15 @@ export const StatTile = ({
|
||||
}
|
||||
return (
|
||||
<Tile>
|
||||
<h3 className="mb-1 text-sm text-vega-clight-100 dark:text-vega-cdark-100 calt">
|
||||
<h3
|
||||
data-testid={testId}
|
||||
className="mb-1 text-sm text-vega-clight-100 dark:text-vega-cdark-100 calt"
|
||||
>
|
||||
{title}
|
||||
</h3>
|
||||
<div className="text-5xl text-left">{children}</div>
|
||||
<div data-testid={`${testId}-value`} className="text-5xl text-left">
|
||||
{children}
|
||||
</div>
|
||||
{description && (
|
||||
<div className="text-sm text-left text-vega-clight-100 dark:text-vega-cdark-100">
|
||||
{description}
|
||||
|
||||
@@ -13,6 +13,7 @@ import { ViewType, useSidebar } from '../sidebar';
|
||||
import { useMarketClickHandler } from '../../lib/hooks/use-market-click-handler';
|
||||
import { useGetCurrentRouteId } from '../../lib/hooks/use-get-current-route-id';
|
||||
import { useT } from '../../lib/use-t';
|
||||
import { usePersistentDepositStore } from '@vegaprotocol/deposits';
|
||||
|
||||
export const AccountsContainer = ({
|
||||
pinnedAsset,
|
||||
@@ -25,6 +26,7 @@ export const AccountsContainer = ({
|
||||
const { open: openAssetDetailsDialog } = useAssetDetailsDialogStore();
|
||||
const currentRouteId = useGetCurrentRouteId();
|
||||
const setViews = useSidebar((store) => store.setViews);
|
||||
const setDepositAsset = usePersistentDepositStore((store) => store.saveValue);
|
||||
|
||||
const gridStore = useAccountStore((store) => store.gridStore);
|
||||
const updateGridStore = useAccountStore((store) => store.updateGridStore);
|
||||
@@ -55,7 +57,10 @@ export const AccountsContainer = ({
|
||||
setViews({ type: ViewType.Withdraw, assetId }, currentRouteId);
|
||||
}}
|
||||
onClickDeposit={(assetId) => {
|
||||
setViews({ type: ViewType.Deposit, assetId }, currentRouteId);
|
||||
setViews({ type: ViewType.Deposit }, currentRouteId);
|
||||
if (assetId) {
|
||||
setDepositAsset({ assetId });
|
||||
}
|
||||
}}
|
||||
onClickTransfer={(assetId) => {
|
||||
setViews({ type: ViewType.Transfer, assetId }, currentRouteId);
|
||||
@@ -68,7 +73,7 @@ export const AccountsContainer = ({
|
||||
);
|
||||
};
|
||||
|
||||
const useAccountStore = create<DataGridSlice>()(
|
||||
export const useAccountStore = create<DataGridSlice>()(
|
||||
persist(createDataGridSlice, {
|
||||
name: 'vega_accounts_store',
|
||||
})
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
import { GridSettings } from '../grid-settings/grid-settings';
|
||||
import { useAccountStore } from './accounts-container';
|
||||
|
||||
export const AccountsSettings = () => (
|
||||
<GridSettings
|
||||
updateGridStore={useAccountStore((store) => store.updateGridStore)}
|
||||
/>
|
||||
);
|
||||
@@ -1 +1,2 @@
|
||||
export * from './accounts-container';
|
||||
export * from './accounts-settings';
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { ChartContainer } from './chart-container';
|
||||
import { useChartSettingsStore } from './use-chart-settings';
|
||||
import { useEnvironment } from '@vegaprotocol/environment';
|
||||
|
||||
jest.mock('@vegaprotocol/candles-chart', () => ({
|
||||
...jest.requireActual('@vegaprotocol/candles-chart'),
|
||||
CandlesChartContainer: ({ marketId }: { marketId: string }) => (
|
||||
<div data-testid="pennant">{marketId}</div>
|
||||
),
|
||||
}));
|
||||
|
||||
jest.mock('@vegaprotocol/trading-view', () => ({
|
||||
...jest.requireActual('@vegaprotocol/trading-view'),
|
||||
TradingViewContainer: ({ marketId }: { marketId: string }) => (
|
||||
<div data-testid="tradingview">{marketId}</div>
|
||||
),
|
||||
}));
|
||||
|
||||
describe('ChartContainer', () => {
|
||||
it('renders pennant if no library path is set', () => {
|
||||
useChartSettingsStore.setState({
|
||||
chartlib: 'tradingview',
|
||||
});
|
||||
|
||||
useEnvironment.setState({
|
||||
CHARTING_LIBRARY_PATH: undefined,
|
||||
CHARTING_LIBRARY_HASH: undefined,
|
||||
});
|
||||
|
||||
const marketId = 'market-id';
|
||||
|
||||
render(<ChartContainer marketId={marketId} />);
|
||||
|
||||
expect(screen.getByTestId('pennant')).toHaveTextContent(marketId);
|
||||
});
|
||||
|
||||
it('renders trading view if library path is set', () => {
|
||||
useChartSettingsStore.setState({
|
||||
chartlib: 'tradingview',
|
||||
});
|
||||
|
||||
useEnvironment.setState({
|
||||
CHARTING_LIBRARY_PATH: 'dummy-path',
|
||||
CHARTING_LIBRARY_HASH: 'hash',
|
||||
});
|
||||
|
||||
const marketId = 'market-id';
|
||||
|
||||
render(<ChartContainer marketId={marketId} />);
|
||||
|
||||
expect(screen.getByTestId('tradingview')).toHaveTextContent(marketId);
|
||||
});
|
||||
|
||||
it('renders pennant chart if stored in settings', () => {
|
||||
useChartSettingsStore.setState({
|
||||
chartlib: 'pennant',
|
||||
});
|
||||
|
||||
const marketId = 'market-id';
|
||||
|
||||
render(<ChartContainer marketId={marketId} />);
|
||||
|
||||
expect(screen.getByTestId('pennant')).toHaveTextContent(marketId);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,120 @@
|
||||
import invert from 'lodash/invert';
|
||||
import { type Interval } from '@vegaprotocol/types';
|
||||
import {
|
||||
TradingViewContainer,
|
||||
ALLOWED_TRADINGVIEW_HOSTNAMES,
|
||||
TRADINGVIEW_INTERVAL_MAP,
|
||||
} from '@vegaprotocol/trading-view';
|
||||
import {
|
||||
CandlesChartContainer,
|
||||
PENNANT_INTERVAL_MAP,
|
||||
} from '@vegaprotocol/candles-chart';
|
||||
import { useEnvironment } from '@vegaprotocol/environment';
|
||||
import { useChartSettings, STUDY_SIZE } from './use-chart-settings';
|
||||
|
||||
/**
|
||||
* Renders either the pennant chart or the tradingview chart
|
||||
*/
|
||||
export const ChartContainer = ({ marketId }: { marketId: string }) => {
|
||||
const { CHARTING_LIBRARY_PATH, CHARTING_LIBRARY_HASH } = useEnvironment();
|
||||
|
||||
const {
|
||||
chartlib,
|
||||
interval,
|
||||
chartType,
|
||||
overlays,
|
||||
studies,
|
||||
studySizes,
|
||||
tradingViewStudies,
|
||||
setInterval,
|
||||
setStudies,
|
||||
setStudySizes,
|
||||
setOverlays,
|
||||
setTradingViewStudies,
|
||||
} = useChartSettings();
|
||||
|
||||
const pennantChart = (
|
||||
<CandlesChartContainer
|
||||
marketId={marketId}
|
||||
interval={toPennantInterval(interval)}
|
||||
chartType={chartType}
|
||||
overlays={overlays}
|
||||
studies={studies}
|
||||
studySizes={studySizes}
|
||||
setStudySizes={setStudySizes}
|
||||
setStudies={setStudies}
|
||||
setOverlays={setOverlays}
|
||||
defaultStudySize={STUDY_SIZE}
|
||||
/>
|
||||
);
|
||||
|
||||
if (!ALLOWED_TRADINGVIEW_HOSTNAMES.includes(window.location.hostname)) {
|
||||
return pennantChart;
|
||||
}
|
||||
|
||||
if (!CHARTING_LIBRARY_PATH || !CHARTING_LIBRARY_HASH) {
|
||||
return pennantChart;
|
||||
}
|
||||
|
||||
switch (chartlib) {
|
||||
case 'tradingview': {
|
||||
return (
|
||||
<TradingViewContainer
|
||||
libraryPath={CHARTING_LIBRARY_PATH}
|
||||
libraryHash={CHARTING_LIBRARY_HASH}
|
||||
marketId={marketId}
|
||||
interval={toTradingViewResolution(interval)}
|
||||
studies={tradingViewStudies}
|
||||
onIntervalChange={(newInterval) => {
|
||||
setInterval(fromTradingViewResolution(newInterval));
|
||||
}}
|
||||
onAutoSaveNeeded={(data: { studies: string[] }) => {
|
||||
setTradingViewStudies(data.studies);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
case 'pennant': {
|
||||
return pennantChart;
|
||||
}
|
||||
default: {
|
||||
throw new Error('invalid chart lib');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const toTradingViewResolution = (interval: Interval) => {
|
||||
const resolution = TRADINGVIEW_INTERVAL_MAP[interval];
|
||||
|
||||
if (!resolution) {
|
||||
throw new Error(
|
||||
`failed to convert interval: ${interval} to valid resolution`
|
||||
);
|
||||
}
|
||||
|
||||
return resolution;
|
||||
};
|
||||
|
||||
const fromTradingViewResolution = (resolution: string) => {
|
||||
const interval = invert(TRADINGVIEW_INTERVAL_MAP)[resolution];
|
||||
|
||||
if (!interval) {
|
||||
throw new Error(
|
||||
`failed to convert resolution: ${resolution} to valid interval`
|
||||
);
|
||||
}
|
||||
|
||||
return interval as Interval;
|
||||
};
|
||||
|
||||
const toPennantInterval = (interval: Interval) => {
|
||||
const pennantInterval = PENNANT_INTERVAL_MAP[interval];
|
||||
|
||||
if (!pennantInterval) {
|
||||
throw new Error(
|
||||
`failed to convert interval: ${interval} to valid pennant interval`
|
||||
);
|
||||
}
|
||||
|
||||
return pennantInterval;
|
||||
};
|
||||
@@ -0,0 +1,140 @@
|
||||
import { render, screen, within } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import { ChartMenu } from './chart-menu';
|
||||
import {
|
||||
useChartSettingsStore,
|
||||
DEFAULT_CHART_SETTINGS,
|
||||
} from './use-chart-settings';
|
||||
import { Overlay, Study, overlayLabels, studyLabels } from 'pennant';
|
||||
import { useEnvironment } from '@vegaprotocol/environment';
|
||||
|
||||
describe('ChartMenu', () => {
|
||||
it('doesnt show trading view option if library path undefined', () => {
|
||||
useEnvironment.setState({ CHARTING_LIBRARY_PATH: undefined });
|
||||
|
||||
render(<ChartMenu />);
|
||||
|
||||
expect(
|
||||
screen.queryByRole('button', { name: 'TradingView' })
|
||||
).not.toBeInTheDocument();
|
||||
expect(
|
||||
screen.queryByRole('button', { name: 'Vega chart' })
|
||||
).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('can switch between charts if library path', async () => {
|
||||
useEnvironment.setState({ CHARTING_LIBRARY_PATH: 'dummy' });
|
||||
|
||||
render(<ChartMenu />);
|
||||
|
||||
await userEvent.click(screen.getByRole('button', { name: 'TradingView' }));
|
||||
expect(useChartSettingsStore.getState().chartlib).toEqual('tradingview');
|
||||
|
||||
await userEvent.click(screen.getByRole('button', { name: 'Vega chart' }));
|
||||
expect(useChartSettingsStore.getState().chartlib).toEqual('pennant');
|
||||
});
|
||||
|
||||
describe('tradingview', () => {
|
||||
beforeEach(() => {
|
||||
useEnvironment.setState({ CHARTING_LIBRARY_PATH: 'dummy-path' });
|
||||
|
||||
// clear store each time to avoid conditional testing of defaults
|
||||
useChartSettingsStore.setState({
|
||||
chartlib: 'tradingview',
|
||||
});
|
||||
});
|
||||
|
||||
it('only shows chartlib switch and attribution', () => {
|
||||
render(<ChartMenu />);
|
||||
|
||||
const buttons = screen.getAllByRole('button');
|
||||
expect(buttons).toHaveLength(1);
|
||||
expect(buttons[0]).toHaveTextContent('Vega chart');
|
||||
|
||||
expect(screen.getByText('Chart by')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe('pennant', () => {
|
||||
const openDropdown = async () => {
|
||||
await userEvent.click(
|
||||
screen.getByRole('button', {
|
||||
name: 'Indicators',
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
// clear store each time to avoid conditional testing of defaults
|
||||
useChartSettingsStore.setState({
|
||||
chartlib: 'pennant',
|
||||
overlays: [],
|
||||
studies: [],
|
||||
});
|
||||
});
|
||||
|
||||
it.each(Object.values(Overlay))('can set %s overlay', async (overlay) => {
|
||||
render(<ChartMenu />);
|
||||
|
||||
await openDropdown();
|
||||
|
||||
const menu = within(await screen.findByRole('menu'));
|
||||
|
||||
await userEvent.click(menu.getByText(overlayLabels[overlay as Overlay]));
|
||||
|
||||
// re-open the dropdown
|
||||
await openDropdown();
|
||||
|
||||
expect(
|
||||
screen.getByText(overlayLabels[overlay as Overlay])
|
||||
).toHaveAttribute('data-state', 'checked');
|
||||
});
|
||||
|
||||
it.each(Object.values(Study))('can set %s study', async (study) => {
|
||||
render(<ChartMenu />);
|
||||
|
||||
await openDropdown();
|
||||
|
||||
const menu = within(await screen.findByRole('menu'));
|
||||
|
||||
await userEvent.click(menu.getByText(studyLabels[study as Study]));
|
||||
|
||||
// re-open the dropdown
|
||||
await openDropdown();
|
||||
|
||||
expect(screen.getByText(studyLabels[study as Study])).toHaveAttribute(
|
||||
'data-state',
|
||||
'checked'
|
||||
);
|
||||
});
|
||||
|
||||
it('should render with the correct default studies and overlays', async () => {
|
||||
useChartSettingsStore.setState({
|
||||
...DEFAULT_CHART_SETTINGS,
|
||||
chartlib: 'pennant',
|
||||
});
|
||||
|
||||
render(<ChartMenu />);
|
||||
|
||||
await userEvent.click(
|
||||
screen.getByRole('button', {
|
||||
name: 'Indicators',
|
||||
})
|
||||
);
|
||||
const menu = within(await screen.findByRole('menu'));
|
||||
|
||||
expect(menu.getByText(studyLabels.volume)).toHaveAttribute(
|
||||
'data-state',
|
||||
'checked'
|
||||
);
|
||||
expect(menu.getByText(studyLabels.macd)).toHaveAttribute(
|
||||
'data-state',
|
||||
'checked'
|
||||
);
|
||||
expect(menu.getByText(overlayLabels.movingAverage)).toHaveAttribute(
|
||||
'data-state',
|
||||
'checked'
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
+85
-14
@@ -1,14 +1,12 @@
|
||||
import 'pennant/dist/style.css';
|
||||
import {
|
||||
ChartType,
|
||||
Interval,
|
||||
Overlay,
|
||||
Study,
|
||||
chartTypeLabels,
|
||||
intervalLabels,
|
||||
overlayLabels,
|
||||
studyLabels,
|
||||
} from 'pennant';
|
||||
import { Trans } from 'react-i18next';
|
||||
import {
|
||||
TradingButton,
|
||||
TradingDropdown,
|
||||
@@ -20,10 +18,21 @@ import {
|
||||
TradingDropdownTrigger,
|
||||
Icon,
|
||||
} from '@vegaprotocol/ui-toolkit';
|
||||
import { type IconName } from '@blueprintjs/icons';
|
||||
import { IconNames } from '@blueprintjs/icons';
|
||||
import { useCandlesChartSettings } from './use-candles-chart-settings';
|
||||
import { useT } from './use-t';
|
||||
import { Interval } from '@vegaprotocol/types';
|
||||
import { useEnvironment } from '@vegaprotocol/environment';
|
||||
import { ALLOWED_TRADINGVIEW_HOSTNAMES } from '@vegaprotocol/trading-view';
|
||||
import { IconNames, type IconName } from '@blueprintjs/icons';
|
||||
import { useChartSettings } from './use-chart-settings';
|
||||
import { useT } from '../../lib/use-t';
|
||||
|
||||
const INTERVALS = [
|
||||
Interval.INTERVAL_I1M,
|
||||
Interval.INTERVAL_I5M,
|
||||
Interval.INTERVAL_I15M,
|
||||
Interval.INTERVAL_I1H,
|
||||
Interval.INTERVAL_I6H,
|
||||
Interval.INTERVAL_I1D,
|
||||
];
|
||||
|
||||
const chartTypeIcon = new Map<ChartType, IconName>([
|
||||
[ChartType.AREA, IconNames.TIMELINE_AREA_CHART],
|
||||
@@ -32,30 +41,46 @@ const chartTypeIcon = new Map<ChartType, IconName>([
|
||||
[ChartType.OHLC, IconNames.WATERFALL_CHART],
|
||||
]);
|
||||
|
||||
export const CandlesMenu = () => {
|
||||
export const ChartMenu = () => {
|
||||
const { CHARTING_LIBRARY_PATH } = useEnvironment();
|
||||
const {
|
||||
chartlib,
|
||||
interval,
|
||||
chartType,
|
||||
studies,
|
||||
overlays,
|
||||
setChartlib,
|
||||
setInterval,
|
||||
setType,
|
||||
setStudies,
|
||||
setOverlays,
|
||||
} = useCandlesChartSettings();
|
||||
} = useChartSettings();
|
||||
const t = useT();
|
||||
const triggerClasses = 'text-xs';
|
||||
|
||||
const contentAlign = 'end';
|
||||
const triggerClasses = 'text-xs';
|
||||
const triggerButtonProps = { size: 'extra-small' } as const;
|
||||
|
||||
return (
|
||||
const isPennant = chartlib === 'pennant';
|
||||
const commonMenuItems = (
|
||||
<TradingButton
|
||||
onClick={() => {
|
||||
setChartlib(isPennant ? 'tradingview' : 'pennant');
|
||||
}}
|
||||
size="extra-small"
|
||||
>
|
||||
{isPennant ? 'TradingView' : t('Vega chart')}
|
||||
</TradingButton>
|
||||
);
|
||||
|
||||
const pennantMenuItems = (
|
||||
<>
|
||||
<TradingDropdown
|
||||
trigger={
|
||||
<TradingDropdownTrigger className={triggerClasses}>
|
||||
<TradingButton {...triggerButtonProps}>
|
||||
{t('Interval: {{interval}}', {
|
||||
interval: intervalLabels[interval],
|
||||
interval: t(interval),
|
||||
})}
|
||||
</TradingButton>
|
||||
</TradingDropdownTrigger>
|
||||
@@ -68,13 +93,13 @@ export const CandlesMenu = () => {
|
||||
setInterval(value as Interval);
|
||||
}}
|
||||
>
|
||||
{Object.values(Interval).map((timeInterval) => (
|
||||
{INTERVALS.map((timeInterval) => (
|
||||
<TradingDropdownRadioItem
|
||||
key={timeInterval}
|
||||
inset
|
||||
value={timeInterval}
|
||||
>
|
||||
{intervalLabels[timeInterval]}
|
||||
{t(timeInterval)}
|
||||
<TradingDropdownItemIndicator />
|
||||
</TradingDropdownRadioItem>
|
||||
))}
|
||||
@@ -158,4 +183,50 @@ export const CandlesMenu = () => {
|
||||
</TradingDropdown>
|
||||
</>
|
||||
);
|
||||
|
||||
const tradingViewMenuItems = (
|
||||
<p className="text-xs mr-2 whitespace-nowrap">
|
||||
<Trans
|
||||
i18nKey="Chart by <0>TradingView</0>"
|
||||
components={[
|
||||
// eslint-disable-next-line
|
||||
<a
|
||||
className="underline"
|
||||
target="_blank"
|
||||
href="https://www.tradingview.com"
|
||||
/>,
|
||||
]}
|
||||
/>
|
||||
</p>
|
||||
);
|
||||
|
||||
if (!ALLOWED_TRADINGVIEW_HOSTNAMES.includes(window.location.hostname)) {
|
||||
return pennantMenuItems;
|
||||
}
|
||||
|
||||
if (!CHARTING_LIBRARY_PATH) {
|
||||
return pennantMenuItems;
|
||||
}
|
||||
|
||||
switch (chartlib) {
|
||||
case 'tradingview': {
|
||||
return (
|
||||
<>
|
||||
{tradingViewMenuItems}
|
||||
{commonMenuItems}
|
||||
</>
|
||||
);
|
||||
}
|
||||
case 'pennant': {
|
||||
return (
|
||||
<>
|
||||
{pennantMenuItems}
|
||||
{commonMenuItems}
|
||||
</>
|
||||
);
|
||||
}
|
||||
default: {
|
||||
throw new Error('invalid chart lib');
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,2 @@
|
||||
export { ChartContainer } from './chart-container';
|
||||
export { ChartMenu } from './chart-menu';
|
||||
+31
-8
@@ -1,18 +1,23 @@
|
||||
import { getValidItem, getValidSubset } from '@vegaprotocol/react-helpers';
|
||||
import { ChartType, Interval, Study } from 'pennant';
|
||||
import { Overlay } from 'pennant';
|
||||
import { ChartType, Overlay, Study } from 'pennant';
|
||||
import { create } from 'zustand';
|
||||
import { persist } from 'zustand/middleware';
|
||||
import { immer } from 'zustand/middleware/immer';
|
||||
import { Interval } from '@vegaprotocol/types';
|
||||
import { getValidItem, getValidSubset } from '@vegaprotocol/react-helpers';
|
||||
|
||||
type StudySizes = { [S in Study]?: number };
|
||||
export type Chartlib = 'pennant' | 'tradingview';
|
||||
|
||||
interface StoredSettings {
|
||||
chartlib: Chartlib;
|
||||
// For interval we use the enum from @vegaprotocol/types, this is to make mapping between different
|
||||
// chart types easier and more consistent
|
||||
interval: Interval;
|
||||
type: ChartType;
|
||||
overlays: Overlay[];
|
||||
studies: Study[];
|
||||
studySizes: StudySizes;
|
||||
tradingViewStudies: string[];
|
||||
}
|
||||
|
||||
export const STUDY_SIZE = 90;
|
||||
@@ -25,20 +30,24 @@ const STUDY_ORDER: Study[] = [
|
||||
];
|
||||
|
||||
export const DEFAULT_CHART_SETTINGS = {
|
||||
interval: Interval.I15M,
|
||||
chartlib: 'pennant' as const,
|
||||
interval: Interval.INTERVAL_I15M,
|
||||
type: ChartType.CANDLE,
|
||||
overlays: [Overlay.MOVING_AVERAGE],
|
||||
studies: [Study.MACD, Study.VOLUME],
|
||||
studySizes: {},
|
||||
tradingViewStudies: ['Volume'],
|
||||
};
|
||||
|
||||
export const useCandlesChartSettingsStore = create<
|
||||
export const useChartSettingsStore = create<
|
||||
StoredSettings & {
|
||||
setType: (type: ChartType) => void;
|
||||
setInterval: (interval: Interval) => void;
|
||||
setOverlays: (overlays?: Overlay[]) => void;
|
||||
setStudies: (studies?: Study[]) => void;
|
||||
setStudySizes: (sizes: number[]) => void;
|
||||
setChartlib: (lib: Chartlib) => void;
|
||||
setTradingViewStudies: (studies: string[]) => void;
|
||||
}
|
||||
>()(
|
||||
persist(
|
||||
@@ -81,6 +90,16 @@ export const useCandlesChartSettingsStore = create<
|
||||
});
|
||||
});
|
||||
},
|
||||
setChartlib: (lib) => {
|
||||
set((state) => {
|
||||
state.chartlib = lib;
|
||||
});
|
||||
},
|
||||
setTradingViewStudies: (studies: string[]) => {
|
||||
set((state) => {
|
||||
state.tradingViewStudies = studies;
|
||||
});
|
||||
},
|
||||
})),
|
||||
{
|
||||
name: 'vega_candles_chart_store',
|
||||
@@ -88,13 +107,13 @@ export const useCandlesChartSettingsStore = create<
|
||||
)
|
||||
);
|
||||
|
||||
export const useCandlesChartSettings = () => {
|
||||
const settings = useCandlesChartSettingsStore();
|
||||
export const useChartSettings = () => {
|
||||
const settings = useChartSettingsStore();
|
||||
|
||||
const interval: Interval = getValidItem(
|
||||
settings.interval,
|
||||
Object.values(Interval),
|
||||
Interval.I15M
|
||||
Interval.INTERVAL_I15M
|
||||
);
|
||||
|
||||
const chartType: ChartType = getValidItem(
|
||||
@@ -122,15 +141,19 @@ export const useCandlesChartSettings = () => {
|
||||
});
|
||||
|
||||
return {
|
||||
chartlib: settings.chartlib,
|
||||
interval,
|
||||
chartType,
|
||||
overlays,
|
||||
studies,
|
||||
studySizes,
|
||||
tradingViewStudies: settings.tradingViewStudies,
|
||||
setInterval: settings.setInterval,
|
||||
setType: settings.setType,
|
||||
setStudies: settings.setStudies,
|
||||
setOverlays: settings.setOverlays,
|
||||
setStudySizes: settings.setStudySizes,
|
||||
setChartlib: settings.setChartlib,
|
||||
setTradingViewStudies: settings.setTradingViewStudies,
|
||||
};
|
||||
};
|
||||
@@ -38,7 +38,7 @@ export const FillsContainer = () => {
|
||||
);
|
||||
};
|
||||
|
||||
const useFillsStore = create<DataGridSlice>()(
|
||||
export const useFillsStore = create<DataGridSlice>()(
|
||||
persist(createDataGridSlice, {
|
||||
name: 'vega_fills_store',
|
||||
})
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
import { GridSettings } from '../grid-settings/grid-settings';
|
||||
import { useFillsStore } from './fills-container';
|
||||
|
||||
export const FillsSettings = () => (
|
||||
<GridSettings
|
||||
updateGridStore={useFillsStore((store) => store.updateGridStore)}
|
||||
/>
|
||||
);
|
||||
@@ -1 +1,2 @@
|
||||
export * from './fills-container';
|
||||
export * from './fills-settings';
|
||||
|
||||
@@ -45,7 +45,7 @@ export const FundingPaymentsContainer = ({
|
||||
);
|
||||
};
|
||||
|
||||
const useFundingPaymentsStore = create<DataGridSlice>()(
|
||||
export const useFundingPaymentsStore = create<DataGridSlice>()(
|
||||
persist(createDataGridSlice, {
|
||||
name: 'vega_funding_payments_store',
|
||||
})
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
import { GridSettings } from '../grid-settings/grid-settings';
|
||||
import { useFundingPaymentsStore } from './funding-payments-container';
|
||||
|
||||
export const FundingPaymentsSettings = () => (
|
||||
<GridSettings
|
||||
updateGridStore={useFundingPaymentsStore((store) => store.updateGridStore)}
|
||||
/>
|
||||
);
|
||||
@@ -1 +1,2 @@
|
||||
export * from './funding-payments-container';
|
||||
export * from './funding-payments-settings';
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
import { useT } from '../../lib/use-t';
|
||||
import type { DataGridStore } from '../../stores/datagrid-store-slice';
|
||||
import { TradingButton as Button } from '@vegaprotocol/ui-toolkit';
|
||||
|
||||
export const GridSettings = ({
|
||||
updateGridStore,
|
||||
}: {
|
||||
updateGridStore: (gridStore: DataGridStore) => void;
|
||||
}) => {
|
||||
const t = useT();
|
||||
return (
|
||||
<Button
|
||||
onClick={() =>
|
||||
updateGridStore({
|
||||
columnState: undefined,
|
||||
filterModel: undefined,
|
||||
})
|
||||
}
|
||||
size="extra-small"
|
||||
>
|
||||
{t('Reset Columns')}
|
||||
</Button>
|
||||
);
|
||||
};
|
||||
@@ -5,7 +5,7 @@ import type { VegaWalletContextShape } from '@vegaprotocol/wallet';
|
||||
import { VegaWalletContext } from '@vegaprotocol/wallet';
|
||||
import { Navbar } from './navbar';
|
||||
import { useGlobalStore } from '../../stores';
|
||||
import { ENV, FLAGS } from '@vegaprotocol/environment';
|
||||
import { ENV, useFeatureFlags } from '@vegaprotocol/environment';
|
||||
|
||||
jest.mock('@vegaprotocol/proposals', () => ({
|
||||
ProtocolUpgradeCountdown: () => null,
|
||||
@@ -48,8 +48,7 @@ describe('Navbar', () => {
|
||||
|
||||
beforeAll(() => {
|
||||
useGlobalStore.setState({ marketId });
|
||||
const mockedFLAGS = jest.mocked(FLAGS);
|
||||
mockedFLAGS.REFERRALS = true;
|
||||
useFeatureFlags.setState({ flags: { REFERRALS: true } });
|
||||
const mockedENV = jest.mocked(ENV);
|
||||
mockedENV.VEGA_TOKEN_URL = 'governance';
|
||||
});
|
||||
|
||||
@@ -6,7 +6,7 @@ import {
|
||||
Networks,
|
||||
DApp,
|
||||
useLinks,
|
||||
FLAGS,
|
||||
useFeatureFlags,
|
||||
useEnvNameMapping,
|
||||
} from '@vegaprotocol/environment';
|
||||
import { useGlobalStore } from '../../stores';
|
||||
@@ -157,6 +157,7 @@ export const Navbar = ({
|
||||
* of the navigation
|
||||
*/
|
||||
const NavbarMenu = ({ onClick }: { onClick: () => void }) => {
|
||||
const featureFlags = useFeatureFlags((state) => state.flags);
|
||||
const t = useT();
|
||||
const envNameMapping = useEnvNameMapping();
|
||||
const { VEGA_ENV, VEGA_NETWORKS, GITHUB_FEEDBACK_URL } = useEnvironment();
|
||||
@@ -201,7 +202,7 @@ const NavbarMenu = ({ onClick }: { onClick: () => void }) => {
|
||||
{t('Portfolio')}
|
||||
</NavbarLink>
|
||||
</NavbarItem>
|
||||
{FLAGS.REFERRALS && (
|
||||
{featureFlags.REFERRALS && (
|
||||
<NavbarItem>
|
||||
<NavbarLink end={false} to={Links.REFERRALS()} onClick={onClick}>
|
||||
{t('Referrals')}
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
import { act, renderHook } from '@testing-library/react';
|
||||
import {
|
||||
FilterStatusValue,
|
||||
STORAGE_KEY,
|
||||
useOrderListGridState,
|
||||
} from './orders-container';
|
||||
import { STORAGE_KEY, useOrderListGridState } from './orders-container';
|
||||
import { Filter } from '@vegaprotocol/orders';
|
||||
import { OrderType } from '@vegaprotocol/types';
|
||||
|
||||
@@ -16,31 +12,6 @@ describe('useOrderListGridState', () => {
|
||||
return renderHook(() => useOrderListGridState(filter));
|
||||
};
|
||||
|
||||
it.each(Object.values(Filter))(
|
||||
'providers correct AgGrid filter for %s',
|
||||
(filter) => {
|
||||
const { result } = setup(filter);
|
||||
expect(typeof result.current.updateGridState).toBe('function');
|
||||
expect(result.current.gridState).toEqual({
|
||||
columnState: undefined,
|
||||
filterModel: {
|
||||
status: {
|
||||
value: FilterStatusValue[filter],
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
it('provides correct AgGrid filter for all', () => {
|
||||
const { result } = setup(undefined);
|
||||
expect(typeof result.current.updateGridState).toBe('function');
|
||||
expect(result.current.gridState).toEqual({
|
||||
columnState: undefined,
|
||||
filterModel: undefined,
|
||||
});
|
||||
});
|
||||
|
||||
it.each(Object.values(Filter))(
|
||||
'sets and stores column state and filters for %s',
|
||||
(filter) => {
|
||||
@@ -59,12 +30,7 @@ describe('useOrderListGridState', () => {
|
||||
|
||||
expect(result.current.gridState).toEqual({
|
||||
columnState: undefined,
|
||||
filterModel: {
|
||||
...filterModel,
|
||||
status: {
|
||||
value: FilterStatusValue[filter],
|
||||
},
|
||||
},
|
||||
filterModel,
|
||||
});
|
||||
|
||||
const columnState = [{ colId: 'status', width: 200 }];
|
||||
@@ -77,12 +43,7 @@ describe('useOrderListGridState', () => {
|
||||
|
||||
expect(result.current.gridState).toEqual({
|
||||
columnState,
|
||||
filterModel: {
|
||||
...filterModel,
|
||||
status: {
|
||||
value: FilterStatusValue[filter],
|
||||
},
|
||||
},
|
||||
filterModel,
|
||||
});
|
||||
|
||||
const storeKeyMap = {
|
||||
|
||||
@@ -9,6 +9,7 @@ import type { DataGridStore } from '../../stores/datagrid-store-slice';
|
||||
import { OrderStatus } from '@vegaprotocol/types';
|
||||
import { Links } from '../../lib/links';
|
||||
import { useT } from '../../lib/use-t';
|
||||
import { GridSettings } from '../grid-settings/grid-settings';
|
||||
|
||||
const resolveNoRowsMessage = (
|
||||
filter: Filter | undefined,
|
||||
@@ -38,6 +39,24 @@ export const FilterStatusValue = {
|
||||
[Filter.Rejected]: [OrderStatus.STATUS_REJECTED],
|
||||
};
|
||||
|
||||
export const DefaultFilterModel = {
|
||||
[Filter.Open]: {
|
||||
status: {
|
||||
value: FilterStatusValue[Filter.Open],
|
||||
},
|
||||
},
|
||||
[Filter.Closed]: {
|
||||
status: {
|
||||
value: FilterStatusValue[Filter.Closed],
|
||||
},
|
||||
},
|
||||
[Filter.Rejected]: {
|
||||
status: {
|
||||
value: FilterStatusValue[Filter.Rejected],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export interface OrderContainerProps {
|
||||
filter?: Filter;
|
||||
}
|
||||
@@ -54,7 +73,8 @@ export const OrdersContainer = ({ filter }: OrderContainerProps) => {
|
||||
(newState) => {
|
||||
updateGridState(filter, newState);
|
||||
},
|
||||
AUTO_SIZE_COLUMNS
|
||||
AUTO_SIZE_COLUMNS,
|
||||
filter && DefaultFilterModel[filter]
|
||||
);
|
||||
|
||||
if (!pubKey) {
|
||||
@@ -80,7 +100,7 @@ export const OrdersContainer = ({ filter }: OrderContainerProps) => {
|
||||
};
|
||||
|
||||
export const STORAGE_KEY = 'vega_order_list_store';
|
||||
const useOrderListStore = create<{
|
||||
export const useOrderListStore = create<{
|
||||
open: DataGridStore;
|
||||
closed: DataGridStore;
|
||||
rejected: DataGridStore;
|
||||
@@ -149,34 +169,19 @@ export const useOrderListGridState = (filter: Filter | undefined) => {
|
||||
case Filter.Open: {
|
||||
return {
|
||||
columnState: store.open.columnState,
|
||||
filterModel: {
|
||||
...store.open.filterModel,
|
||||
status: {
|
||||
value: FilterStatusValue[Filter.Open],
|
||||
},
|
||||
},
|
||||
filterModel: store.open.filterModel,
|
||||
};
|
||||
}
|
||||
case Filter.Closed: {
|
||||
return {
|
||||
columnState: store.closed.columnState,
|
||||
filterModel: {
|
||||
...store.closed.filterModel,
|
||||
status: {
|
||||
value: FilterStatusValue[Filter.Closed],
|
||||
},
|
||||
},
|
||||
filterModel: store.closed.filterModel,
|
||||
};
|
||||
}
|
||||
case Filter.Rejected: {
|
||||
return {
|
||||
columnState: store.rejected.columnState,
|
||||
filterModel: {
|
||||
...store.rejected.filterModel,
|
||||
status: {
|
||||
value: FilterStatusValue[Filter.Rejected],
|
||||
},
|
||||
},
|
||||
filterModel: store.rejected.filterModel,
|
||||
};
|
||||
}
|
||||
default: {
|
||||
@@ -187,3 +192,14 @@ export const useOrderListGridState = (filter: Filter | undefined) => {
|
||||
|
||||
return { gridState, updateGridState };
|
||||
};
|
||||
|
||||
export const OrdersSettings = ({ filter }: { filter?: Filter }) => {
|
||||
const updateGridState = useOrderListStore((state) => state.update);
|
||||
return (
|
||||
<GridSettings
|
||||
updateGridStore={(gridStore: DataGridStore) =>
|
||||
updateGridState(filter, gridStore)
|
||||
}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
export * from './positions-container';
|
||||
export * from './positions-settings';
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
import { GridSettings } from '../grid-settings/grid-settings';
|
||||
import { usePositionsStore } from './positions-container';
|
||||
|
||||
export const PositionsSettings = () => (
|
||||
<GridSettings
|
||||
updateGridStore={usePositionsStore((store) => store.updateGridStore)}
|
||||
/>
|
||||
);
|
||||
@@ -10,6 +10,18 @@ import { useTelemetryApproval } from '../../lib/hooks/use-telemetry-approval';
|
||||
import { useState, type ReactNode } from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { useT } from '../../lib/use-t';
|
||||
import { useFeatureFlags, type FeatureFlags } from '@vegaprotocol/environment';
|
||||
|
||||
export const FeatureFlagSwitch = ({ flag }: { flag: keyof FeatureFlags }) => {
|
||||
const flags = useFeatureFlags((state) => state.flags);
|
||||
const setFeatureFlag = useFeatureFlags((state) => state.setFeatureFlag);
|
||||
return (
|
||||
<Switch
|
||||
onCheckedChange={(checked) => setFeatureFlag(flag, !!checked)}
|
||||
checked={flags[flag]}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export const Settings = () => {
|
||||
const t = useT();
|
||||
@@ -120,7 +132,7 @@ const SettingsGroup = ({
|
||||
})}
|
||||
>
|
||||
<div className={classNames({ 'w-3/4': inline, 'mb-2': !inline })}>
|
||||
<label className="text-sm">{label}</label>
|
||||
<div className="text-sm">{label}</div>
|
||||
{helpText && <p className="text-xs text-muted">{helpText}</p>}
|
||||
</div>
|
||||
{children}
|
||||
|
||||
@@ -1 +1,3 @@
|
||||
export * from './stop-orders-container';
|
||||
|
||||
export * from './stop-orders-settings';
|
||||
|
||||
@@ -35,7 +35,7 @@ export const StopOrdersContainer = () => {
|
||||
);
|
||||
};
|
||||
|
||||
const useStopOrdersStore = create<DataGridSlice>()(
|
||||
export const useStopOrdersStore = create<DataGridSlice>()(
|
||||
persist(createDataGridSlice, {
|
||||
name: 'vega_stop_orders_store',
|
||||
})
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
import { GridSettings } from '../grid-settings/grid-settings';
|
||||
import { useStopOrdersStore } from './stop-orders-container';
|
||||
|
||||
export const StopOrdersSettings = () => (
|
||||
<GridSettings
|
||||
updateGridStore={useStopOrdersStore((store) => store.updateGridStore)}
|
||||
/>
|
||||
);
|
||||
@@ -1 +1,2 @@
|
||||
export * from './trades-container';
|
||||
export * from './trades-settings';
|
||||
|
||||
@@ -17,7 +17,7 @@ export const TradesContainer = ({ marketId }: TradesContainerProps) => {
|
||||
return <TradesManager marketId={marketId} gridProps={gridStoreCallbacks} />;
|
||||
};
|
||||
|
||||
const useTradesStore = create<DataGridSlice>()(
|
||||
export const useTradesStore = create<DataGridSlice>()(
|
||||
persist(createDataGridSlice, {
|
||||
name: 'vega_trades_store',
|
||||
})
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
import { GridSettings } from '../grid-settings/grid-settings';
|
||||
import { useTradesStore } from './trades-container';
|
||||
|
||||
export const TradesSettings = () => (
|
||||
<GridSettings
|
||||
updateGridStore={useTradesStore((store) => store.updateGridStore)}
|
||||
/>
|
||||
);
|
||||
@@ -10,6 +10,7 @@ import { positionsDataProvider } from '@vegaprotocol/positions';
|
||||
import { useGlobalStore } from '../../stores';
|
||||
|
||||
const ONBOARDING_STORAGE_KEY = 'vega_onboarding';
|
||||
|
||||
export const useOnboardingStore = create<{
|
||||
dialogOpen: boolean;
|
||||
walletDialogOpen: boolean;
|
||||
@@ -20,7 +21,7 @@ export const useOnboardingStore = create<{
|
||||
}>()(
|
||||
persist(
|
||||
(set) => ({
|
||||
dialogOpen: true,
|
||||
dialogOpen: false,
|
||||
walletDialogOpen: false,
|
||||
dismissed: false,
|
||||
dismiss: () => set({ dismissed: true }),
|
||||
|
||||
@@ -1,18 +1,30 @@
|
||||
import { useEffect } from 'react';
|
||||
import { matchPath, useLocation } from 'react-router-dom';
|
||||
import { Dialog, Intent } from '@vegaprotocol/ui-toolkit';
|
||||
import { useEnvironment } from '@vegaprotocol/environment';
|
||||
import { VegaConnectDialog } from '@vegaprotocol/wallet';
|
||||
import { useConnectors } from '../../lib/vega-connectors';
|
||||
import { useT } from '../../lib/use-t';
|
||||
import { Routes } from '../../lib/links';
|
||||
import { RiskMessage } from './risk-message';
|
||||
import { WelcomeDialogContent } from './welcome-dialog-content';
|
||||
import { useOnboardingStore } from './use-get-onboarding-step';
|
||||
import { VegaConnectDialog } from '@vegaprotocol/wallet';
|
||||
import { Connectors } from '../../lib/vega-connectors';
|
||||
import { RiskMessage } from './risk-message';
|
||||
import { useT } from '../../lib/use-t';
|
||||
import { ensureSuffix } from '@vegaprotocol/utils';
|
||||
|
||||
/**
|
||||
* A list of paths on which the welcome dialog should be omitted.
|
||||
*/
|
||||
const OMIT_ON_LIST = [ensureSuffix(Routes.REFERRALS, '/*')];
|
||||
|
||||
export const WelcomeDialog = () => {
|
||||
const { pathname } = useLocation();
|
||||
const t = useT();
|
||||
const { VEGA_ENV } = useEnvironment();
|
||||
const connectors = useConnectors();
|
||||
const dismissed = useOnboardingStore((store) => store.dismissed);
|
||||
const dialogOpen = useOnboardingStore((store) => store.dialogOpen);
|
||||
const dismiss = useOnboardingStore((store) => store.dismiss);
|
||||
const setDialogOpen = useOnboardingStore((store) => store.setDialogOpen);
|
||||
const walletDialogOpen = useOnboardingStore(
|
||||
(store) => store.walletDialogOpen
|
||||
);
|
||||
@@ -20,9 +32,19 @@ export const WelcomeDialog = () => {
|
||||
(store) => store.setWalletDialogOpen
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const shouldOmit = OMIT_ON_LIST.map((path) =>
|
||||
matchPath(path, pathname)
|
||||
).some((m) => !!m);
|
||||
|
||||
if (dismissed || shouldOmit) return;
|
||||
|
||||
setDialogOpen(true);
|
||||
}, [dismissed, pathname, setDialogOpen]);
|
||||
|
||||
const content = walletDialogOpen ? (
|
||||
<VegaConnectDialog
|
||||
connectors={Connectors}
|
||||
connectors={connectors}
|
||||
riskMessage={<RiskMessage />}
|
||||
onClose={() => setWalletDialogOpen(false)}
|
||||
contentOnly
|
||||
@@ -31,7 +53,12 @@ export const WelcomeDialog = () => {
|
||||
<WelcomeDialogContent />
|
||||
);
|
||||
|
||||
const onClose = walletDialogOpen ? () => setWalletDialogOpen(false) : dismiss;
|
||||
const onClose = walletDialogOpen
|
||||
? () => setWalletDialogOpen(false)
|
||||
: () => {
|
||||
setDialogOpen(false);
|
||||
dismiss();
|
||||
};
|
||||
|
||||
const title = walletDialogOpen ? null : (
|
||||
<span className="font-alpha calt" data-testid="welcome-title">
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
CONSOLE_IMAGE_NAME=vegaprotocol/trading:latest
|
||||
VEGA_VERSION=v0.73.8
|
||||
VEGA_VERSION=v0.73.9
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
CONSOLE_IMAGE_NAME=vegaprotocol/trading:develop
|
||||
VEGA_VERSION=v0.73.8
|
||||
VEGA_VERSION=v0.73.9
|
||||
|
||||
@@ -87,10 +87,9 @@ docker build -f docker/node-outside-docker.Dockerfile --build-arg APP=trading --
|
||||
|
||||
## Running Tests 🧪
|
||||
|
||||
Before running make sure the docker daemon is runnign so that the app can be served.
|
||||
Before running make sure the docker daemon is running.
|
||||
|
||||
To run a specific test, use the `-k` option followed by the name of the test.
|
||||
|
||||
Run all tests:
|
||||
|
||||
```bash
|
||||
@@ -109,6 +108,25 @@ Run from anywhere:
|
||||
yarn trading:test -- "test_name" -s --headed
|
||||
```
|
||||
|
||||
Run using your locally served console:
|
||||
|
||||
Within one terminal
|
||||
|
||||
```bash
|
||||
yarn nx build trading
|
||||
```
|
||||
|
||||
```bash
|
||||
yarn nx serve trading
|
||||
|
||||
```
|
||||
|
||||
Once console is served you can use the flag --local-server
|
||||
|
||||
```bash
|
||||
poetry run pytest -k "test_name" -s --headed --local-server
|
||||
```
|
||||
|
||||
## Running Tests in Parallel 🔢
|
||||
|
||||
To run tests in parallel, use the `--numprocesses auto` option. The `--dist loadfile` setting ensures that multiple runners are not assigned to a single test file.
|
||||
|
||||
@@ -6,23 +6,27 @@ from typing import Optional
|
||||
WalletConfig = namedtuple("WalletConfig", ["name", "passphrase"])
|
||||
ASSET_NAME = "tDAI"
|
||||
|
||||
|
||||
def wait_for_toast_confirmation(page: Page, timeout: int = 30000):
|
||||
page.wait_for_function("""
|
||||
document.querySelector('[data-testid="toast-content"]') &&
|
||||
document.querySelector('[data-testid="toast-content"]').innerText.includes('AWAITING CONFIRMATION')
|
||||
""", timeout=timeout)
|
||||
|
||||
|
||||
def create_and_faucet_wallet(
|
||||
vega: VegaServiceNull,
|
||||
wallet: WalletConfig,
|
||||
symbol: Optional[str] = None,
|
||||
amount: float = 1e4,
|
||||
|
||||
|
||||
):
|
||||
asset_id = vega.find_asset_id(symbol=symbol if symbol is not None else ASSET_NAME)
|
||||
asset_id = vega.find_asset_id(
|
||||
symbol=symbol if symbol is not None else ASSET_NAME)
|
||||
vega.create_key(wallet.name)
|
||||
vega.mint(wallet.name, asset_id, amount)
|
||||
|
||||
|
||||
def next_epoch(vega: VegaServiceNull):
|
||||
forwards = 0
|
||||
epoch_seq = vega.statistics().epoch_seq
|
||||
@@ -36,13 +40,34 @@ def next_epoch(vega: VegaServiceNull):
|
||||
vega.wait_fn(1)
|
||||
vega.wait_for_total_catchup()
|
||||
|
||||
|
||||
def truncate_middle(market_id, start=6, end=4):
|
||||
if len(market_id) < 11:
|
||||
return market_id
|
||||
return market_id[:start] + '\u2026' + market_id[-end:]
|
||||
|
||||
def change_keys(page: Page, vega:VegaServiceNull, key_name):
|
||||
|
||||
def change_keys(page: Page, vega: VegaServiceNull, key_name):
|
||||
page.get_by_test_id("manage-vega-wallet").click()
|
||||
page.get_by_test_id("key-" + vega.wallet.public_key(key_name)).click()
|
||||
page.click(f'data-testid=key-{vega.wallet.public_key(key_name)} >> .inline-flex')
|
||||
page.click(
|
||||
f'data-testid=key-{vega.wallet.public_key(key_name)} >> .inline-flex')
|
||||
page.reload()
|
||||
|
||||
|
||||
def forward_time(vega: VegaServiceNull, forward_epoch: bool = False):
|
||||
vega.wait_fn(1)
|
||||
vega.wait_for_total_catchup()
|
||||
|
||||
if forward_epoch:
|
||||
next_epoch(vega)
|
||||
|
||||
|
||||
# This is for when the element will initially load but contain an outdated value. It will wait for the element to contain the expected text, returning False after a timeout or exception
|
||||
def selector_contains_text(page: Page, selector, expected_text, timeout=5000):
|
||||
try:
|
||||
page.wait_for_selector(
|
||||
f'{selector} >> text={expected_text}', timeout=timeout)
|
||||
return True
|
||||
except:
|
||||
return False
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
from typing import List, Tuple, Optional
|
||||
from vega_sim.service import VegaService, PeggedOrder
|
||||
|
||||
|
||||
def submit_order(
|
||||
vega: VegaService,
|
||||
wallet_name: str,
|
||||
@@ -35,7 +36,7 @@ def submit_multiple_orders(
|
||||
submit_order(vega, wallet_name, market_id, side, volume, price)
|
||||
|
||||
|
||||
def submit_liquidity(vega: VegaService, wallet_name: str, market_id: str):
|
||||
def submit_liquidity(vega: VegaService, wallet_name: str, market_id: str, buy_vol=99, sell_vol=99, custom_price=None):
|
||||
vega.submit_simple_liquidity(
|
||||
key_name=wallet_name,
|
||||
market_id=market_id,
|
||||
@@ -51,7 +52,7 @@ def submit_liquidity(vega: VegaService, wallet_name: str, market_id: str):
|
||||
pegged_order=PeggedOrder(reference="PEGGED_REFERENCE_MID", offset=1),
|
||||
wait=False,
|
||||
time_in_force="TIME_IN_FORCE_GTC",
|
||||
volume=99,
|
||||
volume=buy_vol,
|
||||
)
|
||||
vega.submit_order(
|
||||
market_id=market_id,
|
||||
@@ -61,5 +62,5 @@ def submit_liquidity(vega: VegaService, wallet_name: str, market_id: str):
|
||||
pegged_order=PeggedOrder(reference="PEGGED_REFERENCE_MID", offset=1),
|
||||
wait=False,
|
||||
time_in_force="TIME_IN_FORCE_GTC",
|
||||
volume=99,
|
||||
)
|
||||
volume=sell_vol,
|
||||
)
|
||||
|
||||
@@ -7,7 +7,6 @@ import time
|
||||
import docker
|
||||
import http.server
|
||||
|
||||
|
||||
from contextlib import contextmanager
|
||||
from vega_sim.null_service import VegaServiceNull
|
||||
from playwright.sync_api import Browser, Page
|
||||
@@ -102,11 +101,23 @@ def init_vega(request=None):
|
||||
logger.info(f"Removing container {container.id}")
|
||||
container.remove()
|
||||
|
||||
|
||||
def pytest_addoption(parser):
|
||||
parser.addoption(
|
||||
"--local-server", action="store_true", default=False,
|
||||
help="Build and serve locally instead of using a container"
|
||||
)
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def local_server(pytestconfig):
|
||||
return pytestconfig.getoption("--local-server")
|
||||
|
||||
@contextmanager
|
||||
def init_page(vega: VegaServiceNull, browser: Browser, request: pytest.FixtureRequest):
|
||||
def init_page(vega: VegaServiceNull, browser: Browser, request: pytest.FixtureRequest, local_server: bool):
|
||||
server_port = "4200" if local_server else str(vega.console_port)
|
||||
with browser.new_context(
|
||||
viewport={"width": 1920, "height": 1080},
|
||||
base_url=f"http://localhost:{vega.console_port}",
|
||||
base_url=f"http://localhost:{server_port}",
|
||||
) as context, context.new_page() as page:
|
||||
context.tracing.start(screenshots=True, snapshots=True, sources=True)
|
||||
try:
|
||||
@@ -115,7 +126,7 @@ def init_page(vega: VegaServiceNull, browser: Browser, request: pytest.FixtureRe
|
||||
while attempts < 100:
|
||||
try:
|
||||
code = requests.get(
|
||||
f"http://localhost:{vega.console_port}/"
|
||||
f"http://localhost:{server_port}/"
|
||||
).status_code
|
||||
if code == 200:
|
||||
break
|
||||
@@ -161,8 +172,8 @@ def vega(request):
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def page(vega, browser, request):
|
||||
with init_page(vega, browser, request) as page_instance:
|
||||
def page(vega, browser, request, local_server):
|
||||
with init_page(vega, browser, request, local_server) as page_instance:
|
||||
yield page_instance
|
||||
|
||||
|
||||
|
||||
@@ -8,12 +8,17 @@ logger = logging.getLogger()
|
||||
mint_amount: float = 10e5
|
||||
market_name = "BTC:DAI_2023"
|
||||
|
||||
default_sell_orders = [[1, 110], [1, 105]]
|
||||
default_buy_orders = [[1, 90], [1, 95]]
|
||||
|
||||
|
||||
def setup_simple_market(
|
||||
vega: VegaService,
|
||||
approve_proposal=True,
|
||||
custom_market_name=market_name,
|
||||
custom_asset_name="tDAI",
|
||||
custom_asset_symbol="tDAI",
|
||||
custom_quantum=1
|
||||
):
|
||||
for wallet in wallets:
|
||||
vega.create_key(wallet.name)
|
||||
@@ -37,6 +42,7 @@ def setup_simple_market(
|
||||
symbol=custom_asset_symbol,
|
||||
decimals=5,
|
||||
max_faucet_amount=1e10,
|
||||
quantum=custom_quantum,
|
||||
)
|
||||
vega.wait_fn(1)
|
||||
vega.wait_for_total_catchup()
|
||||
@@ -111,16 +117,17 @@ def setup_simple_successor_market(
|
||||
return market_id
|
||||
|
||||
|
||||
def setup_opening_auction_market(vega: VegaService, market_id: str = None, **kwargs):
|
||||
if market_id is None or market_id not in vega.all_markets():
|
||||
def setup_opening_auction_market(vega: VegaService, market_id: str = None, buy_orders=default_buy_orders, sell_orders=default_sell_orders, add_liquidity=True, **kwargs):
|
||||
if not market_exists(vega, market_id):
|
||||
market_id = setup_simple_market(vega, **kwargs)
|
||||
|
||||
submit_liquidity(vega, MM_WALLET.name, market_id)
|
||||
if add_liquidity:
|
||||
submit_liquidity(vega, MM_WALLET.name, market_id)
|
||||
submit_multiple_orders(
|
||||
vega, MM_WALLET.name, market_id, "SIDE_SELL", [[1, 110], [1, 105]]
|
||||
vega, MM_WALLET.name, market_id, "SIDE_SELL", sell_orders
|
||||
)
|
||||
submit_multiple_orders(
|
||||
vega, MM_WALLET2.name, market_id, "SIDE_BUY", [[1, 90], [1, 95]]
|
||||
vega, MM_WALLET2.name, market_id, "SIDE_BUY", buy_orders
|
||||
)
|
||||
|
||||
vega.forward("10s")
|
||||
@@ -130,11 +137,22 @@ def setup_opening_auction_market(vega: VegaService, market_id: str = None, **kwa
|
||||
return market_id
|
||||
|
||||
|
||||
def setup_continuous_market(vega: VegaService, market_id: str = None, **kwargs):
|
||||
if market_id is None or market_id not in vega.all_markets():
|
||||
market_id = setup_opening_auction_market(vega, **kwargs)
|
||||
def market_exists(vega: VegaService, market_id: str):
|
||||
if market_id is None:
|
||||
return False
|
||||
all_markets = vega.all_markets()
|
||||
market_ids = [market.id for market in all_markets]
|
||||
return market_id in market_ids
|
||||
|
||||
submit_order(vega, "Key 1", market_id, "SIDE_BUY", 1, 110)
|
||||
|
||||
# Add sell orders and buy orders to put on the book
|
||||
def setup_continuous_market(vega: VegaService, market_id: str = None, buy_orders=default_buy_orders, sell_orders=default_sell_orders, add_liquidity=True, **kwargs):
|
||||
if not market_exists(vega, market_id) or buy_orders != default_buy_orders or sell_orders != default_sell_orders:
|
||||
market_id = setup_opening_auction_market(
|
||||
vega, market_id, buy_orders, sell_orders, add_liquidity, **kwargs)
|
||||
|
||||
submit_order(vega, "Key 1", market_id, "SIDE_BUY",
|
||||
sell_orders[0][0], sell_orders[0][1])
|
||||
|
||||
vega.forward("10s")
|
||||
vega.wait_fn(1)
|
||||
@@ -142,6 +160,7 @@ def setup_continuous_market(vega: VegaService, market_id: str = None, **kwargs):
|
||||
|
||||
return market_id
|
||||
|
||||
|
||||
def setup_perps_market(
|
||||
vega: VegaService,
|
||||
custom_asset_name="tDAI",
|
||||
@@ -210,7 +229,7 @@ def setup_perps_market(
|
||||
settlement_data_key=TERMINATE_WALLET.name,
|
||||
funding_payment_frequency_in_seconds=10,
|
||||
market_decimals=5,
|
||||
)
|
||||
)
|
||||
vega.wait_for_total_catchup()
|
||||
|
||||
submit_liquidity(vega, MM_WALLET.name, market_id)
|
||||
@@ -225,4 +244,4 @@ def setup_perps_market(
|
||||
vega.wait_fn(1)
|
||||
vega.wait_for_total_catchup()
|
||||
|
||||
return market_id
|
||||
return market_id
|
||||
|
||||
@@ -66,16 +66,17 @@ def test_iceberg_open_order(continuous_market, vega: VegaService, page: Page):
|
||||
page.wait_for_selector(".ag-center-cols-container .ag-row")
|
||||
|
||||
expect(
|
||||
page.locator(".ag-center-cols-container .ag-row [col-id='remaining']")
|
||||
page.locator(".ag-center-cols-container .ag-row [col-id='remaining']").first
|
||||
).to_have_text("99")
|
||||
expect(
|
||||
page.locator(".ag-center-cols-container .ag-row [col-id='size']")
|
||||
page.locator(".ag-center-cols-container .ag-row [col-id='size']").first
|
||||
).to_have_text("-102")
|
||||
page.pause()
|
||||
expect(
|
||||
page.locator(".ag-center-cols-container .ag-row [col-id='type'] ")
|
||||
page.locator(".ag-center-cols-container .ag-row [col-id='type'] ").first
|
||||
).to_have_text("Limit (Iceberg)")
|
||||
expect(
|
||||
page.locator(".ag-center-cols-container .ag-row [col-id='status']")
|
||||
page.locator(".ag-center-cols-container .ag-row [col-id='status']").first
|
||||
).to_have_text("Active")
|
||||
expect(page.get_by_test_id("price-10100000")).to_be_visible
|
||||
expect(page.get_by_test_id("ask-vol-10100000")).to_have_text("3")
|
||||
|
||||
@@ -17,8 +17,8 @@ def vega():
|
||||
|
||||
# setting up everything in this single fixture, as all of the tests need the same setup, so no point in creating separate ones
|
||||
@pytest.fixture(scope="module")
|
||||
def page(vega, browser, request):
|
||||
with init_page(vega, browser, request) as page:
|
||||
def page(vega, browser, request, local_server):
|
||||
with init_page(vega, browser, request, local_server) as page:
|
||||
setup_continuous_market(vega)
|
||||
risk_accepted_setup(page)
|
||||
page.goto("/")
|
||||
|
||||
@@ -12,8 +12,8 @@ def vega(request):
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def page(vega, browser, request):
|
||||
with init_page(vega, browser, request) as page:
|
||||
def page(vega, browser, request, local_server):
|
||||
with init_page(vega, browser, request, local_server) as page:
|
||||
risk_accepted_setup(page)
|
||||
page.goto("/#/markets/all")
|
||||
yield page
|
||||
|
||||
@@ -12,8 +12,8 @@ def vega():
|
||||
|
||||
# we can reuse single page instance in all tests
|
||||
@pytest.fixture(scope="module")
|
||||
def page(vega, browser, request):
|
||||
with init_page(vega, browser, request) as page:
|
||||
def page(vega, browser, request, local_server):
|
||||
with init_page(vega, browser, request, local_server) as page:
|
||||
yield page
|
||||
|
||||
|
||||
|
||||
@@ -15,11 +15,11 @@ def verify_data_grid(page: Page, data_test_id, expected_pattern):
|
||||
expect(
|
||||
page.locator(
|
||||
f'[data-testid^="tab-{data_test_id.lower()}"] >> .ag-center-cols-container .ag-row-first'
|
||||
)
|
||||
).first
|
||||
).to_be_visible()
|
||||
actual_text = page.locator(
|
||||
f'[data-testid^="tab-{data_test_id.lower()}"] >> .ag-center-cols-container .ag-row-first'
|
||||
).text_content()
|
||||
).first.text_content()
|
||||
lines = actual_text.strip().split("\n")
|
||||
for expected, actual in zip(expected_pattern, lines):
|
||||
# We are using regex so that we can run tests in different timezones.
|
||||
|
||||
@@ -223,8 +223,8 @@ def markets(vega: VegaService):
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def page(vega, browser, request):
|
||||
with init_page(vega, browser, request) as page:
|
||||
def page(vega, browser, request, local_server):
|
||||
with init_page(vega, browser, request, local_server) as page:
|
||||
risk_accepted_setup(page)
|
||||
auth_setup(vega, page)
|
||||
page.goto("/")
|
||||
|
||||
@@ -131,9 +131,10 @@ def test_perps_market_terminated(page: Page, vega: VegaService):
|
||||
vega.wait_for_total_catchup()
|
||||
|
||||
page.goto(f"/#/markets/{perpetual_market}")
|
||||
# TODO cahnge back to have text once bug #5465 is fixed
|
||||
expect(page.get_by_test_id("market-price")).to_have_text("Mark Price100.00")
|
||||
expect(page.get_by_test_id("market-change")).to_have_text("Change (24h)-")
|
||||
expect(page.get_by_test_id("market-volume")).to_have_text("Volume (24h)-")
|
||||
expect(page.get_by_test_id("market-change")).to_contain_text("Change (24h)")
|
||||
expect(page.get_by_test_id("market-volume")).to_contain_text("Volume (24h)")
|
||||
expect(page.get_by_test_id("market-trading-mode")).to_have_text("Trading modeNo trading")
|
||||
expect(page.get_by_test_id("market-state")).to_have_text("StatusClosed")
|
||||
expect(page.get_by_test_id("liquidity-supplied")).to_have_text("Liquidity supplied 0.00 (0.00%)")
|
||||
|
||||
@@ -0,0 +1,175 @@
|
||||
import pytest
|
||||
from playwright.sync_api import Page, expect
|
||||
from vega_sim.service import VegaService
|
||||
from conftest import init_vega
|
||||
from fixtures.market import setup_continuous_market, setup_simple_market
|
||||
from actions.utils import change_keys, create_and_faucet_wallet, forward_time, selector_contains_text
|
||||
from actions.vega import submit_order, submit_liquidity
|
||||
from wallet_config import MM_WALLET, PARTY_A, PARTY_B
|
||||
|
||||
SELL_ORDERS = [[1, 111], [1, 111], [1, 112], [1, 112], [
|
||||
1, 113], [1, 113], [1, 114], [1, 114], [1, 115], [1, 115]]
|
||||
BUY_ORDERS = [[1, 106], [1, 107], [1, 108]]
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def vega(request):
|
||||
with init_vega(request) as vega:
|
||||
yield vega
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def continuous_market(vega):
|
||||
market = setup_simple_market(vega, custom_quantum=100000)
|
||||
return setup_continuous_market(vega, market, BUY_ORDERS, SELL_ORDERS, add_liquidity=False)
|
||||
|
||||
|
||||
def generate_referrer_expected_value_dic(expected_base_commission, expected_staking_multiplier, expected_final_commission_rate, expected_volume, expected_num_traders, expected_total_commission):
|
||||
return {
|
||||
'[data-testid=my-volume-value]': expected_volume,
|
||||
'[data-testid=total-commission-value]': expected_total_commission,
|
||||
'[data-testid=base-commission-rate-value]': expected_base_commission,
|
||||
'[data-testid=number-of-traders-value]': expected_num_traders,
|
||||
'[data-testid=final-commission-rate-value]': expected_final_commission_rate,
|
||||
'[data-testid=staking-multiplier-value]': expected_staking_multiplier
|
||||
}
|
||||
|
||||
|
||||
def generate_referral_expected_value_dic(expected_volume, expected_tier, expected_discount, expected_epochs, expected_epochs_to_next_tier):
|
||||
return {
|
||||
'[data-testid=combined-volume-value]': expected_volume,
|
||||
'[data-testid=current-tier-value]': expected_tier,
|
||||
'[data-testid=discount-value]': expected_discount,
|
||||
'[data-testid=epochs-in-set-value]': expected_epochs,
|
||||
'[data-testid=epochs-to-next-tier-value]': expected_epochs_to_next_tier
|
||||
}
|
||||
|
||||
|
||||
def check_tile_values(page: Page, expected_results: dict):
|
||||
if "referrals" in page.url:
|
||||
page.reload()
|
||||
else:
|
||||
page.goto("/#/referrals/")
|
||||
|
||||
for selector, expected_text in expected_results.items():
|
||||
assert selector_contains_text(
|
||||
page, selector, expected_text), f"Expected text '{expected_text}' not found in selector '{selector}'"
|
||||
|
||||
|
||||
def create_benefit_tier(minimum_running_notional_taker_volume, minimum_epochs, referral_reward_factor, referral_discount_factor):
|
||||
return {
|
||||
"minimum_running_notional_taker_volume": minimum_running_notional_taker_volume,
|
||||
"minimum_epochs": minimum_epochs,
|
||||
"referral_reward_factor": referral_reward_factor,
|
||||
"referral_discount_factor": referral_discount_factor,
|
||||
}
|
||||
|
||||
|
||||
def create_staking_tier(minimum_staked_tokens, referral_reward_multiplier):
|
||||
return {
|
||||
"minimum_staked_tokens": minimum_staked_tokens,
|
||||
"referral_reward_multiplier": referral_reward_multiplier,
|
||||
}
|
||||
|
||||
|
||||
def setup_market_and_referral_scheme(vega: VegaService, continuous_market: str, page: Page):
|
||||
page.goto(f"/#/markets/{continuous_market}")
|
||||
|
||||
create_and_faucet_wallet(vega=vega, wallet=PARTY_A)
|
||||
create_and_faucet_wallet(vega=vega, wallet=PARTY_B)
|
||||
forward_time(vega)
|
||||
|
||||
benefit_tiers = []
|
||||
staking_tiers = []
|
||||
for i in range(1, 4):
|
||||
benefit_tiers.append(create_benefit_tier(
|
||||
i * 100, i, i * 0.01, i * 0.01))
|
||||
staking_tiers.append(create_staking_tier(
|
||||
i * 100, i))
|
||||
|
||||
vega.update_referral_program(
|
||||
proposal_key=MM_WALLET.name,
|
||||
benefit_tiers=benefit_tiers,
|
||||
staking_tiers=staking_tiers,
|
||||
window_length=1,
|
||||
)
|
||||
forward_time(vega, True)
|
||||
|
||||
vega.create_referral_set(key_name=PARTY_A.name)
|
||||
forward_time(vega, True)
|
||||
|
||||
referral_set_id = list(vega.list_referral_sets().keys())[0]
|
||||
vega.apply_referral_code(key_name=PARTY_B.name, id=referral_set_id)
|
||||
|
||||
tdai_id = vega.find_asset_id(symbol="tDAI")
|
||||
vega.mint(
|
||||
"Key 1",
|
||||
asset=tdai_id,
|
||||
amount=10e6,
|
||||
)
|
||||
vega.mint(
|
||||
PARTY_B.name,
|
||||
asset=tdai_id,
|
||||
amount=10e6,
|
||||
)
|
||||
|
||||
submit_liquidity(vega, MM_WALLET.name, continuous_market, 100, 100)
|
||||
forward_time(vega)
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("page", "auth", "risk_accepted")
|
||||
def test_can_traverse_up_and_down_through_tiers(continuous_market, vega: VegaService, page: Page):
|
||||
setup_market_and_referral_scheme(vega, continuous_market, page)
|
||||
change_keys(page, vega, PARTY_B.name)
|
||||
submit_order(vega, PARTY_B.name, continuous_market, "SIDE_BUY", 1, 115)
|
||||
forward_time(vega, True)
|
||||
check_tile_values(page, generate_referral_expected_value_dic(
|
||||
"110", "1", "1%", "1", "1"))
|
||||
|
||||
change_keys(page, vega, PARTY_A.name)
|
||||
check_tile_values(page, generate_referrer_expected_value_dic(
|
||||
"1%", "1", "1%", "0", "1", "0"))
|
||||
|
||||
change_keys(page, vega, PARTY_B.name)
|
||||
submit_order(vega, PARTY_B.name, continuous_market, "SIDE_BUY", 2, 115)
|
||||
forward_time(vega, True)
|
||||
check_tile_values(page, generate_referral_expected_value_dic(
|
||||
"221", "2", "2%", "2", "1"))
|
||||
|
||||
change_keys(page, vega, PARTY_A.name)
|
||||
check_tile_values(page, generate_referrer_expected_value_dic(
|
||||
"2%", "1", "2%", "0", "1", "0"))
|
||||
|
||||
change_keys(page, vega, PARTY_B.name)
|
||||
submit_order(vega, PARTY_B.name, continuous_market, "SIDE_BUY", 3, 115)
|
||||
forward_time(vega, True)
|
||||
check_tile_values(page, generate_referral_expected_value_dic(
|
||||
"331", "3", "3%", "3", "0"))
|
||||
|
||||
change_keys(page, vega, PARTY_A.name)
|
||||
check_tile_values(page, generate_referrer_expected_value_dic(
|
||||
"3%", "1", "3%", "0", "1", "1"))
|
||||
|
||||
change_keys(page, vega, PARTY_B.name)
|
||||
submit_order(vega, PARTY_B.name, continuous_market, "SIDE_BUY", 1, 115)
|
||||
forward_time(vega, True)
|
||||
check_tile_values(page, generate_referral_expected_value_dic(
|
||||
"110", "1", "1%", "4", "0"))
|
||||
|
||||
change_keys(page, vega, PARTY_A.name)
|
||||
check_tile_values(page, generate_referrer_expected_value_dic(
|
||||
"1%", "1", "1%", "0", "1", "1"))
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("page", "auth", "risk_accepted")
|
||||
def test_does_not_move_up_tiers_when_not_enough_epochs(continuous_market, vega: VegaService, page: Page):
|
||||
setup_market_and_referral_scheme(vega, continuous_market, page)
|
||||
change_keys(page, vega, PARTY_B.name)
|
||||
submit_order(vega, PARTY_B.name, continuous_market, "SIDE_BUY", 2, 115)
|
||||
forward_time(vega, True)
|
||||
check_tile_values(page, generate_referral_expected_value_dic(
|
||||
"221", "1", "1%", "1", "1"))
|
||||
|
||||
change_keys(page, vega, PARTY_A.name)
|
||||
check_tile_values(page, generate_referrer_expected_value_dic(
|
||||
"2%", "1", "2%", "0", "1", "0"))
|
||||
@@ -7,6 +7,9 @@ WalletConfig = namedtuple("WalletConfig", ["name", "passphrase"])
|
||||
MM_WALLET = WalletConfig("market_maker", "pin")
|
||||
MM_WALLET2 = WalletConfig("market_maker_2", "pin2")
|
||||
TERMINATE_WALLET = WalletConfig("FJMKnwfZdd48C8NqvYrG", "bY3DxwtsCstMIIZdNpKs")
|
||||
GOVERNANCE_WALLET = WalletConfig("FJMKnwfZdd48C8NqvYrG", "bY3DxwtsCstMIIZdNpKs")
|
||||
GOVERNANCE_WALLET = WalletConfig(
|
||||
"FJMKnwfZdd48C8NqvYrG", "bY3DxwtsCstMIIZdNpKs")
|
||||
PARTY_A = WalletConfig("party_a", "party_a")
|
||||
PARTY_B = WalletConfig("party_b", "party_b")
|
||||
|
||||
wallets = [MM_WALLET, MM_WALLET2, TERMINATE_WALLET, GOVERNANCE_WALLET]
|
||||
|
||||
@@ -4,9 +4,18 @@ export default {
|
||||
preset: '../../jest.preset.js',
|
||||
transform: {
|
||||
'^(?!.*\\.(js|jsx|ts|tsx|css|json)$)': '@nx/react/plugins/jest',
|
||||
'^.+\\.[tj]sx?$': ['babel-jest', { presets: ['@nx/next/babel'] }],
|
||||
'^.+\\.[tj]sx?$': [
|
||||
'babel-jest',
|
||||
{
|
||||
presets: ['@nx/next/babel'],
|
||||
// required for pennant to work in jest, due to having untranspiled exports
|
||||
plugins: [['@babel/plugin-proposal-private-methods']],
|
||||
},
|
||||
],
|
||||
},
|
||||
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'],
|
||||
coverageDirectory: '../../coverage/apps/trading',
|
||||
setupFilesAfterEnv: ['./setup-tests.ts'],
|
||||
// dont ignore pennant from transpilation
|
||||
transformIgnorePatterns: ['<rootDir>/node_modules/pennant'],
|
||||
};
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { routerConfig } from '../../pages/client-router';
|
||||
import { useRouterConfig } from '../../pages/client-router';
|
||||
import { matchRoutes, useLocation } from 'react-router-dom';
|
||||
|
||||
export const useGetCurrentRouteId = () => {
|
||||
const location = useLocation();
|
||||
const matches = matchRoutes(routerConfig, location);
|
||||
const matches = matchRoutes(useRouterConfig(), location);
|
||||
const lastRoute = matches ? matches[matches.length - 1] : undefined;
|
||||
if (lastRoute) {
|
||||
const id = lastRoute.route.id;
|
||||
|
||||
@@ -75,6 +75,7 @@ i18n
|
||||
'positions',
|
||||
'trades',
|
||||
'trading',
|
||||
'trading-view',
|
||||
'ui-toolkit',
|
||||
'utils',
|
||||
'wallet',
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { FLAGS } from '@vegaprotocol/environment';
|
||||
import { useFeatureFlags } from '@vegaprotocol/environment';
|
||||
import { useMemo } from 'react';
|
||||
import {
|
||||
JsonRpcConnector,
|
||||
ViewConnector,
|
||||
@@ -18,13 +19,17 @@ if (typeof window !== 'undefined') {
|
||||
view = new ViewConnector();
|
||||
}
|
||||
|
||||
export const snap = FLAGS.METAMASK_SNAPS
|
||||
? new SnapConnector(DEFAULT_SNAP_ID)
|
||||
: undefined;
|
||||
export const snap = new SnapConnector(DEFAULT_SNAP_ID);
|
||||
|
||||
export const Connectors = {
|
||||
injected,
|
||||
jsonRpc,
|
||||
view,
|
||||
snap,
|
||||
export const useConnectors = () => {
|
||||
const featureFlags = useFeatureFlags((state) => state.flags);
|
||||
return useMemo(
|
||||
() => ({
|
||||
injected,
|
||||
jsonRpc,
|
||||
view,
|
||||
snap: featureFlags.METAMASK_SNAPS ? snap : undefined,
|
||||
}),
|
||||
[featureFlags.METAMASK_SNAPS]
|
||||
);
|
||||
};
|
||||
|
||||
@@ -22,7 +22,7 @@ import { ApplyCodeFormContainer } from '../client-pages/referrals/apply-code-for
|
||||
import { CreateCodeContainer } from '../client-pages/referrals/create-code-form';
|
||||
import { NotFound as ReferralNotFound } from '../client-pages/referrals/error-boundary';
|
||||
import { compact } from 'lodash';
|
||||
import { FLAGS } from '@vegaprotocol/environment';
|
||||
import { useFeatureFlags } from '@vegaprotocol/environment';
|
||||
import { LiquidityHeader } from '../components/liquidity-header';
|
||||
import { MarketHeader } from '../components/market-header';
|
||||
import { PortfolioSidebar } from '../client-pages/portfolio/portfolio-sidebar';
|
||||
@@ -44,144 +44,147 @@ const NotFound = () => {
|
||||
);
|
||||
};
|
||||
|
||||
export const routerConfig: RouteObject[] = compact([
|
||||
{
|
||||
index: true,
|
||||
element: <Home />,
|
||||
id: AppRoutes.HOME,
|
||||
},
|
||||
{
|
||||
path: 'disclaimer',
|
||||
element: <LayoutCentered />,
|
||||
id: AppRoutes.DISCLAIMER,
|
||||
children: [{ index: true, element: <Disclaimer /> }],
|
||||
},
|
||||
// Referrals routing (the pages should be available if the feature flag is on)
|
||||
FLAGS.REFERRALS
|
||||
? {
|
||||
path: AppRoutes.REFERRALS,
|
||||
element: <LayoutWithSidebar sidebar={<PortfolioSidebar />} />,
|
||||
children: [
|
||||
{
|
||||
element: (
|
||||
<LayoutWithSky>
|
||||
<Referrals />
|
||||
</LayoutWithSky>
|
||||
),
|
||||
children: [
|
||||
{
|
||||
index: true,
|
||||
element: <ReferralStatistics />,
|
||||
},
|
||||
{
|
||||
path: AppRoutes.REFERRALS_CREATE_CODE,
|
||||
element: <CreateCodeContainer />,
|
||||
},
|
||||
{
|
||||
path: AppRoutes.REFERRALS_APPLY_CODE,
|
||||
element: <ApplyCodeFormContainer />,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: '*',
|
||||
element: <ReferralNotFound />,
|
||||
},
|
||||
],
|
||||
}
|
||||
: undefined,
|
||||
{
|
||||
path: 'fees/*',
|
||||
element: <LayoutWithSidebar sidebar={<PortfolioSidebar />} />,
|
||||
children: [
|
||||
{
|
||||
index: true,
|
||||
element: <Fees />,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: 'rewards/*',
|
||||
element: <LayoutWithSidebar sidebar={<PortfolioSidebar />} />,
|
||||
children: [
|
||||
{
|
||||
index: true,
|
||||
element: <Rewards />,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: 'markets/*',
|
||||
element: (
|
||||
<LayoutWithSidebar
|
||||
header={<MarketHeader />}
|
||||
sidebar={<MarketsSidebar />}
|
||||
/>
|
||||
),
|
||||
children: [
|
||||
{
|
||||
index: true,
|
||||
element: <MarketsPage />,
|
||||
id: AppRoutes.MARKETS,
|
||||
},
|
||||
{
|
||||
path: 'all',
|
||||
element: <Navigate to="/markets" />,
|
||||
},
|
||||
{
|
||||
path: ':marketId',
|
||||
element: <MarketPage />,
|
||||
id: AppRoutes.MARKET,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: 'portfolio/*',
|
||||
element: <LayoutWithSidebar sidebar={<PortfolioSidebar />} />,
|
||||
children: [
|
||||
{
|
||||
index: true,
|
||||
element: <Portfolio />,
|
||||
id: AppRoutes.PORTFOLIO,
|
||||
},
|
||||
{
|
||||
path: 'assets',
|
||||
element: <Assets />,
|
||||
id: AppRoutes.ASSETS,
|
||||
children: [
|
||||
{ index: true, element: <Navigate to="deposit" /> },
|
||||
{ path: 'deposit', element: <Deposit />, id: AppRoutes.DEPOSIT },
|
||||
{ path: 'withdraw', element: <Withdraw />, id: AppRoutes.WITHDRAW },
|
||||
{ path: 'transfer', element: <Transfer />, id: AppRoutes.TRANSFER },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
export const useRouterConfig = (): RouteObject[] => {
|
||||
const featureFlags = useFeatureFlags((state) => state.flags);
|
||||
return compact([
|
||||
{
|
||||
index: true,
|
||||
element: <Home />,
|
||||
id: AppRoutes.HOME,
|
||||
},
|
||||
{
|
||||
path: 'disclaimer',
|
||||
element: <LayoutCentered />,
|
||||
id: AppRoutes.DISCLAIMER,
|
||||
children: [{ index: true, element: <Disclaimer /> }],
|
||||
},
|
||||
// Referrals routing (the pages should be available if the feature flag is on)
|
||||
featureFlags.REFERRALS
|
||||
? {
|
||||
path: AppRoutes.REFERRALS,
|
||||
element: <LayoutWithSidebar sidebar={<PortfolioSidebar />} />,
|
||||
children: [
|
||||
{
|
||||
element: (
|
||||
<LayoutWithSky>
|
||||
<Referrals />
|
||||
</LayoutWithSky>
|
||||
),
|
||||
children: [
|
||||
{
|
||||
index: true,
|
||||
element: <ReferralStatistics />,
|
||||
},
|
||||
{
|
||||
path: AppRoutes.REFERRALS_CREATE_CODE,
|
||||
element: <CreateCodeContainer />,
|
||||
},
|
||||
{
|
||||
path: AppRoutes.REFERRALS_APPLY_CODE,
|
||||
element: <ApplyCodeFormContainer />,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: '*',
|
||||
element: <ReferralNotFound />,
|
||||
},
|
||||
],
|
||||
}
|
||||
: undefined,
|
||||
{
|
||||
path: 'fees/*',
|
||||
element: <LayoutWithSidebar sidebar={<PortfolioSidebar />} />,
|
||||
children: [
|
||||
{
|
||||
index: true,
|
||||
element: <Fees />,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: 'rewards/*',
|
||||
element: <LayoutWithSidebar sidebar={<PortfolioSidebar />} />,
|
||||
children: [
|
||||
{
|
||||
index: true,
|
||||
element: <Rewards />,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: 'markets/*',
|
||||
element: (
|
||||
<LayoutWithSidebar
|
||||
header={<MarketHeader />}
|
||||
sidebar={<MarketsSidebar />}
|
||||
/>
|
||||
),
|
||||
children: [
|
||||
{
|
||||
index: true,
|
||||
element: <MarketsPage />,
|
||||
id: AppRoutes.MARKETS,
|
||||
},
|
||||
{
|
||||
path: 'all',
|
||||
element: <Navigate to="/markets" />,
|
||||
},
|
||||
{
|
||||
path: ':marketId',
|
||||
element: <MarketPage />,
|
||||
id: AppRoutes.MARKET,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: 'portfolio/*',
|
||||
element: <LayoutWithSidebar sidebar={<PortfolioSidebar />} />,
|
||||
children: [
|
||||
{
|
||||
index: true,
|
||||
element: <Portfolio />,
|
||||
id: AppRoutes.PORTFOLIO,
|
||||
},
|
||||
{
|
||||
path: 'assets',
|
||||
element: <Assets />,
|
||||
id: AppRoutes.ASSETS,
|
||||
children: [
|
||||
{ index: true, element: <Navigate to="deposit" /> },
|
||||
{ path: 'deposit', element: <Deposit />, id: AppRoutes.DEPOSIT },
|
||||
{ path: 'withdraw', element: <Withdraw />, id: AppRoutes.WITHDRAW },
|
||||
{ path: 'transfer', element: <Transfer />, id: AppRoutes.TRANSFER },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
{
|
||||
path: 'liquidity/*',
|
||||
element: (
|
||||
<LayoutWithSidebar
|
||||
header={<LiquidityHeader />}
|
||||
sidebar={<LiquiditySidebar />}
|
||||
/>
|
||||
),
|
||||
id: AppRoutes.LIQUIDITY,
|
||||
children: [
|
||||
{
|
||||
path: ':marketId',
|
||||
element: <Liquidity />,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: '*',
|
||||
element: <NotFound />,
|
||||
},
|
||||
]);
|
||||
{
|
||||
path: 'liquidity/*',
|
||||
element: (
|
||||
<LayoutWithSidebar
|
||||
header={<LiquidityHeader />}
|
||||
sidebar={<LiquiditySidebar />}
|
||||
/>
|
||||
),
|
||||
id: AppRoutes.LIQUIDITY,
|
||||
children: [
|
||||
{
|
||||
path: ':marketId',
|
||||
element: <Liquidity />,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: '*',
|
||||
element: <NotFound />,
|
||||
},
|
||||
]);
|
||||
};
|
||||
|
||||
export const ClientRouter = () => {
|
||||
const routes = useRoutes(routerConfig);
|
||||
const routes = useRoutes(useRouterConfig());
|
||||
return (
|
||||
<Suspense
|
||||
fallback={
|
||||
|
||||
@@ -3,7 +3,7 @@ import {
|
||||
useAssetDetailsDialogStore,
|
||||
} from '@vegaprotocol/assets';
|
||||
import { VegaConnectDialog, ViewAsDialog } from '@vegaprotocol/wallet';
|
||||
import { Connectors } from '../lib/vega-connectors';
|
||||
import { useConnectors } from '../lib/vega-connectors';
|
||||
import {
|
||||
Web3ConnectUncontrolledDialog,
|
||||
WithdrawalApprovalDialogContainer,
|
||||
@@ -13,13 +13,14 @@ import { RiskMessage } from '../components/welcome-dialog';
|
||||
|
||||
const DialogsContainer = () => {
|
||||
const { isOpen, id, trigger, setOpen } = useAssetDetailsDialogStore();
|
||||
const connectors = useConnectors();
|
||||
return (
|
||||
<>
|
||||
<VegaConnectDialog
|
||||
connectors={Connectors}
|
||||
connectors={connectors}
|
||||
riskMessage={<RiskMessage />}
|
||||
/>
|
||||
<ViewAsDialog connector={Connectors.view} />
|
||||
<ViewAsDialog connector={connectors.view} />
|
||||
<AssetDetailsDialog
|
||||
assetId={id}
|
||||
trigger={trigger || null}
|
||||
|
||||
@@ -7,13 +7,14 @@ import {
|
||||
useVegaWallet,
|
||||
} from '@vegaprotocol/wallet';
|
||||
import { useGlobalStore } from '../stores';
|
||||
import { Connectors } from '../lib/vega-connectors';
|
||||
import { useConnectors } from '../lib/vega-connectors';
|
||||
import { useTelemetryApproval } from '../lib/hooks/use-telemetry-approval';
|
||||
|
||||
export const MaybeConnectEagerly = () => {
|
||||
const { VEGA_ENV, SENTRY_DSN } = useEnvironment();
|
||||
const update = useGlobalStore((store) => store.update);
|
||||
const eagerConnecting = useVegaEagerConnect(Connectors);
|
||||
const connectors = useConnectors();
|
||||
const eagerConnecting = useVegaEagerConnect(connectors);
|
||||
const [isTelemetryApproved] = useTelemetryApproval();
|
||||
useEthereumEagerConnect(
|
||||
isTelemetryApproved ? { dsn: SENTRY_DSN, env: VEGA_ENV } : {}
|
||||
@@ -23,7 +24,7 @@ export const MaybeConnectEagerly = () => {
|
||||
const [searchParams] = useSearchParams();
|
||||
const [query] = useState(searchParams.get('address'));
|
||||
if (query && !pubKey) {
|
||||
connect(Connectors['view']);
|
||||
connect(connectors.view);
|
||||
}
|
||||
useEffect(() => {
|
||||
update({ eagerConnecting });
|
||||
|
||||
@@ -1,43 +1,52 @@
|
||||
import 'pennant/dist/style.css';
|
||||
import { CandlestickChart } from 'pennant';
|
||||
import {
|
||||
CandlestickChart,
|
||||
type Overlay,
|
||||
type ChartType,
|
||||
type Interval,
|
||||
type Study,
|
||||
} from 'pennant';
|
||||
import { VegaDataSource } from './data-source';
|
||||
import { useApolloClient } from '@apollo/client';
|
||||
import { useMemo } from 'react';
|
||||
import debounce from 'lodash/debounce';
|
||||
import AutoSizer from 'react-virtualized-auto-sizer';
|
||||
import { useVegaWallet } from '@vegaprotocol/wallet';
|
||||
import {
|
||||
STUDY_SIZE,
|
||||
useCandlesChartSettings,
|
||||
} from './use-candles-chart-settings';
|
||||
import { useT } from './use-t';
|
||||
import { useThemeSwitcher } from '@vegaprotocol/react-helpers';
|
||||
|
||||
export type CandlesChartContainerProps = {
|
||||
marketId: string;
|
||||
interval: Interval;
|
||||
chartType: ChartType;
|
||||
overlays: Overlay[];
|
||||
studies: Study[];
|
||||
studySizes: number[];
|
||||
defaultStudySize: number;
|
||||
setStudies: (studies?: Study[]) => void;
|
||||
setStudySizes: (sizes: number[]) => void;
|
||||
setOverlays: (overlays?: Overlay[]) => void;
|
||||
};
|
||||
|
||||
const CANDLES_TO_WIDTH_FACTOR = 0.2;
|
||||
|
||||
export const CandlesChartContainer = ({
|
||||
marketId,
|
||||
interval,
|
||||
chartType,
|
||||
overlays,
|
||||
studies,
|
||||
studySizes,
|
||||
defaultStudySize,
|
||||
setStudies,
|
||||
setStudySizes,
|
||||
setOverlays,
|
||||
}: CandlesChartContainerProps) => {
|
||||
const client = useApolloClient();
|
||||
const { pubKey } = useVegaWallet();
|
||||
const { theme } = useThemeSwitcher();
|
||||
const t = useT();
|
||||
|
||||
const {
|
||||
interval,
|
||||
chartType,
|
||||
overlays,
|
||||
studies,
|
||||
studySizes,
|
||||
setStudies,
|
||||
setStudySizes,
|
||||
setOverlays,
|
||||
} = useCandlesChartSettings();
|
||||
|
||||
const handlePaneChange = useMemo(
|
||||
() =>
|
||||
debounce((sizes: number[]) => {
|
||||
@@ -69,7 +78,7 @@ export const CandlesChartContainer = ({
|
||||
</span>
|
||||
),
|
||||
initialNumCandlesToDisplay: candlesCount,
|
||||
studySize: STUDY_SIZE,
|
||||
studySize: defaultStudySize,
|
||||
studySizes,
|
||||
}}
|
||||
interval={interval}
|
||||
|
||||
@@ -1,85 +0,0 @@
|
||||
import { render, screen, within } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import { CandlesMenu } from './candles-menu';
|
||||
import {
|
||||
useCandlesChartSettingsStore,
|
||||
DEFAULT_CHART_SETTINGS,
|
||||
} from './use-candles-chart-settings';
|
||||
import { Overlay, Study, overlayLabels, studyLabels } from 'pennant';
|
||||
|
||||
describe('CandlesMenu', () => {
|
||||
const openDropdown = async () => {
|
||||
await userEvent.click(
|
||||
screen.getByRole('button', {
|
||||
name: 'Indicators',
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
// clear store each time to avoid conditional testing of defaults
|
||||
useCandlesChartSettingsStore.setState({ overlays: [], studies: [] });
|
||||
});
|
||||
|
||||
it.each(Object.values(Overlay))('can set %s overlay', async (overlay) => {
|
||||
render(<CandlesMenu />);
|
||||
|
||||
await openDropdown();
|
||||
|
||||
const menu = within(await screen.findByRole('menu'));
|
||||
|
||||
await userEvent.click(menu.getByText(overlayLabels[overlay as Overlay]));
|
||||
|
||||
// re-open the dropdown
|
||||
await openDropdown();
|
||||
|
||||
expect(screen.getByText(overlayLabels[overlay as Overlay])).toHaveAttribute(
|
||||
'data-state',
|
||||
'checked'
|
||||
);
|
||||
});
|
||||
|
||||
it.each(Object.values(Study))('can set %s study', async (study) => {
|
||||
render(<CandlesMenu />);
|
||||
|
||||
await openDropdown();
|
||||
|
||||
const menu = within(await screen.findByRole('menu'));
|
||||
|
||||
await userEvent.click(menu.getByText(studyLabels[study as Study]));
|
||||
|
||||
// re-open the dropdown
|
||||
await openDropdown();
|
||||
|
||||
expect(screen.getByText(studyLabels[study as Study])).toHaveAttribute(
|
||||
'data-state',
|
||||
'checked'
|
||||
);
|
||||
});
|
||||
|
||||
it('should render with the correct default studies and overlays', async () => {
|
||||
useCandlesChartSettingsStore.setState(DEFAULT_CHART_SETTINGS);
|
||||
|
||||
render(<CandlesMenu />);
|
||||
|
||||
await userEvent.click(
|
||||
screen.getByRole('button', {
|
||||
name: 'Indicators',
|
||||
})
|
||||
);
|
||||
const menu = within(await screen.findByRole('menu'));
|
||||
|
||||
expect(menu.getByText(studyLabels.volume)).toHaveAttribute(
|
||||
'data-state',
|
||||
'checked'
|
||||
);
|
||||
expect(menu.getByText(studyLabels.macd)).toHaveAttribute(
|
||||
'data-state',
|
||||
'checked'
|
||||
);
|
||||
expect(menu.getByText(overlayLabels.movingAverage)).toHaveAttribute(
|
||||
'data-state',
|
||||
'checked'
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,12 @@
|
||||
import { Interval as PennantInterval } from 'pennant';
|
||||
import { Interval } from '@vegaprotocol/types';
|
||||
|
||||
export const PENNANT_INTERVAL_MAP = {
|
||||
[Interval.INTERVAL_BLOCK]: undefined, // TODO: handle block tick
|
||||
[Interval.INTERVAL_I1M]: PennantInterval.I1M,
|
||||
[Interval.INTERVAL_I5M]: PennantInterval.I5M,
|
||||
[Interval.INTERVAL_I15M]: PennantInterval.I15M,
|
||||
[Interval.INTERVAL_I1H]: PennantInterval.I1H,
|
||||
[Interval.INTERVAL_I6H]: PennantInterval.I6H,
|
||||
[Interval.INTERVAL_I1D]: PennantInterval.I1D,
|
||||
} as const;
|
||||
@@ -56,6 +56,7 @@ const defaultConfig = {
|
||||
*/
|
||||
export class VegaDataSource implements DataSource {
|
||||
client: ApolloClient<object>;
|
||||
from?: Date;
|
||||
marketId: string;
|
||||
partyId: null | string;
|
||||
_decimalPlaces = 0;
|
||||
@@ -158,6 +159,7 @@ export class VegaDataSource implements DataSource {
|
||||
*/
|
||||
async query(interval: PennantInterval, from: string) {
|
||||
try {
|
||||
this.from = new Date(from);
|
||||
const { data } = await this.client.query<
|
||||
CandlesQuery,
|
||||
CandlesQueryVariables
|
||||
@@ -215,7 +217,9 @@ export class VegaDataSource implements DataSource {
|
||||
this.decimalPlaces,
|
||||
this.positionDecimalPlaces
|
||||
);
|
||||
|
||||
if (!this.from || candle.date < this.from) {
|
||||
return;
|
||||
}
|
||||
onSubscriptionData(candle);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
export * from './__generated__/Candles';
|
||||
export * from './__generated__/Chart';
|
||||
export * from './candles-chart';
|
||||
export * from './candles-menu';
|
||||
export { PENNANT_INTERVAL_MAP } from './constants';
|
||||
export * from './data-source';
|
||||
|
||||
@@ -65,18 +65,9 @@ export const DateRangeFilter = forwardRef(
|
||||
useImperativeHandle(ref, () => {
|
||||
return {
|
||||
doesFilterPass(params: IDoesFilterPassParams) {
|
||||
const { api, colDef, column, columnApi, context } = props;
|
||||
const { column } = props;
|
||||
const { node } = params;
|
||||
const rowValue = props.valueGetter({
|
||||
api,
|
||||
colDef,
|
||||
column,
|
||||
columnApi,
|
||||
context,
|
||||
data: node.data,
|
||||
getValue: (field) => node.data[field],
|
||||
node,
|
||||
});
|
||||
const rowValue = props.getValue(node, column);
|
||||
if (
|
||||
value.start &&
|
||||
rowValue &&
|
||||
|
||||
@@ -19,18 +19,9 @@ export const SetFilter = forwardRef(
|
||||
useImperativeHandle(ref, () => {
|
||||
return {
|
||||
doesFilterPass(params: IDoesFilterPassParams) {
|
||||
const { api, colDef, column, columnApi, context } = props;
|
||||
const { column } = props;
|
||||
const { node } = params;
|
||||
const getValue = props.valueGetter({
|
||||
api,
|
||||
colDef,
|
||||
column,
|
||||
columnApi,
|
||||
context,
|
||||
data: node.data,
|
||||
getValue: (field) => node.data[field],
|
||||
node,
|
||||
});
|
||||
const getValue = props.getValue(node, column);
|
||||
return Array.isArray(value)
|
||||
? value.includes(getValue)
|
||||
: getValue === value;
|
||||
|
||||
@@ -61,7 +61,7 @@ describe('useDataGridEvents', () => {
|
||||
|
||||
// column state was not updated, so the default width provided by the
|
||||
// col def should be set
|
||||
expect(gridRef?.current?.columnApi.getColumnState()[0].width).toEqual(
|
||||
expect(gridRef?.current?.api.getColumnState()[0].width).toEqual(
|
||||
gridProps.columnDefs[0].width
|
||||
);
|
||||
// no filters set
|
||||
@@ -107,16 +107,57 @@ describe('useDataGridEvents', () => {
|
||||
columnState: [colState],
|
||||
};
|
||||
|
||||
setup(initialState, jest.fn());
|
||||
setup(initialState, jest.fn(), undefined);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(gridRef?.current?.api.getFilterModel()['id']).toEqual(idFilter);
|
||||
expect(gridRef?.current?.columnApi.getColumnState()[0]).toEqual(
|
||||
expect(gridRef?.current?.api.getColumnState()[0]).toEqual(
|
||||
expect.objectContaining(colState)
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('applies default filter model', async () => {
|
||||
const idFilter = {
|
||||
filter: 1,
|
||||
filterType: 'number',
|
||||
type: 'equals',
|
||||
};
|
||||
|
||||
setup({}, jest.fn(), undefined, {
|
||||
id: idFilter,
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(gridRef?.current?.api.getFilterModel()['id']).toEqual(idFilter);
|
||||
});
|
||||
});
|
||||
|
||||
it('default filter overwrites stored filter model', async () => {
|
||||
const idFilter = {
|
||||
filter: 1,
|
||||
filterType: 'number',
|
||||
type: 'equals',
|
||||
};
|
||||
|
||||
setup(
|
||||
{
|
||||
filterModel: {
|
||||
id: { ...idFilter, filter: 2 },
|
||||
},
|
||||
},
|
||||
jest.fn(),
|
||||
undefined,
|
||||
{
|
||||
id: idFilter,
|
||||
}
|
||||
);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(gridRef?.current?.api.getFilterModel()['id']).toEqual(idFilter);
|
||||
});
|
||||
});
|
||||
|
||||
it('ignores events that were not made via the UI', async () => {
|
||||
const callback = jest.fn();
|
||||
const initialState = {
|
||||
@@ -130,7 +171,7 @@ describe('useDataGridEvents', () => {
|
||||
|
||||
// Set col width multiple times
|
||||
await act(async () => {
|
||||
gridRef?.current?.columnApi.setColumnWidth('id', newWidth);
|
||||
gridRef?.current?.api.setColumnWidth('id', newWidth);
|
||||
});
|
||||
|
||||
expect(callback).not.toHaveBeenCalled();
|
||||
@@ -150,14 +191,14 @@ describe('useDataGridEvents', () => {
|
||||
};
|
||||
|
||||
const { rerender } = setup(initialState, callback, ['id']);
|
||||
jest.spyOn(gridRef?.current?.columnApi, 'autoSizeColumns');
|
||||
if (gridRef?.current?.api) {
|
||||
jest.spyOn(gridRef?.current?.api, 'autoSizeColumns');
|
||||
}
|
||||
rerender(<TestComponent hookParams={[initialState, callback, ['id']]} />);
|
||||
act(() => {
|
||||
gridRef?.current?.api.setRowData([{ id: 'test-id' }]);
|
||||
gridRef?.current?.api.setGridOption('rowData', [{ id: 'test-id' }]);
|
||||
jest.advanceTimersByTime(GRID_EVENT_DEBOUNCE_TIME);
|
||||
});
|
||||
expect(gridRef?.current?.columnApi.autoSizeColumns).toHaveBeenCalledWith([
|
||||
'id',
|
||||
]);
|
||||
expect(gridRef?.current?.api.autoSizeColumns).toHaveBeenCalledWith(['id']);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -7,8 +7,9 @@ import {
|
||||
type FirstDataRenderedEvent,
|
||||
type SortChangedEvent,
|
||||
type GridReadyEvent,
|
||||
GridApi,
|
||||
} from 'ag-grid-community';
|
||||
import { useCallback } from 'react';
|
||||
import { useCallback, useEffect, useRef } from 'react';
|
||||
|
||||
type State = {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
@@ -19,8 +20,25 @@ type State = {
|
||||
export const useDataGridEvents = (
|
||||
state: State,
|
||||
callback: (data: State) => void,
|
||||
autoSizeColumns?: string[]
|
||||
autoSizeColumns?: string[],
|
||||
defaultFilterModel?: State['filterModel']
|
||||
) => {
|
||||
const apiRef = useRef<GridApi | undefined>();
|
||||
|
||||
useEffect(() => {
|
||||
if (apiRef.current?.isDestroyed()) {
|
||||
apiRef.current = undefined;
|
||||
}
|
||||
if (apiRef.current) {
|
||||
if (!state.columnState) {
|
||||
apiRef.current.resetColumnState();
|
||||
}
|
||||
if (!state.filterModel) {
|
||||
apiRef.current.setFilterModel(defaultFilterModel);
|
||||
}
|
||||
}
|
||||
}, [state, defaultFilterModel]);
|
||||
|
||||
/**
|
||||
* Callback for filter events
|
||||
*/
|
||||
@@ -39,11 +57,7 @@ export const useDataGridEvents = (
|
||||
* store callback unnecessarily
|
||||
*/
|
||||
const onDebouncedColumnChange = useCallback(
|
||||
({
|
||||
columnApi,
|
||||
source,
|
||||
finished,
|
||||
}: ColumnResizedEvent | ColumnMovedEvent) => {
|
||||
({ api, source, finished }: ColumnResizedEvent | ColumnMovedEvent) => {
|
||||
if (!finished) return;
|
||||
|
||||
// only call back on user interactions, and not events triggered from the api
|
||||
@@ -57,7 +71,7 @@ export const useDataGridEvents = (
|
||||
return;
|
||||
}
|
||||
|
||||
const columnState = columnApi.getColumnState();
|
||||
const columnState = api.getColumnState();
|
||||
|
||||
callback({ columnState });
|
||||
},
|
||||
@@ -68,8 +82,8 @@ export const useDataGridEvents = (
|
||||
* Callback for sort and visible events
|
||||
*/
|
||||
const onColumnChange = useCallback(
|
||||
({ columnApi }: SortChangedEvent | ColumnVisibleEvent) => {
|
||||
const columnState = columnApi.getColumnState();
|
||||
({ api }: SortChangedEvent | ColumnVisibleEvent) => {
|
||||
const columnState = api.getColumnState();
|
||||
callback({ columnState });
|
||||
},
|
||||
[callback]
|
||||
@@ -80,11 +94,11 @@ export const useDataGridEvents = (
|
||||
* State only applied if found, otherwise columns sized to fit available space
|
||||
*/
|
||||
const onGridReady = useCallback(
|
||||
({ api, columnApi }: GridReadyEvent) => {
|
||||
if (!api || !columnApi) return;
|
||||
|
||||
({ api }: GridReadyEvent) => {
|
||||
apiRef.current = api;
|
||||
if (!api) return;
|
||||
if (state.columnState) {
|
||||
columnApi.applyColumnState({
|
||||
api.applyColumnState({
|
||||
state: state.columnState,
|
||||
applyOrder: true,
|
||||
});
|
||||
@@ -92,18 +106,18 @@ export const useDataGridEvents = (
|
||||
api.sizeColumnsToFit();
|
||||
}
|
||||
|
||||
if (state.filterModel) {
|
||||
api.setFilterModel(state.filterModel);
|
||||
if (state.filterModel || defaultFilterModel) {
|
||||
api.setFilterModel({ ...state.filterModel, ...defaultFilterModel });
|
||||
}
|
||||
},
|
||||
[state]
|
||||
[state, defaultFilterModel]
|
||||
);
|
||||
|
||||
const onFirstDataRendered = useCallback(
|
||||
({ columnApi }: FirstDataRenderedEvent) => {
|
||||
if (!columnApi) return;
|
||||
({ api }: FirstDataRenderedEvent) => {
|
||||
if (!api) return;
|
||||
if (!state?.columnState && autoSizeColumns?.length) {
|
||||
columnApi.autoSizeColumns(autoSizeColumns);
|
||||
api.autoSizeColumns(autoSizeColumns);
|
||||
}
|
||||
},
|
||||
[state, autoSizeColumns]
|
||||
|
||||
@@ -11,7 +11,7 @@ import {
|
||||
} from '@vegaprotocol/markets';
|
||||
import { AsyncRendererInline } from '@vegaprotocol/ui-toolkit';
|
||||
import { DealTicket } from './deal-ticket';
|
||||
import { FLAGS } from '@vegaprotocol/environment';
|
||||
import { useFeatureFlags } from '@vegaprotocol/environment';
|
||||
import { useT } from '../../use-t';
|
||||
|
||||
interface DealTicketContainerProps {
|
||||
@@ -24,6 +24,7 @@ export const DealTicketContainer = ({
|
||||
marketId,
|
||||
...props
|
||||
}: DealTicketContainerProps) => {
|
||||
const featureFlags = useFeatureFlags((state) => state.flags);
|
||||
const t = useT();
|
||||
const showStopOrder = useDealTicketFormValues((state) =>
|
||||
isStopOrderType(state.formValues[marketId]?.type)
|
||||
@@ -50,7 +51,7 @@ export const DealTicketContainer = ({
|
||||
reload={reload}
|
||||
>
|
||||
{market && marketData ? (
|
||||
FLAGS.STOP_ORDERS && showStopOrder ? (
|
||||
featureFlags.STOP_ORDERS && showStopOrder ? (
|
||||
<StopOrder
|
||||
market={market}
|
||||
marketPrice={marketPrice}
|
||||
|
||||
@@ -11,7 +11,7 @@ import {
|
||||
DealTicketType,
|
||||
useDealTicketFormValues,
|
||||
} from '../../hooks/use-form-values';
|
||||
import type { FeatureFlags } from '@vegaprotocol/environment';
|
||||
import { useFeatureFlags } from '@vegaprotocol/environment';
|
||||
import { formatForInput } from '@vegaprotocol/utils';
|
||||
|
||||
jest.mock('zustand');
|
||||
@@ -19,17 +19,6 @@ jest.mock('./deal-ticket-fee-details', () => ({
|
||||
DealTicketFeeDetails: () => <div data-testid="deal-ticket-fee-details" />,
|
||||
}));
|
||||
|
||||
jest.mock('@vegaprotocol/environment', () => {
|
||||
const actual = jest.requireActual('@vegaprotocol/environment');
|
||||
return {
|
||||
...actual,
|
||||
FLAGS: {
|
||||
...actual.FLAGS,
|
||||
STOP_ORDERS: true,
|
||||
} as FeatureFlags,
|
||||
};
|
||||
});
|
||||
|
||||
const marketPrice = '200';
|
||||
const market = generateMarket();
|
||||
const submit = jest.fn();
|
||||
@@ -94,6 +83,7 @@ jest.mock('@vegaprotocol/data-provider', () => ({
|
||||
describe('StopOrder', () => {
|
||||
beforeEach(() => {
|
||||
localStorage.clear();
|
||||
useFeatureFlags.setState({ flags: { STOP_ORDERS: true } });
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
|
||||
@@ -18,7 +18,7 @@ import { MarketModeValidationType } from '../../constants';
|
||||
import { DealTicketType } from '../../hooks/use-form-values';
|
||||
import * as RadioGroup from '@radix-ui/react-radio-group';
|
||||
import classNames from 'classnames';
|
||||
import { FLAGS } from '@vegaprotocol/environment';
|
||||
import { useFeatureFlags } from '@vegaprotocol/environment';
|
||||
import { Trans } from 'react-i18next';
|
||||
import { useT, ns } from '../../use-t';
|
||||
|
||||
@@ -49,6 +49,7 @@ export const TypeToggle = ({
|
||||
value,
|
||||
onValueChange,
|
||||
}: Pick<TypeSelectorProps, 'onValueChange' | 'value'>) => {
|
||||
const featureFlags = useFeatureFlags((state) => state.flags);
|
||||
const t = useT();
|
||||
const options = useOptions();
|
||||
const toggles = useToggles();
|
||||
@@ -57,8 +58,8 @@ export const TypeToggle = ({
|
||||
<RadioGroup.Root
|
||||
name="order-type"
|
||||
className={classNames('mb-2 grid h-8 leading-8 font-alpha text-xs', {
|
||||
'grid-cols-3': FLAGS.STOP_ORDERS,
|
||||
'grid-cols-2': !FLAGS.STOP_ORDERS,
|
||||
'grid-cols-3': featureFlags.STOP_ORDERS,
|
||||
'grid-cols-2': !featureFlags.STOP_ORDERS,
|
||||
})}
|
||||
value={value}
|
||||
onValueChange={onValueChange}
|
||||
@@ -80,7 +81,7 @@ export const TypeToggle = ({
|
||||
</button>
|
||||
</RadioGroup.Item>
|
||||
))}
|
||||
{FLAGS.STOP_ORDERS && (
|
||||
{featureFlags.STOP_ORDERS && (
|
||||
<TradingDropdown
|
||||
trigger={
|
||||
<TradingDropdownTrigger
|
||||
|
||||
@@ -5,7 +5,7 @@ import { prepend0x } from '@vegaprotocol/smart-contracts';
|
||||
import sortBy from 'lodash/sortBy';
|
||||
import { useSubmitApproval } from './use-submit-approval';
|
||||
import { useSubmitFaucet } from './use-submit-faucet';
|
||||
import { useCallback, useState } from 'react';
|
||||
import { useCallback, useEffect } from 'react';
|
||||
import { useDepositBalances } from './use-deposit-balances';
|
||||
import type { Asset } from '@vegaprotocol/assets';
|
||||
import {
|
||||
@@ -30,7 +30,7 @@ export const DepositManager = ({
|
||||
const { config } = useEthereumConfig();
|
||||
const [persistentDeposit, savePersistentDeposit] =
|
||||
usePersistentDeposit(initialAssetId);
|
||||
const [assetId, setAssetId] = useState(persistentDeposit?.assetId);
|
||||
const assetId = persistentDeposit?.assetId;
|
||||
const asset = assets.find((a) => a.id === assetId);
|
||||
const bridgeContract = useBridgeContract();
|
||||
|
||||
@@ -70,18 +70,19 @@ export const DepositManager = ({
|
||||
[savePersistentDeposit, persistentDeposit]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
// When we change asset, also clear the tracked faucet/approve transactions so
|
||||
// we dont render stale UI
|
||||
approve.reset();
|
||||
faucet.reset();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [assetId]);
|
||||
|
||||
return (
|
||||
<DepositForm
|
||||
selectedAsset={asset}
|
||||
onDisconnect={reset}
|
||||
onSelectAsset={(id) => {
|
||||
setAssetId(id);
|
||||
savePersistentDeposit({ assetId: id });
|
||||
// When we change asset, also clear the tracked faucet/approve transactions so
|
||||
// we dont render stale UI
|
||||
approve.reset();
|
||||
faucet.reset();
|
||||
}}
|
||||
onSelectAsset={(assetId) => savePersistentDeposit({ assetId })}
|
||||
handleAmountChange={onAmountChange}
|
||||
assets={sortBy(assets, 'name')}
|
||||
submitApprove={approve.perform}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user