feat(environment): users controlled feature flags (#5425)
This commit is contained in:
parent
67be224138
commit
0796f2b31f
@ -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
|
||||
|
@ -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(
|
||||
|
@ -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,
|
||||
},
|
||||
});
|
||||
|
||||
|
@ -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: {
|
||||
|
@ -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,
|
||||
},
|
||||
});
|
||||
|
||||
|
@ -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' });
|
||||
@ -165,7 +166,7 @@ const MainGrid = memo(
|
||||
<TradingViews.orders.component />
|
||||
</ErrorBoundary>
|
||||
</Tab>
|
||||
{FLAGS.STOP_ORDERS ? (
|
||||
{featureFlags.STOP_ORDERS ? (
|
||||
<Tab id="stop-orders" name={t('Stop orders')}>
|
||||
<ErrorBoundary feature="stop-orders">
|
||||
<TradingViews.stopOrders.component />
|
||||
@ -196,6 +197,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 +206,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,7 +4,6 @@ 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 { useT } from '../../lib/use-t';
|
||||
import {
|
||||
@ -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,6 +22,7 @@ interface TradePanelsProps {
|
||||
}
|
||||
|
||||
export const TradePanels = ({ market, pinnedAsset }: TradePanelsProps) => {
|
||||
const featureFlags = useFeatureFlags((state) => state.flags);
|
||||
const [view, setView] = useState<TradingView>('candles');
|
||||
|
||||
const renderView = () => {
|
||||
@ -61,7 +62,7 @@ export const TradePanels = ({ market, pinnedAsset }: TradePanelsProps) => {
|
||||
return (
|
||||
<div className="h-full grid grid-rows-[min-content_min-content_1fr_min-content]">
|
||||
<div>
|
||||
{FLAGS.SUCCESSOR_MARKETS && (
|
||||
{featureFlags.SUCCESSOR_MARKETS && (
|
||||
<>
|
||||
<MarketSuccessorBanner market={market} />
|
||||
<MarketSuccessorProposalBanner marketId={market?.id} />
|
||||
|
@ -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')}
|
||||
|
@ -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}
|
||||
|
@ -3,7 +3,7 @@ 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 { Connectors } from '../../lib/vega-connectors';
|
||||
import { useConnectors } from '../../lib/vega-connectors';
|
||||
import { useT } from '../../lib/use-t';
|
||||
import { Routes } from '../../lib/links';
|
||||
import { RiskMessage } from './risk-message';
|
||||
@ -20,6 +20,7 @@ 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);
|
||||
@ -43,7 +44,7 @@ export const WelcomeDialog = () => {
|
||||
|
||||
const content = walletDialogOpen ? (
|
||||
<VegaConnectDialog
|
||||
connectors={Connectors}
|
||||
connectors={connectors}
|
||||
riskMessage={<RiskMessage />}
|
||||
onClose={() => setWalletDialogOpen(false)}
|
||||
contentOnly
|
||||
|
@ -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;
|
||||
|
@ -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 });
|
||||
|
@ -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
|
||||
|
@ -3,7 +3,13 @@ import { act } from 'react-dom/test-utils';
|
||||
import type { ClientOptions } from '@vegaprotocol/apollo-client';
|
||||
import { createClient } from '@vegaprotocol/apollo-client';
|
||||
import { Networks } from '../types';
|
||||
import { STORAGE_KEY, useEnvironment } from './use-environment';
|
||||
import {
|
||||
STORAGE_KEY,
|
||||
useEnvironment,
|
||||
featureFlagsLocalStorageKey,
|
||||
getUserEnabledFeatureFlags,
|
||||
setUserEnabledFeatureFlag,
|
||||
} from './use-environment';
|
||||
|
||||
const noop = () => {
|
||||
/* no op*/
|
||||
@ -459,3 +465,30 @@ describe('useEnvironment', () => {
|
||||
expect(localStorage.getItem(STORAGE_KEY)).toBe(newUrl);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getUserEnabledFeatureFlags', () => {
|
||||
beforeEach(() => {
|
||||
localStorage.clear();
|
||||
});
|
||||
it('reads enabled flags from local storage', () => {
|
||||
localStorage.setItem(
|
||||
featureFlagsLocalStorageKey,
|
||||
'STOP_ORDERS,STOP_ORDERS,REFERRALS,BLAH'
|
||||
);
|
||||
const userEnabledFlags = getUserEnabledFeatureFlags(true, ['STOP_ORDERS']);
|
||||
expect(userEnabledFlags).toEqual(['STOP_ORDERS']);
|
||||
});
|
||||
});
|
||||
|
||||
describe('setUserEnabledFeatureFlag', () => {
|
||||
beforeEach(() => {
|
||||
localStorage.clear();
|
||||
});
|
||||
it('saves enabled flags to local storage', () => {
|
||||
setUserEnabledFeatureFlag('STOP_ORDERS', true);
|
||||
expect(localStorage.getItem(featureFlagsLocalStorageKey)).toEqual(
|
||||
'STOP_ORDERS'
|
||||
);
|
||||
expect(getUserEnabledFeatureFlags()).toEqual(['STOP_ORDERS']);
|
||||
});
|
||||
});
|
||||
|
@ -40,132 +40,6 @@ const VERSION = 1;
|
||||
export const STORAGE_KEY = `vega_url_${VERSION}`;
|
||||
const SUBSCRIPTION_TIMEOUT = 3000;
|
||||
|
||||
export const ENV = compileEnvVars();
|
||||
export const FLAGS = compileFeatureFlags();
|
||||
|
||||
export const useEnvironment = create<EnvStore>()((set, get) => ({
|
||||
...compileEnvVars(),
|
||||
...compileFeatureFlags(),
|
||||
nodes: [],
|
||||
status: 'default',
|
||||
error: null,
|
||||
setUrl: (url) => {
|
||||
set({ VEGA_URL: url, status: 'success', error: null });
|
||||
LocalStorage.setItem(STORAGE_KEY, url);
|
||||
},
|
||||
initialize: async () => {
|
||||
set({ status: 'pending' });
|
||||
|
||||
// validate env vars
|
||||
try {
|
||||
const rawVars = compileEnvVars();
|
||||
const safeVars = envSchema.parse(rawVars);
|
||||
set({ ...safeVars });
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
} catch (err: any) {
|
||||
const headline = 'Error processing the Vega environment';
|
||||
set({
|
||||
status: 'failed',
|
||||
error: headline,
|
||||
});
|
||||
console.error(compileErrors(headline, err));
|
||||
return;
|
||||
}
|
||||
|
||||
const state = get();
|
||||
const storedUrl = LocalStorage.getItem(STORAGE_KEY);
|
||||
|
||||
let nodes: string[] | undefined;
|
||||
try {
|
||||
nodes = await fetchConfig(state.VEGA_CONFIG_URL);
|
||||
const enrichedNodes = uniq(
|
||||
[...nodes, state.VEGA_URL, storedUrl].filter(Boolean) as string[]
|
||||
);
|
||||
set({ nodes: enrichedNodes });
|
||||
} catch (err) {
|
||||
console.warn(`Could not fetch node config from ${state.VEGA_CONFIG_URL}`);
|
||||
}
|
||||
|
||||
// Node url found in localStorage, if its valid attempt to connect
|
||||
if (storedUrl) {
|
||||
if (isValidUrl(storedUrl)) {
|
||||
set({ VEGA_URL: storedUrl, status: 'success' });
|
||||
return;
|
||||
} else {
|
||||
LocalStorage.removeItem(STORAGE_KEY);
|
||||
}
|
||||
}
|
||||
|
||||
// VEGA_URL env var is set and is a valid url no need to proceed
|
||||
if (state.VEGA_URL) {
|
||||
set({ status: 'success' });
|
||||
return;
|
||||
}
|
||||
|
||||
// No url found in env vars or localStorage, AND no nodes were found in
|
||||
// the config fetched from VEGA_CONFIG_URL, app initialization has failed
|
||||
if (!nodes || !nodes.length) {
|
||||
set({
|
||||
status: 'failed',
|
||||
error: `Failed to fetch node config from ${state.VEGA_CONFIG_URL}`,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Create a map of node urls to client instances
|
||||
const clients: ClientCollection = {};
|
||||
nodes.forEach((url) => {
|
||||
clients[url] = createClient({
|
||||
url,
|
||||
cacheConfig: undefined,
|
||||
retry: false,
|
||||
connectToDevTools: false,
|
||||
});
|
||||
});
|
||||
|
||||
// Find a suitable node to connect to by attempting a query and a
|
||||
// subscription, first to fulfill both will be the resulting url.
|
||||
const url = await findNode(clients);
|
||||
|
||||
if (url !== null) {
|
||||
set({
|
||||
status: 'success',
|
||||
VEGA_URL: url,
|
||||
});
|
||||
LocalStorage.setItem(STORAGE_KEY, url);
|
||||
}
|
||||
// Every node failed either to make a query or retrieve data from
|
||||
// a subscription
|
||||
else {
|
||||
set({
|
||||
status: 'failed',
|
||||
error: 'No node found',
|
||||
});
|
||||
console.warn('No suitable vega node was found');
|
||||
}
|
||||
},
|
||||
}));
|
||||
|
||||
/**
|
||||
* Initialize Vega app to dynamically select a node from the
|
||||
* VEGA_CONFIG_URL
|
||||
*
|
||||
* This can be omitted if you intend to only use a single node,
|
||||
* in those cases be sure to set NX_VEGA_URL
|
||||
*/
|
||||
export const useInitializeEnv = () => {
|
||||
const { initialize, status } = useEnvironment((store) => ({
|
||||
status: store.status,
|
||||
initialize: store.initialize,
|
||||
}));
|
||||
|
||||
useEffect(() => {
|
||||
if (status === 'default') {
|
||||
initialize();
|
||||
}
|
||||
}, [status, initialize]);
|
||||
};
|
||||
|
||||
/**
|
||||
* Fetch and validate a vega node configuration
|
||||
*/
|
||||
@ -269,11 +143,13 @@ const testSubscription = (client: Client) => {
|
||||
});
|
||||
};
|
||||
|
||||
export const userControllableFeatureFlags: (keyof FeatureFlags)[] = [];
|
||||
|
||||
/**
|
||||
* Retrieve env vars, parsing where needed some type casting is needed
|
||||
* here to appease the environment store interface
|
||||
*/
|
||||
function compileEnvVars() {
|
||||
const compileEnvVars = () => {
|
||||
const VEGA_ENV = windowOrDefault(
|
||||
'VEGA_ENV',
|
||||
process.env['NX_VEGA_ENV']
|
||||
@ -380,10 +256,54 @@ function compileEnvVars() {
|
||||
};
|
||||
|
||||
return env;
|
||||
}
|
||||
};
|
||||
|
||||
function compileFeatureFlags(): FeatureFlags {
|
||||
const TRUTHY = ['1', 'true'];
|
||||
export const featureFlagsLocalStorageKey = 'vega_feature_flags';
|
||||
let userEnabledFeatureFlags: (keyof FeatureFlags)[] | undefined = undefined;
|
||||
|
||||
export const setUserEnabledFeatureFlag = (
|
||||
flag: keyof FeatureFlags,
|
||||
enabled = false
|
||||
) => {
|
||||
const enabledFlags = getUserEnabledFeatureFlags();
|
||||
if (enabled && !enabledFlags.includes(flag)) {
|
||||
enabledFlags.push(flag);
|
||||
}
|
||||
if (!enabled && enabledFlags.includes(flag)) {
|
||||
enabledFlags.splice(enabledFlags.indexOf(flag), 1);
|
||||
}
|
||||
userEnabledFeatureFlags = enabledFlags;
|
||||
if (typeof window !== 'undefined') {
|
||||
window.localStorage.setItem(
|
||||
featureFlagsLocalStorageKey,
|
||||
enabledFlags.join(',')
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export const getUserEnabledFeatureFlags = (
|
||||
refresh = false,
|
||||
allowedFlags = userControllableFeatureFlags
|
||||
): (keyof FeatureFlags)[] => {
|
||||
if (typeof window === 'undefined') {
|
||||
return [];
|
||||
}
|
||||
if (typeof userEnabledFeatureFlags !== 'undefined' && !refresh) {
|
||||
return userEnabledFeatureFlags;
|
||||
}
|
||||
const enabledFlags = window.localStorage.getItem(featureFlagsLocalStorageKey);
|
||||
userEnabledFeatureFlags = enabledFlags
|
||||
? uniq(
|
||||
(enabledFlags.split(',') as (keyof FeatureFlags)[]).filter((flag) =>
|
||||
allowedFlags.includes(flag)
|
||||
)
|
||||
)
|
||||
: [];
|
||||
return userEnabledFeatureFlags;
|
||||
};
|
||||
|
||||
const TRUTHY = ['1', 'true'];
|
||||
export const compileFeatureFlags = (refresh = false): FeatureFlags => {
|
||||
const COSMIC_ELEVATOR_FLAGS: CosmicElevatorFlags = {
|
||||
ICEBERG_ORDERS: TRUTHY.includes(
|
||||
windowOrDefault(
|
||||
@ -510,14 +430,16 @@ function compileFeatureFlags(): FeatureFlags {
|
||||
) as string
|
||||
),
|
||||
};
|
||||
return {
|
||||
const flags = {
|
||||
...COSMIC_ELEVATOR_FLAGS,
|
||||
...EXPLORER_FLAGS,
|
||||
...GOVERNANCE_FLAGS,
|
||||
};
|
||||
}
|
||||
getUserEnabledFeatureFlags(refresh).forEach((flag) => (flags[flag] = true));
|
||||
return flags;
|
||||
};
|
||||
|
||||
function parseNetworks(value?: string) {
|
||||
const parseNetworks = (value?: string) => {
|
||||
if (value) {
|
||||
try {
|
||||
return JSON.parse(value);
|
||||
@ -526,34 +448,34 @@ function parseNetworks(value?: string) {
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Provides a fallback ethereum provider url for test purposes in some apps
|
||||
*/
|
||||
function getEthereumProviderUrl(
|
||||
const getEthereumProviderUrl = (
|
||||
network: Networks | undefined,
|
||||
envvar: string | undefined
|
||||
) {
|
||||
) => {
|
||||
if (envvar) return envvar;
|
||||
return network === Networks.MAINNET
|
||||
? 'https://mainnet.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8'
|
||||
: 'https://sepolia.infura.io/v3/4f846e79e13f44d1b51bbd7ed9edefb8';
|
||||
}
|
||||
};
|
||||
/**
|
||||
* Provide a fallback etherscan url for test purposes in some apps
|
||||
*/
|
||||
function getEtherscanUrl(
|
||||
const getEtherscanUrl = (
|
||||
network: Networks | undefined,
|
||||
envvar: string | undefined
|
||||
) {
|
||||
) => {
|
||||
if (envvar) return envvar;
|
||||
return network === Networks.MAINNET
|
||||
? 'https://etherscan.io'
|
||||
: 'https://sepolia.etherscan.io';
|
||||
}
|
||||
};
|
||||
|
||||
export function windowOrDefault(key: string, defaultValue?: string) {
|
||||
const windowOrDefault = (key: string, defaultValue?: string) => {
|
||||
if (typeof window !== 'undefined') {
|
||||
// @ts-ignore avoid conflict in env
|
||||
if (window._env_ && window._env_[key]) {
|
||||
@ -562,4 +484,141 @@ export function windowOrDefault(key: string, defaultValue?: string) {
|
||||
}
|
||||
}
|
||||
return defaultValue || undefined;
|
||||
}
|
||||
};
|
||||
|
||||
export const useFeatureFlags = create<{
|
||||
flags: FeatureFlags;
|
||||
setFeatureFlag: (flag: keyof FeatureFlags, enabled: boolean) => void;
|
||||
}>()((set, get) => ({
|
||||
flags: compileFeatureFlags(),
|
||||
setFeatureFlag: (flag: keyof FeatureFlags, enabled: boolean) => {
|
||||
if (userControllableFeatureFlags.includes(flag)) {
|
||||
setUserEnabledFeatureFlag(flag, enabled);
|
||||
set({ flags: { ...get().flags, [flag]: enabled } });
|
||||
}
|
||||
},
|
||||
}));
|
||||
|
||||
export const useEnvironment = create<EnvStore>()((set, get) => ({
|
||||
...compileEnvVars(),
|
||||
nodes: [],
|
||||
status: 'default',
|
||||
error: null,
|
||||
setUrl: (url) => {
|
||||
set({ VEGA_URL: url, status: 'success', error: null });
|
||||
LocalStorage.setItem(STORAGE_KEY, url);
|
||||
},
|
||||
initialize: async () => {
|
||||
set({ status: 'pending' });
|
||||
|
||||
// validate env vars
|
||||
try {
|
||||
const rawVars = compileEnvVars();
|
||||
const safeVars = envSchema.parse(rawVars);
|
||||
set({ ...safeVars });
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
} catch (err: any) {
|
||||
const headline = 'Error processing the Vega environment';
|
||||
set({
|
||||
status: 'failed',
|
||||
error: headline,
|
||||
});
|
||||
console.error(compileErrors(headline, err));
|
||||
return;
|
||||
}
|
||||
|
||||
const state = get();
|
||||
const storedUrl = LocalStorage.getItem(STORAGE_KEY);
|
||||
|
||||
let nodes: string[] | undefined;
|
||||
try {
|
||||
nodes = await fetchConfig(state.VEGA_CONFIG_URL);
|
||||
const enrichedNodes = uniq(
|
||||
[...nodes, state.VEGA_URL, storedUrl].filter(Boolean) as string[]
|
||||
);
|
||||
set({ nodes: enrichedNodes });
|
||||
} catch (err) {
|
||||
console.warn(`Could not fetch node config from ${state.VEGA_CONFIG_URL}`);
|
||||
}
|
||||
|
||||
// Node url found in localStorage, if its valid attempt to connect
|
||||
if (storedUrl) {
|
||||
if (isValidUrl(storedUrl)) {
|
||||
set({ VEGA_URL: storedUrl, status: 'success' });
|
||||
return;
|
||||
} else {
|
||||
LocalStorage.removeItem(STORAGE_KEY);
|
||||
}
|
||||
}
|
||||
|
||||
// VEGA_URL env var is set and is a valid url no need to proceed
|
||||
if (state.VEGA_URL) {
|
||||
set({ status: 'success' });
|
||||
return;
|
||||
}
|
||||
|
||||
// No url found in env vars or localStorage, AND no nodes were found in
|
||||
// the config fetched from VEGA_CONFIG_URL, app initialization has failed
|
||||
if (!nodes || !nodes.length) {
|
||||
set({
|
||||
status: 'failed',
|
||||
error: `Failed to fetch node config from ${state.VEGA_CONFIG_URL}`,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Create a map of node urls to client instances
|
||||
const clients: ClientCollection = {};
|
||||
nodes.forEach((url) => {
|
||||
clients[url] = createClient({
|
||||
url,
|
||||
cacheConfig: undefined,
|
||||
retry: false,
|
||||
connectToDevTools: false,
|
||||
});
|
||||
});
|
||||
|
||||
// Find a suitable node to connect to by attempting a query and a
|
||||
// subscription, first to fulfill both will be the resulting url.
|
||||
const url = await findNode(clients);
|
||||
|
||||
if (url !== null) {
|
||||
set({
|
||||
status: 'success',
|
||||
VEGA_URL: url,
|
||||
});
|
||||
LocalStorage.setItem(STORAGE_KEY, url);
|
||||
}
|
||||
// Every node failed either to make a query or retrieve data from
|
||||
// a subscription
|
||||
else {
|
||||
set({
|
||||
status: 'failed',
|
||||
error: 'No node found',
|
||||
});
|
||||
console.warn('No suitable vega node was found');
|
||||
}
|
||||
},
|
||||
}));
|
||||
|
||||
/**
|
||||
* Initialize Vega app to dynamically select a node from the
|
||||
* VEGA_CONFIG_URL
|
||||
*
|
||||
* This can be omitted if you intend to only use a single node,
|
||||
* in those cases be sure to set NX_VEGA_URL
|
||||
*/
|
||||
export const useInitializeEnv = () => {
|
||||
const { initialize, status } = useEnvironment((store) => ({
|
||||
status: store.status,
|
||||
initialize: store.initialize,
|
||||
}));
|
||||
|
||||
useEffect(() => {
|
||||
if (status === 'default') {
|
||||
initialize();
|
||||
}
|
||||
}, [status, initialize]);
|
||||
};
|
||||
|
||||
export const ENV = compileEnvVars();
|
||||
|
@ -1,5 +1,5 @@
|
||||
import {
|
||||
FLAGS,
|
||||
useFeatureFlags,
|
||||
TokenStaticLinks,
|
||||
useEnvironment,
|
||||
} from '@vegaprotocol/environment';
|
||||
@ -90,6 +90,7 @@ export const MarketInfoAccordion = ({
|
||||
market,
|
||||
onSelect,
|
||||
}: MarketInfoAccordionProps) => {
|
||||
const featureFlags = useFeatureFlags((state) => state.flags);
|
||||
const t = useT();
|
||||
const { VEGA_TOKEN_URL } = useEnvironment();
|
||||
const headerClassName = 'uppercase text-lg';
|
||||
@ -352,7 +353,7 @@ export const MarketInfoAccordion = ({
|
||||
</>
|
||||
}
|
||||
/>
|
||||
{FLAGS.SUCCESSOR_MARKETS && (
|
||||
{featureFlags.SUCCESSOR_MARKETS && (
|
||||
<AccordionItem
|
||||
itemId="succession-line"
|
||||
title={t('Succession line')}
|
||||
|
@ -50,7 +50,7 @@ import {
|
||||
import {
|
||||
DApp,
|
||||
EtherscanLink,
|
||||
FLAGS,
|
||||
useFeatureFlags,
|
||||
TOKEN_PROPOSAL,
|
||||
useEnvironment,
|
||||
useLinks,
|
||||
@ -189,12 +189,13 @@ export const KeyDetailsInfoPanel = ({
|
||||
market,
|
||||
parentMarket,
|
||||
}: MarketInfoProps) => {
|
||||
const featureFlags = useFeatureFlags((state) => state.flags);
|
||||
const t = useT();
|
||||
const { data: parentMarketIdData } = useParentMarketIdQuery({
|
||||
variables: {
|
||||
marketId: market.id,
|
||||
},
|
||||
skip: !FLAGS.SUCCESSOR_MARKETS,
|
||||
skip: !featureFlags.SUCCESSOR_MARKETS,
|
||||
});
|
||||
|
||||
const { data: successorProposalDetails } =
|
||||
@ -202,7 +203,7 @@ export const KeyDetailsInfoPanel = ({
|
||||
variables: {
|
||||
proposalId: market.proposal?.id || '',
|
||||
},
|
||||
skip: !FLAGS.SUCCESSOR_MARKETS || !market.proposal?.id,
|
||||
skip: !featureFlags.SUCCESSOR_MARKETS || !market.proposal?.id,
|
||||
});
|
||||
|
||||
// The following queries are needed as the parent market could also have been a successor market.
|
||||
@ -245,7 +246,7 @@ export const KeyDetailsInfoPanel = ({
|
||||
</KeyValueTable>
|
||||
<MarketInfoTable
|
||||
data={
|
||||
FLAGS.SUCCESSOR_MARKETS
|
||||
featureFlags.SUCCESSOR_MARKETS
|
||||
? {
|
||||
name: market.tradableInstrument.instrument.name,
|
||||
parentMarketID:
|
||||
|
@ -11,7 +11,7 @@ import { useCallback } from 'react';
|
||||
import * as Schema from '@vegaprotocol/types';
|
||||
import { useVegaTransactionStore } from '@vegaprotocol/web3';
|
||||
import { HALFMAXGOINT64 } from '@vegaprotocol/utils';
|
||||
import { FLAGS } from '@vegaprotocol/environment';
|
||||
import { useFeatureFlags } from '@vegaprotocol/environment';
|
||||
|
||||
interface PositionsManagerProps {
|
||||
partyIds: string[];
|
||||
@ -28,10 +28,11 @@ export const PositionsManager = ({
|
||||
gridProps,
|
||||
showClosed = false,
|
||||
}: PositionsManagerProps) => {
|
||||
const featureFlags = useFeatureFlags((state) => state.flags);
|
||||
const t = useT();
|
||||
const { pubKeys, pubKey } = useVegaWallet();
|
||||
const create = useVegaTransactionStore((store) => store.create);
|
||||
const disableClosePositionsButton = FLAGS.DISABLE_CLOSE_POSITION;
|
||||
const disableClosePositionsButton = featureFlags.DISABLE_CLOSE_POSITION;
|
||||
|
||||
const onClose = useCallback(
|
||||
({ marketId, openVolume }: { marketId: string; openVolume: string }) =>
|
||||
|
Loading…
Reference in New Issue
Block a user